Skip to content

5.6b Keybindings

Customize 60+ keybindings to build a workflow that feels natural.

📝 Course Notes

Key concepts from this lesson:

Keybindings Notes

What You'll Learn

  • Master the Leader key mechanism
  • Customize any keybinding
  • Disable unwanted keybindings
  • Resolve terminal keybinding conflicts

Leader Key

OpenCode uses a Leader key to avoid conflicts with terminal shortcuts.

Default Leader key: Ctrl+X

Usage: Press Leader key, release, then press the second key.

Ctrl+X → n    # New session
Ctrl+X → l    # Session list
Ctrl+X → m    # Model list

Keybinding Configuration

TUI keybindings use a flat keybinds map in a standalone tui.json or tui.jsonc file:

jsonc
{
  "$schema": "https://opencode.ai/tui.json",
  "keybinds": {
    // Change Leader key
    "leader": "ctrl+x",
    
    // Custom keybindings
    "session_new": "<leader>n",
    "model_list": "<leader>m",
    
    // Multiple keys for same function (comma-separated)
    "app_exit": "ctrl+c,ctrl+d,<leader>q",
    
    // Disable keybinding
    "session_compact": false
  }
}

The common config locations are listed below. Later configs override earlier ones:

  1. tui.json / tui.jsonc in the global config directory
  2. A custom file referenced by OPENCODE_TUI_CONFIG
  3. tui.json / tui.jsonc files discovered from the current directory up to the filesystem root, then applied from the rootmost directory back toward the current directory
  4. .opencode/tui.json / .opencode/tui.jsonc files encountered along the way
  5. TUI configuration in OPENCODE_CONFIG_DIR

Ordinary project files are applied from the root side toward the current directory, so the nearest one wins. Multiple .opencode directories are merged in the opposite direction—from the current side toward the root—so on conflicts the rootmost directory is loaded later and wins. OPENCODE_CONFIG_DIR is loaded last.

When upgrading from an older release, the TUI checks legacy main configs in the global location, along the project path, in config directories, and at the path specified by OPENCODE_CONFIG. It migrates their theme, keybinds, and legacy tui fields into a tui.json in the same directory. If the target tui.json already exists, that directory is skipped; tui.jsonc alone does not prevent migration. Only after the new file has been written successfully and a .tui-migration.bak backup has been created or reused are the legacy fields removed from the original config. The main config no longer reads these TUI fields.

Disable Keybindings

Set a binding to "none" or false to disable it:

jsonc
{
  "keybinds": {
    "session_compact": "none",
    "sidebar_toggle": false
  }
}

Multiple Key Bindings

Use commas to separate multiple keys:

jsonc
{
  "keybinds": {
    "input_newline": "shift+return,ctrl+return,alt+return,ctrl+j"
  }
}

Common Configurable Keybindings

Application Control

Key NameDefaultDescription
leaderctrl+xLeader key
app_exitctrl+c,ctrl+d,<leader>qExit application
diff_opennoneOpen the diff viewer (normally opened through /diff or the command palette)
terminal_suspendctrl+zSuspend terminal
terminal_title_togglenoneToggle terminal title

Interface Control

Key NameDefaultDescription
editor_open<leader>eOpen external editor
theme_list<leader>tTheme list
sidebar_toggle<leader>bToggle sidebar
scrollbar_togglenoneToggle scrollbar
status_view<leader>sStatus view
tool_detailsnoneToggle tool details
tips_toggle<leader>hToggle homepage tips

Session Management

Key NameDefaultDescription
session_new<leader>nNew session
session_list<leader>lSession list
session_export<leader>xExport session
session_timeline<leader>gSession timeline
session_interruptescapeInterrupt response
session_backgroundctrl+bMove a synchronously running subagent into the background
session_compact<leader>cCompact context
session_forknoneFork from message
session_renamectrl+rRename session
session_sharenoneShare session
session_unsharenoneUnshare session

Session Navigation

Key NameDefaultDescription
session_child_cyclerightCycle child sessions
session_child_cycle_reverseleftCycle child sessions in reverse
session_parentupReturn to the parent session

Message Operations

Key NameDefaultDescription
messages_copy<leader>yCopy message
messages_undo<leader>uUndo message
messages_redo<leader>rRedo message
messages_toggle_conceal<leader>hToggle code block folding

Here, messages_undo and messages_redo operate at the session level. Undo returns to the selected message and rolls back the associated file patches that came after it; redo restores the reverted state. If you set "snapshot": false in the main opencode.json or opencode.jsonc, messages can still be reverted, but file changes will not be undone or restored. These bindings are distinct from input_undo and input_redo inside the input field.

Diff Viewer

The diff viewer is enabled by default. Open it with /diff, the command palette, or a custom diff_open binding. It includes a file tree and can switch between workspace changes and changes from the latest AI turn. If the current branch is not the default branch, it also offers a comparison against the main branch. The viewer supports file and hunk navigation, single-file patches, unified and split views, and reviewed markers.

Key NameDefaultDescription
diff_closeescape,qClose the viewer and return to the previous screen
diff_toggleenter,spaceExpand a directory or select a file
diff_expand / diff_collapseright / leftExpand or collapse a file-tree item
diff_expand_allEExpand all directories
diff_switch_focustabMove focus between the file tree and patch pane
diff_next_hunk / diff_previous_hunk] / [Jump to the next or previous hunk
diff_next_file / diff_previous_filen / pJump to the next or previous file
diff_toggle_file_treebShow or hide the file tree
diff_single_patchsSwitch between a single patch and all patches
diff_switch_sourcedSwitch the diff source
diff_toggle_viewvSwitch between split and unified views
diff_help?Show the complete diff keybinding help

m is the diff viewer's built-in “mark as reviewed” key. It is not part of the configurable keybinds map.

Message Scrolling

Key NameDefaultDescription
messages_page_uppageup,ctrl+alt+bPage up
messages_page_downpagedown,ctrl+alt+fPage down
messages_half_page_upctrl+alt+uHalf page up
messages_half_page_downctrl+alt+dHalf page down
messages_firstctrl+g,homeJump to first
messages_lastctrl+alt+g,endJump to last
messages_nextnoneNext message
messages_previousnonePrevious message
messages_last_usernoneLast user message

Model & Agent

Key NameDefaultDescription
model_list<leader>mModel list
model_cycle_recentf2Cycle recent models
model_cycle_recent_reverseshift+f2Cycle recent (reverse)
model_cycle_favoritenoneCycle favorite models
model_cycle_favorite_reversenoneCycle favorites (reverse)
variant_cyclectrl+tCycle model variants
agent_list<leader>aAgent list
agent_cycletabCycle Agent
agent_cycle_reverseshift+tabCycle Agent (reverse)
command_listctrl+pCommand palette
prompt_skillsnoneOpen the Skill picker

Input Area Basics

Key NameDefaultDescription
input_submitreturnSend message
input_newlineshift+return,ctrl+return,alt+return,ctrl+jNew line
input_clearctrl+cClear input
input_pastectrl+vPaste
input_undoctrl+-,super+zUndo input
input_redoctrl+.,super+shift+zRedo input

Input Area Cursor Movement

Key NameDefaultDescription
input_move_leftleft,ctrl+bMove left one character
input_move_rightright,ctrl+fMove right one character
input_move_upupMove up one line
input_move_downdownMove down one line
input_word_forwardalt+f,alt+right,ctrl+rightMove forward one word
input_word_backwardalt+b,alt+left,ctrl+leftMove backward one word
input_line_homectrl+aMove to line start
input_line_endctrl+eMove to line end
input_visual_line_homealt+aMove to visual line start
input_visual_line_endalt+eMove to visual line end
input_buffer_homehomeMove to buffer start
input_buffer_endendMove to buffer end

Cursor Appearance

Cursor appearance is not a keybinding. Configure it alongside keybinds:

jsonc
{
  "$schema": "https://opencode.ai/tui.json",
  "cursor": {
    "style": "line",
    "blinking": true
  }
}

style can be block, underline, line, or default. The default value preserves your terminal settings, in which case blinking has no effect.

Input Area Selection

Key NameDefaultDescription
input_select_leftshift+leftSelect left
input_select_rightshift+rightSelect right
input_select_upshift+upSelect up
input_select_downshift+downSelect down
input_select_word_forwardalt+shift+f,alt+shift+rightSelect next word
input_select_word_backwardalt+shift+b,alt+shift+leftSelect previous word
input_select_line_homectrl+shift+aSelect to line start
input_select_line_endctrl+shift+eSelect to line end
input_select_visual_line_homealt+shift+aSelect to visual line start
input_select_visual_line_endalt+shift+eSelect to visual line end
input_select_buffer_homeshift+homeSelect to buffer start
input_select_buffer_endshift+endSelect to buffer end

Input Area Deletion

Key NameDefaultDescription
input_backspacebackspace,shift+backspaceBackspace
input_deletectrl+d,delete,shift+deleteDelete character
input_delete_linectrl+shift+dDelete entire line
input_delete_to_line_endctrl+kDelete to line end
input_delete_to_line_startctrl+uDelete to line start
input_delete_word_forwardalt+d,alt+delete,ctrl+deleteDelete next word
input_delete_word_backwardctrl+w,ctrl+backspace,alt+backspaceDelete previous word

History

Key NameDefaultDescription
history_previousupPrevious history
history_nextdownNext history

OpenCode Desktop Keybindings

OpenCode Desktop's input field supports Readline/Emacs style keybindings (built-in, not configurable):

KeybindingFunction
Ctrl+AMove to line start
Ctrl+EMove to line end
Ctrl+BMove back one character
Ctrl+FMove forward one character
Alt+BMove back one word
Alt+FMove forward one word
Ctrl+DDelete current character
Ctrl+KDelete to line end
Ctrl+UDelete to line start
Ctrl+WDelete previous word
Alt+DDelete next word
Ctrl+TTranspose characters
Ctrl+GCancel popup / Interrupt response

Terminal Compatibility

Shift+Enter Issue

Some terminals don't send the Shift+Enter modifier key by default.

Symptom: Pressing Shift+Enter sends the message instead of creating a new line.

Windows Terminal Configuration

Edit settings.json (path: %LOCALAPPDATA%\Packages\Microsoft.WindowsTerminal_8wekyb3d8bbwe\LocalState\settings.json):

Add to the actions array:

json
{
  "command": {
    "action": "sendInput",
    "input": "\u001b[13;2u"
  },
  "id": "User.sendInput.ShiftEnterCustom"
}

Add to the keybindings array:

json
{
  "keys": "shift+enter",
  "id": "User.sendInput.ShiftEnterCustom"
}

Save and restart Windows Terminal.

Other Terminals

  • iTerm2: Supported by default, no configuration needed
  • Alacritty: Supported by default
  • Kitty: Supported by default
  • GNOME Terminal: May need to update to a newer version

Common Configuration Scenarios

Vim Style

jsonc
{
  "keybinds": {
    "leader": "space",
    "messages_page_up": "ctrl+u",
    "messages_page_down": "ctrl+d",
    "messages_first": "gg",
    "messages_last": "G"
  }
}

Minimal Mode

Disable uncommon keybindings:

jsonc
{
  "keybinds": {
    "sidebar_toggle": "none",
    "scrollbar_toggle": "none",
    "session_fork": "none",
    "session_share": "none",
    "session_unshare": "none",
    "tips_toggle": "none"
  }
}

One-Handed Layout

Concentrate common operations on the left hand:

jsonc
{
  "keybinds": {
    "session_new": "ctrl+n",
    "session_list": "ctrl+l",
    "model_list": "ctrl+m",
    "agent_cycle": "ctrl+tab"
  }
}

Troubleshooting

SymptomCauseSolution
Keybinding not workingTerminal intercepted the keyCheck terminal settings or use a different key
Shift+Enter doesn't create new lineTerminal doesn't send modifier keyConfigure terminal (see above)
Configured but no responseUsed keybind (singular)Use keybinds (plural)
Using null to disable doesn't workSyntax errorUse "none" or false instead
Leader key conflictConflicts with other programsUse a different Leader key like ctrl+space
Ctrl+C doesn't clear inputIntercepted by terminal's SIGINTUse a different key or accept default behavior

Keybinding Mnemonic

Tab cycles Agent, Ctrl+C clears
Press Leader, then a letter for the action you need
n for new, l for list, m for model
u to undo, r to redo
Left and right arrows cycle through child sessions

Lesson Summary

You learned:

  1. Using the Leader key mechanism to avoid conflicts
  2. Customizing keybindings in keybinds
  3. Using "none" or false to disable unwanted keybindings
  4. Using commas to bind multiple keys
  5. Resolving terminal Shift+Enter compatibility issues