Skip to content

fix(desktop): stop chat scroll bounce — at-rest backward jump + wheel-up snap-back - #38221

Merged
OutThisLife merged 2 commits into
mainfrom
hermes/hermes-45accc84
Jun 3, 2026
Merged

fix(desktop): stop chat scroll bounce — at-rest backward jump + wheel-up snap-back#38221
OutThisLife merged 2 commits into
mainfrom
hermes/hermes-45accc84

Conversation

@teknium1

@teknium1 teknium1 commented Jun 3, 2026

Copy link
Copy Markdown
Contributor

Summary

Desktop chat no longer bounces, jumps backward, or snaps back to the bottom while reading. Two distinct disarm bugs in useThreadScrollAnchor are fixed together.

The Desktop scroll anchor had two independent failure modes, both reported across Win11/macOS/Linux (#37997, #37811, #37527):

  1. At-rest backward jump — when content grows mid-frame (virtualizer item measure, streaming token, code re-highlight), the browser emits an interim scroll event whose scrollTop is lower than last frame because scrollHeight just jumped. This fires before the rAF pinToBottom runs, so it was misread as a user scroll-up → sticky-bottom disarmed permanently → viewport stuck ~50px above bottom.
  2. Wheel-up snap-backprogrammaticScrollPendingRef went stale when the browser was already clamped at bottom (the expected programmatic scroll event never arrived). The user's next real wheel-up was consumed as "ours" → re-armed → next content-growth pin yanked them back down.

The two fixes touch disjoint lines of the same hook and are complementary, not competing.

Changes

Validation

Before After
streaming.test.tsx wheel-up regression failed on main 10/10 pass (both wheel-up tests + at-rest cases)
npx tsc -b 0 errors
eslint (changed files) 6 pre-existing warnings 0 errors, same pre-existing warnings

Closes #37997, #37527, #37811.

Salvage of #38019 (@luyao618) + #37831 (@ferminquant), cherry-picked onto current main with authorship preserved.

Infographic

desktop-chat-scroll-stabilized

luyao618 and others added 2 commits June 3, 2026 05:54
…rim scrolls (#37997)

The thread scroll-anchor hook in apps/desktop/src/components/assistant-ui/
thread-virtualizer.tsx was disarming sticky-bottom whenever scrollTop
decreased by >1px between scroll events. That check was too eager: when
content height grows mid-frame (virtualizer measurement of a newly visible
turn, streaming token, Streamdown/Shiki re-tokenization, composer chip
toggle), the browser emits an interim 'scroll' event whose scrollTop is
smaller than the previous frame's because scrollHeight just jumped. The
rAF-scheduled pinToBottom hasn't run yet, so programmaticScrollPendingRef
is 0 and the disarm fired. With sticky-bottom disarmed the scroller stuck
~50px above bottom — the visible at-rest backward jump that #37997
describes (and the same root cause as the wheel-up variant in #37527).

Fix:
- Track scrollHeight per frame (lastHeightRef). Disarm on scrollTop
  decrease ONLY when scrollHeight did not grow this frame. Real upward
  user intent (scrollbar drag, keyboard PgUp, programmatic scrollIntoView)
  still disarms because it moves scrollTop without growing the content.
  Wheel-up and touchmove continue to disarm via their own listeners.
- Stop observing the scroller element itself in the ResizeObserver; only
  observe its content child. Viewport-only resizes (window resize,
  devtools panel toggle) no longer trigger spurious pins, matching the
  intent of the auto-stick-to-bottom behavior.

Verified:
- apps/desktop `tsc -b` clean.
- apps/desktop `vitest run src/components/assistant-ui/streaming.test.tsx`
  passes (9/9), including the existing wheel-up disarm regression test
  that asserts scrollTop stays at 420 after a wheel-up + content growth.
@alt-glitch alt-glitch added type/bug Something isn't working P3 Low — cosmetic, nice to have labels Jun 3, 2026
@OutThisLife
OutThisLife self-requested a review June 3, 2026 13:05
@OutThisLife
OutThisLife merged commit 60b6352 into main Jun 3, 2026
20 checks passed
@OutThisLife
OutThisLife deleted the hermes/hermes-45accc84 branch June 3, 2026 13:05
teknium1 added a commit that referenced this pull request Jun 3, 2026
Follow-up to #38221. Users still saw the chat viewport jump up then snap
back while reading at the bottom — pinned by one reporter to code/patch
blocks being highlighted. PR #38221 fixed the disarm (scrolled-up) path;
this is the armed-at-bottom path.

Root cause, verified live in headless Chromium (CDP), NOT just modeled:
while parked at bottom, Streamdown/Shiki re-tokenizing a code block briefly
REPLACES laid-out DOM, so for one frame the content is SHORTER. The browser
clamps scrollTop upward the instant content shrinks below the scroll
position (1400 -> 1100 with NO pin involved); the next frame content
regrows and the rAF pin snaps it back down. A pin cannot prevent the
up-jump because the clamp happens at layout, before any pin runs (confirmed:
both deferred and synchronous re-pins still show 1400 -> 1100 -> 1480).

Fix: keep the content's height MONOTONIC within a turn. The ResizeObserver
raises a high-water-mark and reserves it as min-height on the content BEFORE
pinning, so a transient shrink never shrinks the scroller and the browser
never clamps. scrollHeight still grows under the viewport so streaming
tokens follow. Reset the high-water-mark in jumpToBottom (new turn / session
/ first content) and on user disarm so an old tall thread can't pad a new
short one and a finished turn can't leave a dead gap.

Live CDP proof (real Chromium native clamping):
  current main:  parked 1400 -> shrink 1100 -> grow 1480   (bounce)
  this fix:      parked 1400 -> shrink 1400 -> grow 1400    (no bounce)

Adds a streaming.test.tsx regression that models the browser clamp-on-shrink
(scrollHeight = max(measured, reserved min-height); scrollTop clamps on
shrink). Armed at bottom, a shrink RO frame must keep scrollTop at 1400 and
reserve min-height 2000px. RED on pre-fix main (min-height stays empty).
@heath0xFF

Copy link
Copy Markdown

thanks for this! :D

davidgut1982 pushed a commit to davidgut1982/hermes-agent that referenced this pull request Jun 5, 2026
…-45accc84

fix(desktop): stop chat scroll bounce — at-rest backward jump + wheel-up snap-back
alt-glitch pushed a commit that referenced this pull request Jun 14, 2026
fix(desktop): stop chat scroll bounce — at-rest backward jump + wheel-up snap-back
T02200059 pushed a commit to T02200059/hermes-agent that referenced this pull request Jun 18, 2026
…-45accc84

fix(desktop): stop chat scroll bounce — at-rest backward jump + wheel-up snap-back
waefrebeorn pushed a commit to waefrebeorn/slermes that referenced this pull request Jul 2, 2026
…-45accc84

fix(desktop): stop chat scroll bounce — at-rest backward jump + wheel-up snap-back
santhreal pushed a commit to santhreal/hermes-agent that referenced this pull request Jul 13, 2026
…-45accc84

fix(desktop): stop chat scroll bounce — at-rest backward jump + wheel-up snap-back
Gravezzz pushed a commit to Gravezzz/hermes-agent that referenced this pull request Jul 21, 2026
…-45accc84

fix(desktop): stop chat scroll bounce — at-rest backward jump + wheel-up snap-back
leewenjie pushed a commit to leewenjie/hermes-agent that referenced this pull request Aug 7, 2026
…-45accc84

fix(desktop): stop chat scroll bounce — at-rest backward jump + wheel-up snap-back
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 type/bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Desktop chat scrollbar jumps backward every few seconds

6 participants