import { cn } from "@/lib/utils" import { memo, useId, useMemo } from "react" import ReactMarkdown, { Components } from "react-markdown" import remarkBreaks from "remark-breaks" import remarkGfm from "remark-gfm" import { CodeBlock, CodeBlockCode } from "./code-block" export type MarkdownProps = { children: string id?: string className?: string components?: Partial } // Stream memoization'ı için bloklara bölme. Eskiden marked.lexer yapıyordu — // react-markdown'ın micromark'ına ek ikinci bir tam parser'ı (~460 KB paket) // client'a taşımak blok bölmek için gereksiz: kod çiti dışındaki boş satırlar // zaten markdown'da blok sınırıdır. Çit içi boş satırlar korunur. function parseMarkdownIntoBlocks(markdown: string): string[] { const bloklar: string[] = [] let mevcut: string[] = [] let citte = false for (const satir of markdown.split("\n")) { if (/^\s*(```|~~~)/.test(satir)) citte = !citte if (!citte && satir.trim() === "") { if (mevcut.length) { bloklar.push(mevcut.join("\n")) mevcut = [] } } else { mevcut.push(satir) } } if (mevcut.length) bloklar.push(mevcut.join("\n")) return bloklar.length ? bloklar : [markdown] } function extractLanguage(className?: string): string { if (!className) return "plaintext" const match = className.match(/language-(\w+)/) return match ? match[1] : "plaintext" } const INITIAL_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 ( ) }, pre: function PreComponent({ children }) { return <>{children} }, } const MemoizedMarkdownBlock = memo( function MarkdownBlock({ content, components = INITIAL_COMPONENTS, }: { content: string components?: Partial }) { return ( {content} ) }, function propsAreEqual(prevProps, nextProps) { return prevProps.content === nextProps.content } ) MemoizedMarkdownBlock.displayName = "MemoizedMarkdownBlock" function MarkdownComponent({ children, id, className, components = INITIAL_COMPONENTS, }: MarkdownProps) { const generatedId = useId() const blockId = id ?? generatedId const blocks = useMemo(() => parseMarkdownIntoBlocks(children), [children]) return (
{blocks.map((block, index) => ( ))}
) } const Markdown = memo(MarkdownComponent) Markdown.displayName = "Markdown" export { Markdown }