From 5cf4da0814fcfa39eeedf91064e51cd53ba99a02 Mon Sep 17 00:00:00 2001 From: Vorflux AI Date: Fri, 22 May 2026 14:04:22 +0000 Subject: [PATCH 1/3] feat: add Save to Space button in Nova chat UI Add a 'Save to [space name]' button in the ChatSidebar component: - saveState useState to track idle/saving/saved states - useEffect to reset saveState to idle when new assistant messages arrive - handleSaveToSpace callback that POSTs to /chat/save-as-document endpoint - Save button rendered in chatToolbarActions, visible only when messages exist and not in auto-space mode The button shows contextual text: 'Save to {space}', 'Saving...', or 'Saved to space' based on the current save state. --- apps/web/components/chat/index.tsx | 50 ++++++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) diff --git a/apps/web/components/chat/index.tsx b/apps/web/components/chat/index.tsx index 98dc46d7b..30971d090 100644 --- a/apps/web/components/chat/index.tsx +++ b/apps/web/components/chat/index.tsx @@ -143,6 +143,9 @@ export function ChatSidebar({ const [confirmingDeleteId, setConfirmingDeleteId] = useState( null, ) + const [saveState, setSaveState] = useState<"idle" | "saving" | "saved">( + "idle", + ) const messagesContainerRef = useRef(null) const isScrolledToBottomRef = useRef(true) const userJustSentRef = useRef(false) @@ -428,6 +431,39 @@ export function ChatSidebar({ setInput("") }, [setThreadId, setMessages]) + // Reset saveState to idle when a new assistant message arrives + const lastMessageRole = messages.at(-1)?.role + useEffect(() => { + if (lastMessageRole === "assistant") { + setSaveState("idle") + } + }, [messages.length, lastMessageRole]) + + const handleSaveToSpace = useCallback(async () => { + if (saveState !== "idle" || messages.length === 0) return + setSaveState("saving") + try { + const response = await fetch( + `${process.env.NEXT_PUBLIC_BACKEND_URL}/chat/save-as-document`, + { + method: "POST", + credentials: "include", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ + chatId: currentChatId, + projectId: chatProject, + messages, + }), + }, + ) + if (!response.ok) throw new Error("Save failed") + setSaveState("saved") + } catch (error) { + console.error("Failed to save chat:", error) + setSaveState("idle") + } + }, [saveState, messages, currentChatId, chatProject]) + const fetchThreads = useCallback(async () => { setIsLoadingThreads(true) try { @@ -905,6 +941,20 @@ export function ChatSidebar({ const chatToolbarActions = (
+ {messages.length > 0 && !isAutoChatSpace && ( + + )}