"use client";
import { memo, useMemo, type ReactNode } from "react";
import ReactMarkdown from "react-markdown";
import remarkGfm from "remark-gfm";
import remarkMath from "remark-math";
import rehypeKatex from "rehype-katex";
import rehypeHighlight from "rehype-highlight";
import { Eye } from "lucide-react";
import { CopyButton } from "../ui";
const PREVIEWABLE = new Set(["html", "svg", "mermaid", "jsx", "tsx", "react", "markdown", "md", "python", "py", "typescript", "ts"]);
export interface MarkdownProps { text: string; onPreview?: (code: string, lang: string) => void; className?: string }
function Code({ className, children, onPreview, ...rest }: { className?: string; children?: ReactNode; onPreview?: MarkdownProps["onPreview"]; node?: unknown }) {
const lang = /language-([\w+-]+)/.exec(className ?? "")?.[1];
const raw = String(children ?? "").replace(/\n$/, "");
if (!lang && !raw.includes("\n")) return {children};
return (
<>
{children}
>
);
}
export const Markdown = memo(function Markdown({ text, onPreview, className }: MarkdownProps) {
// Normalize \[ \] and \( \) LaTeX delimiters that some models emit
const src = useMemo(() => text.replace(/\\\[((?:.|\n)*?)\\\]/g, (_, m) => `$$${m}$$`).replace(/\\\((.+?)\\\)/g, (_, m) => `$${m}$`), [text]);
return (
,
pre: ({ children }) => <>{children}>,
a: ({ href, children }) => {children},
table: ({ children }) =>