From 7c1729d92d80782b8c9949b70b3cfeec708cce45 Mon Sep 17 00:00:00 2001 From: Sito8943 Date: Fri, 3 Jul 2026 21:17:17 +0200 Subject: [PATCH 01/30] bump --- package.json | 2 +- src-tauri/tauri.conf.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 0d5b9ffd..5f29f8c6 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "sito-file-browser", "private": true, - "version": "0.4.0", + "version": "0.4.1", "type": "module", "scripts": { "dev": "vite", diff --git a/src-tauri/tauri.conf.json b/src-tauri/tauri.conf.json index 86c1ab57..df3b5afa 100644 --- a/src-tauri/tauri.conf.json +++ b/src-tauri/tauri.conf.json @@ -1,7 +1,7 @@ { "$schema": "https://schema.tauri.app/config/2", "productName": "Sito File Browser", - "version": "0.4.0", + "version": "0.4.1", "identifier": "com.sito8943.file-browser", "build": { "beforeDevCommand": "npm run dev", From ffdc6e9918e39313a89ad93f97d481beade4dd64 Mon Sep 17 00:00:00 2001 From: Sito8943 Date: Mon, 6 Jul 2026 14:47:52 +0200 Subject: [PATCH 02/30] format --- SSH_PLAN.md | 34 ++++++++++++------- .../ConnectionDialog/ConnectionDialog.tsx | 9 +++-- src/features/directory/actions/open.ts | 3 +- .../components/StatusBar/StatusBar.tsx | 4 +-- src/features/directory/hooks/useDirSizes.ts | 3 +- .../components/SettingsDialog/utils.ts | 4 +-- src/features/sidebar/SideBar.tsx | 7 ++-- .../elements/DeletableChip/DeletableChip.tsx | 5 ++- .../patterns/DialogActions/DialogActions.tsx | 4 +-- 9 files changed, 45 insertions(+), 28 deletions(-) diff --git a/SSH_PLAN.md b/SSH_PLAN.md index dcff0a84..cb90f2f7 100644 --- a/SSH_PLAN.md +++ b/SSH_PLAN.md @@ -81,10 +81,10 @@ export type Connection = { id: string; name: string; host: string; - port: number; // default constant SSH_DEFAULT_PORT = 22 + port: number; // default constant SSH_DEFAULT_PORT = 22 user: string; - authKind: AuthKind; // const-object enum: PASSWORD | KEY | AGENT - keyPath?: string; // for KEY + authKind: AuthKind; // const-object enum: PASSWORD | KEY | AGENT + keyPath?: string; // for KEY }; ``` @@ -98,7 +98,11 @@ export type Connection = { ```ts // src/features/connections/constants.ts -export const AUTH_KIND = { PASSWORD: "password", KEY: "key", AGENT: "agent" } as const; +export const AUTH_KIND = { + PASSWORD: "password", + KEY: "key", + AGENT: "agent", +} as const; export const SSH_DEFAULT_PORT = 22; export const SFTP_SCHEME = "sftp://"; ``` @@ -141,13 +145,13 @@ features/connections/ ## 5. Degradations in remote mode (must be designed up front) -| Feature | Local | Remote (SFTP) | -|---|---|---| -| Live change watching | inotify via `tauri-plugin-fs` (`watch`) | **not available** → poll every N s, or manual refresh | -| Thumbnails | reads local file | partial download or disable for remote | -| Directory size (walk) | local walk | remote walk is slow → lazy / opt-in only | -| Trash / restore | local trash ledger | SFTP has no trash → direct delete behind a confirm | -| Drag-out to other apps | local path | requires downloading to a temp file first | +| Feature | Local | Remote (SFTP) | +| ---------------------- | --------------------------------------- | ----------------------------------------------------- | +| Live change watching | inotify via `tauri-plugin-fs` (`watch`) | **not available** → poll every N s, or manual refresh | +| Thumbnails | reads local file | partial download or disable for remote | +| Directory size (walk) | local walk | remote walk is slow → lazy / opt-in only | +| Trash / restore | local trash ledger | SFTP has no trash → direct delete behind a confirm | +| Drag-out to other apps | local path | requires downloading to a temp file first | Each of these needs an explicit remote branch or a graceful "not supported here" state — not a crash. @@ -167,8 +171,10 @@ Each of these needs an explicit remote branch or a graceful "not supported here" `get_thumbnail`, `open_file`, all write commands. That's phases 2–3. ### Testing phase 1 + Create `connections.toml` in the app config dir (`~/Library/Application Support/com.sito8943.file-browser/`): + ```toml [[connection]] id = "myserver" @@ -179,12 +185,15 @@ Each of these needs an explicit remote branch or a graceful "not supported here" # Phase 1 only — inline password OR export SFB_SSH_PASSWORD before launching. Phase 2 → keychain. password = "secret" ``` + …or provision it headlessly with the `sfb` CLI (no GUI needed — an agent can do this in parallel): + ```sh sfb sftp-add --id myserver --name "My Server" --host example.com --user sito --port 22 --password secret sfb sftp-list # non-secret view (passwords omitted) sfb sftp-remove --id myserver ``` + The CLI writes the same `connections.toml` the GUI reads (shared core `sftp::*_connection*`, config dir resolved by the CLI's own `app_config_dir()` mirroring the Tauri identifier). @@ -193,6 +202,7 @@ Each of these needs an explicit remote branch or a graceful "not supported here" Original phase-1 scope, for reference: `sftp` module (`read_dir`, `stat`, `read`) + router on the read commands (`read_directory`, `get_entry`, open/download). + 2. **Connection management.** `connections.toml` + `keyring` secrets + `connections` feature + `ConnectionDialog` + Network-group rows. Outcome: users add/connect their own hosts. 3. **Write operations.** Router on `move_entry`, `copy_entry`, `rename_entry`, `create_folder`, @@ -225,7 +235,7 @@ local↔remote transfers with progress + streaming, known-hosts, keychain, recon opt-in remote thumbnails, auto-clean cache, full connection CRUD in the UI). Remaining: - **Edit-and-save-back — external apps only (TODO).** The in-app markdown editor now saves a remote - file back to the server (write_text_file routes `sftp://` over SFTP). Opening in an *external* OS + file back to the server (write_text_file routes `sftp://` over SFTP). Opening in an _external_ OS app still edits only the local cache copy — no write-back. Would need a temp watcher + re-upload. - **SSH certificate identities in the agent.** `try_agent_auth` only tries plain public keys, not `AgentIdentity::Certificate` (SSH-CA setups like Teleport/Vault). Niche — plain-key hosts work. diff --git a/src/features/connections/components/ConnectionDialog/ConnectionDialog.tsx b/src/features/connections/components/ConnectionDialog/ConnectionDialog.tsx index 7b2a45c8..29062c71 100644 --- a/src/features/connections/components/ConnectionDialog/ConnectionDialog.tsx +++ b/src/features/connections/components/ConnectionDialog/ConnectionDialog.tsx @@ -84,7 +84,9 @@ const ConnectionDialog = ({ port: parsedPort, user: user.trim(), keyPath: - authKind === AUTH_KIND.KEY && keyPath.trim() ? keyPath.trim() : undefined, + authKind === AUTH_KIND.KEY && keyPath.trim() + ? keyPath.trim() + : undefined, keyPassphrase: authKind === AUTH_KIND.KEY && keyPassphrase ? keyPassphrase : undefined, password: @@ -217,7 +219,10 @@ const ConnectionDialog = ({ - diff --git a/src/features/connections/components/ConnectionDialog/ConnectionDialog.tsx b/src/features/connections/components/ConnectionDialog/ConnectionDialog.tsx index 29062c71..150252bc 100644 --- a/src/features/connections/components/ConnectionDialog/ConnectionDialog.tsx +++ b/src/features/connections/components/ConnectionDialog/ConnectionDialog.tsx @@ -4,6 +4,8 @@ import Dialog from "@/shared/components/patterns/Dialog"; import DialogHeader from "@/shared/components/patterns/DialogHeader"; import DialogActions from "@/shared/components/patterns/DialogActions"; import Button from "@/shared/components/elements/Button"; +import TextInput from "@/shared/components/elements/TextInput"; +import PasswordInput from "@/shared/components/patterns/PasswordInput"; import { useCloseOnEscape } from "@/shared/hooks/useCloseOnEscape"; import type { NewConnection } from "@/shared/services/api"; import { classNames } from "@/shared/utils"; @@ -124,7 +126,7 @@ const ConnectionDialog = ({ >