Keybindings
Every chord goes through one registry. Panes declare the keys they want; the kernel resolves a press to an action and hands it back to the pane that claimed it. So this page is a description of what the shipped panes declare, not a fixed list — add a pane and it brings its own keys, remove one and its keys go with it.
F1 is authoritative. The help screen renders the registry itself, so it cannot drift from the running interface the way a written table can. If this page and F1 disagree, F1 is right.
What the kernel owns #
These are not a pane's to claim, because they have to work whatever is loaded — including when a plugin is broken.
| Key | Does |
|---|---|
| Ctrl+Q | Quit. Sessions keep running — tmux owns them, not thurbox |
| Ctrl+H / Ctrl+L | Move focus back / forward through the ring |
| Ctrl+C / Ctrl+V | Copy a selection / paste |
| F1 or Ctrl+G | Keybindings help — the registry, rendered |
| F6 or Ctrl+, | Settings. ] crosses to the Interface tab |
| F4 or Ctrl+Y | Theme picker (Ctrl+Y is DSUSP in some terminals, hence F4) |
| Ctrl+P | Command palette — every action the registry knows, filtered as you type; Enter runs one |
| F10 | Reload the interface from disk |
| F12 | Perf HUD (needs [features] perf_hud) |
The session list #
Declared by ui/plugins/10_sessions.lua. The single-letter keys apply while the list is
focused; the Ctrl forms work from anywhere, which is what lets you restart or fork a
session without leaving the terminal you are typing in.
| Key | Does |
|---|---|
| j / k · ↓ / ↑ | Next / previous session |
| Ctrl+J / Ctrl+K | The same, from anywhere |
| g | First session |
| Enter | Open it — focus its agent |
| Shift+J / Shift+K | Move it down / up. A parent drags its children; a whole group moves past a group edge |
| Shift+S | Sort by name within each repository group |
| Ctrl+D | Delete — reversible, with an undo window |
| Shift+D | Delete it and its worktree. Confirms first, itemising what would be lost |
| Ctrl+Z | Undo the last delete |
| r or Ctrl+R | Restart the agent, resuming the conversation where the agent supports it |
| Ctrl+F | Fork — a new session in the parent's worktree, carrying its conversation |
| Ctrl+S | Sync every worktree with its base branch |
| Ctrl+O | Open the session's directories in your editor |
| F9 | Hide the session list, for a full-width agent |
The agent pane #
ui/plugins/20_agent.lua. Everything else you type here goes to the agent — that is
the point of it.
| Key | Does |
|---|---|
| Ctrl+T or F8 | A shell in this session's directory, as a second tab beside the agent |
| PgUp / PgDn | Scroll the agent's history. Per session, so it does not follow you |
Creating a session #
Ctrl+N opens the flow (ui/plugins/70_new_session.lua) — one
float rather than a chain of modals. It walks host → repositories → base branch →
name → branch name → agent, skipping the host step when no hosts are configured.
| Key | Does |
|---|---|
| j / k · ↓ / ↑ | Next / previous choice |
| Space | Select a repository, or fold a folder. Multi-select — that is how one session spans several repositories |
| w | Give that repository its own worktree |
| / | Filter the list |
| Alt+P | Import a folder of repositories |
| d | Forget a remembered repository |
| Enter | Confirm the step |
| Esc | Back out. Nothing is created until the last step |
Search #
Ctrl+/ opens a strip above the status bar
(ui/plugins/65_search.lua). It matches session names, agents, branches and repositories
— and the text on each agent's screen, which is the half that finds a session
by the error in it. Matches highlight in the panes themselves; moving the selection previews
it, and Esc puts back what you were looking at.
| Key | Does |
|---|---|
| type | Filter. Names match as a subsequence; screen text as a substring |
| ↓ / ↑ | Next / previous result, previewed as you move |
| Enter | Go to it |
| Esc | Close, restoring what was on screen before |
Why not Ctrl+P/Ctrl+N in the strip? A pane-scoped claim never outranks a global one, so declaring them would take Ctrl+N away from new-session everywhere. One registry for every chord is what buys that guarantee, and this is what it costs.
Rebinding anything #
From F1: select an action and press the chord you want. It takes effect on the next
keystroke and persists to ui.json, beside the plugins you have turned off and the ones
you trust — all three are your decisions, which is why they share a file.
Two rules the registry enforces. A chord already claimed by an action whose scope overlaps is reassigned, and you are told which one lost it — a silent double-binding is worse than a reported move. And a chord freed by a pane you removed stays unbound rather than being inherited by whatever loads next.
Chords the terminal takes first #
thurbox shares the Ctrl+letter namespace with readline, so while an agent has focus some chords are handed to it instead: Ctrl+A and Ctrl+E for line start/end, Ctrl+W and Ctrl+U for delete-word and kill-line, Ctrl+R for reverse-search. Navigation is deliberately never deferred — Ctrl+H/J/K/L, Ctrl+Q and Ctrl+N are how you get out of a focused agent, so they must keep working there.
On macOS, Cmd chords are bindable where the terminal supports the kitty keyboard protocol (iTerm2 3.5+, kitty, WezTerm, Ghostty — not Terminal.app). Shortcuts the emulator itself consumes (Cmd+Q/W/T/K…) never reach thurbox, so only bind what your terminal leaves free. F-keys need Fn on Mac laptops unless you have enabled standard function keys.
Chords a pane you install brings with it #
A pane declares its own keys, so installing one can add chords the shipped set leaves free: thurbox-code-review claims Ctrl+X/F7 and thurbox-info-panel claims F2 — see the interface.