Skip to content

fix(chat-thread): stop the thread yanking back to an earlier prompt - #4026

Draft
adboio wants to merge 3 commits into
mainfrom
posthog-code/fix-chat-thread-anchor-scroll-jumps
Draft

fix(chat-thread): stop the thread yanking back to an earlier prompt#4026
adboio wants to merge 3 commits into
mainfrom
posthog-code/fix-chat-thread-anchor-scroll-jumps

Conversation

@adboio

@adboio adboio commented Jul 30, 2026

Copy link
Copy Markdown
Contributor

Problem

In a task's chat thread the view very often scrolls itself back to the top, and the scroll-to-bottom arrow only moves you one prompt further down instead of to the bottom.

Both come from marking user rows as scrollAnchor. The quill/shadcn scroller engine treats an anchored row as one it owns the scroll position for: on each content change it pins the first anchor it has not pinned before to the top of the viewport. Anchors that exist at mount are never recorded as pinned, so the first row swap after opening a task jumps to the very first prompt, the next swap to the second, and so on. That also eats the scroll-to-bottom: you land at the bottom, then the next swap moves you to the next un-pinned prompt.

Separately, the arrow never re-armed auto-follow (the code comment claimed it did), so hitting bottom mid-reply lasted exactly until the next streamed row.

Why: reported as extremely disruptive while reading a running task — you lose your place constantly and can't get back to the live tail.

Changes

  • No thread row is a scrollAnchor any more, which removes the whole class of engine-initiated scroll jumps.
  • The current turn (minimap highlight, and the anchor handed to the windowed body when a thread crosses the virtualization threshold) is measured from the viewport instead, reusing the computeStickyAnchor helper the virtualized body already uses.
  • The scroll-to-bottom button re-arms auto-follow, so it keeps the bottom while a reply is still streaming.
  • MessageMinimap no longer subscribes to the engine's per-scroll visibility state — both bodies now pass the anchor in.

How did you test this?

  • pnpm typecheck (all 24 packages), pnpm lint, pnpm --filter @posthog/ui test (2411 tests) — all pass.
  • Not manually verified in the running app; the fix is derived from reading the scroller engine's content-change handling, so a dogfood pass on a long thread is worth doing before undrafting.

Automatic notifications

  • Publish to changelog?
  • Alert Sales and Marketing teams?

Created with PostHog Code

The scroller engine treats a row marked `scrollAnchor` as one it owns the
scroll position for: on every content change it pins the first anchor it has
not pinned before to the top of the viewport. Anchors present at mount are
never recorded as pinned, so the first row swap after load scrolls the thread
to the very first prompt, the next swap to the second, and so on — and a
scroll-to-bottom lasts only until the next swap moves you one prompt down.

Rows are no longer anchors, and the current turn (minimap highlight + the
virtualization handoff anchor) is measured from the viewport instead, reusing
the same `computeStickyAnchor` the windowed body already uses.

The scroll-to-bottom button now also re-arms auto-follow, so it holds the
bottom while a reply is still streaming rows in below.

Generated-By: PostHog Code
Task-Id: ae1e23ab-61e6-4a59-bc42-77667af0baab
@trunk-io

trunk-io Bot commented Jul 30, 2026

Copy link
Copy Markdown

Merging to main in this repository is managed by Trunk.

  • To merge this pull request, check the box to the left or comment /trunk merge below.

After your PR is submitted to the merge queue, this comment will be automatically updated with its status. If the PR fails, failure details will also be posted here

@github-actions

github-actions Bot commented Jul 30, 2026

Copy link
Copy Markdown

React Doctor found 1 issue in 1 file · 1 warning.

1 warning

src/features/sessions/components/chat-thread/ChatThread.tsx

Reviewed by React Doctor for commit d933096.

adboio added 2 commits July 30, 2026 18:03
Scanning `[data-message-id]` under the viewport visited every node inside every
turn card — thousands of elements on a long thread — once per scroll frame.

The rows are the content element's direct children and sit in ascending vertical
order, so the scan walks those children backwards and stops at the first prompt
at or above the peek line: at the live tail that is the first row it reads, and
it never touches the thread above the anchor.

Generated-By: PostHog Code
Task-Id: ae1e23ab-61e6-4a59-bc42-77667af0baab
Generated-By: PostHog Code
Task-Id: ae1e23ab-61e6-4a59-bc42-77667af0baab
@posthog

posthog Bot commented Jul 31, 2026

Copy link
Copy Markdown
Contributor

🦔 ReviewHog reviewed this pull request

Nothing worth raising this time, so here's a calming picture instead:

Someone relaxing in a sunny garden

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