Skip to content

capebio/FilenameUpdate2

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

25 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

filenameupdate2

CI Rust License

A keyboard-driven, two-pane file manager (Total Commander–style) tuned for photo workflows — taxonomic, archival, or scientific. Built in Rust on egui.

The headline trick: smart name-sync. When you've renamed P2230437.ORFP2230437 Guilleminea densa.ORF, F5 (copy) and F6 (move) detect that they're the same file (matching base ID + EXIF DateTaken + size) and converge both sides to the more-specific suffix rather than duplicating bytes.


Highlights

  • Two-pane TC layout with green active-pane border, drive pills, breadcrumb path bar, recent-folders dropdown, status bar, fading toasts.
  • List ↔ Thumbnail grid view per pane (Ctrl+T). Visible-only lazy decode, background thumbnail workers, persistent disk cache.
  • Native preview for JXL via pure-Rust jxl-oxide. RAW (ORF/CR2/CR3/NEF/ARW/DNG/RAF/RW2) shows embedded JPEG; HEIC/AVIF same when available.
  • EXIF panel + rotate + zoom in F3 preview. EXIF orientation tag auto-applied to all thumbnails and previews.
  • Smart name-sync with taxonomic specificity: Species > Genus > Family > None. Conflicts queue through Dialog A/B.
  • Batch rename (Shift+F2) with {name} {ext} {n} {n3} {date} {base} tokens + presets.
  • Undo (Ctrl+Z) for the last reversible Copy / Move / Rename actions.
  • Filesystem watch (notify) auto-reloads panes on external changes.
  • Per-folder rating sidecar (.fnu_rate.json). Hotkeys 0..5; Ctrl+6 sorts by rating.
  • Find duplicates (Ctrl+D), regex filter (Ctrl+/), recursive flat view (Ctrl+Shift+R), select-by-mask (Ctrl++ / Ctrl+-), bookmarks (Ctrl+B / Ctrl+L).
  • Right-click context menu: Preview, Copy path, Open with system app, Reveal in Explorer, Copy/Move to other pane (smart-sync routed).
  • Sync stats modal (F9) with side-by-side thumbnails for missing / mismatched / renamed / orphan files between panes.
  • Sidecar-cached EXIF: datetaken + orientation persist in .fnu_index.json; rescans skip EXIF parse when file unchanged.

Build

cargo build --release
./target/release/filenameupdate2

Requires Rust 1.78+ (edition 2021). No native toolchain dependencies — JXL decode is pure Rust.

Hotkeys

Navigation

Key Action
↑/↓ PgUp/PgDn Move cursor
Enter Descend dir / open file
Backspace Up to parent
Tab Swap active pane
Alt+←/→ Folder history back/forward
Alt+F1 / Alt+F2 Drive picker for left / right pane
Ctrl+G Go to path
Ctrl+B / Ctrl+L Add / list bookmarks

Selection

Key Action
Insert Toggle select + advance
Space Toggle select + advance
Shift+↑/↓ Range select
Ctrl+A Select all
Ctrl+I Invert
Ctrl++ / Ctrl+- Select / deselect by mask
Ctrl+D Find + select duplicates

Operations

Key Action
F2 Rename
Shift+F2 Batch rename (token-based)
F3 Preview (RAW / JXL / standard, rotatable + zoomable)
F4 Edit in text editor
F5 Copy → other pane (smart name-sync)
F6 Move → other pane (smart name-sync)
F8 / Del Recycle
Ctrl+F8 / Shift+Del Hard delete
F9 Sync stats modal (compare panes)
Ctrl+Z Undo last action
Ctrl+R Refresh
Ctrl+E Open folder in OS file manager
Ctrl+C Copy path

Preview

Key Action
R / Shift+R Rotate left / right
Mouse wheel Zoom

Rating

Key Action
0 Clear rating
1..5 Set 1–5 stars
Ctrl+6 Sort by rating

View

Key Action
Ctrl+T List ↔ thumbnail grid
Ctrl+/ Toggle regex filter
Ctrl+Shift+R Toggle flat (recursive) view
Ctrl+H Toggle hidden files
Ctrl+1..5 Sort by name / size / mtime / datetaken / base ID
F1 Help overlay
Ctrl+Q Quit

How name-sync works

For each source file, the planner builds an index of the target side keyed by filename and base ID, then classifies each pair:

  1. Same name, same contentNoOp (true sync).
  2. Same content, different suffix — same base ID + size + DateTaken, different name (e.g. left P2230437.ORF, right P2230437 Guilleminea densa.ORF):
    • Parses each side's suffix into None < Family < Genus < Species ranks.
    • Renames the lower-rank side to the higher-rank name.
    • Tie → Dialog B (Use Left name | Use Right name | Auto: more-specific | Skip).
  3. Same name, different content → Dialog A (Keep Left | Keep Right | Keep Both with _2 suffix | Skip).
  4. No match → plain copy or move.

Move actions additionally recycle the (possibly renamed) source after the rename converges names.

Folder-level workflow

When the cursor is on a subdirectory with no file selection, F5/F6 operate on the folder:

  1. plan_folder flags base-ID conflicts (same base ID, different names between source folder and same-named target folder) and missing-in-source files (target has files source doesn't).
  2. Anomalies surface in a confirm dialog.
  3. On confirm, the planner re-plans per file with plan_sync, so twin-name files still converge under the more-specific suffix instead of erroring on collision.

Delete propagation

A .fnu_index.json sidecar is written to each folder on every op. When you delete files manually in one pane, the tool diffs the on-disk state against the prior index and can propose matching deletions in the other pane.

Architecture

src/
├── app.rs           # State, frame tick, dialog wiring
├── pane.rs          # PaneState: cursor, selection, sort, filter, view mode
├── ops.rs           # Step (Copy/Move/Rename/Recycle/...), plan_sync, plan_folder
├── matching.rs      # TargetIndex + MatchKind classifier
├── parse.rs         # Filename → (base_id, suffix, specificity)
├── conflict.rs      # ConflictQueue for Dialog A/B routing
├── scan.rs          # Parallel directory scan (rayon)
├── decode.rs        # Unified decode: JXL (jxl-oxide), RAW embedded JPEG, EXIF rotate
├── thumb_worker.rs  # Background decode pool (crossbeam-channel)
├── thumb_cache.rs   # Disk thumb cache (%LOCALAPPDATA%/filenameupdate2/thumbs)
├── fs_watch.rs      # notify-based pane auto-reload
├── undo.rs          # Reversible Step inverse + capped stack
├── toast.rs         # Fading status toast queue
├── ratings.rs       # .fnu_rate.json sidecar
├── exif.rs          # EXIF DateTaken, Orientation, summary
├── index.rs         # .fnu_index.json sidecar with cached EXIF
├── config.rs        # Persisted per-pane settings, bookmarks, recent
├── ui/
│   ├── panes.rs     # List + grid view rendering
│   ├── dialog.rs    # All modals (conflict, bulk-delete, preview, batch-rename, sync-stats, help)
│   ├── toolbar.rs   # Top button bar
│   ├── statusbar.rs # Bottom status + toast stack
│   └── theme.rs     # Dark theme colors
└── lib.rs / main.rs

Testing

cargo test --tests   # 44 integration tests across 5 suites
cargo test --lib     # 56 unit tests

UI integration tests drive App::tick(&egui::Context) directly with synthetic RawInput. Snapshot tests hash tessellated clip-rects to catch layout drift. End-to-end name-sync resolution is covered by tests/ui_integration.rs::f5_copy_resolves_twin_name_via_specificity.

File-format coverage

Format Thumbnail Preview Notes
JPEG / PNG / GIF / WebP / BMP / TIFF egui native loaders
JXL pure Rust via jxl-oxide
ORF / CR2 / CR3 / NEF / ARW / DNG / RAF / RW2 embedded JPEG extracted from RAW
HEIC / HEIF / AVIF ⚠️ ⚠️ embedded JPEG only (pure-Rust HEVC/AV1 decode not yet viable)

License

MIT.

About

FIlenameUpdate is for anyone that names photographs and finds it difficult to deal with backing up their photos due to duplicate files due to differing names

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages