From 44b080971e0878b07fa934100bac997a8b1f077b Mon Sep 17 00:00:00 2001 From: host Date: Fri, 30 Oct 2026 03:22:00 +0800 Subject: [PATCH] =?UTF-8?q?console:=20=E9=87=8D=E6=8E=92=E7=89=88=E4=B8=BA?= =?UTF-8?q?=E4=BE=A7=E8=BE=B9=E5=AF=BC=E8=88=AA=20+=20=E4=B8=89=E6=A0=8F?= =?UTF-8?q?=E5=B7=A5=E4=BD=9C=E5=8F=B0=20+=20=E4=BB=BB=E5=8A=A1=E8=AF=A6?= =?UTF-8?q?=E6=83=85=E5=86=85=E9=A1=B5=E7=AD=BE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 旧排版(顶栏页签 + 数据卡片置顶 + 长页堆叠 + 详情卡片堆在对话下)全部去掉,功能不变: - 左侧栏:导航按任务/管理分组,底部紧凑概览卡片始终可见;整页不再单页长滚动,各 pane 各自滚动 - 任务工作台:左任务列表轨(搜索/筛选/分页),中派发(可收起)+ 单列对话,右任务详情检查器 - 详情检查器:概览/协作/评审与审批/线程四个内页签,一次只显示一组 - 其余功能各成独立页面:项目与成员、环境与 Workers、技能与自动化、系统与 Key - 视图同步到 location.hash(#view-tasks 等) 构建:npm run build(tsc + vite singlefile)通过,产物 332130 字节;本地全量 cloud 1275 项全过(skipped 4)。 --- cloud/console-react/src/App.tsx | 199 ++++++++---------- .../src/sections/TaskDetailPanel.tsx | 39 +++- cloud/console-react/src/tokens.css | 49 +++++ cloud/console.html | 48 ++--- 4 files changed, 187 insertions(+), 148 deletions(-) diff --git a/cloud/console-react/src/App.tsx b/cloud/console-react/src/App.tsx index 7c6f4bc..b693cb6 100644 --- a/cloud/console-react/src/App.tsx +++ b/cloud/console-react/src/App.tsx @@ -20,156 +20,129 @@ import SkillsToolsPanel from "./sections/SkillsToolsPanel"; import AutomationsSection from "./sections/AutomationsSection"; import TriageSection from "./sections/TriageSection"; -// Layout: GitHub Primer style, data cards on top, then TAB pages -// instead of one endless page. The old layout stacked 11 sections -// (dispatch, tasks, conversation, projects, env, workers, skills, -// automations, triage, admin, API Key, plus detail panels) on one -// page; tabbing is what actually cuts the visible length, while -// list pagination (see useTaskList) cuts the data fetched per page. -// Tabs: -// 工作台:派发 + 任务列表(分页)+ 对话 + 任务详情 -// 项目:项目 + 成员 -// 环境与 Workers:环境 + 环境上下文 + Workers + 健康 -// 技能与自动化:技能 + 技能工具 + 自动化 + 分诊 -// 管理与 Key:管理区 + API Key -// Selected task survives tab switches: the store (useConsoleStore) -// lives in App, above the tabs, so currentTask / detail / chat -// state is not unmounted with a tab — switching back shows the same -// task, and the 10 s refresh re-syncs its detail lists. The tab is -// synced to location.hash (#tab-workbench etc.) so a reload or a -// shared link lands on the same tab; unknown hashes fall back to -// the workbench. -type TabId = "workbench" | "projects" | "env" | "skills" | "admin"; -const TABS: Array<[TabId, string]> = [ - ["workbench", "工作台"], ["projects", "项目"], ["env", "环境与 Workers"], - ["skills", "技能与自动化"], ["admin", "管理与 Key"], +// 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: +// 左侧栏:导航(按工作流分组)+ 数据卡片(紧凑版,始终可见) +// 任务工作台:左 = 任务列表轨(搜索/筛选/分页),中 = 派发 + 对话, +// 右 = 任务详情检查器(概览/协作/评审与审批/线程四个内页签, +// 见 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" | "projects" | "resources" | "automation" | "admin"; +const VIEWS: Array<[ViewId, string, string]> = [ + ["tasks", "任务工作台", "任务"], + ["projects", "项目与成员", "管理"], + ["resources", "环境与 Workers", "管理"], + ["automation", "技能与自动化", "管理"], + ["admin", "系统与 Key", "管理"], ]; -function tabFromHash(): TabId { - const h = window.location.hash.replace("#tab-", ""); - return (TABS.some(([id]) => id === h) ? h : "workbench") as TabId; +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 TopBar({ tab, setTab }: { tab: TabId; setTab: (t: TabId) => void }) { +function SideNav({ view, setView }: { view: ViewId; setView: (v: ViewId) => void }) { return ( -
-
- Codex 命令台 - - - - -
-
+
+
概览
+ +
+ ); } -// Tiny readers so the top bar re-renders from the store. function TopStatus() { const ctx = useConsole(); return {ctx.keySaved ? "● 已连接" : "○ 未连接"}; } -function TopHealth() { - return <>{useConsole().healthText}; -} -function TopQuota() { - return <>{useConsole().quotaText}; -} -function TopStamp() { - return <>自动刷新 · 每 10 秒 · 已刷新 {useConsole().refreshStamp}; -} export default function App() { const store = useConsoleStore(); useAutoRefresh((selectFocused) => store.refreshAll(selectFocused), 10000); useIntervalMs(() => store.refreshSlow(), 60000); - const [tab, setTabState] = useState(() => tabFromHash()); - const setTab = (t: TabId) => { - setTabState(t); - try { window.history.replaceState(null, "", "#tab-" + t); } catch { /* noop */ } + 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 = () => setTabState(tabFromHash()); + const onHash = () => setViewState(viewFromHash()); window.addEventListener("hashchange", onHash); return () => window.removeEventListener("hashchange", onHash); }, []); const scrollToDispatch = () => { - setTab("workbench"); + setView("tasks"); + setComposerOpen(true); setTimeout(() => document.getElementById("sec-dispatch")?.scrollIntoView({ behavior: "smooth" }), 0); }; return ( - -
- - {tab === "workbench" ? ( -
-
-
+
+ +
+
+

{VIEW_TITLES[view]}

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

任务列表

-
-
-
- -
-
-
+
+
+ +
+ {composerOpen ?
: null} +

对话

-
+
+
+
-
- ) : tab === "projects" ? ( -
-
- - -
-
- ) : tab === "env" ? ( -
-
- - -
-
- - -
-
- ) : tab === "skills" ? ( -
-
- - -
-
- -
-
- -
-
- ) : ( -
-
- -
-
- -
-
- )} -
+ ) : ( +
+ {view === "projects" ? (<>) + : view === "resources" ? (<>) + : view === "automation" ? (<>) + : (<>)} +
+ )} + +
); } diff --git a/cloud/console-react/src/sections/TaskDetailPanel.tsx b/cloud/console-react/src/sections/TaskDetailPanel.tsx index 5b7f9a4..90a17b2 100644 --- a/cloud/console-react/src/sections/TaskDetailPanel.tsx +++ b/cloud/console-react/src/sections/TaskDetailPanel.tsx @@ -1,3 +1,4 @@ +import { useState } from "react"; import { useConsole } from "../context"; import { Card } from "../components/ui"; import { shortId, fmt } from "../api/client"; @@ -7,19 +8,25 @@ import DetailActions from "./detail/DetailActions"; import ReviewPanel from "./detail/ReviewPanel"; import { CoordPanel, AgentsPanel, ApprovalPanel, PeerPanel, ThreadPanel } from "./detail/DetailLists"; -// Right detail column for the task view, card-style layout. -// Everything structured lives here — never inside a chat bubble. -// Layout only: data, actions and sub-sections are unchanged, -// just grouped into cards instead of one
-separated block: -// 概览(状态 + 耗时/token/成本三张指标卡) -// 基本信息(键值网格) -// prompt(单独一张卡,长 prompt 不再挤在信息行末尾) -// 操作与结果 -// 协调子任务 / 子代理链路 / 评审队列 / 运行时审批队列 / -// 给其他任务发消息 / 线程(各一张卡) +// Task inspector (redesign 2026-10-30): the old detail stack — +// 概览 + 基本信息 + prompt + 操作 + 协调 + 子代理 + 评审 + +// 审批 + 任务间消息 + 线程, all stacked under the conversation — +// is gone. The same panels now live in four inspector tabs so +// only one group is visible at a time: +// 概览:状态/耗时/token/成本 + 基本信息 + prompt + 操作与结果 +// 协作:协调子任务 / 子代理链路 / 给其他任务发消息 +// 评审与审批:评审队列 / 运行时审批队列 +// 线程:线程管理 +// Data, actions and sub-sections are unchanged; only grouping +// and visibility changed. +type DetailTab = "overview" | "collab" | "review" | "threads"; +const DETAIL_TABS: Array<[DetailTab, string]> = [ + ["overview", "概览"], ["collab", "协作"], ["review", "评审与审批"], ["threads", "线程"], +]; export default function TaskDetailPanel() { const ctx = useConsole(); const t = ctx.currentTask; + const [tab, setTab] = useState("overview"); if (!t) return
点任务行查看详情
; const [dur, durTitle] = durationOf(t); const [tok, tokTitle] = tokenOf(t); @@ -30,6 +37,12 @@ export default function TaskDetailPanel() { : t.verify_command ? "goal 模式(旧语义,验收命令)" : null; return (