# Koshi 0.6.0 — keybinding.kdl (/docs/v0.6/configuration/keybinding-kdl)



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](/docs/v0.6/configuration#where-the-files-go). Each terminal
reads it for itself when it attaches, so two terminals on one session can use
different shortcuts.

<Callout type="warn">
  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.
</Callout>

## Top-level settings [#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 [#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`                                                                                                                                      |

<Callout type="warn">
  Always bracket named keys. A bare word like `Tab` is read as one chord **per
  character** — `T`, then `a`, then `b`.
</Callout>

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 [#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 [#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`, `-right`
* `core:resize-pane-left`, `-down`, `-up`, `-right`
* `core:focus-next-floating-pane`, `core:focus-previous-floating-pane`
* `core: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 [#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.

<Callout>
  `core:quit` leaves the session; it does not end one. See
  [Actions](/docs/v0.6/keybindings/actions).
</Callout>

## The complete default keymap [#the-complete-default-keymap]

```kdl title="keybinding.kdl"
// 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 [#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 [#checking-your-work]

```bash
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
```
