From 3134f2d4301b120ad20bc7581f25a74d923f3ee6 Mon Sep 17 00:00:00 2001 From: Yif <877193178@qq.com> Date: Tue, 28 Jul 2026 12:36:29 +0800 Subject: [PATCH] doc: agent note for the web conversation polish sweep --- ...28-web-conversation-polish-sweep.i18n.yaml | 6 +++ ...026-07-28-web-conversation-polish-sweep.md | 37 +++++++++++++++++++ ...-07-28-web-conversation-polish-sweep.zh.md | 37 +++++++++++++++++++ 3 files changed, 80 insertions(+) create mode 100644 .agents/notes/implemented/bug-fix/2026-07-28-web-conversation-polish-sweep.i18n.yaml create mode 100644 .agents/notes/implemented/bug-fix/2026-07-28-web-conversation-polish-sweep.md create mode 100644 .agents/notes/implemented/bug-fix/2026-07-28-web-conversation-polish-sweep.zh.md diff --git a/.agents/notes/implemented/bug-fix/2026-07-28-web-conversation-polish-sweep.i18n.yaml b/.agents/notes/implemented/bug-fix/2026-07-28-web-conversation-polish-sweep.i18n.yaml new file mode 100644 index 0000000000..3df46dedf2 --- /dev/null +++ b/.agents/notes/implemented/bug-fix/2026-07-28-web-conversation-polish-sweep.i18n.yaml @@ -0,0 +1,6 @@ +# Bilingual-pair consistency record (docs/i18n/README.md): the git blob hash of each +# side as of the last confirmed-consistent state. Both languages carry equal authority; +# after editing either side, bring the other along and re-record with: +# pnpm run verify-translation-pairing --write .agents/notes/implemented/bug-fix/2026-07-28-web-conversation-polish-sweep.md +2026-07-28-web-conversation-polish-sweep.md: cae52217d66017509c025a5d8d37b1e1e8173c6a +2026-07-28-web-conversation-polish-sweep.zh.md: d19a5f75937e9ae9f553b2c941594db118fa434e diff --git a/.agents/notes/implemented/bug-fix/2026-07-28-web-conversation-polish-sweep.md b/.agents/notes/implemented/bug-fix/2026-07-28-web-conversation-polish-sweep.md new file mode 100644 index 0000000000..cae52217d6 --- /dev/null +++ b/.agents/notes/implemented/bug-fix/2026-07-28-web-conversation-polish-sweep.md @@ -0,0 +1,37 @@ +# Agent Note: Web conversation UI polish sweep + +Status: implemented + +English | [中文](2026-07-28-web-conversation-polish-sweep.zh.md) + +## Problem + +A design review of the web GUI's conversation surfaces found a batch of presentation defects: portal menus painted one frame at the wrong position before repositioning (visible open jump), the chat column split one tool run into several groups whenever a step message carried only tool-call heads, tool row summaries printed workspace-absolute paths that consumed most of the row, the running-row sweep was implemented as an alpha mask that dimmed the whole row, the hero workspace chip resurrected a deleted workspace's folder name from the session cwd, and the header showed a turns counter nobody asked for next to a 13px title. + +## Decision + +The sweep lands as presentation-layer changes only; nothing enters the session log. + +- **Portal menus pre-render hidden and measure before paint.** The menu list mounts with `visibility: hidden` at (0,0), measures in `useLayoutEffect`, and becomes visible already at its final position. Menus keep 12px viewport clearance with internal scroll; workspace create actions pin in a non-scrolling footer. +- **The chat flow skips assistant nodes that render nothing.** A finalized assistant node whose blocks are only tool-call heads and blank text/reasoning is dropped from the flow derivation, so consecutive tool results merge into one group. Interrupted nodes always render (they carry the 已停止 marker). +- **Tool row summaries relativize workspace-rooted paths.** The session cwd threads through the toolview slot contract (`ToolRowOwnerProps.cwd`) and `toolRowModel` strips it from summaries that start with it; paths outside the workspace stay verbatim. Display-only — args and the log are untouched. +- **The running sweep is a glare-band overlay.** A fixed-width `::after` gradient band animates across the row (the deepsuite ShimmerText pattern), replacing the previous `mask-image` approach, in both ToolRow and the Bash toolview. +- **The hero workspace chip is a selector, not an echo.** With no live selection (cold start, or the workspace was deleted after the list settled) it shows a "Choose workspace" placeholder; the cwd-derived name only bridges the initial list load, and stale pending picks clear when their workspace leaves a ready list. +- **One 16px vertical rhythm.** The chat column gap and in-group tool-row gap are both 16px, replacing the 10px in-group gap plus a negative cross-group margin. +- **Header title reads 14/20 with no turns counter**; StateDot ongoing and the turn tail use a stepped pixel-chase loading language; `body` gets grayscale antialiasing (`-webkit-font-smoothing` and the Firefox macOS equivalent). + +## Alternatives considered + +- **Position menus synchronously from anchor rects before mount.** Rejected: the list's own size is unknown until it lays out, so clamping to the viewport still needs a post-layout measure; measuring a hidden mounted node is the pattern React and Floating UI document. +- **Filter empty assistant messages host-side.** Rejected: the node is real model output that Trajectory and replay must keep; only the chat presentation should skip it, and the web layer is pure presentation by contract. +- **Relativize paths in each tool's presenter.** Rejected: the redundancy is shared by every path-summarizing tool; one display-only pass in `toolRowModel` covers them all and non-chat consumers keep absolute paths. +- **Keep the mask-based sweep.** Rejected: the mask dims the entire row content including state dots, and its exit transition fought the hover icon crossfade; an overlay band composites above the content without touching its alpha. +- **Keep showing the deleted workspace's name in the chip.** Rejected: the chip is the selector for the *next* session; echoing a cwd whose workspace the user just deleted misrepresents the current pick. + +## Consequences + +Chat renders fewer flow items than the snapshot has nodes: anyone counting rendered blocks against nodes must account for skipped render-nothing assistants (the chat-view spec pins this). The path relativization is a prefix check against the session cwd, so a workspace rename mid-session shows absolute paths until the summary re-derives — accepted as display-only staleness. The uniform 16px rhythm retires the tighter 10px tool-run look; a future denser layout would reintroduce a second constant deliberately. The menu pre-render adds one hidden layout pass per open, negligible at menu sizes. + +## Testing + +`chat-view.spec.tsx` pins the render-nothing grouping (including the interrupted exception); `chat-tool-row.spec.tsx` pins cwd relativization inside/outside the workspace and with an empty cwd; `atoms.spec.tsx` and `workspace-picker.spec.tsx` cover the menu and chip states; the full ui-conversation, ui-primitives, and ui-workspace suites pass. diff --git a/.agents/notes/implemented/bug-fix/2026-07-28-web-conversation-polish-sweep.zh.md b/.agents/notes/implemented/bug-fix/2026-07-28-web-conversation-polish-sweep.zh.md new file mode 100644 index 0000000000..d19a5f7593 --- /dev/null +++ b/.agents/notes/implemented/bug-fix/2026-07-28-web-conversation-polish-sweep.zh.md @@ -0,0 +1,37 @@ +# Agent Note: Web conversation UI polish sweep + +Status: implemented + +[English](2026-07-28-web-conversation-polish-sweep.md) | 中文 + +## Problem + +一次针对 web GUI 对话界面的设计评审发现了一批视觉呈现缺陷:portal 菜单在重新定位前会先在错误位置绘制一帧(打开时可见跳动);只要某条步骤消息只携带工具调用头,聊天列就会把一次工具运行拆成好几组;工具行摘要打印以工作区为根的绝对路径,占掉行内大部分空间;运行中行的扫光效果用 alpha 遮罩实现,把整行都压暗;hero 区的工作区 chip 会从会话 cwd 里复现已删除工作区的文件夹名;标题栏还在 13px 的标题旁显示一个没人需要的轮次计数。 + +## Decision + +本次修复全部为纯展示侧改动落地;不会有任何内容进入会话日志。 + +- **Portal 菜单先隐藏预渲染,绘制前完成测量。**菜单列表以 `visibility: hidden` 挂载在 (0,0),在 `useLayoutEffect` 中测量,显示时已处于最终位置。菜单与视口保持 12px 间距并支持内部滚动;工作区创建操作固定在不滚动的页脚区。 +- **聊天流跳过不渲染任何内容的助手节点。**已定稿的助手节点若其块仅含工具调用头和空白的文本/推理(reasoning)内容,就会从流推导中剔除,于是连续的工具结果合并为一组。被中断的节点始终渲染(它们携带「已停止」标记)。 +- **工具行摘要把以工作区为根的路径转为相对路径。**会话 cwd 经由 toolview 插槽契约(`ToolRowOwnerProps.cwd`)逐层传递,`toolRowModel` 从以其开头的摘要中剥去该前缀;工作区之外的路径保持原样。这只影响显示:工具参数与日志均不受影响。 +- **运行中的扫光效果改为高光带叠加层。**一条固定宽度的 `::after` 渐变光带横向扫过整行(即 deepsuite 的 ShimmerText 模式),取代先前的 `mask-image` 方案,ToolRow 与 Bash toolview 两处均已替换。 +- **hero 区的工作区 chip 是选择器,而非回显。**没有有效选中项时(冷启动,或列表稳定后工作区被删除),它显示「Choose workspace」占位文案;由 cwd 推导的名称只用于衔接列表的首次加载,待定选择对应的工作区从已就绪的列表中消失时,该陈旧选择会被清除。 +- **统一为 16px 的纵向节奏。**聊天列间距与分组内工具行间距统一为 16px,取代原先「分组内 10px 间距加跨分组负外边距」的做法。 +- **标题栏标题改为 14/20,去掉轮次计数**;StateDot 的进行中状态与轮次尾部采用逐格推进的像素追逐式加载视觉语言;`body` 启用灰度抗锯齿(`-webkit-font-smoothing` 及 Firefox 在 macOS 上的等价设置)。 + +## Alternatives considered + +- **挂载前根据锚点矩形同步定位菜单。**不予采纳:列表自身尺寸在布局完成前无从得知,向视口内收拢仍然需要布局后测量;对已挂载的隐藏节点做测量正是 React 与 Floating UI 文档记载的模式。 +- **在宿主侧过滤空的助手消息。**不予采纳:该节点是真实的模型输出,Trajectory 与回放都必须保留它;只有聊天展示应当跳过它,且按契约 web 层只负责呈现。 +- **在每个工具各自的展示器中做路径相对化。**不予采纳:这种冗余是所有输出路径摘要的工具共有的;在 `toolRowModel` 里做一次仅影响显示的处理即可覆盖全部工具,非聊天消费方仍拿到绝对路径。 +- **保留基于遮罩的扫光。**不予采纳:遮罩会把包括状态圆点在内的整行内容压暗,其退出过渡还与悬停图标的交叉淡入淡出相互冲突;叠加光带在内容之上合成,完全不触碰内容的 alpha。 +- **让 chip 继续显示已删除工作区的名称。**不予采纳:chip 是为*下一个*会话服务的选择器;用户刚删掉某个工作区,还回显它的 cwd,就是在错误呈现当前的选择。 + +## Consequences + +聊天渲染出的流条目数少于快照中的节点数:凡是拿渲染出的块与节点数对账的人,都必须把被跳过的「不渲染任何内容」的助手节点计算在内(chat-view 规格测试固定了这一点)。路径相对化只是针对会话 cwd 的前缀检查,因此会话中途重命名工作区后,摘要在重新推导前会显示绝对路径,这被接受为仅影响显示的陈旧状态。统一的 16px 节奏淘汰了原先更紧凑的 10px 工具运行外观;将来若要更紧凑的布局,应当有意识地重新引入第二个常量。菜单预渲染让每次打开多一次隐藏布局计算,在菜单的尺寸量级下开销可忽略。 + +## Testing + +`chat-view.spec.tsx` 固定了「不渲染任何内容」节点的分组行为(含被中断节点这一例外);`chat-tool-row.spec.tsx` 固定了工作区内、工作区外以及 cwd 为空时的 cwd 相对化行为;`atoms.spec.tsx` 与 `workspace-picker.spec.tsx` 覆盖菜单与 chip 的各种状态;ui-conversation、ui-primitives 与 ui-workspace 的全量测试套件通过。