-
Notifications
You must be signed in to change notification settings - Fork 2.6k
Description
Problem Description
The chat view scrollbar jumps to the middle of the conversation during long streaming sessions, making it impossible to follow the latest messages. This occurs when conversations exceed 500 messages and is caused by array index shifting in the virtual scrolling implementation.
Root Cause Analysis
The Index Shifting Bug
The issue stems from the message filtering logic in webview-ui/src/components/chat/ChatView.tsx at lines 899-902:
const visibleMessages = useMemo(() => {
const currentMessageCount = modifiedMessages.length
const startIndex = Math.max(0, currentMessageCount - 500)
const recentMessages = modifiedMessages.slice(startIndex)
// ...When a conversation exceeds 500 messages, each new message causes:
- The
startIndexto increment by 1 - The oldest message to be dropped from the array
- All remaining messages to shift down by one index position
- Virtuoso (the virtual scrolling library) to lose track of the current position
The Sequence of Events
- Initial State: User is viewing message at index 499 (bottom of 500-message window)
- New Message Arrives: Total messages become 501
- Array Mutation:
startIndexchanges from 0 to 1- Message at index 0 is dropped
- Messages 1-500 become indices 0-499
- New message is added at index 499
- Position Lost: Virtuoso was tracking index 499, but that index now contains different content
- View Jumps: The viewport jumps to approximately the middle of the conversation (around index 250)
Why Current Fixes Don't Work
Number.MAX_SAFE_INTEGERscrolling (lines 1337, 1351): This correctly scrolls to bottom, but array mutations happen between scroll commands- Auto-scroll timing (line 1395): Auto-scroll fires every 50ms, but array shifts happen immediately when messages arrive
- PR fix: improve auto-scroll behavior during API activities and streaming #6989: Helps by keeping auto-scroll active during streaming, but doesn't address the underlying array mutation issue
Reproduction Steps
- Start a new conversation with Roo Code
- Continue the conversation until it exceeds 500 messages
- Observe that during streaming, the view jumps to the middle of the conversation
- Scrolling down manually doesn't help as it keeps jumping back up
Technical Details
Affected Components
webview-ui/src/components/chat/ChatView.tsx- React Virtuoso virtual scrolling library
- Message array management (lines 899-963)
Related Code Sections
- Message filtering: Lines 899-902
- Virtuoso configuration: Lines 1869-1885
- Auto-scroll logic: Lines 1390-1400
- Scroll methods: Lines 1335-1356
Proposed Solutions
Option 1: Remove Message Limit (Recommended)
Remove the 500-message slice entirely and let Virtuoso handle virtualization of all messages:
const visibleMessages = useMemo(() => {
// Remove the slicing logic
const newVisibleMessages = modifiedMessages.filter((message: ClineMessage) => {
// Keep existing filter logic
})
// ...Pros:
- Completely eliminates index shifting
- Virtuoso is designed to handle large lists efficiently
- Simplest fix
Cons:
- Slightly higher memory usage (but still bounded by Virtuoso's virtualization)
Option 2: Track by Stable Keys
Configure Virtuoso to track items by message timestamp (ts) instead of array index:
<Virtuoso
ref={virtuosoRef}
key={task.ts}
className="scrollable grow overflow-y-scroll mb-1"
increaseViewportBy={{ top: 3_000, bottom: 1000 }}
data={groupedMessages}
itemContent={itemContent}
computeItemKey={(index, item) => Array.isArray(item) ? item[0].ts : item.ts}
// ...
/>Pros:
- Maintains current memory optimization
- Virtuoso can track position even with array mutations
Cons:
- Requires testing to ensure Virtuoso handles key-based tracking correctly
Option 3: Force Re-scroll on Array Mutation
Detect when startIndex changes and immediately scroll to bottom:
const prevStartIndexRef = useRef(0)
useEffect(() => {
const currentMessageCount = modifiedMessages.length
const startIndex = Math.max(0, currentMessageCount - 500)
if (startIndex !== prevStartIndexRef.current && !disableAutoScrollRef.current) {
scrollToBottomAuto()
prevStartIndexRef.current = startIndex
}
}, [modifiedMessages.length])Pros:
- Minimal change to existing code
- Preserves memory optimization
Cons:
- May cause visible jumps when re-scrolling
- Doesn't fix the root cause, just mitigates symptoms
Impact
- Severity: High - Makes long conversations unusable
- Affected Users: Anyone with conversations exceeding 500 messages
- Regression: Introduced in PR Fix: Resolve Memory Leak in ChatView Virtual Scrolling Implementation #6697 (memory optimization)
Testing Requirements
- Create a conversation with 600+ messages
- Verify scrolling stays at bottom during streaming
- Verify manual scroll-up is respected
- Verify memory usage remains reasonable
- Test with various message types (code blocks, images, etc.)
Related Issues and PRs
- Original issue: Scrollbar does not stay scrolled to the bottom of the RooCode window #6987
- Related issue: Scrolling in Roo window jumps around #7026
- Memory optimization that introduced the bug: PR Fix: Resolve Memory Leak in ChatView Virtual Scrolling Implementation #6697
- Partial fix for jitter: PR fix: Replace scrollToIndex with scrollTo to fix scroll jitter #6780
- Attempted fixes: PR fix: improve auto-scroll behavior during API activities and streaming #6989, fix: dynamic viewport adjustment to prevent scroll jumping on small screens #7027, fix: resolve scroll jumping issue on smaller screens #7028
Additional Context
This investigation revealed that while PR #6989 helps with auto-scroll behavior, it doesn't address the root cause of array index shifting. A proper fix requires preventing the array indices from changing when new messages arrive in long conversations.
The issue is particularly problematic because:
- It makes long conversations effectively unusable
- Users cannot follow along with streaming responses
- Manual intervention (scrolling) doesn't help as the view keeps jumping
@roomote-agent This issue needs immediate attention as it significantly impacts user experience in long conversations.
Metadata
Metadata
Assignees
Labels
Type
Projects
Status