init
This commit is contained in:
36
public/icon.svg
Normal file
36
public/icon.svg
Normal file
@@ -0,0 +1,36 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" width="512" height="512">
|
||||
<defs>
|
||||
<linearGradient id="bgGrad" x1="0%" y1="0%" x2="100%" y2="100%">
|
||||
<stop offset="0%" stop-color="#f59e0b" />
|
||||
<stop offset="100%" stop-color="#b45309" />
|
||||
</linearGradient>
|
||||
<linearGradient id="tileGrad" x1="0%" y1="0%" x2="100%" y2="100%">
|
||||
<stop offset="0%" stop-color="#ffffff" />
|
||||
<stop offset="100%" stop-color="#f3f4f6" />
|
||||
</linearGradient>
|
||||
<filter id="shadow" x="-10%" y="-10%" width="120%" height="120%">
|
||||
<feDropShadow dx="0" dy="8" stdDeviation="10" flood-opacity="0.25" />
|
||||
</filter>
|
||||
</defs>
|
||||
<!-- Background iOS squircle -->
|
||||
<rect width="512" height="512" rx="112" fill="url(#bgGrad)" />
|
||||
|
||||
<!-- 4 Quartile Tiles forming a grid/Q -->
|
||||
<g filter="url(#shadow)">
|
||||
<!-- Top Left -->
|
||||
<rect x="116" y="116" width="124" height="124" rx="28" fill="url(#tileGrad)" />
|
||||
<text x="178" y="200" font-family="-apple-system, BlinkMacSystemFont, 'Inter', sans-serif" font-weight="800" font-size="68" fill="#1f2937" text-anchor="middle">Q</text>
|
||||
|
||||
<!-- Top Right -->
|
||||
<rect x="272" y="116" width="124" height="124" rx="28" fill="url(#tileGrad)" />
|
||||
<text x="334" y="200" font-family="-apple-system, BlinkMacSystemFont, 'Inter', sans-serif" font-weight="800" font-size="68" fill="#1f2937" text-anchor="middle">U</text>
|
||||
|
||||
<!-- Bottom Left -->
|
||||
<rect x="116" y="272" width="124" height="124" rx="28" fill="url(#tileGrad)" />
|
||||
<text x="178" y="356" font-family="-apple-system, BlinkMacSystemFont, 'Inter', sans-serif" font-weight="800" font-size="68" fill="#1f2937" text-anchor="middle">A</text>
|
||||
|
||||
<!-- Bottom Right with emerald accent -->
|
||||
<rect x="272" y="272" width="124" height="124" rx="28" fill="#10b981" />
|
||||
<text x="334" y="356" font-family="-apple-system, BlinkMacSystemFont, 'Inter', sans-serif" font-weight="800" font-size="68" fill="#ffffff" text-anchor="middle">R</text>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.9 KiB |
26
public/manifest.webmanifest
Normal file
26
public/manifest.webmanifest
Normal file
@@ -0,0 +1,26 @@
|
||||
{
|
||||
"name": "Quartiles — Daily Word Puzzle",
|
||||
"short_name": "Quartiles",
|
||||
"description": "Combine letter tiles to form words, find the 5 hidden Quartiles, and score 100 points for Expert rank!",
|
||||
"start_url": "/",
|
||||
"scope": "/",
|
||||
"display": "standalone",
|
||||
"orientation": "portrait-primary",
|
||||
"background_color": "#000000",
|
||||
"theme_color": "#1c1c1e",
|
||||
"categories": ["games", "puzzle"],
|
||||
"icons": [
|
||||
{
|
||||
"src": "/icon.svg",
|
||||
"sizes": "512x512",
|
||||
"type": "image/svg+xml",
|
||||
"purpose": "any"
|
||||
},
|
||||
{
|
||||
"src": "/icon.svg",
|
||||
"sizes": "512x512",
|
||||
"type": "image/svg+xml",
|
||||
"purpose": "maskable"
|
||||
}
|
||||
]
|
||||
}
|
||||
76
public/sw.js
Normal file
76
public/sw.js
Normal file
@@ -0,0 +1,76 @@
|
||||
const CACHE_NAME = 'quartiles-pwa-v1';
|
||||
|
||||
const STATIC_ASSETS = [
|
||||
'/',
|
||||
'/index.html',
|
||||
'/manifest.webmanifest',
|
||||
'/icon.svg'
|
||||
];
|
||||
|
||||
// Install: Cache initial shell assets
|
||||
self.addEventListener('install', event => {
|
||||
event.waitUntil(
|
||||
caches.open(CACHE_NAME).then(cache => {
|
||||
return cache.addAll(STATIC_ASSETS);
|
||||
}).then(() => self.skipWaiting())
|
||||
);
|
||||
});
|
||||
|
||||
// Activate: Clean up old cache versions
|
||||
self.addEventListener('activate', event => {
|
||||
event.waitUntil(
|
||||
caches.keys().then(keys => {
|
||||
return Promise.all(
|
||||
keys.map(key => {
|
||||
if (key !== CACHE_NAME) {
|
||||
return caches.delete(key);
|
||||
}
|
||||
})
|
||||
);
|
||||
}).then(() => self.clients.claim())
|
||||
);
|
||||
});
|
||||
|
||||
// Fetch: Stale-While-Revalidate for local assets, Network-First for HTML navigation
|
||||
self.addEventListener('fetch', event => {
|
||||
const request = event.request;
|
||||
const url = new URL(request.url);
|
||||
|
||||
// Only handle GET requests
|
||||
if (request.method !== 'GET') return;
|
||||
|
||||
// Handle HTML navigation requests (Offline support)
|
||||
if (request.mode === 'navigate') {
|
||||
event.respondWith(
|
||||
fetch(request)
|
||||
.then(response => {
|
||||
if (response && response.status === 200) {
|
||||
const responseClone = response.clone();
|
||||
caches.open(CACHE_NAME).then(cache => cache.put(request, responseClone));
|
||||
}
|
||||
return response;
|
||||
})
|
||||
.catch(() => caches.match('/index.html') || caches.match('/'))
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
// Same-origin static assets or Google Fonts: Stale-While-Revalidate
|
||||
if (url.origin === location.origin || url.hostname.includes('googleapis.com') || url.hostname.includes('gstatic.com')) {
|
||||
event.respondWith(
|
||||
caches.match(request).then(cachedResponse => {
|
||||
const fetchPromise = fetch(request)
|
||||
.then(networkResponse => {
|
||||
if (networkResponse && networkResponse.status === 200) {
|
||||
const responseClone = networkResponse.clone();
|
||||
caches.open(CACHE_NAME).then(cache => cache.put(request, responseClone));
|
||||
}
|
||||
return networkResponse;
|
||||
})
|
||||
.catch(() => cachedResponse);
|
||||
|
||||
return cachedResponse || fetchPromise;
|
||||
})
|
||||
);
|
||||
}
|
||||
});
|
||||
Reference in New Issue
Block a user