Pad/doc/README.md
Greg Pomerantz ab60bb7f16 doc: screen coordinate reference (screen vs display vs app-local px)
The coordinate-space confusion (screen px 1080x2400 for input tap/
screencap, display px 900x2000 for reading PNGs, app-local px offset by
the 128px status bar, pt at density 2.625) cost real debugging time.
Wrote it down in doc/README.md as the permanent reference:

- the four spaces + conversions (display x1.2 = screen; screen Y-128 =
  app-local; /2.625 = pt)
- the practical tap rule: read (x,y) off the PNG, x1.2, input tap --
  screenshot px ARE screen px, no 128 offset for tap-what-you-see
- fixed geometry (status/nav bars, Gboard rows, editor region in pt),
  with re-measure guidance
- tap-test pitfalls (first-tap-swallowed, first input text can fail,
  wrong-place text = cursor/selection suspect not tap math, don't
  memorize drifting content positions)

All keyboard row values re-measured from a fresh screenshot and verified
live: tapping the Q key at (62, 1712) and backspace at (990, 2020) both
landed first try (file content confirmed). This also exposed that the
old notes had mixed display-px row values with screen-px key values in
the same list -- exactly the confusion this section eliminates.

Also fixed the stale Phase 6 'letterboxed window' note in
development_plan.md (window is full-screen; only the 128px status-bar
offset is real; content positions drift; the OpenFileChan logcat line
was removed) and pointed emu.sh's tap help at the x1.2 rule.
2026-08-16 22:19:40 -04:00

144 lines
7.2 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Pad documentation
Three documents, kept at the level of *what, why, and invariants* — not
line-by-line code — so they stay true as the implementation evolves.
| Doc | What it is |
|---|---|
| [`spec.md`](./spec.md) | What the app **actually does**, measured performance, the code layout, and an explicit list of deferred features. |
| [`architecture.md`](./architecture.md) | How it works: single-owner concurrency model, channel topology, Frame handoff contract, ownership rules, editor/browser/render internals. |
| [`development_plan.md`](./development_plan.md) | The active plan: completed phases, remaining work, and the on-device observation loop. |
## Package inventory
| Package | Role |
|---------|------|
| `internal/editor` | Chunked buffer, line index, virtualized viewport, IME ops, autosave, logic goroutine, state, Frame |
| `internal/browser` | Directory browsing, search, sort, pagination, browser state machine |
| `internal/ui` | Element tree, frame layout, renderer (op-based), IME wiring, gestures |
| `internal/perf` | Default-off performance profiler (per-logic-frame cadence + scroll state → CSV) |
| `internal/io/pool` | Worker pool (8 workers, 2 priority lanes) + file/dir tasks |
| `internal/test/e2e` | Harness driving the real `Logic` + `Inspect` |
| `cmd/pad` | `main.go` (entry), `impl_android.go` (base path) |
## Documentation policy
1. **Docs describe invariants and contracts, not code lines.** If a document
has to change on every refactor, it is too detailed — delete it or raise
its level of abstraction. Point-in-time implementation plans are deleted
once implemented (the previous `*_implementation_plan.md`,
`touch.md`, `element_model.md`, `layout_rendering.md`,
`virtual_scroll_render_optimization.md`, and `conflict_resolution.md` were
removed in the 2026-08 doc reorganization for this reason).
2. **Unbuilt behavior is not spec'd.** Requirements that are not in the code
live in `spec.md` §7 (deferred), never as if they worked.
3. **Code wins.** When doc and code disagree, fix the doc.
## Build & install (Android, this workstation)
Prereqs on this box (already installed): JDK 17, Go, Android SDK at
`~/android-sdk` (platform-tools, build-tools 35.0.0, emulator, NDK),
`gogio@v0.10.0` in `~/go/bin`, debug keystore at
`~/.android/debug.keystore`. Env vars are set in `~/.bashrc`.
**Build + install with `./scripts/build_emu.sh`** (add `--no-install` to
build only). It is self-contained: checks the prereqs, auto-downloads
apktool v3.0.3 to `~/android-sdk/tools/apktool.jar` if missing, and uses a
temporary work dir. Output: `cmd/pad/pad-emu.apk`.
What it does, and why (gogio cannot inject manifest permissions):
1. `gogio -target android -targetsdk 35 -arch amd64` → raw APK (from `cmd/pad/`)
2. `apktool d` → decode
3. Inject `<uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE"/>`
(without it, Android 11+ blocks listing `/storage/emulated/0`)
4. `apktool b` → rebuild
5. `apksigner sign` with the debug key → `cmd/pad/pad-emu.apk`
6. `adb install -r` (skipped with `--no-install`)
## Emulator + on-device debug: `scripts/emu.sh`
All emulator lifecycle and on-device debug operations go through
`scripts/emu.sh` (headless AVD `pad_avd`, API 35): `up` / `down` /
`status`, `app start|stop|restart`, `shot`, `log`, `tap X Y`, `type TEXT`,
`cmd <top|bottom|frac F|dp N>` (one-shot editor debug command),
`perf on|off|pull` (in-app profiler), `push`/`pull` (Notes files). See the
script header for the full list and the device paths it encodes.
Snapshot behavior: the AVD auto-saves an instant-boot snapshot on clean
`down` (restore ~10 s vs ~20 s cold boot). A corrupted snapshot makes the
emulator segfault during restore; `up` detects that and falls back to a
cold boot, and the next `down` re-saves a good snapshot (self-healing).
Always stop with `down` — SIGKILLing the emulator corrupts the snapshot.
App package/activity: `pad.pad / org.gioui.GioActivity`. Default root
directory: `/storage/emulated/0/Notes`.
## Screen coordinates
Three coordinate spaces are in play; mixing them up is the #1 cause of
tap-test errors. (Measured 2026-08-16 on `pad_avd`; re-derive with
`adb shell wm size`, `adb shell wm density`, `dumpsys window`, and a fresh
screenshot with the keyboard open if the device config ever changes.)
| space | size | used by |
|--------------|-----------|------------------------------------------|
| screen px | 1080×2400 | `adb input tap`, `screencap` output |
| display px | 900×2000 | what you see when reading a PNG (vision) |
| app-local px | 1080×2272 | what Gio receives (`evtPos`) |
| pt | — | app layout units |
Conversions:
- display → screen: **× 1.2** (both axes)
- screen → app-local: **Y 128** (surface starts below the status bar; X unchanged)
- app-local px → pt: **÷ 2.625** (density 420)
**The tap rule (covers ~95% of needs):** to tap something visible, read its
(x, y) off the PNG you are looking at, multiply both by **1.2**, and
`input tap x y`. Screenshot px *are* screen px — no 128 offset involved.
The 128 offset only matters when converting screen ↔ app-local (e.g.
predicting which text line a tap will hit, or reasoning about `evtPos`
values). The app itself is internally consistent — rendering and input
share the same origin — so the offset only bites at the adb/screenshot
boundary.
Fixed geometry (screen px unless noted):
- status bar: top 128 px; nav bar: bottom 63 px
- Gboard (no suggestion bar): keyboard top ≈ 1518; rows QWERTY ≈ 1716,
ASDF ≈ 1880, ZXCV ≈ 2020 (backspace key ≈ (990, 2020)); space/enter
row ≈ 2168; icon toolbar ≈ 1548. Re-measure from a screenshot if Gboard
changes.
- App space (pt): editor region starts at (10, 62); line height 16.8 pt.
Tap-test pitfalls:
- The first tap right after launch, file open, or logcat clear is often
swallowed — re-tap before concluding anything is broken.
- The first `input text` after a cursor move can silently fail — retype.
- `input text` goes to whatever has IME focus; if typed text lands in the
wrong place, suspect cursor/selection state (see the GlyphLayout
window-relative invariant in `architecture.md`), not tap math.
- Don't memorize content positions (file rows, buttons) — they drift with
content. Measure from the current screenshot with the ×1.2 rule.
## On-device observation loop (quick reference)
Gio renders into one GL surface, so the authoritative debug signals are
data, not pixels (use `scripts/emu.sh log|shot|perf|cmd` for the first
four):
- `logcat -s pad.pad` — app log (errors, limits, recovery; the normal path
is quiet by design).
- `input tap|swipe|text` — drive the UI. **All tap coordinates are screen
px: read the position off the screenshot, ×1.2** (full reference:
§Screen coordinates). The first tap right after launch/open is sometimes
swallowed — re-tap.
- Autosave debounce is 1 s: **wait ~1.6 s before reading a file back from
disk** after typing.
- Memory: `adb shell dumpsys meminfo pad.pad` (watch PSS/RSS; the 3.8 GB
emulator OOMs the app above ~2.5 GB RSS).
- Screenshots are an auxiliary check only — state, logcat, and file diffs
are authoritative.