diff --git a/apps/web/src/views/detail.ejs b/apps/web/src/views/detail.ejs
index 045ca66..6742287 100644
--- a/apps/web/src/views/detail.ejs
+++ b/apps/web/src/views/detail.ejs
@@ -194,16 +194,32 @@
);
}
- function highlightXml(str) {
- return escapeHtml(str)
- .replace(/<(\/?)([\w:-]+)/g, '<$1$2')
- .replace(/([\w:-]+)(=)("[^&]*")/g, '$1$2$3');
+ function highlightHtml(str) {
+ const esc = escapeHtml(str);
+ return esc
+ // Comments:
+ .replace(/<!--[\s\S]*?-->/g, '$&')
+ // DOCTYPE
+ .replace(/<!(DOCTYPE[^&]*?)>/gi, '<!$1>')
+ // Tags: <$1$2')
+ // Closing > and />
+ .replace(/(\/?)\s*>/g, '$1>')
+ // Attributes: name="value" or name='value'
+ .replace(/([\w:-]+)(=)("[^&]*?"|'[^&]*?')/g,
+ '$1$2$3')
+ // Boolean/valueless attributes (standalone word between tag name and >)
+ .replace(/(<\/span>)\s+([\w:-]+)(?=\s|)/g,
+ '$1 $2')
+ // Inline CSS: style content between quotes (already green, make more specific)
+ // Entity references: & < etc
+ .replace(/&[\w#]+;/g, '$&');
}
function highlightBody(body, contentType) {
const ct = (contentType || '').toLowerCase();
if (ct.includes('json')) return highlightJson(body);
- if (ct.includes('xml') || ct.includes('html')) return highlightXml(body);
+ if (ct.includes('xml') || ct.includes('html')) return highlightHtml(body);
return escapeHtml(body);
}