import { memo, useCallback, useRef, useState } from "react"; import ReactMarkdown, { type Components } from "react-markdown"; import remarkGfm from "remark-gfm"; import "./markdown.web.css"; import { type ChatMarkdownProps, closeUnterminatedFence, sanitizeMarkdownUrl } from "./markdown"; function CopyIcon() { return ( ); } function CheckIcon() { return ( ); } function CodeBlock(props: React.ComponentPropsWithoutRef<"pre">) { const preRef = useRef(null); const resetTimerRef = useRef(undefined); const [copied, setCopied] = useState(false); const handleCopy = useCallback(() => { if (!navigator.clipboard) return; const text = preRef.current?.textContent ?? ""; navigator.clipboard .writeText(text) .then(() => { setCopied(true); window.clearTimeout(resetTimerRef.current); resetTimerRef.current = window.setTimeout(() => setCopied(false), 1500); }) .catch(() => {}); }, []); return (
      
    
); } const components: Components = { a({ node: _node, ...props }) { return ; }, img({ node: _node, ...props }) { return {props.alt; }, pre({ node: _node, ...props }) { return ; }, }; export const ChatMarkdown = memo(function ChatMarkdown({ children, streaming = false, }: ChatMarkdownProps) { const source = streaming ? closeUnterminatedFence(children) : children; return (
sanitizeMarkdownUrl(url, true) ?? ""} > {source} {streaming ?
); }); export type { ChatMarkdownProps } from "./markdown";