# Plan — queue selection, visual mode, and the register Executes `architecture/queue-register.md` against `quality/queue-register.md`. Ordered by dependency. Three commits: **(A)** the shared mark/visual trait with the library moved onto it, **(B)** queue marks + the register in `cbd-tui`, **(C)** `cbd-web` parity + docs. No server work: `Remove` already takes many positions and `Insert` takes a path list (G25). ## A — Shared mark and visual behaviour (`cbd-tui`) - [x] **A1 — Fill in `MarkedPane`** in `app/list.rs`: `toggle_visual`, `toggle_mark`, `toggle_mark_view`, `paint_between`, `selection` — moved verbatim in behaviour from `library.rs`, with the `markable` gate taken from the implementor. *Verifies:* G11, G12, G16. - [x] **A2 — `impl MarkedPane for Library`**, deleting the now-duplicated inherent methods and keeping `get_selected` as a thin wrapper over `selection` so existing call sites are untouched. `markable` = `is_queable`. *Verifies:* the existing library visual-mode suite passes unchanged (G11, G12). - [x] **A3 — `carry_marks`** in `app/list.rs`: greedy in-order path match, drop marks whose track is gone, all-false when nothing matches, tolerant of a short `old_marked`. *Verifies:* the ten `carry_marks_tests` (G2, G3). - [x] **A4 — Commit A.** *Merged into commit B:* group A alone leaves `carry_marks` and `Register` unwired, and committing dead code for the next commit to use is worse than one larger commit. The extraction is still proven behaviour-preserving — the library's whole visual-mode suite passes untouched. ## B — Queue marks and the register (`cbd-tui`) - [x] **B1 — `impl MarkedPane for Queue`** with `markable` = always, and a `visual: Option` field on `Queue`. *Verifies:* G12. - [x] **B2 — `update_queue` carries marks** through `carry_marks` instead of rebuilding them as `false`: keep the previous path list and mark flags, reconcile, apply. *Verifies:* G1, G2. - [x] **B3 — `Register::set`** (replace the `todo!()`) and an `App.register` field. *Verifies:* the register suite (G6, G10). - [x] **B4 — `y` in both panes.** New `Action::LibraryYank` and `Action::QueueYank`; each takes `selection()`, writes the register, and clears the marks it consumed. *Verifies:* G5, G14, G15. - [x] **B5 — `d` in the queue deletes every marked row** (cursor row when none), writing them to the register first, in one `RemoveTracks` call with positions from the current list. Replaces the `FIXME`. *Verifies:* G1, G13, G14, G15. - [x] **B6 — `c`/`C` fill the register** with the tracks they drop (`C` everything, `c` everything but the current track). *Verifies:* G4. - [x] **B7 — `p`/`P` paste the register.** `Action::QueuePaste` / `QueuePasteBefore` send `InsertTracks(register.paths(), pos)` with `pos` = cursor + 1 for `p`, cursor for `P`; a no-op on an empty register; the register survives. `QueueInsertHere`'s old cross-pane behaviour is gone. *Verifies:* G7, G8, G9. - [x] **B8 — Queue visual mode in dispatch.** A `queue_move` wrapper mirroring `library_move`, and the visual auto-leave guard generalized to whichever pane is in visual mode with its own movement whitelist. *Verifies:* G11. - [x] **B9 — Bindings + help.** Queue scope gains `s`, `v`, `V`, `y`, `P`; library gains `y`; `p`'s description changes. The help modal derives from `BINDINGS`, so it follows. *Verifies:* G19, G20. - [x] **B10 — Status feedback.** The pane title shows `— VISUAL` in the queue as in the library, and a non-empty register is visible somewhere (title or now-playing line) so paste is not blind. *Verifies:* G22 in spirit; keeps G9's no-op explicable. - [x] **B11 — Dispatch tests** for the gates named above. *Verifies:* G1, G4, G5, G7–G9, G13–G15. - [x] **B12 — Commit B** with the whole `cbd-tui` suite, clippy `-D warnings`, and fmt clean (G24, G26). ## C — `cbd-web` parity and docs - [x] **C1 — `Register` + `carry_marks` in `cbd-web/src/state.rs`**, same shape and same test cases as the TUI's. *Verifies:* G17, G18. - [x] **C2 — Library visual mode** (the piece deferred when `v`/`V` landed in the TUI): `visual: Option` on `LibraryPane`, the same anchored paint, `Action::LibraryVisualMode` on `v`/`V`, auto-leave on non-movement. *Verifies:* G17. - [x] **C3 — Queue marks beside the signal.** The web queue owns no list, so hold `marks: Vec` plus the path list they were taken against, and reconcile on each `Queue` update. *Verifies:* G18. - [x] **C4 — Queue actions:** `QueueToggleMark`, `QueueVisualMode`, `QueueYank`, `QueuePaste`, `QueuePasteBefore`, plus `d`/`c`/`C` writing the register — same semantics as the TUI. *Verifies:* G17. - [x] **C5 — Keymap + `HELP` table** rows for all of the above, keeping the lockstep test green; clickable equivalents where the web client has them. *Verifies:* G19. - [x] **C6 — Verify the web half properly:** clippy for native **and** `wasm32-unknown-unknown`, `cargo test -p cbd-web`, and a trunk bundle build. *Verifies:* G26. - [x] **C7 — Docs.** `docs/src/clients/tui.md` (key table + a register section), `docs/src/clients/web.md`, `docs/src/queue.md` (what the register is and is not), and the README usage section. State plainly that `p` changed meaning. *Verifies:* G21, G22, G23. - [x] **C8 — `plan/summary.md`** entry, then commit C. ## Deferred (recorded, not dropped) - Named registers (`"a`) — `Register` is shaped for it; not built. - A numbered/history register stack, and any cross-client or persistent register. - A per-entry queue id in the proto, which would make mark reconciliation exact instead of a greedy path match.