aboutsummaryrefslogtreecommitdiff
path: root/src/app/input
diff options
context:
space:
mode:
authorBryan Lee <38807139+liby@users.noreply.github.com>2026-07-08 01:54:42 +0800
committerGitHub <noreply@github.com>2026-07-07 20:54:42 +0300
commit1e1d06328cc9aefa354b7ce463f9f2d254395662 (patch)
tree8b1d6bbeab7916a48a9db99e2671e323d1a78b04 /src/app/input
parentd0b75de0252ff70a8f43c221b506bf4a0ab3c54d (diff)
fix: preserve navigator search commands (#1140)
Navigator search treated Escape as either modal close or query reset, which left no way to return to command mode while keeping the filtered row set. `/` also rebuilt search mode by clearing the query, so a user could not resume editing the same filter after backing out. Make Escape leave search focus only, reserve command-mode Escape for closing the picker, and let `/` refocus the existing query. The footer now lists the shortcuts for the active mode so command keys are not advertised while text input owns them. refs #1115
Diffstat (limited to 'src/app/input')
-rw-r--r--src/app/input/modal.rs120
1 files changed, 103 insertions, 17 deletions
diff --git a/src/app/input/modal.rs b/src/app/input/modal.rs
index 82d393d6..84aed8da 100644
--- a/src/app/input/modal.rs
+++ b/src/app/input/modal.rs
@@ -165,15 +165,7 @@ pub(crate) fn handle_navigator_key(
if state.navigator.search_focused {
match key.code {
KeyCode::Esc => {
- if state.navigator.query.is_empty() {
- state.navigator.search_focused = false;
- leave_modal(state);
- } else {
- state.navigator.query.clear();
- state.navigator.state_filter = None;
- state.navigator.search_focused = false;
- state.clamp_navigator_selection_from(terminal_runtimes);
- }
+ state.navigator.search_focused = false;
}
KeyCode::Enter => {
state.accept_navigator_selection_from(terminal_runtimes);
@@ -208,19 +200,12 @@ pub(crate) fn handle_navigator_key(
match key.code {
KeyCode::Esc => {
- if state.navigator.query.is_empty() && state.navigator.state_filter.is_none() {
- leave_modal(state);
- } else {
- state.navigator.query.clear();
- state.navigator.state_filter = None;
- state.clamp_navigator_selection_from(terminal_runtimes);
- }
+ leave_modal(state);
}
KeyCode::Enter => {
state.accept_navigator_selection_from(terminal_runtimes);
}
KeyCode::Char('/') => {
- state.navigator.query.clear();
state.navigator.state_filter = None;
state.navigator.search_focused = true;
state.clamp_navigator_selection_from(terminal_runtimes);
@@ -1644,6 +1629,107 @@ mod tests {
}
#[test]
+ fn navigator_empty_search_escape_returns_to_commands() {
+ let mut state = state_with_workspaces(&["alpha", "beta"]);
+ let terminal_runtimes = crate::terminal::TerminalRuntimeRegistry::new();
+ state.mode = Mode::Navigator;
+ state.navigator.search_focused = true;
+
+ handle_navigator_key(
+ &mut state,
+ &terminal_runtimes,
+ KeyEvent::new(KeyCode::Esc, KeyModifiers::empty()),
+ );
+
+ assert_eq!(state.mode, Mode::Navigator);
+ assert!(!state.navigator.search_focused);
+ assert!(state.navigator.query.is_empty());
+
+ handle_navigator_key(
+ &mut state,
+ &terminal_runtimes,
+ KeyEvent::new(KeyCode::Char('w'), KeyModifiers::empty()),
+ );
+
+ assert_eq!(
+ state.navigator.state_filter,
+ Some(NavigatorStateFilter::Working)
+ );
+ assert!(state.navigator.query.is_empty());
+
+ handle_navigator_key(
+ &mut state,
+ &terminal_runtimes,
+ KeyEvent::new(KeyCode::Esc, KeyModifiers::empty()),
+ );
+
+ assert_eq!(state.mode, Mode::Terminal);
+ }
+
+ #[test]
+ fn navigator_search_escape_blurs_then_next_escape_closes() {
+ let mut state = state_with_workspaces(&["alpha", "beta"]);
+ let terminal_runtimes = crate::terminal::TerminalRuntimeRegistry::new();
+ state.mode = Mode::Navigator;
+ state.navigator.search_focused = true;
+ state.navigator.query = "a".into();
+
+ handle_navigator_key(
+ &mut state,
+ &terminal_runtimes,
+ KeyEvent::new(KeyCode::Esc, KeyModifiers::empty()),
+ );
+
+ assert_eq!(state.mode, Mode::Navigator);
+ assert!(!state.navigator.search_focused);
+ assert_eq!(state.navigator.query, "a");
+
+ handle_navigator_key(
+ &mut state,
+ &terminal_runtimes,
+ KeyEvent::new(KeyCode::Char('j'), KeyModifiers::empty()),
+ );
+
+ assert_eq!(state.navigator.selected, 1);
+ assert_eq!(state.navigator.query, "a");
+
+ handle_navigator_key(
+ &mut state,
+ &terminal_runtimes,
+ KeyEvent::new(KeyCode::Char('/'), KeyModifiers::empty()),
+ );
+
+ assert_eq!(state.mode, Mode::Navigator);
+ assert!(state.navigator.search_focused);
+ assert_eq!(state.navigator.query, "a");
+
+ handle_navigator_key(
+ &mut state,
+ &terminal_runtimes,
+ KeyEvent::new(KeyCode::Char('l'), KeyModifiers::empty()),
+ );
+
+ assert_eq!(state.navigator.query, "al");
+
+ handle_navigator_key(
+ &mut state,
+ &terminal_runtimes,
+ KeyEvent::new(KeyCode::Esc, KeyModifiers::empty()),
+ );
+
+ assert_eq!(state.mode, Mode::Navigator);
+ assert!(!state.navigator.search_focused);
+
+ handle_navigator_key(
+ &mut state,
+ &terminal_runtimes,
+ KeyEvent::new(KeyCode::Esc, KeyModifiers::empty()),
+ );
+
+ assert_eq!(state.mode, Mode::Terminal);
+ }
+
+ #[test]
fn open_rename_active_tab_can_prefill_default_new_tab_name() {
let mut state = state_with_workspaces(&["test"]);
state.workspaces[0].test_add_tab(None);