crabidy/plan/help-modal.md

3.7 KiB

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

  • 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).
  • 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

  • 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

  • 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

  • Implement help::popup_area: content-sized centered Rect clamped to the frame. Verify: popup_never_exceeds_the_frame.
  • 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

  • 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".
  • 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.
  • Write plan/summary.md (or a help-modal section in it) recording any deviations from this plan.