#!/usr/bin/env tsx /** * Generate placeholder PWA icons (icon-512.png, icon-192.png, * apple-touch-icon.png) into apps/web/public/. * * Run once via `pnpm --filter @cmbot/web run gen:icons`. The output is * intentionally minimal — a dark square with the "cm" wordmark in * a light bold sans-serif — until a designer hands us a real icon. * * Sharp is already in the workspace's node_modules (Baileys depends * on it), so we re-use it here rather than introducing a new image * library. Output is written as PNG with no alpha channel; the * "any maskable" purpose in the manifest covers both regular launch * icons and Android adaptive icons (which crop to their own shape). */ import { writeFile } from "node:fs/promises"; import { join } from "node:path"; import sharp from "sharp"; const OUT_DIR = join(import.meta.dirname, "..", "public"); const BG = "#0a0a0a"; // matches the manifest theme_color const FG = "#ffffff"; // wordmark const TEXT = "cm"; /** Render the icon at the given pixel size. */ async function renderIcon(size: number): Promise { // Font size is tuned to fill ~half the icon's height with comfortable // padding around the wordmark — same proportions Apple/Google use // for their own home-screen icons. const fontSize = Math.round(size * 0.46); const svg = ` ${TEXT} `.trim(); return sharp(Buffer.from(svg)).png().toBuffer(); } async function main(): Promise { const targets: Array<{ size: number; filename: string }> = [ { size: 512, filename: "icon-512.png" }, { size: 192, filename: "icon-192.png" }, { size: 180, filename: "apple-touch-icon.png" }, ]; for (const { size, filename } of targets) { const png = await renderIcon(size); const out = join(OUT_DIR, filename); await writeFile(out, png); console.log(` ${filename} (${size}×${size}, ${png.byteLength} bytes)`); } } main().catch((err) => { console.error(err); process.exit(1); });