diff --git a/apps/rowboat/app/projects/[projectId]/workflow/copilot.tsx b/apps/rowboat/app/projects/[projectId]/workflow/copilot.tsx index 4d8e66b1..83848b14 100644 --- a/apps/rowboat/app/projects/[projectId]/workflow/copilot.tsx +++ b/apps/rowboat/app/projects/[projectId]/workflow/copilot.tsx @@ -174,23 +174,35 @@ function App({ workflow, dispatch, chatContext=undefined, + messages, + setMessages, + loadingResponse, + setLoadingResponse, + loadingMessage, + setLoadingMessage, + responseError, + setResponseError, }: { projectId: string; workflow: z.infer; dispatch: (action: WorkflowDispatch) => void; chatContext?: z.infer; + messages: z.infer[]; + setMessages: (messages: z.infer[]) => void; + loadingResponse: boolean; + setLoadingResponse: (loading: boolean) => void; + loadingMessage: string; + setLoadingMessage: (message: string) => void; + responseError: string | null; + setResponseError: (error: string | null) => void; }) { - const [messages, setMessages] = useState[]>([]); - const [loadingResponse, setLoadingResponse] = useState(false); - const [loadingMessage, setLoadingMessage] = useState("Thinking..."); - const [responseError, setResponseError] = useState(null); const messagesEndRef = useRef(null); const [appliedChanges, setAppliedChanges] = useState>({}); const [discardContext, setDiscardContext] = useState(false); const [lastRequest, setLastRequest] = useState(null); const [lastResponse, setLastResponse] = useState(null); - // Cycle through loading messages until reaching the last one + // First useEffect for loading messages useEffect(() => { setLoadingMessage("Thinking"); if (!loadingResponse) return; @@ -210,7 +222,7 @@ function App({ }, 4000); return () => clearInterval(interval); - }, [loadingResponse, messages]); + }, [loadingResponse, setLoadingMessage]); // Reset discardContext when chatContext changes useEffect(() => { @@ -328,7 +340,7 @@ function App({ } }, [dispatch, appliedChanges, messages]); - // get copilot response + // Second useEffect for copilot response useEffect(() => { let ignore = false; @@ -383,7 +395,16 @@ function App({ return () => { ignore = true; }; - }, [messages, projectId, responseError, workflow, effectiveContext]); + }, [ + messages, + projectId, + responseError, + workflow, + effectiveContext, + setLoadingResponse, + setMessages, + setResponseError + ]); function handleCopyChat() { const jsonString = JSON.stringify({ @@ -483,18 +504,30 @@ export function Copilot({ workflow, chatContext=undefined, dispatch, + onNewChat, + messages, + setMessages, + loadingResponse, + setLoadingResponse, + loadingMessage, + setLoadingMessage, + responseError, + setResponseError, }: { projectId: string; workflow: z.infer; chatContext?: z.infer; dispatch: (action: WorkflowDispatch) => void; + onNewChat: () => void; + messages: z.infer[]; + setMessages: (messages: z.infer[]) => void; + loadingResponse: boolean; + setLoadingResponse: (loading: boolean) => void; + loadingMessage: string; + setLoadingMessage: (message: string) => void; + responseError: string | null; + setResponseError: (error: string | null) => void; }) { - const [key, setKey] = useState(0); - - function handleNewChat() { - setKey(key + 1); - } - return ( } - onClick={handleNewChat} + onClick={onNewChat} > - Ask + New ]}> ); diff --git a/apps/rowboat/app/projects/[projectId]/workflow/workflow_editor.tsx b/apps/rowboat/app/projects/[projectId]/workflow/workflow_editor.tsx index 404cd083..2a89661c 100644 --- a/apps/rowboat/app/projects/[projectId]/workflow/workflow_editor.tsx +++ b/apps/rowboat/app/projects/[projectId]/workflow/workflow_editor.tsx @@ -26,8 +26,9 @@ import { apiV1 } from "rowboat-shared"; import { publishWorkflow, renameWorkflow, saveWorkflow } from "../../../actions/workflow_actions"; import { PublishedBadge } from "./published_badge"; import { BackIcon, HamburgerIcon, WorkflowIcon } from "../../../lib/components/icons"; -import { CopyIcon, Layers2Icon, RadioIcon, RedoIcon, UndoIcon } from "lucide-react"; +import { CopyIcon, Layers2Icon, RadioIcon, RedoIcon, Sparkles, UndoIcon } from "lucide-react"; import { EntityList } from "./entity_list"; +import { CopilotMessage } from "../../../lib/types/copilot_types"; enablePatches(); @@ -524,6 +525,13 @@ export function WorkflowEditor({ const saving = useRef(false); const isLive = state.present.workflow._id == state.present.publishedWorkflowId; const [showCopySuccess, setShowCopySuccess] = useState(false); + const [showCopilot, setShowCopilot] = useState(false); + const [copilotWidth, setCopilotWidth] = useState(25); + const [copilotKey, setCopilotKey] = useState(0); + const [copilotMessages, setCopilotMessages] = useState[]>([]); + const [loadingResponse, setLoadingResponse] = useState(false); + const [loadingMessage, setLoadingMessage] = useState("Thinking..."); + const [responseError, setResponseError] = useState(null); console.log(`workflow editor chat key: ${state.present.chatKey}`); @@ -767,6 +775,13 @@ export function WorkflowEditor({ > + } @@ -792,7 +807,11 @@ export function WorkflowEditor({ /> - + - - - 0 ? { - type: 'chat', - messages: chatMessages - } : undefined - } - /> - + {showCopilot && <> + + setCopilotWidth(size)} + > + 0 ? { + type: 'chat', + messages: chatMessages + } : undefined + } + onNewChat={() => { + setCopilotKey(prev => prev + 1); + setCopilotMessages([]); + setLoadingResponse(false); + setLoadingMessage("Thinking..."); + setResponseError(null); + }} + messages={copilotMessages} + setMessages={setCopilotMessages} + loadingResponse={loadingResponse} + setLoadingResponse={setLoadingResponse} + loadingMessage={loadingMessage} + setLoadingMessage={setLoadingMessage} + responseError={responseError} + setResponseError={setResponseError} + /> + + } ; }