@@ -44,9 +44,6 @@ function processTextDelta(sseEvent) {
44
44
const prev = window . _streamingMarkdown . get ( targetElement ) || '' ;
45
45
let updatedMarkdown = prev + markdownChunk ;
46
46
47
- // Apply pending replacements that might now match with the new chunk added
48
- updatedMarkdown = applyPendingReplacements ( targetElement , updatedMarkdown ) ;
49
-
50
47
window . _streamingMarkdown . set ( targetElement , updatedMarkdown ) ;
51
48
renderMarkdown ( targetElement , updatedMarkdown , markdownChunk ) ; // Pass original chunk for fallback
52
49
}
@@ -68,61 +65,24 @@ function processTextReplacement(sseEvent) {
68
65
const replacementUrl = parts [ 1 ] ; // This is the actual download URL
69
66
70
67
if ( ! window . _streamingMarkdown ) {
68
+ // Should have been initialized by processTextDelta
71
69
window . _streamingMarkdown = new WeakMap ( ) ;
72
70
}
73
71
74
72
let currentMarkdown = window . _streamingMarkdown . get ( targetElement ) || '' ;
75
-
76
- const markdownPatternToReplace = `(${ escapeRegExp ( textToReplace ) } )` ;
77
- const markdownReplacementString = `(${ replacementUrl } )` ;
78
-
79
- if ( currentMarkdown . includes ( textToReplace ) ) { // Check if the raw sandbox path is present
80
- // More robust: replace `(sandbox:/path)` with `(our_url)`
81
- // Ensure the pattern targets the URL part of a Markdown link
82
- const regex = new RegExp ( `\\(\s*${ escapeRegExp ( textToReplace ) } \s*\\)` , 'g' ) ;
83
- if ( regex . test ( currentMarkdown ) ) {
84
- currentMarkdown = currentMarkdown . replace ( regex , `(${ replacementUrl } )` ) ;
85
- console . log ( `Applied replacement: ${ textToReplace } -> ${ replacementUrl } ` ) ;
86
- window . _streamingMarkdown . set ( targetElement , currentMarkdown ) ;
87
- renderMarkdown ( targetElement , currentMarkdown , '' ) ; // Re-render the whole thing
88
- } else {
89
- console . warn ( `Sandbox path '${ textToReplace } ' found, but not in typical markdown link format (url). Queuing replacement.` ) ;
90
- addPendingReplacement ( targetElement , textToReplace , replacementUrl ) ;
91
- }
73
+
74
+ // Regex to find the markdown link URL part: (sandbox:/path/to/file)
75
+ const regex = new RegExp ( `\\(\s*${ escapeRegExp ( textToReplace ) } \s*\\)` , 'g' ) ;
76
+
77
+ if ( regex . test ( currentMarkdown ) ) {
78
+ currentMarkdown = currentMarkdown . replace ( regex , `(${ replacementUrl } )` ) ;
79
+ console . log ( `Applied replacement: ${ textToReplace } -> ${ replacementUrl } ` ) ;
80
+ window . _streamingMarkdown . set ( targetElement , currentMarkdown ) ;
81
+ renderMarkdown ( targetElement , currentMarkdown , '' ) ; // Re-render the whole thing
92
82
} else {
93
- console . warn ( `Text to replace '${ textToReplace } ' not found in current markdown. Queuing replacement. Markdown:` , currentMarkdown . substring ( 0 , 200 ) ) ;
94
- addPendingReplacement ( targetElement , textToReplace , replacementUrl ) ;
95
- }
96
- }
97
-
98
- function addPendingReplacement ( targetElement , textToReplace , replacementUrl ) {
99
- if ( ! targetElement . _pendingReplacements ) {
100
- targetElement . _pendingReplacements = [ ] ;
101
- }
102
- // Avoid adding duplicate pending replacements
103
- if ( ! targetElement . _pendingReplacements . some ( p => p . find === textToReplace && p . replaceWith === replacementUrl ) ) {
104
- targetElement . _pendingReplacements . push ( { find : textToReplace , replaceWith : replacementUrl } ) ;
105
- }
106
- }
107
-
108
- function applyPendingReplacements ( targetElement , markdown ) {
109
- if ( targetElement . _pendingReplacements && targetElement . _pendingReplacements . length > 0 ) {
110
- let madeReplacement = false ;
111
- targetElement . _pendingReplacements . forEach ( p => {
112
- const regex = new RegExp ( `\\(\s*${ escapeRegExp ( p . find ) } \s*\\)` , 'g' ) ;
113
- if ( regex . test ( markdown ) ) {
114
- markdown = markdown . replace ( regex , `(${ p . replaceWith } )` ) ;
115
- console . log ( `Applied PENDING replacement: ${ p . find } -> ${ p . replaceWith } ` ) ;
116
- p . applied = true ;
117
- madeReplacement = true ;
118
- }
119
- } ) ;
120
- targetElement . _pendingReplacements = targetElement . _pendingReplacements . filter ( p => ! p . applied ) ;
121
- if ( madeReplacement ) {
122
- window . _streamingMarkdown . set ( targetElement , markdown ) ; // Update map if changes made
123
- }
83
+ console . warn ( `Text to replace '${ textToReplace } ' (in markdown link format) not found in current markdown. Markdown state:` , currentMarkdown . substring ( 0 , 300 ) ) ;
84
+ // If synchronous handling is guaranteed, this path implies an issue or mismatch.
124
85
}
125
- return markdown ;
126
86
}
127
87
128
88
// Helper to parse OOB swap HTML and extract target and content
@@ -161,49 +121,39 @@ function parseOobSwap(oobHTML, eventTypeForLogging) {
161
121
162
122
// Helper to escape string for use in RegExp
163
123
function escapeRegExp ( string ) {
164
- return string . replace ( / [ . * + ? ^ $ { } ( ) | [ \\ ] \\ \\ ] / g, '\\$&' ) ; // $& means the whole matched string
124
+ return string . replace ( / [ . * + ? ^ $ { } ( ) | [ \] \\ ] / g, '\\$&' ) ; // $& means the whole matched string
165
125
}
166
126
167
127
// Extracted rendering logic
168
128
function renderMarkdown ( targetElement , markdownToRender , fallbackChunkOnError ) {
169
- // Ensure pending replacements are applied one last time before rendering
170
- markdownToRender = applyPendingReplacements ( targetElement , markdownToRender ) ;
171
- window . _streamingMarkdown . set ( targetElement , markdownToRender ) ; // Update with potentially replaced markdown
129
+ window . _streamingMarkdown . set ( targetElement , markdownToRender ) ;
172
130
173
131
if ( typeof marked === 'undefined' || typeof DOMPurify === 'undefined' ) {
174
132
console . error ( "marked.js or DOMPurify not loaded." ) ;
175
- // Simple text append if libraries missing, try to use the full accumulated string
176
133
targetElement . textContent = window . _streamingMarkdown . get ( targetElement ) || fallbackChunkOnError ;
177
134
return ;
178
135
}
179
136
try {
180
137
const renderer = new marked . Renderer ( ) ;
181
138
renderer . link = ( { href, title, text } ) => {
182
139
const titleAttr = title ? ` title="${ title } "` : '' ;
183
- // Ensure link text is also sanitized if it contains HTML-like characters,
184
- // though marked.parse usually handles this for 'text'.
185
- // DOMPurify will sanitize the entire output anyway.
186
140
return `<a target="_blank" rel="noopener noreferrer" href="${ href } "${ titleAttr } >${ text } </a>` ;
187
141
} ;
188
142
const rawHtml = marked . parse ( markdownToRender , { renderer } ) ;
189
143
const sanitizedHtml = DOMPurify . sanitize ( rawHtml , {
190
144
USE_PROFILES : { html : true } ,
191
- // Consider adding target="_blank" to all generated links if not handled by renderer
192
- // ADD_ATTR: ['target'], // This would add target to all elements, too broad.
193
- // Instead, ensure renderer adds target="_blank"
194
145
} ) ;
195
146
targetElement . innerHTML = sanitizedHtml ;
196
147
197
148
const messagesContainer = document . getElementById ( 'messages' ) ;
198
149
if ( messagesContainer ) {
199
- const isScrolledToBottom = messagesContainer . scrollHeight - messagesContainer . clientHeight <= messagesContainer . scrollTop + 10 ; // Add some tolerance
150
+ const isScrolledToBottom = messagesContainer . scrollHeight - messagesContainer . clientHeight <= messagesContainer . scrollTop + 10 ;
200
151
if ( isScrolledToBottom ) {
201
152
messagesContainer . scrollTop = messagesContainer . scrollHeight ;
202
153
}
203
154
}
204
155
} catch ( e ) {
205
156
console . error ( "Error processing markdown:" , e ) ;
206
- // Fallback on error: append raw chunk or full markdown to existing text content
207
157
targetElement . textContent = ( window . _streamingMarkdown . get ( targetElement ) || '' ) + ( fallbackChunkOnError || '' ) ;
208
158
}
209
159
}
0 commit comments