Skip to content

fix(desktop): prevent scroll rubber-banding from virtualizer measurement adjustments - #43914

Closed
Colinchiu007 wants to merge 2 commits into
NousResearch:mainfrom
Colinchiu007:fix/scroll-measurement-jitter
Closed

fix(desktop): prevent scroll rubber-banding from virtualizer measurement adjustments#43914
Colinchiu007 wants to merge 2 commits into
NousResearch:mainfrom
Colinchiu007:fix/scroll-measurement-jitter

Conversation

@Colinchiu007

Copy link
Copy Markdown

Problem

对话内容总是自动跳到上下一段距离,不是固定在我阅读的位置。

Root Cause: The conversation uses @tanstack/react-virtual virtual list. After messages render, async operations (Shiki syntax highlighting with 120ms delay, image loading) change item heights. The virtualizer adjusts scroll offset to "preserve viewport position" — but this creates a feedback loop: measure → adjust → re-measure → re-adjust, producing visible rubber-banding / content jumping.

The existing AT_BOTTOM_THRESHOLD guard (added previously) only covered the "parked at bottom" case. When the user is reading mid-conversation, there was no protection.

Fix

  1. New constant MEASUREMENT_NOISE_THRESHOLD = 50 — any virtualizer scroll adjustment ≤ 50px is treated as measurement noise, not user intent.

  2. Updated scrollToFn with two guard cases:

    • Case 1 (existing, refactored): User at bottom → skip adjustments that would scroll up
    • Case 2 (new): User mid-conversation → skip adjustments ≤ 50px threshold

The browser's native CSS overflow-anchor: auto handles real content growth/shrink without feedback loops.

Testing

This is a UX fix for intermittent scroll jitter. Best verified by:

  1. Load a long conversation with code blocks (Shiki highlighting) and images
  2. Scroll to middle of conversation
  3. Verify viewport stays stable as messages below/above finish rendering

Related

Closes the "对话内容跳动" issue.

…ent adjustments

When the user is reading mid-conversation (not parked at bottom),
small virtualizer scroll adjustments (≤ 50px) from async rendering
(Shiki highlighting, image loading) create a feedback loop:
measure → adjust → re-measure → re-adjust → visible content jumping.

The existing AT_BOTTOM_THRESHOLD guard only covered the 'parked at
bottom' case. This adds MEASUREMENT_NOISE_THRESHOLD=50 for the
mid-conversation case: block small measurement-correction scrolls
and let the browser's native CSS overflow-anchor handle real content
growth without feedback loops.

Fixes the core complaint: '对话内容总是自动跳到上下一段距离,
不是固定在我阅读的位置'.
@alt-glitch alt-glitch added type/bug Something isn't working P3 Low — cosmetic, nice to have labels Jun 11, 2026
@Colinchiu007
Colinchiu007 requested a review from a team June 11, 2026 17:50
@teknium1

Copy link
Copy Markdown
Contributor

Thanks for identifying the scroll-jitter failure mode. This is now superseded by current main; this is an automated hermes-sweeper review.

  • 76b93869d8edddab860e225b11ef9e04df33ba63 (fix(desktop): rebuild thread autoscroll on use-stick-to-bottom) deleted apps/desktop/src/components/assistant-ui/thread-virtualizer.tsx, so the PR's scrollToFn target is no longer present.
  • apps/desktop/src/components/assistant-ui/thread/list.tsx:109 makes use-stick-to-bottom the single scrollTop owner, avoiding virtualizer measurement corrections competing with another scroll controller.
  • apps/desktop/src/styles.css:1016 disables native anchoring while following and restores overflow-anchor: auto once the user has scrolled away from the bottom, preserving the mid-conversation reading position behavior this PR addresses.

Closing as implemented on main via 76b93869d8edddab860e225b11ef9e04df33ba63 (included in v2026.6.19).

@teknium1 teknium1 closed this Jul 14, 2026
@teknium1 teknium1 added sweeper:implemented-on-main Sweeper: behavior already present on current main sweeper:risk-compatibility Sweeper risk: may break existing users, config, migrations, defaults, or upgrades sweeper:blast-moderate Sweeper blast radius: moderate — a subsystem or single platform labels Jul 14, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

P3 Low — cosmetic, nice to have sweeper:blast-moderate Sweeper blast radius: moderate — a subsystem or single platform sweeper:implemented-on-main Sweeper: behavior already present on current main sweeper:risk-compatibility Sweeper risk: may break existing users, config, migrations, defaults, or upgrades type/bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants