Commit Graph

10 Commits

Author SHA1 Message Date
2a16c0017c Touch selection (v1): long-press/double-tap word selection, drag handles, floating copy/cut/paste menu
Implement the Android-native touch selection model, verified on-device:
- long-press selects the word under the finger (blank -> caret + paste-only
  menu); double-tap selects the word; drag handles resize the selection,
  drag the highlighted body to move it; floating menu offers copy/cut/paste
  (selection) or paste (bare caret), closing on any item tap.
- Renderer reports finger positions (app-local Dp) as tap/double-tap/
  long-press/selection-drag events; the logic goroutine owns all geometry
  (EditorRegion, menu rect, hit-testing, handles) and the renderer only
  draws the frame snapshot.
- Long press: 400 ms still-press on the editor, cancelled by movement
  (non-grabbing raw pointer probe) or by a scroll/handle grab. The main
  loop keeps invalidating while a press is pending (Gio renders on demand;
  a stationary finger produces no frames).
- Clipboard crosses the goroutine boundary via buffered channels
  (clipboardSetChan/pasteReqChan logic->main, pasteChan main->logic);
  main executes the Gio ops and, on Android, invalidates after ReadCmd
  because a queued transfer.DataEvent schedules no frame of its own.

Renderer fixes found while validating on-device:
- clickReg was stored by value in a map; range yielded copies so per-frame
  press bookkeeping (long-press state) was silently discarded. Now pointers.
- On Android a tap's press+release arrive in the same frame and
  gesture.Click/Drag return one event per Update call; without draining
  each gesture's queue every frame the release was lost on an idle window
  and every menu tap was swallowed (needed a second tap to 'rescue' it).
  Click and drag loops now drain to exhaustion (scroll already does).
- pointer.Filter queries must name Kinds: a zero-kinds filter matches
  nothing (the press-probe query was dead).
- Menu.Draw offsets items by the menu origin; the clippable drawElement
  branch registers SelDrag (handles now draw for the TextField).

Tests: internal/editor/touch_selection_test.go (word range, long-press,
double-tap, tap/menu guards, handle drags, menu actions, selection edits)
and internal/test/e2e/touch_selection_e2e_test.go; full suite green under
-race. Docs: spec.md §2.2 + §7, architecture.md §6.3a, development_plan.md
Phases 8-9.
2026-08-17 08:57:55 -04:00
ec11abf8f1 Add text selection, real-file e2e tests, and Android arrow-key support
Selection: shift+arrow extends a selection (absolute byte offsets,
anchor/caret model); insert/backspace/delete replace the selection; the
IME unions its reported range with the active selection; the highlight
is drawn in the TextField and the selection is pushed to the IME.

Android key input (the blocker found during on-device validation):
Gio v0.10 on Android (a) drops modifier state in the JNI bridge and
(b) wraps plain arrow-key presses in input.SystemEvent for focus
navigation, so arrow keys never reached the editor. main.go now
registers explicit named key.Filters for the four arrows (delivers the
press and suppresses the focus jump) and tracks the shift key itself.
Verified on the emulator: plain arrows move the caret, shift+arrow
shows a highlight, typing replaces the selection.

Real-file e2e tests (real on-disk files via the real FileSystem,
multi-chunk 256KB files, chunk-boundary and multi-byte edits) found
and fixed two real bugs:
  1. Line index: UpdateLineIndexAfterEdit only shifted offsets; edits
     involving newlines left it permanently inconsistent. Replaced with
     newline-aware UpdateLineIndexAfterInsert/UpdateLineIndexAfterDelete.
  2. Rune granularity: HandleBackspace/HandleDelete deleted one byte,
     corrupting multi-byte UTF-8 characters (e.g. a 2-byte char
     straddling a chunk boundary). Now rune-granular.

Also: airtight e2e harness load-wait (StatFile/ReadFile/BuildLineIndex
interleaving could satisfy the old condition early).

Full suite green under -race; on-device verified.
2026-08-17 00:32:53 -04:00
16c39f4221 doc: note GlyphLayout window-relative offset invariant
Add the invariant that GlyphLayout offsets are relative to
IMEWindowStartByte and that cursor positioning must add/subtract the window
base. Record the second tap-to-cursor fix (base offset) alongside the earlier
tapLocalY fix in the development plan.
2026-08-16 20:59:42 -04:00
be48ad8157 perf: default-off in-app profiler + debug scroll jumps; verify scroll perf & clamping
internal/perf: single-goroutine profiler (no locks). When enabled by the
/storage/emulated/0/PadPerf/enable marker it records one CSV row per logic
frame (seq, ms, frame delta, page, scroll Dp, max-scroll Dp, total lines,
visible byte range), logs a rolling ~1/s summary, and on Stop reports
nearest-rank p50/p90/p99/max. Flushes per row-batch but never fsyncs per
flush (avoids periodic hitches in the logic path).

editor: PerfRecord package-level hook (nil when off) + ProbeRecord;
Logic.emitFrame() now centralizes every frame emission so the profiler sees
each logic frame exactly once, on the owner goroutine. State gains
VisibleStart/VisibleEnd so the probe can confirm shaping stays
viewport-bounded.

logic: optional debug cmd poller (off by default) watches <dir>/cmd as a
one-shot file (top/bottom/frac <0..1>/dp <int>) and the owner applies a
clamped [0,MaxScroll] jump + frame. Enables deterministic large-offset scroll
tests without pixel taps.

main: wires the profiler when the marker file exists; logs present-fps
every 2s; stops the profiler on Destroy.

docs: README package inventory (add internal/perf); architecture §11
profiler facility; spec §5 invariant 7 (scroll always clamped to
[0,maxScroll]) + §6 measured rows; development_plan v5 + Phase 6 results.

Verified: go build/vet + full -race green. On-device 10 MB file:
logic-frame cadence flat across offsets 0.02->1.0 (no large-offset
degradation), visible byte range <=4.3 KB at every offset, clamping exact
across 2->130,955-line files, PSS plateaus ~250 MB (bounded, no leak);
profiler overhead negligible.
2026-08-16 16:53:28 -04:00
d3d11b5d20 Doc: reorganize — delete over-detailed docs, rewrite spec + architecture to match code
- Deleted (drift-prone point-in-time plans / dead specs):
  browser_implementation_plan.md, editor_implementation_plan.md,
  virtual_scroll_render_optimization.md, conflict_resolution.md,
  touch.md, element_model.md, layout_rendering.md, bugs.txt
- Rewrote architecture.md: single-owner/no-lock model, channel topology,
  Frame handoff contract, ownership rules, editor/browser/render internals,
  active vs dead task types, testing hooks.
- Rewrote spec.md: actual behavior (browser, editor, IME, autosave, 50 MB
  limit with measured numbers), actual code layout, invariants to preserve,
  explicit deferred-features table (undo, restoration, sync awareness).
- Added doc/README.md: doc index, documentation policy, build/install
  recipe, on-device observation loop.
- Updated development_plan.md: Phase 5 mostly done; §7 spec deltas written
  with two corrections (no undo at all; no external-change detection).
2026-08-16 13:22:21 -04:00
6a43e3c0db Add e2e test harness for testing editor logic without Gio display
- internal/test/e2e/: FrameCapture, Harness, ElementAssertions, helpers
- internal/editor/logic.go: Add done channel and Done() method for graceful shutdown
- internal/editor/mock_setup.go: Mock filesystem for tests
- internal/browser/: Browser layout and search logic
- internal/io/: Worker pool for async tasks
- Update architecture docs and spec
2026-05-31 08:56:30 -04:00
50e9ca5b34 Updates to rendering pipeline and documentation. 2026-05-25 17:40:35 -04:00
8bd9376fa9 Correct runtime architecture: batched input, config channel, frame receiver logic
- Update block diagram to show correct channel/mutex flow
- Frame receiver now calls w.Invalidate() inside the mutex lock
- Logic goroutine now waits on batched []InputEvent and configChan
- Main loop batches events and sends outside the lock
- Remove select-with-default batching from logic

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
2026-05-08 10:56:34 -04:00
f8f81b1fec Update architecture: input batching, priority workers, memory monitoring
- Add input batching to Logic goroutine via non-blocking select loop
- Add two-tier priority queue (high/low) to Worker pool
- Add §10 Memory Monitoring and Management (heap stats, debug page, future governor)
- Update diagram and channel topology to reflect new channels

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
2026-05-08 10:10:06 -04:00
4b048b679e Add runtime architecture specification
- New doc/architecture.md: concurrency model, goroutine responsibilities,
  channel topology, deadlock analysis, sync/async partitioning
- Update SPEC.md §3: split into code organization + runtime architecture,
  cross-reference to architecture.md

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
2026-05-08 08:24:04 -04:00