diff --git a/apps/agent/entrypoints/sidepanel/index/ChatProviderSelector.tsx b/apps/agent/components/chat/ChatProviderSelector.tsx similarity index 98% rename from apps/agent/entrypoints/sidepanel/index/ChatProviderSelector.tsx rename to apps/agent/components/chat/ChatProviderSelector.tsx index 3ddcb1517..b9897cf60 100644 --- a/apps/agent/entrypoints/sidepanel/index/ChatProviderSelector.tsx +++ b/apps/agent/components/chat/ChatProviderSelector.tsx @@ -17,7 +17,7 @@ import { import { BrowserOSIcon, ProviderIcon } from '@/lib/llm-providers/providerIcons' import type { ProviderType } from '@/lib/llm-providers/types' import { cn } from '@/lib/utils' -import type { Provider } from './chatTypes' +import type { Provider } from './chatComponentTypes' interface ChatProviderSelectorProps { providers: Provider[] diff --git a/apps/agent/components/chat/chatComponentTypes.ts b/apps/agent/components/chat/chatComponentTypes.ts new file mode 100644 index 000000000..dc0e8f3ec --- /dev/null +++ b/apps/agent/components/chat/chatComponentTypes.ts @@ -0,0 +1,7 @@ +import type { ProviderType } from '@/lib/llm-providers/types' + +export interface Provider { + id: string + name: string + type: ProviderType +} diff --git a/apps/agent/entrypoints/options/create-graph/CreateGraph.tsx b/apps/agent/entrypoints/options/create-graph/CreateGraph.tsx index 9a1c128ce..52ad337d0 100644 --- a/apps/agent/entrypoints/options/create-graph/CreateGraph.tsx +++ b/apps/agent/entrypoints/options/create-graph/CreateGraph.tsx @@ -5,6 +5,17 @@ import type { FC, FormEvent } from 'react' import { useEffect, useRef, useState } from 'react' import { useSearchParams } from 'react-router' import useDeepCompareEffect from 'use-deep-compare-effect' +import type { Provider } from '@/components/chat/chatComponentTypes' +import { + AlertDialog, + AlertDialogAction, + AlertDialogCancel, + AlertDialogContent, + AlertDialogDescription, + AlertDialogFooter, + AlertDialogHeader, + AlertDialogTitle, +} from '@/components/ui/alert-dialog' import { ResizableHandle, ResizablePanel, @@ -12,11 +23,13 @@ import { } from '@/components/ui/resizable' import { useChatRefs } from '@/entrypoints/sidepanel/index/useChatRefs' import { useAgentServerUrl } from '@/lib/browseros/useBrowserOSProviders' +import { useLlmProviders } from '@/lib/llm-providers/useLlmProviders' import { useRpcClient } from '@/lib/rpc/RpcClientProvider' import { sentry } from '@/lib/sentry/sentry' import { useWorkflows } from '@/lib/workflows/workflowStorage' import { GraphCanvas } from './GraphCanvas' import { GraphChat } from './GraphChat' +import { WorkflowsChatHeader } from './WorkflowsChatHeader' type MessageType = 'create-graph' | 'update-graph' | 'run-graph' @@ -68,8 +81,10 @@ export const CreateGraph: FC = () => { >(undefined) const [query, setQuery] = useState('') + const [showDiscardDialog, setShowDiscardDialog] = useState(false) const { workflows, addWorkflow, editWorkflow } = useWorkflows() + const { providers: llmProviders, setDefaultProvider } = useLlmProviders() const rpcClient = useRpcClient() // Initialize edit mode when workflowId is provided @@ -147,6 +162,8 @@ export const CreateGraph: FC = () => { enabledMcpServersRef, enabledCustomServersRef, personalizationRef, + selectedLlmProvider, + isLoadingProviders, } = useChatRefs() const agentUrlRef = useRef(agentServerUrl) @@ -158,7 +175,7 @@ export const CreateGraph: FC = () => { codeIdRef.current = codeId }, [agentServerUrl, codeId]) - const { sendMessage, stop, status, messages, error } = useChat({ + const { sendMessage, stop, status, messages, error, setMessages } = useChat({ transport: new DefaultChatTransport({ prepareSendMessagesRequest: async ({ messages }) => { const lastMessage = messages[messages.length - 1] @@ -285,6 +302,60 @@ export const CreateGraph: FC = () => { } } + // Provider data for header + const providers: Provider[] = llmProviders.map((p) => ({ + id: p.id, + name: p.name, + type: p.type, + })) + + const selectedProviderForHeader: Provider | undefined = selectedLlmProvider + ? { + id: selectedLlmProvider.id, + name: selectedLlmProvider.name, + type: selectedLlmProvider.type, + } + : providers[0] + + // Has generated code but can't auto-save (no name) + const hasUnsavedWork = codeId && !graphName + + const resetToNewWorkflow = () => { + setCodeId(undefined) + setGraphData(undefined) + setGraphName('') + setSavedWorkflowId(undefined) + setSavedCodeId(undefined) + setMessages([]) + } + + const handleSelectProvider = (provider: Provider) => { + setDefaultProvider(provider.id) + } + + const handleNewWorkflow = async () => { + // Can auto-save: has name AND code + if (graphName && codeId) { + await onClickSave() + resetToNewWorkflow() + return + } + + // Has unsaved work that can't be auto-saved: show confirmation + if (hasUnsavedWork) { + setShowDiscardDialog(true) + return + } + + // Nothing to save, just reset + resetToNewWorkflow() + } + + const handleConfirmDiscard = () => { + setShowDiscardDialog(false) + resetToNewWorkflow() + } + useDeepCompareEffect(() => { if (status === 'ready' && lastAssistantMessageWithGraph) { const metadata = lastAssistantMessageWithGraph.metadata as @@ -295,10 +366,10 @@ export const CreateGraph: FC = () => { } }, [status, lastAssistantMessageWithGraph ?? {}]) - if (!isInitialized) { + if (!isInitialized || isLoadingProviders || !selectedProviderForHeader) { return (