Skip to content

Commit adf665f

Browse files
JSKittyclaude
andcommitted
fix: preserve angle bracket content that isn't valid HTML
Text like "<insert text here>" was being parsed as an unknown HTML element, then stripped by DOMPurify leaving empty message bubbles. Override marked's html renderer to escape unknown tags as literal text while passing safe HTML tags through. Also fix chat list previews which stripped all <tag> patterns indiscriminately. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent b06adf4 commit adf665f

2 files changed

Lines changed: 20 additions & 2 deletions

File tree

src/js/markdown.js

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,24 @@ function initializeMarked() {
139139
</div>`;
140140
};
141141

142+
// Escape unknown HTML tags as literal text — prevents DOMPurify from stripping
143+
// user content like "<insert text here>" which gets parsed as an unknown HTML element.
144+
// Safe tags (b, em, strong, etc.) pass through for user HTML support.
145+
const safeHtmlTags = new Set([
146+
'a', 'abbr', 'b', 'blockquote', 'br', 'code', 'del', 'details', 'div', 'em',
147+
'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'hr', 'i', 'li', 'ol', 'p', 'pre', 's',
148+
'span', 'strong', 'sub', 'summary', 'sup', 'table', 'tbody', 'td', 'th', 'thead',
149+
'tr', 'u', 'ul'
150+
]);
151+
renderer.html = function(token) {
152+
const html = typeof token === 'string' ? token : (token.text || token.raw || '');
153+
const tagMatch = html.match(/^<\/?([a-zA-Z][a-zA-Z0-9]*)/);
154+
if (tagMatch && safeHtmlTags.has(tagMatch[1].toLowerCase())) {
155+
return html; // Known safe tag — pass through
156+
}
157+
return encodeAttr(html); // Unknown tag — escape as literal text
158+
};
159+
142160
// No-preview autolink extension: <https://url> renders as a link but suppresses OG preview
143161
const noPreviewAutolink = {
144162
name: 'noPreviewAutolink',

src/main.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1447,8 +1447,8 @@ function contentToPreviewText(content) {
14471447
let text = content;
14481448
// Replace <br> / <br/> with space
14491449
text = text.replace(/<br\s*\/?>/gi, ' ');
1450-
// Strip all remaining HTML tags (require letter, / or ! after < to avoid matching math like "3 < 5 > 2")
1451-
text = text.replace(/<\/?[a-zA-Z!][^>]*>/g, '');
1450+
// Strip known HTML tags only (preserve unknown angle bracket content like "<insert text here>")
1451+
text = text.replace(/<\/?(a|abbr|b|blockquote|br|code|del|details|div|em|h[1-6]|hr|i|li|ol|p|pre|s|span|strong|sub|summary|sup|table|tbody|td|th|thead|tr|u|ul)(?:\s[^>]*)?\/?>/gi, '');
14521452
// Strip block-level markdown: headers, blockquotes, code fences, horizontal rules
14531453
text = text.replace(/^#{1,6}\s+/gm, '');
14541454
text = text.replace(/^>\s?/gm, '');

0 commit comments

Comments
 (0)