From fd7f8c3ef07846e98d57be25a6784441da5a1ab8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=9B=B7=E7=94=B5=E8=8A=BD=E8=A1=A3?= Date: Fri, 10 Jul 2026 12:15:20 -0400 Subject: [PATCH] feat(tabs): run the idle archive policy on a periodic sweep --- .../src/shell/chrome/settings_layout.rs | 5 ++ .../src/shell/internal_pages/sidebar_tabs.rs | 2 +- crates/ely_app/src/shell/mod.rs | 26 +------ crates/ely_app/src/shell/timers.rs | 73 +++++++++++++++++++ 4 files changed, 82 insertions(+), 24 deletions(-) create mode 100644 crates/ely_app/src/shell/timers.rs diff --git a/crates/ely_app/src/shell/chrome/settings_layout.rs b/crates/ely_app/src/shell/chrome/settings_layout.rs index 1c39d69..b9c5896 100644 --- a/crates/ely_app/src/shell/chrome/settings_layout.rs +++ b/crates/ely_app/src/shell/chrome/settings_layout.rs @@ -23,6 +23,11 @@ const NAV_GROUPS: &[NavGroup] = &[ NavGroup { label: "GENERAL", items: &[ + NavItem { + icon: IconName::Settings2, + label: "General", + route: "ely://settings/general", + }, NavItem { icon: IconName::Palette, label: "Appearance", diff --git a/crates/ely_app/src/shell/internal_pages/sidebar_tabs.rs b/crates/ely_app/src/shell/internal_pages/sidebar_tabs.rs index 080c956..afcfc9a 100644 --- a/crates/ely_app/src/shell/internal_pages/sidebar_tabs.rs +++ b/crates/ely_app/src/shell/internal_pages/sidebar_tabs.rs @@ -24,7 +24,7 @@ const ARCHIVE_POLICY_OPTIONS: &[ArchivePolicyOption] = &[ }, ArchivePolicyOption { label: "Today", - detail: "Archive idle unpinned tabs during daily cleanup.", + detail: "Archive unpinned tabs left idle since yesterday.", policy: ArchivePolicy::IdleDays(0), }, ArchivePolicyOption { diff --git a/crates/ely_app/src/shell/mod.rs b/crates/ely_app/src/shell/mod.rs index a762a8a..8644a04 100644 --- a/crates/ely_app/src/shell/mod.rs +++ b/crates/ely_app/src/shell/mod.rs @@ -31,6 +31,7 @@ mod sync_inbox; mod sync_state; mod tab_groups; mod tab_lifecycle; +mod timers; mod web_surface; mod web_surface_cadence; mod web_surface_controller; @@ -52,7 +53,7 @@ mod gpui_harness_tests; use ely_browser_core::{BrowserCore, InitialBrowserConfig}; use ely_domain::{DEFAULT_TRANSLUCENCY_PCT, ProfileId, SpaceId, TabId}; -use gpui::{AppContext, Context, Entity, FocusHandle, Subscription, Timer, Window}; +use gpui::{AppContext, Context, Entity, FocusHandle, Subscription, Window}; use gpui_component::input::{InputEvent, InputState}; use gpui_component::slider::{SliderEvent, SliderState, SliderValue}; @@ -313,7 +314,7 @@ impl ElyShell { if should_run_initial_sync { shell.trigger_cloud_sync_upload(); } - start_external_web_surface_timer(cx); + timers::start(cx); shell } @@ -477,24 +478,3 @@ impl ElyShell { } } } - -fn start_external_web_surface_timer(cx: &mut Context) { - cx.spawn(async move |shell, cx| { - loop { - let delay = match shell.update(cx, |shell, _| shell.external_web_surface_tick_delay()) { - Ok(delay) => delay, - Err(_) => break, - }; - Timer::after(delay).await; - let result = shell.update(cx, |shell, cx| { - if shell.tick_external_web_surfaces(cx) { - cx.notify(); - } - }); - if result.is_err() { - break; - } - } - }) - .detach(); -} diff --git a/crates/ely_app/src/shell/timers.rs b/crates/ely_app/src/shell/timers.rs new file mode 100644 index 0000000..ae3dee8 --- /dev/null +++ b/crates/ely_app/src/shell/timers.rs @@ -0,0 +1,73 @@ +//! Long-lived shell timers, started once per window at construction. + +use std::time::Duration; + +use gpui::{Context, Timer}; + +use super::{ElyShell, ShellState}; + +/// Idle tabs archive on this cadence when the active Space opts into an +/// idle policy; the sweep is a no-op for `ArchivePolicy::Manual`. +const IDLE_ARCHIVE_SWEEP_INTERVAL: Duration = Duration::from_secs(30 * 60); + +pub(super) fn start(cx: &mut Context) { + start_external_web_surface_timer(cx); + start_idle_archive_timer(cx); +} + +fn start_external_web_surface_timer(cx: &mut Context) { + cx.spawn(async move |shell, cx| { + loop { + let delay = match shell.update(cx, |shell, _| shell.external_web_surface_tick_delay()) { + Ok(delay) => delay, + Err(_) => break, + }; + Timer::after(delay).await; + let result = shell.update(cx, |shell, cx| { + if shell.tick_external_web_surfaces(cx) { + cx.notify(); + } + }); + if result.is_err() { + break; + } + } + }) + .detach(); +} + +fn start_idle_archive_timer(cx: &mut Context) { + cx.spawn(async move |shell, cx| { + loop { + Timer::after(IDLE_ARCHIVE_SWEEP_INTERVAL).await; + let result = shell.update(cx, |shell, cx| { + let ShellState::Ready(core) = &mut shell.state else { + return; + }; + match core.archive_idle_tabs(std::time::SystemTime::now()) { + Ok(0) => {} + Ok(archived) => { + tracing::info!( + target: "ely::archive", + archived, + "idle archive sweep archived tabs", + ); + shell.schedule_cloud_sync_upload(cx); + cx.notify(); + } + Err(error) => { + tracing::warn!( + target: "ely::archive", + error = %error, + "idle archive sweep failed", + ); + } + } + }); + if result.is_err() { + break; + } + } + }) + .detach(); +}