47 lines
1.6 KiB
TypeScript
47 lines
1.6 KiB
TypeScript
|
|
/**
|
|||
|
|
* 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 <head>.
|
|||
|
|
*/
|
|||
|
|
|
|||
|
|
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.");
|