UNPKG

@liveblocks/react-ui

Version:

A set of React pre-built components for the Liveblocks products. Liveblocks is the all-in-one toolkit to build collaborative products like Figma, Notion, and more.

179 lines (175 loc) 6.86 kB
'use strict'; var jsxRuntime = require('react/jsx-runtime'); var react$1 = require('@liveblocks/react'); var _private = require('@liveblocks/react/_private'); var react = require('react'); var ArrowDown = require('../icons/ArrowDown.cjs'); var Spinner = require('../icons/Spinner.cjs'); var overrides = require('../overrides.cjs'); var cn = require('../utils/cn.cjs'); var useVisible = require('../utils/use-visible.cjs'); var AiChatAssistantMessage = require('./internal/AiChatAssistantMessage.cjs'); var AiChatComposer = require('./internal/AiChatComposer.cjs'); var AiChatUserMessage = require('./internal/AiChatUserMessage.cjs'); const MIN_DISTANCE_BOTTOM_SCROLL_INDICATOR = 50; const defaultComponents = { Empty: () => null, Loading: () => /* @__PURE__ */ jsxRuntime.jsx("div", { className: "lb-loading lb-ai-chat-loading", children: /* @__PURE__ */ jsxRuntime.jsx(Spinner.SpinnerIcon, {}) }) }; const AiChat = react.forwardRef( ({ chatId, copilotId, autoFocus, overrides: overrides$1, knowledge, tools = {}, layout = "inset", components, className, ...props }, forwardedRef) => { const { messages, isLoading, error } = react$1.useAiChatMessages(chatId); const $ = overrides.useOverrides(overrides$1); const Empty = components?.Empty ?? defaultComponents.Empty; const Loading = components?.Loading ?? defaultComponents.Loading; const containerRef = react.useRef(null); const containerBottomRef = react.useRef(null); const isScrollIndicatorEnabled = !isLoading && !error; const isScrollAtBottom = useVisible.useVisible(containerBottomRef, { enabled: isScrollIndicatorEnabled, root: containerRef, rootMargin: MIN_DISTANCE_BOTTOM_SCROLL_INDICATOR, initialValue: null }); const isScrollIndicatorVisible = isScrollIndicatorEnabled && isScrollAtBottom !== null ? !isScrollAtBottom : false; const [lastSentMessageId, setLastSentMessageId] = react.useState(null); react.useImperativeHandle( forwardedRef, () => containerRef.current, [] ); const scrollToBottomCallbackRef = react.useRef(void 0); if (scrollToBottomCallbackRef.current === void 0) { scrollToBottomCallbackRef.current = function(behavior) { const container = containerRef.current; if (container === null) return; container.scrollTo({ top: container.scrollHeight, behavior }); }; } const scrollToBottom = scrollToBottomCallbackRef.current; return /* @__PURE__ */ jsxRuntime.jsxs("div", { ref: containerRef, ...props, className: cn.cn( "lb-root lb-ai-chat", layout === "compact" ? "lb-ai-chat:layout-compact" : "lb-ai-chat:layout-inset", className ), children: [ knowledge ? knowledge.map((source, index) => /* @__PURE__ */ jsxRuntime.jsx(react$1.RegisterAiKnowledge, { description: source.description, value: source.value }, index)) : null, Object.entries(tools).map(([name, tool]) => /* @__PURE__ */ jsxRuntime.jsx(react$1.RegisterAiTool, { chatId, name, tool }, name)), /* @__PURE__ */ jsxRuntime.jsx("div", { className: "lb-ai-chat-content", children: isLoading ? /* @__PURE__ */ jsxRuntime.jsx(Loading, {}) : error !== void 0 ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "lb-error lb-ai-chat-error", children: $.AI_CHAT_MESSAGES_ERROR(error) }) : messages.length === 0 ? /* @__PURE__ */ jsxRuntime.jsx(Empty, { chatId, copilotId }) : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [ /* @__PURE__ */ jsxRuntime.jsx(AutoScrollHandler, { lastSentMessageId, scrollToBottom }), /* @__PURE__ */ jsxRuntime.jsx("div", { className: "lb-ai-chat-messages", children: messages.map((message) => { if (message.role === "user") { return /* @__PURE__ */ jsxRuntime.jsx(AiChatUserMessage.AiChatUserMessage, { message, overrides: overrides$1 }, message.id); } else if (message.role === "assistant") { return /* @__PURE__ */ jsxRuntime.jsx(AiChatAssistantMessage.AiChatAssistantMessage, { message, overrides: overrides$1, components }, message.id); } else { return null; } }) }) ] }) }), /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "lb-ai-chat-footer", children: [ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "lb-ai-chat-footer-actions", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "lb-root lb-elevation lb-elevation-moderate lb-ai-chat-scroll-indicator", "data-visible": isScrollIndicatorVisible ? "" : void 0, children: /* @__PURE__ */ jsxRuntime.jsx("button", { className: "lb-ai-chat-scroll-indicator-button", tabIndex: isScrollIndicatorVisible ? 0 : -1, "aria-hidden": !isScrollIndicatorVisible, onClick: () => scrollToBottom("smooth"), children: /* @__PURE__ */ jsxRuntime.jsx("span", { className: "lb-icon-container", children: /* @__PURE__ */ jsxRuntime.jsx(ArrowDown.ArrowDownIcon, {}) }) }) }) }), /* @__PURE__ */ jsxRuntime.jsx(AiChatComposer.AiChatComposer, { chatId, copilotId, overrides: overrides$1, autoFocus, onUserMessageCreate: ({ id }) => setLastSentMessageId(id), className: layout === "inset" ? "lb-elevation lb-elevation-moderate" : void 0 }, chatId) ] }), /* @__PURE__ */ jsxRuntime.jsx("div", { ref: containerBottomRef, style: { position: "sticky", height: 0 }, "aria-hidden": true, "data-scroll-at-bottom": isScrollAtBottom ? "" : void 0 }) ] }); } ); function AutoScrollHandler({ lastSentMessageId, scrollToBottom }) { _private.useLayoutEffect(() => { scrollToBottom("instant"); }, [scrollToBottom]); react.useEffect(() => { scrollToBottom("smooth"); }, [lastSentMessageId, scrollToBottom]); return null; } exports.AiChat = AiChat; //# sourceMappingURL=AiChat.cjs.map