// 工作时间线:思考、对用户的解说、同类工具合并成一行。运行中展开; // 终轮结束后由调用方套进「已工作 X」折叠。 import type { ReactNode } from 'react' import { Collapsible, ToolBlock } from '@ui' import type { Part } from '../contracts' import { formatDuration, humanSize } from '../messageparts' import { timelineEntries, toolGroupLabel, toolTail, type ToolGroupItem } from '../toolkind' export function ProcessTimeline({ parts, live, idKey, renderPart }: { parts: Part[] live: boolean idKey: string renderPart: (part: Part, liveText: boolean) => ReactNode }) { const rows = timelineEntries(parts, live) return (
{rows.map((row, i) => { if (row.kind === 'thinking') { const dur = formatDuration(row.durationMs) const label = row.active ? '思考中' : (dur ? `思考 · 持续了 ${dur}` : '思考') return (
▸ {label}}>
{row.text}
) } if (row.kind === 'text') { return
{renderPart({ type: 'text', text: row.text }, false)}
} if (row.kind === 'check') { const text = row.passed ? '终轮检查通过' : `终轮检查未通过:${row.reason}` return
{text}
} if (row.kind === 'toolblock') { return } if (row.kind === 'tools') { const running = row.items.some(it => it.running) return (
{toolGroupLabel(row.toolKind, row.items.length, running)}
    {row.items.map((it, j) => (
  • {it.headline} {it.status === 'error' && 失败} {it.summary && {it.summary}}
  • ))}
) } return
{renderPart(row.part, false)}
})}
) } function sizeLabel(n: number): string { if (n < 1024) return `${n} 字符` return humanSize(n) } function ToolCallBlock({ item }: { item: ToolGroupItem }) { const composing = item.phase === 'composing' const tail = item.running ? toolTail(item.arguments, item.output) : '' const dur = !item.running ? formatDuration(item.durationMs) : '' const detail = composing ? undefined : (dur ? `${item.headline} · ${dur}` : item.headline) return (
{item.arguments &&
{item.arguments}
} {item.output &&
{item.output}
}
{tail &&
{tail}
}
) }