@liveblocks/react-ui 2.25.0-aiprivatebeta8 → 2.25.0-aiprivatebeta9

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.
Files changed (113) hide show
  1. package/dist/_private/index.cjs +10 -12
  2. package/dist/_private/index.cjs.map +1 -1
  3. package/dist/_private/index.d.cts +141 -112
  4. package/dist/_private/index.d.ts +141 -112
  5. package/dist/_private/index.js +8 -5
  6. package/dist/_private/index.js.map +1 -1
  7. package/dist/components/AiChat.cjs +74 -106
  8. package/dist/components/AiChat.cjs.map +1 -1
  9. package/dist/components/AiChat.js +75 -107
  10. package/dist/components/AiChat.js.map +1 -1
  11. package/dist/components/AiTool.cjs +164 -0
  12. package/dist/components/AiTool.cjs.map +1 -0
  13. package/dist/components/AiTool.js +162 -0
  14. package/dist/components/AiTool.js.map +1 -0
  15. package/dist/components/Comment.cjs +5 -3
  16. package/dist/components/Comment.cjs.map +1 -1
  17. package/dist/components/Comment.js +6 -4
  18. package/dist/components/Comment.js.map +1 -1
  19. package/dist/components/InboxNotificationList.cjs +11 -3
  20. package/dist/components/InboxNotificationList.cjs.map +1 -1
  21. package/dist/components/InboxNotificationList.js +12 -4
  22. package/dist/components/InboxNotificationList.js.map +1 -1
  23. package/dist/components/internal/AiChatAssistantMessage.cjs +43 -199
  24. package/dist/components/internal/AiChatAssistantMessage.cjs.map +1 -1
  25. package/dist/components/internal/AiChatAssistantMessage.js +44 -200
  26. package/dist/components/internal/AiChatAssistantMessage.js.map +1 -1
  27. package/dist/components/internal/AiChatComposer.cjs +1 -1
  28. package/dist/components/internal/AiChatComposer.cjs.map +1 -1
  29. package/dist/components/internal/AiChatComposer.js +1 -1
  30. package/dist/components/internal/AiChatComposer.js.map +1 -1
  31. package/dist/components/internal/AiChatUserMessage.cjs +17 -10
  32. package/dist/components/internal/AiChatUserMessage.cjs.map +1 -1
  33. package/dist/components/internal/AiChatUserMessage.js +17 -10
  34. package/dist/components/internal/AiChatUserMessage.js.map +1 -1
  35. package/dist/components/internal/Button.cjs.map +1 -1
  36. package/dist/components/internal/Button.js.map +1 -1
  37. package/dist/components/internal/CodeBlock.cjs +72 -0
  38. package/dist/components/internal/CodeBlock.cjs.map +1 -0
  39. package/dist/components/internal/CodeBlock.js +70 -0
  40. package/dist/components/internal/CodeBlock.js.map +1 -0
  41. package/dist/components/internal/Emoji.cjs +12 -4
  42. package/dist/components/internal/Emoji.cjs.map +1 -1
  43. package/dist/components/internal/Emoji.js +12 -4
  44. package/dist/components/internal/Emoji.js.map +1 -1
  45. package/dist/components/internal/Prose.cjs +37 -0
  46. package/dist/components/internal/Prose.cjs.map +1 -0
  47. package/dist/components/internal/Prose.js +35 -0
  48. package/dist/components/internal/Prose.js.map +1 -0
  49. package/dist/index.cjs +2 -2
  50. package/dist/index.cjs.map +1 -1
  51. package/dist/index.d.cts +50 -9
  52. package/dist/index.d.ts +50 -9
  53. package/dist/index.js +1 -1
  54. package/dist/index.js.map +1 -1
  55. package/dist/overrides.cjs +2 -4
  56. package/dist/overrides.cjs.map +1 -1
  57. package/dist/overrides.js +2 -4
  58. package/dist/overrides.js.map +1 -1
  59. package/dist/primitives/AiMessage/contexts.cjs +18 -0
  60. package/dist/primitives/AiMessage/contexts.cjs.map +1 -0
  61. package/dist/primitives/AiMessage/contexts.js +15 -0
  62. package/dist/primitives/AiMessage/contexts.js.map +1 -0
  63. package/dist/primitives/AiMessage/index.cjs +133 -0
  64. package/dist/primitives/AiMessage/index.cjs.map +1 -0
  65. package/dist/primitives/AiMessage/index.js +131 -0
  66. package/dist/primitives/AiMessage/index.js.map +1 -0
  67. package/dist/primitives/{internal/Collapsible → Collapsible}/index.cjs +39 -17
  68. package/dist/primitives/Collapsible/index.cjs.map +1 -0
  69. package/dist/primitives/{internal/Collapsible → Collapsible}/index.js +37 -15
  70. package/dist/primitives/Collapsible/index.js.map +1 -0
  71. package/dist/primitives/{internal/Markdown.cjs → Markdown.cjs} +99 -63
  72. package/dist/primitives/Markdown.cjs.map +1 -0
  73. package/dist/primitives/{internal/Markdown.js → Markdown.js} +100 -63
  74. package/dist/primitives/Markdown.js.map +1 -0
  75. package/dist/primitives/index.cjs +4 -6
  76. package/dist/primitives/index.cjs.map +1 -1
  77. package/dist/primitives/index.d.cts +2 -75
  78. package/dist/primitives/index.d.ts +2 -75
  79. package/dist/primitives/index.js +4 -6
  80. package/dist/primitives/index.js.map +1 -1
  81. package/dist/utils/ErrorBoundary.cjs +48 -0
  82. package/dist/utils/ErrorBoundary.cjs.map +1 -0
  83. package/dist/utils/ErrorBoundary.js +45 -0
  84. package/dist/utils/ErrorBoundary.js.map +1 -0
  85. package/dist/utils/use-visible.cjs +63 -45
  86. package/dist/utils/use-visible.cjs.map +1 -1
  87. package/dist/utils/use-visible.js +64 -46
  88. package/dist/utils/use-visible.js.map +1 -1
  89. package/dist/version.cjs +1 -1
  90. package/dist/version.js +1 -1
  91. package/package.json +4 -4
  92. package/src/styles/constants.css +1 -1
  93. package/src/styles/dark/index.css +7 -3
  94. package/src/styles/index.css +555 -253
  95. package/src/styles/utils.css +1 -1
  96. package/styles/dark/attributes.css +1 -1
  97. package/styles/dark/attributes.css.map +1 -1
  98. package/styles/dark/media-query.css +1 -1
  99. package/styles/dark/media-query.css.map +1 -1
  100. package/styles.css +1 -1
  101. package/styles.css.map +1 -1
  102. package/dist/components/AiToolDebugger.cjs +0 -74
  103. package/dist/components/AiToolDebugger.cjs.map +0 -1
  104. package/dist/components/AiToolDebugger.js +0 -72
  105. package/dist/components/AiToolDebugger.js.map +0 -1
  106. package/dist/primitives/internal/Collapsible/index.cjs.map +0 -1
  107. package/dist/primitives/internal/Collapsible/index.js.map +0 -1
  108. package/dist/primitives/internal/Emoji.cjs +0 -32
  109. package/dist/primitives/internal/Emoji.cjs.map +0 -1
  110. package/dist/primitives/internal/Emoji.js +0 -30
  111. package/dist/primitives/internal/Emoji.js.map +0 -1
  112. package/dist/primitives/internal/Markdown.cjs.map +0 -1
  113. package/dist/primitives/internal/Markdown.js.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"AiChat.js","sources":["../../src/components/AiChat.tsx"],"sourcesContent":["import type {\n AiKnowledgeSource,\n AiToolDefinition,\n CopilotId,\n MessageId,\n UiChatMessage,\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 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 { AiChatAssistantMessage } from \"./internal/AiChatAssistantMessage\";\nimport { AiChatComposer } from \"./internal/AiChatComposer\";\nimport { AiChatUserMessage } from \"./internal/AiChatUserMessage\";\n\n/**\n * The number of pixels from the bottom of the messages list to trigger the scroll to bottom.\n */\nconst MIN_DISTANCE_TO_BOTTOM = 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, AiToolDefinition>;\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>; // TODO: Add more slots than the global ones over time (Markdown tags, the empty state, etc)\n}\n\nexport const AiChat = forwardRef<HTMLDivElement, AiChatProps>(\n (\n {\n chatId,\n copilotId,\n autoFocus,\n overrides,\n knowledge,\n tools = {},\n components,\n className,\n ...props\n },\n forwardedRef\n ) => {\n const { messages, isLoading, error } = useAiChatMessages(chatId);\n const $ = useOverrides(overrides);\n\n const containerRef = useRef<HTMLDivElement | null>(null);\n const [distanceToBottom, setDistanceToBottom] = useState<number | null>(\n null\n );\n const client = useClient();\n const ai = client[kInternal].ai;\n\n const [lastSendMessageId, setLastSendMessageId] =\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 useEffect(() => {\n const container = containerRef.current;\n if (container === null) return;\n function handleScrollChange() {\n const container = containerRef.current;\n if (container === null) return;\n\n setDistanceToBottom(\n container.scrollHeight - container.clientHeight - container.scrollTop\n );\n }\n container.addEventListener(\"scroll\", handleScrollChange);\n return () => {\n container.removeEventListener(\"scroll\", handleScrollChange);\n };\n }, []);\n\n useEffect(() => {\n const container = containerRef.current;\n if (container === null) return;\n\n setDistanceToBottom(\n container.scrollHeight - container.clientHeight - container.scrollTop\n );\n }, [messages]);\n\n useEffect(() => {\n const container = containerRef.current;\n if (container === null) return;\n\n container.scrollTo({\n top: container.scrollHeight,\n behavior: \"smooth\",\n });\n }, [lastSendMessageId]);\n\n useEffect(() => {\n const container = containerRef.current;\n if (container === null) return;\n\n const observer = new ResizeObserver(() => {\n const container = containerRef.current;\n if (container === null) return;\n setDistanceToBottom(\n container.scrollHeight - container.clientHeight - container.scrollTop\n );\n });\n observer.observe(container);\n return () => {\n observer.disconnect();\n };\n }, []);\n\n const scrollToBottomCallbackRef = useRef<() => void>(undefined);\n if (scrollToBottomCallbackRef.current === undefined) {\n scrollToBottomCallbackRef.current = function () {\n const container = containerRef.current;\n if (container === null) return;\n\n container.scrollTo({\n top: container.scrollHeight,\n behavior: \"instant\",\n });\n };\n }\n const isScrollIndicatorVisible =\n distanceToBottom !== null && distanceToBottom > MIN_DISTANCE_TO_BOTTOM;\n\n return (\n <div\n ref={containerRef}\n {...props}\n className={classNames(\"lb-root lb-ai-chat\", className)}\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 <div className=\"lb-loading lb-ai-chat-loading\">\n <SpinnerIcon />\n </div>\n ) : error !== undefined ? (\n <div className=\"lb-error lb-ai-chat-error\">\n {$.AI_CHAT_MESSAGES_ERROR(error)}\n </div>\n ) : (\n <div className=\"lb-ai-chat-messages\">\n <Messages\n messages={messages}\n overrides={$}\n components={components}\n onDistanceToBottomChange={scrollToBottomCallbackRef.current}\n />\n </div>\n )}\n </div>\n\n <div className=\"lb-ai-chat-footer\">\n <div className=\"lb-ai-chat-footer-actions\">\n <div\n className=\"lb-elevation 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={() => {\n const container = containerRef.current;\n if (container === null) return;\n\n container.scrollTo({\n top: container.scrollHeight,\n behavior: \"smooth\",\n });\n }}\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 }) => setLastSendMessageId(id)}\n />\n </div>\n </div>\n );\n }\n);\n\nfunction Messages({\n messages,\n overrides,\n components,\n onDistanceToBottomChange,\n}: {\n messages: readonly UiChatMessage[];\n overrides?: Partial<GlobalOverrides & AiChatMessageOverrides>;\n components?: Partial<GlobalComponents>;\n onDistanceToBottomChange: () => void;\n}) {\n useLayoutEffect(() => {\n onDistanceToBottomChange();\n }, [onDistanceToBottomChange]);\n\n return 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}\n"],"names":["container"],"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 { 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\n// No props for now\ntype AiChatComponentsEmptyProps = Record<string, never>;\n\n// No props for now\ntype AiChatComponentsLoadingProps = Record<string, never>;\n\n// TODO: Add Markdown components\ntype AiChatComponents = {\n Empty: ComponentType<AiChatComponentsEmptyProps>;\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 />\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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"}
@@ -0,0 +1,164 @@
1
+ 'use strict';
2
+
3
+ var jsxRuntime = require('react/jsx-runtime');
4
+ var core = require('@liveblocks/core');
5
+ var react = require('react');
6
+ require('../_private/index.cjs');
7
+ require('../icons/index.cjs');
8
+ var contexts = require('../primitives/AiMessage/contexts.cjs');
9
+ var index = require('../primitives/Collapsible/index.cjs');
10
+ var classNames = require('../utils/class-names.cjs');
11
+ var CodeBlock = require('./internal/CodeBlock.cjs');
12
+ var Button = require('./internal/Button.cjs');
13
+ var ChevronRight = require('../icons/ChevronRight.cjs');
14
+ var CheckCircleFill = require('../icons/CheckCircleFill.cjs');
15
+ var Spinner = require('../icons/Spinner.cjs');
16
+
17
+ function AiToolIcon({ className, ...props }) {
18
+ return /* @__PURE__ */ jsxRuntime.jsx("div", {
19
+ className: classNames.classNames("lb-ai-tool-icon", className),
20
+ ...props
21
+ });
22
+ }
23
+ function AiToolInspector({ className, ...props }) {
24
+ const { args, partialArgs, result } = contexts.useAiToolInvocationContext();
25
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", {
26
+ className: classNames.classNames("lb-ai-tool-inspector", className),
27
+ ...props,
28
+ children: [
29
+ /* @__PURE__ */ jsxRuntime.jsx(CodeBlock.CodeBlock, {
30
+ title: "Arguments",
31
+ code: JSON.stringify(args ?? partialArgs, null, 2)
32
+ }),
33
+ result !== void 0 ? /* @__PURE__ */ jsxRuntime.jsx(CodeBlock.CodeBlock, {
34
+ title: "Result",
35
+ code: JSON.stringify(result, null, 2)
36
+ }) : null
37
+ ]
38
+ });
39
+ }
40
+ function AiToolConfirmation({
41
+ children,
42
+ variant = "default",
43
+ confirm,
44
+ cancel,
45
+ className,
46
+ ...props
47
+ }) {
48
+ const { status, args, respond, toolName, toolCallId } = contexts.useAiToolInvocationContext();
49
+ const enabled = status === "executing";
50
+ const context = react.useMemo(
51
+ () => ({ toolName, toolCallId }),
52
+ [toolName, toolCallId]
53
+ );
54
+ const onConfirmClick = react.useCallback(async () => {
55
+ if (enabled) {
56
+ respond(await confirm(args, context));
57
+ }
58
+ }, [enabled, args, confirm, respond, context]);
59
+ const onCancelClick = react.useCallback(async () => {
60
+ if (enabled) {
61
+ respond(await cancel(args, context));
62
+ }
63
+ }, [enabled, args, cancel, respond, context]);
64
+ if (status === "executed") {
65
+ return null;
66
+ }
67
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", {
68
+ className: classNames.classNames("lb-ai-tool-confirmation", className),
69
+ ...props,
70
+ children: [
71
+ children ? /* @__PURE__ */ jsxRuntime.jsx("div", {
72
+ className: "lb-ai-tool-confirmation-content",
73
+ children
74
+ }) : null,
75
+ /* @__PURE__ */ jsxRuntime.jsx("div", {
76
+ className: "lb-ai-tool-confirmation-footer",
77
+ children: /* @__PURE__ */ jsxRuntime.jsxs("div", {
78
+ className: "lb-ai-tool-confirmation-actions",
79
+ children: [
80
+ /* @__PURE__ */ jsxRuntime.jsx(Button.Button, {
81
+ disabled: !enabled,
82
+ onClick: onConfirmClick,
83
+ variant: variant === "destructive" ? "destructive" : "primary",
84
+ children: "Confirm"
85
+ }),
86
+ /* @__PURE__ */ jsxRuntime.jsx(Button.Button, {
87
+ disabled: !enabled,
88
+ onClick: onCancelClick,
89
+ variant: "secondary",
90
+ children: "Cancel"
91
+ })
92
+ ]
93
+ })
94
+ })
95
+ ]
96
+ });
97
+ }
98
+ function prettifyString(string) {
99
+ return string.replace(/([a-z])([A-Z])/g, "$1 $2").replace(/[_-]+/g, " ").replace(/\s+/g, " ").trim().toLowerCase().replace(/^\w/, (character) => character.toUpperCase());
100
+ }
101
+ const noop = () => {
102
+ };
103
+ const AiTool = Object.assign(
104
+ react.forwardRef(
105
+ ({ children, title, icon, className, ...props }, forwardedRef) => {
106
+ const {
107
+ status,
108
+ toolName,
109
+ [core.kInternal]: { execute }
110
+ } = contexts.useAiToolInvocationContext();
111
+ const [isOpen, setIsOpen] = react.useState(true);
112
+ const hasContent = react.Children.count(children) > 0;
113
+ const resolvedTitle = react.useMemo(() => {
114
+ return title ?? prettifyString(toolName);
115
+ }, [title, toolName]);
116
+ return /* @__PURE__ */ jsxRuntime.jsxs(index.Root, {
117
+ ref: forwardedRef,
118
+ className: classNames.classNames("lb-collapsible lb-ai-tool", className),
119
+ ...props,
120
+ open: hasContent ? isOpen : false,
121
+ onOpenChange: hasContent ? setIsOpen : noop,
122
+ disabled: !hasContent,
123
+ children: [
124
+ /* @__PURE__ */ jsxRuntime.jsxs(index.Trigger, {
125
+ className: "lb-collapsible-trigger lb-ai-tool-header",
126
+ children: [
127
+ icon ? /* @__PURE__ */ jsxRuntime.jsx("div", {
128
+ className: "lb-ai-tool-header-icon-container",
129
+ children: icon
130
+ }) : null,
131
+ /* @__PURE__ */ jsxRuntime.jsx("span", {
132
+ className: "lb-ai-tool-header-title",
133
+ children: resolvedTitle
134
+ }),
135
+ hasContent ? /* @__PURE__ */ jsxRuntime.jsx("span", {
136
+ className: "lb-collapsible-chevron lb-icon-container",
137
+ children: /* @__PURE__ */ jsxRuntime.jsx(ChevronRight.ChevronRightIcon, {})
138
+ }) : null,
139
+ /* @__PURE__ */ jsxRuntime.jsx("div", {
140
+ className: "lb-ai-tool-header-status",
141
+ children: status === "executed" ? /* @__PURE__ */ jsxRuntime.jsx(CheckCircleFill.CheckCircleFillIcon, {}) : execute !== void 0 ? /* @__PURE__ */ jsxRuntime.jsx(Spinner.SpinnerIcon, {}) : null
142
+ })
143
+ ]
144
+ }),
145
+ children ? /* @__PURE__ */ jsxRuntime.jsx(index.Content, {
146
+ className: "lb-collapsible-content lb-ai-tool-content-container",
147
+ children: /* @__PURE__ */ jsxRuntime.jsx("div", {
148
+ className: "lb-ai-tool-content",
149
+ children
150
+ })
151
+ }) : null
152
+ ]
153
+ });
154
+ }
155
+ ),
156
+ {
157
+ Icon: AiToolIcon,
158
+ Inspector: AiToolInspector,
159
+ Confirmation: AiToolConfirmation
160
+ }
161
+ );
162
+
163
+ exports.AiTool = AiTool;
164
+ //# sourceMappingURL=AiTool.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AiTool.cjs","sources":["../../src/components/AiTool.tsx"],"sourcesContent":["import {\n type AiToolExecuteCallback,\n type AiToolTypePack,\n type JsonObject,\n kInternal,\n type ToolResultData,\n} from \"@liveblocks/core\";\nimport type { ComponentProps, ReactNode } from \"react\";\nimport { Children, forwardRef, useCallback, useMemo, useState } from \"react\";\n\nimport { Button } from \"../_private\";\nimport { CheckCircleFillIcon, ChevronRightIcon, SpinnerIcon } from \"../icons\";\nimport { useAiToolInvocationContext } from \"../primitives/AiMessage/contexts\";\nimport * as Collapsible from \"../primitives/Collapsible\";\nimport { classNames } from \"../utils/class-names\";\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\nexport type AiToolIconProps = ComponentProps<\"div\">;\n\nexport type AiToolInspectorProps = ComponentProps<\"div\">;\n\n// TODO: AiToolConfirmationProps might need a generic since we're outside of the\n// tool definition so things like inferred args and result types are not\n// available here for `confirm` and `cancel`\n\n/**\n * @private This API will change, and is not considered stable. DO NOT RELY on it.\n *\n * This component can be used to build human-in-the-loop interfaces.\n */\nexport interface AiToolConfirmationProps<\n A extends JsonObject,\n R extends ToolResultData,\n> extends ComponentProps<\"div\"> {\n args?: A;\n confirm: AiToolExecuteCallback<A, R>;\n cancel: AiToolExecuteCallback<A, R>;\n variant?: \"default\" | \"destructive\";\n\n // TODO: Use existing overrides API to allow customizing the \"Confirm\" and \"Cancel\" labels\n // overrides?: Partial<GlobalOverrides & AiToolConfirmationOverrides>;\n}\n\nfunction AiToolIcon({ className, ...props }: AiToolIconProps) {\n return (\n <div className={classNames(\"lb-ai-tool-icon\", className)} {...props} />\n );\n}\n\nfunction AiToolInspector({ className, ...props }: AiToolInspectorProps) {\n const { args, partialArgs, result } = useAiToolInvocationContext();\n\n return (\n <div className={classNames(\"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 ToolResultData = TPack[\"R\"],\n>({\n children,\n variant = \"default\",\n confirm,\n cancel,\n className,\n ...props\n}: AiToolConfirmationProps<A, R>) {\n const { status, args, respond, toolName, toolCallId } =\n useAiToolInvocationContext();\n\n const enabled = status === \"executing\";\n\n const context = useMemo(\n () => ({ toolName, toolCallId }),\n [toolName, toolCallId]\n );\n\n const onConfirmClick = useCallback(async () => {\n if (enabled) {\n respond(await confirm(args as A, context));\n }\n }, [enabled, args, confirm, respond, context]);\n\n const onCancelClick = useCallback(async () => {\n if (enabled) {\n respond(await cancel(args as A, context));\n }\n }, [enabled, args, cancel, respond, context]);\n\n if (status === \"executed\") {\n return null;\n }\n\n return (\n <div\n className={classNames(\"lb-ai-tool-confirmation\", className)}\n {...props}\n >\n {children ? (\n <div className=\"lb-ai-tool-confirmation-content\">{children}</div>\n ) : null}\n <div className=\"lb-ai-tool-confirmation-footer\">\n <div className=\"lb-ai-tool-confirmation-actions\">\n <Button\n disabled={!enabled}\n onClick={onConfirmClick}\n variant={variant === \"destructive\" ? \"destructive\" : \"primary\"}\n >\n Confirm\n </Button>\n <Button\n disabled={!enabled}\n onClick={onCancelClick}\n variant=\"secondary\"\n >\n Cancel\n </Button>\n </div>\n </div>\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\nconst noop = () => {};\n\nexport const AiTool = Object.assign(\n forwardRef<HTMLDivElement, AiToolProps>(\n ({ children, title, icon, className, ...props }, forwardedRef) => {\n const {\n status,\n toolName,\n [kInternal]: { execute },\n } = useAiToolInvocationContext();\n const [isOpen, setIsOpen] = useState(true);\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 const hasContent = Children.count(children) > 0;\n const resolvedTitle = useMemo(() => {\n return title ?? prettifyString(toolName);\n }, [title, toolName]);\n\n return (\n <Collapsible.Root\n ref={forwardedRef}\n className={classNames(\"lb-collapsible lb-ai-tool\", className)}\n {...props}\n open={hasContent ? isOpen : false}\n onOpenChange={hasContent ? setIsOpen : noop}\n disabled={!hasContent}\n >\n <Collapsible.Trigger className=\"lb-collapsible-trigger lb-ai-tool-header\">\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 {hasContent ? (\n <span className=\"lb-collapsible-chevron lb-icon-container\">\n <ChevronRightIcon />\n </span>\n ) : null}\n <div className=\"lb-ai-tool-header-status\">\n {status === \"executed\" ? (\n <CheckCircleFillIcon />\n ) : execute !== undefined ? (\n // Only show a spinner if the tool has an `execute` method.\n <SpinnerIcon />\n ) : null}\n </div>\n </Collapsible.Trigger>\n\n {children ? (\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 Icon: AiToolIcon,\n Inspector: AiToolInspector,\n Confirmation: AiToolConfirmation,\n }\n);\n"],"names":["jsx","classNames","useAiToolInvocationContext","jsxs","CodeBlock","useMemo","useCallback","Button","forwardRef","kInternal","useState","Children","Collapsible.Root","Collapsible.Trigger","ChevronRightIcon","CheckCircleFillIcon","SpinnerIcon","Collapsible.Content"],"mappings":";;;;;;;;;;;;;;;;AAiEA,SAAS,UAAW,CAAA,EAAE,SAAc,EAAA,GAAA,KAAA,EAA0B,EAAA;AAC5D,EAAA,uBACGA,cAAA,CAAA,KAAA,EAAA;AAAA,IAAI,SAAA,EAAWC,qBAAW,CAAA,iBAAA,EAAmB,SAAS,CAAA;AAAA,IAAI,GAAG,KAAA;AAAA,GAAO,CAAA,CAAA;AAEzE,CAAA;AAEA,SAAS,eAAgB,CAAA,EAAE,SAAc,EAAA,GAAA,KAAA,EAA+B,EAAA;AACtE,EAAA,MAAM,EAAE,IAAA,EAAM,WAAa,EAAA,MAAA,KAAWC,mCAA2B,EAAA,CAAA;AAEjE,EAAA,uBACGC,eAAA,CAAA,KAAA,EAAA;AAAA,IAAI,SAAA,EAAWF,qBAAW,CAAA,sBAAA,EAAwB,SAAS,CAAA;AAAA,IAAI,GAAG,KAAA;AAAA,IACjE,QAAA,EAAA;AAAA,sBAACD,cAAA,CAAAI,mBAAA,EAAA;AAAA,QACC,KAAM,EAAA,WAAA;AAAA,QACN,MAAM,IAAK,CAAA,SAAA,CAAU,IAAQ,IAAA,WAAA,EAAa,MAAM,CAAC,CAAA;AAAA,OACnD,CAAA;AAAA,MACC,MAAA,KAAW,yBACTJ,cAAA,CAAAI,mBAAA,EAAA;AAAA,QAAU,KAAM,EAAA,QAAA;AAAA,QAAS,IAAM,EAAA,IAAA,CAAK,SAAU,CAAA,MAAA,EAAQ,MAAM,CAAC,CAAA;AAAA,OAAG,CAC/D,GAAA,IAAA;AAAA,KAAA;AAAA,GACN,CAAA,CAAA;AAEJ,CAAA;AAEA,SAAS,kBAIP,CAAA;AAAA,EACA,QAAA;AAAA,EACA,OAAU,GAAA,SAAA;AAAA,EACV,OAAA;AAAA,EACA,MAAA;AAAA,EACA,SAAA;AAAA,EACG,GAAA,KAAA;AACL,CAAkC,EAAA;AAChC,EAAA,MAAM,EAAE,MAAQ,EAAA,IAAA,EAAM,SAAS,QAAU,EAAA,UAAA,KACvCF,mCAA2B,EAAA,CAAA;AAE7B,EAAA,MAAM,UAAU,MAAW,KAAA,WAAA,CAAA;AAE3B,EAAA,MAAM,OAAU,GAAAG,aAAA;AAAA,IACd,OAAO,EAAE,QAAA,EAAU,UAAW,EAAA,CAAA;AAAA,IAC9B,CAAC,UAAU,UAAU,CAAA;AAAA,GACvB,CAAA;AAEA,EAAM,MAAA,cAAA,GAAiBC,kBAAY,YAAY;AAC7C,IAAA,IAAI,OAAS,EAAA;AACX,MAAA,OAAA,CAAQ,MAAM,OAAA,CAAQ,IAAW,EAAA,OAAO,CAAC,CAAA,CAAA;AAAA,KAC3C;AAAA,KACC,CAAC,OAAA,EAAS,MAAM,OAAS,EAAA,OAAA,EAAS,OAAO,CAAC,CAAA,CAAA;AAE7C,EAAM,MAAA,aAAA,GAAgBA,kBAAY,YAAY;AAC5C,IAAA,IAAI,OAAS,EAAA;AACX,MAAA,OAAA,CAAQ,MAAM,MAAA,CAAO,IAAW,EAAA,OAAO,CAAC,CAAA,CAAA;AAAA,KAC1C;AAAA,KACC,CAAC,OAAA,EAAS,MAAM,MAAQ,EAAA,OAAA,EAAS,OAAO,CAAC,CAAA,CAAA;AAE5C,EAAA,IAAI,WAAW,UAAY,EAAA;AACzB,IAAO,OAAA,IAAA,CAAA;AAAA,GACT;AAEA,EAAA,uBACGH,eAAA,CAAA,KAAA,EAAA;AAAA,IACC,SAAA,EAAWF,qBAAW,CAAA,yBAAA,EAA2B,SAAS,CAAA;AAAA,IACzD,GAAG,KAAA;AAAA,IAEH,QAAA,EAAA;AAAA,MAAA,QAAA,mBACED,cAAA,CAAA,KAAA,EAAA;AAAA,QAAI,SAAU,EAAA,iCAAA;AAAA,QAAmC,QAAA;AAAA,OAAS,CACzD,GAAA,IAAA;AAAA,sBACHA,cAAA,CAAA,KAAA,EAAA;AAAA,QAAI,SAAU,EAAA,gCAAA;AAAA,QACb,QAAC,kBAAAG,eAAA,CAAA,KAAA,EAAA;AAAA,UAAI,SAAU,EAAA,iCAAA;AAAA,UACb,QAAA,EAAA;AAAA,4BAACH,cAAA,CAAAO,aAAA,EAAA;AAAA,cACC,UAAU,CAAC,OAAA;AAAA,cACX,OAAS,EAAA,cAAA;AAAA,cACT,OAAA,EAAS,OAAY,KAAA,aAAA,GAAgB,aAAgB,GAAA,SAAA;AAAA,cACtD,QAAA,EAAA,SAAA;AAAA,aAED,CAAA;AAAA,4BACCP,cAAA,CAAAO,aAAA,EAAA;AAAA,cACC,UAAU,CAAC,OAAA;AAAA,cACX,OAAS,EAAA,aAAA;AAAA,cACT,OAAQ,EAAA,WAAA;AAAA,cACT,QAAA,EAAA,QAAA;AAAA,aAED,CAAA;AAAA,WAAA;AAAA,SACF,CAAA;AAAA,OACF,CAAA;AAAA,KAAA;AAAA,GACF,CAAA,CAAA;AAEJ,CAAA;AAEA,SAAS,eAAe,MAAgB,EAAA;AACtC,EACE,OAAA,MAAA,CAEG,QAAQ,iBAAmB,EAAA,OAAO,EAElC,OAAQ,CAAA,QAAA,EAAU,GAAG,CAAA,CAErB,OAAQ,CAAA,MAAA,EAAQ,GAAG,CAEnB,CAAA,IAAA,EAEA,CAAA,WAAA,EACA,CAAA,OAAA,CAAQ,OAAO,CAAC,SAAA,KAAc,SAAU,CAAA,WAAA,EAAa,CAAA,CAAA;AAE5D,CAAA;AAEA,MAAM,OAAO,MAAM;AAAC,CAAA,CAAA;AAEb,MAAM,SAAS,MAAO,CAAA,MAAA;AAAA,EAC3BC,gBAAA;AAAA,IACE,CAAC,EAAE,QAAU,EAAA,KAAA,EAAO,MAAM,SAAc,EAAA,GAAA,KAAA,IAAS,YAAiB,KAAA;AAChE,MAAM,MAAA;AAAA,QACJ,MAAA;AAAA,QACA,QAAA;AAAA,QACC,CAAAC,cAAA,GAAY,EAAE,OAAQ,EAAA;AAAA,UACrBP,mCAA2B,EAAA,CAAA;AAC/B,MAAA,MAAM,CAAC,MAAA,EAAQ,SAAS,CAAA,GAAIQ,eAAS,IAAI,CAAA,CAAA;AAQzC,MAAA,MAAM,UAAa,GAAAC,cAAA,CAAS,KAAM,CAAA,QAAQ,CAAI,GAAA,CAAA,CAAA;AAC9C,MAAM,MAAA,aAAA,GAAgBN,cAAQ,MAAM;AAClC,QAAO,OAAA,KAAA,IAAS,eAAe,QAAQ,CAAA,CAAA;AAAA,OACtC,EAAA,CAAC,KAAO,EAAA,QAAQ,CAAC,CAAA,CAAA;AAEpB,MACE,uBAAAF,eAAA,CAACS,UAAA,EAAA;AAAA,QACC,GAAK,EAAA,YAAA;AAAA,QACL,SAAA,EAAWX,qBAAW,CAAA,2BAAA,EAA6B,SAAS,CAAA;AAAA,QAC3D,GAAG,KAAA;AAAA,QACJ,IAAA,EAAM,aAAa,MAAS,GAAA,KAAA;AAAA,QAC5B,YAAA,EAAc,aAAa,SAAY,GAAA,IAAA;AAAA,QACvC,UAAU,CAAC,UAAA;AAAA,QAEX,QAAA,EAAA;AAAA,0BAAAE,eAAA,CAACU,aAAA,EAAA;AAAA,YAAoB,SAAU,EAAA,0CAAA;AAAA,YAC5B,QAAA,EAAA;AAAA,cAAA,IAAA,mBACEb,cAAA,CAAA,KAAA,EAAA;AAAA,gBAAI,SAAU,EAAA,kCAAA;AAAA,gBAAoC,QAAA,EAAA,IAAA;AAAA,eAAK,CACtD,GAAA,IAAA;AAAA,8BACHA,cAAA,CAAA,MAAA,EAAA;AAAA,gBAAK,SAAU,EAAA,yBAAA;AAAA,gBAA2B,QAAA,EAAA,aAAA;AAAA,eAAc,CAAA;AAAA,cACxD,6BACEA,cAAA,CAAA,MAAA,EAAA;AAAA,gBAAK,SAAU,EAAA,0CAAA;AAAA,gBACd,yCAACc,6BAAiB,EAAA,EAAA,CAAA;AAAA,eACpB,CACE,GAAA,IAAA;AAAA,8BACHd,cAAA,CAAA,KAAA,EAAA;AAAA,gBAAI,SAAU,EAAA,0BAAA;AAAA,gBACZ,QAAA,EAAA,MAAA,KAAW,6BACTA,cAAA,CAAAe,mCAAA,EAAA,EAAoB,IACnB,OAAY,KAAA,KAAA,CAAA,mBAEbf,cAAA,CAAAgB,mBAAA,EAAA,EAAY,CACX,GAAA,IAAA;AAAA,eACN,CAAA;AAAA,aAAA;AAAA,WACF,CAAA;AAAA,UAEC,QAAA,mBACEhB,cAAA,CAAAiB,aAAA,EAAA;AAAA,YAAoB,SAAU,EAAA,qDAAA;AAAA,YAC7B,QAAC,kBAAAjB,cAAA,CAAA,KAAA,EAAA;AAAA,cAAI,SAAU,EAAA,oBAAA;AAAA,cAAsB,QAAA;AAAA,aAAS,CAAA;AAAA,WAChD,CACE,GAAA,IAAA;AAAA,SAAA;AAAA,OACN,CAAA,CAAA;AAAA,KAEJ;AAAA,GACF;AAAA,EACA;AAAA,IACE,IAAM,EAAA,UAAA;AAAA,IACN,SAAW,EAAA,eAAA;AAAA,IACX,YAAc,EAAA,kBAAA;AAAA,GAChB;AACF;;;;"}
@@ -0,0 +1,162 @@
1
+ import { jsx, jsxs } from 'react/jsx-runtime';
2
+ import { kInternal } from '@liveblocks/core';
3
+ import { useMemo, useCallback, forwardRef, useState, Children } from 'react';
4
+ import '../_private/index.js';
5
+ import '../icons/index.js';
6
+ import { useAiToolInvocationContext } from '../primitives/AiMessage/contexts.js';
7
+ import { Root as CollapsibleRoot, Trigger as CollapsibleTrigger, Content as CollapsibleContent } from '../primitives/Collapsible/index.js';
8
+ import { classNames } from '../utils/class-names.js';
9
+ import { CodeBlock } from './internal/CodeBlock.js';
10
+ import { Button } from './internal/Button.js';
11
+ import { ChevronRightIcon } from '../icons/ChevronRight.js';
12
+ import { CheckCircleFillIcon } from '../icons/CheckCircleFill.js';
13
+ import { SpinnerIcon } from '../icons/Spinner.js';
14
+
15
+ function AiToolIcon({ className, ...props }) {
16
+ return /* @__PURE__ */ jsx("div", {
17
+ className: classNames("lb-ai-tool-icon", className),
18
+ ...props
19
+ });
20
+ }
21
+ function AiToolInspector({ className, ...props }) {
22
+ const { args, partialArgs, result } = useAiToolInvocationContext();
23
+ return /* @__PURE__ */ jsxs("div", {
24
+ className: classNames("lb-ai-tool-inspector", className),
25
+ ...props,
26
+ children: [
27
+ /* @__PURE__ */ jsx(CodeBlock, {
28
+ title: "Arguments",
29
+ code: JSON.stringify(args ?? partialArgs, null, 2)
30
+ }),
31
+ result !== void 0 ? /* @__PURE__ */ jsx(CodeBlock, {
32
+ title: "Result",
33
+ code: JSON.stringify(result, null, 2)
34
+ }) : null
35
+ ]
36
+ });
37
+ }
38
+ function AiToolConfirmation({
39
+ children,
40
+ variant = "default",
41
+ confirm,
42
+ cancel,
43
+ className,
44
+ ...props
45
+ }) {
46
+ const { status, args, respond, toolName, toolCallId } = useAiToolInvocationContext();
47
+ const enabled = status === "executing";
48
+ const context = useMemo(
49
+ () => ({ toolName, toolCallId }),
50
+ [toolName, toolCallId]
51
+ );
52
+ const onConfirmClick = useCallback(async () => {
53
+ if (enabled) {
54
+ respond(await confirm(args, context));
55
+ }
56
+ }, [enabled, args, confirm, respond, context]);
57
+ const onCancelClick = useCallback(async () => {
58
+ if (enabled) {
59
+ respond(await cancel(args, context));
60
+ }
61
+ }, [enabled, args, cancel, respond, context]);
62
+ if (status === "executed") {
63
+ return null;
64
+ }
65
+ return /* @__PURE__ */ jsxs("div", {
66
+ className: classNames("lb-ai-tool-confirmation", className),
67
+ ...props,
68
+ children: [
69
+ children ? /* @__PURE__ */ jsx("div", {
70
+ className: "lb-ai-tool-confirmation-content",
71
+ children
72
+ }) : null,
73
+ /* @__PURE__ */ jsx("div", {
74
+ className: "lb-ai-tool-confirmation-footer",
75
+ children: /* @__PURE__ */ jsxs("div", {
76
+ className: "lb-ai-tool-confirmation-actions",
77
+ children: [
78
+ /* @__PURE__ */ jsx(Button, {
79
+ disabled: !enabled,
80
+ onClick: onConfirmClick,
81
+ variant: variant === "destructive" ? "destructive" : "primary",
82
+ children: "Confirm"
83
+ }),
84
+ /* @__PURE__ */ jsx(Button, {
85
+ disabled: !enabled,
86
+ onClick: onCancelClick,
87
+ variant: "secondary",
88
+ children: "Cancel"
89
+ })
90
+ ]
91
+ })
92
+ })
93
+ ]
94
+ });
95
+ }
96
+ function prettifyString(string) {
97
+ return string.replace(/([a-z])([A-Z])/g, "$1 $2").replace(/[_-]+/g, " ").replace(/\s+/g, " ").trim().toLowerCase().replace(/^\w/, (character) => character.toUpperCase());
98
+ }
99
+ const noop = () => {
100
+ };
101
+ const AiTool = Object.assign(
102
+ forwardRef(
103
+ ({ children, title, icon, className, ...props }, forwardedRef) => {
104
+ const {
105
+ status,
106
+ toolName,
107
+ [kInternal]: { execute }
108
+ } = useAiToolInvocationContext();
109
+ const [isOpen, setIsOpen] = useState(true);
110
+ const hasContent = Children.count(children) > 0;
111
+ const resolvedTitle = useMemo(() => {
112
+ return title ?? prettifyString(toolName);
113
+ }, [title, toolName]);
114
+ return /* @__PURE__ */ jsxs(CollapsibleRoot, {
115
+ ref: forwardedRef,
116
+ className: classNames("lb-collapsible lb-ai-tool", className),
117
+ ...props,
118
+ open: hasContent ? isOpen : false,
119
+ onOpenChange: hasContent ? setIsOpen : noop,
120
+ disabled: !hasContent,
121
+ children: [
122
+ /* @__PURE__ */ jsxs(CollapsibleTrigger, {
123
+ className: "lb-collapsible-trigger lb-ai-tool-header",
124
+ children: [
125
+ icon ? /* @__PURE__ */ jsx("div", {
126
+ className: "lb-ai-tool-header-icon-container",
127
+ children: icon
128
+ }) : null,
129
+ /* @__PURE__ */ jsx("span", {
130
+ className: "lb-ai-tool-header-title",
131
+ children: resolvedTitle
132
+ }),
133
+ hasContent ? /* @__PURE__ */ jsx("span", {
134
+ className: "lb-collapsible-chevron lb-icon-container",
135
+ children: /* @__PURE__ */ jsx(ChevronRightIcon, {})
136
+ }) : null,
137
+ /* @__PURE__ */ jsx("div", {
138
+ className: "lb-ai-tool-header-status",
139
+ children: status === "executed" ? /* @__PURE__ */ jsx(CheckCircleFillIcon, {}) : execute !== void 0 ? /* @__PURE__ */ jsx(SpinnerIcon, {}) : null
140
+ })
141
+ ]
142
+ }),
143
+ children ? /* @__PURE__ */ jsx(CollapsibleContent, {
144
+ className: "lb-collapsible-content lb-ai-tool-content-container",
145
+ children: /* @__PURE__ */ jsx("div", {
146
+ className: "lb-ai-tool-content",
147
+ children
148
+ })
149
+ }) : null
150
+ ]
151
+ });
152
+ }
153
+ ),
154
+ {
155
+ Icon: AiToolIcon,
156
+ Inspector: AiToolInspector,
157
+ Confirmation: AiToolConfirmation
158
+ }
159
+ );
160
+
161
+ export { AiTool };
162
+ //# sourceMappingURL=AiTool.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AiTool.js","sources":["../../src/components/AiTool.tsx"],"sourcesContent":["import {\n type AiToolExecuteCallback,\n type AiToolTypePack,\n type JsonObject,\n kInternal,\n type ToolResultData,\n} from \"@liveblocks/core\";\nimport type { ComponentProps, ReactNode } from \"react\";\nimport { Children, forwardRef, useCallback, useMemo, useState } from \"react\";\n\nimport { Button } from \"../_private\";\nimport { CheckCircleFillIcon, ChevronRightIcon, SpinnerIcon } from \"../icons\";\nimport { useAiToolInvocationContext } from \"../primitives/AiMessage/contexts\";\nimport * as Collapsible from \"../primitives/Collapsible\";\nimport { classNames } from \"../utils/class-names\";\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\nexport type AiToolIconProps = ComponentProps<\"div\">;\n\nexport type AiToolInspectorProps = ComponentProps<\"div\">;\n\n// TODO: AiToolConfirmationProps might need a generic since we're outside of the\n// tool definition so things like inferred args and result types are not\n// available here for `confirm` and `cancel`\n\n/**\n * @private This API will change, and is not considered stable. DO NOT RELY on it.\n *\n * This component can be used to build human-in-the-loop interfaces.\n */\nexport interface AiToolConfirmationProps<\n A extends JsonObject,\n R extends ToolResultData,\n> extends ComponentProps<\"div\"> {\n args?: A;\n confirm: AiToolExecuteCallback<A, R>;\n cancel: AiToolExecuteCallback<A, R>;\n variant?: \"default\" | \"destructive\";\n\n // TODO: Use existing overrides API to allow customizing the \"Confirm\" and \"Cancel\" labels\n // overrides?: Partial<GlobalOverrides & AiToolConfirmationOverrides>;\n}\n\nfunction AiToolIcon({ className, ...props }: AiToolIconProps) {\n return (\n <div className={classNames(\"lb-ai-tool-icon\", className)} {...props} />\n );\n}\n\nfunction AiToolInspector({ className, ...props }: AiToolInspectorProps) {\n const { args, partialArgs, result } = useAiToolInvocationContext();\n\n return (\n <div className={classNames(\"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 ToolResultData = TPack[\"R\"],\n>({\n children,\n variant = \"default\",\n confirm,\n cancel,\n className,\n ...props\n}: AiToolConfirmationProps<A, R>) {\n const { status, args, respond, toolName, toolCallId } =\n useAiToolInvocationContext();\n\n const enabled = status === \"executing\";\n\n const context = useMemo(\n () => ({ toolName, toolCallId }),\n [toolName, toolCallId]\n );\n\n const onConfirmClick = useCallback(async () => {\n if (enabled) {\n respond(await confirm(args as A, context));\n }\n }, [enabled, args, confirm, respond, context]);\n\n const onCancelClick = useCallback(async () => {\n if (enabled) {\n respond(await cancel(args as A, context));\n }\n }, [enabled, args, cancel, respond, context]);\n\n if (status === \"executed\") {\n return null;\n }\n\n return (\n <div\n className={classNames(\"lb-ai-tool-confirmation\", className)}\n {...props}\n >\n {children ? (\n <div className=\"lb-ai-tool-confirmation-content\">{children}</div>\n ) : null}\n <div className=\"lb-ai-tool-confirmation-footer\">\n <div className=\"lb-ai-tool-confirmation-actions\">\n <Button\n disabled={!enabled}\n onClick={onConfirmClick}\n variant={variant === \"destructive\" ? \"destructive\" : \"primary\"}\n >\n Confirm\n </Button>\n <Button\n disabled={!enabled}\n onClick={onCancelClick}\n variant=\"secondary\"\n >\n Cancel\n </Button>\n </div>\n </div>\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\nconst noop = () => {};\n\nexport const AiTool = Object.assign(\n forwardRef<HTMLDivElement, AiToolProps>(\n ({ children, title, icon, className, ...props }, forwardedRef) => {\n const {\n status,\n toolName,\n [kInternal]: { execute },\n } = useAiToolInvocationContext();\n const [isOpen, setIsOpen] = useState(true);\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 const hasContent = Children.count(children) > 0;\n const resolvedTitle = useMemo(() => {\n return title ?? prettifyString(toolName);\n }, [title, toolName]);\n\n return (\n <Collapsible.Root\n ref={forwardedRef}\n className={classNames(\"lb-collapsible lb-ai-tool\", className)}\n {...props}\n open={hasContent ? isOpen : false}\n onOpenChange={hasContent ? setIsOpen : noop}\n disabled={!hasContent}\n >\n <Collapsible.Trigger className=\"lb-collapsible-trigger lb-ai-tool-header\">\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 {hasContent ? (\n <span className=\"lb-collapsible-chevron lb-icon-container\">\n <ChevronRightIcon />\n </span>\n ) : null}\n <div className=\"lb-ai-tool-header-status\">\n {status === \"executed\" ? (\n <CheckCircleFillIcon />\n ) : execute !== undefined ? (\n // Only show a spinner if the tool has an `execute` method.\n <SpinnerIcon />\n ) : null}\n </div>\n </Collapsible.Trigger>\n\n {children ? (\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 Icon: AiToolIcon,\n Inspector: AiToolInspector,\n Confirmation: AiToolConfirmation,\n }\n);\n"],"names":["Collapsible.Root","Collapsible.Trigger","Collapsible.Content"],"mappings":"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"}
@@ -292,10 +292,12 @@ function AutoMarkReadThreadIdHandler({
292
292
  }) {
293
293
  const markThreadAsRead = _private.useMarkRoomThreadAsRead(roomId);
294
294
  const isWindowFocused = useWindowFocus.useWindowFocus();
295
- useVisible.useVisibleCallback(
295
+ useVisible.useIntersectionCallback(
296
296
  commentRef,
297
- () => {
298
- markThreadAsRead(threadId);
297
+ (isIntersecting) => {
298
+ if (isIntersecting) {
299
+ markThreadAsRead(threadId);
300
+ }
299
301
  },
300
302
  {
301
303
  enabled: isWindowFocused