• 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
名称
最后提交
最后更新
..
AgentConsentBanner.tsx 正在载入提交数据...
AttachmentsList.tsx 正在载入提交数据...
AuxiliaryActionsMenu.tsx 正在载入提交数据...
ChatActivity.tsx 正在载入提交数据...
ChatError.tsx 正在载入提交数据...
ChatErrorBox.tsx 正在载入提交数据...
ChatHeader.tsx 正在载入提交数据...
ChatInput.tsx 正在载入提交数据...
ChatMessage.tsx 正在载入提交数据...
CodeHighlight.tsx 正在载入提交数据...
ContextLimitBanner.tsx 正在载入提交数据...
DeleteChatDialog.tsx 正在载入提交数据...
DragDropOverlay.tsx 正在载入提交数据...
DyadAddDependency.tsx 正在载入提交数据...
DyadAddIntegration.tsx 正在载入提交数据...
DyadCodeSearch.tsx 正在载入提交数据...
DyadCodeSearchResult.tsx 正在载入提交数据...
DyadCodebaseContext.tsx 正在载入提交数据...
DyadDatabaseSchema.tsx 正在载入提交数据...
DyadDelete.tsx 正在载入提交数据...
DyadEdit.tsx 正在载入提交数据...
DyadExecuteSql.tsx 正在载入提交数据...
DyadGrep.tsx 正在载入提交数据...
DyadListFiles.tsx 正在载入提交数据...
DyadLogs.tsx 正在载入提交数据...
DyadMarkdownParser.tsx 正在载入提交数据...
DyadMcpToolCall.tsx 正在载入提交数据...
DyadMcpToolResult.tsx 正在载入提交数据...
DyadOutput.tsx 正在载入提交数据...
DyadProblemSummary.tsx 正在载入提交数据...
DyadRead.tsx 正在载入提交数据...
DyadRename.tsx 正在载入提交数据...
DyadSearchReplace.tsx 正在载入提交数据...
DyadStatus.tsx 正在载入提交数据...
DyadSupabaseProjectInfo.tsx 正在载入提交数据...
DyadSupabaseTableSchema.tsx 正在载入提交数据...
DyadThink.tsx 正在载入提交数据...
DyadTokenSavings.tsx 正在载入提交数据...
DyadWebCrawl.tsx 正在载入提交数据...
DyadWebSearch.tsx 正在载入提交数据...
DyadWebSearchResult.tsx 正在载入提交数据...
DyadWrite.tsx 正在载入提交数据...
FileAttachmentDropdown.tsx 正在载入提交数据...
FixAllErrorsButton.tsx 正在载入提交数据...
FreeAgentQuotaBanner.tsx 正在载入提交数据...
HomeChatInput.tsx 正在载入提交数据...
LexicalChatInput.tsx 正在载入提交数据...
MessagesList.tsx 正在载入提交数据...
PromoMessage.tsx 正在载入提交数据...
RenameChatDialog.tsx 正在载入提交数据...
SelectedComponentDisplay.tsx 正在载入提交数据...
StreamingLoadingAnimation.tsx 正在载入提交数据...
SummarizeInNewChatButton.tsx 正在载入提交数据...
TodoList.tsx 正在载入提交数据...
TokenBar.tsx 正在载入提交数据...
UncommittedFilesBanner.tsx 正在载入提交数据...
VersionPane.tsx 正在载入提交数据...
monaco.ts 正在载入提交数据...
stateTypes.ts 正在载入提交数据...
types.d.ts 正在载入提交数据...