@cossistant/react 0.0.3 → 0.0.5

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 (320) hide show
  1. package/README.md +74 -0
  2. package/_virtual/rolldown_runtime.js +19 -0
  3. package/conversation.d.ts +26 -3
  4. package/conversation.d.ts.map +1 -1
  5. package/hooks/index.d.ts +5 -3
  6. package/hooks/index.js +7 -5
  7. package/hooks/private/store/use-conversations-store.d.ts +8 -0
  8. package/hooks/private/store/use-conversations-store.d.ts.map +1 -1
  9. package/hooks/private/store/use-conversations-store.js +8 -0
  10. package/hooks/private/store/use-conversations-store.js.map +1 -1
  11. package/hooks/private/store/use-store-selector.d.ts +4 -0
  12. package/hooks/private/store/use-store-selector.d.ts.map +1 -1
  13. package/hooks/private/store/use-store-selector.js +5 -2
  14. package/hooks/private/store/use-store-selector.js.map +1 -1
  15. package/hooks/private/store/use-website-store.d.ts +4 -0
  16. package/hooks/private/store/use-website-store.d.ts.map +1 -1
  17. package/hooks/private/store/use-website-store.js +6 -3
  18. package/hooks/private/store/use-website-store.js.map +1 -1
  19. package/hooks/private/typing.d.ts +35 -0
  20. package/hooks/private/typing.d.ts.map +1 -0
  21. package/hooks/private/typing.js +49 -0
  22. package/hooks/private/typing.js.map +1 -0
  23. package/hooks/private/use-client-query.d.ts +5 -0
  24. package/hooks/private/use-client-query.d.ts.map +1 -1
  25. package/hooks/private/use-client-query.js +5 -0
  26. package/hooks/private/use-client-query.js.map +1 -1
  27. package/hooks/private/use-grouped-messages.d.ts +10 -4
  28. package/hooks/private/use-grouped-messages.d.ts.map +1 -1
  29. package/hooks/private/use-grouped-messages.js +24 -4
  30. package/hooks/private/use-grouped-messages.js.map +1 -1
  31. package/hooks/private/use-multimodal-input.d.ts.map +1 -1
  32. package/hooks/private/use-rest-client.d.ts.map +1 -1
  33. package/hooks/private/use-visitor-typing-reporter.d.ts +6 -0
  34. package/hooks/private/use-visitor-typing-reporter.d.ts.map +1 -1
  35. package/hooks/private/use-visitor-typing-reporter.js +6 -0
  36. package/hooks/private/use-visitor-typing-reporter.js.map +1 -1
  37. package/hooks/use-composer-refocus.d.ts.map +1 -1
  38. package/hooks/use-conversation-auto-seen.d.ts +9 -0
  39. package/hooks/use-conversation-auto-seen.d.ts.map +1 -1
  40. package/hooks/use-conversation-auto-seen.js +44 -3
  41. package/hooks/use-conversation-auto-seen.js.map +1 -1
  42. package/hooks/use-conversation-history-page.d.ts.map +1 -1
  43. package/hooks/use-conversation-history-page.js +16 -18
  44. package/hooks/use-conversation-history-page.js.map +1 -1
  45. package/hooks/use-conversation-lifecycle.d.ts.map +1 -1
  46. package/hooks/use-conversation-lifecycle.js +2 -4
  47. package/hooks/use-conversation-lifecycle.js.map +1 -1
  48. package/hooks/use-conversation-page.d.ts +6 -0
  49. package/hooks/use-conversation-page.d.ts.map +1 -1
  50. package/hooks/use-conversation-page.js +41 -3
  51. package/hooks/use-conversation-page.js.map +1 -1
  52. package/hooks/use-conversation-preview.d.ts +61 -0
  53. package/hooks/use-conversation-preview.d.ts.map +1 -0
  54. package/hooks/use-conversation-preview.js +173 -0
  55. package/hooks/use-conversation-preview.js.map +1 -0
  56. package/hooks/use-conversation-seen.d.ts +4 -0
  57. package/hooks/use-conversation-seen.d.ts.map +1 -1
  58. package/hooks/use-conversation-seen.js +4 -0
  59. package/hooks/use-conversation-seen.js.map +1 -1
  60. package/hooks/use-conversation-timeline-items.d.ts +4 -0
  61. package/hooks/use-conversation-timeline-items.d.ts.map +1 -1
  62. package/hooks/use-conversation-timeline-items.js +4 -0
  63. package/hooks/use-conversation-timeline-items.js.map +1 -1
  64. package/hooks/use-conversation-timeline.d.ts +32 -0
  65. package/hooks/use-conversation-timeline.d.ts.map +1 -0
  66. package/hooks/use-conversation-timeline.js +41 -0
  67. package/hooks/use-conversation-timeline.js.map +1 -0
  68. package/hooks/use-conversation-typing.d.ts +4 -0
  69. package/hooks/use-conversation-typing.d.ts.map +1 -1
  70. package/hooks/use-conversation-typing.js +4 -0
  71. package/hooks/use-conversation-typing.js.map +1 -1
  72. package/hooks/use-conversation.d.ts +11 -0
  73. package/hooks/use-conversation.d.ts.map +1 -1
  74. package/hooks/use-conversation.js +11 -0
  75. package/hooks/use-conversation.js.map +1 -1
  76. package/hooks/use-conversations.d.ts +12 -0
  77. package/hooks/use-conversations.d.ts.map +1 -1
  78. package/hooks/use-conversations.js +12 -0
  79. package/hooks/use-conversations.js.map +1 -1
  80. package/hooks/use-create-conversation.d.ts +5 -0
  81. package/hooks/use-create-conversation.d.ts.map +1 -1
  82. package/hooks/use-create-conversation.js +12 -9
  83. package/hooks/use-create-conversation.js.map +1 -1
  84. package/hooks/use-home-page.d.ts.map +1 -1
  85. package/hooks/use-home-page.js +6 -4
  86. package/hooks/use-home-page.js.map +1 -1
  87. package/hooks/use-message-composer.d.ts.map +1 -1
  88. package/hooks/use-realtime-support.d.ts.map +1 -1
  89. package/hooks/use-send-message.d.ts +9 -0
  90. package/hooks/use-send-message.d.ts.map +1 -1
  91. package/hooks/use-send-message.js +15 -13
  92. package/hooks/use-send-message.js.map +1 -1
  93. package/hooks/use-visitor.d.ts.map +1 -1
  94. package/hooks/use-visitor.js +28 -30
  95. package/hooks/use-visitor.js.map +1 -1
  96. package/hooks/use-window-visibility-focus.d.ts +4 -0
  97. package/hooks/use-window-visibility-focus.d.ts.map +1 -1
  98. package/hooks/use-window-visibility-focus.js +5 -2
  99. package/hooks/use-window-visibility-focus.js.map +1 -1
  100. package/identify-visitor.d.ts +12 -3
  101. package/identify-visitor.d.ts.map +1 -1
  102. package/identify-visitor.js +58 -9
  103. package/identify-visitor.js.map +1 -1
  104. package/index.d.ts +10 -7
  105. package/index.js +10 -9
  106. package/package.json +14 -17
  107. package/primitives/avatar/avatar.d.ts.map +1 -1
  108. package/primitives/avatar/fallback.d.ts.map +1 -1
  109. package/primitives/avatar/fallback.js +1 -3
  110. package/primitives/avatar/fallback.js.map +1 -1
  111. package/primitives/avatar/image.d.ts.map +1 -1
  112. package/primitives/avatar/index.d.ts +1 -0
  113. package/primitives/bubble.d.ts +2 -0
  114. package/primitives/bubble.d.ts.map +1 -1
  115. package/primitives/bubble.js +8 -2
  116. package/primitives/bubble.js.map +1 -1
  117. package/primitives/button.d.ts.map +1 -1
  118. package/primitives/conversation-timeline.d.ts.map +1 -1
  119. package/primitives/conversation-timeline.js +58 -5
  120. package/primitives/conversation-timeline.js.map +1 -1
  121. package/primitives/index.d.ts +1 -0
  122. package/primitives/index.parts.d.ts +1 -0
  123. package/primitives/multimodal-input.d.ts.map +1 -1
  124. package/primitives/timeline-item-group.d.ts +7 -7
  125. package/primitives/timeline-item-group.d.ts.map +1 -1
  126. package/primitives/timeline-item-group.js.map +1 -1
  127. package/primitives/timeline-item.d.ts +1 -1
  128. package/primitives/timeline-item.d.ts.map +1 -1
  129. package/primitives/timeline-item.js +7 -1
  130. package/primitives/timeline-item.js.map +1 -1
  131. package/primitives/window.d.ts +1 -1
  132. package/primitives/window.d.ts.map +1 -1
  133. package/primitives/window.js +4 -4
  134. package/primitives/window.js.map +1 -1
  135. package/provider.d.ts +23 -43
  136. package/provider.d.ts.map +1 -1
  137. package/provider.js +152 -49
  138. package/provider.js.map +1 -1
  139. package/realtime/event-filter.d.ts +4 -0
  140. package/realtime/event-filter.d.ts.map +1 -1
  141. package/realtime/event-filter.js +4 -0
  142. package/realtime/event-filter.js.map +1 -1
  143. package/realtime/index.js +1 -1
  144. package/realtime/provider.d.ts +7 -2
  145. package/realtime/provider.d.ts.map +1 -1
  146. package/realtime/provider.js +23 -1
  147. package/realtime/provider.js.map +1 -1
  148. package/realtime/seen-store.d.ts +13 -0
  149. package/realtime/seen-store.d.ts.map +1 -1
  150. package/realtime/seen-store.js +14 -2
  151. package/realtime/seen-store.js.map +1 -1
  152. package/realtime/support-provider.d.ts +1 -2
  153. package/realtime/support-provider.d.ts.map +1 -1
  154. package/realtime/support-provider.js +19 -20
  155. package/realtime/support-provider.js.map +1 -1
  156. package/realtime/typing-store.d.ts +18 -0
  157. package/realtime/typing-store.d.ts.map +1 -1
  158. package/realtime/typing-store.js +19 -2
  159. package/realtime/typing-store.js.map +1 -1
  160. package/realtime/use-realtime.d.ts +8 -4
  161. package/realtime/use-realtime.d.ts.map +1 -1
  162. package/realtime/use-realtime.js +4 -0
  163. package/realtime/use-realtime.js.map +1 -1
  164. package/realtime-events.d.ts +17 -3
  165. package/realtime-events.d.ts.map +1 -1
  166. package/schemas.d.ts +7 -1
  167. package/schemas.d.ts.map +1 -1
  168. package/support/components/avatar-stack.d.ts +8 -4
  169. package/support/components/avatar-stack.d.ts.map +1 -1
  170. package/support/components/avatar-stack.js +4 -0
  171. package/support/components/avatar-stack.js.map +1 -1
  172. package/support/components/avatar.d.ts +11 -6
  173. package/support/components/avatar.d.ts.map +1 -1
  174. package/support/components/avatar.js +4 -0
  175. package/support/components/avatar.js.map +1 -1
  176. package/support/components/bubble.d.ts.map +1 -1
  177. package/support/components/bubble.js +29 -6
  178. package/support/components/bubble.js.map +1 -1
  179. package/support/components/button.d.ts +8 -5
  180. package/support/components/button.d.ts.map +1 -1
  181. package/support/components/button.js +5 -1
  182. package/support/components/button.js.map +1 -1
  183. package/support/components/container.d.ts +0 -1
  184. package/support/components/container.d.ts.map +1 -1
  185. package/support/components/container.js +2 -8
  186. package/support/components/container.js.map +1 -1
  187. package/support/components/conversation-button-link.d.ts +8 -21
  188. package/support/components/conversation-button-link.d.ts.map +1 -1
  189. package/support/components/conversation-button-link.js +62 -178
  190. package/support/components/conversation-button-link.js.map +1 -1
  191. package/support/components/conversation-event.d.ts.map +1 -1
  192. package/support/components/conversation-event.js +4 -0
  193. package/support/components/conversation-event.js.map +1 -1
  194. package/support/components/conversation-timeline.d.ts +10 -1
  195. package/support/components/conversation-timeline.d.ts.map +1 -1
  196. package/support/components/conversation-timeline.js +63 -57
  197. package/support/components/conversation-timeline.js.map +1 -1
  198. package/support/components/cossistant-branding.d.ts +5 -2
  199. package/support/components/cossistant-branding.d.ts.map +1 -1
  200. package/support/components/cossistant-branding.js +3 -0
  201. package/support/components/cossistant-branding.js.map +1 -1
  202. package/support/components/header.d.ts.map +1 -1
  203. package/support/components/header.js +2 -2
  204. package/support/components/header.js.map +1 -1
  205. package/support/components/icons.d.ts.map +1 -1
  206. package/support/components/multimodal-input.d.ts.map +1 -1
  207. package/support/components/multimodal-input.js +5 -24
  208. package/support/components/multimodal-input.js.map +1 -1
  209. package/support/components/navigation-tab.d.ts +7 -2
  210. package/support/components/navigation-tab.d.ts.map +1 -1
  211. package/support/components/navigation-tab.js +4 -0
  212. package/support/components/navigation-tab.js.map +1 -1
  213. package/support/components/support-content.d.ts +1 -1
  214. package/support/components/support-content.d.ts.map +1 -1
  215. package/support/components/support-content.js +7 -10
  216. package/support/components/support-content.js.map +1 -1
  217. package/support/components/text-effect.d.ts +5 -2
  218. package/support/components/text-effect.d.ts.map +1 -1
  219. package/support/components/text-effect.js +4 -0
  220. package/support/components/text-effect.js.map +1 -1
  221. package/support/components/timeline-identification-tool.d.ts +7 -0
  222. package/support/components/timeline-identification-tool.d.ts.map +1 -0
  223. package/support/components/timeline-identification-tool.js +139 -0
  224. package/support/components/timeline-identification-tool.js.map +1 -0
  225. package/support/components/timeline-message-group.d.ts +2 -1
  226. package/support/components/timeline-message-group.d.ts.map +1 -1
  227. package/support/components/timeline-message-group.js +4 -19
  228. package/support/components/timeline-message-group.js.map +1 -1
  229. package/support/components/timeline-message-item.d.ts +6 -2
  230. package/support/components/timeline-message-item.d.ts.map +1 -1
  231. package/support/components/timeline-message-item.js +8 -4
  232. package/support/components/timeline-message-item.js.map +1 -1
  233. package/support/components/typing-indicator.d.ts +5 -2
  234. package/support/components/typing-indicator.d.ts.map +1 -1
  235. package/support/components/typing-indicator.js +4 -4
  236. package/support/components/typing-indicator.js.map +1 -1
  237. package/support/components/watermark.d.ts.map +1 -1
  238. package/support/context/websocket.d.ts +8 -0
  239. package/support/context/websocket.d.ts.map +1 -1
  240. package/support/context/websocket.js +12 -6
  241. package/support/context/websocket.js.map +1 -1
  242. package/support/index.d.ts +8 -8
  243. package/support/index.d.ts.map +1 -1
  244. package/support/index.js +18 -18
  245. package/support/index.js.map +1 -1
  246. package/support/pages/conversation-history.js +46 -54
  247. package/support/pages/conversation-history.js.map +1 -1
  248. package/support/pages/conversation.d.ts +3 -6
  249. package/support/pages/conversation.d.ts.map +1 -1
  250. package/support/pages/conversation.js +19 -9
  251. package/support/pages/conversation.js.map +1 -1
  252. package/support/pages/home.d.ts +2 -2
  253. package/support/pages/home.d.ts.map +1 -1
  254. package/support/pages/home.js +64 -77
  255. package/support/pages/home.js.map +1 -1
  256. package/support/store/support-store.d.ts +18 -2
  257. package/support/store/support-store.d.ts.map +1 -1
  258. package/support/store/support-store.js +20 -5
  259. package/support/store/support-store.js.map +1 -1
  260. package/support/{support-CMoDLQoC.css → support-Ck4jy29i.css} +1 -2
  261. package/support/support-Ck4jy29i.css.map +1 -0
  262. package/support/text/index.d.ts +15 -2
  263. package/support/text/index.d.ts.map +1 -1
  264. package/support/text/index.js +15 -2
  265. package/support/text/index.js.map +1 -1
  266. package/support/text/locales/en.js +22 -4
  267. package/support/text/locales/en.js.map +1 -1
  268. package/support/text/locales/es.js +18 -0
  269. package/support/text/locales/es.js.map +1 -1
  270. package/support/text/locales/fr.js +18 -0
  271. package/support/text/locales/fr.js.map +1 -1
  272. package/support/text/locales/keys.d.ts +69 -9
  273. package/support/text/locales/keys.d.ts.map +1 -1
  274. package/support/text/locales/keys.js +18 -0
  275. package/support/text/locales/keys.js.map +1 -1
  276. package/support/text/runtime.d.ts +21 -0
  277. package/support/text/runtime.d.ts.map +1 -1
  278. package/support/text/runtime.js +21 -0
  279. package/support/text/runtime.js.map +1 -1
  280. package/support/utils/index.d.ts +4 -0
  281. package/support/utils/index.d.ts.map +1 -1
  282. package/support/utils/index.js +4 -1
  283. package/support/utils/index.js.map +1 -1
  284. package/support/utils/time.d.ts +3 -0
  285. package/support/utils/time.d.ts.map +1 -1
  286. package/support/utils/time.js +3 -0
  287. package/support/utils/time.js.map +1 -1
  288. package/support-config.d.ts +2 -1
  289. package/support-config.d.ts.map +1 -1
  290. package/support-config.js.map +1 -1
  291. package/support.css +2 -2
  292. package/timeline-item.d.ts +10 -0
  293. package/timeline-item.d.ts.map +1 -1
  294. package/utils/conversation.d.ts +7 -0
  295. package/utils/conversation.d.ts.map +1 -0
  296. package/utils/conversation.js +18 -0
  297. package/utils/conversation.js.map +1 -0
  298. package/utils/id.d.ts +3 -0
  299. package/utils/id.d.ts.map +1 -1
  300. package/utils/id.js +3 -0
  301. package/utils/id.js.map +1 -1
  302. package/utils/index.d.ts +2 -1
  303. package/utils/index.js +2 -1
  304. package/utils/metadata-hash.d.ts +12 -0
  305. package/utils/metadata-hash.d.ts.map +1 -0
  306. package/utils/metadata-hash.js +26 -0
  307. package/utils/metadata-hash.js.map +1 -0
  308. package/utils/text.d.ts +3 -0
  309. package/utils/text.d.ts.map +1 -1
  310. package/utils/text.js +3 -0
  311. package/utils/text.js.map +1 -1
  312. package/utils/use-render-element.d.ts +3 -0
  313. package/utils/use-render-element.d.ts.map +1 -1
  314. package/utils/use-render-element.js +3 -0
  315. package/utils/use-render-element.js.map +1 -1
  316. package/support/context/config.d.ts +0 -32
  317. package/support/context/config.d.ts.map +0 -1
  318. package/support/context/config.js +0 -27
  319. package/support/context/config.js.map +0 -1
  320. package/support/support-CMoDLQoC.css.map +0 -1
@@ -1,192 +1,76 @@
1
1
  import { cn } from "../utils/index.js";
2
- import { useRenderElement } from "../../utils/use-render-element.js";
3
2
  import { Avatar } from "./avatar.js";
4
3
  import { BouncingDots } from "./typing-indicator.js";
5
4
  import { coButtonVariants } from "./button.js";
6
5
  import icons_default from "./icons.js";
7
6
  import { useSupportText } from "../text/index.js";
8
- import { useConversationTimelineItems } from "../../hooks/use-conversation-timeline-items.js";
9
- import { formatTimeAgo } from "../utils/time.js";
10
- import { useSupport } from "../../provider.js";
11
- import { useConversationTyping } from "../../hooks/use-conversation-typing.js";
12
- import { useMemo } from "react";
7
+ import { useConversationPreview } from "../../hooks/use-conversation-preview.js";
13
8
  import { ConversationStatus } from "@cossistant/types";
14
9
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
15
10
 
16
11
  //#region src/support/components/conversation-button-link.tsx
17
- function getLastTimelineItemInfo(item, availableHumanAgents, website, text) {
18
- const isFromVisitor = item.visitorId !== null;
19
- let senderName = text("common.fallbacks.unknown");
20
- let senderImage = null;
21
- if (isFromVisitor) senderName = text("common.fallbacks.you");
22
- else if (item.userId) {
23
- const agent = availableHumanAgents.find((a) => a.id === item.userId);
24
- if (agent) {
25
- senderName = agent.name;
26
- senderImage = agent.image;
27
- } else senderName = text("common.fallbacks.supportTeam");
28
- } else if (item.aiAgentId && website?.availableAIAgents) {
29
- const aiAgent = website.availableAIAgents.find((a) => a.id === item.aiAgentId);
30
- if (aiAgent) {
31
- senderName = aiAgent.name;
32
- senderImage = aiAgent.image;
33
- } else senderName = text("common.fallbacks.aiAssistant");
34
- } else senderName = text("common.fallbacks.supportTeam");
35
- return {
36
- content: item.text || "",
37
- time: formatTimeAgo(item.createdAt),
38
- isFromVisitor,
39
- senderName,
40
- senderImage
41
- };
42
- }
43
- function ConversationButtonLink({ conversation, onClick,...props }) {
44
- const { availableHumanAgents, availableAIAgents, website, visitor } = useSupport();
45
- const { items } = useConversationTimelineItems(conversation.id);
12
+ const STATUS_BADGE_CLASSNAMES = {
13
+ [ConversationStatus.OPEN]: "bg-co-success/20 text-co-success-foreground",
14
+ [ConversationStatus.RESOLVED]: "bg-co-neutral/20 text-co-neutral-foreground",
15
+ [ConversationStatus.SPAM]: "bg-co-warning/20 text-co-warning-foreground"
16
+ };
17
+ const DEFAULT_STATUS_BADGE_CLASSNAME = "bg-co-neutral/20 text-co-neutral-foreground";
18
+ const STATUS_TEXT_KEYS = {
19
+ [ConversationStatus.OPEN]: "component.conversationButtonLink.status.open",
20
+ [ConversationStatus.RESOLVED]: "component.conversationButtonLink.status.resolved",
21
+ [ConversationStatus.SPAM]: "component.conversationButtonLink.status.spam"
22
+ };
23
+ /**
24
+ * Renders a navigable preview card for a conversation including assigned agent
25
+ * details, last message snippets and typing indicators.
26
+ */
27
+ function ConversationButtonLink({ conversation, onClick, className }) {
28
+ const preview = useConversationPreview({ conversation });
46
29
  const text = useSupportText();
47
- const typingEntries = useConversationTyping(conversation.id, { excludeVisitorId: visitor?.id ?? null });
48
- const typingInfo = useMemo(() => {
49
- if (typingEntries.length === 0) return null;
50
- const entry = typingEntries[0];
51
- let name = text("common.fallbacks.someone");
52
- if (entry?.actorType === "user") {
53
- const human = availableHumanAgents.find((agent) => agent.id === entry.actorId);
54
- name = human?.name || text("common.fallbacks.supportTeam");
55
- human?.image;
56
- } else if (entry?.actorType === "ai_agent") {
57
- const ai = availableAIAgents.find((agent) => agent.id === entry.actorId);
58
- name = ai?.name || text("common.fallbacks.aiAssistant");
59
- ai?.image;
60
- }
61
- return { name };
62
- }, [
63
- typingEntries,
64
- availableHumanAgents,
65
- availableAIAgents,
66
- text
67
- ]);
68
- const lastMessage = useMemo(() => {
69
- const timelineItemToDisplay = (items.length > 0 ? items[items.length - 1] : null) || conversation.lastTimelineItem;
70
- return timelineItemToDisplay ? getLastTimelineItemInfo(timelineItemToDisplay, availableHumanAgents, website, text) : null;
71
- }, [
72
- items,
73
- conversation.lastTimelineItem,
74
- availableHumanAgents,
75
- website,
76
- text
77
- ]);
78
- const assignedAgent = useMemo(() => {
79
- const supportFallbackName = text("common.fallbacks.supportTeam");
80
- const aiFallbackName = text("common.fallbacks.aiAssistant");
81
- const knownItems = items.slice();
82
- if (conversation.lastTimelineItem && !knownItems.some((item) => item.id === conversation.lastTimelineItem?.id)) knownItems.push(conversation.lastTimelineItem);
83
- const lastAgentItem = [...knownItems].reverse().find((item) => item.userId !== null || item.aiAgentId !== null);
84
- if (lastAgentItem?.userId) {
85
- const human = availableHumanAgents.find((agent) => agent.id === lastAgentItem.userId);
86
- if (human) return {
87
- type: "human",
88
- name: human.name,
89
- image: human.image ?? null
90
- };
91
- return {
92
- type: "human",
93
- name: supportFallbackName,
94
- image: null
95
- };
96
- }
97
- if (lastAgentItem?.aiAgentId) {
98
- const ai = availableAIAgents.find((agent) => agent.id === lastAgentItem.aiAgentId);
99
- if (ai) return {
100
- type: "ai",
101
- name: ai.name,
102
- image: ai.image ?? null
103
- };
104
- return {
105
- type: "ai",
106
- name: aiFallbackName,
107
- image: null
108
- };
109
- }
110
- const fallbackHuman = availableHumanAgents[0];
111
- if (fallbackHuman) return {
112
- type: "human",
113
- name: fallbackHuman.name,
114
- image: fallbackHuman.image ?? null
115
- };
116
- const fallbackAi = availableAIAgents[0];
117
- if (fallbackAi) return {
118
- type: "ai",
119
- name: fallbackAi.name,
120
- image: fallbackAi.image ?? null
121
- };
122
- return {
123
- type: "fallback",
124
- name: supportFallbackName,
125
- image: null
126
- };
127
- }, [
128
- items,
129
- conversation.lastTimelineItem,
130
- availableHumanAgents,
131
- availableAIAgents,
132
- text
133
- ]);
134
- const conversationTitle = useMemo(() => {
135
- if (conversation.title) return conversation.title;
136
- if (lastMessage?.content) return lastMessage.content;
137
- return text("component.conversationButtonLink.fallbackTitle");
138
- }, [
139
- conversation.title,
140
- lastMessage?.content,
141
- text
142
- ]);
143
- const state = {
144
- conversation,
145
- lastMessage,
146
- assignedAgent
147
- };
148
- return useRenderElement("button", props, {
149
- state,
150
- props: {
151
- onClick,
152
- type: "button",
153
- className: cn(coButtonVariants({
154
- variant: "secondary",
155
- size: "large"
156
- }), "relative gap-3 border-0 border-co-border/50 border-b text-left transition-colors first-of-type:rounded-t last-of-type:rounded-b last-of-type:border-b-0", typeof props.className === "function" ? props.className(state) : props.className),
157
- children: /* @__PURE__ */ jsxs(Fragment, { children: [
158
- /* @__PURE__ */ jsx(Avatar, {
159
- className: "size-8 flex-shrink-0",
160
- image: assignedAgent.image,
161
- name: assignedAgent.name
162
- }),
163
- /* @__PURE__ */ jsx("div", {
164
- className: "flex min-w-0 flex-1 flex-col gap-0.5",
165
- children: typingInfo ? /* @__PURE__ */ jsx(BouncingDots, {}) : /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx("div", {
166
- className: "flex max-w-[90%] items-center justify-between gap-2",
167
- children: /* @__PURE__ */ jsx("h3", {
168
- className: "truncate font-medium text-co-primary text-sm",
169
- children: conversationTitle
170
- })
171
- }), lastMessage ? /* @__PURE__ */ jsx("p", {
172
- className: "text-co-primary/60 text-xs",
173
- children: lastMessage.isFromVisitor ? /* @__PURE__ */ jsx("span", { children: text("component.conversationButtonLink.lastMessage.visitor", { time: lastMessage.time }) }) : /* @__PURE__ */ jsx("span", { children: text("component.conversationButtonLink.lastMessage.agent", {
174
- name: lastMessage.senderName || text("common.fallbacks.supportTeam"),
175
- time: lastMessage.time
176
- }) })
177
- }) : null] })
178
- }),
179
- /* @__PURE__ */ jsx("div", {
180
- className: cn("mr-6 inline-flex items-center rounded px-2 py-0.5 font-medium text-[9px] uppercase", conversation.status === ConversationStatus.OPEN ? "bg-co-success/20 text-co-success-foreground" : conversation.status === ConversationStatus.RESOLVED ? "bg-co-neutral/20 text-co-neutral-foreground" : "bg-co-warning/20 text-co-warning-foreground"),
181
- children: conversation.status
182
- }),
183
- /* @__PURE__ */ jsx(icons_default, {
184
- className: "-translate-y-1/2 absolute top-1/2 right-4 size-3 text-co-primary/60 transition-transform duration-200 group-hover/btn:translate-x-0.5 group-hover/btn:text-co-primary",
185
- name: "arrow-right",
186
- variant: "default"
187
- })
188
- ] })
189
- }
30
+ const { lastMessage, assignedAgent, typing } = preview;
31
+ const statusBadgeClassName = conversation.deletedAt ? STATUS_BADGE_CLASSNAMES[ConversationStatus.RESOLVED] : STATUS_BADGE_CLASSNAMES[conversation.status] ?? DEFAULT_STATUS_BADGE_CLASSNAME;
32
+ const statusTextKey = conversation.deletedAt ? STATUS_TEXT_KEYS[ConversationStatus.RESOLVED] : STATUS_TEXT_KEYS[conversation.status];
33
+ const statusText = conversation.deletedAt ? text("component.conversationButtonLink.status.closed") : statusTextKey ? text(statusTextKey) : text("common.fallbacks.unknown");
34
+ const lastMessageContent = lastMessage ? lastMessage.isFromVisitor ? /* @__PURE__ */ jsx("span", { children: text("component.conversationButtonLink.lastMessage.visitor", { time: lastMessage.time }) }) : /* @__PURE__ */ jsx("span", { children: text("component.conversationButtonLink.lastMessage.agent", {
35
+ name: lastMessage.senderName || text("common.fallbacks.supportTeam"),
36
+ time: lastMessage.time
37
+ }) }) : void 0;
38
+ return /* @__PURE__ */ jsxs("button", {
39
+ className: cn(coButtonVariants({
40
+ variant: "secondary",
41
+ size: "large"
42
+ }), "group/btn relative gap-3 border-0 border-co-border/50 border-b text-left transition-colors first-of-type:rounded-t last-of-type:rounded-b last-of-type:border-b-0", className),
43
+ onClick,
44
+ type: "button",
45
+ children: [
46
+ /* @__PURE__ */ jsx(Avatar, {
47
+ className: "flex size-8 flex-shrink-0 items-center justify-center overflow-clip rounded-full bg-co-background-200 dark:bg-co-background-500",
48
+ image: assignedAgent?.image,
49
+ name: assignedAgent?.name ?? text("common.fallbacks.supportTeam")
50
+ }),
51
+ /* @__PURE__ */ jsx("div", {
52
+ className: "flex min-w-0 flex-1 flex-col gap-0.5",
53
+ children: typing.isTyping ? /* @__PURE__ */ jsx(BouncingDots, {}) : /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx("div", {
54
+ className: "flex max-w-[90%] items-center justify-between gap-2",
55
+ children: /* @__PURE__ */ jsx("h3", {
56
+ className: "truncate font-medium text-co-primary text-sm",
57
+ children: preview.title
58
+ })
59
+ }), lastMessageContent ? /* @__PURE__ */ jsx("p", {
60
+ className: "text-co-primary/60 text-xs",
61
+ children: lastMessageContent
62
+ }) : null] })
63
+ }),
64
+ /* @__PURE__ */ jsx("div", {
65
+ className: cn("mr-6 inline-flex items-center rounded px-2 py-0.5 font-medium text-[9px] uppercase", statusBadgeClassName),
66
+ children: statusText
67
+ }),
68
+ /* @__PURE__ */ jsx(icons_default, {
69
+ className: "-translate-y-1/2 absolute top-1/2 right-4 size-3 text-co-primary/60 transition-transform duration-200 group-hover/btn:translate-x-0.5 group-hover/btn:text-co-primary",
70
+ name: "arrow-right",
71
+ variant: "default"
72
+ })
73
+ ]
190
74
  });
191
75
  }
192
76
 
@@ -1 +1 @@
1
- {"version":3,"file":"conversation-button-link.js","names":["senderImage: string | null","state: ConversationButtonLinkState","Icon"],"sources":["../../../src/support/components/conversation-button-link.tsx"],"sourcesContent":["import { type Conversation, ConversationStatus } from \"@cossistant/types\";\nimport type { TimelineItem } from \"@cossistant/types/api/timeline-item\";\nimport type React from \"react\";\nimport { useMemo } from \"react\";\nimport {\n\tuseConversationTimelineItems,\n\tuseConversationTyping,\n\tuseSupport,\n} from \"../..\";\nimport { useRenderElement } from \"../../utils/use-render-element\";\nimport { useSupportText } from \"../text\";\nimport { cn } from \"../utils\";\nimport { formatTimeAgo } from \"../utils/time\";\nimport { Avatar } from \"./avatar\";\nimport { coButtonVariants } from \"./button\";\nimport Icon from \"./icons\";\nimport { BouncingDots } from \"./typing-indicator\";\n\nexport type ConversationButtonLinkProps = {\n\tconversation: Conversation;\n\tonClick?: () => void;\n\tclassName?: string | ((state: ConversationButtonLinkState) => string);\n\trender?: (\n\t\tprops: React.HTMLProps<HTMLButtonElement>,\n\t\tstate: ConversationButtonLinkState\n\t) => React.ReactElement;\n};\n\nexport type ConversationButtonLinkAgent = {\n\tname: string;\n\timage: string | null;\n\ttype: \"human\" | \"ai\" | \"fallback\";\n};\n\nexport type ConversationButtonLinkState = {\n\tconversation: Conversation;\n\tlastMessage: {\n\t\tcontent: string;\n\t\ttime: string;\n\t\tisFromVisitor: boolean;\n\t\tsenderName?: string;\n\t\tsenderImage?: string | null;\n\t} | null;\n\tassignedAgent: ConversationButtonLinkAgent;\n};\n\nfunction getLastTimelineItemInfo(\n\titem: NonNullable<Conversation[\"lastTimelineItem\"]>,\n\tavailableHumanAgents: ReturnType<typeof useSupport>[\"availableHumanAgents\"],\n\twebsite: ReturnType<typeof useSupport>[\"website\"],\n\ttext: ReturnType<typeof useSupportText>\n) {\n\tconst isFromVisitor = item.visitorId !== null;\n\n\tlet senderName = text(\"common.fallbacks.unknown\");\n\tlet senderImage: string | null = null;\n\n\tif (isFromVisitor) {\n\t\tsenderName = text(\"common.fallbacks.you\");\n\t} else if (item.userId) {\n\t\tconst agent = availableHumanAgents.find((a) => a.id === item.userId);\n\t\tif (agent) {\n\t\t\tsenderName = agent.name;\n\t\t\tsenderImage = agent.image;\n\t\t} else {\n\t\t\tsenderName = text(\"common.fallbacks.supportTeam\");\n\t\t}\n\t} else if (item.aiAgentId && website?.availableAIAgents) {\n\t\tconst aiAgent = website.availableAIAgents.find(\n\t\t\t(a) => a.id === item.aiAgentId\n\t\t);\n\t\tif (aiAgent) {\n\t\t\tsenderName = aiAgent.name;\n\t\t\tsenderImage = aiAgent.image;\n\t\t} else {\n\t\t\tsenderName = text(\"common.fallbacks.aiAssistant\");\n\t\t}\n\t} else {\n\t\tsenderName = text(\"common.fallbacks.supportTeam\");\n\t}\n\n\treturn {\n\t\tcontent: item.text || \"\",\n\t\ttime: formatTimeAgo(item.createdAt),\n\t\tisFromVisitor,\n\t\tsenderName,\n\t\tsenderImage,\n\t};\n}\n\nexport function ConversationButtonLink({\n\tconversation,\n\tonClick,\n\t...props\n}: ConversationButtonLinkProps) {\n\tconst { availableHumanAgents, availableAIAgents, website, visitor } =\n\t\tuseSupport();\n\tconst { items } = useConversationTimelineItems(conversation.id);\n\tconst text = useSupportText();\n\tconst typingEntries = useConversationTyping(conversation.id, {\n\t\texcludeVisitorId: visitor?.id ?? null,\n\t});\n\n\t// Check if anyone is typing in this conversation\n\tconst typingInfo = useMemo(() => {\n\t\tif (typingEntries.length === 0) {\n\t\t\treturn null;\n\t\t}\n\n\t\tconst entry = typingEntries[0];\n\t\tlet name = text(\"common.fallbacks.someone\");\n\t\tlet image: string | null = null;\n\n\t\tif (entry?.actorType === \"user\") {\n\t\t\tconst human = availableHumanAgents.find(\n\t\t\t\t(agent) => agent.id === entry.actorId\n\t\t\t);\n\t\t\tname = human?.name || text(\"common.fallbacks.supportTeam\");\n\t\t\timage = human?.image || null;\n\t\t} else if (entry?.actorType === \"ai_agent\") {\n\t\t\tconst ai = availableAIAgents.find((agent) => agent.id === entry.actorId);\n\t\t\tname = ai?.name || text(\"common.fallbacks.aiAssistant\");\n\t\t\timage = ai?.image || null;\n\t\t}\n\n\t\treturn { name };\n\t}, [typingEntries, availableHumanAgents, availableAIAgents, text]);\n\n\t// Process the last timeline item (memoized to avoid expensive recomputation)\n\tconst lastMessage = useMemo(() => {\n\t\tconst cachedLastTimelineItem =\n\t\t\t// biome-ignore lint/style/useAtIndex: ok here\n\t\t\titems.length > 0 ? items[items.length - 1] : null;\n\n\t\tconst timelineItemToDisplay =\n\t\t\tcachedLastTimelineItem || conversation.lastTimelineItem;\n\n\t\treturn timelineItemToDisplay\n\t\t\t? getLastTimelineItemInfo(\n\t\t\t\t\ttimelineItemToDisplay,\n\t\t\t\t\tavailableHumanAgents,\n\t\t\t\t\twebsite,\n\t\t\t\t\ttext\n\t\t\t\t)\n\t\t\t: null;\n\t}, [\n\t\titems,\n\t\tconversation.lastTimelineItem,\n\t\tavailableHumanAgents,\n\t\twebsite,\n\t\ttext,\n\t]);\n\n\tconst assignedAgent = useMemo<ConversationButtonLinkAgent>(() => {\n\t\tconst supportFallbackName = text(\"common.fallbacks.supportTeam\");\n\t\tconst aiFallbackName = text(\"common.fallbacks.aiAssistant\");\n\n\t\tconst knownItems = items.slice();\n\t\tif (\n\t\t\tconversation.lastTimelineItem &&\n\t\t\t!knownItems.some((item) => item.id === conversation.lastTimelineItem?.id)\n\t\t) {\n\t\t\tknownItems.push(conversation.lastTimelineItem);\n\t\t}\n\n\t\tconst lastAgentItem = [...knownItems]\n\t\t\t.reverse()\n\t\t\t.find((item) => item.userId !== null || item.aiAgentId !== null);\n\n\t\tif (lastAgentItem?.userId) {\n\t\t\tconst human = availableHumanAgents.find(\n\t\t\t\t(agent) => agent.id === lastAgentItem.userId\n\t\t\t);\n\n\t\t\tif (human) {\n\t\t\t\treturn {\n\t\t\t\t\ttype: \"human\",\n\t\t\t\t\tname: human.name,\n\t\t\t\t\timage: human.image ?? null,\n\t\t\t\t} satisfies ConversationButtonLinkAgent;\n\t\t\t}\n\n\t\t\treturn {\n\t\t\t\ttype: \"human\",\n\t\t\t\tname: supportFallbackName,\n\t\t\t\timage: null,\n\t\t\t} satisfies ConversationButtonLinkAgent;\n\t\t}\n\n\t\tif (lastAgentItem?.aiAgentId) {\n\t\t\tconst ai = availableAIAgents.find(\n\t\t\t\t(agent) => agent.id === lastAgentItem.aiAgentId\n\t\t\t);\n\n\t\t\tif (ai) {\n\t\t\t\treturn {\n\t\t\t\t\ttype: \"ai\",\n\t\t\t\t\tname: ai.name,\n\t\t\t\t\timage: ai.image ?? null,\n\t\t\t\t} satisfies ConversationButtonLinkAgent;\n\t\t\t}\n\n\t\t\treturn {\n\t\t\t\ttype: \"ai\",\n\t\t\t\tname: aiFallbackName,\n\t\t\t\timage: null,\n\t\t\t} satisfies ConversationButtonLinkAgent;\n\t\t}\n\n\t\tconst fallbackHuman = availableHumanAgents[0];\n\t\tif (fallbackHuman) {\n\t\t\treturn {\n\t\t\t\ttype: \"human\",\n\t\t\t\tname: fallbackHuman.name,\n\t\t\t\timage: fallbackHuman.image ?? null,\n\t\t\t} satisfies ConversationButtonLinkAgent;\n\t\t}\n\n\t\tconst fallbackAi = availableAIAgents[0];\n\t\tif (fallbackAi) {\n\t\t\treturn {\n\t\t\t\ttype: \"ai\",\n\t\t\t\tname: fallbackAi.name,\n\t\t\t\timage: fallbackAi.image ?? null,\n\t\t\t} satisfies ConversationButtonLinkAgent;\n\t\t}\n\n\t\treturn {\n\t\t\ttype: \"fallback\",\n\t\t\tname: supportFallbackName,\n\t\t\timage: null,\n\t\t} satisfies ConversationButtonLinkAgent;\n\t}, [\n\t\titems,\n\t\tconversation.lastTimelineItem,\n\t\tavailableHumanAgents,\n\t\tavailableAIAgents,\n\t\ttext,\n\t]);\n\n\tconst conversationTitle = useMemo(() => {\n\t\tif (conversation.title) {\n\t\t\treturn conversation.title;\n\t\t}\n\n\t\tif (lastMessage?.content) {\n\t\t\treturn lastMessage.content;\n\t\t}\n\n\t\treturn text(\"component.conversationButtonLink.fallbackTitle\");\n\t}, [conversation.title, lastMessage?.content, text]);\n\n\tconst state: ConversationButtonLinkState = {\n\t\tconversation,\n\t\tlastMessage,\n\t\tassignedAgent,\n\t};\n\n\treturn useRenderElement(\"button\", props, {\n\t\tstate,\n\t\tprops: {\n\t\t\tonClick,\n\t\t\ttype: \"button\",\n\t\t\tclassName: cn(\n\t\t\t\tcoButtonVariants({ variant: \"secondary\", size: \"large\" }),\n\t\t\t\t\"relative gap-3 border-0 border-co-border/50 border-b text-left transition-colors first-of-type:rounded-t last-of-type:rounded-b last-of-type:border-b-0\",\n\t\t\t\ttypeof props.className === \"function\"\n\t\t\t\t\t? props.className(state)\n\t\t\t\t\t: props.className\n\t\t\t),\n\t\t\tchildren: (\n\t\t\t\t<>\n\t\t\t\t\t<Avatar\n\t\t\t\t\t\tclassName=\"size-8 flex-shrink-0\"\n\t\t\t\t\t\timage={assignedAgent.image}\n\t\t\t\t\t\tname={assignedAgent.name}\n\t\t\t\t\t/>\n\n\t\t\t\t\t<div className=\"flex min-w-0 flex-1 flex-col gap-0.5\">\n\t\t\t\t\t\t{typingInfo ? (\n\t\t\t\t\t\t\t<BouncingDots />\n\t\t\t\t\t\t) : (\n\t\t\t\t\t\t\t<>\n\t\t\t\t\t\t\t\t<div className=\"flex max-w-[90%] items-center justify-between gap-2\">\n\t\t\t\t\t\t\t\t\t<h3 className=\"truncate font-medium text-co-primary text-sm\">\n\t\t\t\t\t\t\t\t\t\t{conversationTitle}\n\t\t\t\t\t\t\t\t\t</h3>\n\t\t\t\t\t\t\t\t</div>\n\n\t\t\t\t\t\t\t\t{lastMessage ? (\n\t\t\t\t\t\t\t\t\t<p className=\"text-co-primary/60 text-xs\">\n\t\t\t\t\t\t\t\t\t\t{lastMessage.isFromVisitor ? (\n\t\t\t\t\t\t\t\t\t\t\t<span>\n\t\t\t\t\t\t\t\t\t\t\t\t{text(\n\t\t\t\t\t\t\t\t\t\t\t\t\t\"component.conversationButtonLink.lastMessage.visitor\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\t\t\t\t\ttime: lastMessage.time,\n\t\t\t\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t\t\t\t) : (\n\t\t\t\t\t\t\t\t\t\t\t<span>\n\t\t\t\t\t\t\t\t\t\t\t\t{text(\n\t\t\t\t\t\t\t\t\t\t\t\t\t\"component.conversationButtonLink.lastMessage.agent\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tname:\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tlastMessage.senderName ||\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\ttext(\"common.fallbacks.supportTeam\"),\n\t\t\t\t\t\t\t\t\t\t\t\t\t\ttime: lastMessage.time,\n\t\t\t\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t\t\t</p>\n\t\t\t\t\t\t\t\t) : null}\n\t\t\t\t\t\t\t</>\n\t\t\t\t\t\t)}\n\t\t\t\t\t</div>\n\t\t\t\t\t<div\n\t\t\t\t\t\tclassName={cn(\n\t\t\t\t\t\t\t\"mr-6 inline-flex items-center rounded px-2 py-0.5 font-medium text-[9px] uppercase\",\n\t\t\t\t\t\t\tconversation.status === ConversationStatus.OPEN\n\t\t\t\t\t\t\t\t? \"bg-co-success/20 text-co-success-foreground\"\n\t\t\t\t\t\t\t\t: conversation.status === ConversationStatus.RESOLVED\n\t\t\t\t\t\t\t\t\t? \"bg-co-neutral/20 text-co-neutral-foreground\"\n\t\t\t\t\t\t\t\t\t: \"bg-co-warning/20 text-co-warning-foreground\"\n\t\t\t\t\t\t)}\n\t\t\t\t\t>\n\t\t\t\t\t\t{conversation.status}\n\t\t\t\t\t</div>\n\t\t\t\t\t<Icon\n\t\t\t\t\t\tclassName=\"-translate-y-1/2 absolute top-1/2 right-4 size-3 text-co-primary/60 transition-transform duration-200 group-hover/btn:translate-x-0.5 group-hover/btn:text-co-primary\"\n\t\t\t\t\t\tname=\"arrow-right\"\n\t\t\t\t\t\tvariant=\"default\"\n\t\t\t\t\t/>\n\t\t\t\t</>\n\t\t\t),\n\t\t},\n\t});\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"conversation-button-link.js","names":["STATUS_BADGE_CLASSNAMES: Record<ConversationStatus, string>","STATUS_TEXT_KEYS: Record<ConversationStatus, SupportTextKey>","Icon"],"sources":["../../../src/support/components/conversation-button-link.tsx"],"sourcesContent":["import { type Conversation, ConversationStatus } from \"@cossistant/types\";\nimport type React from \"react\";\nimport { useConversationPreview } from \"../../hooks/use-conversation-preview\";\nimport { type SupportTextKey, useSupportText } from \"../text\";\nimport { cn } from \"../utils\";\nimport { Avatar } from \"./avatar\";\nimport { coButtonVariants } from \"./button\";\nimport Icon from \"./icons\";\nimport { BouncingDots } from \"./typing-indicator\";\n\nexport type ConversationButtonLinkProps = {\n\tconversation: Conversation;\n\tonClick?: () => void;\n\tclassName?: string;\n};\n\nconst STATUS_BADGE_CLASSNAMES: Record<ConversationStatus, string> = {\n\t[ConversationStatus.OPEN]: \"bg-co-success/20 text-co-success-foreground\",\n\t[ConversationStatus.RESOLVED]: \"bg-co-neutral/20 text-co-neutral-foreground\",\n\t[ConversationStatus.SPAM]: \"bg-co-warning/20 text-co-warning-foreground\",\n};\n\nconst DEFAULT_STATUS_BADGE_CLASSNAME =\n\t\"bg-co-neutral/20 text-co-neutral-foreground\";\n\nconst STATUS_TEXT_KEYS: Record<ConversationStatus, SupportTextKey> = {\n\t[ConversationStatus.OPEN]: \"component.conversationButtonLink.status.open\",\n\t[ConversationStatus.RESOLVED]:\n\t\t\"component.conversationButtonLink.status.resolved\",\n\t[ConversationStatus.SPAM]: \"component.conversationButtonLink.status.spam\",\n};\n\n/**\n * Renders a navigable preview card for a conversation including assigned agent\n * details, last message snippets and typing indicators.\n */\nexport function ConversationButtonLink({\n\tconversation,\n\tonClick,\n\tclassName,\n}: ConversationButtonLinkProps): React.ReactElement | null {\n\tconst preview = useConversationPreview({ conversation });\n\tconst text = useSupportText();\n\tconst { lastMessage, assignedAgent, typing } = preview;\n\n\tconst statusBadgeClassName = conversation.deletedAt\n\t\t? STATUS_BADGE_CLASSNAMES[ConversationStatus.RESOLVED]\n\t\t: (STATUS_BADGE_CLASSNAMES[conversation.status] ??\n\t\t\tDEFAULT_STATUS_BADGE_CLASSNAME);\n\n\tconst statusTextKey = conversation.deletedAt\n\t\t? STATUS_TEXT_KEYS[ConversationStatus.RESOLVED]\n\t\t: STATUS_TEXT_KEYS[conversation.status];\n\n\tconst statusText = conversation.deletedAt\n\t\t? text(\"component.conversationButtonLink.status.closed\")\n\t\t: statusTextKey\n\t\t\t? text(statusTextKey)\n\t\t\t: text(\"common.fallbacks.unknown\");\n\n\tconst lastMessageContent = lastMessage ? (\n\t\tlastMessage.isFromVisitor ? (\n\t\t\t<span>\n\t\t\t\t{text(\"component.conversationButtonLink.lastMessage.visitor\", {\n\t\t\t\t\ttime: lastMessage.time,\n\t\t\t\t})}\n\t\t\t</span>\n\t\t) : (\n\t\t\t<span>\n\t\t\t\t{text(\"component.conversationButtonLink.lastMessage.agent\", {\n\t\t\t\t\tname: lastMessage.senderName || text(\"common.fallbacks.supportTeam\"),\n\t\t\t\t\ttime: lastMessage.time,\n\t\t\t\t})}\n\t\t\t</span>\n\t\t)\n\t) : undefined;\n\n\treturn (\n\t\t<button\n\t\t\tclassName={cn(\n\t\t\t\tcoButtonVariants({\n\t\t\t\t\tvariant: \"secondary\",\n\t\t\t\t\tsize: \"large\",\n\t\t\t\t}),\n\t\t\t\t\"group/btn relative gap-3 border-0 border-co-border/50 border-b text-left transition-colors first-of-type:rounded-t last-of-type:rounded-b last-of-type:border-b-0\",\n\t\t\t\tclassName\n\t\t\t)}\n\t\t\tonClick={onClick}\n\t\t\ttype=\"button\"\n\t\t>\n\t\t\t<Avatar\n\t\t\t\tclassName=\"flex size-8 flex-shrink-0 items-center justify-center overflow-clip rounded-full bg-co-background-200 dark:bg-co-background-500\"\n\t\t\t\timage={assignedAgent?.image}\n\t\t\t\tname={assignedAgent?.name ?? text(\"common.fallbacks.supportTeam\")}\n\t\t\t/>\n\n\t\t\t<div className=\"flex min-w-0 flex-1 flex-col gap-0.5\">\n\t\t\t\t{typing.isTyping ? (\n\t\t\t\t\t<BouncingDots />\n\t\t\t\t) : (\n\t\t\t\t\t<>\n\t\t\t\t\t\t<div className=\"flex max-w-[90%] items-center justify-between gap-2\">\n\t\t\t\t\t\t\t<h3 className=\"truncate font-medium text-co-primary text-sm\">\n\t\t\t\t\t\t\t\t{preview.title}\n\t\t\t\t\t\t\t</h3>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t\t{lastMessageContent ? (\n\t\t\t\t\t\t\t<p className=\"text-co-primary/60 text-xs\">{lastMessageContent}</p>\n\t\t\t\t\t\t) : null}\n\t\t\t\t\t</>\n\t\t\t\t)}\n\t\t\t</div>\n\n\t\t\t<div\n\t\t\t\tclassName={cn(\n\t\t\t\t\t\"mr-6 inline-flex items-center rounded px-2 py-0.5 font-medium text-[9px] uppercase\",\n\t\t\t\t\tstatusBadgeClassName\n\t\t\t\t)}\n\t\t\t>\n\t\t\t\t{statusText}\n\t\t\t</div>\n\n\t\t\t<Icon\n\t\t\t\tclassName=\"-translate-y-1/2 absolute top-1/2 right-4 size-3 text-co-primary/60 transition-transform duration-200 group-hover/btn:translate-x-0.5 group-hover/btn:text-co-primary\"\n\t\t\t\tname=\"arrow-right\"\n\t\t\t\tvariant=\"default\"\n\t\t\t/>\n\t\t</button>\n\t);\n}\n"],"mappings":";;;;;;;;;;;AAgBA,MAAMA,0BAA8D;EAClE,mBAAmB,OAAO;EAC1B,mBAAmB,WAAW;EAC9B,mBAAmB,OAAO;CAC3B;AAED,MAAM,iCACL;AAED,MAAMC,mBAA+D;EACnE,mBAAmB,OAAO;EAC1B,mBAAmB,WACnB;EACA,mBAAmB,OAAO;CAC3B;;;;;AAMD,SAAgB,uBAAuB,EACtC,cACA,SACA,aAC0D;CAC1D,MAAM,UAAU,uBAAuB,EAAE,cAAc,CAAC;CACxD,MAAM,OAAO,gBAAgB;CAC7B,MAAM,EAAE,aAAa,eAAe,WAAW;CAE/C,MAAM,uBAAuB,aAAa,YACvC,wBAAwB,mBAAmB,YAC1C,wBAAwB,aAAa,WACvC;CAEF,MAAM,gBAAgB,aAAa,YAChC,iBAAiB,mBAAmB,YACpC,iBAAiB,aAAa;CAEjC,MAAM,aAAa,aAAa,YAC7B,KAAK,iDAAiD,GACtD,gBACC,KAAK,cAAc,GACnB,KAAK,2BAA2B;CAEpC,MAAM,qBAAqB,cAC1B,YAAY,gBACX,oBAAC,oBACC,KAAK,wDAAwD,EAC7D,MAAM,YAAY,MAClB,CAAC,GACI,GAEP,oBAAC,oBACC,KAAK,sDAAsD;EAC3D,MAAM,YAAY,cAAc,KAAK,+BAA+B;EACpE,MAAM,YAAY;EAClB,CAAC,GACI,GAEL;AAEJ,QACC,qBAAC;EACA,WAAW,GACV,iBAAiB;GAChB,SAAS;GACT,MAAM;GACN,CAAC,EACF,qKACA,UACA;EACQ;EACT,MAAK;;GAEL,oBAAC;IACA,WAAU;IACV,OAAO,eAAe;IACtB,MAAM,eAAe,QAAQ,KAAK,+BAA+B;KAChE;GAEF,oBAAC;IAAI,WAAU;cACb,OAAO,WACP,oBAAC,iBAAe,GAEhB,4CACC,oBAAC;KAAI,WAAU;eACd,oBAAC;MAAG,WAAU;gBACZ,QAAQ;OACL;MACA,EACL,qBACA,oBAAC;KAAE,WAAU;eAA8B;MAAuB,GAC/D,QACF;KAEC;GAEN,oBAAC;IACA,WAAW,GACV,sFACA,qBACA;cAEA;KACI;GAEN,oBAACC;IACA,WAAU;IACV,MAAK;IACL,SAAQ;KACP;;GACM"}
@@ -1 +1 @@
1
- {"version":3,"file":"conversation-event.d.ts","names":[],"sources":["../../../src/support/components/conversation-event.tsx"],"sourcesContent":[],"mappings":";;;;KAWY,sBAAA;SACJ;EADI,iBAAA,EAEQ,gBAFc,EAAA;EAAA,oBAAA,EAGX,mBAHW,EAAA;WAC1B,CAAA,EAAA,MAAA;;AAEe,cAIV,iBAJU,EAIS,KAAA,CAAM,EAJf,CAIkB,sBAJlB,CAAA"}
1
+ {"version":3,"file":"conversation-event.d.ts","names":[],"sources":["../../../src/support/components/conversation-event.tsx"],"sourcesContent":[],"mappings":";;;;KAWY,sBAAA;SACJ;EADI,iBAAA,EAEQ,gBAFc,EAAA;EAC1B,oBAAA,EAEe,mBAFf,EAAA;EACY,SAAA,CAAA,EAAA,MAAA;CACG;AAAmB,cAI7B,iBAJ6B,EAIV,KAAA,CAAM,EAJI,CAID,sBAJC,CAAA"}
@@ -24,6 +24,9 @@ const ConversationEvent = ({ event, availableAIAgents, createdAt, availableHuman
24
24
  case "tag_removed": return text("component.conversationEvent.tagRemoved", { actorName });
25
25
  case "resolved": return text("component.conversationEvent.resolved", { actorName });
26
26
  case "reopened": return text("component.conversationEvent.reopened", { actorName });
27
+ case "visitor_blocked": return text("component.conversationEvent.visitorBlocked", { actorName });
28
+ case "visitor_unblocked": return text("component.conversationEvent.visitorUnblocked", { actorName });
29
+ case "visitor_identified": return text("component.conversationEvent.visitorIdentified", { actorName });
27
30
  default: return text("component.conversationEvent.default", { actorName });
28
31
  }
29
32
  };
@@ -70,6 +73,7 @@ const ConversationEvent = ({ event, availableAIAgents, createdAt, availableHuman
70
73
  })
71
74
  });
72
75
  };
76
+ ConversationEvent.displayName = "ConversationEvent";
73
77
 
74
78
  //#endregion
75
79
  export { ConversationEvent };
@@ -1 +1 @@
1
- {"version":3,"file":"conversation-event.js","names":["ConversationEvent: React.FC<ConversationEventProps>"],"sources":["../../../src/support/components/conversation-event.tsx"],"sourcesContent":["import type {\n\tAvailableAIAgent,\n\tAvailableHumanAgent,\n\tTimelinePartEvent,\n} from \"@cossistant/types\";\nimport { motion } from \"motion/react\";\nimport type React from \"react\";\nimport { useSupportText } from \"../text\";\nimport { Avatar } from \"./avatar\";\nimport { CossistantLogo } from \"./cossistant-branding\";\n\nexport type ConversationEventProps = {\n\tevent: TimelinePartEvent;\n\tavailableAIAgents: AvailableAIAgent[];\n\tavailableHumanAgents: AvailableHumanAgent[];\n\tcreatedAt?: string;\n};\n\nexport const ConversationEvent: React.FC<ConversationEventProps> = ({\n\tevent,\n\tavailableAIAgents,\n\tcreatedAt,\n\tavailableHumanAgents,\n}) => {\n\tconst text = useSupportText();\n\tconst isAI = event.actorAiAgentId !== null;\n\tconst humanAgent = availableHumanAgents.find(\n\t\t(agent) => agent.id === event.actorUserId\n\t);\n\tconst aiAgent = availableAIAgents.find(\n\t\t(agent) => agent.id === event.actorAiAgentId\n\t);\n\n\t// Get the actor name\n\tconst actorName = isAI\n\t\t? aiAgent?.name || text(\"common.fallbacks.cossistant\")\n\t\t: humanAgent?.name || text(\"common.fallbacks.someone\");\n\n\t// Convert event type to plain English\n\tconst getEventText = () => {\n\t\tswitch (event.eventType) {\n\t\t\tcase \"assigned\":\n\t\t\t\treturn text(\"component.conversationEvent.assigned\", {\n\t\t\t\t\tactorName,\n\t\t\t\t});\n\t\t\tcase \"unassigned\":\n\t\t\t\treturn text(\"component.conversationEvent.unassigned\", {\n\t\t\t\t\tactorName,\n\t\t\t\t});\n\t\t\tcase \"participant_requested\":\n\t\t\t\treturn text(\"component.conversationEvent.participantRequested\", {\n\t\t\t\t\tactorName,\n\t\t\t\t});\n\t\t\tcase \"participant_joined\":\n\t\t\t\treturn text(\"component.conversationEvent.participantJoined\", {\n\t\t\t\t\tactorName,\n\t\t\t\t});\n\t\t\tcase \"participant_left\":\n\t\t\t\treturn text(\"component.conversationEvent.participantLeft\", {\n\t\t\t\t\tactorName,\n\t\t\t\t});\n\t\t\tcase \"status_changed\":\n\t\t\t\treturn text(\"component.conversationEvent.statusChanged\", {\n\t\t\t\t\tactorName,\n\t\t\t\t});\n\t\t\tcase \"priority_changed\":\n\t\t\t\treturn text(\"component.conversationEvent.priorityChanged\", {\n\t\t\t\t\tactorName,\n\t\t\t\t});\n\t\t\tcase \"tag_added\":\n\t\t\t\treturn text(\"component.conversationEvent.tagAdded\", {\n\t\t\t\t\tactorName,\n\t\t\t\t});\n\t\t\tcase \"tag_removed\":\n\t\t\t\treturn text(\"component.conversationEvent.tagRemoved\", {\n\t\t\t\t\tactorName,\n\t\t\t\t});\n\t\t\tcase \"resolved\":\n\t\t\t\treturn text(\"component.conversationEvent.resolved\", {\n\t\t\t\t\tactorName,\n\t\t\t\t});\n\t\t\tcase \"reopened\":\n\t\t\t\treturn text(\"component.conversationEvent.reopened\", {\n\t\t\t\t\tactorName,\n\t\t\t\t});\n\t\t\tdefault:\n\t\t\t\treturn text(\"component.conversationEvent.default\", {\n\t\t\t\t\tactorName,\n\t\t\t\t});\n\t\t}\n\t};\n\n\treturn (\n\t\t<motion.div\n\t\t\tanimate={{ opacity: 1, scale: 1 }}\n\t\t\tclassName=\"flex items-center justify-center py-4\"\n\t\t\tinitial={{ opacity: 0, scale: 0.95 }}\n\t\t\ttransition={{ duration: 0.3, ease: \"easeOut\" }}\n\t\t>\n\t\t\t<div className=\"flex items-center gap-2 text-muted-foreground text-xs\">\n\t\t\t\t<div className=\"flex flex-col justify-end\">\n\t\t\t\t\t{isAI ? (\n\t\t\t\t\t\t<div className=\"flex size-5 items-center justify-center rounded-full bg-primary/10\">\n\t\t\t\t\t\t\t<CossistantLogo className=\"h-3 w-3 text-primary\" />\n\t\t\t\t\t\t</div>\n\t\t\t\t\t) : (\n\t\t\t\t\t\t<Avatar\n\t\t\t\t\t\t\tclassName=\"size-5 flex-shrink-0 overflow-clip rounded-full\"\n\t\t\t\t\t\t\timage={humanAgent?.image}\n\t\t\t\t\t\t\tname={humanAgent?.name || text(\"common.fallbacks.someone\")}\n\t\t\t\t\t\t/>\n\t\t\t\t\t)}\n\t\t\t\t</div>\n\t\t\t\t<span className=\"px-2\">{getEventText()}</span>\n\t\t\t\t{createdAt && (\n\t\t\t\t\t<time className=\"text-[10px]\">\n\t\t\t\t\t\t{new Date(createdAt).toLocaleTimeString([], {\n\t\t\t\t\t\t\thour: \"2-digit\",\n\t\t\t\t\t\t\tminute: \"2-digit\",\n\t\t\t\t\t\t})}\n\t\t\t\t\t</time>\n\t\t\t\t)}\n\t\t\t</div>\n\t\t</motion.div>\n\t);\n};\n"],"mappings":";;;;;;;AAkBA,MAAaA,qBAAuD,EACnE,OACA,mBACA,WACA,2BACK;CACL,MAAM,OAAO,gBAAgB;CAC7B,MAAM,OAAO,MAAM,mBAAmB;CACtC,MAAM,aAAa,qBAAqB,MACtC,UAAU,MAAM,OAAO,MAAM,YAC9B;CACD,MAAM,UAAU,kBAAkB,MAChC,UAAU,MAAM,OAAO,MAAM,eAC9B;CAGD,MAAM,YAAY,OACf,SAAS,QAAQ,KAAK,8BAA8B,GACpD,YAAY,QAAQ,KAAK,2BAA2B;CAGvD,MAAM,qBAAqB;AAC1B,UAAQ,MAAM,WAAd;GACC,KAAK,WACJ,QAAO,KAAK,wCAAwC,EACnD,WACA,CAAC;GACH,KAAK,aACJ,QAAO,KAAK,0CAA0C,EACrD,WACA,CAAC;GACH,KAAK,wBACJ,QAAO,KAAK,oDAAoD,EAC/D,WACA,CAAC;GACH,KAAK,qBACJ,QAAO,KAAK,iDAAiD,EAC5D,WACA,CAAC;GACH,KAAK,mBACJ,QAAO,KAAK,+CAA+C,EAC1D,WACA,CAAC;GACH,KAAK,iBACJ,QAAO,KAAK,6CAA6C,EACxD,WACA,CAAC;GACH,KAAK,mBACJ,QAAO,KAAK,+CAA+C,EAC1D,WACA,CAAC;GACH,KAAK,YACJ,QAAO,KAAK,wCAAwC,EACnD,WACA,CAAC;GACH,KAAK,cACJ,QAAO,KAAK,0CAA0C,EACrD,WACA,CAAC;GACH,KAAK,WACJ,QAAO,KAAK,wCAAwC,EACnD,WACA,CAAC;GACH,KAAK,WACJ,QAAO,KAAK,wCAAwC,EACnD,WACA,CAAC;GACH,QACC,QAAO,KAAK,uCAAuC,EAClD,WACA,CAAC;;;AAIL,QACC,oBAAC,OAAO;EACP,SAAS;GAAE,SAAS;GAAG,OAAO;GAAG;EACjC,WAAU;EACV,SAAS;GAAE,SAAS;GAAG,OAAO;GAAM;EACpC,YAAY;GAAE,UAAU;GAAK,MAAM;GAAW;YAE9C,qBAAC;GAAI,WAAU;;IACd,oBAAC;KAAI,WAAU;eACb,OACA,oBAAC;MAAI,WAAU;gBACd,oBAAC,kBAAe,WAAU,yBAAyB;OAC9C,GAEN,oBAAC;MACA,WAAU;MACV,OAAO,YAAY;MACnB,MAAM,YAAY,QAAQ,KAAK,2BAA2B;OACzD;MAEE;IACN,oBAAC;KAAK,WAAU;eAAQ,cAAc;MAAQ;IAC7C,aACA,oBAAC;KAAK,WAAU;eACd,IAAI,KAAK,UAAU,CAAC,mBAAmB,EAAE,EAAE;MAC3C,MAAM;MACN,QAAQ;MACR,CAAC;MACI;;IAEH;GACM"}
1
+ {"version":3,"file":"conversation-event.js","names":["ConversationEvent: React.FC<ConversationEventProps>"],"sources":["../../../src/support/components/conversation-event.tsx"],"sourcesContent":["import type {\n\tAvailableAIAgent,\n\tAvailableHumanAgent,\n\tTimelinePartEvent,\n} from \"@cossistant/types\";\nimport { motion } from \"motion/react\";\nimport type React from \"react\";\nimport { useSupportText } from \"../text\";\nimport { Avatar } from \"./avatar\";\nimport { CossistantLogo } from \"./cossistant-branding\";\n\nexport type ConversationEventProps = {\n\tevent: TimelinePartEvent;\n\tavailableAIAgents: AvailableAIAgent[];\n\tavailableHumanAgents: AvailableHumanAgent[];\n\tcreatedAt?: string;\n};\n\nexport const ConversationEvent: React.FC<ConversationEventProps> = ({\n\tevent,\n\tavailableAIAgents,\n\tcreatedAt,\n\tavailableHumanAgents,\n}) => {\n\tconst text = useSupportText();\n\tconst isAI = event.actorAiAgentId !== null;\n\tconst humanAgent = availableHumanAgents.find(\n\t\t(agent) => agent.id === event.actorUserId\n\t);\n\tconst aiAgent = availableAIAgents.find(\n\t\t(agent) => agent.id === event.actorAiAgentId\n\t);\n\n\t// Get the actor name\n\tconst actorName = isAI\n\t\t? aiAgent?.name || text(\"common.fallbacks.cossistant\")\n\t\t: humanAgent?.name || text(\"common.fallbacks.someone\");\n\n\t// Convert event type to plain English\n\tconst getEventText = () => {\n\t\tswitch (event.eventType) {\n\t\t\tcase \"assigned\":\n\t\t\t\treturn text(\"component.conversationEvent.assigned\", {\n\t\t\t\t\tactorName,\n\t\t\t\t});\n\t\t\tcase \"unassigned\":\n\t\t\t\treturn text(\"component.conversationEvent.unassigned\", {\n\t\t\t\t\tactorName,\n\t\t\t\t});\n\t\t\tcase \"participant_requested\":\n\t\t\t\treturn text(\"component.conversationEvent.participantRequested\", {\n\t\t\t\t\tactorName,\n\t\t\t\t});\n\t\t\tcase \"participant_joined\":\n\t\t\t\treturn text(\"component.conversationEvent.participantJoined\", {\n\t\t\t\t\tactorName,\n\t\t\t\t});\n\t\t\tcase \"participant_left\":\n\t\t\t\treturn text(\"component.conversationEvent.participantLeft\", {\n\t\t\t\t\tactorName,\n\t\t\t\t});\n\t\t\tcase \"status_changed\":\n\t\t\t\treturn text(\"component.conversationEvent.statusChanged\", {\n\t\t\t\t\tactorName,\n\t\t\t\t});\n\t\t\tcase \"priority_changed\":\n\t\t\t\treturn text(\"component.conversationEvent.priorityChanged\", {\n\t\t\t\t\tactorName,\n\t\t\t\t});\n\t\t\tcase \"tag_added\":\n\t\t\t\treturn text(\"component.conversationEvent.tagAdded\", {\n\t\t\t\t\tactorName,\n\t\t\t\t});\n\t\t\tcase \"tag_removed\":\n\t\t\t\treturn text(\"component.conversationEvent.tagRemoved\", {\n\t\t\t\t\tactorName,\n\t\t\t\t});\n\t\t\tcase \"resolved\":\n\t\t\t\treturn text(\"component.conversationEvent.resolved\", {\n\t\t\t\t\tactorName,\n\t\t\t\t});\n\t\t\tcase \"reopened\":\n\t\t\t\treturn text(\"component.conversationEvent.reopened\", {\n\t\t\t\t\tactorName,\n\t\t\t\t});\n\t\t\tcase \"visitor_blocked\":\n\t\t\t\treturn text(\"component.conversationEvent.visitorBlocked\", {\n\t\t\t\t\tactorName,\n\t\t\t\t});\n\t\t\tcase \"visitor_unblocked\":\n\t\t\t\treturn text(\"component.conversationEvent.visitorUnblocked\", {\n\t\t\t\t\tactorName,\n\t\t\t\t});\n\t\t\tcase \"visitor_identified\":\n\t\t\t\treturn text(\"component.conversationEvent.visitorIdentified\", {\n\t\t\t\t\tactorName,\n\t\t\t\t});\n\t\t\tdefault:\n\t\t\t\treturn text(\"component.conversationEvent.default\", {\n\t\t\t\t\tactorName,\n\t\t\t\t});\n\t\t}\n\t};\n\n\treturn (\n\t\t<motion.div\n\t\t\tanimate={{ opacity: 1, scale: 1 }}\n\t\t\tclassName=\"flex items-center justify-center py-4\"\n\t\t\tinitial={{ opacity: 0, scale: 0.95 }}\n\t\t\ttransition={{ duration: 0.3, ease: \"easeOut\" }}\n\t\t>\n\t\t\t<div className=\"flex items-center gap-2 text-muted-foreground text-xs\">\n\t\t\t\t<div className=\"flex flex-col justify-end\">\n\t\t\t\t\t{isAI ? (\n\t\t\t\t\t\t<div className=\"flex size-5 items-center justify-center rounded-full bg-primary/10\">\n\t\t\t\t\t\t\t<CossistantLogo className=\"h-3 w-3 text-primary\" />\n\t\t\t\t\t\t</div>\n\t\t\t\t\t) : (\n\t\t\t\t\t\t<Avatar\n\t\t\t\t\t\t\tclassName=\"size-5 flex-shrink-0 overflow-clip rounded-full\"\n\t\t\t\t\t\t\timage={humanAgent?.image}\n\t\t\t\t\t\t\tname={humanAgent?.name || text(\"common.fallbacks.someone\")}\n\t\t\t\t\t\t/>\n\t\t\t\t\t)}\n\t\t\t\t</div>\n\t\t\t\t<span className=\"px-2\">{getEventText()}</span>\n\t\t\t\t{createdAt && (\n\t\t\t\t\t<time className=\"text-[10px]\">\n\t\t\t\t\t\t{new Date(createdAt).toLocaleTimeString([], {\n\t\t\t\t\t\t\thour: \"2-digit\",\n\t\t\t\t\t\t\tminute: \"2-digit\",\n\t\t\t\t\t\t})}\n\t\t\t\t\t</time>\n\t\t\t\t)}\n\t\t\t</div>\n\t\t</motion.div>\n\t);\n};\n\nConversationEvent.displayName = \"ConversationEvent\";\n"],"mappings":";;;;;;;AAkBA,MAAaA,qBAAuD,EACnE,OACA,mBACA,WACA,2BACK;CACL,MAAM,OAAO,gBAAgB;CAC7B,MAAM,OAAO,MAAM,mBAAmB;CACtC,MAAM,aAAa,qBAAqB,MACtC,UAAU,MAAM,OAAO,MAAM,YAC9B;CACD,MAAM,UAAU,kBAAkB,MAChC,UAAU,MAAM,OAAO,MAAM,eAC9B;CAGD,MAAM,YAAY,OACf,SAAS,QAAQ,KAAK,8BAA8B,GACpD,YAAY,QAAQ,KAAK,2BAA2B;CAGvD,MAAM,qBAAqB;AAC1B,UAAQ,MAAM,WAAd;GACC,KAAK,WACJ,QAAO,KAAK,wCAAwC,EACnD,WACA,CAAC;GACH,KAAK,aACJ,QAAO,KAAK,0CAA0C,EACrD,WACA,CAAC;GACH,KAAK,wBACJ,QAAO,KAAK,oDAAoD,EAC/D,WACA,CAAC;GACH,KAAK,qBACJ,QAAO,KAAK,iDAAiD,EAC5D,WACA,CAAC;GACH,KAAK,mBACJ,QAAO,KAAK,+CAA+C,EAC1D,WACA,CAAC;GACH,KAAK,iBACJ,QAAO,KAAK,6CAA6C,EACxD,WACA,CAAC;GACH,KAAK,mBACJ,QAAO,KAAK,+CAA+C,EAC1D,WACA,CAAC;GACH,KAAK,YACJ,QAAO,KAAK,wCAAwC,EACnD,WACA,CAAC;GACH,KAAK,cACJ,QAAO,KAAK,0CAA0C,EACrD,WACA,CAAC;GACH,KAAK,WACJ,QAAO,KAAK,wCAAwC,EACnD,WACA,CAAC;GACH,KAAK,WACJ,QAAO,KAAK,wCAAwC,EACnD,WACA,CAAC;GACH,KAAK,kBACJ,QAAO,KAAK,8CAA8C,EACzD,WACA,CAAC;GACH,KAAK,oBACJ,QAAO,KAAK,gDAAgD,EAC3D,WACA,CAAC;GACH,KAAK,qBACJ,QAAO,KAAK,iDAAiD,EAC5D,WACA,CAAC;GACH,QACC,QAAO,KAAK,uCAAuC,EAClD,WACA,CAAC;;;AAIL,QACC,oBAAC,OAAO;EACP,SAAS;GAAE,SAAS;GAAG,OAAO;GAAG;EACjC,WAAU;EACV,SAAS;GAAE,SAAS;GAAG,OAAO;GAAM;EACpC,YAAY;GAAE,UAAU;GAAK,MAAM;GAAW;YAE9C,qBAAC;GAAI,WAAU;;IACd,oBAAC;KAAI,WAAU;eACb,OACA,oBAAC;MAAI,WAAU;gBACd,oBAAC,kBAAe,WAAU,yBAAyB;OAC9C,GAEN,oBAAC;MACA,WAAU;MACV,OAAO,YAAY;MACnB,MAAM,YAAY,QAAQ,KAAK,2BAA2B;OACzD;MAEE;IACN,oBAAC;KAAK,WAAU;eAAQ,cAAc;MAAQ;IAC7C,aACA,oBAAC;KAAK,WAAU;eACd,IAAI,KAAK,UAAU,CAAC,mBAAmB,EAAE,EAAE;MAC3C,MAAM;MACN,QAAQ;MACR,CAAC;MACI;;IAEH;GACM;;AAIf,kBAAkB,cAAc"}
@@ -3,6 +3,14 @@ import React from "react";
3
3
  import { AvailableAIAgent, AvailableHumanAgent } from "@cossistant/types";
4
4
 
5
5
  //#region src/support/components/conversation-timeline.d.ts
6
+ type ConversationTimelineToolProps = {
7
+ item: TimelineItem;
8
+ conversationId: string;
9
+ };
10
+ type ConversationTimelineToolDefinition = {
11
+ component: React.ComponentType<ConversationTimelineToolProps>;
12
+ };
13
+ type ConversationTimelineTools = Record<string, ConversationTimelineToolDefinition>;
6
14
  type ConversationTimelineProps = {
7
15
  conversationId: string;
8
16
  items: TimelineItem[];
@@ -10,8 +18,9 @@ type ConversationTimelineProps = {
10
18
  availableAIAgents: AvailableAIAgent[];
11
19
  availableHumanAgents: AvailableHumanAgent[];
12
20
  currentVisitorId?: string;
21
+ tools?: ConversationTimelineTools;
13
22
  };
14
23
  declare const ConversationTimelineList: React.FC<ConversationTimelineProps>;
15
24
  //#endregion
16
- export { ConversationTimelineList, ConversationTimelineProps };
25
+ export { ConversationTimelineList, ConversationTimelineProps, ConversationTimelineToolDefinition, ConversationTimelineToolProps, ConversationTimelineTools };
17
26
  //# sourceMappingURL=conversation-timeline.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"conversation-timeline.d.ts","names":[],"sources":["../../../src/support/components/conversation-timeline.tsx"],"sourcesContent":[],"mappings":";;;;;KAoCY,yBAAA;;EAAA,KAAA,EAEJ,YAFI,EAAA;EAAyB,SAAA,CAAA,EAAA,MAAA;mBAE7B,EAEY,gBAFZ,EAAA;sBAEY,EACG,mBADH,EAAA;kBACG,CAAA,EAAA,MAAA;CAAmB;AAI7B,cAAA,wBAsIZ,EAtIsC,KAAA,CAAM,EAsI5C,CAtI+C,yBAsI/C,CAAA"}
1
+ {"version":3,"file":"conversation-timeline.d.ts","names":[],"sources":["../../../src/support/components/conversation-timeline.tsx"],"sourcesContent":[],"mappings":";;;;;KAiCY,6BAAA;QACL;EADK,cAAA,EAAA,MAAA;AAKZ,CAAA;AAIY,KAJA,kCAAA,GAMX;EAGW,SAAA,EARA,KAAA,CAAM,aAQmB,CARL,6BAQK,CAAA;CAE7B;AAEY,KATR,yBAAA,GAA4B,MASpB,CAAA,MAAA,EAPnB,kCAOmB,CAAA;AACG,KALX,yBAAA,GAKW;EAEd,cAAA,EAAA,MAAA;EAAyB,KAAA,EAL1B,YAK0B,EAAA;EAGrB,SAAA,CAAA,EAAA,MAAA;qBANO;wBACG;;UAEd;;cAGI,0BAA0B,KAAA,CAAM,GAAG"}
@@ -1,51 +1,48 @@
1
1
  import { cn } from "../utils/index.js";
2
2
  import { TypingIndicator } from "./typing-indicator.js";
3
3
  import { ConversationTimeline, ConversationTimelineContainer } from "../../primitives/conversation-timeline.js";
4
- import { useGroupedMessages } from "../../hooks/private/use-grouped-messages.js";
4
+ import { useConversationTimeline } from "../../hooks/use-conversation-timeline.js";
5
5
  import { ConversationEvent } from "./conversation-event.js";
6
6
  import { TimelineMessageGroup } from "./timeline-message-group.js";
7
- import { useDebouncedConversationSeen } from "../../hooks/use-conversation-seen.js";
8
- import { useConversationTyping } from "../../hooks/use-conversation-typing.js";
9
- import { useMemo } from "react";
10
- import { SenderType } from "@cossistant/types";
7
+ import { useCallback, useMemo } from "react";
11
8
  import { jsx, jsxs } from "react/jsx-runtime";
12
- import { AnimatePresence } from "motion/react";
13
9
 
14
10
  //#region src/support/components/conversation-timeline.tsx
15
11
  function extractEventPart(item) {
16
12
  if (item.type !== "event") return null;
17
13
  return item.parts.find((part) => part.type === "event") || null;
18
14
  }
19
- const ConversationTimelineList = ({ conversationId, items: timelineItems, className, availableAIAgents = [], availableHumanAgents = [], currentVisitorId }) => {
20
- const seenData = useDebouncedConversationSeen(conversationId);
21
- const typingEntries = useConversationTyping(conversationId, { excludeVisitorId: currentVisitorId ?? null });
22
- const { items, getMessageSeenBy } = useGroupedMessages({
15
+ const EMPTY_SEEN_BY_IDS = Object.freeze([]);
16
+ const EMPTY_SEEN_BY_NAMES = Object.freeze([]);
17
+ const ConversationTimelineList = ({ conversationId, items: timelineItems, className, availableAIAgents = [], availableHumanAgents = [], currentVisitorId, tools }) => {
18
+ const timeline = useConversationTimeline({
19
+ conversationId,
23
20
  items: timelineItems,
24
- seenData,
25
- currentViewerId: currentVisitorId,
26
- viewerType: SenderType.VISITOR
21
+ currentVisitorId
27
22
  });
28
- const lastVisitorMessageGroupIndex = useMemo(() => {
29
- for (let i = items.length - 1; i >= 0; i--) {
30
- const item = items[i];
31
- if (!item) continue;
32
- if (item.type === "message_group") {
33
- if ((item.items?.[0])?.visitorId === currentVisitorId) return i;
34
- }
23
+ const typingIndicatorParticipants = useMemo(() => timeline.typingParticipants.map((participant) => ({
24
+ id: participant.id,
25
+ type: participant.type
26
+ })), [timeline.typingParticipants]);
27
+ const seenNameLookup = useMemo(() => {
28
+ const map = /* @__PURE__ */ new Map();
29
+ for (const agent of availableHumanAgents) if (agent.name) map.set(agent.id, agent.name);
30
+ for (const agent of availableAIAgents) if (agent.name) map.set(agent.id, agent.name);
31
+ return map;
32
+ }, [availableHumanAgents, availableAIAgents]);
33
+ const getSeenByNames = useCallback((ids = EMPTY_SEEN_BY_IDS) => {
34
+ if (ids.length === 0 || seenNameLookup.size === 0) return EMPTY_SEEN_BY_NAMES;
35
+ const uniqueNames = /* @__PURE__ */ new Set();
36
+ const names = [];
37
+ for (const id of ids) {
38
+ const name = seenNameLookup.get(id);
39
+ if (!name || uniqueNames.has(name)) continue;
40
+ uniqueNames.add(name);
41
+ names.push(name);
35
42
  }
36
- return -1;
37
- }, [items, currentVisitorId]);
38
- const typingParticipants = useMemo(() => typingEntries.map((entry) => {
39
- if (entry.actorType === "user") return {
40
- id: entry.actorId,
41
- type: "team_member"
42
- };
43
- if (entry.actorType === "ai_agent") return {
44
- id: entry.actorId,
45
- type: "ai"
46
- };
47
- return null;
48
- }).filter((participant) => participant !== null), [typingEntries]);
43
+ if (names.length === 0) return EMPTY_SEEN_BY_NAMES;
44
+ return Object.freeze(names);
45
+ }, [seenNameLookup]);
49
46
  return /* @__PURE__ */ jsx(ConversationTimeline, {
50
47
  autoScroll: true,
51
48
  className: cn("overflow-y-auto scroll-smooth px-3 py-6", "scrollbar-thin scrollbar-thumb-co-background-300 scrollbar-track-transparent", "h-full w-full", className),
@@ -53,37 +50,46 @@ const ConversationTimelineList = ({ conversationId, items: timelineItems, classN
53
50
  items: timelineItems,
54
51
  children: /* @__PURE__ */ jsxs(ConversationTimelineContainer, {
55
52
  className: "flex min-h-full w-full flex-col gap-3",
56
- children: [/* @__PURE__ */ jsx(AnimatePresence, {
57
- initial: false,
58
- mode: "popLayout",
59
- children: items.map((item, index) => {
60
- if (item.type === "timeline_event") {
61
- const eventPart = extractEventPart(item.item);
62
- if (!eventPart) return null;
63
- return /* @__PURE__ */ jsx(ConversationEvent, {
64
- availableAIAgents,
65
- availableHumanAgents,
66
- createdAt: item.item.createdAt,
67
- event: eventPart
68
- }, item.item.id || `timeline-event-${index}`);
69
- }
70
- const seenByIds = index === lastVisitorMessageGroupIndex && item.lastMessageId ? getMessageSeenBy(item.lastMessageId) : [];
71
- const groupKey = item.items?.[0]?.id || `group-${index}`;
72
- return /* @__PURE__ */ jsx(TimelineMessageGroup, {
53
+ children: [timeline.groupedMessages.items.map((item, index) => {
54
+ if (item.type === "timeline_event") {
55
+ const eventPart = extractEventPart(item.item);
56
+ if (!eventPart) return null;
57
+ return /* @__PURE__ */ jsx(ConversationEvent, {
73
58
  availableAIAgents,
74
59
  availableHumanAgents,
75
- currentVisitorId,
76
- items: item.items || [],
77
- seenByIds
78
- }, groupKey);
79
- })
60
+ createdAt: item.item.createdAt,
61
+ event: eventPart
62
+ }, item.item.id ?? `timeline-event-${item.item.createdAt}`);
63
+ }
64
+ if (item.type === "timeline_tool") {
65
+ const toolName = item.tool ?? item.item.tool ?? item.item.type;
66
+ const toolDefinition = toolName ? tools?.[toolName] : void 0;
67
+ if (!toolDefinition) return null;
68
+ const ToolComponent = toolDefinition.component;
69
+ const toolKey = item.item.id ?? `${toolName}-${item.item.createdAt}-${index}`;
70
+ return /* @__PURE__ */ jsx(ToolComponent, {
71
+ conversationId,
72
+ item: item.item
73
+ }, toolKey);
74
+ }
75
+ const seenByIds = index === timeline.lastVisitorMessageGroupIndex && item.lastMessageId ? timeline.groupedMessages.getMessageSeenBy(item.lastMessageId) : EMPTY_SEEN_BY_IDS;
76
+ const seenByNames = seenByIds.length > 0 ? getSeenByNames(seenByIds) : EMPTY_SEEN_BY_NAMES;
77
+ const groupKey = item.lastMessageId ?? item.items?.[0]?.id ?? `group-${item.items?.[0]?.createdAt ?? index}`;
78
+ return /* @__PURE__ */ jsx(TimelineMessageGroup, {
79
+ availableAIAgents,
80
+ availableHumanAgents,
81
+ currentVisitorId,
82
+ items: item.items || [],
83
+ seenByIds,
84
+ seenByNames
85
+ }, groupKey);
80
86
  }), /* @__PURE__ */ jsx("div", {
81
87
  className: "h-6 w-full",
82
- children: typingParticipants.length > 0 ? /* @__PURE__ */ jsx(TypingIndicator, {
88
+ children: typingIndicatorParticipants.length > 0 ? /* @__PURE__ */ jsx(TypingIndicator, {
83
89
  availableAIAgents,
84
90
  availableHumanAgents,
85
91
  className: "mt-2",
86
- participants: typingParticipants
92
+ participants: typingIndicatorParticipants
87
93
  }) : null
88
94
  })]
89
95
  })
@@ -1 +1 @@
1
- {"version":3,"file":"conversation-timeline.js","names":["ConversationTimelineList: React.FC<ConversationTimelineProps>","PrimitiveConversationTimeline","ConversationEventComponent"],"sources":["../../../src/support/components/conversation-timeline.tsx"],"sourcesContent":["import type { AvailableAIAgent, AvailableHumanAgent } from \"@cossistant/types\";\nimport { SenderType } from \"@cossistant/types\";\nimport type {\n\tTimelineItem,\n\tTimelinePartEvent,\n} from \"@cossistant/types/api/timeline-item\";\nimport { AnimatePresence } from \"motion/react\";\nimport type React from \"react\";\nimport { useMemo } from \"react\";\nimport {\n\tuseConversationTyping,\n\tuseDebouncedConversationSeen,\n} from \"../../hooks\";\nimport { useGroupedMessages } from \"../../hooks/private/use-grouped-messages\";\nimport {\n\tConversationTimelineContainer,\n\tConversationTimeline as PrimitiveConversationTimeline,\n} from \"../../primitives/conversation-timeline\";\nimport { cn } from \"../utils\";\nimport { ConversationEvent as ConversationEventComponent } from \"./conversation-event\";\nimport { TimelineMessageGroup } from \"./timeline-message-group\";\nimport { TypingIndicator, type TypingParticipant } from \"./typing-indicator\";\n\n// Helper to extract event part from timeline item\nfunction extractEventPart(item: TimelineItem): TimelinePartEvent | null {\n\tif (item.type !== \"event\") {\n\t\treturn null;\n\t}\n\n\tconst eventPart = item.parts.find(\n\t\t(part): part is TimelinePartEvent => part.type === \"event\"\n\t);\n\n\treturn eventPart || null;\n}\n\nexport type ConversationTimelineProps = {\n\tconversationId: string;\n\titems: TimelineItem[];\n\tclassName?: string;\n\tavailableAIAgents: AvailableAIAgent[];\n\tavailableHumanAgents: AvailableHumanAgent[];\n\tcurrentVisitorId?: string;\n};\n\nexport const ConversationTimelineList: React.FC<ConversationTimelineProps> = ({\n\tconversationId,\n\titems: timelineItems,\n\tclassName,\n\tavailableAIAgents = [],\n\tavailableHumanAgents = [],\n\tcurrentVisitorId,\n}) => {\n\tconst seenData = useDebouncedConversationSeen(conversationId);\n\tconst typingEntries = useConversationTyping(conversationId, {\n\t\texcludeVisitorId: currentVisitorId ?? null,\n\t});\n\n\tconst { items, getMessageSeenBy } = useGroupedMessages({\n\t\titems: timelineItems,\n\t\tseenData,\n\t\tcurrentViewerId: currentVisitorId,\n\t\tviewerType: SenderType.VISITOR,\n\t});\n\n\t// Find the last message group sent by the current visitor\n\tconst lastVisitorMessageGroupIndex = useMemo(() => {\n\t\tfor (let i = items.length - 1; i >= 0; i--) {\n\t\t\tconst item = items[i];\n\t\t\tif (!item) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (item.type === \"message_group\") {\n\t\t\t\tconst firstItem = item.items?.[0];\n\t\t\t\tif (firstItem?.visitorId === currentVisitorId) {\n\t\t\t\t\treturn i;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn -1;\n\t}, [items, currentVisitorId]);\n\n\tconst typingParticipants = useMemo(\n\t\t() =>\n\t\t\ttypingEntries\n\t\t\t\t.map((entry): TypingParticipant | null => {\n\t\t\t\t\tif (entry.actorType === \"user\") {\n\t\t\t\t\t\treturn {\n\t\t\t\t\t\t\tid: entry.actorId,\n\t\t\t\t\t\t\ttype: \"team_member\" as const,\n\t\t\t\t\t\t};\n\t\t\t\t\t}\n\n\t\t\t\t\tif (entry.actorType === \"ai_agent\") {\n\t\t\t\t\t\treturn {\n\t\t\t\t\t\t\tid: entry.actorId,\n\t\t\t\t\t\t\ttype: \"ai\" as const,\n\t\t\t\t\t\t};\n\t\t\t\t\t}\n\n\t\t\t\t\treturn null;\n\t\t\t\t})\n\t\t\t\t.filter(\n\t\t\t\t\t(participant): participant is TypingParticipant =>\n\t\t\t\t\t\tparticipant !== null\n\t\t\t\t),\n\t\t[typingEntries]\n\t);\n\n\treturn (\n\t\t<PrimitiveConversationTimeline\n\t\t\tautoScroll={true}\n\t\t\tclassName={cn(\n\t\t\t\t\"overflow-y-auto scroll-smooth px-3 py-6\",\n\t\t\t\t\"scrollbar-thin scrollbar-thumb-co-background-300 scrollbar-track-transparent\",\n\t\t\t\t\"h-full w-full\",\n\t\t\t\tclassName\n\t\t\t)}\n\t\t\tid=\"conversation-timeline\"\n\t\t\titems={timelineItems}\n\t\t>\n\t\t\t<ConversationTimelineContainer className=\"flex min-h-full w-full flex-col gap-3\">\n\t\t\t\t<AnimatePresence initial={false} mode=\"popLayout\">\n\t\t\t\t\t{items.map((item, index) => {\n\t\t\t\t\t\tif (item.type === \"timeline_event\") {\n\t\t\t\t\t\t\t// Extract event data from parts\n\t\t\t\t\t\t\tconst eventPart = extractEventPart(item.item);\n\n\t\t\t\t\t\t\t// Only render if we have valid event data\n\t\t\t\t\t\t\tif (!eventPart) {\n\t\t\t\t\t\t\t\treturn null;\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\treturn (\n\t\t\t\t\t\t\t\t<ConversationEventComponent\n\t\t\t\t\t\t\t\t\tavailableAIAgents={availableAIAgents}\n\t\t\t\t\t\t\t\t\tavailableHumanAgents={availableHumanAgents}\n\t\t\t\t\t\t\t\t\tcreatedAt={item.item.createdAt}\n\t\t\t\t\t\t\t\t\tevent={eventPart}\n\t\t\t\t\t\t\t\t\tkey={item.item.id || `timeline-event-${index}`}\n\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// Only show seen indicator on the LAST message group sent by the visitor\n\t\t\t\t\t\tconst isLastVisitorGroup = index === lastVisitorMessageGroupIndex;\n\t\t\t\t\t\tconst seenByIds =\n\t\t\t\t\t\t\tisLastVisitorGroup && item.lastMessageId\n\t\t\t\t\t\t\t\t? getMessageSeenBy(item.lastMessageId)\n\t\t\t\t\t\t\t\t: [];\n\n\t\t\t\t\t\t// Use first timeline item ID as stable key\n\t\t\t\t\t\tconst groupKey = item.items?.[0]?.id || `group-${index}`;\n\n\t\t\t\t\t\treturn (\n\t\t\t\t\t\t\t<TimelineMessageGroup\n\t\t\t\t\t\t\t\tavailableAIAgents={availableAIAgents}\n\t\t\t\t\t\t\t\tavailableHumanAgents={availableHumanAgents}\n\t\t\t\t\t\t\t\tcurrentVisitorId={currentVisitorId}\n\t\t\t\t\t\t\t\titems={item.items || []}\n\t\t\t\t\t\t\t\tkey={groupKey}\n\t\t\t\t\t\t\t\tseenByIds={seenByIds}\n\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t);\n\t\t\t\t\t})}\n\t\t\t\t</AnimatePresence>\n\t\t\t\t<div className=\"h-6 w-full\">\n\t\t\t\t\t{typingParticipants.length > 0 ? (\n\t\t\t\t\t\t<TypingIndicator\n\t\t\t\t\t\t\tavailableAIAgents={availableAIAgents}\n\t\t\t\t\t\t\tavailableHumanAgents={availableHumanAgents}\n\t\t\t\t\t\t\tclassName=\"mt-2\"\n\t\t\t\t\t\t\tparticipants={typingParticipants}\n\t\t\t\t\t\t/>\n\t\t\t\t\t) : null}\n\t\t\t\t</div>\n\t\t\t</ConversationTimelineContainer>\n\t\t</PrimitiveConversationTimeline>\n\t);\n};\n"],"mappings":";;;;;;;;;;;;;;AAwBA,SAAS,iBAAiB,MAA8C;AACvE,KAAI,KAAK,SAAS,QACjB,QAAO;AAOR,QAJkB,KAAK,MAAM,MAC3B,SAAoC,KAAK,SAAS,QACnD,IAEmB;;AAYrB,MAAaA,4BAAiE,EAC7E,gBACA,OAAO,eACP,WACA,oBAAoB,EAAE,EACtB,uBAAuB,EAAE,EACzB,uBACK;CACL,MAAM,WAAW,6BAA6B,eAAe;CAC7D,MAAM,gBAAgB,sBAAsB,gBAAgB,EAC3D,kBAAkB,oBAAoB,MACtC,CAAC;CAEF,MAAM,EAAE,OAAO,qBAAqB,mBAAmB;EACtD,OAAO;EACP;EACA,iBAAiB;EACjB,YAAY,WAAW;EACvB,CAAC;CAGF,MAAM,+BAA+B,cAAc;AAClD,OAAK,IAAI,IAAI,MAAM,SAAS,GAAG,KAAK,GAAG,KAAK;GAC3C,MAAM,OAAO,MAAM;AACnB,OAAI,CAAC,KACJ;AAED,OAAI,KAAK,SAAS,iBAEjB;SADkB,KAAK,QAAQ,KAChB,cAAc,iBAC5B,QAAO;;;AAIV,SAAO;IACL,CAAC,OAAO,iBAAiB,CAAC;CAE7B,MAAM,qBAAqB,cAEzB,cACE,KAAK,UAAoC;AACzC,MAAI,MAAM,cAAc,OACvB,QAAO;GACN,IAAI,MAAM;GACV,MAAM;GACN;AAGF,MAAI,MAAM,cAAc,WACvB,QAAO;GACN,IAAI,MAAM;GACV,MAAM;GACN;AAGF,SAAO;GACN,CACD,QACC,gBACA,gBAAgB,KACjB,EACH,CAAC,cAAc,CACf;AAED,QACC,oBAACC;EACA,YAAY;EACZ,WAAW,GACV,2CACA,gFACA,iBACA,UACA;EACD,IAAG;EACH,OAAO;YAEP,qBAAC;GAA8B,WAAU;cACxC,oBAAC;IAAgB,SAAS;IAAO,MAAK;cACpC,MAAM,KAAK,MAAM,UAAU;AAC3B,SAAI,KAAK,SAAS,kBAAkB;MAEnC,MAAM,YAAY,iBAAiB,KAAK,KAAK;AAG7C,UAAI,CAAC,UACJ,QAAO;AAGR,aACC,oBAACC;OACmB;OACG;OACtB,WAAW,KAAK,KAAK;OACrB,OAAO;SACF,KAAK,KAAK,MAAM,kBAAkB,QACtC;;KAMJ,MAAM,YADqB,UAAU,gCAEd,KAAK,gBACxB,iBAAiB,KAAK,cAAc,GACpC,EAAE;KAGN,MAAM,WAAW,KAAK,QAAQ,IAAI,MAAM,SAAS;AAEjD,YACC,oBAAC;MACmB;MACG;MACJ;MAClB,OAAO,KAAK,SAAS,EAAE;MAEZ;QADN,SAEJ;MAEF;KACe,EAClB,oBAAC;IAAI,WAAU;cACb,mBAAmB,SAAS,IAC5B,oBAAC;KACmB;KACG;KACtB,WAAU;KACV,cAAc;MACb,GACC;KACC;IACyB;GACD"}
1
+ {"version":3,"file":"conversation-timeline.js","names":["EMPTY_SEEN_BY_IDS: readonly string[]","EMPTY_SEEN_BY_NAMES: readonly string[]","ConversationTimelineList: React.FC<ConversationTimelineProps>","names: string[]","PrimitiveConversationTimeline"],"sources":["../../../src/support/components/conversation-timeline.tsx"],"sourcesContent":["import type { AvailableAIAgent, AvailableHumanAgent } from \"@cossistant/types\";\nimport type {\n\tTimelineItem,\n\tTimelinePartEvent,\n} from \"@cossistant/types/api/timeline-item\";\nimport type React from \"react\";\nimport { useCallback, useMemo } from \"react\";\nimport { useConversationTimeline } from \"../../hooks/use-conversation-timeline\";\nimport {\n\tConversationTimelineContainer,\n\tConversationTimeline as PrimitiveConversationTimeline,\n} from \"../../primitives/conversation-timeline\";\nimport { cn } from \"../utils\";\nimport { ConversationEvent } from \"./conversation-event\";\nimport { TimelineMessageGroup } from \"./timeline-message-group\";\nimport { TypingIndicator, type TypingParticipant } from \"./typing-indicator\";\n\n// Helper to extract event part from timeline item\nfunction extractEventPart(item: TimelineItem): TimelinePartEvent | null {\n\tif (item.type !== \"event\") {\n\t\treturn null;\n\t}\n\n\tconst eventPart = item.parts.find(\n\t\t(part): part is TimelinePartEvent => part.type === \"event\"\n\t);\n\n\treturn eventPart || null;\n}\n\nconst EMPTY_SEEN_BY_IDS: readonly string[] = Object.freeze([]);\nconst EMPTY_SEEN_BY_NAMES: readonly string[] = Object.freeze([]);\n\nexport type ConversationTimelineToolProps = {\n\titem: TimelineItem;\n\tconversationId: string;\n};\n\nexport type ConversationTimelineToolDefinition = {\n\tcomponent: React.ComponentType<ConversationTimelineToolProps>;\n};\n\nexport type ConversationTimelineTools = Record<\n\tstring,\n\tConversationTimelineToolDefinition\n>;\n\nexport type ConversationTimelineProps = {\n\tconversationId: string;\n\titems: TimelineItem[];\n\tclassName?: string;\n\tavailableAIAgents: AvailableAIAgent[];\n\tavailableHumanAgents: AvailableHumanAgent[];\n\tcurrentVisitorId?: string;\n\ttools?: ConversationTimelineTools;\n};\n\nexport const ConversationTimelineList: React.FC<ConversationTimelineProps> = ({\n\tconversationId,\n\titems: timelineItems,\n\tclassName,\n\tavailableAIAgents = [],\n\tavailableHumanAgents = [],\n\tcurrentVisitorId,\n\ttools,\n}) => {\n\tconst timeline = useConversationTimeline({\n\t\tconversationId,\n\t\titems: timelineItems,\n\t\tcurrentVisitorId,\n\t});\n\n\tconst typingIndicatorParticipants = useMemo(\n\t\t() =>\n\t\t\ttimeline.typingParticipants.map<TypingParticipant>((participant) => ({\n\t\t\t\tid: participant.id,\n\t\t\t\ttype: participant.type,\n\t\t\t})),\n\t\t[timeline.typingParticipants]\n\t);\n\n\tconst seenNameLookup = useMemo(() => {\n\t\tconst map = new Map<string, string>();\n\n\t\tfor (const agent of availableHumanAgents) {\n\t\t\tif (agent.name) {\n\t\t\t\tmap.set(agent.id, agent.name);\n\t\t\t}\n\t\t}\n\n\t\tfor (const agent of availableAIAgents) {\n\t\t\tif (agent.name) {\n\t\t\t\tmap.set(agent.id, agent.name);\n\t\t\t}\n\t\t}\n\n\t\treturn map;\n\t}, [availableHumanAgents, availableAIAgents]);\n\n\tconst getSeenByNames = useCallback(\n\t\t(ids: readonly string[] = EMPTY_SEEN_BY_IDS): readonly string[] => {\n\t\t\tif (ids.length === 0 || seenNameLookup.size === 0) {\n\t\t\t\treturn EMPTY_SEEN_BY_NAMES;\n\t\t\t}\n\n\t\t\tconst uniqueNames = new Set<string>();\n\t\t\tconst names: string[] = [];\n\n\t\t\tfor (const id of ids) {\n\t\t\t\tconst name = seenNameLookup.get(id);\n\t\t\t\tif (!name || uniqueNames.has(name)) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\tuniqueNames.add(name);\n\t\t\t\tnames.push(name);\n\t\t\t}\n\n\t\t\tif (names.length === 0) {\n\t\t\t\treturn EMPTY_SEEN_BY_NAMES;\n\t\t\t}\n\n\t\t\treturn Object.freeze(names);\n\t\t},\n\t\t[seenNameLookup]\n\t);\n\n\treturn (\n\t\t<PrimitiveConversationTimeline\n\t\t\tautoScroll={true}\n\t\t\tclassName={cn(\n\t\t\t\t\"overflow-y-auto scroll-smooth px-3 py-6\",\n\t\t\t\t\"scrollbar-thin scrollbar-thumb-co-background-300 scrollbar-track-transparent\",\n\t\t\t\t\"h-full w-full\",\n\t\t\t\tclassName\n\t\t\t)}\n\t\t\tid=\"conversation-timeline\"\n\t\t\titems={timelineItems}\n\t\t>\n\t\t\t<ConversationTimelineContainer className=\"flex min-h-full w-full flex-col gap-3\">\n\t\t\t\t{timeline.groupedMessages.items.map((item, index) => {\n\t\t\t\t\tif (item.type === \"timeline_event\") {\n\t\t\t\t\t\t// Extract event data from parts\n\t\t\t\t\t\tconst eventPart = extractEventPart(item.item);\n\n\t\t\t\t\t\t// Only render if we have valid event data\n\t\t\t\t\t\tif (!eventPart) {\n\t\t\t\t\t\t\treturn null;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\treturn (\n\t\t\t\t\t\t\t<ConversationEvent\n\t\t\t\t\t\t\t\tavailableAIAgents={availableAIAgents}\n\t\t\t\t\t\t\t\tavailableHumanAgents={availableHumanAgents}\n\t\t\t\t\t\t\t\tcreatedAt={item.item.createdAt}\n\t\t\t\t\t\t\t\tevent={eventPart}\n\t\t\t\t\t\t\t\tkey={item.item.id ?? `timeline-event-${item.item.createdAt}`}\n\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\n\t\t\t\t\tif (item.type === \"timeline_tool\") {\n\t\t\t\t\t\tconst toolName = item.tool ?? item.item.tool ?? item.item.type;\n\t\t\t\t\t\tconst toolDefinition = toolName ? tools?.[toolName] : undefined;\n\n\t\t\t\t\t\tif (!toolDefinition) {\n\t\t\t\t\t\t\treturn null;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tconst ToolComponent = toolDefinition.component;\n\n\t\t\t\t\t\tconst toolKey =\n\t\t\t\t\t\t\titem.item.id ?? `${toolName}-${item.item.createdAt}-${index}`;\n\n\t\t\t\t\t\treturn (\n\t\t\t\t\t\t\t<ToolComponent\n\t\t\t\t\t\t\t\tconversationId={conversationId}\n\t\t\t\t\t\t\t\titem={item.item}\n\t\t\t\t\t\t\t\tkey={toolKey}\n\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\n\t\t\t\t\t// Only show seen indicator on the LAST message group sent by the visitor\n\t\t\t\t\tconst isLastVisitorGroup =\n\t\t\t\t\t\tindex === timeline.lastVisitorMessageGroupIndex;\n\t\t\t\t\tconst seenByIds =\n\t\t\t\t\t\tisLastVisitorGroup && item.lastMessageId\n\t\t\t\t\t\t\t? timeline.groupedMessages.getMessageSeenBy(item.lastMessageId)\n\t\t\t\t\t\t\t: EMPTY_SEEN_BY_IDS;\n\t\t\t\t\tconst seenByNames =\n\t\t\t\t\t\tseenByIds.length > 0\n\t\t\t\t\t\t\t? getSeenByNames(seenByIds)\n\t\t\t\t\t\t\t: EMPTY_SEEN_BY_NAMES;\n\n\t\t\t\t\t// Use first timeline item ID as stable key\n\t\t\t\t\tconst groupKey =\n\t\t\t\t\t\titem.lastMessageId ??\n\t\t\t\t\t\titem.items?.[0]?.id ??\n\t\t\t\t\t\t`group-${item.items?.[0]?.createdAt ?? index}`;\n\n\t\t\t\t\treturn (\n\t\t\t\t\t\t<TimelineMessageGroup\n\t\t\t\t\t\t\tavailableAIAgents={availableAIAgents}\n\t\t\t\t\t\t\tavailableHumanAgents={availableHumanAgents}\n\t\t\t\t\t\t\tcurrentVisitorId={currentVisitorId}\n\t\t\t\t\t\t\titems={item.items || []}\n\t\t\t\t\t\t\tkey={groupKey}\n\t\t\t\t\t\t\tseenByIds={seenByIds}\n\t\t\t\t\t\t\tseenByNames={seenByNames}\n\t\t\t\t\t\t/>\n\t\t\t\t\t);\n\t\t\t\t})}\n\t\t\t\t<div className=\"h-6 w-full\">\n\t\t\t\t\t{typingIndicatorParticipants.length > 0 ? (\n\t\t\t\t\t\t<TypingIndicator\n\t\t\t\t\t\t\tavailableAIAgents={availableAIAgents}\n\t\t\t\t\t\t\tavailableHumanAgents={availableHumanAgents}\n\t\t\t\t\t\t\tclassName=\"mt-2\"\n\t\t\t\t\t\t\tparticipants={typingIndicatorParticipants}\n\t\t\t\t\t\t/>\n\t\t\t\t\t) : null}\n\t\t\t\t</div>\n\t\t\t</ConversationTimelineContainer>\n\t\t</PrimitiveConversationTimeline>\n\t);\n};\n"],"mappings":"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"}