From 913252329af87aa7974bdfe973a4d43ece69b6f4 Mon Sep 17 00:00:00 2001 From: Dicken Date: Mon, 25 May 2026 15:34:53 +0200 Subject: [PATCH] Dateien nach "frontend" hochladen --- frontend/generate-icons.mjs | 106 ++++++++++++++++++++++++++++++++++++ 1 file changed, 106 insertions(+) create mode 100644 frontend/generate-icons.mjs diff --git a/frontend/generate-icons.mjs b/frontend/generate-icons.mjs new file mode 100644 index 0000000..f0fa36c --- /dev/null +++ b/frontend/generate-icons.mjs @@ -0,0 +1,106 @@ +// 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');