/** * Renders the site icon (docs/07 §11) with headless Chrome. * bun scripts/pwa-icons.ts → apps/web/public/favicon.svg + apps/web/public/icons/*.png * One design (아케이드 게임기 + 천사 고리 + 반짝이) is the source of every icon: * "any" icons keep rounded corners; maskable/apple icons are full-bleed * (the OS applies its own mask) with the art shrunk into the safe circle. */ import { chromium } from 'playwright-core'; import { mkdirSync, writeFileSync } from 'node:fs'; import { join } from 'node:path'; const PUBLIC = join(import.meta.dir, '../apps/web/public'); const OUT = join(PUBLIC, 'icons'); mkdirSync(OUT, { recursive: true }); const INK = '#1e1b4b'; const DEFS = ``; const star = (x: number, y: number, r: number, fill: string) => ``; const ART = [ // 천사 고리: "게임천국" ``, // 게임 패드 ``, ``, ``, ``, ``, ``, // 반짝이 star(53, 11, 4.6, '#fde047'), star(10.5, 14, 3, '#fff'), star(54, 53, 3, '#fff'), star(11, 54, 2.2, '#fde047'), ].join(''); const rounded = `${DEFS}${ART}\n`; const fullBleed = `${DEFS}${ART}`; writeFileSync(join(PUBLIC, 'favicon.svg'), rounded); console.log('✓ favicon.svg'); const ICONS: [file: string, size: number, svg: string][] = [ ['icon-192.png', 192, rounded], ['icon-512.png', 512, rounded], ['icon-maskable-512.png', 512, fullBleed], ['apple-touch-icon.png', 180, fullBleed], ]; const browser = await chromium.launch({ executablePath: process.env.CHROME ?? '/usr/bin/google-chrome', headless: true }); const page = await browser.newPage(); for (const [file, size, svg] of ICONS) { await page.setViewportSize({ width: size, height: size }); await page.setContent(`${svg}`); await page.screenshot({ path: join(OUT, file), omitBackground: true }); console.log(`✓ ${file}`); } await browser.close();