/** * HandList - 左侧导航的 Hands 列表 * * 显示所有可用的 Hands(自主能力包), * 允许用户选择一个 Hand 来查看其任务和结果。 */ import { useEffect } from 'react'; import { useGatewayStore, type Hand } from '../store/gatewayStore'; import { Zap, Loader2, RefreshCw, CheckCircle, XCircle, AlertTriangle } from 'lucide-react'; interface HandListProps { selectedHandId?: string; onSelectHand?: (handId: string) => void; } // 状态图标 function HandStatusIcon({ status }: { status: Hand['status'] }) { switch (status) { case 'running': return ; case 'needs_approval': return ; case 'error': return ; case 'setup_needed': case 'unavailable': return ; default: return ; } } // 状态标签 const STATUS_LABELS: Record = { idle: '就绪', running: '运行中', needs_approval: '待审批', error: '错误', unavailable: '不可用', setup_needed: '需配置', }; export function HandList({ selectedHandId, onSelectHand }: HandListProps) { const { hands, loadHands, isLoading } = useGatewayStore(); useEffect(() => { loadHands(); }, [loadHands]); if (isLoading && hands.length === 0) { return ( 加载中... ); } if (hands.length === 0) { return ( 暂无可用 Hands 连接 OpenFang 后显示 ); } return ( {/* 头部 */} 自主能力包 {hands.length} 个可用 loadHands()} disabled={isLoading} className="p-1.5 text-gray-400 hover:text-gray-600 hover:bg-gray-100 rounded transition-colors disabled:opacity-50" title="刷新" > {/* Hands 列表 */} {hands.map((hand) => ( onSelectHand?.(hand.id)} className={`w-full text-left p-3 border-b border-gray-100 hover:bg-gray-100 transition-colors ${ selectedHandId === hand.id ? 'bg-blue-50 border-l-2 border-l-blue-500' : '' }`} > {hand.icon || '🤖'} {hand.name} {hand.description} {STATUS_LABELS[hand.status]} {hand.toolCount !== undefined && ( {hand.toolCount} 工具 )} ))} ); } export default HandList;
加载中...
暂无可用 Hands
连接 OpenFang 后显示
{hands.length} 个可用
{hand.description}