'use client'; import { useCallback, useEffect, useRef, useState } from 'react'; import mermaid from 'mermaid'; interface MermaidViewerProps { diagram: string; className?: string; onNodeClick?: (nodeId: string) => void; } export function MermaidViewer({ diagram, className = '', onNodeClick }: MermaidViewerProps) { const containerRef = useRef(null); const [error, setError] = useState(null); const [isLoading, setIsLoading] = useState(true); const renderDiagram = useCallback(async () => { if (!containerRef.current || !diagram) { setIsLoading(false); return; } setIsLoading(true); setError(null); try { // Initialize mermaid with theme based on document const isDark = document.documentElement.classList.contains('dark'); mermaid.initialize({ startOnLoad: false, theme: isDark ? 'dark' : 'default', flowchart: { useMaxWidth: true, htmlLabels: true, curve: 'basis', }, securityLevel: 'loose', }); // Generate unique ID for this render const id = `mermaid-${Date.now()}`; // Render the diagram const { svg } = await mermaid.render(id, diagram); if (containerRef.current) { containerRef.current.innerHTML = svg; // Add click handlers to nodes if callback provided if (onNodeClick) { const nodes = containerRef.current.querySelectorAll('.node'); nodes.forEach((node) => { node.addEventListener('click', () => { const nodeId = node.id?.replace(/^flowchart-/, '').replace(/-\d+$/, ''); if (nodeId) { onNodeClick(nodeId); } }); (node as HTMLElement).style.cursor = 'pointer'; }); } } } catch (err) { setError(err instanceof Error ? err.message : 'Failed to render diagram'); } finally { setIsLoading(false); } }, [diagram, onNodeClick]); useEffect(() => { renderDiagram(); }, [renderDiagram]); // Re-render on theme change useEffect(() => { const observer = new MutationObserver((mutations) => { mutations.forEach((mutation) => { if (mutation.attributeName === 'class') { renderDiagram(); } }); }); observer.observe(document.documentElement, { attributes: true }); return () => observer.disconnect(); }, [renderDiagram]); if (!diagram) { return (
No diagram data available
); } if (error) { return (
Failed to render diagram
{error}
          {diagram}
        
); } return (
{isLoading && (
)}
); }