feat(ui): add PWA configuration with service worker and enhanced manifest

Adds service worker with network-first navigation (SPA fallback) and
cache-first static assets. Enhances web manifest with start_url, scope,
id, description, orientation, and maskable icon entry.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Dotta
2026-03-05 17:01:07 -06:00
parent 6ee4315eef
commit 0e387426fa
3 changed files with 81 additions and 4 deletions

View File

@@ -1,6 +1,14 @@
{
"id": "/",
"name": "Paperclip",
"short_name": "Paperclip",
"description": "AI-powered project management and agent coordination platform",
"start_url": "/",
"scope": "/",
"display": "standalone",
"orientation": "any",
"theme_color": "#18181b",
"background_color": "#18181b",
"icons": [
{
"src": "/android-chrome-192x192.png",
@@ -11,9 +19,12 @@
"src": "/android-chrome-512x512.png",
"sizes": "512x512",
"type": "image/png"
},
{
"src": "/android-chrome-512x512.png",
"sizes": "512x512",
"type": "image/png",
"purpose": "maskable"
}
],
"theme_color": "#18181b",
"background_color": "#18181b",
"display": "standalone"
]
}

60
ui/public/sw.js Normal file
View File

@@ -0,0 +1,60 @@
const CACHE_NAME = "paperclip-v1";
const STATIC_ASSETS = ["/", "/favicon.ico", "/favicon.svg"];
self.addEventListener("install", (event) => {
event.waitUntil(
caches.open(CACHE_NAME).then((cache) => cache.addAll(STATIC_ASSETS))
);
self.skipWaiting();
});
self.addEventListener("activate", (event) => {
event.waitUntil(
caches.keys().then((keys) =>
Promise.all(
keys
.filter((key) => key !== CACHE_NAME)
.map((key) => caches.delete(key))
)
)
);
self.clients.claim();
});
self.addEventListener("fetch", (event) => {
const { request } = event;
const url = new URL(request.url);
// Skip non-GET requests and API calls
if (request.method !== "GET" || url.pathname.startsWith("/api")) {
return;
}
// Network-first for navigation requests (SPA)
if (request.mode === "navigate") {
event.respondWith(
fetch(request)
.then((response) => {
const clone = response.clone();
caches.open(CACHE_NAME).then((cache) => cache.put(request, clone));
return response;
})
.catch(() => caches.match("/"))
);
return;
}
// Cache-first for static assets
event.respondWith(
caches.match(request).then((cached) => {
if (cached) return cached;
return fetch(request).then((response) => {
if (response.ok && url.origin === self.location.origin) {
const clone = response.clone();
caches.open(CACHE_NAME).then((cache) => cache.put(request, clone));
}
return response;
});
})
);
});

View File

@@ -15,6 +15,12 @@ import { TooltipProvider } from "@/components/ui/tooltip";
import "@mdxeditor/editor/style.css";
import "./index.css";
if ("serviceWorker" in navigator) {
window.addEventListener("load", () => {
navigator.serviceWorker.register("/sw.js");
});
}
const queryClient = new QueryClient({
defaultOptions: {
queries: {