6d8c96d1db
- Synth styles: blend any two renderers with five mix operations into brand-new generated looks, savable to localStorage as style chips - Share codes bumped to v2 (synth fields), v1 codes still decode - Gradient set generator: 4-12 seed variations with preview grid and ZIP batch download via a dependency-free store-only ZIP writer - Export buttons now open dialogs with live preview and settings - UI redesign: glassy translucent surfaces, rounder geometry, staggered intro animation, hover micro-interactions, refined scrollbars - Docs page, generated favicon/touch icons and OG image, meta tags - Skip chromatic aberration during synth blends to keep the D3D shader linker under its inlining limit Co-authored-by: Cursor <cursoragent@cursor.com>
92 lines
2.8 KiB
JavaScript
92 lines
2.8 KiB
JavaScript
/* Minimal store-only (no compression) ZIP writer for batch PNG export. */
|
|
|
|
var ZipWriter = (function () {
|
|
|
|
var CRC_TABLE = (function () {
|
|
var t = new Uint32Array(256);
|
|
for (var n = 0; n < 256; n++) {
|
|
var c = n;
|
|
for (var k = 0; k < 8; k++) c = (c & 1) ? (0xEDB88320 ^ (c >>> 1)) : (c >>> 1);
|
|
t[n] = c >>> 0;
|
|
}
|
|
return t;
|
|
})();
|
|
|
|
function crc32(buf) {
|
|
var c = 0xFFFFFFFF;
|
|
for (var i = 0; i < buf.length; i++) {
|
|
c = CRC_TABLE[(c ^ buf[i]) & 0xFF] ^ (c >>> 8);
|
|
}
|
|
return (c ^ 0xFFFFFFFF) >>> 0;
|
|
}
|
|
|
|
function dosDateTime() {
|
|
var d = new Date();
|
|
var time = (d.getHours() << 11) | (d.getMinutes() << 5) | (d.getSeconds() >> 1);
|
|
var date = ((d.getFullYear() - 1980) << 9) | ((d.getMonth() + 1) << 5) | d.getDate();
|
|
return { time: time, date: date };
|
|
}
|
|
|
|
/* entries: [{ name: string, data: Uint8Array }] -> Blob */
|
|
function build(entries) {
|
|
var parts = [];
|
|
var central = [];
|
|
var offset = 0;
|
|
var dt = dosDateTime();
|
|
|
|
entries.forEach(function (e) {
|
|
var nameBytes = new TextEncoder().encode(e.name);
|
|
var crc = crc32(e.data);
|
|
var local = new Uint8Array(30 + nameBytes.length);
|
|
var v = new DataView(local.buffer);
|
|
v.setUint32(0, 0x04034b50, true);
|
|
v.setUint16(4, 20, true); // version needed
|
|
v.setUint16(6, 0x0800, true); // utf8 flag
|
|
v.setUint16(8, 0, true); // store
|
|
v.setUint16(10, dt.time, true);
|
|
v.setUint16(12, dt.date, true);
|
|
v.setUint32(14, crc, true);
|
|
v.setUint32(18, e.data.length, true);
|
|
v.setUint32(22, e.data.length, true);
|
|
v.setUint16(26, nameBytes.length, true);
|
|
v.setUint16(28, 0, true);
|
|
local.set(nameBytes, 30);
|
|
parts.push(local, e.data);
|
|
|
|
var cd = new Uint8Array(46 + nameBytes.length);
|
|
var c = new DataView(cd.buffer);
|
|
c.setUint32(0, 0x02014b50, true);
|
|
c.setUint16(4, 20, true);
|
|
c.setUint16(6, 20, true);
|
|
c.setUint16(8, 0x0800, true);
|
|
c.setUint16(10, 0, true);
|
|
c.setUint16(12, dt.time, true);
|
|
c.setUint16(14, dt.date, true);
|
|
c.setUint32(16, crc, true);
|
|
c.setUint32(20, e.data.length, true);
|
|
c.setUint32(24, e.data.length, true);
|
|
c.setUint16(28, nameBytes.length, true);
|
|
c.setUint32(42, offset, true);
|
|
cd.set(nameBytes, 46);
|
|
central.push(cd);
|
|
|
|
offset += local.length + e.data.length;
|
|
});
|
|
|
|
var cdSize = 0;
|
|
central.forEach(function (c) { cdSize += c.length; });
|
|
|
|
var end = new Uint8Array(22);
|
|
var ev = new DataView(end.buffer);
|
|
ev.setUint32(0, 0x06054b50, true);
|
|
ev.setUint16(8, entries.length, true);
|
|
ev.setUint16(10, entries.length, true);
|
|
ev.setUint32(12, cdSize, true);
|
|
ev.setUint32(16, offset, true);
|
|
|
|
return new Blob(parts.concat(central, [end]), { type: "application/zip" });
|
|
}
|
|
|
|
return { build: build };
|
|
})();
|