Make the app installable as a PWA
Adds a web manifest, icons (standard 192/512, a full-bleed maskable 512 with the glyph kept inside the safe zone, and an iOS touch icon), theme-color/apple meta tags, and a service worker, so "Install app" / "Add to Home Screen" gives it its own icon and a standalone window. The service worker is a deliberate no-cache pass-through: it registers a fetch listener (so browsers treat the app as installable) but never calls respondWith(), so every request goes to the network exactly as without a worker. A caching worker would keep serving an old JS bundle after each rebuild — the same stale-build confusion that already cost time on the settings layout fix — and this app is a live view of authenticated data with no useful offline mode. Registered in production builds only. Icons are generated procedurally (server-rack glyph on the sidebar's dark colour) and encoded as real PNGs; visually checked, including that the maskable variant keeps the glyph inside the safe zone. Verified in a real Chromium (headless Edge) over the DevTools protocol against the built bundle served with the same express.static setup as production: the DevTools installability audit reported no errors, the manifest parsed with no errors, the worker registered at scope "/", activated and took control, and a navigation through the worker returned the app page normally. The manifest is served as application/manifest+json and sw.js as JavaScript. The in-app preview pane silently blocks service-worker script fetches, so it could not be used for this — hence the real browser. Real dev database mtime untouched throughout. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -135,6 +135,12 @@ real API response (fixed to derive them from `connectedToControl` and
|
||||
`enabledRoutes`). See the git log for the full verification notes per
|
||||
integration.
|
||||
|
||||
The app is installable as a PWA — "Install app" / "Add to Home Screen" from the
|
||||
browser gives it its own icon and a standalone window on phone or desktop. This
|
||||
needs the site to be served over HTTPS (browsers only offer install on secure
|
||||
origins; `localhost` also counts). The bundled service worker deliberately
|
||||
caches nothing, so an installed copy always shows the current build.
|
||||
|
||||
## Requirements
|
||||
|
||||
- Node.js 20+
|
||||
|
||||
@@ -4,6 +4,13 @@
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Homelab Manager</title>
|
||||
<link rel="manifest" href="/manifest.webmanifest" />
|
||||
<link rel="icon" type="image/png" href="/icon-192.png" />
|
||||
<link rel="apple-touch-icon" href="/apple-touch-icon.png" />
|
||||
<meta name="theme-color" content="#182433" />
|
||||
<meta name="mobile-web-app-capable" content="yes" />
|
||||
<meta name="apple-mobile-web-app-capable" content="yes" />
|
||||
<meta name="apple-mobile-web-app-title" content="Homelab" />
|
||||
<script>
|
||||
(function () {
|
||||
try {
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 797 B |
Binary file not shown.
|
After Width: | Height: | Size: 1.7 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 5.6 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 3.7 KiB |
@@ -0,0 +1,16 @@
|
||||
{
|
||||
"id": "/",
|
||||
"name": "Homelab Manager",
|
||||
"short_name": "Homelab",
|
||||
"description": "One dashboard for Proxmox, Synology, Docker, Tailscale, DNS, and more.",
|
||||
"start_url": "/",
|
||||
"scope": "/",
|
||||
"display": "standalone",
|
||||
"background_color": "#182433",
|
||||
"theme_color": "#182433",
|
||||
"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-maskable-512.png", "sizes": "512x512", "type": "image/png", "purpose": "maskable" }
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
// Exists so browsers treat the app as installable. It deliberately caches
|
||||
// nothing and never calls respondWith(): every request goes to the network
|
||||
// exactly as if there were no service worker. A caching worker would keep
|
||||
// serving an old JS bundle after a rebuild, and this app is a live view of
|
||||
// authenticated data with no useful offline mode anyway.
|
||||
self.addEventListener("install", () => self.skipWaiting());
|
||||
self.addEventListener("activate", (event) => event.waitUntil(self.clients.claim()));
|
||||
self.addEventListener("fetch", () => {});
|
||||
@@ -5,6 +5,15 @@ import App from "./App";
|
||||
import "@tabler/core/dist/css/tabler.min.css";
|
||||
import "./styles.css";
|
||||
|
||||
// Production only — in dev the worker would just add another layer between Vite and the browser.
|
||||
if ("serviceWorker" in navigator && import.meta.env.PROD) {
|
||||
window.addEventListener("load", () => {
|
||||
navigator.serviceWorker.register("/sw.js").catch(() => {
|
||||
// Installability is a nicety; the app works fine without a worker.
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
ReactDOM.createRoot(document.getElementById("root")!).render(
|
||||
<React.StrictMode>
|
||||
<BrowserRouter>
|
||||
|
||||
Reference in New Issue
Block a user