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.

319 lines (315 loc) 11.5 kB
'use strict'; var jsxRuntime = require('react/jsx-runtime'); var react = require('@liveblocks/react'); var react$1 = require('react'); var components = require('../../components.cjs'); var ChevronRight = require('../../icons/ChevronRight.cjs'); var Warning = require('../../icons/Warning.cjs'); var overrides = require('../../overrides.cjs'); var index = require('../../primitives/AiMessage/index.cjs'); var toolInvocation = require('../../primitives/AiMessage/tool-invocation.cjs'); var index$1 = require('../../primitives/Collapsible/index.cjs'); var cn = require('../../utils/cn.cjs'); var ErrorBoundary = require('../../utils/ErrorBoundary.cjs'); var Favicon = require('./Favicon.cjs'); var Prose = require('./Prose.cjs'); function getUrlDomain(url) { return new URL(url).hostname; } function AiChatSource({ source, components: components$1, className, ...props }) { const { Anchor } = components.useComponents(components$1); const { metadata } = react.useUrlMetadata(source.url); const label = react$1.useMemo(() => { return source.title ?? metadata?.title ?? getUrlDomain(source.url); }, [source.title, source.url, metadata?.title]); return /* @__PURE__ */ jsxRuntime.jsxs( Anchor, { href: source.url, target: "_blank", rel: "noopener noreferrer", className: cn.cn("lb-ai-chat-source", className), ...props, children: [ /* @__PURE__ */ jsxRuntime.jsx(Favicon.Favicon, { url: source.url, className: "lb-ai-chat-source-favicon" }), /* @__PURE__ */ jsxRuntime.jsx("span", { className: "lb-ai-chat-source-label", children: label }) ] } ); } function AiChatSources({ sources: allSources, maxSources, components, className, ...props }) { const $ = overrides.useOverrides(); const [isOpen, setOpen] = react$1.useState(false); const visibleSources = typeof maxSources === "number" && !isOpen ? allSources.slice(0, maxSources) : allSources; const handleToggle = react$1.useCallback(() => { setOpen((isOpen2) => !isOpen2); }, []); return /* @__PURE__ */ jsxRuntime.jsxs("ol", { className: cn.cn("lb-ai-chat-sources", className), ...props, children: [ visibleSources.map((source, index) => { return /* @__PURE__ */ jsxRuntime.jsx("li", { children: /* @__PURE__ */ jsxRuntime.jsx(AiChatSource, { source, components }) }, `${index}-${source.url}`); }), visibleSources.length !== allSources.length ? /* @__PURE__ */ jsxRuntime.jsx("li", { children: /* @__PURE__ */ jsxRuntime.jsx("button", { className: "lb-ai-chat-sources-more", onClick: handleToggle, children: /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "lb-ai-chat-sources-more-label", children: [ "+ ", $.LIST_REMAINING(allSources.length - visibleSources.length) ] }) }) }) : null ] }); } const AiChatAssistantMessage = react$1.memo( react$1.forwardRef( ({ message, className, overrides: overrides$1, components, showReasoning, showRetrievals, showSources, ...props }, forwardedRef) => { const $ = overrides.useOverrides(overrides$1); let children = null; const messageContent = /* @__PURE__ */ jsxRuntime.jsx( AssistantMessageContent, { message, components, showReasoning, showRetrievals, showSources } ); if (message.deletedAt !== void 0) { children = /* @__PURE__ */ jsxRuntime.jsx("div", { className: "lb-ai-chat-message-deleted", children: $.AI_CHAT_MESSAGE_DELETED }); } else if (message.status === "generating" || message.status === "awaiting-tool") { if (message.contentSoFar.length === 0) { children = /* @__PURE__ */ jsxRuntime.jsx("div", { className: "lb-ai-chat-message-thinking lb-ai-chat-pending", children: $.AI_CHAT_MESSAGE_THINKING }); } else { children = messageContent; } } else if (message.status === "completed") { children = messageContent; } else if (message.status === "failed") { if (message.errorReason === "Aborted by user") { children = messageContent; } else { children = /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [ messageContent, /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "lb-ai-chat-message-error", children: [ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "lb-icon-container", children: /* @__PURE__ */ jsxRuntime.jsx(Warning.WarningIcon, {}) }), message.errorReason ] }) ] }); } } return /* @__PURE__ */ jsxRuntime.jsx( "div", { className: cn.cn( "lb-ai-chat-message lb-ai-chat-assistant-message", className ), ...props, ref: forwardedRef, children: /* @__PURE__ */ jsxRuntime.jsx(overrides.OverridesProvider, { overrides: overrides$1, children }) } ); } ) ); const NoopComponent = () => null; function AssistantMessageContent({ message, components, showReasoning = true, showRetrievals = true, showSources = true }) { const componentsRef = react$1.useRef(components); let showKnowledgeRetrievals = typeof showRetrievals === "object" ? showRetrievals.knowledge : showRetrievals; let showWebRetrievals = typeof showRetrievals === "object" ? showRetrievals.web : showRetrievals; showKnowledgeRetrievals ??= true; showWebRetrievals ??= true; const BoundTextPart = react$1.useMemo( () => (props) => /* @__PURE__ */ jsxRuntime.jsx(TextPart, { ...props, components: componentsRef.current }), [] ); const BoundReasoningPart = react$1.useMemo( () => (props) => { if (!showReasoning || showReasoning === "during" && !props.isStreaming) { return null; } return /* @__PURE__ */ jsxRuntime.jsx(ReasoningPart, { ...props, components: componentsRef.current }); }, [showReasoning] ); const BoundRetrievalPart = react$1.useMemo( () => (props) => { if (props.part.kind === "knowledge") { if (!showKnowledgeRetrievals || showKnowledgeRetrievals === "during" && !props.isStreaming) { return null; } } else if (props.part.kind === "web") { if (!showWebRetrievals || showWebRetrievals === "during" && !props.isStreaming) { return null; } } return /* @__PURE__ */ jsxRuntime.jsx(RetrievalPart, { ...props }); }, [showKnowledgeRetrievals, showWebRetrievals] ); return /* @__PURE__ */ jsxRuntime.jsx( index.Content, { message, components: { TextPart: BoundTextPart, ReasoningPart: BoundReasoningPart, RetrievalPart: BoundRetrievalPart, SourcesPart: showSources ? SourcesPart : NoopComponent, ToolInvocationPart }, className: "lb-ai-chat-message-content" } ); } function TextPart({ part, components, isStreaming }) { return /* @__PURE__ */ jsxRuntime.jsx( Prose.Prose, { content: part.text, className: "lb-ai-chat-message-text", components, partial: isStreaming } ); } function ReasoningPart({ part, isStreaming, components }) { const [isOpen, setIsOpen] = react$1.useState(isStreaming); const $ = overrides.useOverrides(); react$1.useEffect(() => { if (!isStreaming) { setIsOpen(false); } }, [isStreaming]); return /* @__PURE__ */ jsxRuntime.jsxs( index$1.Root, { className: "lb-collapsible lb-ai-chat-message-reasoning", open: isOpen, onOpenChange: setIsOpen, children: [ /* @__PURE__ */ jsxRuntime.jsxs( index$1.Trigger, { className: cn.cn( "lb-collapsible-trigger", isStreaming && "lb-ai-chat-pending" ), children: [ $.AI_CHAT_MESSAGE_REASONING(isStreaming, part), /* @__PURE__ */ jsxRuntime.jsx("span", { className: "lb-collapsible-chevron lb-icon-container", children: /* @__PURE__ */ jsxRuntime.jsx(ChevronRight.ChevronRightIcon, {}) }) ] } ), /* @__PURE__ */ jsxRuntime.jsx(index$1.Content, { className: "lb-collapsible-content", children: /* @__PURE__ */ jsxRuntime.jsx( Prose.Prose, { content: part.text, partial: isStreaming, components } ) }) ] } ); } function RetrievalPartFavicons({ sources, maxSources }) { if (!sources) { return null; } const visibleSources = typeof maxSources === "number" ? sources.slice(0, maxSources) : sources; return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "lb-ai-chat-message-retrieval-favicons", children: visibleSources.map((source) => /* @__PURE__ */ jsxRuntime.jsx(Favicon.Favicon, { url: source.url }, source.url)) }); } function RetrievalPart({ part, isStreaming }) { const $ = overrides.useOverrides(); let content = null; if (part.kind === "web" && part.sources && part.sources.length > 0) { content = /* @__PURE__ */ jsxRuntime.jsx( AiChatSources, { className: "lb-ai-chat-message-retrieval-sources", sources: part.sources } ); } return /* @__PURE__ */ jsxRuntime.jsxs( index$1.Root, { className: "lb-collapsible lb-ai-chat-message-retrieval", defaultOpen: false, disabled: !content, children: [ /* @__PURE__ */ jsxRuntime.jsxs( index$1.Trigger, { className: cn.cn( "lb-collapsible-trigger", isStreaming && "lb-ai-chat-pending" ), children: [ $.AI_CHAT_MESSAGE_RETRIEVAL(isStreaming, part), part.kind === "web" ? /* @__PURE__ */ jsxRuntime.jsx(RetrievalPartFavicons, { sources: part.sources, maxSources: 3 }) : null, content ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "lb-collapsible-chevron lb-icon-container", children: /* @__PURE__ */ jsxRuntime.jsx(ChevronRight.ChevronRightIcon, {}) }) : null ] } ), content ? /* @__PURE__ */ jsxRuntime.jsx(index$1.Content, { className: "lb-collapsible-content", children: content }) : null ] } ); } function ToolInvocationPart({ part, message }) { return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "lb-ai-chat-message-tool-invocation", children: /* @__PURE__ */ jsxRuntime.jsx( ErrorBoundary.ErrorBoundary, { fallback: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "lb-ai-chat-message-error", children: [ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "lb-icon-container", children: /* @__PURE__ */ jsxRuntime.jsx(Warning.WarningIcon, {}) }), /* @__PURE__ */ jsxRuntime.jsxs("p", { children: [ "Failed to render tool call result for ", /* @__PURE__ */ jsxRuntime.jsx("code", { children: part.name }), ". See console for details." ] }) ] }), children: /* @__PURE__ */ jsxRuntime.jsx(toolInvocation.AiMessageToolInvocation, { part, message }) } ) }); } function SourcesPart({ part }) { return /* @__PURE__ */ jsxRuntime.jsx( AiChatSources, { className: "lb-ai-chat-message-sources", sources: part.sources, maxSources: 5 } ); } exports.AiChatAssistantMessage = AiChatAssistantMessage; //# sourceMappingURL=AiChatAssistantMessage.cjs.map