import { useEffect, useState } from "react"; import { ConsoleContext, useConsole } from "./context"; import { useConsoleStore } from "./hooks/useConsoleStore"; import { useAutoRefresh, useIntervalMs } from "./hooks/useAutoRefresh"; import DataCards from "./sections/DataCards"; import DispatchSection from "./sections/DispatchSection"; import TaskListSection from "./sections/TaskListSection"; import ConversationSection from "./sections/ConversationSection"; import TaskDetailPanel from "./sections/TaskDetailPanel"; import ApiKeySection from "./sections/ApiKeySection"; import AdminSection from "./sections/AdminSection"; import ProjectNav from "./sections/ProjectNav"; import EnvSection from "./sections/EnvSection"; import EnvContextPanel from "./sections/EnvContextPanel"; import WorkersSection from "./sections/WorkersSection"; import HealthPanel from "./sections/HealthPanel"; import SkillsSection from "./sections/SkillsSection"; import SkillsToolsPanel from "./sections/SkillsToolsPanel"; import AutomationsSection from "./sections/AutomationsSection"; import TriageSection from "./sections/TriageSection"; import TodosSection from "./sections/TodosSection"; // Redesign (2026-10-30): the old layout is gone — no top tab bar, no // data-cards row on top of a long stacked page, no detail stack // hanging under the conversation. The new shell is an IDE-style // workspace that never scrolls as one page: // 左侧栏:导航(按工作流分组)+ 项目模块(ProjectNav:项目列表 + // 创建项目 + 成员,id nav-projects,是全站唯一的项目选择器; // 因此不再有独立的“项目与成员”视图,旧 ProjectsSection 已删, // 工作台也不再带项目选择器)+ 数据卡片(紧凑版,始终可见) // 任务工作台:左 = 任务列表轨(搜索/筛选/分页),中 = 派发 + 对话, // 右 = 任务详情检查器(概览/协作/评审与审批/线程四个内页签, // 见 TaskDetailPanel) // 其余功能各成独立页面(项目 / 资源 / 自动化 / 系统),从左侧栏 // 切入,页面内只放该组功能,不再把 11 个模块堆在同一页。 // Selected task survives view switches: the store lives in App, // above the views, so currentTask / detail / chat state is not // unmounted with a view. The view is synced to location.hash // (#view-tasks etc.) so a reload or a shared link lands on the // same view; unknown hashes fall back to the task workspace. type ViewId = "tasks" | "todos" | "resources" | "automation" | "admin"; const VIEWS: Array<[ViewId, string, string]> = [ ["tasks", "任务工作台", "任务"], ["todos", "项目待办", "任务"], ["resources", "环境与 Workers", "管理"], ["automation", "技能与自动化", "管理"], ["admin", "系统与 Key", "管理"], ]; const VIEW_TITLES: Record = Object.fromEntries( VIEWS.map(([id, title]) => [id, title]), ) as Record; function viewFromHash(): ViewId { const h = window.location.hash.replace("#view-", ""); return (VIEWS.some(([id]) => id === h) ? h : "tasks") as ViewId; } function SideNav({ view, setView }: { view: ViewId; setView: (v: ViewId) => void }) { return ( ); } function TopStatus() { const ctx = useConsole(); return {ctx.keySaved ? "● 已连接" : "○ 未连接"}; } export default function App() { const store = useConsoleStore(); useAutoRefresh((selectFocused) => store.refreshAll(selectFocused), 10000); useIntervalMs(() => store.refreshSlow(), 60000); const [view, setViewState] = useState(() => viewFromHash()); const [composerOpen, setComposerOpen] = useState(true); const setView = (v: ViewId) => { setViewState(v); try { window.history.replaceState(null, "", "#view-" + v); } catch { /* noop */ } }; useEffect(() => { const onHash = () => setViewState(viewFromHash()); window.addEventListener("hashchange", onHash); return () => window.removeEventListener("hashchange", onHash); }, []); const scrollToDispatch = () => { setView("tasks"); setComposerOpen(true); setTimeout(() => document.getElementById("sec-dispatch")?.scrollIntoView({ behavior: "smooth" }), 0); }; return (

{VIEW_TITLES[view]}

{store.healthText} {store.quotaText} 自动刷新 · 每 10 秒 · 已刷新 {store.refreshStamp}
{view === "tasks" ? (

任务列表

{composerOpen ?
: null}

对话

) : (
{view === "todos" ? (<>) : view === "resources" ? (<>) : view === "automation" ? (<>) : (<>)}
)}
); }