console: React 生产级重写(左列表 + 中对话 + 右详情)

旧单页 console.html 换成 Vite + React + TypeScript 单文件构建产物
(309525 字节,sha 90345ec8…,重建字节一致),源码在
cloud/console-react/,改页面只改 src 再 npm run build:console,
产物不手改。布局:左侧列表(模块导航 + 任务列表)、中间标准对话
(实时链路时间线 + 继续会话聊天框,共用 Chat 组件)、右侧详情
(元数据、操作、评审、审批、协调子任务、子代理、线程)。
API 与后端语义未改,key 只存浏览器 localStorage。
6 个 console 标记单测随实现改名更新(意图不变:壳确实调了
这些接口、turn 信息仍在详情里、旧 goal 输入仍不存在);
P1 标记单测未动且通过。
This commit is contained in:
host committed 2026-10-29 06:32:38 +08:00
1 parent 3f4e849a93
commit ebc5624893
57 files changed
+4789 -1746

No files matched your search

@@ -0,0 +1,34 @@
import { useState } from "react";
import { useConsole } from "../../context";
import { Bar } from "../../components/ui";
import { TRACE_TERMINAL } from "../../api/types";
// Detail actions + fork only. Continue/followup input lives in
// ContinuePanel (shared ChatInput), not in a second form here.
export default function DetailActions() {
const ctx = useConsole();
const t = ctx.currentTask;
const [forkMsg, setForkMsg] = useState("");
if (!t) return null;
const terminal = TRACE_TERMINAL.includes(t.status);
const retryOk = ["failed", "interrupted", "merge-failed"].includes(t.status);
return (
<>
<Bar>
<button disabled={!retryOk} title={retryOk ? "" : "仅 failed/interrupted/merge-failed 可重试"} onClick={() => ctx.doAction("retry")}>重试</button>
<button className="danger" disabled={terminal} title={terminal ? "终态任务不可取消" : ""} onClick={() => ctx.doAction("cancel")}>取消</button>
<button disabled={t.status !== "completed"} title={t.status !== "completed" ? "仅 completed 可合并" : ""} onClick={() => ctx.doAction("merge")}>合并(completed → merged)</button>
<button disabled={t.status !== "merged"} title={t.status !== "merged" ? "仅 merged 可提 PR" : ""} onClick={() => ctx.doAction("pr")}>提 PR(须 merged)</button>
{terminal ? <button onClick={() => ctx.setForkVisible(true)}>分支</button> : null}
<button onClick={ctx.taskCost}>查成本</button>
<span className="muted">{ctx.actionMsg} {ctx.actionReasons}</span>
</Bar>
{ctx.forkVisible ? (
<div>
<input type="text" placeholder="分支首轮消息(必填,带历史的新会话)" style={{ width: "100%", boxSizing: "border-box", margin: "6px 0" }} value={forkMsg} onChange={(e) => setForkMsg(e.target.value)} />
<Bar><button className="seal" onClick={async () => { await ctx.forkTask(forkMsg); setForkMsg(""); }}>创建分支任务</button><span className="muted">{ctx.forkMsg}</span></Bar>
</div>
) : null}
</>
);
}