PicoBot/docs/superpowers/specs/2026-07-23-webui-refactor-design.md

305 lines
20 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.

# PicoBot WebUI 全面重构设计
- 状态:设计已确认,待实现
- 日期2026-07-23
- 范围:前端(`webui/`)全面重构 + 必要的后端接口新增/调整(`src/gateway/`
## 1. 背景与目标
现有 WebUISvelte 5 + Bits UI随二进制嵌入已具备聊天、配置、记忆、任务、日志、主题切换等基础能力但视觉与交互体验一般且缺少运行状况观测、工具/Skill 浏览、实时日志等能力。本次重构目标:
1. 前端可直接与 PicoBot 沟通(聊天,已有,增强体验)
2. 可修改 PicoBot 各项配置(已有,增强)
3. 可观察 PicoBot 运行情况(**新增**:运行状况仪表盘)
4. 可查看工具列表、Skill 列表(**新增**
5. 可查看实时日志(已有轮询,**升级为流式**
6. 支持亮色/暗色的美观且易用的 UI**全面重设计**
7. 可查看并管理记忆、定时任务等信息(记忆**新增可编辑/可删除**
## 2. 约束与不变量
- **单二进制发布**:前端构建产物仍打包进二进制,运行时从内存提供(`build.rs` → Cargo `OUT_DIR``include_str!`/`include_bytes!`)。最终用户无需 Node.js。
- **无外部 CDN**:生产页面不加载任何 CDN 资源。现有 CSP 为 `default-src 'self'; connect-src 'self' ws: wss:; img-src 'self' data:; style-src 'self'; script-src 'self'; base-uri 'none'; frame-ancestors 'none'`。字体等资产必须同源内嵌。
- **设备鉴权**:所有管理 API 与 `/ws``AuthManager` 保护;新增端点与 `/ws/logs` 同样走现有设备鉴权。
- **聊天复用现有链路**:浏览器聊天继续使用 `/ws``cli_chat` 渠道,复用 dialog scope、每会话串行 worker、历史持久化、出站 lane 与 turn 快照。WebUI 不直接调用 Provider 或 SessionManager。
- **密钥安全**`/api/status` 等任何新响应不得包含密钥;日志脱敏在源头(不在日志中记录 secret
- **只读优先**:除配置(现有可写)与记忆写入(新增)外,其余新能力均为只读。
## 3. 总体方案
- **定位**:均衡控制台,但**聊天优先**——打开即聊天,其余功能区通过扁平导航平等直达;全局"活动脊"提供常驻运行感知。
- **技术栈**:继续使用 Svelte 5 + Bits UI + Vite不引入新框架或状态管理库。
- **推进方式**:一份总体设计 + 分阶段实现(见 §9
## 4. 设计系统Signal Deck
视觉方向为"仪表盘 / 工程仪器":石墨蓝基底 + 琥珀(活动)/青绿(健康)双信号色,数据全部等宽字体,顶部一条永远在呼吸的"活动脊"作为签名元素。
### 4.1 色彩 Tokens
暗色(石墨蓝基底):
| Token | Hex | 用途 |
|-------|-----|------|
| bg | `#0B1017` | 页面背景 |
| panel | `#0E1520` | 面板/卡片 |
| panel-2 | `#131C29` | 次级面板/悬停 |
| border | `#1D2733` | 边框 |
| border-strong | `#2C3A4C` | 强调边框/输入框 |
| text | `#E7ECF3` | 主文本 |
| text-soft | `#B8C4D4` | 次级文本 |
| muted | `#8FA3B8` | 辅助文本 |
| faint | `#5B6B7E` | 最弱文本/时间戳 |
| amber | `#FFB454` | 活动/进行中/警告 |
| teal | `#2DD4BF` | 健康/成功/只读 |
| danger | `#FF7B86` | 错误/危险/独占 |
| info | `#6AA6FF` | 信息/Timeline/思考 |
| code-bg | `#080C12` | 代码/日志底 |
亮色(冷纸白,信号色加深保证对比):
| Token | Hex | Token | Hex |
|-------|-----|-------|-----|
| bg | `#EEF1F5` | text | `#1A2230` |
| panel | `#FFFFFF` | text-soft | `#3D4B5E` |
| panel-2 | `#F4F6F9` | muted | `#5B6B7E` |
| border | `#D8DEE8` | faint | `#8494A8` |
| border-strong | `#C2CCD9` | amber | `#C47400`(填充 `#E08600` |
| teal | `#0D9488` | danger | `#D94354` |
| info | `#2F6FD0` | code-bg | `#F7F9FC` |
**关键规则**:亮色模式下"活动脊"仍为深色条(`#0E1520`),像物理仪器上的 LED 读数——两种主题下同一个记忆点,不做简单反色。
### 4.2 字体
- 展示 / UISpace Grotesk内嵌 woff2仅拉丁中文回落系统字体PingFang SC / Microsoft YaHei / Noto Sans SC
- 数据 / 等宽JetBrains Mono内嵌 woff2用于所有指标、日志、时间戳、small-caps 标签。
- 字号阶梯9pxsmall-caps 标签letter-spacing .12.16em/ 11pxcaption、日志/ 12.514px正文/ 16px小标题/ 19px标题/ 2432px指标数字
- 生产环境不加载 CDN字体以内嵌二进制资产提供见 §8.3)。
### 4.3 签名元素活动脊Activity Spine
全局置于每个页面顶部的等宽状态条,两种状态:
- **有 Turn 在跑**:琥珀脉冲点 + `TURN 042 · STREAMING` + 实时 `▲ tok/s``ctx``queue``ws`,右侧 `gen #N · uptime · version`
- **空闲**:青绿常亮点 + `IDLE` + 最近 turn 摘要。
Turn 实时状态来自聊天 WS 已有的 `turn_updated` 快照(本就实时推送,`WsOutbound::TurnUpdated`gen/uptime/version 等来自 `/api/status` 轮询。各字段来源:`▲ tok/s` 由前端对相邻 `turn_updated` 帧的 `usage.completion_tokens` 差值求导(快照本身不含速率字段);`ctx` 取自 `usage.prompt_tokens``queue`/`ws` 取自 `/api/status`
### 4.4 核心组件
按钮primary=amber / secondary / ghost / danger、状态徽标正常/活动中/异常/离线)、指标块(大等宽数字 + sparkline + 分段容量条、日志行level 着色)、输入框、工具调用卡片(默认折叠,运行中=琥珀脉冲、完成=青绿、表格行、标签页、Toast。图表统一手写 SVG sparkline / 分段仪表,不引入图表库。
## 5. 信息架构与应用外壳
- **导航**:左侧扁平导航——聊天(落地页)、概览、工具&Skills、日志、记忆、任务、配置底部网关状态 + 主题切换。
- **应用外壳**:全局持有聊天 WS 连接(使活动脊在每个页面可用)、主题状态(`localStorage` 持久化 + `prefers-color-scheme` 默认)、设备鉴权状态(未配对显示 PairingPage
- **页面清单**:聊天 / 概览 / 工具&Skills / 日志 / 记忆 / 任务 / 配置,外加 PairingPage鉴权
## 6. 页面设计
### 6.1 聊天页(落地页)
三栏布局:会话列表(搜索/新建/按日期分组/未读点)| 消息流 Todo 计划侧栏(默认收起,按需展开)。
- reasoning 与工具调用默认折叠为紧凑卡片(运行中=琥珀脉冲,完成=青绿)。
- 流式 turn 显示光标与 `▲ tok/s`,输入区出现"停止"按钮。
- 斜杠命令补全来自后端 `get_slash_commands`(不在前端硬编码命令表)。
- 附件走 HTTP 上传(`POST /api/chat/{client_id}/uploads`WS 只传 `upload_id`;历史附件经 `GET .../attachments/{index}` 下载,安全 MIME 白名单内联预览。
- 正常完成合并 `turn_committed` 增量校准历史,不整段重载;断线/失败/取消用 `SessionHistory` 校准。
### 6.2 概览页(运行仪表盘)
- 主状态条:`RUNNING`、运行代、uptime、版本、WS 连接数、后台任务数、上次重载。
- 指标块(带 sparkline会话数、今日 Token+费用)、工具调用(+运行中)、今日 Turns+p95 延迟)。
- Provider 表:名称、模型、状态、延迟 sparkline、今日用量、费用。
- 消息总线inbound/outbound/control 队列深度分段容量条、活跃 lane 数、调度器状态、MCP 连接。
- 渠道状态feishu / cli_chat 等连接状态。
- 调度器任务数、下次运行、7 天失败数。
- 实时活动流:最近 turn/memory/job 事件。
- 数据来自 `/api/status`,默认每 2s 轮询。
### 6.3 工具 & Skills 页
- 三个标签页:工具 / Skills / MCP。
- 工具卡片名称、来源builtin/mcp、描述、调用次数、**能力徽标**、可展开参数 schema。
- **能力标识**(来自 `Tool` trait
- `◇ 只读`teal= `read_only()`
- `⇉ 可并发`info= `read_only() && !exclusive()`(即 `concurrency_safe()`
- `△ 有副作用`amber= `!read_only()`
- `■ 独占`danger= `exclusive()`(如 bash
- 支持搜索 + 按能力筛选(全部/只读/可并发/有副作用/独占)+ 图例。
- Skills 标签名称、描述、always、来源目录。MCP 标签:服务器名 + 连接状态。
### 6.4 日志页(实时流式)
- 工具栏level 过滤(全部/INF/WRN/ERR、关键字搜索、暂停滚动、下载。
- 日志行:时间戳 + level 着色 + target + 消息,自动跟随尾部。
- 进入页面:`GET /api/logs`(保留)拉历史尾 → `/ws/logs` 接管实时;断线重连重新拉尾对齐。
- 顶部显示连接状态(实时推送中 · 行/分)。
### 6.5 记忆页(可编辑 / 可删除)
- **权限**Knowledge 与 Timeline 均可编辑、可删除。
- **大量条目展示**:统计条(总量/Knowledge/Timeline/覆盖会话)→ 语义搜索优先 → 分类/会话/排序筛选 → 日期分组高密度行(列表/卡片视图可切换)→ 虚拟滚动 + 分页加载("已显示 100 / 1,284 · 加载更多")。
- 行内编辑textarea + importance 调节 + 保存/取消(按 key upsert`updated_at` 自动刷新)。
- **删除警告分级**
- Knowledge普通确认"删除后不可恢复,影响后续召回")。
- Timeline**强警告**——"Timeline 是压缩后的历史上下文,删除后模型将永久失去该时段长期记忆且无法自动重建;原始消息仍保留在聊天历史,但不再进入模型上下文",按钮文案"我了解,确认删除"。
### 6.6 任务页
- 两个标签页:定时任务 / 后台任务。
- 定时任务表名称、cron 表达式、下次运行倒计时、上次运行、最近 10 次运行状态点(绿/琥珀/红)、启用状态;可展开运行记录(时间/耗时/摘要)。
- 后台子任务列表:名称、来源 session、运行中脉冲/完成、耗时。
- 只读浏览。
### 6.7 配置页(唯一可写页面之一)
- 标签页config.json / USER.md / AGENTS.md。
- config.jsonJSON 编辑器,密钥掩码(`********`,原样提交自动还原)、实时 JSON 校验 + default agent 有效性、未保存修改提示。
- 右侧配置大纲gateway/providers/agent/channels/memory/scheduler标注"重启生效""含密钥"。
- 重载状态卡:运行代、相位、上次重载结果。
- 操作:保存 / 保存并热重载 / 放弃修改。
- 复用现有 `GET/PUT /api/config``GET/PUT /api/profiles/{name}``POST /api/config/reload``GET /api/config/reload/status`
- 提示 host/port/workspace 与存储路径为进程级不变量,修改后热重载被拒绝、需重启。
## 7. 后端接口设计
### 7.1 新增端点总览
| 方法 | 路径 | 用途 | 数据来源 |
|------|------|------|----------|
| GET | `/api/status` | 运行状况快照 | `Metrics` + 各服务只读查询 |
| GET | `/api/tools` | 工具列表(含能力字段) | `ToolRegistry` |
| GET | `/api/skills` | Skill 列表 | `SkillsLoader` |
| PUT | `/api/memories/{key}` | 更新记忆 content/importance | `Storage::upsert_memory` |
| DELETE | `/api/memories/{key}` | 删除记忆 | `Storage::delete_memory` |
| WS | `/ws/logs` | 实时日志流 | tracing 广播层 |
所有新端点走现有设备鉴权,注册在 `src/gateway/mod.rs` 的 protected router。
### 7.2 `GET /api/status`
返回单一 JSON 快照,概览页每 2s 轮询:
```json
{
"generation": 7, "version": "1.3.0", "uptime_secs": 266400, "phase": "steady",
"ws_connections": 2, "background_tasks": 3,
"sessions": { "total": 14, "active_turns": 1 },
"metrics": { "tokens_today": 1204882, "cost_today": 0.84,
"tool_calls_today": 312, "turns_today": 87, "turn_latency_p95_ms": 4200 },
"bus": { "inbound": {"depth":0,"cap":32}, "outbound": {"depth":1,"cap":64},
"control": {"depth":0,"cap":64}, "active_lanes": 4 },
"providers": [ {"name":"openai","model":"gpt-4o","status":"ok",
"latency_ms":820,"tokens":980000,"cost":0.61} ],
"channels": [ {"name":"feishu","status":"connected","detail":"3 群"} ],
"scheduler": { "enabled": true, "jobs": 5, "failed_7d": 0 },
"mcp": [ {"name":"github","status":"connected"} ]
}
```
聚合来源分两类——**已有查询**与**需新增的内省接口**(后者是 P1 的真实后端工作量,不可当作现成只读查询):
已有 / 低成本可得:
- `reload`generation、相位现有 `ReloadStatus`
- `mcp::get_mcp_status()`MCP 服务器连接状态(现有全局状态注册表)
- `Scheduler` / Storage任务数、下次运行、7 天失败数(现有 Storage API
- `ChannelManager`:各渠道连接状态
- `Metrics`token/费用/工具调用/turn/延迟(见 §7.3,新增)
**需新增的内省接口**(当前代码无对应查询面):
- `MessageBus`:三条队列的深度与容量。现状只有 publish/consume且未保留配置容量`src/bus/mod.rs`)。实现上让 bus 保留各队列 `mpsc::Sender`/容量,深度由 `max_capacity() - capacity()` 派生tokio `mpsc::Sender` 提供这两个方法)。
- `OutboundDispatcher`:活跃 lane 数。现状只有 `new`/`run``src/bus/dispatcher.rs`),需新增计数查询。
- `TaskSupervisor`:运行中任务数。现状无查询面(`src/task_supervisor.rs`),需新增。
- `SessionManager`:会话总数与活动 Turn 数(确认现有方法是否足够,不足则补只读统计)。
- WebSocket 连接数(`ws_connections`):当前无连接计数器,需在 `ws_handler` 用一个 `Arc<AtomicUsize>` 在连接建立/断开时增减。
这些内省方法必须轻量、非阻塞(不加锁等待慢操作),以支撑每 2s 轮询。
**不含任何密钥**provider api_key 等一律不出现)。
### 7.3 指标采集(`Metrics`
- 新增 `Metrics` 结构原子计数为主tokens in/out、cost、per-tool 调用数、turns、per-provider 延迟与错误滚动窗口。
-`AgentLoop` / Provider 在每次 turn / 工具调用时经 `Arc<Metrics>` 更新。
- 纯内存、不持久化、重启归零。"今日"统计为自进程启动起的滚动窗口(文档与 UI 注明,不暗示自然日)。
- provider 状态ok/降级)由最近错误率派生。
### 7.4 `WS /ws/logs`
- 给 tracing 增加一个广播层:格式化日志记录后发送到 `tokio::sync::broadcast`(容量约 1024慢客户端丢旧lag不反压。无订阅者时发送为 no-op近乎零开销。
- handler 连接后订阅,按查询参数 `level` / `search` 过滤,推送 `{ts, level, target, message}` 帧。
- 修改 `src/logging` 的订阅器初始化以挂载该广播层(保持文件轮转不变)。
- `GET /api/logs`(文件尾)保留,用于进入页面时拉取历史与重连对齐。
### 7.5 记忆写入端点
- `PUT /api/memories/{key}`body `{content, importance?}`,按 key upsert复用 `Storage::upsert_memory``updated_at` 自动刷新。
- `DELETE /api/memories/{key}`:复用 `Storage::delete_memory`
- path 中的 key 需 URL 解码;实现时校验 key 存在性,返回 404 若不存在。
- 现有 `GET /api/memories`list/search含 category/session/limit/query保留不变。
### 7.6 工具 / Skills 端点
- `GET /api/tools`:遍历 `ToolRegistry`,每项返回 `name, description, parameters_schema, source(builtin|mcp), read_only, exclusive, concurrency_safe, call_count``call_count` 取自 `Metrics` 的 per-tool 计数。
- `GET /api/skills`:数据源为 `SkillsLoader::get_loaded_skills()`(返回完整 `Skill { name, description, content, always, path }``src/skills/mod.rs`**不要**用 `list_skills()`,它只返回 `(name, description)` 二元组,缺少 `always`/`path`。返回 `name, description, always, source`,其中 `source``Skill.path` 所在目录派生(无独立来源字段)。默认不返回完整 `content`(可能较大)。
## 8. 前端架构
### 8.1 目录与数据层
- 保持 Svelte 5 runes`src/lib/api.js` 扩展为按域划分的客户端模块(如 `api/status.js``api/tools.js``api/memories.js`),不引入状态管理库。
- 组件库 `src/lib/`:在现有 `Markdown.svelte``ToolCallCard.svelte``TurnView.svelte``Toast.svelte``StatusBadge.svelte` 基础上,新增 Signal Deck 组件ActivitySpine、MetricTile、Sparkline、CapacityMeter、LogStream、BadgeSet 等)。
- 页面 `src/pages/`:重构 ChatPage、新增 OverviewPage、ToolsPage、重写 LogsPage、重构 MemoryPage、重构 TasksPage、重构 SettingsPage、保留 PairingPage。
### 8.2 主题
- 设计 tokens 以 CSS 自定义属性表达:`:root`(暗色)与 `:root[data-theme="light"]`(亮色),替换现有 `styles.css` 的变量集。
- 主题切换持久化到 `localStorage`,默认跟随 `prefers-color-scheme`
### 8.3 字体内嵌(构建管线变更)
- 字体文件latin 子集 woff2取自 @fontsource)放入 `webui/public/fonts/`。Vite 默认 `publicDir` 会把 `public/` 内容**原样、固定名**复制到产物根(`OUT_DIR/webui/fonts/*.woff2`),无需改 `vite.config.js``assetFileNames`
- `http.rs`:新增 `/fonts/{name}` 路由,用 `include_bytes!(concat!(env!("OUT_DIR"), "/webui/fonts/...woff2"))` 嵌入(静态 name→bytes 映射),返回 `Content-Type: font/woff2` 与长期缓存头;属公开静态资源层(与 app.js/styles.css 同级,不进设备鉴权)。
- CSP现有 `default-src 'self'` 已允许同源字体font-src 回落到 default-src无需放宽。
- 二进制体积增量约 100150KBSpace Grotesk + JetBrains Mono可考虑子集化
- `build.rs``rerun-if-changed` 需追加 `webui/public`;依赖 stamp 逻辑不变。
### 8.4 全局 WS 与活动脊
- 聊天 WS 连接提升到应用外壳层App.svelte使活动脊在所有页面可用。
- 活动脊消费 WS 的 turn 快照得到实时 Turn 状态;其余字段轮询 `/api/status`
## 9. 分阶段实现
- **P0 地基**设计系统tokens/组件库/双主题/字体内嵌)+ 应用外壳(扁平导航 + 全局活动脊 + 主题/鉴权)+ 聊天页重构。
- **P1 观测**`Metrics` + `GET /api/status` + 概览页 + `GET /api/tools`/`/api/skills` + 工具&Skills 页。
- **P2 日志与数据**tracing 广播层 + `/ws/logs` + 日志页 + 记忆写入端点 + 记忆页重构 + 任务页重构。
- **P3 配置**:配置编辑器重构 + 配置大纲 + profile + reload 状态可视化。
每阶段独立可验证;前端改动须过 `npm run check` + `npm run build` + `cargo build`(验证 OUT_DIR 嵌入Rust 改动须过定向测试 + `cargo test --lib` + `cargo clippy --all-targets --all-features -- -D warnings`
## 10. 风险与开放项
- **字体内嵌**构建管线需同时支持文本include_str!与二进制include_bytes!)资产;需在实现期验证 vite 固定名输出与 Cargo 嵌入路径。若字体子集化复杂,可退回系统字体栈(牺牲部分排版个性)。
- **`Metrics` 侵入性**:在 AgentLoop/Provider 埋点需避免持锁慢操作,遵循"不在持锁时做网络/模型/DB 慢操作"的不变量;计数用原子操作。
- **`/api/status` 聚合成本**:每 2s 轮询,聚合多个服务的只读查询;需确保各查询轻量、不加锁阻塞。必要时缓存短 TTL 快照。
- **tracing 广播层**:需保证无订阅者时零开销、有订阅者时不阻塞日志写入;广播满时丢旧而非阻塞。
- **记忆 key 路由**key 可能含特殊字符URL 编解码与 404 语义需在实现期明确。
- **Timeline 删除语义**UI 已用强警告;后端不做额外保护(用户拥有自己的 Agent但删除为幂等硬删除。
## 11. 验收标准
- 单二进制 `cargo build` 成功WebUI 从内存提供,无外部 CDN 依赖。
- 亮/暗双主题完整覆盖所有页面与组件。
- 聊天页保留现有全部能力dialog scope、历史持久化、turn 快照、斜杠补全、附件、Todo 侧栏)。
- 概览页实时反映运行状况;活动脊在所有页面可见且实时。
- 工具页正确展示 read_only/exclusive/concurrency_safe 能力标识。
- 日志页实时流式推送,支持 level/搜索过滤与暂停。
- 记忆页支持 Knowledge/Timeline 编辑与删除,删除警告分级,大量条目下虚拟滚动流畅。
- 配置页可编辑、密钥掩码、热重载状态可视。
- 所有新端点受设备鉴权保护,响应不含密钥。
- `npm run check``npm run build``cargo build``cargo test --lib``cargo clippy -- -D warnings` 全部通过。