08. 组件规格
翻译说明: 本页是与 英文源规格 一一对应的机器辅助翻译。代码、协议字段和标识符保持原文;如翻译与英文源事实有歧义,以英文版本为准。
布局和 IA 参考:01-ui-ia.md 设计代币和基础:07-ui-design-system.md 交互行为:09-interaction-patterns.md
Shell 布局与 Codex 对齐:左线程侧边栏(固定 275px)、主记录、带有运行时 mode/permission/model 控件的浮动底部编辑器,以及仅包含操作按钮的紧凑顶栏。与蓝板岩镀铬相比,更喜欢中性木炭表面。
优先规则:下面的指标或复制字符串与 决策日志 §D 中的法典平价决策 (D034+),决策日志获胜——它跟踪实时黄金捕获情况。已知 更新值:侧边栏固定 275px,工具栏 46px(不是 44px), 每个 D094/D066 的输入框占位符、每个主空堆栈和底部输入框 D111/D204/D206, 根据 D066/D133 项目索引表,根据 D063 设置整页 shell 来自 D090/D133/D166 的紧凑八个目标目录,并保留路径键控 每个 D093 的项目组(保留 D088 的 Temporary/exact-path 边界 同时恢复范围内的项目和对话组织操作),以及 根据 D094/D160 产品 branding/icon 合约。
1. AppShell
1.1 目的
定位顶栏、侧栏、MainChat 和工作面板的外框架。拥有调整大小逻辑、响应式折叠和主题类。
1.2 解剖学
+------------------+------------------------------+------------------+
| 侧边栏 | 主聊天室 | 工作面板 |
| (275 像素/48 像素) | (弹性-1) | (≥244 像素/动态上限/ |
| | | 隐藏) |
+------------------+------------------------------+------------------+
| 标题栏行:46px,交通灯位于 {x:16,y:16} (D034/D070) |
+--------------------------------------------------------------------+1.3 状态
| 状态 | 行为 |
|---|---|
| 默认 | 侧边栏展开,工作面板隐藏 |
| 窄(<640 像素) | 侧边栏自动折叠到图标栏 |
| 面板打开时的受限工作区域 | 工作面板保持其承诺宽度;MainChat 保持 450px 硬下限,展开的左栏在预算耗尽时让位 |
| 预览(最大化) | 卸载 MainChat,工作面板填充侧边栏之外的客户区;窗口级 chrome 行保留 shell 操作和本机窗口控件 |
| 全屏 | 顶栏仍然存在;侧边栏切换和工件驱动面板保持可用 |
1.4 交互
- 侧边栏切换:键盘快捷键 + 展开侧边栏标题中的图标按钮; 按钮在折叠时移动到主标题栏。这 折叠和展开使用安装后动画的停靠过渡(入口
sidebar-in,退出sidebar-out关键帧),镜像工作面板停靠: 侧面通过退出关键帧保留在树中,然后卸载 (is-exiting标志 +animationend防护,具有超时回退)。只有可见主界面发生 折叠到展开的变化才进入显式is-entering阶段;首次挂载及从设置返回直接恢复布局。 进入设置会取消未完成的入场或退出阶段,快速往返也不重播;不为抑制动画保留隐藏主界面。 - 侧边栏宽度:展开列固定为 275px。折叠/展开只改变列是否存在;历史上的 调整大小手柄会隐藏,旧的持久化宽度偏好会被忽略。
- 工作面板折叠:唯一的控件位于会话窗格标题栏右上角 当面板打开时,其外边缘与分隔板齐平 会话窗格和工作面板之间,因此工作面板内容标题是 没有被占用。开 Windows/Linux,打开工作面板会删除主标题栏的本机 窗口控件间隙,因为这些控件占用工作面板标题 在外窗边缘。 工作面板标题带通过让自身盒子在该保留带之前结束来承载它—— 用外边距而非内边距——因为原生拖拽矩形就是边框盒。
- 工作面板预览:标题栏最大化操作会临时卸载 MainChat,并让面板填充侧边栏 之外的客户区。 The 46px chrome row keeps shell/native controls but has no drag/no-drag rectangle and passes pointer events through outside controls. The panel header alone owns dragging in the preview pane; its border box excludes shell actions plus an 8px gap in both sidebar states on every platform. The left inset is 8px except collapsed-sidebar windowed macOS (88px through
--ds-window-lead-inset: the shared 76px native cluster edge plus 12px). Main uses the same native geometry from@pi-desktop/shared. Right native-control exclusion is unchanged. Header-height paint fills the left lane without an opaque overlay hiding tabs or panel actions. - 工作面板调整大小:左边缘拖动手柄(§5.4)
- 窗口大小调整:本机边缘仅在打开作品时更改 MainChat 宽度 面板保持其承诺的宽度;响应式布局如下 07-ui-design-system.md §10.1
1.5 辅助功能
- 标志性角色:
<nav>用于侧边栏、<main>用于聊天、<aside>用于工作面板、<header>用于顶栏 - 选项卡顺序:顶栏 → 侧边栏 → 主聊天 → 工作面板 → 编辑器
1.6 MVP 约束
- 侧边栏宽度固定为 275px,并独立于折叠图标栏状态;工作面板仍通过自己的 分隔线调整
- 主窗格呈现一个活动转录本和一个选定的工作区,同时 侧边栏可能会保留几个项目tabs/groups
- 侧边栏和工作面板停靠转换也为其灵活分配设置了动画 作为 opacity/transform 反馈,因此 MainChat 在运动持续时间内回流 而不是在第一个绘制的帧之前跳跃。
AppShell仅拥有低频 shell/navigation 状态。流式传输messages、主动轮渲染、内联聊天错误和主动权限 投影是在记忆化的ChatSurface内订阅的,因此令牌更新 无法重新渲染侧边栏、工作面板、窗口镶边、全局对话框或 toast。- 会话选择立即公开目标行,合并悬停/ 焦点预取,并在其中保留最多五个最近访问的记录 渲染器内存。转录 IO 和所需的工作空间对齐可能会运行在 平行;导航代次确保只有最新的选择才能 项目会话、工作区、消息和工作面板上下文。
ChatSurface为每个被保留的会话挂载一个SessionPane,按会话 ID 键控,上限为三个面板(可见的那个加上最近访问的两个)。每个 会话面板在其整个生命周期内拥有自己的转录 DOM、滚动位置和 已挂载行窗口,因此切换是一次可见性交换,而不是重建。非活动 面板保持挂载,但用visibility: hidden+content-visibility: hidden隐藏——绝不用display: none,那会摧毁布局框及其滚动偏移—— 并且是aria-hidden且非交互的。逐出一个面板后,其会话在下一次 访问时表现为冷启动。- 当会话是活动会话时,其面板渲染 store 中实时的
messages,否则 渲染它自己保留的快照,因此任何面板都无法显示另一个会话的行。 会话仍在运行,或仍持有持久化页尚未赶上的实时行时,其渲染器拥有的 实时缓存是再验证的低水位:持久化session.get可以补上更新的已完成 行,但不得抹掉进行中或尚未刷入的助手/工具尾巴,也必须让有界页丢掉 的实时行保持时间顺序,使最新回合仍落在尾部挂载窗口里(D317、D324)。 删除一个会话会释放它的面板、快照和实时缓存。 - 当没有保留面板的目标正在解析时,
ChatSurface让可见面板停留在 它自己的会话上,公开aria-busy,并显示 2px 进度轨道;只有在目标 面板已提交之后才会揭示它。热目标被揭示时完全没有忙碌指示。 没有任何内容被调暗,也不会插入 skeleton-to-transcript 动画(ADR 0137)。 - 设置、插件、拉取请求和计划是路由级惰性模块。 聊天和 shell chrome 保留在初始渲染器包中;第一个条目 次要目的地显示紧凑的本地状态指示器,直到其 本地块解析。
- 没有状态栏(延迟)
1.7 平台应用程序chrome
| 平台 | 顶级镀铬 | 应用菜单 |
|---|---|---|
| macOS | {x:16,y:16} 处原生嵌入式交通灯;展开的侧边栏折叠控件位于右侧,没有 logo/title;打开工作面板折叠位于会话窗格的右上角 | 系统菜单:PI-Desktop、文件、编辑、视图、窗口、帮助 |
| Windows | 无框 46px 标题栏;左侧边栏操作,在 minimize/maximize/close 之前的会话窗格右上角打开工作面板折叠 | 窗户里面没有 |
| Linux | 无框 46px 标题栏;左侧边栏操作,在 minimize/maximize/close 之前的会话窗格右上角打开工作面板折叠 | 窗户里面没有 |
- macOS 启用 Electron 原生
vibrancy: "sidebar"source-list 材质,配合visualEffectState: "followWindow"和透明窗口底(D348)。nativeTheme.themeSource跟随应用主题偏好(system/light/dark/ 插件 base),让毛玻璃底板与 渲染器一致。仅在该来源变化时重设 vibrancy;缺失的插件主题回落system。 主侧栏与设置导航共享.sidebar-surface材质,已渲染的.sidebar-rail同样半透明。 设置外壳透明,但其右侧内容和顶部条保持不透明;渲染器叠加 一层薄主题 tint(--ds-sidebar-glass-tint,深色 40% / 浅色 55%)和上下 sheen。侧栏与不透明主面板齐平,没有接缝。.main-pane、.main-titlebar和.conversation-topbar保持不透明bg-primary。Windows/Linux 仍用不透明底。 - macOS 系统菜单显示“新建任务”、“打开项目”、“设置”、“命令” 调色板、侧边栏、标准编辑、zoom/fullscreen、窗口、帮助、日志和 检查更新操作。 Windows/Linux 公开等效的产品操作 通过应用程序内控件和键盘快捷键,并检查更新 设置->信息。
- 当设置 -> 信息 -> 开发者模式启用时,macOS 查看菜单 另外还公开了本机开发人员工具的角色。所有平台都暴露 F12 和 Windows/Linux 也公开 Ctrl+Shift+I;命令和设置 当该模式被禁用时,打开控制台操作不可用。
- 窗口按钮具有本地化的工具提示和可访问的名称。最大化 字形反映初始本机状态加上后来的 maximize/unmaximize 事件。每个 Windows/Linux 按钮都是一个显式的非拖动指针目标,因此 周围的标题栏拖动区域不能消耗最小化、最大化、 恢复或关闭点击。
- 最小化是每个平台上的常驻 shell 操作:渲染器按钮 将主窗口隐藏到 PI-Desktop 托盘中,而 macOS 本机流量 灯光和窗口菜单角色转换为相同的隐藏到托盘状态。 托盘激活可恢复并聚焦窗口;退出仍然是明确的。
- Windows/Linux 不会在标题栏中呈现 File/Edit/View/Window/Help,并且 不要为应用程序菜单栏保留左侧空间。 F10 和 Shift+F10 仍然可用于聚焦内容。
- macOS 创建或重新加载窗口的本机命令等待渲染器的菜单 订阅确认而不是依赖于时间延迟。
Native tray session menu
The Main-owned native menu contains Open, non-empty Running/Unread/Pinned sections, and Quit. Each section has a disabled localized heading, single-line session rows up to the share allocated to that group, and View more only when it overflows that share. Session rows are globally deduplicated before truncation. View more expands session navigation; session rows enter their original conversation. The menu follows active locale changes and never marks a result read merely by opening. macOS single-click opens the attached menu; Open and double-click restore/focus the window. See ADR tray-session-shortcuts.
2. Topbar
2.1 目的
全局控制栏:任务标题和窗口操作。项目范围仍可从标题工具提示获得。 活动会话的 Agent/Plan/Goal 控件和模型选择均属于 Composer。 (从命令面板或应用程序菜单到达设置,而不是顶部栏。)
2.2 解剖
[☰ Sidebar] [Task title] [●] [+ New] [🔍 Search](图标按功能描述;实际渲染使用 Lucide SVG。[☰ Sidebar] 切换仅在侧边栏折叠时;当侧边栏为 扩展它拥有该控件,因此顶部栏不会重复它。[🔍 Search] 是 chrome 搜索入口;展开的侧边栏标题不再重复该控件。键盘快捷键 和应用菜单仍然可用。)
对话顶部栏仅针对聊天路线呈现;拉取请求、已计划、 插件和设置保留无框拖带。它仅拥有任务标题和窗口操作。 项目范围仍通过标题工具提示提供,而不是添加另一个可见标签。 Composer 拥有 Agent/Plan/Goal 控件以及组合的模型 × 推理选择(§11)。
2.3 布局
- 高度:46px(Codex 工具栏节奏,D034;取代旧的 44px)
- 背景:bg-primary
- 边框:边框-微妙底部
- 位置:绝对46px无框带;
-webkit-app-region: drag与 交互式控件上的no-drag; macOS 仅在侧边栏折叠时保留左侧 88px 的交通 灯空间,Windows/Linux保留权利 本机窗口控件为 112px - 标题簇(任务标题)在预留侧边栏引导、操作图标、工作面板开关和平台窗口控件之后,占用剩余宽度。可见标题仅在该宽度溢出时使用 CSS 省略号;完整标题保留在本机工具提示中。 右侧集群(操作图标)是
flex: 0 0 auto并且永远不会被长标题所挤压。对话表面保持min-width因此其内容不会在狭窄的窗口上被压垮。 - 项目范围可从标题工具提示中获取,但不会呈现为 第二个可见标签。
- macOS 全屏将左侧保留重置为 8px(镜像侧边栏标题)。
- 置顶:
z-sticky - 项目:左对齐控件、右对齐操作
- 标题栏带的空间保留与平台无关(D266)。该带是不透明的绝对定位 元素,因此在所有平台(包括 macOS)上,可滚动的路由内容都会从它下方 经过。所有从顶部边缘开始渲染自身内容的路由表面都必须为该带预留 空间:记录区(
.thread-content)和目的页面框构 (.page-frame,由插件、定时任务、合并请求三个页面共用)都按--ds-toolbar-height填充。缺少这一保留时,页面标头会渲染到带的后面, 标题行被遮挡。只有叠在带之上的表面(z-index: 60的插件详情侧面板) 可以跳过它。 - 每个 chrome 图标控件都是同一个 28px 方形(
--ds-work-panel-toggle-size), 并共用同一个透明底座:顶栏的侧边栏开关、视口固定的工作面板开关、 预览态/路由带上的动作组,以及工作面板头部的+与最大化/还原按钮。 控件与其同级保持一致,不再使用自己更小的尺寸,也不再自带表面,因此默认 由图标本身承载,只有语义化的悬停淡色才会在其下着色。面板头部为动作组 预留的车道宽度也由同一个控件尺寸推导,而不是写死的字面量;开关的打开 状态只改变图形与墨色,这一族控件都不绘制填充或抬升的“开启”胶囊。 With the sidebar collapsed, Plugins, Pull requests, and Scheduled render their sidebar/New Task actions inside.main-titlebar, not the preview-only.window-chrome-row. Both containers must share the same geometry, rest, hover, and disabled rules; route actions must not duplicate those declarations.
2.4 状态
| 元素 | 默认 | 跑步 | 错误 | 没有工作空间 |
|---|---|---|---|---|
| 任务标题 | 会话标题(或无标题),使用可用宽度,仅在溢出时显示省略号 | 相同,加上一个紧凑的脉冲状态点 | 一样 | 一样 |
| 新任务/搜索 | 图标按钮 | 一样 | 一样 | 一样 |
| Composer 停止控件 | 隐藏的 | 仅在运行中的草稿为空时可见 | 隐藏的 | 隐藏的 |
| 项目名称 | 仅标题工具提示 | 一样 | 一样 | 省略 |
2.5 辅助功能
- 每个控件都可以通过 Tab 键盘访问
- 输入框不渲染任何转写或朗读控件;宿主语音能力只能由 IPC 与插件调用(ADR 0291)。
- 停止按钮有
aria-label="Stop generating"
2.6 MVP 约束
- 顶部栏中没有搜索字段(延迟)
- 通知历史记录是有界的D117收件箱;预定的提醒、持久的权限请求历史记录和 通知首选项仍在范围之外。交互询问通知仅通过本机通知显示,不进入收件箱。
3. Sidebar
3.1 目的
范围内的项目和会话导航、管理和通知访问。的 扩展的侧边栏首先在紧凑的 Sessions 下显示无路径对话 以下 Projects 标题下的标题和保留项目选项卡;的 折叠状态是一个图标栏。保留的选项卡是渲染器呈现状态, 不是额外的主机工作区。 侧边栏主体是为会话和项目保留的;页脚暴露了 设置旁边的插件目的地。项目通过“设置”进行管理 → 项目存档;定时任务通过页脚时钟进入,Pull 请求仍不在侧边栏中呈现。
部分级创建和排序控件在静止时保持视觉安静并显示 当拥有的会话或项目工具栏悬停或键盘聚焦时。 项目组 + 和溢出控制遵循相同的 hover/focus 处理; 它们的命中区域保留在布局中,因此显示它们不会改变标签。
3.2 解剖学
Expanded (~275px, D034/D070):
+---------------------------+
| [lights] [◧] | macOS
| [π] PI-Desktop [◧] | Windows/Linux
| PINNED |
| • Pinned task project-A|
| 会话 [消息+][↕] |
| • 无路径会话 ↕ |
| 项目 [目录+] |
| [v] 项目-A [+] … |
| • 项目会话 |
| 项目-B [>] [+] … |
| |
| [⚙][@][铃][版本] |
+---------------------------+
Collapsed (48px):
+----+
| ── |
| 塞斯 |
| 塞斯 |
| ── |
| [⚙][@][☾][铃] |
+----+3.3 版式
主要左轨镀铬保持车身尺寸,因此接下来的目的地仍然可读 到 14px 聊天正文。会话和 project/group 标题使用相邻的紧凑 等级;粗细、缩进和显示图标保留其层次结构:
| 表面 | 代币 | 注释 |
|---|---|---|
| 页脚操作图标 | --text-base (14px) | 设置、扩展、通知;页脚左侧 |
| 会话/话题标题 | --text-md (13px) | 紧凑列表内容 |
| 项目/组标题,空状态文案 | --text-md (13px) | 层次结构来自权重和缩进 |
部分标签(SESSIONS、PROJECTS) | --text-sm (12px) | 大写辅助标签 |
| 页脚配置文件名称 + 配置文件菜单项 | --text-base (14px) | 身份簇与导航主体匹配 |
| 页脚状态/版本 | --text-sm (12px) | 右对齐 build/version 芯片 |
不要渲染 --text-md 下面的主侧边栏列表内容。保持行高 (≈28–32px),因此密度保持 WorkBuddy/Codex-like,同时主要操作保持不变 视觉上与列表内容不同。
3.4 状态
| 状态 | 行为 |
|---|---|
| 扩展 | 完整的会话标题可见 |
| 侧边栏宽度 | 固定为 275px;折叠/展开只改变列是否存在 |
| 倒塌 | 图标栏 — 悬停显示带有会话标题的工具提示 |
| 活动会话 | 强调蓝色轮廓状态环加上活动行背景 |
| 选择会话 | 目标行立即接受主动处理,同时 transcript/workspace 解析继续 |
| 会话进行中 | 橙色呼吸点;减少运动时的静态 |
| 会话已完成 | 未选择该行时呈绿色复选标记 |
| 会话失败 | 未选择该行时,红色圆圈警告标记 |
| 行悬停 | 项目标题与所有会话行共享整行 --ds-bg-hover 背景、圆角和过渡;会话选中背景优先 |
| 当前工作区 | 项目标题通过圆点标记当前工作区,不绘制持久选中背景;顶栏跟随该工作区,输入框不公开工作区身份 |
| 导航选中 | 仅聊天页当前会话使用 --ds-bg-active,包括置顶和独立会话;折叠分组不会把选中态转交给标题。没有选中会话或离开聊天页时,没有选中的会话行 |
| 键盘焦点 | 焦点控件保留可见轮廓;项目标题按钮保持透明,独立操作按钮保留自己的悬停反馈 |
| 崩溃的项目 | 标题保持可见;儿童对话被隐藏 |
| 存档行 | 默认隐藏;在显式存档视图中可见 |
| 无保留项目 | 紧凑的开放式项目入口;独立会话行仍然可用 |
| 空组 | 静音单线空状态;组创建操作仍然可用 |
| 默认会话标题 | 首条提示前显示 New task/New chat(适时本地化) |
| 首条提示标题 | 立即显示规范化的 48 字符提示回退标题;首轮完成后,成功的后台摘要会替换它 |
| 手动会话标题 | 用户定义的标题在刷新和渲染器重启后保持稳定;自动摘要不会覆盖它 |
| 页脚闲置 | 透明58px带;构建和动作控制在视觉上保持安静 |
| 页脚 hover/focus | 仅目标控件接受语义 hover/focus 处理 |
| 个人资料菜单打开 | 配置文件触发器已激活; 280px 菜单在页脚上方 8px 处打开 |
3.5 互动
- 单击项目目录行(V 形、文件夹、标签或剩余的 披露点击区域):必要时激活其路径,然后仅切换 该项目的对话组;保留其他项目组
- 点击会话:必要时激活其绑定的项目,并切换活动会话。 仍然保留着面板的目标(热切换)会立即以自己的内容和 自己的滚动位置被揭示,因此第一个绘制的帧就已经是正确的—— 没有调暗、没有骨架屏、没有转录重新挂载。若目标仍在运行,或仍持有 持久化页尚未赶上的已完成回复,热帧使用其最新的渲染器实时快照, 随后的持久化再验证不得把刚完成的回复卷回去(D317、D324)。没有保留面板的 目标(冷切换)会让当前可见的面板继续显示它自己的会话, 直到目标提交;只有一条细进度轨道标记这段等待,并且在 可见面板成为活动会话之前输入框保持非交互,因此提示无法 发往正在离开的会话。任何转录都不会被调暗,过期的记录也 永远不会用目标会话 ID 重新标记。会话的首次激活在其最新 回合处落定,不会闪烁转录顶部;重新访问的面板回到用户 离开时的位置,而仍然固定在底部的面板重新锚定到底部 (ADR 0137)。
- 将会话行悬停 120 毫秒或键盘聚焦,它会启动一个合并 转录预取。选择重用正在进行的或最近缓存的结果, 在后台重新验证它,并且从不等待旧的被取代 会话在开始最新读取之前读取。
- 在 Windows/Linux 上,单击 PI-Desktop 品牌以返回主窗格 在家聊天,同时保留活跃的对话和工作空间; macOS 故意从侧边栏标题中省略此品牌控制
- 点击设置右侧的页脚插件图标打开插件页;插件页激活时再次点击,按现有 导航历史后退一层。没有可后退条目时打开聊天页。按下状态反映当前页面, 悬停或聚焦时仍显示本地化标签。
- 这个快捷入口复用已有后退操作(同样绑定
Cmd/Ctrl+[),包括其会话选择 和加载行为。不跳过设置历史条目,也不维护额外返回目标。设置导航保持 不变,已有“返回应用”控件打开聊天页。两个页脚目的地按钮都向辅助技术 报告自身激活状态。 - 重开插件页时,在渲染进程内存中保留已安装/市场标签、两个搜索输入和 分类筛选。详情、设置、权限对话框、临时菜单及进行中操作界面不保留。 页面仍正常卸载并释放监听;已在进行中的操作仍会完成,并照常通过提示 (toast)报告结果;不把工作台隐藏后继续运行,也不跨应用重启持久化 这些浏览偏好。
- 页脚操作组保留在左侧,build/version 芯片保留在左侧 右对齐;单击芯片检查更新或打开可用的 在设置中发布
- 单击标题行右侧的“折叠”侧栏以折叠侧边栏。 全局搜索从会话顶栏、快捷键和应用菜单打开;展开的侧边栏标题不再承载搜索控件
- 展开侧边栏固定为 275px;折叠/展开只改变列是否存在,不再通过右边缘手柄 调整或持久化宽度。工作面板仍通过自己的分隔线调整。
- 当工作面板打开时,单击会话窗格右上角的面板折叠 控制隐藏面板而不删除选项卡;工作面板标题保留可滚动标签条和固定
+, 每个标签负责自己的关闭操作 - 单击
Projects标题文件夹 - 加操作:打开项目选择器并 保留所选项目 - 右键单击
Projects标题或空项目列表镶边:打开一个 运行相同的新项目选择器操作的单项创建菜单 - 单击项目
+:激活该项目并打开绑定到其确切路径的未持久化草稿 - 单击
Sessions标题消息加操作:清除工作区并打开无路径的 未持久化草稿 - 当工具栏打开时,会话和项目标题操作会一起显示 悬停或键盘聚焦;控件仍可通过键盘访问 休息时视觉上隐藏
- 右键单击
Sessions标题或空独立列表镶边:打开一个 打开无路径未持久化草稿的单项创建菜单 - 项目溢出:切换、打开文件夹、重命名、pin/unpin、archive/restore、关闭 保留选项卡。重命名只修改本地显示名称;打开文件夹在系统文件管理器中 显示所选项目行的项目目录。
- 对话溢出:pin/unpin、archive/restore、创建分支、删除。 当该对话运行时,创建分支被禁用;成功 激活独立的子会话并集中输入框。
Sessions工具栏将排序按钮放置在消息加新聊天之前 控制。排序菜单和所有其他主体级侧边栏菜单仍然保留 内容大小并在触发器或指针右侧打开 4px。他们的 左边缘永远不会翻转到扳机的左侧;表面有一个视口 窄窗的宽度上限。排序选择保持最近更新, 创建日期、最早的在前和名称;固定的行位于未固定的行之前。 项目行不再显示重排手柄。按住项目标题并移动 8px 开始指针重排,并选择持久化的manual项目顺序,但不会改变会话排序。- 项目组使用紧凑的垂直间距,因此相邻的目录和 对话行被解读为一个密集的导航列表,而不是分离的 牌。目录
+和溢出操作保持隐藏状态,直到悬停或 键盘焦点,而不改变目录标签的位置。 - 侧边栏切换:扩展标题图标 + 键盘快捷键;的 折叠的主标题栏保留展开侧边栏图标;当工作面板处于 打开,右上角的会话窗格包含唯一的面板折叠控件
- 单击本地配置文件触发器:打开或关闭包含以下内容的身份菜单 设置、日志和主题
- 单击页脚铃:打开或关闭持久通知收件箱
3.6 辅助功能
- 项目和会话标题具有本地化名称;每项披露和 创建操作具有特定于范围的可访问名称
- 在
lang=zh-CN下,部分标签保持正常跟踪并跳过text-transform: uppercase因此两个字形标签没有字母间隔 - 会话组使用语义
section容器 - 活动会话:
aria-current="true" - 每个可见的会话指示器都有一个本地化的可访问名称和工具提示; 颜色通过环、点、方格或警报几何形状得到加强
- 项目目录行公开
aria-expanded和aria-controls;菜单 check/radio 项目公开aria-checked - 悬停隐藏部分和项目操作保留在选项卡顺序中并显示 通过
:focus-within;键盘焦点从不依赖于指针悬停 - 折叠状态:每个图标都有
aria-label和会话标题 - 键盘:箭头键导航会话列表
- 页脚设置、插件和通知控件公开本地化 易于理解的名称和可见焦点处理
- 配置文件触发器公开
aria-haspopup="menu"及其扩展状态; 菜单与触发器具有稳定的可访问关系 - 通知触发器有一个本地化的可访问名称,其中包含 未读计数,公开
aria-haspopup="menu"/aria-expanded,并且从不依赖 仅徽章颜色 document.body的个人资料和通知弹出窗口门户,已修复 定位,以便主聊天窗格无法在它们上面进行绘制;工作面板工具 上下文菜单使用相同的主体级浮动层
3.7 品牌和图标合同
可见shell名称为
PI-Desktop; Codex 不用作渲染器 身份。没有文字标签的控件声明
.icon-btn-square,它把两个轴都固定到--ds-control-size(28px)。单独的.icon-btn宽度来自图形加左右各 8px 内边距 —— 这对带文字的胶囊按钮是正确的,对没有文字的控件则是错误的 —— 因此侧边栏收起控件、 会话顶栏开关,以及输入框的添加/增强/撤销控件,都呈现与顶栏和工作面板操作一致的 28px 正方形点击区,而不是"宽大于高"的胶囊。BrandLogo通过 Vite 导入从规范母版派生的渲染器尺寸标记:src/assets/brand/logo-light.png用于浅色模式,src/assets/brand/logo-dark.png用于深色模式(192x192,可覆盖 3x 下的 64 px 启动画面;ADR 0125)。该组件订阅了document.documentElement[data-theme]通过MutationObserver并交换 运行时侧边栏和启动画面的源代码,无需重新加载。的 空屋英雄在 100 像素处使用HomeMascotLogo八帧 GIF。浅色和深色主题 各有一套 GIF 和静止 PNG。CSS 跟随document.documentElement[data-theme], 无需重新加载;非light时使用深色稿。吉祥物循环一段处理后的挥手 动作,并在首帧稍作停留。播放由 GIF 自身完成,指针悬停不改变节奏; 减少运动时切换为对应静止首帧 PNG。这 expanded/collapsed 侧边栏仍为 20px/18px 且启动画面为 64 像素。 主目录和线程停靠的输入框提示行不会呈现领先品牌 图标。项目和临时会话创建控件呈现专用 消息加会话图标。通用
IconPlus保留用于添加非会话实体。当本地化文本标签或可访问名称被使用时,图标具有装饰性 存在;单击、键盘和焦点行为保持不变。
扩展的侧边栏品牌是一个本地化按钮,带有 20px 徽标和 Windows/Linux 上的 shell 名称;指针或键盘激活导航至 回家聊天。 macOS 隐藏该品牌并右对齐折叠 侧边栏与本机交通信号灯位于同一 46 像素行中。全屏保留 品牌在回收原生镀铬填充物时隐藏起来。
项目编辑器打开期间,后台会话或运行状态更新必须保留尚未保存的名称和文件夹修改。
3.8 MVP 约束
- 全局搜索从会话顶栏、键盘快捷键和应用菜单打开; 展开的侧边栏标题不再承载搜索控件
- 项目拖拽/手动重排只改变渲染器本地的显示顺序,不会移动磁盘目录或改变主机选定的工作区
- 项目选项卡不会创建另一个主机工作区或第二个主窗格
3.9 项目组合同
每个保留的项目都是一个标记为 section 的项目,由标准化完整路径键入。 标头拥有项目级控件;子列表拥有对话级别 控制。
| 元素 | 合同 |
|---|---|
| 组根 | 本地化项目名称;悬停和键盘焦点会在门户工具提示中显示完整路径以及可访问的描述,而无需更改行几何形状 |
| 目录披露 | 具有 aria-expanded / aria-controls 的单个全行目标;可能会在切换之前激活不活动的项目,但永远不会存档 |
| 项目引脚 | 仅限演示优先;无主机行删除/移动 |
| 项目重排 | 按住标题移动 8px,或在该标题上按 ArrowUp/ArrowDown,会将连续的规范化路径顺序写入侧边栏偏好;强调色插入线;没有可见 grip |
| 项目档案 | 从默认视图中省略;可从存档视图恢复 |
| 项目结束 | 仅删除保留的选项卡;持久的项目与会话仍然存在 |
| 删除项目 | 行菜单中的危险操作,位于第二次确认之后,该确认会指明项目名称及其会话数量;只要其中仍有会话在运行就会先被拒绝并给出提示消息;移除持久项目行、这些会话、其转录本及其项目记忆;从不删除磁盘上的文件夹;属于多文件夹项目组的路径会被拒绝并给出提示消息,而宿主已不再知晓的路径仍会从列表中移除 |
| 项目记忆 | 行菜单编辑器读取并保存一份紧凑的、按规范项目路径键控的有标题或无标题记忆卡列表;卡片可添加、编辑和移除,该上下文在后续聊天中可用,且它绝不是更高优先级的指令 |
| 会话列表 | 仅精确路径匹配;无基本名称分组 |
| 活跃组 | 恰好一组反映了选定的主机工作区 |
| 任务状态 | 正在进行、已选择、已完成和失败的指标按会话更新,无需替换可见的记录;优先级是进行中、选择、然后是最终结果 |
3.10 本地简介页脚合约
扩展的侧边栏以受 WorkBuddy 启发的本地身份集群结束。 它借用了紧凑的头像和动作语法,但没有暗示云 帐户、订阅或协作后端。
| 元素 | 合同 |
|---|---|
| 页脚带 | 58px高、透明、无顶隔板;保持在可滚动 project/session 区域之外 |
| 配置文件触发器 | 44px 高、灵活宽度、圆形悬停目标;打开个人资料菜单 |
| 用户字形 | 30px 循环本地用户字形;当文本标签命名控件时具有装饰性 |
| 身份证明复印件 | 主要 Custom;次要 Local profile 或本地化 本地配置;两行独立截断 |
| 雪佛龙 | 追踪披露指标;当请求减少运动时反映菜单的打开状态而不运动 |
| 通知快捷方式 | 带有未读徽章的单独 32px 方铃目标;打开页脚上方和右侧的耐用收件箱 |
| 个人资料菜单 | 280px 宽,底部固定在页脚上方的 8px;不透明的高架表面 |
| 身份标头 | 重复字形和两行本地标识;非交互式 |
| 菜单操作 | 按顺序分隔符、设置、日志和主题;主题保留其当前值元数据 |
4. MainChat
4.1 目的
主要聊天区域包含 ChatTranscript 和 Composer。可滚动,工作站的中心。
4.2 解剖学
+--------------------------------------+
| 聊天记录(可滚动,flex-1) |
| 消息气泡 (user/assistant) |
| 工具调用卡 |
| 回合结果卡(单一“继续”操作) |
| InlineReviewCard · 修改 App.tsx +8 −2 |
| 许可卡 |
| ... |
+--------------------------------------+
| 输入框(停靠在线程视图中; |
| 空置房屋底部保留,D204) |
+--------------------------------------+4.3 布局
- 背景:bg-primary
- 最大内容宽度:720px(消息),居中
- 失败的 TurnOutcomeCard 只提供一个主要的“继续”操作,不提供重新生成。 点击后会把当前语言的继续指令追加到同一会话并开始新一轮,同时保留 转录中的失败轮次和已完成工作。父级终态错误(包括 HTTP 429)后即使还有 残留子智能体,“继续”仍然可用;那些委托会被中止,不得把会话留在
AGENT_BUSY(D352)。随后的agent_end不得藏起失败卡片。 - 滚动行为:固定时自动滚动到新消息底部;第一个 向上手动移动会暂停自动滚动,而不会快速返回;发送/重试/ 在布局阶段重新生成重新固定并定位最新内容, 在下一个绘制帧之前,然后继续跟踪流内容
- 目标条目使用一个短 opacity/translate 转换。流媒体 更新发生在安装表面内,并且永远不会重播此转换。
- 会话面板把它自己的首次提交限制在最新的条目上,并在下一帧挂载 其余历史。由于首次提交属于某一个面板,它发生在该会话第一次 被打开时,而不是每次切换回它时。两次提交必须把转录呈现在 同一个位置,展开在它自己的布局阶段重新锚定底部;绘制之后再 校正猜测的高度会表现为转录跳动,因此不允许。在有界帧 期间向上滚动的用户保持该位置,并且该面板在之后的切换中也 保持它。当首次提交是有界的,一层不透明的骨架遮罩会从同一次 提交起覆盖滚动区域,直到滚动区域的几何尺寸连续多帧保持不变 (600ms 上限),然后淡出;输入框在其上方保持可见可用(D287)。
- 转录本的底部保留是高度感知,而不是固定的间隙。 停靠的输入框测量其真实渲染高度(它随多行增长 草稿)并将其发布为
--composer-dock-height自定义属性:root;.thread-content储备calc(var(--composer-dock-height) + 16px)所以最后一条消息位于框上方约 16px 并且永远不会重叠,即使 草案增长。.jump-latest-btn和.minimap-rail锚定相同 变量,因此它们停留在输入框上方。
4.4 状态
| 状态 | 行为 |
|---|---|
| 空 | 可滚动内容区域中的受约束的英雄+可选的入门清单,带有底部保留的主页输入框,没有入门卡或上下文快速操作层(D111/D204/D206) |
| 流媒体 | 固定时自动滚动;追加新标记 |
| 积极进展 | 发送后,在第一个助手或工具事件之前,会内联显示一个紧凑的本地化 Working… 状态以及经过的时间。思考、工具执行、工具完成后的空档和部分回答都不会隐藏该行。具体运行时阶段优先于规划/目标或工作中提示;等待权限、提问或计划/目标批准时隐藏状态,历史阅读窗口不显示实时状态;不会渲染大型通用进度卡。重试行在静止时保持紧凑;悬停或聚焦会弹出错误色 tooltip,底板为 --ds-bg-elevated-opaque,避免透出记录正文。 |
| 回合结果 | 回合失败后,会话范围的恢复卡会总结中断和工具证据。已完成的轮次使用现有的记录和消息范围的 InlineReviewCard,无需额外的成功卡;失败的回合可以重试而不会丢失转录本。 |
| 会话切换 | 首次打开的会话在它最新的记录处绘制;重新访问的面板在它自己保留的位置处绘制。有界的首次提交与全量历史展开显示同一个位置:绘制之后的高度校正不得在任何方向上移动可见行 |
| 启动(发送/重试/重新生成) | 即使用户向上滚动,在绘制之前重新固定和定位最新内容;后来保留的用户消息事件不会在其顶部闪烁记录,并且发送期间的输入框折叠/指示器布局夹紧永远不会释放跟随模式 |
| 空闲(流后) | 自动滚动解锁;用户可以自由滚动 |
| 消息范围的审核快照 | 每个成功的工作区 Write/Edit 工具行后面都有一个紧凑的 InlineReviewCard,其中包含该消息的 added/modified/deleted 状态和显式 addition/deletion 总计。它的大块头位于可扩展的披露后面:默认情况下,每个评论卡(内联和“评论”选项卡中)都是折叠的,用户可以根据需要展开它。该卡在 Git 提交后保留,永远不会成为 bottom/global 条目,并提供哈希保护的回滚,而不会泄漏到另一个会话的记录中。 |
4.5 辅助功能
role="log"用于转录容器- 新消息公告文字记录中的
aria-live="polite" - 当用户在流媒体期间向上滚动时,会出现滚动到底部按钮
- InlineReviewCard 使用带有
aria-expanded的本机按钮和aria-controls。其本地化可访问名称包括路径、状态、 添加计数、删除计数;可见的文字和颜色不是 仅状态信号。 - 清空主页任务条目在始终可见的底部编辑器中启动。有 英雄和英雄之间没有入门卡或上下文快速行动层 输入框。
- 失败回合恢复卡是一个标记为
role="status"的区域,提供明确的文本操作。 它使用图标几何形状加文字,从不单独使用颜色;只显示一个“继续”按钮。 点击后发送本地化的继续指令(Continue the user's unfinished task./继续用户未完成的任务),而不是回滚并重新生成原提示。完成的回合不会渲染这张卡。
4.6 MVP 约束
- 没有分割窗格聊天(单线程)
- 没有 Markdown 编辑器预览分割
5. WorkPanel
替换以前的 ContextPanel 覆盖层。 workspace/model/status 总结它在输入框芯片和设置中承载着生命。
5.1 目的
停靠右侧工作栏,用于检查和引导座席的工作空间。可主动启动的表面是宿主 Review 行与 插件视图(ADR 0104),包括 vendor 进来的文件管理器 pi.file-manager(项目浏览与编辑) 和随应用打包的 pi.browser (工作面板浏览器 chrome;访客页仍由宿主拥有,ADR 0170)。file:<path> 属于 产物表面:由宿主渲染,但由对话打开,因此不出现在启动器中。 应用不再提供交互式终端;Agent Bash 输出保留在对话中。
5.2 解剖学
+---------------------------------------+
| [◫ App.tsx] [▤ 文件] | [+] | header, 46px
+---------------------------------------+
| 可横向滚动的标签条 | 固定 + |
| x 关闭;中键关闭 | 新建菜单 |
+---------------------------------------+
| 活动资源主体 |
| 审阅:记录的更改 + 差异 |
| 浏览器:插件 chrome + 宿主访客页 |
| 文件:对话打开文件的查看器 |
| 插件视图:插件自己的页面 |
| 无资源:空状态 + 工具列表 |
+---------------------------------------+
▌ 活动标签 • 已打开但非活动
^ 10px transparent resize hit area on the left edge标题栏是可横向滚动的 tablist。每个打开的 Review、文件或插件视图都是一个 标签,带图标、省略标签和悬停/聚焦/活动时显示的关闭按钮;中键也可关闭。 + 位于滚动条之外,标签溢出时仍然可见。它的唯一 Tools & panels 菜单包含 宿主的 Review,以及每个当前范围内的 contributes.views 项;Files 和 Browser 不在渲染器中硬编码(ADR 0104)。
没有资源时,主体保持打开并显示简洁的 New 启动器。它与 + 菜单使用同一份 数据驱动工具列表,点击后创建或激活单例标签,不会重复打开:
+---------------------------------------+
| New | 启动器标题
| ◫ Review ▤ 文件 | 工具行
| ◉ 浏览器 |
+---------------------------------------+5.2.1 浅色主题表面
- 面板主体采用静音插页纸(
#fafafa); 46px 标题带和工具 chrome(审阅工具栏、浏览器chrome、文件查看器标题)保持白色 - 46px 标题是裁剪的标签条和紧贴的
+入口。只有标签条滚动,入口始终固定。 头部为视口固定的折叠开关预留 44px 的右侧安全车道(28px 控件 + 12px 视口内缩- 头部自身的 4px 控制间距
--ds-work-panel-control-gap),这一个间距同时 分隔整行:标签条到动作组、+到最大化,以及最大化到开关。动作组不再有自己的 分隔线、内缩或外边距,因此三个按钮读作一组;+与开关之间仍隔着最大化控件, 命中区域彼此独立且视觉间隔大于 24px。三者都是与其他 chrome 图标相同的控件: 28px 方形、透明底座,因此头部呈现的是安静的图标而不是填充方块。+与最大化 从chrome.css的共享 chrome 控件组取得几何与悬停淡色,而不是自带的规则; 视口固定开关的aria-pressed状态只改变墨色与图形,绝不改变背景或阴影。 菜单只有 Tools & panels 分组,Review 在前,插件视图按声明顺序排列;只有真实 存在的快捷键才显示快捷键标签。
- 头部自身的 4px 控制间距
- 标签在悬停、聚焦和活动时显示
×,中键也可关闭。面板溢出由标签条处理, 不再额外渲染资源列表。菜单以 ≤4px 位移淡入,并在 reduced motion 下静止。 - 活动选项卡、文件树行、差异标题用
--motion-duration-fast/--motion-ease-out做悬停填充。调整大小把手与左侧边栏一致:32px 居中短线,悬停/聚焦才出现,键盘焦点和拖动中用实色强调 - 浏览器URL和空工具镀铬共享所使用的光嵌入场处理 通过设置控件 (D148)
- 面板里的每一个空状态 —— 无资源主体,以及每个标签自己的空状态 —— 都使用应用其余部分已经在用的比例(
.ext-empty、.projects-empty): 38px 圆形平铺图标、--text-base-plus/--font-weight-medium-plus的标题,以及弱化的--text-md文案。文案在 34ch 换行而不是 48ch, 因为面板可以只有 244px 宽。没有主视觉插画、卡片或营销式包装 (design-system §14、D206)
5.2.2 文件管理器插件表面
vendor 进来的 pi.file-manager 视图在插件自己的隔离页面内完成浏览、打开与编辑:
文件树每次只加载一个文件夹,文件夹始终排在文件前面,保留已展开的目录,并且不会在打开时 遍历整个工作区,图标随文件类型变化并显示文件大小。按文件名搜索(等价于
fs.glob的匹配)直接列出命中项,不必先展开整棵树;刷新期间按钮禁用,根目录与已展开的文件夹一起重新加载。行上的上下文菜单提供新建、重命名/移动、用默认应用打开和在文件夹中显示。 后两者只对文件提供:宿主会拒绝目录。
选择文件后,文件树被独立的查看页替换:带语法高亮的编辑与保存、Markdown 预览切换、 图片与音视频查看、可分页排序的 CSV/TSV 表格、JSON 折叠树,以及只读的 SQLite 浏览与 SQL 查询框。二进制文件明确提示「不支持预览」而不做解码;每个查看器都写明自己的上限—— 文本 2 MiB、图片 8 MiB、影音 24 MiB,数据库不受体积限制,其字节不进视图。
保存采用原子写;若文件在打开后被外部改动,在用户确认前不会覆盖。加载中、读取失败、 不支持的二进制、过大文件、空文件夹以及加载失败的文件夹都有独立的本地化状态, 失败的文件夹可以原地重试。
视图用自己的左上角控件选择浏览项目中的哪个文件夹:默认是项目的主文件夹,其余文件夹按项目组 顺序排在后面(ADR 0249);选择按项目记忆,与应用当前显示的可见工作区无关。这次切换只影响 插件自身——既不改变可见工作区,也不改变智能体的工具根、会话的主路径,以及项目指令与项目记忆 (ADR 0263)。它自己右键菜单里的那两个动作跟随这个选择:兄弟文件夹里的文件以绝对路径交给宿主, 因此「用默认应用打开」与「在文件夹中显示」落在正在浏览的那个文件夹里的那个文件上(ADR 0264)。
页面通过
app.getAppearance和appearance:changed跟随基础主题及英文/简体中文文案, 并通过workspace:changed跟随打开的项目及其文件夹列表。它调用的宿主通道只有workspace.get、app.getAppearance、fs.openDefault和fs.reveal; 自身的读写走自己的宿主进程,由该进程维持它正在浏览的那一个文件夹的牢笼(绝不是整个项目组)、 拒绝凭据类路径,并把写入记进自己的审计日志(ADR 0241、ADR 0263)。切换浏览器所属会话时,Main 立即隐藏共享 guest,直到目标会话的当前导航完成。 已被替代请求的目录查询或加载完成不能再导航、显示或发布旧会话为当前内容。 没有预览记录的会话保持空白;关闭面板或销毁 guest 的结果不能被未完成请求撤销。 同一会话内的正常导航仍保留本会话当前显示的内容。切换导航失败或超过既有 15 秒等待 上限时保持隐藏,需重试;迟到的网络加载完成不会自动显示。
主框架的同文档导航(锚点链接和 History API 路由)无需完整加载文档, 也必须更新浏览器地址、历史导航按钮及加载状态。子框架、失效会话或 已被替换主框架的事件不得发布浏览器状态。
5.3 状态
| 状态 | 行为 |
|---|---|
| 关闭(默认) | 未渲染;启动时没有保留的选项卡。 Cmd/Ctrl + J 显示活动会话的面板上下文,而无需创建选项卡。内联审阅卡在转录本中仍然可用,因为它们是消息范围的并且不需要工作面板。 |
| 打开 | 主窗格右侧停靠的弹性行;由工件或 Cmd/Ctrl + J 以至少 244px(新用户默认 360px)的首选宽度打开,上限由三栏共享预算决定。它的弹性分配从零减少到承诺的宽度,因此 MainChat 不断回流。它占用客户区空间并且从不扩展操作系统窗口 (ADR 0033)。 |
| 预览(最大化) | 卸载 MainChat,面板填充侧边栏之外的客户区。该模式是临时的,退出时恢复之前的面板宽度和侧边栏状态。 |
| 多个工件 | 标题栏保留可横向滚动的标签条;固定 + 菜单列出 Review 和所有当前范围内的插件视图,不重复已打开的资源标签。 |
| 会话切换 | 目标会话保留的打开状态、选项卡、活动选项卡和浏览器资源自动替换前一个会话的面板上下文;上下文都没有被删除 |
| 调整大小 | 左分隔线遵循锚定指针增量或键盘输入。指针在每个动画帧上更改预览一次,并仅在发布时提交宽度和预留;转义、指针取消或丢失的捕获都可以恢复。本机窗口边缘/角落调整大小仅更改 MainChat;Electron 恢复看门狗会等待稳定边界,因此不会中断慢速手势。 |
| 没有工作空间 | 每个选项卡呈现自己的“打开项目”空状态 |
| 打开但无资源 | Cmd/Ctrl + J 揭示面板而不创建选项卡,因此主体渲染 New 启动器。点击行会创建或选中单例标签;关闭最后一个标签后仍回到这里。这里的主体不是 role="tabpanel",因为没有选项卡为它命名。 |
| 受限工作区域 | 面板受三栏共享预算限制;MainChat 永不跌破 450px 硬下限,展开的左栏在阈值处让位 |
| New 启动器激活 | 主体承载简洁的 Review 与插件视图按钮;每行会用其目标替换启动器标签页,或激活已存在的单例视图;该页面可独立关闭 |
| 插件视图处于活动状态 | 主体以原生 WebContentsView 承载插件自己的隔离页面,按测量到的表面矩形定位。分隔线拖动调整或 + 新建菜单打开时视图保持可见;占位符观察器跟随逐帧合并的面板宽度,菜单打开时只把原生边界裁剪到不透明菜单底边以下,让内容不会闪成面板底色。当该选项卡非活动、面板正在动画或面板级阻塞性浮层打开时,视图隐藏 —— 与浏览器预览同一条规则,因为两者都合成在渲染层之上。插件被禁用、卸载、重新加载或崩溃时其视图会被销毁;选项卡保留,并在下一次生命周期事件时重新打开页面(ADR 0104) |
| 插件不在当前范围内 | 由未在当前项目激活的插件贡献的视图,会在切换项目时从菜单中消失。与全局唯一设置的贡献主题不同,视图属于项目内的工作,因此按范围过滤 |
5.4 互动
- 触发器:file/URL 引用和 BrowserPreview 在原始会话运行时上下文中 创建或激活资源选项卡。BrowserPreview 事件携带
sessionId,渲染器保留 该会话的预览 path/URL 作为其浏览器资源。审阅永不由工具结果触发: 它只会从+启动器行,或视口固定开关与Cmd/Ctrl + J显示的会话 保留上下文打开,因此成功的工作区 Write/Edit 不会在任何会话中打开、 激活或改变面板。 计划/目标审批工件仍会在其来源会话中创建或激活标签,但承载界面由宿主选择: 内置文件视图可启动时用它,否则用宿主机文件标签(D452)。Cmd/Ctrl + J显示活动会话的保留面板上下文,无需 创建资源;如果没有活动会话,它什么也不做。捷径是 当“设置”处于活动页面时被忽略。 背景工件可能会更新保留的上下文,但永远不会揭示它, 调整窗口大小,或更改可见的 selection/focus。抄本确实 不创建全局审核更改启动器:每个成功的工作区 Write/Edit 行仅拥有其相邻的 InlineReviewCard,而另一个会话 无法在其记录中呈现该卡。重复资源去重 在原始会话内。 - 回顾事实:host-core 为每个
details.review添加一条有界details.review记录 成功的工作区 Write/Edit 结果。渲染器读取该记录 拥有的文字记录消息,因此状态、计数和帅哥准确描述 该行更改了什么并在提交、重新启动或之后保持可用 工作区开关。 Review选项卡是同一会话的时间顺序变化 历史记录,而不是当前工作树扫描;它重复使用相同的消息拥有的卡片, 每个默认情况下都会折叠,直到用户将其展开。它的回滚动作 呼叫主人;主机将当前内容与录制内容进行比较 后工具哈希和 返回冲突而不覆盖以后的工作。 - 标题栏标签:标签条是包含所有打开的 Review、文件和插件视图的
tablist。 点击标签激活它,活动标签会滚动到可见范围;关闭按钮和中键都会关闭它, 并按右邻居、左邻居的顺序选择下一个。Arrow/Home/End 在标签之间移动, Delete/Backspace 关闭聚焦标签。固定+打开唯一的 Tools & panels 菜单。 - New 启动器:没有活动标签时,主体使用与
+菜单相同的 Review 加插件工具列表。 点击行创建或激活单例标签;Cmd/Ctrl + J本身仍不会创建任何标签。 - 资源标题:46px 标题显示可滚动的活动标签和固定
+。子代理详情使用返回箭头。 Arrow/Home/End/Escape 操作菜单;打开菜单时原生插件表面裁剪到菜单不透明边界下方。 - 选项卡关闭:关闭活动选项卡会选择其右侧邻居,然后选择其左侧;关闭最后一个 选项卡会保持面板打开并显示 New 启动器。面板级塌陷控制位于会话窗格右上角, 隐藏面板但不删除运行时选项卡集。
- 上下文更改:选择另一个会话自动投影该会话的 保留
{open, tabs, activeTabId, browserResource}状态。上一个 会话的上下文保留在渲染器内存中,并在选择时恢复 再次。没有活动对话的工作区选择会隐藏面板。 每个上下文仍然与其原始 session/workspace 绑定,因此相对 文件和浏览器资源永远不会针对另一个工作区重新解释。 - 调整大小:将指针拖动到左边缘手柄上;
ArrowLeft/ArrowRight以 16px 为步长调整(Shift使用 32px),Home/End达到当前值 钳制到 244px 下限与当前动态上限,双击恢复默认宽度。 指针数学锚定到按压位置和起始承诺宽度, 因此抓住手柄无法跳过分隔线。移动事件是 框架合并;释放 提交一次,而 Escape、指针取消和丢失捕获取消。 10 像素点击区域保留全局列调整大小光标并抑制文本 手势期间的选择。实时预览仅更改渲染器列; 成功提交会更新提交的首选宽度。原生窗口边缘 仅通过回流调整 MainChat 的大小,而不是面板或其首选项 (ADR 0033)。 - 持久性:所有会话上下文仅是渲染器运行时状态。在应用程序上 启动、打开状态、选项卡、活动选项卡选择、文件请求和浏览器 资源重置;仅承诺的首选
{width}保留在 本地存储pi.desktop.workPanel。渲染器总是请求一个原生的 保留宽度为 0,因此操作系统窗口永远不会扩展 (ADR 0033)。崩溃 最后选项卡关闭和分隔符提交仅更新已提交的首选内容 宽度。目标更新是幂等的。面板回流 MainChat 内 固定窗;在受限工作区域,MainChat 保持 450px 硬下限,展开的左栏在阈值处让位。 Maximized/fullscreen 几何形状不受影响。后台会话工件 永远不要更新可见面板。渲染器仅更改面板呈现 最新(零宽度)预订请求成功后;被拒绝或 被取代的请求保留最后确认的呈现状态 (D163,ADR 0032)。
5.5 辅助功能
<aside>里程碑。当前资源控制暴露aria-haspopup="menu"/aria-expanded/aria-controls,保持其可见 label 作为其可访问名称,其role="menu"下拉列表将行分组到 标记为role="group"部分。行为menuitemradio/aria-checked在role="none"内获取真实 DOM 焦点 (tabIndex={-1}) 的按钮 包装器,因此 ArrowDown/ArrowUp/Home/End 仅跨行移动焦点,从不移动焦点 通过尾随关闭按钮; Delete/Backspace 关闭焦点行。 Escape 和 Tab 关闭菜单并将焦点返回到触发器。每个资源 主体仍然是role="tabpanel"- 调整手柄大小:可聚焦
role="separator"aria-orientation="vertical",本地化标签,动态aria-valuemin/aria-valuemax/aria-valuenow,可见焦点,以及 Arrow/Home/End 键盘控制。 Escape 取消活动的指针手势。 - 每个资源关闭和唯一的会话窗格面板折叠按钮公开 本地化名称
5.6 MVP 约束
- 选项卡内容规范:评论有主机保护的回滚,但没有行注释; 浏览器是用户驱动的(无代理控制);文件是只读的
- 单面板实例;没有每个选项卡分离或拆分
6. SessionList
6.1 目的
按侧边栏中的执行上下文列出用户会话。它暴露了 每个保留的项目选项卡的会话以及没有的持久会话 项目。 Pin/archive/collapse 状态是持久主机的表示 会话,而不是替代持久性模型。
6.2 解剖学
小组和会话项目:
[folder] current-project [+]
Session title
[folder] another-project [+]
Session title
SESSIONS [msg+][↕]
Session title项目分组内的行按日期分桶。今天这一桶不绘制标题; 昨天、过去 7 天、过去 14 天以及更早的每一桶都会在其行上方绘制一个弱化的全大写标签, 且只有包含行的桶才会绘制:
[folder] current-project [+]
YESTERDAY
Session title
Session title该标签是列表节奏中的普通一行 —— 没有展开披露、没有状态、没有 aria-expanded —— 不同于项目标题,后者才是真正的可折叠分组。
侧边栏列表中的间距阶梯(按 04-ux/07-ui-design-system.md §6.1 使用 space-0.25 / space-0.5 / space-2):
| 关系 | 间距 |
|---|---|
| 行与行之间,包括日期标签与“加载更多”行 | 1px |
| 项目分组标题到其首行 | 2px |
| 展开分组最后一行到下一个分组 | 8px |
| 折叠分组到下一个分组 | 1px |
| 分区标签到其首行 | 2px |
| 侧边栏分区到侧边栏分区 | 8px |
8px 的尾部间隙属于展开的分组本身,因此间距仅由前一个分组决定: 展开的分组之后是 8px,无论下一个分组是展开还是折叠;折叠的分组之后 则是 1px,两种情况都一样。
折叠是一次运动,而不是两段。折叠的分组是一个单行网格,其行在 200ms 的正常时长内 从 1fr 动画到 0fr,因此每一帧都是该分组实测高度的真实比例,而不再是 max-height 夹取——后者的大部分曲线都落在内容高度之上,末端只能瞬间跳变。 行由带 min-height: 0 的内层盒裁剪,绝不淡出:整个折叠过程中 opacity 始终为 1; 2px / 7px 的内缩量位于该裁剪层内部的列表上,因此它随行一起消失, 而不会把已关闭的行撑开。在 prefers-reduced-motion: reduce 下,折叠保留两端状态, 并以接近零的时长完成。折叠的分组仍然挂载其行,并保持 aria-hidden 与 inert,因此它们既离开可访问性树,也离开 Tab 顺序。
6.3 状态
| 状态 | 外观 |
|---|---|
| 活跃 | 中性口音轮廓环、活动背景突出显示、文本主色 |
| 不活跃 | 背景辅助、文本辅助 |
| 悬停(非活动) | BG-第三级 |
| 进行中 | 警告-橙色呼吸点;减速运动下无运动 |
| 已完成 | 成功-绿色复选标记 |
| 失败 | 错误-红色圆圈警报标记 |
| 已固定的项目 | 填充强调色的星形替换文件夹字形;在所选排序中排在未固定项目之前 |
| 已固定的会话 | 在所选排序中的未固定行之前排序 |
| 已存档 | 默认省略;仅当启用存档视图时显示 |
6.4 交互
- 单击:激活会话
- 项目匹配使用规范化的完整项目路径,而不仅仅是文件夹 基本名称。
- 保留路径的会话显示在其相应项目下方 组。封闭路径的会话仍然可以从“设置”→“项目”中发现 存档。
- 选择临时会话会清除活动工作区,因此会话和 工具上下文并不意味着项目访问。
- Pin/archive 操作更新渲染器呈现元数据;删除残留 显式持久主机操作。
- 创建空闲对话完整当前记录的分支快照 进入独立会话。子会话留在同一个项目或 独立会话部分并变为活动状态;后来 transcript/configuration 发生了变化 不影响来源。对于正在运行的源,该操作被禁用。
- 首先选择与不同项目的对话会激活该对话 项目的工作区。先前选择的会话中的运行回合是 没有中止。
- 从工具栏或行触发器打开的侧边栏主体级菜单仍然存在 内容大小并使用与右键菜单相同的固定规则:打开4px 锚点在右侧而不向左翻转。它们的表面宽度为 为窄视口设置了上限。这包括会话排序菜单, session/project 溢出菜单和部分创建菜单。
- 键盘:箭头 up/down,回车选择
- 删除:行菜单
6.5 辅助功能
- 每组都有一个标记为
section。 - 特定于范围的创建按钮公开本地化的
aria-label值。 - 活动行公开选定的视觉状态并保留其完整标题 工具提示。
- 存档状态和每个任务状态都是宣布的,而不是通过传达的 单独的颜色。状态槽还使用不同的几何形状来选择,在 进度、完成和失败。
6.6 MVP 约束
- 搜索仍然是本地标题过滤器;存档可见性和排序是 本地视图控件而不是主机查询。
- 临时意味着不受项目约束,不是临时存储;这些 会话在重新启动后仍然有效。
- 独立的会话主体是一个 146 像素的窗口,容纳五个紧凑的 28 像素行、它们之间的 1px 行间隙 以及 2px 的标签内缩;当存在更多行时在内部滚动。项目列表使用剩余的 侧边栏高度和滚动独立;两个区域都不滚动页脚 或主要导航。两个列表的滚动条与对话区和工作面板使用相同的全局规则:6px、 无轨道、静止时透明;文字色滑块在其列表被悬停、聚焦或滚动时出现,拖动期间 保持可见,因此独立区域始终可用而不会变成常驻的视觉导轨。
7. ChatTranscript
7.1 目的
可滚动容器呈现用户消息的有序序列,助手 回合、轻量级工具活动行以及会话的权限卡。 由工具调用分隔的提供商级助手片段在 存储,但组成一个助手轮流,直到下一条用户消息。
回合过程与思考展示
详细和紧凑模式都会把每个已加载的助手回合投影为一个整体过程披露,其中按转录顺序 包含推理、工具、托管搜索和中间进度文字。末尾回答在披露之外流式显示;后续活动可以 把暂定回答重新归入过程,但不会改写已存消息。助手错误和中止后的末尾部分回答也留在 过程之外。用户/系统消息及压缩边界不变。
过程内的普通活动组表示两个进度段落之间的一段连续工具、搜索或思考活动。只有当前 模式下至少有两个可见项时才显示组标题。单项活动直接使用自身披露,紧凑模式隐藏的 思考不会制造空包装,委派工作继续使用现有 Task 拓扑作为容器。
详细模式下,活动中和已完成的整体过程默认展开。拥有当前执行片段的普通活动组默认 展开,完成时仅在用户未操作的情况下自动收起;其他已完成组默认收起。紧凑模式下过程 和普通活动组默认收起;若活动回合记录过失败或被拒工具,尚未被用户接管的过程会在 后续成功恢复期间保持展开,并在回合完成后收起。组标题显示计数、运行状态和问题计数, 但不会把失败子项当作整个回合失败。
详细模式仅对最后一个活动组的字面最后一项应用叶子自动展开;该项必须是符合条件的 工具调用或托管搜索。失败和被拒行保持收起,最后一项为思考时不会向前寻找更早工具。 紧凑模式保持所有工具/搜索载荷关闭,不显示推理正文或摘要,只保留活动思考指示器。
整个过程、活动组和条目是相互独立的控件。收起祖先会保留下级选择,重新展开时恢复; 展开父级不会展开所有后代。用户操作子项时会接管祖先但不切换祖先,因此完成事件不能 收起包含已展开、聚焦或选中内容的容器。选择以稳定回合/组/条目标识保存在会话窗格 内,跨模式切换和行重新挂载保留;窗格淘汰、会话删除或渲染器重启后重新应用默认值, 不会写入消息或设置。
搜索和导航只展开拥有目标消息的过程与活动组,并且每个显示请求只 应用一次。紧凑模式中的推理在用户切换到详细模式前保持隐藏。权限、提问、计划/目标 审批及其他待处理操作卡位于隐藏过程之外,始终可达。参见 ADR turn-process-and-thinking-display。
7.2 解剖学
+----+-------------------------------------+
| 地图 | [用户留言气泡] |
| 轨道 | 【思考披露】 |
| | [辅助回合] |
| | [辅助片段] |
| | [工具调用行] |
| | [许可卡](中断) |
| | 【助理片段】 |
| | [元+一个操作工具栏] |
| | [用户留言气泡] |
| | ... |
+----+-------------------------------------+7.3 状态
| 状态 | 行为 |
|---|---|
| 会话激活 | 首次激活在布局过程中、在该面板的第一个绘制帧之前重新固定和定位最后一条记录;重新访问的面板改为恢复它自己保留的滚动位置 |
| 会话转换 | 热目标面板立即以其保留的内容和位置被揭示。若它仍在运行或仍持有尚未刷入的已完成回复,其实时渲染器快照在持久化再验证中保留。冷目标让可见面板在一条细进度轨道下停留在它自己的会话上,直到目标提交;没有任何内容被调暗,隐藏的面板保持挂载且惰性,当前流更新不会延迟 |
| 流媒体 | 追加新令牌;仅在固定到底部时自动滚动 |
| 回合开始 | 发送/重试/重新生成重新固定跟随模式并跳转到底部 |
| 仅思考流式输出 | 详细模式按活动思考策略显示条目;紧凑模式只显示状态指示器。用户手动收起后,后续推理不会重新打开该层级;没有空回答气泡,底部状态继续标明回合仍在运行 |
| 运行中兜底 | 未知具体运行时阶段且不在规划状态时,整轮显示带跳动圆点的紧凑 Working 行,包括部分输出暂停和工具完成后的空档 |
| 运行中规划 | 整轮在同一位置显示带跳动圆点的紧凑规划/目标行,具体运行时阶段优先;等待用户操作或回合结束时隐藏。Composer 模式芯片脉冲 |
| 空闲 | 可滚动;没有自动滚动 |
| 正在等待许可 | 内嵌插入许可卡;决议后继续转录 |
| 上下文检查点 | 现有的转录本仍然可见;压缩在其覆盖的消息后面添加一个分隔行和一个警告 toast |
| 错误 | 带有可操作重试链接的错误消息气泡 |
7.4 交互
- 滚动:第一次向上滚动会立即暂停自动滚动, 取消待处理的后续工作,并显示“滚动到底部”浮动按钮; 流或调整大小更新无法将视口拉回来;发送/重试/ 重新生成重新固定并跳至底部
- 悬停消息:出现复制操作
- 工具调用前后发出的助手片段合二为一
role="article"回合。转牌暴露了一个尾随元行和一个动作 工具栏; Copy 按顺序连接所有内容片段,而 Fork 和 重新生成使用最后一条内容丰富的辅助消息作为持久边界。 仅在这些操作可用时挂载工具栏;运行中或含助手错误的回合不保留空工具栏。 运行提示紧邻最后输出,避免不可见按钮占位造成额外空白。 保留助手消息原有 14px 底部内边距和运行状态栏完整高度,默认字号下纯文字 片段到状态文字为 24px。用户消息保留真实的悬停操作栏与原有间距,包括首次 等待阶段;按钮通过透明度隐藏但保持占位,悬停时不推动内容。 - 切换思考条目只改变该层级,独立于整个过程、活动组和最终回答。流式更新不会重新打开 用户已手动收起的层级;展开内容旁的左侧栏本身是可用指针和键盘聚焦操作的收起控件。
- 悬停代码块:出现复制按钮
- 将鼠标悬停或聚焦在小地图标记上:显示本地化的发件人和有界的 纯文本预览;一个用户内产生多个助手片段 回合合并为一个人工智能响应标记和预览;附近的标记 水平放大,无需对导轨进行回流焊
- 单击小地图标记:在靠近顶部的位置平滑滚动其消息 转录视口
- 滚动文字记录:针对锚点更新活动小地图标记 靠近视口的上三分之一处
- 仅当文字记录溢出一页时才显示小地图轨道;如果 内容适合视口,即使存在两个或多个标记也隐藏导轨
- 将小地图堆栈置于下方无阻碍垂直跨度内的中心 46px 标题栏和停靠的输入框上方。随着标记数量的增加,压缩 标记间距和间距,因此每个标记都保持在该范围内 比进入本机窗口拖动区域
- 来自流事件和内容调整大小的跟随滚动请求被合并到 至多一个待处理的动画帧。新令牌无法取消和重新创建 已经安排好后续工作。
- 向上手动滚动优先于待处理的跟随框架,包括 亚阈值触控板移动仍接近底部。向下 仅当视口返回底部 48 像素以内时,滚动才会重新固定。
- 小地图内容调整大小仅检查溢出。消息位置测量是 保留用于滚动、标记标识更改和视口调整大小,因此 流式内容高度更新不会将每条消息扫描两次。
- 上下文压缩永远不会删除、折叠或替换可见消息 行。它为每个压缩添加一个非消息分隔行,锚定在 检查点覆盖的最后一条消息;无论助手转动它,该行都会结束 落入内部,并且不再绘制锚点不再存在的行。的
new_context工具是普通工具调用,并到达处理组,如 任何其他。
7.5 辅助功能
role="log"容器aria-live="polite"用于新内容公告- 每个用户消息和组成的助手转:
role="article"和aria-label描述发件人 - Thinking 使用
aria-expanded和aria-controls的按钮披露; 本地化标签将显示思维与隐藏思维区分开来,并且 折叠面板对可访问性和焦点遍历隐藏 - 小地图是本地化的导航地标;每个标记都是一个按钮 标有其消息发送者
- 最接近读取位置的标记暴露
aria-current="true"和 键盘焦点会打开与指针悬停时可用的相同预览
7.6 MVP 约束
- 文字记录中没有消息搜索
- 无内联消息分支树;每个用户的重新生成变体保持线性 根转。会话级Create分支产生独立对话 row 而不是在转录本中添加树镶边。
- 小地图仅在至少存在两个符合条件的回合标记时渲染和 转录内容溢出一个视口(scrollHeight > clientHeight)。 每条可见的用户消息都会创建一个标记;所有内容丰富的助手 碎片,直到下一条用户消息创建一个锚定的 AI 响应标记 到第一个内容片段。仅工具行不会创建标记或 分割人工智能的反应,一页的文字记录永远不会显示出轨道。
- 标记预览的上限为 280 个源字符,并且仅供显示
- 派生的可见行、小地图行和活动分组由
messages快照。完成消息行、组成助理回合,以及 活动组保持稳定的渲染边界,而只有当前流 片段变化。
8. MessageBubble
8.1 目的
单个消息渲染 — 用户(纯文本)或助手(Markdown 流)。
8.2 解剖学
用户留言:
+------------------------------------------+
| 明文消息内容 |
| 时间戳·编辑图标 |
+------------------------------------------+助理消息:
+------------------------------------------+
| [思考▾] |
| 单独推理降价(可选) |
| ──────────────────────────────────────── |
| [Markdown 渲染内容] |
| 代码块:mono、bg-inset |
| 内联代码:mono、bg-inset |
| 时间戳 |
+------------------------------------------+8.3 布局
- 最大内容带:760px 线程列;助理身体最大720px
- 工具调用的每一级披露行(整个处理过程、活动组、子代理卡片、单条工具行)都铺满该内容带:标题行是全宽行,标签过长时省略号,箭头贴在行尾,而不是按自身文字宽度收缩的小块,因此跟随用户拖拽后的宽度变化,不会停在原地。
- 用户:右对齐、主题中性的软板(主墨水上的
color-mix, 从来没有固定的口音色调),无边界,radius-lg-plus更紧 右下角,上限为min(82%, 600px),因此简短的提示如下 聊天轮流而不是全角块。气泡按提示的 max-content 收缩,而不是撑到该上限;内联文件芯片使用确定的 240px 名称上限,因此粘贴文件加上短提示仍保持紧凑。用户正文是明文 保留硬换行符(white-space: pre-wrap); 仅应用 trailing/leading 输入框修剪,从不应用内部换行符 崩溃。序列化的@path文件引用画成与输入框一致的叶子名芯片(图标 + 省略号文件名;规范路径放在工具提示和无障碍名称里)。未内联成@path芯片的图片附件渲染为有界缩略图(fs/readImageDataUrl的 data URL);无法解析时保留芯片。消息正文里的裸路径 token 识别 Unicode 字母与数字。在用户消息正文中,它们只是候选:现有的fs/resolveRef查找确认是真实文件后才显示芯片。等待、未找到或失败的查找保留原文,包括使用llama.cpp。显式@path引用和结构化附件保留现有芯片,不做猜测查询。每条消息最多检查 32 个唯一候选,可见行之间最多 4 个并发查找;其余候选保持纯文本。确认结果绑定到当前消息文本、工作区路径和会话;任一变化都会丢弃旧结果并取消排队的工作。之后新建的文件在消息重新挂载或作用域变化时再考虑,不轮询。非 ASCII 文件名仍受支持。绝对路径与~/整体匹配,工作区之外(以及一切家目录路径)保持纯文本,而不是渲染一个永远打不开的芯片——包含边界不变(D322)。点击芯片先经pi-desktop/fs/resolveRef补全引用——该通道搜索整个打开的项目,按项目组文件夹顺序、主文件夹优先(ADR 0263)——再按解析结果打开:项目文件在随应用打包的pi.file-manager工作面板视图中打开(该视图不可用时退回宿主file:选项卡),会话临时目录或附件文件在宿主file:选项卡中打开,主文件夹中的.html/.htm仍由侧边浏览器打开。交给该视图的地址跟随应答的文件夹:主文件夹中的文件用项目内相对路径传递,同一项目的同级文件夹中的文件用绝对路径传递,与会话临时目录和附件文件一致。已解析的图片缩略图按同样规则打开。什么都没匹配到的芯片不打开任何东西,而是自己报告出来;系统默认应用不再由这次点击触发。HTTP(S) URL 仍是内联文本链接;裸 URL 保留路径、查询和片段中成对的圆括号,正文包裹网址时多出的右括号不属于链接;紧跟 URL 右括号的句末标点也不属于链接,但(draft).html这样的后缀仍保留,在侧边浏览器打开;长 URL 在板内换行并保持逻辑开始对齐,而不是继承浏览器的居中按钮文本。 - Assistant:透明表面,左对齐,Markdown 完全渲染 内容宽度。该 Markdown 里的工作区文件路径可预览:行内代码、Markdown 链接与裸路径 token(带已知扩展名)都像芯片一样,在打开项目的每个文件夹里先补全再打开(ADR 0263)—— 解析到的文件在随应用打包的
pi.file-manager视图中打开(没有该视图时退回宿主file:选项卡),主文件夹中的.html/.htm在侧边浏览器打开,什么都没匹配到时不开任何东西 并自己报告出来。 本地 Markdown 图片经同一受限 data-URL 通道内联显示,无法解析时回退为芯片。 - 思考:答案上方单独轻量披露,无卡 背景或外边框。其 Sparkles/chevron 触发器使用辅助文本, 扩展的降价由微妙的主题标记左规则缩进。它 永远不会连接到答案降价中。
- 悬停操作:气泡下安静的仅图标操作芯片 — 空闲助手回合显示复制; 完成的助手回合显示分叉和重新生成;用户回合显示编辑和删除。 流式助手回合在回复落定前不显示复制。辅助行既不公开删除也不公开编辑。芯片渲染字形 单独:标签由
aria-label加上主题 hover/focus 携带 芯片上方 8px 的工具提示(紧凑 raised 阴影,不用 composer 光晕),绝不是可见的标题文本 (D137)。右对齐 对于用户转动,左对齐 助理轮流;在 hover/focus-within 上可见。重新生成截断 持久转录到最近的先前用户提示符并重新运行该回合 就位而不是附加重复的分支。当超过 1 个时 存在变体,root 用户上有一个 ChatGPT 风格的current / total寻呼机 切换归档分支而不丢失历史记录(D109)。之后 Retry/Regenerate 启动,root 用户轮流保留在实时记录中 tail 不得将该寻呼机从用户气泡中移动或分离。寻呼机是 消息操作工具栏的一部分:默认隐藏,并与复制一起在行悬停或键盘聚焦时显示。 右键用户消息或助手回合会打开与悬停操作相同的词汇(复制、选中文本,以及该行自己的编辑 / 删除 / 重新生成 / 分叉 / 版本项),作为文档级、指针锚定的菜单。右键空白记录、系统行、权限卡或结果卡会打开对话菜单:复制整个对话、选中对话文本、滚动到顶部、跳到最新。引用、批注、打开侧边聊天仍按 ADR 0268 退役。流式或空助手回合若没有可提供的项则不打开菜单。说话回合上的「复制」写入打开菜单时该行内的选中文本;光标折叠或选区在行外时退回整段。复制整个对话按需读取完整会话,包含尚未加载的历史和未截断的消息文本,并保留当前可见的生成中回复,输出仍带说话人标签。复制不改变阅读窗口或滚动位置;读取失败时提示错误,不复制残缺历史。菜单项的复制结果走 toast,因为表面在项执行时就会关闭。 Fork 创建并激活一个独立的会话,其快照结束于 选择辅助响应,需要一个空闲源,然后留下 源的转录本、实时运行时和提供程序缓存状态保持不变 (D134)。 编辑属于用户回合:它将提示气泡替换为与底部输入框同族的 composer 面板(--ds-bg-composer填充、--ds-composer-radius, 无描边,无阴影,D297)。文本区域嵌在面板内不加第二层底;底部 28px 工具条放本地化的 “重试”和“取消”(Escape 取消,Cmd/Ctrl+Enter 重试;输入法组词期间忽略这两个快捷键并保留草稿;斜杠转为种子 输入command表单,重试后会重新扩展模板)。打开时把用户列加宽到 助理阅读宽度,并隐藏操作工具栏。重试运行同一会话的 重新生成路径,即使文本没有变化也会执行,因此替换的提示及其整个答案 尾部被存档为 D109 修订版,寻呼机可以返回原来的交换(D274)。 - 工具介导的辅助输出使用前一用户的视觉转向 消息到下一条用户消息。中间提供商消息边界 围绕活动披露的有序降价片段仍然可见,但是 不要创建额外的元行或操作工具栏。单个复制操作 按顺序复制所有内容片段;分叉并重新生成目标最后一个 内容丰富的片段,因此现有的持久转录语义保持不变 (D157)。
- 助理元:答案下方只保留可选模型徽章。紧凑的 Codex 风格上下文检查器 放在输入框右侧工具栏、模型 × 推理芯片左侧,始终对应当前最新一条已 报告用量的助手回合(D347)。占用、剩余容量、已用/窗口计数、本轮合计 以及模型 input/output/cache/reasoning/命中率取该回合最新一条已报告用量 的助手消息(最后一次模型请求),占用为
input + output + reasoning + cacheRead + cacheWrite(D355);它们不是 视觉工具循环里每一次请求的加总。没有用量时不显示。触发器保留容量 圆环和百分比,去掉重复的Context文字;引导数值(圆弧、百分比、令牌 标签、弹层标题、tooltip 与aria-label)跟随settings.contextUsageDisplay——"remaining"(默认)或"used"—— 因此圆环按remainingRatio或usedRatio填充。低容量时根据剩余容量 改变语义颜色(剩余 ≤ 25 % 警告、≤ 10 % 临界),不随显示模式变化, 不让颜色成为唯一信号。点击触发器(或使用键盘激活)会打开非模态摘要 面板,标题跟随同一显示模式,并展示已用/窗口计数以及两个无卡片的本轮/ 速度数值。模型用量压缩为一条内联摘要,保留精确的最后一次请求 input/output/cache/reasoning 数值和可用的缓存命中率。工具用量压缩为 一条聚合摘要,展示工具种类、调用次数和带~的估算令牌数;默认视图 不再显示逐工具行、占比条、来源徽章或解释性提示。标题下方的行共用 左标签/右数值节奏,只用留白分隔,弹层保留浮层描边、不画内部分隔线 (D297)。生成速度只代表已完成回合,流式响应期间不更新。上下文窗口 总计使用与 agent sidecar 相同的 effective model window:已发布的models.devlimit.context会替换遗留的 128k 通用 binding seed, 非默认的按模型 Advanced 值仍然优先;未知模型回退到提供商目录或默认 窗口。面板仍通过 document body 传送为固定视口覆盖层,在触发器上方/ 下方翻转、水平边界限制在会话面板内——工作面板的原生浏览器/插件视图 合成在所有渲染层之上,越过会话面板右缘的部分无论 z-index 多高都会被 覆盖(D357)——并在滚动、窗口调整或会话面板几何变化时重新定位(侧 边栏与工作面板的开关、拖宽和入场动画不会发出 resize 或 scroll 事件, 因此通过面板上的ResizeObserver观察),避免被裁剪容器或原生表面 遮挡(D103、D184、D244、D347)。活动会话存在上下文 检查点时,面板底部增加一条静音摘要,显示压缩次数和最新摘要的估算 成本;记录仍为每次压缩绘制一行(D203)。 - 间隙:连续消息行之间的 12px 垂直填充(比 消费者聊天,更接近 WorkBuddy 任务记录);助理轮流添加一个 底部空气很少,因此完整的答案与下一个提示分开
- 字体:正文基于文本(14px); text-sm (13px) 单声道代码
- 工具活动:工具名称分类选择语义 15px 图标;
fork、fork_agent、fork_task和fork_session使用 GitFork 分支 图标而不是通用工具字形。
8.4 状态
| 状态 | 外观 |
|---|---|
| 流媒体 | 沿着整个辅助回合强调左导轨(碎片+工具行);导轨的空间始终被保留,因此它的褪色 in/out 永远不会回流文本;内容增长 |
| 思维流 | 公开披露;答案气泡被省略,直到答案文本存在 |
| 完成 | 没有流媒体规则;完整渲染的降价 |
| 错误 | 转录本中的助理错误卡;本地化总结+稳定代码;详细信息披露开放给经过编辑的提供商响应、provider/model ID 和复制操作;可重试失败显示重试,配置失败显示打开设置 |
8.4a 上下文压缩行
不是气泡:转录本行之间的全角分隔线,在最后一个之后绘制 其检查点覆盖的消息。
- 一个居中标签 - 会话已压缩多少次 - 两侧不再有细线(D297); 仅靠标签本身和上下留白标记检查点。
- 第二个静音部分说明摘要的估计代币成本,或者没有 摘要已生成(无摘要系列)。
--ds-text-muted位于--text-2xs,带有表格数字;细节部分 步骤到--ds-text-secondary。页边距与文字记录的行节奏相匹配。role="separator"。没有动作,没有悬停状态,没有选择,没有披露。 检查点的任何内容均不可编辑,因此该行仅供参考。
8.5 辅助功能
- 用户:
aria-label="User message" - 助手:
aria-label="Assistant message" - 思考触发器公开本地化的 Show/Hide 标签、
aria-expanded和aria-controls与推理面板的关系 - 输入框工具栏中的上下文检查器触发器是键盘可聚焦的,公开本地化的 剩余百分比和代币数量,并在点击或键盘激活时显示相同的紧凑摘要; Escape 或点击外部会关闭面板并将焦点返回触发器
- 检查器面板 portal 到 document body、以视口坐标定位,但它的水平钳制 基准是会话面板:工作面板的原生浏览器与插件表面合成在所有渲染层之上, 因此一旦弹框伸入面板列就会被覆盖,与其 z-index 无关。当会话面板比弹框 窄时,弹框随面板收窄,而不是越过该边界。
- 时间戳:
aria-label带有完整时间字符串,视觉显示相对时间 - 右键菜单为
role="menu"、行项为role="menuitem",支持方向键 / Home / End,Escape / Tab / 外部按下 / 背后滚动关闭,并带有可访问名称(chat.messageMenu或chat.conversationMenu)。焦点回到被右键打断的控件。编辑用户消息时,复制读取草稿选区(无选区时复制整份草稿),选中消息文本会选中草稿;结束编辑前不提供针对已保存消息的编辑、删除或版本切换操作。
8.6 MVP 约束
- 无消息 reactions
- 无法编辑用户消息(推迟)
- 复制助手答案不包括思考文本
8.7 Markdown & 代码渲染(已实现)
Renderer: apps/desktop/src/components/Markdown.tsx + apps/desktop/src/lib/shiki.ts
styles/prose.css中.prose-chat/.code-block下的散文风格。
无卡顿流式传输:运行时内容块直接渲染,无需 第二个渲染器端打字机或动画帧状态循环。源分裂 通过与渲染一致的 remark/GFM/math 语法切分顶级块;每个块都通过一个渲染 已记住
<ReactMarkdown>。当仅流式传输尾部块时重新解析 (从最后一个块边界开始增量 re-lex),因此成本在 消息长度。美人鱼栅栏保留在正常的源代码演示中 直到其匹配的关闭栅栏到达;部分流图永远不会 输入图表解析器。若消息中任意位置声明了链接或脚注定义,则完全跳过切分: 定义在整条消息范围内解析,脚注还要跨消息编号、复用和回链, 因此该消息作为单一解析上下文渲染,并在其流式期间放弃按块记忆。插件:
remark-gfm(表格、任务列表、删除线、自动链接),remark-math+rehype-katex(内联$…$或\(…\),显示$$…$$或\[…\])。原始 HTML 是 由rehype-raw解析并立即受扩展约束rehype-sanitize默认架构;仅渲染器拥有的 audio/video/source,以及code上的math-inline/math-display类(保证 TeX\[…\]使用块级布局) 允许添加。 KaTeX 的 Vite 内联 WOFF2 字体被允许 渲染器的font-src 'self' data:CSP 指令。复制公式(D619):覆盖到渲染公式的选区,以公式被写下时的 TeX 进入剪贴板 ——行内
$…$,块级$$…$$独占行;两者的定界符长度都像代码段的围栏那样, 比公式内部出现的最长同字符串再长一位,因此含字面$的公式仍能被完整读回。 行内保持窄形式,是因为行内公式的 TeX 可能带换行,而$$一旦落在行首就会 开出块级公式并吞掉整段。KaTeX 每个公式都画两遍(一棵 MathML 树和 一棵可视树),因此平台自带的复制写出的是这两份渲染,而不是源码(issue #414)。lib/selection-tex.ts从 MathMLannotation里把 TeX 读回来,并把落在公式 内部的切口扩展成整个公式;hooks/use-copy-tex.ts是外壳持有的那一个文档级copy监听器,记录区的右键复制经由同一个模块读取同一个选区。只有公式被改写: 归约后的克隆经由Selection.toString()——复制自己跑的那个序列化器——读回,且在 选区原本所处的那个元素里读,因此决定这份读法的层叠就是实时文档的层叠。于是 同处一个选区的正文、列表、表格和代码块,读法与平台原本给出的完全一致,包括 复制本就会略过的那些界面零件——无论base.css是按选择器把它标为user-select: none,还是它仅因继承外壳默认值而不可选。不含公式的选区整份交回 平台处理;除此之外不再做第二重判断,因为 Chromium 的复制事件目标本就是从选区 推导出来的,事件必定落在选区内部——所以无论事件落在选区里多深的位置,整个选区 都会以源码进入剪贴板。复制只写一种 flavour:text/plain。接管事件同时也 丢掉了平台的text/html,而且不再写回——归约后的克隆是应用自己的标记,写回去 会带上文本读法已经略过的那些user-select: none界面零件;若改为携带渲染结果, 则每个公式会被粘贴两遍,因为隐藏 MathML 树的只有 KaTeX 自己的样式表。富文本 粘贴目标会回退到这份纯文本。复制之后公式的边界仍然可解析:相邻的行内围栏之间补一个分隔符;正文里的每个 美元符号都会被转义,本会把围栏转义掉的反斜杠串同理。annotation 里的空白原样 保留;加宽过的多行行内公式用一个字面的
<span>包住,以免它粘在行首时开出 块级公式。TeX 里的换行不做压平,因为它可能用来终止%注释。这个包裹是text/plain里的 Markdown 源码,不是text/html负载;对禁用行内 HTML 的 外部编辑器不作兼容承诺。回归覆盖两个复制入口,以及相邻公式、围栏两侧的正文 美元符号、格式化包装、行与块的边界、补白,以及含 TeX 注释的多行公式各自的 Markdown 往返。美人鱼图(D165):助手中完成的
mermaid围栏块 答案散文通过官方美人鱼包呈现。依赖关系是 仅当图表接近视口时才动态导入;美人鱼的 全局主题配置和渲染调用是序列化的。图源 字符数上限为 20,000 个,图形边数上限为 500 个。严格的安全性, 受保护的配置密钥,禁用 HTML labels/links,以及第二个 DOMPurify SVG 配置文件在插入之前传递。不安全的 external/media 元素,foreignObject、支持事件的链接和 URL 属性已删除。无效 或者过大的输入会退回到可读的源视图。工具栏切换 diagram/source 并复制原始来源; light/dark 主题更改 重新渲染 SVG。思考散文故意将mermaid栅栏保留为 源代码,因此折叠的推理跟踪无法启动图表布局。语法突出显示:使用 JavaScript 正则表达式引擎的 Shiki 单例 (无 wasm),主题
font-src 'self' data:/mermaid紧随data-theme。 以编码为中心的本地目录公开了 48 种规范语法以及常见语法 别名;每个语法都在其第一个匹配的栅栏标签上延迟加载 普通单声道后备直至准备就绪。该目录之外的标签仍然可读 纯文本,而不是将完整的 Shiki 语言分布拉入 应用程序。规范目录为astro、bat、c、cpp、csharp,css、dart、diff、docker、dotenv、go、graphql、groovy、hcl、html、ini、java、javascript、json、jsonc、jsonl、jsx、kotlin、lua、make、markdown、mdx、mermaid、nginx、php、powershell、prisma、proto、python、ruby、rust、scala、shellscript、sql、svelte、swift、terraform、toml、tsx、typescript、vue、xml和yaml。流代码仅重新标记 通过链接 GrammarState(每行缓存)更改行,因此每帧成本 无论块大小如何,都是恒定的。代码块镀铬:
.code-block单面卡(radius-md-plus, 自 D297 起无边框——一块--ds-tile色块;深色#282c34、浅色#fafafa— 匹配 One Dark Pro / 一盏灯编辑器 bg)。标题是透明的(语言标签左,复制右); bodyshellscript/sql/token 跨度具有无嵌套背景,因此 Shiki 令牌 颜色位于一张卡片表面。正文位于 text-sm-plus / 领先-放松,水平滚动,制表符大小为 2。散文:更平静的聊天密度 - 正文以文本为基础/以散文开头 漂亮的包装;航向坡道 h1
text-xl(自 D297 起无下划线)→ h2text-lg-plus→ h3text-lg→ h4text-base-plus→ h5/h6text-base次要; blockquotes 在软板上使用 3px 中性规则; 引用块是无线条的柔和--ds-tile色块(D297);hr 只是留白;列表使用更安静的标记和弹性任务 行;内联代码只有柔和的灰色色调、无边框;表格去掉单元格边框, 改用--ds-tile-deep表头与--ds-tile斑马行;桌子包裹 在.table-wrap(圆壳、标题行、偶数行洗、悬停洗)。 包装层与表格铺满转录列宽;单元格文字可换行 (overflow-wrap: anywhere),多列或长 token 不会撑出横向滚动条。overflow-x: auto仅作为无法断开内容的回退。 显示数学位于一个微妙的插入板上。思考散文重复使用相同的内容 text-sm-plus / 辅助颜色的层次结构。浅色主题:安静的表面 - 链接使用柔和的下划线墨水 (不硬black/blue),内联代码
#f2f2f2,围栏代码卡使用一 浅色#fafafa(无嵌套清洗/阴影),块引用#f6f6f6, 白色表格,带有#f3f3f3标题/#fafafa斑马。黑暗围栏代码 使用 One Dark Pro#282c34。链接:工作面板中的普通点击预览;修改后的点击保留
target="_blank",主进程在 http(s)/mailto 白名单之后走shell.openExternal(D330);窗口内导航仍然被阻止。长转录行为:
.thread-scroll设置overflow-anchor: none(固定跟随拥有滚动位置),.message-row使用content-visibility: auto和屏外美人鱼图延迟加载和 布局,直到它们接近视口。
9. ToolCallRow
9.1 目的
显示语义工具操作的轻量级内联披露行,其主要 参数提示、状态和结果的可读呈现。遵循D071 并且故意不是一张高级卡。
两个进度段落之间的一段连续工具、搜索或思考活动,仅在有两个及以上可见项时成为普通 处理组。单项活动直接使用自身披露,Task 拓扑继续使用原有容器。回合活动期间,拥有 当前执行片段的普通组在详细模式下默认展开,在紧凑模式下默认收起;详细模式的未操作 组在完成时自动收起。详细模式仅在最后一个活动组的字面最后一项是符合条件的工具调用 或托管搜索时自动展开其载荷;更早、失败和被拒行保持收起,最后一项为思考时不会向前 选择工具。紧凑模式保持所有工具/搜索载荷关闭。
组标题在活动时显示 Processing · 12s,完成后显示 Processed for 12s,并提供有界 条目与问题计数。展开后显示有序活动行及其相互独立的结果披露。失败子项仍在自己的 ToolCallRow 上显示错误,但不会让组或整个回合成为终端失败;终端 Agent 错误仍由助手 错误或 TurnOutcomeCard 表面拥有。用户操作组、条目或收起栏时会接管该层级及其祖先, 但不切换祖先,因此流式更新和完成不会覆盖选择,也不会收起聚焦或选中的内容。
9.2 解剖学
[sparkle] Processed for 12s 3 steps [›]
├─ [file] Read /src/foo.ts [›]
├─ [search] Searched TODO 24 matches [›]
└─ [terminal] Ran pnpm test exit 1 [›]
├─ Command [copy]
│ pnpm test
├─ Output [copy]
│ 3 passing
└─ Errors [copy]
1 failing- 领先的 Lucide 图标反映了操作类型:文件、文件夹、搜索、 编辑、终端、Web 或通用工具。
- 多项组标题拥有经过时间以及条目和问题计数,并在完成后保留在转录中。详细模式下 活动组默认展开,完成时仅在用户未操作时收起;其他已完成组默认收起。紧凑模式的组 默认收起。单项活动没有组标题。
- 紧凑模式保持工具/搜索载荷关闭。详细模式仅自动展开最后一个活动组中符合条件的 字面最后一项;失败/被拒项保持收起,最后一项为思考时不会选择更早工具。活动思考 遵循自身披露策略,绝不会展开同级载荷。
- 处理组跨越完整可用的辅助列,因此扩展 即使标头或有效负载很短,结果详细信息也会保持可用宽度。
- 可见标签是自然语言操作(
Read、Ran、Searched), 不是原始函数名称。跑步动作采用渐进形式。 - 主要参数是固定的单行等宽提示。
- 结果芯片遵循提示:退出代码(错误色调)、match/file 计数、 替换计数、写入或读取大小、
truncated、scratch。一个成功的 退出不会获得任何筹码——行状态已经说明了这一点。truncated芯片跟随details.truncated,因此只在本次结果被切断时出现,而不是在更长文件的 Read 窗口已经填满时出现(D306)。 - 在 hover/focus 或扩张之前,披露 V 形是安静的。
9.3 扩展块
扩展的主体是标记块的列表,而不是 JSON 转储 (D192)。的 pi-ai 结果信封携带 details 中的结构化有效负载并重复它 作为模型的文本;仅呈现结构化的一半,因此不会出现任何字节 两次。
| 工具 | 积木 |
|---|---|
| 阅读 | File content — 从文件扩展名中突出显示的语法 |
| 写 | Written content — 从目标扩展中突出显示 |
| 编辑 | Changes — 紧凑差异,仅当没有 ReviewChangeCard 拥有时 |
| 重击 | Command(外壳)、Output、Errors(错误色调);空通道被省略 |
| 全局 | Files — 可点击的工作空间路径 |
| 格雷普 | Matches — 按文件分组,带有 line 装订线和 outputMode: content 的可点击路径标题; filesWithMatches 的可点击路径列表; path → count 的点击计数字段 |
任何主机 notice | Note — 中性,在其合格的块之后(搜索范围、剪裁长行、读取窗口) |
| 任何失败 | Error — 消息加代码,首先列出 |
| 未映射的有效负载 | 标量条目作为 label/value 字段;长或多行字符串作为自己的标记块;嵌套对象为 JSON |
- 仅当结果块不存在时,参数才显示为
Input字段块 已经携带它们,或者对于不透明工具(use、fork、fetch) 争论是有趣的部分。参数已显示为行提示 不重复。 - 每个块都公开一个紧凑的复制操作,该操作复制完整的有效负载,而不是 可见切片。
9.4 布局
- 外排:透明、无边框、无阴影,高约24px
- 图标:15–16px;披露 V 形:12px
- 标题间隙:4px;扩展主体插入:24px
- 芯片:等宽
--text-2xs、--ds-tile-deep填充(无边框,D297)、退出代码的错误色调 - 代码、文件列表、匹配列表和字段块:
font-mono text-sm、 可独立复制,上限为 260px,具有内部滚动功能 - 文件列表中的路径是块级、起始对齐的一行(与 Grep 路径标题同一盒模型)。 全宽
<button>不得把路径字符两端对齐撑满整行。 - 差异块重复使用审查卡的
.diff-line导轨 - 只有扩展的内容才会有插入表面和微妙的边框
9.5 状态
| 状态 | 标头处理 | 扩展内容 |
|---|---|---|
| 运行中 | 渐进动作配可读文字与脉冲标记;run 行还显示旋转器,并让 Working… 旁状态点脉冲 | 详细模式展开活动的多项组;仅符合条件的字面最后一个工具/搜索载荷展开。紧凑载荷保持关闭;活动思考遵循自身指示器/披露策略 |
| 成功 | 过去时动作加结果芯片;除 run 行状态点和 Done 外不显示绿色成功徽标 | 先显示结果块,再显示尚未呈现的参数;未操作的活动组在完成时收起,手动组/条目选择和详细模式字面最后叶子状态保留 |
| 错误 | 过去时动作加紧凑危险状态;详情默认收起,只在用户请求时展开。只要命令退出码非零,run 行就处于此状态,不受调用报告影响(D227) | 先显示错误说明,再显示参数 |
| 被拒绝 | 静音 Denied 状态;载荷在请求前保持关闭 | 可用时显示权限结果 |
9.6 互动
- 单击条目只展开/收起该结果载荷。紧凑模式载荷默认关闭。详细模式仅在该行是最后一个 活动组中符合条件的字面最后一项时默认展开;更早、失败和被拒行保持收起,直到用户 主动打开。
- 单击处理标题只展开/收起该有序活动列表。详细模式的活动组默认展开,完成时仅在用户 未操作的情况下收起;其他已完成组和紧凑模式组默认收起。展开组不会展开所有条目。
- 单击或用键盘激活展开思考、工具详情、委派工作或处理步骤旁的左侧栏,只收起所属 披露,不改变相邻状态。条目操作会把所属组和整个过程标记为用户接管,但不会切换 祖先;之后的流式更新或完成不能自动反转这些状态。
- 失败子行保持错误色并在标题中报告失败,但载荷不会自动展开。包含它的组即使后来工具 已恢复,也会以
Processed for {elapsed}收尾并显示问题计数。展开使用短 height/opacity 过渡,收起内容保持惰性。 - 运行更新替换最新的部分输出。积木已建成 仅在扩展上,因此流媒体费用保持便宜。
- 结果在争论之前呈现,因此主要结果具有更高的 信息优先。
- 文件路径和 Grep 解析时会在工作面板中打开标题 在工作区根目录下;它之外的路径保持纯文本。
- 主机截断标记保持可见,并且不能通过扩展绕过。 渲染的列表和差异有上限并报告隐藏的剩余部分。
- 语法突出显示在 100 KB 或 800 行以上被跳过。
9.7 辅助功能
role="region"和aria-label="Tool call: {toolName}"- 通过本地化的
aria-label文本宣布状态 - Expand/collapse:
aria-expanded+aria-controls - 扩展内容左规则折叠控件是带有 本地化的可访问名称和可见的键盘聚焦环。
- 复制操作带有
aria-label="Copy {block label}" - 键盘对焦使用标准嵌入式对焦环
9.8 MVP 约束
- 没有字级差异细化;编辑差异是基于行的
- 在回合边界可供使用之前,不得进行跨行活动分组 转录成分
9.9 委派卡片和扇出拓扑(D201、D265、D268、D271、D302、ADR 0062)
Task 调用呈现为委派卡片中的一个节点,而不是紧凑的工具行 —— 单个委派 与扇出读起来完全一致(D265)。节点标注它运行的子智能体:优先取自它产出 的行,在任何行到达之前则取自调用自身的 agent 参数;节点还带上调用的 简短 description。
生命周期行(TaskWait/TaskList/TaskStop)仍是紧凑工具行 —— 它们不是拓扑 节点,也不得计入子智能体数量 —— 但呈现为子智能体行,而不是通用工具调用 (D268)。生命周期行以委派 ID 作为参数,读起来只是一串裸 UUID,因此它绝不 从自身参数生成摘要:
└─ [bot] 等待子智能体 2 个 Subagent explorer, fixer 失败 [›]
├─ 提示
│ ## explorer (d1) — completed …
└─ 详情
explorer completed · 3s · 6 turns
fixer failed- 摘要是它所报告的名册,按子智能体名称,读自
details.delegations[](TaskWait/TaskList)或details.stopped[](TaskStop)。重复出现的 子智能体以计数呈现(explorer ×2),而不是列两遍;标签旁附子智能体数量芯片。 - 状态徽标汇总该名册,沿用与拓扑节点相同的
chat.subagentStatus.*文案: 只要还有成员在运行,整行即为运行中;failed/denied成员优先于已完成的 兄弟节点;否则未完成的成员(超出回合上限、超时、已停止)优先于completed。 - 标签说明它对子智能体做了什么,而不是"已委派":只有
Task会委派。该行 以子智能体强调色显示delegate机器人图标,从而与它所报告的卡片归为一组。 - 展开后的正文是名册构成的命名表格,每个子智能体一行,含状态、运行时长和 回合数,前面是合并后的报告提示 —— 绝不是
delegations[]原始 JSON。生命 周期行没有自己的任务简报,因此调用时使用的那些 ID 不会作为参数块再次出现。
[flow] Subagent completed 1 subagent · 1/1 finished · 40s [›]
┌────────────────┐ ┌───────────────────────────────────────────┐
│ (◎) Main agent │────│ [bot] code-reviewer Completed · 32s │
│ Coordinating 1 │ │ check the store diff │
│ delegated task │ │ 3 steps [›] │
└────────────────┘ └───────────────────────────────────────────┘展开节点后,先是该调用携带的块,然后是子智能体自己的行:
└─ [bot] code-reviewer check the store diff Completed · 32s [›]
├─ task [copy]
│ Review the changes in src/stores for …
├─ Details
│ status completed turns 4 toolCalls 9
└─ [bot] What code-reviewer did 3 steps
├─ [thinking] Thought for 2s [›]
├─ [file] Read /src/stores/app-store.ts [›]
└─ The queue drops a request by id, so …- 代表团始终可扩展,即使没有结果限制:简介、 报告和代表自己的行都位于正文中。
- 块顺序是简短输入、报告输出、计数器最后:
task参数作为input块,将报告作为输出块,然后保存一个Details块 返还的计数器 pi —status、turns、toolCalls和usage存在。agent被省略,因为节点标题已经显示了它,并且error呈现为前导错误块,而不是计数器。的 委托自己的行跟随整个正文,因此摘要在正文之前读取 细节。 - 失败的委派显示其错误而不是空报告。
- 委托的行在
.subagent-run块内呈现,这是一块无导轨的--ds-tile色块(D297), 以代理名称和步数为首。它们随节点一起折叠,因此静止的转录 每个活动组读作一张卡片。 - 展开后的运行过程在原处滚动,而不是把转录撑高(D271)。否则一个执行了 四十次工具调用的子智能体,会在节点展开的瞬间新增四十行,把阅读位置和父级 的下一行都顶出视野。这些行位于高度受限的
.subagent-run-rows滚动区内, 高度为min(420px, 48dvh),并设置overscroll-behavior-y: contain,因此 滚动到尽头不会带动其后的转录。滚动始终挂在这个内层容器上,绝不挂在.subagent-run上——折叠导轨定位在运行块的内边距盒之外,在其上设置 overflow 会把导轨裁掉。运行标题留在滚动区之外,因此归属信息不会从它所标注 的行上滚走;该区域是带标签且可获得焦点的分组,键盘用户能滚动指针可滚动的内容。 - 展开后的嵌套滚动区跟随最新输出(D302)。它独立使用与父级转录相同的 吸底跟随契约(
04-ux/09-interaction-patterns.md§9.1):展开时钉在最新一行, 新的思考 / 工具 / 回答行把视口留在底部;第一次真正的向上手势暂停跟随,并在 该滚动区上显示嵌套的「回到最新」控件;布局夹持或程序性的 followscrollTo都不算那次手势。.subagent-run-rows关闭原生 overflow anchoring,以免对抗 吸底。跳转控件复用chat.scrollToBottom,放在滚动区外层的相对定位包装上, 绝不放在.subagent-run上,因此折叠导轨不会被裁掉。 - 每个细节块都以同样方式受限:
fields表格与content、文件列表、匹配列表 一样限制在 260px,因此长名册或含三十个键的插件载荷会滚动而不是拉长页面。 生命周期行合并后的报告渲染为output块——受限且可复制——而不是没有高度 限制的提示块。 - 通过构造,嵌套深度为一层:代表没有
Task工具。 - 委托行是该块内的普通行 - 具有自己的工具行 披露、思考行和答案行——因此不需要新的演示 对于代表所做的事情。
- 报告仅打印一次。 当代表给出答案时 行,该行是报告,正文的输出块被抑制;当它 没有产生任何内容(中止、上限、失败),主体将其打印出来。
- 代表行永远不会出现在回合流、小地图或处理中 他们自己的团体;仅按父级的行进行分组 (
03-runtime/04-data-storage.md§4.7a)。 - 每次渲染时都会从消息列表中重建运行,因此可以进行组记忆 通过行标识和长度而不是对象标识来比较它们 - 否则流委托将冻结在其第一行。
- 活动组里的每一次
Task调用都成为一张全宽委派卡片,而不是紧凑工具行, 单个委派同样如此(D265)。卡片标头显示聚合状态、子代理数量、 settled/total 计数和经过的时间;它保留标准的披露插入符号。聚合状态按 数量选择措辞,因此单个委派不会被写成复数。 - 扩展卡渲染一张具有一个主代理根的低噪声点状画布 按父行顺序连接到
Task节点。运行时暴露没有 委托依赖关系并禁止嵌套Task,因此渲染器不得 发明委托到委托边缘或下游汇总节点。 - 每个节点显示定义名称、简短描述、明确结果、 持续时间和步数。结果更喜欢结构化的
Task结果 (completed、truncated、aborted、stopped、failed)并回退到传输 状态(running、error、denied、success)。父回合结束后,残留的running节点重建为aborted;TaskStop的details.stopped[]也是状态来源, 快照仍写着running时呈现为stopped。已结束的会话不会保持“子智能体工作中” 的实时卡片。单击该节点将展开 现有 brief/report/counters 和嵌套行;报告仍处于打印状态 正好一次。 - 回合激活时首次出现的拓扑会打开一次,以便进度 是可见的,并且在回合时不会自动折叠。重载历史 默认情况下保持折叠状态。标题和每个节点都是键盘 通过 settled/total/
Task进行披露;状态以文本形式写入 并在视觉上得到强化,而不是仅通过颜色来传达。在狭窄的聊天中 宽度使图形成为垂直流,而不会出现水平页面溢出。
9.10 运行行的命令属于它的头部(D226)
run 行是唯一一种「主参数本身就是这次调用的全部要点」的行。它的头部 已经把命令打印出来了,所以正文展开的是输出,而不是一个把读者刚读过的 内容再重复一遍的 Command 块。正文不再提供的两样东西上移进了头部。
└─ [terminal] Ran pnpm test exit 1 • Failed [copy] [›]
desktop test 648 tests
1 failing: run head keeps its caret- 命令只出现一次。
run行的正文只放Output、Errors和宿主的notice—— 从不放命令。权限卡没有自己的头部,所以它继续显示它正在 询问的那条命令。 - 复制按钮位于 V 形旁边,给出的是命令的原始写法,包括一行式头部 提示不得不挤掉的换行。
- 正文就是输出本身,不加框(D227)。
run行的各个块去掉了标题和 卡片 —— 没有边框、没有填充、没有逐块的复制按钮 —— 于是展开后的行读起来 就像文本来源的那个终端。260px 高度上限及其滚动保留:一次长构建不该把 转录本埋掉。Errors保留它的色调,每个通道的名称改由辅助技术承载, 取代原先为它命名的标题。 - 结果说的是命令做了什么,而不是这次调用做了什么(D227)。它读自 shell 报告的退出码:非零就是
Failed,即使包裹它的工具调用本身返回 正常;一个被杀掉、根本没报退出码的 shell 同样是Failed。不报退出码的 工具回退到调用的状态;两者都没有的行就什么都不说,而不是声称Done。 - 结果一定写出来,成功也写:一个带色调的点加上
Done、Failed、Denied或Working…。run行不显示旋转指示器;改由运行中的点脉动, 并在prefers-reduced-motion下保持静止。含义由标签承载,所以点的色相 永远不是唯一信号。失败的命令总会展开它自己的行,无论是哪一层发现了失败。 - 两个新控件都遵循 V 形的「用到才出现」规则:静止时隐藏,行悬停、获得 焦点以及行处于展开状态时显示。状态标签始终可见 —— 它是结果,不是可 操作提示。
- 头部是一个由三个控件组成的 flex 行,因此悬停填充属于头部而不是其中的 披露按钮;无法展开的行完全不取填充。
- V 形是复制控件旁边的指针目标,并留在阅读顺序之外,因为头部本身已经是 键盘披露控件。可见的状态标签同时充当该行的 live region,因此结果只被 播报一次而不是两次。
10. PermissionCard
10.1 目的
内联记录卡请求用户批准高风险工具调用。参见 03-permission-ux.md 了解完整策略。
10.2 解剖(内嵌卡)
+----------------------------------------------+
| ⚠ 需要许可 |
| 工具:写入·风险:高 |
| 原因:Agent想要修改文件 |
| ─────────────────────────── |
| 参数预览(已编辑) |
| 工作空间:/Users/dev/project |
| ─────────────────────────── |
| [允许一次] [允许会话] [拒绝] |
| 超时:120秒倒计时 |
+----------------------------------------------+编辑后的参数预览使用 ToolCallRow 块演示(第 9.3 节): 命令读取为 shell,文件内容读取为代码,其他所有内容读取为 label/value 字段。它绝不是 JSON 转储。
10.3 会话范围
- 该卡在原始会话的最新活动组之后呈现。
- 仅挂载活动会话的待处理请求。后台请求 保持会话键控渲染器状态,而不将内容插入到 可见的文字记录或覆盖另一个目的地。
- 不同的会话可能各自持有一个待处理的请求。分辨率、超时、 中止、工具完成和会话删除仅清除匹配的 请求。
- 倒计时使用请求的绝对接收时间,并且不会在以下时间重新开始 用户切换离开并返回。
10.4 状态
| 状态 | 外观 | 行动 |
|---|---|---|
| 待定 | 警告重音,倒计时可见 | 允许一次/允许会话/拒绝按钮处于活动状态 |
| 解决 | 保留待出庭 | 在请求解决之前,所有三个按钮均被禁用 |
| 允许一次 | 成功边框,“允许(一次)”标签 | 没有动作 |
| 允许的会话 | 成功边框,“允许(会话)”标签 | 没有动作 |
| 被拒绝 | 错误边框,“拒绝”标签 | 没有动作 |
| 超时被拒绝 | 警告边框,“拒绝(超时)”标签 | 没有动作 |
10.5 互动
- 按钮:主要(允许一次)、次要(允许会话)、危险(拒绝)
- 倒计时:可见计时器从 120 秒递减
- 第一个操作锁定所有按钮。解决错误使用错误提示; 成功或失败的完成会将焦点返回到当前输入框。
- 原始会话的输入框无法在等待许可期间发送, 同时文本保持可编辑(根据 03-permission-ux.md §7)
- 中止取消待处理的权限
10.6 辅助功能
role="region"具有本地化的可访问名称;静态标题供应 礼貌的实时公告,因此每秒计时器不会重新公告- 按钮清楚地标记并且可以按正常的转录选项卡顺序访问;的 卡永远不会捕获或强制移动焦点
- 定期(每 30 秒)或根据要求宣布倒计时
10.7 MVP 约束
- 仅限内联卡;没有模态或背景后备
- 没有“始终允许”选项(根据 03-permission-ux.md)
- 无风险级别定制
10A. ContractApprovalCard(Plan / Goal)
10A.1 目的
同一 pi 提交的确切 Markdown 字节的内联批准界面 Agent 并保存在新的不可变 .pi/<kind>/*.md 工件中。它是独特的 来自 PermissionCard:它批准 Plan 或 Goal → Agent 转换和显式 执行权限模式,不是单个工具调用。
10A.2 内容
该卡片呈现结构化标题和确切的开场白 .pi/<kind>/*.md 路径;打开器优先使用内置文件视图,在该视图不可启动时 回退到宿主机文件标签(D452)。打开工件会读取主机写入的文件; 渲染器编辑不会更改批准的字节。提交的问题/ 描述、状态、validity/deadline、内联 Markdown、SHA-256、字节大小、 和 revision/feedback 控件不渲染卡片内容。
由于内置文件视图可以编辑并保存它打开的文件(ADR 0241),在 Approve 之前被改动的 工件不再匹配记录的哈希:宿主会以 PLAN_ARTIFACT_HASH_MISMATCH 让该次审批失败关闭, 直到提议被拒绝并重新提交。
10A.3 动作和状态
| 状态 | 行动 | 合同 |
|---|---|---|
| 待定 | 批准、拒绝 | 请求处于活动状态且范围为 proposal/session/turn/tool-call/version |
| 解决 | 所有操作均已禁用 | 保留提案直至主持人结果 |
| 已批准 | 没有任何动作 | 相同的 Agent 以选定的权限模式在 Agent 中继续 |
| 排队/运行 | 没有任何动作 | 批准的执行处于活动状态并绑定到同一批准行 |
| 被拒绝 | 没有任何动作 | 运行停止并且会话保持其合同模式 |
| 过期/中断 | 没有任何动作 | 关闭失败;除非已提交批准,否则必须提交新合同,在这种情况下,会话仍为 Agent |
批准打开明确询问/接受编辑/自动选择最后选择的内容 该设备上记住的模式。拒绝携带无许可模式。渲染器保留最新的 proposal/execution 快照 会话仅适用于实时主机事件的当前渲染器生命周期,而仅 待处理的快照具有操作或门控 Composer。 Renderer 重新加载调用 plans.pending 并用其原始截止日期恢复仍待处理的行,同时 宿主还活着。它不会再水合被拒绝、过期、 approved/completed,或中断的终端卡;终端卡可能会保留 仅在重新加载之前可见且不可操作。启动恢复中断 提供 RPC 之前的 pending/queued/running 字段,恢复无可操作的过时内容 批准,并且从不重播执行。未决的未批准工作仍然是 Plan 和 已批准的中断执行仍为 Agent;用户界面不需要 重启后呈现中断的终端快照。
10A.4 辅助功能
- 该卡是一个会话范围的
region,具有本地化的计划标题。 - 批准、拒绝和中止控制具有明确的标签和 键盘焦点。
- 所选权限模式公开无线电语义及其 Plan/Goal Bash 结果可在可访问的描述中获得。
- 解决方案不会导航到另一个会话或从某个会话转移焦点 不同的会话。
11. Composer
11.1 目的
MainChat 底部的输入区域,用于撰写和发送提示。支持多行输入、mode/permission 上下文、中止,以及组合的模型 × 推理级别控件。
11.2 解剖学
+----------------------------------------------------------+
| [Agent/Plan/Goal] [权限模式] | [圆环 %] [模型 · 推理 ▾] |
| 排队消息(可选;每项一行) | [⏹ 停止 / → 发送](单个提交位) |
| 文本区域(自动增长,1 行 → 最多 7 行) |
| 占位符:欢迎语 → 命令/文件提示 → 快捷键提示 |
| (仅在页面/会话切换时推进;保留原生值以支持辅助技术) |
+----------------------------------------------------------+11.3 布局
- 高度:默认紧凑的一行外壳;文本区域自动增长到七个 可见行,然后文本区域在内部滚动
- 工作空间上下文:没有渲染项目、本地或分支轨道,或者 在主模式或线程对接模式下保留在外壳上方 (D095)
- 背景:一个坚实的语义输入框表面;无内部梯度, 背景图像,或装饰水洗
- 会话中的
.composer-dock-docked使用主工作区背景绘制整条停靠区域, 遮住滚到悬浮输入框下方以及圆角外侧的正文;首页模式不绘制这条遮罩。 - 仰角:20px半径,只有克制的柔和阴影;细线描边已在 D297 移除; 停靠的文字淡入淡出位于输入框外壳之外
- solid/near-opaque 表面不使用
backdrop-filter; focus-within 添加了一个 1px 提升和令牌阴影,无需通过模糊强制重新绘制文字记录 层。 - 边框:边框-默认顶部
- 填充:px-4 py-3 内部文本区域
- 字体:Agent、Plan 和 Goal 的 text-sm;模式改变语义和工具 控件,而不是排版
- Agent/Plan/Goal 模式芯片保留一个固定的 88px 宽度,尺寸从 最长的英文和中中文内置标签(“Agent”/“智能体”)。它的标签 如果未来的语言环境超出预算,则保持单行并省略,因此 切换模式永远不会回流相邻的 Composer 控件。循环切换时图标和标签就地交叉淡入。 当前回合处于实时
planning时,芯片图标以紫色脉冲,而不是在输入框上方再停一行状态; 回合中暂存的模式选择仍会立刻改芯片,但要等进行中的回合真正投影planning才开始脉冲。 - 右侧工具栏在有用量时于模型 × 推理芯片左侧放置剩余容量检查器(D347);触发器只显示圆环和百分比。模型 × 推理芯片直接显示规范思考等级值,不进行本地化;
off时省略等级文本。 - 空草稿高度:
.composer-input使用min-height: 3lh,空闲输入框默认显示三行。 - 底部锚定:固定在主聊天区域的底部
- 占位提示:首页使用
chat.placeholderHome、chat.placeholderHomeHint和chat.placeholderShortcut;会话输入框使用chat.placeholder、chat.placeholderHint和同一个快捷键提示。页面/会话 上下文不变时保持当前文案;切换首页/会话视图或活动会话时推进到下一条提示, 不再绑定焦点、草稿或 IME 状态的计时器。可见文案通过带 key 的透明度渐变切换, 同时保留 textarea 原生placeholder值以支持辅助技术。
11.4 状态
| 状态 | 外观 | 行动 |
|---|---|---|
| 闲置(无模型) | 文本区域处于活动状态,发送按钮已禁用 + 工具提示“首先配置模型” | Agent 链接在模型菜单中仍然可用 |
| 空闲(就绪) | 文本区域处于活动状态,发送按钮已启用 | 发送活动 |
| Home/new-session 初始化 | 当没有投影活动会话时,textarea 和 mode/thinking/permission 触发器仍然可用;第一个配置选择保留在未持久化的草稿上,并在第一条消息创建会话时应用 | 配置草稿,然后发送 |
| 新会话(推理模型) | 模型 × 推理芯片显示模型及其绑定的默认思考等级 | 用户可以选择绑定已启用的任何级别,包括支持时关闭 |
| 新会话/在另一个会话运行时切换 | 文本区域处于活动状态,为目标会话自己的运行状态启用发送按钮 | 发送活动;除非目标会话正在运行且草稿为空,否则停止隐藏 |
| 跑步 | textarea 和 mode/thinking/permission 控件在下一回合中保持可编辑状态;单一提交槽位在草稿为空时显示停止,有内容时显示发送 | 草稿为空时停止活动;有内容时发送活动;配置已排队 |
| 上下文检查点 | 与运行直到持久检查点完成相同;中间 turn_end 不会重新激活控件。保留尾部回退保持运行并显示警告 toast | 与运行状态相同的单一停止/发送槽位行为 |
| 正在等待许可 | 文本区域已禁用(根据 03-permission-ux.md §7) | 发送禁用;满足运行中且草稿为空时停止仍然可用 |
| Plan / Goal / 规划 | 文本区域在空闲时处于活动状态;合同徽章和许可芯片可见;实时回合投影 planning 时模式芯片脉冲 | 检查、发送或提交合同 |
| Plan / Goal / 等待批准 | 批准表面仅显示标题和工件开启器以获取确切的 .pi/<kind>/*.md 批准;草稿保留为只读,并且该会话的输入框控件仍然被阻止 | 批准或拒绝 |
| Plan / 已排队或正在运行 | Agent 徽章保持选中状态; queue/running状态可见;草稿和下一回合控件保持可编辑 | 草稿为空时停止;有内容时发送并排队下一条提示;无重播控制 |
| Plan / Goal / 提案被拒绝、过期或中断后的计划 | 合约芯片仍然可见且可编辑 | 稍后发送提示;提交新合同;没有执行动作 |
| 没有工作空间 | 文本区域处于活动状态,警告横幅“无项目 - 工具有限” | 发送已启用 |
11.5 互动
- Enter:开启「回车发送」时发送消息;关闭后插入换行
- 发送会在主机往返之前清空输入框(D287):草稿在按下 Enter 的那一帧就离开 文本区域,因此主机变慢时发送不会看起来被忽略,第二次 Enter 也不会把同一 提示重复排队。若 store 拒绝发送,草稿(文本和文件引用)会回到输入框且 光标位于末尾,除非用户已经输入了新内容,此时以新内容为准;用户已离开的 会话被拒绝的发送会恢复到该会话的草稿槽位。发送读取文本区域的实时值。 因模型未配置或粘贴仍在保存而被拒绝的发送会弹出提示,而不是毫无反应。
- 已发送的提示在同一帧就出现在转录中(D288):渲染进程以自己生成的 id 插入 用户行,主机以同一 id 回显持久行并原位替换。文件引用在回显带来会话作用域 ref 之前按源路径显示;斜杠提示在回显带来展开正文和命令芯片之前显示输入原文。 从未到达主机的发送会再把该行撤回。
- Shift+Enter:文本区域中的换行符。关闭「回车发送」后,Cmd/Ctrl+Enter 发送。IME 组合输入和打开的自动补全菜单仍优先于发送。
- 占位提示:首次渲染的上下文从欢迎语开始;页面/会话上下文不变时,即使编辑、 清空草稿、聚焦、失焦或进行 IME 组合也保持不变。切换首页/会话视图或活动会话 时才推进到下一条本地化命令/文件或快捷键提示,并使用透明度渐变;没有计时器, 发送或清空草稿不会改变提示。
- 转义:当文本区域聚焦时,清除输入或模糊(不中止)
- 运行中的发送:草稿有内容时清空当前草稿,并向当前会话的、由 Host 拥有的队列追加 一条 FIFO 条目;当前运行到达
agent_end之后才会作为普通提示发送。草稿为空时, 同一个提交槽位显示停止,因此清空草稿即可显示立即停止操作。切换会话不会影响其他 会话的队列。 - 排队行:内容,然后依次是上移、下移、立即发送、编辑、删除。上移/下移与该行相邻的 等待条目互换,并镜像 Host 持久的
position;在等待区块边界处是空操作,绝不会跨入 优先区块。编辑会移除该行,并把捕获的草稿——文本加内联文件引用芯片——回填到输入框; 输入框非空(或带有附件)时该操作被拒绝并提示,且不改变任何内容。删除立即移除该行。 - 立即发送:把该行提升到会话优先区块的末尾,因此第二次点击排在上一次之后,而不是 顶掉它。随后请求
agent/stop,当前回复/工具批次正常结束后释放优先区块,并先于 所有等待条目启动。第一条优先行启动回合,其余作为相邻的用户消息加入同一回合,因此整块 只被回复一次。会话空闲时立即启动。 - 入队尚未完成的行在 Host 返回持久化 id 前保持锁定:上移/下移、立即发送、编辑和删除 均禁用,五个操作的提示均说明正在保存,立即发送按钮同时显示本地化的“正在保存”。 直接调用编辑/删除也不改变该行或草稿; 入队确认后恢复普通等待行的操作。
- 被提升的行会被锁定:上移/下移、编辑和删除均被禁用,但仍保留 tooltip 与
aria-disabled状态,立即发送按钮显示为已决定(chat.sendNowPending)。该行使用 不同的提升外观,避免被误认为普通等待行。 - 停止:停止正在运行的轮次并取消挂起的权限。在任何之前 辅助文本、思维或工具行开始时,它也会删除刚刚发送的 用户行并恢复预序列化输入框草稿。普通文本 返回到文本区域,文件引用作为叶名片返回;他们的 规范路径永远不会成为文本区域文本。回复开始后,Abort 保持 部分抄本,不恢复草稿。
turn_end不是空闲信号。发送和主机持久性仍然被阻止 通过后续的工具转动和阻止自动检查点生成 直到agent_end或error;草稿和运行时选择器保持可编辑状态 并保留最新的下一回合选择。仅手动检查点变得空闲 在其匹配的compaction_end上。- 自动增长:文本区域测量包裹的视线,从一条可见线开始 行,扩展到七行,然后在内部滚动;删除内容 将其缩小回一行
- 尺寸写入是幂等的(D264):高度未变化时不做任何 DOM 写入,因此
--composer-dock-height不会被重新发布,在一行之内输入时也不会使 文档样式失效。height: auto测量探针仅在文本框可能需要收缩时读取。 - 草稿文本和文件引用芯片按会话保留在渲染器内存中(D301)。缓存位于模块级而非 某一个 Composer 实例内,因此卸载再挂载——空首页 ↔ 会话停靠、聊天 ↔ 设置/插件/ 其他页面、或窗口隐藏再显示——会恢复同一槽位。切换会话会保存来源草稿并恢复目标 草稿;未缓存的目标和每个新创建的会话从空开始。无活动会话的首页输入框有自己的 槽位。成功发送只清除提交该请求的会话槽位(包括请求进行中发生导航时),删除 会话会丢掉其槽位。若窗口在后台时 contenteditable DOM 被清空,下一次聚焦或可见性 恢复会把缓存值重新绘制回去。
- 文本校正关闭(D145):输入框文本区域设置
spellCheck={false},autoCorrect="off"和autoCapitalize="off"所以 browser/OS 的拼写和 自动更正从不重写编码提示 - 运行时芯片使下降器完全可见(D150):模型 × 推理、许可和 Composer 中的模式触发器使用紧凑的行高,而不是
leading-none溢出。Composer 模型标签仍会对长 ID 使用省略号。 - 模式、provider/model、思维和权限更改更新活动 空闲时立即会话。在回合期间,渲染器应用 乐观地将最新选择作为下一轮选择并仅坚持下去 在
agent_end或error之后;主机永远不会改变正在运行的回合的固定 配置。乐观的模型或思考档位固定不得保留为未固定或上一模型 计算的能力字段。只要会话快照没有可用的思考菜单 (supportsReasoning: false、缺省或空的等级列表),Composer 就回落到 所选模型的目录/绑定等级,因此回合中改档不会把子菜单塌缩成只剩关闭思考。有效的待处理 Plan 或 Goal 批准仍会禁用这些功能 控制。等待批准期间的批准操作是例外情况。的 Composer-左Agent/Plan/Goal芯片是唯一模式 单击时控制并循环 Agent → Plan → Goal → Agent;Composer 右侧的 模型 × 推理芯片拥有这两项选择。Palette 和 Composer 斜线模式命令使用 相同的活动会话配置路径;主机确认解决后 审批,审批面被移除而不是保留为终端 行动卡。 - 在项目或会话导航期间,主页输入框可能会暂时没有
activeSessionId。其空闲模式、思考和权限触发器保持不变 已启用;第一个配置操作创建或重用目标 草稿,然后保留所选模式、思维级别或权限模式。 正在运行的回合或等待批准仍然会限制这些控制。 - 继承默认模型支持推理的新会话以该模型存储的默认思考等级启用, 并钳位到已启用档;绑定没有默认值时才回落到最高已启用档。非推理 模型和缺失的功能元数据从
off开始;重新打开或重复使用 现有会话保留其持久选择。 - 模型菜单仅列出已启用、可运行且已配置模型绑定的提供商。缓存或实时发现的 结果可以为这些已配置模型补充信息,但未配置的发现结果不会出现在对话区列表中; 发现不可用时仍显示已配置的模型 ID。
- 打开组合菜单时,会在进入“模型”子菜单前开始加载模型。首个可见行使用缓存元数据或 已配置绑定;实时发现会在后台更新列表,不会把已配置别名替换成 wire ID 或第二个可见名称。
- 组合的模型 × 推理菜单在
bottom: calc(100% + 8px)以role="menu"打开。根层正好两条role="menuitem"条目;当菜单列出一个以上等级时,推理等级条目正下方有一条每个等级一个刻度的拖动滑杆(D458)。滑杆显示一条轨道,每个刻度在轨道上有一个刻度点、轨道下方各有一个标签;每个标签都保持可见并在列内省略。刻度点行和标签行都是满宽 n 列网格,range 输入满宽覆盖在轨道上,两侧各内缩半列减去滑块半径,因此对任意档位数,刻度点、滑块和标签都落在同一列中心。选中档的刻度点和标签用 accent token,其余用 muted token;滑块盖住选中档的刻度点。刻度标签可点但不是 Tab 停靠点,range 输入才是可访问控件。模型子菜单有搜索输入和粘性提供商标题;推理等级子菜单以Current model <model> supports these reasoning levels开头,先列出omit再列出绑定已启用档位的经典单选行。omit作为会话思考等级持久化,且不发送提供商思考覆盖(ADR 0295)。行使用role="menuitemradio"、aria-checked、当前行样式和末尾勾选。从列表选择具体模型或等级会持久化完整会话配置、清除模型过滤并返回根层且不关闭菜单;滑杆和刻度提交最后一次待提交的档位并留在原地。关闭再打开总是从根层开始。 - 未知的 Custom/OpenAI-compatible 模型可以启用显式推理 从模型菜单覆盖。提供商刷新,会话选择 支持的级别最接近
medium,并出现工具栏触发器;已知的 非推理模型仍然不可用,而不是被覆盖。 - 切换提供商保留可用级别,否则使用最近的级别 支撑位(先向上,然后向下);非推理提供商
off仍然存在。 - Plan 权限芯片在模式选择器旁边仍然可见。它显示 有效的询问/接受编辑/自动姿势。在 Plan 和 Goal 中,其帮助文本 表示 Bash 在“询问”或“接受”编辑下得到确认,并且可能会在没有修改的情况下发生变异 自动下确认;这并不意味着 Write/Edit/plugin 工具是 可用。
- Goal 共享 Plan 批准表面 (D198)。操作栏从以下位置读取其文案 提案的
kind,因此目标合约显示匹配的批准标签和 神器开启器,而布局和记住权限拆分按钮保持不变 相同。该栏位于透明 Composer 停靠栏上,因此使用--ds-bg-composer加--ds-shadow-composer,而不是正文流里的--ds-tile薄洗。
11.6 辅助功能
role="textbox"与aria-label="Message input"- 可编辑文本控件永远不会启用浏览器拼写检查或自动更正(D145)
- 发送按钮:
aria-label="Send message" - 停止按钮:
aria-label="Stop generating" - 禁用发送:
aria-disabled="true"带工具提示说明 - 思维水平在本地思维组内使用单选菜单语义; 所选级别公开
aria-checked="true"
11.7 MVP 约束
- 下列规则先选择剪贴板表示:仅当所有文件都是无原生路径的
image/*副本时,非空白text/plain正文优先,以保持 Word 文字可编辑。真实文件、 任意非图片文件、纯图片及空白文字加图片的粘贴仍作为附件。选中的正文应用 相同的大文本阈值(ADR 0059)。 - 粘贴一个或多个操作系统剪贴板文件或图像将其字节保存到 原始会话的临时目录并添加一个紧凑的叶名称 文本区域上方的引用。配置的
largePasteThreshold以内的纯文本粘贴仍保持浏览器 原生文本区域行为;超过阈值的粘贴会保存为会话临时pasted/目录中的 UTF-8 文件,并在粘贴位置插入内联临时文件标记(D197、D209、D262、ADR 0059、ADR 0070、 ADR 0131) - Main 把图片字节存在
attachments/<sha256>,仅当所选 models.dev 模型接受 图片且不超过 10 MB 内联上限时发送视觉输入;否则追加安全的@path回退 (D361)。草稿仍是文本;芯片在发送前序列化为规范@<absolute-path>, 代理用文件工具跟随路径。 - 没有语音输入
11.8 斜线命令、@ 文件引用和剪贴板文件(D123–D125、D197、D209、D262、D362、ADR 0024、ADR 0059、ADR 0070、ADR 0131)
输入框拥有一个内联自动完成菜单——一个组件服务两个组件 模式。焦点永远不会离开文本区域 (D125)。
解剖学:
┌──────────────────────────────────────────────┐
│ group label (sticky) │
│ ▸ item title argument-hint descr. │ ← kb-active row
│ ▸ item title descr. │
│ … │
│ ↑↓ select · Enter confirm · Esc close │ ← hint bar (footer)
└──────────────────────────────────────────────┘
[ file.ext × ] [ another-file.ts × ] ← when references exist
[ composer textarea ]- 锚定在输入上方,跨越整个输入框宽度;同样升高 表面配方作为模型菜单(不透明的升高背景,对话框 阴影、细微发际线、
--radius-lg);最大高度帽,带内部 滚动并跟随scrollIntoView(nearest)键盘。 - 斜线模式(
/在位置 0 处输入,光标位于第一个标记内,无 还没有空格):占位提示会引导用户“输入 / 使用命令 · @ 引用文件”,并按顺序 分组 — 提示模板(名称 +argument-hint鬼文 + 说明,之前的项目源码 用户全局)、应用程序命令(内置斜杠别名)、插件命令。保留核心别名/new、/compact、/agent-mode、/plan-mode和/goal-mode; 匹配的字符以重音突出显示。 - 命令描述使用斜杠名称及可选标题、参数提示之后的剩余空间。在窄输入框中, 长描述也应优先省略,不能把短命令名称挤成省略号。名称或提示自身超过行宽时 仍可省略;命令行和文件行都不得溢出菜单。
- 文件模式(光标处的
@标记,边界之前):行持续显示 仅叶文件或目录名称;目录有一个尾随的/和 接受后继续完成。完整的相对路径仍然可用 通过行工具提示和可访问的名称。接受已完成的文件 (Enter、Tab 或点击)会把@标记替换为光标处的行内叶名芯片 —— 与粘贴文件相同的哨兵芯片 —— 其规范值为原始entry.path; 菜单关闭且这次 Enter 不会发送。接受目录会将文字路径保留在草稿中, 以便完成可以继续。条目来自fs/index(D124、D209、D362)。一个 当索引被限制时出现截断脚注;没有工作空间 菜单显示“打开项目”空状态。 - 接受命令和目录插入文本(
/name/@dir/); 接受完成的文件会插入行内芯片,而不是删除触发符。立即 在调度之前,引用在可见之后以稳定的顺序序列化 使用 D124 的引用将草稿作为完整的@path文本。生成的大段粘贴标记会在原位置 解析为规范临时路径且只解析一次;不会再作为结构化附件或追加的 basename 发送。 剪贴板文件/图像引用仍作为结构化附件传递。仅供参考的草稿是可发送的。 Builtin/plugin 调度仍然绕过模型就绪门当没有发送提示文本或文件参考时。 - Agent/Plan/Goal 模式别名可以在同一草稿中为提示添加前缀:
/agent-mode <prompt>、/plan-mode <prompt>和/goal-mode <prompt>适用 首先发送模式,然后发送<prompt>以及任何序列化引用 正常的提示路径,以便用户轮流保留在记录中。 仅别名模式命令仍保留在本地。输入框只有在之后才被清除 接受当地行动或迅速派遣;失败的调度保留 完整的可见草稿和重试参考。 - 接受的提示调度保留仅渲染器,session/turn-scoped 结构化撤消快照,而回合仍未得到答复。智能停止 按原始参考顺序恢复该快照而不是复制 序列化的消息路径返回到文本区域。回复开始后停止 不得恢复或复制已提交的草稿。
- 按 §11.7 选择表示后,文件粘贴需要至少一个
File。 渲染器传输有界文件字节、名称和 MIME 元数据到 Electron main 以及持久会话 ID。主要验证 会话,在下面写入唯一的清理文件<data_dir>/scratch/<sessionId>/pasted/,并返回每个 UUID 支持的 绝对路径及其经过净化的原始叶名称。输入框展示 叶名称,将路径保持在会话范围内的瞬态引用状态, 并仅在以下情况下使用与文件菜单相同的@引用序列化该路径 发送。移除芯片不会删除暂存字节。超过largePasteThreshold的纯文本粘贴会 通过相同的有界会话桥传输生成的text/plainUTF-8 字节,在原始选择处插入 由哨兵支持的pasted-text-*.txt芯片,并把规范路径映射保留在渲染器草稿中。 点击芯片或按 Enter/Space 会读取有界文本文件,在当前位置以可编辑文本替换 哨兵、移除引用并将插入符号放在内容末尾;读取失败或不支持时保持芯片不变。 默认阈值为 600 个字符并持久化在应用设置中。主页输入框在保存之前创建或重用 持久会话。暂存生命周期使用会话删除粘贴的文件,并且不会弄脏工作区 git 树。 - 参考芯片在提示区域内环绕,暴露规范路径 他们的工具提示和可访问的名称,并提供焦点可见的本地化 删除恢复文本区域焦点的按钮。保留重复的叶子标签 分开是因为身份和调度使用规范路径,而不是名称。
text/plain和.txt芯片还可通过键盘聚焦;点击或按 Enter/Space 会将有界内容展开为可编辑草稿 文本。二进制、图像、过大或读取失败时保留芯片。 未发送图片在输入框外上方单独一行靠左显示缩略图,不占据可编辑正文。 全选、编辑或撤销正文不会删除图片;切换会话保留图片,旧草稿里的图片标记恢复为 独立附件。只有图片的草稿也能点击发送;发送立即失败时仍恢复文字与附件。 附件区限制高度并可纵向滚动,窄聊天面板内也能查看和删除每张图片。 悬停或聚焦时显示独立删除按钮,触屏始终显示。 点击或按 Enter/Space 打开图片浮层,不发送草稿,不改变工作面板选项卡。 图片在深色遮罩内水平、垂直居中,保持比例并适应可用区域,小图默认不放大。 支持缩放、适应窗口、下载原图及同一草稿内的多图切换;切图重置缩放与位置。按住图片拖动或滚动可在任意缩放下平移, 指针移出图片后仍能连续拖动;松开或取消即结束拖动,不关闭浮层。图片保留一部分 在可见区域内,适应窗口会恢复居中。 按 Esc、点击关闭或空白处退出并恢复输入焦点与光标。焦点限制在浮层内, 打开期间隐藏原生插件视图。删除按钮不触发预览或发送。 缩略图与预览沿用有界、受限的fs/readImageDataUrl接口,无项目时也可读取允许 访问的暂存图片。图片丢失、格式不支持、过大或解码失败时显示重试并保留草稿。 切换会话或项目、删除正在查看的附件时关闭预览;晚到的读取结果不得覆盖新图片。 - 发送的模板调用在记录中呈现为等宽命令 来自消息的
command字段的芯片而不是扩展的正文。 - 已发送的
@path文件引用(带引号或不带引号)画成与草稿相同的叶子名芯片。点击芯片先经pi-desktop/fs/resolveRef补全引用——搜索整个打开的项目,按项目组文件夹顺序、主文件夹优先(ADR 0263)——再按解析结果打开:项目文件在随应用打包的pi.file-manager视图中打开(该视图不可用时退回宿主file:选项卡),会话临时目录或附件文件在宿主file:选项卡中打开,主文件夹中的.html/.htm在侧边浏览器打开。交给该视图的地址跟随应答的文件夹:主文件夹中的文件用项目内相对路径传递,同一项目的同级文件夹中的文件用绝对路径传递,与会话临时目录和附件文件一致。什么都没匹配到时既不打开任何东西,也会自己报告出来;系统默认应用不再由这次点击触发。HTTP(S) URL 仍是侧边浏览器的文本链接。 - 状态:键盘活动行使用共享
kb-active处理;空的 查询列出所有内容(斜杠)/最近索引的顺序(文件);零 匹配呈现本地化的空行并且菜单计为关闭 按键处理。
12. Composer 中的模型选择
12.1 目的
模型选择属于 §11 中 Composer 的模型 × 推理组合菜单;不存在独立的顶栏模型选择器。
12.2 解剖学
[✨ model-name · reasoning level ▾]12.3 状态
| 状态 | 外观 |
|---|---|
| 已配置 | Composer 显示当前模型和推理级别,可点击 |
| 无提供商 | Composer 菜单中显示静音模型文本和设置入口 |
| 跑步 | 保持可用,作为下一回合配置 |
| 下拉菜单打开 | 模型和推理条目在同一个菜单中打开子菜单 |
12.4 交互
- 单击:打开包含模型和推理条目的 Composer 菜单
- 缓存的提供商模型在重启后首次打开时可用;打开菜单会在进入“模型”子菜单前开始加载, 后台刷新更新列表而不先清除它
- 选择:切换当前会话的模型
- 键盘:下拉菜单中的 up/down 箭头,Enter 进行选择,Escape 关闭
- 较长的模型名称可以在紧凑菜单中省略;每项只显示一个名称,没有显示名时回退到模型 ID。 配置绑定中的非空别名会在首帧和刷新后的行中保持生效。
12.5 辅助功能
- Composer 模型 × 推理芯片暴露
aria-haspopup="menu"和aria-expanded;当前值通过aria-label公布 - 模型和推理行:
role="menuitemradio"和aria-checked
12.6 MVP 约束
- 无模型 favorites/pinning
- 没有从选择器创建自定义模型(使用设置)
- 下拉列表仅显示已启用提供商中配置的模型绑定;提供商发现只用于补充这些行, 不会额外暴露对话区模型。
13. ProjectPicker
13.1 目的
顶部栏中的控件显示当前工作区。允许打开或清除项目文件夹。
13.2 解剖学
[folder icon] /path/to/project or "No project" [open button]13.3 状态
| 状态 | 外观 |
|---|---|
| 活跃项目 | 显示文件夹名称,可点击路径 |
| 没有项目 | “无项目”静音文本+“打开文件夹”链接 |
| 开幕 | 已禁用,“正在打开...”微调器 |
13.4 交互
- 单击路径:打开系统文件对话框以选择文件夹
- “打开文件夹”:相同的操作,明确的按钮
- “清除项目”:明确的清除按钮
13.5 辅助功能
- 当前项目:
aria-label="Current project: /path/to/project" - “无项目”:
aria-label="No project open" - 打开按钮:
aria-label="Open project folder"
13.6 MVP 约束
- 项目选择可以激活保留的选项卡或添加新的本地项目 选项卡;主机仍然公开一个选定的工作区
- 除了路径显示之外没有项目状态指示器
14. StatusBar
14.1 目的
可选的底部栏显示运行时状态指示器。 从 MVP 推迟 — 在 IA 中提到,但在 M1–M3 中未实现。
14.2 MVP 约束
- 未在 MVP 中实现
- 状态指示器 (running/error/idle) 显示在顶栏中
- 未来:实施时单独的规范
15. 空状态
15.1 目的
当关键数据缺失时,指导就会浮出水面。必须始终提供操作链接,而不仅仅是一条消息。
15.2 状态
| 背景 | 留言 | 行动 |
|---|---|---|
| 没有会话 | “开始你们的第一次对话” | “新任务”按钮→焦点编辑器 |
| 无提供商 | “未配置模型提供商” | “添加提供商”链接 → 设置 → Agent → 提供商 |
| 无项目(Agent、Plan 或 Goal) | “没有打开项目 - 工作区工具不可用” | “打开文件夹”按钮 → ProjectPicker |
| 会话为空(第一条消息) | 上下文占位提示(chat.placeholder、命令/文件提示或快捷键提示) | N/A |
| 首页为空(第一条消息) | 上下文占位提示(chat.placeholderHome、首页命令/文件提示或快捷键提示) | N/A |
15.3 布局
- 聊天主页为空:单个可滚动堆栈(英雄→可选清单)居中 在 MainChat 中,有一个保留在底部的输入框兄弟;任务录入开始 直接在该输入框中,无需入门卡或快速操作层
- 其他空表面:text-xl 标题 + text-sm 描述 + 主要操作
- 标题上方适用的图标(48px Lucide/品牌标记)
- 背景:bg-primary(透明,不是卡片)
15.4 辅助功能
- 操作按钮可通过键盘聚焦
- 图标上的
aria-label提供上下文描述
15.5 MVP 约束
- 没有动画空状态插图
- 无产品介绍叠加(根据 05-onboarding.md §6)
16. 命令面板表面
16.1 目的
**状态:合并到全局搜索界面中。**命令面板覆盖已删除;其命令列表(内置+插件命令)现在呈现为 SearchDialog 内的“命令”部分(使用 Cmd/Ctrl+K 或 Cmd/Ctrl+Shift+P 打开)。在 04-builtin-commands.md 中定义,并由全局搜索组件规范显示。
16.2 解剖学
+----------------------------------------------+
| [搜索输入] |
| ─────────────────────────── |
| 结果列表(可滚动) |
| 类别:会话 |
| ▸ 新任务 |
| ▸ 删除当前会话 |
| 类别:模式 |
| ▸ 切换到 Plan |
| ▸ 切换到 Goal |
| ▸ 切换到 Agent |
| 类别: 回合 |
| ▸ 中止主动回合 |
| ... |
+----------------------------------------------+16.3 布局
- 位置:居中叠加,最大宽度 480 像素,最大高度 360 像素
- 背景:bg-elevated-opaque(高架浮动表面,与
.dialog/.search-dialog一致)、radius-lg-plus、shadow-dialog - Z 索引:
z-command-palette(60) - 背景:半透明背景主色(0.5 不透明度)
16.4 交互
- 搜索:按标题和关键字过滤命令
- 键盘:箭头 up/down 导航,输入执行,退出关闭
- 单击:执行命令
16.5 辅助功能
- 独立的调色板覆盖不再存在;命令是全局搜索对话框的一部分(
role="dialog"、aria-label和nav.search)。 - “命令”部分使用与其他搜索结果组相同的
role="listbox"/role="option"语义。 - 搜索输入自动聚焦于打开;箭头 up/down 导航,Enter 执行,Escape 关闭。输入法组词期间,搜索框的 Escape(包括从输入框冒泡的事件)不得关闭搜索弹窗。
16.6 MVP 约束
- 无子命令嵌套(平面列表)
- 无命令 history/recents
- 插件命令与内置命令一起显示
17. Toast
17.1 目的
针对没有内联表面(后台事件、跨页面确认)的已完成操作和失败的瞬时、非阻塞反馈。一个全局堆栈 — 绝不是每页 Toast 标记。
17.2 解剖学
┌ toast-viewport (fixed top-center, z-toast) ┐
│ ┌──────────────────────────────────────┐ │
newest, at anchor → │ │ (✓) Provider saved ✕ │ │
│ ├──────────────────────────────────────┤ │
oldest, pushed down →│ │ (i) Message text ✕ │ │
│ └──────────────────────────────────────┘ │
└───────────────────────────────────────────┘ToastHost(在components/Toast.tsx中)渲染堆栈;在App.tsx中的每个 shell 分支安装一次- 每张卡片:16px 变体图标(语义色调)·消息·X 关闭按钮
- Surface:
bg-elevated-opaque+ 1pxborder-subtle+shadow-dialog,radius-md-plus — 与菜单相同的浮动系列; 07-ui-design-system.md §11.8 中的指标
17.3 API
状态位于应用商店中 (useAppStore):
showToast(message: string, options?: {
variant?: "info" | "success" | "warning" | "error"; // default "info"
duration?: number; // ms; default 4000 (error 8000); 0 = sticky
});
dismissToast(id: number); // ToastHost internal / tests17.4 使用规则
| 规则 | 详情 |
|---|---|
| 变体语义 | success = 用户操作已完成(保存、创建、加载)。 error = 操作失败(每个 catch 路径)。 warning = degraded/at-risk 状态自行解决。 info = 中性通知(上下文回显,“尚不可用”)。 |
错误始终显示为 error | showToast(e instanceof Error ? e.message : String(e), { variant: "error" }) — 绝不是默认变体 |
| 没有来电计时器 | 自动关闭是toast系统拥有的;调用者不得 setTimeout-clear |
| 国际化 | 消息来自 i18n 目录(D073);原始 host/provider 错误字符串不变地传递 |
| 不用于阻塞流量 | 工具决策使用内联 PermissionCard,而不是 toast |
| 不适用于内联验证 | 字段级错误呈现在字段旁边;消息绑定提供程序故障在记录中呈现为辅助错误消息 |
| 主机推送的 toast | Plugin/main-process Toast 通过 api.onToast 到达并渲染为 info |
17.5 行为
- 自动关闭 4 秒(错误 8 秒,
duration: 0粘性);悬停卡片会暂停其计时器,离开悬停后从剩余时间继续 - 堆叠上限为 4 — 最旧的掉落优先;重新引发相同的消息+变体会重新启动现有的 toast,而不是堆叠一个双胞胎
- 最新的 toast 进入顶部中心锚点(向下滑动 200 毫秒缓出),将旧卡向下推;退出是 150 毫秒的渐入淡出
- 关闭 X 始终可用;每张卡都是明确的非拖动指针目标 因此,悬停暂停和关闭保持交互,其中顶部中心堆栈 重叠无框标题栏拖动镶边
- 减少运动使动画持续时间接近于零,因此移除(必然会
animationend) 仍然会触发
17.6 辅助功能
- 视口为
aria-live="polite";aria-live="polite"/success卡为role="status",role="status"/warning为role="alert" - 标有
toast.dismiss目录键的关闭按钮 - 图标为
aria-hidden;这种变体是通过所宣布的角色来传达的,而不仅仅是颜色
17.7 MVP 约束
- Toast 内没有操作按钮(后 MVP;使用内联错误横幅来显示可操作的错误)
- 无 progress/loading toasts — 运行状态属于工作指示器
- 没有Toast历史表面
18. 导入目的地
18.1 目的
扫描受支持的本地代理存储,找出这台机器能够移交的四样内容——会话、 提供商/模型配置、技能和 MCP 服务器——然后审查候选项、选中它们并 发起一次显式导入。
18.2 解剖学
每个类型一个工作台,位于分段式类型切换器之后;每个类型拥有自己的 扫描、选择和导入操作。
[ Sessions | Models | Skills | MCP ] ← 类型切换器
[ ] Found 12 · 6 selected Group by: Source ▾ [Scan] [Import selected (6)]
───────────────────────────────────────────────────────────────────────────
CLAUDE CODE ~/code/pi 4
[ ] Refactor the importer 12 messages · Jan 5, 2026 [Claude Code]- 切换器复用侧边栏和设置导航栏已经发布的标签(
nav.sessions、settings.nav.models、settings.nav.skills、settings.nav.mcp), 因此该页面不新增自己的目录条目。 - 每个类型都带自己的工具栏:全选复选框连同“found”文案和已选数量、 该类型自己的选项(会话分组、技能导入模式)、重新扫描,以及 Import selected。
- 在某个类型首次扫描之前,其面板会显示一个安静的下一步操作状态:扫描会读取 什么,以及 Scan 操作。切换选项卡绝不会启动扫描(D007 / D342)。
- 分组标题是安静的标签行——来源或项目名称、以等宽字体显示的解析路径, 以及一个计数胶囊——而不是着色条带;它们下方的候选项是独立磁贴。
- 分组控件支持项目路径和来源。来源是默认值。在项目路径模式下, 精确路径在分组标题中保持可见,没有项目路径的会话出现在最后的 无项目分组中。
- 导入来源名称、分组与模式控件、计数、结果和可访问名称都来自共享的 i18n 目录。候选项日期使用当前应用区域设置。
18.3 状态和交互
- 一次成功的扫描会替换先前的候选集合、清空选择,并让每个分组保持展开: 找到的候选项就是扫描的答案,所以它们不会被藏在第二次点击之后。
- 每个类型各自扫描:会话扫描绝不会启动模型配置、技能或 MCP 扫描, 切换选项卡会保留离开的那个类型的结果和选择(非活动面板保持挂载并隐藏)。
- 一次成功的导入会为每个不同的非空项目路径创建或重复使用一个持久的项目索引条目 并刷新 sessions/projects。
- 当一次成功的核心或插件导入在已归档项目下新增一个绑定项目的会话时, 渲染器会在刷新之后恢复该项目的展示状态,使该项目和导入的会话在默认 侧边栏中可见。这只适用于新增的绑定会话;普通刷新、无路径会话和被跳过的 导入都会保留归档状态。
- 无路径导入不会创建项目条目并保留在临时 会话下。导入永远不会创建物理文件系统目录。
- 重新导入现有来源会话会跳过它,而不会重复其项目条目。
- 更改分组模式会保留候选选择,并让每个新形成的分组保持展开。
- 展开或折叠一个分组不会影响其他分组。
- 分组和全局复选框支持选中、未选中和不确定 选择状态;全局复选框将部分选择报告为不确定。
- 每个分组内的候选项以及分组本身都按最新优先排序; 无路径分组在项目路径模式下保持在最后。
18.4 辅助功能
- 类型切换器是一个由
tab控件组成的tablist,每个控件都带有aria-selected和命名其面板的aria-controls。每个面板都是一个由其选项卡 标记的tabpanel;非活动面板是hidden,而不是被视觉覆盖。 - 每个公开按钮都会公开
aria-expanded并引用其主体aria-controls。 - 全局和分组复选框具有本地化的可访问名称,并携带不确定状态。
- 分组和导入模式选择器是共享的应用内菜单选择器,具有可见标签并可通过键盘操作, 绝不是平台绘制的
<select>。 - 分组计数胶囊以其标题携带本地化的计数文案。
- 项目行公开按钮和操作菜单按钮公开本地化、 项目特定的可访问名称。
18.5 ModelConfigImportPanel
扫描同样那些本地 agent 存储中的提供商与模型设置,按来源分组查看候选项, 选中它们,然后发起一次显式导入。
[ ] Found 3 · 1 selected [Scan] [Import selected (1)]
───────────────────────────────────────────────────────────────────────────
CLAUDE CODE 1
[ ] acme-gateway 4 models · api.acme.dev [API key]- 该类型独立于会话导入:它有自己的扫描、选择和 Import selected 操作。 会话扫描绝不会启动模型配置扫描,两者在切换器之后一次只显示一个。
- 按来源分组是唯一的分组方式。一次成功的扫描会替换先前的候选集合、清空 选择,并让所有分组保持展开。
- 每一行显示提供商名称、模型数量、主机、“有 API key / 无 API key”徽章, 以及来源。原始密钥绝不会到达渲染器。
- 导入会为每个选中的候选项创建一条
providers.create记录。若已存在 base URL 归一化结果、API 风格和凭据都相同的提供商,则跳过;同一端点的 不同凭据创建独立记录。仅支持 OAuth 的 来源账户不会出现在扫描结果中。CC Switch 是第五个来源 (~/.cc-switch/cc-switch.db);与某个 CC Switch 端点和凭据都匹配的实时 工具文件不会被列出两次,不同凭据仍会保留。 - 若一次成功创建之后
settings.defaultProviderId仍为空,则第一个新建的 提供商成为全局默认。
19. ProviderStudio(设置 → Agent)
19.1 目的
用于添加 OpenAI 兼容提供程序的现代模型配置界面, 审查准备情况,并在没有密集的情况下管理 connection/default 行为 表格转储。模型参数仍归 pi-ai 所有。
19.2 解剖学
- 英雄摘要 — 踢球者、标题、简短描述、提供商计数/就绪计数/默认对的统计数据
- 默认卡 — 默认提供商/模型选择器;全局运行模式、命令 Shell 和回车发送位于设置的全局 AI 目的地
- 提供商标题 — 部分标题 + 主要添加提供商切换
- Composer — 包含连接字段的对话框(名称、基础 URL、模型 id、API 样式、API 键);没有推理、思维水平、上下文、输出、温度或兼容性控制
- 提供商卡 — 头像缩写、徽章(默认/秘密状态)、主持人+模型、测试/设为默认/删除
19.3 状态
| 状态 | 介绍 |
|---|---|
| 空 | 英雄显示零/无默认值;输入框打开;带有主要添加 CTA 的空面板 |
| 人口稠密 | 卡片列出了每个提供商;添加流程打开模式对话框 |
| 账户编辑器 | 账户标签加上共享的模型选择器(D270):账户的模型、单模型上限和思考级别都可以在此编辑,可用时建议来自已认证账户的权益,仍然接受自由填写的 ID,并且选择器的固定层不改变对话框布局、也不会被对话框溢出裁切 |
| 默认提供商 | 卡片获得微妙的口音清洗 + 默认徽章;设为默认隐藏 |
| 秘密失踪 | 警告标志“没有 API 键”;测试可能会失败关闭 |
| 忙排 | 该卡已禁用 Test/update/delete 操作 |
19.4 交互
- 添加提供商打开模式对话框,对话框留在 overlay 内(可从 1040px 首选宽度收缩)。聚焦的凭据输入框须完整显示 2px 强调环,滚动区域为此留出间隙而不是裁切。Cancel/close 重置字段并关闭对话框
- 左侧模型列表标题旁的全选复选框一次勾选或取消当前可见行;搜索过滤时只作用于匹配行,过滤外已选模型保持不变。可见行全部选中时为勾选,全部未选时为空,部分选中时为不确定态。
- 同一标题旁的紧凑「获取列表」会立刻向服务重新探测。没有可探测端点、正在探测或保存时不可用;空闲但端点已有效(防抖等待)时仍可点,以便跳过该窗口。实时结果到来前保留当前行。
- 右侧「模型设置」标题旁有独立搜索框,随输入过滤已配置的模型:按模型 id、别名与目录显示名做不区分大小写的子串匹配,因此友好名称也能找到它代表的 id。标题旁的计数仍报告全部已配置模型;没有任何匹配时显示独立的空提示,而不是「尚未选择模型」。新增模型(勾选、全选或手动添加)只在当前过滤仍能显示它时保留过滤词;最后一个已配置模型被移除时会清空搜索框,因此新行不会出现在视野之外,也不会有过滤词滞留在用户已无法清除的输入框里。
- 模型 id 和名称在全局禁止选择的外壳内仍可选择文本。带有选区的点击 不会切换行复选框,因此拖选复制和点击切换可以并存(ADR 0192)。
- 别名只是展示标签:非空别名用于 Composer 芯片和选择器命名,配置行和 转录本徽章仍显示真实 id。清空字段后恢复目录发布名。
- 保存创建提供商,存储秘密,成功后将其设置为默认值,并刷新列表
- 测试连接调用
providers.testConnection并祝酒 success/failure - 思考预设更新通过具有 D102 语义的
providers.update持续存在 - 仅对
defaultProviderId/defaultModelId进行默认更新
19.5 辅助功能
- 分段控件公开
aria-pressed - 发现列表标题旁的全选复选框有本地化无障碍名称(全选 / 取消全选),部分可见行被选中时为不确定态
- 输入发送使用
role="switch"+aria-checked - 卡片操作保留可见的文本标签; Thinking select 有一个易于理解的名称
- 两个模型列表搜索框都有本地化无障碍名称;已选列表的搜索框在保存中或尚未配置任何模型时禁用
- 空白和英雄区域暴露本地化标签
19.6 MVP 约束
- 仅在输入框中兼容 OpenAI 的路径(供应商市场推迟)
- 保存后不会重新显示原始秘密
- 还没有目录浏览器;自定义模型 ID 保持一流
20. NotificationInbox(D117)
20.1 目的
公开任务完成和失败事件的有界的、主机拥有的历史记录 用户在当前聚焦的聊天中尚未看到,无需转动 短暂的祝酒进入历史。收件箱仅限本地且跨应用程序持久 重新启动。
20.2 解剖学
Sidebar footer Popover (360px max)
[Bell (12)] -> [Notifications] [All | Unread] [Mark all read] [Clear]
------------------------------------------------------------
[unread dot] [check] Task completed 2m
Session title
------------------------------------------------------------
[x] Task failed 9m
Session title · ERROR_CODE- 触发器:扩展侧边栏右侧的 32px Lucide
Bell图标按钮 页脚,取代了以前的帮助快捷方式。主标题栏没有 重复。紧凑的徽章呈现1–99和99+;其可访问标签 保留准确的计数(耐用存储的上限为 200)。 - 弹出窗口:宽度
min(360px, calc(100vw - 24px)),在上方和右侧打开 页脚的高度,不高于可用窗口,内部有一个窗口 可滚动的行列表。 - 标题:本地化标题、
All/Unread分段过滤器、LucideCheckCheck标记为全部已读按钮,LucideTrash2清除按钮。仅图标 操作带有本地化的工具提示和可访问的名称。 - 行:未读点、语义 completion/failure 图标、本地化事件标签、 快照会话标题、可选的稳定故障代码和本地化 相对时间。行是以 2px 间距堆叠的密集
--radius-sm色块(D297),不是卡片,也不再由细线分隔。 - 显示 title/body 在渲染时从
kind、sessionTitle派生, 和可选的errorCode;没有保留本地化的 title/body 字符串。
20.3 状态
| 状态 | 行为 |
|---|---|
| 没有未读 | 贝尔没有徽章;标记所有已读已禁用 |
| 未读 | 徽章显示计数;未读行带有点和更强的标签权重 |
| 全部为空 | 居中紧凑的“无通知”空状态;列出已禁用的操作 |
| 未读为空 | “你们都陷入困境了”;所有过滤器仍然可用 |
| Loading/refresh | 保留当前行并过滤;禁用突变直到刷新解决 |
| 突变失败 | 保留现有列表并宣布错误 toast;不要乐观地丢失行 |
20.4 互动
- 贝尔切换弹出窗口。打开不会隐式标记任何已读内容。
All显示最新保留的行;Unread过滤至readAt == null。- 选择一行首先调用
notification.markRead,关闭弹出窗口,然后 激活该行的持久会话(包括其项目(如果适用)) 并将记录滚动到最新内容。 - 将所有读取标记为幂等并保留行。清除会删除每个收件箱 行,但绝不会删除会话、转录本或回合。
notification.changed更新可见列表和徽章。打开 弹出窗口还刷新 host-core 中的有界列表。一个 来自 Electron 的notification.activated事件遵循同一会话 激活路径为一行单击。- Completion/failure 进入持久收件箱,除非主窗口处于关闭状态 visible/focused,确切的结束会话是当前聊天。一个 集中后台会话仍会进入收件箱,而没有本机横幅; 未聚焦的当前会话进入收件箱并收到本机横幅。 单击横幅 restores/shows 并在之前聚焦主窗口 为匹配会话发出
notification.activated。 - 中止的回合、交互式权限/ask/Plan 询问、预定提醒和插件通知不会进入此收件箱。 交互询问可以在应用聚焦于其他会话时使用按来源区分的本机表面。
20.5 辅助功能
- Popover 是一个带标签的、非模态的
role="dialog";行集合是 语义列表,每一行都是一个带有完整本地化名称的按钮。 - 打开聚焦第一个未读行,否则聚焦第一行,否则聚焦
All过滤器。ArrowUp/ArrowDown、Home和End在行间移动;Enter/Space激活聚焦行。 Tab通过过滤器、标头操作和没有 a 的行遵循 DOM 顺序 焦点陷阱。Escape或外部按下可关闭弹出窗口;逃脱恢复 将注意力集中到铃声上。- 徽章变更通过一个礼貌状态区域使用确切的 未读计数。 Completion/failure 含义使用图标、文本和可访问性 名字,从来不只是颜色。
- 本机通知可访问性和激活语义使用该平台 API;渲染器不会重新创建本机横幅。
20.6 限制
- 该列表仅包含生成的
task.completed和task.failed记录 从看不见的终端代理轮流。可见当前结果和aborted轮次 是故意沉默的。 - 全局最多保留 200 个最新行。没有分页、预定通知源、持久权限通知源、首选项 页面、通知权限提示或云同步。交互询问横幅是收件箱之外的临时本机表面。
21. 验收标准(所有组件)
- 所有组件均使用 07-ui-design-system.md 中的语义颜色标记 — 无原始十六进制 2.所有交互元素都有可见的聚焦环(2px强调,偏移2px)
- 布局 shell 指标(46px 标题栏行、~275/48 侧边栏、280 上下文、 具有 1-7 行草稿增长的紧凑型输入框)匹配规范
- 聊天消息最大宽度限制为 720px
- ToolCallCard 根据 01-ui-ia.md §5 显示状态、参数预览、结果预览、持续时间
- PermissionCard 显示工具名称、风险、参数、倒计时和每个 03-permission-ux.md 的三个操作按钮
- Composer:开启回车发送时 Enter 发送;关闭后 Cmd/Ctrl+Enter 发送、Enter 换行; Shift+Enter 始终换行。草稿从一增长到七条可见线后滚动;单一提交槽位在有内容时或空闲/空草稿时显示发送,仅在运行中且草稿为空时显示停止
- Composer 模型 × 推理芯片显示 provider/model 对;流期间仍可作为下一回合 配置;未配置时指向设置的链接
- 命令调色板在 z-index 60 处打开,捕获焦点,支持键盘导航
- 空状态总是提供可操作的下一步,而不仅仅是一条消息
- 所有组件都有正确的 ARIA 角色和标签
- 响应式折叠在 800px 和 640px 断点处工作
- Toast 堆叠在顶部中心,带有变体图标 + 关闭、自动关闭 4s/8s、悬停时暂停,并根据 §17 通过 01-ui-ia.md/03-permission-ux.md 进行宣布
- 会话导入默认为源分组,提供项目路径分组,在 scan/group 更改后折叠所有组,并根据 §18 公开可访问的组公开状态
- 导入的项目路径在持久项目索引中只出现一次;无路径导入保留临时会话并且不会创建文件系统目录
- ProviderStudio显示英雄总结+添加对话框+提供商卡;秘密永远不会变得原始; test/default/delete 保持键盘可访问 17.NotificationInbox 公开 All/Unread 视图、确切的未读徽章语义、 行激活、标记所有已读和清除操作;它是键盘可操作的 并且从不将可见当前或中止的回合视为通知 18.原生边缘通过回流调整MainChat大小,而不压缩固定工作面板; 面板可见性和分隔线提交更新提交的首选宽度, 取消的分隔符手势恢复之前的宽度 (ADR 0033)
- 扩展侧边栏会话标题、project/group 标题和空状态文案 使用 13px 紧凑令牌,同时主要侧边栏操作保持在 14px
Provider ordering
Each AI service card can be dragged from its non-interactive surface. After a small movement threshold, the card follows the pointer and surrounding cards animate into the proposed slot. Dragging near the list edge scrolls it. Releasing saves the previewed order; Escape, pointer cancellation, focus loss, unmount or catalog changes cancel the drag. Buttons and form controls retain their actions. There is no separate drag handle. A focused card accepts Up/Down to move one visible row. Saving blocks further moves; a failed save shows an error and restores the accepted order. Late catalog responses cannot restore an earlier order. The default-model picker and Composer model groups follow the persisted order. Sorting changes neither the selected default nor provider configuration. OAuth accounts remain in their separate section.
输入框文件芯片前的原生删除
删除内联文件引用前面的文字时,不得插入空行或把芯片挤到下一行。保留原生编辑与撤销/重做。原生删除前记录浏览器的目标范围和已有 BR 节点;输入之后,仅当去掉那颗新出现的 BR 后草稿恰好等于这次删除的目标时才移除它。绝不裁掉前导换行,也不整批规范化所有 BR。用弱引用记住已证实的占位节点,避免原生重做把它们恢复回来。显式换行、输入法合成、文件引用和芯片删除保持原有行为。由输入框安装并卸载这些原生事件监听。
弹窗长文本边界
扩展提示保留 420px 宽度。标题文字列可以在关闭按钮旁收缩,来源路径在列内 完整换行显示,连续的长标题、插件名、确认消息和选项允许换行。内容高于视口时 在弹窗内滚动,保证操作按钮可达;输入、选择、提交和关闭语义不变。
项目删除说明和插件弹窗标题、安装结果中的长名称也在现有宽度内换行。 项目指令、项目记忆和 OAuth 弹窗维持已有的边界处理。