/**
* 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('
| ${inline(c)} | `).join('') + '
|---|
| ${inline(c)} | `).join('') + '
${inline(heading[2])}
`); i++; continue; } if (/^\s*[-*•]\s+/.test(line)) { const items = []; while (i < lines.length && /^\s*[-*•]\s+/.test(lines[i])) { items.push(`${para.join('
')}