import { Button } from "@/components/ui/button"; import { X } from "lucide-react"; import { Project } from "@/lib/api"; import { getToolIcon } from "@/components/thread/utils"; import React from "react"; import { Slider } from "@/components/ui/slider"; // Import tool view components from the tool-views directory import { CommandToolView } from "./tool-views/CommandToolView"; import { StrReplaceToolView } from "./tool-views/StrReplaceToolView"; import { GenericToolView } from "./tool-views/GenericToolView"; import { FileOperationToolView } from "./tool-views/FileOperationToolView"; import { BrowserToolView } from "./tool-views/BrowserToolView"; import { WebSearchToolView } from "./tool-views/WebSearchToolView"; import { WebCrawlToolView } from "./tool-views/WebCrawlToolView"; // Simple input interface export interface ToolCallInput { assistantCall: { content?: string; name?: string; timestamp?: string; }; toolResult?: { content?: string; isSuccess?: boolean; timestamp?: string; }; } // Get the specialized tool view component based on the tool name function getToolView( toolName: string | undefined, assistantContent: string | undefined, toolContent: string | undefined, assistantTimestamp: string | undefined, toolTimestamp: string | undefined, isSuccess: boolean = true, project?: Project ) { if (!toolName) return null; const normalizedToolName = toolName.toLowerCase(); switch (normalizedToolName) { case 'execute-command': return ( ); case 'str-replace': return ( ); case 'create-file': case 'full-file-rewrite': case 'delete-file': return ( ); case 'browser-navigate': case 'browser-click': case 'browser-extract': case 'browser-fill': case 'browser-wait': return ( ); case 'web-search': return ( ); case 'web-crawl': return ( ); default: // Check if it's a browser operation if (normalizedToolName.startsWith('browser-')) { return ( ); } // Fallback to generic view return ( ); } } interface ToolCallSidePanelProps { isOpen: boolean; onClose: () => void; toolCalls: ToolCallInput[]; currentIndex: number; onNavigate: (newIndex: number) => void; project?: Project; renderAssistantMessage?: (assistantContent?: string, toolContent?: string) => React.ReactNode; renderToolResult?: (toolContent?: string, isSuccess?: boolean) => React.ReactNode; } export function ToolCallSidePanel({ isOpen, onClose, toolCalls, currentIndex, onNavigate, project, renderAssistantMessage, renderToolResult }: ToolCallSidePanelProps) { if (!isOpen) return null; const currentToolCall = toolCalls[currentIndex]; const totalCalls = toolCalls.length; const currentToolName = currentToolCall?.assistantCall?.name || 'Tool Call'; const CurrentToolIcon = getToolIcon(currentToolName === 'Tool Call' ? 'unknown' : currentToolName); // Determine if this is a streaming tool call const isStreaming = currentToolCall?.toolResult?.content === "STREAMING"; // Set up a pulse animation for streaming const [dots, setDots] = React.useState(''); React.useEffect(() => { if (!isStreaming) return; // Create a loading animation with dots const interval = setInterval(() => { setDots(prev => { if (prev === '...') return ''; return prev + '.'; }); }, 500); return () => clearInterval(interval); }, [isStreaming]); const renderContent = () => { if (!currentToolCall) { return (

No tool call details available.

); } // Get the specific tool view based on the tool name return getToolView( currentToolCall.assistantCall.name, currentToolCall.assistantCall.content, currentToolCall.toolResult?.content, currentToolCall.assistantCall.timestamp, currentToolCall.toolResult?.timestamp, isStreaming ? true : (currentToolCall.toolResult?.isSuccess ?? true), project ); }; return (

{isStreaming ? `Suna's Computer (Running${dots})` : "Suna's Computer"}

{renderContent()}
{totalCalls > 1 && (
{currentToolName} {isStreaming && `(Running${dots})`}
Step {currentIndex + 1} of {totalCalls}
onNavigate(newValue)} className="w-full [&>span:first-child]:h-1.5 [&>span:first-child>span]:h-1.5" />
)}
); }