@cossistant/react 0.0.25 → 0.0.28

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 (225) hide show
  1. package/api.d.ts +1 -1
  2. package/api.d.ts.map +1 -1
  3. package/checks.d.ts +1 -1
  4. package/checks.d.ts.map +1 -1
  5. package/clsx.d.ts +1 -1
  6. package/clsx.d.ts.map +1 -1
  7. package/coerce.d.ts +1 -1
  8. package/coerce.d.ts.map +1 -1
  9. package/conversation.d.ts +3 -0
  10. package/conversation.d.ts.map +1 -1
  11. package/core.d.ts +1 -1
  12. package/core.d.ts.map +1 -1
  13. package/errors.d.ts +1 -1
  14. package/errors.d.ts.map +1 -1
  15. package/errors2.d.ts +1 -1
  16. package/errors2.d.ts.map +1 -1
  17. package/hooks/index.d.ts +2 -1
  18. package/hooks/index.js +6 -5
  19. package/hooks/private/store/use-website-store.js +2 -1
  20. package/hooks/private/store/use-website-store.js.map +1 -1
  21. package/hooks/private/use-client-query.d.ts +6 -0
  22. package/hooks/private/use-client-query.d.ts.map +1 -1
  23. package/hooks/private/use-client-query.js +26 -3
  24. package/hooks/private/use-client-query.js.map +1 -1
  25. package/hooks/private/use-multimodal-input.d.ts.map +1 -1
  26. package/hooks/private/use-multimodal-input.js +7 -5
  27. package/hooks/private/use-multimodal-input.js.map +1 -1
  28. package/hooks/private/use-visitor-typing-reporter.d.ts +18 -1
  29. package/hooks/private/use-visitor-typing-reporter.d.ts.map +1 -1
  30. package/hooks/private/use-visitor-typing-reporter.js +34 -4
  31. package/hooks/private/use-visitor-typing-reporter.js.map +1 -1
  32. package/hooks/use-conversation-page.d.ts +1 -0
  33. package/hooks/use-conversation-page.d.ts.map +1 -1
  34. package/hooks/use-conversation-page.js +6 -1
  35. package/hooks/use-conversation-page.js.map +1 -1
  36. package/hooks/use-conversation-preview.d.ts +2 -1
  37. package/hooks/use-conversation-preview.d.ts.map +1 -1
  38. package/hooks/use-conversation-preview.js +1 -1
  39. package/hooks/use-conversation-preview.js.map +1 -1
  40. package/hooks/use-conversation-timeline-items.js +2 -1
  41. package/hooks/use-conversation-timeline-items.js.map +1 -1
  42. package/hooks/use-conversation.js +2 -1
  43. package/hooks/use-conversation.js.map +1 -1
  44. package/hooks/use-conversations.js +1 -0
  45. package/hooks/use-conversations.js.map +1 -1
  46. package/hooks/use-file-upload.d.ts +55 -0
  47. package/hooks/use-file-upload.d.ts.map +1 -0
  48. package/hooks/use-file-upload.js +100 -0
  49. package/hooks/use-file-upload.js.map +1 -0
  50. package/hooks/use-message-composer.d.ts +11 -0
  51. package/hooks/use-message-composer.d.ts.map +1 -1
  52. package/hooks/use-message-composer.js +7 -3
  53. package/hooks/use-message-composer.js.map +1 -1
  54. package/hooks/use-realtime-support.d.ts.map +1 -1
  55. package/hooks/use-send-message.d.ts +1 -0
  56. package/hooks/use-send-message.d.ts.map +1 -1
  57. package/hooks/use-send-message.js +63 -11
  58. package/hooks/use-send-message.js.map +1 -1
  59. package/index.d.ts +6 -3
  60. package/index.js +13 -10
  61. package/openapi30.d.ts +1 -1
  62. package/openapi30.d.ts.map +1 -1
  63. package/openapi31.d.ts +1 -1
  64. package/openapi31.d.ts.map +1 -1
  65. package/package.json +4 -3
  66. package/parse.d.ts +1 -1
  67. package/parse.d.ts.map +1 -1
  68. package/primitives/conversation-timeline.d.ts.map +1 -1
  69. package/primitives/conversation-timeline.js +10 -5
  70. package/primitives/conversation-timeline.js.map +1 -1
  71. package/primitives/index.d.ts +4 -3
  72. package/primitives/index.js +12 -5
  73. package/primitives/index.parts.d.ts +3 -2
  74. package/primitives/index.parts.js +4 -3
  75. package/primitives/timeline-item-attachments.d.ts +100 -0
  76. package/primitives/timeline-item-attachments.d.ts.map +1 -0
  77. package/primitives/timeline-item-attachments.js +151 -0
  78. package/primitives/timeline-item-attachments.js.map +1 -0
  79. package/primitives/trigger.d.ts +91 -0
  80. package/primitives/trigger.d.ts.map +1 -0
  81. package/primitives/trigger.js +74 -0
  82. package/primitives/trigger.js.map +1 -0
  83. package/primitives/window.d.ts +22 -1
  84. package/primitives/window.d.ts.map +1 -1
  85. package/primitives/window.js +91 -5
  86. package/primitives/window.js.map +1 -1
  87. package/provider.d.ts.map +1 -1
  88. package/provider.js +8 -3
  89. package/provider.js.map +1 -1
  90. package/realtime/index.js +1 -1
  91. package/realtime/provider.js +1 -1
  92. package/realtime/support-provider.js +1 -1
  93. package/realtime/support-provider.js.map +1 -1
  94. package/realtime-events.d.ts +39 -0
  95. package/realtime-events.d.ts.map +1 -1
  96. package/registries.d.ts +1 -1
  97. package/registries.d.ts.map +1 -1
  98. package/schemas.d.ts +1 -1
  99. package/schemas.d.ts.map +1 -1
  100. package/schemas2.d.ts +1 -1
  101. package/schemas2.d.ts.map +1 -1
  102. package/schemas3.d.ts +1 -0
  103. package/schemas3.d.ts.map +1 -1
  104. package/specification-extension.d.ts +1 -1
  105. package/specification-extension.d.ts.map +1 -1
  106. package/standard-schema.d.ts +1 -1
  107. package/standard-schema.d.ts.map +1 -1
  108. package/support/components/button.d.ts +1 -1
  109. package/support/components/content.d.ts +30 -0
  110. package/support/components/content.d.ts.map +1 -0
  111. package/support/components/content.js +282 -0
  112. package/support/components/content.js.map +1 -0
  113. package/support/components/conversation-button-link.js +1 -1
  114. package/support/components/conversation-timeline.js +3 -3
  115. package/support/components/conversation-timeline.js.map +1 -1
  116. package/support/components/header.js +1 -1
  117. package/support/components/image-lightbox.d.ts +49 -0
  118. package/support/components/image-lightbox.d.ts.map +1 -0
  119. package/support/components/image-lightbox.js +142 -0
  120. package/support/components/image-lightbox.js.map +1 -0
  121. package/support/components/index.d.ts +5 -4
  122. package/support/components/index.js +4 -4
  123. package/support/components/multimodal-input.d.ts +4 -1
  124. package/support/components/multimodal-input.d.ts.map +1 -1
  125. package/support/components/multimodal-input.js +71 -45
  126. package/support/components/multimodal-input.js.map +1 -1
  127. package/support/components/navigation-tab.js +1 -1
  128. package/support/components/root.d.ts +23 -0
  129. package/support/components/root.d.ts.map +1 -0
  130. package/support/components/root.js +36 -0
  131. package/support/components/root.js.map +1 -0
  132. package/support/components/timeline-message-item.d.ts.map +1 -1
  133. package/support/components/timeline-message-item.js +82 -18
  134. package/support/components/timeline-message-item.js.map +1 -1
  135. package/support/components/trigger.d.ts +14 -0
  136. package/support/components/trigger.d.ts.map +1 -0
  137. package/support/components/{bubble.js → trigger.js} +16 -12
  138. package/support/components/trigger.js.map +1 -0
  139. package/support/components/typing-indicator.d.ts.map +1 -1
  140. package/support/components/typing-indicator.js +1 -0
  141. package/support/components/typing-indicator.js.map +1 -1
  142. package/support/context/controlled-state.d.ts +46 -0
  143. package/support/context/controlled-state.d.ts.map +1 -0
  144. package/support/context/controlled-state.js +34 -0
  145. package/support/context/controlled-state.js.map +1 -0
  146. package/support/context/events.d.ts +103 -0
  147. package/support/context/events.d.ts.map +1 -0
  148. package/support/context/events.js +139 -0
  149. package/support/context/events.js.map +1 -0
  150. package/support/context/handle.d.ts +90 -0
  151. package/support/context/handle.d.ts.map +1 -0
  152. package/support/context/handle.js +79 -0
  153. package/support/context/handle.js.map +1 -0
  154. package/support/context/positioning.d.ts +17 -0
  155. package/support/context/positioning.d.ts.map +1 -0
  156. package/support/context/positioning.js +26 -0
  157. package/support/context/positioning.js.map +1 -0
  158. package/support/context/slots.d.ts +85 -0
  159. package/support/context/slots.d.ts.map +1 -0
  160. package/support/context/slots.js +115 -0
  161. package/support/context/slots.js.map +1 -0
  162. package/support/context/websocket.d.ts +8 -1
  163. package/support/context/websocket.d.ts.map +1 -1
  164. package/support/context/websocket.js +8 -1
  165. package/support/context/websocket.js.map +1 -1
  166. package/support/index.d.ts +239 -54
  167. package/support/index.d.ts.map +1 -1
  168. package/support/index.js +254 -33
  169. package/support/index.js.map +1 -1
  170. package/support/pages/articles.d.ts.map +1 -1
  171. package/support/pages/articles.js +3 -4
  172. package/support/pages/articles.js.map +1 -1
  173. package/support/pages/conversation-history.js +2 -2
  174. package/support/pages/conversation.js +6 -5
  175. package/support/pages/conversation.js.map +1 -1
  176. package/support/pages/home.js +2 -2
  177. package/support/router.d.ts +52 -12
  178. package/support/router.d.ts.map +1 -1
  179. package/support/router.js +78 -30
  180. package/support/router.js.map +1 -1
  181. package/support/store/index.d.ts +2 -2
  182. package/support/store/support-store.d.ts +26 -20
  183. package/support/store/support-store.d.ts.map +1 -1
  184. package/support/store/support-store.js +47 -6
  185. package/support/store/support-store.js.map +1 -1
  186. package/support/{support-D2EgfIts.css → support-C7Xaw-N6.css} +1 -2
  187. package/support/support-C7Xaw-N6.css.map +1 -0
  188. package/support/text/index.js.map +1 -1
  189. package/support/types.d.ts +75 -12
  190. package/support/types.d.ts.map +1 -1
  191. package/support.css +2 -2
  192. package/tailwind.css +0 -1
  193. package/timeline-item.d.ts +68 -2
  194. package/timeline-item.d.ts.map +1 -1
  195. package/util.d.ts +1 -1
  196. package/util.d.ts.map +1 -1
  197. package/utils/index.d.ts +2 -1
  198. package/utils/index.js +2 -1
  199. package/utils/merge-refs.d.ts +30 -0
  200. package/utils/merge-refs.d.ts.map +1 -0
  201. package/utils/merge-refs.js +46 -0
  202. package/utils/merge-refs.js.map +1 -0
  203. package/utils/use-render-element.d.ts.map +1 -1
  204. package/utils/use-render-element.js +20 -7
  205. package/utils/use-render-element.js.map +1 -1
  206. package/versions.d.ts +1 -1
  207. package/versions.d.ts.map +1 -1
  208. package/zod-extensions.d.ts +1 -1
  209. package/zod-extensions.d.ts.map +1 -1
  210. package/primitives/bubble.d.ts +0 -38
  211. package/primitives/bubble.d.ts.map +0 -1
  212. package/primitives/bubble.js +0 -57
  213. package/primitives/bubble.js.map +0 -1
  214. package/support/components/bubble.d.ts +0 -10
  215. package/support/components/bubble.d.ts.map +0 -1
  216. package/support/components/bubble.js.map +0 -1
  217. package/support/components/container.d.ts +0 -13
  218. package/support/components/container.d.ts.map +0 -1
  219. package/support/components/container.js +0 -109
  220. package/support/components/container.js.map +0 -1
  221. package/support/components/support-content.d.ts +0 -22
  222. package/support/components/support-content.d.ts.map +0 -1
  223. package/support/components/support-content.js +0 -48
  224. package/support/components/support-content.js.map +0 -1
  225. package/support/support-D2EgfIts.css.map +0 -1
@@ -1,12 +1,12 @@
1
1
  import { TypingIndicator } from "./typing-indicator.js";
2
- import { Container } from "./container.js";
2
+ import { Content } from "./content.js";
3
+ import { Root } from "./root.js";
3
4
  import icons_default from "./icons.js";
5
+ import { DefaultTrigger } from "./trigger.js";
4
6
  import { ConversationEvent } from "./conversation-event.js";
5
7
  import { TimelineMessageItem } from "./timeline-message-item.js";
6
8
  import { TimelineMessageGroup } from "./timeline-message-group.js";
7
9
  import { ConversationTimelineList } from "./conversation-timeline.js";
8
10
  import { SendButton } from "./multimodal-input.js";
9
- import { Bubble } from "./bubble.js";
10
- import { SupportContent } from "./support-content.js";
11
11
 
12
- export { Bubble, Container, ConversationEvent, ConversationTimelineList, icons_default as Icon, SendButton, SupportContent, TimelineMessageGroup, TimelineMessageItem, TypingIndicator };
12
+ export { Content, ConversationEvent, ConversationTimelineList, DefaultTrigger, icons_default as Icon, Root, SendButton, TimelineMessageGroup, TimelineMessageItem, TypingIndicator };
@@ -10,17 +10,20 @@ type MultimodalInputProps = {
10
10
  placeholder?: string;
11
11
  disabled?: boolean;
12
12
  isSubmitting?: boolean;
13
+ isUploading?: boolean;
14
+ uploadProgress?: number;
13
15
  error?: Error | null;
14
16
  files?: File[];
15
17
  onRemoveFile?: (index: number) => void;
16
18
  maxFiles?: number;
17
19
  maxFileSize?: number;
18
- allowedFileTypes?: string[];
20
+ allowedFileTypes?: string;
19
21
  };
20
22
  declare const MultimodalInput: React.FC<MultimodalInputProps>;
21
23
  type SendButtonProps = {
22
24
  className?: string;
23
25
  disabled?: boolean;
26
+ isUploading?: boolean;
24
27
  };
25
28
  declare const SendButton: React.FC<SendButtonProps>;
26
29
  //#endregion
@@ -1 +1 @@
1
- {"version":3,"file":"multimodal-input.d.ts","names":[],"sources":["../../../src/support/components/multimodal-input.tsx"],"sourcesContent":[],"mappings":";;;KAWY,oBAAA;;EAAA,KAAA,EAAA,MAAA;EAKY,QAAA,EAAA,CAAA,KAAA,EAAA,MAAA,EAAA,GAAA,IAAA;EAIf,QAAA,EAAA,GAAA,GAAA,IAAA;EACA,YAAA,CAAA,EAAA,CAAA,KAAA,EALe,IAKf,EAAA,EAAA,GAAA,IAAA;EAAI,WAAA,CAAA,EAAA,MAAA;EAOA,QAAA,CAAA,EAAA,OAoKZ;EAEW,YAAA,CAAA,EAAA,OAAe;EAKd,KAAA,CAAA,EAnLJ,KAiMR,GAAA,IAAA;UAhMQ;;;;;;cAOI,iBAAiB,KAAA,CAAM,GAAG;KAsK3B,eAAA;;;;cAKC,YAAY,KAAA,CAAM,GAAG"}
1
+ {"version":3,"file":"multimodal-input.d.ts","names":[],"sources":["../../../src/support/components/multimodal-input.tsx"],"sourcesContent":[],"mappings":";;;KAiBY,oBAAA;;EAAA,KAAA,EAAA,MAAA;EAKY,QAAA,EAAA,CAAA,KAAA,EAAA,MAAA,EAAA,GAAA,IAAA;EAMf,QAAA,EAAA,GAAA,GAAA,IAAA;EACA,YAAA,CAAA,EAAA,CAAA,KAAA,EAPe,IAOf,EAAA,EAAA,GAAA,IAAA;EAAI,WAAA,CAAA,EAAA,MAAA;EAOA,QAAA,CAAA,EAAA,OA6KZ;EAEW,YAAA,CAAA,EAAA,OAAe;EAMd,WAAA,CAAA,EAmBZ,OAAA;;UAhNQ;UACA;;;;;;cAOI,iBAAiB,KAAA,CAAM,GAAG;KA+K3B,eAAA;;;;;cAMC,YAAY,KAAA,CAAM,GAAG"}
@@ -3,28 +3,25 @@
3
3
 
4
4
  import { cn } from "../utils/index.js";
5
5
  import { Button } from "../../primitives/button.js";
6
- import { MultimodalInput as MultimodalInput$1 } from "../../primitives/multimodal-input.js";
6
+ import { FileInput, MultimodalInput as MultimodalInput$1 } from "../../primitives/multimodal-input.js";
7
7
  import icons_default from "./icons.js";
8
8
  import { useSupportText } from "../text/index.js";
9
9
  import { useComposerRefocus } from "../../hooks/use-composer-refocus.js";
10
10
  import { Watermark } from "./watermark.js";
11
11
  import { useRef } from "react";
12
- import { jsx, jsxs } from "react/jsx-runtime";
12
+ import { FILE_INPUT_ACCEPT, MAX_FILES_PER_MESSAGE, MAX_FILE_SIZE, formatFileSize } from "@cossistant/core";
13
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
13
14
 
14
15
  //#region src/support/components/multimodal-input.tsx
15
- const MultimodalInput = ({ className, value, onChange, onSubmit, onFileSelect, placeholder, disabled = false, isSubmitting = false, error, files = [], onRemoveFile, maxFiles = 5, maxFileSize = 10 * 1024 * 1024, allowedFileTypes = [
16
- "image/*",
17
- "application/pdf",
18
- "text/*"
19
- ] }) => {
20
- useRef(null);
16
+ const MultimodalInput = ({ className, value, onChange, onSubmit, onFileSelect, placeholder, disabled = false, isSubmitting = false, isUploading = false, uploadProgress = 0, error, files = [], onRemoveFile, maxFiles = MAX_FILES_PER_MESSAGE, maxFileSize = MAX_FILE_SIZE, allowedFileTypes = FILE_INPUT_ACCEPT }) => {
17
+ const fileInputRef = useRef(null);
21
18
  const hasContent = value.trim().length > 0 || files.length > 0;
22
19
  const { focusComposer, inputRef } = useComposerRefocus({
23
20
  disabled,
24
21
  hasContent,
25
- isSubmitting
22
+ isSubmitting: isSubmitting || isUploading
26
23
  });
27
- const canSubmit = !disabled && hasContent;
24
+ const canSubmit = !disabled && hasContent && !isUploading;
28
25
  const text = useSupportText();
29
26
  const resolvedPlaceholder = placeholder ?? text("component.multimodalInput.placeholder");
30
27
  const handleSubmit = () => {
@@ -39,10 +36,8 @@ const MultimodalInput = ({ className, value, onChange, onSubmit, onFileSelect, p
39
36
  e.preventDefault();
40
37
  handleSubmit();
41
38
  };
42
- const formatFileSize = (bytes) => {
43
- if (bytes < 1024) return `${bytes} B`;
44
- if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;
45
- return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
39
+ const handleAttachClick = () => {
40
+ if (files.length < maxFiles) fileInputRef.current?.click();
46
41
  };
47
42
  return /* @__PURE__ */ jsxs("form", {
48
43
  className: "flex flex-col gap-2",
@@ -53,35 +48,47 @@ const MultimodalInput = ({ className, value, onChange, onSubmit, onFileSelect, p
53
48
  id: "multimodal-input-error",
54
49
  children: error.message
55
50
  }),
56
- files.length > 0 && /* @__PURE__ */ jsx("div", {
57
- className: "flex flex-wrap gap-2 p-2",
58
- children: files.map((file, index) => /* @__PURE__ */ jsxs("div", {
59
- className: "flex items-center gap-2 rounded-md bg-co-muted px-2 py-1 text-xs",
60
- children: [
61
- /* @__PURE__ */ jsx(icons_default, {
62
- className: "h-3 w-3",
63
- name: "attachment"
64
- }),
65
- /* @__PURE__ */ jsx("span", {
66
- className: "max-w-[150px] truncate",
67
- children: file.name
68
- }),
69
- /* @__PURE__ */ jsx("span", {
70
- className: "text-co-muted-foreground",
71
- children: formatFileSize(file.size)
72
- }),
73
- onRemoveFile && /* @__PURE__ */ jsx("button", {
74
- "aria-label": text("common.actions.removeFile", { fileName: file.name }),
75
- className: "ml-1 hover:text-co-destructive",
76
- onClick: () => onRemoveFile(index),
77
- type: "button",
78
- children: /* @__PURE__ */ jsx(icons_default, {
51
+ files.length > 0 && /* @__PURE__ */ jsxs("div", {
52
+ className: "flex flex-col gap-2 p-2",
53
+ children: [isUploading && /* @__PURE__ */ jsxs("div", {
54
+ className: "flex items-center gap-2 text-co-muted-foreground text-xs",
55
+ children: [/* @__PURE__ */ jsx("div", {
56
+ className: "h-1 flex-1 overflow-hidden rounded-full bg-co-muted",
57
+ children: /* @__PURE__ */ jsx("div", {
58
+ className: "h-full bg-co-primary transition-all duration-300",
59
+ style: { width: `${uploadProgress}%` }
60
+ })
61
+ }), /* @__PURE__ */ jsxs("span", { children: [uploadProgress, "%"] })]
62
+ }), /* @__PURE__ */ jsx("div", {
63
+ className: "flex flex-wrap gap-2",
64
+ children: files.map((file, index) => /* @__PURE__ */ jsxs("div", {
65
+ className: cn("flex items-center gap-2 rounded-md bg-co-muted px-2 py-1 text-xs", isUploading && "opacity-70"),
66
+ children: [
67
+ /* @__PURE__ */ jsx(icons_default, {
79
68
  className: "h-3 w-3",
80
- name: "close"
69
+ name: "attachment"
70
+ }),
71
+ /* @__PURE__ */ jsx("span", {
72
+ className: "max-w-[150px] truncate",
73
+ children: file.name
74
+ }),
75
+ /* @__PURE__ */ jsx("span", {
76
+ className: "text-co-muted-foreground",
77
+ children: formatFileSize(file.size)
78
+ }),
79
+ onRemoveFile && !isUploading && /* @__PURE__ */ jsx("button", {
80
+ "aria-label": text("common.actions.removeFile", { fileName: file.name }),
81
+ className: "ml-1 hover:text-co-destructive",
82
+ onClick: () => onRemoveFile(index),
83
+ type: "button",
84
+ children: /* @__PURE__ */ jsx(icons_default, {
85
+ className: "h-3 w-3",
86
+ name: "close"
87
+ })
81
88
  })
82
- })
83
- ]
84
- }, `${file.name}-${index}`))
89
+ ]
90
+ }, `${file.name}-${index}`))
91
+ })]
85
92
  }),
86
93
  /* @__PURE__ */ jsxs("div", {
87
94
  className: "group/multimodal-input flex flex-col rounded border border-co-border bg-co-background ring-offset-2 focus-within:ring-1 focus-within:ring-co-primary/10 dark:bg-co-background-200",
@@ -98,20 +105,39 @@ const MultimodalInput = ({ className, value, onChange, onSubmit, onFileSelect, p
98
105
  value
99
106
  }), /* @__PURE__ */ jsxs("div", {
100
107
  className: "flex items-center justify-between py-1 pr-1 pl-3",
101
- children: [/* @__PURE__ */ jsx(Watermark, {}), /* @__PURE__ */ jsx("div", {
108
+ children: [/* @__PURE__ */ jsx(Watermark, {}), /* @__PURE__ */ jsxs("div", {
102
109
  className: "flex items-center gap-0.5",
103
- children: /* @__PURE__ */ jsx(SendButton, { disabled: !canSubmit })
110
+ children: [onFileSelect && /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx("button", {
111
+ "aria-label": text("common.actions.attachFiles"),
112
+ className: cn("group flex h-8 w-8 items-center justify-center rounded-md text-co-muted-foreground hover:bg-co-muted hover:text-co-foreground disabled:cursor-not-allowed disabled:opacity-50", files.length >= maxFiles && "opacity-50"),
113
+ disabled: disabled || isSubmitting || files.length >= maxFiles,
114
+ onClick: handleAttachClick,
115
+ type: "button",
116
+ children: /* @__PURE__ */ jsx(icons_default, {
117
+ className: "h-4 w-4",
118
+ name: "attachment"
119
+ })
120
+ }), /* @__PURE__ */ jsx(FileInput, {
121
+ accept: allowedFileTypes,
122
+ className: "hidden",
123
+ disabled: disabled || isSubmitting || files.length >= maxFiles,
124
+ onFileSelect,
125
+ ref: fileInputRef
126
+ })] }), /* @__PURE__ */ jsx(SendButton, {
127
+ disabled: !canSubmit,
128
+ isUploading
129
+ })]
104
130
  })]
105
131
  })]
106
132
  })
107
133
  ]
108
134
  });
109
135
  };
110
- const SendButton = ({ className, disabled = false }) => /* @__PURE__ */ jsx(Button, {
136
+ const SendButton = ({ className, disabled = false, isUploading = false }) => /* @__PURE__ */ jsx(Button, {
111
137
  className: cn("group flex h-8 w-8 items-center justify-center rounded-md text-co-primary hover:bg-co-muted disabled:cursor-not-allowed disabled:opacity-50", className),
112
138
  disabled,
113
139
  type: "submit",
114
- children: /* @__PURE__ */ jsx(icons_default, {
140
+ children: isUploading ? /* @__PURE__ */ jsx("div", { className: "h-4 w-4 animate-spin rounded-full border-2 border-co-primary border-t-transparent" }) : /* @__PURE__ */ jsx(icons_default, {
115
141
  className: "h-4 w-4",
116
142
  filledOnHover: true,
117
143
  name: "send"
@@ -1 +1 @@
1
- {"version":3,"file":"multimodal-input.js","names":["MultimodalInput: React.FC<MultimodalInputProps>","Icon","Primitive.MultimodalInput","SendButton: React.FC<SendButtonProps>","Primitive.Button"],"sources":["../../../src/support/components/multimodal-input.tsx"],"sourcesContent":["\"use client\";\n\nimport type React from \"react\";\nimport { useRef } from \"react\";\nimport { useComposerRefocus } from \"../../hooks/use-composer-refocus\";\nimport * as Primitive from \"../../primitives\";\nimport { useSupportText } from \"../text\";\nimport { cn } from \"../utils\";\nimport Icon from \"./icons\";\nimport { Watermark } from \"./watermark\";\n\nexport type MultimodalInputProps = {\n\tclassName?: string;\n\tvalue: string;\n\tonChange: (value: string) => void;\n\tonSubmit: () => void;\n\tonFileSelect?: (files: File[]) => void;\n\tplaceholder?: string;\n\tdisabled?: boolean;\n\tisSubmitting?: boolean;\n\terror?: Error | null;\n\tfiles?: File[];\n\tonRemoveFile?: (index: number) => void;\n\tmaxFiles?: number;\n\tmaxFileSize?: number;\n\tallowedFileTypes?: string[];\n};\n\nexport const MultimodalInput: React.FC<MultimodalInputProps> = ({\n\tclassName,\n\tvalue,\n\tonChange,\n\tonSubmit,\n\tonFileSelect,\n\tplaceholder,\n\tdisabled = false,\n\tisSubmitting = false,\n\terror,\n\tfiles = [],\n\tonRemoveFile,\n\tmaxFiles = 5,\n\tmaxFileSize = 10 * 1024 * 1024, // 10MB\n\tallowedFileTypes = [\"image/*\", \"application/pdf\", \"text/*\"],\n}) => {\n\tconst fileInputRef = useRef<HTMLInputElement>(null);\n\tconst hasContent = value.trim().length > 0 || files.length > 0;\n\tconst { focusComposer, inputRef } = useComposerRefocus({\n\t\tdisabled,\n\t\thasContent,\n\t\tisSubmitting,\n\t});\n\tconst canSubmit = !disabled && hasContent;\n\tconst text = useSupportText();\n\tconst resolvedPlaceholder =\n\t\tplaceholder ?? text(\"component.multimodalInput.placeholder\");\n\n\tconst handleSubmit = () => {\n\t\tif (!canSubmit) {\n\t\t\treturn;\n\t\t}\n\n\t\tonSubmit();\n\t\t// Try focusing immediately for optimistic submission UX, then ensure focus\n\t\t// sticks after the submit button handles the click.\n\t\tfocusComposer();\n\t\trequestAnimationFrame(() => {\n\t\t\tfocusComposer();\n\t\t});\n\t};\n\n\tconst handleFormSubmit = (e: React.FormEvent) => {\n\t\te.preventDefault();\n\t\thandleSubmit();\n\t};\n\n\tconst handleAttachClick = () => {\n\t\tif (files.length < maxFiles) {\n\t\t\tfileInputRef.current?.click();\n\t\t}\n\t};\n\n\tconst formatFileSize = (bytes: number) => {\n\t\tif (bytes < 1024) {\n\t\t\treturn `${bytes} B`;\n\t\t}\n\t\tif (bytes < 1024 * 1024) {\n\t\t\treturn `${(bytes / 1024).toFixed(1)} KB`;\n\t\t}\n\t\treturn `${(bytes / (1024 * 1024)).toFixed(1)} MB`;\n\t};\n\n\treturn (\n\t\t<form className=\"flex flex-col gap-2\" onSubmit={handleFormSubmit}>\n\t\t\t{/* Error message */}\n\t\t\t{error && (\n\t\t\t\t<div\n\t\t\t\t\tclassName=\"rounded-md bg-co-destructive-muted p-2 text-co-destructive text-xs\"\n\t\t\t\t\tid=\"multimodal-input-error\"\n\t\t\t\t>\n\t\t\t\t\t{error.message}\n\t\t\t\t</div>\n\t\t\t)}\n\n\t\t\t{/* File attachments */}\n\t\t\t{files.length > 0 && (\n\t\t\t\t<div className=\"flex flex-wrap gap-2 p-2\">\n\t\t\t\t\t{files.map((file, index) => (\n\t\t\t\t\t\t<div\n\t\t\t\t\t\t\tclassName=\"flex items-center gap-2 rounded-md bg-co-muted px-2 py-1 text-xs\"\n\t\t\t\t\t\t\tkey={`${file.name}-${index}`}\n\t\t\t\t\t\t>\n\t\t\t\t\t\t\t<Icon className=\"h-3 w-3\" name=\"attachment\" />\n\t\t\t\t\t\t\t<span className=\"max-w-[150px] truncate\">{file.name}</span>\n\t\t\t\t\t\t\t<span className=\"text-co-muted-foreground\">\n\t\t\t\t\t\t\t\t{formatFileSize(file.size)}\n\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t{onRemoveFile && (\n\t\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\t\taria-label={text(\"common.actions.removeFile\", {\n\t\t\t\t\t\t\t\t\t\tfileName: file.name,\n\t\t\t\t\t\t\t\t\t})}\n\t\t\t\t\t\t\t\t\tclassName=\"ml-1 hover:text-co-destructive\"\n\t\t\t\t\t\t\t\t\tonClick={() => onRemoveFile(index)}\n\t\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t<Icon className=\"h-3 w-3\" name=\"close\" />\n\t\t\t\t\t\t\t\t</button>\n\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t</div>\n\t\t\t\t\t))}\n\t\t\t\t</div>\n\t\t\t)}\n\n\t\t\t{/* Input area */}\n\t\t\t<div className=\"group/multimodal-input flex flex-col rounded border border-co-border bg-co-background ring-offset-2 focus-within:ring-1 focus-within:ring-co-primary/10 dark:bg-co-background-200\">\n\t\t\t\t<Primitive.MultimodalInput\n\t\t\t\t\tautoFocus\n\t\t\t\t\tclassName={cn(\n\t\t\t\t\t\t\"flex-1 resize-none overflow-hidden p-3 text-co-foreground text-sm placeholder:text-co-primary/50 focus-visible:outline-none\",\n\t\t\t\t\t\tclassName\n\t\t\t\t\t)}\n\t\t\t\t\tdisabled={disabled}\n\t\t\t\t\terror={error}\n\t\t\t\t\tonChange={onChange}\n\t\t\t\t\tonFileSelect={onFileSelect}\n\t\t\t\t\tonSubmit={handleSubmit}\n\t\t\t\t\tplaceholder={resolvedPlaceholder}\n\t\t\t\t\tref={inputRef}\n\t\t\t\t\tvalue={value}\n\t\t\t\t/>\n\n\t\t\t\t<div className=\"flex items-center justify-between py-1 pr-1 pl-3\">\n\t\t\t\t\t<Watermark />\n\n\t\t\t\t\t<div className=\"flex items-center gap-0.5\">\n\t\t\t\t\t\t{/* File attachment button */}\n\t\t\t\t\t\t{/* {onFileSelect && (\n\t\t\t\t\t\t\t<>\n\t\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\t\taria-label={text(\"common.actions.attachFiles\")}\n\t\t\t\t\t\t\t\t\tclassName={cn(\n\t\t\t\t\t\t\t\t\t\t\"group flex h-8 w-8 items-center justify-center rounded-md text-co-muted-foreground hover:bg-co-muted hover:text-co-foreground disabled:cursor-not-allowed disabled:opacity-50\",\n\t\t\t\t\t\t\t\t\t\tfiles.length >= maxFiles && \"opacity-50\"\n\t\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t\t\tdisabled={\n\t\t\t\t\t\t\t\t\t\tdisabled || isSubmitting || files.length >= maxFiles\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\tonClick={handleAttachClick}\n\t\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t<Icon className=\"h-4 w-4\" name=\"attachment\" />\n\t\t\t\t\t\t\t\t</button>\n\n\t\t\t\t\t\t\t\t<Primitive.FileInput\n\t\t\t\t\t\t\t\t\taccept={allowedFileTypes.join(\",\")}\n\t\t\t\t\t\t\t\t\tclassName=\"hidden\"\n\t\t\t\t\t\t\t\t\tdisabled={\n\t\t\t\t\t\t\t\t\t\tdisabled || isSubmitting || files.length >= maxFiles\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\tonFileSelect={onFileSelect}\n\t\t\t\t\t\t\t\t\tref={fileInputRef}\n\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t</>\n\t\t\t\t\t\t)} */}\n\n\t\t\t\t\t\t{/* Send button */}\n\t\t\t\t\t\t<SendButton disabled={!canSubmit} />\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t</form>\n\t);\n};\n\nexport type SendButtonProps = {\n\tclassName?: string;\n\tdisabled?: boolean;\n};\n\nexport const SendButton: React.FC<SendButtonProps> = ({\n\tclassName,\n\tdisabled = false,\n}) => (\n\t<Primitive.Button\n\t\tclassName={cn(\n\t\t\t\"group flex h-8 w-8 items-center justify-center rounded-md text-co-primary hover:bg-co-muted disabled:cursor-not-allowed disabled:opacity-50\",\n\t\t\tclassName\n\t\t)}\n\t\tdisabled={disabled}\n\t\ttype=\"submit\"\n\t>\n\t\t<Icon className=\"h-4 w-4\" filledOnHover name=\"send\" />\n\t</Primitive.Button>\n);\n"],"mappings":";;;;;;;;;;;;;;AA4BA,MAAaA,mBAAmD,EAC/D,WACA,OACA,UACA,UACA,cACA,aACA,WAAW,OACX,eAAe,OACf,OACA,QAAQ,EAAE,EACV,cACA,WAAW,GACX,cAAc,KAAK,OAAO,MAC1B,mBAAmB;CAAC;CAAW;CAAmB;CAAS,OACtD;AACgB,QAAyB,KAAK;CACnD,MAAM,aAAa,MAAM,MAAM,CAAC,SAAS,KAAK,MAAM,SAAS;CAC7D,MAAM,EAAE,eAAe,aAAa,mBAAmB;EACtD;EACA;EACA;EACA,CAAC;CACF,MAAM,YAAY,CAAC,YAAY;CAC/B,MAAM,OAAO,gBAAgB;CAC7B,MAAM,sBACL,eAAe,KAAK,wCAAwC;CAE7D,MAAM,qBAAqB;AAC1B,MAAI,CAAC,UACJ;AAGD,YAAU;AAGV,iBAAe;AACf,8BAA4B;AAC3B,kBAAe;IACd;;CAGH,MAAM,oBAAoB,MAAuB;AAChD,IAAE,gBAAgB;AAClB,gBAAc;;CASf,MAAM,kBAAkB,UAAkB;AACzC,MAAI,QAAQ,KACX,QAAO,GAAG,MAAM;AAEjB,MAAI,QAAQ,OAAO,KAClB,QAAO,IAAI,QAAQ,MAAM,QAAQ,EAAE,CAAC;AAErC,SAAO,IAAI,SAAS,OAAO,OAAO,QAAQ,EAAE,CAAC;;AAG9C,QACC,qBAAC;EAAK,WAAU;EAAsB,UAAU;;GAE9C,SACA,oBAAC;IACA,WAAU;IACV,IAAG;cAEF,MAAM;KACF;GAIN,MAAM,SAAS,KACf,oBAAC;IAAI,WAAU;cACb,MAAM,KAAK,MAAM,UACjB,qBAAC;KACA,WAAU;;MAGV,oBAACC;OAAK,WAAU;OAAU,MAAK;QAAe;MAC9C,oBAAC;OAAK,WAAU;iBAA0B,KAAK;QAAY;MAC3D,oBAAC;OAAK,WAAU;iBACd,eAAe,KAAK,KAAK;QACpB;MACN,gBACA,oBAAC;OACA,cAAY,KAAK,6BAA6B,EAC7C,UAAU,KAAK,MACf,CAAC;OACF,WAAU;OACV,eAAe,aAAa,MAAM;OAClC,MAAK;iBAEL,oBAACA;QAAK,WAAU;QAAU,MAAK;SAAU;QACjC;;OAjBL,GAAG,KAAK,KAAK,GAAG,QAmBhB,CACL;KACG;GAIP,qBAAC;IAAI,WAAU;eACd,oBAACC;KACA;KACA,WAAW,GACV,+HACA,UACA;KACS;KACH;KACG;KACI;KACd,UAAU;KACV,aAAa;KACb,KAAK;KACE;MACN,EAEF,qBAAC;KAAI,WAAU;gBACd,oBAAC,cAAY,EAEb,oBAAC;MAAI,WAAU;gBAgCd,oBAAC,cAAW,UAAU,CAAC,YAAa;OAC/B;MACD;KACD;;GACA;;AAST,MAAaC,cAAyC,EACrD,WACA,WAAW,YAEX,oBAACC;CACA,WAAW,GACV,+IACA,UACA;CACS;CACV,MAAK;WAEL,oBAACH;EAAK,WAAU;EAAU;EAAc,MAAK;GAAS;EACpC"}
1
+ {"version":3,"file":"multimodal-input.js","names":["MultimodalInput: React.FC<MultimodalInputProps>","Icon","Primitive.MultimodalInput","Primitive.FileInput","SendButton: React.FC<SendButtonProps>","Primitive.Button"],"sources":["../../../src/support/components/multimodal-input.tsx"],"sourcesContent":["\"use client\";\n\nimport {\n\tFILE_INPUT_ACCEPT,\n\tformatFileSize,\n\tMAX_FILE_SIZE,\n\tMAX_FILES_PER_MESSAGE,\n} from \"@cossistant/core\";\nimport type React from \"react\";\nimport { useRef } from \"react\";\nimport { useComposerRefocus } from \"../../hooks/use-composer-refocus\";\nimport * as Primitive from \"../../primitives\";\nimport { useSupportText } from \"../text\";\nimport { cn } from \"../utils\";\nimport Icon from \"./icons\";\nimport { Watermark } from \"./watermark\";\n\nexport type MultimodalInputProps = {\n\tclassName?: string;\n\tvalue: string;\n\tonChange: (value: string) => void;\n\tonSubmit: () => void;\n\tonFileSelect?: (files: File[]) => void;\n\tplaceholder?: string;\n\tdisabled?: boolean;\n\tisSubmitting?: boolean;\n\tisUploading?: boolean;\n\tuploadProgress?: number;\n\terror?: Error | null;\n\tfiles?: File[];\n\tonRemoveFile?: (index: number) => void;\n\tmaxFiles?: number;\n\tmaxFileSize?: number;\n\tallowedFileTypes?: string;\n};\n\nexport const MultimodalInput: React.FC<MultimodalInputProps> = ({\n\tclassName,\n\tvalue,\n\tonChange,\n\tonSubmit,\n\tonFileSelect,\n\tplaceholder,\n\tdisabled = false,\n\tisSubmitting = false,\n\tisUploading = false,\n\tuploadProgress = 0,\n\terror,\n\tfiles = [],\n\tonRemoveFile,\n\tmaxFiles = MAX_FILES_PER_MESSAGE,\n\tmaxFileSize = MAX_FILE_SIZE,\n\tallowedFileTypes = FILE_INPUT_ACCEPT,\n}) => {\n\tconst fileInputRef = useRef<HTMLInputElement>(null);\n\tconst hasContent = value.trim().length > 0 || files.length > 0;\n\tconst { focusComposer, inputRef } = useComposerRefocus({\n\t\tdisabled,\n\t\thasContent,\n\t\tisSubmitting: isSubmitting || isUploading,\n\t});\n\tconst canSubmit = !disabled && hasContent && !isUploading;\n\tconst text = useSupportText();\n\tconst resolvedPlaceholder =\n\t\tplaceholder ?? text(\"component.multimodalInput.placeholder\");\n\n\tconst handleSubmit = () => {\n\t\tif (!canSubmit) {\n\t\t\treturn;\n\t\t}\n\n\t\tonSubmit();\n\t\t// Try focusing immediately for optimistic submission UX, then ensure focus\n\t\t// sticks after the submit button handles the click.\n\t\tfocusComposer();\n\t\trequestAnimationFrame(() => {\n\t\t\tfocusComposer();\n\t\t});\n\t};\n\n\tconst handleFormSubmit = (e: React.FormEvent) => {\n\t\te.preventDefault();\n\t\thandleSubmit();\n\t};\n\n\tconst handleAttachClick = () => {\n\t\tif (files.length < maxFiles) {\n\t\t\tfileInputRef.current?.click();\n\t\t}\n\t};\n\n\treturn (\n\t\t<form className=\"flex flex-col gap-2\" onSubmit={handleFormSubmit}>\n\t\t\t{/* Error message */}\n\t\t\t{error && (\n\t\t\t\t<div\n\t\t\t\t\tclassName=\"rounded-md bg-co-destructive-muted p-2 text-co-destructive text-xs\"\n\t\t\t\t\tid=\"multimodal-input-error\"\n\t\t\t\t>\n\t\t\t\t\t{error.message}\n\t\t\t\t</div>\n\t\t\t)}\n\n\t\t\t{/* File attachments */}\n\t\t\t{files.length > 0 && (\n\t\t\t\t<div className=\"flex flex-col gap-2 p-2\">\n\t\t\t\t\t{/* Upload progress indicator */}\n\t\t\t\t\t{isUploading && (\n\t\t\t\t\t\t<div className=\"flex items-center gap-2 text-co-muted-foreground text-xs\">\n\t\t\t\t\t\t\t<div className=\"h-1 flex-1 overflow-hidden rounded-full bg-co-muted\">\n\t\t\t\t\t\t\t\t<div\n\t\t\t\t\t\t\t\t\tclassName=\"h-full bg-co-primary transition-all duration-300\"\n\t\t\t\t\t\t\t\t\tstyle={{ width: `${uploadProgress}%` }}\n\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t<span>{uploadProgress}%</span>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t)}\n\t\t\t\t\t<div className=\"flex flex-wrap gap-2\">\n\t\t\t\t\t\t{files.map((file, index) => (\n\t\t\t\t\t\t\t<div\n\t\t\t\t\t\t\t\tclassName={cn(\n\t\t\t\t\t\t\t\t\t\"flex items-center gap-2 rounded-md bg-co-muted px-2 py-1 text-xs\",\n\t\t\t\t\t\t\t\t\tisUploading && \"opacity-70\"\n\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t\tkey={`${file.name}-${index}`}\n\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t<Icon className=\"h-3 w-3\" name=\"attachment\" />\n\t\t\t\t\t\t\t\t<span className=\"max-w-[150px] truncate\">{file.name}</span>\n\t\t\t\t\t\t\t\t<span className=\"text-co-muted-foreground\">\n\t\t\t\t\t\t\t\t\t{formatFileSize(file.size)}\n\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t\t{onRemoveFile && !isUploading && (\n\t\t\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\t\t\taria-label={text(\"common.actions.removeFile\", {\n\t\t\t\t\t\t\t\t\t\t\tfileName: file.name,\n\t\t\t\t\t\t\t\t\t\t})}\n\t\t\t\t\t\t\t\t\t\tclassName=\"ml-1 hover:text-co-destructive\"\n\t\t\t\t\t\t\t\t\t\tonClick={() => onRemoveFile(index)}\n\t\t\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t<Icon className=\"h-3 w-3\" name=\"close\" />\n\t\t\t\t\t\t\t\t\t</button>\n\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t))}\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\t\t\t)}\n\n\t\t\t{/* Input area */}\n\t\t\t<div className=\"group/multimodal-input flex flex-col rounded border border-co-border bg-co-background ring-offset-2 focus-within:ring-1 focus-within:ring-co-primary/10 dark:bg-co-background-200\">\n\t\t\t\t<Primitive.MultimodalInput\n\t\t\t\t\tautoFocus\n\t\t\t\t\tclassName={cn(\n\t\t\t\t\t\t\"flex-1 resize-none overflow-hidden p-3 text-co-foreground text-sm placeholder:text-co-primary/50 focus-visible:outline-none\",\n\t\t\t\t\t\tclassName\n\t\t\t\t\t)}\n\t\t\t\t\tdisabled={disabled}\n\t\t\t\t\terror={error}\n\t\t\t\t\tonChange={onChange}\n\t\t\t\t\tonFileSelect={onFileSelect}\n\t\t\t\t\tonSubmit={handleSubmit}\n\t\t\t\t\tplaceholder={resolvedPlaceholder}\n\t\t\t\t\tref={inputRef}\n\t\t\t\t\tvalue={value}\n\t\t\t\t/>\n\n\t\t\t\t<div className=\"flex items-center justify-between py-1 pr-1 pl-3\">\n\t\t\t\t\t<Watermark />\n\n\t\t\t\t\t<div className=\"flex items-center gap-0.5\">\n\t\t\t\t\t\t{/* File attachment button */}\n\t\t\t\t\t\t{onFileSelect && (\n\t\t\t\t\t\t\t<>\n\t\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\t\taria-label={text(\"common.actions.attachFiles\")}\n\t\t\t\t\t\t\t\t\tclassName={cn(\n\t\t\t\t\t\t\t\t\t\t\"group flex h-8 w-8 items-center justify-center rounded-md text-co-muted-foreground hover:bg-co-muted hover:text-co-foreground disabled:cursor-not-allowed disabled:opacity-50\",\n\t\t\t\t\t\t\t\t\t\tfiles.length >= maxFiles && \"opacity-50\"\n\t\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t\t\tdisabled={\n\t\t\t\t\t\t\t\t\t\tdisabled || isSubmitting || files.length >= maxFiles\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\tonClick={handleAttachClick}\n\t\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t<Icon className=\"h-4 w-4\" name=\"attachment\" />\n\t\t\t\t\t\t\t\t</button>\n\n\t\t\t\t\t\t\t\t<Primitive.FileInput\n\t\t\t\t\t\t\t\t\taccept={allowedFileTypes}\n\t\t\t\t\t\t\t\t\tclassName=\"hidden\"\n\t\t\t\t\t\t\t\t\tdisabled={\n\t\t\t\t\t\t\t\t\t\tdisabled || isSubmitting || files.length >= maxFiles\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\tonFileSelect={onFileSelect}\n\t\t\t\t\t\t\t\t\tref={fileInputRef}\n\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t</>\n\t\t\t\t\t\t)}\n\n\t\t\t\t\t\t{/* Send button */}\n\t\t\t\t\t\t<SendButton disabled={!canSubmit} isUploading={isUploading} />\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t</form>\n\t);\n};\n\nexport type SendButtonProps = {\n\tclassName?: string;\n\tdisabled?: boolean;\n\tisUploading?: boolean;\n};\n\nexport const SendButton: React.FC<SendButtonProps> = ({\n\tclassName,\n\tdisabled = false,\n\tisUploading = false,\n}) => (\n\t<Primitive.Button\n\t\tclassName={cn(\n\t\t\t\"group flex h-8 w-8 items-center justify-center rounded-md text-co-primary hover:bg-co-muted disabled:cursor-not-allowed disabled:opacity-50\",\n\t\t\tclassName\n\t\t)}\n\t\tdisabled={disabled}\n\t\ttype=\"submit\"\n\t>\n\t\t{isUploading ? (\n\t\t\t<div className=\"h-4 w-4 animate-spin rounded-full border-2 border-co-primary border-t-transparent\" />\n\t\t) : (\n\t\t\t<Icon className=\"h-4 w-4\" filledOnHover name=\"send\" />\n\t\t)}\n\t</Primitive.Button>\n);\n"],"mappings":";;;;;;;;;;;;;;;AAoCA,MAAaA,mBAAmD,EAC/D,WACA,OACA,UACA,UACA,cACA,aACA,WAAW,OACX,eAAe,OACf,cAAc,OACd,iBAAiB,GACjB,OACA,QAAQ,EAAE,EACV,cACA,WAAW,uBACX,cAAc,eACd,mBAAmB,wBACd;CACL,MAAM,eAAe,OAAyB,KAAK;CACnD,MAAM,aAAa,MAAM,MAAM,CAAC,SAAS,KAAK,MAAM,SAAS;CAC7D,MAAM,EAAE,eAAe,aAAa,mBAAmB;EACtD;EACA;EACA,cAAc,gBAAgB;EAC9B,CAAC;CACF,MAAM,YAAY,CAAC,YAAY,cAAc,CAAC;CAC9C,MAAM,OAAO,gBAAgB;CAC7B,MAAM,sBACL,eAAe,KAAK,wCAAwC;CAE7D,MAAM,qBAAqB;AAC1B,MAAI,CAAC,UACJ;AAGD,YAAU;AAGV,iBAAe;AACf,8BAA4B;AAC3B,kBAAe;IACd;;CAGH,MAAM,oBAAoB,MAAuB;AAChD,IAAE,gBAAgB;AAClB,gBAAc;;CAGf,MAAM,0BAA0B;AAC/B,MAAI,MAAM,SAAS,SAClB,cAAa,SAAS,OAAO;;AAI/B,QACC,qBAAC;EAAK,WAAU;EAAsB,UAAU;;GAE9C,SACA,oBAAC;IACA,WAAU;IACV,IAAG;cAEF,MAAM;KACF;GAIN,MAAM,SAAS,KACf,qBAAC;IAAI,WAAU;eAEb,eACA,qBAAC;KAAI,WAAU;gBACd,oBAAC;MAAI,WAAU;gBACd,oBAAC;OACA,WAAU;OACV,OAAO,EAAE,OAAO,GAAG,eAAe,IAAI;QACrC;OACG,EACN,qBAAC,qBAAM,gBAAe,OAAQ;MACzB,EAEP,oBAAC;KAAI,WAAU;eACb,MAAM,KAAK,MAAM,UACjB,qBAAC;MACA,WAAW,GACV,oEACA,eAAe,aACf;;OAGD,oBAACC;QAAK,WAAU;QAAU,MAAK;SAAe;OAC9C,oBAAC;QAAK,WAAU;kBAA0B,KAAK;SAAY;OAC3D,oBAAC;QAAK,WAAU;kBACd,eAAe,KAAK,KAAK;SACpB;OACN,gBAAgB,CAAC,eACjB,oBAAC;QACA,cAAY,KAAK,6BAA6B,EAC7C,UAAU,KAAK,MACf,CAAC;QACF,WAAU;QACV,eAAe,aAAa,MAAM;QAClC,MAAK;kBAEL,oBAACA;SAAK,WAAU;SAAU,MAAK;UAAU;SACjC;;QAjBL,GAAG,KAAK,KAAK,GAAG,QAmBhB,CACL;MACG;KACD;GAIP,qBAAC;IAAI,WAAU;eACd,oBAACC;KACA;KACA,WAAW,GACV,+HACA,UACA;KACS;KACH;KACG;KACI;KACd,UAAU;KACV,aAAa;KACb,KAAK;KACE;MACN,EAEF,qBAAC;KAAI,WAAU;gBACd,oBAAC,cAAY,EAEb,qBAAC;MAAI,WAAU;iBAEb,gBACA,4CACC,oBAAC;OACA,cAAY,KAAK,6BAA6B;OAC9C,WAAW,GACV,iLACA,MAAM,UAAU,YAAY,aAC5B;OACD,UACC,YAAY,gBAAgB,MAAM,UAAU;OAE7C,SAAS;OACT,MAAK;iBAEL,oBAACD;QAAK,WAAU;QAAU,MAAK;SAAe;QACtC,EAET,oBAACE;OACA,QAAQ;OACR,WAAU;OACV,UACC,YAAY,gBAAgB,MAAM,UAAU;OAE/B;OACd,KAAK;QACJ,IACA,EAIJ,oBAAC;OAAW,UAAU,CAAC;OAAwB;QAAe;OACzD;MACD;KACD;;GACA;;AAUT,MAAaC,cAAyC,EACrD,WACA,WAAW,OACX,cAAc,YAEd,oBAACC;CACA,WAAW,GACV,+IACA,UACA;CACS;CACV,MAAK;WAEJ,cACA,oBAAC,SAAI,WAAU,sFAAsF,GAErG,oBAACJ;EAAK,WAAU;EAAU;EAAc,MAAK;GAAS;EAErC"}
@@ -1,6 +1,6 @@
1
1
  import { useSupportNavigation } from "../store/support-store.js";
2
- import { CoButton } from "./button.js";
3
2
  import icons_default from "./icons.js";
3
+ import { CoButton } from "./button.js";
4
4
  import { Text } from "../text/index.js";
5
5
  import { jsx, jsxs } from "react/jsx-runtime";
6
6
 
@@ -0,0 +1,23 @@
1
+ import * as React$1 from "react";
2
+
3
+ //#region src/support/components/root.d.ts
4
+ type RootProps = {
5
+ className?: string;
6
+ children: React$1.ReactNode;
7
+ };
8
+ /**
9
+ * Root wrapper component that provides the positioning context.
10
+ * Contains the trigger and content as siblings.
11
+ *
12
+ * @example
13
+ * <Support.Root>
14
+ * <Support.Trigger>Help</Support.Trigger>
15
+ * <Support.Content>
16
+ * <Support.Router />
17
+ * </Support.Content>
18
+ * </Support.Root>
19
+ */
20
+ declare const Root: React$1.FC<RootProps>;
21
+ //#endregion
22
+ export { Root, RootProps };
23
+ //# sourceMappingURL=root.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"root.d.ts","names":[],"sources":["../../../src/support/components/root.tsx"],"sourcesContent":[],"mappings":";;;KAOY,SAAA;;EAAA,QAAA,EAED,OAAA,CAAM,SAAN;AAeX,CAAA;;;;;;;;;;;;;cAAa,MAAM,OAAA,CAAM,GAAG"}
@@ -0,0 +1,36 @@
1
+ "use client";
2
+
3
+
4
+ import { cn } from "../utils/index.js";
5
+ import { TriggerRefProvider } from "../context/positioning.js";
6
+ import { jsx } from "react/jsx-runtime";
7
+ import { motion } from "motion/react";
8
+
9
+ //#region src/support/components/root.tsx
10
+ /**
11
+ * Root wrapper component that provides the positioning context.
12
+ * Contains the trigger and content as siblings.
13
+ *
14
+ * @example
15
+ * <Support.Root>
16
+ * <Support.Trigger>Help</Support.Trigger>
17
+ * <Support.Content>
18
+ * <Support.Router />
19
+ * </Support.Content>
20
+ * </Support.Root>
21
+ */
22
+ const Root = ({ className, children }) => /* @__PURE__ */ jsx(TriggerRefProvider, { children: /* @__PURE__ */ jsx(motion.div, {
23
+ animate: { opacity: 1 },
24
+ className: cn("cossistant relative", className),
25
+ initial: { opacity: 0 },
26
+ layout: "position",
27
+ transition: {
28
+ default: { ease: "anticipate" },
29
+ layout: { duration: .3 }
30
+ },
31
+ children
32
+ }) });
33
+
34
+ //#endregion
35
+ export { Root };
36
+ //# sourceMappingURL=root.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"root.js","names":["Root: React.FC<RootProps>"],"sources":["../../../src/support/components/root.tsx"],"sourcesContent":["\"use client\";\n\nimport { motion } from \"motion/react\";\nimport type * as React from \"react\";\nimport { TriggerRefProvider } from \"../context/positioning\";\nimport { cn } from \"../utils\";\n\nexport type RootProps = {\n\tclassName?: string;\n\tchildren: React.ReactNode;\n};\n\n/**\n * Root wrapper component that provides the positioning context.\n * Contains the trigger and content as siblings.\n *\n * @example\n * <Support.Root>\n * <Support.Trigger>Help</Support.Trigger>\n * <Support.Content>\n * <Support.Router />\n * </Support.Content>\n * </Support.Root>\n */\nexport const Root: React.FC<RootProps> = ({ className, children }) => (\n\t<TriggerRefProvider>\n\t\t<motion.div\n\t\t\tanimate={{ opacity: 1 }}\n\t\t\tclassName={cn(\"cossistant relative\", className)}\n\t\t\tinitial={{ opacity: 0 }}\n\t\t\tlayout=\"position\"\n\t\t\ttransition={{\n\t\t\t\tdefault: { ease: \"anticipate\" },\n\t\t\t\tlayout: { duration: 0.3 },\n\t\t\t}}\n\t\t>\n\t\t\t{children}\n\t\t</motion.div>\n\t</TriggerRefProvider>\n);\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;AAwBA,MAAaA,QAA6B,EAAE,WAAW,eACtD,oBAAC,gCACA,oBAAC,OAAO;CACP,SAAS,EAAE,SAAS,GAAG;CACvB,WAAW,GAAG,uBAAuB,UAAU;CAC/C,SAAS,EAAE,SAAS,GAAG;CACvB,QAAO;CACP,YAAY;EACX,SAAS,EAAE,MAAM,cAAc;EAC/B,QAAQ,EAAE,UAAU,IAAK;EACzB;CAEA;EACW,GACO"}
@@ -1 +1 @@
1
- {"version":3,"file":"timeline-message-item.d.ts","names":[],"sources":["../../../src/support/components/timeline-message-item.tsx"],"sourcesContent":[],"mappings":";;;;KAUY,wBAAA;QACL;EADK,MAAA,CAAA,EAAA,OAAA;EAUI,cAAA,CAAA,EAAA,OAAmB;CAClC;;;;;AAG+C,iBAJhC,mBAAA,CAIgC;EAAA,IAAA;EAAA,MAAA;EAAA;AAAA,CAAA,EAA7C,wBAA6C,CAAA,EAAlB,KAAA,CAAM,YAAY"}
1
+ {"version":3,"file":"timeline-message-item.d.ts","names":[],"sources":["../../../src/support/components/timeline-message-item.tsx"],"sourcesContent":[],"mappings":";;;;KAkBY,wBAAA;QACL;EADK,MAAA,CAAA,EAAA,OAAA;EAUI,cAAA,CAAA,EAAA,OAAmB;CAClC;;;;;AAG+C,iBAJhC,mBAAA,CAIgC;EAAA,IAAA;EAAA,MAAA;EAAA;AAAA,CAAA,EAA7C,wBAA6C,CAAA,EAAlB,KAAA,CAAM,YAAY"}
@@ -1,6 +1,11 @@
1
1
  import { cn } from "../utils/index.js";
2
2
  import { TimelineItem, TimelineItemContent, TimelineItemTimestamp } from "../../primitives/timeline-item.js";
3
+ import { extractFileParts, extractImageParts } from "../../primitives/timeline-item-attachments.js";
4
+ import icons_default from "./icons.js";
3
5
  import { useSupportText } from "../text/index.js";
6
+ import { ImageLightbox } from "./image-lightbox.js";
7
+ import { useState } from "react";
8
+ import { formatFileSize } from "@cossistant/core";
4
9
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
5
10
 
6
11
  //#region src/support/components/timeline-message-item.tsx
@@ -10,7 +15,17 @@ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
10
15
  */
11
16
  function TimelineMessageItem({ item, isLast = false, isSentByViewer = false }) {
12
17
  const text = useSupportText();
13
- return /* @__PURE__ */ jsx(TimelineItem, {
18
+ const [lightboxOpen, setLightboxOpen] = useState(false);
19
+ const [lightboxIndex, setLightboxIndex] = useState(0);
20
+ const images = extractImageParts(item.parts);
21
+ const files = extractFileParts(item.parts);
22
+ const hasAttachments = images.length > 0 || files.length > 0;
23
+ const hasText = item.text && item.text.trim().length > 0;
24
+ const openLightbox = (index) => {
25
+ setLightboxIndex(index);
26
+ setLightboxOpen(true);
27
+ };
28
+ return /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx(TimelineItem, {
14
29
  item,
15
30
  children: ({ isAI, timestamp }) => {
16
31
  const isSentByViewerFinal = isSentByViewer;
@@ -18,27 +33,76 @@ function TimelineMessageItem({ item, isLast = false, isSentByViewer = false }) {
18
33
  className: cn("flex w-full gap-2", isSentByViewerFinal && "flex-row-reverse", !isSentByViewerFinal && "flex-row"),
19
34
  children: /* @__PURE__ */ jsxs("div", {
20
35
  className: cn("flex w-full min-w-0 flex-1 flex-col gap-1", isSentByViewerFinal && "items-end"),
21
- children: [/* @__PURE__ */ jsx(TimelineItemContent, {
22
- className: cn("block min-w-0 max-w-[300px] whitespace-pre-wrap break-words rounded-lg px-3.5 py-2.5 text-sm", {
23
- "bg-co-background-300 text-co-foreground dark:bg-co-background-600": !isSentByViewerFinal,
24
- "bg-co-primary text-co-primary-foreground": isSentByViewerFinal,
25
- "rounded-br-sm": isLast && isSentByViewerFinal,
26
- "rounded-bl-sm": isLast && !isSentByViewerFinal
36
+ children: [
37
+ hasText && /* @__PURE__ */ jsx(TimelineItemContent, {
38
+ className: cn("block min-w-0 max-w-[300px] whitespace-pre-wrap break-words rounded-lg px-3.5 py-2.5 text-sm", {
39
+ "bg-co-background-300 text-co-foreground dark:bg-co-background-600": !isSentByViewerFinal,
40
+ "bg-co-primary text-co-primary-foreground": isSentByViewerFinal,
41
+ "rounded-br-sm": isLast && isSentByViewerFinal && !hasAttachments,
42
+ "rounded-bl-sm": isLast && !isSentByViewerFinal && !hasAttachments
43
+ }),
44
+ renderMarkdown: true,
45
+ text: item.text
27
46
  }),
28
- renderMarkdown: true,
29
- text: item.text
30
- }), isLast && /* @__PURE__ */ jsx(TimelineItemTimestamp, {
31
- className: "px-1 text-co-muted-foreground text-xs",
32
- timestamp,
33
- children: () => /* @__PURE__ */ jsxs(Fragment, { children: [timestamp.toLocaleTimeString([], {
34
- hour: "2-digit",
35
- minute: "2-digit"
36
- }), isAI && ` ${text("component.message.timestamp.aiIndicator")}`] })
37
- })]
47
+ images.length > 0 && /* @__PURE__ */ jsx("div", {
48
+ className: cn("flex flex-wrap gap-2", isSentByViewerFinal && "justify-end"),
49
+ children: images.map((image, index) => /* @__PURE__ */ jsx("button", {
50
+ className: "group relative overflow-hidden rounded-lg focus:outline-none focus:ring-2 focus:ring-co-primary/50",
51
+ onClick: () => openLightbox(index),
52
+ type: "button",
53
+ children: /* @__PURE__ */ jsx("img", {
54
+ alt: image.fileName || `Image ${index + 1}`,
55
+ className: "max-h-[150px] max-w-[200px] cursor-pointer rounded-lg object-cover transition-transform group-hover:scale-105",
56
+ loading: "lazy",
57
+ src: image.url
58
+ })
59
+ }, image.url))
60
+ }),
61
+ files.length > 0 && /* @__PURE__ */ jsx("div", {
62
+ className: "flex flex-col gap-1",
63
+ children: files.map((file) => /* @__PURE__ */ jsxs("a", {
64
+ className: cn("flex items-center gap-2 rounded-lg px-3 py-2 text-xs transition-colors", {
65
+ "bg-co-background-300 text-co-foreground hover:bg-co-background-400 dark:bg-co-background-600 dark:hover:bg-co-background-500": !isSentByViewerFinal,
66
+ "bg-co-primary/80 text-co-primary-foreground hover:bg-co-primary": isSentByViewerFinal
67
+ }),
68
+ download: file.fileName,
69
+ href: file.url,
70
+ rel: "noopener noreferrer",
71
+ target: "_blank",
72
+ children: [
73
+ /* @__PURE__ */ jsx(icons_default, {
74
+ className: "h-4 w-4 shrink-0",
75
+ name: "file"
76
+ }),
77
+ /* @__PURE__ */ jsx("span", {
78
+ className: "flex-1 truncate font-medium",
79
+ children: file.fileName || "Download file"
80
+ }),
81
+ file.size && /* @__PURE__ */ jsx("span", {
82
+ className: "text-co-muted-foreground opacity-70",
83
+ children: formatFileSize(file.size)
84
+ })
85
+ ]
86
+ }, file.url))
87
+ }),
88
+ isLast && /* @__PURE__ */ jsx(TimelineItemTimestamp, {
89
+ className: "px-1 text-co-muted-foreground text-xs",
90
+ timestamp,
91
+ children: () => /* @__PURE__ */ jsxs(Fragment, { children: [timestamp.toLocaleTimeString([], {
92
+ hour: "2-digit",
93
+ minute: "2-digit"
94
+ }), isAI && ` ${text("component.message.timestamp.aiIndicator")}`] })
95
+ })
96
+ ]
38
97
  })
39
98
  });
40
99
  }
41
- });
100
+ }), images.length > 0 && /* @__PURE__ */ jsx(ImageLightbox, {
101
+ images,
102
+ initialIndex: lightboxIndex,
103
+ isOpen: lightboxOpen,
104
+ onClose: () => setLightboxOpen(false)
105
+ })] });
42
106
  }
43
107
 
44
108
  //#endregion
@@ -1 +1 @@
1
- {"version":3,"file":"timeline-message-item.js","names":["PrimitiveTimelineItem"],"sources":["../../../src/support/components/timeline-message-item.tsx"],"sourcesContent":["import type { TimelineItem } from \"@cossistant/types/api/timeline-item\";\nimport type React from \"react\";\nimport {\n\tTimelineItem as PrimitiveTimelineItem,\n\tTimelineItemContent,\n\tTimelineItemTimestamp,\n} from \"../../primitives/timeline-item\";\nimport { useSupportText } from \"../text\";\nimport { cn } from \"../utils\";\n\nexport type TimelineMessageItemProps = {\n\titem: TimelineItem;\n\tisLast?: boolean;\n\tisSentByViewer?: boolean;\n};\n\n/**\n * Message bubble renderer that adapts layout depending on whether the visitor\n * or an agent sent the message.\n */\nexport function TimelineMessageItem({\n\titem,\n\tisLast = false,\n\tisSentByViewer = false,\n}: TimelineMessageItemProps): React.ReactElement {\n\tconst text = useSupportText();\n\treturn (\n\t\t<PrimitiveTimelineItem item={item}>\n\t\t\t{({ isAI, timestamp }) => {\n\t\t\t\t// isSentByViewer defaults to false, meaning messages are treated as received\n\t\t\t\t// (left side with background) unless explicitly marked as sent by viewer\n\t\t\t\tconst isSentByViewerFinal = isSentByViewer;\n\n\t\t\t\treturn (\n\t\t\t\t\t<div\n\t\t\t\t\t\tclassName={cn(\n\t\t\t\t\t\t\t\"flex w-full gap-2\",\n\t\t\t\t\t\t\tisSentByViewerFinal && \"flex-row-reverse\",\n\t\t\t\t\t\t\t!isSentByViewerFinal && \"flex-row\"\n\t\t\t\t\t\t)}\n\t\t\t\t\t>\n\t\t\t\t\t\t<div\n\t\t\t\t\t\t\tclassName={cn(\n\t\t\t\t\t\t\t\t\"flex w-full min-w-0 flex-1 flex-col gap-1\",\n\t\t\t\t\t\t\t\tisSentByViewerFinal && \"items-end\"\n\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t>\n\t\t\t\t\t\t\t<TimelineItemContent\n\t\t\t\t\t\t\t\tclassName={cn(\n\t\t\t\t\t\t\t\t\t\"block min-w-0 max-w-[300px] whitespace-pre-wrap break-words rounded-lg px-3.5 py-2.5 text-sm\",\n\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\t\"bg-co-background-300 text-co-foreground dark:bg-co-background-600\":\n\t\t\t\t\t\t\t\t\t\t\t!isSentByViewerFinal,\n\t\t\t\t\t\t\t\t\t\t\"bg-co-primary text-co-primary-foreground\":\n\t\t\t\t\t\t\t\t\t\t\tisSentByViewerFinal,\n\t\t\t\t\t\t\t\t\t\t\"rounded-br-sm\": isLast && isSentByViewerFinal,\n\t\t\t\t\t\t\t\t\t\t\"rounded-bl-sm\": isLast && !isSentByViewerFinal,\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t\trenderMarkdown\n\t\t\t\t\t\t\t\ttext={item.text}\n\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t{isLast && (\n\t\t\t\t\t\t\t\t<TimelineItemTimestamp\n\t\t\t\t\t\t\t\t\tclassName=\"px-1 text-co-muted-foreground text-xs\"\n\t\t\t\t\t\t\t\t\ttimestamp={timestamp}\n\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t{() => (\n\t\t\t\t\t\t\t\t\t\t<>\n\t\t\t\t\t\t\t\t\t\t\t{timestamp.toLocaleTimeString([], {\n\t\t\t\t\t\t\t\t\t\t\t\thour: \"2-digit\",\n\t\t\t\t\t\t\t\t\t\t\t\tminute: \"2-digit\",\n\t\t\t\t\t\t\t\t\t\t\t})}\n\t\t\t\t\t\t\t\t\t\t\t{isAI &&\n\t\t\t\t\t\t\t\t\t\t\t\t` ${text(\"component.message.timestamp.aiIndicator\")}`}\n\t\t\t\t\t\t\t\t\t\t</>\n\t\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t\t</TimelineItemTimestamp>\n\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t</div>\n\t\t\t\t\t</div>\n\t\t\t\t);\n\t\t\t}}\n\t\t</PrimitiveTimelineItem>\n\t);\n}\n"],"mappings":";;;;;;;;;;AAoBA,SAAgB,oBAAoB,EACnC,MACA,SAAS,OACT,iBAAiB,SAC+B;CAChD,MAAM,OAAO,gBAAgB;AAC7B,QACC,oBAACA;EAA4B;aAC1B,EAAE,MAAM,gBAAgB;GAGzB,MAAM,sBAAsB;AAE5B,UACC,oBAAC;IACA,WAAW,GACV,qBACA,uBAAuB,oBACvB,CAAC,uBAAuB,WACxB;cAED,qBAAC;KACA,WAAW,GACV,6CACA,uBAAuB,YACvB;gBAED,oBAAC;MACA,WAAW,GACV,gGACA;OACC,qEACC,CAAC;OACF,4CACC;OACD,iBAAiB,UAAU;OAC3B,iBAAiB,UAAU,CAAC;OAC5B,CACD;MACD;MACA,MAAM,KAAK;OACV,EACD,UACA,oBAAC;MACA,WAAU;MACC;sBAGV,4CACE,UAAU,mBAAmB,EAAE,EAAE;OACjC,MAAM;OACN,QAAQ;OACR,CAAC,EACD,QACA,IAAI,KAAK,0CAA0C,MAClD;OAEmB;MAEpB;KACD;;GAGe"}
1
+ {"version":3,"file":"timeline-message-item.js","names":["PrimitiveTimelineItem","Icon"],"sources":["../../../src/support/components/timeline-message-item.tsx"],"sourcesContent":["import { formatFileSize } from \"@cossistant/core\";\nimport type { TimelineItem } from \"@cossistant/types/api/timeline-item\";\nimport type React from \"react\";\nimport { useState } from \"react\";\nimport {\n\tTimelineItem as PrimitiveTimelineItem,\n\tTimelineItemContent,\n\tTimelineItemTimestamp,\n} from \"../../primitives/timeline-item\";\nimport {\n\textractFileParts,\n\textractImageParts,\n} from \"../../primitives/timeline-item-attachments\";\nimport { useSupportText } from \"../text\";\nimport { cn } from \"../utils\";\nimport Icon from \"./icons\";\nimport { ImageLightbox } from \"./image-lightbox\";\n\nexport type TimelineMessageItemProps = {\n\titem: TimelineItem;\n\tisLast?: boolean;\n\tisSentByViewer?: boolean;\n};\n\n/**\n * Message bubble renderer that adapts layout depending on whether the visitor\n * or an agent sent the message.\n */\nexport function TimelineMessageItem({\n\titem,\n\tisLast = false,\n\tisSentByViewer = false,\n}: TimelineMessageItemProps): React.ReactElement {\n\tconst text = useSupportText();\n\tconst [lightboxOpen, setLightboxOpen] = useState(false);\n\tconst [lightboxIndex, setLightboxIndex] = useState(0);\n\n\t// Extract image and file parts\n\tconst images = extractImageParts(item.parts);\n\tconst files = extractFileParts(item.parts);\n\tconst hasAttachments = images.length > 0 || files.length > 0;\n\tconst hasText = item.text && item.text.trim().length > 0;\n\n\tconst openLightbox = (index: number) => {\n\t\tsetLightboxIndex(index);\n\t\tsetLightboxOpen(true);\n\t};\n\n\treturn (\n\t\t<>\n\t\t\t<PrimitiveTimelineItem item={item}>\n\t\t\t\t{({ isAI, timestamp }) => {\n\t\t\t\t\t// isSentByViewer defaults to false, meaning messages are treated as received\n\t\t\t\t\t// (left side with background) unless explicitly marked as sent by viewer\n\t\t\t\t\tconst isSentByViewerFinal = isSentByViewer;\n\n\t\t\t\t\treturn (\n\t\t\t\t\t\t<div\n\t\t\t\t\t\t\tclassName={cn(\n\t\t\t\t\t\t\t\t\"flex w-full gap-2\",\n\t\t\t\t\t\t\t\tisSentByViewerFinal && \"flex-row-reverse\",\n\t\t\t\t\t\t\t\t!isSentByViewerFinal && \"flex-row\"\n\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t>\n\t\t\t\t\t\t\t<div\n\t\t\t\t\t\t\t\tclassName={cn(\n\t\t\t\t\t\t\t\t\t\"flex w-full min-w-0 flex-1 flex-col gap-1\",\n\t\t\t\t\t\t\t\t\tisSentByViewerFinal && \"items-end\"\n\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t{/* Text content */}\n\t\t\t\t\t\t\t\t{hasText && (\n\t\t\t\t\t\t\t\t\t<TimelineItemContent\n\t\t\t\t\t\t\t\t\t\tclassName={cn(\n\t\t\t\t\t\t\t\t\t\t\t\"block min-w-0 max-w-[300px] whitespace-pre-wrap break-words rounded-lg px-3.5 py-2.5 text-sm\",\n\t\t\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\t\t\t\"bg-co-background-300 text-co-foreground dark:bg-co-background-600\":\n\t\t\t\t\t\t\t\t\t\t\t\t\t!isSentByViewerFinal,\n\t\t\t\t\t\t\t\t\t\t\t\t\"bg-co-primary text-co-primary-foreground\":\n\t\t\t\t\t\t\t\t\t\t\t\t\tisSentByViewerFinal,\n\t\t\t\t\t\t\t\t\t\t\t\t\"rounded-br-sm\":\n\t\t\t\t\t\t\t\t\t\t\t\t\tisLast && isSentByViewerFinal && !hasAttachments,\n\t\t\t\t\t\t\t\t\t\t\t\t\"rounded-bl-sm\":\n\t\t\t\t\t\t\t\t\t\t\t\t\tisLast && !isSentByViewerFinal && !hasAttachments,\n\t\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t\t\t\trenderMarkdown\n\t\t\t\t\t\t\t\t\t\ttext={item.text}\n\t\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t)}\n\n\t\t\t\t\t\t\t\t{/* Image attachments */}\n\t\t\t\t\t\t\t\t{images.length > 0 && (\n\t\t\t\t\t\t\t\t\t<div\n\t\t\t\t\t\t\t\t\t\tclassName={cn(\n\t\t\t\t\t\t\t\t\t\t\t\"flex flex-wrap gap-2\",\n\t\t\t\t\t\t\t\t\t\t\tisSentByViewerFinal && \"justify-end\"\n\t\t\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t{images.map((image, index) => (\n\t\t\t\t\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\t\t\t\t\tclassName=\"group relative overflow-hidden rounded-lg focus:outline-none focus:ring-2 focus:ring-co-primary/50\"\n\t\t\t\t\t\t\t\t\t\t\t\tkey={image.url}\n\t\t\t\t\t\t\t\t\t\t\t\tonClick={() => openLightbox(index)}\n\t\t\t\t\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t\t{/* biome-ignore lint/performance/noImgElement: React package, not Next.js specific */}\n\t\t\t\t\t\t\t\t\t\t\t\t{/* biome-ignore lint/nursery/useImageSize: Dynamic image dimensions not known at render time */}\n\t\t\t\t\t\t\t\t\t\t\t\t<img\n\t\t\t\t\t\t\t\t\t\t\t\t\talt={image.fileName || `Image ${index + 1}`}\n\t\t\t\t\t\t\t\t\t\t\t\t\tclassName=\"max-h-[150px] max-w-[200px] cursor-pointer rounded-lg object-cover transition-transform group-hover:scale-105\"\n\t\t\t\t\t\t\t\t\t\t\t\t\tloading=\"lazy\"\n\t\t\t\t\t\t\t\t\t\t\t\t\tsrc={image.url}\n\t\t\t\t\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t\t\t\t</button>\n\t\t\t\t\t\t\t\t\t\t))}\n\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t)}\n\n\t\t\t\t\t\t\t\t{/* File attachments */}\n\t\t\t\t\t\t\t\t{files.length > 0 && (\n\t\t\t\t\t\t\t\t\t<div className=\"flex flex-col gap-1\">\n\t\t\t\t\t\t\t\t\t\t{files.map((file) => (\n\t\t\t\t\t\t\t\t\t\t\t<a\n\t\t\t\t\t\t\t\t\t\t\t\tclassName={cn(\n\t\t\t\t\t\t\t\t\t\t\t\t\t\"flex items-center gap-2 rounded-lg px-3 py-2 text-xs transition-colors\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"bg-co-background-300 text-co-foreground hover:bg-co-background-400 dark:bg-co-background-600 dark:hover:bg-co-background-500\":\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t!isSentByViewerFinal,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"bg-co-primary/80 text-co-primary-foreground hover:bg-co-primary\":\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tisSentByViewerFinal,\n\t\t\t\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t\t\t\t\t\tdownload={file.fileName}\n\t\t\t\t\t\t\t\t\t\t\t\thref={file.url}\n\t\t\t\t\t\t\t\t\t\t\t\tkey={file.url}\n\t\t\t\t\t\t\t\t\t\t\t\trel=\"noopener noreferrer\"\n\t\t\t\t\t\t\t\t\t\t\t\ttarget=\"_blank\"\n\t\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t\t<Icon className=\"h-4 w-4 shrink-0\" name=\"file\" />\n\t\t\t\t\t\t\t\t\t\t\t\t<span className=\"flex-1 truncate font-medium\">\n\t\t\t\t\t\t\t\t\t\t\t\t\t{file.fileName || \"Download file\"}\n\t\t\t\t\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t\t\t\t\t\t{file.size && (\n\t\t\t\t\t\t\t\t\t\t\t\t\t<span className=\"text-co-muted-foreground opacity-70\">\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t{formatFileSize(file.size)}\n\t\t\t\t\t\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t\t\t\t\t</a>\n\t\t\t\t\t\t\t\t\t\t))}\n\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t)}\n\n\t\t\t\t\t\t\t\t{isLast && (\n\t\t\t\t\t\t\t\t\t<TimelineItemTimestamp\n\t\t\t\t\t\t\t\t\t\tclassName=\"px-1 text-co-muted-foreground text-xs\"\n\t\t\t\t\t\t\t\t\t\ttimestamp={timestamp}\n\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t{() => (\n\t\t\t\t\t\t\t\t\t\t\t<>\n\t\t\t\t\t\t\t\t\t\t\t\t{timestamp.toLocaleTimeString([], {\n\t\t\t\t\t\t\t\t\t\t\t\t\thour: \"2-digit\",\n\t\t\t\t\t\t\t\t\t\t\t\t\tminute: \"2-digit\",\n\t\t\t\t\t\t\t\t\t\t\t\t})}\n\t\t\t\t\t\t\t\t\t\t\t\t{isAI &&\n\t\t\t\t\t\t\t\t\t\t\t\t\t` ${text(\"component.message.timestamp.aiIndicator\")}`}\n\t\t\t\t\t\t\t\t\t\t\t</>\n\t\t\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t\t\t</TimelineItemTimestamp>\n\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t);\n\t\t\t\t}}\n\t\t\t</PrimitiveTimelineItem>\n\n\t\t\t{/* Lightbox for images */}\n\t\t\t{images.length > 0 && (\n\t\t\t\t<ImageLightbox\n\t\t\t\t\timages={images}\n\t\t\t\t\tinitialIndex={lightboxIndex}\n\t\t\t\t\tisOpen={lightboxOpen}\n\t\t\t\t\tonClose={() => setLightboxOpen(false)}\n\t\t\t\t/>\n\t\t\t)}\n\t\t</>\n\t);\n}\n"],"mappings":";;;;;;;;;;;;;;;AA4BA,SAAgB,oBAAoB,EACnC,MACA,SAAS,OACT,iBAAiB,SAC+B;CAChD,MAAM,OAAO,gBAAgB;CAC7B,MAAM,CAAC,cAAc,mBAAmB,SAAS,MAAM;CACvD,MAAM,CAAC,eAAe,oBAAoB,SAAS,EAAE;CAGrD,MAAM,SAAS,kBAAkB,KAAK,MAAM;CAC5C,MAAM,QAAQ,iBAAiB,KAAK,MAAM;CAC1C,MAAM,iBAAiB,OAAO,SAAS,KAAK,MAAM,SAAS;CAC3D,MAAM,UAAU,KAAK,QAAQ,KAAK,KAAK,MAAM,CAAC,SAAS;CAEvD,MAAM,gBAAgB,UAAkB;AACvC,mBAAiB,MAAM;AACvB,kBAAgB,KAAK;;AAGtB,QACC,4CACC,oBAACA;EAA4B;aAC1B,EAAE,MAAM,gBAAgB;GAGzB,MAAM,sBAAsB;AAE5B,UACC,oBAAC;IACA,WAAW,GACV,qBACA,uBAAuB,oBACvB,CAAC,uBAAuB,WACxB;cAED,qBAAC;KACA,WAAW,GACV,6CACA,uBAAuB,YACvB;;MAGA,WACA,oBAAC;OACA,WAAW,GACV,gGACA;QACC,qEACC,CAAC;QACF,4CACC;QACD,iBACC,UAAU,uBAAuB,CAAC;QACnC,iBACC,UAAU,CAAC,uBAAuB,CAAC;QACpC,CACD;OACD;OACA,MAAM,KAAK;QACV;MAIF,OAAO,SAAS,KAChB,oBAAC;OACA,WAAW,GACV,wBACA,uBAAuB,cACvB;iBAEA,OAAO,KAAK,OAAO,UACnB,oBAAC;QACA,WAAU;QAEV,eAAe,aAAa,MAAM;QAClC,MAAK;kBAIL,oBAAC;SACA,KAAK,MAAM,YAAY,SAAS,QAAQ;SACxC,WAAU;SACV,SAAQ;SACR,KAAK,MAAM;UACV;UAXG,MAAM,IAYH,CACR;QACG;MAIN,MAAM,SAAS,KACf,oBAAC;OAAI,WAAU;iBACb,MAAM,KAAK,SACX,qBAAC;QACA,WAAW,GACV,0EACA;SACC,gIACC,CAAC;SACF,mEACC;SACD,CACD;QACD,UAAU,KAAK;QACf,MAAM,KAAK;QAEX,KAAI;QACJ,QAAO;;SAEP,oBAACC;UAAK,WAAU;UAAmB,MAAK;WAAS;SACjD,oBAAC;UAAK,WAAU;oBACd,KAAK,YAAY;WACZ;SACN,KAAK,QACL,oBAAC;UAAK,WAAU;oBACd,eAAe,KAAK,KAAK;WACpB;;UAXH,KAAK,IAaP,CACH;QACG;MAGN,UACA,oBAAC;OACA,WAAU;OACC;uBAGV,4CACE,UAAU,mBAAmB,EAAE,EAAE;QACjC,MAAM;QACN,QAAQ;QACR,CAAC,EACD,QACA,IAAI,KAAK,0CAA0C,MAClD;QAEmB;;MAEpB;KACD;;GAGe,EAGvB,OAAO,SAAS,KAChB,oBAAC;EACQ;EACR,cAAc;EACd,QAAQ;EACR,eAAe,gBAAgB,MAAM;GACpC,IAED"}
@@ -0,0 +1,14 @@
1
+ import * as React$1 from "react";
2
+
3
+ //#region src/support/components/trigger.d.ts
4
+ type DefaultTriggerProps = {
5
+ className?: string;
6
+ };
7
+ /**
8
+ * Default styled trigger button.
9
+ * Used internally when no custom trigger is provided.
10
+ */
11
+ declare const DefaultTrigger: React$1.FC<DefaultTriggerProps>;
12
+ //#endregion
13
+ export { DefaultTrigger, DefaultTriggerProps };
14
+ //# sourceMappingURL=trigger.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"trigger.d.ts","names":[],"sources":["../../../src/support/components/trigger.tsx"],"sourcesContent":[],"mappings":";;;KA2HY,mBAAA;;AAAZ,CAAA;AAQA;;;;cAAa,gBAAgB,OAAA,CAAM,GAAG"}
@@ -3,26 +3,26 @@
3
3
 
4
4
  import { cn } from "../utils/index.js";
5
5
  import { BouncingDots } from "./typing-indicator.js";
6
- import { SupportBubble } from "../../primitives/bubble.js";
7
- import icons_default from "./icons.js";
6
+ import { SupportTrigger } from "../../primitives/trigger.js";
8
7
  import { useNewMessageSound } from "../../hooks/use-new-message-sound.js";
9
8
  import { useTypingSound } from "../../hooks/use-typing-sound.js";
10
- import { useEffect, useRef } from "react";
9
+ import icons_default from "./icons.js";
10
+ import * as React$1 from "react";
11
11
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
12
12
  import { AnimatePresence, motion } from "motion/react";
13
13
 
14
- //#region src/support/components/bubble.tsx
15
- const BubbleContent = ({ isOpen, unreadCount, isTyping }) => {
14
+ //#region src/support/components/trigger.tsx
15
+ const TriggerContent = ({ isOpen, unreadCount, isTyping }) => {
16
16
  const playNewMessageSound = useNewMessageSound({
17
17
  volume: .7,
18
18
  playbackRate: 1
19
19
  });
20
- const previousUnreadCountRef = useRef(0);
20
+ const previousUnreadCountRef = React$1.useRef(0);
21
21
  useTypingSound(!isOpen && isTyping, {
22
22
  volume: 1,
23
23
  playbackRate: 1.3
24
24
  });
25
- useEffect(() => {
25
+ React$1.useEffect(() => {
26
26
  if (unreadCount > previousUnreadCountRef.current) playNewMessageSound();
27
27
  previousUnreadCountRef.current = unreadCount;
28
28
  }, [unreadCount, playNewMessageSound]);
@@ -127,10 +127,14 @@ const BubbleContent = ({ isOpen, unreadCount, isTyping }) => {
127
127
  }
128
128
  })] });
129
129
  };
130
- const Bubble = ({ className }) => /* @__PURE__ */ jsx(SupportBubble, {
130
+ /**
131
+ * Default styled trigger button.
132
+ * Used internally when no custom trigger is provided.
133
+ */
134
+ const DefaultTrigger = ({ className }) => /* @__PURE__ */ jsx(SupportTrigger, {
131
135
  asChild: true,
132
136
  children: ({ isOpen, unreadCount, isTyping }) => /* @__PURE__ */ jsx(motion.button, {
133
- className: cn("relative flex size-14 cursor-pointer items-center justify-center rounded-full bg-co-primary text-co-primary-foreground transition-colors hover:bg-co-primary/90 data-[open=true]:bg-co-primary/90", className),
137
+ className: cn("relative z-[9999] flex size-14 cursor-pointer items-center justify-center rounded-full bg-co-primary text-co-primary-foreground transition-colors hover:bg-co-primary/90 data-[open=true]:bg-co-primary/90", className),
134
138
  "data-open": isOpen,
135
139
  transition: {
136
140
  type: "spring",
@@ -139,7 +143,7 @@ const Bubble = ({ className }) => /* @__PURE__ */ jsx(SupportBubble, {
139
143
  },
140
144
  type: "button",
141
145
  whileTap: { scale: .95 },
142
- children: /* @__PURE__ */ jsx(BubbleContent, {
146
+ children: /* @__PURE__ */ jsx(TriggerContent, {
143
147
  isOpen,
144
148
  isTyping,
145
149
  unreadCount
@@ -148,5 +152,5 @@ const Bubble = ({ className }) => /* @__PURE__ */ jsx(SupportBubble, {
148
152
  });
149
153
 
150
154
  //#endregion
151
- export { Bubble };
152
- //# sourceMappingURL=bubble.js.map
155
+ export { DefaultTrigger };
156
+ //# sourceMappingURL=trigger.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"trigger.js","names":["TriggerContent: React.FC<TriggerContentProps>","React","Icon","DefaultTrigger: React.FC<DefaultTriggerProps>","Primitive.Trigger"],"sources":["../../../src/support/components/trigger.tsx"],"sourcesContent":["\"use client\";\n\nimport { AnimatePresence, motion } from \"motion/react\";\nimport * as React from \"react\";\nimport { useNewMessageSound } from \"../../hooks/use-new-message-sound\";\nimport { useTypingSound } from \"../../hooks/use-typing-sound\";\nimport * as Primitive from \"../../primitives\";\nimport type { TriggerRenderProps } from \"../types\";\nimport { cn } from \"../utils\";\nimport Icon from \"./icons\";\nimport { BouncingDots } from \"./typing-indicator\";\n\ntype TriggerContentProps = {\n\tisOpen: boolean;\n\tunreadCount: number;\n\tisTyping: boolean;\n};\n\nconst TriggerContent: React.FC<TriggerContentProps> = ({\n\tisOpen,\n\tunreadCount,\n\tisTyping,\n}) => {\n\tconst playNewMessageSound = useNewMessageSound({\n\t\tvolume: 0.7,\n\t\tplaybackRate: 1.0,\n\t});\n\tconst previousUnreadCountRef = React.useRef(0);\n\n\tuseTypingSound(!isOpen && isTyping, {\n\t\tvolume: 1,\n\t\tplaybackRate: 1.3,\n\t});\n\n\tReact.useEffect(() => {\n\t\tif (unreadCount > previousUnreadCountRef.current) {\n\t\t\tplayNewMessageSound();\n\t\t}\n\t\tpreviousUnreadCountRef.current = unreadCount;\n\t}, [unreadCount, playNewMessageSound]);\n\n\treturn (\n\t\t<>\n\t\t\t<AnimatePresence mode=\"wait\">\n\t\t\t\t{isOpen ? (\n\t\t\t\t\t<motion.div\n\t\t\t\t\t\tanimate={{\n\t\t\t\t\t\t\tscale: 1,\n\t\t\t\t\t\t\trotate: 0,\n\t\t\t\t\t\t\topacity: 1,\n\t\t\t\t\t\t\ttransition: { duration: 0.2, ease: \"easeOut\" },\n\t\t\t\t\t\t}}\n\t\t\t\t\t\tclassName=\"flex items-center justify-center\"\n\t\t\t\t\t\texit={{\n\t\t\t\t\t\t\tscale: 0.9,\n\t\t\t\t\t\t\trotate: -45,\n\t\t\t\t\t\t\topacity: 0,\n\t\t\t\t\t\t\ttransition: { duration: 0.1, ease: \"easeIn\" },\n\t\t\t\t\t\t}}\n\t\t\t\t\t\tinitial={{ scale: 0.9, rotate: 45, opacity: 0 }}\n\t\t\t\t\t\tkey=\"chevron\"\n\t\t\t\t\t>\n\t\t\t\t\t\t<Icon className=\"size-5\" name=\"chevron-down\" />\n\t\t\t\t\t</motion.div>\n\t\t\t\t) : isTyping ? (\n\t\t\t\t\t<motion.span\n\t\t\t\t\t\tanimate={{\n\t\t\t\t\t\t\topacity: 1,\n\t\t\t\t\t\t\tscale: 1,\n\t\t\t\t\t\t\ttransition: {\n\t\t\t\t\t\t\t\tduration: 0.2,\n\t\t\t\t\t\t\t\tease: \"easeOut\",\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t}}\n\t\t\t\t\t\tclassName=\"pointer-events-none flex items-center rounded-full text-co-primary\"\n\t\t\t\t\t\texit={{\n\t\t\t\t\t\t\topacity: 0,\n\t\t\t\t\t\t\tscale: 0.9,\n\t\t\t\t\t\t\ttransition: {\n\t\t\t\t\t\t\t\tduration: 0.1,\n\t\t\t\t\t\t\t\tease: \"easeIn\",\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t}}\n\t\t\t\t\t\tinitial={{ opacity: 0, scale: 0.9 }}\n\t\t\t\t\t\tkey=\"typing-indicator\"\n\t\t\t\t\t>\n\t\t\t\t\t\t<BouncingDots className=\"bg-co-primary-foreground\" />\n\t\t\t\t\t</motion.span>\n\t\t\t\t) : (\n\t\t\t\t\t<motion.div\n\t\t\t\t\t\tanimate={{\n\t\t\t\t\t\t\tscale: 1,\n\t\t\t\t\t\t\trotate: 0,\n\t\t\t\t\t\t\topacity: 1,\n\t\t\t\t\t\t\ttransition: { duration: 0.2, ease: \"easeOut\" },\n\t\t\t\t\t\t}}\n\t\t\t\t\t\tclassName=\"flex items-center justify-center\"\n\t\t\t\t\t\texit={{\n\t\t\t\t\t\t\tscale: 0.9,\n\t\t\t\t\t\t\trotate: 45,\n\t\t\t\t\t\t\topacity: 0,\n\t\t\t\t\t\t\ttransition: { duration: 0.1, ease: \"easeIn\" },\n\t\t\t\t\t\t}}\n\t\t\t\t\t\tinitial={{ scale: 0.9, rotate: -45, opacity: 0 }}\n\t\t\t\t\t\tkey=\"chat\"\n\t\t\t\t\t>\n\t\t\t\t\t\t<Icon className=\"size-6.5\" name=\"chat\" variant=\"filled\" />\n\t\t\t\t\t</motion.div>\n\t\t\t\t)}\n\t\t\t</AnimatePresence>\n\n\t\t\t{unreadCount > 0 && (\n\t\t\t\t<motion.div\n\t\t\t\t\tanimate={{ scale: 1, opacity: 1 }}\n\t\t\t\t\tclassName=\"absolute top-0.5 right-0.5 flex size-2 items-center justify-center rounded-full bg-co-destructive font-medium text-[10px] text-co-destructive-foreground text-white text-xs\"\n\t\t\t\t\texit={{ scale: 0, opacity: 0 }}\n\t\t\t\t\tinitial={{ scale: 0, opacity: 0 }}\n\t\t\t\t/>\n\t\t\t)}\n\t\t</>\n\t);\n};\n\nexport type DefaultTriggerProps = {\n\tclassName?: string;\n};\n\n/**\n * Default styled trigger button.\n * Used internally when no custom trigger is provided.\n */\nexport const DefaultTrigger: React.FC<DefaultTriggerProps> = ({\n\tclassName,\n}) => (\n\t<Primitive.Trigger asChild>\n\t\t{({ isOpen, unreadCount, isTyping }: TriggerRenderProps) => (\n\t\t\t<motion.button\n\t\t\t\tclassName={cn(\n\t\t\t\t\t\"relative z-[9999] flex size-14 cursor-pointer items-center justify-center rounded-full bg-co-primary text-co-primary-foreground transition-colors hover:bg-co-primary/90 data-[open=true]:bg-co-primary/90\",\n\t\t\t\t\tclassName\n\t\t\t\t)}\n\t\t\t\tdata-open={isOpen}\n\t\t\t\ttransition={{\n\t\t\t\t\ttype: \"spring\",\n\t\t\t\t\tstiffness: 800,\n\t\t\t\t\tdamping: 17,\n\t\t\t\t}}\n\t\t\t\ttype=\"button\"\n\t\t\t\twhileTap={{ scale: 0.95 }}\n\t\t\t>\n\t\t\t\t<TriggerContent\n\t\t\t\t\tisOpen={isOpen}\n\t\t\t\t\tisTyping={isTyping}\n\t\t\t\t\tunreadCount={unreadCount}\n\t\t\t\t/>\n\t\t\t</motion.button>\n\t\t)}\n\t</Primitive.Trigger>\n);\n"],"mappings":";;;;;;;;;;;;;;AAkBA,MAAMA,kBAAiD,EACtD,QACA,aACA,eACK;CACL,MAAM,sBAAsB,mBAAmB;EAC9C,QAAQ;EACR,cAAc;EACd,CAAC;CACF,MAAM,yBAAyBC,QAAM,OAAO,EAAE;AAE9C,gBAAe,CAAC,UAAU,UAAU;EACnC,QAAQ;EACR,cAAc;EACd,CAAC;AAEF,SAAM,gBAAgB;AACrB,MAAI,cAAc,uBAAuB,QACxC,sBAAqB;AAEtB,yBAAuB,UAAU;IAC/B,CAAC,aAAa,oBAAoB,CAAC;AAEtC,QACC,4CACC,oBAAC;EAAgB,MAAK;YACpB,SACA,oBAAC,OAAO;GACP,SAAS;IACR,OAAO;IACP,QAAQ;IACR,SAAS;IACT,YAAY;KAAE,UAAU;KAAK,MAAM;KAAW;IAC9C;GACD,WAAU;GACV,MAAM;IACL,OAAO;IACP,QAAQ;IACR,SAAS;IACT,YAAY;KAAE,UAAU;KAAK,MAAM;KAAU;IAC7C;GACD,SAAS;IAAE,OAAO;IAAK,QAAQ;IAAI,SAAS;IAAG;aAG/C,oBAACC;IAAK,WAAU;IAAS,MAAK;KAAiB;KAF3C,UAGQ,GACV,WACH,oBAAC,OAAO;GACP,SAAS;IACR,SAAS;IACT,OAAO;IACP,YAAY;KACX,UAAU;KACV,MAAM;KACN;IACD;GACD,WAAU;GACV,MAAM;IACL,SAAS;IACT,OAAO;IACP,YAAY;KACX,UAAU;KACV,MAAM;KACN;IACD;GACD,SAAS;IAAE,SAAS;IAAG,OAAO;IAAK;aAGnC,oBAAC,gBAAa,WAAU,6BAA6B;KAFjD,mBAGS,GAEd,oBAAC,OAAO;GACP,SAAS;IACR,OAAO;IACP,QAAQ;IACR,SAAS;IACT,YAAY;KAAE,UAAU;KAAK,MAAM;KAAW;IAC9C;GACD,WAAU;GACV,MAAM;IACL,OAAO;IACP,QAAQ;IACR,SAAS;IACT,YAAY;KAAE,UAAU;KAAK,MAAM;KAAU;IAC7C;GACD,SAAS;IAAE,OAAO;IAAK,QAAQ;IAAK,SAAS;IAAG;aAGhD,oBAACA;IAAK,WAAU;IAAW,MAAK;IAAO,SAAQ;KAAW;KAFtD,OAGQ;GAEG,EAEjB,cAAc,KACd,oBAAC,OAAO;EACP,SAAS;GAAE,OAAO;GAAG,SAAS;GAAG;EACjC,WAAU;EACV,MAAM;GAAE,OAAO;GAAG,SAAS;GAAG;EAC9B,SAAS;GAAE,OAAO;GAAG,SAAS;GAAG;GAChC,IAED;;;;;;AAYL,MAAaC,kBAAiD,EAC7D,gBAEA,oBAACC;CAAkB;YAChB,EAAE,QAAQ,aAAa,eACxB,oBAAC,OAAO;EACP,WAAW,GACV,8MACA,UACA;EACD,aAAW;EACX,YAAY;GACX,MAAM;GACN,WAAW;GACX,SAAS;GACT;EACD,MAAK;EACL,UAAU,EAAE,OAAO,KAAM;YAEzB,oBAAC;GACQ;GACE;GACG;IACZ;GACa;EAEE"}