electrobun 1.12.3 → 1.13.0-beta.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -40,6 +40,10 @@ export type BrowserViewOptions<T = undefined> = {
40
40
  // Use for untrusted content (remote URLs) to prevent malicious sites from
41
41
  // accessing internal APIs, creating OOPIFs, or communicating with Bun
42
42
  sandbox: boolean;
43
+ // Set transparent on the AbstractView at creation (before first paint)
44
+ startTransparent: boolean;
45
+ // Set passthrough on the AbstractView at creation (before first paint)
46
+ startPassthrough: boolean;
43
47
  // renderer:
44
48
  };
45
49
 
@@ -93,6 +97,8 @@ export class BrowserView<T extends RPCWithTransport = RPCWithTransport> {
93
97
  navigationRules: string | null = null;
94
98
  // Sandbox mode disables RPC and only allows event emission (for untrusted content)
95
99
  sandbox: boolean = false;
100
+ startTransparent: boolean = false;
101
+ startPassthrough: boolean = false;
96
102
 
97
103
  constructor(options: Partial<BrowserViewOptions<T>> = defaultOptions) {
98
104
  // const rpc = options.rpc;
@@ -118,6 +124,8 @@ export class BrowserView<T extends RPCWithTransport = RPCWithTransport> {
118
124
  this.navigationRules = options.navigationRules || null;
119
125
  this.renderer = options.renderer ?? defaultOptions.renderer ?? "native";
120
126
  this.sandbox = options.sandbox ?? false;
127
+ this.startTransparent = options.startTransparent ?? false;
128
+ this.startPassthrough = options.startPassthrough ?? false;
121
129
 
122
130
  BrowserViewMap[this.id] = this;
123
131
  this.ptr = this.init() as Pointer;
@@ -167,6 +175,8 @@ export class BrowserView<T extends RPCWithTransport = RPCWithTransport> {
167
175
  autoResize: this.autoResize,
168
176
  navigationRules: this.navigationRules,
169
177
  sandbox: this.sandbox,
178
+ startTransparent: this.startTransparent,
179
+ startPassthrough: this.startPassthrough,
170
180
  // transparent is looked up from parent window in native.ts
171
181
  });
172
182
  }
@@ -1039,11 +1039,24 @@ del "%~f0"
1039
1039
 
1040
1040
  // Cross-platform app launch (Windows is handled above with its own update script)
1041
1041
  if (currentOS === "macos") {
1042
- // Use a detached shell so relaunch survives after killApp terminates the current process
1042
+ // Wait for the current process to fully exit before relaunching.
1043
+ // macOS 'open' on an already-running app just activates the existing
1044
+ // instance instead of launching a new one, so we must ensure the
1045
+ // current process has exited first. The detached shell survives our
1046
+ // exit and polls until the process is gone.
1047
+ const pid = process.pid;
1043
1048
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
1044
- Bun.spawn(["sh", "-c", `open "${runningAppBundlePath}" &`], {
1045
- detached: true,
1046
- } as any);
1049
+ Bun.spawn(
1050
+ [
1051
+ "sh",
1052
+ "-c",
1053
+ `while kill -0 ${pid} 2>/dev/null; do sleep 0.5; done; sleep 1; open "${runningAppBundlePath}"`,
1054
+ ],
1055
+ {
1056
+ detached: true,
1057
+ stdio: ["ignore", "ignore", "ignore"],
1058
+ } as any,
1059
+ );
1047
1060
  } else if (currentOS === "linux") {
1048
1061
  // On Linux, launch the launcher binary inside the app directory
1049
1062
  const launcherPath = join(runningAppBundlePath, "bin", "launcher");
@@ -2,7 +2,7 @@
2
2
  // Run "bun build.ts" or "bun build:dev" from the package folder to regenerate.
3
3
 
4
4
  // Full preload for trusted webviews (RPC, encryption, drag regions, webview tags)
5
- export const preloadScript = "(() => {\n // src/bun/preload/encryption.ts\n function base64ToUint8Array(base64) {\n return new Uint8Array(atob(base64).split(\"\").map((char) => char.charCodeAt(0)));\n }\n function uint8ArrayToBase64(uint8Array) {\n let binary = \"\";\n for (let i = 0;i < uint8Array.length; i++) {\n binary += String.fromCharCode(uint8Array[i]);\n }\n return btoa(binary);\n }\n async function generateKeyFromBytes(rawKey) {\n return await window.crypto.subtle.importKey(\"raw\", rawKey, { name: \"AES-GCM\" }, true, [\"encrypt\", \"decrypt\"]);\n }\n async function initEncryption() {\n const secretKey = await generateKeyFromBytes(new Uint8Array(window.__electrobunSecretKeyBytes));\n const encryptString = async (plaintext) => {\n const encoder = new TextEncoder;\n const encodedText = encoder.encode(plaintext);\n const iv = window.crypto.getRandomValues(new Uint8Array(12));\n const encryptedBuffer = await window.crypto.subtle.encrypt({ name: \"AES-GCM\", iv }, secretKey, encodedText);\n const encryptedData = new Uint8Array(encryptedBuffer.slice(0, -16));\n const tag = new Uint8Array(encryptedBuffer.slice(-16));\n return {\n encryptedData: uint8ArrayToBase64(encryptedData),\n iv: uint8ArrayToBase64(iv),\n tag: uint8ArrayToBase64(tag)\n };\n };\n const decryptString = async (encryptedDataB64, ivB64, tagB64) => {\n const encryptedData = base64ToUint8Array(encryptedDataB64);\n const iv = base64ToUint8Array(ivB64);\n const tag = base64ToUint8Array(tagB64);\n const combinedData = new Uint8Array(encryptedData.length + tag.length);\n combinedData.set(encryptedData);\n combinedData.set(tag, encryptedData.length);\n const decryptedBuffer = await window.crypto.subtle.decrypt({ name: \"AES-GCM\", iv }, secretKey, combinedData);\n const decoder = new TextDecoder;\n return decoder.decode(decryptedBuffer);\n };\n window.__electrobun_encrypt = encryptString;\n window.__electrobun_decrypt = decryptString;\n }\n\n // src/bun/preload/internalRpc.ts\n var pendingRequests = {};\n var requestId = 0;\n var isProcessingQueue = false;\n var sendQueue = [];\n function processQueue() {\n if (isProcessingQueue) {\n setTimeout(processQueue);\n return;\n }\n if (sendQueue.length === 0)\n return;\n isProcessingQueue = true;\n const batch = JSON.stringify(sendQueue);\n sendQueue.length = 0;\n window.__electrobunInternalBridge?.postMessage(batch);\n setTimeout(() => {\n isProcessingQueue = false;\n }, 2);\n }\n function send(type, payload) {\n sendQueue.push(JSON.stringify({ type: \"message\", id: type, payload }));\n processQueue();\n }\n function request(type, payload) {\n return new Promise((resolve, reject) => {\n const id = `req_${++requestId}_${Date.now()}`;\n pendingRequests[id] = { resolve, reject };\n sendQueue.push(JSON.stringify({\n type: \"request\",\n method: type,\n id,\n params: payload,\n hostWebviewId: window.__electrobunWebviewId\n }));\n processQueue();\n setTimeout(() => {\n if (pendingRequests[id]) {\n delete pendingRequests[id];\n reject(new Error(`Request timeout: ${type}`));\n }\n }, 1e4);\n });\n }\n function handleResponse(msg) {\n if (msg && msg.type === \"response\" && msg.id) {\n const pending = pendingRequests[msg.id];\n if (pending) {\n delete pendingRequests[msg.id];\n if (msg.success)\n pending.resolve(msg.payload);\n else\n pending.reject(msg.payload);\n }\n }\n }\n\n // src/bun/preload/dragRegions.ts\n function isAppRegionDrag(e) {\n const target = e.target;\n if (!target || !target.closest)\n return false;\n const draggableByStyle = target.closest('[style*=\"app-region\"][style*=\"drag\"]');\n const draggableByClass = target.closest(\".electrobun-webkit-app-region-drag\");\n return !!(draggableByStyle || draggableByClass);\n }\n function initDragRegions() {\n document.addEventListener(\"mousedown\", (e) => {\n if (isAppRegionDrag(e)) {\n send(\"startWindowMove\", { id: window.__electrobunWindowId });\n }\n });\n document.addEventListener(\"mouseup\", (e) => {\n if (isAppRegionDrag(e)) {\n send(\"stopWindowMove\", { id: window.__electrobunWindowId });\n }\n });\n }\n\n // src/bun/preload/webviewTag.ts\n var webviewRegistry = {};\n\n class ElectrobunWebviewTag extends HTMLElement {\n webviewId = null;\n maskSelectors = new Set;\n lastRect = { x: 0, y: 0, width: 0, height: 0 };\n resizeObserver = null;\n positionCheckLoop = null;\n transparent = false;\n passthroughEnabled = false;\n hidden = false;\n sandboxed = false;\n _eventListeners = {};\n constructor() {\n super();\n }\n connectedCallback() {\n requestAnimationFrame(() => this.initWebview());\n }\n disconnectedCallback() {\n if (this.webviewId !== null) {\n send(\"webviewTagRemove\", { id: this.webviewId });\n delete webviewRegistry[this.webviewId];\n }\n if (this.resizeObserver)\n this.resizeObserver.disconnect();\n if (this.positionCheckLoop)\n clearInterval(this.positionCheckLoop);\n }\n async initWebview() {\n const rect = this.getBoundingClientRect();\n this.lastRect = {\n x: rect.x,\n y: rect.y,\n width: rect.width,\n height: rect.height\n };\n const url = this.getAttribute(\"src\");\n const html = this.getAttribute(\"html\");\n const preload = this.getAttribute(\"preload\");\n const partition = this.getAttribute(\"partition\");\n const renderer = this.getAttribute(\"renderer\") || \"native\";\n const masks = this.getAttribute(\"masks\");\n const sandbox = this.hasAttribute(\"sandbox\");\n this.sandboxed = sandbox;\n if (masks) {\n masks.split(\",\").forEach((s) => this.maskSelectors.add(s.trim()));\n }\n try {\n const webviewId = await request(\"webviewTagInit\", {\n hostWebviewId: window.__electrobunWebviewId,\n windowId: window.__electrobunWindowId,\n renderer,\n url,\n html,\n preload,\n partition,\n frame: {\n width: rect.width,\n height: rect.height,\n x: rect.x,\n y: rect.y\n },\n navigationRules: null,\n sandbox\n });\n this.webviewId = webviewId;\n this.id = `electrobun-webview-${webviewId}`;\n webviewRegistry[webviewId] = this;\n this.setupObservers();\n this.syncDimensions(true);\n } catch (err) {\n console.error(\"Failed to init webview:\", err);\n }\n }\n setupObservers() {\n this.resizeObserver = new ResizeObserver(() => this.syncDimensions());\n this.resizeObserver.observe(this);\n this.positionCheckLoop = setInterval(() => this.syncDimensions(), 100);\n }\n syncDimensions(force = false) {\n if (this.webviewId === null)\n return;\n const rect = this.getBoundingClientRect();\n const newRect = {\n x: rect.x,\n y: rect.y,\n width: rect.width,\n height: rect.height\n };\n if (!force && newRect.x === this.lastRect.x && newRect.y === this.lastRect.y && newRect.width === this.lastRect.width && newRect.height === this.lastRect.height) {\n return;\n }\n this.lastRect = newRect;\n const masks = [];\n this.maskSelectors.forEach((selector) => {\n try {\n document.querySelectorAll(selector).forEach((el) => {\n const mr = el.getBoundingClientRect();\n masks.push({\n x: mr.x - rect.x,\n y: mr.y - rect.y,\n width: mr.width,\n height: mr.height\n });\n });\n } catch (_e) {}\n });\n send(\"webviewTagResize\", {\n id: this.webviewId,\n frame: newRect,\n masks: JSON.stringify(masks)\n });\n }\n loadURL(url) {\n if (this.webviewId === null)\n return;\n this.setAttribute(\"src\", url);\n send(\"webviewTagUpdateSrc\", { id: this.webviewId, url });\n }\n loadHTML(html) {\n if (this.webviewId === null)\n return;\n send(\"webviewTagUpdateHtml\", { id: this.webviewId, html });\n }\n reload() {\n if (this.webviewId !== null)\n send(\"webviewTagReload\", { id: this.webviewId });\n }\n goBack() {\n if (this.webviewId !== null)\n send(\"webviewTagGoBack\", { id: this.webviewId });\n }\n goForward() {\n if (this.webviewId !== null)\n send(\"webviewTagGoForward\", { id: this.webviewId });\n }\n async canGoBack() {\n if (this.webviewId === null)\n return false;\n return await request(\"webviewTagCanGoBack\", {\n id: this.webviewId\n });\n }\n async canGoForward() {\n if (this.webviewId === null)\n return false;\n return await request(\"webviewTagCanGoForward\", {\n id: this.webviewId\n });\n }\n toggleTransparent(value) {\n if (this.webviewId === null)\n return;\n this.transparent = value !== undefined ? value : !this.transparent;\n send(\"webviewTagSetTransparent\", {\n id: this.webviewId,\n transparent: this.transparent\n });\n }\n togglePassthrough(value) {\n if (this.webviewId === null)\n return;\n this.passthroughEnabled = value !== undefined ? value : !this.passthroughEnabled;\n send(\"webviewTagSetPassthrough\", {\n id: this.webviewId,\n enablePassthrough: this.passthroughEnabled\n });\n }\n toggleHidden(value) {\n if (this.webviewId === null)\n return;\n this.hidden = value !== undefined ? value : !this.hidden;\n send(\"webviewTagSetHidden\", { id: this.webviewId, hidden: this.hidden });\n }\n addMaskSelector(selector) {\n this.maskSelectors.add(selector);\n this.syncDimensions(true);\n }\n removeMaskSelector(selector) {\n this.maskSelectors.delete(selector);\n this.syncDimensions(true);\n }\n setNavigationRules(rules) {\n if (this.webviewId !== null) {\n send(\"webviewTagSetNavigationRules\", { id: this.webviewId, rules });\n }\n }\n findInPage(searchText, options) {\n if (this.webviewId === null)\n return;\n const forward = options?.forward !== false;\n const matchCase = options?.matchCase || false;\n send(\"webviewTagFindInPage\", {\n id: this.webviewId,\n searchText,\n forward,\n matchCase\n });\n }\n stopFindInPage() {\n if (this.webviewId !== null)\n send(\"webviewTagStopFind\", { id: this.webviewId });\n }\n openDevTools() {\n if (this.webviewId !== null)\n send(\"webviewTagOpenDevTools\", { id: this.webviewId });\n }\n closeDevTools() {\n if (this.webviewId !== null)\n send(\"webviewTagCloseDevTools\", { id: this.webviewId });\n }\n toggleDevTools() {\n if (this.webviewId !== null)\n send(\"webviewTagToggleDevTools\", { id: this.webviewId });\n }\n on(event, listener) {\n if (!this._eventListeners[event])\n this._eventListeners[event] = [];\n this._eventListeners[event].push(listener);\n }\n off(event, listener) {\n if (!this._eventListeners[event])\n return;\n const idx = this._eventListeners[event].indexOf(listener);\n if (idx !== -1)\n this._eventListeners[event].splice(idx, 1);\n }\n emit(event, detail) {\n const listeners = this._eventListeners[event];\n if (listeners) {\n const customEvent = new CustomEvent(event, { detail });\n listeners.forEach((fn) => fn(customEvent));\n }\n }\n get src() {\n return this.getAttribute(\"src\");\n }\n set src(value) {\n if (value) {\n this.setAttribute(\"src\", value);\n if (this.webviewId !== null)\n this.loadURL(value);\n } else {\n this.removeAttribute(\"src\");\n }\n }\n get html() {\n return this.getAttribute(\"html\");\n }\n set html(value) {\n if (value) {\n this.setAttribute(\"html\", value);\n if (this.webviewId !== null)\n this.loadHTML(value);\n } else {\n this.removeAttribute(\"html\");\n }\n }\n get preload() {\n return this.getAttribute(\"preload\");\n }\n set preload(value) {\n if (value)\n this.setAttribute(\"preload\", value);\n else\n this.removeAttribute(\"preload\");\n }\n get renderer() {\n return this.getAttribute(\"renderer\") || \"native\";\n }\n set renderer(value) {\n this.setAttribute(\"renderer\", value);\n }\n get sandbox() {\n return this.sandboxed;\n }\n }\n function initWebviewTag() {\n if (!customElements.get(\"electrobun-webview\")) {\n customElements.define(\"electrobun-webview\", ElectrobunWebviewTag);\n }\n const injectStyles = () => {\n const style = document.createElement(\"style\");\n style.textContent = `\nelectrobun-webview {\n\tdisplay: block;\n\twidth: 800px;\n\theight: 300px;\n\tbackground: #fff;\n\tbackground-repeat: no-repeat !important;\n\toverflow: hidden;\n}\n`;\n if (document.head?.firstChild) {\n document.head.insertBefore(style, document.head.firstChild);\n } else if (document.head) {\n document.head.appendChild(style);\n }\n };\n if (document.head) {\n injectStyles();\n } else {\n document.addEventListener(\"DOMContentLoaded\", injectStyles);\n }\n }\n\n // src/bun/preload/events.ts\n function emitWebviewEvent(eventName, detail) {\n setTimeout(() => {\n const bridge = window.__electrobunEventBridge || window.__electrobunInternalBridge;\n bridge?.postMessage(JSON.stringify({\n id: \"webviewEvent\",\n type: \"message\",\n payload: {\n id: window.__electrobunWebviewId,\n eventName,\n detail\n }\n }));\n });\n }\n function initLifecycleEvents() {\n window.addEventListener(\"load\", () => {\n if (window === window.top) {\n emitWebviewEvent(\"dom-ready\", document.location.href);\n }\n });\n window.addEventListener(\"popstate\", () => {\n emitWebviewEvent(\"did-navigate-in-page\", window.location.href);\n });\n window.addEventListener(\"hashchange\", () => {\n emitWebviewEvent(\"did-navigate-in-page\", window.location.href);\n });\n }\n var cmdKeyHeld = false;\n var cmdKeyTimestamp = 0;\n var CMD_KEY_THRESHOLD_MS = 500;\n function isCmdHeld() {\n if (cmdKeyHeld)\n return true;\n return Date.now() - cmdKeyTimestamp < CMD_KEY_THRESHOLD_MS && cmdKeyTimestamp > 0;\n }\n function initCmdClickHandling() {\n window.addEventListener(\"keydown\", (event) => {\n if (event.key === \"Meta\" || event.metaKey) {\n cmdKeyHeld = true;\n cmdKeyTimestamp = Date.now();\n }\n }, true);\n window.addEventListener(\"keyup\", (event) => {\n if (event.key === \"Meta\") {\n cmdKeyHeld = false;\n cmdKeyTimestamp = Date.now();\n }\n }, true);\n window.addEventListener(\"blur\", () => {\n cmdKeyHeld = false;\n });\n window.addEventListener(\"click\", (event) => {\n if (event.metaKey || event.ctrlKey) {\n const anchor = event.target?.closest?.(\"a\");\n if (anchor && anchor.href) {\n event.preventDefault();\n event.stopPropagation();\n event.stopImmediatePropagation();\n emitWebviewEvent(\"new-window-open\", JSON.stringify({\n url: anchor.href,\n isCmdClick: true,\n isSPANavigation: false\n }));\n }\n }\n }, true);\n }\n function initSPANavigationInterception() {\n const originalPushState = history.pushState;\n const originalReplaceState = history.replaceState;\n history.pushState = function(state, title, url) {\n if (isCmdHeld() && url) {\n const resolvedUrl = new URL(String(url), window.location.href).href;\n emitWebviewEvent(\"new-window-open\", JSON.stringify({\n url: resolvedUrl,\n isCmdClick: true,\n isSPANavigation: true\n }));\n return;\n }\n return originalPushState.apply(this, [state, title, url]);\n };\n history.replaceState = function(state, title, url) {\n if (isCmdHeld() && url) {\n const resolvedUrl = new URL(String(url), window.location.href).href;\n emitWebviewEvent(\"new-window-open\", JSON.stringify({\n url: resolvedUrl,\n isCmdClick: true,\n isSPANavigation: true\n }));\n return;\n }\n return originalReplaceState.apply(this, [state, title, url]);\n };\n }\n function initOverscrollPrevention() {\n document.addEventListener(\"DOMContentLoaded\", () => {\n const style = document.createElement(\"style\");\n style.type = \"text/css\";\n style.appendChild(document.createTextNode(\"html, body { overscroll-behavior: none; }\"));\n document.head.appendChild(style);\n });\n }\n\n // src/bun/preload/index.ts\n initEncryption().catch((err) => console.error(\"Failed to initialize encryption:\", err));\n var internalMessageHandler = (msg) => {\n handleResponse(msg);\n };\n if (!window.__electrobun) {\n window.__electrobun = {\n receiveInternalMessageFromBun: internalMessageHandler,\n receiveMessageFromBun: (msg) => {\n console.log(\"receiveMessageFromBun (no handler):\", msg);\n }\n };\n } else {\n window.__electrobun.receiveInternalMessageFromBun = internalMessageHandler;\n window.__electrobun.receiveMessageFromBun = (msg) => {\n console.log(\"receiveMessageFromBun (no handler):\", msg);\n };\n }\n window.__electrobunSendToHost = (message) => {\n emitWebviewEvent(\"host-message\", JSON.stringify(message));\n };\n initLifecycleEvents();\n initCmdClickHandling();\n initSPANavigationInterception();\n initOverscrollPrevention();\n initDragRegions();\n initWebviewTag();\n})();\n";
5
+ export const preloadScript = "(() => {\n // src/bun/preload/encryption.ts\n function base64ToUint8Array(base64) {\n return new Uint8Array(atob(base64).split(\"\").map((char) => char.charCodeAt(0)));\n }\n function uint8ArrayToBase64(uint8Array) {\n let binary = \"\";\n for (let i = 0;i < uint8Array.length; i++) {\n binary += String.fromCharCode(uint8Array[i]);\n }\n return btoa(binary);\n }\n async function generateKeyFromBytes(rawKey) {\n return await window.crypto.subtle.importKey(\"raw\", rawKey, { name: \"AES-GCM\" }, true, [\"encrypt\", \"decrypt\"]);\n }\n async function initEncryption() {\n const secretKey = await generateKeyFromBytes(new Uint8Array(window.__electrobunSecretKeyBytes));\n const encryptString = async (plaintext) => {\n const encoder = new TextEncoder;\n const encodedText = encoder.encode(plaintext);\n const iv = window.crypto.getRandomValues(new Uint8Array(12));\n const encryptedBuffer = await window.crypto.subtle.encrypt({ name: \"AES-GCM\", iv }, secretKey, encodedText);\n const encryptedData = new Uint8Array(encryptedBuffer.slice(0, -16));\n const tag = new Uint8Array(encryptedBuffer.slice(-16));\n return {\n encryptedData: uint8ArrayToBase64(encryptedData),\n iv: uint8ArrayToBase64(iv),\n tag: uint8ArrayToBase64(tag)\n };\n };\n const decryptString = async (encryptedDataB64, ivB64, tagB64) => {\n const encryptedData = base64ToUint8Array(encryptedDataB64);\n const iv = base64ToUint8Array(ivB64);\n const tag = base64ToUint8Array(tagB64);\n const combinedData = new Uint8Array(encryptedData.length + tag.length);\n combinedData.set(encryptedData);\n combinedData.set(tag, encryptedData.length);\n const decryptedBuffer = await window.crypto.subtle.decrypt({ name: \"AES-GCM\", iv }, secretKey, combinedData);\n const decoder = new TextDecoder;\n return decoder.decode(decryptedBuffer);\n };\n window.__electrobun_encrypt = encryptString;\n window.__electrobun_decrypt = decryptString;\n }\n\n // src/bun/preload/internalRpc.ts\n var pendingRequests = {};\n var requestId = 0;\n var isProcessingQueue = false;\n var sendQueue = [];\n function processQueue() {\n if (isProcessingQueue) {\n setTimeout(processQueue);\n return;\n }\n if (sendQueue.length === 0)\n return;\n isProcessingQueue = true;\n const batch = JSON.stringify(sendQueue);\n sendQueue.length = 0;\n window.__electrobunInternalBridge?.postMessage(batch);\n setTimeout(() => {\n isProcessingQueue = false;\n }, 2);\n }\n function send(type, payload) {\n sendQueue.push(JSON.stringify({ type: \"message\", id: type, payload }));\n processQueue();\n }\n function request(type, payload) {\n return new Promise((resolve, reject) => {\n const id = `req_${++requestId}_${Date.now()}`;\n pendingRequests[id] = { resolve, reject };\n sendQueue.push(JSON.stringify({\n type: \"request\",\n method: type,\n id,\n params: payload,\n hostWebviewId: window.__electrobunWebviewId\n }));\n processQueue();\n setTimeout(() => {\n if (pendingRequests[id]) {\n delete pendingRequests[id];\n reject(new Error(`Request timeout: ${type}`));\n }\n }, 1e4);\n });\n }\n function handleResponse(msg) {\n if (msg && msg.type === \"response\" && msg.id) {\n const pending = pendingRequests[msg.id];\n if (pending) {\n delete pendingRequests[msg.id];\n if (msg.success)\n pending.resolve(msg.payload);\n else\n pending.reject(msg.payload);\n }\n }\n }\n\n // src/bun/preload/dragRegions.ts\n function isAppRegionDrag(e) {\n const target = e.target;\n if (!target || !target.closest)\n return false;\n const draggableByStyle = target.closest('[style*=\"app-region\"][style*=\"drag\"]');\n const draggableByClass = target.closest(\".electrobun-webkit-app-region-drag\");\n return !!(draggableByStyle || draggableByClass);\n }\n function initDragRegions() {\n document.addEventListener(\"mousedown\", (e) => {\n if (isAppRegionDrag(e)) {\n send(\"startWindowMove\", { id: window.__electrobunWindowId });\n }\n });\n document.addEventListener(\"mouseup\", (e) => {\n if (isAppRegionDrag(e)) {\n send(\"stopWindowMove\", { id: window.__electrobunWindowId });\n }\n });\n }\n\n // src/bun/preload/webviewTag.ts\n var webviewRegistry = {};\n\n class ElectrobunWebviewTag extends HTMLElement {\n webviewId = null;\n maskSelectors = new Set;\n lastRect = { x: 0, y: 0, width: 0, height: 0 };\n resizeObserver = null;\n positionCheckLoop = null;\n transparent = false;\n passthroughEnabled = false;\n hidden = false;\n sandboxed = false;\n _eventListeners = {};\n constructor() {\n super();\n }\n connectedCallback() {\n requestAnimationFrame(() => this.initWebview());\n }\n disconnectedCallback() {\n if (this.webviewId !== null) {\n send(\"webviewTagRemove\", { id: this.webviewId });\n delete webviewRegistry[this.webviewId];\n }\n if (this.resizeObserver)\n this.resizeObserver.disconnect();\n if (this.positionCheckLoop)\n clearInterval(this.positionCheckLoop);\n }\n async initWebview() {\n const rect = this.getBoundingClientRect();\n this.lastRect = {\n x: rect.x,\n y: rect.y,\n width: rect.width,\n height: rect.height\n };\n const url = this.getAttribute(\"src\");\n const html = this.getAttribute(\"html\");\n const preload = this.getAttribute(\"preload\");\n const partition = this.getAttribute(\"partition\");\n const renderer = this.getAttribute(\"renderer\") || \"native\";\n const masks = this.getAttribute(\"masks\");\n const sandbox = this.hasAttribute(\"sandbox\");\n this.sandboxed = sandbox;\n const transparent = this.hasAttribute(\"transparent\");\n const passthrough = this.hasAttribute(\"passthrough\");\n this.transparent = transparent;\n this.passthroughEnabled = passthrough;\n if (transparent)\n this.style.opacity = \"0\";\n if (passthrough)\n this.style.pointerEvents = \"none\";\n if (masks) {\n masks.split(\",\").forEach((s) => this.maskSelectors.add(s.trim()));\n }\n try {\n const webviewId = await request(\"webviewTagInit\", {\n hostWebviewId: window.__electrobunWebviewId,\n windowId: window.__electrobunWindowId,\n renderer,\n url,\n html,\n preload,\n partition,\n frame: {\n width: rect.width,\n height: rect.height,\n x: rect.x,\n y: rect.y\n },\n navigationRules: null,\n sandbox,\n transparent,\n passthrough\n });\n this.webviewId = webviewId;\n this.id = `electrobun-webview-${webviewId}`;\n webviewRegistry[webviewId] = this;\n this.setupObservers();\n this.syncDimensions(true);\n requestAnimationFrame(() => {\n Object.values(webviewRegistry).forEach((webview) => {\n if (webview !== this && webview.webviewId !== null) {\n webview.syncDimensions(true);\n }\n });\n });\n } catch (err) {\n console.error(\"Failed to init webview:\", err);\n }\n }\n setupObservers() {\n this.resizeObserver = new ResizeObserver(() => this.syncDimensions());\n this.resizeObserver.observe(this);\n this.positionCheckLoop = setInterval(() => this.syncDimensions(), 100);\n }\n syncDimensions(force = false) {\n if (this.webviewId === null)\n return;\n const rect = this.getBoundingClientRect();\n const newRect = {\n x: rect.x,\n y: rect.y,\n width: rect.width,\n height: rect.height\n };\n if (!force && newRect.x === this.lastRect.x && newRect.y === this.lastRect.y && newRect.width === this.lastRect.width && newRect.height === this.lastRect.height) {\n return;\n }\n this.lastRect = newRect;\n const masks = [];\n this.maskSelectors.forEach((selector) => {\n try {\n document.querySelectorAll(selector).forEach((el) => {\n const mr = el.getBoundingClientRect();\n masks.push({\n x: mr.x - rect.x,\n y: mr.y - rect.y,\n width: mr.width,\n height: mr.height\n });\n });\n } catch (_e) {}\n });\n send(\"webviewTagResize\", {\n id: this.webviewId,\n frame: newRect,\n masks: JSON.stringify(masks)\n });\n }\n loadURL(url) {\n if (this.webviewId === null)\n return;\n this.setAttribute(\"src\", url);\n send(\"webviewTagUpdateSrc\", { id: this.webviewId, url });\n }\n loadHTML(html) {\n if (this.webviewId === null)\n return;\n send(\"webviewTagUpdateHtml\", { id: this.webviewId, html });\n }\n reload() {\n if (this.webviewId !== null)\n send(\"webviewTagReload\", { id: this.webviewId });\n }\n goBack() {\n if (this.webviewId !== null)\n send(\"webviewTagGoBack\", { id: this.webviewId });\n }\n goForward() {\n if (this.webviewId !== null)\n send(\"webviewTagGoForward\", { id: this.webviewId });\n }\n async canGoBack() {\n if (this.webviewId === null)\n return false;\n return await request(\"webviewTagCanGoBack\", {\n id: this.webviewId\n });\n }\n async canGoForward() {\n if (this.webviewId === null)\n return false;\n return await request(\"webviewTagCanGoForward\", {\n id: this.webviewId\n });\n }\n toggleTransparent(value) {\n if (this.webviewId === null)\n return;\n this.transparent = value !== undefined ? value : !this.transparent;\n this.style.opacity = this.transparent ? \"0\" : \"\";\n send(\"webviewTagSetTransparent\", {\n id: this.webviewId,\n transparent: this.transparent\n });\n }\n togglePassthrough(value) {\n if (this.webviewId === null)\n return;\n this.passthroughEnabled = value !== undefined ? value : !this.passthroughEnabled;\n this.style.pointerEvents = this.passthroughEnabled ? \"none\" : \"\";\n send(\"webviewTagSetPassthrough\", {\n id: this.webviewId,\n enablePassthrough: this.passthroughEnabled\n });\n }\n toggleHidden(value) {\n if (this.webviewId === null)\n return;\n this.hidden = value !== undefined ? value : !this.hidden;\n send(\"webviewTagSetHidden\", { id: this.webviewId, hidden: this.hidden });\n }\n addMaskSelector(selector) {\n this.maskSelectors.add(selector);\n this.syncDimensions(true);\n }\n removeMaskSelector(selector) {\n this.maskSelectors.delete(selector);\n this.syncDimensions(true);\n }\n setNavigationRules(rules) {\n if (this.webviewId !== null) {\n send(\"webviewTagSetNavigationRules\", { id: this.webviewId, rules });\n }\n }\n findInPage(searchText, options) {\n if (this.webviewId === null)\n return;\n const forward = options?.forward !== false;\n const matchCase = options?.matchCase || false;\n send(\"webviewTagFindInPage\", {\n id: this.webviewId,\n searchText,\n forward,\n matchCase\n });\n }\n stopFindInPage() {\n if (this.webviewId !== null)\n send(\"webviewTagStopFind\", { id: this.webviewId });\n }\n openDevTools() {\n if (this.webviewId !== null)\n send(\"webviewTagOpenDevTools\", { id: this.webviewId });\n }\n closeDevTools() {\n if (this.webviewId !== null)\n send(\"webviewTagCloseDevTools\", { id: this.webviewId });\n }\n toggleDevTools() {\n if (this.webviewId !== null)\n send(\"webviewTagToggleDevTools\", { id: this.webviewId });\n }\n on(event, listener) {\n if (!this._eventListeners[event])\n this._eventListeners[event] = [];\n this._eventListeners[event].push(listener);\n }\n off(event, listener) {\n if (!this._eventListeners[event])\n return;\n const idx = this._eventListeners[event].indexOf(listener);\n if (idx !== -1)\n this._eventListeners[event].splice(idx, 1);\n }\n emit(event, detail) {\n const listeners = this._eventListeners[event];\n if (listeners) {\n const customEvent = new CustomEvent(event, { detail });\n listeners.forEach((fn) => fn(customEvent));\n }\n }\n get src() {\n return this.getAttribute(\"src\");\n }\n set src(value) {\n if (value) {\n this.setAttribute(\"src\", value);\n if (this.webviewId !== null)\n this.loadURL(value);\n } else {\n this.removeAttribute(\"src\");\n }\n }\n get html() {\n return this.getAttribute(\"html\");\n }\n set html(value) {\n if (value) {\n this.setAttribute(\"html\", value);\n if (this.webviewId !== null)\n this.loadHTML(value);\n } else {\n this.removeAttribute(\"html\");\n }\n }\n get preload() {\n return this.getAttribute(\"preload\");\n }\n set preload(value) {\n if (value)\n this.setAttribute(\"preload\", value);\n else\n this.removeAttribute(\"preload\");\n }\n get renderer() {\n return this.getAttribute(\"renderer\") || \"native\";\n }\n set renderer(value) {\n this.setAttribute(\"renderer\", value);\n }\n get sandbox() {\n return this.sandboxed;\n }\n }\n function initWebviewTag() {\n if (!customElements.get(\"electrobun-webview\")) {\n customElements.define(\"electrobun-webview\", ElectrobunWebviewTag);\n }\n const injectStyles = () => {\n const style = document.createElement(\"style\");\n style.textContent = `\nelectrobun-webview {\n\tdisplay: block;\n\twidth: 800px;\n\theight: 300px;\n\tbackground: #fff;\n\tbackground-repeat: no-repeat !important;\n\toverflow: hidden;\n}\n`;\n if (document.head?.firstChild) {\n document.head.insertBefore(style, document.head.firstChild);\n } else if (document.head) {\n document.head.appendChild(style);\n }\n };\n if (document.head) {\n injectStyles();\n } else {\n document.addEventListener(\"DOMContentLoaded\", injectStyles);\n }\n }\n\n // src/bun/preload/events.ts\n function emitWebviewEvent(eventName, detail) {\n setTimeout(() => {\n const bridge = window.__electrobunEventBridge || window.__electrobunInternalBridge;\n bridge?.postMessage(JSON.stringify({\n id: \"webviewEvent\",\n type: \"message\",\n payload: {\n id: window.__electrobunWebviewId,\n eventName,\n detail\n }\n }));\n });\n }\n function initLifecycleEvents() {\n window.addEventListener(\"load\", () => {\n if (window === window.top) {\n emitWebviewEvent(\"dom-ready\", document.location.href);\n }\n });\n window.addEventListener(\"popstate\", () => {\n emitWebviewEvent(\"did-navigate-in-page\", window.location.href);\n });\n window.addEventListener(\"hashchange\", () => {\n emitWebviewEvent(\"did-navigate-in-page\", window.location.href);\n });\n }\n var cmdKeyHeld = false;\n var cmdKeyTimestamp = 0;\n var CMD_KEY_THRESHOLD_MS = 500;\n function isCmdHeld() {\n if (cmdKeyHeld)\n return true;\n return Date.now() - cmdKeyTimestamp < CMD_KEY_THRESHOLD_MS && cmdKeyTimestamp > 0;\n }\n function initCmdClickHandling() {\n window.addEventListener(\"keydown\", (event) => {\n if (event.key === \"Meta\" || event.metaKey) {\n cmdKeyHeld = true;\n cmdKeyTimestamp = Date.now();\n }\n }, true);\n window.addEventListener(\"keyup\", (event) => {\n if (event.key === \"Meta\") {\n cmdKeyHeld = false;\n cmdKeyTimestamp = Date.now();\n }\n }, true);\n window.addEventListener(\"blur\", () => {\n cmdKeyHeld = false;\n });\n window.addEventListener(\"click\", (event) => {\n if (event.metaKey || event.ctrlKey) {\n const anchor = event.target?.closest?.(\"a\");\n if (anchor && anchor.href) {\n event.preventDefault();\n event.stopPropagation();\n event.stopImmediatePropagation();\n emitWebviewEvent(\"new-window-open\", JSON.stringify({\n url: anchor.href,\n isCmdClick: true,\n isSPANavigation: false\n }));\n }\n }\n }, true);\n }\n function initSPANavigationInterception() {\n const originalPushState = history.pushState;\n const originalReplaceState = history.replaceState;\n history.pushState = function(state, title, url) {\n if (isCmdHeld() && url) {\n const resolvedUrl = new URL(String(url), window.location.href).href;\n emitWebviewEvent(\"new-window-open\", JSON.stringify({\n url: resolvedUrl,\n isCmdClick: true,\n isSPANavigation: true\n }));\n return;\n }\n return originalPushState.apply(this, [state, title, url]);\n };\n history.replaceState = function(state, title, url) {\n if (isCmdHeld() && url) {\n const resolvedUrl = new URL(String(url), window.location.href).href;\n emitWebviewEvent(\"new-window-open\", JSON.stringify({\n url: resolvedUrl,\n isCmdClick: true,\n isSPANavigation: true\n }));\n return;\n }\n return originalReplaceState.apply(this, [state, title, url]);\n };\n }\n function initOverscrollPrevention() {\n document.addEventListener(\"DOMContentLoaded\", () => {\n const style = document.createElement(\"style\");\n style.type = \"text/css\";\n style.appendChild(document.createTextNode(\"html, body { overscroll-behavior: none; }\"));\n document.head.appendChild(style);\n });\n }\n\n // src/bun/preload/index.ts\n initEncryption().catch((err) => console.error(\"Failed to initialize encryption:\", err));\n var internalMessageHandler = (msg) => {\n handleResponse(msg);\n };\n if (!window.__electrobun) {\n window.__electrobun = {\n receiveInternalMessageFromBun: internalMessageHandler,\n receiveMessageFromBun: (msg) => {\n console.log(\"receiveMessageFromBun (no handler):\", msg);\n }\n };\n } else {\n window.__electrobun.receiveInternalMessageFromBun = internalMessageHandler;\n window.__electrobun.receiveMessageFromBun = (msg) => {\n console.log(\"receiveMessageFromBun (no handler):\", msg);\n };\n }\n window.__electrobunSendToHost = (message) => {\n emitWebviewEvent(\"host-message\", JSON.stringify(message));\n };\n initLifecycleEvents();\n initCmdClickHandling();\n initSPANavigationInterception();\n initOverscrollPrevention();\n initDragRegions();\n initWebviewTag();\n})();\n";
6
6
 
7
7
  // Minimal preload for sandboxed/untrusted webviews (lifecycle events only, no RPC)
8
8
  export const preloadScriptSandboxed = "(() => {\n // src/bun/preload/events.ts\n function emitWebviewEvent(eventName, detail) {\n setTimeout(() => {\n const bridge = window.__electrobunEventBridge || window.__electrobunInternalBridge;\n bridge?.postMessage(JSON.stringify({\n id: \"webviewEvent\",\n type: \"message\",\n payload: {\n id: window.__electrobunWebviewId,\n eventName,\n detail\n }\n }));\n });\n }\n function initLifecycleEvents() {\n window.addEventListener(\"load\", () => {\n if (window === window.top) {\n emitWebviewEvent(\"dom-ready\", document.location.href);\n }\n });\n window.addEventListener(\"popstate\", () => {\n emitWebviewEvent(\"did-navigate-in-page\", window.location.href);\n });\n window.addEventListener(\"hashchange\", () => {\n emitWebviewEvent(\"did-navigate-in-page\", window.location.href);\n });\n }\n var cmdKeyHeld = false;\n var cmdKeyTimestamp = 0;\n var CMD_KEY_THRESHOLD_MS = 500;\n function isCmdHeld() {\n if (cmdKeyHeld)\n return true;\n return Date.now() - cmdKeyTimestamp < CMD_KEY_THRESHOLD_MS && cmdKeyTimestamp > 0;\n }\n function initCmdClickHandling() {\n window.addEventListener(\"keydown\", (event) => {\n if (event.key === \"Meta\" || event.metaKey) {\n cmdKeyHeld = true;\n cmdKeyTimestamp = Date.now();\n }\n }, true);\n window.addEventListener(\"keyup\", (event) => {\n if (event.key === \"Meta\") {\n cmdKeyHeld = false;\n cmdKeyTimestamp = Date.now();\n }\n }, true);\n window.addEventListener(\"blur\", () => {\n cmdKeyHeld = false;\n });\n window.addEventListener(\"click\", (event) => {\n if (event.metaKey || event.ctrlKey) {\n const anchor = event.target?.closest?.(\"a\");\n if (anchor && anchor.href) {\n event.preventDefault();\n event.stopPropagation();\n event.stopImmediatePropagation();\n emitWebviewEvent(\"new-window-open\", JSON.stringify({\n url: anchor.href,\n isCmdClick: true,\n isSPANavigation: false\n }));\n }\n }\n }, true);\n }\n function initSPANavigationInterception() {\n const originalPushState = history.pushState;\n const originalReplaceState = history.replaceState;\n history.pushState = function(state, title, url) {\n if (isCmdHeld() && url) {\n const resolvedUrl = new URL(String(url), window.location.href).href;\n emitWebviewEvent(\"new-window-open\", JSON.stringify({\n url: resolvedUrl,\n isCmdClick: true,\n isSPANavigation: true\n }));\n return;\n }\n return originalPushState.apply(this, [state, title, url]);\n };\n history.replaceState = function(state, title, url) {\n if (isCmdHeld() && url) {\n const resolvedUrl = new URL(String(url), window.location.href).href;\n emitWebviewEvent(\"new-window-open\", JSON.stringify({\n url: resolvedUrl,\n isCmdClick: true,\n isSPANavigation: true\n }));\n return;\n }\n return originalReplaceState.apply(this, [state, title, url]);\n };\n }\n function initOverscrollPrevention() {\n document.addEventListener(\"DOMContentLoaded\", () => {\n const style = document.createElement(\"style\");\n style.type = \"text/css\";\n style.appendChild(document.createTextNode(\"html, body { overscroll-behavior: none; }\"));\n document.head.appendChild(style);\n });\n }\n\n // src/bun/preload/index-sandboxed.ts\n initLifecycleEvents();\n initCmdClickHandling();\n initSPANavigationInterception();\n initOverscrollPrevention();\n})();\n";
@@ -81,6 +81,13 @@ export class ElectrobunWebviewTag extends HTMLElement {
81
81
  // Sandbox attribute: when present, the child webview is sandboxed (no RPC, events only)
82
82
  const sandbox = this.hasAttribute("sandbox");
83
83
  this.sandboxed = sandbox;
84
+ // Read transparent/passthrough attributes for initial state (avoids flash)
85
+ const transparent = this.hasAttribute("transparent");
86
+ const passthrough = this.hasAttribute("passthrough");
87
+ this.transparent = transparent;
88
+ this.passthroughEnabled = passthrough;
89
+ if (transparent) this.style.opacity = "0";
90
+ if (passthrough) this.style.pointerEvents = "none";
84
91
 
85
92
  if (masks) {
86
93
  masks.split(",").forEach((s) => this.maskSelectors.add(s.trim()));
@@ -103,6 +110,8 @@ export class ElectrobunWebviewTag extends HTMLElement {
103
110
  },
104
111
  navigationRules: null,
105
112
  sandbox,
113
+ transparent,
114
+ passthrough,
106
115
  })) as number;
107
116
 
108
117
  this.webviewId = webviewId;
@@ -112,6 +121,17 @@ export class ElectrobunWebviewTag extends HTMLElement {
112
121
  this.setupObservers();
113
122
  // Force immediate sync after initialization
114
123
  this.syncDimensions(true);
124
+
125
+ // When adding a new webview, force all existing webviews to re-sync their positions
126
+ // This handles layout changes caused by the new webview
127
+ // Use requestAnimationFrame to ensure DOM layout is complete
128
+ requestAnimationFrame(() => {
129
+ Object.values(webviewRegistry).forEach(webview => {
130
+ if (webview !== this && webview.webviewId !== null) {
131
+ webview.syncDimensions(true);
132
+ }
133
+ });
134
+ });
115
135
  } catch (err) {
116
136
  console.error("Failed to init webview:", err);
117
137
  }
@@ -219,6 +239,7 @@ export class ElectrobunWebviewTag extends HTMLElement {
219
239
  toggleTransparent(value?: boolean) {
220
240
  if (this.webviewId === null) return;
221
241
  this.transparent = value !== undefined ? value : !this.transparent;
242
+ this.style.opacity = this.transparent ? "0" : "";
222
243
  send("webviewTagSetTransparent", {
223
244
  id: this.webviewId,
224
245
  transparent: this.transparent,
@@ -229,6 +250,7 @@ export class ElectrobunWebviewTag extends HTMLElement {
229
250
  if (this.webviewId === null) return;
230
251
  this.passthroughEnabled =
231
252
  value !== undefined ? value : !this.passthroughEnabled;
253
+ this.style.pointerEvents = this.passthroughEnabled ? "none" : "";
232
254
  send("webviewTagSetPassthrough", {
233
255
  id: this.webviewId,
234
256
  enablePassthrough: this.passthroughEnabled,
@@ -196,6 +196,14 @@ export const native = (() => {
196
196
  ],
197
197
  returns: FFIType.ptr,
198
198
  },
199
+ // Pre-set flags for the next initWebview call (workaround for FFI param count limits)
200
+ setNextWebviewFlags: {
201
+ args: [
202
+ FFIType.bool, // startTransparent
203
+ FFIType.bool, // startPassthrough
204
+ ],
205
+ returns: FFIType.void,
206
+ },
199
207
 
200
208
  // webviewtag
201
209
  webviewCanGoBack: {
@@ -903,7 +911,10 @@ export const ffi = {
903
911
  autoResize: boolean;
904
912
  navigationRules: string | null;
905
913
  sandbox: boolean;
914
+ startTransparent: boolean;
915
+ startPassthrough: boolean;
906
916
  }): FFIType.ptr => {
917
+
907
918
  const {
908
919
  id,
909
920
  windowId,
@@ -919,6 +930,8 @@ export const ffi = {
919
930
  frame: { x, y, width, height },
920
931
  autoResize,
921
932
  sandbox,
933
+ startTransparent,
934
+ startPassthrough,
922
935
  } = params;
923
936
 
924
937
  const parentWindow = BrowserWindow.getById(windowId);
@@ -971,6 +984,8 @@ window.__electrobunBunBridge = window.__electrobunBunBridge || window.webkit?.me
971
984
 
972
985
  const customPreload = preload;
973
986
 
987
+ // Pre-set flags before initWebview (workaround for FFI param count limits)
988
+ native.symbols.setNextWebviewFlags(startTransparent, startPassthrough);
974
989
  const webviewPtr = native.symbols.initWebview(
975
990
  id,
976
991
  windowPtr,
@@ -2103,6 +2118,8 @@ type WebviewTagInitParams = {
2103
2118
  windowId: number;
2104
2119
  navigationRules: string | null;
2105
2120
  sandbox: boolean;
2121
+ transparent: boolean;
2122
+ passthrough: boolean;
2106
2123
  };
2107
2124
 
2108
2125
  export const internalRpcHandlers = {
@@ -2119,6 +2136,8 @@ export const internalRpcHandlers = {
2119
2136
  frame,
2120
2137
  navigationRules,
2121
2138
  sandbox,
2139
+ transparent,
2140
+ passthrough,
2122
2141
  } = params;
2123
2142
 
2124
2143
  const url = !params.url && !html ? "https://electrobun.dev" : params.url;
@@ -2135,6 +2154,8 @@ export const internalRpcHandlers = {
2135
2154
  renderer, //: "cef",
2136
2155
  navigationRules,
2137
2156
  sandbox,
2157
+ startTransparent: transparent,
2158
+ startPassthrough: passthrough,
2138
2159
  });
2139
2160
 
2140
2161
  return webviewForTag.id;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "electrobun",
3
- "version": "1.12.3",
3
+ "version": "1.13.0-beta.0",
4
4
  "description": "Build ultra fast, tiny, and cross-platform desktop apps with Typescript.",
5
5
  "license": "MIT",
6
6
  "author": "Blackboard Technologies Inc.",
package/src/cli/index.ts CHANGED
@@ -1087,7 +1087,6 @@ const defaultConfig = {
1087
1087
  build: {
1088
1088
  buildFolder: "build",
1089
1089
  artifactFolder: "artifacts",
1090
- targets: undefined as unknown, // Will default to current platform if not specified
1091
1090
  useAsar: false,
1092
1091
  asarUnpack: undefined as string[] | undefined, // Glob patterns for files to exclude from ASAR (e.g., ["*.node", "*.dll"])
1093
1092
  cefVersion: undefined as string | undefined, // Override CEF version: "CEF_VERSION+chromium-CHROMIUM_VERSION"