Keybindings
Vim bindings are the default; set keymap = "emacs" in config (see
Configuration) for the emacs column. q quits katamari from anywhere — a pushed
FileView/timeline/log/inspector/agent panel included, never “back” to whatever’s
underneath. Esc is the generic “get me out of this”: it dismisses the
nearest open overlay (a popup, the hover card, the references panel), and
with nothing local left open it pops exactly the one view a gd/L/t/I
press pushed, revealing what was underneath — at the root diff, where
there’s nothing left to pop, it cancels an active visual selection first,
then widens an active unit scope back to the full diff, then clears a
confirmed search.
Ctrl-o/Ctrl-i are a separate axis entirely: they retrace chronological
cursor history — every significant jump (go to definition/references, a
confirmed search, a diagnostic step, and later a file-tree or mouse jump),
regardless of which feature caused it — not view stacking, so they keep
working exactly the same whether or not Esc has popped anything in
between.
The hint bar along the bottom starts collapsed to a handful of essentials
ending in . more; . expands it to the full list (and back), so the
table below never has to live in your head.
| Action | Vim | Emacs |
|---|---|---|
| Cursor down / up | j / k |
C-n / C-p |
| Half page down / up | C-d / C-u |
C-v / M-v |
| Top / bottom | gg / G |
M-< / M-> |
| Next / prev hunk | ]c / [c |
M-n / M-p |
| Expand / collapse fold | zo / zc |
zo / zc |
| Mark hunk reviewed / advance | r |
r |
| Toggle hunk reviewed | R |
R |
| Mark file reviewed | m f |
m f |
| Mark visible reviewed | m a |
m a |
| Show/hide all reviewed expanded | z R |
z R |
| Next / prev file | ]f / [f |
C-x n / C-x p |
| Next / prev diagnostic | ]d / [d |
M-g M-n / M-g M-p |
| Hover | K |
C-h |
| Go to definition | gd |
M-. |
| Find references | gr |
M-? |
| Jump back / forward | C-o / C-i* (also M-Left/M-Right) |
C-o / C-i* (also M-Left/M-Right) |
| Search diff / next / prev match | / / n / N |
/ / n / N |
| Focus next / prev pane | Tab / BackTab |
Tab / BackTab |
| Next / prev symbol on line | l / h |
M-f / M-b |
| Confirm / cancel | Enter / Esc |
Enter / Esc |
| Toggle sidebar | b |
b |
| Toggle directory (files pane) | Space |
Space |
| Toggle unified/side-by-side | s |
s |
| Toggle timeline | t |
t |
| Toggle log view | L |
L |
| Toggle LSP inspector | I |
I |
| Open scope menu | o |
o |
| Review branch vs base | B |
B |
| Toggle units panel | u |
u |
| Regenerate units | U |
U |
| Open help | ? |
? |
| Toggle hint bar | . |
. |
| Toggle range-select (timeline/log) | v |
C-Space |
| Visual-line select (diff) | V |
V |
| Yank visual selection (diff) | y |
y |
| Add comment | c |
C-c C-c |
| (in comment compose) newline / save / cancel | Enter / C-s / Esc |
same |
| Toggle inline comment bodies | C |
C |
| Ask the agent about this line/selection | a |
a |
| Toggle agent transcript panel | A |
A |
| Push open comments to the agent | p |
p |
| Cancel the agent’s turn | C-g |
C-g |
| Quit | q |
q |
* Jump-forward matches neovim: it’s C-i in terminals that implement the
kitty keyboard protocol
(Ghostty, kitty, WezTerm, iTerm2 3.5+, Alacritty), which lets katamari tell a
literal Tab keypress apart from Ctrl-i on the wire — without it they arrive
as the same byte, and Tab already means focus-next-pane, so katamari
uses M-Right as jump-forward’s canonical binding there instead (notably
Terminal.app, which doesn’t implement the protocol). Detection asks the
terminal directly and, on some terminals, can take a couple of seconds to
hear nothing back — so the answer is cached per terminal ($XDG_STATE_HOME/ katamari/kitty-probe.json, keyed by TERM/TERM_PROGRAM/
TERM_PROGRAM_VERSION) after the first launch in it; every later launch in
that same terminal skips the wait entirely. ktmr reset --cache clears the
cache if you switch terminal emulators and want it re-detected.
M-Left/M-Right are unconditional aliases for back/forward in
both cases — the always-available, terminal-agnostic pair — while C-i
itself is simply left unbound when the terminal can’t distinguish it from
Tab. Ctrl-] and Ctrl-t have no default binding at all: katamari has one
general jump history rather than a separate vim-style tag stack, so there’s
no second “go back” key to bind.
Wheel scrolling works out of the box ([ui] mouse = true, the default):
scrolling over the files pane, the diff pane, a pushed file/timeline/log/
inspector view, or an open hover/references/help overlay scrolls whichever
one is under the pointer, without moving the keyboard cursor or changing
which pane has keyboard focus. A click in the changed-files tree selects a
row, jumps the diff to it, and expands/collapses a directory, the same way
Enter does from the keyboard. A click in the diff or a pushed file pane
moves the cursor to the clicked row/column; clicking an identifier on an
interactive new-side/context/add row runs go-to-definition (the same
readiness-gated action gd does — a click while the server is still
starting shows the same “not ready” status rather than queuing a surprise
later jump), while a gutter, whitespace, a deletion, a side-by-side old
cell, or a non-interactive historical diff only positions the cursor.
Shift-click extends an active visual selection (V) instead, without
triggering go-to-definition. Right-click opens a context-aware action menu
(hover/go to definition/find references on an identifier, expand/collapse
on a tree row, add-comment on a diff row, and more depending on what’s
under the pointer); its entries follow the same LSP-readiness rules as the
keyboard. Drag selection and double-click word selection are not
implemented yet. While capture is on, a terminal’s plain click-and-drag
text selection goes to katamari instead of the terminal — most terminals
still offer native selection by holding Shift while dragging. Set
[ui] mouse = false to leave capture off entirely and get plain, unshifted
drag selection back; katamari makes no attempt to emulate selection itself.
Inside tmux, set -g mouse on is additionally required for wheel events to
reach katamari at all, regardless of [ui] mouse.
Resting the pointer (no click, no button held) on an eligible code symbol
or a changed-file tree row for about 400ms shows details without moving the
keyboard cursor: a code symbol gets the same hover popup K would show
(subject to the same LSP-readiness gating — a not-yet-ready server
just stays quiet rather than queuing a surprise popup later), and a tree
row gets a compact status-bar line with its full path, status, +/-
stats, or old → new path for a rename, plus a changed-descendant count for
a directory. Moving to another target, leaving the pane, pressing a key,
clicking, scrolling, resizing, or opening any overlay cancels it instantly.
Controlled independently of click/wheel/right-click support via
[ui] mouse_hover = true (the default) — false stops katamari from
acting on pointer motion, not from the terminal reporting it:
EnableMouseCapture already requests any-motion reporting whenever
[ui] mouse is on, regardless of mouse_hover.
With a visual selection active (V, above), y copies it to the terminal
clipboard via OSC 52: each selected line’s repo-relative path, old/new line
numbers, and diff marker ( /+/-), grouped by file in selection order —
a path re-entered after the selection has moved on gets its header repeated
rather than merged into the earlier group. Structural rows (file/hunk
headers, fold rows) inside the selection are skipped silently. An empty
result or a payload over the 64 KiB pre-encoding bound (the same limit the
LSP inspector’s own V/y uses) is refused with a status message that
leaves the selection in place to trim and retry; a successful copy clears it,
same as pressing V again.
? from any view opens a floating help window listing every command,
grouped, with its actual key next to it — the bindings shown are live
(preset plus any [keys] override, see Configuration),
never a hardcoded reference the
table above could drift out of sync with. j/k/arrows/C-n/C-p (and
PageDown/PageUp/C-d/C-u, and gg/G for top/bottom) scroll; /
starts a filter that narrows the list live as you type, matching against
each row’s description, its config name, and its key; Enter keeps the
filter and returns to scrolling. Esc while typing the filter clears it
and returns to scrolling; Esc while scrolling the list closes the window
outright, same as q/?, whether or not a filter is still narrowing it.
The window is modal while open — every key goes to it, not whatever view
is underneath.
Ask the agent
Section titled “Ask the agent”Rather than shelling out to an agent CLI fresh for each question,
katamari spawns one coding-agent session per review and keeps it alive
for as long as ktmr diff runs — one persistent, protocol-owned process
instead of a one-shot hand-off, so context carries from question to
question rather than resetting each time.
a opens a small compose box (reusing the comment overlay’s own text
editor) anchored to the cursor’s row or a visual selection (V), and sends
your typed question — together with the selected diff content (path,
old/new line numbers, and each line’s marker) — to a resident coding-agent
session (Claude, via ACP), spawned lazily
the first time you ask or push. Unlike c (add comment), asking works on
any diff, including a historical/read-only one and a selection that
crosses a deletion — there’s nothing to re-anchor later, so nothing to
restrict. A opens or closes a full-screen transcript panel showing the
conversation so far; closing it never stops the session, so reopening it
later picks up right where you left off. p pushes every currently-open
review comment to the agent in one message, the same prompt
ktmr agent-check sends, and opens the panel so you can watch it work.
Whenever the agent wants to make an edit, katamari shows a small modal
naming the tool and waits for y/Enter (allow) or n/Esc (reject) —
it is never auto-approved. Only one turn runs at a time; asking again while
one is already in flight reports that on the status line rather than
queuing a second question. Set the adapter command with [agent] adapter
in your global ~/.config/katamari/config.toml only — see
Configuration for why a repo-local
.katamari/config.toml can never choose it.
From inside the open panel, a sends a follow-up with no diff context
re-attached — the session already has it, so there’s nothing to select
first. C-g cancels the turn currently in flight, from anywhere,
including from the permission modal itself, where it answers the pending
request as a decline and then stops the whole turn — unlike n/Esc
there, which only decline that one tool call and leave the turn running
afterward.
Search
Section titled “Search”/ in the diff view (not the help window’s own filter above) opens a
search prompt on the status bar. Typing narrows incrementally: every match
across every file highlights live, and the cursor jumps to the first match
at or after where you started typing as the query narrows further. Enter
confirms — the matches and highlight stay, the prompt closes — and n/N
then jump to the next/previous match, wrapping around with a “search
wrapped” note. Esc while typing cancels, restoring the cursor and scroll
position from before you pressed /; Esc in the diff view afterward (not
the prompt) clears an already-confirmed search’s highlight, vim’s :noh. A
query matching nowhere shows no matches: <query> and returns the cursor to
where / was pressed.
Matching is literal substring (no regex), smartcase like vim’s own
'smartcase': an all-lowercase query matches either case, but typing even
one uppercase letter makes it case-sensitive. Match granularity is
per-occurrence — a row with three hits is three n stops — across every
file in the diff, in file → hunk → line order. Only visible content is
searched: a fold row’s hidden, unchanged context (git’s own omitted context
between hunks — see zo/zc in Quickstart) isn’t
matched until you unfold it, at which point a confirmed search’s matches
recompute automatically over the newly revealed rows.
Any binding can be overridden per action; see [keys] in
Configuration.
Pass --show-keys to diff/open/log/timeline (or set [ui] show_keys = true to leave it on by default) to show a small overlay chip
in the content area’s bottom-right corner with the most recent key(s)
pressed, in the same notation as the table above (gd, K, ]d) —
useful for recordings and pair-review demos so a viewer can see what’s
being pressed. A multi-key sequence builds up as you type it (g then
gd), repeated identical presses collapse (j ×3), the chip clears after
~1.5s of inactivity, and while typing into the comment-compose or
revision-input overlays it shows a generic [typing…] placeholder instead
of echoing your text.