Files
Kordant/browser-ext/scripts/generate-icons.mjs
Michael Freno b03096f19d feat(browser-ext): move browser extension to browser-ext/ and update API client to tRPC
- Create browser-ext/ with full extension code (MV3 manifest, background
  service worker, content script, popup, options page)
- Add tRPC API client that communicates with unified monolith endpoints
- Implement cache, settings, and phishing detection utilities
- Create extension tRPC router in web app (getAuthStatus, linkDevice,
  reportPhishing)
- Configure Vite build with manifest V3 support
- Write unit tests for cache, phishing detector, and API client
- All 20 tests passing, TypeScript lint clean
2026-05-25 18:13:44 -04:00

81 lines
2.2 KiB
JavaScript

import { writeFileSync, mkdirSync } from "fs";
import { resolve } from "path";
import { deflateSync } from "zlib";
const __dirname = import.meta.dirname;
const iconsDir = resolve(__dirname, "../public/icons");
mkdirSync(iconsDir, { recursive: true });
function crc32(buf) {
let crc = 0xffffffff;
const table = new Int32Array(256);
for (let i = 0; i < 256; i++) {
let c = i;
for (let j = 0; j < 8; j++) c = c & 1 ? 0xedb88320 ^ (c >>> 1) : c >>> 1;
table[i] = c;
}
for (let i = 0; i < buf.length; i++) crc = table[(crc ^ buf[i]) & 0xff] ^ (crc >>> 8);
return (crc ^ 0xffffffff) >>> 0;
}
function chunk(type, data) {
const len = Buffer.alloc(4);
len.writeUInt32BE(data.length);
const typeB = Buffer.from(type, "ascii");
const crcData = Buffer.concat([typeB, data]);
const crcV = Buffer.alloc(4);
crcV.writeUInt32BE(crc32(crcData));
return Buffer.concat([len, typeB, data, crcV]);
}
function createPNG(size) {
const raw = Buffer.alloc(size * size * 4);
const cx = size / 2;
const cy = size / 2;
const r = size * 0.42;
for (let y = 0; y < size; y++) {
for (let x = 0; x < size; x++) {
const dx = x - cx;
const dy = y - cy;
const dist = Math.sqrt(dx * dx + dy * dy);
const i = (y * size + x) * 4;
if (dist <= r) {
raw[i] = 99;
raw[i + 1] = 102;
raw[i + 2] = 241;
raw[i + 3] = 255;
} else {
raw[i] = 0;
raw[i + 1] = 0;
raw[i + 2] = 0;
raw[i + 3] = 0;
}
}
}
const filter = Buffer.alloc(size * size + size);
for (let y = 0; y < size; y++) {
filter[y * (size * 4 + 1)] = 0;
raw.copy(filter, y * (size * 4 + 1) + 1, y * size * 4, (y + 1) * size * 4);
}
const compressed = deflateSync(filter);
const ihdr = Buffer.alloc(13);
ihdr.writeUInt32BE(size, 0);
ihdr.writeUInt32BE(size, 4);
ihdr[8] = 8;
ihdr[9] = 6;
ihdr[10] = 0;
ihdr[11] = 0;
ihdr[12] = 0;
const sig = Buffer.from([137, 80, 78, 71, 13, 10, 26, 10]);
return Buffer.concat([sig, chunk("IHDR", ihdr), chunk("IDAT", compressed), chunk("IEND", Buffer.alloc(0))]);
}
for (const size of [16, 48, 128]) {
writeFileSync(resolve(iconsDir, `icon-${size}.png`), createPNG(size));
console.log(`Created icon-${size}.png`);
}