Until now the desktop app only ran from a source checkout, so Pithos was really usable only inside Theseus. electron-builder (the version Theseus uses) now makes an NSIS installer and a portable exe; s3d is still fetched on first run by the checksum-checked installer, so the app stays small and never ships an s3d it did not verify. Icons are rendered from ui/icon.svg. Quitting also no longer waits on idle keep-alive sockets before exiting.
52 lines
2.5 KiB
JavaScript
52 lines
2.5 KiB
JavaScript
// Renders ui/icon.svg into the desktop app's icons: desktop/icons/icon.png (512 px,
|
|
// for Linux and the window) and desktop/icons/icon.ico (16-256 px, PNG-compressed
|
|
// entries, for Windows). Run with Electron, which brings its own renderer:
|
|
// npx electron scripts/make-icons.cjs
|
|
const { app, BrowserWindow } = require('electron');
|
|
const fs = require('node:fs');
|
|
const path = require('node:path');
|
|
|
|
const root = path.join(__dirname, '..');
|
|
const svg = fs.readFileSync(path.join(root, 'ui', 'icon.svg'), 'utf8');
|
|
const out = path.join(root, 'desktop', 'icons');
|
|
|
|
app.disableHardwareAcceleration();
|
|
app.on('window-all-closed', () => {});
|
|
app.whenReady().then(async () => {
|
|
// The page draws the SVG on a canvas at each size and hands back a PNG.
|
|
const win = new BrowserWindow({ show: false, webPreferences: { offscreen: true } });
|
|
await win.loadURL('data:text/html,<html><body></body></html>');
|
|
const src = `data:image/svg+xml;base64,${Buffer.from(svg).toString('base64')}`;
|
|
const render = async (size) => {
|
|
const url = await win.webContents.executeJavaScript(`new Promise((ok, fail) => {
|
|
const img = new Image(); img.onerror = () => fail(new Error('svg did not load'));
|
|
img.onload = () => { const c = document.createElement('canvas'); c.width = c.height = ${size};
|
|
const g = c.getContext('2d'); g.imageSmoothingQuality = 'high'; g.drawImage(img, 0, 0, ${size}, ${size}); ok(c.toDataURL('image/png')); };
|
|
img.src = ${JSON.stringify(src)};
|
|
})`);
|
|
return Buffer.from(url.split(',')[1], 'base64');
|
|
};
|
|
|
|
fs.mkdirSync(out, { recursive: true });
|
|
fs.writeFileSync(path.join(out, 'icon.png'), await render(512));
|
|
|
|
const sizes = [16, 24, 32, 48, 64, 128, 256];
|
|
const pngs = [];
|
|
for (const s of sizes) pngs.push(await render(s));
|
|
const header = Buffer.alloc(6);
|
|
header.writeUInt16LE(0, 0); header.writeUInt16LE(1, 2); header.writeUInt16LE(sizes.length, 4);
|
|
const dir = Buffer.alloc(16 * sizes.length);
|
|
let offset = 6 + dir.length;
|
|
sizes.forEach((s, i) => {
|
|
const e = i * 16;
|
|
dir.writeUInt8(s >= 256 ? 0 : s, e); dir.writeUInt8(s >= 256 ? 0 : s, e + 1);
|
|
dir.writeUInt8(0, e + 2); dir.writeUInt8(0, e + 3);
|
|
dir.writeUInt16LE(1, e + 4); dir.writeUInt16LE(32, e + 6);
|
|
dir.writeUInt32LE(pngs[i].length, e + 8); dir.writeUInt32LE(offset, e + 12);
|
|
offset += pngs[i].length;
|
|
});
|
|
fs.writeFileSync(path.join(out, 'icon.ico'), Buffer.concat([header, dir, ...pngs]));
|
|
win.destroy();
|
|
console.log('icons written to', out);
|
|
app.quit();
|
|
}).catch((e) => { console.error(e); app.exit(1); });
|