@cossistant/react 0.0.30 → 0.0.32
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/README.md +3 -1
- package/hooks/private/use-grouped-messages.d.ts.map +1 -1
- package/hooks/private/use-grouped-messages.js +41 -12
- package/hooks/private/use-grouped-messages.js.map +1 -1
- package/hooks/use-conversation-page.js +4 -2
- package/hooks/use-conversation-page.js.map +1 -1
- package/hooks/use-conversation-preview.d.ts.map +1 -1
- package/hooks/use-conversation-preview.js +2 -1
- package/hooks/use-conversation-preview.js.map +1 -1
- package/hooks/use-send-message.js +1 -1
- package/hooks/use-send-message.js.map +1 -1
- package/package.json +5 -4
- package/packages/types/src/api/conversation.d.ts +13 -3
- package/packages/types/src/api/conversation.d.ts.map +1 -1
- package/packages/types/src/api/timeline-item.d.ts +2 -0
- package/packages/types/src/api/timeline-item.d.ts.map +1 -1
- package/packages/types/src/realtime-events.d.ts +29 -4
- package/packages/types/src/realtime-events.d.ts.map +1 -1
- package/packages/types/src/schemas.d.ts +4 -1
- package/packages/types/src/schemas.d.ts.map +1 -1
- package/primitives/avatar/image.d.ts +1 -1
- package/primitives/multimodal-input.d.ts +2 -2
- package/primitives/multimodal-input.d.ts.map +1 -1
- package/primitives/timeline-item.d.ts +2 -2
- package/primitives/timeline-item.d.ts.map +1 -1
- package/primitives/timeline-item.js +30 -9
- package/primitives/timeline-item.js.map +1 -1
- package/provider.d.ts.map +1 -1
- package/provider.js +6 -3
- package/provider.js.map +1 -1
- package/support/components/avatar-stack.js +1 -1
- package/support/components/avatar-stack.js.map +1 -1
- package/support/components/avatar.d.ts +1 -2
- package/support/components/avatar.d.ts.map +1 -1
- package/support/components/avatar.js +9 -7
- package/support/components/avatar.js.map +1 -1
- package/support/components/button.d.ts +1 -1
- package/support/components/conversation-button-link.js +2 -1
- package/support/components/conversation-button-link.js.map +1 -1
- package/support/components/conversation-event.js +1 -1
- package/support/components/conversation-event.js.map +1 -1
- package/support/components/conversation-resolved-feedback.d.ts +21 -0
- package/support/components/conversation-resolved-feedback.d.ts.map +1 -0
- package/support/components/conversation-resolved-feedback.js +102 -0
- package/support/components/conversation-resolved-feedback.js.map +1 -0
- package/support/components/conversation-timeline-utils.d.ts +5 -0
- package/support/components/conversation-timeline-utils.d.ts.map +1 -0
- package/support/components/conversation-timeline-utils.js +10 -0
- package/support/components/conversation-timeline-utils.js.map +1 -0
- package/support/components/conversation-timeline.d.ts.map +1 -1
- package/support/components/conversation-timeline.js +2 -1
- package/support/components/conversation-timeline.js.map +1 -1
- package/support/components/icons.d.ts +1 -1
- package/support/components/icons.d.ts.map +1 -1
- package/support/components/icons.js +6 -2
- package/support/components/icons.js.map +1 -1
- package/support/components/index.d.ts +2 -1
- package/support/components/index.js +2 -1
- package/support/components/typing-indicator.d.ts.map +1 -1
- package/support/components/typing-indicator.js +15 -7
- package/support/components/typing-indicator.js.map +1 -1
- package/support/pages/conversation-history.js +1 -1
- package/support/pages/conversation.d.ts.map +1 -1
- package/support/pages/conversation.js +36 -8
- package/support/pages/conversation.js.map +1 -1
- package/support/pages/home.js +1 -1
- package/support/text/locales/en.js +12 -0
- package/support/text/locales/en.js.map +1 -1
- package/support/text/locales/es.js +12 -0
- package/support/text/locales/es.js.map +1 -1
- package/support/text/locales/fr.js +12 -0
- package/support/text/locales/fr.js.map +1 -1
- package/support/text/locales/keys.d.ts +20 -0
- package/support/text/locales/keys.d.ts.map +1 -1
- package/support/text/locales/keys.js +6 -0
- package/support/text/locales/keys.js.map +1 -1
- package/utils/use-render-element.d.ts.map +1 -1
|
@@ -92,6 +92,7 @@ declare const realtimeSchema: {
|
|
|
92
92
|
message: "message";
|
|
93
93
|
event: "event";
|
|
94
94
|
identification: "identification";
|
|
95
|
+
tool: "tool";
|
|
95
96
|
}>;
|
|
96
97
|
text: ZodNullable<ZodString>;
|
|
97
98
|
parts: ZodArray<ZodUnknown>;
|
|
@@ -117,10 +118,12 @@ declare const realtimeSchema: {
|
|
|
117
118
|
visitorId: ZodString;
|
|
118
119
|
websiteId: ZodString;
|
|
119
120
|
status: ZodDefault<ZodEnum<{
|
|
120
|
-
open: "open";
|
|
121
121
|
resolved: "resolved";
|
|
122
|
+
open: "open";
|
|
122
123
|
spam: "spam";
|
|
123
124
|
}>>;
|
|
125
|
+
visitorRating: ZodOptional<ZodNullable<ZodNumber>>;
|
|
126
|
+
visitorRatingAt: ZodOptional<ZodNullable<ZodString>>;
|
|
124
127
|
deletedAt: ZodDefault<ZodNullable<ZodString>>;
|
|
125
128
|
visitorLastSeenAt: ZodOptional<ZodNullable<ZodString>>;
|
|
126
129
|
lastTimelineItem: ZodOptional<ZodObject<{
|
|
@@ -135,6 +138,7 @@ declare const realtimeSchema: {
|
|
|
135
138
|
message: "message";
|
|
136
139
|
event: "event";
|
|
137
140
|
identification: "identification";
|
|
141
|
+
tool: "tool";
|
|
138
142
|
}>;
|
|
139
143
|
text: ZodNullable<ZodString>;
|
|
140
144
|
tool: ZodOptional<ZodNullable<ZodString>>;
|
|
@@ -272,14 +276,14 @@ declare const realtimeSchema: {
|
|
|
272
276
|
header: ZodObject<{
|
|
273
277
|
id: ZodString;
|
|
274
278
|
status: ZodEnum<{
|
|
275
|
-
open: "open";
|
|
276
279
|
resolved: "resolved";
|
|
280
|
+
open: "open";
|
|
277
281
|
spam: "spam";
|
|
278
282
|
}>;
|
|
279
283
|
priority: ZodEnum<{
|
|
280
|
-
normal: "normal";
|
|
281
284
|
high: "high";
|
|
282
285
|
low: "low";
|
|
286
|
+
normal: "normal";
|
|
283
287
|
urgent: "urgent";
|
|
284
288
|
}>;
|
|
285
289
|
organizationId: ZodString;
|
|
@@ -302,6 +306,8 @@ declare const realtimeSchema: {
|
|
|
302
306
|
channel: ZodString;
|
|
303
307
|
title: ZodNullable<ZodString>;
|
|
304
308
|
resolutionTime: ZodNullable<ZodNumber>;
|
|
309
|
+
visitorRating: ZodNullable<ZodNumber>;
|
|
310
|
+
visitorRatingAt: ZodNullable<ZodString>;
|
|
305
311
|
startedAt: ZodNullable<ZodString>;
|
|
306
312
|
firstResponseAt: ZodNullable<ZodString>;
|
|
307
313
|
resolvedAt: ZodNullable<ZodString>;
|
|
@@ -330,6 +336,7 @@ declare const realtimeSchema: {
|
|
|
330
336
|
message: "message";
|
|
331
337
|
event: "event";
|
|
332
338
|
identification: "identification";
|
|
339
|
+
tool: "tool";
|
|
333
340
|
}>;
|
|
334
341
|
text: ZodNullable<ZodString>;
|
|
335
342
|
tool: ZodOptional<ZodNullable<ZodString>>;
|
|
@@ -475,6 +482,7 @@ declare const realtimeSchema: {
|
|
|
475
482
|
message: "message";
|
|
476
483
|
event: "event";
|
|
477
484
|
identification: "identification";
|
|
485
|
+
tool: "tool";
|
|
478
486
|
}>;
|
|
479
487
|
text: ZodNullable<ZodString>;
|
|
480
488
|
tool: ZodOptional<ZodNullable<ZodString>>;
|
|
@@ -724,6 +732,22 @@ declare const realtimeSchema: {
|
|
|
724
732
|
sentimentConfidence: ZodOptional<ZodNullable<ZodNumber>>;
|
|
725
733
|
escalatedAt: ZodOptional<ZodNullable<ZodString>>;
|
|
726
734
|
escalationReason: ZodOptional<ZodNullable<ZodString>>;
|
|
735
|
+
status: ZodOptional<ZodEnum<{
|
|
736
|
+
resolved: "resolved";
|
|
737
|
+
open: "open";
|
|
738
|
+
spam: "spam";
|
|
739
|
+
}>>;
|
|
740
|
+
priority: ZodOptional<ZodEnum<{
|
|
741
|
+
high: "high";
|
|
742
|
+
low: "low";
|
|
743
|
+
normal: "normal";
|
|
744
|
+
urgent: "urgent";
|
|
745
|
+
}>>;
|
|
746
|
+
resolvedAt: ZodOptional<ZodNullable<ZodString>>;
|
|
747
|
+
resolvedByUserId: ZodOptional<ZodNullable<ZodString>>;
|
|
748
|
+
resolvedByAiAgentId: ZodOptional<ZodNullable<ZodString>>;
|
|
749
|
+
resolutionTime: ZodOptional<ZodNullable<ZodNumber>>;
|
|
750
|
+
deletedAt: ZodOptional<ZodNullable<ZodString>>;
|
|
727
751
|
}, $strip>;
|
|
728
752
|
aiAgentId: ZodNullable<ZodString>;
|
|
729
753
|
}, $strip>;
|
|
@@ -795,8 +819,8 @@ declare const realtimeSchema: {
|
|
|
795
819
|
aiAgentId: ZodString;
|
|
796
820
|
workflowRunId: ZodString;
|
|
797
821
|
status: ZodEnum<{
|
|
798
|
-
error: "error";
|
|
799
822
|
success: "success";
|
|
823
|
+
error: "error";
|
|
800
824
|
skipped: "skipped";
|
|
801
825
|
cancelled: "cancelled";
|
|
802
826
|
}>;
|
|
@@ -825,6 +849,7 @@ declare const realtimeSchema: {
|
|
|
825
849
|
message: "message";
|
|
826
850
|
event: "event";
|
|
827
851
|
identification: "identification";
|
|
852
|
+
tool: "tool";
|
|
828
853
|
}>;
|
|
829
854
|
text: ZodNullable<ZodString>;
|
|
830
855
|
parts: ZodArray<ZodUnknown>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"realtime-events.d.ts","names":[],"sources":["../../../../../types/src/realtime-events.ts"],"sourcesContent":[],"mappings":";;;;;;;;;;
|
|
1
|
+
{"version":3,"file":"realtime-events.d.ts","names":[],"sources":["../../../../../types/src/realtime-events.ts"],"sourcesContent":[],"mappings":";;;;;;;;;;cAsBa;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;KA6WD,iBAAA,gBAAiC;KAEjC,+BAA+B,qBAAqB,eACvD,gBAAgB;KAGb,wBAAwB;QAC7B;WACG,qBAAqB;;KAGnB,gBAAA,WACL,oBAAoB,cAAc,KACvC;KAEU,4BAA4B,qBACvC,qBAAqB"}
|
|
@@ -12,10 +12,12 @@ declare const conversationSchema: ZodObject<{
|
|
|
12
12
|
visitorId: ZodString;
|
|
13
13
|
websiteId: ZodString;
|
|
14
14
|
status: ZodDefault<ZodEnum<{
|
|
15
|
-
open: "open";
|
|
16
15
|
resolved: "resolved";
|
|
16
|
+
open: "open";
|
|
17
17
|
spam: "spam";
|
|
18
18
|
}>>;
|
|
19
|
+
visitorRating: ZodOptional<ZodNullable<ZodNumber>>;
|
|
20
|
+
visitorRatingAt: ZodOptional<ZodNullable<ZodString>>;
|
|
19
21
|
deletedAt: ZodDefault<ZodNullable<ZodString>>;
|
|
20
22
|
visitorLastSeenAt: ZodOptional<ZodNullable<ZodString>>;
|
|
21
23
|
lastTimelineItem: ZodOptional<ZodObject<{
|
|
@@ -30,6 +32,7 @@ declare const conversationSchema: ZodObject<{
|
|
|
30
32
|
message: "message";
|
|
31
33
|
event: "event";
|
|
32
34
|
identification: "identification";
|
|
35
|
+
tool: "tool";
|
|
33
36
|
}>;
|
|
34
37
|
text: ZodNullable<ZodString>;
|
|
35
38
|
tool: ZodOptional<ZodNullable<ZodString>>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"schemas.d.ts","names":[],"sources":["../../../../../types/src/schemas.ts"],"sourcesContent":[],"mappings":";;;;;;cAkBa,oBAAkB
|
|
1
|
+
{"version":3,"file":"schemas.d.ts","names":[],"sources":["../../../../../types/src/schemas.ts"],"sourcesContent":[],"mappings":";;;;;;cAkBa,oBAAkB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;KAqBnB,YAAA,GAAe,cAAe;cAE7B,wBAAsB;;;;;;;;;EAvBJ,SAAA,aAAA,UAAA,CAAA;CAAA,QAAA,CAAA;AAqBnB,KAcA,gBAAA,GAAmB,MAdW,CAAA,OAcI,sBAdZ,CAAA"}
|
|
@@ -15,7 +15,7 @@ type AvatarImageProps = Omit<React$1.ImgHTMLAttributes<HTMLImageElement>, "src"
|
|
|
15
15
|
* Controlled `<img>` that syncs its loading status back to the avatar context
|
|
16
16
|
* so fallbacks know when to display.
|
|
17
17
|
*/
|
|
18
|
-
declare const AvatarImage: React$1.ForwardRefExoticComponent<Omit<React$1.ImgHTMLAttributes<HTMLImageElement>, "
|
|
18
|
+
declare const AvatarImage: React$1.ForwardRefExoticComponent<Omit<React$1.ImgHTMLAttributes<HTMLImageElement>, "src" | "alt"> & {
|
|
19
19
|
src: string;
|
|
20
20
|
alt?: string;
|
|
21
21
|
asChild?: boolean;
|
|
@@ -16,7 +16,7 @@ type MultimodalInputProps = Omit<React$1.TextareaHTMLAttributes<HTMLTextAreaElem
|
|
|
16
16
|
* clipboard uploads and auto-resizing while remaining composable via
|
|
17
17
|
* `asChild`.
|
|
18
18
|
*/
|
|
19
|
-
declare const MultimodalInput: React$1.ForwardRefExoticComponent<Omit<React$1.TextareaHTMLAttributes<HTMLTextAreaElement>, "
|
|
19
|
+
declare const MultimodalInput: React$1.ForwardRefExoticComponent<Omit<React$1.TextareaHTMLAttributes<HTMLTextAreaElement>, "onChange" | "value"> & {
|
|
20
20
|
value: string;
|
|
21
21
|
onChange: (value: string) => void;
|
|
22
22
|
onSubmit?: () => void;
|
|
@@ -38,7 +38,7 @@ declare const FileInput: React$1.ForwardRefExoticComponent<React$1.InputHTMLAttr
|
|
|
38
38
|
onFileSelect?: (files: File[]) => void;
|
|
39
39
|
asChild?: boolean;
|
|
40
40
|
} & React$1.RefAttributes<HTMLInputElement>>;
|
|
41
|
-
declare const SupportInput: React$1.ForwardRefExoticComponent<Omit<React$1.TextareaHTMLAttributes<HTMLTextAreaElement>, "
|
|
41
|
+
declare const SupportInput: React$1.ForwardRefExoticComponent<Omit<React$1.TextareaHTMLAttributes<HTMLTextAreaElement>, "onChange" | "value"> & {
|
|
42
42
|
value: string;
|
|
43
43
|
onChange: (value: string) => void;
|
|
44
44
|
onSubmit?: () => void;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"multimodal-input.d.ts","names":[],"sources":["../../src/primitives/multimodal-input.tsx"],"sourcesContent":[],"mappings":";;;KAGY,oBAAA,GAAuB,KAClC,OAAA,CAAM,uBAAuB;;EADlB,QAAA,EAAA,CAAA,KAAA,EAAA,MAAoB,EAAA,GAAA,IAAA;EACF,QAAA,CAAA,EAAA,GAAA,GAAA,IAAA;EAA7B,YAAM,CAAA,EAAA,CAAA,KAAA,EAMiB,IANjB,EAAA,EAAA,GAAA,IAAA;EAD4B,OAAA,CAAA,EAAA,OAAA;EAOX,SAAA,CAAA,EAAA,MAAA;EAGf,KAAA,CAAA,EAAA,KAAA,GAAA,IAAA;EAAK,QAAA,CAAA,EAAA,OAAA;AASd,CAAA;;;;;;cAAa,iBAAe,OAAA,CAAA,0BAAA,KAAA,OAAA,CAAA,uBAAA;;EAAA,QAAA,EAAA,CAAA,KAAA,EAAA,MAAA,EAAA,GAAA,IAAA;EAAA,QAAA,CAAA,EAAA,GAAA,GAAA,IAAA;EA4GhB,YAAA,CAAA,EAAA,CAAA,KAAc,EAxHF,IAwHE,EAAA,EAAA,GAAA,IAAA;EAA6B,OAAA,CAAA,EAAA,OAAA;EAA1B,SAAM,CAAA,EAAA,MAAA;EACX,KAAA,CAAA,EAtHf,KAsHe,GAAA,IAAA;EAAI,QAAA,CAAA,EAAA,OAAA;AAQ5B,CAAA,wBAkCI,oBAAA,CAAA,CAAA;AAlCkB,KATV,cAAA,GAAiB,OAAA,CAAM,mBASb,CATiC,gBASjC,CAAA,GAAA;EAAA,YAAA,CAAA,EAAA,CAAA,KAAA,EARE,IAQF,EAAA,EAAA,GAAA,IAAA;EARE,OAAA,CAAA,EAAA,OAAA;;;;;AA6CxB;AAAyB,cArCZ,SAqCY,EArCH,OAAA,CAAA,yBAqCG,CArCH,OAAA,CAAA,mBAqCG,CArCH,gBAqCG,CAAA,GAAA;EAAA,YAAA,CAAA,EAAA,CAAA,KAAA,EA7CD,IA6CC,EAAA,EAAA,GAAA,IAAA;EAAA,OAAA,CAAA,EAAA,OAAA;CAtKD,wBAAA,iBAAA,CAAA,CAAA;AAGf,cAmKI,YAnKJ,EAmKgB,OAAA,CAAA,yBAnKhB,CAmKgB,IAnKhB,CAmKgB,OAAA,CAAA,sBAnKhB,CAmKgB,mBAnKhB,CAAA,EAAA,
|
|
1
|
+
{"version":3,"file":"multimodal-input.d.ts","names":[],"sources":["../../src/primitives/multimodal-input.tsx"],"sourcesContent":[],"mappings":";;;KAGY,oBAAA,GAAuB,KAClC,OAAA,CAAM,uBAAuB;;EADlB,QAAA,EAAA,CAAA,KAAA,EAAA,MAAoB,EAAA,GAAA,IAAA;EACF,QAAA,CAAA,EAAA,GAAA,GAAA,IAAA;EAA7B,YAAM,CAAA,EAAA,CAAA,KAAA,EAMiB,IANjB,EAAA,EAAA,GAAA,IAAA;EAD4B,OAAA,CAAA,EAAA,OAAA;EAOX,SAAA,CAAA,EAAA,MAAA;EAGf,KAAA,CAAA,EAAA,KAAA,GAAA,IAAA;EAAK,QAAA,CAAA,EAAA,OAAA;AASd,CAAA;;;;;;cAAa,iBAAe,OAAA,CAAA,0BAAA,KAAA,OAAA,CAAA,uBAAA;;EAAA,QAAA,EAAA,CAAA,KAAA,EAAA,MAAA,EAAA,GAAA,IAAA;EAAA,QAAA,CAAA,EAAA,GAAA,GAAA,IAAA;EA4GhB,YAAA,CAAA,EAAA,CAAA,KAAc,EAxHF,IAwHE,EAAA,EAAA,GAAA,IAAA;EAA6B,OAAA,CAAA,EAAA,OAAA;EAA1B,SAAM,CAAA,EAAA,MAAA;EACX,KAAA,CAAA,EAtHf,KAsHe,GAAA,IAAA;EAAI,QAAA,CAAA,EAAA,OAAA;AAQ5B,CAAA,wBAkCI,oBAAA,CAAA,CAAA;AAlCkB,KATV,cAAA,GAAiB,OAAA,CAAM,mBASb,CATiC,gBASjC,CAAA,GAAA;EAAA,YAAA,CAAA,EAAA,CAAA,KAAA,EARE,IAQF,EAAA,EAAA,GAAA,IAAA;EARE,OAAA,CAAA,EAAA,OAAA;;;;;AA6CxB;AAAyB,cArCZ,SAqCY,EArCH,OAAA,CAAA,yBAqCG,CArCH,OAAA,CAAA,mBAqCG,CArCH,gBAqCG,CAAA,GAAA;EAAA,YAAA,CAAA,EAAA,CAAA,KAAA,EA7CD,IA6CC,EAAA,EAAA,GAAA,IAAA;EAAA,OAAA,CAAA,EAAA,OAAA;CAtKD,wBAAA,iBAAA,CAAA,CAAA;AAGf,cAmKI,YAnKJ,EAmKgB,OAAA,CAAA,yBAnKhB,CAmKgB,IAnKhB,CAmKgB,OAAA,CAAA,sBAnKhB,CAmKgB,mBAnKhB,CAAA,EAAA,UAAA,GAAA,OAAA,CAAA,GAAA;;;EAmKgB,QAAA,CAAA,EAAA,GAAA,GAAA,IAAA;EAAA,YAAA,CAAA,EAAA,CAAA,KAAA,EAtKD,IAsKC,EAAA,EAAA,GAAA,IAAA;;;UAnKhB"}
|
|
@@ -14,7 +14,7 @@ type TimelineItemRenderProps = {
|
|
|
14
14
|
timestamp: Date;
|
|
15
15
|
text: string | null;
|
|
16
16
|
senderType: "visitor" | "ai" | "human";
|
|
17
|
-
itemType: "message" | "event" | "identification";
|
|
17
|
+
itemType: "message" | "event" | "identification" | "tool";
|
|
18
18
|
};
|
|
19
19
|
type TimelineItemProps = Omit<React$1.HTMLAttributes<HTMLDivElement>, "children"> & {
|
|
20
20
|
children?: React$1.ReactNode | ((props: TimelineItemRenderProps) => React$1.ReactNode);
|
|
@@ -25,7 +25,7 @@ type TimelineItemProps = Omit<React$1.HTMLAttributes<HTMLDivElement>, "children"
|
|
|
25
25
|
/**
|
|
26
26
|
* Generic timeline item wrapper that adds accessibility attributes and resolves the
|
|
27
27
|
* sender type into convenient render props for custom layouts. Works with
|
|
28
|
-
*
|
|
28
|
+
* message, event, identification, and tool timeline item types.
|
|
29
29
|
*/
|
|
30
30
|
declare const TimelineItem: React$1.ForwardRefExoticComponent<Omit<React$1.HTMLAttributes<HTMLDivElement>, "children"> & {
|
|
31
31
|
children?: React$1.ReactNode | ((props: TimelineItemRenderProps) => React$1.ReactNode);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"timeline-item.d.ts","names":[],"sources":["../../src/primitives/timeline-item.tsx"],"sourcesContent":[],"mappings":";;;;;;;
|
|
1
|
+
{"version":3,"file":"timeline-item.d.ts","names":[],"sources":["../../src/primitives/timeline-item.tsx"],"sourcesContent":[],"mappings":";;;;;;;AAWA;AAUA;AACsB,KAXV,uBAAA,GAWU;EAArB,SAAM,EAAA,OAAA;EADyB,IAAA,EAAA,OAAA;EAK5B,OAAM,EAAA,OAAA;EACG,SAAA,EAZD,IAYC;EAA4B,IAAA,EAAM,MAAA,GAAA,IAAA;EAGxC,UAAA,EAAA,SAAA,GAAA,IAAA,GAAA,OAAA;EAAgB,QAAA,EAAA,SAAA,GAAA,OAAA,GAAA,gBAAA,GAAA,MAAA;AAQvB,CAAA;AAAyB,KAjBb,iBAAA,GAAoB,IAiBP,CAhBxB,OAAA,CAAM,cAgBkB,CAhBH,cAgBG,CAAA,EAAA,UAAA,CAAA,GAAA;EAAA,QAAA,CAAA,EAZrB,OAAA,CAAM,SAYe,GAAA,CAAA,CAAA,KAAA,EAXZ,uBAWY,EAAA,GAXgB,OAAA,CAAM,SAWtB,CAAA;EAAA,OAAA,CAAA,EAAA,OAAA;EAZrB,SAAM,CAAA,EAAA,MAAA;EACG,IAAA,EAGN,cAHM;CAA4B;;;;;;AA6M7B,cAlMC,YAkMuB,EAlMX,OAAA,CAAA,yBAkMW,CAlMX,IAkMW,CAlMX,OAAA,CAAA,cAkMW,CAlMX,cAkMW,CAAA,EAAA,UAAA,CAAA,GAAA;EACd,QAAA,CAAA,EA/MlB,OAAA,CAAM,SA+MY,GAAA,CAAA,CAAA,KAAA,EA9MT,uBA8MS,EAAA,GA9MmB,OAAA,CAAM,SA8MzB,CAAA;EAArB,OAAM,CAAA,EAAA,OAAA;EADgC,SAAA,CAAA,EAAA,MAAA;EAI3B,IAAA,EA9ML,cA8MW;CAAkC,wBAAM,eAAA,CAAA,CAAA;AAAS,KAJvD,wBAAA,GAA2B,IAI4B,CAHlE,OAAA,CAAM,cAG4D,CAH7C,cAG6C,CAAA,EAAA,UAAA,CAAA,GAAA;EAYtD,QAAA,CAAA,EAZD,OAAA,CAAM,SA6Dd,GAAA,CAAA,CAAA,OAAA,EAAA,MAAA,EAAA,GA7DgD,OAAA,CAAM,SA6DtD,CAAA;EAjD4B,OAAA,CAAA,EAAA,OAAA;EAAA,SAAA,CAAA,EAAA,MAAA;EAAA,IAAA,CAAA,EAAA,MAAA,GAAA,IAAA;EAZpB,cAAM,CAAA,EAAA,OAAA;CAAkC;;;;;AA+DpD;AACsB,cApDT,mBAoDS,EApDU,OAAA,CAAA,yBAoDV,CApDU,IAoDV,CApDU,OAAA,CAAA,cAoDV,CApDU,cAoDV,CAAA,EAAA,UAAA,CAAA,GAAA;EAArB,QAAM,CAAA,EAhEK,OAAA,CAAM,SAgEX,GAAA,CAAA,CAAA,OAAA,EAAA,MAAA,EAAA,GAhE6C,OAAA,CAAM,SAgEnD,CAAA;EADkC,OAAA,CAAA,EAAA,OAAA;EAI7B,SAAM,CAAA,EAAA,MAAA;EAAyB,IAAA,CAAA,EAAA,MAAA,GAAA,IAAA;EAAS,cAAM,CAAA,EAAA,OAAA;CAG9C,wBAAA,eAAA,CAAA,CAAA;AACK,KARL,0BAAA,GAA6B,IAQxB,CAPhB,OAAA,CAAM,cAOU,CAPK,eAOL,CAAA,EAAA,UAAA,CAAA,GAAA;EAAI,QAAA,CAAA,EAJT,OAAA,CAAM,SAIG,GAAA,CAAA,CAAA,SAAA,EAJsB,IAItB,EAAA,GAJ+B,OAAA,CAAM,SAIrC,CAAA;EAOR,OAAA,CAAA,EAAA,OAAA;EAAqB,SAAA,CAAA,EAAA,MAAA;EAAA,SAAA,EARtB,IAQsB;EAAA,MAAA,CAAA,EAAA,CAAA,IAAA,EAPjB,IAOiB,EAAA,GAAA,MAAA;CAXtB;;;;;cAWC,uBAAqB,OAAA,CAAA,0BAAA,KAAA,OAAA,CAAA,eAAA;aAXtB,OAAA,CAAM,yBAAyB,SAAS,OAAA,CAAM;EAWxB,OAAA,CAAA,EAAA,OAAA;EAAA,SAAA,CAAA,EAAA,MAAA;aARtB;kBACK"}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { useRenderElement } from "../utils/use-render-element.js";
|
|
2
2
|
import * as React$1 from "react";
|
|
3
3
|
import { jsx } from "react/jsx-runtime";
|
|
4
|
+
import { hasMarkdownFormatting } from "@cossistant/tiny-markdown/utils";
|
|
4
5
|
import ReactMarkdown from "react-markdown";
|
|
5
6
|
import remarkBreaks from "remark-breaks";
|
|
6
7
|
|
|
@@ -8,7 +9,7 @@ import remarkBreaks from "remark-breaks";
|
|
|
8
9
|
/**
|
|
9
10
|
* Generic timeline item wrapper that adds accessibility attributes and resolves the
|
|
10
11
|
* sender type into convenient render props for custom layouts. Works with
|
|
11
|
-
*
|
|
12
|
+
* message, event, identification, and tool timeline item types.
|
|
12
13
|
*/
|
|
13
14
|
const TimelineItem = (() => {
|
|
14
15
|
const Component = React$1.forwardRef(({ children, className, asChild = false, item, ...props }, ref) => {
|
|
@@ -29,6 +30,7 @@ const TimelineItem = (() => {
|
|
|
29
30
|
const itemTypeLabel = (() => {
|
|
30
31
|
if (item.type === "event") return "Event";
|
|
31
32
|
if (item.type === "identification") return "Identification";
|
|
33
|
+
if (item.type === "tool") return "Tool call";
|
|
32
34
|
if (isVisitor) return "visitor";
|
|
33
35
|
if (isAI) return "AI assistant";
|
|
34
36
|
return "human agent";
|
|
@@ -41,7 +43,7 @@ const TimelineItem = (() => {
|
|
|
41
43
|
state: renderProps,
|
|
42
44
|
props: {
|
|
43
45
|
role: "article",
|
|
44
|
-
"aria-label": `${item.type === "message" ? "Message" : "Event"} from ${itemTypeLabel}`,
|
|
46
|
+
"aria-label": `${item.type === "message" ? "Message" : item.type === "tool" ? "Tool call" : "Event"} from ${itemTypeLabel}`,
|
|
45
47
|
...props,
|
|
46
48
|
children: content
|
|
47
49
|
}
|
|
@@ -51,6 +53,10 @@ const TimelineItem = (() => {
|
|
|
51
53
|
return Component;
|
|
52
54
|
})();
|
|
53
55
|
const MemoizedMarkdownBlock = React$1.memo(({ content }) => {
|
|
56
|
+
if (!hasMarkdownFormatting(content)) return /* @__PURE__ */ jsx("span", {
|
|
57
|
+
className: "whitespace-pre-wrap break-words",
|
|
58
|
+
children: content
|
|
59
|
+
});
|
|
54
60
|
return /* @__PURE__ */ jsx(ReactMarkdown, {
|
|
55
61
|
components: {
|
|
56
62
|
p: ({ children }) => {
|
|
@@ -97,15 +103,30 @@ const MemoizedMarkdownBlock = React$1.memo(({ content }) => {
|
|
|
97
103
|
className: "italic",
|
|
98
104
|
children
|
|
99
105
|
}),
|
|
100
|
-
a: ({ href, children }) =>
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
106
|
+
a: ({ href, children, node }) => {
|
|
107
|
+
const rawHref = href || node?.properties?.href || "";
|
|
108
|
+
if (rawHref.startsWith("mention:")) {
|
|
109
|
+
const parts = rawHref.split(":");
|
|
110
|
+
const mentionType = parts[1];
|
|
111
|
+
const mentionId = parts.slice(2).join(":");
|
|
112
|
+
return /* @__PURE__ */ jsx("span", {
|
|
113
|
+
className: "rounded bg-co-orange/15 font-medium text-co-orange",
|
|
114
|
+
"data-mention-id": mentionId,
|
|
115
|
+
"data-mention-type": mentionType,
|
|
116
|
+
children
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
return /* @__PURE__ */ jsx("a", {
|
|
120
|
+
className: "underline hover:opacity-80",
|
|
121
|
+
href,
|
|
122
|
+
rel: "noopener noreferrer",
|
|
123
|
+
target: "_blank",
|
|
124
|
+
children
|
|
125
|
+
});
|
|
126
|
+
}
|
|
107
127
|
},
|
|
108
128
|
remarkPlugins: [remarkBreaks],
|
|
129
|
+
urlTransform: (url) => url,
|
|
109
130
|
children: content
|
|
110
131
|
});
|
|
111
132
|
}, (prevProps, nextProps) => {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"timeline-item.js","names":["React","renderProps: TimelineItemRenderProps"],"sources":["../../src/primitives/timeline-item.tsx"],"sourcesContent":["import type { TimelineItem as TimelineItemType } from \"@cossistant/types/api/timeline-item\";\nimport * as React from \"react\";\nimport ReactMarkdown from \"react-markdown\";\nimport remarkBreaks from \"remark-breaks\";\nimport { useRenderElement } from \"../utils/use-render-element\";\n\n/**\n * Metadata describing the origin of a timeline item and pre-parsed content that can\n * be consumed by render-prop children.\n */\nexport type TimelineItemRenderProps = {\n\tisVisitor: boolean;\n\tisAI: boolean;\n\tisHuman: boolean;\n\ttimestamp: Date;\n\ttext: string | null;\n\tsenderType: \"visitor\" | \"ai\" | \"human\";\n\titemType: \"message\" | \"event\" | \"identification\";\n};\n\nexport type TimelineItemProps = Omit<\n\tReact.HTMLAttributes<HTMLDivElement>,\n\t\"children\"\n> & {\n\tchildren?:\n\t\t| React.ReactNode\n\t\t| ((props: TimelineItemRenderProps) => React.ReactNode);\n\tasChild?: boolean;\n\tclassName?: string;\n\titem: TimelineItemType;\n};\n\n/**\n * Generic timeline item wrapper that adds accessibility attributes and resolves the\n * sender type into convenient render props for custom layouts. Works with\n * both MESSAGE and EVENT timeline item types.\n */\nexport const TimelineItem = (() => {\n\tconst Component = React.forwardRef<HTMLDivElement, TimelineItemProps>(\n\t\t({ children, className, asChild = false, item, ...props }, ref) => {\n\t\t\t// Determine sender type from timeline item properties\n\t\t\tconst isVisitor = item.visitorId !== null;\n\t\t\tconst isAI = item.aiAgentId !== null;\n\t\t\tconst isHuman = item.userId !== null && !isVisitor;\n\n\t\t\tconst senderType = isVisitor ? \"visitor\" : isAI ? \"ai\" : \"human\";\n\n\t\t\tconst renderProps: TimelineItemRenderProps = {\n\t\t\t\tisVisitor,\n\t\t\t\tisAI,\n\t\t\t\tisHuman,\n\t\t\t\ttimestamp: new Date(item.createdAt),\n\t\t\t\ttext: item.text,\n\t\t\t\tsenderType,\n\t\t\t\titemType: item.type,\n\t\t\t};\n\n\t\t\tconst content =\n\t\t\t\ttypeof children === \"function\" ? children(renderProps) : children;\n\n\t\t\tconst itemTypeLabel = (() => {\n\t\t\t\tif (item.type === \"event\") {\n\t\t\t\t\treturn \"Event\";\n\t\t\t\t}\n\t\t\t\tif (item.type === \"identification\") {\n\t\t\t\t\treturn \"Identification\";\n\t\t\t\t}\n\t\t\t\tif (isVisitor) {\n\t\t\t\t\treturn \"visitor\";\n\t\t\t\t}\n\t\t\t\tif (isAI) {\n\t\t\t\t\treturn \"AI assistant\";\n\t\t\t\t}\n\t\t\t\treturn \"human agent\";\n\t\t\t})();\n\n\t\t\treturn useRenderElement(\n\t\t\t\t\"div\",\n\t\t\t\t{\n\t\t\t\t\tclassName,\n\t\t\t\t\tasChild,\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tref,\n\t\t\t\t\tstate: renderProps,\n\t\t\t\t\tprops: {\n\t\t\t\t\t\trole: \"article\",\n\t\t\t\t\t\t\"aria-label\": `${item.type === \"message\" ? \"Message\" : \"Event\"} from ${itemTypeLabel}`,\n\t\t\t\t\t\t...props,\n\t\t\t\t\t\tchildren: content,\n\t\t\t\t\t},\n\t\t\t\t}\n\t\t\t);\n\t\t}\n\t);\n\n\tComponent.displayName = \"TimelineItem\";\n\treturn Component;\n})();\n\nconst MemoizedMarkdownBlock = React.memo(\n\t({ content }: { content: string }) => {\n\t\treturn (\n\t\t\t<ReactMarkdown\n\t\t\t\tcomponents={{\n\t\t\t\t\t// Render paragraphs as block elements to preserve multiline spacing\n\t\t\t\t\tp: ({ children }) => {\n\t\t\t\t\t\t// Skip empty paragraphs (caused by consecutive blank lines in markdown)\n\t\t\t\t\t\tconst isEmpty =\n\t\t\t\t\t\t\tchildren === undefined ||\n\t\t\t\t\t\t\tchildren === null ||\n\t\t\t\t\t\t\tchildren === \"\" ||\n\t\t\t\t\t\t\t(Array.isArray(children) &&\n\t\t\t\t\t\t\t\tchildren.every((c) => c === \"\\n\" || c === \"\" || c == null));\n\t\t\t\t\t\tif (isEmpty) {\n\t\t\t\t\t\t\treturn null;\n\t\t\t\t\t\t}\n\t\t\t\t\t\treturn <span className=\"mt-1 block first:mt-0\">{children}</span>;\n\t\t\t\t\t},\n\t\t\t\t\t// Ensure proper line break handling\n\t\t\t\t\tbr: () => <br />,\n\t\t\t\t\t// Handle code blocks properly\n\t\t\t\t\tcode: ({ children, ...props }) => {\n\t\t\t\t\t\t// Check if it's inline code by looking at the parent element\n\t\t\t\t\t\tconst isInline = !(\n\t\t\t\t\t\t\t\"className\" in props &&\n\t\t\t\t\t\t\ttypeof props.className === \"string\" &&\n\t\t\t\t\t\t\tprops.className.includes(\"language-\")\n\t\t\t\t\t\t);\n\t\t\t\t\t\treturn isInline ? (\n\t\t\t\t\t\t\t<code className=\"rounded bg-co-background-300 px-1 py-0.5 text-xs\">\n\t\t\t\t\t\t\t\t{children}\n\t\t\t\t\t\t\t</code>\n\t\t\t\t\t\t) : (\n\t\t\t\t\t\t\t<pre className=\"overflow-x-auto rounded bg-co-background-300 p-2\">\n\t\t\t\t\t\t\t\t<code className=\"text-xs\">{children}</code>\n\t\t\t\t\t\t\t</pre>\n\t\t\t\t\t\t);\n\t\t\t\t\t},\n\t\t\t\t\t// Handle strong/bold text\n\t\t\t\t\tstrong: ({ children }) => (\n\t\t\t\t\t\t<strong className=\"font-semibold\">{children}</strong>\n\t\t\t\t\t),\n\t\t\t\t\t// Handle ordered lists\n\t\t\t\t\tol: ({ children }) => (\n\t\t\t\t\t\t<ol className=\"my-0 list-decimal pl-6\">{children}</ol>\n\t\t\t\t\t),\n\t\t\t\t\t// Handle unordered lists\n\t\t\t\t\tul: ({ children }) => (\n\t\t\t\t\t\t<ul className=\"my-0 list-disc pl-6\">{children}</ul>\n\t\t\t\t\t),\n\t\t\t\t\t// Handle list items\n\t\t\t\t\tli: ({ children }) => (\n\t\t\t\t\t\t<li className=\"[&>span.block]:mt-0 [&>span.block]:inline\">\n\t\t\t\t\t\t\t{children}\n\t\t\t\t\t\t</li>\n\t\t\t\t\t),\n\t\t\t\t\t// Handle blockquotes\n\t\t\t\t\tblockquote: ({ children }) => (\n\t\t\t\t\t\t<blockquote className=\"my-1 border-co-border border-l-2 pl-3 italic opacity-80\">\n\t\t\t\t\t\t\t{children}\n\t\t\t\t\t\t</blockquote>\n\t\t\t\t\t),\n\t\t\t\t\t// Handle emphasis\n\t\t\t\t\tem: ({ children }) => <em className=\"italic\">{children}</em>,\n\t\t\t\t\t// Handle links\n\t\t\t\t\ta: ({ href, children }) => (\n\t\t\t\t\t\t<a\n\t\t\t\t\t\t\tclassName=\"underline hover:opacity-80\"\n\t\t\t\t\t\t\thref={href}\n\t\t\t\t\t\t\trel=\"noopener noreferrer\"\n\t\t\t\t\t\t\ttarget=\"_blank\"\n\t\t\t\t\t\t>\n\t\t\t\t\t\t\t{children}\n\t\t\t\t\t\t</a>\n\t\t\t\t\t),\n\t\t\t\t}}\n\t\t\t\tremarkPlugins={[remarkBreaks]}\n\t\t\t>\n\t\t\t\t{content}\n\t\t\t</ReactMarkdown>\n\t\t);\n\t},\n\t(prevProps, nextProps) => {\n\t\tif (prevProps.content !== nextProps.content) {\n\t\t\treturn false;\n\t\t}\n\t\treturn true;\n\t}\n);\n\nMemoizedMarkdownBlock.displayName = \"MemoizedMarkdownBlock\";\n\nexport type TimelineItemContentProps = Omit<\n\tReact.HTMLAttributes<HTMLDivElement>,\n\t\"children\"\n> & {\n\tchildren?: React.ReactNode | ((content: string) => React.ReactNode);\n\tasChild?: boolean;\n\tclassName?: string;\n\ttext?: string | null;\n\trenderMarkdown?: boolean;\n};\n\n/**\n * Renders the content of a timeline item, optionally piping Markdown content through a\n * memoised renderer or handing the raw text to a render prop for custom\n * formatting.\n */\nexport const TimelineItemContent = (() => {\n\tconst Component = React.forwardRef<HTMLDivElement, TimelineItemContentProps>(\n\t\t(\n\t\t\t{\n\t\t\t\tchildren,\n\t\t\t\tclassName,\n\t\t\t\tasChild = false,\n\t\t\t\ttext = \"\",\n\t\t\t\trenderMarkdown = true,\n\t\t\t\t...props\n\t\t\t},\n\t\t\tref\n\t\t) => {\n\t\t\tconst content = React.useMemo(() => {\n\t\t\t\tconst textContent = text ?? \"\";\n\t\t\t\tif (typeof children === \"function\") {\n\t\t\t\t\treturn children(textContent);\n\t\t\t\t}\n\t\t\t\tif (children) {\n\t\t\t\t\treturn children;\n\t\t\t\t}\n\t\t\t\tif (renderMarkdown && textContent) {\n\t\t\t\t\treturn <MemoizedMarkdownBlock content={textContent} />;\n\t\t\t\t}\n\t\t\t\treturn textContent;\n\t\t\t}, [children, text, renderMarkdown]);\n\n\t\t\treturn useRenderElement(\n\t\t\t\t\"div\",\n\t\t\t\t{\n\t\t\t\t\tclassName,\n\t\t\t\t\tasChild,\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tref,\n\t\t\t\t\tprops: {\n\t\t\t\t\t\t...props,\n\t\t\t\t\t\tchildren: content,\n\t\t\t\t\t\tstyle: {\n\t\t\t\t\t\t\t...props.style,\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t}\n\t\t\t);\n\t\t}\n\t);\n\n\tComponent.displayName = \"TimelineItemContent\";\n\treturn Component;\n})();\n\nexport type TimelineItemTimestampProps = Omit<\n\tReact.HTMLAttributes<HTMLSpanElement>,\n\t\"children\"\n> & {\n\tchildren?: React.ReactNode | ((timestamp: Date) => React.ReactNode);\n\tasChild?: boolean;\n\tclassName?: string;\n\ttimestamp: Date;\n\tformat?: (date: Date) => string;\n};\n\n/**\n * Timestamp helper that renders a formatted date or allows callers to supply a\n * render prop for custom time displays while preserving semantic markup.\n */\nexport const TimelineItemTimestamp = (() => {\n\tconst Component = React.forwardRef<\n\t\tHTMLSpanElement,\n\t\tTimelineItemTimestampProps\n\t>(\n\t\t(\n\t\t\t{\n\t\t\t\tchildren,\n\t\t\t\tclassName,\n\t\t\t\tasChild = false,\n\t\t\t\ttimestamp,\n\t\t\t\tformat = (date) =>\n\t\t\t\t\tdate.toLocaleTimeString([], {\n\t\t\t\t\t\thour: \"2-digit\",\n\t\t\t\t\t\tminute: \"2-digit\",\n\t\t\t\t\t}),\n\t\t\t\t...props\n\t\t\t},\n\t\t\tref\n\t\t) => {\n\t\t\tconst content =\n\t\t\t\ttypeof children === \"function\"\n\t\t\t\t\t? children(timestamp)\n\t\t\t\t\t: children || format(timestamp);\n\n\t\t\treturn useRenderElement(\n\t\t\t\t\"span\",\n\t\t\t\t{\n\t\t\t\t\tclassName,\n\t\t\t\t\tasChild,\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tref,\n\t\t\t\t\tprops: {\n\t\t\t\t\t\t...props,\n\t\t\t\t\t\tchildren: content,\n\t\t\t\t\t},\n\t\t\t\t}\n\t\t\t);\n\t\t}\n\t);\n\n\tComponent.displayName = \"TimelineItemTimestamp\";\n\treturn Component;\n})();\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"timeline-item.js","names":["React","renderProps: TimelineItemRenderProps"],"sources":["../../src/primitives/timeline-item.tsx"],"sourcesContent":["import { hasMarkdownFormatting } from \"@cossistant/tiny-markdown/utils\";\nimport type { TimelineItem as TimelineItemType } from \"@cossistant/types/api/timeline-item\";\nimport * as React from \"react\";\nimport ReactMarkdown from \"react-markdown\";\nimport remarkBreaks from \"remark-breaks\";\nimport { useRenderElement } from \"../utils/use-render-element\";\n\n/**\n * Metadata describing the origin of a timeline item and pre-parsed content that can\n * be consumed by render-prop children.\n */\nexport type TimelineItemRenderProps = {\n\tisVisitor: boolean;\n\tisAI: boolean;\n\tisHuman: boolean;\n\ttimestamp: Date;\n\ttext: string | null;\n\tsenderType: \"visitor\" | \"ai\" | \"human\";\n\titemType: \"message\" | \"event\" | \"identification\" | \"tool\";\n};\n\nexport type TimelineItemProps = Omit<\n\tReact.HTMLAttributes<HTMLDivElement>,\n\t\"children\"\n> & {\n\tchildren?:\n\t\t| React.ReactNode\n\t\t| ((props: TimelineItemRenderProps) => React.ReactNode);\n\tasChild?: boolean;\n\tclassName?: string;\n\titem: TimelineItemType;\n};\n\n/**\n * Generic timeline item wrapper that adds accessibility attributes and resolves the\n * sender type into convenient render props for custom layouts. Works with\n * message, event, identification, and tool timeline item types.\n */\nexport const TimelineItem = (() => {\n\tconst Component = React.forwardRef<HTMLDivElement, TimelineItemProps>(\n\t\t({ children, className, asChild = false, item, ...props }, ref) => {\n\t\t\t// Determine sender type from timeline item properties\n\t\t\tconst isVisitor = item.visitorId !== null;\n\t\t\tconst isAI = item.aiAgentId !== null;\n\t\t\tconst isHuman = item.userId !== null && !isVisitor;\n\n\t\t\tconst senderType = isVisitor ? \"visitor\" : isAI ? \"ai\" : \"human\";\n\n\t\t\tconst renderProps: TimelineItemRenderProps = {\n\t\t\t\tisVisitor,\n\t\t\t\tisAI,\n\t\t\t\tisHuman,\n\t\t\t\ttimestamp: new Date(item.createdAt),\n\t\t\t\ttext: item.text,\n\t\t\t\tsenderType,\n\t\t\t\titemType: item.type,\n\t\t\t};\n\n\t\t\tconst content =\n\t\t\t\ttypeof children === \"function\" ? children(renderProps) : children;\n\n\t\t\tconst itemTypeLabel = (() => {\n\t\t\t\tif (item.type === \"event\") {\n\t\t\t\t\treturn \"Event\";\n\t\t\t\t}\n\t\t\t\tif (item.type === \"identification\") {\n\t\t\t\t\treturn \"Identification\";\n\t\t\t\t}\n\t\t\t\tif (item.type === \"tool\") {\n\t\t\t\t\treturn \"Tool call\";\n\t\t\t\t}\n\t\t\t\tif (isVisitor) {\n\t\t\t\t\treturn \"visitor\";\n\t\t\t\t}\n\t\t\t\tif (isAI) {\n\t\t\t\t\treturn \"AI assistant\";\n\t\t\t\t}\n\t\t\t\treturn \"human agent\";\n\t\t\t})();\n\n\t\t\treturn useRenderElement(\n\t\t\t\t\"div\",\n\t\t\t\t{\n\t\t\t\t\tclassName,\n\t\t\t\t\tasChild,\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tref,\n\t\t\t\t\tstate: renderProps,\n\t\t\t\t\tprops: {\n\t\t\t\t\t\trole: \"article\",\n\t\t\t\t\t\t\"aria-label\": `${item.type === \"message\" ? \"Message\" : item.type === \"tool\" ? \"Tool call\" : \"Event\"} from ${itemTypeLabel}`,\n\t\t\t\t\t\t...props,\n\t\t\t\t\t\tchildren: content,\n\t\t\t\t\t},\n\t\t\t\t}\n\t\t\t);\n\t\t}\n\t);\n\n\tComponent.displayName = \"TimelineItem\";\n\treturn Component;\n})();\n\nconst MemoizedMarkdownBlock = React.memo(\n\t({ content }: { content: string }) => {\n\t\tconst shouldRenderMarkdown = hasMarkdownFormatting(content);\n\n\t\tif (!shouldRenderMarkdown) {\n\t\t\treturn <span className=\"whitespace-pre-wrap break-words\">{content}</span>;\n\t\t}\n\n\t\treturn (\n\t\t\t<ReactMarkdown\n\t\t\t\t// Allow mention: protocol URLs (not sanitized by default)\n\t\t\t\tcomponents={{\n\t\t\t\t\t// Render paragraphs as block elements to preserve multiline spacing\n\t\t\t\t\tp: ({ children }) => {\n\t\t\t\t\t\t// Skip empty paragraphs (caused by consecutive blank lines in markdown)\n\t\t\t\t\t\tconst isEmpty =\n\t\t\t\t\t\t\tchildren === undefined ||\n\t\t\t\t\t\t\tchildren === null ||\n\t\t\t\t\t\t\tchildren === \"\" ||\n\t\t\t\t\t\t\t(Array.isArray(children) &&\n\t\t\t\t\t\t\t\tchildren.every((c) => c === \"\\n\" || c === \"\" || c == null));\n\t\t\t\t\t\tif (isEmpty) {\n\t\t\t\t\t\t\treturn null;\n\t\t\t\t\t\t}\n\t\t\t\t\t\treturn <span className=\"mt-1 block first:mt-0\">{children}</span>;\n\t\t\t\t\t},\n\t\t\t\t\t// Ensure proper line break handling\n\t\t\t\t\tbr: () => <br />,\n\t\t\t\t\t// Handle code blocks properly\n\t\t\t\t\tcode: ({ children, ...props }) => {\n\t\t\t\t\t\t// Check if it's inline code by looking at the parent element\n\t\t\t\t\t\tconst isInline = !(\n\t\t\t\t\t\t\t\"className\" in props &&\n\t\t\t\t\t\t\ttypeof props.className === \"string\" &&\n\t\t\t\t\t\t\tprops.className.includes(\"language-\")\n\t\t\t\t\t\t);\n\t\t\t\t\t\treturn isInline ? (\n\t\t\t\t\t\t\t<code className=\"rounded bg-co-background-300 px-1 py-0.5 text-xs\">\n\t\t\t\t\t\t\t\t{children}\n\t\t\t\t\t\t\t</code>\n\t\t\t\t\t\t) : (\n\t\t\t\t\t\t\t<pre className=\"overflow-x-auto rounded bg-co-background-300 p-2\">\n\t\t\t\t\t\t\t\t<code className=\"text-xs\">{children}</code>\n\t\t\t\t\t\t\t</pre>\n\t\t\t\t\t\t);\n\t\t\t\t\t},\n\t\t\t\t\t// Handle strong/bold text\n\t\t\t\t\tstrong: ({ children }) => (\n\t\t\t\t\t\t<strong className=\"font-semibold\">{children}</strong>\n\t\t\t\t\t),\n\t\t\t\t\t// Handle ordered lists\n\t\t\t\t\tol: ({ children }) => (\n\t\t\t\t\t\t<ol className=\"my-0 list-decimal pl-6\">{children}</ol>\n\t\t\t\t\t),\n\t\t\t\t\t// Handle unordered lists\n\t\t\t\t\tul: ({ children }) => (\n\t\t\t\t\t\t<ul className=\"my-0 list-disc pl-6\">{children}</ul>\n\t\t\t\t\t),\n\t\t\t\t\t// Handle list items\n\t\t\t\t\tli: ({ children }) => (\n\t\t\t\t\t\t<li className=\"[&>span.block]:mt-0 [&>span.block]:inline\">\n\t\t\t\t\t\t\t{children}\n\t\t\t\t\t\t</li>\n\t\t\t\t\t),\n\t\t\t\t\t// Handle blockquotes\n\t\t\t\t\tblockquote: ({ children }) => (\n\t\t\t\t\t\t<blockquote className=\"my-1 border-co-border border-l-2 pl-3 italic opacity-80\">\n\t\t\t\t\t\t\t{children}\n\t\t\t\t\t\t</blockquote>\n\t\t\t\t\t),\n\t\t\t\t\t// Handle emphasis\n\t\t\t\t\tem: ({ children }) => <em className=\"italic\">{children}</em>,\n\t\t\t\t\t// Handle links - with special handling for mentions\n\t\t\t\t\t// Mention format: [@Name](mention:type:id) - the @ is inside the link\n\t\t\t\t\ta: ({ href, children, node }) => {\n\t\t\t\t\t\t// Get the raw href from the AST node if available (react-markdown may sanitize href)\n\t\t\t\t\t\tconst rawHref =\n\t\t\t\t\t\t\thref || (node?.properties?.href as string | undefined) || \"\";\n\n\t\t\t\t\t\t// Check if this is a mention link: mention:type:id\n\t\t\t\t\t\tif (rawHref.startsWith(\"mention:\")) {\n\t\t\t\t\t\t\t// Parse mention:type:id format\n\t\t\t\t\t\t\tconst parts = rawHref.split(\":\");\n\t\t\t\t\t\t\tconst mentionType = parts[1]; // visitor, ai-agent, human-agent\n\t\t\t\t\t\t\tconst mentionId = parts.slice(2).join(\":\"); // id (may contain colons)\n\n\t\t\t\t\t\t\t// Render as styled orange pill (same design as input)\n\t\t\t\t\t\t\treturn (\n\t\t\t\t\t\t\t\t<span\n\t\t\t\t\t\t\t\t\tclassName=\"rounded bg-co-orange/15 font-medium text-co-orange\"\n\t\t\t\t\t\t\t\t\tdata-mention-id={mentionId}\n\t\t\t\t\t\t\t\t\tdata-mention-type={mentionType}\n\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t{children}\n\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// Regular link\n\t\t\t\t\t\treturn (\n\t\t\t\t\t\t\t<a\n\t\t\t\t\t\t\t\tclassName=\"underline hover:opacity-80\"\n\t\t\t\t\t\t\t\thref={href}\n\t\t\t\t\t\t\t\trel=\"noopener noreferrer\"\n\t\t\t\t\t\t\t\ttarget=\"_blank\"\n\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t{children}\n\t\t\t\t\t\t\t</a>\n\t\t\t\t\t\t);\n\t\t\t\t\t},\n\t\t\t\t}}\n\t\t\t\tremarkPlugins={[remarkBreaks]}\n\t\t\t\turlTransform={(url) => url}\n\t\t\t>\n\t\t\t\t{content}\n\t\t\t</ReactMarkdown>\n\t\t);\n\t},\n\t(prevProps, nextProps) => {\n\t\tif (prevProps.content !== nextProps.content) {\n\t\t\treturn false;\n\t\t}\n\t\treturn true;\n\t}\n);\n\nMemoizedMarkdownBlock.displayName = \"MemoizedMarkdownBlock\";\n\nexport type TimelineItemContentProps = Omit<\n\tReact.HTMLAttributes<HTMLDivElement>,\n\t\"children\"\n> & {\n\tchildren?: React.ReactNode | ((content: string) => React.ReactNode);\n\tasChild?: boolean;\n\tclassName?: string;\n\ttext?: string | null;\n\trenderMarkdown?: boolean;\n};\n\n/**\n * Renders the content of a timeline item, optionally piping Markdown content through a\n * memoised renderer or handing the raw text to a render prop for custom\n * formatting.\n */\nexport const TimelineItemContent = (() => {\n\tconst Component = React.forwardRef<HTMLDivElement, TimelineItemContentProps>(\n\t\t(\n\t\t\t{\n\t\t\t\tchildren,\n\t\t\t\tclassName,\n\t\t\t\tasChild = false,\n\t\t\t\ttext = \"\",\n\t\t\t\trenderMarkdown = true,\n\t\t\t\t...props\n\t\t\t},\n\t\t\tref\n\t\t) => {\n\t\t\tconst content = React.useMemo(() => {\n\t\t\t\tconst textContent = text ?? \"\";\n\t\t\t\tif (typeof children === \"function\") {\n\t\t\t\t\treturn children(textContent);\n\t\t\t\t}\n\t\t\t\tif (children) {\n\t\t\t\t\treturn children;\n\t\t\t\t}\n\t\t\t\tif (renderMarkdown && textContent) {\n\t\t\t\t\treturn <MemoizedMarkdownBlock content={textContent} />;\n\t\t\t\t}\n\t\t\t\treturn textContent;\n\t\t\t}, [children, text, renderMarkdown]);\n\n\t\t\treturn useRenderElement(\n\t\t\t\t\"div\",\n\t\t\t\t{\n\t\t\t\t\tclassName,\n\t\t\t\t\tasChild,\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tref,\n\t\t\t\t\tprops: {\n\t\t\t\t\t\t...props,\n\t\t\t\t\t\tchildren: content,\n\t\t\t\t\t\tstyle: {\n\t\t\t\t\t\t\t...props.style,\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t}\n\t\t\t);\n\t\t}\n\t);\n\n\tComponent.displayName = \"TimelineItemContent\";\n\treturn Component;\n})();\n\nexport type TimelineItemTimestampProps = Omit<\n\tReact.HTMLAttributes<HTMLSpanElement>,\n\t\"children\"\n> & {\n\tchildren?: React.ReactNode | ((timestamp: Date) => React.ReactNode);\n\tasChild?: boolean;\n\tclassName?: string;\n\ttimestamp: Date;\n\tformat?: (date: Date) => string;\n};\n\n/**\n * Timestamp helper that renders a formatted date or allows callers to supply a\n * render prop for custom time displays while preserving semantic markup.\n */\nexport const TimelineItemTimestamp = (() => {\n\tconst Component = React.forwardRef<\n\t\tHTMLSpanElement,\n\t\tTimelineItemTimestampProps\n\t>(\n\t\t(\n\t\t\t{\n\t\t\t\tchildren,\n\t\t\t\tclassName,\n\t\t\t\tasChild = false,\n\t\t\t\ttimestamp,\n\t\t\t\tformat = (date) =>\n\t\t\t\t\tdate.toLocaleTimeString([], {\n\t\t\t\t\t\thour: \"2-digit\",\n\t\t\t\t\t\tminute: \"2-digit\",\n\t\t\t\t\t}),\n\t\t\t\t...props\n\t\t\t},\n\t\t\tref\n\t\t) => {\n\t\t\tconst content =\n\t\t\t\ttypeof children === \"function\"\n\t\t\t\t\t? children(timestamp)\n\t\t\t\t\t: children || format(timestamp);\n\n\t\t\treturn useRenderElement(\n\t\t\t\t\"span\",\n\t\t\t\t{\n\t\t\t\t\tclassName,\n\t\t\t\t\tasChild,\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tref,\n\t\t\t\t\tprops: {\n\t\t\t\t\t\t...props,\n\t\t\t\t\t\tchildren: content,\n\t\t\t\t\t},\n\t\t\t\t}\n\t\t\t);\n\t\t}\n\t);\n\n\tComponent.displayName = \"TimelineItemTimestamp\";\n\treturn Component;\n})();\n"],"mappings":"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"}
|
package/provider.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"provider.d.ts","names":[],"sources":["../src/provider.tsx"],"sourcesContent":[],"mappings":";;;;;;KAsEY,oBAAA;YACD,KAAA,CAAM;EADL,WAAA,CAAA,EAAA,OAAA;EACD,MAAM,CAAA,EAAA,MAAA;EAKE,KAAA,CAAA,EAAA,MAAA;EAKE,SAAA,CAAA,EAAA,MAAA;EAAK,eAAA,CAAA,EALP,cAKO,EAAA;EAId,YAAA,CAAA,EAAA,MAAA,EAAA;EAEA,WAAA,CAAA,EAAA,OAAA;EACF,WAAA,CAAA,EAAA,GAAA,GAAA,IAAA;EACQ,cAAA,CAAA,EAAA,GAAA,GAAA,IAAA;EAEc,SAAA,CAAA,EAAA,CAAA,KAAA,EAVX,KAUW,EAAA,GAAA,IAAA;EAKxB,IAAA,CAAA,EAAA,QAAA,GAAA,QAAA;CACa;AACZ,KAbG,uBAAA,GAA0B,oBAa7B;AAAgB,KAXb,sBAAA,GAWa;EAOpB,OAAA,EAjBK,qBAiBqB,GAAA,IAAA;EAE1B,eAAA,EAlBa,cAkBI,EAAA;EAAG,YAAA,EAAA,MAAA,EAAA;EAEV,kBAAA,EAAA,CAAA,QAAA,EAlBiB,cAkBjB,EAAA,EAAA,GAAA,IAAA;EAAZ,eAAA,EAAA,CAAA,OAAA,EAAA,MAAA,EAAA,EAAA,GAAA,IAAA;EAAW,WAAA,EAAA,MAAA;
|
|
1
|
+
{"version":3,"file":"provider.d.ts","names":[],"sources":["../src/provider.tsx"],"sourcesContent":[],"mappings":";;;;;;KAsEY,oBAAA;YACD,KAAA,CAAM;EADL,WAAA,CAAA,EAAA,OAAA;EACD,MAAM,CAAA,EAAA,MAAA;EAKE,KAAA,CAAA,EAAA,MAAA;EAKE,SAAA,CAAA,EAAA,MAAA;EAAK,eAAA,CAAA,EALP,cAKO,EAAA;EAId,YAAA,CAAA,EAAA,MAAA,EAAA;EAEA,WAAA,CAAA,EAAA,OAAA;EACF,WAAA,CAAA,EAAA,GAAA,GAAA,IAAA;EACQ,cAAA,CAAA,EAAA,GAAA,GAAA,IAAA;EAEc,SAAA,CAAA,EAAA,CAAA,KAAA,EAVX,KAUW,EAAA,GAAA,IAAA;EAKxB,IAAA,CAAA,EAAA,QAAA,GAAA,QAAA;CACa;AACZ,KAbG,uBAAA,GAA0B,oBAa7B;AAAgB,KAXb,sBAAA,GAWa;EAOpB,OAAA,EAjBK,qBAiBqB,GAAA,IAAA;EAE1B,eAAA,EAlBa,cAkBI,EAAA;EAAG,YAAA,EAAA,MAAA,EAAA;EAEV,kBAAA,EAAA,CAAA,QAAA,EAlBiB,cAkBjB,EAAA,EAAA,GAAA,IAAA;EAAZ,eAAA,EAAA,CAAA,OAAA,EAAA,MAAA,EAAA,EAAA,GAAA,IAAA;EAAW,WAAA,EAAA,MAAA;EAiDF,cAAA,EAAA,CAAA,KAAe,EAAA,MAAA,EAAA,GAAA,IAAA;EAAG,SAAA,EAAA,OAAA;EACK,KAAA,EA/D3B,KA+D2B,GAAA,IAAA;EAAZ,kBAAA,EA9DF,kBA8DE,GAAA,IAAA;EACS,MAAA,EA9DvB,gBA8DuB,GAAA,IAAA;EAAZ,MAAA,EAAA,OAAA;EACT,IAAA,EAAA,GAAA,GAAA,IAAA;EAAiB,KAAA,EAAA,GAAA,GAAA,IAAA;EAIf,MAAA,EAAA,GAAA,GAAA,IAED;AAgWZ,CAAA;KA9ZK,WAAA,GAAc,WA+ZlB,CA/Z8B,sBA+Z9B,CAAA,SAAA,CAAA,CAAA;KA7ZI,iBAAA,GAAoB,WA8ZxB,CAAA,SAAA,CAAA,SAAA,IAAA,GAAA,SAAA,GAAA,SAAA,GA5ZE,WA4ZF,CA5Zc,WA4Zd,CAAA,SAAA,CAAA,CAAA,GAAA;EACA,MAAA,EAAA,MAAA,GAAA,IAAA;CACA;AACA,KA9WW,eAAA,GAAkB,sBA8W7B,GAAA;EACA,oBAAA,EA9WsB,WA8WtB,CA9WkC,WA8WlC,CAAA,sBAAA,CAAA,CAAA,GAAA,EAAA;EACA,iBAAA,EA9WmB,WA8WnB,CA9W+B,WA8W/B,CAAA,mBAAA,CAAA,CAAA,GAAA,EAAA;EACA,OAAA,CAAA,EA9WU,iBA8WV;EACA,IAAA,EAAA,QAAA,GAAA,QAAA;CACA;AACA,cA7WY,cA6WZ,EA7W0B,KAAA,CAAA,OA6W1B,CA7W0B,sBA6W1B,GAAA,SAAA,CAAA;;;;;AA0BD;;iBArCgB,eAAA;;;;;;;;;;;;;GAab,uBAAuB,KAAA,CAAM;;;;;iBAwBhB,UAAA,CAAA,GAAc"}
|
package/provider.js
CHANGED
|
@@ -52,7 +52,7 @@ function areConversationSnapshotsEqual(a, b) {
|
|
|
52
52
|
if (!snapshotB) return false;
|
|
53
53
|
const aLastCreatedAt = snapshotA.lastTimelineItem?.createdAt ?? null;
|
|
54
54
|
const bLastCreatedAt = snapshotB.lastTimelineItem?.createdAt ?? null;
|
|
55
|
-
if (snapshotA.id !== snapshotB.id || aLastCreatedAt !== bLastCreatedAt || snapshotA.visitorLastSeenAt !== snapshotB.visitorLastSeenAt) return false;
|
|
55
|
+
if (snapshotA.id !== snapshotB.id || aLastCreatedAt !== bLastCreatedAt || snapshotA.visitorLastSeenAt !== snapshotB.visitorLastSeenAt || snapshotA.status !== snapshotB.status || snapshotA.deletedAt !== snapshotB.deletedAt) return false;
|
|
56
56
|
}
|
|
57
57
|
return true;
|
|
58
58
|
}
|
|
@@ -102,13 +102,16 @@ function SupportProviderInner({ children, apiUrl, wsUrl, publicKey, defaultMessa
|
|
|
102
102
|
return {
|
|
103
103
|
id: conversation.id,
|
|
104
104
|
lastTimelineItem: conversation.lastTimelineItem ?? null,
|
|
105
|
-
visitorLastSeenAt: conversation.visitorLastSeenAt ?? null
|
|
105
|
+
visitorLastSeenAt: conversation.visitorLastSeenAt ?? null,
|
|
106
|
+
status: conversation.status ?? "open",
|
|
107
|
+
deletedAt: conversation.deletedAt ?? null
|
|
106
108
|
};
|
|
107
109
|
}).filter((snapshot) => snapshot !== null) : [], []), areConversationSnapshotsEqual);
|
|
108
110
|
const derivedUnreadCount = React.useMemo(() => {
|
|
109
111
|
if (!visitorId) return 0;
|
|
110
112
|
let count = 0;
|
|
111
|
-
for (const { id: conversationId, lastTimelineItem, visitorLastSeenAt } of conversationSnapshots) {
|
|
113
|
+
for (const { id: conversationId, lastTimelineItem, visitorLastSeenAt, status, deletedAt } of conversationSnapshots) {
|
|
114
|
+
if (status !== "open" || deletedAt) continue;
|
|
112
115
|
if (!lastTimelineItem) continue;
|
|
113
116
|
if (lastTimelineItem.type !== ConversationTimelineType.MESSAGE) continue;
|
|
114
117
|
if (lastTimelineItem.visitorId && lastTimelineItem.visitorId === visitorId) continue;
|
package/provider.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"provider.js","names":[],"sources":["../src/provider.tsx"],"sourcesContent":["import type { CossistantClient } from \"@cossistant/core\";\nimport { CossistantAPIError, normalizeLocale } from \"@cossistant/core\";\nimport type { DefaultMessage, PublicWebsiteResponse } from \"@cossistant/types\";\nimport type { TimelineItem } from \"@cossistant/types/api/timeline-item\";\nimport { ConversationTimelineType } from \"@cossistant/types/enums\";\nimport React from \"react\";\nimport { useStoreSelector } from \"./hooks/private/store/use-store-selector\";\nimport { useWebsiteStore } from \"./hooks/private/store/use-website-store\";\nimport {\n\ttype ConfigurationError,\n\tuseClient,\n} from \"./hooks/private/use-rest-client\";\nimport { useSeenStore } from \"./realtime/seen-store\";\nimport { WebSocketProvider } from \"./support\";\nimport { IdentificationProvider } from \"./support/context/identification\";\nimport {\n\tinitializeSupportStore,\n\tuseSupportStore,\n} from \"./support/store/support-store\";\n\n/**\n * Auth-related error codes that indicate API key issues.\n */\nconst AUTH_ERROR_CODES = new Set([\n\t\"UNAUTHORIZED\",\n\t\"FORBIDDEN\",\n\t\"INVALID_API_KEY\",\n\t\"API_KEY_EXPIRED\",\n\t\"API_KEY_MISSING\",\n\t\"HTTP_401\",\n\t\"HTTP_403\",\n]);\n\n/**\n * Check if an error is an authentication/authorization error.\n */\nfunction isAuthError(error: Error | null): boolean {\n\tif (!error) {\n\t\treturn false;\n\t}\n\n\tif (error instanceof CossistantAPIError) {\n\t\tconst code = error.code?.toUpperCase() ?? \"\";\n\t\treturn (\n\t\t\tAUTH_ERROR_CODES.has(code) ||\n\t\t\tcode.includes(\"AUTH\") ||\n\t\t\tcode.includes(\"API_KEY\")\n\t\t);\n\t}\n\n\t// Check error message as fallback\n\tconst message = error.message?.toLowerCase() ?? \"\";\n\treturn (\n\t\tmessage.includes(\"api key\") ||\n\t\tmessage.includes(\"unauthorized\") ||\n\t\tmessage.includes(\"forbidden\") ||\n\t\tmessage.includes(\"not authorized\")\n\t);\n}\n\n/**\n * Detect if running in a Next.js environment.\n */\nfunction isNextJSEnvironment(): boolean {\n\tif (typeof window !== \"undefined\") {\n\t\treturn \"__NEXT_DATA__\" in window;\n\t}\n\treturn typeof process !== \"undefined\" && \"__NEXT_RUNTIME\" in process.env;\n}\n\nexport type SupportProviderProps = {\n\tchildren: React.ReactNode;\n\tdefaultOpen?: boolean;\n\tapiUrl?: string;\n\twsUrl?: string;\n\tpublicKey?: string;\n\tdefaultMessages?: DefaultMessage[];\n\tquickOptions?: string[];\n\tautoConnect?: boolean;\n\tonWsConnect?: () => void;\n\tonWsDisconnect?: () => void;\n\tonWsError?: (error: Error) => void;\n\tsize?: \"normal\" | \"larger\";\n};\n\nexport type CossistantProviderProps = SupportProviderProps;\n\nexport type CossistantContextValue = {\n\twebsite: PublicWebsiteResponse | null;\n\tdefaultMessages: DefaultMessage[];\n\tquickOptions: string[];\n\tsetDefaultMessages: (messages: DefaultMessage[]) => void;\n\tsetQuickOptions: (options: string[]) => void;\n\tunreadCount: number;\n\tsetUnreadCount: (count: number) => void;\n\tisLoading: boolean;\n\terror: Error | null;\n\tconfigurationError: ConfigurationError | null;\n\tclient: CossistantClient | null;\n\tisOpen: boolean;\n\topen: () => void;\n\tclose: () => void;\n\ttoggle: () => void;\n};\n\ntype WebsiteData = NonNullable<CossistantContextValue[\"website\"]>;\n\ntype VisitorWithLocale = WebsiteData[\"visitor\"] extends null | undefined\n\t? undefined\n\t: NonNullable<WebsiteData[\"visitor\"]> & { locale: string | null };\n\ntype ConversationSnapshot = {\n\tid: string;\n\tlastTimelineItem: TimelineItem | null;\n\tvisitorLastSeenAt: string | null;\n};\n\nfunction areConversationSnapshotsEqual(\n\ta: ConversationSnapshot[],\n\tb: ConversationSnapshot[]\n): boolean {\n\tif (a === b) {\n\t\treturn true;\n\t}\n\n\tif (a.length !== b.length) {\n\t\treturn false;\n\t}\n\n\tfor (let index = 0; index < a.length; index += 1) {\n\t\tconst snapshotA = a[index];\n\t\tconst snapshotB = b[index];\n\n\t\tif (!snapshotA) {\n\t\t\treturn false;\n\t\t}\n\t\tif (!snapshotB) {\n\t\t\treturn false;\n\t\t}\n\n\t\tconst aLastCreatedAt = snapshotA.lastTimelineItem?.createdAt ?? null;\n\t\tconst bLastCreatedAt = snapshotB.lastTimelineItem?.createdAt ?? null;\n\t\tif (\n\t\t\tsnapshotA.id !== snapshotB.id ||\n\t\t\taLastCreatedAt !== bLastCreatedAt ||\n\t\t\tsnapshotA.visitorLastSeenAt !== snapshotB.visitorLastSeenAt\n\t\t) {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\treturn true;\n}\n\nexport type UseSupportValue = CossistantContextValue & {\n\tavailableHumanAgents: NonNullable<WebsiteData[\"availableHumanAgents\"]> | [];\n\tavailableAIAgents: NonNullable<WebsiteData[\"availableAIAgents\"]> | [];\n\tvisitor?: VisitorWithLocale;\n\tsize: \"normal\" | \"larger\";\n};\n\nexport const SupportContext = React.createContext<\n\tCossistantContextValue | undefined\n>(undefined);\n\n/**\n * Internal implementation that wires the REST client and websocket provider\n * together before exposing the combined context.\n */\nfunction SupportProviderInner({\n\tchildren,\n\tapiUrl,\n\twsUrl,\n\tpublicKey,\n\tdefaultMessages,\n\tquickOptions,\n\tautoConnect,\n\tonWsConnect,\n\tonWsDisconnect,\n\tonWsError,\n\tsize = \"normal\",\n\tdefaultOpen = false,\n}: SupportProviderProps) {\n\tconst [unreadCount, setUnreadCount] = React.useState(0);\n\tconst prefetchedVisitorRef = React.useRef<string | null>(null);\n\tconst [_defaultMessages, _setDefaultMessages] = React.useState<\n\t\tDefaultMessage[]\n\t>(defaultMessages ?? []);\n\tconst [_quickOptions, _setQuickOptions] = React.useState<string[]>(\n\t\tquickOptions ?? []\n\t);\n\n\t// Initialize support store with configuration\n\tReact.useEffect(() => {\n\t\tinitializeSupportStore({ size, defaultOpen });\n\t}, [size, defaultOpen]);\n\n\t// Get support store state and actions\n\tconst { config, open, close, toggle } = useSupportStore();\n\n\t// Update state when props change (for initial values from provider)\n\tReact.useEffect(() => {\n\t\tif (defaultMessages?.length) {\n\t\t\t_setDefaultMessages(defaultMessages);\n\t\t}\n\t}, [defaultMessages]);\n\n\tReact.useEffect(() => {\n\t\tif (quickOptions?.length) {\n\t\t\t_setQuickOptions(quickOptions);\n\t\t}\n\t}, [quickOptions]);\n\n\tconst { client, configurationError: clientConfigError } = useClient(\n\t\tpublicKey,\n\t\tapiUrl,\n\t\twsUrl\n\t);\n\n\t// Only use website store if we have a valid client\n\tconst { website, isLoading, error: websiteError } = useWebsiteStore(client);\n\tconst isVisitorBlocked = website?.visitor?.isBlocked ?? false;\n\tconst visitorId = website?.visitor?.id ?? null;\n\n\t// Derive final configuration error from both client error and API auth errors\n\tconst configurationError = React.useMemo<ConfigurationError | null>(() => {\n\t\t// Client-level config error takes precedence (missing API key)\n\t\tif (clientConfigError) {\n\t\t\treturn clientConfigError;\n\t\t}\n\n\t\t// Check if website error is an auth error (invalid/expired API key)\n\t\tif (websiteError && isAuthError(websiteError)) {\n\t\t\tconst isNextJS = isNextJSEnvironment();\n\t\t\tconst envVarName = isNextJS\n\t\t\t\t? \"NEXT_PUBLIC_COSSISTANT_API_KEY\"\n\t\t\t\t: \"COSSISTANT_API_KEY\";\n\n\t\t\treturn {\n\t\t\t\ttype: \"invalid_api_key\",\n\t\t\t\tmessage: websiteError.message,\n\t\t\t\tenvVarName,\n\t\t\t};\n\t\t}\n\n\t\treturn null;\n\t}, [clientConfigError, websiteError]);\n\n\tconst seenEntriesByConversation = useSeenStore(\n\t\tReact.useCallback((state) => state.conversations, [])\n\t);\n\n\tconst conversationSnapshots = useStoreSelector(\n\t\tclient?.conversationsStore ?? null,\n\t\tReact.useCallback(\n\t\t\t(\n\t\t\t\tstate: {\n\t\t\t\t\tids: string[];\n\t\t\t\t\tbyId: Record<\n\t\t\t\t\t\tstring,\n\t\t\t\t\t\t| {\n\t\t\t\t\t\t\t\tid: string;\n\t\t\t\t\t\t\t\tlastTimelineItem?: TimelineItem | null;\n\t\t\t\t\t\t\t\tvisitorLastSeenAt?: string | null;\n\t\t\t\t\t\t }\n\t\t\t\t\t\t| undefined\n\t\t\t\t\t>;\n\t\t\t\t} | null\n\t\t\t): ConversationSnapshot[] =>\n\t\t\t\tstate\n\t\t\t\t\t? state.ids\n\t\t\t\t\t\t\t.map((id) => {\n\t\t\t\t\t\t\t\tconst conversation = state.byId[id];\n\n\t\t\t\t\t\t\t\tif (!conversation) {\n\t\t\t\t\t\t\t\t\treturn null;\n\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\treturn {\n\t\t\t\t\t\t\t\t\tid: conversation.id,\n\t\t\t\t\t\t\t\t\tlastTimelineItem: conversation.lastTimelineItem ?? null,\n\t\t\t\t\t\t\t\t\tvisitorLastSeenAt: conversation.visitorLastSeenAt ?? null,\n\t\t\t\t\t\t\t\t} satisfies ConversationSnapshot;\n\t\t\t\t\t\t\t})\n\t\t\t\t\t\t\t.filter(\n\t\t\t\t\t\t\t\t(snapshot): snapshot is ConversationSnapshot =>\n\t\t\t\t\t\t\t\t\tsnapshot !== null\n\t\t\t\t\t\t\t)\n\t\t\t\t\t: [],\n\t\t\t[]\n\t\t),\n\t\tareConversationSnapshotsEqual\n\t);\n\n\tconst derivedUnreadCount = React.useMemo(() => {\n\t\tif (!visitorId) {\n\t\t\treturn 0;\n\t\t}\n\n\t\tlet count = 0;\n\n\t\tfor (const {\n\t\t\tid: conversationId,\n\t\t\tlastTimelineItem,\n\t\t\tvisitorLastSeenAt,\n\t\t} of conversationSnapshots) {\n\t\t\tif (!lastTimelineItem) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tif (lastTimelineItem.type !== ConversationTimelineType.MESSAGE) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tif (\n\t\t\t\tlastTimelineItem.visitorId &&\n\t\t\t\tlastTimelineItem.visitorId === visitorId\n\t\t\t) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tconst createdAtTime = Date.parse(lastTimelineItem.createdAt);\n\n\t\t\tif (Number.isNaN(createdAtTime)) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\t// First check visitorLastSeenAt from the API response (available immediately)\n\t\t\tif (visitorLastSeenAt) {\n\t\t\t\tconst lastSeenTime = Date.parse(visitorLastSeenAt);\n\t\t\t\tif (!Number.isNaN(lastSeenTime) && createdAtTime <= lastSeenTime) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Fall back to seen store (updated via realtime events)\n\t\t\tconst seenEntries = seenEntriesByConversation[conversationId];\n\n\t\t\tif (seenEntries) {\n\t\t\t\tconst visitorSeenEntry = Object.values(seenEntries).find(\n\t\t\t\t\t(entry) =>\n\t\t\t\t\t\tentry.actorType === \"visitor\" && entry.actorId === visitorId\n\t\t\t\t);\n\n\t\t\t\tif (visitorSeenEntry) {\n\t\t\t\t\tconst lastSeenTime = Date.parse(visitorSeenEntry.lastSeenAt);\n\n\t\t\t\t\tif (!Number.isNaN(lastSeenTime) && createdAtTime <= lastSeenTime) {\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tcount += 1;\n\t\t}\n\n\t\treturn count;\n\t}, [conversationSnapshots, seenEntriesByConversation, visitorId]);\n\n\tReact.useEffect(() => {\n\t\tsetUnreadCount(derivedUnreadCount);\n\t}, [derivedUnreadCount, setUnreadCount]);\n\n\t// Prime REST client with website/visitor context so headers are sent reliably\n\tReact.useEffect(() => {\n\t\tif (!(website && client)) {\n\t\t\treturn;\n\t\t}\n\n\t\tclient.setWebsiteContext(website.id, website.visitor?.id ?? undefined);\n\t}, [client, website]);\n\n\tReact.useEffect(() => {\n\t\tif (!client) {\n\t\t\treturn;\n\t\t}\n\n\t\tif (isVisitorBlocked) {\n\t\t\tprefetchedVisitorRef.current = null;\n\t\t\treturn;\n\t\t}\n\n\t\tif (!autoConnect) {\n\t\t\treturn;\n\t\t}\n\n\t\tif (!website) {\n\t\t\treturn;\n\t\t}\n\n\t\tif (!visitorId) {\n\t\t\treturn;\n\t\t}\n\n\t\tif (prefetchedVisitorRef.current === visitorId) {\n\t\t\treturn;\n\t\t}\n\n\t\tconst hasExistingConversations =\n\t\t\tclient.conversationsStore.getState().ids.length > 0;\n\n\t\tprefetchedVisitorRef.current = visitorId;\n\n\t\tif (hasExistingConversations) {\n\t\t\treturn;\n\t\t}\n\n\t\tvoid client.listConversations().catch((err) => {\n\t\t\tconsole.error(\"[SupportProvider] Failed to prefetch conversations\", err);\n\t\t\tprefetchedVisitorRef.current = null;\n\t\t});\n\t}, [autoConnect, client, isVisitorBlocked, visitorId, website]);\n\n\tconst error = websiteError;\n\n\tReact.useEffect(() => {\n\t\tif (!client) {\n\t\t\treturn;\n\t\t}\n\t\tclient.setVisitorBlocked(isVisitorBlocked);\n\t}, [client, isVisitorBlocked]);\n\n\tconst setDefaultMessages = React.useCallback((messages: DefaultMessage[]) => {\n\t\t_setDefaultMessages(messages);\n\t}, []);\n\n\tconst setQuickOptions = React.useCallback((options: string[]) => {\n\t\t_setQuickOptions(options);\n\t}, []);\n\n\tconst value = React.useMemo<CossistantContextValue>(\n\t\t() => ({\n\t\t\twebsite,\n\t\t\tunreadCount,\n\t\t\tsetUnreadCount,\n\t\t\tisLoading,\n\t\t\terror,\n\t\t\tconfigurationError,\n\t\t\tclient,\n\t\t\tdefaultMessages: _defaultMessages,\n\t\t\tsetDefaultMessages,\n\t\t\tquickOptions: _quickOptions,\n\t\t\tsetQuickOptions,\n\t\t\tisOpen: config.isOpen,\n\t\t\topen,\n\t\t\tclose,\n\t\t\ttoggle,\n\t\t}),\n\t\t[\n\t\t\twebsite,\n\t\t\tunreadCount,\n\t\t\tisLoading,\n\t\t\terror,\n\t\t\tconfigurationError,\n\t\t\tclient,\n\t\t\t_defaultMessages,\n\t\t\t_quickOptions,\n\t\t\tsetDefaultMessages,\n\t\t\tsetQuickOptions,\n\t\t\tconfig.isOpen,\n\t\t\topen,\n\t\t\tclose,\n\t\t\ttoggle,\n\t\t]\n\t);\n\n\tconst webSocketKey = React.useMemo(() => {\n\t\tif (!website) {\n\t\t\treturn \"no-website\";\n\t\t}\n\n\t\tconst visitorKey = website.visitor?.id ?? \"anonymous\";\n\t\tconst blockedState = isVisitorBlocked ? \"blocked\" : \"active\";\n\n\t\treturn `${website.id}:${visitorKey}:${blockedState}`;\n\t}, [isVisitorBlocked, website]);\n\n\treturn (\n\t\t<SupportContext.Provider value={value}>\n\t\t\t<IdentificationProvider>\n\t\t\t\t<WebSocketProvider\n\t\t\t\t\tautoConnect={autoConnect && !isVisitorBlocked && !configurationError}\n\t\t\t\t\tkey={webSocketKey}\n\t\t\t\t\tonConnect={onWsConnect}\n\t\t\t\t\tonDisconnect={onWsDisconnect}\n\t\t\t\t\tonError={onWsError}\n\t\t\t\t\tpublicKey={publicKey}\n\t\t\t\t\tvisitorId={isVisitorBlocked ? undefined : website?.visitor?.id}\n\t\t\t\t\twebsiteId={website?.id}\n\t\t\t\t\twsUrl={wsUrl}\n\t\t\t\t>\n\t\t\t\t\t{children}\n\t\t\t\t</WebSocketProvider>\n\t\t\t</IdentificationProvider>\n\t\t</SupportContext.Provider>\n\t);\n}\n\n/**\n * Hosts the entire customer support widget ecosystem by handing out context\n * about the current website, visitor, unread counts, realtime subscriptions\n * and the REST client. Provide your Cossistant public key plus optional\n * defaults to configure the widget behaviour.\n */\nexport function SupportProvider({\n\tchildren,\n\tapiUrl = \"https://api.cossistant.com/v1\",\n\twsUrl = \"wss://api.cossistant.com/ws\",\n\tpublicKey,\n\tdefaultMessages,\n\tquickOptions,\n\tautoConnect = true,\n\tonWsConnect,\n\tonWsDisconnect,\n\tonWsError,\n\tsize = \"normal\",\n\tdefaultOpen = false,\n}: SupportProviderProps): React.ReactElement {\n\treturn (\n\t\t<SupportProviderInner\n\t\t\tapiUrl={apiUrl}\n\t\t\tautoConnect={autoConnect}\n\t\t\tdefaultMessages={defaultMessages}\n\t\t\tdefaultOpen={defaultOpen}\n\t\t\tonWsConnect={onWsConnect}\n\t\t\tonWsDisconnect={onWsDisconnect}\n\t\t\tonWsError={onWsError}\n\t\t\tpublicKey={publicKey}\n\t\t\tquickOptions={quickOptions}\n\t\t\tsize={size}\n\t\t\twsUrl={wsUrl}\n\t\t>\n\t\t\t{children}\n\t\t</SupportProviderInner>\n\t);\n}\n\n/**\n * Convenience hook that exposes the aggregated support context. Throws when it\n * is consumed outside of `SupportProvider` to catch integration mistakes.\n */\nexport function useSupport(): UseSupportValue {\n\tconst context = React.useContext(SupportContext);\n\tif (!context) {\n\t\tthrow new Error(\n\t\t\t\"useSupport must be used within a cossistant SupportProvider\"\n\t\t);\n\t}\n\n\tconst availableHumanAgents = context.website?.availableHumanAgents || [];\n\tconst availableAIAgents = context.website?.availableAIAgents || [];\n\tconst visitorLanguage = context.website?.visitor?.language || null;\n\n\t// Get additional config from support store\n\tconst { config } = useSupportStore();\n\n\t// Create visitor object with normalized locale\n\tconst visitor = context.website?.visitor\n\t\t? {\n\t\t\t\t...context.website.visitor,\n\t\t\t\tlocale: normalizeLocale(visitorLanguage),\n\t\t\t}\n\t\t: undefined;\n\n\treturn {\n\t\t...context,\n\t\tavailableHumanAgents,\n\t\tavailableAIAgents,\n\t\tvisitor,\n\t\tsize: config.size,\n\t};\n}\n\n// Re-export ConfigurationError type for consumers\nexport type { ConfigurationError } from \"./hooks/private/use-rest-client\";\n"],"mappings":";;;;;;;;;;;;;;;;AAuBA,MAAM,mBAAmB,IAAI,IAAI;CAChC;CACA;CACA;CACA;CACA;CACA;CACA;CACA,CAAC;;;;AAKF,SAAS,YAAY,OAA8B;AAClD,KAAI,CAAC,MACJ,QAAO;AAGR,KAAI,iBAAiB,oBAAoB;EACxC,MAAM,OAAO,MAAM,MAAM,aAAa,IAAI;AAC1C,SACC,iBAAiB,IAAI,KAAK,IAC1B,KAAK,SAAS,OAAO,IACrB,KAAK,SAAS,UAAU;;CAK1B,MAAM,UAAU,MAAM,SAAS,aAAa,IAAI;AAChD,QACC,QAAQ,SAAS,UAAU,IAC3B,QAAQ,SAAS,eAAe,IAChC,QAAQ,SAAS,YAAY,IAC7B,QAAQ,SAAS,iBAAiB;;;;;AAOpC,SAAS,sBAA+B;AACvC,KAAI,OAAO,WAAW,YACrB,QAAO,mBAAmB;AAE3B,QAAO,OAAO,YAAY,eAAe,oBAAoB,QAAQ;;AAkDtE,SAAS,8BACR,GACA,GACU;AACV,KAAI,MAAM,EACT,QAAO;AAGR,KAAI,EAAE,WAAW,EAAE,OAClB,QAAO;AAGR,MAAK,IAAI,QAAQ,GAAG,QAAQ,EAAE,QAAQ,SAAS,GAAG;EACjD,MAAM,YAAY,EAAE;EACpB,MAAM,YAAY,EAAE;AAEpB,MAAI,CAAC,UACJ,QAAO;AAER,MAAI,CAAC,UACJ,QAAO;EAGR,MAAM,iBAAiB,UAAU,kBAAkB,aAAa;EAChE,MAAM,iBAAiB,UAAU,kBAAkB,aAAa;AAChE,MACC,UAAU,OAAO,UAAU,MAC3B,mBAAmB,kBACnB,UAAU,sBAAsB,UAAU,kBAE1C,QAAO;;AAIT,QAAO;;AAUR,MAAa,iBAAiB,MAAM,cAElC,OAAU;;;;;AAMZ,SAAS,qBAAqB,EAC7B,UACA,QACA,OACA,WACA,iBACA,cACA,aACA,aACA,gBACA,WACA,OAAO,UACP,cAAc,SACU;CACxB,MAAM,CAAC,aAAa,kBAAkB,MAAM,SAAS,EAAE;CACvD,MAAM,uBAAuB,MAAM,OAAsB,KAAK;CAC9D,MAAM,CAAC,kBAAkB,uBAAuB,MAAM,SAEpD,mBAAmB,EAAE,CAAC;CACxB,MAAM,CAAC,eAAe,oBAAoB,MAAM,SAC/C,gBAAgB,EAAE,CAClB;AAGD,OAAM,gBAAgB;AACrB,yBAAuB;GAAE;GAAM;GAAa,CAAC;IAC3C,CAAC,MAAM,YAAY,CAAC;CAGvB,MAAM,EAAE,QAAQ,MAAM,OAAO,WAAW,iBAAiB;AAGzD,OAAM,gBAAgB;AACrB,MAAI,iBAAiB,OACpB,qBAAoB,gBAAgB;IAEnC,CAAC,gBAAgB,CAAC;AAErB,OAAM,gBAAgB;AACrB,MAAI,cAAc,OACjB,kBAAiB,aAAa;IAE7B,CAAC,aAAa,CAAC;CAElB,MAAM,EAAE,QAAQ,oBAAoB,sBAAsB,UACzD,WACA,QACA,MACA;CAGD,MAAM,EAAE,SAAS,WAAW,OAAO,iBAAiB,gBAAgB,OAAO;CAC3E,MAAM,mBAAmB,SAAS,SAAS,aAAa;CACxD,MAAM,YAAY,SAAS,SAAS,MAAM;CAG1C,MAAM,qBAAqB,MAAM,cAAyC;AAEzE,MAAI,kBACH,QAAO;AAIR,MAAI,gBAAgB,YAAY,aAAa,EAAE;GAE9C,MAAM,aADW,qBAAqB,GAEnC,mCACA;AAEH,UAAO;IACN,MAAM;IACN,SAAS,aAAa;IACtB;IACA;;AAGF,SAAO;IACL,CAAC,mBAAmB,aAAa,CAAC;CAErC,MAAM,4BAA4B,aACjC,MAAM,aAAa,UAAU,MAAM,eAAe,EAAE,CAAC,CACrD;CAED,MAAM,wBAAwB,iBAC7B,QAAQ,sBAAsB,MAC9B,MAAM,aAEJ,UAaA,QACG,MAAM,IACL,KAAK,OAAO;EACZ,MAAM,eAAe,MAAM,KAAK;AAEhC,MAAI,CAAC,aACJ,QAAO;AAGR,SAAO;GACN,IAAI,aAAa;GACjB,kBAAkB,aAAa,oBAAoB;GACnD,mBAAmB,aAAa,qBAAqB;GACrD;GACA,CACD,QACC,aACA,aAAa,KACd,GACD,EAAE,EACN,EAAE,CACF,EACD,8BACA;CAED,MAAM,qBAAqB,MAAM,cAAc;AAC9C,MAAI,CAAC,UACJ,QAAO;EAGR,IAAI,QAAQ;AAEZ,OAAK,MAAM,EACV,IAAI,gBACJ,kBACA,uBACI,uBAAuB;AAC3B,OAAI,CAAC,iBACJ;AAGD,OAAI,iBAAiB,SAAS,yBAAyB,QACtD;AAGD,OACC,iBAAiB,aACjB,iBAAiB,cAAc,UAE/B;GAGD,MAAM,gBAAgB,KAAK,MAAM,iBAAiB,UAAU;AAE5D,OAAI,OAAO,MAAM,cAAc,CAC9B;AAID,OAAI,mBAAmB;IACtB,MAAM,eAAe,KAAK,MAAM,kBAAkB;AAClD,QAAI,CAAC,OAAO,MAAM,aAAa,IAAI,iBAAiB,aACnD;;GAKF,MAAM,cAAc,0BAA0B;AAE9C,OAAI,aAAa;IAChB,MAAM,mBAAmB,OAAO,OAAO,YAAY,CAAC,MAClD,UACA,MAAM,cAAc,aAAa,MAAM,YAAY,UACpD;AAED,QAAI,kBAAkB;KACrB,MAAM,eAAe,KAAK,MAAM,iBAAiB,WAAW;AAE5D,SAAI,CAAC,OAAO,MAAM,aAAa,IAAI,iBAAiB,aACnD;;;AAKH,YAAS;;AAGV,SAAO;IACL;EAAC;EAAuB;EAA2B;EAAU,CAAC;AAEjE,OAAM,gBAAgB;AACrB,iBAAe,mBAAmB;IAChC,CAAC,oBAAoB,eAAe,CAAC;AAGxC,OAAM,gBAAgB;AACrB,MAAI,EAAE,WAAW,QAChB;AAGD,SAAO,kBAAkB,QAAQ,IAAI,QAAQ,SAAS,MAAM,OAAU;IACpE,CAAC,QAAQ,QAAQ,CAAC;AAErB,OAAM,gBAAgB;AACrB,MAAI,CAAC,OACJ;AAGD,MAAI,kBAAkB;AACrB,wBAAqB,UAAU;AAC/B;;AAGD,MAAI,CAAC,YACJ;AAGD,MAAI,CAAC,QACJ;AAGD,MAAI,CAAC,UACJ;AAGD,MAAI,qBAAqB,YAAY,UACpC;EAGD,MAAM,2BACL,OAAO,mBAAmB,UAAU,CAAC,IAAI,SAAS;AAEnD,uBAAqB,UAAU;AAE/B,MAAI,yBACH;AAGD,EAAK,OAAO,mBAAmB,CAAC,OAAO,QAAQ;AAC9C,WAAQ,MAAM,sDAAsD,IAAI;AACxE,wBAAqB,UAAU;IAC9B;IACA;EAAC;EAAa;EAAQ;EAAkB;EAAW;EAAQ,CAAC;CAE/D,MAAM,QAAQ;AAEd,OAAM,gBAAgB;AACrB,MAAI,CAAC,OACJ;AAED,SAAO,kBAAkB,iBAAiB;IACxC,CAAC,QAAQ,iBAAiB,CAAC;CAE9B,MAAM,qBAAqB,MAAM,aAAa,aAA+B;AAC5E,sBAAoB,SAAS;IAC3B,EAAE,CAAC;CAEN,MAAM,kBAAkB,MAAM,aAAa,YAAsB;AAChE,mBAAiB,QAAQ;IACvB,EAAE,CAAC;CAEN,MAAM,QAAQ,MAAM,eACZ;EACN;EACA;EACA;EACA;EACA;EACA;EACA;EACA,iBAAiB;EACjB;EACA,cAAc;EACd;EACA,QAAQ,OAAO;EACf;EACA;EACA;EACA,GACD;EACC;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA,OAAO;EACP;EACA;EACA;EACA,CACD;CAED,MAAM,eAAe,MAAM,cAAc;AACxC,MAAI,CAAC,QACJ,QAAO;EAGR,MAAM,aAAa,QAAQ,SAAS,MAAM;EAC1C,MAAM,eAAe,mBAAmB,YAAY;AAEpD,SAAO,GAAG,QAAQ,GAAG,GAAG,WAAW,GAAG;IACpC,CAAC,kBAAkB,QAAQ,CAAC;AAE/B,QACC,oBAAC,eAAe;EAAgB;YAC/B,oBAAC,oCACA,oBAAC;GACA,aAAa,eAAe,CAAC,oBAAoB,CAAC;GAElD,WAAW;GACX,cAAc;GACd,SAAS;GACE;GACX,WAAW,mBAAmB,SAAY,SAAS,SAAS;GAC5D,WAAW,SAAS;GACb;GAEN;KATI,aAUc,GACI;GACA;;;;;;;;AAU5B,SAAgB,gBAAgB,EAC/B,UACA,SAAS,iCACT,QAAQ,+BACR,WACA,iBACA,cACA,cAAc,MACd,aACA,gBACA,WACA,OAAO,UACP,cAAc,SAC8B;AAC5C,QACC,oBAAC;EACQ;EACK;EACI;EACJ;EACA;EACG;EACL;EACA;EACG;EACR;EACC;EAEN;GACqB;;;;;;AAQzB,SAAgB,aAA8B;CAC7C,MAAM,UAAU,MAAM,WAAW,eAAe;AAChD,KAAI,CAAC,QACJ,OAAM,IAAI,MACT,8DACA;CAGF,MAAM,uBAAuB,QAAQ,SAAS,wBAAwB,EAAE;CACxE,MAAM,oBAAoB,QAAQ,SAAS,qBAAqB,EAAE;CAClE,MAAM,kBAAkB,QAAQ,SAAS,SAAS,YAAY;CAG9D,MAAM,EAAE,WAAW,iBAAiB;CAGpC,MAAM,UAAU,QAAQ,SAAS,UAC9B;EACA,GAAG,QAAQ,QAAQ;EACnB,QAAQ,gBAAgB,gBAAgB;EACxC,GACA;AAEH,QAAO;EACN,GAAG;EACH;EACA;EACA;EACA,MAAM,OAAO;EACb"}
|
|
1
|
+
{"version":3,"file":"provider.js","names":[],"sources":["../src/provider.tsx"],"sourcesContent":["import type { CossistantClient } from \"@cossistant/core\";\nimport { CossistantAPIError, normalizeLocale } from \"@cossistant/core\";\nimport type { DefaultMessage, PublicWebsiteResponse } from \"@cossistant/types\";\nimport type { TimelineItem } from \"@cossistant/types/api/timeline-item\";\nimport { ConversationTimelineType } from \"@cossistant/types/enums\";\nimport React from \"react\";\nimport { useStoreSelector } from \"./hooks/private/store/use-store-selector\";\nimport { useWebsiteStore } from \"./hooks/private/store/use-website-store\";\nimport {\n\ttype ConfigurationError,\n\tuseClient,\n} from \"./hooks/private/use-rest-client\";\nimport { useSeenStore } from \"./realtime/seen-store\";\nimport { WebSocketProvider } from \"./support\";\nimport { IdentificationProvider } from \"./support/context/identification\";\nimport {\n\tinitializeSupportStore,\n\tuseSupportStore,\n} from \"./support/store/support-store\";\n\n/**\n * Auth-related error codes that indicate API key issues.\n */\nconst AUTH_ERROR_CODES = new Set([\n\t\"UNAUTHORIZED\",\n\t\"FORBIDDEN\",\n\t\"INVALID_API_KEY\",\n\t\"API_KEY_EXPIRED\",\n\t\"API_KEY_MISSING\",\n\t\"HTTP_401\",\n\t\"HTTP_403\",\n]);\n\n/**\n * Check if an error is an authentication/authorization error.\n */\nfunction isAuthError(error: Error | null): boolean {\n\tif (!error) {\n\t\treturn false;\n\t}\n\n\tif (error instanceof CossistantAPIError) {\n\t\tconst code = error.code?.toUpperCase() ?? \"\";\n\t\treturn (\n\t\t\tAUTH_ERROR_CODES.has(code) ||\n\t\t\tcode.includes(\"AUTH\") ||\n\t\t\tcode.includes(\"API_KEY\")\n\t\t);\n\t}\n\n\t// Check error message as fallback\n\tconst message = error.message?.toLowerCase() ?? \"\";\n\treturn (\n\t\tmessage.includes(\"api key\") ||\n\t\tmessage.includes(\"unauthorized\") ||\n\t\tmessage.includes(\"forbidden\") ||\n\t\tmessage.includes(\"not authorized\")\n\t);\n}\n\n/**\n * Detect if running in a Next.js environment.\n */\nfunction isNextJSEnvironment(): boolean {\n\tif (typeof window !== \"undefined\") {\n\t\treturn \"__NEXT_DATA__\" in window;\n\t}\n\treturn typeof process !== \"undefined\" && \"__NEXT_RUNTIME\" in process.env;\n}\n\nexport type SupportProviderProps = {\n\tchildren: React.ReactNode;\n\tdefaultOpen?: boolean;\n\tapiUrl?: string;\n\twsUrl?: string;\n\tpublicKey?: string;\n\tdefaultMessages?: DefaultMessage[];\n\tquickOptions?: string[];\n\tautoConnect?: boolean;\n\tonWsConnect?: () => void;\n\tonWsDisconnect?: () => void;\n\tonWsError?: (error: Error) => void;\n\tsize?: \"normal\" | \"larger\";\n};\n\nexport type CossistantProviderProps = SupportProviderProps;\n\nexport type CossistantContextValue = {\n\twebsite: PublicWebsiteResponse | null;\n\tdefaultMessages: DefaultMessage[];\n\tquickOptions: string[];\n\tsetDefaultMessages: (messages: DefaultMessage[]) => void;\n\tsetQuickOptions: (options: string[]) => void;\n\tunreadCount: number;\n\tsetUnreadCount: (count: number) => void;\n\tisLoading: boolean;\n\terror: Error | null;\n\tconfigurationError: ConfigurationError | null;\n\tclient: CossistantClient | null;\n\tisOpen: boolean;\n\topen: () => void;\n\tclose: () => void;\n\ttoggle: () => void;\n};\n\ntype WebsiteData = NonNullable<CossistantContextValue[\"website\"]>;\n\ntype VisitorWithLocale = WebsiteData[\"visitor\"] extends null | undefined\n\t? undefined\n\t: NonNullable<WebsiteData[\"visitor\"]> & { locale: string | null };\n\ntype ConversationSnapshot = {\n\tid: string;\n\tlastTimelineItem: TimelineItem | null;\n\tvisitorLastSeenAt: string | null;\n\tstatus: string;\n\tdeletedAt: string | null;\n};\n\nfunction areConversationSnapshotsEqual(\n\ta: ConversationSnapshot[],\n\tb: ConversationSnapshot[]\n): boolean {\n\tif (a === b) {\n\t\treturn true;\n\t}\n\n\tif (a.length !== b.length) {\n\t\treturn false;\n\t}\n\n\tfor (let index = 0; index < a.length; index += 1) {\n\t\tconst snapshotA = a[index];\n\t\tconst snapshotB = b[index];\n\n\t\tif (!snapshotA) {\n\t\t\treturn false;\n\t\t}\n\t\tif (!snapshotB) {\n\t\t\treturn false;\n\t\t}\n\n\t\tconst aLastCreatedAt = snapshotA.lastTimelineItem?.createdAt ?? null;\n\t\tconst bLastCreatedAt = snapshotB.lastTimelineItem?.createdAt ?? null;\n\t\tif (\n\t\t\tsnapshotA.id !== snapshotB.id ||\n\t\t\taLastCreatedAt !== bLastCreatedAt ||\n\t\t\tsnapshotA.visitorLastSeenAt !== snapshotB.visitorLastSeenAt ||\n\t\t\tsnapshotA.status !== snapshotB.status ||\n\t\t\tsnapshotA.deletedAt !== snapshotB.deletedAt\n\t\t) {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\treturn true;\n}\n\nexport type UseSupportValue = CossistantContextValue & {\n\tavailableHumanAgents: NonNullable<WebsiteData[\"availableHumanAgents\"]> | [];\n\tavailableAIAgents: NonNullable<WebsiteData[\"availableAIAgents\"]> | [];\n\tvisitor?: VisitorWithLocale;\n\tsize: \"normal\" | \"larger\";\n};\n\nexport const SupportContext = React.createContext<\n\tCossistantContextValue | undefined\n>(undefined);\n\n/**\n * Internal implementation that wires the REST client and websocket provider\n * together before exposing the combined context.\n */\nfunction SupportProviderInner({\n\tchildren,\n\tapiUrl,\n\twsUrl,\n\tpublicKey,\n\tdefaultMessages,\n\tquickOptions,\n\tautoConnect,\n\tonWsConnect,\n\tonWsDisconnect,\n\tonWsError,\n\tsize = \"normal\",\n\tdefaultOpen = false,\n}: SupportProviderProps) {\n\tconst [unreadCount, setUnreadCount] = React.useState(0);\n\tconst prefetchedVisitorRef = React.useRef<string | null>(null);\n\tconst [_defaultMessages, _setDefaultMessages] = React.useState<\n\t\tDefaultMessage[]\n\t>(defaultMessages ?? []);\n\tconst [_quickOptions, _setQuickOptions] = React.useState<string[]>(\n\t\tquickOptions ?? []\n\t);\n\n\t// Initialize support store with configuration\n\tReact.useEffect(() => {\n\t\tinitializeSupportStore({ size, defaultOpen });\n\t}, [size, defaultOpen]);\n\n\t// Get support store state and actions\n\tconst { config, open, close, toggle } = useSupportStore();\n\n\t// Update state when props change (for initial values from provider)\n\tReact.useEffect(() => {\n\t\tif (defaultMessages?.length) {\n\t\t\t_setDefaultMessages(defaultMessages);\n\t\t}\n\t}, [defaultMessages]);\n\n\tReact.useEffect(() => {\n\t\tif (quickOptions?.length) {\n\t\t\t_setQuickOptions(quickOptions);\n\t\t}\n\t}, [quickOptions]);\n\n\tconst { client, configurationError: clientConfigError } = useClient(\n\t\tpublicKey,\n\t\tapiUrl,\n\t\twsUrl\n\t);\n\n\t// Only use website store if we have a valid client\n\tconst { website, isLoading, error: websiteError } = useWebsiteStore(client);\n\tconst isVisitorBlocked = website?.visitor?.isBlocked ?? false;\n\tconst visitorId = website?.visitor?.id ?? null;\n\n\t// Derive final configuration error from both client error and API auth errors\n\tconst configurationError = React.useMemo<ConfigurationError | null>(() => {\n\t\t// Client-level config error takes precedence (missing API key)\n\t\tif (clientConfigError) {\n\t\t\treturn clientConfigError;\n\t\t}\n\n\t\t// Check if website error is an auth error (invalid/expired API key)\n\t\tif (websiteError && isAuthError(websiteError)) {\n\t\t\tconst isNextJS = isNextJSEnvironment();\n\t\t\tconst envVarName = isNextJS\n\t\t\t\t? \"NEXT_PUBLIC_COSSISTANT_API_KEY\"\n\t\t\t\t: \"COSSISTANT_API_KEY\";\n\n\t\t\treturn {\n\t\t\t\ttype: \"invalid_api_key\",\n\t\t\t\tmessage: websiteError.message,\n\t\t\t\tenvVarName,\n\t\t\t};\n\t\t}\n\n\t\treturn null;\n\t}, [clientConfigError, websiteError]);\n\n\tconst seenEntriesByConversation = useSeenStore(\n\t\tReact.useCallback((state) => state.conversations, [])\n\t);\n\n\tconst conversationSnapshots = useStoreSelector(\n\t\tclient?.conversationsStore ?? null,\n\t\tReact.useCallback(\n\t\t\t(\n\t\t\t\tstate: {\n\t\t\t\t\tids: string[];\n\t\t\t\t\tbyId: Record<\n\t\t\t\t\t\tstring,\n\t\t\t\t\t\t| {\n\t\t\t\t\t\t\t\tid: string;\n\t\t\t\t\t\t\t\tlastTimelineItem?: TimelineItem | null;\n\t\t\t\t\t\t\t\tvisitorLastSeenAt?: string | null;\n\t\t\t\t\t\t\t\tstatus?: string;\n\t\t\t\t\t\t\t\tdeletedAt?: string | null;\n\t\t\t\t\t\t }\n\t\t\t\t\t\t| undefined\n\t\t\t\t\t>;\n\t\t\t\t} | null\n\t\t\t): ConversationSnapshot[] =>\n\t\t\t\tstate\n\t\t\t\t\t? state.ids\n\t\t\t\t\t\t\t.map((id) => {\n\t\t\t\t\t\t\t\tconst conversation = state.byId[id];\n\n\t\t\t\t\t\t\t\tif (!conversation) {\n\t\t\t\t\t\t\t\t\treturn null;\n\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\treturn {\n\t\t\t\t\t\t\t\t\tid: conversation.id,\n\t\t\t\t\t\t\t\t\tlastTimelineItem: conversation.lastTimelineItem ?? null,\n\t\t\t\t\t\t\t\t\tvisitorLastSeenAt: conversation.visitorLastSeenAt ?? null,\n\t\t\t\t\t\t\t\t\tstatus: conversation.status ?? \"open\",\n\t\t\t\t\t\t\t\t\tdeletedAt: conversation.deletedAt ?? null,\n\t\t\t\t\t\t\t\t} satisfies ConversationSnapshot;\n\t\t\t\t\t\t\t})\n\t\t\t\t\t\t\t.filter(\n\t\t\t\t\t\t\t\t(snapshot): snapshot is ConversationSnapshot =>\n\t\t\t\t\t\t\t\t\tsnapshot !== null\n\t\t\t\t\t\t\t)\n\t\t\t\t\t: [],\n\t\t\t[]\n\t\t),\n\t\tareConversationSnapshotsEqual\n\t);\n\n\tconst derivedUnreadCount = React.useMemo(() => {\n\t\tif (!visitorId) {\n\t\t\treturn 0;\n\t\t}\n\n\t\tlet count = 0;\n\n\t\tfor (const {\n\t\t\tid: conversationId,\n\t\t\tlastTimelineItem,\n\t\t\tvisitorLastSeenAt,\n\t\t\tstatus,\n\t\t\tdeletedAt,\n\t\t} of conversationSnapshots) {\n\t\t\t// Skip resolved, spam, or deleted conversations\n\t\t\tif (status !== \"open\" || deletedAt) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tif (!lastTimelineItem) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tif (lastTimelineItem.type !== ConversationTimelineType.MESSAGE) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tif (\n\t\t\t\tlastTimelineItem.visitorId &&\n\t\t\t\tlastTimelineItem.visitorId === visitorId\n\t\t\t) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tconst createdAtTime = Date.parse(lastTimelineItem.createdAt);\n\n\t\t\tif (Number.isNaN(createdAtTime)) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\t// First check visitorLastSeenAt from the API response (available immediately)\n\t\t\tif (visitorLastSeenAt) {\n\t\t\t\tconst lastSeenTime = Date.parse(visitorLastSeenAt);\n\t\t\t\tif (!Number.isNaN(lastSeenTime) && createdAtTime <= lastSeenTime) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Fall back to seen store (updated via realtime events)\n\t\t\tconst seenEntries = seenEntriesByConversation[conversationId];\n\n\t\t\tif (seenEntries) {\n\t\t\t\tconst visitorSeenEntry = Object.values(seenEntries).find(\n\t\t\t\t\t(entry) =>\n\t\t\t\t\t\tentry.actorType === \"visitor\" && entry.actorId === visitorId\n\t\t\t\t);\n\n\t\t\t\tif (visitorSeenEntry) {\n\t\t\t\t\tconst lastSeenTime = Date.parse(visitorSeenEntry.lastSeenAt);\n\n\t\t\t\t\tif (!Number.isNaN(lastSeenTime) && createdAtTime <= lastSeenTime) {\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tcount += 1;\n\t\t}\n\n\t\treturn count;\n\t}, [conversationSnapshots, seenEntriesByConversation, visitorId]);\n\n\tReact.useEffect(() => {\n\t\tsetUnreadCount(derivedUnreadCount);\n\t}, [derivedUnreadCount, setUnreadCount]);\n\n\t// Prime REST client with website/visitor context so headers are sent reliably\n\tReact.useEffect(() => {\n\t\tif (!(website && client)) {\n\t\t\treturn;\n\t\t}\n\n\t\tclient.setWebsiteContext(website.id, website.visitor?.id ?? undefined);\n\t}, [client, website]);\n\n\tReact.useEffect(() => {\n\t\tif (!client) {\n\t\t\treturn;\n\t\t}\n\n\t\tif (isVisitorBlocked) {\n\t\t\tprefetchedVisitorRef.current = null;\n\t\t\treturn;\n\t\t}\n\n\t\tif (!autoConnect) {\n\t\t\treturn;\n\t\t}\n\n\t\tif (!website) {\n\t\t\treturn;\n\t\t}\n\n\t\tif (!visitorId) {\n\t\t\treturn;\n\t\t}\n\n\t\tif (prefetchedVisitorRef.current === visitorId) {\n\t\t\treturn;\n\t\t}\n\n\t\tconst hasExistingConversations =\n\t\t\tclient.conversationsStore.getState().ids.length > 0;\n\n\t\tprefetchedVisitorRef.current = visitorId;\n\n\t\tif (hasExistingConversations) {\n\t\t\treturn;\n\t\t}\n\n\t\tvoid client.listConversations().catch((err) => {\n\t\t\tconsole.error(\"[SupportProvider] Failed to prefetch conversations\", err);\n\t\t\tprefetchedVisitorRef.current = null;\n\t\t});\n\t}, [autoConnect, client, isVisitorBlocked, visitorId, website]);\n\n\tconst error = websiteError;\n\n\tReact.useEffect(() => {\n\t\tif (!client) {\n\t\t\treturn;\n\t\t}\n\t\tclient.setVisitorBlocked(isVisitorBlocked);\n\t}, [client, isVisitorBlocked]);\n\n\tconst setDefaultMessages = React.useCallback((messages: DefaultMessage[]) => {\n\t\t_setDefaultMessages(messages);\n\t}, []);\n\n\tconst setQuickOptions = React.useCallback((options: string[]) => {\n\t\t_setQuickOptions(options);\n\t}, []);\n\n\tconst value = React.useMemo<CossistantContextValue>(\n\t\t() => ({\n\t\t\twebsite,\n\t\t\tunreadCount,\n\t\t\tsetUnreadCount,\n\t\t\tisLoading,\n\t\t\terror,\n\t\t\tconfigurationError,\n\t\t\tclient,\n\t\t\tdefaultMessages: _defaultMessages,\n\t\t\tsetDefaultMessages,\n\t\t\tquickOptions: _quickOptions,\n\t\t\tsetQuickOptions,\n\t\t\tisOpen: config.isOpen,\n\t\t\topen,\n\t\t\tclose,\n\t\t\ttoggle,\n\t\t}),\n\t\t[\n\t\t\twebsite,\n\t\t\tunreadCount,\n\t\t\tisLoading,\n\t\t\terror,\n\t\t\tconfigurationError,\n\t\t\tclient,\n\t\t\t_defaultMessages,\n\t\t\t_quickOptions,\n\t\t\tsetDefaultMessages,\n\t\t\tsetQuickOptions,\n\t\t\tconfig.isOpen,\n\t\t\topen,\n\t\t\tclose,\n\t\t\ttoggle,\n\t\t]\n\t);\n\n\tconst webSocketKey = React.useMemo(() => {\n\t\tif (!website) {\n\t\t\treturn \"no-website\";\n\t\t}\n\n\t\tconst visitorKey = website.visitor?.id ?? \"anonymous\";\n\t\tconst blockedState = isVisitorBlocked ? \"blocked\" : \"active\";\n\n\t\treturn `${website.id}:${visitorKey}:${blockedState}`;\n\t}, [isVisitorBlocked, website]);\n\n\treturn (\n\t\t<SupportContext.Provider value={value}>\n\t\t\t<IdentificationProvider>\n\t\t\t\t<WebSocketProvider\n\t\t\t\t\tautoConnect={autoConnect && !isVisitorBlocked && !configurationError}\n\t\t\t\t\tkey={webSocketKey}\n\t\t\t\t\tonConnect={onWsConnect}\n\t\t\t\t\tonDisconnect={onWsDisconnect}\n\t\t\t\t\tonError={onWsError}\n\t\t\t\t\tpublicKey={publicKey}\n\t\t\t\t\tvisitorId={isVisitorBlocked ? undefined : website?.visitor?.id}\n\t\t\t\t\twebsiteId={website?.id}\n\t\t\t\t\twsUrl={wsUrl}\n\t\t\t\t>\n\t\t\t\t\t{children}\n\t\t\t\t</WebSocketProvider>\n\t\t\t</IdentificationProvider>\n\t\t</SupportContext.Provider>\n\t);\n}\n\n/**\n * Hosts the entire customer support widget ecosystem by handing out context\n * about the current website, visitor, unread counts, realtime subscriptions\n * and the REST client. Provide your Cossistant public key plus optional\n * defaults to configure the widget behaviour.\n */\nexport function SupportProvider({\n\tchildren,\n\tapiUrl = \"https://api.cossistant.com/v1\",\n\twsUrl = \"wss://api.cossistant.com/ws\",\n\tpublicKey,\n\tdefaultMessages,\n\tquickOptions,\n\tautoConnect = true,\n\tonWsConnect,\n\tonWsDisconnect,\n\tonWsError,\n\tsize = \"normal\",\n\tdefaultOpen = false,\n}: SupportProviderProps): React.ReactElement {\n\treturn (\n\t\t<SupportProviderInner\n\t\t\tapiUrl={apiUrl}\n\t\t\tautoConnect={autoConnect}\n\t\t\tdefaultMessages={defaultMessages}\n\t\t\tdefaultOpen={defaultOpen}\n\t\t\tonWsConnect={onWsConnect}\n\t\t\tonWsDisconnect={onWsDisconnect}\n\t\t\tonWsError={onWsError}\n\t\t\tpublicKey={publicKey}\n\t\t\tquickOptions={quickOptions}\n\t\t\tsize={size}\n\t\t\twsUrl={wsUrl}\n\t\t>\n\t\t\t{children}\n\t\t</SupportProviderInner>\n\t);\n}\n\n/**\n * Convenience hook that exposes the aggregated support context. Throws when it\n * is consumed outside of `SupportProvider` to catch integration mistakes.\n */\nexport function useSupport(): UseSupportValue {\n\tconst context = React.useContext(SupportContext);\n\tif (!context) {\n\t\tthrow new Error(\n\t\t\t\"useSupport must be used within a cossistant SupportProvider\"\n\t\t);\n\t}\n\n\tconst availableHumanAgents = context.website?.availableHumanAgents || [];\n\tconst availableAIAgents = context.website?.availableAIAgents || [];\n\tconst visitorLanguage = context.website?.visitor?.language || null;\n\n\t// Get additional config from support store\n\tconst { config } = useSupportStore();\n\n\t// Create visitor object with normalized locale\n\tconst visitor = context.website?.visitor\n\t\t? {\n\t\t\t\t...context.website.visitor,\n\t\t\t\tlocale: normalizeLocale(visitorLanguage),\n\t\t\t}\n\t\t: undefined;\n\n\treturn {\n\t\t...context,\n\t\tavailableHumanAgents,\n\t\tavailableAIAgents,\n\t\tvisitor,\n\t\tsize: config.size,\n\t};\n}\n\n// Re-export ConfigurationError type for consumers\nexport type { ConfigurationError } from \"./hooks/private/use-rest-client\";\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"avatar-stack.js","names":[],"sources":["../../../src/support/components/avatar-stack.tsx"],"sourcesContent":["import type { AvailableAIAgent, AvailableHumanAgent } from \"@cossistant/types\";\nimport type { ReactElement, ReactNode } from \"react\";\nimport { useRenderElement } from \"../../utils/use-render-element\";\nimport { cn } from \"../utils\";\nimport { Avatar } from \"./avatar\";\n\ntype AvatarStackProps = {\n\thumanAgents: AvailableHumanAgent[];\n\taiAgents: AvailableAIAgent[];\n\thideBranding?: boolean;\n\thideDefaultAIAgent?: boolean;\n\tclassName?: string;\n\t/** Size of avatars (default: 44px) */\n\tsize?: number;\n\t/** Space between avatars (default: 28px) */\n\tspacing?: number;\n\t/** Gap width between avatars (default: 2px) */\n\tgapWidth?: number;\n};\n\n/**\n * Creates an SVG mask with a rounded rectangle cutout on the left side.\n * This respects the border radius of the avatars.\n */\nfunction createRoundedCutoutMask(\n\tsize: number,\n\tcutoutWidth: number,\n\tborderRadius: number\n): string {\n\t// SVG mask: white = visible, black = hidden\n\t// We create a white rectangle (full size) and subtract a rounded rect on the left\n\t// The cutout rect is extended beyond top/bottom bounds so only the right-side curve is visible\n\tconst extension = borderRadius * 0.15;\n\tconst svg = `\n\t\t<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"${size}\" height=\"${size}\">\n\t\t\t<defs>\n\t\t\t\t<mask id=\"m\">\n\t\t\t\t\t<rect width=\"${size}\" height=\"${size}\" fill=\"white\"/>\n\t\t\t\t\t<rect x=\"${-size + cutoutWidth}\" y=\"${-extension}\" width=\"${size}\" height=\"${size + extension * 2}\" rx=\"${borderRadius}\" ry=\"${borderRadius}\" fill=\"black\"/>\n\t\t\t\t</mask>\n\t\t\t</defs>\n\t\t\t<rect width=\"${size}\" height=\"${size}\" fill=\"white\" mask=\"url(#m)\"/>\n\t\t</svg>\n\t`.replace(/\\s+/g, \" \");\n\n\treturn `url(\"data:image/svg+xml,${encodeURIComponent(svg)}\")`;\n}\n\nexport const AvatarStackItem = ({\n\tchildren,\n\tindex,\n\tsize = 44,\n\tspacing = 32,\n\tgapWidth = 1,\n\tclassName,\n}: {\n\tchildren: ReactNode;\n\tindex: number;\n\tsize?: number;\n\tspacing?: number;\n\tgapWidth?: number;\n\tclassName?: string;\n}): ReactElement | null => {\n\tconst isFirst = index === 0;\n\n\t// Calculate mask for squared avatars with rounded corners\n\t// The mask creates a cutout on the left side where the previous avatar overlaps\n\tconst cutoutWidth = size - spacing + gapWidth;\n\tconst borderRadius = 4; // Match the 4px border radius used on avatars\n\n\tconst maskImage = createRoundedCutoutMask(size, cutoutWidth, borderRadius);\n\n\treturn useRenderElement(\n\t\t\"div\",\n\t\t{ className },\n\t\t{\n\t\t\tprops: {\n\t\t\t\tclassName: cn(\n\t\t\t\t\t\"relative grid place-items-center\",\n\t\t\t\t\t!isFirst && \"[mask-repeat:no-repeat] [mask-size:100%_100%]\"\n\t\t\t\t),\n\t\t\t\tstyle: {\n\t\t\t\t\twidth: `${size}px`,\n\t\t\t\t\theight: `${size}px`,\n\t\t\t\t\t// Apply mask only to non-first items - uses SVG for rounded cutout\n\t\t\t\t\t...(isFirst\n\t\t\t\t\t\t? {}\n\t\t\t\t\t\t: {\n\t\t\t\t\t\t\t\tmaskImage,\n\t\t\t\t\t\t\t\tWebkitMaskImage: maskImage,\n\t\t\t\t\t\t\t}),\n\t\t\t\t},\n\t\t\t\tchildren,\n\t\t\t},\n\t\t}\n\t);\n};\n\n/**\n * Displays a compact row of agent avatars with optional branding and overflow\n * counts.\n */\nexport function AvatarStack({\n\thumanAgents,\n\taiAgents,\n\thideBranding = false,\n\thideDefaultAIAgent = true,\n\tclassName,\n\tsize = 44,\n\tspacing = 36,\n\tgapWidth = 3,\n}: AvatarStackProps): ReactElement | null {\n\tconst displayedHumanAgents = humanAgents.slice(0, 2);\n\tconst remainingHumanAgentsCount = Math.max(0, humanAgents.length - 2);\n\n\t// Create array of all items to display\n\tconst items = [\n\t\t...displayedHumanAgents.map((agent) => ({\n\t\t\ttype: \"human\" as const,\n\t\t\tagent,\n\t\t})),\n\t\t...(remainingHumanAgentsCount > 0\n\t\t\t? [\n\t\t\t\t\t{\n\t\t\t\t\t\ttype: \"count\" as const,\n\t\t\t\t\t\tcount: remainingHumanAgentsCount,\n\t\t\t\t\t},\n\t\t\t\t]\n\t\t\t: []),\n\t\t...(hideDefaultAIAgent\n\t\t\t? []\n\t\t\t: [\n\t\t\t\t\t{\n\t\t\t\t\t\ttype: \"ai\" as const,\n\t\t\t\t\t\tagent: aiAgents[0],\n\t\t\t\t\t},\n\t\t\t\t]),\n\t];\n\n\treturn useRenderElement(\n\t\t\"div\",\n\t\t{ className },\n\t\t{\n\t\t\tprops: {\n\t\t\t\tclassName: \"inline-grid items-center\",\n\t\t\t\tstyle: {\n\t\t\t\t\tgridTemplateColumns: `repeat(${items.length}, ${spacing}px)`,\n\t\t\t\t},\n\t\t\t\tchildren: items.map((item, index) => (\n\t\t\t\t\t<AvatarStackItem\n\t\t\t\t\t\tgapWidth={gapWidth}\n\t\t\t\t\t\tindex={index}\n\t\t\t\t\t\tkey={`avatar-${index}`}\n\t\t\t\t\t\tsize={size}\n\t\t\t\t\t\tspacing={spacing}\n\t\t\t\t\t>\n\t\t\t\t\t\t{item.type === \"human\" && (\n\t\t\t\t\t\t\t<Avatar\n\t\t\t\t\t\t\t\tclassName={cn(\"size-full\")}\n\t\t\t\t\t\t\t\timage={item.agent.image}\n\t\t\t\t\t\t\t\tlastSeenAt={item.agent.lastSeenAt}\n\t\t\t\t\t\t\t\tname={item.agent.name}\n\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t)}\n\t\t\t\t\t\t{item.type === \"count\" && (\n\t\t\t\t\t\t\t<div className=\"flex size-full items-center justify-center rounded bg-co-background-200 font-medium text-co-primary text-sm ring-1 ring-co-border/30 dark:bg-co-background-500\">\n\t\t\t\t\t\t\t\t+{item.count}\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t)}\n\t\t\t\t\t\t{item.type === \"ai\" && (\n\t\t\t\t\t\t\t<Avatar\n\t\t\t\t\t\t\t\tclassName=\"z-0 size-full\"\n\t\t\t\t\t\t\t\timage={item.agent?.image}\n\t\t\t\t\t\t\t\tisAI\n\t\t\t\t\t\t\t\tname={item.agent?.name || \"AI\"}\n\t\t\t\t\t\t\t\tshowBackground
|
|
1
|
+
{"version":3,"file":"avatar-stack.js","names":[],"sources":["../../../src/support/components/avatar-stack.tsx"],"sourcesContent":["import type { AvailableAIAgent, AvailableHumanAgent } from \"@cossistant/types\";\nimport type { ReactElement, ReactNode } from \"react\";\nimport { useRenderElement } from \"../../utils/use-render-element\";\nimport { cn } from \"../utils\";\nimport { Avatar } from \"./avatar\";\n\ntype AvatarStackProps = {\n\thumanAgents: AvailableHumanAgent[];\n\taiAgents: AvailableAIAgent[];\n\thideBranding?: boolean;\n\thideDefaultAIAgent?: boolean;\n\tclassName?: string;\n\t/** Size of avatars (default: 44px) */\n\tsize?: number;\n\t/** Space between avatars (default: 28px) */\n\tspacing?: number;\n\t/** Gap width between avatars (default: 2px) */\n\tgapWidth?: number;\n};\n\n/**\n * Creates an SVG mask with a rounded rectangle cutout on the left side.\n * This respects the border radius of the avatars.\n */\nfunction createRoundedCutoutMask(\n\tsize: number,\n\tcutoutWidth: number,\n\tborderRadius: number\n): string {\n\t// SVG mask: white = visible, black = hidden\n\t// We create a white rectangle (full size) and subtract a rounded rect on the left\n\t// The cutout rect is extended beyond top/bottom bounds so only the right-side curve is visible\n\tconst extension = borderRadius * 0.15;\n\tconst svg = `\n\t\t<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"${size}\" height=\"${size}\">\n\t\t\t<defs>\n\t\t\t\t<mask id=\"m\">\n\t\t\t\t\t<rect width=\"${size}\" height=\"${size}\" fill=\"white\"/>\n\t\t\t\t\t<rect x=\"${-size + cutoutWidth}\" y=\"${-extension}\" width=\"${size}\" height=\"${size + extension * 2}\" rx=\"${borderRadius}\" ry=\"${borderRadius}\" fill=\"black\"/>\n\t\t\t\t</mask>\n\t\t\t</defs>\n\t\t\t<rect width=\"${size}\" height=\"${size}\" fill=\"white\" mask=\"url(#m)\"/>\n\t\t</svg>\n\t`.replace(/\\s+/g, \" \");\n\n\treturn `url(\"data:image/svg+xml,${encodeURIComponent(svg)}\")`;\n}\n\nexport const AvatarStackItem = ({\n\tchildren,\n\tindex,\n\tsize = 44,\n\tspacing = 32,\n\tgapWidth = 1,\n\tclassName,\n}: {\n\tchildren: ReactNode;\n\tindex: number;\n\tsize?: number;\n\tspacing?: number;\n\tgapWidth?: number;\n\tclassName?: string;\n}): ReactElement | null => {\n\tconst isFirst = index === 0;\n\n\t// Calculate mask for squared avatars with rounded corners\n\t// The mask creates a cutout on the left side where the previous avatar overlaps\n\tconst cutoutWidth = size - spacing + gapWidth;\n\tconst borderRadius = 4; // Match the 4px border radius used on avatars\n\n\tconst maskImage = createRoundedCutoutMask(size, cutoutWidth, borderRadius);\n\n\treturn useRenderElement(\n\t\t\"div\",\n\t\t{ className },\n\t\t{\n\t\t\tprops: {\n\t\t\t\tclassName: cn(\n\t\t\t\t\t\"relative grid place-items-center\",\n\t\t\t\t\t!isFirst && \"[mask-repeat:no-repeat] [mask-size:100%_100%]\"\n\t\t\t\t),\n\t\t\t\tstyle: {\n\t\t\t\t\twidth: `${size}px`,\n\t\t\t\t\theight: `${size}px`,\n\t\t\t\t\t// Apply mask only to non-first items - uses SVG for rounded cutout\n\t\t\t\t\t...(isFirst\n\t\t\t\t\t\t? {}\n\t\t\t\t\t\t: {\n\t\t\t\t\t\t\t\tmaskImage,\n\t\t\t\t\t\t\t\tWebkitMaskImage: maskImage,\n\t\t\t\t\t\t\t}),\n\t\t\t\t},\n\t\t\t\tchildren,\n\t\t\t},\n\t\t}\n\t);\n};\n\n/**\n * Displays a compact row of agent avatars with optional branding and overflow\n * counts.\n */\nexport function AvatarStack({\n\thumanAgents,\n\taiAgents,\n\thideBranding = false,\n\thideDefaultAIAgent = true,\n\tclassName,\n\tsize = 44,\n\tspacing = 36,\n\tgapWidth = 3,\n}: AvatarStackProps): ReactElement | null {\n\tconst displayedHumanAgents = humanAgents.slice(0, 2);\n\tconst remainingHumanAgentsCount = Math.max(0, humanAgents.length - 2);\n\n\t// Create array of all items to display\n\tconst items = [\n\t\t...displayedHumanAgents.map((agent) => ({\n\t\t\ttype: \"human\" as const,\n\t\t\tagent,\n\t\t})),\n\t\t...(remainingHumanAgentsCount > 0\n\t\t\t? [\n\t\t\t\t\t{\n\t\t\t\t\t\ttype: \"count\" as const,\n\t\t\t\t\t\tcount: remainingHumanAgentsCount,\n\t\t\t\t\t},\n\t\t\t\t]\n\t\t\t: []),\n\t\t...(hideDefaultAIAgent\n\t\t\t? []\n\t\t\t: [\n\t\t\t\t\t{\n\t\t\t\t\t\ttype: \"ai\" as const,\n\t\t\t\t\t\tagent: aiAgents[0],\n\t\t\t\t\t},\n\t\t\t\t]),\n\t];\n\n\treturn useRenderElement(\n\t\t\"div\",\n\t\t{ className },\n\t\t{\n\t\t\tprops: {\n\t\t\t\tclassName: \"inline-grid items-center\",\n\t\t\t\tstyle: {\n\t\t\t\t\tgridTemplateColumns: `repeat(${items.length}, ${spacing}px)`,\n\t\t\t\t},\n\t\t\t\tchildren: items.map((item, index) => (\n\t\t\t\t\t<AvatarStackItem\n\t\t\t\t\t\tgapWidth={gapWidth}\n\t\t\t\t\t\tindex={index}\n\t\t\t\t\t\tkey={`avatar-${index}`}\n\t\t\t\t\t\tsize={size}\n\t\t\t\t\t\tspacing={spacing}\n\t\t\t\t\t>\n\t\t\t\t\t\t{item.type === \"human\" && (\n\t\t\t\t\t\t\t<Avatar\n\t\t\t\t\t\t\t\tclassName={cn(\"size-full\")}\n\t\t\t\t\t\t\t\timage={item.agent.image}\n\t\t\t\t\t\t\t\tlastSeenAt={item.agent.lastSeenAt}\n\t\t\t\t\t\t\t\tname={item.agent.name}\n\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t)}\n\t\t\t\t\t\t{item.type === \"count\" && (\n\t\t\t\t\t\t\t<div className=\"flex size-full items-center justify-center rounded bg-co-background-200 font-medium text-co-primary text-sm ring-1 ring-co-border/30 dark:bg-co-background-500\">\n\t\t\t\t\t\t\t\t+{item.count}\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t)}\n\t\t\t\t\t\t{item.type === \"ai\" && (\n\t\t\t\t\t\t\t<Avatar\n\t\t\t\t\t\t\t\tclassName=\"z-0 size-full\"\n\t\t\t\t\t\t\t\timage={item.agent?.image}\n\t\t\t\t\t\t\t\tisAI\n\t\t\t\t\t\t\t\tname={item.agent?.name || \"AI\"}\n\t\t\t\t\t\t\t\tshowBackground\n\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t)}\n\t\t\t\t\t</AvatarStackItem>\n\t\t\t\t)),\n\t\t\t},\n\t\t}\n\t);\n}\n"],"mappings":";;;;;;;;;;AAwBA,SAAS,wBACR,MACA,aACA,cACS;CAIT,MAAM,YAAY,eAAe;CACjC,MAAM,MAAM;mDACsC,KAAK,YAAY,KAAK;;;oBAGrD,KAAK,YAAY,KAAK;gBAC1B,CAAC,OAAO,YAAY,OAAO,CAAC,UAAU,WAAW,KAAK,YAAY,OAAO,YAAY,EAAE,QAAQ,aAAa,QAAQ,aAAa;;;kBAG/H,KAAK,YAAY,KAAK;;GAErC,QAAQ,QAAQ,IAAI;AAEtB,QAAO,2BAA2B,mBAAmB,IAAI,CAAC;;AAG3D,MAAa,mBAAmB,EAC/B,UACA,OACA,OAAO,IACP,UAAU,IACV,WAAW,GACX,gBAQ0B;CAC1B,MAAM,UAAU,UAAU;CAO1B,MAAM,YAAY,wBAAwB,MAHtB,OAAO,UAAU,UAChB,EAEqD;AAE1E,QAAO,iBACN,OACA,EAAE,WAAW,EACb,EACC,OAAO;EACN,WAAW,GACV,oCACA,CAAC,WAAW,gDACZ;EACD,OAAO;GACN,OAAO,GAAG,KAAK;GACf,QAAQ,GAAG,KAAK;GAEhB,GAAI,UACD,EAAE,GACF;IACA;IACA,iBAAiB;IACjB;GACH;EACD;EACA,EACD,CACD;;;;;;AAOF,SAAgB,YAAY,EAC3B,aACA,UACA,eAAe,OACf,qBAAqB,MACrB,WACA,OAAO,IACP,UAAU,IACV,WAAW,KAC8B;CACzC,MAAM,uBAAuB,YAAY,MAAM,GAAG,EAAE;CACpD,MAAM,4BAA4B,KAAK,IAAI,GAAG,YAAY,SAAS,EAAE;CAGrE,MAAM,QAAQ;EACb,GAAG,qBAAqB,KAAK,WAAW;GACvC,MAAM;GACN;GACA,EAAE;EACH,GAAI,4BAA4B,IAC7B,CACA;GACC,MAAM;GACN,OAAO;GACP,CACD,GACA,EAAE;EACL,GAAI,qBACD,EAAE,GACF,CACA;GACC,MAAM;GACN,OAAO,SAAS;GAChB,CACD;EACH;AAED,QAAO,iBACN,OACA,EAAE,WAAW,EACb,EACC,OAAO;EACN,WAAW;EACX,OAAO,EACN,qBAAqB,UAAU,MAAM,OAAO,IAAI,QAAQ,MACxD;EACD,UAAU,MAAM,KAAK,MAAM,UAC1B,qBAAC;GACU;GACH;GAED;GACG;;IAER,KAAK,SAAS,WACd,oBAAC;KACA,WAAW,GAAG,YAAY;KAC1B,OAAO,KAAK,MAAM;KAClB,YAAY,KAAK,MAAM;KACvB,MAAM,KAAK,MAAM;MAChB;IAEF,KAAK,SAAS,WACd,qBAAC;KAAI,WAAU;gBAAiK,KAC7K,KAAK;MACF;IAEN,KAAK,SAAS,QACd,oBAAC;KACA,WAAU;KACV,OAAO,KAAK,OAAO;KACnB;KACA,MAAM,KAAK,OAAO,QAAQ;KAC1B;MACC;;KAxBE,UAAU,QA0BE,CACjB;EACF,EACD,CACD"}
|
|
@@ -30,8 +30,7 @@ type AvatarProps = {
|
|
|
30
30
|
};
|
|
31
31
|
/**
|
|
32
32
|
* Renders a squared avatar with graceful fallbacks using Facehash when no
|
|
33
|
-
* image is available. Features
|
|
34
|
-
* and a subtle ring border.
|
|
33
|
+
* image is available. Features rounded corners and a subtle ring border.
|
|
35
34
|
*
|
|
36
35
|
* For AI agents without an image, displays the Cossistant logo without
|
|
37
36
|
* a background.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"avatar.d.ts","names":[],"sources":["../../../src/support/components/avatar.tsx"],"sourcesContent":[],"mappings":";;;KAuBK,WAAA;;EAAA,KAAA,CAAA,EAAA,MAAW,GAAA,IAAA;
|
|
1
|
+
{"version":3,"file":"avatar.d.ts","names":[],"sources":["../../../src/support/components/avatar.tsx"],"sourcesContent":[],"mappings":";;;KAuBK,WAAA;;EAAA,KAAA,CAAA,EAAA,MAAW,GAAA,IAAA;EAmCA,IAAA,EAAA,MAAM;EACrB;EACA,IAAA,CAAA,EAAA,OAAA;EACA;EACA,cAAA,CAAA,EAAA,OAAA;EACA;;;;;EAIgB,YAAA,CAAA,EAAA,MAAA,EAAA;EAAY;;;;;;;;;;;;;;;;;;;;iBATb,MAAA;;;;;;;;;GASb,cAAc"}
|