Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
7c1729d
bump
sito8943 Jul 3, 2026
7260596
Merge pull request #11 from sito8943/0.4.1
sito8943 Jul 3, 2026
ffdc6e9
format
sito8943 Jul 6, 2026
fe92b6c
Merge branch 'main' into 0.5.0
sito8943 Jul 6, 2026
41c3c3d
Merge pull request #12 from sito8943/0.5.0
sito8943 Jul 6, 2026
431cc4d
"feat(archive): zip compress & extract with password + extract-here/t…
sito8943 Jul 6, 2026
54440fd
feat(ui): reusable TextInput, Slider & PasswordInput; migrate dialogs
sito8943 Jul 6, 2026
0e104ea
feat(archive): compress/extract UI, actions & menu wiring
sito8943 Jul 7, 2026
2076701
fix(directory): derive file extension from the last dot
sito8943 Jul 7, 2026
642f272
feat(sftp): remote write, host-key verification & connection secrets
sito8943 Jul 7, 2026
7df372e
fix(menu): mirror context-menu opacity to portaled submenu flyouts
sito8943 Jul 7, 2026
20f733a
style: cargo fmt
sito8943 Jul 7, 2026
2b29b35
feat(archive): 7z compress & extract via system 7-Zip binary
sito8943 Jul 7, 2026
b7777d0
feat(archive): 7z compress/extract UI with availability gating
sito8943 Jul 7, 2026
684e5ba
feat(ui): indeterminate progress bar for operations without a byte total
sito8943 Jul 7, 2026
7c782e6
feat(shared): add Select and TextArea elements
sito8943 Jul 7, 2026
dd6e01e
refactor(ui): route bespoke buttons/inputs through shared elements
sito8943 Jul 7, 2026
2065dd0
feat(shared): add Checkbox element, ban raw form elements in features
sito8943 Jul 7, 2026
84dcce8
copy path contextual menu action
sito8943 Jul 7, 2026
71d804b
copy path keybinding
sito8943 Jul 7, 2026
029f6c3
reorder compress after destroy before properties
sito8943 Jul 7, 2026
422a16c
feat(preview): open image preview in its own window
sito8943 Jul 7, 2026
96e7c8a
feat(sfb): add ui-windows and ui-preview control commands
sito8943 Jul 7, 2026
c00d537
feat(clipboard): copy files to OS clipboard with Finder-style flavors
sito8943 Jul 7, 2026
2cf1633
feat(properties): open properties in its own window
sito8943 Jul 7, 2026
ec3b4cc
feat(directory): show persistent notice when a remote folder fails
sito8943 Jul 7, 2026
669a783
refactor(directory): centralize open routing through DirectoryProvider
sito8943 Jul 7, 2026
0f19b2c
feat(connections): edit saved connection, rehydrate key path
sito8943 Jul 7, 2026
5419b0f
feat(size): ignore more macOS/Windows junk; match by ancestor component
sito8943 Jul 7, 2026
6dad352
feat(dev): add Reload button to dev error overlay
sito8943 Jul 7, 2026
d610f54
fix(preview): use --color-background-app for the preview iframe backg…
sito8943 Jul 7, 2026
eb0e66d
changelog and lint
sito8943 Jul 7, 2026
00d797f
format
sito8943 Jul 7, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 30 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
34 changes: 22 additions & 12 deletions SSH_PLAN.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
};
```

Expand All @@ -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://";
```
Expand Down Expand Up @@ -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.

Expand All @@ -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"
Expand All @@ -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).

Expand All @@ -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`,
Expand Down Expand Up @@ -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.
Expand Down
34 changes: 34 additions & 0 deletions eslint.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -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 <Button> element (pass `unstyled` for bespoke styling) instead of a raw <button>.",
},
{
selector: "JSXOpeningElement[name.name='input']",
message:
"Use a shared input element — <TextInput> (pass `unstyled`) or <Slider> — instead of a raw <input>.",
},
{
selector: "JSXOpeningElement[name.name='select']",
message: "Use the shared <Select> element instead of a raw <select>.",
},
{
selector: "JSXOpeningElement[name.name='textarea']",
message:
"Use the shared <TextArea> element instead of a raw <textarea>.",
},
],
"i18next/no-literal-string": [
"error",
{
Expand All @@ -48,6 +74,14 @@ export default defineConfig([
],
},
},
{
// The shared components ARE the primitive wrappers, so they legitimately render the raw
// <button>/<input>/<select>/<textarea> that the rule above bans everywhere else.
files: ["src/shared/components/**/*.{ts,tsx}"],
rules: {
"no-restricted-syntax": "off",
},
},
{
files: ["*.{js,ts}"],
extends: [js.configs.recommended, ...tseslint.configs.recommended],
Expand Down
Loading
Loading