diff --git a/packages/v1/react-ui/src/components/chat/Chat.tsx b/packages/v1/react-ui/src/components/chat/Chat.tsx index b36f3d89964..df472ac7357 100644 --- a/packages/v1/react-ui/src/components/chat/Chat.tsx +++ b/packages/v1/react-ui/src/components/chat/Chat.tsx @@ -74,7 +74,13 @@ import { RenderMessage as DefaultRenderMessage } from "./messages/RenderMessage" import { AssistantMessage as DefaultAssistantMessage } from "./messages/AssistantMessage"; import { UserMessage as DefaultUserMessage } from "./messages/UserMessage"; import { ImageRenderer as DefaultImageRenderer } from "./messages/ImageRenderer"; -import React, { useEffect, useRef, useState, useCallback } from "react"; +import React, { + useEffect, + useRef, + useState, + useCallback, + useMemo, +} from "react"; import { SystemMessageFunction, useCopilotContext, @@ -335,6 +341,31 @@ export type ImageUpload = { bytes: string; }; +type CopilotChatImageUploadsContextValue = { + selectedImages: Array; + setSelectedImages: React.Dispatch>>; + clearSelectedImages: () => void; + removeSelectedImage: (index: number) => void; +}; + +const CopilotChatImageUploadsContext = React.createContext< + CopilotChatImageUploadsContextValue | undefined +>(undefined); + +/** + * Access image upload state from within a CopilotChat tree. + */ +export function useCopilotChatImageUploads() { + const context = React.useContext(CopilotChatImageUploadsContext); + if (!context) { + throw new Error( + "Context not found. Did you forget to render this hook inside a component?", + ); + } + + return context; +} + export function CopilotChat({ instructions, suggestions = "auto", @@ -391,6 +422,13 @@ export function CopilotChat({ >({}); const fileInputRef = useRef(null); + const clearSelectedImages = useCallback(() => { + setSelectedImages([]); + if (fileInputRef.current) { + fileInputRef.current.value = ""; + } + }, []); + // Helper function to trigger event hooks only if publicApiKey is provided const triggerObservabilityHook = useCallback( (hookName: keyof CopilotObservabilityHooks, ...args: any[]) => { @@ -600,11 +638,7 @@ export function CopilotChat({ // Wrapper for sendMessage to clear selected images const handleSendMessage = (text: string) => { - const images = selectedImages; - setSelectedImages([]); - if (fileInputRef.current) { - fileInputRef.current.value = ""; - } + clearSelectedImages(); // Trigger message sent event triggerObservabilityHook("onMessageSent", text); @@ -686,6 +720,16 @@ export function CopilotChat({ setSelectedImages((prev) => prev.filter((_, i) => i !== index)); }; + const imageUploadsContextValue = useMemo( + () => ({ + selectedImages, + setSelectedImages, + clearSelectedImages, + removeSelectedImage, + }), + [selectedImages, clearSelectedImages], + ); + const handleThumbsUp = (message: Message) => { if (onThumbsUp) { onThumbsUp(message); @@ -717,80 +761,84 @@ export function CopilotChat({ }; return ( - - {/* Render error above messages if present */} - {chatError && - renderError && - renderError({ - ...chatError, - onDismiss: () => setChatError(null), - onRetry: () => { - // Clear error and potentially retry based on operation - setChatError(null); - // TODO: Implement specific retry logic based on operation type - }, - })} - - - {currentSuggestions.length > 0 && ( - + + + {/* Render error above messages if present */} + {chatError && + renderError && + renderError({ + ...chatError, + onDismiss: () => setChatError(null), + onRetry: () => { + // Clear error and potentially retry based on operation + setChatError(null); + // TODO: Implement specific retry logic based on operation type + }, + })} + + + {currentSuggestions.length > 0 && ( + + )} + + + {imageUploadsEnabled && ( + <> + + + )} - - - {imageUploadsEnabled && ( - <> - - - - )} - fileInputRef.current?.click() : undefined - } - hideStopButton={hideStopButton} - /> - + fileInputRef.current?.click() + : undefined + } + hideStopButton={hideStopButton} + /> + + ); } diff --git a/packages/v1/react-ui/src/components/chat/index.tsx b/packages/v1/react-ui/src/components/chat/index.tsx index 7a5fcc35f95..7bf06bc517b 100644 --- a/packages/v1/react-ui/src/components/chat/index.tsx +++ b/packages/v1/react-ui/src/components/chat/index.tsx @@ -2,6 +2,7 @@ export * from "./props"; export { CopilotPopup } from "./Popup"; export { CopilotSidebar } from "./Sidebar"; export { CopilotChat } from "./Chat"; +export { useCopilotChatImageUploads } from "./Chat"; export { Markdown } from "./Markdown"; export { AssistantMessage } from "./messages/AssistantMessage"; export { UserMessage } from "./messages/UserMessage";