import { cacheLife } from "next/cache"; import { codeToHtml } from "shiki"; import { CopyButton } from "@/components/copy-button"; import { cn } from "@/lib/utils"; /** * Recursively extracts plain text content from React nodes. * Replacement for the `react-to-text` package. */ const getTextContent = (node: React.ReactNode): string => { if (node == null || typeof node === "boolean") return ""; if (typeof node === "string" || typeof node === "number") return String(node); if (Array.isArray(node)) return node.map(getTextContent).join(""); if (typeof node === "object" && "props" in node) { return getTextContent( (node as React.ReactElement<{ children?: React.ReactNode }>).props.children, ); } return ""; }; interface CodeBlockProps extends React.ComponentProps<"pre"> { showLineNumbers?: boolean; } const renderCode = async (code: string, lang: string): Promise => { "use cache"; cacheLife("max"); return codeToHtml(code, { lang, themes: { light: "github-light", dark: "github-dark" }, }); }; const CodeBlock = async ({ children, className, showLineNumbers = true, ...props }: CodeBlockProps) => { // Escape hatch for non-code pre blocks if (!children || typeof children !== "object" || !("props" in children)) { return (
        {children}
      
); } const codeProps = children.props as React.ComponentProps<"code">; const codeString = getTextContent(codeProps.children).trim(); const lang = codeProps.className?.split("language-")[1] ?? "text"; const html = await renderCode(codeString, lang); return (
); }; export { CodeBlock };