Editor opens in native windows (one per file)
Real BrowserWindow per edited file with its own RPC instance, deduped by sessionId+path. The same Angular bundle boots an editor-only root via preload globals. Native close isn't cancellable in electrobun, so the editor mirrors its draft to bun; traffic-light closes get a native Save/Discard prompt after the fact, and failed saves park the draft in the edit-leftovers dir. In-window closes (Cmd+W, Close) use a styled prompt; the quit guard now also counts unsaved editors. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { Electroview } from 'electrobun/view';
|
||||
import type { AppRPC } from '@shared/rpc';
|
||||
import type { TransferSnapshot } from '@shared/types';
|
||||
import type { AppSettings, TransferSnapshot } from '@shared/types';
|
||||
|
||||
// Matches MAX_REQUEST_TIME_MS on the bun side — SFTP ops can be slow.
|
||||
const MAX_REQUEST_TIME_MS = 10 * 60_000;
|
||||
@@ -19,6 +19,9 @@ export class RpcService {
|
||||
private transferListeners = new Set<(s: TransferSnapshot) => void>();
|
||||
private closedListeners = new Set<(e: SessionClosedEvent) => void>();
|
||||
private menuListeners = new Set<(action: string) => void>();
|
||||
private settingsListeners = new Set<(settings: AppSettings) => void>();
|
||||
private pathRefreshListeners = new Set<(e: { sessionId: string; dir: string }) => void>();
|
||||
private editorCloseListeners = new Set<() => void>();
|
||||
|
||||
private rpc: ReturnType<typeof Electroview.defineRPC<AppRPC>> | undefined;
|
||||
private ready: Promise<void> = Promise.resolve();
|
||||
@@ -40,6 +43,15 @@ export class RpcService {
|
||||
menuAction: ({ action }) => {
|
||||
for (const listener of this.menuListeners) listener(action);
|
||||
},
|
||||
settingsChanged: ({ settings }) => {
|
||||
for (const listener of this.settingsListeners) listener(settings);
|
||||
},
|
||||
pathRefresh: (event) => {
|
||||
for (const listener of this.pathRefreshListeners) listener(event);
|
||||
},
|
||||
editorCloseRequest: () => {
|
||||
for (const listener of this.editorCloseListeners) listener();
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
@@ -68,6 +80,18 @@ export class RpcService {
|
||||
this.menuListeners.add(listener);
|
||||
}
|
||||
|
||||
onSettingsChanged(listener: (settings: AppSettings) => void): void {
|
||||
this.settingsListeners.add(listener);
|
||||
}
|
||||
|
||||
onPathRefresh(listener: (event: { sessionId: string; dir: string }) => void): void {
|
||||
this.pathRefreshListeners.add(listener);
|
||||
}
|
||||
|
||||
onEditorCloseRequest(listener: () => void): void {
|
||||
this.editorCloseListeners.add(listener);
|
||||
}
|
||||
|
||||
/** Typed request proxy: `await rpc.request.listDir({...})`. Waits for the bridge. */
|
||||
get request() {
|
||||
const rpc = this.rpc;
|
||||
@@ -87,4 +111,20 @@ export class RpcService {
|
||||
});
|
||||
}
|
||||
|
||||
/** Fire-and-forget message proxy: `rpc.send.editorState({...})`. Waits for the bridge. */
|
||||
get send() {
|
||||
const rpc = this.rpc;
|
||||
if (!rpc) throw new Error('RPC is only available inside the desktop app');
|
||||
const ready = this.ready;
|
||||
return new Proxy({} as (typeof rpc)['send'], {
|
||||
get:
|
||||
(_target, method: string) =>
|
||||
(payload?: unknown) => {
|
||||
void ready.then(() => {
|
||||
const methods = rpc.send as unknown as Record<string, (p?: unknown) => void>;
|
||||
methods[method](payload);
|
||||
});
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user