'use client'; import { cn } from '@/lib/utils'; import React, { useEffect, useState } from 'react'; import { codeToHtml } from 'shiki'; import { useTheme } from 'next-themes'; export type CodeBlockProps = { children?: React.ReactNode; className?: string; } & React.HTMLProps; function CodeBlock({ children, className, ...props }: CodeBlockProps) { return (
{children}
); } export type CodeBlockCodeProps = { code: string; language?: string; theme?: string; className?: string; } & React.HTMLProps; function CodeBlockCode({ code, language = 'tsx', theme: propTheme, className, ...props }: CodeBlockCodeProps) { const { resolvedTheme } = useTheme(); const [highlightedHtml, setHighlightedHtml] = useState(null); // Use github-dark when in dark mode, github-light when in light mode const theme = propTheme || (resolvedTheme === 'dark' ? 'github-dark' : 'github-light'); useEffect(() => { async function highlight() { if (!code || typeof code !== 'string') { setHighlightedHtml(null); return; } const html = await codeToHtml(code, { lang: language, theme, transformers: [ { pre(node) { if (node.properties.style) { node.properties.style = (node.properties.style as string) .replace(/background-color:[^;]+;?/g, ''); } } } ] }); setHighlightedHtml(html); } highlight(); }, [code, language, theme]); const classNames = cn('[&_pre]:!bg-background/95 [&_pre]:rounded-lg [&_pre]:p-4 [&_pre]:!overflow-x-auto [&_pre]:!w-px [&_pre]:!flex-grow [&_pre]:!min-w-0 [&_pre]:!box-border [&_.shiki]:!overflow-x-auto [&_.shiki]:!w-px [&_.shiki]:!flex-grow [&_.shiki]:!min-w-0 [&_code]:!min-w-0 [&_code]:!whitespace-pre', 'w-px flex-grow min-w-0 overflow-hidden flex w-full', className); // SSR fallback: render plain code if not hydrated yet return highlightedHtml ? (
) : (
        {code}
      
); } export type CodeBlockGroupProps = React.HTMLAttributes; function CodeBlockGroup({ children, className, ...props }: CodeBlockGroupProps) { return (
{children}
); } export { CodeBlockGroup, CodeBlockCode, CodeBlock };