electrobun 1.11.4-beta.0 → 1.11.5-beta.2

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.
@@ -1,5 +1,6 @@
1
1
  import { ffi, type ApplicationMenuItemConfig } from "../proc/native";
2
2
  import electrobunEventEmitter from "../events/eventEmitter";
3
+ import { roleLabelMap } from "./menuRoles";
3
4
 
4
5
  type NonDividerMenuItem = {
5
6
  type?: "normal";
@@ -30,32 +31,6 @@ export const on = (
30
31
  electrobunEventEmitter.on(specificName, handler);
31
32
  };
32
33
 
33
- const roleLabelMap = {
34
- quit: "Quit",
35
- hide: "Hide",
36
- hideOthers: "Hide Others",
37
- showAll: "Show All",
38
- undo: "Undo",
39
- redo: "Redo",
40
- cut: "Cut",
41
- copy: "Copy",
42
- paste: "Paste",
43
- pasteAndMatchStyle: "Paste And Match Style",
44
- delete: "Delete",
45
- selectAll: "Select All",
46
- startSpeaking: "Start Speaking",
47
- stopSpeaking: "Stop Speaking",
48
- enterFullScreen: "Enter FullScreen",
49
- exitFullScreen: "Exit FullScreen",
50
- toggleFullScreen: "Toggle Full Screen",
51
- minimize: "Minimize",
52
- zoom: "Zoom",
53
- bringAllToFront: "Bring All To Front",
54
- close: "Close",
55
- cycleThroughWindows: "Cycle Through Windows",
56
- showHelp: "Show Help",
57
- };
58
-
59
34
  const menuConfigWithDefaults = (
60
35
  menu: Array<ApplicationMenuItemConfig>,
61
36
  ): Array<ApplicationMenuItemConfig> => {
@@ -1,6 +1,7 @@
1
1
  // TODO: have a context specific menu that excludes role
2
2
  import { ffi, type ApplicationMenuItemConfig } from "../proc/native";
3
3
  import electrobunEventEmitter from "../events/eventEmitter";
4
+ import { roleLabelMap } from "./menuRoles";
4
5
 
5
6
  type NonDividerMenuItem = {
6
7
  type?: "normal";
@@ -31,33 +32,6 @@ export const on = (
31
32
  electrobunEventEmitter.on(specificName, handler);
32
33
  };
33
34
 
34
- // todo: Consolidate Application menu, context menu, and tray menus can all have roles.
35
- const roleLabelMap = {
36
- quit: "Quit",
37
- hide: "Hide",
38
- hideOthers: "Hide Others",
39
- showAll: "Show All",
40
- undo: "Undo",
41
- redo: "Redo",
42
- cut: "Cut",
43
- copy: "Copy",
44
- paste: "Paste",
45
- pasteAndMatchStyle: "Paste And Match Style",
46
- delete: "Delete",
47
- selectAll: "Select All",
48
- startSpeaking: "Start Speaking",
49
- stopSpeaking: "Stop Speaking",
50
- enterFullScreen: "Enter FullScreen",
51
- exitFullScreen: "Exit FullScreen",
52
- toggleFullScreen: "Toggle Full Screen",
53
- minimize: "Minimize",
54
- zoom: "Zoom",
55
- bringAllToFront: "Bring All To Front",
56
- close: "Close",
57
- cycleThroughWindows: "Cycle Through Windows",
58
- showHelp: "Show Help",
59
- };
60
-
61
35
  const menuConfigWithDefaults = (
62
36
  menu: Array<ApplicationMenuItemConfig>,
63
37
  ): Array<ApplicationMenuItemConfig> => {
@@ -96,37 +96,6 @@ function emitStatus(
96
96
  // native.symbols.killApp();
97
97
  // }, 1000)
98
98
 
99
- // Create launcher script for AppImage
100
- async function createLinuxAppImageLauncherScript(
101
- appImagePath: string,
102
- ): Promise<void> {
103
- const parentDir = dirname(appImagePath);
104
- const launcherPath = join(parentDir, "run.sh");
105
-
106
- const launcherContent = `#!/bin/bash
107
- # Electrobun AppImage Launcher
108
- # This script launches the AppImage
109
-
110
- # Get the directory where this script is located
111
- SCRIPT_DIR="$(cd "$(dirname "\${BASH_SOURCE[0]}")" && pwd)"
112
- APPIMAGE_PATH="$SCRIPT_DIR/$(basename "${appImagePath}")"
113
-
114
- # Force X11 backend for compatibility
115
- export GDK_BACKEND=x11
116
-
117
- # Launch the AppImage
118
- exec "$APPIMAGE_PATH" "$@"
119
- `;
120
-
121
- await Bun.write(launcherPath, launcherContent);
122
-
123
- // Make it executable
124
- execSync(`chmod +x "${launcherPath}"`);
125
-
126
- console.log(
127
- `Created/updated Linux AppImage launcher script: ${launcherPath}`,
128
- );
129
- }
130
99
 
131
100
  // Cross-platform app data directory
132
101
  function getAppDataDir(): string {
@@ -850,15 +819,15 @@ const Updater = {
850
819
  // Platform-specific path handling
851
820
  let newAppBundlePath: string;
852
821
  if (currentOS === "linux") {
853
- // On Linux, the tarball contains a directory named {appFileName}, and inside it is the AppImage
854
- // Structure: extractionDir/{appFileName}/{appFileName}.AppImage
855
- const innerDirName = localInfo.name.replace(/ /g, "");
856
- const appImageName = `${localInfo.name.replace(/ /g, "").replace(/\./g, "-")}.AppImage`;
857
- newAppBundlePath = join(extractionDir, innerDirName, appImageName);
822
+ // On Linux, the tarball contains a directory bundle, not an AppImage
823
+ // Structure: extractionDir/{appBundleName}
824
+ const appBundleName = `${localInfo.name.replace(/ /g, "").replace(/\./g, "")}-${localInfo.channel}`;
825
+ newAppBundlePath = join(extractionDir, appBundleName);
858
826
 
859
- // Verify the AppImage exists
860
- if (!statSync(newAppBundlePath, { throwIfNoEntry: false })) {
861
- console.error(`AppImage not found at: ${newAppBundlePath}`);
827
+ // Verify the app bundle directory exists
828
+ const bundleStats = statSync(newAppBundlePath, { throwIfNoEntry: false });
829
+ if (!bundleStats || !bundleStats.isDirectory()) {
830
+ console.error(`App bundle directory not found at: ${newAppBundlePath}`);
862
831
  console.log("Contents of extraction directory:");
863
832
  try {
864
833
  const files = readdirSync(extractionDir);
@@ -906,20 +875,16 @@ const Updater = {
906
875
  }
907
876
  // Platform-specific app path calculation
908
877
  let runningAppBundlePath: string;
878
+ const appDataFolder = await Updater.appDataFolder();
879
+
909
880
  if (currentOS === "macos") {
910
881
  // On macOS, executable is at Contents/MacOS/binary inside .app bundle
911
882
  runningAppBundlePath = resolve(dirname(process.execPath), "..", "..");
883
+ } else if (currentOS === "linux" || currentOS === "win") {
884
+ // On Linux and Windows, use fixed 'app' folder to match extractor
885
+ runningAppBundlePath = join(appDataFolder, "app");
912
886
  } else {
913
- // Platform-specific app path calculation
914
- const appDataFolder = await Updater.appDataFolder();
915
- if (currentOS === "linux") {
916
- // On Linux, store AppImage as a single file
917
- const appImageName = `${localInfo.name.replace(/ /g, "").replace(/\./g, "-")}.AppImage`;
918
- runningAppBundlePath = join(appDataFolder, appImageName);
919
- } else {
920
- // On Windows, use fixed 'app' folder to match extractor
921
- runningAppBundlePath = join(appDataFolder, "app");
922
- }
887
+ throw new Error(`Unsupported platform: ${currentOS}`);
923
888
  }
924
889
  try {
925
890
  emitStatus("replacing-app", "Removing old version...");
@@ -946,30 +911,29 @@ const Updater = {
946
911
  // Ignore errors - attribute may not exist
947
912
  }
948
913
  } else if (currentOS === "linux") {
949
- // On Linux, remove existing AppImage and replace with new one
950
- if (statSync(runningAppBundlePath, { throwIfNoEntry: false })) {
951
- unlinkSync(runningAppBundlePath);
914
+ // On Linux, we now have directory bundles instead of AppImage files
915
+ // The app is stored in {appDataFolder}/app/
916
+ const appBundleDir = join(appDataFolder, "app");
917
+
918
+ // Remove existing app directory if it exists
919
+ if (statSync(appBundleDir, { throwIfNoEntry: false })) {
920
+ rmdirSync(appBundleDir, { recursive: true });
952
921
  }
953
922
 
954
- // Move new AppImage to app location
955
- renameSync(newAppBundlePath, runningAppBundlePath);
923
+ // Move new app bundle directory to app location
924
+ renameSync(newAppBundlePath, appBundleDir);
956
925
 
957
- // Clean up the extracted inner directory (contains leftover icon, shortcut, metadata.json)
958
- const innerDirName = localInfo.name.replace(/ /g, "");
959
- const extractedInnerDir = join(extractionDir, innerDirName);
960
- if (
961
- statSync(extractedInnerDir, {
962
- throwIfNoEntry: false,
963
- })?.isDirectory()
964
- ) {
965
- rmdirSync(extractedInnerDir, { recursive: true });
926
+ // Ensure launcher binary is executable
927
+ const launcherPath = join(appBundleDir, "bin", "launcher");
928
+ if (statSync(launcherPath, { throwIfNoEntry: false })) {
929
+ execSync(`chmod +x "${launcherPath}"`);
966
930
  }
967
931
 
968
- // Make AppImage executable
969
- execSync(`chmod +x "${runningAppBundlePath}"`);
970
-
971
- // Create/update launcher script that points to the AppImage
972
- await createLinuxAppImageLauncherScript(runningAppBundlePath);
932
+ // Also ensure other binaries are executable
933
+ const bunPath = join(appBundleDir, "bin", "bun");
934
+ if (statSync(bunPath, { throwIfNoEntry: false })) {
935
+ execSync(`chmod +x "${bunPath}"`);
936
+ }
973
937
  }
974
938
 
975
939
  // Clean up stale files in extraction folder
@@ -1078,9 +1042,10 @@ del "%~f0"
1078
1042
  detached: true,
1079
1043
  } as any);
1080
1044
  } else if (currentOS === "linux") {
1081
- // On Linux, launch the AppImage directly
1045
+ // On Linux, launch the launcher binary inside the app directory
1046
+ const launcherPath = join(runningAppBundlePath, "bin", "launcher");
1082
1047
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
1083
- Bun.spawn(["sh", "-c", `"${runningAppBundlePath}" &`], {
1048
+ Bun.spawn(["sh", "-c", `"${launcherPath}" &`], {
1084
1049
  detached: true,
1085
1050
  } as any);
1086
1051
  }
@@ -0,0 +1,181 @@
1
+ /**
2
+ * Shared menu role definitions used by ApplicationMenu, ContextMenu, and Tray menus.
3
+ * These map to macOS NSResponder selectors for native text editing support.
4
+ */
5
+
6
+ export const roleLabelMap: Record<string, string> = {
7
+ // Application roles
8
+ about: "About",
9
+ quit: "Quit",
10
+ hide: "Hide",
11
+ hideOthers: "Hide Others",
12
+ showAll: "Show All",
13
+
14
+ // Window roles
15
+ minimize: "Minimize",
16
+ zoom: "Zoom",
17
+ close: "Close",
18
+ bringAllToFront: "Bring All To Front",
19
+ cycleThroughWindows: "Cycle Through Windows",
20
+ enterFullScreen: "Enter Full Screen",
21
+ exitFullScreen: "Exit Full Screen",
22
+ toggleFullScreen: "Toggle Full Screen",
23
+
24
+ // Standard edit roles
25
+ undo: "Undo",
26
+ redo: "Redo",
27
+ cut: "Cut",
28
+ copy: "Copy",
29
+ paste: "Paste",
30
+ pasteAndMatchStyle: "Paste and Match Style",
31
+ delete: "Delete",
32
+ selectAll: "Select All",
33
+
34
+ // Speech roles
35
+ startSpeaking: "Start Speaking",
36
+ stopSpeaking: "Stop Speaking",
37
+
38
+ // Help
39
+ showHelp: "Show Help",
40
+
41
+ // Movement - basic
42
+ moveForward: "Move Forward",
43
+ moveBackward: "Move Backward",
44
+ moveLeft: "Move Left",
45
+ moveRight: "Move Right",
46
+ moveUp: "Move Up",
47
+ moveDown: "Move Down",
48
+
49
+ // Movement - by word
50
+ moveWordForward: "Move Word Forward",
51
+ moveWordBackward: "Move Word Backward",
52
+ moveWordLeft: "Move Word Left",
53
+ moveWordRight: "Move Word Right",
54
+
55
+ // Movement - by line
56
+ moveToBeginningOfLine: "Move to Beginning of Line",
57
+ moveToEndOfLine: "Move to End of Line",
58
+ moveToLeftEndOfLine: "Move to Left End of Line",
59
+ moveToRightEndOfLine: "Move to Right End of Line",
60
+
61
+ // Movement - by paragraph
62
+ moveToBeginningOfParagraph: "Move to Beginning of Paragraph",
63
+ moveToEndOfParagraph: "Move to End of Paragraph",
64
+ moveParagraphForward: "Move Paragraph Forward",
65
+ moveParagraphBackward: "Move Paragraph Backward",
66
+
67
+ // Movement - by document
68
+ moveToBeginningOfDocument: "Move to Beginning of Document",
69
+ moveToEndOfDocument: "Move to End of Document",
70
+
71
+ // Movement with selection - basic
72
+ moveForwardAndModifySelection: "Move Forward and Modify Selection",
73
+ moveBackwardAndModifySelection: "Move Backward and Modify Selection",
74
+ moveLeftAndModifySelection: "Move Left and Modify Selection",
75
+ moveRightAndModifySelection: "Move Right and Modify Selection",
76
+ moveUpAndModifySelection: "Move Up and Modify Selection",
77
+ moveDownAndModifySelection: "Move Down and Modify Selection",
78
+
79
+ // Movement with selection - by word
80
+ moveWordForwardAndModifySelection: "Move Word Forward and Modify Selection",
81
+ moveWordBackwardAndModifySelection: "Move Word Backward and Modify Selection",
82
+ moveWordLeftAndModifySelection: "Move Word Left and Modify Selection",
83
+ moveWordRightAndModifySelection: "Move Word Right and Modify Selection",
84
+
85
+ // Movement with selection - by line
86
+ moveToBeginningOfLineAndModifySelection:
87
+ "Move to Beginning of Line and Modify Selection",
88
+ moveToEndOfLineAndModifySelection:
89
+ "Move to End of Line and Modify Selection",
90
+ moveToLeftEndOfLineAndModifySelection:
91
+ "Move to Left End of Line and Modify Selection",
92
+ moveToRightEndOfLineAndModifySelection:
93
+ "Move to Right End of Line and Modify Selection",
94
+
95
+ // Movement with selection - by paragraph
96
+ moveToBeginningOfParagraphAndModifySelection:
97
+ "Move to Beginning of Paragraph and Modify Selection",
98
+ moveToEndOfParagraphAndModifySelection:
99
+ "Move to End of Paragraph and Modify Selection",
100
+ moveParagraphForwardAndModifySelection:
101
+ "Move Paragraph Forward and Modify Selection",
102
+ moveParagraphBackwardAndModifySelection:
103
+ "Move Paragraph Backward and Modify Selection",
104
+
105
+ // Movement with selection - by document
106
+ moveToBeginningOfDocumentAndModifySelection:
107
+ "Move to Beginning of Document and Modify Selection",
108
+ moveToEndOfDocumentAndModifySelection:
109
+ "Move to End of Document and Modify Selection",
110
+
111
+ // Page movement
112
+ pageUp: "Page Up",
113
+ pageDown: "Page Down",
114
+ pageUpAndModifySelection: "Page Up and Modify Selection",
115
+ pageDownAndModifySelection: "Page Down and Modify Selection",
116
+
117
+ // Scrolling
118
+ scrollLineUp: "Scroll Line Up",
119
+ scrollLineDown: "Scroll Line Down",
120
+ scrollPageUp: "Scroll Page Up",
121
+ scrollPageDown: "Scroll Page Down",
122
+ scrollToBeginningOfDocument: "Scroll to Beginning of Document",
123
+ scrollToEndOfDocument: "Scroll to End of Document",
124
+ centerSelectionInVisibleArea: "Center Selection in Visible Area",
125
+
126
+ // Deletion - character
127
+ deleteBackward: "Delete Backward",
128
+ deleteForward: "Delete Forward",
129
+ deleteBackwardByDecomposingPreviousCharacter:
130
+ "Delete Backward by Decomposing Previous Character",
131
+
132
+ // Deletion - word
133
+ deleteWordBackward: "Delete Word Backward",
134
+ deleteWordForward: "Delete Word Forward",
135
+
136
+ // Deletion - line
137
+ deleteToBeginningOfLine: "Delete to Beginning of Line",
138
+ deleteToEndOfLine: "Delete to End of Line",
139
+
140
+ // Deletion - paragraph
141
+ deleteToBeginningOfParagraph: "Delete to Beginning of Paragraph",
142
+ deleteToEndOfParagraph: "Delete to End of Paragraph",
143
+
144
+ // Selection
145
+ selectWord: "Select Word",
146
+ selectLine: "Select Line",
147
+ selectParagraph: "Select Paragraph",
148
+ selectToMark: "Select to Mark",
149
+ setMark: "Set Mark",
150
+ swapWithMark: "Swap with Mark",
151
+ deleteToMark: "Delete to Mark",
152
+
153
+ // Text transformation
154
+ capitalizeWord: "Capitalize Word",
155
+ uppercaseWord: "Uppercase Word",
156
+ lowercaseWord: "Lowercase Word",
157
+ transpose: "Transpose",
158
+ transposeWords: "Transpose Words",
159
+
160
+ // Insertion
161
+ insertNewline: "Insert Newline",
162
+ insertLineBreak: "Insert Line Break",
163
+ insertParagraphSeparator: "Insert Paragraph Separator",
164
+ insertTab: "Insert Tab",
165
+ insertBacktab: "Insert Backtab",
166
+ insertTabIgnoringFieldEditor: "Insert Tab Ignoring Field Editor",
167
+ insertNewlineIgnoringFieldEditor: "Insert Newline Ignoring Field Editor",
168
+
169
+ // Kill ring (Emacs-style)
170
+ yank: "Yank",
171
+ yankAndSelect: "Yank and Select",
172
+
173
+ // Completion
174
+ complete: "Complete",
175
+ cancelOperation: "Cancel Operation",
176
+
177
+ // Indentation
178
+ indent: "Indent",
179
+ };
180
+
181
+ export type MenuRole = keyof typeof roleLabelMap;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "electrobun",
3
- "version": "1.11.4-beta.0",
3
+ "version": "1.11.5-beta.2",
4
4
  "description": "Build ultra fast, tiny, and cross-platform desktop apps with Typescript.",
5
5
  "license": "MIT",
6
6
  "author": "Blackboard Technologies Inc.",