Files
pikasTech-unidesk/docs/reference/frontend.md
T
2026-05-17 16:46:42 +00:00

189 lines
40 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# UniDesk Frontend Reference
UniDesk 前端是 React 组件化工业控制台,不追求展示型大屏效果。设计目标是高信息密度、低装饰、低字号、低间距,并让调度、节点、事件和配置入口在单屏内快速切换。
## Source Contract
frontend 应用源码必须使用 TypeScript + React,禁止在 `src/components/frontend` 中维护手写 `.js` / `.jsx` 应用源码。浏览器请求的 `/app.js` 只能由 frontend Bun server 从 `src/components/frontend/src/app.tsx` 及其 TSX imports 转译生成;`public/` 目录只保存 HTML/CSS 等静态资产,不提交手写 `app.js`
`src/components/frontend/src/app.tsx` 只承担应用 shell、登录、全局数据加载、主模块/子标签路由和通用控制台页面。用户服务前端必须模块化到独立 TSX 文件,禁止继续把所有业务页面堆进 `app.tsx`。当前长期固定入口为:`todo-note.tsx` 承载 Todo Note 工作台,`findjob.tsx` 承载 FindJob 工作台,`pipeline.tsx` 承载 Pipeline 工作台,`met-nonlinear.tsx` 承载 MET Nonlinear 训练编排工作台,`claudeqq.tsx` 承载 ClaudeQQ QQ 消息网关工作台,`baidu-netdisk.tsx` 承载 Baidu Netdisk 存储工作台,`code-queue.tsx` 承载 Code Queue 控制台,`oa-event-flow.tsx` 承载统一 OA 事件流控制台,`k3sctl.tsx` 承载 k3s Control Plane 页面;新增用户服务也必须按同样规则新增独立页面模块,并由 `app.tsx` 只做导入和路由分发。
## Shared Dialog Component
通用模态弹窗统一复用 `src/components/frontend/src/dialog.tsx``UniDeskDialog`。新增确认弹窗、选择弹窗、表单弹窗或小窗口不得在业务页面内重复手写 `modal-backdrop` / `raw-dialog` 结构;业务页面只传入 title、body、actions、test id、关闭行为和少量业务 class。这样可以统一 `role="dialog"``aria-modal`、关闭按钮、点击遮罩关闭、滚动边界、action 区和 E2E 定位语义。
Code Queue 的 queue 合并弹窗是该公共组件的首个业务复用示例;后续用户服务前端需要独立小窗口时应优先复用 `UniDeskDialog`,只有原始 JSON 查看器这类已有专用展示器可以继续保留自己的内容渲染。
## Deployment Contract
任何会影响公网 WebUI 的 frontend 源码、样式或导航变更,都必须在同一任务内完成 Docker 上线;只运行 TypeScript 检查、`Bun.build` 或修改仓库文件不代表公网生效。
**关键规则**:frontend 容器启动时才从镜像内复制的 `src/components/frontend/src/app.tsx` 及其 TSX imports 构建 `/app.js`,运行中的 `unidesk-frontend` 不会自动读取工作区新文件。
**上线流程**
1. 修改源码/CSS
2. 执行 `bun scripts/cli.ts server rebuild frontend`
3.`bun scripts/cli.ts job status latest` 确认 `status=succeeded`
4. 用 E2E `frontend:layout-overflow` 验证改动已生效
禁止用"本地 bundle 校验通过"代替 `server rebuild frontend`;禁止跳过 E2E 验证直接交付。
前端修改必须通过 `bun scripts/cli.ts server rebuild frontend` 上线。确认 job status=succeeded 后,用 E2E `frontend:layout-overflow` 验证改动已生效。
如果公网仍是旧界面:1. 确认 job 已成功且容器创建时间晚于代码修改;2. 强制刷新或开无痕窗口;3. 用 curl -H "Cache-Control: no-cache" http://74.48.78.17:18081/app.js 查新 bundle。
## Time Zone Contract
frontend 所有默认可见的时间、日期、时钟、更新时间、心跳时间、Trace 时间、Gantt 时间轴刻度、导出文件日期和 `datetime-local` 输入值都必须按北京时间显示,即使用 IANA 时区 `Asia/Shanghai` / UTC+8。禁止依赖浏览器本地时区、服务器系统时区或裸 `Date.toLocaleString()` 默认值;新增页面必须复用 `src/components/frontend/src/time.ts` 的统一格式化和输入转换函数。原始 JSON 中的 ISO 时间戳只能在用户显式点击 `查看原始JSON` 后作为原始数据出现,默认结构化控件不得把 UTC/本地时区混入北京时间显示。
## Layout
左侧边栏只切换主模块:运行总览、资源节点、任务调度、用户服务、系统配置。顶部标签只切换当前主模块内的子功能;例如资源节点下的节点清单、资源标签、心跳状态只属于资源节点,用户服务下的服务目录、Todo Note、FindJob、Pipeline、MET Nonlinear、ClaudeQQ、Baidu Netdisk、Code Queue、OA Event Flow 只属于用户服务,和运行总览、任务调度、系统配置没有重复或共享语义。桌面端左侧边栏必须支持收起,只保留模块 code 和展开按钮,以便最大化主面板空间;移动端左侧边栏会转为顶部横向主模块条,但高度必须在不同主模块之间保持一致,并保持窄条、单行、不换行;主内容区无论内容多少都必须从顶部向下排列,空状态也不得上下居中制造大块留白。
## Route Model
frontend shell 必须把左侧主模块与顶部子标签编译为统一的 URL 路由,而不是在 router 中手工为每个页面逐个挂路径。长期规则如下:
- 导航权威数据只有一份:主模块、子标签、显示文案、route segment、默认子标签都从同一个 TypeScript 导航定义派生,左侧边栏、顶部标签、浏览器地址栏解析和前进/后退都复用同一套 registry。
- Canonical route 必须按主模块前缀分组:运行总览使用 `/ops/<tab-segment>/`,资源节点使用 `/nodes/<tab-segment>/`,任务调度使用 `/tasks/<tab-segment>/`,系统配置使用 `/config/<tab-segment>/`;只有用户服务主模块使用 `/app/<tab-segment>/`。例如 `态势总览` 固定为 `/ops/status/``Docker 状态` 固定为 `/nodes/docker/``Pipeline` 固定为 `/app/pipeline/`
- 当 future 需要新增主模块时,通用机制必须允许为该模块声明自己的顶层前缀,而不是继续把所有页面都塞进 `/app/*`
- 主模块根路径如 `/ops/``/nodes/``/tasks/``/app/``/config/` 只作为默认子标签或最近活动子标签的入口别名,实际当前页面仍应落到某个具体子标签;浏览器地址栏不能停留在“只有主模块,没有具体页面”的模糊状态。
- route segment 生成顺序固定为:显式 `routeSegment` > ASCII-safe `id` > 由 label 派生的 Unicode-safe slug > 稳定 hash fallback。这样新增 Unicode 标签时默认仍可得到稳定路径,而不要求每个标签单独写一段路由代码。
- 浏览器直开、刷新、`history.back()` / `history.forward()`、点击总览 drilldown 卡片、点击左侧边栏、点击顶部标签都必须走同一个路由状态机;不得出现“页面内容切换了,但 URL 没变”或“URL 变了,但 shell 仍停在旧 tab”的分裂状态。
- frontend Bun server 必须把这些模块前缀下的深链接路由作为 SPA 入口返回同一个 `index.html` 和同一个 UniDesk shell;实现上允许统一把非静态资源路径都回到同一个 shell,但判定标准是公网直开 `/ops/status/``/nodes/docker/``/app/pipeline/``/app/code-queue/` 等深链接时都不得 404,且必须和从主页导航进入时一样显示左侧主模块边栏、顶部状态栏、顶部子标签和完整业务页面。禁止为某个用户服务 deep link 返回缺少 UniDesk shell 的独立/standalone bundle;新增应用服务、普通页面和性能优化入口也必须满足“直开 URL 与站内导航同壳同页”的一致性要求。
## Overview Task Drilldown
`态势总览` 中的 `待处理任务` 指标必须可点击进入任务调度的 `待处理任务` 子标签,展示具体 queued、dispatched、running 任务的状态、Provider、已等待时间、payload 摘要和显式 `查看原始JSON` 操作。总览不得只给出无法追溯的数字;当后台把超时未终态任务转为 failed 后,待处理指标应回落,历史记录仍可在任务历史和执行结果中查看。核心指标还必须展示 `PGDATA`,显示 PostgreSQL 当前数据库用量、命名卷 `unidesk_pgdata_10gb` 和配置容量,便于从总览判断数据库状态。
## Performance Panel
`运行总览 / 性能面板` 固定路由为 `/ops/performance/`,用于汇总 UniDesk 控制面的通用性能指标。页面必须参考服务端性能看板形态,包含 `Bwebui` 内存/Bundle 趋势图、组件汇总、最近失败请求、内部操作汇总和最近慢操作。组件汇总至少展示组件名、请求数、失败数、失败率、平均延迟和 P95;最近失败请求必须在没有失败时明确显示“最近没有失败请求”,不能留空;内部操作汇总和慢操作必须展示服务、操作名、耗时、结果和细节。
性能面板的数据来自两个同源端点:frontend 自身的 `/api/frontend-performance` 记录 webui 静态资源、登录/session 和 API 代理请求,backend-core 的 `/api/performance` 记录 core API、用户服务代理、provider ingress、数据库与内部操作。页面只展示聚合表和趋势图,完整原始指标只能通过 `查看原始JSON` 打开。Bwebui 曲线优先使用浏览器 `performance.memory.usedJSHeapSize`,不可用时回退到 frontend bundle size 或 frontend 进程 heap,用 MB 作为纵轴口径。
## Task History Diagnostics
`任务调度 / 任务历史` 必须把任务生命周期渲染为可诊断表格,不得只显示更新时间和原始 payload 摘要。每行至少展示状态、任务命令和 id、Provider、任务耗时、载荷摘要、诊断信息、更新时间和显式 `查看原始JSON` 操作;终态任务的耗时按 `updatedAt - createdAt` 计算,待处理任务按当前时间减 `createdAt` 计算。耗时必须保留毫秒到秒的精度,小于 1 秒的任务显示小数秒或 `<0.01s`,不得把真实的亚秒级任务四舍五入或向下取整成 `0s`。失败任务必须在默认视图中提取 `result.error``result.message``result.stderr``result.reason` 或等价字段作为失败原因,并将 exit code、timeout、previous status 等关键诊断字段渲染为控件;完整 result 只能通过 `查看原始JSON` 展开。
## Resource Node Monitor View
资源节点模块必须提供 `资源监控` 子标签,用类似 Windows 任务管理器的性能页展示每个 provider 节点的 CPU、内存和硬盘用量历史曲线。该页面应包含节点切换、当前用量摘要、CPU/Memory/Disk 三条曲线、进程资源占用表、采样说明和 `Provider Gateway 升级` 控制区;曲线和进程表数据来自 backend-core 的 `/api/nodes/system-status`,不得在页面默认展示原始 JSON。内存曲线必须使用实际内存口径,不把 Linux page cache / buffer 计入占用。进程资源占用表必须用 React 表格控件展示进程名、命令摘要、PID、用户、状态、CPU、内存、PSS/RSS、磁盘 I/O、线程和运行时长,并支持按列排序;默认排序必须按进程 `memoryBytes` 降序,其中 `memoryBytes` 优先为 PSS 以避免共享内存重复计算,表头要能明确显示当前排序方向。完整进程快照只能通过 `查看原始JSON` 显式查看。
## Resource Node Docker View
资源节点模块必须提供 `Docker 状态` 子标签,用类似 Docker Desktop 的结构展示每个 provider 节点的 Docker daemon 状态。该页面应包含节点切换、daemon 摘要、Containers/Images/Volumes/Networks 指标、容器表格、镜像/卷/网络侧栏,并通过状态徽标区分 running、paused、exited 等状态。数据库命名卷 `unidesk_pgdata_10gb` 的检测和数据库命名卷卡片只对 `main-server` Provider 有效;D518、D601 等计算节点不要求也不应提示缺少 pgdata 数据库卷。`main-server` 的 Volumes 区域和数据库命名卷卡片必须显式展示 `unidesk_pgdata_10gb`,不得因为列表截断或匿名卷排序被隐藏。
## Provider Gateway Version View
资源节点模块必须提供 `网关版本` 子标签,按每个 Provider 展示 provider-gateway 版本号、升级策略、启动时间、能力摘要、SSH 透传可用性、远程更新可用性、最近远程更新状态和远程更新记录。SSH 透传可用性必须由 `unideskCapabilities` 是否包含 `host.ssh``hostSshConfigured``hostSshKeyPresent``hostSshTarget` 渲染为结构化徽标;远程更新可用性必须由 `unideskCapabilities` 是否包含 `provider.upgrade``providerGatewayUpgradePolicy: "always-enabled"` 渲染为结构化徽标。远程更新记录的数据源是 `provider.upgrade` 任务历史,默认必须渲染为结构化表格字段:状态、模式、任务 id、来源、耗时、策略、结果摘要和更新时间;亚秒级升级耗时必须显示小数秒,不得显示成 `0s`;不得把升级 plan、task result 或服务日志作为裸 JSON 直接铺在页面上。`最近远程更新` 应优先选择最新 `mode: "schedule"` 的真实升级记录,避免后续预检 plan 覆盖真正的升级结果;完整升级任务 JSON 只能通过对应行的 `查看原始JSON` 按钮显式打开。
## Provider Operation Availability
`资源节点 / 节点清单``资源节点 / 网关版本` 和总览中的 Provider 卡片必须显示每个计算节点的运维可用性,不允许只在原始 labels JSON 中隐藏。`SSH 透传` 徽标至少区分可用、未配置、缺 key、未声明能力;`远程更新` 徽标至少区分可用、策略待确认、未声明能力。可用性判断只来自 provider-gateway 注册/心跳 labels 和能力声明,不用前端猜测,也不得把缺失能力默认显示为成功。
## Provider Gateway Upgrade Control
`资源监控` 子标签中的升级控制区通过 backend-core `/api/dispatch` 下发 `provider.upgrade` 任务。默认 `预检升级` 只生成升级计划并回传任务结果;`执行升级` 才允许调度节点本地 updater 容器执行 Compose 重建。前端只展示结构化任务状态、task id、摘要和当前节点的远程更新记录,完整升级计划必须通过 `查看原始JSON` 显式查看。
## User Service Frontend
- `用户服务` 主模块用于展示挂载在计算节点或主 server Docker 中的业务后端。
- `服务目录` 必须显示 service id、Provider、仓库 URL、commit id、业务 Dockerfile/docker-compose 引用、节点后端私有映射、SSH 透传开发入口和运行态容器摘要。
- `Todo Note` 子标签必须把主 server `todo-note-backend` 后端渲染为 UniDesk React 控件,包括迁移清单、树形任务、筛选、提醒、拖放/移动、撤销/重做、字号控制和显式原始 JSON 按钮。
- `FindJob` 子标签必须把 D601 findjob 后端渲染为 UniDesk React 控件,包括岗位指标、岗位预览、草稿报告和显式原始 JSON 按钮。
- `ClaudeQQ` 子标签必须把 D601 ClaudeQQ 后端渲染为 UniDesk React 控件,包括 NapCat 容器登录二维码、NapCat HTTP/WS 状态、事件缓存、QQ 事件订阅表、订阅创建表单、消息推送表单、主用户私聊账号 `645275593` 标记、最近 QQ 事件、已发送记录和显式原始 JSON 按钮。
- `Baidu Netdisk` 子标签必须把主 server `baidu-netdisk-backend` 后端渲染为 UniDesk React 控件,包括 OAuth 设备码二维码/用户码登录、账号容量、配置工作根文件浏览(当前默认百度网盘根目录 `/`)、staging 目录上传/下载任务、上传/下载自测按钮与 MD5 结果、脱敏安全说明、日志摘要和显式原始 JSON 按钮;不得把 access token、refresh token、dlink 或 staging 文件字节流裸露到浏览器。
- `OA Event Flow` 子标签必须把主 server `oa-event-flow-backend` 后端渲染为 UniDesk React 控件,包括服务健康、事件表、tag 过滤、SSE live 状态、Trace/STEP stats 表、Code Queue/Pipeline 标签入口和显式原始 JSON 按钮;默认页面不得裸铺完整事件 JSON,事件表只展示结构化摘要,完整 envelope/payload 只能通过 `查看原始JSON` 打开。
- `k3s Control` 子标签必须把 D601 `k3sctl-adapter` 控制面渲染为 UniDesk React 控件,包括 control plane 状态、manifest 列表、D601 scheduler/read/write 实例、active instance、single-writer/no-fallback 路径、Kubernetes API service proxy 状态、kubectl/k3s snapshot 摘要和显式原始 JSON 按钮;页面只能通过 `/api/microservices/k3sctl-adapter/proxy/api/control-plane` 取数,不得直接访问 provider-gateway、NodePort、业务容器端口或裸 k3s/kubectl API。
- `Code Queue` 子标签必须把稳定 `code-queue` 用户服务渲染为 UniDesk React 控件,前端 API 基址只能是 `/api/microservices/code-queue/proxy`,不能继续使用旧 `/api/code-queue-direct` 别名;backend-core 会把 queue CRUD、submit、history、readAt 和轻量 Trace 读取分流到主 server `code-queue-mgr`,把 active run steer/interrupt、judge、dev-container 和执行面健康分流到 D601 k3s/k8s Code Queue 执行面。页面包括多 queue lane、queue 内串行、queue 间并行、queue 合并(点击“合并 queue”后必须用公共 `UniDeskDialog` 打开独立小窗口,用下拉菜单选择源 queue;不得把源 queue 选择控件塞进正常提交任务的 Queue 选择区;合并后自动删除源 queue,只保留合并后的目标 queue,目标 queue 按原 queueEnteredAt/createdAt 时间顺序串行)、任务 ID/复制任务 ID、引用按钮、任务耗时、任务提交/批量提交、引用任务 ID、创建成功提示、清空输入、模型下拉、执行 Provider 下拉、执行模式下拉(默认容器/本机或 `windows-native`)、显式入队份数、默认模型 `gpt-5.5`、MiniMax judge 状态、Codex CLI-like 输出流、attempt 终态、运行中追加 prompt、打断、手动重试和显式原始 JSON 按钮;`windows-native` 模式必须在任务 JSON、卡片和 Trace 头部显示,并要求非本机 WSL Provider 与 `/mnt/<drive>` 工作目录;Codex CLI-like 输出流必须始终保留任务的初始 `Submitted prompt` 和运行中 `Steer prompt`;整个 agent loop 消息流统一命名为专有名词 `Trace``Trace` 包含 assistant message、user prompt、system event 和 tool call,但非错误 system event 默认只保留在原始输出/数据库中,不在 TraceView 展示;Code Queue 与 Pipeline/OpenCode messages 必须共用 `src/components/frontend/src/trace.tsx` 的 Trace 公共组件、统一 Trace item 接口和 codex/opencode port 适配层;连续 read/edit/run 工具调用只是在 Trace 内折叠为可展开工具调用组,汇总格式至少包含 `xx read, xx edit, xx run`,并展示读取文件、编辑文件、运行命令和耗时摘要;最近 3 个工具调用保持展开,工具调用内容不得自动换行且必须在工具调用块内部横向滚动,工具调用组展开后不得再增加额外左侧缩进;message 与 prompt 必须自动换行,普通 message 不显示左侧项目符号缩进且永不折叠;Trace 首屏可以是摘要预览,但终态任务被选中后必须自动在后台加载完整 Trace,手动“加载完整 Trace”也必须从 Code Queue output archive 分页补齐早期 trace,不得把 preview 的 `hasMore=false` 当成完整历史;即使热状态为控制体积裁剪了早期 raw output,也要从结构化 `basePrompt/displayPrompt/promptHistory` 和 archive 合成完整用户输入与 agent trace,并且初始 prompt 默认显示注入前 prompt 而不是引用注入全文;当初始 prompt 含引用注入时,引用内容必须默认折叠,并只在 Trace 的初始消息中提供可展开的“最终传入 Codex 的真实完整 prompt”,不得再渲染独立 Prompt 全量卡片;多轮引用注入必须按上游/最早上下文在前、直接引用在后的顺序排列,每一轮必须有明确 `Reference Round N/M` 分割线和时间范围,不能用固定 6 轮截断引用链;点击队列引用按钮必须自动把该任务 ID 写入提交表单的引用输入框,引用任务 ID 创建新任务时必须自动注入 `bun scripts/cli.ts codex task <taskId>` 的提示;连续执行同一 prompt 应通过入队份数一次性生成多条任务,避免快速连点造成操作员误判。
- `MDTODO` 子标签必须把 D601 k3s `mdtodo` Service 渲染为 UniDesk React 控件,前端 API 基址只能是 `/api/microservices/mdtodo/proxy`;页面包括 TODO Markdown 文件列表、任务树、状态徽标、标题与正文编辑、新增根任务/子任务、删除任务、执行命令生成、hostPath 健康摘要和显式原始 JSON 按钮,不得 iframe 原 VS Code webview、公开 VSIX 旧前端或把完整 Markdown/JSON 默认铺在页面上。
- `Code Queue` 前端改进必须在同一任务内重建并上线公网 frontend,不能只修改源码或本地 bundle;重建 frontend 是无状态 WebUI 替换,不会导致 Code Queue 长期任务失败。已结束未读任务只能在 task card 边角显示类似未读消息的 `codex-unread-badge` 圆点和“标为已读”操作,不得把整张卡片改成红色/琥珀色失败态边框、背景或胶囊标签;状态栏的“结束未读”提示也不得使用失败态红色。
- `Code Queue` 前端必须把 PostgreSQL-backed backend API 作为 task、queue、readAt/未读状态和 attempt 状态的唯一数据来源;不得用 `localStorage``sessionStorage` 或 IndexedDB 持久化这些业务状态,也不得在后端标记已读失败时伪造本地成功。前端允许保留 React 内存态、请求 in-flight guard 和本轮页面缓存,但刷新页面或切换设备后的状态必须完全由后端 PostgreSQL 数据恢复。
- `Code Queue` 前后端通信必须采用渐进式披露:首屏只请求 queue/task 轻量摘要、必要的 selected preview 和小体积统计,不得默认拉取完整 transcript、raw output、原始 JSON 或全部历史任务;加载下一页或搜索分页时必须显式传递 `selected=0``includeActive=0``stats=0` 等价开关,避免每一页重复请求 selected/active/stats;点击/选中 task 后再按需加载 summary、prompt part、trace step、raw output 或完整 Trace。`read`/`mark all read` 应调用专用 mutation 并用后端返回的 patch 更新当前内存态,不能为了隐藏未读圆点而强制刷新完整 overview;请求仍需遵守 PostgreSQL 权威源,失败时不得本地伪造已读。Code Queue 性能问题应优先通过缩小 API 响应、分页/cursor、去重 in-flight 请求、短 TTL 且 mutation 失效的页面缓存和后端 SQL 聚合解决,避免以重写渲染层或把大 JSON 藏在 DOM/React state 中规避慢请求。
- `Code Queue` 的 queue/session 左侧边栏必须提供 task 关键词搜索,并采用顶部对齐和内容高度优先布局:搜索栏、列表、分组和 task card 都不得用居中、space-between、stretch 或隐式等高网格去拉满侧栏高度;item 少时允许下半部分留空,不能把单个 item 拉高来铺满;每个 task card 必须显示 `最近更新: ...前` 这类相对更新时间,便于判断运行中的 Trace 是否卡住;`queued` task card 的状态徽标必须显示排队原因,例如 `QUEUED(PREV TASK)``QUEUED(MEM LIMIT)`。Code Queue 的事件驱动刷新必须订阅统一 `oa-event-flow``service:code-queue` tag stream,并消费 `trace-stats-updated` 等统计变化事件;旧 Code Queue 私有 SSE 不得作为 Trace/STEP 刷新权威。左侧 STEP、执行过程摘要页的 read/edit/run/STEP 只能显示 `oa_trace_stats` 投影值;投影缺失时显示同步中/`--`,不得回退到 task JSON、transcript 或前端 `toolCallCount` 重算。事件驱动刷新只能更新队列列表与当前选中任务的详情,不得因为其他任务产生 `task-step``task-updated` 而自动切换当前选中的 task;只有用户点击任务、提交新任务或显式切换 queue 才能改变选中任务。提交任务时必须立即锁定 prompt、引用 ID、queue、模型、工作目录、最大尝试和入队份数等输入控件,显示等待状态,并用前端 in-flight guard 阻止重复点击造成重复入队;当解析到多个待入队任务时必须显式要求用户勾选批量确认,防止 `---` 分隔或入队份数误操作导致错误传入多个任务。Trace 面板的主滚动条使用全站细窄现代滚动条;工具调用块内部的横向滚动必须可滚动但隐藏横向滚动条,避免移动端阅读被滚动条占用。公共 `TraceView` 的自动滚动必须采用 follow-tail 语义:只有当前滚动位置在底部附近时才跟随新增输出;用户手动向上滚动后立即暂停自动滚动,异步刷新不得把视图拉回底部,直到用户再次滚动到最底部才恢复自动跟随。
- 用户服务页面不得 iframe 业务旧前端、Todo Note 原 Vite 前端或 Pipeline 自身 WebUI,不得把用户服务后端端口暴露为浏览器直连 URL,也不得把业务 API 的 JSON 裸铺在页面上。
- `Pipeline` 子标签是 D601 `/home/ubuntu/pipeline` 的 UniDesk host UI。
- Pipeline 仓库自带 WebUI 前端已经废弃;UniDesk frontend 是唯一用户可见的 Pipeline UI。
- Pipeline 用户服务只提供 backend/control APIUniDesk 通过 `/api/microservices/pipeline/proxy/...` 拉取 snapshot、Gantt DTO、node detail 和控制接口。
- Pipeline 页面必须通过同源用户服务代理展示 `model/minimax-m27` 的 MiniMax 限额,包括当前窗口总量/已用/剩余、重置时间和查询状态;主界面不得展示 API key,完整 quota JSON 只能通过显式 `查看原始JSON` 打开。
- Pipeline 控制与观测的最终权威是统一 `oa-event-flow` 事件流与 Pipeline OA 状态机;分阶段迁移不得在交付态保留点对点控制、旧审核事件或旧 batch 推进逻辑,共享事件表与 tag 订阅规则见 `docs/reference/oa-event-flow.md`Pipeline 控制流完整规则见 `docs/reference/pipeline-oa-event-flow.md`
- 基础视图必须包含组件矩阵、React Flow 控制图框图、epoch 列表、运行材料索引、epoch 甘特图和 node 精细控制面板。首屏信息顺序必须把核心操作前置:`控制图` 是 Pipeline hero 之后的第一个业务面板,`Epoch 甘特图` 紧随其后;观测指标、评分器、MiniMax 限额、OA 事件流和组件矩阵只能排在这两个核心面板之后,移动端也必须保持同一 DOM 顺序。控制图和甘特图的右侧详情栏默认收起,主图占满可用宽度;点击控制图 node 或甘特图执行线/事件点后才展开对应右侧栏,并必须提供手动收起入口。
- Pipeline scorer 结果必须结构化展示为 `x/N`、通过率、scorer 状态、item pass/fail badge 和 raw inspector 入口;主界面不得裸展示 scorer JSON。
- 用户点击控制图中的 node 后,必须通过同源用户服务代理抓取该 node 执行过程,并支持向运行中 node 追加 prompt、给下次尝试下发 guide、对已完成 node 排队 modify、提交 monitor 审核 approve,以及排队 restart/redo。
- append-prompt、guide、modify、redo/restart 统称为 node 的管理行为;approve 是 monitor 审核决策;fetch/status 只属于观察行为。
- UniDesk 只负责同源代理、结构化展示和人工控制入口,不直接写 Pipeline `.state` 文件。
- node 精细控制面板的 append、guide、modify、approve、redo/restart 必须调用 Pipeline 后端 OA control API`node-control` 可作为 HTTP 路由名保留,但内部必须写入 OA 控制事件,并把 UniDesk frontend 发起者记录为结构化事件;历史兼容字段可继续使用 `sourceKind=webui` 表示前端来源。
- `Pipeline` epoch 和甘特图规则。
- 一个 epoch 定义为同一个 pipeline 从入口到终态完整执行一遍。
- 同一 `pipelineId` 下的多个 run 必须作为多个 epoch 展示,并允许操作员切换当前 epoch;控制图、node 控制面板、运行材料索引和甘特图都必须跟随当前 epoch。
- epoch 下拉框必须通过同源 snapshot 轮询自动看到新 run,不能依赖浏览器强制刷新,但也不应在用户已选中旧 epoch 时自动跳走。
- 甘特图默认时间尺度固定为 `100 px/min`,并且默认关闭 `自动隐藏空闲列`,让操作员首次进入页面时先看到完整 node 列。
- epoch 甘特图的纵轴是时间,左侧第一列展示时间刻度,后续每列对应一个 node;node 在 `startedAt``finishedAt` 或当前时间之间处于工作态时绘制竖向工作条,空闲时间留空。
- 甘特图 node 列顺序必须遵循 pipeline 拓扑,从上游到下游依次向右展开;不得把下游 node 排在上游左侧。
- 甘特图必须提供时间尺度滑块,用同一份时间数据调整每分钟像素密度:全局尺度压缩纵向高度以查看完整 epoch,细节尺度拉长纵向高度以查看短时间内的 prompt 点、控制点和执行线。
- 甘特图必须提供当前 epoch 的渲染图导出按钮;导出图应使用当前可见布局,执行区间先绘制,控制/观察箭头覆盖在执行区间之上,真实事件点覆盖在最上层,避免箭头被执行条遮住。
- 精确数学图形的坐标权威规则。
- Pipeline epoch 甘特图的 y 坐标权威在 UniDesk 前端完成,后端只提供 run、procedure、prompt/control 事件和时间戳事实,避免跨 provider HTTP 代理在后端反复计算布局。
- 前端必须用同一线性公式把 `startMs/endMs/tick.ms/marker.ms` 映射到 `layout.source=frontend-y``chartHeight``y = clamp((ms - startMs) / (endMs - startMs), 0..1) * chartHeight`;执行区间 `top` 使用 `startMs`,自然高度使用 `endMs-startMs` 对应 y 差,短区间可设最小可点击高度但不得改变记录的 `data-y1/data-y2`
- 时间尺度滑块只改变前端每分钟像素密度和 `chartHeight`,不得因为缩放重新请求后端 Gantt 布局;run 级过程数据使用轻量 timeline 视图刷新。
- 甘特图必须根据当前可见的前端 y 区间自动隐藏该窗口内没有任何工作区间或事件点的 node 列,避免宽图把无关空闲 node 挤在屏幕中。
- E2E 必须验证前端 DOM 暴露的 `data-start-ms/data-end-ms/data-chart-height/data-y*` 与公式计算结果一致,并确认布局来源为 `frontend-y`
- `Pipeline` 甘特图事件来源。
- 甘特图上的执行线、prompt 点、控制点和 monitor 虚线箭头必须通过同源 `node-control` HTTP 读取接口驱动。
- run 级图形事实数据使用 `GET /api/node-control/runs/{runId}?view=timeline&tail=N`;该接口不得要求返回后端 y 坐标。
- node 级 OpenCode 明细来自用户选择后的 `GET /api/node-control/runs/{runId}/nodes/{nodeId}`,避免跨 UniDesk provider HTTP 代理时一次性拉取全 run 的大体积 step 数组。
- 这些 monitor/control 事件的权威来源是 Pipeline OA 事件流;Pipeline 后端会为每条事件自动附加当前 `epoch:{runId}``pipeline:{pipelineId}` scope tag,并在 monitor 订阅时自动追加同一组 scope 限制,避免不同 epoch 之间串流。UniDesk 只消费 Pipeline 暴露的结构化读接口,不直接依赖旧的 monitor append 文件语义;OA 事件流的完整约束见 `docs/reference/pipeline-oa-event-flow.md`
- prompt 点只能来源于 attempt 级 `controlEventRecords` 中的 prompt-delivered 事件;`controlPromptRecords` / `monitorPromptRecords` 只能作为旧数据取证字段,不得参与交付态 UI 渲染或补点。
- Monitor prompt 点必须将 `node-finished``node-long-running-observation` 作为一等结构化事件展示;`node-finished` 是中性完成事实,不得携带是否审核的权威策略字段。是否需要审核由 OA backend 根据当前 epoch 的 Pipeline config/topology 判定;审核开启时同一个 node completion 在 UI 中只保留一条完成/等待审核点,不得再生成独立 `legacy node audit request event``legacy monitor audit request event` 点。
- 控制点和 monitor/人工控制箭头只能来源于 run 级 `controlEvents``controlCommands` 不得作为 Gantt/UI 补点或控制显示 fallback。
- 控制事件必须依赖后端记录的 `sourceKind``sourceNodeId``targetNodeId``commandId``eventId``resetNodeIds``interruptedProcedureRunIds` 等结构化字段识别 monitor、UniDesk frontend、CLI 等发起者,不能通过解析 monitor 自己的 step 文本来反推。
- `Pipeline` monitor 审核与详情展示。
- Pipeline 页面必须提供结构化 `OA 事件流` 诊断面板,通过 `/api/microservices/pipeline/proxy/api/oa-event-flow/diagnostics` 展示 100% 事件流模式、禁止残留数、no-audit 证据、monitor 审核证据、控制 queued/applied 计数和近期 run 证据;原始 diagnostics 只能通过 `查看原始JSON` 打开。
- Pipeline 后端可通过 `control.monitoring.audit.enabled=true` 开启 monitor 审核门禁,但是否审核必须由 OA backend 加载 config snapshot 后判断,不能写入 `node-finished` 事实事件。
- 当前 pipeline 存在 monitor node 时,node 成功后必须先进入 OA 事件流;OA 判定需要审核后才向 monitor 推送 prompt,随后必须等待 monitor 通过 OA 控制事件发起 `approve``modify``redo/restart` 或等价管理行为。
- 长时间处于 `running` 的 node 必须触发 `node-long-running-observation` 观察事件,默认节奏为 2、5、10、20 分钟,之后每 20 分钟继续发送一次;前端需要把这类观察事件和普通 `node-finished` 清晰区分。
- 前端应把审核/控制事件渲染为结构化点和虚线箭头;有 monitor 的 pipeline 中,monitor node 必须固定在甘特图第 1 个 node 列,其余 node 再按上游到下游顺序从左到右排列。
- `node-long-running-observation` 必须显示从被观察 node 指向 monitor node 的观察连线,不能只在 monitor 列留下孤立事件点;观察来源本身不是一个真实行为,不能在被观察 node 上额外绘制“观察来源”点,点只代表真实收到 prompt、发出管理行为、发出审核结果等事件。
- 甘特图箭头的末端必须在目标点外回缩约一个箭头长度,箭头尖不能插入 prompt 点、控制点或 monitor 观察点内部,避免把“连线”和“真实行为点”混在一起。
- 仍处于 `running` 的 node 必须显示从实际开始时间延伸到当前时间的实时执行条,并用明确的闪动/扫描效果标识“仍在执行”;不得把 running node 渲染成只有起始点或 1s 极短条线。
- 点击甘特图中的执行线、prompt 点或控制点后,右侧边栏必须从默认收起状态展开,并展示结构化事件字段、匹配的 procedure/attempt、以及对应 OpenCode step 的摘要与展开详情,而不是在主界面直接铺 raw JSON、JSONL、worker log 或 control event 文本。
- OpenCode step/message 展示必须进入公共 `TraceView`,视觉与交互以 `Code Queue` 的 Trace 为唯一标准;Pipeline 原有 `pipeline-opencode-step``pipeline-step-message-card``pipeline-opencode-part` 等 step/message/tool 卡片风格已废弃,不得继续作为用户可见 Trace。
- 右侧边栏中的 OpenCode Trace 必须把公共 session 信息(agent、model、session id)聚合到 Trace 头部,不得在每个 step 重复;Trace 正文必须由 `src/components/frontend/src/trace.tsx` 的 opencode port 转换后统一渲染,工具调用折叠、摘要、横向滚动、message 去缩进规则与 Code Queue 完全一致。
- 右侧边栏排版必须优先保护横向可读宽度:时间放在 step 顶部 header,而不是单独占用左侧窄列;默认摘要不得引入右侧边栏内部横向滚动条,也不得因为窄列挤压把 step 高度拉得过高。
- OpenCode Trace 不能使用 Pipeline 旧连续 step 装饰线或旧 step 卡片;相邻 step 之间若存在真实时间空闲区间,不得被任何连续连接线误渲染为持续执行。
- 调整任何高信息密度右侧边栏布局时,都必须把 `总高度``横向滚动条` 作为显式验收指标,用 Playwright 打开真实页面验证,而不是只看静态代码或本地想象。
- 运行材料只能作为结构化索引行展示计数、状态、时间和来源摘要,完整 JSON、JSONL 或 log tail 只能通过显式 `查看原始JSON` 按钮打开。
- `Pipeline` 渲染与算法验证。
- 涉及 monitor 审核、管理行为或甘特图算法的改动,必须用 Pipeline 侧通用 fixture 组合验证。
- 验证组合至少包括拓扑 split/merge/feedback、生产型 PikaPython workflow、`monitor-management-behavior-test``monitor-audit-behavior-test`
- 所有 fixture 都应走同一渲染与事件解析路径;不得为了某个 pipeline id、node id 或 run id 在 UniDesk 前端加入特例。
- `MET Nonlinear` 子标签必须把 D601 `/home/ubuntu/met_nonlinear` 的训练编排后端渲染为下载器式工作台。
- 工作台包括项目库选择、从已有 Project fork 新 Project、加入待启动队列、启动队列、最大并发设置、当前队列、已完成、失败诊断、GPU/镜像、训练进度、ETA、历史记录和显式原始 JSON 按钮。
- 运行中训练若后端未直接给出 ETA,前端必须用 `startedAt`、当前 epoch 和目标 epoch 做可解释的剩余时间估算。
- 训练队列和已完成列表必须显示训练速度 `epoch/h`
- 项目库必须按真实文件路径分层显示 `projects/``ex_projects/`,文件夹计数必须等于其子树中的 Project 数,不能用模型名、状态或其他派生字段替代文件树层级。
- 项目库、当前队列、已完成和失败诊断中的行必须可点击打开结构化详情。
- 详情必须把 `config.json``data/training_state.json``data/training_info.json``data/metrics.json``data/model_info.json``data/compute_analysis.json` 中的训练状态、模型参数量、模型层、指标和 data 文件清单渲染为字段卡、表格和 chip,不得默认显示裸 JSON。
- 不得提供硬编码的固定数量/固定轮数测试按钮。
## Component Data Rendering
前端必须把 backend-core 返回的 JSON 渲染为合适的控件:状态徽标、指标卡、表格列、标签 chip、字段摘要、任务结果卡、日志行和表单控件。默认页面禁止暴露裸 JSON、`pre` JSON 或整段 `JSON.stringify` 文本;只有用户明确点击 `查看原始JSON` 按钮后,才允许在弹窗或高级编辑区展示原始 JSON。
## Loading State Title Indicator
所有 UniDesk 前端模块只要处于加载、刷新、提交、抓取详情、导入导出、远程调度或其他异步等待状态,必须在对应卡片/面板/侧栏的标题框内显示公共加载小图标,避免操作员把“仍在加载”误判为“数据确实为 0”。加载图标统一复用 `src/components/frontend/src/loading-indicator.tsx` 中的 `LoadingTitle` / `LoadingIndicator`,视觉为简洁旋转圆环,不包含鼠标指针或其他额外符号;禁止各页面临时手写不同 spinner、只在按钮文案写“刷新中”或只在空状态里提示加载。新增面板组件必须透传 `loading` 属性到标题框,已有数据列表在刷新期间也应保留旧数据并在标题框显示加载图标。
## Login
frontend 提供账号密码登录,默认账号为 `admin`,默认密码为 `Liang6516.`。登录会话使用 frontend 容器签发的 HttpOnly Cookie;浏览器后续只访问同源 frontend APIfrontend 再通过 Docker 内网代理 backend-core。
## Visual Language
界面使用深钢蓝、炭黑、琥珀和冷青作为工业控制台色板;字体选择窄体和等宽组合,以减少横向浪费。字号、表格行高和面板间距保持克制,避免大标题和松散卡片造成信息密度下降。
## Data Flow
浏览器不直接访问 backend-core。frontend 容器服务 React 静态资产、登录接口、会话接口和同源 API 代理;代理目标是 Docker 内网 `http://backend-core:8080`。provider-gateway 的外部接入地址只作为连接拓扑信息展示,不作为浏览器数据源。