# Plan — help modal (cbd-tui) Ordered tasks for the `implement` stage. Inputs: `architecture/help-modal.md`, stubs in `cbd-tui/src/app/{bindings,help,mod}.rs`, gates in `quality/help-modal.md`. Run tests with `devenv shell -- cargo test -p cbd-tui`. ## 1. Binding lookup - [x] Implement `bindings::lookup`: scan `BINDINGS` in order; when `help_open` only `Scope::Help` entries are eligible, otherwise `Scope::Global` plus the scope matching `focus`. Compare chords with `SHIFT` ignored for `KeyCode::Char` codes, exact modifiers otherwise. Return the first match. **Verify**: all `app::bindings::tests::*lookup*`-related tests pass (`global_bindings_match_in_any_focus`, `pane_bindings_only_match_their_own_pane`, `same_chord_resolves_per_pane`, `shift_is_ignored_for_char_codes`, `control_must_match_exactly`, `open_help_swallows_everything_but_close`, `help_scope_never_matches_while_help_is_closed`). - [x] Implement `bindings::key_label`: `Char(' ')` → `"Space"`, other chars → the char itself, `Tab`/`Enter`/`Esc` named, `CONTROL` prefix `"Ctrl+"`; no panic on any input (fall back to `Debug`-ish formatting for unbound codes). **Verify**: `key_labels_are_human_readable` passes; gate "no panics on user input". ## 2. Action dispatch - [x] Implement `App::dispatch` as one `match action` reproducing, arm for arm, the behavior of the old key match in `main.rs` (send via `self.tx` with `let _ =`, or call `self.library`/`self.queue` methods). `OpenHelp`/`CloseHelp` set `show_help`; `Quit` returns `DispatchResult::Quit`; everything else `Continue`. **Verify**: all `app::tests::*` pass; gate "behavior parity" (cross-check against the pre-change `main.rs` match arm by arm). ## 3. Wire the event loop - [x] Replace the key match in `main.rs run_ui` with: on key press, `bindings::lookup(app.focus, app.show_help, key)` then `app.dispatch(action)`, breaking the loop on `DispatchResult::Quit`. Keep the `KeyEventKind::Press` filter. Remove the now-dead imports (`KeyCode`, `KeyModifiers`, `UiFocus`, `MessageFromUi` uses that move into `dispatch`). **Verify**: `cargo check` clean, no key handling left in `main.rs` (gate "single source of truth"); manual smoke via `?`. ## 4. Help modal rendering - [x] Implement `help::popup_area`: content-sized centered `Rect` clamped to the frame. **Verify**: `popup_never_exceeds_the_frame`. - [x] Implement `help::render`: `Clear` the popup area, draw a bordered block (style matching the panes: rounded borders, `COLOR_PRIMARY`), a short usage paragraph naming the Library and Queue panes and `Tab` to switch, then `BINDINGS` grouped by scope in table order with labels from `key_label`. Truncate lines that don't fit; never panic on tiny frames. **Verify**: `help_lists_bindings_from_the_table`, `help_explains_basic_usage`, `help_survives_tiny_terminals` pass; gates "modality" (overlay drawn last, `Clear` used) and "no panics". ## 5. Polish and gates - [x] Resolve the `TODO(api-design)` on `LibraryQueueNext`: check what `MessageFromUi::QueueTracks` does in `crabidy-server` and fix the description text if needed. **Verify**: gate "TODO resolved". - [x] Sweep: doc comments still accurate, no dead-code warnings left for `bindings`/`help`, no new dependencies. **Verify**: `devenv shell -- cargo fmt --check`, `devenv shell -- cargo clippy -p cbd-tui` (no new warnings), `devenv shell -- cargo test -p cbd-tui` all green; every box in `quality/help-modal.md` checked. - [x] Write `plan/summary.md` (or a `help-modal` section in it) recording any deviations from this plan.