UNPKG

@copilotkit/react-ui

Version:

<img src="https://github.com/user-attachments/assets/0a6b64d9-e193-4940-a3f6-60334ac34084" alt="banner" style="border-radius: 12px; border: 2px solid #d6d4fa;" />

435 lines (433 loc) • 13.5 kB
import { Suggestions } from "./chunk-QB3GUN2N.mjs"; import { ImageUploadQueue } from "./chunk-PLHTVHUW.mjs"; import { Input } from "./chunk-3W6J75HS.mjs"; import { Messages } from "./chunk-HIW7RXCD.mjs"; import { RenderMessage } from "./chunk-NCIAFFQ2.mjs"; import { AssistantMessage } from "./chunk-IHFR6PYG.mjs"; import { ImageRenderer } from "./chunk-DBKRAOH7.mjs"; import { UserMessage } from "./chunk-RYUCX3ZK.mjs"; import { ChatContext, ChatContextProvider } from "./chunk-IEMQ2SQW.mjs"; import { __async, __spreadProps, __spreadValues } from "./chunk-MRXNTQOX.mjs"; // src/components/chat/Chat.tsx import React, { useEffect, useRef, useState, useCallback } from "react"; import { useCopilotContext, useCopilotChatInternal } from "@copilotkit/react-core"; import { CopilotKitError, CopilotKitErrorCode, Severity, ErrorVisibility, styledConsole, randomUUID } from "@copilotkit/shared"; import { Fragment, jsx, jsxs } from "react/jsx-runtime"; function CopilotChat({ instructions, suggestions = "auto", onSubmitMessage, makeSystemMessage, disableSystemMessage, onInProgress, onStopGeneration, onReloadMessages, onRegenerate, onCopy, onThumbsUp, onThumbsDown, markdownTagRenderers, Messages: Messages2 = Messages, RenderMessage: RenderMessage2 = RenderMessage, RenderSuggestionsList = Suggestions, Input: Input2 = Input, className, icons, labels, AssistantMessage: AssistantMessage2 = AssistantMessage, UserMessage: UserMessage2 = UserMessage, ImageRenderer: ImageRenderer2 = ImageRenderer, ErrorMessage, imageUploadsEnabled, inputFileAccept = "image/*", hideStopButton, observabilityHooks, renderError, onError, // Legacy props - deprecated RenderTextMessage, RenderActionExecutionMessage, RenderAgentStateMessage, RenderResultMessage, RenderImageMessage }) { const { additionalInstructions, setChatInstructions, copilotApiConfig, setBannerError, setInternalErrorHandler, removeInternalErrorHandler } = useCopilotContext(); const { publicApiKey, chatApiEndpoint } = copilotApiConfig; const [selectedImages, setSelectedImages] = useState([]); const [chatError, setChatError] = useState(null); const [messageFeedback, setMessageFeedback] = useState( {} ); const fileInputRef = useRef(null); const triggerObservabilityHook = useCallback( (hookName, ...args) => { if (publicApiKey && (observabilityHooks == null ? void 0 : observabilityHooks[hookName])) { observabilityHooks[hookName](...args); } if ((observabilityHooks == null ? void 0 : observabilityHooks[hookName]) && !publicApiKey) { setBannerError( new CopilotKitError({ message: "observabilityHooks requires a publicApiKey to function.", code: CopilotKitErrorCode.MISSING_PUBLIC_API_KEY_ERROR, severity: Severity.CRITICAL, visibility: ErrorVisibility.BANNER }) ); styledConsole.publicApiKeyRequired("observabilityHooks"); } }, [publicApiKey, observabilityHooks, setBannerError] ); const triggerChatError = useCallback( (error, operation, originalError) => { const errorMessage = (error == null ? void 0 : error.message) || (error == null ? void 0 : error.toString()) || "An error occurred"; setChatError({ message: errorMessage, operation, timestamp: Date.now() }); const errorEvent = { type: "error", timestamp: Date.now(), context: { source: "ui", request: { operation, url: chatApiEndpoint, startTime: Date.now() }, technical: { environment: "browser", userAgent: typeof navigator !== "undefined" ? navigator.userAgent : void 0, stackTrace: originalError instanceof Error ? originalError.stack : void 0 } }, error }; if (onError) { onError(errorEvent); } if (publicApiKey && (observabilityHooks == null ? void 0 : observabilityHooks.onError)) { observabilityHooks.onError(errorEvent); } if ((observabilityHooks == null ? void 0 : observabilityHooks.onError) && !publicApiKey) { setBannerError( new CopilotKitError({ message: "observabilityHooks.onError requires a publicApiKey to function.", code: CopilotKitErrorCode.MISSING_PUBLIC_API_KEY_ERROR, severity: Severity.CRITICAL, visibility: ErrorVisibility.BANNER }) ); styledConsole.publicApiKeyRequired("observabilityHooks.onError"); } }, [publicApiKey, chatApiEndpoint, observabilityHooks, setBannerError] ); useEffect(() => { const id = "chat-component"; setInternalErrorHandler({ [id]: (error) => { if (!error) return; triggerChatError(error.error, "sendMessage"); } }); return () => { removeInternalErrorHandler == null ? void 0 : removeInternalErrorHandler(id); }; }, [triggerChatError, setInternalErrorHandler, removeInternalErrorHandler]); useEffect(() => { if (!imageUploadsEnabled) return; const handlePaste = (e) => __async(this, null, function* () { var _a, _b; const target = e.target; if (!((_a = target.parentElement) == null ? void 0 : _a.classList.contains("copilotKitInput"))) return; const items = Array.from(((_b = e.clipboardData) == null ? void 0 : _b.items) || []); const imageItems = items.filter((item) => item.type.startsWith("image/")); if (imageItems.length === 0) return; e.preventDefault(); const imagePromises = imageItems.map((item) => { const file = item.getAsFile(); if (!file) return Promise.resolve(null); return new Promise((resolve, reject) => { const reader = new FileReader(); reader.onload = (e2) => { var _a2, _b2; const base64String = (_b2 = (_a2 = e2.target) == null ? void 0 : _a2.result) == null ? void 0 : _b2.split(",")[1]; if (base64String) { resolve({ contentType: file.type, bytes: base64String }); } else { resolve(null); } }; reader.onerror = reject; reader.readAsDataURL(file); }); }); try { const loadedImages = (yield Promise.all(imagePromises)).filter((img) => img !== null); setSelectedImages((prev) => [...prev, ...loadedImages]); } catch (error) { triggerChatError(error, "processClipboardImages", error); console.error("Error processing pasted images:", error); } }); document.addEventListener("paste", handlePaste); return () => document.removeEventListener("paste", handlePaste); }, [imageUploadsEnabled, triggerChatError]); useEffect(() => { if (!(additionalInstructions == null ? void 0 : additionalInstructions.length)) { setChatInstructions(instructions || ""); return; } const combinedAdditionalInstructions = [ instructions, "Additionally, follow these instructions:", ...additionalInstructions.map((instruction) => `- ${instruction}`) ]; setChatInstructions(combinedAdditionalInstructions.join("\n") || ""); }, [instructions, additionalInstructions]); const { messages, isLoading, sendMessage, stopGeneration, reloadMessages, suggestions: currentSuggestions, isLoadingSuggestions, agent } = useCopilotChatInternal({ suggestions, onInProgress, onSubmitMessage, onStopGeneration, onReloadMessages }); const prevIsLoading = useRef(isLoading); useEffect(() => { if (prevIsLoading.current !== isLoading) { if (isLoading) { triggerObservabilityHook("onChatStarted"); } else { triggerObservabilityHook("onChatStopped"); } prevIsLoading.current = isLoading; } }, [isLoading, triggerObservabilityHook]); const handleSendMessage = (text) => { const images = selectedImages; setSelectedImages([]); if (fileInputRef.current) { fileInputRef.current.value = ""; } triggerObservabilityHook("onMessageSent", text); return sendMessage({ id: randomUUID(), content: text, role: "user" }); }; const chatContext = React.useContext(ChatContext); const isVisible = chatContext ? chatContext.open : true; const handleRegenerate = (messageId) => { if (onRegenerate) { onRegenerate(messageId); } triggerObservabilityHook("onMessageRegenerated", messageId); reloadMessages(messageId); }; const handleCopy = (message) => { if (onCopy) { onCopy(message); } triggerObservabilityHook("onMessageCopied", message); }; const handleImageUpload = (event) => __async(this, null, function* () { if (!event.target.files || event.target.files.length === 0) { return; } const files = Array.from(event.target.files).filter((file) => file.type.startsWith("image/")); if (files.length === 0) return; const fileReadPromises = files.map((file) => { return new Promise((resolve, reject) => { const reader = new FileReader(); reader.onload = (e) => { var _a, _b; const base64String = ((_b = (_a = e.target) == null ? void 0 : _a.result) == null ? void 0 : _b.split(",")[1]) || ""; if (base64String) { resolve({ contentType: file.type, bytes: base64String }); } }; reader.onerror = reject; reader.readAsDataURL(file); }); }); try { const loadedImages = yield Promise.all(fileReadPromises); setSelectedImages((prev) => [...prev, ...loadedImages]); } catch (error) { triggerChatError(error, "processUploadedImages", error); console.error("Error reading files:", error); } }); const removeSelectedImage = (index) => { setSelectedImages((prev) => prev.filter((_, i) => i !== index)); }; const handleThumbsUp = (message) => { if (onThumbsUp) { onThumbsUp(message); } setMessageFeedback((prev) => __spreadProps(__spreadValues({}, prev), { [message.id]: "thumbsUp" })); triggerObservabilityHook("onFeedbackGiven", message.id, "thumbsUp"); }; const handleThumbsDown = (message) => { if (onThumbsDown) { onThumbsDown(message); } setMessageFeedback((prev) => __spreadProps(__spreadValues({}, prev), { [message.id]: "thumbsDown" })); triggerObservabilityHook("onFeedbackGiven", message.id, "thumbsDown"); }; return /* @__PURE__ */ jsxs(WrappedCopilotChat, { icons, labels, className, children: [ chatError && renderError && renderError(__spreadProps(__spreadValues({}, chatError), { onDismiss: () => setChatError(null), onRetry: () => { setChatError(null); } })), /* @__PURE__ */ jsx( Messages2, { AssistantMessage: AssistantMessage2, UserMessage: UserMessage2, RenderMessage: RenderMessage2, messages, inProgress: isLoading, onRegenerate: handleRegenerate, onCopy: handleCopy, onThumbsUp: handleThumbsUp, onThumbsDown: handleThumbsDown, messageFeedback, markdownTagRenderers, ImageRenderer: ImageRenderer2, ErrorMessage, chatError, RenderTextMessage, RenderActionExecutionMessage, RenderAgentStateMessage, RenderResultMessage, RenderImageMessage, children: currentSuggestions.length > 0 && /* @__PURE__ */ jsx( RenderSuggestionsList, { onSuggestionClick: handleSendMessage, suggestions: currentSuggestions, isLoading: isLoadingSuggestions } ) } ), imageUploadsEnabled && /* @__PURE__ */ jsxs(Fragment, { children: [ /* @__PURE__ */ jsx(ImageUploadQueue, { images: selectedImages, onRemoveImage: removeSelectedImage }), /* @__PURE__ */ jsx( "input", { type: "file", multiple: true, ref: fileInputRef, onChange: handleImageUpload, accept: inputFileAccept, style: { display: "none" } } ) ] }), /* @__PURE__ */ jsx( Input2, { inProgress: isLoading, chatReady: Boolean(agent), onSend: handleSendMessage, isVisible, onStop: stopGeneration, onUpload: imageUploadsEnabled ? () => { var _a; return (_a = fileInputRef.current) == null ? void 0 : _a.click(); } : void 0, hideStopButton } ) ] }); } function WrappedCopilotChat({ children, icons, labels, className }) { const chatContext = React.useContext(ChatContext); if (!chatContext) { return /* @__PURE__ */ jsx(ChatContextProvider, { icons, labels, open: true, setOpen: () => { }, children: /* @__PURE__ */ jsx("div", { className: `copilotKitChat ${className != null ? className : ""}`, children }) }); } return /* @__PURE__ */ jsx(Fragment, { children }); } export { CopilotChat, WrappedCopilotChat }; //# sourceMappingURL=chunk-QPQRLXN3.mjs.map