// Markdown → sanitized HTML, ported from app.js's `mdNode`. Message
// bodies rendered into the live stream (assistant text, send/ask/answer/
// recv payloads) are untrusted (peer-agent / matrix-relayed content,
// agent-authored files) — `marked` itself no longer sanitizes (v5+
// dropped the built-in sanitizer), so every parse is run through
// DOMPurify before it's ever handed to `dangerouslySetInnerHTML`.
import { marked } from 'marked';
import DOMPurify from 'dompurify';
marked.setOptions({ breaks: true, gfm: true });
const ESCAPE_RE = /[&<>"]/g;
const ESCAPE_MAP: Record = { '&': '&', '<': '<', '>': '>', '"': '"' };
/** Render `text` as sanitized markdown HTML. Falls back to escaped plain
* text if `marked` throws (mirrors app.js's try/catch fallback). */
export function renderMarkdown(text: string | null | undefined): string {
const src = String(text ?? '');
try {
return DOMPurify.sanitize(marked.parse(src) as string);
} catch (err) {
console.warn('marked failed', err);
return src.replace(ESCAPE_RE, (c) => ESCAPE_MAP[c] ?? c);
}
}