| 32 | } & React.HTMLProps<HTMLDivElement>; |
| 33 | |
| 34 | function CodeBlockCode({ |
| 35 | code, |
| 36 | language = "tsx", |
| 37 | theme = "github-light", |
| 38 | className, |
| 39 | ...props |
| 40 | }: CodeBlockCodeProps) { |
| 41 | const [highlightedHtml, setHighlightedHtml] = useState<string | null>(null); |
| 42 | |
| 43 | useEffect(() => { |
| 44 | async function highlight() { |
| 45 | const html = await codeToHtml(code, { lang: language, theme }); |
| 46 | setHighlightedHtml(html); |
| 47 | } |
| 48 | highlight(); |
| 49 | }, [code, language, theme]); |
| 50 | |
| 51 | const classNames = cn( |
| 52 | "w-full overflow-x-auto text-[13px] [&>pre]:px-4 [&>pre]:py-4", |
| 53 | className |
| 54 | ); |
| 55 | |
| 56 | // SSR fallback: render plain code if not hydrated yet |
| 57 | return highlightedHtml ? ( |
| 58 | <div |
| 59 | className={classNames} |
| 60 | dangerouslySetInnerHTML={{ __html: highlightedHtml }} |
| 61 | {...props} |
| 62 | /> |
| 63 | ) : ( |
| 64 | <div className={classNames} {...props}> |
| 65 | <pre> |
| 66 | <code>{code}</code> |
| 67 | </pre> |
| 68 | </div> |
| 69 | ); |
| 70 | } |
| 71 | |
| 72 | export type CodeBlockGroupProps = React.HTMLAttributes<HTMLDivElement>; |
| 73 | |