Every default binding the TUI ships with, in one table per surface. Each remappable action is listed under the exact tui.yaml key that rebinds it, so this page doubles as the reference for a hand-written keymap. Source of truth: docs/user/tui-reference.md in the sonny repository.

Remappable actions

Every action below can be rebound in tui.yaml; these are the defaults that ship unconfigured.

Action (tui.yaml key)Default keysEffect
palette.openctrl+pOpen the command palette
help.openf1Open help
tab.newctrl+shift+tNew chat tab (opens the recipe picker); soft cap of 9 tabs; only on terminals with keyboard enhancements
tab.chord_leaderctrl+tTab-switch leader chord — press, then n/p/t/1-9
tab.chord_nextnAfter the tab-chord leader: next tab
tab.chord_prevpAfter the tab-chord leader: previous tab
tab.chord_newtAfter the tab-chord leader: new chat tab (recipe picker) — works on every terminal
session.managerctrl+sOpen the session manager
history.searchctrl+rFilter command history and load a selection into the composer without sending it
session.pickerctrl+bSession picker (local tabs + attached remotes)
search.quickctrl+;Quick-search sessions/history/mesh
session.jumpalt+sOne list of every destination — open tabs, recent sessions, sessions on mesh nodes — Enter goes straight there
session.resumealt+rChoose a session from the current directory to resume
mesh.attach_recentalt+mRe-attach the last remote mesh session, no dialog
search.filesctrl+/Find file
agent.pickerctrl+g, ctrl+shift+aSub-agent picker (also /sub-agents)
mesh.actionsctrl+nMesh actions (node picker)
tab.closectrl+wClose the current tab (quits if last) — deletes the previous word instead while the draft is non-empty
tab.nextctrl+tab, alt+nCycle to the next chat tab
tab.prevctrl+shift+tab, alt+pCycle to the previous chat tab
tab.move_leftalt+shift+leftMove the current tab left
tab.move_rightalt+shift+rightMove the current tab right
tab.lastalt+lJump to the last-active tab
remote.terminatectrl+kTerminate the attached remote session — kills to end of line instead when the draft has text after the caret
alert.dismissctrl+xDismiss the failure panel above the input
queue.clearctrl+qClear the prompt queue, after a y / n confirmation; a no-op with an empty queue
queue.focusctrl+eToggle queue focus — moves to end of line instead when the draft has text after the caret
clipboard.pastectrl+vPaste (an image becomes an @path mention; otherwise plain text)
clipboard.copy_replyctrl+yCopy the last assistant reply
sidebar.toggle_sectionsalt+cCollapse or expand every sidebar section (same as /sidebar toggle all)
chat.expandalt+zToggle full-width chat: hide the sidebar (same as /expand)
transcript.verbosectrl+oToggle verbose transcript (same as /verbose)
transcript.navctrl+upLeave the composer for transcript navigation mode; inside navigation it also steps to the previous message
transcript.findctrl+fFind in the session transcript — enters navigation mode if needed, or refocuses an open find bar
session.plan_modeshift+tabToggle the session’s requested mode between plan and execute (same as /plan); bound in the composer only. Rebinding it onto another action’s chord is refused
tab.jump_1 … tab.jump_9ctrl+1…ctrl+9, alt+1…alt+9Jump directly to tab N

Press ctrl+up to review a long reply without scrolling by hand: it leaves the composer and puts the transcript into navigation mode, selecting the most recent message and scrolling it fully into view. y/c then copy the whole message, or just its code blocks, in one keystroke. Navigation mode is per-tab and never saved; switching tabs or /clear exits it. Streaming output never moves your selection.

These actions act only while navigation mode has focus; outside it the same keys type into the composer.

Action (tui.yaml key)Default keysEffect
transcript.nav_prevup, k, shift+tabSelect the previous message
transcript.nav_nextdown, j, tabSelect the next message
transcript.nav_firstg, homeSelect the first message
transcript.nav_lastshift+g (G), endSelect the last message
transcript.nav_page_uppgupScroll up a page and select a visible message
transcript.nav_page_downpgdnScroll down a page and select a visible message
transcript.nav_half_page_upctrl+uScroll up half a page and select a visible message
transcript.nav_half_page_downctrl+dScroll down half a page and select a visible message
transcript.copyyCopy the selected message
transcript.copy_codecCopy the code blocks of the selected message
transcript.nav_find/Find in the transcript (opens the find bar)
transcript.find_nextnJump to the next find match
transcript.find_prevshift+n (N)Jump to the previous find match
transcript.nav_exitctrl+down, esc, q, iLeave navigation and return to the chat composer

Find bar

ctrl+f (or / once navigating) opens the find bar. It searches the rendered transcript incrementally, is smartcase, caps the query at 256 characters, and keeps the last query when closed.

Action (tui.yaml key)Default keysEffect
transcript.find_doneescBar focused → unfocus it; open and unfocused → close it
transcript.find_bar_nextenter, downJump to the next match — bar focused
transcript.find_bar_prevshift+enter, upJump to the previous match — bar focused

esc is a three-step ladder: unfocus the bar, then close it, then leave navigation. ctrl+down skips the ladder and always returns to the composer in one press.

Fixed keys

ctrl+c and esc outside suggestions have fixed behavior and cannot be rebound.

  • ctrl+c — clears the draft and cancels a local response on the first press; exits on a second consecutive press. Another key or paste resets the sequence. In transcript navigation the first press only returns to the composer and keeps the draft.
  • esc — context-dependent, checked in order: returns focus from the queue pane to the composer; else on a remote-attached tab opens “Detach from remote?”; else goes to an open dialog; else cancels an in-flight turn (and only that); else sends the queued head when nothing is in flight; else opens the quit confirmation. The composer placeholder names whichever one the next esc will do.

Input and navigation

Fixed, not remappable:

KeyEffect
enterSend the message; confirms the focused dialog option
shift+enterInsert a newline (multi-line draft)
pgup / pgdnScroll the transcript up/down by a page
ctrl+downScroll the transcript down by a line (ctrl+up enters navigation)
home / endLine start / end in the composer; with an empty draft, transcript top / bottom (end resumes auto-follow)
ctrl+home / ctrl+endStart / end of the whole draft
up / downRecall input history (or scroll the transcript; navigate dialog lists)
alt+up / alt+downReorder the selected queued prompt (while the queue has focus)

Composer editing keys

The composer is a readline-style editor: ctrl+a/ctrl+e line start/end, ctrl+u/ctrl+k delete before/after the caret, ctrl+w delete the previous word, alt+b/alt+f (or ctrl+left/ctrl+right) move one word. ctrl+f no longer moves the caret forward — it’s transcript.find now (SONNY-2560); use → for that instead.

Four keys are shared with an application action, and the composer wins only when the key would really edit or move within the draft:

KeyComposerFalls back to
home / endLine start / end (any draft)Transcript top / bottom, when the draft is empty
ctrl+eEnd of line, when there is text after the caretqueue.focus
ctrl+wDelete the previous word, whenever the draft is non-emptytab.close
ctrl+kKill to end of line, when there is text after the caretremote.terminate

So ctrl+w closes the tab only with an empty draft — clear the box first if that is what you want. Only these three actions yield to the composer.

Queue keys

Anything you send while a turn is running is queued. The strip above the input says so: ↻ ready · 2 queued, or ↻ paused · 2 queued · s to send next after a turn errored or was canceled with messages still queued.

From the composer (no queue focus needed):

KeyEffect
sSend the first queued prompt now — only with an empty draft, a non-empty queue, and no turn in flight
enter on an empty draftSame as s
escSends the first queued prompt when nothing is in flight (cancels the turn instead when one is)
ctrl+qClear the whole queue, after a y / n confirmation

Once queue.focus (ctrl+e) has entered queue-focus mode:

KeyEffect
up / downMove the selection
alt+up / alt+downReorder the selected entry
dRemove the selected queued prompt
sSend the selected prompt now (cancels an in-flight turn first)
enter / eLoad it back into the input for editing
escLeave queue focus

s, d and e also accept their shifted/CapsLock forms (S, D, E). The agent-output pane (full-screen sub-agent stream view) closes on esc or q.

Dialog keys

  • Permission prompt — a Allow · A AlwaysAllow · ! Approve all for session · e Edit params (esc cancels, ctrl+s submits) · d or esc Deny. Publication calls (push, release, merge request, package or image publish, a forge write) get their own prompt headed Permission required — PUBLISH TO A REMOTE, whose row is [a] Allow once [e] Edit [d] Deny Esc → Deny; A does nothing there.
  • Quit dialog — y yes, end this node’s sessions · b background sessions and quit · t terminate every session on this node and quit · n or esc cancel.
  • Session manager — up/down select · enter open/resume · n new session · k/d kill (active row) or delete (previous row) · K kill every active session · esc close.
  • Command history — type to filter · up/down select · enter load into the composer (not sent) · esc close. The initial filter is the draft already in the composer.
  • Recipe picker — alphabetical by id · type to filter · Tab marks the highlighted recipe as the default · enter confirm · esc cancel.
  • Model menu (/models) — up/down select · enter switch this session to the highlighted connection · Tab then enter set/clear the default · Ctrl+L lock the highlighted model (on a provider header, lock the provider) · Ctrl+U unlock without reconnecting · Ctrl+H show/hide unreachable connections · esc cancel.
  • Command palette (Ctrl+P) — alphabetical · type to filter, prefix matches rank above contains-matches · enter run · esc close.
  • Skills dialog — up/down select · space toggles the highlighted category · esc close.

Customizing

Bindings live in tui.yaml under the keys: section, keyed by the action names in the tables above. What tui.yaml does not configure is what the fixed keys above are: the ctrl+c/esc semantics and the composer editing keys are deliberate, not unimplemented. If you hand-edit tui.yaml, run sonny doctor — it refuses a keymap that binds an unknown action or collides two chords (the session.plan_mode chord in particular is refused onto another action’s key).