Windows: Shift+Enter does not insert newline — terminal protocol limitation requires Win32 input support
Summary
On Windows, shift+enter is documented and configurable as a keybinding (e.g. chat:newline), but it does not work — it submits the message identically to plain Enter.
Environment
- OS: Windows 11 Enterprise
- Terminal: Windows Terminal (wt.exe) with Command Prompt profile
- Shell: cmd.exe (
wt.exe cmd /k claude) - Claude Code keybindings.json:
{
"$schema": "https://www.schemastore.org/claude-code-keybindings.json",
"$docs": "https://code.claude.com/docs/en/keybindings",
"bindings": [
{
"context": "Chat",
"bindings": {
"shift+enter": "chat:newline"
}
}
]
}
Current Behavior
Pressing Shift+Enter submits the message, same as plain Enter. The keybinding is correctly configured but has no effect.
Expected Behavior
Pressing Shift+Enter should insert a newline (trigger chat:newline) rather than submit the message.
Root Cause
This is a terminal protocol limitation: in traditional VT terminals (including Windows Terminal's ConPTY layer), Shift+Enter and plain Enter both send the same byte (CR, 0x0D). The Shift key state is not transmitted, so Claude Code cannot distinguish them from the input stream.
Possible Solution
On Windows, the Win32 Console API (ReadConsoleInput) provides full key events including modifier state (SHIFT_PRESSED), which would allow distinguishing Shift+Enter from Enter. If Claude Code's Windows input handling were updated to use Win32 console input (or request an extended keyboard protocol such as the Kitty keyboard protocol from Windows Terminal), this distinction could be made reliably.
Impact
Users who want to use Shift+Enter as a newline shortcut on Windows cannot do so regardless of keybindings configuration.
3 Comments
I hit this today on Windows 11 + Windows Terminal 1.24.11911.0 + PowerShell, Claude Code v2.1.216 (npm install, Bun single-file
claude.exe), and did some digging that narrows the root cause further. Two findings that change the "possible solution" picture:1. Windows Terminal already supports the needed protocol — the app just never asks for it
Windows Terminal implements win32-input-mode (
DECSET 9001, spec). Once a client app emitsESC[?9001h, every keystroke arrives with full virtual-key + modifier state — noReadConsoleInputor kitty protocol needed.Verified with a raw-mode Node probe in the same WT tab:
So the terminal side is fully capable: Enter-with-Shift is unambiguously distinguishable the moment the mode is enabled.
2. claude.exe half-implements this mode: it disables it on teardown but never enables it
Strings analysis of the v2.1.216 binary:
"\x1B[?9001l"(disable) exists as a literal and is written on disconnect/teardown when the platform is Windows.Buffer.from("\x1B[?9001")exists, used in a stream filter that strips9001h/9001lsequences.ESC[?9001h(enable) exists anywhere in the binary, and no kitty protocol (CSI >u) ormodifyOtherKeyssequences either.So the input path never receives anything but a bare
0x0Dfor Shift+Enter, and nokeybindings.jsonconfiguration can help — the information is discarded before the app ever sees it. The disable-on-teardown suggests win32-input-mode support was started but the startup enable never landed (or was removed).Related:
/terminal-setupcurrently reports "Shift+Enter is natively supported in Windows Terminal. No configuration needed." — which is misleading while the enable is missing. Same claim in the terminal-config docs.Suggested fix
On Windows, when stdin is a ConPTY/VT terminal, emit
ESC[?9001hat raw-mode startup and parse theESC[Vk;Sc;Uc;Kd;Cs;Rc_events (the9001lteardown write is already there). WT ≥ 1.4 supports it; terminals that don't recognize the DECSET simply ignore it.Workaround for other users hitting this
Make Windows Terminal translate Shift+Enter to a linefeed (
\n= Ctrl+J), which is Claude Code's defaultchat:newlinebinding — works immediately, no Claude Code config needed. In WTsettings.json:Caveat: this applies to every app running in WT (e.g. it shadows PSReadLine's own Shift+Enter multiline binding), so it should be removed once Claude Code enables win32-input-mode itself.
Confirming this on Windows 11 (build 26200, version 25H2) + Windows Terminal 1.24.11911.0 + PowerShell 7.
/terminal-setup says Shift+Enter should work, but it still submits instead of adding a newline.
I tried @deganii's Windows Terminal workaround (remapping Shift+Enter to send a newline directly). It does fix Claude Code, but since that remap applies to the whole terminal, it also broke Shift+Enter in PowerShell: instead of inserting a new line at the cursor, PSReadLine inserted it above the current line. Had to undo the workaround because of that.
Using Ctrl+J or Ctrl+Enter for now, but would be great to see this fixed properly so Shift+Enter works without side effects elsewhere.
Still reproducing on v2.1.224 (winget build,
claude.exe), Windows 11 26200 + WindowsTerminal 1.24.11911.0 + PowerShell 7. Re-ran @deganii's strings analysis against this
newer build: unchanged.
ESC[?9001lis present,ESC[?9001his not, and there is nokitty push (
CSI > <flags> u) anywhere — I searched every spelling includingtemplate-constructed ones. So the missing enable is still missing four versions later.
Four things I can add that aren't in the thread yet.
1. The exact reason
/terminal-setupmisreportssupportsShiftEnter()checks the terminal against a table of terminals assumed to emitCSI-u natively. In v2.1.224 that table is:
It backs
getNativeCSIuTerminalDisplayNameand is consulted alongside a separate listof terminals that need setup (
["vscode", "cursor", "windsurf", "alacritty", "zed"]).Because
windows-terminalsits in the first table,/terminal-setupshort-circuits to"Shift+Enter is natively supported in Windows Terminal. No configuration needed.", the
newline hint in the UI renders as
shift+enterinstead ofctrl+j, and no workaround isever offered.
That entry is wrong for every shipping Windows Terminal today: WT emits CSI-u only under
an extended keyboard protocol, and the app never requests one. Removing it is a
one-line change that would at least stop the tool from reporting success while the key
does nothing — which is the part that makes this so hard to diagnose. (This is also the
whole of #80817, which looks like a duplicate of this issue.)
2. The CSI-u decode path is already complete — only the negotiation is missing
Worth stating explicitly, because it narrows the fix: the input parser already handles
kitty-style CSI-u correctly. Keycode
13maps toreturn, and the modifier parameter isdecoded as
(param - 1)bitmask →shift / meta / ctrl / super. It also parses a kittykeyboard response (
CSI ? <flags> u), even though nothing ever sends the query.You can confirm this end-to-end today by feeding it CSI-u by hand:
where
<ESC>is a JSON escape for U+001B: a backslash followed byu001b.This works. So everything downstream of the terminal handshake is done — the only gap is
that Claude Code never asks the terminal to send the disambiguated form.
(Note that both halves are required. Configuring only
keybindings.jsondoes nothing,which is a common way to conclude the keybinding system is broken when it isn't.)
3. The
sendInputworkarounds break more than PSReadLine — including Codex CLI@anton-ubi reported above that the
"\n"workaround breaks PSReadLine's Shift+Enter. Italso breaks Codex CLI, and that case is worth spelling out because it doubles as
evidence for the proposed fix:
Codex CLI is a Rust/crossterm TUI. On Windows, crossterm reads Win32 console input
records, so it receives
VK_RETURNwithSHIFT_PRESSEDdirectly and Shift+Enter workswith zero terminal configuration — same Windows Terminal, same PowerShell profile,
same session, default WT settings:
A WT-level
sendInputbinding replaces the real key event with injected VT bytes, so itregresses Codex to fix Claude Code. Anyone running both agents in one terminal has to
pick which one gets a working Shift+Enter.
Two implications:
It globally overrides Shift+Enter for every application in Windows Terminal, including
ones that already handle it correctly.
in production in a comparable tool on the same terminal.** It requires no terminal
configuration, no protocol negotiation, and works in conhost too, so it degrades better
than the
9001hroute.4. Windows Terminal Preview 1.25 probably does not fix this on its own
#80817 has a comment suggesting WT Preview 1.25 resolves it, citing the 1.25 release
notes' built-in kitty keyboard protocol support. I don't think that follows: the kitty
protocol is opt-in — the application must push flags with
CSI > <flags> u— andv2.1.224 emits no such sequence. Upgrading the terminal alone shouldn't change what
arrives on stdin.
It would be useful for someone who has actually verified it on WT Preview 1.25 to say
which Claude Code version they were on, since if it genuinely works, an enable is landing
somewhere I couldn't find it.
---
Happy to test a build with
ESC[?9001hor a kitty push on WT 1.24 if that's useful.