Skip to content

fix(chat): 修复回复区原生滚动条拇指拖动被吸回底部 - #773

Merged
su-fen merged 1 commit into
mainfrom
fix/native-scrollbar-thumb-drag
Sep 7, 2026
Merged

fix(chat): 修复回复区原生滚动条拇指拖动被吸回底部#773
su-fen merged 1 commit into
mainfrom
fix/native-scrollbar-thumb-drag

Conversation

@su-fen

@su-fen su-fen commented Sep 7, 2026

Copy link
Copy Markdown
Member

Linked issue

Closes #772

Summary

回复区处于贴底跟随时,用鼠标按住原生滚动条拇指向上拖,拇指刚被拖开就被立刻拉回底部,反复拖也几乎不动。

根因:原生滚动条拇指拖动时 Chromium/WebKit 把指针事件路由给滚动条,页面只收到 scroll 事件、收不到 pointermoveuseScrollFollow 只有在 pointermove 位移超过 4px 时才把按压提升为拖动(pointerDragStart),这条路径在原生拇指拖动中永远不会触发;于是拖动期间 following 仍为 true,reducer 的 corrector 分支把每一帧拉开的 gap 当作噪声重新 pin 回底部。旧代码只对自定义滚动条元素 [data-scroll-area-scrollbar] 在 pointerdown 时即时提升,而 GUI 转录区自 #622 起使用原生 viewport(overflow-y-auto + scrollbar-gutter: stable),没有这个 DOM 节点。

修法agent-ui 共享层,两端一处生效):

  • scrollFollowCore.ts 新增纯函数 isPointInNativeScrollbarGutter(x, y, box):点在边框盒(getBoundingClientRect)内、但在客户盒(clientLeft/Top + clientWidth/Height)外,即落在元素自身的原生滚动条 / 轨道 / scrollbar-gutter 预留列上。客户盒天然排除了 gutter 预留列,RTL 左侧滚动条则由 clientLeft 承载条宽,无需特判。
  • useScrollFollow.tshandlePointerDown:对 pointerType === "mouse"event.target === viewport 的按压做上述几何判定,命中即 cancelJumpAnimation() + dispatch({ type: "pointerDragStart" }),与自定义滚动条的既有路径同构。之后拖动产生的第一个远离底部的 scroll 事件就会走 pointerDragging 分支解除跟随;松手时若已拖回底部区域则照常重新吸附。
  • 守卫有意收窄:target === viewport 是因为 listenerRoot 可能是 ScrollArea 根,兄弟元素上的按压冒泡上来不该被当作滚动条按压,内容元素上的点击也永远不该走几何判定;触摸永远不会抓原生拇指(走 touchmove 路径),手写笔按压不是滚动意图,故只认 mouse。内容区按压保持原有位移门槛(静态点击 + 布局回声不得读作拖动)。

Change scope

  • Modules: agent-ui(GUI / WebUI 共享)、agent-gui(测试)
  • Key paths:
    • crates/agent-ui/src/lib/chat-scroll/scrollFollowCore.ts(新增 ScrollerBox 类型与 isPointInNativeScrollbarGutter
    • crates/agent-ui/src/lib/chat-scroll/useScrollFollow.ts(pointerdown 时对原生滚动条按压即时提升为拖动)
    • crates/agent-gui/test/chat/scroll-follow-core.test.mjs(reducer 场景:原生拇指拖动仅靠 scroll 事件解除跟随;gutter 命中测试含 RTL / 无滚动条 / 元素外)
    • crates/agent-gui/test/chat/use-scroll-follow-native-scrollbar.test.mjs(新增:jsdom 真渲染 hook,伪造几何 + scroll 事件,修复前必失败)

Screenshots / preview

Verification

在本分支(基于 main@dfb682b4)复跑:

  • pnpm typecheck:ui
  • pnpm check:ui-boundaries
  • 改动的两个 agent-ui 源文件 biome check 无诊断(agent-gui 的 biome 配置不覆盖 test/,与既有测试一致)
  • crates/agent-guitest/chat 目录全量 1327 项全通过;本次直接相关的 scroll-follow-core(28 项)与新增的 use-scroll-follow-native-scrollbar(4 项)在修复分支上重跑通过
  • 新增回归测试覆盖:鼠标在滚动条列按下 + 仅 scroll 事件的拖动解除跟随且位置不被纠正;拖回底部松手重新吸附;内容区静态按压 + 布局回声仍被纠正(位移门槛不变);触摸落在滚动条列不算滚动条按压

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.

🤖 Generated with Claude Code

原生滚动条拇指拖动时 Chromium/WebKit 把指针事件路由给滚动条,页面只收到
scroll 事件、收不到 pointermove,useScrollFollow 依赖位移超过 4px 才把按压
提升为拖动的逻辑永远不会触发;拖动期间 following 仍为 true,corrector 把每一
帧拉开的 gap 当作噪声重新钉回底部,拇指看起来被粘死。

scrollFollowCore 新增纯函数 isPointInNativeScrollbarGutter:点在边框盒内但在
客户盒外即落在元素自身的原生滚动条/gutter 上。hook 在 pointerdown 时对
pointerType 为 mouse 且 target 为 viewport 本身的按压做该几何判定,命中即取消
跳转动画并立即 dispatch pointerDragStart,与自定义滚动条元素的既有路径同构。
内容区按压保持原有位移门槛,触摸/手写笔不参与判定。

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@StackCairn
StackCairn marked this pull request as draft September 7, 2026 01:52
@github-actions

github-actions Bot commented Sep 7, 2026

Copy link
Copy Markdown
Contributor

PR governance checks failed — this PR has been converted to draft.

  • UI change without screenshots: this PR modifies frontend code. Please add before/after screenshots or a recording under "Screenshots / preview" in the PR body.

Fix the items above, then click Ready for review to re-run the checks.

@su-fen su-fen added the governance-exempt Skip PR governance checks label Sep 7, 2026
@su-fen
su-fen marked this pull request as ready for review September 7, 2026 02:02
@su-fen
su-fen merged commit c5d642d into main Sep 7, 2026
9 of 10 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

governance-exempt Skip PR governance checks

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug] 回复区拖动原生滚动条拇指时被吸回底部,无法拖动查看历史

1 participant