import { deflateSync } from "node:zlib"; import { writeFileSync, mkdirSync } from "node:fs"; // Civetest: minimal solid-color PNG encoder (non-interlaced, 8-bit RGB) function crc32(buf) { let c = ~0; for (let n = 0; n < buf.length; n++) { c ^= buf[n]; for (let k = 0; k < 8; k++) c = (c >>> 1) ^ (0xedb88320 & -(c & 1)); } return ~c >>> 0; } function chunk(type, data) { const len = Buffer.alloc(4); len.writeUInt32BE(data.length, 0); const typeBuf = Buffer.from(type, "ascii"); const crc = Buffer.alloc(4); crc.writeUInt32BE(crc32(Buffer.concat([typeBuf, data])), 0); return Buffer.concat([len, typeBuf, data, crc]); } // Simple canvas: draw background + centered white square "page" motif function draw(size) { const bg = [37, 99, 235]; // accent #2563eb const white = [255, 255, 255]; const rows = []; for (let y = 0; y < size; y++) { const row = Buffer.alloc(1 + size * 3); let p = 1; for (let x = 0; x < size; x++) { let color = bg; // white rounded "page / document" block centered const margin = Math.round(size * 0.22); const inner = size - margin * 2; const xm = x - margin; const ym = y - margin; const radius = Math.round(size * 0.08); if ( xm >= 0 && ym >= 0 && xm < inner && ym < inner && (xm <= radius || xm >= inner - radius || ym <= radius || ym >= inner - radius || Math.hypot(xm - radius, ym - radius) <= radius || Math.hypot(xm - (inner - radius), ym - radius) <= radius || Math.hypot(xm - radius, ym - (inner - radius)) <= radius || Math.hypot(xm - (inner - radius), ym - (inner - radius)) <= radius) ) { color = white; } // blue horizontal bars to suggest "CV" lines const barTop = Math.round(size * 0.45); const barHeight = Math.round(size * 0.06); if (ym >= barTop && ym < barTop + barHeight && xm > Math.round(size * 0.32) && xm < inner - Math.round(size * 0.25)) { color = bg; } if (ym >= barTop + barHeight * 2 && ym < barTop + barHeight * 3 && xm > Math.round(size * 0.38) && xm < inner - Math.round(size * 0.1)) { color = bg; } row[p++] = color[0]; row[p++] = color[1]; row[p++] = color[2]; } rows.push(row); } return Buffer.concat(rows); } function png(size, file) { const raw = draw(size); const ihdr = Buffer.alloc(13); ihdr.writeUInt32BE(size, 0); ihdr.writeUInt32BE(size, 4); ihdr[8] = 8; // bit depth ihdr[9] = 2; // color type RGB ihdr[10] = 0; ihdr[11] = 0; ihdr[12] = 0; const png = Buffer.concat([ Buffer.from([137, 80, 78, 71, 13, 10, 26, 10]), chunk("IHDR", ihdr), chunk("IDAT", deflateSync(raw, { level: 9 })), chunk("IEND", Buffer.alloc(0)), ]); writeFileSync(file, png); console.log(`generated ${file} (${size}x${size})`); } mkdirSync("./public/icons", { recursive: true }); png(192, "./public/icons/icon-192.png"); png(512, "./public/icons/icon-512.png");