// Generates favicons / PWA icons / Apple touch icon / favicon.ico from public/brand/rareindex-mark.svg. // Run from apps/web: node scripts/brand-assets.mjs import { readFileSync, writeFileSync, copyFileSync } from 'node:fs'; import sharp from 'sharp'; const svg = readFileSync('public/brand/rareindex-mark.svg'); const png = (size) => sharp(svg, { density: Math.ceil((size / 64) * 72) }).resize(size, size).png().toBuffer(); const out = { 'src/app/icon.png': 32, 'src/app/apple-icon.png': 180, 'public/apple-touch-icon.png': 180, 'public/icon-192.png': 192, 'public/icon-512.png': 512, }; for (const [path, size] of Object.entries(out)) writeFileSync(path, await png(size)); // maskable: the mark on a full-bleed ink square with safe-zone padding (icon at 80 %) const inner = await png(410); const maskable = await sharp({ create: { width: 512, height: 512, channels: 4, background: '#0B0F19' } }) .composite([{ input: inner, gravity: 'centre' }]) .png() .toBuffer(); writeFileSync('public/icon-maskable-512.png', maskable); // favicon.ico = PNG-in-ICO container with 16, 32 and 48 px images const sizes = [16, 32, 48]; const images = await Promise.all(sizes.map((s) => png(s))); const header = Buffer.alloc(6); header.writeUInt16LE(0, 0); // reserved header.writeUInt16LE(1, 2); // type: icon header.writeUInt16LE(images.length, 4); const dir = Buffer.alloc(16 * images.length); let offset = 6 + dir.length; images.forEach((img, i) => { const s = sizes[i]; dir.writeUInt8(s === 256 ? 0 : s, i * 16 + 0); dir.writeUInt8(s === 256 ? 0 : s, i * 16 + 1); dir.writeUInt8(0, i * 16 + 2); // palette dir.writeUInt8(0, i * 16 + 3); // reserved dir.writeUInt16LE(1, i * 16 + 4); // planes dir.writeUInt16LE(32, i * 16 + 6); // bpp dir.writeUInt32LE(img.length, i * 16 + 8); dir.writeUInt32LE(offset, i * 16 + 12); offset += img.length; }); writeFileSync('src/app/favicon.ico', Buffer.concat([header, dir, ...images])); copyFileSync('public/brand/rareindex-mark.svg', 'src/app/icon.svg'); copyFileSync('public/brand/rareindex-mark.svg', 'public/icon.svg'); console.log('brand assets written');