const fs = require('fs'); const path = require('path'); const zlib = require('zlib'); function crc32(buf) { let c = ~0; const table = Array.from({ length: 256 }, (_, n) => { let c = n; for (let k = 0; k < 8; k++) { c = c & 1 ? 0xEDB88320 ^ (c >>> 1) : c >>> 1; } return c >>> 0; }); for (let i = 0; i < buf.length; i++) { c = table[(c ^ buf[i]) & 0xff] ^ (c >>> 1); } return ~(c >>> 0) >>> 0; } function pngChunk(type, data) { const typeBuf = Buffer.from(type, 'ascii'); const buf = Buffer.concat([typeBuf, data]); const crc = crc32(buf); const len = Buffer.allocUnsafe(4); len.writeUInt32BE(data.length, 0); const crcBuf = Buffer.allocUnsafe(4); crcBuf.writeUInt32BE(crc, 0); return Buffer.concat([len, buf, crcBuf]); } function createSolidPng(size, r, g, b) { const header = Buffer.from([0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A]); const ihdr = Buffer.allocUnsafe(13); ihdr.writeUInt32BE(size, 0); // width ihdr.writeUInt32BE(size, 4); // height ihdr[8] = 8; // bit depth ihdr[9] = 2; // color type (RGB) ihdr[10] = 0; // compression ihdr[11] = 0; // filter method ihdr[12] = 0; // interlace const row = Buffer.allocUnsafe(1 + size * 3); row[0] = 0; // filter byte for (let x = 0; x < size; x++) { row[1 + x * 3] = r; row[1 + x * 3 + 1] = g; row[1 + x * 3 + 2] = b; } const rawData = Buffer.concat(Array.from({ length: size }, () => row)); const idat = zlib.deflateSync(rawData, { level: 9 }); return Buffer.concat([header, pngChunk('IHDR', ihdr), pngChunk('IDAT', idat), pngChunk('IEND', Buffer.alloc(0))]); } const dir = path.join(__dirname, 'public', 'icons'); fs.mkdirSync(dir, { recursive: true }); const sizes = [16, 48, 128]; const colors = { 16: [59, 130, 246], 48: [59, 130, 246], 128: [59, 130, 246] }; for (const size of sizes) { const [r, g, b] = colors[size]; const png = createSolidPng(size, r, g, b); fs.writeFileSync(path.join(dir, `icon${size}.png`), png); console.log(`Generated icon${size}.png`); }