@cossistant/react 0.0.26 → 0.0.29

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 (247) hide show
  1. package/README.md +1 -1
  2. package/api.d.ts +1 -1
  3. package/api.d.ts.map +1 -1
  4. package/checks.d.ts +1 -1
  5. package/checks.d.ts.map +1 -1
  6. package/coerce.d.ts +1 -1
  7. package/coerce.d.ts.map +1 -1
  8. package/conversation.d.ts +6 -3
  9. package/conversation.d.ts.map +1 -1
  10. package/core.d.ts +1 -1
  11. package/core.d.ts.map +1 -1
  12. package/errors.d.ts +12 -3
  13. package/errors.d.ts.map +1 -1
  14. package/errors2.d.ts +1 -1
  15. package/errors2.d.ts.map +1 -1
  16. package/hooks/index.d.ts +3 -2
  17. package/hooks/index.js +6 -5
  18. package/hooks/private/store/use-website-store.js +2 -1
  19. package/hooks/private/store/use-website-store.js.map +1 -1
  20. package/hooks/private/use-client-query.d.ts +6 -0
  21. package/hooks/private/use-client-query.d.ts.map +1 -1
  22. package/hooks/private/use-client-query.js +26 -3
  23. package/hooks/private/use-client-query.js.map +1 -1
  24. package/hooks/private/use-grouped-messages.d.ts +8 -5
  25. package/hooks/private/use-grouped-messages.d.ts.map +1 -1
  26. package/hooks/private/use-grouped-messages.js +44 -11
  27. package/hooks/private/use-grouped-messages.js.map +1 -1
  28. package/hooks/private/use-multimodal-input.d.ts.map +1 -1
  29. package/hooks/private/use-multimodal-input.js +7 -5
  30. package/hooks/private/use-multimodal-input.js.map +1 -1
  31. package/hooks/private/use-visitor-typing-reporter.d.ts +18 -1
  32. package/hooks/private/use-visitor-typing-reporter.d.ts.map +1 -1
  33. package/hooks/private/use-visitor-typing-reporter.js +34 -4
  34. package/hooks/private/use-visitor-typing-reporter.js.map +1 -1
  35. package/hooks/use-conversation-page.d.ts +1 -0
  36. package/hooks/use-conversation-page.d.ts.map +1 -1
  37. package/hooks/use-conversation-page.js +6 -1
  38. package/hooks/use-conversation-page.js.map +1 -1
  39. package/hooks/use-conversation-preview.d.ts +2 -1
  40. package/hooks/use-conversation-preview.d.ts.map +1 -1
  41. package/hooks/use-conversation-preview.js +1 -1
  42. package/hooks/use-conversation-preview.js.map +1 -1
  43. package/hooks/use-conversation-seen.js +1 -1
  44. package/hooks/use-conversation-seen.js.map +1 -1
  45. package/hooks/use-conversation-timeline-items.js +2 -1
  46. package/hooks/use-conversation-timeline-items.js.map +1 -1
  47. package/hooks/use-conversation-timeline.d.ts.map +1 -1
  48. package/hooks/use-conversation-timeline.js +1 -3
  49. package/hooks/use-conversation-timeline.js.map +1 -1
  50. package/hooks/use-conversation.js +2 -1
  51. package/hooks/use-conversation.js.map +1 -1
  52. package/hooks/use-conversations.js +1 -0
  53. package/hooks/use-conversations.js.map +1 -1
  54. package/hooks/use-create-conversation.d.ts.map +1 -1
  55. package/hooks/use-file-upload.d.ts +55 -0
  56. package/hooks/use-file-upload.d.ts.map +1 -0
  57. package/hooks/use-file-upload.js +100 -0
  58. package/hooks/use-file-upload.js.map +1 -0
  59. package/hooks/use-message-composer.d.ts +11 -0
  60. package/hooks/use-message-composer.d.ts.map +1 -1
  61. package/hooks/use-message-composer.js +7 -3
  62. package/hooks/use-message-composer.js.map +1 -1
  63. package/hooks/use-send-message.d.ts +1 -0
  64. package/hooks/use-send-message.d.ts.map +1 -1
  65. package/hooks/use-send-message.js +63 -11
  66. package/hooks/use-send-message.js.map +1 -1
  67. package/index.d.ts +7 -4
  68. package/index.js +13 -10
  69. package/json-schema.d.ts +70 -0
  70. package/json-schema.d.ts.map +1 -0
  71. package/package.json +4 -3
  72. package/parse.d.ts +1 -1
  73. package/parse.d.ts.map +1 -1
  74. package/primitives/avatar/fallback.d.ts.map +1 -1
  75. package/primitives/avatar/fallback.js +1 -1
  76. package/primitives/avatar/fallback.js.map +1 -1
  77. package/primitives/conversation-timeline.d.ts.map +1 -1
  78. package/primitives/conversation-timeline.js +10 -5
  79. package/primitives/conversation-timeline.js.map +1 -1
  80. package/primitives/day-separator.d.ts +76 -0
  81. package/primitives/day-separator.d.ts.map +1 -0
  82. package/primitives/day-separator.js +111 -0
  83. package/primitives/day-separator.js.map +1 -0
  84. package/primitives/index.d.ts +5 -3
  85. package/primitives/index.js +17 -5
  86. package/primitives/index.parts.d.ts +4 -2
  87. package/primitives/index.parts.js +5 -3
  88. package/primitives/timeline-item-attachments.d.ts +100 -0
  89. package/primitives/timeline-item-attachments.d.ts.map +1 -0
  90. package/primitives/timeline-item-attachments.js +151 -0
  91. package/primitives/timeline-item-attachments.js.map +1 -0
  92. package/primitives/timeline-item-group.d.ts.map +1 -1
  93. package/primitives/timeline-item-group.js +1 -1
  94. package/primitives/timeline-item-group.js.map +1 -1
  95. package/primitives/timeline-item.js +1 -1
  96. package/primitives/timeline-item.js.map +1 -1
  97. package/primitives/trigger.d.ts +91 -0
  98. package/primitives/trigger.d.ts.map +1 -0
  99. package/primitives/trigger.js +74 -0
  100. package/primitives/trigger.js.map +1 -0
  101. package/primitives/window.d.ts +22 -1
  102. package/primitives/window.d.ts.map +1 -1
  103. package/primitives/window.js +91 -5
  104. package/primitives/window.js.map +1 -1
  105. package/provider.d.ts.map +1 -1
  106. package/provider.js +8 -3
  107. package/provider.js.map +1 -1
  108. package/realtime/index.js +1 -1
  109. package/realtime/provider.js +1 -1
  110. package/realtime/support-provider.js +5 -1
  111. package/realtime/support-provider.js.map +1 -1
  112. package/realtime-events.d.ts +165 -2
  113. package/realtime-events.d.ts.map +1 -1
  114. package/registries.d.ts +1 -1
  115. package/registries.d.ts.map +1 -1
  116. package/schemas.d.ts +305 -7
  117. package/schemas.d.ts.map +1 -1
  118. package/schemas2.d.ts +29 -4
  119. package/schemas2.d.ts.map +1 -1
  120. package/schemas3.d.ts +2 -1
  121. package/schemas3.d.ts.map +1 -1
  122. package/standard-schema.d.ts +83 -21
  123. package/standard-schema.d.ts.map +1 -1
  124. package/support/components/button.d.ts +1 -1
  125. package/support/components/content.d.ts +30 -0
  126. package/support/components/content.d.ts.map +1 -0
  127. package/support/components/content.js +282 -0
  128. package/support/components/content.js.map +1 -0
  129. package/support/components/conversation-button-link.js +1 -1
  130. package/support/components/conversation-timeline.d.ts +5 -0
  131. package/support/components/conversation-timeline.d.ts.map +1 -1
  132. package/support/components/conversation-timeline.js +25 -5
  133. package/support/components/conversation-timeline.js.map +1 -1
  134. package/support/components/header.js +1 -1
  135. package/support/components/image-lightbox.d.ts +49 -0
  136. package/support/components/image-lightbox.d.ts.map +1 -0
  137. package/support/components/image-lightbox.js +142 -0
  138. package/support/components/image-lightbox.js.map +1 -0
  139. package/support/components/index.d.ts +5 -4
  140. package/support/components/index.js +4 -4
  141. package/support/components/multimodal-input.d.ts +4 -1
  142. package/support/components/multimodal-input.d.ts.map +1 -1
  143. package/support/components/multimodal-input.js +71 -45
  144. package/support/components/multimodal-input.js.map +1 -1
  145. package/support/components/navigation-tab.js +1 -1
  146. package/support/components/root.d.ts +23 -0
  147. package/support/components/root.d.ts.map +1 -0
  148. package/support/components/root.js +36 -0
  149. package/support/components/root.js.map +1 -0
  150. package/support/components/timeline-message-item.d.ts.map +1 -1
  151. package/support/components/timeline-message-item.js +82 -18
  152. package/support/components/timeline-message-item.js.map +1 -1
  153. package/support/components/trigger.d.ts +14 -0
  154. package/support/components/trigger.d.ts.map +1 -0
  155. package/support/components/{bubble.js → trigger.js} +16 -12
  156. package/support/components/trigger.js.map +1 -0
  157. package/support/components/typing-indicator.d.ts.map +1 -1
  158. package/support/components/typing-indicator.js +1 -0
  159. package/support/components/typing-indicator.js.map +1 -1
  160. package/support/context/controlled-state.d.ts +46 -0
  161. package/support/context/controlled-state.d.ts.map +1 -0
  162. package/support/context/controlled-state.js +34 -0
  163. package/support/context/controlled-state.js.map +1 -0
  164. package/support/context/events.d.ts +103 -0
  165. package/support/context/events.d.ts.map +1 -0
  166. package/support/context/events.js +139 -0
  167. package/support/context/events.js.map +1 -0
  168. package/support/context/handle.d.ts +90 -0
  169. package/support/context/handle.d.ts.map +1 -0
  170. package/support/context/handle.js +79 -0
  171. package/support/context/handle.js.map +1 -0
  172. package/support/context/positioning.d.ts +17 -0
  173. package/support/context/positioning.d.ts.map +1 -0
  174. package/support/context/positioning.js +26 -0
  175. package/support/context/positioning.js.map +1 -0
  176. package/support/context/slots.d.ts +85 -0
  177. package/support/context/slots.d.ts.map +1 -0
  178. package/support/context/slots.js +115 -0
  179. package/support/context/slots.js.map +1 -0
  180. package/support/context/websocket.d.ts +8 -1
  181. package/support/context/websocket.d.ts.map +1 -1
  182. package/support/context/websocket.js +8 -1
  183. package/support/context/websocket.js.map +1 -1
  184. package/support/index.d.ts +239 -54
  185. package/support/index.d.ts.map +1 -1
  186. package/support/index.js +254 -33
  187. package/support/index.js.map +1 -1
  188. package/support/pages/articles.d.ts.map +1 -1
  189. package/support/pages/articles.js +3 -4
  190. package/support/pages/articles.js.map +1 -1
  191. package/support/pages/conversation-history.js +2 -2
  192. package/support/pages/conversation.js +6 -5
  193. package/support/pages/conversation.js.map +1 -1
  194. package/support/pages/home.js +2 -2
  195. package/support/router.d.ts +52 -12
  196. package/support/router.d.ts.map +1 -1
  197. package/support/router.js +78 -30
  198. package/support/router.js.map +1 -1
  199. package/support/store/index.d.ts +2 -2
  200. package/support/store/support-store.d.ts +26 -20
  201. package/support/store/support-store.d.ts.map +1 -1
  202. package/support/store/support-store.js +47 -6
  203. package/support/store/support-store.js.map +1 -1
  204. package/support/{support-D2EgfIts.css → support-C7Xaw-N6.css} +1 -2
  205. package/support/support-C7Xaw-N6.css.map +1 -0
  206. package/support/text/index.d.ts +1 -1
  207. package/support/text/index.d.ts.map +1 -1
  208. package/support/text/index.js.map +1 -1
  209. package/support/types.d.ts +75 -12
  210. package/support/types.d.ts.map +1 -1
  211. package/support.css +2 -2
  212. package/tailwind.css +0 -1
  213. package/timeline-item.d.ts +68 -2
  214. package/timeline-item.d.ts.map +1 -1
  215. package/to-json-schema.d.ts +96 -0
  216. package/to-json-schema.d.ts.map +1 -0
  217. package/util.d.ts +6 -2
  218. package/util.d.ts.map +1 -1
  219. package/utils/index.d.ts +2 -1
  220. package/utils/index.js +2 -1
  221. package/utils/merge-refs.d.ts +30 -0
  222. package/utils/merge-refs.d.ts.map +1 -0
  223. package/utils/merge-refs.js +46 -0
  224. package/utils/merge-refs.js.map +1 -0
  225. package/utils/use-render-element.d.ts.map +1 -1
  226. package/utils/use-render-element.js +36 -8
  227. package/utils/use-render-element.js.map +1 -1
  228. package/versions.d.ts +2 -2
  229. package/versions.d.ts.map +1 -1
  230. package/zod-extensions.d.ts +1 -1
  231. package/zod-extensions.d.ts.map +1 -1
  232. package/primitives/bubble.d.ts +0 -38
  233. package/primitives/bubble.d.ts.map +0 -1
  234. package/primitives/bubble.js +0 -57
  235. package/primitives/bubble.js.map +0 -1
  236. package/support/components/bubble.d.ts +0 -10
  237. package/support/components/bubble.d.ts.map +0 -1
  238. package/support/components/bubble.js.map +0 -1
  239. package/support/components/container.d.ts +0 -13
  240. package/support/components/container.d.ts.map +0 -1
  241. package/support/components/container.js +0 -109
  242. package/support/components/container.js.map +0 -1
  243. package/support/components/support-content.d.ts +0 -22
  244. package/support/components/support-content.d.ts.map +0 -1
  245. package/support/components/support-content.js +0 -48
  246. package/support/components/support-content.js.map +0 -1
  247. package/support/support-D2EgfIts.css.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"use-conversations.js","names":[],"sources":["../../src/hooks/use-conversations.ts"],"sourcesContent":["import type { ConversationPagination } from \"@cossistant/core\";\nimport type {\n\tListConversationsRequest,\n\tListConversationsResponse,\n} from \"@cossistant/types/api/conversation\";\nimport { useCallback, useMemo } from \"react\";\nimport { useSupport } from \"../provider\";\nimport { useStoreSelector } from \"./private/store/use-store-selector\";\nimport { useClientQuery } from \"./private/use-client-query\";\n\ntype ConversationsSelection = {\n\tconversations: ListConversationsResponse[\"conversations\"];\n\tpagination: ConversationPagination | null;\n};\n\nfunction areSelectionsEqual(\n\ta: ConversationsSelection,\n\tb: ConversationsSelection\n): boolean {\n\tconst samePagination =\n\t\ta.pagination === b.pagination ||\n\t\t(Boolean(a.pagination) &&\n\t\t\tBoolean(b.pagination) &&\n\t\t\ta.pagination?.page === b.pagination?.page &&\n\t\t\ta.pagination?.limit === b.pagination?.limit &&\n\t\t\ta.pagination?.total === b.pagination?.total &&\n\t\t\ta.pagination?.totalPages === b.pagination?.totalPages &&\n\t\t\ta.pagination?.hasMore === b.pagination?.hasMore);\n\n\tif (!samePagination) {\n\t\treturn false;\n\t}\n\n\tif (a.conversations.length !== b.conversations.length) {\n\t\treturn false;\n\t}\n\n\treturn a.conversations.every(\n\t\t(conversation, index) => conversation === b.conversations[index]\n\t);\n}\n\nexport type UseConversationsOptions = Partial<\n\tOmit<ListConversationsRequest, \"visitorId\">\n> & {\n\tenabled?: boolean;\n\trefetchInterval?: number | false;\n\trefetchOnWindowFocus?: boolean;\n};\n\nexport type UseConversationsResult = {\n\tconversations: ListConversationsResponse[\"conversations\"];\n\tpagination: ConversationPagination | null;\n\tisLoading: boolean;\n\terror: Error | null;\n\trefetch: (\n\t\targs?: Partial<ListConversationsRequest>\n\t) => Promise<ListConversationsResponse | undefined>;\n};\n\n/**\n * Fetches and subscribes to the authenticated visitor's conversation list.\n *\n * The hook keeps the store in sync with the REST client and exposes\n * pagination metadata plus a refetch helper for manual refreshes. The\n * `options` mirror the public API filters so the UI can request slices of the\n * inbox without duplicating data-fetching logic.\n *\n * @param options Filtering and lifecycle controls for the query.\n * @returns Conversations, pagination data, loading state, and a refetch\n * helper.\n */\nexport function useConversations(\n\toptions: UseConversationsOptions = {}\n): UseConversationsResult {\n\tconst { client } = useSupport();\n\n\tconst {\n\t\tlimit,\n\t\tpage,\n\t\torder,\n\t\torderBy,\n\t\tstatus,\n\t\tenabled = true,\n\t\trefetchInterval = false,\n\t\trefetchOnWindowFocus = true,\n\t} = options;\n\n\tconst requestDefaults = useMemo(\n\t\t() => ({ limit, page, status, orderBy, order }),\n\t\t[limit, page, status, orderBy, order]\n\t);\n\n\tconst store = client.conversationsStore;\n\n\tconst selection = useStoreSelector(\n\t\tstore,\n\t\t(state): ConversationsSelection => ({\n\t\t\tconversations: state.ids\n\t\t\t\t.map((id) => state.byId[id])\n\t\t\t\t.filter(\n\t\t\t\t\t(\n\t\t\t\t\t\tconversation\n\t\t\t\t\t): conversation is ListConversationsResponse[\"conversations\"][number] =>\n\t\t\t\t\t\tBoolean(conversation)\n\t\t\t\t),\n\t\t\tpagination: state.pagination,\n\t\t}),\n\t\tareSelectionsEqual\n\t);\n\n\tconst {\n\t\trefetch: queryRefetch,\n\t\tisLoading: queryLoading,\n\t\terror,\n\t} = useClientQuery<\n\t\tListConversationsResponse,\n\t\tPartial<ListConversationsRequest>\n\t>({\n\t\tclient,\n\t\tqueryFn: (instance, args) =>\n\t\t\tinstance.listConversations({\n\t\t\t\t...requestDefaults,\n\t\t\t\t...args,\n\t\t\t}),\n\t\tenabled,\n\t\trefetchInterval,\n\t\trefetchOnWindowFocus,\n\t\trefetchOnMount: selection.conversations.length === 0,\n\t\tinitialArgs: requestDefaults,\n\t\tdependencies: [limit, page, status, orderBy, order],\n\t});\n\n\tconst refetch = useCallback(\n\t\t(args?: Partial<ListConversationsRequest>) =>\n\t\t\tqueryRefetch({\n\t\t\t\t...requestDefaults,\n\t\t\t\t...args,\n\t\t\t}),\n\t\t[queryRefetch, requestDefaults]\n\t);\n\n\tconst isInitialLoad = selection.conversations.length === 0;\n\tconst isLoading = isInitialLoad ? queryLoading : false;\n\n\treturn {\n\t\tconversations: selection.conversations,\n\t\tpagination: selection.pagination,\n\t\tisLoading,\n\t\terror,\n\t\trefetch,\n\t};\n}\n"],"mappings":";;;;;;AAeA,SAAS,mBACR,GACA,GACU;AAWV,KAAI,EATH,EAAE,eAAe,EAAE,cAClB,QAAQ,EAAE,WAAW,IACrB,QAAQ,EAAE,WAAW,IACrB,EAAE,YAAY,SAAS,EAAE,YAAY,QACrC,EAAE,YAAY,UAAU,EAAE,YAAY,SACtC,EAAE,YAAY,UAAU,EAAE,YAAY,SACtC,EAAE,YAAY,eAAe,EAAE,YAAY,cAC3C,EAAE,YAAY,YAAY,EAAE,YAAY,SAGzC,QAAO;AAGR,KAAI,EAAE,cAAc,WAAW,EAAE,cAAc,OAC9C,QAAO;AAGR,QAAO,EAAE,cAAc,OACrB,cAAc,UAAU,iBAAiB,EAAE,cAAc,OAC1D;;;;;;;;;;;;;;AAiCF,SAAgB,iBACf,UAAmC,EAAE,EACZ;CACzB,MAAM,EAAE,WAAW,YAAY;CAE/B,MAAM,EACL,OACA,MACA,OACA,SACA,QACA,UAAU,MACV,kBAAkB,OAClB,uBAAuB,SACpB;CAEJ,MAAM,kBAAkB,eAChB;EAAE;EAAO;EAAM;EAAQ;EAAS;EAAO,GAC9C;EAAC;EAAO;EAAM;EAAQ;EAAS;EAAM,CACrC;CAED,MAAM,QAAQ,OAAO;CAErB,MAAM,YAAY,iBACjB,QACC,WAAmC;EACnC,eAAe,MAAM,IACnB,KAAK,OAAO,MAAM,KAAK,IAAI,CAC3B,QAEC,iBAEA,QAAQ,aAAa,CACtB;EACF,YAAY,MAAM;EAClB,GACD,mBACA;CAED,MAAM,EACL,SAAS,cACT,WAAW,cACX,UACG,eAGF;EACD;EACA,UAAU,UAAU,SACnB,SAAS,kBAAkB;GAC1B,GAAG;GACH,GAAG;GACH,CAAC;EACH;EACA;EACA;EACA,gBAAgB,UAAU,cAAc,WAAW;EACnD,aAAa;EACb,cAAc;GAAC;GAAO;GAAM;GAAQ;GAAS;GAAM;EACnD,CAAC;CAEF,MAAM,UAAU,aACd,SACA,aAAa;EACZ,GAAG;EACH,GAAG;EACH,CAAC,EACH,CAAC,cAAc,gBAAgB,CAC/B;CAGD,MAAM,YADgB,UAAU,cAAc,WAAW,IACvB,eAAe;AAEjD,QAAO;EACN,eAAe,UAAU;EACzB,YAAY,UAAU;EACtB;EACA;EACA;EACA"}
1
+ {"version":3,"file":"use-conversations.js","names":[],"sources":["../../src/hooks/use-conversations.ts"],"sourcesContent":["import type { ConversationPagination } from \"@cossistant/core\";\nimport type {\n\tListConversationsRequest,\n\tListConversationsResponse,\n} from \"@cossistant/types/api/conversation\";\nimport { useCallback, useMemo } from \"react\";\nimport { useSupport } from \"../provider\";\nimport { useStoreSelector } from \"./private/store/use-store-selector\";\nimport { useClientQuery } from \"./private/use-client-query\";\n\ntype ConversationsSelection = {\n\tconversations: ListConversationsResponse[\"conversations\"];\n\tpagination: ConversationPagination | null;\n};\n\nfunction areSelectionsEqual(\n\ta: ConversationsSelection,\n\tb: ConversationsSelection\n): boolean {\n\tconst samePagination =\n\t\ta.pagination === b.pagination ||\n\t\t(Boolean(a.pagination) &&\n\t\t\tBoolean(b.pagination) &&\n\t\t\ta.pagination?.page === b.pagination?.page &&\n\t\t\ta.pagination?.limit === b.pagination?.limit &&\n\t\t\ta.pagination?.total === b.pagination?.total &&\n\t\t\ta.pagination?.totalPages === b.pagination?.totalPages &&\n\t\t\ta.pagination?.hasMore === b.pagination?.hasMore);\n\n\tif (!samePagination) {\n\t\treturn false;\n\t}\n\n\tif (a.conversations.length !== b.conversations.length) {\n\t\treturn false;\n\t}\n\n\treturn a.conversations.every(\n\t\t(conversation, index) => conversation === b.conversations[index]\n\t);\n}\n\nexport type UseConversationsOptions = Partial<\n\tOmit<ListConversationsRequest, \"visitorId\">\n> & {\n\tenabled?: boolean;\n\trefetchInterval?: number | false;\n\trefetchOnWindowFocus?: boolean;\n};\n\nexport type UseConversationsResult = {\n\tconversations: ListConversationsResponse[\"conversations\"];\n\tpagination: ConversationPagination | null;\n\tisLoading: boolean;\n\terror: Error | null;\n\trefetch: (\n\t\targs?: Partial<ListConversationsRequest>\n\t) => Promise<ListConversationsResponse | undefined>;\n};\n\n/**\n * Fetches and subscribes to the authenticated visitor's conversation list.\n *\n * The hook keeps the store in sync with the REST client and exposes\n * pagination metadata plus a refetch helper for manual refreshes. The\n * `options` mirror the public API filters so the UI can request slices of the\n * inbox without duplicating data-fetching logic.\n *\n * @param options Filtering and lifecycle controls for the query.\n * @returns Conversations, pagination data, loading state, and a refetch\n * helper.\n */\nexport function useConversations(\n\toptions: UseConversationsOptions = {}\n): UseConversationsResult {\n\tconst { client } = useSupport();\n\n\tconst {\n\t\tlimit,\n\t\tpage,\n\t\torder,\n\t\torderBy,\n\t\tstatus,\n\t\tenabled = true,\n\t\trefetchInterval = false,\n\t\trefetchOnWindowFocus = true,\n\t} = options;\n\n\tconst requestDefaults = useMemo(\n\t\t() => ({ limit, page, status, orderBy, order }),\n\t\t[limit, page, status, orderBy, order]\n\t);\n\n\tconst store = client.conversationsStore;\n\n\tconst selection = useStoreSelector(\n\t\tstore,\n\t\t(state): ConversationsSelection => ({\n\t\t\tconversations: state.ids\n\t\t\t\t.map((id) => state.byId[id])\n\t\t\t\t.filter(\n\t\t\t\t\t(\n\t\t\t\t\t\tconversation\n\t\t\t\t\t): conversation is ListConversationsResponse[\"conversations\"][number] =>\n\t\t\t\t\t\tBoolean(conversation)\n\t\t\t\t),\n\t\t\tpagination: state.pagination,\n\t\t}),\n\t\tareSelectionsEqual\n\t);\n\n\tconst {\n\t\trefetch: queryRefetch,\n\t\tisLoading: queryLoading,\n\t\terror,\n\t} = useClientQuery<\n\t\tListConversationsResponse,\n\t\tPartial<ListConversationsRequest>\n\t>({\n\t\tclient,\n\t\tqueryKey: `conversations:${limit ?? \"\"}:${page ?? \"\"}:${status ?? \"\"}:${orderBy ?? \"\"}:${order ?? \"\"}`,\n\t\tqueryFn: (instance, args) =>\n\t\t\tinstance.listConversations({\n\t\t\t\t...requestDefaults,\n\t\t\t\t...args,\n\t\t\t}),\n\t\tenabled,\n\t\trefetchInterval,\n\t\trefetchOnWindowFocus,\n\t\trefetchOnMount: selection.conversations.length === 0,\n\t\tinitialArgs: requestDefaults,\n\t\tdependencies: [limit, page, status, orderBy, order],\n\t});\n\n\tconst refetch = useCallback(\n\t\t(args?: Partial<ListConversationsRequest>) =>\n\t\t\tqueryRefetch({\n\t\t\t\t...requestDefaults,\n\t\t\t\t...args,\n\t\t\t}),\n\t\t[queryRefetch, requestDefaults]\n\t);\n\n\tconst isInitialLoad = selection.conversations.length === 0;\n\tconst isLoading = isInitialLoad ? queryLoading : false;\n\n\treturn {\n\t\tconversations: selection.conversations,\n\t\tpagination: selection.pagination,\n\t\tisLoading,\n\t\terror,\n\t\trefetch,\n\t};\n}\n"],"mappings":";;;;;;AAeA,SAAS,mBACR,GACA,GACU;AAWV,KAAI,EATH,EAAE,eAAe,EAAE,cAClB,QAAQ,EAAE,WAAW,IACrB,QAAQ,EAAE,WAAW,IACrB,EAAE,YAAY,SAAS,EAAE,YAAY,QACrC,EAAE,YAAY,UAAU,EAAE,YAAY,SACtC,EAAE,YAAY,UAAU,EAAE,YAAY,SACtC,EAAE,YAAY,eAAe,EAAE,YAAY,cAC3C,EAAE,YAAY,YAAY,EAAE,YAAY,SAGzC,QAAO;AAGR,KAAI,EAAE,cAAc,WAAW,EAAE,cAAc,OAC9C,QAAO;AAGR,QAAO,EAAE,cAAc,OACrB,cAAc,UAAU,iBAAiB,EAAE,cAAc,OAC1D;;;;;;;;;;;;;;AAiCF,SAAgB,iBACf,UAAmC,EAAE,EACZ;CACzB,MAAM,EAAE,WAAW,YAAY;CAE/B,MAAM,EACL,OACA,MACA,OACA,SACA,QACA,UAAU,MACV,kBAAkB,OAClB,uBAAuB,SACpB;CAEJ,MAAM,kBAAkB,eAChB;EAAE;EAAO;EAAM;EAAQ;EAAS;EAAO,GAC9C;EAAC;EAAO;EAAM;EAAQ;EAAS;EAAM,CACrC;CAED,MAAM,QAAQ,OAAO;CAErB,MAAM,YAAY,iBACjB,QACC,WAAmC;EACnC,eAAe,MAAM,IACnB,KAAK,OAAO,MAAM,KAAK,IAAI,CAC3B,QAEC,iBAEA,QAAQ,aAAa,CACtB;EACF,YAAY,MAAM;EAClB,GACD,mBACA;CAED,MAAM,EACL,SAAS,cACT,WAAW,cACX,UACG,eAGF;EACD;EACA,UAAU,iBAAiB,SAAS,GAAG,GAAG,QAAQ,GAAG,GAAG,UAAU,GAAG,GAAG,WAAW,GAAG,GAAG,SAAS;EAClG,UAAU,UAAU,SACnB,SAAS,kBAAkB;GAC1B,GAAG;GACH,GAAG;GACH,CAAC;EACH;EACA;EACA;EACA,gBAAgB,UAAU,cAAc,WAAW;EACnD,aAAa;EACb,cAAc;GAAC;GAAO;GAAM;GAAQ;GAAS;GAAM;EACnD,CAAC;CAEF,MAAM,UAAU,aACd,SACA,aAAa;EACZ,GAAG;EACH,GAAG;EACH,CAAC,EACH,CAAC,cAAc,gBAAgB,CAC/B;CAGD,MAAM,YADgB,UAAU,cAAc,WAAW,IACvB,eAAe;AAEjD,QAAO;EACN,eAAe,UAAU;EACzB,YAAY,UAAU;EACtB;EACA;EACA;EACA"}
@@ -1 +1 @@
1
- {"version":3,"file":"use-create-conversation.d.ts","names":[],"sources":["../../src/hooks/use-create-conversation.ts"],"sourcesContent":[],"mappings":";;;;;;KAOY,4BAAA;WACF;EADE,SAAA,CAAA,EAAA,CAAA,IAAA,EAEQ,8BAFoB,EAAA,GAAA,IAAA;EAC9B,OAAA,CAAA,EAAA,CAAA,KAAA,EAES,KAFT,EAAA,GAAA,IAAA;CACU;AACD,KAGP,2BAAA,GAHO;EAAK,cAAA,CAAA,EAAA,MAAA;EAGZ,oBAAA,CAAA,EAEY,YAFe,EAAA;EAS3B,SAAA,CAAA,EAAA,MAAA;EACU,SAAA,CAAA,EAAA,MAAA,GAAA,IAAA;EAER,MAAA,CAAA,EAPJ,YAOI,CAAA,QAAA,CAAA;EACA,KAAA,CAAA,EAAA,MAAA,GAAA,IAAA;CAAR;AAEE,KANI,2BAAA,GAMJ;EAAK,MAAA,EAAA,CAAA,SAAA,CAAA,EALS,2BAKT,EAAA,GAAA,IAAA;EAqBG,WAAA,EAAA,CAAA,SACN,CAD2B,EAxBvB,2BAyBJ,EAAA,GAxBJ,OAyBH,CAzBW,8BAyBgB,GAAA,IAAA,CAAA;;SAvBtB;;;;;;;;iBAqBQ,qBAAA,WACN,+BACP"}
1
+ {"version":3,"file":"use-create-conversation.d.ts","names":[],"sources":["../../src/hooks/use-create-conversation.ts"],"sourcesContent":[],"mappings":";;;;;;KAOY,4BAAA;WACF;EADE,SAAA,CAAA,EAAA,CAAA,IAAA,EAEQ,8BAFoB,EAAA,GAAA,IAAA;EAC9B,OAAA,CAAA,EAAA,CAAA,KAAA,EAES,KAFT,EAAA,GAAA,IAAA;CACU;AACD,KAGP,2BAAA,GAHO;EAAK,cAAA,CAAA,EAAA,MAAA;EAGZ,oBAAA,CAAA,EAEY,YAFe,EAAA;EAS3B,SAAA,CAAA,EAAA,MAAA;EACU,SAAA,CAAA,EAAA,MAAA,GAAA,IAAA;EAER,MAAA,CAAA,EAPJ,YAOI,CAAA,QAAA,CAAA;EACA,KAAA,CAAA,EAAA,MAAA,GAAA,IAAA;CAAR;AAEE,KANI,2BAAA,GAMJ;EAAK,MAAA,EAAA,CAAA,SAAA,CAAA,EALS,2BAKT,EAAA,GAAA,IAAA;EAqBG,WAAA,EAAA,CAAA,SACN,CAD2B,EAxBvB,2BAyBJ,EAAA,GAxBJ,OAwBI,CAxBI,8BAyBgB,GAAA,IAAA,CAAA;;SAvBtB;;;;;;;;iBAqBQ,qBAAA,WACN,+BACP"}
@@ -0,0 +1,55 @@
1
+ import { TimelinePartFile, TimelinePartImage } from "../timeline-item.js";
2
+ import { CossistantClient } from "@cossistant/core";
3
+
4
+ //#region src/hooks/use-file-upload.d.ts
5
+ type FileUploadPart = TimelinePartImage | TimelinePartFile;
6
+ type UseFileUploadOptions = {
7
+ /**
8
+ * Optional Cossistant client instance.
9
+ * If not provided, uses the client from SupportProvider context.
10
+ */
11
+ client?: CossistantClient;
12
+ };
13
+ type UseFileUploadReturn = {
14
+ /**
15
+ * Upload files and return timeline parts ready to include in a message.
16
+ * Files are uploaded to S3 in parallel.
17
+ */
18
+ uploadFiles: (files: File[], conversationId: string) => Promise<FileUploadPart[]>;
19
+ /**
20
+ * Whether an upload is currently in progress.
21
+ */
22
+ isUploading: boolean;
23
+ /**
24
+ * Upload progress (0-100). Updates as files complete.
25
+ */
26
+ progress: number;
27
+ /**
28
+ * Error from the most recent upload attempt, if any.
29
+ */
30
+ error: Error | null;
31
+ /**
32
+ * Reset the upload state (clear errors and progress).
33
+ */
34
+ reset: () => void;
35
+ };
36
+ /**
37
+ * Hook for uploading files to S3 for inclusion in chat messages.
38
+ * Handles validation, upload progress tracking, and error management.
39
+ *
40
+ * @example
41
+ * ```tsx
42
+ * const { uploadFiles, isUploading, error } = useFileUpload();
43
+ *
44
+ * const handleSend = async () => {
45
+ * if (files.length > 0) {
46
+ * const parts = await uploadFiles(files, conversationId);
47
+ * // Include parts in message...
48
+ * }
49
+ * };
50
+ * ```
51
+ */
52
+ declare function useFileUpload(options?: UseFileUploadOptions): UseFileUploadReturn;
53
+ //#endregion
54
+ export { FileUploadPart, UseFileUploadOptions, UseFileUploadReturn, useFileUpload };
55
+ //# sourceMappingURL=use-file-upload.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"use-file-upload.d.ts","names":[],"sources":["../../src/hooks/use-file-upload.ts"],"sourcesContent":[],"mappings":";;;;KAeY,cAAA,GAAiB,oBAAoB;KAErC,oBAAA;EAFA;AAEZ;AAQA;;EAQc,MAAA,CAAA,EAXJ,gBAWI;CAAR;AAeE,KAvBI,mBAAA,GAuBJ;EAAK;AAwBb;;;uBAzCS,mCAEH,QAAQ;;;;;;;;;;;;SAeN;;;;;;;;;;;;;;;;;;;;;;iBAwBQ,aAAA,WACN,uBACP"}
@@ -0,0 +1,100 @@
1
+ "use client";
2
+
3
+
4
+ import { useSupport } from "../provider.js";
5
+ import { useCallback, useState } from "react";
6
+ import { MAX_FILES_PER_MESSAGE, isImageMimeType, validateFiles } from "@cossistant/core";
7
+
8
+ //#region src/hooks/use-file-upload.ts
9
+ /**
10
+ * Hook for uploading files to S3 for inclusion in chat messages.
11
+ * Handles validation, upload progress tracking, and error management.
12
+ *
13
+ * @example
14
+ * ```tsx
15
+ * const { uploadFiles, isUploading, error } = useFileUpload();
16
+ *
17
+ * const handleSend = async () => {
18
+ * if (files.length > 0) {
19
+ * const parts = await uploadFiles(files, conversationId);
20
+ * // Include parts in message...
21
+ * }
22
+ * };
23
+ * ```
24
+ */
25
+ function useFileUpload(options = {}) {
26
+ const { client: contextClient } = useSupport();
27
+ const client = options.client ?? contextClient;
28
+ const [isUploading, setIsUploading] = useState(false);
29
+ const [progress, setProgress] = useState(0);
30
+ const [error, setError] = useState(null);
31
+ const reset = useCallback(() => {
32
+ setIsUploading(false);
33
+ setProgress(0);
34
+ setError(null);
35
+ }, []);
36
+ return {
37
+ uploadFiles: useCallback(async (files, conversationId) => {
38
+ if (files.length === 0) return [];
39
+ const validationError = validateFiles(files);
40
+ if (validationError) {
41
+ const err = new Error(validationError);
42
+ setError(err);
43
+ throw err;
44
+ }
45
+ if (files.length > MAX_FILES_PER_MESSAGE) {
46
+ const err = /* @__PURE__ */ new Error(`Cannot upload more than ${MAX_FILES_PER_MESSAGE} files at once`);
47
+ setError(err);
48
+ throw err;
49
+ }
50
+ setIsUploading(true);
51
+ setProgress(0);
52
+ setError(null);
53
+ try {
54
+ const totalFiles = files.length;
55
+ let completedFiles = 0;
56
+ const uploadPromises = files.map(async (file) => {
57
+ const uploadInfo = await client.generateUploadUrl({
58
+ conversationId,
59
+ contentType: file.type,
60
+ fileName: file.name
61
+ });
62
+ await client.uploadFile(file, uploadInfo.uploadUrl, file.type);
63
+ completedFiles += 1;
64
+ setProgress(Math.round(completedFiles / totalFiles * 100));
65
+ if (isImageMimeType(file.type)) return {
66
+ type: "image",
67
+ url: uploadInfo.publicUrl,
68
+ mediaType: file.type,
69
+ fileName: file.name,
70
+ size: file.size
71
+ };
72
+ return {
73
+ type: "file",
74
+ url: uploadInfo.publicUrl,
75
+ mediaType: file.type,
76
+ fileName: file.name,
77
+ size: file.size
78
+ };
79
+ });
80
+ const parts = await Promise.all(uploadPromises);
81
+ setIsUploading(false);
82
+ setProgress(100);
83
+ return parts;
84
+ } catch (err) {
85
+ const normalizedError = err instanceof Error ? err : /* @__PURE__ */ new Error("Upload failed");
86
+ setError(normalizedError);
87
+ setIsUploading(false);
88
+ throw normalizedError;
89
+ }
90
+ }, [client]),
91
+ isUploading,
92
+ progress,
93
+ error,
94
+ reset
95
+ };
96
+ }
97
+
98
+ //#endregion
99
+ export { useFileUpload };
100
+ //# sourceMappingURL=use-file-upload.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"use-file-upload.js","names":[],"sources":["../../src/hooks/use-file-upload.ts"],"sourcesContent":["\"use client\";\n\nimport type { CossistantClient } from \"@cossistant/core\";\nimport {\n\tisImageMimeType,\n\tMAX_FILES_PER_MESSAGE,\n\tvalidateFiles,\n} from \"@cossistant/core\";\nimport type {\n\tTimelinePartFile,\n\tTimelinePartImage,\n} from \"@cossistant/types/api/timeline-item\";\nimport { useCallback, useState } from \"react\";\nimport { useSupport } from \"../provider\";\n\nexport type FileUploadPart = TimelinePartImage | TimelinePartFile;\n\nexport type UseFileUploadOptions = {\n\t/**\n\t * Optional Cossistant client instance.\n\t * If not provided, uses the client from SupportProvider context.\n\t */\n\tclient?: CossistantClient;\n};\n\nexport type UseFileUploadReturn = {\n\t/**\n\t * Upload files and return timeline parts ready to include in a message.\n\t * Files are uploaded to S3 in parallel.\n\t */\n\tuploadFiles: (\n\t\tfiles: File[],\n\t\tconversationId: string\n\t) => Promise<FileUploadPart[]>;\n\n\t/**\n\t * Whether an upload is currently in progress.\n\t */\n\tisUploading: boolean;\n\n\t/**\n\t * Upload progress (0-100). Updates as files complete.\n\t */\n\tprogress: number;\n\n\t/**\n\t * Error from the most recent upload attempt, if any.\n\t */\n\terror: Error | null;\n\n\t/**\n\t * Reset the upload state (clear errors and progress).\n\t */\n\treset: () => void;\n};\n\n/**\n * Hook for uploading files to S3 for inclusion in chat messages.\n * Handles validation, upload progress tracking, and error management.\n *\n * @example\n * ```tsx\n * const { uploadFiles, isUploading, error } = useFileUpload();\n *\n * const handleSend = async () => {\n * if (files.length > 0) {\n * const parts = await uploadFiles(files, conversationId);\n * // Include parts in message...\n * }\n * };\n * ```\n */\nexport function useFileUpload(\n\toptions: UseFileUploadOptions = {}\n): UseFileUploadReturn {\n\tconst { client: contextClient } = useSupport();\n\tconst client = options.client ?? contextClient;\n\n\tconst [isUploading, setIsUploading] = useState(false);\n\tconst [progress, setProgress] = useState(0);\n\tconst [error, setError] = useState<Error | null>(null);\n\n\tconst reset = useCallback(() => {\n\t\tsetIsUploading(false);\n\t\tsetProgress(0);\n\t\tsetError(null);\n\t}, []);\n\n\tconst uploadFiles = useCallback(\n\t\tasync (\n\t\t\tfiles: File[],\n\t\t\tconversationId: string\n\t\t): Promise<FileUploadPart[]> => {\n\t\t\tif (files.length === 0) {\n\t\t\t\treturn [];\n\t\t\t}\n\n\t\t\t// Validate files before upload\n\t\t\tconst validationError = validateFiles(files);\n\t\t\tif (validationError) {\n\t\t\t\tconst err = new Error(validationError);\n\t\t\t\tsetError(err);\n\t\t\t\tthrow err;\n\t\t\t}\n\n\t\t\tif (files.length > MAX_FILES_PER_MESSAGE) {\n\t\t\t\tconst err = new Error(\n\t\t\t\t\t`Cannot upload more than ${MAX_FILES_PER_MESSAGE} files at once`\n\t\t\t\t);\n\t\t\t\tsetError(err);\n\t\t\t\tthrow err;\n\t\t\t}\n\n\t\t\tsetIsUploading(true);\n\t\t\tsetProgress(0);\n\t\t\tsetError(null);\n\n\t\t\ttry {\n\t\t\t\tconst totalFiles = files.length;\n\t\t\t\tlet completedFiles = 0;\n\n\t\t\t\t// Upload files in parallel\n\t\t\t\tconst uploadPromises = files.map(async (file) => {\n\t\t\t\t\t// Generate presigned URL\n\t\t\t\t\tconst uploadInfo = await client.generateUploadUrl({\n\t\t\t\t\t\tconversationId,\n\t\t\t\t\t\tcontentType: file.type,\n\t\t\t\t\t\tfileName: file.name,\n\t\t\t\t\t});\n\n\t\t\t\t\t// Upload file to S3\n\t\t\t\t\tawait client.uploadFile(file, uploadInfo.uploadUrl, file.type);\n\n\t\t\t\t\t// Update progress\n\t\t\t\t\tcompletedFiles += 1;\n\t\t\t\t\tsetProgress(Math.round((completedFiles / totalFiles) * 100));\n\n\t\t\t\t\t// Return timeline part based on file type\n\t\t\t\t\tconst isImage = isImageMimeType(file.type);\n\n\t\t\t\t\tif (isImage) {\n\t\t\t\t\t\treturn {\n\t\t\t\t\t\t\ttype: \"image\" as const,\n\t\t\t\t\t\t\turl: uploadInfo.publicUrl,\n\t\t\t\t\t\t\tmediaType: file.type,\n\t\t\t\t\t\t\tfileName: file.name,\n\t\t\t\t\t\t\tsize: file.size,\n\t\t\t\t\t\t} satisfies TimelinePartImage;\n\t\t\t\t\t}\n\n\t\t\t\t\treturn {\n\t\t\t\t\t\ttype: \"file\" as const,\n\t\t\t\t\t\turl: uploadInfo.publicUrl,\n\t\t\t\t\t\tmediaType: file.type,\n\t\t\t\t\t\tfileName: file.name,\n\t\t\t\t\t\tsize: file.size,\n\t\t\t\t\t} satisfies TimelinePartFile;\n\t\t\t\t});\n\n\t\t\t\tconst parts = await Promise.all(uploadPromises);\n\n\t\t\t\tsetIsUploading(false);\n\t\t\t\tsetProgress(100);\n\n\t\t\t\treturn parts;\n\t\t\t} catch (err) {\n\t\t\t\tconst normalizedError =\n\t\t\t\t\terr instanceof Error ? err : new Error(\"Upload failed\");\n\t\t\t\tsetError(normalizedError);\n\t\t\t\tsetIsUploading(false);\n\t\t\t\tthrow normalizedError;\n\t\t\t}\n\t\t},\n\t\t[client]\n\t);\n\n\treturn {\n\t\tuploadFiles,\n\t\tisUploading,\n\t\tprogress,\n\t\terror,\n\t\treset,\n\t};\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;AAwEA,SAAgB,cACf,UAAgC,EAAE,EACZ;CACtB,MAAM,EAAE,QAAQ,kBAAkB,YAAY;CAC9C,MAAM,SAAS,QAAQ,UAAU;CAEjC,MAAM,CAAC,aAAa,kBAAkB,SAAS,MAAM;CACrD,MAAM,CAAC,UAAU,eAAe,SAAS,EAAE;CAC3C,MAAM,CAAC,OAAO,YAAY,SAAuB,KAAK;CAEtD,MAAM,QAAQ,kBAAkB;AAC/B,iBAAe,MAAM;AACrB,cAAY,EAAE;AACd,WAAS,KAAK;IACZ,EAAE,CAAC;AA0FN,QAAO;EACN,aAzFmB,YACnB,OACC,OACA,mBAC+B;AAC/B,OAAI,MAAM,WAAW,EACpB,QAAO,EAAE;GAIV,MAAM,kBAAkB,cAAc,MAAM;AAC5C,OAAI,iBAAiB;IACpB,MAAM,MAAM,IAAI,MAAM,gBAAgB;AACtC,aAAS,IAAI;AACb,UAAM;;AAGP,OAAI,MAAM,SAAS,uBAAuB;IACzC,MAAM,sBAAM,IAAI,MACf,2BAA2B,sBAAsB,gBACjD;AACD,aAAS,IAAI;AACb,UAAM;;AAGP,kBAAe,KAAK;AACpB,eAAY,EAAE;AACd,YAAS,KAAK;AAEd,OAAI;IACH,MAAM,aAAa,MAAM;IACzB,IAAI,iBAAiB;IAGrB,MAAM,iBAAiB,MAAM,IAAI,OAAO,SAAS;KAEhD,MAAM,aAAa,MAAM,OAAO,kBAAkB;MACjD;MACA,aAAa,KAAK;MAClB,UAAU,KAAK;MACf,CAAC;AAGF,WAAM,OAAO,WAAW,MAAM,WAAW,WAAW,KAAK,KAAK;AAG9D,uBAAkB;AAClB,iBAAY,KAAK,MAAO,iBAAiB,aAAc,IAAI,CAAC;AAK5D,SAFgB,gBAAgB,KAAK,KAAK,CAGzC,QAAO;MACN,MAAM;MACN,KAAK,WAAW;MAChB,WAAW,KAAK;MAChB,UAAU,KAAK;MACf,MAAM,KAAK;MACX;AAGF,YAAO;MACN,MAAM;MACN,KAAK,WAAW;MAChB,WAAW,KAAK;MAChB,UAAU,KAAK;MACf,MAAM,KAAK;MACX;MACA;IAEF,MAAM,QAAQ,MAAM,QAAQ,IAAI,eAAe;AAE/C,mBAAe,MAAM;AACrB,gBAAY,IAAI;AAEhB,WAAO;YACC,KAAK;IACb,MAAM,kBACL,eAAe,QAAQ,sBAAM,IAAI,MAAM,gBAAgB;AACxD,aAAS,gBAAgB;AACzB,mBAAe,MAAM;AACrB,UAAM;;KAGR,CAAC,OAAO,CACR;EAIA;EACA;EACA;EACA;EACA"}
@@ -1,5 +1,6 @@
1
1
  import { TimelineItem } from "../timeline-item.js";
2
2
  import { UseMultimodalInputOptions } from "./private/use-multimodal-input.js";
3
+ import { AnyRealtimeEvent } from "../realtime-events.js";
3
4
  import { CossistantClient } from "@cossistant/core";
4
5
 
5
6
  //#region src/hooks/use-message-composer.d.ts
@@ -35,12 +36,22 @@ type UseMessageComposerOptions = {
35
36
  * File upload options (max size, allowed types, etc.)
36
37
  */
37
38
  fileOptions?: Pick<UseMultimodalInputOptions, "maxFileSize" | "maxFiles" | "allowedFileTypes">;
39
+ /**
40
+ * Optional WebSocket send function for real-time typing events.
41
+ * When provided, typing indicators are sent via WebSocket for better performance.
42
+ */
43
+ realtimeSend?: ((event: AnyRealtimeEvent) => void) | null;
44
+ /**
45
+ * Whether the WebSocket connection is currently established.
46
+ */
47
+ isRealtimeConnected?: boolean;
38
48
  };
39
49
  type UseMessageComposerReturn = {
40
50
  message: string;
41
51
  files: File[];
42
52
  error: Error | null;
43
53
  isSubmitting: boolean;
54
+ isUploading: boolean;
44
55
  canSubmit: boolean;
45
56
  setMessage: (message: string) => void;
46
57
  addFiles: (files: File[]) => void;
@@ -1 +1 @@
1
- {"version":3,"file":"use-message-composer.d.ts","names":[],"sources":["../../src/hooks/use-message-composer.ts"],"sourcesContent":[],"mappings":";;;;;KAUY,yBAAA;;AAAZ;;EAewB,MAAA,EAXf,gBAWe;EAiBL;;;;EAWP,cAAA,EAAA,MAAA,GAAA,IAAwB;EAG5B;;;EASe,oBAAA,CAAA,EAxCC,YAwCD,EAAA;EAwCP;;;;;;;;;;;;;oBA/DG;;;;gBAKJ,KACb;;KAKU,wBAAA;;SAGJ;SACA;;;;oBAQW;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAwCH,kBAAA,UACN,4BACP"}
1
+ {"version":3,"file":"use-message-composer.d.ts","names":[],"sources":["../../src/hooks/use-message-composer.ts"],"sourcesContent":[],"mappings":";;;;;;KAWY,yBAAA;;AAAZ;;EAewB,MAAA,EAXf,gBAWe;EAiBL;;;;EAcsB,cAAA,EAAA,MAAA,GAAA,IAAA;EAQ7B;;;EAaO,oBAAA,CAAA,EApDK,YAoDL,EAAA;EAAI;AAwCvB;;;;;;;;;;;;oBA3EmB;;;;gBAKJ,KACb;;;;;0BAQuB;;;;;;KAQb,wBAAA;;SAGJ;SACA;;;;;oBASW;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAwCH,kBAAA,UACN,4BACP"}
@@ -38,11 +38,13 @@ import { useCallback, useEffect } from "react";
38
38
  * ```
39
39
  */
40
40
  function useMessageComposer(options) {
41
- const { client, conversationId, defaultTimelineItems = [], visitorId, onMessageSent, onError, fileOptions } = options;
41
+ const { client, conversationId, defaultTimelineItems = [], visitorId, onMessageSent, onError, fileOptions, realtimeSend, isRealtimeConnected = false } = options;
42
42
  const sendMessage = useSendMessage({ client });
43
43
  const { handleInputChange: reportTyping, handleSubmit: stopTyping, stop: forceStopTyping } = useVisitorTypingReporter({
44
44
  client,
45
- conversationId
45
+ conversationId,
46
+ realtimeSend,
47
+ isRealtimeConnected
46
48
  });
47
49
  const multimodalInput = useMultimodalInput({
48
50
  onSubmit: async ({ message: messageText, files }) => {
@@ -72,13 +74,15 @@ function useMessageComposer(options) {
72
74
  reportTyping(value);
73
75
  }, [multimodalInput, reportTyping]);
74
76
  const isSubmitting = multimodalInput.isSubmitting || sendMessage.isPending;
77
+ const isUploading = sendMessage.isUploading;
75
78
  const error = multimodalInput.error || sendMessage.error;
76
- const canSubmit = multimodalInput.canSubmit && !sendMessage.isPending;
79
+ const canSubmit = multimodalInput.canSubmit && !sendMessage.isPending && !isUploading;
77
80
  return {
78
81
  message: multimodalInput.message,
79
82
  files: multimodalInput.files,
80
83
  error,
81
84
  isSubmitting,
85
+ isUploading,
82
86
  canSubmit,
83
87
  setMessage,
84
88
  addFiles: multimodalInput.addFiles,
@@ -1 +1 @@
1
- {"version":3,"file":"use-message-composer.js","names":[],"sources":["../../src/hooks/use-message-composer.ts"],"sourcesContent":["import type { CossistantClient } from \"@cossistant/core\";\nimport type { TimelineItem } from \"@cossistant/types/api/timeline-item\";\nimport { useCallback, useEffect } from \"react\";\nimport {\n\ttype UseMultimodalInputOptions,\n\tuseMultimodalInput,\n} from \"./private/use-multimodal-input\";\nimport { useVisitorTypingReporter } from \"./private/use-visitor-typing-reporter\";\nimport { useSendMessage } from \"./use-send-message\";\n\nexport type UseMessageComposerOptions = {\n\t/**\n\t * The Cossistant client instance.\n\t */\n\tclient: CossistantClient;\n\n\t/**\n\t * Current conversation ID. Can be null if no real conversation exists yet.\n\t * Pass null when showing default timeline items before user sends first message.\n\t */\n\tconversationId: string | null;\n\n\t/**\n\t * Default timeline items to include when creating a new conversation.\n\t */\n\tdefaultTimelineItems?: TimelineItem[];\n\n\t/**\n\t * Visitor ID to associate with messages.\n\t */\n\tvisitorId?: string;\n\n\t/**\n\t * Callback when a message is successfully sent.\n\t * @param conversationId - The conversation ID (may be newly created)\n\t * @param messageId - The sent message ID\n\t */\n\tonMessageSent?: (conversationId: string, messageId: string) => void;\n\n\t/**\n\t * Callback when message sending fails.\n\t */\n\tonError?: (error: Error) => void;\n\n\t/**\n\t * File upload options (max size, allowed types, etc.)\n\t */\n\tfileOptions?: Pick<\n\t\tUseMultimodalInputOptions,\n\t\t\"maxFileSize\" | \"maxFiles\" | \"allowedFileTypes\"\n\t>;\n};\n\nexport type UseMessageComposerReturn = {\n\t// Input state\n\tmessage: string;\n\tfiles: File[];\n\terror: Error | null;\n\n\t// Status\n\tisSubmitting: boolean;\n\tcanSubmit: boolean;\n\n\t// Actions\n\tsetMessage: (message: string) => void;\n\taddFiles: (files: File[]) => void;\n\tremoveFile: (index: number) => void;\n\tclearFiles: () => void;\n\tsubmit: () => void;\n\treset: () => void;\n};\n\n/**\n * Combines message input, typing indicators, and message sending into\n * a single, cohesive hook for building message composers.\n *\n * This hook:\n * - Manages text input and file attachments via useMultimodalInput\n * - Sends typing indicators while user is composing\n * - Handles message submission with proper error handling\n * - Automatically resets input after successful send\n * - Works with both pending and real conversations\n *\n * @example\n * ```tsx\n * const composer = useMessageComposer({\n * client,\n * conversationId: realConversationId, // null if pending\n * defaultMessages,\n * visitorId: visitor?.id,\n * onMessageSent: (convId) => {\n * // Update conversation ID if it was created\n * },\n * });\n *\n * return (\n * <MessageInput\n * value={composer.message}\n * onChange={composer.setMessage}\n * onSubmit={composer.submit}\n * disabled={composer.isSubmitting}\n * />\n * );\n * ```\n */\nexport function useMessageComposer(\n\toptions: UseMessageComposerOptions\n): UseMessageComposerReturn {\n\tconst {\n\t\tclient,\n\t\tconversationId,\n\t\tdefaultTimelineItems = [],\n\t\tvisitorId,\n\t\tonMessageSent,\n\t\tonError,\n\t\tfileOptions,\n\t} = options;\n\n\tconst sendMessage = useSendMessage({ client });\n\n\tconst {\n\t\thandleInputChange: reportTyping,\n\t\thandleSubmit: stopTyping,\n\t\tstop: forceStopTyping,\n\t} = useVisitorTypingReporter({\n\t\tclient,\n\t\tconversationId,\n\t});\n\n\tconst multimodalInput = useMultimodalInput({\n\t\tonSubmit: async ({ message: messageText, files }) => {\n\t\t\t// Stop typing indicator\n\t\t\tstopTyping();\n\n\t\t\t// Send the message\n\t\t\tsendMessage.mutate({\n\t\t\t\tconversationId,\n\t\t\t\tmessage: messageText,\n\t\t\t\tfiles,\n\t\t\t\tdefaultTimelineItems,\n\t\t\t\tvisitorId,\n\t\t\t\tonSuccess: (resultConversationId, messageId) => {\n\t\t\t\t\tonMessageSent?.(resultConversationId, messageId);\n\t\t\t\t},\n\t\t\t\tonError: (err) => {\n\t\t\t\t\tonError?.(err);\n\t\t\t\t},\n\t\t\t});\n\t\t},\n\t\tonError,\n\t\t...fileOptions,\n\t});\n\n\t// Clean up typing indicator on unmount\n\tuseEffect(\n\t\t() => () => {\n\t\t\tforceStopTyping();\n\t\t},\n\t\t[forceStopTyping]\n\t);\n\n\t// Wrap setMessage to also report typing\n\tconst setMessage = useCallback(\n\t\t(value: string) => {\n\t\t\tmultimodalInput.setMessage(value);\n\t\t\treportTyping(value);\n\t\t},\n\t\t[multimodalInput, reportTyping]\n\t);\n\n\t// Combine submission states\n\tconst isSubmitting = multimodalInput.isSubmitting || sendMessage.isPending;\n\tconst error = multimodalInput.error || sendMessage.error;\n\tconst canSubmit = multimodalInput.canSubmit && !sendMessage.isPending;\n\n\treturn {\n\t\tmessage: multimodalInput.message,\n\t\tfiles: multimodalInput.files,\n\t\terror,\n\t\tisSubmitting,\n\t\tcanSubmit,\n\t\tsetMessage,\n\t\taddFiles: multimodalInput.addFiles,\n\t\tremoveFile: multimodalInput.removeFile,\n\t\tclearFiles: multimodalInput.clearFiles,\n\t\tsubmit: multimodalInput.submit,\n\t\treset: multimodalInput.reset,\n\t};\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAyGA,SAAgB,mBACf,SAC2B;CAC3B,MAAM,EACL,QACA,gBACA,uBAAuB,EAAE,EACzB,WACA,eACA,SACA,gBACG;CAEJ,MAAM,cAAc,eAAe,EAAE,QAAQ,CAAC;CAE9C,MAAM,EACL,mBAAmB,cACnB,cAAc,YACd,MAAM,oBACH,yBAAyB;EAC5B;EACA;EACA,CAAC;CAEF,MAAM,kBAAkB,mBAAmB;EAC1C,UAAU,OAAO,EAAE,SAAS,aAAa,YAAY;AAEpD,eAAY;AAGZ,eAAY,OAAO;IAClB;IACA,SAAS;IACT;IACA;IACA;IACA,YAAY,sBAAsB,cAAc;AAC/C,qBAAgB,sBAAsB,UAAU;;IAEjD,UAAU,QAAQ;AACjB,eAAU,IAAI;;IAEf,CAAC;;EAEH;EACA,GAAG;EACH,CAAC;AAGF,uBACa;AACX,mBAAiB;IAElB,CAAC,gBAAgB,CACjB;CAGD,MAAM,aAAa,aACjB,UAAkB;AAClB,kBAAgB,WAAW,MAAM;AACjC,eAAa,MAAM;IAEpB,CAAC,iBAAiB,aAAa,CAC/B;CAGD,MAAM,eAAe,gBAAgB,gBAAgB,YAAY;CACjE,MAAM,QAAQ,gBAAgB,SAAS,YAAY;CACnD,MAAM,YAAY,gBAAgB,aAAa,CAAC,YAAY;AAE5D,QAAO;EACN,SAAS,gBAAgB;EACzB,OAAO,gBAAgB;EACvB;EACA;EACA;EACA;EACA,UAAU,gBAAgB;EAC1B,YAAY,gBAAgB;EAC5B,YAAY,gBAAgB;EAC5B,QAAQ,gBAAgB;EACxB,OAAO,gBAAgB;EACvB"}
1
+ {"version":3,"file":"use-message-composer.js","names":[],"sources":["../../src/hooks/use-message-composer.ts"],"sourcesContent":["import type { CossistantClient } from \"@cossistant/core\";\nimport type { TimelineItem } from \"@cossistant/types/api/timeline-item\";\nimport type { AnyRealtimeEvent } from \"@cossistant/types/realtime-events\";\nimport { useCallback, useEffect } from \"react\";\nimport {\n\ttype UseMultimodalInputOptions,\n\tuseMultimodalInput,\n} from \"./private/use-multimodal-input\";\nimport { useVisitorTypingReporter } from \"./private/use-visitor-typing-reporter\";\nimport { useSendMessage } from \"./use-send-message\";\n\nexport type UseMessageComposerOptions = {\n\t/**\n\t * The Cossistant client instance.\n\t */\n\tclient: CossistantClient;\n\n\t/**\n\t * Current conversation ID. Can be null if no real conversation exists yet.\n\t * Pass null when showing default timeline items before user sends first message.\n\t */\n\tconversationId: string | null;\n\n\t/**\n\t * Default timeline items to include when creating a new conversation.\n\t */\n\tdefaultTimelineItems?: TimelineItem[];\n\n\t/**\n\t * Visitor ID to associate with messages.\n\t */\n\tvisitorId?: string;\n\n\t/**\n\t * Callback when a message is successfully sent.\n\t * @param conversationId - The conversation ID (may be newly created)\n\t * @param messageId - The sent message ID\n\t */\n\tonMessageSent?: (conversationId: string, messageId: string) => void;\n\n\t/**\n\t * Callback when message sending fails.\n\t */\n\tonError?: (error: Error) => void;\n\n\t/**\n\t * File upload options (max size, allowed types, etc.)\n\t */\n\tfileOptions?: Pick<\n\t\tUseMultimodalInputOptions,\n\t\t\"maxFileSize\" | \"maxFiles\" | \"allowedFileTypes\"\n\t>;\n\n\t/**\n\t * Optional WebSocket send function for real-time typing events.\n\t * When provided, typing indicators are sent via WebSocket for better performance.\n\t */\n\trealtimeSend?: ((event: AnyRealtimeEvent) => void) | null;\n\n\t/**\n\t * Whether the WebSocket connection is currently established.\n\t */\n\tisRealtimeConnected?: boolean;\n};\n\nexport type UseMessageComposerReturn = {\n\t// Input state\n\tmessage: string;\n\tfiles: File[];\n\terror: Error | null;\n\n\t// Status\n\tisSubmitting: boolean;\n\tisUploading: boolean;\n\tcanSubmit: boolean;\n\n\t// Actions\n\tsetMessage: (message: string) => void;\n\taddFiles: (files: File[]) => void;\n\tremoveFile: (index: number) => void;\n\tclearFiles: () => void;\n\tsubmit: () => void;\n\treset: () => void;\n};\n\n/**\n * Combines message input, typing indicators, and message sending into\n * a single, cohesive hook for building message composers.\n *\n * This hook:\n * - Manages text input and file attachments via useMultimodalInput\n * - Sends typing indicators while user is composing\n * - Handles message submission with proper error handling\n * - Automatically resets input after successful send\n * - Works with both pending and real conversations\n *\n * @example\n * ```tsx\n * const composer = useMessageComposer({\n * client,\n * conversationId: realConversationId, // null if pending\n * defaultMessages,\n * visitorId: visitor?.id,\n * onMessageSent: (convId) => {\n * // Update conversation ID if it was created\n * },\n * });\n *\n * return (\n * <MessageInput\n * value={composer.message}\n * onChange={composer.setMessage}\n * onSubmit={composer.submit}\n * disabled={composer.isSubmitting}\n * />\n * );\n * ```\n */\nexport function useMessageComposer(\n\toptions: UseMessageComposerOptions\n): UseMessageComposerReturn {\n\tconst {\n\t\tclient,\n\t\tconversationId,\n\t\tdefaultTimelineItems = [],\n\t\tvisitorId,\n\t\tonMessageSent,\n\t\tonError,\n\t\tfileOptions,\n\t\trealtimeSend,\n\t\tisRealtimeConnected = false,\n\t} = options;\n\n\tconst sendMessage = useSendMessage({ client });\n\n\tconst {\n\t\thandleInputChange: reportTyping,\n\t\thandleSubmit: stopTyping,\n\t\tstop: forceStopTyping,\n\t} = useVisitorTypingReporter({\n\t\tclient,\n\t\tconversationId,\n\t\trealtimeSend,\n\t\tisRealtimeConnected,\n\t});\n\n\tconst multimodalInput = useMultimodalInput({\n\t\tonSubmit: async ({ message: messageText, files }) => {\n\t\t\t// Stop typing indicator\n\t\t\tstopTyping();\n\n\t\t\t// Send the message\n\t\t\tsendMessage.mutate({\n\t\t\t\tconversationId,\n\t\t\t\tmessage: messageText,\n\t\t\t\tfiles,\n\t\t\t\tdefaultTimelineItems,\n\t\t\t\tvisitorId,\n\t\t\t\tonSuccess: (resultConversationId, messageId) => {\n\t\t\t\t\tonMessageSent?.(resultConversationId, messageId);\n\t\t\t\t},\n\t\t\t\tonError: (err) => {\n\t\t\t\t\tonError?.(err);\n\t\t\t\t},\n\t\t\t});\n\t\t},\n\t\tonError,\n\t\t...fileOptions,\n\t});\n\n\t// Clean up typing indicator on unmount\n\tuseEffect(\n\t\t() => () => {\n\t\t\tforceStopTyping();\n\t\t},\n\t\t[forceStopTyping]\n\t);\n\n\t// Wrap setMessage to also report typing\n\tconst setMessage = useCallback(\n\t\t(value: string) => {\n\t\t\tmultimodalInput.setMessage(value);\n\t\t\treportTyping(value);\n\t\t},\n\t\t[multimodalInput, reportTyping]\n\t);\n\n\t// Combine submission states\n\tconst isSubmitting = multimodalInput.isSubmitting || sendMessage.isPending;\n\tconst isUploading = sendMessage.isUploading;\n\tconst error = multimodalInput.error || sendMessage.error;\n\tconst canSubmit =\n\t\tmultimodalInput.canSubmit && !sendMessage.isPending && !isUploading;\n\n\treturn {\n\t\tmessage: multimodalInput.message,\n\t\tfiles: multimodalInput.files,\n\t\terror,\n\t\tisSubmitting,\n\t\tisUploading,\n\t\tcanSubmit,\n\t\tsetMessage,\n\t\taddFiles: multimodalInput.addFiles,\n\t\tremoveFile: multimodalInput.removeFile,\n\t\tclearFiles: multimodalInput.clearFiles,\n\t\tsubmit: multimodalInput.submit,\n\t\treset: multimodalInput.reset,\n\t};\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAsHA,SAAgB,mBACf,SAC2B;CAC3B,MAAM,EACL,QACA,gBACA,uBAAuB,EAAE,EACzB,WACA,eACA,SACA,aACA,cACA,sBAAsB,UACnB;CAEJ,MAAM,cAAc,eAAe,EAAE,QAAQ,CAAC;CAE9C,MAAM,EACL,mBAAmB,cACnB,cAAc,YACd,MAAM,oBACH,yBAAyB;EAC5B;EACA;EACA;EACA;EACA,CAAC;CAEF,MAAM,kBAAkB,mBAAmB;EAC1C,UAAU,OAAO,EAAE,SAAS,aAAa,YAAY;AAEpD,eAAY;AAGZ,eAAY,OAAO;IAClB;IACA,SAAS;IACT;IACA;IACA;IACA,YAAY,sBAAsB,cAAc;AAC/C,qBAAgB,sBAAsB,UAAU;;IAEjD,UAAU,QAAQ;AACjB,eAAU,IAAI;;IAEf,CAAC;;EAEH;EACA,GAAG;EACH,CAAC;AAGF,uBACa;AACX,mBAAiB;IAElB,CAAC,gBAAgB,CACjB;CAGD,MAAM,aAAa,aACjB,UAAkB;AAClB,kBAAgB,WAAW,MAAM;AACjC,eAAa,MAAM;IAEpB,CAAC,iBAAiB,aAAa,CAC/B;CAGD,MAAM,eAAe,gBAAgB,gBAAgB,YAAY;CACjE,MAAM,cAAc,YAAY;CAChC,MAAM,QAAQ,gBAAgB,SAAS,YAAY;CACnD,MAAM,YACL,gBAAgB,aAAa,CAAC,YAAY,aAAa,CAAC;AAEzD,QAAO;EACN,SAAS,gBAAgB;EACzB,OAAO,gBAAgB;EACvB;EACA;EACA;EACA;EACA;EACA,UAAU,gBAAgB;EAC1B,YAAY,gBAAgB;EAC5B,YAAY,gBAAgB;EAC5B,QAAQ,gBAAgB;EACxB,OAAO,gBAAgB;EACvB"}
@@ -27,6 +27,7 @@ type UseSendMessageResult = {
27
27
  mutate: (options: SendMessageOptions) => void;
28
28
  mutateAsync: (options: SendMessageOptions) => Promise<SendMessageResult | null>;
29
29
  isPending: boolean;
30
+ isUploading: boolean;
30
31
  error: Error | null;
31
32
  reset: () => void;
32
33
  };
@@ -1 +1 @@
1
- {"version":3,"file":"use-send-message.d.ts","names":[],"sources":["../../src/hooks/use-send-message.ts"],"sourcesContent":[],"mappings":";;;;;KAQY,kBAAA;;EAAA,OAAA,EAAA,MAAA;EAGH,KAAA,CAAA,EAAA,IAAA,EAAA;EACe,oBAAA,CAAA,EAAA,YAAA,EAAA;EAQL,SAAA,CAAA,EAAA,MAAA;EAAK;AAGxB;AAOA;;EAGW,SAAA,CAAA,EAAA,MAAA;EACG,SAAA,CAAA,EAAA,CAAA,cAAA,EAAA,MAAA,EAAA,SAAA,EAAA,MAAA,EAAA,GAAA,IAAA;EAAR,OAAA,CAAA,EAAA,CAAA,KAAA,EAda,KAcb,EAAA,GAAA,IAAA;CAEE;AAAK,KAbD,iBAAA,GAaC;EAID,cAAA,EAAA,MAAA;EA6CI,SAAA,EAAA,MAAc;iBA3Dd;yBACQ;;KAGZ,oBAAA;oBACO;yBAER,uBACL,QAAQ;;SAEN;;;KAII,qBAAA;WACF;;;;;;iBA4CM,cAAA,WACN,wBACP"}
1
+ {"version":3,"file":"use-send-message.d.ts","names":[],"sources":["../../src/hooks/use-send-message.ts"],"sourcesContent":[],"mappings":";;;;;KAiBY,kBAAA;;EAAA,OAAA,EAAA,MAAA;EAGH,KAAA,CAAA,EAAA,IAAA,EAAA;EACe,oBAAA,CAAA,EAAA,YAAA,EAAA;EAQL,SAAA,CAAA,EAAA,MAAA;EAAK;AAGxB;AAOA;;EAGW,SAAA,CAAA,EAAA,MAAA;EACG,SAAA,CAAA,EAAA,CAAA,cAAA,EAAA,MAAA,EAAA,SAAA,EAAA,MAAA,EAAA,GAAA,IAAA;EAAR,OAAA,CAAA,EAAA,CAAA,KAAA,EAda,KAcb,EAAA,GAAA,IAAA;CAGE;AAAK,KAdD,iBAAA,GAcC;EAID,cAAA,EAAA,MAAA;EAoHI,SAAA,EAAA,MAAc;iBAnId;yBACQ;;KAGZ,oBAAA;oBACO;yBAER,uBACL,QAAQ;;;SAGN;;;KAII,qBAAA;WACF;;;;;;iBAmHM,cAAA,WACN,wBACP"}
@@ -1,6 +1,6 @@
1
1
  import { useSupport } from "../provider.js";
2
2
  import { useCallback, useState } from "react";
3
- import { generateMessageId } from "@cossistant/core";
3
+ import { generateMessageId, isImageMimeType, validateFiles } from "@cossistant/core";
4
4
 
5
5
  //#region src/hooks/use-send-message.ts
6
6
  function toError(error) {
@@ -8,18 +8,21 @@ function toError(error) {
8
8
  if (typeof error === "string") return new Error(error);
9
9
  return /* @__PURE__ */ new Error("Unknown error");
10
10
  }
11
- function buildTimelineItemPayload(body, conversationId, visitorId, messageId) {
11
+ function buildTimelineItemPayload({ body, conversationId, visitorId, messageId, fileParts }) {
12
12
  const nowIso = typeof window !== "undefined" ? (/* @__PURE__ */ new Date()).toISOString() : "";
13
+ const id = messageId ?? generateMessageId();
14
+ const parts = [{
15
+ type: "text",
16
+ text: body
17
+ }];
18
+ if (fileParts && fileParts.length > 0) parts.push(...fileParts);
13
19
  return {
14
- id: messageId ?? generateMessageId(),
20
+ id,
15
21
  conversationId,
16
22
  organizationId: "",
17
23
  type: "message",
18
24
  text: body,
19
- parts: [{
20
- type: "text",
21
- text: body
22
- }],
25
+ parts,
23
26
  visibility: "public",
24
27
  userId: null,
25
28
  aiAgentId: null,
@@ -29,6 +32,37 @@ function buildTimelineItemPayload(body, conversationId, visitorId, messageId) {
29
32
  };
30
33
  }
31
34
  /**
35
+ * Upload files and return timeline parts for inclusion in a message.
36
+ */
37
+ async function uploadFilesForMessage(client, files, conversationId) {
38
+ if (files.length === 0) return [];
39
+ const validationError = validateFiles(files);
40
+ if (validationError) throw new Error(validationError);
41
+ const uploadPromises = files.map(async (file) => {
42
+ const uploadInfo = await client.generateUploadUrl({
43
+ conversationId,
44
+ contentType: file.type,
45
+ fileName: file.name
46
+ });
47
+ await client.uploadFile(file, uploadInfo.uploadUrl, file.type);
48
+ if (isImageMimeType(file.type)) return {
49
+ type: "image",
50
+ url: uploadInfo.publicUrl,
51
+ mediaType: file.type,
52
+ fileName: file.name,
53
+ size: file.size
54
+ };
55
+ return {
56
+ type: "file",
57
+ url: uploadInfo.publicUrl,
58
+ mediaType: file.type,
59
+ fileName: file.name,
60
+ size: file.size
61
+ };
62
+ });
63
+ return Promise.all(uploadPromises);
64
+ }
65
+ /**
32
66
  * Sends visitor messages while handling optimistic pending conversations and
33
67
  * exposing react-query-like mutation state.
34
68
  */
@@ -36,11 +70,12 @@ function useSendMessage(options = {}) {
36
70
  const { client: contextClient } = useSupport();
37
71
  const client = options.client ?? contextClient;
38
72
  const [isPending, setIsPending] = useState(false);
73
+ const [isUploading, setIsUploading] = useState(false);
39
74
  const [error, setError] = useState(null);
40
75
  const mutateAsync = useCallback(async (payload) => {
41
- const { conversationId: providedConversationId, message, defaultTimelineItems = [], visitorId, messageId: providedMessageId, onSuccess, onError } = payload;
42
- if (!message.trim()) {
43
- const emptyMessageError = /* @__PURE__ */ new Error("Message cannot be empty");
76
+ const { conversationId: providedConversationId, message, files = [], defaultTimelineItems = [], visitorId, messageId: providedMessageId, onSuccess, onError } = payload;
77
+ if (!message.trim() && files.length === 0) {
78
+ const emptyMessageError = /* @__PURE__ */ new Error("Message cannot be empty (or attach files)");
44
79
  setError(emptyMessageError);
45
80
  onError?.(emptyMessageError);
46
81
  return null;
@@ -60,7 +95,22 @@ function useSendMessage(options = {}) {
60
95
  preparedDefaultTimelineItems = initiated.defaultTimelineItems;
61
96
  initialConversation = initiated.conversation;
62
97
  }
63
- const timelineItemPayload = buildTimelineItemPayload(message, conversationId, visitorId ?? null, providedMessageId);
98
+ let fileParts = [];
99
+ if (files.length > 0) {
100
+ setIsUploading(true);
101
+ try {
102
+ fileParts = await uploadFilesForMessage(client, files, conversationId);
103
+ } finally {
104
+ setIsUploading(false);
105
+ }
106
+ }
107
+ const timelineItemPayload = buildTimelineItemPayload({
108
+ body: message,
109
+ conversationId,
110
+ visitorId: visitorId ?? null,
111
+ messageId: providedMessageId,
112
+ fileParts
113
+ });
64
114
  const response = await client.sendMessage({
65
115
  conversationId,
66
116
  item: {
@@ -107,10 +157,12 @@ function useSendMessage(options = {}) {
107
157
  }, [mutateAsync]),
108
158
  mutateAsync,
109
159
  isPending,
160
+ isUploading,
110
161
  error,
111
162
  reset: useCallback(() => {
112
163
  setError(null);
113
164
  setIsPending(false);
165
+ setIsUploading(false);
114
166
  }, [])
115
167
  };
116
168
  }
@@ -1 +1 @@
1
- {"version":3,"file":"use-send-message.js","names":["initialConversation:\n\t\t\t\t\t| CreateConversationResponseBody[\"conversation\"]\n\t\t\t\t\t| undefined","result: SendMessageResult"],"sources":["../../src/hooks/use-send-message.ts"],"sourcesContent":["import type { CossistantClient } from \"@cossistant/core\";\nimport { generateMessageId } from \"@cossistant/core\";\nimport type { CreateConversationResponseBody } from \"@cossistant/types/api/conversation\";\nimport type { TimelineItem } from \"@cossistant/types/api/timeline-item\";\nimport { useCallback, useState } from \"react\";\n\nimport { useSupport } from \"../provider\";\n\nexport type SendMessageOptions = {\n\tconversationId?: string | null;\n\tmessage: string;\n\tfiles?: File[];\n\tdefaultTimelineItems?: TimelineItem[];\n\tvisitorId?: string;\n\t/**\n\t * Optional message ID to use for the optimistic update and API request.\n\t * When not provided, a ULID will be generated on the client.\n\t */\n\tmessageId?: string;\n\tonSuccess?: (conversationId: string, messageId: string) => void;\n\tonError?: (error: Error) => void;\n};\n\nexport type SendMessageResult = {\n\tconversationId: string;\n\tmessageId: string;\n\tconversation?: CreateConversationResponseBody[\"conversation\"];\n\tinitialTimelineItems?: CreateConversationResponseBody[\"initialTimelineItems\"];\n};\n\nexport type UseSendMessageResult = {\n\tmutate: (options: SendMessageOptions) => void;\n\tmutateAsync: (\n\t\toptions: SendMessageOptions\n\t) => Promise<SendMessageResult | null>;\n\tisPending: boolean;\n\terror: Error | null;\n\treset: () => void;\n};\n\nexport type UseSendMessageOptions = {\n\tclient?: CossistantClient;\n};\n\nfunction toError(error: unknown): Error {\n\tif (error instanceof Error) {\n\t\treturn error;\n\t}\n\n\tif (typeof error === \"string\") {\n\t\treturn new Error(error);\n\t}\n\n\treturn new Error(\"Unknown error\");\n}\n\nfunction buildTimelineItemPayload(\n\tbody: string,\n\tconversationId: string,\n\tvisitorId: string | null,\n\tmessageId?: string\n): TimelineItem {\n\tconst nowIso = typeof window !== \"undefined\" ? new Date().toISOString() : \"\";\n\tconst id = messageId ?? generateMessageId();\n\n\treturn {\n\t\tid,\n\t\tconversationId,\n\t\torganizationId: \"\", // Will be set by backend\n\t\ttype: \"message\" as const,\n\t\ttext: body,\n\t\tparts: [{ type: \"text\" as const, text: body }],\n\t\tvisibility: \"public\" as const,\n\t\tuserId: null,\n\t\taiAgentId: null,\n\t\tvisitorId: visitorId ?? null,\n\t\tcreatedAt: nowIso,\n\t\tdeletedAt: null,\n\t} satisfies TimelineItem;\n}\n\n/**\n * Sends visitor messages while handling optimistic pending conversations and\n * exposing react-query-like mutation state.\n */\nexport function useSendMessage(\n\toptions: UseSendMessageOptions = {}\n): UseSendMessageResult {\n\tconst { client: contextClient } = useSupport();\n\tconst client = options.client ?? contextClient;\n\n\tconst [isPending, setIsPending] = useState(false);\n\tconst [error, setError] = useState<Error | null>(null);\n\n\tconst mutateAsync = useCallback(\n\t\tasync (payload: SendMessageOptions): Promise<SendMessageResult | null> => {\n\t\t\tconst {\n\t\t\t\tconversationId: providedConversationId,\n\t\t\t\tmessage,\n\t\t\t\tdefaultTimelineItems = [],\n\t\t\t\tvisitorId,\n\t\t\t\tmessageId: providedMessageId,\n\t\t\t\tonSuccess,\n\t\t\t\tonError,\n\t\t\t} = payload;\n\n\t\t\tif (!message.trim()) {\n\t\t\t\tconst emptyMessageError = new Error(\"Message cannot be empty\");\n\t\t\t\tsetError(emptyMessageError);\n\t\t\t\tonError?.(emptyMessageError);\n\t\t\t\treturn null;\n\t\t\t}\n\n\t\t\tsetIsPending(true);\n\t\t\tsetError(null);\n\n\t\t\ttry {\n\t\t\t\tlet conversationId = providedConversationId ?? undefined;\n\t\t\t\tlet preparedDefaultTimelineItems = defaultTimelineItems;\n\t\t\t\tlet initialConversation:\n\t\t\t\t\t| CreateConversationResponseBody[\"conversation\"]\n\t\t\t\t\t| undefined;\n\n\t\t\t\tif (!conversationId) {\n\t\t\t\t\tconst initiated = client.initiateConversation({\n\t\t\t\t\t\tdefaultTimelineItems,\n\t\t\t\t\t\tvisitorId: visitorId ?? undefined,\n\t\t\t\t\t});\n\t\t\t\t\tconversationId = initiated.conversationId;\n\t\t\t\t\tpreparedDefaultTimelineItems = initiated.defaultTimelineItems;\n\t\t\t\t\tinitialConversation = initiated.conversation;\n\t\t\t\t}\n\n\t\t\t\tconst timelineItemPayload = buildTimelineItemPayload(\n\t\t\t\t\tmessage,\n\t\t\t\t\tconversationId,\n\t\t\t\t\tvisitorId ?? null,\n\t\t\t\t\tprovidedMessageId\n\t\t\t\t);\n\n\t\t\t\tconst response = await client.sendMessage({\n\t\t\t\t\tconversationId,\n\t\t\t\t\titem: {\n\t\t\t\t\t\tid: timelineItemPayload.id,\n\t\t\t\t\t\ttext: timelineItemPayload.text ?? \"\",\n\t\t\t\t\t\ttype:\n\t\t\t\t\t\t\ttimelineItemPayload.type === \"identification\"\n\t\t\t\t\t\t\t\t? \"message\"\n\t\t\t\t\t\t\t\t: timelineItemPayload.type,\n\t\t\t\t\t\tvisibility: timelineItemPayload.visibility,\n\t\t\t\t\t\tuserId: timelineItemPayload.userId,\n\t\t\t\t\t\taiAgentId: timelineItemPayload.aiAgentId,\n\t\t\t\t\t\tvisitorId: timelineItemPayload.visitorId,\n\t\t\t\t\t\tcreatedAt: timelineItemPayload.createdAt,\n\t\t\t\t\t\tparts: timelineItemPayload.parts,\n\t\t\t\t\t},\n\t\t\t\t\tcreateIfPending: true,\n\t\t\t\t});\n\n\t\t\t\tconst messageId = response.item.id;\n\n\t\t\t\tif (!messageId) {\n\t\t\t\t\tthrow new Error(\"SendMessage response missing item.id\");\n\t\t\t\t}\n\n\t\t\t\tconst result: SendMessageResult = {\n\t\t\t\t\tconversationId,\n\t\t\t\t\tmessageId,\n\t\t\t\t};\n\n\t\t\t\tif (\"conversation\" in response && response.conversation) {\n\t\t\t\t\tresult.conversation = response.conversation;\n\t\t\t\t\tresult.initialTimelineItems = response.initialTimelineItems;\n\t\t\t\t} else if (initialConversation) {\n\t\t\t\t\tresult.conversation = initialConversation;\n\t\t\t\t\tresult.initialTimelineItems = preparedDefaultTimelineItems;\n\t\t\t\t}\n\n\t\t\t\tsetIsPending(false);\n\t\t\t\tsetError(null);\n\t\t\t\tonSuccess?.(result.conversationId, result.messageId);\n\t\t\t\treturn result;\n\t\t\t} catch (raw) {\n\t\t\t\tconst normalised = toError(raw);\n\t\t\t\tsetIsPending(false);\n\t\t\t\tsetError(normalised);\n\t\t\t\tonError?.(normalised);\n\t\t\t\tthrow normalised;\n\t\t\t}\n\t\t},\n\t\t[client]\n\t);\n\n\tconst mutate = useCallback(\n\t\t(opts: SendMessageOptions) => {\n\t\t\tvoid mutateAsync(opts).catch(() => {\n\t\t\t\t// Swallow errors to mimic react-query behaviour for mutate\n\t\t\t});\n\t\t},\n\t\t[mutateAsync]\n\t);\n\n\tconst reset = useCallback(() => {\n\t\tsetError(null);\n\t\tsetIsPending(false);\n\t}, []);\n\n\treturn {\n\t\tmutate,\n\t\tmutateAsync,\n\t\tisPending,\n\t\terror,\n\t\treset,\n\t};\n}\n"],"mappings":";;;;;AA4CA,SAAS,QAAQ,OAAuB;AACvC,KAAI,iBAAiB,MACpB,QAAO;AAGR,KAAI,OAAO,UAAU,SACpB,QAAO,IAAI,MAAM,MAAM;AAGxB,wBAAO,IAAI,MAAM,gBAAgB;;AAGlC,SAAS,yBACR,MACA,gBACA,WACA,WACe;CACf,MAAM,SAAS,OAAO,WAAW,+BAAc,IAAI,MAAM,EAAC,aAAa,GAAG;AAG1E,QAAO;EACN,IAHU,aAAa,mBAAmB;EAI1C;EACA,gBAAgB;EAChB,MAAM;EACN,MAAM;EACN,OAAO,CAAC;GAAE,MAAM;GAAiB,MAAM;GAAM,CAAC;EAC9C,YAAY;EACZ,QAAQ;EACR,WAAW;EACX,WAAW,aAAa;EACxB,WAAW;EACX,WAAW;EACX;;;;;;AAOF,SAAgB,eACf,UAAiC,EAAE,EACZ;CACvB,MAAM,EAAE,QAAQ,kBAAkB,YAAY;CAC9C,MAAM,SAAS,QAAQ,UAAU;CAEjC,MAAM,CAAC,WAAW,gBAAgB,SAAS,MAAM;CACjD,MAAM,CAAC,OAAO,YAAY,SAAuB,KAAK;CAEtD,MAAM,cAAc,YACnB,OAAO,YAAmE;EACzE,MAAM,EACL,gBAAgB,wBAChB,SACA,uBAAuB,EAAE,EACzB,WACA,WAAW,mBACX,WACA,YACG;AAEJ,MAAI,CAAC,QAAQ,MAAM,EAAE;GACpB,MAAM,oCAAoB,IAAI,MAAM,0BAA0B;AAC9D,YAAS,kBAAkB;AAC3B,aAAU,kBAAkB;AAC5B,UAAO;;AAGR,eAAa,KAAK;AAClB,WAAS,KAAK;AAEd,MAAI;GACH,IAAI,iBAAiB,0BAA0B;GAC/C,IAAI,+BAA+B;GACnC,IAAIA;AAIJ,OAAI,CAAC,gBAAgB;IACpB,MAAM,YAAY,OAAO,qBAAqB;KAC7C;KACA,WAAW,aAAa;KACxB,CAAC;AACF,qBAAiB,UAAU;AAC3B,mCAA+B,UAAU;AACzC,0BAAsB,UAAU;;GAGjC,MAAM,sBAAsB,yBAC3B,SACA,gBACA,aAAa,MACb,kBACA;GAED,MAAM,WAAW,MAAM,OAAO,YAAY;IACzC;IACA,MAAM;KACL,IAAI,oBAAoB;KACxB,MAAM,oBAAoB,QAAQ;KAClC,MACC,oBAAoB,SAAS,mBAC1B,YACA,oBAAoB;KACxB,YAAY,oBAAoB;KAChC,QAAQ,oBAAoB;KAC5B,WAAW,oBAAoB;KAC/B,WAAW,oBAAoB;KAC/B,WAAW,oBAAoB;KAC/B,OAAO,oBAAoB;KAC3B;IACD,iBAAiB;IACjB,CAAC;GAEF,MAAM,YAAY,SAAS,KAAK;AAEhC,OAAI,CAAC,UACJ,OAAM,IAAI,MAAM,uCAAuC;GAGxD,MAAMC,SAA4B;IACjC;IACA;IACA;AAED,OAAI,kBAAkB,YAAY,SAAS,cAAc;AACxD,WAAO,eAAe,SAAS;AAC/B,WAAO,uBAAuB,SAAS;cAC7B,qBAAqB;AAC/B,WAAO,eAAe;AACtB,WAAO,uBAAuB;;AAG/B,gBAAa,MAAM;AACnB,YAAS,KAAK;AACd,eAAY,OAAO,gBAAgB,OAAO,UAAU;AACpD,UAAO;WACC,KAAK;GACb,MAAM,aAAa,QAAQ,IAAI;AAC/B,gBAAa,MAAM;AACnB,YAAS,WAAW;AACpB,aAAU,WAAW;AACrB,SAAM;;IAGR,CAAC,OAAO,CACR;AAgBD,QAAO;EACN,QAfc,aACb,SAA6B;AAC7B,GAAK,YAAY,KAAK,CAAC,YAAY,GAEjC;KAEH,CAAC,YAAY,CACb;EASA;EACA;EACA;EACA,OAVa,kBAAkB;AAC/B,YAAS,KAAK;AACd,gBAAa,MAAM;KACjB,EAAE,CAAC;EAQL"}
1
+ {"version":3,"file":"use-send-message.js","names":["parts: TimelineItemParts","initialConversation:\n\t\t\t\t\t| CreateConversationResponseBody[\"conversation\"]\n\t\t\t\t\t| undefined","fileParts: Array<TimelinePartImage | TimelinePartFile>","result: SendMessageResult"],"sources":["../../src/hooks/use-send-message.ts"],"sourcesContent":["import type { CossistantClient } from \"@cossistant/core\";\nimport {\n\tgenerateMessageId,\n\tisImageMimeType,\n\tvalidateFiles,\n} from \"@cossistant/core\";\nimport type { CreateConversationResponseBody } from \"@cossistant/types/api/conversation\";\nimport type {\n\tTimelineItem,\n\tTimelineItemParts,\n\tTimelinePartFile,\n\tTimelinePartImage,\n} from \"@cossistant/types/api/timeline-item\";\nimport { useCallback, useState } from \"react\";\n\nimport { useSupport } from \"../provider\";\n\nexport type SendMessageOptions = {\n\tconversationId?: string | null;\n\tmessage: string;\n\tfiles?: File[];\n\tdefaultTimelineItems?: TimelineItem[];\n\tvisitorId?: string;\n\t/**\n\t * Optional message ID to use for the optimistic update and API request.\n\t * When not provided, a ULID will be generated on the client.\n\t */\n\tmessageId?: string;\n\tonSuccess?: (conversationId: string, messageId: string) => void;\n\tonError?: (error: Error) => void;\n};\n\nexport type SendMessageResult = {\n\tconversationId: string;\n\tmessageId: string;\n\tconversation?: CreateConversationResponseBody[\"conversation\"];\n\tinitialTimelineItems?: CreateConversationResponseBody[\"initialTimelineItems\"];\n};\n\nexport type UseSendMessageResult = {\n\tmutate: (options: SendMessageOptions) => void;\n\tmutateAsync: (\n\t\toptions: SendMessageOptions\n\t) => Promise<SendMessageResult | null>;\n\tisPending: boolean;\n\tisUploading: boolean;\n\terror: Error | null;\n\treset: () => void;\n};\n\nexport type UseSendMessageOptions = {\n\tclient?: CossistantClient;\n};\n\nfunction toError(error: unknown): Error {\n\tif (error instanceof Error) {\n\t\treturn error;\n\t}\n\n\tif (typeof error === \"string\") {\n\t\treturn new Error(error);\n\t}\n\n\treturn new Error(\"Unknown error\");\n}\n\ntype BuildTimelineItemPayloadOptions = {\n\tbody: string;\n\tconversationId: string;\n\tvisitorId: string | null;\n\tmessageId?: string;\n\tfileParts?: Array<TimelinePartImage | TimelinePartFile>;\n};\n\nfunction buildTimelineItemPayload({\n\tbody,\n\tconversationId,\n\tvisitorId,\n\tmessageId,\n\tfileParts,\n}: BuildTimelineItemPayloadOptions): TimelineItem {\n\tconst nowIso = typeof window !== \"undefined\" ? new Date().toISOString() : \"\";\n\tconst id = messageId ?? generateMessageId();\n\n\t// Build parts array: text first, then any file/image parts\n\tconst parts: TimelineItemParts = [{ type: \"text\" as const, text: body }];\n\n\tif (fileParts && fileParts.length > 0) {\n\t\tparts.push(...fileParts);\n\t}\n\n\treturn {\n\t\tid,\n\t\tconversationId,\n\t\torganizationId: \"\", // Will be set by backend\n\t\ttype: \"message\" as const,\n\t\ttext: body,\n\t\tparts,\n\t\tvisibility: \"public\" as const,\n\t\tuserId: null,\n\t\taiAgentId: null,\n\t\tvisitorId: visitorId ?? null,\n\t\tcreatedAt: nowIso,\n\t\tdeletedAt: null,\n\t} satisfies TimelineItem;\n}\n\n/**\n * Upload files and return timeline parts for inclusion in a message.\n */\nasync function uploadFilesForMessage(\n\tclient: CossistantClient,\n\tfiles: File[],\n\tconversationId: string\n): Promise<Array<TimelinePartImage | TimelinePartFile>> {\n\tif (files.length === 0) {\n\t\treturn [];\n\t}\n\n\t// Validate files first\n\tconst validationError = validateFiles(files);\n\tif (validationError) {\n\t\tthrow new Error(validationError);\n\t}\n\n\t// Upload files in parallel\n\tconst uploadPromises = files.map(async (file) => {\n\t\t// Generate presigned URL\n\t\tconst uploadInfo = await client.generateUploadUrl({\n\t\t\tconversationId,\n\t\t\tcontentType: file.type,\n\t\t\tfileName: file.name,\n\t\t});\n\n\t\t// Upload file to S3\n\t\tawait client.uploadFile(file, uploadInfo.uploadUrl, file.type);\n\n\t\t// Return timeline part based on file type\n\t\tconst isImage = isImageMimeType(file.type);\n\n\t\tif (isImage) {\n\t\t\treturn {\n\t\t\t\ttype: \"image\" as const,\n\t\t\t\turl: uploadInfo.publicUrl,\n\t\t\t\tmediaType: file.type,\n\t\t\t\tfileName: file.name,\n\t\t\t\tsize: file.size,\n\t\t\t} satisfies TimelinePartImage;\n\t\t}\n\n\t\treturn {\n\t\t\ttype: \"file\" as const,\n\t\t\turl: uploadInfo.publicUrl,\n\t\t\tmediaType: file.type,\n\t\t\tfileName: file.name,\n\t\t\tsize: file.size,\n\t\t} satisfies TimelinePartFile;\n\t});\n\n\treturn Promise.all(uploadPromises);\n}\n\n/**\n * Sends visitor messages while handling optimistic pending conversations and\n * exposing react-query-like mutation state.\n */\nexport function useSendMessage(\n\toptions: UseSendMessageOptions = {}\n): UseSendMessageResult {\n\tconst { client: contextClient } = useSupport();\n\tconst client = options.client ?? contextClient;\n\n\tconst [isPending, setIsPending] = useState(false);\n\tconst [isUploading, setIsUploading] = useState(false);\n\tconst [error, setError] = useState<Error | null>(null);\n\n\tconst mutateAsync = useCallback(\n\t\tasync (payload: SendMessageOptions): Promise<SendMessageResult | null> => {\n\t\t\tconst {\n\t\t\t\tconversationId: providedConversationId,\n\t\t\t\tmessage,\n\t\t\t\tfiles = [],\n\t\t\t\tdefaultTimelineItems = [],\n\t\t\t\tvisitorId,\n\t\t\t\tmessageId: providedMessageId,\n\t\t\t\tonSuccess,\n\t\t\t\tonError,\n\t\t\t} = payload;\n\n\t\t\t// Allow empty message if there are files\n\t\t\tif (!message.trim() && files.length === 0) {\n\t\t\t\tconst emptyMessageError = new Error(\n\t\t\t\t\t\"Message cannot be empty (or attach files)\"\n\t\t\t\t);\n\t\t\t\tsetError(emptyMessageError);\n\t\t\t\tonError?.(emptyMessageError);\n\t\t\t\treturn null;\n\t\t\t}\n\n\t\t\tsetIsPending(true);\n\t\t\tsetError(null);\n\n\t\t\ttry {\n\t\t\t\tlet conversationId = providedConversationId ?? undefined;\n\t\t\t\tlet preparedDefaultTimelineItems = defaultTimelineItems;\n\t\t\t\tlet initialConversation:\n\t\t\t\t\t| CreateConversationResponseBody[\"conversation\"]\n\t\t\t\t\t| undefined;\n\n\t\t\t\tif (!conversationId) {\n\t\t\t\t\tconst initiated = client.initiateConversation({\n\t\t\t\t\t\tdefaultTimelineItems,\n\t\t\t\t\t\tvisitorId: visitorId ?? undefined,\n\t\t\t\t\t});\n\t\t\t\t\tconversationId = initiated.conversationId;\n\t\t\t\t\tpreparedDefaultTimelineItems = initiated.defaultTimelineItems;\n\t\t\t\t\tinitialConversation = initiated.conversation;\n\t\t\t\t}\n\n\t\t\t\t// Upload files BEFORE sending the message\n\t\t\t\tlet fileParts: Array<TimelinePartImage | TimelinePartFile> = [];\n\t\t\t\tif (files.length > 0) {\n\t\t\t\t\tsetIsUploading(true);\n\t\t\t\t\ttry {\n\t\t\t\t\t\tfileParts = await uploadFilesForMessage(\n\t\t\t\t\t\t\tclient,\n\t\t\t\t\t\t\tfiles,\n\t\t\t\t\t\t\tconversationId\n\t\t\t\t\t\t);\n\t\t\t\t\t} finally {\n\t\t\t\t\t\tsetIsUploading(false);\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tconst timelineItemPayload = buildTimelineItemPayload({\n\t\t\t\t\tbody: message,\n\t\t\t\t\tconversationId,\n\t\t\t\t\tvisitorId: visitorId ?? null,\n\t\t\t\t\tmessageId: providedMessageId,\n\t\t\t\t\tfileParts,\n\t\t\t\t});\n\n\t\t\t\tconst response = await client.sendMessage({\n\t\t\t\t\tconversationId,\n\t\t\t\t\titem: {\n\t\t\t\t\t\tid: timelineItemPayload.id,\n\t\t\t\t\t\ttext: timelineItemPayload.text ?? \"\",\n\t\t\t\t\t\ttype:\n\t\t\t\t\t\t\ttimelineItemPayload.type === \"identification\"\n\t\t\t\t\t\t\t\t? \"message\"\n\t\t\t\t\t\t\t\t: timelineItemPayload.type,\n\t\t\t\t\t\tvisibility: timelineItemPayload.visibility,\n\t\t\t\t\t\tuserId: timelineItemPayload.userId,\n\t\t\t\t\t\taiAgentId: timelineItemPayload.aiAgentId,\n\t\t\t\t\t\tvisitorId: timelineItemPayload.visitorId,\n\t\t\t\t\t\tcreatedAt: timelineItemPayload.createdAt,\n\t\t\t\t\t\tparts: timelineItemPayload.parts,\n\t\t\t\t\t},\n\t\t\t\t\tcreateIfPending: true,\n\t\t\t\t});\n\n\t\t\t\tconst messageId = response.item.id;\n\n\t\t\t\tif (!messageId) {\n\t\t\t\t\tthrow new Error(\"SendMessage response missing item.id\");\n\t\t\t\t}\n\n\t\t\t\tconst result: SendMessageResult = {\n\t\t\t\t\tconversationId,\n\t\t\t\t\tmessageId,\n\t\t\t\t};\n\n\t\t\t\tif (\"conversation\" in response && response.conversation) {\n\t\t\t\t\tresult.conversation = response.conversation;\n\t\t\t\t\tresult.initialTimelineItems = response.initialTimelineItems;\n\t\t\t\t} else if (initialConversation) {\n\t\t\t\t\tresult.conversation = initialConversation;\n\t\t\t\t\tresult.initialTimelineItems = preparedDefaultTimelineItems;\n\t\t\t\t}\n\n\t\t\t\tsetIsPending(false);\n\t\t\t\tsetError(null);\n\t\t\t\tonSuccess?.(result.conversationId, result.messageId);\n\t\t\t\treturn result;\n\t\t\t} catch (raw) {\n\t\t\t\tconst normalised = toError(raw);\n\t\t\t\tsetIsPending(false);\n\t\t\t\tsetError(normalised);\n\t\t\t\tonError?.(normalised);\n\t\t\t\tthrow normalised;\n\t\t\t}\n\t\t},\n\t\t[client]\n\t);\n\n\tconst mutate = useCallback(\n\t\t(opts: SendMessageOptions) => {\n\t\t\tvoid mutateAsync(opts).catch(() => {\n\t\t\t\t// Swallow errors to mimic react-query behaviour for mutate\n\t\t\t});\n\t\t},\n\t\t[mutateAsync]\n\t);\n\n\tconst reset = useCallback(() => {\n\t\tsetError(null);\n\t\tsetIsPending(false);\n\t\tsetIsUploading(false);\n\t}, []);\n\n\treturn {\n\t\tmutate,\n\t\tmutateAsync,\n\t\tisPending,\n\t\tisUploading,\n\t\terror,\n\t\treset,\n\t};\n}\n"],"mappings":";;;;;AAsDA,SAAS,QAAQ,OAAuB;AACvC,KAAI,iBAAiB,MACpB,QAAO;AAGR,KAAI,OAAO,UAAU,SACpB,QAAO,IAAI,MAAM,MAAM;AAGxB,wBAAO,IAAI,MAAM,gBAAgB;;AAWlC,SAAS,yBAAyB,EACjC,MACA,gBACA,WACA,WACA,aACiD;CACjD,MAAM,SAAS,OAAO,WAAW,+BAAc,IAAI,MAAM,EAAC,aAAa,GAAG;CAC1E,MAAM,KAAK,aAAa,mBAAmB;CAG3C,MAAMA,QAA2B,CAAC;EAAE,MAAM;EAAiB,MAAM;EAAM,CAAC;AAExE,KAAI,aAAa,UAAU,SAAS,EACnC,OAAM,KAAK,GAAG,UAAU;AAGzB,QAAO;EACN;EACA;EACA,gBAAgB;EAChB,MAAM;EACN,MAAM;EACN;EACA,YAAY;EACZ,QAAQ;EACR,WAAW;EACX,WAAW,aAAa;EACxB,WAAW;EACX,WAAW;EACX;;;;;AAMF,eAAe,sBACd,QACA,OACA,gBACuD;AACvD,KAAI,MAAM,WAAW,EACpB,QAAO,EAAE;CAIV,MAAM,kBAAkB,cAAc,MAAM;AAC5C,KAAI,gBACH,OAAM,IAAI,MAAM,gBAAgB;CAIjC,MAAM,iBAAiB,MAAM,IAAI,OAAO,SAAS;EAEhD,MAAM,aAAa,MAAM,OAAO,kBAAkB;GACjD;GACA,aAAa,KAAK;GAClB,UAAU,KAAK;GACf,CAAC;AAGF,QAAM,OAAO,WAAW,MAAM,WAAW,WAAW,KAAK,KAAK;AAK9D,MAFgB,gBAAgB,KAAK,KAAK,CAGzC,QAAO;GACN,MAAM;GACN,KAAK,WAAW;GAChB,WAAW,KAAK;GAChB,UAAU,KAAK;GACf,MAAM,KAAK;GACX;AAGF,SAAO;GACN,MAAM;GACN,KAAK,WAAW;GAChB,WAAW,KAAK;GAChB,UAAU,KAAK;GACf,MAAM,KAAK;GACX;GACA;AAEF,QAAO,QAAQ,IAAI,eAAe;;;;;;AAOnC,SAAgB,eACf,UAAiC,EAAE,EACZ;CACvB,MAAM,EAAE,QAAQ,kBAAkB,YAAY;CAC9C,MAAM,SAAS,QAAQ,UAAU;CAEjC,MAAM,CAAC,WAAW,gBAAgB,SAAS,MAAM;CACjD,MAAM,CAAC,aAAa,kBAAkB,SAAS,MAAM;CACrD,MAAM,CAAC,OAAO,YAAY,SAAuB,KAAK;CAEtD,MAAM,cAAc,YACnB,OAAO,YAAmE;EACzE,MAAM,EACL,gBAAgB,wBAChB,SACA,QAAQ,EAAE,EACV,uBAAuB,EAAE,EACzB,WACA,WAAW,mBACX,WACA,YACG;AAGJ,MAAI,CAAC,QAAQ,MAAM,IAAI,MAAM,WAAW,GAAG;GAC1C,MAAM,oCAAoB,IAAI,MAC7B,4CACA;AACD,YAAS,kBAAkB;AAC3B,aAAU,kBAAkB;AAC5B,UAAO;;AAGR,eAAa,KAAK;AAClB,WAAS,KAAK;AAEd,MAAI;GACH,IAAI,iBAAiB,0BAA0B;GAC/C,IAAI,+BAA+B;GACnC,IAAIC;AAIJ,OAAI,CAAC,gBAAgB;IACpB,MAAM,YAAY,OAAO,qBAAqB;KAC7C;KACA,WAAW,aAAa;KACxB,CAAC;AACF,qBAAiB,UAAU;AAC3B,mCAA+B,UAAU;AACzC,0BAAsB,UAAU;;GAIjC,IAAIC,YAAyD,EAAE;AAC/D,OAAI,MAAM,SAAS,GAAG;AACrB,mBAAe,KAAK;AACpB,QAAI;AACH,iBAAY,MAAM,sBACjB,QACA,OACA,eACA;cACQ;AACT,oBAAe,MAAM;;;GAIvB,MAAM,sBAAsB,yBAAyB;IACpD,MAAM;IACN;IACA,WAAW,aAAa;IACxB,WAAW;IACX;IACA,CAAC;GAEF,MAAM,WAAW,MAAM,OAAO,YAAY;IACzC;IACA,MAAM;KACL,IAAI,oBAAoB;KACxB,MAAM,oBAAoB,QAAQ;KAClC,MACC,oBAAoB,SAAS,mBAC1B,YACA,oBAAoB;KACxB,YAAY,oBAAoB;KAChC,QAAQ,oBAAoB;KAC5B,WAAW,oBAAoB;KAC/B,WAAW,oBAAoB;KAC/B,WAAW,oBAAoB;KAC/B,OAAO,oBAAoB;KAC3B;IACD,iBAAiB;IACjB,CAAC;GAEF,MAAM,YAAY,SAAS,KAAK;AAEhC,OAAI,CAAC,UACJ,OAAM,IAAI,MAAM,uCAAuC;GAGxD,MAAMC,SAA4B;IACjC;IACA;IACA;AAED,OAAI,kBAAkB,YAAY,SAAS,cAAc;AACxD,WAAO,eAAe,SAAS;AAC/B,WAAO,uBAAuB,SAAS;cAC7B,qBAAqB;AAC/B,WAAO,eAAe;AACtB,WAAO,uBAAuB;;AAG/B,gBAAa,MAAM;AACnB,YAAS,KAAK;AACd,eAAY,OAAO,gBAAgB,OAAO,UAAU;AACpD,UAAO;WACC,KAAK;GACb,MAAM,aAAa,QAAQ,IAAI;AAC/B,gBAAa,MAAM;AACnB,YAAS,WAAW;AACpB,aAAU,WAAW;AACrB,SAAM;;IAGR,CAAC,OAAO,CACR;AAiBD,QAAO;EACN,QAhBc,aACb,SAA6B;AAC7B,GAAK,YAAY,KAAK,CAAC,YAAY,GAEjC;KAEH,CAAC,YAAY,CACb;EAUA;EACA;EACA;EACA;EACA,OAZa,kBAAkB;AAC/B,YAAS,KAAK;AACd,gBAAa,MAAM;AACnB,kBAAe,MAAM;KACnB,EAAE,CAAC;EASL"}
package/index.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import { useClientQuery } from "./hooks/private/use-client-query.js";
2
2
  import { useDefaultMessages } from "./hooks/private/use-default-messages.js";
3
- import { ConversationItem, GroupedMessage, TimelineEventItem, TimelineToolItem, UseGroupedMessagesOptions, UseGroupedMessagesProps, useGroupedMessages } from "./hooks/private/use-grouped-messages.js";
3
+ import { ConversationItem, DaySeparatorItem, GroupedMessage, TimelineEventItem, TimelineToolItem, UseGroupedMessagesOptions, UseGroupedMessagesProps, useGroupedMessages } from "./hooks/private/use-grouped-messages.js";
4
4
  import { UseMultimodalInputOptions, UseMultimodalInputReturn, useMultimodalInput } from "./hooks/private/use-multimodal-input.js";
5
5
  import { UseClientResult, useClient } from "./hooks/private/use-rest-client.js";
6
6
  import { UseComposerRefocusOptions, UseComposerRefocusReturn, useComposerRefocus } from "./hooks/use-composer-refocus.js";
@@ -17,6 +17,7 @@ import { ConversationTypingParticipant, useConversationTyping } from "./hooks/us
17
17
  import { ConversationTimelineTypingParticipant, UseConversationTimelineOptions, UseConversationTimelineReturn, useConversationTimeline } from "./hooks/use-conversation-timeline.js";
18
18
  import { UseConversationsOptions, UseConversationsResult, useConversations } from "./hooks/use-conversations.js";
19
19
  import { CreateConversationVariables, UseCreateConversationOptions, UseCreateConversationResult, useCreateConversation } from "./hooks/use-create-conversation.js";
20
+ import { FileUploadPart, UseFileUploadOptions, UseFileUploadReturn, useFileUpload } from "./hooks/use-file-upload.js";
20
21
  import { UseHomePageOptions, UseHomePageReturn, useHomePage } from "./hooks/use-home-page.js";
21
22
  import { UseMessageComposerOptions, UseMessageComposerReturn, useMessageComposer } from "./hooks/use-message-composer.js";
22
23
  import { useNewMessageSound } from "./hooks/use-new-message-sound.js";
@@ -39,11 +40,13 @@ import { applyConversationTypingEvent, clearTypingFromTimelineItem, clearTypingS
39
40
  import { RealtimeEventHandler, RealtimeEventHandlerEntry, RealtimeEventHandlersMap, RealtimeEventMeta, useRealtime } from "./realtime/use-realtime.js";
40
41
  import "./realtime/index.js";
41
42
  import { CustomPage } from "./support/router.js";
43
+ import { ConversationEndEvent, ConversationStartEvent, ErrorEvent, MessageReceivedEvent, MessageSentEvent, SupportEvent, SupportEventCallbacks, SupportEventType, useSupportEventEmitter, useSupportEvents } from "./support/context/events.js";
44
+ import { SupportHandle, useSupportHandle } from "./support/context/handle.js";
42
45
  import { Text, useSupportText } from "./support/text/index.js";
43
- import { BubbleSlotProps, ContainerSlotProps, RouterSlotProps } from "./support/types.js";
46
+ import { Align, CollisionPadding, ContentProps, RootProps, Side, TriggerRenderProps } from "./support/types.js";
44
47
  import { CoButton } from "./support/components/button.js";
45
48
  import { Header } from "./support/components/header.js";
46
49
  import { WebSocketContextValue, WebSocketProvider, useWebSocket } from "./support/context/websocket.js";
47
50
  import { useSupportConfig, useSupportNavigation, useSupportStore } from "./support/store/support-store.js";
48
- import { DefaultRoutes, NavigationState, RouteRegistry, Support, SupportPage, SupportProps } from "./support/index.js";
49
- export { BubbleSlotProps, CoButton as Button, CONVERSATION_AUTO_SEEN_DELAY_MS, ContainerSlotProps, ConversationItem, ConversationLifecycleState, ConversationPreviewAssignedAgent, ConversationPreviewLastMessage, ConversationPreviewTypingParticipant, ConversationPreviewTypingState, ConversationTimelineTypingParticipant, ConversationTypingParticipant, CossistantContextValue, CossistantProviderProps, CreateConversationVariables, CustomPage, DefaultRoutes, GroupedMessage, Header, IdentifySupportVisitor, IdentifySupportVisitorProps, NavigationState, index_d_exports as Primitives, RealtimeAuthConfig, RealtimeContextValue, RealtimeEventHandler, RealtimeEventHandlerEntry, RealtimeEventHandlersMap, RealtimeEventMeta, RealtimeProvider, RealtimeProviderProps, RouteRegistry, RouterSlotProps, SendMessageOptions, SendMessageResult, Support, SupportConfig, SupportConfigProps, SupportContext, SupportLocale, SupportPage, SupportProps, SupportProvider, SupportProviderProps, SupportRealtimeProvider, SupportTextContentOverrides, Text, TimelineEventItem, TimelineToolItem, UseClientResult, UseComposerRefocusOptions, UseComposerRefocusReturn, UseConversationAutoSeenOptions, UseConversationHistoryPageOptions, UseConversationHistoryPageReturn, UseConversationLifecycleOptions, UseConversationLifecycleReturn, UseConversationOptions, UseConversationPageOptions, UseConversationPageReturn, UseConversationPreviewOptions, UseConversationPreviewReturn, UseConversationResult, UseConversationTimelineItemsOptions, UseConversationTimelineItemsResult, UseConversationTimelineOptions, UseConversationTimelineReturn, UseConversationsOptions, UseConversationsResult, UseCreateConversationOptions, UseCreateConversationResult, UseGroupedMessagesOptions, UseGroupedMessagesProps, UseHomePageOptions, UseHomePageReturn, UseMessageComposerOptions, UseMessageComposerReturn, UseMultimodalInputOptions, UseMultimodalInputReturn, UseRealtimeSupportOptions, UseRealtimeSupportResult, UseScrollMaskOptions, UseScrollMaskReturn, UseSendMessageOptions, UseSendMessageResult, UseSoundEffectOptions, UseSoundEffectReturn, UseSupportValue, UseVisitorReturn, WebSocketContextValue, WebSocketProvider, WindowVisibilityFocusState, applyConversationSeenEvent, applyConversationTypingEvent, clearTypingFromTimelineItem, clearTypingState, hydrateConversationSeen, setTypingState, upsertConversationSeen, useClient, useClientQuery, useComposerRefocus, useConversation, useConversationAutoSeen, useConversationHistoryPage, useConversationLifecycle, useConversationPage, useConversationPreview, useConversationSeen, useConversationTimeline, useConversationTimelineItems, useConversationTyping, useConversations, useCreateConversation, useDebouncedConversationSeen, useDefaultMessages, useGroupedMessages, useHomePage, useMessageComposer, useMultimodalInput, useNewMessageSound, useRealtime, useRealtimeConnection, useRealtimeSupport, useScrollMask, useSendMessage, useSoundEffect, useSupport, useSupportConfig, useSupportNavigation, useSupportStore, useSupportText, useTypingSound, useVisitor, useWebSocket, useWindowVisibilityFocus };
51
+ import { DefaultRoutes, NavigationState, RouteRegistry, Support, SupportContentProps, SupportPageProps, SupportPageType, SupportProps, SupportRootProps, SupportRouterProps, SupportTriggerProps } from "./support/index.js";
52
+ export { Align, CoButton as Button, CONVERSATION_AUTO_SEEN_DELAY_MS, CollisionPadding, ContentProps, ConversationEndEvent, ConversationItem, ConversationLifecycleState, ConversationPreviewAssignedAgent, ConversationPreviewLastMessage, ConversationPreviewTypingParticipant, ConversationPreviewTypingState, ConversationStartEvent, ConversationTimelineTypingParticipant, ConversationTypingParticipant, CossistantContextValue, CossistantProviderProps, CreateConversationVariables, CustomPage, DaySeparatorItem, DefaultRoutes, ErrorEvent, FileUploadPart, GroupedMessage, Header, IdentifySupportVisitor, IdentifySupportVisitorProps, MessageReceivedEvent, MessageSentEvent, NavigationState, index_d_exports as Primitives, RealtimeAuthConfig, RealtimeContextValue, RealtimeEventHandler, RealtimeEventHandlerEntry, RealtimeEventHandlersMap, RealtimeEventMeta, RealtimeProvider, RealtimeProviderProps, RootProps, RouteRegistry, SendMessageOptions, SendMessageResult, Side, Support, SupportConfig, SupportConfigProps, SupportContentProps, SupportContext, SupportEvent, SupportEventCallbacks, SupportEventType, SupportHandle, SupportLocale, SupportPageProps, SupportPageType, SupportProps, SupportProvider, SupportProviderProps, SupportRealtimeProvider, SupportRootProps, SupportRouterProps, SupportTextContentOverrides, SupportTriggerProps, Text, TimelineEventItem, TimelineToolItem, TriggerRenderProps, UseClientResult, UseComposerRefocusOptions, UseComposerRefocusReturn, UseConversationAutoSeenOptions, UseConversationHistoryPageOptions, UseConversationHistoryPageReturn, UseConversationLifecycleOptions, UseConversationLifecycleReturn, UseConversationOptions, UseConversationPageOptions, UseConversationPageReturn, UseConversationPreviewOptions, UseConversationPreviewReturn, UseConversationResult, UseConversationTimelineItemsOptions, UseConversationTimelineItemsResult, UseConversationTimelineOptions, UseConversationTimelineReturn, UseConversationsOptions, UseConversationsResult, UseCreateConversationOptions, UseCreateConversationResult, UseFileUploadOptions, UseFileUploadReturn, UseGroupedMessagesOptions, UseGroupedMessagesProps, UseHomePageOptions, UseHomePageReturn, UseMessageComposerOptions, UseMessageComposerReturn, UseMultimodalInputOptions, UseMultimodalInputReturn, UseRealtimeSupportOptions, UseRealtimeSupportResult, UseScrollMaskOptions, UseScrollMaskReturn, UseSendMessageOptions, UseSendMessageResult, UseSoundEffectOptions, UseSoundEffectReturn, UseSupportValue, UseVisitorReturn, WebSocketContextValue, WebSocketProvider, WindowVisibilityFocusState, applyConversationSeenEvent, applyConversationTypingEvent, clearTypingFromTimelineItem, clearTypingState, hydrateConversationSeen, setTypingState, upsertConversationSeen, useClient, useClientQuery, useComposerRefocus, useConversation, useConversationAutoSeen, useConversationHistoryPage, useConversationLifecycle, useConversationPage, useConversationPreview, useConversationSeen, useConversationTimeline, useConversationTimelineItems, useConversationTyping, useConversations, useCreateConversation, useDebouncedConversationSeen, useDefaultMessages, useFileUpload, useGroupedMessages, useHomePage, useMessageComposer, useMultimodalInput, useNewMessageSound, useRealtime, useRealtimeConnection, useRealtimeSupport, useScrollMask, useSendMessage, useSoundEffect, useSupport, useSupportConfig, useSupportEventEmitter, useSupportEvents, useSupportHandle, useSupportNavigation, useSupportStore, useSupportText, useTypingSound, useVisitor, useWebSocket, useWindowVisibilityFocus };
package/index.js CHANGED
@@ -1,18 +1,23 @@
1
1
  import { useClientQuery } from "./hooks/private/use-client-query.js";
2
2
  import { useClient } from "./hooks/private/use-rest-client.js";
3
3
  import { applyConversationSeenEvent, hydrateConversationSeen, upsertConversationSeen } from "./realtime/seen-store.js";
4
- import { RealtimeProvider, useRealtimeConnection } from "./realtime/provider.js";
5
- import { applyConversationTypingEvent, clearTypingFromTimelineItem, clearTypingState, setTypingState } from "./realtime/typing-store.js";
6
- import { useRealtime } from "./realtime/use-realtime.js";
7
- import { SupportRealtimeProvider } from "./realtime/support-provider.js";
8
4
  import { SupportConfig } from "./support-config.js";
9
5
  import { useScrollMask } from "./hooks/use-scroll-mask.js";
6
+ import { applyConversationTypingEvent, clearTypingFromTimelineItem, clearTypingState, setTypingState } from "./realtime/typing-store.js";
10
7
  import { useSupportConfig, useSupportNavigation, useSupportStore } from "./support/store/support-store.js";
11
8
  import { primitives_exports } from "./primitives/index.js";
9
+ import { RealtimeProvider, useRealtimeConnection } from "./realtime/provider.js";
10
+ import { useRealtime } from "./realtime/use-realtime.js";
11
+ import { SupportRealtimeProvider } from "./realtime/support-provider.js";
12
+ import { useSoundEffect } from "./hooks/use-sound-effect.js";
13
+ import { useNewMessageSound } from "./hooks/use-new-message-sound.js";
14
+ import { useTypingSound } from "./hooks/use-typing-sound.js";
15
+ import { useSupportEventEmitter, useSupportEvents } from "./support/context/events.js";
16
+ import { useSupportHandle } from "./support/context/handle.js";
12
17
  import { CoButton } from "./support/components/button.js";
13
18
  import { Header } from "./support/components/header.js";
14
19
  import { Text, useSupportText } from "./support/text/index.js";
15
- import { useConversation } from "./hooks/use-conversation.js";
20
+ import { WebSocketProvider, useWebSocket } from "./support/context/websocket.js";
16
21
  import { useWindowVisibilityFocus } from "./hooks/use-window-visibility-focus.js";
17
22
  import { CONVERSATION_AUTO_SEEN_DELAY_MS, useConversationAutoSeen } from "./hooks/use-conversation-auto-seen.js";
18
23
  import { useConversationLifecycle } from "./hooks/use-conversation-lifecycle.js";
@@ -21,25 +26,23 @@ import { useMultimodalInput } from "./hooks/private/use-multimodal-input.js";
21
26
  import { useSendMessage } from "./hooks/use-send-message.js";
22
27
  import { useMessageComposer } from "./hooks/use-message-composer.js";
23
28
  import { useConversationPage } from "./hooks/use-conversation-page.js";
24
- import { useSoundEffect } from "./hooks/use-sound-effect.js";
25
- import { useNewMessageSound } from "./hooks/use-new-message-sound.js";
26
29
  import { useGroupedMessages } from "./hooks/private/use-grouped-messages.js";
27
30
  import { useConversationSeen, useDebouncedConversationSeen } from "./hooks/use-conversation-seen.js";
28
31
  import { useConversationTyping } from "./hooks/use-conversation-typing.js";
29
32
  import { useConversationTimeline } from "./hooks/use-conversation-timeline.js";
30
- import { useTypingSound } from "./hooks/use-typing-sound.js";
31
33
  import { useComposerRefocus } from "./hooks/use-composer-refocus.js";
32
34
  import { useVisitor } from "./hooks/use-visitor.js";
33
35
  import { useConversations } from "./hooks/use-conversations.js";
34
36
  import { useConversationHistoryPage } from "./hooks/use-conversation-history-page.js";
35
37
  import { useConversationPreview } from "./hooks/use-conversation-preview.js";
36
38
  import { useHomePage } from "./hooks/use-home-page.js";
37
- import { WebSocketProvider, useWebSocket } from "./support/context/websocket.js";
38
39
  import { Support } from "./support/index.js";
39
40
  import { SupportContext, SupportProvider, useSupport } from "./provider.js";
40
41
  import { useDefaultMessages } from "./hooks/private/use-default-messages.js";
42
+ import { useConversation } from "./hooks/use-conversation.js";
41
43
  import { useCreateConversation } from "./hooks/use-create-conversation.js";
44
+ import { useFileUpload } from "./hooks/use-file-upload.js";
42
45
  import { useRealtimeSupport } from "./hooks/use-realtime-support.js";
43
46
  import { IdentifySupportVisitor } from "./identify-visitor.js";
44
47
 
45
- export { CoButton as Button, CONVERSATION_AUTO_SEEN_DELAY_MS, Header, IdentifySupportVisitor, primitives_exports as Primitives, RealtimeProvider, Support, SupportConfig, SupportContext, SupportProvider, SupportRealtimeProvider, Text, WebSocketProvider, applyConversationSeenEvent, applyConversationTypingEvent, clearTypingFromTimelineItem, clearTypingState, hydrateConversationSeen, setTypingState, upsertConversationSeen, useClient, useClientQuery, useComposerRefocus, useConversation, useConversationAutoSeen, useConversationHistoryPage, useConversationLifecycle, useConversationPage, useConversationPreview, useConversationSeen, useConversationTimeline, useConversationTimelineItems, useConversationTyping, useConversations, useCreateConversation, useDebouncedConversationSeen, useDefaultMessages, useGroupedMessages, useHomePage, useMessageComposer, useMultimodalInput, useNewMessageSound, useRealtime, useRealtimeConnection, useRealtimeSupport, useScrollMask, useSendMessage, useSoundEffect, useSupport, useSupportConfig, useSupportNavigation, useSupportStore, useSupportText, useTypingSound, useVisitor, useWebSocket, useWindowVisibilityFocus };
48
+ export { CoButton as Button, CONVERSATION_AUTO_SEEN_DELAY_MS, Header, IdentifySupportVisitor, primitives_exports as Primitives, RealtimeProvider, Support, SupportConfig, SupportContext, SupportProvider, SupportRealtimeProvider, Text, WebSocketProvider, applyConversationSeenEvent, applyConversationTypingEvent, clearTypingFromTimelineItem, clearTypingState, hydrateConversationSeen, setTypingState, upsertConversationSeen, useClient, useClientQuery, useComposerRefocus, useConversation, useConversationAutoSeen, useConversationHistoryPage, useConversationLifecycle, useConversationPage, useConversationPreview, useConversationSeen, useConversationTimeline, useConversationTimelineItems, useConversationTyping, useConversations, useCreateConversation, useDebouncedConversationSeen, useDefaultMessages, useFileUpload, useGroupedMessages, useHomePage, useMessageComposer, useMultimodalInput, useNewMessageSound, useRealtime, useRealtimeConnection, useRealtimeSupport, useScrollMask, useSendMessage, useSoundEffect, useSupport, useSupportConfig, useSupportEventEmitter, useSupportEvents, useSupportHandle, useSupportNavigation, useSupportStore, useSupportText, useTypingSound, useVisitor, useWebSocket, useWindowVisibilityFocus };