import { Children, isValidElement, useMemo } from "react"; import type { Components } from "react-markdown"; import ReactMarkdown from "react-markdown"; import rehypeKatex from "rehype-katex"; import remarkBreaks from "remark-breaks"; import remarkGfm from "remark-gfm"; import remarkMath from "remark-math"; import { CodeBlock } from "@/components/CodeBlock"; import { FileReferenceChip, isLikelyFilePath } from "@/components/FileReferenceChip"; import { cn } from "@/lib/utils"; import "katex/dist/katex.min.css"; interface MarkdownTextRendererProps { children: string; className?: string; highlightCode?: boolean; } const remarkPlugins = [remarkBreaks, remarkGfm, remarkMath]; const rehypePlugins = [rehypeKatex]; /** * Heavy markdown stack (GFM, math, KaTeX, syntax highlighting) kept in a * separate chunk so the app shell can paint sooner on refresh. */ export default function MarkdownTextRenderer({ children, className, highlightCode = true, }: MarkdownTextRendererProps) { const components = useMemo( () => ({ code({ className: cls, children: kids, ...props }) { const match = /language-(\w+)/.exec(cls || ""); if (match) { const code = String(kids).replace(/\n$/, ""); return ( ); } const raw = String(kids).replace(/\n$/, ""); if (isLikelyFilePath(raw)) { return ; } /** Plain fenced ``` blocks (no language) & wide one-liners: block monospace, not inline pill. */ const widePlainBlock = raw.includes("\n") || raw.length > 120; if (widePlainBlock) { return ( {kids} ); } return ( {kids} ); }, pre({ children: markdownChildren }) { const kids = Children.toArray(markdownChildren); const lone = kids.length === 1 ? kids[0] : null; /** Highlighted fences render ``CodeBlock`` (block shell); skip invalid ``
``. */ if (lone != null && isValidElement(lone) && lone.type === CodeBlock) { return <>{markdownChildren}; } return (
            {markdownChildren}
          
); }, a({ href, children: markdownChildren, ...props }) { return ( {markdownChildren} ); }, img({ src, alt, node: _node, className: imgClassName, ...props }) { void _node; const source = typeof src === "string" ? src : ""; if (!source) return null; const label = typeof alt === "string" ? alt : ""; return ( {label} {label ? ( {label} ) : null} ); }, }), [highlightCode], ); return (
{children}
); }