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:
1 parent
3f4e849a93
commit
ebc5624893
57 files changed
+4789
-1746
No files matched your search
@@ -0,0 +1,46 @@
|
||||
import { useConsole } from "../context";
|
||||
import { DataListRaw } from "../components/ui";
|
||||
import { shortId } from "../api/client";
|
||||
|
||||
// Left-rail compact task list. The live 9-column table does not fit a
|
||||
// 300px rail, so the rail shows id + status + prompt + executor; the
|
||||
// full metadata (turn, root turn, host, PR, updated) lives in the right
|
||||
// detail panel for the selected task. Filters/search are the live ones.
|
||||
export default function TaskListSection() {
|
||||
const ctx = useConsole();
|
||||
return (
|
||||
<div className="left-tasks">
|
||||
<div className="bar">
|
||||
<select aria-label="状态筛选" value={ctx.statusFilter} onChange={(e) => ctx.setStatusFilter(e.target.value)}>
|
||||
<option value="">状态:全部</option>
|
||||
{["queued", "running", "completed", "failed", "interrupted", "merged", "merge-failed"].map((s) => <option key={s} value={s}>{s}</option>)}
|
||||
</select>
|
||||
<select aria-label="执行器筛选" value={ctx.executorFilter} onChange={(e) => ctx.setExecutorFilter(e.target.value)}>
|
||||
<option value="">执行器:全部</option><option value="local">local</option><option value="docker">docker</option><option value="cloud">cloud</option>
|
||||
</select>
|
||||
</div>
|
||||
<input type="text" placeholder="搜索任务 id / prompt" value={ctx.searchText} onChange={(e) => ctx.setSearchText(e.target.value)} />
|
||||
<div className="bar">
|
||||
<button onClick={ctx.refreshTasks}>刷新</button>
|
||||
<span className="muted">{ctx.taskCountText}</span>
|
||||
</div>
|
||||
{ctx.taskError ? <div className="err">{ctx.taskError}</div>
|
||||
: !ctx.projectId ? <div className="muted">先选项目</div>
|
||||
: ctx.filteredTasks.length === 0 ? <div className="muted">{ctx.tasks.length ? "没有符合筛选条件的任务" : "该项目暂无任务"}</div>
|
||||
: (
|
||||
<DataListRaw>
|
||||
{ctx.filteredTasks.slice().reverse().map((t) => (
|
||||
<li key={t.id} className={ctx.currentTask?.id === t.id ? "selected-task" : ""} onClick={() => ctx.showDetail(t.id)} style={{ cursor: "pointer" }}>
|
||||
<span className="row-main">
|
||||
<b>{shortId(t.id)}</b> <span className={"st-" + t.status}>{t.status}</span>
|
||||
<br />
|
||||
{t.plan ? "[计划] " : ""}{t.forked_from ? "[分支] " : ""}{t.coordinator_parts ? "[协调] " : ""}{t.parent_task_id ? "[协调子任务] " : ""}{String(t.prompt || "").slice(0, 60)}
|
||||
<br /><span className="muted">{t.executor || ""}</span>
|
||||
</span>
|
||||
</li>
|
||||
))}
|
||||
</DataListRaw>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in new issue
Block a user