* fix(mobile): stop Android from selecting words while the chat transcript scrolls
On Android every paragraph, heading, quote, code block and table cell in
the native chat transcript was a selectable TextView. Android starts a
word selection, with the magnifier, on a double tap or a long press, and
two flicks in the same spot while scrolling a FlatList register as a
double tap, so scrolling the chat kept selecting words. iOS is unaffected:
its UITextView path arbitrates scroll against selection itself.
Android now renders transcript text without inline selection: one gate in
MarkdownText covers every selectable span, and the user bubble follows
it. A long press on a message opens a sheet with "Copy message" and
"Select text", the latter a screen whose only content is one selectable
Text, so a selection can only start where the user asked for it. The
message row owns that sheet and mounts it only while open. iOS and web
keep their inline selection and get no long-press handler.
Verified on a Pixel 10 Pro Fold (Android 17): an adb double tap on the
transcript selects nothing, the same double tap inside "Select text"
selects a word, tool rows inside the bubble still expand on tap, and the
long press opens the sheet.
* fix(mobile): scope the Android selection gate to the transcript and route span long presses
Review follow-ups on #22871:
- The gate now applies only where `rangeSelectable` is passed (the chat
transcript). Task comments and file previews keep their selectable text
on Android as before.
- On the Android transcript, spans that take taps (links, file paths)
also take the row's long press, so a link under the finger no longer
swallows the copy/select sheet.
- Copied text keeps its whitespace; only whitespace-only blocks are dropped.
- The Android markdown test compares `String(node.type)` instead of a
type assertion, which the changed-code quality gate rejects.
* fix(mobile): route long presses on Markdown images to the row on Android
An image block is a Pressable of its own, so on the Android transcript it
now carries the row's onLongPress like tappable spans do; a long press on
an image opens the copy/select sheet instead of being swallowed. Test
extended with an image block.
* test(mobile): pin the Android long press from a chat row to its actions sheet
The existing row suite runs as iOS, where the bubble has no long press.
This one runs as Android: the bubble's long press mounts the actions
sheet with the message, the markdown receives the same handler, closing
unmounts the sheet, and the user bubble carries no inline selection.
* fix(mobile): preserve Android message selection while replies stream
---------
Co-authored-by: Neil <neil@stably.ai>