'use client'; import React, { useEffect, useState } from 'react'; import { Blocks, RefreshCw, Loader2, AlertCircle, Bot, Terminal, Wrench, ChevronDown, ChevronRight, Globe, FolderOpen, } from 'lucide-react'; import { listPlugins } from '@/lib/api'; import { Button } from '@/components/ui/button'; import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'; import { Badge } from '@/components/ui/badge'; import type { PluginInfo } from '@/types'; import { pickAppText } from '@/lib/i18n/core'; import { useAppI18n } from '@/lib/i18n/provider'; export default function PluginsPage() { const { locale } = useAppI18n(); const [plugins, setPlugins] = useState([]); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); const load = async () => { setLoading(true); setError(null); try { const data = await listPlugins(); setPlugins(Array.isArray(data) ? data : []); } catch (err: any) { setError(err.message || pickAppText(locale, '加载插件失败', 'Failed to load plugins')); } finally { setLoading(false); } }; useEffect(() => { load(); }, []); if (loading) { return (
); } return (
{/* Page header */}

{pickAppText(locale, '插件', 'Plugins')}

{pickAppText(locale, '已安装位置:全局插件目录或当前 workspace 的 ', 'Installed from the global plugin directory or this workspace\'s ')} plugins/

{/* Error */} {error && (
{error}
)} {/* Empty state */} {!error && plugins.length === 0 && (

{pickAppText(locale, '还没有安装任何插件', 'No plugins are installed yet')}

{pickAppText(locale, '把插件目录放到全局插件目录或当前 workspace 的 ', 'Put a plugin directory in the global plugin directory or this workspace\'s ')} plugins/ {pickAppText(locale, ',然后重启 Boardware Agent Sandbox。', ', then restart Boardware Agent Sandbox.')}

)} {/* Plugin cards */}
{plugins.map((plugin) => ( ))}
); } function PluginCard({ plugin }: { plugin: PluginInfo }) { const { locale } = useAppI18n(); const [agentsOpen, setAgentsOpen] = useState(true); const [commandsOpen, setCommandsOpen] = useState(true); const [skillsOpen, setSkillsOpen] = useState(false); const totalItems = plugin.agents.length + plugin.commands.length + plugin.skills.length; return (
{plugin.name}
{plugin.description && (

{plugin.description}

)}
{/* Summary chips */}
{plugin.agents.length > 0 && ( {pickAppText(locale, `${plugin.agents.length} 个智能体`, `${plugin.agents.length} agents`)} )} {plugin.commands.length > 0 && ( {pickAppText(locale, `${plugin.commands.length} 条命令`, `${plugin.commands.length} commands`)} )} {plugin.skills.length > 0 && ( {pickAppText(locale, `${plugin.skills.length} 个技能`, `${plugin.skills.length} skills`)} )}
{totalItems > 0 && ( {/* Agents */} {plugin.agents.length > 0 && (
} label={pickAppText(locale, '智能体', 'Agents')} count={plugin.agents.length} open={agentsOpen} onToggle={() => setAgentsOpen((v) => !v)} >
{plugin.agents.map((agent) => (
{agent.name}

{agent.description || '—'}

{agent.model && ( {agent.model} )}
))}
)} {/* Commands */} {plugin.commands.length > 0 && (
} label={pickAppText(locale, '命令', 'Commands')} count={plugin.commands.length} open={commandsOpen} onToggle={() => setCommandsOpen((v) => !v)} >
{plugin.commands.map((cmd) => (
/{cmd.name} {cmd.argument_hint && ( {cmd.argument_hint} )}

{cmd.description || '—'}

))}
)} {/* Skills */} {plugin.skills.length > 0 && (
} label={pickAppText(locale, '技能', 'Skills')} count={plugin.skills.length} open={skillsOpen} onToggle={() => setSkillsOpen((v) => !v)} >
{plugin.skills.map((skill) => ( {skill} ))}
)}
)}
); } function SourceBadge({ source }: { source: 'global' | 'workspace' }) { const { locale } = useAppI18n(); if (source === 'workspace') { return ( {pickAppText(locale, '工作区', 'Workspace')} ); } return ( {pickAppText(locale, '全局', 'Global')} ); } function Section({ icon, label, count, open, onToggle, children, }: { icon: React.ReactNode; label: string; count: number; open: boolean; onToggle: () => void; children: React.ReactNode; }) { return (
{open && children}
); }