import { useEffect } from 'react'; import { createPortal } from 'react-dom'; import { X, Terminal, Clock, Maximize2 } from 'lucide-react'; import ReactMarkdown from 'react-markdown'; import remarkGfm from 'remark-gfm'; // 模块级常量:保持引用稳定,避免每次渲染重建数组 const REMARK_PLUGINS = [remarkGfm]; interface ToolDetailModalProps { toolName: string; status: string; statusLabel: string; arguments?: unknown; resultContent: string; callContent: string; durationMs?: number; onClose: () => void; } function formatDuration(ms: number): string { if (ms < 1000) return `${ms}ms`; if (ms < 60000) return `${(ms / 1000).toFixed(1)}s`; const minutes = Math.floor(ms / 60000); const seconds = Math.floor((ms % 60000) / 1000); return `${minutes}m ${seconds}s`; } function formatJSON(text: string): string { if (!text) return ''; try { const parsed = JSON.parse(text); return JSON.stringify(parsed, null, 2); } catch { return text; } } export function ToolDetailModal({ toolName, status, statusLabel, arguments: args, resultContent, callContent, durationMs, onClose, }: ToolDetailModalProps) { useEffect(() => { const handleKeyDown = (e: KeyboardEvent) => { if (e.key === 'Escape') onClose(); }; document.addEventListener('keydown', handleKeyDown); return () => document.removeEventListener('keydown', handleKeyDown); }, [onClose]); const displayContent = resultContent || callContent; const formattedContent = formatJSON(displayContent); const statusColor = status === 'calling' ? 'var(--accent-amber)' : status === 'result' ? 'var(--accent-green)' : status === 'pending' ? 'var(--accent-amber)' : 'var(--text-muted)'; // portal 到 body:在虚拟化消息行内打开时,行元素的 transform 会成为 // fixed 后代的包含块,遮罩/定位全部失效(同 ImageLightbox)。 // z-[10000]:高于下拉选择器(z-[9999])等全局浮层。 return createPortal(
{JSON.stringify(args, null, 2)}