import { useState } from "react"; interface TimelineEvent { id: string; type: "action" | "observation" | "message" | "error" | "status"; actionType?: "run" | "edit" | "think" | "browse" | "task" | string; title: string; detail?: string; timestamp: Date; duration?: number; status?: "running" | "done" | "error" | "skipped"; } interface AgentTimelineProps { events: TimelineEvent[]; } const ACTION_ICONS: Record = { run: ( ), edit: ( ), think: ( ), browse: ( ), task: ( ), }; function getIcon(event: TimelineEvent): React.ReactNode { if (event.type === "error") { return ( ); } if (event.type === "message") { return ( ); } return event.actionType ? (ACTION_ICONS[event.actionType] ?? ACTION_ICONS.task) : ACTION_ICONS.task; } function getColor(event: TimelineEvent): string { if (event.type === "error") return "var(--diff-del-text)"; if (event.status === "running") return "var(--accent)"; if (event.status === "done") return "var(--diff-add-text)"; if (event.status === "skipped") return "var(--text-quiet)"; return "var(--text-secondary)"; } export function AgentTimeline({ events }: AgentTimelineProps) { const [collapsed, setCollapsed] = useState(false); if (events.length === 0) return null; return (
{/* Header */} {/* Events */} {!collapsed && (
{events.map((event, idx) => ( ))}
)}
); } function TimelineRow({ event, isLast, isFirst, }: { event: TimelineEvent; isLast: boolean; isFirst: boolean; }) { const [showDetail, setShowDetail] = useState(false); const color = getColor(event); return (
{/* Timeline line + dot */}
{!isFirst && (
)}
{getIcon(event)}
{!isLast && (
)}
{/* Content */}
{event.duration && ( {event.duration}ms )} {event.status === "running" && ( )} {event.status === "done" && ( )} {event.status === "error" && ( )}
{showDetail && event.detail && (

{event.detail}

)}
); }