P1: typed RPC bridge + FileSystemProvider + dual-panel local browsing

- shared/types + shared/rpc: full wire contract (Result envelope, no exceptions cross the bridge)
- FileSystemProvider interface, LocalProvider, SessionRegistry with built-in 'local'
- walk.ts: provider-generic recursive walk + removeRecursive
- rpc-handlers wired into BrowserWindow; 10min request timeout for slow SFTP ops
- Angular: RpcService (Electroview bridge + plain-browser guard), PanelsService with
  signals-based PanelState (sort/cursor/selection), FilePanel component with
  keyboard nav (arrows/Space/Enter/Backspace/Tab), path bar, toasts
- verified: both panels listDir home over the bridge in the running app

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Jokin
2026-07-11 13:35:02 +02:00
co-authored by Claude Fable 5
parent 8092cb0c08
commit a150c5ee2e
17 changed files with 1062 additions and 15 deletions
+68
View File
@@ -0,0 +1,68 @@
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 };
export interface ConnectionConfig {
host: string;
port: number;
username: string;
auth: AuthConfig;
initialPath?: string | null;
}
export interface SavedConnection extends ConnectionConfig {
id: string;
folderId: string | null;
name: string;
}
export interface ConnFolder {
id: string;
name: string;
}
export interface ConnectionsFile {
version: 1;
folders: ConnFolder[];
connections: SavedConnection[];
}
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;
}