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
- Open a conversation thread with many messages (enough to overflow the viewport).
- Scroll up to review earlier messages.
- Click on a different thread in the sidebar.
- 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
This is a UX quality-of-life issue that makes navigating between threads feel sluggish and disorienting.
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
Expected behaviour
The scroll position should either:
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
ConversationTimelinecomponent already has infrastructure to preserve scroll position per thread — it saves scroll state inlastTimelineScrollTopBySessionRefandlastTimelinePinnedBySessionRef(per-sessionMaps stored in refs) during the cleanup effect of auseLayoutEffectkeyed onselectedSessionKey.Root cause: A separate layout effect fires on
selectedSessionKeychange that unconditionally resetspinnedToBottomRef.current = truebefore the per-session saved state gets a chance to be read:Then when the timeline pane element callback fires and reads the saved state,
savedPinned ?? pinnedToBottomRef.currentevaluates totrue, 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 (smallscrollHeight), then content loads and the scroll position shifts.Potential fix: Remove or conditionally set
pinnedToBottomRef.current = truebased on the saved per-session state. The saved-pinned-per-session mechanism already exists and handles this correctly — the reset just overrides it.Environment
This is a UX quality-of-life issue that makes navigating between threads feel sluggish and disorienting.