${codeRuns[+idx]}`);
};
// 1) Normalize line endings
let tmp = text.replace(/\r\n/g, '\n').replace(/\r/g, '\n');
// 2) Extract code blocks and replace with placeholders (protect from all formatting)
const codeblocks = [];
const placeholder = idx => `@@CODEBLOCK${idx}@@`;
tmp = tmp.replace(/```([^\n]*)\n([\s\S]*?)```/g, (_, lang, code) => {
// Strip trailing whitespace-only lines at the end of the block
let cleaned = (code || '').replace(/\r\n/g, '\n').replace(/\r/g, '\n');
const lines = cleaned.split('\n');
while (lines.length > 0 && /^\s*$/.test(lines[lines.length - 1])) lines.pop();
cleaned = lines.join('\n');
codeblocks.push({ lang: (lang || '').trim(), code: cleaned });
return placeholder(codeblocks.length - 1);
});
// 3) HTML-escape special characters (outside of code blocks)
let escaped = escapeHtml(tmp);
// 4) Headings
escaped = escaped
.replace(/^#### (.+)$/gm, "${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 => `
; keep raw \n (no
here!)
const escapedCode = escapeHtml(code);
// Single-line header to avoid global
interference
const encodedForCopy = encodeURIComponent(code);
const head = `${titleLabel}`;
// Ensure wrapping inside container and preserve newlines for copy/paste
const body = `${escapedCode}
`;
return `${head}${body}`;
});
// 8) Final cleanup around codeblocks specifically (remove stray
added next to placeholders)
html = html
.replace(/
\s*(?= before opening
.replace(/(]*>[\s\S]*?<\/div>)\s*
/g, "$1"); //
right after closing
return html;
}
// Virtually close an unfinished fenced code block so it renders during streaming.
function balanceStreamingCodeFence(md) {
// Split into lines; we only consider fences that start a line.
const lines = md.split(/\r?\n/);
// Track the last unmatched opening fence we see while scanning.
// { fenceChar: '`' or '~', fenceLen: number }
let open = null;
for (let i = 0; i < lines.length; i++) {
const line = lines[i];
// Opening fence? ^\s*([`~]{3,})(.*)$
if (!open) {
const m = line.match(/^\s*([`~]{3,})([^\s]*)?.*$/);
if (m) {
// Treat as an opening fence
open = { fenceChar: m[1][0], fenceLen: m[1].length };
continue;
}
} else {
// Closing fence: must match same char and length or longer
const re = new RegExp(`^\\s*(${open.fenceChar}{${open.fenceLen},})\\s*$`);
if (re.test(line)) {
// Closed
open = null;
continue;
}
// Otherwise still inside the code block; keep scanning
}
}
if (open) {
// Virtually close with the same fence so the block renders now
const virtual = `${open.fenceChar.repeat(open.fenceLen)}`;
return md.endsWith('\n') ? md + virtual : md + '\n' + virtual;
}
return md;
}