@@ -3,6 +3,8 @@ import { useCallback, useState, useEffect, useMemo, useRef } from 'react'
33
44import { TextAttributes , type ScrollBoxRenderable } from '@opentui/core'
55
6+ import { logger } from '../utils/logger'
7+
68const mixColors = ( foreground : string , background : string , alpha = 0.4 ) : string => {
79 const parseHex = ( hex : string ) => {
810 const normalized = hex . trim ( ) . replace ( '#' , '' )
@@ -197,15 +199,72 @@ export function MultilineInput({
197199 key . sequence [ 1 ] !== '[' ) ,
198200 )
199201
200- // Enter (without shift) submits
201- if ( key . name === 'return' && ! key . shift ) {
202- if ( 'preventDefault' in key ) ( key as any ) . preventDefault ( )
203- onSubmit ( )
204- return
202+ const isEnterKey = key . name === 'return' || key . name === 'enter'
203+ const hasEscapePrefix =
204+ typeof key . sequence === 'string' &&
205+ key . sequence . length > 0 &&
206+ key . sequence . charCodeAt ( 0 ) === 0x1b
207+ const isPlainEnter =
208+ isEnterKey &&
209+ ! key . shift &&
210+ ! key . ctrl &&
211+ ! key . meta &&
212+ ! key . alt &&
213+ ! key . option &&
214+ ! isAltLikeModifier &&
215+ ! hasEscapePrefix &&
216+ key . sequence === '\r'
217+ const isShiftEnter =
218+ isEnterKey &&
219+ ( Boolean ( key . shift ) || key . sequence === '\n' )
220+ const isOptionEnter = isEnterKey && ( isAltLikeModifier || hasEscapePrefix )
221+ const isCtrlJ =
222+ key . ctrl &&
223+ ! key . meta &&
224+ ! key . option &&
225+ ! key . alt &&
226+ ( lowerKeyName === 'j' || isEnterKey )
227+
228+ if ( isEnterKey || lowerKeyName === 'j' ) {
229+ const snapshot : Record < string , unknown > = {
230+ name : key . name ,
231+ sequence : key . sequence ,
232+ raw : ( key as any ) . raw ,
233+ ctrl : Boolean ( key . ctrl ) ,
234+ meta : Boolean ( key . meta ) ,
235+ alt : Boolean ( key . alt ) ,
236+ option : Boolean ( key . option ) ,
237+ shift : Boolean ( key . shift ) ,
238+ isEnterKey,
239+ hasEscapePrefix,
240+ code : ( key as any ) . code ,
241+ charCode : key . sequence ? key . sequence . charCodeAt ( 0 ) : null ,
242+ }
243+ try {
244+ const ownProps = Object . getOwnPropertyNames ( key )
245+ for ( const prop of ownProps ) {
246+ if ( prop in snapshot ) continue
247+ const value = ( key as any ) [ prop ]
248+ if ( typeof value === 'function' ) continue
249+ snapshot [ prop ] = value
250+ }
251+ for ( const prop in key ) {
252+ if ( prop in snapshot ) continue
253+ const value = ( key as any ) [ prop ]
254+ if ( typeof value === 'function' ) continue
255+ snapshot [ prop ] = value
256+ }
257+ } catch {
258+ // ignore property introspection errors
259+ }
260+ logger . info ( '[input-debug] keypress' , {
261+ ...snapshot ,
262+ } )
205263 }
206264
207- // Shift+Enter creates newline
208- if ( key . name === 'return' && key . shift ) {
265+ const shouldInsertNewline = isShiftEnter || isOptionEnter || isCtrlJ
266+
267+ if ( shouldInsertNewline ) {
209268 if ( 'preventDefault' in key ) ( key as any ) . preventDefault ( )
210269 const newValue =
211270 value . slice ( 0 , cursorPosition ) + '\n' + value . slice ( cursorPosition )
@@ -214,6 +273,12 @@ export function MultilineInput({
214273 return
215274 }
216275
276+ if ( isPlainEnter ) {
277+ if ( 'preventDefault' in key ) ( key as any ) . preventDefault ( )
278+ onSubmit ( )
279+ return
280+ }
281+
217282 // Calculate boundaries for shortcuts
218283 const lineStart = findLineStart ( value , cursorPosition )
219284 const lineEnd = findLineEnd ( value , cursorPosition )
0 commit comments