fix(maestro): align TUI and web shortcuts

Spawn was `s`/`S` in the TUI and `n`/`N` in the web client; the TUI now
uses `n`/`N` too. The web sidebar gains `c`, `r` and `x` — collapse,
rename, kill — which the TUI already bound.

docs/SIDEBAR.md now carries the shared table, so a new binding has one
place to land on both clients.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Miguel Palhas
2026-09-03 08:25:35 +01:00
co-authored by Claude Opus 5
parent b36d571b21
commit b1527f5fc1
8 changed files with 1744 additions and 12 deletions
+10 -9
View File
@@ -52,10 +52,11 @@ const DEFAULT_NORMAL: &[(&str, &str)] = &[
// because Normal mode only exists while the sidebar is focused, so
// nothing forwards it to a pty.
("tab", "AttachHighlighted"),
("s", "OpenSpawnModal"),
// Fast-forward spawn (#67): the spawn key's neighbour does the same
// `n`/`N`, the same pair the web client binds.
("n", "OpenSpawnModal"),
// Fast-forward spawn (#67): the spawn key's shifted twin does the same
// thing with every default resolved and no form.
("S", "FastForwardSpawn"),
("N", "FastForwardSpawn"),
("x", "KillHighlighted"),
("r", "RenameHighlighted"),
// Focus
@@ -220,11 +221,11 @@ mod tests {
Some(Action::AttachHighlighted),
);
assert_eq!(
cfg.keymap.resolve(Mode::Normal, "s"),
cfg.keymap.resolve(Mode::Normal, "n"),
Some(Action::OpenSpawnModal),
);
assert_eq!(
cfg.keymap.resolve(Mode::Normal, "S"),
cfg.keymap.resolve(Mode::Normal, "N"),
Some(Action::FastForwardSpawn),
);
}
@@ -311,18 +312,18 @@ mod tests {
assert!(s.contains("ctrl-glorb"), "diagnostic: {s}");
}
/// Regression for #70: the previous canonical form of a real Shift+S
/// keypress was `"shift-S"`, which the default `"S"` binding never
/// Regression for #70: the previous canonical form of a real Shift+N
/// keypress was `"shift-N"`, which the default `"N"` binding never
/// matched. Drive the actual crossterm event, not the binding string,
/// so the dead-key bug can't come back unnoticed.
#[test]
fn real_shift_s_keypress_resolves_to_fast_forward_spawn() {
fn real_shift_n_keypress_resolves_to_fast_forward_spawn() {
use crossterm::event::{Event as CtEvent, KeyCode, KeyEvent, KeyModifiers};
use crate::input::{canonicalise, Region};
let cfg = TuiConfig::load(&TuiSection::default()).unwrap();
let ev = CtEvent::Key(KeyEvent::new(KeyCode::Char('S'), KeyModifiers::SHIFT));
let ev = CtEvent::Key(KeyEvent::new(KeyCode::Char('N'), KeyModifiers::SHIFT));
let key = canonicalise(&ev, |_, _| Region::Unknown).expect("key event canonicalises");
assert_eq!(
+2 -1
View File
@@ -597,7 +597,8 @@ mod tests {
/// so a real Shift+S delivers `Char { c: 'S', shift: true }`. Its
/// canonical form must be the bare `"S"` a keymap binds, not `"shift-S"` —
/// the uppercase letter already says "shifted"; a redundant prefix would
/// make the default `S` binding unreachable from the keyboard (#70).
/// make every uppercase binding (the default `N`) unreachable from the
/// keyboard (#70).
#[test]
fn shift_flag_is_redundant_for_an_already_uppercase_char() {
let k = InputKey::Char {