The BUG-930 pin spacer sat on the last assistant row, pushing its sibling .message-actions (and follow-ups / delivery card) below half a screen of empty space. Move the same min-height onto the last turn's entry (`:last-child:has(.message-assistant)`) with align-content: start so the rectification grid wrap does not stretch its rows. Hook logic unchanged. Records BUG-1043 / BUG-1044 (pre-existing, investigating) found during browser verification. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017eEAG8HD3mm8gsKXgk8uU8
93 lines
8.9 KiB
Markdown
93 lines
8.9 KiB
Markdown
# PROGRESS · 最后一轮回答与操作按钮之间的大段空白(2026-09-26)
|
||
|
||
任务书:`TASK-latest-turn-actions-gap-20260926.md`。执行方:Claude 子代理(直接执行模式)。分支 `codex/latest-turn-actions-gap-20260926`,worktree `.worktrees/latest-turn-actions-gap-20260926`,本地提交未推送。
|
||
|
||
## 基线
|
||
|
||
- `origin/staging` = `a57c4310`。
|
||
- 基线全量前端测试(Node 20.19.2):3960 条,pass 3872 / fail 61(均为环境缺口:Node 20 `mock.module` 的 `ERR_MODULE_NOT_FOUND @/…`、无 Docker)。
|
||
- 基线 rootMainFiles gzip 130933 B。
|
||
- BUG 编号:开工时 `docs/BUG_HISTORY.md` 最大 BUG-1041,`docs/tasks/` 无更大号 → 本单 BUG-1042;验证中另查出的两个既有问题记为 BUG-1043、BUG-1044(investigating,未修)。
|
||
|
||
## DOM 核对(D2)
|
||
|
||
| 面 | 最后一轮的 `.message-list > :last-child` | 内容 | 用户行 |
|
||
| --- | --- | --- | --- |
|
||
| 普通咨询 | `div.message-entry`(`chat-transcript.tsx` `MessageEntry`) | `article.message-assistant` → `.message-actions` → `.conversation-follow-ups` | 单独一个 `.message-entry`,不在末轮外层里 |
|
||
| 生时校正 | `div.rectification-message-wrap.rectification-message-entry`(`display: grid; gap: var(--space-1)`) | `article.message-assistant` → `.message-actions` → 交付卡 / 只读区间(若有) | 单独一个 wrap,不在末轮外层里 |
|
||
|
||
两处外层都**从助手行开始**、都不含用户行,所以 `--latest-turn-head-height` 的口径原样可用:头是用户行时扣用户行高度;头就是这条助手行(BUG-931)时写 `0px`,外层最小高度 = 整个视口,正好把它留在顶部。hook 不需要改逻辑。
|
||
|
||
末轮外层不是助手外层的情况(校正面的 persisted-question / pending-note / error 是最后一个子节点、普通咨询 `error-message`):旧规则 `:last-child .message-assistant` 也匹配不到,新规则 `:last-child:has(.message-assistant)` 同样不匹配,行为不变。
|
||
|
||
## 改动
|
||
|
||
| 文件 | 内容 |
|
||
| --- | --- |
|
||
| `frontend/src/app/globals.css` | `.conversation .message-list > :last-child .message-assistant { min-height: … }` → `.conversation .message-list > :last-child:has(.message-assistant) { min-height: …; align-content: start; }`。计算式、变量不变。`align-content: start` 是必要的:生时校正外层是 grid,给了最小高度后 `align-content: normal` 会把多余高度分摊给各行,操作行又会离开正文。 |
|
||
| `frontend/src/hooks/use-conversation-scroll-anchor.ts` | 只改 `turnHeadHeightForSpacer` 的注释(留白承载者从助手行变为末轮外层,BUG-931 写 0 的理由随之更新)。逻辑未动。 |
|
||
| `frontend/tests/chat-notice-and-scroll-contract.test.ts` | 见下「改动既有断言」;新增 1 条。 |
|
||
| `frontend/DESIGN.md` | Answer start anchor · Spacer 条目改写。 |
|
||
| 记录 | `docs/BUG_HISTORY.md`(BUG-1042 / 1043 / 1044)、`CHANGELOG.md`、`docs/testing/latest-turn-actions-gap-20260926.md` + 4 张截图、`docs/tasks/README.md`。 |
|
||
|
||
`:has()` 兼容性:Safari 15.4+ / Chrome 105+ / Firefox 121+,低于本站语法下限 Safari 16.4;`globals.css` 已有多处 `:has()`。
|
||
|
||
### 改动既有断言(三栏也写在测试文件内)
|
||
|
||
| 测试 | 原值 | 新值 | 原因 |
|
||
| --- | --- | --- | --- |
|
||
| 「turn spacer uses conversation viewport variables and is not sticky」 | 选择器 `.conversation .message-list > :last-child .message-assistant` 带该 `min-height` 计算式 | 选择器 `.conversation .message-list > :last-child:has(.message-assistant)` 带同一计算式,并断言 `align-content: start` | BUG-1042:留白从助手行移到整轮外层;计算式与变量口径不变,`sticky` 否定断言保留,变量写入 / 清除的行为断言原样保留 |
|
||
|
||
新增「the turn spacer trails the actions row instead of sitting between text and actions (BUG-1042)」:任何 `.message-assistant { … }` 规则里不得出现 `--conversation-viewport`;普通咨询 `MessageEntry` 里 `ChatMessageRow` → `ChatMessageActions` → `ConversationFollowUps` 顺序同在 `.message-entry`;校正面 `ChatMessageRow` 在 `ChatMessageActions` 之前且 `RectificationMessageEntry` 直接挂在 `.rectification-message-wrap` 里。
|
||
|
||
BUG-930 / 931 / 932 的其余断言(`resolveTurnHead`、头即尾写 0、闲置不写留白、`pinLatestTurn` 源码断言、`lastTurnTail` 假 DOM)一条未改。
|
||
|
||
## 测试
|
||
|
||
| 项 | 结果 |
|
||
| --- | --- |
|
||
| `tsc --noEmit` | 0 错 |
|
||
| `npm run lint` | 0 error(126 warning,均为既有) |
|
||
| 全量 `npm test` | 3961 条,pass 3873 / fail 61 |
|
||
| 失败名单 vs 基线 | 逐条一致,新增失败 0、修好 0 |
|
||
| 测试名单 vs 基线 | +1 新名(上面新增那条),消失 0 |
|
||
| Python 源码合同 | `test_session_management_entrypoints.py`、`test_daily_and_rectification_entrypoints.py`、`test_birth_time_journey_contract.py` 全过;`test_frontend_theme_contract.py::test_lightweight_warm_palette_contract` 失败,**改动前同样失败**(`--color-ink-secondary` 色值不符,与本单无关,未动) |
|
||
| `npm run build -- --webpack` | 通过;`/` ○ Static |
|
||
| gzip | rootMainFiles 130933 → 130933 B(0,本单只改 CSS);`index.html` 引用的全部 js/css 合计 656748 B |
|
||
|
||
## 真实浏览器验证
|
||
|
||
环境:本地 `next build --webpack` + `next start`,无头 Chrome 151(`--headless=new --disable-dev-shm-usage`),CDP `Fetch` 拦截 `/api/*` 回虚构数据(5 轮虚构历史撑出一屏以上;普通咨询 `/api/consult` 回纯文本,校正 `/api/rectification/agent` 回 ndjson `answer.delta` + `run.completed`)。「改动前」= 同一构建里注入旧规则(新规则 `min-height: 0 !important` + 旧选择器原样),因为本单只改了这一条 CSS。输入框用 CDP `Input.insertText` + Enter 真发问,钉顶由真实 `pinLatestTurn` 完成。
|
||
|
||
「正文末行 → 操作行」= `.message-actions` 顶边 − 末轮回答文本节点最后一行的底边。「开头距顶」= 本轮用户行顶边 − 滚动容器顶边。
|
||
|
||
| 面 | 宽度 | 改动前 间距 | 改动后 间距 | 改动后 开头距顶 | 改动后 外层高度 / 助手行高度 |
|
||
| --- | --- | --- | --- | --- | --- |
|
||
| 普通咨询 短回答 | 375 | 526px(操作行在视口外) | **23px** | 10px | 589 / 86 |
|
||
| 普通咨询 短回答 | 1280 | 608px(操作行在视口外) | **23px** | 10px | 671 / 86 |
|
||
| 生时校正 短回答 | 375 | 452px(且开头被顶出视口,见下) | **23px** | 10px | 495 / 66 |
|
||
| 生时校正 短回答 | 1280 | 554px(同上) | **23px** | 10px | 597 / 66 |
|
||
|
||
23px 即操作行自身间距:同一页面里历史轮(非末轮,从未带留白)的同一量测得也是 23px(普通咨询、生时校正各测一次)。
|
||
|
||
长回答(14 段):
|
||
|
||
- 普通咨询 375 / 1280:开头距顶 10 / 9px,「跳到最新」出现;点击后落底(`distanceFromBottom` 0)、按钮消失、操作行可见;随后往末轮插入 400px 内容,仍保持落底(跟随正常)。
|
||
- 生时校正 375 / 1280:本轮开头**没有**停在顶部,回答一到就落底——改动前完全相同(开头位置同为 −1323 / −482px)。定性为既有问题 BUG-1044,见下。
|
||
|
||
截图(入库,375 宽):`docs/testing/latest-turn-actions-gap-20260926/consult-375-{before,after}.png`、`rectification-375-{before,after}.png`。其余(1280、长回答、跳到最新后)在执行方 scratchpad `gap/`,未入库。校正截图底部的「合盘历史暂时无法读取」提示是虚构数据没回合盘接口所致,与本单无关。
|
||
|
||
附带改善:改动前校正面短回答结算时,操作行追加在已撑满视口的助手行之后,内容变高触发贴底跟随,把本轮开头顶出视口(上表「改动前」开头距顶 −118px);改动后操作行落在外层的最小高度之内,内容总高不变,开头留在顶部。
|
||
|
||
## 验证中查出的既有问题(未修,前后表现一致)
|
||
|
||
1. **BUG-1043**:刷新或经 `?c=` / 启动落点直接进入已有消息的普通咨询会话时,`useConversationScrollAnchor` 的 scroll 监听与 ResizeObserver 一次都没挂上(给 `addEventListener` / `ResizeObserver.observe` 打点实测为 0),结果短回答也显示「跳到最新」、滚动不更新、点击后不跟随。先 `/?new=1` 再点侧栏进入同一会话则正常(本单的普通咨询测量都走这条路径)。推测:加载闸门期间容器未挂载、effect 早退,揭幕后依赖不变不重跑。
|
||
2. **BUG-1044**:生时校正钉顶后静止时 `distanceFromBottom` = 94 ≤ 阈值 96,`measure()` 当成「回到底部」解除 `holdUnpin`、`anchored` 翻真,长回答随后贴底。普通咨询同一时刻约 198,不触发。
|
||
|
||
两条都在 hook 里,任务书红线禁止改 hook,只记为 investigating,建议另开单。
|
||
|
||
## 未做 / 环境缺口
|
||
|
||
- 登录态 iPhone Safari 真机:留给 `docs/testing/latest-turn-actions-gap-20260926.md`。
|
||
- 未部署、未推送。
|