import ReactMarkdown from "react-markdown"; import remarkGfm from "remark-gfm"; import remarkMath from "remark-math"; import rehypeKatex from "rehype-katex"; import type { Components } from "react-markdown"; function preprocessMarkdown(content: string): string { let result = content.replace(/\|\|/g, "|\n|"); const lines = result.split("\n"); const processed = lines.map((line) => { const tableStart = line.indexOf("|"); if (tableStart > 0) { const tableContent = line.slice(tableStart); if ((tableContent.match(/\|/g) ?? []).length >= 2) { return line.slice(0, tableStart).trimEnd() + "\n\n" + tableContent; } } return line; }); result = processed.join("\n"); result = result.replace(/([^|\n])\n(\|)/g, "$1\n\n$2"); return result; } const components: Components = { p: ({ children }) => (

{children}

), h1: ({ children }) => (

{children}

), h2: ({ children }) => (

{children}

), h3: ({ children }) => (

{children}

), ul: ({ children }) => ( ), ol: ({ children }) => (
    {children}
), li: ({ children }) =>
  • {children}
  • , code: ({ children, className }) => { const isBlock = className?.startsWith("language-"); const language = className?.replace("language-", "") ?? ""; if (isBlock) { return (
    {language && (
    {language}
    )}
    {children}
    ); } return ( {children} ); }, pre: ({ children }) => <>{children}, blockquote: ({ children }) => (
    {children}
    ), a: ({ children, href }) => ( {children} ), strong: ({ children }) => {children}, hr: () =>
    , table: ({ children }) => (
    {children}
    ), thead: ({ children }) => ( {children} ), th: ({ children }) => ( {children} ), td: ({ children }) => ( {children} ), tr: ({ children }) => ( {children} ), }; interface MarkdownRendererProps { content: string; skipPreprocess?: boolean; } export default function MarkdownRenderer({ content, skipPreprocess }: MarkdownRendererProps) { const processed = skipPreprocess ? content : preprocessMarkdown(content); if (!skipPreprocess) { const hasCR = content.includes("\r"); const hasDoubleNewline = content.includes("\n\n"); // console.log( // `[MarkdownRenderer] skipPreprocess=false contentLen=${content.length} same=${content === processed} hasCR=${hasCR} hasDoubleNewline=${hasDoubleNewline}` // ); // console.log("[MarkdownRenderer] CONTENT JSON →", JSON.stringify(content.slice(0, 600))); } return ( {processed} ); }