import React, { Suspense } from "react"; import Markdown from "react-markdown"; import remarkGfm from "remark-gfm"; import type { ThemedToken } from "shiki"; import { cn } from "@/lib/utils"; import { CopyButton } from "@/components/ui/copy-button"; interface MarkdownRendererProps { children: string; } export function MarkdownRenderer({ children }: MarkdownRendererProps) { return (
{children}
); } interface HighlightedPre extends React.HTMLAttributes { children: string; language: string; } // Synchronous wrapper that uses Suspense const HighlightedPre = ({ children, language, ...props }: HighlightedPre) => { return ( {children}}> {children} ); }; // Async logic moved here, loaded with lazy or dynamic inside the component const AsyncHighlightedPre = (props: HighlightedPre) => { const [tokens, setTokens] = React.useState([]); const [loaded, setLoaded] = React.useState(false); React.useEffect(() => { (async () => { const { codeToTokens, bundledLanguages } = await import("shiki"); if (!(props.language in bundledLanguages)) { setTokens(null); setLoaded(true); return; } const { tokens } = await codeToTokens(props.children, { lang: props.language as keyof typeof bundledLanguages, defaultColor: false, themes: { light: "github-light", dark: "github-dark", }, }); setTokens(tokens); setLoaded(true); })(); }, [props.children, props.language]); if (!loaded) { return
{props.children}
; } if (!tokens) { return
{props.children}
; } return (
      
        {tokens.map((line, lineIndex) => (
          
            {line.map((token, tokenIndex) => {
              const style =
                typeof token.htmlStyle === "string"
                  ? undefined
                  : token.htmlStyle;

              return (
                
                  {token.content}
                
              );
            })}
            {"\n"}
          
        ))}
      
    
); }; HighlightedPre.displayName = "HighlightedCode"; interface CodeBlockProps extends React.HTMLAttributes { children: React.ReactNode; className?: string; language: string; } const CodeBlock = ({ children, className, language, ...restProps }: CodeBlockProps) => { const code = typeof children === "string" ? children : childrenTakeAllStringContents(children); const preClass = cn( "overflow-x-scroll rounded-md border bg-background/50 p-4 font-mono text-sm [scrollbar-width:none]", className ); return (
{children} } > {code}
); }; function childrenTakeAllStringContents(element: any): string { if (typeof element === "string") { return element; } if (element?.props?.children) { let children = element.props.children; if (Array.isArray(children)) { return children .map((child) => childrenTakeAllStringContents(child)) .join(""); } else { return childrenTakeAllStringContents(children); } } return ""; } const COMPONENTS = { h1: withClass("h1", "text-2xl font-semibold"), h2: withClass("h2", "font-semibold text-xl"), h3: withClass("h3", "font-semibold text-lg"), h4: withClass("h4", "font-semibold text-base"), h5: withClass("h5", "font-medium"), strong: withClass("strong", "font-semibold"), a: withClass("a", "text-primary underline underline-offset-2"), blockquote: withClass("blockquote", "border-l-2 border-primary pl-4"), code: ({ children, className, node, ...rest }: any) => { const match = /language-(\w+)/.exec(className || ""); return match ? ( {children} ) : ( &]:rounded-md [:not(pre)>&]:bg-background/50 [:not(pre)>&]:px-1 [:not(pre)>&]:py-0.5" )} {...rest} > {children} ); }, pre: ({ children }: any) => children, ol: withClass("ol", "list-decimal space-y-2 pl-6"), ul: withClass("ul", "list-disc space-y-2 pl-6"), li: withClass("li", "my-1.5"), table: withClass( "table", "w-full border-collapse overflow-y-auto rounded-md border border-foreground/20" ), th: withClass( "th", "border border-foreground/20 px-4 py-2 text-left font-bold [&[align=center]]:text-center [&[align=right]]:text-right" ), td: withClass( "td", "border border-foreground/20 px-4 py-2 text-left [&[align=center]]:text-center [&[align=right]]:text-right" ), tr: withClass("tr", "m-0 border-t p-0 even:bg-muted"), p: withClass("p", "whitespace-pre-wrap"), hr: withClass("hr", "border-foreground/20"), }; function withClass(Tag: keyof JSX.IntrinsicElements, classes: string) { const Component = ({ node, ...props }: any) => ( ); Component.displayName = Tag; return Component; }