import type { MessageSegment } from "./chat-data"; export interface SegmentView { readonly segment: MessageSegment; readonly isExpanded: boolean; readonly displayTitle: string; readonly displayStatus: string; readonly statusColor: string; readonly canExpand: boolean; readonly formattedContent?: string; readonly metadata?: SegmentMetadata; } export interface SegmentMetadata { readonly toolType?: "todo" | "console" | "file" | "default"; readonly fileName?: string; readonly lineCount?: number; readonly errorMessage?: string; readonly duration?: number; } export interface TodoListView { readonly tasks: TodoTask[]; readonly completedCount: number; readonly totalCount: number; readonly lastUpdated: number; } export interface TodoTask { readonly id: number; readonly description: string; readonly status: "pending" | "in_progress" | "completed"; readonly emoji: string; } export function createSegmentView( segment: MessageSegment, isExpanded: boolean = false, ): SegmentView { const statusColors: Record = { pending: "rgba(255, 193, 7, 0.8)", running: "rgba(33, 150, 243, 0.8)", completed: "rgba(76, 175, 80, 0.8)", error: "rgba(244, 67, 54, 0.8)", }; const displayTitle = getSegmentTitle(segment); const displayStatus = getSegmentStatus(segment); const statusColor = statusColors[segment.toolStatus || "pending"] || "rgba(156, 163, 175, 0.8)"; const canExpand = segment.type === "tool-invocation" || segment.type === "tool-result"; return { segment, isExpanded, displayTitle, displayStatus, statusColor, canExpand, metadata: extractMetadata(segment), }; } function getSegmentTitle(segment: MessageSegment): string { if (segment.type === "text") { return "Text"; } if (segment.type === "reasoning") { return "Reasoning"; } if (segment.toolName) { const toolNames: Record = { plan_tasks: "📋 Plan Tasks", update_task: "✏️ Update Task", view_tasks: "👀 View Tasks", observe_console: "📺 Console Output", }; return toolNames[segment.toolName] || `🔧 ${segment.toolName}`; } return "Tool"; } function getSegmentStatus(segment: MessageSegment): string { if (segment.streaming) { return "streaming..."; } if (segment.toolStatus) { const statusLabels: Record = { pending: "Pending", running: "Running", completed: "Completed", error: "Error", }; return statusLabels[segment.toolStatus] || segment.toolStatus; } if (segment.endTime && segment.startTime) { const duration = segment.endTime - segment.startTime; if (duration < 1000) { return `${duration}ms`; } return `${(duration / 1000).toFixed(1)}s`; } return ""; } function extractMetadata(segment: MessageSegment): SegmentMetadata { let toolType: SegmentMetadata["toolType"]; let fileName: string | undefined; let errorMessage: string | undefined; let duration: number | undefined; let lineCount: number | undefined; if (segment.toolName?.includes("task")) { toolType = "todo"; } else if (segment.toolName === "observe_console") { toolType = "console"; } else if (segment.toolName?.includes("file")) { toolType = "file"; if (segment.toolArgs?.path) { fileName = segment.toolArgs.path as string; } } else if (segment.toolName) { toolType = "default"; } if (segment.toolError) { errorMessage = segment.toolError; } if (segment.endTime && segment.startTime) { duration = segment.endTime - segment.startTime; } if (segment.toolOutput) { lineCount = segment.toolOutput.split("\n").length; } return { toolType, fileName, errorMessage, duration, lineCount, }; } export function parseTodoList(content: string): TodoListView | null { const lines = content.split("\n"); const tasks: TodoTask[] = []; for (const line of lines) { const match = line.match( /([⏳🔄✅])\s*\[(\d+)\]\s*(.+?)\s*\((pending|in_progress|completed)\)/u, ); if (match) { const [, emoji, id, description, status] = match; tasks.push({ id: parseInt(id, 10), description: description.trim(), status: status as TodoTask["status"], emoji, }); } } if (tasks.length === 0) { return null; } const completedCount = tasks.filter((t) => t.status === "completed").length; return { tasks, completedCount, totalCount: tasks.length, lastUpdated: Date.now(), }; }