/** Shared utility functions. */ export function scroll(el) { el.scrollTop = el.scrollHeight; } export function esc(s) { const d = document.createElement('span'); d.textContent = s; return d.innerHTML; } export function truncate(s, n) { return s.length > n ? s.slice(0, n) + '\u2026' : s; } export function renderMarkdown(text) { let html = esc(text); // Code blocks html = html.replace(/```(\w*)\n([\s\S]*?)```/g, (_, lang, code) => '
' + code.trim() + '
'); // Inline code html = html.replace(/`([^`]+)`/g, '$1'); // Bold html = html.replace(/\*\*(.+?)\*\*/g, '$1'); // Italic html = html.replace(/\*(.+?)\*/g, '$1'); // Bullet lists html = html.replace(/^[\*\-]\s+(.+)$/gm, '
  • $1
  • '); html = html.replace(/(
  • .*<\/li>\n?)+/g, m => ''); // Numbered lists html = html.replace(/^\d+\.\s+(.+)$/gm, '
  • $1
  • '); // Line breaks (but not inside pre/code) html = html.replace(/\n/g, '
    '); return html; }