Folders become Stacks (connections.json v2, no v1 migration): collapsible stack-first list, open-stack-in-tabs (first reuses a pristine tab), drag connections between stacks. More... reveals a per-connection Local directory applied to the other panel when local and untouched. Import reads a WinSCP.ini export incl. deobfuscated passwords. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
137 lines
3.7 KiB
TypeScript
137 lines
3.7 KiB
TypeScript
export type EntryType = "file" | "dir" | "symlink";
|
|
|
|
export interface FileEntry {
|
|
name: string;
|
|
path: string; // absolute path on its filesystem
|
|
type: EntryType;
|
|
size: number; // bytes
|
|
mtime: number; // epoch milliseconds
|
|
mode: number; // permission bits only (masked & 0o7777)
|
|
}
|
|
|
|
/** Every RPC response uses this envelope; errors never cross the bridge as exceptions. */
|
|
export type Result<T = object> = ({ ok: true } & T) | { ok: false; error: string };
|
|
|
|
export type AuthConfig =
|
|
| { kind: "password"; password: string }
|
|
| { kind: "key"; privateKeyPath: string; passphrase?: string }
|
|
| { kind: "agent" };
|
|
|
|
export interface ConnectionConfig {
|
|
host: string;
|
|
port: number;
|
|
username: string;
|
|
auth: AuthConfig;
|
|
initialPath?: string | null;
|
|
}
|
|
|
|
export interface SavedConnection extends ConnectionConfig {
|
|
id: string;
|
|
stackId: string | null;
|
|
name: string;
|
|
/** Other-panel local dir applied when that panel is local and untouched. */
|
|
localDir?: string | null;
|
|
}
|
|
|
|
/** A named group of connections that can be opened together as tabs. */
|
|
export interface Stack {
|
|
id: string;
|
|
name: string;
|
|
/** Collapsed in the connection-manager list (UI state, persisted). */
|
|
collapsed?: boolean;
|
|
}
|
|
|
|
/** A concrete host alias resolved from ~/.ssh/config (read-only). */
|
|
export interface SshConfigHost {
|
|
name: string; // the Host alias as written
|
|
hostname: string; // resolved HostName (defaults to alias)
|
|
port: number;
|
|
username: string;
|
|
identityFile?: string; // first existing IdentityFile, absolute path
|
|
unsupported?: "proxy"; // ProxyJump/ProxyCommand present — direct connect can't work
|
|
}
|
|
|
|
export interface ConnectionsFile {
|
|
version: 2;
|
|
stacks: Stack[];
|
|
connections: SavedConnection[];
|
|
}
|
|
|
|
export type AccentTone =
|
|
| "graphite"
|
|
| "blue"
|
|
| "indigo"
|
|
| "purple"
|
|
| "rose"
|
|
| "orange"
|
|
| "green"
|
|
| "teal";
|
|
|
|
export interface AppSettings {
|
|
theme: "system" | "light" | "dark";
|
|
/** Dark appearance: grey ("dark") or near-black AMOLED ("darker"). */
|
|
darkTone: "dark" | "darker";
|
|
/** Accent tone for chrome and selection tint; graphite = neutral zinc. */
|
|
accent: AccentTone;
|
|
/** File-list icons: plain neutral glyphs, or per-category glyphs and colors. */
|
|
iconColors: "plain" | "multi";
|
|
speedUnit: "MBps" | "Mbps";
|
|
/** Size of the panel interface: file list, path bar, header controls. */
|
|
textSize: "compact" | "default" | "large";
|
|
/** Run transfer jobs one at a time, or several in parallel. */
|
|
transferMode: "single" | "multi";
|
|
/** How many jobs run at once when transferMode is "multi". */
|
|
transferConcurrency: number;
|
|
/** Remove completed transfers from the list automatically. */
|
|
autoClearCompleted: boolean;
|
|
/** Edit files with the built-in editor or an external application. */
|
|
editor: "builtin" | "custom";
|
|
/** Full path to the external editor .app; empty = OS default application. */
|
|
editorApp: string;
|
|
}
|
|
|
|
export const DEFAULT_SETTINGS: AppSettings = {
|
|
theme: "system",
|
|
darkTone: "dark",
|
|
accent: "blue",
|
|
iconColors: "multi",
|
|
speedUnit: "MBps",
|
|
textSize: "default",
|
|
transferMode: "single",
|
|
transferConcurrency: 3,
|
|
autoClearCompleted: true,
|
|
editor: "builtin",
|
|
editorApp: "",
|
|
};
|
|
|
|
export function effectiveConcurrency(settings: AppSettings): number {
|
|
if (settings.transferMode === "single") return 1;
|
|
return Math.max(1, Math.min(8, Math.round(settings.transferConcurrency) || 1));
|
|
}
|
|
|
|
export type TransferState =
|
|
| "queued"
|
|
| "scanning"
|
|
| "running"
|
|
| "completed"
|
|
| "failed"
|
|
| "cancelled";
|
|
|
|
export interface TransferSnapshot {
|
|
id: string;
|
|
name: string; // display name (basename of source)
|
|
srcSessionId: string;
|
|
srcPath: string;
|
|
dstSessionId: string;
|
|
dstDir: string;
|
|
state: TransferState;
|
|
totalFiles: number;
|
|
totalBytes: number;
|
|
doneFiles: number;
|
|
doneBytes: number;
|
|
currentFile: string;
|
|
currentFileBytes: number;
|
|
currentFileTotal: number;
|
|
error?: string;
|
|
}
|