From b5fde2d5d4f62c9d210b3fd127e0f313ce6fba0a Mon Sep 17 00:00:00 2001 From: Christoffer Martinsson Date: Fri, 12 Dec 2025 19:52:48 +0100 Subject: [PATCH] Handle MPV force kill gracefully When MPV is killed externally, detect the dead process and automatically recreate it for next playback. Connection errors no longer crash the application - they're logged and handled silently. --- Cargo.toml | 2 +- src/main.rs | 65 ++---- src/player/mod.rs | 6 +- src/state/mod.rs | 529 ++++++++-------------------------------------- src/ui/mod.rs | 18 +- 5 files changed, 122 insertions(+), 498 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index e92b322..557c0d7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cm-player" -version = "0.1.33" +version = "0.1.35" edition = "2021" [dependencies] diff --git a/src/main.rs b/src/main.rs index c9a5cbd..128c3d9 100644 --- a/src/main.rs +++ b/src/main.rs @@ -528,15 +528,15 @@ fn run_app( } } - // Check if mpv process died (e.g., user closed video window) + // Check if mpv process died (e.g., user closed video window or force killed) if !player.is_process_alive() { - if let Some(player_state) = player.get_player_state() { - if player_state != PlayerState::Stopped { - state.current_position = 0.0; - state.current_duration = 0.0; - state_changed = true; - } - } + tracing::info!("MPV process died, recreating..."); + state.current_position = 0.0; + state.current_duration = 0.0; + state_changed = true; + + // Recreate player for next playback + *player = player::Player::new()?; } // Always update all properties in one batch to keep state synchronized with MPV @@ -710,6 +710,20 @@ fn handle_key_event(terminal: &mut Terminal, st // Handle search mode separately if state.search_mode { match key.code { + KeyCode::Char('n') => { + if state.focus_playlist { + state.next_playlist_search_result(); + } else { + state.next_search_result(); + } + } + KeyCode::Char('N') => { + if state.focus_playlist { + state.prev_playlist_search_result(); + } else { + state.prev_search_result(); + } + } KeyCode::Char(c) => { if state.focus_playlist { state.append_playlist_search_char(c); @@ -724,20 +738,6 @@ fn handle_key_event(terminal: &mut Terminal, st state.backspace_search(); } } - KeyCode::Tab => { - if state.focus_playlist { - state.playlist_tab_search_next(); - } else { - state.tab_search_next(); - } - } - KeyCode::BackTab => { - if state.focus_playlist { - state.playlist_tab_search_prev(); - } else { - state.tab_search_prev(); - } - } KeyCode::Enter => { if state.focus_playlist { state.execute_playlist_search(); @@ -800,32 +800,11 @@ fn handle_key_event(terminal: &mut Terminal, st } } (KeyCode::Esc, _) => { - if !state.search_matches.is_empty() { - state.search_matches.clear(); - } - if !state.playlist_search_matches.is_empty() { - state.playlist_search_matches.clear(); - state.playlist_tab_search_results.clear(); - } if state.visual_mode { state.visual_mode = false; state.marked_files.clear(); } } - (KeyCode::Char('n'), _) => { - if !state.search_matches.is_empty() { - state.next_search_match(); - } else if !state.playlist_search_matches.is_empty() { - state.next_playlist_search_match(); - } - } - (KeyCode::Char('N'), KeyModifiers::SHIFT) => { - if !state.search_matches.is_empty() { - state.prev_search_match(); - } else if !state.playlist_search_matches.is_empty() { - state.prev_playlist_search_match(); - } - } (KeyCode::Char('J'), KeyModifiers::SHIFT) => { // Next track action_navigate_track(state, player, 1, skip_position_update)?; diff --git a/src/player/mod.rs b/src/player/mod.rs index 21a043c..03069ef 100644 --- a/src/player/mod.rs +++ b/src/player/mod.rs @@ -76,8 +76,10 @@ impl Player { let stream = match UnixStream::connect(&self.socket_path) { Ok(s) => s, Err(e) => { - // Connection failed - MPV probably not ready yet - return Err(anyhow::anyhow!("Failed to connect: {}", e)); + // Connection failed - MPV probably not ready yet or has been killed + // Don't propagate error - just leave socket as None + tracing::debug!("Failed to connect to MPV socket: {}", e); + return Ok(()); } }; diff --git a/src/state/mod.rs b/src/state/mod.rs index b03f5a1..cb14f92 100644 --- a/src/state/mod.rs +++ b/src/state/mod.rs @@ -74,14 +74,10 @@ pub struct AppState { pub is_refreshing: bool, pub search_mode: bool, pub search_query: String, - pub search_matches: Vec, - pub search_match_index: usize, - pub tab_search_results: Vec, - pub tab_search_index: usize, - pub playlist_search_matches: Vec, - pub playlist_search_match_index: usize, - pub playlist_tab_search_results: Vec, - pub playlist_tab_search_index: usize, + pub search_results: Vec, + pub search_result_index: usize, + pub playlist_search_results: Vec, + pub playlist_search_result_index: usize, pub visual_mode: bool, pub visual_anchor: usize, pub saved_expanded_dirs: HashSet, @@ -131,14 +127,10 @@ impl AppState { is_refreshing: false, search_mode: false, search_query: String::new(), - search_matches: Vec::new(), - search_match_index: 0, - tab_search_results: Vec::new(), - tab_search_index: 0, - playlist_search_matches: Vec::new(), - playlist_search_match_index: 0, - playlist_tab_search_results: Vec::new(), - playlist_tab_search_index: 0, + search_results: Vec::new(), + search_result_index: 0, + playlist_search_results: Vec::new(), + playlist_search_result_index: 0, visual_mode: false, visual_anchor: 0, saved_expanded_dirs: HashSet::new(), @@ -680,16 +672,12 @@ impl AppState { if self.focus_playlist { // Clear playlist search state - self.playlist_search_matches.clear(); - self.playlist_search_match_index = 0; - self.playlist_tab_search_results.clear(); - self.playlist_tab_search_index = 0; + self.playlist_search_results.clear(); + self.playlist_search_result_index = 0; } else { // Clear file search state - self.search_matches.clear(); - self.search_match_index = 0; - self.tab_search_results.clear(); - self.tab_search_index = 0; + self.search_results.clear(); + self.search_result_index = 0; // Save current folder state self.saved_expanded_dirs = self.expanded_dirs.clone(); } @@ -697,15 +685,16 @@ impl AppState { pub fn exit_search_mode(&mut self) { self.search_mode = false; + self.search_query.clear(); if self.focus_playlist { // Clear playlist search state - self.playlist_tab_search_results.clear(); - self.playlist_tab_search_index = 0; + self.playlist_search_results.clear(); + self.playlist_search_result_index = 0; } else { // Clear file search state - self.tab_search_results.clear(); - self.tab_search_index = 0; + self.search_results.clear(); + self.search_result_index = 0; // Restore folder state from before search self.expanded_dirs = self.saved_expanded_dirs.clone(); self.rebuild_flattened_items(); @@ -724,9 +713,8 @@ impl AppState { fn perform_incremental_search(&mut self) { if self.search_query.is_empty() { - self.tab_search_results.clear(); - self.tab_search_index = 0; - // Don't rebuild tree on every keystroke - only when exiting search + self.search_results.clear(); + self.search_result_index = 0; return; } @@ -735,8 +723,8 @@ impl AppState { collect_matching_paths(&self.cache.file_tree, &self.search_query, &mut matching_paths_with_scores); if matching_paths_with_scores.is_empty() { - self.tab_search_results.clear(); - self.tab_search_index = 0; + self.search_results.clear(); + self.search_result_index = 0; return; } @@ -750,22 +738,30 @@ impl AppState { // Sort by score (highest first), then by original index to prefer first occurrence indexed_matches.sort_by(|a, b| b.1.cmp(&a.1).then_with(|| a.2.cmp(&b.2))); - // Store all matches for tab completion - self.tab_search_results = indexed_matches.iter().map(|(path, _, _)| path.clone()).collect(); - self.tab_search_index = 0; + // Store all matches + self.search_results = indexed_matches.iter().map(|(path, _, _)| path.clone()).collect(); + self.search_result_index = 0; - // Only expand and rebuild if this is a new best match - let best_match = self.tab_search_results[0].clone(); + // Jump to first match + self.jump_to_current_search_result(); + } + + fn jump_to_current_search_result(&mut self) { + if self.search_results.is_empty() { + return; + } + + let current_match = self.search_results[self.search_result_index].clone(); // Check if we need to expand folders for this match - let needs_expand = best_match.ancestors() + let needs_expand = current_match.ancestors() .skip(1) // Skip the file itself .any(|p| !self.expanded_dirs.contains(p)); if needs_expand { - // Close all folders and expand only for the best match + // Close all folders and expand only for the current match self.expanded_dirs.clear(); - let mut parent = best_match.parent(); + let mut parent = current_match.parent(); while let Some(p) = parent { self.expanded_dirs.insert(p.to_path_buf()); parent = p.parent(); @@ -775,8 +771,8 @@ impl AppState { self.rebuild_flattened_items(); } - // Find the best match in the flattened list and jump to it - if let Some(idx) = self.flattened_items.iter().position(|item| item.path == best_match) { + // Find the match in the flattened list and jump to it + if let Some(idx) = self.flattened_items.iter().position(|item| item.path == current_match) { self.selected_index = idx; // Scroll to show the match @@ -794,233 +790,26 @@ impl AppState { } } + pub fn next_search_result(&mut self) { + if !self.search_results.is_empty() { + self.search_result_index = (self.search_result_index + 1) % self.search_results.len(); + self.jump_to_current_search_result(); + } + } + + pub fn prev_search_result(&mut self) { + if !self.search_results.is_empty() { + self.search_result_index = if self.search_result_index == 0 { + self.search_results.len() - 1 + } else { + self.search_result_index - 1 + }; + self.jump_to_current_search_result(); + } + } + pub fn execute_search(&mut self) { - if self.search_query.is_empty() { - self.search_mode = false; - return; - } - - // Collect all matching paths with scores and preserve order - let mut matching_paths_with_scores = Vec::new(); - collect_matching_paths(&self.cache.file_tree, &self.search_query, &mut matching_paths_with_scores); - - if matching_paths_with_scores.is_empty() { - self.search_mode = false; - return; - } - - // Add index to preserve original tree order when scores are equal - let mut indexed_matches: Vec<(PathBuf, i32, usize)> = matching_paths_with_scores - .into_iter() - .enumerate() - .map(|(idx, (path, score))| (path, score, idx)) - .collect(); - - // Sort by score (highest first), then by original index to prefer first occurrence - indexed_matches.sort_by(|a, b| b.1.cmp(&a.1).then_with(|| a.2.cmp(&b.2))); - - let matching_paths_with_scores: Vec<(PathBuf, i32)> = indexed_matches - .into_iter() - .map(|(path, score, _)| (path, score)) - .collect(); - let matching_paths: Vec = matching_paths_with_scores.iter().map(|(path, _)| path.clone()).collect(); - - // Store matching paths (not indices, as they change when folders collapse) - self.search_matches = matching_paths; - - if !self.search_matches.is_empty() { - self.search_match_index = 0; - // Close all folders and expand only for first match - self.expanded_dirs.clear(); - let first_match = self.search_matches[0].clone(); - let mut parent = first_match.parent(); - while let Some(p) = parent { - self.expanded_dirs.insert(p.to_path_buf()); - parent = p.parent(); - } - - // Rebuild flattened items - self.rebuild_flattened_items(); - - // Find first match in flattened list - if let Some(idx) = self.flattened_items.iter().position(|item| item.path == first_match) { - self.selected_index = idx; - } - } - - self.search_mode = false; - } - - pub fn next_search_match(&mut self) { - if !self.search_matches.is_empty() { - self.search_match_index = (self.search_match_index + 1) % self.search_matches.len(); - let target_path = self.search_matches[self.search_match_index].clone(); - - // Close all folders and expand only for this match - self.expanded_dirs.clear(); - let mut parent = target_path.parent(); - while let Some(p) = parent { - self.expanded_dirs.insert(p.to_path_buf()); - parent = p.parent(); - } - - // Rebuild flattened items - self.rebuild_flattened_items(); - - // Find the path in current flattened items - if let Some(idx) = self.flattened_items.iter().position(|item| item.path == target_path) { - self.selected_index = idx; - - // Scroll to show the match - // Account for "... X more below" indicator which takes one line - let visible_end = self.scroll_offset + self.file_panel_visible_height; - let items_below = if visible_end < self.flattened_items.len() { - self.flattened_items.len() - visible_end - } else { - 0 - }; - let effective_height = if items_below > 0 { - self.file_panel_visible_height.saturating_sub(1) - } else { - self.file_panel_visible_height - }; - - if self.selected_index < self.scroll_offset { - self.scroll_offset = self.selected_index; - } else if self.selected_index >= self.scroll_offset + effective_height { - self.scroll_offset = self.selected_index - effective_height + 1; - } - } - } - } - - pub fn prev_search_match(&mut self) { - if !self.search_matches.is_empty() { - if self.search_match_index == 0 { - self.search_match_index = self.search_matches.len() - 1; - } else { - self.search_match_index -= 1; - } - let target_path = self.search_matches[self.search_match_index].clone(); - - // Close all folders and expand only for this match - self.expanded_dirs.clear(); - let mut parent = target_path.parent(); - while let Some(p) = parent { - self.expanded_dirs.insert(p.to_path_buf()); - parent = p.parent(); - } - - // Rebuild flattened items - self.rebuild_flattened_items(); - - // Find the path in current flattened items - if let Some(idx) = self.flattened_items.iter().position(|item| item.path == target_path) { - self.selected_index = idx; - - // Scroll to show the match - // Account for "... X more below" indicator which takes one line - let visible_end = self.scroll_offset + self.file_panel_visible_height; - let items_below = if visible_end < self.flattened_items.len() { - self.flattened_items.len() - visible_end - } else { - 0 - }; - let effective_height = if items_below > 0 { - self.file_panel_visible_height.saturating_sub(1) - } else { - self.file_panel_visible_height - }; - - if self.selected_index < self.scroll_offset { - self.scroll_offset = self.selected_index; - } else if self.selected_index >= self.scroll_offset + effective_height { - self.scroll_offset = self.selected_index - effective_height + 1; - } - } - } - } - - pub fn tab_search_next(&mut self) { - if self.tab_search_results.is_empty() { - return; - } - - // Cycle to next match - self.tab_search_index = (self.tab_search_index + 1) % self.tab_search_results.len(); - let next_match = self.tab_search_results[self.tab_search_index].clone(); - - // Close all folders and expand only for this match - self.expanded_dirs.clear(); - let mut parent = next_match.parent(); - while let Some(p) = parent { - self.expanded_dirs.insert(p.to_path_buf()); - parent = p.parent(); - } - - // Rebuild flattened items - self.rebuild_flattened_items(); - - // Find and select the match - if let Some(idx) = self.flattened_items.iter().position(|item| item.path == next_match) { - self.selected_index = idx; - - // Scroll to show the match - let effective_height = calculate_effective_height( - self.scroll_offset, - self.file_panel_visible_height, - self.flattened_items.len() - ); - - if self.selected_index < self.scroll_offset { - self.scroll_offset = self.selected_index; - } else if self.selected_index >= self.scroll_offset + effective_height { - self.scroll_offset = self.selected_index - effective_height + 1; - } - } - } - - pub fn tab_search_prev(&mut self) { - if self.tab_search_results.is_empty() { - return; - } - - // Cycle to previous match - if self.tab_search_index == 0 { - self.tab_search_index = self.tab_search_results.len() - 1; - } else { - self.tab_search_index -= 1; - } - let prev_match = self.tab_search_results[self.tab_search_index].clone(); - - // Close all folders and expand only for this match - self.expanded_dirs.clear(); - let mut parent = prev_match.parent(); - while let Some(p) = parent { - self.expanded_dirs.insert(p.to_path_buf()); - parent = p.parent(); - } - - // Rebuild flattened items - self.rebuild_flattened_items(); - - // Find and select the match - if let Some(idx) = self.flattened_items.iter().position(|item| item.path == prev_match) { - self.selected_index = idx; - - // Scroll to show the match - let effective_height = calculate_effective_height( - self.scroll_offset, - self.file_panel_visible_height, - self.flattened_items.len() - ); - - if self.selected_index < self.scroll_offset { - self.scroll_offset = self.selected_index; - } else if self.selected_index >= self.scroll_offset + effective_height { - self.scroll_offset = self.selected_index - effective_height + 1; - } - } + self.exit_search_mode(); } pub fn append_playlist_search_char(&mut self, c: char) { @@ -1035,8 +824,8 @@ impl AppState { fn perform_playlist_incremental_search(&mut self) { if self.search_query.is_empty() { - self.playlist_tab_search_results.clear(); - self.playlist_tab_search_index = 0; + self.playlist_search_results.clear(); + self.playlist_search_result_index = 0; return; } @@ -1053,21 +842,29 @@ impl AppState { .collect(); if matching_indices_with_scores.is_empty() { - self.playlist_tab_search_results.clear(); - self.playlist_tab_search_index = 0; + self.playlist_search_results.clear(); + self.playlist_search_result_index = 0; return; } // Sort by score (highest first) matching_indices_with_scores.sort_by(|a, b| b.1.cmp(&a.1)); - // Store all matches for tab completion - self.playlist_tab_search_results = matching_indices_with_scores.iter().map(|(idx, _)| *idx).collect(); - self.playlist_tab_search_index = 0; + // Store all matches + self.playlist_search_results = matching_indices_with_scores.iter().map(|(idx, _)| *idx).collect(); + self.playlist_search_result_index = 0; - // Jump to best match - let best_match_idx = self.playlist_tab_search_results[0]; - self.selected_playlist_index = best_match_idx; + // Jump to first match + self.jump_to_current_playlist_search_result(); + } + + fn jump_to_current_playlist_search_result(&mut self) { + if self.playlist_search_results.is_empty() { + return; + } + + let match_idx = self.playlist_search_results[self.playlist_search_result_index]; + self.selected_playlist_index = match_idx; // Scroll to show the match let effective_height = calculate_effective_height( @@ -1083,174 +880,26 @@ impl AppState { } } - pub fn playlist_tab_search_next(&mut self) { - if self.playlist_tab_search_results.is_empty() { - return; - } - - // Cycle to next match - self.playlist_tab_search_index = (self.playlist_tab_search_index + 1) % self.playlist_tab_search_results.len(); - let next_match_idx = self.playlist_tab_search_results[self.playlist_tab_search_index]; - self.selected_playlist_index = next_match_idx; - - // Scroll to show the match - let effective_height = calculate_effective_height( - self.playlist_scroll_offset, - self.playlist_visible_height, - self.playlist.len() - ); - - if self.selected_playlist_index < self.playlist_scroll_offset { - self.playlist_scroll_offset = self.selected_playlist_index; - } else if self.selected_playlist_index >= self.playlist_scroll_offset + effective_height { - self.playlist_scroll_offset = self.selected_playlist_index - effective_height + 1; + pub fn next_playlist_search_result(&mut self) { + if !self.playlist_search_results.is_empty() { + self.playlist_search_result_index = (self.playlist_search_result_index + 1) % self.playlist_search_results.len(); + self.jump_to_current_playlist_search_result(); } } - pub fn playlist_tab_search_prev(&mut self) { - if self.playlist_tab_search_results.is_empty() { - return; - } - - // Cycle to previous match - if self.playlist_tab_search_index == 0 { - self.playlist_tab_search_index = self.playlist_tab_search_results.len() - 1; - } else { - self.playlist_tab_search_index -= 1; - } - let prev_match_idx = self.playlist_tab_search_results[self.playlist_tab_search_index]; - self.selected_playlist_index = prev_match_idx; - - // Scroll to show the match - let effective_height = calculate_effective_height( - self.playlist_scroll_offset, - self.playlist_visible_height, - self.playlist.len() - ); - - if self.selected_playlist_index < self.playlist_scroll_offset { - self.playlist_scroll_offset = self.selected_playlist_index; - } else if self.selected_playlist_index >= self.playlist_scroll_offset + effective_height { - self.playlist_scroll_offset = self.selected_playlist_index - effective_height + 1; + pub fn prev_playlist_search_result(&mut self) { + if !self.playlist_search_results.is_empty() { + self.playlist_search_result_index = if self.playlist_search_result_index == 0 { + self.playlist_search_results.len() - 1 + } else { + self.playlist_search_result_index - 1 + }; + self.jump_to_current_playlist_search_result(); } } pub fn execute_playlist_search(&mut self) { - if self.search_query.is_empty() { - self.search_mode = false; - return; - } - - // Collect all matching indices with scores - let mut matching_indices_with_scores: Vec<(usize, i32)> = self.playlist - .iter() - .enumerate() - .filter_map(|(idx, path)| { - let filename = path.file_name() - .and_then(|n| n.to_str()) - .unwrap_or(""); - fuzzy_match(filename, &self.search_query).map(|score| (idx, score)) - }) - .collect(); - - if matching_indices_with_scores.is_empty() { - self.search_mode = false; - return; - } - - // Sort by score (highest first) - matching_indices_with_scores.sort_by(|a, b| b.1.cmp(&a.1)); - - // Store matching indices - self.playlist_search_matches = matching_indices_with_scores.iter().map(|(idx, _)| *idx).collect(); - - if !self.playlist_search_matches.is_empty() { - self.playlist_search_match_index = 0; - let first_match_idx = self.playlist_search_matches[0]; - self.selected_playlist_index = first_match_idx; - - // Scroll to show the match - // Account for "... X more below" indicator which takes one line - let visible_end = self.playlist_scroll_offset + self.playlist_visible_height; - let items_below = if visible_end < self.playlist.len() { - self.playlist.len() - visible_end - } else { - 0 - }; - let effective_height = if items_below > 0 { - self.playlist_visible_height.saturating_sub(1) - } else { - self.playlist_visible_height - }; - - if self.selected_playlist_index < self.playlist_scroll_offset { - self.playlist_scroll_offset = self.selected_playlist_index; - } else if self.selected_playlist_index >= self.playlist_scroll_offset + effective_height { - self.playlist_scroll_offset = self.selected_playlist_index - effective_height + 1; - } - } - - self.search_mode = false; - } - - pub fn next_playlist_search_match(&mut self) { - if !self.playlist_search_matches.is_empty() { - self.playlist_search_match_index = (self.playlist_search_match_index + 1) % self.playlist_search_matches.len(); - let match_idx = self.playlist_search_matches[self.playlist_search_match_index]; - self.selected_playlist_index = match_idx; - - // Scroll to show the match - // Account for "... X more below" indicator which takes one line - let visible_end = self.playlist_scroll_offset + self.playlist_visible_height; - let items_below = if visible_end < self.playlist.len() { - self.playlist.len() - visible_end - } else { - 0 - }; - let effective_height = if items_below > 0 { - self.playlist_visible_height.saturating_sub(1) - } else { - self.playlist_visible_height - }; - - if self.selected_playlist_index < self.playlist_scroll_offset { - self.playlist_scroll_offset = self.selected_playlist_index; - } else if self.selected_playlist_index >= self.playlist_scroll_offset + effective_height { - self.playlist_scroll_offset = self.selected_playlist_index - effective_height + 1; - } - } - } - - pub fn prev_playlist_search_match(&mut self) { - if !self.playlist_search_matches.is_empty() { - if self.playlist_search_match_index == 0 { - self.playlist_search_match_index = self.playlist_search_matches.len() - 1; - } else { - self.playlist_search_match_index -= 1; - } - let match_idx = self.playlist_search_matches[self.playlist_search_match_index]; - self.selected_playlist_index = match_idx; - - // Scroll to show the match - // Account for "... X more below" indicator which takes one line - let visible_end = self.playlist_scroll_offset + self.playlist_visible_height; - let items_below = if visible_end < self.playlist.len() { - self.playlist.len() - visible_end - } else { - 0 - }; - let effective_height = if items_below > 0 { - self.playlist_visible_height.saturating_sub(1) - } else { - self.playlist_visible_height - }; - - if self.selected_playlist_index < self.playlist_scroll_offset { - self.playlist_scroll_offset = self.selected_playlist_index; - } else if self.selected_playlist_index >= self.playlist_scroll_offset + effective_height { - self.playlist_scroll_offset = self.selected_playlist_index - effective_height + 1; - } - } + self.exit_search_mode(); } } diff --git a/src/ui/mod.rs b/src/ui/mod.rs index 3c87147..7efa85d 100644 --- a/src/ui/mod.rs +++ b/src/ui/mod.rs @@ -162,7 +162,7 @@ fn render_file_panel(frame: &mut Frame, state: &mut AppState, area: Rect, _tab_m // Store visible height for keyboard navigation scroll calculations state.file_panel_visible_height = visible_height; - let in_search = !state.focus_playlist && (state.search_mode || !state.search_matches.is_empty()); + let in_search = !state.focus_playlist && state.search_mode; let search_query = if in_search { state.search_query.to_lowercase() } else { String::new() }; // Calculate how many items are below the visible area @@ -306,7 +306,7 @@ fn render_right_panel(frame: &mut Frame, state: &mut AppState, area: Rect, _tab_ }; // Check if in playlist search mode - let in_playlist_search = state.focus_playlist && (state.search_mode || !state.playlist_tab_search_results.is_empty() || !state.playlist_search_matches.is_empty()); + let in_playlist_search = state.focus_playlist && state.search_mode; let playlist_search_query = if in_playlist_search { state.search_query.to_lowercase() } else { String::new() }; // Playlist panel (no longer need the player status box) @@ -502,8 +502,8 @@ fn render_status_bar(frame: &mut Frame, state: &AppState, player: &mut Player, a // Show search prompt with current query and match count - LEFT aligned if state.focus_playlist { // Searching in playlist - if !state.playlist_tab_search_results.is_empty() { - format!("/{}_ Playlist Search: {}/{}", state.search_query, state.playlist_tab_search_index + 1, state.playlist_tab_search_results.len()) + if !state.playlist_search_results.is_empty() { + format!("/{}_ Playlist Search: {}/{}", state.search_query, state.playlist_search_result_index + 1, state.playlist_search_results.len()) } else if !state.search_query.is_empty() { format!("/{}_ [no matches]", state.search_query) } else { @@ -511,20 +511,14 @@ fn render_status_bar(frame: &mut Frame, state: &AppState, player: &mut Player, a } } else { // Searching in file panel - if !state.tab_search_results.is_empty() { - format!("/{}_ Search: {}/{}", state.search_query, state.tab_search_index + 1, state.tab_search_results.len()) + if !state.search_results.is_empty() { + format!("/{}_ Search: {}/{}", state.search_query, state.search_result_index + 1, state.search_results.len()) } else if !state.search_query.is_empty() { format!("/{}_ [no matches]", state.search_query) } else { format!("/{}_", state.search_query) } } - } else if !state.search_matches.is_empty() { - // Show search navigation when file search results are active - format!("/{} Search: {}/{}", state.search_query, state.search_match_index + 1, state.search_matches.len()) - } else if !state.playlist_search_matches.is_empty() { - // Show search navigation when playlist search results are active - format!("/{} Playlist Search: {}/{}", state.search_query, state.playlist_search_match_index + 1, state.playlist_search_matches.len()) } else if state.visual_mode { // Show visual mode indicator format!("-- VISUAL -- {} files marked", state.marked_files.len())