@liveblocks/react-ui 2.25.0-aiprivatebeta10 → 2.25.0-aiprivatebeta11
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.
- package/dist/components/AiChat.cjs +10 -16
- package/dist/components/AiChat.cjs.map +1 -1
- package/dist/components/AiChat.js +11 -17
- package/dist/components/AiChat.js.map +1 -1
- package/dist/primitives/AiMessage/index.cjs +1 -1
- package/dist/primitives/AiMessage/index.cjs.map +1 -1
- package/dist/primitives/AiMessage/index.js +1 -1
- package/dist/primitives/AiMessage/index.js.map +1 -1
- package/dist/utils/use-visible.cjs +3 -1
- package/dist/utils/use-visible.cjs.map +1 -1
- package/dist/utils/use-visible.js +3 -1
- package/dist/utils/use-visible.js.map +1 -1
- package/dist/version.cjs +1 -1
- package/dist/version.js +1 -1
- package/package.json +4 -4
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
var jsxRuntime = require('react/jsx-runtime');
|
|
4
|
-
var core = require('@liveblocks/core');
|
|
5
4
|
var react$1 = require('@liveblocks/react');
|
|
6
5
|
var _private = require('@liveblocks/react/_private');
|
|
7
6
|
var react = require('react');
|
|
@@ -41,30 +40,20 @@ const AiChat = react.forwardRef(
|
|
|
41
40
|
const Loading = components?.Loading ?? defaultComponents.Loading;
|
|
42
41
|
const containerRef = react.useRef(null);
|
|
43
42
|
const containerBottomRef = react.useRef(null);
|
|
43
|
+
const isScrollIndicatorEnabled = !isLoading && !error;
|
|
44
44
|
const isScrollAtBottom = useVisible.useVisible(containerBottomRef, {
|
|
45
|
-
enabled:
|
|
45
|
+
enabled: isScrollIndicatorEnabled,
|
|
46
46
|
root: containerRef,
|
|
47
|
-
rootMargin: MIN_DISTANCE_BOTTOM_SCROLL_INDICATOR
|
|
47
|
+
rootMargin: MIN_DISTANCE_BOTTOM_SCROLL_INDICATOR,
|
|
48
|
+
initialValue: null
|
|
48
49
|
});
|
|
49
|
-
const isScrollIndicatorVisible =
|
|
50
|
-
const client = react$1.useClient();
|
|
51
|
-
const ai = client[core.kInternal].ai;
|
|
50
|
+
const isScrollIndicatorVisible = isScrollIndicatorEnabled && isScrollAtBottom !== null ? !isScrollAtBottom : false;
|
|
52
51
|
const [lastSentMessageId, setLastSentMessageId] = react.useState(null);
|
|
53
52
|
react.useImperativeHandle(
|
|
54
53
|
forwardedRef,
|
|
55
54
|
() => containerRef.current,
|
|
56
55
|
[]
|
|
57
56
|
);
|
|
58
|
-
react.useEffect(() => {
|
|
59
|
-
Object.entries(tools).map(
|
|
60
|
-
([key, value]) => ai.registerChatTool(chatId, key, value)
|
|
61
|
-
);
|
|
62
|
-
return () => {
|
|
63
|
-
Object.entries(tools).map(
|
|
64
|
-
([key]) => ai.unregisterChatTool(chatId, key)
|
|
65
|
-
);
|
|
66
|
-
};
|
|
67
|
-
}, [ai, chatId, tools]);
|
|
68
57
|
const scrollToBottomCallbackRef = react.useRef(void 0);
|
|
69
58
|
if (scrollToBottomCallbackRef.current === void 0) {
|
|
70
59
|
scrollToBottomCallbackRef.current = function(behavior) {
|
|
@@ -91,6 +80,11 @@ const AiChat = react.forwardRef(
|
|
|
91
80
|
description: source.description,
|
|
92
81
|
value: source.value
|
|
93
82
|
}, index)) : null,
|
|
83
|
+
Object.entries(tools).map(([name, tool]) => /* @__PURE__ */ jsxRuntime.jsx(react$1.RegisterAiTool, {
|
|
84
|
+
chatId,
|
|
85
|
+
name,
|
|
86
|
+
tool
|
|
87
|
+
}, name)),
|
|
94
88
|
/* @__PURE__ */ jsxRuntime.jsx("div", {
|
|
95
89
|
className: "lb-ai-chat-content",
|
|
96
90
|
children: isLoading ? /* @__PURE__ */ jsxRuntime.jsx(Loading, {}) : error !== void 0 ? /* @__PURE__ */ jsxRuntime.jsx("div", {
|
|
@@ -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 { kInternal } from \"@liveblocks/core\";\nimport {\n RegisterAiKnowledge,\n useAiChatMessages,\n useClient,\n} from \"@liveblocks/react\";\nimport { useLayoutEffect } from \"@liveblocks/react/_private\";\nimport {\n type ComponentProps,\n type ComponentType,\n forwardRef,\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 AiChatComposerOverrides,\n type AiChatMessageOverrides,\n type AiChatOverrides,\n type GlobalOverrides,\n useOverrides,\n} from \"../overrides\";\nimport { classNames } from \"../utils/class-names\";\nimport { useVisible } from \"../utils/use-visible\";\nimport { AiChatAssistantMessage } from \"./internal/AiChatAssistantMessage\";\nimport { AiChatComposer } from \"./internal/AiChatComposer\";\nimport { AiChatUserMessage } from \"./internal/AiChatUserMessage\";\n\nexport type AiChatComponentsEmptyProps = {\n /**\n * The chat id provided to the `AiChat` component.\n */\n chatId: string;\n /**\n * The copilot id provided to the `AiChat` component.\n */\n copilotId?: string;\n};\n\nexport type AiChatComponentsLoadingProps = Record<string, never>;\n\n// TODO: Add Markdown components\nexport type AiChatComponents = {\n /**\n * The component used to render the empty state of the chat.\n */\n Empty: ComponentType<AiChatComponentsEmptyProps>;\n /**\n * The component used to render the loading state of the chat.\n */\n Loading: ComponentType<AiChatComponentsLoadingProps>;\n};\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 = 50;\n\nexport interface AiChatProps extends ComponentProps<\"div\"> {\n /**\n * The id of the chat the composer belongs to.\n */\n chatId: string;\n /**\n * Whether to focus the chat composer on mount.\n */\n autoFocus?: boolean;\n /**\n * The id of the copilot to use to send the message.\n */\n copilotId?: string;\n /**\n * The contextual knowledge to include in the chat. May be used by the assistant when generating responses.\n * Any knowledge you provide via this prop will be added to any already globally registered knowledge via <RegisterAiKnowledge />.\n */\n knowledge?: AiKnowledgeSource[];\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 * The layout of the chat and its composer.\n */\n layout?: \"inset\" | \"compact\";\n /**\n * Override the component's strings.\n */\n overrides?: Partial<\n GlobalOverrides &\n AiChatMessageOverrides &\n AiChatComposerOverrides &\n AiChatOverrides\n >;\n /**\n * Override the component's components.\n */\n components?: Partial<GlobalComponents & AiChatComponents>;\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\nexport const AiChat = forwardRef<HTMLDivElement, AiChatProps>(\n (\n {\n chatId,\n copilotId,\n autoFocus,\n overrides,\n knowledge,\n tools = {},\n layout = \"inset\",\n components,\n className,\n ...props\n },\n forwardedRef\n ) => {\n const { messages, isLoading, error } = useAiChatMessages(chatId);\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 containerBottomRef = useRef<HTMLDivElement | null>(null);\n const isScrollAtBottom = useVisible(containerBottomRef, {\n enabled: !isLoading && !error,\n root: containerRef,\n rootMargin: MIN_DISTANCE_BOTTOM_SCROLL_INDICATOR,\n });\n const isScrollIndicatorVisible =\n isLoading || error ? false : !isScrollAtBottom;\n\n const client = useClient();\n const ai = client[kInternal].ai;\n\n const [lastSentMessageId, setLastSentMessageId] =\n useState<MessageId | null>(null);\n\n useImperativeHandle<HTMLDivElement | null, HTMLDivElement | null>(\n forwardedRef,\n () => containerRef.current,\n []\n );\n\n // Register the provided tools to the chat on mount and unregister them on unmount\n useEffect(() => {\n Object.entries(tools).map(([key, value]) =>\n ai.registerChatTool(chatId, key, value)\n );\n return () => {\n Object.entries(tools).map(([key]) =>\n ai.unregisterChatTool(chatId, key)\n );\n };\n }, [ai, chatId, tools]);\n\n const scrollToBottomCallbackRef =\n useRef<(behavior: \"instant\" | \"smooth\") => void>(undefined);\n if (scrollToBottomCallbackRef.current === undefined) {\n scrollToBottomCallbackRef.current = function (\n behavior: \"instant\" | \"smooth\"\n ) {\n const container = containerRef.current;\n if (container === null) return;\n\n container.scrollTo({\n top: container.scrollHeight,\n behavior,\n });\n };\n }\n const scrollToBottom = scrollToBottomCallbackRef.current;\n\n return (\n <div\n ref={containerRef}\n {...props}\n className={classNames(\n \"lb-root lb-ai-chat\",\n layout === \"compact\"\n ? \"lb-ai-chat:layout-compact\"\n : \"lb-ai-chat:layout-inset\",\n className\n )}\n >\n {knowledge\n ? knowledge.map((source, index) => (\n <RegisterAiKnowledge\n key={index}\n description={source.description}\n value={source.value}\n // knowledgeKey={source.knowledgeKey}\n />\n ))\n : null}\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 <AutoScrollHandler\n lastSentMessageId={lastSentMessageId}\n scrollToBottom={scrollToBottom}\n />\n <div className=\"lb-ai-chat-messages\">\n {messages.map((message) => {\n if (message.role === \"user\") {\n return (\n <AiChatUserMessage\n key={message.id}\n message={message}\n overrides={overrides}\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 </div>\n\n <div className=\"lb-ai-chat-footer\">\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 disabled={!isScrollIndicatorVisible}\n onClick={() => scrollToBottom(\"smooth\")}\n >\n <span className=\"lb-icon-container\">\n <ArrowDownIcon />\n </span>\n </button>\n </div>\n </div>\n <AiChatComposer\n key={chatId}\n chatId={chatId}\n copilotId={copilotId as CopilotId}\n overrides={$}\n autoFocus={autoFocus}\n onUserMessageCreate={({ id }) => setLastSentMessageId(id)}\n className={\n layout === \"inset\"\n ? \"lb-elevation lb-elevation-moderate\"\n : undefined\n }\n />\n </div>\n {/* This invisible element 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 fully scrolled.\n */}\n <div\n ref={containerBottomRef}\n style={{ position: \"sticky\", height: 0 }}\n aria-hidden\n data-scroll-at-bottom={isScrollAtBottom ? \"\" : undefined}\n />\n </div>\n );\n }\n);\n\nfunction AutoScrollHandler({\n lastSentMessageId,\n scrollToBottom,\n}: {\n lastSentMessageId: MessageId | null;\n scrollToBottom: (behavior: \"instant\" | \"smooth\") => void;\n}) {\n // Scroll to bottom when the component first mounts\n useLayoutEffect(() => {\n scrollToBottom(\"instant\");\n }, [scrollToBottom]);\n\n // Scroll to bottom when sending a new message\n useEffect(() => {\n scrollToBottom(\"smooth\");\n }, [lastSentMessageId, scrollToBottom]);\n\n return null;\n}\n"],"names":["jsx","SpinnerIcon","forwardRef","overrides","useAiChatMessages","useOverrides","useRef","useVisible","useClient","kInternal","useState","useImperativeHandle","useEffect","jsxs","classNames","RegisterAiKnowledge","Fragment","AiChatUserMessage","AiChatAssistantMessage","ArrowDownIcon","AiChatComposer","useLayoutEffect"],"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 {\n RegisterAiKnowledge,\n RegisterAiTool,\n useAiChatMessages,\n} from \"@liveblocks/react\";\nimport { useLayoutEffect } from \"@liveblocks/react/_private\";\nimport {\n type ComponentProps,\n type ComponentType,\n forwardRef,\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 AiChatComposerOverrides,\n type AiChatMessageOverrides,\n type AiChatOverrides,\n type GlobalOverrides,\n useOverrides,\n} from \"../overrides\";\nimport { classNames } from \"../utils/class-names\";\nimport { useVisible } from \"../utils/use-visible\";\nimport { AiChatAssistantMessage } from \"./internal/AiChatAssistantMessage\";\nimport { AiChatComposer } from \"./internal/AiChatComposer\";\nimport { AiChatUserMessage } from \"./internal/AiChatUserMessage\";\n\nexport type AiChatComponentsEmptyProps = {\n /**\n * The chat id provided to the `AiChat` component.\n */\n chatId: string;\n /**\n * The copilot id provided to the `AiChat` component.\n */\n copilotId?: string;\n};\n\nexport type AiChatComponentsLoadingProps = Record<string, never>;\n\n// TODO: Add Markdown components\nexport type AiChatComponents = {\n /**\n * The component used to render the empty state of the chat.\n */\n Empty: ComponentType<AiChatComponentsEmptyProps>;\n /**\n * The component used to render the loading state of the chat.\n */\n Loading: ComponentType<AiChatComponentsLoadingProps>;\n};\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 = 50;\n\nexport interface AiChatProps extends ComponentProps<\"div\"> {\n /**\n * The id of the chat the composer belongs to.\n */\n chatId: string;\n /**\n * Whether to focus the chat composer on mount.\n */\n autoFocus?: boolean;\n /**\n * The id of the copilot to use to send the message.\n */\n copilotId?: string;\n /**\n * The contextual knowledge to include in the chat. May be used by the assistant when generating responses.\n * Any knowledge you provide via this prop will be added to any already globally registered knowledge via <RegisterAiKnowledge />.\n */\n knowledge?: AiKnowledgeSource[];\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 * The layout of the chat and its composer.\n */\n layout?: \"inset\" | \"compact\";\n /**\n * Override the component's strings.\n */\n overrides?: Partial<\n GlobalOverrides &\n AiChatMessageOverrides &\n AiChatComposerOverrides &\n AiChatOverrides\n >;\n /**\n * Override the component's components.\n */\n components?: Partial<GlobalComponents & AiChatComponents>;\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\nexport const AiChat = forwardRef<HTMLDivElement, AiChatProps>(\n (\n {\n chatId,\n copilotId,\n autoFocus,\n overrides,\n knowledge,\n tools = {},\n layout = \"inset\",\n components,\n className,\n ...props\n },\n forwardedRef\n ) => {\n const { messages, isLoading, error } = useAiChatMessages(chatId);\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 containerBottomRef = useRef<HTMLDivElement | null>(null);\n const isScrollIndicatorEnabled = !isLoading && !error;\n const isScrollAtBottom = useVisible(containerBottomRef, {\n enabled: isScrollIndicatorEnabled,\n root: containerRef,\n rootMargin: MIN_DISTANCE_BOTTOM_SCROLL_INDICATOR,\n initialValue: null,\n });\n const isScrollIndicatorVisible =\n isScrollIndicatorEnabled && isScrollAtBottom !== null\n ? !isScrollAtBottom\n : false;\n\n const [lastSentMessageId, setLastSentMessageId] =\n useState<MessageId | null>(null);\n\n useImperativeHandle<HTMLDivElement | null, HTMLDivElement | null>(\n forwardedRef,\n () => containerRef.current,\n []\n );\n\n const scrollToBottomCallbackRef =\n useRef<(behavior: \"instant\" | \"smooth\") => void>(undefined);\n if (scrollToBottomCallbackRef.current === undefined) {\n scrollToBottomCallbackRef.current = function (\n behavior: \"instant\" | \"smooth\"\n ) {\n const container = containerRef.current;\n if (container === null) return;\n\n container.scrollTo({\n top: container.scrollHeight,\n behavior,\n });\n };\n }\n const scrollToBottom = scrollToBottomCallbackRef.current;\n\n return (\n <div\n ref={containerRef}\n {...props}\n className={classNames(\n \"lb-root lb-ai-chat\",\n layout === \"compact\"\n ? \"lb-ai-chat:layout-compact\"\n : \"lb-ai-chat:layout-inset\",\n className\n )}\n >\n {knowledge\n ? knowledge.map((source, index) => (\n <RegisterAiKnowledge\n key={index}\n description={source.description}\n value={source.value}\n // knowledgeKey={source.knowledgeKey}\n />\n ))\n : null}\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 <AutoScrollHandler\n lastSentMessageId={lastSentMessageId}\n scrollToBottom={scrollToBottom}\n />\n <div className=\"lb-ai-chat-messages\">\n {messages.map((message) => {\n if (message.role === \"user\") {\n return (\n <AiChatUserMessage\n key={message.id}\n message={message}\n overrides={overrides}\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 </div>\n\n <div className=\"lb-ai-chat-footer\">\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 disabled={!isScrollIndicatorVisible}\n onClick={() => scrollToBottom(\"smooth\")}\n >\n <span className=\"lb-icon-container\">\n <ArrowDownIcon />\n </span>\n </button>\n </div>\n </div>\n <AiChatComposer\n key={chatId}\n chatId={chatId}\n copilotId={copilotId as CopilotId}\n overrides={$}\n autoFocus={autoFocus}\n onUserMessageCreate={({ id }) => setLastSentMessageId(id)}\n className={\n layout === \"inset\"\n ? \"lb-elevation lb-elevation-moderate\"\n : undefined\n }\n />\n </div>\n {/* This invisible element 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 fully scrolled.\n */}\n <div\n ref={containerBottomRef}\n style={{ position: \"sticky\", height: 0 }}\n aria-hidden\n data-scroll-at-bottom={isScrollAtBottom ? \"\" : undefined}\n />\n </div>\n );\n }\n);\n\nfunction AutoScrollHandler({\n lastSentMessageId,\n scrollToBottom,\n}: {\n lastSentMessageId: MessageId | null;\n scrollToBottom: (behavior: \"instant\" | \"smooth\") => void;\n}) {\n // Scroll to bottom when the component first mounts\n useLayoutEffect(() => {\n scrollToBottom(\"instant\");\n }, [scrollToBottom]);\n\n // Scroll to bottom when sending a new message\n useEffect(() => {\n scrollToBottom(\"smooth\");\n }, [lastSentMessageId, scrollToBottom]);\n\n return null;\n}\n"],"names":["jsx","SpinnerIcon","forwardRef","overrides","useAiChatMessages","useOverrides","useRef","useVisible","useState","useImperativeHandle","jsxs","classNames","RegisterAiKnowledge","RegisterAiTool","Fragment","AiChatUserMessage","AiChatAssistantMessage","ArrowDownIcon","AiChatComposer","useLayoutEffect","useEffect"],"mappings":";;;;;;;;;;;;;;;AAmEA,MAAM,oCAAuC,GAAA,EAAA,CAAA;AA2C7C,MAAM,iBAAsC,GAAA;AAAA,EAC1C,OAAO,MAAM,IAAA;AAAA,EACb,OAAA,EAAS,sBACNA,cAAA,CAAA,KAAA,EAAA;AAAA,IAAI,SAAU,EAAA,+BAAA;AAAA,IACb,yCAACC,mBAAY,EAAA,EAAA,CAAA;AAAA,GACf,CAAA;AAEJ,CAAA,CAAA;AAEO,MAAM,MAAS,GAAAC,gBAAA;AAAA,EACpB,CACE;AAAA,IACE,MAAA;AAAA,IACA,SAAA;AAAA,IACA,SAAA;AAAA,eACAC,WAAA;AAAA,IACA,SAAA;AAAA,IACA,QAAQ,EAAC;AAAA,IACT,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,GAAIC,0BAAkB,MAAM,CAAA,CAAA;AAC/D,IAAM,MAAA,CAAA,GAAIC,uBAAaF,WAAS,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,GAAeG,aAA8B,IAAI,CAAA,CAAA;AACvD,IAAM,MAAA,kBAAA,GAAqBA,aAA8B,IAAI,CAAA,CAAA;AAC7D,IAAM,MAAA,wBAAA,GAA2B,CAAC,SAAA,IAAa,CAAC,KAAA,CAAA;AAChD,IAAM,MAAA,gBAAA,GAAmBC,sBAAW,kBAAoB,EAAA;AAAA,MACtD,OAAS,EAAA,wBAAA;AAAA,MACT,IAAM,EAAA,YAAA;AAAA,MACN,UAAY,EAAA,oCAAA;AAAA,MACZ,YAAc,EAAA,IAAA;AAAA,KACf,CAAA,CAAA;AACD,IAAA,MAAM,wBACJ,GAAA,wBAAA,IAA4B,gBAAqB,KAAA,IAAA,GAC7C,CAAC,gBACD,GAAA,KAAA,CAAA;AAEN,IAAA,MAAM,CAAC,iBAAA,EAAmB,oBAAoB,CAAA,GAC5CC,eAA2B,IAAI,CAAA,CAAA;AAEjC,IAAAC,yBAAA;AAAA,MACE,YAAA;AAAA,MACA,MAAM,YAAa,CAAA,OAAA;AAAA,MACnB,EAAC;AAAA,KACH,CAAA;AAEA,IAAM,MAAA,yBAAA,GACJH,aAAiD,KAAS,CAAA,CAAA,CAAA;AAC5D,IAAI,IAAA,yBAAA,CAA0B,YAAY,KAAW,CAAA,EAAA;AACnD,MAA0B,yBAAA,CAAA,OAAA,GAAU,SAClC,QACA,EAAA;AACA,QAAA,MAAM,YAAY,YAAa,CAAA,OAAA,CAAA;AAC/B,QAAA,IAAI,SAAc,KAAA,IAAA;AAAM,UAAA,OAAA;AAExB,QAAA,SAAA,CAAU,QAAS,CAAA;AAAA,UACjB,KAAK,SAAU,CAAA,YAAA;AAAA,UACf,QAAA;AAAA,SACD,CAAA,CAAA;AAAA,OACH,CAAA;AAAA,KACF;AACA,IAAA,MAAM,iBAAiB,yBAA0B,CAAA,OAAA,CAAA;AAEjD,IAAA,uBACGI,eAAA,CAAA,KAAA,EAAA;AAAA,MACC,GAAK,EAAA,YAAA;AAAA,MACJ,GAAG,KAAA;AAAA,MACJ,SAAW,EAAAC,qBAAA;AAAA,QACT,oBAAA;AAAA,QACA,MAAA,KAAW,YACP,2BACA,GAAA,yBAAA;AAAA,QACJ,SAAA;AAAA,OACF;AAAA,MAEC,QAAA,EAAA;AAAA,QAAA,SAAA,GACG,SAAU,CAAA,GAAA,CAAI,CAAC,MAAA,EAAQ,0BACpBX,cAAA,CAAAY,2BAAA,EAAA;AAAA,UAEC,aAAa,MAAO,CAAA,WAAA;AAAA,UACpB,OAAO,MAAO,CAAA,KAAA;AAAA,SAFT,EAAA,KAIP,CACD,CACD,GAAA,IAAA;AAAA,QAEH,MAAA,CAAO,OAAQ,CAAA,KAAK,CAAE,CAAA,GAAA,CAAI,CAAC,CAAC,IAAA,EAAM,IAAI,CAAA,qBACpCZ,cAAA,CAAAa,sBAAA,EAAA;AAAA,UAA0B,MAAA;AAAA,UAAgB,IAAA;AAAA,UAAY,IAAA;AAAA,SAAA,EAAlC,IAA8C,CACpE,CAAA;AAAA,wBAEAb,cAAA,CAAA,KAAA,EAAA;AAAA,UAAI,SAAU,EAAA,oBAAA;AAAA,UACZ,sCACEA,cAAA,CAAA,OAAA,EAAA,EAAQ,CACP,GAAA,KAAA,KAAU,yBACXA,cAAA,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,mBACrBA,cAAA,CAAA,KAAA,EAAA;AAAA,YAAM,MAAA;AAAA,YAAgB,SAAA;AAAA,WAAsB,CAE7C,mBAAAU,eAAA,CAAAI,mBAAA,EAAA;AAAA,YACE,QAAA,EAAA;AAAA,8BAACd,cAAA,CAAA,iBAAA,EAAA;AAAA,gBACC,iBAAA;AAAA,gBACA,cAAA;AAAA,eACF,CAAA;AAAA,8BACCA,cAAA,CAAA,KAAA,EAAA;AAAA,gBAAI,SAAU,EAAA,qBAAA;AAAA,gBACZ,QAAA,EAAA,QAAA,CAAS,GAAI,CAAA,CAAC,OAAY,KAAA;AACzB,kBAAI,IAAA,OAAA,CAAQ,SAAS,MAAQ,EAAA;AAC3B,oBAAA,uBACGA,cAAA,CAAAe,mCAAA,EAAA;AAAA,sBAEC,OAAA;AAAA,iCACAZ,WAAA;AAAA,qBAAA,EAFK,QAAQ,EAGf,CAAA,CAAA;AAAA,mBAEJ,MAAA,IAAW,OAAQ,CAAA,IAAA,KAAS,WAAa,EAAA;AACvC,oBAAA,uBACGH,cAAA,CAAAgB,6CAAA,EAAA;AAAA,sBAEC,OAAA;AAAA,iCACAb,WAAA;AAAA,sBACA,UAAA;AAAA,qBAAA,EAHK,QAAQ,EAIf,CAAA,CAAA;AAAA,mBAEG,MAAA;AACL,oBAAO,OAAA,IAAA,CAAA;AAAA,mBACT;AAAA,iBACD,CAAA;AAAA,eACH,CAAA;AAAA,aAAA;AAAA,WACF,CAAA;AAAA,SAEJ,CAAA;AAAA,wBAECO,eAAA,CAAA,KAAA,EAAA;AAAA,UAAI,SAAU,EAAA,mBAAA;AAAA,UACb,QAAA,EAAA;AAAA,4BAACV,cAAA,CAAA,KAAA,EAAA;AAAA,cAAI,SAAU,EAAA,2BAAA;AAAA,cACb,QAAC,kBAAAA,cAAA,CAAA,KAAA,EAAA;AAAA,gBACC,SAAU,EAAA,wEAAA;AAAA,gBACV,cAAA,EAAc,2BAA2B,EAAK,GAAA,KAAA,CAAA;AAAA,gBAE9C,QAAC,kBAAAA,cAAA,CAAA,QAAA,EAAA;AAAA,kBACC,SAAU,EAAA,oCAAA;AAAA,kBACV,QAAA,EAAU,2BAA2B,CAAI,GAAA,CAAA,CAAA;AAAA,kBACzC,eAAa,CAAC,wBAAA;AAAA,kBACd,UAAU,CAAC,wBAAA;AAAA,kBACX,OAAA,EAAS,MAAM,cAAA,CAAe,QAAQ,CAAA;AAAA,kBAEtC,QAAC,kBAAAA,cAAA,CAAA,MAAA,EAAA;AAAA,oBAAK,SAAU,EAAA,mBAAA;AAAA,oBACd,yCAACiB,uBAAc,EAAA,EAAA,CAAA;AAAA,mBACjB,CAAA;AAAA,iBACF,CAAA;AAAA,eACF,CAAA;AAAA,aACF,CAAA;AAAA,4BACCjB,cAAA,CAAAkB,6BAAA,EAAA;AAAA,cAEC,MAAA;AAAA,cACA,SAAA;AAAA,cACA,SAAW,EAAA,CAAA;AAAA,cACX,SAAA;AAAA,cACA,qBAAqB,CAAC,EAAE,EAAG,EAAA,KAAM,qBAAqB,EAAE,CAAA;AAAA,cACxD,SAAA,EACE,MAAW,KAAA,OAAA,GACP,oCACA,GAAA,KAAA,CAAA;AAAA,aAAA,EATD,MAWP,CAAA;AAAA,WAAA;AAAA,SACF,CAAA;AAAA,wBAQClB,cAAA,CAAA,KAAA,EAAA;AAAA,UACC,GAAK,EAAA,kBAAA;AAAA,UACL,KAAO,EAAA,EAAE,QAAU,EAAA,QAAA,EAAU,QAAQ,CAAE,EAAA;AAAA,UACvC,aAAW,EAAA,IAAA;AAAA,UACX,uBAAA,EAAuB,mBAAmB,EAAK,GAAA,KAAA,CAAA;AAAA,SACjD,CAAA;AAAA,OAAA;AAAA,KACF,CAAA,CAAA;AAAA,GAEJ;AACF,EAAA;AAEA,SAAS,iBAAkB,CAAA;AAAA,EACzB,iBAAA;AAAA,EACA,cAAA;AACF,CAGG,EAAA;AAED,EAAAmB,wBAAA,CAAgB,MAAM;AACpB,IAAA,cAAA,CAAe,SAAS,CAAA,CAAA;AAAA,GAC1B,EAAG,CAAC,cAAc,CAAC,CAAA,CAAA;AAGnB,EAAAC,eAAA,CAAU,MAAM;AACd,IAAA,cAAA,CAAe,QAAQ,CAAA,CAAA;AAAA,GACtB,EAAA,CAAC,iBAAmB,EAAA,cAAc,CAAC,CAAA,CAAA;AAEtC,EAAO,OAAA,IAAA,CAAA;AACT;;;;"}
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
|
|
2
|
-
import {
|
|
3
|
-
import { useAiChatMessages, useClient, RegisterAiKnowledge } from '@liveblocks/react';
|
|
2
|
+
import { useAiChatMessages, RegisterAiKnowledge, RegisterAiTool } from '@liveblocks/react';
|
|
4
3
|
import { useLayoutEffect } from '@liveblocks/react/_private';
|
|
5
4
|
import { forwardRef, useRef, useState, useImperativeHandle, useEffect } from 'react';
|
|
6
5
|
import { ArrowDownIcon } from '../icons/ArrowDown.js';
|
|
@@ -39,30 +38,20 @@ const AiChat = forwardRef(
|
|
|
39
38
|
const Loading = components?.Loading ?? defaultComponents.Loading;
|
|
40
39
|
const containerRef = useRef(null);
|
|
41
40
|
const containerBottomRef = useRef(null);
|
|
41
|
+
const isScrollIndicatorEnabled = !isLoading && !error;
|
|
42
42
|
const isScrollAtBottom = useVisible(containerBottomRef, {
|
|
43
|
-
enabled:
|
|
43
|
+
enabled: isScrollIndicatorEnabled,
|
|
44
44
|
root: containerRef,
|
|
45
|
-
rootMargin: MIN_DISTANCE_BOTTOM_SCROLL_INDICATOR
|
|
45
|
+
rootMargin: MIN_DISTANCE_BOTTOM_SCROLL_INDICATOR,
|
|
46
|
+
initialValue: null
|
|
46
47
|
});
|
|
47
|
-
const isScrollIndicatorVisible =
|
|
48
|
-
const client = useClient();
|
|
49
|
-
const ai = client[kInternal].ai;
|
|
48
|
+
const isScrollIndicatorVisible = isScrollIndicatorEnabled && isScrollAtBottom !== null ? !isScrollAtBottom : false;
|
|
50
49
|
const [lastSentMessageId, setLastSentMessageId] = useState(null);
|
|
51
50
|
useImperativeHandle(
|
|
52
51
|
forwardedRef,
|
|
53
52
|
() => containerRef.current,
|
|
54
53
|
[]
|
|
55
54
|
);
|
|
56
|
-
useEffect(() => {
|
|
57
|
-
Object.entries(tools).map(
|
|
58
|
-
([key, value]) => ai.registerChatTool(chatId, key, value)
|
|
59
|
-
);
|
|
60
|
-
return () => {
|
|
61
|
-
Object.entries(tools).map(
|
|
62
|
-
([key]) => ai.unregisterChatTool(chatId, key)
|
|
63
|
-
);
|
|
64
|
-
};
|
|
65
|
-
}, [ai, chatId, tools]);
|
|
66
55
|
const scrollToBottomCallbackRef = useRef(void 0);
|
|
67
56
|
if (scrollToBottomCallbackRef.current === void 0) {
|
|
68
57
|
scrollToBottomCallbackRef.current = function(behavior) {
|
|
@@ -89,6 +78,11 @@ const AiChat = forwardRef(
|
|
|
89
78
|
description: source.description,
|
|
90
79
|
value: source.value
|
|
91
80
|
}, index)) : null,
|
|
81
|
+
Object.entries(tools).map(([name, tool]) => /* @__PURE__ */ jsx(RegisterAiTool, {
|
|
82
|
+
chatId,
|
|
83
|
+
name,
|
|
84
|
+
tool
|
|
85
|
+
}, name)),
|
|
92
86
|
/* @__PURE__ */ jsx("div", {
|
|
93
87
|
className: "lb-ai-chat-content",
|
|
94
88
|
children: isLoading ? /* @__PURE__ */ jsx(Loading, {}) : error !== void 0 ? /* @__PURE__ */ jsx("div", {
|
|
@@ -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 { kInternal } from \"@liveblocks/core\";\nimport {\n RegisterAiKnowledge,\n useAiChatMessages,\n useClient,\n} from \"@liveblocks/react\";\nimport { useLayoutEffect } from \"@liveblocks/react/_private\";\nimport {\n type ComponentProps,\n type ComponentType,\n forwardRef,\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 AiChatComposerOverrides,\n type AiChatMessageOverrides,\n type AiChatOverrides,\n type GlobalOverrides,\n useOverrides,\n} from \"../overrides\";\nimport { classNames } from \"../utils/class-names\";\nimport { useVisible } from \"../utils/use-visible\";\nimport { AiChatAssistantMessage } from \"./internal/AiChatAssistantMessage\";\nimport { AiChatComposer } from \"./internal/AiChatComposer\";\nimport { AiChatUserMessage } from \"./internal/AiChatUserMessage\";\n\nexport type AiChatComponentsEmptyProps = {\n /**\n * The chat id provided to the `AiChat` component.\n */\n chatId: string;\n /**\n * The copilot id provided to the `AiChat` component.\n */\n copilotId?: string;\n};\n\nexport type AiChatComponentsLoadingProps = Record<string, never>;\n\n// TODO: Add Markdown components\nexport type AiChatComponents = {\n /**\n * The component used to render the empty state of the chat.\n */\n Empty: ComponentType<AiChatComponentsEmptyProps>;\n /**\n * The component used to render the loading state of the chat.\n */\n Loading: ComponentType<AiChatComponentsLoadingProps>;\n};\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 = 50;\n\nexport interface AiChatProps extends ComponentProps<\"div\"> {\n /**\n * The id of the chat the composer belongs to.\n */\n chatId: string;\n /**\n * Whether to focus the chat composer on mount.\n */\n autoFocus?: boolean;\n /**\n * The id of the copilot to use to send the message.\n */\n copilotId?: string;\n /**\n * The contextual knowledge to include in the chat. May be used by the assistant when generating responses.\n * Any knowledge you provide via this prop will be added to any already globally registered knowledge via <RegisterAiKnowledge />.\n */\n knowledge?: AiKnowledgeSource[];\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 * The layout of the chat and its composer.\n */\n layout?: \"inset\" | \"compact\";\n /**\n * Override the component's strings.\n */\n overrides?: Partial<\n GlobalOverrides &\n AiChatMessageOverrides &\n AiChatComposerOverrides &\n AiChatOverrides\n >;\n /**\n * Override the component's components.\n */\n components?: Partial<GlobalComponents & AiChatComponents>;\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\nexport const AiChat = forwardRef<HTMLDivElement, AiChatProps>(\n (\n {\n chatId,\n copilotId,\n autoFocus,\n overrides,\n knowledge,\n tools = {},\n layout = \"inset\",\n components,\n className,\n ...props\n },\n forwardedRef\n ) => {\n const { messages, isLoading, error } = useAiChatMessages(chatId);\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 containerBottomRef = useRef<HTMLDivElement | null>(null);\n const isScrollAtBottom = useVisible(containerBottomRef, {\n enabled: !isLoading && !error,\n root: containerRef,\n rootMargin: MIN_DISTANCE_BOTTOM_SCROLL_INDICATOR,\n });\n const isScrollIndicatorVisible =\n isLoading || error ? false : !isScrollAtBottom;\n\n const client = useClient();\n const ai = client[kInternal].ai;\n\n const [lastSentMessageId, setLastSentMessageId] =\n useState<MessageId | null>(null);\n\n useImperativeHandle<HTMLDivElement | null, HTMLDivElement | null>(\n forwardedRef,\n () => containerRef.current,\n []\n );\n\n // Register the provided tools to the chat on mount and unregister them on unmount\n useEffect(() => {\n Object.entries(tools).map(([key, value]) =>\n ai.registerChatTool(chatId, key, value)\n );\n return () => {\n Object.entries(tools).map(([key]) =>\n ai.unregisterChatTool(chatId, key)\n );\n };\n }, [ai, chatId, tools]);\n\n const scrollToBottomCallbackRef =\n useRef<(behavior: \"instant\" | \"smooth\") => void>(undefined);\n if (scrollToBottomCallbackRef.current === undefined) {\n scrollToBottomCallbackRef.current = function (\n behavior: \"instant\" | \"smooth\"\n ) {\n const container = containerRef.current;\n if (container === null) return;\n\n container.scrollTo({\n top: container.scrollHeight,\n behavior,\n });\n };\n }\n const scrollToBottom = scrollToBottomCallbackRef.current;\n\n return (\n <div\n ref={containerRef}\n {...props}\n className={classNames(\n \"lb-root lb-ai-chat\",\n layout === \"compact\"\n ? \"lb-ai-chat:layout-compact\"\n : \"lb-ai-chat:layout-inset\",\n className\n )}\n >\n {knowledge\n ? knowledge.map((source, index) => (\n <RegisterAiKnowledge\n key={index}\n description={source.description}\n value={source.value}\n // knowledgeKey={source.knowledgeKey}\n />\n ))\n : null}\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 <AutoScrollHandler\n lastSentMessageId={lastSentMessageId}\n scrollToBottom={scrollToBottom}\n />\n <div className=\"lb-ai-chat-messages\">\n {messages.map((message) => {\n if (message.role === \"user\") {\n return (\n <AiChatUserMessage\n key={message.id}\n message={message}\n overrides={overrides}\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 </div>\n\n <div className=\"lb-ai-chat-footer\">\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 disabled={!isScrollIndicatorVisible}\n onClick={() => scrollToBottom(\"smooth\")}\n >\n <span className=\"lb-icon-container\">\n <ArrowDownIcon />\n </span>\n </button>\n </div>\n </div>\n <AiChatComposer\n key={chatId}\n chatId={chatId}\n copilotId={copilotId as CopilotId}\n overrides={$}\n autoFocus={autoFocus}\n onUserMessageCreate={({ id }) => setLastSentMessageId(id)}\n className={\n layout === \"inset\"\n ? \"lb-elevation lb-elevation-moderate\"\n : undefined\n }\n />\n </div>\n {/* This invisible element 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 fully scrolled.\n */}\n <div\n ref={containerBottomRef}\n style={{ position: \"sticky\", height: 0 }}\n aria-hidden\n data-scroll-at-bottom={isScrollAtBottom ? \"\" : undefined}\n />\n </div>\n );\n }\n);\n\nfunction AutoScrollHandler({\n lastSentMessageId,\n scrollToBottom,\n}: {\n lastSentMessageId: MessageId | null;\n scrollToBottom: (behavior: \"instant\" | \"smooth\") => void;\n}) {\n // Scroll to bottom when the component first mounts\n useLayoutEffect(() => {\n scrollToBottom(\"instant\");\n }, [scrollToBottom]);\n\n // Scroll to bottom when sending a new message\n useEffect(() => {\n scrollToBottom(\"smooth\");\n }, [lastSentMessageId, scrollToBottom]);\n\n return null;\n}\n"],"names":[],"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 {\n RegisterAiKnowledge,\n RegisterAiTool,\n useAiChatMessages,\n} from \"@liveblocks/react\";\nimport { useLayoutEffect } from \"@liveblocks/react/_private\";\nimport {\n type ComponentProps,\n type ComponentType,\n forwardRef,\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 AiChatComposerOverrides,\n type AiChatMessageOverrides,\n type AiChatOverrides,\n type GlobalOverrides,\n useOverrides,\n} from \"../overrides\";\nimport { classNames } from \"../utils/class-names\";\nimport { useVisible } from \"../utils/use-visible\";\nimport { AiChatAssistantMessage } from \"./internal/AiChatAssistantMessage\";\nimport { AiChatComposer } from \"./internal/AiChatComposer\";\nimport { AiChatUserMessage } from \"./internal/AiChatUserMessage\";\n\nexport type AiChatComponentsEmptyProps = {\n /**\n * The chat id provided to the `AiChat` component.\n */\n chatId: string;\n /**\n * The copilot id provided to the `AiChat` component.\n */\n copilotId?: string;\n};\n\nexport type AiChatComponentsLoadingProps = Record<string, never>;\n\n// TODO: Add Markdown components\nexport type AiChatComponents = {\n /**\n * The component used to render the empty state of the chat.\n */\n Empty: ComponentType<AiChatComponentsEmptyProps>;\n /**\n * The component used to render the loading state of the chat.\n */\n Loading: ComponentType<AiChatComponentsLoadingProps>;\n};\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 = 50;\n\nexport interface AiChatProps extends ComponentProps<\"div\"> {\n /**\n * The id of the chat the composer belongs to.\n */\n chatId: string;\n /**\n * Whether to focus the chat composer on mount.\n */\n autoFocus?: boolean;\n /**\n * The id of the copilot to use to send the message.\n */\n copilotId?: string;\n /**\n * The contextual knowledge to include in the chat. May be used by the assistant when generating responses.\n * Any knowledge you provide via this prop will be added to any already globally registered knowledge via <RegisterAiKnowledge />.\n */\n knowledge?: AiKnowledgeSource[];\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 * The layout of the chat and its composer.\n */\n layout?: \"inset\" | \"compact\";\n /**\n * Override the component's strings.\n */\n overrides?: Partial<\n GlobalOverrides &\n AiChatMessageOverrides &\n AiChatComposerOverrides &\n AiChatOverrides\n >;\n /**\n * Override the component's components.\n */\n components?: Partial<GlobalComponents & AiChatComponents>;\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\nexport const AiChat = forwardRef<HTMLDivElement, AiChatProps>(\n (\n {\n chatId,\n copilotId,\n autoFocus,\n overrides,\n knowledge,\n tools = {},\n layout = \"inset\",\n components,\n className,\n ...props\n },\n forwardedRef\n ) => {\n const { messages, isLoading, error } = useAiChatMessages(chatId);\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 containerBottomRef = useRef<HTMLDivElement | null>(null);\n const isScrollIndicatorEnabled = !isLoading && !error;\n const isScrollAtBottom = useVisible(containerBottomRef, {\n enabled: isScrollIndicatorEnabled,\n root: containerRef,\n rootMargin: MIN_DISTANCE_BOTTOM_SCROLL_INDICATOR,\n initialValue: null,\n });\n const isScrollIndicatorVisible =\n isScrollIndicatorEnabled && isScrollAtBottom !== null\n ? !isScrollAtBottom\n : false;\n\n const [lastSentMessageId, setLastSentMessageId] =\n useState<MessageId | null>(null);\n\n useImperativeHandle<HTMLDivElement | null, HTMLDivElement | null>(\n forwardedRef,\n () => containerRef.current,\n []\n );\n\n const scrollToBottomCallbackRef =\n useRef<(behavior: \"instant\" | \"smooth\") => void>(undefined);\n if (scrollToBottomCallbackRef.current === undefined) {\n scrollToBottomCallbackRef.current = function (\n behavior: \"instant\" | \"smooth\"\n ) {\n const container = containerRef.current;\n if (container === null) return;\n\n container.scrollTo({\n top: container.scrollHeight,\n behavior,\n });\n };\n }\n const scrollToBottom = scrollToBottomCallbackRef.current;\n\n return (\n <div\n ref={containerRef}\n {...props}\n className={classNames(\n \"lb-root lb-ai-chat\",\n layout === \"compact\"\n ? \"lb-ai-chat:layout-compact\"\n : \"lb-ai-chat:layout-inset\",\n className\n )}\n >\n {knowledge\n ? knowledge.map((source, index) => (\n <RegisterAiKnowledge\n key={index}\n description={source.description}\n value={source.value}\n // knowledgeKey={source.knowledgeKey}\n />\n ))\n : null}\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 <AutoScrollHandler\n lastSentMessageId={lastSentMessageId}\n scrollToBottom={scrollToBottom}\n />\n <div className=\"lb-ai-chat-messages\">\n {messages.map((message) => {\n if (message.role === \"user\") {\n return (\n <AiChatUserMessage\n key={message.id}\n message={message}\n overrides={overrides}\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 </div>\n\n <div className=\"lb-ai-chat-footer\">\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 disabled={!isScrollIndicatorVisible}\n onClick={() => scrollToBottom(\"smooth\")}\n >\n <span className=\"lb-icon-container\">\n <ArrowDownIcon />\n </span>\n </button>\n </div>\n </div>\n <AiChatComposer\n key={chatId}\n chatId={chatId}\n copilotId={copilotId as CopilotId}\n overrides={$}\n autoFocus={autoFocus}\n onUserMessageCreate={({ id }) => setLastSentMessageId(id)}\n className={\n layout === \"inset\"\n ? \"lb-elevation lb-elevation-moderate\"\n : undefined\n }\n />\n </div>\n {/* This invisible element 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 fully scrolled.\n */}\n <div\n ref={containerBottomRef}\n style={{ position: \"sticky\", height: 0 }}\n aria-hidden\n data-scroll-at-bottom={isScrollAtBottom ? \"\" : undefined}\n />\n </div>\n );\n }\n);\n\nfunction AutoScrollHandler({\n lastSentMessageId,\n scrollToBottom,\n}: {\n lastSentMessageId: MessageId | null;\n scrollToBottom: (behavior: \"instant\" | \"smooth\") => void;\n}) {\n // Scroll to bottom when the component first mounts\n useLayoutEffect(() => {\n scrollToBottom(\"instant\");\n }, [scrollToBottom]);\n\n // Scroll to bottom when sending a new message\n useEffect(() => {\n scrollToBottom(\"smooth\");\n }, [lastSentMessageId, scrollToBottom]);\n\n return null;\n}\n"],"names":[],"mappings":"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"}
|
|
@@ -31,7 +31,7 @@ function ToolInvocation({
|
|
|
31
31
|
}) {
|
|
32
32
|
const client = react.useClient();
|
|
33
33
|
const ai = client[core.kInternal].ai;
|
|
34
|
-
const tool = _private.useSignal(ai.signals.
|
|
34
|
+
const tool = _private.useSignal(ai.signals.getTool\u03A3(part.toolName, chatId));
|
|
35
35
|
const respond = react$1.useCallback(
|
|
36
36
|
(result) => {
|
|
37
37
|
if (part.status === "receiving") {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.cjs","sources":["../../../src/primitives/AiMessage/index.tsx"],"sourcesContent":["import type {\n AiToolInvocationPart,\n AiToolInvocationProps,\n JsonObject,\n MessageId,\n ToolResultData,\n} from \"@liveblocks/core\";\nimport { kInternal } from \"@liveblocks/core\";\nimport { useClient } from \"@liveblocks/react\";\nimport { useSignal } from \"@liveblocks/react/_private\";\nimport { Slot } from \"@radix-ui/react-slot\";\nimport type { ComponentType } from \"react\";\nimport { forwardRef, useCallback, useMemo } from \"react\";\n\nimport { ErrorBoundary } from \"../../utils/ErrorBoundary\";\nimport { Markdown } from \"../Markdown\";\nimport { AiToolInvocationContext } from \"./contexts\";\nimport type {\n AiMessageContentComponents,\n AiMessageContentProps,\n} from \"./types\";\n\nconst AI_MESSAGE_CONTENT_NAME = \"AiMessageContent\";\n\nconst defaultMessageContentComponents: AiMessageContentComponents = {\n TextPart: ({ part }) => {\n return <Markdown content={part.text} />;\n },\n ReasoningPart: ({ part }) => {\n return <Markdown content={part.text} />;\n },\n ToolInvocationPart: ({ children }) => children,\n};\n\n/* -------------------------------------------------------------------------------------------------\n * ToolInvocationPart\n * -----------------------------------------------------------------------------------------------*/\n\nfunction ToolInvocation({\n chatId,\n messageId,\n part,\n}: {\n chatId: string;\n messageId: MessageId;\n part: AiToolInvocationPart;\n}) {\n const client = useClient();\n const ai = client[kInternal].ai;\n const tool = useSignal(ai.signals.
|
|
1
|
+
{"version":3,"file":"index.cjs","sources":["../../../src/primitives/AiMessage/index.tsx"],"sourcesContent":["import type {\n AiToolInvocationPart,\n AiToolInvocationProps,\n JsonObject,\n MessageId,\n ToolResultData,\n} from \"@liveblocks/core\";\nimport { kInternal } from \"@liveblocks/core\";\nimport { useClient } from \"@liveblocks/react\";\nimport { useSignal } from \"@liveblocks/react/_private\";\nimport { Slot } from \"@radix-ui/react-slot\";\nimport type { ComponentType } from \"react\";\nimport { forwardRef, useCallback, useMemo } from \"react\";\n\nimport { ErrorBoundary } from \"../../utils/ErrorBoundary\";\nimport { Markdown } from \"../Markdown\";\nimport { AiToolInvocationContext } from \"./contexts\";\nimport type {\n AiMessageContentComponents,\n AiMessageContentProps,\n} from \"./types\";\n\nconst AI_MESSAGE_CONTENT_NAME = \"AiMessageContent\";\n\nconst defaultMessageContentComponents: AiMessageContentComponents = {\n TextPart: ({ part }) => {\n return <Markdown content={part.text} />;\n },\n ReasoningPart: ({ part }) => {\n return <Markdown content={part.text} />;\n },\n ToolInvocationPart: ({ children }) => children,\n};\n\n/* -------------------------------------------------------------------------------------------------\n * ToolInvocationPart\n * -----------------------------------------------------------------------------------------------*/\n\nfunction ToolInvocation({\n chatId,\n messageId,\n part,\n}: {\n chatId: string;\n messageId: MessageId;\n part: AiToolInvocationPart;\n}) {\n const client = useClient();\n const ai = client[kInternal].ai;\n const tool = useSignal(ai.signals.getToolΣ(part.toolName, chatId));\n\n const respond = useCallback(\n (result: ToolResultData) => {\n if (part.status === \"receiving\") {\n console.log(\n `Ignoring respond(): tool '${part.toolName}' (${part.toolCallId}) is still receiving`\n );\n } else if (part.status === \"executed\") {\n console.log(\n `Ignoring respond(): tool '${part.toolName}' (${part.toolCallId}) has already executed`\n );\n } else {\n ai.setToolResult(\n chatId,\n messageId,\n part.toolCallId,\n result\n // TODO Pass in AiGenerationOptions here?\n );\n }\n },\n [ai, chatId, messageId, part.status, part.toolName, part.toolCallId]\n );\n\n if (tool === undefined || tool.render === undefined) return null;\n const RenderFn = tool.render as ComponentType<\n AiToolInvocationProps<JsonObject, ToolResultData>\n >;\n\n const { type: _, ...rest } = part;\n const props = {\n ...rest,\n respond,\n types: undefined as never,\n [kInternal]: {\n execute: tool.execute,\n },\n };\n return (\n <ErrorBoundary\n fallback={\n <p style={{ color: \"red\" }}>\n Failed to render tool call result for ‘{part.toolName}’. See console\n for details.\n </p>\n }\n >\n <AiToolInvocationContext.Provider value={props}>\n <RenderFn {...props} />\n </AiToolInvocationContext.Provider>\n </ErrorBoundary>\n );\n}\n\n/**\n * --------------------------------------------------------------------------\n * @private The API for this component is not yet stable.\n * --------------------------------------------------------------------------\n *\n * Primitive to help display an user or assistant message’s content, which is\n * an array of parts.\n *\n * @example\n * <AiMessage.Content message={message} components={{ TextPart }} />\n */\nconst AiMessageContent = forwardRef<HTMLDivElement, AiMessageContentProps>(\n ({ message, components, asChild, ...props }, forwardedRef) => {\n const Component = asChild ? Slot : \"div\";\n const { TextPart, ReasoningPart, ToolInvocationPart } = useMemo(\n () => ({ ...defaultMessageContentComponents, ...components }),\n [components]\n );\n\n const content = message.content ?? message.contentSoFar;\n const numParts = content.length;\n const isGenerating =\n message.role === \"assistant\" && message.status === \"generating\";\n return (\n <Component {...props} ref={forwardedRef}>\n {content.map((part, index) => {\n // A part is considered to be still \"streaming in\" if it's the last\n // part in the content array, and the message is in \"generating\"\n // state.\n const isStreaming = isGenerating && index === numParts - 1;\n const extra = { index, isStreaming };\n switch (part.type) {\n case \"text\":\n return <TextPart key={index} part={part} {...extra} />;\n case \"reasoning\":\n return <ReasoningPart key={index} part={part} {...extra} />;\n case \"tool-invocation\":\n // TODO: If the render() method doesn't exist, we should not render the ToolInvocationPart\n // or pass it no children so that it can decide to not render?\n return (\n <ToolInvocationPart key={index} part={part} {...extra}>\n <ToolInvocation\n key={index}\n part={part}\n chatId={message.chatId}\n messageId={message.id}\n />\n </ToolInvocationPart>\n );\n default:\n return null;\n }\n })}\n </Component>\n );\n }\n);\n\nif (process.env.NODE_ENV !== \"production\") {\n AiMessageContent.displayName = AI_MESSAGE_CONTENT_NAME;\n}\n\n// NOTE: Every export from this file will be available publicly as AiMessage.*\nexport { AiMessageContent as Content };\n"],"names":["jsx","Markdown","useClient","kInternal","useSignal","useCallback","ErrorBoundary","jsxs","AiToolInvocationContext","forwardRef","Slot","useMemo"],"mappings":"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"}
|
|
@@ -29,7 +29,7 @@ function ToolInvocation({
|
|
|
29
29
|
}) {
|
|
30
30
|
const client = useClient();
|
|
31
31
|
const ai = client[kInternal].ai;
|
|
32
|
-
const tool = useSignal(ai.signals.
|
|
32
|
+
const tool = useSignal(ai.signals.getTool\u03A3(part.toolName, chatId));
|
|
33
33
|
const respond = useCallback(
|
|
34
34
|
(result) => {
|
|
35
35
|
if (part.status === "receiving") {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sources":["../../../src/primitives/AiMessage/index.tsx"],"sourcesContent":["import type {\n AiToolInvocationPart,\n AiToolInvocationProps,\n JsonObject,\n MessageId,\n ToolResultData,\n} from \"@liveblocks/core\";\nimport { kInternal } from \"@liveblocks/core\";\nimport { useClient } from \"@liveblocks/react\";\nimport { useSignal } from \"@liveblocks/react/_private\";\nimport { Slot } from \"@radix-ui/react-slot\";\nimport type { ComponentType } from \"react\";\nimport { forwardRef, useCallback, useMemo } from \"react\";\n\nimport { ErrorBoundary } from \"../../utils/ErrorBoundary\";\nimport { Markdown } from \"../Markdown\";\nimport { AiToolInvocationContext } from \"./contexts\";\nimport type {\n AiMessageContentComponents,\n AiMessageContentProps,\n} from \"./types\";\n\nconst AI_MESSAGE_CONTENT_NAME = \"AiMessageContent\";\n\nconst defaultMessageContentComponents: AiMessageContentComponents = {\n TextPart: ({ part }) => {\n return <Markdown content={part.text} />;\n },\n ReasoningPart: ({ part }) => {\n return <Markdown content={part.text} />;\n },\n ToolInvocationPart: ({ children }) => children,\n};\n\n/* -------------------------------------------------------------------------------------------------\n * ToolInvocationPart\n * -----------------------------------------------------------------------------------------------*/\n\nfunction ToolInvocation({\n chatId,\n messageId,\n part,\n}: {\n chatId: string;\n messageId: MessageId;\n part: AiToolInvocationPart;\n}) {\n const client = useClient();\n const ai = client[kInternal].ai;\n const tool = useSignal(ai.signals.
|
|
1
|
+
{"version":3,"file":"index.js","sources":["../../../src/primitives/AiMessage/index.tsx"],"sourcesContent":["import type {\n AiToolInvocationPart,\n AiToolInvocationProps,\n JsonObject,\n MessageId,\n ToolResultData,\n} from \"@liveblocks/core\";\nimport { kInternal } from \"@liveblocks/core\";\nimport { useClient } from \"@liveblocks/react\";\nimport { useSignal } from \"@liveblocks/react/_private\";\nimport { Slot } from \"@radix-ui/react-slot\";\nimport type { ComponentType } from \"react\";\nimport { forwardRef, useCallback, useMemo } from \"react\";\n\nimport { ErrorBoundary } from \"../../utils/ErrorBoundary\";\nimport { Markdown } from \"../Markdown\";\nimport { AiToolInvocationContext } from \"./contexts\";\nimport type {\n AiMessageContentComponents,\n AiMessageContentProps,\n} from \"./types\";\n\nconst AI_MESSAGE_CONTENT_NAME = \"AiMessageContent\";\n\nconst defaultMessageContentComponents: AiMessageContentComponents = {\n TextPart: ({ part }) => {\n return <Markdown content={part.text} />;\n },\n ReasoningPart: ({ part }) => {\n return <Markdown content={part.text} />;\n },\n ToolInvocationPart: ({ children }) => children,\n};\n\n/* -------------------------------------------------------------------------------------------------\n * ToolInvocationPart\n * -----------------------------------------------------------------------------------------------*/\n\nfunction ToolInvocation({\n chatId,\n messageId,\n part,\n}: {\n chatId: string;\n messageId: MessageId;\n part: AiToolInvocationPart;\n}) {\n const client = useClient();\n const ai = client[kInternal].ai;\n const tool = useSignal(ai.signals.getToolΣ(part.toolName, chatId));\n\n const respond = useCallback(\n (result: ToolResultData) => {\n if (part.status === \"receiving\") {\n console.log(\n `Ignoring respond(): tool '${part.toolName}' (${part.toolCallId}) is still receiving`\n );\n } else if (part.status === \"executed\") {\n console.log(\n `Ignoring respond(): tool '${part.toolName}' (${part.toolCallId}) has already executed`\n );\n } else {\n ai.setToolResult(\n chatId,\n messageId,\n part.toolCallId,\n result\n // TODO Pass in AiGenerationOptions here?\n );\n }\n },\n [ai, chatId, messageId, part.status, part.toolName, part.toolCallId]\n );\n\n if (tool === undefined || tool.render === undefined) return null;\n const RenderFn = tool.render as ComponentType<\n AiToolInvocationProps<JsonObject, ToolResultData>\n >;\n\n const { type: _, ...rest } = part;\n const props = {\n ...rest,\n respond,\n types: undefined as never,\n [kInternal]: {\n execute: tool.execute,\n },\n };\n return (\n <ErrorBoundary\n fallback={\n <p style={{ color: \"red\" }}>\n Failed to render tool call result for ‘{part.toolName}’. See console\n for details.\n </p>\n }\n >\n <AiToolInvocationContext.Provider value={props}>\n <RenderFn {...props} />\n </AiToolInvocationContext.Provider>\n </ErrorBoundary>\n );\n}\n\n/**\n * --------------------------------------------------------------------------\n * @private The API for this component is not yet stable.\n * --------------------------------------------------------------------------\n *\n * Primitive to help display an user or assistant message’s content, which is\n * an array of parts.\n *\n * @example\n * <AiMessage.Content message={message} components={{ TextPart }} />\n */\nconst AiMessageContent = forwardRef<HTMLDivElement, AiMessageContentProps>(\n ({ message, components, asChild, ...props }, forwardedRef) => {\n const Component = asChild ? Slot : \"div\";\n const { TextPart, ReasoningPart, ToolInvocationPart } = useMemo(\n () => ({ ...defaultMessageContentComponents, ...components }),\n [components]\n );\n\n const content = message.content ?? message.contentSoFar;\n const numParts = content.length;\n const isGenerating =\n message.role === \"assistant\" && message.status === \"generating\";\n return (\n <Component {...props} ref={forwardedRef}>\n {content.map((part, index) => {\n // A part is considered to be still \"streaming in\" if it's the last\n // part in the content array, and the message is in \"generating\"\n // state.\n const isStreaming = isGenerating && index === numParts - 1;\n const extra = { index, isStreaming };\n switch (part.type) {\n case \"text\":\n return <TextPart key={index} part={part} {...extra} />;\n case \"reasoning\":\n return <ReasoningPart key={index} part={part} {...extra} />;\n case \"tool-invocation\":\n // TODO: If the render() method doesn't exist, we should not render the ToolInvocationPart\n // or pass it no children so that it can decide to not render?\n return (\n <ToolInvocationPart key={index} part={part} {...extra}>\n <ToolInvocation\n key={index}\n part={part}\n chatId={message.chatId}\n messageId={message.id}\n />\n </ToolInvocationPart>\n );\n default:\n return null;\n }\n })}\n </Component>\n );\n }\n);\n\nif (process.env.NODE_ENV !== \"production\") {\n AiMessageContent.displayName = AI_MESSAGE_CONTENT_NAME;\n}\n\n// NOTE: Every export from this file will be available publicly as AiMessage.*\nexport { AiMessageContent as Content };\n"],"names":[],"mappings":"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"}
|
|
@@ -74,7 +74,9 @@ function useIntersectionCallback(ref, callback, options) {
|
|
|
74
74
|
}, [ref, enabled, latestCallback, root, rootMargin]);
|
|
75
75
|
}
|
|
76
76
|
function useVisible(ref, options) {
|
|
77
|
-
const [isVisible, setVisible] = react.useState(
|
|
77
|
+
const [isVisible, setVisible] = react.useState(
|
|
78
|
+
options?.initialValue !== void 0 ? options.initialValue : false
|
|
79
|
+
);
|
|
78
80
|
useIntersectionCallback(
|
|
79
81
|
ref,
|
|
80
82
|
(isIntersecting) => setVisible(isIntersecting),
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"use-visible.cjs","sources":["../../src/utils/use-visible.ts"],"sourcesContent":["import { type RefObject, useEffect, useState } from \"react\";\n\nimport { useLatest } from \"./use-latest\";\n\ninterface ObserveOptions {\n rootMargin?: string | number;\n root?: RefObject<Element>;\n}\n\ninterface Options extends ObserveOptions {\n enabled?: boolean;\n}\n\ntype IntersectionObserverSingleCallback = (\n entry: IntersectionObserverEntry\n) => void;\n\nlet optionlessIntersectionObserver: IntersectionObserver | undefined;\nconst optionlessIntersectionCallbacks = new WeakMap<\n Element,\n IntersectionObserverSingleCallback\n>();\n\nconst individualIntersectionObservers = new WeakMap<\n Element,\n IntersectionObserver\n>();\n\nfunction observe(\n element: Element,\n callback: IntersectionObserverSingleCallback,\n options?: ObserveOptions\n) {\n // Observers without options share a common IntersectionObserver instance, ones with options have their own\n if (!options) {\n if (!optionlessIntersectionObserver) {\n optionlessIntersectionObserver = new IntersectionObserver((entries) => {\n for (const entry of entries) {\n const callback = optionlessIntersectionCallbacks.get(entry.target);\n\n callback?.(entry);\n }\n });\n }\n\n optionlessIntersectionCallbacks.set(element, callback);\n optionlessIntersectionObserver.observe(element);\n } else {\n const observer = new IntersectionObserver(\n (entries) => {\n for (const entry of entries) {\n callback?.(entry);\n }\n },\n {\n root: options.root?.current,\n rootMargin:\n typeof options.rootMargin === \"number\"\n ? `${options.rootMargin}px`\n : options.rootMargin,\n }\n );\n\n individualIntersectionObservers.set(element, observer);\n observer.observe(element);\n }\n}\n\nfunction unobserve(element: Element, options?: ObserveOptions) {\n if (!options) {\n optionlessIntersectionCallbacks.delete(element);\n optionlessIntersectionObserver?.unobserve(element);\n } else {\n const observer = individualIntersectionObservers.get(element);\n\n observer?.unobserve(element);\n individualIntersectionObservers.delete(element);\n }\n}\n\n/**\n * Observe when an element enters or exits the viewport.\n *\n * If you only need to get a stateful visibility value, use the higher level hook `useVisible` instead.\n */\nexport function useIntersectionCallback(\n ref: RefObject<Element>,\n callback: (isIntersecting: boolean, entry: IntersectionObserverEntry) => void,\n options?: Options\n) {\n const enabled = options?.enabled ?? true;\n const latestCallback = useLatest(callback);\n const { root, rootMargin } = options ?? {};\n\n useEffect(() => {\n const element = ref.current;\n\n if (!element) {\n return;\n }\n\n const observeOptions: ObserveOptions = {\n root,\n rootMargin,\n };\n\n if (enabled) {\n observe(\n element,\n (entry) => {\n // The intersection observer entry might be useful in some cases but the main information\n // is whether the element is intersecting or not so we pass that as the first argument.\n latestCallback.current(entry.isIntersecting, entry);\n },\n observeOptions\n );\n } else {\n unobserve(element, observeOptions);\n }\n\n return () => {\n unobserve(element, observeOptions);\n };\n }, [ref, enabled, latestCallback, root, rootMargin]);\n}\n\n/**\n * Observe whether an element is currently visible or not.\n */\nexport function useVisible(ref: RefObject<Element
|
|
1
|
+
{"version":3,"file":"use-visible.cjs","sources":["../../src/utils/use-visible.ts"],"sourcesContent":["import { type RefObject, useEffect, useState } from \"react\";\n\nimport { useLatest } from \"./use-latest\";\n\ninterface ObserveOptions {\n rootMargin?: string | number;\n root?: RefObject<Element>;\n}\n\ninterface Options extends ObserveOptions {\n enabled?: boolean;\n}\n\ninterface VisibleOptions<T = boolean> extends Options {\n initialValue?: T;\n}\n\ntype IntersectionObserverSingleCallback = (\n entry: IntersectionObserverEntry\n) => void;\n\nlet optionlessIntersectionObserver: IntersectionObserver | undefined;\nconst optionlessIntersectionCallbacks = new WeakMap<\n Element,\n IntersectionObserverSingleCallback\n>();\n\nconst individualIntersectionObservers = new WeakMap<\n Element,\n IntersectionObserver\n>();\n\nfunction observe(\n element: Element,\n callback: IntersectionObserverSingleCallback,\n options?: ObserveOptions\n) {\n // Observers without options share a common IntersectionObserver instance, ones with options have their own\n if (!options) {\n if (!optionlessIntersectionObserver) {\n optionlessIntersectionObserver = new IntersectionObserver((entries) => {\n for (const entry of entries) {\n const callback = optionlessIntersectionCallbacks.get(entry.target);\n\n callback?.(entry);\n }\n });\n }\n\n optionlessIntersectionCallbacks.set(element, callback);\n optionlessIntersectionObserver.observe(element);\n } else {\n const observer = new IntersectionObserver(\n (entries) => {\n for (const entry of entries) {\n callback?.(entry);\n }\n },\n {\n root: options.root?.current,\n rootMargin:\n typeof options.rootMargin === \"number\"\n ? `${options.rootMargin}px`\n : options.rootMargin,\n }\n );\n\n individualIntersectionObservers.set(element, observer);\n observer.observe(element);\n }\n}\n\nfunction unobserve(element: Element, options?: ObserveOptions) {\n if (!options) {\n optionlessIntersectionCallbacks.delete(element);\n optionlessIntersectionObserver?.unobserve(element);\n } else {\n const observer = individualIntersectionObservers.get(element);\n\n observer?.unobserve(element);\n individualIntersectionObservers.delete(element);\n }\n}\n\n/**\n * Observe when an element enters or exits the viewport.\n *\n * If you only need to get a stateful visibility value, use the higher level hook `useVisible` instead.\n */\nexport function useIntersectionCallback(\n ref: RefObject<Element>,\n callback: (isIntersecting: boolean, entry: IntersectionObserverEntry) => void,\n options?: Options\n) {\n const enabled = options?.enabled ?? true;\n const latestCallback = useLatest(callback);\n const { root, rootMargin } = options ?? {};\n\n useEffect(() => {\n const element = ref.current;\n\n if (!element) {\n return;\n }\n\n const observeOptions: ObserveOptions = {\n root,\n rootMargin,\n };\n\n if (enabled) {\n observe(\n element,\n (entry) => {\n // The intersection observer entry might be useful in some cases but the main information\n // is whether the element is intersecting or not so we pass that as the first argument.\n latestCallback.current(entry.isIntersecting, entry);\n },\n observeOptions\n );\n } else {\n unobserve(element, observeOptions);\n }\n\n return () => {\n unobserve(element, observeOptions);\n };\n }, [ref, enabled, latestCallback, root, rootMargin]);\n}\n\n/**\n * Observe whether an element is currently visible or not.\n */\nexport function useVisible<T extends boolean | null = boolean>(\n ref: RefObject<Element>,\n options?: VisibleOptions<T>\n) {\n const [isVisible, setVisible] = useState(\n options?.initialValue !== undefined ? options.initialValue : false\n );\n\n useIntersectionCallback(\n ref,\n (isIntersecting) => setVisible(isIntersecting),\n options\n );\n\n return isVisible;\n}\n"],"names":["callback","useLatest","useEffect","useState"],"mappings":"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"}
|
|
@@ -72,7 +72,9 @@ function useIntersectionCallback(ref, callback, options) {
|
|
|
72
72
|
}, [ref, enabled, latestCallback, root, rootMargin]);
|
|
73
73
|
}
|
|
74
74
|
function useVisible(ref, options) {
|
|
75
|
-
const [isVisible, setVisible] = useState(
|
|
75
|
+
const [isVisible, setVisible] = useState(
|
|
76
|
+
options?.initialValue !== void 0 ? options.initialValue : false
|
|
77
|
+
);
|
|
76
78
|
useIntersectionCallback(
|
|
77
79
|
ref,
|
|
78
80
|
(isIntersecting) => setVisible(isIntersecting),
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"use-visible.js","sources":["../../src/utils/use-visible.ts"],"sourcesContent":["import { type RefObject, useEffect, useState } from \"react\";\n\nimport { useLatest } from \"./use-latest\";\n\ninterface ObserveOptions {\n rootMargin?: string | number;\n root?: RefObject<Element>;\n}\n\ninterface Options extends ObserveOptions {\n enabled?: boolean;\n}\n\ntype IntersectionObserverSingleCallback = (\n entry: IntersectionObserverEntry\n) => void;\n\nlet optionlessIntersectionObserver: IntersectionObserver | undefined;\nconst optionlessIntersectionCallbacks = new WeakMap<\n Element,\n IntersectionObserverSingleCallback\n>();\n\nconst individualIntersectionObservers = new WeakMap<\n Element,\n IntersectionObserver\n>();\n\nfunction observe(\n element: Element,\n callback: IntersectionObserverSingleCallback,\n options?: ObserveOptions\n) {\n // Observers without options share a common IntersectionObserver instance, ones with options have their own\n if (!options) {\n if (!optionlessIntersectionObserver) {\n optionlessIntersectionObserver = new IntersectionObserver((entries) => {\n for (const entry of entries) {\n const callback = optionlessIntersectionCallbacks.get(entry.target);\n\n callback?.(entry);\n }\n });\n }\n\n optionlessIntersectionCallbacks.set(element, callback);\n optionlessIntersectionObserver.observe(element);\n } else {\n const observer = new IntersectionObserver(\n (entries) => {\n for (const entry of entries) {\n callback?.(entry);\n }\n },\n {\n root: options.root?.current,\n rootMargin:\n typeof options.rootMargin === \"number\"\n ? `${options.rootMargin}px`\n : options.rootMargin,\n }\n );\n\n individualIntersectionObservers.set(element, observer);\n observer.observe(element);\n }\n}\n\nfunction unobserve(element: Element, options?: ObserveOptions) {\n if (!options) {\n optionlessIntersectionCallbacks.delete(element);\n optionlessIntersectionObserver?.unobserve(element);\n } else {\n const observer = individualIntersectionObservers.get(element);\n\n observer?.unobserve(element);\n individualIntersectionObservers.delete(element);\n }\n}\n\n/**\n * Observe when an element enters or exits the viewport.\n *\n * If you only need to get a stateful visibility value, use the higher level hook `useVisible` instead.\n */\nexport function useIntersectionCallback(\n ref: RefObject<Element>,\n callback: (isIntersecting: boolean, entry: IntersectionObserverEntry) => void,\n options?: Options\n) {\n const enabled = options?.enabled ?? true;\n const latestCallback = useLatest(callback);\n const { root, rootMargin } = options ?? {};\n\n useEffect(() => {\n const element = ref.current;\n\n if (!element) {\n return;\n }\n\n const observeOptions: ObserveOptions = {\n root,\n rootMargin,\n };\n\n if (enabled) {\n observe(\n element,\n (entry) => {\n // The intersection observer entry might be useful in some cases but the main information\n // is whether the element is intersecting or not so we pass that as the first argument.\n latestCallback.current(entry.isIntersecting, entry);\n },\n observeOptions\n );\n } else {\n unobserve(element, observeOptions);\n }\n\n return () => {\n unobserve(element, observeOptions);\n };\n }, [ref, enabled, latestCallback, root, rootMargin]);\n}\n\n/**\n * Observe whether an element is currently visible or not.\n */\nexport function useVisible(ref: RefObject<Element
|
|
1
|
+
{"version":3,"file":"use-visible.js","sources":["../../src/utils/use-visible.ts"],"sourcesContent":["import { type RefObject, useEffect, useState } from \"react\";\n\nimport { useLatest } from \"./use-latest\";\n\ninterface ObserveOptions {\n rootMargin?: string | number;\n root?: RefObject<Element>;\n}\n\ninterface Options extends ObserveOptions {\n enabled?: boolean;\n}\n\ninterface VisibleOptions<T = boolean> extends Options {\n initialValue?: T;\n}\n\ntype IntersectionObserverSingleCallback = (\n entry: IntersectionObserverEntry\n) => void;\n\nlet optionlessIntersectionObserver: IntersectionObserver | undefined;\nconst optionlessIntersectionCallbacks = new WeakMap<\n Element,\n IntersectionObserverSingleCallback\n>();\n\nconst individualIntersectionObservers = new WeakMap<\n Element,\n IntersectionObserver\n>();\n\nfunction observe(\n element: Element,\n callback: IntersectionObserverSingleCallback,\n options?: ObserveOptions\n) {\n // Observers without options share a common IntersectionObserver instance, ones with options have their own\n if (!options) {\n if (!optionlessIntersectionObserver) {\n optionlessIntersectionObserver = new IntersectionObserver((entries) => {\n for (const entry of entries) {\n const callback = optionlessIntersectionCallbacks.get(entry.target);\n\n callback?.(entry);\n }\n });\n }\n\n optionlessIntersectionCallbacks.set(element, callback);\n optionlessIntersectionObserver.observe(element);\n } else {\n const observer = new IntersectionObserver(\n (entries) => {\n for (const entry of entries) {\n callback?.(entry);\n }\n },\n {\n root: options.root?.current,\n rootMargin:\n typeof options.rootMargin === \"number\"\n ? `${options.rootMargin}px`\n : options.rootMargin,\n }\n );\n\n individualIntersectionObservers.set(element, observer);\n observer.observe(element);\n }\n}\n\nfunction unobserve(element: Element, options?: ObserveOptions) {\n if (!options) {\n optionlessIntersectionCallbacks.delete(element);\n optionlessIntersectionObserver?.unobserve(element);\n } else {\n const observer = individualIntersectionObservers.get(element);\n\n observer?.unobserve(element);\n individualIntersectionObservers.delete(element);\n }\n}\n\n/**\n * Observe when an element enters or exits the viewport.\n *\n * If you only need to get a stateful visibility value, use the higher level hook `useVisible` instead.\n */\nexport function useIntersectionCallback(\n ref: RefObject<Element>,\n callback: (isIntersecting: boolean, entry: IntersectionObserverEntry) => void,\n options?: Options\n) {\n const enabled = options?.enabled ?? true;\n const latestCallback = useLatest(callback);\n const { root, rootMargin } = options ?? {};\n\n useEffect(() => {\n const element = ref.current;\n\n if (!element) {\n return;\n }\n\n const observeOptions: ObserveOptions = {\n root,\n rootMargin,\n };\n\n if (enabled) {\n observe(\n element,\n (entry) => {\n // The intersection observer entry might be useful in some cases but the main information\n // is whether the element is intersecting or not so we pass that as the first argument.\n latestCallback.current(entry.isIntersecting, entry);\n },\n observeOptions\n );\n } else {\n unobserve(element, observeOptions);\n }\n\n return () => {\n unobserve(element, observeOptions);\n };\n }, [ref, enabled, latestCallback, root, rootMargin]);\n}\n\n/**\n * Observe whether an element is currently visible or not.\n */\nexport function useVisible<T extends boolean | null = boolean>(\n ref: RefObject<Element>,\n options?: VisibleOptions<T>\n) {\n const [isVisible, setVisible] = useState(\n options?.initialValue !== undefined ? options.initialValue : false\n );\n\n useIntersectionCallback(\n ref,\n (isIntersecting) => setVisible(isIntersecting),\n options\n );\n\n return isVisible;\n}\n"],"names":["callback"],"mappings":"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"}
|
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 "2.25.0-
|
|
4
|
+
const PKG_VERSION = typeof "2.25.0-aiprivatebeta11" === "string" && "2.25.0-aiprivatebeta11";
|
|
5
5
|
const PKG_FORMAT = typeof "cjs" === "string" && "cjs";
|
|
6
6
|
|
|
7
7
|
exports.PKG_FORMAT = PKG_FORMAT;
|
package/dist/version.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
const PKG_NAME = "@liveblocks/react-ui";
|
|
2
|
-
const PKG_VERSION = typeof "2.25.0-
|
|
2
|
+
const PKG_VERSION = typeof "2.25.0-aiprivatebeta11" === "string" && "2.25.0-aiprivatebeta11";
|
|
3
3
|
const PKG_FORMAT = typeof "esm" === "string" && "esm";
|
|
4
4
|
|
|
5
5
|
export { PKG_FORMAT, PKG_NAME, PKG_VERSION };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@liveblocks/react-ui",
|
|
3
|
-
"version": "2.25.0-
|
|
3
|
+
"version": "2.25.0-aiprivatebeta11",
|
|
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": "2.25.0-
|
|
80
|
-
"@liveblocks/core": "2.25.0-
|
|
81
|
-
"@liveblocks/react": "2.25.0-
|
|
79
|
+
"@liveblocks/client": "2.25.0-aiprivatebeta11",
|
|
80
|
+
"@liveblocks/core": "2.25.0-aiprivatebeta11",
|
|
81
|
+
"@liveblocks/react": "2.25.0-aiprivatebeta11",
|
|
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",
|