import { CopilotChatDefaultLabels, useCopilotChatConfiguration } from "../../providers/CopilotChatConfigurationProvider.mjs"; import { cn } from "../../lib/utils.mjs"; import { Button } from "../ui/button.mjs"; import { renderSlot } from "../../lib/slots.mjs"; import CopilotChatInput_default from "./CopilotChatInput.mjs"; import CopilotChatSuggestionView from "./CopilotChatSuggestionView.mjs"; import CopilotChatMessageView from "./CopilotChatMessageView.mjs"; import { useKeyboardHeight } from "../../hooks/use-keyboard-height.mjs"; import React, { useEffect, useRef, useState } from "react"; import { twMerge } from "tailwind-merge"; import { ChevronDown } from "lucide-react"; import { Fragment, jsx, jsxs } from "react/jsx-runtime"; import { StickToBottom, useStickToBottom, useStickToBottomContext } from "use-stick-to-bottom"; //#region src/components/chat/CopilotChatView.tsx const FEATHER_HEIGHT = 96; function CopilotChatView({ messageView, input, scrollView, suggestionView, welcomeScreen, messages = [], autoScroll = true, isRunning = false, suggestions, suggestionLoadingIndexes, onSelectSuggestion, onSubmitMessage, onStop, inputMode, inputValue, onInputChange, onStartTranscribe, onCancelTranscribe, onFinishTranscribe, onFinishTranscribeWithAudio, disclaimer, children, className, ...props }) { const inputContainerRef = useRef(null); const [inputContainerHeight, setInputContainerHeight] = useState(0); const [isResizing, setIsResizing] = useState(false); const resizeTimeoutRef = useRef(null); const { isKeyboardOpen, keyboardHeight, availableHeight } = useKeyboardHeight(); useEffect(() => { const element = inputContainerRef.current; if (!element) return; const resizeObserver = new ResizeObserver((entries) => { for (const entry of entries) { const newHeight = entry.contentRect.height; setInputContainerHeight((prevHeight) => { if (newHeight !== prevHeight) { setIsResizing(true); if (resizeTimeoutRef.current) clearTimeout(resizeTimeoutRef.current); resizeTimeoutRef.current = setTimeout(() => { setIsResizing(false); }, 250); return newHeight; } return prevHeight; }); } }); resizeObserver.observe(element); setInputContainerHeight(element.offsetHeight); return () => { resizeObserver.disconnect(); if (resizeTimeoutRef.current) clearTimeout(resizeTimeoutRef.current); }; }, []); const BoundMessageView = renderSlot(messageView, CopilotChatMessageView, { messages, isRunning }); const BoundInput = renderSlot(input, CopilotChatInput_default, { onSubmitMessage, onStop, mode: inputMode, value: inputValue, onChange: onInputChange, isRunning, onStartTranscribe, onCancelTranscribe, onFinishTranscribe, onFinishTranscribeWithAudio, positioning: "absolute", keyboardHeight: isKeyboardOpen ? keyboardHeight : 0, containerRef: inputContainerRef, showDisclaimer: true, ...disclaimer !== void 0 ? { disclaimer } : {} }); const hasSuggestions = Array.isArray(suggestions) && suggestions.length > 0; const BoundSuggestionView = hasSuggestions ? renderSlot(suggestionView, CopilotChatSuggestionView, { suggestions, loadingIndexes: suggestionLoadingIndexes, onSelectSuggestion, className: "cpk:mb-3 cpk:lg:ml-4 cpk:lg:mr-4 cpk:ml-0 cpk:mr-0" }) : null; const BoundScrollView = renderSlot(scrollView, CopilotChatView.ScrollView, { autoScroll, inputContainerHeight, isResizing, children: /* @__PURE__ */ jsx("div", { style: { paddingBottom: `${inputContainerHeight + FEATHER_HEIGHT + (hasSuggestions ? 4 : 32)}px` }, children: /* @__PURE__ */ jsxs("div", { className: "cpk:max-w-3xl cpk:mx-auto", children: [BoundMessageView, hasSuggestions ? /* @__PURE__ */ jsx("div", { className: "cpk:pl-0 cpk:pr-4 cpk:sm:px-0 cpk:mt-4", children: BoundSuggestionView }) : null] }) }) }); if (messages.length === 0 && !(welcomeScreen === false)) { const BoundInputForWelcome = renderSlot(input, CopilotChatInput_default, { onSubmitMessage, onStop, mode: inputMode, value: inputValue, onChange: onInputChange, isRunning, onStartTranscribe, onCancelTranscribe, onFinishTranscribe, onFinishTranscribeWithAudio, positioning: "static", showDisclaimer: true, ...disclaimer !== void 0 ? { disclaimer } : {} }); const BoundWelcomeScreen = renderSlot(welcomeScreen === true ? void 0 : welcomeScreen, CopilotChatView.WelcomeScreen, { input: BoundInputForWelcome, suggestionView: BoundSuggestionView ?? /* @__PURE__ */ jsx(Fragment, {}) }); return /* @__PURE__ */ jsx("div", { "data-copilotkit": true, "data-testid": "copilot-chat", "data-copilot-running": isRunning ? "true" : "false", className: twMerge("copilotKitChat cpk:relative cpk:h-full cpk:flex cpk:flex-col", className), ...props, children: BoundWelcomeScreen }); } if (children) return /* @__PURE__ */ jsx("div", { "data-copilotkit": true, style: { display: "contents" }, children: children({ messageView: BoundMessageView, input: BoundInput, scrollView: BoundScrollView, suggestionView: BoundSuggestionView ?? /* @__PURE__ */ jsx(Fragment, {}) }) }); return /* @__PURE__ */ jsxs("div", { "data-copilotkit": true, "data-testid": "copilot-chat", "data-copilot-running": isRunning ? "true" : "false", className: twMerge("copilotKitChat cpk:relative cpk:h-full", className), ...props, children: [BoundScrollView, BoundInput] }); } (function(_CopilotChatView) { const ScrollContent = ({ children, scrollToBottomButton, feather, inputContainerHeight, isResizing }) => { const { isAtBottom, scrollToBottom } = useStickToBottomContext(); const BoundFeather = renderSlot(feather, CopilotChatView.Feather, {}); return /* @__PURE__ */ jsxs(Fragment, { children: [ /* @__PURE__ */ jsx(StickToBottom.Content, { className: "cpk:overflow-y-scroll cpk:overflow-x-hidden", style: { flex: "1 1 0%", minHeight: 0 }, children: /* @__PURE__ */ jsx("div", { className: "cpk:px-4 cpk:sm:px-0 cpk:[div[data-sidebar-chat]_&]:px-8 cpk:[div[data-popup-chat]_&]:px-6", children }) }), BoundFeather, !isAtBottom && !isResizing && /* @__PURE__ */ jsx("div", { className: "cpk:absolute cpk:inset-x-0 cpk:flex cpk:justify-center cpk:z-30 cpk:pointer-events-none", style: { bottom: `${inputContainerHeight + FEATHER_HEIGHT + 16}px` }, children: renderSlot(scrollToBottomButton, CopilotChatView.ScrollToBottomButton, { onClick: () => scrollToBottom() }) }) ] }); }; _CopilotChatView.ScrollView = ({ children, autoScroll = true, scrollToBottomButton, feather, inputContainerHeight = 0, isResizing = false, className, ...props }) => { const [hasMounted, setHasMounted] = useState(false); const { scrollRef, contentRef, scrollToBottom } = useStickToBottom(); const [showScrollButton, setShowScrollButton] = useState(false); useEffect(() => { setHasMounted(true); }, []); useEffect(() => { if (autoScroll) return; const scrollElement = scrollRef.current; if (!scrollElement) return; const checkScroll = () => { setShowScrollButton(!(scrollElement.scrollHeight - scrollElement.scrollTop - scrollElement.clientHeight < 10)); }; checkScroll(); scrollElement.addEventListener("scroll", checkScroll); const resizeObserver = new ResizeObserver(checkScroll); resizeObserver.observe(scrollElement); return () => { scrollElement.removeEventListener("scroll", checkScroll); resizeObserver.disconnect(); }; }, [scrollRef, autoScroll]); if (!hasMounted) return /* @__PURE__ */ jsx("div", { className: "cpk:h-full cpk:max-h-full cpk:flex cpk:flex-col cpk:min-h-0 cpk:overflow-y-scroll cpk:overflow-x-hidden", children: /* @__PURE__ */ jsx("div", { className: "cpk:px-4 cpk:sm:px-0 cpk:[div[data-sidebar-chat]_&]:px-8 cpk:[div[data-popup-chat]_&]:px-6", children }) }); if (!autoScroll) { const BoundFeather = renderSlot(feather, CopilotChatView.Feather, {}); return /* @__PURE__ */ jsxs("div", { ref: scrollRef, className: cn("cpk:h-full cpk:max-h-full cpk:flex cpk:flex-col cpk:min-h-0 cpk:overflow-y-scroll cpk:overflow-x-hidden cpk:relative", className), ...props, children: [ /* @__PURE__ */ jsx("div", { ref: contentRef, className: "cpk:px-4 cpk:sm:px-0 cpk:[div[data-sidebar-chat]_&]:px-8 cpk:[div[data-popup-chat]_&]:px-6", children }), BoundFeather, showScrollButton && !isResizing && /* @__PURE__ */ jsx("div", { className: "cpk:absolute cpk:inset-x-0 cpk:flex cpk:justify-center cpk:z-30 cpk:pointer-events-none", style: { bottom: `${inputContainerHeight + FEATHER_HEIGHT + 16}px` }, children: renderSlot(scrollToBottomButton, CopilotChatView.ScrollToBottomButton, { onClick: () => scrollToBottom() }) }) ] }); } return /* @__PURE__ */ jsx(StickToBottom, { className: cn("cpk:h-full cpk:max-h-full cpk:flex cpk:flex-col cpk:min-h-0 cpk:relative", className), resize: "smooth", initial: "smooth", ...props, children: /* @__PURE__ */ jsx(ScrollContent, { scrollToBottomButton, feather, inputContainerHeight, isResizing, children }) }); }; _CopilotChatView.ScrollToBottomButton = ({ className, ...props }) => /* @__PURE__ */ jsx(Button, { "data-testid": "copilot-scroll-to-bottom", variant: "outline", size: "sm", className: twMerge("cpk:rounded-full cpk:w-10 cpk:h-10 cpk:p-0 cpk:pointer-events-auto", "cpk:bg-white cpk:dark:bg-gray-900", "cpk:shadow-lg cpk:border cpk:border-gray-200 cpk:dark:border-gray-700", "cpk:hover:bg-gray-50 cpk:dark:hover:bg-gray-800", "cpk:flex cpk:items-center cpk:justify-center cpk:cursor-pointer", className), ...props, children: /* @__PURE__ */ jsx(ChevronDown, { className: "cpk:w-4 cpk:h-4 cpk:text-gray-600 cpk:dark:text-white" }) }); _CopilotChatView.Feather = ({ className, style, ...props }) => /* @__PURE__ */ jsx("div", { className: cn("cpk:absolute cpk:bottom-0 cpk:left-0 cpk:right-4 cpk:h-24 cpk:pointer-events-none cpk:z-10 cpk:bg-gradient-to-t", "cpk:from-white cpk:via-white cpk:to-transparent", "cpk:dark:from-[rgb(33,33,33)] cpk:dark:via-[rgb(33,33,33)]", className), style, ...props }); _CopilotChatView.WelcomeMessage = ({ className, ...props }) => { const labels = useCopilotChatConfiguration()?.labels ?? CopilotChatDefaultLabels; return /* @__PURE__ */ jsx("h1", { className: cn("cpk:text-xl cpk:sm:text-2xl cpk:font-medium cpk:text-foreground cpk:text-center", className), ...props, children: labels.welcomeMessageText }); }; _CopilotChatView.WelcomeScreen = ({ welcomeMessage, input, suggestionView, className, children, ...props }) => { const BoundWelcomeMessage = renderSlot(welcomeMessage, CopilotChatView.WelcomeMessage, {}); if (children) return /* @__PURE__ */ jsx("div", { "data-copilotkit": true, style: { display: "contents" }, children: children({ welcomeMessage: BoundWelcomeMessage, input, suggestionView, className, ...props }) }); return /* @__PURE__ */ jsx("div", { "data-testid": "copilot-welcome-screen", className: cn("cpk:flex-1 cpk:flex cpk:flex-col cpk:items-center cpk:justify-center cpk:px-4", className), ...props, children: /* @__PURE__ */ jsxs("div", { className: "cpk:w-full cpk:max-w-3xl cpk:flex cpk:flex-col cpk:items-center", children: [ /* @__PURE__ */ jsx("div", { className: "cpk:mb-6", children: BoundWelcomeMessage }), /* @__PURE__ */ jsx("div", { className: "cpk:w-full", children: input }), /* @__PURE__ */ jsx("div", { className: "cpk:mt-4 cpk:flex cpk:justify-center", children: suggestionView }) ] }) }); }; })(CopilotChatView || (CopilotChatView = {})); var CopilotChatView_default = CopilotChatView; //#endregion export { CopilotChatView, CopilotChatView_default as default }; //# sourceMappingURL=CopilotChatView.mjs.map