@cossistant/react 0.0.29 → 0.0.30

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 (280) hide show
  1. package/_virtual/rolldown_runtime.js +9 -23
  2. package/hooks/index.d.ts +2 -2
  3. package/hooks/private/store/use-conversations-store.d.ts +2 -0
  4. package/hooks/private/store/use-conversations-store.d.ts.map +1 -1
  5. package/hooks/private/store/use-conversations-store.js +15 -8
  6. package/hooks/private/store/use-conversations-store.js.map +1 -1
  7. package/hooks/private/store/use-store-selector.d.ts +3 -0
  8. package/hooks/private/store/use-store-selector.d.ts.map +1 -1
  9. package/hooks/private/store/use-store-selector.js +4 -8
  10. package/hooks/private/store/use-store-selector.js.map +1 -1
  11. package/hooks/private/store/use-website-store.d.ts +3 -1
  12. package/hooks/private/store/use-website-store.d.ts.map +1 -1
  13. package/hooks/private/store/use-website-store.js +14 -6
  14. package/hooks/private/store/use-website-store.js.map +1 -1
  15. package/hooks/private/use-client-query.d.ts +1 -1
  16. package/hooks/private/use-client-query.d.ts.map +1 -1
  17. package/hooks/private/use-client-query.js +1 -0
  18. package/hooks/private/use-client-query.js.map +1 -1
  19. package/hooks/private/use-default-messages.d.ts +1 -1
  20. package/hooks/private/use-grouped-messages.d.ts +2 -2
  21. package/hooks/private/use-rest-client.d.ts +13 -3
  22. package/hooks/private/use-rest-client.d.ts.map +1 -1
  23. package/hooks/private/use-rest-client.js +49 -22
  24. package/hooks/private/use-rest-client.js.map +1 -1
  25. package/hooks/private/use-visitor-typing-reporter.d.ts +1 -1
  26. package/hooks/use-conversation-auto-seen.d.ts +1 -1
  27. package/hooks/use-conversation-page.d.ts +1 -1
  28. package/hooks/use-conversation-page.d.ts.map +1 -1
  29. package/hooks/use-conversation-page.js +10 -3
  30. package/hooks/use-conversation-page.js.map +1 -1
  31. package/hooks/use-conversation-preview.d.ts +3 -1
  32. package/hooks/use-conversation-preview.d.ts.map +1 -1
  33. package/hooks/use-conversation-preview.js +6 -3
  34. package/hooks/use-conversation-preview.js.map +1 -1
  35. package/hooks/use-conversation-seen.d.ts +1 -1
  36. package/hooks/use-conversation-timeline-items.d.ts +1 -1
  37. package/hooks/use-conversation-timeline-items.js +2 -3
  38. package/hooks/use-conversation-timeline-items.js.map +1 -1
  39. package/hooks/use-conversation-timeline.d.ts +1 -1
  40. package/hooks/use-conversation.d.ts +1 -1
  41. package/hooks/use-conversation.js +2 -3
  42. package/hooks/use-conversation.js.map +1 -1
  43. package/hooks/use-conversations.d.ts +1 -1
  44. package/hooks/use-conversations.js +5 -3
  45. package/hooks/use-conversations.js.map +1 -1
  46. package/hooks/use-create-conversation.d.ts +3 -3
  47. package/hooks/use-create-conversation.js +1 -0
  48. package/hooks/use-create-conversation.js.map +1 -1
  49. package/hooks/use-file-upload.d.ts +1 -1
  50. package/hooks/use-file-upload.js +3 -3
  51. package/hooks/use-file-upload.js.map +1 -1
  52. package/hooks/use-home-page.js +3 -3
  53. package/hooks/use-home-page.js.map +1 -1
  54. package/hooks/use-message-composer.d.ts +10 -3
  55. package/hooks/use-message-composer.d.ts.map +1 -1
  56. package/hooks/use-message-composer.js +5 -2
  57. package/hooks/use-message-composer.js.map +1 -1
  58. package/hooks/use-realtime-support.d.ts +1 -1
  59. package/hooks/use-send-message.d.ts +8 -2
  60. package/hooks/use-send-message.d.ts.map +1 -1
  61. package/hooks/use-send-message.js +5 -3
  62. package/hooks/use-send-message.js.map +1 -1
  63. package/hooks/use-visitor.js +2 -2
  64. package/hooks/use-visitor.js.map +1 -1
  65. package/identify-visitor.d.ts.map +1 -1
  66. package/identify-visitor.js +15 -1
  67. package/identify-visitor.js.map +1 -1
  68. package/index.d.ts +2 -2
  69. package/index.js +1 -1
  70. package/package.json +5 -3
  71. package/{conversation.d.ts → packages/types/src/api/conversation.d.ts} +365 -61
  72. package/packages/types/src/api/conversation.d.ts.map +1 -0
  73. package/packages/types/src/api/timeline-item.d.ts +460 -0
  74. package/packages/types/src/api/timeline-item.d.ts.map +1 -0
  75. package/{realtime-events.d.ts → packages/types/src/realtime-events.d.ts} +443 -45
  76. package/packages/types/src/realtime-events.d.ts.map +1 -0
  77. package/{schemas3.d.ts → packages/types/src/schemas.d.ts} +94 -18
  78. package/packages/types/src/schemas.d.ts.map +1 -0
  79. package/primitives/avatar/avatar.js +1 -1
  80. package/primitives/avatar/avatar.js.map +1 -1
  81. package/primitives/avatar/fallback.js +1 -1
  82. package/primitives/avatar/fallback.js.map +1 -1
  83. package/primitives/avatar/image.js +1 -1
  84. package/primitives/avatar/image.js.map +1 -1
  85. package/primitives/button.js +1 -1
  86. package/primitives/button.js.map +1 -1
  87. package/primitives/conversation-timeline.d.ts +1 -1
  88. package/primitives/conversation-timeline.js +4 -4
  89. package/primitives/conversation-timeline.js.map +1 -1
  90. package/primitives/day-separator.js +3 -3
  91. package/primitives/day-separator.js.map +1 -1
  92. package/primitives/multimodal-input.js +2 -2
  93. package/primitives/multimodal-input.js.map +1 -1
  94. package/primitives/timeline-item-attachments.d.ts +1 -1
  95. package/primitives/timeline-item-attachments.js +6 -7
  96. package/primitives/timeline-item-attachments.js.map +1 -1
  97. package/primitives/timeline-item-group.d.ts +1 -1
  98. package/primitives/timeline-item-group.js +7 -7
  99. package/primitives/timeline-item-group.js.map +1 -1
  100. package/primitives/timeline-item.d.ts +1 -1
  101. package/primitives/timeline-item.d.ts.map +1 -1
  102. package/primitives/timeline-item.js +33 -8
  103. package/primitives/timeline-item.js.map +1 -1
  104. package/primitives/trigger.js +1 -1
  105. package/primitives/trigger.js.map +1 -1
  106. package/primitives/window.js +1 -1
  107. package/primitives/window.js.map +1 -1
  108. package/provider.d.ts +4 -2
  109. package/provider.d.ts.map +1 -1
  110. package/provider.js +56 -8
  111. package/provider.js.map +1 -1
  112. package/realtime/event-filter.d.ts +4 -1
  113. package/realtime/event-filter.d.ts.map +1 -1
  114. package/realtime/event-filter.js +14 -0
  115. package/realtime/event-filter.js.map +1 -1
  116. package/realtime/provider.d.ts +1 -1
  117. package/realtime/provider.d.ts.map +1 -1
  118. package/realtime/provider.js +1 -2
  119. package/realtime/provider.js.map +1 -1
  120. package/realtime/seen-store.d.ts +2 -2
  121. package/realtime/support-provider.js +3 -2
  122. package/realtime/support-provider.js.map +1 -1
  123. package/realtime/typing-store.d.ts +1 -1
  124. package/realtime/use-realtime.d.ts +1 -1
  125. package/support/components/avatar-stack.d.ts.map +1 -1
  126. package/support/components/avatar-stack.js +32 -12
  127. package/support/components/avatar-stack.js.map +1 -1
  128. package/support/components/avatar.d.ts +34 -3
  129. package/support/components/avatar.d.ts.map +1 -1
  130. package/support/components/avatar.js +61 -8
  131. package/support/components/avatar.js.map +1 -1
  132. package/support/components/button.d.ts +3 -1
  133. package/support/components/button.d.ts.map +1 -1
  134. package/support/components/button.js +3 -3
  135. package/support/components/button.js.map +1 -1
  136. package/support/components/configuration-error.d.ts +16 -0
  137. package/support/components/configuration-error.d.ts.map +1 -0
  138. package/support/components/configuration-error.js +162 -0
  139. package/support/components/configuration-error.js.map +1 -0
  140. package/support/components/content.js +1 -2
  141. package/support/components/content.js.map +1 -1
  142. package/support/components/conversation-button-link.js +18 -23
  143. package/support/components/conversation-button-link.js.map +1 -1
  144. package/support/components/conversation-event.d.ts.map +1 -1
  145. package/support/components/conversation-event.js +7 -5
  146. package/support/components/conversation-event.js.map +1 -1
  147. package/support/components/conversation-timeline.d.ts +1 -1
  148. package/support/components/conversation-timeline.js +3 -3
  149. package/support/components/conversation-timeline.js.map +1 -1
  150. package/support/components/header.js +1 -1
  151. package/support/components/image-lightbox.d.ts +1 -1
  152. package/support/components/image-lightbox.js +1 -2
  153. package/support/components/image-lightbox.js.map +1 -1
  154. package/support/components/index.js +1 -1
  155. package/support/components/multimodal-input.js +0 -1
  156. package/support/components/multimodal-input.js.map +1 -1
  157. package/support/components/navigation-tab.js +1 -1
  158. package/support/components/online-indicator.d.ts +50 -0
  159. package/support/components/online-indicator.d.ts.map +1 -0
  160. package/support/components/online-indicator.js +65 -0
  161. package/support/components/online-indicator.js.map +1 -0
  162. package/support/components/root.js +0 -1
  163. package/support/components/root.js.map +1 -1
  164. package/support/components/timeline-identification-tool.js +4 -4
  165. package/support/components/timeline-identification-tool.js.map +1 -1
  166. package/support/components/timeline-message-group.d.ts +1 -1
  167. package/support/components/timeline-message-group.d.ts.map +1 -1
  168. package/support/components/timeline-message-group.js +6 -4
  169. package/support/components/timeline-message-group.js.map +1 -1
  170. package/support/components/timeline-message-item.d.ts +1 -1
  171. package/support/components/timeline-message-item.js +4 -4
  172. package/support/components/timeline-message-item.js.map +1 -1
  173. package/support/components/trigger.js +1 -2
  174. package/support/components/trigger.js.map +1 -1
  175. package/support/components/typing-indicator.js +1 -1
  176. package/support/components/typing-indicator.js.map +1 -1
  177. package/support/context/controlled-state.js +0 -1
  178. package/support/context/controlled-state.js.map +1 -1
  179. package/support/context/events.d.ts +1 -1
  180. package/support/context/events.js +0 -1
  181. package/support/context/events.js.map +1 -1
  182. package/support/context/handle.js +0 -1
  183. package/support/context/handle.js.map +1 -1
  184. package/support/context/identification.d.ts +33 -0
  185. package/support/context/identification.d.ts.map +1 -0
  186. package/support/context/identification.js +34 -0
  187. package/support/context/identification.js.map +1 -0
  188. package/support/context/positioning.js +0 -1
  189. package/support/context/positioning.js.map +1 -1
  190. package/support/context/slots.js +0 -1
  191. package/support/context/slots.js.map +1 -1
  192. package/support/context/websocket.d.ts +1 -1
  193. package/support/context/websocket.js +0 -1
  194. package/support/context/websocket.js.map +1 -1
  195. package/support/index.d.ts.map +1 -1
  196. package/support/index.js +51 -18
  197. package/support/index.js.map +1 -1
  198. package/support/pages/conversation-history.js +2 -1
  199. package/support/pages/conversation-history.js.map +1 -1
  200. package/support/pages/conversation.d.ts +1 -1
  201. package/support/pages/conversation.js +1 -1
  202. package/support/pages/conversation.js.map +1 -1
  203. package/support/pages/home.js +5 -3
  204. package/support/pages/home.js.map +1 -1
  205. package/support/router.d.ts.map +1 -1
  206. package/support/router.js +4 -0
  207. package/support/router.js.map +1 -1
  208. package/support/store/support-store.js +0 -1
  209. package/support/store/support-store.js.map +1 -1
  210. package/support/{support-C7Xaw-N6.css → support-DmViRaga.css} +2 -2
  211. package/support/{support-C7Xaw-N6.css.map → support-DmViRaga.css.map} +1 -1
  212. package/support/text/index.js +1 -1
  213. package/support/text/index.js.map +1 -1
  214. package/support/text/locales/en.js +1 -1
  215. package/support/text/locales/en.js.map +1 -1
  216. package/support/text/locales/es.js +1 -1
  217. package/support/text/locales/es.js.map +1 -1
  218. package/support/text/locales/fr.js +1 -1
  219. package/support/text/locales/fr.js.map +1 -1
  220. package/support/utils/index.d.ts +1 -1
  221. package/support-config.js +0 -1
  222. package/support-config.js.map +1 -1
  223. package/support.css +1 -1
  224. package/tailwind.css +1 -1
  225. package/utils/conversation.d.ts.map +1 -1
  226. package/utils/conversation.js +1 -3
  227. package/utils/conversation.js.map +1 -1
  228. package/utils/use-render-element.js +2 -2
  229. package/utils/use-render-element.js.map +1 -1
  230. package/api.d.ts +0 -71
  231. package/api.d.ts.map +0 -1
  232. package/checks.d.ts +0 -189
  233. package/checks.d.ts.map +0 -1
  234. package/clsx.d.ts +0 -7
  235. package/clsx.d.ts.map +0 -1
  236. package/coerce.d.ts +0 -9
  237. package/coerce.d.ts.map +0 -1
  238. package/conversation.d.ts.map +0 -1
  239. package/core.d.ts +0 -35
  240. package/core.d.ts.map +0 -1
  241. package/errors.d.ts +0 -130
  242. package/errors.d.ts.map +0 -1
  243. package/errors2.d.ts +0 -24
  244. package/errors2.d.ts.map +0 -1
  245. package/index2.d.ts +0 -4
  246. package/index3.d.ts +0 -1
  247. package/json-schema.d.ts +0 -70
  248. package/json-schema.d.ts.map +0 -1
  249. package/metadata.d.ts +0 -1
  250. package/openapi-generator.d.ts +0 -1
  251. package/openapi-generator2.d.ts +0 -1
  252. package/openapi-generator3.d.ts +0 -1
  253. package/openapi30.d.ts +0 -125
  254. package/openapi30.d.ts.map +0 -1
  255. package/openapi31.d.ts +0 -131
  256. package/openapi31.d.ts.map +0 -1
  257. package/parse.d.ts +0 -17
  258. package/parse.d.ts.map +0 -1
  259. package/realtime-events.d.ts.map +0 -1
  260. package/registries.d.ts +0 -32
  261. package/registries.d.ts.map +0 -1
  262. package/schemas.d.ts +0 -971
  263. package/schemas.d.ts.map +0 -1
  264. package/schemas2.d.ts +0 -345
  265. package/schemas2.d.ts.map +0 -1
  266. package/schemas3.d.ts.map +0 -1
  267. package/specification-extension.d.ts +0 -9
  268. package/specification-extension.d.ts.map +0 -1
  269. package/standard-schema.d.ts +0 -121
  270. package/standard-schema.d.ts.map +0 -1
  271. package/timeline-item.d.ts +0 -227
  272. package/timeline-item.d.ts.map +0 -1
  273. package/to-json-schema.d.ts +0 -96
  274. package/to-json-schema.d.ts.map +0 -1
  275. package/util.d.ts +0 -45
  276. package/util.d.ts.map +0 -1
  277. package/versions.d.ts +0 -9
  278. package/versions.d.ts.map +0 -1
  279. package/zod-extensions.d.ts +0 -39
  280. package/zod-extensions.d.ts.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"image-lightbox.js","names":["Icon"],"sources":["../../../src/support/components/image-lightbox.tsx"],"sourcesContent":["\"use client\";\n\nimport type { TimelinePartImage } from \"@cossistant/types/api/timeline-item\";\nimport type * as React from \"react\";\nimport { useCallback, useEffect, useState } from \"react\";\nimport { createPortal } from \"react-dom\";\nimport { cn } from \"../utils\";\nimport Icon from \"./icons\";\n\nexport type ImageLightboxProps = {\n\t/**\n\t * Array of images to display in the lightbox.\n\t */\n\timages: TimelinePartImage[];\n\t/**\n\t * Index of the initially selected image.\n\t */\n\tinitialIndex?: number;\n\t/**\n\t * Whether the lightbox is open.\n\t */\n\tisOpen: boolean;\n\t/**\n\t * Callback when the lightbox should close.\n\t */\n\tonClose: () => void;\n\t/**\n\t * Optional className for the overlay.\n\t */\n\tclassName?: string;\n};\n\n/**\n * Simple image lightbox/modal for viewing full-size images.\n * Supports keyboard navigation (Escape to close, Arrow keys to navigate).\n */\nexport function ImageLightbox({\n\timages,\n\tinitialIndex = 0,\n\tisOpen,\n\tonClose,\n\tclassName,\n}: ImageLightboxProps): React.ReactElement | null {\n\tconst [currentIndex, setCurrentIndex] = useState(initialIndex);\n\tconst [mounted, setMounted] = useState(false);\n\n\t// SSR safety: only render portal after component mounts on client\n\tuseEffect(() => {\n\t\tsetMounted(true);\n\t}, []);\n\n\t// Reset index when lightbox opens with new initial index\n\tuseEffect(() => {\n\t\tif (isOpen) {\n\t\t\tsetCurrentIndex(initialIndex);\n\t\t}\n\t}, [isOpen, initialIndex]);\n\n\t// Handle keyboard navigation\n\tconst handleKeyDown = useCallback(\n\t\t(event: KeyboardEvent) => {\n\t\t\tif (!isOpen) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tswitch (event.key) {\n\t\t\t\tcase \"Escape\":\n\t\t\t\t\tonClose();\n\t\t\t\t\tbreak;\n\t\t\t\tcase \"ArrowLeft\":\n\t\t\t\t\tsetCurrentIndex((prev) => (prev > 0 ? prev - 1 : images.length - 1));\n\t\t\t\t\tbreak;\n\t\t\t\tcase \"ArrowRight\":\n\t\t\t\t\tsetCurrentIndex((prev) => (prev < images.length - 1 ? prev + 1 : 0));\n\t\t\t\t\tbreak;\n\t\t\t\tdefault:\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t},\n\t\t[isOpen, images.length, onClose]\n\t);\n\n\tuseEffect(() => {\n\t\tdocument.addEventListener(\"keydown\", handleKeyDown);\n\t\treturn () => {\n\t\t\tdocument.removeEventListener(\"keydown\", handleKeyDown);\n\t\t};\n\t}, [handleKeyDown]);\n\n\t// Prevent body scroll when lightbox is open\n\tuseEffect(() => {\n\t\tif (isOpen) {\n\t\t\tdocument.body.style.overflow = \"hidden\";\n\t\t} else {\n\t\t\tdocument.body.style.overflow = \"\";\n\t\t}\n\t\treturn () => {\n\t\t\tdocument.body.style.overflow = \"\";\n\t\t};\n\t}, [isOpen]);\n\n\t// Don't render until mounted (SSR safety) or if not open/no images\n\tif (!(mounted && isOpen) || images.length === 0) {\n\t\treturn null;\n\t}\n\n\tconst currentImage = images[currentIndex];\n\tconst hasMultiple = images.length > 1;\n\n\tconst handlePrevious = () => {\n\t\tsetCurrentIndex((prev) => (prev > 0 ? prev - 1 : images.length - 1));\n\t};\n\n\tconst handleNext = () => {\n\t\tsetCurrentIndex((prev) => (prev < images.length - 1 ? prev + 1 : 0));\n\t};\n\n\tconst handleBackdropClick = (event: React.MouseEvent) => {\n\t\tif (event.target === event.currentTarget) {\n\t\t\tonClose();\n\t\t}\n\t};\n\n\t// Render via portal to document.body to escape any CSS containing blocks\n\t// (e.g., transforms on widget containers that break position: fixed)\n\treturn createPortal(\n\t\t// biome-ignore lint/a11y/noNoninteractiveElementInteractions: Dialog backdrop needs click handler for closing\n\t\t<div\n\t\t\taria-label=\"Image viewer\"\n\t\t\taria-modal=\"true\"\n\t\t\tclassName={cn(\n\t\t\t\t\"fixed inset-0 z-[99999] flex items-center justify-center bg-black/90 p-4\",\n\t\t\t\tclassName\n\t\t\t)}\n\t\t\tonClick={handleBackdropClick}\n\t\t\tonKeyDown={(e) => e.key === \"Escape\" && onClose()}\n\t\t\trole=\"dialog\"\n\t\t>\n\t\t\t{/* Close button */}\n\t\t\t<button\n\t\t\t\taria-label=\"Close lightbox\"\n\t\t\t\tclassName=\"absolute top-4 right-4 flex h-10 w-10 items-center justify-center rounded-full bg-white/10 text-white transition-colors hover:bg-white/20 focus:outline-none focus:ring-2 focus:ring-white/50\"\n\t\t\t\tonClick={onClose}\n\t\t\t\ttype=\"button\"\n\t\t\t>\n\t\t\t\t<Icon className=\"h-6 w-6\" name=\"close\" />\n\t\t\t</button>\n\n\t\t\t{/* Navigation buttons */}\n\t\t\t{hasMultiple && (\n\t\t\t\t<>\n\t\t\t\t\t<button\n\t\t\t\t\t\taria-label=\"Previous image\"\n\t\t\t\t\t\tclassName=\"absolute left-4 flex h-10 w-10 items-center justify-center rounded-full bg-white/10 text-white transition-colors hover:bg-white/20 focus:outline-none focus:ring-2 focus:ring-white/50\"\n\t\t\t\t\t\tonClick={handlePrevious}\n\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t>\n\t\t\t\t\t\t<Icon className=\"h-6 w-6\" name=\"arrow-left\" />\n\t\t\t\t\t</button>\n\t\t\t\t\t<button\n\t\t\t\t\t\taria-label=\"Next image\"\n\t\t\t\t\t\tclassName=\"absolute right-4 flex h-10 w-10 items-center justify-center rounded-full bg-white/10 text-white transition-colors hover:bg-white/20 focus:outline-none focus:ring-2 focus:ring-white/50\"\n\t\t\t\t\t\tonClick={handleNext}\n\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t>\n\t\t\t\t\t\t<Icon className=\"h-6 w-6\" name=\"arrow-right\" />\n\t\t\t\t\t</button>\n\t\t\t\t</>\n\t\t\t)}\n\n\t\t\t{/* Image */}\n\t\t\t{/* biome-ignore lint/performance/noImgElement: React package, not Next.js specific */}\n\t\t\t{/* biome-ignore lint/nursery/useImageSize: Dynamic image dimensions not known at render time */}\n\t\t\t<img\n\t\t\t\talt={currentImage?.fileName || `Image ${currentIndex + 1}`}\n\t\t\t\tclassName=\"max-h-[90vh] max-w-[90vw] object-contain\"\n\t\t\t\tsrc={currentImage?.url}\n\t\t\t/>\n\n\t\t\t{/* Image counter */}\n\t\t\t{hasMultiple && (\n\t\t\t\t<div className=\"-translate-x-1/2 absolute bottom-4 left-1/2 rounded-full bg-black/50 px-3 py-1 text-sm text-white\">\n\t\t\t\t\t{currentIndex + 1} / {images.length}\n\t\t\t\t</div>\n\t\t\t)}\n\t\t</div>,\n\t\tdocument.body\n\t);\n}\n\n/**\n * Hook to manage lightbox state.\n */\nexport function useLightbox() {\n\tconst [isOpen, setIsOpen] = useState(false);\n\tconst [selectedIndex, setSelectedIndex] = useState(0);\n\n\tconst openLightbox = useCallback((index = 0) => {\n\t\tsetSelectedIndex(index);\n\t\tsetIsOpen(true);\n\t}, []);\n\n\tconst closeLightbox = useCallback(() => {\n\t\tsetIsOpen(false);\n\t}, []);\n\n\treturn {\n\t\tisOpen,\n\t\tselectedIndex,\n\t\topenLightbox,\n\t\tcloseLightbox,\n\t};\n}\n"],"mappings":";;;;;;;;;;;;;;AAoCA,SAAgB,cAAc,EAC7B,QACA,eAAe,GACf,QACA,SACA,aACiD;CACjD,MAAM,CAAC,cAAc,mBAAmB,SAAS,aAAa;CAC9D,MAAM,CAAC,SAAS,cAAc,SAAS,MAAM;AAG7C,iBAAgB;AACf,aAAW,KAAK;IACd,EAAE,CAAC;AAGN,iBAAgB;AACf,MAAI,OACH,iBAAgB,aAAa;IAE5B,CAAC,QAAQ,aAAa,CAAC;CAG1B,MAAM,gBAAgB,aACpB,UAAyB;AACzB,MAAI,CAAC,OACJ;AAGD,UAAQ,MAAM,KAAd;GACC,KAAK;AACJ,aAAS;AACT;GACD,KAAK;AACJ,qBAAiB,SAAU,OAAO,IAAI,OAAO,IAAI,OAAO,SAAS,EAAG;AACpE;GACD,KAAK;AACJ,qBAAiB,SAAU,OAAO,OAAO,SAAS,IAAI,OAAO,IAAI,EAAG;AACpE;GACD,QACC;;IAGH;EAAC;EAAQ,OAAO;EAAQ;EAAQ,CAChC;AAED,iBAAgB;AACf,WAAS,iBAAiB,WAAW,cAAc;AACnD,eAAa;AACZ,YAAS,oBAAoB,WAAW,cAAc;;IAErD,CAAC,cAAc,CAAC;AAGnB,iBAAgB;AACf,MAAI,OACH,UAAS,KAAK,MAAM,WAAW;MAE/B,UAAS,KAAK,MAAM,WAAW;AAEhC,eAAa;AACZ,YAAS,KAAK,MAAM,WAAW;;IAE9B,CAAC,OAAO,CAAC;AAGZ,KAAI,EAAE,WAAW,WAAW,OAAO,WAAW,EAC7C,QAAO;CAGR,MAAM,eAAe,OAAO;CAC5B,MAAM,cAAc,OAAO,SAAS;CAEpC,MAAM,uBAAuB;AAC5B,mBAAiB,SAAU,OAAO,IAAI,OAAO,IAAI,OAAO,SAAS,EAAG;;CAGrE,MAAM,mBAAmB;AACxB,mBAAiB,SAAU,OAAO,OAAO,SAAS,IAAI,OAAO,IAAI,EAAG;;CAGrE,MAAM,uBAAuB,UAA4B;AACxD,MAAI,MAAM,WAAW,MAAM,cAC1B,UAAS;;AAMX,QAAO,aAEN,qBAAC;EACA,cAAW;EACX,cAAW;EACX,WAAW,GACV,4EACA,UACA;EACD,SAAS;EACT,YAAY,MAAM,EAAE,QAAQ,YAAY,SAAS;EACjD,MAAK;;GAGL,oBAAC;IACA,cAAW;IACX,WAAU;IACV,SAAS;IACT,MAAK;cAEL,oBAACA;KAAK,WAAU;KAAU,MAAK;MAAU;KACjC;GAGR,eACA,4CACC,oBAAC;IACA,cAAW;IACX,WAAU;IACV,SAAS;IACT,MAAK;cAEL,oBAACA;KAAK,WAAU;KAAU,MAAK;MAAe;KACtC,EACT,oBAAC;IACA,cAAW;IACX,WAAU;IACV,SAAS;IACT,MAAK;cAEL,oBAACA;KAAK,WAAU;KAAU,MAAK;MAAgB;KACvC,IACP;GAMJ,oBAAC;IACA,KAAK,cAAc,YAAY,SAAS,eAAe;IACvD,WAAU;IACV,KAAK,cAAc;KAClB;GAGD,eACA,qBAAC;IAAI,WAAU;;KACb,eAAe;KAAE;KAAI,OAAO;;KACxB;;GAEF,EACN,SAAS,KACT;;;;;AAMF,SAAgB,cAAc;CAC7B,MAAM,CAAC,QAAQ,aAAa,SAAS,MAAM;CAC3C,MAAM,CAAC,eAAe,oBAAoB,SAAS,EAAE;AAWrD,QAAO;EACN;EACA;EACA,cAZoB,aAAa,QAAQ,MAAM;AAC/C,oBAAiB,MAAM;AACvB,aAAU,KAAK;KACb,EAAE,CAAC;EAUL,eARqB,kBAAkB;AACvC,aAAU,MAAM;KACd,EAAE,CAAC;EAOL"}
1
+ {"version":3,"file":"image-lightbox.js","names":["Icon"],"sources":["../../../src/support/components/image-lightbox.tsx"],"sourcesContent":["\"use client\";\n\nimport type { TimelinePartImage } from \"@cossistant/types/api/timeline-item\";\nimport type * as React from \"react\";\nimport { useCallback, useEffect, useState } from \"react\";\nimport { createPortal } from \"react-dom\";\nimport { cn } from \"../utils\";\nimport Icon from \"./icons\";\n\nexport type ImageLightboxProps = {\n\t/**\n\t * Array of images to display in the lightbox.\n\t */\n\timages: TimelinePartImage[];\n\t/**\n\t * Index of the initially selected image.\n\t */\n\tinitialIndex?: number;\n\t/**\n\t * Whether the lightbox is open.\n\t */\n\tisOpen: boolean;\n\t/**\n\t * Callback when the lightbox should close.\n\t */\n\tonClose: () => void;\n\t/**\n\t * Optional className for the overlay.\n\t */\n\tclassName?: string;\n};\n\n/**\n * Simple image lightbox/modal for viewing full-size images.\n * Supports keyboard navigation (Escape to close, Arrow keys to navigate).\n */\nexport function ImageLightbox({\n\timages,\n\tinitialIndex = 0,\n\tisOpen,\n\tonClose,\n\tclassName,\n}: ImageLightboxProps): React.ReactElement | null {\n\tconst [currentIndex, setCurrentIndex] = useState(initialIndex);\n\tconst [mounted, setMounted] = useState(false);\n\n\t// SSR safety: only render portal after component mounts on client\n\tuseEffect(() => {\n\t\tsetMounted(true);\n\t}, []);\n\n\t// Reset index when lightbox opens with new initial index\n\tuseEffect(() => {\n\t\tif (isOpen) {\n\t\t\tsetCurrentIndex(initialIndex);\n\t\t}\n\t}, [isOpen, initialIndex]);\n\n\t// Handle keyboard navigation\n\tconst handleKeyDown = useCallback(\n\t\t(event: KeyboardEvent) => {\n\t\t\tif (!isOpen) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tswitch (event.key) {\n\t\t\t\tcase \"Escape\":\n\t\t\t\t\tonClose();\n\t\t\t\t\tbreak;\n\t\t\t\tcase \"ArrowLeft\":\n\t\t\t\t\tsetCurrentIndex((prev) => (prev > 0 ? prev - 1 : images.length - 1));\n\t\t\t\t\tbreak;\n\t\t\t\tcase \"ArrowRight\":\n\t\t\t\t\tsetCurrentIndex((prev) => (prev < images.length - 1 ? prev + 1 : 0));\n\t\t\t\t\tbreak;\n\t\t\t\tdefault:\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t},\n\t\t[isOpen, images.length, onClose]\n\t);\n\n\tuseEffect(() => {\n\t\tdocument.addEventListener(\"keydown\", handleKeyDown);\n\t\treturn () => {\n\t\t\tdocument.removeEventListener(\"keydown\", handleKeyDown);\n\t\t};\n\t}, [handleKeyDown]);\n\n\t// Prevent body scroll when lightbox is open\n\tuseEffect(() => {\n\t\tif (isOpen) {\n\t\t\tdocument.body.style.overflow = \"hidden\";\n\t\t} else {\n\t\t\tdocument.body.style.overflow = \"\";\n\t\t}\n\t\treturn () => {\n\t\t\tdocument.body.style.overflow = \"\";\n\t\t};\n\t}, [isOpen]);\n\n\t// Don't render until mounted (SSR safety) or if not open/no images\n\tif (!(mounted && isOpen) || images.length === 0) {\n\t\treturn null;\n\t}\n\n\tconst currentImage = images[currentIndex];\n\tconst hasMultiple = images.length > 1;\n\n\tconst handlePrevious = () => {\n\t\tsetCurrentIndex((prev) => (prev > 0 ? prev - 1 : images.length - 1));\n\t};\n\n\tconst handleNext = () => {\n\t\tsetCurrentIndex((prev) => (prev < images.length - 1 ? prev + 1 : 0));\n\t};\n\n\tconst handleBackdropClick = (event: React.MouseEvent) => {\n\t\tif (event.target === event.currentTarget) {\n\t\t\tonClose();\n\t\t}\n\t};\n\n\t// Render via portal to document.body to escape any CSS containing blocks\n\t// (e.g., transforms on widget containers that break position: fixed)\n\treturn createPortal(\n\t\t// biome-ignore lint/a11y/noNoninteractiveElementInteractions: Dialog backdrop needs click handler for closing\n\t\t<div\n\t\t\taria-label=\"Image viewer\"\n\t\t\taria-modal=\"true\"\n\t\t\tclassName={cn(\n\t\t\t\t\"fixed inset-0 z-[99999] flex items-center justify-center bg-black/90 p-4\",\n\t\t\t\tclassName\n\t\t\t)}\n\t\t\tonClick={handleBackdropClick}\n\t\t\tonKeyDown={(e) => e.key === \"Escape\" && onClose()}\n\t\t\trole=\"dialog\"\n\t\t>\n\t\t\t{/* Close button */}\n\t\t\t<button\n\t\t\t\taria-label=\"Close lightbox\"\n\t\t\t\tclassName=\"absolute top-4 right-4 flex h-10 w-10 items-center justify-center rounded-full bg-white/10 text-white transition-colors hover:bg-white/20 focus:outline-none focus:ring-2 focus:ring-white/50\"\n\t\t\t\tonClick={onClose}\n\t\t\t\ttype=\"button\"\n\t\t\t>\n\t\t\t\t<Icon className=\"h-6 w-6\" name=\"close\" />\n\t\t\t</button>\n\n\t\t\t{/* Navigation buttons */}\n\t\t\t{hasMultiple && (\n\t\t\t\t<>\n\t\t\t\t\t<button\n\t\t\t\t\t\taria-label=\"Previous image\"\n\t\t\t\t\t\tclassName=\"absolute left-4 flex h-10 w-10 items-center justify-center rounded-full bg-white/10 text-white transition-colors hover:bg-white/20 focus:outline-none focus:ring-2 focus:ring-white/50\"\n\t\t\t\t\t\tonClick={handlePrevious}\n\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t>\n\t\t\t\t\t\t<Icon className=\"h-6 w-6\" name=\"arrow-left\" />\n\t\t\t\t\t</button>\n\t\t\t\t\t<button\n\t\t\t\t\t\taria-label=\"Next image\"\n\t\t\t\t\t\tclassName=\"absolute right-4 flex h-10 w-10 items-center justify-center rounded-full bg-white/10 text-white transition-colors hover:bg-white/20 focus:outline-none focus:ring-2 focus:ring-white/50\"\n\t\t\t\t\t\tonClick={handleNext}\n\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t>\n\t\t\t\t\t\t<Icon className=\"h-6 w-6\" name=\"arrow-right\" />\n\t\t\t\t\t</button>\n\t\t\t\t</>\n\t\t\t)}\n\n\t\t\t{/* Image */}\n\t\t\t{/* biome-ignore lint/performance/noImgElement: React package, not Next.js specific */}\n\t\t\t{/* biome-ignore lint/nursery/useImageSize: Dynamic image dimensions not known at render time */}\n\t\t\t<img\n\t\t\t\talt={currentImage?.filename || `Image ${currentIndex + 1}`}\n\t\t\t\tclassName=\"max-h-[90vh] max-w-[90vw] object-contain\"\n\t\t\t\tsrc={currentImage?.url}\n\t\t\t/>\n\n\t\t\t{/* Image counter */}\n\t\t\t{hasMultiple && (\n\t\t\t\t<div className=\"-translate-x-1/2 absolute bottom-4 left-1/2 rounded-full bg-black/50 px-3 py-1 text-sm text-white\">\n\t\t\t\t\t{currentIndex + 1} / {images.length}\n\t\t\t\t</div>\n\t\t\t)}\n\t\t</div>,\n\t\tdocument.body\n\t);\n}\n\n/**\n * Hook to manage lightbox state.\n */\nexport function useLightbox() {\n\tconst [isOpen, setIsOpen] = useState(false);\n\tconst [selectedIndex, setSelectedIndex] = useState(0);\n\n\tconst openLightbox = useCallback((index = 0) => {\n\t\tsetSelectedIndex(index);\n\t\tsetIsOpen(true);\n\t}, []);\n\n\tconst closeLightbox = useCallback(() => {\n\t\tsetIsOpen(false);\n\t}, []);\n\n\treturn {\n\t\tisOpen,\n\t\tselectedIndex,\n\t\topenLightbox,\n\t\tcloseLightbox,\n\t};\n}\n"],"mappings":";;;;;;;;;;;;;AAoCA,SAAgB,cAAc,EAC7B,QACA,eAAe,GACf,QACA,SACA,aACiD;CACjD,MAAM,CAAC,cAAc,mBAAmB,SAAS,aAAa;CAC9D,MAAM,CAAC,SAAS,cAAc,SAAS,MAAM;AAG7C,iBAAgB;AACf,aAAW,KAAK;IACd,EAAE,CAAC;AAGN,iBAAgB;AACf,MAAI,OACH,iBAAgB,aAAa;IAE5B,CAAC,QAAQ,aAAa,CAAC;CAG1B,MAAM,gBAAgB,aACpB,UAAyB;AACzB,MAAI,CAAC,OACJ;AAGD,UAAQ,MAAM,KAAd;GACC,KAAK;AACJ,aAAS;AACT;GACD,KAAK;AACJ,qBAAiB,SAAU,OAAO,IAAI,OAAO,IAAI,OAAO,SAAS,EAAG;AACpE;GACD,KAAK;AACJ,qBAAiB,SAAU,OAAO,OAAO,SAAS,IAAI,OAAO,IAAI,EAAG;AACpE;GACD,QACC;;IAGH;EAAC;EAAQ,OAAO;EAAQ;EAAQ,CAChC;AAED,iBAAgB;AACf,WAAS,iBAAiB,WAAW,cAAc;AACnD,eAAa;AACZ,YAAS,oBAAoB,WAAW,cAAc;;IAErD,CAAC,cAAc,CAAC;AAGnB,iBAAgB;AACf,MAAI,OACH,UAAS,KAAK,MAAM,WAAW;MAE/B,UAAS,KAAK,MAAM,WAAW;AAEhC,eAAa;AACZ,YAAS,KAAK,MAAM,WAAW;;IAE9B,CAAC,OAAO,CAAC;AAGZ,KAAI,EAAE,WAAW,WAAW,OAAO,WAAW,EAC7C,QAAO;CAGR,MAAM,eAAe,OAAO;CAC5B,MAAM,cAAc,OAAO,SAAS;CAEpC,MAAM,uBAAuB;AAC5B,mBAAiB,SAAU,OAAO,IAAI,OAAO,IAAI,OAAO,SAAS,EAAG;;CAGrE,MAAM,mBAAmB;AACxB,mBAAiB,SAAU,OAAO,OAAO,SAAS,IAAI,OAAO,IAAI,EAAG;;CAGrE,MAAM,uBAAuB,UAA4B;AACxD,MAAI,MAAM,WAAW,MAAM,cAC1B,UAAS;;AAMX,QAAO,aAEN,qBAAC;EACA,cAAW;EACX,cAAW;EACX,WAAW,GACV,4EACA,UACA;EACD,SAAS;EACT,YAAY,MAAM,EAAE,QAAQ,YAAY,SAAS;EACjD,MAAK;;GAGL,oBAAC;IACA,cAAW;IACX,WAAU;IACV,SAAS;IACT,MAAK;cAEL,oBAACA;KAAK,WAAU;KAAU,MAAK;MAAU;KACjC;GAGR,eACA,4CACC,oBAAC;IACA,cAAW;IACX,WAAU;IACV,SAAS;IACT,MAAK;cAEL,oBAACA;KAAK,WAAU;KAAU,MAAK;MAAe;KACtC,EACT,oBAAC;IACA,cAAW;IACX,WAAU;IACV,SAAS;IACT,MAAK;cAEL,oBAACA;KAAK,WAAU;KAAU,MAAK;MAAgB;KACvC,IACP;GAMJ,oBAAC;IACA,KAAK,cAAc,YAAY,SAAS,eAAe;IACvD,WAAU;IACV,KAAK,cAAc;KAClB;GAGD,eACA,qBAAC;IAAI,WAAU;;KACb,eAAe;KAAE;KAAI,OAAO;;KACxB;;GAEF,EACN,SAAS,KACT;;;;;AAMF,SAAgB,cAAc;CAC7B,MAAM,CAAC,QAAQ,aAAa,SAAS,MAAM;CAC3C,MAAM,CAAC,eAAe,oBAAoB,SAAS,EAAE;AAWrD,QAAO;EACN;EACA;EACA,cAZoB,aAAa,QAAQ,MAAM;AAC/C,oBAAiB,MAAM;AACvB,aAAU,KAAK;KACb,EAAE,CAAC;EAUL,eARqB,kBAAkB;AACvC,aAAU,MAAM;KACd,EAAE,CAAC;EAOL"}
@@ -1,7 +1,7 @@
1
1
  import { TypingIndicator } from "./typing-indicator.js";
2
+ import icons_default from "./icons.js";
2
3
  import { Content } from "./content.js";
3
4
  import { Root } from "./root.js";
4
- import icons_default from "./icons.js";
5
5
  import { DefaultTrigger } from "./trigger.js";
6
6
  import { ConversationEvent } from "./conversation-event.js";
7
7
  import { TimelineMessageItem } from "./timeline-message-item.js";
@@ -1,6 +1,5 @@
1
1
  "use client";
2
2
 
3
-
4
3
  import { cn } from "../utils/index.js";
5
4
  import { Button } from "../../primitives/button.js";
6
5
  import { FileInput, MultimodalInput as MultimodalInput$1 } from "../../primitives/multimodal-input.js";
@@ -1 +1 @@
1
- {"version":3,"file":"multimodal-input.js","names":["MultimodalInput: React.FC<MultimodalInputProps>","Icon","Primitive.MultimodalInput","Primitive.FileInput","SendButton: React.FC<SendButtonProps>","Primitive.Button"],"sources":["../../../src/support/components/multimodal-input.tsx"],"sourcesContent":["\"use client\";\n\nimport {\n\tFILE_INPUT_ACCEPT,\n\tformatFileSize,\n\tMAX_FILE_SIZE,\n\tMAX_FILES_PER_MESSAGE,\n} from \"@cossistant/core\";\nimport type React from \"react\";\nimport { useRef } from \"react\";\nimport { useComposerRefocus } from \"../../hooks/use-composer-refocus\";\nimport * as Primitive from \"../../primitives\";\nimport { useSupportText } from \"../text\";\nimport { cn } from \"../utils\";\nimport Icon from \"./icons\";\nimport { Watermark } from \"./watermark\";\n\nexport type MultimodalInputProps = {\n\tclassName?: string;\n\tvalue: string;\n\tonChange: (value: string) => void;\n\tonSubmit: () => void;\n\tonFileSelect?: (files: File[]) => void;\n\tplaceholder?: string;\n\tdisabled?: boolean;\n\tisSubmitting?: boolean;\n\tisUploading?: boolean;\n\tuploadProgress?: number;\n\terror?: Error | null;\n\tfiles?: File[];\n\tonRemoveFile?: (index: number) => void;\n\tmaxFiles?: number;\n\tmaxFileSize?: number;\n\tallowedFileTypes?: string;\n};\n\nexport const MultimodalInput: React.FC<MultimodalInputProps> = ({\n\tclassName,\n\tvalue,\n\tonChange,\n\tonSubmit,\n\tonFileSelect,\n\tplaceholder,\n\tdisabled = false,\n\tisSubmitting = false,\n\tisUploading = false,\n\tuploadProgress = 0,\n\terror,\n\tfiles = [],\n\tonRemoveFile,\n\tmaxFiles = MAX_FILES_PER_MESSAGE,\n\tmaxFileSize = MAX_FILE_SIZE,\n\tallowedFileTypes = FILE_INPUT_ACCEPT,\n}) => {\n\tconst fileInputRef = useRef<HTMLInputElement>(null);\n\tconst hasContent = value.trim().length > 0 || files.length > 0;\n\tconst { focusComposer, inputRef } = useComposerRefocus({\n\t\tdisabled,\n\t\thasContent,\n\t\tisSubmitting: isSubmitting || isUploading,\n\t});\n\tconst canSubmit = !disabled && hasContent && !isUploading;\n\tconst text = useSupportText();\n\tconst resolvedPlaceholder =\n\t\tplaceholder ?? text(\"component.multimodalInput.placeholder\");\n\n\tconst handleSubmit = () => {\n\t\tif (!canSubmit) {\n\t\t\treturn;\n\t\t}\n\n\t\tonSubmit();\n\t\t// Try focusing immediately for optimistic submission UX, then ensure focus\n\t\t// sticks after the submit button handles the click.\n\t\tfocusComposer();\n\t\trequestAnimationFrame(() => {\n\t\t\tfocusComposer();\n\t\t});\n\t};\n\n\tconst handleFormSubmit = (e: React.FormEvent) => {\n\t\te.preventDefault();\n\t\thandleSubmit();\n\t};\n\n\tconst handleAttachClick = () => {\n\t\tif (files.length < maxFiles) {\n\t\t\tfileInputRef.current?.click();\n\t\t}\n\t};\n\n\treturn (\n\t\t<form className=\"flex flex-col gap-2\" onSubmit={handleFormSubmit}>\n\t\t\t{/* Error message */}\n\t\t\t{error && (\n\t\t\t\t<div\n\t\t\t\t\tclassName=\"rounded-md bg-co-destructive-muted p-2 text-co-destructive text-xs\"\n\t\t\t\t\tid=\"multimodal-input-error\"\n\t\t\t\t>\n\t\t\t\t\t{error.message}\n\t\t\t\t</div>\n\t\t\t)}\n\n\t\t\t{/* File attachments */}\n\t\t\t{files.length > 0 && (\n\t\t\t\t<div className=\"flex flex-col gap-2 p-2\">\n\t\t\t\t\t{/* Upload progress indicator */}\n\t\t\t\t\t{isUploading && (\n\t\t\t\t\t\t<div className=\"flex items-center gap-2 text-co-muted-foreground text-xs\">\n\t\t\t\t\t\t\t<div className=\"h-1 flex-1 overflow-hidden rounded-full bg-co-muted\">\n\t\t\t\t\t\t\t\t<div\n\t\t\t\t\t\t\t\t\tclassName=\"h-full bg-co-primary transition-all duration-300\"\n\t\t\t\t\t\t\t\t\tstyle={{ width: `${uploadProgress}%` }}\n\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t<span>{uploadProgress}%</span>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t)}\n\t\t\t\t\t<div className=\"flex flex-wrap gap-2\">\n\t\t\t\t\t\t{files.map((file, index) => (\n\t\t\t\t\t\t\t<div\n\t\t\t\t\t\t\t\tclassName={cn(\n\t\t\t\t\t\t\t\t\t\"flex items-center gap-2 rounded-md bg-co-muted px-2 py-1 text-xs\",\n\t\t\t\t\t\t\t\t\tisUploading && \"opacity-70\"\n\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t\tkey={`${file.name}-${index}`}\n\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t<Icon className=\"h-3 w-3\" name=\"attachment\" />\n\t\t\t\t\t\t\t\t<span className=\"max-w-[150px] truncate\">{file.name}</span>\n\t\t\t\t\t\t\t\t<span className=\"text-co-muted-foreground\">\n\t\t\t\t\t\t\t\t\t{formatFileSize(file.size)}\n\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t\t{onRemoveFile && !isUploading && (\n\t\t\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\t\t\taria-label={text(\"common.actions.removeFile\", {\n\t\t\t\t\t\t\t\t\t\t\tfileName: file.name,\n\t\t\t\t\t\t\t\t\t\t})}\n\t\t\t\t\t\t\t\t\t\tclassName=\"ml-1 hover:text-co-destructive\"\n\t\t\t\t\t\t\t\t\t\tonClick={() => onRemoveFile(index)}\n\t\t\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t<Icon className=\"h-3 w-3\" name=\"close\" />\n\t\t\t\t\t\t\t\t\t</button>\n\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t))}\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\t\t\t)}\n\n\t\t\t{/* Input area */}\n\t\t\t<div className=\"group/multimodal-input flex flex-col rounded border border-co-border bg-co-background ring-offset-2 focus-within:ring-1 focus-within:ring-co-primary/10 dark:bg-co-background-200\">\n\t\t\t\t<Primitive.MultimodalInput\n\t\t\t\t\tautoFocus\n\t\t\t\t\tclassName={cn(\n\t\t\t\t\t\t\"flex-1 resize-none overflow-hidden p-3 text-co-foreground text-sm placeholder:text-co-primary/50 focus-visible:outline-none\",\n\t\t\t\t\t\tclassName\n\t\t\t\t\t)}\n\t\t\t\t\tdisabled={disabled}\n\t\t\t\t\terror={error}\n\t\t\t\t\tonChange={onChange}\n\t\t\t\t\tonFileSelect={onFileSelect}\n\t\t\t\t\tonSubmit={handleSubmit}\n\t\t\t\t\tplaceholder={resolvedPlaceholder}\n\t\t\t\t\tref={inputRef}\n\t\t\t\t\tvalue={value}\n\t\t\t\t/>\n\n\t\t\t\t<div className=\"flex items-center justify-between py-1 pr-1 pl-3\">\n\t\t\t\t\t<Watermark />\n\n\t\t\t\t\t<div className=\"flex items-center gap-0.5\">\n\t\t\t\t\t\t{/* File attachment button */}\n\t\t\t\t\t\t{onFileSelect && (\n\t\t\t\t\t\t\t<>\n\t\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\t\taria-label={text(\"common.actions.attachFiles\")}\n\t\t\t\t\t\t\t\t\tclassName={cn(\n\t\t\t\t\t\t\t\t\t\t\"group flex h-8 w-8 items-center justify-center rounded-md text-co-muted-foreground hover:bg-co-muted hover:text-co-foreground disabled:cursor-not-allowed disabled:opacity-50\",\n\t\t\t\t\t\t\t\t\t\tfiles.length >= maxFiles && \"opacity-50\"\n\t\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t\t\tdisabled={\n\t\t\t\t\t\t\t\t\t\tdisabled || isSubmitting || files.length >= maxFiles\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\tonClick={handleAttachClick}\n\t\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t<Icon className=\"h-4 w-4\" name=\"attachment\" />\n\t\t\t\t\t\t\t\t</button>\n\n\t\t\t\t\t\t\t\t<Primitive.FileInput\n\t\t\t\t\t\t\t\t\taccept={allowedFileTypes}\n\t\t\t\t\t\t\t\t\tclassName=\"hidden\"\n\t\t\t\t\t\t\t\t\tdisabled={\n\t\t\t\t\t\t\t\t\t\tdisabled || isSubmitting || files.length >= maxFiles\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\tonFileSelect={onFileSelect}\n\t\t\t\t\t\t\t\t\tref={fileInputRef}\n\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t</>\n\t\t\t\t\t\t)}\n\n\t\t\t\t\t\t{/* Send button */}\n\t\t\t\t\t\t<SendButton disabled={!canSubmit} isUploading={isUploading} />\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t</form>\n\t);\n};\n\nexport type SendButtonProps = {\n\tclassName?: string;\n\tdisabled?: boolean;\n\tisUploading?: boolean;\n};\n\nexport const SendButton: React.FC<SendButtonProps> = ({\n\tclassName,\n\tdisabled = false,\n\tisUploading = false,\n}) => (\n\t<Primitive.Button\n\t\tclassName={cn(\n\t\t\t\"group flex h-8 w-8 items-center justify-center rounded-md text-co-primary hover:bg-co-muted disabled:cursor-not-allowed disabled:opacity-50\",\n\t\t\tclassName\n\t\t)}\n\t\tdisabled={disabled}\n\t\ttype=\"submit\"\n\t>\n\t\t{isUploading ? (\n\t\t\t<div className=\"h-4 w-4 animate-spin rounded-full border-2 border-co-primary border-t-transparent\" />\n\t\t) : (\n\t\t\t<Icon className=\"h-4 w-4\" filledOnHover name=\"send\" />\n\t\t)}\n\t</Primitive.Button>\n);\n"],"mappings":";;;;;;;;;;;;;;;AAoCA,MAAaA,mBAAmD,EAC/D,WACA,OACA,UACA,UACA,cACA,aACA,WAAW,OACX,eAAe,OACf,cAAc,OACd,iBAAiB,GACjB,OACA,QAAQ,EAAE,EACV,cACA,WAAW,uBACX,cAAc,eACd,mBAAmB,wBACd;CACL,MAAM,eAAe,OAAyB,KAAK;CACnD,MAAM,aAAa,MAAM,MAAM,CAAC,SAAS,KAAK,MAAM,SAAS;CAC7D,MAAM,EAAE,eAAe,aAAa,mBAAmB;EACtD;EACA;EACA,cAAc,gBAAgB;EAC9B,CAAC;CACF,MAAM,YAAY,CAAC,YAAY,cAAc,CAAC;CAC9C,MAAM,OAAO,gBAAgB;CAC7B,MAAM,sBACL,eAAe,KAAK,wCAAwC;CAE7D,MAAM,qBAAqB;AAC1B,MAAI,CAAC,UACJ;AAGD,YAAU;AAGV,iBAAe;AACf,8BAA4B;AAC3B,kBAAe;IACd;;CAGH,MAAM,oBAAoB,MAAuB;AAChD,IAAE,gBAAgB;AAClB,gBAAc;;CAGf,MAAM,0BAA0B;AAC/B,MAAI,MAAM,SAAS,SAClB,cAAa,SAAS,OAAO;;AAI/B,QACC,qBAAC;EAAK,WAAU;EAAsB,UAAU;;GAE9C,SACA,oBAAC;IACA,WAAU;IACV,IAAG;cAEF,MAAM;KACF;GAIN,MAAM,SAAS,KACf,qBAAC;IAAI,WAAU;eAEb,eACA,qBAAC;KAAI,WAAU;gBACd,oBAAC;MAAI,WAAU;gBACd,oBAAC;OACA,WAAU;OACV,OAAO,EAAE,OAAO,GAAG,eAAe,IAAI;QACrC;OACG,EACN,qBAAC,qBAAM,gBAAe,OAAQ;MACzB,EAEP,oBAAC;KAAI,WAAU;eACb,MAAM,KAAK,MAAM,UACjB,qBAAC;MACA,WAAW,GACV,oEACA,eAAe,aACf;;OAGD,oBAACC;QAAK,WAAU;QAAU,MAAK;SAAe;OAC9C,oBAAC;QAAK,WAAU;kBAA0B,KAAK;SAAY;OAC3D,oBAAC;QAAK,WAAU;kBACd,eAAe,KAAK,KAAK;SACpB;OACN,gBAAgB,CAAC,eACjB,oBAAC;QACA,cAAY,KAAK,6BAA6B,EAC7C,UAAU,KAAK,MACf,CAAC;QACF,WAAU;QACV,eAAe,aAAa,MAAM;QAClC,MAAK;kBAEL,oBAACA;SAAK,WAAU;SAAU,MAAK;UAAU;SACjC;;QAjBL,GAAG,KAAK,KAAK,GAAG,QAmBhB,CACL;MACG;KACD;GAIP,qBAAC;IAAI,WAAU;eACd,oBAACC;KACA;KACA,WAAW,GACV,+HACA,UACA;KACS;KACH;KACG;KACI;KACd,UAAU;KACV,aAAa;KACb,KAAK;KACE;MACN,EAEF,qBAAC;KAAI,WAAU;gBACd,oBAAC,cAAY,EAEb,qBAAC;MAAI,WAAU;iBAEb,gBACA,4CACC,oBAAC;OACA,cAAY,KAAK,6BAA6B;OAC9C,WAAW,GACV,iLACA,MAAM,UAAU,YAAY,aAC5B;OACD,UACC,YAAY,gBAAgB,MAAM,UAAU;OAE7C,SAAS;OACT,MAAK;iBAEL,oBAACD;QAAK,WAAU;QAAU,MAAK;SAAe;QACtC,EAET,oBAACE;OACA,QAAQ;OACR,WAAU;OACV,UACC,YAAY,gBAAgB,MAAM,UAAU;OAE/B;OACd,KAAK;QACJ,IACA,EAIJ,oBAAC;OAAW,UAAU,CAAC;OAAwB;QAAe;OACzD;MACD;KACD;;GACA;;AAUT,MAAaC,cAAyC,EACrD,WACA,WAAW,OACX,cAAc,YAEd,oBAACC;CACA,WAAW,GACV,+IACA,UACA;CACS;CACV,MAAK;WAEJ,cACA,oBAAC,SAAI,WAAU,sFAAsF,GAErG,oBAACJ;EAAK,WAAU;EAAU;EAAc,MAAK;GAAS;EAErC"}
1
+ {"version":3,"file":"multimodal-input.js","names":["MultimodalInput: React.FC<MultimodalInputProps>","Icon","Primitive.MultimodalInput","Primitive.FileInput","SendButton: React.FC<SendButtonProps>","Primitive.Button"],"sources":["../../../src/support/components/multimodal-input.tsx"],"sourcesContent":["\"use client\";\n\nimport {\n\tFILE_INPUT_ACCEPT,\n\tformatFileSize,\n\tMAX_FILE_SIZE,\n\tMAX_FILES_PER_MESSAGE,\n} from \"@cossistant/core\";\nimport type React from \"react\";\nimport { useRef } from \"react\";\nimport { useComposerRefocus } from \"../../hooks/use-composer-refocus\";\nimport * as Primitive from \"../../primitives\";\nimport { useSupportText } from \"../text\";\nimport { cn } from \"../utils\";\nimport Icon from \"./icons\";\nimport { Watermark } from \"./watermark\";\n\nexport type MultimodalInputProps = {\n\tclassName?: string;\n\tvalue: string;\n\tonChange: (value: string) => void;\n\tonSubmit: () => void;\n\tonFileSelect?: (files: File[]) => void;\n\tplaceholder?: string;\n\tdisabled?: boolean;\n\tisSubmitting?: boolean;\n\tisUploading?: boolean;\n\tuploadProgress?: number;\n\terror?: Error | null;\n\tfiles?: File[];\n\tonRemoveFile?: (index: number) => void;\n\tmaxFiles?: number;\n\tmaxFileSize?: number;\n\tallowedFileTypes?: string;\n};\n\nexport const MultimodalInput: React.FC<MultimodalInputProps> = ({\n\tclassName,\n\tvalue,\n\tonChange,\n\tonSubmit,\n\tonFileSelect,\n\tplaceholder,\n\tdisabled = false,\n\tisSubmitting = false,\n\tisUploading = false,\n\tuploadProgress = 0,\n\terror,\n\tfiles = [],\n\tonRemoveFile,\n\tmaxFiles = MAX_FILES_PER_MESSAGE,\n\tmaxFileSize = MAX_FILE_SIZE,\n\tallowedFileTypes = FILE_INPUT_ACCEPT,\n}) => {\n\tconst fileInputRef = useRef<HTMLInputElement>(null);\n\tconst hasContent = value.trim().length > 0 || files.length > 0;\n\tconst { focusComposer, inputRef } = useComposerRefocus({\n\t\tdisabled,\n\t\thasContent,\n\t\tisSubmitting: isSubmitting || isUploading,\n\t});\n\tconst canSubmit = !disabled && hasContent && !isUploading;\n\tconst text = useSupportText();\n\tconst resolvedPlaceholder =\n\t\tplaceholder ?? text(\"component.multimodalInput.placeholder\");\n\n\tconst handleSubmit = () => {\n\t\tif (!canSubmit) {\n\t\t\treturn;\n\t\t}\n\n\t\tonSubmit();\n\t\t// Try focusing immediately for optimistic submission UX, then ensure focus\n\t\t// sticks after the submit button handles the click.\n\t\tfocusComposer();\n\t\trequestAnimationFrame(() => {\n\t\t\tfocusComposer();\n\t\t});\n\t};\n\n\tconst handleFormSubmit = (e: React.FormEvent) => {\n\t\te.preventDefault();\n\t\thandleSubmit();\n\t};\n\n\tconst handleAttachClick = () => {\n\t\tif (files.length < maxFiles) {\n\t\t\tfileInputRef.current?.click();\n\t\t}\n\t};\n\n\treturn (\n\t\t<form className=\"flex flex-col gap-2\" onSubmit={handleFormSubmit}>\n\t\t\t{/* Error message */}\n\t\t\t{error && (\n\t\t\t\t<div\n\t\t\t\t\tclassName=\"rounded-md bg-co-destructive-muted p-2 text-co-destructive text-xs\"\n\t\t\t\t\tid=\"multimodal-input-error\"\n\t\t\t\t>\n\t\t\t\t\t{error.message}\n\t\t\t\t</div>\n\t\t\t)}\n\n\t\t\t{/* File attachments */}\n\t\t\t{files.length > 0 && (\n\t\t\t\t<div className=\"flex flex-col gap-2 p-2\">\n\t\t\t\t\t{/* Upload progress indicator */}\n\t\t\t\t\t{isUploading && (\n\t\t\t\t\t\t<div className=\"flex items-center gap-2 text-co-muted-foreground text-xs\">\n\t\t\t\t\t\t\t<div className=\"h-1 flex-1 overflow-hidden rounded-full bg-co-muted\">\n\t\t\t\t\t\t\t\t<div\n\t\t\t\t\t\t\t\t\tclassName=\"h-full bg-co-primary transition-all duration-300\"\n\t\t\t\t\t\t\t\t\tstyle={{ width: `${uploadProgress}%` }}\n\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t<span>{uploadProgress}%</span>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t)}\n\t\t\t\t\t<div className=\"flex flex-wrap gap-2\">\n\t\t\t\t\t\t{files.map((file, index) => (\n\t\t\t\t\t\t\t<div\n\t\t\t\t\t\t\t\tclassName={cn(\n\t\t\t\t\t\t\t\t\t\"flex items-center gap-2 rounded-md bg-co-muted px-2 py-1 text-xs\",\n\t\t\t\t\t\t\t\t\tisUploading && \"opacity-70\"\n\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t\tkey={`${file.name}-${index}`}\n\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t<Icon className=\"h-3 w-3\" name=\"attachment\" />\n\t\t\t\t\t\t\t\t<span className=\"max-w-[150px] truncate\">{file.name}</span>\n\t\t\t\t\t\t\t\t<span className=\"text-co-muted-foreground\">\n\t\t\t\t\t\t\t\t\t{formatFileSize(file.size)}\n\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t\t{onRemoveFile && !isUploading && (\n\t\t\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\t\t\taria-label={text(\"common.actions.removeFile\", {\n\t\t\t\t\t\t\t\t\t\t\tfileName: file.name,\n\t\t\t\t\t\t\t\t\t\t})}\n\t\t\t\t\t\t\t\t\t\tclassName=\"ml-1 hover:text-co-destructive\"\n\t\t\t\t\t\t\t\t\t\tonClick={() => onRemoveFile(index)}\n\t\t\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t<Icon className=\"h-3 w-3\" name=\"close\" />\n\t\t\t\t\t\t\t\t\t</button>\n\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t))}\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\t\t\t)}\n\n\t\t\t{/* Input area */}\n\t\t\t<div className=\"group/multimodal-input flex flex-col rounded border border-co-border bg-co-background ring-offset-2 focus-within:ring-1 focus-within:ring-co-primary/10 dark:bg-co-background-200\">\n\t\t\t\t<Primitive.MultimodalInput\n\t\t\t\t\tautoFocus\n\t\t\t\t\tclassName={cn(\n\t\t\t\t\t\t\"flex-1 resize-none overflow-hidden p-3 text-co-foreground text-sm placeholder:text-co-primary/50 focus-visible:outline-none\",\n\t\t\t\t\t\tclassName\n\t\t\t\t\t)}\n\t\t\t\t\tdisabled={disabled}\n\t\t\t\t\terror={error}\n\t\t\t\t\tonChange={onChange}\n\t\t\t\t\tonFileSelect={onFileSelect}\n\t\t\t\t\tonSubmit={handleSubmit}\n\t\t\t\t\tplaceholder={resolvedPlaceholder}\n\t\t\t\t\tref={inputRef}\n\t\t\t\t\tvalue={value}\n\t\t\t\t/>\n\n\t\t\t\t<div className=\"flex items-center justify-between py-1 pr-1 pl-3\">\n\t\t\t\t\t<Watermark />\n\n\t\t\t\t\t<div className=\"flex items-center gap-0.5\">\n\t\t\t\t\t\t{/* File attachment button */}\n\t\t\t\t\t\t{onFileSelect && (\n\t\t\t\t\t\t\t<>\n\t\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\t\taria-label={text(\"common.actions.attachFiles\")}\n\t\t\t\t\t\t\t\t\tclassName={cn(\n\t\t\t\t\t\t\t\t\t\t\"group flex h-8 w-8 items-center justify-center rounded-md text-co-muted-foreground hover:bg-co-muted hover:text-co-foreground disabled:cursor-not-allowed disabled:opacity-50\",\n\t\t\t\t\t\t\t\t\t\tfiles.length >= maxFiles && \"opacity-50\"\n\t\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t\t\tdisabled={\n\t\t\t\t\t\t\t\t\t\tdisabled || isSubmitting || files.length >= maxFiles\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\tonClick={handleAttachClick}\n\t\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t<Icon className=\"h-4 w-4\" name=\"attachment\" />\n\t\t\t\t\t\t\t\t</button>\n\n\t\t\t\t\t\t\t\t<Primitive.FileInput\n\t\t\t\t\t\t\t\t\taccept={allowedFileTypes}\n\t\t\t\t\t\t\t\t\tclassName=\"hidden\"\n\t\t\t\t\t\t\t\t\tdisabled={\n\t\t\t\t\t\t\t\t\t\tdisabled || isSubmitting || files.length >= maxFiles\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\tonFileSelect={onFileSelect}\n\t\t\t\t\t\t\t\t\tref={fileInputRef}\n\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t</>\n\t\t\t\t\t\t)}\n\n\t\t\t\t\t\t{/* Send button */}\n\t\t\t\t\t\t<SendButton disabled={!canSubmit} isUploading={isUploading} />\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t</form>\n\t);\n};\n\nexport type SendButtonProps = {\n\tclassName?: string;\n\tdisabled?: boolean;\n\tisUploading?: boolean;\n};\n\nexport const SendButton: React.FC<SendButtonProps> = ({\n\tclassName,\n\tdisabled = false,\n\tisUploading = false,\n}) => (\n\t<Primitive.Button\n\t\tclassName={cn(\n\t\t\t\"group flex h-8 w-8 items-center justify-center rounded-md text-co-primary hover:bg-co-muted disabled:cursor-not-allowed disabled:opacity-50\",\n\t\t\tclassName\n\t\t)}\n\t\tdisabled={disabled}\n\t\ttype=\"submit\"\n\t>\n\t\t{isUploading ? (\n\t\t\t<div className=\"h-4 w-4 animate-spin rounded-full border-2 border-co-primary border-t-transparent\" />\n\t\t) : (\n\t\t\t<Icon className=\"h-4 w-4\" filledOnHover name=\"send\" />\n\t\t)}\n\t</Primitive.Button>\n);\n"],"mappings":";;;;;;;;;;;;;;AAoCA,MAAaA,mBAAmD,EAC/D,WACA,OACA,UACA,UACA,cACA,aACA,WAAW,OACX,eAAe,OACf,cAAc,OACd,iBAAiB,GACjB,OACA,QAAQ,EAAE,EACV,cACA,WAAW,uBACX,cAAc,eACd,mBAAmB,wBACd;CACL,MAAM,eAAe,OAAyB,KAAK;CACnD,MAAM,aAAa,MAAM,MAAM,CAAC,SAAS,KAAK,MAAM,SAAS;CAC7D,MAAM,EAAE,eAAe,aAAa,mBAAmB;EACtD;EACA;EACA,cAAc,gBAAgB;EAC9B,CAAC;CACF,MAAM,YAAY,CAAC,YAAY,cAAc,CAAC;CAC9C,MAAM,OAAO,gBAAgB;CAC7B,MAAM,sBACL,eAAe,KAAK,wCAAwC;CAE7D,MAAM,qBAAqB;AAC1B,MAAI,CAAC,UACJ;AAGD,YAAU;AAGV,iBAAe;AACf,8BAA4B;AAC3B,kBAAe;IACd;;CAGH,MAAM,oBAAoB,MAAuB;AAChD,IAAE,gBAAgB;AAClB,gBAAc;;CAGf,MAAM,0BAA0B;AAC/B,MAAI,MAAM,SAAS,SAClB,cAAa,SAAS,OAAO;;AAI/B,QACC,qBAAC;EAAK,WAAU;EAAsB,UAAU;;GAE9C,SACA,oBAAC;IACA,WAAU;IACV,IAAG;cAEF,MAAM;KACF;GAIN,MAAM,SAAS,KACf,qBAAC;IAAI,WAAU;eAEb,eACA,qBAAC;KAAI,WAAU;gBACd,oBAAC;MAAI,WAAU;gBACd,oBAAC;OACA,WAAU;OACV,OAAO,EAAE,OAAO,GAAG,eAAe,IAAI;QACrC;OACG,EACN,qBAAC,qBAAM,gBAAe,OAAQ;MACzB,EAEP,oBAAC;KAAI,WAAU;eACb,MAAM,KAAK,MAAM,UACjB,qBAAC;MACA,WAAW,GACV,oEACA,eAAe,aACf;;OAGD,oBAACC;QAAK,WAAU;QAAU,MAAK;SAAe;OAC9C,oBAAC;QAAK,WAAU;kBAA0B,KAAK;SAAY;OAC3D,oBAAC;QAAK,WAAU;kBACd,eAAe,KAAK,KAAK;SACpB;OACN,gBAAgB,CAAC,eACjB,oBAAC;QACA,cAAY,KAAK,6BAA6B,EAC7C,UAAU,KAAK,MACf,CAAC;QACF,WAAU;QACV,eAAe,aAAa,MAAM;QAClC,MAAK;kBAEL,oBAACA;SAAK,WAAU;SAAU,MAAK;UAAU;SACjC;;QAjBL,GAAG,KAAK,KAAK,GAAG,QAmBhB,CACL;MACG;KACD;GAIP,qBAAC;IAAI,WAAU;eACd,oBAACC;KACA;KACA,WAAW,GACV,+HACA,UACA;KACS;KACH;KACG;KACI;KACd,UAAU;KACV,aAAa;KACb,KAAK;KACE;MACN,EAEF,qBAAC;KAAI,WAAU;gBACd,oBAAC,cAAY,EAEb,qBAAC;MAAI,WAAU;iBAEb,gBACA,4CACC,oBAAC;OACA,cAAY,KAAK,6BAA6B;OAC9C,WAAW,GACV,iLACA,MAAM,UAAU,YAAY,aAC5B;OACD,UACC,YAAY,gBAAgB,MAAM,UAAU;OAE7C,SAAS;OACT,MAAK;iBAEL,oBAACD;QAAK,WAAU;QAAU,MAAK;SAAe;QACtC,EAET,oBAACE;OACA,QAAQ;OACR,WAAU;OACV,UACC,YAAY,gBAAgB,MAAM,UAAU;OAE/B;OACd,KAAK;QACJ,IACA,EAIJ,oBAAC;OAAW,UAAU,CAAC;OAAwB;QAAe;OACzD;MACD;KACD;;GACA;;AAUT,MAAaC,cAAyC,EACrD,WACA,WAAW,OACX,cAAc,YAEd,oBAACC;CACA,WAAW,GACV,+IACA,UACA;CACS;CACV,MAAK;WAEJ,cACA,oBAAC,SAAI,WAAU,sFAAsF,GAErG,oBAACJ;EAAK,WAAU;EAAU;EAAc,MAAK;GAAS;EAErC"}
@@ -1,6 +1,6 @@
1
1
  import { useSupportNavigation } from "../store/support-store.js";
2
- import icons_default from "./icons.js";
3
2
  import { CoButton } from "./button.js";
3
+ import icons_default from "./icons.js";
4
4
  import { Text } from "../text/index.js";
5
5
  import { jsx, jsxs } from "react/jsx-runtime";
6
6
 
@@ -0,0 +1,50 @@
1
+ import { ReactElement } from "react";
2
+
3
+ //#region src/support/components/online-indicator.d.ts
4
+ type AgentStatus = "online" | "away" | "offline";
5
+ /**
6
+ * Determines an agent's status based on their lastSeenAt timestamp.
7
+ * - Online: seen within the last 15 minutes
8
+ * - Away: seen within the last hour
9
+ * - Offline: not seen in the last hour or no timestamp
10
+ */
11
+ declare function getAgentStatus(lastSeenAt: string | null | undefined): AgentStatus;
12
+ /**
13
+ * @deprecated Use getAgentStatus instead for more granular status.
14
+ * Determines if an agent is online based on their lastSeenAt timestamp.
15
+ * An agent is considered online if they were seen within the last 15 minutes.
16
+ */
17
+ declare function isAgentOnline(lastSeenAt: string | null | undefined): boolean;
18
+ type OnlineIndicatorProps = {
19
+ /**
20
+ * The agent's current status.
21
+ * - "online": green dot
22
+ * - "away": orange dot
23
+ * - "offline": nothing rendered
24
+ */
25
+ status: AgentStatus;
26
+ /**
27
+ * Size of the indicator dot in pixels.
28
+ * @default 6
29
+ */
30
+ size?: number;
31
+ /**
32
+ * Additional class names for positioning.
33
+ */
34
+ className?: string;
35
+ };
36
+ /**
37
+ * A small dot indicator showing agent status.
38
+ * - Green for online (active in last 15 minutes)
39
+ * - Orange for away (active in last hour)
40
+ * Features a transparent outline that adapts to any background using
41
+ * the theme's background color via box-shadow.
42
+ */
43
+ declare function OnlineIndicator({
44
+ status,
45
+ size,
46
+ className
47
+ }: OnlineIndicatorProps): ReactElement | null;
48
+ //#endregion
49
+ export { AgentStatus, OnlineIndicator, getAgentStatus, isAgentOnline };
50
+ //# sourceMappingURL=online-indicator.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"online-indicator.d.ts","names":[],"sources":["../../../src/support/components/online-indicator.tsx"],"sourcesContent":[],"mappings":";;;KAeY,WAAA;;AAAZ;AAQA;AA4BA;AAEC;AAkCD;AACC,iBAjEe,cAAA,CAiEf,UAAA,EAAA,MAAA,GAAA,IAAA,GAAA,SAAA,CAAA,EA/DE,WA+DF;;;;;;iBArCe,aAAA;KAIX,oBAAA;;;;;;;UAOI;;;;;;;;;;;;;;;;;;iBAyBO,eAAA;;;;GAIb,uBAAuB"}
@@ -0,0 +1,65 @@
1
+ import { cn } from "../utils/index.js";
2
+ import { jsx } from "react/jsx-runtime";
3
+
4
+ //#region src/support/components/online-indicator.tsx
5
+ /**
6
+ * Threshold in milliseconds for considering an agent as "online".
7
+ * Default: 15 minutes (900,000ms)
8
+ */
9
+ const ONLINE_THRESHOLD_MS = 900 * 1e3;
10
+ /**
11
+ * Threshold in milliseconds for considering an agent as "away".
12
+ * Default: 1 hour (3,600,000ms)
13
+ */
14
+ const AWAY_THRESHOLD_MS = 3600 * 1e3;
15
+ /**
16
+ * Determines an agent's status based on their lastSeenAt timestamp.
17
+ * - Online: seen within the last 15 minutes
18
+ * - Away: seen within the last hour
19
+ * - Offline: not seen in the last hour or no timestamp
20
+ */
21
+ function getAgentStatus(lastSeenAt) {
22
+ if (!lastSeenAt) return "offline";
23
+ const lastSeen = new Date(lastSeenAt);
24
+ const now = Date.now();
25
+ const onlineThreshold = new Date(now - ONLINE_THRESHOLD_MS);
26
+ const awayThreshold = new Date(now - AWAY_THRESHOLD_MS);
27
+ if (lastSeen > onlineThreshold) return "online";
28
+ if (lastSeen > awayThreshold) return "away";
29
+ return "offline";
30
+ }
31
+ /**
32
+ * @deprecated Use getAgentStatus instead for more granular status.
33
+ * Determines if an agent is online based on their lastSeenAt timestamp.
34
+ * An agent is considered online if they were seen within the last 15 minutes.
35
+ */
36
+ function isAgentOnline(lastSeenAt) {
37
+ return getAgentStatus(lastSeenAt) === "online";
38
+ }
39
+ const STATUS_COLORS = {
40
+ online: "bg-co-success",
41
+ away: "bg-co-warning",
42
+ offline: ""
43
+ };
44
+ /**
45
+ * A small dot indicator showing agent status.
46
+ * - Green for online (active in last 15 minutes)
47
+ * - Orange for away (active in last hour)
48
+ * Features a transparent outline that adapts to any background using
49
+ * the theme's background color via box-shadow.
50
+ */
51
+ function OnlineIndicator({ status, size = 6, className }) {
52
+ if (status === "offline") return null;
53
+ return /* @__PURE__ */ jsx("span", {
54
+ "aria-hidden": "true",
55
+ className: cn("absolute block rounded-full ring-2 ring-co-background", STATUS_COLORS[status], className),
56
+ style: {
57
+ width: `${size}px`,
58
+ height: `${size}px`
59
+ }
60
+ });
61
+ }
62
+
63
+ //#endregion
64
+ export { OnlineIndicator, getAgentStatus, isAgentOnline };
65
+ //# sourceMappingURL=online-indicator.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"online-indicator.js","names":["STATUS_COLORS: Record<AgentStatus, string>"],"sources":["../../../src/support/components/online-indicator.tsx"],"sourcesContent":["import type { ReactElement } from \"react\";\nimport { cn } from \"../utils\";\n\n/**\n * Threshold in milliseconds for considering an agent as \"online\".\n * Default: 15 minutes (900,000ms)\n */\nconst ONLINE_THRESHOLD_MS = 15 * 60 * 1000;\n\n/**\n * Threshold in milliseconds for considering an agent as \"away\".\n * Default: 1 hour (3,600,000ms)\n */\nconst AWAY_THRESHOLD_MS = 60 * 60 * 1000;\n\nexport type AgentStatus = \"online\" | \"away\" | \"offline\";\n\n/**\n * Determines an agent's status based on their lastSeenAt timestamp.\n * - Online: seen within the last 15 minutes\n * - Away: seen within the last hour\n * - Offline: not seen in the last hour or no timestamp\n */\nexport function getAgentStatus(\n\tlastSeenAt: string | null | undefined\n): AgentStatus {\n\tif (!lastSeenAt) {\n\t\treturn \"offline\";\n\t}\n\n\tconst lastSeen = new Date(lastSeenAt);\n\tconst now = Date.now();\n\tconst onlineThreshold = new Date(now - ONLINE_THRESHOLD_MS);\n\tconst awayThreshold = new Date(now - AWAY_THRESHOLD_MS);\n\n\tif (lastSeen > onlineThreshold) {\n\t\treturn \"online\";\n\t}\n\n\tif (lastSeen > awayThreshold) {\n\t\treturn \"away\";\n\t}\n\n\treturn \"offline\";\n}\n\n/**\n * @deprecated Use getAgentStatus instead for more granular status.\n * Determines if an agent is online based on their lastSeenAt timestamp.\n * An agent is considered online if they were seen within the last 15 minutes.\n */\nexport function isAgentOnline(lastSeenAt: string | null | undefined): boolean {\n\treturn getAgentStatus(lastSeenAt) === \"online\";\n}\n\ntype OnlineIndicatorProps = {\n\t/**\n\t * The agent's current status.\n\t * - \"online\": green dot\n\t * - \"away\": orange dot\n\t * - \"offline\": nothing rendered\n\t */\n\tstatus: AgentStatus;\n\t/**\n\t * Size of the indicator dot in pixels.\n\t * @default 6\n\t */\n\tsize?: number;\n\t/**\n\t * Additional class names for positioning.\n\t */\n\tclassName?: string;\n};\n\nconst STATUS_COLORS: Record<AgentStatus, string> = {\n\tonline: \"bg-co-success\",\n\taway: \"bg-co-warning\",\n\toffline: \"\",\n};\n\n/**\n * A small dot indicator showing agent status.\n * - Green for online (active in last 15 minutes)\n * - Orange for away (active in last hour)\n * Features a transparent outline that adapts to any background using\n * the theme's background color via box-shadow.\n */\nexport function OnlineIndicator({\n\tstatus,\n\tsize = 6,\n\tclassName,\n}: OnlineIndicatorProps): ReactElement | null {\n\tif (status === \"offline\") {\n\t\treturn null;\n\t}\n\n\treturn (\n\t\t<span\n\t\t\taria-hidden=\"true\"\n\t\t\tclassName={cn(\n\t\t\t\t\"absolute block rounded-full ring-2 ring-co-background\",\n\t\t\t\tSTATUS_COLORS[status],\n\t\t\t\tclassName\n\t\t\t)}\n\t\t\tstyle={{\n\t\t\t\twidth: `${size}px`,\n\t\t\t\theight: `${size}px`,\n\t\t\t}}\n\t\t/>\n\t);\n}\n"],"mappings":";;;;;;;;AAOA,MAAM,sBAAsB,MAAU;;;;;AAMtC,MAAM,oBAAoB,OAAU;;;;;;;AAUpC,SAAgB,eACf,YACc;AACd,KAAI,CAAC,WACJ,QAAO;CAGR,MAAM,WAAW,IAAI,KAAK,WAAW;CACrC,MAAM,MAAM,KAAK,KAAK;CACtB,MAAM,kBAAkB,IAAI,KAAK,MAAM,oBAAoB;CAC3D,MAAM,gBAAgB,IAAI,KAAK,MAAM,kBAAkB;AAEvD,KAAI,WAAW,gBACd,QAAO;AAGR,KAAI,WAAW,cACd,QAAO;AAGR,QAAO;;;;;;;AAQR,SAAgB,cAAc,YAAgD;AAC7E,QAAO,eAAe,WAAW,KAAK;;AAsBvC,MAAMA,gBAA6C;CAClD,QAAQ;CACR,MAAM;CACN,SAAS;CACT;;;;;;;;AASD,SAAgB,gBAAgB,EAC/B,QACA,OAAO,GACP,aAC6C;AAC7C,KAAI,WAAW,UACd,QAAO;AAGR,QACC,oBAAC;EACA,eAAY;EACZ,WAAW,GACV,yDACA,cAAc,SACd,UACA;EACD,OAAO;GACN,OAAO,GAAG,KAAK;GACf,QAAQ,GAAG,KAAK;GAChB;GACA"}
@@ -1,6 +1,5 @@
1
1
  "use client";
2
2
 
3
-
4
3
  import { cn } from "../utils/index.js";
5
4
  import { TriggerRefProvider } from "../context/positioning.js";
6
5
  import { jsx } from "react/jsx-runtime";
@@ -1 +1 @@
1
- {"version":3,"file":"root.js","names":["Root: React.FC<RootProps>"],"sources":["../../../src/support/components/root.tsx"],"sourcesContent":["\"use client\";\n\nimport { motion } from \"motion/react\";\nimport type * as React from \"react\";\nimport { TriggerRefProvider } from \"../context/positioning\";\nimport { cn } from \"../utils\";\n\nexport type RootProps = {\n\tclassName?: string;\n\tchildren: React.ReactNode;\n};\n\n/**\n * Root wrapper component that provides the positioning context.\n * Contains the trigger and content as siblings.\n *\n * @example\n * <Support.Root>\n * <Support.Trigger>Help</Support.Trigger>\n * <Support.Content>\n * <Support.Router />\n * </Support.Content>\n * </Support.Root>\n */\nexport const Root: React.FC<RootProps> = ({ className, children }) => (\n\t<TriggerRefProvider>\n\t\t<motion.div\n\t\t\tanimate={{ opacity: 1 }}\n\t\t\tclassName={cn(\"cossistant relative\", className)}\n\t\t\tinitial={{ opacity: 0 }}\n\t\t\tlayout=\"position\"\n\t\t\ttransition={{\n\t\t\t\tdefault: { ease: \"anticipate\" },\n\t\t\t\tlayout: { duration: 0.3 },\n\t\t\t}}\n\t\t>\n\t\t\t{children}\n\t\t</motion.div>\n\t</TriggerRefProvider>\n);\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;AAwBA,MAAaA,QAA6B,EAAE,WAAW,eACtD,oBAAC,gCACA,oBAAC,OAAO;CACP,SAAS,EAAE,SAAS,GAAG;CACvB,WAAW,GAAG,uBAAuB,UAAU;CAC/C,SAAS,EAAE,SAAS,GAAG;CACvB,QAAO;CACP,YAAY;EACX,SAAS,EAAE,MAAM,cAAc;EAC/B,QAAQ,EAAE,UAAU,IAAK;EACzB;CAEA;EACW,GACO"}
1
+ {"version":3,"file":"root.js","names":["Root: React.FC<RootProps>"],"sources":["../../../src/support/components/root.tsx"],"sourcesContent":["\"use client\";\n\nimport { motion } from \"motion/react\";\nimport type * as React from \"react\";\nimport { TriggerRefProvider } from \"../context/positioning\";\nimport { cn } from \"../utils\";\n\nexport type RootProps = {\n\tclassName?: string;\n\tchildren: React.ReactNode;\n};\n\n/**\n * Root wrapper component that provides the positioning context.\n * Contains the trigger and content as siblings.\n *\n * @example\n * <Support.Root>\n * <Support.Trigger>Help</Support.Trigger>\n * <Support.Content>\n * <Support.Router />\n * </Support.Content>\n * </Support.Root>\n */\nexport const Root: React.FC<RootProps> = ({ className, children }) => (\n\t<TriggerRefProvider>\n\t\t<motion.div\n\t\t\tanimate={{ opacity: 1 }}\n\t\t\tclassName={cn(\"cossistant relative\", className)}\n\t\t\tinitial={{ opacity: 0 }}\n\t\t\tlayout=\"position\"\n\t\t\ttransition={{\n\t\t\t\tdefault: { ease: \"anticipate\" },\n\t\t\t\tlayout: { duration: 0.3 },\n\t\t\t}}\n\t\t>\n\t\t\t{children}\n\t\t</motion.div>\n\t</TriggerRefProvider>\n);\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAwBA,MAAaA,QAA6B,EAAE,WAAW,eACtD,oBAAC,gCACA,oBAAC,OAAO;CACP,SAAS,EAAE,SAAS,GAAG;CACvB,WAAW,GAAG,uBAAuB,UAAU;CAC/C,SAAS,EAAE,SAAS,GAAG;CACvB,QAAO;CACP,YAAY;EACX,SAAS,EAAE,MAAM,cAAc;EAC/B,QAAQ,EAAE,UAAU,IAAK;EACzB;CAEA;EACW,GACO"}
@@ -42,7 +42,7 @@ const IdentificationTimelineTool = ({ conversationId }) => {
42
42
  conversationId,
43
43
  item: {
44
44
  type: ConversationTimelineType.EVENT,
45
- text: text("component.identificationTool.eventLog"),
45
+ text: "",
46
46
  tool: null,
47
47
  parts: [{
48
48
  type: "event",
@@ -51,16 +51,16 @@ const IdentificationTimelineTool = ({ conversationId }) => {
51
51
  actorAiAgentId: null,
52
52
  targetUserId: null,
53
53
  targetAiAgentId: null,
54
- message: text("component.identificationTool.eventLog")
54
+ message: null
55
55
  }],
56
56
  visitorId: identifyResult.visitorId,
57
57
  visibility: TimelineItemVisibility.PUBLIC
58
58
  }
59
59
  };
60
- await client.sendMessage(payload);
60
+ await client?.sendMessage(payload);
61
61
  setStatus("success");
62
62
  setEmail("");
63
- client.fetchWebsite({ force: true }).catch(() => {});
63
+ client?.fetchWebsite({ force: true }).catch(() => {});
64
64
  } catch {
65
65
  setStatus("error");
66
66
  setErrorMessage(text("component.identificationTool.error"));
@@ -1 +1 @@
1
- {"version":3,"file":"timeline-identification-tool.js","names":["IdentificationTimelineTool: React.FC<\n\tConversationTimelineToolProps\n>","payload: SendTimelineItemRequest"],"sources":["../../../src/support/components/timeline-identification-tool.tsx"],"sourcesContent":["import type { SendTimelineItemRequest } from \"@cossistant/types/api/timeline-item\";\nimport {\n\tConversationEventType,\n\tConversationTimelineType,\n\tTimelineItemVisibility,\n} from \"@cossistant/types/enums\";\nimport { type FormEventHandler, useCallback, useMemo, useState } from \"react\";\n\nimport { useVisitor } from \"../../hooks/use-visitor\";\nimport { useSupport } from \"../../provider\";\nimport { useSupportText } from \"../text\";\nimport { CoButton } from \"./button\";\nimport type { ConversationTimelineToolProps } from \"./conversation-timeline\";\n\nexport const IdentificationTimelineTool: React.FC<\n\tConversationTimelineToolProps\n> = ({ conversationId }) => {\n\tconst text = useSupportText();\n\tconst { identify, visitor } = useVisitor();\n\tconst { client } = useSupport();\n\tconst [email, setEmail] = useState(\"\");\n\tconst [status, setStatus] = useState<\n\t\t\"idle\" | \"submitting\" | \"success\" | \"error\"\n\t>(\"idle\");\n\tconst [errorMessage, setErrorMessage] = useState<string | null>(null);\n\n\tconst isAlreadyIdentified = Boolean(visitor?.contact);\n\tconst hasSucceeded = status === \"success\" || isAlreadyIdentified;\n\n\tconst ctaLabel = text(\"component.identificationTool.cta\");\n\tconst successLabel = text(\"component.identificationTool.success\");\n\tconst description = text(\"component.identificationTool.description\");\n\tconst title = text(\"component.identificationTool.title\");\n\n\tconst submitDisabled = hasSucceeded || status === \"submitting\";\n\n\tconst handleSubmit = useCallback<FormEventHandler<HTMLFormElement>>(\n\t\tasync (event) => {\n\t\t\tevent.preventDefault();\n\n\t\t\tif (submitDisabled) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tconst trimmedEmail = email.trim();\n\t\t\tif (!trimmedEmail) {\n\t\t\t\tsetErrorMessage(text(\"component.identificationTool.validation\"));\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tsetStatus(\"submitting\");\n\t\t\tsetErrorMessage(null);\n\n\t\t\ttry {\n\t\t\t\tconst identifyResult = await identify({ email: trimmedEmail });\n\n\t\t\t\tif (!identifyResult) {\n\t\t\t\t\tsetStatus(\"error\");\n\t\t\t\t\tsetErrorMessage(text(\"component.identificationTool.error\"));\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\tconst payload: SendTimelineItemRequest = {\n\t\t\t\t\tconversationId,\n\t\t\t\t\titem: {\n\t\t\t\t\t\ttype: ConversationTimelineType.EVENT,\n\t\t\t\t\t\ttext: text(\"component.identificationTool.eventLog\"),\n\t\t\t\t\t\ttool: null,\n\t\t\t\t\t\tparts: [\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\ttype: \"event\" as const,\n\t\t\t\t\t\t\t\teventType: ConversationEventType.VISITOR_IDENTIFIED,\n\t\t\t\t\t\t\t\tactorUserId: null,\n\t\t\t\t\t\t\t\tactorAiAgentId: null,\n\t\t\t\t\t\t\t\ttargetUserId: null,\n\t\t\t\t\t\t\t\ttargetAiAgentId: null,\n\t\t\t\t\t\t\t\tmessage: text(\"component.identificationTool.eventLog\"),\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t],\n\t\t\t\t\t\tvisitorId: identifyResult.visitorId,\n\t\t\t\t\t\tvisibility: TimelineItemVisibility.PUBLIC,\n\t\t\t\t\t},\n\t\t\t\t};\n\n\t\t\t\tawait client.sendMessage(payload);\n\n\t\t\t\tsetStatus(\"success\");\n\t\t\t\tsetEmail(\"\");\n\n\t\t\t\tvoid client.fetchWebsite({ force: true }).catch(() => {});\n\t\t\t} catch {\n\t\t\t\tsetStatus(\"error\");\n\t\t\t\tsetErrorMessage(text(\"component.identificationTool.error\"));\n\t\t\t}\n\t\t},\n\t\t[conversationId, email, identify, client, submitDisabled, text]\n\t);\n\n\tconst helperMessage = useMemo(() => {\n\t\tif (errorMessage) {\n\t\t\treturn (\n\t\t\t\t<p className=\"text-co-destructive text-xs\" role=\"alert\">\n\t\t\t\t\t{errorMessage}\n\t\t\t\t</p>\n\t\t\t);\n\t\t}\n\n\t\tif (hasSucceeded) {\n\t\t\treturn <p className=\"text-co-primary text-xs\">{successLabel}</p>;\n\t\t}\n\n\t\treturn null;\n\t}, [errorMessage, hasSucceeded, successLabel]);\n\n\treturn (\n\t\t<div className=\"mt-6 rounded border border-co-border bg-co-background p-4\">\n\t\t\t<div className=\"flex flex-col gap-3\">\n\t\t\t\t<div className=\"flex flex-col gap-1\">\n\t\t\t\t\t<h3 className=\"font-semibold text-sm\">{title}</h3>\n\t\t\t\t\t<p className=\"text-co-muted-foreground text-xs\">{description}</p>\n\t\t\t\t</div>\n\t\t\t\t{hasSucceeded ? (\n\t\t\t\t\t<div className=\"rounded-md bg-co-primary/10 px-3 py-2 text-co-primary text-sm\">\n\t\t\t\t\t\t{successLabel}\n\t\t\t\t\t</div>\n\t\t\t\t) : (\n\t\t\t\t\t<form className=\"flex gap-2\" onSubmit={handleSubmit}>\n\t\t\t\t\t\t<input\n\t\t\t\t\t\t\taria-label={text(\"component.identificationTool.inputLabel\")}\n\t\t\t\t\t\t\tautoComplete=\"email\"\n\t\t\t\t\t\t\tclassName=\"h-10 w-full rounded border border-co-border bg-transparent px-3 py-2 text-sm outline-none focus:border-co-primary focus:ring-2 focus:ring-co-primary/20\"\n\t\t\t\t\t\t\tdisabled={submitDisabled}\n\t\t\t\t\t\t\tinputMode=\"email\"\n\t\t\t\t\t\t\tonChange={(event) => setEmail(event.target.value)}\n\t\t\t\t\t\t\tplaceholder={text(\n\t\t\t\t\t\t\t\t\"component.identificationTool.inputPlaceholder\"\n\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\ttype=\"email\"\n\t\t\t\t\t\t\tvalue={email}\n\t\t\t\t\t\t/>\n\t\t\t\t\t\t{helperMessage}\n\t\t\t\t\t\t<CoButton className=\"h-10\" disabled={submitDisabled} type=\"submit\">\n\t\t\t\t\t\t\t{status === \"submitting\"\n\t\t\t\t\t\t\t\t? text(\"component.identificationTool.loading\")\n\t\t\t\t\t\t\t\t: ctaLabel}\n\t\t\t\t\t\t</CoButton>\n\t\t\t\t\t</form>\n\t\t\t\t)}\n\t\t\t</div>\n\t\t</div>\n\t);\n};\n\nIdentificationTimelineTool.displayName = \"IdentificationTimelineTool\";\n"],"mappings":";;;;;;;;;AAcA,MAAaA,8BAER,EAAE,qBAAqB;CAC3B,MAAM,OAAO,gBAAgB;CAC7B,MAAM,EAAE,UAAU,YAAY,YAAY;CAC1C,MAAM,EAAE,WAAW,YAAY;CAC/B,MAAM,CAAC,OAAO,YAAY,SAAS,GAAG;CACtC,MAAM,CAAC,QAAQ,aAAa,SAE1B,OAAO;CACT,MAAM,CAAC,cAAc,mBAAmB,SAAwB,KAAK;CAErE,MAAM,sBAAsB,QAAQ,SAAS,QAAQ;CACrD,MAAM,eAAe,WAAW,aAAa;CAE7C,MAAM,WAAW,KAAK,mCAAmC;CACzD,MAAM,eAAe,KAAK,uCAAuC;CACjE,MAAM,cAAc,KAAK,2CAA2C;CACpE,MAAM,QAAQ,KAAK,qCAAqC;CAExD,MAAM,iBAAiB,gBAAgB,WAAW;CAElD,MAAM,eAAe,YACpB,OAAO,UAAU;AAChB,QAAM,gBAAgB;AAEtB,MAAI,eACH;EAGD,MAAM,eAAe,MAAM,MAAM;AACjC,MAAI,CAAC,cAAc;AAClB,mBAAgB,KAAK,0CAA0C,CAAC;AAChE;;AAGD,YAAU,aAAa;AACvB,kBAAgB,KAAK;AAErB,MAAI;GACH,MAAM,iBAAiB,MAAM,SAAS,EAAE,OAAO,cAAc,CAAC;AAE9D,OAAI,CAAC,gBAAgB;AACpB,cAAU,QAAQ;AAClB,oBAAgB,KAAK,qCAAqC,CAAC;AAC3D;;GAGD,MAAMC,UAAmC;IACxC;IACA,MAAM;KACL,MAAM,yBAAyB;KAC/B,MAAM,KAAK,wCAAwC;KACnD,MAAM;KACN,OAAO,CACN;MACC,MAAM;MACN,WAAW,sBAAsB;MACjC,aAAa;MACb,gBAAgB;MAChB,cAAc;MACd,iBAAiB;MACjB,SAAS,KAAK,wCAAwC;MACtD,CACD;KACD,WAAW,eAAe;KAC1B,YAAY,uBAAuB;KACnC;IACD;AAED,SAAM,OAAO,YAAY,QAAQ;AAEjC,aAAU,UAAU;AACpB,YAAS,GAAG;AAEZ,GAAK,OAAO,aAAa,EAAE,OAAO,MAAM,CAAC,CAAC,YAAY,GAAG;UAClD;AACP,aAAU,QAAQ;AAClB,mBAAgB,KAAK,qCAAqC,CAAC;;IAG7D;EAAC;EAAgB;EAAO;EAAU;EAAQ;EAAgB;EAAK,CAC/D;CAED,MAAM,gBAAgB,cAAc;AACnC,MAAI,aACH,QACC,oBAAC;GAAE,WAAU;GAA8B,MAAK;aAC9C;IACE;AAIN,MAAI,aACH,QAAO,oBAAC;GAAE,WAAU;aAA2B;IAAiB;AAGjE,SAAO;IACL;EAAC;EAAc;EAAc;EAAa,CAAC;AAE9C,QACC,oBAAC;EAAI,WAAU;YACd,qBAAC;GAAI,WAAU;cACd,qBAAC;IAAI,WAAU;eACd,oBAAC;KAAG,WAAU;eAAyB;MAAW,EAClD,oBAAC;KAAE,WAAU;eAAoC;MAAgB;KAC5D,EACL,eACA,oBAAC;IAAI,WAAU;cACb;KACI,GAEN,qBAAC;IAAK,WAAU;IAAa,UAAU;;KACtC,oBAAC;MACA,cAAY,KAAK,0CAA0C;MAC3D,cAAa;MACb,WAAU;MACV,UAAU;MACV,WAAU;MACV,WAAW,UAAU,SAAS,MAAM,OAAO,MAAM;MACjD,aAAa,KACZ,gDACA;MACD,MAAK;MACL,OAAO;OACN;KACD;KACD,oBAAC;MAAS,WAAU;MAAO,UAAU;MAAgB,MAAK;gBACxD,WAAW,eACT,KAAK,uCAAuC,GAC5C;OACO;;KACL;IAEH;GACD;;AAIR,2BAA2B,cAAc"}
1
+ {"version":3,"file":"timeline-identification-tool.js","names":["IdentificationTimelineTool: React.FC<\n\tConversationTimelineToolProps\n>","payload: SendTimelineItemRequest"],"sources":["../../../src/support/components/timeline-identification-tool.tsx"],"sourcesContent":["import type { SendTimelineItemRequest } from \"@cossistant/types/api/timeline-item\";\nimport {\n\tConversationEventType,\n\tConversationTimelineType,\n\tTimelineItemVisibility,\n} from \"@cossistant/types/enums\";\nimport { type FormEventHandler, useCallback, useMemo, useState } from \"react\";\n\nimport { useVisitor } from \"../../hooks/use-visitor\";\nimport { useSupport } from \"../../provider\";\nimport { useSupportText } from \"../text\";\nimport { CoButton } from \"./button\";\nimport type { ConversationTimelineToolProps } from \"./conversation-timeline\";\n\nexport const IdentificationTimelineTool: React.FC<\n\tConversationTimelineToolProps\n> = ({ conversationId }) => {\n\tconst text = useSupportText();\n\tconst { identify, visitor } = useVisitor();\n\tconst { client } = useSupport();\n\tconst [email, setEmail] = useState(\"\");\n\tconst [status, setStatus] = useState<\n\t\t\"idle\" | \"submitting\" | \"success\" | \"error\"\n\t>(\"idle\");\n\tconst [errorMessage, setErrorMessage] = useState<string | null>(null);\n\n\tconst isAlreadyIdentified = Boolean(visitor?.contact);\n\tconst hasSucceeded = status === \"success\" || isAlreadyIdentified;\n\n\tconst ctaLabel = text(\"component.identificationTool.cta\");\n\tconst successLabel = text(\"component.identificationTool.success\");\n\tconst description = text(\"component.identificationTool.description\");\n\tconst title = text(\"component.identificationTool.title\");\n\n\tconst submitDisabled = hasSucceeded || status === \"submitting\";\n\n\tconst handleSubmit = useCallback<FormEventHandler<HTMLFormElement>>(\n\t\tasync (event) => {\n\t\t\tevent.preventDefault();\n\n\t\t\tif (submitDisabled) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tconst trimmedEmail = email.trim();\n\t\t\tif (!trimmedEmail) {\n\t\t\t\tsetErrorMessage(text(\"component.identificationTool.validation\"));\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tsetStatus(\"submitting\");\n\t\t\tsetErrorMessage(null);\n\n\t\t\ttry {\n\t\t\t\tconst identifyResult = await identify({ email: trimmedEmail });\n\n\t\t\t\tif (!identifyResult) {\n\t\t\t\t\tsetStatus(\"error\");\n\t\t\t\t\tsetErrorMessage(text(\"component.identificationTool.error\"));\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\tconst payload: SendTimelineItemRequest = {\n\t\t\t\t\tconversationId,\n\t\t\t\t\titem: {\n\t\t\t\t\t\ttype: ConversationTimelineType.EVENT,\n\t\t\t\t\t\ttext: \"\",\n\t\t\t\t\t\ttool: null,\n\t\t\t\t\t\tparts: [\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\ttype: \"event\" as const,\n\t\t\t\t\t\t\t\teventType: ConversationEventType.VISITOR_IDENTIFIED,\n\t\t\t\t\t\t\t\tactorUserId: null,\n\t\t\t\t\t\t\t\tactorAiAgentId: null,\n\t\t\t\t\t\t\t\ttargetUserId: null,\n\t\t\t\t\t\t\t\ttargetAiAgentId: null,\n\t\t\t\t\t\t\t\tmessage: null,\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t],\n\t\t\t\t\t\tvisitorId: identifyResult.visitorId,\n\t\t\t\t\t\tvisibility: TimelineItemVisibility.PUBLIC,\n\t\t\t\t\t},\n\t\t\t\t};\n\n\t\t\t\tawait client?.sendMessage(payload);\n\n\t\t\t\tsetStatus(\"success\");\n\t\t\t\tsetEmail(\"\");\n\n\t\t\t\tvoid client?.fetchWebsite({ force: true }).catch(() => {});\n\t\t\t} catch {\n\t\t\t\tsetStatus(\"error\");\n\t\t\t\tsetErrorMessage(text(\"component.identificationTool.error\"));\n\t\t\t}\n\t\t},\n\t\t[conversationId, email, identify, client, submitDisabled, text]\n\t);\n\n\tconst helperMessage = useMemo(() => {\n\t\tif (errorMessage) {\n\t\t\treturn (\n\t\t\t\t<p className=\"text-co-destructive text-xs\" role=\"alert\">\n\t\t\t\t\t{errorMessage}\n\t\t\t\t</p>\n\t\t\t);\n\t\t}\n\n\t\tif (hasSucceeded) {\n\t\t\treturn <p className=\"text-co-primary text-xs\">{successLabel}</p>;\n\t\t}\n\n\t\treturn null;\n\t}, [errorMessage, hasSucceeded, successLabel]);\n\n\treturn (\n\t\t<div className=\"mt-6 rounded border border-co-border bg-co-background p-4\">\n\t\t\t<div className=\"flex flex-col gap-3\">\n\t\t\t\t<div className=\"flex flex-col gap-1\">\n\t\t\t\t\t<h3 className=\"font-semibold text-sm\">{title}</h3>\n\t\t\t\t\t<p className=\"text-co-muted-foreground text-xs\">{description}</p>\n\t\t\t\t</div>\n\t\t\t\t{hasSucceeded ? (\n\t\t\t\t\t<div className=\"rounded-md bg-co-primary/10 px-3 py-2 text-co-primary text-sm\">\n\t\t\t\t\t\t{successLabel}\n\t\t\t\t\t</div>\n\t\t\t\t) : (\n\t\t\t\t\t<form className=\"flex gap-2\" onSubmit={handleSubmit}>\n\t\t\t\t\t\t<input\n\t\t\t\t\t\t\taria-label={text(\"component.identificationTool.inputLabel\")}\n\t\t\t\t\t\t\tautoComplete=\"email\"\n\t\t\t\t\t\t\tclassName=\"h-10 w-full rounded border border-co-border bg-transparent px-3 py-2 text-sm outline-none focus:border-co-primary focus:ring-2 focus:ring-co-primary/20\"\n\t\t\t\t\t\t\tdisabled={submitDisabled}\n\t\t\t\t\t\t\tinputMode=\"email\"\n\t\t\t\t\t\t\tonChange={(event) => setEmail(event.target.value)}\n\t\t\t\t\t\t\tplaceholder={text(\n\t\t\t\t\t\t\t\t\"component.identificationTool.inputPlaceholder\"\n\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\ttype=\"email\"\n\t\t\t\t\t\t\tvalue={email}\n\t\t\t\t\t\t/>\n\t\t\t\t\t\t{helperMessage}\n\t\t\t\t\t\t<CoButton className=\"h-10\" disabled={submitDisabled} type=\"submit\">\n\t\t\t\t\t\t\t{status === \"submitting\"\n\t\t\t\t\t\t\t\t? text(\"component.identificationTool.loading\")\n\t\t\t\t\t\t\t\t: ctaLabel}\n\t\t\t\t\t\t</CoButton>\n\t\t\t\t\t</form>\n\t\t\t\t)}\n\t\t\t</div>\n\t\t</div>\n\t);\n};\n\nIdentificationTimelineTool.displayName = \"IdentificationTimelineTool\";\n"],"mappings":";;;;;;;;;AAcA,MAAaA,8BAER,EAAE,qBAAqB;CAC3B,MAAM,OAAO,gBAAgB;CAC7B,MAAM,EAAE,UAAU,YAAY,YAAY;CAC1C,MAAM,EAAE,WAAW,YAAY;CAC/B,MAAM,CAAC,OAAO,YAAY,SAAS,GAAG;CACtC,MAAM,CAAC,QAAQ,aAAa,SAE1B,OAAO;CACT,MAAM,CAAC,cAAc,mBAAmB,SAAwB,KAAK;CAErE,MAAM,sBAAsB,QAAQ,SAAS,QAAQ;CACrD,MAAM,eAAe,WAAW,aAAa;CAE7C,MAAM,WAAW,KAAK,mCAAmC;CACzD,MAAM,eAAe,KAAK,uCAAuC;CACjE,MAAM,cAAc,KAAK,2CAA2C;CACpE,MAAM,QAAQ,KAAK,qCAAqC;CAExD,MAAM,iBAAiB,gBAAgB,WAAW;CAElD,MAAM,eAAe,YACpB,OAAO,UAAU;AAChB,QAAM,gBAAgB;AAEtB,MAAI,eACH;EAGD,MAAM,eAAe,MAAM,MAAM;AACjC,MAAI,CAAC,cAAc;AAClB,mBAAgB,KAAK,0CAA0C,CAAC;AAChE;;AAGD,YAAU,aAAa;AACvB,kBAAgB,KAAK;AAErB,MAAI;GACH,MAAM,iBAAiB,MAAM,SAAS,EAAE,OAAO,cAAc,CAAC;AAE9D,OAAI,CAAC,gBAAgB;AACpB,cAAU,QAAQ;AAClB,oBAAgB,KAAK,qCAAqC,CAAC;AAC3D;;GAGD,MAAMC,UAAmC;IACxC;IACA,MAAM;KACL,MAAM,yBAAyB;KAC/B,MAAM;KACN,MAAM;KACN,OAAO,CACN;MACC,MAAM;MACN,WAAW,sBAAsB;MACjC,aAAa;MACb,gBAAgB;MAChB,cAAc;MACd,iBAAiB;MACjB,SAAS;MACT,CACD;KACD,WAAW,eAAe;KAC1B,YAAY,uBAAuB;KACnC;IACD;AAED,SAAM,QAAQ,YAAY,QAAQ;AAElC,aAAU,UAAU;AACpB,YAAS,GAAG;AAEZ,GAAK,QAAQ,aAAa,EAAE,OAAO,MAAM,CAAC,CAAC,YAAY,GAAG;UACnD;AACP,aAAU,QAAQ;AAClB,mBAAgB,KAAK,qCAAqC,CAAC;;IAG7D;EAAC;EAAgB;EAAO;EAAU;EAAQ;EAAgB;EAAK,CAC/D;CAED,MAAM,gBAAgB,cAAc;AACnC,MAAI,aACH,QACC,oBAAC;GAAE,WAAU;GAA8B,MAAK;aAC9C;IACE;AAIN,MAAI,aACH,QAAO,oBAAC;GAAE,WAAU;aAA2B;IAAiB;AAGjE,SAAO;IACL;EAAC;EAAc;EAAc;EAAa,CAAC;AAE9C,QACC,oBAAC;EAAI,WAAU;YACd,qBAAC;GAAI,WAAU;cACd,qBAAC;IAAI,WAAU;eACd,oBAAC;KAAG,WAAU;eAAyB;MAAW,EAClD,oBAAC;KAAE,WAAU;eAAoC;MAAgB;KAC5D,EACL,eACA,oBAAC;IAAI,WAAU;cACb;KACI,GAEN,qBAAC;IAAK,WAAU;IAAa,UAAU;;KACtC,oBAAC;MACA,cAAY,KAAK,0CAA0C;MAC3D,cAAa;MACb,WAAU;MACV,UAAU;MACV,WAAU;MACV,WAAW,UAAU,SAAS,MAAM,OAAO,MAAM;MACjD,aAAa,KACZ,gDACA;MACD,MAAK;MACL,OAAO;OACN;KACD;KACD,oBAAC;MAAS,WAAU;MAAO,UAAU;MAAgB,MAAK;gBACxD,WAAW,eACT,KAAK,uCAAuC,GAC5C;OACO;;KACL;IAEH;GACD;;AAIR,2BAA2B,cAAc"}
@@ -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 React from "react";
3
3
  import { AvailableAIAgent, AvailableHumanAgent } from "@cossistant/types";
4
4
 
@@ -1 +1 @@
1
- {"version":3,"file":"timeline-message-group.d.ts","names":[],"sources":["../../../src/support/components/timeline-message-group.tsx"],"sourcesContent":[],"mappings":";;;;;KAoCY,yBAAA;SACJ;EADI,iBAAA,EAEQ,gBAFiB,EAAA;EAC7B,oBAAA,EAEe,mBAFf,EAAA;EACY,gBAAA,CAAA,EAAA,MAAA;EACG,SAAA,CAAA,EAAA,SAAA,MAAA,EAAA;EAAmB,WAAA,CAAA,EAAA,SAAA,MAAA,EAAA;AAS1C,CAAA;cAAa,sBAAsB,KAAA,CAAM,GAAG"}
1
+ {"version":3,"file":"timeline-message-group.d.ts","names":[],"sources":["../../../src/support/components/timeline-message-group.tsx"],"sourcesContent":[],"mappings":";;;;;KAmCY,yBAAA;SACJ;EADI,iBAAA,EAEQ,gBAFiB,EAAA;EAC7B,oBAAA,EAEe,mBAFf,EAAA;EACY,gBAAA,CAAA,EAAA,MAAA;EACG,SAAA,CAAA,EAAA,SAAA,MAAA,EAAA;EAAmB,WAAA,CAAA,EAAA,SAAA,MAAA,EAAA;AAS1C,CAAA;cAAa,sBAAsB,KAAA,CAAM,GAAG"}
@@ -1,6 +1,5 @@
1
1
  import { cn } from "../utils/index.js";
2
2
  import { Avatar } from "./avatar.js";
3
- import { CossistantLogo } from "./cossistant-branding.js";
4
3
  import { TimelineItemGroup, TimelineItemGroupAvatar, TimelineItemGroupContent, TimelineItemGroupHeader, TimelineItemGroupSeenIndicator } from "../../primitives/timeline-item-group.js";
5
4
  import { TimelineMessageItem } from "./timeline-message-item.js";
6
5
  import { SenderType } from "@cossistant/types";
@@ -53,9 +52,12 @@ const TimelineMessageGroup = ({ items, availableAIAgents, availableHumanAgents,
53
52
  className: cn("flex w-full gap-2", isSentByViewer && "flex-row-reverse", isReceivedByViewer && "flex-row"),
54
53
  children: [isReceivedByViewer && /* @__PURE__ */ jsx(TimelineItemGroupAvatar, {
55
54
  className: "flex flex-shrink-0 flex-col justify-end",
56
- children: isAI ? /* @__PURE__ */ jsx("div", {
57
- className: "flex size-6 items-center justify-center rounded-full bg-co-primary/10",
58
- children: /* @__PURE__ */ jsx(CossistantLogo, { className: "h-4 w-4 text-co-primary" })
55
+ children: isAI ? /* @__PURE__ */ jsx(Avatar, {
56
+ className: "size-6",
57
+ image: aiAgent?.image,
58
+ isAI: true,
59
+ name: aiAgent?.name || "AI Assistant",
60
+ showBackground: !!aiAgent?.image
59
61
  }) : /* @__PURE__ */ jsx(Avatar, {
60
62
  className: "size-6",
61
63
  image: humanAgent?.image,
@@ -1 +1 @@
1
- {"version":3,"file":"timeline-message-group.js","names":["EMPTY_SEEN_BY_IDS: readonly string[]","EMPTY_SEEN_BY_NAMES: readonly string[]","TimelineMessageGroup: React.FC<TimelineMessageGroupProps>","PrimitiveTimelineItemGroup"],"sources":["../../../src/support/components/timeline-message-group.tsx"],"sourcesContent":["import type { AvailableAIAgent, AvailableHumanAgent } from \"@cossistant/types\";\nimport { SenderType } from \"@cossistant/types\";\nimport type { TimelineItem } from \"@cossistant/types/api/timeline-item\";\nimport { motion } from \"motion/react\";\nimport type React from \"react\";\nimport {\n\tTimelineItemGroup as PrimitiveTimelineItemGroup,\n\tTimelineItemGroupAvatar,\n\tTimelineItemGroupContent,\n\tTimelineItemGroupHeader,\n\tTimelineItemGroupSeenIndicator,\n} from \"../../primitives/timeline-item-group\";\nimport { cn } from \"../utils\";\nimport { Avatar } from \"./avatar\";\nimport { CossistantLogo } from \"./cossistant-branding\";\nimport { TimelineMessageItem } from \"./timeline-message-item\";\n\nconst MESSAGE_ANIMATION = {\n\tinitial: { opacity: 0, y: 6 },\n\tanimate: { opacity: 1, y: 0 },\n\texit: { opacity: 0 },\n\ttransition: {\n\t\tduration: 0.1,\n\t\tease: [0.25, 0.46, 0.45, 0.94] as const, // easeOutCubic\n\t},\n} as const;\n\nconst SEEN_ANIMATION = {\n\tinitial: { opacity: 0 },\n\tanimate: { opacity: 1 },\n\ttransition: {\n\t\tduration: 0.1,\n\t\tease: \"easeOut\" as const,\n\t},\n} as const;\n\nexport type TimelineMessageGroupProps = {\n\titems: TimelineItem[];\n\tavailableAIAgents: AvailableAIAgent[];\n\tavailableHumanAgents: AvailableHumanAgent[];\n\tcurrentVisitorId?: string;\n\tseenByIds?: readonly string[];\n\tseenByNames?: readonly string[];\n};\n\nconst EMPTY_SEEN_BY_IDS: readonly string[] = Object.freeze([]);\nconst EMPTY_SEEN_BY_NAMES: readonly string[] = Object.freeze([]);\n\nexport const TimelineMessageGroup: React.FC<TimelineMessageGroupProps> = ({\n\titems,\n\tavailableAIAgents,\n\tavailableHumanAgents,\n\tcurrentVisitorId,\n\tseenByIds = EMPTY_SEEN_BY_IDS,\n\tseenByNames = EMPTY_SEEN_BY_NAMES,\n}) => {\n\t// Get agent info for the sender\n\tconst firstItem = items[0];\n\tconst humanAgent = availableHumanAgents.find(\n\t\t(agent) => agent.id === firstItem?.userId\n\t);\n\tconst aiAgent = availableAIAgents.find(\n\t\t(agent) => agent.id === firstItem?.aiAgentId\n\t);\n\n\tif (items.length === 0) {\n\t\treturn null;\n\t}\n\n\tconst hasSeenIndicator = seenByIds.length > 0;\n\n\treturn (\n\t\t<PrimitiveTimelineItemGroup\n\t\t\titems={items}\n\t\t\tseenByIds={seenByIds}\n\t\t\tviewerId={currentVisitorId}\n\t\t\tviewerType={SenderType.VISITOR}\n\t\t>\n\t\t\t{({\n\t\t\t\tisSentByViewer,\n\t\t\t\tisReceivedByViewer,\n\t\t\t\tisVisitor,\n\t\t\t\tisAI,\n\t\t\t\tisTeamMember,\n\t\t\t}) => (\n\t\t\t\t<div\n\t\t\t\t\tclassName={cn(\n\t\t\t\t\t\t\"flex w-full gap-2\",\n\t\t\t\t\t\t// Support widget POV: visitor messages are sent (right side)\n\t\t\t\t\t\t// Agent messages are received (left side)\n\t\t\t\t\t\tisSentByViewer && \"flex-row-reverse\",\n\t\t\t\t\t\tisReceivedByViewer && \"flex-row\"\n\t\t\t\t\t)}\n\t\t\t\t>\n\t\t\t\t\t{/* Avatar - only show for received messages (agents) */}\n\t\t\t\t\t{isReceivedByViewer && (\n\t\t\t\t\t\t<TimelineItemGroupAvatar className=\"flex flex-shrink-0 flex-col justify-end\">\n\t\t\t\t\t\t\t{isAI ? (\n\t\t\t\t\t\t\t\t<div className=\"flex size-6 items-center justify-center rounded-full bg-co-primary/10\">\n\t\t\t\t\t\t\t\t\t<CossistantLogo className=\"h-4 w-4 text-co-primary\" />\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\t\t<Avatar\n\t\t\t\t\t\t\t\t\tclassName=\"size-6\"\n\t\t\t\t\t\t\t\t\timage={humanAgent?.image}\n\t\t\t\t\t\t\t\t\tname={humanAgent?.name || \"Support\"}\n\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t</TimelineItemGroupAvatar>\n\t\t\t\t\t)}\n\n\t\t\t\t\t<TimelineItemGroupContent\n\t\t\t\t\t\tclassName={cn(\"flex flex-col gap-1\", isSentByViewer && \"items-end\")}\n\t\t\t\t\t>\n\t\t\t\t\t\t{/* Header - show sender name for received messages (agents) */}\n\t\t\t\t\t\t{isReceivedByViewer && (\n\t\t\t\t\t\t\t<TimelineItemGroupHeader className=\"px-1 text-co-muted-foreground text-xs\">\n\t\t\t\t\t\t\t\t{isAI\n\t\t\t\t\t\t\t\t\t? aiAgent?.name || \"AI Assistant\"\n\t\t\t\t\t\t\t\t\t: humanAgent?.name || \"Support\"}\n\t\t\t\t\t\t\t</TimelineItemGroupHeader>\n\t\t\t\t\t\t)}\n\n\t\t\t\t\t\t{items.map((item, index) => (\n\t\t\t\t\t\t\t<motion.div key={item.id} {...MESSAGE_ANIMATION}>\n\t\t\t\t\t\t\t\t<TimelineMessageItem\n\t\t\t\t\t\t\t\t\tisLast={index === items.length - 1}\n\t\t\t\t\t\t\t\t\tisSentByViewer={isSentByViewer}\n\t\t\t\t\t\t\t\t\titem={item}\n\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t</motion.div>\n\t\t\t\t\t\t))}\n\n\t\t\t\t\t\t{isSentByViewer && (\n\t\t\t\t\t\t\t<div className={cn(\"\", hasSeenIndicator && \"mt-2\")}>\n\t\t\t\t\t\t\t\t<div className=\"min-h-[1.25rem]\">\n\t\t\t\t\t\t\t\t\t{hasSeenIndicator && (\n\t\t\t\t\t\t\t\t\t\t<motion.div key=\"seen-indicator\" {...SEEN_ANIMATION}>\n\t\t\t\t\t\t\t\t\t\t\t<TimelineItemGroupSeenIndicator\n\t\t\t\t\t\t\t\t\t\t\t\tclassName=\"px-1 text-co-muted-foreground text-xs\"\n\t\t\t\t\t\t\t\t\t\t\t\tseenByIds={seenByIds}\n\t\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t\t{() =>\n\t\t\t\t\t\t\t\t\t\t\t\t\tseenByNames.length > 0\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t? `Seen by ${seenByNames.join(\", \")}`\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t: \"Seen\"\n\t\t\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t\t\t</TimelineItemGroupSeenIndicator>\n\t\t\t\t\t\t\t\t\t\t</motion.div>\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</div>\n\t\t\t\t\t\t)}\n\t\t\t\t\t</TimelineItemGroupContent>\n\t\t\t\t</div>\n\t\t\t)}\n\t\t</PrimitiveTimelineItemGroup>\n\t);\n};\n\nTimelineMessageGroup.displayName = \"TimelineMessageGroup\";\n"],"mappings":";;;;;;;;;;AAiBA,MAAM,oBAAoB;CACzB,SAAS;EAAE,SAAS;EAAG,GAAG;EAAG;CAC7B,SAAS;EAAE,SAAS;EAAG,GAAG;EAAG;CAC7B,MAAM,EAAE,SAAS,GAAG;CACpB,YAAY;EACX,UAAU;EACV,MAAM;GAAC;GAAM;GAAM;GAAM;GAAK;EAC9B;CACD;AAED,MAAM,iBAAiB;CACtB,SAAS,EAAE,SAAS,GAAG;CACvB,SAAS,EAAE,SAAS,GAAG;CACvB,YAAY;EACX,UAAU;EACV,MAAM;EACN;CACD;AAWD,MAAMA,oBAAuC,OAAO,OAAO,EAAE,CAAC;AAC9D,MAAMC,sBAAyC,OAAO,OAAO,EAAE,CAAC;AAEhE,MAAaC,wBAA6D,EACzE,OACA,mBACA,sBACA,kBACA,YAAY,mBACZ,cAAc,0BACT;CAEL,MAAM,YAAY,MAAM;CACxB,MAAM,aAAa,qBAAqB,MACtC,UAAU,MAAM,OAAO,WAAW,OACnC;CACD,MAAM,UAAU,kBAAkB,MAChC,UAAU,MAAM,OAAO,WAAW,UACnC;AAED,KAAI,MAAM,WAAW,EACpB,QAAO;CAGR,MAAM,mBAAmB,UAAU,SAAS;AAE5C,QACC,oBAACC;EACO;EACI;EACX,UAAU;EACV,YAAY,WAAW;aAErB,EACD,gBACA,oBACA,WACA,MACA,mBAEA,qBAAC;GACA,WAAW,GACV,qBAGA,kBAAkB,oBAClB,sBAAsB,WACtB;cAGA,sBACA,oBAAC;IAAwB,WAAU;cACjC,OACA,oBAAC;KAAI,WAAU;eACd,oBAAC,kBAAe,WAAU,4BAA4B;MACjD,GAEN,oBAAC;KACA,WAAU;KACV,OAAO,YAAY;KACnB,MAAM,YAAY,QAAQ;MACzB;KAEsB,EAG3B,qBAAC;IACA,WAAW,GAAG,uBAAuB,kBAAkB,YAAY;;KAGlE,sBACA,oBAAC;MAAwB,WAAU;gBACjC,OACE,SAAS,QAAQ,iBACjB,YAAY,QAAQ;OACE;KAG1B,MAAM,KAAK,MAAM,UACjB,oBAAC,OAAO;MAAkB,GAAI;gBAC7B,oBAAC;OACA,QAAQ,UAAU,MAAM,SAAS;OACjB;OACV;QACL;QALc,KAAK,GAMT,CACZ;KAED,kBACA,oBAAC;MAAI,WAAW,GAAG,IAAI,oBAAoB,OAAO;gBACjD,oBAAC;OAAI,WAAU;iBACb,oBACA,oBAAC,OAAO;QAAyB,GAAI;kBACpC,oBAAC;SACA,WAAU;SACC;yBAGV,YAAY,SAAS,IAClB,WAAW,YAAY,KAAK,KAAK,KACjC;UAE4B;UAVlB,iBAWH;QAET;OACD;;KAEmB;IACtB;GAEqB;;AAI/B,qBAAqB,cAAc"}
1
+ {"version":3,"file":"timeline-message-group.js","names":["EMPTY_SEEN_BY_IDS: readonly string[]","EMPTY_SEEN_BY_NAMES: readonly string[]","TimelineMessageGroup: React.FC<TimelineMessageGroupProps>","PrimitiveTimelineItemGroup"],"sources":["../../../src/support/components/timeline-message-group.tsx"],"sourcesContent":["import type { AvailableAIAgent, AvailableHumanAgent } from \"@cossistant/types\";\nimport { SenderType } from \"@cossistant/types\";\nimport type { TimelineItem } from \"@cossistant/types/api/timeline-item\";\nimport { motion } from \"motion/react\";\nimport type React from \"react\";\nimport {\n\tTimelineItemGroup as PrimitiveTimelineItemGroup,\n\tTimelineItemGroupAvatar,\n\tTimelineItemGroupContent,\n\tTimelineItemGroupHeader,\n\tTimelineItemGroupSeenIndicator,\n} from \"../../primitives/timeline-item-group\";\nimport { cn } from \"../utils\";\nimport { Avatar } from \"./avatar\";\nimport { TimelineMessageItem } from \"./timeline-message-item\";\n\nconst MESSAGE_ANIMATION = {\n\tinitial: { opacity: 0, y: 6 },\n\tanimate: { opacity: 1, y: 0 },\n\texit: { opacity: 0 },\n\ttransition: {\n\t\tduration: 0.1,\n\t\tease: [0.25, 0.46, 0.45, 0.94] as const, // easeOutCubic\n\t},\n} as const;\n\nconst SEEN_ANIMATION = {\n\tinitial: { opacity: 0 },\n\tanimate: { opacity: 1 },\n\ttransition: {\n\t\tduration: 0.1,\n\t\tease: \"easeOut\" as const,\n\t},\n} as const;\n\nexport type TimelineMessageGroupProps = {\n\titems: TimelineItem[];\n\tavailableAIAgents: AvailableAIAgent[];\n\tavailableHumanAgents: AvailableHumanAgent[];\n\tcurrentVisitorId?: string;\n\tseenByIds?: readonly string[];\n\tseenByNames?: readonly string[];\n};\n\nconst EMPTY_SEEN_BY_IDS: readonly string[] = Object.freeze([]);\nconst EMPTY_SEEN_BY_NAMES: readonly string[] = Object.freeze([]);\n\nexport const TimelineMessageGroup: React.FC<TimelineMessageGroupProps> = ({\n\titems,\n\tavailableAIAgents,\n\tavailableHumanAgents,\n\tcurrentVisitorId,\n\tseenByIds = EMPTY_SEEN_BY_IDS,\n\tseenByNames = EMPTY_SEEN_BY_NAMES,\n}) => {\n\t// Get agent info for the sender\n\tconst firstItem = items[0];\n\tconst humanAgent = availableHumanAgents.find(\n\t\t(agent) => agent.id === firstItem?.userId\n\t);\n\tconst aiAgent = availableAIAgents.find(\n\t\t(agent) => agent.id === firstItem?.aiAgentId\n\t);\n\n\tif (items.length === 0) {\n\t\treturn null;\n\t}\n\n\tconst hasSeenIndicator = seenByIds.length > 0;\n\n\treturn (\n\t\t<PrimitiveTimelineItemGroup\n\t\t\titems={items}\n\t\t\tseenByIds={seenByIds}\n\t\t\tviewerId={currentVisitorId}\n\t\t\tviewerType={SenderType.VISITOR}\n\t\t>\n\t\t\t{({\n\t\t\t\tisSentByViewer,\n\t\t\t\tisReceivedByViewer,\n\t\t\t\tisVisitor,\n\t\t\t\tisAI,\n\t\t\t\tisTeamMember,\n\t\t\t}) => (\n\t\t\t\t<div\n\t\t\t\t\tclassName={cn(\n\t\t\t\t\t\t\"flex w-full gap-2\",\n\t\t\t\t\t\t// Support widget POV: visitor messages are sent (right side)\n\t\t\t\t\t\t// Agent messages are received (left side)\n\t\t\t\t\t\tisSentByViewer && \"flex-row-reverse\",\n\t\t\t\t\t\tisReceivedByViewer && \"flex-row\"\n\t\t\t\t\t)}\n\t\t\t\t>\n\t\t\t\t\t{/* Avatar - only show for received messages (agents) */}\n\t\t\t\t\t{isReceivedByViewer && (\n\t\t\t\t\t\t<TimelineItemGroupAvatar className=\"flex flex-shrink-0 flex-col justify-end\">\n\t\t\t\t\t\t\t{isAI ? (\n\t\t\t\t\t\t\t\t<Avatar\n\t\t\t\t\t\t\t\t\tclassName=\"size-6\"\n\t\t\t\t\t\t\t\t\timage={aiAgent?.image}\n\t\t\t\t\t\t\t\t\tisAI\n\t\t\t\t\t\t\t\t\tname={aiAgent?.name || \"AI Assistant\"}\n\t\t\t\t\t\t\t\t\tshowBackground={!!aiAgent?.image}\n\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t) : (\n\t\t\t\t\t\t\t\t<Avatar\n\t\t\t\t\t\t\t\t\tclassName=\"size-6\"\n\t\t\t\t\t\t\t\t\timage={humanAgent?.image}\n\t\t\t\t\t\t\t\t\tname={humanAgent?.name || \"Support\"}\n\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t</TimelineItemGroupAvatar>\n\t\t\t\t\t)}\n\n\t\t\t\t\t<TimelineItemGroupContent\n\t\t\t\t\t\tclassName={cn(\"flex flex-col gap-1\", isSentByViewer && \"items-end\")}\n\t\t\t\t\t>\n\t\t\t\t\t\t{/* Header - show sender name for received messages (agents) */}\n\t\t\t\t\t\t{isReceivedByViewer && (\n\t\t\t\t\t\t\t<TimelineItemGroupHeader className=\"px-1 text-co-muted-foreground text-xs\">\n\t\t\t\t\t\t\t\t{isAI\n\t\t\t\t\t\t\t\t\t? aiAgent?.name || \"AI Assistant\"\n\t\t\t\t\t\t\t\t\t: humanAgent?.name || \"Support\"}\n\t\t\t\t\t\t\t</TimelineItemGroupHeader>\n\t\t\t\t\t\t)}\n\n\t\t\t\t\t\t{items.map((item, index) => (\n\t\t\t\t\t\t\t<motion.div key={item.id} {...MESSAGE_ANIMATION}>\n\t\t\t\t\t\t\t\t<TimelineMessageItem\n\t\t\t\t\t\t\t\t\tisLast={index === items.length - 1}\n\t\t\t\t\t\t\t\t\tisSentByViewer={isSentByViewer}\n\t\t\t\t\t\t\t\t\titem={item}\n\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t</motion.div>\n\t\t\t\t\t\t))}\n\n\t\t\t\t\t\t{isSentByViewer && (\n\t\t\t\t\t\t\t<div className={cn(\"\", hasSeenIndicator && \"mt-2\")}>\n\t\t\t\t\t\t\t\t<div className=\"min-h-[1.25rem]\">\n\t\t\t\t\t\t\t\t\t{hasSeenIndicator && (\n\t\t\t\t\t\t\t\t\t\t<motion.div key=\"seen-indicator\" {...SEEN_ANIMATION}>\n\t\t\t\t\t\t\t\t\t\t\t<TimelineItemGroupSeenIndicator\n\t\t\t\t\t\t\t\t\t\t\t\tclassName=\"px-1 text-co-muted-foreground text-xs\"\n\t\t\t\t\t\t\t\t\t\t\t\tseenByIds={seenByIds}\n\t\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t\t{() =>\n\t\t\t\t\t\t\t\t\t\t\t\t\tseenByNames.length > 0\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t? `Seen by ${seenByNames.join(\", \")}`\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t: \"Seen\"\n\t\t\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t\t\t</TimelineItemGroupSeenIndicator>\n\t\t\t\t\t\t\t\t\t\t</motion.div>\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</div>\n\t\t\t\t\t\t)}\n\t\t\t\t\t</TimelineItemGroupContent>\n\t\t\t\t</div>\n\t\t\t)}\n\t\t</PrimitiveTimelineItemGroup>\n\t);\n};\n\nTimelineMessageGroup.displayName = \"TimelineMessageGroup\";\n"],"mappings":";;;;;;;;;AAgBA,MAAM,oBAAoB;CACzB,SAAS;EAAE,SAAS;EAAG,GAAG;EAAG;CAC7B,SAAS;EAAE,SAAS;EAAG,GAAG;EAAG;CAC7B,MAAM,EAAE,SAAS,GAAG;CACpB,YAAY;EACX,UAAU;EACV,MAAM;GAAC;GAAM;GAAM;GAAM;GAAK;EAC9B;CACD;AAED,MAAM,iBAAiB;CACtB,SAAS,EAAE,SAAS,GAAG;CACvB,SAAS,EAAE,SAAS,GAAG;CACvB,YAAY;EACX,UAAU;EACV,MAAM;EACN;CACD;AAWD,MAAMA,oBAAuC,OAAO,OAAO,EAAE,CAAC;AAC9D,MAAMC,sBAAyC,OAAO,OAAO,EAAE,CAAC;AAEhE,MAAaC,wBAA6D,EACzE,OACA,mBACA,sBACA,kBACA,YAAY,mBACZ,cAAc,0BACT;CAEL,MAAM,YAAY,MAAM;CACxB,MAAM,aAAa,qBAAqB,MACtC,UAAU,MAAM,OAAO,WAAW,OACnC;CACD,MAAM,UAAU,kBAAkB,MAChC,UAAU,MAAM,OAAO,WAAW,UACnC;AAED,KAAI,MAAM,WAAW,EACpB,QAAO;CAGR,MAAM,mBAAmB,UAAU,SAAS;AAE5C,QACC,oBAACC;EACO;EACI;EACX,UAAU;EACV,YAAY,WAAW;aAErB,EACD,gBACA,oBACA,WACA,MACA,mBAEA,qBAAC;GACA,WAAW,GACV,qBAGA,kBAAkB,oBAClB,sBAAsB,WACtB;cAGA,sBACA,oBAAC;IAAwB,WAAU;cACjC,OACA,oBAAC;KACA,WAAU;KACV,OAAO,SAAS;KAChB;KACA,MAAM,SAAS,QAAQ;KACvB,gBAAgB,CAAC,CAAC,SAAS;MAC1B,GAEF,oBAAC;KACA,WAAU;KACV,OAAO,YAAY;KACnB,MAAM,YAAY,QAAQ;MACzB;KAEsB,EAG3B,qBAAC;IACA,WAAW,GAAG,uBAAuB,kBAAkB,YAAY;;KAGlE,sBACA,oBAAC;MAAwB,WAAU;gBACjC,OACE,SAAS,QAAQ,iBACjB,YAAY,QAAQ;OACE;KAG1B,MAAM,KAAK,MAAM,UACjB,oBAAC,OAAO;MAAkB,GAAI;gBAC7B,oBAAC;OACA,QAAQ,UAAU,MAAM,SAAS;OACjB;OACV;QACL;QALc,KAAK,GAMT,CACZ;KAED,kBACA,oBAAC;MAAI,WAAW,GAAG,IAAI,oBAAoB,OAAO;gBACjD,oBAAC;OAAI,WAAU;iBACb,oBACA,oBAAC,OAAO;QAAyB,GAAI;kBACpC,oBAAC;SACA,WAAU;SACC;yBAGV,YAAY,SAAS,IAClB,WAAW,YAAY,KAAK,KAAK,KACjC;UAE4B;UAVlB,iBAWH;QAET;OACD;;KAEmB;IACtB;GAEqB;;AAI/B,qBAAqB,cAAc"}
@@ -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 React from "react";
3
3
 
4
4
  //#region src/support/components/timeline-message-item.d.ts
@@ -35,7 +35,7 @@ function TimelineMessageItem({ item, isLast = false, isSentByViewer = false }) {
35
35
  className: cn("flex w-full min-w-0 flex-1 flex-col gap-1", isSentByViewerFinal && "items-end"),
36
36
  children: [
37
37
  hasText && /* @__PURE__ */ jsx(TimelineItemContent, {
38
- className: cn("block min-w-0 max-w-[300px] whitespace-pre-wrap break-words rounded-lg px-3.5 py-2.5 text-sm", {
38
+ className: cn("block min-w-0 max-w-[300px] break-words rounded-lg px-3.5 py-2.5 text-sm", {
39
39
  "bg-co-background-300 text-co-foreground dark:bg-co-background-600": !isSentByViewerFinal,
40
40
  "bg-co-primary text-co-primary-foreground": isSentByViewerFinal,
41
41
  "rounded-br-sm": isLast && isSentByViewerFinal && !hasAttachments,
@@ -51,7 +51,7 @@ function TimelineMessageItem({ item, isLast = false, isSentByViewer = false }) {
51
51
  onClick: () => openLightbox(index),
52
52
  type: "button",
53
53
  children: /* @__PURE__ */ jsx("img", {
54
- alt: image.fileName || `Image ${index + 1}`,
54
+ alt: image.filename || `Image ${index + 1}`,
55
55
  className: "max-h-[150px] max-w-[200px] cursor-pointer rounded-lg object-cover transition-transform group-hover:scale-105",
56
56
  loading: "lazy",
57
57
  src: image.url
@@ -65,7 +65,7 @@ function TimelineMessageItem({ item, isLast = false, isSentByViewer = false }) {
65
65
  "bg-co-background-300 text-co-foreground hover:bg-co-background-400 dark:bg-co-background-600 dark:hover:bg-co-background-500": !isSentByViewerFinal,
66
66
  "bg-co-primary/80 text-co-primary-foreground hover:bg-co-primary": isSentByViewerFinal
67
67
  }),
68
- download: file.fileName,
68
+ download: file.filename,
69
69
  href: file.url,
70
70
  rel: "noopener noreferrer",
71
71
  target: "_blank",
@@ -76,7 +76,7 @@ function TimelineMessageItem({ item, isLast = false, isSentByViewer = false }) {
76
76
  }),
77
77
  /* @__PURE__ */ jsx("span", {
78
78
  className: "flex-1 truncate font-medium",
79
- children: file.fileName || "Download file"
79
+ children: file.filename || "Download file"
80
80
  }),
81
81
  file.size && /* @__PURE__ */ jsx("span", {
82
82
  className: "text-co-muted-foreground opacity-70",
@@ -1 +1 @@
1
- {"version":3,"file":"timeline-message-item.js","names":["PrimitiveTimelineItem","Icon"],"sources":["../../../src/support/components/timeline-message-item.tsx"],"sourcesContent":["import { formatFileSize } from \"@cossistant/core\";\nimport type { TimelineItem } from \"@cossistant/types/api/timeline-item\";\nimport type React from \"react\";\nimport { useState } from \"react\";\nimport {\n\tTimelineItem as PrimitiveTimelineItem,\n\tTimelineItemContent,\n\tTimelineItemTimestamp,\n} from \"../../primitives/timeline-item\";\nimport {\n\textractFileParts,\n\textractImageParts,\n} from \"../../primitives/timeline-item-attachments\";\nimport { useSupportText } from \"../text\";\nimport { cn } from \"../utils\";\nimport Icon from \"./icons\";\nimport { ImageLightbox } from \"./image-lightbox\";\n\nexport type TimelineMessageItemProps = {\n\titem: TimelineItem;\n\tisLast?: boolean;\n\tisSentByViewer?: boolean;\n};\n\n/**\n * Message bubble renderer that adapts layout depending on whether the visitor\n * or an agent sent the message.\n */\nexport function TimelineMessageItem({\n\titem,\n\tisLast = false,\n\tisSentByViewer = false,\n}: TimelineMessageItemProps): React.ReactElement {\n\tconst text = useSupportText();\n\tconst [lightboxOpen, setLightboxOpen] = useState(false);\n\tconst [lightboxIndex, setLightboxIndex] = useState(0);\n\n\t// Extract image and file parts\n\tconst images = extractImageParts(item.parts);\n\tconst files = extractFileParts(item.parts);\n\tconst hasAttachments = images.length > 0 || files.length > 0;\n\tconst hasText = item.text && item.text.trim().length > 0;\n\n\tconst openLightbox = (index: number) => {\n\t\tsetLightboxIndex(index);\n\t\tsetLightboxOpen(true);\n\t};\n\n\treturn (\n\t\t<>\n\t\t\t<PrimitiveTimelineItem item={item}>\n\t\t\t\t{({ isAI, timestamp }) => {\n\t\t\t\t\t// isSentByViewer defaults to false, meaning messages are treated as received\n\t\t\t\t\t// (left side with background) unless explicitly marked as sent by viewer\n\t\t\t\t\tconst isSentByViewerFinal = isSentByViewer;\n\n\t\t\t\t\treturn (\n\t\t\t\t\t\t<div\n\t\t\t\t\t\t\tclassName={cn(\n\t\t\t\t\t\t\t\t\"flex w-full gap-2\",\n\t\t\t\t\t\t\t\tisSentByViewerFinal && \"flex-row-reverse\",\n\t\t\t\t\t\t\t\t!isSentByViewerFinal && \"flex-row\"\n\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t>\n\t\t\t\t\t\t\t<div\n\t\t\t\t\t\t\t\tclassName={cn(\n\t\t\t\t\t\t\t\t\t\"flex w-full min-w-0 flex-1 flex-col gap-1\",\n\t\t\t\t\t\t\t\t\tisSentByViewerFinal && \"items-end\"\n\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t{/* Text content */}\n\t\t\t\t\t\t\t\t{hasText && (\n\t\t\t\t\t\t\t\t\t<TimelineItemContent\n\t\t\t\t\t\t\t\t\t\tclassName={cn(\n\t\t\t\t\t\t\t\t\t\t\t\"block min-w-0 max-w-[300px] whitespace-pre-wrap break-words rounded-lg px-3.5 py-2.5 text-sm\",\n\t\t\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\t\t\t\"bg-co-background-300 text-co-foreground dark:bg-co-background-600\":\n\t\t\t\t\t\t\t\t\t\t\t\t\t!isSentByViewerFinal,\n\t\t\t\t\t\t\t\t\t\t\t\t\"bg-co-primary text-co-primary-foreground\":\n\t\t\t\t\t\t\t\t\t\t\t\t\tisSentByViewerFinal,\n\t\t\t\t\t\t\t\t\t\t\t\t\"rounded-br-sm\":\n\t\t\t\t\t\t\t\t\t\t\t\t\tisLast && isSentByViewerFinal && !hasAttachments,\n\t\t\t\t\t\t\t\t\t\t\t\t\"rounded-bl-sm\":\n\t\t\t\t\t\t\t\t\t\t\t\t\tisLast && !isSentByViewerFinal && !hasAttachments,\n\t\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t\t\t\trenderMarkdown\n\t\t\t\t\t\t\t\t\t\ttext={item.text}\n\t\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t)}\n\n\t\t\t\t\t\t\t\t{/* Image attachments */}\n\t\t\t\t\t\t\t\t{images.length > 0 && (\n\t\t\t\t\t\t\t\t\t<div\n\t\t\t\t\t\t\t\t\t\tclassName={cn(\n\t\t\t\t\t\t\t\t\t\t\t\"flex flex-wrap gap-2\",\n\t\t\t\t\t\t\t\t\t\t\tisSentByViewerFinal && \"justify-end\"\n\t\t\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t{images.map((image, index) => (\n\t\t\t\t\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\t\t\t\t\tclassName=\"group relative overflow-hidden rounded-lg focus:outline-none focus:ring-2 focus:ring-co-primary/50\"\n\t\t\t\t\t\t\t\t\t\t\t\tkey={image.url}\n\t\t\t\t\t\t\t\t\t\t\t\tonClick={() => openLightbox(index)}\n\t\t\t\t\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t\t{/* biome-ignore lint/performance/noImgElement: React package, not Next.js specific */}\n\t\t\t\t\t\t\t\t\t\t\t\t{/* biome-ignore lint/nursery/useImageSize: Dynamic image dimensions not known at render time */}\n\t\t\t\t\t\t\t\t\t\t\t\t<img\n\t\t\t\t\t\t\t\t\t\t\t\t\talt={image.fileName || `Image ${index + 1}`}\n\t\t\t\t\t\t\t\t\t\t\t\t\tclassName=\"max-h-[150px] max-w-[200px] cursor-pointer rounded-lg object-cover transition-transform group-hover:scale-105\"\n\t\t\t\t\t\t\t\t\t\t\t\t\tloading=\"lazy\"\n\t\t\t\t\t\t\t\t\t\t\t\t\tsrc={image.url}\n\t\t\t\t\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t\t\t\t</button>\n\t\t\t\t\t\t\t\t\t\t))}\n\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t)}\n\n\t\t\t\t\t\t\t\t{/* File attachments */}\n\t\t\t\t\t\t\t\t{files.length > 0 && (\n\t\t\t\t\t\t\t\t\t<div className=\"flex flex-col gap-1\">\n\t\t\t\t\t\t\t\t\t\t{files.map((file) => (\n\t\t\t\t\t\t\t\t\t\t\t<a\n\t\t\t\t\t\t\t\t\t\t\t\tclassName={cn(\n\t\t\t\t\t\t\t\t\t\t\t\t\t\"flex items-center gap-2 rounded-lg px-3 py-2 text-xs transition-colors\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"bg-co-background-300 text-co-foreground hover:bg-co-background-400 dark:bg-co-background-600 dark:hover:bg-co-background-500\":\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t!isSentByViewerFinal,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"bg-co-primary/80 text-co-primary-foreground hover:bg-co-primary\":\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tisSentByViewerFinal,\n\t\t\t\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t\t\t\t\t\tdownload={file.fileName}\n\t\t\t\t\t\t\t\t\t\t\t\thref={file.url}\n\t\t\t\t\t\t\t\t\t\t\t\tkey={file.url}\n\t\t\t\t\t\t\t\t\t\t\t\trel=\"noopener noreferrer\"\n\t\t\t\t\t\t\t\t\t\t\t\ttarget=\"_blank\"\n\t\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t\t<Icon className=\"h-4 w-4 shrink-0\" name=\"file\" />\n\t\t\t\t\t\t\t\t\t\t\t\t<span className=\"flex-1 truncate font-medium\">\n\t\t\t\t\t\t\t\t\t\t\t\t\t{file.fileName || \"Download file\"}\n\t\t\t\t\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t\t\t\t\t\t{file.size && (\n\t\t\t\t\t\t\t\t\t\t\t\t\t<span className=\"text-co-muted-foreground opacity-70\">\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t{formatFileSize(file.size)}\n\t\t\t\t\t\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t\t\t\t\t</a>\n\t\t\t\t\t\t\t\t\t\t))}\n\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t)}\n\n\t\t\t\t\t\t\t\t{isLast && (\n\t\t\t\t\t\t\t\t\t<TimelineItemTimestamp\n\t\t\t\t\t\t\t\t\t\tclassName=\"px-1 text-co-muted-foreground text-xs\"\n\t\t\t\t\t\t\t\t\t\ttimestamp={timestamp}\n\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t{() => (\n\t\t\t\t\t\t\t\t\t\t\t<>\n\t\t\t\t\t\t\t\t\t\t\t\t{timestamp.toLocaleTimeString([], {\n\t\t\t\t\t\t\t\t\t\t\t\t\thour: \"2-digit\",\n\t\t\t\t\t\t\t\t\t\t\t\t\tminute: \"2-digit\",\n\t\t\t\t\t\t\t\t\t\t\t\t})}\n\t\t\t\t\t\t\t\t\t\t\t\t{isAI &&\n\t\t\t\t\t\t\t\t\t\t\t\t\t` ${text(\"component.message.timestamp.aiIndicator\")}`}\n\t\t\t\t\t\t\t\t\t\t\t</>\n\t\t\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t\t\t</TimelineItemTimestamp>\n\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t);\n\t\t\t\t}}\n\t\t\t</PrimitiveTimelineItem>\n\n\t\t\t{/* Lightbox for images */}\n\t\t\t{images.length > 0 && (\n\t\t\t\t<ImageLightbox\n\t\t\t\t\timages={images}\n\t\t\t\t\tinitialIndex={lightboxIndex}\n\t\t\t\t\tisOpen={lightboxOpen}\n\t\t\t\t\tonClose={() => setLightboxOpen(false)}\n\t\t\t\t/>\n\t\t\t)}\n\t\t</>\n\t);\n}\n"],"mappings":";;;;;;;;;;;;;;;AA4BA,SAAgB,oBAAoB,EACnC,MACA,SAAS,OACT,iBAAiB,SAC+B;CAChD,MAAM,OAAO,gBAAgB;CAC7B,MAAM,CAAC,cAAc,mBAAmB,SAAS,MAAM;CACvD,MAAM,CAAC,eAAe,oBAAoB,SAAS,EAAE;CAGrD,MAAM,SAAS,kBAAkB,KAAK,MAAM;CAC5C,MAAM,QAAQ,iBAAiB,KAAK,MAAM;CAC1C,MAAM,iBAAiB,OAAO,SAAS,KAAK,MAAM,SAAS;CAC3D,MAAM,UAAU,KAAK,QAAQ,KAAK,KAAK,MAAM,CAAC,SAAS;CAEvD,MAAM,gBAAgB,UAAkB;AACvC,mBAAiB,MAAM;AACvB,kBAAgB,KAAK;;AAGtB,QACC,4CACC,oBAACA;EAA4B;aAC1B,EAAE,MAAM,gBAAgB;GAGzB,MAAM,sBAAsB;AAE5B,UACC,oBAAC;IACA,WAAW,GACV,qBACA,uBAAuB,oBACvB,CAAC,uBAAuB,WACxB;cAED,qBAAC;KACA,WAAW,GACV,6CACA,uBAAuB,YACvB;;MAGA,WACA,oBAAC;OACA,WAAW,GACV,gGACA;QACC,qEACC,CAAC;QACF,4CACC;QACD,iBACC,UAAU,uBAAuB,CAAC;QACnC,iBACC,UAAU,CAAC,uBAAuB,CAAC;QACpC,CACD;OACD;OACA,MAAM,KAAK;QACV;MAIF,OAAO,SAAS,KAChB,oBAAC;OACA,WAAW,GACV,wBACA,uBAAuB,cACvB;iBAEA,OAAO,KAAK,OAAO,UACnB,oBAAC;QACA,WAAU;QAEV,eAAe,aAAa,MAAM;QAClC,MAAK;kBAIL,oBAAC;SACA,KAAK,MAAM,YAAY,SAAS,QAAQ;SACxC,WAAU;SACV,SAAQ;SACR,KAAK,MAAM;UACV;UAXG,MAAM,IAYH,CACR;QACG;MAIN,MAAM,SAAS,KACf,oBAAC;OAAI,WAAU;iBACb,MAAM,KAAK,SACX,qBAAC;QACA,WAAW,GACV,0EACA;SACC,gIACC,CAAC;SACF,mEACC;SACD,CACD;QACD,UAAU,KAAK;QACf,MAAM,KAAK;QAEX,KAAI;QACJ,QAAO;;SAEP,oBAACC;UAAK,WAAU;UAAmB,MAAK;WAAS;SACjD,oBAAC;UAAK,WAAU;oBACd,KAAK,YAAY;WACZ;SACN,KAAK,QACL,oBAAC;UAAK,WAAU;oBACd,eAAe,KAAK,KAAK;WACpB;;UAXH,KAAK,IAaP,CACH;QACG;MAGN,UACA,oBAAC;OACA,WAAU;OACC;uBAGV,4CACE,UAAU,mBAAmB,EAAE,EAAE;QACjC,MAAM;QACN,QAAQ;QACR,CAAC,EACD,QACA,IAAI,KAAK,0CAA0C,MAClD;QAEmB;;MAEpB;KACD;;GAGe,EAGvB,OAAO,SAAS,KAChB,oBAAC;EACQ;EACR,cAAc;EACd,QAAQ;EACR,eAAe,gBAAgB,MAAM;GACpC,IAED"}
1
+ {"version":3,"file":"timeline-message-item.js","names":["PrimitiveTimelineItem","Icon"],"sources":["../../../src/support/components/timeline-message-item.tsx"],"sourcesContent":["import { formatFileSize } from \"@cossistant/core\";\nimport type { TimelineItem } from \"@cossistant/types/api/timeline-item\";\nimport type React from \"react\";\nimport { useState } from \"react\";\nimport {\n\tTimelineItem as PrimitiveTimelineItem,\n\tTimelineItemContent,\n\tTimelineItemTimestamp,\n} from \"../../primitives/timeline-item\";\nimport {\n\textractFileParts,\n\textractImageParts,\n} from \"../../primitives/timeline-item-attachments\";\nimport { useSupportText } from \"../text\";\nimport { cn } from \"../utils\";\nimport Icon from \"./icons\";\nimport { ImageLightbox } from \"./image-lightbox\";\n\nexport type TimelineMessageItemProps = {\n\titem: TimelineItem;\n\tisLast?: boolean;\n\tisSentByViewer?: boolean;\n};\n\n/**\n * Message bubble renderer that adapts layout depending on whether the visitor\n * or an agent sent the message.\n */\nexport function TimelineMessageItem({\n\titem,\n\tisLast = false,\n\tisSentByViewer = false,\n}: TimelineMessageItemProps): React.ReactElement {\n\tconst text = useSupportText();\n\tconst [lightboxOpen, setLightboxOpen] = useState(false);\n\tconst [lightboxIndex, setLightboxIndex] = useState(0);\n\n\t// Extract image and file parts\n\tconst images = extractImageParts(item.parts);\n\tconst files = extractFileParts(item.parts);\n\tconst hasAttachments = images.length > 0 || files.length > 0;\n\tconst hasText = item.text && item.text.trim().length > 0;\n\n\tconst openLightbox = (index: number) => {\n\t\tsetLightboxIndex(index);\n\t\tsetLightboxOpen(true);\n\t};\n\n\treturn (\n\t\t<>\n\t\t\t<PrimitiveTimelineItem item={item}>\n\t\t\t\t{({ isAI, timestamp }) => {\n\t\t\t\t\t// isSentByViewer defaults to false, meaning messages are treated as received\n\t\t\t\t\t// (left side with background) unless explicitly marked as sent by viewer\n\t\t\t\t\tconst isSentByViewerFinal = isSentByViewer;\n\n\t\t\t\t\treturn (\n\t\t\t\t\t\t<div\n\t\t\t\t\t\t\tclassName={cn(\n\t\t\t\t\t\t\t\t\"flex w-full gap-2\",\n\t\t\t\t\t\t\t\tisSentByViewerFinal && \"flex-row-reverse\",\n\t\t\t\t\t\t\t\t!isSentByViewerFinal && \"flex-row\"\n\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t>\n\t\t\t\t\t\t\t<div\n\t\t\t\t\t\t\t\tclassName={cn(\n\t\t\t\t\t\t\t\t\t\"flex w-full min-w-0 flex-1 flex-col gap-1\",\n\t\t\t\t\t\t\t\t\tisSentByViewerFinal && \"items-end\"\n\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t{/* Text content */}\n\t\t\t\t\t\t\t\t{hasText && (\n\t\t\t\t\t\t\t\t\t<TimelineItemContent\n\t\t\t\t\t\t\t\t\t\tclassName={cn(\n\t\t\t\t\t\t\t\t\t\t\t\"block min-w-0 max-w-[300px] break-words rounded-lg px-3.5 py-2.5 text-sm\",\n\t\t\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\t\t\t\"bg-co-background-300 text-co-foreground dark:bg-co-background-600\":\n\t\t\t\t\t\t\t\t\t\t\t\t\t!isSentByViewerFinal,\n\t\t\t\t\t\t\t\t\t\t\t\t\"bg-co-primary text-co-primary-foreground\":\n\t\t\t\t\t\t\t\t\t\t\t\t\tisSentByViewerFinal,\n\t\t\t\t\t\t\t\t\t\t\t\t\"rounded-br-sm\":\n\t\t\t\t\t\t\t\t\t\t\t\t\tisLast && isSentByViewerFinal && !hasAttachments,\n\t\t\t\t\t\t\t\t\t\t\t\t\"rounded-bl-sm\":\n\t\t\t\t\t\t\t\t\t\t\t\t\tisLast && !isSentByViewerFinal && !hasAttachments,\n\t\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t\t\t\trenderMarkdown\n\t\t\t\t\t\t\t\t\t\ttext={item.text}\n\t\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t)}\n\n\t\t\t\t\t\t\t\t{/* Image attachments */}\n\t\t\t\t\t\t\t\t{images.length > 0 && (\n\t\t\t\t\t\t\t\t\t<div\n\t\t\t\t\t\t\t\t\t\tclassName={cn(\n\t\t\t\t\t\t\t\t\t\t\t\"flex flex-wrap gap-2\",\n\t\t\t\t\t\t\t\t\t\t\tisSentByViewerFinal && \"justify-end\"\n\t\t\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t{images.map((image, index) => (\n\t\t\t\t\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\t\t\t\t\tclassName=\"group relative overflow-hidden rounded-lg focus:outline-none focus:ring-2 focus:ring-co-primary/50\"\n\t\t\t\t\t\t\t\t\t\t\t\tkey={image.url}\n\t\t\t\t\t\t\t\t\t\t\t\tonClick={() => openLightbox(index)}\n\t\t\t\t\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t\t{/* biome-ignore lint/performance/noImgElement: React package, not Next.js specific */}\n\t\t\t\t\t\t\t\t\t\t\t\t{/* biome-ignore lint/nursery/useImageSize: Dynamic image dimensions not known at render time */}\n\t\t\t\t\t\t\t\t\t\t\t\t<img\n\t\t\t\t\t\t\t\t\t\t\t\t\talt={image.filename || `Image ${index + 1}`}\n\t\t\t\t\t\t\t\t\t\t\t\t\tclassName=\"max-h-[150px] max-w-[200px] cursor-pointer rounded-lg object-cover transition-transform group-hover:scale-105\"\n\t\t\t\t\t\t\t\t\t\t\t\t\tloading=\"lazy\"\n\t\t\t\t\t\t\t\t\t\t\t\t\tsrc={image.url}\n\t\t\t\t\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t\t\t\t</button>\n\t\t\t\t\t\t\t\t\t\t))}\n\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t)}\n\n\t\t\t\t\t\t\t\t{/* File attachments */}\n\t\t\t\t\t\t\t\t{files.length > 0 && (\n\t\t\t\t\t\t\t\t\t<div className=\"flex flex-col gap-1\">\n\t\t\t\t\t\t\t\t\t\t{files.map((file) => (\n\t\t\t\t\t\t\t\t\t\t\t<a\n\t\t\t\t\t\t\t\t\t\t\t\tclassName={cn(\n\t\t\t\t\t\t\t\t\t\t\t\t\t\"flex items-center gap-2 rounded-lg px-3 py-2 text-xs transition-colors\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"bg-co-background-300 text-co-foreground hover:bg-co-background-400 dark:bg-co-background-600 dark:hover:bg-co-background-500\":\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t!isSentByViewerFinal,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"bg-co-primary/80 text-co-primary-foreground hover:bg-co-primary\":\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tisSentByViewerFinal,\n\t\t\t\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t\t\t\t\t\tdownload={file.filename}\n\t\t\t\t\t\t\t\t\t\t\t\thref={file.url}\n\t\t\t\t\t\t\t\t\t\t\t\tkey={file.url}\n\t\t\t\t\t\t\t\t\t\t\t\trel=\"noopener noreferrer\"\n\t\t\t\t\t\t\t\t\t\t\t\ttarget=\"_blank\"\n\t\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t\t<Icon className=\"h-4 w-4 shrink-0\" name=\"file\" />\n\t\t\t\t\t\t\t\t\t\t\t\t<span className=\"flex-1 truncate font-medium\">\n\t\t\t\t\t\t\t\t\t\t\t\t\t{file.filename || \"Download file\"}\n\t\t\t\t\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t\t\t\t\t\t{file.size && (\n\t\t\t\t\t\t\t\t\t\t\t\t\t<span className=\"text-co-muted-foreground opacity-70\">\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t{formatFileSize(file.size)}\n\t\t\t\t\t\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t\t\t\t\t</a>\n\t\t\t\t\t\t\t\t\t\t))}\n\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t)}\n\n\t\t\t\t\t\t\t\t{isLast && (\n\t\t\t\t\t\t\t\t\t<TimelineItemTimestamp\n\t\t\t\t\t\t\t\t\t\tclassName=\"px-1 text-co-muted-foreground text-xs\"\n\t\t\t\t\t\t\t\t\t\ttimestamp={timestamp}\n\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t{() => (\n\t\t\t\t\t\t\t\t\t\t\t<>\n\t\t\t\t\t\t\t\t\t\t\t\t{timestamp.toLocaleTimeString([], {\n\t\t\t\t\t\t\t\t\t\t\t\t\thour: \"2-digit\",\n\t\t\t\t\t\t\t\t\t\t\t\t\tminute: \"2-digit\",\n\t\t\t\t\t\t\t\t\t\t\t\t})}\n\t\t\t\t\t\t\t\t\t\t\t\t{isAI &&\n\t\t\t\t\t\t\t\t\t\t\t\t\t` ${text(\"component.message.timestamp.aiIndicator\")}`}\n\t\t\t\t\t\t\t\t\t\t\t</>\n\t\t\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t\t\t</TimelineItemTimestamp>\n\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t);\n\t\t\t\t}}\n\t\t\t</PrimitiveTimelineItem>\n\n\t\t\t{/* Lightbox for images */}\n\t\t\t{images.length > 0 && (\n\t\t\t\t<ImageLightbox\n\t\t\t\t\timages={images}\n\t\t\t\t\tinitialIndex={lightboxIndex}\n\t\t\t\t\tisOpen={lightboxOpen}\n\t\t\t\t\tonClose={() => setLightboxOpen(false)}\n\t\t\t\t/>\n\t\t\t)}\n\t\t</>\n\t);\n}\n"],"mappings":";;;;;;;;;;;;;;;AA4BA,SAAgB,oBAAoB,EACnC,MACA,SAAS,OACT,iBAAiB,SAC+B;CAChD,MAAM,OAAO,gBAAgB;CAC7B,MAAM,CAAC,cAAc,mBAAmB,SAAS,MAAM;CACvD,MAAM,CAAC,eAAe,oBAAoB,SAAS,EAAE;CAGrD,MAAM,SAAS,kBAAkB,KAAK,MAAM;CAC5C,MAAM,QAAQ,iBAAiB,KAAK,MAAM;CAC1C,MAAM,iBAAiB,OAAO,SAAS,KAAK,MAAM,SAAS;CAC3D,MAAM,UAAU,KAAK,QAAQ,KAAK,KAAK,MAAM,CAAC,SAAS;CAEvD,MAAM,gBAAgB,UAAkB;AACvC,mBAAiB,MAAM;AACvB,kBAAgB,KAAK;;AAGtB,QACC,4CACC,oBAACA;EAA4B;aAC1B,EAAE,MAAM,gBAAgB;GAGzB,MAAM,sBAAsB;AAE5B,UACC,oBAAC;IACA,WAAW,GACV,qBACA,uBAAuB,oBACvB,CAAC,uBAAuB,WACxB;cAED,qBAAC;KACA,WAAW,GACV,6CACA,uBAAuB,YACvB;;MAGA,WACA,oBAAC;OACA,WAAW,GACV,4EACA;QACC,qEACC,CAAC;QACF,4CACC;QACD,iBACC,UAAU,uBAAuB,CAAC;QACnC,iBACC,UAAU,CAAC,uBAAuB,CAAC;QACpC,CACD;OACD;OACA,MAAM,KAAK;QACV;MAIF,OAAO,SAAS,KAChB,oBAAC;OACA,WAAW,GACV,wBACA,uBAAuB,cACvB;iBAEA,OAAO,KAAK,OAAO,UACnB,oBAAC;QACA,WAAU;QAEV,eAAe,aAAa,MAAM;QAClC,MAAK;kBAIL,oBAAC;SACA,KAAK,MAAM,YAAY,SAAS,QAAQ;SACxC,WAAU;SACV,SAAQ;SACR,KAAK,MAAM;UACV;UAXG,MAAM,IAYH,CACR;QACG;MAIN,MAAM,SAAS,KACf,oBAAC;OAAI,WAAU;iBACb,MAAM,KAAK,SACX,qBAAC;QACA,WAAW,GACV,0EACA;SACC,gIACC,CAAC;SACF,mEACC;SACD,CACD;QACD,UAAU,KAAK;QACf,MAAM,KAAK;QAEX,KAAI;QACJ,QAAO;;SAEP,oBAACC;UAAK,WAAU;UAAmB,MAAK;WAAS;SACjD,oBAAC;UAAK,WAAU;oBACd,KAAK,YAAY;WACZ;SACN,KAAK,QACL,oBAAC;UAAK,WAAU;oBACd,eAAe,KAAK,KAAK;WACpB;;UAXH,KAAK,IAaP,CACH;QACG;MAGN,UACA,oBAAC;OACA,WAAU;OACC;uBAGV,4CACE,UAAU,mBAAmB,EAAE,EAAE;QACjC,MAAM;QACN,QAAQ;QACR,CAAC,EACD,QACA,IAAI,KAAK,0CAA0C,MAClD;QAEmB;;MAEpB;KACD;;GAGe,EAGvB,OAAO,SAAS,KAChB,oBAAC;EACQ;EACR,cAAc;EACd,QAAQ;EACR,eAAe,gBAAgB,MAAM;GACpC,IAED"}
@@ -1,12 +1,11 @@
1
1
  "use client";
2
2
 
3
-
4
3
  import { cn } from "../utils/index.js";
5
4
  import { BouncingDots } from "./typing-indicator.js";
6
5
  import { SupportTrigger } from "../../primitives/trigger.js";
6
+ import icons_default from "./icons.js";
7
7
  import { useNewMessageSound } from "../../hooks/use-new-message-sound.js";
8
8
  import { useTypingSound } from "../../hooks/use-typing-sound.js";
9
- import icons_default from "./icons.js";
10
9
  import * as React$1 from "react";
11
10
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
12
11
  import { AnimatePresence, motion } from "motion/react";
@@ -1 +1 @@
1
- {"version":3,"file":"trigger.js","names":["TriggerContent: React.FC<TriggerContentProps>","React","Icon","DefaultTrigger: React.FC<DefaultTriggerProps>","Primitive.Trigger"],"sources":["../../../src/support/components/trigger.tsx"],"sourcesContent":["\"use client\";\n\nimport { AnimatePresence, motion } from \"motion/react\";\nimport * as React from \"react\";\nimport { useNewMessageSound } from \"../../hooks/use-new-message-sound\";\nimport { useTypingSound } from \"../../hooks/use-typing-sound\";\nimport * as Primitive from \"../../primitives\";\nimport type { TriggerRenderProps } from \"../types\";\nimport { cn } from \"../utils\";\nimport Icon from \"./icons\";\nimport { BouncingDots } from \"./typing-indicator\";\n\ntype TriggerContentProps = {\n\tisOpen: boolean;\n\tunreadCount: number;\n\tisTyping: boolean;\n};\n\nconst TriggerContent: React.FC<TriggerContentProps> = ({\n\tisOpen,\n\tunreadCount,\n\tisTyping,\n}) => {\n\tconst playNewMessageSound = useNewMessageSound({\n\t\tvolume: 0.7,\n\t\tplaybackRate: 1.0,\n\t});\n\tconst previousUnreadCountRef = React.useRef(0);\n\n\tuseTypingSound(!isOpen && isTyping, {\n\t\tvolume: 1,\n\t\tplaybackRate: 1.3,\n\t});\n\n\tReact.useEffect(() => {\n\t\tif (unreadCount > previousUnreadCountRef.current) {\n\t\t\tplayNewMessageSound();\n\t\t}\n\t\tpreviousUnreadCountRef.current = unreadCount;\n\t}, [unreadCount, playNewMessageSound]);\n\n\treturn (\n\t\t<>\n\t\t\t<AnimatePresence mode=\"wait\">\n\t\t\t\t{isOpen ? (\n\t\t\t\t\t<motion.div\n\t\t\t\t\t\tanimate={{\n\t\t\t\t\t\t\tscale: 1,\n\t\t\t\t\t\t\trotate: 0,\n\t\t\t\t\t\t\topacity: 1,\n\t\t\t\t\t\t\ttransition: { duration: 0.2, ease: \"easeOut\" },\n\t\t\t\t\t\t}}\n\t\t\t\t\t\tclassName=\"flex items-center justify-center\"\n\t\t\t\t\t\texit={{\n\t\t\t\t\t\t\tscale: 0.9,\n\t\t\t\t\t\t\trotate: -45,\n\t\t\t\t\t\t\topacity: 0,\n\t\t\t\t\t\t\ttransition: { duration: 0.1, ease: \"easeIn\" },\n\t\t\t\t\t\t}}\n\t\t\t\t\t\tinitial={{ scale: 0.9, rotate: 45, opacity: 0 }}\n\t\t\t\t\t\tkey=\"chevron\"\n\t\t\t\t\t>\n\t\t\t\t\t\t<Icon className=\"size-5\" name=\"chevron-down\" />\n\t\t\t\t\t</motion.div>\n\t\t\t\t) : isTyping ? (\n\t\t\t\t\t<motion.span\n\t\t\t\t\t\tanimate={{\n\t\t\t\t\t\t\topacity: 1,\n\t\t\t\t\t\t\tscale: 1,\n\t\t\t\t\t\t\ttransition: {\n\t\t\t\t\t\t\t\tduration: 0.2,\n\t\t\t\t\t\t\t\tease: \"easeOut\",\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t}}\n\t\t\t\t\t\tclassName=\"pointer-events-none flex items-center rounded-full text-co-primary\"\n\t\t\t\t\t\texit={{\n\t\t\t\t\t\t\topacity: 0,\n\t\t\t\t\t\t\tscale: 0.9,\n\t\t\t\t\t\t\ttransition: {\n\t\t\t\t\t\t\t\tduration: 0.1,\n\t\t\t\t\t\t\t\tease: \"easeIn\",\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t}}\n\t\t\t\t\t\tinitial={{ opacity: 0, scale: 0.9 }}\n\t\t\t\t\t\tkey=\"typing-indicator\"\n\t\t\t\t\t>\n\t\t\t\t\t\t<BouncingDots className=\"bg-co-primary-foreground\" />\n\t\t\t\t\t</motion.span>\n\t\t\t\t) : (\n\t\t\t\t\t<motion.div\n\t\t\t\t\t\tanimate={{\n\t\t\t\t\t\t\tscale: 1,\n\t\t\t\t\t\t\trotate: 0,\n\t\t\t\t\t\t\topacity: 1,\n\t\t\t\t\t\t\ttransition: { duration: 0.2, ease: \"easeOut\" },\n\t\t\t\t\t\t}}\n\t\t\t\t\t\tclassName=\"flex items-center justify-center\"\n\t\t\t\t\t\texit={{\n\t\t\t\t\t\t\tscale: 0.9,\n\t\t\t\t\t\t\trotate: 45,\n\t\t\t\t\t\t\topacity: 0,\n\t\t\t\t\t\t\ttransition: { duration: 0.1, ease: \"easeIn\" },\n\t\t\t\t\t\t}}\n\t\t\t\t\t\tinitial={{ scale: 0.9, rotate: -45, opacity: 0 }}\n\t\t\t\t\t\tkey=\"chat\"\n\t\t\t\t\t>\n\t\t\t\t\t\t<Icon className=\"size-6.5\" name=\"chat\" variant=\"filled\" />\n\t\t\t\t\t</motion.div>\n\t\t\t\t)}\n\t\t\t</AnimatePresence>\n\n\t\t\t{unreadCount > 0 && (\n\t\t\t\t<motion.div\n\t\t\t\t\tanimate={{ scale: 1, opacity: 1 }}\n\t\t\t\t\tclassName=\"absolute top-0.5 right-0.5 flex size-2 items-center justify-center rounded-full bg-co-destructive font-medium text-[10px] text-co-destructive-foreground text-white text-xs\"\n\t\t\t\t\texit={{ scale: 0, opacity: 0 }}\n\t\t\t\t\tinitial={{ scale: 0, opacity: 0 }}\n\t\t\t\t/>\n\t\t\t)}\n\t\t</>\n\t);\n};\n\nexport type DefaultTriggerProps = {\n\tclassName?: string;\n};\n\n/**\n * Default styled trigger button.\n * Used internally when no custom trigger is provided.\n */\nexport const DefaultTrigger: React.FC<DefaultTriggerProps> = ({\n\tclassName,\n}) => (\n\t<Primitive.Trigger asChild>\n\t\t{({ isOpen, unreadCount, isTyping }: TriggerRenderProps) => (\n\t\t\t<motion.button\n\t\t\t\tclassName={cn(\n\t\t\t\t\t\"relative z-[9999] flex size-14 cursor-pointer items-center justify-center rounded-full bg-co-primary text-co-primary-foreground transition-colors hover:bg-co-primary/90 data-[open=true]:bg-co-primary/90\",\n\t\t\t\t\tclassName\n\t\t\t\t)}\n\t\t\t\tdata-open={isOpen}\n\t\t\t\ttransition={{\n\t\t\t\t\ttype: \"spring\",\n\t\t\t\t\tstiffness: 800,\n\t\t\t\t\tdamping: 17,\n\t\t\t\t}}\n\t\t\t\ttype=\"button\"\n\t\t\t\twhileTap={{ scale: 0.95 }}\n\t\t\t>\n\t\t\t\t<TriggerContent\n\t\t\t\t\tisOpen={isOpen}\n\t\t\t\t\tisTyping={isTyping}\n\t\t\t\t\tunreadCount={unreadCount}\n\t\t\t\t/>\n\t\t\t</motion.button>\n\t\t)}\n\t</Primitive.Trigger>\n);\n"],"mappings":";;;;;;;;;;;;;;AAkBA,MAAMA,kBAAiD,EACtD,QACA,aACA,eACK;CACL,MAAM,sBAAsB,mBAAmB;EAC9C,QAAQ;EACR,cAAc;EACd,CAAC;CACF,MAAM,yBAAyBC,QAAM,OAAO,EAAE;AAE9C,gBAAe,CAAC,UAAU,UAAU;EACnC,QAAQ;EACR,cAAc;EACd,CAAC;AAEF,SAAM,gBAAgB;AACrB,MAAI,cAAc,uBAAuB,QACxC,sBAAqB;AAEtB,yBAAuB,UAAU;IAC/B,CAAC,aAAa,oBAAoB,CAAC;AAEtC,QACC,4CACC,oBAAC;EAAgB,MAAK;YACpB,SACA,oBAAC,OAAO;GACP,SAAS;IACR,OAAO;IACP,QAAQ;IACR,SAAS;IACT,YAAY;KAAE,UAAU;KAAK,MAAM;KAAW;IAC9C;GACD,WAAU;GACV,MAAM;IACL,OAAO;IACP,QAAQ;IACR,SAAS;IACT,YAAY;KAAE,UAAU;KAAK,MAAM;KAAU;IAC7C;GACD,SAAS;IAAE,OAAO;IAAK,QAAQ;IAAI,SAAS;IAAG;aAG/C,oBAACC;IAAK,WAAU;IAAS,MAAK;KAAiB;KAF3C,UAGQ,GACV,WACH,oBAAC,OAAO;GACP,SAAS;IACR,SAAS;IACT,OAAO;IACP,YAAY;KACX,UAAU;KACV,MAAM;KACN;IACD;GACD,WAAU;GACV,MAAM;IACL,SAAS;IACT,OAAO;IACP,YAAY;KACX,UAAU;KACV,MAAM;KACN;IACD;GACD,SAAS;IAAE,SAAS;IAAG,OAAO;IAAK;aAGnC,oBAAC,gBAAa,WAAU,6BAA6B;KAFjD,mBAGS,GAEd,oBAAC,OAAO;GACP,SAAS;IACR,OAAO;IACP,QAAQ;IACR,SAAS;IACT,YAAY;KAAE,UAAU;KAAK,MAAM;KAAW;IAC9C;GACD,WAAU;GACV,MAAM;IACL,OAAO;IACP,QAAQ;IACR,SAAS;IACT,YAAY;KAAE,UAAU;KAAK,MAAM;KAAU;IAC7C;GACD,SAAS;IAAE,OAAO;IAAK,QAAQ;IAAK,SAAS;IAAG;aAGhD,oBAACA;IAAK,WAAU;IAAW,MAAK;IAAO,SAAQ;KAAW;KAFtD,OAGQ;GAEG,EAEjB,cAAc,KACd,oBAAC,OAAO;EACP,SAAS;GAAE,OAAO;GAAG,SAAS;GAAG;EACjC,WAAU;EACV,MAAM;GAAE,OAAO;GAAG,SAAS;GAAG;EAC9B,SAAS;GAAE,OAAO;GAAG,SAAS;GAAG;GAChC,IAED;;;;;;AAYL,MAAaC,kBAAiD,EAC7D,gBAEA,oBAACC;CAAkB;YAChB,EAAE,QAAQ,aAAa,eACxB,oBAAC,OAAO;EACP,WAAW,GACV,8MACA,UACA;EACD,aAAW;EACX,YAAY;GACX,MAAM;GACN,WAAW;GACX,SAAS;GACT;EACD,MAAK;EACL,UAAU,EAAE,OAAO,KAAM;YAEzB,oBAAC;GACQ;GACE;GACG;IACZ;GACa;EAEE"}
1
+ {"version":3,"file":"trigger.js","names":["TriggerContent: React.FC<TriggerContentProps>","React","Icon","DefaultTrigger: React.FC<DefaultTriggerProps>","Primitive.Trigger"],"sources":["../../../src/support/components/trigger.tsx"],"sourcesContent":["\"use client\";\n\nimport { AnimatePresence, motion } from \"motion/react\";\nimport * as React from \"react\";\nimport { useNewMessageSound } from \"../../hooks/use-new-message-sound\";\nimport { useTypingSound } from \"../../hooks/use-typing-sound\";\nimport * as Primitive from \"../../primitives\";\nimport type { TriggerRenderProps } from \"../types\";\nimport { cn } from \"../utils\";\nimport Icon from \"./icons\";\nimport { BouncingDots } from \"./typing-indicator\";\n\ntype TriggerContentProps = {\n\tisOpen: boolean;\n\tunreadCount: number;\n\tisTyping: boolean;\n};\n\nconst TriggerContent: React.FC<TriggerContentProps> = ({\n\tisOpen,\n\tunreadCount,\n\tisTyping,\n}) => {\n\tconst playNewMessageSound = useNewMessageSound({\n\t\tvolume: 0.7,\n\t\tplaybackRate: 1.0,\n\t});\n\tconst previousUnreadCountRef = React.useRef(0);\n\n\tuseTypingSound(!isOpen && isTyping, {\n\t\tvolume: 1,\n\t\tplaybackRate: 1.3,\n\t});\n\n\tReact.useEffect(() => {\n\t\tif (unreadCount > previousUnreadCountRef.current) {\n\t\t\tplayNewMessageSound();\n\t\t}\n\t\tpreviousUnreadCountRef.current = unreadCount;\n\t}, [unreadCount, playNewMessageSound]);\n\n\treturn (\n\t\t<>\n\t\t\t<AnimatePresence mode=\"wait\">\n\t\t\t\t{isOpen ? (\n\t\t\t\t\t<motion.div\n\t\t\t\t\t\tanimate={{\n\t\t\t\t\t\t\tscale: 1,\n\t\t\t\t\t\t\trotate: 0,\n\t\t\t\t\t\t\topacity: 1,\n\t\t\t\t\t\t\ttransition: { duration: 0.2, ease: \"easeOut\" },\n\t\t\t\t\t\t}}\n\t\t\t\t\t\tclassName=\"flex items-center justify-center\"\n\t\t\t\t\t\texit={{\n\t\t\t\t\t\t\tscale: 0.9,\n\t\t\t\t\t\t\trotate: -45,\n\t\t\t\t\t\t\topacity: 0,\n\t\t\t\t\t\t\ttransition: { duration: 0.1, ease: \"easeIn\" },\n\t\t\t\t\t\t}}\n\t\t\t\t\t\tinitial={{ scale: 0.9, rotate: 45, opacity: 0 }}\n\t\t\t\t\t\tkey=\"chevron\"\n\t\t\t\t\t>\n\t\t\t\t\t\t<Icon className=\"size-5\" name=\"chevron-down\" />\n\t\t\t\t\t</motion.div>\n\t\t\t\t) : isTyping ? (\n\t\t\t\t\t<motion.span\n\t\t\t\t\t\tanimate={{\n\t\t\t\t\t\t\topacity: 1,\n\t\t\t\t\t\t\tscale: 1,\n\t\t\t\t\t\t\ttransition: {\n\t\t\t\t\t\t\t\tduration: 0.2,\n\t\t\t\t\t\t\t\tease: \"easeOut\",\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t}}\n\t\t\t\t\t\tclassName=\"pointer-events-none flex items-center rounded-full text-co-primary\"\n\t\t\t\t\t\texit={{\n\t\t\t\t\t\t\topacity: 0,\n\t\t\t\t\t\t\tscale: 0.9,\n\t\t\t\t\t\t\ttransition: {\n\t\t\t\t\t\t\t\tduration: 0.1,\n\t\t\t\t\t\t\t\tease: \"easeIn\",\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t}}\n\t\t\t\t\t\tinitial={{ opacity: 0, scale: 0.9 }}\n\t\t\t\t\t\tkey=\"typing-indicator\"\n\t\t\t\t\t>\n\t\t\t\t\t\t<BouncingDots className=\"bg-co-primary-foreground\" />\n\t\t\t\t\t</motion.span>\n\t\t\t\t) : (\n\t\t\t\t\t<motion.div\n\t\t\t\t\t\tanimate={{\n\t\t\t\t\t\t\tscale: 1,\n\t\t\t\t\t\t\trotate: 0,\n\t\t\t\t\t\t\topacity: 1,\n\t\t\t\t\t\t\ttransition: { duration: 0.2, ease: \"easeOut\" },\n\t\t\t\t\t\t}}\n\t\t\t\t\t\tclassName=\"flex items-center justify-center\"\n\t\t\t\t\t\texit={{\n\t\t\t\t\t\t\tscale: 0.9,\n\t\t\t\t\t\t\trotate: 45,\n\t\t\t\t\t\t\topacity: 0,\n\t\t\t\t\t\t\ttransition: { duration: 0.1, ease: \"easeIn\" },\n\t\t\t\t\t\t}}\n\t\t\t\t\t\tinitial={{ scale: 0.9, rotate: -45, opacity: 0 }}\n\t\t\t\t\t\tkey=\"chat\"\n\t\t\t\t\t>\n\t\t\t\t\t\t<Icon className=\"size-6.5\" name=\"chat\" variant=\"filled\" />\n\t\t\t\t\t</motion.div>\n\t\t\t\t)}\n\t\t\t</AnimatePresence>\n\n\t\t\t{unreadCount > 0 && (\n\t\t\t\t<motion.div\n\t\t\t\t\tanimate={{ scale: 1, opacity: 1 }}\n\t\t\t\t\tclassName=\"absolute top-0.5 right-0.5 flex size-2 items-center justify-center rounded-full bg-co-destructive font-medium text-[10px] text-co-destructive-foreground text-white text-xs\"\n\t\t\t\t\texit={{ scale: 0, opacity: 0 }}\n\t\t\t\t\tinitial={{ scale: 0, opacity: 0 }}\n\t\t\t\t/>\n\t\t\t)}\n\t\t</>\n\t);\n};\n\nexport type DefaultTriggerProps = {\n\tclassName?: string;\n};\n\n/**\n * Default styled trigger button.\n * Used internally when no custom trigger is provided.\n */\nexport const DefaultTrigger: React.FC<DefaultTriggerProps> = ({\n\tclassName,\n}) => (\n\t<Primitive.Trigger asChild>\n\t\t{({ isOpen, unreadCount, isTyping }: TriggerRenderProps) => (\n\t\t\t<motion.button\n\t\t\t\tclassName={cn(\n\t\t\t\t\t\"relative z-[9999] flex size-14 cursor-pointer items-center justify-center rounded-full bg-co-primary text-co-primary-foreground transition-colors hover:bg-co-primary/90 data-[open=true]:bg-co-primary/90\",\n\t\t\t\t\tclassName\n\t\t\t\t)}\n\t\t\t\tdata-open={isOpen}\n\t\t\t\ttransition={{\n\t\t\t\t\ttype: \"spring\",\n\t\t\t\t\tstiffness: 800,\n\t\t\t\t\tdamping: 17,\n\t\t\t\t}}\n\t\t\t\ttype=\"button\"\n\t\t\t\twhileTap={{ scale: 0.95 }}\n\t\t\t>\n\t\t\t\t<TriggerContent\n\t\t\t\t\tisOpen={isOpen}\n\t\t\t\t\tisTyping={isTyping}\n\t\t\t\t\tunreadCount={unreadCount}\n\t\t\t\t/>\n\t\t\t</motion.button>\n\t\t)}\n\t</Primitive.Trigger>\n);\n"],"mappings":";;;;;;;;;;;;;AAkBA,MAAMA,kBAAiD,EACtD,QACA,aACA,eACK;CACL,MAAM,sBAAsB,mBAAmB;EAC9C,QAAQ;EACR,cAAc;EACd,CAAC;CACF,MAAM,yBAAyBC,QAAM,OAAO,EAAE;AAE9C,gBAAe,CAAC,UAAU,UAAU;EACnC,QAAQ;EACR,cAAc;EACd,CAAC;AAEF,SAAM,gBAAgB;AACrB,MAAI,cAAc,uBAAuB,QACxC,sBAAqB;AAEtB,yBAAuB,UAAU;IAC/B,CAAC,aAAa,oBAAoB,CAAC;AAEtC,QACC,4CACC,oBAAC;EAAgB,MAAK;YACpB,SACA,oBAAC,OAAO;GACP,SAAS;IACR,OAAO;IACP,QAAQ;IACR,SAAS;IACT,YAAY;KAAE,UAAU;KAAK,MAAM;KAAW;IAC9C;GACD,WAAU;GACV,MAAM;IACL,OAAO;IACP,QAAQ;IACR,SAAS;IACT,YAAY;KAAE,UAAU;KAAK,MAAM;KAAU;IAC7C;GACD,SAAS;IAAE,OAAO;IAAK,QAAQ;IAAI,SAAS;IAAG;aAG/C,oBAACC;IAAK,WAAU;IAAS,MAAK;KAAiB;KAF3C,UAGQ,GACV,WACH,oBAAC,OAAO;GACP,SAAS;IACR,SAAS;IACT,OAAO;IACP,YAAY;KACX,UAAU;KACV,MAAM;KACN;IACD;GACD,WAAU;GACV,MAAM;IACL,SAAS;IACT,OAAO;IACP,YAAY;KACX,UAAU;KACV,MAAM;KACN;IACD;GACD,SAAS;IAAE,SAAS;IAAG,OAAO;IAAK;aAGnC,oBAAC,gBAAa,WAAU,6BAA6B;KAFjD,mBAGS,GAEd,oBAAC,OAAO;GACP,SAAS;IACR,OAAO;IACP,QAAQ;IACR,SAAS;IACT,YAAY;KAAE,UAAU;KAAK,MAAM;KAAW;IAC9C;GACD,WAAU;GACV,MAAM;IACL,OAAO;IACP,QAAQ;IACR,SAAS;IACT,YAAY;KAAE,UAAU;KAAK,MAAM;KAAU;IAC7C;GACD,SAAS;IAAE,OAAO;IAAK,QAAQ;IAAK,SAAS;IAAG;aAGhD,oBAACA;IAAK,WAAU;IAAW,MAAK;IAAO,SAAQ;KAAW;KAFtD,OAGQ;GAEG,EAEjB,cAAc,KACd,oBAAC,OAAO;EACP,SAAS;GAAE,OAAO;GAAG,SAAS;GAAG;EACjC,WAAU;EACV,MAAM;GAAE,OAAO;GAAG,SAAS;GAAG;EAC9B,SAAS;GAAE,OAAO;GAAG,SAAS;GAAG;GAChC,IAED;;;;;;AAYL,MAAaC,kBAAiD,EAC7D,gBAEA,oBAACC;CAAkB;YAChB,EAAE,QAAQ,aAAa,eACxB,oBAAC,OAAO;EACP,WAAW,GACV,8MACA,UACA;EACD,aAAW;EACX,YAAY;GACX,MAAM;GACN,WAAW;GACX,SAAS;GACT;EACD,MAAK;EACL,UAAU,EAAE,OAAO,KAAM;YAEzB,oBAAC;GACQ;GACE;GACG;IACZ;GACa;EAEE"}
@@ -12,7 +12,7 @@ const BouncingDots = ({ className }) => /* @__PURE__ */ jsxs("div", {
12
12
  /* @__PURE__ */ jsx("span", { className: cn("dot-bounce-3 size-1 rounded-full bg-co-primary", className) })
13
13
  ]
14
14
  });
15
- const TypingIndicator = React$1.forwardRef(({ participants, availableAIAgents = [], availableHumanAgents = [], withAvatars = true, className,...props }, ref) => {
15
+ const TypingIndicator = React$1.forwardRef(({ participants, availableAIAgents = [], availableHumanAgents = [], withAvatars = true, className, ...props }, ref) => {
16
16
  if (!participants || participants.length === 0) return null;
17
17
  const humanParticipantIds = participants.filter((p) => p.type === "team_member").map((p) => p.id);
18
18
  const aiParticipantIds = participants.filter((p) => p.type === "ai").map((p) => p.id);