docs(ux): as-built for sub-arc 3 + arc close (relative/hybrid, protocol v14)

Record sub-arc 3: the shared LineNumberMode enum + number_for in
pmacs-protocol, the v14 mode bump, the cursor-line repaint-on-move
dependency, stable gutter width, and the binary-toggle + completion-picker
selection UX. Plus the arc-close note: Q#UX1 scored false (two protocol
bumps), and the deferred gutter-riding features.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014TXbAwk27agwhrNNrhLi2U
This commit is contained in:
Levi Neuwirth 2026-07-07 10:03:32 -04:00
parent 40ebdd8d7e
commit 076bdfc5f4
1 changed files with 44 additions and 0 deletions

View File

@ -281,4 +281,48 @@ while eyeballing this sub-arc against the two-frontend setup.)
highlight sticks / doesn't wrap on arrow-up; reproduces on the "normal" highlight sticks / doesn't wrap on arrow-up; reproduces on the "normal"
nav path but not the alternate one. Its own thread. nav path but not the alternate one. Its own thread.
**Sub-arc 3 — relative + hybrid line-number modes (TUI + GPU, protocol v14).**
The last two of the framed modes (Q#UX4): **Relative** (each line shows its
distance from the cursor line; cursor = 0) and **Hybrid** (cursor line shows
its absolute number, others relative — Vim `number`+`relativenumber`).
- **One enum, one number rule.** `LineNumberMode {Off, Absolute, Relative,
Hybrid}` + `number_for(line, cursor_line)` live in **pmacs-protocol** so
the wire, daemon, TUI, and GPU all compute the same value (Q#UX7);
`pmacs` re-exports it as `crate::window::LineNumberMode`.
- **Protocol v14 (the scored-false Q#UX1 debt, part two).** Sub-arc 1
shipped `LineNumbers { enabled: bool }` (off/absolute) as a deliberate
placeholder; relative/hybrid need the mode, so the variant now carries
`mode: LineNumberMode`. Encoding change → 13 → 14, daemon-gated `< 14`
(a v13 peer gets no `LineNumbers`), same shape as the v10 `SearchPrompt`
bump.
- **Cursor-line dependency + repaint-on-move.** Relative numbers change as
the cursor moves, not just on scroll. TUI: the full-frame render already
repaints on cursor motion, and `paint_line_number_gutter` reads the
cursor's buffer line (`text_view.line_at_offset(cursor)`) — free. GPU:
`refresh_gutter_buffer` rebuilds every render (which cursor moves already
trigger) using `cursor_line()` off `current_line_starts` — also free.
- **Stable width.** `gutter_width` is sized by `digits(line_count)` for
every on-mode, so the text never jitters as the cursor moves.
- **Selection UX (chosen over a 4-way cycle):** `window.toggle-line-numbers`
stays a binary off/absolute toggle; `window.set-line-numbers` opens the
minibuffer with an arrow-navigable completion dropdown
(off|absolute|relative|hybrid) — dogfooding the #89 arrow-nav fix.
Validated: `fmt` + `clippy --all-targets` clean both flavors + gpu; 1446
lib (incl. `number_for` across all modes), 12 protocol, 55 pmacs-gpu (incl.
a headless render proving relative ≠ absolute). Both frontends eyeballed,
including live cursor-move renumbering.
## Arc close
All four sub-arcs shipped: 1) gutter + absolute (v13), 2) diagnostic signs,
3) relative/hybrid (v14). Q#UX1 ("no protocol change") **scored false**
the gutter cost two protocol bumps (v13 daemon-owned toggle, v14 mode) —
because the *control* is daemon-owned even though *rendering* is
frontend-local. Deferred, riding the gutter when someone wants them:
signs-without-numbers mode; whitespace/indent guides; folding placeholders;
git change markers.
<!-- next sub-arcs appended here --> <!-- next sub-arcs appended here -->