Add Electrobun configuration and renderer bridge for desktop app.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Tom Butcher 2026-07-28 22:03:06 +01:00
parent f0b6059e70
commit 97974ada12
4 changed files with 112 additions and 1 deletions

34
electrobun.config.ts Normal file
View File

@ -0,0 +1,34 @@
// Electrobun requires this filename; application code remains JavaScript.
export default {
app: {
name: "Farm Control Server",
identifier: "com.tombutcher.farmcontrolserver",
version: "1.0.0",
description: "Farm Control Server desktop host application",
},
build: {
buildFolder: "build",
artifactFolder: "app_dist",
bun: {
entrypoint: "src/bun/index.js",
},
copy: {
"dist/app/index.html": "views/mainview/index.html",
"dist/app/assets": "views/mainview/assets",
"config.json": "config.json",
},
watchIgnore: ["dist/**"],
mac: {
bundleCEF: false,
},
linux: {
bundleCEF: false,
},
win: {
bundleCEF: false,
},
},
runtime: {
exitOnLastWindowClosed: false,
},
};

View File

@ -0,0 +1,75 @@
import Electrobun, { Electroview } from "electrobun/view";
const listeners = new Map();
const rpc = Electroview.defineRPC({
maxRequestTime: 30000,
handlers: {
requests: {},
messages: {
"*": (channel, data) => {
const channelListeners = listeners.get(channel);
if (!channelListeners) {
return;
}
for (const callback of channelListeners) {
callback(data);
}
},
},
},
});
const electroview = new Electrobun.Electroview({ rpc });
function onIPCData(channel, callback) {
if (!listeners.has(channel)) {
listeners.set(channel, new Set());
}
listeners.get(channel).add(callback);
}
function removeAllListeners(channel) {
listeners.delete(channel);
}
function sendIPC(channel, data) {
if (channel === "getData") {
electroview.rpc.request.getData({});
return;
}
if (channel === "authenticateOTP") {
electroview.rpc.request.authenticateOTP({ otp: data });
return;
}
if (channel === "window-minimize") {
electroview.rpc.request.minimizeWindow({});
return;
}
if (channel === "window-maximize") {
electroview.rpc.request.maximizeWindow({});
return;
}
if (channel === "window-close") {
electroview.rpc.request.closeWindow({});
return;
}
console.warn(`Unhandled IPC channel: ${channel}`);
}
window.electronAPI = {
onIPCData,
sendIPC,
minimize: () => sendIPC("window-minimize"),
maximize: () => sendIPC("window-maximize"),
close: () => sendIPC("window-close"),
removeAllListeners,
};
export default window.electronAPI;

View File

@ -1,5 +1,6 @@
import { StrictMode } from "react"; import { StrictMode } from "react";
import { createRoot } from "react-dom/client"; import { createRoot } from "react-dom/client";
import "./electrobun-bridge.js";
import "./index.css"; import "./index.css";
import App from "./App.jsx"; import App from "./App.jsx";
import "antd/dist/reset.css"; import "antd/dist/reset.css";

View File

@ -6,10 +6,11 @@ import svgo from "vite-plugin-svgo";
export default defineConfig({ export default defineConfig({
base: "./", base: "./",
publicDir: path.resolve(__dirname, "public"),
plugins: [react(), svgr(), svgo()], plugins: [react(), svgr(), svgo()],
root: path.resolve(__dirname), root: path.resolve(__dirname),
build: { build: {
outDir: path.resolve(__dirname, "../../build/electron"), outDir: path.resolve(__dirname, "../../dist/app"),
emptyOutDir: true, emptyOutDir: true,
rollupOptions: { rollupOptions: {
input: { input: {