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