@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.
- package/conversation.d.ts +28 -0
- package/conversation.d.ts.map +1 -1
- package/hooks/index.d.ts +4 -1
- package/hooks/index.js +4 -1
- package/hooks/private/use-grouped-messages.d.ts.map +1 -1
- package/hooks/private/use-grouped-messages.js +3 -17
- package/hooks/private/use-grouped-messages.js.map +1 -1
- package/hooks/private/use-multimodal-input.d.ts.map +1 -1
- package/hooks/private/use-multimodal-input.js +16 -3
- package/hooks/private/use-multimodal-input.js.map +1 -1
- package/hooks/use-composer-refocus.js +1 -1
- package/hooks/use-composer-refocus.js.map +1 -1
- package/hooks/use-conversation-auto-seen.d.ts +1 -1
- package/hooks/use-conversation-auto-seen.js +30 -46
- package/hooks/use-conversation-auto-seen.js.map +1 -1
- package/hooks/use-conversation-seen.d.ts.map +1 -1
- package/hooks/use-conversation-seen.js +7 -3
- package/hooks/use-conversation-seen.js.map +1 -1
- package/hooks/use-new-message-sound.d.ts +23 -0
- package/hooks/use-new-message-sound.d.ts.map +1 -0
- package/hooks/use-new-message-sound.js +34 -0
- package/hooks/use-new-message-sound.js.map +1 -0
- package/hooks/use-sound-effect.d.ts +30 -0
- package/hooks/use-sound-effect.d.ts.map +1 -0
- package/hooks/use-sound-effect.js +104 -0
- package/hooks/use-sound-effect.js.map +1 -0
- package/hooks/use-typing-sound.d.ts +18 -0
- package/hooks/use-typing-sound.d.ts.map +1 -0
- package/hooks/use-typing-sound.js +38 -0
- package/hooks/use-typing-sound.js.map +1 -0
- package/index.d.ts +5 -2
- package/index.js +8 -6
- package/package.json +3 -3
- package/primitives/avatar/image.d.ts +1 -1
- package/primitives/bubble.js +1 -1
- package/primitives/index.d.ts +3 -5
- package/primitives/index.js +3 -9
- package/primitives/index.parts.d.ts +2 -4
- package/primitives/index.parts.js +2 -4
- package/primitives/router.d.ts +19 -20
- package/primitives/router.d.ts.map +1 -1
- package/primitives/router.js +17 -11
- package/primitives/router.js.map +1 -1
- package/realtime/index.js +1 -1
- package/realtime/provider.js +1 -1
- package/realtime-events.d.ts +83 -0
- package/realtime-events.d.ts.map +1 -1
- package/schemas3.d.ts +7 -0
- package/schemas3.d.ts.map +1 -1
- package/support/components/bubble.d.ts.map +1 -1
- package/support/components/bubble.js +27 -4
- package/support/components/bubble.js.map +1 -1
- package/support/components/button.d.ts +1 -1
- package/support/components/conversation-event.js +1 -1
- package/support/components/conversation-event.js.map +1 -1
- package/support/components/conversation-timeline.d.ts.map +1 -1
- package/support/components/conversation-timeline.js +5 -0
- package/support/components/conversation-timeline.js.map +1 -1
- package/support/components/multimodal-input.d.ts.map +1 -1
- package/support/components/multimodal-input.js +6 -2
- package/support/components/multimodal-input.js.map +1 -1
- package/support/components/support-content.d.ts +2 -0
- package/support/components/support-content.d.ts.map +1 -1
- package/support/components/support-content.js +5 -2
- package/support/components/support-content.js.map +1 -1
- package/support/components/timeline-message-group.js +2 -2
- package/support/components/timeline-message-group.js.map +1 -1
- package/support/components/timeline-message-item.js +2 -2
- package/support/components/timeline-message-item.js.map +1 -1
- package/support/index.d.ts +12 -7
- package/support/index.d.ts.map +1 -1
- package/support/index.js +28 -29
- package/support/index.js.map +1 -1
- package/support/pages/conversation.d.ts.map +1 -1
- package/support/pages/conversation.js +19 -1
- package/support/pages/conversation.js.map +1 -1
- package/support/router.d.ts +19 -9
- package/support/router.d.ts.map +1 -1
- package/support/router.js +31 -30
- package/support/router.js.map +1 -1
- package/timeline-item.d.ts +14 -0
- package/timeline-item.d.ts.map +1 -1
- package/utils/use-render-element.d.ts.map +1 -1
- package/primitives/page-registry.d.ts +0 -30
- package/primitives/page-registry.d.ts.map +0 -1
- package/primitives/page-registry.js +0 -45
- package/primitives/page-registry.js.map +0 -1
- package/primitives/page.d.ts +0 -21
- package/primitives/page.d.ts.map +0 -1
- package/primitives/page.js +0 -18
- package/primitives/page.js.map +0 -1
package/realtime/provider.js
CHANGED
|
@@ -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;
|
package/realtime-events.d.ts
CHANGED
|
@@ -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>;
|
package/realtime-events.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"realtime-events.d.ts","names":[],"sources":["../../types/src/realtime-events.ts"],"sourcesContent":[],"mappings":";;;;;;;;;;cAiBa
|
|
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
|
|
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":";;;
|
|
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
|
|
13
|
-
|
|
14
|
-
|
|
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"
|
|
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?: "
|
|
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
|
|
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
|
|
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":";;;;;
|
|
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":"
|
|
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,
|
|
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 = !
|
|
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
|
|
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 = !
|
|
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":"
|
|
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, {
|
|
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":";;;;;;;;;;;;;;
|
|
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
|
|
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
|
|
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-
|
|
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-
|
|
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"}
|