- 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
81 lines
2.2 KiB
JavaScript
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`);
|
|
}
|