fix(web): drop the branch action from user and steering bubbles

The branch control on user and consumed-steering bubbles could enable
only when a completed turn ended with no node after the message at all,
so readers saw a permanently unavailable control whose tooltip promised
a state it could not reach. An enabled one would mislead anyway: a fork
at a message seq cuts at the containing turn/end and takes the answer
along, the opposite of the branch-to-re-ask reading a control on one's
own bubble suggests.

MessageItem loses its fork props, PendingSteeringBubble loses the
showBranch special case, and messageBranchSeqs narrows to
assistantBranchSeqs: only a completed turn's transcript tail that is
the turn's own content-text assistant may fork. A steered turn keeps
its fork point under the settled answer, because fork is a log-prefix
cut and the steer is model-visible history the child inherits.

Web aria goldens drop the user-bubble disabled-branch row and its
hidden explanation text; the nested-subagent golden also loses the one
enabled user-tail fork handle, a loss the decision note accepts.
This commit is contained in:
creatixchu
2026-08-06 17:46:18 +08:00
parent 6902b51fea
commit 84a6bae1c7
49 changed files with 154 additions and 199 deletions

View File

@@ -2,5 +2,5 @@
# 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/feature/2026-07-29-web-message-icon-actions-and-clock.md
2026-07-29-web-message-icon-actions-and-clock.md: 3b97089cdffe006bbb401c4cf61c1379da7f8828
2026-07-29-web-message-icon-actions-and-clock.zh.md: abb6e200ccea4a227e5db3ac48f0410cb3349526
2026-07-29-web-message-icon-actions-and-clock.md: feced6aeb11d176d6c774242a4d1dae14f6730f8
2026-07-29-web-message-icon-actions-and-clock.zh.md: 5e33182421b423f45c84dbe1a979505f4c31b819

View File

@@ -12,7 +12,7 @@ The web chat user bubble already had copy / branch / edit IconActions but no clo
**User bubbles prepend a date-aware local clock to the existing IconActions row; the last content-text assistant of each turn appends a copy / branch / clock row with `margin-top: 16px`; both seats stay visible whenever mounted and re-format at the next local midnight.**
The assistant seat is narrowed by the [completed-turn decision](../bug-fix/2026-08-05-turn-tail-actions-require-a-completed-turn.md): only a turn with a `turn/end` grants it, so a turn still producing steps hands the row to nothing.
The assistant seat is narrowed by the [completed-turn decision](../bug-fix/2026-08-05-turn-tail-actions-require-a-completed-turn.md): only a turn with a `turn/end` grants it, so a turn still producing steps hands the row to nothing. The user seat's branch control is removed outright by the [user-bubble branch removal](../simplification/2026-08-06-user-bubbles-drop-the-branch-action.md); a user row's IconActions are clock and copy.
Both seats format `node.time` through `formatMessageClock`: same calendar day → `HH:mm`, earlier this year → `M月D日 HH:mm`, other years → `YYYY年M月D日 HH:mm`. `useCalendarDay` is a component-local day tick (timeout to the next local midnight) so memoized rows re-render when the calendar day changes without a new framework hook. `MessageItem` places the label before copy (figma `388:20051`). `ChatView` derives turn-tail seqs via `assistantActionsSeqs` and withholds `time` for mid-turn content; `AssistantMarkdown` places the row after branch (figma `43:32997`) only when `streaming` is false, the event time is known, and the node has non-empty text content. Think-only nodes, mid-turn narration, and the streaming tail omit the row. Copy writes joined text blocks. Both message rows pass their event's `seq` to the same fork callback; [Web session fork actions](2026-07-27-web-session-fork-actions.md) define the real mutation contract. Clipboard write and the clock helpers live in `message-chrome.ts`. The assembled surface is pinned by `apps/web/tests/message-actions.e2e.ts` (cold-seeded history + aria golden); aria normalization collapses every clock shape to `{{clock}}`.

View File

@@ -12,7 +12,7 @@ Web 聊天的用户气泡已有复制、分支、编辑 IconActions,但没有
**用户气泡在既有 IconActions 行的开头添加感知日期的本地时钟;每个轮次中最后一条带 text 内容的 assistant 在正文下追加带 `margin-top: 16px` 的复制、分支、时钟;两边只要挂载就保持可见,并在下一个本地午夜重新格式化。**
assistant 一侧的座位由[已完成轮次决策](../bug-fix/2026-08-05-turn-tail-actions-require-a-completed-turn.md)收紧:只有存在 `turn/end` 的轮次才授予该行,仍在产出步骤的轮次不把该行交给任何节点。
assistant 一侧的座位由[已完成轮次决策](../bug-fix/2026-08-05-turn-tail-actions-require-a-completed-turn.md)收紧:只有存在 `turn/end` 的轮次才授予该行,仍在产出步骤的轮次不把该行交给任何节点。user 一侧的分支控件被 [user 气泡分支移除决策](../simplification/2026-08-06-user-bubbles-drop-the-branch-action.md)直接移除;user 行的 IconActions 只有时钟与复制。
两边都通过 `formatMessageClock` 格式化 `node.time`:同一日历日 → `HH:mm`,同年更早 → `M月D日 HH:mm`,跨年 → `YYYY年M月D日 HH:mm`。`useCalendarDay` 是组件本地的日刻度(定时到下一个本地午夜),因此 memo 行在日历日变化时会重渲染,且不新增框架钩子。`MessageItem` 把标签放在复制之前(figma `388:20051`)。`ChatView` 通过 `assistantActionsSeqs` 推导轮次尾部的 seq,并不为轮次中间的内容传入 `time`;`AssistantMarkdown` 把该行放在分支之后(figma `43:32997`),且仅在 `streaming` 为 false、已知事件时间、且节点含非空 text 内容时渲染。纯 Think 节点、轮次中间的叙述与流式尾部省略该行。复制写入拼接后的 text 块。两种消息行都把自己的事件 `seq` 交给同一个 fork 回调;真实 mutation 契约由 [Web session fork 操作](2026-07-27-web-session-fork-actions.md)定义。剪贴板写入与时钟辅助函数放在 `message-chrome.ts`。组装后的界面由 `apps/web/tests/message-actions.e2e.ts`(冷 seed 历史 + aria golden)钉住;aria 归一化把每种时钟形态折叠为 `{{clock}}`。