export function markdownToHTML(text) { // 0) Remove .../... blocks text = text.replace( /(^|\n)\s*[\s\S]*?<\/think(?:ing)?>\s*(\n\s*\n)?/gi, (_, lead) => (lead ? '\n' : '') ); // 1) Normalize line endings let tmp = text.replace(/\r\n/g, '\n').replace(/\r/g, '\n'); // 2) Extract code blocks and replace with placeholders const codeblocks = []; const placeholder = idx => `@@CODEBLOCK${idx}@@`; tmp = tmp.replace(/```(\w*)\n([\s\S]*?)```/g, (_, lang, code) => { codeblocks.push({ lang, code }); return placeholder(codeblocks.length - 1); }); // 3) HTML-escape special characters let escaped = tmp .replace(/&/g, "&") .replace(//g, ">"); // 4) Headings escaped = escaped .replace(/^#### (.+)$/gm, "

$1

") .replace(/^### (.+)$/gm, "

$1

") .replace(/^## (.+)$/gm, "

$1

") .replace(/^# (.+)$/gm, "

$1

"); // 4.5) Unordered lists escaped = escaped.replace( /(^|\n)([ \t]*\* .+(?:\n[ \t]*\* .+)*)/g, (_, lead, listBlock) => { const items = listBlock .split(/\n/) .map(line => line.replace(/^[ \t]*\*\s+/, '').trim()) .map(item => `
  • ${item}
  • `) .join(''); return `${lead}`; } ); // 5) Bold, italic, inline code let html = escaped .replace(/\*\*(.+?)\*\*/g, "$1") .replace(/(?$1") .replace(/`(.+?)`/g, "$1"); // 6) Restore code blocks html = html.replace(/@@CODEBLOCK(\d+)@@/g, (_, idx) => { const { lang, code } = codeblocks[+idx]; const escapedCode = code.replace(//g, ">"); return `
    ${escapedCode}
    `; }); // 7) Convert line-breaks to
    html = html.replace(/\n/g, "
    "); // 8) Cleanup stray
    immediately before/after lists html = html .replace(/
    \s*(