function escapeHtml(text) { return text .replace(/&/g, '&') .replace(//g, '>') .replace(/"/g, '"') .replace(/'/g, '''); } export function parseMarkdown(text) { if (!text) return ''; // Escape HTML first to prevent XSS let html = escapeHtml(text); // Code blocks (``` ... ```) html = html.replace(/```(\w*)\n([\s\S]*?)```/g, (match, lang, code) => { const cls = lang ? ` class="language-${lang}"` : ''; return `
${code.trim()}
`; }); // Inline code html = html.replace(/`([^`\n]+)`/g, '$1'); // Bold html = html.replace(/\*\*([^*]+)\*\*/g, '$1'); // Italic (avoid matching bold remnants) html = html.replace(/(?$1'); html = html.replace(/(?$1'); // Blockquotes (lines starting with >) html = html.replace(/^>\s?(.+)$/gm, '
$1
'); // @mentions html = html.replace(/@(\w+)/g, '@$1'); // URLs — match http/https links html = html.replace( /(?$1' ); // Line breaks html = html.replace(/\n/g, '
'); // Merge consecutive blockquotes html = html.replace(/<\/blockquote>
/g, '
'); return html; }