diff --git a/.gitignore b/.gitignore index 657dc48..5dd051f 100644 --- a/.gitignore +++ b/.gitignore @@ -9,4 +9,5 @@ node_modules/ .DS_Store .playwright-mcp/ *.png +!public/icons/*.png .claude/ diff --git a/app/plugins/pwa.client.ts b/app/plugins/pwa.client.ts new file mode 100644 index 0000000..990a1a2 --- /dev/null +++ b/app/plugins/pwa.client.ts @@ -0,0 +1,6 @@ +export default defineNuxtPlugin(() => { + if (!("serviceWorker" in navigator)) return; + window.addEventListener("load", () => { + navigator.serviceWorker.register("/sw.js").catch(() => {}); + }); +}); diff --git a/nuxt.config.ts b/nuxt.config.ts index 9423ce5..87f694b 100644 --- a/nuxt.config.ts +++ b/nuxt.config.ts @@ -23,11 +23,15 @@ export default defineNuxtConfig({ { name: "color-scheme", content: "light dark" }, { name: "theme-color", media: "(prefers-color-scheme: light)", content: "#f4f1ea" }, { name: "theme-color", media: "(prefers-color-scheme: dark)", content: "#181410" }, + { name: "mobile-web-app-capable", content: "yes" }, { name: "apple-mobile-web-app-capable", content: "yes" }, { name: "apple-mobile-web-app-status-bar-style", content: "default" }, { name: "apple-mobile-web-app-title", content: "ClaudeDo" }, ], link: [ + { rel: "manifest", href: "/manifest.webmanifest" }, + { rel: "apple-touch-icon", href: "/icons/apple-touch-icon.png" }, + { rel: "icon", type: "image/png", sizes: "192x192", href: "/icons/icon-192.png" }, { rel: "preconnect", href: "https://fonts.googleapis.com" }, { rel: "preconnect", href: "https://fonts.gstatic.com", crossorigin: "" }, { diff --git a/public/icons/apple-touch-icon.png b/public/icons/apple-touch-icon.png new file mode 100644 index 0000000..59b90f7 Binary files /dev/null and b/public/icons/apple-touch-icon.png differ diff --git a/public/icons/icon-192.png b/public/icons/icon-192.png new file mode 100644 index 0000000..6e85e8b Binary files /dev/null and b/public/icons/icon-192.png differ diff --git a/public/icons/icon-512.png b/public/icons/icon-512.png new file mode 100644 index 0000000..59fd7e1 Binary files /dev/null and b/public/icons/icon-512.png differ diff --git a/public/icons/icon-maskable-192.png b/public/icons/icon-maskable-192.png new file mode 100644 index 0000000..2b65207 Binary files /dev/null and b/public/icons/icon-maskable-192.png differ diff --git a/public/icons/icon-maskable-512.png b/public/icons/icon-maskable-512.png new file mode 100644 index 0000000..883932f Binary files /dev/null and b/public/icons/icon-maskable-512.png differ diff --git a/public/manifest.webmanifest b/public/manifest.webmanifest new file mode 100644 index 0000000..18ba159 --- /dev/null +++ b/public/manifest.webmanifest @@ -0,0 +1,17 @@ +{ + "id": "/", + "name": "ClaudeDo Inbox", + "short_name": "ClaudeDo", + "description": "Task inbox for the ClaudeDo backlog", + "start_url": "/", + "scope": "/", + "display": "standalone", + "background_color": "#181410", + "theme_color": "#181410", + "icons": [ + { "src": "/icons/icon-192.png", "sizes": "192x192", "type": "image/png", "purpose": "any" }, + { "src": "/icons/icon-512.png", "sizes": "512x512", "type": "image/png", "purpose": "any" }, + { "src": "/icons/icon-maskable-192.png", "sizes": "192x192", "type": "image/png", "purpose": "maskable" }, + { "src": "/icons/icon-maskable-512.png", "sizes": "512x512", "type": "image/png", "purpose": "maskable" } + ] +} diff --git a/public/sw.js b/public/sw.js new file mode 100644 index 0000000..e96ef74 --- /dev/null +++ b/public/sw.js @@ -0,0 +1,6 @@ +// Minimal service worker: enables PWA installability across browsers. +// Intentionally no caching — the app is a private, auth-gated SPA and stale +// assets after a deploy are worse than requiring a connection. +self.addEventListener("install", () => self.skipWaiting()); +self.addEventListener("activate", (event) => event.waitUntil(self.clients.claim())); +self.addEventListener("fetch", () => {});