mirror of
https://github.com/Termix-SSH/Termix.git
synced 2026-11-07 02:55:24 +00:00
117 lines
3.4 KiB
JavaScript
117 lines
3.4 KiB
JavaScript
import sharp from "sharp";
|
|
import { readFileSync, writeFileSync, mkdirSync, copyFileSync } from "fs";
|
|
import { join, dirname } from "path";
|
|
import { fileURLToPath } from "url";
|
|
|
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
const root = join(__dirname, "..");
|
|
const publicDir = join(root, "public");
|
|
const iconsDir = join(publicDir, "icons");
|
|
|
|
mkdirSync(iconsDir, { recursive: true });
|
|
|
|
const svgBuffer = readFileSync(join(publicDir, "icon-square.svg"));
|
|
const render = (size) =>
|
|
sharp(svgBuffer, { density: 72 * Math.max(1, size / 1024) })
|
|
.resize(size, size)
|
|
.png({ compressionLevel: 9 })
|
|
.toBuffer();
|
|
|
|
const pngSizes = [16, 24, 32, 48, 64, 128, 256, 512, 1024];
|
|
const png = Object.fromEntries(
|
|
await Promise.all(pngSizes.map(async (s) => [s, await render(s)])),
|
|
);
|
|
|
|
for (const size of pngSizes) {
|
|
writeFileSync(join(iconsDir, `${size}x${size}.png`), png[size]);
|
|
}
|
|
writeFileSync(join(publicDir, "icon.png"), png[1024]);
|
|
writeFileSync(join(publicDir, "full-icon.png"), png[1024]);
|
|
writeFileSync(join(publicDir, "icon-mac.png"), png[512]);
|
|
|
|
writeFileSync(
|
|
join(publicDir, "favicon.ico"),
|
|
buildIco(
|
|
[16, 32, 48].map((s) => png[s]),
|
|
[16, 32, 48],
|
|
),
|
|
);
|
|
const winSizes = [16, 24, 32, 48, 64, 128, 256];
|
|
writeFileSync(
|
|
join(publicDir, "icon.ico"),
|
|
buildIco(
|
|
winSizes.map((s) => png[s]),
|
|
winSizes,
|
|
),
|
|
);
|
|
writeFileSync(
|
|
join(publicDir, "icon.icns"),
|
|
buildIcns([
|
|
["icp4", png[16]],
|
|
["icp5", png[32]],
|
|
["icp6", png[64]],
|
|
["ic07", png[128]],
|
|
["ic08", png[256]],
|
|
["ic09", png[512]],
|
|
["ic10", png[1024]],
|
|
["ic11", png[32]],
|
|
["ic12", png[64]],
|
|
["ic13", png[256]],
|
|
["ic14", png[512]],
|
|
]),
|
|
);
|
|
copyFileSync(join(publicDir, "icon.ico"), join(iconsDir, "icon.ico"));
|
|
copyFileSync(join(publicDir, "icon.icns"), join(iconsDir, "icon.icns"));
|
|
|
|
// Transparent foreground from public/icon.svg; macOS colors this template.
|
|
const traySvg = readFileSync(join(iconsDir, "trayTemplate.svg"));
|
|
for (const scale of [1, 2]) {
|
|
await sharp(traySvg, { density: 72 * scale })
|
|
.resize(16 * scale, 16 * scale)
|
|
.png()
|
|
.toFile(join(iconsDir, `trayTemplate${scale === 2 ? "@2x" : ""}.png`));
|
|
}
|
|
|
|
console.log("Icons generated from public/icon-square.svg");
|
|
|
|
function buildIco(pngBuffers, sizes) {
|
|
const headerSize = 6;
|
|
const dirEntrySize = 16;
|
|
let offset = headerSize + pngBuffers.length * dirEntrySize;
|
|
const entries = pngBuffers.map((buf, i) => {
|
|
const entry = { size: sizes[i], buf, offset };
|
|
offset += buf.length;
|
|
return entry;
|
|
});
|
|
|
|
const ico = Buffer.alloc(offset);
|
|
ico.writeUInt16LE(0, 0);
|
|
ico.writeUInt16LE(1, 2);
|
|
ico.writeUInt16LE(entries.length, 4);
|
|
entries.forEach((e, i) => {
|
|
const base = headerSize + i * dirEntrySize;
|
|
ico.writeUInt8(e.size > 255 ? 0 : e.size, base);
|
|
ico.writeUInt8(e.size > 255 ? 0 : e.size, base + 1);
|
|
ico.writeUInt16LE(1, base + 4);
|
|
ico.writeUInt16LE(32, base + 6);
|
|
ico.writeUInt32LE(e.buf.length, base + 8);
|
|
ico.writeUInt32LE(e.offset, base + 12);
|
|
e.buf.copy(ico, e.offset);
|
|
});
|
|
return ico;
|
|
}
|
|
|
|
function buildIcns(chunks) {
|
|
const parts = chunks.map(([type, data]) => {
|
|
const head = Buffer.alloc(8);
|
|
head.write(type, 0, "ascii");
|
|
head.writeUInt32BE(data.length + 8, 4);
|
|
return Buffer.concat([head, data]);
|
|
});
|
|
const body = Buffer.concat(parts);
|
|
const head = Buffer.alloc(8);
|
|
head.write("icns", 0, "ascii");
|
|
head.writeUInt32BE(body.length + 8, 4);
|
|
return Buffer.concat([head, body]);
|
|
}
|