Skip to content

Commit bdbd817

Browse files
Simplify client-side JS since OOB swap guarantees synchronous delta processing
1 parent e782bc9 commit bdbd817

File tree

1 file changed

+15
-65
lines changed

1 file changed

+15
-65
lines changed

static/stream-md.js

Lines changed: 15 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -44,9 +44,6 @@ function processTextDelta(sseEvent) {
4444
const prev = window._streamingMarkdown.get(targetElement) || '';
4545
let updatedMarkdown = prev + markdownChunk;
4646

47-
// Apply pending replacements that might now match with the new chunk added
48-
updatedMarkdown = applyPendingReplacements(targetElement, updatedMarkdown);
49-
5047
window._streamingMarkdown.set(targetElement, updatedMarkdown);
5148
renderMarkdown(targetElement, updatedMarkdown, markdownChunk); // Pass original chunk for fallback
5249
}
@@ -68,61 +65,24 @@ function processTextReplacement(sseEvent) {
6865
const replacementUrl = parts[1]; // This is the actual download URL
6966

7067
if (!window._streamingMarkdown) {
68+
// Should have been initialized by processTextDelta
7169
window._streamingMarkdown = new WeakMap();
7270
}
7371

7472
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
9282
} 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.
12485
}
125-
return markdown;
12686
}
12787

12888
// Helper to parse OOB swap HTML and extract target and content
@@ -161,49 +121,39 @@ function parseOobSwap(oobHTML, eventTypeForLogging) {
161121

162122
// Helper to escape string for use in RegExp
163123
function escapeRegExp(string) {
164-
return string.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\$&'); // $& means the whole matched string
124+
return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); // $& means the whole matched string
165125
}
166126

167127
// Extracted rendering logic
168128
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);
172130

173131
if (typeof marked === 'undefined' || typeof DOMPurify === 'undefined') {
174132
console.error("marked.js or DOMPurify not loaded.");
175-
// Simple text append if libraries missing, try to use the full accumulated string
176133
targetElement.textContent = window._streamingMarkdown.get(targetElement) || fallbackChunkOnError;
177134
return;
178135
}
179136
try {
180137
const renderer = new marked.Renderer();
181138
renderer.link = ({ href, title, text }) => {
182139
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.
186140
return `<a target="_blank" rel="noopener noreferrer" href="${href}"${titleAttr}>${text}</a>`;
187141
};
188142
const rawHtml = marked.parse(markdownToRender, { renderer });
189143
const sanitizedHtml = DOMPurify.sanitize(rawHtml, {
190144
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"
194145
});
195146
targetElement.innerHTML = sanitizedHtml;
196147

197148
const messagesContainer = document.getElementById('messages');
198149
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;
200151
if (isScrolledToBottom) {
201152
messagesContainer.scrollTop = messagesContainer.scrollHeight;
202153
}
203154
}
204155
} catch (e) {
205156
console.error("Error processing markdown:", e);
206-
// Fallback on error: append raw chunk or full markdown to existing text content
207157
targetElement.textContent = (window._streamingMarkdown.get(targetElement) || '') + (fallbackChunkOnError || '');
208158
}
209159
}

0 commit comments

Comments
 (0)