@liveblocks/react-ui 3.8.0-next4 → 3.8.0-tiptap1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -51,37 +51,19 @@ const AiChatMessages = react.forwardRef(
51
51
  }
52
52
  const trailingSpacer = trailingSpacerRef.current;
53
53
  const bottomTrailingMarker = bottomTrailingMarkerRef.current;
54
- let containerHeight = void 0;
55
- let footerHeight = void 0;
56
- let messagesHeight = void 0;
54
+ let containerHeight = null;
55
+ let footerHeight = null;
56
+ let messagesHeight = null;
57
57
  const resetTrailingSpace = () => {
58
58
  trailingSpacer?.style.removeProperty("height");
59
59
  bottomTrailingMarker?.style.removeProperty("top");
60
60
  };
61
- const updateTrailingSpace = (updatedContainerHeight, updatedFooterHeight, updatedMessagesHeight) => {
61
+ const resizeObserver = new ResizeObserver((entries) => {
62
62
  if (!trailingSpacer || !bottomTrailingMarker) {
63
63
  return;
64
64
  }
65
65
  const lastMessage = messages2.lastElementChild;
66
66
  const penultimateMessage = lastMessage?.previousElementSibling;
67
- if (updatedContainerHeight === void 0) {
68
- updatedContainerHeight = container.getBoundingClientRect().height;
69
- }
70
- if (updatedFooterHeight === void 0) {
71
- updatedFooterHeight = footer.getBoundingClientRect().height;
72
- }
73
- if (updatedMessagesHeight === void 0) {
74
- updatedMessagesHeight = messages2.getBoundingClientRect().height;
75
- }
76
- if (updatedContainerHeight === containerHeight && updatedFooterHeight === footerHeight && updatedMessagesHeight === messagesHeight) {
77
- if (!lastMessage || !penultimateMessage || container.scrollHeight === container.clientHeight) {
78
- resetTrailingSpace();
79
- }
80
- return;
81
- }
82
- containerHeight = updatedContainerHeight;
83
- footerHeight = updatedFooterHeight;
84
- messagesHeight = updatedMessagesHeight;
85
67
  if (!lastMessage || !penultimateMessage) {
86
68
  resetTrailingSpace();
87
69
  return;
@@ -90,41 +72,43 @@ const AiChatMessages = react.forwardRef(
90
72
  resetTrailingSpace();
91
73
  return;
92
74
  }
93
- const penultimateMessageScrollMarginTop = Number.parseFloat(
94
- getComputedStyle(penultimateMessage).scrollMarginTop
95
- );
96
- const messagesRect = messages2.getBoundingClientRect();
97
- const penultimateMessageRect = penultimateMessage.getBoundingClientRect();
98
- const heightFromPenultimateMessageTopToMessagesListBottom = messagesRect.bottom - penultimateMessageRect.top;
99
- const differenceHeight = penultimateMessageScrollMarginTop + heightFromPenultimateMessageTopToMessagesListBottom + (footerHeight ?? 0);
100
- const trailingSpace = Math.max(containerHeight - differenceHeight, 0);
101
- trailingSpacer.style.height = `${trailingSpace}px`;
102
- bottomTrailingMarker.style.top = `${-trailingSpace}px`;
103
- };
104
- const resizeObserver = new ResizeObserver((entries) => {
105
75
  let updatedContainerHeight = containerHeight;
106
76
  let updatedFooterHeight = footerHeight;
107
77
  let updatedMessagesHeight = messagesHeight;
108
78
  for (const entry of entries) {
109
79
  const entryHeight = entry.borderBoxSize?.[0]?.blockSize ?? entry.contentRect.height;
110
80
  if (entry.target === container) {
111
- updatedContainerHeight = entryHeight;
81
+ updatedContainerHeight = entryHeight ?? null;
112
82
  } else if (entry.target === footer) {
113
- updatedFooterHeight = entryHeight;
83
+ updatedFooterHeight = entryHeight ?? null;
114
84
  } else if (entry.target === messages2) {
115
- updatedMessagesHeight = entryHeight;
85
+ updatedMessagesHeight = entryHeight ?? null;
116
86
  }
117
87
  }
118
- updateTrailingSpace(
119
- updatedContainerHeight,
120
- updatedFooterHeight,
121
- updatedMessagesHeight
88
+ if (updatedContainerHeight === null || updatedFooterHeight === null || updatedMessagesHeight === null) {
89
+ resetTrailingSpace();
90
+ return;
91
+ }
92
+ if (updatedContainerHeight === containerHeight && updatedFooterHeight === footerHeight && updatedMessagesHeight === messagesHeight) {
93
+ return;
94
+ }
95
+ containerHeight = updatedContainerHeight;
96
+ footerHeight = updatedFooterHeight;
97
+ messagesHeight = updatedMessagesHeight;
98
+ const penultimateMessageScrollMarginTop = Number.parseFloat(
99
+ getComputedStyle(penultimateMessage).scrollMarginTop
122
100
  );
101
+ const messagesRect = messages2.getBoundingClientRect();
102
+ const penultimateMessageRect = penultimateMessage.getBoundingClientRect();
103
+ const heightFromPenultimateMessageTopToMessagesListBottom = messagesRect.bottom - penultimateMessageRect.top;
104
+ const differenceHeight = penultimateMessageScrollMarginTop + heightFromPenultimateMessageTopToMessagesListBottom + (footerHeight ?? 0);
105
+ const trailingSpace = Math.max(containerHeight - differenceHeight, 0);
106
+ trailingSpacer.style.height = `${trailingSpace}px`;
107
+ bottomTrailingMarker.style.top = `${-trailingSpace}px`;
123
108
  });
124
109
  resizeObserver.observe(container);
125
110
  resizeObserver.observe(footer);
126
111
  resizeObserver.observe(messages2);
127
- requestAnimationFrame(() => updateTrailingSpace());
128
112
  return () => {
129
113
  resizeObserver.disconnect();
130
114
  resetTrailingSpace();
@@ -224,12 +208,12 @@ const AiChat = react.forwardRef(
224
208
  const scrollToBottom = _private.useLatest(
225
209
  (behavior, includeTrailingSpace = false) => {
226
210
  if (includeTrailingSpace) {
227
- setTimeout(() => {
211
+ requestAnimationFrame(() => {
228
212
  bottomMarkerRef.current?.scrollIntoView({
229
213
  behavior,
230
214
  block: "end"
231
215
  });
232
- }, 0);
216
+ });
233
217
  } else {
234
218
  bottomTrailingMarkerRef.current?.scrollIntoView({
235
219
  behavior,
@@ -280,8 +264,7 @@ const AiChat = react.forwardRef(
280
264
  ref: trailingSpacerRef,
281
265
  "data-trailing-spacer": "",
282
266
  style: {
283
- pointerEvents: "none",
284
- height: 0
267
+ pointerEvents: "none"
285
268
  },
286
269
  "aria-hidden": true
287
270
  })
@@ -1 +1 @@
1
- {"version":3,"file":"AiChat.cjs","sources":["../../src/components/AiChat.tsx"],"sourcesContent":["import type {\n AiKnowledgeSource,\n AiOpaqueToolDefinition,\n CopilotId,\n MessageId,\n} from \"@liveblocks/core\";\nimport { RegisterAiTool, useAiChatMessages } from \"@liveblocks/react\";\nimport { useLatest } from \"@liveblocks/react/_private\";\nimport {\n type ComponentProps,\n type ComponentType,\n forwardRef,\n type MutableRefObject,\n useEffect,\n useImperativeHandle,\n useRef,\n useState,\n} from \"react\";\n\nimport type { GlobalComponents } from \"../components\";\nimport { ArrowDownIcon } from \"../icons/ArrowDown\";\nimport { SpinnerIcon } from \"../icons/Spinner\";\nimport {\n type AiChatMessageOverrides,\n type AiChatOverrides,\n type AiComposerOverrides,\n type GlobalOverrides,\n useOverrides,\n} from \"../overrides\";\nimport type { MarkdownComponents } from \"../primitives/Markdown\";\nimport { cn } from \"../utils/cn\";\nimport { useIntersectionCallback } from \"../utils/use-visible\";\nimport { AiChatAssistantMessage } from \"./internal/AiChatAssistantMessage\";\nimport { AiChatUserMessage } from \"./internal/AiChatUserMessage\";\nimport { AiComposer, type AiComposerProps } from \"./internal/AiComposer\";\n\n/**\n * The minimum number of pixels from the bottom of the scrollable area\n * before showing the scroll to bottom indicator.\n */\nconst MIN_DISTANCE_BOTTOM_SCROLL_INDICATOR = 60;\n\nexport type AiChatComponentsEmptyProps = {\n /**\n * The chat ID provided to the `AiChat` component.\n */\n chatId: string;\n\n /**\n * The copilot ID provided to the `AiChat` component.\n */\n copilotId?: string;\n};\n\nexport type AiChatComponentsLoadingProps = Record<string, never>;\n\nexport type AiChatComponents = {\n /**\n * The component used to render the empty state of the chat.\n */\n Empty: ComponentType<AiChatComponentsEmptyProps>;\n\n /**\n * The component used to render the loading state of the chat.\n */\n Loading: ComponentType<AiChatComponentsLoadingProps>;\n\n /**\n * The components used to render Markdown content.\n */\n markdown?: Partial<MarkdownComponents>;\n};\n\nexport interface AiChatProps extends ComponentProps<\"div\"> {\n /**\n * The ID of the chat the composer belongs to.\n */\n chatId: string;\n\n /**\n * Whether to focus the chat composer on mount.\n */\n autoFocus?: boolean;\n\n /**\n * The ID of the copilot to use to send the message.\n */\n copilotId?: string;\n\n /**\n * The contextual knowledge to include in the chat. May be used by the\n * assistant when generating responses. In addition to the knowledge passed\n * in via this prop, the AiChat instance will also have access to any\n * globally registered knowledge via <RegisterAiKnowledge />.\n */\n knowledge?: AiKnowledgeSource[];\n\n /**\n * Tool definitions to make available within this chat. May be used by the assistant when generating responses.\n */\n tools?: Record<string, AiOpaqueToolDefinition>;\n\n /**\n * The event handler called when the composer is submitted.\n */\n onComposerSubmit?: AiComposerProps[\"onComposerSubmit\"];\n\n /**\n * The layout of the chat and its composer.\n */\n layout?: \"inset\" | \"compact\";\n\n /**\n * Override the component's strings.\n */\n overrides?: Partial<\n GlobalOverrides &\n AiComposerOverrides &\n AiChatMessageOverrides &\n AiChatOverrides\n >;\n\n /**\n * Override the component's components.\n */\n components?: Partial<GlobalComponents & AiChatComponents>;\n}\n\ninterface AiChatMessagesProps extends ComponentProps<\"div\"> {\n messages: NonNullable<ReturnType<typeof useAiChatMessages>[\"messages\"]>;\n overrides: AiChatProps[\"overrides\"];\n components: AiChatProps[\"components\"];\n lastSentMessageId: MessageId | null;\n scrollToBottom: MutableRefObject<\n (behavior: \"instant\" | \"smooth\", includeTrailingSpace?: boolean) => void\n >;\n onScrollAtBottomChange: MutableRefObject<\n (isScrollAtBottom: boolean | null) => void\n >;\n containerRef: MutableRefObject<HTMLDivElement | null>;\n footerRef: MutableRefObject<HTMLDivElement | null>;\n messagesRef: MutableRefObject<HTMLDivElement | null>;\n bottomTrailingMarkerRef: MutableRefObject<HTMLDivElement | null>;\n trailingSpacerRef: MutableRefObject<HTMLDivElement | null>;\n}\n\nconst defaultComponents: AiChatComponents = {\n Empty: () => null,\n Loading: () => (\n <div className=\"lb-loading lb-ai-chat-loading\">\n <SpinnerIcon />\n </div>\n ),\n};\n\nconst AiChatMessages = forwardRef<HTMLDivElement, AiChatMessagesProps>(\n (\n {\n messages,\n overrides,\n components,\n lastSentMessageId,\n scrollToBottom,\n onScrollAtBottomChange,\n containerRef,\n footerRef,\n messagesRef,\n bottomTrailingMarkerRef,\n trailingSpacerRef,\n className,\n ...props\n },\n forwardedRef\n ) => {\n const hasLastSentMessage = lastSentMessageId !== null;\n\n /**\n * Every time the container, footer, or messages list change size,\n * we calculate the trailing space that would allow the penultimate\n * message to be at the top of the viewport, and apply it.\n *\n * ┌─────────────────────────────────────────┐▲ A = The `scroll-margin-top`\n * │ ┌─────────────────────────┐ │▼▲ value of the penultimate message\n * │ │ The penultimate message │ │ │\n * │ └─────────────────────────┘ │ │ B = The height from the top of\n * │ │ │ the penultimate message to the\n * │ ┌─────────────────────────┐ │ │ bottom of the messages list,\n * │ │ The last message │ │ │ including the messages' heights,\n * │ └─────────────────────────┘ │ │ and any padding, gap, etc\n * │ │ │\n * ├ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┤▲▼\n * │ ││ The trailing space needed to\n * │ = container height - (A + B + C) ││ allow the penultimate message\n * │ ││ to be at the top of the viewport\n * ├ ┬─────────────────────────────────────┬ ┤▼▲\n * │ │ │ │ │\n * │ │ │ │ │ C = The footer's height,\n * │ │ │ │ │ including any padding\n * │ └─────────────────────────────────────┘ │ │\n * └─────────────────────────────────────────┘ ▼\n */\n useEffect(\n () => {\n if (!hasLastSentMessage) {\n return;\n }\n\n const container = containerRef.current;\n const footer = footerRef.current;\n const messages = messagesRef.current;\n\n if (!container || !footer || !messages) {\n return;\n }\n\n const trailingSpacer = trailingSpacerRef.current;\n const bottomTrailingMarker = bottomTrailingMarkerRef.current;\n\n let containerHeight: number | undefined = undefined;\n let footerHeight: number | undefined = undefined;\n let messagesHeight: number | undefined = undefined;\n\n const resetTrailingSpace = () => {\n trailingSpacer?.style.removeProperty(\"height\");\n bottomTrailingMarker?.style.removeProperty(\"top\");\n };\n\n const updateTrailingSpace = (\n updatedContainerHeight?: number,\n updatedFooterHeight?: number,\n updatedMessagesHeight?: number\n ) => {\n if (!trailingSpacer || !bottomTrailingMarker) {\n return;\n }\n\n const lastMessage = messages.lastElementChild;\n const penultimateMessage = lastMessage?.previousElementSibling;\n\n if (updatedContainerHeight === undefined) {\n updatedContainerHeight = container.getBoundingClientRect().height;\n }\n\n if (updatedFooterHeight === undefined) {\n updatedFooterHeight = footer.getBoundingClientRect().height;\n }\n\n if (updatedMessagesHeight === undefined) {\n updatedMessagesHeight = messages.getBoundingClientRect().height;\n }\n\n // If the heights haven't changed, there's no need to update the trailing space.\n if (\n updatedContainerHeight === containerHeight &&\n updatedFooterHeight === footerHeight &&\n updatedMessagesHeight === messagesHeight\n ) {\n if (\n !lastMessage ||\n !penultimateMessage ||\n container.scrollHeight === container.clientHeight\n ) {\n resetTrailingSpace();\n }\n return;\n }\n\n // Now that we have compared them, we can update the heights.\n containerHeight = updatedContainerHeight;\n footerHeight = updatedFooterHeight;\n messagesHeight = updatedMessagesHeight;\n\n // If there's no last pair of messages, there's no need for any trailing space.\n if (!lastMessage || !penultimateMessage) {\n resetTrailingSpace();\n return;\n }\n\n // If the container isn't scrollable, there's no need for trailing space.\n if (container.scrollHeight === container.clientHeight) {\n resetTrailingSpace();\n return;\n }\n\n // A\n const penultimateMessageScrollMarginTop = Number.parseFloat(\n getComputedStyle(penultimateMessage as HTMLElement).scrollMarginTop\n );\n\n // B\n const messagesRect = messages.getBoundingClientRect();\n const penultimateMessageRect =\n penultimateMessage.getBoundingClientRect();\n const heightFromPenultimateMessageTopToMessagesListBottom =\n messagesRect.bottom - penultimateMessageRect.top;\n\n // A + B + C\n const differenceHeight =\n penultimateMessageScrollMarginTop +\n heightFromPenultimateMessageTopToMessagesListBottom +\n (footerHeight ?? 0);\n\n // = container height - (A + B + C)\n const trailingSpace = Math.max(containerHeight - differenceHeight, 0);\n\n // Update the trailing space.\n trailingSpacer.style.height = `${trailingSpace}px`;\n\n // Offset what \"the bottom\" is to the \"scroll at the bottom\" detection logic,\n // so that it doesn't include the trailing space.\n bottomTrailingMarker.style.top = `${-trailingSpace}px`;\n };\n\n const resizeObserver = new ResizeObserver((entries) => {\n let updatedContainerHeight: number | undefined = containerHeight;\n let updatedFooterHeight: number | undefined = footerHeight;\n let updatedMessagesHeight: number | undefined = messagesHeight;\n\n for (const entry of entries) {\n const entryHeight =\n entry.borderBoxSize?.[0]?.blockSize ?? entry.contentRect.height;\n\n if (entry.target === container) {\n updatedContainerHeight = entryHeight;\n } else if (entry.target === footer) {\n updatedFooterHeight = entryHeight;\n } else if (entry.target === messages) {\n updatedMessagesHeight = entryHeight;\n }\n }\n\n updateTrailingSpace(\n updatedContainerHeight,\n updatedFooterHeight,\n updatedMessagesHeight\n );\n });\n\n resizeObserver.observe(container);\n resizeObserver.observe(footer);\n resizeObserver.observe(messages);\n\n // Initialize before the first resize.\n requestAnimationFrame(() => updateTrailingSpace());\n\n return () => {\n resizeObserver.disconnect();\n resetTrailingSpace();\n };\n },\n // This effect only uses stable refs.\n [hasLastSentMessage] // eslint-disable-line react-hooks/exhaustive-deps\n );\n\n /**\n * Update the \"scroll at bottom\" state when needed.\n */\n useIntersectionCallback(\n bottomTrailingMarkerRef,\n (isIntersecting) => {\n onScrollAtBottomChange.current(isIntersecting);\n },\n { root: containerRef, rootMargin: MIN_DISTANCE_BOTTOM_SCROLL_INDICATOR }\n );\n\n /**\n * Instantly scroll to the bottom for the initial state.\n */\n useEffect(\n () => {\n scrollToBottom.current(\"instant\");\n },\n // `scrollToBottom` is a stable ref containing the callback.\n [] // eslint-disable-line react-hooks/exhaustive-deps\n );\n\n /**\n * Scroll to new messages when sending them.\n */\n useEffect(\n () => {\n if (lastSentMessageId) {\n scrollToBottom.current(\"smooth\", true);\n }\n },\n // `scrollToBottom` is a stable ref containing the callback.\n [lastSentMessageId] // eslint-disable-line react-hooks/exhaustive-deps\n );\n\n /**\n * Reset the \"scroll at bottom\" state when the component unmounts.\n */\n useEffect(\n () => {\n const onScrollAtBottomChangeCallback = onScrollAtBottomChange.current;\n\n return () => {\n onScrollAtBottomChangeCallback(null);\n };\n },\n // `onScrollAtBottomChange` is a stable ref containing the callback.\n [] // eslint-disable-line react-hooks/exhaustive-deps\n );\n\n return (\n <div\n className={cn(\"lb-ai-chat-messages\", className)}\n ref={forwardedRef}\n {...props}\n >\n {messages.map((message) => {\n if (message.role === \"user\") {\n return (\n <AiChatUserMessage\n key={message.id}\n message={message}\n overrides={overrides}\n components={components}\n />\n );\n } else if (message.role === \"assistant\") {\n return (\n <AiChatAssistantMessage\n key={message.id}\n message={message}\n overrides={overrides}\n components={components}\n />\n );\n } else {\n return null;\n }\n })}\n </div>\n );\n }\n);\n\nexport const AiChat = forwardRef<HTMLDivElement, AiChatProps>(\n (\n {\n chatId,\n copilotId,\n autoFocus,\n overrides,\n knowledge: localKnowledge,\n tools = {},\n onComposerSubmit,\n layout = \"inset\",\n components,\n className,\n ...props\n },\n forwardedRef\n ) => {\n const { messages, isLoading, error } = useAiChatMessages(chatId);\n const [lastSentMessageId, setLastSentMessageId] =\n useState<MessageId | null>(null);\n\n const $ = useOverrides(overrides);\n const Empty = components?.Empty ?? defaultComponents.Empty;\n const Loading = components?.Loading ?? defaultComponents.Loading;\n\n const containerRef = useRef<HTMLDivElement | null>(null);\n const messagesRef = useRef<HTMLDivElement | null>(null);\n const footerRef = useRef<HTMLDivElement | null>(null);\n const bottomMarkerRef = useRef<HTMLDivElement | null>(null);\n const bottomTrailingMarkerRef = useRef<HTMLDivElement | null>(null);\n const trailingSpacerRef = useRef<HTMLDivElement | null>(null);\n\n const [isScrollAtBottom, setScrollAtBottom] = useState<boolean | null>(\n null\n );\n // `useState`'s setter is stable but this is for clarity in the places it's used.\n const onScrollAtBottomChange = useLatest(setScrollAtBottom);\n const isScrollIndicatorVisible =\n messages && isScrollAtBottom !== null ? !isScrollAtBottom : false;\n\n useImperativeHandle<HTMLDivElement | null, HTMLDivElement | null>(\n forwardedRef,\n () => containerRef.current,\n []\n );\n\n const scrollToBottom = useLatest(\n (behavior: \"instant\" | \"smooth\", includeTrailingSpace = false) => {\n if (includeTrailingSpace) {\n // Scroll to the bottom marker to include the trailing space,\n // but wait for the next tick in case the trailing space hasn't\n // been updated yet. (e.g. when sending a new message)\n setTimeout(() => {\n bottomMarkerRef.current?.scrollIntoView({\n behavior,\n block: \"end\",\n });\n }, 0);\n } else {\n // Scroll to the trailing space marker to only scroll to the\n // bottom of the messages, without including the trailing space.\n bottomTrailingMarkerRef.current?.scrollIntoView({\n behavior,\n block: \"end\",\n });\n }\n }\n );\n\n return (\n <div\n ref={containerRef}\n {...props}\n className={cn(\n \"lb-root lb-ai-chat\",\n `lb-ai-chat:layout-${layout}`,\n className\n )}\n >\n {Object.entries(tools).map(([name, tool]) => (\n <RegisterAiTool key={name} chatId={chatId} name={name} tool={tool} />\n ))}\n\n <div className=\"lb-ai-chat-content\">\n {isLoading ? (\n <Loading />\n ) : error !== undefined ? (\n <div className=\"lb-error lb-ai-chat-error\">\n {$.AI_CHAT_MESSAGES_ERROR(error)}\n </div>\n ) : messages.length === 0 ? (\n <Empty chatId={chatId} copilotId={copilotId} />\n ) : (\n <>\n <AiChatMessages\n ref={messagesRef}\n messages={messages}\n overrides={overrides}\n components={components}\n lastSentMessageId={lastSentMessageId}\n scrollToBottom={scrollToBottom}\n onScrollAtBottomChange={onScrollAtBottomChange}\n containerRef={containerRef}\n footerRef={footerRef}\n messagesRef={messagesRef}\n bottomTrailingMarkerRef={bottomTrailingMarkerRef}\n trailingSpacerRef={trailingSpacerRef}\n />\n\n {/**\n * This trailing spacer is used to extend the scrollable area beyond its actual\n * content, to allow messages to appear at the top of the viewport for example.\n */}\n <div\n ref={trailingSpacerRef}\n data-trailing-spacer=\"\"\n style={{\n pointerEvents: \"none\",\n height: 0,\n }}\n aria-hidden\n />\n </>\n )}\n </div>\n\n <div className=\"lb-ai-chat-footer\" ref={footerRef}>\n <div className=\"lb-ai-chat-footer-actions\">\n <div\n className=\"lb-root lb-elevation lb-elevation-moderate lb-ai-chat-scroll-indicator\"\n data-visible={isScrollIndicatorVisible ? \"\" : undefined}\n >\n <button\n className=\"lb-ai-chat-scroll-indicator-button\"\n tabIndex={isScrollIndicatorVisible ? 0 : -1}\n aria-hidden={!isScrollIndicatorVisible}\n onClick={() => scrollToBottom.current(\"smooth\")}\n >\n <span className=\"lb-icon-container\">\n <ArrowDownIcon />\n </span>\n </button>\n </div>\n </div>\n <AiComposer\n key={chatId}\n chatId={chatId}\n copilotId={copilotId as CopilotId}\n overrides={overrides}\n autoFocus={autoFocus}\n knowledge={localKnowledge}\n onComposerSubmit={onComposerSubmit}\n onComposerSubmitted={({ id }) => setLastSentMessageId(id)}\n className={cn(\n \"lb-ai-chat-composer\",\n layout === \"inset\"\n ? \"lb-elevation lb-elevation-moderate\"\n : undefined\n )}\n />\n </div>\n\n {/**\n * This invisible marker is a trick which allows us to use IntersectionObserver to detect when the\n * scrollable area is fully scrolled to the bottom instead of manually tracking the scroll position\n * and having to deal with resizes, etc.\n *\n * It's positioned at the bottom of the scrollable area and reliably only becomes \"visible\" to the\n * IntersectionObserver when the scrollable area is scrolled to the bottom.\n */}\n {messages && messages.length > 0 ? (\n <div\n ref={bottomMarkerRef}\n style={{ position: \"sticky\", height: 0 }}\n aria-hidden\n data-bottom-marker=\"\"\n >\n {/**\n * This inner marker is absolutely offset by the same distance as the trailing space so its\n * visibility means the scrollable area is at the bottom of the messages, not the full bottom.\n */}\n <div\n ref={bottomTrailingMarkerRef}\n style={{\n position: \"absolute\",\n height: 0,\n }}\n data-bottom-trailing-marker=\"\"\n />\n </div>\n ) : null}\n </div>\n );\n }\n);\n"],"names":["jsx","SpinnerIcon","forwardRef","useEffect","messages","useIntersectionCallback","cn","AiChatUserMessage","AiChatAssistantMessage","overrides","useAiChatMessages","useState","useOverrides","useRef","useLatest","useImperativeHandle","jsxs","RegisterAiTool","Fragment","ArrowDownIcon","AiComposer"],"mappings":"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"}
1
+ {"version":3,"file":"AiChat.cjs","sources":["../../src/components/AiChat.tsx"],"sourcesContent":["import type {\n AiKnowledgeSource,\n AiOpaqueToolDefinition,\n CopilotId,\n MessageId,\n} from \"@liveblocks/core\";\nimport { RegisterAiTool, useAiChatMessages } from \"@liveblocks/react\";\nimport { useLatest } from \"@liveblocks/react/_private\";\nimport {\n type ComponentProps,\n type ComponentType,\n forwardRef,\n type MutableRefObject,\n useEffect,\n useImperativeHandle,\n useRef,\n useState,\n} from \"react\";\n\nimport type { GlobalComponents } from \"../components\";\nimport { ArrowDownIcon } from \"../icons/ArrowDown\";\nimport { SpinnerIcon } from \"../icons/Spinner\";\nimport {\n type AiChatMessageOverrides,\n type AiChatOverrides,\n type AiComposerOverrides,\n type GlobalOverrides,\n useOverrides,\n} from \"../overrides\";\nimport type { MarkdownComponents } from \"../primitives/Markdown\";\nimport { cn } from \"../utils/cn\";\nimport { useIntersectionCallback } from \"../utils/use-visible\";\nimport { AiChatAssistantMessage } from \"./internal/AiChatAssistantMessage\";\nimport { AiChatUserMessage } from \"./internal/AiChatUserMessage\";\nimport { AiComposer, type AiComposerProps } from \"./internal/AiComposer\";\n\n/**\n * The minimum number of pixels from the bottom of the scrollable area\n * before showing the scroll to bottom indicator.\n */\nconst MIN_DISTANCE_BOTTOM_SCROLL_INDICATOR = 60;\n\nexport type AiChatComponentsEmptyProps = {\n /**\n * The chat ID provided to the `AiChat` component.\n */\n chatId: string;\n\n /**\n * The copilot ID provided to the `AiChat` component.\n */\n copilotId?: string;\n};\n\nexport type AiChatComponentsLoadingProps = Record<string, never>;\n\nexport type AiChatComponents = {\n /**\n * The component used to render the empty state of the chat.\n */\n Empty: ComponentType<AiChatComponentsEmptyProps>;\n\n /**\n * The component used to render the loading state of the chat.\n */\n Loading: ComponentType<AiChatComponentsLoadingProps>;\n\n /**\n * The components used to render Markdown content.\n */\n markdown?: Partial<MarkdownComponents>;\n};\n\nexport interface AiChatProps extends ComponentProps<\"div\"> {\n /**\n * The ID of the chat the composer belongs to.\n */\n chatId: string;\n\n /**\n * Whether to focus the chat composer on mount.\n */\n autoFocus?: boolean;\n\n /**\n * The ID of the copilot to use to send the message.\n */\n copilotId?: string;\n\n /**\n * The contextual knowledge to include in the chat. May be used by the\n * assistant when generating responses. In addition to the knowledge passed\n * in via this prop, the AiChat instance will also have access to any\n * globally registered knowledge via <RegisterAiKnowledge />.\n */\n knowledge?: AiKnowledgeSource[];\n\n /**\n * Tool definitions to make available within this chat. May be used by the assistant when generating responses.\n */\n tools?: Record<string, AiOpaqueToolDefinition>;\n\n /**\n * The event handler called when the composer is submitted.\n */\n onComposerSubmit?: AiComposerProps[\"onComposerSubmit\"];\n\n /**\n * The layout of the chat and its composer.\n */\n layout?: \"inset\" | \"compact\";\n\n /**\n * Override the component's strings.\n */\n overrides?: Partial<\n GlobalOverrides &\n AiComposerOverrides &\n AiChatMessageOverrides &\n AiChatOverrides\n >;\n\n /**\n * Override the component's components.\n */\n components?: Partial<GlobalComponents & AiChatComponents>;\n}\n\ninterface AiChatMessagesProps extends ComponentProps<\"div\"> {\n messages: NonNullable<ReturnType<typeof useAiChatMessages>[\"messages\"]>;\n overrides: AiChatProps[\"overrides\"];\n components: AiChatProps[\"components\"];\n lastSentMessageId: MessageId | null;\n scrollToBottom: MutableRefObject<\n (behavior: \"instant\" | \"smooth\", includeTrailingSpace?: boolean) => void\n >;\n onScrollAtBottomChange: MutableRefObject<\n (isScrollAtBottom: boolean | null) => void\n >;\n containerRef: MutableRefObject<HTMLDivElement | null>;\n footerRef: MutableRefObject<HTMLDivElement | null>;\n messagesRef: MutableRefObject<HTMLDivElement | null>;\n bottomTrailingMarkerRef: MutableRefObject<HTMLDivElement | null>;\n trailingSpacerRef: MutableRefObject<HTMLDivElement | null>;\n}\n\nconst defaultComponents: AiChatComponents = {\n Empty: () => null,\n Loading: () => (\n <div className=\"lb-loading lb-ai-chat-loading\">\n <SpinnerIcon />\n </div>\n ),\n};\n\nconst AiChatMessages = forwardRef<HTMLDivElement, AiChatMessagesProps>(\n (\n {\n messages,\n overrides,\n components,\n lastSentMessageId,\n scrollToBottom,\n onScrollAtBottomChange,\n containerRef,\n footerRef,\n messagesRef,\n bottomTrailingMarkerRef,\n trailingSpacerRef,\n className,\n ...props\n },\n forwardedRef\n ) => {\n const hasLastSentMessage = lastSentMessageId !== null;\n\n /**\n * Every time the container, footer, or messages list change size,\n * we calculate the trailing space that would allow the penultimate\n * message to be at the top of the viewport, and apply it.\n *\n * ┌─────────────────────────────────────────┐▲ A = The `scroll-margin-top`\n * │ ┌─────────────────────────┐ │▼▲ value of the penultimate message\n * │ │ The penultimate message │ │ │\n * │ └─────────────────────────┘ │ │ B = The height from the top of\n * │ │ │ the penultimate message to the\n * │ ┌─────────────────────────┐ │ │ bottom of the messages list,\n * │ │ The last message │ │ │ including the messages' heights,\n * │ └─────────────────────────┘ │ │ and any padding, gap, etc\n * │ │ │\n * ├ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┤▲▼\n * │ ││ The trailing space needed to\n * │ = container height - (A + B + C) ││ allow the penultimate message\n * │ ││ to be at the top of the viewport\n * ├ ┬─────────────────────────────────────┬ ┤▼▲\n * │ │ │ │ │\n * │ │ │ │ │ C = The footer's height,\n * │ │ │ │ │ including any padding\n * │ └─────────────────────────────────────┘ │ │\n * └─────────────────────────────────────────┘ ▼\n */\n useEffect(\n () => {\n if (!hasLastSentMessage) {\n return;\n }\n\n const container = containerRef.current;\n const footer = footerRef.current;\n const messages = messagesRef.current;\n\n if (!container || !footer || !messages) {\n return;\n }\n\n const trailingSpacer = trailingSpacerRef.current;\n const bottomTrailingMarker = bottomTrailingMarkerRef.current;\n\n let containerHeight: number | null = null;\n let footerHeight: number | null = null;\n let messagesHeight: number | null = null;\n\n const resetTrailingSpace = () => {\n trailingSpacer?.style.removeProperty(\"height\");\n bottomTrailingMarker?.style.removeProperty(\"top\");\n };\n\n const resizeObserver = new ResizeObserver((entries) => {\n if (!trailingSpacer || !bottomTrailingMarker) {\n return;\n }\n\n const lastMessage = messages.lastElementChild;\n const penultimateMessage = lastMessage?.previousElementSibling;\n\n // If there's no last pair of messages, there's no need for any trailing space.\n if (!lastMessage || !penultimateMessage) {\n resetTrailingSpace();\n return;\n }\n\n // If the container's height is based on its content, the container isn't scrollable and there's no need for any trailing space.\n if (container.scrollHeight === container.clientHeight) {\n resetTrailingSpace();\n return;\n }\n\n let updatedContainerHeight: number | null = containerHeight;\n let updatedFooterHeight: number | null = footerHeight;\n let updatedMessagesHeight: number | null = messagesHeight;\n\n for (const entry of entries) {\n const entryHeight =\n entry.borderBoxSize?.[0]?.blockSize ?? entry.contentRect.height;\n\n if (entry.target === container) {\n updatedContainerHeight = entryHeight ?? null;\n } else if (entry.target === footer) {\n updatedFooterHeight = entryHeight ?? null;\n } else if (entry.target === messages) {\n updatedMessagesHeight = entryHeight ?? null;\n }\n }\n\n // If we don't have all the heights, we can't compute the trailing space.\n if (\n updatedContainerHeight === null ||\n updatedFooterHeight === null ||\n updatedMessagesHeight === null\n ) {\n resetTrailingSpace();\n return;\n }\n\n // If none of the heights have changed, we don't need to do anything.\n if (\n updatedContainerHeight === containerHeight &&\n updatedFooterHeight === footerHeight &&\n updatedMessagesHeight === messagesHeight\n ) {\n return;\n }\n\n // Now that we have compared them, we can update the heights.\n containerHeight = updatedContainerHeight;\n footerHeight = updatedFooterHeight;\n messagesHeight = updatedMessagesHeight;\n\n // A\n const penultimateMessageScrollMarginTop = Number.parseFloat(\n getComputedStyle(penultimateMessage as HTMLElement).scrollMarginTop\n );\n\n // B\n const messagesRect = messages.getBoundingClientRect();\n const penultimateMessageRect =\n penultimateMessage.getBoundingClientRect();\n const heightFromPenultimateMessageTopToMessagesListBottom =\n messagesRect.bottom - penultimateMessageRect.top;\n\n // A + B + C\n const differenceHeight =\n penultimateMessageScrollMarginTop +\n heightFromPenultimateMessageTopToMessagesListBottom +\n (footerHeight ?? 0);\n\n // = container height - (A + B + C)\n const trailingSpace = Math.max(containerHeight - differenceHeight, 0);\n\n // Update the trailing space.\n trailingSpacer.style.height = `${trailingSpace}px`;\n\n // Offset what \"the bottom\" is to the \"scroll at the bottom\" detection logic,\n // so that it doesn't include the trailing space.\n bottomTrailingMarker.style.top = `${-trailingSpace}px`;\n });\n\n resizeObserver.observe(container);\n resizeObserver.observe(footer);\n resizeObserver.observe(messages);\n\n return () => {\n resizeObserver.disconnect();\n resetTrailingSpace();\n };\n },\n // This effect only uses stable refs.\n [hasLastSentMessage] // eslint-disable-line react-hooks/exhaustive-deps\n );\n\n /**\n * Update the \"scroll at bottom\" state when needed.\n */\n useIntersectionCallback(\n bottomTrailingMarkerRef,\n (isIntersecting) => {\n onScrollAtBottomChange.current(isIntersecting);\n },\n { root: containerRef, rootMargin: MIN_DISTANCE_BOTTOM_SCROLL_INDICATOR }\n );\n\n /**\n * Instantly scroll to the bottom for the initial state.\n */\n useEffect(\n () => {\n scrollToBottom.current(\"instant\");\n },\n // `scrollToBottom` is a stable ref containing the callback.\n [] // eslint-disable-line react-hooks/exhaustive-deps\n );\n\n /**\n * Scroll to new messages when sending them.\n */\n useEffect(\n () => {\n if (lastSentMessageId) {\n scrollToBottom.current(\"smooth\", true);\n }\n },\n // `scrollToBottom` is a stable ref containing the callback.\n [lastSentMessageId] // eslint-disable-line react-hooks/exhaustive-deps\n );\n\n /**\n * Reset the \"scroll at bottom\" state when the component unmounts.\n */\n useEffect(\n () => {\n const onScrollAtBottomChangeCallback = onScrollAtBottomChange.current;\n\n return () => {\n onScrollAtBottomChangeCallback(null);\n };\n },\n // `onScrollAtBottomChange` is a stable ref containing the callback.\n [] // eslint-disable-line react-hooks/exhaustive-deps\n );\n\n return (\n <div\n className={cn(\"lb-ai-chat-messages\", className)}\n ref={forwardedRef}\n {...props}\n >\n {messages.map((message) => {\n if (message.role === \"user\") {\n return (\n <AiChatUserMessage\n key={message.id}\n message={message}\n overrides={overrides}\n components={components}\n />\n );\n } else if (message.role === \"assistant\") {\n return (\n <AiChatAssistantMessage\n key={message.id}\n message={message}\n overrides={overrides}\n components={components}\n />\n );\n } else {\n return null;\n }\n })}\n </div>\n );\n }\n);\n\nexport const AiChat = forwardRef<HTMLDivElement, AiChatProps>(\n (\n {\n chatId,\n copilotId,\n autoFocus,\n overrides,\n knowledge: localKnowledge,\n tools = {},\n onComposerSubmit,\n layout = \"inset\",\n components,\n className,\n ...props\n },\n forwardedRef\n ) => {\n const { messages, isLoading, error } = useAiChatMessages(chatId);\n const [lastSentMessageId, setLastSentMessageId] =\n useState<MessageId | null>(null);\n\n const $ = useOverrides(overrides);\n const Empty = components?.Empty ?? defaultComponents.Empty;\n const Loading = components?.Loading ?? defaultComponents.Loading;\n\n const containerRef = useRef<HTMLDivElement | null>(null);\n const messagesRef = useRef<HTMLDivElement | null>(null);\n const footerRef = useRef<HTMLDivElement | null>(null);\n const bottomMarkerRef = useRef<HTMLDivElement | null>(null);\n const bottomTrailingMarkerRef = useRef<HTMLDivElement | null>(null);\n const trailingSpacerRef = useRef<HTMLDivElement | null>(null);\n\n const [isScrollAtBottom, setScrollAtBottom] = useState<boolean | null>(\n null\n );\n // `useState`'s setter is stable but this is for clarity in the places it's used.\n const onScrollAtBottomChange = useLatest(setScrollAtBottom);\n const isScrollIndicatorVisible =\n messages && isScrollAtBottom !== null ? !isScrollAtBottom : false;\n\n useImperativeHandle<HTMLDivElement | null, HTMLDivElement | null>(\n forwardedRef,\n () => containerRef.current,\n []\n );\n\n const scrollToBottom = useLatest(\n (behavior: \"instant\" | \"smooth\", includeTrailingSpace = false) => {\n if (includeTrailingSpace) {\n // Scroll to the bottom marker to include the trailing space,\n // and wait for a frame in case the trailing space hasn't\n // been updated yet. (e.g. when sending a new message)\n requestAnimationFrame(() => {\n bottomMarkerRef.current?.scrollIntoView({\n behavior,\n block: \"end\",\n });\n });\n } else {\n // Scroll to the trailing space marker to only scroll to the\n // bottom of the messages, without including the trailing space.\n bottomTrailingMarkerRef.current?.scrollIntoView({\n behavior,\n block: \"end\",\n });\n }\n }\n );\n\n return (\n <div\n ref={containerRef}\n {...props}\n className={cn(\n \"lb-root lb-ai-chat\",\n `lb-ai-chat:layout-${layout}`,\n className\n )}\n >\n {Object.entries(tools).map(([name, tool]) => (\n <RegisterAiTool key={name} chatId={chatId} name={name} tool={tool} />\n ))}\n\n <div className=\"lb-ai-chat-content\">\n {isLoading ? (\n <Loading />\n ) : error !== undefined ? (\n <div className=\"lb-error lb-ai-chat-error\">\n {$.AI_CHAT_MESSAGES_ERROR(error)}\n </div>\n ) : messages.length === 0 ? (\n <Empty chatId={chatId} copilotId={copilotId} />\n ) : (\n <>\n <AiChatMessages\n ref={messagesRef}\n messages={messages}\n overrides={overrides}\n components={components}\n lastSentMessageId={lastSentMessageId}\n scrollToBottom={scrollToBottom}\n onScrollAtBottomChange={onScrollAtBottomChange}\n containerRef={containerRef}\n footerRef={footerRef}\n messagesRef={messagesRef}\n bottomTrailingMarkerRef={bottomTrailingMarkerRef}\n trailingSpacerRef={trailingSpacerRef}\n />\n\n {/**\n * This trailing spacer is used to extend the scrollable area beyond its actual\n * content, to allow messages to appear at the top of the viewport for example.\n */}\n <div\n ref={trailingSpacerRef}\n data-trailing-spacer=\"\"\n style={{\n pointerEvents: \"none\",\n }}\n aria-hidden\n />\n </>\n )}\n </div>\n\n <div className=\"lb-ai-chat-footer\" ref={footerRef}>\n <div className=\"lb-ai-chat-footer-actions\">\n <div\n className=\"lb-root lb-elevation lb-elevation-moderate lb-ai-chat-scroll-indicator\"\n data-visible={isScrollIndicatorVisible ? \"\" : undefined}\n >\n <button\n className=\"lb-ai-chat-scroll-indicator-button\"\n tabIndex={isScrollIndicatorVisible ? 0 : -1}\n aria-hidden={!isScrollIndicatorVisible}\n onClick={() => scrollToBottom.current(\"smooth\")}\n >\n <span className=\"lb-icon-container\">\n <ArrowDownIcon />\n </span>\n </button>\n </div>\n </div>\n <AiComposer\n key={chatId}\n chatId={chatId}\n copilotId={copilotId as CopilotId}\n overrides={overrides}\n autoFocus={autoFocus}\n knowledge={localKnowledge}\n onComposerSubmit={onComposerSubmit}\n onComposerSubmitted={({ id }) => setLastSentMessageId(id)}\n className={cn(\n \"lb-ai-chat-composer\",\n layout === \"inset\"\n ? \"lb-elevation lb-elevation-moderate\"\n : undefined\n )}\n />\n </div>\n\n {/**\n * This invisible marker is a trick which allows us to use IntersectionObserver to detect when the\n * scrollable area is fully scrolled to the bottom instead of manually tracking the scroll position\n * and having to deal with resizes, etc.\n *\n * It's positioned at the bottom of the scrollable area and reliably only becomes \"visible\" to the\n * IntersectionObserver when the scrollable area is scrolled to the bottom.\n */}\n {messages && messages.length > 0 ? (\n <div\n ref={bottomMarkerRef}\n style={{ position: \"sticky\", height: 0 }}\n aria-hidden\n data-bottom-marker=\"\"\n >\n {/**\n * This inner marker is absolutely offset by the same distance as the trailing space so its\n * visibility means the scrollable area is at the bottom of the messages, not the full bottom.\n */}\n <div\n ref={bottomTrailingMarkerRef}\n style={{\n position: \"absolute\",\n height: 0,\n }}\n data-bottom-trailing-marker=\"\"\n />\n </div>\n ) : null}\n </div>\n );\n }\n);\n"],"names":["jsx","SpinnerIcon","forwardRef","useEffect","messages","useIntersectionCallback","cn","AiChatUserMessage","AiChatAssistantMessage","overrides","useAiChatMessages","useState","useOverrides","useRef","useLatest","useImperativeHandle","jsxs","RegisterAiTool","Fragment","ArrowDownIcon","AiComposer"],"mappings":"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"}
@@ -49,37 +49,19 @@ const AiChatMessages = forwardRef(
49
49
  }
50
50
  const trailingSpacer = trailingSpacerRef.current;
51
51
  const bottomTrailingMarker = bottomTrailingMarkerRef.current;
52
- let containerHeight = void 0;
53
- let footerHeight = void 0;
54
- let messagesHeight = void 0;
52
+ let containerHeight = null;
53
+ let footerHeight = null;
54
+ let messagesHeight = null;
55
55
  const resetTrailingSpace = () => {
56
56
  trailingSpacer?.style.removeProperty("height");
57
57
  bottomTrailingMarker?.style.removeProperty("top");
58
58
  };
59
- const updateTrailingSpace = (updatedContainerHeight, updatedFooterHeight, updatedMessagesHeight) => {
59
+ const resizeObserver = new ResizeObserver((entries) => {
60
60
  if (!trailingSpacer || !bottomTrailingMarker) {
61
61
  return;
62
62
  }
63
63
  const lastMessage = messages2.lastElementChild;
64
64
  const penultimateMessage = lastMessage?.previousElementSibling;
65
- if (updatedContainerHeight === void 0) {
66
- updatedContainerHeight = container.getBoundingClientRect().height;
67
- }
68
- if (updatedFooterHeight === void 0) {
69
- updatedFooterHeight = footer.getBoundingClientRect().height;
70
- }
71
- if (updatedMessagesHeight === void 0) {
72
- updatedMessagesHeight = messages2.getBoundingClientRect().height;
73
- }
74
- if (updatedContainerHeight === containerHeight && updatedFooterHeight === footerHeight && updatedMessagesHeight === messagesHeight) {
75
- if (!lastMessage || !penultimateMessage || container.scrollHeight === container.clientHeight) {
76
- resetTrailingSpace();
77
- }
78
- return;
79
- }
80
- containerHeight = updatedContainerHeight;
81
- footerHeight = updatedFooterHeight;
82
- messagesHeight = updatedMessagesHeight;
83
65
  if (!lastMessage || !penultimateMessage) {
84
66
  resetTrailingSpace();
85
67
  return;
@@ -88,41 +70,43 @@ const AiChatMessages = forwardRef(
88
70
  resetTrailingSpace();
89
71
  return;
90
72
  }
91
- const penultimateMessageScrollMarginTop = Number.parseFloat(
92
- getComputedStyle(penultimateMessage).scrollMarginTop
93
- );
94
- const messagesRect = messages2.getBoundingClientRect();
95
- const penultimateMessageRect = penultimateMessage.getBoundingClientRect();
96
- const heightFromPenultimateMessageTopToMessagesListBottom = messagesRect.bottom - penultimateMessageRect.top;
97
- const differenceHeight = penultimateMessageScrollMarginTop + heightFromPenultimateMessageTopToMessagesListBottom + (footerHeight ?? 0);
98
- const trailingSpace = Math.max(containerHeight - differenceHeight, 0);
99
- trailingSpacer.style.height = `${trailingSpace}px`;
100
- bottomTrailingMarker.style.top = `${-trailingSpace}px`;
101
- };
102
- const resizeObserver = new ResizeObserver((entries) => {
103
73
  let updatedContainerHeight = containerHeight;
104
74
  let updatedFooterHeight = footerHeight;
105
75
  let updatedMessagesHeight = messagesHeight;
106
76
  for (const entry of entries) {
107
77
  const entryHeight = entry.borderBoxSize?.[0]?.blockSize ?? entry.contentRect.height;
108
78
  if (entry.target === container) {
109
- updatedContainerHeight = entryHeight;
79
+ updatedContainerHeight = entryHeight ?? null;
110
80
  } else if (entry.target === footer) {
111
- updatedFooterHeight = entryHeight;
81
+ updatedFooterHeight = entryHeight ?? null;
112
82
  } else if (entry.target === messages2) {
113
- updatedMessagesHeight = entryHeight;
83
+ updatedMessagesHeight = entryHeight ?? null;
114
84
  }
115
85
  }
116
- updateTrailingSpace(
117
- updatedContainerHeight,
118
- updatedFooterHeight,
119
- updatedMessagesHeight
86
+ if (updatedContainerHeight === null || updatedFooterHeight === null || updatedMessagesHeight === null) {
87
+ resetTrailingSpace();
88
+ return;
89
+ }
90
+ if (updatedContainerHeight === containerHeight && updatedFooterHeight === footerHeight && updatedMessagesHeight === messagesHeight) {
91
+ return;
92
+ }
93
+ containerHeight = updatedContainerHeight;
94
+ footerHeight = updatedFooterHeight;
95
+ messagesHeight = updatedMessagesHeight;
96
+ const penultimateMessageScrollMarginTop = Number.parseFloat(
97
+ getComputedStyle(penultimateMessage).scrollMarginTop
120
98
  );
99
+ const messagesRect = messages2.getBoundingClientRect();
100
+ const penultimateMessageRect = penultimateMessage.getBoundingClientRect();
101
+ const heightFromPenultimateMessageTopToMessagesListBottom = messagesRect.bottom - penultimateMessageRect.top;
102
+ const differenceHeight = penultimateMessageScrollMarginTop + heightFromPenultimateMessageTopToMessagesListBottom + (footerHeight ?? 0);
103
+ const trailingSpace = Math.max(containerHeight - differenceHeight, 0);
104
+ trailingSpacer.style.height = `${trailingSpace}px`;
105
+ bottomTrailingMarker.style.top = `${-trailingSpace}px`;
121
106
  });
122
107
  resizeObserver.observe(container);
123
108
  resizeObserver.observe(footer);
124
109
  resizeObserver.observe(messages2);
125
- requestAnimationFrame(() => updateTrailingSpace());
126
110
  return () => {
127
111
  resizeObserver.disconnect();
128
112
  resetTrailingSpace();
@@ -222,12 +206,12 @@ const AiChat = forwardRef(
222
206
  const scrollToBottom = useLatest(
223
207
  (behavior, includeTrailingSpace = false) => {
224
208
  if (includeTrailingSpace) {
225
- setTimeout(() => {
209
+ requestAnimationFrame(() => {
226
210
  bottomMarkerRef.current?.scrollIntoView({
227
211
  behavior,
228
212
  block: "end"
229
213
  });
230
- }, 0);
214
+ });
231
215
  } else {
232
216
  bottomTrailingMarkerRef.current?.scrollIntoView({
233
217
  behavior,
@@ -278,8 +262,7 @@ const AiChat = forwardRef(
278
262
  ref: trailingSpacerRef,
279
263
  "data-trailing-spacer": "",
280
264
  style: {
281
- pointerEvents: "none",
282
- height: 0
265
+ pointerEvents: "none"
283
266
  },
284
267
  "aria-hidden": true
285
268
  })
@@ -1 +1 @@
1
- {"version":3,"file":"AiChat.js","sources":["../../src/components/AiChat.tsx"],"sourcesContent":["import type {\n AiKnowledgeSource,\n AiOpaqueToolDefinition,\n CopilotId,\n MessageId,\n} from \"@liveblocks/core\";\nimport { RegisterAiTool, useAiChatMessages } from \"@liveblocks/react\";\nimport { useLatest } from \"@liveblocks/react/_private\";\nimport {\n type ComponentProps,\n type ComponentType,\n forwardRef,\n type MutableRefObject,\n useEffect,\n useImperativeHandle,\n useRef,\n useState,\n} from \"react\";\n\nimport type { GlobalComponents } from \"../components\";\nimport { ArrowDownIcon } from \"../icons/ArrowDown\";\nimport { SpinnerIcon } from \"../icons/Spinner\";\nimport {\n type AiChatMessageOverrides,\n type AiChatOverrides,\n type AiComposerOverrides,\n type GlobalOverrides,\n useOverrides,\n} from \"../overrides\";\nimport type { MarkdownComponents } from \"../primitives/Markdown\";\nimport { cn } from \"../utils/cn\";\nimport { useIntersectionCallback } from \"../utils/use-visible\";\nimport { AiChatAssistantMessage } from \"./internal/AiChatAssistantMessage\";\nimport { AiChatUserMessage } from \"./internal/AiChatUserMessage\";\nimport { AiComposer, type AiComposerProps } from \"./internal/AiComposer\";\n\n/**\n * The minimum number of pixels from the bottom of the scrollable area\n * before showing the scroll to bottom indicator.\n */\nconst MIN_DISTANCE_BOTTOM_SCROLL_INDICATOR = 60;\n\nexport type AiChatComponentsEmptyProps = {\n /**\n * The chat ID provided to the `AiChat` component.\n */\n chatId: string;\n\n /**\n * The copilot ID provided to the `AiChat` component.\n */\n copilotId?: string;\n};\n\nexport type AiChatComponentsLoadingProps = Record<string, never>;\n\nexport type AiChatComponents = {\n /**\n * The component used to render the empty state of the chat.\n */\n Empty: ComponentType<AiChatComponentsEmptyProps>;\n\n /**\n * The component used to render the loading state of the chat.\n */\n Loading: ComponentType<AiChatComponentsLoadingProps>;\n\n /**\n * The components used to render Markdown content.\n */\n markdown?: Partial<MarkdownComponents>;\n};\n\nexport interface AiChatProps extends ComponentProps<\"div\"> {\n /**\n * The ID of the chat the composer belongs to.\n */\n chatId: string;\n\n /**\n * Whether to focus the chat composer on mount.\n */\n autoFocus?: boolean;\n\n /**\n * The ID of the copilot to use to send the message.\n */\n copilotId?: string;\n\n /**\n * The contextual knowledge to include in the chat. May be used by the\n * assistant when generating responses. In addition to the knowledge passed\n * in via this prop, the AiChat instance will also have access to any\n * globally registered knowledge via <RegisterAiKnowledge />.\n */\n knowledge?: AiKnowledgeSource[];\n\n /**\n * Tool definitions to make available within this chat. May be used by the assistant when generating responses.\n */\n tools?: Record<string, AiOpaqueToolDefinition>;\n\n /**\n * The event handler called when the composer is submitted.\n */\n onComposerSubmit?: AiComposerProps[\"onComposerSubmit\"];\n\n /**\n * The layout of the chat and its composer.\n */\n layout?: \"inset\" | \"compact\";\n\n /**\n * Override the component's strings.\n */\n overrides?: Partial<\n GlobalOverrides &\n AiComposerOverrides &\n AiChatMessageOverrides &\n AiChatOverrides\n >;\n\n /**\n * Override the component's components.\n */\n components?: Partial<GlobalComponents & AiChatComponents>;\n}\n\ninterface AiChatMessagesProps extends ComponentProps<\"div\"> {\n messages: NonNullable<ReturnType<typeof useAiChatMessages>[\"messages\"]>;\n overrides: AiChatProps[\"overrides\"];\n components: AiChatProps[\"components\"];\n lastSentMessageId: MessageId | null;\n scrollToBottom: MutableRefObject<\n (behavior: \"instant\" | \"smooth\", includeTrailingSpace?: boolean) => void\n >;\n onScrollAtBottomChange: MutableRefObject<\n (isScrollAtBottom: boolean | null) => void\n >;\n containerRef: MutableRefObject<HTMLDivElement | null>;\n footerRef: MutableRefObject<HTMLDivElement | null>;\n messagesRef: MutableRefObject<HTMLDivElement | null>;\n bottomTrailingMarkerRef: MutableRefObject<HTMLDivElement | null>;\n trailingSpacerRef: MutableRefObject<HTMLDivElement | null>;\n}\n\nconst defaultComponents: AiChatComponents = {\n Empty: () => null,\n Loading: () => (\n <div className=\"lb-loading lb-ai-chat-loading\">\n <SpinnerIcon />\n </div>\n ),\n};\n\nconst AiChatMessages = forwardRef<HTMLDivElement, AiChatMessagesProps>(\n (\n {\n messages,\n overrides,\n components,\n lastSentMessageId,\n scrollToBottom,\n onScrollAtBottomChange,\n containerRef,\n footerRef,\n messagesRef,\n bottomTrailingMarkerRef,\n trailingSpacerRef,\n className,\n ...props\n },\n forwardedRef\n ) => {\n const hasLastSentMessage = lastSentMessageId !== null;\n\n /**\n * Every time the container, footer, or messages list change size,\n * we calculate the trailing space that would allow the penultimate\n * message to be at the top of the viewport, and apply it.\n *\n * ┌─────────────────────────────────────────┐▲ A = The `scroll-margin-top`\n * │ ┌─────────────────────────┐ │▼▲ value of the penultimate message\n * │ │ The penultimate message │ │ │\n * │ └─────────────────────────┘ │ │ B = The height from the top of\n * │ │ │ the penultimate message to the\n * │ ┌─────────────────────────┐ │ │ bottom of the messages list,\n * │ │ The last message │ │ │ including the messages' heights,\n * │ └─────────────────────────┘ │ │ and any padding, gap, etc\n * │ │ │\n * ├ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┤▲▼\n * │ ││ The trailing space needed to\n * │ = container height - (A + B + C) ││ allow the penultimate message\n * │ ││ to be at the top of the viewport\n * ├ ┬─────────────────────────────────────┬ ┤▼▲\n * │ │ │ │ │\n * │ │ │ │ │ C = The footer's height,\n * │ │ │ │ │ including any padding\n * │ └─────────────────────────────────────┘ │ │\n * └─────────────────────────────────────────┘ ▼\n */\n useEffect(\n () => {\n if (!hasLastSentMessage) {\n return;\n }\n\n const container = containerRef.current;\n const footer = footerRef.current;\n const messages = messagesRef.current;\n\n if (!container || !footer || !messages) {\n return;\n }\n\n const trailingSpacer = trailingSpacerRef.current;\n const bottomTrailingMarker = bottomTrailingMarkerRef.current;\n\n let containerHeight: number | undefined = undefined;\n let footerHeight: number | undefined = undefined;\n let messagesHeight: number | undefined = undefined;\n\n const resetTrailingSpace = () => {\n trailingSpacer?.style.removeProperty(\"height\");\n bottomTrailingMarker?.style.removeProperty(\"top\");\n };\n\n const updateTrailingSpace = (\n updatedContainerHeight?: number,\n updatedFooterHeight?: number,\n updatedMessagesHeight?: number\n ) => {\n if (!trailingSpacer || !bottomTrailingMarker) {\n return;\n }\n\n const lastMessage = messages.lastElementChild;\n const penultimateMessage = lastMessage?.previousElementSibling;\n\n if (updatedContainerHeight === undefined) {\n updatedContainerHeight = container.getBoundingClientRect().height;\n }\n\n if (updatedFooterHeight === undefined) {\n updatedFooterHeight = footer.getBoundingClientRect().height;\n }\n\n if (updatedMessagesHeight === undefined) {\n updatedMessagesHeight = messages.getBoundingClientRect().height;\n }\n\n // If the heights haven't changed, there's no need to update the trailing space.\n if (\n updatedContainerHeight === containerHeight &&\n updatedFooterHeight === footerHeight &&\n updatedMessagesHeight === messagesHeight\n ) {\n if (\n !lastMessage ||\n !penultimateMessage ||\n container.scrollHeight === container.clientHeight\n ) {\n resetTrailingSpace();\n }\n return;\n }\n\n // Now that we have compared them, we can update the heights.\n containerHeight = updatedContainerHeight;\n footerHeight = updatedFooterHeight;\n messagesHeight = updatedMessagesHeight;\n\n // If there's no last pair of messages, there's no need for any trailing space.\n if (!lastMessage || !penultimateMessage) {\n resetTrailingSpace();\n return;\n }\n\n // If the container isn't scrollable, there's no need for trailing space.\n if (container.scrollHeight === container.clientHeight) {\n resetTrailingSpace();\n return;\n }\n\n // A\n const penultimateMessageScrollMarginTop = Number.parseFloat(\n getComputedStyle(penultimateMessage as HTMLElement).scrollMarginTop\n );\n\n // B\n const messagesRect = messages.getBoundingClientRect();\n const penultimateMessageRect =\n penultimateMessage.getBoundingClientRect();\n const heightFromPenultimateMessageTopToMessagesListBottom =\n messagesRect.bottom - penultimateMessageRect.top;\n\n // A + B + C\n const differenceHeight =\n penultimateMessageScrollMarginTop +\n heightFromPenultimateMessageTopToMessagesListBottom +\n (footerHeight ?? 0);\n\n // = container height - (A + B + C)\n const trailingSpace = Math.max(containerHeight - differenceHeight, 0);\n\n // Update the trailing space.\n trailingSpacer.style.height = `${trailingSpace}px`;\n\n // Offset what \"the bottom\" is to the \"scroll at the bottom\" detection logic,\n // so that it doesn't include the trailing space.\n bottomTrailingMarker.style.top = `${-trailingSpace}px`;\n };\n\n const resizeObserver = new ResizeObserver((entries) => {\n let updatedContainerHeight: number | undefined = containerHeight;\n let updatedFooterHeight: number | undefined = footerHeight;\n let updatedMessagesHeight: number | undefined = messagesHeight;\n\n for (const entry of entries) {\n const entryHeight =\n entry.borderBoxSize?.[0]?.blockSize ?? entry.contentRect.height;\n\n if (entry.target === container) {\n updatedContainerHeight = entryHeight;\n } else if (entry.target === footer) {\n updatedFooterHeight = entryHeight;\n } else if (entry.target === messages) {\n updatedMessagesHeight = entryHeight;\n }\n }\n\n updateTrailingSpace(\n updatedContainerHeight,\n updatedFooterHeight,\n updatedMessagesHeight\n );\n });\n\n resizeObserver.observe(container);\n resizeObserver.observe(footer);\n resizeObserver.observe(messages);\n\n // Initialize before the first resize.\n requestAnimationFrame(() => updateTrailingSpace());\n\n return () => {\n resizeObserver.disconnect();\n resetTrailingSpace();\n };\n },\n // This effect only uses stable refs.\n [hasLastSentMessage] // eslint-disable-line react-hooks/exhaustive-deps\n );\n\n /**\n * Update the \"scroll at bottom\" state when needed.\n */\n useIntersectionCallback(\n bottomTrailingMarkerRef,\n (isIntersecting) => {\n onScrollAtBottomChange.current(isIntersecting);\n },\n { root: containerRef, rootMargin: MIN_DISTANCE_BOTTOM_SCROLL_INDICATOR }\n );\n\n /**\n * Instantly scroll to the bottom for the initial state.\n */\n useEffect(\n () => {\n scrollToBottom.current(\"instant\");\n },\n // `scrollToBottom` is a stable ref containing the callback.\n [] // eslint-disable-line react-hooks/exhaustive-deps\n );\n\n /**\n * Scroll to new messages when sending them.\n */\n useEffect(\n () => {\n if (lastSentMessageId) {\n scrollToBottom.current(\"smooth\", true);\n }\n },\n // `scrollToBottom` is a stable ref containing the callback.\n [lastSentMessageId] // eslint-disable-line react-hooks/exhaustive-deps\n );\n\n /**\n * Reset the \"scroll at bottom\" state when the component unmounts.\n */\n useEffect(\n () => {\n const onScrollAtBottomChangeCallback = onScrollAtBottomChange.current;\n\n return () => {\n onScrollAtBottomChangeCallback(null);\n };\n },\n // `onScrollAtBottomChange` is a stable ref containing the callback.\n [] // eslint-disable-line react-hooks/exhaustive-deps\n );\n\n return (\n <div\n className={cn(\"lb-ai-chat-messages\", className)}\n ref={forwardedRef}\n {...props}\n >\n {messages.map((message) => {\n if (message.role === \"user\") {\n return (\n <AiChatUserMessage\n key={message.id}\n message={message}\n overrides={overrides}\n components={components}\n />\n );\n } else if (message.role === \"assistant\") {\n return (\n <AiChatAssistantMessage\n key={message.id}\n message={message}\n overrides={overrides}\n components={components}\n />\n );\n } else {\n return null;\n }\n })}\n </div>\n );\n }\n);\n\nexport const AiChat = forwardRef<HTMLDivElement, AiChatProps>(\n (\n {\n chatId,\n copilotId,\n autoFocus,\n overrides,\n knowledge: localKnowledge,\n tools = {},\n onComposerSubmit,\n layout = \"inset\",\n components,\n className,\n ...props\n },\n forwardedRef\n ) => {\n const { messages, isLoading, error } = useAiChatMessages(chatId);\n const [lastSentMessageId, setLastSentMessageId] =\n useState<MessageId | null>(null);\n\n const $ = useOverrides(overrides);\n const Empty = components?.Empty ?? defaultComponents.Empty;\n const Loading = components?.Loading ?? defaultComponents.Loading;\n\n const containerRef = useRef<HTMLDivElement | null>(null);\n const messagesRef = useRef<HTMLDivElement | null>(null);\n const footerRef = useRef<HTMLDivElement | null>(null);\n const bottomMarkerRef = useRef<HTMLDivElement | null>(null);\n const bottomTrailingMarkerRef = useRef<HTMLDivElement | null>(null);\n const trailingSpacerRef = useRef<HTMLDivElement | null>(null);\n\n const [isScrollAtBottom, setScrollAtBottom] = useState<boolean | null>(\n null\n );\n // `useState`'s setter is stable but this is for clarity in the places it's used.\n const onScrollAtBottomChange = useLatest(setScrollAtBottom);\n const isScrollIndicatorVisible =\n messages && isScrollAtBottom !== null ? !isScrollAtBottom : false;\n\n useImperativeHandle<HTMLDivElement | null, HTMLDivElement | null>(\n forwardedRef,\n () => containerRef.current,\n []\n );\n\n const scrollToBottom = useLatest(\n (behavior: \"instant\" | \"smooth\", includeTrailingSpace = false) => {\n if (includeTrailingSpace) {\n // Scroll to the bottom marker to include the trailing space,\n // but wait for the next tick in case the trailing space hasn't\n // been updated yet. (e.g. when sending a new message)\n setTimeout(() => {\n bottomMarkerRef.current?.scrollIntoView({\n behavior,\n block: \"end\",\n });\n }, 0);\n } else {\n // Scroll to the trailing space marker to only scroll to the\n // bottom of the messages, without including the trailing space.\n bottomTrailingMarkerRef.current?.scrollIntoView({\n behavior,\n block: \"end\",\n });\n }\n }\n );\n\n return (\n <div\n ref={containerRef}\n {...props}\n className={cn(\n \"lb-root lb-ai-chat\",\n `lb-ai-chat:layout-${layout}`,\n className\n )}\n >\n {Object.entries(tools).map(([name, tool]) => (\n <RegisterAiTool key={name} chatId={chatId} name={name} tool={tool} />\n ))}\n\n <div className=\"lb-ai-chat-content\">\n {isLoading ? (\n <Loading />\n ) : error !== undefined ? (\n <div className=\"lb-error lb-ai-chat-error\">\n {$.AI_CHAT_MESSAGES_ERROR(error)}\n </div>\n ) : messages.length === 0 ? (\n <Empty chatId={chatId} copilotId={copilotId} />\n ) : (\n <>\n <AiChatMessages\n ref={messagesRef}\n messages={messages}\n overrides={overrides}\n components={components}\n lastSentMessageId={lastSentMessageId}\n scrollToBottom={scrollToBottom}\n onScrollAtBottomChange={onScrollAtBottomChange}\n containerRef={containerRef}\n footerRef={footerRef}\n messagesRef={messagesRef}\n bottomTrailingMarkerRef={bottomTrailingMarkerRef}\n trailingSpacerRef={trailingSpacerRef}\n />\n\n {/**\n * This trailing spacer is used to extend the scrollable area beyond its actual\n * content, to allow messages to appear at the top of the viewport for example.\n */}\n <div\n ref={trailingSpacerRef}\n data-trailing-spacer=\"\"\n style={{\n pointerEvents: \"none\",\n height: 0,\n }}\n aria-hidden\n />\n </>\n )}\n </div>\n\n <div className=\"lb-ai-chat-footer\" ref={footerRef}>\n <div className=\"lb-ai-chat-footer-actions\">\n <div\n className=\"lb-root lb-elevation lb-elevation-moderate lb-ai-chat-scroll-indicator\"\n data-visible={isScrollIndicatorVisible ? \"\" : undefined}\n >\n <button\n className=\"lb-ai-chat-scroll-indicator-button\"\n tabIndex={isScrollIndicatorVisible ? 0 : -1}\n aria-hidden={!isScrollIndicatorVisible}\n onClick={() => scrollToBottom.current(\"smooth\")}\n >\n <span className=\"lb-icon-container\">\n <ArrowDownIcon />\n </span>\n </button>\n </div>\n </div>\n <AiComposer\n key={chatId}\n chatId={chatId}\n copilotId={copilotId as CopilotId}\n overrides={overrides}\n autoFocus={autoFocus}\n knowledge={localKnowledge}\n onComposerSubmit={onComposerSubmit}\n onComposerSubmitted={({ id }) => setLastSentMessageId(id)}\n className={cn(\n \"lb-ai-chat-composer\",\n layout === \"inset\"\n ? \"lb-elevation lb-elevation-moderate\"\n : undefined\n )}\n />\n </div>\n\n {/**\n * This invisible marker is a trick which allows us to use IntersectionObserver to detect when the\n * scrollable area is fully scrolled to the bottom instead of manually tracking the scroll position\n * and having to deal with resizes, etc.\n *\n * It's positioned at the bottom of the scrollable area and reliably only becomes \"visible\" to the\n * IntersectionObserver when the scrollable area is scrolled to the bottom.\n */}\n {messages && messages.length > 0 ? (\n <div\n ref={bottomMarkerRef}\n style={{ position: \"sticky\", height: 0 }}\n aria-hidden\n data-bottom-marker=\"\"\n >\n {/**\n * This inner marker is absolutely offset by the same distance as the trailing space so its\n * visibility means the scrollable area is at the bottom of the messages, not the full bottom.\n */}\n <div\n ref={bottomTrailingMarkerRef}\n style={{\n position: \"absolute\",\n height: 0,\n }}\n data-bottom-trailing-marker=\"\"\n />\n </div>\n ) : null}\n </div>\n );\n }\n);\n"],"names":["messages"],"mappings":"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"}
1
+ {"version":3,"file":"AiChat.js","sources":["../../src/components/AiChat.tsx"],"sourcesContent":["import type {\n AiKnowledgeSource,\n AiOpaqueToolDefinition,\n CopilotId,\n MessageId,\n} from \"@liveblocks/core\";\nimport { RegisterAiTool, useAiChatMessages } from \"@liveblocks/react\";\nimport { useLatest } from \"@liveblocks/react/_private\";\nimport {\n type ComponentProps,\n type ComponentType,\n forwardRef,\n type MutableRefObject,\n useEffect,\n useImperativeHandle,\n useRef,\n useState,\n} from \"react\";\n\nimport type { GlobalComponents } from \"../components\";\nimport { ArrowDownIcon } from \"../icons/ArrowDown\";\nimport { SpinnerIcon } from \"../icons/Spinner\";\nimport {\n type AiChatMessageOverrides,\n type AiChatOverrides,\n type AiComposerOverrides,\n type GlobalOverrides,\n useOverrides,\n} from \"../overrides\";\nimport type { MarkdownComponents } from \"../primitives/Markdown\";\nimport { cn } from \"../utils/cn\";\nimport { useIntersectionCallback } from \"../utils/use-visible\";\nimport { AiChatAssistantMessage } from \"./internal/AiChatAssistantMessage\";\nimport { AiChatUserMessage } from \"./internal/AiChatUserMessage\";\nimport { AiComposer, type AiComposerProps } from \"./internal/AiComposer\";\n\n/**\n * The minimum number of pixels from the bottom of the scrollable area\n * before showing the scroll to bottom indicator.\n */\nconst MIN_DISTANCE_BOTTOM_SCROLL_INDICATOR = 60;\n\nexport type AiChatComponentsEmptyProps = {\n /**\n * The chat ID provided to the `AiChat` component.\n */\n chatId: string;\n\n /**\n * The copilot ID provided to the `AiChat` component.\n */\n copilotId?: string;\n};\n\nexport type AiChatComponentsLoadingProps = Record<string, never>;\n\nexport type AiChatComponents = {\n /**\n * The component used to render the empty state of the chat.\n */\n Empty: ComponentType<AiChatComponentsEmptyProps>;\n\n /**\n * The component used to render the loading state of the chat.\n */\n Loading: ComponentType<AiChatComponentsLoadingProps>;\n\n /**\n * The components used to render Markdown content.\n */\n markdown?: Partial<MarkdownComponents>;\n};\n\nexport interface AiChatProps extends ComponentProps<\"div\"> {\n /**\n * The ID of the chat the composer belongs to.\n */\n chatId: string;\n\n /**\n * Whether to focus the chat composer on mount.\n */\n autoFocus?: boolean;\n\n /**\n * The ID of the copilot to use to send the message.\n */\n copilotId?: string;\n\n /**\n * The contextual knowledge to include in the chat. May be used by the\n * assistant when generating responses. In addition to the knowledge passed\n * in via this prop, the AiChat instance will also have access to any\n * globally registered knowledge via <RegisterAiKnowledge />.\n */\n knowledge?: AiKnowledgeSource[];\n\n /**\n * Tool definitions to make available within this chat. May be used by the assistant when generating responses.\n */\n tools?: Record<string, AiOpaqueToolDefinition>;\n\n /**\n * The event handler called when the composer is submitted.\n */\n onComposerSubmit?: AiComposerProps[\"onComposerSubmit\"];\n\n /**\n * The layout of the chat and its composer.\n */\n layout?: \"inset\" | \"compact\";\n\n /**\n * Override the component's strings.\n */\n overrides?: Partial<\n GlobalOverrides &\n AiComposerOverrides &\n AiChatMessageOverrides &\n AiChatOverrides\n >;\n\n /**\n * Override the component's components.\n */\n components?: Partial<GlobalComponents & AiChatComponents>;\n}\n\ninterface AiChatMessagesProps extends ComponentProps<\"div\"> {\n messages: NonNullable<ReturnType<typeof useAiChatMessages>[\"messages\"]>;\n overrides: AiChatProps[\"overrides\"];\n components: AiChatProps[\"components\"];\n lastSentMessageId: MessageId | null;\n scrollToBottom: MutableRefObject<\n (behavior: \"instant\" | \"smooth\", includeTrailingSpace?: boolean) => void\n >;\n onScrollAtBottomChange: MutableRefObject<\n (isScrollAtBottom: boolean | null) => void\n >;\n containerRef: MutableRefObject<HTMLDivElement | null>;\n footerRef: MutableRefObject<HTMLDivElement | null>;\n messagesRef: MutableRefObject<HTMLDivElement | null>;\n bottomTrailingMarkerRef: MutableRefObject<HTMLDivElement | null>;\n trailingSpacerRef: MutableRefObject<HTMLDivElement | null>;\n}\n\nconst defaultComponents: AiChatComponents = {\n Empty: () => null,\n Loading: () => (\n <div className=\"lb-loading lb-ai-chat-loading\">\n <SpinnerIcon />\n </div>\n ),\n};\n\nconst AiChatMessages = forwardRef<HTMLDivElement, AiChatMessagesProps>(\n (\n {\n messages,\n overrides,\n components,\n lastSentMessageId,\n scrollToBottom,\n onScrollAtBottomChange,\n containerRef,\n footerRef,\n messagesRef,\n bottomTrailingMarkerRef,\n trailingSpacerRef,\n className,\n ...props\n },\n forwardedRef\n ) => {\n const hasLastSentMessage = lastSentMessageId !== null;\n\n /**\n * Every time the container, footer, or messages list change size,\n * we calculate the trailing space that would allow the penultimate\n * message to be at the top of the viewport, and apply it.\n *\n * ┌─────────────────────────────────────────┐▲ A = The `scroll-margin-top`\n * │ ┌─────────────────────────┐ │▼▲ value of the penultimate message\n * │ │ The penultimate message │ │ │\n * │ └─────────────────────────┘ │ │ B = The height from the top of\n * │ │ │ the penultimate message to the\n * │ ┌─────────────────────────┐ │ │ bottom of the messages list,\n * │ │ The last message │ │ │ including the messages' heights,\n * │ └─────────────────────────┘ │ │ and any padding, gap, etc\n * │ │ │\n * ├ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┤▲▼\n * │ ││ The trailing space needed to\n * │ = container height - (A + B + C) ││ allow the penultimate message\n * │ ││ to be at the top of the viewport\n * ├ ┬─────────────────────────────────────┬ ┤▼▲\n * │ │ │ │ │\n * │ │ │ │ │ C = The footer's height,\n * │ │ │ │ │ including any padding\n * │ └─────────────────────────────────────┘ │ │\n * └─────────────────────────────────────────┘ ▼\n */\n useEffect(\n () => {\n if (!hasLastSentMessage) {\n return;\n }\n\n const container = containerRef.current;\n const footer = footerRef.current;\n const messages = messagesRef.current;\n\n if (!container || !footer || !messages) {\n return;\n }\n\n const trailingSpacer = trailingSpacerRef.current;\n const bottomTrailingMarker = bottomTrailingMarkerRef.current;\n\n let containerHeight: number | null = null;\n let footerHeight: number | null = null;\n let messagesHeight: number | null = null;\n\n const resetTrailingSpace = () => {\n trailingSpacer?.style.removeProperty(\"height\");\n bottomTrailingMarker?.style.removeProperty(\"top\");\n };\n\n const resizeObserver = new ResizeObserver((entries) => {\n if (!trailingSpacer || !bottomTrailingMarker) {\n return;\n }\n\n const lastMessage = messages.lastElementChild;\n const penultimateMessage = lastMessage?.previousElementSibling;\n\n // If there's no last pair of messages, there's no need for any trailing space.\n if (!lastMessage || !penultimateMessage) {\n resetTrailingSpace();\n return;\n }\n\n // If the container's height is based on its content, the container isn't scrollable and there's no need for any trailing space.\n if (container.scrollHeight === container.clientHeight) {\n resetTrailingSpace();\n return;\n }\n\n let updatedContainerHeight: number | null = containerHeight;\n let updatedFooterHeight: number | null = footerHeight;\n let updatedMessagesHeight: number | null = messagesHeight;\n\n for (const entry of entries) {\n const entryHeight =\n entry.borderBoxSize?.[0]?.blockSize ?? entry.contentRect.height;\n\n if (entry.target === container) {\n updatedContainerHeight = entryHeight ?? null;\n } else if (entry.target === footer) {\n updatedFooterHeight = entryHeight ?? null;\n } else if (entry.target === messages) {\n updatedMessagesHeight = entryHeight ?? null;\n }\n }\n\n // If we don't have all the heights, we can't compute the trailing space.\n if (\n updatedContainerHeight === null ||\n updatedFooterHeight === null ||\n updatedMessagesHeight === null\n ) {\n resetTrailingSpace();\n return;\n }\n\n // If none of the heights have changed, we don't need to do anything.\n if (\n updatedContainerHeight === containerHeight &&\n updatedFooterHeight === footerHeight &&\n updatedMessagesHeight === messagesHeight\n ) {\n return;\n }\n\n // Now that we have compared them, we can update the heights.\n containerHeight = updatedContainerHeight;\n footerHeight = updatedFooterHeight;\n messagesHeight = updatedMessagesHeight;\n\n // A\n const penultimateMessageScrollMarginTop = Number.parseFloat(\n getComputedStyle(penultimateMessage as HTMLElement).scrollMarginTop\n );\n\n // B\n const messagesRect = messages.getBoundingClientRect();\n const penultimateMessageRect =\n penultimateMessage.getBoundingClientRect();\n const heightFromPenultimateMessageTopToMessagesListBottom =\n messagesRect.bottom - penultimateMessageRect.top;\n\n // A + B + C\n const differenceHeight =\n penultimateMessageScrollMarginTop +\n heightFromPenultimateMessageTopToMessagesListBottom +\n (footerHeight ?? 0);\n\n // = container height - (A + B + C)\n const trailingSpace = Math.max(containerHeight - differenceHeight, 0);\n\n // Update the trailing space.\n trailingSpacer.style.height = `${trailingSpace}px`;\n\n // Offset what \"the bottom\" is to the \"scroll at the bottom\" detection logic,\n // so that it doesn't include the trailing space.\n bottomTrailingMarker.style.top = `${-trailingSpace}px`;\n });\n\n resizeObserver.observe(container);\n resizeObserver.observe(footer);\n resizeObserver.observe(messages);\n\n return () => {\n resizeObserver.disconnect();\n resetTrailingSpace();\n };\n },\n // This effect only uses stable refs.\n [hasLastSentMessage] // eslint-disable-line react-hooks/exhaustive-deps\n );\n\n /**\n * Update the \"scroll at bottom\" state when needed.\n */\n useIntersectionCallback(\n bottomTrailingMarkerRef,\n (isIntersecting) => {\n onScrollAtBottomChange.current(isIntersecting);\n },\n { root: containerRef, rootMargin: MIN_DISTANCE_BOTTOM_SCROLL_INDICATOR }\n );\n\n /**\n * Instantly scroll to the bottom for the initial state.\n */\n useEffect(\n () => {\n scrollToBottom.current(\"instant\");\n },\n // `scrollToBottom` is a stable ref containing the callback.\n [] // eslint-disable-line react-hooks/exhaustive-deps\n );\n\n /**\n * Scroll to new messages when sending them.\n */\n useEffect(\n () => {\n if (lastSentMessageId) {\n scrollToBottom.current(\"smooth\", true);\n }\n },\n // `scrollToBottom` is a stable ref containing the callback.\n [lastSentMessageId] // eslint-disable-line react-hooks/exhaustive-deps\n );\n\n /**\n * Reset the \"scroll at bottom\" state when the component unmounts.\n */\n useEffect(\n () => {\n const onScrollAtBottomChangeCallback = onScrollAtBottomChange.current;\n\n return () => {\n onScrollAtBottomChangeCallback(null);\n };\n },\n // `onScrollAtBottomChange` is a stable ref containing the callback.\n [] // eslint-disable-line react-hooks/exhaustive-deps\n );\n\n return (\n <div\n className={cn(\"lb-ai-chat-messages\", className)}\n ref={forwardedRef}\n {...props}\n >\n {messages.map((message) => {\n if (message.role === \"user\") {\n return (\n <AiChatUserMessage\n key={message.id}\n message={message}\n overrides={overrides}\n components={components}\n />\n );\n } else if (message.role === \"assistant\") {\n return (\n <AiChatAssistantMessage\n key={message.id}\n message={message}\n overrides={overrides}\n components={components}\n />\n );\n } else {\n return null;\n }\n })}\n </div>\n );\n }\n);\n\nexport const AiChat = forwardRef<HTMLDivElement, AiChatProps>(\n (\n {\n chatId,\n copilotId,\n autoFocus,\n overrides,\n knowledge: localKnowledge,\n tools = {},\n onComposerSubmit,\n layout = \"inset\",\n components,\n className,\n ...props\n },\n forwardedRef\n ) => {\n const { messages, isLoading, error } = useAiChatMessages(chatId);\n const [lastSentMessageId, setLastSentMessageId] =\n useState<MessageId | null>(null);\n\n const $ = useOverrides(overrides);\n const Empty = components?.Empty ?? defaultComponents.Empty;\n const Loading = components?.Loading ?? defaultComponents.Loading;\n\n const containerRef = useRef<HTMLDivElement | null>(null);\n const messagesRef = useRef<HTMLDivElement | null>(null);\n const footerRef = useRef<HTMLDivElement | null>(null);\n const bottomMarkerRef = useRef<HTMLDivElement | null>(null);\n const bottomTrailingMarkerRef = useRef<HTMLDivElement | null>(null);\n const trailingSpacerRef = useRef<HTMLDivElement | null>(null);\n\n const [isScrollAtBottom, setScrollAtBottom] = useState<boolean | null>(\n null\n );\n // `useState`'s setter is stable but this is for clarity in the places it's used.\n const onScrollAtBottomChange = useLatest(setScrollAtBottom);\n const isScrollIndicatorVisible =\n messages && isScrollAtBottom !== null ? !isScrollAtBottom : false;\n\n useImperativeHandle<HTMLDivElement | null, HTMLDivElement | null>(\n forwardedRef,\n () => containerRef.current,\n []\n );\n\n const scrollToBottom = useLatest(\n (behavior: \"instant\" | \"smooth\", includeTrailingSpace = false) => {\n if (includeTrailingSpace) {\n // Scroll to the bottom marker to include the trailing space,\n // and wait for a frame in case the trailing space hasn't\n // been updated yet. (e.g. when sending a new message)\n requestAnimationFrame(() => {\n bottomMarkerRef.current?.scrollIntoView({\n behavior,\n block: \"end\",\n });\n });\n } else {\n // Scroll to the trailing space marker to only scroll to the\n // bottom of the messages, without including the trailing space.\n bottomTrailingMarkerRef.current?.scrollIntoView({\n behavior,\n block: \"end\",\n });\n }\n }\n );\n\n return (\n <div\n ref={containerRef}\n {...props}\n className={cn(\n \"lb-root lb-ai-chat\",\n `lb-ai-chat:layout-${layout}`,\n className\n )}\n >\n {Object.entries(tools).map(([name, tool]) => (\n <RegisterAiTool key={name} chatId={chatId} name={name} tool={tool} />\n ))}\n\n <div className=\"lb-ai-chat-content\">\n {isLoading ? (\n <Loading />\n ) : error !== undefined ? (\n <div className=\"lb-error lb-ai-chat-error\">\n {$.AI_CHAT_MESSAGES_ERROR(error)}\n </div>\n ) : messages.length === 0 ? (\n <Empty chatId={chatId} copilotId={copilotId} />\n ) : (\n <>\n <AiChatMessages\n ref={messagesRef}\n messages={messages}\n overrides={overrides}\n components={components}\n lastSentMessageId={lastSentMessageId}\n scrollToBottom={scrollToBottom}\n onScrollAtBottomChange={onScrollAtBottomChange}\n containerRef={containerRef}\n footerRef={footerRef}\n messagesRef={messagesRef}\n bottomTrailingMarkerRef={bottomTrailingMarkerRef}\n trailingSpacerRef={trailingSpacerRef}\n />\n\n {/**\n * This trailing spacer is used to extend the scrollable area beyond its actual\n * content, to allow messages to appear at the top of the viewport for example.\n */}\n <div\n ref={trailingSpacerRef}\n data-trailing-spacer=\"\"\n style={{\n pointerEvents: \"none\",\n }}\n aria-hidden\n />\n </>\n )}\n </div>\n\n <div className=\"lb-ai-chat-footer\" ref={footerRef}>\n <div className=\"lb-ai-chat-footer-actions\">\n <div\n className=\"lb-root lb-elevation lb-elevation-moderate lb-ai-chat-scroll-indicator\"\n data-visible={isScrollIndicatorVisible ? \"\" : undefined}\n >\n <button\n className=\"lb-ai-chat-scroll-indicator-button\"\n tabIndex={isScrollIndicatorVisible ? 0 : -1}\n aria-hidden={!isScrollIndicatorVisible}\n onClick={() => scrollToBottom.current(\"smooth\")}\n >\n <span className=\"lb-icon-container\">\n <ArrowDownIcon />\n </span>\n </button>\n </div>\n </div>\n <AiComposer\n key={chatId}\n chatId={chatId}\n copilotId={copilotId as CopilotId}\n overrides={overrides}\n autoFocus={autoFocus}\n knowledge={localKnowledge}\n onComposerSubmit={onComposerSubmit}\n onComposerSubmitted={({ id }) => setLastSentMessageId(id)}\n className={cn(\n \"lb-ai-chat-composer\",\n layout === \"inset\"\n ? \"lb-elevation lb-elevation-moderate\"\n : undefined\n )}\n />\n </div>\n\n {/**\n * This invisible marker is a trick which allows us to use IntersectionObserver to detect when the\n * scrollable area is fully scrolled to the bottom instead of manually tracking the scroll position\n * and having to deal with resizes, etc.\n *\n * It's positioned at the bottom of the scrollable area and reliably only becomes \"visible\" to the\n * IntersectionObserver when the scrollable area is scrolled to the bottom.\n */}\n {messages && messages.length > 0 ? (\n <div\n ref={bottomMarkerRef}\n style={{ position: \"sticky\", height: 0 }}\n aria-hidden\n data-bottom-marker=\"\"\n >\n {/**\n * This inner marker is absolutely offset by the same distance as the trailing space so its\n * visibility means the scrollable area is at the bottom of the messages, not the full bottom.\n */}\n <div\n ref={bottomTrailingMarkerRef}\n style={{\n position: \"absolute\",\n height: 0,\n }}\n data-bottom-trailing-marker=\"\"\n />\n </div>\n ) : null}\n </div>\n );\n }\n);\n"],"names":["messages"],"mappings":";;;;;;;;;;;;;AAwCA,MAAM,oCAAuC,GAAA,EAAA,CAAA;AA0G7C,MAAM,iBAAsC,GAAA;AAAA,EAC1C,OAAO,MAAM,IAAA;AAAA,EACb,OAAA,EAAS,sBACN,GAAA,CAAA,KAAA,EAAA;AAAA,IAAI,SAAU,EAAA,+BAAA;AAAA,IACb,8BAAC,WAAY,EAAA,EAAA,CAAA;AAAA,GACf,CAAA;AAEJ,CAAA,CAAA;AAEA,MAAM,cAAiB,GAAA,UAAA;AAAA,EACrB,CACE;AAAA,IACE,QAAA;AAAA,IACA,SAAA;AAAA,IACA,UAAA;AAAA,IACA,iBAAA;AAAA,IACA,cAAA;AAAA,IACA,sBAAA;AAAA,IACA,YAAA;AAAA,IACA,SAAA;AAAA,IACA,WAAA;AAAA,IACA,uBAAA;AAAA,IACA,iBAAA;AAAA,IACA,SAAA;AAAA,IACG,GAAA,KAAA;AAAA,KAEL,YACG,KAAA;AACH,IAAA,MAAM,qBAAqB,iBAAsB,KAAA,IAAA,CAAA;AA2BjD,IAAA,SAAA;AAAA,MACE,MAAM;AACJ,QAAA,IAAI,CAAC,kBAAoB,EAAA;AACvB,UAAA,OAAA;AAAA,SACF;AAEA,QAAA,MAAM,YAAY,YAAa,CAAA,OAAA,CAAA;AAC/B,QAAA,MAAM,SAAS,SAAU,CAAA,OAAA,CAAA;AACzB,QAAA,MAAMA,YAAW,WAAY,CAAA,OAAA,CAAA;AAE7B,QAAA,IAAI,CAAC,SAAA,IAAa,CAAC,MAAA,IAAU,CAACA,SAAU,EAAA;AACtC,UAAA,OAAA;AAAA,SACF;AAEA,QAAA,MAAM,iBAAiB,iBAAkB,CAAA,OAAA,CAAA;AACzC,QAAA,MAAM,uBAAuB,uBAAwB,CAAA,OAAA,CAAA;AAErD,QAAA,IAAI,eAAiC,GAAA,IAAA,CAAA;AACrC,QAAA,IAAI,YAA8B,GAAA,IAAA,CAAA;AAClC,QAAA,IAAI,cAAgC,GAAA,IAAA,CAAA;AAEpC,QAAA,MAAM,qBAAqB,MAAM;AAC/B,UAAgB,cAAA,EAAA,KAAA,CAAM,eAAe,QAAQ,CAAA,CAAA;AAC7C,UAAsB,oBAAA,EAAA,KAAA,CAAM,eAAe,KAAK,CAAA,CAAA;AAAA,SAClD,CAAA;AAEA,QAAA,MAAM,cAAiB,GAAA,IAAI,cAAe,CAAA,CAAC,OAAY,KAAA;AACrD,UAAI,IAAA,CAAC,cAAkB,IAAA,CAAC,oBAAsB,EAAA;AAC5C,YAAA,OAAA;AAAA,WACF;AAEA,UAAA,MAAM,cAAcA,SAAS,CAAA,gBAAA,CAAA;AAC7B,UAAA,MAAM,qBAAqB,WAAa,EAAA,sBAAA,CAAA;AAGxC,UAAI,IAAA,CAAC,WAAe,IAAA,CAAC,kBAAoB,EAAA;AACvC,YAAmB,kBAAA,EAAA,CAAA;AACnB,YAAA,OAAA;AAAA,WACF;AAGA,UAAI,IAAA,SAAA,CAAU,YAAiB,KAAA,SAAA,CAAU,YAAc,EAAA;AACrD,YAAmB,kBAAA,EAAA,CAAA;AACnB,YAAA,OAAA;AAAA,WACF;AAEA,UAAA,IAAI,sBAAwC,GAAA,eAAA,CAAA;AAC5C,UAAA,IAAI,mBAAqC,GAAA,YAAA,CAAA;AACzC,UAAA,IAAI,qBAAuC,GAAA,cAAA,CAAA;AAE3C,UAAA,KAAA,MAAW,SAAS,OAAS,EAAA;AAC3B,YAAA,MAAM,cACJ,KAAM,CAAA,aAAA,GAAgB,CAAI,CAAA,EAAA,SAAA,IAAa,MAAM,WAAY,CAAA,MAAA,CAAA;AAE3D,YAAI,IAAA,KAAA,CAAM,WAAW,SAAW,EAAA;AAC9B,cAAA,sBAAA,GAAyB,WAAe,IAAA,IAAA,CAAA;AAAA,aAC1C,MAAA,IAAW,KAAM,CAAA,MAAA,KAAW,MAAQ,EAAA;AAClC,cAAA,mBAAA,GAAsB,WAAe,IAAA,IAAA,CAAA;AAAA,aACvC,MAAA,IAAW,KAAM,CAAA,MAAA,KAAWA,SAAU,EAAA;AACpC,cAAA,qBAAA,GAAwB,WAAe,IAAA,IAAA,CAAA;AAAA,aACzC;AAAA,WACF;AAGA,UAAA,IACE,sBAA2B,KAAA,IAAA,IAC3B,mBAAwB,KAAA,IAAA,IACxB,0BAA0B,IAC1B,EAAA;AACA,YAAmB,kBAAA,EAAA,CAAA;AACnB,YAAA,OAAA;AAAA,WACF;AAGA,UAAA,IACE,sBAA2B,KAAA,eAAA,IAC3B,mBAAwB,KAAA,YAAA,IACxB,0BAA0B,cAC1B,EAAA;AACA,YAAA,OAAA;AAAA,WACF;AAGA,UAAkB,eAAA,GAAA,sBAAA,CAAA;AAClB,UAAe,YAAA,GAAA,mBAAA,CAAA;AACf,UAAiB,cAAA,GAAA,qBAAA,CAAA;AAGjB,UAAA,MAAM,oCAAoC,MAAO,CAAA,UAAA;AAAA,YAC/C,gBAAA,CAAiB,kBAAiC,CAAE,CAAA,eAAA;AAAA,WACtD,CAAA;AAGA,UAAM,MAAA,YAAA,GAAeA,UAAS,qBAAsB,EAAA,CAAA;AACpD,UAAM,MAAA,sBAAA,GACJ,mBAAmB,qBAAsB,EAAA,CAAA;AAC3C,UAAM,MAAA,mDAAA,GACJ,YAAa,CAAA,MAAA,GAAS,sBAAuB,CAAA,GAAA,CAAA;AAG/C,UAAM,MAAA,gBAAA,GACJ,iCACA,GAAA,mDAAA,IACC,YAAgB,IAAA,CAAA,CAAA,CAAA;AAGnB,UAAA,MAAM,aAAgB,GAAA,IAAA,CAAK,GAAI,CAAA,eAAA,GAAkB,kBAAkB,CAAC,CAAA,CAAA;AAGpE,UAAe,cAAA,CAAA,KAAA,CAAM,SAAS,CAAG,EAAA,aAAA,CAAA,EAAA,CAAA,CAAA;AAIjC,UAAqB,oBAAA,CAAA,KAAA,CAAM,GAAM,GAAA,CAAA,EAAG,CAAC,aAAA,CAAA,EAAA,CAAA,CAAA;AAAA,SACtC,CAAA,CAAA;AAED,QAAA,cAAA,CAAe,QAAQ,SAAS,CAAA,CAAA;AAChC,QAAA,cAAA,CAAe,QAAQ,MAAM,CAAA,CAAA;AAC7B,QAAA,cAAA,CAAe,QAAQA,SAAQ,CAAA,CAAA;AAE/B,QAAA,OAAO,MAAM;AACX,UAAA,cAAA,CAAe,UAAW,EAAA,CAAA;AAC1B,UAAmB,kBAAA,EAAA,CAAA;AAAA,SACrB,CAAA;AAAA,OACF;AAAA,MAEA,CAAC,kBAAkB,CAAA;AAAA,KACrB,CAAA;AAKA,IAAA,uBAAA;AAAA,MACE,uBAAA;AAAA,MACA,CAAC,cAAmB,KAAA;AAClB,QAAA,sBAAA,CAAuB,QAAQ,cAAc,CAAA,CAAA;AAAA,OAC/C;AAAA,MACA,EAAE,IAAA,EAAM,YAAc,EAAA,UAAA,EAAY,oCAAqC,EAAA;AAAA,KACzE,CAAA;AAKA,IAAA,SAAA;AAAA,MACE,MAAM;AACJ,QAAA,cAAA,CAAe,QAAQ,SAAS,CAAA,CAAA;AAAA,OAClC;AAAA,MAEA,EAAC;AAAA,KACH,CAAA;AAKA,IAAA,SAAA;AAAA,MACE,MAAM;AACJ,QAAA,IAAI,iBAAmB,EAAA;AACrB,UAAe,cAAA,CAAA,OAAA,CAAQ,UAAU,IAAI,CAAA,CAAA;AAAA,SACvC;AAAA,OACF;AAAA,MAEA,CAAC,iBAAiB,CAAA;AAAA,KACpB,CAAA;AAKA,IAAA,SAAA;AAAA,MACE,MAAM;AACJ,QAAA,MAAM,iCAAiC,sBAAuB,CAAA,OAAA,CAAA;AAE9D,QAAA,OAAO,MAAM;AACX,UAAA,8BAAA,CAA+B,IAAI,CAAA,CAAA;AAAA,SACrC,CAAA;AAAA,OACF;AAAA,MAEA,EAAC;AAAA,KACH,CAAA;AAEA,IAAA,uBACG,GAAA,CAAA,KAAA,EAAA;AAAA,MACC,SAAA,EAAW,EAAG,CAAA,qBAAA,EAAuB,SAAS,CAAA;AAAA,MAC9C,GAAK,EAAA,YAAA;AAAA,MACJ,GAAG,KAAA;AAAA,MAEH,QAAA,EAAA,QAAA,CAAS,GAAI,CAAA,CAAC,OAAY,KAAA;AACzB,QAAI,IAAA,OAAA,CAAQ,SAAS,MAAQ,EAAA;AAC3B,UAAA,uBACG,GAAA,CAAA,iBAAA,EAAA;AAAA,YAEC,OAAA;AAAA,YACA,SAAA;AAAA,YACA,UAAA;AAAA,WAAA,EAHK,QAAQ,EAIf,CAAA,CAAA;AAAA,SAEJ,MAAA,IAAW,OAAQ,CAAA,IAAA,KAAS,WAAa,EAAA;AACvC,UAAA,uBACG,GAAA,CAAA,sBAAA,EAAA;AAAA,YAEC,OAAA;AAAA,YACA,SAAA;AAAA,YACA,UAAA;AAAA,WAAA,EAHK,QAAQ,EAIf,CAAA,CAAA;AAAA,SAEG,MAAA;AACL,UAAO,OAAA,IAAA,CAAA;AAAA,SACT;AAAA,OACD,CAAA;AAAA,KACH,CAAA,CAAA;AAAA,GAEJ;AACF,CAAA,CAAA;AAEO,MAAM,MAAS,GAAA,UAAA;AAAA,EACpB,CACE;AAAA,IACE,MAAA;AAAA,IACA,SAAA;AAAA,IACA,SAAA;AAAA,IACA,SAAA;AAAA,IACA,SAAW,EAAA,cAAA;AAAA,IACX,QAAQ,EAAC;AAAA,IACT,gBAAA;AAAA,IACA,MAAS,GAAA,OAAA;AAAA,IACT,UAAA;AAAA,IACA,SAAA;AAAA,IACG,GAAA,KAAA;AAAA,KAEL,YACG,KAAA;AACH,IAAA,MAAM,EAAE,QAAU,EAAA,SAAA,EAAW,KAAM,EAAA,GAAI,kBAAkB,MAAM,CAAA,CAAA;AAC/D,IAAA,MAAM,CAAC,iBAAA,EAAmB,oBAAoB,CAAA,GAC5C,SAA2B,IAAI,CAAA,CAAA;AAEjC,IAAM,MAAA,CAAA,GAAI,aAAa,SAAS,CAAA,CAAA;AAChC,IAAM,MAAA,KAAA,GAAQ,UAAY,EAAA,KAAA,IAAS,iBAAkB,CAAA,KAAA,CAAA;AACrD,IAAM,MAAA,OAAA,GAAU,UAAY,EAAA,OAAA,IAAW,iBAAkB,CAAA,OAAA,CAAA;AAEzD,IAAM,MAAA,YAAA,GAAe,OAA8B,IAAI,CAAA,CAAA;AACvD,IAAM,MAAA,WAAA,GAAc,OAA8B,IAAI,CAAA,CAAA;AACtD,IAAM,MAAA,SAAA,GAAY,OAA8B,IAAI,CAAA,CAAA;AACpD,IAAM,MAAA,eAAA,GAAkB,OAA8B,IAAI,CAAA,CAAA;AAC1D,IAAM,MAAA,uBAAA,GAA0B,OAA8B,IAAI,CAAA,CAAA;AAClE,IAAM,MAAA,iBAAA,GAAoB,OAA8B,IAAI,CAAA,CAAA;AAE5D,IAAM,MAAA,CAAC,gBAAkB,EAAA,iBAAiB,CAAI,GAAA,QAAA;AAAA,MAC5C,IAAA;AAAA,KACF,CAAA;AAEA,IAAM,MAAA,sBAAA,GAAyB,UAAU,iBAAiB,CAAA,CAAA;AAC1D,IAAA,MAAM,wBACJ,GAAA,QAAA,IAAY,gBAAqB,KAAA,IAAA,GAAO,CAAC,gBAAmB,GAAA,KAAA,CAAA;AAE9D,IAAA,mBAAA;AAAA,MACE,YAAA;AAAA,MACA,MAAM,YAAa,CAAA,OAAA;AAAA,MACnB,EAAC;AAAA,KACH,CAAA;AAEA,IAAA,MAAM,cAAiB,GAAA,SAAA;AAAA,MACrB,CAAC,QAAgC,EAAA,oBAAA,GAAuB,KAAU,KAAA;AAChE,QAAA,IAAI,oBAAsB,EAAA;AAIxB,UAAA,qBAAA,CAAsB,MAAM;AAC1B,YAAA,eAAA,CAAgB,SAAS,cAAe,CAAA;AAAA,cACtC,QAAA;AAAA,cACA,KAAO,EAAA,KAAA;AAAA,aACR,CAAA,CAAA;AAAA,WACF,CAAA,CAAA;AAAA,SACI,MAAA;AAGL,UAAA,uBAAA,CAAwB,SAAS,cAAe,CAAA;AAAA,YAC9C,QAAA;AAAA,YACA,KAAO,EAAA,KAAA;AAAA,WACR,CAAA,CAAA;AAAA,SACH;AAAA,OACF;AAAA,KACF,CAAA;AAEA,IAAA,uBACG,IAAA,CAAA,KAAA,EAAA;AAAA,MACC,GAAK,EAAA,YAAA;AAAA,MACJ,GAAG,KAAA;AAAA,MACJ,SAAW,EAAA,EAAA;AAAA,QACT,oBAAA;AAAA,QACA,CAAqB,kBAAA,EAAA,MAAA,CAAA,CAAA;AAAA,QACrB,SAAA;AAAA,OACF;AAAA,MAEC,QAAA,EAAA;AAAA,QAAO,MAAA,CAAA,OAAA,CAAQ,KAAK,CAAE,CAAA,GAAA,CAAI,CAAC,CAAC,IAAA,EAAM,IAAI,CAAA,qBACpC,GAAA,CAAA,cAAA,EAAA;AAAA,UAA0B,MAAA;AAAA,UAAgB,IAAA;AAAA,UAAY,IAAA;AAAA,SAAA,EAAlC,IAA8C,CACpE,CAAA;AAAA,wBAEA,GAAA,CAAA,KAAA,EAAA;AAAA,UAAI,SAAU,EAAA,oBAAA;AAAA,UACZ,sCACE,GAAA,CAAA,OAAA,EAAA,EAAQ,CACP,GAAA,KAAA,KAAU,yBACX,GAAA,CAAA,KAAA,EAAA;AAAA,YAAI,SAAU,EAAA,2BAAA;AAAA,YACZ,QAAA,EAAA,CAAA,CAAE,uBAAuB,KAAK,CAAA;AAAA,WACjC,CACE,GAAA,QAAA,CAAS,MAAW,KAAA,CAAA,mBACrB,GAAA,CAAA,KAAA,EAAA;AAAA,YAAM,MAAA;AAAA,YAAgB,SAAA;AAAA,WAAsB,CAE7C,mBAAA,IAAA,CAAA,QAAA,EAAA;AAAA,YACE,QAAA,EAAA;AAAA,8BAAC,GAAA,CAAA,cAAA,EAAA;AAAA,gBACC,GAAK,EAAA,WAAA;AAAA,gBACL,QAAA;AAAA,gBACA,SAAA;AAAA,gBACA,UAAA;AAAA,gBACA,iBAAA;AAAA,gBACA,cAAA;AAAA,gBACA,sBAAA;AAAA,gBACA,YAAA;AAAA,gBACA,SAAA;AAAA,gBACA,WAAA;AAAA,gBACA,uBAAA;AAAA,gBACA,iBAAA;AAAA,eACF,CAAA;AAAA,8BAMC,GAAA,CAAA,KAAA,EAAA;AAAA,gBACC,GAAK,EAAA,iBAAA;AAAA,gBACL,sBAAqB,EAAA,EAAA;AAAA,gBACrB,KAAO,EAAA;AAAA,kBACL,aAAe,EAAA,MAAA;AAAA,iBACjB;AAAA,gBACA,aAAW,EAAA,IAAA;AAAA,eACb,CAAA;AAAA,aAAA;AAAA,WACF,CAAA;AAAA,SAEJ,CAAA;AAAA,wBAEC,IAAA,CAAA,KAAA,EAAA;AAAA,UAAI,SAAU,EAAA,mBAAA;AAAA,UAAoB,GAAK,EAAA,SAAA;AAAA,UACtC,QAAA,EAAA;AAAA,4BAAC,GAAA,CAAA,KAAA,EAAA;AAAA,cAAI,SAAU,EAAA,2BAAA;AAAA,cACb,QAAC,kBAAA,GAAA,CAAA,KAAA,EAAA;AAAA,gBACC,SAAU,EAAA,wEAAA;AAAA,gBACV,cAAA,EAAc,2BAA2B,EAAK,GAAA,KAAA,CAAA;AAAA,gBAE9C,QAAC,kBAAA,GAAA,CAAA,QAAA,EAAA;AAAA,kBACC,SAAU,EAAA,oCAAA;AAAA,kBACV,QAAA,EAAU,2BAA2B,CAAI,GAAA,CAAA,CAAA;AAAA,kBACzC,eAAa,CAAC,wBAAA;AAAA,kBACd,OAAS,EAAA,MAAM,cAAe,CAAA,OAAA,CAAQ,QAAQ,CAAA;AAAA,kBAE9C,QAAC,kBAAA,GAAA,CAAA,MAAA,EAAA;AAAA,oBAAK,SAAU,EAAA,mBAAA;AAAA,oBACd,8BAAC,aAAc,EAAA,EAAA,CAAA;AAAA,mBACjB,CAAA;AAAA,iBACF,CAAA;AAAA,eACF,CAAA;AAAA,aACF,CAAA;AAAA,4BACC,GAAA,CAAA,UAAA,EAAA;AAAA,cAEC,MAAA;AAAA,cACA,SAAA;AAAA,cACA,SAAA;AAAA,cACA,SAAA;AAAA,cACA,SAAW,EAAA,cAAA;AAAA,cACX,gBAAA;AAAA,cACA,qBAAqB,CAAC,EAAE,EAAG,EAAA,KAAM,qBAAqB,EAAE,CAAA;AAAA,cACxD,SAAW,EAAA,EAAA;AAAA,gBACT,qBAAA;AAAA,gBACA,MAAA,KAAW,UACP,oCACA,GAAA,KAAA,CAAA;AAAA,eACN;AAAA,aAAA,EAbK,MAcP,CAAA;AAAA,WAAA;AAAA,SACF,CAAA;AAAA,QAUC,QAAY,IAAA,QAAA,CAAS,MAAS,GAAA,CAAA,mBAC5B,GAAA,CAAA,KAAA,EAAA;AAAA,UACC,GAAK,EAAA,eAAA;AAAA,UACL,KAAO,EAAA,EAAE,QAAU,EAAA,QAAA,EAAU,QAAQ,CAAE,EAAA;AAAA,UACvC,aAAW,EAAA,IAAA;AAAA,UACX,oBAAmB,EAAA,EAAA;AAAA,UAMnB,QAAC,kBAAA,GAAA,CAAA,KAAA,EAAA;AAAA,YACC,GAAK,EAAA,uBAAA;AAAA,YACL,KAAO,EAAA;AAAA,cACL,QAAU,EAAA,UAAA;AAAA,cACV,MAAQ,EAAA,CAAA;AAAA,aACV;AAAA,YACA,6BAA4B,EAAA,EAAA;AAAA,WAC9B,CAAA;AAAA,SACF,CACE,GAAA,IAAA;AAAA,OAAA;AAAA,KACN,CAAA,CAAA;AAAA,GAEJ;AACF;;;;"}
@@ -1 +1 @@
1
- {"version":3,"file":"AiTool.cjs","sources":["../../src/components/AiTool.tsx"],"sourcesContent":["import type {\n AiToolExecuteCallback,\n AiToolTypePack,\n JsonObject,\n NoInfr,\n} from \"@liveblocks/core\";\nimport { kInternal } from \"@liveblocks/core\";\nimport type { ComponentProps, ReactNode } from \"react\";\nimport { Children, forwardRef, useCallback, useMemo } from \"react\";\n\nimport { Button } from \"../_private\";\nimport {\n CheckCircleFillIcon,\n ChevronRightIcon,\n CrossCircleFillIcon,\n MinusCircleIcon,\n SpinnerIcon,\n} from \"../icons\";\nimport {\n type AiToolConfirmationOverrides,\n type GlobalOverrides,\n useOverrides,\n} from \"../overrides\";\nimport { useAiToolInvocationContext } from \"../primitives/AiMessage/contexts\";\nimport * as Collapsible from \"../primitives/Collapsible\";\nimport { cn } from \"../utils/cn\";\nimport { useSemiControllableState } from \"../utils/use-controllable-state\";\nimport { CodeBlock } from \"./internal/CodeBlock\";\n\nexport interface AiToolProps\n extends Omit<ComponentProps<\"div\">, \"title\" | \"children\"> {\n /**\n * The tool's title.\n *\n * By default, a human-readable version of the tool's name is used:\n * - `\"showTodo\"` → \"Show todo\"\n * - `\"get_weather\"` → \"Get weather\"\n */\n title?: ReactNode;\n\n /**\n * An optional icon displayed next to the title.\n */\n icon?: ReactNode;\n\n /**\n * The content shown in the tool.\n */\n children?: ReactNode;\n\n /**\n * The visual appearance of the tool.\n */\n variant?: \"block\" | \"minimal\";\n\n /**\n * Whether the content is currently collapsed.\n * It is not a traditional controlled value, as in if you set it to `true` it would only stay expanded.\n * Instead, it is \"semi-controlled\", meaning that setting it to `true` will expand it, but it\n * can still be collapsed/expanded by clicking on it.\n */\n collapsed?: boolean;\n\n /**\n * The event handler called when the content is collapsed or expanded by clicking on it.\n */\n onCollapsedChange?: (collapsed: boolean) => void;\n\n /**\n * Whether the content can be collapsed/expanded.\n * If set to `false`, clicking on it will have no effect.\n * If there's no content, this prop has no effect.\n */\n collapsible?: boolean;\n}\n\nexport type AiToolIconProps = ComponentProps<\"div\">;\n\nexport type AiToolInspectorProps = ComponentProps<\"div\">;\n\nexport interface AiToolConfirmationProps<\n A extends JsonObject,\n R extends JsonObject,\n> extends ComponentProps<\"div\"> {\n /**\n * The callback invoked when the user clicks the confirm button.\n */\n confirm: AiToolExecuteCallback<A, R>;\n\n /**\n * The callback invoked when the user clicks the cancel button.\n */\n cancel?: AiToolExecuteCallback<A, R>;\n\n /**\n * The visual appearance.\n */\n variant?: \"default\" | \"destructive\";\n\n /**\n * Override the component's strings.\n */\n overrides?: Partial<GlobalOverrides & AiToolConfirmationOverrides>;\n\n /**\n * The tool's result type, to be used with the `types` prop in the `render` method.\n *\n * @example\n * defineAiTool<{ value: number }>()({\n * // ...\n * render: ({ types }) => (\n * <AiTool.Confirmation\n * types={types}\n * confirm={() => {\n * return {\n * // Using `types` makes the result type-safe\n * // based on the tool's definition\n * data: { value: 123 },\n * };\n * }}\n * />\n * ),\n * })\n */\n types?: NoInfr<AiToolTypePack<A, R>>;\n}\n\nfunction AiToolIcon({ className, ...props }: AiToolIconProps) {\n return <div className={cn(\"lb-ai-tool-icon\", className)} {...props} />;\n}\n\nfunction AiToolInspector({ className, ...props }: AiToolInspectorProps) {\n const { args, partialArgs, result } = useAiToolInvocationContext();\n\n return (\n <div className={cn(\"lb-ai-tool-inspector\", className)} {...props}>\n <CodeBlock\n title=\"Arguments\"\n code={JSON.stringify(args ?? partialArgs, null, 2)}\n />\n {result !== undefined ? (\n <CodeBlock title=\"Result\" code={JSON.stringify(result, null, 2)} />\n ) : null}\n </div>\n );\n}\n\nfunction AiToolConfirmation<\n TPack extends AiToolTypePack,\n A extends JsonObject = TPack[\"A\"],\n R extends JsonObject = TPack[\"R\"],\n>({\n children,\n variant = \"default\",\n confirm,\n cancel,\n overrides,\n className,\n ...props\n}: AiToolConfirmationProps<A, R>) {\n const { stage, args, respond, name, invocationId } =\n useAiToolInvocationContext();\n const $ = useOverrides(overrides);\n\n const enabled = stage === \"executing\";\n\n const context = useMemo(() => ({ name, invocationId }), [name, invocationId]);\n\n const onConfirmClick = useCallback(async () => {\n if (enabled) {\n const result = await confirm(args as A, context);\n respond(result ?? undefined);\n }\n }, [enabled, args, confirm, respond, context]);\n\n const onCancelClick = useCallback(async () => {\n if (enabled) {\n if (cancel === undefined) {\n respond({ cancel: true });\n } else {\n const result = await cancel(args as A, context);\n respond(result ?? undefined);\n }\n }\n }, [enabled, args, cancel, respond, context]);\n\n // If there's no content and the tool has been executed (so there's no\n // confirmation UI displayed either), don't render anything.\n if (stage === \"executed\" && !children) {\n return null;\n }\n\n return (\n <div className={cn(\"lb-ai-tool-confirmation\", className)} {...props}>\n {children ? (\n <div className=\"lb-ai-tool-confirmation-content\">{children}</div>\n ) : null}\n {stage !== \"executed\" && (\n <div className=\"lb-ai-tool-confirmation-footer\">\n <div className=\"lb-ai-tool-confirmation-actions\">\n <Button\n disabled={!enabled}\n onClick={onCancelClick}\n variant=\"secondary\"\n >\n {$.AI_TOOL_CONFIRMATION_CANCEL}\n </Button>\n <Button\n disabled={!enabled}\n onClick={onConfirmClick}\n variant={variant === \"destructive\" ? \"destructive\" : \"primary\"}\n >\n {$.AI_TOOL_CONFIRMATION_CONFIRM}\n </Button>\n </div>\n </div>\n )}\n </div>\n );\n}\n\nfunction prettifyString(string: string) {\n return (\n string\n // Convert camelCase to spaces\n .replace(/([a-z])([A-Z])/g, \"$1 $2\")\n // Convert snake_case and kebab-case to spaces\n .replace(/[_-]+/g, \" \")\n // Collapse multiple following spaces\n .replace(/\\s+/g, \" \")\n // Trim leading and trailing spaces\n .trim()\n // Capitalize first word\n .toLowerCase()\n .replace(/^\\w/, (character) => character.toUpperCase())\n );\n}\n\n/**\n * A pre-built component which displays a tool call.\n *\n * By default, a human-readable version of the tool's name is used as a title:\n * - `\"showTodo\"` → \"Show todo\"\n * - `\"get_weather\"` → \"Get weather\"\n *\n * @example\n * defineAiTool()({\n * // ...\n * render: () => (\n * <AiTool />\n * ),\n * })\n *\n * It can be customized in various ways:\n * - adding an icon\n * - customizing the title (even dynamically)\n * - adding custom content inside it\n * - collapsing it conditionally\n * - etc.\n *\n * @example\n * defineAiTool()({\n * // ...\n * render: ({ stage, result }) => (\n * <AiTool\n * icon=\"🔍\"\n *\n * // Override the default title based on the tool's stage\n * title={stage === \"executing\" ? \"Searching…\" : \"Search results\"}\n *\n * // Start open and automatically collapse after it is executed\n * // The user can still expand/collapse it manually at any time\n * collapsed={stage === \"executed\"}\n * >\n * <SearchResults data={result.data} />\n * </AiTool>\n * ),\n * })\n *\n * It also comes with a few built-in sub-components:\n * - `AiTool.Confirmation` to display a human-in-the-loop confirmation step\n * which can be accepted or cancelled by the user.\n * - `AiTool.Inspector` to display the tool's arguments and result which can\n * be useful during development.\n *\n * @example\n * defineAiTool()({\n * // ...\n * render: () => (\n * <AiTool>\n * <AiTool.Confirmation\n * // Use a destructive visual appearance\n * variant=\"destructive\"\n *\n * // The tool's arguments can be directly accessed like in `execute`\n * confirm={({ pageIds }) => {\n * const deletedPageTitles = pages\n * .filter((p) => pageIds.includes(p.id))\n * .map((page) => page.title);\n *\n * deletePages(pageIds);\n *\n * // This result will be available as `result` in the tool's `render` props\n * return { data: { deletedPageTitles } };\n * }}\n *\n * // If needed, `cancel={() => ...}` would work similarly\n * >\n * Do you want to delete these pages?\n * <PagesPreviews />\n * </AiTool.Confirmation>\n * </AiTool>\n * ),\n * })\n *\n * @example\n * defineAiTool()({\n * // ...\n * render: () => (\n * <AiTool>\n * <AiTool.Inspector />\n * </AiTool>\n * ),\n * })\n */\nexport const AiTool = Object.assign(\n forwardRef<HTMLDivElement, AiToolProps>(\n (\n {\n children,\n title,\n icon,\n collapsible,\n collapsed,\n onCollapsedChange,\n variant = \"block\",\n className,\n ...props\n },\n forwardedRef\n ) => {\n const {\n stage,\n result,\n name,\n [kInternal]: { execute, messageStatus },\n } = useAiToolInvocationContext();\n // Only mark the tool as pending visually (e.g. show a spinner, add a shimmer animation, etc.)\n // if it has an `execute` method and it isn't in the \"executed\" stage.\n const isVisuallyPending =\n execute !== undefined &&\n stage !== \"executed\" &&\n // If it's in the \"receiving\" stage, we also check that the outer message is still generating.\n (stage === \"receiving\" ? messageStatus === \"generating\" : true);\n const [semiControlledCollapsed, onSemiControlledCollapsed] =\n useSemiControllableState(collapsed ?? false, onCollapsedChange);\n // TODO: This check won't work for cases like:\n // <AiTool>\n // <ComponentThatRendersNull />\n // <ComponentThatAlsoRendersNull />\n // </AiTool>\n // One solution could be to check the DOM on every render with `useLayoutEffect`\n // to see if there's any actual content.\n // For now we're limiting the visual issues caused by the above by using CSS's\n // `:empty` pseudo-class to make the content 0px high if it's actually empty.\n const hasContent = Children.count(children) > 0;\n // If there's no content, the tool is never collapsible.\n const isCollapsible = hasContent ? (collapsible ?? true) : false;\n const resolvedTitle = useMemo(() => {\n return title ?? prettifyString(name);\n }, [title, name]);\n\n // `AiTool` uses \"collapsed\" instead of \"open\" (like the `Composer` component) because \"open\"\n // makes sense next to something called \"Collapsible\" but less so for something called \"AiTool\".\n const handleCollapsibleOpenChange = useCallback(\n (open: boolean) => {\n onSemiControlledCollapsed(!open);\n },\n [onSemiControlledCollapsed]\n );\n\n return (\n <Collapsible.Root\n ref={forwardedRef}\n className={cn(\n \"lb-collapsible lb-ai-tool\",\n `lb-ai-tool:variant-${variant}`,\n className\n )}\n {...props}\n // Regardless of `semiControlledCollapsed`, the collapsible is closed if there's no content.\n open={hasContent ? !semiControlledCollapsed : false}\n onOpenChange={handleCollapsibleOpenChange}\n disabled={!isCollapsible}\n data-result={result?.type}\n data-stage={stage}\n >\n <Collapsible.Trigger\n className={cn(\n \"lb-collapsible-trigger lb-ai-tool-header\",\n variant === \"minimal\" && isVisuallyPending && \"lb-ai-chat-pending\"\n )}\n >\n {icon ? (\n <div className=\"lb-ai-tool-header-icon-container\">{icon}</div>\n ) : null}\n <span className=\"lb-ai-tool-header-title\">{resolvedTitle}</span>\n {isCollapsible ? (\n <span className=\"lb-collapsible-chevron lb-icon-container\">\n <ChevronRightIcon />\n </span>\n ) : null}\n {variant !== \"minimal\" ? (\n <div className=\"lb-ai-tool-header-status\">\n {stage === \"executed\" ? (\n result.type === \"success\" ? (\n <CheckCircleFillIcon />\n ) : result.type === \"error\" ? (\n <CrossCircleFillIcon />\n ) : result.type === \"cancelled\" ? (\n <MinusCircleIcon />\n ) : null\n ) : isVisuallyPending ? (\n <SpinnerIcon />\n ) : null}\n </div>\n ) : null}\n </Collapsible.Trigger>\n\n {hasContent ? (\n <Collapsible.Content className=\"lb-collapsible-content lb-ai-tool-content-container\">\n <div className=\"lb-ai-tool-content\">{children}</div>\n </Collapsible.Content>\n ) : null}\n </Collapsible.Root>\n );\n }\n ),\n {\n /**\n * Display an icon in a container.\n *\n * @example\n * <AiTool\n * icon={\n * <AiTool.Icon>🔍</AiTool.Icon>\n * }\n * />\n */\n Icon: AiToolIcon,\n\n /**\n * Display the tool's arguments and result, which can be useful during\n * development.\n *\n * @example\n * <AiTool>\n * <AiTool.Inspector />\n * </AiTool>\n */\n Inspector: AiToolInspector,\n\n /**\n * Display a human-in-the-loop confirmation step which can be accepted\n * or cancelled by the user.\n *\n * The `confirm` and `cancel` callbacks work like `execute` in tool definitions: they can\n * perform side-effects, be async if needed, and return a result. The tool call will stay\n * pending until either `confirm` or `cancel` is called.\n *\n * @example\n * <AiTool>\n * <AiTool.Confirmation\n * // Use a destructive visual appearance\n * variant=\"destructive\"\n *\n * // The tool's arguments can be directly accessed like in `execute`\n * confirm={({ pageIds }) => {\n * const deletedPageTitles = pages\n * .filter((p) => pageIds.includes(p.id))\n * .map((page) => page.title);\n *\n * deletePages(pageIds);\n *\n * // This result will be available as `result` in the tool's `render` props\n * return { data: { deletedPageTitles } };\n * }}\n *\n * // If needed, `cancel={() => ...}` would work similarly\n * >\n * Do you want to delete these pages?\n * <PagesPreviews />\n * </AiTool.Confirmation>\n * </AiTool>\n */\n Confirmation: AiToolConfirmation,\n }\n);\n"],"names":["jsx","cn","useAiToolInvocationContext","jsxs","CodeBlock","overrides","useOverrides","useMemo","useCallback","Button","forwardRef","kInternal","useSemiControllableState","Children","Collapsible.Root","Collapsible.Trigger","ChevronRightIcon","CheckCircleFillIcon","CrossCircleFillIcon","MinusCircleIcon","SpinnerIcon","Collapsible.Content"],"mappings":"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"}
1
+ {"version":3,"file":"AiTool.cjs","sources":["../../src/components/AiTool.tsx"],"sourcesContent":["import type {\n AiToolExecuteCallback,\n AiToolTypePack,\n JsonObject,\n NoInfr,\n} from \"@liveblocks/core\";\nimport { kInternal } from \"@liveblocks/core\";\nimport type { ComponentProps, ReactNode } from \"react\";\nimport { Children, forwardRef, useCallback, useMemo } from \"react\";\n\nimport { Button } from \"../_private\";\nimport {\n CheckCircleFillIcon,\n ChevronRightIcon,\n CrossCircleFillIcon,\n MinusCircleIcon,\n SpinnerIcon,\n} from \"../icons\";\nimport {\n type AiToolConfirmationOverrides,\n type GlobalOverrides,\n useOverrides,\n} from \"../overrides\";\nimport { useAiToolInvocationContext } from \"../primitives/AiMessage/contexts\";\nimport * as Collapsible from \"../primitives/Collapsible\";\nimport { cn } from \"../utils/cn\";\nimport { useSemiControllableState } from \"../utils/use-controllable-state\";\nimport { CodeBlock } from \"./internal/CodeBlock\";\n\nexport interface AiToolProps\n extends Omit<ComponentProps<\"div\">, \"title\" | \"children\"> {\n /**\n * The tool's title.\n *\n * By default, a human-readable version of the tool's name is used:\n * - `\"showTodo\"` → \"Show todo\"\n * - `\"get_weather\"` → \"Get weather\"\n */\n title?: string;\n\n /**\n * An optional icon displayed next to the title.\n */\n icon?: ReactNode;\n\n /**\n * The content shown in the tool.\n */\n children?: ReactNode;\n\n /**\n * The visual appearance of the tool.\n */\n variant?: \"block\" | \"minimal\";\n\n /**\n * Whether the content is currently collapsed.\n * It is not a traditional controlled value, as in if you set it to `true` it would only stay expanded.\n * Instead, it is \"semi-controlled\", meaning that setting it to `true` will expand it, but it\n * can still be collapsed/expanded by clicking on it.\n */\n collapsed?: boolean;\n\n /**\n * The event handler called when the content is collapsed or expanded by clicking on it.\n */\n onCollapsedChange?: (collapsed: boolean) => void;\n\n /**\n * Whether the content can be collapsed/expanded.\n * If set to `false`, clicking on it will have no effect.\n * If there's no content, this prop has no effect.\n */\n collapsible?: boolean;\n}\n\nexport type AiToolIconProps = ComponentProps<\"div\">;\n\nexport type AiToolInspectorProps = ComponentProps<\"div\">;\n\nexport interface AiToolConfirmationProps<\n A extends JsonObject,\n R extends JsonObject,\n> extends ComponentProps<\"div\"> {\n /**\n * The callback invoked when the user clicks the confirm button.\n */\n confirm: AiToolExecuteCallback<A, R>;\n\n /**\n * The callback invoked when the user clicks the cancel button.\n */\n cancel?: AiToolExecuteCallback<A, R>;\n\n /**\n * The visual appearance.\n */\n variant?: \"default\" | \"destructive\";\n\n /**\n * Override the component's strings.\n */\n overrides?: Partial<GlobalOverrides & AiToolConfirmationOverrides>;\n\n /**\n * The tool's result type, to be used with the `types` prop in the `render` method.\n *\n * @example\n * defineAiTool<{ value: number }>()({\n * // ...\n * render: ({ types }) => (\n * <AiTool.Confirmation\n * types={types}\n * confirm={() => {\n * return {\n * // Using `types` makes the result type-safe\n * // based on the tool's definition\n * data: { value: 123 },\n * };\n * }}\n * />\n * ),\n * })\n */\n types?: NoInfr<AiToolTypePack<A, R>>;\n}\n\nfunction AiToolIcon({ className, ...props }: AiToolIconProps) {\n return <div className={cn(\"lb-ai-tool-icon\", className)} {...props} />;\n}\n\nfunction AiToolInspector({ className, ...props }: AiToolInspectorProps) {\n const { args, partialArgs, result } = useAiToolInvocationContext();\n\n return (\n <div className={cn(\"lb-ai-tool-inspector\", className)} {...props}>\n <CodeBlock\n title=\"Arguments\"\n code={JSON.stringify(args ?? partialArgs, null, 2)}\n />\n {result !== undefined ? (\n <CodeBlock title=\"Result\" code={JSON.stringify(result, null, 2)} />\n ) : null}\n </div>\n );\n}\n\nfunction AiToolConfirmation<\n TPack extends AiToolTypePack,\n A extends JsonObject = TPack[\"A\"],\n R extends JsonObject = TPack[\"R\"],\n>({\n children,\n variant = \"default\",\n confirm,\n cancel,\n overrides,\n className,\n ...props\n}: AiToolConfirmationProps<A, R>) {\n const { stage, args, respond, name, invocationId } =\n useAiToolInvocationContext();\n const $ = useOverrides(overrides);\n\n const enabled = stage === \"executing\";\n\n const context = useMemo(() => ({ name, invocationId }), [name, invocationId]);\n\n const onConfirmClick = useCallback(async () => {\n if (enabled) {\n const result = await confirm(args as A, context);\n respond(result ?? undefined);\n }\n }, [enabled, args, confirm, respond, context]);\n\n const onCancelClick = useCallback(async () => {\n if (enabled) {\n if (cancel === undefined) {\n respond({ cancel: true });\n } else {\n const result = await cancel(args as A, context);\n respond(result ?? undefined);\n }\n }\n }, [enabled, args, cancel, respond, context]);\n\n // If there's no content and the tool has been executed (so there's no\n // confirmation UI displayed either), don't render anything.\n if (stage === \"executed\" && !children) {\n return null;\n }\n\n return (\n <div className={cn(\"lb-ai-tool-confirmation\", className)} {...props}>\n {children ? (\n <div className=\"lb-ai-tool-confirmation-content\">{children}</div>\n ) : null}\n {stage !== \"executed\" && (\n <div className=\"lb-ai-tool-confirmation-footer\">\n <div className=\"lb-ai-tool-confirmation-actions\">\n <Button\n disabled={!enabled}\n onClick={onCancelClick}\n variant=\"secondary\"\n >\n {$.AI_TOOL_CONFIRMATION_CANCEL}\n </Button>\n <Button\n disabled={!enabled}\n onClick={onConfirmClick}\n variant={variant === \"destructive\" ? \"destructive\" : \"primary\"}\n >\n {$.AI_TOOL_CONFIRMATION_CONFIRM}\n </Button>\n </div>\n </div>\n )}\n </div>\n );\n}\n\nfunction prettifyString(string: string) {\n return (\n string\n // Convert camelCase to spaces\n .replace(/([a-z])([A-Z])/g, \"$1 $2\")\n // Convert snake_case and kebab-case to spaces\n .replace(/[_-]+/g, \" \")\n // Collapse multiple following spaces\n .replace(/\\s+/g, \" \")\n // Trim leading and trailing spaces\n .trim()\n // Capitalize first word\n .toLowerCase()\n .replace(/^\\w/, (character) => character.toUpperCase())\n );\n}\n\n/**\n * A pre-built component which displays a tool call.\n *\n * By default, a human-readable version of the tool's name is used as a title:\n * - `\"showTodo\"` → \"Show todo\"\n * - `\"get_weather\"` → \"Get weather\"\n *\n * @example\n * defineAiTool()({\n * // ...\n * render: () => (\n * <AiTool />\n * ),\n * })\n *\n * It can be customized in various ways:\n * - adding an icon\n * - customizing the title (even dynamically)\n * - adding custom content inside it\n * - collapsing it conditionally\n * - etc.\n *\n * @example\n * defineAiTool()({\n * // ...\n * render: ({ stage, result }) => (\n * <AiTool\n * icon=\"🔍\"\n *\n * // Override the default title based on the tool's stage\n * title={stage === \"executing\" ? \"Searching…\" : \"Search results\"}\n *\n * // Start open and automatically collapse after it is executed\n * // The user can still expand/collapse it manually at any time\n * collapsed={stage === \"executed\"}\n * >\n * <SearchResults data={result.data} />\n * </AiTool>\n * ),\n * })\n *\n * It also comes with a few built-in sub-components:\n * - `AiTool.Confirmation` to display a human-in-the-loop confirmation step\n * which can be accepted or cancelled by the user.\n * - `AiTool.Inspector` to display the tool's arguments and result which can\n * be useful during development.\n *\n * @example\n * defineAiTool()({\n * // ...\n * render: () => (\n * <AiTool>\n * <AiTool.Confirmation\n * // Use a destructive visual appearance\n * variant=\"destructive\"\n *\n * // The tool's arguments can be directly accessed like in `execute`\n * confirm={({ pageIds }) => {\n * const deletedPageTitles = pages\n * .filter((p) => pageIds.includes(p.id))\n * .map((page) => page.title);\n *\n * deletePages(pageIds);\n *\n * // This result will be available as `result` in the tool's `render` props\n * return { data: { deletedPageTitles } };\n * }}\n *\n * // If needed, `cancel={() => ...}` would work similarly\n * >\n * Do you want to delete these pages?\n * <PagesPreviews />\n * </AiTool.Confirmation>\n * </AiTool>\n * ),\n * })\n *\n * @example\n * defineAiTool()({\n * // ...\n * render: () => (\n * <AiTool>\n * <AiTool.Inspector />\n * </AiTool>\n * ),\n * })\n */\nexport const AiTool = Object.assign(\n forwardRef<HTMLDivElement, AiToolProps>(\n (\n {\n children,\n title,\n icon,\n collapsible,\n collapsed,\n onCollapsedChange,\n variant = \"block\",\n className,\n ...props\n },\n forwardedRef\n ) => {\n const {\n stage,\n result,\n name,\n [kInternal]: { execute, messageStatus },\n } = useAiToolInvocationContext();\n // Only mark the tool as pending visually (e.g. show a spinner, add a shimmer animation, etc.)\n // if it has an `execute` method and it isn't in the \"executed\" stage.\n const isVisuallyPending =\n execute !== undefined &&\n stage !== \"executed\" &&\n // If it's in the \"receiving\" stage, we also check that the outer message is still generating.\n (stage === \"receiving\" ? messageStatus === \"generating\" : true);\n const [semiControlledCollapsed, onSemiControlledCollapsed] =\n useSemiControllableState(collapsed ?? false, onCollapsedChange);\n // TODO: This check won't work for cases like:\n // <AiTool>\n // <ComponentThatRendersNull />\n // <ComponentThatAlsoRendersNull />\n // </AiTool>\n // One solution could be to check the DOM on every render with `useLayoutEffect`\n // to see if there's any actual content.\n // For now we're limiting the visual issues caused by the above by using CSS's\n // `:empty` pseudo-class to make the content 0px high if it's actually empty.\n const hasContent = Children.count(children) > 0;\n // If there's no content, the tool is never collapsible.\n const isCollapsible = hasContent ? (collapsible ?? true) : false;\n const resolvedTitle = useMemo(() => {\n return title ?? prettifyString(name);\n }, [title, name]);\n\n // `AiTool` uses \"collapsed\" instead of \"open\" (like the `Composer` component) because \"open\"\n // makes sense next to something called \"Collapsible\" but less so for something called \"AiTool\".\n const handleCollapsibleOpenChange = useCallback(\n (open: boolean) => {\n onSemiControlledCollapsed(!open);\n },\n [onSemiControlledCollapsed]\n );\n\n return (\n <Collapsible.Root\n ref={forwardedRef}\n className={cn(\n \"lb-collapsible lb-ai-tool\",\n `lb-ai-tool:variant-${variant}`,\n className\n )}\n {...props}\n // Regardless of `semiControlledCollapsed`, the collapsible is closed if there's no content.\n open={hasContent ? !semiControlledCollapsed : false}\n onOpenChange={handleCollapsibleOpenChange}\n disabled={!isCollapsible}\n data-result={result?.type}\n data-stage={stage}\n >\n <Collapsible.Trigger\n className={cn(\n \"lb-collapsible-trigger lb-ai-tool-header\",\n variant === \"minimal\" && isVisuallyPending && \"lb-ai-chat-pending\"\n )}\n >\n {icon ? (\n <div className=\"lb-ai-tool-header-icon-container\">{icon}</div>\n ) : null}\n <span className=\"lb-ai-tool-header-title\">{resolvedTitle}</span>\n {isCollapsible ? (\n <span className=\"lb-collapsible-chevron lb-icon-container\">\n <ChevronRightIcon />\n </span>\n ) : null}\n {variant !== \"minimal\" ? (\n <div className=\"lb-ai-tool-header-status\">\n {stage === \"executed\" ? (\n result.type === \"success\" ? (\n <CheckCircleFillIcon />\n ) : result.type === \"error\" ? (\n <CrossCircleFillIcon />\n ) : result.type === \"cancelled\" ? (\n <MinusCircleIcon />\n ) : null\n ) : isVisuallyPending ? (\n <SpinnerIcon />\n ) : null}\n </div>\n ) : null}\n </Collapsible.Trigger>\n\n {hasContent ? (\n <Collapsible.Content className=\"lb-collapsible-content lb-ai-tool-content-container\">\n <div className=\"lb-ai-tool-content\">{children}</div>\n </Collapsible.Content>\n ) : null}\n </Collapsible.Root>\n );\n }\n ),\n {\n /**\n * Display an icon in a container.\n *\n * @example\n * <AiTool\n * icon={\n * <AiTool.Icon>🔍</AiTool.Icon>\n * }\n * />\n */\n Icon: AiToolIcon,\n\n /**\n * Display the tool's arguments and result, which can be useful during\n * development.\n *\n * @example\n * <AiTool>\n * <AiTool.Inspector />\n * </AiTool>\n */\n Inspector: AiToolInspector,\n\n /**\n * Display a human-in-the-loop confirmation step which can be accepted\n * or cancelled by the user.\n *\n * The `confirm` and `cancel` callbacks work like `execute` in tool definitions: they can\n * perform side-effects, be async if needed, and return a result. The tool call will stay\n * pending until either `confirm` or `cancel` is called.\n *\n * @example\n * <AiTool>\n * <AiTool.Confirmation\n * // Use a destructive visual appearance\n * variant=\"destructive\"\n *\n * // The tool's arguments can be directly accessed like in `execute`\n * confirm={({ pageIds }) => {\n * const deletedPageTitles = pages\n * .filter((p) => pageIds.includes(p.id))\n * .map((page) => page.title);\n *\n * deletePages(pageIds);\n *\n * // This result will be available as `result` in the tool's `render` props\n * return { data: { deletedPageTitles } };\n * }}\n *\n * // If needed, `cancel={() => ...}` would work similarly\n * >\n * Do you want to delete these pages?\n * <PagesPreviews />\n * </AiTool.Confirmation>\n * </AiTool>\n */\n Confirmation: AiToolConfirmation,\n }\n);\n"],"names":["jsx","cn","useAiToolInvocationContext","jsxs","CodeBlock","overrides","useOverrides","useMemo","useCallback","Button","forwardRef","kInternal","useSemiControllableState","Children","Collapsible.Root","Collapsible.Trigger","ChevronRightIcon","CheckCircleFillIcon","CrossCircleFillIcon","MinusCircleIcon","SpinnerIcon","Collapsible.Content"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"AiTool.js","sources":["../../src/components/AiTool.tsx"],"sourcesContent":["import type {\n AiToolExecuteCallback,\n AiToolTypePack,\n JsonObject,\n NoInfr,\n} from \"@liveblocks/core\";\nimport { kInternal } from \"@liveblocks/core\";\nimport type { ComponentProps, ReactNode } from \"react\";\nimport { Children, forwardRef, useCallback, useMemo } from \"react\";\n\nimport { Button } from \"../_private\";\nimport {\n CheckCircleFillIcon,\n ChevronRightIcon,\n CrossCircleFillIcon,\n MinusCircleIcon,\n SpinnerIcon,\n} from \"../icons\";\nimport {\n type AiToolConfirmationOverrides,\n type GlobalOverrides,\n useOverrides,\n} from \"../overrides\";\nimport { useAiToolInvocationContext } from \"../primitives/AiMessage/contexts\";\nimport * as Collapsible from \"../primitives/Collapsible\";\nimport { cn } from \"../utils/cn\";\nimport { useSemiControllableState } from \"../utils/use-controllable-state\";\nimport { CodeBlock } from \"./internal/CodeBlock\";\n\nexport interface AiToolProps\n extends Omit<ComponentProps<\"div\">, \"title\" | \"children\"> {\n /**\n * The tool's title.\n *\n * By default, a human-readable version of the tool's name is used:\n * - `\"showTodo\"` → \"Show todo\"\n * - `\"get_weather\"` → \"Get weather\"\n */\n title?: ReactNode;\n\n /**\n * An optional icon displayed next to the title.\n */\n icon?: ReactNode;\n\n /**\n * The content shown in the tool.\n */\n children?: ReactNode;\n\n /**\n * The visual appearance of the tool.\n */\n variant?: \"block\" | \"minimal\";\n\n /**\n * Whether the content is currently collapsed.\n * It is not a traditional controlled value, as in if you set it to `true` it would only stay expanded.\n * Instead, it is \"semi-controlled\", meaning that setting it to `true` will expand it, but it\n * can still be collapsed/expanded by clicking on it.\n */\n collapsed?: boolean;\n\n /**\n * The event handler called when the content is collapsed or expanded by clicking on it.\n */\n onCollapsedChange?: (collapsed: boolean) => void;\n\n /**\n * Whether the content can be collapsed/expanded.\n * If set to `false`, clicking on it will have no effect.\n * If there's no content, this prop has no effect.\n */\n collapsible?: boolean;\n}\n\nexport type AiToolIconProps = ComponentProps<\"div\">;\n\nexport type AiToolInspectorProps = ComponentProps<\"div\">;\n\nexport interface AiToolConfirmationProps<\n A extends JsonObject,\n R extends JsonObject,\n> extends ComponentProps<\"div\"> {\n /**\n * The callback invoked when the user clicks the confirm button.\n */\n confirm: AiToolExecuteCallback<A, R>;\n\n /**\n * The callback invoked when the user clicks the cancel button.\n */\n cancel?: AiToolExecuteCallback<A, R>;\n\n /**\n * The visual appearance.\n */\n variant?: \"default\" | \"destructive\";\n\n /**\n * Override the component's strings.\n */\n overrides?: Partial<GlobalOverrides & AiToolConfirmationOverrides>;\n\n /**\n * The tool's result type, to be used with the `types` prop in the `render` method.\n *\n * @example\n * defineAiTool<{ value: number }>()({\n * // ...\n * render: ({ types }) => (\n * <AiTool.Confirmation\n * types={types}\n * confirm={() => {\n * return {\n * // Using `types` makes the result type-safe\n * // based on the tool's definition\n * data: { value: 123 },\n * };\n * }}\n * />\n * ),\n * })\n */\n types?: NoInfr<AiToolTypePack<A, R>>;\n}\n\nfunction AiToolIcon({ className, ...props }: AiToolIconProps) {\n return <div className={cn(\"lb-ai-tool-icon\", className)} {...props} />;\n}\n\nfunction AiToolInspector({ className, ...props }: AiToolInspectorProps) {\n const { args, partialArgs, result } = useAiToolInvocationContext();\n\n return (\n <div className={cn(\"lb-ai-tool-inspector\", className)} {...props}>\n <CodeBlock\n title=\"Arguments\"\n code={JSON.stringify(args ?? partialArgs, null, 2)}\n />\n {result !== undefined ? (\n <CodeBlock title=\"Result\" code={JSON.stringify(result, null, 2)} />\n ) : null}\n </div>\n );\n}\n\nfunction AiToolConfirmation<\n TPack extends AiToolTypePack,\n A extends JsonObject = TPack[\"A\"],\n R extends JsonObject = TPack[\"R\"],\n>({\n children,\n variant = \"default\",\n confirm,\n cancel,\n overrides,\n className,\n ...props\n}: AiToolConfirmationProps<A, R>) {\n const { stage, args, respond, name, invocationId } =\n useAiToolInvocationContext();\n const $ = useOverrides(overrides);\n\n const enabled = stage === \"executing\";\n\n const context = useMemo(() => ({ name, invocationId }), [name, invocationId]);\n\n const onConfirmClick = useCallback(async () => {\n if (enabled) {\n const result = await confirm(args as A, context);\n respond(result ?? undefined);\n }\n }, [enabled, args, confirm, respond, context]);\n\n const onCancelClick = useCallback(async () => {\n if (enabled) {\n if (cancel === undefined) {\n respond({ cancel: true });\n } else {\n const result = await cancel(args as A, context);\n respond(result ?? undefined);\n }\n }\n }, [enabled, args, cancel, respond, context]);\n\n // If there's no content and the tool has been executed (so there's no\n // confirmation UI displayed either), don't render anything.\n if (stage === \"executed\" && !children) {\n return null;\n }\n\n return (\n <div className={cn(\"lb-ai-tool-confirmation\", className)} {...props}>\n {children ? (\n <div className=\"lb-ai-tool-confirmation-content\">{children}</div>\n ) : null}\n {stage !== \"executed\" && (\n <div className=\"lb-ai-tool-confirmation-footer\">\n <div className=\"lb-ai-tool-confirmation-actions\">\n <Button\n disabled={!enabled}\n onClick={onCancelClick}\n variant=\"secondary\"\n >\n {$.AI_TOOL_CONFIRMATION_CANCEL}\n </Button>\n <Button\n disabled={!enabled}\n onClick={onConfirmClick}\n variant={variant === \"destructive\" ? \"destructive\" : \"primary\"}\n >\n {$.AI_TOOL_CONFIRMATION_CONFIRM}\n </Button>\n </div>\n </div>\n )}\n </div>\n );\n}\n\nfunction prettifyString(string: string) {\n return (\n string\n // Convert camelCase to spaces\n .replace(/([a-z])([A-Z])/g, \"$1 $2\")\n // Convert snake_case and kebab-case to spaces\n .replace(/[_-]+/g, \" \")\n // Collapse multiple following spaces\n .replace(/\\s+/g, \" \")\n // Trim leading and trailing spaces\n .trim()\n // Capitalize first word\n .toLowerCase()\n .replace(/^\\w/, (character) => character.toUpperCase())\n );\n}\n\n/**\n * A pre-built component which displays a tool call.\n *\n * By default, a human-readable version of the tool's name is used as a title:\n * - `\"showTodo\"` → \"Show todo\"\n * - `\"get_weather\"` → \"Get weather\"\n *\n * @example\n * defineAiTool()({\n * // ...\n * render: () => (\n * <AiTool />\n * ),\n * })\n *\n * It can be customized in various ways:\n * - adding an icon\n * - customizing the title (even dynamically)\n * - adding custom content inside it\n * - collapsing it conditionally\n * - etc.\n *\n * @example\n * defineAiTool()({\n * // ...\n * render: ({ stage, result }) => (\n * <AiTool\n * icon=\"🔍\"\n *\n * // Override the default title based on the tool's stage\n * title={stage === \"executing\" ? \"Searching…\" : \"Search results\"}\n *\n * // Start open and automatically collapse after it is executed\n * // The user can still expand/collapse it manually at any time\n * collapsed={stage === \"executed\"}\n * >\n * <SearchResults data={result.data} />\n * </AiTool>\n * ),\n * })\n *\n * It also comes with a few built-in sub-components:\n * - `AiTool.Confirmation` to display a human-in-the-loop confirmation step\n * which can be accepted or cancelled by the user.\n * - `AiTool.Inspector` to display the tool's arguments and result which can\n * be useful during development.\n *\n * @example\n * defineAiTool()({\n * // ...\n * render: () => (\n * <AiTool>\n * <AiTool.Confirmation\n * // Use a destructive visual appearance\n * variant=\"destructive\"\n *\n * // The tool's arguments can be directly accessed like in `execute`\n * confirm={({ pageIds }) => {\n * const deletedPageTitles = pages\n * .filter((p) => pageIds.includes(p.id))\n * .map((page) => page.title);\n *\n * deletePages(pageIds);\n *\n * // This result will be available as `result` in the tool's `render` props\n * return { data: { deletedPageTitles } };\n * }}\n *\n * // If needed, `cancel={() => ...}` would work similarly\n * >\n * Do you want to delete these pages?\n * <PagesPreviews />\n * </AiTool.Confirmation>\n * </AiTool>\n * ),\n * })\n *\n * @example\n * defineAiTool()({\n * // ...\n * render: () => (\n * <AiTool>\n * <AiTool.Inspector />\n * </AiTool>\n * ),\n * })\n */\nexport const AiTool = Object.assign(\n forwardRef<HTMLDivElement, AiToolProps>(\n (\n {\n children,\n title,\n icon,\n collapsible,\n collapsed,\n onCollapsedChange,\n variant = \"block\",\n className,\n ...props\n },\n forwardedRef\n ) => {\n const {\n stage,\n result,\n name,\n [kInternal]: { execute, messageStatus },\n } = useAiToolInvocationContext();\n // Only mark the tool as pending visually (e.g. show a spinner, add a shimmer animation, etc.)\n // if it has an `execute` method and it isn't in the \"executed\" stage.\n const isVisuallyPending =\n execute !== undefined &&\n stage !== \"executed\" &&\n // If it's in the \"receiving\" stage, we also check that the outer message is still generating.\n (stage === \"receiving\" ? messageStatus === \"generating\" : true);\n const [semiControlledCollapsed, onSemiControlledCollapsed] =\n useSemiControllableState(collapsed ?? false, onCollapsedChange);\n // TODO: This check won't work for cases like:\n // <AiTool>\n // <ComponentThatRendersNull />\n // <ComponentThatAlsoRendersNull />\n // </AiTool>\n // One solution could be to check the DOM on every render with `useLayoutEffect`\n // to see if there's any actual content.\n // For now we're limiting the visual issues caused by the above by using CSS's\n // `:empty` pseudo-class to make the content 0px high if it's actually empty.\n const hasContent = Children.count(children) > 0;\n // If there's no content, the tool is never collapsible.\n const isCollapsible = hasContent ? (collapsible ?? true) : false;\n const resolvedTitle = useMemo(() => {\n return title ?? prettifyString(name);\n }, [title, name]);\n\n // `AiTool` uses \"collapsed\" instead of \"open\" (like the `Composer` component) because \"open\"\n // makes sense next to something called \"Collapsible\" but less so for something called \"AiTool\".\n const handleCollapsibleOpenChange = useCallback(\n (open: boolean) => {\n onSemiControlledCollapsed(!open);\n },\n [onSemiControlledCollapsed]\n );\n\n return (\n <Collapsible.Root\n ref={forwardedRef}\n className={cn(\n \"lb-collapsible lb-ai-tool\",\n `lb-ai-tool:variant-${variant}`,\n className\n )}\n {...props}\n // Regardless of `semiControlledCollapsed`, the collapsible is closed if there's no content.\n open={hasContent ? !semiControlledCollapsed : false}\n onOpenChange={handleCollapsibleOpenChange}\n disabled={!isCollapsible}\n data-result={result?.type}\n data-stage={stage}\n >\n <Collapsible.Trigger\n className={cn(\n \"lb-collapsible-trigger lb-ai-tool-header\",\n variant === \"minimal\" && isVisuallyPending && \"lb-ai-chat-pending\"\n )}\n >\n {icon ? (\n <div className=\"lb-ai-tool-header-icon-container\">{icon}</div>\n ) : null}\n <span className=\"lb-ai-tool-header-title\">{resolvedTitle}</span>\n {isCollapsible ? (\n <span className=\"lb-collapsible-chevron lb-icon-container\">\n <ChevronRightIcon />\n </span>\n ) : null}\n {variant !== \"minimal\" ? (\n <div className=\"lb-ai-tool-header-status\">\n {stage === \"executed\" ? (\n result.type === \"success\" ? (\n <CheckCircleFillIcon />\n ) : result.type === \"error\" ? (\n <CrossCircleFillIcon />\n ) : result.type === \"cancelled\" ? (\n <MinusCircleIcon />\n ) : null\n ) : isVisuallyPending ? (\n <SpinnerIcon />\n ) : null}\n </div>\n ) : null}\n </Collapsible.Trigger>\n\n {hasContent ? (\n <Collapsible.Content className=\"lb-collapsible-content lb-ai-tool-content-container\">\n <div className=\"lb-ai-tool-content\">{children}</div>\n </Collapsible.Content>\n ) : null}\n </Collapsible.Root>\n );\n }\n ),\n {\n /**\n * Display an icon in a container.\n *\n * @example\n * <AiTool\n * icon={\n * <AiTool.Icon>🔍</AiTool.Icon>\n * }\n * />\n */\n Icon: AiToolIcon,\n\n /**\n * Display the tool's arguments and result, which can be useful during\n * development.\n *\n * @example\n * <AiTool>\n * <AiTool.Inspector />\n * </AiTool>\n */\n Inspector: AiToolInspector,\n\n /**\n * Display a human-in-the-loop confirmation step which can be accepted\n * or cancelled by the user.\n *\n * The `confirm` and `cancel` callbacks work like `execute` in tool definitions: they can\n * perform side-effects, be async if needed, and return a result. The tool call will stay\n * pending until either `confirm` or `cancel` is called.\n *\n * @example\n * <AiTool>\n * <AiTool.Confirmation\n * // Use a destructive visual appearance\n * variant=\"destructive\"\n *\n * // The tool's arguments can be directly accessed like in `execute`\n * confirm={({ pageIds }) => {\n * const deletedPageTitles = pages\n * .filter((p) => pageIds.includes(p.id))\n * .map((page) => page.title);\n *\n * deletePages(pageIds);\n *\n * // This result will be available as `result` in the tool's `render` props\n * return { data: { deletedPageTitles } };\n * }}\n *\n * // If needed, `cancel={() => ...}` would work similarly\n * >\n * Do you want to delete these pages?\n * <PagesPreviews />\n * </AiTool.Confirmation>\n * </AiTool>\n */\n Confirmation: AiToolConfirmation,\n }\n);\n"],"names":["Collapsible.Root","Collapsible.Trigger","Collapsible.Content"],"mappings":"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"}
1
+ {"version":3,"file":"AiTool.js","sources":["../../src/components/AiTool.tsx"],"sourcesContent":["import type {\n AiToolExecuteCallback,\n AiToolTypePack,\n JsonObject,\n NoInfr,\n} from \"@liveblocks/core\";\nimport { kInternal } from \"@liveblocks/core\";\nimport type { ComponentProps, ReactNode } from \"react\";\nimport { Children, forwardRef, useCallback, useMemo } from \"react\";\n\nimport { Button } from \"../_private\";\nimport {\n CheckCircleFillIcon,\n ChevronRightIcon,\n CrossCircleFillIcon,\n MinusCircleIcon,\n SpinnerIcon,\n} from \"../icons\";\nimport {\n type AiToolConfirmationOverrides,\n type GlobalOverrides,\n useOverrides,\n} from \"../overrides\";\nimport { useAiToolInvocationContext } from \"../primitives/AiMessage/contexts\";\nimport * as Collapsible from \"../primitives/Collapsible\";\nimport { cn } from \"../utils/cn\";\nimport { useSemiControllableState } from \"../utils/use-controllable-state\";\nimport { CodeBlock } from \"./internal/CodeBlock\";\n\nexport interface AiToolProps\n extends Omit<ComponentProps<\"div\">, \"title\" | \"children\"> {\n /**\n * The tool's title.\n *\n * By default, a human-readable version of the tool's name is used:\n * - `\"showTodo\"` → \"Show todo\"\n * - `\"get_weather\"` → \"Get weather\"\n */\n title?: string;\n\n /**\n * An optional icon displayed next to the title.\n */\n icon?: ReactNode;\n\n /**\n * The content shown in the tool.\n */\n children?: ReactNode;\n\n /**\n * The visual appearance of the tool.\n */\n variant?: \"block\" | \"minimal\";\n\n /**\n * Whether the content is currently collapsed.\n * It is not a traditional controlled value, as in if you set it to `true` it would only stay expanded.\n * Instead, it is \"semi-controlled\", meaning that setting it to `true` will expand it, but it\n * can still be collapsed/expanded by clicking on it.\n */\n collapsed?: boolean;\n\n /**\n * The event handler called when the content is collapsed or expanded by clicking on it.\n */\n onCollapsedChange?: (collapsed: boolean) => void;\n\n /**\n * Whether the content can be collapsed/expanded.\n * If set to `false`, clicking on it will have no effect.\n * If there's no content, this prop has no effect.\n */\n collapsible?: boolean;\n}\n\nexport type AiToolIconProps = ComponentProps<\"div\">;\n\nexport type AiToolInspectorProps = ComponentProps<\"div\">;\n\nexport interface AiToolConfirmationProps<\n A extends JsonObject,\n R extends JsonObject,\n> extends ComponentProps<\"div\"> {\n /**\n * The callback invoked when the user clicks the confirm button.\n */\n confirm: AiToolExecuteCallback<A, R>;\n\n /**\n * The callback invoked when the user clicks the cancel button.\n */\n cancel?: AiToolExecuteCallback<A, R>;\n\n /**\n * The visual appearance.\n */\n variant?: \"default\" | \"destructive\";\n\n /**\n * Override the component's strings.\n */\n overrides?: Partial<GlobalOverrides & AiToolConfirmationOverrides>;\n\n /**\n * The tool's result type, to be used with the `types` prop in the `render` method.\n *\n * @example\n * defineAiTool<{ value: number }>()({\n * // ...\n * render: ({ types }) => (\n * <AiTool.Confirmation\n * types={types}\n * confirm={() => {\n * return {\n * // Using `types` makes the result type-safe\n * // based on the tool's definition\n * data: { value: 123 },\n * };\n * }}\n * />\n * ),\n * })\n */\n types?: NoInfr<AiToolTypePack<A, R>>;\n}\n\nfunction AiToolIcon({ className, ...props }: AiToolIconProps) {\n return <div className={cn(\"lb-ai-tool-icon\", className)} {...props} />;\n}\n\nfunction AiToolInspector({ className, ...props }: AiToolInspectorProps) {\n const { args, partialArgs, result } = useAiToolInvocationContext();\n\n return (\n <div className={cn(\"lb-ai-tool-inspector\", className)} {...props}>\n <CodeBlock\n title=\"Arguments\"\n code={JSON.stringify(args ?? partialArgs, null, 2)}\n />\n {result !== undefined ? (\n <CodeBlock title=\"Result\" code={JSON.stringify(result, null, 2)} />\n ) : null}\n </div>\n );\n}\n\nfunction AiToolConfirmation<\n TPack extends AiToolTypePack,\n A extends JsonObject = TPack[\"A\"],\n R extends JsonObject = TPack[\"R\"],\n>({\n children,\n variant = \"default\",\n confirm,\n cancel,\n overrides,\n className,\n ...props\n}: AiToolConfirmationProps<A, R>) {\n const { stage, args, respond, name, invocationId } =\n useAiToolInvocationContext();\n const $ = useOverrides(overrides);\n\n const enabled = stage === \"executing\";\n\n const context = useMemo(() => ({ name, invocationId }), [name, invocationId]);\n\n const onConfirmClick = useCallback(async () => {\n if (enabled) {\n const result = await confirm(args as A, context);\n respond(result ?? undefined);\n }\n }, [enabled, args, confirm, respond, context]);\n\n const onCancelClick = useCallback(async () => {\n if (enabled) {\n if (cancel === undefined) {\n respond({ cancel: true });\n } else {\n const result = await cancel(args as A, context);\n respond(result ?? undefined);\n }\n }\n }, [enabled, args, cancel, respond, context]);\n\n // If there's no content and the tool has been executed (so there's no\n // confirmation UI displayed either), don't render anything.\n if (stage === \"executed\" && !children) {\n return null;\n }\n\n return (\n <div className={cn(\"lb-ai-tool-confirmation\", className)} {...props}>\n {children ? (\n <div className=\"lb-ai-tool-confirmation-content\">{children}</div>\n ) : null}\n {stage !== \"executed\" && (\n <div className=\"lb-ai-tool-confirmation-footer\">\n <div className=\"lb-ai-tool-confirmation-actions\">\n <Button\n disabled={!enabled}\n onClick={onCancelClick}\n variant=\"secondary\"\n >\n {$.AI_TOOL_CONFIRMATION_CANCEL}\n </Button>\n <Button\n disabled={!enabled}\n onClick={onConfirmClick}\n variant={variant === \"destructive\" ? \"destructive\" : \"primary\"}\n >\n {$.AI_TOOL_CONFIRMATION_CONFIRM}\n </Button>\n </div>\n </div>\n )}\n </div>\n );\n}\n\nfunction prettifyString(string: string) {\n return (\n string\n // Convert camelCase to spaces\n .replace(/([a-z])([A-Z])/g, \"$1 $2\")\n // Convert snake_case and kebab-case to spaces\n .replace(/[_-]+/g, \" \")\n // Collapse multiple following spaces\n .replace(/\\s+/g, \" \")\n // Trim leading and trailing spaces\n .trim()\n // Capitalize first word\n .toLowerCase()\n .replace(/^\\w/, (character) => character.toUpperCase())\n );\n}\n\n/**\n * A pre-built component which displays a tool call.\n *\n * By default, a human-readable version of the tool's name is used as a title:\n * - `\"showTodo\"` → \"Show todo\"\n * - `\"get_weather\"` → \"Get weather\"\n *\n * @example\n * defineAiTool()({\n * // ...\n * render: () => (\n * <AiTool />\n * ),\n * })\n *\n * It can be customized in various ways:\n * - adding an icon\n * - customizing the title (even dynamically)\n * - adding custom content inside it\n * - collapsing it conditionally\n * - etc.\n *\n * @example\n * defineAiTool()({\n * // ...\n * render: ({ stage, result }) => (\n * <AiTool\n * icon=\"🔍\"\n *\n * // Override the default title based on the tool's stage\n * title={stage === \"executing\" ? \"Searching…\" : \"Search results\"}\n *\n * // Start open and automatically collapse after it is executed\n * // The user can still expand/collapse it manually at any time\n * collapsed={stage === \"executed\"}\n * >\n * <SearchResults data={result.data} />\n * </AiTool>\n * ),\n * })\n *\n * It also comes with a few built-in sub-components:\n * - `AiTool.Confirmation` to display a human-in-the-loop confirmation step\n * which can be accepted or cancelled by the user.\n * - `AiTool.Inspector` to display the tool's arguments and result which can\n * be useful during development.\n *\n * @example\n * defineAiTool()({\n * // ...\n * render: () => (\n * <AiTool>\n * <AiTool.Confirmation\n * // Use a destructive visual appearance\n * variant=\"destructive\"\n *\n * // The tool's arguments can be directly accessed like in `execute`\n * confirm={({ pageIds }) => {\n * const deletedPageTitles = pages\n * .filter((p) => pageIds.includes(p.id))\n * .map((page) => page.title);\n *\n * deletePages(pageIds);\n *\n * // This result will be available as `result` in the tool's `render` props\n * return { data: { deletedPageTitles } };\n * }}\n *\n * // If needed, `cancel={() => ...}` would work similarly\n * >\n * Do you want to delete these pages?\n * <PagesPreviews />\n * </AiTool.Confirmation>\n * </AiTool>\n * ),\n * })\n *\n * @example\n * defineAiTool()({\n * // ...\n * render: () => (\n * <AiTool>\n * <AiTool.Inspector />\n * </AiTool>\n * ),\n * })\n */\nexport const AiTool = Object.assign(\n forwardRef<HTMLDivElement, AiToolProps>(\n (\n {\n children,\n title,\n icon,\n collapsible,\n collapsed,\n onCollapsedChange,\n variant = \"block\",\n className,\n ...props\n },\n forwardedRef\n ) => {\n const {\n stage,\n result,\n name,\n [kInternal]: { execute, messageStatus },\n } = useAiToolInvocationContext();\n // Only mark the tool as pending visually (e.g. show a spinner, add a shimmer animation, etc.)\n // if it has an `execute` method and it isn't in the \"executed\" stage.\n const isVisuallyPending =\n execute !== undefined &&\n stage !== \"executed\" &&\n // If it's in the \"receiving\" stage, we also check that the outer message is still generating.\n (stage === \"receiving\" ? messageStatus === \"generating\" : true);\n const [semiControlledCollapsed, onSemiControlledCollapsed] =\n useSemiControllableState(collapsed ?? false, onCollapsedChange);\n // TODO: This check won't work for cases like:\n // <AiTool>\n // <ComponentThatRendersNull />\n // <ComponentThatAlsoRendersNull />\n // </AiTool>\n // One solution could be to check the DOM on every render with `useLayoutEffect`\n // to see if there's any actual content.\n // For now we're limiting the visual issues caused by the above by using CSS's\n // `:empty` pseudo-class to make the content 0px high if it's actually empty.\n const hasContent = Children.count(children) > 0;\n // If there's no content, the tool is never collapsible.\n const isCollapsible = hasContent ? (collapsible ?? true) : false;\n const resolvedTitle = useMemo(() => {\n return title ?? prettifyString(name);\n }, [title, name]);\n\n // `AiTool` uses \"collapsed\" instead of \"open\" (like the `Composer` component) because \"open\"\n // makes sense next to something called \"Collapsible\" but less so for something called \"AiTool\".\n const handleCollapsibleOpenChange = useCallback(\n (open: boolean) => {\n onSemiControlledCollapsed(!open);\n },\n [onSemiControlledCollapsed]\n );\n\n return (\n <Collapsible.Root\n ref={forwardedRef}\n className={cn(\n \"lb-collapsible lb-ai-tool\",\n `lb-ai-tool:variant-${variant}`,\n className\n )}\n {...props}\n // Regardless of `semiControlledCollapsed`, the collapsible is closed if there's no content.\n open={hasContent ? !semiControlledCollapsed : false}\n onOpenChange={handleCollapsibleOpenChange}\n disabled={!isCollapsible}\n data-result={result?.type}\n data-stage={stage}\n >\n <Collapsible.Trigger\n className={cn(\n \"lb-collapsible-trigger lb-ai-tool-header\",\n variant === \"minimal\" && isVisuallyPending && \"lb-ai-chat-pending\"\n )}\n >\n {icon ? (\n <div className=\"lb-ai-tool-header-icon-container\">{icon}</div>\n ) : null}\n <span className=\"lb-ai-tool-header-title\">{resolvedTitle}</span>\n {isCollapsible ? (\n <span className=\"lb-collapsible-chevron lb-icon-container\">\n <ChevronRightIcon />\n </span>\n ) : null}\n {variant !== \"minimal\" ? (\n <div className=\"lb-ai-tool-header-status\">\n {stage === \"executed\" ? (\n result.type === \"success\" ? (\n <CheckCircleFillIcon />\n ) : result.type === \"error\" ? (\n <CrossCircleFillIcon />\n ) : result.type === \"cancelled\" ? (\n <MinusCircleIcon />\n ) : null\n ) : isVisuallyPending ? (\n <SpinnerIcon />\n ) : null}\n </div>\n ) : null}\n </Collapsible.Trigger>\n\n {hasContent ? (\n <Collapsible.Content className=\"lb-collapsible-content lb-ai-tool-content-container\">\n <div className=\"lb-ai-tool-content\">{children}</div>\n </Collapsible.Content>\n ) : null}\n </Collapsible.Root>\n );\n }\n ),\n {\n /**\n * Display an icon in a container.\n *\n * @example\n * <AiTool\n * icon={\n * <AiTool.Icon>🔍</AiTool.Icon>\n * }\n * />\n */\n Icon: AiToolIcon,\n\n /**\n * Display the tool's arguments and result, which can be useful during\n * development.\n *\n * @example\n * <AiTool>\n * <AiTool.Inspector />\n * </AiTool>\n */\n Inspector: AiToolInspector,\n\n /**\n * Display a human-in-the-loop confirmation step which can be accepted\n * or cancelled by the user.\n *\n * The `confirm` and `cancel` callbacks work like `execute` in tool definitions: they can\n * perform side-effects, be async if needed, and return a result. The tool call will stay\n * pending until either `confirm` or `cancel` is called.\n *\n * @example\n * <AiTool>\n * <AiTool.Confirmation\n * // Use a destructive visual appearance\n * variant=\"destructive\"\n *\n * // The tool's arguments can be directly accessed like in `execute`\n * confirm={({ pageIds }) => {\n * const deletedPageTitles = pages\n * .filter((p) => pageIds.includes(p.id))\n * .map((page) => page.title);\n *\n * deletePages(pageIds);\n *\n * // This result will be available as `result` in the tool's `render` props\n * return { data: { deletedPageTitles } };\n * }}\n *\n * // If needed, `cancel={() => ...}` would work similarly\n * >\n * Do you want to delete these pages?\n * <PagesPreviews />\n * </AiTool.Confirmation>\n * </AiTool>\n */\n Confirmation: AiToolConfirmation,\n }\n);\n"],"names":["Collapsible.Root","Collapsible.Trigger","Collapsible.Content"],"mappings":"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"}
package/dist/index.d.cts CHANGED
@@ -535,7 +535,7 @@ interface AiToolProps extends Omit<ComponentProps<"div">, "title" | "children">
535
535
  * - `"showTodo"` → "Show todo"
536
536
  * - `"get_weather"` → "Get weather"
537
537
  */
538
- title?: ReactNode;
538
+ title?: string;
539
539
  /**
540
540
  * An optional icon displayed next to the title.
541
541
  */
package/dist/index.d.ts CHANGED
@@ -535,7 +535,7 @@ interface AiToolProps extends Omit<ComponentProps<"div">, "title" | "children">
535
535
  * - `"showTodo"` → "Show todo"
536
536
  * - `"get_weather"` → "Get weather"
537
537
  */
538
- title?: ReactNode;
538
+ title?: string;
539
539
  /**
540
540
  * An optional icon displayed next to the title.
541
541
  */
package/dist/version.cjs CHANGED
@@ -1,7 +1,7 @@
1
1
  'use strict';
2
2
 
3
3
  const PKG_NAME = "@liveblocks/react-ui";
4
- const PKG_VERSION = typeof "3.8.0-next4" === "string" && "3.8.0-next4";
4
+ const PKG_VERSION = typeof "3.8.0-tiptap1" === "string" && "3.8.0-tiptap1";
5
5
  const PKG_FORMAT = typeof "cjs" === "string" && "cjs";
6
6
 
7
7
  exports.PKG_FORMAT = PKG_FORMAT;
@@ -1 +1 @@
1
- {"version":3,"file":"version.cjs","sources":["../src/version.ts"],"sourcesContent":["declare const __VERSION__: string;\ndeclare const ROLLUP_FORMAT: string;\n\nexport const PKG_NAME = \"@liveblocks/react-ui\";\nexport const PKG_VERSION = typeof __VERSION__ === \"string\" && __VERSION__;\nexport const PKG_FORMAT = typeof ROLLUP_FORMAT === \"string\" && ROLLUP_FORMAT;\n"],"names":[],"mappings":";;AAGO,MAAM,QAAW,GAAA,uBAAA;AACX,MAAA,WAAA,GAAc,OAAO,aAAA,KAAgB,QAAY,IAAA,cAAA;AACjD,MAAA,UAAA,GAAa,OAAO,KAAA,KAAkB,QAAY,IAAA;;;;;;"}
1
+ {"version":3,"file":"version.cjs","sources":["../src/version.ts"],"sourcesContent":["declare const __VERSION__: string;\ndeclare const ROLLUP_FORMAT: string;\n\nexport const PKG_NAME = \"@liveblocks/react-ui\";\nexport const PKG_VERSION = typeof __VERSION__ === \"string\" && __VERSION__;\nexport const PKG_FORMAT = typeof ROLLUP_FORMAT === \"string\" && ROLLUP_FORMAT;\n"],"names":[],"mappings":";;AAGO,MAAM,QAAW,GAAA,uBAAA;AACX,MAAA,WAAA,GAAc,OAAO,eAAA,KAAgB,QAAY,IAAA,gBAAA;AACjD,MAAA,UAAA,GAAa,OAAO,KAAA,KAAkB,QAAY,IAAA;;;;;;"}
package/dist/version.js CHANGED
@@ -1,5 +1,5 @@
1
1
  const PKG_NAME = "@liveblocks/react-ui";
2
- const PKG_VERSION = typeof "3.8.0-next4" === "string" && "3.8.0-next4";
2
+ const PKG_VERSION = typeof "3.8.0-tiptap1" === "string" && "3.8.0-tiptap1";
3
3
  const PKG_FORMAT = typeof "esm" === "string" && "esm";
4
4
 
5
5
  export { PKG_FORMAT, PKG_NAME, PKG_VERSION };
@@ -1 +1 @@
1
- {"version":3,"file":"version.js","sources":["../src/version.ts"],"sourcesContent":["declare const __VERSION__: string;\ndeclare const ROLLUP_FORMAT: string;\n\nexport const PKG_NAME = \"@liveblocks/react-ui\";\nexport const PKG_VERSION = typeof __VERSION__ === \"string\" && __VERSION__;\nexport const PKG_FORMAT = typeof ROLLUP_FORMAT === \"string\" && ROLLUP_FORMAT;\n"],"names":[],"mappings":"AAGO,MAAM,QAAW,GAAA,uBAAA;AACX,MAAA,WAAA,GAAc,OAAO,aAAA,KAAgB,QAAY,IAAA,cAAA;AACjD,MAAA,UAAA,GAAa,OAAO,KAAA,KAAkB,QAAY,IAAA;;;;"}
1
+ {"version":3,"file":"version.js","sources":["../src/version.ts"],"sourcesContent":["declare const __VERSION__: string;\ndeclare const ROLLUP_FORMAT: string;\n\nexport const PKG_NAME = \"@liveblocks/react-ui\";\nexport const PKG_VERSION = typeof __VERSION__ === \"string\" && __VERSION__;\nexport const PKG_FORMAT = typeof ROLLUP_FORMAT === \"string\" && ROLLUP_FORMAT;\n"],"names":[],"mappings":"AAGO,MAAM,QAAW,GAAA,uBAAA;AACX,MAAA,WAAA,GAAc,OAAO,eAAA,KAAgB,QAAY,IAAA,gBAAA;AACjD,MAAA,UAAA,GAAa,OAAO,KAAA,KAAkB,QAAY,IAAA;;;;"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@liveblocks/react-ui",
3
- "version": "3.8.0-next4",
3
+ "version": "3.8.0-tiptap1",
4
4
  "description": "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.",
5
5
  "license": "Apache-2.0",
6
6
  "type": "module",
@@ -76,9 +76,9 @@
76
76
  },
77
77
  "dependencies": {
78
78
  "@floating-ui/react-dom": "^2.1.2",
79
- "@liveblocks/client": "3.8.0-next4",
80
- "@liveblocks/core": "3.8.0-next4",
81
- "@liveblocks/react": "3.8.0-next4",
79
+ "@liveblocks/client": "3.8.0-tiptap1",
80
+ "@liveblocks/core": "3.8.0-tiptap1",
81
+ "@liveblocks/react": "3.8.0-tiptap1",
82
82
  "@radix-ui/react-dropdown-menu": "^2.1.2",
83
83
  "@radix-ui/react-popover": "^1.1.2",
84
84
  "@radix-ui/react-slot": "^1.1.0",