Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions ui-tui/src/components/textInput.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,14 @@ const PRINTABLE = /^[ -~\u00a0-\uffff]+$/
const BRACKET_PASTE = new RegExp(`${ESC}?\\[20[01]~`, 'g')
const FRAME_BATCH_MS = 16
const MULTI_CLICK_MS = 500

/**
* If the last printable input arrived within this many ms before Enter,
* the Enter is treated as IME composition confirmation (not submit).
* 30ms = well below human key-repeat (50ms+) but above IME commit +
* Enter arriving in the same event-loop tick (~0-5ms).
*/
const IME_GUARD_MS = 30
type MinimalEnv = Record<string, string | undefined>

const invert = (s: string) => INV + s + INV_OFF
Expand Down Expand Up @@ -465,6 +473,7 @@ export function TextInput({
const lineWidthRef = useRef(stringWidth(value.includes('\n') ? value.slice(value.lastIndexOf('\n') + 1) : value))
const mouseAnchorRef = useRef<null | number>(null)
const lastClickRef = useRef<{ at: number; offset: number }>({ at: 0, offset: -1 })
const lastInputAt = useRef(0)
const undo = useRef<{ cursor: number; value: string }[]>([])
const redo = useRef<{ cursor: number; value: string }[]>([])

Expand Down Expand Up @@ -968,6 +977,12 @@ export function TextInput({
if (k.return) {
flushKeyBurst()

const imeGuard = Date.now() - lastInputAt.current < IME_GUARD_MS

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.

This guard runs before the existing Shift/Ctrl/Meta Return branch. A printable input followed within 30ms by a modified Return will now be absorbed instead of inserting the configured newline; restrict this guard to unmodified plain Return.

if (imeGuard) {
lastInputAt.current = 0
return
}

const sequence = (event.keypress as { sequence?: string }).sequence
const preserveBareLineFeed = shouldPreserveCtrlJNewline() && sequence === '\n'

Expand Down Expand Up @@ -1112,6 +1127,10 @@ export function TextInput({
const bracketed = event.keypress.isPasted || inp.includes('[200~')
const text = inp.replace(BRACKET_PASTE, '').replace(/\r\n/g, '\n').replace(/\r/g, '\n')

if (!event.keypress.isPasted) {

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.

This timestamp is never reached when xterm delivers finalized IME text together with Return in one input event: the k.return branch returns first. The timeline-linked PR #39695 covers that event shape by preserving the printable Return payload before submission.

lastInputAt.current = Date.now()
}

if (bracketed && emitPaste({ bracketed: true, cursor: c, text, value: v })) {
return
}
Expand Down