Skip to content

Commit 2353140

Browse files
committed
Fix keypad enter LF detection
1 parent b8f2bd9 commit 2353140

4 files changed

Lines changed: 75 additions & 9 deletions

File tree

cli/src/components/multiline-input.tsx

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,10 @@ import {
2121
isKeypadEnter,
2222
} from '../utils/keypad-keys'
2323
import { clamp } from '../utils/math'
24-
import { isLinefeedActingAsEnter, markReturnKeySeen } from '../utils/terminal-enter-detection'
24+
import {
25+
isLinefeedActingAsEnter,
26+
markReturnKeySeenForKey,
27+
} from '../utils/terminal-enter-detection'
2528
import { supportsTruecolor } from '../utils/theme-system'
2629
import { calculateNewCursorPosition } from '../utils/word-wrap-utils'
2730

@@ -554,9 +557,7 @@ export const MultilineInput = forwardRef<
554557
const isReturnOrEnter =
555558
key.name === 'return' || key.name === 'enter' || keypadEnter
556559

557-
if (isReturnOrEnter) {
558-
markReturnKeySeen()
559-
}
560+
markReturnKeySeenForKey(key)
560561

561562
const linefeedIsEnter = lowerKeyName === 'linefeed' && isLinefeedActingAsEnter()
562563
const isEnterKey = isReturnOrEnter || linefeedIsEnter

cli/src/hooks/use-chat-keyboard.ts

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ import {
1212
type ChatKeyboardState,
1313
type ChatKeyboardAction,
1414
} from '../utils/keyboard-actions'
15-
import { markReturnKeySeen } from '../utils/terminal-enter-detection'
15+
import { markReturnKeySeenForKey } from '../utils/terminal-enter-detection'
1616

1717
import type { KeyEvent } from '@opentui/core'
1818

@@ -305,9 +305,7 @@ export function useChatKeyboard({
305305
reportActivity()
306306
}
307307

308-
if (key.name === 'return' || key.name === 'enter') {
309-
markReturnKeySeen()
310-
}
308+
markReturnKeySeenForKey(key)
311309

312310
const action = resolveChatKeyboardAction(key, state)
313311
const handled = dispatchAction(action, handlers)
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
import { afterEach, beforeEach, describe, expect, test } from 'bun:test'
2+
3+
import {
4+
isLinefeedActingAsEnter,
5+
markReturnKeySeenForKey,
6+
resetReturnKeySeenForTests,
7+
shouldMarkReturnKeySeen,
8+
} from '../terminal-enter-detection'
9+
10+
describe('terminal enter detection', () => {
11+
beforeEach(() => {
12+
resetReturnKeySeenForTests(false)
13+
})
14+
15+
afterEach(() => {
16+
resetReturnKeySeenForTests()
17+
})
18+
19+
test('marks real carriage-return Enter as return seen', () => {
20+
expect(shouldMarkReturnKeySeen({ name: 'return', sequence: '\r' })).toBe(
21+
true,
22+
)
23+
24+
markReturnKeySeenForKey({ name: 'return', sequence: '\r' })
25+
26+
expect(isLinefeedActingAsEnter()).toBe(false)
27+
})
28+
29+
test('does not mark keypad Enter escape sequences as return seen', () => {
30+
expect(
31+
shouldMarkReturnKeySeen({ name: 'kpenter', sequence: '\x1b[57414u' }),
32+
).toBe(false)
33+
expect(shouldMarkReturnKeySeen({ name: '', sequence: '\x1bOM' })).toBe(
34+
false,
35+
)
36+
37+
markReturnKeySeenForKey({ name: 'kpenter', sequence: '\x1b[57414u' })
38+
markReturnKeySeenForKey({ name: '', sequence: '\x1bOM' })
39+
40+
expect(isLinefeedActingAsEnter()).toBe(true)
41+
})
42+
})

cli/src/utils/terminal-enter-detection.ts

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,38 @@
55
* ever seen a \r ("return") key event. On macOS, Enter always sends \r.
66
*/
77

8-
let hasSeenReturnKey = process.platform === 'darwin'
8+
type EnterDetectionKey = {
9+
name?: string
10+
sequence?: string
11+
}
12+
13+
const defaultHasSeenReturnKey = process.platform === 'darwin'
14+
15+
let hasSeenReturnKey = defaultHasSeenReturnKey
16+
17+
export function shouldMarkReturnKeySeen(key: EnterDetectionKey): boolean {
18+
return (
19+
(key.name === 'return' || key.name === 'enter') && key.sequence === '\r'
20+
)
21+
}
922

1023
export function markReturnKeySeen(): void {
1124
hasSeenReturnKey = true
1225
}
1326

27+
export function markReturnKeySeenForKey(key: EnterDetectionKey): void {
28+
if (shouldMarkReturnKeySeen(key)) {
29+
markReturnKeySeen()
30+
}
31+
}
32+
1433
/** True when a "linefeed" (\n) key event should be treated as Enter. */
1534
export function isLinefeedActingAsEnter(): boolean {
1635
return !hasSeenReturnKey
1736
}
37+
38+
export function resetReturnKeySeenForTests(
39+
hasSeenReturn: boolean = defaultHasSeenReturnKey,
40+
): void {
41+
hasSeenReturnKey = hasSeenReturn
42+
}

0 commit comments

Comments
 (0)