import { useCallback, useEffect, useState } from 'react' import { HashRouter, Link, Navigate, Route, Routes, useLocation } from 'react-router-dom' import { Blocks, Circle, RefreshCw } from 'lucide-react' import { Button } from '@/components/ui/button' import { AppSidebar, navigation } from '@/components/app-sidebar' import { LoginForm } from '@/components/login-form' import { SidebarInset, SidebarProvider, SidebarTrigger } from '@/components/ui/sidebar' import { Separator } from '@/components/ui/separator' import { TooltipProvider } from '@/components/ui/tooltip' import { Alert, AlertDescription } from '@/components/ui/alert' import { Breadcrumb, BreadcrumbItem, BreadcrumbList, BreadcrumbPage } from '@/components/ui/breadcrumb' import { Workbench } from '@/lib/context' import { request, errorText } from '@/lib/api' import type { State, AttentionItem } from '@/lib/types' import { projectTitle, date } from '@/lib/types' import VersionNotice from '@/components/version-notice' import Overview from '@/pages/overview' import Tasks from '@/pages/tasks' import Cluster from '@/pages/cluster' import Communication from '@/pages/communication' import Decisions from '@/pages/decisions' import Logs from '@/pages/logs' function attentionTarget(item: AttentionItem) { const target = item.href.startsWith('#/') ? item.href.slice(1) : item.href return /^\/(overview|tasks|cluster|communication|decisions|logs)(?:[?#]|$)/.test(target) ? target : `/${item.page}` } function AttentionNotice({ state, pathname }: { state: State; pathname: string }) { const items = (state.attention?.items ?? []).filter(item => item.required && `/${item.page}` === pathname) if (!items.length) return null return } function Shell({ token, initial, logout }: { token: string; initial: State; logout: () => void }) { const [state, setState] = useState(initial), [error, setError] = useState('') const title = projectTitle(state), location = useLocation() const refresh = useCallback(async () => { const latest = await request(token, '/api/state'); setState(latest); setError('') }, [token]) useEffect(() => { const controller = new AbortController(); let busy = false const poll = async () => { if (busy) return; busy = true; try { const latest = await request(token, '/api/state', undefined, controller.signal); if (!controller.signal.aborted) { setState(latest); setError('') } } catch (e) { if (!controller.signal.aborted) setError(errorText(e)) } finally { busy = false } } const timer = setInterval(() => void poll(), 3000) return () => { clearInterval(timer); controller.abort() } }, [token]) useEffect(() => { document.title = `${navigation.find(n => n.url === location.pathname)?.title || '总览'} · ${title}` }, [title, location.pathname]) const isChat = location.pathname === '/communication' return
{isChat ?

项目沟通

询问进度、讨论方案,或告诉我你的新想法。

: {navigation.find(n => n.url === location.pathname)?.title || '总览'}}
{isChat ? :
{error ? '连接中断' : '实时同步'}{date(state.updated)}
}
{error && {error} 当前显示上次同步的数据。}} />} />} />} />} />} />} />
{!isChat &&
Agent Workbench · 每 3 秒同步 · AI 接入状态以会话显示为准
}
} export default function App() { const [auth, setAuth] = useState<{ token: string; state: State } | null>(null) return {auth ? setAuth(null)} /> :
Agent Workbench
setAuth({ token, state })} />
}
}