- Adjusted CSS for macOS vibrancy, refining background opacity for light and dark modes. - Introduced new styles for selected menu items in dark mode to enhance UI consistency. - Added traffic light positioning and window chrome observer in native macOS window effects for better window management. - Refactored Electron context to utilize the Electrobun bridge for improved API interactions and error handling. - Streamlined the initialization process for the Electrobun bridge in the main application entry point.
192 lines
4.7 KiB
JavaScript
192 lines
4.7 KiB
JavaScript
const listeners = new Map()
|
|
const pendingMessages = new Map()
|
|
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) {
|
|
const channelListeners = listeners.get(channel)
|
|
if (!channelListeners?.size) {
|
|
if (!pendingMessages.has(channel)) {
|
|
pendingMessages.set(channel, [])
|
|
}
|
|
pendingMessages.get(channel).push(data)
|
|
return
|
|
}
|
|
|
|
for (const callback of channelListeners) {
|
|
callback(data)
|
|
}
|
|
}
|
|
|
|
// Bun delivers push messages via executeJavascript for immediate handling.
|
|
// WebSocket RPC messages can be deferred by WKWebView during native window
|
|
// transitions (e.g. fullscreen) until the next user interaction.
|
|
if (typeof window !== 'undefined') {
|
|
window.__farmcontrolDispatchRpcMessage = dispatchMessage
|
|
}
|
|
|
|
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: {
|
|
requests: {},
|
|
messages: {
|
|
'*': (channel, data) => {
|
|
dispatchMessage(channel, data)
|
|
}
|
|
}
|
|
}
|
|
})
|
|
|
|
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) {
|
|
void initElectrobunBridge()
|
|
|
|
if (!listeners.has(channel)) {
|
|
listeners.set(channel, new Set())
|
|
}
|
|
listeners.get(channel).add(callback)
|
|
|
|
const queued = pendingMessages.get(channel)
|
|
if (queued?.length) {
|
|
pendingMessages.delete(channel)
|
|
for (const payload of queued) {
|
|
callback(payload)
|
|
}
|
|
}
|
|
|
|
return () => {
|
|
listeners.get(channel)?.delete(callback)
|
|
}
|
|
}
|
|
|
|
function removeAllListeners(channel) {
|
|
listeners.delete(channel)
|
|
pendingMessages.delete(channel)
|
|
}
|
|
|
|
async function invokeRequest(method, params = {}) {
|
|
await initElectrobunBridge()
|
|
|
|
if (!rpc?.request) {
|
|
console.warn(`Electrobun RPC unavailable for request: ${method}`)
|
|
return null
|
|
}
|
|
|
|
try {
|
|
return await rpc.request[method](params)
|
|
} catch (error) {
|
|
console.warn(`Electrobun RPC request failed: ${method}`, error)
|
|
return null
|
|
}
|
|
}
|
|
|
|
const electronAPI = {
|
|
get isDesktop() {
|
|
return isElectrobunDesktop()
|
|
},
|
|
onMessage,
|
|
removeAllListeners,
|
|
getOsInfo: () => invokeRequest('getOsInfo'),
|
|
getWindowState: () => invokeRequest('getWindowState'),
|
|
windowControl: (action) => invokeRequest('windowControl', { action }),
|
|
openExternalUrl: (url) => invokeRequest('openExternalUrl', { url }),
|
|
openInternalUrl: (url) => invokeRequest('openInternalUrl', { url }),
|
|
getAuthSession: () => invokeRequest('getAuthSession'),
|
|
setAuthSession: (session) => invokeRequest('setAuthSession', { session }),
|
|
clearAuthSession: () => invokeRequest('clearAuthSession'),
|
|
getAppSettings: () => invokeRequest('getAppSettings'),
|
|
setAppSettings: (settings) => invokeRequest('setAppSettings', { settings }),
|
|
startAppUpdate: (update) => invokeRequest('startAppUpdate', { update }),
|
|
resizeSpotlightWindow: (height) =>
|
|
invokeRequest('resizeSpotlightWindow', { height }),
|
|
setSidebarViewMenu: (sections) =>
|
|
invokeRequest('setSidebarViewMenu', { sections }),
|
|
getAppVersion: () => invokeRequest('getAppVersion')
|
|
}
|
|
|
|
if (typeof window !== 'undefined') {
|
|
window.electronAPI = electronAPI
|
|
}
|
|
|
|
export default electronAPI
|