Files
dickendock/frontend/generate-icons.mjs

107 lines
3.3 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// Generiert icon-192.png und icon-512.png ohne externe Abhängigkeiten
// Wird automatisch vor dem Build ausgeführt (prebuild script)
import { createWriteStream } from 'fs';
import { mkdir } from 'fs/promises';
await mkdir('./public', { recursive: true });
function createPNG(size) {
// Minimales PNG: dunkel (#0d0d0f) mit DD in teal (#4ecdc4)
// Wir nutzen den Canvas aus dem Vite/Node Kontext nicht
// stattdessen schreiben wir ein gültiges einfarbiges PNG per Hand
const width = size, height = size;
function crc32(buf) {
let c, crcTable = [];
for (let n = 0; n < 256; n++) {
c = n;
for (let k = 0; k < 8; k++) c = (c & 1) ? 0xedb88320 ^ (c >>> 1) : c >>> 1;
crcTable[n] = c;
}
let crc = 0xffffffff;
for (let i = 0; i < buf.length; i++) crc = crcTable[(crc ^ buf[i]) & 0xff] ^ (crc >>> 8);
return (crc ^ 0xffffffff) >>> 0;
}
function chunk(type, data) {
const t = Buffer.from(type);
const len = Buffer.alloc(4); len.writeUInt32BE(data.length);
const crcBuf = Buffer.concat([t, data]);
const crc = Buffer.alloc(4); crc.writeUInt32BE(crc32(crcBuf));
return Buffer.concat([len, t, data, crc]);
}
// IHDR
const ihdr = Buffer.alloc(13);
ihdr.writeUInt32BE(width, 0); ihdr.writeUInt32BE(height, 4);
ihdr[8] = 8; ihdr[9] = 2; ihdr[10] = 0; ihdr[11] = 0; ihdr[12] = 0;
// Pixel data: dark background with a teal circle
const rows = [];
const cx = width / 2, cy = height / 2, r = width * 0.35;
const r2 = width * 0.28;
for (let y = 0; y < height; y++) {
const row = [0]; // filter byte
for (let x = 0; x < width; x++) {
const dx = x - cx, dy = y - cy;
const dist = Math.sqrt(dx*dx + dy*dy);
if (dist < r2) {
// teal inner circle
row.push(0x4e, 0xcd, 0xc4);
} else if (dist < r) {
// slightly lighter ring
row.push(0x1a, 0x8a, 0x84);
} else {
// dark background
row.push(0x0d, 0x0d, 0x0f);
}
}
rows.push(Buffer.from(row));
}
// zlib compress (deflate) minimal uncompressed blocks
const raw = Buffer.concat(rows);
// Split into 65535-byte chunks for uncompressed deflate
const chunks = [];
const CHUNK = 65535;
for (let i = 0; i < raw.length; i += CHUNK) {
const slice = raw.slice(i, i + CHUNK);
const last = i + CHUNK >= raw.length ? 1 : 0;
const hdr = Buffer.alloc(5);
hdr[0] = last; hdr.writeUInt16LE(slice.length, 1); hdr.writeUInt16LE(~slice.length & 0xffff, 3);
chunks.push(hdr, slice);
}
const deflated = Buffer.concat(chunks);
// Adler32
let s1 = 1, s2 = 0;
for (const b of raw) { s1 = (s1 + b) % 65521; s2 = (s2 + s1) % 65521; }
const adler = Buffer.alloc(4); adler.writeUInt32BE((s2 << 16) | s1);
const zlib = Buffer.concat([
Buffer.from([0x78, 0x01]), // zlib header (no compression)
deflated,
adler
]);
const png = Buffer.concat([
Buffer.from([137, 80, 78, 71, 13, 10, 26, 10]), // PNG signature
chunk('IHDR', ihdr),
chunk('IDAT', zlib),
chunk('IEND', Buffer.alloc(0))
]);
return png;
}
const png192 = createPNG(192);
const png512 = createPNG(512);
const ws192 = createWriteStream('./public/icon-192.png');
ws192.write(png192); ws192.end();
const ws512 = createWriteStream('./public/icon-512.png');
ws512.write(png512); ws512.end();
console.log('✅ Icons generiert: icon-192.png, icon-512.png');