function escapeHtml(value) { return value.replace(/[&<>"']/g, (char) => ({ "&": "&", "<": "<", ">": ">", '"': """, "'": "'" }[char])); } function inline(value) { return escapeHtml(value) .replace(/`([^`]+)`/g, "$1") .replace(/\*\*([^*]+)\*\*/g, "$1") .replace(/~~([^~]+)~~/g, "$1") .replace(/\*([^*]+)\*/g, "$1") .replace(/\[([^\]]+)\]\((https?:\/\/[^\s)]+)\)/g, '$1'); } export function renderMarkdown(source) { let html = "", inCode = false, list = null; const closeList = () => { if (list) { html += ``; list = null; } }; for (const line of source.split("\n")) { if (line.startsWith("```")) { closeList(); html += inCode ? "" : "
"; inCode = !inCode; continue; }
    if (inCode) { html += `${escapeHtml(line)}\n`; continue; }
    const heading = line.match(/^(#{1,6})\s+(.+)$/);
    const ul = line.match(/^\s*[-*+]\s+(.+)$/);
    const ol = line.match(/^\s*\d+\.\s+(.+)$/);
    if (heading) { closeList(); const n = heading[1].length; html += `${inline(heading[2])}`; }
    else if (ul || ol) { const type = ul ? "ul" : "ol"; if (list !== type) { closeList(); html += `<${type}>`; list = type; } html += `
  • ${inline((ul || ol)[1])}
  • `; } else { closeList(); if (/^---+$/.test(line)) html += "
    "; else if (line.startsWith("> ")) html += `
    ${inline(line.slice(2))}
    `; else if (line.trim()) html += `

    ${inline(line)}

    `; else html += "
    "; } } closeList(); if (inCode) html += "
    "; return html; }