Spaces:
Sleeping
Sleeping
| 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<string | null>(null); | |
| const [error, setError] = useState<string | null>(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 ( | |
| <div style={{ background: "#0d0d18", border: "1px solid #1e1e2e", borderRadius: 10, marginBottom: "0.6em", overflow: "hidden" }}> | |
| <div style={{ display: "flex", alignItems: "center", gap: 6, padding: "0.4rem 0.85rem", background: "#0a0a14", borderBottom: "1px solid #1e1e2e" }}> | |
| <span style={{ fontSize: "0.78rem", color: "#6b6b7b", fontWeight: 600 }}>Diagramma</span> | |
| {loading && <span className="spin" style={{ fontSize: "0.8rem", color: "#6b6b7b" }}>⟳</span>} | |
| </div> | |
| <div style={{ padding: "0.75rem", display: "flex", justifyContent: "center", minHeight: 60 }}> | |
| {loading && <div style={{ color: "#5a5a72", fontSize: "0.78rem", alignSelf: "center" }}>Rendering...</div>} | |
| {error && <div style={{ color: "#fca5a5", fontSize: "0.78rem" }}>! {error}</div>} | |
| {svg && <div dangerouslySetInnerHTML={{ __html: svg }} style={{ maxWidth: "100%", overflow: "auto" }} />} | |
| </div> | |
| </div> | |
| ); | |
| } | |
| ); | |
| export default MermaidBlock; | |