From eef96548b81dc582fb62d37ac6414ea9bae3733f Mon Sep 17 00:00:00 2001 From: naps62-yolo Date: Sat, 20 Jun 2026 21:38:44 +0000 Subject: [PATCH] feat(maestro): make web UI an installable PWA Add a web app manifest, icons, and a runtime-caching service worker so the UI can be installed on Android Chrome (and added to the iOS home screen). - web/public/manifest.webmanifest: standalone display, theme/bg colors, icon set - web/public/icon.svg + generated icon-192/512 and apple-touch-icon PNGs - web/public/sw.js: caches the static shell for offline launch; never touches /api (HTTP or WS), so live-daemon behavior is unchanged - index.html: link manifest + apple-touch-icon, iOS standalone metas - main.ts: register the service worker (production only) Assets land in web/dist via Vite's public/ dir and are embedded by the existing include_dir! in maestro-web, so no Rust changes are needed. Co-Authored-By: Claude Opus 4.8 (1M context) --- web/index.html | 7 ++++ web/public/apple-touch-icon.png | Bin 0 -> 8023 bytes web/public/icon-192.png | Bin 0 -> 8589 bytes web/public/icon-512.png | Bin 0 -> 24276 bytes web/public/icon.svg | 6 +++ web/public/manifest.webmanifest | 38 +++++++++++++++++ web/public/sw.js | 72 ++++++++++++++++++++++++++++++++ web/src/main.ts | 13 ++++++ 8 files changed, 136 insertions(+) create mode 100644 web/public/apple-touch-icon.png create mode 100644 web/public/icon-192.png create mode 100644 web/public/icon-512.png create mode 100644 web/public/icon.svg create mode 100644 web/public/manifest.webmanifest create mode 100644 web/public/sw.js diff --git a/web/index.html b/web/index.html index 8749601..7d656d6 100644 --- a/web/index.html +++ b/web/index.html @@ -14,6 +14,13 @@ rel="icon" href="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 32 32'%3E%3Crect width='32' height='32' rx='8' fill='%231a1d24'/%3E%3Ccircle cx='16' cy='16' r='6' fill='%235fe3a1'/%3E%3Ccircle cx='16' cy='16' r='10' fill='none' stroke='%235fe3a1' stroke-opacity='0.4' stroke-width='1.5'/%3E%3C/svg%3E" /> + + + + + + +
diff --git a/web/public/apple-touch-icon.png b/web/public/apple-touch-icon.png new file mode 100644 index 0000000000000000000000000000000000000000..872e2b38b8dd02bb2247c53aa629a2e5e902f687 GIT binary patch literal 8023 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 literal 0 HcmV?d00001 diff --git a/web/public/icon-192.png b/web/public/icon-192.png new file mode 100644 index 0000000000000000000000000000000000000000..ceefb25657ac1d3acae9c5c00873db784f313f60 GIT binary patch literal 8589 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 H*gWii%>l(! literal 0 HcmV?d00001 diff --git a/web/public/icon-512.png b/web/public/icon-512.png new file mode 100644 index 0000000000000000000000000000000000000000..27c9d9862961daed91a335aa32df3df075c753ad GIT binary patch literal 24276 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 TJ0b87fk4U%>hc&_lYsvNSv%al literal 0 HcmV?d00001 diff --git a/web/public/icon.svg b/web/public/icon.svg new file mode 100644 index 0000000..ec5ae5c --- /dev/null +++ b/web/public/icon.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/web/public/manifest.webmanifest b/web/public/manifest.webmanifest new file mode 100644 index 0000000..dbe56ba --- /dev/null +++ b/web/public/manifest.webmanifest @@ -0,0 +1,38 @@ +{ + "name": "maestro", + "short_name": "maestro", + "description": "Unified terminal-session orchestrator — see and attach to every shell and agent across your machines.", + "id": "/", + "start_url": "/", + "scope": "/", + "display": "standalone", + "orientation": "any", + "background_color": "#1a1d24", + "theme_color": "#1a1d24", + "icons": [ + { + "src": "/icon-192.png", + "sizes": "192x192", + "type": "image/png", + "purpose": "any" + }, + { + "src": "/icon-512.png", + "sizes": "512x512", + "type": "image/png", + "purpose": "any" + }, + { + "src": "/icon-512.png", + "sizes": "512x512", + "type": "image/png", + "purpose": "maskable" + }, + { + "src": "/icon.svg", + "sizes": "any", + "type": "image/svg+xml", + "purpose": "any" + } + ] +} diff --git a/web/public/sw.js b/web/public/sw.js new file mode 100644 index 0000000..9b48878 --- /dev/null +++ b/web/public/sw.js @@ -0,0 +1,72 @@ +// maestro service worker. +// +// Goal: make the app installable and let the shell load offline. It is NOT a +// data cache — anything under /api (HTTP and WebSocket upgrades) is always left +// to the network, since a terminal orchestrator is useless without a live +// daemon. Static assets are cached at runtime so we never need to know Vite's +// hashed filenames ahead of time. + +const CACHE = "maestro-shell-v1"; + +self.addEventListener("install", (event) => { + // Warm the cache with the app entry so a cold offline launch still paints. + event.waitUntil( + caches + .open(CACHE) + .then((cache) => cache.addAll(["/", "/manifest.webmanifest"])) + .catch(() => {}), + ); + self.skipWaiting(); +}); + +self.addEventListener("activate", (event) => { + // Drop caches from previous versions, then take control immediately. + event.waitUntil( + caches + .keys() + .then((keys) => + Promise.all(keys.filter((k) => k !== CACHE).map((k) => caches.delete(k))), + ) + .then(() => self.clients.claim()), + ); +}); + +self.addEventListener("fetch", (event) => { + const req = event.request; + if (req.method !== "GET") return; + + const url = new URL(req.url); + // Only handle same-origin GETs; never touch the daemon API. + if (url.origin !== self.location.origin) return; + if (url.pathname.startsWith("/api/")) return; + + // SPA navigations: network-first, fall back to the cached shell offline. + if (req.mode === "navigate") { + event.respondWith( + fetch(req) + .then((res) => { + const copy = res.clone(); + caches.open(CACHE).then((c) => c.put("/", copy)).catch(() => {}); + return res; + }) + .catch(() => caches.match("/").then((r) => r || caches.match(req))), + ); + return; + } + + // Static assets (Vite emits content-hashed names): stale-while-revalidate. + event.respondWith( + caches.match(req).then((cached) => { + const network = fetch(req) + .then((res) => { + if (res.ok && res.type === "basic") { + const copy = res.clone(); + caches.open(CACHE).then((c) => c.put(req, copy)).catch(() => {}); + } + return res; + }) + .catch(() => cached); + return cached || network; + }), + ); +}); diff --git a/web/src/main.ts b/web/src/main.ts index f4eb6cb..9cd615d 100644 --- a/web/src/main.ts +++ b/web/src/main.ts @@ -9,6 +9,19 @@ import "./styles.css"; initTheme(); installViewportSizing(); +registerServiceWorker(); + +// Register the PWA service worker so the app is installable and the shell loads +// offline. Production only — in dev the SW would fight Vite's HMR. +function registerServiceWorker(): void { + if (import.meta.env.DEV) return; + if (!("serviceWorker" in navigator)) return; + window.addEventListener("load", () => { + void navigator.serviceWorker.register("/sw.js").catch(() => { + // Best-effort: a failed registration must never break the app. + }); + }); +} const rootEl = document.getElementById("app"); if (!rootEl) {