From b94a976e5f6801c40ee3f3dcaead3e5cfa5b3e16 Mon Sep 17 00:00:00 2001 From: Julian Freeman Date: Sun, 22 Mar 2026 16:46:37 -0400 Subject: [PATCH] support set interval --- src-tauri/src/engine.rs | 41 ++++++++++++++++++++++++++--------------- src-tauri/src/lib.rs | 4 +++- src/App.vue | 19 +++++++++++++++++++ 3 files changed, 48 insertions(+), 16 deletions(-) diff --git a/src-tauri/src/engine.rs b/src-tauri/src/engine.rs index 3245c8a..610c489 100644 --- a/src-tauri/src/engine.rs +++ b/src-tauri/src/engine.rs @@ -10,9 +10,15 @@ use tauri_plugin_store::StoreExt; pub struct AppState { pub is_paused: Arc, + pub capture_interval_secs: std::sync::atomic::AtomicU64, pub toggle_menu_item: std::sync::Mutex>>, } +#[tauri::command] +pub fn update_interval(state: tauri::State<'_, AppState>, seconds: u64) { + state.capture_interval_secs.store(seconds, Ordering::SeqCst); +} + #[tauri::command] pub fn toggle_pause(app: tauri::AppHandle, state: tauri::State<'_, AppState>) -> bool { let current = state.is_paused.load(Ordering::SeqCst); @@ -78,26 +84,27 @@ pub fn get_timeline(date: String, base_dir: String) -> Vec { } pub fn start_engine(app: AppHandle) { - let state = app.state::(); - let is_paused = state.is_paused.clone(); - // Start Cleanup routine - let app_clone = app.clone(); + let app_cleanup = app.clone(); tauri::async_runtime::spawn(async move { // Run once on startup, then every 24 hours let mut ticker = interval(Duration::from_secs(60 * 60 * 24)); loop { ticker.tick().await; - if let Err(e) = run_cleanup(&app_clone).await { + if let Err(e) = run_cleanup(&app_cleanup).await { eprintln!("Cleanup error: {}", e); } } }); + let app_capture = app.clone(); tauri::async_runtime::spawn(async move { - // Check every 500ms to be precise about the 0th second + let state = app_capture.state::(); + let is_paused = state.is_paused.clone(); + + // Check frequently to catch the aligned second let mut ticker = interval(Duration::from_millis(500)); - let mut last_minute = -1; + let mut last_capture_time = 0; loop { ticker.tick().await; @@ -106,16 +113,20 @@ pub fn start_engine(app: AppHandle) { continue; } - let now = Local::now(); - let current_minute = now.format("%M").to_string().parse::().unwrap_or(0); - let current_second = now.format("%S").to_string().parse::().unwrap_or(0); + let interval_secs = state.capture_interval_secs.load(Ordering::SeqCst); + if interval_secs == 0 { continue; } - // Trigger only if it's the 0th second and we haven't captured this minute yet - if current_second == 0 && current_minute != last_minute { - last_minute = current_minute; - println!("Tick: capturing screen at {:02}:{:02}:00...", now.format("%H"), current_minute); - if let Err(e) = capture_screens(&app).await { + let now = Local::now(); + let timestamp = now.timestamp() as u64; + + // Trigger if aligned to interval and we haven't captured this exact second yet + if timestamp % interval_secs == 0 && timestamp != last_capture_time { + last_capture_time = timestamp; + println!("Tick: capturing screen at {}...", now.format("%H:%M:%S")); + if let Err(e) = capture_screens(&app_capture).await { eprintln!("Failed to capture screens: {}", e); + } else { + let _ = app_capture.emit("refresh-timeline", ()); } } } diff --git a/src-tauri/src/lib.rs b/src-tauri/src/lib.rs index e794469..6efbc1b 100644 --- a/src-tauri/src/lib.rs +++ b/src-tauri/src/lib.rs @@ -20,11 +20,13 @@ pub fn run() { engine::toggle_pause, engine::get_pause_state, engine::get_timeline, - engine::get_image_base64 + engine::get_image_base64, + engine::update_interval ]) .setup(|app| { app.manage(engine::AppState { is_paused: Arc::new(AtomicBool::new(false)), + capture_interval_secs: std::sync::atomic::AtomicU64::new(30), toggle_menu_item: std::sync::Mutex::new(None), }); tray::create_tray(app.handle())?; diff --git a/src/App.vue b/src/App.vue index a1ef76b..fe95b73 100644 --- a/src/App.vue +++ b/src/App.vue @@ -29,6 +29,7 @@ const isFullscreen = ref(false); const isSettingsOpen = ref(false); const mergeScreens = ref(false); const retainDays = ref(30); +const captureInterval = ref(30); const hoveredTime = ref(null); const timelineRef = ref(null); @@ -45,6 +46,11 @@ onMounted(async () => { isSetupComplete.value = true; mergeScreens.value = (await store.get("mergeScreens")) as boolean || false; retainDays.value = (await store.get("retainDays")) as number || 30; + captureInterval.value = (await store.get("captureInterval")) as number || 30; + + // Sync initial interval to Rust + await invoke("update_interval", { seconds: captureInterval.value }); + isPaused.value = await invoke("get_pause_state"); await loadTimeline(); } @@ -77,7 +83,11 @@ const selectFolder = async () => { const updateSettings = async () => { await store.set("mergeScreens", mergeScreens.value); await store.set("retainDays", retainDays.value); + await store.set("captureInterval", captureInterval.value); await store.save(); + + // Sync interval to Rust + await invoke("update_interval", { seconds: captureInterval.value }); }; const togglePauseState = async () => { @@ -325,6 +335,15 @@ const handleTimelineClick = (e: MouseEvent) => { +
+
+ + {{ captureInterval }} Seconds +
+ +

Frequency of snapshots (10s to 10m).

+
+