/** * One-shot: render public/icons/source.svg into the four PNGs the manifest * and iOS expect. * * Run with `npx tsx scripts/build-icons.ts` from the project root. The * output is committed so the deployed bundle does not need `@resvg/resvg-js` * at runtime. * * Sizes follow the manifest spec: * - icon-192.png: 192×192, used by Android Chrome and most launchers * - icon-512.png: 512×512, used by Android splash and Play Store * - icon-maskable-512.png: 512×512 with the safe-zone padding Android * applies when the icon is shown in a non-square mask (e.g. circle, * squircle). The source SVG already has 96/512 = ~19% corner radius, * which is within Android's safe zone. * - apple-touch-icon.png: 180×180, used by iOS Safari's "Add to Home * Screen". iOS does not honour the manifest for this; the link is in * the layout's . */ import { readFileSync, writeFileSync } from "node:fs"; import { join } from "node:path"; import { Resvg } from "@resvg/resvg-js"; const root = join(process.cwd(), "public", "icons"); const svg = readFileSync(join(root, "source.svg"), "utf8"); const targets = [ { file: "icon-192.png", size: 192 }, { file: "icon-512.png", size: 512 }, { file: "icon-maskable-512.png", size: 512 }, { file: "apple-touch-icon.png", size: 180 }, ]; for (const target of targets) { const png = new Resvg(svg, { fitTo: { mode: "width", value: target.size }, }) .render() .asPng(); writeFileSync(join(root, target.file), png); console.log(` + ${target.file} (${target.size}x${target.size})`); } console.log("Done.");