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
@@ -0,0 +1,948 @@
1
+ (function(){// src/preload/encryption.ts
2
+ function base64ToUint8Array(base64) {
3
+ return new Uint8Array(atob(base64).split("").map((char) => char.charCodeAt(0)));
4
+ }
5
+ function uint8ArrayToBase64(uint8Array) {
6
+ let binary = "";
7
+ for (let i = 0;i < uint8Array.length; i++) {
8
+ binary += String.fromCharCode(uint8Array[i]);
9
+ }
10
+ return btoa(binary);
11
+ }
12
+ function toArrayBuffer(bytes) {
13
+ const buffer = new ArrayBuffer(bytes.byteLength);
14
+ new Uint8Array(buffer).set(bytes);
15
+ return buffer;
16
+ }
17
+ async function generateKeyFromBytes(rawKey) {
18
+ return await window.crypto.subtle.importKey("raw", toArrayBuffer(rawKey), { name: "AES-GCM" }, true, ["encrypt", "decrypt"]);
19
+ }
20
+ async function initEncryption() {
21
+ const secretKey = await generateKeyFromBytes(new Uint8Array(window.__electrobunSecretKeyBytes));
22
+ const encryptString = async (plaintext) => {
23
+ const encoder = new TextEncoder;
24
+ const encodedText = encoder.encode(plaintext);
25
+ const iv = window.crypto.getRandomValues(new Uint8Array(12));
26
+ const encryptedBuffer = await window.crypto.subtle.encrypt({ name: "AES-GCM", iv: toArrayBuffer(iv) }, secretKey, toArrayBuffer(encodedText));
27
+ const encryptedData = new Uint8Array(encryptedBuffer.slice(0, -16));
28
+ const tag = new Uint8Array(encryptedBuffer.slice(-16));
29
+ return {
30
+ encryptedData: uint8ArrayToBase64(encryptedData),
31
+ iv: uint8ArrayToBase64(iv),
32
+ tag: uint8ArrayToBase64(tag)
33
+ };
34
+ };
35
+ const decryptString = async (encryptedDataB64, ivB64, tagB64) => {
36
+ const encryptedData = base64ToUint8Array(encryptedDataB64);
37
+ const iv = base64ToUint8Array(ivB64);
38
+ const tag = base64ToUint8Array(tagB64);
39
+ const combinedData = new Uint8Array(encryptedData.length + tag.length);
40
+ combinedData.set(encryptedData);
41
+ combinedData.set(tag, encryptedData.length);
42
+ const decryptedBuffer = await window.crypto.subtle.decrypt({ name: "AES-GCM", iv: toArrayBuffer(iv) }, secretKey, toArrayBuffer(combinedData));
43
+ const decoder = new TextDecoder;
44
+ return decoder.decode(decryptedBuffer);
45
+ };
46
+ window.__electrobun_encrypt = encryptString;
47
+ window.__electrobun_decrypt = decryptString;
48
+ }
49
+
50
+ // src/preload/internalRpc.ts
51
+ var pendingRequests = {};
52
+ var requestId = 0;
53
+ var isProcessingQueue = false;
54
+ var sendQueue = [];
55
+ function processQueue() {
56
+ if (isProcessingQueue) {
57
+ setTimeout(processQueue);
58
+ return;
59
+ }
60
+ if (sendQueue.length === 0)
61
+ return;
62
+ isProcessingQueue = true;
63
+ const batch = JSON.stringify(sendQueue);
64
+ sendQueue.length = 0;
65
+ window.__electrobunInternalBridge?.postMessage(batch);
66
+ setTimeout(() => {
67
+ isProcessingQueue = false;
68
+ }, 2);
69
+ }
70
+ function send(type, payload) {
71
+ sendQueue.push(JSON.stringify({ type: "message", id: type, payload }));
72
+ processQueue();
73
+ }
74
+ function request(type, payload) {
75
+ return new Promise((resolve, reject) => {
76
+ const id = `req_${++requestId}_${Date.now()}`;
77
+ pendingRequests[id] = { resolve, reject };
78
+ sendQueue.push(JSON.stringify({
79
+ type: "request",
80
+ method: type,
81
+ id,
82
+ params: payload,
83
+ hostWebviewId: window.__electrobunWebviewId
84
+ }));
85
+ processQueue();
86
+ setTimeout(() => {
87
+ if (pendingRequests[id]) {
88
+ delete pendingRequests[id];
89
+ reject(new Error(`Request timeout: ${type}`));
90
+ }
91
+ }, 1e4);
92
+ });
93
+ }
94
+ function handleResponse(msg) {
95
+ if (msg && msg.type === "response" && msg.id) {
96
+ const pending = pendingRequests[msg.id];
97
+ if (pending) {
98
+ delete pendingRequests[msg.id];
99
+ if (msg.success)
100
+ pending.resolve(msg.payload);
101
+ else
102
+ pending.reject(msg.payload);
103
+ }
104
+ }
105
+ }
106
+
107
+ // src/preload/dragRegions.ts
108
+ function isAppRegionDrag(e) {
109
+ const target = e.target;
110
+ if (!target || !target.closest)
111
+ return false;
112
+ if (target.closest(".electrobun-webkit-app-region-no-drag") || target.closest('[style*="app-region"][style*="no-drag"]')) {
113
+ return false;
114
+ }
115
+ const draggableByStyle = target.closest('[style*="app-region"][style*="drag"]');
116
+ const draggableByClass = target.closest(".electrobun-webkit-app-region-drag");
117
+ return !!(draggableByStyle || draggableByClass);
118
+ }
119
+ function initDragRegions() {
120
+ document.addEventListener("mousedown", (e) => {
121
+ if (isAppRegionDrag(e)) {
122
+ send("startWindowMove", { id: window.__electrobunWindowId });
123
+ }
124
+ });
125
+ document.addEventListener("mouseup", (e) => {
126
+ if (isAppRegionDrag(e)) {
127
+ send("stopWindowMove", { id: window.__electrobunWindowId });
128
+ }
129
+ });
130
+ }
131
+
132
+ // src/preload/overlaySync.ts
133
+ class OverlaySyncController {
134
+ element;
135
+ options;
136
+ lastRect = { x: 0, y: 0, width: 0, height: 0 };
137
+ resizeObserver = null;
138
+ positionLoop = null;
139
+ resizeHandler = null;
140
+ burstUntil = 0;
141
+ constructor(element, options) {
142
+ this.element = element;
143
+ this.options = {
144
+ onSync: options.onSync,
145
+ getMasks: options.getMasks ?? (() => []),
146
+ burstIntervalMs: options.burstIntervalMs ?? 50,
147
+ baseIntervalMs: options.baseIntervalMs ?? 100,
148
+ burstDurationMs: options.burstDurationMs ?? 500
149
+ };
150
+ }
151
+ start() {
152
+ this.resizeObserver = new ResizeObserver(() => this.sync());
153
+ this.resizeObserver.observe(this.element);
154
+ const loop = () => {
155
+ this.sync();
156
+ const now = performance.now();
157
+ const interval = now < this.burstUntil ? this.options.burstIntervalMs : this.options.baseIntervalMs;
158
+ this.positionLoop = setTimeout(loop, interval);
159
+ };
160
+ this.positionLoop = setTimeout(loop, this.options.baseIntervalMs);
161
+ this.resizeHandler = () => this.sync(true);
162
+ window.addEventListener("resize", this.resizeHandler);
163
+ }
164
+ stop() {
165
+ if (this.resizeObserver)
166
+ this.resizeObserver.disconnect();
167
+ if (this.positionLoop)
168
+ clearTimeout(this.positionLoop);
169
+ if (this.resizeHandler) {
170
+ window.removeEventListener("resize", this.resizeHandler);
171
+ }
172
+ this.resizeObserver = null;
173
+ this.positionLoop = null;
174
+ this.resizeHandler = null;
175
+ }
176
+ forceSync() {
177
+ this.sync(true);
178
+ }
179
+ setLastRect(rect) {
180
+ this.lastRect = rect;
181
+ }
182
+ sync(force = false) {
183
+ const rect = this.element.getBoundingClientRect();
184
+ const newRect = {
185
+ x: rect.x,
186
+ y: rect.y,
187
+ width: rect.width,
188
+ height: rect.height
189
+ };
190
+ if (newRect.width === 0 && newRect.height === 0) {
191
+ return;
192
+ }
193
+ if (!force && newRect.x === this.lastRect.x && newRect.y === this.lastRect.y && newRect.width === this.lastRect.width && newRect.height === this.lastRect.height) {
194
+ return;
195
+ }
196
+ this.burstUntil = performance.now() + this.options.burstDurationMs;
197
+ this.lastRect = newRect;
198
+ const masks = this.options.getMasks();
199
+ this.options.onSync(newRect, JSON.stringify(masks));
200
+ }
201
+ }
202
+
203
+ // src/preload/webviewTag.ts
204
+ var webviewRegistry = {};
205
+
206
+ class ElectrobunWebviewTag extends HTMLElement {
207
+ webviewId = null;
208
+ maskSelectors = new Set;
209
+ _sync = null;
210
+ transparent = false;
211
+ passthroughEnabled = false;
212
+ hidden = false;
213
+ sandboxed = false;
214
+ _eventListeners = {};
215
+ static get observedAttributes() {
216
+ return ["src", "html"];
217
+ }
218
+ constructor() {
219
+ super();
220
+ }
221
+ connectedCallback() {
222
+ requestAnimationFrame(() => this.initWebview());
223
+ }
224
+ attributeChangedCallback(name, oldValue, newValue) {
225
+ if (oldValue === newValue)
226
+ return;
227
+ if (newValue === null)
228
+ return;
229
+ if (this.webviewId === null)
230
+ return;
231
+ if (name === "src")
232
+ this.loadURL(newValue);
233
+ else if (name === "html")
234
+ this.loadHTML(newValue);
235
+ }
236
+ disconnectedCallback() {
237
+ if (this.webviewId !== null) {
238
+ send("webviewTagRemove", { id: this.webviewId });
239
+ delete webviewRegistry[this.webviewId];
240
+ }
241
+ if (this._sync)
242
+ this._sync.stop();
243
+ }
244
+ getInitialNavigationRules() {
245
+ const rawRules = this.getAttribute("navigation-rules");
246
+ if (rawRules === null) {
247
+ return null;
248
+ }
249
+ const trimmed = rawRules.trim();
250
+ if (!trimmed) {
251
+ return [];
252
+ }
253
+ try {
254
+ const parsed = JSON.parse(trimmed);
255
+ if (!Array.isArray(parsed) || !parsed.every((rule) => typeof rule === "string")) {
256
+ throw new Error("navigation-rules must be a JSON string array");
257
+ }
258
+ return parsed;
259
+ } catch (error) {
260
+ console.error("Invalid navigation-rules attribute:", error);
261
+ return [];
262
+ }
263
+ }
264
+ async initWebview() {
265
+ const rect = this.getBoundingClientRect();
266
+ const initialRect = {
267
+ x: rect.x,
268
+ y: rect.y,
269
+ width: rect.width,
270
+ height: rect.height
271
+ };
272
+ const url = this.getAttribute("src");
273
+ const html = this.getAttribute("html");
274
+ const preload = this.getAttribute("preload");
275
+ const partition = this.getAttribute("partition");
276
+ const renderer = this.getAttribute("renderer") || "native";
277
+ const masks = this.getAttribute("masks");
278
+ const navigationRules = this.getInitialNavigationRules();
279
+ const sandbox = this.hasAttribute("sandbox");
280
+ this.sandboxed = sandbox;
281
+ const transparent = this.hasAttribute("transparent");
282
+ const passthrough = this.hasAttribute("passthrough");
283
+ this.transparent = transparent;
284
+ this.passthroughEnabled = passthrough;
285
+ if (transparent)
286
+ this.style.opacity = "0";
287
+ if (passthrough)
288
+ this.style.pointerEvents = "none";
289
+ if (masks) {
290
+ masks.split(",").forEach((s) => this.maskSelectors.add(s.trim()));
291
+ }
292
+ try {
293
+ const webviewInitParams = {
294
+ hostWebviewId: window.__electrobunWebviewId,
295
+ windowId: window.__electrobunWindowId,
296
+ renderer,
297
+ url,
298
+ html,
299
+ preload,
300
+ partition,
301
+ frame: {
302
+ width: rect.width,
303
+ height: rect.height,
304
+ x: rect.x,
305
+ y: rect.y
306
+ },
307
+ sandbox,
308
+ transparent,
309
+ passthrough,
310
+ ...navigationRules === null ? {} : { navigationRules }
311
+ };
312
+ const webviewId = await request("webviewTagInit", webviewInitParams);
313
+ this.webviewId = webviewId;
314
+ this.id = `electrobun-webview-${webviewId}`;
315
+ webviewRegistry[webviewId] = this;
316
+ this.setupObservers(initialRect);
317
+ this.syncDimensions(true);
318
+ requestAnimationFrame(() => {
319
+ Object.values(webviewRegistry).forEach((webview) => {
320
+ if (webview !== this && webview.webviewId !== null) {
321
+ webview.syncDimensions(true);
322
+ }
323
+ });
324
+ });
325
+ } catch (err) {
326
+ console.error("Failed to init webview:", err);
327
+ }
328
+ }
329
+ setupObservers(initialRect) {
330
+ const getMasks = () => {
331
+ const rect = this.getBoundingClientRect();
332
+ const masks = [];
333
+ this.maskSelectors.forEach((selector) => {
334
+ try {
335
+ document.querySelectorAll(selector).forEach((el) => {
336
+ const mr = el.getBoundingClientRect();
337
+ masks.push({
338
+ x: mr.x - rect.x,
339
+ y: mr.y - rect.y,
340
+ width: mr.width,
341
+ height: mr.height
342
+ });
343
+ });
344
+ } catch (_e) {}
345
+ });
346
+ return masks;
347
+ };
348
+ this._sync = new OverlaySyncController(this, {
349
+ onSync: (rect, masksJson) => {
350
+ if (this.webviewId === null)
351
+ return;
352
+ send("webviewTagResize", {
353
+ id: this.webviewId,
354
+ frame: rect,
355
+ masks: masksJson
356
+ });
357
+ },
358
+ getMasks,
359
+ burstIntervalMs: 10,
360
+ baseIntervalMs: 100,
361
+ burstDurationMs: 50
362
+ });
363
+ this._sync.setLastRect(initialRect);
364
+ this._sync.start();
365
+ }
366
+ syncDimensions(force = false) {
367
+ if (!this._sync)
368
+ return;
369
+ if (force) {
370
+ this._sync.forceSync();
371
+ }
372
+ }
373
+ loadURL(url) {
374
+ if (this.webviewId === null)
375
+ return;
376
+ this.setAttribute("src", url);
377
+ send("webviewTagUpdateSrc", { id: this.webviewId, url });
378
+ }
379
+ loadHTML(html) {
380
+ if (this.webviewId === null)
381
+ return;
382
+ send("webviewTagUpdateHtml", { id: this.webviewId, html });
383
+ }
384
+ reload() {
385
+ if (this.webviewId !== null)
386
+ send("webviewTagReload", { id: this.webviewId });
387
+ }
388
+ goBack() {
389
+ if (this.webviewId !== null)
390
+ send("webviewTagGoBack", { id: this.webviewId });
391
+ }
392
+ goForward() {
393
+ if (this.webviewId !== null)
394
+ send("webviewTagGoForward", { id: this.webviewId });
395
+ }
396
+ async canGoBack() {
397
+ if (this.webviewId === null)
398
+ return false;
399
+ return await request("webviewTagCanGoBack", {
400
+ id: this.webviewId
401
+ });
402
+ }
403
+ async canGoForward() {
404
+ if (this.webviewId === null)
405
+ return false;
406
+ return await request("webviewTagCanGoForward", {
407
+ id: this.webviewId
408
+ });
409
+ }
410
+ toggleTransparent(value) {
411
+ if (this.webviewId === null)
412
+ return;
413
+ this.transparent = value !== undefined ? value : !this.transparent;
414
+ this.style.opacity = this.transparent ? "0" : "";
415
+ send("webviewTagSetTransparent", {
416
+ id: this.webviewId,
417
+ transparent: this.transparent
418
+ });
419
+ }
420
+ togglePassthrough(value) {
421
+ if (this.webviewId === null)
422
+ return;
423
+ this.passthroughEnabled = value !== undefined ? value : !this.passthroughEnabled;
424
+ this.style.pointerEvents = this.passthroughEnabled ? "none" : "";
425
+ send("webviewTagSetPassthrough", {
426
+ id: this.webviewId,
427
+ enablePassthrough: this.passthroughEnabled
428
+ });
429
+ }
430
+ toggleHidden(value) {
431
+ if (this.webviewId === null)
432
+ return;
433
+ this.hidden = value !== undefined ? value : !this.hidden;
434
+ send("webviewTagSetHidden", { id: this.webviewId, hidden: this.hidden });
435
+ }
436
+ addMaskSelector(selector) {
437
+ this.maskSelectors.add(selector);
438
+ this.syncDimensions(true);
439
+ }
440
+ removeMaskSelector(selector) {
441
+ this.maskSelectors.delete(selector);
442
+ this.syncDimensions(true);
443
+ }
444
+ setNavigationRules(rules) {
445
+ if (this.webviewId !== null) {
446
+ send("webviewTagSetNavigationRules", { id: this.webviewId, rules });
447
+ }
448
+ }
449
+ findInPage(searchText, options) {
450
+ if (this.webviewId === null)
451
+ return;
452
+ const forward = options?.forward !== false;
453
+ const matchCase = options?.matchCase || false;
454
+ send("webviewTagFindInPage", {
455
+ id: this.webviewId,
456
+ searchText,
457
+ forward,
458
+ matchCase
459
+ });
460
+ }
461
+ stopFindInPage() {
462
+ if (this.webviewId !== null)
463
+ send("webviewTagStopFind", { id: this.webviewId });
464
+ }
465
+ openDevTools() {
466
+ if (this.webviewId !== null)
467
+ send("webviewTagOpenDevTools", { id: this.webviewId });
468
+ }
469
+ closeDevTools() {
470
+ if (this.webviewId !== null)
471
+ send("webviewTagCloseDevTools", { id: this.webviewId });
472
+ }
473
+ toggleDevTools() {
474
+ if (this.webviewId !== null)
475
+ send("webviewTagToggleDevTools", { id: this.webviewId });
476
+ }
477
+ executeJavascript(js) {
478
+ if (this.webviewId === null)
479
+ return;
480
+ send("webviewTagExecuteJavascript", { id: this.webviewId, js });
481
+ }
482
+ on(event, listener) {
483
+ if (!this._eventListeners[event])
484
+ this._eventListeners[event] = [];
485
+ this._eventListeners[event].push(listener);
486
+ }
487
+ off(event, listener) {
488
+ if (!this._eventListeners[event])
489
+ return;
490
+ const idx = this._eventListeners[event].indexOf(listener);
491
+ if (idx !== -1)
492
+ this._eventListeners[event].splice(idx, 1);
493
+ }
494
+ emit(event, detail) {
495
+ const listeners = this._eventListeners[event];
496
+ if (listeners) {
497
+ const customEvent = new CustomEvent(event, { detail });
498
+ listeners.forEach((fn) => fn(customEvent));
499
+ }
500
+ }
501
+ get src() {
502
+ return this.getAttribute("src");
503
+ }
504
+ set src(value) {
505
+ if (value) {
506
+ this.setAttribute("src", value);
507
+ } else {
508
+ this.removeAttribute("src");
509
+ }
510
+ }
511
+ get html() {
512
+ return this.getAttribute("html");
513
+ }
514
+ set html(value) {
515
+ if (value) {
516
+ this.setAttribute("html", value);
517
+ } else {
518
+ this.removeAttribute("html");
519
+ }
520
+ }
521
+ get preload() {
522
+ return this.getAttribute("preload");
523
+ }
524
+ set preload(value) {
525
+ if (value)
526
+ this.setAttribute("preload", value);
527
+ else
528
+ this.removeAttribute("preload");
529
+ }
530
+ get renderer() {
531
+ return this.getAttribute("renderer") || "native";
532
+ }
533
+ set renderer(value) {
534
+ this.setAttribute("renderer", value);
535
+ }
536
+ get sandbox() {
537
+ return this.sandboxed;
538
+ }
539
+ }
540
+ function initWebviewTag() {
541
+ if (!customElements.get("electrobun-webview")) {
542
+ customElements.define("electrobun-webview", ElectrobunWebviewTag);
543
+ }
544
+ const injectStyles = () => {
545
+ const style = document.createElement("style");
546
+ style.textContent = `
547
+ electrobun-webview {
548
+ display: block;
549
+ width: 800px;
550
+ height: 300px;
551
+ background: #fff;
552
+ background-repeat: no-repeat !important;
553
+ overflow: hidden;
554
+ }
555
+ `;
556
+ if (document.head?.firstChild) {
557
+ document.head.insertBefore(style, document.head.firstChild);
558
+ } else if (document.head) {
559
+ document.head.appendChild(style);
560
+ }
561
+ };
562
+ if (document.head) {
563
+ injectStyles();
564
+ } else {
565
+ document.addEventListener("DOMContentLoaded", injectStyles);
566
+ }
567
+ }
568
+
569
+ // src/preload/wgpuTag.ts
570
+ var wgpuTagRegistry = {};
571
+
572
+ class ElectrobunWgpuTag extends HTMLElement {
573
+ wgpuViewId = null;
574
+ maskSelectors = new Set;
575
+ _sync = null;
576
+ transparent = false;
577
+ passthroughEnabled = false;
578
+ hidden = false;
579
+ _ready = false;
580
+ _initializing = false;
581
+ _eventListeners = {};
582
+ constructor() {
583
+ super();
584
+ }
585
+ connectedCallback() {
586
+ requestAnimationFrame(() => {
587
+ if (this.isConnected)
588
+ this.initWgpuView();
589
+ });
590
+ }
591
+ disconnectedCallback() {
592
+ if (this.wgpuViewId !== null) {
593
+ send("wgpuTagRemove", { id: this.wgpuViewId });
594
+ delete wgpuTagRegistry[this.wgpuViewId];
595
+ this.wgpuViewId = null;
596
+ }
597
+ if (this._sync)
598
+ this._sync.stop();
599
+ this._sync = null;
600
+ this._ready = false;
601
+ }
602
+ async initWgpuView() {
603
+ if (this._initializing || this.wgpuViewId !== null)
604
+ return;
605
+ this._initializing = true;
606
+ const rect = this.getBoundingClientRect();
607
+ const initialRect = {
608
+ x: rect.x,
609
+ y: rect.y,
610
+ width: rect.width,
611
+ height: rect.height
612
+ };
613
+ const transparent = this.hasAttribute("transparent");
614
+ const passthrough = this.hasAttribute("passthrough");
615
+ const hidden = this.hasAttribute("hidden");
616
+ const masks = this.getAttribute("masks");
617
+ this.transparent = transparent;
618
+ this.passthroughEnabled = passthrough;
619
+ this.hidden = hidden;
620
+ if (masks) {
621
+ masks.split(",").forEach((s) => this.maskSelectors.add(s.trim()));
622
+ }
623
+ if (transparent)
624
+ this.style.opacity = "0";
625
+ if (passthrough)
626
+ this.style.pointerEvents = "none";
627
+ try {
628
+ const wgpuViewId = await request("wgpuTagInit", {
629
+ windowId: window.__electrobunWindowId,
630
+ frame: {
631
+ width: rect.width,
632
+ height: rect.height,
633
+ x: rect.x,
634
+ y: rect.y
635
+ },
636
+ transparent,
637
+ passthrough
638
+ });
639
+ if (!this.isConnected) {
640
+ send("wgpuTagRemove", { id: wgpuViewId });
641
+ return;
642
+ }
643
+ this.wgpuViewId = wgpuViewId;
644
+ if (!this.id) {
645
+ this.id = `electrobun-wgpu-${wgpuViewId}`;
646
+ }
647
+ wgpuTagRegistry[wgpuViewId] = this;
648
+ this.setupObservers(initialRect);
649
+ this.syncDimensions(true);
650
+ if (hidden) {
651
+ this.toggleHidden(true);
652
+ }
653
+ requestAnimationFrame(() => {
654
+ Object.values(wgpuTagRegistry).forEach((view) => {
655
+ if (view !== this && view.wgpuViewId !== null) {
656
+ view.syncDimensions(true);
657
+ }
658
+ });
659
+ });
660
+ this._ready = true;
661
+ this.emit("ready", { id: wgpuViewId });
662
+ } catch (err) {
663
+ console.error("Failed to init WGPU view:", err);
664
+ } finally {
665
+ this._initializing = false;
666
+ }
667
+ }
668
+ setupObservers(initialRect) {
669
+ const getMasks = () => {
670
+ const rect = this.getBoundingClientRect();
671
+ const masks = [];
672
+ this.maskSelectors.forEach((selector) => {
673
+ try {
674
+ document.querySelectorAll(selector).forEach((el) => {
675
+ const mr = el.getBoundingClientRect();
676
+ masks.push({
677
+ x: mr.x - rect.x,
678
+ y: mr.y - rect.y,
679
+ width: mr.width,
680
+ height: mr.height
681
+ });
682
+ });
683
+ } catch (_e) {}
684
+ });
685
+ return masks;
686
+ };
687
+ this._sync = new OverlaySyncController(this, {
688
+ onSync: (rect, masksJson) => {
689
+ if (this.wgpuViewId === null)
690
+ return;
691
+ send("wgpuTagResize", {
692
+ id: this.wgpuViewId,
693
+ frame: rect,
694
+ masks: masksJson
695
+ });
696
+ },
697
+ getMasks,
698
+ burstIntervalMs: 10,
699
+ baseIntervalMs: 100,
700
+ burstDurationMs: 50
701
+ });
702
+ this._sync.setLastRect(initialRect);
703
+ this._sync.start();
704
+ }
705
+ syncDimensions(force = false) {
706
+ if (!this._sync)
707
+ return;
708
+ if (force) {
709
+ this._sync.forceSync();
710
+ }
711
+ }
712
+ toggleTransparent(value) {
713
+ if (this.wgpuViewId === null)
714
+ return;
715
+ this.transparent = value !== undefined ? value : !this.transparent;
716
+ this.style.opacity = this.transparent ? "0" : "";
717
+ send("wgpuTagSetTransparent", {
718
+ id: this.wgpuViewId,
719
+ transparent: this.transparent
720
+ });
721
+ }
722
+ togglePassthrough(value) {
723
+ if (this.wgpuViewId === null)
724
+ return;
725
+ this.passthroughEnabled = value !== undefined ? value : !this.passthroughEnabled;
726
+ this.style.pointerEvents = this.passthroughEnabled ? "none" : "";
727
+ send("wgpuTagSetPassthrough", {
728
+ id: this.wgpuViewId,
729
+ passthrough: this.passthroughEnabled
730
+ });
731
+ }
732
+ toggleHidden(value) {
733
+ if (this.wgpuViewId === null)
734
+ return;
735
+ this.hidden = value !== undefined ? value : !this.hidden;
736
+ send("wgpuTagSetHidden", { id: this.wgpuViewId, hidden: this.hidden });
737
+ }
738
+ runTest() {
739
+ if (this.wgpuViewId === null)
740
+ return;
741
+ send("wgpuTagRunTest", { id: this.wgpuViewId });
742
+ }
743
+ addMaskSelector(selector) {
744
+ this.maskSelectors.add(selector);
745
+ this.syncDimensions(true);
746
+ }
747
+ removeMaskSelector(selector) {
748
+ this.maskSelectors.delete(selector);
749
+ this.syncDimensions(true);
750
+ }
751
+ on(event, listener) {
752
+ if (!this._eventListeners[event])
753
+ this._eventListeners[event] = [];
754
+ this._eventListeners[event].push(listener);
755
+ if (event === "ready" && this._ready && this.wgpuViewId !== null) {
756
+ const readyEvent = new CustomEvent(event, {
757
+ detail: { id: this.wgpuViewId }
758
+ });
759
+ queueMicrotask(() => {
760
+ if (this._eventListeners[event]?.includes(listener)) {
761
+ listener(readyEvent);
762
+ }
763
+ });
764
+ }
765
+ }
766
+ off(event, listener) {
767
+ if (!this._eventListeners[event])
768
+ return;
769
+ const idx = this._eventListeners[event].indexOf(listener);
770
+ if (idx !== -1)
771
+ this._eventListeners[event].splice(idx, 1);
772
+ }
773
+ emit(event, detail) {
774
+ const listeners = this._eventListeners[event];
775
+ if (listeners) {
776
+ const customEvent = new CustomEvent(event, { detail });
777
+ listeners.forEach((fn) => fn(customEvent));
778
+ }
779
+ }
780
+ }
781
+ function initWgpuTag() {
782
+ if (!customElements.get("electrobun-wgpu")) {
783
+ customElements.define("electrobun-wgpu", ElectrobunWgpuTag);
784
+ }
785
+ const injectStyles = () => {
786
+ const style = document.createElement("style");
787
+ style.textContent = `
788
+ electrobun-wgpu {
789
+ display: block;
790
+ width: 800px;
791
+ height: 300px;
792
+ background: #000;
793
+ overflow: hidden;
794
+ }
795
+ `;
796
+ if (document.head?.firstChild) {
797
+ document.head.insertBefore(style, document.head.firstChild);
798
+ } else if (document.head) {
799
+ document.head.appendChild(style);
800
+ }
801
+ };
802
+ if (document.head) {
803
+ injectStyles();
804
+ } else {
805
+ document.addEventListener("DOMContentLoaded", injectStyles);
806
+ }
807
+ }
808
+
809
+ // src/preload/events.ts
810
+ function emitWebviewEvent(eventName, detail) {
811
+ setTimeout(() => {
812
+ const bridge = window.__electrobunEventBridge || window.__electrobunInternalBridge;
813
+ bridge?.postMessage(JSON.stringify({
814
+ id: "webviewEvent",
815
+ type: "message",
816
+ payload: {
817
+ id: window.__electrobunWebviewId,
818
+ eventName,
819
+ detail
820
+ }
821
+ }));
822
+ });
823
+ }
824
+ function initLifecycleEvents() {
825
+ window.addEventListener("load", () => {
826
+ if (window === window.top) {
827
+ emitWebviewEvent("dom-ready", document.location.href);
828
+ }
829
+ });
830
+ window.addEventListener("popstate", () => {
831
+ emitWebviewEvent("did-navigate-in-page", window.location.href);
832
+ });
833
+ window.addEventListener("hashchange", () => {
834
+ emitWebviewEvent("did-navigate-in-page", window.location.href);
835
+ });
836
+ }
837
+ var cmdKeyHeld = false;
838
+ var cmdKeyTimestamp = 0;
839
+ var CMD_KEY_THRESHOLD_MS = 500;
840
+ function isCmdHeld() {
841
+ if (cmdKeyHeld)
842
+ return true;
843
+ return Date.now() - cmdKeyTimestamp < CMD_KEY_THRESHOLD_MS && cmdKeyTimestamp > 0;
844
+ }
845
+ function initCmdClickHandling() {
846
+ window.addEventListener("keydown", (event) => {
847
+ if (event.key === "Meta" || event.metaKey) {
848
+ cmdKeyHeld = true;
849
+ cmdKeyTimestamp = Date.now();
850
+ }
851
+ }, true);
852
+ window.addEventListener("keyup", (event) => {
853
+ if (event.key === "Meta") {
854
+ cmdKeyHeld = false;
855
+ cmdKeyTimestamp = Date.now();
856
+ }
857
+ }, true);
858
+ window.addEventListener("blur", () => {
859
+ cmdKeyHeld = false;
860
+ });
861
+ window.addEventListener("click", (event) => {
862
+ if (event.metaKey || event.ctrlKey) {
863
+ const anchor = event.target?.closest?.("a");
864
+ if (anchor && anchor.href) {
865
+ event.preventDefault();
866
+ event.stopPropagation();
867
+ event.stopImmediatePropagation();
868
+ emitWebviewEvent("new-window-open", JSON.stringify({
869
+ url: anchor.href,
870
+ isCmdClick: true,
871
+ isSPANavigation: false
872
+ }));
873
+ }
874
+ }
875
+ }, true);
876
+ }
877
+ function initSPANavigationInterception() {
878
+ const originalPushState = history.pushState;
879
+ const originalReplaceState = history.replaceState;
880
+ history.pushState = function(state, title, url) {
881
+ if (isCmdHeld() && url) {
882
+ const resolvedUrl = new URL(String(url), window.location.href).href;
883
+ emitWebviewEvent("new-window-open", JSON.stringify({
884
+ url: resolvedUrl,
885
+ isCmdClick: true,
886
+ isSPANavigation: true
887
+ }));
888
+ return;
889
+ }
890
+ return originalPushState.apply(this, [state, title, url]);
891
+ };
892
+ history.replaceState = function(state, title, url) {
893
+ if (isCmdHeld() && url) {
894
+ const resolvedUrl = new URL(String(url), window.location.href).href;
895
+ emitWebviewEvent("new-window-open", JSON.stringify({
896
+ url: resolvedUrl,
897
+ isCmdClick: true,
898
+ isSPANavigation: true
899
+ }));
900
+ return;
901
+ }
902
+ return originalReplaceState.apply(this, [state, title, url]);
903
+ };
904
+ }
905
+ function initOverscrollPrevention() {
906
+ document.addEventListener("DOMContentLoaded", () => {
907
+ const style = document.createElement("style");
908
+ style.type = "text/css";
909
+ style.appendChild(document.createTextNode("html, body { overscroll-behavior: none; }"));
910
+ document.head.appendChild(style);
911
+ });
912
+ }
913
+
914
+ // src/preload/index.ts
915
+ initEncryption().catch((err) => console.error("Failed to initialize encryption:", err));
916
+ var internalMessageHandler = (msg) => {
917
+ handleResponse(msg);
918
+ };
919
+ var defaultUserMessageHandler = (msg) => {
920
+ if (!window.__electrobunPendingHostMessages) {
921
+ window.__electrobunPendingHostMessages = [];
922
+ }
923
+ window.__electrobunPendingHostMessages.push(msg);
924
+ };
925
+ if (!window.__electrobun) {
926
+ window.__electrobun = {
927
+ receiveInternalMessageFromHost: internalMessageHandler,
928
+ receiveMessageFromHost: defaultUserMessageHandler,
929
+ receiveInternalMessageFromBun: internalMessageHandler,
930
+ receiveMessageFromBun: defaultUserMessageHandler
931
+ };
932
+ } else {
933
+ window.__electrobun.receiveInternalMessageFromHost = internalMessageHandler;
934
+ window.__electrobun.receiveMessageFromHost = defaultUserMessageHandler;
935
+ window.__electrobun.receiveInternalMessageFromBun = internalMessageHandler;
936
+ window.__electrobun.receiveMessageFromBun = defaultUserMessageHandler;
937
+ }
938
+ window.__electrobunSendToHost = (message) => {
939
+ emitWebviewEvent("host-message", JSON.stringify(message));
940
+ };
941
+ initLifecycleEvents();
942
+ initCmdClickHandling();
943
+ initSPANavigationInterception();
944
+ initOverscrollPrevention();
945
+ initDragRegions();
946
+ initWebviewTag();
947
+ initWgpuTag();
948
+ })();