electrobun 0.11.0-beta.3 → 0.13.0-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 +25 -1
- package/dist/api/browser/rpc/webview.ts +9 -0
- package/dist/api/browser/webviewtag.ts +28 -0
- package/dist/api/bun/ElectrobunConfig.ts +38 -4
- package/dist/api/bun/core/BrowserView.ts +11 -10
- package/dist/api/bun/core/BrowserWindow.ts +71 -13
- package/dist/api/bun/core/BuildConfig.ts +38 -0
- package/dist/api/bun/core/Socket.ts +1 -1
- package/dist/api/bun/core/Updater.ts +67 -24
- package/dist/api/bun/index.ts +4 -0
- package/dist/api/bun/proc/native.ts +155 -57
- package/dist/main.js +8 -4
- package/package.json +3 -1
- package/src/cli/index.ts +930 -495
package/bun.lock
CHANGED
|
@@ -7,6 +7,8 @@
|
|
|
7
7
|
"dependencies": {
|
|
8
8
|
"@oneidentity/zstd-js": "^1.0.3",
|
|
9
9
|
"archiver": "^7.0.1",
|
|
10
|
+
"png-to-ico": "^2.1.8",
|
|
11
|
+
"rcedit": "^4.0.1",
|
|
10
12
|
"rpc-anywhere": "1.5.0",
|
|
11
13
|
"tar": "^6.2.1",
|
|
12
14
|
},
|
|
@@ -19,6 +21,8 @@
|
|
|
19
21
|
"packages": {
|
|
20
22
|
"@isaacs/cliui": ["@isaacs/cliui@8.0.2", "", { "dependencies": { "string-width": "^5.1.2", "string-width-cjs": "npm:string-width@^4.2.0", "strip-ansi": "^7.0.1", "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", "wrap-ansi": "^8.1.0", "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" } }, "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA=="],
|
|
21
23
|
|
|
24
|
+
"@malept/cross-spawn-promise": ["@malept/cross-spawn-promise@1.1.1", "", { "dependencies": { "cross-spawn": "^7.0.1" } }, "sha512-RTBGWL5FWQcg9orDOCcp4LvItNzUPcyEU9bwaeJX0rJ1IQxzucC48Y0/sQLp/g6t99IQgAlGIaesJS+gTn7tVQ=="],
|
|
25
|
+
|
|
22
26
|
"@oneidentity/zstd-js": ["@oneidentity/zstd-js@1.0.3", "", { "dependencies": { "@types/emscripten": "^1.39.4" } }, "sha512-Jm6sawqxLzBrjC4sg2BeXToa33yPzUmq20CKsehKY2++D/gHb/oSwVjNgT+RH4vys+r8FynrgcNzGwhZWMLzfQ=="],
|
|
23
27
|
|
|
24
28
|
"@pkgjs/parseargs": ["@pkgjs/parseargs@0.11.0", "", {}, "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg=="],
|
|
@@ -35,7 +39,7 @@
|
|
|
35
39
|
|
|
36
40
|
"@types/har-format": ["@types/har-format@1.2.16", "", {}, "sha512-fluxdy7ryD3MV6h8pTfTYpy/xQzCFC7m89nOH9y94cNqJ1mDIDPut7MnRHI3F6qRmh/cT2fUjG1MLdCNb4hE9A=="],
|
|
37
41
|
|
|
38
|
-
"@types/node": ["@types/node@
|
|
42
|
+
"@types/node": ["@types/node@17.0.45", "", {}, "sha512-w+tIMs3rq2afQdsPJlODhoUEKzFP1ayaoyl1CcnwtIlsVe7K7bA1NGm4s3PraqTLlXnbIN84zuBlxBWo1u9BLw=="],
|
|
39
43
|
|
|
40
44
|
"@types/readdir-glob": ["@types/readdir-glob@1.1.5", "", { "dependencies": { "@types/node": "*" } }, "sha512-raiuEPUYqXu+nvtY2Pe8s8FEmZ3x5yAH4VkLdihcPdalvsHltomrRC9BzuStrJ9yk06470hS0Crw0f1pXqD+Hg=="],
|
|
41
45
|
|
|
@@ -89,6 +93,8 @@
|
|
|
89
93
|
|
|
90
94
|
"cross-spawn": ["cross-spawn@7.0.6", "", { "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", "which": "^2.0.1" } }, "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA=="],
|
|
91
95
|
|
|
96
|
+
"cross-spawn-windows-exe": ["cross-spawn-windows-exe@1.2.0", "", { "dependencies": { "@malept/cross-spawn-promise": "^1.1.0", "is-wsl": "^2.2.0", "which": "^2.0.2" } }, "sha512-mkLtJJcYbDCxEG7Js6eUnUNndWjyUZwJ3H7bErmmtOYU/Zb99DyUkpamuIZE0b3bhmJyZ7D90uS6f+CGxRRjOw=="],
|
|
97
|
+
|
|
92
98
|
"eastasianwidth": ["eastasianwidth@0.2.0", "", {}, "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA=="],
|
|
93
99
|
|
|
94
100
|
"emoji-regex": ["emoji-regex@9.2.2", "", {}, "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg=="],
|
|
@@ -113,10 +119,14 @@
|
|
|
113
119
|
|
|
114
120
|
"inherits": ["inherits@2.0.4", "", {}, "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="],
|
|
115
121
|
|
|
122
|
+
"is-docker": ["is-docker@2.2.1", "", { "bin": { "is-docker": "cli.js" } }, "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ=="],
|
|
123
|
+
|
|
116
124
|
"is-fullwidth-code-point": ["is-fullwidth-code-point@3.0.0", "", {}, "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg=="],
|
|
117
125
|
|
|
118
126
|
"is-stream": ["is-stream@2.0.1", "", {}, "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg=="],
|
|
119
127
|
|
|
128
|
+
"is-wsl": ["is-wsl@2.2.0", "", { "dependencies": { "is-docker": "^2.0.0" } }, "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww=="],
|
|
129
|
+
|
|
120
130
|
"isarray": ["isarray@1.0.0", "", {}, "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ=="],
|
|
121
131
|
|
|
122
132
|
"isexe": ["isexe@2.0.0", "", {}, "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw=="],
|
|
@@ -131,6 +141,8 @@
|
|
|
131
141
|
|
|
132
142
|
"minimatch": ["minimatch@5.1.6", "", { "dependencies": { "brace-expansion": "^2.0.1" } }, "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g=="],
|
|
133
143
|
|
|
144
|
+
"minimist": ["minimist@1.2.8", "", {}, "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA=="],
|
|
145
|
+
|
|
134
146
|
"minipass": ["minipass@5.0.0", "", {}, "sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ=="],
|
|
135
147
|
|
|
136
148
|
"minizlib": ["minizlib@2.1.2", "", { "dependencies": { "minipass": "^3.0.0", "yallist": "^4.0.0" } }, "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg=="],
|
|
@@ -145,10 +157,16 @@
|
|
|
145
157
|
|
|
146
158
|
"path-scurry": ["path-scurry@1.11.1", "", { "dependencies": { "lru-cache": "^10.2.0", "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" } }, "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA=="],
|
|
147
159
|
|
|
160
|
+
"png-to-ico": ["png-to-ico@2.1.8", "", { "dependencies": { "@types/node": "^17.0.36", "minimist": "^1.2.6", "pngjs": "^6.0.0" }, "bin": { "png-to-ico": "bin/cli.js" } }, "sha512-Nf+IIn/cZ/DIZVdGveJp86NG5uNib1ZXMiDd/8x32HCTeKSvgpyg6D/6tUBn1QO/zybzoMK0/mc3QRgAyXdv9w=="],
|
|
161
|
+
|
|
162
|
+
"pngjs": ["pngjs@6.0.0", "", {}, "sha512-TRzzuFRRmEoSW/p1KVAmiOgPco2Irlah+bGFCeNfJXxxYGwSw7YwAOAcd7X28K/m5bjBWKsC29KyoMfHbypayg=="],
|
|
163
|
+
|
|
148
164
|
"process": ["process@0.11.10", "", {}, "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A=="],
|
|
149
165
|
|
|
150
166
|
"process-nextick-args": ["process-nextick-args@2.0.1", "", {}, "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag=="],
|
|
151
167
|
|
|
168
|
+
"rcedit": ["rcedit@4.0.1", "", { "dependencies": { "cross-spawn-windows-exe": "^1.1.0" } }, "sha512-bZdaQi34krFWhrDn+O53ccBDw0MkAT2Vhu75SqhtvhQu4OPyFM4RoVheyYiVQYdjhUi6EJMVWQ0tR6bCIYVkUg=="],
|
|
169
|
+
|
|
152
170
|
"readable-stream": ["readable-stream@4.7.0", "", { "dependencies": { "abort-controller": "^3.0.0", "buffer": "^6.0.3", "events": "^3.3.0", "process": "^0.11.10", "string_decoder": "^1.3.0" } }, "sha512-oIGGmcpTLwPga8Bn6/Z75SVaH1z5dUut2ibSyAMVhmUggWpmDn2dapB0n7f8nwaSiRtepAsfJyfXIO5DCVAODg=="],
|
|
153
171
|
|
|
154
172
|
"readdir-glob": ["readdir-glob@1.1.3", "", { "dependencies": { "minimatch": "^5.1.0" } }, "sha512-v05I2k7xN8zXvPD9N+z/uhXPaj0sUFCe2rcWZIpBsqxfP7xXFQ0tipAd/wjj1YxWyWtUS5IDJpOG82JKt2EAVA=="],
|
|
@@ -195,6 +213,12 @@
|
|
|
195
213
|
|
|
196
214
|
"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=="],
|
|
197
215
|
|
|
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
|
+
|
|
198
222
|
"fs-minipass/minipass": ["minipass@3.3.6", "", { "dependencies": { "yallist": "^4.0.0" } }, "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw=="],
|
|
199
223
|
|
|
200
224
|
"glob/minimatch": ["minimatch@9.0.5", "", { "dependencies": { "brace-expansion": "^2.0.1" } }, "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow=="],
|
|
@@ -78,6 +78,15 @@ export type InternalWebviewHandlers = RPCSchema<{
|
|
|
78
78
|
id: number;
|
|
79
79
|
rules: string[];
|
|
80
80
|
};
|
|
81
|
+
webviewTagFindInPage: {
|
|
82
|
+
id: number;
|
|
83
|
+
searchText: string;
|
|
84
|
+
forward: boolean;
|
|
85
|
+
matchCase: boolean;
|
|
86
|
+
};
|
|
87
|
+
webviewTagStopFind: {
|
|
88
|
+
id: number;
|
|
89
|
+
};
|
|
81
90
|
};
|
|
82
91
|
}>;
|
|
83
92
|
|
|
@@ -572,6 +572,34 @@ const ConfigureWebviewTags = (
|
|
|
572
572
|
rules: rules,
|
|
573
573
|
});
|
|
574
574
|
}
|
|
575
|
+
|
|
576
|
+
findInPage(searchText: string, options?: {forward?: boolean; matchCase?: boolean}) {
|
|
577
|
+
if (!this.webviewId) {
|
|
578
|
+
console.warn('findInPage called on removed webview');
|
|
579
|
+
return;
|
|
580
|
+
}
|
|
581
|
+
|
|
582
|
+
const forward = options?.forward ?? true;
|
|
583
|
+
const matchCase = options?.matchCase ?? false;
|
|
584
|
+
|
|
585
|
+
this.internalRpc.send.webviewTagFindInPage({
|
|
586
|
+
id: this.webviewId,
|
|
587
|
+
searchText,
|
|
588
|
+
forward,
|
|
589
|
+
matchCase,
|
|
590
|
+
});
|
|
591
|
+
}
|
|
592
|
+
|
|
593
|
+
stopFindInPage() {
|
|
594
|
+
if (!this.webviewId) {
|
|
595
|
+
console.warn('stopFindInPage called on removed webview');
|
|
596
|
+
return;
|
|
597
|
+
}
|
|
598
|
+
|
|
599
|
+
this.internalRpc.send.webviewTagStopFind({
|
|
600
|
+
id: this.webviewId,
|
|
601
|
+
});
|
|
602
|
+
}
|
|
575
603
|
}
|
|
576
604
|
|
|
577
605
|
customElements.define("electrobun-webview", WebviewTag);
|
|
@@ -129,24 +129,30 @@ export interface ElectrobunConfig {
|
|
|
129
129
|
* @default false
|
|
130
130
|
*/
|
|
131
131
|
codesign?: boolean;
|
|
132
|
-
|
|
132
|
+
|
|
133
133
|
/**
|
|
134
134
|
* Enable notarization for macOS builds (requires codesign)
|
|
135
135
|
* @default false
|
|
136
136
|
*/
|
|
137
137
|
notarize?: boolean;
|
|
138
|
-
|
|
138
|
+
|
|
139
139
|
/**
|
|
140
140
|
* Bundle CEF (Chromium Embedded Framework) instead of using system WebView
|
|
141
141
|
* @default false
|
|
142
142
|
*/
|
|
143
143
|
bundleCEF?: boolean;
|
|
144
|
-
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* Default renderer for webviews when not explicitly specified
|
|
147
|
+
* @default 'native'
|
|
148
|
+
*/
|
|
149
|
+
defaultRenderer?: 'native' | 'cef';
|
|
150
|
+
|
|
145
151
|
/**
|
|
146
152
|
* macOS entitlements for code signing
|
|
147
153
|
*/
|
|
148
154
|
entitlements?: Record<string, boolean | string>;
|
|
149
|
-
|
|
155
|
+
|
|
150
156
|
/**
|
|
151
157
|
* Path to .iconset folder containing app icons
|
|
152
158
|
* @default "icon.iconset"
|
|
@@ -163,6 +169,20 @@ export interface ElectrobunConfig {
|
|
|
163
169
|
* @default false
|
|
164
170
|
*/
|
|
165
171
|
bundleCEF?: boolean;
|
|
172
|
+
|
|
173
|
+
/**
|
|
174
|
+
* Default renderer for webviews when not explicitly specified
|
|
175
|
+
* @default 'native'
|
|
176
|
+
*/
|
|
177
|
+
defaultRenderer?: 'native' | 'cef';
|
|
178
|
+
|
|
179
|
+
/**
|
|
180
|
+
* Path to application icon (.ico format)
|
|
181
|
+
* Used for the installer/extractor wrapper, desktop shortcuts, and taskbar
|
|
182
|
+
* Should include multiple sizes (16x16, 32x32, 48x48, 256x256) for best results
|
|
183
|
+
* @example "assets/icon.ico"
|
|
184
|
+
*/
|
|
185
|
+
icon?: string;
|
|
166
186
|
};
|
|
167
187
|
|
|
168
188
|
/**
|
|
@@ -175,6 +195,20 @@ export interface ElectrobunConfig {
|
|
|
175
195
|
* @default false
|
|
176
196
|
*/
|
|
177
197
|
bundleCEF?: boolean;
|
|
198
|
+
|
|
199
|
+
/**
|
|
200
|
+
* Default renderer for webviews when not explicitly specified
|
|
201
|
+
* @default 'native'
|
|
202
|
+
*/
|
|
203
|
+
defaultRenderer?: 'native' | 'cef';
|
|
204
|
+
|
|
205
|
+
/**
|
|
206
|
+
* Path to application icon (PNG format recommended)
|
|
207
|
+
* Used for desktop entries, window icons, and taskbar
|
|
208
|
+
* Should be at least 256x256 pixels for best results
|
|
209
|
+
* @example "assets/icon.png"
|
|
210
|
+
*/
|
|
211
|
+
icon?: string;
|
|
178
212
|
};
|
|
179
213
|
};
|
|
180
214
|
|
|
@@ -11,6 +11,7 @@ import {
|
|
|
11
11
|
createRPC,
|
|
12
12
|
} from "rpc-anywhere";
|
|
13
13
|
import { Updater } from "./Updater";
|
|
14
|
+
import { BuildConfig } from "./BuildConfig";
|
|
14
15
|
import type { BuiltinBunToWebviewSchema,BuiltinWebviewToBunSchema } from "../../browser/builtinrpcSchema";
|
|
15
16
|
import { rpcPort, sendMessageToWebviewViaSocket } from "./Socket";
|
|
16
17
|
import { randomBytes } from "crypto";
|
|
@@ -49,11 +50,14 @@ interface ElectrobunWebviewRPCSChema {
|
|
|
49
50
|
webview: RPCSchema;
|
|
50
51
|
}
|
|
51
52
|
|
|
53
|
+
const hash = await Updater.localInfo.hash();
|
|
54
|
+
const buildConfig = await BuildConfig.get();
|
|
55
|
+
|
|
52
56
|
const defaultOptions: Partial<BrowserViewOptions> = {
|
|
53
57
|
url: null,
|
|
54
58
|
html: null,
|
|
55
59
|
preload: null,
|
|
56
|
-
renderer:
|
|
60
|
+
renderer: buildConfig.defaultRenderer,
|
|
57
61
|
frame: {
|
|
58
62
|
x: 0,
|
|
59
63
|
y: 0,
|
|
@@ -61,10 +65,6 @@ const defaultOptions: Partial<BrowserViewOptions> = {
|
|
|
61
65
|
height: 600,
|
|
62
66
|
},
|
|
63
67
|
};
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
const hash = await Updater.localInfo.hash();
|
|
68
68
|
// Note: we use the build's hash to separate from different apps and different builds
|
|
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);
|
|
@@ -136,22 +136,22 @@ export class BrowserView<T> {
|
|
|
136
136
|
}
|
|
137
137
|
}
|
|
138
138
|
|
|
139
|
-
init() {
|
|
139
|
+
init() {
|
|
140
140
|
this.createStreams();
|
|
141
|
-
|
|
141
|
+
|
|
142
142
|
// TODO: add a then to this that fires an onReady event
|
|
143
143
|
return ffi.request.createWebview({
|
|
144
144
|
id: this.id,
|
|
145
145
|
windowId: this.windowId,
|
|
146
|
-
renderer: this.renderer,
|
|
146
|
+
renderer: this.renderer,
|
|
147
147
|
rpcPort: rpcPort,
|
|
148
148
|
// todo: consider sending secretKey as base64
|
|
149
149
|
secretKey: this.secretKey.toString(),
|
|
150
150
|
hostWebviewId: this.hostWebviewId || null,
|
|
151
151
|
pipePrefix: this.pipePrefix,
|
|
152
|
-
partition: this.partition,
|
|
152
|
+
partition: this.partition,
|
|
153
153
|
// Only pass URL if no HTML content is provided to avoid conflicts
|
|
154
|
-
url: this.html ? null : this.url,
|
|
154
|
+
url: this.html ? null : this.url,
|
|
155
155
|
html: this.html,
|
|
156
156
|
preload: this.preload,
|
|
157
157
|
frame: {
|
|
@@ -162,6 +162,7 @@ export class BrowserView<T> {
|
|
|
162
162
|
},
|
|
163
163
|
autoResize: this.autoResize,
|
|
164
164
|
navigationRules: this.navigationRules,
|
|
165
|
+
// transparent is looked up from parent window in native.ts
|
|
165
166
|
});
|
|
166
167
|
|
|
167
168
|
|
|
@@ -3,6 +3,9 @@ import electrobunEventEmitter from "../events/eventEmitter";
|
|
|
3
3
|
import { BrowserView } from "./BrowserView";
|
|
4
4
|
import { type RPC } from "rpc-anywhere";
|
|
5
5
|
import {FFIType} from 'bun:ffi'
|
|
6
|
+
import { BuildConfig } from "./BuildConfig";
|
|
7
|
+
|
|
8
|
+
const buildConfig = await BuildConfig.get();
|
|
6
9
|
|
|
7
10
|
let nextWindowId = 1;
|
|
8
11
|
|
|
@@ -18,15 +21,20 @@ type WindowOptionsType<T = undefined> = {
|
|
|
18
21
|
html: string | null;
|
|
19
22
|
preload: string | null;
|
|
20
23
|
renderer: 'native' | 'cef';
|
|
21
|
-
rpc?: T;
|
|
24
|
+
rpc?: T;
|
|
22
25
|
styleMask?: {};
|
|
23
|
-
//
|
|
24
|
-
|
|
26
|
+
// titleBarStyle options:
|
|
27
|
+
// - 'default': normal titlebar with native window controls
|
|
28
|
+
// - 'hidden': no titlebar, no native window controls (for fully custom chrome)
|
|
29
|
+
// - 'hiddenInset': transparent titlebar with inset native controls
|
|
30
|
+
titleBarStyle: "hidden" | "hiddenInset" | "default";
|
|
31
|
+
// transparent: when true, window background is transparent (see-through)
|
|
32
|
+
transparent: boolean;
|
|
25
33
|
navigationRules: string | null;
|
|
26
34
|
};
|
|
27
35
|
|
|
28
36
|
const defaultOptions: WindowOptionsType = {
|
|
29
|
-
title: "Electrobun",
|
|
37
|
+
title: "Electrobun",
|
|
30
38
|
frame: {
|
|
31
39
|
x: 0,
|
|
32
40
|
y: 0,
|
|
@@ -36,8 +44,9 @@ const defaultOptions: WindowOptionsType = {
|
|
|
36
44
|
url: "https://electrobun.dev",
|
|
37
45
|
html: null,
|
|
38
46
|
preload: null,
|
|
39
|
-
renderer:
|
|
47
|
+
renderer: buildConfig.defaultRenderer,
|
|
40
48
|
titleBarStyle: "default",
|
|
49
|
+
transparent: false,
|
|
41
50
|
navigationRules: null,
|
|
42
51
|
};
|
|
43
52
|
|
|
@@ -52,6 +61,7 @@ export class BrowserWindow<T> {
|
|
|
52
61
|
html: string | null = null;
|
|
53
62
|
preload: string | null = null;
|
|
54
63
|
renderer: 'native' | 'cef';
|
|
64
|
+
transparent: boolean = false;
|
|
55
65
|
frame: {
|
|
56
66
|
x: number;
|
|
57
67
|
y: number;
|
|
@@ -74,22 +84,24 @@ export class BrowserWindow<T> {
|
|
|
74
84
|
this.url = options.url || null;
|
|
75
85
|
this.html = options.html || null;
|
|
76
86
|
this.preload = options.preload || null;
|
|
77
|
-
this.renderer = options.renderer
|
|
87
|
+
this.renderer = options.renderer || defaultOptions.renderer;
|
|
88
|
+
this.transparent = options.transparent ?? false;
|
|
78
89
|
this.navigationRules = options.navigationRules || null;
|
|
79
|
-
|
|
90
|
+
|
|
80
91
|
this.init(options);
|
|
81
92
|
}
|
|
82
93
|
|
|
83
94
|
init({
|
|
84
|
-
rpc,
|
|
95
|
+
rpc,
|
|
85
96
|
styleMask,
|
|
86
97
|
titleBarStyle,
|
|
98
|
+
transparent,
|
|
87
99
|
}: Partial<WindowOptionsType<T>>) {
|
|
88
|
-
|
|
100
|
+
|
|
89
101
|
this.ptr = ffi.request.createWindow({
|
|
90
102
|
id: this.id,
|
|
91
103
|
title: this.title,
|
|
92
|
-
url: this.url || "",
|
|
104
|
+
url: this.url || "",
|
|
93
105
|
frame: {
|
|
94
106
|
width: this.frame.width,
|
|
95
107
|
height: this.frame.height,
|
|
@@ -110,14 +122,23 @@ export class BrowserWindow<T> {
|
|
|
110
122
|
NonactivatingPanel: false,
|
|
111
123
|
HUDWindow: false,
|
|
112
124
|
...(styleMask || {}),
|
|
125
|
+
// hiddenInset: transparent titlebar with inset native controls
|
|
113
126
|
...(titleBarStyle === "hiddenInset"
|
|
114
127
|
? {
|
|
115
128
|
Titled: true,
|
|
116
129
|
FullSizeContentView: true,
|
|
117
130
|
}
|
|
118
131
|
: {}),
|
|
132
|
+
// hidden: no titlebar, no native controls (for fully custom chrome)
|
|
133
|
+
...(titleBarStyle === "hidden"
|
|
134
|
+
? {
|
|
135
|
+
Titled: false,
|
|
136
|
+
FullSizeContentView: true,
|
|
137
|
+
}
|
|
138
|
+
: {}),
|
|
119
139
|
},
|
|
120
140
|
titleBarStyle: titleBarStyle || "default",
|
|
141
|
+
transparent: transparent ?? false,
|
|
121
142
|
});
|
|
122
143
|
|
|
123
144
|
BrowserWindowMap[this.id] = this;
|
|
@@ -153,8 +174,6 @@ export class BrowserWindow<T> {
|
|
|
153
174
|
console.log('setting webviewId: ', webview.id)
|
|
154
175
|
|
|
155
176
|
this.webviewId = webview.id;
|
|
156
|
-
|
|
157
|
-
|
|
158
177
|
}
|
|
159
178
|
|
|
160
179
|
get webview() {
|
|
@@ -180,12 +199,16 @@ export class BrowserWindow<T> {
|
|
|
180
199
|
return ffi.request.focusWindow({ winId: this.id });
|
|
181
200
|
}
|
|
182
201
|
|
|
202
|
+
show() {
|
|
203
|
+
return ffi.request.focusWindow({ winId: this.id });
|
|
204
|
+
}
|
|
205
|
+
|
|
183
206
|
minimize() {
|
|
184
207
|
return ffi.request.minimizeWindow({ winId: this.id });
|
|
185
208
|
}
|
|
186
209
|
|
|
187
210
|
unminimize() {
|
|
188
|
-
return ffi.request.
|
|
211
|
+
return ffi.request.restoreWindow({ winId: this.id });
|
|
189
212
|
}
|
|
190
213
|
|
|
191
214
|
isMinimized(): boolean {
|
|
@@ -220,6 +243,41 @@ export class BrowserWindow<T> {
|
|
|
220
243
|
return ffi.request.isWindowAlwaysOnTop({ winId: this.id });
|
|
221
244
|
}
|
|
222
245
|
|
|
246
|
+
setPosition(x: number, y: number) {
|
|
247
|
+
this.frame.x = x;
|
|
248
|
+
this.frame.y = y;
|
|
249
|
+
return ffi.request.setWindowPosition({ winId: this.id, x, y });
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
setSize(width: number, height: number) {
|
|
253
|
+
this.frame.width = width;
|
|
254
|
+
this.frame.height = height;
|
|
255
|
+
return ffi.request.setWindowSize({ winId: this.id, width, height });
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
setFrame(x: number, y: number, width: number, height: number) {
|
|
259
|
+
this.frame = { x, y, width, height };
|
|
260
|
+
return ffi.request.setWindowFrame({ winId: this.id, x, y, width, height });
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
getFrame(): { x: number; y: number; width: number; height: number } {
|
|
264
|
+
const frame = ffi.request.getWindowFrame({ winId: this.id });
|
|
265
|
+
// Update internal state
|
|
266
|
+
this.frame = frame;
|
|
267
|
+
return frame;
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
getPosition(): { x: number; y: number } {
|
|
271
|
+
const frame = this.getFrame();
|
|
272
|
+
return { x: frame.x, y: frame.y };
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
getSize(): { width: number; height: number } {
|
|
276
|
+
const frame = this.getFrame();
|
|
277
|
+
return { width: frame.width, height: frame.height };
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
|
|
223
281
|
// todo (yoav): move this to a class that also has off, append, prepend, etc.
|
|
224
282
|
// name should only allow browserWindow events
|
|
225
283
|
on(name, handler) {
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
export type BuildConfigType = {
|
|
2
|
+
defaultRenderer: 'native' | 'cef';
|
|
3
|
+
availableRenderers: ('native' | 'cef')[];
|
|
4
|
+
};
|
|
5
|
+
|
|
6
|
+
let buildConfig: BuildConfigType | null = null;
|
|
7
|
+
|
|
8
|
+
const BuildConfig = {
|
|
9
|
+
/**
|
|
10
|
+
* Get the build configuration. Loads from build.json on first call, then returns cached value.
|
|
11
|
+
*/
|
|
12
|
+
get: async (): Promise<BuildConfigType> => {
|
|
13
|
+
if (buildConfig) {
|
|
14
|
+
return buildConfig;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
try {
|
|
18
|
+
const resourcesDir = 'Resources';
|
|
19
|
+
buildConfig = await Bun.file(`../${resourcesDir}/build.json`).json();
|
|
20
|
+
return buildConfig!;
|
|
21
|
+
} catch (error) {
|
|
22
|
+
// Fallback for dev mode or missing file
|
|
23
|
+
buildConfig = {
|
|
24
|
+
defaultRenderer: 'native',
|
|
25
|
+
availableRenderers: ['native'],
|
|
26
|
+
};
|
|
27
|
+
return buildConfig;
|
|
28
|
+
}
|
|
29
|
+
},
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Get the cached build configuration synchronously.
|
|
33
|
+
* Returns null if config hasn't been loaded yet.
|
|
34
|
+
*/
|
|
35
|
+
getCached: (): BuildConfigType | null => buildConfig,
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
export { BuildConfig };
|
|
@@ -101,7 +101,7 @@ const startRPCServer = () => {
|
|
|
101
101
|
return;
|
|
102
102
|
}
|
|
103
103
|
const { webviewId } = ws.data;
|
|
104
|
-
console.log("Closed:", webviewId, code, reason);
|
|
104
|
+
// console.log("Closed:", webviewId, code, reason);
|
|
105
105
|
if (socketMap[webviewId]) {
|
|
106
106
|
socketMap[webviewId].socket = null;
|
|
107
107
|
}
|
|
@@ -57,6 +57,34 @@ exec ./launcher "$@"
|
|
|
57
57
|
console.log(`Created/updated Linux launcher script: ${launcherPath}`);
|
|
58
58
|
}
|
|
59
59
|
|
|
60
|
+
// Create launcher script for AppImage
|
|
61
|
+
async function createLinuxAppImageLauncherScript(appImagePath: string): Promise<void> {
|
|
62
|
+
const parentDir = dirname(appImagePath);
|
|
63
|
+
const launcherPath = join(parentDir, "run.sh");
|
|
64
|
+
|
|
65
|
+
const launcherContent = `#!/bin/bash
|
|
66
|
+
# Electrobun AppImage Launcher
|
|
67
|
+
# This script launches the AppImage
|
|
68
|
+
|
|
69
|
+
# Get the directory where this script is located
|
|
70
|
+
SCRIPT_DIR="$(cd "$(dirname "\${BASH_SOURCE[0]}")" && pwd)"
|
|
71
|
+
APPIMAGE_PATH="$SCRIPT_DIR/$(basename "${appImagePath}")"
|
|
72
|
+
|
|
73
|
+
# Force X11 backend for compatibility
|
|
74
|
+
export GDK_BACKEND=x11
|
|
75
|
+
|
|
76
|
+
# Launch the AppImage
|
|
77
|
+
exec "$APPIMAGE_PATH" "$@"
|
|
78
|
+
`;
|
|
79
|
+
|
|
80
|
+
await Bun.write(launcherPath, launcherContent);
|
|
81
|
+
|
|
82
|
+
// Make it executable
|
|
83
|
+
execSync(`chmod +x "${launcherPath}"`);
|
|
84
|
+
|
|
85
|
+
console.log(`Created/updated Linux AppImage launcher script: ${launcherPath}`);
|
|
86
|
+
}
|
|
87
|
+
|
|
60
88
|
// Cross-platform app data directory
|
|
61
89
|
function getAppDataDir(): string {
|
|
62
90
|
switch (currentOS) {
|
|
@@ -450,10 +478,28 @@ const Updater = {
|
|
|
450
478
|
|
|
451
479
|
// Platform-specific path handling
|
|
452
480
|
let newAppBundlePath: string;
|
|
453
|
-
if (currentOS === 'linux'
|
|
454
|
-
// On Linux
|
|
481
|
+
if (currentOS === 'linux') {
|
|
482
|
+
// On Linux, look for the .AppImage file in the extraction directory
|
|
483
|
+
const appImageName = `${localInfo.name.replace(/ /g, "").replace(/\./g, "-")}.AppImage`;
|
|
484
|
+
newAppBundlePath = join(extractionDir, appImageName);
|
|
485
|
+
|
|
486
|
+
// Verify the AppImage exists
|
|
487
|
+
if (!statSync(newAppBundlePath, { throwIfNoEntry: false })) {
|
|
488
|
+
console.error(`AppImage not found at: ${newAppBundlePath}`);
|
|
489
|
+
console.log("Contents of extraction directory:");
|
|
490
|
+
try {
|
|
491
|
+
const files = readdirSync(extractionDir);
|
|
492
|
+
for (const file of files) {
|
|
493
|
+
console.log(` - ${file}`);
|
|
494
|
+
}
|
|
495
|
+
} catch (e) {
|
|
496
|
+
console.log("Could not list directory contents:", e);
|
|
497
|
+
}
|
|
498
|
+
return;
|
|
499
|
+
}
|
|
500
|
+
} else if (currentOS === 'win') {
|
|
501
|
+
// On Windows, the actual app is inside a subdirectory
|
|
455
502
|
// Use same sanitization as extractor: remove spaces and dots
|
|
456
|
-
// Note: localInfo.name already includes the channel (e.g., "test1-canary")
|
|
457
503
|
const appBundleName = localInfo.name.replace(/ /g, "").replace(/\./g, "-");
|
|
458
504
|
newAppBundlePath = join(extractionDir, appBundleName);
|
|
459
505
|
|
|
@@ -485,10 +531,12 @@ const Updater = {
|
|
|
485
531
|
".."
|
|
486
532
|
);
|
|
487
533
|
} else {
|
|
488
|
-
//
|
|
534
|
+
// Platform-specific app path calculation
|
|
489
535
|
const appDataFolder = await Updater.appDataFolder();
|
|
490
536
|
if (currentOS === 'linux') {
|
|
491
|
-
|
|
537
|
+
// On Linux, store AppImage as a single file
|
|
538
|
+
const appImageName = `${localInfo.name.replace(/ /g, "").replace(/\./g, "-")}.AppImage`;
|
|
539
|
+
runningAppBundlePath = join(appDataFolder, appImageName);
|
|
492
540
|
} else {
|
|
493
541
|
// On Windows, use versioned app folders
|
|
494
542
|
const currentHash = (await Updater.getLocallocalInfo()).hash;
|
|
@@ -519,29 +567,25 @@ const Updater = {
|
|
|
519
567
|
// Move new app to running location
|
|
520
568
|
renameSync(newAppBundlePath, runningAppBundlePath);
|
|
521
569
|
} else if (currentOS === 'linux') {
|
|
522
|
-
// On Linux,
|
|
523
|
-
// Remove existing backup
|
|
570
|
+
// On Linux, backup and replace AppImage file
|
|
571
|
+
// Remove existing backup if it exists
|
|
524
572
|
if (statSync(backupPath, { throwIfNoEntry: false })) {
|
|
525
573
|
unlinkSync(backupPath);
|
|
526
574
|
}
|
|
527
575
|
|
|
528
|
-
// Create
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
cwd: dirname(runningAppBundlePath),
|
|
533
|
-
},
|
|
534
|
-
[basename(runningAppBundlePath)]
|
|
535
|
-
);
|
|
536
|
-
|
|
537
|
-
// Remove current app
|
|
538
|
-
rmdirSync(runningAppBundlePath, { recursive: true });
|
|
576
|
+
// Create backup of current AppImage (if it exists)
|
|
577
|
+
if (statSync(runningAppBundlePath, { throwIfNoEntry: false })) {
|
|
578
|
+
renameSync(runningAppBundlePath, backupPath);
|
|
579
|
+
}
|
|
539
580
|
|
|
540
|
-
// Move new
|
|
581
|
+
// Move new AppImage to app location
|
|
541
582
|
renameSync(newAppBundlePath, runningAppBundlePath);
|
|
542
583
|
|
|
543
|
-
//
|
|
544
|
-
|
|
584
|
+
// Make AppImage executable
|
|
585
|
+
execSync(`chmod +x "${runningAppBundlePath}"`);
|
|
586
|
+
|
|
587
|
+
// Create/update launcher script that points to the AppImage
|
|
588
|
+
await createLinuxAppImageLauncherScript(runningAppBundlePath);
|
|
545
589
|
} else {
|
|
546
590
|
// On Windows, use versioned app folders
|
|
547
591
|
const parentDir = dirname(runningAppBundlePath);
|
|
@@ -630,9 +674,8 @@ start "" launcher.exe
|
|
|
630
674
|
await Bun.spawn(["cmd", "/c", runBatPath], { detached: true });
|
|
631
675
|
break;
|
|
632
676
|
case 'linux':
|
|
633
|
-
// On Linux,
|
|
634
|
-
|
|
635
|
-
Bun.spawn(["sh", "-c", `${linuxLauncher} &`], { detached: true});
|
|
677
|
+
// On Linux, launch the AppImage directly
|
|
678
|
+
Bun.spawn(["sh", "-c", `"${runningAppBundlePath}" &`], { detached: true});
|
|
636
679
|
break;
|
|
637
680
|
}
|
|
638
681
|
// Use native killApp to properly clean up all resources
|