界面
文档入口:用户指南 · 关联:架构(事件驱动渲染) · 权限(审批卡片语义) 相关调研:OpenWebUI 的消息树、触发符和流式渲染,以及 Claude Code Desktop 和 Codex 的工具卡片布局。
当前已有侧边栏、全屏页、共享消息流与输入框,以及十一项设置导航。队列编辑、恢复卡、Attachments、站点指令、ModelPreset、Plugin、MCP Prompt/Resource 与交互卡已接入。待审批、待输入或恢复通知可以打开对应会话;Skill
auto_suggest胶囊和完整的性能诊断界面仍未接入。
1. 设计 Token
设计 Token 以
src/ui/styles/global.css为准。本节只解释用途,不复制完整色值。
- 品牌色:靛青(indigo)。light = indigo-600
--primary+ 白前景;dark = indigo-400 + indigo-950 前景(亮填充保证 text-primary 在暗底可读)。--ring恒等于--primary。 - 语义色各司其职:warning = 琥珀(审批卡、恢复横幅的警示语义,light 用 amber-700 保文本对比度);info = 青色(工具/Agent 活动、reasoning、L2 升级 —— 刻意远离靛青,避免与品牌色混淆);success / destructive 常规绿红(light 取深档达 4.5:1)。
- 中性色带轻微靛青偏色(hue ~245),让 chrome 与品牌色成一体。
- 状态色是"文本色"标准:light 一律取深档(700 级),dark 取亮档 —— 改动时先算对比度再落值。
--font-ui: Inter, 'PingFang SC', system-ui;
--font-mono: 'JetBrains Mono', ui-monospace; /* 工具参数、代码、ref、成本数字 */
--radius-card: 10px;
--radius-input: 12px;
--radius-chip: 999px;
--space-unit: 4px; /* 间距全为 4 的倍数;消息纵距 16px,工具卡片纵距 8px(紧凑)*/
--text-body: 14px/1.6;
--text-tool: 13px;
--text-meta: 11px;- 主题:跟随系统 + 手动三态;两套色板同一变量名,组件零感知。
- 动效克制:流式光标(accent 方块闪烁)、工具卡片状态色过渡 150ms、审批卡片入场 slide-in 200ms。禁用装饰性动画。
2. 组件层级树(两形态共享核心)
<ThreadView> ← 侧边栏与全屏页共用
├─ <MessageStream> 虚拟滚动(长会话)
│ ├─ <UserMessage> 编辑重发入口 → 分叉
│ ├─ <AssistantMessage> 同一 turn 的完整 Agent 工作单元
│ │ ├─ <Collapsible> 运行中展开过程,完成后默认收起
│ │ │ ├─ <ReasoningBlock> 按真实事件顺序展示思考
│ │ │ └─ <ToolCallGroup> 与思考交替出现的连续工具调用组
│ │ │ └─ <ToolCallCard> 一行摘要 + 可展开详情
│ │ │ └─ <SnapshotViewer>/<ScreenshotViewer>/<DiffViewer> details 通道渲染
│ │ ├─ <MarkdownRenderer> 最终回答常驻在折叠过程之外
│ │ ├─ <CitationsPill> 本轮访问来源
│ │ └─ <BranchSwitcher> ‹ 2/3 ›(siblings 派生,见数据模型文档 §3.2)
│ ├─ <ApprovalCard> 审批 RPC 的 UI 端
│ └─ <SystemNotice> 暂停/软提醒等浅色横条
│ └─ <InteractionCard> 提问/接管/页面等待/定时/MCP Elicitation
├─ <PromptInput>
│ ├─ <TriggerMenu> @ / 斜杠 统一建议框架
│ ├─ <AttachmentTray> <ContextChips> 已附着的页面/选区/文件胶囊
│ └─ <InputToolbar> 模型选择器 · 工具级别开关 · 发送/Stop3. 页面线框
3.1 全屏对话页(双栏,对话内容 768px 上限居中)
┌─────────────┬───────────────────────────────────────────────────────────┐
│ ⌕ 搜索会话 │ 耳机调研对比 claude-sonnet-5 │
│ ✚ 新会话 │ ───────────────────────────────────────────────────────── │
│─────────────│ ◆ 帮我调研这三款耳机的评价并做对比表 │
│ 📁 购物研究 │ │
│ ● 耳机调研 │ 处理中 ▾ │
│ 翻译PDF │ ──────────────────────────────────────────────────────── │
│ 今天 │ 我将先读取当前页面,再根据结果继续打开并提取其他页面。 │
│ 周报生成 │ ▸ 运行了 3 个命令 │
│ 昨天 │ 已获得三款商品的评价数据,正在整理对比。 │
│ … │ │
│ │ ⚠ 允许在 taobao.com 点击 [加入购物车] ? │
│ │ [允许一次·Y] [本轮会话·S] [本站始终·A] [拒绝·N] │
│─────────────│ ───────────────────────────────────────────────────────── │
│ ⚙ 设置 │ ⌨ @引用 /命令… [claude-s5▾][🌐▾][➤] │
└─────────────┴───────────────────────────────────────────────────────────┘当前左栏:按更新时间分组、置顶、未读/运行/审批状态;搜索先查最近 200 个 Thread 标题,再对最多 50 个标题未命中的最近会话扫描消息正文,不是 Dexie FTS 索引。行菜单支持改名、置顶/取消置顶、删除;文件夹、移动、归档和单会话导出尚未接入。思考与工具调用按真实事件顺序保留在同一条 Agent 消息内,不再使用独立任务面板。
响应式布局:宽度达到 1024px 时显示可调整宽度的左侧会话栏;更窄时会话栏进入左侧 Sheet,由顶栏按钮打开,主对话独占可用宽度。顶栏使用对称三列网格,模型入口位于左侧,会话标题保持视觉居中;消息流和输入区继续共享 768px 内容上限。
3.2 侧边栏(360–500px)
侧边栏头部的展开按钮和 Cmd/Ctrl+E 会在全页对话中定位当前会话;全页标签创建成功后关闭当前窗口中的 Panelot 侧边栏,避免两种对话形态同时占用界面。全页端的 Cmd/Ctrl+E 会先重新打开同一窗口的 Panelot 侧边栏,成功后再关闭全页标签。
侧边栏按独立应用页面处理:根布局使用动态视口高度并锁定横向溢出,头部、消息流和底部输入区分别承担固定导航、可滚动内容和持续可用的主操作。窄宽度下消息、空态、审批卡和输入区收紧水平留白;模型与权限入口允许在中间工具带内滚动,发送/停止按钮始终保留在可见区域。输入框按实际换行后的内容高度自适应,宽度变化时重新测量;长草稿达到面板 42dvh / 16rem 或全页 45dvh / 20rem 上限后改为内部滚动,连续长字符串可在任意位置换行。
侧边栏会在 chrome.storage.local 记录最后选中的真实会话。浏览器或侧边栏重新打开时先校验该 Thread 仍存在、未归档且未处于删除状态,再恢复原会话;记录失效时回退到最近更新的有效会话,无有效会话时进入不落库的新会话草稿。若 UI 与后台协议版本不一致,界面停止重连和加载骨架,禁用输入并提示重载扩展,不把该状态误报为 Provider 端点错误。
┌────────────────────────────┐
│ 耳机调研 ▾ ⛶ ✚ ⋯ │ ← 会话下拉 / 展开全屏(定位当前会话) / 新会话
│────────────────────────────│
│ 📎 当前页: 淘宝-XX耳机 [+] │ ← 上下文胶囊:默认不注入,点+附着
│────────────────────────────│
│ (MessageStream 同款) │
│────────────────────────────│
│ ⌨ 问问当前页面… [➤] │
│ claude-sonnet-5 ▾ 🌐▾ ⏹ │
└────────────────────────────┘3.3 首次引导(Onboarding,全屏页首启)
首次引导分三步:选择连接模板并填写 API Key,运行内联 Verify;选择默认权限模式;最后显示一条可直接尝试的页面总结指令。引导可以跳过。没有可用模型连接时,空会话持续显示引导;已有消息的会话仍可查看历史,但底部只显示“添加模型”操作栏,不渲染消息输入框。
3.4 设置页
当前左侧导航为 Attachments / Sites / Presets / 通用 / 模型 / 浏览器权限 / Skills / Plugins / MCP 服务器 / 数据 / 关于。各页内容如下:
- 模型:连接卡片列表(启停、编辑、删除)+ 必须指向已有可用模型的默认模型选择器;对话模型选择器中的“默认模型”表示使用这里配置的模型(Thread 绑定的 preset 仍优先)。编辑表单含 template、baseUrl、多 Key、自定义头、quirks、手动模型列表和本次 Verify 结构化结果,Verify 状态不持久化为卡片状态点;
- 浏览器权限:“默认权限策略”三选一(全程询问 / 操作询问 / 自动操作)+ 规则表(工具/站点/裁决/来源/删除)+ 手动添加 + 用户敏感站点区块;当前规则来源不能跳回原会话;
- MCP:服务器卡片显示 URL/auth/启停、连接状态、工具数与逐工具开关;OAuth 服务器有授权按钮,支持连接/断开、删除和粘贴 JSON 导入;脱敏日志抽屉仍未实现。
- 数据:导入对话框先执行后台预检并显示活动任务、暂停任务、待审批和待交互计数;暂停/排队状态需要单独勾选确认,第二次点击才覆盖提交。提交后显示扩展重载提示,重载前 Chat 与 Side Panel 的新命令会显示维护拒绝状态。
- 关于:显示 manifest 版本,并可手动检查最新的 GitHub Release。发现新版本时只提供当前浏览器对应 ZIP 的下载入口;开发者模式安装仍需覆盖原目录并在扩展管理页重新加载。
4. 交互状态
4.1 流式渲染
idle → streaming(item.delta 追加)
规则1: 未闭合代码块 —— 检测 ``` 奇偶,未闭合时以纯 pre 渲染,闭合后才交给 Shiki/Mermaid(防闪烁报错,OpenWebUI 教训)
规则2: Mermaid/KaTeX 仅在块完整后渲染,失败降级为代码块
规则3: 自动滚动 —— 用户上滚即解除跟随,出现 [↓ 回到底部] 胶囊
→ complete(终稿替换) | error(局部保留 + 重试按钮)4.2 工具卡片
pending(参数已知, ⏳) → running(onUpdate 进度文本) → ok(✓ + 耗时) | fail(✗ + 错误摘要)
折叠规则: 连续 ≥3 张卡片折叠为组头 "N 步浏览器操作 ✓m ✗k";运行中的组自动展开尾部一张
展开态: 参数(mono 字体) / content 结果 / details 富渲染(快照高亮、截图縮略图点击放大)同一用户 turn 内的 reasoning、工具调用、阶段性文本和最终回答统一收进一张 AssistantMessage 卡片,并按真实到达顺序形成活动时间线;思考与工具调用可以多次交替,只有真正连续的工具调用才合并为一组。卡片头部持续显示运行/完成状态,底部统一承载引用、用量、复制、重试与分支操作。
4.3 审批卡片
出现后临时替换底部输入框并获得焦点。操作栏只保留目标、风险摘要和主要决策;完整参数按需展开。
键盘: Y=允许一次 S=本轮会话 A=本站始终 N=拒绝 (焦点自动落卡片, Esc=拒绝并停止)
多个排队: 队列显示 "1/3",逐张处理
超时(5min): 变灰 + "已超时按拒绝处理"
flags 渲染: `sensitive_payload` / `escalation_l2` 显示告警;`cross_scope` 仅为兼容旧事件保留,引擎当前不再发出4.4 ask_user 选择器
ask_user 等待回答时,底部选择器临时替换消息输入框;提交、跳过或取消后恢复原输入框。选择器一次展示一个问题,使用单列编号选项、推荐标记、问题进度与前后导航;选择最后一题后提交结构化答案,也可以在底部输入自由回答。其它交互类型继续显示在输入框上方,不触发输入框替换。
4.5 运行/停止
发送键 ➤ ↔ 运行时变 ⏹(Esc 同效)。运行中输入:Enter=插话 steer(不可插话轮自动降级排队并 toast 说明),Shift+Alt+Enter=显式排队;队列胶囊显示待发条数、可点开删改。
5. 触发符与动态变量
<TriggerMenu> 统一调度(模糊搜索、↑↓ 选择):
| 触发 | 内容 |
|---|---|
@ | 当前只列出打开的可脚本化标签页;选择后抽取该 tab 正文为 ContextBlock |
/ | enabled Skill + MCP /server:prompt;带参数的 Skill/Prompt 弹结构化表单 |
{{ | 动态变量自动补全:{{PAGE_URL}} {{PAGE_TITLE}} {{SELECTION}} {{CLIPBOARD}} {{CURRENT_DATE}},提交时求值 |
输入框的 + 菜单可附着当前页、其它 tab 或用户文件;@ 可搜索 MCP Resource,/ 可调用 MCP Prompt。用户文件只在 Thread 已由首条消息显式创建后开放持久化;初始会话选择上传时必须提示先发送消息,不打开文件选择器,也不创建隐形空 Thread。附件随后持久化为 user-provenance 记录,再随同一 submissionId 发送/排队;上传工具只接受当前 Thread 的用户来源附件。截图由 L2 screenshot 工具生成并作为不可信 page attachment。
6. 快捷键全表
快捷键以 src/ui/shortcuts.ts 中的 SHORTCUT_REGISTRY 为准;扩展页内按 ? 可打开帮助。
| 键 | 作用 | 作用域 |
|---|---|---|
Alt+P | 开/关侧边栏 | 全局(commands) |
Cmd/Ctrl+K | 命令面板(切会话/改模型/命令) | 扩展页 |
Cmd/Ctrl+N | 新会话 | 扩展页 |
Cmd/Ctrl+, | 设置 | 扩展页 |
Cmd/Ctrl+E | 侧边栏 ⇄ 全屏页切换 | 扩展页 |
Cmd/Ctrl+Shift+S | 折叠/展开会话列表 | 扩展页 |
? | 快捷键帮助 | 扩展页 |
Enter / Shift+Enter | 发送(运行中=插话)/ 换行 | 输入框 |
Shift+Alt+Enter | 排队(当前轮结束后执行) | 输入框 |
Esc | 停止当前 turn | 输入框 |
↑ | 召回上一条输入 | 输入框(空时) |
@ / / / {{ | 引用 / 命令 / 变量触发菜单 | 输入框 |
Cmd/Ctrl+↑↓ | 分支切换 | 消息流 |
Cmd/Ctrl+Shift+C | 复制最后一条回复 | 消息流 |
Shift+Esc | 焦点回输入框 | 消息流 |
Y / S / A / N | 审批:一次 / 本轮会话 / 本站始终 / 拒绝 | 审批卡片(保留键,不可改绑) |
Esc | 拒绝并停止 | 审批卡片(保留键) |
7. 空态 / 错误态 / 加载态
- 空会话:居中 logo + 最多 4 条建议;侧边栏用 URL 规则提供视频/PDF/GitHub/普通页面建议,当前没有合并 Skill auto_suggest;点击只预填输入框,不自动发送;
- Provider 未配置:空会话显示首次引导;已有会话用“添加模型”操作栏替换输入框;
- 错误态规范:ProviderError 按 Provider §7 归因显示易懂文案(“API Key 无效,请检查 [设置]”);可重试错误显示“重试”按钮,网络断开时在顶部显示细横幅;
- 加载态:会话切换用骨架屏(3 条消息形状);架构 §3.4 握手期间,输入框显示“重新连接引擎…”。
8. i18n 与可达性
- 对话与设置组件使用
src/ui/i18n.ts中的 zh-CN/en key;设置搜索索引仍会保留中英文关键词。默认语言来自global_settings.language,未配置时为 zh-CN,不自动读取浏览器语言; - 审批卡片、工具卡片有完整 aria 标注;全键盘可达是验收项(Tab 顺序:消息流 → 审批 → 输入框);
- 颜色对比度 ≥ WCAG AA;状态不只靠颜色(✓/✗/⏳ 图标并存)。
9. 当前约束
- Mermaid 暗色主题:渲染时按当前主题传
theme: 'dark' | 'default'给mermaid.initialize(Markdown.tsx),不注入自定义变量。 - 虚拟滚动选型:react-virtuoso,流式追加用
followOutput锚定,替代手写 auto-scroll。 - ToolCallCard 窄容器不做专门降级布局:卡片头部文本已 truncate,耗时/状态是行尾短标签,侧边栏最小宽度下无溢出,单一布局即可。