Skip to content

fix(tui): navigate the transcript with keypad Home and End - #59770

Open
argszero wants to merge 1 commit into
anomalyco:devfrom
argszero:numpad-prompt-home
Open

argszero wants to merge 1 commit into
anomalyco:devfrom
argszero:numpad-prompt-home

Conversation

@argszero

@argszero argszero commented Oct 27, 2026 •

Copy link
Copy Markdown
Contributor

Issue for this PR

Closes #55533. The same bug was reported separately as #57113, already closed as a duplicate of #55533.

Type of change

  • Bug fix
  • New feature
  • Refactor / code improvement
  • Documentation

What does this PR do?

Under the kitty keyboard protocol a terminal reports NumPad navigation keys as their own functional key codes. @opentui/core 0.6.8 decodes them as kppageup (CSI 57421 u), kppagedown (CSI 57422 u), kphome (CSI 57423 u) and kpend (CSI 57424 u), and @opentui/keymap accepts all four as named single-stroke keys — but no default binding claimed any of them. So the keypad key next to a working key was silently dropped: in a session, plain End jumps to the last message and NumPad End does nothing.

This PR lists each keypad key beside the key it mirrors, in packages/tui/src/config/keybind.ts:

command before after
session.first ctrl+g,home,alt+home ctrl+g,home,alt+home,kphome
session.last ctrl+alt+g,end ctrl+alt+g,end,kpend
session.page.up pageup,ctrl+alt+b pageup,ctrl+alt+b,kppageup
session.page.down pagedown,ctrl+alt+f pagedown,ctrl+alt+f,kppagedown

Why the session navigation commands and not the prompt. These are the navigation keys whose plain counterpart already has exactly one meaning in the session view, and that meaning is transcript navigation. Plain Home and End are deliberately reserved for the transcript: input.buffer.home and input.buffer.end default to none, and config-v2.test.tsx pins that with reserves home and end for navigation. Binding the keypad keys to the buffer commands instead would give NumPad Home a different meaning from the plain Home key beside it, which is the split #55533 asks to be rid of. Users who want the prompt behaviour still get it from the keybinds override the issue itself shows.

Scope. The NumPad arrow keys are not included: up and down are shared by the prompt, history, dialogs and the session, so mirroring them changes far more than the reported End/Home/PageUp/PageDown case. alt+end (session.messages_last_user) is not mirrored either, since there is no natural keypad chord for it.

An earlier PR of mine, #55870, proposed the same keypad mirrors; I closed it when the prompt half looked like the target. This PR is that direction, aimed at the transcript half #55533 actually reports.

How did you verify your code works?

  • New test mirrors plain navigation keys on the keypad in config-v2.test.tsx asserts, for each of the four commands, that the resolved bindings contain both the plain key and the keypad key. It runs against resolve() from src/config, the table the TUI loads (src/app.tsx). config.test.tsx pins the legacy src/config/v1 table, which only config migration still reads (packages/cli/src/config/migrate.ts), so it is unchanged by design.
  • Red before green: with the four default values reverted to their previous strings, the new test fails (1 fail, 35 pass); with the change, config, config-v2 and keybind pass (37 pass, 0 fail).
  • The key names and codepoints above are read from the installed @opentui/core 0.6.8 (functional-key table) and @opentui/keymap 0.6.8 (named single-stroke key set), not from memory. The branch installs the versions the workspace catalog pins.
  • tsc -b clean for packages/tui; oxlint and prettier --check clean on both changed files.
  • I could not press a physical NumPad key while testing: this machine has no numeric keypad. The link from key to command therefore rests on the core's codepoint table and the keymap's named-key set cited above, plus tui: NumPad End/Home ignored under kitty keyboard protocol (Windows Terminal reports KP_END 57424) #55533's reproduction, which shows the terminal sending ^[[57424u while the binding never fires.

Checklist

  • I have tested my changes locally
  • I have not included unrelated changes in this PR

Added by the opencode bot (AI-generated).

Related: #58946

NumPad Home, End, PageUp and PageDown arrive as `kphome`, `kpend`,
`kppageup` and `kppagedown` (kitty `CSI 57423 u`, `CSI 57424 u`, and
`CSI 57421 u` / `CSI 57422 u`), while the session bindings list only the
plain `home`, `end`, `pageup` and `pagedown`. The keypad key beside a
working key therefore does nothing: pressing NumPad End in the transcript
does not jump to the last message.

List the keypad key beside the key it mirrors on `session.first`,
`session.last`, `session.page.up` and `session.page.down`. Plain Home and
End stay reserved for transcript navigation, so `input.buffer.home` and
`input.buffer.end` keep their empty defaults.

@opencodejr opencodejr Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This fixes it in the transcript. Using the simulated model, I scrolled a long answer to the top and sent the kitty keypad sequences. On the base branch, NumPad End (CSI 57424 u) did nothing. With this PR it jumps to the last message, and NumPad Home and NumPad PageDown also work. The config and config-v2 tests pass, and tsc -b is clean for packages/tui.

Public API: no config keys or schema change. Only the default values of four existing keybinds gain an extra key, so existing user overrides keep working.

Binding the transcript commands rather than the prompt makes sense, since plain Home and End do nothing in the prompt either.

Not blocking: the dialog list bindings (dialog.select.home/end/page_up/page_down) and the diff viewer bindings (diff.first/last/page.up/page.down) still list only the plain keys, so the keypad keys still do nothing in dialogs and the diff viewer. You could mirror them here too or leave them for a follow-up.

For a fix this small, the new test and its comment aren't needed. They can be dropped to keep the change minimal.

Related: #58946

Before

After

@argszero

Copy link
Copy Markdown
Contributor Author

Both points checked rather than assumed, and I am declining both for now — say so if you disagree on either.

The dialog and diff-viewer gap is real, and it is a deliberate follow-up. I confirmed it on the branch: dialog.select.home, dialog.select.end, dialog.select.page_up, dialog.select.page_down, diff.first, diff.last, diff.page.up and diff.page.down list only the plain keys, so the keypad keys are still inert there. I left them out because #55533 reports the transcript, and mirroring eight more bindings changes the behaviour of dialogs and the diff viewer — surfaces the report never touches — in a PR whose whole claim is that it is the transcript fix. If you want them in scope, I will add them; otherwise a follow-up is the honest place for them.

I would rather keep the test. The red-before-green measurement in the description depends on it: with the four defaults reverted the test fails, with them it passes. Dropping it would leave the claim unverified in the repo, which is the part a later change to these defaults would want a guard on. If the concern is the comment above the test rather than the test itself, I can shorten that.

Nothing in the diff changed for this reply; the head is still 4c1cfb9335. Also re-checked against today's dev (86ce19d96ecc), which has since added model.dialog.usage and usage.show to the same file: the branch still applies cleanly and all four mappings survive the merge.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

tui: NumPad End/Home ignored under kitty keyboard protocol (Windows Terminal reports KP_END 57424)

1 participant