export function markdownToHTML(text) { // 0) Remove .../... blocks // This regex will match an an opening or tag, // followed by any characters (non-greedy), until either a closing // or tag is found, OR the end of the string ($). text = text.replace(/[\s\S]*?(?:<\/think(?:ing)?>|$)/gi, ''); // 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(/```([^\n]*)\n([\s\S]*?)```/g, (_, lang, code) => { codeblocks.push({ lang: (lang || '').trim(), 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.2) Remove blank empty lines immediately after headings escaped = escaped.replace( /(.*?<\/h[1-4]>)[ \t]*\n(?:[ \t]*\n)+/g, "$1\n" ); // 4.3) Blockquotes escaped = escaped.replace( /(^|\n)([ \t]*> .+(?:\n[ \t]*> .+)*)/g, (_, lead, blockquoteBlock) => { const lines = blockquoteBlock .split(/\n/) .map(line => line.replace(/^[ \t]*>\s*/, '').trim()) .join('\n'); return `${lead}
${lines}
`; } ); // 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}`; } ); // 4.6) Markdown tables (GitHub-style). Strict: requires header, separator, ≥2 cols. const mdTableBlockRe = /(^\|[^\n]*\|?\s*\n\|\s*[:\-]+(?:\s*\|\s*[:\-]+)+\s*\|?\s*\n(?:\|[^\n]*\|?\s*(?:\n|$))*)/gm; escaped = escaped.replace(mdTableBlockRe, (block) => { const hadTrailingNewline = /\n$/.test(block); const lines = block.replace(/\n$/, '').split('\n'); const split = (line) => line.replace(/^\||\|$/g, '').split('|').map(s => s.trim()); const headers = split(lines[0]); const seps = split(lines[1]); if (headers.length < 2 || seps.length < 2) return block; if (!seps.every(s => /^[ :\-]+$/.test(s) && /-/.test(s))) return block; const aligns = seps.map(seg => { const s = seg.replace(/\s+/g,''); const left = s.startsWith(':'); const right = s.endsWith(':'); if (left && right) return 'center'; if (right) return 'right'; return 'left'; }); const bodyLines = lines.slice(2).filter(l => /^\|/.test(l.trim())); const cellStyle = (i) => ` style="text-align:${aligns[i] || 'left'};vertical-align:top;padding:.6rem .75rem"`; const ths = headers.map((h,i)=>`${h}`).join(''); const rows = bodyLines.map(line => { const cells = split(line); const tds = cells.map((c,i)=>`${c}`).join(''); return `${tds}`; }).join(''); const table = `${ths}${rows}
    `; return table + (hadTrailingNewline ? '\n' : ''); }); // 4.75) Horizontal rules escaped = escaped.replace(/^---\s*$/gm, "
    "); // 5) Bold, italic, inline code let html = escaped .replace(/\*\*(.+?)\*\*/g, "$1") .replace(/(?$1") .replace(/`(.+?)`/g, "$1"); // 5.5) Links html = html.replace(/\[([^\]]+?)\]\(([^)]+?)\)/g, '$1 $2'); // 6) Restore code blocks with title bar (language) html = html.replace(/@@CODEBLOCK(\d+)@@/g, (_, idx) => { const { lang, code } = codeblocks[+idx]; const title = (lang && lang.trim()) ? lang.trim() : 'code'; const escapedCode = code.replace(//g, ">"); const head = `
    ${title}
    `; const body = `
    ${escapedCode}
    `; return `
    ${head}${body}
    `; }); // 7) Convert line-breaks to
    html = html.replace(/\n/g, "
    "); // 8) Cleanup stray
    around lists/tables/wrappers html = html .replace(/
    \s*(
      )/g, "$1") .replace(/(<\/ul>)\s*
      /g, "$1") .replace(/
      \s*(
      ]*>)/g, "$1") .replace(/(<\/div>)\s*
      /g, "$1") .replace(/
      \s*(
      ]*>)/g, "$1") .replace(/(<\/div>)\s*
      /g, "$1") .replace(/
      \s*(]*>)/g, "$1") .replace(/(<\/table>)\s*
      /g, "$1") .replace(/
      \s*(
      )/g, "$1") // New: Cleanup
      before blockquote .replace(/(<\/blockquote>)\s*
      /g, "$1"); // New: Cleanup
      after blockquote // 9) Trim spaces/tabs and remove empty newline(s) immediately after
      , blockquote, and ul html = html .replace(/(
      )[ \t]+/g, "$1") // remove spaces/tabs .replace(/(
      )(?:[ \t]*
      )+/g, "$1") // remove one or more blank lines (now
      ) after
      .replace(/(<\/blockquote>)(?:[ \t]*
      )+/g, "$1") // New: Remove empty lines after blockquote .replace(/(<\/ul>)(?:[ \t]*
      )+/g, "$1"); // New: Remove empty lines after ul return html; }