feat(app): vi-style j/k/g/G navigation in the load picker (#24)

Add j (down), k (up), g (first) and G (last) to the load picker's
list sub-mode, alongside the existing arrow keys. Typeable keys keep
the picker drivable by autocast in the website's documentation casts,
which cannot emit arrow keys. Footer hint left unchanged.
This commit is contained in:
claude@clouddev1
2026-06-10 21:36:18 +00:00
parent 18303784a0
commit 638b4c9664
2 changed files with 101 additions and 2 deletions
+16 -2
View File
@@ -2488,20 +2488,34 @@ impl App {
self.note_system(crate::t!("modal.load_cancelled"));
Vec::new()
}
KeyCode::Up => {
// `k` mirrors Up; vi-style keys keep the picker drivable by
// autocast, which can only emit typeable characters (#24).
KeyCode::Up | KeyCode::Char('k') => {
if state.selected > 0 {
state.selected -= 1;
}
self.modal = Some(Modal::LoadPicker(state));
Vec::new()
}
KeyCode::Down => {
// `j` mirrors Down (see the Up arm above).
KeyCode::Down | KeyCode::Char('j') => {
if state.selected + 1 < state.entries.len() {
state.selected += 1;
}
self.modal = Some(Modal::LoadPicker(state));
Vec::new()
}
// `g` jumps to the first entry, `G` to the last (vi convention).
KeyCode::Char('g') => {
state.selected = 0;
self.modal = Some(Modal::LoadPicker(state));
Vec::new()
}
KeyCode::Char('G') => {
state.selected = state.entries.len().saturating_sub(1);
self.modal = Some(Modal::LoadPicker(state));
Vec::new()
}
KeyCode::Enter => {
if let Some(entry) = state.entries.get(state.selected).cloned() {
self.modal = None;