@@ -27,6 +27,7 @@ import ContextMenu from "./ContextMenu"
2727import { VolumeX , Pin , Check } from "lucide-react"
2828import { IconButton } from "./IconButton"
2929import { cn } from "@/lib/utils"
30+ import { usePromptHistory } from "./hooks/usePromptHistory"
3031
3132interface ChatTextAreaProps {
3233 inputValue : string
@@ -159,29 +160,24 @@ const ChatTextArea = forwardRef<HTMLTextAreaElement, ChatTextAreaProps>(
159160 const [ isEnhancingPrompt , setIsEnhancingPrompt ] = useState ( false )
160161 const [ isFocused , setIsFocused ] = useState ( false )
161162
162- // Prompt history navigation state
163- const [ historyIndex , setHistoryIndex ] = useState ( - 1 )
164- const [ tempInput , setTempInput ] = useState ( "" )
165- const [ promptHistory , setPromptHistory ] = useState < string [ ] > ( [ ] )
166- const [ inputValueWithCursor , setInputValueWithCursor ] = useState < CursorPositionState > ( { value : inputValue } )
167-
168- // Initialize prompt history from task history
169- useEffect ( ( ) => {
170- if ( taskHistory && taskHistory . length > 0 && cwd ) {
171- // Extract user prompts from task history for the current workspace only
172- const prompts = taskHistory
173- . filter ( ( item ) => {
174- // Filter by workspace and ensure task is not empty
175- return item . task && item . task . trim ( ) !== "" && ( ! item . workspace || item . workspace === cwd )
176- } )
177- . map ( ( item ) => item . task )
178- // taskHistory is already in chronological order (oldest first)
179- // We keep it as-is so that navigation works correctly:
180- // - Arrow up increases index to go back in history (older prompts)
181- // - Arrow down decreases index to go forward (newer prompts)
182- setPromptHistory ( prompts )
183- }
184- } , [ taskHistory , cwd ] )
163+ // Use custom hook for prompt history navigation
164+ const {
165+ historyIndex,
166+ setHistoryIndex,
167+ tempInput,
168+ setTempInput,
169+ promptHistory,
170+ inputValueWithCursor,
171+ setInputValueWithCursor,
172+ handleHistoryNavigation,
173+ resetHistoryNavigation,
174+ resetOnInputChange,
175+ } = usePromptHistory ( {
176+ taskHistory,
177+ cwd,
178+ inputValue,
179+ setInputValue,
180+ } )
185181
186182 // Fetch git commits when Git is selected or when typing a hash.
187183 useEffect ( ( ) => {
@@ -390,75 +386,17 @@ const ChatTextArea = forwardRef<HTMLTextAreaElement, ChatTextAreaProps>(
390386
391387 const isComposing = event . nativeEvent ?. isComposing ?? false
392388
393- // Handle prompt history navigation
394- if ( ! showContextMenu && promptHistory . length > 0 && ! isComposing ) {
395- const textarea = event . currentTarget
396- const { selectionStart, selectionEnd, value } = textarea
397- const lines = value . substring ( 0 , selectionStart ) . split ( "\n" )
398- const currentLineIndex = lines . length - 1
399- const totalLines = value . split ( "\n" ) . length
400- const isAtFirstLine = currentLineIndex === 0
401- const isAtLastLine = currentLineIndex === totalLines - 1
402- const hasSelection = selectionStart !== selectionEnd
403-
404- // Only navigate history if cursor is at first/last line and no text is selected
405- if ( ! hasSelection ) {
406- if ( event . key === "ArrowUp" && isAtFirstLine ) {
407- event . preventDefault ( )
408-
409- // Save current input if starting navigation
410- if ( historyIndex === - 1 && inputValue . trim ( ) !== "" ) {
411- setTempInput ( inputValue )
412- }
413-
414- // Navigate to previous prompt
415- const newIndex = historyIndex + 1
416- if ( newIndex < promptHistory . length ) {
417- setHistoryIndex ( newIndex )
418- const historicalPrompt = promptHistory [ newIndex ]
419- setInputValue ( historicalPrompt )
420- setInputValueWithCursor ( {
421- value : historicalPrompt ,
422- afterRender : "SET_CURSOR_FIRST_LINE" ,
423- } )
424- }
425- return
426- }
427-
428- if ( event . key === "ArrowDown" && isAtLastLine ) {
429- event . preventDefault ( )
430-
431- // Navigate to next prompt
432- if ( historyIndex > 0 ) {
433- const newIndex = historyIndex - 1
434- setHistoryIndex ( newIndex )
435- const historicalPrompt = promptHistory [ newIndex ]
436- setInputValue ( historicalPrompt )
437- setInputValueWithCursor ( {
438- value : historicalPrompt ,
439- afterRender : "SET_CURSOR_LAST_LINE" ,
440- } )
441- } else if ( historyIndex === 0 ) {
442- // Return to current input
443- setHistoryIndex ( - 1 )
444- setInputValue ( tempInput )
445- setInputValueWithCursor ( {
446- value : tempInput ,
447- afterRender : "SET_CURSOR_START" ,
448- } )
449- }
450- return
451- }
452- }
389+ // Handle prompt history navigation using custom hook
390+ if ( handleHistoryNavigation ( event , showContextMenu , isComposing ) ) {
391+ return
453392 }
454393
455394 if ( event . key === "Enter" && ! event . shiftKey && ! isComposing ) {
456395 event . preventDefault ( )
457396
458397 if ( ! sendingDisabled ) {
459398 // Reset history navigation state when sending
460- setHistoryIndex ( - 1 )
461- setTempInput ( "" )
399+ resetHistoryNavigation ( )
462400 onSend ( )
463401 }
464402 }
@@ -522,9 +460,8 @@ const ChatTextArea = forwardRef<HTMLTextAreaElement, ChatTextAreaProps>(
522460 queryItems ,
523461 customModes ,
524462 fileSearchResults ,
525- historyIndex ,
526- tempInput ,
527- promptHistory ,
463+ handleHistoryNavigation ,
464+ resetHistoryNavigation ,
528465 ] ,
529466 )
530467
@@ -565,10 +502,7 @@ const ChatTextArea = forwardRef<HTMLTextAreaElement, ChatTextAreaProps>(
565502 setInputValue ( newValue )
566503
567504 // Reset history navigation when user types
568- if ( historyIndex !== - 1 ) {
569- setHistoryIndex ( - 1 )
570- setTempInput ( "" )
571- }
505+ resetOnInputChange ( newValue )
572506
573507 const newCursorPosition = e . target . selectionStart
574508 setCursorPosition ( newCursorPosition )
@@ -624,7 +558,7 @@ const ChatTextArea = forwardRef<HTMLTextAreaElement, ChatTextAreaProps>(
624558 setFileSearchResults ( [ ] ) // Clear file search results.
625559 }
626560 } ,
627- [ setInputValue , setSearchRequestId , setFileSearchResults , setSearchLoading , historyIndex ] ,
561+ [ setInputValue , setSearchRequestId , setFileSearchResults , setSearchLoading , resetOnInputChange ] ,
628562 )
629563
630564 useEffect ( ( ) => {
0 commit comments