suna/frontend/src/components/thread/tool-components.tsx

656 lines
25 KiB
TypeScript
Raw Normal View History

2025-04-13 00:37:45 +08:00
'use client';
import React from 'react';
2025-04-13 20:28:23 +08:00
import { ParsedTag, ToolComponentProps } from '@/lib/types/tool-calls';
2025-04-13 00:37:45 +08:00
import {
File, FileText, Terminal, FolderPlus, Folder, Code, Search as SearchIcon,
2025-04-16 02:07:31 +08:00
Bell, Replace, Plus, Minus, Globe, Search
2025-04-13 00:37:45 +08:00
} from 'lucide-react';
import { cn } from '@/lib/utils';
import { diffLines } from 'diff';
2025-04-13 20:28:23 +08:00
// Shared compact mode component
const CompactToolDisplay: React.FC<{
icon: React.ReactNode,
name: string,
input: string,
isRunning?: boolean
}> = ({ icon, name, input, isRunning }) => {
return (
<div className="flex items-center gap-2 px-2 py-1 text-xs bg-muted/30 rounded">
{icon}
<span className="font-medium">{name}: {input}</span>
{isRunning && (
<div className="flex items-center gap-1">
<span className="text-amber-500">Running</span>
<div className="h-2 w-2 rounded-full bg-amber-500 animate-pulse"></div>
</div>
)}
</div>
);
};
2025-04-13 00:37:45 +08:00
/**
2025-04-13 20:28:23 +08:00
* Create File Tool Component
2025-04-13 00:37:45 +08:00
*/
2025-04-13 20:28:23 +08:00
export const CreateFileTool: React.FC<ToolComponentProps> = ({ tag, mode }) => {
const filePath = tag.attributes.file_path || '';
const fileContent = tag.content || '';
2025-04-13 00:37:45 +08:00
const isRunning = tag.status === 'running';
2025-04-13 20:28:23 +08:00
if (mode === 'compact') {
return (
<CompactToolDisplay
icon={<FileText className="h-4 w-4 mr-2" />}
name={isRunning ? "Creating file" : "Created file"}
input={filePath}
isRunning={isRunning}
/>
);
}
2025-04-13 00:37:45 +08:00
return (
<div className="border rounded-lg overflow-hidden border-subtle dark:border-white/10">
2025-04-13 20:28:23 +08:00
<div className="flex items-center px-2 py-1 text-xs font-medium border-b border-subtle dark:border-white/10 bg-background-secondary dark:bg-background-secondary text-foreground">
<FileText className="h-4 w-4 mr-2" />
<div className="flex-1">{isRunning ? "Creating file" : "Created file"}: {filePath}</div>
2025-04-13 00:37:45 +08:00
{isRunning && (
<div className="flex items-center gap-2">
<span className="text-amber-500">Running</span>
<div className="h-2 w-2 rounded-full bg-amber-500 animate-pulse"></div>
</div>
)}
</div>
2025-04-13 20:28:23 +08:00
<div className="p-3 bg-card-bg dark:bg-background-secondary text-foreground">
<div className="space-y-2">
<div className="flex items-center gap-1 text-xs text-muted-foreground mb-1">
<FileText className="h-3 w-3" />
<span className="font-mono">{filePath}</span>
</div>
<pre className="whitespace-pre-wrap bg-muted p-2 rounded-md text-xs overflow-x-auto max-h-60">
{fileContent}
</pre>
2025-04-13 00:37:45 +08:00
</div>
</div>
2025-04-13 20:28:23 +08:00
</div>
2025-04-13 00:37:45 +08:00
);
};
/**
* Full File Rewrite Tool Component
*/
export const FullFileRewriteTool: React.FC<ToolComponentProps> = ({ tag, mode }) => {
const filePath = tag.attributes.file_path || '';
const fileContent = tag.content || '';
2025-04-13 20:28:23 +08:00
const isRunning = tag.status === 'running';
2025-04-13 00:37:45 +08:00
2025-04-13 20:28:23 +08:00
if (mode === 'compact') {
return (
<CompactToolDisplay
icon={<FileText className="h-4 w-4 mr-2" />}
name={isRunning ? "Rewriting file" : "Rewrote file"}
input={filePath}
isRunning={isRunning}
/>
);
}
2025-04-13 00:37:45 +08:00
return (
2025-04-13 20:28:23 +08:00
<div className="border rounded-lg overflow-hidden border-subtle dark:border-white/10">
<div className="flex items-center px-2 py-1 text-xs font-medium border-b border-subtle dark:border-white/10 bg-background-secondary dark:bg-background-secondary text-foreground">
<FileText className="h-4 w-4 mr-2" />
<div className="flex-1">{isRunning ? "Rewriting file" : "Rewrote file"}: {filePath}</div>
{isRunning && (
<div className="flex items-center gap-2">
<span className="text-amber-500">Running</span>
<div className="h-2 w-2 rounded-full bg-amber-500 animate-pulse"></div>
2025-04-13 00:37:45 +08:00
</div>
2025-04-13 20:28:23 +08:00
)}
2025-04-13 00:37:45 +08:00
</div>
2025-04-13 20:28:23 +08:00
<div className="p-3 bg-card-bg dark:bg-background-secondary text-foreground">
<div className="space-y-2">
<div className="flex items-center gap-1 text-xs text-muted-foreground mb-1">
<FileText className="h-3 w-3" />
<span className="font-mono">{filePath}</span>
</div>
<pre className="whitespace-pre-wrap bg-muted p-2 rounded-md text-xs overflow-x-auto max-h-60">
{fileContent}
</pre>
</div>
</div>
</div>
2025-04-13 00:37:45 +08:00
);
};
/**
* Read File Tool Component
*/
export const ReadFileTool: React.FC<ToolComponentProps> = ({ tag, mode }) => {
const filePath = tag.attributes.file_path || '';
const fileContent = tag.content || '';
2025-04-13 20:28:23 +08:00
const isRunning = tag.status === 'running';
2025-04-13 00:37:45 +08:00
2025-04-13 20:28:23 +08:00
if (mode === 'compact') {
return (
<CompactToolDisplay
icon={<File className="h-4 w-4 mr-2" />}
name={isRunning ? "Reading file" : "Read file"}
input={filePath}
isRunning={isRunning}
/>
);
}
2025-04-13 00:37:45 +08:00
return (
2025-04-13 20:28:23 +08:00
<div className="border rounded-lg overflow-hidden border-subtle dark:border-white/10">
<div className="flex items-center px-2 py-1 text-xs font-medium border-b border-subtle dark:border-white/10 bg-background-secondary dark:bg-background-secondary text-foreground">
<File className="h-4 w-4 mr-2" />
<div className="flex-1">{isRunning ? "Reading file" : "Read file"}: {filePath}</div>
{isRunning && (
<div className="flex items-center gap-2">
<span className="text-amber-500">Running</span>
<div className="h-2 w-2 rounded-full bg-amber-500 animate-pulse"></div>
</div>
)}
</div>
<div className="p-3 bg-card-bg dark:bg-background-secondary text-foreground">
<div className="space-y-2">
<div className="flex items-center gap-1 text-xs text-muted-foreground mb-1">
<File className="h-3 w-3" />
<span className="font-mono">{filePath}</span>
</div>
<pre className="whitespace-pre-wrap bg-muted p-2 rounded-md text-xs overflow-x-auto max-h-60">
{fileContent}
</pre>
2025-04-13 00:37:45 +08:00
</div>
</div>
2025-04-13 20:28:23 +08:00
</div>
2025-04-13 00:37:45 +08:00
);
};
/**
* Execute Command Tool Component
*/
export const ExecuteCommandTool: React.FC<ToolComponentProps> = ({ tag, mode }) => {
const output = tag.content || '';
2025-04-13 20:28:23 +08:00
const command = tag.resultTag?.content || '';
2025-04-13 00:37:45 +08:00
const isRunning = tag.status === 'running';
2025-04-13 20:28:23 +08:00
if (mode === 'compact') {
return (
<CompactToolDisplay
icon={<Terminal className="h-4 w-4 mr-2" />}
name={isRunning ? "Executing" : "Executed"}
input={tag.content} // in compact mode, the command is in the content
isRunning={isRunning}
/>
);
}
2025-04-13 00:37:45 +08:00
return (
2025-04-13 20:28:23 +08:00
<div className="border rounded-lg overflow-hidden border-subtle dark:border-white/10">
<div className="flex items-center px-2 py-1 text-xs font-medium border-b border-subtle dark:border-white/10 bg-background-secondary dark:bg-background-secondary text-foreground">
<Terminal className="h-4 w-4 mr-2" />
<div className="flex-1">{isRunning ? `Executing: ${command}` : `Executed: ${command}`}</div>
{isRunning && (
<div className="flex items-center gap-2">
<span className="text-amber-500">Running</span>
<div className="h-2 w-2 rounded-full bg-amber-500 animate-pulse"></div>
</div>
)}
</div>
<div className="p-3 bg-card-bg dark:bg-background-secondary text-foreground">
<div className="font-mono text-xs bg-black text-green-400 p-3 rounded-md">
<div className="flex items-center gap-2 mb-2 text-white/80">
<div className="w-3 h-3 rounded-full bg-red-500" />
<div className="w-3 h-3 rounded-full bg-yellow-500" />
<div className="w-3 h-3 rounded-full bg-green-500" />
<div className="flex-1 text-center text-xs">Terminal</div>
</div>
<div className="space-y-1">
<div className="flex items-start gap-2">
<span className="text-blue-400">user@localhost</span>
<span className="text-white/60">:</span>
<span className="text-purple-400">~</span>
<span className="text-white/60">$</span>
<span>{command}</span>
</div>
2025-04-13 00:37:45 +08:00
{output && (
2025-04-13 20:28:23 +08:00
<pre className="whitespace-pre-wrap pl-4 text-white/90">{output}</pre>
2025-04-13 00:37:45 +08:00
)}
2025-04-13 20:28:23 +08:00
{!isRunning && (
<div className="flex items-start gap-2">
<span className="text-blue-400">user@localhost</span>
<span className="text-white/60">:</span>
<span className="text-purple-400">~</span>
<span className="text-white/60">$</span>
<span className="animate-pulse"></span>
2025-04-13 00:37:45 +08:00
</div>
2025-04-13 20:28:23 +08:00
)}
2025-04-13 00:37:45 +08:00
</div>
2025-04-13 20:28:23 +08:00
</div>
2025-04-13 00:37:45 +08:00
</div>
2025-04-13 20:28:23 +08:00
</div>
2025-04-13 00:37:45 +08:00
);
};
2025-04-13 20:28:23 +08:00
2025-04-13 00:37:45 +08:00
/**
* String Replace Tool Component
*/
export const StringReplaceTool: React.FC<ToolComponentProps> = ({ tag, mode }) => {
const content = tag.content || '';
2025-04-13 20:28:23 +08:00
const isRunning = tag.status === 'running';
2025-04-13 00:37:45 +08:00
// Parse the old and new strings from the content
const oldStrMatch = content.match(/<old_str>([\s\S]*?)<\/old_str>/);
const newStrMatch = content.match(/<new_str>([\s\S]*?)<\/new_str>/);
const oldStr = oldStrMatch ? oldStrMatch[1] : '';
const newStr = newStrMatch ? newStrMatch[1] : '';
// Calculate the diff between old and new strings
const diff = diffLines(oldStr, newStr);
interface DiffPart {
added?: boolean;
removed?: boolean;
value: string;
}
2025-04-13 20:28:23 +08:00
if (mode === 'compact') {
return (
<CompactToolDisplay
icon={<Replace className="h-4 w-4 mr-2" />}
name={isRunning ? "Updating file" : "Updated file"}
input=""
isRunning={isRunning}
/>
);
}
2025-04-13 00:37:45 +08:00
return (
2025-04-13 20:28:23 +08:00
<div className="border rounded-lg overflow-hidden border-subtle dark:border-white/10">
<div className="flex items-center px-2 py-1 text-xs font-medium border-b border-subtle dark:border-white/10 bg-background-secondary dark:bg-background-secondary text-foreground">
<Replace className="h-4 w-4 mr-2" />
<div className="flex-1">{isRunning ? "Updating file" : "Updated file"}</div>
{isRunning && (
<div className="flex items-center gap-2">
<span className="text-amber-500">Running</span>
<div className="h-2 w-2 rounded-full bg-amber-500 animate-pulse"></div>
</div>
)}
</div>
<div className="p-3 bg-card-bg dark:bg-background-secondary text-foreground">
<div className="space-y-2">
<div className="border border-subtle dark:border-white/10 rounded-md overflow-hidden font-mono text-xs">
{diff.map((part: DiffPart, index: number) => (
<div
key={index}
className={cn(
"px-2 py-0.5 flex items-start",
part.added ? "bg-green-500/10 text-green-700 dark:text-green-400" :
part.removed ? "bg-red-500/10 text-red-700 dark:text-red-400" :
"text-foreground"
)}
>
<span className="mr-2">
{part.added ? <Plus className="h-3 w-3" /> :
part.removed ? <Minus className="h-3 w-3" /> :
<span className="w-3" />}
</span>
<pre className="whitespace-pre-wrap flex-1">{part.value}</pre>
</div>
))}
</div>
2025-04-13 00:37:45 +08:00
</div>
</div>
2025-04-13 20:28:23 +08:00
</div>
2025-04-13 00:37:45 +08:00
);
};
/**
* Notification Tool Component
*/
export const NotifyTool: React.FC<ToolComponentProps> = ({ tag, mode }) => {
const message = tag.attributes.message || '';
const type = tag.attributes.type || 'info';
2025-04-13 20:28:23 +08:00
const isRunning = tag.status === 'running';
2025-04-13 00:37:45 +08:00
2025-04-13 20:28:23 +08:00
if (mode === 'compact') {
return (
<CompactToolDisplay
icon={<Bell className="h-4 w-4 mr-2" />}
name={isRunning ? "Sending notification" : "Sent notification"}
input={message.substring(0, 30) + (message.length > 30 ? '...' : '')}
isRunning={isRunning}
/>
);
}
2025-04-13 00:37:45 +08:00
return (
2025-04-13 20:28:23 +08:00
<div className="border rounded-lg overflow-hidden border-subtle dark:border-white/10">
<div className="flex items-center px-2 py-1 text-xs font-medium border-b border-subtle dark:border-white/10 bg-background-secondary dark:bg-background-secondary text-foreground">
<Bell className="h-4 w-4 mr-2" />
<div className="flex-1">{isRunning ? "Sending notification" : "Sent notification"}: {message.substring(0, 30)}{message.length > 30 ? '...' : ''}</div>
{isRunning && (
<div className="flex items-center gap-2">
<span className="text-amber-500">Running</span>
<div className="h-2 w-2 rounded-full bg-amber-500 animate-pulse"></div>
</div>
)}
</div>
<div className="p-3 bg-card-bg dark:bg-background-secondary text-foreground">
<div className={cn(
"p-2 rounded-md",
type === 'error' ? 'bg-red-500/10 text-red-600 dark:text-red-400' :
type === 'warning' ? 'bg-amber-500/10 text-amber-600 dark:text-amber-400' :
type === 'success' ? 'bg-green-500/10 text-green-600 dark:text-green-400' :
'bg-blue-500/10 text-blue-600 dark:text-blue-400'
)}>
{message}
</div>
2025-04-13 00:37:45 +08:00
</div>
2025-04-13 20:28:23 +08:00
</div>
2025-04-13 00:37:45 +08:00
);
};
/**
* Directory Tool Component (for create-directory and list-directory)
*/
export const DirectoryTool: React.FC<ToolComponentProps> = ({ tag, mode }) => {
const path = tag.attributes.path || '';
const content = tag.content || '';
const isListDirectory = tag.tagName === 'list-directory';
2025-04-13 20:28:23 +08:00
const isRunning = tag.status === 'running';
2025-04-13 00:37:45 +08:00
2025-04-13 20:28:23 +08:00
if (mode === 'compact') {
return (
<CompactToolDisplay
icon={isListDirectory ? <Folder className="h-4 w-4 mr-2" /> : <FolderPlus className="h-4 w-4 mr-2" />}
name={isListDirectory ?
(isRunning ? "Listing directory" : "Listed directory") :
(isRunning ? "Creating directory" : "Created directory")}
input={path}
isRunning={isRunning}
/>
);
}
2025-04-13 00:37:45 +08:00
return (
2025-04-13 20:28:23 +08:00
<div className="border rounded-lg overflow-hidden border-subtle dark:border-white/10">
<div className="flex items-center px-2 py-1 text-xs font-medium border-b border-subtle dark:border-white/10 bg-background-secondary dark:bg-background-secondary text-foreground">
{isListDirectory ? <Folder className="h-4 w-4 mr-2" /> : <FolderPlus className="h-4 w-4 mr-2" />}
<div className="flex-1">
{isListDirectory ?
(isRunning ? `Listing directory: ${path}` : `Listed directory: ${path}`) :
(isRunning ? `Creating directory: ${path}` : `Created directory: ${path}`)}
2025-04-13 00:37:45 +08:00
</div>
2025-04-13 20:28:23 +08:00
{isRunning && (
<div className="flex items-center gap-2">
<span className="text-amber-500">Running</span>
<div className="h-2 w-2 rounded-full bg-amber-500 animate-pulse"></div>
</div>
2025-04-13 00:37:45 +08:00
)}
</div>
2025-04-13 20:28:23 +08:00
<div className="p-3 bg-card-bg dark:bg-background-secondary text-foreground">
<div className="space-y-2">
<div className="flex items-center gap-1 text-xs text-muted-foreground mb-1">
<Folder className="h-3 w-3" />
<span className="font-mono">{path}</span>
</div>
{isListDirectory && content && (
<pre className="whitespace-pre-wrap bg-muted p-2 rounded-md text-xs overflow-x-auto max-h-60">
{content}
</pre>
)}
</div>
</div>
</div>
2025-04-13 00:37:45 +08:00
);
};
/**
* Search Code Tool Component
*/
export const SearchCodeTool: React.FC<ToolComponentProps> = ({ tag, mode }) => {
const query = tag.attributes.query || '';
const content = tag.content || '';
2025-04-13 20:28:23 +08:00
const isRunning = tag.status === 'running';
2025-04-13 00:37:45 +08:00
2025-04-13 20:28:23 +08:00
if (mode === 'compact') {
return (
<CompactToolDisplay
icon={<SearchIcon className="h-4 w-4 mr-2" />}
name={isRunning ? "Searching code" : "Searched code"}
input={query}
isRunning={isRunning}
/>
);
}
2025-04-13 00:37:45 +08:00
return (
2025-04-13 20:28:23 +08:00
<div className="border rounded-lg overflow-hidden border-subtle dark:border-white/10">
<div className="flex items-center px-2 py-1 text-xs font-medium border-b border-subtle dark:border-white/10 bg-background-secondary dark:bg-background-secondary text-foreground">
<SearchIcon className="h-4 w-4 mr-2" />
<div className="flex-1">{isRunning ? "Searching code" : "Searched code"}: {query}</div>
{isRunning && (
<div className="flex items-center gap-2">
<span className="text-amber-500">Running</span>
<div className="h-2 w-2 rounded-full bg-amber-500 animate-pulse"></div>
</div>
)}
</div>
<div className="p-3 bg-card-bg dark:bg-background-secondary text-foreground">
<div className="space-y-2">
<div className="flex items-center gap-1 text-xs bg-primary/10 p-1 rounded">
<SearchIcon className="h-3 w-3" />
<span className="font-mono">{query}</span>
</div>
<pre className="whitespace-pre-wrap bg-muted p-2 rounded-md text-xs overflow-x-auto max-h-60">
{content}
</pre>
2025-04-13 00:37:45 +08:00
</div>
</div>
2025-04-13 20:28:23 +08:00
</div>
2025-04-13 00:37:45 +08:00
);
};
2025-04-15 22:34:26 +08:00
/**
* Browser Navigate Tool Component
*/
export const BrowserNavigateTool: React.FC<ToolComponentProps> = ({ tag, mode }) => {
const url = tag.content || '';
const isRunning = tag.status === 'running';
if (mode === 'compact') {
return (
<CompactToolDisplay
icon={<Globe className="h-4 w-4 mr-2" />}
name={isRunning ? "Navigating to" : "Navigated to"}
input={url}
isRunning={isRunning}
/>
);
}
return (
<div className="border rounded-lg overflow-hidden border-subtle dark:border-white/10">
<div className="flex items-center px-2 py-1 text-xs font-medium border-b border-subtle dark:border-white/10 bg-background-secondary dark:bg-background-secondary text-foreground">
<Globe className="h-4 w-4 mr-2" />
<div className="flex-1">{isRunning ? `Navigating to` : `Navigated to`}: {url}</div>
{isRunning && (
<div className="flex items-center gap-2">
<span className="text-amber-500">Running</span>
<div className="h-2 w-2 rounded-full bg-amber-500 animate-pulse"></div>
</div>
)}
</div>
<div className="p-3 bg-card-bg dark:bg-background-secondary text-foreground">
<div className="space-y-2">
<div className="flex items-center gap-1 text-xs text-muted-foreground mb-1">
<Globe className="h-3 w-3" />
<span className="font-mono">{url}</span>
</div>
{/* Display VNC preview if available */}
{tag.vncPreview && (
<div className="mt-2 border border-subtle dark:border-white/10 rounded-md overflow-hidden">
<div className="text-xs bg-black text-white p-1">VNC Preview</div>
<div className="relative w-full h-[300px] overflow-hidden">
<iframe
src={tag.vncPreview}
title="Browser preview"
className="absolute top-0 left-0 border-0"
style={{
width: '200%',
height: '200%',
transform: 'scale(0.5)',
transformOrigin: '0 0'
}}
sandbox="allow-same-origin allow-scripts"
/>
</div>
</div>
)}
</div>
</div>
</div>
);
};
2025-04-16 02:07:31 +08:00
/**
* Web Search Tool Component
*/
export const WebSearchTool: React.FC<ToolComponentProps> = ({ tag, mode }) => {
const query = tag.attributes.query || '';
const isRunning = tag.status === 'running';
if (mode === 'compact') {
return (
<CompactToolDisplay
icon={<Search className="h-4 w-4 mr-2" />}
name={isRunning ? "Web search in progress..." : "Web search complete"}
input={query}
isRunning={isRunning}
/>
);
}
const results = tag.result?.output ? JSON.parse(tag.result.output) : [];
return (
<div className="border rounded-lg overflow-hidden border-subtle dark:border-white/10">
<div className="flex items-center px-2 py-1 text-xs font-medium border-b border-subtle dark:border-white/10 bg-background-secondary dark:bg-background-secondary text-foreground">
<Search className="h-4 w-4 mr-2" />
<div className="flex-1">Web Search: {query}</div>
{isRunning && (
<div className="flex items-center gap-2">
<span className="text-amber-500">Searching</span>
<div className="h-2 w-2 rounded-full bg-amber-500 animate-pulse"></div>
</div>
)}
</div>
<div className="p-3 bg-card-bg dark:bg-background-secondary text-foreground">
{results.length > 0 ? (
<div className="space-y-3">
{results.map((result: any, index: number) => (
<div key={index} className="text-sm">
<a href={result.URL} target="_blank" rel="noopener noreferrer" className="font-medium text-blue-600 hover:underline">
{result.Title}
</a>
<div className="text-xs text-muted-foreground mt-1">
{result.URL}
{result['Published Date'] && (
<span className="ml-2">
({new Date(result['Published Date']).toLocaleDateString()})
</span>
)}
</div>
</div>
))}
</div>
) : (
<div className="text-sm text-muted-foreground">No results found</div>
)}
</div>
</div>
);
};
2025-04-13 00:37:45 +08:00
// Tool component registry
export const ToolComponentRegistry: Record<string, React.FC<ToolComponentProps>> = {
'create-file': CreateFileTool,
'read-file': ReadFileTool,
'execute-command': ExecuteCommandTool,
'str-replace': StringReplaceTool,
'create-directory': DirectoryTool,
'list-directory': DirectoryTool,
'search-code': SearchCodeTool,
'notify': NotifyTool,
'ask': NotifyTool, // Handle ask similar to notify for now
'complete': NotifyTool, // Handle complete similar to notify for now
'full-file-rewrite': FullFileRewriteTool,
2025-04-15 22:34:26 +08:00
'browser-navigate-to': BrowserNavigateTool,
'browser-click-element': BrowserNavigateTool,
'browser-input-text': BrowserNavigateTool,
'browser-go-back': BrowserNavigateTool,
'browser-wait': BrowserNavigateTool,
'browser-scroll-down': BrowserNavigateTool,
'browser-scroll-up': BrowserNavigateTool,
'browser-scroll-to-text': BrowserNavigateTool,
'browser-switch-tab': BrowserNavigateTool,
'browser-close-tab': BrowserNavigateTool,
'browser-get-dropdown-options': BrowserNavigateTool,
'browser-select-dropdown-option': BrowserNavigateTool,
'browser-drag-drop': BrowserNavigateTool,
2025-04-16 02:07:31 +08:00
'web-search': WebSearchTool,
2025-04-13 00:37:45 +08:00
};
// Helper function to get the appropriate component for a tag
export function getComponentForTag(tag: ParsedTag): React.FC<ToolComponentProps> {
2025-04-16 02:07:31 +08:00
console.log("getComponentForTag", tag);
if (!tag || !tag?.tagName) {
console.warn(`No tag name for tag: ${tag}`);
}
2025-04-13 00:37:45 +08:00
if (!ToolComponentRegistry[tag.tagName]) {
console.warn(`No component registered for tag type: ${tag.tagName}`);
}
return ToolComponentRegistry[tag.tagName] ||
// Fallback component for unknown tag types
(({tag, mode}) => {
console.log(`Rendering fallback component for tag: ${tag.tagName}`, tag);
2025-04-13 20:28:23 +08:00
const isRunning = tag.status === 'running';
if (mode === 'compact') {
return (
<CompactToolDisplay
icon={<Code className="h-4 w-4 mr-2" />}
name={isRunning ? `${tag.tagName} operation running` : `${tag.tagName} operation complete`}
input=""
isRunning={isRunning}
/>
);
}
2025-04-13 00:37:45 +08:00
return (
2025-04-13 20:28:23 +08:00
<div className="border rounded-lg overflow-hidden border-subtle dark:border-white/10">
<div className="flex items-center px-2 py-1 text-xs font-medium border-b border-subtle dark:border-white/10 bg-background-secondary dark:bg-background-secondary text-foreground">
<Code className="h-4 w-4 mr-2" />
<div className="flex-1">{isRunning ? `${tag.tagName} operation running` : `${tag.tagName} operation complete`}</div>
{isRunning && (
<div className="flex items-center gap-2">
<span className="text-amber-500">Running</span>
<div className="h-2 w-2 rounded-full bg-amber-500 animate-pulse"></div>
</div>
)}
</div>
<div className="p-3 bg-card-bg dark:bg-background-secondary text-foreground">
<pre className="whitespace-pre-wrap bg-muted p-2 rounded-md text-xs">{tag.content || ''}</pre>
</div>
</div>
2025-04-13 00:37:45 +08:00
);
});
2025-04-13 20:28:23 +08:00
}