74 lines
2.3 KiB
TypeScript
74 lines
2.3 KiB
TypeScript
import sharp from "sharp";
|
|
import path from "node:path";
|
|
import fs from "node:fs";
|
|
|
|
const PUBLIC_DIR = path.resolve(import.meta.dirname, "../public");
|
|
const OUTPUT_DIR = path.join(PUBLIC_DIR, "pwa-icons");
|
|
|
|
const ICON_SIZES = [
|
|
{ size: 16, name: "favicon-16x16.png" },
|
|
{ size: 32, name: "favicon-32x32.png" },
|
|
{ size: 180, name: "apple-touch-icon-180x180.png" },
|
|
{ size: 192, name: "pwa-192x192.png" },
|
|
{ size: 512, name: "pwa-512x512.png" },
|
|
{ size: 512, name: "pwa-512x512-maskable.png", maskable: true },
|
|
];
|
|
|
|
// Maskable icons need 10% safe zone padding (per spec).
|
|
// We add a colored background and shrink the source to 80% to stay within the safe zone.
|
|
const MASKABLE_PADDING_RATIO = 0.1;
|
|
const MASKABLE_BG_COLOR = "#ffffff";
|
|
|
|
async function generateIcons(sourceFile: string) {
|
|
if (!fs.existsSync(OUTPUT_DIR)) {
|
|
fs.mkdirSync(OUTPUT_DIR, { recursive: true });
|
|
}
|
|
|
|
for (const icon of ICON_SIZES) {
|
|
const outputPath = path.join(OUTPUT_DIR, icon.name);
|
|
|
|
if (icon.maskable) {
|
|
// Create maskable icon with padding and background
|
|
const innerSize = Math.round(icon.size * (1 - MASKABLE_PADDING_RATIO * 2));
|
|
const offset = Math.round(icon.size * MASKABLE_PADDING_RATIO);
|
|
|
|
const resizedIcon = await sharp(sourceFile)
|
|
.resize(innerSize, innerSize, { fit: "contain" })
|
|
.toBuffer();
|
|
|
|
await sharp({
|
|
create: {
|
|
width: icon.size,
|
|
height: icon.size,
|
|
channels: 4,
|
|
background: MASKABLE_BG_COLOR,
|
|
},
|
|
})
|
|
.composite([{ input: resizedIcon, left: offset, top: offset }])
|
|
.png()
|
|
.toFile(outputPath);
|
|
} else {
|
|
await sharp(sourceFile)
|
|
.resize(icon.size, icon.size, { fit: "contain" })
|
|
.png()
|
|
.toFile(outputPath);
|
|
}
|
|
|
|
console.log(`Generated: ${icon.name} (${icon.size}x${icon.size})`);
|
|
}
|
|
}
|
|
|
|
// Determine source: pass "staging" as first arg for staging icons
|
|
const variant = process.argv[2];
|
|
const sourceFile =
|
|
variant === "staging"
|
|
? path.join(PUBLIC_DIR, "staging_icon.jpg")
|
|
: path.join(PUBLIC_DIR, "icon.jpg");
|
|
|
|
if (!fs.existsSync(sourceFile)) {
|
|
console.error(`Source icon not found: ${sourceFile}`);
|
|
process.exit(1);
|
|
}
|
|
|
|
console.log(`Generating PWA icons from: ${sourceFile}`);
|
|
generateIcons(sourceFile).then(() => console.log("Done!"));
|