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.
- package/dist/api/bun/core/ApplicationMenu.ts +1 -26
- package/dist/api/bun/core/ContextMenu.ts +1 -27
- package/dist/api/bun/core/Updater.ts +35 -70
- package/dist/api/bun/core/menuRoles.ts +181 -0
- package/package.json +1 -1
- package/src/cli/index.ts +175 -1001
|
@@ -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
|
|
854
|
-
// Structure: extractionDir/{
|
|
855
|
-
const
|
|
856
|
-
|
|
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
|
|
860
|
-
|
|
861
|
-
|
|
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
|
-
|
|
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,
|
|
950
|
-
|
|
951
|
-
|
|
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
|
|
955
|
-
renameSync(newAppBundlePath,
|
|
923
|
+
// Move new app bundle directory to app location
|
|
924
|
+
renameSync(newAppBundlePath, appBundleDir);
|
|
956
925
|
|
|
957
|
-
//
|
|
958
|
-
const
|
|
959
|
-
|
|
960
|
-
|
|
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
|
-
//
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
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
|
|
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", `"${
|
|
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