Walk the grid with the arrow keys, and open with Return
Build and test / Desktop (Linux) (push) Successful in 17m36s
Build and test / Layer separation (push) Successful in 34s
Traceability / Requirement traces (push) Failing after 25s
🐳 Android image / Build and push (push) Successful in 3s
Build and test / android-image (push) Successful in 3s
Build and test / Android (aarch64) (push) Failing after 9m17s

A cull is thousands of decisions. The grid already bound 0-5, P, X and U so
the judgement itself needs no mouse, and then made the photographer reach
for one to move to the next frame — which is most of the gesture, most of
the time. Arrows now move a cursor, shift+arrow extends the selection,
Home/End and PageUp/PageDown cover ground, and Return opens what the cursor
is on.

The cursor is a library ordinal, not a row of the loaded window. The window
is a few screenfuls around wherever the user is looking, so a cursor held as
a row would stop at its edge or keep counting into cells belonging to other
photographs; walking out of the window reloads it around the new position,
exactly as scrolling does. This is the argument the selection already made
by keying on ids rather than rows.

The anchor had that fault for real: it was a window row, so a shift-click
after a scroll extended from whatever image had since drifted into it. It is
an ordinal now, and apply_press takes the window's offset to map between the
two. A range longer than the loaded window truncates to what is loaded,
since selection is by id and an image the catalog has not been asked for has
none.

The pointer and the keyboard share select_row so the two cannot drift apart.
The one deliberate difference: a plain arrow collapses the selection onto
the cursor, where a plain click on an already-selected cell leaves it alone
— that exception exists so a multi-image drag can start from one of its
members, and there is no drag behind a keystroke.

The grid scrolls to the cursor only when the cursor leaves the viewport, and
then by as little as will do it. Reusing the scrub's seek() would put the
cursor's row at the top on every press, which makes a row unreadable as you
walk along it.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-16 16:25:13 +02:00
co-authored by Claude Opus 5
parent 9b2ee0d0eb
commit 70435b712e
5 changed files with 483 additions and 43 deletions
+38
View File
@@ -440,6 +440,44 @@ there is hover state but no *selection* state.
**Done when.** The grid reads as images rather than boxes, the current image
is unambiguous, and arrows plus Enter navigate it without the mouse.
### Keyboard navigation — **done**
Arrows walk the grid, shift+arrow extends the selection, Home/End reach the
ends, PageUp/PageDown move by a screenful, and `Return` opens what the cursor
is on. With the judgement keys the grid already bound, a culling pass is now a
keyboard job end to end — which is the point: a cull is thousands of decisions,
and reaching for the mouse between each is the difference between an hour and
an evening.
**The cursor is a library ordinal, not a row of the loaded window** — that is
the whole design, and it is the same argument the selection already made by
keying on ids. The window is a few screenfuls around wherever the user is
looking; a cursor held as a row would stop at the window's edge or, worse, keep
counting into cells that belong to other photographs. Walking out of the window
reloads it around the new position, exactly as scrolling does.
The same fault was already live in the **anchor**, which was a window row: a
shift-click after a scroll extended from whatever image had drifted into that
row. It is now an ordinal too, and `apply_press` takes the window's offset to
map between the two. A range longer than the loaded window truncates to what is
loaded — selection is by id, and an image the catalog has not been asked for
has no id to select — which is the honest failure, not the silent one.
`select_row` is shared by the pointer and the keyboard so the two cannot drift:
"click here, shift+down twice" has to mean what "click here, shift-click there"
means. The one deliberate difference is that a plain arrow collapses the
selection onto the cursor, where a plain *click* on an already-selected cell
leaves it alone — that exception exists so a multi-image drag can start from
one of its members, and there is no drag behind a keystroke.
**Not done here:** a cursor marker distinct from the selection ring. A plain
arrow selects what it lands on, so the ring shows it; only during a shift
extension is the moving end indistinguishable from the rest of the range.
That wants a `cursor` flag on `LibraryCell` and a second ring treatment.
**Still open in D:** cells cropping to fill, the cell background receding to
the ground, and hover reading distinctly from selection.
---
## Workstream E — Chrome hierarchy