/** * Lazy Mermaid diagram rendering for ```mermaid code blocks. * Dark theme matching glass design, cached renders. */ let mermaidModule: typeof import('mermaid') | null = null let mermaidLoading: Promise | null = null let mermaidInitialized = false async function loadMermaid() { if (mermaidModule) return mermaidModule if (mermaidLoading) return mermaidLoading mermaidLoading = import('mermaid').then((m) => { mermaidModule = m return m }) return mermaidLoading } function initMermaid() { if (mermaidInitialized || !mermaidModule) return mermaidModule.default.initialize({ startOnLoad: false, theme: 'dark', themeVariables: { primaryColor: '#F7931A', primaryTextColor: '#fff', primaryBorderColor: '#F7931A', lineColor: '#666', secondaryColor: '#1a1a1a', tertiaryColor: '#111', background: '#0a0a0a', mainBkg: '#1a1a1a', nodeBorder: '#444', clusterBkg: '#111', clusterBorder: '#333', titleColor: '#ddd', edgeLabelBackground: '#1a1a1a', }, fontFamily: 'Inter, system-ui, sans-serif', fontSize: 13, }) mermaidInitialized = true } // Cache rendered diagrams const renderCache = new Map() export function hasMermaid(text: string): boolean { return /```mermaid/i.test(text) } let renderCounter = 0 export async function renderMermaidBlocks(html: string): Promise { await loadMermaid() initMermaid() const mermaid = mermaidModule!.default // Find
...
blocks const PRE_RE = /
([\s\S]*?)<\/code><\/pre>/gi
  const matches = [...html.matchAll(PRE_RE)]
  if (matches.length === 0) return html

  let result = html
  for (const match of matches) {
    const raw = match[1]
      .replace(/&/g, '&')
      .replace(/</g, '<')
      .replace(/>/g, '>')
      .replace(/"/g, '"')
      .replace(/'/g, "'")
      .trim()

    const cacheKey = raw
    if (renderCache.has(cacheKey)) {
      result = result.replace(match[0], renderCache.get(cacheKey)!)
      continue
    }

    try {
      const id = `mermaid-${++renderCounter}`
      const { svg } = await mermaid.render(id, raw)
      const wrapped = `
${svg}
` renderCache.set(cacheKey, wrapped) result = result.replace(match[0], wrapped) } catch { // Show error inline without crashing const errHtml = `
Mermaid render error
${match[1]}
` result = result.replace(match[0], errHtml) } } return result }