@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
JavaScript
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