diff --git a/src/app/update/mod.rs b/src/app/update/mod.rs index 6e0adfad8..51e485f3d 100644 --- a/src/app/update/mod.rs +++ b/src/app/update/mod.rs @@ -850,15 +850,23 @@ impl OpenCADStudio { // Web: fetch every script queued by startup language selection or // drawing text discovery. Each script has one shared store entry. Message::PollWebFonts => { + #[cfg(target_arch = "wasm32")] + let autosave = if web_autosave_due(self.savetime_min) { + self.on_autosave() + } else { + Task::none() + }; + #[cfg(not(target_arch = "wasm32"))] + let autosave = Task::none(); let pending = crate::scene::text::web_font::take_pending(); if pending.is_empty() { - return Task::none(); + return autosave; } - Task::batch(pending.into_iter().map(|script| { + Task::batch(std::iter::once(autosave).chain(pending.into_iter().map(|script| { Task::perform(crate::scene::text::web_font::fetch(script), move |res| { Message::WebFontLoaded(script, res) }) - })) + }))) } // Web: a per-script font arrived. The same bytes feed drawing text, @@ -12347,3 +12355,24 @@ fn set_preview_bit(current: u8, bit: u8, enabled: bool) -> u8 { current & !bit } } + +/// Web autosave deadline (SAVETIME minutes), polled from a short tick: the +/// first call arms it, a later one past it re-arms it and answers true. +#[cfg(target_arch = "wasm32")] +fn web_autosave_due(savetime_min: i32) -> bool { + use std::cell::Cell; + thread_local!(static DUE_MS: Cell = const { Cell::new(0.0) }); + let now = js_sys::Date::now(); + let period = savetime_min.max(0) as f64 * 60_000.0; + DUE_MS.with(|due| { + if period == 0.0 || due.get() == 0.0 { + due.set(if period == 0.0 { 0.0 } else { now + period }); + return false; + } + let fire = now >= due.get(); + if fire { + due.set(now + period); + } + fire + }) +} diff --git a/src/app/view/mod.rs b/src/app/view/mod.rs index cdfb01b8f..3ff6f049a 100644 --- a/src/app/view/mod.rs +++ b/src/app/view/mod.rs @@ -2512,8 +2512,10 @@ impl OpenCADStudio { let web_fonts = Subscription::none(); // Periodic autosave (SAVETIME): a `.sv$` recovery copy beside the file // on native, an OPFS snapshot on web. SAVETIME is the interval in - // minutes; 0 disables it. - let autosave = if self.savetime_min > 0 { + // minutes; 0 disables it. The web build checks the deadline from the + // `PollWebFonts` tick instead: a long `time::every` there stalls every + // other timer for its whole period (#1775). + let autosave = if self.savetime_min > 0 && cfg!(not(target_arch = "wasm32")) { iced::time::every(std::time::Duration::from_secs( self.savetime_min as u64 * 60, ))