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 path from "node:path";
|
||||||
import { spawnSync } from "node:child_process";
|
import { spawnSync } from "node:child_process";
|
||||||
import { fileURLToPath } from "node:url";
|
import { fileURLToPath } from "node:url";
|
||||||
@ -81,6 +81,16 @@ if (buildEnv === "dev") {
|
|||||||
console.log(
|
console.log(
|
||||||
"pre-build: dev environment — skipping production renderer build (use dev:renderer for Vite)",
|
"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})`);
|
console.log(`pre-build: validation passed (${buildEnv})`);
|
||||||
process.exit(0);
|
process.exit(0);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -4,10 +4,15 @@ import { useNavigate } from 'react-router-dom'
|
|||||||
|
|
||||||
const electron = window.require ? window.require('electron') : null
|
const electron = window.require ? window.require('electron') : null
|
||||||
const ipcRenderer = electron ? electron.ipcRenderer : 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
|
// eslint-disable-next-line react-refresh/only-export-components
|
||||||
export function isElectron() {
|
export function isElectron() {
|
||||||
|
const desktopAPI = getDesktopAPI()
|
||||||
|
|
||||||
if (desktopAPI?.isDesktop) {
|
if (desktopAPI?.isDesktop) {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
@ -61,7 +66,12 @@ const ElectronProvider = ({ children }) => {
|
|||||||
const [isMaximized, setIsMaximized] = useState(false)
|
const [isMaximized, setIsMaximized] = useState(false)
|
||||||
const [isFullScreen, setIsFullScreen] = useState(false)
|
const [isFullScreen, setIsFullScreen] = useState(false)
|
||||||
const [electronAvailable] = useState(isElectron())
|
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 navigate = useNavigate()
|
||||||
const lastNavigationAtRef = useRef(0)
|
const lastNavigationAtRef = useRef(0)
|
||||||
|
|
||||||
@ -91,7 +101,7 @@ const ElectronProvider = ({ children }) => {
|
|||||||
|
|
||||||
const openExternalUrl = (url) => {
|
const openExternalUrl = (url) => {
|
||||||
if (useElectrobun) {
|
if (useElectrobun) {
|
||||||
desktopAPI.openExternalUrl(url)
|
getDesktopAPI().openExternalUrl(url)
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
if (electronAvailable && ipcRenderer) {
|
if (electronAvailable && ipcRenderer) {
|
||||||
@ -103,7 +113,7 @@ const ElectronProvider = ({ children }) => {
|
|||||||
|
|
||||||
const openInternalUrl = (url) => {
|
const openInternalUrl = (url) => {
|
||||||
if (useElectrobun) {
|
if (useElectrobun) {
|
||||||
desktopAPI.openInternalUrl(url)
|
getDesktopAPI().openInternalUrl(url)
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
if (electronAvailable && ipcRenderer) {
|
if (electronAvailable && ipcRenderer) {
|
||||||
@ -117,7 +127,7 @@ const ElectronProvider = ({ children }) => {
|
|||||||
if (!electronAvailable) return
|
if (!electronAvailable) return
|
||||||
|
|
||||||
if (useElectrobun) {
|
if (useElectrobun) {
|
||||||
desktopAPI.getOsInfo().then((info) => {
|
getDesktopAPI().getOsInfo().then((info) => {
|
||||||
if (info?.platform) {
|
if (info?.platform) {
|
||||||
setPlatform(info.platform)
|
setPlatform(info.platform)
|
||||||
if (info.platform === 'darwin') {
|
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 unsubWindowState = getDesktopAPI().onMessage(
|
||||||
const unsubNavigate = desktopAPI.onMessage('navigate', (url) => {
|
'windowState',
|
||||||
|
applyWindowState
|
||||||
|
)
|
||||||
|
const unsubNavigate = getDesktopAPI().onMessage('navigate', (url) => {
|
||||||
navigate(url)
|
navigate(url)
|
||||||
})
|
})
|
||||||
const unsubNavigationGesture = desktopAPI.onMessage(
|
const unsubNavigationGesture = getDesktopAPI().onMessage(
|
||||||
'navigationGesture',
|
'navigationGesture',
|
||||||
navigateHistory
|
navigateHistory
|
||||||
)
|
)
|
||||||
@ -217,7 +230,7 @@ const ElectronProvider = ({ children }) => {
|
|||||||
|
|
||||||
const handleWindowControl = (action) => {
|
const handleWindowControl = (action) => {
|
||||||
if (useElectrobun) {
|
if (useElectrobun) {
|
||||||
desktopAPI.windowControl(action)
|
getDesktopAPI().windowControl(action)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if (electronAvailable && ipcRenderer) {
|
if (electronAvailable && ipcRenderer) {
|
||||||
@ -227,7 +240,7 @@ const ElectronProvider = ({ children }) => {
|
|||||||
|
|
||||||
const getAuthSession = async () => {
|
const getAuthSession = async () => {
|
||||||
if (!electronAvailable) return null
|
if (!electronAvailable) return null
|
||||||
if (useElectrobun) return await desktopAPI.getAuthSession()
|
if (useElectrobun) return await getDesktopAPI().getAuthSession()
|
||||||
if (!ipcRenderer) return null
|
if (!ipcRenderer) return null
|
||||||
return await ipcRenderer.invoke('auth-session-get')
|
return await ipcRenderer.invoke('auth-session-get')
|
||||||
}
|
}
|
||||||
@ -235,7 +248,7 @@ const ElectronProvider = ({ children }) => {
|
|||||||
const setAuthSession = async (session) => {
|
const setAuthSession = async (session) => {
|
||||||
if (!electronAvailable) return false
|
if (!electronAvailable) return false
|
||||||
if (useElectrobun) {
|
if (useElectrobun) {
|
||||||
const result = await desktopAPI.setAuthSession(session)
|
const result = await getDesktopAPI().setAuthSession(session)
|
||||||
return result?.ok ?? false
|
return result?.ok ?? false
|
||||||
}
|
}
|
||||||
if (!ipcRenderer) return false
|
if (!ipcRenderer) return false
|
||||||
@ -245,7 +258,7 @@ const ElectronProvider = ({ children }) => {
|
|||||||
const clearAuthSession = async () => {
|
const clearAuthSession = async () => {
|
||||||
if (!electronAvailable) return false
|
if (!electronAvailable) return false
|
||||||
if (useElectrobun) {
|
if (useElectrobun) {
|
||||||
const result = await desktopAPI.clearAuthSession()
|
const result = await getDesktopAPI().clearAuthSession()
|
||||||
return result?.ok ?? false
|
return result?.ok ?? false
|
||||||
}
|
}
|
||||||
if (!ipcRenderer) return false
|
if (!ipcRenderer) return false
|
||||||
@ -254,7 +267,7 @@ const ElectronProvider = ({ children }) => {
|
|||||||
|
|
||||||
const getAppSettings = useCallback(async () => {
|
const getAppSettings = useCallback(async () => {
|
||||||
if (!electronAvailable) return {}
|
if (!electronAvailable) return {}
|
||||||
if (useElectrobun) return await desktopAPI.getAppSettings()
|
if (useElectrobun) return await getDesktopAPI().getAppSettings()
|
||||||
if (!ipcRenderer) return {}
|
if (!ipcRenderer) return {}
|
||||||
return await ipcRenderer.invoke('app-settings-get')
|
return await ipcRenderer.invoke('app-settings-get')
|
||||||
}, [electronAvailable, useElectrobun])
|
}, [electronAvailable, useElectrobun])
|
||||||
@ -263,7 +276,7 @@ const ElectronProvider = ({ children }) => {
|
|||||||
async (settings) => {
|
async (settings) => {
|
||||||
if (!electronAvailable) return false
|
if (!electronAvailable) return false
|
||||||
if (useElectrobun) {
|
if (useElectrobun) {
|
||||||
const result = await desktopAPI.setAppSettings(settings)
|
const result = await getDesktopAPI().setAppSettings(settings)
|
||||||
return result?.ok ?? false
|
return result?.ok ?? false
|
||||||
}
|
}
|
||||||
if (!ipcRenderer) return false
|
if (!ipcRenderer) return false
|
||||||
@ -276,7 +289,7 @@ const ElectronProvider = ({ children }) => {
|
|||||||
async (update) => {
|
async (update) => {
|
||||||
if (!electronAvailable) return false
|
if (!electronAvailable) return false
|
||||||
if (useElectrobun) {
|
if (useElectrobun) {
|
||||||
const result = await desktopAPI.startAppUpdate(update)
|
const result = await getDesktopAPI().startAppUpdate(update)
|
||||||
return result?.ok ?? false
|
return result?.ok ?? false
|
||||||
}
|
}
|
||||||
if (!ipcRenderer) return false
|
if (!ipcRenderer) return false
|
||||||
@ -292,7 +305,7 @@ const ElectronProvider = ({ children }) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (useElectrobun) {
|
if (useElectrobun) {
|
||||||
return desktopAPI.onMessage('appUpdateProgress', handler)
|
return getDesktopAPI().onMessage('appUpdateProgress', handler)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!ipcRenderer) return () => {}
|
if (!ipcRenderer) return () => {}
|
||||||
@ -324,7 +337,7 @@ const ElectronProvider = ({ children }) => {
|
|||||||
if (!electronAvailable) return false
|
if (!electronAvailable) return false
|
||||||
try {
|
try {
|
||||||
if (useElectrobun) {
|
if (useElectrobun) {
|
||||||
const result = await desktopAPI.resizeSpotlightWindow(height)
|
const result = await getDesktopAPI().resizeSpotlightWindow(height)
|
||||||
return result?.ok ?? false
|
return result?.ok ?? false
|
||||||
}
|
}
|
||||||
if (!ipcRenderer) return false
|
if (!ipcRenderer) return false
|
||||||
@ -342,7 +355,7 @@ const ElectronProvider = ({ children }) => {
|
|||||||
async (sections) => {
|
async (sections) => {
|
||||||
if (!electronAvailable) return false
|
if (!electronAvailable) return false
|
||||||
if (useElectrobun) {
|
if (useElectrobun) {
|
||||||
const result = await desktopAPI.setSidebarViewMenu(sections)
|
const result = await getDesktopAPI().setSidebarViewMenu(sections)
|
||||||
return result?.ok ?? false
|
return result?.ok ?? false
|
||||||
}
|
}
|
||||||
if (!ipcRenderer) return false
|
if (!ipcRenderer) return false
|
||||||
@ -353,7 +366,7 @@ const ElectronProvider = ({ children }) => {
|
|||||||
|
|
||||||
const getElectronVersion = useCallback(async () => {
|
const getElectronVersion = useCallback(async () => {
|
||||||
if (!electronAvailable) return null
|
if (!electronAvailable) return null
|
||||||
if (useElectrobun) return await desktopAPI.getAppVersion()
|
if (useElectrobun) return await getDesktopAPI().getAppVersion()
|
||||||
if (!ipcRenderer) return null
|
if (!ipcRenderer) return null
|
||||||
return await ipcRenderer.invoke('electron-version')
|
return await ipcRenderer.invoke('electron-version')
|
||||||
}, [electronAvailable, useElectrobun])
|
}, [electronAvailable, useElectrobun])
|
||||||
|
|||||||
@ -1,14 +1,16 @@
|
|||||||
import Electrobun, { Electroview } from "electrobun/view";
|
|
||||||
|
|
||||||
const isDesktop = Boolean(
|
|
||||||
typeof window !== "undefined" &&
|
|
||||||
window.__electrobunWebviewId &&
|
|
||||||
window.__electrobunRpcSocketPort,
|
|
||||||
);
|
|
||||||
|
|
||||||
const listeners = new Map();
|
const listeners = new Map();
|
||||||
const pendingMessages = new Map();
|
const pendingMessages = new Map();
|
||||||
let rpc = null;
|
let rpc = null;
|
||||||
|
let initPromise = null;
|
||||||
|
let initialized = false;
|
||||||
|
|
||||||
|
export function isElectrobunDesktop() {
|
||||||
|
return Boolean(
|
||||||
|
typeof window !== "undefined" &&
|
||||||
|
window.__electrobunWebviewId &&
|
||||||
|
window.__electrobunRpcSocketPort,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
function dispatchMessage(channel, data) {
|
function dispatchMessage(channel, data) {
|
||||||
const channelListeners = listeners.get(channel);
|
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({
|
rpc = Electroview.defineRPC({
|
||||||
maxRequestTime: 30000,
|
maxRequestTime: 30000,
|
||||||
handlers: {
|
handlers: {
|
||||||
@ -41,6 +47,70 @@ if (isDesktop) {
|
|||||||
new Electrobun.Electroview({ rpc });
|
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) {
|
function onMessage(channel, callback) {
|
||||||
if (!listeners.has(channel)) {
|
if (!listeners.has(channel)) {
|
||||||
listeners.set(channel, new Set());
|
listeners.set(channel, new Set());
|
||||||
@ -66,6 +136,8 @@ function removeAllListeners(channel) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function invokeRequest(method, params = {}) {
|
async function invokeRequest(method, params = {}) {
|
||||||
|
await initElectrobunBridge();
|
||||||
|
|
||||||
if (!rpc?.request?.[method]) {
|
if (!rpc?.request?.[method]) {
|
||||||
console.warn(`Unhandled RPC request: ${method}`);
|
console.warn(`Unhandled RPC request: ${method}`);
|
||||||
return null;
|
return null;
|
||||||
@ -75,7 +147,9 @@ async function invokeRequest(method, params = {}) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const electronAPI = {
|
const electronAPI = {
|
||||||
isDesktop,
|
get isDesktop() {
|
||||||
|
return isElectrobunDesktop();
|
||||||
|
},
|
||||||
onMessage,
|
onMessage,
|
||||||
removeAllListeners,
|
removeAllListeners,
|
||||||
getOsInfo: () => invokeRequest("getOsInfo"),
|
getOsInfo: () => invokeRequest("getOsInfo"),
|
||||||
@ -96,8 +170,4 @@ const electronAPI = {
|
|||||||
getAppVersion: () => invokeRequest("getAppVersion"),
|
getAppVersion: () => invokeRequest("getAppVersion"),
|
||||||
};
|
};
|
||||||
|
|
||||||
if (isDesktop) {
|
|
||||||
window.electronAPI = electronAPI;
|
|
||||||
}
|
|
||||||
|
|
||||||
export default electronAPI;
|
export default electronAPI;
|
||||||
|
|||||||
@ -1,9 +1,11 @@
|
|||||||
import './electrobun-bridge.js'
|
import { initElectrobunBridge } from './electrobun-bridge.js'
|
||||||
import reportWebVitals from './reportWebVitals'
|
import reportWebVitals from './reportWebVitals'
|
||||||
import ReactDOM from 'react-dom/client'
|
import ReactDOM from 'react-dom/client'
|
||||||
import FarmControlApp from './App'
|
import FarmControlApp from './App'
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
|
|
||||||
|
await initElectrobunBridge()
|
||||||
|
|
||||||
const root = ReactDOM.createRoot(document.getElementById('root'))
|
const root = ReactDOM.createRoot(document.getElementById('root'))
|
||||||
root.render(
|
root.render(
|
||||||
<React.StrictMode>
|
<React.StrictMode>
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user