electrobun 1.18.4-beta.6 → 2.0.1-beta.13

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 (63) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +31 -170
  3. package/bin/electrobun.cjs +230 -153
  4. package/package.json +18 -49
  5. package/bun.lock +0 -119
  6. package/dist/api/browser/builtinrpcSchema.ts +0 -19
  7. package/dist/api/browser/global.d.ts +0 -36
  8. package/dist/api/browser/index.ts +0 -234
  9. package/dist/api/browser/webviewtag.ts +0 -88
  10. package/dist/api/browser/wgputag.ts +0 -48
  11. package/dist/api/bun/ElectrobunConfig.ts +0 -530
  12. package/dist/api/bun/__tests__/ffi-contract.test.ts +0 -105
  13. package/dist/api/bun/core/ApplicationMenu.ts +0 -70
  14. package/dist/api/bun/core/BrowserView.ts +0 -419
  15. package/dist/api/bun/core/BrowserWindow.ts +0 -400
  16. package/dist/api/bun/core/BuildConfig.ts +0 -71
  17. package/dist/api/bun/core/ContextMenu.ts +0 -75
  18. package/dist/api/bun/core/GpuWindow.ts +0 -289
  19. package/dist/api/bun/core/Paths.ts +0 -5
  20. package/dist/api/bun/core/Socket.ts +0 -22
  21. package/dist/api/bun/core/Tray.ts +0 -197
  22. package/dist/api/bun/core/Updater.ts +0 -1162
  23. package/dist/api/bun/core/Utils.ts +0 -487
  24. package/dist/api/bun/core/WGPUView.ts +0 -167
  25. package/dist/api/bun/core/menuRoles.ts +0 -181
  26. package/dist/api/bun/events/ApplicationEvents.ts +0 -22
  27. package/dist/api/bun/events/event.ts +0 -29
  28. package/dist/api/bun/events/eventEmitter.ts +0 -45
  29. package/dist/api/bun/events/trayEvents.ts +0 -11
  30. package/dist/api/bun/events/webviewEvents.ts +0 -39
  31. package/dist/api/bun/events/windowEvents.ts +0 -23
  32. package/dist/api/bun/index.ts +0 -298
  33. package/dist/api/bun/preload/.generated/compiled.ts +0 -8
  34. package/dist/api/bun/preload/build.ts +0 -65
  35. package/dist/api/bun/preload/dragRegions.ts +0 -41
  36. package/dist/api/bun/preload/encryption.ts +0 -86
  37. package/dist/api/bun/preload/events.ts +0 -171
  38. package/dist/api/bun/preload/globals.d.ts +0 -45
  39. package/dist/api/bun/preload/index-sandboxed.ts +0 -28
  40. package/dist/api/bun/preload/index.ts +0 -77
  41. package/dist/api/bun/preload/internalRpc.ts +0 -80
  42. package/dist/api/bun/preload/overlaySync.ts +0 -107
  43. package/dist/api/bun/preload/webviewTag.ts +0 -451
  44. package/dist/api/bun/preload/wgpuTag.ts +0 -246
  45. package/dist/api/bun/proc/linux.md +0 -43
  46. package/dist/api/bun/proc/native.ts +0 -3385
  47. package/dist/api/bun/webGPU.ts +0 -346
  48. package/dist/api/bun/webgpuAdapter.ts +0 -3011
  49. package/dist/api/shared/bun-version.ts +0 -3
  50. package/dist/api/shared/cef-version.ts +0 -5
  51. package/dist/api/shared/electrobun-version.ts +0 -2
  52. package/dist/api/shared/naming.test.ts +0 -327
  53. package/dist/api/shared/naming.ts +0 -188
  54. package/dist/api/shared/platform.ts +0 -48
  55. package/dist/api/shared/rpc.ts +0 -541
  56. package/dist/main.js +0 -168
  57. package/dist/preload-full.js +0 -913
  58. package/dist/preload-sandboxed.js +0 -111
  59. package/dist/zig-sdk/electrobun.zig +0 -1993
  60. package/src/cli/bun.lockb +0 -0
  61. package/src/cli/index.ts +0 -5689
  62. package/src/cli/package-lock.json +0 -81
  63. package/src/cli/package.json +0 -11
@@ -1,107 +0,0 @@
1
- import "./globals.d.ts";
2
-
3
- export interface Rect {
4
- x: number;
5
- y: number;
6
- width: number;
7
- height: number;
8
- }
9
-
10
- export type MaskRect = Rect;
11
-
12
- export type OverlaySyncOptions = {
13
- onSync: (rect: Rect, masksJson: string) => void;
14
- getMasks?: () => MaskRect[];
15
- burstIntervalMs?: number;
16
- baseIntervalMs?: number;
17
- burstDurationMs?: number;
18
- };
19
-
20
- export class OverlaySyncController {
21
- private element: HTMLElement;
22
- private options: Required<OverlaySyncOptions>;
23
- private lastRect: Rect = { x: 0, y: 0, width: 0, height: 0 };
24
- private resizeObserver: ResizeObserver | null = null;
25
- private positionLoop: ReturnType<typeof setTimeout> | null = null;
26
- private resizeHandler: (() => void) | null = null;
27
- private burstUntil = 0;
28
-
29
- constructor(element: HTMLElement, options: OverlaySyncOptions) {
30
- this.element = element;
31
- this.options = {
32
- onSync: options.onSync,
33
- getMasks: options.getMasks ?? (() => []),
34
- burstIntervalMs: options.burstIntervalMs ?? 50,
35
- baseIntervalMs: options.baseIntervalMs ?? 100,
36
- burstDurationMs: options.burstDurationMs ?? 500,
37
- };
38
- }
39
-
40
- start() {
41
- this.resizeObserver = new ResizeObserver(() => this.sync());
42
- this.resizeObserver.observe(this.element);
43
-
44
- const loop = () => {
45
- this.sync();
46
- const now = performance.now();
47
- const interval =
48
- now < this.burstUntil
49
- ? this.options.burstIntervalMs
50
- : this.options.baseIntervalMs;
51
- this.positionLoop = setTimeout(loop, interval);
52
- };
53
- this.positionLoop = setTimeout(loop, this.options.baseIntervalMs);
54
-
55
- this.resizeHandler = () => this.sync(true);
56
- window.addEventListener("resize", this.resizeHandler);
57
- }
58
-
59
- stop() {
60
- if (this.resizeObserver) this.resizeObserver.disconnect();
61
- if (this.positionLoop) clearTimeout(this.positionLoop);
62
- if (this.resizeHandler) {
63
- window.removeEventListener("resize", this.resizeHandler);
64
- }
65
- this.resizeObserver = null;
66
- this.positionLoop = null;
67
- this.resizeHandler = null;
68
- }
69
-
70
- forceSync() {
71
- this.sync(true);
72
- }
73
-
74
- setLastRect(rect: Rect) {
75
- this.lastRect = rect;
76
- }
77
-
78
- private sync(force = false) {
79
- const rect = this.element.getBoundingClientRect();
80
- const newRect: Rect = {
81
- x: rect.x,
82
- y: rect.y,
83
- width: rect.width,
84
- height: rect.height,
85
- };
86
-
87
- if (newRect.width === 0 && newRect.height === 0) {
88
- return;
89
- }
90
-
91
- if (
92
- !force &&
93
- newRect.x === this.lastRect.x &&
94
- newRect.y === this.lastRect.y &&
95
- newRect.width === this.lastRect.width &&
96
- newRect.height === this.lastRect.height
97
- ) {
98
- return;
99
- }
100
-
101
- this.burstUntil = performance.now() + this.options.burstDurationMs;
102
- this.lastRect = newRect;
103
-
104
- const masks = this.options.getMasks();
105
- this.options.onSync(newRect, JSON.stringify(masks));
106
- }
107
- }
@@ -1,451 +0,0 @@
1
- // <electrobun-webview> Custom Element
2
- // Provides OOPIF (out-of-process iframe) functionality
3
-
4
- import "./globals.d.ts";
5
- import { send, request } from "./internalRpc";
6
- import { OverlaySyncController, type Rect } from "./overlaySync";
7
-
8
- type WebviewEventType =
9
- | "will-navigate"
10
- | "did-navigate"
11
- | "did-navigate-in-page"
12
- | "did-commit-navigation"
13
- | "dom-ready"
14
- | "new-window-open"
15
- | "host-message"
16
- | "download-started"
17
- | "download-progress"
18
- | "download-completed"
19
- | "download-failed"
20
- | "load-started"
21
- | "load-committed"
22
- | "load-finished";
23
-
24
- // Registry for webview instances (for event routing from bun)
25
- export const webviewRegistry: Record<number, ElectrobunWebviewTag> = {};
26
-
27
- export class ElectrobunWebviewTag extends HTMLElement {
28
- webviewId: number | null = null;
29
- maskSelectors: Set<string> = new Set();
30
- private _sync: OverlaySyncController | null = null;
31
- transparent = false;
32
- passthroughEnabled = false;
33
- hidden = false;
34
- // Sandbox mode: when true, disables RPC and only allows event emission in the child webview
35
- sandboxed = false;
36
- private _eventListeners: Record<string, Array<(event: CustomEvent) => void>> =
37
- {};
38
-
39
- static get observedAttributes() {
40
- return ["src", "html"];
41
- }
42
-
43
- constructor() {
44
- super();
45
- }
46
-
47
- connectedCallback() {
48
- requestAnimationFrame(() => this.initWebview());
49
- }
50
-
51
- attributeChangedCallback(
52
- name: string,
53
- oldValue: string | null,
54
- newValue: string | null,
55
- ) {
56
- if (oldValue === newValue) return;
57
- if (newValue === null) return;
58
- if (this.webviewId === null) return;
59
- if (name === "src") this.loadURL(newValue);
60
- else if (name === "html") this.loadHTML(newValue);
61
- }
62
-
63
- disconnectedCallback() {
64
- if (this.webviewId !== null) {
65
- send("webviewTagRemove", { id: this.webviewId });
66
- delete webviewRegistry[this.webviewId];
67
- }
68
- if (this._sync) this._sync.stop();
69
- }
70
-
71
- private getInitialNavigationRules(): string[] | null {
72
- const rawRules = this.getAttribute("navigation-rules");
73
- if (rawRules === null) {
74
- return null;
75
- }
76
-
77
- const trimmed = rawRules.trim();
78
- if (!trimmed) {
79
- return [];
80
- }
81
-
82
- try {
83
- const parsed = JSON.parse(trimmed);
84
- if (!Array.isArray(parsed) || !parsed.every((rule) => typeof rule === "string")) {
85
- throw new Error("navigation-rules must be a JSON string array");
86
- }
87
- return parsed;
88
- } catch (error) {
89
- console.error("Invalid navigation-rules attribute:", error);
90
- return [];
91
- }
92
- }
93
-
94
- async initWebview() {
95
- const rect = this.getBoundingClientRect();
96
- const initialRect = {
97
- x: rect.x,
98
- y: rect.y,
99
- width: rect.width,
100
- height: rect.height,
101
- };
102
-
103
- const url = this.getAttribute("src");
104
- const html = this.getAttribute("html");
105
- const preload = this.getAttribute("preload");
106
- const partition = this.getAttribute("partition");
107
- const renderer = (this.getAttribute("renderer") || "native") as
108
- | "native"
109
- | "cef";
110
- const masks = this.getAttribute("masks");
111
- const navigationRules = this.getInitialNavigationRules();
112
- // Sandbox attribute: when present, the child webview is sandboxed (no RPC, events only)
113
- const sandbox = this.hasAttribute("sandbox");
114
- this.sandboxed = sandbox;
115
- // Read transparent/passthrough attributes for initial state (avoids flash)
116
- const transparent = this.hasAttribute("transparent");
117
- const passthrough = this.hasAttribute("passthrough");
118
- this.transparent = transparent;
119
- this.passthroughEnabled = passthrough;
120
- if (transparent) this.style.opacity = "0";
121
- if (passthrough) this.style.pointerEvents = "none";
122
-
123
- if (masks) {
124
- masks.split(",").forEach((s) => this.maskSelectors.add(s.trim()));
125
- }
126
-
127
- try {
128
- const webviewInitParams = {
129
- hostWebviewId: window.__electrobunWebviewId,
130
- windowId: window.__electrobunWindowId,
131
- renderer,
132
- url,
133
- html,
134
- preload,
135
- partition,
136
- frame: {
137
- width: rect.width,
138
- height: rect.height,
139
- x: rect.x,
140
- y: rect.y,
141
- },
142
- sandbox,
143
- transparent,
144
- passthrough,
145
- ...(navigationRules === null ? {} : { navigationRules }),
146
- };
147
-
148
- const webviewId = (await request(
149
- "webviewTagInit",
150
- webviewInitParams,
151
- )) as number;
152
-
153
- this.webviewId = webviewId;
154
- this.id = `electrobun-webview-${webviewId}`;
155
- webviewRegistry[webviewId] = this;
156
-
157
- this.setupObservers(initialRect);
158
- // Force immediate sync after initialization
159
- this.syncDimensions(true);
160
-
161
- // When adding a new webview, force all existing webviews to re-sync their positions
162
- // This handles layout changes caused by the new webview
163
- // Use requestAnimationFrame to ensure DOM layout is complete
164
- requestAnimationFrame(() => {
165
- Object.values(webviewRegistry).forEach((webview) => {
166
- if (webview !== this && webview.webviewId !== null) {
167
- webview.syncDimensions(true);
168
- }
169
- });
170
- });
171
- } catch (err) {
172
- console.error("Failed to init webview:", err);
173
- }
174
- }
175
-
176
- setupObservers(initialRect: Rect) {
177
- const getMasks = () => {
178
- const rect = this.getBoundingClientRect();
179
- const masks: Rect[] = [];
180
- this.maskSelectors.forEach((selector) => {
181
- try {
182
- document.querySelectorAll(selector).forEach((el) => {
183
- const mr = el.getBoundingClientRect();
184
- masks.push({
185
- x: mr.x - rect.x,
186
- y: mr.y - rect.y,
187
- width: mr.width,
188
- height: mr.height,
189
- });
190
- });
191
- } catch (_e) {
192
- // Invalid selector, ignore
193
- }
194
- });
195
- return masks;
196
- };
197
-
198
- this._sync = new OverlaySyncController(this, {
199
- onSync: (rect, masksJson) => {
200
- if (this.webviewId === null) return;
201
- send("webviewTagResize", {
202
- id: this.webviewId,
203
- frame: rect,
204
- masks: masksJson,
205
- });
206
- },
207
- getMasks,
208
- burstIntervalMs: 10,
209
- baseIntervalMs: 100,
210
- burstDurationMs: 50,
211
- });
212
- this._sync.setLastRect(initialRect);
213
- this._sync.start();
214
- }
215
-
216
- syncDimensions(force = false) {
217
- if (!this._sync) return;
218
- if (force) {
219
- this._sync.forceSync();
220
- }
221
- }
222
-
223
- // Navigation methods
224
- loadURL(url: string) {
225
- if (this.webviewId === null) return;
226
- this.setAttribute("src", url);
227
- send("webviewTagUpdateSrc", { id: this.webviewId, url });
228
- }
229
-
230
- loadHTML(html: string) {
231
- if (this.webviewId === null) return;
232
- send("webviewTagUpdateHtml", { id: this.webviewId, html });
233
- }
234
-
235
- reload() {
236
- if (this.webviewId !== null)
237
- send("webviewTagReload", { id: this.webviewId });
238
- }
239
-
240
- goBack() {
241
- if (this.webviewId !== null)
242
- send("webviewTagGoBack", { id: this.webviewId });
243
- }
244
-
245
- goForward() {
246
- if (this.webviewId !== null)
247
- send("webviewTagGoForward", { id: this.webviewId });
248
- }
249
-
250
- async canGoBack(): Promise<boolean> {
251
- if (this.webviewId === null) return false;
252
- return (await request("webviewTagCanGoBack", {
253
- id: this.webviewId,
254
- })) as boolean;
255
- }
256
-
257
- async canGoForward(): Promise<boolean> {
258
- if (this.webviewId === null) return false;
259
- return (await request("webviewTagCanGoForward", {
260
- id: this.webviewId,
261
- })) as boolean;
262
- }
263
-
264
- // Visibility methods
265
- toggleTransparent(value?: boolean) {
266
- if (this.webviewId === null) return;
267
- this.transparent = value !== undefined ? value : !this.transparent;
268
- this.style.opacity = this.transparent ? "0" : "";
269
- send("webviewTagSetTransparent", {
270
- id: this.webviewId,
271
- transparent: this.transparent,
272
- });
273
- }
274
-
275
- togglePassthrough(value?: boolean) {
276
- if (this.webviewId === null) return;
277
- this.passthroughEnabled =
278
- value !== undefined ? value : !this.passthroughEnabled;
279
- this.style.pointerEvents = this.passthroughEnabled ? "none" : "";
280
- send("webviewTagSetPassthrough", {
281
- id: this.webviewId,
282
- enablePassthrough: this.passthroughEnabled,
283
- });
284
- }
285
-
286
- toggleHidden(value?: boolean) {
287
- if (this.webviewId === null) return;
288
- this.hidden = value !== undefined ? value : !this.hidden;
289
- send("webviewTagSetHidden", { id: this.webviewId, hidden: this.hidden });
290
- }
291
-
292
- // Mask management
293
- addMaskSelector(selector: string) {
294
- this.maskSelectors.add(selector);
295
- this.syncDimensions(true);
296
- }
297
-
298
- removeMaskSelector(selector: string) {
299
- this.maskSelectors.delete(selector);
300
- this.syncDimensions(true);
301
- }
302
-
303
- // Navigation rules
304
- setNavigationRules(rules: string[]) {
305
- if (this.webviewId !== null) {
306
- send("webviewTagSetNavigationRules", { id: this.webviewId, rules });
307
- }
308
- }
309
-
310
- // Find in page
311
- findInPage(
312
- searchText: string,
313
- options?: { forward?: boolean; matchCase?: boolean },
314
- ) {
315
- if (this.webviewId === null) return;
316
- const forward = options?.forward !== false;
317
- const matchCase = options?.matchCase || false;
318
- send("webviewTagFindInPage", {
319
- id: this.webviewId,
320
- searchText,
321
- forward,
322
- matchCase,
323
- });
324
- }
325
-
326
- stopFindInPage() {
327
- if (this.webviewId !== null)
328
- send("webviewTagStopFind", { id: this.webviewId });
329
- }
330
-
331
- // DevTools
332
- openDevTools() {
333
- if (this.webviewId !== null)
334
- send("webviewTagOpenDevTools", { id: this.webviewId });
335
- }
336
-
337
- closeDevTools() {
338
- if (this.webviewId !== null)
339
- send("webviewTagCloseDevTools", { id: this.webviewId });
340
- }
341
-
342
- toggleDevTools() {
343
- if (this.webviewId !== null)
344
- send("webviewTagToggleDevTools", { id: this.webviewId });
345
- }
346
-
347
- // JavaScript execution
348
- executeJavascript(js: string) {
349
- if (this.webviewId === null) return;
350
- send("webviewTagExecuteJavascript", { id: this.webviewId, js });
351
- }
352
-
353
- // Event handling
354
- on(event: WebviewEventType, listener: (event: CustomEvent) => void) {
355
- if (!this._eventListeners[event]) this._eventListeners[event] = [];
356
- this._eventListeners[event].push(listener);
357
- }
358
-
359
- off(event: WebviewEventType, listener: (event: CustomEvent) => void) {
360
- if (!this._eventListeners[event]) return;
361
- const idx = this._eventListeners[event].indexOf(listener);
362
- if (idx !== -1) this._eventListeners[event].splice(idx, 1);
363
- }
364
-
365
- emit(event: WebviewEventType, detail: unknown) {
366
- const listeners = this._eventListeners[event];
367
- if (listeners) {
368
- const customEvent = new CustomEvent(event, { detail });
369
- listeners.forEach((fn) => fn(customEvent));
370
- }
371
- }
372
-
373
- // Property getters/setters
374
- get src(): string | null {
375
- return this.getAttribute("src");
376
- }
377
- set src(value: string | null) {
378
- if (value) {
379
- this.setAttribute("src", value);
380
- } else {
381
- this.removeAttribute("src");
382
- }
383
- }
384
-
385
- get html(): string | null {
386
- return this.getAttribute("html");
387
- }
388
- set html(value: string | null) {
389
- if (value) {
390
- this.setAttribute("html", value);
391
- } else {
392
- this.removeAttribute("html");
393
- }
394
- }
395
-
396
- get preload(): string | null {
397
- return this.getAttribute("preload");
398
- }
399
- set preload(value: string | null) {
400
- if (value) this.setAttribute("preload", value);
401
- else this.removeAttribute("preload");
402
- }
403
-
404
- get renderer(): "native" | "cef" {
405
- return (this.getAttribute("renderer") as "native" | "cef") || "native";
406
- }
407
- set renderer(value: "native" | "cef") {
408
- this.setAttribute("renderer", value);
409
- }
410
-
411
- // Sandbox is read-only after creation (set via attribute before adding to DOM)
412
- get sandbox(): boolean {
413
- return this.sandboxed;
414
- }
415
- }
416
-
417
- export function initWebviewTag() {
418
- // Register the custom element if not already registered
419
- if (!customElements.get("electrobun-webview")) {
420
- customElements.define("electrobun-webview", ElectrobunWebviewTag);
421
- }
422
-
423
- // Add default styles for <electrobun-webview> elements
424
- // These can be easily overridden in the host document
425
- const injectStyles = () => {
426
- const style = document.createElement("style");
427
- style.textContent = `
428
- electrobun-webview {
429
- display: block;
430
- width: 800px;
431
- height: 300px;
432
- background: #fff;
433
- background-repeat: no-repeat !important;
434
- overflow: hidden;
435
- }
436
- `;
437
- // Insert at the beginning of <head> so app styles take precedence
438
- if (document.head?.firstChild) {
439
- document.head.insertBefore(style, document.head.firstChild);
440
- } else if (document.head) {
441
- document.head.appendChild(style);
442
- }
443
- };
444
-
445
- // document.head may not exist at document start, defer if needed
446
- if (document.head) {
447
- injectStyles();
448
- } else {
449
- document.addEventListener("DOMContentLoaded", injectStyles);
450
- }
451
- }