macOS application menu

Standard menus (App: hide/quit; Edit: clipboard roles enabling Cmd+C/V/X/Z;
View: Refresh Cmd+R + fullscreen; Window: minimize/zoom/cycle) plus File with
New/Close Tab (Cmd+T/W), New Folder/File, and a Connect submenu built from
saved connections (folders as nested submenus, Connection Manager entry).
Custom actions forward to the webview as menuAction messages; the menu rebuilds
whenever connections change. macOS-only per electrobun support.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Jokin
2026-07-11 19:17:25 +02:00
co-authored by Claude Fable 5
parent 20e5703d29
commit 2d7e7b44cd
6 changed files with 165 additions and 5 deletions
+8
View File
@@ -18,6 +18,7 @@ export class RpcService {
// (this froze the app), and signal coalescing would drop progress snapshots.
private transferListeners = new Set<(s: TransferSnapshot) => void>();
private closedListeners = new Set<(e: SessionClosedEvent) => void>();
private menuListeners = new Set<(action: string) => void>();
private rpc: ReturnType<typeof Electroview.defineRPC<AppRPC>> | undefined;
private ready: Promise<void> = Promise.resolve();
@@ -36,6 +37,9 @@ export class RpcService {
sessionClosed: (event) => {
for (const listener of this.closedListeners) listener(event);
},
menuAction: ({ action }) => {
for (const listener of this.menuListeners) listener(action);
},
},
},
});
@@ -60,6 +64,10 @@ export class RpcService {
this.closedListeners.add(listener);
}
onMenuAction(listener: (action: string) => void): void {
this.menuListeners.add(listener);
}
/** Typed request proxy: `await rpc.request.listDir({...})`. Waits for the bridge. */
get request() {
const rpc = this.rpc;