import { cn } from "@/lib/utils"; import { memo, useId, useMemo } from "react"; import ReactMarkdown, { type Components } from "react-markdown"; import remarkBreaks from "remark-breaks"; import remarkGfm from "remark-gfm"; function extractLanguage(className?: string): string { if (!className) return "plaintext"; const match = className.match(/language-(\w+)/); return match ? match[1] : "plaintext"; } const components: Partial = { code: function CodeComponent({ className, children, ...props }) { const isInline = !props.node?.position?.start.line || props.node?.position?.start.line === props.node?.position?.end.line; if (isInline) { return ( {children} ); } const language = extractLanguage(className); return (
{language !== "plaintext" && (
{language}
)}
					{children}
				
); }, pre: function PreComponent({ children }) { return <>{children}; }, }; const MemoizedMarkdownBlock = memo( function MarkdownBlock({ content }: { content: string }) { return ( {content} ); }, (prev, next) => prev.content === next.content, ); MemoizedMarkdownBlock.displayName = "MemoizedMarkdownBlock"; function MarkdownComponent({ children, id, className, }: { children: string; id?: string; className?: string }) { const generatedId = useId(); const blockId = id ?? generatedId; const blocks = useMemo(() => children.split(/\n\n+/), [children]); return (
{blocks.map((block, index) => ( // biome-ignore lint/suspicious/noArrayIndexKey: stable blockId prefix makes this safe ))}
); } const Markdown = memo(MarkdownComponent); Markdown.displayName = "Markdown"; export { Markdown };