From b7781a67e2d7f89ce07022d8ecdf39687ea552b0 Mon Sep 17 00:00:00 2001 From: Eric Yue Date: Thu, 24 Sep 2026 19:45:59 +0800 Subject: [PATCH] fix: match navigator search terms independently (#4535) * fix: match navigator search terms independently refs #4273 * docs: remove redundant navigator search note refs #4273 --- src/client/shell/aggregate_navigation.rs | 9 +++++- src/client/shell/tests/copy.rs | 36 ++++++++++++++++++++++++ 2 files changed, 44 insertions(+), 1 deletion(-) diff --git a/src/client/shell/aggregate_navigation.rs b/src/client/shell/aggregate_navigation.rs index b1a4d35f..67753e8e 100644 --- a/src/client/shell/aggregate_navigation.rs +++ b/src/client/shell/aggregate_navigation.rs @@ -288,7 +288,14 @@ pub(super) fn navigator_rows( Some(ClientNavigatorFilter::Done) => status == crate::api::schema::AgentStatus::Done, None => true, }; - let text = |value: &str| query.is_empty() || value.to_lowercase().contains(&query); + let words = query.split_whitespace().collect::>(); + let text = |value: &str| { + if words.is_empty() { + return true; + } + let value = value.to_lowercase(); + words.iter().all(|word| value.contains(word)) + }; let filtering = navigator.filter.is_some() || !query.is_empty(); let federated = endpoints.len() > 1; let depth_offset = u8::from(federated); diff --git a/src/client/shell/tests/copy.rs b/src/client/shell/tests/copy.rs index 617af89b..112417fc 100644 --- a/src/client/shell/tests/copy.rs +++ b/src/client/shell/tests/copy.rs @@ -1080,6 +1080,42 @@ fn navigator_renders_every_terminal_in_workspace_sections() { assert!(visible.iter().all(|row| !row.contains("second"))); } +#[test] +fn navigator_search_matches_non_adjacent_words_without_losing_the_pane_target() { + let mut projected = snapshot(); + projected.panes[0].label = Some("alpha beta gamma".into()); + let mut state = ClientShellState::new(ClientShellConfig::from_config(&Config::default())); + state.set_snapshot(Box::new(projected)); + state.set_pane_surface(surface()); + state.open_navigator_overlay(); + let Some(ClientShellOverlay::Navigator(navigator)) = state.overlay.as_mut() else { + panic!("navigator"); + }; + for (query, matches) in [ + ("alpha gamma", true), + (" ALP\tGAM ", true), + ("gamma alpha", true), + ("beta gamma", true), + ("alpha missing", false), + ("alphagamma", false), + ] { + navigator.query = query.into(); + navigator.selected = None; + let rows = + render::client_navigator_rows(&state.endpoints, &state.active_endpoint_id, navigator); + let target = + super::super::aggregate_navigation::selected_navigator_target(&rows, navigator); + assert_eq!( + target, + matches.then(|| ClientNavigatorTarget::Pane { + endpoint_id: state.active_endpoint_id.clone(), + pane_id: "pane_1".into(), + }), + "query={query:?}" + ); + } +} + #[test] fn navigator_searches_ancestor_context_and_keeps_split_agents_individually_actionable() { let mut projected = snapshot();