mirror of
https://github.com/purrgrammer/grimoire.git
synced 2026-04-08 22:47:02 +02:00
* feat: add site icon and favicon - Downloaded and cropped Grimoire icon from nostr.build - Created multiple favicon sizes (16x16, 32x32, 180x180, 512x512) - Generated traditional .ico format and PNG variants - Added Apple touch icon for iOS devices - Updated index.html with proper favicon links * feat: make Grimoire installable as a PWA - Created web manifest (site.webmanifest) with app metadata - Added app name, description, theme colors - Configured standalone display mode for native-like experience - Included all required icon sizes (192x192, 512x512) - Added keyboard shortcut for command palette - Generated 192x192 icon for PWA requirements - Added manifest and theme-color meta tags to index.html - Implemented service worker (sw.js) for offline functionality - Network-first caching strategy for optimal performance - Precaches core assets on install - Provides offline fallback for navigation requests - Registered service worker in main.tsx Users can now install Grimoire as a standalone app on desktop and mobile devices. * fix: properly configure maskable PWA icons The previous configuration incorrectly marked regular icons as "maskable", which would cause them to be cropped when displayed in circular or rounded shapes on Android devices. Changes: - Created dedicated maskable icons with 10% padding (safe zone) - Maskable icons use dark background (#020817) matching app theme - Separated "any" and "maskable" purposes in manifest - Regular icons (192x192, 512x512) use full space with purpose="any" - Maskable icons (192x192-maskable, 512x512-maskable) have padding with purpose="maskable" This ensures icons display correctly in all contexts: - Regular icons for browser tabs, shortcuts, splash screens - Maskable icons for adaptive icon shapes on Android * chore: simplify PWA manifest - Simplify name to just 'Grimoire' - Add 'nostr' to categories for better discoverability - Remove shortcuts (not needed for initial launch) --------- Co-authored-by: Claude <noreply@anthropic.com>
85 lines
2.2 KiB
JavaScript
85 lines
2.2 KiB
JavaScript
// Grimoire Service Worker - v1.0.0
|
|
const CACHE_NAME = "grimoire-v1";
|
|
const RUNTIME_CACHE = "grimoire-runtime";
|
|
|
|
// Core assets to cache on install
|
|
const PRECACHE_URLS = [
|
|
"/",
|
|
"/index.html",
|
|
"/favicon.ico",
|
|
"/favicon-192x192.png",
|
|
"/favicon-512x512.png",
|
|
];
|
|
|
|
// Install event - precache core assets
|
|
self.addEventListener("install", (event) => {
|
|
event.waitUntil(
|
|
caches.open(CACHE_NAME).then((cache) => {
|
|
return cache.addAll(PRECACHE_URLS);
|
|
}),
|
|
);
|
|
// Activate immediately
|
|
self.skipWaiting();
|
|
});
|
|
|
|
// Activate event - clean up old caches
|
|
self.addEventListener("activate", (event) => {
|
|
event.waitUntil(
|
|
caches.keys().then((cacheNames) => {
|
|
return Promise.all(
|
|
cacheNames
|
|
.filter((name) => name !== CACHE_NAME && name !== RUNTIME_CACHE)
|
|
.map((name) => caches.delete(name)),
|
|
);
|
|
}),
|
|
);
|
|
// Take control immediately
|
|
self.clients.claim();
|
|
});
|
|
|
|
// Fetch event - network first, fallback to cache
|
|
self.addEventListener("fetch", (event) => {
|
|
// Skip non-GET requests
|
|
if (event.request.method !== "GET") return;
|
|
|
|
// Skip cross-origin requests
|
|
if (!event.request.url.startsWith(self.location.origin)) return;
|
|
|
|
// Network first strategy for app shell and assets
|
|
event.respondWith(
|
|
fetch(event.request)
|
|
.then((response) => {
|
|
// Clone the response before caching
|
|
const responseClone = response.clone();
|
|
|
|
// Cache successful responses
|
|
if (response.status === 200) {
|
|
caches.open(RUNTIME_CACHE).then((cache) => {
|
|
cache.put(event.request, responseClone);
|
|
});
|
|
}
|
|
|
|
return response;
|
|
})
|
|
.catch(() => {
|
|
// Fallback to cache if network fails
|
|
return caches.match(event.request).then((cachedResponse) => {
|
|
if (cachedResponse) {
|
|
return cachedResponse;
|
|
}
|
|
|
|
// If no cache, return offline page for navigation requests
|
|
if (event.request.mode === "navigate") {
|
|
return caches.match("/index.html");
|
|
}
|
|
|
|
// For other requests, just fail
|
|
return new Response("Offline", {
|
|
status: 503,
|
|
statusText: "Service Unavailable",
|
|
});
|
|
});
|
|
}),
|
|
);
|
|
});
|