A GPUI + gpui-omarchy treemap explorer for disk usage on Omarchy. Read
README.md for the product; this file is the working contract.
Find what is eating a volume, mark paths for removal, review the list, and remove it — with the volume's free space live on screen the whole time. Two phases after choosing a local/SSH source and root: explore (treemap, breadcrumbs, selection and status) and review (the marked list, the removal mode, the confirmation). Marking is never destructive.
make build # release build
make run # build and open the source/root chooser
make install # ~/.local: binary, desktop entry, icon
make install PREFIX=/usr/local # system-wide (needs root)
make uninstall
make bundle # macOS: target/bundle/disktree.app and its zip
make lint # rustfmt --check, then clippy --all-targets -D warnings
make test # core and window-harness tests
make ci # lint, then test
make fmt # format in placemake install is the supported way to put this on a machine: it installs the
release binary, packaging/disktree.desktop.in (rendered with the real install
prefix and the crate version) and assets/disktree.svg. Keep the desktop
entry's Categories to a single main category plus additional ones, or
desktop-file-validate complains.
On macOS make install installs target/bundle/disktree.app into
~/Applications instead, built by cargo xtask bundle from
packaging/macos/ (the Info.plist template and the icon's SVG). The bundle
identifier there keys every macOS permission grant; do not change it lightly.
cargo xtask lint is the gate. It must be green before anything is called
done, and it must not fix anything: a red local run is the same signal CI gives.
On Windows, where there is no make, run cargo xtask lint, cargo xtask test
and cargo build --release directly; CI runs the gate on both systems.
- Strict lints from omatrack's style.
clippy::allandclippy::pedanticare errors,clippy::nurserywarns, and-D warningspromotes the rest. Every deliberate exception is listed with its reason in the workspaceCargo.tomlor as an#[allow(..., reason = "...")]on the item — never silently. - 80 columns, 4-space indent, by
rustfmt.toml. Only stable rustfmt options, because the gate runs on stable; unstable options would be ignored with a warning instead of applying. - Comments say why. The code says what. Any non-obvious number, ordering or boundary deserves the reason next to it.
- Tests live beside the promise they make.
disktree-coretests size accounting, layout and deletion guards against real temporary trees;disktree-app/src/tests.rsdrives the real window harness — draw a frame, press keys — so a screen that panics while painting fails a test. - Never delete anything outside a marked path. See
removal.rs; the guards are load-bearing and are covered by tests.
-
Sizes come from
st_blocks * 512unless apparent size was asked for. That is the number that comes back when a file is deleted. On macOS the directory listing reports it andst_sizefor a whole buffer of entries: every fork's allocation, and the data fork's length; seemacos.rs. On Windows it is the allocation the directory listing reports; seewindows.rs. An elevated scan of a whole NTFS drive reads it from the master file table instead, keeping the walk's rules for hidden entries, links, cloud folders and depth; seemft.rs. That path counts every stream's allocation, alternate data streams included since they go when the file goes, so for a file with alternate streams it can exceed the walk's number. -
Totals are derived, never tracked. A leaf's
bytesis the only input:tree::aggregatecomputes every directory'sbytes,filesanddirsfrom its children, andown_bytes()/own_files()read the direct children when asked. Hardlink de-duplication zeroes a duplicate leaf'sbyteswhile that pass runs; anything that patches a directory'sbytesdirectly will be overwritten.Nodeis paid for tens of millions of times, soa_node_stays_smallholds its size. -
A directory is only built when its own scan and every subdirectory task has finished. That is the
+1sentinel inPendingDir::pending. Building early silently drops whole subtrees — it has happened once. -
Only paths under the scanned root may be removed, and mount points, the root, the home directory, any directory holding it, and symlink targets are refused. On Windows so is every directory directly in the profiles folder Windows reports (
FOLDERID_UserProfiles, usuallyC:\Users); what is inside a profile stays removable. -
Marks are keyed by absolute path, not tree position, so they survive a re-scan;
Marks::refreshre-reads their sizes and drops what is gone. -
The treemap is painted, not composed of elements. Thousands of rectangles belong in one canvas callback; labels are shaped there too so they clip to their own tile.
-
Tile crumbs are absolute.
treemap::layouttakes the drawn node's crumbs and every tile extends them, so a tile resolves from the scanned root at any depth. Relative crumbs look right at~and silently point at other directories after descending — including for marks. Any code that turns a path into crumbs walks from the scanned root, too. -
The view transform is the only thing zoom changes. Layout runs in base-space pixels and is cached;
screen = (base - origin) * scale. -
The status bar never claims a saving it cannot measure. Projections come from marked bytes on the volume the meter measures, which is the scan's own notion of a volume — the mount source — so a mark on a disk mounted inside the tree, which
-Xand the media exception allow, is reported and never added to this volume's free space; seespace::attribution. The final number comes fromstatvfsbefore and after. -
Discovery is not a scan. No implicit home walk at startup. List mounts in the selected machine's namespace without probing them; scans stop at nested mounts, including bind mounts and same-device subvolumes. Switching machines clears marks and identity. The Unix SSH helper streams records and never retains the tree.
-
A live overview is not an actionable tree. Keep provisional, bounded display summaries separate from
Disktree::treeand all path, mark and removal APIs. Only a complete scan may replace the real tree. Counters and sampled paths belong in fixed, monospace progress lanes.
| change | where |
|---|---|
| bounded Unix SSH traversal | crates/disktree-core/src/remote_walk.rs |
| SSH protocol, discovery and transport | crates/disktree-core/src/remote.rs |
| measurement, filtering, parallelism | crates/disktree-core/src/scan.rs |
| what a node is, or a derived total | crates/disktree-core/src/tree.rs |
| tile geometry, nesting, the merged tail | crates/disktree-core/src/treemap.rs |
| anything that deletes, or refuses to | crates/disktree-core/src/removal.rs |
| free space and projections | crates/disktree-core/src/space.rs |
| what macOS lists and measures in bulk | crates/disktree-core/src/macos.rs — unsafe for getattrlistbulk |
| what Windows lists, measures and compares differently | crates/disktree-core/src/windows.rs — unsafe for Win32 |
| reading a whole NTFS drive from its file table | crates/disktree-core/src/mft.rs |
| a key, a screen transition, a mark | crates/disktree-app/src/state.rs |
| spacing, type and size | crates/disktree-app/src/ui.rs — tokens only, no px in layout |
| the mosaic's painting or labels | crates/disktree-app/src/treemap_view.rs |
| layout of a screen | crates/disktree-app/src/views.rs |
| colours derived from the theme | crates/disktree-app/src/palette.rs |
- Size accounting, hardlinks, symlinks, hidden entries, depth limits, the
removal guards and squarified layout are covered by
disktree-coretests against real temporary trees. - The screens are covered by window-harness tests that draw frames and press keys, including one that marks a directory, confirms the removal and checks the files are gone while unmarked neighbours are untouched.
- Rendering was verified by those tests and by running the app against a real home directory; it has not been eyeballed in every theme and font size.