From 7c80f5ce7e44b95d16e19de40de0f8079377c6b9 Mon Sep 17 00:00:00 2001 From: yyamashita Date: Sat, 22 Aug 2026 19:21:12 +0900 Subject: Initial commit: todo app with passkey auth and PWA support Co-Authored-By: Claude Sonnet 4.6 --- scripts/gen-icons.mjs | 98 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 98 insertions(+) create mode 100644 scripts/gen-icons.mjs (limited to 'scripts') diff --git a/scripts/gen-icons.mjs b/scripts/gen-icons.mjs new file mode 100644 index 0000000..ea23326 --- /dev/null +++ b/scripts/gen-icons.mjs @@ -0,0 +1,98 @@ +import { deflateSync } from "zlib"; +import { writeFileSync, mkdirSync } from "fs"; + +const CRC_TABLE = (() => { + const t = new Uint32Array(256); + for (let n = 0; n < 256; n++) { + let c = n; + for (let k = 0; k < 8; k++) c = c & 1 ? 0xedb88320 ^ (c >>> 1) : c >>> 1; + t[n] = c; + } + return t; +})(); + +function crc32(buf) { + let crc = 0xffffffff; + for (let i = 0; i < buf.length; i++) crc = CRC_TABLE[(crc ^ buf[i]) & 0xff] ^ (crc >>> 8); + return (crc ^ 0xffffffff) >>> 0; +} + +function chunk(type, data) { + const typeBytes = Buffer.from(type, "ascii"); + const crcVal = crc32(Buffer.concat([typeBytes, data])); + const out = Buffer.allocUnsafe(4 + 4 + data.length + 4); + out.writeUInt32BE(data.length, 0); + typeBytes.copy(out, 4); + data.copy(out, 8); + out.writeUInt32BE(crcVal, 8 + data.length); + return out; +} + +// Purple #6c63ff = rgb(108, 99, 255) +// With a white checkmark drawn on it +function createIcon(size) { + const sig = Buffer.from([137, 80, 78, 71, 13, 10, 26, 10]); + + const ihdr = Buffer.allocUnsafe(13); + ihdr.writeUInt32BE(size, 0); + ihdr.writeUInt32BE(size, 4); + ihdr[8] = 8; // bit depth + ihdr[9] = 2; // color type RGB + ihdr[10] = ihdr[11] = ihdr[12] = 0; + + // Draw pixels: purple bg with white checkmark + const pixels = Buffer.alloc(size * size * 3); + const scale = size / 64; + + // Fill purple + for (let i = 0; i < size * size; i++) { + pixels[i * 3] = 108; + pixels[i * 3 + 1] = 99; + pixels[i * 3 + 2] = 255; + } + + // Draw white checkmark: ✓ + // Checkmark path: (14,34)→(26,46)→(50,18) scaled to icon size + function setPixel(x, y) { + const xi = Math.round(x * scale); + const yi = Math.round(y * scale); + const thick = Math.max(2, Math.round(4 * scale)); + for (let dy = -thick; dy <= thick; dy++) { + for (let dx = -thick; dx <= thick; dx++) { + const px = xi + dx; + const py = yi + dy; + if (px >= 0 && px < size && py >= 0 && py < size) { + pixels[(py * size + px) * 3] = 255; + pixels[(py * size + px) * 3 + 1] = 255; + pixels[(py * size + px) * 3 + 2] = 255; + } + } + } + } + + // Left leg: (14,34) → (26,46) + const steps = 40; + for (let i = 0; i <= steps; i++) { + setPixel(14 + (26 - 14) * (i / steps), 34 + (46 - 34) * (i / steps)); + } + // Right leg: (26,46) → (50,18) + for (let i = 0; i <= steps; i++) { + setPixel(26 + (50 - 26) * (i / steps), 46 + (18 - 46) * (i / steps)); + } + + // Build scanlines with filter=0 prefix + const raw = Buffer.allocUnsafe(size * (1 + size * 3)); + for (let y = 0; y < size; y++) { + raw[y * (1 + size * 3)] = 0; + pixels.copy(raw, y * (1 + size * 3) + 1, y * size * 3, (y + 1) * size * 3); + } + + const compressed = deflateSync(raw); + + return Buffer.concat([sig, chunk("IHDR", ihdr), chunk("IDAT", compressed), chunk("IEND", Buffer.alloc(0))]); +} + +mkdirSync("public/icons", { recursive: true }); +writeFileSync("public/icons/icon-192.png", createIcon(192)); +writeFileSync("public/icons/icon-512.png", createIcon(512)); +console.log("Icons generated: public/icons/icon-{192,512}.png"); -- cgit v1.2.3