A Tauri v2 desktop storage scanner and cleanup tool inspired by DaisyDisk.
| Lines | Section |
|---|---|
| 1 | Title + Badges |
| 7 | Tagline |
| 9 | Description |
| 13 | Prerequisites |
| 19 | Installation |
| 25 | Usage |
| 31 | Features |
| 47 | Command Reference |
| 75 | Configuration |
| 82 | Development |
| 89 | Tests |
| 99 | Contributing |
| 105 | Changelog |
| 109 | Links |
| 113 | License |
| 117 | Credits |
Disk Map scans folders and mounted volumes, maps file and folder sizes recursively, previews items, and stages cleanup candidates. It surfaces hidden and purgeable space (macOS APFS) so you can see what is consuming a volume beyond the files you can browse.
It is a staged-cleanup tool: nothing is mutated until you commit the Collector. Items move to Trash or are permanently deleted only after explicit confirmation, with per-item risk checks that block irreversible actions on critical paths.
The frontend is React + TypeScript on Vite; all filesystem work runs in a Rust backend command invoked over Tauri's IPC. Long scans execute in a blocking thread pool (spawn_blocking) so the UI stays responsive and emits live progress events.
- Runtime: Node.js 20.19+ (or newer LTS)
- Package manager: npm
- Rust:
rustuptoolchain (stable) - Platform deps: Tauri v2 macOS prerequisites (
webkit2gtk/Xcode CLI equivalents). See Tauri v2 setup.
npm installThe Rust backend is compiled by Tauri at dev/build time; no separate install step is required.
npm run tauri dev # launch the app in development
npm run tauri build # produce a signed/unsigned desktop bundleIn-app: paste or pick a path, set max depth and exclude patterns, then Scan. Use the tree, largest-items, and file-type panels to find space hogs. Select items into the Collector, review risks, then commit to Trash or permanent delete.
- Recursive scan: full directory size = sum of children; children sorted by size descending.
- Display depth pruning: server clamps
max_display_depthto[1, 12](default4); deeper nodes keep their aggregated size but drop descendants from the payload. - Symlink-safe: uses
symlink_metadataso symlinks are reported as file-like leaf nodes and never traversed. - Exclude patterns: case-insensitive substring match on name or full path; comma-separated from the UI.
- Live progress:
scan-progressevents every 250 entries; newer scan for the same root cancels the older one. - Hidden + purgeable space: macOS APFS purgeable bytes via
diskutil;hidden_bytes = volume.used − scanned. - Staged Collector: add/remove items, see summary + risk flags (missing, symlink, critical path, covered-by-parent). Permanent delete is blocked while any blocker risk exists.
- Actions: Reveal in Finder, Open, Quick Look preview, Move to Trash, Permanently delete, plus
list_volumesandreclaim_purgeable_space(macOS). - Favorites: persist starred scan roots under
disk-map:favorites(deduped, survives restart).
All commands are invoked from the frontend via @tauri-apps/api/coreinvoke. The authoritative source is src-tauri/src/lib.rs; the registered handler list is the contract:
grep -n "scan_directory\|cancel_scan\|reveal_in_finder\|open_path\|preview_path\|move_to_trash\|inspect_path\|permanently_delete_path\|list_volumes\|get_purgeable_space\|reclaim_purgeable_space" src-tauri/src/lib.rs | grep "async fn\|fn "| Command | Args | Returns | Purpose |
|---|---|---|---|
scan_directory | path, max_display_depth?, excludes?, client_scan_id? | ScanResult | Recursive size map + analytics |
cancel_scan | — | — | Cancel all active scans (generation bump) |
reveal_in_finder | path | Result<(), String> | Reveal in OS file manager |
open_path | path | Result<(), String> | Open item in default app |
preview_path | path | Result<(), String> | Quick Look (macOS) / open |
move_to_trash | path | Result<(), String> | Move to Trash / Recycle Bin |
inspect_path | path | PathInspection | exists/isSymlink/isDir |
permanently_delete_path | path | Result<(), String> | Delete file or dir (guarded) |
list_volumes | — | Vec<VolumeInfo> | Mounted volumes + capacity |
get_purgeable_space | path | Option<u64> | APFS purgeable bytes |
reclaim_purgeable_space | path | Result<(), String> | tmutil thinlocalsnapshots (macOS) |
ScanResult shape (camelCase over the wire): root: DiskNode, largestFiles, fileTypes, skippedPaths, hiddenBytes?, purgeableBytes?, nodes, files, dirs, skipped, permissionDenied, errors, elapsedMs. See src-tauri/REFERENCE.md for every struct and constraint.
No env vars are read by the app. The bundle is configured in src-tauri/tauri.conf.json:
| Field | Value |
|---|---|
productName | Disk Map |
identifier | com.shadowwalker.disk-map |
version | 0.1.0 |
app.windows[0] | 1100 × 800, title Disk Map |
build.devUrl | http://localhost:1420 |
bundle.targets | all |
Capabilities are declared in src-tauri/capabilities/default.json (core:default, opener:default, dialog:default).
git clone https://github.com/buluma/disk-map.git
cd disk-map
npm install
npm run tauri devnpm test# vitest (frontend utils/collector logic)
cargo test --manifest-path src-tauri/Cargo.toml # Rust scan engine + safety guardsverify: both suites must pass before a PR is mergeable.
- Fork the repo.
- Create a feature branch (
git checkout -b feature/my-thing). - Commit changes (
git commit -am 'Add my thing'). - Push (
git push origin feature/my-thing). - Open a Pull Request. Ensure
npm testandcargo testpass.
See Releases. (No CHANGELOG.md yet — TODO.)
- Repository: https://github.com/buluma/disk-map
- Issue tracker: https://github.com/buluma/disk-map/issues
TODO — no LICENSE file present. Add one and update this section.
Built with Tauri v2 (Rust + React/TypeScript), inspired by DaisyDisk.