pmacs/src
Levi Neuwirth 4bd3a76618
feat(panel): replay PanelPointer --- the wiring, the seam, and the producer half
First implementation commit for parent acceptance 48. The daemon-side
replay and every producer-side rule land here; the daemon-side witness
matrices (A1-A5, B1-B6, and Q#BP-R2's document->terminal row) are the
next commit and are NOT claimed yet.

**MODIFIERS NOW CROSS THE SEAM (R-a).** The daemon destructured `mods`
into `..` and the dispatcher had no parameter for them, which inverted
two behaviours rather than degrading them: `apply_terminal_gesture`
gates child reporting on `!shift`, so Shift is the user's "select
locally instead of talking to the child" override, and the document
path reads Shift to extend the selection.

**THE REPLAY IS THE SHARED PATHS, NOT NEW ONES.** A terminal panel goes
through `apply_terminal_gesture` --- "the one terminal pointer path,
shared by both frontend kinds" --- with the side window's
`TerminalViewKey` and a viewport of `rows - 1`, never the full grid: the
frame would make the mode line a child cell and put every clamp a row
out. A document panel scrolls through the window-scoped `scroll_window`
and replays selection through new window-TARGETED writers.

Those writers exist because the selection API is active-window scoped.
`Drag` and `Up` do not activate, and another frontend can interleave
between a `Down` and its tail, so a replay reading `active_window_mut()`
would act on whatever happened to be active then. `panel_cell_byte`
converts against the SIDE window's own `view_top` and fold map without
`activate_and_position`'s `set_active_window_id`. The one place the
ambient helper is used is the double-click word selection, two
statements after the `Down` activated that window synchronously, and it
says so.

**Q#BP-R2 IS ORDERED, NOT MERELY PLACED.** A terminal panel's chrome
wheel is consumed before `focus_window`, before `active_frontend`,
before any controller claim and before the shared path --- `activates`
is `!Move` for a terminal, so a check any lower would leave the wheel
changing FOCUS while scrolling nothing.

Producer half, all target-blind because `PanelFrame` carries no
target-kind field:

  - a press on the band's MODE LINE neither sends nor arms. Arming
    would let a drag into content emit a `Drag` with no accepted
    `Down`, which no receiver-side rule can undo.
  - `gesture_last_content_cell`, a TERMINATION FALLBACK distinct from
    the dedupe baseline. `last_pointer_cell` is cleared on press
    precisely so the first drag after a press reaches the daemon
    (asserted at `main.rs:19841`); storing the press cell there would
    suppress it. The new field is written on arm and on each accepted
    content motion, cleared on release and on either identity change,
    and `panel_motion_is_new` never consults it.
  - a crossing `Drag` is normalized and then deduped; `Up` is always
    sent, always at a content coordinate.
  - the gesture latch now dies on a change of EITHER identity --- panel
    or geometry --- and survives a same-identity repaint.

Six mutations, each biting its own row:

  M-P1  arm on a chrome press          -> the producer arming row
  M-P2  release reads the dedupe field -> the chrome and no-motion rows
  M-D1  no reset on panel epoch        -> the identity row
  M-D2  no reset on geometry epoch     -> the identity row
  M-D3  reset clears `pointer_held` only -> the identity row
  M-D4  reset on every frame           -> the identity row's negative leg

M-P2 caught a defect in my own witness before it caught the code: the
no-intervening-motion row called `panel_motion_is_new` BEFORE asserting
the release, which populated the very field the mutation reads, so a
conflated implementation passed. The probe now runs after the
assertion, and the row is named for a scenario it actually performs.

One existing test moved with the contract rather than against it:
`a_held_button_makes_panel_motion_a_drag_and_a_release_lands_outside`
poked `panel_motion_is_new` and expected the release to follow it. It
now drives both fields as the production motion path does; its
assertion, and the dedupe guarantee it protects, are unchanged.

Gates: all nine green under `env -u TMPDIR`, log 20260814T151901Z.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016bqGA6s9tTUFzYpbeW3tai
2026-08-14 17:24:04 +02:00
..
audit
…
bin feat(lsp): D3 --- the file watcher stops sleeping and walks once per scan (#233) 2026-08-11 14:59:28 +02:00
lua_bindings feat(lsp): D3 --- the file watcher stops sleeping and walks once per scan (#233) 2026-08-11 14:59:28 +02:00
packages
…
terminal feat(workers): a required purpose on every job and process — worker identity Stage 1 2026-08-10 14:51:54 +02:00
ansi.rs
…
async_runtime.rs feat(lsp): D3 --- the file watcher stops sleeping and walks once per scan (#233) 2026-08-11 14:59:28 +02:00
attach.rs feat(release): binaries on tag — Distribution Stage 1 2026-08-01 14:40:47 -04:00
attach_dispatch.rs
…
attach_reconnect.rs
…
autosave.rs
…
bootstrap.rs feat(bootstrap): make the ambient storage roots a parameter 2026-07-31 18:48:07 -04:00
buffer.rs wip(stage2a): name provenance plus the View rename hook 2026-07-29 17:38:21 -04:00
buffer_mirror.rs
…
buffer_registry.rs
…
builtin_packages.rs
…
cell.rs
…
code_action.rs
…
command.rs fix(discovery): clip command descriptions at the single-row surfaces 2026-08-10 13:52:32 +02:00
completion.rs feat(fold): Stage 2 — grid (daemon-rendered) collapse 2026-07-23 19:24:07 -04:00
completion_framework.rs
…
config.rs
…
config_registry.rs test: the report itself, at a real PTY — and §1.1 was wrong 2026-08-07 19:37:46 +02:00
crdt.rs fix(buffer): fan out generated writes, and clear the history that exists 2026-07-26 13:43:22 -04:00
daemon.rs feat(panel): replay PanelPointer --- the wiring, the seam, and the producer half 2026-08-14 17:24:04 +02:00
daemon_attach.rs
…
definition.rs
…
desktop.rs feat(view): horizontal scroll, text and decorations together 2026-08-07 22:43:17 +02:00
diag.rs feat(view): horizontal scroll, text and decorations together 2026-08-07 22:43:17 +02:00
display_width.rs feat(render): unify tab-width projection 2026-07-22 15:03:30 -04:00
document_highlight.rs
…
editor.rs feat(panel): replay PanelPointer --- the wiring, the seam, and the producer half 2026-08-14 17:24:04 +02:00
editor_core.rs fix: 1a review round 2 --- provenance was armed but never completed 2026-08-12 17:20:15 +02:00
file_io.rs
…
fold.rs fix(fold): address PR #142 review round 1 2026-07-23 13:42:20 -04:00
fold_view.rs feat(fold): Stage 2 — grid (daemon-rendered) collapse 2026-07-23 19:24:07 -04:00
font_pref.rs
…
formatting.rs
…
frontend.rs feat(discovery): M-x rows carry descriptions — protocol v22 -> v23 2026-08-10 13:52:32 +02:00
fs.rs fix(tests): D3 round three --- two witness overclaims, and the CI red was a core-count assumption 2026-08-11 17:18:47 +02:00
hash.rs
…
help.rs
…
highlight.rs feat(view): horizontal scroll, text and decorations together 2026-08-07 22:43:17 +02:00
hook.rs
…
hover.rs feat(fold): Stage 2 — grid (daemon-rendered) collapse 2026-07-23 19:24:07 -04:00
inlay_hint.rs
…
instance_buffer.rs
…
instance_render.rs
…
key.rs
…
keymap_stack.rs
…
keymap_tree.rs
…
lib.rs feat(bootstrap): make the ambient storage roots a parameter 2026-07-31 18:48:07 -04:00
locations.rs
…
lockfile.rs
…
lsp.rs feat(workers): a required purpose on every job and process — worker identity Stage 1 2026-08-10 14:51:54 +02:00
lsp_status.rs
…
lua.rs
…
lua_isolation.rs
…
main.rs docs: absorb the v1.1.0 release, and correct what it made stale 2026-08-01 18:09:59 -04:00
mcp.rs feat(workers): a required purpose on every job and process — worker identity Stage 1 2026-08-10 14:51:54 +02:00
menu.rs feat(fold): Stage 2 — grid (daemon-rendered) collapse 2026-07-23 19:24:07 -04:00
message_bus.rs
…
minibuffer.rs
…
optimistic.rs
…
overlay.rs feat(view): horizontal scroll, text and decorations together 2026-08-07 22:43:17 +02:00
overlay_color.rs
…
overlay_paint.rs feat(view): wrap-aware coordinates, breaking in and additive out 2026-08-07 13:07:54 +02:00
prepare_rename.rs
…
presence.rs
…
process.rs feat(workers): a required purpose on every job and process — worker identity Stage 1 2026-08-10 14:51:54 +02:00
project.rs
…
project_index.rs
…
protocol.rs docs: state the PanelPointer pin's blast radius exactly 2026-08-12 22:56:41 +02:00
rename.rs
…
rope.rs
…
search.rs feat(view): horizontal scroll, text and decorations together 2026-08-07 22:43:17 +02:00
semantic_client.rs
…
semantic_render.rs fix(discovery): clip command descriptions at the single-row surfaces 2026-08-10 13:52:32 +02:00
semantic_tokens.rs
…
signature.rs feat(fold): Stage 2 — grid (daemon-rendered) collapse 2026-07-23 19:24:07 -04:00
socket_path.rs
…
state.rs
…
statusline.rs fix(panel): close Stage 2A review round 2 (2 P1, 1 P2) 2026-07-26 10:38:21 -04:00
symbol.rs
…
syntax.rs feat(lean4): editing surface + Stage 1 acceptance (Q#LN5, LN6, LN17) 2026-07-25 09:59:48 -04:00
text_view.rs feat(view): horizontal scroll, text and decorations together 2026-08-07 22:43:17 +02:00
transport.rs
…
view.rs feat(view): horizontal scroll, text and decorations together 2026-08-07 22:43:17 +02:00
window.rs feat(view): horizontal scroll, text and decorations together 2026-08-07 22:43:17 +02:00
worker.rs
…
workers_buffer.rs fix(workers): a safe display-text boundary for purpose and handler names 2026-08-10 14:51:54 +02:00