electrobun 1.0.3-beta.2 → 1.0.4-beta.0
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/bun.lock +12 -8
- package/dist/api/browser/global.d.ts +31 -0
- package/dist/api/browser/index.ts +17 -11
- package/dist/api/browser/stylesAndElements.ts +1 -1
- package/dist/api/browser/webviewtag.ts +73 -8
- package/dist/api/bun/core/ApplicationMenu.ts +29 -14
- package/dist/api/bun/core/BrowserView.ts +43 -38
- package/dist/api/bun/core/BrowserWindow.ts +13 -9
- package/dist/api/bun/core/ContextMenu.ts +28 -13
- package/dist/api/bun/core/Socket.ts +1 -1
- package/dist/api/bun/core/Tray.ts +19 -16
- package/dist/api/bun/core/Updater.ts +11 -76
- package/dist/api/bun/events/ApplicationEvents.ts +9 -6
- package/dist/api/bun/events/trayEvents.ts +4 -2
- package/dist/api/bun/events/webviewEvents.ts +33 -30
- package/dist/api/bun/events/windowEvents.ts +10 -9
- package/dist/api/bun/proc/native.ts +100 -104
- package/dist/main.js +7 -7
- package/package.json +4 -2
- package/src/cli/index.ts +69 -45
package/bun.lock
CHANGED
|
@@ -15,6 +15,8 @@
|
|
|
15
15
|
"devDependencies": {
|
|
16
16
|
"@types/archiver": "^6.0.3",
|
|
17
17
|
"@types/bun": "1.1.9",
|
|
18
|
+
"@types/tar": "^6.1.13",
|
|
19
|
+
"typescript": "^5.9.3",
|
|
18
20
|
},
|
|
19
21
|
},
|
|
20
22
|
},
|
|
@@ -39,10 +41,12 @@
|
|
|
39
41
|
|
|
40
42
|
"@types/har-format": ["@types/har-format@1.2.16", "", {}, "sha512-fluxdy7ryD3MV6h8pTfTYpy/xQzCFC7m89nOH9y94cNqJ1mDIDPut7MnRHI3F6qRmh/cT2fUjG1MLdCNb4hE9A=="],
|
|
41
43
|
|
|
42
|
-
"@types/node": ["@types/node@
|
|
44
|
+
"@types/node": ["@types/node@20.12.14", "", { "dependencies": { "undici-types": "~5.26.4" } }, "sha512-scnD59RpYD91xngrQQLGkE+6UrHUPzeKZWhhjBSa3HSkwjbQc38+q3RoIVEwxQGRw3M+j5hpNAM+lgV3cVormg=="],
|
|
43
45
|
|
|
44
46
|
"@types/readdir-glob": ["@types/readdir-glob@1.1.5", "", { "dependencies": { "@types/node": "*" } }, "sha512-raiuEPUYqXu+nvtY2Pe8s8FEmZ3x5yAH4VkLdihcPdalvsHltomrRC9BzuStrJ9yk06470hS0Crw0f1pXqD+Hg=="],
|
|
45
47
|
|
|
48
|
+
"@types/tar": ["@types/tar@6.1.13", "", { "dependencies": { "@types/node": "*", "minipass": "^4.0.0" } }, "sha512-IznnlmU5f4WcGTh2ltRu/Ijpmk8wiWXfF0VA4s+HPjHZgvFggk1YaIkbo5krX/zUCzWF8N/l4+W/LNxnvAJ8nw=="],
|
|
49
|
+
|
|
46
50
|
"@types/webextension-polyfill": ["@types/webextension-polyfill@0.12.3", "", {}, "sha512-F58aDVSeN/MjUGazXo/cPsmR76EvqQhQ1v4x23hFjUX0cfAJYE+JBWwiOGW36/VJGGxoH74sVlRIF3z7SJCKyg=="],
|
|
47
51
|
|
|
48
52
|
"@types/ws": ["@types/ws@8.5.14", "", { "dependencies": { "@types/node": "*" } }, "sha512-bd/YFLW+URhBzMXurx7lWByOu+xzU9+kb3RboOteXYDfW+tr+JZa99OyNmPINEGB/ahzKrEuc8rcv4gnpJmxTw=="],
|
|
@@ -143,7 +147,7 @@
|
|
|
143
147
|
|
|
144
148
|
"minimist": ["minimist@1.2.8", "", {}, "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA=="],
|
|
145
149
|
|
|
146
|
-
"minipass": ["minipass@
|
|
150
|
+
"minipass": ["minipass@4.2.8", "", {}, "sha512-fNzuVyifolSLFL4NzpF+wEF4qrgqaaKX0haXPQEdQ7NKAN+WecoKMHV09YcuL/DHxrUsYQOK3MiuDf7Ip2OXfQ=="],
|
|
147
151
|
|
|
148
152
|
"minizlib": ["minizlib@2.1.2", "", { "dependencies": { "minipass": "^3.0.0", "yallist": "^4.0.0" } }, "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg=="],
|
|
149
153
|
|
|
@@ -199,6 +203,8 @@
|
|
|
199
203
|
|
|
200
204
|
"text-decoder": ["text-decoder@1.2.3", "", { "dependencies": { "b4a": "^1.6.4" } }, "sha512-3/o9z3X0X0fTupwsYvR03pJ/DjWuqqrfwBgTQzdWDiQSm9KitAyz/9WqsT2JQW7KV2m+bC2ol/zqpW37NHxLaA=="],
|
|
201
205
|
|
|
206
|
+
"typescript": ["typescript@5.9.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw=="],
|
|
207
|
+
|
|
202
208
|
"undici-types": ["undici-types@5.26.5", "", {}, "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA=="],
|
|
203
209
|
|
|
204
210
|
"util-deprecate": ["util-deprecate@1.0.2", "", {}, "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw=="],
|
|
@@ -213,12 +219,6 @@
|
|
|
213
219
|
|
|
214
220
|
"zip-stream": ["zip-stream@6.0.1", "", { "dependencies": { "archiver-utils": "^5.0.0", "compress-commons": "^6.0.2", "readable-stream": "^4.0.0" } }, "sha512-zK7YHHz4ZXpW89AHXUPbQVGKI7uvkd3hzusTdotCg1UxyaVtg0zFJSTfW/Dq5f7OBBVnq6cZIaC8Ti4hb6dtCA=="],
|
|
215
221
|
|
|
216
|
-
"@types/readdir-glob/@types/node": ["@types/node@20.12.14", "", { "dependencies": { "undici-types": "~5.26.4" } }, "sha512-scnD59RpYD91xngrQQLGkE+6UrHUPzeKZWhhjBSa3HSkwjbQc38+q3RoIVEwxQGRw3M+j5hpNAM+lgV3cVormg=="],
|
|
217
|
-
|
|
218
|
-
"@types/ws/@types/node": ["@types/node@20.12.14", "", { "dependencies": { "undici-types": "~5.26.4" } }, "sha512-scnD59RpYD91xngrQQLGkE+6UrHUPzeKZWhhjBSa3HSkwjbQc38+q3RoIVEwxQGRw3M+j5hpNAM+lgV3cVormg=="],
|
|
219
|
-
|
|
220
|
-
"bun-types/@types/node": ["@types/node@20.12.14", "", { "dependencies": { "undici-types": "~5.26.4" } }, "sha512-scnD59RpYD91xngrQQLGkE+6UrHUPzeKZWhhjBSa3HSkwjbQc38+q3RoIVEwxQGRw3M+j5hpNAM+lgV3cVormg=="],
|
|
221
|
-
|
|
222
222
|
"fs-minipass/minipass": ["minipass@3.3.6", "", { "dependencies": { "yallist": "^4.0.0" } }, "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw=="],
|
|
223
223
|
|
|
224
224
|
"glob/minimatch": ["minimatch@9.0.5", "", { "dependencies": { "brace-expansion": "^2.0.1" } }, "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow=="],
|
|
@@ -231,12 +231,16 @@
|
|
|
231
231
|
|
|
232
232
|
"path-scurry/minipass": ["minipass@7.1.2", "", {}, "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw=="],
|
|
233
233
|
|
|
234
|
+
"png-to-ico/@types/node": ["@types/node@17.0.45", "", {}, "sha512-w+tIMs3rq2afQdsPJlODhoUEKzFP1ayaoyl1CcnwtIlsVe7K7bA1NGm4s3PraqTLlXnbIN84zuBlxBWo1u9BLw=="],
|
|
235
|
+
|
|
234
236
|
"string-width-cjs/emoji-regex": ["emoji-regex@8.0.0", "", {}, "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="],
|
|
235
237
|
|
|
236
238
|
"string-width-cjs/strip-ansi": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="],
|
|
237
239
|
|
|
238
240
|
"strip-ansi-cjs/ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="],
|
|
239
241
|
|
|
242
|
+
"tar/minipass": ["minipass@5.0.0", "", {}, "sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ=="],
|
|
243
|
+
|
|
240
244
|
"wrap-ansi-cjs/ansi-styles": ["ansi-styles@4.3.0", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="],
|
|
241
245
|
|
|
242
246
|
"wrap-ansi-cjs/string-width": ["string-width@4.2.3", "", { "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" } }, "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g=="],
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
// Global type declarations for Electrobun browser environment
|
|
2
|
+
|
|
3
|
+
interface ElectrobunEncryptResult {
|
|
4
|
+
encryptedData: string;
|
|
5
|
+
iv: string;
|
|
6
|
+
tag: string;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
interface ElectrobunBridge {
|
|
10
|
+
receiveMessageFromBun: (msg: unknown) => void;
|
|
11
|
+
receiveInternalMessageFromBun: (msg: unknown) => void;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
interface MessageHandler {
|
|
15
|
+
postMessage: (msg: string) => void;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
declare global {
|
|
19
|
+
interface Window {
|
|
20
|
+
__electrobunWebviewId: number;
|
|
21
|
+
__electrobunWindowId: number;
|
|
22
|
+
__electrobunRpcSocketPort: number;
|
|
23
|
+
__electrobun?: ElectrobunBridge;
|
|
24
|
+
__electrobun_encrypt: (msg: string) => Promise<ElectrobunEncryptResult>;
|
|
25
|
+
__electrobun_decrypt: (encryptedData: string, iv: string, tag: string) => Promise<string>;
|
|
26
|
+
__electrobunInternalBridge?: MessageHandler;
|
|
27
|
+
__electrobunBunBridge?: MessageHandler;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export {};
|
|
@@ -7,27 +7,32 @@ import {
|
|
|
7
7
|
type RPCTransport,
|
|
8
8
|
createRPC,
|
|
9
9
|
} from "rpc-anywhere";
|
|
10
|
-
import { ConfigureWebviewTags } from "./webviewtag";
|
|
10
|
+
import { ConfigureWebviewTags, type WebviewTagElement, type WebviewEventTypes } from "./webviewtag";
|
|
11
11
|
// todo: should this just be injected as a preload script?
|
|
12
12
|
import { isAppRegionDrag } from "./stylesAndElements";
|
|
13
13
|
import type { BuiltinBunToWebviewSchema, BuiltinWebviewToBunSchema } from "./builtinrpcSchema";
|
|
14
14
|
import type { InternalWebviewHandlers, WebviewTagHandlers } from "./rpc/webview";
|
|
15
|
+
import "./global.d.ts";
|
|
15
16
|
|
|
16
17
|
interface ElectrobunWebviewRPCSChema {
|
|
17
18
|
bun: RPCSchema;
|
|
18
19
|
webview: RPCSchema;
|
|
19
20
|
}
|
|
20
21
|
|
|
22
|
+
interface RPCWithTransport {
|
|
23
|
+
setTransport: (transport: RPCTransport) => void;
|
|
24
|
+
}
|
|
25
|
+
|
|
21
26
|
const WEBVIEW_ID = window.__electrobunWebviewId;
|
|
22
27
|
const WINDOW_ID = window.__electrobunWindowId;
|
|
23
28
|
const RPC_SOCKET_PORT = window.__electrobunRpcSocketPort;
|
|
24
29
|
|
|
25
30
|
|
|
26
|
-
class Electroview<T> {
|
|
31
|
+
class Electroview<T extends RPCWithTransport> {
|
|
27
32
|
bunSocket?: WebSocket;
|
|
28
33
|
// user's custom rpc browser <-> bun
|
|
29
34
|
rpc?: T;
|
|
30
|
-
rpcHandler?: (msg:
|
|
35
|
+
rpcHandler?: (msg: unknown) => void;
|
|
31
36
|
// electrobun rpc browser <-> bun
|
|
32
37
|
internalRpc?: any;
|
|
33
38
|
internalRpcHandler?: (msg: any) => void;
|
|
@@ -106,7 +111,7 @@ class Electroview<T> {
|
|
|
106
111
|
console.error("Socket error:", event);
|
|
107
112
|
});
|
|
108
113
|
|
|
109
|
-
socket.addEventListener("close", (
|
|
114
|
+
socket.addEventListener("close", (_event) => {
|
|
110
115
|
// console.log("Socket closed:", event);
|
|
111
116
|
});
|
|
112
117
|
}
|
|
@@ -123,7 +128,7 @@ class Electroview<T> {
|
|
|
123
128
|
// TODO: implement proper rpc-anywhere style rpc here
|
|
124
129
|
// todo: this is duplicated in webviewtag.ts and should be DRYed up
|
|
125
130
|
isProcessingQueue = false;
|
|
126
|
-
sendToInternalQueue = [];
|
|
131
|
+
sendToInternalQueue: string[] = [];
|
|
127
132
|
sendToBunInternal(message: {}) {
|
|
128
133
|
try {
|
|
129
134
|
const strMessage = JSON.stringify(message);
|
|
@@ -189,10 +194,10 @@ class Electroview<T> {
|
|
|
189
194
|
});
|
|
190
195
|
}
|
|
191
196
|
|
|
192
|
-
createTransport() {
|
|
197
|
+
createTransport(): RPCTransport {
|
|
193
198
|
const that = this;
|
|
194
199
|
return {
|
|
195
|
-
send(message) {
|
|
200
|
+
send(message: unknown) {
|
|
196
201
|
try {
|
|
197
202
|
const messageString = JSON.stringify(message);
|
|
198
203
|
// console.log("sending message bunbridge", messageString);
|
|
@@ -201,7 +206,7 @@ class Electroview<T> {
|
|
|
201
206
|
console.error("bun: failed to serialize message to webview", error);
|
|
202
207
|
}
|
|
203
208
|
},
|
|
204
|
-
registerHandler(handler) {
|
|
209
|
+
registerHandler(handler: (msg: unknown) => void) {
|
|
205
210
|
that.rpcHandler = handler;
|
|
206
211
|
},
|
|
207
212
|
};
|
|
@@ -270,7 +275,7 @@ class Electroview<T> {
|
|
|
270
275
|
}
|
|
271
276
|
}
|
|
272
277
|
|
|
273
|
-
receiveMessageFromBun(msg) {
|
|
278
|
+
receiveMessageFromBun(msg: unknown) {
|
|
274
279
|
// NOTE: in the webview messages are passed by executing ElectrobunView.receiveMessageFromBun(object)
|
|
275
280
|
// so they're already parsed into an object here
|
|
276
281
|
if (this.rpcHandler) {
|
|
@@ -380,7 +385,8 @@ class Electroview<T> {
|
|
|
380
385
|
// while types in here are borked, they resolve correctly/bubble up to the defineRPC call site.
|
|
381
386
|
rpc.addMessageListener(
|
|
382
387
|
"*",
|
|
383
|
-
|
|
388
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
389
|
+
(messageName: keyof WebviewSchema["messages"], payload: any) => {
|
|
384
390
|
const globalHandler = messageHandlers["*"];
|
|
385
391
|
if (globalHandler) {
|
|
386
392
|
globalHandler(messageName, payload);
|
|
@@ -398,7 +404,7 @@ class Electroview<T> {
|
|
|
398
404
|
}
|
|
399
405
|
}
|
|
400
406
|
|
|
401
|
-
export { type RPCSchema, createRPC, Electroview };
|
|
407
|
+
export { type RPCSchema, createRPC, Electroview, type WebviewTagElement, type WebviewEventTypes };
|
|
402
408
|
|
|
403
409
|
const Electrobun = {
|
|
404
410
|
Electroview,
|
|
@@ -1,16 +1,81 @@
|
|
|
1
|
+
import "./global.d.ts";
|
|
2
|
+
|
|
1
3
|
type WebviewEventTypes =
|
|
2
4
|
| "did-navigate"
|
|
3
5
|
| "did-navigate-in-page"
|
|
4
6
|
| "did-commit-navigation"
|
|
5
7
|
| "dom-ready"
|
|
6
|
-
| "host-message"
|
|
8
|
+
| "host-message"
|
|
9
|
+
| "new-window-open";
|
|
7
10
|
|
|
8
11
|
type Rect = { x: number; y: number; width: number; height: number };
|
|
9
12
|
|
|
13
|
+
/**
|
|
14
|
+
* Interface representing an <electrobun-webview> custom element.
|
|
15
|
+
* Use this to properly type webview elements obtained via querySelector.
|
|
16
|
+
*
|
|
17
|
+
* @example
|
|
18
|
+
* const webview = document.querySelector('electrobun-webview') as WebviewTagElement;
|
|
19
|
+
* webview.loadURL('https://example.com');
|
|
20
|
+
* webview.toggleHidden(false);
|
|
21
|
+
*/
|
|
22
|
+
interface WebviewTagElement extends HTMLElement {
|
|
23
|
+
// Properties
|
|
24
|
+
webviewId?: number;
|
|
25
|
+
maskSelectors: Set<string>;
|
|
26
|
+
transparent: boolean;
|
|
27
|
+
passthroughEnabled: boolean;
|
|
28
|
+
hidden: boolean;
|
|
29
|
+
hiddenMirrorMode: boolean;
|
|
30
|
+
partition: string | null;
|
|
31
|
+
|
|
32
|
+
// Attribute-backed properties (getters/setters)
|
|
33
|
+
src: string | null;
|
|
34
|
+
html: string | null;
|
|
35
|
+
preload: string | null;
|
|
36
|
+
renderer: 'cef' | 'native';
|
|
37
|
+
|
|
38
|
+
// Mask management
|
|
39
|
+
addMaskSelector(selector: string): void;
|
|
40
|
+
removeMaskSelector(selector: string): void;
|
|
41
|
+
|
|
42
|
+
// Navigation
|
|
43
|
+
canGoBack(): Promise<boolean>;
|
|
44
|
+
canGoForward(): Promise<boolean>;
|
|
45
|
+
goBack(): void;
|
|
46
|
+
goForward(): void;
|
|
47
|
+
reload(): void;
|
|
48
|
+
loadURL(url: string): void;
|
|
49
|
+
loadHTML(html: string): void;
|
|
50
|
+
|
|
51
|
+
// JavaScript execution
|
|
52
|
+
callAsyncJavaScript(options: { script: string }): Promise<unknown>;
|
|
53
|
+
|
|
54
|
+
// Visibility and interaction
|
|
55
|
+
toggleTransparent(transparent?: boolean, bypassState?: boolean): void;
|
|
56
|
+
togglePassthrough(enablePassthrough?: boolean, bypassState?: boolean): void;
|
|
57
|
+
toggleHidden(hidden?: boolean, bypassState?: boolean): void;
|
|
58
|
+
|
|
59
|
+
// Events - listener receives a CustomEvent with detail property
|
|
60
|
+
on(event: WebviewEventTypes, listener: (event: CustomEvent) => void): void;
|
|
61
|
+
off(event: WebviewEventTypes, listener: (event: CustomEvent) => void): void;
|
|
62
|
+
emit(event: WebviewEventTypes, detail: unknown): void;
|
|
63
|
+
|
|
64
|
+
// Dimension sync
|
|
65
|
+
syncDimensions(force?: boolean): void;
|
|
66
|
+
|
|
67
|
+
// Navigation rules
|
|
68
|
+
setNavigationRules(rules: string[]): void;
|
|
69
|
+
|
|
70
|
+
// Find in page
|
|
71
|
+
findInPage(searchText: string, options?: { forward?: boolean; matchCase?: boolean }): void;
|
|
72
|
+
stopFindInPage(): void;
|
|
73
|
+
}
|
|
74
|
+
|
|
10
75
|
const ConfigureWebviewTags = (
|
|
11
76
|
enableWebviewTags: boolean,
|
|
12
|
-
internalRpc:
|
|
13
|
-
bunRpc:
|
|
77
|
+
internalRpc: unknown,
|
|
78
|
+
bunRpc: unknown
|
|
14
79
|
) => {
|
|
15
80
|
if (!enableWebviewTags) {
|
|
16
81
|
return;
|
|
@@ -398,12 +463,12 @@ const ConfigureWebviewTags = (
|
|
|
398
463
|
return ["src", "html", "preload", "class", "style"];
|
|
399
464
|
}
|
|
400
465
|
|
|
401
|
-
attributeChangedCallback(name, oldValue, newValue) {
|
|
402
|
-
if (name === "src" && oldValue !== newValue) {
|
|
466
|
+
attributeChangedCallback(name: string, oldValue: string | null, newValue: string | null) {
|
|
467
|
+
if (name === "src" && oldValue !== newValue && newValue !== null) {
|
|
403
468
|
this.updateIFrameSrc(newValue);
|
|
404
|
-
} else if (name === "html" && oldValue !== newValue) {
|
|
469
|
+
} else if (name === "html" && oldValue !== newValue && newValue !== null) {
|
|
405
470
|
this.updateIFrameHtml(newValue);
|
|
406
|
-
} else if (name === "preload" && oldValue !== newValue) {
|
|
471
|
+
} else if (name === "preload" && oldValue !== newValue && newValue !== null) {
|
|
407
472
|
this.updateIFramePreload(newValue);
|
|
408
473
|
} else {
|
|
409
474
|
this.syncDimensions();
|
|
@@ -638,4 +703,4 @@ electrobun-webview {
|
|
|
638
703
|
}
|
|
639
704
|
};
|
|
640
705
|
|
|
641
|
-
export { ConfigureWebviewTags };
|
|
706
|
+
export { ConfigureWebviewTags, type WebviewTagElement, type WebviewEventTypes };
|
|
@@ -1,6 +1,20 @@
|
|
|
1
1
|
import { ffi, type ApplicationMenuItemConfig } from "../proc/native";
|
|
2
2
|
import electrobunEventEmitter from "../events/eventEmitter";
|
|
3
3
|
|
|
4
|
+
type NonDividerMenuItem = {
|
|
5
|
+
type?: "normal";
|
|
6
|
+
label?: string;
|
|
7
|
+
tooltip?: string;
|
|
8
|
+
action?: string;
|
|
9
|
+
role?: string;
|
|
10
|
+
data?: unknown;
|
|
11
|
+
submenu?: Array<ApplicationMenuItemConfig>;
|
|
12
|
+
enabled?: boolean;
|
|
13
|
+
checked?: boolean;
|
|
14
|
+
hidden?: boolean;
|
|
15
|
+
accelerator?: string;
|
|
16
|
+
};
|
|
17
|
+
|
|
4
18
|
export const setApplicationMenu = (menu: Array<ApplicationMenuItemConfig>) => {
|
|
5
19
|
const menuWithDefaults = menuConfigWithDefaults(menu);
|
|
6
20
|
ffi.request.setApplicationMenu({
|
|
@@ -8,7 +22,7 @@ export const setApplicationMenu = (menu: Array<ApplicationMenuItemConfig>) => {
|
|
|
8
22
|
});
|
|
9
23
|
};
|
|
10
24
|
|
|
11
|
-
export const on = (name: "application-menu-clicked", handler) => {
|
|
25
|
+
export const on = (name: "application-menu-clicked", handler: (event: unknown) => void) => {
|
|
12
26
|
const specificName = `${name}`;
|
|
13
27
|
electrobunEventEmitter.on(specificName, handler);
|
|
14
28
|
};
|
|
@@ -44,24 +58,25 @@ const menuConfigWithDefaults = (
|
|
|
44
58
|
): Array<ApplicationMenuItemConfig> => {
|
|
45
59
|
return menu.map((item) => {
|
|
46
60
|
if (item.type === "divider" || item.type === "separator") {
|
|
47
|
-
return { type: "divider" };
|
|
61
|
+
return { type: "divider" } as const;
|
|
48
62
|
} else {
|
|
63
|
+
const menuItem = item as NonDividerMenuItem;
|
|
49
64
|
// Use shared serialization method
|
|
50
|
-
const actionWithDataId = ffi.internal.serializeMenuAction(
|
|
51
|
-
|
|
65
|
+
const actionWithDataId = ffi.internal.serializeMenuAction(menuItem.action || "", menuItem.data);
|
|
66
|
+
|
|
52
67
|
return {
|
|
53
|
-
label:
|
|
54
|
-
type:
|
|
68
|
+
label: menuItem.label || roleLabelMap[menuItem.role as keyof typeof roleLabelMap] || "",
|
|
69
|
+
type: menuItem.type || "normal",
|
|
55
70
|
// application menus can either have an action or a role. not both.
|
|
56
|
-
...(
|
|
71
|
+
...(menuItem.role ? { role: menuItem.role } : { action: actionWithDataId }),
|
|
57
72
|
// default enabled to true unless explicitly set to false
|
|
58
|
-
enabled:
|
|
59
|
-
checked: Boolean(
|
|
60
|
-
hidden: Boolean(
|
|
61
|
-
tooltip:
|
|
62
|
-
accelerator:
|
|
63
|
-
...(
|
|
64
|
-
? { submenu: menuConfigWithDefaults(
|
|
73
|
+
enabled: menuItem.enabled === false ? false : true,
|
|
74
|
+
checked: Boolean(menuItem.checked),
|
|
75
|
+
hidden: Boolean(menuItem.hidden),
|
|
76
|
+
tooltip: menuItem.tooltip || undefined,
|
|
77
|
+
accelerator: menuItem.accelerator || undefined,
|
|
78
|
+
...(menuItem.submenu
|
|
79
|
+
? { submenu: menuConfigWithDefaults(menuItem.submenu) }
|
|
65
80
|
: {}),
|
|
66
81
|
};
|
|
67
82
|
}
|
|
@@ -1,6 +1,5 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { native, toCString, ffi } from "../proc/native";
|
|
2
2
|
import * as fs from "fs";
|
|
3
|
-
import { execSync } from "child_process";
|
|
4
3
|
import electrobunEventEmitter from "../events/eventEmitter";
|
|
5
4
|
import {
|
|
6
5
|
type RPCSchema,
|
|
@@ -12,18 +11,15 @@ import {
|
|
|
12
11
|
} from "rpc-anywhere";
|
|
13
12
|
import { Updater } from "./Updater";
|
|
14
13
|
import { BuildConfig } from "./BuildConfig";
|
|
15
|
-
import type { BuiltinBunToWebviewSchema,BuiltinWebviewToBunSchema } from "../../browser/builtinrpcSchema";
|
|
16
14
|
import { rpcPort, sendMessageToWebviewViaSocket } from "./Socket";
|
|
17
15
|
import { randomBytes } from "crypto";
|
|
18
|
-
import {
|
|
16
|
+
import { type Pointer } from 'bun:ffi';
|
|
19
17
|
|
|
20
18
|
const BrowserViewMap: {
|
|
21
19
|
[id: number]: BrowserView<any>;
|
|
22
20
|
} = {};
|
|
23
21
|
let nextWebviewId = 1;
|
|
24
22
|
|
|
25
|
-
const CHUNK_SIZE = 1024 * 4; // 4KB
|
|
26
|
-
|
|
27
23
|
type BrowserViewOptions<T = undefined> = {
|
|
28
24
|
url: string | null;
|
|
29
25
|
html: string | null;
|
|
@@ -50,6 +46,10 @@ interface ElectrobunWebviewRPCSChema {
|
|
|
50
46
|
webview: RPCSchema;
|
|
51
47
|
}
|
|
52
48
|
|
|
49
|
+
interface RPCWithTransport {
|
|
50
|
+
setTransport: (transport: { send: (msg: unknown) => void; registerHandler: (handler: (msg: unknown) => void) => void }) => void;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
53
|
const hash = await Updater.localInfo.hash();
|
|
54
54
|
const buildConfig = await BuildConfig.get();
|
|
55
55
|
|
|
@@ -69,12 +69,12 @@ const defaultOptions: Partial<BrowserViewOptions> = {
|
|
|
69
69
|
// but we also want a randomId to separate different instances of the same app
|
|
70
70
|
const randomId = Math.random().toString(36).substring(7);
|
|
71
71
|
|
|
72
|
-
export class BrowserView<T> {
|
|
72
|
+
export class BrowserView<T extends RPCWithTransport = RPCWithTransport> {
|
|
73
73
|
id: number = nextWebviewId++;
|
|
74
|
-
ptr
|
|
74
|
+
ptr!: Pointer;
|
|
75
75
|
hostWebviewId?: number;
|
|
76
|
-
windowId
|
|
77
|
-
renderer
|
|
76
|
+
windowId!: number;
|
|
77
|
+
renderer!: 'cef' | 'native';
|
|
78
78
|
url: string | null = null;
|
|
79
79
|
html: string | null = null;
|
|
80
80
|
preload: string | null = null;
|
|
@@ -91,37 +91,40 @@ export class BrowserView<T> {
|
|
|
91
91
|
width: 800,
|
|
92
92
|
height: 600,
|
|
93
93
|
};
|
|
94
|
-
pipePrefix
|
|
95
|
-
inStream
|
|
96
|
-
outStream
|
|
97
|
-
secretKey
|
|
98
|
-
rpc?: T;
|
|
99
|
-
rpcHandler?: (msg:
|
|
100
|
-
navigationRules: string | null;
|
|
94
|
+
pipePrefix!: string;
|
|
95
|
+
inStream!: fs.WriteStream;
|
|
96
|
+
outStream!: ReadableStream<Uint8Array>;
|
|
97
|
+
secretKey!: Uint8Array;
|
|
98
|
+
rpc?: T;
|
|
99
|
+
rpcHandler?: (msg: unknown) => void;
|
|
100
|
+
navigationRules: string | null = null;
|
|
101
101
|
|
|
102
102
|
constructor(options: Partial<BrowserViewOptions<T>> = defaultOptions) {
|
|
103
|
-
// const rpc = options.rpc;
|
|
104
|
-
|
|
103
|
+
// const rpc = options.rpc;
|
|
104
|
+
|
|
105
105
|
this.url = options.url || defaultOptions.url || null;
|
|
106
106
|
this.html = options.html || defaultOptions.html || null;
|
|
107
107
|
this.preload = options.preload || defaultOptions.preload || null;
|
|
108
|
-
this.frame =
|
|
109
|
-
|
|
110
|
-
:
|
|
108
|
+
this.frame = {
|
|
109
|
+
x: options.frame?.x ?? defaultOptions.frame!.x,
|
|
110
|
+
y: options.frame?.y ?? defaultOptions.frame!.y,
|
|
111
|
+
width: options.frame?.width ?? defaultOptions.frame!.width,
|
|
112
|
+
height: options.frame?.height ?? defaultOptions.frame!.height,
|
|
113
|
+
};
|
|
111
114
|
this.rpc = options.rpc;
|
|
112
|
-
this.secretKey = new Uint8Array(randomBytes(32));
|
|
115
|
+
this.secretKey = new Uint8Array(randomBytes(32));
|
|
113
116
|
this.partition = options.partition || null;
|
|
114
117
|
// todo (yoav): since collisions can crash the app add a function that checks if the
|
|
115
118
|
// file exists first
|
|
116
119
|
this.pipePrefix = `/private/tmp/electrobun_ipc_pipe_${hash}_${randomId}_${this.id}`;
|
|
117
120
|
this.hostWebviewId = options.hostWebviewId;
|
|
118
|
-
this.windowId = options.windowId;
|
|
121
|
+
this.windowId = options.windowId ?? 0;
|
|
119
122
|
this.autoResize = options.autoResize === false ? false : true;
|
|
120
123
|
this.navigationRules = options.navigationRules || null;
|
|
121
|
-
this.renderer = options.renderer
|
|
124
|
+
this.renderer = options.renderer ?? defaultOptions.renderer ?? 'native';
|
|
122
125
|
|
|
123
126
|
BrowserViewMap[this.id] = this;
|
|
124
|
-
this.ptr = this.init();
|
|
127
|
+
this.ptr = this.init() as Pointer;
|
|
125
128
|
|
|
126
129
|
// If HTML content was provided, load it after webview creation
|
|
127
130
|
if (this.html) {
|
|
@@ -168,18 +171,18 @@ export class BrowserView<T> {
|
|
|
168
171
|
|
|
169
172
|
}
|
|
170
173
|
|
|
171
|
-
createStreams() {
|
|
174
|
+
createStreams() {
|
|
172
175
|
if (!this.rpc) {
|
|
176
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
173
177
|
this.rpc = BrowserView.defineRPC({
|
|
174
178
|
handlers: { requests: {}, messages: {} },
|
|
175
|
-
});
|
|
179
|
+
}) as any;
|
|
176
180
|
}
|
|
177
|
-
|
|
178
|
-
this.rpc
|
|
179
|
-
|
|
181
|
+
|
|
182
|
+
this.rpc!.setTransport(this.createTransport());
|
|
180
183
|
}
|
|
181
184
|
|
|
182
|
-
sendMessageToWebviewViaExecute(jsonMessage) {
|
|
185
|
+
sendMessageToWebviewViaExecute(jsonMessage: unknown) {
|
|
183
186
|
const stringifiedMessage =
|
|
184
187
|
typeof jsonMessage === "string"
|
|
185
188
|
? jsonMessage
|
|
@@ -189,7 +192,7 @@ export class BrowserView<T> {
|
|
|
189
192
|
this.executeJavascript(wrappedMessage);
|
|
190
193
|
}
|
|
191
194
|
|
|
192
|
-
sendInternalMessageViaExecute(jsonMessage) {
|
|
195
|
+
sendInternalMessageViaExecute(jsonMessage: unknown) {
|
|
193
196
|
const stringifiedMessage =
|
|
194
197
|
typeof jsonMessage === "string"
|
|
195
198
|
? jsonMessage
|
|
@@ -258,7 +261,7 @@ export class BrowserView<T> {
|
|
|
258
261
|
| "download-progress"
|
|
259
262
|
| "download-completed"
|
|
260
263
|
| "download-failed",
|
|
261
|
-
handler
|
|
264
|
+
handler: (event: unknown) => void
|
|
262
265
|
) {
|
|
263
266
|
const specificName = `${name}-${this.id}`;
|
|
264
267
|
electrobunEventEmitter.on(specificName, handler);
|
|
@@ -280,8 +283,8 @@ export class BrowserView<T> {
|
|
|
280
283
|
}
|
|
281
284
|
}
|
|
282
285
|
},
|
|
283
|
-
registerHandler(handler) {
|
|
284
|
-
that.rpcHandler = handler;
|
|
286
|
+
registerHandler(handler: (msg: unknown) => void) {
|
|
287
|
+
that.rpcHandler = handler;
|
|
285
288
|
},
|
|
286
289
|
};
|
|
287
290
|
};
|
|
@@ -342,7 +345,8 @@ export class BrowserView<T> {
|
|
|
342
345
|
messages: WebviewSchema["messages"];
|
|
343
346
|
};
|
|
344
347
|
|
|
345
|
-
type mixedBunSchema = {
|
|
348
|
+
type mixedBunSchema = {
|
|
349
|
+
requests: WebviewSchema["requests"];
|
|
346
350
|
messages: BunSchema["messages"];
|
|
347
351
|
};
|
|
348
352
|
|
|
@@ -367,7 +371,8 @@ export class BrowserView<T> {
|
|
|
367
371
|
// while types in here are borked, they resolve correctly/bubble up to the defineRPC call site.
|
|
368
372
|
rpc.addMessageListener(
|
|
369
373
|
"*",
|
|
370
|
-
|
|
374
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
375
|
+
(messageName: keyof BunSchema["messages"], payload: any) => {
|
|
371
376
|
const globalHandler = messageHandlers["*"];
|
|
372
377
|
if (globalHandler) {
|
|
373
378
|
globalHandler(messageName, payload);
|
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
import { ffi } from "../proc/native";
|
|
2
2
|
import electrobunEventEmitter from "../events/eventEmitter";
|
|
3
3
|
import { BrowserView } from "./BrowserView";
|
|
4
|
-
import { type
|
|
5
|
-
import {FFIType} from 'bun:ffi'
|
|
4
|
+
import { type Pointer } from 'bun:ffi';
|
|
6
5
|
import { BuildConfig } from "./BuildConfig";
|
|
7
6
|
|
|
8
7
|
const buildConfig = await BuildConfig.get();
|
|
@@ -50,18 +49,23 @@ const defaultOptions: WindowOptionsType = {
|
|
|
50
49
|
navigationRules: null,
|
|
51
50
|
};
|
|
52
51
|
|
|
53
|
-
export const BrowserWindowMap = {};
|
|
52
|
+
export const BrowserWindowMap: { [id: number]: BrowserWindow<RPCWithTransport> } = {};
|
|
54
53
|
|
|
55
|
-
|
|
54
|
+
interface RPCWithTransport {
|
|
55
|
+
setTransport: (transport: { send: (msg: unknown) => void; registerHandler: (handler: (msg: unknown) => void) => void }) => void;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export class BrowserWindow<T extends RPCWithTransport = RPCWithTransport> {
|
|
56
59
|
id: number = nextWindowId++;
|
|
57
|
-
ptr
|
|
60
|
+
ptr!: Pointer;
|
|
58
61
|
title: string = "Electrobun";
|
|
59
62
|
state: "creating" | "created" = "creating";
|
|
60
63
|
url: string | null = null;
|
|
61
64
|
html: string | null = null;
|
|
62
65
|
preload: string | null = null;
|
|
63
|
-
renderer:
|
|
66
|
+
renderer: 'native' | 'cef' = 'native';
|
|
64
67
|
transparent: boolean = false;
|
|
68
|
+
navigationRules: string | null = null;
|
|
65
69
|
frame: {
|
|
66
70
|
x: number;
|
|
67
71
|
y: number;
|
|
@@ -74,7 +78,7 @@ export class BrowserWindow<T> {
|
|
|
74
78
|
height: 600,
|
|
75
79
|
};
|
|
76
80
|
// todo (yoav): make this an array of ids or something
|
|
77
|
-
webviewId
|
|
81
|
+
webviewId!: number;
|
|
78
82
|
|
|
79
83
|
constructor(options: Partial<WindowOptionsType<T>> = defaultOptions) {
|
|
80
84
|
this.title = options.title || "New Window";
|
|
@@ -139,7 +143,7 @@ export class BrowserWindow<T> {
|
|
|
139
143
|
},
|
|
140
144
|
titleBarStyle: titleBarStyle || "default",
|
|
141
145
|
transparent: transparent ?? false,
|
|
142
|
-
});
|
|
146
|
+
}) as Pointer;
|
|
143
147
|
|
|
144
148
|
BrowserWindowMap[this.id] = this;
|
|
145
149
|
|
|
@@ -280,7 +284,7 @@ export class BrowserWindow<T> {
|
|
|
280
284
|
|
|
281
285
|
// todo (yoav): move this to a class that also has off, append, prepend, etc.
|
|
282
286
|
// name should only allow browserWindow events
|
|
283
|
-
on(name, handler) {
|
|
287
|
+
on(name: string, handler: (event: unknown) => void) {
|
|
284
288
|
const specificName = `${name}-${this.id}`;
|
|
285
289
|
electrobunEventEmitter.on(specificName, handler);
|
|
286
290
|
}
|