electrobun 0.1.2 → 0.1.7

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/README.md CHANGED
@@ -8,7 +8,7 @@
8
8
 
9
9
  ## What is Electrobun?
10
10
 
11
- > Electrobun is in the **_very_** early stages. We currently support development on macOS, Windows, and Linux, but it has memory leaks, has no tests, and many core things are still missing. We're actively working on stabilizing cross-platform support.
11
+ > Electrobun is in the **_very_** early stages. We currently support development on macOS, Windows, and Linux. You can also bundle your app for these platform targets and cross-bundle them from a mac. We're actively working on stabilizing so any bug reports, especially on different platforms are most welcome.
12
12
 
13
13
  Electrobun aims to be a complete **solution-in-a-box** for building, updating, and shipping ultra fast, tiny, and cross-platform desktop applications written in Typescript.
14
14
  Under the hood it uses <a href="https://bun.sh">bun</a> to execute the main process and to bundle webview typescript, and has native bindings written in <a href="https://ziglang.org/">zig</a>.
@@ -60,13 +60,17 @@ Ways to get involved at this early stage:
60
60
  - cmake
61
61
  - webkit2gtk and GTK development packages
62
62
 
63
+ Un Ubuntu/Debian based distros: `sudo apt install build-essential cmake pkg-config libgtk-3-dev libwebkit2gtk-4.1-dev libayatana-appindicator3-dev librsvg2-dev`
64
+
63
65
  ### First-time Setup
64
66
 
65
67
  ```bash
66
- git clone <repo-url>
68
+ git clone --recurse-submodules https://github.com/blackboardsh/electrobun.git
67
69
  cd electrobun
68
70
  bun install
69
71
  bun dev:playground:clean
72
+ # On Linux, use:
73
+ bun dev:playground:clean:linux
70
74
  ```
71
75
 
72
76
  ### Development Workflow
@@ -74,12 +78,16 @@ bun dev:playground:clean
74
78
  ```bash
75
79
  # After making changes to source code
76
80
  bun dev:playground
81
+ # On Linux, use:
82
+ bun dev:playground:linux
77
83
 
78
84
  # If you only changed playground code (not electrobun source)
79
85
  bun dev:playground:rerun
80
86
 
81
87
  # If you need a completely fresh start
82
88
  bun dev:playground:clean
89
+ # On Linux, use:
90
+ bun dev:playground:clean:linux
83
91
  ```
84
92
 
85
93
  ### Additional Commands
@@ -111,7 +111,7 @@ const ConfigureWebviewTags = (
111
111
  // todo: wire up to a param and a method to update them
112
112
  navigationRules: null,
113
113
  });
114
-
114
+ console.log('electrobun webviewid: ', webviewId)
115
115
  this.webviewId = webviewId;
116
116
  this.id = `electrobun-webview-${webviewId}`;
117
117
  // todo: replace bun -> webviewtag communication with a global instead of
@@ -252,8 +252,10 @@ const ConfigureWebviewTags = (
252
252
 
253
253
  if (width === 0 && height === 0) {
254
254
  if (this.wasZeroRect === false) {
255
+ console.log('WAS NOT ZERO RECT', this.webviewId)
255
256
  this.wasZeroRect = true;
256
- this.toggleHidden(true, true);
257
+ this.toggleTransparent(true, true);
258
+ this.togglePassthrough(true, true);
257
259
  }
258
260
  return;
259
261
  }
@@ -312,7 +314,9 @@ const ConfigureWebviewTags = (
312
314
 
313
315
  if (this.wasZeroRect) {
314
316
  this.wasZeroRect = false;
315
- this.toggleHidden(false, true);
317
+ console.log('WAS ZERO RECT', this.webviewId)
318
+ this.toggleTransparent(false, true);
319
+ this.togglePassthrough(false, true);
316
320
  }
317
321
  }
318
322
 
@@ -492,17 +496,20 @@ const ConfigureWebviewTags = (
492
496
  return;
493
497
  }
494
498
 
495
- if (!bypassState) {
496
- if (typeof transparent === "undefined") {
497
- this.transparent = !this.transparent;
498
- } else {
499
- this.transparent = transparent;
500
- }
499
+ let newValue;
500
+ if (typeof transparent === "undefined") {
501
+ newValue = !this.transparent;
502
+ } else {
503
+ newValue = Boolean(transparent);
501
504
  }
502
505
 
506
+ if (!bypassState) {
507
+ this.transparent = newValue;
508
+ }
509
+
503
510
  this.internalRpc.send.webviewTagSetTransparent({
504
511
  id: this.webviewId,
505
- transparent: this.transparent || Boolean(transparent),
512
+ transparent: newValue,
506
513
  });
507
514
  }
508
515
  togglePassthrough(enablePassthrough?: boolean, bypassState?: boolean) {
@@ -511,12 +518,15 @@ const ConfigureWebviewTags = (
511
518
  return;
512
519
  }
513
520
 
514
- if (!bypassState) {
515
- if (typeof enablePassthrough === "undefined") {
516
- this.passthroughEnabled = !this.passthroughEnabled;
517
- } else {
518
- this.passthroughEnabled = enablePassthrough;
519
- }
521
+ let newValue;
522
+ if (typeof enablePassthrough === "undefined") {
523
+ newValue = !this.passthroughEnabled;
524
+ } else {
525
+ newValue = Boolean(enablePassthrough);
526
+ }
527
+
528
+ if (!bypassState) {
529
+ this.passthroughEnabled = newValue;
520
530
  }
521
531
 
522
532
  this.internalRpc.send.webviewTagSetPassthrough({
@@ -532,17 +542,21 @@ const ConfigureWebviewTags = (
532
542
  return;
533
543
  }
534
544
 
535
- if (!bypassState) {
536
- if (typeof hidden === "undefined") {
537
- this.hidden = !this.hidden;
538
- } else {
539
- this.hidden = hidden;
540
- }
545
+ let newValue;
546
+ if (typeof hidden === "undefined") {
547
+ newValue = !this.hidden;
548
+ } else {
549
+ newValue = Boolean(hidden);
550
+ }
551
+
552
+ if (!bypassState) {
553
+ this.hidden = newValue;
541
554
  }
542
555
 
556
+ console.trace('electrobun toggle hidden: ', this.hidden, this.webviewId)
543
557
  this.internalRpc.send.webviewTagSetHidden({
544
558
  id: this.webviewId,
545
- hidden: this.hidden || Boolean(hidden),
559
+ hidden: this.hidden|| Boolean(hidden),
546
560
  });
547
561
  }
548
562
  }
@@ -124,12 +124,9 @@ export class BrowserView<T> {
124
124
  this.ptr = this.init();
125
125
  }
126
126
 
127
- init() {
128
- console.log('browserView init', this.id, this.windowId, this.renderer);
127
+ init() {
129
128
  this.createStreams();
130
-
131
-
132
- console.log('ffi createWEbview')
129
+
133
130
  // TODO: add a then to this that fires an onReady event
134
131
  return ffi.request.createWebview({
135
132
  id: this.id,
@@ -162,8 +162,7 @@ export class BrowserWindow<T> {
162
162
 
163
163
  }
164
164
 
165
- get webview() {
166
- console.log('getting webview for window: ', this.webviewId)
165
+ get webview() {
167
166
  // todo (yoav): we don't want this to be undefined, so maybe we should just
168
167
  // link directly to the browserview object instead of a getter
169
168
  return BrowserView.getById(this.webviewId) as BrowserView<T>;
@@ -25,17 +25,21 @@ export class Tray {
25
25
  template = true,
26
26
  width = 16,
27
27
  height = 16,
28
- }: ConstructorOptions = {}) {
29
- console.log("img", image);
30
- console.log("img", this.resolveImagePath(image));
31
- this.ptr = ffi.request.createTray({
32
- id: this.id,
33
- title,
34
- image: this.resolveImagePath(image),
35
- template,
36
- width,
37
- height,
38
- });
28
+ }: ConstructorOptions = {}) {
29
+ try {
30
+ this.ptr = ffi.request.createTray({
31
+ id: this.id,
32
+ title,
33
+ image: this.resolveImagePath(image),
34
+ template,
35
+ width,
36
+ height,
37
+ });
38
+ } catch (error) {
39
+ console.warn('Tray creation failed:', error);
40
+ console.warn('System tray functionality may not be available on this platform');
41
+ this.ptr = null;
42
+ }
39
43
 
40
44
  TrayMap[this.id] = this;
41
45
  }
@@ -50,10 +54,12 @@ export class Tray {
50
54
  }
51
55
 
52
56
  setTitle(title: string) {
57
+ if (!this.ptr) return;
53
58
  ffi.request.setTrayTitle({ id: this.id, title });
54
59
  }
55
60
 
56
61
  setImage(imgPath: string) {
62
+ if (!this.ptr) return;
57
63
  ffi.request.setTrayImage({
58
64
  id: this.id,
59
65
  image: this.resolveImagePath(imgPath),
@@ -61,6 +67,7 @@ export class Tray {
61
67
  }
62
68
 
63
69
  setMenu(menu: Array<MenuItemConfig>) {
70
+ if (!this.ptr) return;
64
71
  const menuWithDefaults = menuConfigWithDefaults(menu);
65
72
  ffi.request.setTrayMenu({
66
73
  id: this.id,
@@ -75,7 +82,9 @@ export class Tray {
75
82
 
76
83
  remove() {
77
84
  console.log('Tray.remove() called for id:', this.id);
78
- ffi.request.removeTray({ id: this.id });
85
+ if (this.ptr) {
86
+ ffi.request.removeTray({ id: this.id });
87
+ }
79
88
  delete TrayMap[this.id];
80
89
  console.log('Tray removed from TrayMap');
81
90
  }
@@ -1,4 +1,4 @@
1
- import { ffi } from "../proc/native";
1
+ import { ffi, native } from "../proc/native";
2
2
 
3
3
  // TODO: move this to a more appropriate namespace
4
4
  export const moveToTrash = (path: string) => {
@@ -9,6 +9,11 @@ export const showItemInFolder = (path: string) => {
9
9
  return ffi.request.showItemInFolder({ path });
10
10
  };
11
11
 
12
+ export const quit = () => {
13
+ // Use native killApp for graceful shutdown
14
+ native.symbols.killApp();
15
+ };
16
+
12
17
  export const openFileDialog = async (
13
18
  opts: {
14
19
  startingFolder?: string;
@@ -526,8 +526,7 @@ export const ffi = {
526
526
  // init (like views://myview/index.html) so fast while the Bun FFI to load a url is still executing
527
527
  // or something where the JSCallback that this postMessage fires is not available or busy or
528
528
  // its memory is allocated to something else or something and the handler receives garbage data in Bun.
529
- setTimeout(() => {
530
- console.log('emitWebviewEvent', eventName, detail)
529
+ setTimeout(() => {
531
530
  window.__electrobunInternalBridge?.postMessage(JSON.stringify({id: 'webviewEvent', type: 'message', payload: {id: window.__electrobunWebviewId, eventName, detail}}));
532
531
  });
533
532
  };
@@ -683,7 +682,7 @@ export const ffi = {
683
682
  } = params;
684
683
 
685
684
  const tray = Tray.getById(id);
686
- console.log('native.symbols.setTrayMenu', tray.ptr, menuConfig)
685
+
687
686
  native.symbols.setTrayMenu(
688
687
  tray.ptr,
689
688
  toCString(menuConfig)
@@ -691,18 +690,14 @@ export const ffi = {
691
690
  },
692
691
 
693
692
  removeTray: (params: { id: number }): void => {
694
- const { id } = params;
695
- console.log('removeTray called with id:', id);
696
- const tray = Tray.getById(id);
697
- console.log('Found tray:', tray);
693
+ const { id } = params;
694
+ const tray = Tray.getById(id);
698
695
 
699
696
  if (!tray) {
700
697
  throw `Can't remove tray. Tray no longer exists`;
701
698
  }
702
-
703
- console.log('Calling native.symbols.removeTray with ptr:', tray.ptr);
704
- native.symbols.removeTray(tray.ptr);
705
- console.log('Native removeTray called successfully');
699
+
700
+ native.symbols.removeTray(tray.ptr);
706
701
  // The Tray class will handle removing from TrayMap
707
702
  },
708
703
  setApplicationMenu: (params: {menuConfig: string}): void => {
@@ -900,7 +895,7 @@ native.symbols.setJSUtils(getMimeType, getHTMLForWebviewSync);
900
895
 
901
896
  // TODO XX: revisit this as integrated into the will-navigate handler
902
897
  const webviewDecideNavigation = new JSCallback((webviewId, url) => {
903
- console.log('webviewDecideNavigation', webviewId, new CString(url))
898
+ console.log('TODO: webviewDecideNavigation', webviewId, new CString(url))
904
899
  return true;
905
900
  }, {
906
901
  args: [FFIType.u32, FFIType.cstring],
@@ -1056,10 +1051,13 @@ const internalBridgeHandler = new JSCallback((id, msg) => {
1056
1051
  threadsafe: true
1057
1052
  });
1058
1053
 
1059
- const trayItemHandler = new JSCallback((id, action) => {
1054
+ const trayItemHandler = new JSCallback((id, action) => {
1055
+ // Note: Some invisible character that doesn't appear in .length
1056
+ // is causing issues
1057
+ const actionString = (new CString(action).toString() || "").trim();
1060
1058
  const event = electrobunEventEmitter.events.tray.trayClicked({
1061
1059
  id,
1062
- action: new CString(action),
1060
+ action: actionString,
1063
1061
  });
1064
1062
 
1065
1063
  let result;
@@ -1171,7 +1169,7 @@ export const internalRpcHandlers = {
1171
1169
  return native.symbols.webviewCanGoForward(webviewPtr);
1172
1170
  },
1173
1171
  webviewTagCallAsyncJavaScript: (params) => {
1174
- console.log('-----------+ request: ', 'webviewTagCallAsyncJavaScript', params)
1172
+ console.log('TODO: webviewTagCallAsyncJavaScript NOT YET IMPLEMENTED', params)
1175
1173
  // Not implemented - users can use RPC for JavaScript execution if needed
1176
1174
  }
1177
1175
  },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "electrobun",
3
- "version": "0.1.2",
3
+ "version": "0.1.7",
4
4
  "description": "Build ultra fast, tiny, and cross-platform desktop apps with Typescript.",
5
5
  "license": "MIT",
6
6
  "author": "Blackboard Technologies Inc.",
@@ -33,7 +33,8 @@
33
33
  "build:release": "bun build.ts --release",
34
34
  "dev:playground": "bun build:dev && bun build:cli && cd playground && npm install && bun build:dev && bun start",
35
35
  "dev:playground:linux": "bun build:dev && npm link && cd playground && npm link electrobun && bun build:dev && bun start",
36
- "dev:playground:clean": "cd playground && rm -rf node_modules && npm install && cd .. && bun dev:playground",
36
+ "dev:playground:clean": "cd playground && rm -rf node_modules && rm -rf vendors/cef && npm install && cd .. && bun dev:playground",
37
+ "dev:playground:clean:linux": "cd playground && rm -rf node_modules && rm -rf vendors/cef && npm install && cd .. && bun dev:playground:linux",
37
38
  "dev:playground:rerun": "cd playground && bun start",
38
39
  "dev:playground:canary": "bun build:release && cd playground && npm install && bun build:canary && bun start:canary",
39
40
  "run:playground": "bun build:dev && bun build:cli && cd templates/interactive-playground && npm install && bun build:dev && bun start",