Skip to content

fix(tui): preserve Ctrl+J newline across extended keys and tmux - #88957

Open
valda wants to merge 3 commits into
NousResearch:mainfrom
valda:fix/tui-encoded-ctrl-j-newline
Open

fix(tui): preserve Ctrl+J newline across extended keys and tmux#88957
valda wants to merge 3 commits into
NousResearch:mainfrom
valda:fix/tui-encoded-ctrl-j-newline

Conversation

@valda

@valda valda commented Aug 18, 2026

Copy link
Copy Markdown
Contributor

Summary

  • treat normalized Ctrl+J events from Kitty CSI-u / modifyOtherKeys as a composer newline
  • preserve bare LF as Ctrl+J under tmux when extended keys are disabled
  • keep plain Return (CR) as submit and leave extra-modifier chords untouched

Root cause

Extended-key protocols report Ctrl+J as input = "j" with ctrl = true, not as a Return/LF event. The composer had no binding for that normalized form, so it inserted a literal j.

With tmux extended-keys=off, Ctrl+J instead collapses to bare LF. shouldPreserveCtrlJNewline() did not recognize tmux, so that LF followed the submit path.

This is separate from #51546, which only adds VTE detection for bare LF, and from the classic CLI fixes in #32860 / #75473.

Behavior

  • Kitty CSI-u / modifyOtherKeys Ctrl+J: insert newline
  • tmux bare LF Ctrl+J: insert newline
  • plain Return (CR): submit
  • Ctrl+Shift+J and other extra-modifier chords: unchanged

Testing

  • npm test --workspace ui-tui -- --run src/__tests__/textInputReturnAction.test.ts src/__tests__/textInputPassThrough.test.ts (16 passed, including parsed CSI-u and modifyOtherKeys wire forms)
  • npm run typecheck --workspace ui-tui
  • npm run lint --workspace ui-tui (0 errors; 2 pre-existing warnings)
  • npx prettier --check ui-tui/src/components/textInput.tsx ui-tui/src/__tests__/textInputReturnAction.test.ts
  • npm run build --workspace ui-tui
  • git diff --check upstream/main...HEAD

Dogfood

Verified on Alacritty 0.16.1 both directly and under tmux 3.7b (extended-keys=off): Ctrl+J inserts a newline, Shift+Return inserts a newline, and normal Return still submits.

@alt-glitch alt-glitch added type/bug Something isn't working comp/tui Terminal UI (ui-tui/ + tui_gateway/) P2 Medium — degraded but workaround exists labels Aug 18, 2026
@Enough1122

Copy link
Copy Markdown
Contributor

AI code review — automated review for reference; please use your judgment.

Right fix for a real regression class: extended-key protocols report Ctrl+J as a modified printable ([106;5u, [27;5;106~) instead of the legacy LF byte, so enabling those protocols silently ate the documented Ctrl+J newline binding. The new isCtrlJNewline predicate is exactly right — strict ctrl-only with every other modifier excluded (tested), placed before the k.return branch so it can't submit, and driven through the real parser in tests rather than mocked shapes. Points:

  1. ui-tui/src/components/textInput.tsx:isCtrlJNewline (~246) — input.toLowerCase() === 'j' also matches when a non-extended path delivers name j with ctrl (legacy byte already handled elsewhere?). If the legacy LF handler runs earlier, fine; otherwise confirm no double-insert when both the byte path and this predicate fire for the same press. The test suite doesn't cover that interleaving. (nit)
  2. Adding TMUX to shouldPreserveCtrlJNewline first-in-line is correct (tmux collapses Ctrl+J to LF), and on macOS-under-tmux the outcome coincides with the existing darwin bare-LF rule — no behavioral fork introduced. (positive)
  3. Consider exporting ReturnDecisionKey alongside the predicate so tests and future callers stop re-declaring structural types. Very minor. (nit)

No blocking issues found.

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/) 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