Repository navigation
Conversation
…ong-pressed Tapping selectable text in a long agent answer, or long-pressing it to copy, scrolled the chat away: text near the top of an answer sent the list to the bottom, text near its end sent it back up. Selectable text takes focus on a tap and on a long-press, and the ScrollView then brings the focused view into view. It works out where that view is from layout positions alone (ReactScrollView.scrollToChild, View.requestRectangleOnScreen), skipping every transform. An inverted FlashList rotates each row by 180 degrees, so inside a row the position comes out mirrored across the row's middle. In an answer taller than the screen the mirrored position is off screen, and the list jumps there. The vertical ScrollView is now a ReactScrollView subclass. When a transform sits between the focused view and the scroll view, it maps rectangles through each view's matrix, so dragging a selection handle still scrolls toward where the text really is. In touch mode it does not scroll for a focus change at all: focus lands under the finger, or the framework moves it on its own when a recycled row takes the focused text with it, handing it to the first focusable view, far from the reader. Keyboard navigation still scrolls, mapped the same way. Scroll views with no transform in the path behave exactly as before. A config plugin copies the Kotlin sources into the generated project and registers the package ahead of MainReactPackage, because React Native uses the first package that answers for "RCTScrollView". This needs a native build; an OTA update cannot carry it. Generated with [Claude Code](https://claude.ai/code) via [Happy](https://happy.engineering) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Co-Authored-By: Happy <yesreply@happy.engineering>
This branch has not been deployed
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 Android, tapping or long-pressing selectable text in a long agent answer scrolls the chat away before anything can be selected: text near the top of an answer sends the list to the bottom, text near its end sends it back up. Selectable text takes focus on a tap and on a long-press, and
ReactScrollViewthen brings the focused view into view, working out where it is from layout offsets only (ReactScrollView.scrollToChildviaoffsetDescendantRectToMyCoords, andTextView.bringPointIntoViewviaView.requestRectangleOnScreen), which skip transforms. The invertedFlashListrotates every row by 180°, so inside a row that position comes out mirrored across the row's middle, and in an answer taller than the screen the mirrored spot is off screen. This replaces the Android vertical ScrollView with aReactScrollViewsubclass that, when a transform sits between the focused view and the scroll view, maps rectangles through each view's matrix (so dragging a selection handle toward an edge still scrolls the right way) and does not scroll for a focus change in touch mode. A config plugin copies the Kotlin into the prebuilt project and registers its package ahead ofMainReactPackage, because the bridgeless view-manager lookup takes the first package that answers forRCTScrollView. Scroll views with no transform in that path behave exactly as before. Agent answers keep native selection, as0e8392314left them; only the scroll changes. It is a native change, so it ships with a build, not an OTA.Proof
Android emulator (API 36, x86_64 debug build of this branch on
main8517ab23). For the "main" runs the same build had the plugin's oneadd(0, …)line commented out, so the stock view was in use;dumpsys activity topshowscom.facebook.react.views.scroll.ReactScrollViewthere andengineering.happy.scroll.TransformAwareScrollViewwith this PR. The session's latest message is a 40-paragraph agent answer.Why no scroll at all on a touch-mode focus change
Mapping the rectangle correctly is not enough on its own. In touch mode, focus lands either under the finger, which is already on screen, or where the framework puts it by itself: when a recycled row unmounts the focused text,
ViewGroup.removeViewInternalcallsrootViewRequestFocus(), and the first focusable view it finds is usually nowhere near the reader. With the scroll mapped but still performed, that moved the list by 10,472 px while I was scrolling past the row I had tapped earlier:Trace (an earlier revision of this branch that still scrolled on focus)
Keyboard and D-pad navigation run outside touch mode and still bring the newly focused view into view, with the mapped rectangle.
🤖 Generated with Claude Code