# Changelog All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). ## [0.3.0] - 2026-06-14 First release since 0.2.4; this entry consolidates all changes shipped since that release. ### Security - **Enforced plugin sandbox permissions** - Declared `PluginPermissions` are now bridged into an enforced `SandboxConfig` scoped to the scan root, so a plugin's filesystem/network/env/command grants are actually applied rather than advisory; filesystem access defaults to deny-all via the new `fs_read_roots` configuration - **Hardened hook dispatch** - Added a Tower-based middleware pipeline (timeout/util) around hook dispatch to bound plugin execution - **WASM runtime removed** - Removed the Extism/WASM plugin runtime because the latest Extism release pins a vulnerable Wasmtime line; Lua and Rhai plugin runtimes remain supported ### Fixed - **Data-loss-safe overwrites** - `Overwrite`/`OverwriteAll` copy and move operations now stage into a temporary sibling and atomically swap into place, so a mid-operation failure can never destroy the existing destination - **Directory recursion guard** - Added a maximum directory recursion depth to prevent stack overflow on adversarially deep nesting during recursive copy/move - **Lua value conversion drift** - Centralized Lua↔`Value` conversions in `lua/convert.rs` so the main runtime and the isolated context can no longer disagree on array-vs-object handling (the isolate previously flattened Lua arrays into objects) - **Quit unresponsive during mouse activity** - Replaced sync `crossterm::event::poll`/`read` drain loop with async `EventStream::now_or_never()`, fixing key events (`q`, Ctrl-C) being dropped when mouse motion floods the event queue - **Slow quit** - Skip event drain, navigation check, and suspend handling once quit is triggered; reduced runtime shutdown timeout from 100ms to 10ms - **Terminal cleanup on quit** - Drain buffered events after disabling mouse capture before restoring terminal, preventing raw escape sequences from leaking to the shell - **Crossterm backend alignment** - Updated Ratatui/Crossterm integration to use a single Crossterm 0.29 backend stack - **Unused `treemd` dependency** - Removed the optional Rust `treemd` crate and feature while preserving external `treemd` command integration - **Robustness coverage** - Added integration tests for copy/move/undo file workflows, symlink-preserving copies, duplicate hardlink handling, and age-analysis bucket/stale-directory reporting ### Changed - **Ops internals deduplicated** - Extracted shared copy/symlink/remove helpers across the copy and move paths - **TUI/scan/core refinements** - Rendering, scanning, tree, and configuration cleanups - **Dependencies updated** - Bumped direct workspace dependencies to current releases, including `zip` 8, `bzip2` 0.6, `rayon` 1.12, `compact_str` 0.9, `which` 8, and `crossterm` 0.29 - **Latest dependency refresh** - Updated direct dependencies to latest available releases, including `mlua` 0.12.0-rc.1 and the direct Unix `libc` dependency to 1.0.0-alpha.3 - **Lockfile refreshed** - Pruned stale transitive dependencies after removing the unsafe WASM runtime path and unused optional dependencies - **Clippy cleanups** - Kept the workspace warning-free under `cargo clippy --workspace --all-features -- -D warnings` ## [0.2.4] - 2026-03-11 ### Added - **WASM Plugin Runtime** - Full WebAssembly plugin support via Extism: - Load `.wasm` plugins alongside Lua and Rhai scripts - Hook dispatch, method invocation, and isolated execution contexts - Automatic hook discovery by inspecting WASM exports - **Plugin System Integration** - TUI now initializes and dispatches lifecycle hooks: - `OnStartup`, `OnShutdown`, `OnNavigate`, `OnScanComplete` hooks - Plugin manager with Lua, Rhai, and WASM runtimes registered at startup - Non-blocking hook dispatch via `tokio::spawn` for scan-complete events - **Git Status Integration** - Real-time git status indicators in file listings: - Modified (`M`), Staged (`A`), Untracked (`?`), Ignored (`!`), Conflict (`C`) indicators - Color-coded status using theme colors (modified=yellow, staged=green, etc.) - Automatic detection of git repositories - Works in both Tree and Miller column views - **Treemap Visualization** - Squarified treemap view for disk usage analysis: - Space-filling rectangular layout showing relative file/directory sizes - Color intensity based on size ratio - Keyboard navigation (arrow keys, Enter to drill down, Backspace to go up) - Directory/file indicators with different border styles - Size labels displayed within rectangles - **Visual Mode** - Vim-style range selection: - Press `V` to enter visual mode - Use `j`/`k` to extend selection up/down - Selected range highlighted - `Esc` exits and marks the selected range - Works seamlessly with existing mark system - **Archive Support** - Create and extract archives with full format support: - **Supported formats:** ZIP, TAR, TAR.GZ, TAR.BZ2, TAR.XZ - **Commands:** `:extract [destination]`, `:compress ` - **Archive preview:** Shows file listing with sizes, compression ratios, and symlink indicators - **Symlink support:** Preserves symlinks in archives (ZIP via Unix mode, TAR via link headers) - **Loop detection:** Prevents infinite recursion when archiving circular symlinks ### Security - **CRIT: Rhai code injection** - Replaced string interpolation in `call_function` with `Engine::call_fn` using typed args and stored AST, eliminating arbitrary code execution via plugin args - **CRIT: TAR decompression bomb bypass** - Size limits now check actual bytes written to disk via `symlink_metadata` instead of trusting attacker-controlled header sizes - **CRIT: Cross-filesystem move data loss** - Added cleanup on partial copy failure and warning on source removal failure to prevent duplicate/orphaned content - **HIGH: Plugin sandbox bypass (Rhai)** - All `fs_*` functions now gate on `SandboxConfig.can_read()` via `Arc`-shared config - **HIGH: Plugin sandbox bypass (Lua)** - `LuaRuntime` now stores and passes `SandboxConfig` to `create_fs_api` instead of `None` - **HIGH: Plugin sandbox bypass (WASM)** - Manifest now maps sandbox config to `allowed_paths`, memory limits, timeout; WASI disabled by default - **MED: ZIP symlink escape** - Deferred symlink extraction to second pass preventing symlink-redirected `create_dir_all` - **MED: Chained symlink attack** - Always validate symlink targets against canonical destination, not only when `..` present - **MED: Empty sandbox = full access** - Empty `allowed_read_paths`/`allowed_write_paths` now deny by default instead of granting unrestricted access - **MED: Lua `fs.read_bytes` unbounded read** - Uses `File::open().take(limit).read_to_end()` instead of full allocation - **MED: Lua instruction timeout bypass** - Wrapped isolate execution in `tokio::time::timeout` for wall-clock enforcement alongside instruction counter - Archive extraction hardened with path traversal prevention, ZIP bomb detection, and symlink escape prevention ### Fixed - **Windows build** - Replaced unstable `windows_by_handle` APIs (`file_index()`, `number_of_links()`) with stable fallbacks; hardlink dedup skipped on Windows - **Move undo pipeline** - Added `MoveComplete` struct carrying `moved_pairs` from move operations, enabling proper undo recording - **Cross-filesystem undo** - Undo now falls back to copy+delete when `fs::rename` fails across filesystems - **Windows symlink** - Choose `symlink_dir` vs `symlink_file` based on target type; log failures instead of silent discard - **Fuzzy search highlights** - Fixed misalignment on non-ASCII filenames by using character positions instead of byte offsets to match nucleo's UTF-32 indices - **Viewport height** - Correctly subtracts 5 rows when directory tabs are visible instead of always 4 - **Git pathspec** - Empty relative path at repo root now produces `**` instead of `/**` - **Filename validation** - Uses byte length (`name.len() > 255`) matching OS enforcement instead of char count - **Archive symlink icon** - Symlinks in archive preview now show distinct `@ ` icon instead of directory icon - **Doc link warnings** - Escaped `[PATH]` in doc comments to prevent rustdoc treating them as links - Broken doc comment continuation in plugin crate module docs - Test assertions updated to match actual `ScanConfig` defaults - `ContentHash` test helpers updated for `Box` optimization - Duplicate finder tests fixed for correct file count assertions ### Changed - **API Modernization** - ~25 function signatures changed from `&PathBuf` to `&Path` across all crates, following Rust idioms for borrowed path parameters - **Clippy Clean** - Eliminated all clippy warnings across the workspace (let-chains, derive Default, redundant closures, clamp, strip_prefix, etc.) - **InodeTracker** - Replaced `DashMap` with `HashMap` since access is single-threaded; ~15% faster inode tracking - **GlobSet reuse** - Scanner reuses `ScanConfig`'s pre-compiled `GlobSet` with fallback compilation for builder-created configs - **Progress counter** - Replaced `Arc` with plain `u64` in sequential scan loop - **`truncate_to_width`** - Extracted shared function to `ui/mod.rs`, eliminating 3-way duplication - **`TuiConfig`** - Added `#[non_exhaustive]` to prevent future breakage from field additions - **Cross-device filter** - Extended to non-directory entries (files and symlinks), not just directory pruning - **Undo System Simplification** - `FilesDeleted` variant now stores `paths: Vec`; `can_undo()` always returns `false` for deletions - **HookResult Serialization** - Added `Serialize`/`Deserialize` derives for WASM interop - **Dependencies Updated** - All workspace dependencies brought to latest versions ## [0.2.2] - 2026-01-16 ### Fixed - **Critical: Safe Deletion with Trash** - All deletions now move files to system trash instead of permanent deletion, allowing recovery of accidentally deleted files - **Critical: Symlink Safety** - Fixed symlink handling across all file operations to prevent accidental deletion of symlink targets: - Uses `symlink_metadata()` instead of `is_dir()` to avoid following symlinks - Symlinks are always removed as files, never following their targets - Applies to delete, copy, move, and undo operations - **Critical: Errors View Delete Behavior** - Pressing `d` in Errors view now correctly deletes the selected broken symlink, not items from the Explorer view - **Delete Confirmation Modal** - Now shows full paths (truncated from left if needed) instead of just filenames, with type indicators (🔗 for symlinks, 📁 for directories) - **Errors View Toggle Mark** - Space key now properly marks/unmarks broken symlinks in Errors view ### Added - **Errors View Footer Hints** - Shows available actions (`Spc` to select, `d` to delete, `Esc` to clear) - **Errors View Delete Hint** - Selected broken symlinks show `[d to delete]` hint - **Errors View Mark Indicator** - Marked items show `[x]` prefix - **Comprehensive Symlink Tests** - Added tests for deleting symlinks to files, directories, broken symlinks, symlink chains, and directories containing symlinks ### Changed - Delete confirmation now says "Move to trash?" instead of "cannot be undone" - Success messages now say "Moved to trash" instead of "Deleted" ## [0.2.1] - 2025-12-14 ### Added - **Settings Modal** - Persistent user settings with `,` keybinding: - Scan on startup (enabled by default) - Show hidden files - Default layout mode - Settings saved to `~/.config/gravityfile/settings.toml` - **Duplicates View Heat Graph** - Visual heat bars showing relative wasted space per group - **Duplicates View Help Section** - Dedicated help section explaining duplicates-specific keybindings - **Context-Aware Footer** - Footer hints change based on selection (group vs file) - **Parent Directory Navigation** - Navigate beyond scan root all the way to `/`: - `h`/`←`/`Backspace` now navigate to parent directory in both Tree and Miller modes - Tree view: `h` collapses if expanded, navigates back if collapsed or on file - Automatically lazy-loads parent directories when navigating above scan root - Miller columns parent pane updates correctly when navigating beyond original scan root ### Changed - **Duplicates View UX Overhaul**: - `Space` on group header now toggles all files in group - `d` on group header marks all duplicates (keeps first as original) - Files labeled as "keep" (green) vs "dup" (muted) for clarity - Marked files shown in red for visibility - Header shows "reclaimable" instead of "wasted" (positive framing) - Heat bars use color gradient (red → orange → yellow → green) based on impact - **Polished Footer Styling** - Keybinding hints now use pill/badge style with background colors - **Scan on Startup** - Now enabled by default (can be disabled in settings) ### Fixed - Duplicates view marking behavior - `Space` and `d` now work intuitively on both headers and individual files - **Layout Toggle Sync** - Switching between Tree and Miller views preserves exact selection: - Selecting a file in tree and pressing `v` now shows that same file selected in Miller - For nested items, view_root adjusts automatically so the selected item is visible - **Rescan Current Directory** - `R` (refresh) now scans the current directory, not the original startup path - Navigate anywhere, press `R` to scan that location - Parent navigation uses quick_list (no auto-scan) ## [0.2.0] - 2025-12-13 ### Added - **Miller Columns Layout** - Ranger-style three-pane view (Parent | Current | Preview), toggle with `v` or `:layout miller` - **File Operations** - Full clipboard support with vim-style keybindings: - `y` - Yank (copy) to clipboard - `x` - Cut to clipboard - `p` - Paste from clipboard - `r` - Rename file/directory - `a` - Create new file - `A` - Create new directory - `T` - Take (mkdir + cd into new directory) - **Conflict Resolution** - Interactive handling for file conflicts during copy/move operations - **Cross-Platform Releases** - GitHub Actions workflow for automated releases: - Linux x86_64 and ARM64 - macOS Intel and Apple Silicon (with optional code signing/notarization) - Windows x86_64 - **CI Pipeline** - Automated build and test on push/PR to main ### Changed - Improved navigation with history preservation across drill-down operations - Enhanced details panel with more file metadata ## [0.1.2] - 2025-12-09 ### Changed - Renamed binary from `gf` to `gravityfile` (with `grav` alias) to avoid conflict with git-fetch ## [0.1.1] - 2025-12-09 ### Added - **Interactive TUI** - Terminal interface with vim-style navigation (`j`/`k`, `h`/`l`, `g`/`G`) - **Parallel Scanning** - Fast directory traversal using `jwalk` for efficient multi-threaded scanning - **Duplicate Detection** - Three-phase algorithm using BLAKE3 hashing: - Phase 1: Group files by size - Phase 2: Partial hash (first + last 4KB) for quick filtering - Phase 3: Full hash for candidate verification - **Age Analysis** - Identify stale directories and analyze file age distribution - **Drill-Down Navigation** - Explore directories without rescanning; maintains expansion state - **Command Palette** - Vim-style `:` commands (`:cd`, `:root`, `:theme`, `:help`, `:q`) - **Multiple Themes** - Dark and light theme support with semantic color palette - **Export Support** - Export scan results to JSON format - **Library-First Design** - Modular crate structure for use as a library: - `gravityfile-core` - Core types (FileNode, FileTree) - `gravityfile-scan` - File system scanning engine - `gravityfile-analyze` - Analysis algorithms (duplicates, age) - `gravityfile-tui` - Terminal user interface ### CLI Commands - `gravityfile [PATH]` - Launch interactive TUI (default) - `gravityfile scan [PATH]` - Quick disk usage summary with tree output - `gravityfile duplicates [PATH]` - Find duplicate files - `gravityfile age [PATH]` - Analyze file ages - `gravityfile export [PATH]` - Export scan results to JSON [0.2.6]: https://github.com/epistates/gravityfile/releases/tag/v0.2.6 [0.2.5]: https://github.com/epistates/gravityfile/releases/tag/v0.2.5 [0.2.4]: https://github.com/epistates/gravityfile/releases/tag/v0.2.4 [0.2.2]: https://github.com/epistates/gravityfile/releases/tag/v0.2.2 [0.2.1]: https://github.com/epistates/gravityfile/releases/tag/v0.2.1 [0.2.0]: https://github.com/epistates/gravityfile/releases/tag/v0.2.0 [0.1.2]: https://github.com/epistates/gravityfile/releases/tag/v0.1.2 [0.1.1]: https://github.com/epistates/gravityfile/releases/tag/v0.1.1 [0.1.0]: https://github.com/epistates/gravityfile/releases/tag/v0.1.0