Skip to content

fix(transcript): scroll to latest message on first open#134

Open
mpmisha wants to merge 1 commit into
InbarR:mainfrom
mpmisha:users/mimer/fix/transcript-scroll-to-bottom-on-open
Open

fix(transcript): scroll to latest message on first open#134
mpmisha wants to merge 1 commit into
InbarR:mainfrom
mpmisha:users/mimer/fix/transcript-scroll-to-bottom-on-open

Conversation

@mpmisha

@mpmisha mpmisha commented Jun 15, 2026

Copy link
Copy Markdown
Contributor

What

Opening the Transcript panel (⌘+⌥+T / title-bar button) on a long conversation used to dump you at the very first message instead of jumping to the most recent one.

Why

The initial fetch in TranscriptPanel.tsx called scrollToBottom() inside a single requestAnimationFrame immediately after setMsgs(next). React hadn't actually rendered the new bubbles yet at that point, so scrollHeight ≈ clientHeight and scrollTop = scrollHeight effectively landed at the top. After the real render committed, the view stayed parked at message #1.

How

  • Added a useLayoutEffect keyed on [open, aiSessionId, msgs] that performs the first-render jump. It runs after layout (so scrollHeight is correct) but before paint (no visible flicker / jump).
  • A new initialScrolledForRef tracks which session id has already had its initial scroll-to-bottom done, so the jump fires exactly once per session/open cycle and live polls don't re-trigger it.
  • The existing live-poll "glue to bottom" behavior is preserved for follow-up updates when the user is already at the bottom; only the racy initial || short-circuit in the fetch path was removed.

Testing

Manual:

  • Opened the transcript on a long (200+ message) session — now lands on the most recent message.
  • Re-opened on the same session multiple times — consistent.
  • Switched panes / followed focus to a different session — jumps to that session's latest message.
  • Scrolled up, waited for a live update — view stays put (no auto-scroll). Scrolled back to bottom, new message arrived — glues to bottom. Both unchanged from before.
  • Opened the search bar and parked on a hit — live updates don't yank the view away. Unchanged.

npx tsc --noEmit shows no new errors in the touched file (a handful of pre-existing errors in unrelated files are untouched).

assistance: agentic-cli
type: bug
agent-tool: copilot-cli
agent-model: claude-opus-4.7
work-item: n/a

Opening the transcript on a long conversation used to land at the
very first message instead of the most recent one. The initial fetch
called scrollToBottom() inside a single requestAnimationFrame right
after setMsgs(next), but React hadn't rendered the new bubbles yet
so scrollHeight was still ~clientHeight and scrollTop = scrollHeight
effectively landed at the top.

Move the first-render jump into a useLayoutEffect keyed on the
session id, which fires after the DOM is laid out (scrollHeight is
correct) but before paint (no visible flicker). A ref tracks which
session has already had its initial scroll done, so the jump runs
exactly once per session/open cycle and live polls don't re-trigger it.

The live-poll glue-to-bottom path is preserved for follow-up updates
when the user is already at the bottom.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant