"use client"; import { useEffect, useState } from "react"; import { NodeViewWrapper, NodeViewContent } from "@tiptap/react"; import type { NodeViewProps } from "@tiptap/react"; import { Copy, Check } from "lucide-react"; import { useT } from "../../i18n"; import { MermaidDiagram } from "../mermaid-diagram"; // Coalesces fast keystrokes before re-rendering the Mermaid preview. // `mermaid.initialize()` mutates a process-global config, so back-to-back // renders during typing can race a concurrent ReadonlyContent render // (e.g. a comment card) and clobber its theme variables. 200ms keeps the // "live preview" feel while making concurrent inits unlikely in practice. const MERMAID_PREVIEW_DEBOUNCE_MS = 200; function useDebouncedValue(value: T, delayMs: number): T { const [debounced, setDebounced] = useState(value); useEffect(() => { const id = setTimeout(() => setDebounced(value), delayMs); return () => clearTimeout(id); }, [value, delayMs]); return debounced; } function CodeBlockView({ node }: NodeViewProps) { const { t } = useT("editor"); const [copied, setCopied] = useState(false); const language = node.attrs.language || ""; const isMermaid = language === "mermaid"; const chart = node.textContent; const debouncedChart = useDebouncedValue( isMermaid ? chart : "", MERMAID_PREVIEW_DEBOUNCE_MS, ); const handleCopy = async () => { const text = node.textContent; if (!text) return; await navigator.clipboard.writeText(text); setCopied(true); setTimeout(() => setCopied(false), 2000); }; return ( {isMermaid && debouncedChart.trim() && (
)}
{language && ( {language} )}
        {/* @ts-expect-error -- NodeViewContent supports as="code" at runtime */}
        
      
); } export { CodeBlockView };