fix(tui): , walks the view history forward (improvise-htl6)

`view-forward` was bound in NO keymap, in either front end, from the day it was
written. The TUI reached it as `:view-forward`; a browser could not reach it at
all, because `declined()` refuses to enter CommandMode at the door. It survived
because pressing `<` twice is the only way to observe the forward stack, and
until PR #115 no client maintained one.

THE KEY IS `,`, AND `>` IS WHY THE OBVIOUS ANSWER IS WRONG. `<` is `view-back`
and `>` is `drill-into-cell`, so the pair that LOOKS symmetric is not one: `>`
moves within the grid, `<` moves between views. Binding the forward half to
anything reading as `>`'s partner would deepen the confusion that hid the gap.
`,` is the unshifted twin of `<`, so both halves of the history sit on one
physical key, and it carries no direction-in-the-grid meaning.

IT IS DELIBERATELY NOT A CTRL- KEY. Ctrl-R, the universal "forward through a
history" spelling, is browser reload. The keymap is SHARED between the two front
ends, so a key chosen for the terminal is one the browser must live with, and
improvise-clso is already open for exactly this against Ctrl-S.

The absence assertion PR #117 placed in
`the_forward_step_mirrors_its_cmd_and_no_key_reaches_it` is INVERTED rather than
deleted: renamed `..._and_one_key_reaches_it`, it now asserts the binding exists,
keeps `view-back` as the control, and pins the key itself so the binding cannot
drift while the presence check stays happy.

Reasoning: context/code/tui.md.
Evidence: context/test-evidence/{command,clients}.md.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FC2jVn8Au7E9JPkw6bwXXH
This commit is contained in:
Edward Langley
2026-09-22 19:25:18 -07:00
parent 57be9144d1
commit e645ec883d
7 changed files with 137 additions and 10 deletions
+41
View File
@@ -338,3 +338,44 @@ draw one cache and nothing else. Two facts worth keeping:
the selected tile still fits in the available width, and draws `‹`/`›` overflow
indicators. That is the file's only piece of real arithmetic and it is local
geometry, not a shared derivation.
## `,` walks the view history forward (improvise-htl6)
`view-forward` was bound in **no** keymap, in either front end, from the day it
was written. The TUI reached it as `:view-forward` and a browser could not reach
it at all, because `declined()` refuses to enter `CommandMode` at the door. It
survived because pressing `<` twice is the only way to observe the forward stack
in the first place, and until PR #115 no client maintained one.
**The key is `,`, and `>` is why the obvious answer is wrong.** `<` is
`view-back` and `>` is `drill-into-cell`, so the pair that *looks* symmetric is
not one — `>` moves within the grid, `<` moves between views. Binding the
forward half to anything that reads as `>`'s partner would deepen exactly the
confusion that hid the gap. `,` is the unshifted twin of `<`, so both halves of
the history sit on one physical key, and it carries no direction-in-the-grid
meaning to collide with.
**It is deliberately not a `Ctrl-` key.** `Ctrl-R`, the universal "forward
through a history" spelling, is browser reload; `Ctrl-S` and `Ctrl-W` are
already bound here and `Ctrl-S` is already a filed bug for precisely that reason
(improvise-clso, "Ctrl-S saves and also opens the browser's own save dialog").
The keymap is **shared** between the two front ends, so a binding chosen for the
terminal is a binding the browser must also live with, and adding a second
instance of an open bug is not a thing to do by accident.
Nothing else moved. `help.rs` keys its lines off the command *name* and looks
the key up in the keymap, so the overlay and the hint line follow the binding;
`every_bound_command_name_is_registered` already covers the new entry. The
absence assertion PR #117 put in
`resolve::tests::the_forward_step_mirrors_its_cmd_and_no_key_reaches_it` was
**inverted rather than deleted** — the test is renamed `…_and_one_key_reaches_it`
and now asserts the binding exists, with `view-back` kept as the control so it
cannot pass against a table that binds everything or nothing. Coming there and
saying so was the point of that assertion, and an inverted guard keeps the
property under watch where deleting it would have let the key quietly go away
again.
`,` is not advertised in `AppMode::Normal`'s hint line, and neither is `<`: the
hint is width-constrained and the history lives on help page 5 under "View
history" with both halves. That is the #149 lesson — a hint that does not fit is
never drawn, so the test pinning the widget cannot see it.