import Markdown from "react-markdown"; import rehypeMathjax from "rehype-mathjax"; import rehypeRaw from "rehype-raw"; import remarkGfm from "remark-gfm"; import { cn } from "@/lib/utils"; import CodeComponent from "./code-component"; type MarkdownRendererProps = { chatMessage: string; className?: string; }; const preprocessChatMessage = (text: string): string => { // Handle tags let processed = text .replace(//g, "``") .replace(/<\/think>/g, "``"); // Clean up tables if present if (isMarkdownTable(processed)) { processed = cleanupTableEmptyCells(processed); } return processed; }; export const isMarkdownTable = (text: string): boolean => { if (!text?.trim()) return false; // Single regex to detect markdown table with header separator return /\|.*\|.*\n\s*\|[\s\-:]+\|/m.test(text); }; export const cleanupTableEmptyCells = (text: string): string => { return text .split("\n") .filter((line) => { const trimmed = line.trim(); // Keep non-table lines if (!trimmed.includes("|")) return true; // Keep separator rows (contain only |, -, :, spaces) if (/^\|[\s\-:]+\|$/.test(trimmed)) return true; // For data rows, check if any cell has content const cells = trimmed.split("|").slice(1, -1); // Remove delimiter cells return cells.some((cell) => cell.trim() !== ""); }) .join("\n"); }; export const MarkdownRenderer = ({ chatMessage, className, }: MarkdownRendererProps) => { // Process the chat message to handle tags and clean up tables const processedChatMessage = preprocessChatMessage(chatMessage); return (
url} components={{ p({ node, ...props }) { return (

{props.children}

); }, ol({ node, ...props }) { return
    {props.children}
; }, strong({ node, ...props }) { return {props.children}; }, h1({ node, ...props }) { return

{props.children}

; }, h2({ node, ...props }) { return

{props.children}

; }, h3({ node, ...props }) { return

{props.children}

; }, hr() { return
; }, ul({ node, ...props }) { return
    {props.children}
; }, pre({ node, ...props }) { return <>{props.children}; }, table: ({ node, ...props }) => { return (
{props.children}
); }, a({ node, ...props }) { return ( {props.children} ); }, code(props) { const { children, className, ...rest } = props; let content = children as string; if ( Array.isArray(children) && children.length === 1 && typeof children[0] === "string" ) { content = children[0] as string; } if (typeof content === "string") { if (content.length) { if (content[0] === "▍") { return ; } // Specifically handle tags that were wrapped in backticks if (content === "" || content === "") { return {content}; } } const match = /language-(\w+)/.exec(className || ""); const isInline = !className?.startsWith("language-"); return !isInline ? ( ) : ( {content} ); } }, }} > {processedChatMessage}
); };