/** * Minimal, safe Markdown renderer for chat answers. * Supports: **bold**, *italic*, `code`, headings, bullet/numbered lists, tables, paragraphs, * and highlights [Source N] citations. All text is HTML-escaped FIRST, so model output can * never inject markup. */ (function (root) { function esc(text) { return String(text) .replace(/&/g, '&').replace(//g, '>') .replace(/"/g, '"').replace(/'/g, '''); } function inline(s) { return s .replace(/`([^`]+)`/g, '$1') .replace(/\*\*([^*]+?)\*\*/g, '$1') .replace(/(^|[^*])\*([^*\s][^*]*?)\*(?!\*)/g, '$1$2') .replace(/\[(Source\s+\d+)\]/g, '[$1]'); } const isTableSep = (line) => /^\s*\|?\s*:?-{3,}:?\s*(\|\s*:?-{3,}:?\s*)*\|?\s*$/.test(line || ''); const cells = (line) => line.trim().replace(/^\|/, '').replace(/\|$/, '').split('|').map((c) => c.trim()); function renderMarkdown(text) { const lines = esc(text || '').replace(/\r\n?/g, '\n').split('\n'); const out = []; let i = 0; while (i < lines.length) { const line = lines[i]; if (!line.trim()) { i++; continue; } if (line.trim().startsWith('|') && isTableSep(lines[i + 1])) { const head = cells(line); i += 2; const rows = []; while (i < lines.length && lines[i].trim().startsWith('|')) { rows.push(cells(lines[i])); i++; } out.push('
' + head.map((c) => ``).join('') + '' + rows.map((r) => '' + r.map((c) => ``).join('') + '').join('') + '
${inline(c)}
${inline(c)}
'); continue; } const heading = line.match(/^\s*(#{1,4})\s+(.*)$/); if (heading) { out.push(`

${inline(heading[2])}

`); i++; continue; } if (/^\s*[-*•]\s+/.test(line)) { const items = []; while (i < lines.length && /^\s*[-*•]\s+/.test(lines[i])) { items.push(`
  • ${inline(lines[i].replace(/^\s*[-*•]\s+/, ''))}
  • `); i++; } out.push(``); continue; } if (/^\s*\d+[.)]\s+/.test(line)) { const items = []; while (i < lines.length && /^\s*\d+[.)]\s+/.test(lines[i])) { items.push(`
  • ${inline(lines[i].replace(/^\s*\d+[.)]\s+/, ''))}
  • `); i++; } out.push(`
      ${items.join('')}
    `); continue; } const para = []; while (i < lines.length && lines[i].trim() && !/^\s*([-*•]|\d+[.)])\s+/.test(lines[i]) && !lines[i].trim().startsWith('|') && !/^\s*#{1,4}\s+/.test(lines[i])) { para.push(inline(lines[i])); i++; } if (para.length) out.push(`

    ${para.join('
    ')}

    `); else i++; } return out.join(''); } if (typeof module !== 'undefined' && module.exports) module.exports = { renderMarkdown }; else root.renderMarkdown = renderMarkdown; })(typeof window !== 'undefined' ? window : globalThis);