@cossistant/react 0.0.20 → 0.0.23

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 (91) hide show
  1. package/conversation.d.ts +28 -0
  2. package/conversation.d.ts.map +1 -1
  3. package/hooks/index.d.ts +4 -1
  4. package/hooks/index.js +4 -1
  5. package/hooks/private/use-grouped-messages.d.ts.map +1 -1
  6. package/hooks/private/use-grouped-messages.js +3 -17
  7. package/hooks/private/use-grouped-messages.js.map +1 -1
  8. package/hooks/private/use-multimodal-input.d.ts.map +1 -1
  9. package/hooks/private/use-multimodal-input.js +16 -3
  10. package/hooks/private/use-multimodal-input.js.map +1 -1
  11. package/hooks/use-composer-refocus.js +1 -1
  12. package/hooks/use-composer-refocus.js.map +1 -1
  13. package/hooks/use-conversation-auto-seen.d.ts +1 -1
  14. package/hooks/use-conversation-auto-seen.js +30 -46
  15. package/hooks/use-conversation-auto-seen.js.map +1 -1
  16. package/hooks/use-conversation-seen.d.ts.map +1 -1
  17. package/hooks/use-conversation-seen.js +7 -3
  18. package/hooks/use-conversation-seen.js.map +1 -1
  19. package/hooks/use-new-message-sound.d.ts +23 -0
  20. package/hooks/use-new-message-sound.d.ts.map +1 -0
  21. package/hooks/use-new-message-sound.js +34 -0
  22. package/hooks/use-new-message-sound.js.map +1 -0
  23. package/hooks/use-sound-effect.d.ts +30 -0
  24. package/hooks/use-sound-effect.d.ts.map +1 -0
  25. package/hooks/use-sound-effect.js +104 -0
  26. package/hooks/use-sound-effect.js.map +1 -0
  27. package/hooks/use-typing-sound.d.ts +18 -0
  28. package/hooks/use-typing-sound.d.ts.map +1 -0
  29. package/hooks/use-typing-sound.js +38 -0
  30. package/hooks/use-typing-sound.js.map +1 -0
  31. package/index.d.ts +5 -2
  32. package/index.js +8 -6
  33. package/package.json +3 -3
  34. package/primitives/avatar/image.d.ts +1 -1
  35. package/primitives/bubble.js +1 -1
  36. package/primitives/index.d.ts +3 -5
  37. package/primitives/index.js +3 -9
  38. package/primitives/index.parts.d.ts +2 -4
  39. package/primitives/index.parts.js +2 -4
  40. package/primitives/router.d.ts +19 -20
  41. package/primitives/router.d.ts.map +1 -1
  42. package/primitives/router.js +17 -11
  43. package/primitives/router.js.map +1 -1
  44. package/realtime/index.js +1 -1
  45. package/realtime/provider.js +1 -1
  46. package/realtime-events.d.ts +83 -0
  47. package/realtime-events.d.ts.map +1 -1
  48. package/schemas3.d.ts +7 -0
  49. package/schemas3.d.ts.map +1 -1
  50. package/support/components/bubble.d.ts.map +1 -1
  51. package/support/components/bubble.js +27 -4
  52. package/support/components/bubble.js.map +1 -1
  53. package/support/components/button.d.ts +1 -1
  54. package/support/components/conversation-event.js +1 -1
  55. package/support/components/conversation-event.js.map +1 -1
  56. package/support/components/conversation-timeline.d.ts.map +1 -1
  57. package/support/components/conversation-timeline.js +5 -0
  58. package/support/components/conversation-timeline.js.map +1 -1
  59. package/support/components/multimodal-input.d.ts.map +1 -1
  60. package/support/components/multimodal-input.js +6 -2
  61. package/support/components/multimodal-input.js.map +1 -1
  62. package/support/components/support-content.d.ts +2 -0
  63. package/support/components/support-content.d.ts.map +1 -1
  64. package/support/components/support-content.js +5 -2
  65. package/support/components/support-content.js.map +1 -1
  66. package/support/components/timeline-message-group.js +2 -2
  67. package/support/components/timeline-message-group.js.map +1 -1
  68. package/support/components/timeline-message-item.js +2 -2
  69. package/support/components/timeline-message-item.js.map +1 -1
  70. package/support/index.d.ts +12 -7
  71. package/support/index.d.ts.map +1 -1
  72. package/support/index.js +28 -29
  73. package/support/index.js.map +1 -1
  74. package/support/pages/conversation.d.ts.map +1 -1
  75. package/support/pages/conversation.js +19 -1
  76. package/support/pages/conversation.js.map +1 -1
  77. package/support/router.d.ts +19 -9
  78. package/support/router.d.ts.map +1 -1
  79. package/support/router.js +31 -30
  80. package/support/router.js.map +1 -1
  81. package/timeline-item.d.ts +14 -0
  82. package/timeline-item.d.ts.map +1 -1
  83. package/utils/use-render-element.d.ts.map +1 -1
  84. package/primitives/page-registry.d.ts +0 -30
  85. package/primitives/page-registry.d.ts.map +0 -1
  86. package/primitives/page-registry.js +0 -45
  87. package/primitives/page-registry.js.map +0 -1
  88. package/primitives/page.d.ts +0 -21
  89. package/primitives/page.d.ts.map +0 -1
  90. package/primitives/page.js +0 -18
  91. package/primitives/page.js.map +0 -1
@@ -2,9 +2,9 @@
2
2
 
3
3
 
4
4
  import { createContext, useCallback, useContext, useEffect, useMemo, useRef, useState } from "react";
5
- import { jsx } from "react/jsx-runtime";
6
5
  import { isValidEventType, validateRealtimeEvent } from "@cossistant/types/realtime-events";
7
6
  import useWebSocket, { ReadyState } from "react-use-websocket";
7
+ import { jsx } from "react/jsx-runtime";
8
8
 
9
9
  //#region src/realtime/provider.tsx
10
10
  const DEFAULT_HEARTBEAT_INTERVAL_MS = 15e3;
@@ -171,6 +171,13 @@ declare const realtimeSchema: {
171
171
  mediaType: ZodString;
172
172
  fileName: ZodOptional<ZodString>;
173
173
  size: ZodOptional<ZodNumber>;
174
+ }, $strip>, ZodObject<{
175
+ type: ZodLiteral<"metadata">;
176
+ source: ZodEnum<{
177
+ email: "email";
178
+ widget: "widget";
179
+ api: "api";
180
+ }>;
174
181
  }, $strip>]>>;
175
182
  userId: ZodNullable<ZodString>;
176
183
  aiAgentId: ZodNullable<ZodString>;
@@ -222,6 +229,75 @@ declare const realtimeSchema: {
222
229
  deletedAt: ZodNullable<ZodString>;
223
230
  lastMessageAt: ZodNullable<ZodString>;
224
231
  lastSeenAt: ZodNullable<ZodString>;
232
+ lastMessageTimelineItem: ZodNullable<ZodObject<{
233
+ id: ZodOptional<ZodString>;
234
+ conversationId: ZodString;
235
+ organizationId: ZodString;
236
+ visibility: ZodEnum<{
237
+ public: "public";
238
+ private: "private";
239
+ }>;
240
+ type: ZodEnum<{
241
+ message: "message";
242
+ event: "event";
243
+ identification: "identification";
244
+ }>;
245
+ text: ZodNullable<ZodString>;
246
+ tool: ZodOptional<ZodNullable<ZodString>>;
247
+ parts: ZodArray<ZodUnion<readonly [ZodObject<{
248
+ type: ZodLiteral<"text">;
249
+ text: ZodString;
250
+ }, $strip>, ZodObject<{
251
+ type: ZodLiteral<"event">;
252
+ eventType: ZodEnum<{
253
+ assigned: "assigned";
254
+ unassigned: "unassigned";
255
+ participant_requested: "participant_requested";
256
+ participant_joined: "participant_joined";
257
+ participant_left: "participant_left";
258
+ status_changed: "status_changed";
259
+ priority_changed: "priority_changed";
260
+ tag_added: "tag_added";
261
+ tag_removed: "tag_removed";
262
+ resolved: "resolved";
263
+ reopened: "reopened";
264
+ visitor_blocked: "visitor_blocked";
265
+ visitor_unblocked: "visitor_unblocked";
266
+ visitor_identified: "visitor_identified";
267
+ }>;
268
+ actorUserId: ZodNullable<ZodString>;
269
+ actorAiAgentId: ZodNullable<ZodString>;
270
+ targetUserId: ZodNullable<ZodString>;
271
+ targetAiAgentId: ZodNullable<ZodString>;
272
+ message: ZodOptional<ZodNullable<ZodString>>;
273
+ }, $strip>, ZodObject<{
274
+ type: ZodLiteral<"image">;
275
+ url: ZodString;
276
+ mediaType: ZodString;
277
+ fileName: ZodOptional<ZodString>;
278
+ size: ZodOptional<ZodNumber>;
279
+ width: ZodOptional<ZodNumber>;
280
+ height: ZodOptional<ZodNumber>;
281
+ }, $strip>, ZodObject<{
282
+ type: ZodLiteral<"file">;
283
+ url: ZodString;
284
+ mediaType: ZodString;
285
+ fileName: ZodOptional<ZodString>;
286
+ size: ZodOptional<ZodNumber>;
287
+ }, $strip>, ZodObject<{
288
+ type: ZodLiteral<"metadata">;
289
+ source: ZodEnum<{
290
+ email: "email";
291
+ widget: "widget";
292
+ api: "api";
293
+ }>;
294
+ }, $strip>]>>;
295
+ userId: ZodNullable<ZodString>;
296
+ aiAgentId: ZodNullable<ZodString>;
297
+ visitorId: ZodNullable<ZodString>;
298
+ createdAt: ZodString;
299
+ deletedAt: ZodOptional<ZodNullable<ZodString>>;
300
+ }, $strip>>;
225
301
  lastTimelineItem: ZodNullable<ZodObject<{
226
302
  id: ZodOptional<ZodString>;
227
303
  conversationId: ZodString;
@@ -277,6 +353,13 @@ declare const realtimeSchema: {
277
353
  mediaType: ZodString;
278
354
  fileName: ZodOptional<ZodString>;
279
355
  size: ZodOptional<ZodNumber>;
356
+ }, $strip>, ZodObject<{
357
+ type: ZodLiteral<"metadata">;
358
+ source: ZodEnum<{
359
+ email: "email";
360
+ widget: "widget";
361
+ api: "api";
362
+ }>;
280
363
  }, $strip>]>>;
281
364
  userId: ZodNullable<ZodString>;
282
365
  aiAgentId: ZodNullable<ZodString>;
@@ -1 +1 @@
1
- {"version":3,"file":"realtime-events.d.ts","names":[],"sources":["../../types/src/realtime-events.ts"],"sourcesContent":[],"mappings":";;;;;;;;;;cAiBa;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;KAmED,iBAAA,gBAAiC;KAEjC,+BAA+B,qBAAqB,eACvD,gBAAgB;KAGb,wBAAwB;QAC7B;WACG,qBAAqB;;KAGnB,gBAAA,WACL,oBAAoB,cAAc,KACvC;KAEU,4BAA4B,qBACvC,qBAAqB"}
1
+ {"version":3,"file":"realtime-events.d.ts","names":[],"sources":["../../types/src/realtime-events.ts"],"sourcesContent":[],"mappings":";;;;;;;;;;cAiBa;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;KAmED,iBAAA,gBAAiC;KAEjC,+BAA+B,qBAAqB,eACvD,gBAAgB;KAGb,wBAAwB;QAC7B;WACG,qBAAqB;;KAGnB,gBAAA,WACL,oBAAoB,cAAc,KACvC;KAEU,4BAA4B,qBACvC,qBAAqB"}
package/schemas3.d.ts CHANGED
@@ -72,6 +72,13 @@ declare const conversationSchema: ZodObject<{
72
72
  mediaType: ZodString;
73
73
  fileName: ZodOptional<ZodString>;
74
74
  size: ZodOptional<ZodNumber>;
75
+ }, $strip>, ZodObject<{
76
+ type: ZodLiteral<"metadata">;
77
+ source: ZodEnum<{
78
+ email: "email";
79
+ widget: "widget";
80
+ api: "api";
81
+ }>;
75
82
  }, $strip>]>>;
76
83
  userId: ZodNullable<ZodString>;
77
84
  aiAgentId: ZodNullable<ZodString>;
package/schemas3.d.ts.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"schemas3.d.ts","names":[],"sources":["../../types/src/schemas.ts"],"sourcesContent":[],"mappings":";;;;;;cAkBa,oBAAkB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;IAAA,SAAA,aAAA,YAAA,UAAA,CAAA,CAAA;EAAA,CAAA,QAAA,CAAA,CAAA;AAkB/B,CAAA,QAAY,CAAA;AAEC,KAFD,YAAA,GAAe,MAYzB,CAAA,OAZwC,kBAYxC,CAAA;cAVW,wBAAsB;;;;;;;;;;;KAYvB,gBAAA,GAAmB,cAAe"}
1
+ {"version":3,"file":"schemas3.d.ts","names":[],"sources":["../../types/src/schemas.ts"],"sourcesContent":[],"mappings":";;;;;;cAkBa,oBAAkB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;IAAA,SAAA,aAAA,UAAA,CAAA;IAAA,SAAA,aAAA,UAAA,CAAA;IAkBnB,SAAA,WAAY;IAEX,SAAA,aAUX,YAAA,UAAA,CAAA,CAAA;;;KAZU,YAAA,GAAe,cAAe;cAE7B,wBAAsB;;;;;;;;;;;AAAA,KAYvB,gBAAA,GAAmB,MAZI,CAAA,OAYW,sBAZX,CAAA"}
@@ -1 +1 @@
1
- {"version":3,"file":"bubble.d.ts","names":[],"sources":["../../../src/support/components/bubble.tsx"],"sourcesContent":[],"mappings":";;;KASY,WAAA;;AAAZ,CAAA;AAIa,cAAA,MAAiB,EAAT,KAAA,CAAM,EAAG,CAAA,WAAD,CAAA"}
1
+ {"version":3,"file":"bubble.d.ts","names":[],"sources":["../../../src/support/components/bubble.tsx"],"sourcesContent":[],"mappings":";;;KAgIY,WAAA;;AAAZ,CAAA;AAIa,cAAA,MAAiB,EAAT,KAAA,CAAM,EAAG,CAAA,WAAD,CAAA"}
@@ -5,13 +5,28 @@ import { cn } from "../utils/index.js";
5
5
  import { BouncingDots } from "./typing-indicator.js";
6
6
  import { SupportBubble } from "../../primitives/bubble.js";
7
7
  import icons_default from "./icons.js";
8
+ import { useNewMessageSound } from "../../hooks/use-new-message-sound.js";
9
+ import { useTypingSound } from "../../hooks/use-typing-sound.js";
10
+ import { useEffect, useRef } from "react";
8
11
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
9
12
  import { AnimatePresence, motion } from "motion/react";
10
13
 
11
14
  //#region src/support/components/bubble.tsx
12
- const Bubble = ({ className }) => /* @__PURE__ */ jsx(SupportBubble, {
13
- className: cn("relative flex size-12 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),
14
- children: ({ isOpen, unreadCount, isTyping }) => /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx(AnimatePresence, {
15
+ const BubbleContent = ({ isOpen, unreadCount, isTyping }) => {
16
+ const playNewMessageSound = useNewMessageSound({
17
+ volume: .7,
18
+ playbackRate: 1
19
+ });
20
+ const previousUnreadCountRef = useRef(0);
21
+ useTypingSound(!isOpen && isTyping, {
22
+ volume: 1,
23
+ playbackRate: 1.3
24
+ });
25
+ useEffect(() => {
26
+ if (unreadCount > previousUnreadCountRef.current) playNewMessageSound();
27
+ previousUnreadCountRef.current = unreadCount;
28
+ }, [unreadCount, playNewMessageSound]);
29
+ return /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx(AnimatePresence, {
15
30
  mode: "wait",
16
31
  children: isOpen ? /* @__PURE__ */ jsx(motion.div, {
17
32
  animate: {
@@ -110,7 +125,15 @@ const Bubble = ({ className }) => /* @__PURE__ */ jsx(SupportBubble, {
110
125
  scale: 0,
111
126
  opacity: 0
112
127
  }
113
- })] })
128
+ })] });
129
+ };
130
+ const Bubble = ({ className }) => /* @__PURE__ */ jsx(SupportBubble, {
131
+ className: cn("relative flex size-12 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),
132
+ children: ({ isOpen, unreadCount, isTyping }) => /* @__PURE__ */ jsx(BubbleContent, {
133
+ isOpen,
134
+ isTyping,
135
+ unreadCount
136
+ })
114
137
  });
115
138
 
116
139
  //#endregion
@@ -1 +1 @@
1
- {"version":3,"file":"bubble.js","names":["Bubble: React.FC<BubbleProps>","Primitive.Bubble","Icon"],"sources":["../../../src/support/components/bubble.tsx"],"sourcesContent":["\"use client\";\n\nimport { AnimatePresence, motion } from \"motion/react\";\nimport type React from \"react\";\nimport * as Primitive from \"../../primitives\";\nimport { cn } from \"../utils\";\nimport Icon from \"./icons\";\nimport { BouncingDots } from \"./typing-indicator\";\n\nexport type BubbleProps = {\n\tclassName?: string;\n};\n\nexport const Bubble: React.FC<BubbleProps> = ({ className }) => (\n\t<Primitive.Bubble\n\t\tclassName={cn(\n\t\t\t\"relative flex size-12 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\tclassName\n\t\t)}\n\t>\n\t\t{({ isOpen, unreadCount, isTyping }) => (\n\t\t\t<>\n\t\t\t\t<AnimatePresence mode=\"wait\">\n\t\t\t\t\t{isOpen ? (\n\t\t\t\t\t\t<motion.div\n\t\t\t\t\t\t\tanimate={{\n\t\t\t\t\t\t\t\tscale: 1,\n\t\t\t\t\t\t\t\trotate: 0,\n\t\t\t\t\t\t\t\topacity: 1,\n\t\t\t\t\t\t\t\ttransition: { duration: 0.2, ease: \"easeOut\" },\n\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\tclassName=\"flex items-center justify-center\"\n\t\t\t\t\t\t\texit={{\n\t\t\t\t\t\t\t\tscale: 0.9,\n\t\t\t\t\t\t\t\trotate: -45,\n\t\t\t\t\t\t\t\topacity: 0,\n\t\t\t\t\t\t\t\ttransition: { duration: 0.1, ease: \"easeIn\" },\n\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\tinitial={{ scale: 0.9, rotate: 45, opacity: 0 }}\n\t\t\t\t\t\t\tkey=\"chevron\"\n\t\t\t\t\t\t>\n\t\t\t\t\t\t\t<Icon className=\"h-5 w-5\" name=\"chevron-down\" />\n\t\t\t\t\t\t</motion.div>\n\t\t\t\t\t) : isTyping ? (\n\t\t\t\t\t\t<motion.span\n\t\t\t\t\t\t\tanimate={{\n\t\t\t\t\t\t\t\topacity: 1,\n\t\t\t\t\t\t\t\tscale: 1,\n\t\t\t\t\t\t\t\ttransition: {\n\t\t\t\t\t\t\t\t\tduration: 0.2,\n\t\t\t\t\t\t\t\t\tease: \"easeOut\",\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\tclassName=\"pointer-events-none flex items-center rounded-full text-co-primary\"\n\t\t\t\t\t\t\texit={{\n\t\t\t\t\t\t\t\topacity: 0,\n\t\t\t\t\t\t\t\tscale: 0.9,\n\t\t\t\t\t\t\t\ttransition: {\n\t\t\t\t\t\t\t\t\tduration: 0.1,\n\t\t\t\t\t\t\t\t\tease: \"easeIn\",\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\tinitial={{ opacity: 0, scale: 0.9 }}\n\t\t\t\t\t\t\tkey=\"typing-indicator\"\n\t\t\t\t\t\t>\n\t\t\t\t\t\t\t<BouncingDots className=\"bg-co-primary-foreground\" />\n\t\t\t\t\t\t</motion.span>\n\t\t\t\t\t) : (\n\t\t\t\t\t\t<motion.div\n\t\t\t\t\t\t\tanimate={{\n\t\t\t\t\t\t\t\tscale: 1,\n\t\t\t\t\t\t\t\trotate: 0,\n\t\t\t\t\t\t\t\topacity: 1,\n\t\t\t\t\t\t\t\ttransition: { duration: 0.2, ease: \"easeOut\" },\n\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\tclassName=\"flex items-center justify-center\"\n\t\t\t\t\t\t\texit={{\n\t\t\t\t\t\t\t\tscale: 0.9,\n\t\t\t\t\t\t\t\trotate: 45,\n\t\t\t\t\t\t\t\topacity: 0,\n\t\t\t\t\t\t\t\ttransition: { duration: 0.1, ease: \"easeIn\" },\n\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\tinitial={{ scale: 0.9, rotate: -45, opacity: 0 }}\n\t\t\t\t\t\t\tkey=\"chat\"\n\t\t\t\t\t\t>\n\t\t\t\t\t\t\t<Icon className=\"h-6 w-6\" name=\"chat\" variant=\"filled\" />\n\t\t\t\t\t\t</motion.div>\n\t\t\t\t\t)}\n\t\t\t\t</AnimatePresence>\n\n\t\t\t\t{unreadCount > 0 && (\n\t\t\t\t\t<motion.div\n\t\t\t\t\t\tanimate={{ scale: 1, opacity: 1 }}\n\t\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\t\texit={{ scale: 0, opacity: 0 }}\n\t\t\t\t\t\tinitial={{ scale: 0, opacity: 0 }}\n\t\t\t\t\t/>\n\t\t\t\t)}\n\t\t\t</>\n\t\t)}\n\t</Primitive.Bubble>\n);\n"],"mappings":";;;;;;;;;;;AAaA,MAAaA,UAAiC,EAAE,gBAC/C,oBAACC;CACA,WAAW,GACV,qMACA,UACA;YAEC,EAAE,QAAQ,aAAa,eACxB,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;IAAU,MAAK;KAAiB;KAF5C,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;IAAU,MAAK;IAAO,SAAQ;KAAW;KAFrD,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;EAEc"}
1
+ {"version":3,"file":"bubble.js","names":["BubbleContent: React.FC<BubbleContentProps>","Icon","Bubble: React.FC<BubbleProps>","Primitive.Bubble"],"sources":["../../../src/support/components/bubble.tsx"],"sourcesContent":["\"use client\";\n\nimport { AnimatePresence, motion } from \"motion/react\";\nimport type React from \"react\";\nimport { useEffect, useRef } from \"react\";\nimport { useNewMessageSound } from \"../../hooks/use-new-message-sound\";\nimport { useTypingSound } from \"../../hooks/use-typing-sound\";\nimport * as Primitive from \"../../primitives\";\nimport { cn } from \"../utils\";\nimport Icon from \"./icons\";\nimport { BouncingDots } from \"./typing-indicator\";\n\ntype BubbleContentProps = {\n\tisOpen: boolean;\n\tunreadCount: number;\n\tisTyping: boolean;\n};\n\nconst BubbleContent: React.FC<BubbleContentProps> = ({\n\tisOpen,\n\tunreadCount,\n\tisTyping,\n}) => {\n\t// Customize playback settings here:\n\t// - volume: 0.0 to 1.0+ (default varies by sound)\n\t// - playbackRate: 0.5 to 2.0 (1.0 is normal speed, higher = faster)\n\tconst playNewMessageSound = useNewMessageSound({\n\t\tvolume: 0.7,\n\t\tplaybackRate: 1.0,\n\t});\n\tconst previousUnreadCountRef = useRef(0);\n\n\t// Play typing sound when widget is closed and someone is typing\n\tuseTypingSound(!isOpen && isTyping, {\n\t\tvolume: 1,\n\t\tplaybackRate: 1.3,\n\t});\n\n\t// Play new message sound when unread count increases\n\tuseEffect(() => {\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=\"h-5 w-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=\"h-6 w-6\" 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 BubbleProps = {\n\tclassName?: string;\n};\n\nexport const Bubble: React.FC<BubbleProps> = ({ className }) => (\n\t<Primitive.Bubble\n\t\tclassName={cn(\n\t\t\t\"relative flex size-12 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\tclassName\n\t\t)}\n\t>\n\t\t{({ isOpen, unreadCount, isTyping }) => (\n\t\t\t<BubbleContent\n\t\t\t\tisOpen={isOpen}\n\t\t\t\tisTyping={isTyping}\n\t\t\t\tunreadCount={unreadCount}\n\t\t\t/>\n\t\t)}\n\t</Primitive.Bubble>\n);\n"],"mappings":";;;;;;;;;;;;;;AAkBA,MAAMA,iBAA+C,EACpD,QACA,aACA,eACK;CAIL,MAAM,sBAAsB,mBAAmB;EAC9C,QAAQ;EACR,cAAc;EACd,CAAC;CACF,MAAM,yBAAyB,OAAO,EAAE;AAGxC,gBAAe,CAAC,UAAU,UAAU;EACnC,QAAQ;EACR,cAAc;EACd,CAAC;AAGF,iBAAgB;AACf,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;IAAU,MAAK;KAAiB;KAF5C,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;IAAU,MAAK;IAAO,SAAQ;KAAW;KAFrD,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;;AAQL,MAAaC,UAAiC,EAAE,gBAC/C,oBAACC;CACA,WAAW,GACV,qMACA,UACA;YAEC,EAAE,QAAQ,aAAa,eACxB,oBAAC;EACQ;EACE;EACG;GACZ;EAEe"}
@@ -4,7 +4,7 @@ import * as class_variance_authority_dist_types0 from "class-variance-authority/
4
4
 
5
5
  //#region src/support/components/button.d.ts
6
6
  declare const coButtonVariants: (props?: ({
7
- variant?: "tab" | "default" | "secondary" | "ghost" | "outline" | "tab-selected" | null | undefined;
7
+ variant?: "default" | "tab" | "secondary" | "ghost" | "outline" | "tab-selected" | null | undefined;
8
8
  size?: "default" | "large" | "icon" | null | undefined;
9
9
  } & class_variance_authority_dist_types0.ClassProp) | undefined) => string;
10
10
  type CossistantButtonProps = React$1.ComponentProps<"button"> & VariantProps<typeof coButtonVariants>;
@@ -35,7 +35,7 @@ const ConversationEvent = ({ event, availableAIAgents, createdAt, availableHuman
35
35
  opacity: 1,
36
36
  scale: 1
37
37
  },
38
- className: "flex items-center justify-center py-4",
38
+ className: "flex items-center justify-center pt-4 pb-8",
39
39
  initial: {
40
40
  opacity: 0,
41
41
  scale: .95
@@ -1 +1 @@
1
- {"version":3,"file":"conversation-event.js","names":["ConversationEvent: React.FC<ConversationEventProps>"],"sources":["../../../src/support/components/conversation-event.tsx"],"sourcesContent":["import type {\n\tAvailableAIAgent,\n\tAvailableHumanAgent,\n\tTimelinePartEvent,\n} from \"@cossistant/types\";\nimport { motion } from \"motion/react\";\nimport type React from \"react\";\nimport { useSupportText } from \"../text\";\nimport { Avatar } from \"./avatar\";\nimport { CossistantLogo } from \"./cossistant-branding\";\n\nexport type ConversationEventProps = {\n\tevent: TimelinePartEvent;\n\tavailableAIAgents: AvailableAIAgent[];\n\tavailableHumanAgents: AvailableHumanAgent[];\n\tcreatedAt?: string;\n};\n\nexport const ConversationEvent: React.FC<ConversationEventProps> = ({\n\tevent,\n\tavailableAIAgents,\n\tcreatedAt,\n\tavailableHumanAgents,\n}) => {\n\tconst text = useSupportText();\n\tconst isAI = event.actorAiAgentId !== null;\n\tconst humanAgent = availableHumanAgents.find(\n\t\t(agent) => agent.id === event.actorUserId\n\t);\n\tconst aiAgent = availableAIAgents.find(\n\t\t(agent) => agent.id === event.actorAiAgentId\n\t);\n\n\t// Get the actor name\n\tconst actorName = isAI\n\t\t? aiAgent?.name || text(\"common.fallbacks.cossistant\")\n\t\t: humanAgent?.name || text(\"common.fallbacks.someone\");\n\n\t// Convert event type to plain English\n\tconst getEventText = () => {\n\t\tswitch (event.eventType) {\n\t\t\tcase \"assigned\":\n\t\t\t\treturn text(\"component.conversationEvent.assigned\", {\n\t\t\t\t\tactorName,\n\t\t\t\t});\n\t\t\tcase \"unassigned\":\n\t\t\t\treturn text(\"component.conversationEvent.unassigned\", {\n\t\t\t\t\tactorName,\n\t\t\t\t});\n\t\t\tcase \"participant_requested\":\n\t\t\t\treturn text(\"component.conversationEvent.participantRequested\", {\n\t\t\t\t\tactorName,\n\t\t\t\t});\n\t\t\tcase \"participant_joined\":\n\t\t\t\treturn text(\"component.conversationEvent.participantJoined\", {\n\t\t\t\t\tactorName,\n\t\t\t\t});\n\t\t\tcase \"participant_left\":\n\t\t\t\treturn text(\"component.conversationEvent.participantLeft\", {\n\t\t\t\t\tactorName,\n\t\t\t\t});\n\t\t\tcase \"status_changed\":\n\t\t\t\treturn text(\"component.conversationEvent.statusChanged\", {\n\t\t\t\t\tactorName,\n\t\t\t\t});\n\t\t\tcase \"priority_changed\":\n\t\t\t\treturn text(\"component.conversationEvent.priorityChanged\", {\n\t\t\t\t\tactorName,\n\t\t\t\t});\n\t\t\tcase \"tag_added\":\n\t\t\t\treturn text(\"component.conversationEvent.tagAdded\", {\n\t\t\t\t\tactorName,\n\t\t\t\t});\n\t\t\tcase \"tag_removed\":\n\t\t\t\treturn text(\"component.conversationEvent.tagRemoved\", {\n\t\t\t\t\tactorName,\n\t\t\t\t});\n\t\t\tcase \"resolved\":\n\t\t\t\treturn text(\"component.conversationEvent.resolved\", {\n\t\t\t\t\tactorName,\n\t\t\t\t});\n\t\t\tcase \"reopened\":\n\t\t\t\treturn text(\"component.conversationEvent.reopened\", {\n\t\t\t\t\tactorName,\n\t\t\t\t});\n\t\t\tcase \"visitor_blocked\":\n\t\t\t\treturn text(\"component.conversationEvent.visitorBlocked\", {\n\t\t\t\t\tactorName,\n\t\t\t\t});\n\t\t\tcase \"visitor_unblocked\":\n\t\t\t\treturn text(\"component.conversationEvent.visitorUnblocked\", {\n\t\t\t\t\tactorName,\n\t\t\t\t});\n\t\t\tcase \"visitor_identified\":\n\t\t\t\treturn text(\"component.conversationEvent.visitorIdentified\", {\n\t\t\t\t\tactorName,\n\t\t\t\t});\n\t\t\tdefault:\n\t\t\t\treturn text(\"component.conversationEvent.default\", {\n\t\t\t\t\tactorName,\n\t\t\t\t});\n\t\t}\n\t};\n\n\treturn (\n\t\t<motion.div\n\t\t\tanimate={{ opacity: 1, scale: 1 }}\n\t\t\tclassName=\"flex items-center justify-center py-4\"\n\t\t\tinitial={{ opacity: 0, scale: 0.95 }}\n\t\t\ttransition={{ duration: 0.3, ease: \"easeOut\" }}\n\t\t>\n\t\t\t<div className=\"flex items-center gap-2 text-co-muted-foreground text-xs\">\n\t\t\t\t<div className=\"flex flex-col justify-end\">\n\t\t\t\t\t{isAI ? (\n\t\t\t\t\t\t<div className=\"flex size-5 items-center justify-center rounded-full bg-co-primary/10\">\n\t\t\t\t\t\t\t<CossistantLogo className=\"h-3 w-3 text-co-primary\" />\n\t\t\t\t\t\t</div>\n\t\t\t\t\t) : (\n\t\t\t\t\t\t<Avatar\n\t\t\t\t\t\t\tclassName=\"size-5 flex-shrink-0 overflow-clip rounded-full\"\n\t\t\t\t\t\t\timage={humanAgent?.image}\n\t\t\t\t\t\t\tname={humanAgent?.name || text(\"common.fallbacks.someone\")}\n\t\t\t\t\t\t/>\n\t\t\t\t\t)}\n\t\t\t\t</div>\n\t\t\t\t<span className=\"px-2\">{getEventText()}</span>\n\t\t\t\t{createdAt && (\n\t\t\t\t\t<time className=\"text-[10px]\">\n\t\t\t\t\t\t{new Date(createdAt).toLocaleTimeString([], {\n\t\t\t\t\t\t\thour: \"2-digit\",\n\t\t\t\t\t\t\tminute: \"2-digit\",\n\t\t\t\t\t\t})}\n\t\t\t\t\t</time>\n\t\t\t\t)}\n\t\t\t</div>\n\t\t</motion.div>\n\t);\n};\n\nConversationEvent.displayName = \"ConversationEvent\";\n"],"mappings":";;;;;;;AAkBA,MAAaA,qBAAuD,EACnE,OACA,mBACA,WACA,2BACK;CACL,MAAM,OAAO,gBAAgB;CAC7B,MAAM,OAAO,MAAM,mBAAmB;CACtC,MAAM,aAAa,qBAAqB,MACtC,UAAU,MAAM,OAAO,MAAM,YAC9B;CACD,MAAM,UAAU,kBAAkB,MAChC,UAAU,MAAM,OAAO,MAAM,eAC9B;CAGD,MAAM,YAAY,OACf,SAAS,QAAQ,KAAK,8BAA8B,GACpD,YAAY,QAAQ,KAAK,2BAA2B;CAGvD,MAAM,qBAAqB;AAC1B,UAAQ,MAAM,WAAd;GACC,KAAK,WACJ,QAAO,KAAK,wCAAwC,EACnD,WACA,CAAC;GACH,KAAK,aACJ,QAAO,KAAK,0CAA0C,EACrD,WACA,CAAC;GACH,KAAK,wBACJ,QAAO,KAAK,oDAAoD,EAC/D,WACA,CAAC;GACH,KAAK,qBACJ,QAAO,KAAK,iDAAiD,EAC5D,WACA,CAAC;GACH,KAAK,mBACJ,QAAO,KAAK,+CAA+C,EAC1D,WACA,CAAC;GACH,KAAK,iBACJ,QAAO,KAAK,6CAA6C,EACxD,WACA,CAAC;GACH,KAAK,mBACJ,QAAO,KAAK,+CAA+C,EAC1D,WACA,CAAC;GACH,KAAK,YACJ,QAAO,KAAK,wCAAwC,EACnD,WACA,CAAC;GACH,KAAK,cACJ,QAAO,KAAK,0CAA0C,EACrD,WACA,CAAC;GACH,KAAK,WACJ,QAAO,KAAK,wCAAwC,EACnD,WACA,CAAC;GACH,KAAK,WACJ,QAAO,KAAK,wCAAwC,EACnD,WACA,CAAC;GACH,KAAK,kBACJ,QAAO,KAAK,8CAA8C,EACzD,WACA,CAAC;GACH,KAAK,oBACJ,QAAO,KAAK,gDAAgD,EAC3D,WACA,CAAC;GACH,KAAK,qBACJ,QAAO,KAAK,iDAAiD,EAC5D,WACA,CAAC;GACH,QACC,QAAO,KAAK,uCAAuC,EAClD,WACA,CAAC;;;AAIL,QACC,oBAAC,OAAO;EACP,SAAS;GAAE,SAAS;GAAG,OAAO;GAAG;EACjC,WAAU;EACV,SAAS;GAAE,SAAS;GAAG,OAAO;GAAM;EACpC,YAAY;GAAE,UAAU;GAAK,MAAM;GAAW;YAE9C,qBAAC;GAAI,WAAU;;IACd,oBAAC;KAAI,WAAU;eACb,OACA,oBAAC;MAAI,WAAU;gBACd,oBAAC,kBAAe,WAAU,4BAA4B;OACjD,GAEN,oBAAC;MACA,WAAU;MACV,OAAO,YAAY;MACnB,MAAM,YAAY,QAAQ,KAAK,2BAA2B;OACzD;MAEE;IACN,oBAAC;KAAK,WAAU;eAAQ,cAAc;MAAQ;IAC7C,aACA,oBAAC;KAAK,WAAU;eACd,IAAI,KAAK,UAAU,CAAC,mBAAmB,EAAE,EAAE;MAC3C,MAAM;MACN,QAAQ;MACR,CAAC;MACI;;IAEH;GACM;;AAIf,kBAAkB,cAAc"}
1
+ {"version":3,"file":"conversation-event.js","names":["ConversationEvent: React.FC<ConversationEventProps>"],"sources":["../../../src/support/components/conversation-event.tsx"],"sourcesContent":["import type {\n\tAvailableAIAgent,\n\tAvailableHumanAgent,\n\tTimelinePartEvent,\n} from \"@cossistant/types\";\nimport { motion } from \"motion/react\";\nimport type React from \"react\";\nimport { useSupportText } from \"../text\";\nimport { Avatar } from \"./avatar\";\nimport { CossistantLogo } from \"./cossistant-branding\";\n\nexport type ConversationEventProps = {\n\tevent: TimelinePartEvent;\n\tavailableAIAgents: AvailableAIAgent[];\n\tavailableHumanAgents: AvailableHumanAgent[];\n\tcreatedAt?: string;\n};\n\nexport const ConversationEvent: React.FC<ConversationEventProps> = ({\n\tevent,\n\tavailableAIAgents,\n\tcreatedAt,\n\tavailableHumanAgents,\n}) => {\n\tconst text = useSupportText();\n\tconst isAI = event.actorAiAgentId !== null;\n\tconst humanAgent = availableHumanAgents.find(\n\t\t(agent) => agent.id === event.actorUserId\n\t);\n\tconst aiAgent = availableAIAgents.find(\n\t\t(agent) => agent.id === event.actorAiAgentId\n\t);\n\n\t// Get the actor name\n\tconst actorName = isAI\n\t\t? aiAgent?.name || text(\"common.fallbacks.cossistant\")\n\t\t: humanAgent?.name || text(\"common.fallbacks.someone\");\n\n\t// Convert event type to plain English\n\tconst getEventText = () => {\n\t\tswitch (event.eventType) {\n\t\t\tcase \"assigned\":\n\t\t\t\treturn text(\"component.conversationEvent.assigned\", {\n\t\t\t\t\tactorName,\n\t\t\t\t});\n\t\t\tcase \"unassigned\":\n\t\t\t\treturn text(\"component.conversationEvent.unassigned\", {\n\t\t\t\t\tactorName,\n\t\t\t\t});\n\t\t\tcase \"participant_requested\":\n\t\t\t\treturn text(\"component.conversationEvent.participantRequested\", {\n\t\t\t\t\tactorName,\n\t\t\t\t});\n\t\t\tcase \"participant_joined\":\n\t\t\t\treturn text(\"component.conversationEvent.participantJoined\", {\n\t\t\t\t\tactorName,\n\t\t\t\t});\n\t\t\tcase \"participant_left\":\n\t\t\t\treturn text(\"component.conversationEvent.participantLeft\", {\n\t\t\t\t\tactorName,\n\t\t\t\t});\n\t\t\tcase \"status_changed\":\n\t\t\t\treturn text(\"component.conversationEvent.statusChanged\", {\n\t\t\t\t\tactorName,\n\t\t\t\t});\n\t\t\tcase \"priority_changed\":\n\t\t\t\treturn text(\"component.conversationEvent.priorityChanged\", {\n\t\t\t\t\tactorName,\n\t\t\t\t});\n\t\t\tcase \"tag_added\":\n\t\t\t\treturn text(\"component.conversationEvent.tagAdded\", {\n\t\t\t\t\tactorName,\n\t\t\t\t});\n\t\t\tcase \"tag_removed\":\n\t\t\t\treturn text(\"component.conversationEvent.tagRemoved\", {\n\t\t\t\t\tactorName,\n\t\t\t\t});\n\t\t\tcase \"resolved\":\n\t\t\t\treturn text(\"component.conversationEvent.resolved\", {\n\t\t\t\t\tactorName,\n\t\t\t\t});\n\t\t\tcase \"reopened\":\n\t\t\t\treturn text(\"component.conversationEvent.reopened\", {\n\t\t\t\t\tactorName,\n\t\t\t\t});\n\t\t\tcase \"visitor_blocked\":\n\t\t\t\treturn text(\"component.conversationEvent.visitorBlocked\", {\n\t\t\t\t\tactorName,\n\t\t\t\t});\n\t\t\tcase \"visitor_unblocked\":\n\t\t\t\treturn text(\"component.conversationEvent.visitorUnblocked\", {\n\t\t\t\t\tactorName,\n\t\t\t\t});\n\t\t\tcase \"visitor_identified\":\n\t\t\t\treturn text(\"component.conversationEvent.visitorIdentified\", {\n\t\t\t\t\tactorName,\n\t\t\t\t});\n\t\t\tdefault:\n\t\t\t\treturn text(\"component.conversationEvent.default\", {\n\t\t\t\t\tactorName,\n\t\t\t\t});\n\t\t}\n\t};\n\n\treturn (\n\t\t<motion.div\n\t\t\tanimate={{ opacity: 1, scale: 1 }}\n\t\t\tclassName=\"flex items-center justify-center pt-4 pb-8\"\n\t\t\tinitial={{ opacity: 0, scale: 0.95 }}\n\t\t\ttransition={{ duration: 0.3, ease: \"easeOut\" }}\n\t\t>\n\t\t\t<div className=\"flex items-center gap-2 text-co-muted-foreground text-xs\">\n\t\t\t\t<div className=\"flex flex-col justify-end\">\n\t\t\t\t\t{isAI ? (\n\t\t\t\t\t\t<div className=\"flex size-5 items-center justify-center rounded-full bg-co-primary/10\">\n\t\t\t\t\t\t\t<CossistantLogo className=\"h-3 w-3 text-co-primary\" />\n\t\t\t\t\t\t</div>\n\t\t\t\t\t) : (\n\t\t\t\t\t\t<Avatar\n\t\t\t\t\t\t\tclassName=\"size-5 flex-shrink-0 overflow-clip rounded-full\"\n\t\t\t\t\t\t\timage={humanAgent?.image}\n\t\t\t\t\t\t\tname={humanAgent?.name || text(\"common.fallbacks.someone\")}\n\t\t\t\t\t\t/>\n\t\t\t\t\t)}\n\t\t\t\t</div>\n\t\t\t\t<span className=\"px-2\">{getEventText()}</span>\n\t\t\t\t{createdAt && (\n\t\t\t\t\t<time className=\"text-[10px]\">\n\t\t\t\t\t\t{new Date(createdAt).toLocaleTimeString([], {\n\t\t\t\t\t\t\thour: \"2-digit\",\n\t\t\t\t\t\t\tminute: \"2-digit\",\n\t\t\t\t\t\t})}\n\t\t\t\t\t</time>\n\t\t\t\t)}\n\t\t\t</div>\n\t\t</motion.div>\n\t);\n};\n\nConversationEvent.displayName = \"ConversationEvent\";\n"],"mappings":";;;;;;;AAkBA,MAAaA,qBAAuD,EACnE,OACA,mBACA,WACA,2BACK;CACL,MAAM,OAAO,gBAAgB;CAC7B,MAAM,OAAO,MAAM,mBAAmB;CACtC,MAAM,aAAa,qBAAqB,MACtC,UAAU,MAAM,OAAO,MAAM,YAC9B;CACD,MAAM,UAAU,kBAAkB,MAChC,UAAU,MAAM,OAAO,MAAM,eAC9B;CAGD,MAAM,YAAY,OACf,SAAS,QAAQ,KAAK,8BAA8B,GACpD,YAAY,QAAQ,KAAK,2BAA2B;CAGvD,MAAM,qBAAqB;AAC1B,UAAQ,MAAM,WAAd;GACC,KAAK,WACJ,QAAO,KAAK,wCAAwC,EACnD,WACA,CAAC;GACH,KAAK,aACJ,QAAO,KAAK,0CAA0C,EACrD,WACA,CAAC;GACH,KAAK,wBACJ,QAAO,KAAK,oDAAoD,EAC/D,WACA,CAAC;GACH,KAAK,qBACJ,QAAO,KAAK,iDAAiD,EAC5D,WACA,CAAC;GACH,KAAK,mBACJ,QAAO,KAAK,+CAA+C,EAC1D,WACA,CAAC;GACH,KAAK,iBACJ,QAAO,KAAK,6CAA6C,EACxD,WACA,CAAC;GACH,KAAK,mBACJ,QAAO,KAAK,+CAA+C,EAC1D,WACA,CAAC;GACH,KAAK,YACJ,QAAO,KAAK,wCAAwC,EACnD,WACA,CAAC;GACH,KAAK,cACJ,QAAO,KAAK,0CAA0C,EACrD,WACA,CAAC;GACH,KAAK,WACJ,QAAO,KAAK,wCAAwC,EACnD,WACA,CAAC;GACH,KAAK,WACJ,QAAO,KAAK,wCAAwC,EACnD,WACA,CAAC;GACH,KAAK,kBACJ,QAAO,KAAK,8CAA8C,EACzD,WACA,CAAC;GACH,KAAK,oBACJ,QAAO,KAAK,gDAAgD,EAC3D,WACA,CAAC;GACH,KAAK,qBACJ,QAAO,KAAK,iDAAiD,EAC5D,WACA,CAAC;GACH,QACC,QAAO,KAAK,uCAAuC,EAClD,WACA,CAAC;;;AAIL,QACC,oBAAC,OAAO;EACP,SAAS;GAAE,SAAS;GAAG,OAAO;GAAG;EACjC,WAAU;EACV,SAAS;GAAE,SAAS;GAAG,OAAO;GAAM;EACpC,YAAY;GAAE,UAAU;GAAK,MAAM;GAAW;YAE9C,qBAAC;GAAI,WAAU;;IACd,oBAAC;KAAI,WAAU;eACb,OACA,oBAAC;MAAI,WAAU;gBACd,oBAAC,kBAAe,WAAU,4BAA4B;OACjD,GAEN,oBAAC;MACA,WAAU;MACV,OAAO,YAAY;MACnB,MAAM,YAAY,QAAQ,KAAK,2BAA2B;OACzD;MAEE;IACN,oBAAC;KAAK,WAAU;eAAQ,cAAc;MAAQ;IAC7C,aACA,oBAAC;KAAK,WAAU;eACd,IAAI,KAAK,UAAU,CAAC,mBAAmB,EAAE,EAAE;MAC3C,MAAM;MACN,QAAQ;MACR,CAAC;MACI;;IAEH;GACM;;AAIf,kBAAkB,cAAc"}
@@ -1 +1 @@
1
- {"version":3,"file":"conversation-timeline.d.ts","names":[],"sources":["../../../src/support/components/conversation-timeline.tsx"],"sourcesContent":[],"mappings":";;;;;KAiCY,6BAAA;QACL;EADK,cAAA,EAAA,MAAA;AAKZ,CAAA;AAIY,KAJA,kCAAA,GAMX;EAGW,SAAA,EARA,KAAA,CAAM,aAQmB,CARL,6BAQK,CAAA;CAE7B;AAEY,KATR,yBAAA,GAA4B,MASpB,CAAA,MAAA,EAPnB,kCAOmB,CAAA;AACG,KALX,yBAAA,GAKW;EAEd,cAAA,EAAA,MAAA;EAAyB,KAAA,EAL1B,YAK0B,EAAA;EAGrB,SAAA,CAAA,EAAA,MAAA;qBANO;wBACG;;UAEd;;cAGI,0BAA0B,KAAA,CAAM,GAAG"}
1
+ {"version":3,"file":"conversation-timeline.d.ts","names":[],"sources":["../../../src/support/components/conversation-timeline.tsx"],"sourcesContent":[],"mappings":";;;;;KAkCY,6BAAA;QACL;EADK,cAAA,EAAA,MAAA;AAKZ,CAAA;AAIY,KAJA,kCAAA,GAMX;EAGW,SAAA,EARA,KAAA,CAAM,aAQmB,CARL,6BAQK,CAAA;CAE7B;AAEY,KATR,yBAAA,GAA4B,MASpB,CAAA,MAAA,EAPnB,kCAOmB,CAAA;AACG,KALX,yBAAA,GAKW;EAEd,cAAA,EAAA,MAAA;EAAyB,KAAA,EAL1B,YAK0B,EAAA;EAGrB,SAAA,CAAA,EAAA,MAAA;qBANO;wBACG;;UAEd;;cAGI,0BAA0B,KAAA,CAAM,GAAG"}
@@ -2,6 +2,7 @@ import { cn } from "../utils/index.js";
2
2
  import { TypingIndicator } from "./typing-indicator.js";
3
3
  import { ConversationTimeline, ConversationTimelineContainer } from "../../primitives/conversation-timeline.js";
4
4
  import { useConversationTimeline } from "../../hooks/use-conversation-timeline.js";
5
+ import { useTypingSound } from "../../hooks/use-typing-sound.js";
5
6
  import { ConversationEvent } from "./conversation-event.js";
6
7
  import { TimelineMessageGroup } from "./timeline-message-group.js";
7
8
  import { useCallback, useMemo } from "react";
@@ -24,6 +25,10 @@ const ConversationTimelineList = ({ conversationId, items: timelineItems, classN
24
25
  id: participant.id,
25
26
  type: participant.type
26
27
  })), [timeline.typingParticipants]);
28
+ useTypingSound(typingIndicatorParticipants.length > 0, {
29
+ volume: 1,
30
+ playbackRate: 1.3
31
+ });
27
32
  const seenNameLookup = useMemo(() => {
28
33
  const map = /* @__PURE__ */ new Map();
29
34
  for (const agent of availableHumanAgents) if (agent.name) map.set(agent.id, agent.name);
@@ -1 +1 @@
1
- {"version":3,"file":"conversation-timeline.js","names":["EMPTY_SEEN_BY_IDS: readonly string[]","EMPTY_SEEN_BY_NAMES: readonly string[]","ConversationTimelineList: React.FC<ConversationTimelineProps>","names: string[]","PrimitiveConversationTimeline"],"sources":["../../../src/support/components/conversation-timeline.tsx"],"sourcesContent":["import type { AvailableAIAgent, AvailableHumanAgent } from \"@cossistant/types\";\nimport type {\n\tTimelineItem,\n\tTimelinePartEvent,\n} from \"@cossistant/types/api/timeline-item\";\nimport type React from \"react\";\nimport { useCallback, useMemo } from \"react\";\nimport { useConversationTimeline } from \"../../hooks/use-conversation-timeline\";\nimport {\n\tConversationTimelineContainer,\n\tConversationTimeline as PrimitiveConversationTimeline,\n} from \"../../primitives/conversation-timeline\";\nimport { cn } from \"../utils\";\nimport { ConversationEvent } from \"./conversation-event\";\nimport { TimelineMessageGroup } from \"./timeline-message-group\";\nimport { TypingIndicator, type TypingParticipant } from \"./typing-indicator\";\n\n// Helper to extract event part from timeline item\nfunction extractEventPart(item: TimelineItem): TimelinePartEvent | null {\n\tif (item.type !== \"event\") {\n\t\treturn null;\n\t}\n\n\tconst eventPart = item.parts.find(\n\t\t(part): part is TimelinePartEvent => part.type === \"event\"\n\t);\n\n\treturn eventPart || null;\n}\n\nconst EMPTY_SEEN_BY_IDS: readonly string[] = Object.freeze([]);\nconst EMPTY_SEEN_BY_NAMES: readonly string[] = Object.freeze([]);\n\nexport type ConversationTimelineToolProps = {\n\titem: TimelineItem;\n\tconversationId: string;\n};\n\nexport type ConversationTimelineToolDefinition = {\n\tcomponent: React.ComponentType<ConversationTimelineToolProps>;\n};\n\nexport type ConversationTimelineTools = Record<\n\tstring,\n\tConversationTimelineToolDefinition\n>;\n\nexport type ConversationTimelineProps = {\n\tconversationId: string;\n\titems: TimelineItem[];\n\tclassName?: string;\n\tavailableAIAgents: AvailableAIAgent[];\n\tavailableHumanAgents: AvailableHumanAgent[];\n\tcurrentVisitorId?: string;\n\ttools?: ConversationTimelineTools;\n};\n\nexport const ConversationTimelineList: React.FC<ConversationTimelineProps> = ({\n\tconversationId,\n\titems: timelineItems,\n\tclassName,\n\tavailableAIAgents = [],\n\tavailableHumanAgents = [],\n\tcurrentVisitorId,\n\ttools,\n}) => {\n\tconst timeline = useConversationTimeline({\n\t\tconversationId,\n\t\titems: timelineItems,\n\t\tcurrentVisitorId,\n\t});\n\n\tconst typingIndicatorParticipants = useMemo(\n\t\t() =>\n\t\t\ttimeline.typingParticipants.map<TypingParticipant>((participant) => ({\n\t\t\t\tid: participant.id,\n\t\t\t\ttype: participant.type,\n\t\t\t})),\n\t\t[timeline.typingParticipants]\n\t);\n\n\tconst seenNameLookup = useMemo(() => {\n\t\tconst map = new Map<string, string>();\n\n\t\tfor (const agent of availableHumanAgents) {\n\t\t\tif (agent.name) {\n\t\t\t\tmap.set(agent.id, agent.name);\n\t\t\t}\n\t\t}\n\n\t\tfor (const agent of availableAIAgents) {\n\t\t\tif (agent.name) {\n\t\t\t\tmap.set(agent.id, agent.name);\n\t\t\t}\n\t\t}\n\n\t\treturn map;\n\t}, [availableHumanAgents, availableAIAgents]);\n\n\tconst getSeenByNames = useCallback(\n\t\t(ids: readonly string[] = EMPTY_SEEN_BY_IDS): readonly string[] => {\n\t\t\tif (ids.length === 0 || seenNameLookup.size === 0) {\n\t\t\t\treturn EMPTY_SEEN_BY_NAMES;\n\t\t\t}\n\n\t\t\tconst uniqueNames = new Set<string>();\n\t\t\tconst names: string[] = [];\n\n\t\t\tfor (const id of ids) {\n\t\t\t\tconst name = seenNameLookup.get(id);\n\t\t\t\tif (!name || uniqueNames.has(name)) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\tuniqueNames.add(name);\n\t\t\t\tnames.push(name);\n\t\t\t}\n\n\t\t\tif (names.length === 0) {\n\t\t\t\treturn EMPTY_SEEN_BY_NAMES;\n\t\t\t}\n\n\t\t\treturn Object.freeze(names);\n\t\t},\n\t\t[seenNameLookup]\n\t);\n\n\treturn (\n\t\t<PrimitiveConversationTimeline\n\t\t\tautoScroll={true}\n\t\t\tclassName={cn(\n\t\t\t\t\"overflow-y-scroll scroll-smooth px-3 py-6\",\n\t\t\t\t\"co-scrollbar-thin\",\n\t\t\t\t\"h-full w-full\",\n\t\t\t\tclassName\n\t\t\t)}\n\t\t\tid=\"conversation-timeline\"\n\t\t\titems={timelineItems}\n\t\t>\n\t\t\t<ConversationTimelineContainer className=\"flex min-h-full w-full flex-col gap-3\">\n\t\t\t\t{timeline.groupedMessages.items.map((item, index) => {\n\t\t\t\t\tif (item.type === \"timeline_event\") {\n\t\t\t\t\t\t// Extract event data from parts\n\t\t\t\t\t\tconst eventPart = extractEventPart(item.item);\n\n\t\t\t\t\t\t// Only render if we have valid event data\n\t\t\t\t\t\tif (!eventPart) {\n\t\t\t\t\t\t\treturn null;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\treturn (\n\t\t\t\t\t\t\t<ConversationEvent\n\t\t\t\t\t\t\t\tavailableAIAgents={availableAIAgents}\n\t\t\t\t\t\t\t\tavailableHumanAgents={availableHumanAgents}\n\t\t\t\t\t\t\t\tcreatedAt={item.item.createdAt}\n\t\t\t\t\t\t\t\tevent={eventPart}\n\t\t\t\t\t\t\t\tkey={item.item.id ?? `timeline-event-${item.item.createdAt}`}\n\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\n\t\t\t\t\tif (item.type === \"timeline_tool\") {\n\t\t\t\t\t\tconst toolName = item.tool ?? item.item.tool ?? item.item.type;\n\t\t\t\t\t\tconst toolDefinition = toolName ? tools?.[toolName] : undefined;\n\n\t\t\t\t\t\tif (!toolDefinition) {\n\t\t\t\t\t\t\treturn null;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tconst ToolComponent = toolDefinition.component;\n\n\t\t\t\t\t\tconst toolKey =\n\t\t\t\t\t\t\titem.item.id ?? `${toolName}-${item.item.createdAt}-${index}`;\n\n\t\t\t\t\t\treturn (\n\t\t\t\t\t\t\t<ToolComponent\n\t\t\t\t\t\t\t\tconversationId={conversationId}\n\t\t\t\t\t\t\t\titem={item.item}\n\t\t\t\t\t\t\t\tkey={toolKey}\n\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\n\t\t\t\t\t// Only show seen indicator on the LAST message group sent by the visitor\n\t\t\t\t\tconst isLastVisitorGroup =\n\t\t\t\t\t\tindex === timeline.lastVisitorMessageGroupIndex;\n\t\t\t\t\tconst seenByIds =\n\t\t\t\t\t\tisLastVisitorGroup && item.lastMessageId\n\t\t\t\t\t\t\t? timeline.groupedMessages.getMessageSeenBy(item.lastMessageId)\n\t\t\t\t\t\t\t: EMPTY_SEEN_BY_IDS;\n\t\t\t\t\tconst seenByNames =\n\t\t\t\t\t\tseenByIds.length > 0\n\t\t\t\t\t\t\t? getSeenByNames(seenByIds)\n\t\t\t\t\t\t\t: EMPTY_SEEN_BY_NAMES;\n\n\t\t\t\t\t// Use first timeline item ID as stable key\n\t\t\t\t\tconst groupKey =\n\t\t\t\t\t\titem.lastMessageId ??\n\t\t\t\t\t\titem.items?.[0]?.id ??\n\t\t\t\t\t\t`group-${item.items?.[0]?.createdAt ?? index}`;\n\n\t\t\t\t\treturn (\n\t\t\t\t\t\t<TimelineMessageGroup\n\t\t\t\t\t\t\tavailableAIAgents={availableAIAgents}\n\t\t\t\t\t\t\tavailableHumanAgents={availableHumanAgents}\n\t\t\t\t\t\t\tcurrentVisitorId={currentVisitorId}\n\t\t\t\t\t\t\titems={item.items || []}\n\t\t\t\t\t\t\tkey={groupKey}\n\t\t\t\t\t\t\tseenByIds={seenByIds}\n\t\t\t\t\t\t\tseenByNames={seenByNames}\n\t\t\t\t\t\t/>\n\t\t\t\t\t);\n\t\t\t\t})}\n\t\t\t\t<div className=\"h-6 w-full\">\n\t\t\t\t\t{typingIndicatorParticipants.length > 0 ? (\n\t\t\t\t\t\t<TypingIndicator\n\t\t\t\t\t\t\tavailableAIAgents={availableAIAgents}\n\t\t\t\t\t\t\tavailableHumanAgents={availableHumanAgents}\n\t\t\t\t\t\t\tclassName=\"mt-2\"\n\t\t\t\t\t\t\tparticipants={typingIndicatorParticipants}\n\t\t\t\t\t\t/>\n\t\t\t\t\t) : null}\n\t\t\t\t</div>\n\t\t\t</ConversationTimelineContainer>\n\t\t</PrimitiveConversationTimeline>\n\t);\n};\n"],"mappings":";;;;;;;;;;AAkBA,SAAS,iBAAiB,MAA8C;AACvE,KAAI,KAAK,SAAS,QACjB,QAAO;AAOR,QAJkB,KAAK,MAAM,MAC3B,SAAoC,KAAK,SAAS,QACnD,IAEmB;;AAGrB,MAAMA,oBAAuC,OAAO,OAAO,EAAE,CAAC;AAC9D,MAAMC,sBAAyC,OAAO,OAAO,EAAE,CAAC;AA0BhE,MAAaC,4BAAiE,EAC7E,gBACA,OAAO,eACP,WACA,oBAAoB,EAAE,EACtB,uBAAuB,EAAE,EACzB,kBACA,YACK;CACL,MAAM,WAAW,wBAAwB;EACxC;EACA,OAAO;EACP;EACA,CAAC;CAEF,MAAM,8BAA8B,cAElC,SAAS,mBAAmB,KAAwB,iBAAiB;EACpE,IAAI,YAAY;EAChB,MAAM,YAAY;EAClB,EAAE,EACJ,CAAC,SAAS,mBAAmB,CAC7B;CAED,MAAM,iBAAiB,cAAc;EACpC,MAAM,sBAAM,IAAI,KAAqB;AAErC,OAAK,MAAM,SAAS,qBACnB,KAAI,MAAM,KACT,KAAI,IAAI,MAAM,IAAI,MAAM,KAAK;AAI/B,OAAK,MAAM,SAAS,kBACnB,KAAI,MAAM,KACT,KAAI,IAAI,MAAM,IAAI,MAAM,KAAK;AAI/B,SAAO;IACL,CAAC,sBAAsB,kBAAkB,CAAC;CAE7C,MAAM,iBAAiB,aACrB,MAAyB,sBAAyC;AAClE,MAAI,IAAI,WAAW,KAAK,eAAe,SAAS,EAC/C,QAAO;EAGR,MAAM,8BAAc,IAAI,KAAa;EACrC,MAAMC,QAAkB,EAAE;AAE1B,OAAK,MAAM,MAAM,KAAK;GACrB,MAAM,OAAO,eAAe,IAAI,GAAG;AACnC,OAAI,CAAC,QAAQ,YAAY,IAAI,KAAK,CACjC;AAGD,eAAY,IAAI,KAAK;AACrB,SAAM,KAAK,KAAK;;AAGjB,MAAI,MAAM,WAAW,EACpB,QAAO;AAGR,SAAO,OAAO,OAAO,MAAM;IAE5B,CAAC,eAAe,CAChB;AAED,QACC,oBAACC;EACA,YAAY;EACZ,WAAW,GACV,6CACA,qBACA,iBACA,UACA;EACD,IAAG;EACH,OAAO;YAEP,qBAAC;GAA8B,WAAU;cACvC,SAAS,gBAAgB,MAAM,KAAK,MAAM,UAAU;AACpD,QAAI,KAAK,SAAS,kBAAkB;KAEnC,MAAM,YAAY,iBAAiB,KAAK,KAAK;AAG7C,SAAI,CAAC,UACJ,QAAO;AAGR,YACC,oBAAC;MACmB;MACG;MACtB,WAAW,KAAK,KAAK;MACrB,OAAO;QACF,KAAK,KAAK,MAAM,kBAAkB,KAAK,KAAK,YAChD;;AAIJ,QAAI,KAAK,SAAS,iBAAiB;KAClC,MAAM,WAAW,KAAK,QAAQ,KAAK,KAAK,QAAQ,KAAK,KAAK;KAC1D,MAAM,iBAAiB,WAAW,QAAQ,YAAY;AAEtD,SAAI,CAAC,eACJ,QAAO;KAGR,MAAM,gBAAgB,eAAe;KAErC,MAAM,UACL,KAAK,KAAK,MAAM,GAAG,SAAS,GAAG,KAAK,KAAK,UAAU,GAAG;AAEvD,YACC,oBAAC;MACgB;MAChB,MAAM,KAAK;QACN,QACJ;;IAOJ,MAAM,YADL,UAAU,SAAS,gCAEG,KAAK,gBACxB,SAAS,gBAAgB,iBAAiB,KAAK,cAAc,GAC7D;IACJ,MAAM,cACL,UAAU,SAAS,IAChB,eAAe,UAAU,GACzB;IAGJ,MAAM,WACL,KAAK,iBACL,KAAK,QAAQ,IAAI,MACjB,SAAS,KAAK,QAAQ,IAAI,aAAa;AAExC,WACC,oBAAC;KACmB;KACG;KACJ;KAClB,OAAO,KAAK,SAAS,EAAE;KAEZ;KACE;OAFR,SAGJ;KAEF,EACF,oBAAC;IAAI,WAAU;cACb,4BAA4B,SAAS,IACrC,oBAAC;KACmB;KACG;KACtB,WAAU;KACV,cAAc;MACb,GACC;KACC;IACyB;GACD"}
1
+ {"version":3,"file":"conversation-timeline.js","names":["EMPTY_SEEN_BY_IDS: readonly string[]","EMPTY_SEEN_BY_NAMES: readonly string[]","ConversationTimelineList: React.FC<ConversationTimelineProps>","names: string[]","PrimitiveConversationTimeline"],"sources":["../../../src/support/components/conversation-timeline.tsx"],"sourcesContent":["import type { AvailableAIAgent, AvailableHumanAgent } from \"@cossistant/types\";\nimport type {\n\tTimelineItem,\n\tTimelinePartEvent,\n} from \"@cossistant/types/api/timeline-item\";\nimport type React from \"react\";\nimport { useCallback, useMemo } from \"react\";\nimport { useConversationTimeline } from \"../../hooks/use-conversation-timeline\";\nimport { useTypingSound } from \"../../hooks/use-typing-sound\";\nimport {\n\tConversationTimelineContainer,\n\tConversationTimeline as PrimitiveConversationTimeline,\n} from \"../../primitives/conversation-timeline\";\nimport { cn } from \"../utils\";\nimport { ConversationEvent } from \"./conversation-event\";\nimport { TimelineMessageGroup } from \"./timeline-message-group\";\nimport { TypingIndicator, type TypingParticipant } from \"./typing-indicator\";\n\n// Helper to extract event part from timeline item\nfunction extractEventPart(item: TimelineItem): TimelinePartEvent | null {\n\tif (item.type !== \"event\") {\n\t\treturn null;\n\t}\n\n\tconst eventPart = item.parts.find(\n\t\t(part): part is TimelinePartEvent => part.type === \"event\"\n\t);\n\n\treturn eventPart || null;\n}\n\nconst EMPTY_SEEN_BY_IDS: readonly string[] = Object.freeze([]);\nconst EMPTY_SEEN_BY_NAMES: readonly string[] = Object.freeze([]);\n\nexport type ConversationTimelineToolProps = {\n\titem: TimelineItem;\n\tconversationId: string;\n};\n\nexport type ConversationTimelineToolDefinition = {\n\tcomponent: React.ComponentType<ConversationTimelineToolProps>;\n};\n\nexport type ConversationTimelineTools = Record<\n\tstring,\n\tConversationTimelineToolDefinition\n>;\n\nexport type ConversationTimelineProps = {\n\tconversationId: string;\n\titems: TimelineItem[];\n\tclassName?: string;\n\tavailableAIAgents: AvailableAIAgent[];\n\tavailableHumanAgents: AvailableHumanAgent[];\n\tcurrentVisitorId?: string;\n\ttools?: ConversationTimelineTools;\n};\n\nexport const ConversationTimelineList: React.FC<ConversationTimelineProps> = ({\n\tconversationId,\n\titems: timelineItems,\n\tclassName,\n\tavailableAIAgents = [],\n\tavailableHumanAgents = [],\n\tcurrentVisitorId,\n\ttools,\n}) => {\n\tconst timeline = useConversationTimeline({\n\t\tconversationId,\n\t\titems: timelineItems,\n\t\tcurrentVisitorId,\n\t});\n\n\tconst typingIndicatorParticipants = useMemo(\n\t\t() =>\n\t\t\ttimeline.typingParticipants.map<TypingParticipant>((participant) => ({\n\t\t\t\tid: participant.id,\n\t\t\t\ttype: participant.type,\n\t\t\t})),\n\t\t[timeline.typingParticipants]\n\t);\n\n\t// Play typing sound when someone is typing\n\tuseTypingSound(typingIndicatorParticipants.length > 0, {\n\t\tvolume: 1,\n\t\tplaybackRate: 1.3,\n\t});\n\n\tconst seenNameLookup = useMemo(() => {\n\t\tconst map = new Map<string, string>();\n\n\t\tfor (const agent of availableHumanAgents) {\n\t\t\tif (agent.name) {\n\t\t\t\tmap.set(agent.id, agent.name);\n\t\t\t}\n\t\t}\n\n\t\tfor (const agent of availableAIAgents) {\n\t\t\tif (agent.name) {\n\t\t\t\tmap.set(agent.id, agent.name);\n\t\t\t}\n\t\t}\n\n\t\treturn map;\n\t}, [availableHumanAgents, availableAIAgents]);\n\n\tconst getSeenByNames = useCallback(\n\t\t(ids: readonly string[] = EMPTY_SEEN_BY_IDS): readonly string[] => {\n\t\t\tif (ids.length === 0 || seenNameLookup.size === 0) {\n\t\t\t\treturn EMPTY_SEEN_BY_NAMES;\n\t\t\t}\n\n\t\t\tconst uniqueNames = new Set<string>();\n\t\t\tconst names: string[] = [];\n\n\t\t\tfor (const id of ids) {\n\t\t\t\tconst name = seenNameLookup.get(id);\n\t\t\t\tif (!name || uniqueNames.has(name)) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\tuniqueNames.add(name);\n\t\t\t\tnames.push(name);\n\t\t\t}\n\n\t\t\tif (names.length === 0) {\n\t\t\t\treturn EMPTY_SEEN_BY_NAMES;\n\t\t\t}\n\n\t\t\treturn Object.freeze(names);\n\t\t},\n\t\t[seenNameLookup]\n\t);\n\n\treturn (\n\t\t<PrimitiveConversationTimeline\n\t\t\tautoScroll={true}\n\t\t\tclassName={cn(\n\t\t\t\t\"overflow-y-scroll scroll-smooth px-3 py-6\",\n\t\t\t\t\"co-scrollbar-thin\",\n\t\t\t\t\"h-full w-full\",\n\t\t\t\tclassName\n\t\t\t)}\n\t\t\tid=\"conversation-timeline\"\n\t\t\titems={timelineItems}\n\t\t>\n\t\t\t<ConversationTimelineContainer className=\"flex min-h-full w-full flex-col gap-3\">\n\t\t\t\t{timeline.groupedMessages.items.map((item, index) => {\n\t\t\t\t\tif (item.type === \"timeline_event\") {\n\t\t\t\t\t\t// Extract event data from parts\n\t\t\t\t\t\tconst eventPart = extractEventPart(item.item);\n\n\t\t\t\t\t\t// Only render if we have valid event data\n\t\t\t\t\t\tif (!eventPart) {\n\t\t\t\t\t\t\treturn null;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\treturn (\n\t\t\t\t\t\t\t<ConversationEvent\n\t\t\t\t\t\t\t\tavailableAIAgents={availableAIAgents}\n\t\t\t\t\t\t\t\tavailableHumanAgents={availableHumanAgents}\n\t\t\t\t\t\t\t\tcreatedAt={item.item.createdAt}\n\t\t\t\t\t\t\t\tevent={eventPart}\n\t\t\t\t\t\t\t\tkey={item.item.id ?? `timeline-event-${item.item.createdAt}`}\n\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\n\t\t\t\t\tif (item.type === \"timeline_tool\") {\n\t\t\t\t\t\tconst toolName = item.tool ?? item.item.tool ?? item.item.type;\n\t\t\t\t\t\tconst toolDefinition = toolName ? tools?.[toolName] : undefined;\n\n\t\t\t\t\t\tif (!toolDefinition) {\n\t\t\t\t\t\t\treturn null;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tconst ToolComponent = toolDefinition.component;\n\n\t\t\t\t\t\tconst toolKey =\n\t\t\t\t\t\t\titem.item.id ?? `${toolName}-${item.item.createdAt}-${index}`;\n\n\t\t\t\t\t\treturn (\n\t\t\t\t\t\t\t<ToolComponent\n\t\t\t\t\t\t\t\tconversationId={conversationId}\n\t\t\t\t\t\t\t\titem={item.item}\n\t\t\t\t\t\t\t\tkey={toolKey}\n\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\n\t\t\t\t\t// Only show seen indicator on the LAST message group sent by the visitor\n\t\t\t\t\tconst isLastVisitorGroup =\n\t\t\t\t\t\tindex === timeline.lastVisitorMessageGroupIndex;\n\t\t\t\t\tconst seenByIds =\n\t\t\t\t\t\tisLastVisitorGroup && item.lastMessageId\n\t\t\t\t\t\t\t? timeline.groupedMessages.getMessageSeenBy(item.lastMessageId)\n\t\t\t\t\t\t\t: EMPTY_SEEN_BY_IDS;\n\t\t\t\t\tconst seenByNames =\n\t\t\t\t\t\tseenByIds.length > 0\n\t\t\t\t\t\t\t? getSeenByNames(seenByIds)\n\t\t\t\t\t\t\t: EMPTY_SEEN_BY_NAMES;\n\n\t\t\t\t\t// Use first timeline item ID as stable key\n\t\t\t\t\tconst groupKey =\n\t\t\t\t\t\titem.lastMessageId ??\n\t\t\t\t\t\titem.items?.[0]?.id ??\n\t\t\t\t\t\t`group-${item.items?.[0]?.createdAt ?? index}`;\n\n\t\t\t\t\treturn (\n\t\t\t\t\t\t<TimelineMessageGroup\n\t\t\t\t\t\t\tavailableAIAgents={availableAIAgents}\n\t\t\t\t\t\t\tavailableHumanAgents={availableHumanAgents}\n\t\t\t\t\t\t\tcurrentVisitorId={currentVisitorId}\n\t\t\t\t\t\t\titems={item.items || []}\n\t\t\t\t\t\t\tkey={groupKey}\n\t\t\t\t\t\t\tseenByIds={seenByIds}\n\t\t\t\t\t\t\tseenByNames={seenByNames}\n\t\t\t\t\t\t/>\n\t\t\t\t\t);\n\t\t\t\t})}\n\t\t\t\t<div className=\"h-6 w-full\">\n\t\t\t\t\t{typingIndicatorParticipants.length > 0 ? (\n\t\t\t\t\t\t<TypingIndicator\n\t\t\t\t\t\t\tavailableAIAgents={availableAIAgents}\n\t\t\t\t\t\t\tavailableHumanAgents={availableHumanAgents}\n\t\t\t\t\t\t\tclassName=\"mt-2\"\n\t\t\t\t\t\t\tparticipants={typingIndicatorParticipants}\n\t\t\t\t\t\t/>\n\t\t\t\t\t) : null}\n\t\t\t\t</div>\n\t\t\t</ConversationTimelineContainer>\n\t\t</PrimitiveConversationTimeline>\n\t);\n};\n"],"mappings":";;;;;;;;;;;AAmBA,SAAS,iBAAiB,MAA8C;AACvE,KAAI,KAAK,SAAS,QACjB,QAAO;AAOR,QAJkB,KAAK,MAAM,MAC3B,SAAoC,KAAK,SAAS,QACnD,IAEmB;;AAGrB,MAAMA,oBAAuC,OAAO,OAAO,EAAE,CAAC;AAC9D,MAAMC,sBAAyC,OAAO,OAAO,EAAE,CAAC;AA0BhE,MAAaC,4BAAiE,EAC7E,gBACA,OAAO,eACP,WACA,oBAAoB,EAAE,EACtB,uBAAuB,EAAE,EACzB,kBACA,YACK;CACL,MAAM,WAAW,wBAAwB;EACxC;EACA,OAAO;EACP;EACA,CAAC;CAEF,MAAM,8BAA8B,cAElC,SAAS,mBAAmB,KAAwB,iBAAiB;EACpE,IAAI,YAAY;EAChB,MAAM,YAAY;EAClB,EAAE,EACJ,CAAC,SAAS,mBAAmB,CAC7B;AAGD,gBAAe,4BAA4B,SAAS,GAAG;EACtD,QAAQ;EACR,cAAc;EACd,CAAC;CAEF,MAAM,iBAAiB,cAAc;EACpC,MAAM,sBAAM,IAAI,KAAqB;AAErC,OAAK,MAAM,SAAS,qBACnB,KAAI,MAAM,KACT,KAAI,IAAI,MAAM,IAAI,MAAM,KAAK;AAI/B,OAAK,MAAM,SAAS,kBACnB,KAAI,MAAM,KACT,KAAI,IAAI,MAAM,IAAI,MAAM,KAAK;AAI/B,SAAO;IACL,CAAC,sBAAsB,kBAAkB,CAAC;CAE7C,MAAM,iBAAiB,aACrB,MAAyB,sBAAyC;AAClE,MAAI,IAAI,WAAW,KAAK,eAAe,SAAS,EAC/C,QAAO;EAGR,MAAM,8BAAc,IAAI,KAAa;EACrC,MAAMC,QAAkB,EAAE;AAE1B,OAAK,MAAM,MAAM,KAAK;GACrB,MAAM,OAAO,eAAe,IAAI,GAAG;AACnC,OAAI,CAAC,QAAQ,YAAY,IAAI,KAAK,CACjC;AAGD,eAAY,IAAI,KAAK;AACrB,SAAM,KAAK,KAAK;;AAGjB,MAAI,MAAM,WAAW,EACpB,QAAO;AAGR,SAAO,OAAO,OAAO,MAAM;IAE5B,CAAC,eAAe,CAChB;AAED,QACC,oBAACC;EACA,YAAY;EACZ,WAAW,GACV,6CACA,qBACA,iBACA,UACA;EACD,IAAG;EACH,OAAO;YAEP,qBAAC;GAA8B,WAAU;cACvC,SAAS,gBAAgB,MAAM,KAAK,MAAM,UAAU;AACpD,QAAI,KAAK,SAAS,kBAAkB;KAEnC,MAAM,YAAY,iBAAiB,KAAK,KAAK;AAG7C,SAAI,CAAC,UACJ,QAAO;AAGR,YACC,oBAAC;MACmB;MACG;MACtB,WAAW,KAAK,KAAK;MACrB,OAAO;QACF,KAAK,KAAK,MAAM,kBAAkB,KAAK,KAAK,YAChD;;AAIJ,QAAI,KAAK,SAAS,iBAAiB;KAClC,MAAM,WAAW,KAAK,QAAQ,KAAK,KAAK,QAAQ,KAAK,KAAK;KAC1D,MAAM,iBAAiB,WAAW,QAAQ,YAAY;AAEtD,SAAI,CAAC,eACJ,QAAO;KAGR,MAAM,gBAAgB,eAAe;KAErC,MAAM,UACL,KAAK,KAAK,MAAM,GAAG,SAAS,GAAG,KAAK,KAAK,UAAU,GAAG;AAEvD,YACC,oBAAC;MACgB;MAChB,MAAM,KAAK;QACN,QACJ;;IAOJ,MAAM,YADL,UAAU,SAAS,gCAEG,KAAK,gBACxB,SAAS,gBAAgB,iBAAiB,KAAK,cAAc,GAC7D;IACJ,MAAM,cACL,UAAU,SAAS,IAChB,eAAe,UAAU,GACzB;IAGJ,MAAM,WACL,KAAK,iBACL,KAAK,QAAQ,IAAI,MACjB,SAAS,KAAK,QAAQ,IAAI,aAAa;AAExC,WACC,oBAAC;KACmB;KACG;KACJ;KAClB,OAAO,KAAK,SAAS,EAAE;KAEZ;KACE;OAFR,SAGJ;KAEF,EACF,oBAAC;IAAI,WAAU;cACb,4BAA4B,SAAS,IACrC,oBAAC;KACmB;KACG;KACtB,WAAU;KACV,cAAc;MACb,GACC;KACC;IACyB;GACD"}
@@ -1 +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,OA8JZ;EAEW,YAAA,CAAA,EAAA,OAAe;EAKd,KAAA,CAAA,EA7KJ,KA2LR,GAAA,IAAA;UA1LQ;;;;;;cAOI,iBAAiB,KAAA,CAAM,GAAG;KAgK3B,eAAA;;;;cAKC,YAAY,KAAA,CAAM,GAAG"}
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"}
@@ -24,13 +24,16 @@ const MultimodalInput = ({ className, value, onChange, onSubmit, onFileSelect, p
24
24
  hasContent,
25
25
  isSubmitting
26
26
  });
27
- const canSubmit = !(disabled || isSubmitting) && hasContent;
27
+ const canSubmit = !disabled && hasContent;
28
28
  const text = useSupportText();
29
29
  const resolvedPlaceholder = placeholder ?? text("component.multimodalInput.placeholder");
30
30
  const handleSubmit = () => {
31
31
  if (!canSubmit) return;
32
32
  onSubmit();
33
33
  focusComposer();
34
+ requestAnimationFrame(() => {
35
+ focusComposer();
36
+ });
34
37
  };
35
38
  const handleFormSubmit = (e) => {
36
39
  e.preventDefault();
@@ -83,8 +86,9 @@ const MultimodalInput = ({ className, value, onChange, onSubmit, onFileSelect, p
83
86
  /* @__PURE__ */ jsxs("div", {
84
87
  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",
85
88
  children: [/* @__PURE__ */ jsx(MultimodalInput$1, {
89
+ autoFocus: true,
86
90
  className: cn("flex-1 resize-none overflow-hidden p-3 text-co-foreground text-sm placeholder:text-co-primary/50 focus-visible:outline-none", className),
87
- disabled: disabled || isSubmitting,
91
+ disabled,
88
92
  error,
89
93
  onChange,
90
94
  onFileSelect,
@@ -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 || isSubmitting) && 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\tfocusComposer();\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\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 || isSubmitting}\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,EAAE,YAAY,iBAAiB;CACjD,MAAM,OAAO,gBAAgB;CAC7B,MAAM,sBACL,eAAe,KAAK,wCAAwC;CAE7D,MAAM,qBAAqB;AAC1B,MAAI,CAAC,UACJ;AAGD,YAAU;AACV,iBAAe;;CAGhB,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,WAAW,GACV,+HACA,UACA;KACD,UAAU,YAAY;KACf;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","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,3 +1,4 @@
1
+ import { CustomPage } from "../router.js";
1
2
  import { SupportProps } from "../index.js";
2
3
  import React from "react";
3
4
 
@@ -9,6 +10,7 @@ type SupportContentProps = {
9
10
  positioning?: "fixed" | "absolute";
10
11
  slots?: SupportProps["slots"];
11
12
  classNames?: SupportProps["classNames"];
13
+ customPages?: CustomPage[];
12
14
  children?: React.ReactNode;
13
15
  };
14
16
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"support-content.d.ts","names":[],"sources":["../../../src/support/components/support-content.tsx"],"sourcesContent":[],"mappings":";;;;KAWK,mBAAA;;EAAA,QAAA,CAAA,EAAA,KAAA,GAAA,QAAmB;EAKf,KAAA,CAAA,EAAA,OAAA,GAAA,MAAA;EACK,WAAA,CAAA,EAAA,OAAA,GAAA,UAAA;EACF,KAAM,CAAA,EAFT,YAES,CAAA,OAAA,CAAA;EAAS,UAAA,CAAA,EADb,YACa,CAAA,YAAA,CAAA;EAMd,QAAA,CAAA,EAND,KAAA,CAAM,SAwDjB;;;;;cAlDY,gBAAgB,KAAA,CAAM,GAAG"}
1
+ {"version":3,"file":"support-content.d.ts","names":[],"sources":["../../../src/support/components/support-content.tsx"],"sourcesContent":[],"mappings":";;;;;KAYK,mBAAA;;EAAA,QAAA,CAAA,EAAA,KAAA,GAAA,QAAmB;EAKf,KAAA,CAAA,EAAA,OAAA,GAAA,MAAA;EACK,WAAA,CAAA,EAAA,OAAA,GAAA,UAAA;EACC,KAAA,CAAA,EAFN,YAEM,CAAA,OAAA,CAAA;EACH,UAAM,CAAA,EAFJ,YAEI,CAAA,YAAA,CAAA;EAAS,WAAA,CAAA,EADZ,UACY,EAAA;EAMd,QAAA,CAAA,EAND,KAAA,CAAM,SAyDjB;;;;;cAnDY,gBAAgB,KAAA,CAAM,GAAG"}
@@ -12,7 +12,7 @@ import { motion } from "motion/react";
12
12
  /**
13
13
  * Main support widget container handling layout, positioning, and routing.
14
14
  */
15
- const SupportContent = ({ className, position = "bottom", align = "right", positioning = "fixed", slots = {}, classNames = {}, children }) => {
15
+ const SupportContent = ({ className, position = "bottom", align = "right", positioning = "fixed", slots = {}, classNames = {}, customPages, children }) => {
16
16
  const BubbleComponent = slots.bubble ?? Bubble;
17
17
  const ContainerComponent = slots.container ?? Container;
18
18
  const RouterComponent = slots.router ?? SupportRouter;
@@ -35,7 +35,10 @@ const SupportContent = ({ className, position = "bottom", align = "right", posit
35
35
  align,
36
36
  className: classNames.container,
37
37
  position,
38
- children: /* @__PURE__ */ jsx(RouterComponent, { children })
38
+ children: /* @__PURE__ */ jsx(RouterComponent, {
39
+ customPages,
40
+ children
41
+ })
39
42
  })]
40
43
  });
41
44
  };
@@ -1 +1 @@
1
- {"version":3,"file":"support-content.js","names":["SupportContent: React.FC<SupportContentProps>"],"sources":["../../../src/support/components/support-content.tsx"],"sourcesContent":["\"use client\";\n\nimport { motion } from \"motion/react\";\nimport type React from \"react\";\n\nimport type { SupportProps } from \"../index\";\nimport { SupportRouter } from \"../router\";\nimport { cn } from \"../utils\";\nimport { Bubble } from \"./bubble\";\nimport { Container } from \"./container\";\n\ntype SupportContentProps = {\n\tclassName?: string;\n\tposition?: \"top\" | \"bottom\";\n\talign?: \"right\" | \"left\";\n\tpositioning?: \"fixed\" | \"absolute\";\n\tslots?: SupportProps[\"slots\"];\n\tclassNames?: SupportProps[\"classNames\"];\n\tchildren?: React.ReactNode;\n};\n\n/**\n * Main support widget container handling layout, positioning, and routing.\n */\nexport const SupportContent: React.FC<SupportContentProps> = ({\n\tclassName,\n\tposition = \"bottom\",\n\talign = \"right\",\n\tpositioning = \"fixed\",\n\tslots = {},\n\tclassNames = {},\n\tchildren,\n}) => {\n\t// Use custom components if provided, otherwise use defaults\n\tconst BubbleComponent = slots.bubble ?? Bubble;\n\tconst ContainerComponent = slots.container ?? Container;\n\tconst RouterComponent = slots.router ?? SupportRouter;\n\n\tconst containerClasses = cn(\n\t\t\"cossistant z-[9999]\",\n\t\tpositioning === \"fixed\" ? \"fixed\" : \"absolute\",\n\t\t{\n\t\t\t\"bottom-4\": position === \"bottom\",\n\t\t\t\"top-4\": position === \"top\",\n\t\t\t\"right-4\": align === \"right\",\n\t\t\t\"left-4\": align === \"left\",\n\t\t},\n\t\tclassNames.root,\n\t\tclassName\n\t);\n\n\treturn (\n\t\t<motion.div\n\t\t\tanimate={{ opacity: 1 }}\n\t\t\tclassName={containerClasses}\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<BubbleComponent\n\t\t\t\tclassName={cn(\"z-[1000] md:z-[9999]\", classNames.bubble)}\n\t\t\t/>\n\t\t\t<ContainerComponent\n\t\t\t\talign={align}\n\t\t\t\tclassName={classNames.container}\n\t\t\t\tposition={position}\n\t\t\t>\n\t\t\t\t<RouterComponent>{children}</RouterComponent>\n\t\t\t</ContainerComponent>\n\t\t</motion.div>\n\t);\n};\n"],"mappings":";;;;;;;;;;;;;;AAwBA,MAAaA,kBAAiD,EAC7D,WACA,WAAW,UACX,QAAQ,SACR,cAAc,SACd,QAAQ,EAAE,EACV,aAAa,EAAE,EACf,eACK;CAEL,MAAM,kBAAkB,MAAM,UAAU;CACxC,MAAM,qBAAqB,MAAM,aAAa;CAC9C,MAAM,kBAAkB,MAAM,UAAU;CAExC,MAAM,mBAAmB,GACxB,uBACA,gBAAgB,UAAU,UAAU,YACpC;EACC,YAAY,aAAa;EACzB,SAAS,aAAa;EACtB,WAAW,UAAU;EACrB,UAAU,UAAU;EACpB,EACD,WAAW,MACX,UACA;AAED,QACC,qBAAC,OAAO;EACP,SAAS,EAAE,SAAS,GAAG;EACvB,WAAW;EACX,SAAS,EAAE,SAAS,GAAG;EACvB,QAAO;EACP,YAAY;GACX,SAAS,EAAE,MAAM,cAAc;GAC/B,QAAQ,EAAE,UAAU,IAAK;GACzB;aAED,oBAAC,mBACA,WAAW,GAAG,wBAAwB,WAAW,OAAO,GACvD,EACF,oBAAC;GACO;GACP,WAAW,WAAW;GACZ;aAEV,oBAAC,mBAAiB,WAA2B;IACzB;GACT"}
1
+ {"version":3,"file":"support-content.js","names":["SupportContent: React.FC<SupportContentProps>"],"sources":["../../../src/support/components/support-content.tsx"],"sourcesContent":["\"use client\";\n\nimport { motion } from \"motion/react\";\nimport type React from \"react\";\n\nimport type { SupportProps } from \"../index\";\nimport type { CustomPage } from \"../router\";\nimport { SupportRouter } from \"../router\";\nimport { cn } from \"../utils\";\nimport { Bubble } from \"./bubble\";\nimport { Container } from \"./container\";\n\ntype SupportContentProps = {\n\tclassName?: string;\n\tposition?: \"top\" | \"bottom\";\n\talign?: \"right\" | \"left\";\n\tpositioning?: \"fixed\" | \"absolute\";\n\tslots?: SupportProps[\"slots\"];\n\tclassNames?: SupportProps[\"classNames\"];\n\tcustomPages?: CustomPage[];\n\tchildren?: React.ReactNode;\n};\n\n/**\n * Main support widget container handling layout, positioning, and routing.\n */\nexport const SupportContent: React.FC<SupportContentProps> = ({\n\tclassName,\n\tposition = \"bottom\",\n\talign = \"right\",\n\tpositioning = \"fixed\",\n\tslots = {},\n\tclassNames = {},\n\tcustomPages,\n\tchildren,\n}) => {\n\t// Use custom components if provided, otherwise use defaults\n\tconst BubbleComponent = slots.bubble ?? Bubble;\n\tconst ContainerComponent = slots.container ?? Container;\n\tconst RouterComponent = slots.router ?? SupportRouter;\n\n\tconst containerClasses = cn(\n\t\t\"cossistant z-[9999]\",\n\t\tpositioning === \"fixed\" ? \"fixed\" : \"absolute\",\n\t\t{\n\t\t\t\"bottom-4\": position === \"bottom\",\n\t\t\t\"top-4\": position === \"top\",\n\t\t\t\"right-4\": align === \"right\",\n\t\t\t\"left-4\": align === \"left\",\n\t\t},\n\t\tclassNames.root,\n\t\tclassName\n\t);\n\n\treturn (\n\t\t<motion.div\n\t\t\tanimate={{ opacity: 1 }}\n\t\t\tclassName={containerClasses}\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<BubbleComponent\n\t\t\t\tclassName={cn(\"z-[1000] md:z-[9999]\", classNames.bubble)}\n\t\t\t/>\n\t\t\t<ContainerComponent\n\t\t\t\talign={align}\n\t\t\t\tclassName={classNames.container}\n\t\t\t\tposition={position}\n\t\t\t>\n\t\t\t\t<RouterComponent customPages={customPages}>{children}</RouterComponent>\n\t\t\t</ContainerComponent>\n\t\t</motion.div>\n\t);\n};\n"],"mappings":";;;;;;;;;;;;;;AA0BA,MAAaA,kBAAiD,EAC7D,WACA,WAAW,UACX,QAAQ,SACR,cAAc,SACd,QAAQ,EAAE,EACV,aAAa,EAAE,EACf,aACA,eACK;CAEL,MAAM,kBAAkB,MAAM,UAAU;CACxC,MAAM,qBAAqB,MAAM,aAAa;CAC9C,MAAM,kBAAkB,MAAM,UAAU;CAExC,MAAM,mBAAmB,GACxB,uBACA,gBAAgB,UAAU,UAAU,YACpC;EACC,YAAY,aAAa;EACzB,SAAS,aAAa;EACtB,WAAW,UAAU;EACrB,UAAU,UAAU;EACpB,EACD,WAAW,MACX,UACA;AAED,QACC,qBAAC,OAAO;EACP,SAAS,EAAE,SAAS,GAAG;EACvB,WAAW;EACX,SAAS,EAAE,SAAS,GAAG;EACvB,QAAO;EACP,YAAY;GACX,SAAS,EAAE,MAAM,cAAc;GAC/B,QAAQ,EAAE,UAAU,IAAK;GACzB;aAED,oBAAC,mBACA,WAAW,GAAG,wBAAwB,WAAW,OAAO,GACvD,EACF,oBAAC;GACO;GACP,WAAW,WAAW;GACZ;aAEV,oBAAC;IAA6B;IAAc;KAA2B;IACnD;GACT"}
@@ -43,7 +43,7 @@ const TimelineMessageGroup = ({ items, availableAIAgents, availableHumanAgents,
43
43
  const humanAgent = availableHumanAgents.find((agent) => agent.id === firstItem?.userId);
44
44
  const aiAgent = availableAIAgents.find((agent) => agent.id === firstItem?.aiAgentId);
45
45
  if (items.length === 0) return null;
46
- const hasSeenIndicator = seenByIds.length > 0 && seenByNames.length > 0;
46
+ const hasSeenIndicator = seenByIds.length > 0;
47
47
  return /* @__PURE__ */ jsx(TimelineItemGroup, {
48
48
  items,
49
49
  seenByIds,
@@ -85,7 +85,7 @@ const TimelineMessageGroup = ({ items, availableAIAgents, availableHumanAgents,
85
85
  children: /* @__PURE__ */ jsx(TimelineItemGroupSeenIndicator, {
86
86
  className: "px-1 text-co-muted-foreground text-xs",
87
87
  seenByIds,
88
- children: () => `Seen by ${seenByNames.join(", ")}`
88
+ children: () => seenByNames.length > 0 ? `Seen by ${seenByNames.join(", ")}` : "Seen"
89
89
  })
90
90
  }, "seen-indicator")
91
91
  })
@@ -1 +1 @@
1
- {"version":3,"file":"timeline-message-group.js","names":["EMPTY_SEEN_BY_IDS: readonly string[]","EMPTY_SEEN_BY_NAMES: readonly string[]","TimelineMessageGroup: React.FC<TimelineMessageGroupProps>","PrimitiveTimelineItemGroup"],"sources":["../../../src/support/components/timeline-message-group.tsx"],"sourcesContent":["import type { AvailableAIAgent, AvailableHumanAgent } from \"@cossistant/types\";\nimport { SenderType } from \"@cossistant/types\";\nimport type { TimelineItem } from \"@cossistant/types/api/timeline-item\";\nimport { motion } from \"motion/react\";\nimport type React from \"react\";\nimport {\n\tTimelineItemGroup as PrimitiveTimelineItemGroup,\n\tTimelineItemGroupAvatar,\n\tTimelineItemGroupContent,\n\tTimelineItemGroupHeader,\n\tTimelineItemGroupSeenIndicator,\n} from \"../../primitives/timeline-item-group\";\nimport { cn } from \"../utils\";\nimport { Avatar } from \"./avatar\";\nimport { CossistantLogo } from \"./cossistant-branding\";\nimport { TimelineMessageItem } from \"./timeline-message-item\";\n\nconst MESSAGE_ANIMATION = {\n\tinitial: { opacity: 0, y: 6 },\n\tanimate: { opacity: 1, y: 0 },\n\texit: { opacity: 0 },\n\ttransition: {\n\t\tduration: 0.1,\n\t\tease: [0.25, 0.46, 0.45, 0.94] as const, // easeOutCubic\n\t},\n} as const;\n\nconst SEEN_ANIMATION = {\n\tinitial: { opacity: 0 },\n\tanimate: { opacity: 1 },\n\ttransition: {\n\t\tduration: 0.1,\n\t\tease: \"easeOut\" as const,\n\t},\n} as const;\n\nexport type TimelineMessageGroupProps = {\n\titems: TimelineItem[];\n\tavailableAIAgents: AvailableAIAgent[];\n\tavailableHumanAgents: AvailableHumanAgent[];\n\tcurrentVisitorId?: string;\n\tseenByIds?: readonly string[];\n\tseenByNames?: readonly string[];\n};\n\nconst EMPTY_SEEN_BY_IDS: readonly string[] = Object.freeze([]);\nconst EMPTY_SEEN_BY_NAMES: readonly string[] = Object.freeze([]);\n\nexport const TimelineMessageGroup: React.FC<TimelineMessageGroupProps> = ({\n\titems,\n\tavailableAIAgents,\n\tavailableHumanAgents,\n\tcurrentVisitorId,\n\tseenByIds = EMPTY_SEEN_BY_IDS,\n\tseenByNames = EMPTY_SEEN_BY_NAMES,\n}) => {\n\t// Get agent info for the sender\n\tconst firstItem = items[0];\n\tconst humanAgent = availableHumanAgents.find(\n\t\t(agent) => agent.id === firstItem?.userId\n\t);\n\tconst aiAgent = availableAIAgents.find(\n\t\t(agent) => agent.id === firstItem?.aiAgentId\n\t);\n\n\tif (items.length === 0) {\n\t\treturn null;\n\t}\n\n\tconst hasSeenIndicator = seenByIds.length > 0 && seenByNames.length > 0;\n\n\treturn (\n\t\t<PrimitiveTimelineItemGroup\n\t\t\titems={items}\n\t\t\tseenByIds={seenByIds}\n\t\t\tviewerId={currentVisitorId}\n\t\t\tviewerType={SenderType.VISITOR}\n\t\t>\n\t\t\t{({\n\t\t\t\tisSentByViewer,\n\t\t\t\tisReceivedByViewer,\n\t\t\t\tisVisitor,\n\t\t\t\tisAI,\n\t\t\t\tisTeamMember,\n\t\t\t}) => (\n\t\t\t\t<div\n\t\t\t\t\tclassName={cn(\n\t\t\t\t\t\t\"flex w-full gap-2\",\n\t\t\t\t\t\t// Support widget POV: visitor messages are sent (right side)\n\t\t\t\t\t\t// Agent messages are received (left side)\n\t\t\t\t\t\tisSentByViewer && \"flex-row-reverse\",\n\t\t\t\t\t\tisReceivedByViewer && \"flex-row\"\n\t\t\t\t\t)}\n\t\t\t\t>\n\t\t\t\t\t{/* Avatar - only show for received messages (agents) */}\n\t\t\t\t\t{isReceivedByViewer && (\n\t\t\t\t\t\t<TimelineItemGroupAvatar className=\"flex flex-shrink-0 flex-col justify-end\">\n\t\t\t\t\t\t\t{isAI ? (\n\t\t\t\t\t\t\t\t<div className=\"flex size-6 items-center justify-center rounded-full bg-co-primary/10\">\n\t\t\t\t\t\t\t\t\t<CossistantLogo className=\"h-4 w-4 text-co-primary\" />\n\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t) : (\n\t\t\t\t\t\t\t\t<Avatar\n\t\t\t\t\t\t\t\t\tclassName=\"size-6\"\n\t\t\t\t\t\t\t\t\timage={humanAgent?.image}\n\t\t\t\t\t\t\t\t\tname={humanAgent?.name || \"Support\"}\n\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t</TimelineItemGroupAvatar>\n\t\t\t\t\t)}\n\n\t\t\t\t\t<TimelineItemGroupContent\n\t\t\t\t\t\tclassName={cn(\"flex flex-col gap-1\", isSentByViewer && \"items-end\")}\n\t\t\t\t\t>\n\t\t\t\t\t\t{/* Header - show sender name for received messages (agents) */}\n\t\t\t\t\t\t{isReceivedByViewer && (\n\t\t\t\t\t\t\t<TimelineItemGroupHeader className=\"px-1 text-co-muted-foreground text-xs\">\n\t\t\t\t\t\t\t\t{isAI\n\t\t\t\t\t\t\t\t\t? aiAgent?.name || \"AI Assistant\"\n\t\t\t\t\t\t\t\t\t: humanAgent?.name || \"Support\"}\n\t\t\t\t\t\t\t</TimelineItemGroupHeader>\n\t\t\t\t\t\t)}\n\n\t\t\t\t\t\t{items.map((item, index) => (\n\t\t\t\t\t\t\t<motion.div key={item.id} {...MESSAGE_ANIMATION}>\n\t\t\t\t\t\t\t\t<TimelineMessageItem\n\t\t\t\t\t\t\t\t\tisLast={index === items.length - 1}\n\t\t\t\t\t\t\t\t\tisSentByViewer={isSentByViewer}\n\t\t\t\t\t\t\t\t\titem={item}\n\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t</motion.div>\n\t\t\t\t\t\t))}\n\n\t\t\t\t\t\t{isSentByViewer && (\n\t\t\t\t\t\t\t<div className={cn(\"\", hasSeenIndicator && \"mt-2\")}>\n\t\t\t\t\t\t\t\t<div className=\"min-h-[1.25rem]\">\n\t\t\t\t\t\t\t\t\t{hasSeenIndicator && (\n\t\t\t\t\t\t\t\t\t\t<motion.div key=\"seen-indicator\" {...SEEN_ANIMATION}>\n\t\t\t\t\t\t\t\t\t\t\t<TimelineItemGroupSeenIndicator\n\t\t\t\t\t\t\t\t\t\t\t\tclassName=\"px-1 text-co-muted-foreground text-xs\"\n\t\t\t\t\t\t\t\t\t\t\t\tseenByIds={seenByIds}\n\t\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t\t{() => `Seen by ${seenByNames.join(\", \")}`}\n\t\t\t\t\t\t\t\t\t\t\t</TimelineItemGroupSeenIndicator>\n\t\t\t\t\t\t\t\t\t\t</motion.div>\n\t\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t)}\n\t\t\t\t\t</TimelineItemGroupContent>\n\t\t\t\t</div>\n\t\t\t)}\n\t\t</PrimitiveTimelineItemGroup>\n\t);\n};\n\nTimelineMessageGroup.displayName = \"TimelineMessageGroup\";\n"],"mappings":";;;;;;;;;;AAiBA,MAAM,oBAAoB;CACzB,SAAS;EAAE,SAAS;EAAG,GAAG;EAAG;CAC7B,SAAS;EAAE,SAAS;EAAG,GAAG;EAAG;CAC7B,MAAM,EAAE,SAAS,GAAG;CACpB,YAAY;EACX,UAAU;EACV,MAAM;GAAC;GAAM;GAAM;GAAM;GAAK;EAC9B;CACD;AAED,MAAM,iBAAiB;CACtB,SAAS,EAAE,SAAS,GAAG;CACvB,SAAS,EAAE,SAAS,GAAG;CACvB,YAAY;EACX,UAAU;EACV,MAAM;EACN;CACD;AAWD,MAAMA,oBAAuC,OAAO,OAAO,EAAE,CAAC;AAC9D,MAAMC,sBAAyC,OAAO,OAAO,EAAE,CAAC;AAEhE,MAAaC,wBAA6D,EACzE,OACA,mBACA,sBACA,kBACA,YAAY,mBACZ,cAAc,0BACT;CAEL,MAAM,YAAY,MAAM;CACxB,MAAM,aAAa,qBAAqB,MACtC,UAAU,MAAM,OAAO,WAAW,OACnC;CACD,MAAM,UAAU,kBAAkB,MAChC,UAAU,MAAM,OAAO,WAAW,UACnC;AAED,KAAI,MAAM,WAAW,EACpB,QAAO;CAGR,MAAM,mBAAmB,UAAU,SAAS,KAAK,YAAY,SAAS;AAEtE,QACC,oBAACC;EACO;EACI;EACX,UAAU;EACV,YAAY,WAAW;aAErB,EACD,gBACA,oBACA,WACA,MACA,mBAEA,qBAAC;GACA,WAAW,GACV,qBAGA,kBAAkB,oBAClB,sBAAsB,WACtB;cAGA,sBACA,oBAAC;IAAwB,WAAU;cACjC,OACA,oBAAC;KAAI,WAAU;eACd,oBAAC,kBAAe,WAAU,4BAA4B;MACjD,GAEN,oBAAC;KACA,WAAU;KACV,OAAO,YAAY;KACnB,MAAM,YAAY,QAAQ;MACzB;KAEsB,EAG3B,qBAAC;IACA,WAAW,GAAG,uBAAuB,kBAAkB,YAAY;;KAGlE,sBACA,oBAAC;MAAwB,WAAU;gBACjC,OACE,SAAS,QAAQ,iBACjB,YAAY,QAAQ;OACE;KAG1B,MAAM,KAAK,MAAM,UACjB,oBAAC,OAAO;MAAkB,GAAI;gBAC7B,oBAAC;OACA,QAAQ,UAAU,MAAM,SAAS;OACjB;OACV;QACL;QALc,KAAK,GAMT,CACZ;KAED,kBACA,oBAAC;MAAI,WAAW,GAAG,IAAI,oBAAoB,OAAO;gBACjD,oBAAC;OAAI,WAAU;iBACb,oBACA,oBAAC,OAAO;QAAyB,GAAI;kBACpC,oBAAC;SACA,WAAU;SACC;yBAEJ,WAAW,YAAY,KAAK,KAAK;UACR;UANlB,iBAOH;QAET;OACD;;KAEmB;IACtB;GAEqB;;AAI/B,qBAAqB,cAAc"}
1
+ {"version":3,"file":"timeline-message-group.js","names":["EMPTY_SEEN_BY_IDS: readonly string[]","EMPTY_SEEN_BY_NAMES: readonly string[]","TimelineMessageGroup: React.FC<TimelineMessageGroupProps>","PrimitiveTimelineItemGroup"],"sources":["../../../src/support/components/timeline-message-group.tsx"],"sourcesContent":["import type { AvailableAIAgent, AvailableHumanAgent } from \"@cossistant/types\";\nimport { SenderType } from \"@cossistant/types\";\nimport type { TimelineItem } from \"@cossistant/types/api/timeline-item\";\nimport { motion } from \"motion/react\";\nimport type React from \"react\";\nimport {\n\tTimelineItemGroup as PrimitiveTimelineItemGroup,\n\tTimelineItemGroupAvatar,\n\tTimelineItemGroupContent,\n\tTimelineItemGroupHeader,\n\tTimelineItemGroupSeenIndicator,\n} from \"../../primitives/timeline-item-group\";\nimport { cn } from \"../utils\";\nimport { Avatar } from \"./avatar\";\nimport { CossistantLogo } from \"./cossistant-branding\";\nimport { TimelineMessageItem } from \"./timeline-message-item\";\n\nconst MESSAGE_ANIMATION = {\n\tinitial: { opacity: 0, y: 6 },\n\tanimate: { opacity: 1, y: 0 },\n\texit: { opacity: 0 },\n\ttransition: {\n\t\tduration: 0.1,\n\t\tease: [0.25, 0.46, 0.45, 0.94] as const, // easeOutCubic\n\t},\n} as const;\n\nconst SEEN_ANIMATION = {\n\tinitial: { opacity: 0 },\n\tanimate: { opacity: 1 },\n\ttransition: {\n\t\tduration: 0.1,\n\t\tease: \"easeOut\" as const,\n\t},\n} as const;\n\nexport type TimelineMessageGroupProps = {\n\titems: TimelineItem[];\n\tavailableAIAgents: AvailableAIAgent[];\n\tavailableHumanAgents: AvailableHumanAgent[];\n\tcurrentVisitorId?: string;\n\tseenByIds?: readonly string[];\n\tseenByNames?: readonly string[];\n};\n\nconst EMPTY_SEEN_BY_IDS: readonly string[] = Object.freeze([]);\nconst EMPTY_SEEN_BY_NAMES: readonly string[] = Object.freeze([]);\n\nexport const TimelineMessageGroup: React.FC<TimelineMessageGroupProps> = ({\n\titems,\n\tavailableAIAgents,\n\tavailableHumanAgents,\n\tcurrentVisitorId,\n\tseenByIds = EMPTY_SEEN_BY_IDS,\n\tseenByNames = EMPTY_SEEN_BY_NAMES,\n}) => {\n\t// Get agent info for the sender\n\tconst firstItem = items[0];\n\tconst humanAgent = availableHumanAgents.find(\n\t\t(agent) => agent.id === firstItem?.userId\n\t);\n\tconst aiAgent = availableAIAgents.find(\n\t\t(agent) => agent.id === firstItem?.aiAgentId\n\t);\n\n\tif (items.length === 0) {\n\t\treturn null;\n\t}\n\n\tconst hasSeenIndicator = seenByIds.length > 0;\n\n\treturn (\n\t\t<PrimitiveTimelineItemGroup\n\t\t\titems={items}\n\t\t\tseenByIds={seenByIds}\n\t\t\tviewerId={currentVisitorId}\n\t\t\tviewerType={SenderType.VISITOR}\n\t\t>\n\t\t\t{({\n\t\t\t\tisSentByViewer,\n\t\t\t\tisReceivedByViewer,\n\t\t\t\tisVisitor,\n\t\t\t\tisAI,\n\t\t\t\tisTeamMember,\n\t\t\t}) => (\n\t\t\t\t<div\n\t\t\t\t\tclassName={cn(\n\t\t\t\t\t\t\"flex w-full gap-2\",\n\t\t\t\t\t\t// Support widget POV: visitor messages are sent (right side)\n\t\t\t\t\t\t// Agent messages are received (left side)\n\t\t\t\t\t\tisSentByViewer && \"flex-row-reverse\",\n\t\t\t\t\t\tisReceivedByViewer && \"flex-row\"\n\t\t\t\t\t)}\n\t\t\t\t>\n\t\t\t\t\t{/* Avatar - only show for received messages (agents) */}\n\t\t\t\t\t{isReceivedByViewer && (\n\t\t\t\t\t\t<TimelineItemGroupAvatar className=\"flex flex-shrink-0 flex-col justify-end\">\n\t\t\t\t\t\t\t{isAI ? (\n\t\t\t\t\t\t\t\t<div className=\"flex size-6 items-center justify-center rounded-full bg-co-primary/10\">\n\t\t\t\t\t\t\t\t\t<CossistantLogo className=\"h-4 w-4 text-co-primary\" />\n\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t) : (\n\t\t\t\t\t\t\t\t<Avatar\n\t\t\t\t\t\t\t\t\tclassName=\"size-6\"\n\t\t\t\t\t\t\t\t\timage={humanAgent?.image}\n\t\t\t\t\t\t\t\t\tname={humanAgent?.name || \"Support\"}\n\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t</TimelineItemGroupAvatar>\n\t\t\t\t\t)}\n\n\t\t\t\t\t<TimelineItemGroupContent\n\t\t\t\t\t\tclassName={cn(\"flex flex-col gap-1\", isSentByViewer && \"items-end\")}\n\t\t\t\t\t>\n\t\t\t\t\t\t{/* Header - show sender name for received messages (agents) */}\n\t\t\t\t\t\t{isReceivedByViewer && (\n\t\t\t\t\t\t\t<TimelineItemGroupHeader className=\"px-1 text-co-muted-foreground text-xs\">\n\t\t\t\t\t\t\t\t{isAI\n\t\t\t\t\t\t\t\t\t? aiAgent?.name || \"AI Assistant\"\n\t\t\t\t\t\t\t\t\t: humanAgent?.name || \"Support\"}\n\t\t\t\t\t\t\t</TimelineItemGroupHeader>\n\t\t\t\t\t\t)}\n\n\t\t\t\t\t\t{items.map((item, index) => (\n\t\t\t\t\t\t\t<motion.div key={item.id} {...MESSAGE_ANIMATION}>\n\t\t\t\t\t\t\t\t<TimelineMessageItem\n\t\t\t\t\t\t\t\t\tisLast={index === items.length - 1}\n\t\t\t\t\t\t\t\t\tisSentByViewer={isSentByViewer}\n\t\t\t\t\t\t\t\t\titem={item}\n\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t</motion.div>\n\t\t\t\t\t\t))}\n\n\t\t\t\t\t\t{isSentByViewer && (\n\t\t\t\t\t\t\t<div className={cn(\"\", hasSeenIndicator && \"mt-2\")}>\n\t\t\t\t\t\t\t\t<div className=\"min-h-[1.25rem]\">\n\t\t\t\t\t\t\t\t\t{hasSeenIndicator && (\n\t\t\t\t\t\t\t\t\t\t<motion.div key=\"seen-indicator\" {...SEEN_ANIMATION}>\n\t\t\t\t\t\t\t\t\t\t\t<TimelineItemGroupSeenIndicator\n\t\t\t\t\t\t\t\t\t\t\t\tclassName=\"px-1 text-co-muted-foreground text-xs\"\n\t\t\t\t\t\t\t\t\t\t\t\tseenByIds={seenByIds}\n\t\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t\t{() =>\n\t\t\t\t\t\t\t\t\t\t\t\t\tseenByNames.length > 0\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t? `Seen by ${seenByNames.join(\", \")}`\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t: \"Seen\"\n\t\t\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t\t\t</TimelineItemGroupSeenIndicator>\n\t\t\t\t\t\t\t\t\t\t</motion.div>\n\t\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t)}\n\t\t\t\t\t</TimelineItemGroupContent>\n\t\t\t\t</div>\n\t\t\t)}\n\t\t</PrimitiveTimelineItemGroup>\n\t);\n};\n\nTimelineMessageGroup.displayName = \"TimelineMessageGroup\";\n"],"mappings":";;;;;;;;;;AAiBA,MAAM,oBAAoB;CACzB,SAAS;EAAE,SAAS;EAAG,GAAG;EAAG;CAC7B,SAAS;EAAE,SAAS;EAAG,GAAG;EAAG;CAC7B,MAAM,EAAE,SAAS,GAAG;CACpB,YAAY;EACX,UAAU;EACV,MAAM;GAAC;GAAM;GAAM;GAAM;GAAK;EAC9B;CACD;AAED,MAAM,iBAAiB;CACtB,SAAS,EAAE,SAAS,GAAG;CACvB,SAAS,EAAE,SAAS,GAAG;CACvB,YAAY;EACX,UAAU;EACV,MAAM;EACN;CACD;AAWD,MAAMA,oBAAuC,OAAO,OAAO,EAAE,CAAC;AAC9D,MAAMC,sBAAyC,OAAO,OAAO,EAAE,CAAC;AAEhE,MAAaC,wBAA6D,EACzE,OACA,mBACA,sBACA,kBACA,YAAY,mBACZ,cAAc,0BACT;CAEL,MAAM,YAAY,MAAM;CACxB,MAAM,aAAa,qBAAqB,MACtC,UAAU,MAAM,OAAO,WAAW,OACnC;CACD,MAAM,UAAU,kBAAkB,MAChC,UAAU,MAAM,OAAO,WAAW,UACnC;AAED,KAAI,MAAM,WAAW,EACpB,QAAO;CAGR,MAAM,mBAAmB,UAAU,SAAS;AAE5C,QACC,oBAACC;EACO;EACI;EACX,UAAU;EACV,YAAY,WAAW;aAErB,EACD,gBACA,oBACA,WACA,MACA,mBAEA,qBAAC;GACA,WAAW,GACV,qBAGA,kBAAkB,oBAClB,sBAAsB,WACtB;cAGA,sBACA,oBAAC;IAAwB,WAAU;cACjC,OACA,oBAAC;KAAI,WAAU;eACd,oBAAC,kBAAe,WAAU,4BAA4B;MACjD,GAEN,oBAAC;KACA,WAAU;KACV,OAAO,YAAY;KACnB,MAAM,YAAY,QAAQ;MACzB;KAEsB,EAG3B,qBAAC;IACA,WAAW,GAAG,uBAAuB,kBAAkB,YAAY;;KAGlE,sBACA,oBAAC;MAAwB,WAAU;gBACjC,OACE,SAAS,QAAQ,iBACjB,YAAY,QAAQ;OACE;KAG1B,MAAM,KAAK,MAAM,UACjB,oBAAC,OAAO;MAAkB,GAAI;gBAC7B,oBAAC;OACA,QAAQ,UAAU,MAAM,SAAS;OACjB;OACV;QACL;QALc,KAAK,GAMT,CACZ;KAED,kBACA,oBAAC;MAAI,WAAW,GAAG,IAAI,oBAAoB,OAAO;gBACjD,oBAAC;OAAI,WAAU;iBACb,oBACA,oBAAC,OAAO;QAAyB,GAAI;kBACpC,oBAAC;SACA,WAAU;SACC;yBAGV,YAAY,SAAS,IAClB,WAAW,YAAY,KAAK,KAAK,KACjC;UAE4B;UAVlB,iBAWH;QAET;OACD;;KAEmB;IACtB;GAEqB;;AAI/B,qBAAqB,cAAc"}
@@ -17,9 +17,9 @@ function TimelineMessageItem({ item, isLast = false, isSentByViewer = false }) {
17
17
  return /* @__PURE__ */ jsx("div", {
18
18
  className: cn("flex w-full gap-2", isSentByViewerFinal && "flex-row-reverse", !isSentByViewerFinal && "flex-row"),
19
19
  children: /* @__PURE__ */ jsxs("div", {
20
- className: cn("flex w-full flex-1 flex-col gap-1", isSentByViewerFinal && "items-end"),
20
+ className: cn("flex w-full min-w-0 flex-1 flex-col gap-1", isSentByViewerFinal && "items-end"),
21
21
  children: [/* @__PURE__ */ jsx(TimelineItemContent, {
22
- className: cn("block w-max max-w-[300px] rounded-lg px-3.5 py-2.5 text-sm", {
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
23
  "bg-co-background-300 text-co-foreground dark:bg-co-background-600": !isSentByViewerFinal,
24
24
  "bg-co-primary text-co-primary-foreground": isSentByViewerFinal,
25
25
  "rounded-br-sm": isLast && isSentByViewerFinal,
@@ -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 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 w-max max-w-[300px] 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,qCACA,uBAAuB,YACvB;gBAED,oBAAC;MACA,WAAW,GACV,8DACA;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"],"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"}