All files / src/components MermaidDiagram.tsx

90.47% Statements 19/21
66.66% Branches 8/12
100% Functions 4/4
94.73% Lines 18/19

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92                          7x 7x 7x   7x 5x   5x 5x 5x 5x                                                         5x 5x     5x     5x 5x 5x       7x 2x                   5x                              
"use client";
 
import { useEffect, useRef, useState } from "react";
 
interface MermaidDiagramProps {
  chart: string;
  caption?: string;
}
 
export default function MermaidDiagram({
  chart,
  caption,
}: MermaidDiagramProps) {
  const containerRef = useRef<HTMLDivElement>(null);
  const [svg, setSvg] = useState<string>("");
  const [error, setError] = useState<string>("");
 
  useEffect(() => {
    let cancelled = false;
    async function render() {
      try {
        const mermaid = (await import("mermaid")).default;
        const isDark = document.documentElement.classList.contains("dark");
        mermaid.initialize({
          startOnLoad: false,
          theme: isDark ? "dark" : "default",
          themeVariables: isDark
            ? {
                darkMode: true,
                background: "#1a1a2e",
                primaryColor: "#6366f1",
                primaryTextColor: "#e2e8f0",
                primaryBorderColor: "#818cf8",
                lineColor: "#e2e8f0",
                secondaryColor: "#1e293b",
                tertiaryColor: "#0f172a",
                edgeLabelBackground: "#1e293b",
                fontFamily: "ui-sans-serif, system-ui, sans-serif",
              }
            : {
                darkMode: false,
                background: "#ffffff",
                primaryColor: "#4f46e5",
                primaryTextColor: "#1e293b",
                primaryBorderColor: "#6366f1",
                lineColor: "#334155",
                secondaryColor: "#f1f5f9",
                tertiaryColor: "#e2e8f0",
                edgeLabelBackground: "#f8fafc",
                fontFamily: "ui-sans-serif, system-ui, sans-serif",
              },
        });
        const id = `mermaid-${Math.random().toString(36).slice(2, 9)}`;
        const { svg: rendered } = await mermaid.render(id, chart.trim());
        if (!cancelled) setSvg(rendered);
      } catch (err: unknown) {
        if (!cancelled) setError(String(err));
      }
    }
    render();
    return () => {
      cancelled = true;
    };
  }, [chart]);
 
  if (error) {
    return (
      <div className="border border-red-700 rounded-lg p-4 my-4">
        <p className="text-red-400 text-sm">Diagram render error: {error}</p>
        <pre className="text-xs text-[var(--text-secondary)] mt-2 overflow-x-auto">
          {chart}
        </pre>
      </div>
    );
  }
 
  return (
    <figure className="my-6">
      <div
        ref={containerRef}
        className="bg-[var(--surface)]/50 border border-[var(--border)] rounded-lg p-4 overflow-x-auto flex justify-center"
        dangerouslySetInnerHTML={{ __html: svg }}
      />
      {caption && (
        <figcaption className="text-center text-sm text-[var(--text-secondary)] mt-2 italic">
          {caption}
        </figcaption>
      )}
    </figure>
  );
}