1
0

feat: pwa manifest, icon and minimal service worker

This commit is contained in:
2026-09-03 18:57:31 +02:00
parent 3efde1a828
commit c1852435c8
6 changed files with 64 additions and 0 deletions

View File

@@ -4,6 +4,9 @@
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover" />
<meta name="theme-color" content="#0a0a0a" />
<link rel="manifest" href="/manifest.webmanifest" />
<link rel="icon" href="/icon.svg" type="image/svg+xml" />
<link rel="apple-touch-icon" href="/icon.svg" />
<title>record-shop</title>
</head>
<body>

7
web/public/icon.svg Normal file
View File

@@ -0,0 +1,7 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512">
<rect width="512" height="512" rx="96" fill="#0a0a0a"/>
<circle cx="256" cy="256" r="160" fill="#171717" stroke="#34d399" stroke-width="16"/>
<circle cx="256" cy="256" r="96" fill="none" stroke="#262626" stroke-width="8"/>
<circle cx="256" cy="256" r="40" fill="#34d399"/>
<circle cx="256" cy="256" r="12" fill="#0a0a0a"/>
</svg>

After

Width:  |  Height:  |  Size: 403 B

View File

@@ -0,0 +1,13 @@
{
"name": "record-shop",
"short_name": "record-shop",
"description": "Track your physical music collection",
"start_url": "/",
"display": "standalone",
"background_color": "#0a0a0a",
"theme_color": "#0a0a0a",
"icons": [
{ "src": "/icon.svg", "sizes": "any", "type": "image/svg+xml", "purpose": "any" },
{ "src": "/icon.svg", "sizes": "any", "type": "image/svg+xml", "purpose": "maskable" }
]
}

11
web/public/sw.js Normal file
View File

@@ -0,0 +1,11 @@
self.addEventListener('install', () => {
self.skipWaiting()
})
self.addEventListener('activate', (event) => {
event.waitUntil(self.clients.claim())
})
self.addEventListener('fetch', () => {
// no-op: presence of a fetch handler enables PWA install
})

View File

@@ -8,3 +8,7 @@ ReactDOM.createRoot(document.getElementById('root')!).render(
<App />
</React.StrictMode>
)
if ('serviceWorker' in navigator && import.meta.env.PROD) {
void navigator.serviceWorker.register('/sw.js')
}

26
web/test/pwa.test.ts Normal file
View File

@@ -0,0 +1,26 @@
import { describe, it, expect } from 'vitest'
import { readFileSync } from 'node:fs'
import path from 'node:path'
const pub = path.resolve(__dirname, '../public')
describe('PWA assets', () => {
it('manifest has required fields and the icon exists', () => {
const manifest = JSON.parse(readFileSync(path.join(pub, 'manifest.webmanifest'), 'utf8')) as {
name: string
display: string
start_url: string
icons: { src: string; sizes: string; type: string }[]
}
expect(manifest.name).toContain('record-shop')
expect(manifest.display).toBe('standalone')
expect(manifest.start_url).toBe('/')
expect(manifest.icons.some((i) => i.src === '/icon.svg')).toBe(true)
expect(() => readFileSync(path.join(pub, 'icon.svg'))).not.toThrow()
})
it('service worker registers a fetch handler', () => {
const sw = readFileSync(path.join(pub, 'sw.js'), 'utf8')
expect(sw).toContain("addEventListener('fetch'")
})
})