import { useMemo, useState } from 'react' import { CodeViewer } from './CodeViewer' import { DiffViewer } from './DiffViewer' import { TerminalChrome } from './TerminalChrome' import { CopyButton } from '../shared/CopyButton' type Props = { toolName: string input: unknown result?: { content: unknown; isError: boolean } | null compact?: boolean } const TOOL_ICONS: Record = { Bash: 'terminal', Read: 'description', Write: 'edit_document', Edit: 'edit_note', Glob: 'search', Grep: 'find_in_page', Agent: 'smart_toy', WebSearch: 'travel_explore', WebFetch: 'cloud_download', NotebookEdit: 'note', Skill: 'auto_awesome', } export function ToolCallBlock({ toolName, input, result, compact = false }: Props) { const [expanded, setExpanded] = useState(false) const obj = input && typeof input === 'object' ? (input as Record) : {} const icon = TOOL_ICONS[toolName] || 'build' const filePath = typeof obj.file_path === 'string' ? obj.file_path : '' const summary = getToolSummary(toolName, obj) const outputSummary = getToolResultSummary(toolName, result?.content) const preview = useMemo(() => renderPreview(toolName, obj, result), [obj, result, toolName]) const details = useMemo(() => renderDetails(toolName, obj), [obj, toolName]) const expandable = toolName === 'Edit' || toolName === 'Write' return (
{expandable && expanded && (
{preview} {details}
)}
) } function renderPreview( toolName: string, obj: Record, result?: { content: unknown; isError: boolean } | null, ) { const filePath = typeof obj.file_path === 'string' ? obj.file_path : 'file' if (toolName === 'Edit' && typeof obj.old_string === 'string' && typeof obj.new_string === 'string') { return } if (toolName === 'Write' && typeof obj.content === 'string') { return } if (toolName === 'Bash' && typeof obj.command === 'string') { return (
$ {obj.command}
) } if (toolName === 'Read') { return null } if (result) { const text = extractTextContent(result.content) if (text) { return (
{result.isError ? 'Error Output' : 'Tool Output'}
) } } return null } function renderDetails(toolName: string, obj: Record) { if (toolName === 'Edit' || toolName === 'Write') { return null } const text = JSON.stringify(obj, null, 2) return (
Tool Input
) } function getToolResultSummary(toolName: string, content: unknown): string { if (toolName === 'Bash') return '' const text = extractTextContent(content) if (!text) return '' const lineCount = text.split('\n').length if (lineCount > 1) { return `${lineCount} lines output` } const compact = text.replace(/\s+/g, ' ').trim() if (!compact) return '' if (compact.length <= 36) return compact return `${compact.slice(0, 36)}…` } function getToolSummary(toolName: string, obj: Record): string { switch (toolName) { case 'Bash': return typeof obj.command === 'string' ? obj.command : '' case 'Read': return typeof obj.limit === 'number' ? `Read file contents` : 'Read file contents' case 'Write': return typeof obj.content === 'string' ? `${obj.content.split('\n').length} lines created` : 'Create file' case 'Edit': return typeof obj.old_string === 'string' && typeof obj.new_string === 'string' ? changedLineSummary(obj.old_string, obj.new_string) : 'Update file contents' case 'Glob': return typeof obj.pattern === 'string' ? obj.pattern : '' case 'Grep': return typeof obj.pattern === 'string' ? obj.pattern : '' case 'Agent': return typeof obj.description === 'string' ? obj.description : '' default: return '' } } function extractTextContent(content: unknown): string | null { if (typeof content === 'string') return content if (Array.isArray(content)) { return content .map((chunk: any) => (typeof chunk === 'string' ? chunk : chunk?.text || '')) .filter(Boolean) .join('\n') } if (content && typeof content === 'object') { return JSON.stringify(content, null, 2) } return null } function changedLineSummary(oldString: string, newString: string): string { const oldLines = oldString.split('\n') const newLines = newString.split('\n') let changed = 0 const max = Math.max(oldLines.length, newLines.length) for (let index = 0; index < max; index += 1) { if ((oldLines[index] ?? '') !== (newLines[index] ?? '')) { changed += 1 } } return `${changed} lines changed` }