Skip to main content

Module tui

Module tui 

Source
Expand description

The rollup tree TUI: the front end the whole tool is for.

§What it is

The filesystem tree, pruned to paths that lead to something reclaimable, with every ancestor carrying the bytes recoverable beneath it — collapsed by default and drilled into on demand. A row’s number is not “how big is this directory” but “how much do I get back by emptying this subtree”, so ~/repos/archived is one row worth 118 GB rather than forty rows a reader has to recognise as related.

That is the thing neither reference implementation has. kondo’s own README calls it “essentially rm -rf with a prompt”, which is a decision per hit, and hits are what scale: one real home directory here holds 16,013 of them. npkill is better and still flat, so the row a reader actually wants does not exist and has to be assembled from forty selections; its range-select is npkill approximating a tree without having one.

§Three moving parts, and the channel between them

  • The walker runs on its own thread and reports Found events. Rows appear as it finds them, which is npkill’s good idea and is easier on a tree: a new claim updates ancestor totals in place instead of reordering a flat list.
  • The view (state::View) holds everything a keystroke can change. It never touches the terminal or the filesystem, so every rule it has is a unit test.
  • The deleter runs a marked batch on a pool and reports each target as it finishes, which is why the cursor is anchored to a path: rows vanish under it.

All three meet on one channel, drained once per frame. The event loop blocks on the terminal with a short timeout rather than on the channel, so a scan that finds nothing for a second still repaints and a keystroke is never waiting behind a walk.

§The TUI prices what it shows, and the CLI does not

A default scan leaves claims crate::Size::Unmeasured, because pricing one means enumerating the subtree the walk deliberately pruned at — 4.6 s against 55.8 s over one real ~/repos. That is right for a listing you read once and wrong for a tree you steer by: unpriced, the rollup has nothing to roll up, and the headline question has no answer at any depth.

So the TUI turns the breakdown on unless the command line has scoped it. It can afford to, and #618 is why: prices are computed on a pool and arrive as separate events, so the rows are on screen at 7.5 s while the numbers fill in behind them for the following minute. The reader marks and deletes throughout. --breakdown-under <PATH> still means what it says, for a reader who wants one subtree priced and the rest left alone — and a double click is the same request made afterwards, on the one row in front of the reader.

§The pointer is generic, and that is the whole choice

The alternative on the table was OSC 8 hyperlinks, which only ever express “open this path”. A pointer gives row selection, click-to-expand, click-to-mark, the wheel and column-heading sorting from one hit test, and every later feature gets it for free. Three parts, mirroring the keyboard’s three:

  • render::hit resolves a cell to a Spot against the frame that was drawn. That is also where the routing lives — an overlay covers the screen it is over, so a press inside one cannot reach the tree, without anything restating the order.
  • [Pointer] holds what one event cannot see about the events before it: what the press landed on, whether it has moved since, and whether one landed here a moment ago.
  • keymap::pointer turns a gesture and a spot into an Action, off a table the help overlay reads too.

A Spot::Row names its row by identity and never by position, which matters more here than in pua: rows re-sort as prices land and vanish as removals complete, and a press resolved to a position and acted on a moment later would delete the wrong subtree.

Modules§

chrome
Everything the terminal shows that is not a cell of the frame.
keymap
What a keypress means: one table, and the chain that reads it.
lens
What is on screen: two independent axes, with presets on top.
moving
What is moving on the screen right now, and why each thing is allowed to.
render
Drawing one frame, and reading back where it put things.
state
The live view’s whole state: what is open, what is marked, and where the cursor is.
treemap
The treemap pane — a spike, and its escape hatch is Maps not being Maps::Can.

Structs§

Options
Everything the front end needs from the command line.
Outcome
What the run turns out to have been, for the exit status.

Functions§

run
Runs the view until the reader quits, then puts the terminal back.
size_mode
The size mode a live view runs under.