'use client';
import React from 'react';
import { ParsedTag, ToolComponentProps } from '@/lib/types/tool-calls';
import {
File, FileText, Terminal, FolderPlus, Folder, Code, Search as SearchIcon,
Bell, Replace, Plus, Minus, Globe, Search
} from 'lucide-react';
import { cn } from '@/lib/utils';
import { diffLines } from 'diff';
// Shared compact mode component
const CompactToolDisplay: React.FC<{
icon: React.ReactNode,
name: string,
input: string,
isRunning?: boolean
}> = ({ icon, name, input, isRunning }) => {
return (
{icon}
{name}: {input}
{isRunning && (
)}
);
};
/**
* Create File Tool Component
*/
export const CreateFileTool: React.FC = ({ tag, mode }) => {
const filePath = tag.attributes.file_path || '';
const fileContent = tag.content || '';
const isRunning = tag.status === 'running';
if (mode === 'compact') {
return (
}
name={isRunning ? "Creating file" : "Created file"}
input={filePath}
isRunning={isRunning}
/>
);
}
return (
{isRunning ? "Creating file" : "Created file"}: {filePath}
{isRunning && (
)}
);
};
/**
* Full File Rewrite Tool Component
*/
export const FullFileRewriteTool: React.FC = ({ tag, mode }) => {
const filePath = tag.attributes.file_path || '';
const fileContent = tag.content || '';
const isRunning = tag.status === 'running';
if (mode === 'compact') {
return (
}
name={isRunning ? "Rewriting file" : "Rewrote file"}
input={filePath}
isRunning={isRunning}
/>
);
}
return (
{isRunning ? "Rewriting file" : "Rewrote file"}: {filePath}
{isRunning && (
)}
);
};
/**
* Read File Tool Component
*/
export const ReadFileTool: React.FC = ({ tag, mode }) => {
const filePath = tag.attributes.file_path || '';
const fileContent = tag.content || '';
const isRunning = tag.status === 'running';
if (mode === 'compact') {
return (
}
name={isRunning ? "Reading file" : "Read file"}
input={filePath}
isRunning={isRunning}
/>
);
}
return (
{isRunning ? "Reading file" : "Read file"}: {filePath}
{isRunning && (
)}
);
};
/**
* Execute Command Tool Component
*/
export const ExecuteCommandTool: React.FC = ({ tag, mode }) => {
const output = tag.content || '';
const command = tag.resultTag?.content || '';
const isRunning = tag.status === 'running';
if (mode === 'compact') {
return (
}
name={isRunning ? "Executing" : "Executed"}
input={tag.content} // in compact mode, the command is in the content
isRunning={isRunning}
/>
);
}
return (
{isRunning ? `Executing: ${command}` : `Executed: ${command}`}
{isRunning && (
)}
user@localhost
:
~
$
{command}
{output && (
{output}
)}
{!isRunning && (
user@localhost
:
~
$
█
)}
);
};
/**
* String Replace Tool Component
*/
export const StringReplaceTool: React.FC = ({ tag, mode }) => {
const content = tag.content || '';
const isRunning = tag.status === 'running';
// Parse the old and new strings from the content
const oldStrMatch = content.match(/([\s\S]*?)<\/old_str>/);
const newStrMatch = content.match(/([\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;
}
if (mode === 'compact') {
return (
}
name={isRunning ? "Updating file" : "Updated file"}
input=""
isRunning={isRunning}
/>
);
}
return (
{isRunning ? "Updating file" : "Updated file"}
{isRunning && (
)}
{diff.map((part: DiffPart, index: number) => (
{part.added ? :
part.removed ? :
}
{part.value}
))}
);
};
/**
* Notification Tool Component
*/
export const NotifyTool: React.FC = ({ tag, mode }) => {
const message = tag.attributes.message || '';
const type = tag.attributes.type || 'info';
const isRunning = tag.status === 'running';
if (mode === 'compact') {
return (
}
name={isRunning ? "Sending notification" : "Sent notification"}
input={message.substring(0, 30) + (message.length > 30 ? '...' : '')}
isRunning={isRunning}
/>
);
}
return (
{isRunning ? "Sending notification" : "Sent notification"}: {message.substring(0, 30)}{message.length > 30 ? '...' : ''}
{isRunning && (
)}
);
};
/**
* Directory Tool Component (for create-directory and list-directory)
*/
export const DirectoryTool: React.FC = ({ tag, mode }) => {
const path = tag.attributes.path || '';
const content = tag.content || '';
const isListDirectory = tag.tagName === 'list-directory';
const isRunning = tag.status === 'running';
if (mode === 'compact') {
return (
: }
name={isListDirectory ?
(isRunning ? "Listing directory" : "Listed directory") :
(isRunning ? "Creating directory" : "Created directory")}
input={path}
isRunning={isRunning}
/>
);
}
return (
{isListDirectory ?
:
}
{isListDirectory ?
(isRunning ? `Listing directory: ${path}` : `Listed directory: ${path}`) :
(isRunning ? `Creating directory: ${path}` : `Created directory: ${path}`)}
{isRunning && (
)}
{path}
{isListDirectory && content && (
{content}
)}
);
};
/**
* Search Code Tool Component
*/
export const SearchCodeTool: React.FC = ({ tag, mode }) => {
const query = tag.attributes.query || '';
const content = tag.content || '';
const isRunning = tag.status === 'running';
if (mode === 'compact') {
return (
}
name={isRunning ? "Searching code" : "Searched code"}
input={query}
isRunning={isRunning}
/>
);
}
return (
{isRunning ? "Searching code" : "Searched code"}: {query}
{isRunning && (
)}
);
};
/**
* Browser Navigate Tool Component
*/
export const BrowserNavigateTool: React.FC = ({ tag, mode }) => {
const url = tag.content || '';
const isRunning = tag.status === 'running';
if (mode === 'compact') {
return (
}
name={isRunning ? "Navigating to" : "Navigated to"}
input={url}
isRunning={isRunning}
/>
);
}
return (
{isRunning ? `Navigating to` : `Navigated to`}: {url}
{isRunning && (
)}
{url}
{/* Display VNC preview if available */}
{tag.vncPreview && (
)}
);
};
/**
* Web Search Tool Component
*/
export const WebSearchTool: React.FC = ({ tag, mode }) => {
const query = tag.attributes.query || '';
const isRunning = tag.status === 'running';
if (mode === 'compact') {
return (
}
name={isRunning ? "Web search in progress..." : "Web search complete"}
input={query}
isRunning={isRunning}
/>
);
}
const results = tag.result?.output ? JSON.parse(tag.result.output) : [];
return (
Web Search: {query}
{isRunning && (
)}
{results.length > 0 ? (
{results.map((result: any, index: number) => (
{result.Title}
{result.URL}
{result['Published Date'] && (
({new Date(result['Published Date']).toLocaleDateString()})
)}
))}
) : (
No results found
)}
);
};
// Tool component registry
export const ToolComponentRegistry: Record> = {
'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,
'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,
'web-search': WebSearchTool,
};
// Helper function to get the appropriate component for a tag
export function getComponentForTag(tag: ParsedTag): React.FC {
console.log("getComponentForTag", tag);
if (!tag || !tag?.tagName) {
console.warn(`No tag name for tag: ${tag}`);
}
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);
const isRunning = tag.status === 'running';
if (mode === 'compact') {
return (
}
name={isRunning ? `${tag.tagName} operation running` : `${tag.tagName} operation complete`}
input=""
isRunning={isRunning}
/>
);
}
return (
{isRunning ? `${tag.tagName} operation running` : `${tag.tagName} operation complete`}
{isRunning && (
)}
);
});
}