Skip to content

fix(desktop): stop pinning stale message at top on chat switch / compaction (#39959) - #39995

Closed
victorftrdba wants to merge 1 commit into
NousResearch:mainfrom
victorftrdba:fix/39959-message-list-pin
Closed

fix(desktop): stop pinning stale message at top on chat switch / compaction (#39959)#39995
victorftrdba wants to merge 1 commit into
NousResearch:mainfrom
victorftrdba:fix/39959-message-list-pin

Conversation

@victorftrdba

@victorftrdba victorftrdba commented Jun 5, 2026

Copy link
Copy Markdown

Fixes #39959

Problem

After switching chats while a session is busy — or when automatic context compaction rotates to a successor session — the last sent message becomes pinned to the top of the viewport, and the pinned item changes while scrolling up. Classic virtualization row-reuse.

Root cause

  • thread-virtualizer.tsx keyed the inner turn rows with the array index (key={index}), not the message id. On a session swap the new messages occupy the same indices, so React/@tanstack/react-virtual reuse the previous session's row DOM and state. (The outer virtual rows were already correctly keyed via getItemKey.)
  • User bubbles use position: sticky (StickyHumanMessageContainer), so a reused/stale row is what visibly sticks to the top.
  • <Thread> was never remounted per session — only passed sessionKey as a prop — so scroll-anchor / sticky state leaked across sessions.

Fix

  1. Key inner turn rows by stable message id. turn groups now carry { index, id }[] instead of number[]; buildGroups populates both (iteration is unchanged).
  2. Add key={threadKey} to <Thread> so it fully remounts on session switch/compaction, resetting leaked scroll-anchor and sticky state. AssistantRuntimeProvider stays mounted above it, so in-flight streams are unaffected.

Verification

  • npx tsc -b in apps/desktop passes (exit 0).
  • Manual: open two long sessions, send a message, switch chats while busy / trigger compaction → the correct session's user bubble sticks; no stale message pins to the top; scrolling no longer swaps the pinned row.

…ssion (NousResearch#39959)

The conversation virtualizer keyed inner turn rows by array index, so
switching chats while busy — or context compaction rotating the session
id — reused row DOM/state across sessions. Combined with the sticky user
bubble (position: sticky), the wrong/stale message stayed pinned at the
top and shifted while scrolling.

Key inner rows by stable message id (turn groups now carry per-message
ids), and remount the Thread on session/threadKey change so leaked
scroll-anchor and sticky state reset cleanly. The AssistantRuntimeProvider
stays mounted above Thread, so in-flight streams are unaffected.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

comp/tui Terminal UI (ui-tui/ + tui_gateway/) 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.

[Bug]: Desktop chat switching/new session pins last sent message at top of viewport

2 participants