feat(ui): move between regions, name rows by identity, sort from the column - #51
Open
Brad (bmethod) wants to merge 5 commits into
Open
feat(ui): move between regions, name rows by identity, sort from the column#51Brad (bmethod) wants to merge 5 commits into
Brad (bmethod) wants to merge 5 commits into
Conversation
|
You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard. |
Brad (bmethod)
force-pushed
the
issue-24-design
branch
6 times, most recently
from
September 10, 2026 19:25
bce9925 to
7cb69c0
Compare
A screen's lists are regions of one flat selection. Left and right move between them, up and down move inside the one in focus, and a region with no rows is skipped because a reader cannot stand on a row that is not there. The focused region's title takes the accent colour and its rule stops being dim, so nobody has to press a key to find out where they are. Home has four regions and its tile row is one of them, which makes the tiles the same rule as everything else rather than a second one. Storage has three. A line that expands under another now carries a rule down its left edge as well as its indent. The indent alone reads as a new top-level line at a glance, and it is the rule that says whose line this is. One helper draws the concern detail, the capability diagnostic, the Storage drill-down and the agent detail's open sections. One marker says a row has more inside it: `▸` closed, `▾` open, the count beside the name. It is drawn whether or not anything is open, so a reader can see what is worth opening without opening it. The agent detail's sections, the Settings capability rows and unreadable sources, the Home concerns and the Storage filesystems all draw through it. `Busiest agents` re-sorts every second and a reader's target moves under the cursor. A key now holds the row order while every reading in those rows keeps updating. Holding the order is not freezing the data: a dashboard that lies while it is read would be worse than one that moves. A lane that ended drops out, a lane that climbed does not push in, the section says it is holding where its count sits, and leaving the screen releases it. Claude-Session: https://claude.ai/code/session_01B4rKWUEy6WF5MRxtBEwFdB
A name holds the name. Nothing is appended to make one unique any more: an id that appears on some rows and not others reads as arbitrary, so the process id is a column of its own on every row, beside the pane address phase 7 added. Two lanes with one name are told apart by scanning a column. A narrowing list sheds its optional columns in one order until the name has its floor back: the trend first, then the pane address, and the id last, because the id is the one that is on every row. A sorted column says so in its own heading. The arrow leads on a numeric column, so the heading still ends where the digits under it end, and follows the word on a text column. Each screen sorts the headings it draws, which is why Home's `Busiest agents` cycles its own four rather than the Agents column set: a marker has to sit on a heading the reader can see. An unread source says what it costs instead of which interface was missing. The readings it feeds are blank rather than zero, and the row names where those readings are. Blank and zero are different answers, and a dashboard that shows zero for a number it could not read is lying. The interface and the system's own words stay in the drill-down, which is where a reader goes to fix it. The collapsed group of unreadable sources now sits at the depth of the rows it summarises rather than two columns to their left. `Recent changes` gives its time, kind and subject their own columns, so three rows scan as three rows and a cut subject ends in its mark instead of stopping mid-word. Claude-Session: https://claude.ai/code/session_01B4rKWUEy6WF5MRxtBEwFdB
An arrow moves within the focused region, along that region's own axis: left and right along the horizontal tile row, up and down down a vertical list, and left and right along Timeline's time cursor, which is what that region contains. Tab and Shift+Tab move between regions. That is one rule rather than two. The earlier draft gave left and right the job of leaving a region, which could not be universal: those arrows already move the time cursor inside Timeline's own region, and a horizontal row of tiles was left navigating vertically. Tab no longer changes screens anywhere. Every screen has a number key and those are printed across the header at all times, so the discoverable route to a screen is not the one being repurposed, and a key that moved regions on two screens and changed screens on the rest would be the second rule this removes. The footer and the help panel say so, and the help panel now names the number keys as the way to a screen. Claude-Session: https://claude.ai/code/session_01B4rKWUEy6WF5MRxtBEwFdB
Taking the disambiguator out of lane names moved the problem rather than removing it: Home's busiest agents and the Builds lanes had no id column to put it in, so six rows read `method` with nothing beside them. Ambiguous but labelled became ambiguous and silent. Both lists now carry the same dim id column the Agents list has, and `LaneBuilds` carries the process leading its lane so the Builds row has one to draw. The agent detail already named its process in the identity block, which is where one lane at a time belongs; a test now holds it there. Home shares its width with the column beside it, so when the name cannot keep its floor the state column goes instead. A blocked or running lane already shows in its numbers; a name that identifies nothing shows in nothing. The guard is the property that failed rather than the column that fixes it: six lanes named `method`, every reading identical, rendered on Home, Agents and Builds at two widths, and no two drawn rows may read the same. A future narrowing that drops the column fails it whatever the column is called. Claude-Session: https://claude.ai/code/session_01B4rKWUEy6WF5MRxtBEwFdB
Brad (bmethod)
force-pushed
the
issue-24-design
branch
from
September 10, 2026 19:56
7cb69c0 to
2ad2874
Compare
Six lanes named `method` drew as six rows a reader could not tell apart at 37 of the 127 terminal widths between 84 and 210. Reproduced before anything changed, by arithmetic and again by rendering every width: the runs are 84 to 106 and 150 to 163, and both agree exactly. The second run is the one worth knowing. The side pane opens at 150 and takes a third of the terminal, so a wider terminal makes the list narrower: at 150 the list is 97 columns against 120 at a terminal width of 120. The principle this branch already states is that identity that always exists outranks identity that usually does not. One step further: identity outranks readings. So the process id leaves the optional set altogether and `Program`, which reads `claude` on every row, and `Wait`, which reads `0.0%` on every row, take its place in it. The order is now trend, program, wait, pane address, and the id is never given up. Measured against the two alternatives rather than argued. Making the id fixed alone closes both windows but leaves the name column at 17 columns at a terminal width of 100, under its floor of 24. Moving only the readings below the id closes one window and leaves 20 widths collapsed, because the id is still not asked for at all below a list width of 100. Doing both closes all 37 and holds the name floor everywhere. And with the pane column wanted, ranking it above the readings keeps it at every width, where ranking it below loses it at 52 widths and keeps `Wait` instead. A name that falls back to carrying its id was not taken: it is the appended id this branch exists to remove. The guard for that property could not fail for it. It rendered 120 and 180, one width from each safe band, so the narrowing it was named for already existed and it passed. It now renders 99, 106, 120, 163 and 180 and reads the ids out of the column by its heading offset, because comparing whole rendered lines is unsound above 150: at 163 the side pane text beside four identical rows made them look distinct, and whole lines gave 6 of 7 where the column held six rows carrying no id at all. The two byte-identical tests of the shedding order are now one, named for what it checks. The one that survived states the new order; the other stated the rule the diff had already reversed. Moving back to the tiles left them off screen: the scroll effect named `selected` and setting `tile` re-ran nothing, so the frame did not move and the only visible change was the marker disappearing. Enter then opened another screen with nothing on this one saying so. That is the third screen with this cause, so the shape is now one hook rather than a third copy. It asks the drawing two questions instead of enumerating state. The reader moved: the target's id is not the one it was, so go to it. The drawing moved under a reader who did not: the id is the same and the target sits elsewhere in the content, so chase it only if it has left the screen and only once the reader has chosen something. Three things measured rather than assumed, each having reversed a decision. These boxes take the wheel, so scrolling on every render drags the reader back on the next sample. A child's `y` is a screen position, not a content one, so guarding on it fights the wheel too; the content position is `y` plus the scroll offset. And cancelling the pending reading on the next render starves it: a reader holding a key down renders faster than a timeout fires, and the screen never follows them at all. `settings-screen.tsx` keeps its own effect. Its trigger list is the same shape, but what it does is different arithmetic, not this scroll. Also on Home: asking for a different order now releases the held one, because `o` then `s` read `↓ Memory` over rows still in the held CPU order; and a concern's marker reads the same question its detail is drawn under, because on the tiles it drew `▾` with no rule and nothing beneath. Eleven claims corrected to say what the code does. Three of them a user reads: the README said Tab changes screens and listed neither the region key nor `o`; the README and the Settings help both said vsys appends the working directory or the process id to a name, which this branch deleted, and a reader reaches that one by pressing 7. `ui-invariants.md` stated the shedding order backwards, in exactly the direction this branch tried and rejected. `ui.md` claimed the wide Agents table carries the id column; it draws configured columns and there is no `pid` among them. Three `Covers:` entries named files that are not there. `builds.md` and `storage.md` now name the `-screen.tsx` files this branch changes, so the drift check can see them; `verdict.md` named `src/ui/overview.tsx`, which was deleted with no successor. Controls, each reddening its own test: the branch's own column set, for the guard and for the shed order; a Home scroll that does not know the tiles are a place to stand; a hook that scrolls on every render; one that reads the screen position; one with no settled reading; one that does not follow a moving selection; one that does not chase the drawing. A clause requiring the row to have been on screen was deleted rather than kept, because with the reader-has-chosen bit it could not be made to fail. 415 tests, 0 fail, three runs. Claude-Session: https://claude.ai/code/session_01B4rKWUEy6WF5MRxtBEwFdB
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
All of #38: the second design pass, and the last of the #24 work. Stacked on #50.
Replaces #45, which was opened against the phase-7 branch and never drew a review: this repository's automatic reviewer fires on a pull request's original open, and reopening one it has never reviewed does not re-arm it. The branch is unchanged; only the pull request is new.
The owner ran the app after the first design pass and asked for a second. Every item below was re-checked at 180x50 against a tree holding all seven phases, after a 200-second run so the charts held real history — anything already fixed was struck rather than filed.
Move between regions, not just within one
Home holds four regions and the keyboard reached them as one flat list. Arrows move within the focused region along its own axis;
TabandShift+Tabmove between regions. Left and right run along the horizontal tile row, up and down through a list, and Timeline keeps left and right for its time cursor.The first draft had left and right leaving the region, which reads as one rule and is not: it makes moving along a horizontal row of tiles a matter of pressing down, and Timeline could not follow it at all because those keys already mean the cursor there. The agent building it flagged that rather than picking the nicer-feeling option, and the rule it exposed is the one shipped.
Tab no longer changes screens anywhere. The alternative was Tab moving regions on the two screens that have them and changing screens on the other five, which is the second rule this change exists to remove. The number keys
1-7remain and are printed across the header at all times; the help panel now states them, since that route had been implied by Tab. A test pins that Tab leaves a single-region screen alone.Regions exist on Home and Storage, the only screens with more than one selectable list.
A child line looks like a child
A vertical rule at the left of every continuation line, keeping the indent — drawn as a box's own left border, so it runs the full height of its content without anyone counting lines. Four sites through one helper: the concern detail, the capability diagnostic, the Storage drill-down, the agent detail sections.
One pattern for one idea
▸collapsed,▾open, the count beside the name. Searching the UI sources for those glyphs finds them inwidgets.tsxalone; five sites draw through one component, and a row draws its marker whether or not anything is open — so a reader can see what is worth pressing Enter on without pressing it.Hold the order while reading
Busiest agentsre-sorted every second and the reader's target moved under the cursor.oholds the row order while the values keep updating: a dashboard that freezes its data lies while you read it. A lane that ended drops out; a lane that climbed does not push in. The hold releases on leaving the screen.orather thanp, because pinning a sample and holding an order are different things.An id is a column, not part of a name
The same list read
.hclaude claude ken-1295besidemethod PID 3405beside.2claude claude dotfiles PID 2557288— correct behaviour, since the id appeared only where names collided, and unreadable presentation, because to a reader it looks arbitrary.The name column holds the name; a dim id column holds the process id for every row.
distinguishis gone from the lane model, so no name is appended to.Every list that draws lane names carries the column — Home, Agents and Builds. The builds lane model had no process id at all and carries one now. The agent detail already names its process in its identity line, which is where that belongs for a screen showing one lane, and a test holds it there.
The guard pins the property rather than the column: six lanes named
methodwith every reading identical, rendered at two widths, and no two drawn rows may read the same. A future narrowing that drops the column fails it whatever the column is called.Where Home's width cannot keep the name readable, the state column goes instead. A blocked or running lane already shows in its numbers; a name that identifies nothing shows in nothing.
Sort from the column
The sorted heading carries
↑or↓, chosen in one place. On a numeric column the arrow leads, so the heading still ends where its digits end — putting it after the label broke the alignment an earlier phase established.Builds and Resources do not sort, each for a reason rather than by omission: Builds' rows are not lanes and have no comparator, and Resources is a tree that sorting would flatten.
Say what unread means
57 sources vsys cannot readnamed a mechanism and no consequence. It now names what a reader loses — every wait reading is blank rather than zero, and which screens those are. The interface and the system's own words stay in the drill-down, which is where someone goes to fix it rather than to understand what they are missing.Separate what is separate
Recent changestruncated mid-sentence with no mark. It has three columns now, and its subject cuts through the same helper every other cell uses.Proof
399 tests, 0 fail.
This line read 385 while the branch sat under #43 and #44. The stack was replayed onto the merged phase-5, which carries four tests the review rounds added there, and 385 was itself one too high — a duplicate entered during the test-file split and overstated every figure from that commit until it was found. #43 records why only the count could see it.
Plants throughout, and three of them caught a test rather than the code:
One decision came from a capture rather than a test. The Agents list sheds optional columns as it narrows, ordered trend, id, pane — identity last, which sounded right. The 180-column frame showed an empty pane column surviving while every process id was dropped. The order is now trend, pane, id: identity that always exists outranks identity that usually does not. Four widths are pinned.
Closes #38. Part of #24.
https://claude.ai/code/session_01B4rKWUEy6WF5MRxtBEwFdB