Composer 控制栏响应式设计

范围:聊天输入框上方的 environment chips,以及下方的 permission、more、active tools、chat model、execution model、effort 和 context 控件。

两排控制栏分别按自身可用宽度响应。底排沿用既有设置;上排分四级收缩:先全部完整显示;再把长名字截到约 12 个字加省略号,Local chip 只留图标;再收起 git 分支名,只留分支图标和改动 badge;最后隐藏全部文字(状态、surface、项目、目录以及 DAG 的 Fit、缩放百分比和 Legend),改动 badge 缩成左绿右红的小圆点。空间恢复后逐级展开。每一级使用同一段过渡动画,菜单、当前值、键盘操作、tooltip 和 accessible name 保持不变。

当前实现与缺口

.composer-bottom-row 同样不用固定断点:它需要的宽度取决于开了哪些工具开关、模型名多长。use-compact-controls-row.ts 在整行旁边放一份隐藏副本,按内容最大宽度排版,并关掉 flex 收缩和过渡(data-measuring)。它累加副本各组宽度和间距,在绘制前移除副本,超过整行宽度就设置 data-compact。测量从不切换真实的这一行:行里的按钮对所有属性做过渡,刚退出 compact 的行仍会报出压缩后的宽度。尺寸变化、内容或 class 变化、网页字体加载完成时都会重新测量,权限、模型和思考档位的文字随之收成各自的图标。展开回去需要留出 12px 余量。.envChips 不再使用固定断点,因为聊天页和 DAG 页的控件数量不同,同一宽度下需要的空间也不同。它选出第一个能放进自身 clientWidth 的级别,所需宽度取最后一个胶囊的右边界,测量时把靠右胶囊的自动外边距清零(不能用 scrollWidth:那个自动外边距让内容永远铺满整行,收缩后就再也展不开):先关掉过渡(data-measuring)把整行按每一级排一遍,再以动画切到选中的 data-squeeze 级别;第 3 级同时设置 data-compact,隐藏全部 label。回到更宽松的级别需要留出 12px 余量。单页和 split pane 运行完全相同的测量逻辑。

现有组件已经提供四类互异图标:Shield 表示权限,Message 表示 chat model,Terminal 表示 execution model,Biceps 表示 effort。所有触发器已经具备 Popover/Menu、tooltip 或 accessible name,因此不新增图标组件和菜单。effort 触发器点开思考力度选择卡;其标题行、点阵滑轨和"推荐"标注见 effort-picker.zh.html。

组件边界

Composer 按交互职责组织目录:输入编辑归入 input/,提交链路归入 submit/,模式分发归入 modes/,会话设置归入 state/,附件、粘贴、slash 与 controls 沿用各自现有目录。环境栏内部再区分整体布局与单个 chip;ProjectBadge、WorkingDirChips 和 GoalChip 保留在其共享业务模块。DAG HUD 由 DAG 模块负责,通过 EnvironmentRow 的通用尾部内容插槽接入,EnvironmentRow 不包含 DAG 名称、DOM id 或控制逻辑。

目录或文件唯一职责不负责
composer/environment-row/environment-row.tsx组合环境栏子组件并渲染调用方提供的尾部内容。不包含 DAG 名称、portal id、聊天提交或项目状态。
composer/environment-row/use-compact-environment-row.ts测量环境栏实际宽度并维护 compact 属性。不渲染组件、不定义视觉样式。
composer/environment-row/environment-row.module.css环境栏布局、pill surface、四级收缩动画和 DAG slot 样式。不包含输入框、底部 controls 或其他 Composer 模式。
composer/environment-row/chips/connection-status-chip.tsx、web-surface-chip.tsx分别负责连接状态入口和当前网页 surface 入口。不控制环境栏整体响应状态。
composer/input/textarea、键盘事件与历史回调。不发送消息或切换 Composer mode。
composer/attach/、paste/、modes/question/组件实现与其专用 CSS Module 放在同一职责目录。不把组件专用选择器留在根 composer.module.css。
composer/submit/构造并提交聊天消息,维护提交副作用。不渲染输入区。
composer/modes/composer-body.tsx根据派生 mode 选择普通输入、函数表单或问题表单。不拥有输入、附件或会话状态。
composer/state/use-composer-settings.ts读取和更新当前 SessionScope 的 Composer 设置。不提供全局 focused-session fallback。
composer/controls/use-*.ts分别管理模型可用性、工具 profile、无人值守状态及既有控制项。不在 Composer 根组件中直接请求或同步这些状态。
composer/index.tsx传入 session、tools 和 toggle 参数,渲染一个 EnvironmentRow。不持有环境栏 ref、observer 或内部 JSX。
scripts/chat/check-composer-environment-row.mjs只检查环境栏组件边界和响应契约。不累积其他聊天 UI 检查。

参考与选择

参考适用能力采用方式
W3C CSS 包含第 3 级规范@container 根据组件容器尺寸控制后代样式,不依赖 viewport。只保留给上下文圆环的文字和思考档位卡片的宽度;文字收起改为实测,因为这一栏需要的宽度不只取决于它自身的宽度。
WAI 可访问名称隐藏可见文字时,交互控件仍需保留可计算的名称。沿用现有 tooltip、Popover trigger 和标签;视觉隐藏不删除 DOM 语义。
横向滚动保留任意长度的文字,但会让部分设置入口离开当前可见范围。保留为极窄宽度的后备;不作为主要响应方式。
换成两行全部文字可见,但会改变 composer 高度并影响 transcript 底部预留。拒绝;本任务保持单行。

目标显示

◇Ask permissions+
◯Chat model〉_Execution model⌁中◌
◇Ask permissions+
◯Chat model〉_Execution model⌁中◌

控制按钮 surface 层级

上方 environment chips 使用填充与边缘层级,底部 controls 则只有 20px 高。底部 permission、more、active tools、chat model、execution model、effort 与 context 只复用主题级 --chip-bg,形成始终可见的浅色填充;常态 surface 不新增 border、内描边或外部阴影,避免压缩小控件的内部视觉空间。既有 hover 前景色和键盘 focus-visible 指示保持不变。

状态表面保持不变
常态background:var(--chip-bg);computed border 为 0、shadow 为 none20px 高度、5px 圆角、图标与文字颜色
hover / open在 --chip-bg 上混入 8% --text-bright原菜单、tooltip、动画、既有 hover 前景色与 effort 等级色
窄态与常态同一 surface,不因文字视觉隐藏而改变四个核心触发器仍为 20px 图标按钮

Context 统计弹层的数据时序

ContextBreakdownPanel 在关闭时会卸载。若每次挂载都把已有结果清空并进入 loading,用户重复打开同一会话、同一 DAG 分支时会先看到一张只有“Loading…”的卡片,再看到统计内容。网络请求即使很短,这次整卡替换仍然可见。

场景立即显示后台行为
同一会话、同一分支再次打开上一次成功取得的完整统计重新请求并原位更新,不进入整卡 loading
首次打开或缓存不存在既有 loading 空态请求当前统计,成功后写入缓存
切换会话或 DAG 分支只读取新 sessionId + headId 对应的缓存取消旧请求;旧响应不得覆盖当前面板
请求失败且已有缓存继续保留缓存内容本次刷新失败不得用错误页替换可用统计

缓存只驻留当前前端进程,按最近使用顺序保留 32 个会话分支,不写入持久存储,不设置定时刷新,不新增依赖。同一 key 的并发刷新只允许最新请求更新缓存。服务端仍是统计数据的唯一来源;缓存只消除重复打开时的过渡画面。

收缩规则

  1. environment chips 的 label 不参与 flex 收缩;被截短的名字以省略号结尾,不出现半截文字。
  2. 内容溢出时整行逐级收紧直到放得下:截短名字、收起分支名、隐藏文字并显示改动圆点;空间恢复后逐级展开。
  3. 各图标触发器保持尺寸、原顺序和操作能力。

交互与无障碍

  • 点击图标打开原菜单。
  • chat 与 execution 使用不同图标。
  • effort 图标颜色继续反映当前级别。
  • focus、tooltip 和 accessible name 不随文字隐藏而消失。

边界

  • 不改模型、权限和 effort 状态。
  • 使用原生 ResizeObserver 与 MutationObserver,不新增依赖。
  • 不换行、不隐藏整个控件。
  • 不调整 composer 高度和 splitRatio。

实施 brief 与验收

上排实现集中在 apps/web/components/chat/composer/environment-row/:组件、测量 hook、样式和两个专用 chip 按职责分文件。composer/index.tsx 只渲染 EnvironmentRow。Context 缓存批次限定为 apps/web/components/chat/context-badge.tsx、context-breakdown-panel.tsx 和 apps/web/lib/chat/context-breakdown-cache.ts。独立检查由 apps/web/scripts/chat/check-composer-environment-row.mjs 提供,并纳入 npm --prefix apps/web run check:chat-ui。这些批次不修改 Popover、Menu、provider、session store 或提交逻辑。

  1. public-entry 检查必须证明 environment chips 由实际溢出逐级切换 data-squeeze,最后一级为 data-compact,不存在固定宽度特例或 label 的 flex-shrink:1。
  2. 状态、surface、项目、目录、Fit、缩放百分比和 Legend 必须共用同一动画规则;不得使用 display:none 或只收起其中一部分。
  3. 默认安装版在 split pane 中把 divider 向左拖到触发阈值后,右侧四个设置入口和左侧 permission/more 均可见、无重叠。
  4. 分别点击 permission、chat model、execution model 和 effort 图标,原菜单或面板正常展开。
  5. 把 pane 拉宽到完整内容可容纳后,所有文字一起恢复;重新选择 split、切换聊天/DAG 和刷新不改变该行为。
  6. 底部所有可见控制按钮在未 hover 时均有非透明的 --chip-bg surface,computed border 为 0 且 shadow 为 none;hover 或菜单展开时背景必须比常态更明显。键盘 focus-visible 指示不属于常态边框,继续保留。
  7. Context 统计第一次成功加载后,关闭并重新打开同一会话、同一分支必须立即复用缓存;后台刷新完成后更新缓存和圆环统计。
  8. Context 缓存必须按会话与分支隔离;快速切换时取消过期请求,失败刷新不得覆盖已有缓存。

实现状态

项目状态
设计与任务边界完成
公开入口 RED / GREEN完成:静态入口先因缺少窄态 effort 图标失败,实施后通过;真实 App E2E 覆盖 700px 与 360px 两种容器宽度。
规格审查 / 质量审查完成:规格审查与修复后的独立质量审查均通过。
完整检查与默认安装版可见验收完成:Web 全量检查、TypeScript、构建、desktop webtab/transfer、单元测试和默认安装版 CDP 验收均通过。
底部控制按钮常态 surface完成:常态仅使用 --chip-bg 填充,不增加边框或内描边;真实 App computed style 验证 border 为 0、shadow 为 none,hover/open 仅增强背景。
Composer 职责目录完成:输入、提交、mode、session state、附件和环境 chips 分别归入职责目录;输入、粘贴、图片附件与问题面板使用同目录 CSS Module。EnvironmentRow 只接受通用尾部内容,不包含 DAG id 或控制逻辑。
Context 统计重复打开已实现:缓存按会话与分支隔离;重复打开立即显示上次结果并后台刷新。check:chat-ui 可执行验证缓存隔离、迟到请求取消、同 key 反序响应、刷新失败保留、32 项容量淘汰,并约束面板按会话与分支 key 同步重建。

可见验收使用实际 /Applications/OpenProgram.app、默认 18100 页面和 9223 CDP。测试操作真实 permission、more、active tool、chat model、execution model、effort 与 context 触发器,确认常态无边框、hover/open 背景增强、窄态无重叠且菜单可打开,并在结束时关闭菜单、恢复焦点与原始页面样式。