@@ -84,11 +84,15 @@ export function useChat(runCode: RunCodeFn, pluginId: string | null) {
8484 const messagesRef = useRef < ChatMessage [ ] > ( [ ] ) ;
8585 messagesRef . current = messages ;
8686 const templateUsedRef = useRef < string | null > ( null ) ;
87+ const abortControllerRef = useRef < AbortController | null > ( null ) ;
8788
8889 // Load existing messages when pluginId changes
8990 useEffect ( ( ) => {
9091 if ( ! pluginId ) {
92+ abortControllerRef . current ?. abort ( ) ;
93+ abortControllerRef . current = null ;
9194 setMessages ( [ ] ) ;
95+ setIsLoading ( false ) ;
9296 setHistoryLoaded ( false ) ;
9397 templateUsedRef . current = null ;
9498 return ;
@@ -182,6 +186,10 @@ export function useChat(runCode: RunCodeFn, pluginId: string | null) {
182186 setMessages ( ( prev ) => [ ...prev , userMsg , assistantMsg ] ) ;
183187 setIsLoading ( true ) ;
184188
189+ abortControllerRef . current ?. abort ( ) ;
190+ const abortController = new AbortController ( ) ;
191+ abortControllerRef . current = abortController ;
192+
185193 // Save user message to Firestore
186194 if ( pluginId ) {
187195 saveMessage ( pluginId , { role : "user" , content : prompt } ) . catch ( ( e ) => console . warn ( "Failed to save user message:" , e ) ) ;
@@ -245,7 +253,7 @@ export function useChat(runCode: RunCodeFn, pluginId: string | null) {
245253 // Phase 1: Stream LLM response (accumulate but don't show in UI)
246254 let fullResponse = "" ;
247255 let streamError = false ;
248- for await ( const event of streamLLM ( currentPrompt , model , currentHistory , attempt === 0 ? template : undefined , attempt === 0 ? templateCode : undefined ) ) {
256+ for await ( const event of streamLLM ( currentPrompt , model , currentHistory , attempt === 0 ? template : undefined , attempt === 0 ? templateCode : undefined , abortController . signal ) ) {
249257 if ( event . type === "chunk" ) {
250258 fullResponse += event . content || "" ;
251259 } else if ( event . type === "error" ) {
@@ -424,6 +432,7 @@ export function useChat(runCode: RunCodeFn, pluginId: string | null) {
424432 }
425433 } // end retry loop
426434 } catch ( err ) {
435+ if ( err instanceof DOMException && err . name === "AbortError" ) return ;
427436 const isRateLimited = err instanceof RateLimitError ;
428437 const errorMsg = err instanceof Error ? err . message : "Unknown error" ;
429438 setMessages ( ( prev ) =>
@@ -434,7 +443,10 @@ export function useChat(runCode: RunCodeFn, pluginId: string | null) {
434443 )
435444 ) ;
436445 } finally {
437- setIsLoading ( false ) ;
446+ if ( abortControllerRef . current === abortController ) {
447+ setIsLoading ( false ) ;
448+ abortControllerRef . current = null ;
449+ }
438450 }
439451 } ,
440452 [ runCode , pluginId ]
@@ -447,6 +459,10 @@ export function useChat(runCode: RunCodeFn, pluginId: string | null) {
447459 setMessages ( ( prev ) => [ ...prev , userMsg , assistantMsg ] ) ;
448460 setIsLoading ( true ) ;
449461
462+ abortControllerRef . current ?. abort ( ) ;
463+ const abortController = new AbortController ( ) ;
464+ abortControllerRef . current = abortController ;
465+
450466 if ( pluginId ) {
451467 saveMessage ( pluginId , { role : "user" , content : userMsg . content } ) . catch ( ( e ) => console . warn ( "Failed to save user message:" , e ) ) ;
452468 }
@@ -477,6 +493,8 @@ export function useChat(runCode: RunCodeFn, pluginId: string | null) {
477493 const code = await fetchTemplateCode ( templateName ) ;
478494 const rewritten = rewriteSavePaths ( code ) ;
479495
496+ if ( abortController . signal . aborted ) return ;
497+
480498 setMessages ( ( prev ) =>
481499 prev . map ( ( m ) => ( m . id === assistantId ? { ...m , status : "running" as const } : m ) )
482500 ) ;
@@ -566,19 +584,26 @@ export function useChat(runCode: RunCodeFn, pluginId: string | null) {
566584 }
567585 }
568586 } catch ( err ) {
587+ if ( err instanceof DOMException && err . name === "AbortError" ) return ;
569588 const errorMsg = err instanceof Error ? err . message : "Template build failed" ;
570589 setMessages ( ( prev ) =>
571590 prev . map ( ( m ) => ( m . id === assistantId ? { ...m , error : errorMsg , status : "error" } : m ) )
572591 ) ;
573592 } finally {
574- setIsLoading ( false ) ;
593+ if ( abortControllerRef . current === abortController ) {
594+ setIsLoading ( false ) ;
595+ abortControllerRef . current = null ;
596+ }
575597 }
576598 } ,
577599 [ runCode , pluginId ]
578600 ) ;
579601
580602 const clearMessages = useCallback ( ( ) => {
603+ abortControllerRef . current ?. abort ( ) ;
604+ abortControllerRef . current = null ;
581605 setMessages ( [ ] ) ;
606+ setIsLoading ( false ) ;
582607 templateUsedRef . current = null ;
583608 } , [ ] ) ;
584609
0 commit comments