@@ -17,6 +17,7 @@ import {
1717 normalizePastedText ,
1818} from "../../../utils/composerText" ;
1919import { useComposerAutocompleteState } from "../hooks/useComposerAutocompleteState" ;
20+ import { usePromptHistory } from "../hooks/usePromptHistory" ;
2021import { ComposerInput } from "./ComposerInput" ;
2122import { ComposerMetaBar } from "./ComposerMetaBar" ;
2223import { ComposerQueue } from "./ComposerQueue" ;
@@ -51,6 +52,7 @@ type ComposerProps = {
5152 sendLabel ?: string ;
5253 draftText ?: string ;
5354 onDraftChange ?: ( text : string ) => void ;
55+ historyKey ?: string | null ;
5456 attachedImages ?: string [ ] ;
5557 onPickImages ?: ( ) => void ;
5658 onAttachImages ?: ( paths : string [ ] ) => void ;
@@ -117,6 +119,7 @@ export function Composer({
117119 sendLabel = "Send" ,
118120 draftText = "" ,
119121 onDraftChange,
122+ historyKey = null ,
120123 attachedImages = [ ] ,
121124 onPickImages,
122125 onAttachImages,
@@ -170,6 +173,51 @@ export function Composer({
170173 [ onDraftChange ] ,
171174 ) ;
172175
176+ const {
177+ isAutocompleteOpen,
178+ autocompleteMatches,
179+ highlightIndex,
180+ setHighlightIndex,
181+ applyAutocomplete,
182+ handleInputKeyDown,
183+ handleTextChange,
184+ handleSelectionChange,
185+ } = useComposerAutocompleteState ( {
186+ text,
187+ selectionStart,
188+ disabled,
189+ skills,
190+ prompts,
191+ files,
192+ textareaRef,
193+ setText : setComposerText ,
194+ setSelectionStart,
195+ } ) ;
196+
197+ const {
198+ handleHistoryKeyDown,
199+ handleHistoryTextChange,
200+ recordHistory,
201+ resetHistoryNavigation,
202+ } = usePromptHistory ( {
203+ historyKey,
204+ text,
205+ hasAttachments : attachedImages . length > 0 ,
206+ disabled,
207+ isAutocompleteOpen,
208+ textareaRef,
209+ setText : setComposerText ,
210+ setSelectionStart,
211+ } ) ;
212+
213+ const handleTextChangeWithHistory = useCallback (
214+ ( next : string , cursor : number | null ) => {
215+ handleHistoryTextChange ( next ) ;
216+ handleTextChange ( next , cursor ) ;
217+ } ,
218+ [ handleHistoryTextChange , handleTextChange ] ,
219+ ) ;
220+
173221 const handleSend = useCallback ( ( ) => {
174222 if ( disabled ) {
175223 return ;
@@ -178,9 +226,21 @@ export function Composer({
178226 if ( ! trimmed && attachedImages . length === 0 ) {
179227 return ;
180228 }
229+ if ( trimmed ) {
230+ recordHistory ( trimmed ) ;
231+ }
181232 onSend ( trimmed , attachedImages ) ;
233+ resetHistoryNavigation ( ) ;
182234 setComposerText ( "" ) ;
183- } , [ attachedImages , disabled , onSend , setComposerText , text ] ) ;
235+ } , [
236+ attachedImages ,
237+ disabled ,
238+ onSend ,
239+ recordHistory ,
240+ resetHistoryNavigation ,
241+ setComposerText ,
242+ text ,
243+ ] ) ;
184244
185245 const handleQueue = useCallback ( ( ) => {
186246 if ( disabled ) {
@@ -190,46 +250,39 @@ export function Composer({
190250 if ( ! trimmed && attachedImages . length === 0 ) {
191251 return ;
192252 }
253+ if ( trimmed ) {
254+ recordHistory ( trimmed ) ;
255+ }
193256 onQueue ( trimmed , attachedImages ) ;
257+ resetHistoryNavigation ( ) ;
194258 setComposerText ( "" ) ;
195- } , [ attachedImages , disabled , onQueue , setComposerText , text ] ) ;
196-
197- const {
198- isAutocompleteOpen,
199- autocompleteMatches,
200- highlightIndex,
201- setHighlightIndex,
202- applyAutocomplete,
203- handleInputKeyDown,
204- handleTextChange,
205- handleSelectionChange,
206- } = useComposerAutocompleteState ( {
207- text,
208- selectionStart,
259+ } , [
260+ attachedImages ,
209261 disabled ,
210- skills,
211- prompts,
212- files,
213- textareaRef,
214- setText : setComposerText ,
215- setSelectionStart,
216- } ) ;
262+ onQueue ,
263+ recordHistory ,
264+ resetHistoryNavigation ,
265+ setComposerText ,
266+ text ,
267+ ] ) ;
217268
218269 useEffect ( ( ) => {
219270 if ( ! prefillDraft ) {
220271 return ;
221272 }
222273 setComposerText ( prefillDraft . text ) ;
274+ resetHistoryNavigation ( ) ;
223275 onPrefillHandled ?.( prefillDraft . id ) ;
224- } , [ prefillDraft , onPrefillHandled , setComposerText ] ) ;
276+ } , [ onPrefillHandled , prefillDraft , resetHistoryNavigation , setComposerText ] ) ;
225277
226278 useEffect ( ( ) => {
227279 if ( ! insertText ) {
228280 return ;
229281 }
230282 setComposerText ( insertText . text ) ;
283+ resetHistoryNavigation ( ) ;
231284 onInsertHandled ?.( insertText . id ) ;
232- } , [ insertText , onInsertHandled , setComposerText ] ) ;
285+ } , [ insertText , onInsertHandled , resetHistoryNavigation , setComposerText ] ) ;
233286
234287 useEffect ( ( ) => {
235288 if ( ! dictationTranscript ) {
@@ -250,6 +303,7 @@ export function Composer({
250303 end ,
251304 ) ;
252305 setComposerText ( nextText ) ;
306+ resetHistoryNavigation ( ) ;
253307 requestAnimationFrame ( ( ) => {
254308 if ( ! textareaRef . current ) {
255309 return ;
@@ -263,6 +317,7 @@ export function Composer({
263317 dictationTranscript ,
264318 handleSelectionChange ,
265319 onDictationTranscriptHandled ,
320+ resetHistoryNavigation ,
266321 selectionStart ,
267322 setComposerText ,
268323 text ,
@@ -412,7 +467,7 @@ export function Composer({
412467 onAddAttachment = { onPickImages }
413468 onAttachImages = { onAttachImages }
414469 onRemoveAttachment = { onRemoveImage }
415- onTextChange = { handleTextChange }
470+ onTextChange = { handleTextChangeWithHistory }
416471 onSelectionChange = { handleSelectionChange }
417472 onTextPaste = { handleTextPaste }
418473 isExpanded = { editorExpanded }
@@ -421,6 +476,10 @@ export function Composer({
421476 if ( isComposingEvent ( event ) ) {
422477 return ;
423478 }
479+ handleHistoryKeyDown ( event ) ;
480+ if ( event . defaultPrevented ) {
481+ return ;
482+ }
424483 if (
425484 expandFenceOnSpace &&
426485 event . key === " " &&
0 commit comments