import { memo, useEffect, useRef, useState } from "react"; interface MermaidBlockProps { content: string } let mermaidLoaded = false; let mermaidInstance: typeof import("mermaid").default | null = null; async function getMermaid() { if (mermaidInstance) return mermaidInstance; const m = await import("mermaid"); mermaidInstance = m.default; if (!mermaidLoaded) { mermaidInstance.initialize({ startOnLoad: false, securityLevel: "sandbox", // S167-Fix3: previene esecuzione JS in SVG theme: "dark", themeVariables: { primaryColor: "#1a3a6b", primaryTextColor: "#e2e2f0", primaryBorderColor: "#2a3a5e", lineColor: "#4f8ef7", secondaryColor: "#12121f", tertiaryColor: "#12121f", background: "#0a0a0f", mainBkg: "#12121f", nodeBorder: "#2a2a40", clusterBkg: "#12121f", titleColor: "#e2e2f0", edgeLabelBackground: "#12121f", fontSize: "13px", }, flowchart: { curve: "basis" }, }); mermaidLoaded = true; } return mermaidInstance; } const MermaidBlock = memo(function MermaidBlock({ content }: MermaidBlockProps) { const [svg, setSvg] = useState(null); const [error, setError] = useState(null); const [loading, setLoading] = useState(true); const idRef = useRef(`mermaid-${Math.random().toString(36).slice(2)}`); useEffect(() => { let cancelled = false; setLoading(true); setSvg(null); setError(null); (async () => { try { const mermaid = await getMermaid(); const { svg } = await mermaid.render(idRef.current, content.trim()); if (!cancelled) { setSvg(svg); setLoading(false); } } catch (e) { if (!cancelled) { setError(e instanceof Error ? e.message : "Errore parsing Mermaid"); setLoading(false); } } })(); return () => { cancelled = true; }; }, [content]); return (
Diagramma {loading && }
{loading &&
Rendering...
} {error &&
! {error}
} {svg &&
}
); } ); export default MermaidBlock;