fix(ui-kit-chat): user input detaches thread auto-follow so touch scrolling wins over streaming re-pin - #154
Merged
Merged
Conversation
…olling wins over streaming re-pin
Contributor
There was a problem hiding this comment.
Pull request overview
Adds gesture-aware thread auto-follow detachment to prevent streaming content from overriding user scrolling.
Changes:
- Tracks touch, wheel, and keyboard scroll intent.
- Adds six Chromium browser tests.
- Adds browser-test dependencies and release changeset.
Reviewed changes
Copilot reviewed 5 out of 6 changed files in this pull request and generated 1 comment.
Show a summary per file
| File | Description |
|---|---|
.changeset/thread-scroll-user-detach.md |
Documents the patch. |
packages/ui-kit-chat/package.json |
Adds browser-test dependencies. |
packages/ui-kit-chat/src/behaviors/use-thread-auto-scroll.ts |
Implements gesture detachment and reattachment. |
packages/ui-kit-chat/test/thread-auto-scroll.browser.test.tsx |
Tests auto-follow behavior in Chromium. |
packages/ui-kit-chat/vitest.config.ts |
Adds a Playwright browser project. |
pnpm-lock.yaml |
Locks the new dependencies. |
Files not reviewed (1)
- pnpm-lock.yaml: Generated file
Comments suppressed due to low confidence (1)
packages/ui-kit-chat/src/behaviors/use-thread-auto-scroll.ts:129
- Downward keys (
ArrowDown,PageDown, andEnd) also detach while already at bottom. If a stream mutation arrives before keyup, the viewport becomes geometrically away from bottom, so the keyup recomputation cannot reattach and a key intended to stay at/latest breaks auto-follow. Only upward keys should detach from the bottom; downward keys should detach only when the viewport is already away from it.
const handleKeyDown = (event: KeyboardEvent) => {
if (SCROLL_KEYS.has(event.key)) detach()
}
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Contributor
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 5 out of 6 changed files in this pull request and generated no new comments.
Files not reviewed (1)
- pnpm-lock.yaml: Generated file
Comments suppressed due to low confidence (1)
packages/ui-kit-chat/src/behaviors/use-thread-auto-scroll.ts:168
- Downward keys are treated as detach intent even when the viewport is already at the bottom. If a streaming mutation lands between
keydownandkeyup,userDetachedblocks the re-pin;keyupthen observes the newly increased distance from the bottom and cannot reattach, so ArrowDown/PageDown/End can permanently stop following despite never moving the reader away. Apply the same direction-aware handling as wheel input, and add a regression case that streams between a downward keydown/keyup at the bottom.
const handleKeyDown = (event: KeyboardEvent) => {
if (SCROLL_KEYS.has(event.key)) detach()
}
Merged
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
On iOS WKWebView the thread pane was un-scrollable during streaming: the auto-follow re-pins to bottom on every mutation, and release depended on a scroll event observed away-from-bottom - a race WKWebView loses because its scroll events arrive late/coalesced while mutations apply instantly. Desktop never showed it because Blink delivers scroll synchronously. The only gesture listener (pointerdown) never gated the re-pin.
Fix: an explicit userDetached flag set by real scroll intent (touchmove, wheel, scroll keys) gates the re-pin; it clears when the user lands back at bottom or explicitly follows again (the Latest pill / run start). The snap-back suppressor no longer applies during an active touch, so rubber-band bounces cannot mask release. Once a finger moves, no stream batch can drag the view back - the race is deleted, not narrowed.
Tests: new real-Chromium vitest browser project for the hook (6 behavior tests: touch/wheel/key detach, at-bottom + pill re-attach, tap does not detach), proven non-vacuous (gate reverted: 5/6 fail). Existing storybook play coverage for the hook still 4/4. Adds two test-only devDeps at pins already used by sibling packages.
Verified by orchestrator: full diff review + hard-rule greps (flagged hits are pre-existing lines), package suite rerun 57/57 incl. the chromium project, fallow pass 0 introduced.
🤖 Generated with Claude Code