文档:同步 review 修正后的会话滚动契约

- ADR 第 1 条补「一次加载与它所属的世代绑定」,第 2 条补块集合缓存与失效信号
- 决策记录追加两处内部缺陷的症状、修正与验证口径

Co-authored-by: Junie <junie@jetbrains.com>
This commit is contained in:
2026-10-02 19:59:34 +08:00
parent 1caf8ae8b0
commit a589dad003
2 changed files with 4 additions and 0 deletions
@@ -22,6 +22,7 @@ DirectProject 聊天区(`apps/ai-game-creator-shell/src/view/project-developme
- 两个触发都不越过既有的首屏订阅锚点 `lastCompletedItemId`;一次加载仍最多连拉 5 页(口径见 [`【ADR】DirectProject对话历史单一事实源-2026-09-16`](./【ADR】DirectProject对话历史单一事实源-2026-09-16.md))。
- 加载中在列表最上方(比最旧一条回合更靠上)挂载一行 `role="status"`、`aria-live="polite"` 的「正在加载更早的对话」,带旋转圈;延迟 150ms 才显示,加载结束即卸载。它按需挂载,靠位置补偿(见第 2 条)保证下面的消息不跳。
- 失败:挂起自动加载,列表顶部保留一行 `role="alert"` 的「加载更早对话失败 · 重试」,重试是可聚焦按钮;**不自动重试**,只有点重试(或切换项目)才重新开始;重试成功后错误行消失。
- 一次加载与它所属的**世代**绑定:切换项目或新起一次读取都推进世代号(`historyLoadTokenRef`),旧世代落地时整段失效——不合并条目、不写游标、不关加载态。只比项目路径不够:A→B→A 之后在飞的旧读取又落回同一个路径,原守卫放行,会把新一代的加载行与 `historyLoadingRef` 这道并发闸门一起改掉。
### 2. 前插锚定:回合 key + 偏移,关掉浏览器原生锚定
@@ -29,6 +30,7 @@ DirectProject 聊天区(`apps/ai-game-creator-shell/src/view/project-developme
- 合并更早历史前记录锚点,合并后按同一 key/序号把偏移还原,较早内容出现在上方而用户正在读的位置不动。
- 列表上加 `overflow-anchor: none`,关掉浏览器原生 scroll anchoring,保证只有一套补偿在跑(不做「两套补偿交替生效」)。
- 一次加载期间(`historyLoading` 为真)所有补偿——ResizeObserver 回调、加载行挂载、历史合并——都还原同一个冻结锚点;加载结束再刷新锚点。
- 块集合(列表里所有带 `data-turn-key` 的节点)在列表子元素没变时复用同一份缓存,缓存由子元素增删(与 `ResizeObserver` 重订阅共用同一个 `MutationObserver`)失效:否则一次滚动帧里读锚点、还原锚点会各查一遍整份列表,长会话下就是 O(块数) 的滚动抖动。
### 3. 回到底部胶囊