tia/scripts/generate-icons.mjs
Mannu b6814579c6 feat(pwa): add Serwist service worker, manifest, icons, install prompt
- Wrap next.config.ts with @serwist/next (webpack mode, disabled in dev)
- Service worker: NetworkOnly for /api/*, offline fallback → /~offline
- Web app manifest via Next.js metadata API (app/manifest.ts)
- PNG icon set generated with sharp (192, 512, maskable-512, apple-180)
- iOS meta tags: appleWebApp, themeColor viewport export
- Middleware: pwaAssets early-return so /sw.js never gets a 302→login
- Offline fallback page at /~offline (static, no auth dependency)
- InstallPrompt component: beforeinstallprompt (Android) + iOS Share sheet instructions
- Logout (menu/page.tsx): purge all SW caches on signout (shared-device safety)
- Fix invite/[token]/page.tsx params type for Next.js 16 (use(params))
- Build script: next build --webpack (Serwist requires webpack, not Turbopack)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-27 23:20:48 +05:30

47 lines
1.7 KiB
JavaScript

// Generates PWA icon PNGs from an SVG template using sharp.
// Run: node scripts/generate-icons.mjs
import sharp from "sharp";
import { writeFileSync } from "fs";
import path from "path";
import { fileURLToPath } from "url";
const __dirname = path.dirname(fileURLToPath(import.meta.url));
const outDir = path.join(__dirname, "../public/icons");
// Brand colors from the app
const BG = "#fb7185"; // rose-400
// SVG template — rounded square with cherry blossom character.
// For maskable: full bleed background, content within safe zone (inner 80%).
function makeSvg(size, maskable = false) {
const radius = maskable ? 0 : Math.round(size * 0.18);
const fontSize = maskable ? Math.round(size * 0.48) : Math.round(size * 0.56);
const y = maskable ? Math.round(size * 0.68) : Math.round(size * 0.72);
return `<svg xmlns="http://www.w3.org/2000/svg" width="${size}" height="${size}" viewBox="0 0 ${size} ${size}">
<rect width="${size}" height="${size}" rx="${radius}" fill="${BG}"/>
<text
x="${size / 2}"
y="${y}"
font-family="-apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif"
font-weight="700"
font-size="${fontSize}"
text-anchor="middle"
fill="white"
dominant-baseline="auto"
>T</text>
</svg>`;
}
const icons = [
{ name: "192.png", size: 192, maskable: false },
{ name: "512.png", size: 512, maskable: false },
{ name: "maskable-512.png", size: 512, maskable: true },
{ name: "apple-180.png", size: 180, maskable: false },
];
for (const icon of icons) {
const svg = Buffer.from(makeSvg(icon.size, icon.maskable));
const outPath = path.join(outDir, icon.name);
await sharp(svg).png().toFile(outPath);
console.log(`${icon.name}`);
}