/** * Generate PWA Icons from SVG using Sharp * Run: npm install sharp && node generate-pwa-icons-sharp.js */ import sharp from 'sharp'; import { readFileSync, mkdirSync } from 'fs'; import { join, dirname } from 'path'; import { fileURLToPath } from 'url'; const __filename = fileURLToPath(import.meta.url); const __dirname = dirname(__filename); const svgPath = join(__dirname, 'assets', 'images', 'app-icon.svg'); const iconsDir = join(__dirname, 'public', 'icons'); // Create icons directory mkdirSync(iconsDir, { recursive: true }); const svgBuffer = readFileSync(svgPath); const sizes = [ { size: 192, name: 'icon-192.png', maskable: false }, { size: 512, name: 'icon-512.png', maskable: false }, { size: 192, name: 'icon-192-maskable.png', maskable: true }, { size: 512, name: 'icon-512-maskable.png', maskable: true }, { size: 180, name: 'apple-touch-icon.png', maskable: false } ]; async function generateIcons() { console.log('Generating PWA icons...'); for (const { size, name, maskable } of sizes) { try { let pipeline = sharp(svgBuffer).resize(size, size); // For maskable icons, add padding (safe zone) if (maskable) { const padding = Math.floor(size * 0.1); // 10% padding const innerSize = size - (padding * 2); pipeline = sharp(svgBuffer) .resize(innerSize, innerSize) .extend({ top: padding, bottom: padding, left: padding, right: padding, background: { r: 10, g: 10, b: 10, alpha: 1 } // #0a0a0a }); } await pipeline.png().toFile(join(iconsDir, name)); console.log(`āœ“ Generated ${name}`); } catch (error) { console.error(`āœ— Failed to generate ${name}:`, error.message); } } console.log('\nāœ“ All icons generated successfully!'); console.log('Icons saved to: public/icons/'); } generateIcons().catch(console.error);