Skip to content

Thread switching scrolls from top to bottom every time, causing jarring rubber-band effect #35

@pechhe

Description

@pechhe

Summary

When switching to an existing conversation thread in pi-gui, the chat view resets its scroll position to the top and then slowly auto-scrolls back down to the bottom. This causes a jarring "rubber band" visual effect every time you switch threads, wasting time as the GUI stutters through old messages to reach the current position.

Steps to reproduce

  1. Open a conversation thread with many messages (enough to overflow the viewport).
  2. Scroll up to review earlier messages.
  3. Click on a different thread in the sidebar.
  4. Click back to the first thread.

Expected behaviour

The scroll position should either:

  • Stay pinned at the bottom (if the thread was previously at the bottom), or
  • Stay where you left it (if you had scrolled to review an earlier message).

Actual behaviour

The view jumps to the very top of the conversation, then animates/scrolls through the entire history down to the latest message. This happens on every thread switch, regardless of where you were in the thread.

Technical findings (from inspecting the bundled JS source)

The ConversationTimeline component already has infrastructure to preserve scroll position per thread — it saves scroll state in lastTimelineScrollTopBySessionRef and lastTimelinePinnedBySessionRef (per-session Maps stored in refs) during the cleanup effect of a useLayoutEffect keyed on selectedSessionKey.

Root cause: A separate layout effect fires on selectedSessionKey change that unconditionally resets pinnedToBottomRef.current = true before the per-session saved state gets a chance to be read:

// This effect fires on session switch:
useLayoutEffect(() => {
    pinnedToBottomRef.current = true;                    // <-- overrides saved state
    preserveBottomOnNextPaneResizeRef.current = false;
    resetExactBottomRestoreState(selectedSessionKey || null);
    setDisableTimelineVirtualization(Boolean(selectedSessionKey));
}, [selectedSessionKey]);

Then when the timeline pane element callback fires and reads the saved state, savedPinned ?? pinnedToBottomRef.current evaluates to true, so it always scrolls to the bottom even if the user had scrolled up. The "starts at top and scrolls down" visual effect happens because the pane initially mounts with no content (small scrollHeight), then content loads and the scroll position shifts.

Potential fix: Remove or conditionally set pinnedToBottomRef.current = true based on the saved per-session state. The saved-pinned-per-session mechanism already exists and handles this correctly — the reset just overrides it.

Environment

  • pi-gui (latest)
  • macOS

This is a UX quality-of-life issue that makes navigating between threads feel sluggish and disorienting.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions