Skip to content

fix(chat): 对话区接近底部时正文突然上跳:去掉回贴区吸附并修正末端锚定口径 - #812

Open
AlphaCatMeow wants to merge 4 commits into
Stack-Cairn:mainfrom
AlphaCatMeow:fix/transcript-bottom-snap-jump
Open

AlphaCatMeow wants to merge 4 commits into
Stack-Cairn:mainfrom
AlphaCatMeow:fix/transcript-bottom-snap-jump

Conversation

@AlphaCatMeow

@AlphaCatMeow AlphaCatMeow commented Sep 14, 2026

Copy link
Copy Markdown
Contributor

Linked issue

Closes #811

Summary

WebUI 对话区在接近底部时正文会突然上跳,触发询问用户卡片或工具审批请求时更明显。三处原因,本 PR 逐一处理:

  1. 共享滚动跟随引擎默认的 192px 回贴区:滚轮下行进入该区间的那一 tick 直接把视口钉到底(复现页实测单 tick 视觉位移 231px)。WebUI 主转写区与桌面端 ChatTranscript 都改为 reattachZonePx: 0,只在真正到达底部时恢复跟随;到达底部后向下滚轮、手势落底、指针在底部释放三条路径仍会重新贴底。
  2. virtual-corewasAtEnd 用列表局部距离判"在末端",不含转写 shell 的 18px 顶部 padding 与 194px 底部占位,于是距真实 clamp 约 220px 内都算"在末端",脱离跟随的读者会被任何底部行增高拖走(询问卡片到达 305px、审批请求 63px、流式增高 302px)。改为按 DOM 的 getDistanceFromEnd() 判定,与 followOnAppend 已用的口径一致;两端共用该 fork,桌面端同样受益。
  3. WebUI 工作台背景 Pane 手写的贴底逻辑(距底 48px 即视为跟随、行数一变就写 scrollTop)换成共享引擎,与主 Pane 和桌面端各 Pane 一致。

Change scope

  • Modules: agent-gateway/web、agent-gui、agent-ui、virtual-core
  • Key paths:
    • crates/agent-gateway/web/src/app/GatewayApp.tsx
    • crates/agent-gateway/web/src/app/workbench/GatewayConversationPaneHost.tsx
    • crates/agent-gui/src/pages/chat/transcript/ChatTranscript.tsxcrates/agent-ui/src/lib/chat-scroll/scrollFollowCore.ts(注释)
    • crates/virtual-core/src/index.tscrates/virtual-core/types/index.d.ts
    • 测试:crates/virtual-core/test/end-anchor-distance.test.mjs(harness 新增 extraScrollHeight / maxScrollOffset)、crates/agent-gateway/web/test/transcript-scroll-follow-contract.test.mjscrates/agent-gui/test/chat/chat-transcript-scroller.test.mjs(新增 1 例)
    • 复现页:crates/agent-gateway/web/test/browser/scroll-follow-probe.{html,cdp.mjs,eval.mjs}

Screenshots / preview

2026-09-14_22-32-32

复现页测得的视觉位移(px,负值为内容上移;无头 Chromium,关闭平滑滚动,合成对话):

场景 修前 修后
滚轮下行到底 进入 231px 处一步跳到底 每步 100,最后一步 31 到底并恢复跟随
脱离态停在距底 100px,末行增高 470px -302 0
脱离态停在距底 120px,AskUserQuestion 卡片到达 -305 0
脱离态停在距底 120px,工具审批请求到达 -63 0
脱离态停在距底 400px,以上各种变化 0 0
跟随态在底部,卡片/审批到达、审批栏增高 -380 / -63 / -72 不变(贴底本身)

Verification

  • pnpm typecheck:virtual-corepnpm test:virtual-core:27/27 通过(含新增 4 例)
  • crates/agent-gateway/webtsc --noEmit 通过;pnpm test:webui 720/720 通过;改动文件 biome check 通过
  • crates/agent-guitsc --noEmit 通过;chat-transcript-scrollerscroll-follow-coreuse-scroll-follow-native-scrollbarthinking-disclosure 44/44 通过;改动文件 biome check 通过
  • 浏览器复现:web 包 pnpm exec vite --port 5173 + msedge --headless=new --remote-debugging-port=9333node test/browser/scroll-follow-probe.cdp.mjs all|arrive|approve|ask(URL 加 ?zone=0?ask=2?approval=2
  • 人工验收:作者在本机 WebUI 验证,基本符合预期;桌面端开发版在第四个提交前仍可见 192px 回贴区的上跳,该提交后待作者复验
  • 未运行:桌面端 Tauri 打包构建;触屏与 WebKit;check:ui-boundaries 在基线上已有 3 处与本 PR 无关的报错

Depends-On: none
Stack-Root: #812

Pre-submit checklist

  • A requirement issue is linked (or this is a trivial fix that needs no issue, as explained in the summary).
  • Synced with the target branch; no merge conflicts.
  • The change is focused, with no unrelated modifications.
  • No secrets, tokens, or personal data included.
  • Docs are updated for changes affecting user behavior, deployment, or configuration.

AlphaCatMeow and others added 4 commits September 14, 2026 22:26
resizeItem 的 wasAtEnd 此前用 getVirtualDistanceFromEnd()(列表局部坐标)判
"在末端"。宿主在虚拟列表下方还渲染了 18px shell padding 与 194px 底部占位,
列表末端离真实 clamp 约 212px,于是脱离跟随、停在距底 220px 内的读者都被
当成"在末端",任何底部行尺寸变化都经 applyScrollAdjustment 直写 scrollTop:
AskUserQuestion 卡片到达视觉位移 305px,审批请求 63px,末行流式增高 302px。

改用 getDistanceFromEnd()(DOM scrollHeight 口径,与 _willUpdate 里
followOnAppend 的 isAtEnd 一致)并加 scrollElement 判空;删除不再使用的私有
getVirtualDistanceFromEnd,types 同步并为 scrollEndThreshold 补口径说明。

测试:harness 新增 extraScrollHeight / maxScrollOffset 模拟列表下方的非虚拟
高度;end-anchor-distance 4 例覆盖带内不写、带边不写、真实 clamp 仍钉底、
默认 harness 语义不变。

Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
主转写区的 useScrollFollow 沿用默认 192px 回贴区:滚轮下行进入该区间的那一
tick 直接 pin 到底,读者看到正文突然上跳(复现页实测单 tick 231px)。改为
reattachZonePx: 0,只在真正到达底部(8px 容差)时恢复跟随;到达底部后向下
滚轮、手势落底、指针在底部释放仍会重新贴底。

工作台背景 Pane 原本手写一套贴底:距底不足 48px 即视为跟随,行数或 revision
一变就写 scrollTop,读者停在底部附近会被每次流式增量吸回,且与主 Pane、
桌面端各 Pane 的引擎语义不一致。换成同一 useScrollFollow(zone 0,会话切换
stickToBottom),ScrollArea 根与视口经 callback ref 接入,删除三处 scrollTop
写入。

新增 transcript-scroll-follow-contract 源码契约测试锁住两处配置。

Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
scroll-follow-probe.html 挂真实 ScrollArea + GatewayTranscript + useScrollFollow,
拦截 scrollTop/scrollTo 写入并按调用栈归因(follow-engine / virtualizer /
base-ui),用视口顶部那一行的屏幕位移衡量肉眼可见的跳动;?zone= 试回贴区,
?ask=1|2 与 ?approval=1|2 构造挂起的询问卡片与审批请求。
scroll-follow-probe.cdp.mjs 用 Node 内建 WebSocket 直连无头 Chromium 的 CDP,
逐 tick 发滚轮事件记录每步位移与写入方;scroll-follow-probe.eval.mjs 对已
打开的页面即时求值。

Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
桌面端 ChatTranscript 的 useScrollFollow 显式传 reattachZonePx: BOTTOM_REATTACH_ZONE_PX,
滚轮下行进入距底 192px 的那一 tick 直接 pin 到底,从本分支起的开发版上仍能看到
"快到最新回复结尾时上跳一下"。与 WebUI 同口径改为 0:只在真正到达底部(8px 容差)
时恢复跟随,到达底部后向下滚轮、手势落底、指针在底部释放仍会重新贴底。

BOTTOM_REATTACH_ZONE_PX 保留:它仍是桌面端底部预留带与 WebUI spacer 的最小值,
scrollFollowCore 的注释改写为现状说明。chat-transcript-scroller 契约测试新增一例
锁住零回贴区配置与预留带取值。

Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
@AlphaCatMeow
AlphaCatMeow marked this pull request as ready for review September 14, 2026 14:55
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug] 对话区接近底部时正文突然上跳:滚轮下行被回贴区吸到底,询问卡片/审批请求到达时被末端锚定拖动

1 participant