keybinding.kdl
Rebind koshi's keys — leader, chord timeouts, key grammar, repeating keys, and the mode blocks for normal, locked, pane moves, the floating pane tray, and the notification center.
User bindings are applied over the built-in ones. A user binding wins for the same key; every key you do not mention keeps its built-in action.
The file sits directly in the config directory. Each terminal reads it for itself when it attaches, so two terminals on one session can use different shortcuts.
Any error drops the whole file. Run koshi keys conflicts after editing — it
exits 2 when koshi would drop the file — and koshi keys list to see what is
actually active.
Top-level settings
version is required. Every other setting is optional and may appear at most
once, beside the mode blocks.
| Key | Value | Default | Since |
|---|---|---|---|
chord-timeout-ms | integer — ambiguity delay when one sequence is both a complete binding and the prefix of a longer binding | 500 | ≥ 0.1.0 |
which-key-delay-ms | integer — ms from a shortcut's first key until the hint bar shows the keys that continue it; 0 shows them with the first key | 300 | ≥ 0.1.0 |
max-chord-depth | integer 0–255 — most keys one shortcut may chain. A shortcut longer than this can never fire, and koshi keys conflicts reports it | 4 | ≥ 0.1.0 |
leader | string — what <leader> stands for: a modifier run like "C-", or a single chord like "<Space>" | "C-" | ≥ 0.1.0 |
unlock-alternative | string — the chord that leaves locked mode, in place of <C-l> | none | ≥ 0.1.0 |
Key grammar
Keys use angle brackets. Spaces join keys into a sequence.
| Written | Means |
|---|---|
<C-t> | Ctrl+t. Modifiers are C Ctrl, A Alt, S Shift, D Super |
<A-S-n> | Several modifiers on one key |
<Tab> <CR> <Esc> <BS> <Space> | Named keys. The others are <Del>, <Insert>, <Home>, <End>, <PageUp>, <PageDown>, <Left>, <Right>, <Up>, <Down>, and <F1> to <F24> |
T | An uppercase letter is that letter with Shift: <S-t> |
<leader>p | The leader, then p |
Always bracket named keys. A bare word like Tab is read as one chord per
character — T, then a, then b.
With leader "C-", <leader>p is <C-p>. With leader "<Space>" it becomes
<Space> then p. Explicit keys such as <A-f> never move when the leader
changes.
Key sequences
A key that starts a longer shortcut opens a sequence. While it is open, no key
reaches the program. A sequence that is not also a complete binding waits for
its next key with no time limit; a key that continues nothing is discarded and
the sequence stays open. <Esc> ends it. For example, <C-p> then z
discards z, and a following n opens a new pane.
Repeating keys
These actions repeat from their prefix: after a shortcut of two or more keys fires one, the prefix stays open and the last key alone fires it again.
core:focus-pane-left,-down,-up,-rightcore:resize-pane-left,-down,-up,-rightcore:focus-next-floating-pane,core:focus-previous-floating-panecore:scroll-pane-up,core:scroll-pane-down
<C-p> <Left> <Left> <Down> focuses left, left again, then down. A key that
fires a non-repeating action ends the prefix; <Esc> ends it too.
koshi keys describe prints continuous: true for these shortcuts.
mode blocks
A mode block holds the bindings for one mode. "normal" and "locked" are
the base modes: locked passes keys straight to the program except the unlock
chord. The other modes are open only while their screen is:
| Mode | Open while |
|---|---|
"pane-placement" | The tiled pane move preview is open |
"pane-move-choice" | The move chooser is open |
"floating-pane-move" | Floating pane move mode is open |
"floating-pane-tray" | The floating pane tray is open |
"notification-center" | The notification center is open |
A block for any other mode name binds nothing, and koshi keys conflicts warns
that the mode is unregistered.
| Node | Meaning |
|---|---|
bind "<key>" "core:action" | Two strings: the key sequence and the full, namespaced action |
remove "<key>" | Void a key in this mode so lower layers no longer bind it |
A bare action name like new-tab is rejected with a hint — write
core:new-tab. A bind takes no arguments: a fixed choice lives in the action
name (core:new-pane-left), an open value lives in the CLI. Run
koshi actions list for every name you can bind to, and
koshi actions run <action> to run one from a shell.
core:quit leaves the session; it does not end one. See
Actions.
The complete default keymap
// keybinding.kdl — the complete default keymap.
version 2
chord-timeout-ms 500
which-key-delay-ms 300
max-chord-depth 4
leader "C-" // the leader = the Ctrl modifier run (default)
// unlock-alternative "<A-u>" // replaces <C-l>; also bind it to core:unlock in "locked"
mode "normal" {
// reserved lock/unlock chord — explicit, never moves with the leader
bind "<C-l>" "core:lock"
// leader-relative — rebind `leader` and all of these move together
bind "<leader>q" "core:quit"
bind "<leader>g" "core:mouse-select"
bind "<leader>p n" "core:new-pane" // <C-p> n with the default leader
bind "<leader>p h" "core:new-pane-left"
bind "<leader>p j" "core:new-pane-down"
bind "<leader>p k" "core:new-pane-up"
bind "<leader>p l" "core:new-pane-right"
bind "<leader>p s" "core:new-pane-stacked"
bind "<leader>p f" "core:new-pane-floating"
bind "<leader>p u" "core:toggle-pane-floating"
bind "<leader>p <Tab>" "core:focus-next-floating-pane" // repeats
bind "<leader>p <S-Tab>" "core:focus-previous-floating-pane" // repeats
bind "<leader>p w" "core:toggle-floating-pane-tray"
bind "<leader>p i" "core:toggle-pane-pinned"
bind "<leader>p m" "core:begin-pane-placement"
bind "<leader>p x" "core:close-pane-tree"
bind "<leader>p <Left>" "core:focus-pane-left" // repeats
bind "<leader>p <Down>" "core:focus-pane-down"
bind "<leader>p <Up>" "core:focus-pane-up"
bind "<leader>p <Right>" "core:focus-pane-right"
bind "<leader>s <Left>" "core:resize-pane-left" // repeats
bind "<leader>s <Down>" "core:resize-pane-down"
bind "<leader>s <Up>" "core:resize-pane-up"
bind "<leader>s <Right>" "core:resize-pane-right"
bind "<leader>t n" "core:new-tab" // <C-t> n with the default leader
bind "<leader>t x" "core:close-tab"
// explicit chords — they never move with the leader
bind "<A-f>" "core:toggle-pane-fullscreen"
bind "<A-n>" "core:toggle-notification-center"
bind "<Tab>" "core:next-tab"
bind "<S-Tab>" "core:previous-tab"
}
mode "locked" {
// locked mode passes every other key straight to the program.
bind "<C-l>" "core:unlock"
bind "<leader>q" "core:quit"
bind "<leader>g" "core:mouse-select"
}
mode "pane-placement" {
bind "<Left>" "core:select-pane-target-left"
bind "<Down>" "core:select-pane-target-down"
bind "<Up>" "core:select-pane-target-up"
bind "<Right>" "core:select-pane-target-right"
bind "<S-Left>" "core:select-pane-insertion-left"
bind "<S-Down>" "core:select-pane-insertion-down"
bind "<S-Up>" "core:select-pane-insertion-up"
bind "<S-Right>" "core:select-pane-insertion-right"
bind "<Space>" "core:cycle-pane-placement-span"
bind "<Tab>" "core:select-next-placement-tab"
bind "<S-Tab>" "core:select-previous-placement-tab"
bind "s" "core:toggle-pane-placement-stack"
bind "<CR>" "core:confirm-pane-placement"
bind "<Esc>" "core:cancel-pane-placement"
}
mode "pane-move-choice" {
bind "t" "core:begin-tiled-pane-placement"
bind "f" "core:begin-floating-pane-move"
bind "<CR>" "core:confirm-pane-move-choice"
bind "<Esc>" "core:cancel-pane-move-choice"
}
mode "floating-pane-move" {
bind "<Left>" "core:move-floating-pane-left"
bind "<Down>" "core:move-floating-pane-down"
bind "<Up>" "core:move-floating-pane-up"
bind "<Right>" "core:move-floating-pane-right"
bind "<Tab>" "core:select-next-floating-pane"
bind "<S-Tab>" "core:select-previous-floating-pane"
bind "<CR>" "core:confirm-floating-pane-move"
bind "<Esc>" "core:cancel-floating-pane-move"
}
mode "floating-pane-tray" {
bind "<Left>" "core:select-tray-tile-left"
bind "<Down>" "core:select-tray-tile-down"
bind "<Up>" "core:select-tray-tile-up"
bind "<Right>" "core:select-tray-tile-right"
bind "<CR>" "core:activate-selected-tray-tile"
bind "a" "core:toggle-minimize-all-floating-panes"
bind "<Esc>" "core:close-floating-pane-tray"
bind "<leader>p w" "core:toggle-floating-pane-tray"
}
mode "notification-center" {
bind "<Up>" "core:select-newer-notification"
bind "<Down>" "core:select-older-notification"
bind "x" "core:dismiss-selected-notification"
bind "c" "core:clear-notifications"
bind "<Esc>" "core:close-notification-center"
bind "<A-n>" "core:toggle-notification-center"
}Moving a pane
<leader>p m starts a move preview in the current client. When the client
shows a floating pane it did not pin, it opens the "pane-move-choice" chooser
first. The preview's mode name is "pane-placement"; its keymap takes priority
over the normal or locked map until the move is accepted or cancelled. The
base mode is retained, so cancelling a move started from locked mode returns to
locked mode. The default locked map binds no key that opens it; a locked client
can drag a pane's grab handle, or you can bind a key in the locked block.
Arrow keys choose a pane to swap with. Shift+Arrow chooses an edge to insert
beside the selected pane or stack. Space cycles the available split spans.
Tab and Shift+Tab preview another visible tab, where the same keys can choose
a pane or insertion edge. s switches the target between a pane and its
stack. Enter accepts the move; Escape cancels it.
Each local mode must keep a live binding for its closing action, or the file
is rejected: core:cancel-pane-placement in pane-placement,
core:cancel-pane-move-choice in pane-move-choice,
core:cancel-floating-pane-move in floating-pane-move,
core:close-floating-pane-tray in floating-pane-tray, and
core:close-notification-center in notification-center.
Only the client that started the move sees the preview. After it is accepted,
all clients see the new layout. By default the mode stays active after an
accepted move; stay-in-pane-placement-mode-after-placement #false makes it
end instead. Mouse drags can also preview and accept a move. To insert with a
mouse, hold Shift when starting the drag.
Checking your work
koshi keys validate ~/.config/koshi/keybinding.kdl # before adopting it
koshi keys conflicts # clashes and dead shortcuts
koshi keys describe "<C-p> l" # what one sequence does
koshi keys list --scope user # only what you added