Skip to content

bug(streaming): auto-scroll still overrides user scroll position on macOS app #1469

@berkinduz

Description

@berkinduz

Even though #1360 is marked resolved and my app is up-to-date, I can still reproduce this on master.

Steps to reproduce

  1. Send a prompt that produces a long response (or kicks off many tool calls).
  2. While streaming, scroll up to re-read earlier content.
  3. Observe: the view snaps back to the bottom within ~100–500ms.

Browser: Chrome 147.0.7727.138

Diagnosis

After digging into static/ui.js:1170-1184, I think the 150 → 250px threshold bump in the previous fix papers over the symptom rather than addressing the root cause. Two issues:

  1. The scroll listener can't tell programmatic scrolls from user scrolls. Every time scrollIfPinned() writes el.scrollTop = el.scrollHeight, the same listener fires, sees nearBottom = true, and re-pins — even if the user just scrolled up moments before.
  2. The 250px threshold is racy against streaming. A fast wheel flick clears it in one event, which is why the issue feels "almost fixed". But on a slow trackpad scroll the user only accumulates ~50px per event, and each streaming tick re-snaps to bottom before that delta can cross the threshold. Slow scroll within an active streaming message becomes effectively impossible.

Suggested approach

A position-threshold approach is fundamentally racy against streaming. A more robust fix would:

  • Decouple programmatic scrolls from user-scroll detection (flag set before our scrollTop writes, cleared on the next frame, listener ignores while set).
  • Drive unpin from user intent, not position — listen for wheel (deltaY < 0), touchmove (finger moves down = content moves up), and keydown (ArrowUp / PageUp / Home) on #messages. Any upward intent unpins immediately, regardless of distance to bottom.
  • Re-pin only when the user actually reaches the bottom (small tolerance, e.g. <8px) — not when they're merely "near" it.

I tried this approach locally and slow scroll within an active stream now stays put. Happy to help test a fix.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workinghelp wantedExtra attention is neededsprint-candidateStrong candidate for next sprintstreamingSSE streaming, gateway sync, real-time updatesuxUser experience / visual polish

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions