/** * Génère le favicon et les icônes à partir du logo (spirale dorée), jamais * d'icône générique. Usage : pnpm icons */ import { writeFileSync, mkdirSync } from "node:fs"; import sharp from "sharp"; import { buildSpiral } from "../src/lib/spiral.ts"; const { squares, path, bbox } = buildSpiral(8); const pad = 1.2; const vb = `${bbox.x - pad} ${bbox.y - pad} ${bbox.w + pad * 2} ${bbox.h + pad * 2}`; const gold = "#c7a075"; const ivory = "#f5eee4"; function svg({ background, size }: { background: boolean; size?: number }) { const rects = squares .map( (s) => ``, ) .join(""); const dim = size ? ` width="${size}" height="${size}"` : ""; // Le rectangle d'or est 34×21 : on le centre dans un carré pour l'icône. const side = Math.max(bbox.w, bbox.h) + pad * 2; const sq = `${bbox.x + bbox.w / 2 - side / 2} ${bbox.y + bbox.h / 2 - side / 2} ${side} ${side}`; return ` ${background ? `` : ""} ${rects} `; } mkdirSync("src/app", { recursive: true }); mkdirSync("public", { recursive: true }); writeFileSync("src/app/icon.svg", svg({ background: true })); writeFileSync("public/logo-mark.svg", svg({ background: false })); const png = (s: number) => sharp(Buffer.from(svg({ background: true, size: s }))).png(); await png(180).toFile("src/app/apple-icon.png"); await png(512).toFile("public/icon-512.png"); await png(192).toFile("public/icon-192.png"); // favicon.ico : PNG 48 px encapsulé (format accepté par les navigateurs modernes) const p48 = await png(48).toBuffer(); const ico = Buffer.alloc(6 + 16); ico.writeUInt16LE(0, 0); ico.writeUInt16LE(1, 2); ico.writeUInt16LE(1, 4); ico.writeUInt8(48, 6); ico.writeUInt8(48, 7); ico.writeUInt8(0, 8); ico.writeUInt8(0, 9); ico.writeUInt16LE(1, 10); ico.writeUInt16LE(32, 12); ico.writeUInt32LE(p48.length, 14); ico.writeUInt32LE(22, 18); writeFileSync("src/app/favicon.ico", Buffer.concat([ico, p48])); console.log( "Icônes générées : src/app/icon.svg, favicon.ico, apple-icon.png, public/icon-*.png, public/logo-mark.svg", );