diff --git a/crates/ely_browser_core/src/state/commands.rs b/crates/ely_browser_core/src/state/commands.rs index 1012eeb..ba30ac1 100644 --- a/crates/ely_browser_core/src/state/commands.rs +++ b/crates/ely_browser_core/src/state/commands.rs @@ -216,6 +216,9 @@ impl BrowserCore { "sleep-tab-group" | "sleep tab group" | "discard-tab-group" | "discard tab group" => { Ok(self.discard_active_tab_group()?.is_some()) } + "refresh-tab-group" | "refresh tab group" | "reload-tab-group" | "reload tab group" => { + Ok(self.refresh_active_tab_group()?.is_some()) + } "close-tab-group" | "close tab group" | "archive-tab-group" | "archive tab group" => { Ok(self.close_active_tab_group()?.is_some()) } diff --git a/crates/ely_browser_core/src/state/tab_groups.rs b/crates/ely_browser_core/src/state/tab_groups.rs index 757e8d9..c3f8ff5 100644 --- a/crates/ely_browser_core/src/state/tab_groups.rs +++ b/crates/ely_browser_core/src/state/tab_groups.rs @@ -93,6 +93,19 @@ impl BrowserCore { Ok(Some(tab_ids.len())) } + pub fn refresh_active_tab_group(&mut self) -> Result, CoreError> { + let Some(group_id) = self.active_tab_group_id()? else { + return Ok(None); + }; + let tab_ids = self.active_space_group_tab_ids(&group_id); + + for tab_id in &tab_ids { + self.refresh_tab(tab_id)?; + } + + Ok(Some(tab_ids.len())) + } + pub fn close_active_tab_group(&mut self) -> Result, CoreError> { let Some(group_id) = self.active_tab_group_id()? else { return Ok(None); diff --git a/crates/ely_browser_core/src/state/tab_lifecycle.rs b/crates/ely_browser_core/src/state/tab_lifecycle.rs index 9ef3e9e..2ab4e7e 100644 --- a/crates/ely_browser_core/src/state/tab_lifecycle.rs +++ b/crates/ely_browser_core/src/state/tab_lifecycle.rs @@ -1,3 +1,5 @@ +use std::time::SystemTime; + use ely_domain::TabId; use super::BrowserCore; @@ -42,6 +44,20 @@ impl BrowserCore { self.mark_tab_ready(tab_id) } + pub(super) fn refresh_tab(&mut self, tab_id: &TabId) -> Result { + { + let tab = self + .tabs + .iter_mut() + .find(|tab| tab.id() == tab_id) + .ok_or_else(|| CoreError::TabNotFound { id: tab_id.clone() })?; + tab.mark_ready(); + } + + self.record_tab_activity(tab_id, SystemTime::now()); + Ok(tab_id.clone()) + } + fn mark_tab_ready(&mut self, tab_id: &TabId) -> Result { { let tab = self diff --git a/crates/ely_browser_core/tests/tab_groups.rs b/crates/ely_browser_core/tests/tab_groups.rs index 45713dc..7ba3756 100644 --- a/crates/ely_browser_core/tests/tab_groups.rs +++ b/crates/ely_browser_core/tests/tab_groups.rs @@ -203,6 +203,46 @@ fn sleep_tab_group_command_preserves_query_without_active_group() -> Result<(), Ok(()) } +#[test] +fn refresh_tab_group_command_marks_active_group_tabs_ready() -> Result<(), Box> { + let mut core = BrowserCore::new(InitialBrowserConfig::ely_defaults()?)?; + let ungrouped_tab_id = core.open_tab(UrlText::parse("https://servo.org")?); + let first_group_tab_id = core.open_tab(UrlText::parse("https://example.com/a")?); + core.group_active_tab("Docs")?; + let second_group_tab_id = core.open_tab(UrlText::parse("https://example.com/b")?); + core.group_active_tab("Docs")?; + core.crash_tab(&first_group_tab_id)?; + core.discard_tab(&second_group_tab_id)?; + + core.set_command_query(">refresh-tab-group"); + let intent = core.submit_command()?; + let snapshot = core.snapshot()?; + + assert_eq!(intent, Some(CommandIntent::Command("refresh-tab-group".to_string()))); + assert_eq!(snapshot.command_query, ""); + assert_eq!(snapshot.tab_groups.len(), 1); + assert_eq!(tab_state(&snapshot, &first_group_tab_id), Some(&TabState::Ready)); + assert_eq!(tab_state(&snapshot, &second_group_tab_id), Some(&TabState::Ready)); + assert_eq!(tab_state(&snapshot, &ungrouped_tab_id), Some(&TabState::Ready)); + Ok(()) +} + +#[test] +fn refresh_tab_group_command_preserves_query_without_active_group() -> Result<(), Box> { + let mut core = BrowserCore::new(InitialBrowserConfig::ely_defaults()?)?; + let tab_id = core.open_tab(UrlText::parse("https://example.com")?); + core.discard_tab(&tab_id)?; + + core.set_command_query(">refresh-tab-group"); + let intent = core.submit_command()?; + let snapshot = core.snapshot()?; + + assert_eq!(intent, Some(CommandIntent::Command("refresh-tab-group".to_string()))); + assert_eq!(snapshot.command_query, ">refresh-tab-group"); + assert_eq!(tab_state(&snapshot, &tab_id), Some(&TabState::Discarded)); + Ok(()) +} + #[test] fn close_tab_group_command_archives_active_group_tabs() -> Result<(), Box> { let mut core = BrowserCore::new(InitialBrowserConfig::ely_defaults()?)?; diff --git a/docs/ui-shell.md b/docs/ui-shell.md index cff768b..c19c95a 100644 --- a/docs/ui-shell.md +++ b/docs/ui-shell.md @@ -104,6 +104,23 @@ Group sleep applies the sleeping state to every tab in the active group: └──────────────────────────────┴───────────────────────────────────────────────┘ ``` +Group refresh brings every tab in the active group back to ready: + +```text +┌──────────────────────────────────────────────────────────────────────────────┐ +│ ELY Browser [ >refresh-tab-group.......................... ] [pin] [*] [+] │ +├──────────────────────────────┬───────────────────────────────────────────────┤ +│ Tabs │ example.com │ +│ [folder] Docs │ https://example.com/b │ +│ 2 tabs - Expanded │ │ +│ example.com │ State Ready │ +│ example.com │ Group Docs │ +│ example.com │ │ +│ example.com │ │ +│ servo.org │ │ +└──────────────────────────────┴───────────────────────────────────────────────┘ +``` + Group close archives every tab in the active group and removes the empty group row: ```text