[FEATURE] Add user-configurable keyboard shortcut actions
Preflight Checklist
- [x] I have searched existing requests and this feature hasn't been requested yet
- [x] This is a single feature request (not multiple features)
Problem Statement
It is frequently the case that Claude's keyboard shortcuts collide with terminal or OS shortcuts, or an action that would be helpful for a user workflow to take (e.g. a custom slash command) is not bound to a shortcut at all. Claude Code does not currently expose a way to have user-configurable keyboard shortcuts.
Proposed Solution
Users should be able to bind keystrokes or keychords which correspond to some set of Claude-exposed hooks or custom slash commands.
This could be configurable, in, say, a settings.json manner similar to the way that VSCode and VSCodium do. As an example, imagine a schema and instantiation like:
"settings.user.keybindings" : [
{
"keybinding": "ctrl+C",
"action": ["/clear"]
}
{
"keybinding": "ctrl+M A",
"action": ["/compact", "/custom_one", "/custom_two"]
}
]
In this example, hitting Ctrl+C causes the TUI to receive the /clear command. Hitting Ctrl+M followed by A causes the TUI to receive the /compact command followed by /custom_one and then /custom_two.
Additional desired behaviors:
- Keybinding actions are in a stack, such that the first receiver of any keybinding wins. The stack for each keybinding is initialized with the Claude defaults at initialization time, and the user defaults are then added next. This means that whenever one of Claude's default keybindings conflicts with a user keybinding, the _user_ keybinding takes precedence over the Claude default.
- When keybindings are updated this mapping is reset without needing to restart Claude.
Alternative Solutions
Some other issues propose specific keyboard shortcuts, e.g. #9153. Most of the alternatives rely on using a specific upstream terminal or UI that encodes these macros for the benefit of the Claude TUI. These are generally unreliable because Claude Code doesn't expose a standard interface to receive these, and therefore is in the best position to implement keyboard shortcuts as a feature, rather than something upstream emulating keypresses.
Priority
High - Significant impact on productivity
Feature Category
CLI commands and flags
Use Case Example
Two examples:
- Imagine that you use terminal multiplexer like
tmuxorscreenwith a keyboard shortcut that conflicts with Claude. Without this feature, there will be no way to send Claude the keyboard shortcut because you can't override the settings. If there were a user-configurable capability for keyboard shortcuts, this wouldn't be a problem.
- You frequently send a sequence of custom slash commands. Rather than typing these out each time, it would be nicer if you could use a keyboard shortcut for this instead. Without this feature, there's no way to do this either.
20 Comments
Found 2 possible duplicate issues:
This issue will be automatically closed as a duplicate in 3 days.
🤖 Generated with Claude Code
Responding to potential duplicates:
I accidentally made a duplicate https://github.com/anthropics/claude-code/issues/9490 - already closed it but it's helpful in that I linked a bunch of disparate issues related to keyboard shortcut remapping to show the range of conflicts people are experiencing. I mentioned a new conflict not documented yet - ctrl+g for external text editor conflicts with Zellij's lock mode.
adding my own since I'm tired of consuming all my tokens by accidentally turning on thinking mode and not noticing it:
Description:
Currently, Claude Code uses Tab to toggle thinking mode on/off. This
keybinding is easy to trigger accidentally during normal typing workflows.
I'd like to request support for customizable keybindings in the settings
configuration.
Proposed Solution
Add a keybindings section to ~/.claude/settings.json (and project-level
.claude/settings.json):
{
"alwaysThinkingEnabled": false,
"keybindings": {
"toggleThinking": "Shift+Tab",
// Future extensibility for other shortcuts
"clearScreen": "Ctrl+L",
"cancelRequest": "Ctrl+C"
}
}
Use Case
The Tab key is frequently used during:
Accidentally toggling thinking mode disrupts the flow. A less collision-prone
keybinding (like Shift+Tab, Option+Tab, or Ctrl+T) would improve the UX.
Alternative Solutions
entirely
conflicts
toggle actions
Additional Context
customization
I'd also like to bind F5 to /start-server for quick server startup Shift+F5 stop server during development.
Just adding that Ctrl+B is the default Tmux mapping for entering command mode. Claude is using it to background processes and has worked around the conflict by requiring double tap.
This means if I accidentally press Ctrl+B twice when trying to send a command to Tmux (which happens often, such as opening a new pane while a Claude command is running) the currently running Claude command will be backgrounded.
The only way to avoid this is to remap the Tmux shortcut to something else and unlearn years muscle memory.
Use Case: Stream Deck / External Control Device Integration
I'd like to add an use case for this feature: integration with external control devices like Stream Deck, macro keyboards, or accessibility tools.
The mode-switching problem: With the current cyclic
Shift+Tabtoggle, you can't assign a button to jump directly to a specific mode. Pressing the same button multiple times cycles through all modes unpredictably.What would help: Direct keyboard shortcuts to jump to specific modes, e.g.:
Alt+Ctrl+Shift+P→ Plan ModeAlt+Ctrl+Shift+A→ Ask before editsAlt+Ctrl+Shift+E→ Edit automaticallyWith configurable shortcuts, users could map Stream Deck buttons reliably and build better automation around Claude Code.
This issue has been inactive for 30 days. If the issue is still occurring, please comment to let us know. Otherwise, this issue will be automatically closed in 30 days for housekeeping purposes.
The issue is still occurring.
+1 on this, my use case is that I want to have a shortcut for "/resume"
I came here after finding #16176 closed. I am in the habit of pressing Escape to get to Normal mode (vim, and even use vim bindings in my terminal)... I've accidentally interrupted claude a few times doing this and would appreciate a method to unbind Escape.
Adding my own since it looks like every other thread is getting closed:
another possible solution....
<h2 style="color: rgb(204, 204, 204); font-family: -apple-system, "system-ui", "Segoe UI", Roboto, sans-serif; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; white-space: normal; background-color: rgb(24, 24, 24); text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial;">Summary</h2><p style="white-space: pre-wrap; margin-top: 0.1em; margin-bottom: 0.2em; color: rgb(204, 204, 204); font-family: -apple-system, "system-ui", "Segoe UI", Roboto, sans-serif; font-size: 13px; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; background-color: rgb(24, 24, 24); text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial;">The VS Code extension doesn't expose a command to programmatically send a message to the chat input. This prevents users from creating keybindings that open Claude Code and submit a prompt in one step.</p><h2 style="color: rgb(204, 204, 204); font-family: -apple-system, "system-ui", "Segoe UI", Roboto, sans-serif; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; white-space: normal; background-color: rgb(24, 24, 24); text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial;">Use Case</h2><p style="white-space: pre-wrap; margin-top: 0.1em; margin-bottom: 0.2em; color: rgb(204, 204, 204); font-family: -apple-system, "system-ui", "Segoe UI", Roboto, sans-serif; font-size: 13px; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; background-color: rgb(24, 24, 24); text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial;">I want a keybinding that opens a new Claude Code tab and runs <code style="font-family: monospace; color: rgb(208, 208, 208); background-color: rgb(60, 60, 60); padding: 2px 4px; border-radius: 3px; font-size: 0.9em; word-break: break-word;">/prime</code> automatically:</p><div class="codeBlockWrapper_-a7MRw" style="position: relative; margin: 8px 0px; color: rgb(204, 204, 204); font-family: -apple-system, "system-ui", "Segoe UI", Roboto, sans-serif; font-size: 13px; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; white-space: normal; background-color: rgb(24, 24, 24); text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial;"><button class="copyButton_CEmTFw copyButton_-a7MRw" title="Copy code" aria-label="Copy code to clipboard" style="color: rgb(204, 204, 204); font-family: -apple-system, "system-ui", "Segoe UI", Roboto, sans-serif; font-size: 13px; background: none 0% 0% / auto repeat scroll padding-box border-box rgb(31, 31, 31); border-color: rgb(69, 69, 69); border-style: solid; border-width: 0.833333px; border-image: none 100% / 1 / 0 stretch; cursor: pointer; opacity: 0; display: flex; border-radius: 4px; justify-content: center; align-items: center; padding: 4px; transition: opacity 0.15s, background 0.15s; position: absolute; top: 4px; right: 4px;"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true" data-slot="icon" class="copyIcon_CEmTFw"><path fill-rule="evenodd" d="M15.988 3.012A2.25 2.25 0 0 1 18 5.25v6.5A2.25 2.25 0 0 1 15.75 14H13.5v-3.379a3 3 0 0 0-.879-2.121l-3.12-3.121a3 3 0 0 0-1.402-.791 2.252 2.252 0 0 1 1.913-1.576A2.25 2.25 0 0 1 12.25 1h1.5a2.25 2.25 0 0 1 2.238 2.012ZM11.5 3.25a.75.75 0 0 1 .75-.75h1.5a.75.75 0 0 1 .75.75v.25h-3v-.25Z" clip-rule="evenodd"></path><path d="M3.5 6A1.5 1.5 0 0 0 2 7.5v9A1.5 1.5 0 0 0 3.5 18h7a1.5 1.5 0 0 0 1.5-1.5v-5.879a1.5 1.5 0 0 0-.44-1.06L8.44 6.439A1.5 1.5 0 0 0 7.378 6H3.5Z"></path></svg></button><pre style="overflow-x: auto; white-space: pre; box-sizing: border-box; border-radius: 4px; max-width: 100%; margin: 0px; padding: 8px;"><code class="language-json" style="font-family: monospace; color: rgb(208, 208, 208); background-color: rgb(60, 60, 60); padding: 0px; border-radius: 3px; font-size: 0.9em; word-break: break-word;">{
"key": "ctrl+alt+c",
"command": "runCommands",
"args": {
"commands": [
"claude-vscode.editor.open",
{
"command": "claude-vscode.sendMessage",
"args": { "text": "/prime" }
}
]
}
}
</code></pre></div><p style="white-space: pre-wrap; margin-top: 0.1em; margin-bottom: 0.2em; color: rgb(204, 204, 204); font-family: -apple-system, "system-ui", "Segoe UI", Roboto, sans-serif; font-size: 13px; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; background-color: rgb(24, 24, 24); text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial;">This isn't possible today. The extension registers <code style="font-family: monospace; color: rgb(208, 208, 208); background-color: rgb(60, 60, 60); padding: 2px 4px; border-radius: 3px; font-size: 0.9em; word-break: break-word;">open</code>, <code style="font-family: monospace; color: rgb(208, 208, 208); background-color: rgb(60, 60, 60); padding: 2px 4px; border-radius: 3px; font-size: 0.9em; word-break: break-word;">focus</code>, <code style="font-family: monospace; color: rgb(208, 208, 208); background-color: rgb(60, 60, 60); padding: 2px 4px; border-radius: 3px; font-size: 0.9em; word-break: break-word;">newConversation</code>, etc. but nothing to insert or submit text.</p><h2 style="color: rgb(204, 204, 204); font-family: -apple-system, "system-ui", "Segoe UI", Roboto, sans-serif; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; white-space: normal; background-color: rgb(24, 24, 24); text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial;">Precedent</h2><p style="white-space: pre-wrap; margin-top: 0.1em; margin-bottom: 0.2em; color: rgb(204, 204, 204); font-family: -apple-system, "system-ui", "Segoe UI", Roboto, sans-serif; font-size: 13px; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; background-color: rgb(24, 24, 24); text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial;">GitHub Copilot Chat exposes <code style="font-family: monospace; color: rgb(208, 208, 208); background-color: rgb(60, 60, 60); padding: 2px 4px; border-radius: 3px; font-size: 0.9em; word-break: break-word;">workbench.action.chat.send</code> which accepts a message string — this is a well-established pattern in VS Code extensions with chat interfaces.</p><h2 style="color: rgb(204, 204, 204); font-family: -apple-system, "system-ui", "Segoe UI", Roboto, sans-serif; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; white-space: normal; background-color: rgb(24, 24, 24); text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial;">Proposed Commands</h2>
Command | Args | Behavior
-- | -- | --
claude-vscode.sendMessage | { "text": "string" } | Insert text and submit
claude-vscode.insertText (optional) | { "text": "string" } | Insert text without submitting
<p style="white-space: pre-wrap; margin-top: 0.1em; margin-bottom: 0.2em; color: rgb(204, 204, 204); font-family: -apple-system, "system-ui", "Segoe UI", Roboto, sans-serif; font-size: 13px; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; background-color: rgb(24, 24, 24); text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial;">The first alone would cover most use cases. The second would be nice for composing prompts with multiple keybindings.</p>
I would love to be able to remap the behaviour of return so that it enters a naive newline instead of submitting the chat, and then bind something else to "submit chat".
Another
ctrl-gconflict: Chrome's organization-managed Gemini shortcut interceptsctrl-gglobally when Chrome is open. Since the Chrome profile is managed by myorganization, I can't disable this shortcut. Being able to rebind plan mode's
editor shortcut would solve this so I could actually view plans easily from Claude.
Escape... is a killer (literally 😄). Please let us remap it.
Ideally escape would just not kill the current agent process (only clear the input).
+1 — calling out the specific
ctrl+ccase here since it's currently listed as explicitly non-rebindable in~/.claude/keybindings.json(along withctrl+d,ctrl+m).The conflict with the universal copy shortcut is painful on Windows in particular, where
ctrl+c= copy is muscle memory. I regularly lose sessions by reflex when trying to copy text from the chat.Two acceptable resolutions:
ctrl+cto be remapped/unbound viakeybindings.json(preferred — fits this issue's scope)ctrl+shift+c) and let users opt back in to the legacy bindingToday there's no escape hatch — even setting
"ctrl+c": nullproduces a/doctorerror.+1 on this. My specific use case: I want to bind
ctrl+alt+cto/copyso I can quickly copy the last response without typing the command each time. A generic action likechat:command:/copyorchat:runCommandthat accepts any slash command would cover this and many other workflows. Would love to see this land.Seconding this. I'm tired of accidentally cancelling a long running query by accidentally pressing escape. At least please let us rebind or remove Escape as a shortcut to cancel thinking.
I constantly use escape to navigate, but then press escape one time too many and either exit claude or interrupt workflows (that might have been running for a long time, using subagents possibly).
Using the same key for navigation and interruption is a problem!