import React, { useState, useEffect } from 'react'; import { CheckCircle2, CheckCircle, AlertTriangle, Loader2, ListChecks, Sparkles, Trophy, Paperclip, ExternalLink, } from 'lucide-react'; import { ToolViewProps } from './types'; import { formatTimestamp, getToolTitle, normalizeContentToString, extractToolData, getFileIconAndColor, } from './utils'; import { cn } from '@/lib/utils'; import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'; import { Badge } from '@/components/ui/badge'; import { ScrollArea } from "@/components/ui/scroll-area"; import { Progress } from '@/components/ui/progress'; import { Markdown } from '@/components/ui/markdown'; interface CompleteContent { summary?: string; result?: string; tasksCompleted?: string[]; finalOutput?: string; attachments?: string[]; } interface CompleteToolViewProps extends ToolViewProps { onFileClick?: (filePath: string) => void; } export function CompleteToolView({ name = 'complete', assistantContent, toolContent, assistantTimestamp, toolTimestamp, isSuccess = true, isStreaming = false, onFileClick, }: CompleteToolViewProps) { const [completeData, setCompleteData] = useState({}); const [progress, setProgress] = useState(0); // Extract completion summary and attachments from assistant content useEffect(() => { if (assistantContent) { try { const contentStr = normalizeContentToString(assistantContent); // Try to extract content from tag const completeMatch = contentStr.match(/]*>([^<]*)<\/complete>/); if (completeMatch) { setCompleteData(prev => ({ ...prev, summary: completeMatch[1].trim() })); } else { // Fallback: use the whole content as summary setCompleteData(prev => ({ ...prev, summary: contentStr })); } // Extract attachments if present const attachmentsMatch = contentStr.match(/attachments=["']([^"']*)["']/i); if (attachmentsMatch) { const attachments = attachmentsMatch[1].split(',').map(a => a.trim()).filter(a => a.length > 0); setCompleteData(prev => ({ ...prev, attachments })); } // Try to extract any task list items const taskMatches = contentStr.match(/- ([^\n]+)/g); if (taskMatches) { const tasks = taskMatches.map(task => task.replace('- ', '').trim()); setCompleteData(prev => ({ ...prev, tasksCompleted: tasks })); } } catch (e) { console.error('Error parsing complete content:', e); } } }, [assistantContent]); // Extract result from tool content useEffect(() => { if (toolContent && !isStreaming) { try { const contentStr = normalizeContentToString(toolContent); // Try to extract from ToolResult pattern const toolResultMatch = contentStr.match(/ToolResult\([^)]*output=['"]([^'"]+)['"]/); if (toolResultMatch) { setCompleteData(prev => ({ ...prev, result: toolResultMatch[1] })); } else { // Fallback: use the content directly setCompleteData(prev => ({ ...prev, result: contentStr })); } } catch (e) { console.error('Error parsing tool response:', e); } } }, [toolContent, isStreaming]); // Simulate progress when streaming useEffect(() => { if (isStreaming) { const timer = setInterval(() => { setProgress((prevProgress) => { if (prevProgress >= 95) { clearInterval(timer); return prevProgress; } return prevProgress + 5; }); }, 300); return () => clearInterval(timer); } else { setProgress(100); } }, [isStreaming]); const toolTitle = getToolTitle(name) || 'Task Complete'; const handleFileClick = (filePath: string) => { if (onFileClick) { onFileClick(filePath); } }; return (
{toolTitle}
{!isStreaming && ( {isSuccess ? ( ) : ( )} {isSuccess ? 'Completed' : 'Failed'} )} {isStreaming && ( Completing )}
{/* Success Animation/Icon - Only show when completed successfully */} {!isStreaming && isSuccess && !completeData.summary && !completeData.tasksCompleted && !completeData.attachments && (
)} {/* Summary Section */} {completeData.summary && (
{completeData.summary}
)} {/* Attachments Section */} {completeData.attachments && completeData.attachments.length > 0 && (
Files ({completeData.attachments.length})
{completeData.attachments.map((attachment, index) => { const { icon: FileIcon, color, bgColor } = getFileIconAndColor(attachment); const fileName = attachment.split('/').pop() || attachment; const filePath = attachment.includes('/') ? attachment.substring(0, attachment.lastIndexOf('/')) : ''; return ( ); })}
)} {/* Tasks Completed Section */} {completeData.tasksCompleted && completeData.tasksCompleted.length > 0 && (
Tasks Completed
{completeData.tasksCompleted.map((task, index) => (
{task}
))}
)} {/* Progress Section for Streaming */} {isStreaming && (
Completing task... {progress}%
)} {/* Empty State */} {!completeData.summary && !completeData.result && !completeData.attachments && !completeData.tasksCompleted && !isStreaming && (

Task Completed

No additional details provided

)}
{/* Footer */}
Task Completion
{toolTimestamp && !isStreaming ? formatTimestamp(toolTimestamp) : assistantTimestamp ? formatTimestamp(assistantTimestamp) : ''}
); }