import { useState } from "react"; import { Button, message } from "antd"; import { Check, Copy } from "lucide-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 "katex/dist/katex.min.css"; import "highlight.js/styles/github.css"; import type { ReactNode } from "react"; type StreamMessageProps = { content: string; }; function extractText(node: ReactNode): string { if (typeof node === "string") return node; if (typeof node === "number") return String(node); if (!node) return ""; if (Array.isArray(node)) return node.map((item) => extractText(item)).join(""); if (typeof node === "object" && "props" in node) { const child = (node as { props?: { children?: ReactNode } }).props?.children; return extractText(child); } return ""; } function MarkdownPreBlock(props: { children: ReactNode }) { const [copied, setCopied] = useState(false); const codeText = extractText(props.children).replace(/\n$/, ""); const onCopy = async () => { try { await navigator.clipboard.writeText(codeText); setCopied(true); message.success("代码已复制"); window.setTimeout(() => setCopied(false), 1200); } catch { message.error("复制失败"); } }; return (
{props.children}
); } export default function StreamMessage(props: StreamMessageProps) { return (
{children}, table: ({ children }) => (
{children}
), th: ({ children }) => ( {children} ), td: ({ children }) => ( {children} ), blockquote: ({ children }) => (
{children}
), }} > {props.content}
); }