From 4cfabec7a4c6eafe53d9226753aef7991875bae9 Mon Sep 17 00:00:00 2001 From: Greg Pomerantz Date: Sun, 16 Aug 2026 09:45:51 -0400 Subject: [PATCH] Doc: record Phase 2 on-device IME validation (PASSING) + 3 bugs found - IME commit path validated on emulator (Gboard/API 35): single-char, multi-char (human cadence), deletion (deleteSurroundingText->EditEvent), and Unicode all commit correctly with cursor in sync; autosave persists. - Build recipe scripted (gogio + apktool MANAGE_EXTERNAL_STORAGE + apksigner). - Bugs: (1) open-file crash via Termux bridge -> fixed (c9c0d47); (2) viewport opens at EOF because the opening tap leaks into the editor (open, Phase 3); (3) rapid synthetic IME commits desync via per-frame snippet re-push (edge). --- doc/development_plan.md | 74 ++++++++++++++++++++++++++++++++++------- 1 file changed, 62 insertions(+), 12 deletions(-) diff --git a/doc/development_plan.md b/doc/development_plan.md index 49a3c1c..d894c5f 100644 --- a/doc/development_plan.md +++ b/doc/development_plan.md @@ -1,7 +1,9 @@ # Development Plan: reach a lean, usable Android text editor -Status: v2, 2026-08-16. Written against the **live** repo `/home/gmp/pad` -(HEAD 03fb638). v1 (the widget-rebuild plan) is superseded — see §12 for why. +Status: v2, 2026-08-16 (Phase 2 on-device IME validation complete). Written +against the **live** repo `/home/gmp/pad`. v1 (the widget-rebuild plan) is +superseded — see §12 for why. Phases 0–2 done; Phase 3 (large-file + the +viewport-on-open bug) is next. ## 1. Decision summary (updated) @@ -123,16 +125,60 @@ window (the IME discarding and re-anchoring the snippet — a rare case) is clamped to the window end by the byte-conversion, so it degrades rather than corrupting; the common within-window path is exact. -### Phase 2 — emulator verification (the observation loop, §9.3) -1. Install Android SDK + NDK + platform-tools; create an x86_64 AVD (16 GB VM, - KVM verified present). -2. Build the APK (`GOOS=android go build ./cmd/pad` with `ANDROID_NDK_HOME`), - install, launch. -3. Drive + observe: state-dump debug flag (add in Phase 1 as a debug build), - logcat, `dumpsys gfxinfo`, screenshot→PIL/OCR. -4. **IME experiment with the AOSP keyboard:** `input swipe` over the keyboard area - and verify behaviorally (did the commit land, non-duplicated, cursor correct?) - via state dump + logcat — this is what confirms §4 works end to end. +### Phase 2 — emulator verification — DONE (IME commit path validated on-device) +Environment (this 16 GB VM): Android SDK + NDK 27 + platform-tools + emulator + +API 35 `google_apis/x86_64` system image installed to `~/android-sdk`; AVD +`pad_avd` (Pixel 6 profile) boots with KVM (`/dev/kvm` chmod 666). Build recipe: +`gogio -target android -targetsdk 35 -arch amd64` → inject +`MANAGE_EXTERNAL_STORAGE` via apktool → sign with the debug keystore → +`adb install -r` (scripted in `/tmp/build_pad.sh`). + +**On-device IME validation — PASSING** (Gboard, API 35; observed via the gated +`imeDebugLog` trace + logcat + autosaved file diff + screenshots): +- Browser lists `/storage/emulated/0/Notes`; a row tap opens the file in the + in-app editor (see the open-file fix below). +- Editor focus → `key.FocusCmd`/`FocusFilter` → Gboard shows (soft input up). +- **Single-char commit** (tap a Gboard key): one `EditEvent{Range:{c,c},Text}` → + `HandleReplaceRange` inserts at the caret; buffer + cursor + autosaved file all + correct. +- **Multi-char commit** (tap keys with human ~0.6 s gaps): cursor advances in sync + (c→c+1→c+2…), each char lands at the right byte offset, file correct. +- **Deletion** (Gboard backspace): routed through the IME as + `EditEvent{Range:{i,i+1},Text:""}` (i.e. `deleteSurroundingText`) → + `HandleReplaceRange` deletes the range correctly. +- **Unicode** content (`héllo wörld 日本語`) displays and the buffer stays + consistent through edits around it. +- Conclusion: §4 (the IME wiring gap) works end to end for a real IME's commit + path. Swipe-typing and autocorrect use the *same* commit path + (`commitText`/`setComposingText` → `EditEvent`), so they are covered by this; + a real-device final sign-off with a swipe/autocorrect IME is still worth doing. + +**Bugs found + fixed on-device (Phase 2):** +1. **Open-file crash (fixed, `c9c0d47`):** browser-row taps were routed through + the Android Termux bridge (`ui.OpenFile = openfunc`), which built a `file://` + Intent URI and crashed on Android 7+ with `FileUriExposedException`, and it + bypassed the editor entirely. Fix: `ui.OpenFile` now calls the in-app + `editor.OpenFile(path)`; the Termux bridge is retained as a dormant + `TheState.open` hook. +2. **Viewport opens at EOF (open bug, Phase 3):** after a file opens, the + *opening* tap leaks into the now-visible editor and `SetCursorFromPoint`/scroll + moves the viewport to the tapped row (below a short file's content) → the + content area renders blank (`VisibleByteRange` lands on a past-EOF visual + line; `visibleContent (0)`). Workaround used during testing: swipe to top. + Root cause is the tap that switches `page=EditorPage` also being delivered as an + editor tap. Needs: ignore the opening tap in the editor (or clamp + `SetCursorFromPoint`/scroll so a short file that fits the viewport never + scrolls). +3. **Rapid synthetic IME commits desync (edge case):** `adb shell input text + "…"` fires per-char commits faster than a frame; the per-frame `SnippetCmd` + re-push resets the IME's cursor, so fast commits interleave/corrupt + (observed `" IME123"` → `E123IM…`). Real typing (human cadence) stays in sync + (validated above). Robustness note for Phase 3: only re-push the snippet when + it actually changes, or suppress re-push mid-composition. + +Observation loop that worked (no state-dump flag needed in the end): logcat +(`imeDebugLog` + `HandleKeyDown` + `VisibleByteRange`), autosaved-file diff via +`adb shell cat`, and screenshots (vision, auxiliary). ### Phase 3 — large-file validation + honest size limit 1. Open a real **10 MB** file via the chunked buffer; verify smooth scroll + edit @@ -255,6 +301,10 @@ within ~1 s. Repo: `go test -race ./...` green. to stay fully in memory (the ~50 MB target is borderline; see §6). - **AOSP keyboard** is a proxy for real IMEs (Gboard, etc.); final swipe/autocorrect sign-off needs a real device with a real IME. +- **Viewport opens at EOF** (Phase 2, open): the opening tap leaks into the editor + and scrolls a short file past its content → blank editor until you scroll to top. + Blocks a clean first impression; fix in Phase 3 (ignore the opening tap / clamp + scroll for files that fit the viewport). See §5 Phase 2 bugs. ## 12. Why v1 (widget rebuild) is now a fallback, not the plan v1 was written against a stale snapshot (May 31) and concluded "delete