Files
SuperCharged-Claude-Code-Up…/dexto/packages/webui/components/Playground/ToolsList.tsx
admin b52318eeae feat: Add intelligent auto-router and enhanced integrations
- Add intelligent-router.sh hook for automatic agent routing
- Add AUTO-TRIGGER-SUMMARY.md documentation
- Add FINAL-INTEGRATION-SUMMARY.md documentation
- Complete Prometheus integration (6 commands + 4 tools)
- Complete Dexto integration (12 commands + 5 tools)
- Enhanced Ralph with access to all agents
- Fix /clawd command (removed disable-model-invocation)
- Update hooks.json to v5 with intelligent routing
- 291 total skills now available
- All 21 commands with automatic routing

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2026-01-28 00:27:56 +04:00

187 lines
8.3 KiB
TypeScript

import React from 'react';
import { Wrench, Search, Loader2 } from 'lucide-react';
import { Input } from '@/components/ui/input';
import { Badge } from '@/components/ui/badge';
import { Skeleton } from '@/components/ui/skeleton';
import { cn } from '@/lib/utils';
import type { McpServer, McpTool } from '@/components/hooks/useServers';
interface ToolsListProps {
tools: McpTool[];
selectedTool: McpTool | null;
selectedServer: McpServer | null;
isLoading: boolean;
error: string | null;
searchQuery: string;
onSearchChange: (query: string) => void;
onToolSelect: (tool: McpTool) => void;
}
export function ToolsList({
tools,
selectedTool,
selectedServer,
isLoading,
error,
searchQuery,
onSearchChange,
onToolSelect,
}: ToolsListProps) {
const filteredTools = tools.filter(
(tool) =>
tool.name.toLowerCase().includes(searchQuery.toLowerCase()) ||
tool.description?.toLowerCase().includes(searchQuery.toLowerCase())
);
return (
<div className="flex flex-col h-full">
{/* Header */}
<div className="pb-3 mb-3 border-b border-border">
<div className="flex items-center gap-2 mb-3">
<Wrench className="h-4 w-4 text-muted-foreground" />
<h2 className="text-sm font-semibold text-foreground">Tools</h2>
{isLoading && tools.length === 0 && (
<Loader2 className="h-3 w-3 animate-spin text-muted-foreground ml-auto" />
)}
{tools.length > 0 && (
<Badge variant="secondary" className="ml-auto text-xs">
{filteredTools.length}
</Badge>
)}
</div>
{tools.length > 0 && (
<div className="relative">
<Search className="absolute left-2 top-1/2 -translate-y-1/2 h-3 w-3 text-muted-foreground" />
<Input
placeholder="Search tools..."
value={searchQuery}
onChange={(e) => onSearchChange(e.target.value)}
className="h-8 text-sm pl-7"
/>
</div>
)}
</div>
{/* No Server Selected */}
{!selectedServer && (
<div className="flex-1 flex items-center justify-center p-4">
<div className="text-center">
<Wrench className="h-8 w-8 mx-auto text-muted-foreground/50 mb-2" />
<p className="text-sm text-muted-foreground">Select a server</p>
<p className="text-xs text-muted-foreground mt-1">
Choose a connected server to view its tools
</p>
</div>
</div>
)}
{/* Server Not Connected */}
{selectedServer && selectedServer.status !== 'connected' && (
<div className="flex-1 flex items-center justify-center p-4">
<div className="text-center">
<Wrench className="h-8 w-8 mx-auto text-muted-foreground/50 mb-2" />
<p className="text-sm text-muted-foreground">Server not connected</p>
<p className="text-xs text-muted-foreground mt-1">
"{selectedServer.name}" is {selectedServer.status}
</p>
</div>
</div>
)}
{/* Error State */}
{error && selectedServer?.status === 'connected' && !isLoading && (
<div className="p-3 bg-destructive/10 text-destructive text-sm rounded-md">
<p className="font-medium">Error loading tools</p>
<p className="text-xs mt-1">{error}</p>
</div>
)}
{/* Loading State */}
{isLoading && selectedServer?.status === 'connected' && tools.length === 0 && (
<div className="flex-1 space-y-2 pr-1">
{[1, 2, 3, 4].map((i) => (
<div key={i} className="p-3 rounded-lg border border-border">
<div className="flex items-start gap-2">
<Skeleton className="h-4 w-4 mt-0.5 flex-shrink-0 rounded-full" />
<div className="flex-1 space-y-2">
<Skeleton className="h-4 w-3/4" />
<Skeleton className="h-3 w-full" />
<Skeleton className="h-3 w-5/6" />
</div>
</div>
</div>
))}
</div>
)}
{/* Empty State */}
{selectedServer &&
selectedServer.status === 'connected' &&
!isLoading &&
tools.length === 0 &&
!error && (
<div className="flex-1 flex items-center justify-center p-4">
<div className="text-center">
<Wrench className="h-8 w-8 mx-auto text-muted-foreground/50 mb-2" />
<p className="text-sm text-muted-foreground">No tools available</p>
<p className="text-xs text-muted-foreground mt-1">
No tools found for {selectedServer.name}
</p>
</div>
</div>
)}
{/* Tools List */}
{filteredTools.length > 0 && (
<div className="flex-1 overflow-y-auto space-y-1 pr-1">
{filteredTools.map((tool) => (
<button
key={tool.id}
onClick={() => onToolSelect(tool)}
className={cn(
'w-full p-3 rounded-lg text-left transition-all duration-200',
'hover:shadow-sm border border-transparent',
selectedTool?.id === tool.id
? 'bg-primary text-primary-foreground shadow-sm border-primary/20'
: 'hover:bg-muted hover:border-border'
)}
>
<div className="flex items-start gap-2">
<div className="flex-1 min-w-0">
<h3 className="font-medium text-sm truncate">{tool.name}</h3>
{tool.description && (
<p
className={cn(
'text-xs mt-1 line-clamp-2',
selectedTool?.id === tool.id
? 'text-primary-foreground/80'
: 'text-muted-foreground'
)}
>
{tool.description}
</p>
)}
</div>
</div>
</button>
))}
</div>
)}
{/* No Search Results */}
{filteredTools.length === 0 && tools.length > 0 && (
<div className="flex-1 flex items-center justify-center p-4">
<div className="text-center">
<Search className="h-8 w-8 mx-auto text-muted-foreground/50 mb-2" />
<p className="text-sm text-muted-foreground">No tools match your search</p>
<p className="text-xs text-muted-foreground mt-1">
Try a different search term
</p>
</div>
</div>
)}
</div>
);
}