import type { AppSettings, ConnectionConfig, ConnectionsFile, EntryType, FileEntry, Result, SavedConnection, SshConfigHost, Stack, TransferSnapshot, } from "./types"; /** Everything an editor window needs to know about the file it hosts. */ export interface EditorOpenContext { sessionId: string; path: string; name: string; sessionTitle: string; } // Structurally equivalent to electrobun's ElectrobunRPCSchema, written in // resolved form so this file stays importable from the Angular bundle without // pulling in bun-only type modules. // // bun.requests = calls the webview makes, handled in src/bun/rpc-handlers.ts // webview.messages = push events from bun, handled in ui rpc.service.ts // // ONE schema serves both window kinds (main + editor): electrobun allows // partial handler sets, and messages nobody listens to are silently dropped, // so each side only implements/hears what applies to it. export type AppRPC = { bun: { requests: { // sessions connect: { params: { config: ConnectionConfig }; response: Result<{ sessionId: string; homeDir: string; fsKey: string }>; }; disconnect: { params: { sessionId: string }; response: Result }; // filesystem (uniform across "local" and sftp sessions) listDir: { params: { sessionId: string; path: string | null }; // null -> home dir response: Result<{ path: string; entries: FileEntry[] }>; }; // Local dirs to fs.watch for auto-refresh (the active tab's local panels); // a change pushes pathRefresh. Remote panels are polled UI-side instead. setWatchedDirs: { params: { dirs: string[] }; response: Result }; mkdir: { params: { sessionId: string; path: string }; response: Result }; createFile: { params: { sessionId: string; path: string }; response: Result }; rename: { params: { sessionId: string; oldPath: string; newPath: string }; response: Result; }; deleteEntries: { params: { sessionId: string; entries: { path: string; type: EntryType }[] }; response: Result; }; chmod: { params: { sessionId: string; paths: string[]; mode: number }; response: Result; }; // transfers enqueueTransfers: { params: { srcSessionId: string; items: { path: string; type: EntryType }[]; dstSessionId: string; dstDir: string; }; response: Result<{ jobIds: string[] }>; }; cancelTransfer: { params: { jobId: string }; response: Result }; dismissTransfer: { params: { jobId: string }; response: Result }; // connection store getConnections: { params: Record; response: Result<{ store: ConnectionsFile }>; }; upsertConnection: { params: { connection: SavedConnection }; response: Result<{ id: string }>; }; deleteConnection: { params: { id: string }; response: Result }; upsertStack: { params: { stack: Stack }; response: Result<{ id: string }> }; deleteStack: { params: { id: string }; response: Result }; /** Native file picker + parse of a WinSCP.ini export into the store. */ importWinScp: { params: Record; response: Result<{ connections: number; stacks: number; skipped: number }>; }; getSshConfigHosts: { params: Record; response: Result<{ hosts: SshConfigHost[] }>; }; // settings getSettings: { params: Record; response: Result<{ settings: AppSettings }>; }; saveSettings: { params: { settings: AppSettings }; response: Result }; // file editing readFile: { params: { sessionId: string; path: string }; response: Result<{ content: string }>; }; writeFile: { params: { sessionId: string; path: string; content: string }; response: Result; }; editExternal: { params: { sessionId: string; path: string }; response: Result<{ watching: boolean }>; }; pickEditorApp: { params: Record; response: Result<{ path: string | null }>; }; pickKeyFile: { params: Record; response: Result<{ path: string | null }>; }; getEditLeftovers: { params: Record; response: Result<{ names: string[] }>; }; clearEditLeftovers: { params: Record; response: Result }; // local shell integration /** * Shell actions the context menu triggers on a LOCAL path. The menu * itself is HTML (ui/src/app/context-menu.ts) — electrobun's native * context menu is a no-op on Linux — but these three still need the * bun side's Utils. */ shellItemAction: { params: { action: "reveal" | "open" | "copyPath"; path: string }; response: Result; }; moveToTrash: { params: { path: string }; response: Result }; /** Native About box (name, Build n, commit, engine versions). */ showAbout: { params: Record; response: Result }; /** Windows drive roots for the local panel ("C:\", …); empty elsewhere. */ listDrives: { params: Record; response: Result<{ drives: string[] }> }; // editor windows /** Main window only: open (or focus) a native editor window. */ openEditor: { params: EditorOpenContext; response: Result }; /** Editor windows only — handled by that window's own rpc closure. */ editorSave: { params: { content: string }; response: Result }; editorClose: { params: Record; response: Result }; // connection-manager window /** Main window: open (or focus) the single connection-manager window. */ openConnectionManager: { params: Record; response: Result }; /** Manager window only: connect/open-stack run in the MAIN window. */ managerConnect: { params: { id: string }; response: Result }; managerOpenStack: { params: { id: string }; response: Result }; managerClose: { params: Record; response: Result }; // custom window chrome (win7 skin — the window is frameless, so the // webview's title bar owns the controls). Served by every window kind. windowMinimize: { params: Record; response: Result }; /** Maximize/restore in one call; responds with the resulting state. */ windowToggleMaximize: { params: Record; response: Result<{ maximized: boolean }>; }; windowClose: { params: Record; response: Result }; /** First paint: the title bar needs the state before any resize fires. */ windowIsMaximized: { params: Record; response: Result<{ maximized: boolean }>; }; /** * STOPGAP — the two halves of the synthesized resize. A frameless GTK * window has no resize borders and electrobun exposes no native resize * gesture, so the webview drags its own handles and moves the frame * itself. Delete both with the rest of the hack; see the removal * checklist in ui/src/app/skins/7like/window-resize/window-resize.ts. */ windowGetFrame: { params: Record; response: Result<{ x: number; y: number; width: number; height: number }>; }; windowSetFrame: { params: { x: number; y: number; width: number; height: number }; response: Result; }; // auto-updater (electrobun Updater) /** Main window: re-run the update check on demand (menu item). */ checkForUpdate: { params: Record; response: Result<{ updateAvailable: boolean; version: string }>; }; /** Download the pending update (patch-chain, else full). Progress arrives via updateStatus. */ downloadUpdate: { params: Record; response: Result }; /** Swap in the downloaded update and relaunch — quits the app, so may not resolve. */ applyUpdate: { params: Record; response: Result }; }; messages: { /** Editor → bun draft mirror; safety net for the non-cancellable native close. */ editorState: { dirty: boolean; content: string }; }; }; webview: { requests: Record; messages: { transferUpdate: TransferSnapshot; sessionClosed: { sessionId: string; reason: string }; menuAction: { action: string }; /** bun → editor windows when the main window saves settings. */ settingsChanged: { settings: AppSettings }; /** bun → main after an editor save, so matching panels refresh. */ pathRefresh: { sessionId: string; dir: string }; /** bun → one editor: run the in-window close flow (dirty prompt). */ editorCloseRequest: Record; /** bun → main: the modal connection manager opened/closed. */ managerState: { open: boolean }; /** * bun → any window: maximize state changed, so the custom title bar can * swap its maximize/restore glyph. electrobun has no maximize event — * this is derived from the window's `resize` event (see windows.ts * trackMaximizeState), which also covers OS-initiated Aero Snap. */ windowState: { maximized: boolean }; /** bun → main: the manager window changed the connection store. */ connectionsChanged: Record; /** * bun → main: auto-updater lifecycle, drives the tab-bar update pill. * "ready" is not a phase — the UI derives it from downloadUpdate resolving ok. */ updateStatus: { phase: "available" | "downloading" | "error"; version?: string; progress?: number; error?: string; }; }; }; };