@cossistant/react 0.0.14 → 0.0.17

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 (125) hide show
  1. package/hooks/index.d.ts +2 -1
  2. package/hooks/index.js +2 -1
  3. package/hooks/use-home-page.js +1 -1
  4. package/hooks/use-home-page.js.map +1 -1
  5. package/hooks/use-scroll-mask.d.ts +24 -0
  6. package/hooks/use-scroll-mask.d.ts.map +1 -0
  7. package/hooks/use-scroll-mask.js +90 -0
  8. package/hooks/use-scroll-mask.js.map +1 -0
  9. package/hooks/use-send-message.js +1 -1
  10. package/hooks/use-send-message.js.map +1 -1
  11. package/index.d.ts +7 -2
  12. package/index.js +9 -5
  13. package/package.json +8 -4
  14. package/parse.d.ts.map +1 -1
  15. package/primitives/avatar/image.d.ts +1 -1
  16. package/primitives/bubble.d.ts +10 -2
  17. package/primitives/bubble.d.ts.map +1 -1
  18. package/primitives/bubble.js +11 -3
  19. package/primitives/bubble.js.map +1 -1
  20. package/primitives/conversation-timeline.d.ts.map +1 -1
  21. package/primitives/conversation-timeline.js +10 -20
  22. package/primitives/conversation-timeline.js.map +1 -1
  23. package/primitives/index.d.ts +5 -2
  24. package/primitives/index.js +11 -3
  25. package/primitives/index.parts.d.ts +4 -1
  26. package/primitives/index.parts.js +5 -2
  27. package/primitives/multimodal-input.d.ts +2 -2
  28. package/primitives/multimodal-input.d.ts.map +1 -1
  29. package/primitives/page-registry.d.ts +30 -0
  30. package/primitives/page-registry.d.ts.map +1 -0
  31. package/primitives/page-registry.js +45 -0
  32. package/primitives/page-registry.js.map +1 -0
  33. package/primitives/page.d.ts +21 -0
  34. package/primitives/page.d.ts.map +1 -0
  35. package/primitives/page.js +18 -0
  36. package/primitives/page.js.map +1 -0
  37. package/primitives/router.d.ts +35 -0
  38. package/primitives/router.d.ts.map +1 -0
  39. package/primitives/router.js +22 -0
  40. package/primitives/router.js.map +1 -0
  41. package/primitives/window.d.ts +8 -3
  42. package/primitives/window.d.ts.map +1 -1
  43. package/primitives/window.js +8 -3
  44. package/primitives/window.js.map +1 -1
  45. package/realtime/index.js +1 -1
  46. package/realtime/provider.js +1 -1
  47. package/realtime/support-provider.js +0 -4
  48. package/realtime/support-provider.js.map +1 -1
  49. package/schemas2.d.ts.map +1 -1
  50. package/support/components/avatar-stack.js +1 -1
  51. package/support/components/avatar-stack.js.map +1 -1
  52. package/support/components/bubble.js +1 -1
  53. package/support/components/bubble.js.map +1 -1
  54. package/support/components/button.js +3 -3
  55. package/support/components/button.js.map +1 -1
  56. package/support/components/container.js +1 -1
  57. package/support/components/container.js.map +1 -1
  58. package/support/components/conversation-timeline.js +1 -1
  59. package/support/components/conversation-timeline.js.map +1 -1
  60. package/support/components/multimodal-input.js +2 -2
  61. package/support/components/multimodal-input.js.map +1 -1
  62. package/support/components/support-content.d.ts +5 -7
  63. package/support/components/support-content.d.ts.map +1 -1
  64. package/support/components/support-content.js +9 -11
  65. package/support/components/support-content.js.map +1 -1
  66. package/support/components/theme-wrapper.d.ts +15 -0
  67. package/support/components/theme-wrapper.d.ts.map +1 -0
  68. package/support/components/theme-wrapper.js +18 -0
  69. package/support/components/theme-wrapper.js.map +1 -0
  70. package/support/components/timeline-identification-tool.js +2 -2
  71. package/support/components/timeline-identification-tool.js.map +1 -1
  72. package/support/components/watermark.js +2 -2
  73. package/support/components/watermark.js.map +1 -1
  74. package/support/index.d.ts +36 -6
  75. package/support/index.d.ts.map +1 -1
  76. package/support/index.js +43 -20
  77. package/support/index.js.map +1 -1
  78. package/support/pages/articles.d.ts +4 -1
  79. package/support/pages/articles.d.ts.map +1 -1
  80. package/support/pages/articles.js +1 -1
  81. package/support/pages/articles.js.map +1 -1
  82. package/support/pages/conversation-history.d.ts +5 -10
  83. package/support/pages/conversation-history.d.ts.map +1 -1
  84. package/support/pages/conversation-history.js +2 -9
  85. package/support/pages/conversation-history.js.map +1 -1
  86. package/support/pages/conversation.d.ts +17 -12
  87. package/support/pages/conversation.d.ts.map +1 -1
  88. package/support/pages/conversation.js +5 -2
  89. package/support/pages/conversation.js.map +1 -1
  90. package/support/pages/home.d.ts +5 -12
  91. package/support/pages/home.d.ts.map +1 -1
  92. package/support/pages/home.js +3 -12
  93. package/support/pages/home.js.map +1 -1
  94. package/support/router.d.ts +9 -4
  95. package/support/router.d.ts.map +1 -1
  96. package/support/router.js +34 -15
  97. package/support/router.js.map +1 -1
  98. package/support/store/support-store.d.ts +17 -14
  99. package/support/store/support-store.d.ts.map +1 -1
  100. package/support/store/support-store.js +13 -10
  101. package/support/store/support-store.js.map +1 -1
  102. package/support/{support-D0JydWRx.css → support-BQhCt9Za.css} +10 -9
  103. package/support/support-BQhCt9Za.css.map +1 -0
  104. package/support/types.d.ts +28 -0
  105. package/support/types.d.ts.map +1 -0
  106. package/support/types.js +1 -0
  107. package/support.css +1 -1
  108. package/tailwind.css +9 -8
  109. package/utils/conversation.d.ts.map +1 -1
  110. package/utils/conversation.js +3 -1
  111. package/utils/conversation.js.map +1 -1
  112. package/zod-extensions.d.ts.map +1 -1
  113. package/index4.d.ts +0 -18
  114. package/index4.d.ts.map +0 -1
  115. package/index5.d.ts +0 -999
  116. package/index5.d.ts.map +0 -1
  117. package/index6.d.ts +0 -6
  118. package/react.d.ts +0 -4
  119. package/support/components/text-effect.d.ts +0 -53
  120. package/support/components/text-effect.d.ts.map +0 -1
  121. package/support/components/text-effect.js +0 -225
  122. package/support/components/text-effect.js.map +0 -1
  123. package/support/support-D0JydWRx.css.map +0 -1
  124. package/types.d-BJcRxCew.d.ts +0 -39
  125. package/types.d-BJcRxCew.d.ts.map +0 -1
package/hooks/index.d.ts CHANGED
@@ -19,7 +19,8 @@ import { CreateConversationVariables, UseCreateConversationOptions, UseCreateCon
19
19
  import { UseHomePageOptions, UseHomePageReturn, useHomePage } from "./use-home-page.js";
20
20
  import { UseMessageComposerOptions, UseMessageComposerReturn, useMessageComposer } from "./use-message-composer.js";
21
21
  import { UseRealtimeSupportOptions, UseRealtimeSupportResult, useRealtimeSupport } from "./use-realtime-support.js";
22
+ import { UseScrollMaskOptions, UseScrollMaskReturn, useScrollMask } from "./use-scroll-mask.js";
22
23
  import { SendMessageOptions, SendMessageResult, UseSendMessageOptions, UseSendMessageResult, useSendMessage } from "./use-send-message.js";
23
24
  import { UseVisitorReturn, useVisitor } from "./use-visitor.js";
24
25
  import { WindowVisibilityFocusState, useWindowVisibilityFocus } from "./use-window-visibility-focus.js";
25
- export { CONVERSATION_AUTO_SEEN_DELAY_MS, ConversationItem, ConversationLifecycleState, ConversationPreviewAssignedAgent, ConversationPreviewLastMessage, ConversationPreviewTypingParticipant, ConversationPreviewTypingState, ConversationTimelineTypingParticipant, ConversationTypingParticipant, CreateConversationVariables, GroupedMessage, SendMessageOptions, SendMessageResult, 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, UseSendMessageOptions, UseSendMessageResult, UseVisitorReturn, WindowVisibilityFocusState, useClient, useClientQuery, useComposerRefocus, useConversation, useConversationAutoSeen, useConversationHistoryPage, useConversationLifecycle, useConversationPage, useConversationPreview, useConversationSeen, useConversationTimeline, useConversationTimelineItems, useConversationTyping, useConversations, useCreateConversation, useDebouncedConversationSeen, useDefaultMessages, useGroupedMessages, useHomePage, useMessageComposer, useMultimodalInput, useRealtimeSupport, useSendMessage, useVisitor, useWindowVisibilityFocus };
26
+ export { CONVERSATION_AUTO_SEEN_DELAY_MS, ConversationItem, ConversationLifecycleState, ConversationPreviewAssignedAgent, ConversationPreviewLastMessage, ConversationPreviewTypingParticipant, ConversationPreviewTypingState, ConversationTimelineTypingParticipant, ConversationTypingParticipant, CreateConversationVariables, GroupedMessage, SendMessageOptions, SendMessageResult, 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, UseVisitorReturn, WindowVisibilityFocusState, useClient, useClientQuery, useComposerRefocus, useConversation, useConversationAutoSeen, useConversationHistoryPage, useConversationLifecycle, useConversationPage, useConversationPreview, useConversationSeen, useConversationTimeline, useConversationTimelineItems, useConversationTyping, useConversations, useCreateConversation, useDebouncedConversationSeen, useDefaultMessages, useGroupedMessages, useHomePage, useMessageComposer, useMultimodalInput, useRealtimeSupport, useScrollMask, useSendMessage, useVisitor, useWindowVisibilityFocus };
package/hooks/index.js CHANGED
@@ -1,5 +1,6 @@
1
1
  import { useClientQuery } from "./private/use-client-query.js";
2
2
  import { useClient } from "./private/use-rest-client.js";
3
+ import { useScrollMask } from "./use-scroll-mask.js";
3
4
  import { useConversation } from "./use-conversation.js";
4
5
  import { useWindowVisibilityFocus } from "./use-window-visibility-focus.js";
5
6
  import { CONVERSATION_AUTO_SEEN_DELAY_MS, useConversationAutoSeen } from "./use-conversation-auto-seen.js";
@@ -23,4 +24,4 @@ import { useDefaultMessages } from "./private/use-default-messages.js";
23
24
  import { useCreateConversation } from "./use-create-conversation.js";
24
25
  import { useRealtimeSupport } from "./use-realtime-support.js";
25
26
 
26
- export { CONVERSATION_AUTO_SEEN_DELAY_MS, useClient, useClientQuery, useComposerRefocus, useConversation, useConversationAutoSeen, useConversationHistoryPage, useConversationLifecycle, useConversationPage, useConversationPreview, useConversationSeen, useConversationTimeline, useConversationTimelineItems, useConversationTyping, useConversations, useCreateConversation, useDebouncedConversationSeen, useDefaultMessages, useGroupedMessages, useHomePage, useMessageComposer, useMultimodalInput, useRealtimeSupport, useSendMessage, useVisitor, useWindowVisibilityFocus };
27
+ export { CONVERSATION_AUTO_SEEN_DELAY_MS, useClient, useClientQuery, useComposerRefocus, useConversation, useConversationAutoSeen, useConversationHistoryPage, useConversationLifecycle, useConversationPage, useConversationPreview, useConversationSeen, useConversationTimeline, useConversationTimelineItems, useConversationTyping, useConversations, useCreateConversation, useDebouncedConversationSeen, useDefaultMessages, useGroupedMessages, useHomePage, useMessageComposer, useMultimodalInput, useRealtimeSupport, useScrollMask, useSendMessage, useVisitor, useWindowVisibilityFocus };
@@ -58,7 +58,7 @@ function useHomePage(options = {}) {
58
58
  });
59
59
  const conversations = useMemo(() => allConversations.filter(shouldDisplayConversation), [allConversations]);
60
60
  const { lastOpenConversation, availableConversationsCount } = useMemo(() => {
61
- const openConversation$1 = conversations.find((conv) => conv.status === ConversationStatus.OPEN || conv.status === "open");
61
+ const openConversation$1 = conversations.find((conv) => conv.status === ConversationStatus.OPEN);
62
62
  return {
63
63
  lastOpenConversation: openConversation$1,
64
64
  availableConversationsCount: Math.max(conversations.length - (openConversation$1 ? 1 : 0), 0)
@@ -1 +1 @@
1
- {"version":3,"file":"use-home-page.js","names":["openConversation"],"sources":["../../src/hooks/use-home-page.ts"],"sourcesContent":["import { type Conversation, ConversationStatus } from \"@cossistant/types\";\nimport { useCallback, useMemo } from \"react\";\nimport { shouldDisplayConversation } from \"../utils/conversation\";\nimport { useConversations } from \"./use-conversations\";\n\nexport type UseHomePageOptions = {\n\t/**\n\t * Whether to enable conversations fetching.\n\t * Default: true\n\t */\n\tenabled?: boolean;\n\n\t/**\n\t * Callback when user wants to start a new conversation.\n\t */\n\tonStartConversation?: (initialMessage?: string) => void;\n\n\t/**\n\t * Callback when user wants to open an existing conversation.\n\t */\n\tonOpenConversation?: (conversationId: string) => void;\n\n\t/**\n\t * Callback when user wants to view conversation history.\n\t */\n\tonOpenConversationHistory?: () => void;\n};\n\nexport type UseHomePageReturn = {\n\t// Conversations data\n\tconversations: Conversation[];\n\tisLoading: boolean;\n\terror: Error | null;\n\n\t// Derived state\n\tlastOpenConversation: Conversation | undefined;\n\tavailableConversationsCount: number;\n\thasConversations: boolean;\n\n\t// Actions\n\tstartConversation: (initialMessage?: string) => void;\n\topenConversation: (conversationId: string) => void;\n\topenConversationHistory: () => void;\n};\n\n/**\n * Main hook for the home page of the support widget.\n *\n * This hook:\n * - Fetches and manages conversations list\n * - Derives useful state (last open conversation, conversation counts)\n * - Provides navigation actions for the home page\n *\n * It encapsulates all home page logic, making the component\n * purely presentational.\n *\n * @example\n * ```tsx\n * export function HomePage() {\n * const home = useHomePage({\n * onStartConversation: (msg) => {\n * navigate('conversation', { conversationId: PENDING_CONVERSATION_ID, initialMessage: msg });\n * },\n * onOpenConversation: (id) => {\n * navigate('conversation', { conversationId: id });\n * },\n * onOpenConversationHistory: () => {\n * navigate('conversation-history');\n * },\n * });\n *\n * return (\n * <>\n * <h1>How can we help?</h1>\n *\n * {home.lastOpenConversation && (\n * <ConversationCard\n * conversation={home.lastOpenConversation}\n * onClick={() => home.openConversation(home.lastOpenConversation.id)}\n * />\n * )}\n *\n * <Button onClick={() => home.startConversation()}>\n * Ask a question\n * </Button>\n * </>\n * );\n * }\n * ```\n */\nexport function useHomePage(\n\toptions: UseHomePageOptions = {}\n): UseHomePageReturn {\n\tconst {\n\t\tenabled = true,\n\t\tonStartConversation,\n\t\tonOpenConversation,\n\t\tonOpenConversationHistory,\n\t} = options;\n\n\t// Fetch conversations\n\tconst {\n\t\tconversations: allConversations,\n\t\tisLoading,\n\t\terror,\n\t} = useConversations({\n\t\tenabled,\n\t\t// Fetch most recent conversations first\n\t\torderBy: \"updatedAt\",\n\t\torder: \"desc\",\n\t});\n\n\tconst conversations = useMemo(\n\t\t() => allConversations.filter(shouldDisplayConversation),\n\t\t[allConversations]\n\t);\n\n\t// Derive useful state from conversations\n\tconst { lastOpenConversation, availableConversationsCount } = useMemo(() => {\n\t\t// Find the most recent open conversation\n\t\tconst openConversation = conversations.find(\n\t\t\t(conv) =>\n\t\t\t\tconv.status === ConversationStatus.OPEN || conv.status === \"open\"\n\t\t);\n\n\t\t// Count other conversations (excluding the one we're showing)\n\t\tconst otherCount = Math.max(\n\t\t\tconversations.length - (openConversation ? 1 : 0),\n\t\t\t0\n\t\t);\n\n\t\treturn {\n\t\t\tlastOpenConversation: openConversation,\n\t\t\tavailableConversationsCount: otherCount,\n\t\t};\n\t}, [conversations]);\n\n\t// Navigation actions\n\tconst startConversation = useCallback(\n\t\t(initialMessage?: string) => {\n\t\t\tonStartConversation?.(initialMessage);\n\t\t},\n\t\t[onStartConversation]\n\t);\n\n\tconst openConversation = useCallback(\n\t\t(conversationId: string) => {\n\t\t\tonOpenConversation?.(conversationId);\n\t\t},\n\t\t[onOpenConversation]\n\t);\n\n\tconst openConversationHistory = useCallback(() => {\n\t\tonOpenConversationHistory?.();\n\t}, [onOpenConversationHistory]);\n\n\treturn {\n\t\tconversations,\n\t\tisLoading,\n\t\terror,\n\t\tlastOpenConversation,\n\t\tavailableConversationsCount,\n\t\thasConversations: conversations.length > 0,\n\t\tstartConversation,\n\t\topenConversation,\n\t\topenConversationHistory,\n\t};\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA0FA,SAAgB,YACf,UAA8B,EAAE,EACZ;CACpB,MAAM,EACL,UAAU,MACV,qBACA,oBACA,8BACG;CAGJ,MAAM,EACL,eAAe,kBACf,WACA,UACG,iBAAiB;EACpB;EAEA,SAAS;EACT,OAAO;EACP,CAAC;CAEF,MAAM,gBAAgB,cACf,iBAAiB,OAAO,0BAA0B,EACxD,CAAC,iBAAiB,CAClB;CAGD,MAAM,EAAE,sBAAsB,gCAAgC,cAAc;EAE3E,MAAMA,qBAAmB,cAAc,MACrC,SACA,KAAK,WAAW,mBAAmB,QAAQ,KAAK,WAAW,OAC5D;AAQD,SAAO;GACN,sBAAsBA;GACtB,6BAPkB,KAAK,IACvB,cAAc,UAAUA,qBAAmB,IAAI,IAC/C,EACA;GAKA;IACC,CAAC,cAAc,CAAC;CAGnB,MAAM,oBAAoB,aACxB,mBAA4B;AAC5B,wBAAsB,eAAe;IAEtC,CAAC,oBAAoB,CACrB;CAED,MAAM,mBAAmB,aACvB,mBAA2B;AAC3B,uBAAqB,eAAe;IAErC,CAAC,mBAAmB,CACpB;CAED,MAAM,0BAA0B,kBAAkB;AACjD,+BAA6B;IAC3B,CAAC,0BAA0B,CAAC;AAE/B,QAAO;EACN;EACA;EACA;EACA;EACA;EACA,kBAAkB,cAAc,SAAS;EACzC;EACA;EACA;EACA"}
1
+ {"version":3,"file":"use-home-page.js","names":["openConversation"],"sources":["../../src/hooks/use-home-page.ts"],"sourcesContent":["import { type Conversation, ConversationStatus } from \"@cossistant/types\";\nimport { useCallback, useMemo } from \"react\";\nimport { shouldDisplayConversation } from \"../utils/conversation\";\nimport { useConversations } from \"./use-conversations\";\n\nexport type UseHomePageOptions = {\n\t/**\n\t * Whether to enable conversations fetching.\n\t * Default: true\n\t */\n\tenabled?: boolean;\n\n\t/**\n\t * Callback when user wants to start a new conversation.\n\t */\n\tonStartConversation?: (initialMessage?: string) => void;\n\n\t/**\n\t * Callback when user wants to open an existing conversation.\n\t */\n\tonOpenConversation?: (conversationId: string) => void;\n\n\t/**\n\t * Callback when user wants to view conversation history.\n\t */\n\tonOpenConversationHistory?: () => void;\n};\n\nexport type UseHomePageReturn = {\n\t// Conversations data\n\tconversations: Conversation[];\n\tisLoading: boolean;\n\terror: Error | null;\n\n\t// Derived state\n\tlastOpenConversation: Conversation | undefined;\n\tavailableConversationsCount: number;\n\thasConversations: boolean;\n\n\t// Actions\n\tstartConversation: (initialMessage?: string) => void;\n\topenConversation: (conversationId: string) => void;\n\topenConversationHistory: () => void;\n};\n\n/**\n * Main hook for the home page of the support widget.\n *\n * This hook:\n * - Fetches and manages conversations list\n * - Derives useful state (last open conversation, conversation counts)\n * - Provides navigation actions for the home page\n *\n * It encapsulates all home page logic, making the component\n * purely presentational.\n *\n * @example\n * ```tsx\n * export function HomePage() {\n * const home = useHomePage({\n * onStartConversation: (msg) => {\n * navigate('conversation', { conversationId: PENDING_CONVERSATION_ID, initialMessage: msg });\n * },\n * onOpenConversation: (id) => {\n * navigate('conversation', { conversationId: id });\n * },\n * onOpenConversationHistory: () => {\n * navigate('conversation-history');\n * },\n * });\n *\n * return (\n * <>\n * <h1>How can we help?</h1>\n *\n * {home.lastOpenConversation && (\n * <ConversationCard\n * conversation={home.lastOpenConversation}\n * onClick={() => home.openConversation(home.lastOpenConversation.id)}\n * />\n * )}\n *\n * <Button onClick={() => home.startConversation()}>\n * Ask a question\n * </Button>\n * </>\n * );\n * }\n * ```\n */\nexport function useHomePage(\n\toptions: UseHomePageOptions = {}\n): UseHomePageReturn {\n\tconst {\n\t\tenabled = true,\n\t\tonStartConversation,\n\t\tonOpenConversation,\n\t\tonOpenConversationHistory,\n\t} = options;\n\n\t// Fetch conversations\n\tconst {\n\t\tconversations: allConversations,\n\t\tisLoading,\n\t\terror,\n\t} = useConversations({\n\t\tenabled,\n\t\t// Fetch most recent conversations first\n\t\torderBy: \"updatedAt\",\n\t\torder: \"desc\",\n\t});\n\n\tconst conversations = useMemo(\n\t\t() => allConversations.filter(shouldDisplayConversation),\n\t\t[allConversations]\n\t);\n\n\t// Derive useful state from conversations\n\tconst { lastOpenConversation, availableConversationsCount } = useMemo(() => {\n\t\t// Find the most recent open conversation\n\t\tconst openConversation = conversations.find(\n\t\t\t(conv) => conv.status === ConversationStatus.OPEN\n\t\t);\n\n\t\t// Count other conversations (excluding the one we're showing)\n\t\tconst otherCount = Math.max(\n\t\t\tconversations.length - (openConversation ? 1 : 0),\n\t\t\t0\n\t\t);\n\n\t\treturn {\n\t\t\tlastOpenConversation: openConversation,\n\t\t\tavailableConversationsCount: otherCount,\n\t\t};\n\t}, [conversations]);\n\n\t// Navigation actions\n\tconst startConversation = useCallback(\n\t\t(initialMessage?: string) => {\n\t\t\tonStartConversation?.(initialMessage);\n\t\t},\n\t\t[onStartConversation]\n\t);\n\n\tconst openConversation = useCallback(\n\t\t(conversationId: string) => {\n\t\t\tonOpenConversation?.(conversationId);\n\t\t},\n\t\t[onOpenConversation]\n\t);\n\n\tconst openConversationHistory = useCallback(() => {\n\t\tonOpenConversationHistory?.();\n\t}, [onOpenConversationHistory]);\n\n\treturn {\n\t\tconversations,\n\t\tisLoading,\n\t\terror,\n\t\tlastOpenConversation,\n\t\tavailableConversationsCount,\n\t\thasConversations: conversations.length > 0,\n\t\tstartConversation,\n\t\topenConversation,\n\t\topenConversationHistory,\n\t};\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA0FA,SAAgB,YACf,UAA8B,EAAE,EACZ;CACpB,MAAM,EACL,UAAU,MACV,qBACA,oBACA,8BACG;CAGJ,MAAM,EACL,eAAe,kBACf,WACA,UACG,iBAAiB;EACpB;EAEA,SAAS;EACT,OAAO;EACP,CAAC;CAEF,MAAM,gBAAgB,cACf,iBAAiB,OAAO,0BAA0B,EACxD,CAAC,iBAAiB,CAClB;CAGD,MAAM,EAAE,sBAAsB,gCAAgC,cAAc;EAE3E,MAAMA,qBAAmB,cAAc,MACrC,SAAS,KAAK,WAAW,mBAAmB,KAC7C;AAQD,SAAO;GACN,sBAAsBA;GACtB,6BAPkB,KAAK,IACvB,cAAc,UAAUA,qBAAmB,IAAI,IAC/C,EACA;GAKA;IACC,CAAC,cAAc,CAAC;CAGnB,MAAM,oBAAoB,aACxB,mBAA4B;AAC5B,wBAAsB,eAAe;IAEtC,CAAC,oBAAoB,CACrB;CAED,MAAM,mBAAmB,aACvB,mBAA2B;AAC3B,uBAAqB,eAAe;IAErC,CAAC,mBAAmB,CACpB;CAED,MAAM,0BAA0B,kBAAkB;AACjD,+BAA6B;IAC3B,CAAC,0BAA0B,CAAC;AAE/B,QAAO;EACN;EACA;EACA;EACA;EACA;EACA,kBAAkB,cAAc,SAAS;EACzC;EACA;EACA;EACA"}
@@ -0,0 +1,24 @@
1
+ import * as React$1 from "react";
2
+
3
+ //#region src/hooks/use-scroll-mask.d.ts
4
+ type UseScrollMaskOptions = {
5
+ maskHeight?: string;
6
+ scrollbarWidth?: string;
7
+ topThreshold?: number;
8
+ bottomThreshold?: number;
9
+ };
10
+ type UseScrollMaskReturn = {
11
+ ref: React$1.RefObject<HTMLDivElement | null>;
12
+ style: React$1.CSSProperties;
13
+ };
14
+ /**
15
+ * Hook that provides dynamic scroll mask styles based on scroll position and scrollability.
16
+ * Only shows gradients when content is scrollable and when not at the edges.
17
+ *
18
+ * @param options - Configuration for mask appearance and scroll thresholds
19
+ * @returns Object with ref to attach to scrollable element and computed style object
20
+ */
21
+ declare function useScrollMask(options?: UseScrollMaskOptions): UseScrollMaskReturn;
22
+ //#endregion
23
+ export { UseScrollMaskOptions, UseScrollMaskReturn, useScrollMask };
24
+ //# sourceMappingURL=use-scroll-mask.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"use-scroll-mask.d.ts","names":[],"sources":["../../src/hooks/use-scroll-mask.ts"],"sourcesContent":[],"mappings":";;;KAEY,oBAAA;;EAAA,cAAA,CAAA,EAAA,MAAoB;EAOpB,YAAA,CAAA,EAAA,MAAA;EACU,eAAA,CAAA,EAAA,MAAA;CAAhB;AACE,KAFI,mBAAA,GAEE;EAAa,GAAA,EADrB,OAAA,CAAM,SACe,CADL,cACK,GAAA,IAAA,CAAA;EAUX,KAAA,EAVR,OAAA,CAAM,aAUe;;;;;;;;;iBAAb,aAAA,WACN,uBACP"}
@@ -0,0 +1,90 @@
1
+ import * as React$1 from "react";
2
+
3
+ //#region src/hooks/use-scroll-mask.ts
4
+ /**
5
+ * Hook that provides dynamic scroll mask styles based on scroll position and scrollability.
6
+ * Only shows gradients when content is scrollable and when not at the edges.
7
+ *
8
+ * @param options - Configuration for mask appearance and scroll thresholds
9
+ * @returns Object with ref to attach to scrollable element and computed style object
10
+ */
11
+ function useScrollMask(options = {}) {
12
+ const { maskHeight = "54px", scrollbarWidth = "12px", topThreshold = 2, bottomThreshold = 12 } = options;
13
+ const ref = React$1.useRef(null);
14
+ const [scrollState, setScrollState] = React$1.useState({
15
+ isScrollable: false,
16
+ isAtTop: true,
17
+ isAtBottom: false
18
+ });
19
+ const updateScrollState = React$1.useCallback(() => {
20
+ const element = ref.current;
21
+ if (!element) return;
22
+ const { scrollTop, scrollHeight, clientHeight } = element;
23
+ setScrollState({
24
+ isScrollable: scrollHeight > clientHeight,
25
+ isAtTop: scrollTop <= topThreshold,
26
+ isAtBottom: scrollHeight - scrollTop - clientHeight <= bottomThreshold
27
+ });
28
+ }, [topThreshold, bottomThreshold]);
29
+ React$1.useEffect(() => {
30
+ const element = ref.current;
31
+ if (!element) return;
32
+ element.addEventListener("scroll", updateScrollState, { passive: true });
33
+ return () => {
34
+ element.removeEventListener("scroll", updateScrollState);
35
+ };
36
+ }, [updateScrollState]);
37
+ React$1.useEffect(() => {
38
+ const element = ref.current;
39
+ if (!element) return;
40
+ updateScrollState();
41
+ const resizeObserver = new ResizeObserver(updateScrollState);
42
+ resizeObserver.observe(element);
43
+ const mutationObserver = new MutationObserver(updateScrollState);
44
+ mutationObserver.observe(element, {
45
+ childList: true,
46
+ subtree: true,
47
+ characterData: true
48
+ });
49
+ return () => {
50
+ resizeObserver.disconnect();
51
+ mutationObserver.disconnect();
52
+ };
53
+ }, [updateScrollState]);
54
+ return {
55
+ ref,
56
+ style: React$1.useMemo(() => {
57
+ const { isScrollable, isAtTop, isAtBottom } = scrollState;
58
+ if (!isScrollable) return {};
59
+ const showTopGradient = !isAtTop;
60
+ const showBottomGradient = !isAtBottom;
61
+ let gradientStops;
62
+ if (showTopGradient && showBottomGradient) gradientStops = `transparent, black ${maskHeight}, black calc(100% - ${maskHeight}), transparent`;
63
+ else if (showTopGradient && !showBottomGradient) gradientStops = `transparent, black ${maskHeight}, black`;
64
+ else if (!showTopGradient && showBottomGradient) gradientStops = `black, black calc(100% - ${maskHeight}), transparent`;
65
+ else return {};
66
+ const maskImage = `linear-gradient(to bottom, ${gradientStops}), linear-gradient(black, black)`;
67
+ const maskSize = `calc(100% - ${scrollbarWidth}) 100%, ${scrollbarWidth} 100%`;
68
+ const maskPosition = "0 0, 100% 0";
69
+ const maskRepeat = "no-repeat, no-repeat";
70
+ return {
71
+ maskImage,
72
+ maskSize,
73
+ maskPosition,
74
+ maskRepeat,
75
+ WebkitMaskImage: maskImage,
76
+ WebkitMaskSize: maskSize,
77
+ WebkitMaskPosition: maskPosition,
78
+ WebkitMaskRepeat: maskRepeat
79
+ };
80
+ }, [
81
+ scrollState,
82
+ maskHeight,
83
+ scrollbarWidth
84
+ ])
85
+ };
86
+ }
87
+
88
+ //#endregion
89
+ export { useScrollMask };
90
+ //# sourceMappingURL=use-scroll-mask.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"use-scroll-mask.js","names":["React","gradientStops: string"],"sources":["../../src/hooks/use-scroll-mask.ts"],"sourcesContent":["import * as React from \"react\";\n\nexport type UseScrollMaskOptions = {\n\tmaskHeight?: string;\n\tscrollbarWidth?: string;\n\ttopThreshold?: number;\n\tbottomThreshold?: number;\n};\n\nexport type UseScrollMaskReturn = {\n\tref: React.RefObject<HTMLDivElement | null>;\n\tstyle: React.CSSProperties;\n};\n\n/**\n * Hook that provides dynamic scroll mask styles based on scroll position and scrollability.\n * Only shows gradients when content is scrollable and when not at the edges.\n *\n * @param options - Configuration for mask appearance and scroll thresholds\n * @returns Object with ref to attach to scrollable element and computed style object\n */\nexport function useScrollMask(\n\toptions: UseScrollMaskOptions = {}\n): UseScrollMaskReturn {\n\tconst {\n\t\tmaskHeight = \"54px\",\n\t\tscrollbarWidth = \"12px\",\n\t\ttopThreshold = 2,\n\t\tbottomThreshold = 12,\n\t} = options;\n\n\tconst ref = React.useRef<HTMLDivElement>(null);\n\tconst [scrollState, setScrollState] = React.useState({\n\t\tisScrollable: false,\n\t\tisAtTop: true,\n\t\tisAtBottom: false,\n\t});\n\n\t// Check scrollability and position\n\tconst updateScrollState = React.useCallback(() => {\n\t\tconst element = ref.current;\n\t\tif (!element) {\n\t\t\treturn;\n\t\t}\n\n\t\tconst { scrollTop, scrollHeight, clientHeight } = element;\n\t\tconst isScrollable = scrollHeight > clientHeight;\n\t\tconst isAtTop = scrollTop <= topThreshold;\n\t\tconst distanceFromBottom = scrollHeight - scrollTop - clientHeight;\n\t\tconst isAtBottom = distanceFromBottom <= bottomThreshold;\n\n\t\tsetScrollState({\n\t\t\tisScrollable,\n\t\t\tisAtTop,\n\t\t\tisAtBottom,\n\t\t});\n\t}, [topThreshold, bottomThreshold]);\n\n\t// Update on scroll\n\tReact.useEffect(() => {\n\t\tconst element = ref.current;\n\t\tif (!element) {\n\t\t\treturn;\n\t\t}\n\n\t\telement.addEventListener(\"scroll\", updateScrollState, { passive: true });\n\t\treturn () => {\n\t\t\telement.removeEventListener(\"scroll\", updateScrollState);\n\t\t};\n\t}, [updateScrollState]);\n\n\t// Update on resize or content changes\n\tReact.useEffect(() => {\n\t\tconst element = ref.current;\n\t\tif (!element) {\n\t\t\treturn;\n\t\t}\n\n\t\t// Initial check\n\t\tupdateScrollState();\n\n\t\tconst resizeObserver = new ResizeObserver(updateScrollState);\n\t\tresizeObserver.observe(element);\n\n\t\t// Also observe children changes (content updates)\n\t\tconst mutationObserver = new MutationObserver(updateScrollState);\n\t\tmutationObserver.observe(element, {\n\t\t\tchildList: true,\n\t\t\tsubtree: true,\n\t\t\tcharacterData: true,\n\t\t});\n\n\t\treturn () => {\n\t\t\tresizeObserver.disconnect();\n\t\t\tmutationObserver.disconnect();\n\t\t};\n\t}, [updateScrollState]);\n\n\t// Generate mask styles based on scroll state\n\tconst style = React.useMemo<React.CSSProperties>(() => {\n\t\tconst { isScrollable, isAtTop, isAtBottom } = scrollState;\n\n\t\t// No mask if content isn't scrollable\n\t\tif (!isScrollable) {\n\t\t\treturn {};\n\t\t}\n\n\t\t// Determine which gradients to show\n\t\tconst showTopGradient = !isAtTop;\n\t\tconst showBottomGradient = !isAtBottom;\n\n\t\t// Build gradient stops\n\t\tlet gradientStops: string;\n\t\tif (showTopGradient && showBottomGradient) {\n\t\t\t// Both gradients (middle of scroll)\n\t\t\tgradientStops = `transparent, black ${maskHeight}, black calc(100% - ${maskHeight}), transparent`;\n\t\t} else if (showTopGradient && !showBottomGradient) {\n\t\t\t// Only top gradient (at bottom)\n\t\t\tgradientStops = `transparent, black ${maskHeight}, black`;\n\t\t} else if (!showTopGradient && showBottomGradient) {\n\t\t\t// Only bottom gradient (at top)\n\t\t\tgradientStops = `black, black calc(100% - ${maskHeight}), transparent`;\n\t\t} else {\n\t\t\t// No gradients (shouldn't happen if scrollable, but handle it)\n\t\t\treturn {};\n\t\t}\n\n\t\tconst maskImage = `linear-gradient(to bottom, ${gradientStops}), linear-gradient(black, black)`;\n\t\tconst maskSize = `calc(100% - ${scrollbarWidth}) 100%, ${scrollbarWidth} 100%`;\n\t\tconst maskPosition = \"0 0, 100% 0\";\n\t\tconst maskRepeat = \"no-repeat, no-repeat\";\n\n\t\treturn {\n\t\t\tmaskImage,\n\t\t\tmaskSize,\n\t\t\tmaskPosition,\n\t\t\tmaskRepeat,\n\t\t\tWebkitMaskImage: maskImage,\n\t\t\tWebkitMaskSize: maskSize,\n\t\t\tWebkitMaskPosition: maskPosition,\n\t\t\tWebkitMaskRepeat: maskRepeat,\n\t\t};\n\t}, [scrollState, maskHeight, scrollbarWidth]);\n\n\treturn { ref, style };\n}\n"],"mappings":";;;;;;;;;;AAqBA,SAAgB,cACf,UAAgC,EAAE,EACZ;CACtB,MAAM,EACL,aAAa,QACb,iBAAiB,QACjB,eAAe,GACf,kBAAkB,OACf;CAEJ,MAAM,MAAMA,QAAM,OAAuB,KAAK;CAC9C,MAAM,CAAC,aAAa,kBAAkBA,QAAM,SAAS;EACpD,cAAc;EACd,SAAS;EACT,YAAY;EACZ,CAAC;CAGF,MAAM,oBAAoBA,QAAM,kBAAkB;EACjD,MAAM,UAAU,IAAI;AACpB,MAAI,CAAC,QACJ;EAGD,MAAM,EAAE,WAAW,cAAc,iBAAiB;AAMlD,iBAAe;GACd,cANoB,eAAe;GAOnC,SANe,aAAa;GAO5B,YAN0B,eAAe,YAAY,gBACb;GAMxC,CAAC;IACA,CAAC,cAAc,gBAAgB,CAAC;AAGnC,SAAM,gBAAgB;EACrB,MAAM,UAAU,IAAI;AACpB,MAAI,CAAC,QACJ;AAGD,UAAQ,iBAAiB,UAAU,mBAAmB,EAAE,SAAS,MAAM,CAAC;AACxE,eAAa;AACZ,WAAQ,oBAAoB,UAAU,kBAAkB;;IAEvD,CAAC,kBAAkB,CAAC;AAGvB,SAAM,gBAAgB;EACrB,MAAM,UAAU,IAAI;AACpB,MAAI,CAAC,QACJ;AAID,qBAAmB;EAEnB,MAAM,iBAAiB,IAAI,eAAe,kBAAkB;AAC5D,iBAAe,QAAQ,QAAQ;EAG/B,MAAM,mBAAmB,IAAI,iBAAiB,kBAAkB;AAChE,mBAAiB,QAAQ,SAAS;GACjC,WAAW;GACX,SAAS;GACT,eAAe;GACf,CAAC;AAEF,eAAa;AACZ,kBAAe,YAAY;AAC3B,oBAAiB,YAAY;;IAE5B,CAAC,kBAAkB,CAAC;AAgDvB,QAAO;EAAE;EAAK,OA7CAA,QAAM,cAAmC;GACtD,MAAM,EAAE,cAAc,SAAS,eAAe;AAG9C,OAAI,CAAC,aACJ,QAAO,EAAE;GAIV,MAAM,kBAAkB,CAAC;GACzB,MAAM,qBAAqB,CAAC;GAG5B,IAAIC;AACJ,OAAI,mBAAmB,mBAEtB,iBAAgB,sBAAsB,WAAW,sBAAsB,WAAW;YACxE,mBAAmB,CAAC,mBAE9B,iBAAgB,sBAAsB,WAAW;YACvC,CAAC,mBAAmB,mBAE9B,iBAAgB,4BAA4B,WAAW;OAGvD,QAAO,EAAE;GAGV,MAAM,YAAY,8BAA8B,cAAc;GAC9D,MAAM,WAAW,eAAe,eAAe,UAAU,eAAe;GACxE,MAAM,eAAe;GACrB,MAAM,aAAa;AAEnB,UAAO;IACN;IACA;IACA;IACA;IACA,iBAAiB;IACjB,gBAAgB;IAChB,oBAAoB;IACpB,kBAAkB;IAClB;KACC;GAAC;GAAa;GAAY;GAAe,CAAC;EAExB"}
@@ -66,7 +66,7 @@ function useSendMessage(options = {}) {
66
66
  item: {
67
67
  id: timelineItemPayload.id,
68
68
  text: timelineItemPayload.text ?? "",
69
- type: timelineItemPayload.type,
69
+ type: timelineItemPayload.type === "identification" ? "message" : timelineItemPayload.type,
70
70
  visibility: timelineItemPayload.visibility,
71
71
  userId: timelineItemPayload.userId,
72
72
  aiAgentId: timelineItemPayload.aiAgentId,
@@ -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 = 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: 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,0BAAS,IAAI,MAAM,EAAC,aAAa;AAGvC,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,MAAM,oBAAoB;KAC1B,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":["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 = 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,0BAAS,IAAI,MAAM,EAAC,aAAa;AAGvC,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"}
package/index.d.ts CHANGED
@@ -20,12 +20,14 @@ import { CreateConversationVariables, UseCreateConversationOptions, UseCreateCon
20
20
  import { UseHomePageOptions, UseHomePageReturn, useHomePage } from "./hooks/use-home-page.js";
21
21
  import { UseMessageComposerOptions, UseMessageComposerReturn, useMessageComposer } from "./hooks/use-message-composer.js";
22
22
  import { UseRealtimeSupportOptions, UseRealtimeSupportResult, useRealtimeSupport } from "./hooks/use-realtime-support.js";
23
+ import { UseScrollMaskOptions, UseScrollMaskReturn, useScrollMask } from "./hooks/use-scroll-mask.js";
23
24
  import { SendMessageOptions, SendMessageResult, UseSendMessageOptions, UseSendMessageResult, useSendMessage } from "./hooks/use-send-message.js";
24
25
  import { UseVisitorReturn, useVisitor } from "./hooks/use-visitor.js";
25
26
  import { WindowVisibilityFocusState, useWindowVisibilityFocus } from "./hooks/use-window-visibility-focus.js";
26
27
  import "./hooks/index.js";
27
28
  import { IdentifySupportVisitor, IdentifySupportVisitorProps } from "./identify-visitor.js";
28
29
  import { SupportConfig, SupportConfigProps } from "./support-config.js";
30
+ import { Page, PageProps } from "./primitives/page.js";
29
31
  import { index_d_exports } from "./primitives/index.js";
30
32
  import { CossistantContextValue, CossistantProviderProps, SupportContext, SupportProvider, SupportProviderProps, UseSupportValue, useSupport } from "./provider.js";
31
33
  import { RealtimeAuthConfig, RealtimeContextValue, RealtimeProvider, RealtimeProviderProps, useRealtimeConnection } from "./realtime/provider.js";
@@ -35,7 +37,10 @@ import { applyConversationTypingEvent, clearTypingFromTimelineItem, clearTypingS
35
37
  import { RealtimeEventHandler, RealtimeEventHandlerEntry, RealtimeEventHandlersMap, RealtimeEventMeta, useRealtime } from "./realtime/use-realtime.js";
36
38
  import "./realtime/index.js";
37
39
  import { Text, useSupportText } from "./support/text/index.js";
40
+ import { BubbleSlotProps, ContainerSlotProps, RouterSlotProps } from "./support/types.js";
41
+ import { CoButton } from "./support/components/button.js";
42
+ import { Header } from "./support/components/header.js";
38
43
  import { WebSocketContextValue, WebSocketProvider, useWebSocket } from "./support/context/websocket.js";
39
44
  import { useSupportConfig, useSupportNavigation, useSupportStore } from "./support/store/support-store.js";
40
- import { Support, SupportProps } from "./support/index.js";
41
- export { CONVERSATION_AUTO_SEEN_DELAY_MS, ConversationItem, ConversationLifecycleState, ConversationPreviewAssignedAgent, ConversationPreviewLastMessage, ConversationPreviewTypingParticipant, ConversationPreviewTypingState, ConversationTimelineTypingParticipant, ConversationTypingParticipant, CossistantContextValue, CossistantProviderProps, CreateConversationVariables, GroupedMessage, IdentifySupportVisitor, IdentifySupportVisitorProps, index_d_exports as Primitives, RealtimeAuthConfig, RealtimeContextValue, RealtimeEventHandler, RealtimeEventHandlerEntry, RealtimeEventHandlersMap, RealtimeEventMeta, RealtimeProvider, RealtimeProviderProps, SendMessageOptions, SendMessageResult, Support, SupportConfig, SupportConfigProps, SupportContext, SupportLocale, 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, UseSendMessageOptions, UseSendMessageResult, 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, useRealtime, useRealtimeConnection, useRealtimeSupport, useSendMessage, useSupport, useSupportConfig, useSupportNavigation, useSupportStore, useSupportText, useVisitor, useWebSocket, useWindowVisibilityFocus };
45
+ import { DefaultRoutes, NavigationState, RouteRegistry, Support, SupportPage, SupportProps } from "./support/index.js";
46
+ export { BubbleSlotProps, CoButton as Button, CONVERSATION_AUTO_SEEN_DELAY_MS, ContainerSlotProps, ConversationItem, ConversationLifecycleState, ConversationPreviewAssignedAgent, ConversationPreviewLastMessage, ConversationPreviewTypingParticipant, ConversationPreviewTypingState, ConversationTimelineTypingParticipant, ConversationTypingParticipant, CossistantContextValue, CossistantProviderProps, CreateConversationVariables, DefaultRoutes, GroupedMessage, Header, IdentifySupportVisitor, IdentifySupportVisitorProps, NavigationState, Page, PageProps, 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, 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, useRealtime, useRealtimeConnection, useRealtimeSupport, useScrollMask, useSendMessage, useSupport, useSupportConfig, useSupportNavigation, useSupportStore, useSupportText, useVisitor, useWebSocket, useWindowVisibilityFocus };
package/index.js CHANGED
@@ -1,13 +1,17 @@
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";
5
+ import { applyConversationTypingEvent, clearTypingFromTimelineItem, clearTypingState, setTypingState } from "./realtime/typing-store.js";
6
+ import { useScrollMask } from "./hooks/use-scroll-mask.js";
7
+ import { Page } from "./primitives/page.js";
9
8
  import { useSupportConfig, useSupportNavigation, useSupportStore } from "./support/store/support-store.js";
10
9
  import { primitives_exports } from "./primitives/index.js";
10
+ import { RealtimeProvider, useRealtimeConnection } from "./realtime/provider.js";
11
+ import { useRealtime } from "./realtime/use-realtime.js";
12
+ import { SupportRealtimeProvider } from "./realtime/support-provider.js";
13
+ import { CoButton } from "./support/components/button.js";
14
+ import { Header } from "./support/components/header.js";
11
15
  import { Text, useSupportText } from "./support/text/index.js";
12
16
  import { useConversation } from "./hooks/use-conversation.js";
13
17
  import { useWindowVisibilityFocus } from "./hooks/use-window-visibility-focus.js";
@@ -36,4 +40,4 @@ import { useCreateConversation } from "./hooks/use-create-conversation.js";
36
40
  import { useRealtimeSupport } from "./hooks/use-realtime-support.js";
37
41
  import { IdentifySupportVisitor } from "./identify-visitor.js";
38
42
 
39
- export { CONVERSATION_AUTO_SEEN_DELAY_MS, 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, useRealtime, useRealtimeConnection, useRealtimeSupport, useSendMessage, useSupport, useSupportConfig, useSupportNavigation, useSupportStore, useSupportText, useVisitor, useWebSocket, useWindowVisibilityFocus };
43
+ export { CoButton as Button, CONVERSATION_AUTO_SEEN_DELAY_MS, Header, IdentifySupportVisitor, Page, 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, useRealtime, useRealtimeConnection, useRealtimeSupport, useScrollMask, useSendMessage, useSupport, useSupportConfig, useSupportNavigation, useSupportStore, useSupportText, useVisitor, useWebSocket, useWindowVisibilityFocus };
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@cossistant/react",
3
3
  "type": "module",
4
- "version": "0.0.14",
4
+ "version": "0.0.17",
5
5
  "private": false,
6
6
  "author": "Cossistant team",
7
7
  "description": "Headless React SDK for building AI-powered support/chat widgets. Hooks + primitives, WS-driven, TypeScript-first. Next.js-ready, Tailwind optional.",
@@ -88,8 +88,8 @@
88
88
  "*.css"
89
89
  ],
90
90
  "dependencies": {
91
- "@cossistant/core": "0.0.14",
92
- "@cossistant/types": "0.0.14",
91
+ "@cossistant/core": "0.0.17",
92
+ "@cossistant/types": "0.0.17",
93
93
  "class-variance-authority": "^0.7.1",
94
94
  "clsx": "^2.1.1",
95
95
  "nanoid": "^5.1.5",
@@ -103,7 +103,8 @@
103
103
  "react-dom": ">=18 <20",
104
104
  "@types/react": "",
105
105
  "motion": "^12.18.1",
106
- "tailwindcss": "*"
106
+ "tailwindcss": "*",
107
+ "tailwind-scrollbar": "*"
107
108
  },
108
109
  "peerDependenciesMeta": {
109
110
  "@types/react": {
@@ -111,6 +112,9 @@
111
112
  },
112
113
  "tailwindcss": {
113
114
  "optional": true
115
+ },
116
+ "tailwind-scrollbar": {
117
+ "optional": true
114
118
  }
115
119
  },
116
120
  "browserslist": {
package/parse.d.ts.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"parse.d.ts","names":["core","ZodError","ZodSafeParseResult","T","ZodSafeParseSuccess","ZodSafeParseError","parse","$ZodType","$ZodIssue","ParseContext","util","AnyFunc","$ZodErrorClass","output","parseAsync","Promise","safeParse","safeParseAsync","encode","input","decode","encodeAsync","decodeAsync","safeEncode","safeDecode","safeEncodeAsync","safeDecodeAsync"],"sources":["../../../node_modules/.bun/zod@4.1.12/node_modules/zod/v4/classic/parse.d.cts"],"sourcesContent":["import * as core from \"../core/index.cjs\";\nimport { type ZodError } from \"./errors.cjs\";\nexport type ZodSafeParseResult<T> = ZodSafeParseSuccess<T> | ZodSafeParseError<T>;\nexport type ZodSafeParseSuccess<T> = {\n success: true;\n data: T;\n error?: never;\n};\nexport type ZodSafeParseError<T> = {\n success: false;\n data?: never;\n error: ZodError<T>;\n};\nexport declare const parse: <T extends core.$ZodType>(schema: T, value: unknown, _ctx?: core.ParseContext<core.$ZodIssue>, _params?: {\n callee?: core.util.AnyFunc;\n Err?: core.$ZodErrorClass;\n}) => core.output<T>;\nexport declare const parseAsync: <T extends core.$ZodType>(schema: T, value: unknown, _ctx?: core.ParseContext<core.$ZodIssue>, _params?: {\n callee?: core.util.AnyFunc;\n Err?: core.$ZodErrorClass;\n}) => Promise<core.output<T>>;\nexport declare const safeParse: <T extends core.$ZodType>(schema: T, value: unknown, _ctx?: core.ParseContext<core.$ZodIssue>) => ZodSafeParseResult<core.output<T>>;\nexport declare const safeParseAsync: <T extends core.$ZodType>(schema: T, value: unknown, _ctx?: core.ParseContext<core.$ZodIssue>) => Promise<ZodSafeParseResult<core.output<T>>>;\nexport declare const encode: <T extends core.$ZodType>(schema: T, value: core.output<T>, _ctx?: core.ParseContext<core.$ZodIssue>) => core.input<T>;\nexport declare const decode: <T extends core.$ZodType>(schema: T, value: core.input<T>, _ctx?: core.ParseContext<core.$ZodIssue>) => core.output<T>;\nexport declare const encodeAsync: <T extends core.$ZodType>(schema: T, value: core.output<T>, _ctx?: core.ParseContext<core.$ZodIssue>) => Promise<core.input<T>>;\nexport declare const decodeAsync: <T extends core.$ZodType>(schema: T, value: core.input<T>, _ctx?: core.ParseContext<core.$ZodIssue>) => Promise<core.output<T>>;\nexport declare const safeEncode: <T extends core.$ZodType>(schema: T, value: core.output<T>, _ctx?: core.ParseContext<core.$ZodIssue>) => ZodSafeParseResult<core.input<T>>;\nexport declare const safeDecode: <T extends core.$ZodType>(schema: T, value: core.input<T>, _ctx?: core.ParseContext<core.$ZodIssue>) => ZodSafeParseResult<core.output<T>>;\nexport declare const safeEncodeAsync: <T extends core.$ZodType>(schema: T, value: core.output<T>, _ctx?: core.ParseContext<core.$ZodIssue>) => Promise<ZodSafeParseResult<core.input<T>>>;\nexport declare const safeDecodeAsync: <T extends core.$ZodType>(schema: T, value: core.input<T>, _ctx?: core.ParseContext<core.$ZodIssue>) => Promise<ZodSafeParseResult<core.output<T>>>;\n"],"x_google_ignoreList":[0],"mappings":";;;KAEYE,wBAAwBE,oBAAoBD,KAAKE,kBAAkBF;AAAnED,KACAE,mBADkB,CAAA,CAAAD,CAAAA,GAAAA;EAA0BA,OAAAA,EAAAA,IAAAA;EAApBC,IAAAA,EAG1BD,CAH0BC;EAA2CD,KAAAA,CAAAA,EAAAA,KAAAA;CAAlBE;AAAiB,KAMlEA,iBANkE,CAAA,CAAA,CAAA,GAAA;EAClED,OAAAA,EAAAA,KAAAA;EAKAC,IAAAA,CAAAA,EAAAA,KAAAA;SAGDJ,SAASE"}
1
+ {"version":3,"file":"parse.d.ts","names":["core","ZodError","ZodSafeParseResult","T","ZodSafeParseSuccess","ZodSafeParseError","parse","$ZodType","$ZodIssue","ParseContext","util","AnyFunc","$ZodErrorClass","output","parseAsync","Promise","safeParse","safeParseAsync","encode","input","decode","encodeAsync","decodeAsync","safeEncode","safeDecode","safeEncodeAsync","safeDecodeAsync"],"sources":["../../../node_modules/.bun/zod@4.1.12/node_modules/zod/v4/classic/parse.d.cts"],"sourcesContent":["import * as core from \"../core/index.cjs\";\nimport { type ZodError } from \"./errors.cjs\";\nexport type ZodSafeParseResult<T> = ZodSafeParseSuccess<T> | ZodSafeParseError<T>;\nexport type ZodSafeParseSuccess<T> = {\n success: true;\n data: T;\n error?: never;\n};\nexport type ZodSafeParseError<T> = {\n success: false;\n data?: never;\n error: ZodError<T>;\n};\nexport declare const parse: <T extends core.$ZodType>(schema: T, value: unknown, _ctx?: core.ParseContext<core.$ZodIssue>, _params?: {\n callee?: core.util.AnyFunc;\n Err?: core.$ZodErrorClass;\n}) => core.output<T>;\nexport declare const parseAsync: <T extends core.$ZodType>(schema: T, value: unknown, _ctx?: core.ParseContext<core.$ZodIssue>, _params?: {\n callee?: core.util.AnyFunc;\n Err?: core.$ZodErrorClass;\n}) => Promise<core.output<T>>;\nexport declare const safeParse: <T extends core.$ZodType>(schema: T, value: unknown, _ctx?: core.ParseContext<core.$ZodIssue>) => ZodSafeParseResult<core.output<T>>;\nexport declare const safeParseAsync: <T extends core.$ZodType>(schema: T, value: unknown, _ctx?: core.ParseContext<core.$ZodIssue>) => Promise<ZodSafeParseResult<core.output<T>>>;\nexport declare const encode: <T extends core.$ZodType>(schema: T, value: core.output<T>, _ctx?: core.ParseContext<core.$ZodIssue>) => core.input<T>;\nexport declare const decode: <T extends core.$ZodType>(schema: T, value: core.input<T>, _ctx?: core.ParseContext<core.$ZodIssue>) => core.output<T>;\nexport declare const encodeAsync: <T extends core.$ZodType>(schema: T, value: core.output<T>, _ctx?: core.ParseContext<core.$ZodIssue>) => Promise<core.input<T>>;\nexport declare const decodeAsync: <T extends core.$ZodType>(schema: T, value: core.input<T>, _ctx?: core.ParseContext<core.$ZodIssue>) => Promise<core.output<T>>;\nexport declare const safeEncode: <T extends core.$ZodType>(schema: T, value: core.output<T>, _ctx?: core.ParseContext<core.$ZodIssue>) => ZodSafeParseResult<core.input<T>>;\nexport declare const safeDecode: <T extends core.$ZodType>(schema: T, value: core.input<T>, _ctx?: core.ParseContext<core.$ZodIssue>) => ZodSafeParseResult<core.output<T>>;\nexport declare const safeEncodeAsync: <T extends core.$ZodType>(schema: T, value: core.output<T>, _ctx?: core.ParseContext<core.$ZodIssue>) => Promise<ZodSafeParseResult<core.input<T>>>;\nexport declare const safeDecodeAsync: <T extends core.$ZodType>(schema: T, value: core.input<T>, _ctx?: core.ParseContext<core.$ZodIssue>) => Promise<ZodSafeParseResult<core.output<T>>>;\n"],"x_google_ignoreList":[0],"mappings":";;;KAEYE,wBAAwBE,oBAAoBD,KAAKE,kBAAkBF;AAAnED,KACAE,mBADkB,CAAAD,CAAAA,CAAAA,GAAAA;EAA0BA,OAAAA,EAAAA,IAAAA;EAApBC,IAAAA,EAG1BD,CAH0BC;EAA2CD,KAAAA,CAAAA,EAAAA,KAAAA;CAAlBE;AAAiB,KAMlEA,iBANkE,CAAA,CAAA,CAAA,GAAA;EAClED,OAAAA,EAAAA,KAAAA;EAKAC,IAAAA,CAAAA,EAAAA,KAAAA;SAGDJ,SAASE"}
@@ -15,7 +15,7 @@ type AvatarImageProps = Omit<React$1.ImgHTMLAttributes<HTMLImageElement>, "src"
15
15
  * Controlled `<img>` that syncs its loading status back to the avatar context
16
16
  * so fallbacks know when to display.
17
17
  */
18
- declare const AvatarImage: React$1.ForwardRefExoticComponent<Omit<React$1.ImgHTMLAttributes<HTMLImageElement>, "alt" | "src"> & {
18
+ declare const AvatarImage: React$1.ForwardRefExoticComponent<Omit<React$1.ImgHTMLAttributes<HTMLImageElement>, "src" | "alt"> & {
19
19
  src: string;
20
20
  alt?: string;
21
21
  asChild?: boolean;
@@ -12,8 +12,16 @@ type SupportBubbleProps = Omit<React$1.ButtonHTMLAttributes<HTMLButtonElement>,
12
12
  className?: string;
13
13
  };
14
14
  /**
15
- * Floating action button that toggles the support window. Exposes widget state
16
- * and unread counts to render-prop children for fully custom UI shells.
15
+ * Floating action button that toggles the support window.
16
+ *
17
+ * @example
18
+ * <Bubble>
19
+ * {({ isOpen, unreadCount, toggle }) => (
20
+ * <button onClick={toggle}>
21
+ * {isOpen ? "×" : "💬"} {unreadCount > 0 && `(${unreadCount})`}
22
+ * </button>
23
+ * )}
24
+ * </Bubble>
17
25
  */
18
26
  declare const SupportBubble: React$1.ForwardRefExoticComponent<Omit<React$1.ButtonHTMLAttributes<HTMLButtonElement>, "children"> & {
19
27
  children?: React$1.ReactNode | ((props: {
@@ -1 +1 @@
1
- {"version":3,"file":"bubble.d.ts","names":[],"sources":["../../src/primitives/bubble.tsx"],"sourcesContent":[],"mappings":";;;KAMY,kBAAA,GAAqB,KAChC,OAAA,CAAM,qBAAqB;aAIxB,OAAA,CAAM;IALE,MAAA,EAAA,OAAA;IACgB,WAAA,EAAA,MAAA;IAA3B,QAAM,EAAA,OAAA;IAD0B,MAAA,EAAA,GAAA,GAAA,IAAA;EAK7B,CAAA,EAAA,GAMM,OAAA,CAAM,SANN,CAAA;EAMA,OAAM,CAAA,EAAA,OAAA;EAAS,SAAA,CAAA,EAAA,MAAA;AASzB,CAAA;;;;;AATU,cASG,aATG,EASU,OAAA,CAAA,yBATV,CASU,IATV,CASU,OAAA,CAAA,oBATV,CASU,iBATV,CAAA,EAAA,UAAA,CAAA,GAAA;aANZ,OAAA,CAAM;;IAegB,WAAA,EAAA,MAAA;IAAA,QAAA,EAAA,OAAA;;QAThB,OAAA,CAAM"}
1
+ {"version":3,"file":"bubble.d.ts","names":[],"sources":["../../src/primitives/bubble.tsx"],"sourcesContent":[],"mappings":";;;KAMY,kBAAA,GAAqB,KAChC,OAAA,CAAM,qBAAqB;aAIxB,OAAA,CAAM;IALE,MAAA,EAAA,OAAA;IACgB,WAAA,EAAA,MAAA;IAA3B,QAAM,EAAA,OAAA;IAD0B,MAAA,EAAA,GAAA,GAAA,IAAA;EAK7B,CAAA,EAAA,GAMM,OAAA,CAAM,SANN,CAAA;EAMA,OAAM,CAAA,EAAA,OAAA;EAAS,SAAA,CAAA,EAAA,MAAA;AAiBzB,CAAA;;;;;;;;;;;;;cAAa,eAAa,OAAA,CAAA,0BAAA,KAAA,OAAA,CAAA,qBAAA;aAvBtB,OAAA,CAAM;;;;;QAMA,OAAA,CAAM"}
@@ -1,13 +1,21 @@
1
- import { useTypingStore } from "../realtime/typing-store.js";
2
1
  import { useRenderElement } from "../utils/use-render-element.js";
2
+ import { useTypingStore } from "../realtime/typing-store.js";
3
3
  import { useSupportConfig } from "../support/store/support-store.js";
4
4
  import { useSupport } from "../provider.js";
5
5
  import * as React$1 from "react";
6
6
 
7
7
  //#region src/primitives/bubble.tsx
8
8
  /**
9
- * Floating action button that toggles the support window. Exposes widget state
10
- * and unread counts to render-prop children for fully custom UI shells.
9
+ * Floating action button that toggles the support window.
10
+ *
11
+ * @example
12
+ * <Bubble>
13
+ * {({ isOpen, unreadCount, toggle }) => (
14
+ * <button onClick={toggle}>
15
+ * {isOpen ? "×" : "💬"} {unreadCount > 0 && `(${unreadCount})`}
16
+ * </button>
17
+ * )}
18
+ * </Bubble>
11
19
  */
12
20
  const SupportBubble = (() => {
13
21
  const Component = React$1.forwardRef(({ children, className, asChild = false,...props }, ref) => {
@@ -1 +1 @@
1
- {"version":3,"file":"bubble.js","names":["React"],"sources":["../../src/primitives/bubble.tsx"],"sourcesContent":["import * as React from \"react\";\nimport { useSupport } from \"../provider\";\nimport { useTypingStore } from \"../realtime/typing-store\";\nimport { useSupportConfig } from \"../support\";\nimport { useRenderElement } from \"../utils/use-render-element\";\n\nexport type SupportBubbleProps = Omit<\n\tReact.ButtonHTMLAttributes<HTMLButtonElement>,\n\t\"children\"\n> & {\n\tchildren?:\n\t\t| React.ReactNode\n\t\t| ((props: {\n\t\t\t\tisOpen: boolean;\n\t\t\t\tunreadCount: number;\n\t\t\t\tisTyping: boolean;\n\t\t\t\ttoggle: () => void;\n\t\t }) => React.ReactNode);\n\tasChild?: boolean;\n\tclassName?: string;\n};\n\n/**\n * Floating action button that toggles the support window. Exposes widget state\n * and unread counts to render-prop children for fully custom UI shells.\n */\nexport const SupportBubble = (() => {\n\tconst Component = React.forwardRef<HTMLButtonElement, SupportBubbleProps>(\n\t\t({ children, className, asChild = false, ...props }, ref) => {\n\t\t\tconst { isOpen, toggle } = useSupportConfig();\n\t\t\tconst { unreadCount, visitor } = useSupport();\n\t\t\tconst visitorId = visitor?.id ?? null;\n\n\t\t\tconst hasTyping = useTypingStore(\n\t\t\t\tReact.useCallback(\n\t\t\t\t\t(state) =>\n\t\t\t\t\t\tObject.values(state.conversations).some((entries) =>\n\t\t\t\t\t\t\tObject.values(entries).some((entry) => {\n\t\t\t\t\t\t\t\tif (\n\t\t\t\t\t\t\t\t\tvisitorId &&\n\t\t\t\t\t\t\t\t\tentry.actorType === \"visitor\" &&\n\t\t\t\t\t\t\t\t\tentry.actorId === visitorId\n\t\t\t\t\t\t\t\t) {\n\t\t\t\t\t\t\t\t\treturn false;\n\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\treturn true;\n\t\t\t\t\t\t\t})\n\t\t\t\t\t\t),\n\t\t\t\t\t[visitorId]\n\t\t\t\t)\n\t\t\t);\n\n\t\t\tconst renderProps = {\n\t\t\t\tisOpen,\n\t\t\t\tunreadCount,\n\t\t\t\tisTyping: hasTyping,\n\t\t\t\ttoggle,\n\t\t\t};\n\n\t\t\tconst content =\n\t\t\t\ttypeof children === \"function\" ? children(renderProps) : children;\n\n\t\t\treturn useRenderElement(\n\t\t\t\t\"button\",\n\t\t\t\t{\n\t\t\t\t\tasChild,\n\t\t\t\t\tclassName,\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tref,\n\t\t\t\t\tstate: renderProps,\n\t\t\t\t\tprops: {\n\t\t\t\t\t\ttype: \"button\",\n\t\t\t\t\t\t\"aria-haspopup\": \"dialog\",\n\t\t\t\t\t\t\"aria-expanded\": isOpen,\n\t\t\t\t\t\tonClick: toggle,\n\t\t\t\t\t\t...props,\n\t\t\t\t\t\tchildren: content,\n\t\t\t\t\t},\n\t\t\t\t}\n\t\t\t);\n\t\t}\n\t);\n\n\tComponent.displayName = \"SupportBubble\";\n\treturn Component;\n})();\n"],"mappings":";;;;;;;;;;;AA0BA,MAAa,uBAAuB;CACnC,MAAM,YAAYA,QAAM,YACtB,EAAE,UAAU,WAAW,UAAU,MAAO,GAAG,SAAS,QAAQ;EAC5D,MAAM,EAAE,QAAQ,WAAW,kBAAkB;EAC7C,MAAM,EAAE,aAAa,YAAY,YAAY;EAC7C,MAAM,YAAY,SAAS,MAAM;EAsBjC,MAAM,cAAc;GACnB;GACA;GACA,UAvBiB,eACjBA,QAAM,aACJ,UACA,OAAO,OAAO,MAAM,cAAc,CAAC,MAAM,YACxC,OAAO,OAAO,QAAQ,CAAC,MAAM,UAAU;AACtC,QACC,aACA,MAAM,cAAc,aACpB,MAAM,YAAY,UAElB,QAAO;AAGR,WAAO;KACN,CACF,EACF,CAAC,UAAU,CACX,CACD;GAMA;GACA;EAED,MAAM,UACL,OAAO,aAAa,aAAa,SAAS,YAAY,GAAG;AAE1D,SAAO,iBACN,UACA;GACC;GACA;GACA,EACD;GACC;GACA,OAAO;GACP,OAAO;IACN,MAAM;IACN,iBAAiB;IACjB,iBAAiB;IACjB,SAAS;IACT,GAAG;IACH,UAAU;IACV;GACD,CACD;GAEF;AAED,WAAU,cAAc;AACxB,QAAO;IACJ"}
1
+ {"version":3,"file":"bubble.js","names":["React"],"sources":["../../src/primitives/bubble.tsx"],"sourcesContent":["import * as React from \"react\";\nimport { useSupport } from \"../provider\";\nimport { useTypingStore } from \"../realtime/typing-store\";\nimport { useSupportConfig } from \"../support\";\nimport { useRenderElement } from \"../utils/use-render-element\";\n\nexport type SupportBubbleProps = Omit<\n\tReact.ButtonHTMLAttributes<HTMLButtonElement>,\n\t\"children\"\n> & {\n\tchildren?:\n\t\t| React.ReactNode\n\t\t| ((props: {\n\t\t\t\tisOpen: boolean;\n\t\t\t\tunreadCount: number;\n\t\t\t\tisTyping: boolean;\n\t\t\t\ttoggle: () => void;\n\t\t }) => React.ReactNode);\n\tasChild?: boolean;\n\tclassName?: string;\n};\n\n/**\n * Floating action button that toggles the support window.\n *\n * @example\n * <Bubble>\n * {({ isOpen, unreadCount, toggle }) => (\n * <button onClick={toggle}>\n * {isOpen ? \"×\" : \"💬\"} {unreadCount > 0 && `(${unreadCount})`}\n * </button>\n * )}\n * </Bubble>\n */\nexport const SupportBubble = (() => {\n\tconst Component = React.forwardRef<HTMLButtonElement, SupportBubbleProps>(\n\t\t({ children, className, asChild = false, ...props }, ref) => {\n\t\t\tconst { isOpen, toggle } = useSupportConfig();\n\t\t\tconst { unreadCount, visitor } = useSupport();\n\t\t\tconst visitorId = visitor?.id ?? null;\n\n\t\t\tconst hasTyping = useTypingStore(\n\t\t\t\tReact.useCallback(\n\t\t\t\t\t(state) =>\n\t\t\t\t\t\tObject.values(state.conversations).some((entries) =>\n\t\t\t\t\t\t\tObject.values(entries).some((entry) => {\n\t\t\t\t\t\t\t\tif (\n\t\t\t\t\t\t\t\t\tvisitorId &&\n\t\t\t\t\t\t\t\t\tentry.actorType === \"visitor\" &&\n\t\t\t\t\t\t\t\t\tentry.actorId === visitorId\n\t\t\t\t\t\t\t\t) {\n\t\t\t\t\t\t\t\t\treturn false;\n\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\treturn true;\n\t\t\t\t\t\t\t})\n\t\t\t\t\t\t),\n\t\t\t\t\t[visitorId]\n\t\t\t\t)\n\t\t\t);\n\n\t\t\tconst renderProps = {\n\t\t\t\tisOpen,\n\t\t\t\tunreadCount,\n\t\t\t\tisTyping: hasTyping,\n\t\t\t\ttoggle,\n\t\t\t};\n\n\t\t\tconst content =\n\t\t\t\ttypeof children === \"function\" ? children(renderProps) : children;\n\n\t\t\treturn useRenderElement(\n\t\t\t\t\"button\",\n\t\t\t\t{\n\t\t\t\t\tasChild,\n\t\t\t\t\tclassName,\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tref,\n\t\t\t\t\tstate: renderProps,\n\t\t\t\t\tprops: {\n\t\t\t\t\t\ttype: \"button\",\n\t\t\t\t\t\t\"aria-haspopup\": \"dialog\",\n\t\t\t\t\t\t\"aria-expanded\": isOpen,\n\t\t\t\t\t\tonClick: toggle,\n\t\t\t\t\t\t...props,\n\t\t\t\t\t\tchildren: content,\n\t\t\t\t\t},\n\t\t\t\t}\n\t\t\t);\n\t\t}\n\t);\n\n\tComponent.displayName = \"SupportBubble\";\n\treturn Component;\n})();\n"],"mappings":";;;;;;;;;;;;;;;;;;;AAkCA,MAAa,uBAAuB;CACnC,MAAM,YAAYA,QAAM,YACtB,EAAE,UAAU,WAAW,UAAU,MAAO,GAAG,SAAS,QAAQ;EAC5D,MAAM,EAAE,QAAQ,WAAW,kBAAkB;EAC7C,MAAM,EAAE,aAAa,YAAY,YAAY;EAC7C,MAAM,YAAY,SAAS,MAAM;EAsBjC,MAAM,cAAc;GACnB;GACA;GACA,UAvBiB,eACjBA,QAAM,aACJ,UACA,OAAO,OAAO,MAAM,cAAc,CAAC,MAAM,YACxC,OAAO,OAAO,QAAQ,CAAC,MAAM,UAAU;AACtC,QACC,aACA,MAAM,cAAc,aACpB,MAAM,YAAY,UAElB,QAAO;AAGR,WAAO;KACN,CACF,EACF,CAAC,UAAU,CACX,CACD;GAMA;GACA;EAED,MAAM,UACL,OAAO,aAAa,aAAa,SAAS,YAAY,GAAG;AAE1D,SAAO,iBACN,UACA;GACC;GACA;GACA,EACD;GACC;GACA,OAAO;GACP,OAAO;IACN,MAAM;IACN,iBAAiB;IACjB,iBAAiB;IACjB,SAAS;IACT,GAAG;IACH,UAAU;IACV;GACD,CACD;GAEF;AAED,WAAU,cAAc;AACxB,QAAO;IACJ"}
@@ -1 +1 @@
1
- {"version":3,"file":"conversation-timeline.d.ts","names":[],"sources":["../../src/primitives/conversation-timeline.tsx"],"sourcesContent":[],"mappings":";;;;;;;AAQA;AAOA;AACsB,KARV,+BAAA,GAQU;EAArB,SAAM,EAAA,MAAA;EADiC,SAAA,CAAA,EAAA,OAAA;EAKpC,OAAM,CAAA,EAAA,OAAA;EACG,OAAA,EAAA,OAAA;CAAoC;AAGxC,KATG,yBAAA,GAA4B,IAS/B,CARR,OAAA,CAAM,cAQE,CARa,cAQb,CAAA,EAAA,UAAA,CAAA,GAAA;EAAgB,QAAA,CAAA,EAJrB,OAAA,CAAM,SAIe,GAAA,CAAA,CAAA,KAAA,EAHZ,+BAGY,EAAA,GAHwB,OAAA,CAAM,SAG9B,CAAA;EA6BZ,OAAA,CAAA,EAAA,OAAA;EAAoB,SAAA,CAAA,EAAA,MAAA;EAAA,KAAA,CAAA,EA7BxB,YA6BwB,EAAA;EAAA,SAAA,CAAA,EAAA,OAAA;EAjC7B,OAAM,CAAA,EAAA,OAAA;EACG,UAAA,CAAA,EAAA,OAAA;EAAoC,WAAM,CAAA,EAAA,GAAA,GAAA,IAAA;EAG9C,aAAA,CAAA,EAAA,GAAA,GAAA,IAAA;;;;;AA+LT;AACsB,cAnKT,oBAmKS,EAnKW,OAAA,CAAA,yBAmKX,CAnKW,IAmKX,CAnKW,OAAA,CAAA,cAmKX,CAnKW,cAmKX,CAAA,EAAA,UAAA,CAAA,GAAA;EAArB,QAAM,CAAA,EApMH,OAAA,CAAM,SAoMH,GAAA,CAAA,CAAA,KAAA,EAnMM,+BAmMN,EAAA,GAnM0C,OAAA,CAAM,SAmMhD,CAAA;EAD0C,OAAA,CAAA,EAAA,OAAA;EAIrC,SAAM,CAAA,EAAA,MAAA;EAAS,KAAA,CAAA,EAnMlB,YAmMkB,EAAA;EASd,SAAA,CAAA,EAAA,OAAA;EAA6B,OAAA,CAAA,EAAA,OAAA;EAAA,UAAA,CAAA,EAAA,OAAA;EAAA,WAAA,CAAA,EAAA,GAAA,GAAA,IAAA;EAT9B,aAAM,CAAA,EAAA,GAAA,GAAA,IAAA;;KAJN,kCAAA,GAAqC,KAChD,OAAA,CAAM,eAAe;EAYoB,QAAA,CAAA,EAT9B,OAAA,CAAM,SASwB;EAAA,OAAA,CAAA,EAAA,OAAA;EAyB9B,SAAA,CAAA,EAAA,MAAA;CACU;;;;;AAYT,cAtCA,6BA+DT,EA/DsC,OAAA,CAAA,yBA+DtC,CA/DsC,IA+DtC,CA/DsC,OAAA,CAAA,cA+DtC,CA/DsC,cA+DtC,CAAA,EAAA,UAAA,CAAA,GAAA;EAzBoC,QAAA,CAAA,EA/C5B,OAAA,CAAM,SA+CsB;EAAA,OAAA,CAAA,EAAA,OAAA;EAAA,SAAA,CAAA,EAAA,MAAA;CAT5B,wBAAM,eAAA,CAAA,CAAA;KAJN,gCAAA,GAAmC,KAC9C,OAAA,CAAM,eAAe;aAGV,OAAA,CAAM;EASsB,OAAA,CAAA,EAAA,OAAA;EAAA,SAAA,CAAA,EAAA,MAAA;AA2BxC,CAAA;;;;;AAI2B,cA/Bd,2BA+Bc,EA/Ba,OAAA,CAAA,yBA+Bb,CA/Ba,IA+Bb,CA/Ba,OAAA,CAAA,cA+Bb,CA/Ba,cA+Bb,CAAA,EAAA,UAAA,CAAA,GAAA;EASd,QAAA,CAAA,EAjDD,OAAA,CAAM,SAiDL;EAAyB,OAAA,CAAA,EAAA,OAAA;EAAA,SAAA,CAAA,EAAA,MAAA;CAAA,wBAAA,eAAA,CAAA,CAAA;AAT1B,KAJA,8BAAA,GAAiC,IAI3B,CAHjB,OAAA,CAAM,cAGW,CAHI,cAGJ,CAAA,EAAA,UAAA,CAAA,GAAA;aAAN,OAAA,CAAM;;EASoB,SAAA,CAAA,EAAA,MAAA;CAAA;;;;;cAAzB,2BAAyB,OAAA,CAAA,0BAAA,KAAA,OAAA,CAAA,eAAA;aAT1B,OAAA,CAAM"}
1
+ {"version":3,"file":"conversation-timeline.d.ts","names":[],"sources":["../../src/primitives/conversation-timeline.tsx"],"sourcesContent":[],"mappings":";;;;;;;AASA;AAOA;AACsB,KARV,+BAAA,GAQU;EAArB,SAAM,EAAA,MAAA;EADiC,SAAA,CAAA,EAAA,OAAA;EAKpC,OAAM,CAAA,EAAA,OAAA;EACG,OAAA,EAAA,OAAA;CAAoC;AAGxC,KATG,yBAAA,GAA4B,IAS/B,CARR,OAAA,CAAM,cAQE,CARa,cAQb,CAAA,EAAA,UAAA,CAAA,GAAA;EAAgB,QAAA,CAAA,EAJrB,OAAA,CAAM,SAIe,GAAA,CAAA,CAAA,KAAA,EAHZ,+BAGY,EAAA,GAHwB,OAAA,CAAM,SAG9B,CAAA;EA6BZ,OAAA,CAAA,EAAA,OAAA;EAAoB,SAAA,CAAA,EAAA,MAAA;EAAA,KAAA,CAAA,EA7BxB,YA6BwB,EAAA;EAAA,SAAA,CAAA,EAAA,OAAA;EAjC7B,OAAM,CAAA,EAAA,OAAA;EACG,UAAA,CAAA,EAAA,OAAA;EAAoC,WAAM,CAAA,EAAA,GAAA,GAAA,IAAA;EAG9C,aAAA,CAAA,EAAA,GAAA,GAAA,IAAA;;;;;AAmLT;AACsB,cAvJT,oBAuJS,EAvJW,OAAA,CAAA,yBAuJX,CAvJW,IAuJX,CAvJW,OAAA,CAAA,cAuJX,CAvJW,cAuJX,CAAA,EAAA,UAAA,CAAA,GAAA;EAArB,QAAM,CAAA,EAxLH,OAAA,CAAM,SAwLH,GAAA,CAAA,CAAA,KAAA,EAvLM,+BAuLN,EAAA,GAvL0C,OAAA,CAAM,SAuLhD,CAAA;EAD0C,OAAA,CAAA,EAAA,OAAA;EAIrC,SAAM,CAAA,EAAA,MAAA;EAAS,KAAA,CAAA,EAvLlB,YAuLkB,EAAA;EASd,SAAA,CAAA,EAAA,OAAA;EAA6B,OAAA,CAAA,EAAA,OAAA;EAAA,UAAA,CAAA,EAAA,OAAA;EAAA,WAAA,CAAA,EAAA,GAAA,GAAA,IAAA;EAT9B,aAAM,CAAA,EAAA,GAAA,GAAA,IAAA;;KAJN,kCAAA,GAAqC,KAChD,OAAA,CAAM,eAAe;EAYoB,QAAA,CAAA,EAT9B,OAAA,CAAM,SASwB;EAAA,OAAA,CAAA,EAAA,OAAA;EAyB9B,SAAA,CAAA,EAAA,MAAA;CACU;;;;;AAYT,cAtCA,6BA+DT,EA/DsC,OAAA,CAAA,yBA+DtC,CA/DsC,IA+DtC,CA/DsC,OAAA,CAAA,cA+DtC,CA/DsC,cA+DtC,CAAA,EAAA,UAAA,CAAA,GAAA;EAzBoC,QAAA,CAAA,EA/C5B,OAAA,CAAM,SA+CsB;EAAA,OAAA,CAAA,EAAA,OAAA;EAAA,SAAA,CAAA,EAAA,MAAA;CAT5B,wBAAM,eAAA,CAAA,CAAA;KAJN,gCAAA,GAAmC,KAC9C,OAAA,CAAM,eAAe;aAGV,OAAA,CAAM;EASsB,OAAA,CAAA,EAAA,OAAA;EAAA,SAAA,CAAA,EAAA,MAAA;AA2BxC,CAAA;;;;;AAI2B,cA/Bd,2BA+Bc,EA/Ba,OAAA,CAAA,yBA+Bb,CA/Ba,IA+Bb,CA/Ba,OAAA,CAAA,cA+Bb,CA/Ba,cA+Bb,CAAA,EAAA,UAAA,CAAA,GAAA;EASd,QAAA,CAAA,EAjDD,OAAA,CAAM,SAiDL;EAAyB,OAAA,CAAA,EAAA,OAAA;EAAA,SAAA,CAAA,EAAA,MAAA;CAAA,wBAAA,eAAA,CAAA,CAAA;AAT1B,KAJA,8BAAA,GAAiC,IAI3B,CAHjB,OAAA,CAAM,cAGW,CAHI,cAGJ,CAAA,EAAA,UAAA,CAAA,GAAA;aAAN,OAAA,CAAM;;EASoB,SAAA,CAAA,EAAA,MAAA;CAAA;;;;;cAAzB,2BAAyB,OAAA,CAAA,0BAAA,KAAA,OAAA,CAAA,eAAA;aAT1B,OAAA,CAAM"}
@@ -1,4 +1,5 @@
1
1
  import { useRenderElement } from "../utils/use-render-element.js";
2
+ import { useScrollMask } from "../hooks/use-scroll-mask.js";
2
3
  import * as React$1 from "react";
3
4
 
4
5
  //#region src/primitives/conversation-timeline.tsx
@@ -17,11 +18,18 @@ function getLastItemKey(items) {
17
18
  const ConversationTimeline = (() => {
18
19
  const Component = React$1.forwardRef(({ children, className, asChild = false, items = [], isLoading = false, hasMore = false, autoScroll = true, onScrollEnd, onScrollStart,...props }, ref) => {
19
20
  const internalRef = React$1.useRef(null);
21
+ const { ref: scrollMaskRef, style: scrollMaskStyle } = useScrollMask({
22
+ maskHeight: "54px",
23
+ scrollbarWidth: "8px",
24
+ topThreshold: TOP_THRESHOLD_PX,
25
+ bottomThreshold: BOTTOM_THRESHOLD_PX
26
+ });
20
27
  const setRefs = React$1.useCallback((node) => {
21
28
  internalRef.current = node;
29
+ scrollMaskRef.current = node;
22
30
  if (typeof ref === "function") ref(node);
23
31
  else if (ref) ref.current = node;
24
- }, [ref]);
32
+ }, [ref, scrollMaskRef]);
25
33
  const isInitialRender = React$1.useRef(true);
26
34
  const previousItemCount = React$1.useRef(items.length);
27
35
  const previousLastItemKey = React$1.useRef(getLastItemKey(items));
@@ -69,24 +77,6 @@ const ConversationTimeline = (() => {
69
77
  if (atTop && !isAtTop.current) onScrollStart?.();
70
78
  isAtTop.current = atTop;
71
79
  }, [onScrollStart, onScrollEnd]);
72
- const fadeStyle = React$1.useMemo(() => {
73
- const maskHeight = "90px";
74
- const scrollbarWidth = "8px";
75
- const maskImage = `linear-gradient(to bottom, transparent, black ${maskHeight}, black calc(100% - ${maskHeight}), transparent), linear-gradient(black, black)`;
76
- const maskSize = `calc(100% - ${scrollbarWidth}) 100%, ${scrollbarWidth} 100%`;
77
- const maskPosition = "0 0, 100% 0";
78
- const maskRepeat = "no-repeat, no-repeat";
79
- return {
80
- maskImage,
81
- maskSize,
82
- maskPosition,
83
- maskRepeat,
84
- WebkitMaskImage: maskImage,
85
- WebkitMaskSize: maskSize,
86
- WebkitMaskPosition: maskPosition,
87
- WebkitMaskRepeat: maskRepeat
88
- };
89
- }, []);
90
80
  return useRenderElement("div", {
91
81
  className,
92
82
  asChild
@@ -99,7 +89,7 @@ const ConversationTimeline = (() => {
99
89
  "aria-live": "polite",
100
90
  "aria-relevant": "additions",
101
91
  onScroll: handleScroll,
102
- style: fadeStyle,
92
+ style: scrollMaskStyle,
103
93
  ...props,
104
94
  children: content
105
95
  }