/** * TraceStep — 单个 Trace 步骤 */ import { useState } from 'react'; import { Box, Typography, IconButton, Collapse, Stack } from '@mui/material'; import { ChevronDown, ChevronRight, CircleDot, CheckCircle, Loader2 } from 'lucide-react'; import { TRACE_STATE_COLORS, TRACE_STATE_LABELS } from '@renderer/lib/constants'; import { formatDuration, formatTokens } from '@renderer/lib/formatters'; import type { TraceStep as TraceStepType } from '@renderer/stores/agent-store'; interface TraceStepProps { step: TraceStepType; isCurrent?: boolean; } export function TraceStep({ step, isCurrent }: TraceStepProps): React.JSX.Element { const [expanded, setExpanded] = useState(false); const color = TRACE_STATE_COLORS[step.state] ?? '#8b8fa7'; const label = TRACE_STATE_LABELS[step.state] ?? step.state; const duration = step.completedAt ? step.completedAt - step.startedAt : null; return ( setExpanded(!expanded)} sx={{ width: '100%', justifyContent: 'flex-start', gap: 1, px: 1.5, py: 1, borderRadius: '4px 4px 0 0', color: 'text.primary', fontSize: 12 }}> {expanded ? : } {isCurrent ? : step.completedAt ? : } #{step.iteration} {label} {duration != null && {formatDuration(duration)}} {step.tokenUsage && {formatTokens(step.tokenUsage.totalTokens)} tok} {step.thought && ( 💭 Thought {step.thought} )} {step.toolCalls?.map((tc) => ( 🔧 {tc.name} {JSON.stringify(tc.args, null, 2)} ))} ); }