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

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 (70) hide show
  1. package/dist/_private/index.cjs +10 -4
  2. package/dist/_private/index.cjs.map +1 -1
  3. package/dist/_private/index.d.cts +76 -10
  4. package/dist/_private/index.d.ts +76 -10
  5. package/dist/_private/index.js +4 -2
  6. package/dist/_private/index.js.map +1 -1
  7. package/dist/components/AiChat.cjs +24 -27
  8. package/dist/components/AiChat.cjs.map +1 -1
  9. package/dist/components/AiChat.js +24 -27
  10. package/dist/components/AiChat.js.map +1 -1
  11. package/dist/components/AiToolDebugger.cjs +74 -0
  12. package/dist/components/AiToolDebugger.cjs.map +1 -0
  13. package/dist/components/AiToolDebugger.js +72 -0
  14. package/dist/components/AiToolDebugger.js.map +1 -0
  15. package/dist/components/Thread.cjs +3 -3
  16. package/dist/components/Thread.cjs.map +1 -1
  17. package/dist/components/Thread.js +3 -3
  18. package/dist/components/Thread.js.map +1 -1
  19. package/dist/components/internal/AiChatAssistantMessage.cjs +127 -147
  20. package/dist/components/internal/AiChatAssistantMessage.cjs.map +1 -1
  21. package/dist/components/internal/AiChatAssistantMessage.js +129 -149
  22. package/dist/components/internal/AiChatAssistantMessage.js.map +1 -1
  23. package/dist/components/internal/AiChatComposer.cjs +28 -16
  24. package/dist/components/internal/AiChatComposer.cjs.map +1 -1
  25. package/dist/components/internal/AiChatComposer.js +28 -16
  26. package/dist/components/internal/AiChatComposer.js.map +1 -1
  27. package/dist/icon.cjs +2 -0
  28. package/dist/icon.cjs.map +1 -1
  29. package/dist/icon.js +1 -0
  30. package/dist/icon.js.map +1 -1
  31. package/dist/icons/{Resolve.cjs → CheckCircle.cjs} +3 -3
  32. package/dist/icons/CheckCircle.cjs.map +1 -0
  33. package/dist/icons/{Resolve.js → CheckCircle.js} +3 -3
  34. package/dist/icons/CheckCircle.js.map +1 -0
  35. package/dist/icons/{Resolved.cjs → CheckCircleFill.cjs} +3 -3
  36. package/dist/icons/CheckCircleFill.cjs.map +1 -0
  37. package/dist/icons/{Resolved.js → CheckCircleFill.js} +3 -3
  38. package/dist/icons/CheckCircleFill.js.map +1 -0
  39. package/dist/icons/index.cjs +4 -4
  40. package/dist/icons/index.js +2 -2
  41. package/dist/index.cjs +2 -0
  42. package/dist/index.cjs.map +1 -1
  43. package/dist/index.d.cts +27 -14
  44. package/dist/index.d.ts +27 -14
  45. package/dist/index.js +1 -0
  46. package/dist/index.js.map +1 -1
  47. package/dist/overrides.cjs +0 -4
  48. package/dist/overrides.cjs.map +1 -1
  49. package/dist/overrides.js +0 -4
  50. package/dist/overrides.js.map +1 -1
  51. package/dist/primitives/AiChatComposer/index.cjs +1 -2
  52. package/dist/primitives/AiChatComposer/index.cjs.map +1 -1
  53. package/dist/primitives/AiChatComposer/index.js +1 -2
  54. package/dist/primitives/AiChatComposer/index.js.map +1 -1
  55. package/dist/primitives/index.d.cts +0 -4
  56. package/dist/primitives/index.d.ts +0 -4
  57. package/dist/primitives/internal/Markdown.cjs +56 -25
  58. package/dist/primitives/internal/Markdown.cjs.map +1 -1
  59. package/dist/primitives/internal/Markdown.js +56 -25
  60. package/dist/primitives/internal/Markdown.js.map +1 -1
  61. package/dist/version.cjs +1 -1
  62. package/dist/version.js +1 -1
  63. package/package.json +5 -5
  64. package/src/styles/index.css +105 -61
  65. package/styles.css +1 -1
  66. package/styles.css.map +1 -1
  67. package/dist/icons/Resolve.cjs.map +0 -1
  68. package/dist/icons/Resolve.js.map +0 -1
  69. package/dist/icons/Resolved.cjs.map +0 -1
  70. package/dist/icons/Resolved.js.map +0 -1
@@ -1,6 +1,6 @@
1
1
  import { jsxs, jsx } from 'react/jsx-runtime';
2
2
  import { kInternal } from '@liveblocks/core';
3
- import { useAiChatMessages, useClient } from '@liveblocks/react';
3
+ import { useAiChatMessages, useClient, RegisterAiKnowledge } from '@liveblocks/react';
4
4
  import { useLayoutEffect } from '@liveblocks/react/_private';
5
5
  import { forwardRef, useRef, useState, useImperativeHandle, useEffect } from 'react';
6
6
  import { ArrowDownIcon } from '../icons/ArrowDown.js';
@@ -18,8 +18,9 @@ const AiChat = forwardRef(
18
18
  copilotId,
19
19
  autoFocus,
20
20
  overrides,
21
- knowledgeSources = [],
21
+ knowledge,
22
22
  tools = {},
23
+ components,
23
24
  className,
24
25
  ...props
25
26
  }, forwardedRef) => {
@@ -30,29 +31,23 @@ const AiChat = forwardRef(
30
31
  null
31
32
  );
32
33
  const client = useClient();
34
+ const ai = client[kInternal].ai;
35
+ const [lastSendMessageId, setLastSendMessageId] = useState(null);
33
36
  useImperativeHandle(
34
37
  forwardedRef,
35
38
  () => containerRef.current,
36
39
  []
37
40
  );
38
- useEffect(() => {
39
- const unregister = knowledgeSources.map(
40
- (source) => client[kInternal].ai.registerKnowledgeSource(chatId, source)
41
- );
42
- return () => {
43
- unregister.forEach((unregister2) => unregister2());
44
- };
45
- }, [client, chatId, knowledgeSources]);
46
41
  useEffect(() => {
47
42
  Object.entries(tools).map(
48
- ([key, value]) => client[kInternal].ai.registerChatTool(chatId, key, value)
43
+ ([key, value]) => ai.registerChatTool(chatId, key, value)
49
44
  );
50
45
  return () => {
51
46
  Object.entries(tools).map(
52
- ([key]) => client[kInternal].ai.unregisterChatTool(chatId, key)
47
+ ([key]) => ai.unregisterChatTool(chatId, key)
53
48
  );
54
49
  };
55
- }, [client, chatId, tools]);
50
+ }, [ai, chatId, tools]);
56
51
  useEffect(() => {
57
52
  const container = containerRef.current;
58
53
  if (container === null)
@@ -82,16 +77,11 @@ const AiChat = forwardRef(
82
77
  const container = containerRef.current;
83
78
  if (container === null)
84
79
  return;
85
- if (messages === void 0)
86
- return;
87
- const lastMessage = messages[messages.length - 1];
88
- if (lastMessage !== void 0 && lastMessage.role === "user") {
89
- container.scrollTo({
90
- top: container.scrollHeight,
91
- behavior: "smooth"
92
- });
93
- }
94
- }, [messages]);
80
+ container.scrollTo({
81
+ top: container.scrollHeight,
82
+ behavior: "smooth"
83
+ });
84
+ }, [lastSendMessageId]);
95
85
  useEffect(() => {
96
86
  const container = containerRef.current;
97
87
  if (container === null)
@@ -127,6 +117,10 @@ const AiChat = forwardRef(
127
117
  ...props,
128
118
  className: classNames("lb-root lb-ai-chat", className),
129
119
  children: [
120
+ knowledge ? knowledge.map((source, index) => /* @__PURE__ */ jsx(RegisterAiKnowledge, {
121
+ description: source.description,
122
+ value: source.value
123
+ }, index)) : null,
130
124
  /* @__PURE__ */ jsx("div", {
131
125
  className: "lb-ai-chat-content",
132
126
  children: isLoading ? /* @__PURE__ */ jsx("div", {
@@ -140,6 +134,7 @@ const AiChat = forwardRef(
140
134
  children: /* @__PURE__ */ jsx(Messages, {
141
135
  messages,
142
136
  overrides: $,
137
+ components,
143
138
  onDistanceToBottomChange: scrollToBottomCallbackRef.current
144
139
  })
145
140
  })
@@ -177,7 +172,8 @@ const AiChat = forwardRef(
177
172
  chatId,
178
173
  copilotId,
179
174
  overrides: $,
180
- autoFocus
175
+ autoFocus,
176
+ onUserMessageCreate: ({ id }) => setLastSendMessageId(id)
181
177
  }, chatId)
182
178
  ]
183
179
  })
@@ -188,9 +184,9 @@ const AiChat = forwardRef(
188
184
  function Messages({
189
185
  messages,
190
186
  overrides,
187
+ components,
191
188
  onDistanceToBottomChange
192
189
  }) {
193
- const $ = useOverrides(overrides);
194
190
  useLayoutEffect(() => {
195
191
  onDistanceToBottomChange();
196
192
  }, [onDistanceToBottomChange]);
@@ -198,12 +194,13 @@ function Messages({
198
194
  if (message.role === "user") {
199
195
  return /* @__PURE__ */ jsx(AiChatUserMessage, {
200
196
  message,
201
- overrides: $
197
+ overrides
202
198
  }, message.id);
203
199
  } else if (message.role === "assistant") {
204
200
  return /* @__PURE__ */ jsx(AiChatAssistantMessage, {
205
201
  message,
206
- overrides: $
202
+ overrides,
203
+ components
207
204
  }, message.id);
208
205
  } else {
209
206
  return null;
@@ -1 +1 @@
1
- {"version":3,"file":"AiChat.js","sources":["../../src/components/AiChat.tsx"],"sourcesContent":["import type {\n AiKnowledgeSource,\n ClientToolDefinition,\n CopilotId,\n UiChatMessage,\n} from \"@liveblocks/core\";\nimport { kInternal } from \"@liveblocks/core\";\nimport { useAiChatMessages, useClient } 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 { 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 */\n knowledgeSources?: 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, ClientToolDefinition>;\n /**\n * Override the component's strings.\n */\n overrides?: Partial<\n GlobalOverrides &\n AiChatMessageOverrides &\n AiChatComposerOverrides &\n AiChatOverrides\n >;\n}\n\nexport const AiChat = forwardRef<HTMLDivElement, AiChatProps>(\n (\n {\n chatId,\n copilotId,\n autoFocus,\n overrides,\n knowledgeSources = [],\n tools = {},\n className,\n ...props\n },\n forwardedRef\n ) => {\n const { messages, isLoading, error } = useAiChatMessages(chatId);\n const $ = useOverrides(overrides);\n const containerRef = useRef<HTMLDivElement | null>(null);\n const [distanceToBottom, setDistanceToBottom] = useState<number | null>(\n null\n );\n const client = useClient();\n\n useImperativeHandle<HTMLDivElement | null, HTMLDivElement | null>(\n forwardedRef,\n () => containerRef.current,\n []\n );\n\n // Add the provided contextual information to the chat on mount and remove on unmount\n // Note: 'contexts' will most likely be a new object on each render (unless user passes a stable object), but this won't be an issue as context addition and removal is a quick operation\n useEffect(() => {\n const unregister = knowledgeSources.map((source) =>\n client[kInternal].ai.registerKnowledgeSource(chatId, source)\n );\n return () => {\n unregister.forEach((unregister) => unregister());\n };\n }, [client, chatId, knowledgeSources]);\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 client[kInternal].ai.registerChatTool(chatId, key, value)\n );\n return () => {\n Object.entries(tools).map(([key]) =>\n client[kInternal].ai.unregisterChatTool(chatId, key)\n );\n };\n }, [client, 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 if (messages === undefined) return;\n const lastMessage = messages[messages.length - 1];\n if (lastMessage !== undefined && lastMessage.role === \"user\") {\n container.scrollTo({\n top: container.scrollHeight,\n behavior: \"smooth\",\n });\n }\n }, [messages]);\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 <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 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 />\n </div>\n </div>\n );\n }\n);\n\nfunction Messages({\n messages,\n overrides,\n onDistanceToBottomChange,\n}: {\n messages: readonly UiChatMessage[];\n overrides: Partial<GlobalOverrides & AiChatMessageOverrides>;\n onDistanceToBottomChange: () => void;\n}) {\n const $ = useOverrides(overrides);\n\n useLayoutEffect(() => {\n onDistanceToBottomChange();\n }, [onDistanceToBottomChange]);\n\n return messages.map((message) => {\n if (message.role === \"user\") {\n return (\n <AiChatUserMessage key={message.id} message={message} overrides={$} />\n );\n } else if (message.role === \"assistant\") {\n return (\n <AiChatAssistantMessage\n key={message.id}\n message={message}\n overrides={$}\n />\n );\n } else {\n return null;\n }\n });\n}\n"],"names":["unregister","container"],"mappings":"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"}
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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"}
@@ -0,0 +1,74 @@
1
+ 'use strict';
2
+
3
+ var jsxRuntime = require('react/jsx-runtime');
4
+
5
+ function AiToolDebugger(props) {
6
+ const color = props.status === "executed" ? "darkgreen" : props.status === "executing" ? "orange" : "gray";
7
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", {
8
+ className: "lb-ai-chat-message-tool",
9
+ style: {
10
+ border: `2px solid ${color}`,
11
+ padding: "1rem"
12
+ },
13
+ children: [
14
+ /* @__PURE__ */ jsxRuntime.jsxs("div", {
15
+ children: [
16
+ /* @__PURE__ */ jsxRuntime.jsx("b", {
17
+ children: "status:"
18
+ }),
19
+ " ",
20
+ /* @__PURE__ */ jsxRuntime.jsx("span", {
21
+ style: { color },
22
+ children: props.status
23
+ })
24
+ ]
25
+ }),
26
+ /* @__PURE__ */ jsxRuntime.jsxs("div", {
27
+ children: [
28
+ /* @__PURE__ */ jsxRuntime.jsx("b", {
29
+ children: "name:"
30
+ }),
31
+ " ",
32
+ props.toolName
33
+ ]
34
+ }),
35
+ /* @__PURE__ */ jsxRuntime.jsx("div", {
36
+ children: props.partialArgs ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, {
37
+ children: [
38
+ /* @__PURE__ */ jsxRuntime.jsx("b", {
39
+ children: "partialArgs:"
40
+ }),
41
+ " ",
42
+ /* @__PURE__ */ jsxRuntime.jsx("code", {
43
+ children: JSON.stringify(props.partialArgs)
44
+ })
45
+ ]
46
+ }) : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, {
47
+ children: [
48
+ /* @__PURE__ */ jsxRuntime.jsx("b", {
49
+ children: "args:"
50
+ }),
51
+ " ",
52
+ /* @__PURE__ */ jsxRuntime.jsx("code", {
53
+ children: JSON.stringify(props.args)
54
+ })
55
+ ]
56
+ })
57
+ }),
58
+ /* @__PURE__ */ jsxRuntime.jsxs("div", {
59
+ children: [
60
+ /* @__PURE__ */ jsxRuntime.jsx("b", {
61
+ children: "result:"
62
+ }),
63
+ " ",
64
+ props.result ? /* @__PURE__ */ jsxRuntime.jsx("code", {
65
+ children: JSON.stringify(props.result)
66
+ }) : "\u2014"
67
+ ]
68
+ })
69
+ ]
70
+ });
71
+ }
72
+
73
+ exports.AiToolDebugger = AiToolDebugger;
74
+ //# sourceMappingURL=AiToolDebugger.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AiToolDebugger.cjs","sources":["../../src/components/AiToolDebugger.tsx"],"sourcesContent":["import type { AiToolDefinitionRenderProps } from \"@liveblocks/core\";\n\n/**\n * @experimental\n * Helper to debug tool invocations.\n *\n * Simply drop this into your tool definition's `render` property to visually\n * see what's going on with your tool calls.\n */\nexport function AiToolDebugger(props: AiToolDefinitionRenderProps) {\n const color =\n props.status === \"executed\"\n ? \"darkgreen\"\n : props.status === \"executing\"\n ? \"orange\"\n : \"gray\";\n return (\n <div\n className=\"lb-ai-chat-message-tool\"\n style={{\n border: `2px solid ${color}`,\n padding: \"1rem\",\n }}\n >\n <div>\n <b>status:</b> <span style={{ color }}>{props.status}</span>\n </div>\n <div>\n <b>name:</b> {props.toolName}\n </div>\n <div>\n {props.partialArgs ? (\n <>\n <b>partialArgs:</b> <code>{JSON.stringify(props.partialArgs)}</code>\n </>\n ) : (\n <>\n <b>args:</b> <code>{JSON.stringify(props.args)}</code>\n </>\n )}\n </div>\n <div>\n <b>result:</b>{\" \"}\n {props.result ? <code>{JSON.stringify(props.result)}</code> : \"—\"}\n </div>\n </div>\n );\n}\n"],"names":["jsxs","jsx","Fragment"],"mappings":";;;;AASO,SAAS,eAAe,KAAoC,EAAA;AACjE,EAAM,MAAA,KAAA,GACJ,MAAM,MAAW,KAAA,UAAA,GACb,cACA,KAAM,CAAA,MAAA,KAAW,cACf,QACA,GAAA,MAAA,CAAA;AACR,EAAA,uBACGA,eAAA,CAAA,KAAA,EAAA;AAAA,IACC,SAAU,EAAA,yBAAA;AAAA,IACV,KAAO,EAAA;AAAA,MACL,QAAQ,CAAa,UAAA,EAAA,KAAA,CAAA,CAAA;AAAA,MACrB,OAAS,EAAA,MAAA;AAAA,KACX;AAAA,IAEA,QAAA,EAAA;AAAA,sBAACA,eAAA,CAAA,KAAA,EAAA;AAAA,QACC,QAAA,EAAA;AAAA,0BAACC,cAAA,CAAA,GAAA,EAAA;AAAA,YAAE,QAAA,EAAA,SAAA;AAAA,WAAO,CAAA;AAAA,UAAI,GAAA;AAAA,0BAAEA,cAAA,CAAA,MAAA,EAAA;AAAA,YAAK,KAAA,EAAO,EAAE,KAAM,EAAA;AAAA,YAAI,QAAM,EAAA,KAAA,CAAA,MAAA;AAAA,WAAO,CAAA;AAAA,SAAA;AAAA,OACvD,CAAA;AAAA,sBACCD,eAAA,CAAA,KAAA,EAAA;AAAA,QACC,QAAA,EAAA;AAAA,0BAACC,cAAA,CAAA,GAAA,EAAA;AAAA,YAAE,QAAA,EAAA,OAAA;AAAA,WAAK,CAAA;AAAA,UAAI,GAAA;AAAA,UAAE,KAAM,CAAA,QAAA;AAAA,SAAA;AAAA,OACtB,CAAA;AAAA,sBACCA,cAAA,CAAA,KAAA,EAAA;AAAA,QACE,gBAAM,WACL,mBAAAD,eAAA,CAAAE,mBAAA,EAAA;AAAA,UACE,QAAA,EAAA;AAAA,4BAACD,cAAA,CAAA,GAAA,EAAA;AAAA,cAAE,QAAA,EAAA,cAAA;AAAA,aAAY,CAAA;AAAA,YAAI,GAAA;AAAA,4BAAEA,cAAA,CAAA,MAAA,EAAA;AAAA,cAAM,QAAA,EAAA,IAAA,CAAK,SAAU,CAAA,KAAA,CAAM,WAAW,CAAA;AAAA,aAAE,CAAA;AAAA,WAAA;AAAA,SAC/D,CAEA,mBAAAD,eAAA,CAAAE,mBAAA,EAAA;AAAA,UACE,QAAA,EAAA;AAAA,4BAACD,cAAA,CAAA,GAAA,EAAA;AAAA,cAAE,QAAA,EAAA,OAAA;AAAA,aAAK,CAAA;AAAA,YAAI,GAAA;AAAA,4BAAEA,cAAA,CAAA,MAAA,EAAA;AAAA,cAAM,QAAA,EAAA,IAAA,CAAK,SAAU,CAAA,KAAA,CAAM,IAAI,CAAA;AAAA,aAAE,CAAA;AAAA,WAAA;AAAA,SACjD,CAAA;AAAA,OAEJ,CAAA;AAAA,sBACCD,eAAA,CAAA,KAAA,EAAA;AAAA,QACC,QAAA,EAAA;AAAA,0BAACC,cAAA,CAAA,GAAA,EAAA;AAAA,YAAE,QAAA,EAAA,SAAA;AAAA,WAAO,CAAA;AAAA,UAAK,GAAA;AAAA,UACd,KAAA,CAAM,yBAAUA,cAAA,CAAA,MAAA,EAAA;AAAA,YAAM,QAAA,EAAA,IAAA,CAAK,SAAU,CAAA,KAAA,CAAM,MAAM,CAAA;AAAA,WAAE,CAAU,GAAA,QAAA;AAAA,SAAA;AAAA,OAChE,CAAA;AAAA,KAAA;AAAA,GACF,CAAA,CAAA;AAEJ;;;;"}
@@ -0,0 +1,72 @@
1
+ import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
2
+
3
+ function AiToolDebugger(props) {
4
+ const color = props.status === "executed" ? "darkgreen" : props.status === "executing" ? "orange" : "gray";
5
+ return /* @__PURE__ */ jsxs("div", {
6
+ className: "lb-ai-chat-message-tool",
7
+ style: {
8
+ border: `2px solid ${color}`,
9
+ padding: "1rem"
10
+ },
11
+ children: [
12
+ /* @__PURE__ */ jsxs("div", {
13
+ children: [
14
+ /* @__PURE__ */ jsx("b", {
15
+ children: "status:"
16
+ }),
17
+ " ",
18
+ /* @__PURE__ */ jsx("span", {
19
+ style: { color },
20
+ children: props.status
21
+ })
22
+ ]
23
+ }),
24
+ /* @__PURE__ */ jsxs("div", {
25
+ children: [
26
+ /* @__PURE__ */ jsx("b", {
27
+ children: "name:"
28
+ }),
29
+ " ",
30
+ props.toolName
31
+ ]
32
+ }),
33
+ /* @__PURE__ */ jsx("div", {
34
+ children: props.partialArgs ? /* @__PURE__ */ jsxs(Fragment, {
35
+ children: [
36
+ /* @__PURE__ */ jsx("b", {
37
+ children: "partialArgs:"
38
+ }),
39
+ " ",
40
+ /* @__PURE__ */ jsx("code", {
41
+ children: JSON.stringify(props.partialArgs)
42
+ })
43
+ ]
44
+ }) : /* @__PURE__ */ jsxs(Fragment, {
45
+ children: [
46
+ /* @__PURE__ */ jsx("b", {
47
+ children: "args:"
48
+ }),
49
+ " ",
50
+ /* @__PURE__ */ jsx("code", {
51
+ children: JSON.stringify(props.args)
52
+ })
53
+ ]
54
+ })
55
+ }),
56
+ /* @__PURE__ */ jsxs("div", {
57
+ children: [
58
+ /* @__PURE__ */ jsx("b", {
59
+ children: "result:"
60
+ }),
61
+ " ",
62
+ props.result ? /* @__PURE__ */ jsx("code", {
63
+ children: JSON.stringify(props.result)
64
+ }) : "\u2014"
65
+ ]
66
+ })
67
+ ]
68
+ });
69
+ }
70
+
71
+ export { AiToolDebugger };
72
+ //# sourceMappingURL=AiToolDebugger.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AiToolDebugger.js","sources":["../../src/components/AiToolDebugger.tsx"],"sourcesContent":["import type { AiToolDefinitionRenderProps } from \"@liveblocks/core\";\n\n/**\n * @experimental\n * Helper to debug tool invocations.\n *\n * Simply drop this into your tool definition's `render` property to visually\n * see what's going on with your tool calls.\n */\nexport function AiToolDebugger(props: AiToolDefinitionRenderProps) {\n const color =\n props.status === \"executed\"\n ? \"darkgreen\"\n : props.status === \"executing\"\n ? \"orange\"\n : \"gray\";\n return (\n <div\n className=\"lb-ai-chat-message-tool\"\n style={{\n border: `2px solid ${color}`,\n padding: \"1rem\",\n }}\n >\n <div>\n <b>status:</b> <span style={{ color }}>{props.status}</span>\n </div>\n <div>\n <b>name:</b> {props.toolName}\n </div>\n <div>\n {props.partialArgs ? (\n <>\n <b>partialArgs:</b> <code>{JSON.stringify(props.partialArgs)}</code>\n </>\n ) : (\n <>\n <b>args:</b> <code>{JSON.stringify(props.args)}</code>\n </>\n )}\n </div>\n <div>\n <b>result:</b>{\" \"}\n {props.result ? <code>{JSON.stringify(props.result)}</code> : \"—\"}\n </div>\n </div>\n );\n}\n"],"names":[],"mappings":";;AASO,SAAS,eAAe,KAAoC,EAAA;AACjE,EAAM,MAAA,KAAA,GACJ,MAAM,MAAW,KAAA,UAAA,GACb,cACA,KAAM,CAAA,MAAA,KAAW,cACf,QACA,GAAA,MAAA,CAAA;AACR,EAAA,uBACG,IAAA,CAAA,KAAA,EAAA;AAAA,IACC,SAAU,EAAA,yBAAA;AAAA,IACV,KAAO,EAAA;AAAA,MACL,QAAQ,CAAa,UAAA,EAAA,KAAA,CAAA,CAAA;AAAA,MACrB,OAAS,EAAA,MAAA;AAAA,KACX;AAAA,IAEA,QAAA,EAAA;AAAA,sBAAC,IAAA,CAAA,KAAA,EAAA;AAAA,QACC,QAAA,EAAA;AAAA,0BAAC,GAAA,CAAA,GAAA,EAAA;AAAA,YAAE,QAAA,EAAA,SAAA;AAAA,WAAO,CAAA;AAAA,UAAI,GAAA;AAAA,0BAAE,GAAA,CAAA,MAAA,EAAA;AAAA,YAAK,KAAA,EAAO,EAAE,KAAM,EAAA;AAAA,YAAI,QAAM,EAAA,KAAA,CAAA,MAAA;AAAA,WAAO,CAAA;AAAA,SAAA;AAAA,OACvD,CAAA;AAAA,sBACC,IAAA,CAAA,KAAA,EAAA;AAAA,QACC,QAAA,EAAA;AAAA,0BAAC,GAAA,CAAA,GAAA,EAAA;AAAA,YAAE,QAAA,EAAA,OAAA;AAAA,WAAK,CAAA;AAAA,UAAI,GAAA;AAAA,UAAE,KAAM,CAAA,QAAA;AAAA,SAAA;AAAA,OACtB,CAAA;AAAA,sBACC,GAAA,CAAA,KAAA,EAAA;AAAA,QACE,gBAAM,WACL,mBAAA,IAAA,CAAA,QAAA,EAAA;AAAA,UACE,QAAA,EAAA;AAAA,4BAAC,GAAA,CAAA,GAAA,EAAA;AAAA,cAAE,QAAA,EAAA,cAAA;AAAA,aAAY,CAAA;AAAA,YAAI,GAAA;AAAA,4BAAE,GAAA,CAAA,MAAA,EAAA;AAAA,cAAM,QAAA,EAAA,IAAA,CAAK,SAAU,CAAA,KAAA,CAAM,WAAW,CAAA;AAAA,aAAE,CAAA;AAAA,WAAA;AAAA,SAC/D,CAEA,mBAAA,IAAA,CAAA,QAAA,EAAA;AAAA,UACE,QAAA,EAAA;AAAA,4BAAC,GAAA,CAAA,GAAA,EAAA;AAAA,cAAE,QAAA,EAAA,OAAA;AAAA,aAAK,CAAA;AAAA,YAAI,GAAA;AAAA,4BAAE,GAAA,CAAA,MAAA,EAAA;AAAA,cAAM,QAAA,EAAA,IAAA,CAAK,SAAU,CAAA,KAAA,CAAM,IAAI,CAAA;AAAA,aAAE,CAAA;AAAA,WAAA;AAAA,SACjD,CAAA;AAAA,OAEJ,CAAA;AAAA,sBACC,IAAA,CAAA,KAAA,EAAA;AAAA,QACC,QAAA,EAAA;AAAA,0BAAC,GAAA,CAAA,GAAA,EAAA;AAAA,YAAE,QAAA,EAAA,SAAA;AAAA,WAAO,CAAA;AAAA,UAAK,GAAA;AAAA,UACd,KAAA,CAAM,yBAAU,GAAA,CAAA,MAAA,EAAA;AAAA,YAAM,QAAA,EAAA,IAAA,CAAK,SAAU,CAAA,KAAA,CAAM,MAAM,CAAA;AAAA,WAAE,CAAU,GAAA,QAAA;AAAA,SAAA;AAAA,OAChE,CAAA;AAAA,KAAA;AAAA,GACF,CAAA,CAAA;AAEJ;;;;"}
@@ -8,8 +8,8 @@ var react = require('react');
8
8
  var ArrowDown = require('../icons/ArrowDown.cjs');
9
9
  var Bell = require('../icons/Bell.cjs');
10
10
  var BellCrossed = require('../icons/BellCrossed.cjs');
11
- var Resolve = require('../icons/Resolve.cjs');
12
- var Resolved = require('../icons/Resolved.cjs');
11
+ var CheckCircle = require('../icons/CheckCircle.cjs');
12
+ var CheckCircleFill = require('../icons/CheckCircleFill.cjs');
13
13
  var overrides = require('../overrides.cjs');
14
14
  var classNames = require('../utils/class-names.cjs');
15
15
  var findLastIndex = require('../utils/find-last-index.cjs');
@@ -189,7 +189,7 @@ const Thread = react.forwardRef(
189
189
  className: "lb-comment-action",
190
190
  onClick: stopPropagation,
191
191
  "aria-label": thread.resolved ? $.THREAD_UNRESOLVE : $.THREAD_RESOLVE,
192
- icon: thread.resolved ? /* @__PURE__ */ jsxRuntime.jsx(Resolved.ResolvedIcon, {}) : /* @__PURE__ */ jsxRuntime.jsx(Resolve.ResolveIcon, {})
192
+ icon: thread.resolved ? /* @__PURE__ */ jsxRuntime.jsx(CheckCircleFill.CheckCircleFillIcon, {}) : /* @__PURE__ */ jsxRuntime.jsx(CheckCircle.CheckCircleIcon, {})
193
193
  })
194
194
  })
195
195
  }) : null,
@@ -1 +1 @@
1
- {"version":3,"file":"Thread.cjs","sources":["../../src/components/Thread.tsx"],"sourcesContent":["\"use client\";\n\nimport type {\n BaseMetadata,\n CommentData,\n DM,\n ThreadData,\n} from \"@liveblocks/core\";\nimport {\n useMarkRoomThreadAsResolved,\n useMarkRoomThreadAsUnresolved,\n useRoomThreadSubscription,\n} from \"@liveblocks/react/_private\";\nimport * as TogglePrimitive from \"@radix-ui/react-toggle\";\nimport type {\n ComponentPropsWithoutRef,\n ForwardedRef,\n RefAttributes,\n SyntheticEvent,\n} from \"react\";\nimport {\n forwardRef,\n Fragment,\n useCallback,\n useEffect,\n useMemo,\n useState,\n} from \"react\";\n\nimport { ArrowDownIcon } from \"../icons/ArrowDown\";\nimport { BellIcon } from \"../icons/Bell\";\nimport { BellCrossedIcon } from \"../icons/BellCrossed\";\nimport { ResolveIcon } from \"../icons/Resolve\";\nimport { ResolvedIcon } from \"../icons/Resolved\";\nimport type {\n CommentOverrides,\n ComposerOverrides,\n GlobalOverrides,\n ThreadOverrides,\n} from \"../overrides\";\nimport { useOverrides } from \"../overrides\";\nimport { classNames } from \"../utils/class-names\";\nimport { findLastIndex } from \"../utils/find-last-index\";\nimport type { CommentProps } from \"./Comment\";\nimport { Comment } from \"./Comment\";\nimport type { ComposerProps } from \"./Composer\";\nimport { Composer } from \"./Composer\";\nimport { Button } from \"./internal/Button\";\nimport { DropdownItem } from \"./internal/Dropdown\";\nimport { Tooltip, TooltipProvider } from \"./internal/Tooltip\";\n\nexport interface ThreadProps<M extends BaseMetadata = DM>\n extends ComponentPropsWithoutRef<\"div\"> {\n /**\n * The thread to display.\n */\n thread: ThreadData<M>;\n\n /**\n * How to show or hide the composer to reply to the thread.\n */\n showComposer?: boolean | \"collapsed\";\n\n /**\n * Whether to show the action to resolve the thread.\n */\n showResolveAction?: boolean;\n\n /**\n * How to show or hide the actions.\n */\n showActions?: CommentProps[\"showActions\"];\n\n /**\n * Whether to show reactions.\n */\n showReactions?: CommentProps[\"showReactions\"];\n\n /**\n * Whether to show the composer's formatting controls.\n */\n showComposerFormattingControls?: ComposerProps[\"showFormattingControls\"];\n\n /**\n * Whether to indent the comments' content.\n */\n indentCommentContent?: CommentProps[\"indentContent\"];\n\n /**\n * Whether to show deleted comments.\n */\n showDeletedComments?: CommentProps[\"showDeleted\"];\n\n /**\n * Whether to show attachments.\n */\n showAttachments?: boolean;\n\n /**\n * The event handler called when changing the resolved status.\n */\n onResolvedChange?: (resolved: boolean) => void;\n\n /**\n * The event handler called when a comment is edited.\n */\n onCommentEdit?: CommentProps[\"onCommentEdit\"];\n\n /**\n * The event handler called when a comment is deleted.\n */\n onCommentDelete?: CommentProps[\"onCommentDelete\"];\n\n /**\n * The event handler called when the thread is deleted.\n * A thread is deleted when all its comments are deleted.\n */\n onThreadDelete?: (thread: ThreadData<M>) => void;\n\n /**\n * The event handler called when clicking on a comment's author.\n */\n onAuthorClick?: CommentProps[\"onAuthorClick\"];\n\n /**\n * The event handler called when clicking on a mention.\n */\n onMentionClick?: CommentProps[\"onMentionClick\"];\n\n /**\n * The event handler called when clicking on a comment's attachment.\n */\n onAttachmentClick?: CommentProps[\"onAttachmentClick\"];\n\n /**\n * The event handler called when the composer is submitted.\n */\n onComposerSubmit?: ComposerProps[\"onComposerSubmit\"];\n\n /**\n * Override the component's strings.\n */\n overrides?: Partial<\n GlobalOverrides & ThreadOverrides & CommentOverrides & ComposerOverrides\n >;\n}\n\n/**\n * Displays a thread of comments, with a composer to reply\n * to it.\n *\n * @example\n * <>\n * {threads.map((thread) => (\n * <Thread key={thread.id} thread={thread} />\n * ))}\n * </>\n */\nexport const Thread = forwardRef(\n <M extends BaseMetadata = DM>(\n {\n thread,\n indentCommentContent = true,\n showActions = \"hover\",\n showDeletedComments,\n showResolveAction = true,\n showReactions = true,\n showComposer = \"collapsed\",\n showAttachments = true,\n showComposerFormattingControls = true,\n onResolvedChange,\n onCommentEdit,\n onCommentDelete,\n onThreadDelete,\n onAuthorClick,\n onMentionClick,\n onAttachmentClick,\n onComposerSubmit,\n overrides,\n className,\n ...props\n }: ThreadProps<M>,\n forwardedRef: ForwardedRef<HTMLDivElement>\n ) => {\n const markThreadAsResolved = useMarkRoomThreadAsResolved(thread.roomId);\n const markThreadAsUnresolved = useMarkRoomThreadAsUnresolved(thread.roomId);\n const $ = useOverrides(overrides);\n const firstCommentIndex = useMemo(() => {\n return showDeletedComments\n ? 0\n : thread.comments.findIndex((comment) => comment.body);\n }, [showDeletedComments, thread.comments]);\n const lastCommentIndex = useMemo(() => {\n return showDeletedComments\n ? thread.comments.length - 1\n : findLastIndex(thread.comments, (comment) => comment.body);\n }, [showDeletedComments, thread.comments]);\n const {\n status: subscriptionStatus,\n unreadSince,\n subscribe,\n unsubscribe,\n } = useRoomThreadSubscription(thread.roomId, thread.id);\n const unreadIndex = useMemo(() => {\n // The user is not subscribed to this thread.\n if (subscriptionStatus !== \"subscribed\") {\n return;\n }\n\n // The user hasn't read the thread yet, so all comments are unread.\n if (unreadSince === null) {\n return firstCommentIndex;\n }\n\n // The user has read the thread, so we find the first unread comment.\n const unreadIndex = thread.comments.findIndex(\n (comment) =>\n (showDeletedComments ? true : comment.body) &&\n comment.createdAt > unreadSince\n );\n\n return unreadIndex >= 0 && unreadIndex < thread.comments.length\n ? unreadIndex\n : undefined;\n }, [\n firstCommentIndex,\n showDeletedComments,\n subscriptionStatus,\n thread.comments,\n unreadSince,\n ]);\n const [newIndex, setNewIndex] = useState<number>();\n const newIndicatorIndex = newIndex === undefined ? unreadIndex : newIndex;\n\n useEffect(() => {\n if (unreadIndex) {\n // Keep the \"new\" indicator at the lowest unread index.\n setNewIndex((persistedUnreadIndex) =>\n Math.min(persistedUnreadIndex ?? Infinity, unreadIndex)\n );\n }\n }, [unreadIndex]);\n\n const stopPropagation = useCallback((event: SyntheticEvent) => {\n event.stopPropagation();\n }, []);\n\n const handleResolvedChange = useCallback(\n (resolved: boolean) => {\n onResolvedChange?.(resolved);\n\n if (resolved) {\n markThreadAsResolved(thread.id);\n } else {\n markThreadAsUnresolved(thread.id);\n }\n },\n [\n markThreadAsResolved,\n markThreadAsUnresolved,\n onResolvedChange,\n thread.id,\n ]\n );\n\n const handleCommentDelete = useCallback(\n (comment: CommentData) => {\n onCommentDelete?.(comment);\n\n const filteredComments = thread.comments.filter(\n (comment) => comment.body\n );\n\n if (filteredComments.length <= 1) {\n onThreadDelete?.(thread);\n }\n },\n [onCommentDelete, onThreadDelete, thread]\n );\n\n const handleSubscribeChange = useCallback(() => {\n if (subscriptionStatus === \"subscribed\") {\n unsubscribe();\n } else {\n subscribe();\n }\n }, [subscriptionStatus, subscribe, unsubscribe]);\n\n return (\n <TooltipProvider>\n <div\n className={classNames(\n \"lb-root lb-thread\",\n showActions === \"hover\" && \"lb-thread:show-actions-hover\",\n className\n )}\n data-resolved={thread.resolved ? \"\" : undefined}\n data-unread={unreadIndex !== undefined ? \"\" : undefined}\n dir={$.dir}\n {...props}\n ref={forwardedRef}\n >\n <div className=\"lb-thread-comments\">\n {thread.comments.map((comment, index) => {\n const isFirstComment = index === firstCommentIndex;\n const isUnread =\n unreadIndex !== undefined && index >= unreadIndex;\n\n const children = (\n <Comment\n key={comment.id}\n overrides={overrides}\n className=\"lb-thread-comment\"\n data-unread={isUnread ? \"\" : undefined}\n comment={comment}\n indentContent={indentCommentContent}\n showDeleted={showDeletedComments}\n showActions={showActions}\n showReactions={showReactions}\n showAttachments={showAttachments}\n showComposerFormattingControls={\n showComposerFormattingControls\n }\n onCommentEdit={onCommentEdit}\n onCommentDelete={handleCommentDelete}\n onAuthorClick={onAuthorClick}\n onMentionClick={onMentionClick}\n onAttachmentClick={onAttachmentClick}\n autoMarkReadThreadId={\n index === lastCommentIndex && isUnread\n ? thread.id\n : undefined\n }\n additionalActionsClassName={\n isFirstComment ? \"lb-thread-actions\" : undefined\n }\n additionalActions={\n isFirstComment && showResolveAction ? (\n <Tooltip\n content={\n thread.resolved\n ? $.THREAD_UNRESOLVE\n : $.THREAD_RESOLVE\n }\n >\n <TogglePrimitive.Root\n pressed={thread.resolved}\n onPressedChange={handleResolvedChange}\n asChild\n >\n <Button\n className=\"lb-comment-action\"\n onClick={stopPropagation}\n aria-label={\n thread.resolved\n ? $.THREAD_UNRESOLVE\n : $.THREAD_RESOLVE\n }\n icon={\n thread.resolved ? (\n <ResolvedIcon />\n ) : (\n <ResolveIcon />\n )\n }\n />\n </TogglePrimitive.Root>\n </Tooltip>\n ) : null\n }\n additionalDropdownItemsBefore={\n isFirstComment ? (\n <DropdownItem\n onSelect={handleSubscribeChange}\n onClick={stopPropagation}\n icon={\n subscriptionStatus === \"subscribed\" ? (\n <BellCrossedIcon />\n ) : (\n <BellIcon />\n )\n }\n >\n {subscriptionStatus === \"subscribed\"\n ? $.THREAD_UNSUBSCRIBE\n : $.THREAD_SUBSCRIBE}\n </DropdownItem>\n ) : null\n }\n />\n );\n\n return index === newIndicatorIndex &&\n newIndicatorIndex !== firstCommentIndex &&\n newIndicatorIndex <= lastCommentIndex ? (\n <Fragment key={comment.id}>\n <div\n className=\"lb-thread-new-indicator\"\n aria-label={$.THREAD_NEW_INDICATOR_DESCRIPTION}\n >\n <span className=\"lb-thread-new-indicator-label\">\n <ArrowDownIcon className=\"lb-thread-new-indicator-label-icon\" />\n {$.THREAD_NEW_INDICATOR}\n </span>\n </div>\n {children}\n </Fragment>\n ) : (\n children\n );\n })}\n </div>\n {showComposer && (\n <Composer\n className=\"lb-thread-composer\"\n threadId={thread.id}\n defaultCollapsed={showComposer === \"collapsed\" ? true : undefined}\n showAttachments={showAttachments}\n showFormattingControls={showComposerFormattingControls}\n onComposerSubmit={onComposerSubmit}\n overrides={{\n COMPOSER_PLACEHOLDER: $.THREAD_COMPOSER_PLACEHOLDER,\n COMPOSER_SEND: $.THREAD_COMPOSER_SEND,\n ...overrides,\n }}\n roomId={thread.roomId}\n />\n )}\n </div>\n </TooltipProvider>\n );\n }\n) as <M extends BaseMetadata = DM>(\n props: ThreadProps<M> & RefAttributes<HTMLDivElement>\n) => JSX.Element;\n"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA8JO;AAAe;AAElB;AACE;AACuB;AACT;AACd;AACoB;AACJ;AACD;AACG;AACe;AACjC;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACG;AAIL;AACA;AACA;AACA;AACE;AAEuD;AAEzD;AACE;AAE4D;AAE9D;AAAM;AACI;AACR;AACA;AACA;AAEF;AAEE;AACE;AAAA;AAIF;AACE;AAAO;AAIT;AAAoC;AAGZ;AAGxB;AAEI;AACH;AACD;AACA;AACA;AACO;AACP;AAEF;AACA;AAEA;AACE;AAEE;AAAA;AACwD;AACxD;AACF;AAGF;AACE;AAAsB;AAGxB;AAA6B;AAEzB;AAEA;AACE;AAA8B;AAE9B;AAAgC;AAClC;AACF;AACA;AACE;AACA;AACA;AACO;AACT;AAGF;AAA4B;AAExB;AAEA;AAAyC;AAClB;AAGvB;AACE;AAAuB;AACzB;AACF;AACwC;AAG1C;AACE;AACE;AAAY;AAEZ;AAAU;AACZ;AAGF;AACG;AACE;AACY;AACT;AAC2B;AAC3B;AACF;AACsC;AACQ;AACvC;AACH;AACC;AAEL;AAAC;AAAc;AAEX;AACA;AAGA;AACG;AAEC;AACU;AACmB;AAC7B;AACe;AACF;AACb;AACA;AACA;AACA;AAGA;AACiB;AACjB;AACA;AACA;AAIM;AAGmC;AAIpC;AAIS;AAGP;AACiB;AACC;AACV;AAEN;AACW;AACD;AAID;AAMS;AAGnB;AACF;AAEA;AAID;AACW;AACD;AAKK;AAMR;AAEN;AAKV;AAGG;AACC;AAAC;AACW;AACI;AAEb;AAAe;AACd;AAAC;AAAwB;AAAqC;AAC3D;AAAA;AACL;AACF;AACC;AAAA;AAGH;AAEH;AACH;AAEG;AACW;AACO;AACuC;AACxD;AACwB;AACxB;AACW;AACe;AACP;AACd;AACL;AACe;AACjB;AAAA;AAEJ;AACF;AAGN;;"}
1
+ {"version":3,"file":"Thread.cjs","sources":["../../src/components/Thread.tsx"],"sourcesContent":["\"use client\";\n\nimport type {\n BaseMetadata,\n CommentData,\n DM,\n ThreadData,\n} from \"@liveblocks/core\";\nimport {\n useMarkRoomThreadAsResolved,\n useMarkRoomThreadAsUnresolved,\n useRoomThreadSubscription,\n} from \"@liveblocks/react/_private\";\nimport * as TogglePrimitive from \"@radix-ui/react-toggle\";\nimport type {\n ComponentPropsWithoutRef,\n ForwardedRef,\n RefAttributes,\n SyntheticEvent,\n} from \"react\";\nimport {\n forwardRef,\n Fragment,\n useCallback,\n useEffect,\n useMemo,\n useState,\n} from \"react\";\n\nimport { ArrowDownIcon } from \"../icons/ArrowDown\";\nimport { BellIcon } from \"../icons/Bell\";\nimport { BellCrossedIcon } from \"../icons/BellCrossed\";\nimport { CheckCircleIcon } from \"../icons/CheckCircle\";\nimport { CheckCircleFillIcon } from \"../icons/CheckCircleFill\";\nimport type {\n CommentOverrides,\n ComposerOverrides,\n GlobalOverrides,\n ThreadOverrides,\n} from \"../overrides\";\nimport { useOverrides } from \"../overrides\";\nimport { classNames } from \"../utils/class-names\";\nimport { findLastIndex } from \"../utils/find-last-index\";\nimport type { CommentProps } from \"./Comment\";\nimport { Comment } from \"./Comment\";\nimport type { ComposerProps } from \"./Composer\";\nimport { Composer } from \"./Composer\";\nimport { Button } from \"./internal/Button\";\nimport { DropdownItem } from \"./internal/Dropdown\";\nimport { Tooltip, TooltipProvider } from \"./internal/Tooltip\";\n\nexport interface ThreadProps<M extends BaseMetadata = DM>\n extends ComponentPropsWithoutRef<\"div\"> {\n /**\n * The thread to display.\n */\n thread: ThreadData<M>;\n\n /**\n * How to show or hide the composer to reply to the thread.\n */\n showComposer?: boolean | \"collapsed\";\n\n /**\n * Whether to show the action to resolve the thread.\n */\n showResolveAction?: boolean;\n\n /**\n * How to show or hide the actions.\n */\n showActions?: CommentProps[\"showActions\"];\n\n /**\n * Whether to show reactions.\n */\n showReactions?: CommentProps[\"showReactions\"];\n\n /**\n * Whether to show the composer's formatting controls.\n */\n showComposerFormattingControls?: ComposerProps[\"showFormattingControls\"];\n\n /**\n * Whether to indent the comments' content.\n */\n indentCommentContent?: CommentProps[\"indentContent\"];\n\n /**\n * Whether to show deleted comments.\n */\n showDeletedComments?: CommentProps[\"showDeleted\"];\n\n /**\n * Whether to show attachments.\n */\n showAttachments?: boolean;\n\n /**\n * The event handler called when changing the resolved status.\n */\n onResolvedChange?: (resolved: boolean) => void;\n\n /**\n * The event handler called when a comment is edited.\n */\n onCommentEdit?: CommentProps[\"onCommentEdit\"];\n\n /**\n * The event handler called when a comment is deleted.\n */\n onCommentDelete?: CommentProps[\"onCommentDelete\"];\n\n /**\n * The event handler called when the thread is deleted.\n * A thread is deleted when all its comments are deleted.\n */\n onThreadDelete?: (thread: ThreadData<M>) => void;\n\n /**\n * The event handler called when clicking on a comment's author.\n */\n onAuthorClick?: CommentProps[\"onAuthorClick\"];\n\n /**\n * The event handler called when clicking on a mention.\n */\n onMentionClick?: CommentProps[\"onMentionClick\"];\n\n /**\n * The event handler called when clicking on a comment's attachment.\n */\n onAttachmentClick?: CommentProps[\"onAttachmentClick\"];\n\n /**\n * The event handler called when the composer is submitted.\n */\n onComposerSubmit?: ComposerProps[\"onComposerSubmit\"];\n\n /**\n * Override the component's strings.\n */\n overrides?: Partial<\n GlobalOverrides & ThreadOverrides & CommentOverrides & ComposerOverrides\n >;\n}\n\n/**\n * Displays a thread of comments, with a composer to reply\n * to it.\n *\n * @example\n * <>\n * {threads.map((thread) => (\n * <Thread key={thread.id} thread={thread} />\n * ))}\n * </>\n */\nexport const Thread = forwardRef(\n <M extends BaseMetadata = DM>(\n {\n thread,\n indentCommentContent = true,\n showActions = \"hover\",\n showDeletedComments,\n showResolveAction = true,\n showReactions = true,\n showComposer = \"collapsed\",\n showAttachments = true,\n showComposerFormattingControls = true,\n onResolvedChange,\n onCommentEdit,\n onCommentDelete,\n onThreadDelete,\n onAuthorClick,\n onMentionClick,\n onAttachmentClick,\n onComposerSubmit,\n overrides,\n className,\n ...props\n }: ThreadProps<M>,\n forwardedRef: ForwardedRef<HTMLDivElement>\n ) => {\n const markThreadAsResolved = useMarkRoomThreadAsResolved(thread.roomId);\n const markThreadAsUnresolved = useMarkRoomThreadAsUnresolved(thread.roomId);\n const $ = useOverrides(overrides);\n const firstCommentIndex = useMemo(() => {\n return showDeletedComments\n ? 0\n : thread.comments.findIndex((comment) => comment.body);\n }, [showDeletedComments, thread.comments]);\n const lastCommentIndex = useMemo(() => {\n return showDeletedComments\n ? thread.comments.length - 1\n : findLastIndex(thread.comments, (comment) => comment.body);\n }, [showDeletedComments, thread.comments]);\n const {\n status: subscriptionStatus,\n unreadSince,\n subscribe,\n unsubscribe,\n } = useRoomThreadSubscription(thread.roomId, thread.id);\n const unreadIndex = useMemo(() => {\n // The user is not subscribed to this thread.\n if (subscriptionStatus !== \"subscribed\") {\n return;\n }\n\n // The user hasn't read the thread yet, so all comments are unread.\n if (unreadSince === null) {\n return firstCommentIndex;\n }\n\n // The user has read the thread, so we find the first unread comment.\n const unreadIndex = thread.comments.findIndex(\n (comment) =>\n (showDeletedComments ? true : comment.body) &&\n comment.createdAt > unreadSince\n );\n\n return unreadIndex >= 0 && unreadIndex < thread.comments.length\n ? unreadIndex\n : undefined;\n }, [\n firstCommentIndex,\n showDeletedComments,\n subscriptionStatus,\n thread.comments,\n unreadSince,\n ]);\n const [newIndex, setNewIndex] = useState<number>();\n const newIndicatorIndex = newIndex === undefined ? unreadIndex : newIndex;\n\n useEffect(() => {\n if (unreadIndex) {\n // Keep the \"new\" indicator at the lowest unread index.\n setNewIndex((persistedUnreadIndex) =>\n Math.min(persistedUnreadIndex ?? Infinity, unreadIndex)\n );\n }\n }, [unreadIndex]);\n\n const stopPropagation = useCallback((event: SyntheticEvent) => {\n event.stopPropagation();\n }, []);\n\n const handleResolvedChange = useCallback(\n (resolved: boolean) => {\n onResolvedChange?.(resolved);\n\n if (resolved) {\n markThreadAsResolved(thread.id);\n } else {\n markThreadAsUnresolved(thread.id);\n }\n },\n [\n markThreadAsResolved,\n markThreadAsUnresolved,\n onResolvedChange,\n thread.id,\n ]\n );\n\n const handleCommentDelete = useCallback(\n (comment: CommentData) => {\n onCommentDelete?.(comment);\n\n const filteredComments = thread.comments.filter(\n (comment) => comment.body\n );\n\n if (filteredComments.length <= 1) {\n onThreadDelete?.(thread);\n }\n },\n [onCommentDelete, onThreadDelete, thread]\n );\n\n const handleSubscribeChange = useCallback(() => {\n if (subscriptionStatus === \"subscribed\") {\n unsubscribe();\n } else {\n subscribe();\n }\n }, [subscriptionStatus, subscribe, unsubscribe]);\n\n return (\n <TooltipProvider>\n <div\n className={classNames(\n \"lb-root lb-thread\",\n showActions === \"hover\" && \"lb-thread:show-actions-hover\",\n className\n )}\n data-resolved={thread.resolved ? \"\" : undefined}\n data-unread={unreadIndex !== undefined ? \"\" : undefined}\n dir={$.dir}\n {...props}\n ref={forwardedRef}\n >\n <div className=\"lb-thread-comments\">\n {thread.comments.map((comment, index) => {\n const isFirstComment = index === firstCommentIndex;\n const isUnread =\n unreadIndex !== undefined && index >= unreadIndex;\n\n const children = (\n <Comment\n key={comment.id}\n overrides={overrides}\n className=\"lb-thread-comment\"\n data-unread={isUnread ? \"\" : undefined}\n comment={comment}\n indentContent={indentCommentContent}\n showDeleted={showDeletedComments}\n showActions={showActions}\n showReactions={showReactions}\n showAttachments={showAttachments}\n showComposerFormattingControls={\n showComposerFormattingControls\n }\n onCommentEdit={onCommentEdit}\n onCommentDelete={handleCommentDelete}\n onAuthorClick={onAuthorClick}\n onMentionClick={onMentionClick}\n onAttachmentClick={onAttachmentClick}\n autoMarkReadThreadId={\n index === lastCommentIndex && isUnread\n ? thread.id\n : undefined\n }\n additionalActionsClassName={\n isFirstComment ? \"lb-thread-actions\" : undefined\n }\n additionalActions={\n isFirstComment && showResolveAction ? (\n <Tooltip\n content={\n thread.resolved\n ? $.THREAD_UNRESOLVE\n : $.THREAD_RESOLVE\n }\n >\n <TogglePrimitive.Root\n pressed={thread.resolved}\n onPressedChange={handleResolvedChange}\n asChild\n >\n <Button\n className=\"lb-comment-action\"\n onClick={stopPropagation}\n aria-label={\n thread.resolved\n ? $.THREAD_UNRESOLVE\n : $.THREAD_RESOLVE\n }\n icon={\n thread.resolved ? (\n <CheckCircleFillIcon />\n ) : (\n <CheckCircleIcon />\n )\n }\n />\n </TogglePrimitive.Root>\n </Tooltip>\n ) : null\n }\n additionalDropdownItemsBefore={\n isFirstComment ? (\n <DropdownItem\n onSelect={handleSubscribeChange}\n onClick={stopPropagation}\n icon={\n subscriptionStatus === \"subscribed\" ? (\n <BellCrossedIcon />\n ) : (\n <BellIcon />\n )\n }\n >\n {subscriptionStatus === \"subscribed\"\n ? $.THREAD_UNSUBSCRIBE\n : $.THREAD_SUBSCRIBE}\n </DropdownItem>\n ) : null\n }\n />\n );\n\n return index === newIndicatorIndex &&\n newIndicatorIndex !== firstCommentIndex &&\n newIndicatorIndex <= lastCommentIndex ? (\n <Fragment key={comment.id}>\n <div\n className=\"lb-thread-new-indicator\"\n aria-label={$.THREAD_NEW_INDICATOR_DESCRIPTION}\n >\n <span className=\"lb-thread-new-indicator-label\">\n <ArrowDownIcon className=\"lb-thread-new-indicator-label-icon\" />\n {$.THREAD_NEW_INDICATOR}\n </span>\n </div>\n {children}\n </Fragment>\n ) : (\n children\n );\n })}\n </div>\n {showComposer && (\n <Composer\n className=\"lb-thread-composer\"\n threadId={thread.id}\n defaultCollapsed={showComposer === \"collapsed\" ? true : undefined}\n showAttachments={showAttachments}\n showFormattingControls={showComposerFormattingControls}\n onComposerSubmit={onComposerSubmit}\n overrides={{\n COMPOSER_PLACEHOLDER: $.THREAD_COMPOSER_PLACEHOLDER,\n COMPOSER_SEND: $.THREAD_COMPOSER_SEND,\n ...overrides,\n }}\n roomId={thread.roomId}\n />\n )}\n </div>\n </TooltipProvider>\n );\n }\n) as <M extends BaseMetadata = DM>(\n props: ThreadProps<M> & RefAttributes<HTMLDivElement>\n) => JSX.Element;\n"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA8JO;AAAe;AAElB;AACE;AACuB;AACT;AACd;AACoB;AACJ;AACD;AACG;AACe;AACjC;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACG;AAIL;AACA;AACA;AACA;AACE;AAEuD;AAEzD;AACE;AAE4D;AAE9D;AAAM;AACI;AACR;AACA;AACA;AAEF;AAEE;AACE;AAAA;AAIF;AACE;AAAO;AAIT;AAAoC;AAGZ;AAGxB;AAEI;AACH;AACD;AACA;AACA;AACO;AACP;AAEF;AACA;AAEA;AACE;AAEE;AAAA;AACwD;AACxD;AACF;AAGF;AACE;AAAsB;AAGxB;AAA6B;AAEzB;AAEA;AACE;AAA8B;AAE9B;AAAgC;AAClC;AACF;AACA;AACE;AACA;AACA;AACO;AACT;AAGF;AAA4B;AAExB;AAEA;AAAyC;AAClB;AAGvB;AACE;AAAuB;AACzB;AACF;AACwC;AAG1C;AACE;AACE;AAAY;AAEZ;AAAU;AACZ;AAGF;AACG;AACE;AACY;AACT;AAC2B;AAC3B;AACF;AACsC;AACQ;AACvC;AACH;AACC;AAEL;AAAC;AAAc;AAEX;AACA;AAGA;AACG;AAEC;AACU;AACmB;AAC7B;AACe;AACF;AACb;AACA;AACA;AACA;AAGA;AACiB;AACjB;AACA;AACA;AAIM;AAGmC;AAIpC;AAIS;AAGP;AACiB;AACC;AACV;AAEN;AACW;AACD;AAID;AAMa;AAGvB;AACF;AAEA;AAID;AACW;AACD;AAKK;AAMR;AAEN;AAKV;AAGG;AACC;AAAC;AACW;AACI;AAEb;AAAe;AACd;AAAC;AAAwB;AAAqC;AAC3D;AAAA;AACL;AACF;AACC;AAAA;AAGH;AAEH;AACH;AAEG;AACW;AACO;AACuC;AACxD;AACwB;AACxB;AACW;AACe;AACP;AACd;AACL;AACe;AACjB;AAAA;AAEJ;AACF;AAGN;;"}
@@ -6,8 +6,8 @@ import { forwardRef, useMemo, useState, useEffect, useCallback, Fragment } from
6
6
  import { ArrowDownIcon } from '../icons/ArrowDown.js';
7
7
  import { BellIcon } from '../icons/Bell.js';
8
8
  import { BellCrossedIcon } from '../icons/BellCrossed.js';
9
- import { ResolveIcon } from '../icons/Resolve.js';
10
- import { ResolvedIcon } from '../icons/Resolved.js';
9
+ import { CheckCircleIcon } from '../icons/CheckCircle.js';
10
+ import { CheckCircleFillIcon } from '../icons/CheckCircleFill.js';
11
11
  import { useOverrides } from '../overrides.js';
12
12
  import { classNames } from '../utils/class-names.js';
13
13
  import { findLastIndex } from '../utils/find-last-index.js';
@@ -168,7 +168,7 @@ const Thread = forwardRef(
168
168
  className: "lb-comment-action",
169
169
  onClick: stopPropagation,
170
170
  "aria-label": thread.resolved ? $.THREAD_UNRESOLVE : $.THREAD_RESOLVE,
171
- icon: thread.resolved ? /* @__PURE__ */ jsx(ResolvedIcon, {}) : /* @__PURE__ */ jsx(ResolveIcon, {})
171
+ icon: thread.resolved ? /* @__PURE__ */ jsx(CheckCircleFillIcon, {}) : /* @__PURE__ */ jsx(CheckCircleIcon, {})
172
172
  })
173
173
  })
174
174
  }) : null,
@@ -1 +1 @@
1
- {"version":3,"file":"Thread.js","sources":["../../src/components/Thread.tsx"],"sourcesContent":["\"use client\";\n\nimport type {\n BaseMetadata,\n CommentData,\n DM,\n ThreadData,\n} from \"@liveblocks/core\";\nimport {\n useMarkRoomThreadAsResolved,\n useMarkRoomThreadAsUnresolved,\n useRoomThreadSubscription,\n} from \"@liveblocks/react/_private\";\nimport * as TogglePrimitive from \"@radix-ui/react-toggle\";\nimport type {\n ComponentPropsWithoutRef,\n ForwardedRef,\n RefAttributes,\n SyntheticEvent,\n} from \"react\";\nimport {\n forwardRef,\n Fragment,\n useCallback,\n useEffect,\n useMemo,\n useState,\n} from \"react\";\n\nimport { ArrowDownIcon } from \"../icons/ArrowDown\";\nimport { BellIcon } from \"../icons/Bell\";\nimport { BellCrossedIcon } from \"../icons/BellCrossed\";\nimport { ResolveIcon } from \"../icons/Resolve\";\nimport { ResolvedIcon } from \"../icons/Resolved\";\nimport type {\n CommentOverrides,\n ComposerOverrides,\n GlobalOverrides,\n ThreadOverrides,\n} from \"../overrides\";\nimport { useOverrides } from \"../overrides\";\nimport { classNames } from \"../utils/class-names\";\nimport { findLastIndex } from \"../utils/find-last-index\";\nimport type { CommentProps } from \"./Comment\";\nimport { Comment } from \"./Comment\";\nimport type { ComposerProps } from \"./Composer\";\nimport { Composer } from \"./Composer\";\nimport { Button } from \"./internal/Button\";\nimport { DropdownItem } from \"./internal/Dropdown\";\nimport { Tooltip, TooltipProvider } from \"./internal/Tooltip\";\n\nexport interface ThreadProps<M extends BaseMetadata = DM>\n extends ComponentPropsWithoutRef<\"div\"> {\n /**\n * The thread to display.\n */\n thread: ThreadData<M>;\n\n /**\n * How to show or hide the composer to reply to the thread.\n */\n showComposer?: boolean | \"collapsed\";\n\n /**\n * Whether to show the action to resolve the thread.\n */\n showResolveAction?: boolean;\n\n /**\n * How to show or hide the actions.\n */\n showActions?: CommentProps[\"showActions\"];\n\n /**\n * Whether to show reactions.\n */\n showReactions?: CommentProps[\"showReactions\"];\n\n /**\n * Whether to show the composer's formatting controls.\n */\n showComposerFormattingControls?: ComposerProps[\"showFormattingControls\"];\n\n /**\n * Whether to indent the comments' content.\n */\n indentCommentContent?: CommentProps[\"indentContent\"];\n\n /**\n * Whether to show deleted comments.\n */\n showDeletedComments?: CommentProps[\"showDeleted\"];\n\n /**\n * Whether to show attachments.\n */\n showAttachments?: boolean;\n\n /**\n * The event handler called when changing the resolved status.\n */\n onResolvedChange?: (resolved: boolean) => void;\n\n /**\n * The event handler called when a comment is edited.\n */\n onCommentEdit?: CommentProps[\"onCommentEdit\"];\n\n /**\n * The event handler called when a comment is deleted.\n */\n onCommentDelete?: CommentProps[\"onCommentDelete\"];\n\n /**\n * The event handler called when the thread is deleted.\n * A thread is deleted when all its comments are deleted.\n */\n onThreadDelete?: (thread: ThreadData<M>) => void;\n\n /**\n * The event handler called when clicking on a comment's author.\n */\n onAuthorClick?: CommentProps[\"onAuthorClick\"];\n\n /**\n * The event handler called when clicking on a mention.\n */\n onMentionClick?: CommentProps[\"onMentionClick\"];\n\n /**\n * The event handler called when clicking on a comment's attachment.\n */\n onAttachmentClick?: CommentProps[\"onAttachmentClick\"];\n\n /**\n * The event handler called when the composer is submitted.\n */\n onComposerSubmit?: ComposerProps[\"onComposerSubmit\"];\n\n /**\n * Override the component's strings.\n */\n overrides?: Partial<\n GlobalOverrides & ThreadOverrides & CommentOverrides & ComposerOverrides\n >;\n}\n\n/**\n * Displays a thread of comments, with a composer to reply\n * to it.\n *\n * @example\n * <>\n * {threads.map((thread) => (\n * <Thread key={thread.id} thread={thread} />\n * ))}\n * </>\n */\nexport const Thread = forwardRef(\n <M extends BaseMetadata = DM>(\n {\n thread,\n indentCommentContent = true,\n showActions = \"hover\",\n showDeletedComments,\n showResolveAction = true,\n showReactions = true,\n showComposer = \"collapsed\",\n showAttachments = true,\n showComposerFormattingControls = true,\n onResolvedChange,\n onCommentEdit,\n onCommentDelete,\n onThreadDelete,\n onAuthorClick,\n onMentionClick,\n onAttachmentClick,\n onComposerSubmit,\n overrides,\n className,\n ...props\n }: ThreadProps<M>,\n forwardedRef: ForwardedRef<HTMLDivElement>\n ) => {\n const markThreadAsResolved = useMarkRoomThreadAsResolved(thread.roomId);\n const markThreadAsUnresolved = useMarkRoomThreadAsUnresolved(thread.roomId);\n const $ = useOverrides(overrides);\n const firstCommentIndex = useMemo(() => {\n return showDeletedComments\n ? 0\n : thread.comments.findIndex((comment) => comment.body);\n }, [showDeletedComments, thread.comments]);\n const lastCommentIndex = useMemo(() => {\n return showDeletedComments\n ? thread.comments.length - 1\n : findLastIndex(thread.comments, (comment) => comment.body);\n }, [showDeletedComments, thread.comments]);\n const {\n status: subscriptionStatus,\n unreadSince,\n subscribe,\n unsubscribe,\n } = useRoomThreadSubscription(thread.roomId, thread.id);\n const unreadIndex = useMemo(() => {\n // The user is not subscribed to this thread.\n if (subscriptionStatus !== \"subscribed\") {\n return;\n }\n\n // The user hasn't read the thread yet, so all comments are unread.\n if (unreadSince === null) {\n return firstCommentIndex;\n }\n\n // The user has read the thread, so we find the first unread comment.\n const unreadIndex = thread.comments.findIndex(\n (comment) =>\n (showDeletedComments ? true : comment.body) &&\n comment.createdAt > unreadSince\n );\n\n return unreadIndex >= 0 && unreadIndex < thread.comments.length\n ? unreadIndex\n : undefined;\n }, [\n firstCommentIndex,\n showDeletedComments,\n subscriptionStatus,\n thread.comments,\n unreadSince,\n ]);\n const [newIndex, setNewIndex] = useState<number>();\n const newIndicatorIndex = newIndex === undefined ? unreadIndex : newIndex;\n\n useEffect(() => {\n if (unreadIndex) {\n // Keep the \"new\" indicator at the lowest unread index.\n setNewIndex((persistedUnreadIndex) =>\n Math.min(persistedUnreadIndex ?? Infinity, unreadIndex)\n );\n }\n }, [unreadIndex]);\n\n const stopPropagation = useCallback((event: SyntheticEvent) => {\n event.stopPropagation();\n }, []);\n\n const handleResolvedChange = useCallback(\n (resolved: boolean) => {\n onResolvedChange?.(resolved);\n\n if (resolved) {\n markThreadAsResolved(thread.id);\n } else {\n markThreadAsUnresolved(thread.id);\n }\n },\n [\n markThreadAsResolved,\n markThreadAsUnresolved,\n onResolvedChange,\n thread.id,\n ]\n );\n\n const handleCommentDelete = useCallback(\n (comment: CommentData) => {\n onCommentDelete?.(comment);\n\n const filteredComments = thread.comments.filter(\n (comment) => comment.body\n );\n\n if (filteredComments.length <= 1) {\n onThreadDelete?.(thread);\n }\n },\n [onCommentDelete, onThreadDelete, thread]\n );\n\n const handleSubscribeChange = useCallback(() => {\n if (subscriptionStatus === \"subscribed\") {\n unsubscribe();\n } else {\n subscribe();\n }\n }, [subscriptionStatus, subscribe, unsubscribe]);\n\n return (\n <TooltipProvider>\n <div\n className={classNames(\n \"lb-root lb-thread\",\n showActions === \"hover\" && \"lb-thread:show-actions-hover\",\n className\n )}\n data-resolved={thread.resolved ? \"\" : undefined}\n data-unread={unreadIndex !== undefined ? \"\" : undefined}\n dir={$.dir}\n {...props}\n ref={forwardedRef}\n >\n <div className=\"lb-thread-comments\">\n {thread.comments.map((comment, index) => {\n const isFirstComment = index === firstCommentIndex;\n const isUnread =\n unreadIndex !== undefined && index >= unreadIndex;\n\n const children = (\n <Comment\n key={comment.id}\n overrides={overrides}\n className=\"lb-thread-comment\"\n data-unread={isUnread ? \"\" : undefined}\n comment={comment}\n indentContent={indentCommentContent}\n showDeleted={showDeletedComments}\n showActions={showActions}\n showReactions={showReactions}\n showAttachments={showAttachments}\n showComposerFormattingControls={\n showComposerFormattingControls\n }\n onCommentEdit={onCommentEdit}\n onCommentDelete={handleCommentDelete}\n onAuthorClick={onAuthorClick}\n onMentionClick={onMentionClick}\n onAttachmentClick={onAttachmentClick}\n autoMarkReadThreadId={\n index === lastCommentIndex && isUnread\n ? thread.id\n : undefined\n }\n additionalActionsClassName={\n isFirstComment ? \"lb-thread-actions\" : undefined\n }\n additionalActions={\n isFirstComment && showResolveAction ? (\n <Tooltip\n content={\n thread.resolved\n ? $.THREAD_UNRESOLVE\n : $.THREAD_RESOLVE\n }\n >\n <TogglePrimitive.Root\n pressed={thread.resolved}\n onPressedChange={handleResolvedChange}\n asChild\n >\n <Button\n className=\"lb-comment-action\"\n onClick={stopPropagation}\n aria-label={\n thread.resolved\n ? $.THREAD_UNRESOLVE\n : $.THREAD_RESOLVE\n }\n icon={\n thread.resolved ? (\n <ResolvedIcon />\n ) : (\n <ResolveIcon />\n )\n }\n />\n </TogglePrimitive.Root>\n </Tooltip>\n ) : null\n }\n additionalDropdownItemsBefore={\n isFirstComment ? (\n <DropdownItem\n onSelect={handleSubscribeChange}\n onClick={stopPropagation}\n icon={\n subscriptionStatus === \"subscribed\" ? (\n <BellCrossedIcon />\n ) : (\n <BellIcon />\n )\n }\n >\n {subscriptionStatus === \"subscribed\"\n ? $.THREAD_UNSUBSCRIBE\n : $.THREAD_SUBSCRIBE}\n </DropdownItem>\n ) : null\n }\n />\n );\n\n return index === newIndicatorIndex &&\n newIndicatorIndex !== firstCommentIndex &&\n newIndicatorIndex <= lastCommentIndex ? (\n <Fragment key={comment.id}>\n <div\n className=\"lb-thread-new-indicator\"\n aria-label={$.THREAD_NEW_INDICATOR_DESCRIPTION}\n >\n <span className=\"lb-thread-new-indicator-label\">\n <ArrowDownIcon className=\"lb-thread-new-indicator-label-icon\" />\n {$.THREAD_NEW_INDICATOR}\n </span>\n </div>\n {children}\n </Fragment>\n ) : (\n children\n );\n })}\n </div>\n {showComposer && (\n <Composer\n className=\"lb-thread-composer\"\n threadId={thread.id}\n defaultCollapsed={showComposer === \"collapsed\" ? true : undefined}\n showAttachments={showAttachments}\n showFormattingControls={showComposerFormattingControls}\n onComposerSubmit={onComposerSubmit}\n overrides={{\n COMPOSER_PLACEHOLDER: $.THREAD_COMPOSER_PLACEHOLDER,\n COMPOSER_SEND: $.THREAD_COMPOSER_SEND,\n ...overrides,\n }}\n roomId={thread.roomId}\n />\n )}\n </div>\n </TooltipProvider>\n );\n }\n) as <M extends BaseMetadata = DM>(\n props: ThreadProps<M> & RefAttributes<HTMLDivElement>\n) => JSX.Element;\n"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;AA8JO;AAAe;AAElB;AACE;AACuB;AACT;AACd;AACoB;AACJ;AACD;AACG;AACe;AACjC;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACG;AAIL;AACA;AACA;AACA;AACE;AAEuD;AAEzD;AACE;AAE4D;AAE9D;AAAM;AACI;AACR;AACA;AACA;AAEF;AAEE;AACE;AAAA;AAIF;AACE;AAAO;AAIT;AAAoC;AAGZ;AAGxB;AAEI;AACH;AACD;AACA;AACA;AACO;AACP;AAEF;AACA;AAEA;AACE;AAEE;AAAA;AACwD;AACxD;AACF;AAGF;AACE;AAAsB;AAGxB;AAA6B;AAEzB;AAEA;AACE;AAA8B;AAE9B;AAAgC;AAClC;AACF;AACA;AACE;AACA;AACA;AACO;AACT;AAGF;AAA4B;AAExB;AAEA;AAAyC;AAClB;AAGvB;AACE;AAAuB;AACzB;AACF;AACwC;AAG1C;AACE;AACE;AAAY;AAEZ;AAAU;AACZ;AAGF;AACG;AACE;AACY;AACT;AAC2B;AAC3B;AACF;AACsC;AACQ;AACvC;AACH;AACC;AAEL;AAAC;AAAc;AAEX;AACA;AAGA;AACG;AAEC;AACU;AACmB;AAC7B;AACe;AACF;AACb;AACA;AACA;AACA;AAGA;AACiB;AACjB;AACA;AACA;AAIM;AAGmC;AAIpC;AAIS;AAGP;AACiB;AACC;AACV;AAEN;AACW;AACD;AAID;AAMS;AAGnB;AACF;AAEA;AAID;AACW;AACD;AAKK;AAMR;AAEN;AAKV;AAGG;AACC;AAAC;AACW;AACI;AAEb;AAAe;AACd;AAAC;AAAwB;AAAqC;AAC3D;AAAA;AACL;AACF;AACC;AAAA;AAGH;AAEH;AACH;AAEG;AACW;AACO;AACuC;AACxD;AACwB;AACxB;AACW;AACe;AACP;AACd;AACL;AACe;AACjB;AAAA;AAEJ;AACF;AAGN;;"}
1
+ {"version":3,"file":"Thread.js","sources":["../../src/components/Thread.tsx"],"sourcesContent":["\"use client\";\n\nimport type {\n BaseMetadata,\n CommentData,\n DM,\n ThreadData,\n} from \"@liveblocks/core\";\nimport {\n useMarkRoomThreadAsResolved,\n useMarkRoomThreadAsUnresolved,\n useRoomThreadSubscription,\n} from \"@liveblocks/react/_private\";\nimport * as TogglePrimitive from \"@radix-ui/react-toggle\";\nimport type {\n ComponentPropsWithoutRef,\n ForwardedRef,\n RefAttributes,\n SyntheticEvent,\n} from \"react\";\nimport {\n forwardRef,\n Fragment,\n useCallback,\n useEffect,\n useMemo,\n useState,\n} from \"react\";\n\nimport { ArrowDownIcon } from \"../icons/ArrowDown\";\nimport { BellIcon } from \"../icons/Bell\";\nimport { BellCrossedIcon } from \"../icons/BellCrossed\";\nimport { CheckCircleIcon } from \"../icons/CheckCircle\";\nimport { CheckCircleFillIcon } from \"../icons/CheckCircleFill\";\nimport type {\n CommentOverrides,\n ComposerOverrides,\n GlobalOverrides,\n ThreadOverrides,\n} from \"../overrides\";\nimport { useOverrides } from \"../overrides\";\nimport { classNames } from \"../utils/class-names\";\nimport { findLastIndex } from \"../utils/find-last-index\";\nimport type { CommentProps } from \"./Comment\";\nimport { Comment } from \"./Comment\";\nimport type { ComposerProps } from \"./Composer\";\nimport { Composer } from \"./Composer\";\nimport { Button } from \"./internal/Button\";\nimport { DropdownItem } from \"./internal/Dropdown\";\nimport { Tooltip, TooltipProvider } from \"./internal/Tooltip\";\n\nexport interface ThreadProps<M extends BaseMetadata = DM>\n extends ComponentPropsWithoutRef<\"div\"> {\n /**\n * The thread to display.\n */\n thread: ThreadData<M>;\n\n /**\n * How to show or hide the composer to reply to the thread.\n */\n showComposer?: boolean | \"collapsed\";\n\n /**\n * Whether to show the action to resolve the thread.\n */\n showResolveAction?: boolean;\n\n /**\n * How to show or hide the actions.\n */\n showActions?: CommentProps[\"showActions\"];\n\n /**\n * Whether to show reactions.\n */\n showReactions?: CommentProps[\"showReactions\"];\n\n /**\n * Whether to show the composer's formatting controls.\n */\n showComposerFormattingControls?: ComposerProps[\"showFormattingControls\"];\n\n /**\n * Whether to indent the comments' content.\n */\n indentCommentContent?: CommentProps[\"indentContent\"];\n\n /**\n * Whether to show deleted comments.\n */\n showDeletedComments?: CommentProps[\"showDeleted\"];\n\n /**\n * Whether to show attachments.\n */\n showAttachments?: boolean;\n\n /**\n * The event handler called when changing the resolved status.\n */\n onResolvedChange?: (resolved: boolean) => void;\n\n /**\n * The event handler called when a comment is edited.\n */\n onCommentEdit?: CommentProps[\"onCommentEdit\"];\n\n /**\n * The event handler called when a comment is deleted.\n */\n onCommentDelete?: CommentProps[\"onCommentDelete\"];\n\n /**\n * The event handler called when the thread is deleted.\n * A thread is deleted when all its comments are deleted.\n */\n onThreadDelete?: (thread: ThreadData<M>) => void;\n\n /**\n * The event handler called when clicking on a comment's author.\n */\n onAuthorClick?: CommentProps[\"onAuthorClick\"];\n\n /**\n * The event handler called when clicking on a mention.\n */\n onMentionClick?: CommentProps[\"onMentionClick\"];\n\n /**\n * The event handler called when clicking on a comment's attachment.\n */\n onAttachmentClick?: CommentProps[\"onAttachmentClick\"];\n\n /**\n * The event handler called when the composer is submitted.\n */\n onComposerSubmit?: ComposerProps[\"onComposerSubmit\"];\n\n /**\n * Override the component's strings.\n */\n overrides?: Partial<\n GlobalOverrides & ThreadOverrides & CommentOverrides & ComposerOverrides\n >;\n}\n\n/**\n * Displays a thread of comments, with a composer to reply\n * to it.\n *\n * @example\n * <>\n * {threads.map((thread) => (\n * <Thread key={thread.id} thread={thread} />\n * ))}\n * </>\n */\nexport const Thread = forwardRef(\n <M extends BaseMetadata = DM>(\n {\n thread,\n indentCommentContent = true,\n showActions = \"hover\",\n showDeletedComments,\n showResolveAction = true,\n showReactions = true,\n showComposer = \"collapsed\",\n showAttachments = true,\n showComposerFormattingControls = true,\n onResolvedChange,\n onCommentEdit,\n onCommentDelete,\n onThreadDelete,\n onAuthorClick,\n onMentionClick,\n onAttachmentClick,\n onComposerSubmit,\n overrides,\n className,\n ...props\n }: ThreadProps<M>,\n forwardedRef: ForwardedRef<HTMLDivElement>\n ) => {\n const markThreadAsResolved = useMarkRoomThreadAsResolved(thread.roomId);\n const markThreadAsUnresolved = useMarkRoomThreadAsUnresolved(thread.roomId);\n const $ = useOverrides(overrides);\n const firstCommentIndex = useMemo(() => {\n return showDeletedComments\n ? 0\n : thread.comments.findIndex((comment) => comment.body);\n }, [showDeletedComments, thread.comments]);\n const lastCommentIndex = useMemo(() => {\n return showDeletedComments\n ? thread.comments.length - 1\n : findLastIndex(thread.comments, (comment) => comment.body);\n }, [showDeletedComments, thread.comments]);\n const {\n status: subscriptionStatus,\n unreadSince,\n subscribe,\n unsubscribe,\n } = useRoomThreadSubscription(thread.roomId, thread.id);\n const unreadIndex = useMemo(() => {\n // The user is not subscribed to this thread.\n if (subscriptionStatus !== \"subscribed\") {\n return;\n }\n\n // The user hasn't read the thread yet, so all comments are unread.\n if (unreadSince === null) {\n return firstCommentIndex;\n }\n\n // The user has read the thread, so we find the first unread comment.\n const unreadIndex = thread.comments.findIndex(\n (comment) =>\n (showDeletedComments ? true : comment.body) &&\n comment.createdAt > unreadSince\n );\n\n return unreadIndex >= 0 && unreadIndex < thread.comments.length\n ? unreadIndex\n : undefined;\n }, [\n firstCommentIndex,\n showDeletedComments,\n subscriptionStatus,\n thread.comments,\n unreadSince,\n ]);\n const [newIndex, setNewIndex] = useState<number>();\n const newIndicatorIndex = newIndex === undefined ? unreadIndex : newIndex;\n\n useEffect(() => {\n if (unreadIndex) {\n // Keep the \"new\" indicator at the lowest unread index.\n setNewIndex((persistedUnreadIndex) =>\n Math.min(persistedUnreadIndex ?? Infinity, unreadIndex)\n );\n }\n }, [unreadIndex]);\n\n const stopPropagation = useCallback((event: SyntheticEvent) => {\n event.stopPropagation();\n }, []);\n\n const handleResolvedChange = useCallback(\n (resolved: boolean) => {\n onResolvedChange?.(resolved);\n\n if (resolved) {\n markThreadAsResolved(thread.id);\n } else {\n markThreadAsUnresolved(thread.id);\n }\n },\n [\n markThreadAsResolved,\n markThreadAsUnresolved,\n onResolvedChange,\n thread.id,\n ]\n );\n\n const handleCommentDelete = useCallback(\n (comment: CommentData) => {\n onCommentDelete?.(comment);\n\n const filteredComments = thread.comments.filter(\n (comment) => comment.body\n );\n\n if (filteredComments.length <= 1) {\n onThreadDelete?.(thread);\n }\n },\n [onCommentDelete, onThreadDelete, thread]\n );\n\n const handleSubscribeChange = useCallback(() => {\n if (subscriptionStatus === \"subscribed\") {\n unsubscribe();\n } else {\n subscribe();\n }\n }, [subscriptionStatus, subscribe, unsubscribe]);\n\n return (\n <TooltipProvider>\n <div\n className={classNames(\n \"lb-root lb-thread\",\n showActions === \"hover\" && \"lb-thread:show-actions-hover\",\n className\n )}\n data-resolved={thread.resolved ? \"\" : undefined}\n data-unread={unreadIndex !== undefined ? \"\" : undefined}\n dir={$.dir}\n {...props}\n ref={forwardedRef}\n >\n <div className=\"lb-thread-comments\">\n {thread.comments.map((comment, index) => {\n const isFirstComment = index === firstCommentIndex;\n const isUnread =\n unreadIndex !== undefined && index >= unreadIndex;\n\n const children = (\n <Comment\n key={comment.id}\n overrides={overrides}\n className=\"lb-thread-comment\"\n data-unread={isUnread ? \"\" : undefined}\n comment={comment}\n indentContent={indentCommentContent}\n showDeleted={showDeletedComments}\n showActions={showActions}\n showReactions={showReactions}\n showAttachments={showAttachments}\n showComposerFormattingControls={\n showComposerFormattingControls\n }\n onCommentEdit={onCommentEdit}\n onCommentDelete={handleCommentDelete}\n onAuthorClick={onAuthorClick}\n onMentionClick={onMentionClick}\n onAttachmentClick={onAttachmentClick}\n autoMarkReadThreadId={\n index === lastCommentIndex && isUnread\n ? thread.id\n : undefined\n }\n additionalActionsClassName={\n isFirstComment ? \"lb-thread-actions\" : undefined\n }\n additionalActions={\n isFirstComment && showResolveAction ? (\n <Tooltip\n content={\n thread.resolved\n ? $.THREAD_UNRESOLVE\n : $.THREAD_RESOLVE\n }\n >\n <TogglePrimitive.Root\n pressed={thread.resolved}\n onPressedChange={handleResolvedChange}\n asChild\n >\n <Button\n className=\"lb-comment-action\"\n onClick={stopPropagation}\n aria-label={\n thread.resolved\n ? $.THREAD_UNRESOLVE\n : $.THREAD_RESOLVE\n }\n icon={\n thread.resolved ? (\n <CheckCircleFillIcon />\n ) : (\n <CheckCircleIcon />\n )\n }\n />\n </TogglePrimitive.Root>\n </Tooltip>\n ) : null\n }\n additionalDropdownItemsBefore={\n isFirstComment ? (\n <DropdownItem\n onSelect={handleSubscribeChange}\n onClick={stopPropagation}\n icon={\n subscriptionStatus === \"subscribed\" ? (\n <BellCrossedIcon />\n ) : (\n <BellIcon />\n )\n }\n >\n {subscriptionStatus === \"subscribed\"\n ? $.THREAD_UNSUBSCRIBE\n : $.THREAD_SUBSCRIBE}\n </DropdownItem>\n ) : null\n }\n />\n );\n\n return index === newIndicatorIndex &&\n newIndicatorIndex !== firstCommentIndex &&\n newIndicatorIndex <= lastCommentIndex ? (\n <Fragment key={comment.id}>\n <div\n className=\"lb-thread-new-indicator\"\n aria-label={$.THREAD_NEW_INDICATOR_DESCRIPTION}\n >\n <span className=\"lb-thread-new-indicator-label\">\n <ArrowDownIcon className=\"lb-thread-new-indicator-label-icon\" />\n {$.THREAD_NEW_INDICATOR}\n </span>\n </div>\n {children}\n </Fragment>\n ) : (\n children\n );\n })}\n </div>\n {showComposer && (\n <Composer\n className=\"lb-thread-composer\"\n threadId={thread.id}\n defaultCollapsed={showComposer === \"collapsed\" ? true : undefined}\n showAttachments={showAttachments}\n showFormattingControls={showComposerFormattingControls}\n onComposerSubmit={onComposerSubmit}\n overrides={{\n COMPOSER_PLACEHOLDER: $.THREAD_COMPOSER_PLACEHOLDER,\n COMPOSER_SEND: $.THREAD_COMPOSER_SEND,\n ...overrides,\n }}\n roomId={thread.roomId}\n />\n )}\n </div>\n </TooltipProvider>\n );\n }\n) as <M extends BaseMetadata = DM>(\n props: ThreadProps<M> & RefAttributes<HTMLDivElement>\n) => JSX.Element;\n"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;AA8JO;AAAe;AAElB;AACE;AACuB;AACT;AACd;AACoB;AACJ;AACD;AACG;AACe;AACjC;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACG;AAIL;AACA;AACA;AACA;AACE;AAEuD;AAEzD;AACE;AAE4D;AAE9D;AAAM;AACI;AACR;AACA;AACA;AAEF;AAEE;AACE;AAAA;AAIF;AACE;AAAO;AAIT;AAAoC;AAGZ;AAGxB;AAEI;AACH;AACD;AACA;AACA;AACO;AACP;AAEF;AACA;AAEA;AACE;AAEE;AAAA;AACwD;AACxD;AACF;AAGF;AACE;AAAsB;AAGxB;AAA6B;AAEzB;AAEA;AACE;AAA8B;AAE9B;AAAgC;AAClC;AACF;AACA;AACE;AACA;AACA;AACO;AACT;AAGF;AAA4B;AAExB;AAEA;AAAyC;AAClB;AAGvB;AACE;AAAuB;AACzB;AACF;AACwC;AAG1C;AACE;AACE;AAAY;AAEZ;AAAU;AACZ;AAGF;AACG;AACE;AACY;AACT;AAC2B;AAC3B;AACF;AACsC;AACQ;AACvC;AACH;AACC;AAEL;AAAC;AAAc;AAEX;AACA;AAGA;AACG;AAEC;AACU;AACmB;AAC7B;AACe;AACF;AACb;AACA;AACA;AACA;AAGA;AACiB;AACjB;AACA;AACA;AAIM;AAGmC;AAIpC;AAIS;AAGP;AACiB;AACC;AACV;AAEN;AACW;AACD;AAID;AAMa;AAGvB;AACF;AAEA;AAID;AACW;AACD;AAKK;AAMR;AAEN;AAKV;AAGG;AACC;AAAC;AACW;AACI;AAEb;AAAe;AACd;AAAC;AAAwB;AAAqC;AAC3D;AAAA;AACL;AACF;AACC;AAAA;AAGH;AAEH;AACH;AAEG;AACW;AACO;AACuC;AACxD;AACwB;AACxB;AACW;AACe;AACP;AACd;AACL;AACe;AACjB;AAAA;AAEJ;AACF;AAGN;;"}