Skip to content

Commit b88ce78

Browse files
Use a custom marked renderer to add target _blank to file download links
1 parent e9f0078 commit b88ce78

File tree

1 file changed

+29
-21
lines changed

1 file changed

+29
-21
lines changed

static/stream-md.js

Lines changed: 29 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ function handleSSETextDelta(evt) {
2626
}
2727

2828
const swapOobAttr = oobElement.getAttribute('hx-swap-oob');
29-
const markdownChunk = oobElement.textContent || '';
29+
const markdownChunk = oobElement.innerHTML || '';
3030

3131
if (!swapOobAttr) {
3232
// Might be a non-OOB textDelta, handle differently or ignore?
@@ -72,27 +72,35 @@ function handleSSETextDelta(evt) {
7272
}
7373

7474
try {
75-
// Use marked.parse() for incremental updates.
76-
const rawHtml = marked.parse(updatedMarkdown);
77-
// Configure DOMPurify
78-
const sanitizedHtml = DOMPurify.sanitize(rawHtml, {
79-
// Allows standard HTML elements
80-
USE_PROFILES: { html: true }
81-
});
82-
targetElement.innerHTML = sanitizedHtml;
75+
// Create custom renderer with link target
76+
const renderer = new marked.Renderer();
77+
// Override link renderer: destructure the token object for href and text
78+
renderer.link = ({ href, title, text }) => {
79+
const titleAttr = title ? ` title="${title}"` : '';
80+
return `<a target="_blank" rel="noopener noreferrer" href="${href}"${titleAttr}>${text}</a>`;
81+
};
8382

84-
// 3) Auto-scroll the main messages container
85-
const messagesContainer = document.getElementById('messages');
86-
if (messagesContainer) {
87-
// Scroll only if the user isn't intentionally scrolled up
88-
const isScrolledToBottom = messagesContainer.scrollHeight - messagesContainer.clientHeight <= messagesContainer.scrollTop + 1; // +1 for tolerance
89-
if(isScrolledToBottom) {
90-
messagesContainer.scrollTop = messagesContainer.scrollHeight;
91-
}
92-
}
83+
// Use marked.parse() with custom renderer
84+
const rawHtml = marked.parse(updatedMarkdown, { renderer });
85+
// Configure DOMPurify
86+
const sanitizedHtml = DOMPurify.sanitize(rawHtml, {
87+
// Allows standard HTML elements
88+
USE_PROFILES: { html: true }
89+
});
90+
targetElement.innerHTML = sanitizedHtml;
91+
92+
// 3) Auto-scroll the main messages container
93+
const messagesContainer = document.getElementById('messages');
94+
if (messagesContainer) {
95+
// Scroll only if the user isn't intentionally scrolled up
96+
const isScrolledToBottom = messagesContainer.scrollHeight - messagesContainer.clientHeight <= messagesContainer.scrollTop + 1;
97+
if(isScrolledToBottom) {
98+
messagesContainer.scrollTop = messagesContainer.scrollHeight;
99+
}
100+
}
93101
} catch (e) {
94-
console.error("Error processing markdown:", e);
95-
// Fallback on error: append raw chunk to existing text content
96-
targetElement.textContent = (targetElement.textContent || '') + markdownChunk;
102+
console.error("Error processing markdown:", e);
103+
// Fallback on error: append raw chunk to existing text content
104+
targetElement.innerHTML = (targetElement.innerHTML || '') + markdownChunk;
97105
}
98106
}

0 commit comments

Comments
 (0)