Name the key on the row that needs it, and stop hardcoding keys - #16
Merged
Merged
Conversation
The keymap is vim's, which is transparent if you know vim and opaque otherwise: nothing on screen said that the ▸ on a folded turn opens with `za`. The selected row — and only the selected row — now carries a right-aligned hint for the one action it affords: `za open`, `za fold`, `→ expand`, `⏎ switch`, `space crop`, `u restore`. It is cursor chrome, passed into `rowLine` as an argument the live-search path does not pass, so `/` never matches it, and it gives way to the row's own text rather than clipping it on a narrow terminal. One vim-faithful addition alongside it: `l`/`→` opens a folded turn, the way vim's `foldopen` default (`hor`) opens a fold on a horizontal move. Only opening — nothing in vim closes a fold by moving, so `h`/`←` keep their branch meaning and `za`/`zc` stay the way to close one. On a turn row this key did nothing at all before, which is what left it free. Neither is any use if the key named is not the key bound, so every key on screen now comes from `keyLabel(command, keybinds)`: the ? pane, all five footers, `goVerb`, the inspector's Crop and branch lines, the zr/zm notices, the discard gate. `test/help.test.ts` runs its whole suite twice — once on the defaults, once with every verb it documents rebound and `branch` unbound — which is what a pane assembled from string literals cannot survive. Three bugs it caught on the way in: - the decisions footer still advertised `E export` after export moved to `ge` in beta.1; - `keyLabel` wrote a modifier with no separator, so a rebind to `ctrl+u` displayed as `ctrlu`; - an unbound command left a headless ` branch — try something risky…` rather than dropping its clause. And the tree footer was the one line on screen with no width budget (the header and the status line both had one), overflowing at about 97 columns and more with a long branch name. `core/help.ts#footerLine` now drops verbs from the right until it fits, keeping `? help` last since it is the route to everything it dropped; it is unit-tested at every width from 40 to 200. `test/docs-links.test.ts` also holds the guide's `keybinds` command list against DEFAULT_KEYS now — the list is a hand copy, and a name missing from it is a rebind nobody can discover, since an unknown command name is silently ignored. The fold e2e is rewritten for the world beta.2 actually ships: the default posture opens one turn, not three, so its old assertions were stale. It now walks the real tree — gg, za, zm, l, zr — and checks the row hint appears on the cursor's row and only there. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VkCWU9Ab6wYHmTke8vuVMv
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.
The keymap is vim's, which is transparent if you already know vim and opaque otherwise: nothing on screen said that the
▸on a folded turn opens withza.The row tells you the key
The selected row, and only the selected row, carries a right-aligned hint for the one action it affords:
za open/za foldon a turn ·→ expand/⏎ switchon a branch ·space cropon a fat tool result ·u restoreon a cropped one.It is cursor chrome, not content, so it is passed into
rowLineas an argument that the live-search path does not pass —/can never match it. It gives way to the row's own text rather than clipping it on a narrow terminal, and crop mode and the?pane suppress it (both already own a key legend of their own).l/→opens a folded turnvim's
foldopendefault includeshor: a horizontal move opens the fold under the cursor. Only opening — nothing in vim closes a fold by moving, soh/←keep their branch meaning andza/zcstay the way to close one. On a turn row this key did nothing at all before (at depth 0 there is no branch to expand, and deeper the branch is already shown), which is what left it free to mean this.Nothing names a key with a string literal any more
A hint is worthless if the key it names is not the key that is bound, so every key on screen now comes from
keyLabel(command, keybinds): the?pane, all five footers,goVerb, the inspector'sCropand branch lines, thezr/zmnotices, the discard gate.UNDO_KEYis gone.test/help.test.tsruns its whole suite twice — once on the defaults, once with every verb it documents rebound andbranchunbound — which is exactly what a pane assembled from literals cannot survive. Three bugs it caught on the way in:E exportafter export moved togein beta.1;keyLabelwrote a modifier with no separator, so a rebind toctrl+udisplayed asctrlu;branch — try something risky…instead of dropping its clause.The footers fit the terminal
The tree footer was the one line on screen with no width budget — the header and the status line both had one — and it overflowed at about 97 columns, more with a long branch name.
core/help.ts#footerLinedrops verbs from the right until the line fits, keeping? helplast since it is the route to everything it dropped.Drift guards
test/help.test.ts— the doubled suite above, plus unit tests forkeyLabel,rowHint, andfooterLineat every width from 40 to 200.test/docs-links.test.ts— the guide'skeybindscommand list is now held againstDEFAULT_KEYS. The list is a hand copy, and a name missing from it is a rebind nobody can discover, since an unknown command name is silently ignored.test/e2e/tui.test.ts— the fold test is rewritten for the world beta.2 actually ships. The default posture opens one turn, not three, so its old assertions were stale; it now walks the real tree (gg,za,zm,l,zr) and checks the hint appears on the cursor's row and only there.I mutation-checked the two guards that matter: restoring a literal
c cropand restoring a headless clause each fail the rebind pass.Verification
bun run typecheck,bun test(451 pass, 0 fail),bun run build— clean.CTREE_E2E=1, OpenCode 1.18.26 in a pty), including the hint andl-opens-a-fold. The full e2e suite was still running at the time of writing; I will report it on this PR if anything comes back red.What to look at by hand
/tree, move up to a folded turn — the row should readza openon the right, and the hint should move with the cursor.lon it opens it;hshould not re-fold it.gs consumers, thenu undo, and never wraps."keybinds": { "fold_toggle": "f2", "crop": "none" }— the hint saysf2 open, the pane saysf2 fold this turn, and crop disappears from both the pane and the footer rather than showing a keylesscrop —.🤖 Generated with Claude Code
https://claude.ai/code/session_01VkCWU9Ab6wYHmTke8vuVMv
Generated by Claude Code