Keybindings

August 16, 2026 · View on GitHub

Every keyboard shortcut is a named command, and the [keybindings] table maps command ids to the keys you want them on:

[keybindings]
"hunk.app.quit" = "ctrl+x"               # one chord
"hunk.review.nextHunk" = ["]", "ctrl+n"] # several chords for one command
"hunk.review.focusFilter" = "f"          # takes "f" away from page-down
"hunk.view.toggleMenuBar" = false        # unbind it entirely
"myext.toggle" = "ctrl+g"                # extension commands too

Every id starts with the name of whoever owns the command: Hunk's own commands live under hunk., and an extension's live under its extension id. That split is structural — hunk is a reserved extension id, so an extension can never mint a command id that shadows a built-in, whatever Hunk adds later.

Rules worth knowing:

  • User bindings replace defaults. Listing chords for a command is the complete set of keys it answers to, not an addition to the shipped ones.
  • A key you bind is yours. Any command that held the same chord only as a default gives it up, keeping its other keys. Above, page-down still answers to PageDown and Space after f moves to the filter.
  • false (or []) unbinds a command, leaving its keys doing nothing.
  • Two entries claiming one chord is a conflict: the first in the file wins and the session reports the other. Unknown command ids and unusable chords are reported the same way, and the rest of the table still applies.

Chords are ctrl, alt/option, cmd/meta, and shift joined with + around a base key: a character ("y", "["), an uppercase letter for its shifted form ("G"), or a named key ("tab", "pageup", "left", "f2"). shift applies to letters and named keys only — for a shifted symbol or digit, write the character the shift produces ("!", not "shift+1"), since that is what terminals report. ctrl+<letter> also matches an unnamed bare control byte; named Tab and Enter events stay distinct.

The built-in commands and the keys they ship with:

Command idDoesDefault keys
hunk.app.openAgentSkillShow agent skill(none)
hunk.app.quitQuitq
hunk.app.refreshRefresh the reviewr
hunk.app.toggleFocusAreaSwitch focus between files and filtertab
hunk.app.toggleHelpToggle help?
hunk.review.alignCurrentLineBottomAlign current line to viewport bottom(none)
hunk.review.alignCurrentLineCenterCenter current line in viewport(none)
hunk.review.alignCurrentLineTopAlign current line to viewport top(none)
hunk.review.editSelectedFileOpen the selected file in your editore
hunk.review.focusFilterFocus the file filter/
hunk.review.halfPageDownScroll down half a paged
hunk.review.halfPageUpScroll up half a pageu
hunk.review.jumpToBottomJump to endG, end
hunk.review.jumpToTopJump to startg, home
hunk.review.nextAnnotatedFileNext annotated file(none)
hunk.review.nextAnnotatedHunkNext annotated hunk}
hunk.review.nextFileNext file.
hunk.review.nextHunkNext hunk]
hunk.review.pageDownScroll down one pagepagedown, space, f
hunk.review.pageUpScroll up one pagepageup, b, shift+space
hunk.review.previousAnnotatedFilePrevious annotated file(none)
hunk.review.previousAnnotatedHunkPrevious annotated hunk{
hunk.review.previousFilePrevious file,
hunk.review.previousHunkPrevious hunk[
hunk.review.scrollCodeLeftScroll code left (shifted scrolls fast)left, shift+left
hunk.review.scrollCodeRightScroll code right (shifted scrolls fast)right, shift+right
hunk.review.startNoteAdd a review notec
hunk.review.stepDownScroll down one rowdown, j
hunk.review.stepUpScroll up one rowup, k
hunk.review.toggleHunkGapExpand or collapse the selected contextz
hunk.view.applyFilePresentationToAllMatchingApply current file presentation to all matches(none)
hunk.view.cursorLineNumberMark the current line number(none)
hunk.view.cursorLineOffHide the current-line marker(none)
hunk.view.cursorLineRowHighlight the current row(none)
hunk.view.layoutAutoAuto layout0
hunk.view.layoutSplitSplit layout1
hunk.view.layoutStackStack layout2
hunk.view.openThemeSelectorChoose themet
hunk.view.toggleAgentNotesToggle agent notesa
hunk.view.toggleCopyDecorationsToggle copy decorations(none)
hunk.view.toggleFilesPaneToggle files panes
hunk.view.toggleHunkHeadersToggle hunk headersm
hunk.view.toggleLineNumbersToggle line numbersl
hunk.view.toggleLineWrapToggle line wrappingw
hunk.view.toggleMenuBarToggle menu barM

The files-pane command follows the named hunk:files role. If an extension replaces that role, the command and View → Files pane toggle the resolved replacement on any terminal edge without changing unrelated panes. Remapping or unbinding hunk.view.toggleFilesPane changes that role-aware action, not an extension pane's own commands. The former hunk.view.toggleSidebar id remains a compatibility alias; prefer the files-pane name in new config and extension code.

Commands marked (none) ship without a key: they remain callable by command id and can be assigned a shortcut through [keybindings]. Some also appear in a menu, while semantic commands such as current-line alignment do not need a menu entry.

The menus and the controls help dialog (?) show the keys for the commands they present, so remapping something changes what they advertise. Unbinding a menu command keeps its menu item and simply stops showing a key.

Extension commands are named <extensionId>.<commandId> and remap the same way (see docs/extensions.md). An explicitly activated extension keyboard mode is a routing layer rather than a second command table: it may consume a key, pass it to these resolved bindings, or consume it and exit. Its multi-key grammar and counts are extension-owned, but resolved actions should invoke these same public hunk.* commands.

Routing precedence is host prompts and dialogs, menus/overlays, focused text inputs, an interactive file-view mode, a session extension keyboard mode, then the command table and focused review widget. Keys that belong to a dialog, menu, or focused text input — Esc, Enter, Ctrl-S while writing a note — are part of those widgets rather than commands, and are not remappable. Escape is also the reserved exit from each active extension mode, so an extension cannot trap the keyboard.

[keybindings] is read from your user config only — never from a repository's .hunk/config.toml. Which keys do what is a property of your keyboard and your habits, so a checkout you review cannot rearrange them.