import { toJsxRuntime } from "hast-util-to-jsx-runtime"; import { Check, Copy } from "lucide-react"; import { useEffect, useLayoutEffect, useState, type ReactNode } from "react"; import { Fragment, jsx, jsxs } from "react/jsx-runtime"; import { Button } from "../cmp/button"; import { cn } from "../lib/utils"; import { highlightCode, highlightedLineClassNames, isSupportedLanguage, shikiLineClassNames, shikiPreClassName, } from "./shiki"; type CodeBlockProps = { code: string; filename?: string; highlightedLines: ReadonlySet; language?: string; showLineNumbers?: boolean; }; function CodeBlock({ code, filename, highlightedLines, language, showLineNumbers = true, }: CodeBlockProps) { const [highlighted, setHighlighted] = useState(null); const [copied, setCopied] = useState(false); const supported = isSupportedLanguage(language); const highlightedLineKey = [...highlightedLines] .sort((left, right) => left - right) .join(","); useLayoutEffect(() => { let active = true; setHighlighted(null); if (!language || !supported) return () => void (active = false); const lines = new Set( highlightedLineKey.split(",").filter(Boolean).map(Number), ); void highlightCode(code, language, lines).then((tree) => { if (!active || !tree) return; const rendered = toJsxRuntime(tree, { Fragment, jsx, jsxs }); setHighlighted(rendered); }); return () => void (active = false); }, [code, highlightedLineKey, language, supported]); useEffect(() => { if (!copied) return; const timeout = window.setTimeout(() => setCopied(false), 2_000); return () => window.clearTimeout(timeout); }, [copied]); async function copyCode() { try { await navigator.clipboard.writeText(code); setCopied(true); } catch { setCopied(false); } } return (
{filename ?? language ?? "Plain text"} {!supported && language ? ( Plain text ) : null} {copied ? "Code copied to clipboard" : ""}
{highlighted ?? (
            
              {code.split("\n").map((line, index, lines) => (
                
                  
                    {line}
                  
                  {index < lines.length - 1 ? "\n" : null}
                
              ))}
            
          
)}
); } export { CodeBlock, type CodeBlockProps };