// @bun var __defProp = Object.defineProperty; var __getOwnPropNames = Object.getOwnPropertyNames; var __getOwnPropDesc = Object.getOwnPropertyDescriptor; var __hasOwnProp = Object.prototype.hasOwnProperty; var __moduleCache = /* @__PURE__ */ new WeakMap; var __toCommonJS = (from) => { var entry = __moduleCache.get(from), desc; if (entry) return entry; entry = __defProp({}, "__esModule", { value: true }); if (from && typeof from === "object" || typeof from === "function") __getOwnPropNames(from).map((key) => !__hasOwnProp.call(entry, key) && __defProp(entry, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable })); __moduleCache.set(from, entry); return entry; }; var __export = (target, all) => { for (var name in all) __defProp(target, name, { get: all[name], enumerable: true, configurable: true, set: (newValue) => all[name] = () => newValue }); }; var __esm = (fn, res) => () => (fn && (res = fn(fn = 0)), res); var __promiseAll = (args) => Promise.all(args); // node_modules/electrobun/dist/api/bun/events/event.ts class ElectrobunEvent { name; data; _response; responseWasSet = false; constructor(name, data) { this.name = name; this.data = data; } get response() { return this._response; } set response(value) { this._response = value; this.responseWasSet = true; } clearResponse() { this._response = undefined; this.responseWasSet = false; } } // node_modules/electrobun/dist/api/bun/events/windowEvents.ts var windowEvents_default; var init_windowEvents = __esm(() => { windowEvents_default = { close: (data) => new ElectrobunEvent("close", data), resize: (data) => new ElectrobunEvent("resize", data), move: (data) => new ElectrobunEvent("move", data), focus: (data) => new ElectrobunEvent("focus", data) }; }); // node_modules/electrobun/dist/api/bun/events/webviewEvents.ts var webviewEvents_default; var init_webviewEvents = __esm(() => { webviewEvents_default = { willNavigate: (data) => new ElectrobunEvent("will-navigate", data), didNavigate: (data) => new ElectrobunEvent("did-navigate", data), didNavigateInPage: (data) => new ElectrobunEvent("did-navigate-in-page", data), didCommitNavigation: (data) => new ElectrobunEvent("did-commit-navigation", data), domReady: (data) => new ElectrobunEvent("dom-ready", data), newWindowOpen: (data) => new ElectrobunEvent("new-window-open", data), hostMessage: (data) => new ElectrobunEvent("host-message", data), downloadStarted: (data) => new ElectrobunEvent("download-started", data), downloadProgress: (data) => new ElectrobunEvent("download-progress", data), downloadCompleted: (data) => new ElectrobunEvent("download-completed", data), downloadFailed: (data) => new ElectrobunEvent("download-failed", data) }; }); // node_modules/electrobun/dist/api/bun/events/trayEvents.ts var trayEvents_default; var init_trayEvents = __esm(() => { trayEvents_default = { trayClicked: (data) => new ElectrobunEvent("tray-clicked", data) }; }); // node_modules/electrobun/dist/api/bun/events/ApplicationEvents.ts var ApplicationEvents_default; var init_ApplicationEvents = __esm(() => { ApplicationEvents_default = { applicationMenuClicked: (data) => new ElectrobunEvent("application-menu-clicked", data), contextMenuClicked: (data) => new ElectrobunEvent("context-menu-clicked", data), openUrl: (data) => new ElectrobunEvent("open-url", data), beforeQuit: (data) => new ElectrobunEvent("before-quit", data) }; }); // node_modules/electrobun/dist/api/bun/events/eventEmitter.ts import EventEmitter from "events"; var ElectrobunEventEmitter, electrobunEventEmitter, eventEmitter_default; var init_eventEmitter = __esm(() => { init_windowEvents(); init_webviewEvents(); init_trayEvents(); init_ApplicationEvents(); ElectrobunEventEmitter = class ElectrobunEventEmitter extends EventEmitter { constructor() { super(); } emitEvent(ElectrobunEvent2, specifier) { if (specifier) { this.emit(`${ElectrobunEvent2.name}-${specifier}`, ElectrobunEvent2); } else { this.emit(ElectrobunEvent2.name, ElectrobunEvent2); } } events = { window: { ...windowEvents_default }, webview: { ...webviewEvents_default }, tray: { ...trayEvents_default }, app: { ...ApplicationEvents_default } }; }; electrobunEventEmitter = new ElectrobunEventEmitter; eventEmitter_default = electrobunEventEmitter; }); // node_modules/electrobun/dist/api/shared/rpc.ts function missingTransportMethodError(methods, action) { const methodsString = methods.map((m) => `"${m}"`).join(", "); return new Error(`This RPC instance cannot ${action} because the transport did not provide one or more of these methods: ${methodsString}`); } function createRPC(options = {}) { let debugHooks = {}; let transport = {}; let requestHandler = undefined; function setTransport(newTransport) { if (transport.unregisterHandler) transport.unregisterHandler(); transport = newTransport; transport.registerHandler?.(handler); } function setRequestHandler(h) { if (typeof h === "function") { requestHandler = h; return; } requestHandler = (method, params) => { const handlerFn = h[method]; if (handlerFn) return handlerFn(params); const fallbackHandler = h._; if (!fallbackHandler) throw new Error(`The requested method has no handler: ${String(method)}`); return fallbackHandler(method, params); }; } const { maxRequestTime = DEFAULT_MAX_REQUEST_TIME } = options; if (options.transport) setTransport(options.transport); if (options.requestHandler) setRequestHandler(options.requestHandler); if (options._debugHooks) debugHooks = options._debugHooks; let lastRequestId = 0; function getRequestId() { if (lastRequestId <= MAX_ID) return ++lastRequestId; return lastRequestId = 0; } const requestListeners = new Map; const requestTimeouts = new Map; function requestFn(method, ...args) { const params = args[0]; return new Promise((resolve, reject) => { if (!transport.send) throw missingTransportMethodError(["send"], "make requests"); const requestId = getRequestId(); const request2 = { type: "request", id: requestId, method, params }; requestListeners.set(requestId, { resolve, reject }); if (maxRequestTime !== Infinity) requestTimeouts.set(requestId, setTimeout(() => { requestTimeouts.delete(requestId); reject(new Error("RPC request timed out.")); }, maxRequestTime)); debugHooks.onSend?.(request2); transport.send(request2); }); } const request = new Proxy(requestFn, { get: (target, prop, receiver) => { if (prop in target) return Reflect.get(target, prop, receiver); return (params) => requestFn(prop, params); } }); const requestProxy = request; function sendFn(message, ...args) { const payload = args[0]; if (!transport.send) throw missingTransportMethodError(["send"], "send messages"); const rpcMessage = { type: "message", id: message, payload }; debugHooks.onSend?.(rpcMessage); transport.send(rpcMessage); } const send = new Proxy(sendFn, { get: (target, prop, receiver) => { if (prop in target) return Reflect.get(target, prop, receiver); return (payload) => sendFn(prop, payload); } }); const sendProxy = send; const messageListeners = new Map; const wildcardMessageListeners = new Set; function addMessageListener(message, listener) { if (!transport.registerHandler) throw missingTransportMethodError(["registerHandler"], "register message listeners"); if (message === "*") { wildcardMessageListeners.add(listener); return; } if (!messageListeners.has(message)) messageListeners.set(message, new Set); messageListeners.get(message).add(listener); } function removeMessageListener(message, listener) { if (message === "*") { wildcardMessageListeners.delete(listener); return; } messageListeners.get(message)?.delete(listener); if (messageListeners.get(message)?.size === 0) messageListeners.delete(message); } async function handler(message) { debugHooks.onReceive?.(message); if (!("type" in message)) throw new Error("Message does not contain a type."); if (message.type === "request") { if (!transport.send || !requestHandler) throw missingTransportMethodError(["send", "requestHandler"], "handle requests"); const { id, method, params } = message; let response; try { response = { type: "response", id, success: true, payload: await requestHandler(method, params) }; } catch (error) { if (!(error instanceof Error)) throw error; response = { type: "response", id, success: false, error: error.message }; } debugHooks.onSend?.(response); transport.send(response); return; } if (message.type === "response") { const timeout = requestTimeouts.get(message.id); if (timeout != null) clearTimeout(timeout); const { resolve, reject } = requestListeners.get(message.id) ?? {}; if (!message.success) reject?.(new Error(message.error)); else resolve?.(message.payload); return; } if (message.type === "message") { for (const listener of wildcardMessageListeners) listener(message.id, message.payload); const listeners = messageListeners.get(message.id); if (!listeners) return; for (const listener of listeners) listener(message.payload); return; } throw new Error(`Unexpected RPC message type: ${message.type}`); } const proxy = { send: sendProxy, request: requestProxy }; return { setTransport, setRequestHandler, request, requestProxy, send, sendProxy, addMessageListener, removeMessageListener, proxy }; } function defineElectrobunRPC(_side, config) { const rpcOptions = { maxRequestTime: config.maxRequestTime, requestHandler: { ...config.handlers.requests, ...config.extraRequestHandlers }, transport: { registerHandler: () => {} } }; const rpc = createRPC(rpcOptions); const messageHandlers = config.handlers.messages; if (messageHandlers) { rpc.addMessageListener("*", (messageName, payload) => { const globalHandler = messageHandlers["*"]; if (globalHandler) { globalHandler(messageName, payload); } const messageHandler = messageHandlers[messageName]; if (messageHandler) { messageHandler(payload); } }); } return rpc; } var MAX_ID = 10000000000, DEFAULT_MAX_REQUEST_TIME = 1000; // node_modules/electrobun/dist/api/shared/platform.ts import { platform, arch } from "os"; var platformName, archName, OS, ARCH; var init_platform = __esm(() => { platformName = platform(); archName = arch(); OS = (() => { switch (platformName) { case "win32": return "win"; case "darwin": return "macos"; case "linux": return "linux"; default: throw new Error(`Unsupported platform: ${platformName}`); } })(); ARCH = (() => { if (OS === "win") { return "x64"; } switch (archName) { case "arm64": return "arm64"; case "x64": return "x64"; default: throw new Error(`Unsupported architecture: ${archName}`); } })(); }); // node_modules/electrobun/dist/api/shared/naming.ts function sanitizeAppName(appName) { return appName.replace(/ /g, ""); } function getAppFileName(appName, buildEnvironment) { const sanitized = sanitizeAppName(appName); return buildEnvironment === "stable" ? sanitized : `${sanitized}-${buildEnvironment}`; } function getPlatformPrefix(buildEnvironment, os, arch2) { return `${buildEnvironment}-${os}-${arch2}`; } function getTarballFileName(appFileName, os) { return os === "macos" ? `${appFileName}.app.tar.zst` : `${appFileName}.tar.zst`; } // node_modules/electrobun/dist/api/bun/core/Utils.ts var exports_Utils = {}; __export(exports_Utils, { showNotification: () => showNotification, showMessageBox: () => showMessageBox, showItemInFolder: () => showItemInFolder, quit: () => quit, paths: () => paths, openPath: () => openPath, openFileDialog: () => openFileDialog, openExternal: () => openExternal, moveToTrash: () => moveToTrash, clipboardWriteText: () => clipboardWriteText, clipboardWriteImage: () => clipboardWriteImage, clipboardReadText: () => clipboardReadText, clipboardReadImage: () => clipboardReadImage, clipboardClear: () => clipboardClear, clipboardAvailableFormats: () => clipboardAvailableFormats }); import { homedir, tmpdir } from "os"; import { join } from "path"; import { readFileSync } from "fs"; function getLinuxXdgUserDirs() { try { const content = readFileSync(join(home, ".config", "user-dirs.dirs"), "utf-8"); const dirs = {}; for (const line of content.split(` `)) { const trimmed = line.trim(); if (trimmed.startsWith("#") || !trimmed.includes("=")) continue; const eqIdx = trimmed.indexOf("="); const key = trimmed.slice(0, eqIdx); let value = trimmed.slice(eqIdx + 1); if (value.startsWith('"') && value.endsWith('"')) { value = value.slice(1, -1); } value = value.replace(/\$HOME/g, home); dirs[key] = value; } return dirs; } catch { return {}; } } function xdgUserDir(key, fallbackName) { if (OS !== "linux") return ""; if (!_xdgUserDirs) _xdgUserDirs = getLinuxXdgUserDirs(); return _xdgUserDirs[key] || join(home, fallbackName); } function getVersionInfo() { if (_versionInfo) return _versionInfo; try { const resourcesDir = "Resources"; const raw = readFileSync(join("..", resourcesDir, "version.json"), "utf-8"); const parsed = JSON.parse(raw); _versionInfo = { identifier: parsed.identifier, channel: parsed.channel }; return _versionInfo; } catch (error) { console.error("Failed to read version.json", error); throw error; } } function getAppDataDir() { switch (OS) { case "macos": return join(home, "Library", "Application Support"); case "win": return process.env["LOCALAPPDATA"] || join(home, "AppData", "Local"); case "linux": return process.env["XDG_DATA_HOME"] || join(home, ".local", "share"); } } function getCacheDir() { switch (OS) { case "macos": return join(home, "Library", "Caches"); case "win": return process.env["LOCALAPPDATA"] || join(home, "AppData", "Local"); case "linux": return process.env["XDG_CACHE_HOME"] || join(home, ".cache"); } } function getLogsDir() { switch (OS) { case "macos": return join(home, "Library", "Logs"); case "win": return process.env["LOCALAPPDATA"] || join(home, "AppData", "Local"); case "linux": return process.env["XDG_STATE_HOME"] || join(home, ".local", "state"); } } function getConfigDir() { switch (OS) { case "macos": return join(home, "Library", "Application Support"); case "win": return process.env["APPDATA"] || join(home, "AppData", "Roaming"); case "linux": return process.env["XDG_CONFIG_HOME"] || join(home, ".config"); } } function getUserDir(macName, winName, xdgKey, fallbackName) { switch (OS) { case "macos": return join(home, macName); case "win": { const userProfile = process.env["USERPROFILE"] || home; return join(userProfile, winName); } case "linux": return xdgUserDir(xdgKey, fallbackName); } } var moveToTrash = (path) => { return ffi.request.moveToTrash({ path }); }, showItemInFolder = (path) => { return ffi.request.showItemInFolder({ path }); }, openExternal = (url) => { return ffi.request.openExternal({ url }); }, openPath = (path) => { return ffi.request.openPath({ path }); }, showNotification = (options) => { const { title, body, subtitle, silent } = options; ffi.request.showNotification({ title, body, subtitle, silent }); }, isQuitting = false, quit = () => { if (isQuitting) return; isQuitting = true; const beforeQuitEvent = electrobunEventEmitter.events.app.beforeQuit({}); electrobunEventEmitter.emitEvent(beforeQuitEvent); if (beforeQuitEvent.responseWasSet && beforeQuitEvent.response?.allow === false) { isQuitting = false; return; } native.symbols.stopEventLoop(); native.symbols.waitForShutdownComplete(5000); native.symbols.forceExit(0); }, openFileDialog = async (opts = {}) => { const optsWithDefault = { ...{ startingFolder: "~/", allowedFileTypes: "*", canChooseFiles: true, canChooseDirectory: true, allowsMultipleSelection: true }, ...opts }; const result = await ffi.request.openFileDialog({ startingFolder: optsWithDefault.startingFolder, allowedFileTypes: optsWithDefault.allowedFileTypes, canChooseFiles: optsWithDefault.canChooseFiles, canChooseDirectory: optsWithDefault.canChooseDirectory, allowsMultipleSelection: optsWithDefault.allowsMultipleSelection }); const filePaths = result.split(","); return filePaths; }, showMessageBox = async (opts = {}) => { const { type = "info", title = "", message = "", detail = "", buttons = ["OK"], defaultId = 0, cancelId = -1 } = opts; const response = ffi.request.showMessageBox({ type, title, message, detail, buttons, defaultId, cancelId }); return { response }; }, clipboardReadText = () => { return ffi.request.clipboardReadText(); }, clipboardWriteText = (text) => { ffi.request.clipboardWriteText({ text }); }, clipboardReadImage = () => { return ffi.request.clipboardReadImage(); }, clipboardWriteImage = (pngData) => { ffi.request.clipboardWriteImage({ pngData }); }, clipboardClear = () => { ffi.request.clipboardClear(); }, clipboardAvailableFormats = () => { return ffi.request.clipboardAvailableFormats(); }, home, _xdgUserDirs, _versionInfo, paths; var init_Utils = __esm(async () => { init_eventEmitter(); init_platform(); await init_native(); process.exit = (code) => { if (isQuitting) { native.symbols.forceExit(code ?? 0); return; } quit(); }; home = homedir(); paths = { get home() { return home; }, get appData() { return getAppDataDir(); }, get config() { return getConfigDir(); }, get cache() { return getCacheDir(); }, get temp() { return tmpdir(); }, get logs() { return getLogsDir(); }, get documents() { return getUserDir("Documents", "Documents", "XDG_DOCUMENTS_DIR", "Documents"); }, get downloads() { return getUserDir("Downloads", "Downloads", "XDG_DOWNLOAD_DIR", "Downloads"); }, get desktop() { return getUserDir("Desktop", "Desktop", "XDG_DESKTOP_DIR", "Desktop"); }, get pictures() { return getUserDir("Pictures", "Pictures", "XDG_PICTURES_DIR", "Pictures"); }, get music() { return getUserDir("Music", "Music", "XDG_MUSIC_DIR", "Music"); }, get videos() { return getUserDir("Movies", "Videos", "XDG_VIDEOS_DIR", "Videos"); }, get userData() { const { identifier, channel } = getVersionInfo(); return join(getAppDataDir(), identifier, channel); }, get userCache() { const { identifier, channel } = getVersionInfo(); return join(getCacheDir(), identifier, channel); }, get userLogs() { const { identifier, channel } = getVersionInfo(); return join(getLogsDir(), identifier, channel); } }; }); // node_modules/electrobun/dist/api/bun/core/Updater.ts import { join as join2, dirname, resolve } from "path"; import { homedir as homedir2 } from "os"; import { renameSync, unlinkSync, mkdirSync, rmdirSync, statSync, readdirSync } from "fs"; import { execSync } from "child_process"; function emitStatus(status, message, details) { const entry = { status, message, timestamp: Date.now(), details }; statusHistory.push(entry); if (onStatusChangeCallback) { onStatusChangeCallback(entry); } } function getAppDataDir2() { switch (OS) { case "macos": return join2(homedir2(), "Library", "Application Support"); case "win": return process.env["LOCALAPPDATA"] || join2(homedir2(), "AppData", "Local"); case "linux": return process.env["XDG_DATA_HOME"] || join2(homedir2(), ".local", "share"); default: return join2(homedir2(), ".config"); } } function cleanupExtractionFolder(extractionFolder, keepTarHash) { const keepFile = `${keepTarHash}.tar`; try { const entries = readdirSync(extractionFolder); for (const entry of entries) { if (entry === keepFile) continue; const fullPath = join2(extractionFolder, entry); try { const s = statSync(fullPath); if (s.isDirectory()) { rmdirSync(fullPath, { recursive: true }); } else { unlinkSync(fullPath); } } catch (e) {} } } catch (e) {} } var statusHistory, onStatusChangeCallback = null, localInfo, updateInfo, Updater; var init_Updater = __esm(async () => { init_platform(); await init_Utils(); statusHistory = []; Updater = { updateInfo: () => { return updateInfo; }, getStatusHistory: () => { return [...statusHistory]; }, clearStatusHistory: () => { statusHistory.length = 0; }, onStatusChange: (callback) => { onStatusChangeCallback = callback; }, checkForUpdate: async () => { emitStatus("checking", "Checking for updates..."); const localInfo2 = await Updater.getLocallocalInfo(); if (localInfo2.channel === "dev") { emitStatus("no-update", "Dev channel - updates disabled", { currentHash: localInfo2.hash }); return { version: localInfo2.version, hash: localInfo2.hash, updateAvailable: false, updateReady: false, error: "" }; } const cacheBuster = Math.random().toString(36).substring(7); const platformPrefix = getPlatformPrefix(localInfo2.channel, OS, ARCH); const updateInfoUrl = `${localInfo2.baseUrl.replace(/\/+$/, "")}/${platformPrefix}-update.json?${cacheBuster}`; try { const updateInfoResponse = await fetch(updateInfoUrl); if (updateInfoResponse.ok) { const responseText = await updateInfoResponse.text(); try { updateInfo = JSON.parse(responseText); } catch { emitStatus("error", "Invalid update.json: failed to parse JSON", { url: updateInfoUrl }); return { version: "", hash: "", updateAvailable: false, updateReady: false, error: `Invalid update.json: failed to parse JSON` }; } if (!updateInfo.hash) { emitStatus("error", "Invalid update.json: missing hash", { url: updateInfoUrl }); return { version: "", hash: "", updateAvailable: false, updateReady: false, error: `Invalid update.json: missing hash` }; } if (updateInfo.hash !== localInfo2.hash) { updateInfo.updateAvailable = true; emitStatus("update-available", `Update available: ${localInfo2.hash.slice(0, 8)} \u2192 ${updateInfo.hash.slice(0, 8)}`, { currentHash: localInfo2.hash, latestHash: updateInfo.hash }); } else { emitStatus("no-update", "Already on latest version", { currentHash: localInfo2.hash }); } } else { emitStatus("error", `Failed to fetch update info (HTTP ${updateInfoResponse.status})`, { url: updateInfoUrl }); return { version: "", hash: "", updateAvailable: false, updateReady: false, error: `Failed to fetch update info from ${updateInfoUrl}` }; } } catch (error) { return { version: "", hash: "", updateAvailable: false, updateReady: false, error: `Failed to fetch update info from ${updateInfoUrl}` }; } return updateInfo; }, downloadUpdate: async () => { emitStatus("download-starting", "Starting update download..."); const appDataFolder = await Updater.appDataFolder(); await Updater.channelBucketUrl(); const appFileName = localInfo.name; let currentHash = (await Updater.getLocallocalInfo()).hash; let latestHash = (await Updater.checkForUpdate()).hash; const extractionFolder = join2(appDataFolder, "self-extraction"); if (!await Bun.file(extractionFolder).exists()) { mkdirSync(extractionFolder, { recursive: true }); } let currentTarPath = join2(extractionFolder, `${currentHash}.tar`); const latestTarPath = join2(extractionFolder, `${latestHash}.tar`); const seenHashes = []; let patchesApplied = 0; let usedPatchPath = false; if (!await Bun.file(latestTarPath).exists()) { emitStatus("checking-local-tar", `Checking for local tar file: ${currentHash.slice(0, 8)}`, { currentHash }); while (currentHash !== latestHash) { seenHashes.push(currentHash); const currentTar = Bun.file(currentTarPath); if (!await currentTar.exists()) { emitStatus("local-tar-missing", `Local tar not found for ${currentHash.slice(0, 8)}, will download full bundle`, { currentHash }); break; } emitStatus("local-tar-found", `Found local tar for ${currentHash.slice(0, 8)}`, { currentHash }); const platformPrefix = getPlatformPrefix(localInfo.channel, OS, ARCH); const patchUrl = `${localInfo.baseUrl.replace(/\/+$/, "")}/${platformPrefix}-${currentHash}.patch`; emitStatus("fetching-patch", `Checking for patch: ${currentHash.slice(0, 8)}`, { currentHash, url: patchUrl }); const patchResponse = await fetch(patchUrl); if (!patchResponse.ok) { emitStatus("patch-not-found", `No patch available for ${currentHash.slice(0, 8)}, will download full bundle`, { currentHash }); break; } emitStatus("patch-found", `Patch found for ${currentHash.slice(0, 8)}`, { currentHash }); emitStatus("downloading-patch", `Downloading patch for ${currentHash.slice(0, 8)}...`, { currentHash }); const patchFilePath = join2(appDataFolder, "self-extraction", `${currentHash}.patch`); await Bun.write(patchFilePath, await patchResponse.arrayBuffer()); const tmpPatchedTarFilePath = join2(appDataFolder, "self-extraction", `from-${currentHash}.tar`); const bunBinDir = dirname(process.execPath); const bspatchBinName = OS === "win" ? "bspatch.exe" : "bspatch"; const bspatchPath = join2(bunBinDir, bspatchBinName); emitStatus("applying-patch", `Applying patch ${patchesApplied + 1} for ${currentHash.slice(0, 8)}...`, { currentHash, patchNumber: patchesApplied + 1 }); if (!statSync(bspatchPath, { throwIfNoEntry: false })) { emitStatus("patch-failed", `bspatch binary not found at ${bspatchPath}`, { currentHash, errorMessage: `bspatch not found: ${bspatchPath}` }); console.error("bspatch not found:", bspatchPath); break; } if (!statSync(currentTarPath, { throwIfNoEntry: false })) { emitStatus("patch-failed", `Old tar not found at ${currentTarPath}`, { currentHash, errorMessage: `old tar not found: ${currentTarPath}` }); console.error("old tar not found:", currentTarPath); break; } if (!statSync(patchFilePath, { throwIfNoEntry: false })) { emitStatus("patch-failed", `Patch file not found at ${patchFilePath}`, { currentHash, errorMessage: `patch not found: ${patchFilePath}` }); console.error("patch file not found:", patchFilePath); break; } try { const patchResult = Bun.spawnSync([ bspatchPath, currentTarPath, tmpPatchedTarFilePath, patchFilePath ]); if (patchResult.exitCode !== 0 || patchResult.success === false) { const stderr = patchResult.stderr ? patchResult.stderr.toString() : ""; const stdout = patchResult.stdout ? patchResult.stdout.toString() : ""; if (updateInfo) { updateInfo.error = stderr || `bspatch failed with exit code ${patchResult.exitCode}`; } emitStatus("patch-failed", `Patch application failed: ${stderr || `exit code ${patchResult.exitCode}`}`, { currentHash, errorMessage: stderr || `exit code ${patchResult.exitCode}` }); console.error("bspatch failed", { exitCode: patchResult.exitCode, stdout, stderr, bspatchPath, oldTar: currentTarPath, newTar: tmpPatchedTarFilePath, patch: patchFilePath }); break; } } catch (error) { emitStatus("patch-failed", `Patch threw exception: ${error.message}`, { currentHash, errorMessage: error.message }); console.error("bspatch threw", error, { bspatchPath }); break; } patchesApplied++; emitStatus("patch-applied", `Patch ${patchesApplied} applied successfully`, { currentHash, patchNumber: patchesApplied }); emitStatus("extracting-version", "Extracting version info from patched tar...", { currentHash }); let hashFilePath = ""; const resourcesDir = "Resources"; const patchedTarBytes = await Bun.file(tmpPatchedTarFilePath).arrayBuffer(); const patchedArchive = new Bun.Archive(patchedTarBytes); const patchedFiles = await patchedArchive.files(); for (const [filePath] of patchedFiles) { if (filePath.endsWith(`${resourcesDir}/version.json`) || filePath.endsWith("metadata.json")) { hashFilePath = filePath; break; } } if (!hashFilePath) { emitStatus("error", "Could not find version/metadata file in patched tar", { currentHash }); console.error("Neither Resources/version.json nor metadata.json found in patched tar:", tmpPatchedTarFilePath); break; } const hashFile = patchedFiles.get(hashFilePath); const hashFileJson = JSON.parse(await hashFile.text()); const nextHash = hashFileJson.hash; if (seenHashes.includes(nextHash)) { emitStatus("error", "Cyclical update detected, falling back to full download", { currentHash: nextHash }); console.log("Warning: cyclical update detected"); break; } seenHashes.push(nextHash); if (!nextHash) { emitStatus("error", "Could not determine next hash from patched tar", { currentHash }); break; } const updatedTarPath = join2(appDataFolder, "self-extraction", `${nextHash}.tar`); renameSync(tmpPatchedTarFilePath, updatedTarPath); unlinkSync(currentTarPath); unlinkSync(patchFilePath); currentHash = nextHash; currentTarPath = join2(appDataFolder, "self-extraction", `${currentHash}.tar`); emitStatus("patch-applied", `Patched to ${nextHash.slice(0, 8)}, checking for more patches...`, { currentHash: nextHash, toHash: latestHash, totalPatchesApplied: patchesApplied }); } if (currentHash === latestHash && patchesApplied > 0) { usedPatchPath = true; emitStatus("patch-chain-complete", `Patch chain complete! Applied ${patchesApplied} patches`, { totalPatchesApplied: patchesApplied, currentHash: latestHash, usedPatchPath: true }); } if (currentHash !== latestHash) { emitStatus("downloading-full-bundle", "Downloading full update bundle...", { currentHash, latestHash, usedPatchPath: false }); const cacheBuster = Math.random().toString(36).substring(7); const platformPrefix = getPlatformPrefix(localInfo.channel, OS, ARCH); const tarballName = getTarballFileName(appFileName, OS); const urlToLatestTarball = `${localInfo.baseUrl.replace(/\/+$/, "")}/${platformPrefix}-${tarballName}`; const prevVersionCompressedTarballPath = join2(appDataFolder, "self-extraction", "latest.tar.zst"); emitStatus("download-progress", `Fetching ${tarballName}...`, { url: urlToLatestTarball }); const response = await fetch(urlToLatestTarball + `?${cacheBuster}`); if (response.ok && response.body) { const contentLength = response.headers.get("content-length"); const totalBytes = contentLength ? parseInt(contentLength, 10) : undefined; let bytesDownloaded = 0; const reader = response.body.getReader(); const writer = Bun.file(prevVersionCompressedTarballPath).writer(); while (true) { const { done, value } = await reader.read(); if (done) break; await writer.write(value); bytesDownloaded += value.length; if (bytesDownloaded % 500000 < value.length) { emitStatus("download-progress", `Downloading: ${(bytesDownloaded / 1024 / 1024).toFixed(1)} MB`, { bytesDownloaded, totalBytes, progress: totalBytes ? Math.round(bytesDownloaded / totalBytes * 100) : undefined }); } } await writer.flush(); writer.end(); emitStatus("download-progress", `Download complete: ${(bytesDownloaded / 1024 / 1024).toFixed(1)} MB`, { bytesDownloaded, totalBytes, progress: 100 }); } else { emitStatus("error", `Failed to download: ${urlToLatestTarball}`, { url: urlToLatestTarball }); console.log("latest version not found at: ", urlToLatestTarball); } emitStatus("decompressing", "Decompressing update bundle..."); const bunBinDir = dirname(process.execPath); const zstdBinName = OS === "win" ? "zig-zstd.exe" : "zig-zstd"; const zstdPath = join2(bunBinDir, zstdBinName); if (!statSync(zstdPath, { throwIfNoEntry: false })) { updateInfo.error = `zig-zstd not found: ${zstdPath}`; emitStatus("error", updateInfo.error, { zstdPath }); console.error("zig-zstd not found:", zstdPath); } else { const decompressResult = Bun.spawnSync([ zstdPath, "decompress", "-i", prevVersionCompressedTarballPath, "-o", latestTarPath, "--no-timing" ], { cwd: extractionFolder, stdout: "inherit", stderr: "inherit" }); if (!decompressResult.success) { updateInfo.error = `zig-zstd failed with exit code ${decompressResult.exitCode}`; emitStatus("error", updateInfo.error, { zstdPath, exitCode: decompressResult.exitCode }); console.error("zig-zstd failed", { exitCode: decompressResult.exitCode, zstdPath }); } else { emitStatus("decompressing", "Decompression complete"); } } unlinkSync(prevVersionCompressedTarballPath); } } if (await Bun.file(latestTarPath).exists()) { updateInfo.updateReady = true; emitStatus("download-complete", `Update ready to install (used ${usedPatchPath ? "patch" : "full download"} path)`, { latestHash, usedPatchPath, totalPatchesApplied: patchesApplied }); } else { updateInfo.error = "Failed to download latest version"; emitStatus("error", "Failed to download latest version", { latestHash }); } cleanupExtractionFolder(extractionFolder, latestHash); }, applyUpdate: async () => { if (updateInfo?.updateReady) { emitStatus("applying", "Starting update installation..."); const appDataFolder = await Updater.appDataFolder(); const extractionFolder = join2(appDataFolder, "self-extraction"); if (!await Bun.file(extractionFolder).exists()) { mkdirSync(extractionFolder, { recursive: true }); } let latestHash = (await Updater.checkForUpdate()).hash; const latestTarPath = join2(extractionFolder, `${latestHash}.tar`); let appBundleSubpath = ""; if (await Bun.file(latestTarPath).exists()) { emitStatus("extracting", `Extracting update to ${latestHash.slice(0, 8)}...`, { latestHash }); const extractionDir = OS === "win" ? join2(extractionFolder, `temp-${latestHash}`) : extractionFolder; if (OS === "win") { mkdirSync(extractionDir, { recursive: true }); } const latestTarBytes = await Bun.file(latestTarPath).arrayBuffer(); const latestArchive = new Bun.Archive(latestTarBytes); await latestArchive.extract(extractionDir); if (OS === "macos") { const extractedFiles = readdirSync(extractionDir); for (const file of extractedFiles) { if (file.endsWith(".app")) { appBundleSubpath = file + "/"; break; } } } else { appBundleSubpath = "./"; } console.log(`Tar extraction completed. Found appBundleSubpath: ${appBundleSubpath}`); if (!appBundleSubpath) { console.error("Failed to find app in tarball"); return; } const extractedAppPath = resolve(join2(extractionDir, appBundleSubpath)); let newAppBundlePath; if (OS === "linux") { const extractedFiles = readdirSync(extractionDir); const appBundleDir = extractedFiles.find((file) => { const filePath = join2(extractionDir, file); return statSync(filePath).isDirectory() && !file.endsWith(".tar"); }); if (!appBundleDir) { console.error("Could not find app bundle directory in extraction"); return; } newAppBundlePath = join2(extractionDir, appBundleDir); const bundleStats = statSync(newAppBundlePath, { throwIfNoEntry: false }); if (!bundleStats || !bundleStats.isDirectory()) { console.error(`App bundle directory not found at: ${newAppBundlePath}`); console.log("Contents of extraction directory:"); try { const files = readdirSync(extractionDir); for (const file of files) { console.log(` - ${file}`); const subPath = join2(extractionDir, file); if (statSync(subPath).isDirectory()) { const subFiles = readdirSync(subPath); for (const subFile of subFiles) { console.log(` - ${subFile}`); } } } } catch (e) { console.log("Could not list directory contents:", e); } return; } } else if (OS === "win") { const appBundleName = getAppFileName(localInfo.name, localInfo.channel); newAppBundlePath = join2(extractionDir, appBundleName); if (!statSync(newAppBundlePath, { throwIfNoEntry: false })) { console.error(`Extracted app not found at: ${newAppBundlePath}`); console.log("Contents of extraction directory:"); try { const files = readdirSync(extractionDir); for (const file of files) { console.log(` - ${file}`); } } catch (e) { console.log("Could not list directory contents:", e); } return; } } else { newAppBundlePath = extractedAppPath; } let runningAppBundlePath; const appDataFolder2 = await Updater.appDataFolder(); if (OS === "macos") { runningAppBundlePath = resolve(dirname(process.execPath), "..", ".."); } else if (OS === "linux" || OS === "win") { runningAppBundlePath = join2(appDataFolder2, "app"); } else { throw new Error(`Unsupported platform: ${OS}`); } try { emitStatus("replacing-app", "Removing old version..."); if (OS === "macos") { if (statSync(runningAppBundlePath, { throwIfNoEntry: false })) { rmdirSync(runningAppBundlePath, { recursive: true }); } emitStatus("replacing-app", "Installing new version..."); renameSync(newAppBundlePath, runningAppBundlePath); try { execSync(`xattr -r -d com.apple.quarantine "${runningAppBundlePath}"`, { stdio: "ignore" }); } catch (e) {} } else if (OS === "linux") { const appBundleDir = join2(appDataFolder2, "app"); if (statSync(appBundleDir, { throwIfNoEntry: false })) { rmdirSync(appBundleDir, { recursive: true }); } renameSync(newAppBundlePath, appBundleDir); const launcherPath = join2(appBundleDir, "bin", "launcher"); if (statSync(launcherPath, { throwIfNoEntry: false })) { execSync(`chmod +x "${launcherPath}"`); } const bunPath = join2(appBundleDir, "bin", "bun"); if (statSync(bunPath, { throwIfNoEntry: false })) { execSync(`chmod +x "${bunPath}"`); } } if (OS !== "win") { cleanupExtractionFolder(extractionFolder, latestHash); } if (OS === "win") { const parentDir = dirname(runningAppBundlePath); const updateScriptPath = join2(parentDir, "update.bat"); const launcherPath = join2(runningAppBundlePath, "bin", "launcher.exe"); const runningAppWin = runningAppBundlePath.replace(/\//g, "\\"); const newAppWin = newAppBundlePath.replace(/\//g, "\\"); const extractionDirWin = extractionDir.replace(/\//g, "\\"); const launcherPathWin = launcherPath.replace(/\//g, "\\"); const updateScript = `@echo off setlocal :: Wait for the app to fully exit (check if launcher.exe is still running) :waitloop tasklist /FI "IMAGENAME eq launcher.exe" 2>NUL | find /I /N "launcher.exe">NUL if "%ERRORLEVEL%"=="0" ( timeout /t 1 /nobreak >nul goto waitloop ) :: Small extra delay to ensure all file handles are released timeout /t 2 /nobreak >nul :: Remove current app folder if exist "${runningAppWin}" ( rmdir /s /q "${runningAppWin}" ) :: Move new app to current location move "${newAppWin}" "${runningAppWin}" :: Clean up extraction directory rmdir /s /q "${extractionDirWin}" 2>nul :: Launch the new app start "" "${launcherPathWin}" :: Clean up scheduled tasks starting with ElectrobunUpdate_ for /f "tokens=1" %%t in ('schtasks /query /fo list ^| findstr /i "ElectrobunUpdate_"') do ( schtasks /delete /tn "%%t" /f >nul 2>&1 ) :: Delete this update script after a short delay ping -n 2 127.0.0.1 >nul del "%~f0" `; await Bun.write(updateScriptPath, updateScript); const scriptPathWin = updateScriptPath.replace(/\//g, "\\"); const taskName = `ElectrobunUpdate_${Date.now()}`; execSync(`schtasks /create /tn "${taskName}" /tr "cmd /c \\"${scriptPathWin}\\"" /sc once /st 00:00 /f`, { stdio: "ignore" }); execSync(`schtasks /run /tn "${taskName}"`, { stdio: "ignore" }); quit(); } } catch (error) { emitStatus("error", `Failed to replace app: ${error.message}`, { errorMessage: error.message }); console.error("Failed to replace app with new version", error); return; } emitStatus("launching-new-version", "Launching updated version..."); if (OS === "macos") { const pid = process.pid; Bun.spawn([ "sh", "-c", `while kill -0 ${pid} 2>/dev/null; do sleep 0.5; done; sleep 1; open "${runningAppBundlePath}"` ], { detached: true, stdio: ["ignore", "ignore", "ignore"] }); } else if (OS === "linux") { const launcherPath = join2(runningAppBundlePath, "bin", "launcher"); Bun.spawn(["sh", "-c", `"${launcherPath}" &`], { detached: true }); } emitStatus("complete", "Update complete, restarting application..."); quit(); } } }, channelBucketUrl: async () => { await Updater.getLocallocalInfo(); return localInfo.baseUrl; }, appDataFolder: async () => { await Updater.getLocallocalInfo(); const appDataFolder = join2(getAppDataDir2(), localInfo.identifier, localInfo.channel); return appDataFolder; }, localInfo: { version: async () => { return (await Updater.getLocallocalInfo()).version; }, hash: async () => { return (await Updater.getLocallocalInfo()).hash; }, channel: async () => { return (await Updater.getLocallocalInfo()).channel; }, baseUrl: async () => { return (await Updater.getLocallocalInfo()).baseUrl; } }, getLocallocalInfo: async () => { if (localInfo) { return localInfo; } try { const resourcesDir = "Resources"; localInfo = await Bun.file(`../${resourcesDir}/version.json`).json(); return localInfo; } catch (error) { console.error("Failed to read version.json", error); throw error; } } }; }); // node_modules/electrobun/dist/api/bun/core/BuildConfig.ts var buildConfig = null, BuildConfig; var init_BuildConfig = __esm(() => { BuildConfig = { get: async () => { if (buildConfig) { return buildConfig; } try { const resourcesDir = "Resources"; buildConfig = await Bun.file(`../${resourcesDir}/build.json`).json(); return buildConfig; } catch (error) { buildConfig = { defaultRenderer: "native", availableRenderers: ["native"] }; return buildConfig; } }, getCached: () => buildConfig }; }); // node_modules/electrobun/dist/api/bun/core/Socket.ts var exports_Socket = {}; __export(exports_Socket, { socketMap: () => socketMap, sendMessageToWebviewViaSocket: () => sendMessageToWebviewViaSocket, rpcServer: () => rpcServer, rpcPort: () => rpcPort }); import { createCipheriv, createDecipheriv, randomBytes } from "crypto"; function base64ToUint8Array(base64) { { return new Uint8Array(atob(base64).split("").map((char) => char.charCodeAt(0))); } } function encrypt(secretKey, text) { const iv = new Uint8Array(randomBytes(12)); const cipher = createCipheriv("aes-256-gcm", secretKey, iv); const encrypted = Buffer.concat([ new Uint8Array(cipher.update(text, "utf8")), new Uint8Array(cipher.final()) ]).toString("base64"); const tag = cipher.getAuthTag().toString("base64"); return { encrypted, iv: Buffer.from(iv).toString("base64"), tag }; } function decrypt(secretKey, encryptedData, iv, tag) { const decipher = createDecipheriv("aes-256-gcm", secretKey, iv); decipher.setAuthTag(tag); const decrypted = Buffer.concat([ new Uint8Array(decipher.update(encryptedData)), new Uint8Array(decipher.final()) ]); return decrypted.toString("utf8"); } var socketMap, startRPCServer = () => { const startPort = 50000; const endPort = 65535; const payloadLimit = 1024 * 1024 * 500; let port = startPort; let server = null; while (port <= endPort) { try { server = Bun.serve({ port, fetch(req, server2) { const url = new URL(req.url); if (url.pathname === "/socket") { const webviewIdString = url.searchParams.get("webviewId"); if (!webviewIdString) { return new Response("Missing webviewId", { status: 400 }); } const webviewId = parseInt(webviewIdString, 10); const success = server2.upgrade(req, { data: { webviewId } }); return success ? undefined : new Response("Upgrade failed", { status: 500 }); } console.log("unhandled RPC Server request", req.url); }, websocket: { idleTimeout: 960, maxPayloadLength: payloadLimit, backpressureLimit: payloadLimit * 2, open(ws) { if (!ws?.data) { return; } const { webviewId } = ws.data; if (!socketMap[webviewId]) { socketMap[webviewId] = { socket: ws, queue: [] }; } else { socketMap[webviewId].socket = ws; } }, close(ws, _code, _reason) { if (!ws?.data) { return; } const { webviewId } = ws.data; if (socketMap[webviewId]) { socketMap[webviewId].socket = null; } }, message(ws, message) { if (!ws?.data) { return; } const { webviewId } = ws.data; const browserView = BrowserView.getById(webviewId); if (!browserView) { return; } if (browserView.rpcHandler) { if (typeof message === "string") { try { const encryptedPacket = JSON.parse(message); const decrypted = decrypt(browserView.secretKey, base64ToUint8Array(encryptedPacket.encryptedData), base64ToUint8Array(encryptedPacket.iv), base64ToUint8Array(encryptedPacket.tag)); browserView.rpcHandler(JSON.parse(decrypted)); } catch (error) { console.log("Error handling message:", error); } } else if (message instanceof ArrayBuffer) { console.log("TODO: Received ArrayBuffer message:", message); } } } } }); break; } catch (error) { if (error.code === "EADDRINUSE") { console.log(`Port ${port} in use, trying next port...`); port++; } else { throw error; } } } return { rpcServer: server, rpcPort: port }; }, rpcServer, rpcPort, sendMessageToWebviewViaSocket = (webviewId, message) => { const rpc = socketMap[webviewId]; const browserView = BrowserView.getById(webviewId); if (!browserView) return false; if (rpc?.socket?.readyState === WebSocket.OPEN) { try { const unencryptedString = JSON.stringify(message); const encrypted = encrypt(browserView.secretKey, unencryptedString); const encryptedPacket = { encryptedData: encrypted.encrypted, iv: encrypted.iv, tag: encrypted.tag }; const encryptedPacketString = JSON.stringify(encryptedPacket); rpc.socket.send(encryptedPacketString); return true; } catch (error) { console.error("Error sending message to webview via socket:", error); } } return false; }; var init_Socket = __esm(async () => { await init_BrowserView(); socketMap = {}; ({ rpcServer, rpcPort } = startRPCServer()); console.log("Server started at", rpcServer?.url.origin); }); // node_modules/electrobun/dist/api/bun/core/BrowserView.ts import { randomBytes as randomBytes2 } from "crypto"; class BrowserView { id = nextWebviewId++; ptr; hostWebviewId; windowId; renderer; url = null; html = null; preload = null; partition = null; autoResize = true; frame = { x: 0, y: 0, width: 800, height: 600 }; pipePrefix; inStream; outStream; secretKey; rpc; rpcHandler; navigationRules = null; sandbox = false; startTransparent = false; startPassthrough = false; constructor(options = defaultOptions) { this.url = options.url || defaultOptions.url || null; this.html = options.html || defaultOptions.html || null; this.preload = options.preload || defaultOptions.preload || null; this.frame = { x: options.frame?.x ?? defaultOptions.frame.x, y: options.frame?.y ?? defaultOptions.frame.y, width: options.frame?.width ?? defaultOptions.frame.width, height: options.frame?.height ?? defaultOptions.frame.height }; this.rpc = options.rpc; this.secretKey = new Uint8Array(randomBytes2(32)); this.partition = options.partition || null; this.pipePrefix = `/private/tmp/electrobun_ipc_pipe_${hash}_${randomId}_${this.id}`; this.hostWebviewId = options.hostWebviewId; this.windowId = options.windowId ?? 0; this.autoResize = options.autoResize === false ? false : true; this.navigationRules = options.navigationRules || null; this.renderer = options.renderer ?? defaultOptions.renderer ?? "native"; this.sandbox = options.sandbox ?? false; this.startTransparent = options.startTransparent ?? false; this.startPassthrough = options.startPassthrough ?? false; BrowserViewMap[this.id] = this; this.ptr = this.init(); if (this.html) { console.log(`DEBUG: BrowserView constructor triggering loadHTML for webview ${this.id}`); setTimeout(() => { console.log(`DEBUG: BrowserView delayed loadHTML for webview ${this.id}`); this.loadHTML(this.html); }, 100); } else { console.log(`DEBUG: BrowserView constructor - no HTML provided for webview ${this.id}`); } } init() { this.createStreams(); return ffi.request.createWebview({ id: this.id, windowId: this.windowId, renderer: this.renderer, rpcPort, secretKey: this.secretKey.toString(), hostWebviewId: this.hostWebviewId || null, pipePrefix: this.pipePrefix, partition: this.partition, url: this.html ? null : this.url, html: this.html, preload: this.preload, frame: { width: this.frame.width, height: this.frame.height, x: this.frame.x, y: this.frame.y }, autoResize: this.autoResize, navigationRules: this.navigationRules, sandbox: this.sandbox, startTransparent: this.startTransparent, startPassthrough: this.startPassthrough }); } createStreams() { if (!this.rpc) { this.rpc = BrowserView.defineRPC({ handlers: { requests: {}, messages: {} } }); } this.rpc.setTransport(this.createTransport()); } sendMessageToWebviewViaExecute(jsonMessage) { const stringifiedMessage = typeof jsonMessage === "string" ? jsonMessage : JSON.stringify(jsonMessage); const wrappedMessage = `window.__electrobun.receiveMessageFromBun(${stringifiedMessage})`; this.executeJavascript(wrappedMessage); } sendInternalMessageViaExecute(jsonMessage) { const stringifiedMessage = typeof jsonMessage === "string" ? jsonMessage : JSON.stringify(jsonMessage); const wrappedMessage = `window.__electrobun.receiveInternalMessageFromBun(${stringifiedMessage})`; this.executeJavascript(wrappedMessage); } executeJavascript(js) { ffi.request.evaluateJavascriptWithNoCompletion({ id: this.id, js }); } loadURL(url) { console.log(`DEBUG: loadURL called for webview ${this.id}: ${url}`); this.url = url; native.symbols.loadURLInWebView(this.ptr, toCString(this.url)); } loadHTML(html) { this.html = html; console.log(`DEBUG: Setting HTML content for webview ${this.id}:`, html.substring(0, 50) + "..."); if (this.renderer === "cef") { native.symbols.setWebviewHTMLContent(this.id, toCString(html)); this.loadURL("views://internal/index.html"); } else { native.symbols.loadHTMLInWebView(this.ptr, toCString(html)); } } setNavigationRules(rules) { this.navigationRules = JSON.stringify(rules); const rulesJson = JSON.stringify(rules); native.symbols.setWebviewNavigationRules(this.ptr, toCString(rulesJson)); } findInPage(searchText, options) { const forward = options?.forward ?? true; const matchCase = options?.matchCase ?? false; native.symbols.webviewFindInPage(this.ptr, toCString(searchText), forward, matchCase); } stopFindInPage() { native.symbols.webviewStopFind(this.ptr); } openDevTools() { native.symbols.webviewOpenDevTools(this.ptr); } closeDevTools() { native.symbols.webviewCloseDevTools(this.ptr); } toggleDevTools() { native.symbols.webviewToggleDevTools(this.ptr); } on(name, handler) { const specificName = `${name}-${this.id}`; eventEmitter_default.on(specificName, handler); } createTransport = () => { const that = this; return { send(message) { const sentOverSocket = sendMessageToWebviewViaSocket(that.id, message); if (!sentOverSocket) { try { const messageString = JSON.stringify(message); that.sendMessageToWebviewViaExecute(messageString); } catch (error) { console.error("bun: failed to serialize message to webview", error); } } }, registerHandler(handler) { that.rpcHandler = handler; } }; }; remove() { native.symbols.webviewRemove(this.ptr); delete BrowserViewMap[this.id]; } static getById(id) { return BrowserViewMap[id]; } static getAll() { return Object.values(BrowserViewMap); } static defineRPC(config) { return defineElectrobunRPC("bun", config); } } var BrowserViewMap, nextWebviewId = 1, hash, buildConfig2, defaultOptions, randomId; var init_BrowserView = __esm(async () => { init_eventEmitter(); init_BuildConfig(); await __promiseAll([ init_native(), init_Updater(), init_Socket() ]); BrowserViewMap = {}; hash = await Updater.localInfo.hash(); buildConfig2 = await BuildConfig.get(); defaultOptions = { url: null, html: null, preload: null, renderer: buildConfig2.defaultRenderer, frame: { x: 0, y: 0, width: 800, height: 600 } }; randomId = Math.random().toString(36).substring(7); }); // node_modules/electrobun/dist/api/bun/core/Paths.ts var exports_Paths = {}; __export(exports_Paths, { VIEWS_FOLDER: () => VIEWS_FOLDER }); import { resolve as resolve2 } from "path"; var RESOURCES_FOLDER, VIEWS_FOLDER; var init_Paths = __esm(() => { RESOURCES_FOLDER = resolve2("../Resources/"); VIEWS_FOLDER = resolve2(RESOURCES_FOLDER, "app/views"); }); // node_modules/electrobun/dist/api/bun/core/Tray.ts import { join as join3 } from "path"; class Tray { id = nextTrayId++; ptr = null; constructor({ title = "", image = "", template = true, width = 16, height = 16 } = {}) { try { this.ptr = ffi.request.createTray({ id: this.id, title, image: this.resolveImagePath(image), template, width, height }); } catch (error) { console.warn("Tray creation failed:", error); console.warn("System tray functionality may not be available on this platform"); this.ptr = null; } TrayMap[this.id] = this; } resolveImagePath(imgPath) { if (imgPath.startsWith("views://")) { return join3(VIEWS_FOLDER, imgPath.replace("views://", "")); } else { return imgPath; } } setTitle(title) { if (!this.ptr) return; ffi.request.setTrayTitle({ id: this.id, title }); } setImage(imgPath) { if (!this.ptr) return; ffi.request.setTrayImage({ id: this.id, image: this.resolveImagePath(imgPath) }); } setMenu(menu) { if (!this.ptr) return; const menuWithDefaults = menuConfigWithDefaults(menu); ffi.request.setTrayMenu({ id: this.id, menuConfig: JSON.stringify(menuWithDefaults) }); } on(name, handler) { const specificName = `${name}-${this.id}`; eventEmitter_default.on(specificName, handler); } remove() { console.log("Tray.remove() called for id:", this.id); if (this.ptr) { ffi.request.removeTray({ id: this.id }); } delete TrayMap[this.id]; console.log("Tray removed from TrayMap"); } static getById(id) { return TrayMap[id]; } static getAll() { return Object.values(TrayMap); } static removeById(id) { const tray = TrayMap[id]; if (tray) { tray.remove(); } } } var nextTrayId = 1, TrayMap, menuConfigWithDefaults = (menu) => { return menu.map((item) => { if (item.type === "divider" || item.type === "separator") { return { type: "divider" }; } else { const menuItem = item; const actionWithDataId = ffi.internal.serializeMenuAction(menuItem.action || "", menuItem.data); return { label: menuItem.label || "", type: menuItem.type || "normal", action: actionWithDataId, enabled: menuItem.enabled === false ? false : true, checked: Boolean(menuItem.checked), hidden: Boolean(menuItem.hidden), tooltip: menuItem.tooltip || undefined, ...menuItem.submenu ? { submenu: menuConfigWithDefaults(menuItem.submenu) } : {} }; } }); }; var init_Tray = __esm(async () => { init_eventEmitter(); init_Paths(); await init_native(); TrayMap = {}; }); // node_modules/electrobun/dist/api/bun/preload/.generated/compiled.ts var preloadScript = `(() => { // src/bun/preload/encryption.ts function base64ToUint8Array(base64) { return new Uint8Array(atob(base64).split("").map((char) => char.charCodeAt(0))); } function uint8ArrayToBase64(uint8Array) { let binary = ""; for (let i = 0;i < uint8Array.length; i++) { binary += String.fromCharCode(uint8Array[i]); } return btoa(binary); } async function generateKeyFromBytes(rawKey) { return await window.crypto.subtle.importKey("raw", rawKey, { name: "AES-GCM" }, true, ["encrypt", "decrypt"]); } async function initEncryption() { const secretKey = await generateKeyFromBytes(new Uint8Array(window.__electrobunSecretKeyBytes)); const encryptString = async (plaintext) => { const encoder = new TextEncoder; const encodedText = encoder.encode(plaintext); const iv = window.crypto.getRandomValues(new Uint8Array(12)); const encryptedBuffer = await window.crypto.subtle.encrypt({ name: "AES-GCM", iv }, secretKey, encodedText); const encryptedData = new Uint8Array(encryptedBuffer.slice(0, -16)); const tag = new Uint8Array(encryptedBuffer.slice(-16)); return { encryptedData: uint8ArrayToBase64(encryptedData), iv: uint8ArrayToBase64(iv), tag: uint8ArrayToBase64(tag) }; }; const decryptString = async (encryptedDataB64, ivB64, tagB64) => { const encryptedData = base64ToUint8Array(encryptedDataB64); const iv = base64ToUint8Array(ivB64); const tag = base64ToUint8Array(tagB64); const combinedData = new Uint8Array(encryptedData.length + tag.length); combinedData.set(encryptedData); combinedData.set(tag, encryptedData.length); const decryptedBuffer = await window.crypto.subtle.decrypt({ name: "AES-GCM", iv }, secretKey, combinedData); const decoder = new TextDecoder; return decoder.decode(decryptedBuffer); }; window.__electrobun_encrypt = encryptString; window.__electrobun_decrypt = decryptString; } // src/bun/preload/internalRpc.ts var pendingRequests = {}; var requestId = 0; var isProcessingQueue = false; var sendQueue = []; function processQueue() { if (isProcessingQueue) { setTimeout(processQueue); return; } if (sendQueue.length === 0) return; isProcessingQueue = true; const batch = JSON.stringify(sendQueue); sendQueue.length = 0; window.__electrobunInternalBridge?.postMessage(batch); setTimeout(() => { isProcessingQueue = false; }, 2); } function send(type, payload) { sendQueue.push(JSON.stringify({ type: "message", id: type, payload })); processQueue(); } function request(type, payload) { return new Promise((resolve, reject) => { const id = \`req_\${++requestId}_\${Date.now()}\`; pendingRequests[id] = { resolve, reject }; sendQueue.push(JSON.stringify({ type: "request", method: type, id, params: payload, hostWebviewId: window.__electrobunWebviewId })); processQueue(); setTimeout(() => { if (pendingRequests[id]) { delete pendingRequests[id]; reject(new Error(\`Request timeout: \${type}\`)); } }, 1e4); }); } function handleResponse(msg) { if (msg && msg.type === "response" && msg.id) { const pending = pendingRequests[msg.id]; if (pending) { delete pendingRequests[msg.id]; if (msg.success) pending.resolve(msg.payload); else pending.reject(msg.payload); } } } // src/bun/preload/dragRegions.ts function isAppRegionDrag(e) { const target = e.target; if (!target || !target.closest) return false; const draggableByStyle = target.closest('[style*="app-region"][style*="drag"]'); const draggableByClass = target.closest(".electrobun-webkit-app-region-drag"); return !!(draggableByStyle || draggableByClass); } function initDragRegions() { document.addEventListener("mousedown", (e) => { if (isAppRegionDrag(e)) { send("startWindowMove", { id: window.__electrobunWindowId }); } }); document.addEventListener("mouseup", (e) => { if (isAppRegionDrag(e)) { send("stopWindowMove", { id: window.__electrobunWindowId }); } }); } // src/bun/preload/webviewTag.ts var webviewRegistry = {}; class ElectrobunWebviewTag extends HTMLElement { webviewId = null; maskSelectors = new Set; lastRect = { x: 0, y: 0, width: 0, height: 0 }; resizeObserver = null; positionCheckLoop = null; transparent = false; passthroughEnabled = false; hidden = false; sandboxed = false; _eventListeners = {}; constructor() { super(); } connectedCallback() { requestAnimationFrame(() => this.initWebview()); } disconnectedCallback() { if (this.webviewId !== null) { send("webviewTagRemove", { id: this.webviewId }); delete webviewRegistry[this.webviewId]; } if (this.resizeObserver) this.resizeObserver.disconnect(); if (this.positionCheckLoop) clearInterval(this.positionCheckLoop); } async initWebview() { const rect = this.getBoundingClientRect(); this.lastRect = { x: rect.x, y: rect.y, width: rect.width, height: rect.height }; const url = this.getAttribute("src"); const html = this.getAttribute("html"); const preload = this.getAttribute("preload"); const partition = this.getAttribute("partition"); const renderer = this.getAttribute("renderer") || "native"; const masks = this.getAttribute("masks"); const sandbox = this.hasAttribute("sandbox"); this.sandboxed = sandbox; const transparent = this.hasAttribute("transparent"); const passthrough = this.hasAttribute("passthrough"); this.transparent = transparent; this.passthroughEnabled = passthrough; if (transparent) this.style.opacity = "0"; if (passthrough) this.style.pointerEvents = "none"; if (masks) { masks.split(",").forEach((s) => this.maskSelectors.add(s.trim())); } try { const webviewId = await request("webviewTagInit", { hostWebviewId: window.__electrobunWebviewId, windowId: window.__electrobunWindowId, renderer, url, html, preload, partition, frame: { width: rect.width, height: rect.height, x: rect.x, y: rect.y }, navigationRules: null, sandbox, transparent, passthrough }); this.webviewId = webviewId; this.id = \`electrobun-webview-\${webviewId}\`; webviewRegistry[webviewId] = this; this.setupObservers(); this.syncDimensions(true); requestAnimationFrame(() => { Object.values(webviewRegistry).forEach((webview) => { if (webview !== this && webview.webviewId !== null) { webview.syncDimensions(true); } }); }); } catch (err) { console.error("Failed to init webview:", err); } } setupObservers() { this.resizeObserver = new ResizeObserver(() => this.syncDimensions()); this.resizeObserver.observe(this); this.positionCheckLoop = setInterval(() => this.syncDimensions(), 100); } syncDimensions(force = false) { if (this.webviewId === null) return; const rect = this.getBoundingClientRect(); const newRect = { x: rect.x, y: rect.y, width: rect.width, height: rect.height }; if (newRect.width === 0 && newRect.height === 0) { return; } if (!force && newRect.x === this.lastRect.x && newRect.y === this.lastRect.y && newRect.width === this.lastRect.width && newRect.height === this.lastRect.height) { return; } this.lastRect = newRect; const masks = []; this.maskSelectors.forEach((selector) => { try { document.querySelectorAll(selector).forEach((el) => { const mr = el.getBoundingClientRect(); masks.push({ x: mr.x - rect.x, y: mr.y - rect.y, width: mr.width, height: mr.height }); }); } catch (_e) {} }); send("webviewTagResize", { id: this.webviewId, frame: newRect, masks: JSON.stringify(masks) }); } loadURL(url) { if (this.webviewId === null) return; this.setAttribute("src", url); send("webviewTagUpdateSrc", { id: this.webviewId, url }); } loadHTML(html) { if (this.webviewId === null) return; send("webviewTagUpdateHtml", { id: this.webviewId, html }); } reload() { if (this.webviewId !== null) send("webviewTagReload", { id: this.webviewId }); } goBack() { if (this.webviewId !== null) send("webviewTagGoBack", { id: this.webviewId }); } goForward() { if (this.webviewId !== null) send("webviewTagGoForward", { id: this.webviewId }); } async canGoBack() { if (this.webviewId === null) return false; return await request("webviewTagCanGoBack", { id: this.webviewId }); } async canGoForward() { if (this.webviewId === null) return false; return await request("webviewTagCanGoForward", { id: this.webviewId }); } toggleTransparent(value) { if (this.webviewId === null) return; this.transparent = value !== undefined ? value : !this.transparent; this.style.opacity = this.transparent ? "0" : ""; send("webviewTagSetTransparent", { id: this.webviewId, transparent: this.transparent }); } togglePassthrough(value) { if (this.webviewId === null) return; this.passthroughEnabled = value !== undefined ? value : !this.passthroughEnabled; this.style.pointerEvents = this.passthroughEnabled ? "none" : ""; send("webviewTagSetPassthrough", { id: this.webviewId, enablePassthrough: this.passthroughEnabled }); } toggleHidden(value) { if (this.webviewId === null) return; this.hidden = value !== undefined ? value : !this.hidden; send("webviewTagSetHidden", { id: this.webviewId, hidden: this.hidden }); } addMaskSelector(selector) { this.maskSelectors.add(selector); this.syncDimensions(true); } removeMaskSelector(selector) { this.maskSelectors.delete(selector); this.syncDimensions(true); } setNavigationRules(rules) { if (this.webviewId !== null) { send("webviewTagSetNavigationRules", { id: this.webviewId, rules }); } } findInPage(searchText, options) { if (this.webviewId === null) return; const forward = options?.forward !== false; const matchCase = options?.matchCase || false; send("webviewTagFindInPage", { id: this.webviewId, searchText, forward, matchCase }); } stopFindInPage() { if (this.webviewId !== null) send("webviewTagStopFind", { id: this.webviewId }); } openDevTools() { if (this.webviewId !== null) send("webviewTagOpenDevTools", { id: this.webviewId }); } closeDevTools() { if (this.webviewId !== null) send("webviewTagCloseDevTools", { id: this.webviewId }); } toggleDevTools() { if (this.webviewId !== null) send("webviewTagToggleDevTools", { id: this.webviewId }); } on(event, listener) { if (!this._eventListeners[event]) this._eventListeners[event] = []; this._eventListeners[event].push(listener); } off(event, listener) { if (!this._eventListeners[event]) return; const idx = this._eventListeners[event].indexOf(listener); if (idx !== -1) this._eventListeners[event].splice(idx, 1); } emit(event, detail) { const listeners = this._eventListeners[event]; if (listeners) { const customEvent = new CustomEvent(event, { detail }); listeners.forEach((fn) => fn(customEvent)); } } get src() { return this.getAttribute("src"); } set src(value) { if (value) { this.setAttribute("src", value); if (this.webviewId !== null) this.loadURL(value); } else { this.removeAttribute("src"); } } get html() { return this.getAttribute("html"); } set html(value) { if (value) { this.setAttribute("html", value); if (this.webviewId !== null) this.loadHTML(value); } else { this.removeAttribute("html"); } } get preload() { return this.getAttribute("preload"); } set preload(value) { if (value) this.setAttribute("preload", value); else this.removeAttribute("preload"); } get renderer() { return this.getAttribute("renderer") || "native"; } set renderer(value) { this.setAttribute("renderer", value); } get sandbox() { return this.sandboxed; } } function initWebviewTag() { if (!customElements.get("electrobun-webview")) { customElements.define("electrobun-webview", ElectrobunWebviewTag); } const injectStyles = () => { const style = document.createElement("style"); style.textContent = \` electrobun-webview { display: block; width: 800px; height: 300px; background: #fff; background-repeat: no-repeat !important; overflow: hidden; } \`; if (document.head?.firstChild) { document.head.insertBefore(style, document.head.firstChild); } else if (document.head) { document.head.appendChild(style); } }; if (document.head) { injectStyles(); } else { document.addEventListener("DOMContentLoaded", injectStyles); } } // src/bun/preload/events.ts function emitWebviewEvent(eventName, detail) { setTimeout(() => { const bridge = window.__electrobunEventBridge || window.__electrobunInternalBridge; bridge?.postMessage(JSON.stringify({ id: "webviewEvent", type: "message", payload: { id: window.__electrobunWebviewId, eventName, detail } })); }); } function initLifecycleEvents() { window.addEventListener("load", () => { if (window === window.top) { emitWebviewEvent("dom-ready", document.location.href); } }); window.addEventListener("popstate", () => { emitWebviewEvent("did-navigate-in-page", window.location.href); }); window.addEventListener("hashchange", () => { emitWebviewEvent("did-navigate-in-page", window.location.href); }); } var cmdKeyHeld = false; var cmdKeyTimestamp = 0; var CMD_KEY_THRESHOLD_MS = 500; function isCmdHeld() { if (cmdKeyHeld) return true; return Date.now() - cmdKeyTimestamp < CMD_KEY_THRESHOLD_MS && cmdKeyTimestamp > 0; } function initCmdClickHandling() { window.addEventListener("keydown", (event) => { if (event.key === "Meta" || event.metaKey) { cmdKeyHeld = true; cmdKeyTimestamp = Date.now(); } }, true); window.addEventListener("keyup", (event) => { if (event.key === "Meta") { cmdKeyHeld = false; cmdKeyTimestamp = Date.now(); } }, true); window.addEventListener("blur", () => { cmdKeyHeld = false; }); window.addEventListener("click", (event) => { if (event.metaKey || event.ctrlKey) { const anchor = event.target?.closest?.("a"); if (anchor && anchor.href) { event.preventDefault(); event.stopPropagation(); event.stopImmediatePropagation(); emitWebviewEvent("new-window-open", JSON.stringify({ url: anchor.href, isCmdClick: true, isSPANavigation: false })); } } }, true); } function initSPANavigationInterception() { const originalPushState = history.pushState; const originalReplaceState = history.replaceState; history.pushState = function(state, title, url) { if (isCmdHeld() && url) { const resolvedUrl = new URL(String(url), window.location.href).href; emitWebviewEvent("new-window-open", JSON.stringify({ url: resolvedUrl, isCmdClick: true, isSPANavigation: true })); return; } return originalPushState.apply(this, [state, title, url]); }; history.replaceState = function(state, title, url) { if (isCmdHeld() && url) { const resolvedUrl = new URL(String(url), window.location.href).href; emitWebviewEvent("new-window-open", JSON.stringify({ url: resolvedUrl, isCmdClick: true, isSPANavigation: true })); return; } return originalReplaceState.apply(this, [state, title, url]); }; } function initOverscrollPrevention() { document.addEventListener("DOMContentLoaded", () => { const style = document.createElement("style"); style.type = "text/css"; style.appendChild(document.createTextNode("html, body { overscroll-behavior: none; }")); document.head.appendChild(style); }); } // src/bun/preload/index.ts initEncryption().catch((err) => console.error("Failed to initialize encryption:", err)); var internalMessageHandler = (msg) => { handleResponse(msg); }; if (!window.__electrobun) { window.__electrobun = { receiveInternalMessageFromBun: internalMessageHandler, receiveMessageFromBun: (msg) => { console.log("receiveMessageFromBun (no handler):", msg); } }; } else { window.__electrobun.receiveInternalMessageFromBun = internalMessageHandler; window.__electrobun.receiveMessageFromBun = (msg) => { console.log("receiveMessageFromBun (no handler):", msg); }; } window.__electrobunSendToHost = (message) => { emitWebviewEvent("host-message", JSON.stringify(message)); }; initLifecycleEvents(); initCmdClickHandling(); initSPANavigationInterception(); initOverscrollPrevention(); initDragRegions(); initWebviewTag(); })(); `, preloadScriptSandboxed = `(() => { // src/bun/preload/events.ts function emitWebviewEvent(eventName, detail) { setTimeout(() => { const bridge = window.__electrobunEventBridge || window.__electrobunInternalBridge; bridge?.postMessage(JSON.stringify({ id: "webviewEvent", type: "message", payload: { id: window.__electrobunWebviewId, eventName, detail } })); }); } function initLifecycleEvents() { window.addEventListener("load", () => { if (window === window.top) { emitWebviewEvent("dom-ready", document.location.href); } }); window.addEventListener("popstate", () => { emitWebviewEvent("did-navigate-in-page", window.location.href); }); window.addEventListener("hashchange", () => { emitWebviewEvent("did-navigate-in-page", window.location.href); }); } var cmdKeyHeld = false; var cmdKeyTimestamp = 0; var CMD_KEY_THRESHOLD_MS = 500; function isCmdHeld() { if (cmdKeyHeld) return true; return Date.now() - cmdKeyTimestamp < CMD_KEY_THRESHOLD_MS && cmdKeyTimestamp > 0; } function initCmdClickHandling() { window.addEventListener("keydown", (event) => { if (event.key === "Meta" || event.metaKey) { cmdKeyHeld = true; cmdKeyTimestamp = Date.now(); } }, true); window.addEventListener("keyup", (event) => { if (event.key === "Meta") { cmdKeyHeld = false; cmdKeyTimestamp = Date.now(); } }, true); window.addEventListener("blur", () => { cmdKeyHeld = false; }); window.addEventListener("click", (event) => { if (event.metaKey || event.ctrlKey) { const anchor = event.target?.closest?.("a"); if (anchor && anchor.href) { event.preventDefault(); event.stopPropagation(); event.stopImmediatePropagation(); emitWebviewEvent("new-window-open", JSON.stringify({ url: anchor.href, isCmdClick: true, isSPANavigation: false })); } } }, true); } function initSPANavigationInterception() { const originalPushState = history.pushState; const originalReplaceState = history.replaceState; history.pushState = function(state, title, url) { if (isCmdHeld() && url) { const resolvedUrl = new URL(String(url), window.location.href).href; emitWebviewEvent("new-window-open", JSON.stringify({ url: resolvedUrl, isCmdClick: true, isSPANavigation: true })); return; } return originalPushState.apply(this, [state, title, url]); }; history.replaceState = function(state, title, url) { if (isCmdHeld() && url) { const resolvedUrl = new URL(String(url), window.location.href).href; emitWebviewEvent("new-window-open", JSON.stringify({ url: resolvedUrl, isCmdClick: true, isSPANavigation: true })); return; } return originalReplaceState.apply(this, [state, title, url]); }; } function initOverscrollPrevention() { document.addEventListener("DOMContentLoaded", () => { const style = document.createElement("style"); style.type = "text/css"; style.appendChild(document.createTextNode("html, body { overscroll-behavior: none; }")); document.head.appendChild(style); }); } // src/bun/preload/index-sandboxed.ts initLifecycleEvents(); initCmdClickHandling(); initSPANavigationInterception(); initOverscrollPrevention(); })(); `; // node_modules/electrobun/dist/api/bun/proc/native.ts import { join as join4 } from "path"; import { dlopen, suffix, JSCallback, CString, ptr, FFIType, toArrayBuffer } from "bun:ffi"; function storeMenuData(data) { const id = `menuData_${++menuDataCounter}`; menuDataRegistry.set(id, data); return id; } function getMenuData(id) { return menuDataRegistry.get(id); } function clearMenuData(id) { menuDataRegistry.delete(id); } function serializeMenuAction(action, data) { const dataId = storeMenuData(data); return `${ELECTROBUN_DELIMITER}${dataId}|${action}`; } function deserializeMenuAction(encodedAction) { let actualAction = encodedAction; let data = undefined; if (encodedAction.startsWith(ELECTROBUN_DELIMITER)) { const parts = encodedAction.split("|"); if (parts.length >= 4) { const dataId = parts[2]; actualAction = parts.slice(3).join("|"); data = getMenuData(dataId); clearMenuData(dataId); } } return { action: actualAction, data }; } class SessionCookies { partitionId; constructor(partitionId) { this.partitionId = partitionId; } get(filter) { const filterJson = JSON.stringify(filter || {}); const result = native.symbols.sessionGetCookies(toCString(this.partitionId), toCString(filterJson)); if (!result) return []; try { return JSON.parse(result.toString()); } catch { return []; } } set(cookie) { const cookieJson = JSON.stringify(cookie); return native.symbols.sessionSetCookie(toCString(this.partitionId), toCString(cookieJson)); } remove(url, name) { return native.symbols.sessionRemoveCookie(toCString(this.partitionId), toCString(url), toCString(name)); } clear() { native.symbols.sessionClearCookies(toCString(this.partitionId)); } } class SessionInstance { partition; cookies; constructor(partition) { this.partition = partition; this.cookies = new SessionCookies(partition); } clearStorageData(types = "all") { const typesArray = types === "all" ? ["all"] : types; native.symbols.sessionClearStorageData(toCString(this.partition), toCString(JSON.stringify(typesArray))); } } function toCString(jsString, addNullTerminator = true) { let appendWith = ""; if (addNullTerminator && !jsString.endsWith("\x00")) { appendWith = "\x00"; } const buff = Buffer.from(jsString + appendWith, "utf8"); return ptr(buff); } var menuDataRegistry, menuDataCounter = 0, ELECTROBUN_DELIMITER = "|EB|", native, ffi, windowCloseCallback, windowMoveCallback, windowResizeCallback, windowFocusCallback, getMimeType, getHTMLForWebviewSync, urlOpenCallback, quitRequestedCallback, globalShortcutHandlers, globalShortcutCallback, GlobalShortcut, Screen, sessionCache, Session, webviewDecideNavigation, webviewEventHandler = (id, eventName, detail) => { const webview = BrowserView.getById(id); if (!webview) { console.error("[webviewEventHandler] No webview found for id:", id); return; } if (webview.hostWebviewId) { const hostWebview = BrowserView.getById(webview.hostWebviewId); if (!hostWebview) { console.error("[webviewEventHandler] No webview found for id:", id); return; } let js; if (eventName === "new-window-open" || eventName === "host-message") { js = `document.querySelector('#electrobun-webview-${id}').emit(${JSON.stringify(eventName)}, ${detail});`; } else { js = `document.querySelector('#electrobun-webview-${id}').emit(${JSON.stringify(eventName)}, ${JSON.stringify(detail)});`; } native.symbols.evaluateJavaScriptWithNoCompletion(hostWebview.ptr, toCString(js)); } const eventMap = { "will-navigate": "willNavigate", "did-navigate": "didNavigate", "did-navigate-in-page": "didNavigateInPage", "did-commit-navigation": "didCommitNavigation", "dom-ready": "domReady", "new-window-open": "newWindowOpen", "host-message": "hostMessage", "download-started": "downloadStarted", "download-progress": "downloadProgress", "download-completed": "downloadCompleted", "download-failed": "downloadFailed", "load-started": "loadStarted", "load-committed": "loadCommitted", "load-finished": "loadFinished" }; const mappedName = eventMap[eventName]; const handler = mappedName ? eventEmitter_default.events.webview[mappedName] : undefined; if (!handler) { return { success: false }; } let parsedDetail = detail; if (eventName === "new-window-open" || eventName === "host-message" || eventName === "download-started" || eventName === "download-progress" || eventName === "download-completed" || eventName === "download-failed") { try { parsedDetail = JSON.parse(detail); } catch (e) { console.error("[webviewEventHandler] Failed to parse JSON:", e); parsedDetail = detail; } } const event = handler({ detail: parsedDetail }); eventEmitter_default.emitEvent(event); eventEmitter_default.emitEvent(event, id); }, webviewEventJSCallback, bunBridgePostmessageHandler, eventBridgeHandler, internalBridgeHandler, trayItemHandler, applicationMenuHandler, contextMenuHandler, internalRpcHandlers; var init_native = __esm(async () => { init_eventEmitter(); await __promiseAll([ init_BrowserView(), init_Tray(), init_BrowserWindow() ]); menuDataRegistry = new Map; native = (() => { try { const nativeWrapperPath = join4(process.cwd(), `libNativeWrapper.${suffix}`); return dlopen(nativeWrapperPath, { createWindowWithFrameAndStyleFromWorker: { args: [ FFIType.u32, FFIType.f64, FFIType.f64, FFIType.f64, FFIType.f64, FFIType.u32, FFIType.cstring, FFIType.bool, FFIType.function, FFIType.function, FFIType.function, FFIType.function ], returns: FFIType.ptr }, setWindowTitle: { args: [ FFIType.ptr, FFIType.cstring ], returns: FFIType.void }, showWindow: { args: [ FFIType.ptr ], returns: FFIType.void }, closeWindow: { args: [ FFIType.ptr ], returns: FFIType.void }, minimizeWindow: { args: [FFIType.ptr], returns: FFIType.void }, restoreWindow: { args: [FFIType.ptr], returns: FFIType.void }, isWindowMinimized: { args: [FFIType.ptr], returns: FFIType.bool }, maximizeWindow: { args: [FFIType.ptr], returns: FFIType.void }, unmaximizeWindow: { args: [FFIType.ptr], returns: FFIType.void }, isWindowMaximized: { args: [FFIType.ptr], returns: FFIType.bool }, setWindowFullScreen: { args: [FFIType.ptr, FFIType.bool], returns: FFIType.void }, isWindowFullScreen: { args: [FFIType.ptr], returns: FFIType.bool }, setWindowAlwaysOnTop: { args: [FFIType.ptr, FFIType.bool], returns: FFIType.void }, isWindowAlwaysOnTop: { args: [FFIType.ptr], returns: FFIType.bool }, setWindowPosition: { args: [FFIType.ptr, FFIType.f64, FFIType.f64], returns: FFIType.void }, setWindowSize: { args: [FFIType.ptr, FFIType.f64, FFIType.f64], returns: FFIType.void }, setWindowFrame: { args: [FFIType.ptr, FFIType.f64, FFIType.f64, FFIType.f64, FFIType.f64], returns: FFIType.void }, getWindowFrame: { args: [FFIType.ptr, FFIType.ptr, FFIType.ptr, FFIType.ptr, FFIType.ptr], returns: FFIType.void }, initWebview: { args: [ FFIType.u32, FFIType.ptr, FFIType.cstring, FFIType.cstring, FFIType.f64, FFIType.f64, FFIType.f64, FFIType.f64, FFIType.bool, FFIType.cstring, FFIType.function, FFIType.function, FFIType.function, FFIType.function, FFIType.function, FFIType.cstring, FFIType.cstring, FFIType.bool, FFIType.bool ], returns: FFIType.ptr }, setNextWebviewFlags: { args: [ FFIType.bool, FFIType.bool ], returns: FFIType.void }, webviewCanGoBack: { args: [FFIType.ptr], returns: FFIType.bool }, webviewCanGoForward: { args: [FFIType.ptr], returns: FFIType.bool }, resizeWebview: { args: [ FFIType.ptr, FFIType.f64, FFIType.f64, FFIType.f64, FFIType.f64, FFIType.cstring ], returns: FFIType.void }, loadURLInWebView: { args: [FFIType.ptr, FFIType.cstring], returns: FFIType.void }, loadHTMLInWebView: { args: [FFIType.ptr, FFIType.cstring], returns: FFIType.void }, updatePreloadScriptToWebView: { args: [ FFIType.ptr, FFIType.cstring, FFIType.cstring, FFIType.bool ], returns: FFIType.void }, webviewGoBack: { args: [FFIType.ptr], returns: FFIType.void }, webviewGoForward: { args: [FFIType.ptr], returns: FFIType.void }, webviewReload: { args: [FFIType.ptr], returns: FFIType.void }, webviewRemove: { args: [FFIType.ptr], returns: FFIType.void }, setWebviewHTMLContent: { args: [FFIType.u32, FFIType.cstring], returns: FFIType.void }, startWindowMove: { args: [FFIType.ptr], returns: FFIType.void }, stopWindowMove: { args: [], returns: FFIType.void }, webviewSetTransparent: { args: [FFIType.ptr, FFIType.bool], returns: FFIType.void }, webviewSetPassthrough: { args: [FFIType.ptr, FFIType.bool], returns: FFIType.void }, webviewSetHidden: { args: [FFIType.ptr, FFIType.bool], returns: FFIType.void }, setWebviewNavigationRules: { args: [FFIType.ptr, FFIType.cstring], returns: FFIType.void }, webviewFindInPage: { args: [FFIType.ptr, FFIType.cstring, FFIType.bool, FFIType.bool], returns: FFIType.void }, webviewStopFind: { args: [FFIType.ptr], returns: FFIType.void }, evaluateJavaScriptWithNoCompletion: { args: [FFIType.ptr, FFIType.cstring], returns: FFIType.void }, webviewOpenDevTools: { args: [FFIType.ptr], returns: FFIType.void }, webviewCloseDevTools: { args: [FFIType.ptr], returns: FFIType.void }, webviewToggleDevTools: { args: [FFIType.ptr], returns: FFIType.void }, createTray: { args: [ FFIType.u32, FFIType.cstring, FFIType.cstring, FFIType.bool, FFIType.u32, FFIType.u32, FFIType.function ], returns: FFIType.ptr }, setTrayTitle: { args: [FFIType.ptr, FFIType.cstring], returns: FFIType.void }, setTrayImage: { args: [FFIType.ptr, FFIType.cstring], returns: FFIType.void }, setTrayMenu: { args: [FFIType.ptr, FFIType.cstring], returns: FFIType.void }, removeTray: { args: [FFIType.ptr], returns: FFIType.void }, setApplicationMenu: { args: [FFIType.cstring, FFIType.function], returns: FFIType.void }, showContextMenu: { args: [FFIType.cstring, FFIType.function], returns: FFIType.void }, moveToTrash: { args: [FFIType.cstring], returns: FFIType.bool }, showItemInFolder: { args: [FFIType.cstring], returns: FFIType.void }, openExternal: { args: [FFIType.cstring], returns: FFIType.bool }, openPath: { args: [FFIType.cstring], returns: FFIType.bool }, showNotification: { args: [ FFIType.cstring, FFIType.cstring, FFIType.cstring, FFIType.bool ], returns: FFIType.void }, setGlobalShortcutCallback: { args: [FFIType.function], returns: FFIType.void }, registerGlobalShortcut: { args: [FFIType.cstring], returns: FFIType.bool }, unregisterGlobalShortcut: { args: [FFIType.cstring], returns: FFIType.bool }, unregisterAllGlobalShortcuts: { args: [], returns: FFIType.void }, isGlobalShortcutRegistered: { args: [FFIType.cstring], returns: FFIType.bool }, getAllDisplays: { args: [], returns: FFIType.cstring }, getPrimaryDisplay: { args: [], returns: FFIType.cstring }, getCursorScreenPoint: { args: [], returns: FFIType.cstring }, openFileDialog: { args: [ FFIType.cstring, FFIType.cstring, FFIType.int, FFIType.int, FFIType.int ], returns: FFIType.cstring }, showMessageBox: { args: [ FFIType.cstring, FFIType.cstring, FFIType.cstring, FFIType.cstring, FFIType.cstring, FFIType.int, FFIType.int ], returns: FFIType.int }, clipboardReadText: { args: [], returns: FFIType.cstring }, clipboardWriteText: { args: [FFIType.cstring], returns: FFIType.void }, clipboardReadImage: { args: [FFIType.ptr], returns: FFIType.ptr }, clipboardWriteImage: { args: [FFIType.ptr, FFIType.u64], returns: FFIType.void }, clipboardClear: { args: [], returns: FFIType.void }, clipboardAvailableFormats: { args: [], returns: FFIType.cstring }, sessionGetCookies: { args: [FFIType.cstring, FFIType.cstring], returns: FFIType.cstring }, sessionSetCookie: { args: [FFIType.cstring, FFIType.cstring], returns: FFIType.bool }, sessionRemoveCookie: { args: [FFIType.cstring, FFIType.cstring, FFIType.cstring], returns: FFIType.bool }, sessionClearCookies: { args: [FFIType.cstring], returns: FFIType.void }, sessionClearStorageData: { args: [FFIType.cstring, FFIType.cstring], returns: FFIType.void }, setURLOpenHandler: { args: [FFIType.function], returns: FFIType.void }, getWindowStyle: { args: [ FFIType.bool, FFIType.bool, FFIType.bool, FFIType.bool, FFIType.bool, FFIType.bool, FFIType.bool, FFIType.bool, FFIType.bool, FFIType.bool, FFIType.bool, FFIType.bool ], returns: FFIType.u32 }, setJSUtils: { args: [ FFIType.function, FFIType.function ], returns: FFIType.void }, setWindowIcon: { args: [ FFIType.ptr, FFIType.cstring ], returns: FFIType.void }, killApp: { args: [], returns: FFIType.void }, stopEventLoop: { args: [], returns: FFIType.void }, waitForShutdownComplete: { args: [FFIType.i32], returns: FFIType.void }, forceExit: { args: [FFIType.i32], returns: FFIType.void }, setQuitRequestedHandler: { args: [FFIType.function], returns: FFIType.void }, testFFI2: { args: [FFIType.function], returns: FFIType.void } }); } catch (err) { console.log("FATAL Error opening native FFI:", err.message); console.log("This may be due to:"); console.log(" - Missing libNativeWrapper.dll/so/dylib"); console.log(" - Architecture mismatch (ARM64 vs x64)"); console.log(" - Missing WebView2 or CEF dependencies"); if (suffix === "so") { console.log(" - Missing system libraries (try: ldd ./libNativeWrapper.so)"); } console.log("Check that the build process completed successfully for your architecture."); process.exit(); } })(); ffi = { request: { createWindow: (params) => { const { id, url: _url, title, frame: { x, y, width, height }, styleMask: { Borderless, Titled, Closable, Miniaturizable, Resizable, UnifiedTitleAndToolbar, FullScreen, FullSizeContentView, UtilityWindow, DocModalWindow, NonactivatingPanel, HUDWindow }, titleBarStyle, transparent } = params; const styleMask = native.symbols.getWindowStyle(Borderless, Titled, Closable, Miniaturizable, Resizable, UnifiedTitleAndToolbar, FullScreen, FullSizeContentView, UtilityWindow, DocModalWindow, NonactivatingPanel, HUDWindow); const windowPtr = native.symbols.createWindowWithFrameAndStyleFromWorker(id, x, y, width, height, styleMask, toCString(titleBarStyle), transparent, windowCloseCallback, windowMoveCallback, windowResizeCallback, windowFocusCallback); if (!windowPtr) { throw "Failed to create window"; } native.symbols.setWindowTitle(windowPtr, toCString(title)); native.symbols.showWindow(windowPtr); return windowPtr; }, setTitle: (params) => { const { winId, title } = params; const windowPtr = BrowserWindow.getById(winId)?.ptr; if (!windowPtr) { throw `Can't add webview to window. window no longer exists`; } native.symbols.setWindowTitle(windowPtr, toCString(title)); }, closeWindow: (params) => { const { winId } = params; const windowPtr = BrowserWindow.getById(winId)?.ptr; if (!windowPtr) { throw `Can't close window. Window no longer exists`; } native.symbols.closeWindow(windowPtr); }, focusWindow: (params) => { const { winId } = params; const windowPtr = BrowserWindow.getById(winId)?.ptr; if (!windowPtr) { throw `Can't focus window. Window no longer exists`; } native.symbols.showWindow(windowPtr); }, minimizeWindow: (params) => { const { winId } = params; const windowPtr = BrowserWindow.getById(winId)?.ptr; if (!windowPtr) { throw `Can't minimize window. Window no longer exists`; } native.symbols.minimizeWindow(windowPtr); }, restoreWindow: (params) => { const { winId } = params; const windowPtr = BrowserWindow.getById(winId)?.ptr; if (!windowPtr) { throw `Can't restore window. Window no longer exists`; } native.symbols.restoreWindow(windowPtr); }, isWindowMinimized: (params) => { const { winId } = params; const windowPtr = BrowserWindow.getById(winId)?.ptr; if (!windowPtr) { return false; } return native.symbols.isWindowMinimized(windowPtr); }, maximizeWindow: (params) => { const { winId } = params; const windowPtr = BrowserWindow.getById(winId)?.ptr; if (!windowPtr) { throw `Can't maximize window. Window no longer exists`; } native.symbols.maximizeWindow(windowPtr); }, unmaximizeWindow: (params) => { const { winId } = params; const windowPtr = BrowserWindow.getById(winId)?.ptr; if (!windowPtr) { throw `Can't unmaximize window. Window no longer exists`; } native.symbols.unmaximizeWindow(windowPtr); }, isWindowMaximized: (params) => { const { winId } = params; const windowPtr = BrowserWindow.getById(winId)?.ptr; if (!windowPtr) { return false; } return native.symbols.isWindowMaximized(windowPtr); }, setWindowFullScreen: (params) => { const { winId, fullScreen } = params; const windowPtr = BrowserWindow.getById(winId)?.ptr; if (!windowPtr) { throw `Can't set fullscreen. Window no longer exists`; } native.symbols.setWindowFullScreen(windowPtr, fullScreen); }, isWindowFullScreen: (params) => { const { winId } = params; const windowPtr = BrowserWindow.getById(winId)?.ptr; if (!windowPtr) { return false; } return native.symbols.isWindowFullScreen(windowPtr); }, setWindowAlwaysOnTop: (params) => { const { winId, alwaysOnTop } = params; const windowPtr = BrowserWindow.getById(winId)?.ptr; if (!windowPtr) { throw `Can't set always on top. Window no longer exists`; } native.symbols.setWindowAlwaysOnTop(windowPtr, alwaysOnTop); }, isWindowAlwaysOnTop: (params) => { const { winId } = params; const windowPtr = BrowserWindow.getById(winId)?.ptr; if (!windowPtr) { return false; } return native.symbols.isWindowAlwaysOnTop(windowPtr); }, setWindowPosition: (params) => { const { winId, x, y } = params; const windowPtr = BrowserWindow.getById(winId)?.ptr; if (!windowPtr) { throw `Can't set window position. Window no longer exists`; } native.symbols.setWindowPosition(windowPtr, x, y); }, setWindowSize: (params) => { const { winId, width, height } = params; const windowPtr = BrowserWindow.getById(winId)?.ptr; if (!windowPtr) { throw `Can't set window size. Window no longer exists`; } native.symbols.setWindowSize(windowPtr, width, height); }, setWindowFrame: (params) => { const { winId, x, y, width, height } = params; const windowPtr = BrowserWindow.getById(winId)?.ptr; if (!windowPtr) { throw `Can't set window frame. Window no longer exists`; } native.symbols.setWindowFrame(windowPtr, x, y, width, height); }, getWindowFrame: (params) => { const { winId } = params; const windowPtr = BrowserWindow.getById(winId)?.ptr; if (!windowPtr) { return { x: 0, y: 0, width: 0, height: 0 }; } const xBuf = new Float64Array(1); const yBuf = new Float64Array(1); const widthBuf = new Float64Array(1); const heightBuf = new Float64Array(1); native.symbols.getWindowFrame(windowPtr, ptr(xBuf), ptr(yBuf), ptr(widthBuf), ptr(heightBuf)); return { x: xBuf[0], y: yBuf[0], width: widthBuf[0], height: heightBuf[0] }; }, createWebview: (params) => { const { id, windowId, renderer, rpcPort: rpcPort2, secretKey, url, partition, preload, frame: { x, y, width, height }, autoResize, sandbox, startTransparent, startPassthrough } = params; const parentWindow = BrowserWindow.getById(windowId); const windowPtr = parentWindow?.ptr; const transparent = parentWindow?.transparent ?? false; if (!windowPtr) { throw `Can't add webview to window. window no longer exists`; } let dynamicPreload; let selectedPreloadScript; if (sandbox) { dynamicPreload = ` window.__electrobunWebviewId = ${id}; window.__electrobunWindowId = ${windowId}; window.__electrobunEventBridge = window.__electrobunEventBridge || window.webkit?.messageHandlers?.eventBridge || window.eventBridge || window.chrome?.webview?.hostObjects?.eventBridge; window.__electrobunInternalBridge = window.__electrobunInternalBridge || window.webkit?.messageHandlers?.internalBridge || window.internalBridge || window.chrome?.webview?.hostObjects?.internalBridge; `; selectedPreloadScript = preloadScriptSandboxed; } else { dynamicPreload = ` window.__electrobunWebviewId = ${id}; window.__electrobunWindowId = ${windowId}; window.__electrobunRpcSocketPort = ${rpcPort2}; window.__electrobunSecretKeyBytes = [${secretKey}]; window.__electrobunEventBridge = window.__electrobunEventBridge || window.webkit?.messageHandlers?.eventBridge || window.eventBridge || window.chrome?.webview?.hostObjects?.eventBridge; window.__electrobunInternalBridge = window.__electrobunInternalBridge || window.webkit?.messageHandlers?.internalBridge || window.internalBridge || window.chrome?.webview?.hostObjects?.internalBridge; window.__electrobunBunBridge = window.__electrobunBunBridge || window.webkit?.messageHandlers?.bunBridge || window.bunBridge || window.chrome?.webview?.hostObjects?.bunBridge; `; selectedPreloadScript = preloadScript; } const electrobunPreload = dynamicPreload + selectedPreloadScript; const customPreload = preload; native.symbols.setNextWebviewFlags(startTransparent, startPassthrough); const webviewPtr = native.symbols.initWebview(id, windowPtr, toCString(renderer), toCString(url || ""), x, y, width, height, autoResize, toCString(partition || "persist:default"), webviewDecideNavigation, webviewEventJSCallback, eventBridgeHandler, bunBridgePostmessageHandler, internalBridgeHandler, toCString(electrobunPreload), toCString(customPreload || ""), transparent, sandbox); if (!webviewPtr) { throw "Failed to create webview"; } return webviewPtr; }, evaluateJavascriptWithNoCompletion: (params) => { const { id, js } = params; const webview = BrowserView.getById(id); if (!webview?.ptr) { return; } native.symbols.evaluateJavaScriptWithNoCompletion(webview.ptr, toCString(js)); }, createTray: (params) => { const { id, title, image, template, width, height } = params; const trayPtr = native.symbols.createTray(id, toCString(title), toCString(image), template, width, height, trayItemHandler); if (!trayPtr) { throw "Failed to create tray"; } return trayPtr; }, setTrayTitle: (params) => { const { id, title } = params; const tray = Tray.getById(id); if (!tray) return; native.symbols.setTrayTitle(tray.ptr, toCString(title)); }, setTrayImage: (params) => { const { id, image } = params; const tray = Tray.getById(id); if (!tray) return; native.symbols.setTrayImage(tray.ptr, toCString(image)); }, setTrayMenu: (params) => { const { id, menuConfig } = params; const tray = Tray.getById(id); if (!tray) return; native.symbols.setTrayMenu(tray.ptr, toCString(menuConfig)); }, removeTray: (params) => { const { id } = params; const tray = Tray.getById(id); if (!tray) { throw `Can't remove tray. Tray no longer exists`; } native.symbols.removeTray(tray.ptr); }, setApplicationMenu: (params) => { const { menuConfig } = params; native.symbols.setApplicationMenu(toCString(menuConfig), applicationMenuHandler); }, showContextMenu: (params) => { const { menuConfig } = params; native.symbols.showContextMenu(toCString(menuConfig), contextMenuHandler); }, moveToTrash: (params) => { const { path } = params; return native.symbols.moveToTrash(toCString(path)); }, showItemInFolder: (params) => { const { path } = params; native.symbols.showItemInFolder(toCString(path)); }, openExternal: (params) => { const { url } = params; return native.symbols.openExternal(toCString(url)); }, openPath: (params) => { const { path } = params; return native.symbols.openPath(toCString(path)); }, showNotification: (params) => { const { title, body = "", subtitle = "", silent = false } = params; native.symbols.showNotification(toCString(title), toCString(body), toCString(subtitle), silent); }, openFileDialog: (params) => { const { startingFolder, allowedFileTypes, canChooseFiles, canChooseDirectory, allowsMultipleSelection } = params; const filePath = native.symbols.openFileDialog(toCString(startingFolder), toCString(allowedFileTypes), canChooseFiles ? 1 : 0, canChooseDirectory ? 1 : 0, allowsMultipleSelection ? 1 : 0); return filePath.toString(); }, showMessageBox: (params) => { const { type = "info", title = "", message = "", detail = "", buttons = ["OK"], defaultId = 0, cancelId = -1 } = params; const buttonsStr = buttons.join(","); return native.symbols.showMessageBox(toCString(type), toCString(title), toCString(message), toCString(detail), toCString(buttonsStr), defaultId, cancelId); }, clipboardReadText: () => { const result = native.symbols.clipboardReadText(); if (!result) return null; return result.toString(); }, clipboardWriteText: (params) => { native.symbols.clipboardWriteText(toCString(params.text)); }, clipboardReadImage: () => { const sizeBuffer = new BigUint64Array(1); const dataPtr = native.symbols.clipboardReadImage(ptr(sizeBuffer)); if (!dataPtr) return null; const size = Number(sizeBuffer[0]); if (size === 0) return null; const result = new Uint8Array(size); const sourceView = new Uint8Array(toArrayBuffer(dataPtr, 0, size)); result.set(sourceView); return result; }, clipboardWriteImage: (params) => { const { pngData } = params; native.symbols.clipboardWriteImage(ptr(pngData), BigInt(pngData.length)); }, clipboardClear: () => { native.symbols.clipboardClear(); }, clipboardAvailableFormats: () => { const result = native.symbols.clipboardAvailableFormats(); if (!result) return []; const formatsStr = result.toString(); if (!formatsStr) return []; return formatsStr.split(",").filter((f) => f.length > 0); } }, internal: { storeMenuData, getMenuData, clearMenuData, serializeMenuAction, deserializeMenuAction } }; process.on("uncaughtException", (err) => { console.error("Uncaught exception in worker:", err); native.symbols.stopEventLoop(); native.symbols.waitForShutdownComplete(5000); native.symbols.forceExit(1); }); process.on("unhandledRejection", (reason, _promise) => { console.error("Unhandled rejection in worker:", reason); }); process.on("SIGINT", () => { console.log("[electrobun] Received SIGINT, running quit sequence..."); const { quit: quit2 } = (init_Utils(), __toCommonJS(exports_Utils)); quit2(); }); process.on("SIGTERM", () => { console.log("[electrobun] Received SIGTERM, running quit sequence..."); const { quit: quit2 } = (init_Utils(), __toCommonJS(exports_Utils)); quit2(); }); windowCloseCallback = new JSCallback((id) => { const handler = eventEmitter_default.events.window.close; const event = handler({ id }); eventEmitter_default.emitEvent(event, id); eventEmitter_default.emitEvent(event); }, { args: ["u32"], returns: "void", threadsafe: true }); windowMoveCallback = new JSCallback((id, x, y) => { const handler = eventEmitter_default.events.window.move; const event = handler({ id, x, y }); eventEmitter_default.emitEvent(event); eventEmitter_default.emitEvent(event, id); }, { args: ["u32", "f64", "f64"], returns: "void", threadsafe: true }); windowResizeCallback = new JSCallback((id, x, y, width, height) => { const handler = eventEmitter_default.events.window.resize; const event = handler({ id, x, y, width, height }); eventEmitter_default.emitEvent(event); eventEmitter_default.emitEvent(event, id); }, { args: ["u32", "f64", "f64", "f64", "f64"], returns: "void", threadsafe: true }); windowFocusCallback = new JSCallback((id) => { const handler = eventEmitter_default.events.window.focus; const event = handler({ id }); eventEmitter_default.emitEvent(event); eventEmitter_default.emitEvent(event, id); }, { args: ["u32"], returns: "void", threadsafe: true }); getMimeType = new JSCallback((filePath) => { const _filePath = new CString(filePath).toString(); const mimeType = Bun.file(_filePath).type; return toCString(mimeType.split(";")[0]); }, { args: [FFIType.cstring], returns: FFIType.cstring }); getHTMLForWebviewSync = new JSCallback((webviewId) => { const webview = BrowserView.getById(webviewId); return toCString(webview?.html || ""); }, { args: [FFIType.u32], returns: FFIType.cstring }); native.symbols.setJSUtils(getMimeType, getHTMLForWebviewSync); urlOpenCallback = new JSCallback((urlPtr) => { const url = new CString(urlPtr).toString(); const handler = eventEmitter_default.events.app.openUrl; const event = handler({ url }); eventEmitter_default.emitEvent(event); }, { args: [FFIType.cstring], returns: "void", threadsafe: true }); if (process.platform === "darwin") { native.symbols.setURLOpenHandler(urlOpenCallback); } quitRequestedCallback = new JSCallback(() => { const { quit: quit2 } = (init_Utils(), __toCommonJS(exports_Utils)); quit2(); }, { args: [], returns: "void", threadsafe: true }); native.symbols.setQuitRequestedHandler(quitRequestedCallback); globalShortcutHandlers = new Map; globalShortcutCallback = new JSCallback((acceleratorPtr) => { const accelerator = new CString(acceleratorPtr).toString(); const handler = globalShortcutHandlers.get(accelerator); if (handler) { handler(); } }, { args: [FFIType.cstring], returns: "void", threadsafe: true }); native.symbols.setGlobalShortcutCallback(globalShortcutCallback); GlobalShortcut = { register: (accelerator, callback) => { if (globalShortcutHandlers.has(accelerator)) { return false; } const result = native.symbols.registerGlobalShortcut(toCString(accelerator)); if (result) { globalShortcutHandlers.set(accelerator, callback); } return result; }, unregister: (accelerator) => { const result = native.symbols.unregisterGlobalShortcut(toCString(accelerator)); if (result) { globalShortcutHandlers.delete(accelerator); } return result; }, unregisterAll: () => { native.symbols.unregisterAllGlobalShortcuts(); globalShortcutHandlers.clear(); }, isRegistered: (accelerator) => { return native.symbols.isGlobalShortcutRegistered(toCString(accelerator)); } }; Screen = { getPrimaryDisplay: () => { const jsonStr = native.symbols.getPrimaryDisplay(); if (!jsonStr) { return { id: 0, bounds: { x: 0, y: 0, width: 0, height: 0 }, workArea: { x: 0, y: 0, width: 0, height: 0 }, scaleFactor: 1, isPrimary: true }; } try { return JSON.parse(jsonStr.toString()); } catch { return { id: 0, bounds: { x: 0, y: 0, width: 0, height: 0 }, workArea: { x: 0, y: 0, width: 0, height: 0 }, scaleFactor: 1, isPrimary: true }; } }, getAllDisplays: () => { const jsonStr = native.symbols.getAllDisplays(); if (!jsonStr) { return []; } try { return JSON.parse(jsonStr.toString()); } catch { return []; } }, getCursorScreenPoint: () => { const jsonStr = native.symbols.getCursorScreenPoint(); if (!jsonStr) { return { x: 0, y: 0 }; } try { return JSON.parse(jsonStr.toString()); } catch { return { x: 0, y: 0 }; } } }; sessionCache = new Map; Session = { fromPartition: (partition) => { let session = sessionCache.get(partition); if (!session) { session = new SessionInstance(partition); sessionCache.set(partition, session); } return session; }, get defaultSession() { return Session.fromPartition("persist:default"); } }; webviewDecideNavigation = new JSCallback((_webviewId, _url) => { return true; }, { args: [FFIType.u32, FFIType.cstring], returns: FFIType.u32, threadsafe: true }); webviewEventJSCallback = new JSCallback((id, _eventName, _detail) => { let eventName = ""; let detail = ""; try { eventName = new CString(_eventName).toString(); detail = new CString(_detail).toString(); } catch (err) { console.error("[webviewEventJSCallback] Error converting strings:", err); console.error("[webviewEventJSCallback] Raw values:", { _eventName, _detail }); return; } webviewEventHandler(id, eventName, detail); }, { args: [FFIType.u32, FFIType.cstring, FFIType.cstring], returns: FFIType.void, threadsafe: true }); bunBridgePostmessageHandler = new JSCallback((id, msg) => { try { const msgStr = new CString(msg); if (!msgStr.length) { return; } const msgJson = JSON.parse(msgStr.toString()); const webview = BrowserView.getById(id); if (!webview) return; webview.rpcHandler?.(msgJson); } catch (err) { console.error("error sending message to bun: ", err); console.error("msgString: ", new CString(msg)); } }, { args: [FFIType.u32, FFIType.cstring], returns: FFIType.void, threadsafe: true }); eventBridgeHandler = new JSCallback((_id, msg) => { try { const message = new CString(msg); const jsonMessage = JSON.parse(message.toString()); if (jsonMessage.id === "webviewEvent") { const { payload } = jsonMessage; webviewEventHandler(payload.id, payload.eventName, payload.detail); } } catch (err) { console.error("error in eventBridgeHandler: ", err); } }, { args: [FFIType.u32, FFIType.cstring], returns: FFIType.void, threadsafe: true }); internalBridgeHandler = new JSCallback((_id, msg) => { try { const batchMessage = new CString(msg); const jsonBatch = JSON.parse(batchMessage.toString()); if (jsonBatch.id === "webviewEvent") { const { payload } = jsonBatch; webviewEventHandler(payload.id, payload.eventName, payload.detail); return; } jsonBatch.forEach((msgStr) => { const msgJson = JSON.parse(msgStr); if (msgJson.type === "message") { const handler = internalRpcHandlers.message[msgJson.id]; handler?.(msgJson.payload); } else if (msgJson.type === "request") { const hostWebview = BrowserView.getById(msgJson.hostWebviewId); const handler = internalRpcHandlers.request[msgJson.method]; const payload = handler?.(msgJson.params); const resultObj = { type: "response", id: msgJson.id, success: true, payload }; if (!hostWebview) { console.log("--->>> internal request in bun: NO HOST WEBVIEW FOUND"); return; } hostWebview.sendInternalMessageViaExecute(resultObj); } }); } catch (err) { console.error("error in internalBridgeHandler: ", err); } }, { args: [FFIType.u32, FFIType.cstring], returns: FFIType.void, threadsafe: true }); trayItemHandler = new JSCallback((id, action) => { const actionString = (new CString(action).toString() || "").trim(); const { action: actualAction, data } = deserializeMenuAction(actionString); const event = eventEmitter_default.events.tray.trayClicked({ id, action: actualAction, data }); eventEmitter_default.emitEvent(event); eventEmitter_default.emitEvent(event, id); }, { args: [FFIType.u32, FFIType.cstring], returns: FFIType.void, threadsafe: true }); applicationMenuHandler = new JSCallback((id, action) => { const actionString = new CString(action).toString(); const { action: actualAction, data } = deserializeMenuAction(actionString); const event = eventEmitter_default.events.app.applicationMenuClicked({ id, action: actualAction, data }); eventEmitter_default.emitEvent(event); }, { args: [FFIType.u32, FFIType.cstring], returns: FFIType.void, threadsafe: true }); contextMenuHandler = new JSCallback((_id, action) => { const actionString = new CString(action).toString(); const { action: actualAction, data } = deserializeMenuAction(actionString); const event = eventEmitter_default.events.app.contextMenuClicked({ action: actualAction, data }); eventEmitter_default.emitEvent(event); }, { args: [FFIType.u32, FFIType.cstring], returns: FFIType.void, threadsafe: true }); internalRpcHandlers = { request: { webviewTagInit: (params) => { const { hostWebviewId, windowId, renderer, html, preload, partition, frame, navigationRules, sandbox, transparent, passthrough } = params; const url = !params.url && !html ? "https://electrobun.dev" : params.url; const webviewForTag = new BrowserView({ url, html, preload, partition, frame, hostWebviewId, autoResize: false, windowId, renderer, navigationRules, sandbox, startTransparent: transparent, startPassthrough: passthrough }); return webviewForTag.id; }, webviewTagCanGoBack: (params) => { const { id } = params; const webviewPtr = BrowserView.getById(id)?.ptr; if (!webviewPtr) { console.error("no webview ptr"); return false; } return native.symbols.webviewCanGoBack(webviewPtr); }, webviewTagCanGoForward: (params) => { const { id } = params; const webviewPtr = BrowserView.getById(id)?.ptr; if (!webviewPtr) { console.error("no webview ptr"); return false; } return native.symbols.webviewCanGoForward(webviewPtr); } }, message: { webviewTagResize: (params) => { const browserView = BrowserView.getById(params.id); const webviewPtr = browserView?.ptr; if (!webviewPtr) { console.log("[Bun] ERROR: webviewTagResize - no webview ptr found for id:", params.id); return; } const { x, y, width, height } = params.frame; native.symbols.resizeWebview(webviewPtr, x, y, width, height, toCString(params.masks)); }, webviewTagUpdateSrc: (params) => { const webview = BrowserView.getById(params.id); if (!webview || !webview.ptr) { console.error(`webviewTagUpdateSrc: BrowserView not found or has no ptr for id ${params.id}`); return; } native.symbols.loadURLInWebView(webview.ptr, toCString(params.url)); }, webviewTagUpdateHtml: (params) => { const webview = BrowserView.getById(params.id); if (!webview || !webview.ptr) { console.error(`webviewTagUpdateHtml: BrowserView not found or has no ptr for id ${params.id}`); return; } native.symbols.setWebviewHTMLContent(webview.id, toCString(params.html)); webview.loadHTML(params.html); webview.html = params.html; }, webviewTagUpdatePreload: (params) => { const webview = BrowserView.getById(params.id); if (!webview || !webview.ptr) { console.error(`webviewTagUpdatePreload: BrowserView not found or has no ptr for id ${params.id}`); return; } native.symbols.updatePreloadScriptToWebView(webview.ptr, toCString("electrobun_custom_preload_script"), toCString(params.preload), true); }, webviewTagGoBack: (params) => { const webview = BrowserView.getById(params.id); if (!webview || !webview.ptr) { console.error(`webviewTagGoBack: BrowserView not found or has no ptr for id ${params.id}`); return; } native.symbols.webviewGoBack(webview.ptr); }, webviewTagGoForward: (params) => { const webview = BrowserView.getById(params.id); if (!webview || !webview.ptr) { console.error(`webviewTagGoForward: BrowserView not found or has no ptr for id ${params.id}`); return; } native.symbols.webviewGoForward(webview.ptr); }, webviewTagReload: (params) => { const webview = BrowserView.getById(params.id); if (!webview || !webview.ptr) { console.error(`webviewTagReload: BrowserView not found or has no ptr for id ${params.id}`); return; } native.symbols.webviewReload(webview.ptr); }, webviewTagRemove: (params) => { const webview = BrowserView.getById(params.id); if (!webview || !webview.ptr) { console.error(`webviewTagRemove: BrowserView not found or has no ptr for id ${params.id}`); return; } native.symbols.webviewRemove(webview.ptr); }, startWindowMove: (params) => { const window = BrowserWindow.getById(params.id); if (!window) return; native.symbols.startWindowMove(window.ptr); }, stopWindowMove: (_params) => { native.symbols.stopWindowMove(); }, webviewTagSetTransparent: (params) => { const webview = BrowserView.getById(params.id); if (!webview || !webview.ptr) { console.error(`webviewTagSetTransparent: BrowserView not found or has no ptr for id ${params.id}`); return; } native.symbols.webviewSetTransparent(webview.ptr, params.transparent); }, webviewTagSetPassthrough: (params) => { const webview = BrowserView.getById(params.id); if (!webview || !webview.ptr) { console.error(`webviewTagSetPassthrough: BrowserView not found or has no ptr for id ${params.id}`); return; } native.symbols.webviewSetPassthrough(webview.ptr, params.enablePassthrough); }, webviewTagSetHidden: (params) => { const webview = BrowserView.getById(params.id); if (!webview || !webview.ptr) { console.error(`webviewTagSetHidden: BrowserView not found or has no ptr for id ${params.id}`); return; } native.symbols.webviewSetHidden(webview.ptr, params.hidden); }, webviewTagSetNavigationRules: (params) => { const webview = BrowserView.getById(params.id); if (!webview || !webview.ptr) { console.error(`webviewTagSetNavigationRules: BrowserView not found or has no ptr for id ${params.id}`); return; } const rulesJson = JSON.stringify(params.rules); native.symbols.setWebviewNavigationRules(webview.ptr, toCString(rulesJson)); }, webviewTagFindInPage: (params) => { const webview = BrowserView.getById(params.id); if (!webview || !webview.ptr) { console.error(`webviewTagFindInPage: BrowserView not found or has no ptr for id ${params.id}`); return; } native.symbols.webviewFindInPage(webview.ptr, toCString(params.searchText), params.forward, params.matchCase); }, webviewTagStopFind: (params) => { const webview = BrowserView.getById(params.id); if (!webview || !webview.ptr) { console.error(`webviewTagStopFind: BrowserView not found or has no ptr for id ${params.id}`); return; } native.symbols.webviewStopFind(webview.ptr); }, webviewTagOpenDevTools: (params) => { const webview = BrowserView.getById(params.id); if (!webview || !webview.ptr) { console.error(`webviewTagOpenDevTools: BrowserView not found or has no ptr for id ${params.id}`); return; } native.symbols.webviewOpenDevTools(webview.ptr); }, webviewTagCloseDevTools: (params) => { const webview = BrowserView.getById(params.id); if (!webview || !webview.ptr) { console.error(`webviewTagCloseDevTools: BrowserView not found or has no ptr for id ${params.id}`); return; } native.symbols.webviewCloseDevTools(webview.ptr); }, webviewTagToggleDevTools: (params) => { const webview = BrowserView.getById(params.id); if (!webview || !webview.ptr) { console.error(`webviewTagToggleDevTools: BrowserView not found or has no ptr for id ${params.id}`); return; } native.symbols.webviewToggleDevTools(webview.ptr); }, webviewEvent: (params) => { console.log("-----------------+webviewEvent", params); } } }; }); // node_modules/electrobun/dist/api/bun/core/BrowserWindow.ts class BrowserWindow { id = nextWindowId++; ptr; title = "Electrobun"; state = "creating"; url = null; html = null; preload = null; renderer = "native"; transparent = false; navigationRules = null; sandbox = false; frame = { x: 0, y: 0, width: 800, height: 600 }; webviewId; constructor(options = defaultOptions2) { this.title = options.title || "New Window"; this.frame = options.frame ? { ...defaultOptions2.frame, ...options.frame } : { ...defaultOptions2.frame }; this.url = options.url || null; this.html = options.html || null; this.preload = options.preload || null; this.renderer = options.renderer || defaultOptions2.renderer; this.transparent = options.transparent ?? false; this.navigationRules = options.navigationRules || null; this.sandbox = options.sandbox ?? false; this.init(options); } init({ rpc, styleMask, titleBarStyle, transparent }) { this.ptr = ffi.request.createWindow({ id: this.id, title: this.title, url: this.url || "", frame: { width: this.frame.width, height: this.frame.height, x: this.frame.x, y: this.frame.y }, styleMask: { Borderless: false, Titled: true, Closable: true, Miniaturizable: true, Resizable: true, UnifiedTitleAndToolbar: false, FullScreen: false, FullSizeContentView: false, UtilityWindow: false, DocModalWindow: false, NonactivatingPanel: false, HUDWindow: false, ...styleMask || {}, ...titleBarStyle === "hiddenInset" ? { Titled: true, FullSizeContentView: true } : {}, ...titleBarStyle === "hidden" ? { Titled: false, FullSizeContentView: true } : {} }, titleBarStyle: titleBarStyle || "default", transparent: transparent ?? false }); BrowserWindowMap[this.id] = this; const webview = new BrowserView({ url: this.url, html: this.html, preload: this.preload, renderer: this.renderer, frame: { x: 0, y: 0, width: this.frame.width, height: this.frame.height }, rpc, windowId: this.id, navigationRules: this.navigationRules, sandbox: this.sandbox }); console.log("setting webviewId: ", webview.id); this.webviewId = webview.id; } get webview() { return BrowserView.getById(this.webviewId); } static getById(id) { return BrowserWindowMap[id]; } setTitle(title) { this.title = title; return ffi.request.setTitle({ winId: this.id, title }); } close() { return ffi.request.closeWindow({ winId: this.id }); } focus() { return ffi.request.focusWindow({ winId: this.id }); } show() { return ffi.request.focusWindow({ winId: this.id }); } minimize() { return ffi.request.minimizeWindow({ winId: this.id }); } unminimize() { return ffi.request.restoreWindow({ winId: this.id }); } isMinimized() { return ffi.request.isWindowMinimized({ winId: this.id }); } maximize() { return ffi.request.maximizeWindow({ winId: this.id }); } unmaximize() { return ffi.request.unmaximizeWindow({ winId: this.id }); } isMaximized() { return ffi.request.isWindowMaximized({ winId: this.id }); } setFullScreen(fullScreen) { return ffi.request.setWindowFullScreen({ winId: this.id, fullScreen }); } isFullScreen() { return ffi.request.isWindowFullScreen({ winId: this.id }); } setAlwaysOnTop(alwaysOnTop) { return ffi.request.setWindowAlwaysOnTop({ winId: this.id, alwaysOnTop }); } isAlwaysOnTop() { return ffi.request.isWindowAlwaysOnTop({ winId: this.id }); } setPosition(x, y) { this.frame.x = x; this.frame.y = y; return ffi.request.setWindowPosition({ winId: this.id, x, y }); } setSize(width, height) { this.frame.width = width; this.frame.height = height; return ffi.request.setWindowSize({ winId: this.id, width, height }); } setFrame(x, y, width, height) { this.frame = { x, y, width, height }; return ffi.request.setWindowFrame({ winId: this.id, x, y, width, height }); } getFrame() { const frame = ffi.request.getWindowFrame({ winId: this.id }); this.frame = frame; return frame; } getPosition() { const frame = this.getFrame(); return { x: frame.x, y: frame.y }; } getSize() { const frame = this.getFrame(); return { width: frame.width, height: frame.height }; } on(name, handler) { const specificName = `${name}-${this.id}`; eventEmitter_default.on(specificName, handler); } } var buildConfig3, nextWindowId = 1, defaultOptions2, BrowserWindowMap; var init_BrowserWindow = __esm(async () => { init_eventEmitter(); init_BuildConfig(); await __promiseAll([ init_native(), init_BrowserView(), init_Utils() ]); buildConfig3 = await BuildConfig.get(); defaultOptions2 = { title: "Electrobun", frame: { x: 0, y: 0, width: 800, height: 600 }, url: "https://electrobun.dev", html: null, preload: null, renderer: buildConfig3.defaultRenderer, titleBarStyle: "default", transparent: false, navigationRules: null, sandbox: false }; BrowserWindowMap = {}; eventEmitter_default.on("close", (event) => { const windowId = event.data.id; delete BrowserWindowMap[windowId]; for (const view of BrowserView.getAll()) { if (view.windowId === windowId) { view.remove(); } } const exitOnLastWindowClosed = buildConfig3.runtime?.exitOnLastWindowClosed ?? true; if (exitOnLastWindowClosed && Object.keys(BrowserWindowMap).length === 0) { quit(); } }); }); // node_modules/electrobun/dist/api/bun/index.ts init_eventEmitter(); await __promiseAll([ init_BrowserWindow(), init_BrowserView(), init_Tray() ]); // node_modules/electrobun/dist/api/bun/core/ApplicationMenu.ts init_eventEmitter(); await init_native(); var exports_ApplicationMenu = {}; __export(exports_ApplicationMenu, { setApplicationMenu: () => setApplicationMenu, on: () => on }); // node_modules/electrobun/dist/api/bun/core/menuRoles.ts var roleLabelMap = { about: "About", quit: "Quit", hide: "Hide", hideOthers: "Hide Others", showAll: "Show All", minimize: "Minimize", zoom: "Zoom", close: "Close", bringAllToFront: "Bring All To Front", cycleThroughWindows: "Cycle Through Windows", enterFullScreen: "Enter Full Screen", exitFullScreen: "Exit Full Screen", toggleFullScreen: "Toggle Full Screen", undo: "Undo", redo: "Redo", cut: "Cut", copy: "Copy", paste: "Paste", pasteAndMatchStyle: "Paste and Match Style", delete: "Delete", selectAll: "Select All", startSpeaking: "Start Speaking", stopSpeaking: "Stop Speaking", showHelp: "Show Help", moveForward: "Move Forward", moveBackward: "Move Backward", moveLeft: "Move Left", moveRight: "Move Right", moveUp: "Move Up", moveDown: "Move Down", moveWordForward: "Move Word Forward", moveWordBackward: "Move Word Backward", moveWordLeft: "Move Word Left", moveWordRight: "Move Word Right", moveToBeginningOfLine: "Move to Beginning of Line", moveToEndOfLine: "Move to End of Line", moveToLeftEndOfLine: "Move to Left End of Line", moveToRightEndOfLine: "Move to Right End of Line", moveToBeginningOfParagraph: "Move to Beginning of Paragraph", moveToEndOfParagraph: "Move to End of Paragraph", moveParagraphForward: "Move Paragraph Forward", moveParagraphBackward: "Move Paragraph Backward", moveToBeginningOfDocument: "Move to Beginning of Document", moveToEndOfDocument: "Move to End of Document", moveForwardAndModifySelection: "Move Forward and Modify Selection", moveBackwardAndModifySelection: "Move Backward and Modify Selection", moveLeftAndModifySelection: "Move Left and Modify Selection", moveRightAndModifySelection: "Move Right and Modify Selection", moveUpAndModifySelection: "Move Up and Modify Selection", moveDownAndModifySelection: "Move Down and Modify Selection", moveWordForwardAndModifySelection: "Move Word Forward and Modify Selection", moveWordBackwardAndModifySelection: "Move Word Backward and Modify Selection", moveWordLeftAndModifySelection: "Move Word Left and Modify Selection", moveWordRightAndModifySelection: "Move Word Right and Modify Selection", moveToBeginningOfLineAndModifySelection: "Move to Beginning of Line and Modify Selection", moveToEndOfLineAndModifySelection: "Move to End of Line and Modify Selection", moveToLeftEndOfLineAndModifySelection: "Move to Left End of Line and Modify Selection", moveToRightEndOfLineAndModifySelection: "Move to Right End of Line and Modify Selection", moveToBeginningOfParagraphAndModifySelection: "Move to Beginning of Paragraph and Modify Selection", moveToEndOfParagraphAndModifySelection: "Move to End of Paragraph and Modify Selection", moveParagraphForwardAndModifySelection: "Move Paragraph Forward and Modify Selection", moveParagraphBackwardAndModifySelection: "Move Paragraph Backward and Modify Selection", moveToBeginningOfDocumentAndModifySelection: "Move to Beginning of Document and Modify Selection", moveToEndOfDocumentAndModifySelection: "Move to End of Document and Modify Selection", pageUp: "Page Up", pageDown: "Page Down", pageUpAndModifySelection: "Page Up and Modify Selection", pageDownAndModifySelection: "Page Down and Modify Selection", scrollLineUp: "Scroll Line Up", scrollLineDown: "Scroll Line Down", scrollPageUp: "Scroll Page Up", scrollPageDown: "Scroll Page Down", scrollToBeginningOfDocument: "Scroll to Beginning of Document", scrollToEndOfDocument: "Scroll to End of Document", centerSelectionInVisibleArea: "Center Selection in Visible Area", deleteBackward: "Delete Backward", deleteForward: "Delete Forward", deleteBackwardByDecomposingPreviousCharacter: "Delete Backward by Decomposing Previous Character", deleteWordBackward: "Delete Word Backward", deleteWordForward: "Delete Word Forward", deleteToBeginningOfLine: "Delete to Beginning of Line", deleteToEndOfLine: "Delete to End of Line", deleteToBeginningOfParagraph: "Delete to Beginning of Paragraph", deleteToEndOfParagraph: "Delete to End of Paragraph", selectWord: "Select Word", selectLine: "Select Line", selectParagraph: "Select Paragraph", selectToMark: "Select to Mark", setMark: "Set Mark", swapWithMark: "Swap with Mark", deleteToMark: "Delete to Mark", capitalizeWord: "Capitalize Word", uppercaseWord: "Uppercase Word", lowercaseWord: "Lowercase Word", transpose: "Transpose", transposeWords: "Transpose Words", insertNewline: "Insert Newline", insertLineBreak: "Insert Line Break", insertParagraphSeparator: "Insert Paragraph Separator", insertTab: "Insert Tab", insertBacktab: "Insert Backtab", insertTabIgnoringFieldEditor: "Insert Tab Ignoring Field Editor", insertNewlineIgnoringFieldEditor: "Insert Newline Ignoring Field Editor", yank: "Yank", yankAndSelect: "Yank and Select", complete: "Complete", cancelOperation: "Cancel Operation", indent: "Indent" }; // node_modules/electrobun/dist/api/bun/core/ApplicationMenu.ts var setApplicationMenu = (menu) => { const menuWithDefaults = menuConfigWithDefaults2(menu); ffi.request.setApplicationMenu({ menuConfig: JSON.stringify(menuWithDefaults) }); }; var on = (name, handler) => { const specificName = `${name}`; eventEmitter_default.on(specificName, handler); }; var menuConfigWithDefaults2 = (menu) => { return menu.map((item) => { if (item.type === "divider" || item.type === "separator") { return { type: "divider" }; } else { const menuItem = item; const actionWithDataId = ffi.internal.serializeMenuAction(menuItem.action || "", menuItem.data); return { label: menuItem.label || roleLabelMap[menuItem.role] || "", type: menuItem.type || "normal", ...menuItem.role ? { role: menuItem.role } : { action: actionWithDataId }, enabled: menuItem.enabled === false ? false : true, checked: Boolean(menuItem.checked), hidden: Boolean(menuItem.hidden), tooltip: menuItem.tooltip || undefined, accelerator: menuItem.accelerator || undefined, ...menuItem.submenu ? { submenu: menuConfigWithDefaults2(menuItem.submenu) } : {} }; } }); }; // node_modules/electrobun/dist/api/bun/core/ContextMenu.ts init_eventEmitter(); await init_native(); var exports_ContextMenu = {}; __export(exports_ContextMenu, { showContextMenu: () => showContextMenu, on: () => on2 }); var showContextMenu = (menu) => { const menuWithDefaults = menuConfigWithDefaults3(menu); ffi.request.showContextMenu({ menuConfig: JSON.stringify(menuWithDefaults) }); }; var on2 = (name, handler) => { const specificName = `${name}`; eventEmitter_default.on(specificName, handler); }; var menuConfigWithDefaults3 = (menu) => { return menu.map((item) => { if (item.type === "divider" || item.type === "separator") { return { type: "divider" }; } else { const menuItem = item; const actionWithDataId = ffi.internal.serializeMenuAction(menuItem.action || "", menuItem.data); return { label: menuItem.label || roleLabelMap[menuItem.role] || "", type: menuItem.type || "normal", ...menuItem.role ? { role: menuItem.role } : { action: actionWithDataId }, enabled: menuItem.enabled === false ? false : true, checked: Boolean(menuItem.checked), hidden: Boolean(menuItem.hidden), tooltip: menuItem.tooltip || undefined, ...menuItem.accelerator ? { accelerator: menuItem.accelerator } : {}, ...menuItem.submenu ? { submenu: menuConfigWithDefaults3(menuItem.submenu) } : {} }; } }); }; // node_modules/electrobun/dist/api/bun/index.ts init_Paths(); init_BuildConfig(); await __promiseAll([ init_Updater(), init_Utils(), init_Socket(), init_native() ]); var Electrobun = { BrowserWindow, BrowserView, Tray, Updater, Utils: exports_Utils, ApplicationMenu: exports_ApplicationMenu, ContextMenu: exports_ContextMenu, GlobalShortcut, Screen, Session, BuildConfig, events: eventEmitter_default, PATHS: exports_Paths, Socket: exports_Socket }; var bun_default = Electrobun; // src/bun/index.ts import { join as join5 } from "path"; // src/bun/mainWindow.ts var win = null; function getAppUrl() { if (process.env.ELECTROBUN_START_URL || process.env.ELECTRON_START_URL) { return process.env.ELECTROBUN_START_URL || process.env.ELECTRON_START_URL || "http://localhost:5173"; } return "views://main/index.html"; } function setupWindowEvents(browserWindow, rpc) { if (!browserWindow) return; browserWindow.on?.("resize", () => { const webview = browserWindow.webview; if (webview?.rpc) { try { webview.rpc.windowState?.({ isMaximized: browserWindow.isMaximized?.(), isFullScreen: browserWindow.isFullScreen?.() }); } catch (_) {} } }); const sendWindowState = (state) => { const webview = browserWindow.webview; if (webview?.rpc) { try { webview.rpc.windowState?.(state); } catch (_) {} } }; browserWindow.on?.("maximize", () => sendWindowState({ isMaximized: true })); browserWindow.on?.("unmaximize", () => sendWindowState({ isMaximized: false })); browserWindow.on?.("enter-full-screen", () => sendWindowState({ isFullScreen: true })); browserWindow.on?.("leave-full-screen", () => sendWindowState({ isFullScreen: false })); } function createMainWindow(rpc) { const url = getAppUrl(); win = new BrowserWindow({ title: "Farm Control", url, frame: { width: 1200, height: 800 }, titleBarStyle: "hiddenInset", preload: "views://preload/index.js", rpc, styleMask: { Titled: true, Closable: true, Miniaturizable: true, Resizable: true, FullSizeContentView: true } }); setupWindowEvents(win, rpc); bun_default.events.on("close", () => { const wins = BrowserWindow.getAll?.() ?? []; if (wins.length <= 1) { if (process.platform !== "darwin") { exports_Utils.quit?.(); } } }); } function getMainWindow() { return win; } // src/bun/spotlightWindow.ts var spotlightWin = null; function getSpotlightRouteUrl() { const routePath = "/dashboard/electron/spotlightcontent"; if (process.env.ELECTROBUN_START_URL || process.env.ELECTRON_START_URL) { const base = String(process.env.ELECTROBUN_START_URL || process.env.ELECTRON_START_URL).replace(/\/$/, ""); return `${base}${routePath}`; } return `views://main/index.html#${routePath}`; } function openSpotlightContentWindow() { if (spotlightWin && !spotlightWin.isDestroyed?.()) { spotlightWin.show?.(); spotlightWin.focus?.(); return; } const target = getSpotlightRouteUrl(); spotlightWin = new BrowserWindow({ title: "Spotlight", url: target, frame: { width: 700, height: 40 }, titleBarStyle: "hidden", transparent: true, resizable: false }); spotlightWin.on?.("close", (e) => { if (e?.preventDefault) e.preventDefault(); if (spotlightWin && !spotlightWin.isDestroyed?.()) { spotlightWin.hide?.(); } }); spotlightWin.on?.("blur", () => { if (spotlightWin && !spotlightWin.isDestroyed?.()) { spotlightWin.hide?.(); } }); } function registerGlobalShortcuts() { try { const success = GlobalShortcut.register("Alt+Shift+Q", () => { openSpotlightContentWindow(); }); if (!success) { console.warn("[GlobalShortcut] Failed to register Alt+Shift+Q"); } } catch (e) { console.warn("[GlobalShortcut] Error:", e?.message); } } function setupSpotlightRPC(height) { if (!spotlightWin || spotlightWin.isDestroyed?.()) return false; try { const frame = spotlightWin.getFrame?.(); if (frame) { spotlightWin.setSize?.(frame.width, height); spotlightWin.center?.(); } return true; } catch (e) { console.warn("[spotlight] Failed to resize:", e?.message); return false; } } // src/bun/index.ts var AUTH_FILE = join5(exports_Utils.paths.userData, "auth-session.json"); async function readAuthSession() { try { const f = Bun.file(AUTH_FILE); if (!await f.exists()) return null; const raw = await f.text(); return JSON.parse(raw); } catch { return null; } } async function writeAuthSession(session) { try { await Bun.write(AUTH_FILE, JSON.stringify(session)); return true; } catch (e) { console.warn("[auth] Failed to write session:", e); return false; } } async function clearAuthSession() { try { const f = Bun.file(AUTH_FILE); if (await f.exists()) { await Bun.write(AUTH_FILE, "{}"); } return true; } catch (e) { console.warn("[auth] Failed to clear session:", e); return false; } } var farmControlRPC = BrowserView.defineRPC({ maxRequestTime: 5000, handlers: { requests: { osInfo: () => ({ platform: process.platform }), windowState: () => { const win2 = getMainWindow(); if (!win2 || win2.isDestroyed?.()) { return { isFullScreen: false, isMaximized: false }; } return { isFullScreen: win2.isFullScreen?.(), isMaximized: win2.isMaximized?.() }; }, openExternalUrl: ({ url }) => { exports_Utils.openExternal(url); }, openInternalUrl: ({ url }) => { const win2 = getMainWindow(); if (!win2 || win2.isDestroyed?.()) { createMainWindow(farmControlRPC); const newWin = getMainWindow(); if (newWin?.webview) { newWin.webview.on?.("dom-ready", () => { newWin.webview.rpc?.navigate?.({ url }); newWin.show?.(); newWin.focus?.(); }); } } else if (win2.webview) { win2.webview.rpc?.navigate?.({ url }); win2.show?.(); win2.focus?.(); } return true; }, windowControl: ({ action }) => { const win2 = getMainWindow(); if (!win2) return; switch (action) { case "minimize": win2.minimize?.(); break; case "maximize": if (win2.isMaximized?.()) { win2.unmaximize?.(); } else { win2.maximize?.(); } break; case "close": win2.close?.(); break; } }, authSessionGet: () => readAuthSession(), authSessionSet: ({ session }) => writeAuthSession(session), authSessionClear: () => clearAuthSession(), spotlightWindowResize: ({ height }) => setupSpotlightRPC(height) }, messages: {} } }); createMainWindow(farmControlRPC); registerGlobalShortcuts(); var env = "development".trim(); if (env === "development") { exports_ApplicationMenu.setApplicationMenu([ { label: "Developer", submenu: [ { label: "Toggle Developer Tools", accelerator: process.platform === "darwin" ? "Alt+Command+I" : "Ctrl+Shift+I", action: "toggle-devtools" } ] } ]); } else { exports_ApplicationMenu.setApplicationMenu([]); } bun_default.events.on("open-url", (e) => { const url = e.data.url; if (url.startsWith("farmcontrol://app")) { const redirectPath = url.replace("farmcontrol://app", "") || "/"; const win2 = getMainWindow(); if (win2?.webview) { win2.webview.rpc?.navigate?.({ url: redirectPath }); } } }); bun_default.events.on("before-quit", () => { GlobalShortcut.unregisterAll(); });