Skip to content

tui: Shift+Enter submits instead of inserting newline in macOS Terminal.app (worked in V1) #43286

Closed

Description

@araidz

Summary

Since V2, pressing Shift+Enter in the macOS built-in Terminal (Terminal.app) submits the prompt instead of inserting a newline — even with alt+return explicitly bound to input.newline. This worked in V1 with the same terminal and the same keypress. The V2 input stack (opentui) no longer resolves Terminal.app's ambiguous ESC+CR sequence.

Environment

  • opencode version: 0.0.0-beta-17519 (beta)
  • OS: Darwin 27.0.0 (macOS, arm64)
  • Terminal: Terminal.app (TERM_PROGRAM=Apple_Terminal, TERM=xterm-256color)
  • Shell: /bin/zsh
  • Install/channel: beta
  • Active plugins: opencode-claude-auth-v2, a custom TUI sidebar plugin (neither affects input handling)

Reproduction

  1. In macOS Terminal.app, run opencode2.
  2. Type some text in the prompt.
  3. Press Shift+Enter.
  4. The prompt submits instead of inserting a newline.

Raw bytes captured from Terminal.app: pressing Shift+Enter sends ESC + CR (\x1b\r) in raw mode (ESC + LF in cooked mode, i.e. cat -v shows ^[ then a line break). Terminal.app does not support the kitty keyboard protocol, so this is the only encoding it can produce.

Expected Behavior

Shift+Enter inserts a newline. This worked in V1 in the same Terminal.app with the same keypress.

Actual Behavior

Shift+Enter submits the prompt. Only ctrl+j inserts a newline.

Additional Context

Keybind combinations tried in ~/.config/opencode/cli.json (each tested after a full TUI restart):

{ "keybinds": { "input.newline": "shift+return,ctrl+return,alt+return,ctrl+j" } }
{ "keybinds": { "input.newline": "shift+return,ctrl+return,alt+return,alt+linefeed,ctrl+j" } }

Neither restores Shift+Enter in Terminal.app. opentui's keypress parser (packages/core/src/lib/parse.keypress.ts) decodes \x1b\r as { name: "return", meta: true } and \x1b\n as { name: "linefeed", meta: true }, so alt+return/alt+linefeed should match — but the event does not reach the keybind matcher as such (or is consumed by the submit path).

Notes:

Workaround

ctrl+j inserts a newline; Shift+Enter works in kitty-protocol terminals (e.g. Ghostty). Neither is the stock-terminal experience V1 provided.

Activity

  1. github-actions commented on Aug 18, 2026

    @github-actions
    Contributor

    This issue might be a duplicate of an existing issue. Please check:

    For keybind-related issues, please also check our pinned keybinds documentation: #4997

  2. vbobkov1968 commented on Oct 27, 2026

    @vbobkov1968

    Confirming this still reproduces on v2.2.0 (stable), macOS 26.6.2 (arm64), Terminal.app.

    I captured the raw bytes in Terminal.app (stty raw -echo) to verify the encoding claim above:

    • Shift+Enter → 0d (CR) — byte-for-byte identical to plain Enter
    • Option+Enter → 1b 0d (ESC CR)

    So Shift+Enter and Option+Enter are not the same sequence here. Option+Enter correctly triggers input.newline through the alt+return binding; Shift+Enter is indistinguishable from Enter at the byte level, so it never reaches the matcher as shift+return and submits the prompt instead.

    That points away from the ESC+CR parsing path and toward the missing fallback: when a bare Return arrives, inspect the physical modifier state (macOS CGEventSource.flagsState / NSEvent.modifierFlags) and treat it as Shift+Enter if Shift is held. Claude Code does exactly this on macOS and works in Terminal.app without setup, per its terminal docs. Ghostty works via the kitty keyboard protocol, which is the current workaround.

    Repro on v2.2.0: Terminal.app → opencode → type text → Shift+Enter submits; Option+Enter and ctrl+j insert a newline.

    Since this was closed as completed, please treat this as a regression report for 2.2.0 — happy to test a patch.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions