diff options
| author | 2026-03-26 10:08:23 +0800 | |
|---|---|---|
| committer | 2026-03-26 10:08:23 +0800 | |
| commit | 14832b994f2ad504c70f5e101ddfa96018e657d6 (patch) | |
| tree | 4ece6bc8421ff58915a0a17a4d981a9a30a43a68 /packages/docs/app | |
| parent | 8f2f72a857083122bce14277089ebccd64312061 (diff) | |
| download | DropOut-14832b994f2ad504c70f5e101ddfa96018e657d6.tar.gz DropOut-14832b994f2ad504c70f5e101ddfa96018e657d6.zip | |
chore(docs): refactor Mermaid component to render charts asynchronously and improve security
Diffstat (limited to 'packages/docs/app')
| -rw-r--r-- | packages/docs/app/components/mermaid.tsx | 29 |
1 files changed, 20 insertions, 9 deletions
diff --git a/packages/docs/app/components/mermaid.tsx b/packages/docs/app/components/mermaid.tsx index d408aad..04d89d9 100644 --- a/packages/docs/app/components/mermaid.tsx +++ b/packages/docs/app/components/mermaid.tsx @@ -6,25 +6,36 @@ import { useEffect, useRef } from "react"; mermaid.initialize({ startOnLoad: false, theme: "default", + securityLevel: "strict", }); export function Mermaid({ chart }: { chart: string }) { const ref = useRef<HTMLDivElement>(null); useEffect(() => { - if (ref.current) { - ref.current.innerHTML = chart; - mermaid.run({ - nodes: [ref.current], - }); - } + const renderChart = async () => { + if (!ref.current) return; + + try { + const id = `mermaid-${Math.random().toString(36).slice(2, 9)}`; + const { svg } = await mermaid.render(id, chart); + // Use innerHTML with sanitized SVG from mermaid.render + // biome-disable-next-line security/noInnerHtml + ref.current.innerHTML = svg; + } catch { + // Invalid chart definition, render nothing + if (ref.current) { + ref.current.innerHTML = ""; + } + } + }; + + renderChart(); }, [chart]); return ( <div className="not-prose my-6"> - <div ref={ref} className="mermaid"> - {chart} - </div> + <div ref={ref} className="mermaid" /> </div> ); } |