feat: add pwa manifest and service worker

Wire up a production service worker and web app manifest with install metadata and icon assets.
Bump project version to 2.1.0 and register the worker from the main page for offline-ready behavior.

Made-with: Cursor
This commit is contained in:
Frank Schwenk
2026-04-14 15:34:10 +02:00
parent 55cba1495f
commit 5bdea62a9f
5 changed files with 118 additions and 3 deletions
+2 -2
View File
@@ -1,12 +1,12 @@
{ {
"name": "growing-galaxy", "name": "growing-galaxy",
"version": "0.0.1", "version": "2.1.0",
"lockfileVersion": 3, "lockfileVersion": 3,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "growing-galaxy", "name": "growing-galaxy",
"version": "0.0.1", "version": "2.1.0",
"dependencies": { "dependencies": {
"@astrojs/preact": "^4.1.3", "@astrojs/preact": "^4.1.3",
"astro": "^5.15.5", "astro": "^5.15.5",
+1 -1
View File
@@ -1,7 +1,7 @@
{ {
"name": "growing-galaxy", "name": "growing-galaxy",
"type": "module", "type": "module",
"version": "0.0.1", "version": "2.1.0",
"scripts": { "scripts": {
"dev": "astro dev --host", "dev": "astro dev --host",
"build": "astro build", "build": "astro build",
+35
View File
@@ -0,0 +1,35 @@
{
"name": "BSC Score",
"short_name": "BSC Score",
"description": "Professional pool and billiards scoring application",
"start_url": "/",
"scope": "/",
"display": "standalone",
"orientation": "portrait",
"background_color": "#1a1a1a",
"theme_color": "#1a1a1a",
"lang": "de",
"categories": [
"sports",
"utilities",
"productivity"
],
"icons": [
{
"src": "/icon-192.png",
"sizes": "192x192",
"type": "image/png"
},
{
"src": "/icon-512.png",
"sizes": "512x512",
"type": "image/png"
},
{
"src": "/icon-512.png",
"sizes": "512x512",
"type": "image/png",
"purpose": "maskable"
}
]
}
+68
View File
@@ -0,0 +1,68 @@
const CACHE_NAME = 'bscscore-v2.1.0';
const APP_SHELL = [
'/',
'/index.html',
'/manifest.webmanifest',
'/favicon.ico',
'/icon-192.png',
'/icon-512.png',
];
self.addEventListener('install', (event) => {
event.waitUntil(
caches.open(CACHE_NAME).then((cache) => cache.addAll(APP_SHELL)),
);
self.skipWaiting();
});
self.addEventListener('activate', (event) => {
event.waitUntil(
caches.keys().then((keys) =>
Promise.all(
keys.map((key) => {
if (key !== CACHE_NAME) {
return caches.delete(key);
}
return Promise.resolve();
}),
),
),
);
self.clients.claim();
});
self.addEventListener('fetch', (event) => {
const { request } = event;
const requestUrl = new URL(request.url);
if (request.method !== 'GET' || requestUrl.origin !== self.location.origin) {
return;
}
if (request.mode === 'navigate') {
event.respondWith(
fetch(request)
.then((response) => {
const responseClone = response.clone();
caches.open(CACHE_NAME).then((cache) => cache.put(request, responseClone));
return response;
})
.catch(() => caches.match(request).then((cached) => cached || caches.match('/'))),
);
return;
}
event.respondWith(
caches.match(request).then((cachedResponse) => {
if (cachedResponse) {
return cachedResponse;
}
return fetch(request).then((networkResponse) => {
const networkClone = networkResponse.clone();
caches.open(CACHE_NAME).then((cache) => cache.put(request, networkClone));
return networkResponse;
});
}),
);
});
+12
View File
@@ -19,11 +19,14 @@ import App from "../components/App";
<meta name="theme-color" content="#1a1a1a"> <meta name="theme-color" content="#1a1a1a">
<meta name="apple-mobile-web-app-capable" content="yes"> <meta name="apple-mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent"> <meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
<meta name="mobile-web-app-capable" content="yes">
<link rel="manifest" href="/manifest.webmanifest">
<!-- Favicon --> <!-- Favicon -->
<link rel="icon" type="image/x-icon" href="/favicon.ico"> <link rel="icon" type="image/x-icon" href="/favicon.ico">
<link rel="icon" type="image/png" sizes="192x192" href="/icon-192.png"> <link rel="icon" type="image/png" sizes="192x192" href="/icon-192.png">
<link rel="icon" type="image/png" sizes="512x512" href="/icon-512.png"> <link rel="icon" type="image/png" sizes="512x512" href="/icon-512.png">
<link rel="apple-touch-icon" href="/icon-192.png">
</head> </head>
<body> <body>
@@ -34,5 +37,14 @@ import App from "../components/App";
--> -->
<App client:only="preact" slot="app-content" /> <App client:only="preact" slot="app-content" />
</BscScoreApp> </BscScoreApp>
<script>
if (typeof window !== 'undefined' && 'serviceWorker' in navigator) {
window.addEventListener('load', () => {
navigator.serviceWorker.register('/sw.js').catch((error) => {
console.error('Service worker registration failed:', error);
});
});
}
</script>
</body> </body>
</html> </html>