electrobun 0.0.3 → 0.0.5
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.
- package/dist/webview +0 -0
- package/package.json +1 -1
- package/src/browser/index.ts +29 -16
- package/src/browser/webviewtag.ts +91 -19
- package/src/bun/core/BrowserView.ts +6 -2
- package/src/bun/proc/zig.ts +1 -0
package/dist/webview
CHANGED
|
Binary file
|
package/package.json
CHANGED
package/src/browser/index.ts
CHANGED
|
@@ -45,6 +45,7 @@ type WebviewTagHandlers = RPCSchema<{
|
|
|
45
45
|
width: number;
|
|
46
46
|
height: number;
|
|
47
47
|
};
|
|
48
|
+
masks: string;
|
|
48
49
|
};
|
|
49
50
|
webviewTagUpdateSrc: {
|
|
50
51
|
id: number;
|
|
@@ -77,6 +78,10 @@ type WebviewTagHandlers = RPCSchema<{
|
|
|
77
78
|
id: number;
|
|
78
79
|
transparent: boolean;
|
|
79
80
|
};
|
|
81
|
+
webviewTagToggleMirroring: {
|
|
82
|
+
id: number;
|
|
83
|
+
enable: boolean;
|
|
84
|
+
};
|
|
80
85
|
webviewTagSetPassthrough: {
|
|
81
86
|
id: number;
|
|
82
87
|
enablePassthrough: boolean;
|
|
@@ -236,24 +241,32 @@ class Electroview<T> {
|
|
|
236
241
|
}
|
|
237
242
|
|
|
238
243
|
bunBridge(msg: string) {
|
|
239
|
-
// Note:
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
//
|
|
245
|
-
//
|
|
246
|
-
//
|
|
247
|
-
|
|
248
|
-
//
|
|
249
|
-
|
|
244
|
+
// Note: messageHandlers seem to freeze when sending large messages
|
|
245
|
+
// but xhr to views://rpc can run into CORS issues on non views://
|
|
246
|
+
// loaded content (eg: when writing extensions/preload scripts for
|
|
247
|
+
// remote content).
|
|
248
|
+
|
|
249
|
+
// Since most messages--especially those on remote content, are small
|
|
250
|
+
// we can solve most use cases by having a fallback to xhr for
|
|
251
|
+
// large messages
|
|
252
|
+
|
|
253
|
+
// TEMP: disable the fallback for now. for some reason suddenly can't
|
|
254
|
+
// repro now that other places are chunking messages and laptop restart
|
|
255
|
+
if (true || msg.length < 8 * 1024) {
|
|
256
|
+
window.webkit.messageHandlers.bunBridge.postMessage(msg);
|
|
257
|
+
} else {
|
|
258
|
+
var xhr = new XMLHttpRequest();
|
|
259
|
+
|
|
260
|
+
// Note: we're only using synchronouse http on this async
|
|
261
|
+
// call to get around CORS for now
|
|
262
|
+
// Note: DO NOT use postMessage handlers since it
|
|
263
|
+
// freezes the process when sending lots of large messages
|
|
264
|
+
|
|
265
|
+
xhr.open("POST", "views://rpc", false); // sychronous call
|
|
266
|
+
xhr.send(msg);
|
|
267
|
+
}
|
|
250
268
|
}
|
|
251
269
|
|
|
252
|
-
// webviewTagBridge(msg) {
|
|
253
|
-
// // Note: zig sets up this custom message handler bridge
|
|
254
|
-
// window.webkit.messageHandlers.webviewTagBridge.postMessage(msg);
|
|
255
|
-
// }
|
|
256
|
-
|
|
257
270
|
receiveMessageFromBun(msg) {
|
|
258
271
|
// NOTE: in the webview messages are passed by executing ElectrobunView.receiveMessageFromBun(object)
|
|
259
272
|
// so they're already parsed into an object here
|
|
@@ -4,6 +4,8 @@ type WebviewEventTypes =
|
|
|
4
4
|
| "did-commit-navigation"
|
|
5
5
|
| "dom-ready";
|
|
6
6
|
|
|
7
|
+
type Rect = { x: number; y: number; width: number; height: number };
|
|
8
|
+
|
|
7
9
|
const ConfigureWebviewTags = (
|
|
8
10
|
enableWebviewTags: boolean,
|
|
9
11
|
zigRpc: (params: any) => any,
|
|
@@ -24,6 +26,10 @@ const ConfigureWebviewTags = (
|
|
|
24
26
|
zigRpc: any;
|
|
25
27
|
syncRpc: any;
|
|
26
28
|
|
|
29
|
+
// querySelectors for elements that you want to appear
|
|
30
|
+
// in front of the webview.
|
|
31
|
+
maskSelectors: Set<string> = new Set();
|
|
32
|
+
|
|
27
33
|
// observers
|
|
28
34
|
resizeObserver?: ResizeObserver;
|
|
29
35
|
// intersectionObserver?: IntersectionObserver;
|
|
@@ -39,12 +45,16 @@ const ConfigureWebviewTags = (
|
|
|
39
45
|
height: 0,
|
|
40
46
|
};
|
|
41
47
|
|
|
48
|
+
lastMasksJSON: string = "";
|
|
49
|
+
lastMasks: Rect[] = [];
|
|
50
|
+
|
|
42
51
|
transparent: boolean = false;
|
|
43
52
|
passthroughEnabled: boolean = false;
|
|
44
53
|
hidden: boolean = false;
|
|
45
54
|
delegateMode: boolean = false;
|
|
46
55
|
hiddenMirrorMode: boolean = false;
|
|
47
56
|
wasZeroRect: boolean = false;
|
|
57
|
+
isMirroring: boolean = false;
|
|
48
58
|
|
|
49
59
|
partition: string | null = null;
|
|
50
60
|
|
|
@@ -59,6 +69,16 @@ const ConfigureWebviewTags = (
|
|
|
59
69
|
});
|
|
60
70
|
}
|
|
61
71
|
|
|
72
|
+
addMaskSelector(selector: string) {
|
|
73
|
+
this.maskSelectors.add(selector);
|
|
74
|
+
this.syncDimensions();
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
removeMaskSelector(selector: string) {
|
|
78
|
+
this.maskSelectors.delete(selector);
|
|
79
|
+
this.syncDimensions();
|
|
80
|
+
}
|
|
81
|
+
|
|
62
82
|
initWebview() {
|
|
63
83
|
const rect = this.getBoundingClientRect();
|
|
64
84
|
this.lastRect = rect;
|
|
@@ -206,6 +226,10 @@ const ConfigureWebviewTags = (
|
|
|
206
226
|
// know that they're chaning something in order to eliminate the lag that the
|
|
207
227
|
// catch all loop will catch
|
|
208
228
|
syncDimensions(force: boolean = false) {
|
|
229
|
+
if (!force && this.hidden) {
|
|
230
|
+
return;
|
|
231
|
+
}
|
|
232
|
+
|
|
209
233
|
const rect = this.getBoundingClientRect();
|
|
210
234
|
const { x, y, width, height } =
|
|
211
235
|
this.adjustDimensionsForHiddenMirrorMode(rect);
|
|
@@ -215,24 +239,49 @@ const ConfigureWebviewTags = (
|
|
|
215
239
|
if (this.wasZeroRect === false) {
|
|
216
240
|
this.wasZeroRect = true;
|
|
217
241
|
this.toggleHidden(true, true);
|
|
218
|
-
this.stopMirroring();
|
|
219
242
|
}
|
|
220
243
|
return;
|
|
221
244
|
}
|
|
222
245
|
|
|
246
|
+
const masks: Rect[] = [];
|
|
247
|
+
this.maskSelectors.forEach((selector) => {
|
|
248
|
+
const els = document.querySelectorAll(selector);
|
|
249
|
+
|
|
250
|
+
for (let i = 0; i < els.length; i++) {
|
|
251
|
+
const el = els[i];
|
|
252
|
+
|
|
253
|
+
if (el) {
|
|
254
|
+
const maskRect = el.getBoundingClientRect();
|
|
255
|
+
|
|
256
|
+
masks.push({
|
|
257
|
+
// reposition the bounding rect to be relative to the webview rect
|
|
258
|
+
// so objc can apply the mask correctly and handle the actual overlap
|
|
259
|
+
x: maskRect.x - x,
|
|
260
|
+
y: maskRect.y - y,
|
|
261
|
+
width: maskRect.width,
|
|
262
|
+
height: maskRect.height,
|
|
263
|
+
});
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
});
|
|
267
|
+
|
|
268
|
+
// store jsonStringified last masks value to compare
|
|
269
|
+
const masksJson = masks.length ? JSON.stringify(masks) : "";
|
|
270
|
+
|
|
223
271
|
if (
|
|
224
272
|
force ||
|
|
225
273
|
lastRect.x !== x ||
|
|
226
274
|
lastRect.y !== y ||
|
|
227
275
|
lastRect.width !== width ||
|
|
228
|
-
lastRect.height !== height
|
|
276
|
+
lastRect.height !== height ||
|
|
277
|
+
this.lastMasksJSON !== masksJson
|
|
229
278
|
) {
|
|
230
|
-
//
|
|
231
|
-
|
|
232
|
-
this.setPositionCheckLoop(true);
|
|
233
|
-
}
|
|
279
|
+
// let it know we're still accelerating
|
|
280
|
+
this.setPositionCheckLoop(true);
|
|
234
281
|
|
|
235
282
|
this.lastRect = rect;
|
|
283
|
+
this.lastMasks = masks;
|
|
284
|
+
this.lastMasksJSON = masksJson;
|
|
236
285
|
|
|
237
286
|
this.zigRpc.send.webviewTagResize({
|
|
238
287
|
id: this.webviewId,
|
|
@@ -242,6 +291,7 @@ const ConfigureWebviewTags = (
|
|
|
242
291
|
x: x,
|
|
243
292
|
y: y,
|
|
244
293
|
},
|
|
294
|
+
masks: masksJson,
|
|
245
295
|
});
|
|
246
296
|
}
|
|
247
297
|
|
|
@@ -265,13 +315,12 @@ const ConfigureWebviewTags = (
|
|
|
265
315
|
this.positionCheckLoopReset = undefined;
|
|
266
316
|
}
|
|
267
317
|
|
|
268
|
-
const delay = accelerate ?
|
|
318
|
+
const delay = accelerate ? 0 : 300;
|
|
269
319
|
|
|
270
320
|
if (accelerate) {
|
|
271
|
-
clearTimeout(this.positionCheckLoopReset);
|
|
272
321
|
this.positionCheckLoopReset = setTimeout(() => {
|
|
273
322
|
this.setPositionCheckLoop(false);
|
|
274
|
-
},
|
|
323
|
+
}, 2000);
|
|
275
324
|
}
|
|
276
325
|
// Note: Since there's not catch all way to listen for x/y changes
|
|
277
326
|
// we have a 400ms interval to check
|
|
@@ -300,7 +349,6 @@ const ConfigureWebviewTags = (
|
|
|
300
349
|
// we still need to send it to objc to calculate from its bottom left position
|
|
301
350
|
// otherwise it'll move around unexpectedly.
|
|
302
351
|
window.addEventListener("resize", this.boundForceSyncDimensions);
|
|
303
|
-
|
|
304
352
|
window.addEventListener("scroll", this.boundSyncDimensions);
|
|
305
353
|
|
|
306
354
|
// todo: For chromium webviews (windows native or chromium bundled)
|
|
@@ -311,7 +359,6 @@ const ConfigureWebviewTags = (
|
|
|
311
359
|
disconnectedCallback() {
|
|
312
360
|
// removed from the dom
|
|
313
361
|
clearInterval(this.positionCheckLoop);
|
|
314
|
-
this.stopMirroring();
|
|
315
362
|
|
|
316
363
|
this.resizeObserver?.disconnect();
|
|
317
364
|
// this.intersectionObserver?.disconnect();
|
|
@@ -407,9 +454,8 @@ const ConfigureWebviewTags = (
|
|
|
407
454
|
DEFAULT_FRAME_RATE = Math.round(1000 / 30); // 30fps
|
|
408
455
|
streamScreenInterval?: Timer;
|
|
409
456
|
|
|
410
|
-
//
|
|
411
|
-
|
|
412
|
-
startMirroring(frameRate: number = this.DEFAULT_FRAME_RATE) {
|
|
457
|
+
// NOTE: This is very cpu intensive, Prefer startMirroring where possible
|
|
458
|
+
startMirroringToDom(frameRate: number = this.DEFAULT_FRAME_RATE) {
|
|
413
459
|
if (this.streamScreenInterval) {
|
|
414
460
|
clearInterval(this.streamScreenInterval);
|
|
415
461
|
}
|
|
@@ -419,13 +465,39 @@ const ConfigureWebviewTags = (
|
|
|
419
465
|
}, frameRate);
|
|
420
466
|
}
|
|
421
467
|
|
|
422
|
-
|
|
468
|
+
stopMirroringToDom() {
|
|
423
469
|
if (this.streamScreenInterval) {
|
|
424
470
|
clearInterval(this.streamScreenInterval);
|
|
425
471
|
this.streamScreenInterval = undefined;
|
|
426
472
|
}
|
|
427
473
|
}
|
|
428
474
|
|
|
475
|
+
startMirroring() {
|
|
476
|
+
// TEMP: mirroring now happens automatically in objc
|
|
477
|
+
// when the mouse moves. I'm leaving this here for now
|
|
478
|
+
// because I suspect there may still be use cases to
|
|
479
|
+
// toggle it from the dom outside of the mouse moving.
|
|
480
|
+
return;
|
|
481
|
+
if (this.isMirroring === false) {
|
|
482
|
+
this.isMirroring = true;
|
|
483
|
+
this.zigRpc.send.webviewTagToggleMirroring({
|
|
484
|
+
id: this.webviewId,
|
|
485
|
+
enable: true,
|
|
486
|
+
});
|
|
487
|
+
}
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
stopMirroring() {
|
|
491
|
+
return;
|
|
492
|
+
if (this.isMirroring === true) {
|
|
493
|
+
this.isMirroring = false;
|
|
494
|
+
this.zigRpc.send.webviewTagToggleMirroring({
|
|
495
|
+
id: this.webviewId,
|
|
496
|
+
enable: false,
|
|
497
|
+
});
|
|
498
|
+
}
|
|
499
|
+
}
|
|
500
|
+
|
|
429
501
|
clearScreenImage() {
|
|
430
502
|
this.style.backgroundImage = "";
|
|
431
503
|
}
|
|
@@ -503,11 +575,11 @@ const ConfigureWebviewTags = (
|
|
|
503
575
|
this.syncScreenshot(() => {
|
|
504
576
|
this.delegateMode = true;
|
|
505
577
|
this.toggleTransparent(true, true);
|
|
506
|
-
this.
|
|
578
|
+
this.startMirroringToDom();
|
|
507
579
|
});
|
|
508
580
|
} else {
|
|
509
581
|
this.delegateMode = false;
|
|
510
|
-
this.
|
|
582
|
+
this.stopMirroringToDom();
|
|
511
583
|
this.toggleTransparent(this.transparent);
|
|
512
584
|
this.tryClearScreenImage();
|
|
513
585
|
}
|
|
@@ -527,10 +599,10 @@ const ConfigureWebviewTags = (
|
|
|
527
599
|
this.hiddenMirrorMode = true;
|
|
528
600
|
this.toggleHidden(true, true);
|
|
529
601
|
this.togglePassthrough(true, true);
|
|
530
|
-
this.
|
|
602
|
+
this.startMirroringToDom();
|
|
531
603
|
});
|
|
532
604
|
} else {
|
|
533
|
-
this.
|
|
605
|
+
this.stopMirroringToDom();
|
|
534
606
|
this.toggleHidden(this.hidden);
|
|
535
607
|
this.togglePassthrough(this.passthroughEnabled);
|
|
536
608
|
this.tryClearScreenImage();
|
|
@@ -16,7 +16,7 @@ import type { BuiltinBunToWebviewSchema } from "../../browser/builtinrpcSchema";
|
|
|
16
16
|
const BrowserViewMap = {};
|
|
17
17
|
let nextWebviewId = 1;
|
|
18
18
|
|
|
19
|
-
const CHUNK_SIZE =
|
|
19
|
+
const CHUNK_SIZE = 1024 * 4; // 4KB
|
|
20
20
|
|
|
21
21
|
type BrowserViewOptions<T = undefined> = {
|
|
22
22
|
url: string | null;
|
|
@@ -32,6 +32,7 @@ type BrowserViewOptions<T = undefined> = {
|
|
|
32
32
|
rpc: T;
|
|
33
33
|
syncRpc: { [method: string]: (params: any) => any };
|
|
34
34
|
hostWebviewId: number;
|
|
35
|
+
autoResize: boolean;
|
|
35
36
|
};
|
|
36
37
|
|
|
37
38
|
interface ElectrobunWebviewRPCSChema {
|
|
@@ -68,6 +69,7 @@ const internalSyncRpcHandlers = {
|
|
|
68
69
|
partition,
|
|
69
70
|
frame,
|
|
70
71
|
hostWebviewId,
|
|
72
|
+
autoResize: false,
|
|
71
73
|
});
|
|
72
74
|
|
|
73
75
|
// Note: we have to give it a couple of ticks to fully create the browserview
|
|
@@ -104,6 +106,7 @@ export class BrowserView<T> {
|
|
|
104
106
|
html: string | null = null;
|
|
105
107
|
preload: string | null = null;
|
|
106
108
|
partition: string | null = null;
|
|
109
|
+
autoResize: boolean = true;
|
|
107
110
|
frame: {
|
|
108
111
|
x: number;
|
|
109
112
|
y: number;
|
|
@@ -135,6 +138,7 @@ export class BrowserView<T> {
|
|
|
135
138
|
// file exists first
|
|
136
139
|
this.pipePrefix = `/private/tmp/electrobun_ipc_pipe_${hash}_${randomId}_${this.id}`;
|
|
137
140
|
this.hostWebviewId = options.hostWebviewId;
|
|
141
|
+
this.autoResize = options.autoResize === false ? false : true;
|
|
138
142
|
|
|
139
143
|
this.init();
|
|
140
144
|
}
|
|
@@ -158,7 +162,7 @@ export class BrowserView<T> {
|
|
|
158
162
|
x: this.frame.x,
|
|
159
163
|
y: this.frame.y,
|
|
160
164
|
},
|
|
161
|
-
|
|
165
|
+
autoResize: this.autoResize,
|
|
162
166
|
});
|
|
163
167
|
|
|
164
168
|
this.createStreams();
|