Enhance Electrobun integration and pre-build process
- Updated pre-build script to create a stub for the dev environment, preventing warnings during Vite development. - Refactored electrobun-bridge.js to improve initialization logic and ensure proper setup of the Electrobun RPC. - Modified ElectronContext to dynamically retrieve the desktop API, enhancing compatibility and maintainability. - Integrated Electrobun bridge initialization in the main application entry point for improved startup behavior.
This commit is contained in:
parent
f59000778e
commit
f6978f44b2
@ -1,4 +1,4 @@
|
||||
import { existsSync } from "node:fs";
|
||||
import { existsSync, mkdirSync, writeFileSync } from "node:fs";
|
||||
import path from "node:path";
|
||||
import { spawnSync } from "node:child_process";
|
||||
import { fileURLToPath } from "node:url";
|
||||
@ -81,6 +81,16 @@ if (buildEnv === "dev") {
|
||||
console.log(
|
||||
"pre-build: dev environment — skipping production renderer build (use dev:renderer for Vite)",
|
||||
);
|
||||
|
||||
// Electrobun still copies dist/mainview into the app bundle; provide a stub so
|
||||
// dev builds don't warn when Vite serves the renderer instead.
|
||||
const stubDir = path.join(rootDir, "dist/mainview");
|
||||
mkdirSync(stubDir, { recursive: true });
|
||||
writeFileSync(
|
||||
path.join(stubDir, "index.html"),
|
||||
"<!doctype html><html><body>Dev mode — start Vite with <code>bun run dev:renderer</code>.</body></html>",
|
||||
);
|
||||
|
||||
console.log(`pre-build: validation passed (${buildEnv})`);
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
@ -4,10 +4,15 @@ import { useNavigate } from 'react-router-dom'
|
||||
|
||||
const electron = window.require ? window.require('electron') : null
|
||||
const ipcRenderer = electron ? electron.ipcRenderer : null
|
||||
const desktopAPI = window.electronAPI
|
||||
|
||||
function getDesktopAPI() {
|
||||
return window.electronAPI
|
||||
}
|
||||
|
||||
// eslint-disable-next-line react-refresh/only-export-components
|
||||
export function isElectron() {
|
||||
const desktopAPI = getDesktopAPI()
|
||||
|
||||
if (desktopAPI?.isDesktop) {
|
||||
return true
|
||||
}
|
||||
@ -61,7 +66,12 @@ const ElectronProvider = ({ children }) => {
|
||||
const [isMaximized, setIsMaximized] = useState(false)
|
||||
const [isFullScreen, setIsFullScreen] = useState(false)
|
||||
const [electronAvailable] = useState(isElectron())
|
||||
const useElectrobun = Boolean(desktopAPI?.isDesktop)
|
||||
const useElectrobun = Boolean(
|
||||
getDesktopAPI()?.isDesktop ||
|
||||
(typeof window !== 'undefined' &&
|
||||
window.__electrobunWebviewId &&
|
||||
window.__electrobunRpcSocketPort)
|
||||
)
|
||||
const navigate = useNavigate()
|
||||
const lastNavigationAtRef = useRef(0)
|
||||
|
||||
@ -91,7 +101,7 @@ const ElectronProvider = ({ children }) => {
|
||||
|
||||
const openExternalUrl = (url) => {
|
||||
if (useElectrobun) {
|
||||
desktopAPI.openExternalUrl(url)
|
||||
getDesktopAPI().openExternalUrl(url)
|
||||
return true
|
||||
}
|
||||
if (electronAvailable && ipcRenderer) {
|
||||
@ -103,7 +113,7 @@ const ElectronProvider = ({ children }) => {
|
||||
|
||||
const openInternalUrl = (url) => {
|
||||
if (useElectrobun) {
|
||||
desktopAPI.openInternalUrl(url)
|
||||
getDesktopAPI().openInternalUrl(url)
|
||||
return true
|
||||
}
|
||||
if (electronAvailable && ipcRenderer) {
|
||||
@ -117,7 +127,7 @@ const ElectronProvider = ({ children }) => {
|
||||
if (!electronAvailable) return
|
||||
|
||||
if (useElectrobun) {
|
||||
desktopAPI.getOsInfo().then((info) => {
|
||||
getDesktopAPI().getOsInfo().then((info) => {
|
||||
if (info?.platform) {
|
||||
setPlatform(info.platform)
|
||||
if (info.platform === 'darwin') {
|
||||
@ -126,13 +136,16 @@ const ElectronProvider = ({ children }) => {
|
||||
}
|
||||
})
|
||||
|
||||
desktopAPI.getWindowState().then(applyWindowState)
|
||||
getDesktopAPI().getWindowState().then(applyWindowState)
|
||||
|
||||
const unsubWindowState = desktopAPI.onMessage('windowState', applyWindowState)
|
||||
const unsubNavigate = desktopAPI.onMessage('navigate', (url) => {
|
||||
const unsubWindowState = getDesktopAPI().onMessage(
|
||||
'windowState',
|
||||
applyWindowState
|
||||
)
|
||||
const unsubNavigate = getDesktopAPI().onMessage('navigate', (url) => {
|
||||
navigate(url)
|
||||
})
|
||||
const unsubNavigationGesture = desktopAPI.onMessage(
|
||||
const unsubNavigationGesture = getDesktopAPI().onMessage(
|
||||
'navigationGesture',
|
||||
navigateHistory
|
||||
)
|
||||
@ -217,7 +230,7 @@ const ElectronProvider = ({ children }) => {
|
||||
|
||||
const handleWindowControl = (action) => {
|
||||
if (useElectrobun) {
|
||||
desktopAPI.windowControl(action)
|
||||
getDesktopAPI().windowControl(action)
|
||||
return
|
||||
}
|
||||
if (electronAvailable && ipcRenderer) {
|
||||
@ -227,7 +240,7 @@ const ElectronProvider = ({ children }) => {
|
||||
|
||||
const getAuthSession = async () => {
|
||||
if (!electronAvailable) return null
|
||||
if (useElectrobun) return await desktopAPI.getAuthSession()
|
||||
if (useElectrobun) return await getDesktopAPI().getAuthSession()
|
||||
if (!ipcRenderer) return null
|
||||
return await ipcRenderer.invoke('auth-session-get')
|
||||
}
|
||||
@ -235,7 +248,7 @@ const ElectronProvider = ({ children }) => {
|
||||
const setAuthSession = async (session) => {
|
||||
if (!electronAvailable) return false
|
||||
if (useElectrobun) {
|
||||
const result = await desktopAPI.setAuthSession(session)
|
||||
const result = await getDesktopAPI().setAuthSession(session)
|
||||
return result?.ok ?? false
|
||||
}
|
||||
if (!ipcRenderer) return false
|
||||
@ -245,7 +258,7 @@ const ElectronProvider = ({ children }) => {
|
||||
const clearAuthSession = async () => {
|
||||
if (!electronAvailable) return false
|
||||
if (useElectrobun) {
|
||||
const result = await desktopAPI.clearAuthSession()
|
||||
const result = await getDesktopAPI().clearAuthSession()
|
||||
return result?.ok ?? false
|
||||
}
|
||||
if (!ipcRenderer) return false
|
||||
@ -254,7 +267,7 @@ const ElectronProvider = ({ children }) => {
|
||||
|
||||
const getAppSettings = useCallback(async () => {
|
||||
if (!electronAvailable) return {}
|
||||
if (useElectrobun) return await desktopAPI.getAppSettings()
|
||||
if (useElectrobun) return await getDesktopAPI().getAppSettings()
|
||||
if (!ipcRenderer) return {}
|
||||
return await ipcRenderer.invoke('app-settings-get')
|
||||
}, [electronAvailable, useElectrobun])
|
||||
@ -263,7 +276,7 @@ const ElectronProvider = ({ children }) => {
|
||||
async (settings) => {
|
||||
if (!electronAvailable) return false
|
||||
if (useElectrobun) {
|
||||
const result = await desktopAPI.setAppSettings(settings)
|
||||
const result = await getDesktopAPI().setAppSettings(settings)
|
||||
return result?.ok ?? false
|
||||
}
|
||||
if (!ipcRenderer) return false
|
||||
@ -276,7 +289,7 @@ const ElectronProvider = ({ children }) => {
|
||||
async (update) => {
|
||||
if (!electronAvailable) return false
|
||||
if (useElectrobun) {
|
||||
const result = await desktopAPI.startAppUpdate(update)
|
||||
const result = await getDesktopAPI().startAppUpdate(update)
|
||||
return result?.ok ?? false
|
||||
}
|
||||
if (!ipcRenderer) return false
|
||||
@ -292,7 +305,7 @@ const ElectronProvider = ({ children }) => {
|
||||
}
|
||||
|
||||
if (useElectrobun) {
|
||||
return desktopAPI.onMessage('appUpdateProgress', handler)
|
||||
return getDesktopAPI().onMessage('appUpdateProgress', handler)
|
||||
}
|
||||
|
||||
if (!ipcRenderer) return () => {}
|
||||
@ -324,7 +337,7 @@ const ElectronProvider = ({ children }) => {
|
||||
if (!electronAvailable) return false
|
||||
try {
|
||||
if (useElectrobun) {
|
||||
const result = await desktopAPI.resizeSpotlightWindow(height)
|
||||
const result = await getDesktopAPI().resizeSpotlightWindow(height)
|
||||
return result?.ok ?? false
|
||||
}
|
||||
if (!ipcRenderer) return false
|
||||
@ -342,7 +355,7 @@ const ElectronProvider = ({ children }) => {
|
||||
async (sections) => {
|
||||
if (!electronAvailable) return false
|
||||
if (useElectrobun) {
|
||||
const result = await desktopAPI.setSidebarViewMenu(sections)
|
||||
const result = await getDesktopAPI().setSidebarViewMenu(sections)
|
||||
return result?.ok ?? false
|
||||
}
|
||||
if (!ipcRenderer) return false
|
||||
@ -353,7 +366,7 @@ const ElectronProvider = ({ children }) => {
|
||||
|
||||
const getElectronVersion = useCallback(async () => {
|
||||
if (!electronAvailable) return null
|
||||
if (useElectrobun) return await desktopAPI.getAppVersion()
|
||||
if (useElectrobun) return await getDesktopAPI().getAppVersion()
|
||||
if (!ipcRenderer) return null
|
||||
return await ipcRenderer.invoke('electron-version')
|
||||
}, [electronAvailable, useElectrobun])
|
||||
|
||||
@ -1,14 +1,16 @@
|
||||
import Electrobun, { Electroview } from "electrobun/view";
|
||||
const listeners = new Map();
|
||||
const pendingMessages = new Map();
|
||||
let rpc = null;
|
||||
let initPromise = null;
|
||||
let initialized = false;
|
||||
|
||||
const isDesktop = Boolean(
|
||||
export function isElectrobunDesktop() {
|
||||
return Boolean(
|
||||
typeof window !== "undefined" &&
|
||||
window.__electrobunWebviewId &&
|
||||
window.__electrobunRpcSocketPort,
|
||||
);
|
||||
|
||||
const listeners = new Map();
|
||||
const pendingMessages = new Map();
|
||||
let rpc = null;
|
||||
}
|
||||
|
||||
function dispatchMessage(channel, data) {
|
||||
const channelListeners = listeners.get(channel);
|
||||
@ -25,7 +27,11 @@ function dispatchMessage(channel, data) {
|
||||
}
|
||||
}
|
||||
|
||||
if (isDesktop) {
|
||||
async function setupRpc() {
|
||||
// electrobun/view captures webview globals at import time — import only after
|
||||
// Electrobun preload has set them (Vite dev can evaluate modules earlier).
|
||||
const { default: Electrobun, Electroview } = await import("electrobun/view");
|
||||
|
||||
rpc = Electroview.defineRPC({
|
||||
maxRequestTime: 30000,
|
||||
handlers: {
|
||||
@ -41,6 +47,70 @@ if (isDesktop) {
|
||||
new Electrobun.Electroview({ rpc });
|
||||
}
|
||||
|
||||
function shouldWaitForElectrobun() {
|
||||
if (isElectrobunDesktop()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (typeof window === "undefined") {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (window.__electrobunWebviewId) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const { protocol, hostname, port } = window.location;
|
||||
|
||||
if (protocol === "views:") {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Vite dev server used by electrobun dev:app
|
||||
if (hostname === "localhost" && port === "5780") {
|
||||
return true;
|
||||
}
|
||||
|
||||
return Boolean(
|
||||
window.__electrobun ||
|
||||
window.__electrobunEventBridge ||
|
||||
window.__electrobunInternalBridge,
|
||||
);
|
||||
}
|
||||
|
||||
export async function initElectrobunBridge() {
|
||||
if (initialized) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!initPromise) {
|
||||
initPromise = (async () => {
|
||||
if (!shouldWaitForElectrobun()) {
|
||||
return;
|
||||
}
|
||||
|
||||
const deadline = Date.now() + 3000;
|
||||
|
||||
while (Date.now() < deadline) {
|
||||
if (isElectrobunDesktop()) {
|
||||
await setupRpc();
|
||||
window.electronAPI = electronAPI;
|
||||
initialized = true;
|
||||
return;
|
||||
}
|
||||
|
||||
await new Promise((resolve) => setTimeout(resolve, 50));
|
||||
}
|
||||
|
||||
console.warn(
|
||||
"Electrobun bridge: webview globals not found; desktop RPC unavailable.",
|
||||
);
|
||||
})();
|
||||
}
|
||||
|
||||
await initPromise;
|
||||
}
|
||||
|
||||
function onMessage(channel, callback) {
|
||||
if (!listeners.has(channel)) {
|
||||
listeners.set(channel, new Set());
|
||||
@ -66,6 +136,8 @@ function removeAllListeners(channel) {
|
||||
}
|
||||
|
||||
async function invokeRequest(method, params = {}) {
|
||||
await initElectrobunBridge();
|
||||
|
||||
if (!rpc?.request?.[method]) {
|
||||
console.warn(`Unhandled RPC request: ${method}`);
|
||||
return null;
|
||||
@ -75,7 +147,9 @@ async function invokeRequest(method, params = {}) {
|
||||
}
|
||||
|
||||
const electronAPI = {
|
||||
isDesktop,
|
||||
get isDesktop() {
|
||||
return isElectrobunDesktop();
|
||||
},
|
||||
onMessage,
|
||||
removeAllListeners,
|
||||
getOsInfo: () => invokeRequest("getOsInfo"),
|
||||
@ -96,8 +170,4 @@ const electronAPI = {
|
||||
getAppVersion: () => invokeRequest("getAppVersion"),
|
||||
};
|
||||
|
||||
if (isDesktop) {
|
||||
window.electronAPI = electronAPI;
|
||||
}
|
||||
|
||||
export default electronAPI;
|
||||
|
||||
@ -1,9 +1,11 @@
|
||||
import './electrobun-bridge.js'
|
||||
import { initElectrobunBridge } from './electrobun-bridge.js'
|
||||
import reportWebVitals from './reportWebVitals'
|
||||
import ReactDOM from 'react-dom/client'
|
||||
import FarmControlApp from './App'
|
||||
import React from 'react'
|
||||
|
||||
await initElectrobunBridge()
|
||||
|
||||
const root = ReactDOM.createRoot(document.getElementById('root'))
|
||||
root.render(
|
||||
<React.StrictMode>
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user