From 0459f20129db73729c18ecd57c6c7b6d410b43b9 Mon Sep 17 00:00:00 2001 From: Afonsop Date: Mon, 12 Oct 2026 11:34:20 +0300 Subject: [PATCH] fix: close the review findings on the restore-all/deep-links/PWA wave MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Two reviewers, no blockers, two majors — all fixed. - The remembered-tab focus after a Restore-all batch fired at the message boundary, but the LAST restore's tab inserts one async round later (its OPFS recent-cache write precedes the insertion) and the insertion's own activation overwrote the selection. The focus flag now also consumes at the insertion point, so the batch focus holds in the common all-success case. - The service worker cached ANY successful in-scope navigation under the shell key — a direct navigation to a font or JSON would be served as the app shell for every offline navigation until the next online visit. Only the scope root caches as the shell now. Also: '/app/' gets its own web app manifest (the previous commit's 'installable' claim was true only of the marketing site, whose start_url sits OUTSIDE the service worker's scope — an installed app opened offline showed an error page). Its icon is the existing assets/logo.svg, copied next to the page. (cherry picked from commit 369e2c8b) Ported onto main (#1717 review): the build-web.sh and README hunks stay out with the wrapper. --- src/app/update/file.rs | 19 +++++++++++++++---- src/app/update/mod.rs | 2 +- web-app.html | 3 +++ web/app.webmanifest | 18 ++++++++++++++++++ web/sw.js | 6 +++++- 5 files changed, 42 insertions(+), 6 deletions(-) create mode 100644 web/app.webmanifest diff --git a/src/app/update/file.rs b/src/app/update/file.rs index c18815d09..d762a2303 100644 --- a/src/app/update/file.rs +++ b/src/app/update/file.rs @@ -2019,6 +2019,15 @@ impl OpenCADStudio { self.apply_display_defaults(idx); idx }; + // A Restore-all batch's LAST restore inserts its tab here — one + // async round AFTER its settle message. The focus flag therefore + // also fires here: the message-boundary hook alone could run + // before this insertion and be overwritten by it. + #[cfg(target_arch = "wasm32")] + if self.web_session_focus_pending && self.web_autosave_restore_all_remaining == 0 { + self.web_session_focus_pending = false; + self.select_remembered_session_tab(); + } // Missing-on-open: neutral command-line notice plus a flag // the Reference Manager reads. Never an auto-open modal. @@ -4176,19 +4185,19 @@ impl OpenCADStudio { /// from the after-message hook (`web_session_focus_pending`), so the /// whole settle batch — including the last tab insertion — is done. #[cfg(target_arch = "wasm32")] - pub(super) fn select_remembered_session_tab(&mut self) { + pub(super) fn select_remembered_session_tab(&mut self) -> Task { let Some(session) = self.web_session.as_ref() else { - return; + return Task::none(); }; let Some(remembered) = session.tabs.get(session.active) else { - return; + return Task::none(); }; let recovered = crate::io::web_autosave::recovered_name(remembered); let Some(index) = self.tabs.iter().position(|tab| { let name = tab.tab_display_name(); name == remembered.as_str() || name == recovered }) else { - return; + return Task::none(); }; self.active_tab = index; // The active tab changed under the ribbon; re-sync its chips like @@ -4196,6 +4205,8 @@ impl OpenCADStudio { self.sync_ribbon_layers(); self.sync_ribbon_styles(); self.sync_ribbon_from_selection(); + + Task::none() } /// Remember the open-tab list (names + active index) in localStorage — diff --git a/src/app/update/mod.rs b/src/app/update/mod.rs index 7ccce051f..9cb0e0006 100644 --- a/src/app/update/mod.rs +++ b/src/app/update/mod.rs @@ -487,7 +487,7 @@ impl OpenCADStudio { #[cfg(target_arch = "wasm32")] if self.web_session_focus_pending { self.web_session_focus_pending = false; - self.select_remembered_session_tab(); + return self.select_remembered_session_tab(); } self.sync_open_command_history(); // Close the document-level first-touch transaction started by diff --git a/web-app.html b/web-app.html index b66bb39f0..3c380d6df 100644 --- a/web-app.html +++ b/web-app.html @@ -1,6 +1,7 @@ + @@ -46,6 +47,8 @@ + +
diff --git a/web/app.webmanifest b/web/app.webmanifest new file mode 100644 index 000000000..0e79b2c53 --- /dev/null +++ b/web/app.webmanifest @@ -0,0 +1,18 @@ +{ + "name": "Open CAD Studio", + "short_name": "OCS", + "description": "A free CAD editor for DWG and DXF drawings.", + "start_url": "./", + "scope": "./", + "display": "standalone", + "background_color": "#1e1e1e", + "theme_color": "#1e1e1e", + "icons": [ + { + "src": "logo.svg", + "sizes": "any", + "type": "image/svg+xml", + "purpose": "any" + } + ] +} diff --git a/web/sw.js b/web/sw.js index d1e76c95f..597a62c39 100644 --- a/web/sw.js +++ b/web/sw.js @@ -89,9 +89,13 @@ self.addEventListener("fetch", (event) => { event.respondWith( (async () => { const cache = await caches.open(CACHE_NAME); + // Only the scope root is the app shell: caching any other in-scope + // navigation here would store e.g. a font or JSON body under the + // shell key and serve it for every offline navigation. + const isShell = new URL(request.url).pathname === SHELL_URL.pathname; try { const fresh = await fetch(request); - await put(cache, SHELL_URL, fresh); + if (isShell) await put(cache, SHELL_URL, fresh); return fresh; } catch { const cached = await cache.match(SHELL_URL);