33import { memo , useCallback , useEffect , useMemo , useRef , useState } from 'react'
44import { createLogger } from '@sim/logger'
55import { toError } from '@sim/utils/errors'
6+ import { useQueryClient } from '@tanstack/react-query'
67import { History , Plus , Square } from 'lucide-react'
78import { useParams , useRouter } from 'next/navigation'
89import { usePostHog } from 'posthog-js/react'
@@ -60,6 +61,11 @@ import { useWorkflowExecution } from '@/app/workspace/[workspaceId]/w/[workflowI
6061import { getWorkflowLockToggleIds } from '@/app/workspace/[workspaceId]/w/[workflowId]/utils'
6162import { useDeleteWorkflow , useImportWorkflow } from '@/app/workspace/[workspaceId]/w/hooks'
6263import { useCopilotChatSelection } from '@/hooks/queries/copilot-chat-selection'
64+ import {
65+ type CopilotChatListItem ,
66+ copilotChatsKeys ,
67+ useCopilotChats ,
68+ } from '@/hooks/queries/copilot-chats'
6369import { useDuplicateWorkflowMutation , useWorkflowMap } from '@/hooks/queries/workflows'
6470import { useCollaborativeWorkflow } from '@/hooks/use-collaborative-workflow'
6571import { usePermissionConfig } from '@/hooks/use-permission-config'
@@ -78,6 +84,7 @@ import { useWorkflowStore } from '@/stores/workflows/workflow/store'
7884import type { WorkflowState } from '@/stores/workflows/workflow/types'
7985
8086const logger = createLogger ( 'Panel' )
87+ const EMPTY_COPILOT_CHATS : readonly CopilotChatListItem [ ] = [ ]
8188/**
8289 * Panel component with resizable width and tab navigation that persists across page refreshes.
8390 *
@@ -227,9 +234,9 @@ export const Panel = memo(function Panel({ workspaceId: propWorkspaceId }: Panel
227234 activeWorkflowId ?? undefined
228235 )
229236
230- const [ copilotChatList , setCopilotChatList ] = useState <
231- { id : string ; title : string | null ; updatedAt : string ; activeStreamId : string | null } [ ]
232- > ( [ ] )
237+ const { data : copilotChatList = EMPTY_COPILOT_CHATS } = useCopilotChats (
238+ activeWorkflowId ?? undefined
239+ )
233240 const [ isCopilotHistoryOpen , setIsCopilotHistoryOpen ] = useState ( false )
234241
235242 const copilotChatTitle = useMemo (
@@ -238,50 +245,32 @@ export const Panel = memo(function Panel({ workspaceId: propWorkspaceId }: Panel
238245 [ copilotChatId , copilotChatList ]
239246 )
240247
241- const copilotChatIdRef = useRef ( copilotChatId )
242- copilotChatIdRef . current = copilotChatId
243- const copilotInitialLoadDoneRef = useRef ( false )
244- const activeWorkflowIdRef = useRef ( activeWorkflowId )
245- activeWorkflowIdRef . current = activeWorkflowId
246-
248+ const queryClient = useQueryClient ( )
247249 const loadCopilotChats = useCallback ( ( ) => {
248250 if ( ! activeWorkflowId ) return
249- const requestWorkflowId = activeWorkflowId
250- fetch ( '/api/copilot/chats' )
251- . then ( ( res ) => ( res . ok ? res . json ( ) : { chats : [ ] } ) )
252- . then ( ( data ) => {
253- // Drop responses for a workflow we've already switched away from.
254- if ( requestWorkflowId !== activeWorkflowIdRef . current ) return
255- const allChats = Array . isArray ( data ?. chats ) ? data . chats : [ ]
256- const filtered = allChats . filter (
257- ( c : { workflowId ?: string } ) => c . workflowId === activeWorkflowId
258- ) as Array < {
259- id : string
260- title : string | null
261- updatedAt : string
262- activeStreamId : string | null
263- } >
264- setCopilotChatList ( filtered )
265-
266- const currentId = copilotChatIdRef . current
267- let resolvedCurrentId = currentId
268- if ( currentId && ! filtered . find ( ( c : { id : string } ) => c . id === currentId ) ) {
269- setCopilotChatId ( undefined )
270- resolvedCurrentId = undefined
271- }
272-
273- if ( ! copilotInitialLoadDoneRef . current && ! resolvedCurrentId && filtered . length > 0 ) {
274- setCopilotChatId ( filtered [ 0 ] . id )
275- }
276- copilotInitialLoadDoneRef . current = true
277- } )
278- . catch ( ( ) => { } )
279- } , [ activeWorkflowId , setCopilotChatId ] )
251+ queryClient . invalidateQueries ( { queryKey : copilotChatsKeys . list ( activeWorkflowId ) } )
252+ } , [ activeWorkflowId , queryClient ] )
280253
254+ // Auto-select most recent on first list arrival per workflow, and drop a
255+ // selection that no longer matches anything in the current list (e.g. the
256+ // chat was deleted in another tab).
257+ const autoSelectAttemptedForRef = useRef < Set < string > > ( new Set ( ) )
281258 useEffect ( ( ) => {
282- copilotInitialLoadDoneRef . current = false
283- loadCopilotChats ( )
284- } , [ loadCopilotChats ] )
259+ if ( ! activeWorkflowId ) return
260+
261+ if ( copilotChatId && ! copilotChatList . find ( ( c ) => c . id === copilotChatId ) ) {
262+ setCopilotChatId ( undefined )
263+ return
264+ }
265+
266+ if ( copilotChatId ) return
267+ if ( autoSelectAttemptedForRef . current . has ( activeWorkflowId ) ) return
268+ autoSelectAttemptedForRef . current . add ( activeWorkflowId )
269+
270+ if ( copilotChatList . length > 0 ) {
271+ setCopilotChatId ( copilotChatList [ 0 ] . id )
272+ }
273+ } , [ copilotChatList , copilotChatId , activeWorkflowId , setCopilotChatId ] )
285274
286275 useEffect ( ( ) => {
287276 posthogRef . current = posthog
0 commit comments