Skip to content

fix(terminal): Shift+Enter inserts a newline instead of submitting - #546

Merged
chaitanyagiri merged 1 commit into
HarnessMD:mainfrom
snehithareddy28:fix/terminal-shift-enter
Oct 2, 2026
Merged

chaitanyagiri merged 1 commit into
HarnessMD:mainfrom
snehithareddy28:fix/terminal-shift-enter

Conversation

@snehithareddy28

Copy link
Copy Markdown
Contributor

What & why

Closes #481, which is on the 0.5.2 incident log.

xterm.js encodes Shift+Enter exactly like Enter — a bare CR — so a TUI cannot tell the two apart. At a Claude Code prompt inside an agent's terminal pane, Shift+Enter therefore submits the line instead of opening a new one, and the only way to write a multi-line prompt by hand is to end each line with a backslash. Terminals that get this right send a distinct sequence; the one /terminal-setup installs for iTerm2 and VS Code is ESC CR. Nothing configures that for our pane, so the pane has to send it.

Shift+Enter now writes ESC CR. That is provider-neutral on purpose: it is the same sequence Alt+Enter has always produced, so any TUI honouring one honours the other. As the issue says, this is about how the terminal encodes a modifier, not about Claude Code.

The mapping is deliberately narrow, and it is the part worth reviewing:

  • plain Enter still submits — that is the whole point of a prompt;
  • Alt+Enter is untouched: it already produces this sequence natively, so intercepting it would double-encode;
  • Ctrl+Enter / Cmd+Enter stay with the TUI and with the app's own shortcuts rather than being quietly redefined;
  • keydown only: xterm's custom-key hook also sees keypress and keyup, and answering on more than one would insert the newline two or three times.

The check sits before the existing Ctrl/Cmd gate in the handler. That gate is why this was broken: Shift+Enter holds neither modifier, so it returned early into xterm's default. Writing to the pty is the same path the OSC colour replies immediately below already use, and is skipped once the pty has exited.

The mapping lives in its own structural module with no xterm and no window, so it is unit-testable on its own — the same shape askMeOrder.ts and queueDelivery.ts use.

Type of change

  • Bug fix
  • New feature
  • Refactor / cleanup
  • Docs
  • Build / CI

Evidence

Before

Test step log on main: the terminal-keys tests cannot even load — there is no key-encoding module, because Shift+Enter had no mapping at all and fell through to xterm's default CR

After

Test step log with the fix: Shift+Enter sends ESC CR, plain Enter still submits, Enter with another modifier is left to the TUI, only keydown answers, and Shift with any other key is not claimed


Notes for review:

  • Five tests in test/terminal-keys.test.cjs, one per rule above. They fail on main because the module does not exist there.
  • Only the encoding is new. Nothing else about the handler changes: the Ctrl/Cmd copy and paste branches below are untouched, and a key this does not claim returns true exactly as before.
  • Not attempted here: the kitty keyboard protocol / CSI u that the issue mentions as an alternative. That is a much larger change to how every modifier is encoded, and ESC CR is what /terminal-setup actually installs today, so it fixes the reported case without committing the pane to a protocol.
  • npm run typecheck and the full npm run test:focused suite (839/839 on this branch) pass locally.

Discord: asr2805

xterm.js encodes Shift+Enter exactly like Enter — a bare CR — so a TUI
cannot tell the two apart. At a Claude Code prompt inside an agent's
terminal pane, Shift+Enter therefore submits the line instead of opening a
new one, and writing a multi-line prompt by hand means typing a trailing
backslash. `/terminal-setup` installs the right mapping for iTerm2 and VS
Code, but there is no way to apply it to our pane, so the pane has to send
the sequence itself.

Shift+Enter now writes ESC CR, the sequence /terminal-setup configures.
This is provider-neutral: it is the same sequence Alt+Enter has always
produced, so any TUI that honours one honours the other.

The mapping is deliberately narrow, and lives in its own structural module
so it is unit-testable without xterm or a window. Plain Enter still
submits. Alt+Enter is untouched, since it already produces this sequence
natively and intercepting it would double-encode. Ctrl+Enter and Cmd+Enter
stay with the TUI and the app's own shortcuts. Only keydown answers —
xterm's custom-key hook also sees keypress and keyup, and answering twice
would insert two newlines.

The check sits BEFORE the existing Ctrl/Cmd gate in the handler, because
Shift+Enter holds neither modifier and was returning early into xterm's
default. Writing to the pty is the path the OSC colour replies alongside it
already use, and is skipped once the pty has exited.

Five tests in test/terminal-keys.test.cjs.

Closes HarnessMD#481
@chaitanyagiri
chaitanyagiri merged commit b9e20f7 into HarnessMD:main Oct 2, 2026
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

2 participants