A disk usage visualizer for macOS (and friends), built with Tauri + React. Think Disk Inventory X, but with an explicit "I'm here to free up space" workflow built in.
Scans a volume, renders the contents as a squarified treemap, sunburst diagram, or flat table — colored by file type (or by modification age or last-access time, as a heatmap) — and lets you mark suspect files into a cleanup queue that can be moved to Trash or deleted forever.
Visit the project landing page at fosterdill.github.io/ansel-web.
- Treemap, sunburst, and table views — squarified treemap, Daisy Disk-style sunburst, and flat sortable table. Canvas-rendered at native DPR with spatial-grid hit-testing.
- Four color modes — by file type (14 categories), by modification age (cool→warm heatmap), by last-access time, or by folder (treemap-only).
- Cleanup queue — mark files for review, then move to Trash or delete permanently with a two-step confirmation.
- Multi-select —
⌘-clickacross all views; shared state with the inspector panel. - Search — case-insensitive search over the entire scan tree, results sorted by size with keyboard navigation.
- Cross-platform — macOS, Linux, and Windows. Platform-appropriate file manager reveal, keyboard shortcuts, and Trash/recycle-bin integration.
- On-disk size — files sized by allocated blocks (
stat.blocks × 512on Unix), so sparse files show their real footprint.
| Action | Result |
|---|---|
| Click | Select (replaces current selection) |
| ⌘-click (Ctrl-click) | Toggle in multi-selection |
| ⌥-click (Alt-click) | Toggle in cleanup queue |
| Double-click on a folder | Zoom in |
| Right-click on a folder segment (sunburst) | Zoom into that directory |
| Click center circle (sunburst) | Zoom out to parent directory |
| Shift+click on a folder rect | Zoom into that folder (treemap) |
| Right-click on a folder rect | Zoom into that folder (treemap) |
| Shift+⌘-click (Shift+Ctrl-click) | Select all items in the folder group |
| Shift+⌥-click (Shift+Alt-click) | Mark all items in the folder group |
| Hold Shift | Highlight the current folder group under the cursor (treemap dimming) |
| Click a breadcrumb | Zoom back out to any ancestor |
| Cmd+↑ (Ctrl+↑) | Zoom out to parent directory |
| Shift+↑/↓ | Adjust folder grouping depth |
| Drag-to-select (treemap) | Marquee-select multiple rects |
| Drag sidebar gutter | Resize the file tree sidebar |
| Esc | Clear selection / close modal |
The toolbar shows three numbers:
- Total — the volume's capacity, as reported by the filesystem (
statvfson Unix,GetDiskFreeSpaceExWon Windows). Definitive. - Scanned — the sum of on-disk allocation (
st_blocks × 512on Unix,GetCompressedFileSizeWon Windows) for every file the scanner reached. Also definitive, but a different measurement. - Free — derived as
total − scanned. Represents everything the scanner didn't attribute to individual files: filesystem metadata, allocation slack between file boundaries, APFS snapshots, directories skipped due to permissions (see TCC below), and sparse-file holes.
These three will not match Finder's Used/Free numbers, for good reason:
- Ansel's "scanned" will be higher than Finder's "Used" because on-disk allocation counts partial blocks per file, while Finder reports logical usage.
- Ansel's derived "free" will be lower than Finder's "Available" because Finder includes purgeable space (Time Machine local snapshots, caches) that
statvfsdoes not count as free. - On macOS, the totals will differ by ~7% from Finder's decimal GB display because Finder uses base-10 while Ansel uses base-2 on non-macOS platforms. On macOS itself, Ansel matches Finder's base-10 convention.
Every disk scanner faces the same reconciliation problem. The scanned file count and per-file sizes are the authoritative numbers; the bar exists to show you roughly where the volume stands.
Apple has become increasingly aggressive with unsigned apps. Right-click → Open no longer suffices for recent macOS versions. To launch Ansel for the first time:
- Download and attempt to open the
.app(Gatekeeper will block it). - Open System Settings → Privacy & Security.
- Scroll to the bottom — you'll see a message that Ansel was blocked. Click Open Anyway.
- Confirm the second dialog that appears.
Subsequent launches work normally.
Parts of ~/Library, /private/var, and other user homes are hidden behind TCC. Without Full Disk Access granted to whatever launched the app (or to the built binary), those subtrees come back as 0 bytes. The scan succeeds — it just under-counts.
Cloned files (copy-on-write duplicates) share blocks on disk, but each clone is counted with its own block tally by stat(2). This can inflate the scanned total when many clones exist.
Requires Rust (stable), Node 20+, pnpm.
pnpm install
pnpm tauri devThe first pnpm tauri dev cold-compiles the Rust backend (~30–60s on Apple Silicon); subsequent runs hot-reload the frontend in milliseconds and incrementally rebuild Rust changes.
src/ React frontend
App.tsx Layout shell — toolbar, three-column layout, side effects (Tauri events, keyboard shortcuts, transitions, sidebar resize)
components/
WelcomeScreen.tsx Volume picker, scanning animation, error/retry
Treemap.tsx Canvas-rendered squarified treemap, DPR-aware, grid hittest, dimming
Sunburst.tsx Canvas-rendered concentric-ring sunburst, polar hittest, zoom-in/out
SearchBar.tsx Search over in-memory scan tree, results sorted by size with keyboard navigation
TreeView.tsx Lazy-loaded file tree sidebar
LargestFiles.tsx Flat sortable table of biggest files
InfoPanel.tsx Inspector (single + multi-select views)
Breadcrumbs.tsx Clickable path breadcrumbs
CleanupQueue.tsx Marked-items review modal with batch actions
DeleteConfirm.tsx Two-step permanent deletion confirmation
lib/
store.ts Zustand store — all application state and actions (scan, navigation, selection, UI, cleanup)
api.ts Typed Tauri IPC wrappers, shared types
treemap.ts Squarified layout algorithm (Bruls-Huizing-van Wijk)
color.ts FNV-1a hash, HSL jitter, age heatmap, folder coloring
fileTypes.ts 14 file categories, ~120 extension mappings
format.ts Platform-aware byte formatting (decimal on macOS, binary elsewhere)
src-tauri/
src/lib.rs Tauri commands, AppState, volume discovery, subtree builder
src/scanner.rs Parallel rayon scanner, tree mutation, top-files, extension breakdown
src/main.rs Entry point
tauri.conf.json Bundle identifier, window config
Cargo.toml Rust dependencies (tauri, rayon, serde, trash, windows-sys)
capabilities/ Tauri v2 permissions (core + opener)
pnpm tauri buildArtifacts land under src-tauri/target/release/bundle/ (.dmg on macOS, .AppImage/.deb on Linux, .msi on Windows).
See CONTRIBUTING.md for setup instructions and workflow details. main is protected — all changes land via feature branch → pull request → squash merge.
MIT
Report vulnerabilities by opening a GitHub issue or emailing fosterdill@gmail.com. See SECURITY.md for the full policy.
