1
0

Make Electron build

I historically have never liked Electron, and I don't like it now, but
unfortunately due to poor browser support, I don't have a choice to
ship anything more lightweight if I want to use the Filesystem API
as part of the packaged build, which is kind of the point.

I guess it's true:
"You either die a hero or you live long enough to become the villain."

This is an experimental build.
This commit is contained in:
2026-03-22 13:30:38 +01:00
parent c0d564b642
commit b117f3290b
7 changed files with 5091 additions and 1 deletions

16
electron/copy-dist.mjs Normal file
View File

@@ -0,0 +1,16 @@
import { cpSync, rmSync, existsSync } from 'node:fs';
import { join, dirname } from 'node:path';
import { fileURLToPath } from 'node:url';
const dir = dirname(fileURLToPath(import.meta.url));
const src = join(dir, '..', 'web', 'dist');
const dst = join(dir, 'dist');
if (!existsSync(src)) {
console.error('web/dist/ does not exist — run "cd web && npm run build" first.');
process.exit(1);
}
rmSync(dst, { recursive: true, force: true });
cpSync(src, dst, { recursive: true });
console.log(`Copied ${src} -> ${dst}`);

76
electron/main.js Normal file
View File

@@ -0,0 +1,76 @@
const { app, BrowserWindow, shell } = require('electron');
const http = require('http');
const fs = require('fs');
const path = require('path');
const DIST = path.join(__dirname, 'dist');
const MIME = {
'.html': 'text/html',
'.css': 'text/css',
'.js': 'application/javascript',
'.json': 'application/json',
'.wasm': 'application/wasm',
'.zip': 'application/zip',
'.tgz': 'application/gzip',
'.svg': 'image/svg+xml',
'.png': 'image/png',
'.ico': 'image/x-icon',
'.webmanifest': 'application/manifest+json',
};
let server;
let win;
function createWindow() {
win = new BrowserWindow({
width: 1200,
height: 900,
icon: path.join(DIST, 'favicon', 'favicon-96x96.png'),
autoHideMenuBar: true,
webPreferences: { contextIsolation: true },
});
const localOrigin = `http://localhost:${server.address().port}`;
win.loadURL(localOrigin);
win.on('closed', () => { win = null; });
const isExternal = (url) => {
try { return new URL(url).origin !== localOrigin; } catch { return false; }
};
win.webContents.on('will-navigate', (e, url) => {
if (isExternal(url)) { e.preventDefault(); shell.openExternal(url); }
});
win.webContents.setWindowOpenHandler(({ url }) => {
if (isExternal(url)) shell.openExternal(url);
return { action: 'deny' };
});
}
server = http.createServer((req, res) => {
const url = new URL(req.url, 'http://localhost');
let filePath = path.join(DIST, decodeURIComponent(url.pathname));
try {
const stat = fs.statSync(filePath);
if (stat.isDirectory()) filePath = path.join(filePath, 'index.html');
} catch {}
try {
fs.statSync(filePath);
res.writeHead(200, { 'Content-Type': MIME[path.extname(filePath)] || 'application/octet-stream' });
fs.createReadStream(filePath).pipe(res);
} catch {
res.writeHead(404, { 'Content-Type': 'text/plain' });
res.end('Not found');
}
});
server.listen(0, '127.0.0.1', () => {
app.whenReady().then(createWindow);
});
app.on('window-all-closed', () => { app.quit(); });
app.on('before-quit', () => { server.close(); });

4895
electron/package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

32
electron/package.json Normal file
View File

@@ -0,0 +1,32 @@
{
"name": "kobopatch-electron",
"version": "1.5.0",
"private": true,
"main": "main.js",
"scripts": {
"copy-dist": "node copy-dist.mjs",
"start": "npm run copy-dist && electron .",
"build": "npm run copy-dist && electron-builder",
"build:mac": "npm run copy-dist && electron-builder --mac",
"build:win": "npm run copy-dist && electron-builder --win",
"build:linux": "npm run copy-dist && electron-builder --linux"
},
"devDependencies": {
"electron": "^35.0.0",
"electron-builder": "^26.0.0"
},
"build": {
"appId": "be.nicoverbruggen.kobopatch-webui",
"productName": "KoboPatch Web UI",
"directories": {
"output": "release"
},
"files": [
"main.js",
"dist/**/*"
],
"mac": { "target": "dmg", "icon": "dist/favicon/web-app-manifest-512x512.png" },
"win": { "target": "portable", "icon": "dist/favicon/web-app-manifest-512x512.png" },
"linux": { "target": "AppImage", "icon": "dist/favicon/web-app-manifest-512x512.png" }
}
}