• Will Chen's avatar
    Fix chat auto-scroll: replace timeout-based tracking with position-ba… (#2448) · ec2807f0
    Will Chen 提交于
    …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: 's avatarClaude <noreply@anthropic.com>
    Co-authored-by: 's avatarclaude[bot] <41898282+claude[bot]@users.noreply.github.com>
    ec2807f0
ChatPanel.tsx 7.2 KB