feat: Add PWA support
- PWA_MOBILE_SPEC.md documentation - manifest.json with app metadata - Service Worker with caching strategies - Offline page - PWA registration in layout - App icons (placeholder)
This commit is contained in:
BIN
apps/web/public/icons/apple-touch-icon.png
Normal file
BIN
apps/web/public/icons/apple-touch-icon.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.4 KiB |
61
apps/web/public/icons/generate-icons.js
Normal file
61
apps/web/public/icons/generate-icons.js
Normal file
@@ -0,0 +1,61 @@
|
||||
const fs = require('fs');
|
||||
const { createCanvas } = require('canvas');
|
||||
|
||||
function generateIcon(size, filename) {
|
||||
const canvas = createCanvas(size, size);
|
||||
const ctx = canvas.getContext('2d');
|
||||
|
||||
// Background
|
||||
ctx.fillStyle = '#0f172a';
|
||||
ctx.beginPath();
|
||||
ctx.roundRect(0, 0, size, size, size * 0.18);
|
||||
ctx.fill();
|
||||
|
||||
// Gradient for sparkle
|
||||
const gradient = ctx.createLinearGradient(0, 0, size, size);
|
||||
gradient.addColorStop(0, '#22d3ee');
|
||||
gradient.addColorStop(1, '#3b82f6');
|
||||
|
||||
// Draw sparkle
|
||||
const cx = size / 2;
|
||||
const cy = size / 2;
|
||||
const s = size * 0.23; // sparkle size
|
||||
|
||||
ctx.fillStyle = gradient;
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(cx, cy - s);
|
||||
ctx.lineTo(cx + s * 0.12, cy - s * 0.12);
|
||||
ctx.lineTo(cx + s, cy);
|
||||
ctx.lineTo(cx + s * 0.12, cy + s * 0.12);
|
||||
ctx.lineTo(cx, cy + s);
|
||||
ctx.lineTo(cx - s * 0.12, cy + s * 0.12);
|
||||
ctx.lineTo(cx - s, cy);
|
||||
ctx.lineTo(cx - s * 0.12, cy - s * 0.12);
|
||||
ctx.closePath();
|
||||
ctx.fill();
|
||||
|
||||
// Small sparkles
|
||||
ctx.fillStyle = '#22d3ee';
|
||||
ctx.globalAlpha = 0.7;
|
||||
ctx.beginPath();
|
||||
ctx.arc(cx + size * 0.15, cy - size * 0.15, size * 0.023, 0, Math.PI * 2);
|
||||
ctx.fill();
|
||||
|
||||
ctx.fillStyle = '#3b82f6';
|
||||
ctx.globalAlpha = 0.6;
|
||||
ctx.beginPath();
|
||||
ctx.arc(cx - size * 0.14, cy + size * 0.14, size * 0.015, 0, Math.PI * 2);
|
||||
ctx.fill();
|
||||
|
||||
const buffer = canvas.toBuffer('image/png');
|
||||
fs.writeFileSync(filename, buffer);
|
||||
console.log(`Generated ${filename}`);
|
||||
}
|
||||
|
||||
try {
|
||||
generateIcon(192, 'icon-192x192.png');
|
||||
generateIcon(512, 'icon-512x512.png');
|
||||
generateIcon(180, 'apple-touch-icon.png');
|
||||
} catch (e) {
|
||||
console.error('Error:', e.message);
|
||||
}
|
||||
BIN
apps/web/public/icons/icon-192x192.png
Normal file
BIN
apps/web/public/icons/icon-192x192.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.4 KiB |
BIN
apps/web/public/icons/icon-512x512.png
Normal file
BIN
apps/web/public/icons/icon-512x512.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.4 KiB |
16
apps/web/public/icons/icon.svg
Normal file
16
apps/web/public/icons/icon.svg
Normal file
@@ -0,0 +1,16 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="none">
|
||||
<rect width="512" height="512" rx="96" fill="#0f172a"/>
|
||||
<defs>
|
||||
<linearGradient id="sparkle" x1="0%" y1="0%" x2="100%" y2="100%">
|
||||
<stop offset="0%" style="stop-color:#22d3ee"/>
|
||||
<stop offset="100%" style="stop-color:#3b82f6"/>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
<g transform="translate(256, 256)">
|
||||
<path d="M0 -120 L15 -15 L120 0 L15 15 L0 120 L-15 15 L-120 0 L-15 -15 Z" fill="url(#sparkle)" opacity="0.9"/>
|
||||
<circle cx="80" cy="-80" r="12" fill="#22d3ee" opacity="0.7"/>
|
||||
<circle cx="-70" cy="70" r="8" fill="#3b82f6" opacity="0.6"/>
|
||||
<circle cx="60" cy="90" r="6" fill="#22d3ee" opacity="0.5"/>
|
||||
<circle cx="-90" cy="-50" r="10" fill="#3b82f6" opacity="0.6"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 785 B |
Reference in New Issue
Block a user