electrobun 0.0.3 → 0.0.4
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 +5 -0
- package/src/browser/webviewtag.ts +149 -17
- package/src/bun/core/BrowserView.ts +5 -1
- package/src/bun/proc/zig.ts +1 -0
- package/src/extractor/zig-out/bin/extractor +0 -0
- package/src/launcher/zig-out/bin/launcher +0 -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;
|
|
@@ -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);
|
|
@@ -220,19 +244,45 @@ const ConfigureWebviewTags = (
|
|
|
220
244
|
return;
|
|
221
245
|
}
|
|
222
246
|
|
|
247
|
+
const masks: Rect[] = [];
|
|
248
|
+
this.maskSelectors.forEach((selector) => {
|
|
249
|
+
const els = document.querySelectorAll(selector);
|
|
250
|
+
|
|
251
|
+
for (let i = 0; i < els.length; i++) {
|
|
252
|
+
const el = els[i];
|
|
253
|
+
|
|
254
|
+
if (el) {
|
|
255
|
+
const maskRect = el.getBoundingClientRect();
|
|
256
|
+
|
|
257
|
+
masks.push({
|
|
258
|
+
// reposition the bounding rect to be relative to the webview rect
|
|
259
|
+
// so objc can apply the mask correctly and handle the actual overlap
|
|
260
|
+
x: maskRect.x - x,
|
|
261
|
+
y: maskRect.y - y,
|
|
262
|
+
width: maskRect.width,
|
|
263
|
+
height: maskRect.height,
|
|
264
|
+
});
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
});
|
|
268
|
+
|
|
269
|
+
// store jsonStringified last masks value to compare
|
|
270
|
+
const masksJson = masks.length ? JSON.stringify(masks) : "";
|
|
271
|
+
|
|
223
272
|
if (
|
|
224
273
|
force ||
|
|
225
274
|
lastRect.x !== x ||
|
|
226
275
|
lastRect.y !== y ||
|
|
227
276
|
lastRect.width !== width ||
|
|
228
|
-
lastRect.height !== height
|
|
277
|
+
lastRect.height !== height ||
|
|
278
|
+
this.lastMasksJSON !== masksJson
|
|
229
279
|
) {
|
|
230
|
-
//
|
|
231
|
-
|
|
232
|
-
this.setPositionCheckLoop(true);
|
|
233
|
-
}
|
|
280
|
+
// let it know we're still accelerating
|
|
281
|
+
this.setPositionCheckLoop(true);
|
|
234
282
|
|
|
235
283
|
this.lastRect = rect;
|
|
284
|
+
this.lastMasks = masks;
|
|
285
|
+
this.lastMasksJSON = masksJson;
|
|
236
286
|
|
|
237
287
|
this.zigRpc.send.webviewTagResize({
|
|
238
288
|
id: this.webviewId,
|
|
@@ -242,6 +292,7 @@ const ConfigureWebviewTags = (
|
|
|
242
292
|
x: x,
|
|
243
293
|
y: y,
|
|
244
294
|
},
|
|
295
|
+
masks: masksJson,
|
|
245
296
|
});
|
|
246
297
|
}
|
|
247
298
|
|
|
@@ -265,13 +316,12 @@ const ConfigureWebviewTags = (
|
|
|
265
316
|
this.positionCheckLoopReset = undefined;
|
|
266
317
|
}
|
|
267
318
|
|
|
268
|
-
const delay = accelerate ?
|
|
319
|
+
const delay = accelerate ? 0 : 300;
|
|
269
320
|
|
|
270
321
|
if (accelerate) {
|
|
271
|
-
clearTimeout(this.positionCheckLoopReset);
|
|
272
322
|
this.positionCheckLoopReset = setTimeout(() => {
|
|
273
323
|
this.setPositionCheckLoop(false);
|
|
274
|
-
},
|
|
324
|
+
}, 2000);
|
|
275
325
|
}
|
|
276
326
|
// Note: Since there's not catch all way to listen for x/y changes
|
|
277
327
|
// we have a 400ms interval to check
|
|
@@ -288,6 +338,59 @@ const ConfigureWebviewTags = (
|
|
|
288
338
|
this.positionCheckLoop = setInterval(() => this.syncDimensions(), delay);
|
|
289
339
|
}
|
|
290
340
|
|
|
341
|
+
// The global document mousemove will fire even when the mouse is over
|
|
342
|
+
// an OOPIF that's layered above this host webview. The two edge cases we
|
|
343
|
+
// solve for are:
|
|
344
|
+
// 1. dragging an element on the host over or dropping on the webview anchor
|
|
345
|
+
// 2. clicking on an element that's "layered over" the OOPIF visually but really uses a
|
|
346
|
+
// mask to make a section transparent. We want the underlying overlay UI to be
|
|
347
|
+
// interactive not the OOPIF.
|
|
348
|
+
//
|
|
349
|
+
// Solution: Have mirroing on by default.
|
|
350
|
+
// 1. mouse move events don't fire during drag. So the OOPIF remains non-interactive
|
|
351
|
+
// and effectively passes through to the host's anchor element underneath letting you
|
|
352
|
+
// react to drag events on the host as needed.
|
|
353
|
+
// 2. Detect when the mouse is moving over the anchor and turn mirroring off to make
|
|
354
|
+
// it interactive. Unless the mouse is over a masked area in which case we want to
|
|
355
|
+
// keep it non-interactive and pass through to the host "overylay UI".
|
|
356
|
+
handleDocumentMouseMove(e: MouseEvent) {
|
|
357
|
+
if (this.hidden) {
|
|
358
|
+
return;
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
const isInBounds =
|
|
362
|
+
e.clientX >= this.lastRect.x &&
|
|
363
|
+
e.clientX <= this.lastRect.x + this.lastRect.width &&
|
|
364
|
+
e.clientY >= this.lastRect.y &&
|
|
365
|
+
e.clientY <= this.lastRect.y + this.lastRect.height;
|
|
366
|
+
|
|
367
|
+
if (isInBounds) {
|
|
368
|
+
const isInMaskBounds = this.lastMasks.find((mask) => {
|
|
369
|
+
// we send relative x/y to objc but here we need the clientX/Y
|
|
370
|
+
// to compare against. consider doing the opposite or storing both.
|
|
371
|
+
const clientX = this.lastRect.x + mask.x;
|
|
372
|
+
const clientY = this.lastRect.y + mask.y;
|
|
373
|
+
const isInMaskBounds =
|
|
374
|
+
e.clientX >= clientX &&
|
|
375
|
+
e.clientX <= clientX + mask.width &&
|
|
376
|
+
e.clientY <= clientY + mask.height &&
|
|
377
|
+
e.clientY >= clientY;
|
|
378
|
+
|
|
379
|
+
return isInMaskBounds;
|
|
380
|
+
});
|
|
381
|
+
if (isInMaskBounds) {
|
|
382
|
+
this.startMirroring();
|
|
383
|
+
} else {
|
|
384
|
+
this.stopMirroring();
|
|
385
|
+
}
|
|
386
|
+
} else {
|
|
387
|
+
this.startMirroring();
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
boundhandleDocumentMouseMove = (e: MouseEvent) =>
|
|
392
|
+
this.handleDocumentMouseMove(e);
|
|
393
|
+
|
|
291
394
|
connectedCallback() {
|
|
292
395
|
this.setPositionCheckLoop();
|
|
293
396
|
|
|
@@ -300,8 +403,14 @@ const ConfigureWebviewTags = (
|
|
|
300
403
|
// we still need to send it to objc to calculate from its bottom left position
|
|
301
404
|
// otherwise it'll move around unexpectedly.
|
|
302
405
|
window.addEventListener("resize", this.boundForceSyncDimensions);
|
|
303
|
-
|
|
304
406
|
window.addEventListener("scroll", this.boundSyncDimensions);
|
|
407
|
+
// Note: mousemove won't fire during a drag so we get that behaviour
|
|
408
|
+
// for free without doing calculations.
|
|
409
|
+
document.addEventListener(
|
|
410
|
+
"mousemove",
|
|
411
|
+
this.boundhandleDocumentMouseMove,
|
|
412
|
+
true
|
|
413
|
+
);
|
|
305
414
|
|
|
306
415
|
// todo: For chromium webviews (windows native or chromium bundled)
|
|
307
416
|
// should be able to use performanceObservers on layout-shift to
|
|
@@ -318,6 +427,10 @@ const ConfigureWebviewTags = (
|
|
|
318
427
|
// this.mutationObserver?.disconnect();
|
|
319
428
|
window.removeEventListener("resize", this.boundForceSyncDimensions);
|
|
320
429
|
window.removeEventListener("scroll", this.boundSyncDimensions);
|
|
430
|
+
document.removeEventListener(
|
|
431
|
+
"mousemove",
|
|
432
|
+
this.boundhandleDocumentMouseMove
|
|
433
|
+
);
|
|
321
434
|
this.zigRpc.send.webviewTagRemove({ id: this.webviewId });
|
|
322
435
|
}
|
|
323
436
|
|
|
@@ -407,9 +520,8 @@ const ConfigureWebviewTags = (
|
|
|
407
520
|
DEFAULT_FRAME_RATE = Math.round(1000 / 30); // 30fps
|
|
408
521
|
streamScreenInterval?: Timer;
|
|
409
522
|
|
|
410
|
-
//
|
|
411
|
-
|
|
412
|
-
startMirroring(frameRate: number = this.DEFAULT_FRAME_RATE) {
|
|
523
|
+
// NOTE: This is very cpu intensive, Prefer startMirroring where possible
|
|
524
|
+
startMirroringToDom(frameRate: number = this.DEFAULT_FRAME_RATE) {
|
|
413
525
|
if (this.streamScreenInterval) {
|
|
414
526
|
clearInterval(this.streamScreenInterval);
|
|
415
527
|
}
|
|
@@ -419,13 +531,33 @@ const ConfigureWebviewTags = (
|
|
|
419
531
|
}, frameRate);
|
|
420
532
|
}
|
|
421
533
|
|
|
422
|
-
|
|
534
|
+
stopMirroringToDom() {
|
|
423
535
|
if (this.streamScreenInterval) {
|
|
424
536
|
clearInterval(this.streamScreenInterval);
|
|
425
537
|
this.streamScreenInterval = undefined;
|
|
426
538
|
}
|
|
427
539
|
}
|
|
428
540
|
|
|
541
|
+
startMirroring() {
|
|
542
|
+
if (this.isMirroring === false) {
|
|
543
|
+
this.isMirroring = true;
|
|
544
|
+
this.zigRpc.send.webviewTagToggleMirroring({
|
|
545
|
+
id: this.webviewId,
|
|
546
|
+
enable: true,
|
|
547
|
+
});
|
|
548
|
+
}
|
|
549
|
+
}
|
|
550
|
+
|
|
551
|
+
stopMirroring() {
|
|
552
|
+
if (this.isMirroring === true) {
|
|
553
|
+
this.isMirroring = false;
|
|
554
|
+
this.zigRpc.send.webviewTagToggleMirroring({
|
|
555
|
+
id: this.webviewId,
|
|
556
|
+
enable: false,
|
|
557
|
+
});
|
|
558
|
+
}
|
|
559
|
+
}
|
|
560
|
+
|
|
429
561
|
clearScreenImage() {
|
|
430
562
|
this.style.backgroundImage = "";
|
|
431
563
|
}
|
|
@@ -503,11 +635,11 @@ const ConfigureWebviewTags = (
|
|
|
503
635
|
this.syncScreenshot(() => {
|
|
504
636
|
this.delegateMode = true;
|
|
505
637
|
this.toggleTransparent(true, true);
|
|
506
|
-
this.
|
|
638
|
+
this.startMirroringToDom();
|
|
507
639
|
});
|
|
508
640
|
} else {
|
|
509
641
|
this.delegateMode = false;
|
|
510
|
-
this.
|
|
642
|
+
this.stopMirroringToDom();
|
|
511
643
|
this.toggleTransparent(this.transparent);
|
|
512
644
|
this.tryClearScreenImage();
|
|
513
645
|
}
|
|
@@ -527,10 +659,10 @@ const ConfigureWebviewTags = (
|
|
|
527
659
|
this.hiddenMirrorMode = true;
|
|
528
660
|
this.toggleHidden(true, true);
|
|
529
661
|
this.togglePassthrough(true, true);
|
|
530
|
-
this.
|
|
662
|
+
this.startMirroringToDom();
|
|
531
663
|
});
|
|
532
664
|
} else {
|
|
533
|
-
this.
|
|
665
|
+
this.stopMirroringToDom();
|
|
534
666
|
this.toggleHidden(this.hidden);
|
|
535
667
|
this.togglePassthrough(this.passthroughEnabled);
|
|
536
668
|
this.tryClearScreenImage();
|
|
@@ -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();
|
package/src/bun/proc/zig.ts
CHANGED
|
Binary file
|
|
Binary file
|