fix(ios): clear a long text value wherever the caret lands
Summary
On iOS, fill over a value longer than ~112 characters left part of it behind, so new text was appended (#3851). One delete burst is capped at 120, and the caret edge-tap lands mid-text in a field too narrow for its value.
After the tap, the clear now selects all and deletes through clearUntilEmpty, the synthesized route's loop renamed (up to 4 passes, since a fresh React Native process drops its first select-alls). Only readable values take this path, because a read proves the field emptied; masked values keep the old clear. Leftover text goes to delete bursts that re-read after each burst, for at most length/120 + 2 rounds. The clear posts and reads through the caller's TextEntryReader, so resolveTextEntryInput decides which input it may reach. tvOS and macOS get the re-reading bursts but not select-all.
Fixes #3851
Validation
At 9797ef6d0 vs main 6ea9f8173, iOS 26.5 simulator, test app Form, a fresh app process per run: fill 501, fill "", fill 500.
| Field | Main: after clear / fill 500 | Head |
|---|---|---|
field-name | 430 / 1336, clear exit 1 | 0 / 500 |
field-notes | 457 / 1376, clear exit 1 | 0 / 500 |
Every head clear logged outcome=cleared (2 passes on the first clear per process, then 1).
- All 104 text-entry XCTests pass. The long-value tests fail with the select-all block disabled.
- Short replacement clear: ~1.09 s → ~1.24 s; the first per process ~2.0 s.
check:affected --run --head 9797ef6d0passed (exactCommit=true).
Risks:
- Not run on a physical device, tvOS, or macOS.
- A readable field that ignores ⌘A costs ~3 s.
- Long masked values are still partly left, as on main.
Written with Claude Code (Opus 5.5).