Skip to content

fix(react): don't re-anchor historical turns on same-count content changes#11182

Open
devnomic wants to merge 1 commit into
shadcn-ui:mainfrom
devnomic:fix/message-scroller-stale-anchor-reanchor
Open

fix(react): don't re-anchor historical turns on same-count content changes#11182
devnomic wants to merge 1 commit into
shadcn-ui:mainfrom
devnomic:fix/message-scroller-stale-anchor-reanchor

Conversation

@devnomic

Copy link
Copy Markdown
Contributor

Fixes #11181

The bug

MessageScroller scrolls a historical anchor to the reading line (usually the very top of the thread) whenever a content mutation replaces one child with another at the same item count. The canonical trigger is the standard chat pattern: send appends the user turn + a pending loader, then the first streamed chunk swaps the loader for the assistant item in one commit. Every turn, the viewport jumps to the oldest unhandled user message.

Cause: the first-content branch of handleContentChange never adds initially mounted anchors to handledScrollAnchorsRef, and the same-count branch scans the whole list for any never-handled anchor — so it matches anchors that mounted with the initial content instead of only just-swapped-in ones.

The fix

Mark every current anchor as handled at the end of each handleContentChange pass (new markScrollAnchorsHandled helper in geometry.ts). An anchor still gets exactly the reconcile pass that introduced it:

  • appended anchors are still scrolled to the reading line (existing behavior, existing tests),
  • an anchor swapped in at the same count (optimistic → confirmed user message) is still anchored, because marking runs after the branch logic within the same pass — covered by the new still anchors a new turn swapped in at the same count test,
  • but a later mutation can never re-anchor an older turn — covered by the new does not re-anchor a historical turn when an item is swapped at the same count test, which fails on main and passes with this change.

The two inline handledScrollAnchorsRef.current.add(anchor) calls became redundant and were removed.

Tests

  • pnpm --filter @shadcn/react test — 60 passed
  • pnpm --filter @shadcn/react test:browser — 25 passed (both new regression tests fail without the source change)
  • pnpm --filter @shadcn/react typecheck — clean

@vercel

vercel Bot commented Jul 15, 2026

Copy link
Copy Markdown
Contributor

Someone is attempting to deploy a commit to the shadcn-pro Team on Vercel.

A member of the Team first needs to authorize it.

@github-actions

Copy link
Copy Markdown
Contributor

Can you sign the commits please? See https://docs.github.com/en/authentication/managing-commit-signature-verification/signing-commits. Thank you.

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.

[bug]: MessageScroller re-anchors a historical turn (jumps to top) on same-count content changes

1 participant