diff --git a/CHANGELOG.md b/CHANGELOG.md index b4f77dff..a6313883 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,36 @@ All notable changes to this project are documented in this file. +## [0.6.0] + +### Added + +- Archive support — compress files/folders to `.zip` (optional password) and extract archives, with extract-here / extract-to-folder, entry actions, and context-menu wiring (`431cc4d`, `0e104ea`) +- 7-Zip archives — compress & extract via the system 7-Zip binary, with a compress/extract UI gated on 7z availability (`2b29b35`, `b7777d0`) +- Copy files to the OS clipboard the Finder way — a copied selection pastes as real files into file-aware apps (Finder, Mail) and as names into text fields (macOS multi-flavor `NSPasteboard`) (`c00d537`) +- Open properties in its own detached window — an `openPropertiesInWindow` setting, plus `sfb ui-properties` and a `properties` control action (`2cf1633`) +- Open an image preview in its own window (`422a16c`) +- Copy path — a context-menu action and keybinding that copy an entry's path (`84dcce8`, `71d804b`) +- Edit a saved SSH connection from the sidebar; the stored key path rehydrates the auth dialog (`0f19b2c`) +- Persistent notice when a remote folder fails to load, with a Retry — replaces the transient toast that left the folder deceptively empty (`ec3b4cc`) +- Indeterminate progress bar for operations without a byte total (`684e5ba`) +- Reload button on the dev error overlay (`6dad352`) +- `sfb ui-windows` and `ui-preview` control commands (`96e7c8a`) +- Remote write over SFTP, host-key verification, and Keychain-stored connection secrets (`642f272`) +- Shared UI element library — `Checkbox`, `Select`, `TextArea`, `TextInput`, `Slider`, `PasswordInput`; feature code is barred from raw form elements and routed through these primitives (`2065dd0`, `7c782e6`, `54440fd`, `dd6e01e`) + +### Changed + +- More macOS/Windows junk excluded from folder-size totals by default (`com.apple.chrono`, `.localized`, `.AppleDouble`, `Thumbs.db`, `desktop.ini`); ignore rules now match any path component, so deep watcher events under an ignored dir are dropped (`5419b0f`) +- Committing a file path in the path bar now opens the file (open routing centralized through the directory provider; the redundant Preview action is hidden when Open already previews) (`669a783`) +- Reordered the Compress action in the context menu — after Destroy, before Properties (`029f6c3`) + +### Fixed + +- Context-menu opacity now mirrors to portaled submenu flyouts (`7df372e`) +- Derive a file's extension from the last dot, not the first (`2076701`) +- Preview iframe background uses the app-background token (`d610f54`) + ## [0.5.0] ### Added 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/eslint.config.js b/eslint.config.js index f5b2f385..2be92e65 100644 --- a/eslint.config.js +++ b/eslint.config.js @@ -22,6 +22,32 @@ export default defineConfig([ globals: globals.browser, }, rules: { + // Route UI through the shared elements (Button, TextInput, Select, TextArea, Slider) so the + // theme-aware look + a11y defaults live in one place. Bespoke fields use the element's + // `unstyled` prop; genuinely element-less gaps (e.g. checkbox — no shared element yet) may + // opt out with an inline eslint-disable + a comment saying why. + "no-restricted-syntax": [ + "error", + { + selector: "JSXOpeningElement[name.name='button']", + message: + "Use the shared