Skip to content

feat(tui): portable newline keys in the composer#31176

Open
OutThisLife wants to merge 1 commit into
mainfrom
bb/tui-ctrlj-newline
Open

feat(tui): portable newline keys in the composer#31176
OutThisLife wants to merge 1 commit into
mainfrom
bb/tui-ctrlj-newline

Conversation

@OutThisLife

@OutThisLife OutThisLife commented May 23, 2026

Copy link
Copy Markdown
Collaborator

Summary

Shift+Enter, Alt+Enter and Ctrl+Enter only reach the app when the terminal forwards them via the kitty keyboard or modifyOtherKeys protocols. On Windows + WSL2, plain Apple Terminal, and SSH-without-extended-keys, every Enter variant collapses onto a bare `\r` (or `\x1b\r` for Alt) and submits the message with no way to insert a newline.

This PR gives every terminal a working newline binding without any capability detection.

Changes

  1. parse-keypress — recognise the legacy single/two-byte Enter encodings:

    • `\r` → Enter
    • `\n` → Ctrl+Enter (Ctrl+J)
    • `\x1b\r` → Alt+Enter
    • `\x1b\n` → Alt+Ctrl+Enter

    Modern CSI-u sequences still parse via the existing kitty / modifyOtherKeys path; this only fills the legacy gap.

  2. Inline `\+Enter` — when the char left of the cursor is a literal backslash, the `\` is consumed and replaced with `\n` in-place. Mirrors Claude Code's `PromptInput` behaviour: discoverable, universal, in-editor (vs. the existing submit-time buffered fallback in `useSubmission.ts`, which is kept as a paste safety net).

    Extracted as a pure `resolveReturn(value, cursor, modifier)` helper so the contract is unit-tested independently of React.

  3. Hotkeys help documents the trio: `Shift+Enter / Alt+Enter / Ctrl+J` plus `\+Enter` inline.

Prior art

  • Claude Code: `\+Enter` inline backspace + insert `\n`; Shift+Enter / Meta+Enter; Apple Terminal native shift detection.
  • OpenAI Codex CLI: `Ctrl+J` / `Ctrl+M` / `Enter` / `Shift+Enter` / `Alt+Enter` all listed as `insert_newline` defaults.
  • Aider: `/multiline` modal toggle.

This PR takes the union of the discoverable variants without the modal mode.

Test plan

  • `cd ui-tui && npx vitest run packages/hermes-ink/src/ink/parse-keypress.test.ts src/tests/textInputReturn.test.ts`
  • `cd ui-tui && npx vitest run packages/hermes-ink/src/ink/ src/tests/textInput` (178 tests pass)
  • Manual: TUI composer on macOS iTerm2 (Shift+Enter via kitty), Apple Terminal (Ctrl+J), WSL2 Ubuntu under Windows Terminal (Alt+Enter via ESC+CR, Ctrl+J, `\+Enter`).

@github-actions

github-actions Bot commented May 23, 2026

Copy link
Copy Markdown
Contributor

🔎 Lint report: bb/tui-ctrlj-newline vs origin/main

ruff

Total: 0 on HEAD, 0 on base (➖ 0)

🆕 New issues: none

✅ Fixed issues: none

Unchanged: 0 pre-existing issues carried over.

ty (type checker)

Total: 9037 on HEAD, 9037 on base (➖ 0)

🆕 New issues: none

✅ Fixed issues: none

Unchanged: 4811 pre-existing issues carried over.

Diagnostics are surfaced as warnings — this check never fails the build.

@OutThisLife

Copy link
Copy Markdown
Collaborator Author

/copilot review

Shift+Enter, Alt+Enter and Ctrl+Enter only reach the app when the
terminal forwards them via the kitty keyboard or modifyOtherKeys
protocols. On Windows + WSL2, plain Apple Terminal, and SSH-without-
extended-keys, every Enter variant collapses onto a bare '\r' (or
'\x1b\r' for Alt) and submits the message with no way to insert a
newline. Three small changes give every terminal a working newline
binding without any capability detection:

1. parse-keypress now recognises the legacy single/two-byte Enter
   encodings:
     '\r'      -> return
     '\n'      -> Ctrl+Enter (Ctrl+J)
     '\x1b\r'  -> Alt+Enter
     '\x1b\n'  -> Alt+Ctrl+Enter
   Modern CSI-u sequences still parse via the kitty path; this only
   fills the legacy gap.

2. The composer's Enter handler picks up `\<Enter>` as an inline
   line continuation: when the char left of the cursor is a literal
   backslash, the '\\' is consumed and replaced with '\n'. Mirrors
   Claude Code's PromptInput behaviour — discoverable, universal,
   in-place (vs. the existing submit-time buffered fallback).
   Extracted as a pure `resolveReturn(value, cursor, modifier)`
   helper so the contract is unit-tested.

3. Hotkeys help documents the trio: Shift+Enter / Alt+Enter / Ctrl+J
   plus `\\+Enter` inline.

Prior art: Claude Code (`\\+Enter` inline backspace), OpenAI Codex
(Ctrl+J / Ctrl+M / Enter / Shift+Enter / Alt+Enter as alternates),
Aider (`/multiline` modal toggle). This PR takes the union of the
discoverable variants without the modal mode.
@OutThisLife OutThisLife force-pushed the bb/tui-ctrlj-newline branch from f3784a1 to 696a714 Compare May 23, 2026 23:27
@alt-glitch alt-glitch added type/bug Something isn't working P2 Medium — degraded but workaround exists comp/tui Terminal UI (ui-tui/ + tui_gateway/) javascript Pull requests that update javascript code labels May 23, 2026
@OutThisLife OutThisLife changed the title feat(tui): treat Ctrl+J as a portable newline insert feat(tui): portable newline keys in the composer May 23, 2026
@OutThisLife

Copy link
Copy Markdown
Collaborator Author

@copilot review please

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR improves multiline composition in the TUI composer across terminals that don’t forward extended modified-Enter key sequences, by adding portable “newline insert” keypaths and documenting them.

Changes:

  • Extend parse-keypress to recognize legacy Enter/LF/ESC+Enter encodings and map them onto return with appropriate ctrl/meta modifiers.
  • Add a pure resolveReturn(value, cursor, modifier) helper to implement universal newline insertion, including inline \ + Enter continuation (consumes the backslash and inserts \n).
  • Add unit tests for both the new return-family decoding and the resolveReturn contract, and update hotkeys help text.

Reviewed changes

Copilot reviewed 5 out of 5 changed files in this pull request and generated no comments.

Show a summary per file
File Description
ui-tui/src/content/hotkeys.ts Updates hotkeys help to document Ctrl+J and inline \+Enter behavior.
ui-tui/src/components/textInput.tsx Introduces resolveReturn and routes Enter handling through it for portable newline insertion.
ui-tui/src/tests/textInputReturn.test.ts Adds unit tests covering resolveReturn submit/newline/continuation behaviors.
ui-tui/packages/hermes-ink/src/ink/parse-keypress.ts Adds legacy Enter-family decoding for \r, \n, ESC+\r, ESC+\n with modifier flags.
ui-tui/packages/hermes-ink/src/ink/parse-keypress.test.ts Adds tests verifying the new return-family modifier decoding.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

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

Labels

comp/tui Terminal UI (ui-tui/ + tui_gateway/) javascript Pull requests that update javascript code P2 Medium — degraded but workaround exists type/bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants