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>
181 lines
5.6 KiB
TypeScript
181 lines
5.6 KiB
TypeScript
import { Utils, type BrowserWindow } from "electrobun/bun";
|
|
import { BRANDING } from "../shared/branding";
|
|
import type { EditorOpenContext } from "../shared/rpc";
|
|
import type { AppSettings } from "../shared/types";
|
|
import { writeFileText, type EditManager } from "./edits";
|
|
import type { SessionRegistry } from "./fs/registry";
|
|
import { createEditorRPC } from "./rpc-handlers";
|
|
import { openSecondaryWindow, WindowRegistry } from "./windows";
|
|
import type { SettingsStore } from "./store/settings";
|
|
|
|
const EDITOR_WIDTH = 900;
|
|
const EDITOR_HEIGHT = 680;
|
|
|
|
interface EditorRecord {
|
|
win: BrowserWindow;
|
|
ctx: EditorOpenContext;
|
|
dirty: boolean;
|
|
/** Last mirrored content — what the native-close Save fallback writes. */
|
|
draft: string | null;
|
|
/** Set by editorClose: the webview already resolved its dirty prompt. */
|
|
suppressPrompt: boolean;
|
|
rpc: ReturnType<typeof createEditorRPC>;
|
|
}
|
|
|
|
export interface EditorWindowsDeps {
|
|
registry: SessionRegistry;
|
|
settings: SettingsStore;
|
|
edits: EditManager;
|
|
/** The once-resolved dev/prod view url. */
|
|
url: string;
|
|
mainFrame: () => { x: number; y: number };
|
|
/** Late-bound senders targeting the MAIN window's webview. */
|
|
notifyMain: { pathRefresh: (info: { sessionId: string; dir: string }) => void };
|
|
}
|
|
|
|
/**
|
|
* One native window per edited file (key: sessionId+path). The webview owns
|
|
* the styled dirty prompt for closes it can see (⌘W, Close button); the
|
|
* native traffic-light close is NOT cancellable in electrobun, so the draft
|
|
* mirrored via editorState lets us offer a native Save/Discard box after the
|
|
* window is already gone. A failed fallback save parks the draft in the
|
|
* edits leftovers dir (offered back at next boot).
|
|
*/
|
|
export class EditorWindows {
|
|
private windows: WindowRegistry<EditorRecord>;
|
|
|
|
constructor(private deps: EditorWindowsDeps) {
|
|
this.windows = new WindowRegistry(deps.mainFrame);
|
|
}
|
|
|
|
open(ctx: EditorOpenContext): void {
|
|
const key = `${ctx.sessionId} |