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

20 KiB
Raw Blame History

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_DIRinclude_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 与 /wsAuthManager 保护;新增端点与 /ws/logs 同样走现有设备鉴权。
  • 聊天复用现有链路:浏览器聊天继续使用 /wscli_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/sctxqueuews,右侧 gen #N · uptime · version
  • 空闲:青绿常亮点 + IDLE + 最近 turn 摘要。

Turn 实时状态来自聊天 WS 已有的 turn_updated 快照(本就实时推送,WsOutbound::TurnUpdatedgen/uptime/version 等来自 /api/status 轮询。各字段来源:▲ tok/s 由前端对相邻 turn_updated 帧的 usage.completion_tokens 差值求导(快照本身不含速率字段);ctx 取自 usage.prompt_tokensqueue/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}/uploadsWS 只传 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 upsertupdated_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/configGET/PUT /api/profiles/{name}POST /api/config/reloadGET /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 轮询:

{
  "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 的真实后端工作量,不可当作现成只读查询):

已有 / 低成本可得:

  • reloadgeneration、相位现有 ReloadStatus
  • mcp::get_mcp_status()MCP 服务器连接状态(现有全局状态注册表)
  • Scheduler / Storage任务数、下次运行、7 天失败数(现有 Storage API
  • ChannelManager:各渠道连接状态
  • Metricstoken/费用/工具调用/turn/延迟(见 §7.3,新增)

需新增的内省接口(当前代码无对应查询面):

  • MessageBus:三条队列的深度与容量。现状只有 publish/consume且未保留配置容量src/bus/mod.rs)。实现上让 bus 保留各队列 mpsc::Sender/容量,深度由 max_capacity() - capacity() 派生tokio mpsc::Sender 提供这两个方法)。
  • OutboundDispatcher:活跃 lane 数。现状只有 new/runsrc/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_memoryupdated_at 自动刷新。
  • DELETE /api/memories/{key}:复用 Storage::delete_memory
  • path 中的 key 需 URL 解码;实现时校验 key 存在性,返回 404 若不存在。
  • 现有 GET /api/memorieslist/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_countcall_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,其中 sourceSkill.path 所在目录派生(无独立来源字段)。默认不返回完整 content(可能较大)。

8. 前端架构

8.1 目录与数据层

  • 保持 Svelte 5 runessrc/lib/api.js 扩展为按域划分的客户端模块(如 api/status.jsapi/tools.jsapi/memories.js),不引入状态管理库。
  • 组件库 src/lib/:在现有 Markdown.svelteToolCallCard.svelteTurnView.svelteToast.svelteStatusBadge.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.jsassetFileNames
  • 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.rsrerun-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 checknpm run buildcargo buildcargo test --libcargo clippy -- -D warnings 全部通过。