Skip to content

Keybindings, overview

gitoui has a two-layer keybind system:

  1. A global map of UserEvent → key list. Same shape as upstream serie: quit = ["q"], navigate_up = ["k", "up"], etc. Available everywhere.
  2. Per-view scopes, [keybind.scope.<path>] sub-tables that override the global map for a specific view. Scopes nest: pr.conversation walks back to pr then to the global section if a key isn’t bound in the leaf.

The two layers exist for one reason: views need keys to mean different things. a is stage globally (Uncommitted view) but approve in PR review; d is delete_own_comment in PR conversation but drop_commit in the commit list. A flat key map can’t represent that, scopes can.

  1. Scoped lookup first, the view checks its scope chain (deepest path → walks parents, e.g. pr.conversationpr).
  2. Global fallback, if the scoped chain misses, the global UserEvent map fires.
  3. Literal-char escape, if neither matches, the key is forwarded to the view’s handle_event with UserEvent::Unknown so view-local literal-char matches still get a chance.

This is what makes a mean different things across views without needing explicit “if PR-view do X else Y” branching in the dispatcher.

Both global and scoped overrides are REPLACING per event/action, not additive. If your config says approve = ["ctrl-a"], the default a → approve binding is dropped, only Ctrl+A triggers approve.

To keep both keys, list them explicitly:

[keybind.scope.pr]
approve = ["a", "ctrl-a"] # both fire approve

Anything you don’t touch keeps its full default key list.

gitoui accepts at most one modifier prefix per key ctrl-X alt-X shift-X. Chained combos like ctrl-shift-a are rejected at config-load with a styled diagnostic.

Why: the displays stay compact (Ctrl+a vs Ctrl+Shift+a), and most terminals are unreliable with three-key combos anyway. If you need more than one modifier, pick a different base key (F-keys are usually free).

Every scope below ships with default bindings, see Custom keybindings for the full action list per scope.

Scope pathView
listCommit list (the home screen).
detailCommit detail, currently hosts revert (v), which would otherwise collide with clean_untracked in the uncommitted view.
prPull Request, actions on the open PR.
pr.listPR list (browse all PRs).
pr.conversationPR Conversation tab (card-local actions).
issuesIssue, actions on the open issue.
issues.listIssue list.
issues.detailIssue Conversation tab.
rebaseInteractive rebase plan editor.
rebase.resumeResume prompt for paused rebases.
rebase.reword_editorThe inline subject editor when rewording.
conflict3-way conflict editor.
composeCompose forms (new PR / new Issue / comment / reply).

Every footer, sub-header, comment-card ribbon, and the Help page renders the current key for each action by looking it up against your config at paint time. Rebind approve = ["ctrl-a"] and Ctrl+a:approve is what the PR detail footer will show, no app restart, no doc lag.

PathWhat
assets/default-keybind.toml (embedded)The defaults gitoui ships with.
~/.config/gitoui/config.toml[keybind] and [keybind.scope.<path>]Your overrides.

A clean way to see your effective bindings is the in-app Help page (? or F1), every shortcut listed there is computed from your live config.

In-app Help page, sectioned grid of shortcuts per scope, keys drawn from the live keybind map