feat(commands): add panel cursor and tile selection commands

Add comprehensive command implementations for managing panel cursors
(formula_cursor, cat_panel_cursor, view_panel_cursor), tile selection,
text buffers, and search functionality.

Update EnterEditMode to use SetBuffer effect before changing mode.
Update EnterTileSelect to use SetTileCatIdx effect before changing mode.

Add keymap bindings for all new modes with navigation (arrows/hjkl),
editing actions (Enter, Backspace, Char), and mode transitions (Esc, Tab).

Co-Authored-By: fiddlerwoaroof/git-smart-commit (unsloth/Qwen3.5-35B-A3B-GGUF:Q5_K_M)
This commit is contained in:
Edward Langley
2026-04-04 09:58:31 -07:00
parent ebe8df89ee
commit d8f7d9a501
2 changed files with 746 additions and 7 deletions

View File

@ -341,6 +341,198 @@ impl KeymapSet {
help.bind_cmd(KeyCode::Char('q'), none, cmd::EnterMode(AppMode::Normal));
set.insert(ModeKey::Help, Arc::new(help));
// ── Formula panel mode ───────────────────────────────────────────
let mut fp = Keymap::new();
fp.bind_cmd(KeyCode::Esc, none, cmd::EnterMode(AppMode::Normal));
fp.bind_cmd(KeyCode::Tab, none, cmd::EnterMode(AppMode::Normal));
fp.bind_cmd(
KeyCode::Up,
none,
cmd::MovePanelCursor { panel: crate::ui::effect::Panel::Formula, delta: -1 },
);
fp.bind_cmd(
KeyCode::Char('k'),
none,
cmd::MovePanelCursor { panel: crate::ui::effect::Panel::Formula, delta: -1 },
);
fp.bind_cmd(
KeyCode::Down,
none,
cmd::MovePanelCursor { panel: crate::ui::effect::Panel::Formula, delta: 1 },
);
fp.bind_cmd(
KeyCode::Char('j'),
none,
cmd::MovePanelCursor { panel: crate::ui::effect::Panel::Formula, delta: 1 },
);
fp.bind_cmd(KeyCode::Char('a'), none, cmd::EnterFormulaEdit);
fp.bind_cmd(KeyCode::Char('n'), none, cmd::EnterFormulaEdit);
fp.bind_cmd(KeyCode::Char('o'), none, cmd::EnterFormulaEdit);
fp.bind_cmd(KeyCode::Char('d'), none, cmd::DeleteFormulaAtCursor);
fp.bind_cmd(KeyCode::Delete, none, cmd::DeleteFormulaAtCursor);
set.insert(ModeKey::FormulaPanel, Arc::new(fp));
// ── Category panel mode ──────────────────────────────────────────
let mut cp = Keymap::new();
cp.bind_cmd(KeyCode::Esc, none, cmd::EnterMode(AppMode::Normal));
cp.bind_cmd(KeyCode::Tab, none, cmd::EnterMode(AppMode::Normal));
cp.bind_cmd(
KeyCode::Up,
none,
cmd::MovePanelCursor { panel: crate::ui::effect::Panel::Category, delta: -1 },
);
cp.bind_cmd(
KeyCode::Char('k'),
none,
cmd::MovePanelCursor { panel: crate::ui::effect::Panel::Category, delta: -1 },
);
cp.bind_cmd(
KeyCode::Down,
none,
cmd::MovePanelCursor { panel: crate::ui::effect::Panel::Category, delta: 1 },
);
cp.bind_cmd(
KeyCode::Char('j'),
none,
cmd::MovePanelCursor { panel: crate::ui::effect::Panel::Category, delta: 1 },
);
cp.bind_cmd(KeyCode::Enter, none, cmd::CycleAxisAtCursor);
cp.bind_cmd(KeyCode::Char(' '), none, cmd::CycleAxisAtCursor);
cp.bind_cmd(
KeyCode::Char('n'),
none,
cmd::EnterMode(AppMode::CategoryAdd { buffer: String::new() }),
);
cp.bind_cmd(KeyCode::Char('a'), none, cmd::OpenItemAddAtCursor);
cp.bind_cmd(KeyCode::Char('o'), none, cmd::OpenItemAddAtCursor);
set.insert(ModeKey::CategoryPanel, Arc::new(cp));
// ── View panel mode ──────────────────────────────────────────────
let mut vp = Keymap::new();
vp.bind_cmd(KeyCode::Esc, none, cmd::EnterMode(AppMode::Normal));
vp.bind_cmd(KeyCode::Tab, none, cmd::EnterMode(AppMode::Normal));
vp.bind_cmd(
KeyCode::Up,
none,
cmd::MovePanelCursor { panel: crate::ui::effect::Panel::View, delta: -1 },
);
vp.bind_cmd(
KeyCode::Char('k'),
none,
cmd::MovePanelCursor { panel: crate::ui::effect::Panel::View, delta: -1 },
);
vp.bind_cmd(
KeyCode::Down,
none,
cmd::MovePanelCursor { panel: crate::ui::effect::Panel::View, delta: 1 },
);
vp.bind_cmd(
KeyCode::Char('j'),
none,
cmd::MovePanelCursor { panel: crate::ui::effect::Panel::View, delta: 1 },
);
vp.bind_cmd(KeyCode::Enter, none, cmd::SwitchViewAtCursor);
vp.bind_cmd(KeyCode::Char('n'), none, cmd::CreateAndSwitchView);
vp.bind_cmd(KeyCode::Char('o'), none, cmd::CreateAndSwitchView);
vp.bind_cmd(KeyCode::Char('d'), none, cmd::DeleteViewAtCursor);
vp.bind_cmd(KeyCode::Delete, none, cmd::DeleteViewAtCursor);
set.insert(ModeKey::ViewPanel, Arc::new(vp));
// ── Tile select mode ─────────────────────────────────────────────
let mut ts = Keymap::new();
ts.bind_cmd(KeyCode::Esc, none, cmd::EnterMode(AppMode::Normal));
ts.bind_cmd(KeyCode::Tab, none, cmd::EnterMode(AppMode::Normal));
ts.bind_cmd(KeyCode::Left, none, cmd::MoveTileCursor(-1));
ts.bind_cmd(KeyCode::Char('h'), none, cmd::MoveTileCursor(-1));
ts.bind_cmd(KeyCode::Right, none, cmd::MoveTileCursor(1));
ts.bind_cmd(KeyCode::Char('l'), none, cmd::MoveTileCursor(1));
ts.bind_cmd(KeyCode::Enter, none, cmd::CycleAxisForTile);
ts.bind_cmd(KeyCode::Char(' '), none, cmd::CycleAxisForTile);
ts.bind_cmd(KeyCode::Char('r'), none, cmd::SetAxisForTile(Axis::Row));
ts.bind_cmd(KeyCode::Char('c'), none, cmd::SetAxisForTile(Axis::Column));
ts.bind_cmd(KeyCode::Char('p'), none, cmd::SetAxisForTile(Axis::Page));
ts.bind_cmd(KeyCode::Char('n'), none, cmd::SetAxisForTile(Axis::None));
set.insert(ModeKey::TileSelect, Arc::new(ts));
// ── Editing mode ─────────────────────────────────────────────────
let mut ed = Keymap::new();
ed.bind_cmd(KeyCode::Esc, none, cmd::EnterMode(AppMode::Normal));
ed.bind_cmd(KeyCode::Enter, none, cmd::CommitCellEdit);
ed.bind_cmd(
KeyCode::Backspace,
none,
cmd::PopChar { buffer: "edit".to_string() },
);
ed.bind_any_char(cmd::AppendChar { buffer: "edit".to_string() });
set.insert(ModeKey::Editing, Arc::new(ed));
// ── Formula edit mode ────────────────────────────────────────────
let mut fe = Keymap::new();
fe.bind_cmd(KeyCode::Esc, none, cmd::EnterMode(AppMode::FormulaPanel));
fe.bind_cmd(KeyCode::Enter, none, cmd::CommitFormula);
fe.bind_cmd(
KeyCode::Backspace,
none,
cmd::PopChar { buffer: "formula".to_string() },
);
fe.bind_any_char(cmd::AppendChar { buffer: "formula".to_string() });
set.insert(ModeKey::FormulaEdit, Arc::new(fe));
// ── Category add mode ────────────────────────────────────────────
let mut ca = Keymap::new();
ca.bind_cmd(KeyCode::Esc, none, cmd::EnterMode(AppMode::CategoryPanel));
ca.bind_cmd(KeyCode::Enter, none, cmd::CommitCategoryAdd);
ca.bind_cmd(KeyCode::Tab, none, cmd::CommitCategoryAdd);
ca.bind_cmd(
KeyCode::Backspace,
none,
cmd::PopChar { buffer: "category".to_string() },
);
ca.bind_any_char(cmd::AppendChar { buffer: "category".to_string() });
set.insert(ModeKey::CategoryAdd, Arc::new(ca));
// ── Item add mode ────────────────────────────────────────────────
let mut ia = Keymap::new();
ia.bind_cmd(KeyCode::Esc, none, cmd::EnterMode(AppMode::CategoryPanel));
ia.bind_cmd(KeyCode::Enter, none, cmd::CommitItemAdd);
ia.bind_cmd(KeyCode::Tab, none, cmd::CommitItemAdd);
ia.bind_cmd(
KeyCode::Backspace,
none,
cmd::PopChar { buffer: "item".to_string() },
);
ia.bind_any_char(cmd::AppendChar { buffer: "item".to_string() });
set.insert(ModeKey::ItemAdd, Arc::new(ia));
// ── Export prompt mode ───────────────────────────────────────────
let mut ep = Keymap::new();
ep.bind_cmd(KeyCode::Esc, none, cmd::EnterMode(AppMode::Normal));
ep.bind_cmd(KeyCode::Enter, none, cmd::CommitExport);
ep.bind_cmd(
KeyCode::Backspace,
none,
cmd::PopChar { buffer: "export".to_string() },
);
ep.bind_any_char(cmd::AppendChar { buffer: "export".to_string() });
set.insert(ModeKey::ExportPrompt, Arc::new(ep));
// ── Command mode ─────────────────────────────────────────────────
let mut cm = Keymap::new();
cm.bind_cmd(KeyCode::Esc, none, cmd::EnterMode(AppMode::Normal));
// Enter → execute_command (still handled by old handler for now —
// the complex execute_command logic isn't easily a single Cmd)
cm.bind_cmd(KeyCode::Backspace, none, cmd::CommandModeBackspace);
cm.bind_any_char(cmd::AppendChar { buffer: "command".to_string() });
set.insert(ModeKey::CommandMode, Arc::new(cm));
// ── Search mode ──────────────────────────────────────────────────
let mut sm = Keymap::new();
sm.bind_cmd(KeyCode::Esc, none, cmd::ExitSearchMode);
sm.bind_cmd(KeyCode::Enter, none, cmd::ExitSearchMode);
sm.bind_cmd(KeyCode::Backspace, none, cmd::SearchPopChar);
sm.bind_any_char(cmd::SearchAppendChar);
set.insert(ModeKey::SearchMode, Arc::new(sm));
set
}
}