import { renderSlot } from "../../lib/slots.mjs"; import { useEffect, useRef, useState } from "react"; import { twMerge } from "tailwind-merge"; import { ChevronRight } from "lucide-react"; import { jsx, jsxs } from "react/jsx-runtime"; import { Streamdown } from "streamdown"; //#region src/components/chat/CopilotChatReasoningMessage.tsx /** * Formats an elapsed duration (in seconds) to a human-readable string. */ function formatDuration(seconds) { if (seconds < 1) return "a few seconds"; if (seconds < 60) return `${Math.round(seconds)} seconds`; const mins = Math.floor(seconds / 60); const secs = Math.round(seconds % 60); if (secs === 0) return `${mins} minute${mins > 1 ? "s" : ""}`; return `${mins}m ${secs}s`; } function CopilotChatReasoningMessage({ message, messages, isRunning, header, contentView, toggle, children, className, ...props }) { const isLatest = messages?.[messages.length - 1]?.id === message.id; const isStreaming = !!(isRunning && isLatest); const hasContent = !!(message.content && message.content.length > 0); const startTimeRef = useRef(null); const [elapsed, setElapsed] = useState(0); useEffect(() => { if (isStreaming && startTimeRef.current === null) startTimeRef.current = Date.now(); if (!isStreaming && startTimeRef.current !== null) { setElapsed((Date.now() - startTimeRef.current) / 1e3); return; } if (!isStreaming) return; const timer = setInterval(() => { if (startTimeRef.current !== null) setElapsed((Date.now() - startTimeRef.current) / 1e3); }, 1e3); return () => clearInterval(timer); }, [isStreaming]); const [isOpen, setIsOpen] = useState(isStreaming); useEffect(() => { if (isStreaming) setIsOpen(true); else setIsOpen(false); }, [isStreaming]); const label = isStreaming ? "Thinking…" : `Thought for ${formatDuration(elapsed)}`; const boundHeader = renderSlot(header, CopilotChatReasoningMessage.Header, { isOpen, label, hasContent, isStreaming, onClick: hasContent ? () => setIsOpen((prev) => !prev) : void 0 }); const boundContent = renderSlot(contentView, CopilotChatReasoningMessage.Content, { isStreaming, hasContent, children: message.content }); const boundToggle = renderSlot(toggle, CopilotChatReasoningMessage.Toggle, { isOpen, children: boundContent }); if (children) return /* @__PURE__ */ jsx("div", { "data-copilotkit": true, style: { display: "contents" }, children: children({ header: boundHeader, contentView: boundContent, toggle: boundToggle, message, messages, isRunning }) }); return /* @__PURE__ */ jsxs("div", { className: twMerge("cpk:my-1", className), "data-message-id": message.id, ...props, children: [boundHeader, boundToggle] }); } (function(_CopilotChatReasoningMessage) { _CopilotChatReasoningMessage.Header = ({ isOpen, label = "Thoughts", hasContent, isStreaming, className, children: headerChildren, ...headerProps }) => { const isExpandable = !!hasContent; return /* @__PURE__ */ jsxs("button", { type: "button", className: twMerge("cpk:inline-flex cpk:items-center cpk:gap-1 cpk:py-1 cpk:text-sm cpk:text-muted-foreground cpk:transition-colors cpk:select-none", isExpandable ? "cpk:hover:text-foreground cpk:cursor-pointer" : "cpk:cursor-default", className), "aria-expanded": isExpandable ? isOpen : void 0, ...headerProps, children: [ /* @__PURE__ */ jsx("span", { className: "cpk:font-medium", children: label }), isStreaming && !hasContent && /* @__PURE__ */ jsx("span", { className: "cpk:inline-flex cpk:items-center cpk:ml-1", children: /* @__PURE__ */ jsx("span", { className: "cpk:w-1.5 cpk:h-1.5 cpk:rounded-full cpk:bg-muted-foreground cpk:animate-pulse" }) }), headerChildren, isExpandable && /* @__PURE__ */ jsx(ChevronRight, { className: twMerge("cpk:size-3.5 cpk:shrink-0 cpk:transition-transform cpk:duration-200", isOpen && "cpk:rotate-90") }) ] }); }; _CopilotChatReasoningMessage.Content = ({ isStreaming, hasContent, className, children: contentChildren, ...contentProps }) => { if (!hasContent && !isStreaming) return null; return /* @__PURE__ */ jsx("div", { className: twMerge("cpk:pb-2 cpk:pt-1", className), ...contentProps, children: /* @__PURE__ */ jsxs("div", { className: "cpk:text-sm cpk:text-muted-foreground", children: [/* @__PURE__ */ jsx(Streamdown, { children: typeof contentChildren === "string" ? contentChildren : "" }), isStreaming && hasContent && /* @__PURE__ */ jsx("span", { className: "cpk:inline-flex cpk:items-center cpk:ml-1 cpk:align-middle", children: /* @__PURE__ */ jsx("span", { className: "cpk:w-2 cpk:h-2 cpk:rounded-full cpk:bg-muted-foreground cpk:animate-pulse-cursor" }) })] }) }); }; _CopilotChatReasoningMessage.Toggle = ({ isOpen, className, children: toggleChildren, ...toggleProps }) => { return /* @__PURE__ */ jsx("div", { className: twMerge("cpk:grid cpk:transition-[grid-template-rows] cpk:duration-200 cpk:ease-in-out", className), style: { gridTemplateRows: isOpen ? "1fr" : "0fr" }, ...toggleProps, children: /* @__PURE__ */ jsx("div", { className: "cpk:overflow-hidden", children: toggleChildren }) }); }; })(CopilotChatReasoningMessage || (CopilotChatReasoningMessage = {})); CopilotChatReasoningMessage.Header.displayName = "CopilotChatReasoningMessage.Header"; CopilotChatReasoningMessage.Content.displayName = "CopilotChatReasoningMessage.Content"; CopilotChatReasoningMessage.Toggle.displayName = "CopilotChatReasoningMessage.Toggle"; var CopilotChatReasoningMessage_default = CopilotChatReasoningMessage; //#endregion export { CopilotChatReasoningMessage_default as default }; //# sourceMappingURL=CopilotChatReasoningMessage.mjs.map