Builds out host-drawn text selection for embedded napplet/nsite/browser surfaces toward native parity, and fixes the bugs found while exercising it. - Magnifier loupe (#4): EmbeddedMagnifier + provider-side pixel capture (EmbeddedMagnifierProbe) shipped over IPC for both embed paths; the caret/selection handles drive it via OnMagnify. - SelectionUiState: single source of truth for the overlay show/hide rules (insertion caret / in-field range / page-text range + dragging/scrolling). - EmbeddedSelectionDrag: suspends the nav drawer's edge swipe while a handle is dragged (auto-scroll #9). Bug fixes: - No more overlay blink on word-select: the shim's selection-reveal scrolls (a textarea auto-scrolling to show a forming/re-asserted range) no longer trip the hide-on-scroll path, and the hide self-heals instead of being re-armed indefinitely. - RemoteImeView debounces the range-lost signal so a transient collapse that gets re-asserted doesn't flicker the handles/toolbar. - Focusing a field clears any page-text selection (shim + host), so the stale page handles/Copy bar no longer linger above — and stop stealing drags from — the field overlays; also cancels any in-flight scroll-hide on focus. - Caret insertion-handle drag now actually moves the caret: read the pointer delta with positionChangeIgnoreConsumed() before consuming, so the value isn't zeroed by our own consume (or the sandbox surface consuming the move). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
IME / text-selection test harness
A single-file web page (index.html) for exercising and profiling the embedded
WebView IME + text-selection relay (see
amethyst/plans/2026-06-25-embed-text-selection-native-parity.md). It has a
plain <input> and a <textarea> plus an on-page green log that records, with
millisecond timestamps:
- focus/blur,
selectionchange,keydown/beforeinput/input, composition events, and the resultingvalue/selection — to catch erase, caret-jump, and focus-transfer regressions; - paint latency (
requestAnimationFrameafter each DOM change) — the metric that exposed the first-letter freeze; - long-task + main-thread-block detectors and a focus/selection heartbeat — to catch anything stalling the WebView main thread or spontaneously moving focus/selection.
The log lines are tagged [ImeDiag] and also go to console.log, so they show
up in adb logcat (the :napplet process owns the WebView console). Nothing
here ships in the app — it's a dev tool, which is why the [ImeDiag] strings
live only under tools/.
Run it
-
Serve this directory over HTTP from your dev machine:
cd tools/ime-test && python3 -m http.server 8765 -
Reach it from the device/emulator:
- Emulator: the page is at
http://10.0.2.2:8765(10.0.2.2is the emulator's alias for the host loopback). - Physical device (USB):
adb reverse tcp:8765 tcp:8765, then the page is athttp://localhost:8765.
- Emulator: the page is at
-
Open that URL as an embedded tab (this is the path that uses the relay — not a full-screen activity):
- Open the in-app browser (
BrowserScreen) and type the URL into its address bar. The embedded browser handleshttp/https, so it loads into the:nappletSurfaceControlViewHost surface.
To compare against native behavior, open the same URL in a full-screen activity (where the WebView renders in-window with the native keyboard) — that is also how you reproduce the full-screen round-trip highlight bug (open full-screen,
back, then selection highlight is dead across all embeds). - Open the in-app browser (
Reading the log
INPUT … val=… sel=…right after a keystroke with the right value = no erase.PAINT-LATENCY Nmsspiking to ~1000ms = the first-letter freeze (should stay low now that the surface no longer resizes on IME show).MAINTHREAD BLOCKED/LONGTASK= something is stalling the WebView thread.HEARTBEATlines changing while idle = spontaneous focus/selection drift.