Fix chat auto-scroll: replace timeout-based tracking with position-ba… (#2448)
…sed approach
The previous stick-to-bottom mechanism used a timeout-based
`isUserScrolling` flag that reset after 2 seconds, causing the chat to
snap back to auto-follow even when the user intentionally scrolled away.
It also used manual scroll event listeners that duplicated Virtuoso's
built-in capabilities.
Replace with Virtuoso's native `atBottomStateChange` callback as the
single source of truth for scroll position tracking. This gives:
- Reliable stick-to-bottom: follows streaming output when at bottom
- Clean escape: scrolling up past 80px threshold stops auto-follow
immediately
- No timeout jank: purely position-based, no 2-second reset timer
- Simpler code: removes manual scroll listeners, cleanup refs, and
timeout logic
https://claude.ai/code/session_01PC8tFKJ439W8cVaT5Efyzr
<!-- devin-review-badge-begin -->
---
<a href="https://app.devin.ai/review/dyad-sh/dyad/pull/2448">
<picture>
<source media="(prefers-color-scheme: dark)"
srcset="https://static.devin.ai/assets/gh-open-in-devin-review-dark.svg?v=1">
<img
src="https://static.devin.ai/assets/gh-open-in-devin-review-light.svg?v=1"
alt="Open with Devin">
</picture>
</a>
<!-- devin-review-badge-end -->
<!-- CURSOR_SUMMARY -->
---
> [!NOTE]
> **Medium Risk**
> Changes core chat scrolling behavior during streaming by replacing
timeout-based user-scroll detection with position-based at-bottom state,
which could regress auto-follow or scroll-button behavior in edge cases.
Scope is limited to the chat UI and does not touch data/auth logic.
>
> **Overview**
> Refactors chat auto-scroll to be **purely position-based**: replaces
the timeout-driven `isUserScrolling`/manual scroller listeners with a
single at-bottom source of truth (`atBottomStateChange` in Virtuoso,
plus a simple test-mode scroll handler).
>
> Auto-follow now resumes only when the user is actually at the bottom,
and the stream-complete “scroll to footer” behavior runs only if the
user stayed at bottom during streaming; the scroll-to-bottom button is
driven directly by the at-bottom state.
>
> <sup>Written by [Cursor
Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit
3273c28f3284afcde3fac58e50f243333404b799. This will update automatically
on new commits. Configure
[here](https://cursor.com/dashboard?tab=bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
<!-- This is an auto-generated description by cubic. -->
---
## Summary by cubic
Fixes chat auto-scroll so the view only follows streaming when you’re at
the bottom and stays put when you scroll up. Removes timeout-based
tracking and manual listeners for a smoother, simpler experience.
- **Bug Fixes**
- Stops snap-back: scrolling up past 80px disables auto-follow
immediately.
- Follows streaming output only when at bottom; shows a scroll-to-bottom
button when not.
- After a stream finishes, auto-scrolls only if you were already at
bottom.
- **Refactors**
- Replaced timeout-based isUserScrolling with Virtuoso’s
atBottomStateChange + followOutput.
- Removed manual scroll listeners and cleanup refs; rely on Virtuoso for
position tracking.
- Updated MessagesList to use atBottomThreshold=80 and a simplified
followOutput callback; test mode mirrors behavior with lightweight
scroll tracking.
<sup>Written for commit a7b3fba8b0528f52b11d0a6711cec574be3732d8.
Summary will update on new commits.</sup>
<!-- End of auto-generated description by cubic. -->
---------
Co-authored-by:
Claude <noreply@anthropic.com>
Co-authored-by:
claude[bot] <41898282+claude[bot]@users.noreply.github.com>
正在显示
请
注册
或者
登录
后发表评论