@@ -245,30 +245,40 @@ export default function TerminalChatInput({
245245 }
246246
247247 if ( _key . upArrow ) {
248- // Only recall history when the caret was *already* on the very first
248+ let moveThroughHistory = true ;
249+
250+ // Only use history when the caret was *already* on the very first
249251 // row *before* this key-press.
250252 const cursorRow = editorRef . current ?. getRow ?.( ) ?? 0 ;
251253 const wasAtFirstRow = ( prevCursorRow . current ?? cursorRow ) === 0 ;
254+ if ( ! ( cursorRow === 0 && wasAtFirstRow ) ) {
255+ moveThroughHistory = false ;
256+ }
252257
253- if ( history . length > 0 && cursorRow === 0 && wasAtFirstRow ) {
254- if ( historyIndex == null ) {
255- const currentDraft = editorRef . current ?. getText ?.( ) ?? input ;
256- setDraftInput ( currentDraft ) ;
257- }
258+ // Only use history if we are already in history mode or if the input is empty.
259+ if ( historyIndex == null && input . trim ( ) !== "" ) {
260+ moveThroughHistory = false ;
261+ }
258262
263+ // Move through history.
264+ if ( history . length && moveThroughHistory ) {
259265 let newIndex : number ;
260266 if ( historyIndex == null ) {
267+ const currentDraft = editorRef . current ?. getText ?.( ) ?? input ;
268+ setDraftInput ( currentDraft ) ;
261269 newIndex = history . length - 1 ;
262270 } else {
263271 newIndex = Math . max ( 0 , historyIndex - 1 ) ;
264272 }
265273 setHistoryIndex ( newIndex ) ;
274+
266275 setInput ( history [ newIndex ] ?. command ?? "" ) ;
267276 // Re-mount the editor so it picks up the new initialText
268277 setEditorKey ( ( k ) => k + 1 ) ;
269- return ; // we handled the key
278+ return ; // handled
270279 }
271- // Otherwise let the event propagate so the editor moves the caret
280+
281+ // Otherwise let it propagate.
272282 }
273283
274284 if ( _key . downArrow ) {
@@ -339,73 +349,60 @@ export default function TerminalChatInput({
339349 const onSubmit = useCallback (
340350 async ( value : string ) => {
341351 const inputValue = value . trim ( ) ;
342- // If the user only entered a slash, do not send a chat message
352+
353+ // If the user only entered a slash, do not send a chat message.
343354 if ( inputValue === "/" ) {
344355 setInput ( "" ) ;
345356 return ;
346357 }
347- // Skip this submit if we just autocompleted a slash command
358+
359+ // Skip this submit if we just autocompleted a slash command.
348360 if ( skipNextSubmit ) {
349361 setSkipNextSubmit ( false ) ;
350362 return ;
351363 }
364+
352365 if ( ! inputValue ) {
353366 return ;
354- }
355-
356- if ( inputValue === "/history" ) {
367+ } else if ( inputValue === "/history" ) {
357368 setInput ( "" ) ;
358369 openOverlay ( ) ;
359370 return ;
360- }
361-
362- if ( inputValue === "/help" ) {
371+ } else if ( inputValue === "/help" ) {
363372 setInput ( "" ) ;
364373 openHelpOverlay ( ) ;
365374 return ;
366- }
367-
368- if ( inputValue === "/diff" ) {
375+ } else if ( inputValue === "/diff" ) {
369376 setInput ( "" ) ;
370377 openDiffOverlay ( ) ;
371378 return ;
372- }
373-
374- if ( inputValue === "/compact" ) {
379+ } else if ( inputValue === "/compact" ) {
375380 setInput ( "" ) ;
376381 onCompact ( ) ;
377382 return ;
378- }
379-
380- if ( inputValue . startsWith ( "/model" ) ) {
383+ } else if ( inputValue . startsWith ( "/model" ) ) {
381384 setInput ( "" ) ;
382385 openModelOverlay ( ) ;
383386 return ;
384- }
385-
386- if ( inputValue . startsWith ( "/approval" ) ) {
387+ } else if ( inputValue . startsWith ( "/approval" ) ) {
387388 setInput ( "" ) ;
388389 openApprovalOverlay ( ) ;
389390 return ;
390- }
391-
392- if ( inputValue === "q" || inputValue === ":q" || inputValue === "exit" ) {
391+ } else if ( inputValue === "exit" ) {
393392 setInput ( "" ) ;
394- // wait one 60ms frame
395393 setTimeout ( ( ) => {
396394 app . exit ( ) ;
397395 onExit ( ) ;
398396 process . exit ( 0 ) ;
399- } , 60 ) ;
397+ } , 60 ) ; // Wait one frame.
400398 return ;
401399 } else if ( inputValue === "/clear" || inputValue === "clear" ) {
402400 setInput ( "" ) ;
403401 setSessionId ( "" ) ;
404402 setLastResponseId ( "" ) ;
405- // Clear the terminal screen (including scrollback) before resetting context
406- clearTerminal ( ) ;
407403
408- // Emit a system notice in the chat; no raw console writes so Ink keeps control.
404+ // Clear the terminal screen (including scrollback) before resetting context.
405+ clearTerminal ( ) ;
409406
410407 // Emit a system message to confirm the clear action. We *append*
411408 // it so Ink's <Static> treats it as new output and actually renders it.
@@ -449,7 +446,7 @@ export default function TerminalChatInput({
449446 await clearCommandHistory ( ) ;
450447 setHistory ( [ ] ) ;
451448
452- // Emit a system message to confirm the history clear action
449+ // Emit a system message to confirm the history clear action.
453450 setItems ( ( prev ) => [
454451 ...prev ,
455452 {
@@ -466,7 +463,7 @@ export default function TerminalChatInput({
466463
467464 return ;
468465 } else if ( inputValue === "/bug" ) {
469- // Generate a GitHub bug report URL pre‑filled with session details
466+ // Generate a GitHub bug report URL pre‑filled with session details.
470467 setInput ( "" ) ;
471468
472469 try {
@@ -519,10 +516,10 @@ export default function TerminalChatInput({
519516
520517 return ;
521518 } else if ( inputValue . startsWith ( "/" ) ) {
522- // Handle invalid/unrecognized commands.
523- // Only single-word inputs starting with '/' (e.g., /command) that are not recognized are caught here.
524- // Any other input, including those starting with '/' but containing spaces
525- // (e.g., "/command arg"), will fall through and be treated as a regular prompt.
519+ // Handle invalid/unrecognized commands. Only single-word inputs starting with '/'
520+ // (e.g., /command) that are not recognized are caught here. Any other input, including
521+ // those starting with '/' but containing spaces (e.g., "/command arg"), will fall through
522+ // and be treated as a regular prompt.
526523 const trimmed = inputValue . trim ( ) ;
527524
528525 if ( / ^ \/ \S + $ / . test ( trimmed ) ) {
@@ -549,11 +546,13 @@ export default function TerminalChatInput({
549546 // detect image file paths for dynamic inclusion
550547 const images : Array < string > = [ ] ;
551548 let text = inputValue ;
549+
552550 // markdown-style image syntax: 
553551 text = text . replace ( / ! \[ [ ^ \] ] * ?\] \( ( [ ^ ) ] + ) \) / g, ( _m , p1 : string ) => {
554552 images . push ( p1 . startsWith ( "file://" ) ? fileURLToPath ( p1 ) : p1 ) ;
555553 return "" ;
556554 } ) ;
555+
557556 // quoted file paths ending with common image extensions (e.g. '/path/to/img.png')
558557 text = text . replace (
559558 / [ ' " ] ( [ ^ ' " ] + ?\. (?: p n g | j p e ? g | g i f | b m p | w e b p | s v g ) ) [ ' " ] / gi,
@@ -562,6 +561,7 @@ export default function TerminalChatInput({
562561 return "" ;
563562 } ,
564563 ) ;
564+
565565 // bare file paths ending with common image extensions
566566 text = text . replace (
567567 // eslint-disable-next-line no-useless-escape
@@ -578,10 +578,10 @@ export default function TerminalChatInput({
578578 const inputItem = await createInputItem ( text , images ) ;
579579 submitInput ( [ inputItem ] ) ;
580580
581- // Get config for history persistence
581+ // Get config for history persistence.
582582 const config = loadConfig ( ) ;
583583
584- // Add to history and update state
584+ // Add to history and update state.
585585 const updatedHistory = await addToHistory ( value , history , {
586586 maxSize : config . history ?. maxSize ?? 1000 ,
587587 saveHistory : config . history ?. saveHistory ?? true ,
@@ -723,8 +723,7 @@ export default function TerminalChatInput({
723723 />
724724 ) : (
725725 < Text dimColor >
726- send q or ctrl+c to exit | send "/clear" to reset | send "/help" for
727- commands | press enter to send | shift+enter for new line
726+ ctrl+c to exit | "/" to see commands | enter to send
728727 { contextLeftPercent > 25 && (
729728 < >
730729 { " — " }
0 commit comments