diff --git a/cli/src/native/browser.rs b/cli/src/native/browser.rs index 45b5a25..ee2b92d 100644 --- a/cli/src/native/browser.rs +++ b/cli/src/native/browser.rs @@ -111,6 +111,26 @@ fn update_page_target_info_in_pages(pages: &mut [PageInfo], target: &TargetInfo) false } +fn active_page_index_after_removal( + active_page_index: usize, + removed_index: usize, + remaining_pages: usize, +) -> usize { + if remaining_pages == 0 { + return 0; + } + + if removed_index < active_page_index { + return active_page_index - 1; + } + + if active_page_index >= remaining_pages { + return remaining_pages - 1; + } + + active_page_index +} + /// Converts common error messages into AI-friendly, actionable descriptions. pub fn to_ai_friendly_error(error: &str) -> String { let lower = error.to_lowercase(); @@ -830,6 +850,14 @@ impl BrowserManager { } } + fn update_active_page_after_removal(&mut self, removed_index: usize) { + self.active_page_index = active_page_index_after_removal( + self.active_page_index, + removed_index, + self.pages.len(), + ); + } + pub fn tab_list(&self) -> Vec { self.pages .iter() @@ -929,6 +957,7 @@ impl BrowserManager { } let page = self.pages.remove(target_index); + self.update_active_page_after_removal(target_index); let _ = self .client .send_command_typed::<_, Value>( @@ -940,10 +969,6 @@ impl BrowserManager { ) .await; - if self.active_page_index >= self.pages.len() { - self.active_page_index = self.pages.len() - 1; - } - let session_id = self.pages[self.active_page_index].session_id.clone(); self.enable_domains(&session_id).await?; @@ -1201,7 +1226,7 @@ impl BrowserManager { pub fn remove_page_by_target_id(&mut self, target_id: &str) { if let Some(pos) = self.pages.iter().position(|p| p.target_id == target_id) { self.pages.remove(pos); - self.update_active_page_if_needed(); + self.update_active_page_after_removal(pos); } } @@ -1526,6 +1551,26 @@ mod tests { assert_eq!(pages[0].title, "Popup"); } + #[test] + fn test_active_page_index_after_removal_shifts_when_earlier_tab_is_removed() { + assert_eq!(active_page_index_after_removal(2, 0, 3), 1); + } + + #[test] + fn test_active_page_index_after_removal_keeps_same_slot_when_later_tab_is_removed() { + assert_eq!(active_page_index_after_removal(1, 2, 3), 1); + } + + #[test] + fn test_active_page_index_after_removal_clamps_when_active_last_tab_is_removed() { + assert_eq!(active_page_index_after_removal(3, 3, 3), 2); + } + + #[test] + fn test_active_page_index_after_removal_resets_when_last_page_disappears() { + assert_eq!(active_page_index_after_removal(0, 0, 0), 0); + } + #[test] fn test_validate_launch_options_extensions_and_cdp() { let ext = vec!["/path/to/ext".to_string()];