| import { Link, useLocation } from 'react-router-dom'; |
| import { Package, ChevronRight, Puzzle, Wrench, Server, Settings } from 'lucide-react'; |
| import { usePlugins } from '@hooks/usePlugins'; |
| import { cn } from '@lib/utils'; |
|
|
| |
| |
| |
| |
| const platformNavItems = [ |
| { |
| path: '/plugins', |
| label: '插件中心', |
| icon: Puzzle, |
| matchPattern: /^\/plugins/, |
| }, |
| { |
| path: '/tools', |
| label: 'Tool 目录', |
| icon: Wrench, |
| matchPattern: /^\/tools/, |
| }, |
| { |
| path: '/mcp', |
| label: 'MCP 服务', |
| icon: Server, |
| matchPattern: /^\/mcp/, |
| }, |
| { |
| path: '/system', |
| label: '系统状态', |
| icon: Settings, |
| matchPattern: /^\/system/, |
| }, |
| ]; |
|
|
| |
| |
| |
| |
| export function Sidebar() { |
| const location = useLocation(); |
| const { data: plugins, isLoading } = usePlugins(); |
|
|
| |
| const enabledPlugins = plugins?.filter((p) => p.status === 'enabled') || []; |
|
|
| return ( |
| <aside className="w-56 h-full border-r border-console-border bg-console-surface-raised overflow-y-auto"> |
| <nav className="p-3" aria-label="控制台导航"> |
| {/* 平台导航区 */} |
| <div className="mb-4"> |
| <h2 className="px-3 mb-2 text-xs font-medium text-console-text-secondary uppercase tracking-wider"> |
| 平台 |
| </h2> |
| <ul className="space-y-0.5" role="list"> |
| {platformNavItems.map((item) => { |
| const isActive = item.matchPattern.test(location.pathname); |
| return ( |
| <li key={item.path}> |
| <Link |
| to={item.path} |
| className={cn( |
| 'flex items-center gap-2.5 px-3 py-1.5 rounded-console-sm text-sm transition-colors', |
| isActive |
| ? 'bg-console-status-info-bg text-console-status-info font-medium' |
| : 'text-console-text-primary hover:bg-console-surface-base' |
| )} |
| aria-current={isActive ? 'page' : undefined} |
| > |
| <item.icon className="h-4 w-4 shrink-0" aria-hidden="true" /> |
| <span className="truncate">{item.label}</span> |
| </Link> |
| </li> |
| ); |
| })} |
| </ul> |
| </div> |
| |
| {/* 插件运行区 */} |
| <div> |
| <h2 className="px-3 mb-2 text-xs font-medium text-console-text-secondary uppercase tracking-wider"> |
| 插件运行 |
| </h2> |
| |
| {isLoading ? ( |
| <div className="px-3 py-2 text-xs text-console-text-muted"> |
| 加载中... |
| </div> |
| ) : enabledPlugins.length === 0 ? ( |
| <div className="px-3 py-2 text-xs text-console-text-muted"> |
| 暂无启用插件 |
| </div> |
| ) : ( |
| <ul className="space-y-0.5" role="list"> |
| {enabledPlugins.map((plugin) => { |
| const isActive = location.pathname === `/tool/${plugin.metadata.name}`; |
| return ( |
| <li key={plugin.metadata.name}> |
| <Link |
| to={`/tool/${plugin.metadata.name}`} |
| className={cn( |
| 'flex items-center gap-2.5 px-3 py-1.5 rounded-console-sm text-sm transition-colors', |
| isActive |
| ? 'bg-console-status-info-bg text-console-status-info font-medium' |
| : 'text-console-text-primary hover:bg-console-surface-base' |
| )} |
| aria-current={isActive ? 'page' : undefined} |
| > |
| <Package className="h-4 w-4 shrink-0" aria-hidden="true" /> |
| <span className="flex-1 truncate"> |
| {plugin.metadata.name} |
| </span> |
| <ChevronRight className="h-3.5 w-3.5 text-console-text-muted shrink-0" aria-hidden="true" /> |
| </Link> |
| </li> |
| ); |
| })} |
| </ul> |
| )} |
| </div> |
| </nav> |
| </aside> |
| ); |
| } |
|
|