From d1616c63bcb2b4918b357d72afa46b14565d3192 Mon Sep 17 00:00:00 2001 From: Alexander Date: Sun, 10 May 2026 13:37:52 +0200 Subject: [PATCH] feat: add SPC w {h,j,k,l} spatial window navigation Pane focus follows actual screen layout: Artists (left), Albums (top-right), Tracks (bottom-right). h/l jump between left/right columns, j/k move between vertically stacked panes in the right column. Ultraworked with [Sisyphus](https://github.com/code-yeongyu/claude-agent) Co-authored-by: Sisyphus --- src/application/handlers.rs | 3 ++- src/application/library_state.rs | 28 ++++++++++++++-------------- src/input/action.rs | 3 ++- src/input/keymap.rs | 9 ++++++++- 4 files changed, 26 insertions(+), 17 deletions(-) diff --git a/src/application/handlers.rs b/src/application/handlers.rs index ef77757..47680c3 100644 --- a/src/application/handlers.rs +++ b/src/application/handlers.rs @@ -28,7 +28,8 @@ impl App { } AppAction::FocusLeft => self.library.focus_left(), AppAction::FocusRight => self.library.focus_right(), - AppAction::CycleFocus => self.library.cycle_focus(), + AppAction::FocusDown => self.library.focus_down(), + AppAction::FocusUp => self.library.focus_up(), AppAction::GotoFirst => match self.library.focus { LibraryFocus::Artists => self.library.artist_state.select(Some(0)), LibraryFocus::Albums => self.library.album_state.select(Some(0)), diff --git a/src/application/library_state.rs b/src/application/library_state.rs index 67eb50e..86021c4 100644 --- a/src/application/library_state.rs +++ b/src/application/library_state.rs @@ -138,8 +138,9 @@ impl LibraryState { pub fn focus_left(&mut self) { match self.focus { LibraryFocus::Artists => {} - LibraryFocus::Albums => self.focus = LibraryFocus::Artists, - LibraryFocus::Tracks => self.focus = LibraryFocus::Albums, + LibraryFocus::Albums | LibraryFocus::Tracks => { + self.focus = LibraryFocus::Artists; + } } } @@ -150,21 +151,20 @@ impl LibraryState { self.focus = LibraryFocus::Albums; } } - LibraryFocus::Albums => { - if self.selected_album().is_some() { - self.focus = LibraryFocus::Tracks; - } - } - LibraryFocus::Tracks => {} + LibraryFocus::Albums | LibraryFocus::Tracks => {} } } - pub fn cycle_focus(&mut self) { - self.focus = match self.focus { - LibraryFocus::Artists => LibraryFocus::Albums, - LibraryFocus::Albums => LibraryFocus::Tracks, - LibraryFocus::Tracks => LibraryFocus::Artists, - }; + pub fn focus_down(&mut self) { + if self.focus == LibraryFocus::Albums { + self.focus = LibraryFocus::Tracks; + } + } + + pub fn focus_up(&mut self) { + if self.focus == LibraryFocus::Tracks { + self.focus = LibraryFocus::Albums; + } } fn reset_album_selection(&mut self) { diff --git a/src/input/action.rs b/src/input/action.rs index 2cbb265..0d9eac7 100644 --- a/src/input/action.rs +++ b/src/input/action.rs @@ -5,7 +5,8 @@ pub enum AppAction { MoveDown, FocusLeft, FocusRight, - CycleFocus, + FocusDown, + FocusUp, GotoFirst, GotoLast, HalfPageDown, diff --git a/src/input/keymap.rs b/src/input/keymap.rs index 90e4a65..d45d558 100644 --- a/src/input/keymap.rs +++ b/src/input/keymap.rs @@ -11,7 +11,7 @@ pub fn build_normal_keymap() -> KeyTrie { .unwrap(); t.bind_desc("l", AppAction::FocusRight, "focus right") .unwrap(); - t.bind_desc("Tab", AppAction::CycleFocus, "cycle focus") + t.bind_desc("Tab", AppAction::FocusRight, "next pane") .unwrap(); t.bind_desc("G", AppAction::GotoLast, "last item").unwrap(); t.bind_desc("C-d", AppAction::HalfPageDown, "half page down") @@ -41,6 +41,13 @@ pub fn build_normal_keymap() -> KeyTrie { b.bind_desc("p", AppAction::PrevTab, "prev")?; Ok(()) })?; + g.group("w", "+window", |w| { + w.bind_desc("h", AppAction::FocusLeft, "pane left")?; + w.bind_desc("l", AppAction::FocusRight, "pane right")?; + w.bind_desc("j", AppAction::FocusDown, "pane down")?; + w.bind_desc("k", AppAction::FocusUp, "pane up")?; + Ok(()) + })?; g.bind_desc("h", AppAction::ShowHelp, "help")?; g.bind_desc("q", AppAction::Quit, "quit")?; g.bind_desc("n", AppAction::ToggleNotifications, "notifications")?;