@liveblocks/react-ui 2.25.0-aiprivatebeta1 → 2.25.0-aiprivatebeta10

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 (273) hide show
  1. package/dist/_private/index.cjs +18 -10
  2. package/dist/_private/index.cjs.map +1 -1
  3. package/dist/_private/index.d.cts +204 -172
  4. package/dist/_private/index.d.ts +204 -172
  5. package/dist/_private/index.js +12 -5
  6. package/dist/_private/index.js.map +1 -1
  7. package/dist/components/AiChat.cjs +185 -0
  8. package/dist/components/AiChat.cjs.map +1 -0
  9. package/dist/components/AiChat.js +183 -0
  10. package/dist/components/AiChat.js.map +1 -0
  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 +7 -5
  16. package/dist/components/Comment.cjs.map +1 -1
  17. package/dist/components/Comment.js +7 -5
  18. package/dist/components/Comment.js.map +1 -1
  19. package/dist/components/Composer.cjs +1 -2
  20. package/dist/components/Composer.cjs.map +1 -1
  21. package/dist/components/Composer.js +1 -2
  22. package/dist/components/Composer.js.map +1 -1
  23. package/dist/components/InboxNotificationList.cjs +11 -3
  24. package/dist/components/InboxNotificationList.cjs.map +1 -1
  25. package/dist/components/InboxNotificationList.js +12 -4
  26. package/dist/components/InboxNotificationList.js.map +1 -1
  27. package/dist/components/Thread.cjs +3 -3
  28. package/dist/components/Thread.cjs.map +1 -1
  29. package/dist/components/Thread.js +3 -3
  30. package/dist/components/Thread.js.map +1 -1
  31. package/dist/components/internal/AiChatAssistantMessage.cjs +74 -291
  32. package/dist/components/internal/AiChatAssistantMessage.cjs.map +1 -1
  33. package/dist/components/internal/AiChatAssistantMessage.js +76 -293
  34. package/dist/components/internal/AiChatAssistantMessage.js.map +1 -1
  35. package/dist/components/internal/AiChatComposer.cjs +58 -282
  36. package/dist/components/internal/AiChatComposer.cjs.map +1 -1
  37. package/dist/components/internal/AiChatComposer.js +63 -283
  38. package/dist/components/internal/AiChatComposer.js.map +1 -1
  39. package/dist/components/internal/AiChatUserMessage.cjs +26 -169
  40. package/dist/components/internal/AiChatUserMessage.cjs.map +1 -1
  41. package/dist/components/internal/AiChatUserMessage.js +28 -171
  42. package/dist/components/internal/AiChatUserMessage.js.map +1 -1
  43. package/dist/components/internal/Button.cjs.map +1 -1
  44. package/dist/components/internal/Button.js.map +1 -1
  45. package/dist/components/internal/CodeBlock.cjs +72 -0
  46. package/dist/components/internal/CodeBlock.cjs.map +1 -0
  47. package/dist/components/internal/CodeBlock.js +70 -0
  48. package/dist/components/internal/CodeBlock.js.map +1 -0
  49. package/dist/components/internal/Emoji.cjs +12 -4
  50. package/dist/components/internal/Emoji.cjs.map +1 -1
  51. package/dist/components/internal/Emoji.js +12 -4
  52. package/dist/components/internal/Emoji.js.map +1 -1
  53. package/dist/components/internal/Prose.cjs +37 -0
  54. package/dist/components/internal/Prose.cjs.map +1 -0
  55. package/dist/components/internal/Prose.js +35 -0
  56. package/dist/components/internal/Prose.js.map +1 -0
  57. package/dist/constants.cjs +2 -0
  58. package/dist/constants.cjs.map +1 -1
  59. package/dist/constants.js +2 -1
  60. package/dist/constants.js.map +1 -1
  61. package/dist/icon.cjs +6 -0
  62. package/dist/icon.cjs.map +1 -1
  63. package/dist/icon.js +3 -0
  64. package/dist/icon.js.map +1 -1
  65. package/dist/icons/{Resolve.cjs → CheckCircle.cjs} +3 -3
  66. package/dist/icons/CheckCircle.cjs.map +1 -0
  67. package/dist/icons/{Resolve.js → CheckCircle.js} +3 -3
  68. package/dist/icons/CheckCircle.js.map +1 -0
  69. package/dist/icons/{Resolved.cjs → CheckCircleFill.cjs} +3 -3
  70. package/dist/icons/CheckCircleFill.cjs.map +1 -0
  71. package/dist/icons/{Resolved.js → CheckCircleFill.js} +3 -3
  72. package/dist/icons/CheckCircleFill.js.map +1 -0
  73. package/dist/icons/Copy.cjs +8 -9
  74. package/dist/icons/Copy.cjs.map +1 -1
  75. package/dist/icons/Copy.js +8 -9
  76. package/dist/icons/Copy.js.map +1 -1
  77. package/dist/icons/Retry.cjs +21 -0
  78. package/dist/icons/Retry.cjs.map +1 -0
  79. package/dist/icons/Retry.js +19 -0
  80. package/dist/icons/Retry.js.map +1 -0
  81. package/dist/icons/index.cjs +8 -4
  82. package/dist/icons/index.cjs.map +1 -1
  83. package/dist/icons/index.js +4 -2
  84. package/dist/icons/index.js.map +1 -1
  85. package/dist/index.cjs +3 -1
  86. package/dist/index.cjs.map +1 -1
  87. package/dist/index.d.cts +102 -43
  88. package/dist/index.d.ts +102 -43
  89. package/dist/index.js +2 -1
  90. package/dist/index.js.map +1 -1
  91. package/dist/overrides.cjs +6 -12
  92. package/dist/overrides.cjs.map +1 -1
  93. package/dist/overrides.js +6 -12
  94. package/dist/overrides.js.map +1 -1
  95. package/dist/primitives/AiChatComposer/index.cjs +202 -0
  96. package/dist/primitives/AiChatComposer/index.cjs.map +1 -0
  97. package/dist/primitives/AiChatComposer/index.js +195 -0
  98. package/dist/primitives/AiChatComposer/index.js.map +1 -0
  99. package/dist/primitives/AiMessage/contexts.cjs +18 -0
  100. package/dist/primitives/AiMessage/contexts.cjs.map +1 -0
  101. package/dist/primitives/AiMessage/contexts.js +15 -0
  102. package/dist/primitives/AiMessage/contexts.js.map +1 -0
  103. package/dist/primitives/AiMessage/index.cjs +134 -0
  104. package/dist/primitives/AiMessage/index.cjs.map +1 -0
  105. package/dist/primitives/AiMessage/index.js +132 -0
  106. package/dist/primitives/AiMessage/index.js.map +1 -0
  107. package/dist/primitives/Collapsible/index.cjs +127 -0
  108. package/dist/primitives/Collapsible/index.cjs.map +1 -0
  109. package/dist/primitives/Collapsible/index.js +123 -0
  110. package/dist/primitives/Collapsible/index.js.map +1 -0
  111. package/dist/primitives/Comment/index.cjs +2 -2
  112. package/dist/primitives/Comment/index.cjs.map +1 -1
  113. package/dist/primitives/Comment/index.js +1 -1
  114. package/dist/primitives/Comment/index.js.map +1 -1
  115. package/dist/primitives/Composer/index.cjs +19 -14
  116. package/dist/primitives/Composer/index.cjs.map +1 -1
  117. package/dist/primitives/Composer/index.js +18 -13
  118. package/dist/primitives/Composer/index.js.map +1 -1
  119. package/dist/{slate → primitives/Composer/slate}/plugins/auto-formatting.cjs +3 -3
  120. package/dist/primitives/Composer/slate/plugins/auto-formatting.cjs.map +1 -0
  121. package/dist/{slate → primitives/Composer/slate}/plugins/auto-formatting.js +3 -3
  122. package/dist/primitives/Composer/slate/plugins/auto-formatting.js.map +1 -0
  123. package/dist/{slate → primitives/Composer/slate}/plugins/auto-links.cjs +7 -2
  124. package/dist/primitives/Composer/slate/plugins/auto-links.cjs.map +1 -0
  125. package/dist/{slate → primitives/Composer/slate}/plugins/auto-links.js +8 -3
  126. package/dist/primitives/Composer/slate/plugins/auto-links.js.map +1 -0
  127. package/dist/{slate → primitives/Composer/slate}/plugins/custom-links.cjs +8 -3
  128. package/dist/primitives/Composer/slate/plugins/custom-links.cjs.map +1 -0
  129. package/dist/{slate → primitives/Composer/slate}/plugins/custom-links.js +9 -4
  130. package/dist/primitives/Composer/slate/plugins/custom-links.js.map +1 -0
  131. package/dist/{slate → primitives/Composer/slate}/plugins/mentions.cjs +9 -10
  132. package/dist/primitives/Composer/slate/plugins/mentions.cjs.map +1 -0
  133. package/dist/{slate → primitives/Composer/slate}/plugins/mentions.js +6 -6
  134. package/dist/primitives/Composer/slate/plugins/mentions.js.map +1 -0
  135. package/dist/{slate → primitives/Composer/slate}/plugins/paste.cjs +1 -1
  136. package/dist/primitives/Composer/slate/plugins/paste.cjs.map +1 -0
  137. package/dist/{slate → primitives/Composer/slate}/plugins/paste.js +1 -1
  138. package/dist/primitives/Composer/slate/plugins/paste.js.map +1 -0
  139. package/dist/primitives/Composer/utils.cjs +4 -4
  140. package/dist/primitives/Composer/utils.cjs.map +1 -1
  141. package/dist/primitives/Composer/utils.js +4 -4
  142. package/dist/primitives/Composer/utils.js.map +1 -1
  143. package/dist/primitives/{internal/Markdown.cjs → Markdown.cjs} +150 -83
  144. package/dist/primitives/Markdown.cjs.map +1 -0
  145. package/dist/primitives/{internal/Markdown.js → Markdown.js} +151 -83
  146. package/dist/primitives/Markdown.js.map +1 -0
  147. package/dist/primitives/index.cjs +4 -9
  148. package/dist/primitives/index.cjs.map +1 -1
  149. package/dist/primitives/index.d.cts +4 -110
  150. package/dist/primitives/index.d.ts +4 -110
  151. package/dist/primitives/index.js +0 -1
  152. package/dist/primitives/index.js.map +1 -1
  153. package/dist/primitives/slate/plugins/empty-clear-formatting.cjs.map +1 -0
  154. package/dist/primitives/slate/plugins/empty-clear-formatting.js.map +1 -0
  155. package/dist/{slate → primitives/slate}/plugins/normalize.cjs +0 -5
  156. package/dist/primitives/slate/plugins/normalize.cjs.map +1 -0
  157. package/dist/{slate → primitives/slate}/plugins/normalize.js +0 -5
  158. package/dist/primitives/slate/plugins/normalize.js.map +1 -0
  159. package/dist/primitives/slate/utils/get-character.cjs.map +1 -0
  160. package/dist/primitives/slate/utils/get-character.js.map +1 -0
  161. package/dist/primitives/slate/utils/get-dom-range.cjs.map +1 -0
  162. package/dist/primitives/slate/utils/get-dom-range.js.map +1 -0
  163. package/dist/primitives/slate/utils/get-match-range.cjs.map +1 -0
  164. package/dist/primitives/slate/utils/get-match-range.js.map +1 -0
  165. package/dist/primitives/slate/utils/is-empty-string.cjs.map +1 -0
  166. package/dist/primitives/slate/utils/is-empty-string.js.map +1 -0
  167. package/dist/primitives/slate/utils/is-empty.cjs.map +1 -0
  168. package/dist/primitives/slate/utils/is-empty.js.map +1 -0
  169. package/dist/primitives/slate/utils/is-text.cjs.map +1 -0
  170. package/dist/primitives/slate/utils/is-text.js.map +1 -0
  171. package/dist/primitives/slate/utils/is-whitespace-character.cjs.map +1 -0
  172. package/dist/primitives/slate/utils/is-whitespace-character.js.map +1 -0
  173. package/dist/{slate → primitives/slate}/utils/marks.cjs +9 -9
  174. package/dist/primitives/slate/utils/marks.cjs.map +1 -0
  175. package/dist/{slate → primitives/slate}/utils/marks.js +9 -9
  176. package/dist/primitives/slate/utils/marks.js.map +1 -0
  177. package/dist/primitives/slate/utils/selection-contains-inlines.cjs.map +1 -0
  178. package/dist/primitives/slate/utils/selection-contains-inlines.js.map +1 -0
  179. package/dist/utils/ErrorBoundary.cjs +48 -0
  180. package/dist/utils/ErrorBoundary.cjs.map +1 -0
  181. package/dist/utils/ErrorBoundary.js +45 -0
  182. package/dist/utils/ErrorBoundary.js.map +1 -0
  183. package/dist/utils/use-visible.cjs +63 -45
  184. package/dist/utils/use-visible.cjs.map +1 -1
  185. package/dist/utils/use-visible.js +64 -46
  186. package/dist/utils/use-visible.js.map +1 -1
  187. package/dist/version.cjs +1 -1
  188. package/dist/version.cjs.map +1 -1
  189. package/dist/version.js +1 -1
  190. package/dist/version.js.map +1 -1
  191. package/package.json +18 -6
  192. package/src/styles/constants.css +1 -1
  193. package/src/styles/dark/index.css +7 -3
  194. package/src/styles/index.css +640 -307
  195. package/src/styles/utils.css +8 -3
  196. package/styles/dark/attributes.css +1 -1
  197. package/styles/dark/attributes.css.map +1 -1
  198. package/styles/dark/media-query.css +1 -1
  199. package/styles/dark/media-query.css.map +1 -1
  200. package/styles.css +1 -1
  201. package/styles.css.map +1 -1
  202. package/dist/components/AiChat/AiChat.cjs +0 -211
  203. package/dist/components/AiChat/AiChat.cjs.map +0 -1
  204. package/dist/components/AiChat/AiChat.js +0 -209
  205. package/dist/components/AiChat/AiChat.js.map +0 -1
  206. package/dist/icons/Resolve.cjs.map +0 -1
  207. package/dist/icons/Resolve.js.map +0 -1
  208. package/dist/icons/Resolved.cjs.map +0 -1
  209. package/dist/icons/Resolved.js.map +0 -1
  210. package/dist/primitives/Chat/Composer/index.cjs +0 -323
  211. package/dist/primitives/Chat/Composer/index.cjs.map +0 -1
  212. package/dist/primitives/Chat/Composer/index.js +0 -315
  213. package/dist/primitives/Chat/Composer/index.js.map +0 -1
  214. package/dist/primitives/internal/Collapsible.cjs +0 -99
  215. package/dist/primitives/internal/Collapsible.cjs.map +0 -1
  216. package/dist/primitives/internal/Collapsible.js +0 -95
  217. package/dist/primitives/internal/Collapsible.js.map +0 -1
  218. package/dist/primitives/internal/Emoji.cjs +0 -32
  219. package/dist/primitives/internal/Emoji.cjs.map +0 -1
  220. package/dist/primitives/internal/Emoji.js +0 -30
  221. package/dist/primitives/internal/Emoji.js.map +0 -1
  222. package/dist/primitives/internal/Markdown.cjs.map +0 -1
  223. package/dist/primitives/internal/Markdown.js.map +0 -1
  224. package/dist/slate/plugins/auto-formatting.cjs.map +0 -1
  225. package/dist/slate/plugins/auto-formatting.js.map +0 -1
  226. package/dist/slate/plugins/auto-links.cjs.map +0 -1
  227. package/dist/slate/plugins/auto-links.js.map +0 -1
  228. package/dist/slate/plugins/custom-links.cjs.map +0 -1
  229. package/dist/slate/plugins/custom-links.js.map +0 -1
  230. package/dist/slate/plugins/empty-clear-formatting.cjs.map +0 -1
  231. package/dist/slate/plugins/empty-clear-formatting.js.map +0 -1
  232. package/dist/slate/plugins/mentions.cjs.map +0 -1
  233. package/dist/slate/plugins/mentions.js.map +0 -1
  234. package/dist/slate/plugins/normalize.cjs.map +0 -1
  235. package/dist/slate/plugins/normalize.js.map +0 -1
  236. package/dist/slate/plugins/paste.cjs.map +0 -1
  237. package/dist/slate/plugins/paste.js.map +0 -1
  238. package/dist/slate/utils/get-character.cjs.map +0 -1
  239. package/dist/slate/utils/get-character.js.map +0 -1
  240. package/dist/slate/utils/get-dom-range.cjs.map +0 -1
  241. package/dist/slate/utils/get-dom-range.js.map +0 -1
  242. package/dist/slate/utils/get-match-range.cjs.map +0 -1
  243. package/dist/slate/utils/get-match-range.js.map +0 -1
  244. package/dist/slate/utils/is-empty-string.cjs.map +0 -1
  245. package/dist/slate/utils/is-empty-string.js.map +0 -1
  246. package/dist/slate/utils/is-empty.cjs.map +0 -1
  247. package/dist/slate/utils/is-empty.js.map +0 -1
  248. package/dist/slate/utils/is-text.cjs.map +0 -1
  249. package/dist/slate/utils/is-text.js.map +0 -1
  250. package/dist/slate/utils/is-whitespace-character.cjs.map +0 -1
  251. package/dist/slate/utils/is-whitespace-character.js.map +0 -1
  252. package/dist/slate/utils/marks.cjs.map +0 -1
  253. package/dist/slate/utils/marks.js.map +0 -1
  254. package/dist/slate/utils/selection-contains-inlines.cjs.map +0 -1
  255. package/dist/slate/utils/selection-contains-inlines.js.map +0 -1
  256. /package/dist/{slate → primitives/slate}/plugins/empty-clear-formatting.cjs +0 -0
  257. /package/dist/{slate → primitives/slate}/plugins/empty-clear-formatting.js +0 -0
  258. /package/dist/{slate → primitives/slate}/utils/get-character.cjs +0 -0
  259. /package/dist/{slate → primitives/slate}/utils/get-character.js +0 -0
  260. /package/dist/{slate → primitives/slate}/utils/get-dom-range.cjs +0 -0
  261. /package/dist/{slate → primitives/slate}/utils/get-dom-range.js +0 -0
  262. /package/dist/{slate → primitives/slate}/utils/get-match-range.cjs +0 -0
  263. /package/dist/{slate → primitives/slate}/utils/get-match-range.js +0 -0
  264. /package/dist/{slate → primitives/slate}/utils/is-empty-string.cjs +0 -0
  265. /package/dist/{slate → primitives/slate}/utils/is-empty-string.js +0 -0
  266. /package/dist/{slate → primitives/slate}/utils/is-empty.cjs +0 -0
  267. /package/dist/{slate → primitives/slate}/utils/is-empty.js +0 -0
  268. /package/dist/{slate → primitives/slate}/utils/is-text.cjs +0 -0
  269. /package/dist/{slate → primitives/slate}/utils/is-text.js +0 -0
  270. /package/dist/{slate → primitives/slate}/utils/is-whitespace-character.cjs +0 -0
  271. /package/dist/{slate → primitives/slate}/utils/is-whitespace-character.js +0 -0
  272. /package/dist/{slate → primitives/slate}/utils/selection-contains-inlines.cjs +0 -0
  273. /package/dist/{slate → primitives/slate}/utils/selection-contains-inlines.js +0 -0
@@ -0,0 +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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"}
@@ -0,0 +1,183 @@
1
+ import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
2
+ import { kInternal } from '@liveblocks/core';
3
+ import { useAiChatMessages, useClient, RegisterAiKnowledge } from '@liveblocks/react';
4
+ import { useLayoutEffect } from '@liveblocks/react/_private';
5
+ import { forwardRef, useRef, useState, useImperativeHandle, useEffect } from 'react';
6
+ import { ArrowDownIcon } from '../icons/ArrowDown.js';
7
+ import { SpinnerIcon } from '../icons/Spinner.js';
8
+ import { useOverrides } from '../overrides.js';
9
+ import { classNames } from '../utils/class-names.js';
10
+ import { useVisible } from '../utils/use-visible.js';
11
+ import { AiChatAssistantMessage } from './internal/AiChatAssistantMessage.js';
12
+ import { AiChatComposer } from './internal/AiChatComposer.js';
13
+ import { AiChatUserMessage } from './internal/AiChatUserMessage.js';
14
+
15
+ const MIN_DISTANCE_BOTTOM_SCROLL_INDICATOR = 50;
16
+ const defaultComponents = {
17
+ Empty: () => null,
18
+ Loading: () => /* @__PURE__ */ jsx("div", {
19
+ className: "lb-loading lb-ai-chat-loading",
20
+ children: /* @__PURE__ */ jsx(SpinnerIcon, {})
21
+ })
22
+ };
23
+ const AiChat = forwardRef(
24
+ ({
25
+ chatId,
26
+ copilotId,
27
+ autoFocus,
28
+ overrides,
29
+ knowledge,
30
+ tools = {},
31
+ layout = "inset",
32
+ components,
33
+ className,
34
+ ...props
35
+ }, forwardedRef) => {
36
+ const { messages, isLoading, error } = useAiChatMessages(chatId);
37
+ const $ = useOverrides(overrides);
38
+ const Empty = components?.Empty ?? defaultComponents.Empty;
39
+ const Loading = components?.Loading ?? defaultComponents.Loading;
40
+ const containerRef = useRef(null);
41
+ const containerBottomRef = useRef(null);
42
+ const isScrollAtBottom = useVisible(containerBottomRef, {
43
+ enabled: !isLoading && !error,
44
+ root: containerRef,
45
+ rootMargin: MIN_DISTANCE_BOTTOM_SCROLL_INDICATOR
46
+ });
47
+ const isScrollIndicatorVisible = isLoading || error ? false : !isScrollAtBottom;
48
+ const client = useClient();
49
+ const ai = client[kInternal].ai;
50
+ const [lastSentMessageId, setLastSentMessageId] = useState(null);
51
+ useImperativeHandle(
52
+ forwardedRef,
53
+ () => containerRef.current,
54
+ []
55
+ );
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
+ const scrollToBottomCallbackRef = useRef(void 0);
67
+ if (scrollToBottomCallbackRef.current === void 0) {
68
+ scrollToBottomCallbackRef.current = function(behavior) {
69
+ const container = containerRef.current;
70
+ if (container === null)
71
+ return;
72
+ container.scrollTo({
73
+ top: container.scrollHeight,
74
+ behavior
75
+ });
76
+ };
77
+ }
78
+ const scrollToBottom = scrollToBottomCallbackRef.current;
79
+ return /* @__PURE__ */ jsxs("div", {
80
+ ref: containerRef,
81
+ ...props,
82
+ className: classNames(
83
+ "lb-root lb-ai-chat",
84
+ layout === "compact" ? "lb-ai-chat:layout-compact" : "lb-ai-chat:layout-inset",
85
+ className
86
+ ),
87
+ children: [
88
+ knowledge ? knowledge.map((source, index) => /* @__PURE__ */ jsx(RegisterAiKnowledge, {
89
+ description: source.description,
90
+ value: source.value
91
+ }, index)) : null,
92
+ /* @__PURE__ */ jsx("div", {
93
+ className: "lb-ai-chat-content",
94
+ children: isLoading ? /* @__PURE__ */ jsx(Loading, {}) : error !== void 0 ? /* @__PURE__ */ jsx("div", {
95
+ className: "lb-error lb-ai-chat-error",
96
+ children: $.AI_CHAT_MESSAGES_ERROR(error)
97
+ }) : messages.length === 0 ? /* @__PURE__ */ jsx(Empty, {
98
+ chatId,
99
+ copilotId
100
+ }) : /* @__PURE__ */ jsxs(Fragment, {
101
+ children: [
102
+ /* @__PURE__ */ jsx(AutoScrollHandler, {
103
+ lastSentMessageId,
104
+ scrollToBottom
105
+ }),
106
+ /* @__PURE__ */ jsx("div", {
107
+ className: "lb-ai-chat-messages",
108
+ children: messages.map((message) => {
109
+ if (message.role === "user") {
110
+ return /* @__PURE__ */ jsx(AiChatUserMessage, {
111
+ message,
112
+ overrides
113
+ }, message.id);
114
+ } else if (message.role === "assistant") {
115
+ return /* @__PURE__ */ jsx(AiChatAssistantMessage, {
116
+ message,
117
+ overrides,
118
+ components
119
+ }, message.id);
120
+ } else {
121
+ return null;
122
+ }
123
+ })
124
+ })
125
+ ]
126
+ })
127
+ }),
128
+ /* @__PURE__ */ jsxs("div", {
129
+ className: "lb-ai-chat-footer",
130
+ children: [
131
+ /* @__PURE__ */ jsx("div", {
132
+ className: "lb-ai-chat-footer-actions",
133
+ children: /* @__PURE__ */ jsx("div", {
134
+ className: "lb-root lb-elevation lb-elevation-moderate lb-ai-chat-scroll-indicator",
135
+ "data-visible": isScrollIndicatorVisible ? "" : void 0,
136
+ children: /* @__PURE__ */ jsx("button", {
137
+ className: "lb-ai-chat-scroll-indicator-button",
138
+ tabIndex: isScrollIndicatorVisible ? 0 : -1,
139
+ "aria-hidden": !isScrollIndicatorVisible,
140
+ disabled: !isScrollIndicatorVisible,
141
+ onClick: () => scrollToBottom("smooth"),
142
+ children: /* @__PURE__ */ jsx("span", {
143
+ className: "lb-icon-container",
144
+ children: /* @__PURE__ */ jsx(ArrowDownIcon, {})
145
+ })
146
+ })
147
+ })
148
+ }),
149
+ /* @__PURE__ */ jsx(AiChatComposer, {
150
+ chatId,
151
+ copilotId,
152
+ overrides: $,
153
+ autoFocus,
154
+ onUserMessageCreate: ({ id }) => setLastSentMessageId(id),
155
+ className: layout === "inset" ? "lb-elevation lb-elevation-moderate" : void 0
156
+ }, chatId)
157
+ ]
158
+ }),
159
+ /* @__PURE__ */ jsx("div", {
160
+ ref: containerBottomRef,
161
+ style: { position: "sticky", height: 0 },
162
+ "aria-hidden": true,
163
+ "data-scroll-at-bottom": isScrollAtBottom ? "" : void 0
164
+ })
165
+ ]
166
+ });
167
+ }
168
+ );
169
+ function AutoScrollHandler({
170
+ lastSentMessageId,
171
+ scrollToBottom
172
+ }) {
173
+ useLayoutEffect(() => {
174
+ scrollToBottom("instant");
175
+ }, [scrollToBottom]);
176
+ useEffect(() => {
177
+ scrollToBottom("smooth");
178
+ }, [lastSentMessageId, scrollToBottom]);
179
+ return null;
180
+ }
181
+
182
+ export { AiChat };
183
+ //# sourceMappingURL=AiChat.js.map
@@ -0,0 +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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"}
@@ -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 NoInfr,\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 types?: NoInfr<AiToolTypePack<A, R>>;\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":"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"}
@@ -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 NoInfr,\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 types?: NoInfr<AiToolTypePack<A, R>>;\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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"}