Skip to content

Fix/transcript anchor on first token - #339

Open
zopeVaibhav wants to merge 2 commits into
CopilotKit:mainfrom
zopeVaibhav:fix/transcript-anchor-on-first-token
Open

Fix/transcript anchor on first token#339
zopeVaibhav wants to merge 2 commits into
CopilotKit:mainfrom
zopeVaibhav:fix/transcript-anchor-on-first-token

Conversation

@zopeVaibhav

Copy link
Copy Markdown
Contributor

Fixes #338.

What this changes

Keeps the transcript where the reader left it when a Bot's first token arrives. Today it jumps to the first message of the conversation on every turn, because MessageScroller counts the element children of its content to work out what changed, and Thinking, Stopped and the queued lines sit in that list alongside the messages. A turn's first token swaps one for the other, the count holds, the scroller reads that as the list having been rewritten and re-anchors on the top of the conversation.

Those three are moved into a single always-rendered wrapper, placed ahead of the message rows so the scroller's append arithmetic still lines up, and put back visually with order. The scroller's own spacer is given a later order so it stays below them rather than between them and the last message.

Also in here: the send scroll animates instead of jumping. The scroller calls scrollTo with behavior: "auto", which defers to the element's CSS, so the viewport is switched to scroll-behavior: smooth for the 700ms around a send and switched back. Only the send — an answer streaming in re-anchors on every resize, and smoothing that produces a scroll that never catches up with its own text. prefers-reduced-motion opts out.

Where it runs

  • New state that outlives a request? None. One useRef holding the last user message id, per mounted transcript, in the browser.
  • What happens on the second replica? Nothing differs. No server code is touched; this is render order and a CSS property in one component.
  • Anything serialised? None.
  • Anything fanned out to a browser? None. The transcript renders the messages it is already given.
  • New listener, port, or schedule? None.

Boundary and audit

  • Every acting call still goes through the gateway: unchanged, no acting call is touched.
  • New refusals and new failures each write a row: none added.
  • Nothing new is trusted from the client: nothing new is read from the client.

Changelog

  • A line under Unreleased: The transcript stays with the question when an answer starts arriving.

Proof

Reproduced and verified in Chromium against an isolated harness running the real @shadcn/react@0.3.0 MessageScroller with the transcript's own child structure — eight messages of history, 600px viewport — reading the position of the question just sent.

Before, on main:

step scrollTop question, relative to viewport top
after send 600 72px
first token 0 672px below the fold
send while scrolled up 812 72px
first token 114 770px below the fold

After:

step scrollTop question, relative to viewport top
after send 600 72px
first token 600 72px
send while scrolled up 812 72px
first token 812 72px
send from the bottom 1023 72px
first token 1023 72px

Every send lands identically regardless of the reader's scroll position, and holds through the answer.

The Thinking line now sits 24px under the question — one gap-6 — with the scroller's spacer below it, rather than 529px down with the spacer in between.

The send scroll was sampled mid-animation to confirm it eases rather than jumps: 85, 155, 386, 494, 536, 570, 589, 596, 600, 600.

bun run typecheck and bunx biome check are clean. bun test shows no new failures — the suite has pre-existing failures in the scheduler tests that need a database, nine with this change and eleven on stock main.

fix.mp4

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.

The transcript jumps to the top of the conversation when a Bot's first token arrives

1 participant