/** * The desktop app's bridge (window.electronAPI), typed for plugins. It exists * only inside the Termix desktop app; check isElectron before calling it. */ import type { PluginSshPromptRequest } from "./backend.js"; export interface ServerConfig { serverUrl?: string; allowInvalidCertificate?: boolean; [key: string]: unknown; } export interface ConnectionTestResult { success: boolean; error?: string; [key: string]: unknown; } export interface DialogOptions { title?: string; defaultPath?: string; buttonLabel?: string; filters?: Array<{ name: string; extensions: string[] }>; properties?: string[]; [key: string]: unknown; } export interface DialogResult { canceled: boolean; filePath?: string; filePaths?: string[]; [key: string]: unknown; } export interface LocalDirectoryResult { success: boolean; path: string; parent?: string; entries: LocalFileEntry[]; error?: string; } export interface LocalCollectedFile { path: string; name: string; relativePath: string; size: number; created?: string; modified: string; } export type LocalPathMutationResult = | ({ success: true } & Partial) | { success: false; error?: string }; export interface ElectronAPI { getAppVersion: () => Promise; setZoomFactor?: (factor: number) => void; getPlatform: () => Promise; getSetting?: (key: string) => Promise; setSetting?: (key: string, value: string) => Promise; getServerConfig: () => Promise; saveServerConfig: (config: ServerConfig) => Promise<{ success: boolean }>; testServerConnection: ( serverUrl: string, allowInvalidCertificate?: boolean, ) => Promise; answerC2SAuth: (id: string, answer: string | null) => Promise; onC2SAuthPrompt: ( callback: (prompt: { id: string; closed?: boolean; tunnelName?: string; request?: PluginSshPromptRequest; }) => void, ) => () => void; getC2STunnelConfig: () => Promise; saveC2STunnelConfig: ( config: unknown[], ) => Promise<{ success: boolean; error?: string }>; checkLocalPortAvailable: ( host: string, port: number, ) => Promise<{ available: boolean; error?: string }>; getC2STunnelPresetDefaultName: () => Promise; startC2STunnel: ( tunnel: unknown, index: number, authToken?: string, ) => Promise<{ success: boolean; tunnelName?: string; error?: string; code?: string; }>; testC2STunnel: ( tunnel: unknown, index: number, authToken?: string, ) => Promise<{ success: boolean; message?: string; error?: string; code?: string; }>; stopC2STunnel: ( tunnelName: string, ) => Promise<{ success: boolean; error?: string }>; getC2STunnelStatuses: () => Promise>; onC2STunnelStatuses?: ( callback: (statuses: Record) => void, ) => () => void; startC2SAutoStartTunnels: () => Promise<{ success: boolean; started: number; errors: string[]; }>; /** Where the linked server relays client tunnel streams, a /plugin-ws/ path. */ setC2SRelayPath?: ( relayPath: string, ) => Promise<{ success: boolean; error?: string }>; onCloseActiveTab?: (callback: () => void) => () => void; clearSessionCookies: () => Promise; getSessionCookie: ( name: string, targetUrl?: string, ) => Promise; waitForSessionCookie: ( name: string, targetUrl?: string, previousValue?: string | null, timeoutMs?: number, ) => Promise<{ success: boolean; value?: string; error?: string }>; showSaveDialog: (options: DialogOptions) => Promise; showOpenDialog: (options: DialogOptions) => Promise; getLocalHomeDirectory?: () => Promise; listLocalDirectory?: (path?: string) => Promise; statLocalPaths?: ( paths: string[], ) => Promise< Array< | (LocalFileEntry & { success: true }) | { success: false; path: string; error?: string } > >; collectLocalFiles?: (paths: string[]) => Promise<{ success: boolean; files: LocalCollectedFile[]; truncated?: boolean; error?: string; }>; createLocalFolder?: ( parentPath: string, folderName: string, ) => Promise; renameLocalPath?: ( path: string, newName: string, ) => Promise; trashLocalPath?: (path: string) => Promise; chmodLocalPath?: ( path: string, permissions: string, ) => Promise; openExternalEditor: (fileData: { fileName: string; content: string; encoding?: "utf8" | "base64"; editorPath?: string | null; }) => Promise<{ success: boolean; editId?: string; path?: string; error?: string; }>; closeExternalEditor: (editId: string) => Promise<{ success: boolean; error?: string; }>; onExternalEditorSaved?: ( callback: (payload: { editId: string; content: string; encoding: "utf8"; path: string; }) => void, ) => () => void; onUpdateAvailable: (callback: () => void) => void; onUpdateDownloaded: (callback: () => void) => void; removeAllListeners: (channel: string) => void; isElectron: boolean; isDev: boolean; invoke: (channel: string, ...args: unknown[]) => Promise; createTempFile: (fileData: { fileName: string; content: string; encoding?: "base64" | "utf8"; }) => Promise<{ success: boolean; tempId?: string; path?: string; error?: string; }>; createTempFolder: (folderData: { folderName: string; files: Array<{ relativePath: string; content: string; encoding?: "base64" | "utf8"; }>; }) => Promise<{ success: boolean; tempId?: string; path?: string; error?: string; }>; startDragToDesktop: (dragData: { tempId: string; fileName: string; }) => Promise<{ success: boolean; error?: string; }>; cleanupTempFile: (tempId: string) => Promise<{ success: boolean; error?: string; }>; startLocalTerminal(dimensions: { cols: number; rows: number; shell?: "default" | "wsl"; }): Promise<{ sessionId: string; shell: string }>; readyLocalTerminal(sessionId: string): Promise; writeLocalTerminal(sessionId: string, data: string): Promise; resizeLocalTerminal( sessionId: string, cols: number, rows: number, ): Promise; closeLocalTerminal(sessionId: string): Promise; onLocalTerminalData( sessionId: string, callback: (data: string) => void, ): () => void; onLocalTerminalExit( sessionId: string, callback: (exitCode: number) => void, ): () => void; /** Local disk browsing for the dual-pane file manager (desktop only). */ localFs?: { home: () => Promise>; list: (dirPath: string) => Promise>; mkdir: ( parentPath: string, name: string, ) => Promise>; createFile: ( parentPath: string, name: string, ) => Promise>; rename: ( oldPath: string, newName: string, ) => Promise>; trash: (paths: string[]) => Promise>; ensureDir: ( dirPath: string, rootPath?: string, ) => Promise>; exists: (paths: string[]) => Promise>; walk: (paths: string[]) => Promise>; reveal: (targetPath: string) => Promise>; open: (targetPath: string) => Promise>; }; /** Streamed local<->remote transfers driven by the main process. */ localTransfer?: { /** The /plugin-api/ path that serves the streaming routes. */ setApiPath?: (apiPath: string) => Promise>; upload: ( options: LocalUploadRequest, ) => Promise>; download: ( options: LocalDownloadRequest, ) => Promise>; cancel: ( transferId: string, ) => Promise>; onProgress: ( callback: (payload: LocalTransferProgress) => void, ) => () => void; }; } export type LocalFsResult = ({ success: true } & T) | { success: false; error: string; code?: string }; export interface LocalFsHomeInfo { home: string; separator: string; platform: string; } export interface LocalFileEntry { name: string; path: string; type: "file" | "directory" | "link" | "other"; created?: string; modified?: string; permissions?: string; owner?: string; group?: string; size: number; modifiedTimestamp?: number; linkTarget?: string; hidden: boolean; } export interface LocalDirectoryListing { path: string; parent: string | null; entries: LocalFileEntry[]; } export interface LocalTrashResult { trashed: number; failed: Array<{ path: string; error: string }>; } export interface LocalWalkFile { localPath: string; /** Path relative to the drop root; "/"-separated and includes the root's own name. */ relativePath: string; size: number; } export interface LocalWalkResult { files: LocalWalkFile[]; emptyDirs: string[]; totalBytes: number; } /** * Which Termix backend a transfer talks to. The main process resolves the * actual URL and credentials for the origin; the renderer never supplies them. */ export type LocalTransferOrigin = "local" | "remote"; export interface LocalUploadRequest { transferId: string; origin: LocalTransferOrigin; fields: Record; localPath: string; fileName: string; deviceId?: string; /** The renderer's token for the embedded backend (origin "local"); sent as a Bearer header. */ authToken?: string; } export interface LocalDownloadRequest { transferId: string; origin: LocalTransferOrigin; body: Record; destPath: string; /** Selected download folder; destPath must resolve strictly inside it (code EINVAL otherwise). */ rootPath: string; expectedSize?: number; /** Replace an existing file at destPath; otherwise the transfer is refused with code EEXIST. */ overwrite?: boolean; deviceId?: string; /** The renderer's token for the embedded backend (origin "local"); sent as a Bearer header. */ authToken?: string; } export interface LocalTransferProgress { transferId: string; transferred: number; total?: number; } declare global { interface Window { electronAPI: ElectronAPI; IS_ELECTRON: boolean; electronClipboard?: { writeText(text: string): Promise; readText(): Promise; }; } }