File tree Expand file tree Collapse file tree 5 files changed +39
-6
lines changed
Expand file tree Collapse file tree 5 files changed +39
-6
lines changed Original file line number Diff line number Diff line change @@ -12,6 +12,7 @@ import { useAskUserBridge } from '../hooks/use-ask-user-bridge'
1212import { useEvent } from '../hooks/use-event'
1313import { useChatStore } from '../state/chat-store'
1414import { getInputModeConfig } from '../utils/input-modes'
15+ import { isLinefeedActingAsEnter } from '../utils/terminal-enter-detection'
1516import { BORDER_CHARS } from '../utils/ui-constants'
1617
1718import type { useTheme } from '../hooks/use-theme'
@@ -131,7 +132,8 @@ export const ChatInputBar = ({
131132 option ?: boolean
132133 } ) => {
133134 const isPlainEnter =
134- ( key . name === 'return' || key . name === 'enter' ) &&
135+ ( key . name === 'return' || key . name === 'enter' ||
136+ ( key . name === 'linefeed' && isLinefeedActingAsEnter ( ) ) ) &&
135137 ! key . shift &&
136138 ! key . ctrl &&
137139 ! key . meta &&
Original file line number Diff line number Diff line change @@ -13,6 +13,7 @@ import { InputCursor } from './input-cursor'
1313import { useTheme } from '../hooks/use-theme'
1414import { useChatStore } from '../state/chat-store'
1515import { clamp } from '../utils/math'
16+ import { isLinefeedActingAsEnter , markReturnKeySeen } from '../utils/terminal-enter-detection'
1617import { supportsTruecolor } from '../utils/theme-system'
1718import { calculateNewCursorPosition } from '../utils/word-wrap-utils'
1819
@@ -523,11 +524,17 @@ export const MultilineInput = forwardRef<
523524 const handleEnterKeys = useCallback (
524525 ( key : KeyEvent ) : boolean => {
525526 const lowerKeyName = ( key . name ?? '' ) . toLowerCase ( )
526- const isEnterKey = key . name === 'return' || key . name === 'enter'
527- // Ctrl+J is translated by the terminal to a linefeed character (0x0a)
528- // So we detect it by checking for name === 'linefeed' rather than ctrl + j
527+ const isReturnOrEnter = key . name === 'return' || key . name === 'enter'
528+
529+ if ( isReturnOrEnter ) {
530+ markReturnKeySeen ( )
531+ }
532+
533+ const linefeedIsEnter = lowerKeyName === 'linefeed' && isLinefeedActingAsEnter ( )
534+ const isEnterKey = isReturnOrEnter || linefeedIsEnter
535+
529536 const isCtrlJ =
530- lowerKeyName === 'linefeed' ||
537+ ( lowerKeyName === 'linefeed' && ! linefeedIsEnter ) ||
531538 ( key . ctrl &&
532539 ! key . meta &&
533540 ! key . option &&
Original file line number Diff line number Diff line change @@ -12,6 +12,7 @@ import {
1212 type ChatKeyboardState ,
1313 type ChatKeyboardAction ,
1414} from '../utils/keyboard-actions'
15+ import { markReturnKeySeen } from '../utils/terminal-enter-detection'
1516
1617import type { KeyEvent } from '@opentui/core'
1718
@@ -304,6 +305,10 @@ export function useChatKeyboard({
304305 reportActivity ( )
305306 }
306307
308+ if ( key . name === 'return' || key . name === 'enter' ) {
309+ markReturnKeySeen ( )
310+ }
311+
307312 const action = resolveChatKeyboardAction ( key , state )
308313 const handled = dispatchAction ( action , handlers )
309314
Original file line number Diff line number Diff line change 11import { getInputModeConfig , type InputMode } from './input-modes'
2+ import { isLinefeedActingAsEnter } from './terminal-enter-detection'
23import type { KeyEvent } from '@opentui/core'
34
45
@@ -131,7 +132,8 @@ export function resolveChatKeyboardAction(
131132 const isShiftTab =
132133 key . name === 'tab' && key . shift && ! key . ctrl && ! key . meta && ! key . option
133134 const isEnter =
134- ( key . name === 'return' || key . name === 'enter' ) &&
135+ ( key . name === 'return' || key . name === 'enter' ||
136+ ( key . name === 'linefeed' && isLinefeedActingAsEnter ( ) ) ) &&
135137 ! key . shift &&
136138 ! hasModifier ( key )
137139 const isPageUp = key . name === 'pageup' && ! hasModifier ( key )
Original file line number Diff line number Diff line change 1+ /**
2+ * Most terminals send \r for Enter and \n for Ctrl+J. A few niche Linux
3+ * terminal emulators send \n for Enter instead, making the two
4+ * indistinguishable. We detect this at runtime by tracking whether we've
5+ * ever seen a \r ("return") key event. On macOS, Enter always sends \r.
6+ */
7+
8+ let hasSeenReturnKey = process . platform === 'darwin'
9+
10+ export function markReturnKeySeen ( ) : void {
11+ hasSeenReturnKey = true
12+ }
13+
14+ /** True when a "linefeed" (\n) key event should be treated as Enter. */
15+ export function isLinefeedActingAsEnter ( ) : boolean {
16+ return ! hasSeenReturnKey
17+ }
You can’t perform that action at this time.
0 commit comments