electrobun 1.18.1 → 1.18.4-beta.10

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.
Files changed (64) hide show
  1. package/README.md +37 -14
  2. package/dash.config.ts +28 -0
  3. package/dist/api/browser/global.d.ts +6 -0
  4. package/dist/api/browser/index.ts +162 -44
  5. package/dist/api/{bun → config}/ElectrobunConfig.ts +109 -40
  6. package/dist/api/config/validate.test.ts +31 -0
  7. package/dist/api/config/validate.ts +19 -0
  8. package/dist/api/preload/.generated/compiled.ts +8 -0
  9. package/dist/api/{bun/preload → preload}/build.ts +6 -6
  10. package/dist/api/{bun/preload → preload}/encryption.ts +11 -5
  11. package/dist/api/{bun/preload → preload}/globals.d.ts +8 -0
  12. package/dist/api/{bun/preload → preload}/index.ts +18 -8
  13. package/dist/api/{bun/preload → preload}/webviewTag.ts +32 -3
  14. package/dist/api/{bun/preload → preload}/wgpuTag.ts +33 -2
  15. package/dist/api/{bun → sdks/bun}/__tests__/ffi-contract.test.ts +6 -9
  16. package/dist/api/{bun → sdks/bun}/core/BrowserView.ts +96 -55
  17. package/dist/api/{bun → sdks/bun}/core/BrowserWindow.ts +20 -30
  18. package/dist/api/{bun → sdks/bun}/core/BuildConfig.ts +32 -5
  19. package/dist/api/{bun → sdks/bun}/core/GpuWindow.ts +20 -7
  20. package/dist/api/sdks/bun/core/Socket.ts +22 -0
  21. package/dist/api/{bun → sdks/bun}/core/Tray.ts +33 -32
  22. package/dist/api/{bun → sdks/bun}/core/Updater.ts +10 -10
  23. package/dist/api/{bun → sdks/bun}/core/Utils.ts +3 -5
  24. package/dist/api/{bun → sdks/bun}/core/WGPUView.ts +43 -13
  25. package/dist/api/{bun → sdks/bun}/index.ts +39 -10
  26. package/dist/api/{bun → sdks/bun}/proc/native.ts +942 -746
  27. package/dist/api/{bun → sdks/bun}/webGPU.ts +1 -1
  28. package/dist/api/{bun → sdks/bun}/webgpuAdapter.ts +40 -33
  29. package/dist/api/shared/build-dependencies.test.ts +48 -0
  30. package/dist/api/shared/build-dependencies.ts +46 -0
  31. package/dist/api/shared/go-version.ts +3 -0
  32. package/dist/api/shared/odin-version.ts +6 -0
  33. package/dist/api/shared/rust-version.ts +3 -0
  34. package/dist/go-sdk/callbacks.go +260 -0
  35. package/dist/go-sdk/electrobun.go +2021 -0
  36. package/dist/main.js +35 -28
  37. package/dist/odin-sdk/electrobun/electrobun.odin +2337 -0
  38. package/dist/preload-full.js +948 -0
  39. package/dist/preload-sandboxed.js +111 -0
  40. package/dist/rust-sdk/electrobun.rs +2396 -0
  41. package/dist/zig-sdk/electrobun.zig +2005 -0
  42. package/package.json +5 -29
  43. package/src/cli/index.ts +868 -654
  44. package/bin/electrobun.cjs +0 -169
  45. package/dist/api/bun/core/Socket.ts +0 -205
  46. package/dist/api/bun/core/windowIds.ts +0 -5
  47. package/dist/api/bun/preload/.generated/compiled.ts +0 -8
  48. package/dist/api/shared/bun-version.ts +0 -3
  49. /package/dist/api/{bun/preload → preload}/dragRegions.ts +0 -0
  50. /package/dist/api/{bun/preload → preload}/events.ts +0 -0
  51. /package/dist/api/{bun/preload → preload}/index-sandboxed.ts +0 -0
  52. /package/dist/api/{bun/preload → preload}/internalRpc.ts +0 -0
  53. /package/dist/api/{bun/preload → preload}/overlaySync.ts +0 -0
  54. /package/dist/api/{bun → sdks/bun}/core/ApplicationMenu.ts +0 -0
  55. /package/dist/api/{bun → sdks/bun}/core/ContextMenu.ts +0 -0
  56. /package/dist/api/{bun → sdks/bun}/core/Paths.ts +0 -0
  57. /package/dist/api/{bun → sdks/bun}/core/menuRoles.ts +0 -0
  58. /package/dist/api/{bun → sdks/bun}/events/ApplicationEvents.ts +0 -0
  59. /package/dist/api/{bun → sdks/bun}/events/event.ts +0 -0
  60. /package/dist/api/{bun → sdks/bun}/events/eventEmitter.ts +0 -0
  61. /package/dist/api/{bun → sdks/bun}/events/trayEvents.ts +0 -0
  62. /package/dist/api/{bun → sdks/bun}/events/webviewEvents.ts +0 -0
  63. /package/dist/api/{bun → sdks/bun}/events/windowEvents.ts +0 -0
  64. /package/dist/api/{bun → sdks/bun}/proc/linux.md +0 -0
@@ -1,18 +1,17 @@
1
- import { join } from "path";
1
+ import { dirname, join } from "path";
2
+ import { createReadStream } from "node:fs";
2
3
  import electrobunEventEmitter from "../events/eventEmitter";
3
4
  import ElectrobunEvent from "../events/event";
4
5
  import { BrowserView } from "../core/BrowserView";
5
6
  import { WGPUView } from "../core/WGPUView";
6
- import { Tray } from "../core/Tray";
7
7
  import {
8
8
  preloadScript,
9
9
  preloadScriptSandboxed,
10
- } from "../preload/.generated/compiled";
10
+ } from "../../../preload/.generated/compiled";
11
11
 
12
12
  // Menu data reference system to avoid serialization overhead
13
13
  const menuDataRegistry = new Map<string, any>();
14
14
  let menuDataCounter = 0;
15
-
16
15
  function storeMenuData(data: any): string {
17
16
  const id = `menuData_${++menuDataCounter}`;
18
17
  menuDataRegistry.set(id, data);
@@ -70,142 +69,586 @@ import {
70
69
  toArrayBuffer,
71
70
  type Pointer,
72
71
  } from "bun:ffi";
73
- import { BrowserWindow } from "../core/BrowserWindow";
74
- import { GpuWindow } from "../core/GpuWindow";
72
+
73
+ function getElectrobunLibraryPathCandidates(fileName: string) {
74
+ const candidates = new Set<string>();
75
+ candidates.add(join(process.cwd(), fileName));
76
+ if (process.argv0) {
77
+ candidates.add(join(dirname(process.argv0), fileName));
78
+ }
79
+ return Array.from(candidates);
80
+ }
81
+
82
+ function tryDlopenCandidates<T extends Record<string, { args: FFIType[]; returns: FFIType }>>(
83
+ fileName: string,
84
+ symbols: T,
85
+ ) {
86
+ let lastError: unknown = null;
87
+ for (const candidatePath of getElectrobunLibraryPathCandidates(fileName)) {
88
+ try {
89
+ return dlopen(candidatePath, symbols);
90
+ } catch (error) {
91
+ lastError = error;
92
+ }
93
+ }
94
+ throw lastError ?? new Error(`Failed to load ${fileName}`);
95
+ }
75
96
 
76
97
  function getWindowPtr(winId: number) {
77
- return (
78
- BrowserWindow.getById(winId)?.ptr ?? GpuWindow.getById(winId)?.ptr ?? null
98
+ return core?.symbols.getWindowPointer(winId) || null;
99
+ }
100
+
101
+ function getCoreLastError(): string | null {
102
+ const error = core?.symbols.electrobun_core_last_error();
103
+ if (!error) {
104
+ return null;
105
+ }
106
+
107
+ const message = error.toString();
108
+ return message.length > 0 ? message : null;
109
+ }
110
+
111
+ let webviewRuntimeConfigured = false;
112
+
113
+ function ensureWebviewRuntimeConfigured() {
114
+ if (webviewRuntimeConfigured) {
115
+ return;
116
+ }
117
+
118
+ const configured = core?.symbols.configureWebviewRuntime(
119
+ 0,
120
+ toCString(preloadScript),
121
+ toCString(preloadScriptSandboxed),
79
122
  );
123
+
124
+ if (!configured) {
125
+ throw getCoreLastError() || "Failed to configure webview runtime";
126
+ }
127
+
128
+ webviewRuntimeConfigured = true;
80
129
  }
81
130
 
82
- export const native = (() => {
131
+ const core = (() => {
83
132
  try {
84
- // Use absolute path to native wrapper DLL to avoid working directory issues
85
- // On Windows shortcuts, the working directory may not be set correctly
86
- const nativeWrapperPath = join(process.cwd(), `libNativeWrapper.${suffix}`);
87
- return dlopen(nativeWrapperPath, {
88
- // window
89
- createWindowWithFrameAndStyleFromWorker: {
90
- // Pass each parameter individually
133
+ const coreFileName =
134
+ process.platform === "win32"
135
+ ? "ElectrobunCore.dll"
136
+ : `libElectrobunCore.${suffix}`;
137
+ return tryDlopenCandidates(coreFileName, {
138
+ electrobun_core_last_error: {
139
+ args: [],
140
+ returns: FFIType.cstring,
141
+ },
142
+ getWindowStyle: {
143
+ args: [
144
+ FFIType.bool,
145
+ FFIType.bool,
146
+ FFIType.bool,
147
+ FFIType.bool,
148
+ FFIType.bool,
149
+ FFIType.bool,
150
+ FFIType.bool,
151
+ FFIType.bool,
152
+ FFIType.bool,
153
+ FFIType.bool,
154
+ FFIType.bool,
155
+ FFIType.bool,
156
+ ],
157
+ returns: FFIType.u32,
158
+ },
159
+ createWindow: {
91
160
  args: [
92
- FFIType.u32, // windowId
93
161
  FFIType.f64,
94
- FFIType.f64, // x, y
95
162
  FFIType.f64,
96
- FFIType.f64, // width, height
97
- FFIType.u32, // styleMask
98
- FFIType.cstring, // titleBarStyle
99
- FFIType.bool, // transparent
100
- FFIType.f64, // trafficLightOffsetX
101
- FFIType.f64, // trafficLightOffsetY
102
- FFIType.function, // closeHandler
103
- FFIType.function, // moveHandler
104
- FFIType.function, // resizeHandler
105
- FFIType.function, // focusHandler
106
- FFIType.function, // blurHandler
107
- FFIType.function, // keyHandler
163
+ FFIType.f64,
164
+ FFIType.f64,
165
+ FFIType.u32,
166
+ FFIType.cstring,
167
+ FFIType.bool,
168
+ FFIType.cstring,
169
+ FFIType.bool,
170
+ FFIType.bool,
171
+ FFIType.f64,
172
+ FFIType.f64,
173
+ FFIType.function,
174
+ FFIType.function,
175
+ FFIType.function,
176
+ FFIType.function,
177
+ FFIType.function,
178
+ FFIType.function,
108
179
  ],
180
+ returns: FFIType.u32,
181
+ },
182
+ getWindowPointer: {
183
+ args: [FFIType.u32],
109
184
  returns: FFIType.ptr,
110
185
  },
111
186
  setWindowTitle: {
187
+ args: [FFIType.u32, FFIType.cstring],
188
+ returns: FFIType.void,
189
+ },
190
+ minimizeWindow: {
191
+ args: [FFIType.u32],
192
+ returns: FFIType.void,
193
+ },
194
+ restoreWindow: {
195
+ args: [FFIType.u32],
196
+ returns: FFIType.void,
197
+ },
198
+ isWindowMinimized: {
199
+ args: [FFIType.u32],
200
+ returns: FFIType.bool,
201
+ },
202
+ maximizeWindow: {
203
+ args: [FFIType.u32],
204
+ returns: FFIType.void,
205
+ },
206
+ unmaximizeWindow: {
207
+ args: [FFIType.u32],
208
+ returns: FFIType.void,
209
+ },
210
+ isWindowMaximized: {
211
+ args: [FFIType.u32],
212
+ returns: FFIType.bool,
213
+ },
214
+ showWindow: {
215
+ args: [FFIType.u32, FFIType.bool],
216
+ returns: FFIType.void,
217
+ },
218
+ activateWindow: {
219
+ args: [FFIType.u32],
220
+ returns: FFIType.void,
221
+ },
222
+ hideWindow: {
223
+ args: [FFIType.u32],
224
+ returns: FFIType.void,
225
+ },
226
+ closeWindow: {
227
+ args: [FFIType.u32],
228
+ returns: FFIType.void,
229
+ },
230
+ setWindowFullScreen: {
231
+ args: [FFIType.u32, FFIType.bool],
232
+ returns: FFIType.void,
233
+ },
234
+ isWindowFullScreen: {
235
+ args: [FFIType.u32],
236
+ returns: FFIType.bool,
237
+ },
238
+ setWindowAlwaysOnTop: {
239
+ args: [FFIType.u32, FFIType.bool],
240
+ returns: FFIType.void,
241
+ },
242
+ isWindowAlwaysOnTop: {
243
+ args: [FFIType.u32],
244
+ returns: FFIType.bool,
245
+ },
246
+ setWindowVisibleOnAllWorkspaces: {
247
+ args: [FFIType.u32, FFIType.bool],
248
+ returns: FFIType.void,
249
+ },
250
+ isWindowVisibleOnAllWorkspaces: {
251
+ args: [FFIType.u32],
252
+ returns: FFIType.bool,
253
+ },
254
+ setWindowPosition: {
255
+ args: [FFIType.u32, FFIType.f64, FFIType.f64],
256
+ returns: FFIType.void,
257
+ },
258
+ setWindowButtonPosition: {
259
+ args: [FFIType.u32, FFIType.f64, FFIType.f64],
260
+ returns: FFIType.void,
261
+ },
262
+ setWindowSize: {
263
+ args: [FFIType.u32, FFIType.f64, FFIType.f64],
264
+ returns: FFIType.void,
265
+ },
266
+ setWindowFrame: {
267
+ args: [FFIType.u32, FFIType.f64, FFIType.f64, FFIType.f64, FFIType.f64],
268
+ returns: FFIType.void,
269
+ },
270
+ getWindowFrame: {
271
+ args: [FFIType.u32, FFIType.ptr, FFIType.ptr, FFIType.ptr, FFIType.ptr],
272
+ returns: FFIType.void,
273
+ },
274
+ configureWebviewRuntime: {
275
+ args: [
276
+ FFIType.u32,
277
+ FFIType.cstring,
278
+ FFIType.cstring,
279
+ ],
280
+ returns: FFIType.bool,
281
+ },
282
+ createWebview: {
283
+ args: [
284
+ FFIType.u32,
285
+ FFIType.u32,
286
+ FFIType.cstring,
287
+ FFIType.cstring,
288
+ FFIType.f64,
289
+ FFIType.f64,
290
+ FFIType.f64,
291
+ FFIType.f64,
292
+ FFIType.bool,
293
+ FFIType.cstring,
294
+ FFIType.function,
295
+ FFIType.function,
296
+ FFIType.function,
297
+ FFIType.function,
298
+ FFIType.function,
299
+ FFIType.cstring,
300
+ FFIType.cstring,
301
+ FFIType.cstring,
302
+ FFIType.bool,
303
+ FFIType.bool,
304
+ FFIType.bool,
305
+ ],
306
+ returns: FFIType.u32,
307
+ },
308
+ getWebviewPointer: {
309
+ args: [FFIType.u32],
310
+ returns: FFIType.ptr,
311
+ },
312
+ resizeWebview: {
112
313
  args: [
113
- FFIType.ptr, // window ptr
114
- FFIType.cstring, // title
314
+ FFIType.u32,
315
+ FFIType.f64,
316
+ FFIType.f64,
317
+ FFIType.f64,
318
+ FFIType.f64,
319
+ FFIType.cstring,
115
320
  ],
116
321
  returns: FFIType.void,
117
322
  },
118
- showWindow: {
323
+ loadURLInWebView: {
324
+ args: [FFIType.u32, FFIType.cstring],
325
+ returns: FFIType.void,
326
+ },
327
+ loadHTMLInWebView: {
328
+ args: [FFIType.u32, FFIType.cstring],
329
+ returns: FFIType.void,
330
+ },
331
+ updatePreloadScriptToWebView: {
332
+ args: [FFIType.u32, FFIType.cstring, FFIType.cstring, FFIType.bool],
333
+ returns: FFIType.void,
334
+ },
335
+ webviewCanGoBack: {
336
+ args: [FFIType.u32],
337
+ returns: FFIType.bool,
338
+ },
339
+ webviewCanGoForward: {
340
+ args: [FFIType.u32],
341
+ returns: FFIType.bool,
342
+ },
343
+ webviewGoBack: {
344
+ args: [FFIType.u32],
345
+ returns: FFIType.void,
346
+ },
347
+ webviewGoForward: {
348
+ args: [FFIType.u32],
349
+ returns: FFIType.void,
350
+ },
351
+ webviewReload: {
352
+ args: [FFIType.u32],
353
+ returns: FFIType.void,
354
+ },
355
+ webviewRemove: {
356
+ args: [FFIType.u32],
357
+ returns: FFIType.void,
358
+ },
359
+ setWebviewHTMLContent: {
360
+ args: [FFIType.u32, FFIType.cstring],
361
+ returns: FFIType.void,
362
+ },
363
+ webviewSetTransparent: {
364
+ args: [FFIType.u32, FFIType.bool],
365
+ returns: FFIType.void,
366
+ },
367
+ webviewSetPassthrough: {
368
+ args: [FFIType.u32, FFIType.bool],
369
+ returns: FFIType.void,
370
+ },
371
+ webviewSetHidden: {
372
+ args: [FFIType.u32, FFIType.bool],
373
+ returns: FFIType.void,
374
+ },
375
+ setWebviewNavigationRules: {
376
+ args: [FFIType.u32, FFIType.cstring],
377
+ returns: FFIType.void,
378
+ },
379
+ webviewFindInPage: {
380
+ args: [FFIType.u32, FFIType.cstring, FFIType.bool, FFIType.bool],
381
+ returns: FFIType.void,
382
+ },
383
+ webviewStopFind: {
384
+ args: [FFIType.u32],
385
+ returns: FFIType.void,
386
+ },
387
+ evaluateJavaScriptWithNoCompletion: {
388
+ args: [FFIType.u32, FFIType.cstring],
389
+ returns: FFIType.void,
390
+ },
391
+ sendHostMessageToWebviewViaTransport: {
392
+ args: [FFIType.u32, FFIType.cstring],
393
+ returns: FFIType.bool,
394
+ },
395
+ popNextQueuedHostMessage: {
396
+ args: [FFIType.ptr],
397
+ returns: FFIType.ptr,
398
+ },
399
+ getHostMessageWakeupReadFD: {
400
+ args: [],
401
+ returns: FFIType.int,
402
+ },
403
+ freeCoreString: {
404
+ args: [FFIType.ptr],
405
+ returns: FFIType.void,
406
+ },
407
+ clearWebviewHostTransport: {
408
+ args: [FFIType.u32],
409
+ returns: FFIType.void,
410
+ },
411
+ dispatchHostWebviewEvent: {
412
+ args: [FFIType.u32, FFIType.cstring, FFIType.cstring],
413
+ returns: FFIType.bool,
414
+ },
415
+ sendInternalMessageToWebview: {
416
+ args: [FFIType.u32, FFIType.cstring],
417
+ returns: FFIType.bool,
418
+ },
419
+ webviewOpenDevTools: {
420
+ args: [FFIType.u32],
421
+ returns: FFIType.void,
422
+ },
423
+ webviewCloseDevTools: {
424
+ args: [FFIType.u32],
425
+ returns: FFIType.void,
426
+ },
427
+ webviewToggleDevTools: {
428
+ args: [FFIType.u32],
429
+ returns: FFIType.void,
430
+ },
431
+ webviewSetPageZoom: {
432
+ args: [FFIType.u32, FFIType.f64],
433
+ returns: FFIType.void,
434
+ },
435
+ webviewGetPageZoom: {
436
+ args: [FFIType.u32],
437
+ returns: FFIType.f64,
438
+ },
439
+ createWGPUView: {
119
440
  args: [
120
- FFIType.ptr, // window ptr
121
- FFIType.bool, // activate
441
+ FFIType.u32,
442
+ FFIType.f64,
443
+ FFIType.f64,
444
+ FFIType.f64,
445
+ FFIType.f64,
446
+ FFIType.bool,
447
+ FFIType.bool,
448
+ FFIType.bool,
122
449
  ],
450
+ returns: FFIType.u32,
451
+ },
452
+ getWGPUViewPointer: {
453
+ args: [FFIType.u32],
454
+ returns: FFIType.ptr,
455
+ },
456
+ setWGPUViewFrame: {
457
+ args: [FFIType.u32, FFIType.f64, FFIType.f64, FFIType.f64, FFIType.f64],
123
458
  returns: FFIType.void,
124
459
  },
125
- activateWindow: {
460
+ resizeWGPUView: {
461
+ args: [FFIType.u32, FFIType.f64, FFIType.f64, FFIType.f64, FFIType.f64, FFIType.cstring],
462
+ returns: FFIType.void,
463
+ },
464
+ setWGPUViewTransparent: {
465
+ args: [FFIType.u32, FFIType.bool],
466
+ returns: FFIType.void,
467
+ },
468
+ setWGPUViewPassthrough: {
469
+ args: [FFIType.u32, FFIType.bool],
470
+ returns: FFIType.void,
471
+ },
472
+ setWGPUViewHidden: {
473
+ args: [FFIType.u32, FFIType.bool],
474
+ returns: FFIType.void,
475
+ },
476
+ removeWGPUView: {
477
+ args: [FFIType.u32],
478
+ returns: FFIType.void,
479
+ },
480
+ getWGPUViewNativeHandle: {
481
+ args: [FFIType.u32],
482
+ returns: FFIType.ptr,
483
+ },
484
+ runWGPUViewTest: {
485
+ args: [FFIType.u32],
486
+ returns: FFIType.void,
487
+ },
488
+ createTray: {
489
+ args: [
490
+ FFIType.cstring,
491
+ FFIType.cstring,
492
+ FFIType.bool,
493
+ FFIType.u32,
494
+ FFIType.u32,
495
+ FFIType.function,
496
+ ],
497
+ returns: FFIType.u32,
498
+ },
499
+ showTray: {
500
+ args: [FFIType.u32],
501
+ returns: FFIType.bool,
502
+ },
503
+ hideTray: {
504
+ args: [FFIType.u32],
505
+ returns: FFIType.void,
506
+ },
507
+ setTrayTitle: {
508
+ args: [FFIType.u32, FFIType.cstring],
509
+ returns: FFIType.void,
510
+ },
511
+ setTrayImage: {
512
+ args: [FFIType.u32, FFIType.cstring],
513
+ returns: FFIType.void,
514
+ },
515
+ setTrayMenu: {
516
+ args: [FFIType.u32, FFIType.cstring],
517
+ returns: FFIType.void,
518
+ },
519
+ removeTray: {
520
+ args: [FFIType.u32],
521
+ returns: FFIType.void,
522
+ },
523
+ getTrayBounds: {
524
+ args: [FFIType.u32],
525
+ returns: FFIType.cstring,
526
+ },
527
+ setApplicationMenu: {
528
+ args: [FFIType.cstring, FFIType.function],
529
+ returns: FFIType.void,
530
+ },
531
+ showContextMenu: {
532
+ args: [FFIType.cstring, FFIType.function],
533
+ returns: FFIType.void,
534
+ },
535
+ moveToTrash: {
536
+ args: [FFIType.cstring],
537
+ returns: FFIType.bool,
538
+ },
539
+ showItemInFolder: {
540
+ args: [FFIType.cstring],
541
+ returns: FFIType.void,
542
+ },
543
+ openExternal: {
544
+ args: [FFIType.cstring],
545
+ returns: FFIType.bool,
546
+ },
547
+ openPath: {
548
+ args: [FFIType.cstring],
549
+ returns: FFIType.bool,
550
+ },
551
+ showNotification: {
552
+ args: [
553
+ FFIType.cstring,
554
+ FFIType.cstring,
555
+ FFIType.cstring,
556
+ FFIType.bool,
557
+ ],
558
+ returns: FFIType.void,
559
+ },
560
+ getAllDisplays: {
561
+ args: [],
562
+ returns: FFIType.cstring,
563
+ },
564
+ getPrimaryDisplay: {
565
+ args: [],
566
+ returns: FFIType.cstring,
567
+ },
568
+ getCursorScreenPoint: {
569
+ args: [],
570
+ returns: FFIType.cstring,
571
+ },
572
+ getMouseButtons: {
573
+ args: [],
574
+ returns: FFIType.u64,
575
+ },
576
+ openFileDialog: {
126
577
  args: [
127
- FFIType.ptr, // window ptr
578
+ FFIType.cstring,
579
+ FFIType.cstring,
580
+ FFIType.int,
581
+ FFIType.int,
582
+ FFIType.int,
128
583
  ],
129
- returns: FFIType.void,
130
- },
131
- hideWindow: {
132
- args: [FFIType.ptr],
133
- returns: FFIType.void,
584
+ returns: FFIType.cstring,
134
585
  },
135
- closeWindow: {
586
+ showMessageBox: {
136
587
  args: [
137
- FFIType.ptr, // window ptr
588
+ FFIType.cstring,
589
+ FFIType.cstring,
590
+ FFIType.cstring,
591
+ FFIType.cstring,
592
+ FFIType.cstring,
593
+ FFIType.int,
594
+ FFIType.int,
138
595
  ],
139
- returns: FFIType.void,
596
+ returns: FFIType.int,
140
597
  },
141
- minimizeWindow: {
142
- args: [FFIType.ptr],
143
- returns: FFIType.void,
598
+ clipboardReadText: {
599
+ args: [],
600
+ returns: FFIType.cstring,
144
601
  },
145
- restoreWindow: {
146
- args: [FFIType.ptr],
602
+ clipboardWriteText: {
603
+ args: [FFIType.cstring],
147
604
  returns: FFIType.void,
148
605
  },
149
- isWindowMinimized: {
150
- args: [FFIType.ptr],
151
- returns: FFIType.bool,
152
- },
153
- maximizeWindow: {
606
+ clipboardReadImage: {
154
607
  args: [FFIType.ptr],
155
- returns: FFIType.void,
608
+ returns: FFIType.ptr,
156
609
  },
157
- unmaximizeWindow: {
158
- args: [FFIType.ptr],
610
+ clipboardWriteImage: {
611
+ args: [FFIType.ptr, FFIType.u64],
159
612
  returns: FFIType.void,
160
613
  },
161
- isWindowMaximized: {
162
- args: [FFIType.ptr],
163
- returns: FFIType.bool,
164
- },
165
- setWindowFullScreen: {
166
- args: [FFIType.ptr, FFIType.bool],
614
+ clipboardClear: {
615
+ args: [],
167
616
  returns: FFIType.void,
168
617
  },
169
- isWindowFullScreen: {
170
- args: [FFIType.ptr],
171
- returns: FFIType.bool,
618
+ clipboardAvailableFormats: {
619
+ args: [],
620
+ returns: FFIType.cstring,
172
621
  },
173
- setWindowAlwaysOnTop: {
174
- args: [FFIType.ptr, FFIType.bool],
622
+ setDockIconVisible: {
623
+ args: [FFIType.bool],
175
624
  returns: FFIType.void,
176
625
  },
177
- isWindowAlwaysOnTop: {
178
- args: [FFIType.ptr],
626
+ isDockIconVisible: {
627
+ args: [],
179
628
  returns: FFIType.bool,
180
629
  },
181
- setWindowVisibleOnAllWorkspaces: {
182
- args: [FFIType.ptr, FFIType.bool],
630
+ setExitOnLastWindowClosed: {
631
+ args: [FFIType.bool],
183
632
  returns: FFIType.void,
184
633
  },
185
- isWindowVisibleOnAllWorkspaces: {
186
- args: [FFIType.ptr],
187
- returns: FFIType.bool,
188
- },
189
- setWindowPosition: {
190
- args: [FFIType.ptr, FFIType.f64, FFIType.f64],
191
- returns: FFIType.void,
192
- },
193
- setWindowButtonPosition: {
194
- args: [FFIType.ptr, FFIType.f64, FFIType.f64],
195
- returns: FFIType.void,
196
- },
197
- setWindowSize: {
198
- args: [FFIType.ptr, FFIType.f64, FFIType.f64],
199
- returns: FFIType.void,
200
- },
201
- setWindowFrame: {
202
- args: [FFIType.ptr, FFIType.f64, FFIType.f64, FFIType.f64, FFIType.f64],
634
+ setQuitRequestedHandler: {
635
+ args: [FFIType.function],
203
636
  returns: FFIType.void,
204
637
  },
205
- getWindowFrame: {
206
- args: [FFIType.ptr, FFIType.ptr, FFIType.ptr, FFIType.ptr, FFIType.ptr],
638
+ quitGracefully: {
639
+ args: [FFIType.i32, FFIType.i32],
207
640
  returns: FFIType.void,
208
641
  },
642
+ });
643
+ } catch {
644
+ return null;
645
+ }
646
+ })();
647
+
648
+ export const native = (() => {
649
+ try {
650
+ const nativeWrapperFileName = `libNativeWrapper.${suffix}`;
651
+ return tryDlopenCandidates(nativeWrapperFileName, {
209
652
  // webview
210
653
  initWebview: {
211
654
  args: [
@@ -222,13 +665,13 @@ export const native = (() => {
222
665
  FFIType.function, // decideNavigation: *const fn (u32, [*:0]const u8) callconv(.C) bool,
223
666
  FFIType.function, // webviewEventHandler: *const fn (u32, [*:0]const u8, [*:0]const u8) callconv(.C) void,
224
667
  FFIType.function, // eventBridgeHandler: *const fn (u32, [*:0]const u8) callconv(.C) void (events only, always active)
225
- FFIType.function, // bunBridgePostmessageHandler: *const fn (u32, [*:0]const u8) callconv(.C) void (user RPC, disabled in sandbox)
668
+ FFIType.function, // hostBridgePostmessageHandler: *const fn (u32, [*:0]const u8) callconv(.C) void (user RPC, disabled in sandbox)
226
669
  FFIType.function, // internalBridgeHandler: *const fn (u32, [*:0]const u8) callconv(.C) void (internal RPC, disabled in sandbox)
227
670
  FFIType.cstring, // electrobunPreloadScript
228
671
  FFIType.cstring, // customPreloadScript
229
672
  FFIType.cstring, // viewsRoot
230
673
  FFIType.bool, // transparent
231
- FFIType.bool, // sandbox - when true, bunBridge and internalBridge are not set up
674
+ FFIType.bool, // sandbox - when true, hostBridge and internalBridge are not set up
232
675
  ],
233
676
  returns: FFIType.ptr,
234
677
  },
@@ -463,73 +906,6 @@ export const native = (() => {
463
906
  args: [FFIType.ptr, FFIType.ptr],
464
907
  returns: FFIType.ptr,
465
908
  },
466
- // Tray
467
- createTray: {
468
- args: [
469
- FFIType.u32, // id
470
- FFIType.cstring, // title
471
- FFIType.cstring, // pathToImage
472
- FFIType.bool, // isTemplate
473
- FFIType.u32, // width
474
- FFIType.u32, //height
475
- FFIType.function, // trayItemHandler
476
- ],
477
- returns: FFIType.ptr,
478
- },
479
- setTrayTitle: {
480
- args: [FFIType.ptr, FFIType.cstring],
481
- returns: FFIType.void,
482
- },
483
- setTrayImage: {
484
- args: [FFIType.ptr, FFIType.cstring],
485
- returns: FFIType.void,
486
- },
487
- setTrayMenu: {
488
- args: [FFIType.ptr, FFIType.cstring],
489
- returns: FFIType.void,
490
- },
491
- removeTray: {
492
- args: [FFIType.ptr],
493
- returns: FFIType.void,
494
- },
495
- getTrayBounds: {
496
- args: [FFIType.ptr],
497
- returns: FFIType.cstring,
498
- },
499
- setApplicationMenu: {
500
- args: [FFIType.cstring, FFIType.function],
501
- returns: FFIType.void,
502
- },
503
- showContextMenu: {
504
- args: [FFIType.cstring, FFIType.function],
505
- returns: FFIType.void,
506
- },
507
- moveToTrash: {
508
- args: [FFIType.cstring],
509
- returns: FFIType.bool,
510
- },
511
- showItemInFolder: {
512
- args: [FFIType.cstring],
513
- returns: FFIType.void,
514
- },
515
- openExternal: {
516
- args: [FFIType.cstring],
517
- returns: FFIType.bool,
518
- },
519
- openPath: {
520
- args: [FFIType.cstring],
521
- returns: FFIType.bool,
522
- },
523
- showNotification: {
524
- args: [
525
- FFIType.cstring, // title
526
- FFIType.cstring, // body
527
- FFIType.cstring, // subtitle
528
- FFIType.bool, // silent
529
- ],
530
- returns: FFIType.void,
531
- },
532
-
533
909
  // Global keyboard shortcuts
534
910
  setGlobalShortcutCallback: {
535
911
  args: [FFIType.function],
@@ -552,73 +928,6 @@ export const native = (() => {
552
928
  returns: FFIType.bool,
553
929
  },
554
930
 
555
- // Screen API
556
- getAllDisplays: {
557
- args: [],
558
- returns: FFIType.cstring,
559
- },
560
- getPrimaryDisplay: {
561
- args: [],
562
- returns: FFIType.cstring,
563
- },
564
- getCursorScreenPoint: {
565
- args: [],
566
- returns: FFIType.cstring,
567
- },
568
- getMouseButtons: {
569
- args: [],
570
- returns: FFIType.u64,
571
- },
572
-
573
- openFileDialog: {
574
- args: [
575
- FFIType.cstring,
576
- FFIType.cstring,
577
- FFIType.int,
578
- FFIType.int,
579
- FFIType.int,
580
- ],
581
- returns: FFIType.cstring,
582
- },
583
- showMessageBox: {
584
- args: [
585
- FFIType.cstring, // type
586
- FFIType.cstring, // title
587
- FFIType.cstring, // message
588
- FFIType.cstring, // detail
589
- FFIType.cstring, // buttons (comma-separated)
590
- FFIType.int, // defaultId
591
- FFIType.int, // cancelId
592
- ],
593
- returns: FFIType.int,
594
- },
595
-
596
- // Clipboard API
597
- clipboardReadText: {
598
- args: [],
599
- returns: FFIType.cstring,
600
- },
601
- clipboardWriteText: {
602
- args: [FFIType.cstring],
603
- returns: FFIType.void,
604
- },
605
- clipboardReadImage: {
606
- args: [FFIType.ptr], // pointer to size_t for output size
607
- returns: FFIType.ptr, // pointer to PNG data
608
- },
609
- clipboardWriteImage: {
610
- args: [FFIType.ptr, FFIType.u64], // PNG data pointer, size
611
- returns: FFIType.void,
612
- },
613
- clipboardClear: {
614
- args: [],
615
- returns: FFIType.void,
616
- },
617
- clipboardAvailableFormats: {
618
- args: [],
619
- returns: FFIType.cstring,
620
- },
621
-
622
931
  // Session/Cookie API
623
932
  sessionGetCookies: {
624
933
  args: [FFIType.cstring, FFIType.cstring],
@@ -650,33 +959,7 @@ export const native = (() => {
650
959
  args: [FFIType.function],
651
960
  returns: FFIType.void,
652
961
  },
653
- setDockIconVisible: {
654
- args: [FFIType.bool],
655
- returns: FFIType.void,
656
- },
657
- isDockIconVisible: {
658
- args: [],
659
- returns: FFIType.bool,
660
- },
661
962
 
662
- // Window style utilities
663
- getWindowStyle: {
664
- args: [
665
- FFIType.bool,
666
- FFIType.bool,
667
- FFIType.bool,
668
- FFIType.bool,
669
- FFIType.bool,
670
- FFIType.bool,
671
- FFIType.bool,
672
- FFIType.bool,
673
- FFIType.bool,
674
- FFIType.bool,
675
- FFIType.bool,
676
- FFIType.bool,
677
- ],
678
- returns: FFIType.u32,
679
- },
680
963
  // JSCallback utils for native code to use
681
964
  setJSUtils: {
682
965
  args: [
@@ -727,7 +1010,7 @@ export const native = (() => {
727
1010
  }
728
1011
  })();
729
1012
 
730
- export const hasFFI = native !== null;
1013
+ export const hasFFI = native !== null && core !== null;
731
1014
 
732
1015
  // PostMessage bridge for carrot workers (inter-carrot communication, host events).
733
1016
  // Created when __bunnyCarrotBootstrap exists, regardless of FFI availability.
@@ -809,7 +1092,14 @@ function createFfiRequestProxy(ffiRequest: Record<string, Function>): Record<str
809
1092
  return new Proxy(ffiRequest, {
810
1093
  get(target, method: string) {
811
1094
  if (typeof method !== "string") return target[method];
812
- return (params?: unknown) => bridge!.requestHost(method, params);
1095
+ return (params?: unknown) => {
1096
+ if (!bridge) {
1097
+ throw new Error(
1098
+ `Electrobun FFI is unavailable and no host bridge exists for request ${method}`,
1099
+ );
1100
+ }
1101
+ return bridge.requestHost(method, params);
1102
+ };
813
1103
  },
814
1104
  });
815
1105
  }
@@ -821,12 +1111,103 @@ function createFfiRequestProxy(ffiRequest: Record<string, Function>): Record<str
821
1111
  // a zig bug I ran into last year. So check number of args in a signature when alignment issues occur.
822
1112
 
823
1113
  // Non-null accessor for use inside _ffiImpl — these methods are only called when hasFFI is true.
1114
+ const core_ = core!;
824
1115
  const native_ = native!;
1116
+ const queuedHostMessageWebviewIdBuf = new Uint32Array(1);
1117
+
1118
+ const drainQueuedHostMessages = () => {
1119
+ if (!core) {
1120
+ return;
1121
+ }
1122
+
1123
+ for (;;) {
1124
+ let rawMessage = "";
1125
+ let webviewId = 0;
1126
+ const messagePtr = core_.symbols.popNextQueuedHostMessage(
1127
+ ptr(queuedHostMessageWebviewIdBuf),
1128
+ ) as Pointer | null;
1129
+
1130
+ if (!messagePtr) {
1131
+ return;
1132
+ }
1133
+
1134
+ try {
1135
+ webviewId = queuedHostMessageWebviewIdBuf[0]!;
1136
+ rawMessage = new CString(messagePtr).toString();
1137
+ if (!rawMessage) {
1138
+ continue;
1139
+ }
1140
+
1141
+ const webview = BrowserView.ensureWrapped(webviewId);
1142
+ if (!webview) {
1143
+ continue;
1144
+ }
1145
+
1146
+ webview.rpcHandler?.(JSON.parse(rawMessage));
1147
+ } catch (err) {
1148
+ console.error("error draining queued host message:", {
1149
+ webviewId,
1150
+ messagePreview: rawMessage.slice(0, 500),
1151
+ error:
1152
+ err instanceof Error
1153
+ ? { name: err.name, message: err.message, stack: err.stack }
1154
+ : err,
1155
+ });
1156
+ } finally {
1157
+ core_.symbols.freeCoreString(messagePtr);
1158
+ }
1159
+ }
1160
+ };
1161
+
1162
+ let hostMessagePollingStarted = false;
1163
+ const startHostMessagePolling = (error?: unknown) => {
1164
+ if (hostMessagePollingStarted) {
1165
+ return;
1166
+ }
1167
+ hostMessagePollingStarted = true;
1168
+ const code =
1169
+ error && typeof error === "object" && "code" in error
1170
+ ? String((error as { code?: unknown }).code)
1171
+ : "";
1172
+ if (error && code !== "EAGAIN") {
1173
+ console.error("host message wakeup stream failed, falling back to polling:", error);
1174
+ }
1175
+ setInterval(drainQueuedHostMessages, 16);
1176
+ drainQueuedHostMessages();
1177
+ };
1178
+
1179
+ if (core) {
1180
+ const wakeupReadFd = core_.symbols.getHostMessageWakeupReadFD();
1181
+ const isRealBunRuntime = typeof process.versions?.bun === "string";
1182
+
1183
+ if (isRealBunRuntime) {
1184
+ startHostMessagePolling();
1185
+ } else if (typeof wakeupReadFd === "number" && wakeupReadFd >= 0) {
1186
+ try {
1187
+ const wakeupStream = createReadStream("/dev/null", {
1188
+ fd: wakeupReadFd,
1189
+ autoClose: false,
1190
+ });
1191
+ wakeupStream.on("data", () => {
1192
+ drainQueuedHostMessages();
1193
+ });
1194
+ wakeupStream.on("error", (error) => {
1195
+ wakeupStream.destroy();
1196
+ startHostMessagePolling(error);
1197
+ });
1198
+ } catch (error) {
1199
+ startHostMessagePolling(error);
1200
+ }
1201
+ } else {
1202
+ startHostMessagePolling();
1203
+ }
1204
+
1205
+ drainQueuedHostMessages();
1206
+ }
825
1207
 
826
1208
  const _ffiImpl = {
827
1209
  request: {
828
1210
  createWindow: (params: {
829
- id: number;
830
1211
  url: string | null;
831
1212
  title: string;
832
1213
  frame: {
@@ -857,9 +1238,8 @@ const _ffiImpl = {
857
1238
  x: number;
858
1239
  y: number;
859
1240
  };
860
- }): FFIType.ptr => {
1241
+ }): number => {
861
1242
  const {
862
- id,
863
1243
  url: _url,
864
1244
  title,
865
1245
  frame: { x, y, width, height },
@@ -884,7 +1264,7 @@ const _ffiImpl = {
884
1264
  trafficLightOffset = { x: 0, y: 0 },
885
1265
  } = params;
886
1266
 
887
- const styleMask = native_.symbols.getWindowStyle(
1267
+ const styleMask = core_.symbols.getWindowStyle(
888
1268
  Borderless,
889
1269
  Titled,
890
1270
  Closable,
@@ -899,8 +1279,7 @@ const _ffiImpl = {
899
1279
  HUDWindow,
900
1280
  );
901
1281
 
902
- const windowPtr = native_.symbols.createWindowWithFrameAndStyleFromWorker(
903
- id,
1282
+ const windowId = core_.symbols.createWindow(
904
1283
  // frame
905
1284
  x,
906
1285
  y,
@@ -910,6 +1289,9 @@ const _ffiImpl = {
910
1289
  // style
911
1290
  toCString(titleBarStyle),
912
1291
  transparent,
1292
+ toCString(title),
1293
+ hidden,
1294
+ activate,
913
1295
  trafficLightOffset.x,
914
1296
  trafficLightOffset.y,
915
1297
  // callbacks
@@ -921,26 +1303,24 @@ const _ffiImpl = {
921
1303
  windowKeyCallback,
922
1304
  );
923
1305
 
924
- if (!windowPtr) {
925
- throw "Failed to create window";
926
- }
927
-
928
- native_.symbols.setWindowTitle(windowPtr, toCString(title));
929
- if (!hidden) {
930
- native_.symbols.showWindow(windowPtr, activate);
1306
+ if (!windowId) {
1307
+ throw getCoreLastError() || "Failed to create window";
931
1308
  }
932
1309
 
933
- return windowPtr;
1310
+ return windowId;
1311
+ },
1312
+ getWindowPointer: (params: { winId: number }): Pointer | null => {
1313
+ return getWindowPtr(params.winId);
934
1314
  },
935
1315
  setTitle: (params: { winId: number; title: string }) => {
936
1316
  const { winId, title } = params;
937
1317
  const windowPtr = getWindowPtr(winId);
938
1318
 
939
1319
  if (!windowPtr) {
940
- throw `Can't add webview to window. window no longer exists`;
1320
+ throw `Can't set window title. Window no longer exists`;
941
1321
  }
942
1322
 
943
- native_.symbols.setWindowTitle(windowPtr, toCString(title));
1323
+ core_.symbols.setWindowTitle(winId, toCString(title));
944
1324
  },
945
1325
 
946
1326
  closeWindow: (params: { winId: number }) => {
@@ -952,7 +1332,7 @@ const _ffiImpl = {
952
1332
  return;
953
1333
  }
954
1334
 
955
- native_.symbols.closeWindow(windowPtr);
1335
+ core_.symbols.closeWindow(winId);
956
1336
  // Note: Cleanup of BrowserWindowMap happens in the windowCloseCallback
957
1337
  },
958
1338
 
@@ -964,7 +1344,7 @@ const _ffiImpl = {
964
1344
  throw `Can't show window. Window no longer exists`;
965
1345
  }
966
1346
 
967
- native_.symbols.showWindow(windowPtr, params.activate ?? true);
1347
+ core_.symbols.showWindow(winId, params.activate ?? true);
968
1348
  },
969
1349
 
970
1350
  activateWindow: (params: { winId: number }) => {
@@ -975,7 +1355,7 @@ const _ffiImpl = {
975
1355
  throw `Can't activate window. Window no longer exists`;
976
1356
  }
977
1357
 
978
- native_.symbols.activateWindow(windowPtr);
1358
+ core_.symbols.activateWindow(winId);
979
1359
  },
980
1360
 
981
1361
  hideWindow: (params: { winId: number }) => {
@@ -986,7 +1366,7 @@ const _ffiImpl = {
986
1366
  throw `Can't hide window. Window no longer exists`;
987
1367
  }
988
1368
 
989
- native_.symbols.hideWindow(windowPtr);
1369
+ core_.symbols.hideWindow(winId);
990
1370
  },
991
1371
 
992
1372
  minimizeWindow: (params: { winId: number }) => {
@@ -997,7 +1377,7 @@ const _ffiImpl = {
997
1377
  throw `Can't minimize window. Window no longer exists`;
998
1378
  }
999
1379
 
1000
- native_.symbols.minimizeWindow(windowPtr);
1380
+ core_.symbols.minimizeWindow(winId);
1001
1381
  },
1002
1382
 
1003
1383
  restoreWindow: (params: { winId: number }) => {
@@ -1008,7 +1388,7 @@ const _ffiImpl = {
1008
1388
  throw `Can't restore window. Window no longer exists`;
1009
1389
  }
1010
1390
 
1011
- native_.symbols.restoreWindow(windowPtr);
1391
+ core_.symbols.restoreWindow(winId);
1012
1392
  },
1013
1393
 
1014
1394
  isWindowMinimized: (params: { winId: number }): boolean => {
@@ -1019,7 +1399,7 @@ const _ffiImpl = {
1019
1399
  return false;
1020
1400
  }
1021
1401
 
1022
- return native_.symbols.isWindowMinimized(windowPtr);
1402
+ return core_.symbols.isWindowMinimized(winId);
1023
1403
  },
1024
1404
 
1025
1405
  maximizeWindow: (params: { winId: number }) => {
@@ -1030,7 +1410,7 @@ const _ffiImpl = {
1030
1410
  throw `Can't maximize window. Window no longer exists`;
1031
1411
  }
1032
1412
 
1033
- native_.symbols.maximizeWindow(windowPtr);
1413
+ core_.symbols.maximizeWindow(winId);
1034
1414
  },
1035
1415
 
1036
1416
  unmaximizeWindow: (params: { winId: number }) => {
@@ -1041,7 +1421,7 @@ const _ffiImpl = {
1041
1421
  throw `Can't unmaximize window. Window no longer exists`;
1042
1422
  }
1043
1423
 
1044
- native_.symbols.unmaximizeWindow(windowPtr);
1424
+ core_.symbols.unmaximizeWindow(winId);
1045
1425
  },
1046
1426
 
1047
1427
  isWindowMaximized: (params: { winId: number }): boolean => {
@@ -1052,7 +1432,7 @@ const _ffiImpl = {
1052
1432
  return false;
1053
1433
  }
1054
1434
 
1055
- return native_.symbols.isWindowMaximized(windowPtr);
1435
+ return core_.symbols.isWindowMaximized(winId);
1056
1436
  },
1057
1437
 
1058
1438
  setWindowFullScreen: (params: { winId: number; fullScreen: boolean }) => {
@@ -1063,7 +1443,7 @@ const _ffiImpl = {
1063
1443
  throw `Can't set fullscreen. Window no longer exists`;
1064
1444
  }
1065
1445
 
1066
- native_.symbols.setWindowFullScreen(windowPtr, fullScreen);
1446
+ core_.symbols.setWindowFullScreen(winId, fullScreen);
1067
1447
  },
1068
1448
 
1069
1449
  isWindowFullScreen: (params: { winId: number }): boolean => {
@@ -1074,7 +1454,7 @@ const _ffiImpl = {
1074
1454
  return false;
1075
1455
  }
1076
1456
 
1077
- return native_.symbols.isWindowFullScreen(windowPtr);
1457
+ return core_.symbols.isWindowFullScreen(winId);
1078
1458
  },
1079
1459
 
1080
1460
  setWindowAlwaysOnTop: (params: { winId: number; alwaysOnTop: boolean }) => {
@@ -1085,7 +1465,7 @@ const _ffiImpl = {
1085
1465
  throw `Can't set always on top. Window no longer exists`;
1086
1466
  }
1087
1467
 
1088
- native_.symbols.setWindowAlwaysOnTop(windowPtr, alwaysOnTop);
1468
+ core_.symbols.setWindowAlwaysOnTop(winId, alwaysOnTop);
1089
1469
  },
1090
1470
 
1091
1471
  isWindowAlwaysOnTop: (params: { winId: number }): boolean => {
@@ -1096,7 +1476,7 @@ const _ffiImpl = {
1096
1476
  return false;
1097
1477
  }
1098
1478
 
1099
- return native_.symbols.isWindowAlwaysOnTop(windowPtr);
1479
+ return core_.symbols.isWindowAlwaysOnTop(winId);
1100
1480
  },
1101
1481
 
1102
1482
  setWindowVisibleOnAllWorkspaces: (params: {
@@ -1104,55 +1484,55 @@ const _ffiImpl = {
1104
1484
  visibleOnAllWorkspaces: boolean;
1105
1485
  }) => {
1106
1486
  const { winId, visibleOnAllWorkspaces } = params;
1107
- const windowPtr = BrowserWindow.getById(winId)?.ptr;
1487
+ const windowPtr = getWindowPtr(winId);
1108
1488
 
1109
1489
  if (!windowPtr) {
1110
1490
  throw `Can't set visible on all workspaces. Window no longer exists`;
1111
1491
  }
1112
1492
 
1113
- native_.symbols.setWindowVisibleOnAllWorkspaces(
1114
- windowPtr,
1493
+ core_.symbols.setWindowVisibleOnAllWorkspaces(
1494
+ winId,
1115
1495
  visibleOnAllWorkspaces,
1116
1496
  );
1117
1497
  },
1118
1498
 
1119
1499
  isWindowVisibleOnAllWorkspaces: (params: { winId: number }): boolean => {
1120
1500
  const { winId } = params;
1121
- const windowPtr = BrowserWindow.getById(winId)?.ptr;
1501
+ const windowPtr = getWindowPtr(winId);
1122
1502
 
1123
1503
  if (!windowPtr) {
1124
1504
  return false;
1125
1505
  }
1126
1506
 
1127
- return native_.symbols.isWindowVisibleOnAllWorkspaces(windowPtr);
1507
+ return core_.symbols.isWindowVisibleOnAllWorkspaces(winId);
1128
1508
  },
1129
1509
 
1130
- setWindowPosition: (params: { winId: number; x: number; y: number }) => {
1131
- const { winId, x, y } = params;
1132
- const windowPtr = getWindowPtr(winId);
1510
+ setWindowPosition: (params: { winId: number; x: number; y: number }) => {
1511
+ const { winId, x, y } = params;
1512
+ const windowPtr = getWindowPtr(winId);
1133
1513
 
1134
- if (!windowPtr) {
1135
- throw `Can't set window position. Window no longer exists`;
1136
- }
1514
+ if (!windowPtr) {
1515
+ throw `Can't set window position. Window no longer exists`;
1516
+ }
1137
1517
 
1138
- native_.symbols.setWindowPosition(windowPtr, x, y);
1139
- },
1518
+ core_.symbols.setWindowPosition(winId, x, y);
1519
+ },
1140
1520
 
1141
- setWindowButtonPosition: (params: { winId: number; x: number; y: number }) => {
1142
- const { winId, x, y } = params;
1143
- const windowPtr = getWindowPtr(winId);
1521
+ setWindowButtonPosition: (params: { winId: number; x: number; y: number }) => {
1522
+ const { winId, x, y } = params;
1523
+ const windowPtr = getWindowPtr(winId);
1144
1524
 
1145
- if (!windowPtr) {
1146
- throw `Can't set window button position. Window no longer exists`;
1147
- }
1525
+ if (!windowPtr) {
1526
+ throw `Can't set window button position. Window no longer exists`;
1527
+ }
1148
1528
 
1149
- native_.symbols.setWindowButtonPosition(windowPtr, x, y);
1150
- },
1529
+ core_.symbols.setWindowButtonPosition(winId, x, y);
1530
+ },
1151
1531
 
1152
- setWindowSize: (params: {
1153
- winId: number;
1154
- width: number;
1155
- height: number;
1532
+ setWindowSize: (params: {
1533
+ winId: number;
1534
+ width: number;
1535
+ height: number;
1156
1536
  }) => {
1157
1537
  const { winId, width, height } = params;
1158
1538
  const windowPtr = getWindowPtr(winId);
@@ -1161,7 +1541,7 @@ const _ffiImpl = {
1161
1541
  throw `Can't set window size. Window no longer exists`;
1162
1542
  }
1163
1543
 
1164
- native_.symbols.setWindowSize(windowPtr, width, height);
1544
+ core_.symbols.setWindowSize(winId, width, height);
1165
1545
  },
1166
1546
 
1167
1547
  setWindowFrame: (params: {
@@ -1178,7 +1558,7 @@ const _ffiImpl = {
1178
1558
  throw `Can't set window frame. Window no longer exists`;
1179
1559
  }
1180
1560
 
1181
- native_.symbols.setWindowFrame(windowPtr, x, y, width, height);
1561
+ core_.symbols.setWindowFrame(winId, x, y, width, height);
1182
1562
  },
1183
1563
 
1184
1564
  getWindowFrame: (params: {
@@ -1197,8 +1577,8 @@ const _ffiImpl = {
1197
1577
  const widthBuf = new Float64Array(1);
1198
1578
  const heightBuf = new Float64Array(1);
1199
1579
 
1200
- native_.symbols.getWindowFrame(
1201
- windowPtr,
1580
+ core_.symbols.getWindowFrame(
1581
+ winId,
1202
1582
  ptr(xBuf),
1203
1583
  ptr(yBuf),
1204
1584
  ptr(widthBuf),
@@ -1212,17 +1592,12 @@ const _ffiImpl = {
1212
1592
  height: heightBuf[0]!,
1213
1593
  };
1214
1594
  },
1215
-
1216
1595
  createWebview: (params: {
1217
- id: number;
1218
1596
  windowId: number;
1597
+ hostWebviewId: number | null;
1219
1598
  renderer: "cef" | "native";
1220
- rpcPort: number;
1221
1599
  secretKey: string;
1222
- hostWebviewId: number | null;
1223
- pipePrefix: string;
1224
1600
  url: string | null;
1225
- html: string | null;
1226
1601
  partition: string | null;
1227
1602
  preload: string | null;
1228
1603
  viewsRoot: string | null;
@@ -1237,17 +1612,13 @@ const _ffiImpl = {
1237
1612
  sandbox: boolean;
1238
1613
  startTransparent: boolean;
1239
1614
  startPassthrough: boolean;
1240
- }): FFIType.ptr => {
1615
+ }): number => {
1241
1616
  const {
1242
- id,
1243
1617
  windowId,
1618
+ hostWebviewId,
1244
1619
  renderer,
1245
- rpcPort,
1246
1620
  secretKey,
1247
- // hostWebviewId: number | null;
1248
- // pipePrefix: string;
1249
1621
  url,
1250
- // html: string | null;
1251
1622
  partition,
1252
1623
  preload,
1253
1624
  viewsRoot,
@@ -1257,62 +1628,11 @@ const _ffiImpl = {
1257
1628
  startTransparent,
1258
1629
  startPassthrough,
1259
1630
  } = params;
1631
+ ensureWebviewRuntimeConfigured();
1260
1632
 
1261
- const parentWindow = BrowserWindow.getById(windowId);
1262
- const windowPtr = parentWindow?.ptr;
1263
- // Get transparent flag from parent window
1264
- const transparent = parentWindow?.transparent ?? false;
1265
-
1266
- if (!windowPtr) {
1267
- throw `Can't add webview to window. window no longer exists`;
1268
- }
1269
-
1270
- // Dynamic setup per-webview (variables that change for each webview)
1271
- // EventBridge is available for ALL webviews (including sandboxed) for event emission
1272
- // InternalBridge and BunBridge are only available for trusted (non-sandboxed) webviews
1273
- let dynamicPreload: string;
1274
- let selectedPreloadScript: string;
1275
-
1276
- if (sandbox) {
1277
- // Sandboxed webview: minimal preload with only event emission capability
1278
- // Note: We set up internalBridge for event emission fallback (until native code
1279
- // adds dedicated eventBridge handler). The security is enforced because:
1280
- // 1. Sandboxed preload has NO RPC code - it can only emit events
1281
- // 2. No bunBridge is set up - no user RPC communication
1282
- // 3. No secretKey/rpcPort - no encrypted socket RPC
1283
- // 4. No webview tag support - can't create OOPIFs
1284
- // Note: Check existing value first to preserve bridges already set by CEF's OnContextCreated
1285
- dynamicPreload = `
1286
- window.__electrobunWebviewId = ${id};
1287
- window.__electrobunWindowId = ${windowId};
1288
- window.__electrobunEventBridge = window.__electrobunEventBridge || window.webkit?.messageHandlers?.eventBridge || window.eventBridge || window.chrome?.webview?.hostObjects?.eventBridge;
1289
- window.__electrobunInternalBridge = window.__electrobunInternalBridge || window.webkit?.messageHandlers?.internalBridge || window.internalBridge || window.chrome?.webview?.hostObjects?.internalBridge;
1290
- `;
1291
- selectedPreloadScript = preloadScriptSandboxed;
1292
- } else {
1293
- // Trusted webview: all bridges, full preload
1294
- // Note: Check existing value first to preserve bridges already set by CEF's OnContextCreated
1295
- dynamicPreload = `
1296
- window.__electrobunWebviewId = ${id};
1297
- window.__electrobunWindowId = ${windowId};
1298
- window.__electrobunRpcSocketPort = ${rpcPort};
1299
- window.__electrobunSecretKeyBytes = [${secretKey}];
1300
- window.__electrobunEventBridge = window.__electrobunEventBridge || window.webkit?.messageHandlers?.eventBridge || window.eventBridge || window.chrome?.webview?.hostObjects?.eventBridge;
1301
- window.__electrobunInternalBridge = window.__electrobunInternalBridge || window.webkit?.messageHandlers?.internalBridge || window.internalBridge || window.chrome?.webview?.hostObjects?.internalBridge;
1302
- window.__electrobunBunBridge = window.__electrobunBunBridge || window.webkit?.messageHandlers?.bunBridge || window.bunBridge || window.chrome?.webview?.hostObjects?.bunBridge;
1303
- `;
1304
- selectedPreloadScript = preloadScript;
1305
- }
1306
-
1307
- const electrobunPreload = dynamicPreload + selectedPreloadScript;
1308
-
1309
- const customPreload = preload;
1310
-
1311
- // Pre-set flags before initWebview (workaround for FFI param count limits)
1312
- native_.symbols.setNextWebviewFlags(startTransparent, startPassthrough);
1313
- const webviewPtr = native_.symbols.initWebview(
1314
- id,
1315
- windowPtr,
1633
+ const webviewId = core_.symbols.createWebview(
1634
+ windowId,
1635
+ hostWebviewId || 0,
1316
1636
  toCString(renderer),
1317
1637
  toCString(url || ""),
1318
1638
  x,
@@ -1323,25 +1643,104 @@ window.__electrobunBunBridge = window.__electrobunBunBridge || window.webkit?.me
1323
1643
  toCString(partition || "persist:default"),
1324
1644
  webviewDecideNavigation,
1325
1645
  webviewEventJSCallback,
1326
- eventBridgeHandler, // Event-only bridge (always active, for dom-ready, navigation, etc.)
1327
- bunBridgePostmessageHandler, // User RPC bridge (disabled in sandbox mode)
1328
- internalBridgeHandler, // Internal RPC bridge (disabled in sandbox mode)
1329
- toCString(electrobunPreload),
1330
- toCString(customPreload || ""),
1646
+ eventBridgeHandler,
1647
+ hostBridgePostmessageHandler,
1648
+ internalBridgeHandler,
1649
+ toCString(secretKey),
1650
+ toCString(preload || ""),
1331
1651
  toCString(viewsRoot || ""),
1332
- transparent,
1333
- sandbox, // When true, bunBridge and internalBridge are not set up in native code
1652
+ sandbox, // When true, hostBridge and internalBridge are not set up in native code
1653
+ startTransparent,
1654
+ startPassthrough,
1334
1655
  );
1335
1656
 
1336
- if (!webviewPtr) {
1337
- throw "Failed to create webview";
1657
+ if (!webviewId) {
1658
+ throw getCoreLastError() || "Failed to create webview";
1338
1659
  }
1339
1660
 
1340
- return webviewPtr;
1661
+ return webviewId;
1662
+ },
1663
+ getWebviewPointer: (params: { id: number }): Pointer | null => {
1664
+ return core_.symbols.getWebviewPointer(params.id) || null;
1665
+ },
1666
+ resizeWebview: (params: {
1667
+ id: number;
1668
+ frame: { x: number; y: number; width: number; height: number };
1669
+ masks?: string;
1670
+ }) => {
1671
+ const { id, frame: { x, y, width, height }, masks = "[]" } = params;
1672
+ core_.symbols.resizeWebview(id, x, y, width, height, toCString(masks));
1673
+ },
1674
+ loadURLInWebView: (params: { id: number; url: string }) => {
1675
+ core_.symbols.loadURLInWebView(params.id, toCString(params.url));
1676
+ },
1677
+ loadHTMLInWebView: (params: { id: number; html: string }) => {
1678
+ core_.symbols.loadHTMLInWebView(params.id, toCString(params.html));
1679
+ },
1680
+ updatePreloadScriptToWebView: (params: {
1681
+ id: number;
1682
+ scriptIdentifier: string;
1683
+ script: string;
1684
+ allFrames: boolean;
1685
+ }) => {
1686
+ core_.symbols.updatePreloadScriptToWebView(
1687
+ params.id,
1688
+ toCString(params.scriptIdentifier),
1689
+ toCString(params.script),
1690
+ params.allFrames,
1691
+ );
1692
+ },
1693
+ webviewCanGoBack: (params: { id: number }) => {
1694
+ return core_.symbols.webviewCanGoBack(params.id);
1695
+ },
1696
+ webviewCanGoForward: (params: { id: number }) => {
1697
+ return core_.symbols.webviewCanGoForward(params.id);
1698
+ },
1699
+ webviewGoBack: (params: { id: number }) => {
1700
+ core_.symbols.webviewGoBack(params.id);
1701
+ },
1702
+ webviewGoForward: (params: { id: number }) => {
1703
+ core_.symbols.webviewGoForward(params.id);
1704
+ },
1705
+ webviewReload: (params: { id: number }) => {
1706
+ core_.symbols.webviewReload(params.id);
1707
+ },
1708
+ webviewRemove: (params: { id: number }) => {
1709
+ core_.symbols.webviewRemove(params.id);
1710
+ },
1711
+ setWebviewHTMLContent: (params: { id: number; html: string }) => {
1712
+ core_.symbols.setWebviewHTMLContent(params.id, toCString(params.html));
1713
+ },
1714
+ webviewSetTransparent: (params: { id: number; transparent: boolean }) => {
1715
+ core_.symbols.webviewSetTransparent(params.id, params.transparent);
1716
+ },
1717
+ webviewSetPassthrough: (params: { id: number; passthrough: boolean }) => {
1718
+ core_.symbols.webviewSetPassthrough(params.id, params.passthrough);
1719
+ },
1720
+ webviewSetHidden: (params: { id: number; hidden: boolean }) => {
1721
+ core_.symbols.webviewSetHidden(params.id, params.hidden);
1722
+ },
1723
+ setWebviewNavigationRules: (params: { id: number; rulesJson: string }) => {
1724
+ core_.symbols.setWebviewNavigationRules(params.id, toCString(params.rulesJson));
1725
+ },
1726
+ webviewFindInPage: (params: {
1727
+ id: number;
1728
+ searchText: string;
1729
+ forward: boolean;
1730
+ matchCase: boolean;
1731
+ }) => {
1732
+ core_.symbols.webviewFindInPage(
1733
+ params.id,
1734
+ toCString(params.searchText),
1735
+ params.forward,
1736
+ params.matchCase,
1737
+ );
1738
+ },
1739
+ webviewStopFind: (params: { id: number }) => {
1740
+ core_.symbols.webviewStopFind(params.id);
1341
1741
  },
1342
1742
 
1343
1743
  createWGPUView: (params: {
1344
- id: number;
1345
1744
  windowId: number;
1346
1745
  frame: {
1347
1746
  x: number;
@@ -1352,9 +1751,8 @@ window.__electrobunBunBridge = window.__electrobunBunBridge || window.webkit?.me
1352
1751
  autoResize: boolean;
1353
1752
  startTransparent: boolean;
1354
1753
  startPassthrough: boolean;
1355
- }): FFIType.ptr => {
1754
+ }): number => {
1356
1755
  const {
1357
- id,
1358
1756
  windowId,
1359
1757
  frame: { x, y, width, height },
1360
1758
  autoResize,
@@ -1362,14 +1760,8 @@ window.__electrobunBunBridge = window.__electrobunBunBridge || window.webkit?.me
1362
1760
  startPassthrough,
1363
1761
  } = params;
1364
1762
 
1365
- const windowPtr = getWindowPtr(windowId);
1366
- if (!windowPtr) {
1367
- throw `Can't add WGPUView to window. window no longer exists`;
1368
- }
1369
-
1370
- const viewPtr = native_.symbols.initWGPUView(
1371
- id,
1372
- windowPtr,
1763
+ const viewId = core_.symbols.createWGPUView(
1764
+ windowId,
1373
1765
  x,
1374
1766
  y,
1375
1767
  width,
@@ -1379,11 +1771,14 @@ window.__electrobunBunBridge = window.__electrobunBunBridge || window.webkit?.me
1379
1771
  startPassthrough,
1380
1772
  );
1381
1773
 
1382
- if (!viewPtr) {
1774
+ if (!viewId) {
1383
1775
  throw "Failed to create WGPUView";
1384
1776
  }
1385
1777
 
1386
- return viewPtr;
1778
+ return viewId;
1779
+ },
1780
+ getWGPUViewPointer: (params: { id: number }): Pointer | null => {
1781
+ return core_.symbols.getWGPUViewPointer(params.id) || null;
1387
1782
  },
1388
1783
 
1389
1784
  wgpuViewSetFrame: (params: {
@@ -1393,16 +1788,8 @@ window.__electrobunBunBridge = window.__electrobunBunBridge || window.webkit?.me
1393
1788
  width: number;
1394
1789
  height: number;
1395
1790
  }) => {
1396
- const view = WGPUView.getById(params.id);
1397
- if (!view?.ptr) {
1398
- console.error(
1399
- `wgpuViewSetFrame: WGPUView not found or has no ptr for id ${params.id}`,
1400
- );
1401
- return;
1402
- }
1403
-
1404
- native_.symbols.wgpuViewSetFrame(
1405
- view.ptr,
1791
+ core_.symbols.setWGPUViewFrame(
1792
+ params.id,
1406
1793
  params.x,
1407
1794
  params.y,
1408
1795
  params.width,
@@ -1411,97 +1798,83 @@ window.__electrobunBunBridge = window.__electrobunBunBridge || window.webkit?.me
1411
1798
  },
1412
1799
 
1413
1800
  wgpuViewSetTransparent: (params: { id: number; transparent: boolean }) => {
1414
- const view = WGPUView.getById(params.id);
1415
- if (!view?.ptr) {
1416
- console.error(
1417
- `wgpuViewSetTransparent: WGPUView not found or has no ptr for id ${params.id}`,
1418
- );
1419
- return;
1420
- }
1421
-
1422
- native_.symbols.wgpuViewSetTransparent(view.ptr, params.transparent);
1801
+ core_.symbols.setWGPUViewTransparent(params.id, params.transparent);
1423
1802
  },
1424
1803
 
1425
1804
  wgpuViewSetPassthrough: (params: {
1426
1805
  id: number;
1427
1806
  passthrough: boolean;
1428
1807
  }) => {
1429
- const view = WGPUView.getById(params.id);
1430
- if (!view?.ptr) {
1431
- console.error(
1432
- `wgpuViewSetPassthrough: WGPUView not found or has no ptr for id ${params.id}`,
1433
- );
1434
- return;
1435
- }
1436
-
1437
- native_.symbols.wgpuViewSetPassthrough(view.ptr, params.passthrough);
1808
+ core_.symbols.setWGPUViewPassthrough(params.id, params.passthrough);
1438
1809
  },
1439
1810
 
1440
1811
  wgpuViewSetHidden: (params: { id: number; hidden: boolean }) => {
1441
- const view = WGPUView.getById(params.id);
1442
- if (!view?.ptr) {
1443
- console.error(
1444
- `wgpuViewSetHidden: WGPUView not found or has no ptr for id ${params.id}`,
1445
- );
1446
- return;
1447
- }
1448
-
1449
- native_.symbols.wgpuViewSetHidden(view.ptr, params.hidden);
1812
+ core_.symbols.setWGPUViewHidden(params.id, params.hidden);
1450
1813
  },
1451
1814
 
1452
1815
  wgpuViewRemove: (params: { id: number }) => {
1453
- const view = WGPUView.getById(params.id);
1454
- if (!view?.ptr) {
1455
- console.error(
1456
- `wgpuViewRemove: WGPUView not found or has no ptr for id ${params.id}`,
1457
- );
1458
- return;
1459
- }
1460
-
1461
- native_.symbols.wgpuViewRemove(view.ptr);
1816
+ core_.symbols.removeWGPUView(params.id);
1462
1817
  },
1463
1818
  wgpuViewGetNativeHandle: (params: { id: number }): Pointer | null => {
1464
- const view = WGPUView.getById(params.id);
1465
- if (!view?.ptr) {
1466
- console.error(
1467
- `wgpuViewGetNativeHandle: WGPUView not found or has no ptr for id ${params.id}`,
1468
- );
1469
- return null;
1470
- }
1471
-
1472
- const handle = native_.symbols.wgpuViewGetNativeHandle(view.ptr);
1473
- return handle || null;
1819
+ return core_.symbols.getWGPUViewNativeHandle(params.id) || null;
1820
+ },
1821
+ runWGPUViewTest: (params: { id: number }) => {
1822
+ core_.symbols.runWGPUViewTest(params.id);
1474
1823
  },
1475
1824
 
1476
1825
  evaluateJavascriptWithNoCompletion: (params: {
1477
1826
  id: number;
1478
1827
  js: string;
1479
1828
  }) => {
1480
- const { id, js } = params;
1481
- const webview = BrowserView.getById(id);
1482
-
1483
- if (!webview?.ptr) {
1484
- return;
1485
- }
1486
-
1487
- native_.symbols.evaluateJavaScriptWithNoCompletion(
1488
- webview.ptr,
1489
- toCString(js),
1829
+ core_.symbols.evaluateJavaScriptWithNoCompletion(
1830
+ params.id,
1831
+ toCString(params.js),
1832
+ );
1833
+ },
1834
+ sendHostMessageToWebviewViaTransport: (params: {
1835
+ id: number;
1836
+ messageJson: string;
1837
+ }): boolean => {
1838
+ return core_.symbols.sendHostMessageToWebviewViaTransport(
1839
+ params.id,
1840
+ toCString(params.messageJson),
1490
1841
  );
1491
1842
  },
1843
+ clearWebviewHostTransport: (params: { id: number }) => {
1844
+ core_.symbols.clearWebviewHostTransport(params.id);
1845
+ },
1846
+ webviewOpenDevTools: (params: { id: number }) => {
1847
+ core_.symbols.webviewOpenDevTools(params.id);
1848
+ },
1849
+ webviewCloseDevTools: (params: { id: number }) => {
1850
+ core_.symbols.webviewCloseDevTools(params.id);
1851
+ },
1852
+ webviewToggleDevTools: (params: { id: number }) => {
1853
+ core_.symbols.webviewToggleDevTools(params.id);
1854
+ },
1855
+ webviewSetPageZoom: (params: { id: number; zoomLevel: number }) => {
1856
+ core_.symbols.webviewSetPageZoom(params.id, params.zoomLevel);
1857
+ },
1858
+ webviewGetPageZoom: (params: { id: number }): number => {
1859
+ return core_.symbols.webviewGetPageZoom(params.id);
1860
+ },
1861
+ setExitOnLastWindowClosed: (params: { enabled: boolean }) => {
1862
+ core_.symbols.setExitOnLastWindowClosed(params.enabled);
1863
+ },
1864
+ quitGracefully: (params: { code: number; timeoutMs: number }) => {
1865
+ core_.symbols.quitGracefully(params.code, params.timeoutMs);
1866
+ },
1492
1867
 
1493
1868
  createTray: (params: {
1494
- id: number;
1495
1869
  title: string;
1496
1870
  image: string;
1497
1871
  template: boolean;
1498
1872
  width: number;
1499
1873
  height: number;
1500
- }): FFIType.ptr => {
1501
- const { id, title, image, template, width, height } = params;
1874
+ }): number => {
1875
+ const { title, image, template, width, height } = params;
1502
1876
 
1503
- const trayPtr = native_.symbols.createTray(
1504
- id,
1877
+ const trayId = core_.symbols.createTray(
1505
1878
  toCString(title),
1506
1879
  toCString(image),
1507
1880
  template,
@@ -1510,27 +1883,25 @@ window.__electrobunBunBridge = window.__electrobunBunBridge || window.webkit?.me
1510
1883
  trayItemHandler,
1511
1884
  );
1512
1885
 
1513
- if (!trayPtr) {
1886
+ if (!trayId) {
1514
1887
  throw "Failed to create tray";
1515
1888
  }
1516
1889
 
1517
- return trayPtr;
1890
+ return trayId;
1891
+ },
1892
+ showTray: (params: { id: number }): boolean => {
1893
+ return core_.symbols.showTray(params.id);
1894
+ },
1895
+ hideTray: (params: { id: number }): void => {
1896
+ core_.symbols.hideTray(params.id);
1518
1897
  },
1519
1898
  setTrayTitle: (params: { id: number; title: string }): void => {
1520
1899
  const { id, title } = params;
1521
-
1522
- const tray = Tray.getById(id);
1523
- if (!tray) return;
1524
-
1525
- native_.symbols.setTrayTitle(tray.ptr, toCString(title));
1900
+ core_.symbols.setTrayTitle(id, toCString(title));
1526
1901
  },
1527
1902
  setTrayImage: (params: { id: number; image: string }): void => {
1528
1903
  const { id, image } = params;
1529
-
1530
- const tray = Tray.getById(id);
1531
- if (!tray) return;
1532
-
1533
- native_.symbols.setTrayImage(tray.ptr, toCString(image));
1904
+ core_.symbols.setTrayImage(id, toCString(image));
1534
1905
  },
1535
1906
  setTrayMenu: (params: {
1536
1907
  id: number;
@@ -1538,31 +1909,14 @@ window.__electrobunBunBridge = window.__electrobunBunBridge || window.webkit?.me
1538
1909
  menuConfig: string;
1539
1910
  }): void => {
1540
1911
  const { id, menuConfig } = params;
1541
-
1542
- const tray = Tray.getById(id);
1543
- if (!tray) return;
1544
-
1545
- native_.symbols.setTrayMenu(tray.ptr, toCString(menuConfig));
1912
+ core_.symbols.setTrayMenu(id, toCString(menuConfig));
1546
1913
  },
1547
1914
 
1548
1915
  removeTray: (params: { id: number }): void => {
1549
- const { id } = params;
1550
- const tray = Tray.getById(id);
1551
-
1552
- if (!tray) {
1553
- throw `Can't remove tray. Tray no longer exists`;
1554
- }
1555
-
1556
- native_.symbols.removeTray(tray.ptr);
1557
- // The Tray class will handle removing from TrayMap
1916
+ core_.symbols.removeTray(params.id);
1558
1917
  },
1559
1918
  getTrayBounds: (params: { id: number }): Rectangle => {
1560
- const tray = Tray.getById(params.id);
1561
- if (!tray?.ptr) {
1562
- return { x: 0, y: 0, width: 0, height: 0 };
1563
- }
1564
-
1565
- const jsonStr = native_.symbols.getTrayBounds(tray.ptr);
1919
+ const jsonStr = core_.symbols.getTrayBounds(params.id);
1566
1920
  if (!jsonStr) {
1567
1921
  return { x: 0, y: 0, width: 0, height: 0 };
1568
1922
  }
@@ -1576,7 +1930,7 @@ window.__electrobunBunBridge = window.__electrobunBunBridge || window.webkit?.me
1576
1930
  setApplicationMenu: (params: { menuConfig: string }): void => {
1577
1931
  const { menuConfig } = params;
1578
1932
 
1579
- native_.symbols.setApplicationMenu(
1933
+ core_.symbols.setApplicationMenu(
1580
1934
  toCString(menuConfig),
1581
1935
  applicationMenuHandler,
1582
1936
  );
@@ -1584,25 +1938,25 @@ window.__electrobunBunBridge = window.__electrobunBunBridge || window.webkit?.me
1584
1938
  showContextMenu: (params: { menuConfig: string }): void => {
1585
1939
  const { menuConfig } = params;
1586
1940
 
1587
- native_.symbols.showContextMenu(toCString(menuConfig), contextMenuHandler);
1941
+ core_.symbols.showContextMenu(toCString(menuConfig), contextMenuHandler);
1588
1942
  },
1589
1943
  moveToTrash: (params: { path: string }): boolean => {
1590
1944
  const { path } = params;
1591
1945
 
1592
- return native_.symbols.moveToTrash(toCString(path));
1946
+ return core_.symbols.moveToTrash(toCString(path));
1593
1947
  },
1594
1948
  showItemInFolder: (params: { path: string }): void => {
1595
1949
  const { path } = params;
1596
1950
 
1597
- native_.symbols.showItemInFolder(toCString(path));
1951
+ core_.symbols.showItemInFolder(toCString(path));
1598
1952
  },
1599
1953
  openExternal: (params: { url: string }): boolean => {
1600
1954
  const { url } = params;
1601
- return native_.symbols.openExternal(toCString(url));
1955
+ return core_.symbols.openExternal(toCString(url));
1602
1956
  },
1603
1957
  openPath: (params: { path: string }): boolean => {
1604
1958
  const { path } = params;
1605
- return native_.symbols.openPath(toCString(path));
1959
+ return core_.symbols.openPath(toCString(path));
1606
1960
  },
1607
1961
  showNotification: (params: {
1608
1962
  title: string;
@@ -1611,7 +1965,7 @@ window.__electrobunBunBridge = window.__electrobunBunBridge || window.webkit?.me
1611
1965
  silent?: boolean;
1612
1966
  }): void => {
1613
1967
  const { title, body = "", subtitle = "", silent = false } = params;
1614
- native_.symbols.showNotification(
1968
+ core_.symbols.showNotification(
1615
1969
  toCString(title),
1616
1970
  toCString(body),
1617
1971
  toCString(subtitle),
@@ -1619,10 +1973,10 @@ window.__electrobunBunBridge = window.__electrobunBunBridge || window.webkit?.me
1619
1973
  );
1620
1974
  },
1621
1975
  setDockIconVisible: (params: { visible: boolean }): void => {
1622
- native_.symbols.setDockIconVisible(params.visible);
1976
+ core_.symbols.setDockIconVisible(params.visible);
1623
1977
  },
1624
1978
  isDockIconVisible: (): boolean => {
1625
- return native_.symbols.isDockIconVisible();
1979
+ return core_.symbols.isDockIconVisible();
1626
1980
  },
1627
1981
  openFileDialog: (params: {
1628
1982
  startingFolder: string;
@@ -1638,7 +1992,7 @@ window.__electrobunBunBridge = window.__electrobunBunBridge || window.webkit?.me
1638
1992
  canChooseDirectory,
1639
1993
  allowsMultipleSelection,
1640
1994
  } = params;
1641
- const filePath = native_.symbols.openFileDialog(
1995
+ const filePath = core_.symbols.openFileDialog(
1642
1996
  toCString(startingFolder),
1643
1997
  toCString(allowedFileTypes),
1644
1998
  canChooseFiles ? 1 : 0,
@@ -1668,7 +2022,7 @@ window.__electrobunBunBridge = window.__electrobunBunBridge || window.webkit?.me
1668
2022
  } = params;
1669
2023
  // Convert buttons array to comma-separated string
1670
2024
  const buttonsStr = buttons.join(",");
1671
- return native_.symbols.showMessageBox(
2025
+ return core_.symbols.showMessageBox(
1672
2026
  toCString(type),
1673
2027
  toCString(title),
1674
2028
  toCString(message),
@@ -1681,17 +2035,17 @@ window.__electrobunBunBridge = window.__electrobunBunBridge || window.webkit?.me
1681
2035
 
1682
2036
  // Clipboard API
1683
2037
  clipboardReadText: (): string | null => {
1684
- const result = native_.symbols.clipboardReadText();
2038
+ const result = core_.symbols.clipboardReadText();
1685
2039
  if (!result) return null;
1686
2040
  return result.toString();
1687
2041
  },
1688
2042
  clipboardWriteText: (params: { text: string }): void => {
1689
- native_.symbols.clipboardWriteText(toCString(params.text));
2043
+ core_.symbols.clipboardWriteText(toCString(params.text));
1690
2044
  },
1691
2045
  clipboardReadImage: (): Uint8Array | null => {
1692
2046
  // Allocate a buffer for the size output
1693
2047
  const sizeBuffer = new BigUint64Array(1);
1694
- const dataPtr = native_.symbols.clipboardReadImage(ptr(sizeBuffer));
2048
+ const dataPtr = core_.symbols.clipboardReadImage(ptr(sizeBuffer));
1695
2049
 
1696
2050
  if (!dataPtr) return null;
1697
2051
 
@@ -1711,13 +2065,13 @@ window.__electrobunBunBridge = window.__electrobunBunBridge || window.webkit?.me
1711
2065
  },
1712
2066
  clipboardWriteImage: (params: { pngData: Uint8Array }): void => {
1713
2067
  const { pngData } = params;
1714
- native_.symbols.clipboardWriteImage(ptr(pngData), BigInt(pngData.length));
2068
+ core_.symbols.clipboardWriteImage(ptr(pngData), BigInt(pngData.length));
1715
2069
  },
1716
2070
  clipboardClear: (): void => {
1717
- native_.symbols.clipboardClear();
2071
+ core_.symbols.clipboardClear();
1718
2072
  },
1719
2073
  clipboardAvailableFormats: (): string[] => {
1720
- const result = native_.symbols.clipboardAvailableFormats();
2074
+ const result = core_.symbols.clipboardAvailableFormats();
1721
2075
  if (!result) return [];
1722
2076
  const formatsStr = result.toString();
1723
2077
  if (!formatsStr) return [];
@@ -2054,7 +2408,7 @@ const getMimeType = new JSCallback(
2054
2408
 
2055
2409
  const getHTMLForWebviewSync = new JSCallback(
2056
2410
  (webviewId) => {
2057
- const webview = BrowserView.getById(webviewId);
2411
+ const webview = BrowserView.ensureWrapped(webviewId);
2058
2412
 
2059
2413
  return toCString(webview?.html || "");
2060
2414
  },
@@ -2088,7 +2442,7 @@ if (native) {
2088
2442
  const appReopenCallback = new JSCallback(
2089
2443
  () => {
2090
2444
  if (process.platform === "darwin") {
2091
- native_.symbols.setDockIconVisible(true);
2445
+ core_.symbols.setDockIconVisible(true);
2092
2446
  }
2093
2447
  const handler = electrobunEventEmitter.events.app.reopen;
2094
2448
  const event = handler({});
@@ -2107,7 +2461,7 @@ if (native) {
2107
2461
  },
2108
2462
  { args: [], returns: "void", threadsafe: true },
2109
2463
  );
2110
- native_.symbols.setQuitRequestedHandler(quitRequestedCallback);
2464
+ core_.symbols.setQuitRequestedHandler(quitRequestedCallback);
2111
2465
 
2112
2466
  const globalShortcutCallback = new JSCallback(
2113
2467
  (acceleratorPtr) => {
@@ -2178,7 +2532,7 @@ export const Screen = {
2178
2532
  * @returns Display object for the primary monitor
2179
2533
  */
2180
2534
  getPrimaryDisplay: (): Display => {
2181
- const jsonStr = native ? native_.symbols.getPrimaryDisplay() : null;
2535
+ const jsonStr = hasFFI ? core_.symbols.getPrimaryDisplay() : null;
2182
2536
  if (!jsonStr) {
2183
2537
  return {
2184
2538
  id: 0,
@@ -2206,7 +2560,7 @@ export const Screen = {
2206
2560
  * @returns Array of Display objects
2207
2561
  */
2208
2562
  getAllDisplays: (): Display[] => {
2209
- const jsonStr = native ? native_.symbols.getAllDisplays() : null;
2563
+ const jsonStr = hasFFI ? core_.symbols.getAllDisplays() : null;
2210
2564
  if (!jsonStr) {
2211
2565
  return [];
2212
2566
  }
@@ -2222,7 +2576,7 @@ export const Screen = {
2222
2576
  * @returns Point with x and y coordinates
2223
2577
  */
2224
2578
  getCursorScreenPoint: (): Point => {
2225
- const jsonStr = native ? native_.symbols.getCursorScreenPoint() : null;
2579
+ const jsonStr = hasFFI ? core_.symbols.getCursorScreenPoint() : null;
2226
2580
  if (!jsonStr) {
2227
2581
  return { x: 0, y: 0 };
2228
2582
  }
@@ -2238,7 +2592,7 @@ export const Screen = {
2238
2592
  */
2239
2593
  getMouseButtons: (): bigint => {
2240
2594
  try {
2241
- return native ? native_.symbols.getMouseButtons() : BigInt(0);
2595
+ return hasFFI ? core_.symbols.getMouseButtons() : BigInt(0);
2242
2596
  } catch {
2243
2597
  return 0n;
2244
2598
  }
@@ -2403,35 +2757,13 @@ const webviewDecideNavigation = new JSCallback(
2403
2757
  );
2404
2758
 
2405
2759
  const webviewEventHandler = (id: number, eventName: string, detail: string) => {
2406
- const webview = BrowserView.getById(id);
2407
- if (!webview) {
2408
- console.error("[webviewEventHandler] No webview found for id:", id);
2409
- return;
2410
- }
2411
-
2412
- if (webview.hostWebviewId) {
2413
- const hostWebview = BrowserView.getById(webview.hostWebviewId);
2414
-
2415
- if (!hostWebview) {
2416
- console.error("[webviewEventHandler] No webview found for id:", id);
2417
- return;
2418
- }
2760
+ BrowserView.ensureWrapped(id);
2419
2761
 
2420
- // This is a webviewtag so we should send the event into the parent as well
2421
- // NOTE: for new-window-open and host-message the detail is a json string that needs to be parsed
2422
- let js;
2423
- if (eventName === "new-window-open" || eventName === "host-message") {
2424
- // detail is already a JSON string that will be parsed as a JS object
2425
- js = `document.querySelector('#electrobun-webview-${id}').emit(${JSON.stringify(eventName)}, ${detail});`;
2426
- } else {
2427
- js = `document.querySelector('#electrobun-webview-${id}').emit(${JSON.stringify(eventName)}, ${JSON.stringify(detail)});`;
2428
- }
2429
-
2430
- native_.symbols.evaluateJavaScriptWithNoCompletion(
2431
- hostWebview.ptr,
2432
- toCString(js),
2433
- );
2434
- }
2762
+ core_.symbols.dispatchHostWebviewEvent(
2763
+ id,
2764
+ toCString(eventName),
2765
+ toCString(detail),
2766
+ );
2435
2767
 
2436
2768
  const eventMap: Record<string, string> = {
2437
2769
  "will-navigate": "willNavigate",
@@ -2525,7 +2857,7 @@ const webviewEventJSCallback = new JSCallback(
2525
2857
  },
2526
2858
  );
2527
2859
 
2528
- const bunBridgePostmessageHandler = new JSCallback(
2860
+ const hostBridgePostmessageHandler = new JSCallback(
2529
2861
  (id, msg) => {
2530
2862
  try {
2531
2863
  const msgStr = new CString(msg);
@@ -2539,12 +2871,14 @@ const bunBridgePostmessageHandler = new JSCallback(
2539
2871
  }
2540
2872
  const msgJson = JSON.parse(rawMessage);
2541
2873
 
2542
- const webview = BrowserView.getById(id);
2543
- if (!webview) return;
2874
+ const webview = BrowserView.ensureWrapped(id);
2875
+ if (!webview) {
2876
+ return;
2877
+ }
2544
2878
 
2545
2879
  webview.rpcHandler?.(msgJson);
2546
2880
  } catch (err) {
2547
- console.error("error sending message to bun: ", err);
2881
+ console.error("error sending message to host: ", err);
2548
2882
  }
2549
2883
  },
2550
2884
  {
@@ -2620,8 +2954,6 @@ const internalBridgeHandler = new JSCallback(
2620
2954
  )[msgJson.id];
2621
2955
  handler?.(msgJson.payload);
2622
2956
  } else if (msgJson.type === "request") {
2623
- const hostWebview = BrowserView.getById(msgJson.hostWebviewId);
2624
- // const targetWebview = BrowserView.getById(msgJson.params.params.hostWebviewId);
2625
2957
  const handler = (
2626
2958
  internalRpcHandlers.request as Record<
2627
2959
  string,
@@ -2637,15 +2969,10 @@ const internalBridgeHandler = new JSCallback(
2637
2969
  success: true,
2638
2970
  payload,
2639
2971
  };
2640
-
2641
- if (!hostWebview) {
2642
- console.log(
2643
- "--->>> internal request in bun: NO HOST WEBVIEW FOUND",
2644
- );
2645
- return;
2646
- }
2647
-
2648
- hostWebview.sendInternalMessageViaExecute(resultObj);
2972
+ core_.symbols.sendInternalMessageToWebview(
2973
+ msgJson.hostWebviewId,
2974
+ toCString(JSON.stringify(resultObj)),
2975
+ );
2649
2976
  }
2650
2977
  });
2651
2978
  } catch (err) {
@@ -2826,24 +3153,10 @@ export const internalRpcHandlers = {
2826
3153
  return viewForTag.id;
2827
3154
  },
2828
3155
  webviewTagCanGoBack: (params: { id: number }) => {
2829
- const { id } = params;
2830
- const webviewPtr = BrowserView.getById(id)?.ptr;
2831
- if (!webviewPtr) {
2832
- console.error("no webview ptr");
2833
- return false;
2834
- }
2835
-
2836
- return native_.symbols.webviewCanGoBack(webviewPtr);
3156
+ return core_.symbols.webviewCanGoBack(params.id);
2837
3157
  },
2838
3158
  webviewTagCanGoForward: (params: { id: number }) => {
2839
- const { id } = params;
2840
- const webviewPtr = BrowserView.getById(id)?.ptr;
2841
- if (!webviewPtr) {
2842
- console.error("no webview ptr");
2843
- return false;
2844
- }
2845
-
2846
- return native_.symbols.webviewCanGoForward(webviewPtr);
3159
+ return core_.symbols.webviewCanGoForward(params.id);
2847
3160
  },
2848
3161
  },
2849
3162
  message: {
@@ -2852,25 +3165,14 @@ export const internalRpcHandlers = {
2852
3165
  frame: { x: number; y: number; width: number; height: number };
2853
3166
  masks: string;
2854
3167
  }) => {
2855
- const browserView = BrowserView.getById(params.id);
2856
- const webviewPtr = browserView?.ptr;
2857
-
2858
- if (!webviewPtr) {
2859
- console.log(
2860
- "[Bun] ERROR: webviewTagResize - no webview ptr found for id:",
2861
- params.id,
2862
- );
2863
- return;
2864
- }
2865
-
2866
3168
  const { x, y, width, height } = params.frame;
2867
- native_.symbols.resizeWebview(
2868
- webviewPtr,
3169
+ core_.symbols.resizeWebview(
3170
+ params.id,
2869
3171
  x,
2870
3172
  y,
2871
3173
  width,
2872
3174
  height,
2873
- toCString(params.masks),
3175
+ toCString(params.masks ?? "[]"),
2874
3176
  );
2875
3177
  },
2876
3178
  wgpuTagResize: (params: {
@@ -2897,81 +3199,47 @@ export const internalRpcHandlers = {
2897
3199
  );
2898
3200
  },
2899
3201
  webviewTagUpdateSrc: (params: { id: number; url: string }) => {
2900
- const webview = BrowserView.getById(params.id);
2901
- if (!webview || !webview.ptr) {
2902
- console.error(
2903
- `webviewTagUpdateSrc: BrowserView not found or has no ptr for id ${params.id}`,
2904
- );
2905
- return;
3202
+ const webview = BrowserView.ensureWrapped(params.id);
3203
+ if (webview) {
3204
+ webview.url = params.url;
2906
3205
  }
2907
- native_.symbols.loadURLInWebView(webview.ptr, toCString(params.url));
3206
+ core_.symbols.loadURLInWebView(params.id, toCString(params.url));
2908
3207
  },
2909
3208
  webviewTagUpdateHtml: (params: { id: number; html: string }) => {
2910
- const webview = BrowserView.getById(params.id);
2911
- if (!webview || !webview.ptr) {
2912
- console.error(
2913
- `webviewTagUpdateHtml: BrowserView not found or has no ptr for id ${params.id}`,
2914
- );
3209
+ const webview = BrowserView.ensureWrapped(params.id);
3210
+ if (!webview) {
3211
+ console.error(`webviewTagUpdateHtml: BrowserView not found for id ${params.id}`);
2915
3212
  return;
2916
3213
  }
2917
3214
 
2918
- // Store HTML content in native map for scheme handlers
2919
- native_.symbols.setWebviewHTMLContent(webview.id, toCString(params.html));
2920
-
2921
3215
  webview.loadHTML(params.html);
2922
3216
  webview.html = params.html;
2923
3217
  },
2924
3218
  webviewTagUpdatePreload: (params: { id: number; preload: string }) => {
2925
- const webview = BrowserView.getById(params.id);
2926
- if (!webview || !webview.ptr) {
2927
- console.error(
2928
- `webviewTagUpdatePreload: BrowserView not found or has no ptr for id ${params.id}`,
2929
- );
2930
- return;
3219
+ const webview = BrowserView.ensureWrapped(params.id);
3220
+ if (webview) {
3221
+ webview.preload = params.preload;
2931
3222
  }
2932
- native_.symbols.updatePreloadScriptToWebView(
2933
- webview.ptr,
3223
+ core_.symbols.updatePreloadScriptToWebView(
3224
+ params.id,
2934
3225
  toCString("electrobun_custom_preload_script"),
2935
3226
  toCString(params.preload),
2936
3227
  true,
2937
3228
  );
2938
3229
  },
2939
3230
  webviewTagGoBack: (params: { id: number }) => {
2940
- const webview = BrowserView.getById(params.id);
2941
- if (!webview || !webview.ptr) {
2942
- console.error(
2943
- `webviewTagGoBack: BrowserView not found or has no ptr for id ${params.id}`,
2944
- );
2945
- return;
2946
- }
2947
- native_.symbols.webviewGoBack(webview.ptr);
3231
+ core_.symbols.webviewGoBack(params.id);
2948
3232
  },
2949
3233
  webviewTagGoForward: (params: { id: number }) => {
2950
- const webview = BrowserView.getById(params.id);
2951
- if (!webview || !webview.ptr) {
2952
- console.error(
2953
- `webviewTagGoForward: BrowserView not found or has no ptr for id ${params.id}`,
2954
- );
2955
- return;
2956
- }
2957
- native_.symbols.webviewGoForward(webview.ptr);
3234
+ core_.symbols.webviewGoForward(params.id);
2958
3235
  },
2959
3236
  webviewTagReload: (params: { id: number }) => {
2960
- const webview = BrowserView.getById(params.id);
2961
- if (!webview || !webview.ptr) {
2962
- console.error(
2963
- `webviewTagReload: BrowserView not found or has no ptr for id ${params.id}`,
2964
- );
2965
- return;
2966
- }
2967
- native_.symbols.webviewReload(webview.ptr);
3237
+ core_.symbols.webviewReload(params.id);
2968
3238
  },
2969
3239
  webviewTagRemove: (params: { id: number }) => {
2970
- const webview = BrowserView.getById(params.id);
2971
- if (!webview || !webview.ptr) {
2972
- console.error(
2973
- `webviewTagRemove: BrowserView not found or has no ptr for id ${params.id}`,
2974
- );
3240
+ const webview = BrowserView.ensureWrapped(params.id);
3241
+ if (!webview) {
3242
+ console.error(`webviewTagRemove: BrowserView not found for id ${params.id}`);
2975
3243
  return;
2976
3244
  }
2977
3245
  webview.remove();
@@ -2988,14 +3256,7 @@ export const internalRpcHandlers = {
2988
3256
  id: number;
2989
3257
  transparent: boolean;
2990
3258
  }) => {
2991
- const webview = BrowserView.getById(params.id);
2992
- if (!webview || !webview.ptr) {
2993
- console.error(
2994
- `webviewTagSetTransparent: BrowserView not found or has no ptr for id ${params.id}`,
2995
- );
2996
- return;
2997
- }
2998
- native_.symbols.webviewSetTransparent(webview.ptr, params.transparent);
3259
+ core_.symbols.webviewSetTransparent(params.id, params.transparent);
2999
3260
  },
3000
3261
  wgpuTagSetTransparent: (params: {
3001
3262
  id: number;
@@ -3014,17 +3275,7 @@ export const internalRpcHandlers = {
3014
3275
  id: number;
3015
3276
  enablePassthrough: boolean;
3016
3277
  }) => {
3017
- const webview = BrowserView.getById(params.id);
3018
- if (!webview || !webview.ptr) {
3019
- console.error(
3020
- `webviewTagSetPassthrough: BrowserView not found or has no ptr for id ${params.id}`,
3021
- );
3022
- return;
3023
- }
3024
- native_.symbols.webviewSetPassthrough(
3025
- webview.ptr,
3026
- params.enablePassthrough,
3027
- );
3278
+ core_.symbols.webviewSetPassthrough(params.id, params.enablePassthrough);
3028
3279
  },
3029
3280
  wgpuTagSetPassthrough: (params: { id: number; passthrough: boolean }) => {
3030
3281
  const view = WGPUView.getById(params.id);
@@ -3037,14 +3288,7 @@ export const internalRpcHandlers = {
3037
3288
  native_.symbols.wgpuViewSetPassthrough(view.ptr, params.passthrough);
3038
3289
  },
3039
3290
  webviewTagSetHidden: (params: { id: number; hidden: boolean }) => {
3040
- const webview = BrowserView.getById(params.id);
3041
- if (!webview || !webview.ptr) {
3042
- console.error(
3043
- `webviewTagSetHidden: BrowserView not found or has no ptr for id ${params.id}`,
3044
- );
3045
- return;
3046
- }
3047
- native_.symbols.webviewSetHidden(webview.ptr, params.hidden);
3291
+ core_.symbols.webviewSetHidden(params.id, params.hidden);
3048
3292
  },
3049
3293
  wgpuTagSetHidden: (params: { id: number; hidden: boolean }) => {
3050
3294
  const view = WGPUView.getById(params.id);
@@ -3081,18 +3325,12 @@ export const internalRpcHandlers = {
3081
3325
  native_.symbols.wgpuRunGPUTest(view.ptr);
3082
3326
  },
3083
3327
  webviewTagSetNavigationRules: (params: { id: number; rules: string[] }) => {
3084
- const webview = BrowserView.getById(params.id);
3085
- if (!webview || !webview.ptr) {
3086
- console.error(
3087
- `webviewTagSetNavigationRules: BrowserView not found or has no ptr for id ${params.id}`,
3088
- );
3089
- return;
3090
- }
3091
3328
  const rulesJson = JSON.stringify(params.rules);
3092
- native_.symbols.setWebviewNavigationRules(
3093
- webview.ptr,
3094
- toCString(rulesJson),
3095
- );
3329
+ const webview = BrowserView.ensureWrapped(params.id);
3330
+ if (webview) {
3331
+ webview.navigationRules = rulesJson;
3332
+ }
3333
+ core_.symbols.setWebviewNavigationRules(params.id, toCString(rulesJson));
3096
3334
  },
3097
3335
  webviewTagFindInPage: (params: {
3098
3336
  id: number;
@@ -3100,70 +3338,28 @@ export const internalRpcHandlers = {
3100
3338
  forward: boolean;
3101
3339
  matchCase: boolean;
3102
3340
  }) => {
3103
- const webview = BrowserView.getById(params.id);
3104
- if (!webview || !webview.ptr) {
3105
- console.error(
3106
- `webviewTagFindInPage: BrowserView not found or has no ptr for id ${params.id}`,
3107
- );
3108
- return;
3109
- }
3110
- native_.symbols.webviewFindInPage(
3111
- webview.ptr,
3341
+ core_.symbols.webviewFindInPage(
3342
+ params.id,
3112
3343
  toCString(params.searchText),
3113
3344
  params.forward,
3114
3345
  params.matchCase,
3115
3346
  );
3116
3347
  },
3117
3348
  webviewTagStopFind: (params: { id: number }) => {
3118
- const webview = BrowserView.getById(params.id);
3119
- if (!webview || !webview.ptr) {
3120
- console.error(
3121
- `webviewTagStopFind: BrowserView not found or has no ptr for id ${params.id}`,
3122
- );
3123
- return;
3124
- }
3125
- native_.symbols.webviewStopFind(webview.ptr);
3349
+ core_.symbols.webviewStopFind(params.id);
3126
3350
  },
3127
3351
  webviewTagOpenDevTools: (params: { id: number }) => {
3128
- const webview = BrowserView.getById(params.id);
3129
- if (!webview || !webview.ptr) {
3130
- console.error(
3131
- `webviewTagOpenDevTools: BrowserView not found or has no ptr for id ${params.id}`,
3132
- );
3133
- return;
3134
- }
3135
- native_.symbols.webviewOpenDevTools(webview.ptr);
3352
+ core_.symbols.webviewOpenDevTools(params.id);
3136
3353
  },
3137
3354
  webviewTagCloseDevTools: (params: { id: number }) => {
3138
- const webview = BrowserView.getById(params.id);
3139
- if (!webview || !webview.ptr) {
3140
- console.error(
3141
- `webviewTagCloseDevTools: BrowserView not found or has no ptr for id ${params.id}`,
3142
- );
3143
- return;
3144
- }
3145
- native_.symbols.webviewCloseDevTools(webview.ptr);
3355
+ core_.symbols.webviewCloseDevTools(params.id);
3146
3356
  },
3147
3357
  webviewTagToggleDevTools: (params: { id: number }) => {
3148
- const webview = BrowserView.getById(params.id);
3149
- if (!webview || !webview.ptr) {
3150
- console.error(
3151
- `webviewTagToggleDevTools: BrowserView not found or has no ptr for id ${params.id}`,
3152
- );
3153
- return;
3154
- }
3155
- native_.symbols.webviewToggleDevTools(webview.ptr);
3358
+ core_.symbols.webviewToggleDevTools(params.id);
3156
3359
  },
3157
3360
  webviewTagExecuteJavascript: (params: { id: number; js: string }) => {
3158
- const webview = BrowserView.getById(params.id);
3159
- if (!webview || !webview.ptr) {
3160
- console.error(
3161
- `webviewTagExecuteJavascript: BrowserView not found or has no ptr for id ${params.id}`,
3162
- );
3163
- return;
3164
- }
3165
- native_.symbols.evaluateJavaScriptWithNoCompletion(
3166
- webview.ptr,
3361
+ core_.symbols.evaluateJavaScriptWithNoCompletion(
3362
+ params.id,
3167
3363
  toCString(params.js),
3168
3364
  );
3169
3365
  },