/* eslint-disable react-refresh/only-export-components -- renderer constant is co-located with its inline component by design */ import type { ToolResultBlock } from '@agentscope-ai/agentscope/message'; import { ChevronRight } from 'lucide-react'; import { useState } from 'react'; import { CornerLine, getFilePath, ToolStateIcon } from './_shared'; import type { TFunction, ToolCallWithResult, ToolRenderer } from './types'; import { Button } from '@/components/ui/button.tsx'; import { Collapsible, CollapsibleContent, CollapsibleTrigger } from '@/components/ui/collapsible'; import { formatNumber } from '@/utils/common'; /** Count lines in a tool result's output string, including text content from * non-text blocks. Returns 0 when the result is missing or empty. */ function countResultLines(result?: ToolResultBlock): number { if (!result) return 0; let str: string; if (typeof result.output === 'string') { str = result.output; } else { str = result.output.map((b) => (b.type === 'text' ? b.text : '')).join('\n'); } if (!str) return 0; return str.split('\n').length; } /** Collapse consecutive Read calls of the same `file_path` into one bucket so * the path is shown once, followed by one corner-row per call. Order is * preserved; a different path or a re-occurrence after another path starts a * new bucket. */ function groupByConsecutivePath( calls: ToolCallWithResult[], ): Array<{ path: string; calls: ToolCallWithResult[] }> { const groups: Array<{ path: string; calls: ToolCallWithResult[] }> = []; for (const item of calls) { const path = getFilePath(item.call.input); const last = groups[groups.length - 1]; if (last && last.path === path) { last.calls.push(item); } else { groups.push({ path, calls: [item] }); } } return groups; } function ReadGroup({ calls, t }: { calls: ToolCallWithResult[]; t: TFunction }) { const [open, setOpen] = useState(false); const name = t('tool.read.name'); return ( {groupByConsecutivePath(calls).map((group, gIdx) => (
{/*{name}*/} {/*({group.path})*/} {group.path}
{group.calls.map(({ call, result }) => { if (!result) return null; const lines = countResultLines(result); return (
{t('tool.read.lineCount', { count: lines, formatted: formatNumber(lines), })}
); })}
))}
); } export const ReadRenderer: ToolRenderer = { getDisplayName: (_call, t) => t('tool.read.name'), renderCallArgs: (call) => getFilePath(call.input), renderConfirmBody: (call) => (
{getFilePath(call.input)}
), renderGroup: (calls, t) => , };