@cossistant/react 0.0.29 → 0.0.31

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 (305) hide show
  1. package/README.md +3 -1
  2. package/_virtual/rolldown_runtime.js +9 -23
  3. package/hooks/index.d.ts +2 -2
  4. package/hooks/private/store/use-conversations-store.d.ts +2 -0
  5. package/hooks/private/store/use-conversations-store.d.ts.map +1 -1
  6. package/hooks/private/store/use-conversations-store.js +15 -8
  7. package/hooks/private/store/use-conversations-store.js.map +1 -1
  8. package/hooks/private/store/use-store-selector.d.ts +3 -0
  9. package/hooks/private/store/use-store-selector.d.ts.map +1 -1
  10. package/hooks/private/store/use-store-selector.js +4 -8
  11. package/hooks/private/store/use-store-selector.js.map +1 -1
  12. package/hooks/private/store/use-website-store.d.ts +3 -1
  13. package/hooks/private/store/use-website-store.d.ts.map +1 -1
  14. package/hooks/private/store/use-website-store.js +14 -6
  15. package/hooks/private/store/use-website-store.js.map +1 -1
  16. package/hooks/private/use-client-query.d.ts +1 -1
  17. package/hooks/private/use-client-query.d.ts.map +1 -1
  18. package/hooks/private/use-client-query.js +1 -0
  19. package/hooks/private/use-client-query.js.map +1 -1
  20. package/hooks/private/use-default-messages.d.ts +1 -1
  21. package/hooks/private/use-grouped-messages.d.ts +2 -2
  22. package/hooks/private/use-grouped-messages.d.ts.map +1 -1
  23. package/hooks/private/use-grouped-messages.js +34 -10
  24. package/hooks/private/use-grouped-messages.js.map +1 -1
  25. package/hooks/private/use-rest-client.d.ts +13 -3
  26. package/hooks/private/use-rest-client.d.ts.map +1 -1
  27. package/hooks/private/use-rest-client.js +49 -22
  28. package/hooks/private/use-rest-client.js.map +1 -1
  29. package/hooks/private/use-visitor-typing-reporter.d.ts +1 -1
  30. package/hooks/use-conversation-auto-seen.d.ts +1 -1
  31. package/hooks/use-conversation-page.d.ts +1 -1
  32. package/hooks/use-conversation-page.d.ts.map +1 -1
  33. package/hooks/use-conversation-page.js +13 -4
  34. package/hooks/use-conversation-page.js.map +1 -1
  35. package/hooks/use-conversation-preview.d.ts +3 -1
  36. package/hooks/use-conversation-preview.d.ts.map +1 -1
  37. package/hooks/use-conversation-preview.js +8 -4
  38. package/hooks/use-conversation-preview.js.map +1 -1
  39. package/hooks/use-conversation-seen.d.ts +1 -1
  40. package/hooks/use-conversation-timeline-items.d.ts +1 -1
  41. package/hooks/use-conversation-timeline-items.js +2 -3
  42. package/hooks/use-conversation-timeline-items.js.map +1 -1
  43. package/hooks/use-conversation-timeline.d.ts +1 -1
  44. package/hooks/use-conversation.d.ts +1 -1
  45. package/hooks/use-conversation.js +2 -3
  46. package/hooks/use-conversation.js.map +1 -1
  47. package/hooks/use-conversations.d.ts +1 -1
  48. package/hooks/use-conversations.js +5 -3
  49. package/hooks/use-conversations.js.map +1 -1
  50. package/hooks/use-create-conversation.d.ts +3 -3
  51. package/hooks/use-create-conversation.js +1 -0
  52. package/hooks/use-create-conversation.js.map +1 -1
  53. package/hooks/use-file-upload.d.ts +1 -1
  54. package/hooks/use-file-upload.js +3 -3
  55. package/hooks/use-file-upload.js.map +1 -1
  56. package/hooks/use-home-page.js +3 -3
  57. package/hooks/use-home-page.js.map +1 -1
  58. package/hooks/use-message-composer.d.ts +10 -3
  59. package/hooks/use-message-composer.d.ts.map +1 -1
  60. package/hooks/use-message-composer.js +5 -2
  61. package/hooks/use-message-composer.js.map +1 -1
  62. package/hooks/use-realtime-support.d.ts +1 -1
  63. package/hooks/use-send-message.d.ts +8 -2
  64. package/hooks/use-send-message.d.ts.map +1 -1
  65. package/hooks/use-send-message.js +5 -3
  66. package/hooks/use-send-message.js.map +1 -1
  67. package/hooks/use-visitor.js +2 -2
  68. package/hooks/use-visitor.js.map +1 -1
  69. package/identify-visitor.d.ts.map +1 -1
  70. package/identify-visitor.js +15 -1
  71. package/identify-visitor.js.map +1 -1
  72. package/index.d.ts +2 -2
  73. package/index.js +1 -1
  74. package/package.json +6 -3
  75. package/{conversation.d.ts → packages/types/src/api/conversation.d.ts} +374 -64
  76. package/packages/types/src/api/conversation.d.ts.map +1 -0
  77. package/packages/types/src/api/timeline-item.d.ts +460 -0
  78. package/packages/types/src/api/timeline-item.d.ts.map +1 -0
  79. package/{realtime-events.d.ts → packages/types/src/realtime-events.d.ts} +449 -47
  80. package/packages/types/src/realtime-events.d.ts.map +1 -0
  81. package/{schemas3.d.ts → packages/types/src/schemas.d.ts} +97 -19
  82. package/packages/types/src/schemas.d.ts.map +1 -0
  83. package/primitives/avatar/avatar.js +1 -1
  84. package/primitives/avatar/avatar.js.map +1 -1
  85. package/primitives/avatar/fallback.js +1 -1
  86. package/primitives/avatar/fallback.js.map +1 -1
  87. package/primitives/avatar/image.js +1 -1
  88. package/primitives/avatar/image.js.map +1 -1
  89. package/primitives/button.js +1 -1
  90. package/primitives/button.js.map +1 -1
  91. package/primitives/conversation-timeline.d.ts +1 -1
  92. package/primitives/conversation-timeline.js +4 -4
  93. package/primitives/conversation-timeline.js.map +1 -1
  94. package/primitives/day-separator.js +3 -3
  95. package/primitives/day-separator.js.map +1 -1
  96. package/primitives/multimodal-input.d.ts +2 -2
  97. package/primitives/multimodal-input.d.ts.map +1 -1
  98. package/primitives/multimodal-input.js +2 -2
  99. package/primitives/multimodal-input.js.map +1 -1
  100. package/primitives/timeline-item-attachments.d.ts +1 -1
  101. package/primitives/timeline-item-attachments.js +6 -7
  102. package/primitives/timeline-item-attachments.js.map +1 -1
  103. package/primitives/timeline-item-group.d.ts +1 -1
  104. package/primitives/timeline-item-group.js +7 -7
  105. package/primitives/timeline-item-group.js.map +1 -1
  106. package/primitives/timeline-item.d.ts +1 -1
  107. package/primitives/timeline-item.d.ts.map +1 -1
  108. package/primitives/timeline-item.js +54 -14
  109. package/primitives/timeline-item.js.map +1 -1
  110. package/primitives/trigger.js +1 -1
  111. package/primitives/trigger.js.map +1 -1
  112. package/primitives/window.js +1 -1
  113. package/primitives/window.js.map +1 -1
  114. package/provider.d.ts +4 -2
  115. package/provider.d.ts.map +1 -1
  116. package/provider.js +56 -8
  117. package/provider.js.map +1 -1
  118. package/realtime/event-filter.d.ts +4 -1
  119. package/realtime/event-filter.d.ts.map +1 -1
  120. package/realtime/event-filter.js +14 -0
  121. package/realtime/event-filter.js.map +1 -1
  122. package/realtime/provider.d.ts +1 -1
  123. package/realtime/provider.d.ts.map +1 -1
  124. package/realtime/provider.js +1 -2
  125. package/realtime/provider.js.map +1 -1
  126. package/realtime/seen-store.d.ts +2 -2
  127. package/realtime/support-provider.js +3 -2
  128. package/realtime/support-provider.js.map +1 -1
  129. package/realtime/typing-store.d.ts +1 -1
  130. package/realtime/use-realtime.d.ts +1 -1
  131. package/support/components/avatar-stack.d.ts.map +1 -1
  132. package/support/components/avatar-stack.js +32 -12
  133. package/support/components/avatar-stack.js.map +1 -1
  134. package/support/components/avatar.d.ts +34 -3
  135. package/support/components/avatar.d.ts.map +1 -1
  136. package/support/components/avatar.js +61 -8
  137. package/support/components/avatar.js.map +1 -1
  138. package/support/components/button.d.ts +4 -2
  139. package/support/components/button.d.ts.map +1 -1
  140. package/support/components/button.js +3 -3
  141. package/support/components/button.js.map +1 -1
  142. package/support/components/configuration-error.d.ts +16 -0
  143. package/support/components/configuration-error.d.ts.map +1 -0
  144. package/support/components/configuration-error.js +162 -0
  145. package/support/components/configuration-error.js.map +1 -0
  146. package/support/components/content.js +1 -2
  147. package/support/components/content.js.map +1 -1
  148. package/support/components/conversation-button-link.js +18 -23
  149. package/support/components/conversation-button-link.js.map +1 -1
  150. package/support/components/conversation-event.d.ts.map +1 -1
  151. package/support/components/conversation-event.js +7 -5
  152. package/support/components/conversation-event.js.map +1 -1
  153. package/support/components/conversation-resolved-feedback.d.ts +21 -0
  154. package/support/components/conversation-resolved-feedback.d.ts.map +1 -0
  155. package/support/components/conversation-resolved-feedback.js +59 -0
  156. package/support/components/conversation-resolved-feedback.js.map +1 -0
  157. package/support/components/conversation-timeline-utils.d.ts +5 -0
  158. package/support/components/conversation-timeline-utils.d.ts.map +1 -0
  159. package/support/components/conversation-timeline-utils.js +10 -0
  160. package/support/components/conversation-timeline-utils.js.map +1 -0
  161. package/support/components/conversation-timeline.d.ts +1 -1
  162. package/support/components/conversation-timeline.d.ts.map +1 -1
  163. package/support/components/conversation-timeline.js +5 -4
  164. package/support/components/conversation-timeline.js.map +1 -1
  165. package/support/components/header.js +1 -1
  166. package/support/components/icons.d.ts +1 -1
  167. package/support/components/icons.d.ts.map +1 -1
  168. package/support/components/icons.js +6 -2
  169. package/support/components/icons.js.map +1 -1
  170. package/support/components/image-lightbox.d.ts +1 -1
  171. package/support/components/image-lightbox.js +1 -2
  172. package/support/components/image-lightbox.js.map +1 -1
  173. package/support/components/index.d.ts +2 -1
  174. package/support/components/index.js +3 -2
  175. package/support/components/multimodal-input.js +0 -1
  176. package/support/components/multimodal-input.js.map +1 -1
  177. package/support/components/navigation-tab.js +1 -1
  178. package/support/components/online-indicator.d.ts +50 -0
  179. package/support/components/online-indicator.d.ts.map +1 -0
  180. package/support/components/online-indicator.js +65 -0
  181. package/support/components/online-indicator.js.map +1 -0
  182. package/support/components/root.js +0 -1
  183. package/support/components/root.js.map +1 -1
  184. package/support/components/timeline-identification-tool.js +4 -4
  185. package/support/components/timeline-identification-tool.js.map +1 -1
  186. package/support/components/timeline-message-group.d.ts +1 -1
  187. package/support/components/timeline-message-group.d.ts.map +1 -1
  188. package/support/components/timeline-message-group.js +6 -4
  189. package/support/components/timeline-message-group.js.map +1 -1
  190. package/support/components/timeline-message-item.d.ts +1 -1
  191. package/support/components/timeline-message-item.js +4 -4
  192. package/support/components/timeline-message-item.js.map +1 -1
  193. package/support/components/trigger.js +1 -2
  194. package/support/components/trigger.js.map +1 -1
  195. package/support/components/typing-indicator.js +1 -1
  196. package/support/components/typing-indicator.js.map +1 -1
  197. package/support/context/controlled-state.js +0 -1
  198. package/support/context/controlled-state.js.map +1 -1
  199. package/support/context/events.d.ts +1 -1
  200. package/support/context/events.js +0 -1
  201. package/support/context/events.js.map +1 -1
  202. package/support/context/handle.js +0 -1
  203. package/support/context/handle.js.map +1 -1
  204. package/support/context/identification.d.ts +33 -0
  205. package/support/context/identification.d.ts.map +1 -0
  206. package/support/context/identification.js +34 -0
  207. package/support/context/identification.js.map +1 -0
  208. package/support/context/positioning.js +0 -1
  209. package/support/context/positioning.js.map +1 -1
  210. package/support/context/slots.js +0 -1
  211. package/support/context/slots.js.map +1 -1
  212. package/support/context/websocket.d.ts +1 -1
  213. package/support/context/websocket.js +0 -1
  214. package/support/context/websocket.js.map +1 -1
  215. package/support/index.d.ts.map +1 -1
  216. package/support/index.js +51 -18
  217. package/support/index.js.map +1 -1
  218. package/support/pages/conversation-history.js +2 -1
  219. package/support/pages/conversation-history.js.map +1 -1
  220. package/support/pages/conversation.d.ts +1 -1
  221. package/support/pages/conversation.d.ts.map +1 -1
  222. package/support/pages/conversation.js +34 -8
  223. package/support/pages/conversation.js.map +1 -1
  224. package/support/pages/home.js +5 -3
  225. package/support/pages/home.js.map +1 -1
  226. package/support/router.d.ts.map +1 -1
  227. package/support/router.js +4 -0
  228. package/support/router.js.map +1 -1
  229. package/support/store/support-store.js +0 -1
  230. package/support/store/support-store.js.map +1 -1
  231. package/support/{support-C7Xaw-N6.css → support-DmViRaga.css} +2 -2
  232. package/support/{support-C7Xaw-N6.css.map → support-DmViRaga.css.map} +1 -1
  233. package/support/text/index.js +1 -1
  234. package/support/text/index.js.map +1 -1
  235. package/support/text/locales/en.js +10 -1
  236. package/support/text/locales/en.js.map +1 -1
  237. package/support/text/locales/es.js +10 -1
  238. package/support/text/locales/es.js.map +1 -1
  239. package/support/text/locales/fr.js +10 -1
  240. package/support/text/locales/fr.js.map +1 -1
  241. package/support/text/locales/keys.d.ts +11 -0
  242. package/support/text/locales/keys.d.ts.map +1 -1
  243. package/support/text/locales/keys.js +3 -0
  244. package/support/text/locales/keys.js.map +1 -1
  245. package/support/utils/index.d.ts +1 -1
  246. package/support-config.js +0 -1
  247. package/support-config.js.map +1 -1
  248. package/support.css +1 -1
  249. package/tailwind.css +1 -1
  250. package/utils/conversation.d.ts.map +1 -1
  251. package/utils/conversation.js +1 -3
  252. package/utils/conversation.js.map +1 -1
  253. package/utils/use-render-element.js +2 -2
  254. package/utils/use-render-element.js.map +1 -1
  255. package/api.d.ts +0 -71
  256. package/api.d.ts.map +0 -1
  257. package/checks.d.ts +0 -189
  258. package/checks.d.ts.map +0 -1
  259. package/clsx.d.ts +0 -7
  260. package/clsx.d.ts.map +0 -1
  261. package/coerce.d.ts +0 -9
  262. package/coerce.d.ts.map +0 -1
  263. package/conversation.d.ts.map +0 -1
  264. package/core.d.ts +0 -35
  265. package/core.d.ts.map +0 -1
  266. package/errors.d.ts +0 -130
  267. package/errors.d.ts.map +0 -1
  268. package/errors2.d.ts +0 -24
  269. package/errors2.d.ts.map +0 -1
  270. package/index2.d.ts +0 -4
  271. package/index3.d.ts +0 -1
  272. package/json-schema.d.ts +0 -70
  273. package/json-schema.d.ts.map +0 -1
  274. package/metadata.d.ts +0 -1
  275. package/openapi-generator.d.ts +0 -1
  276. package/openapi-generator2.d.ts +0 -1
  277. package/openapi-generator3.d.ts +0 -1
  278. package/openapi30.d.ts +0 -125
  279. package/openapi30.d.ts.map +0 -1
  280. package/openapi31.d.ts +0 -131
  281. package/openapi31.d.ts.map +0 -1
  282. package/parse.d.ts +0 -17
  283. package/parse.d.ts.map +0 -1
  284. package/realtime-events.d.ts.map +0 -1
  285. package/registries.d.ts +0 -32
  286. package/registries.d.ts.map +0 -1
  287. package/schemas.d.ts +0 -971
  288. package/schemas.d.ts.map +0 -1
  289. package/schemas2.d.ts +0 -345
  290. package/schemas2.d.ts.map +0 -1
  291. package/schemas3.d.ts.map +0 -1
  292. package/specification-extension.d.ts +0 -9
  293. package/specification-extension.d.ts.map +0 -1
  294. package/standard-schema.d.ts +0 -121
  295. package/standard-schema.d.ts.map +0 -1
  296. package/timeline-item.d.ts +0 -227
  297. package/timeline-item.d.ts.map +0 -1
  298. package/to-json-schema.d.ts +0 -96
  299. package/to-json-schema.d.ts.map +0 -1
  300. package/util.d.ts +0 -45
  301. package/util.d.ts.map +0 -1
  302. package/versions.d.ts +0 -9
  303. package/versions.d.ts.map +0 -1
  304. package/zod-extensions.d.ts +0 -39
  305. package/zod-extensions.d.ts.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"content.js","names":["React","Content: React.FC<ContentPropsType>","Primitive.Window","ContentInner: React.FC<{\n\tchildren: React.ReactNode;\n\tcontainerRef: React.RefObject<HTMLDivElement | null>;\n\tshowScrollIndicator: boolean;\n}>"],"sources":["../../../src/support/components/content.tsx"],"sourcesContent":["\"use client\";\n\nimport {\n\tautoUpdate,\n\tflip,\n\toffset,\n\ttype Placement,\n\tshift,\n\tuseFloating,\n} from \"@floating-ui/react\";\nimport { AnimatePresence, motion } from \"motion/react\";\nimport * as React from \"react\";\nimport * as Primitive from \"../../primitives\";\nimport { useTriggerRef } from \"../context/positioning\";\nimport { SlotProvider, useSlots } from \"../context/slots\";\nimport { useSupportConfig } from \"../store/support-store\";\nimport type {\n\tAlign,\n\tCollisionPadding,\n\tContentProps as ContentPropsType,\n\tSide,\n} from \"../types\";\nimport { cn } from \"../utils\";\n\nexport type { CollisionPadding, ContentProps } from \"../types\";\n\n// =============================================================================\n// Utils\n// =============================================================================\n\n/**\n * Convert side + align props to Floating UI placement\n */\nfunction getPlacement(side: Side, align: Align): Placement {\n\tif (align === \"center\") {\n\t\treturn side;\n\t}\n\treturn `${side}-${align}` as Placement;\n}\n\n/**\n * Get fallback positioning classes for when Floating UI is not available\n * (e.g., trigger ref not set, or avoidCollisions is false)\n */\nfunction getFallbackPositioningClasses(side: Side, align: Align): string {\n\tconst sideClasses: Record<Side, string> = {\n\t\ttop: \"md:bottom-full md:mb-4\",\n\t\tbottom: \"md:top-full md:mt-4\",\n\t\tleft: \"md:right-full md:mr-4\",\n\t\tright: \"md:left-full md:ml-4\",\n\t};\n\n\tconst alignClasses: Record<Side, Record<Align, string>> = {\n\t\ttop: {\n\t\t\tstart: \"md:left-0\",\n\t\t\tcenter: \"md:left-1/2 md:-translate-x-1/2\",\n\t\t\tend: \"md:right-0\",\n\t\t},\n\t\tbottom: {\n\t\t\tstart: \"md:left-0\",\n\t\t\tcenter: \"md:left-1/2 md:-translate-x-1/2\",\n\t\t\tend: \"md:right-0\",\n\t\t},\n\t\tleft: {\n\t\t\tstart: \"md:top-0\",\n\t\t\tcenter: \"md:top-1/2 md:-translate-y-1/2\",\n\t\t\tend: \"md:bottom-0\",\n\t\t},\n\t\tright: {\n\t\t\tstart: \"md:top-0\",\n\t\t\tcenter: \"md:top-1/2 md:-translate-y-1/2\",\n\t\t\tend: \"md:bottom-0\",\n\t\t},\n\t};\n\n\treturn cn(sideClasses[side], alignClasses[side][align]);\n}\n\n/**\n * Get fallback offset styles for static positioning\n */\nfunction getFallbackOffsetStyle(\n\tside: Side,\n\tsideOffset: number\n): React.CSSProperties | undefined {\n\tif (sideOffset === 16) {\n\t\treturn;\n\t}\n\n\tconst offsetMap: Record<Side, React.CSSProperties> = {\n\t\ttop: { marginBottom: sideOffset },\n\t\tbottom: { marginTop: sideOffset },\n\t\tleft: { marginRight: sideOffset },\n\t\tright: { marginLeft: sideOffset },\n\t};\n\n\treturn offsetMap[side];\n}\n\n// =============================================================================\n// Hook for responsive detection\n// =============================================================================\n\nfunction useIsMobile(): boolean {\n\tconst [isMobile, setIsMobile] = React.useState(false);\n\n\tReact.useEffect(() => {\n\t\tconst mediaQuery = window.matchMedia(\"(max-width: 767px)\");\n\t\tsetIsMobile(mediaQuery.matches);\n\n\t\tconst handler = (event: MediaQueryListEvent) => {\n\t\t\tsetIsMobile(event.matches);\n\t\t};\n\n\t\tmediaQuery.addEventListener(\"change\", handler);\n\t\treturn () => mediaQuery.removeEventListener(\"change\", handler);\n\t}, []);\n\n\treturn isMobile;\n}\n\n// =============================================================================\n// Content Component\n// =============================================================================\n\n/**\n * Content component for the support window.\n * Uses Floating UI for automatic collision detection on desktop.\n * Fullscreen on mobile, floating on desktop.\n *\n * @example\n * // Basic usage (uses defaults: side=\"top\", align=\"end\")\n * <Support.Content />\n *\n * @example\n * // Custom positioning with collision avoidance\n * <Support.Content side=\"bottom\" align=\"start\" sideOffset={24} />\n *\n * @example\n * // Disable collision avoidance for static positioning\n * <Support.Content avoidCollisions={false} />\n *\n * @example\n * // Custom collision padding\n * <Support.Content collisionPadding={{ top: 16, bottom: 32 }} />\n */\nexport const Content: React.FC<ContentPropsType> = ({\n\tclassName,\n\tchildren,\n\tside = \"top\",\n\talign = \"end\",\n\tsideOffset = 16,\n\tavoidCollisions = true,\n\tcollisionPadding = 8,\n}) => {\n\tconst [showScrollIndicator, setShowScrollIndicator] = React.useState(false);\n\tconst containerRef = React.useRef<HTMLDivElement>(null);\n\tconst hasEverPositionedRef = React.useRef(false);\n\tconst isMobile = useIsMobile();\n\tconst triggerRefContext = useTriggerRef();\n\tconst { isOpen } = useSupportConfig();\n\n\t// Set up Floating UI middleware\n\tconst middleware = React.useMemo(() => {\n\t\tconst middlewares = [offset(sideOffset)];\n\n\t\tif (avoidCollisions) {\n\t\t\tmiddlewares.push(\n\t\t\t\tflip({\n\t\t\t\t\tpadding: collisionPadding,\n\t\t\t\t\tfallbackAxisSideDirection: \"start\",\n\t\t\t\t}),\n\t\t\t\tshift({\n\t\t\t\t\tpadding: collisionPadding,\n\t\t\t\t})\n\t\t\t);\n\t\t}\n\n\t\treturn middlewares;\n\t}, [sideOffset, avoidCollisions, collisionPadding]);\n\n\t// Get trigger element from context (stored in state for reactivity)\n\tconst triggerElement = triggerRefContext?.triggerElement ?? null;\n\n\t// Initialize Floating UI with the trigger element as reference\n\t// Using strategy: 'fixed' because Content uses position: fixed (md:fixed class)\n\t// This ensures Floating UI calculates positions relative to the viewport\n\t// The `open` prop synchronizes Floating UI with visibility state for proper autoUpdate\n\tconst { refs, update, x, y, isPositioned } = useFloating({\n\t\tplacement: getPlacement(side, align),\n\t\tstrategy: \"fixed\",\n\t\tmiddleware,\n\t\twhileElementsMounted: autoUpdate,\n\t\topen: isOpen,\n\t\telements: {\n\t\t\treference: triggerElement,\n\t\t},\n\t});\n\n\t// Merge refs for the floating element - ensures proper ref forwarding with motion.div\n\tconst setFloatingRef = React.useCallback(\n\t\t(node: HTMLDivElement | null) => {\n\t\t\trefs.setFloating(node);\n\t\t},\n\t\t[refs]\n\t);\n\n\t// Force position recalculation when trigger element becomes available\n\t// This handles the case where content mounts before trigger\n\tReact.useEffect(() => {\n\t\tif (triggerElement && isOpen) {\n\t\t\t// Defer update to ensure DOM is ready\n\t\t\trequestAnimationFrame(() => {\n\t\t\t\tupdate();\n\t\t\t});\n\t\t}\n\t}, [triggerElement, isOpen, update]);\n\n\t// Determine if we should use Floating UI positioning\n\t// Only use Floating UI when trigger element is available\n\tconst useFloatingPositioning =\n\t\tavoidCollisions && !isMobile && triggerElement !== null;\n\n\t// Scroll indicator logic\n\tconst checkScroll = React.useCallback(() => {\n\t\tconst container = containerRef.current;\n\t\tif (!container) {\n\t\t\treturn;\n\t\t}\n\n\t\tconst { scrollTop, scrollHeight, clientHeight } = container;\n\t\tconst isScrollable = scrollHeight > clientHeight;\n\t\tconst isAtBottom = Math.abs(scrollHeight - scrollTop - clientHeight) < 5;\n\n\t\tsetShowScrollIndicator(isScrollable && !isAtBottom);\n\t}, []);\n\n\tReact.useEffect(() => {\n\t\tconst container = containerRef.current;\n\t\tif (!container) {\n\t\t\treturn;\n\t\t}\n\n\t\tcheckScroll();\n\n\t\tconst handleScroll = () => {\n\t\t\tcheckScroll();\n\t\t};\n\n\t\tcontainer.addEventListener(\"scroll\", handleScroll, { passive: true });\n\n\t\tconst resizeObserver = new ResizeObserver(() => {\n\t\t\tcheckScroll();\n\t\t});\n\n\t\tresizeObserver.observe(container);\n\n\t\tconst mutationObserver = new MutationObserver(() => {\n\t\t\tcheckScroll();\n\t\t});\n\n\t\tmutationObserver.observe(container, {\n\t\t\tchildList: true,\n\t\t\tsubtree: true,\n\t\t\tcharacterData: true,\n\t\t});\n\n\t\treturn () => {\n\t\t\tcontainer.removeEventListener(\"scroll\", handleScroll);\n\t\t\tresizeObserver.disconnect();\n\t\t\tmutationObserver.disconnect();\n\t\t};\n\t}, [checkScroll]);\n\n\t// Track when Floating UI has successfully positioned at least once\n\t// Using a ref to persist across renders - once positioned, always valid\n\t// This avoids the bug where (0,0) was wrongly treated as invalid\n\tif (isPositioned) {\n\t\thasEverPositionedRef.current = true;\n\t}\n\n\t// Check if Floating UI has successfully calculated valid positions\n\t// We use the ref to handle legitimate (0,0) positions correctly\n\tconst hasValidFloatingPosition = hasEverPositionedRef.current;\n\n\t// Compute styles based on positioning mode\n\t// Use raw x, y coordinates from Floating UI when available\n\tconst computedStyles = React.useMemo<React.CSSProperties>(() => {\n\t\tif (isMobile) {\n\t\t\t// Mobile: no positioning styles needed, handled by CSS classes\n\t\t\treturn {};\n\t\t}\n\n\t\tif (useFloatingPositioning && hasValidFloatingPosition) {\n\t\t\t// Desktop with Floating UI: use calculated coordinates\n\t\t\t// Using top/left instead of transform to avoid conflicts with motion animations\n\t\t\treturn {\n\t\t\t\tposition: \"fixed\" as const,\n\t\t\t\tleft: x,\n\t\t\t\ttop: y,\n\t\t\t};\n\t\t}\n\n\t\t// Desktop fallback: use static offset styles when Floating UI isn't ready\n\t\treturn getFallbackOffsetStyle(side, sideOffset) ?? {};\n\t}, [\n\t\tisMobile,\n\t\tuseFloatingPositioning,\n\t\thasValidFloatingPosition,\n\t\tx,\n\t\ty,\n\t\tside,\n\t\tsideOffset,\n\t]);\n\n\t// Compute className based on positioning mode\n\tconst computedClassName = cn(\n\t\t// Common base styles\n\t\t\"flex flex-col overflow-hidden overscroll-none bg-co-background\",\n\n\t\t// Mobile: fullscreen fixed\n\t\t\"max-md:fixed max-md:inset-0 max-md:z-[9999]\",\n\n\t\t// Desktop: floating window base styles\n\t\t\"md:z-[9999] md:aspect-[9/17] md:max-h-[calc(100vh-6rem)] md:w-[400px] md:rounded-md md:border md:border-co-border md:shadow md:dark:shadow-co-background-600/50\",\n\n\t\t// Positioning mode specific styles\n\t\t// Use fixed positioning when Floating UI has valid coordinates,\n\t\t// otherwise use fallback absolute positioning with CSS classes\n\t\tuseFloatingPositioning && hasValidFloatingPosition\n\t\t\t? \"md:fixed\"\n\t\t\t: cn(\"md:absolute\", getFallbackPositioningClasses(side, align)),\n\n\t\tclassName\n\t);\n\n\treturn (\n\t\t<SlotProvider>\n\t\t\t<Primitive.Window asChild>\n\t\t\t\t<motion.div\n\t\t\t\t\tanimate=\"visible\"\n\t\t\t\t\tclassName={computedClassName}\n\t\t\t\t\texit=\"exit\"\n\t\t\t\t\tinitial=\"hidden\"\n\t\t\t\t\tref={setFloatingRef}\n\t\t\t\t\tstyle={computedStyles}\n\t\t\t\t\ttransition={{\n\t\t\t\t\t\tdefault: { ease: \"anticipate\" },\n\t\t\t\t\t\tlayout: { duration: 0.3 },\n\t\t\t\t\t}}\n\t\t\t\t\tvariants={{\n\t\t\t\t\t\thidden: { opacity: 0, scale: 0.95, filter: \"blur(6px)\" },\n\t\t\t\t\t\tvisible: { opacity: 1, scale: 1, filter: \"blur(0px)\" },\n\t\t\t\t\t\texit: { opacity: 0, scale: 0.95, filter: \"blur(6px)\" },\n\t\t\t\t\t}}\n\t\t\t\t>\n\t\t\t\t\t<ContentInner\n\t\t\t\t\t\tcontainerRef={containerRef}\n\t\t\t\t\t\tshowScrollIndicator={showScrollIndicator}\n\t\t\t\t\t>\n\t\t\t\t\t\t{children}\n\t\t\t\t\t</ContentInner>\n\t\t\t\t</motion.div>\n\t\t\t</Primitive.Window>\n\t\t</SlotProvider>\n\t);\n};\n\n/**\n * Inner content component that consumes slots.\n * Separated to allow slot context to be established before consuming it.\n */\nconst ContentInner: React.FC<{\n\tchildren: React.ReactNode;\n\tcontainerRef: React.RefObject<HTMLDivElement | null>;\n\tshowScrollIndicator: boolean;\n}> = ({ children, containerRef, showScrollIndicator }) => {\n\tconst { header, footer, hasCustomHeader, hasCustomFooter } = useSlots();\n\n\treturn (\n\t\t<div className=\"relative flex h-full w-full flex-col\">\n\t\t\t{/* Custom header slot */}\n\t\t\t{hasCustomHeader && <div className=\"flex-shrink-0\">{header}</div>}\n\n\t\t\t<div\n\t\t\t\tclassName={cn(\n\t\t\t\t\t\"flex flex-1 flex-col overflow-y-auto\",\n\t\t\t\t\t// Only add top padding if no custom header (default header is absolute positioned)\n\t\t\t\t\t!hasCustomHeader && \"pt-18\"\n\t\t\t\t)}\n\t\t\t\tref={containerRef}\n\t\t\t>\n\t\t\t\t{children}\n\t\t\t</div>\n\n\t\t\t{/* Custom footer slot */}\n\t\t\t{hasCustomFooter && <div className=\"flex-shrink-0\">{footer}</div>}\n\n\t\t\t<AnimatePresence>\n\t\t\t\t{showScrollIndicator && (\n\t\t\t\t\t<>\n\t\t\t\t\t\t<motion.div\n\t\t\t\t\t\t\tanimate={{ opacity: 1 }}\n\t\t\t\t\t\t\tclassName=\"pointer-events-none absolute inset-x-0 bottom-0 z-5 h-32 bg-gradient-to-t from-co-background via-co-background/70 to-transparent\"\n\t\t\t\t\t\t\texit={{ opacity: 0 }}\n\t\t\t\t\t\t\tinitial={{ opacity: 0 }}\n\t\t\t\t\t\t\ttransition={{ duration: 0.3, ease: \"easeInOut\" }}\n\t\t\t\t\t\t/>\n\t\t\t\t\t\t<motion.div\n\t\t\t\t\t\t\tanimate={{ opacity: 0.6 }}\n\t\t\t\t\t\t\tclassName=\"pointer-events-none absolute inset-x-0 bottom-0 z-5 h-48 bg-gradient-to-t from-co-background/80 via-co-background/30 to-transparent\"\n\t\t\t\t\t\t\texit={{ opacity: 0 }}\n\t\t\t\t\t\t\tinitial={{ opacity: 0 }}\n\t\t\t\t\t\t\ttransition={{ duration: 0.4, ease: \"easeInOut\", delay: 0.05 }}\n\t\t\t\t\t\t/>\n\t\t\t\t\t</>\n\t\t\t\t)}\n\t\t\t</AnimatePresence>\n\t\t</div>\n\t);\n};\n"],"mappings":"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"}
1
+ {"version":3,"file":"content.js","names":["React","Content: React.FC<ContentPropsType>","Primitive.Window","ContentInner: React.FC<{\n\tchildren: React.ReactNode;\n\tcontainerRef: React.RefObject<HTMLDivElement | null>;\n\tshowScrollIndicator: boolean;\n}>"],"sources":["../../../src/support/components/content.tsx"],"sourcesContent":["\"use client\";\n\nimport {\n\tautoUpdate,\n\tflip,\n\toffset,\n\ttype Placement,\n\tshift,\n\tuseFloating,\n} from \"@floating-ui/react\";\nimport { AnimatePresence, motion } from \"motion/react\";\nimport * as React from \"react\";\nimport * as Primitive from \"../../primitives\";\nimport { useTriggerRef } from \"../context/positioning\";\nimport { SlotProvider, useSlots } from \"../context/slots\";\nimport { useSupportConfig } from \"../store/support-store\";\nimport type {\n\tAlign,\n\tCollisionPadding,\n\tContentProps as ContentPropsType,\n\tSide,\n} from \"../types\";\nimport { cn } from \"../utils\";\n\nexport type { CollisionPadding, ContentProps } from \"../types\";\n\n// =============================================================================\n// Utils\n// =============================================================================\n\n/**\n * Convert side + align props to Floating UI placement\n */\nfunction getPlacement(side: Side, align: Align): Placement {\n\tif (align === \"center\") {\n\t\treturn side;\n\t}\n\treturn `${side}-${align}` as Placement;\n}\n\n/**\n * Get fallback positioning classes for when Floating UI is not available\n * (e.g., trigger ref not set, or avoidCollisions is false)\n */\nfunction getFallbackPositioningClasses(side: Side, align: Align): string {\n\tconst sideClasses: Record<Side, string> = {\n\t\ttop: \"md:bottom-full md:mb-4\",\n\t\tbottom: \"md:top-full md:mt-4\",\n\t\tleft: \"md:right-full md:mr-4\",\n\t\tright: \"md:left-full md:ml-4\",\n\t};\n\n\tconst alignClasses: Record<Side, Record<Align, string>> = {\n\t\ttop: {\n\t\t\tstart: \"md:left-0\",\n\t\t\tcenter: \"md:left-1/2 md:-translate-x-1/2\",\n\t\t\tend: \"md:right-0\",\n\t\t},\n\t\tbottom: {\n\t\t\tstart: \"md:left-0\",\n\t\t\tcenter: \"md:left-1/2 md:-translate-x-1/2\",\n\t\t\tend: \"md:right-0\",\n\t\t},\n\t\tleft: {\n\t\t\tstart: \"md:top-0\",\n\t\t\tcenter: \"md:top-1/2 md:-translate-y-1/2\",\n\t\t\tend: \"md:bottom-0\",\n\t\t},\n\t\tright: {\n\t\t\tstart: \"md:top-0\",\n\t\t\tcenter: \"md:top-1/2 md:-translate-y-1/2\",\n\t\t\tend: \"md:bottom-0\",\n\t\t},\n\t};\n\n\treturn cn(sideClasses[side], alignClasses[side][align]);\n}\n\n/**\n * Get fallback offset styles for static positioning\n */\nfunction getFallbackOffsetStyle(\n\tside: Side,\n\tsideOffset: number\n): React.CSSProperties | undefined {\n\tif (sideOffset === 16) {\n\t\treturn;\n\t}\n\n\tconst offsetMap: Record<Side, React.CSSProperties> = {\n\t\ttop: { marginBottom: sideOffset },\n\t\tbottom: { marginTop: sideOffset },\n\t\tleft: { marginRight: sideOffset },\n\t\tright: { marginLeft: sideOffset },\n\t};\n\n\treturn offsetMap[side];\n}\n\n// =============================================================================\n// Hook for responsive detection\n// =============================================================================\n\nfunction useIsMobile(): boolean {\n\tconst [isMobile, setIsMobile] = React.useState(false);\n\n\tReact.useEffect(() => {\n\t\tconst mediaQuery = window.matchMedia(\"(max-width: 767px)\");\n\t\tsetIsMobile(mediaQuery.matches);\n\n\t\tconst handler = (event: MediaQueryListEvent) => {\n\t\t\tsetIsMobile(event.matches);\n\t\t};\n\n\t\tmediaQuery.addEventListener(\"change\", handler);\n\t\treturn () => mediaQuery.removeEventListener(\"change\", handler);\n\t}, []);\n\n\treturn isMobile;\n}\n\n// =============================================================================\n// Content Component\n// =============================================================================\n\n/**\n * Content component for the support window.\n * Uses Floating UI for automatic collision detection on desktop.\n * Fullscreen on mobile, floating on desktop.\n *\n * @example\n * // Basic usage (uses defaults: side=\"top\", align=\"end\")\n * <Support.Content />\n *\n * @example\n * // Custom positioning with collision avoidance\n * <Support.Content side=\"bottom\" align=\"start\" sideOffset={24} />\n *\n * @example\n * // Disable collision avoidance for static positioning\n * <Support.Content avoidCollisions={false} />\n *\n * @example\n * // Custom collision padding\n * <Support.Content collisionPadding={{ top: 16, bottom: 32 }} />\n */\nexport const Content: React.FC<ContentPropsType> = ({\n\tclassName,\n\tchildren,\n\tside = \"top\",\n\talign = \"end\",\n\tsideOffset = 16,\n\tavoidCollisions = true,\n\tcollisionPadding = 8,\n}) => {\n\tconst [showScrollIndicator, setShowScrollIndicator] = React.useState(false);\n\tconst containerRef = React.useRef<HTMLDivElement>(null);\n\tconst hasEverPositionedRef = React.useRef(false);\n\tconst isMobile = useIsMobile();\n\tconst triggerRefContext = useTriggerRef();\n\tconst { isOpen } = useSupportConfig();\n\n\t// Set up Floating UI middleware\n\tconst middleware = React.useMemo(() => {\n\t\tconst middlewares = [offset(sideOffset)];\n\n\t\tif (avoidCollisions) {\n\t\t\tmiddlewares.push(\n\t\t\t\tflip({\n\t\t\t\t\tpadding: collisionPadding,\n\t\t\t\t\tfallbackAxisSideDirection: \"start\",\n\t\t\t\t}),\n\t\t\t\tshift({\n\t\t\t\t\tpadding: collisionPadding,\n\t\t\t\t})\n\t\t\t);\n\t\t}\n\n\t\treturn middlewares;\n\t}, [sideOffset, avoidCollisions, collisionPadding]);\n\n\t// Get trigger element from context (stored in state for reactivity)\n\tconst triggerElement = triggerRefContext?.triggerElement ?? null;\n\n\t// Initialize Floating UI with the trigger element as reference\n\t// Using strategy: 'fixed' because Content uses position: fixed (md:fixed class)\n\t// This ensures Floating UI calculates positions relative to the viewport\n\t// The `open` prop synchronizes Floating UI with visibility state for proper autoUpdate\n\tconst { refs, update, x, y, isPositioned } = useFloating({\n\t\tplacement: getPlacement(side, align),\n\t\tstrategy: \"fixed\",\n\t\tmiddleware,\n\t\twhileElementsMounted: autoUpdate,\n\t\topen: isOpen,\n\t\telements: {\n\t\t\treference: triggerElement,\n\t\t},\n\t});\n\n\t// Merge refs for the floating element - ensures proper ref forwarding with motion.div\n\tconst setFloatingRef = React.useCallback(\n\t\t(node: HTMLDivElement | null) => {\n\t\t\trefs.setFloating(node);\n\t\t},\n\t\t[refs]\n\t);\n\n\t// Force position recalculation when trigger element becomes available\n\t// This handles the case where content mounts before trigger\n\tReact.useEffect(() => {\n\t\tif (triggerElement && isOpen) {\n\t\t\t// Defer update to ensure DOM is ready\n\t\t\trequestAnimationFrame(() => {\n\t\t\t\tupdate();\n\t\t\t});\n\t\t}\n\t}, [triggerElement, isOpen, update]);\n\n\t// Determine if we should use Floating UI positioning\n\t// Only use Floating UI when trigger element is available\n\tconst useFloatingPositioning =\n\t\tavoidCollisions && !isMobile && triggerElement !== null;\n\n\t// Scroll indicator logic\n\tconst checkScroll = React.useCallback(() => {\n\t\tconst container = containerRef.current;\n\t\tif (!container) {\n\t\t\treturn;\n\t\t}\n\n\t\tconst { scrollTop, scrollHeight, clientHeight } = container;\n\t\tconst isScrollable = scrollHeight > clientHeight;\n\t\tconst isAtBottom = Math.abs(scrollHeight - scrollTop - clientHeight) < 5;\n\n\t\tsetShowScrollIndicator(isScrollable && !isAtBottom);\n\t}, []);\n\n\tReact.useEffect(() => {\n\t\tconst container = containerRef.current;\n\t\tif (!container) {\n\t\t\treturn;\n\t\t}\n\n\t\tcheckScroll();\n\n\t\tconst handleScroll = () => {\n\t\t\tcheckScroll();\n\t\t};\n\n\t\tcontainer.addEventListener(\"scroll\", handleScroll, { passive: true });\n\n\t\tconst resizeObserver = new ResizeObserver(() => {\n\t\t\tcheckScroll();\n\t\t});\n\n\t\tresizeObserver.observe(container);\n\n\t\tconst mutationObserver = new MutationObserver(() => {\n\t\t\tcheckScroll();\n\t\t});\n\n\t\tmutationObserver.observe(container, {\n\t\t\tchildList: true,\n\t\t\tsubtree: true,\n\t\t\tcharacterData: true,\n\t\t});\n\n\t\treturn () => {\n\t\t\tcontainer.removeEventListener(\"scroll\", handleScroll);\n\t\t\tresizeObserver.disconnect();\n\t\t\tmutationObserver.disconnect();\n\t\t};\n\t}, [checkScroll]);\n\n\t// Track when Floating UI has successfully positioned at least once\n\t// Using a ref to persist across renders - once positioned, always valid\n\t// This avoids the bug where (0,0) was wrongly treated as invalid\n\tif (isPositioned) {\n\t\thasEverPositionedRef.current = true;\n\t}\n\n\t// Check if Floating UI has successfully calculated valid positions\n\t// We use the ref to handle legitimate (0,0) positions correctly\n\tconst hasValidFloatingPosition = hasEverPositionedRef.current;\n\n\t// Compute styles based on positioning mode\n\t// Use raw x, y coordinates from Floating UI when available\n\tconst computedStyles = React.useMemo<React.CSSProperties>(() => {\n\t\tif (isMobile) {\n\t\t\t// Mobile: no positioning styles needed, handled by CSS classes\n\t\t\treturn {};\n\t\t}\n\n\t\tif (useFloatingPositioning && hasValidFloatingPosition) {\n\t\t\t// Desktop with Floating UI: use calculated coordinates\n\t\t\t// Using top/left instead of transform to avoid conflicts with motion animations\n\t\t\treturn {\n\t\t\t\tposition: \"fixed\" as const,\n\t\t\t\tleft: x,\n\t\t\t\ttop: y,\n\t\t\t};\n\t\t}\n\n\t\t// Desktop fallback: use static offset styles when Floating UI isn't ready\n\t\treturn getFallbackOffsetStyle(side, sideOffset) ?? {};\n\t}, [\n\t\tisMobile,\n\t\tuseFloatingPositioning,\n\t\thasValidFloatingPosition,\n\t\tx,\n\t\ty,\n\t\tside,\n\t\tsideOffset,\n\t]);\n\n\t// Compute className based on positioning mode\n\tconst computedClassName = cn(\n\t\t// Common base styles\n\t\t\"flex flex-col overflow-hidden overscroll-none bg-co-background\",\n\n\t\t// Mobile: fullscreen fixed\n\t\t\"max-md:fixed max-md:inset-0 max-md:z-[9999]\",\n\n\t\t// Desktop: floating window base styles\n\t\t\"md:z-[9999] md:aspect-[9/17] md:max-h-[calc(100vh-6rem)] md:w-[400px] md:rounded-md md:border md:border-co-border md:shadow md:dark:shadow-co-background-600/50\",\n\n\t\t// Positioning mode specific styles\n\t\t// Use fixed positioning when Floating UI has valid coordinates,\n\t\t// otherwise use fallback absolute positioning with CSS classes\n\t\tuseFloatingPositioning && hasValidFloatingPosition\n\t\t\t? \"md:fixed\"\n\t\t\t: cn(\"md:absolute\", getFallbackPositioningClasses(side, align)),\n\n\t\tclassName\n\t);\n\n\treturn (\n\t\t<SlotProvider>\n\t\t\t<Primitive.Window asChild>\n\t\t\t\t<motion.div\n\t\t\t\t\tanimate=\"visible\"\n\t\t\t\t\tclassName={computedClassName}\n\t\t\t\t\texit=\"exit\"\n\t\t\t\t\tinitial=\"hidden\"\n\t\t\t\t\tref={setFloatingRef}\n\t\t\t\t\tstyle={computedStyles}\n\t\t\t\t\ttransition={{\n\t\t\t\t\t\tdefault: { ease: \"anticipate\" },\n\t\t\t\t\t\tlayout: { duration: 0.3 },\n\t\t\t\t\t}}\n\t\t\t\t\tvariants={{\n\t\t\t\t\t\thidden: { opacity: 0, scale: 0.95, filter: \"blur(6px)\" },\n\t\t\t\t\t\tvisible: { opacity: 1, scale: 1, filter: \"blur(0px)\" },\n\t\t\t\t\t\texit: { opacity: 0, scale: 0.95, filter: \"blur(6px)\" },\n\t\t\t\t\t}}\n\t\t\t\t>\n\t\t\t\t\t<ContentInner\n\t\t\t\t\t\tcontainerRef={containerRef}\n\t\t\t\t\t\tshowScrollIndicator={showScrollIndicator}\n\t\t\t\t\t>\n\t\t\t\t\t\t{children}\n\t\t\t\t\t</ContentInner>\n\t\t\t\t</motion.div>\n\t\t\t</Primitive.Window>\n\t\t</SlotProvider>\n\t);\n};\n\n/**\n * Inner content component that consumes slots.\n * Separated to allow slot context to be established before consuming it.\n */\nconst ContentInner: React.FC<{\n\tchildren: React.ReactNode;\n\tcontainerRef: React.RefObject<HTMLDivElement | null>;\n\tshowScrollIndicator: boolean;\n}> = ({ children, containerRef, showScrollIndicator }) => {\n\tconst { header, footer, hasCustomHeader, hasCustomFooter } = useSlots();\n\n\treturn (\n\t\t<div className=\"relative flex h-full w-full flex-col\">\n\t\t\t{/* Custom header slot */}\n\t\t\t{hasCustomHeader && <div className=\"flex-shrink-0\">{header}</div>}\n\n\t\t\t<div\n\t\t\t\tclassName={cn(\n\t\t\t\t\t\"flex flex-1 flex-col overflow-y-auto\",\n\t\t\t\t\t// Only add top padding if no custom header (default header is absolute positioned)\n\t\t\t\t\t!hasCustomHeader && \"pt-16\"\n\t\t\t\t)}\n\t\t\t\tref={containerRef}\n\t\t\t>\n\t\t\t\t{children}\n\t\t\t</div>\n\n\t\t\t{/* Custom footer slot */}\n\t\t\t{hasCustomFooter && <div className=\"flex-shrink-0\">{footer}</div>}\n\n\t\t\t<AnimatePresence>\n\t\t\t\t{showScrollIndicator && (\n\t\t\t\t\t<>\n\t\t\t\t\t\t<motion.div\n\t\t\t\t\t\t\tanimate={{ opacity: 1 }}\n\t\t\t\t\t\t\tclassName=\"pointer-events-none absolute inset-x-0 bottom-0 z-5 h-32 bg-gradient-to-t from-co-background via-co-background/70 to-transparent\"\n\t\t\t\t\t\t\texit={{ opacity: 0 }}\n\t\t\t\t\t\t\tinitial={{ opacity: 0 }}\n\t\t\t\t\t\t\ttransition={{ duration: 0.3, ease: \"easeInOut\" }}\n\t\t\t\t\t\t/>\n\t\t\t\t\t\t<motion.div\n\t\t\t\t\t\t\tanimate={{ opacity: 0.6 }}\n\t\t\t\t\t\t\tclassName=\"pointer-events-none absolute inset-x-0 bottom-0 z-5 h-48 bg-gradient-to-t from-co-background/80 via-co-background/30 to-transparent\"\n\t\t\t\t\t\t\texit={{ opacity: 0 }}\n\t\t\t\t\t\t\tinitial={{ opacity: 0 }}\n\t\t\t\t\t\t\ttransition={{ duration: 0.4, ease: \"easeInOut\", delay: 0.05 }}\n\t\t\t\t\t\t/>\n\t\t\t\t\t</>\n\t\t\t\t)}\n\t\t\t</AnimatePresence>\n\t\t</div>\n\t);\n};\n"],"mappings":";;;;;;;;;;;;;;;;AAiCA,SAAS,aAAa,MAAY,OAAyB;AAC1D,KAAI,UAAU,SACb,QAAO;AAER,QAAO,GAAG,KAAK,GAAG;;;;;;AAOnB,SAAS,8BAA8B,MAAY,OAAsB;AA+BxE,QAAO,GA9BmC;EACzC,KAAK;EACL,QAAQ;EACR,MAAM;EACN,OAAO;EACP,CAyBqB,OAvBoC;EACzD,KAAK;GACJ,OAAO;GACP,QAAQ;GACR,KAAK;GACL;EACD,QAAQ;GACP,OAAO;GACP,QAAQ;GACR,KAAK;GACL;EACD,MAAM;GACL,OAAO;GACP,QAAQ;GACR,KAAK;GACL;EACD,OAAO;GACN,OAAO;GACP,QAAQ;GACR,KAAK;GACL;EACD,CAEyC,MAAM,OAAO;;;;;AAMxD,SAAS,uBACR,MACA,YACkC;AAClC,KAAI,eAAe,GAClB;AAUD,QAPqD;EACpD,KAAK,EAAE,cAAc,YAAY;EACjC,QAAQ,EAAE,WAAW,YAAY;EACjC,MAAM,EAAE,aAAa,YAAY;EACjC,OAAO,EAAE,YAAY,YAAY;EACjC,CAEgB;;AAOlB,SAAS,cAAuB;CAC/B,MAAM,CAAC,UAAU,eAAeA,QAAM,SAAS,MAAM;AAErD,SAAM,gBAAgB;EACrB,MAAM,aAAa,OAAO,WAAW,qBAAqB;AAC1D,cAAY,WAAW,QAAQ;EAE/B,MAAM,WAAW,UAA+B;AAC/C,eAAY,MAAM,QAAQ;;AAG3B,aAAW,iBAAiB,UAAU,QAAQ;AAC9C,eAAa,WAAW,oBAAoB,UAAU,QAAQ;IAC5D,EAAE,CAAC;AAEN,QAAO;;;;;;;;;;;;;;;;;;;;;;;AA4BR,MAAaC,WAAuC,EACnD,WACA,UACA,OAAO,OACP,QAAQ,OACR,aAAa,IACb,kBAAkB,MAClB,mBAAmB,QACd;CACL,MAAM,CAAC,qBAAqB,0BAA0BD,QAAM,SAAS,MAAM;CAC3E,MAAM,eAAeA,QAAM,OAAuB,KAAK;CACvD,MAAM,uBAAuBA,QAAM,OAAO,MAAM;CAChD,MAAM,WAAW,aAAa;CAC9B,MAAM,oBAAoB,eAAe;CACzC,MAAM,EAAE,WAAW,kBAAkB;CAGrC,MAAM,aAAaA,QAAM,cAAc;EACtC,MAAM,cAAc,CAAC,OAAO,WAAW,CAAC;AAExC,MAAI,gBACH,aAAY,KACX,KAAK;GACJ,SAAS;GACT,2BAA2B;GAC3B,CAAC,EACF,MAAM,EACL,SAAS,kBACT,CAAC,CACF;AAGF,SAAO;IACL;EAAC;EAAY;EAAiB;EAAiB,CAAC;CAGnD,MAAM,iBAAiB,mBAAmB,kBAAkB;CAM5D,MAAM,EAAE,MAAM,QAAQ,GAAG,GAAG,iBAAiB,YAAY;EACxD,WAAW,aAAa,MAAM,MAAM;EACpC,UAAU;EACV;EACA,sBAAsB;EACtB,MAAM;EACN,UAAU,EACT,WAAW,gBACX;EACD,CAAC;CAGF,MAAM,iBAAiBA,QAAM,aAC3B,SAAgC;AAChC,OAAK,YAAY,KAAK;IAEvB,CAAC,KAAK,CACN;AAID,SAAM,gBAAgB;AACrB,MAAI,kBAAkB,OAErB,6BAA4B;AAC3B,WAAQ;IACP;IAED;EAAC;EAAgB;EAAQ;EAAO,CAAC;CAIpC,MAAM,yBACL,mBAAmB,CAAC,YAAY,mBAAmB;CAGpD,MAAM,cAAcA,QAAM,kBAAkB;EAC3C,MAAM,YAAY,aAAa;AAC/B,MAAI,CAAC,UACJ;EAGD,MAAM,EAAE,WAAW,cAAc,iBAAiB;EAClD,MAAM,eAAe,eAAe;EACpC,MAAM,aAAa,KAAK,IAAI,eAAe,YAAY,aAAa,GAAG;AAEvE,yBAAuB,gBAAgB,CAAC,WAAW;IACjD,EAAE,CAAC;AAEN,SAAM,gBAAgB;EACrB,MAAM,YAAY,aAAa;AAC/B,MAAI,CAAC,UACJ;AAGD,eAAa;EAEb,MAAM,qBAAqB;AAC1B,gBAAa;;AAGd,YAAU,iBAAiB,UAAU,cAAc,EAAE,SAAS,MAAM,CAAC;EAErE,MAAM,iBAAiB,IAAI,qBAAqB;AAC/C,gBAAa;IACZ;AAEF,iBAAe,QAAQ,UAAU;EAEjC,MAAM,mBAAmB,IAAI,uBAAuB;AACnD,gBAAa;IACZ;AAEF,mBAAiB,QAAQ,WAAW;GACnC,WAAW;GACX,SAAS;GACT,eAAe;GACf,CAAC;AAEF,eAAa;AACZ,aAAU,oBAAoB,UAAU,aAAa;AACrD,kBAAe,YAAY;AAC3B,oBAAiB,YAAY;;IAE5B,CAAC,YAAY,CAAC;AAKjB,KAAI,aACH,sBAAqB,UAAU;CAKhC,MAAM,2BAA2B,qBAAqB;CAItD,MAAM,iBAAiBA,QAAM,cAAmC;AAC/D,MAAI,SAEH,QAAO,EAAE;AAGV,MAAI,0BAA0B,yBAG7B,QAAO;GACN,UAAU;GACV,MAAM;GACN,KAAK;GACL;AAIF,SAAO,uBAAuB,MAAM,WAAW,IAAI,EAAE;IACnD;EACF;EACA;EACA;EACA;EACA;EACA;EACA;EACA,CAAC;CAGF,MAAM,oBAAoB,GAEzB,kEAGA,+CAGA,mKAKA,0BAA0B,2BACvB,aACA,GAAG,eAAe,8BAA8B,MAAM,MAAM,CAAC,EAEhE,UACA;AAED,QACC,oBAAC,0BACA,oBAACE;EAAiB;YACjB,oBAAC,OAAO;GACP,SAAQ;GACR,WAAW;GACX,MAAK;GACL,SAAQ;GACR,KAAK;GACL,OAAO;GACP,YAAY;IACX,SAAS,EAAE,MAAM,cAAc;IAC/B,QAAQ,EAAE,UAAU,IAAK;IACzB;GACD,UAAU;IACT,QAAQ;KAAE,SAAS;KAAG,OAAO;KAAM,QAAQ;KAAa;IACxD,SAAS;KAAE,SAAS;KAAG,OAAO;KAAG,QAAQ;KAAa;IACtD,MAAM;KAAE,SAAS;KAAG,OAAO;KAAM,QAAQ;KAAa;IACtD;aAED,oBAAC;IACc;IACO;IAEpB;KACa;IACH;GACK,GACL;;;;;;AAQjB,MAAMC,gBAIA,EAAE,UAAU,cAAc,0BAA0B;CACzD,MAAM,EAAE,QAAQ,QAAQ,iBAAiB,oBAAoB,UAAU;AAEvE,QACC,qBAAC;EAAI,WAAU;;GAEb,mBAAmB,oBAAC;IAAI,WAAU;cAAiB;KAAa;GAEjE,oBAAC;IACA,WAAW,GACV,wCAEA,CAAC,mBAAmB,QACpB;IACD,KAAK;IAEJ;KACI;GAGL,mBAAmB,oBAAC;IAAI,WAAU;cAAiB;KAAa;GAEjE,oBAAC,6BACC,uBACA,4CACC,oBAAC,OAAO;IACP,SAAS,EAAE,SAAS,GAAG;IACvB,WAAU;IACV,MAAM,EAAE,SAAS,GAAG;IACpB,SAAS,EAAE,SAAS,GAAG;IACvB,YAAY;KAAE,UAAU;KAAK,MAAM;KAAa;KAC/C,EACF,oBAAC,OAAO;IACP,SAAS,EAAE,SAAS,IAAK;IACzB,WAAU;IACV,MAAM,EAAE,SAAS,GAAG;IACpB,SAAS,EAAE,SAAS,GAAG;IACvB,YAAY;KAAE,UAAU;KAAK,MAAM;KAAa,OAAO;KAAM;KAC5D,IACA,GAEa;;GACb"}
@@ -1,12 +1,12 @@
1
1
  import { cn } from "../utils/index.js";
2
2
  import { Avatar } from "./avatar.js";
3
3
  import { BouncingDots } from "./typing-indicator.js";
4
- import icons_default from "./icons.js";
5
4
  import { coButtonVariants } from "./button.js";
5
+ import icons_default from "./icons.js";
6
6
  import { useSupportText } from "../text/index.js";
7
7
  import { useConversationPreview } from "../../hooks/use-conversation-preview.js";
8
8
  import { ConversationStatus } from "@cossistant/types";
9
- import { Fragment, jsx, jsxs } from "react/jsx-runtime";
9
+ import { jsx, jsxs } from "react/jsx-runtime";
10
10
 
11
11
  //#region src/support/components/conversation-button-link.tsx
12
12
  const STATUS_BADGE_CLASSNAMES = {
@@ -14,7 +14,6 @@ const STATUS_BADGE_CLASSNAMES = {
14
14
  [ConversationStatus.RESOLVED]: "bg-co-neutral/20 text-co-neutral-foreground",
15
15
  [ConversationStatus.SPAM]: "bg-co-warning/20 text-co-warning-foreground"
16
16
  };
17
- const DEFAULT_STATUS_BADGE_CLASSNAME = "bg-co-neutral/20 text-co-neutral-foreground";
18
17
  const STATUS_TEXT_KEYS = {
19
18
  [ConversationStatus.OPEN]: "component.conversationButtonLink.status.open",
20
19
  [ConversationStatus.RESOLVED]: "component.conversationButtonLink.status.resolved",
@@ -28,42 +27,38 @@ function ConversationButtonLink({ conversation, onClick, className }) {
28
27
  const preview = useConversationPreview({ conversation });
29
28
  const text = useSupportText();
30
29
  const { lastMessage, assignedAgent, typing } = preview;
31
- const statusBadgeClassName = conversation.deletedAt ? STATUS_BADGE_CLASSNAMES[ConversationStatus.RESOLVED] : STATUS_BADGE_CLASSNAMES[conversation.status] ?? DEFAULT_STATUS_BADGE_CLASSNAME;
30
+ conversation.deletedAt ? STATUS_BADGE_CLASSNAMES[ConversationStatus.RESOLVED] : STATUS_BADGE_CLASSNAMES[conversation.status];
32
31
  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;
32
+ conversation.deletedAt ? text("component.conversationButtonLink.status.closed") : statusTextKey ? text(statusTextKey) : text("common.fallbacks.unknown");
33
+ const displayTitle = conversation.title || preview.title;
34
+ const messagePreview = lastMessage?.content || null;
38
35
  return /* @__PURE__ */ jsxs("button", {
39
36
  className: cn(coButtonVariants({
40
37
  variant: "secondary",
41
38
  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),
39
+ }), "group/btn relative gap-2 border-0 border-co-border/50 border-b pr-3 pl-3 text-left transition-colors first-of-type:rounded-t last-of-type:rounded-b last-of-type:border-b-0 has-[>svg]:pl-3", className),
43
40
  onClick,
44
41
  type: "button",
45
42
  children: [
46
43
  /* @__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",
44
+ className: "size-8 flex-shrink-0",
48
45
  image: assignedAgent?.image,
46
+ isAI: assignedAgent?.type === "ai",
47
+ lastSeenAt: assignedAgent?.lastSeenAt,
49
48
  name: assignedAgent?.name ?? text("common.fallbacks.supportTeam")
50
49
  }),
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", {
50
+ /* @__PURE__ */ jsxs("div", {
51
+ className: "mr-6 ml-1 flex min-w-0 flex-1 flex-col gap-0.5",
52
+ children: [/* @__PURE__ */ jsx("div", {
54
53
  className: "flex max-w-[90%] items-center justify-between gap-2",
55
54
  children: /* @__PURE__ */ jsx("h3", {
56
55
  className: "truncate font-medium text-co-primary text-sm",
57
- children: preview.title
56
+ children: displayTitle
58
57
  })
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
58
+ }), typing.isTyping ? /* @__PURE__ */ jsx(BouncingDots, {}) : messagePreview ? /* @__PURE__ */ jsx("p", {
59
+ className: "truncate text-co-primary/60 text-sm",
60
+ children: messagePreview
61
+ }) : null]
67
62
  }),
68
63
  /* @__PURE__ */ jsx(icons_default, {
69
64
  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",
@@ -1 +1 @@
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
+ {"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\t// Show the actual title if it exists, otherwise use the preview title (which may fallback to message)\n\tconst displayTitle = conversation.title || preview.title;\n\n\t// Show the truncated message content as secondary text\n\tconst messagePreview = lastMessage?.content || null;\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-2 border-0 border-co-border/50 border-b pr-3 pl-3 text-left transition-colors first-of-type:rounded-t last-of-type:rounded-b last-of-type:border-b-0 has-[>svg]:pl-3\",\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=\"size-8 flex-shrink-0\"\n\t\t\t\timage={assignedAgent?.image}\n\t\t\t\tisAI={assignedAgent?.type === \"ai\"}\n\t\t\t\tlastSeenAt={assignedAgent?.lastSeenAt}\n\t\t\t\tname={assignedAgent?.name ?? text(\"common.fallbacks.supportTeam\")}\n\t\t\t/>\n\n\t\t\t<div className=\"mr-6 ml-1 flex min-w-0 flex-1 flex-col gap-0.5\">\n\t\t\t\t<div className=\"flex max-w-[90%] items-center justify-between gap-2\">\n\t\t\t\t\t<h3 className=\"truncate font-medium text-co-primary text-sm\">\n\t\t\t\t\t\t{displayTitle}\n\t\t\t\t\t</h3>\n\t\t\t\t</div>\n\t\t\t\t{typing.isTyping ? (\n\t\t\t\t\t<BouncingDots />\n\t\t\t\t) : messagePreview ? (\n\t\t\t\t\t<p className=\"truncate text-co-primary/60 text-sm\">\n\t\t\t\t\t\t{messagePreview}\n\t\t\t\t\t</p>\n\t\t\t\t) : null}\n\t\t\t</div>\n\n\t\t\t{/* <div\n className={cn(\n \"mr-6 inline-flex items-center rounded px-2 py-0.5 font-medium text-[9px] uppercase\",\n statusBadgeClassName,\n )}\n >\n {statusText}\n </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;AAKD,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;AAElB,cAAa,YACvC,wBAAwB,mBAAmB,YAC1C,wBAAwB,aAAa;CAGzC,MAAM,gBAAgB,aAAa,YAChC,iBAAiB,mBAAmB,YACpC,iBAAiB,aAAa;AAEd,cAAa,YAC7B,KAAK,iDAAiD,GACtD,gBACC,KAAK,cAAc,GACnB,KAAK,2BAA2B;CAGpC,MAAM,eAAe,aAAa,SAAS,QAAQ;CAGnD,MAAM,iBAAiB,aAAa,WAAW;AAE/C,QACC,qBAAC;EACA,WAAW,GACV,iBAAiB;GAChB,SAAS;GACT,MAAM;GACN,CAAC,EACF,+LACA,UACA;EACQ;EACT,MAAK;;GAEL,oBAAC;IACA,WAAU;IACV,OAAO,eAAe;IACtB,MAAM,eAAe,SAAS;IAC9B,YAAY,eAAe;IAC3B,MAAM,eAAe,QAAQ,KAAK,+BAA+B;KAChE;GAEF,qBAAC;IAAI,WAAU;eACd,oBAAC;KAAI,WAAU;eACd,oBAAC;MAAG,WAAU;gBACZ;OACG;MACA,EACL,OAAO,WACP,oBAAC,iBAAe,GACb,iBACH,oBAAC;KAAE,WAAU;eACX;MACE,GACD;KACC;GAWN,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;EAC1B,oBAAA,EAEe,mBAFf,EAAA;EACY,SAAA,CAAA,EAAA,MAAA;CACG;AAAmB,cAI7B,iBAJ6B,EAIV,KAAA,CAAM,EAJI,CAID,sBAJC,CAAA"}
1
+ {"version":3,"file":"conversation-event.d.ts","names":[],"sources":["../../../src/support/components/conversation-event.tsx"],"sourcesContent":[],"mappings":";;;;KAUY,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"}
@@ -1,5 +1,4 @@
1
1
  import { Avatar } from "./avatar.js";
2
- import { CossistantLogo } from "./cossistant-branding.js";
3
2
  import { useSupportText } from "../text/index.js";
4
3
  import { jsx, jsxs } from "react/jsx-runtime";
5
4
  import { motion } from "motion/react";
@@ -49,11 +48,14 @@ const ConversationEvent = ({ event, availableAIAgents, createdAt, availableHuman
49
48
  children: [
50
49
  /* @__PURE__ */ jsx("div", {
51
50
  className: "flex flex-col justify-end",
52
- children: isAI ? /* @__PURE__ */ jsx("div", {
53
- className: "flex size-5 items-center justify-center rounded-full bg-co-primary/10",
54
- children: /* @__PURE__ */ jsx(CossistantLogo, { className: "h-3 w-3 text-co-primary" })
51
+ children: isAI ? /* @__PURE__ */ jsx(Avatar, {
52
+ className: "size-5 flex-shrink-0",
53
+ image: aiAgent?.image,
54
+ isAI: true,
55
+ name: aiAgent?.name || text("common.fallbacks.cossistant"),
56
+ showBackground: !!aiAgent?.image
55
57
  }) : /* @__PURE__ */ jsx(Avatar, {
56
- className: "size-5 flex-shrink-0 overflow-clip rounded-full",
58
+ className: "size-5 flex-shrink-0 overflow-clip rounded-lg",
57
59
  image: humanAgent?.image,
58
60
  name: humanAgent?.name || text("common.fallbacks.someone")
59
61
  })
@@ -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\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 pt-4 pb-8\"\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-co-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-co-primary/10\">\n\t\t\t\t\t\t\t<CossistantLogo className=\"h-3 w-3 text-co-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,4BAA4B;OACjD,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"}
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\";\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 pt-4 pb-8\"\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-co-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<Avatar\n\t\t\t\t\t\t\tclassName=\"size-5 flex-shrink-0\"\n\t\t\t\t\t\t\timage={aiAgent?.image}\n\t\t\t\t\t\t\tisAI\n\t\t\t\t\t\t\tname={aiAgent?.name || text(\"common.fallbacks.cossistant\")}\n\t\t\t\t\t\t\tshowBackground={!!aiAgent?.image}\n\t\t\t\t\t\t/>\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-lg\"\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":";;;;;;AAiBA,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;MACA,WAAU;MACV,OAAO,SAAS;MAChB;MACA,MAAM,SAAS,QAAQ,KAAK,8BAA8B;MAC1D,gBAAgB,CAAC,CAAC,SAAS;OAC1B,GAEF,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"}
@@ -0,0 +1,21 @@
1
+ import { ConversationStatus } from "@cossistant/types";
2
+ import * as react_jsx_runtime0 from "react/jsx-runtime";
3
+
4
+ //#region src/support/components/conversation-resolved-feedback.d.ts
5
+ type ConversationResolvedFeedbackProps = {
6
+ status: ConversationStatus | null;
7
+ rating: number | null;
8
+ onRate?: (rating: number) => void;
9
+ isSubmitting?: boolean;
10
+ className?: string;
11
+ };
12
+ declare function ConversationResolvedFeedback({
13
+ status,
14
+ rating,
15
+ onRate,
16
+ isSubmitting,
17
+ className
18
+ }: ConversationResolvedFeedbackProps): react_jsx_runtime0.JSX.Element;
19
+ //#endregion
20
+ export { ConversationResolvedFeedback };
21
+ //# sourceMappingURL=conversation-resolved-feedback.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"conversation-resolved-feedback.d.ts","names":[],"sources":["../../../src/support/components/conversation-resolved-feedback.tsx"],"sourcesContent":[],"mappings":";;;;KAKK,iCAAA;UACI;;EADJ,MAAA,CAAA,EAAA,CAAA,MAAA,EAAA,MAAA,EAAA,GAAA,IAAA;EAUW,YAAA,CAAA,EAAA,OAAA;EACf,SAAA,CAAA,EAAA,MAAA;CACA;AACA,iBAHe,4BAAA,CAGf;EAAA,MAAA;EAAA,MAAA;EAAA,MAAA;EAAA,YAAA;EAAA;AAAA,CAAA,EAGE,iCAHF,CAAA,EAGmC,kBAAA,CAAA,GAAA,CAAA,OAHnC"}
@@ -0,0 +1,59 @@
1
+ import { cn } from "../utils/index.js";
2
+ import icons_default from "./icons.js";
3
+ import { Text, useSupportText } from "../text/index.js";
4
+ import { ConversationStatus } from "@cossistant/types";
5
+ import { jsx, jsxs } from "react/jsx-runtime";
6
+
7
+ //#region src/support/components/conversation-resolved-feedback.tsx
8
+ const STAR_COUNT = 5;
9
+ function ConversationResolvedFeedback({ status, rating, onRate, isSubmitting = false, className }) {
10
+ const text = useSupportText();
11
+ const isResolved = status === ConversationStatus.RESOLVED;
12
+ const isRated = rating != null;
13
+ const isInteractive = Boolean(onRate) && !isSubmitting && !isRated;
14
+ if (!isResolved) return /* @__PURE__ */ jsx("div", {
15
+ className: cn("m-4 flex items-center justify-center text-balance px-4 pb-6 text-center font-medium text-co-muted-foreground text-sm", className),
16
+ children: /* @__PURE__ */ jsx(Text, {
17
+ as: "p",
18
+ textKey: "component.conversationPage.closedMessage"
19
+ })
20
+ });
21
+ return /* @__PURE__ */ jsxs("div", {
22
+ className: cn("m-4 rounded-md border border-co-border/60 bg-co-background-100 px-4 py-3 text-center text-sm shadow-sm", className),
23
+ children: [
24
+ /* @__PURE__ */ jsx(Text, {
25
+ as: "p",
26
+ className: "font-medium text-co-foreground",
27
+ textKey: isRated ? "component.conversationPage.ratingThanks" : "component.conversationPage.ratingPrompt"
28
+ }),
29
+ /* @__PURE__ */ jsx("div", {
30
+ className: "mt-2 flex items-center justify-center gap-1",
31
+ children: Array.from({ length: STAR_COUNT }).map((_, index) => {
32
+ const value = index + 1;
33
+ const isFilled = rating ? value <= rating : false;
34
+ return /* @__PURE__ */ jsx("button", {
35
+ "aria-label": text("component.conversationPage.ratingLabel", { rating: value }),
36
+ className: cn("group inline-flex h-8 w-8 items-center justify-center rounded-md transition-colors", isInteractive ? "hover:bg-co-muted" : "cursor-default opacity-70"),
37
+ disabled: !isInteractive,
38
+ onClick: () => onRate?.(value),
39
+ type: "button",
40
+ children: /* @__PURE__ */ jsx(icons_default, {
41
+ className: "h-4 w-4 text-co-primary",
42
+ name: "star",
43
+ variant: isFilled ? "filled" : "default"
44
+ })
45
+ }, value);
46
+ })
47
+ }),
48
+ /* @__PURE__ */ jsx(Text, {
49
+ as: "p",
50
+ className: "mt-2 text-co-muted-foreground text-xs",
51
+ textKey: "component.conversationPage.closedMessage"
52
+ })
53
+ ]
54
+ });
55
+ }
56
+
57
+ //#endregion
58
+ export { ConversationResolvedFeedback };
59
+ //# sourceMappingURL=conversation-resolved-feedback.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"conversation-resolved-feedback.js","names":["Icon"],"sources":["../../../src/support/components/conversation-resolved-feedback.tsx"],"sourcesContent":["import { ConversationStatus } from \"@cossistant/types\";\nimport { Text, useSupportText } from \"../text\";\nimport { cn } from \"../utils\";\nimport Icon from \"./icons\";\n\ntype ConversationResolvedFeedbackProps = {\n\tstatus: ConversationStatus | null;\n\trating: number | null;\n\tonRate?: (rating: number) => void;\n\tisSubmitting?: boolean;\n\tclassName?: string;\n};\n\nconst STAR_COUNT = 5;\n\nexport function ConversationResolvedFeedback({\n\tstatus,\n\trating,\n\tonRate,\n\tisSubmitting = false,\n\tclassName,\n}: ConversationResolvedFeedbackProps) {\n\tconst text = useSupportText();\n\tconst isResolved = status === ConversationStatus.RESOLVED;\n\tconst isRated = rating != null;\n\tconst isInteractive = Boolean(onRate) && !isSubmitting && !isRated;\n\n\tif (!isResolved) {\n\t\treturn (\n\t\t\t<div\n\t\t\t\tclassName={cn(\n\t\t\t\t\t\"m-4 flex items-center justify-center text-balance px-4 pb-6 text-center font-medium text-co-muted-foreground text-sm\",\n\t\t\t\t\tclassName\n\t\t\t\t)}\n\t\t\t>\n\t\t\t\t<Text as=\"p\" textKey=\"component.conversationPage.closedMessage\" />\n\t\t\t</div>\n\t\t);\n\t}\n\n\treturn (\n\t\t<div\n\t\t\tclassName={cn(\n\t\t\t\t\"m-4 rounded-md border border-co-border/60 bg-co-background-100 px-4 py-3 text-center text-sm shadow-sm\",\n\t\t\t\tclassName\n\t\t\t)}\n\t\t>\n\t\t\t<Text\n\t\t\t\tas=\"p\"\n\t\t\t\tclassName=\"font-medium text-co-foreground\"\n\t\t\t\ttextKey={\n\t\t\t\t\tisRated\n\t\t\t\t\t\t? \"component.conversationPage.ratingThanks\"\n\t\t\t\t\t\t: \"component.conversationPage.ratingPrompt\"\n\t\t\t\t}\n\t\t\t/>\n\t\t\t<div className=\"mt-2 flex items-center justify-center gap-1\">\n\t\t\t\t{Array.from({ length: STAR_COUNT }).map((_, index) => {\n\t\t\t\t\tconst value = index + 1;\n\t\t\t\t\tconst isFilled = rating ? value <= rating : false;\n\n\t\t\t\t\treturn (\n\t\t\t\t\t\t<button\n\t\t\t\t\t\t\taria-label={text(\"component.conversationPage.ratingLabel\", {\n\t\t\t\t\t\t\t\trating: value,\n\t\t\t\t\t\t\t})}\n\t\t\t\t\t\t\tclassName={cn(\n\t\t\t\t\t\t\t\t\"group inline-flex h-8 w-8 items-center justify-center rounded-md transition-colors\",\n\t\t\t\t\t\t\t\tisInteractive\n\t\t\t\t\t\t\t\t\t? \"hover:bg-co-muted\"\n\t\t\t\t\t\t\t\t\t: \"cursor-default opacity-70\"\n\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\tdisabled={!isInteractive}\n\t\t\t\t\t\t\tkey={value}\n\t\t\t\t\t\t\tonClick={() => onRate?.(value)}\n\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t>\n\t\t\t\t\t\t\t<Icon\n\t\t\t\t\t\t\t\tclassName=\"h-4 w-4 text-co-primary\"\n\t\t\t\t\t\t\t\tname=\"star\"\n\t\t\t\t\t\t\t\tvariant={isFilled ? \"filled\" : \"default\"}\n\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t</button>\n\t\t\t\t\t);\n\t\t\t\t})}\n\t\t\t</div>\n\t\t\t<Text\n\t\t\t\tas=\"p\"\n\t\t\t\tclassName=\"mt-2 text-co-muted-foreground text-xs\"\n\t\t\t\ttextKey=\"component.conversationPage.closedMessage\"\n\t\t\t/>\n\t\t</div>\n\t);\n}\n"],"mappings":";;;;;;;AAaA,MAAM,aAAa;AAEnB,SAAgB,6BAA6B,EAC5C,QACA,QACA,QACA,eAAe,OACf,aACqC;CACrC,MAAM,OAAO,gBAAgB;CAC7B,MAAM,aAAa,WAAW,mBAAmB;CACjD,MAAM,UAAU,UAAU;CAC1B,MAAM,gBAAgB,QAAQ,OAAO,IAAI,CAAC,gBAAgB,CAAC;AAE3D,KAAI,CAAC,WACJ,QACC,oBAAC;EACA,WAAW,GACV,wHACA,UACA;YAED,oBAAC;GAAK,IAAG;GAAI,SAAQ;IAA6C;GAC7D;AAIR,QACC,qBAAC;EACA,WAAW,GACV,0GACA,UACA;;GAED,oBAAC;IACA,IAAG;IACH,WAAU;IACV,SACC,UACG,4CACA;KAEH;GACF,oBAAC;IAAI,WAAU;cACb,MAAM,KAAK,EAAE,QAAQ,YAAY,CAAC,CAAC,KAAK,GAAG,UAAU;KACrD,MAAM,QAAQ,QAAQ;KACtB,MAAM,WAAW,SAAS,SAAS,SAAS;AAE5C,YACC,oBAAC;MACA,cAAY,KAAK,0CAA0C,EAC1D,QAAQ,OACR,CAAC;MACF,WAAW,GACV,sFACA,gBACG,sBACA,4BACH;MACD,UAAU,CAAC;MAEX,eAAe,SAAS,MAAM;MAC9B,MAAK;gBAEL,oBAACA;OACA,WAAU;OACV,MAAK;OACL,SAAS,WAAW,WAAW;QAC9B;QARG,MASG;MAET;KACG;GACN,oBAAC;IACA,IAAG;IACH,WAAU;IACV,SAAQ;KACP;;GACG"}
@@ -0,0 +1,5 @@
1
+ //#region src/support/components/conversation-timeline-utils.d.ts
2
+ declare function filterSeenByIdsForViewer(seenByIds: readonly string[], viewerId?: string): readonly string[];
3
+ //#endregion
4
+ export { filterSeenByIdsForViewer };
5
+ //# sourceMappingURL=conversation-timeline-utils.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"conversation-timeline-utils.d.ts","names":[],"sources":["../../../src/support/components/conversation-timeline-utils.ts"],"sourcesContent":[],"mappings":";iBAAgB,wBAAA"}
@@ -0,0 +1,10 @@
1
+ //#region src/support/components/conversation-timeline-utils.ts
2
+ function filterSeenByIdsForViewer(seenByIds, viewerId) {
3
+ if (!viewerId || seenByIds.length === 0) return seenByIds;
4
+ if (!seenByIds.includes(viewerId)) return seenByIds;
5
+ return seenByIds.filter((id) => id !== viewerId);
6
+ }
7
+
8
+ //#endregion
9
+ export { filterSeenByIdsForViewer };
10
+ //# sourceMappingURL=conversation-timeline-utils.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"conversation-timeline-utils.js","names":[],"sources":["../../../src/support/components/conversation-timeline-utils.ts"],"sourcesContent":["export function filterSeenByIdsForViewer(\n\tseenByIds: readonly string[],\n\tviewerId?: string\n): readonly string[] {\n\tif (!viewerId || seenByIds.length === 0) {\n\t\treturn seenByIds;\n\t}\n\n\tif (!seenByIds.includes(viewerId)) {\n\t\treturn seenByIds;\n\t}\n\n\treturn seenByIds.filter((id) => id !== viewerId);\n}\n"],"mappings":";AAAA,SAAgB,yBACf,WACA,UACoB;AACpB,KAAI,CAAC,YAAY,UAAU,WAAW,EACrC,QAAO;AAGR,KAAI,CAAC,UAAU,SAAS,SAAS,CAChC,QAAO;AAGR,QAAO,UAAU,QAAQ,OAAO,OAAO,SAAS"}
@@ -1,4 +1,4 @@
1
- import { TimelineItem } from "../../timeline-item.js";
1
+ import { TimelineItem } from "../../packages/types/src/api/timeline-item.js";
2
2
  import { DaySeparatorItem } from "../../hooks/private/use-grouped-messages.js";
3
3
  import React from "react";
4
4
  import { AvailableAIAgent, AvailableHumanAgent } from "@cossistant/types";
@@ -1 +1 @@
1
- {"version":3,"file":"conversation-timeline.d.ts","names":[],"sources":["../../../src/support/components/conversation-timeline.tsx"],"sourcesContent":[],"mappings":";;;;;;KAyCY,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;EAED,KAAA,EAPA,YAOA,EAAA;EACa,SAAA,CAAA,EAAA,MAAA;EACd,iBAAM,EAPO,gBAOP,EAAA;EAAS,oBAAA,EANC,mBAMD,EAAA;EAGT,gBAAA,CAAA,EAAA,MAAA;UAPJ;;UAED;uBACa;QACd,KAAA,CAAM;;cAGA,0BAA0B,KAAA,CAAM,GAAG"}
1
+ {"version":3,"file":"conversation-timeline.d.ts","names":[],"sources":["../../../src/support/components/conversation-timeline.tsx"],"sourcesContent":[],"mappings":";;;;;;KA0CY,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;EAED,KAAA,EAPA,YAOA,EAAA;EACa,SAAA,CAAA,EAAA,MAAA;EACd,iBAAM,EAPO,gBAOP,EAAA;EAAS,oBAAA,EANC,mBAMD,EAAA;EAGT,gBAAA,CAAA,EAAA,MAAA;UAPJ;;UAED;uBACa;QACd,KAAA,CAAM;;cAGA,0BAA0B,KAAA,CAAM,GAAG"}
@@ -5,6 +5,7 @@ import { DaySeparator, DaySeparatorLabel, DaySeparatorLine, defaultFormatDate }
5
5
  import { useTypingSound } from "../../hooks/use-typing-sound.js";
6
6
  import { useConversationTimeline } from "../../hooks/use-conversation-timeline.js";
7
7
  import { ConversationEvent } from "./conversation-event.js";
8
+ import { filterSeenByIdsForViewer } from "./conversation-timeline-utils.js";
8
9
  import { TimelineMessageGroup } from "./timeline-message-group.js";
9
10
  import { useCallback, useMemo } from "react";
10
11
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
@@ -67,12 +68,12 @@ const ConversationTimelineList = ({ conversationId, items: timelineItems, classN
67
68
  date: item.date,
68
69
  dateString: item.dateString,
69
70
  children: ({ formattedDate }) => /* @__PURE__ */ jsxs(Fragment, { children: [
70
- /* @__PURE__ */ jsx(DaySeparatorLine, { className: "flex-1 border-gray-200 border-t dark:border-gray-700" }),
71
+ /* @__PURE__ */ jsx(DaySeparatorLine, { className: "flex-1 border-gray-300/20 border-t dark:border-gray-600/20" }),
71
72
  /* @__PURE__ */ jsx(DaySeparatorLabel, {
72
- className: "px-3 text-gray-500 text-xs dark:text-gray-400",
73
+ className: "px-3 text-gray-400/50 text-xs dark:text-gray-500/50",
73
74
  formattedDate
74
75
  }),
75
- /* @__PURE__ */ jsx(DaySeparatorLine, { className: "flex-1 border-gray-200 border-t dark:border-gray-700" })
76
+ /* @__PURE__ */ jsx(DaySeparatorLine, { className: "flex-1 border-gray-300/20 border-t dark:border-gray-600/20" })
76
77
  ] })
77
78
  }, `day-separator-${item.dateString}`);
78
79
  }
@@ -97,7 +98,7 @@ const ConversationTimelineList = ({ conversationId, items: timelineItems, classN
97
98
  item: item.item
98
99
  }, toolKey);
99
100
  }
100
- const seenByIds = index === timeline.lastVisitorMessageGroupIndex && item.lastMessageId ? timeline.groupedMessages.getMessageSeenBy(item.lastMessageId) : EMPTY_SEEN_BY_IDS;
101
+ const seenByIds = filterSeenByIdsForViewer(index === timeline.lastVisitorMessageGroupIndex && item.lastMessageId ? timeline.groupedMessages.getMessageSeenBy(item.lastMessageId) : EMPTY_SEEN_BY_IDS, currentVisitorId);
101
102
  const seenByNames = seenByIds.length > 0 ? getSeenByNames(seenByIds) : EMPTY_SEEN_BY_NAMES;
102
103
  const groupKey = item.lastMessageId ?? item.items?.[0]?.id ?? `group-${item.items?.[0]?.createdAt ?? index}`;
103
104
  return /* @__PURE__ */ jsx(TimelineMessageGroup, {
@@ -1 +1 @@
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 type { DaySeparatorItem } from \"../../hooks/private/use-grouped-messages\";\nimport { useConversationTimeline } from \"../../hooks/use-conversation-timeline\";\nimport { useTypingSound } from \"../../hooks/use-typing-sound\";\nimport {\n\tConversationTimelineContainer,\n\tConversationTimeline as PrimitiveConversationTimeline,\n} from \"../../primitives/conversation-timeline\";\nimport {\n\tDaySeparator,\n\tDaySeparatorLabel,\n\tDaySeparatorLine,\n\tdefaultFormatDate,\n} from \"../../primitives/day-separator\";\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\trenderDaySeparator?: (props: {\n\t\titem: DaySeparatorItem;\n\t\tformatDate: (date: Date) => string;\n\t}) => React.ReactNode;\n};\n\nexport const ConversationTimelineList: React.FC<ConversationTimelineProps> = ({\n\tconversationId,\n\titems: timelineItems,\n\tclassName,\n\tavailableAIAgents = [],\n\tavailableHumanAgents = [],\n\tcurrentVisitorId,\n\ttools,\n\trenderDaySeparator,\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\t// Play typing sound when someone is typing\n\tuseTypingSound(typingIndicatorParticipants.length > 0, {\n\t\tvolume: 1,\n\t\tplaybackRate: 1.3,\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-scroll px-3 py-6\",\n\t\t\t\t\"co-scrollbar-thin\",\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-5\">\n\t\t\t\t{timeline.groupedMessages.items.map((item, index) => {\n\t\t\t\t\tif (item.type === \"day_separator\") {\n\t\t\t\t\t\t// Render day separator - allow custom rendering via prop\n\t\t\t\t\t\tif (renderDaySeparator) {\n\t\t\t\t\t\t\treturn (\n\t\t\t\t\t\t\t\t<div key={`day-separator-${item.dateString}`}>\n\t\t\t\t\t\t\t\t\t{renderDaySeparator({\n\t\t\t\t\t\t\t\t\t\titem,\n\t\t\t\t\t\t\t\t\t\tformatDate: defaultFormatDate,\n\t\t\t\t\t\t\t\t\t})}\n\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// Default day separator using the primitive\n\t\t\t\t\t\treturn (\n\t\t\t\t\t\t\t<DaySeparator\n\t\t\t\t\t\t\t\tclassName=\"flex items-center justify-center py-2\"\n\t\t\t\t\t\t\t\tdate={item.date}\n\t\t\t\t\t\t\t\tdateString={item.dateString}\n\t\t\t\t\t\t\t\tkey={`day-separator-${item.dateString}`}\n\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t{({ formattedDate }) => (\n\t\t\t\t\t\t\t\t\t<>\n\t\t\t\t\t\t\t\t\t\t<DaySeparatorLine className=\"flex-1 border-gray-200 border-t dark:border-gray-700\" />\n\t\t\t\t\t\t\t\t\t\t<DaySeparatorLabel\n\t\t\t\t\t\t\t\t\t\t\tclassName=\"px-3 text-gray-500 text-xs dark:text-gray-400\"\n\t\t\t\t\t\t\t\t\t\t\tformattedDate={formattedDate}\n\t\t\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t\t\t<DaySeparatorLine className=\"flex-1 border-gray-200 border-t dark:border-gray-700\" />\n\t\t\t\t\t\t\t\t\t</>\n\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t</DaySeparator>\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\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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"}
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 type { DaySeparatorItem } from \"../../hooks/private/use-grouped-messages\";\nimport { useConversationTimeline } from \"../../hooks/use-conversation-timeline\";\nimport { useTypingSound } from \"../../hooks/use-typing-sound\";\nimport {\n\tConversationTimelineContainer,\n\tConversationTimeline as PrimitiveConversationTimeline,\n} from \"../../primitives/conversation-timeline\";\nimport {\n\tDaySeparator,\n\tDaySeparatorLabel,\n\tDaySeparatorLine,\n\tdefaultFormatDate,\n} from \"../../primitives/day-separator\";\nimport { cn } from \"../utils\";\nimport { ConversationEvent } from \"./conversation-event\";\nimport { filterSeenByIdsForViewer } from \"./conversation-timeline-utils\";\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\trenderDaySeparator?: (props: {\n\t\titem: DaySeparatorItem;\n\t\tformatDate: (date: Date) => string;\n\t}) => React.ReactNode;\n};\n\nexport const ConversationTimelineList: React.FC<ConversationTimelineProps> = ({\n\tconversationId,\n\titems: timelineItems,\n\tclassName,\n\tavailableAIAgents = [],\n\tavailableHumanAgents = [],\n\tcurrentVisitorId,\n\ttools,\n\trenderDaySeparator,\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\t// Play typing sound when someone is typing\n\tuseTypingSound(typingIndicatorParticipants.length > 0, {\n\t\tvolume: 1,\n\t\tplaybackRate: 1.3,\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-scroll px-3 py-6\",\n\t\t\t\t\"co-scrollbar-thin\",\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-5\">\n\t\t\t\t{timeline.groupedMessages.items.map((item, index) => {\n\t\t\t\t\tif (item.type === \"day_separator\") {\n\t\t\t\t\t\t// Render day separator - allow custom rendering via prop\n\t\t\t\t\t\tif (renderDaySeparator) {\n\t\t\t\t\t\t\treturn (\n\t\t\t\t\t\t\t\t<div key={`day-separator-${item.dateString}`}>\n\t\t\t\t\t\t\t\t\t{renderDaySeparator({\n\t\t\t\t\t\t\t\t\t\titem,\n\t\t\t\t\t\t\t\t\t\tformatDate: defaultFormatDate,\n\t\t\t\t\t\t\t\t\t})}\n\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// Default day separator using the primitive\n\t\t\t\t\t\treturn (\n\t\t\t\t\t\t\t<DaySeparator\n\t\t\t\t\t\t\t\tclassName=\"flex items-center justify-center py-2\"\n\t\t\t\t\t\t\t\tdate={item.date}\n\t\t\t\t\t\t\t\tdateString={item.dateString}\n\t\t\t\t\t\t\t\tkey={`day-separator-${item.dateString}`}\n\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t{({ formattedDate }) => (\n\t\t\t\t\t\t\t\t\t<>\n\t\t\t\t\t\t\t\t\t\t<DaySeparatorLine className=\"flex-1 border-gray-300/20 border-t dark:border-gray-600/20\" />\n\t\t\t\t\t\t\t\t\t\t<DaySeparatorLabel\n\t\t\t\t\t\t\t\t\t\t\tclassName=\"px-3 text-gray-400/50 text-xs dark:text-gray-500/50\"\n\t\t\t\t\t\t\t\t\t\t\tformattedDate={formattedDate}\n\t\t\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t\t\t<DaySeparatorLine className=\"flex-1 border-gray-300/20 border-t dark:border-gray-600/20\" />\n\t\t\t\t\t\t\t\t\t</>\n\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t</DaySeparator>\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\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 rawSeenByIds =\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 seenByIds = filterSeenByIdsForViewer(\n\t\t\t\t\t\trawSeenByIds,\n\t\t\t\t\t\tcurrentVisitorId\n\t\t\t\t\t);\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":";;;;;;;;;;;;;AA2BA,SAAS,iBAAiB,MAA8C;AACvE,KAAI,KAAK,SAAS,QACjB,QAAO;AAOR,QAJkB,KAAK,MAAM,MAC3B,SAAoC,KAAK,SAAS,QACnD,IAEmB;;AAGrB,MAAMA,oBAAuC,OAAO,OAAO,EAAE,CAAC;AAC9D,MAAMC,sBAAyC,OAAO,OAAO,EAAE,CAAC;AA8BhE,MAAaC,4BAAiE,EAC7E,gBACA,OAAO,eACP,WACA,oBAAoB,EAAE,EACtB,uBAAuB,EAAE,EACzB,kBACA,OACA,yBACK;CACL,MAAM,WAAW,wBAAwB;EACxC;EACA,OAAO;EACP;EACA,CAAC;CAEF,MAAM,8BAA8B,cAElC,SAAS,mBAAmB,KAAwB,iBAAiB;EACpE,IAAI,YAAY;EAChB,MAAM,YAAY;EAClB,EAAE,EACJ,CAAC,SAAS,mBAAmB,CAC7B;AAGD,gBAAe,4BAA4B,SAAS,GAAG;EACtD,QAAQ;EACR,cAAc;EACd,CAAC;CAEF,MAAM,iBAAiB,cAAc;EACpC,MAAM,sBAAM,IAAI,KAAqB;AAErC,OAAK,MAAM,SAAS,qBACnB,KAAI,MAAM,KACT,KAAI,IAAI,MAAM,IAAI,MAAM,KAAK;AAI/B,OAAK,MAAM,SAAS,kBACnB,KAAI,MAAM,KACT,KAAI,IAAI,MAAM,IAAI,MAAM,KAAK;AAI/B,SAAO;IACL,CAAC,sBAAsB,kBAAkB,CAAC;CAE7C,MAAM,iBAAiB,aACrB,MAAyB,sBAAyC;AAClE,MAAI,IAAI,WAAW,KAAK,eAAe,SAAS,EAC/C,QAAO;EAGR,MAAM,8BAAc,IAAI,KAAa;EACrC,MAAMC,QAAkB,EAAE;AAE1B,OAAK,MAAM,MAAM,KAAK;GACrB,MAAM,OAAO,eAAe,IAAI,GAAG;AACnC,OAAI,CAAC,QAAQ,YAAY,IAAI,KAAK,CACjC;AAGD,eAAY,IAAI,KAAK;AACrB,SAAM,KAAK,KAAK;;AAGjB,MAAI,MAAM,WAAW,EACpB,QAAO;AAGR,SAAO,OAAO,OAAO,MAAM;IAE5B,CAAC,eAAe,CAChB;AAED,QACC,oBAACC;EACA,YAAY;EACZ,WAAW,GACV,+BACA,qBACA,iBACA,UACA;EACD,IAAG;EACH,OAAO;YAEP,qBAAC;GAA8B,WAAU;cACvC,SAAS,gBAAgB,MAAM,KAAK,MAAM,UAAU;AACpD,QAAI,KAAK,SAAS,iBAAiB;AAElC,SAAI,mBACH,QACC,oBAAC,mBACC,mBAAmB;MACnB;MACA,YAAY;MACZ,CAAC,IAJO,iBAAiB,KAAK,aAK1B;AAKR,YACC,oBAAC;MACA,WAAU;MACV,MAAM,KAAK;MACX,YAAY,KAAK;iBAGf,EAAE,oBACH;OACC,oBAAC,oBAAiB,WAAU,+DAA+D;OAC3F,oBAAC;QACA,WAAU;QACK;SACd;OACF,oBAAC,oBAAiB,WAAU,+DAA+D;UACzF;QAVC,iBAAiB,KAAK,aAYb;;AAIjB,QAAI,KAAK,SAAS,kBAAkB;KAEnC,MAAM,YAAY,iBAAiB,KAAK,KAAK;AAG7C,SAAI,CAAC,UACJ,QAAO;AAGR,YACC,oBAAC;MACmB;MACG;MACtB,WAAW,KAAK,KAAK;MACrB,OAAO;QACF,KAAK,KAAK,MAAM,kBAAkB,KAAK,KAAK,YAChD;;AAIJ,QAAI,KAAK,SAAS,iBAAiB;KAClC,MAAM,WAAW,KAAK,QAAQ,KAAK,KAAK,QAAQ,KAAK,KAAK;KAC1D,MAAM,iBAAiB,WAAW,QAAQ,YAAY;AAEtD,SAAI,CAAC,eACJ,QAAO;KAGR,MAAM,gBAAgB,eAAe;KAErC,MAAM,UACL,KAAK,KAAK,MAAM,GAAG,SAAS,GAAG,KAAK,KAAK,UAAU,GAAG;AAEvD,YACC,oBAAC;MACgB;MAChB,MAAM,KAAK;QACN,QACJ;;IAWJ,MAAM,YAAY,yBALjB,UAAU,SAAS,gCAEG,KAAK,gBACxB,SAAS,gBAAgB,iBAAiB,KAAK,cAAc,GAC7D,mBAGH,iBACA;IACD,MAAM,cACL,UAAU,SAAS,IAChB,eAAe,UAAU,GACzB;IAGJ,MAAM,WACL,KAAK,iBACL,KAAK,QAAQ,IAAI,MACjB,SAAS,KAAK,QAAQ,IAAI,aAAa;AAExC,WACC,oBAAC;KACmB;KACG;KACJ;KAClB,OAAO,KAAK,SAAS,EAAE;KAEZ;KACE;OAFR,SAGJ;KAEF,EACF,oBAAC;IAAI,WAAU;cACb,4BAA4B,SAAS,IACrC,oBAAC;KACmB;KACG;KACtB,WAAU;KACV,cAAc;MACb,GACC;KACC;IACyB;GACD"}
@@ -1,7 +1,7 @@
1
1
  import { cn } from "../utils/index.js";
2
2
  import { useSupportConfig } from "../store/support-store.js";
3
- import icons_default from "./icons.js";
4
3
  import { CoButton } from "./button.js";
4
+ import icons_default from "./icons.js";
5
5
  import { jsx, jsxs } from "react/jsx-runtime";
6
6
 
7
7
  //#region src/support/components/header.tsx
@@ -2,7 +2,7 @@ import React from "react";
2
2
 
3
3
  //#region src/support/components/icons.d.ts
4
4
  type IconVariant = "default" | "filled";
5
- type IconName = "sidebar" | "check" | "x" | "clipboard" | "send" | "chevron-down" | "arrow-left" | "arrow-right" | "arrow-up-right" | "attachment" | "file" | "close" | "home" | "articles" | "chat";
5
+ type IconName = "sidebar" | "check" | "x" | "clipboard" | "send" | "chevron-down" | "arrow-left" | "arrow-right" | "arrow-up-right" | "attachment" | "file" | "close" | "home" | "articles" | "chat" | "star";
6
6
  type IconProps = {
7
7
  name: IconName;
8
8
  variant?: IconVariant;
@@ -1 +1 @@
1
- {"version":3,"file":"icons.d.ts","names":[],"sources":["../../../src/support/components/icons.tsx"],"sourcesContent":[],"mappings":";;;KAGY,WAAA;KAEA,QAAA;AAFA,KAmBA,SAAA,GAnBW;EAEX,IAAA,EAkBL,QAlBa;EAiBR,OAAA,CAAA,EAED,WAFU;EA+FR,SAmGZ,CAAA,EAAA,MAAA;EAEW,aAAA,CAAA,EAAA,OAAkB;;cArGjB,MAAM,KAAA,CAAM,GAAG;KAqGhB,kBAAA"}
1
+ {"version":3,"file":"icons.d.ts","names":[],"sources":["../../../src/support/components/icons.tsx"],"sourcesContent":[],"mappings":";;;KAGY,WAAA;KAEA,QAAA;AAFA,KAoBA,SAAA,GApBW;EAEX,IAAA,EAmBL,QAnBa;EAkBR,OAAA,CAAA,EAED,WAFU;EAqGR,SAmGZ,CAAA,EAAA,MAAA;EAEW,aAAA,CAAA,EAAA,OAAkB;;cArGjB,MAAM,KAAA,CAAM,GAAG;KAqGhB,kBAAA"}
@@ -15,6 +15,10 @@ const iconRegistry = {
15
15
  default: "M2 10C2 7.19974 2 5.79961 2.54497 4.73005C3.02433 3.78924 3.78924 3.02433 4.73005 2.54497C5.79961 2 7.19974 2 10 2H14C16.8003 2 18.2004 2 19.27 2.54497C20.2108 3.02433 20.9757 3.78924 21.455 4.73005C22 5.79961 22 7.19974 22 10V19.1708C22 20.1969 22 20.71 21.8373 21.0302C21.5642 21.5676 20.996 21.8893 20.3947 21.847C20.0363 21.8218 19.5964 21.5578 18.7165 21.0299V21.0299C18.1917 20.715 17.9293 20.5576 17.6542 20.4347C17.1972 20.2306 16.7122 20.0962 16.2154 20.0362C15.9163 20 15.6103 20 14.9983 20H10C7.19974 20 5.79961 20 4.73005 19.455C3.78924 18.9757 3.02433 18.2108 2.54497 17.27C2 16.2004 2 14.8003 2 12V10Z",
16
16
  filled: "M9.25 1.25C6.44974 1.25 5.04961 1.25 3.98005 1.79497C3.03924 2.27433 2.27433 3.03924 1.79497 3.98005C1.25 5.04961 1.25 6.44974 1.25 9.25V12.75C1.25 15.5503 1.25 16.9504 1.79497 18.02C2.27433 18.9608 3.03924 19.7257 3.98005 20.205C5.04961 20.75 6.44974 20.75 9.25 20.75H14.5764C15.4012 20.75 15.8135 20.75 16.2144 20.8156C16.5703 20.8738 16.9189 20.9703 17.254 21.1035C17.6315 21.2535 17.9851 21.4657 18.6924 21.8901L19.1152 22.1438C20.3049 22.8576 20.8998 23.2145 21.3892 23.1689C21.816 23.1291 22.2054 22.9086 22.4591 22.5631C22.75 22.1669 22.75 21.4732 22.75 20.0858V9.25C22.75 6.44974 22.75 5.04961 22.205 3.98005C21.7257 3.03924 20.9608 2.27433 20.02 1.79497C18.9504 1.25 17.5503 1.25 14.75 1.25H9.25Z"
17
17
  },
18
+ star: {
19
+ default: "M12 2L14.618 8.472L21.5 9.09L16.25 13.77L17.804 20.5L12 17.1L6.196 20.5L7.75 13.77L2.5 9.09L9.382 8.472L12 2Z",
20
+ filled: "M12 2L14.618 8.472L21.5 9.09L16.25 13.77L17.804 20.5L12 17.1L6.196 20.5L7.75 13.77L2.5 9.09L9.382 8.472L12 2Z"
21
+ },
18
22
  check: {
19
23
  default: "M4 13L9 18L20 6",
20
24
  filled: "M4 13L9 18L20 6"
@@ -86,7 +90,7 @@ const Icon = ({ name, variant = "default", className, filledOnHover }) => {
86
90
  stroke: "currentColor",
87
91
  strokeLinecap: "round",
88
92
  strokeLinejoin: "round",
89
- strokeWidth: "1.5"
93
+ strokeWidth: "2"
90
94
  })]
91
95
  }), /* @__PURE__ */ jsxs("svg", {
92
96
  className: cn("absolute inset-0 h-full w-full opacity-0 transition-opacity duration-200 group-hover/btn:opacity-100", { "opacity-100": variant === "filled" }, className),
@@ -120,7 +124,7 @@ const Icon = ({ name, variant = "default", className, filledOnHover }) => {
120
124
  stroke: "currentColor",
121
125
  strokeLinecap: "round",
122
126
  strokeLinejoin: "round",
123
- strokeWidth: "1.5"
127
+ strokeWidth: "2"
124
128
  })]
125
129
  });
126
130
  };