@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.
Files changed (77) hide show
  1. package/README.md +3 -1
  2. package/hooks/private/use-grouped-messages.d.ts.map +1 -1
  3. package/hooks/private/use-grouped-messages.js +41 -12
  4. package/hooks/private/use-grouped-messages.js.map +1 -1
  5. package/hooks/use-conversation-page.js +4 -2
  6. package/hooks/use-conversation-page.js.map +1 -1
  7. package/hooks/use-conversation-preview.d.ts.map +1 -1
  8. package/hooks/use-conversation-preview.js +2 -1
  9. package/hooks/use-conversation-preview.js.map +1 -1
  10. package/hooks/use-send-message.js +1 -1
  11. package/hooks/use-send-message.js.map +1 -1
  12. package/package.json +5 -4
  13. package/packages/types/src/api/conversation.d.ts +13 -3
  14. package/packages/types/src/api/conversation.d.ts.map +1 -1
  15. package/packages/types/src/api/timeline-item.d.ts +2 -0
  16. package/packages/types/src/api/timeline-item.d.ts.map +1 -1
  17. package/packages/types/src/realtime-events.d.ts +29 -4
  18. package/packages/types/src/realtime-events.d.ts.map +1 -1
  19. package/packages/types/src/schemas.d.ts +4 -1
  20. package/packages/types/src/schemas.d.ts.map +1 -1
  21. package/primitives/avatar/image.d.ts +1 -1
  22. package/primitives/multimodal-input.d.ts +2 -2
  23. package/primitives/multimodal-input.d.ts.map +1 -1
  24. package/primitives/timeline-item.d.ts +2 -2
  25. package/primitives/timeline-item.d.ts.map +1 -1
  26. package/primitives/timeline-item.js +30 -9
  27. package/primitives/timeline-item.js.map +1 -1
  28. package/provider.d.ts.map +1 -1
  29. package/provider.js +6 -3
  30. package/provider.js.map +1 -1
  31. package/support/components/avatar-stack.js +1 -1
  32. package/support/components/avatar-stack.js.map +1 -1
  33. package/support/components/avatar.d.ts +1 -2
  34. package/support/components/avatar.d.ts.map +1 -1
  35. package/support/components/avatar.js +9 -7
  36. package/support/components/avatar.js.map +1 -1
  37. package/support/components/button.d.ts +1 -1
  38. package/support/components/conversation-button-link.js +2 -1
  39. package/support/components/conversation-button-link.js.map +1 -1
  40. package/support/components/conversation-event.js +1 -1
  41. package/support/components/conversation-event.js.map +1 -1
  42. package/support/components/conversation-resolved-feedback.d.ts +21 -0
  43. package/support/components/conversation-resolved-feedback.d.ts.map +1 -0
  44. package/support/components/conversation-resolved-feedback.js +102 -0
  45. package/support/components/conversation-resolved-feedback.js.map +1 -0
  46. package/support/components/conversation-timeline-utils.d.ts +5 -0
  47. package/support/components/conversation-timeline-utils.d.ts.map +1 -0
  48. package/support/components/conversation-timeline-utils.js +10 -0
  49. package/support/components/conversation-timeline-utils.js.map +1 -0
  50. package/support/components/conversation-timeline.d.ts.map +1 -1
  51. package/support/components/conversation-timeline.js +2 -1
  52. package/support/components/conversation-timeline.js.map +1 -1
  53. package/support/components/icons.d.ts +1 -1
  54. package/support/components/icons.d.ts.map +1 -1
  55. package/support/components/icons.js +6 -2
  56. package/support/components/icons.js.map +1 -1
  57. package/support/components/index.d.ts +2 -1
  58. package/support/components/index.js +2 -1
  59. package/support/components/typing-indicator.d.ts.map +1 -1
  60. package/support/components/typing-indicator.js +15 -7
  61. package/support/components/typing-indicator.js.map +1 -1
  62. package/support/pages/conversation-history.js +1 -1
  63. package/support/pages/conversation.d.ts.map +1 -1
  64. package/support/pages/conversation.js +36 -8
  65. package/support/pages/conversation.js.map +1 -1
  66. package/support/pages/home.js +1 -1
  67. package/support/text/locales/en.js +12 -0
  68. package/support/text/locales/en.js.map +1 -1
  69. package/support/text/locales/es.js +12 -0
  70. package/support/text/locales/es.js.map +1 -1
  71. package/support/text/locales/fr.js +12 -0
  72. package/support/text/locales/fr.js.map +1 -1
  73. package/support/text/locales/keys.d.ts +20 -0
  74. package/support/text/locales/keys.d.ts.map +1 -1
  75. package/support/text/locales/keys.js +6 -0
  76. package/support/text/locales/keys.js.map +1 -1
  77. 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":";;;;;;;;;;cAqBa;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;KA8VD,iBAAA,gBAAiC;KAEjC,+BAA+B,qBAAqB,eACvD,gBAAgB;KAGb,wBAAwB;QAC7B;WACG,qBAAqB;;KAGnB,gBAAA,WACL,oBAAoB,cAAc,KACvC;KAEU,4BAA4B,qBACvC,qBAAqB"}
1
+ {"version":3,"file":"realtime-events.d.ts","names":[],"sources":["../../../../../types/src/realtime-events.ts"],"sourcesContent":[],"mappings":";;;;;;;;;;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;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;KAmBnB,YAAA,GAAe,cAAe;cAE7B,wBAAsB;;;;;;EArBJ,UAAA,WAAA;EAAA,SAAA,WAAA;EAmBnB,SAAA,WAAY;EAEX,SAAA,aAAA,UAUX,CAAA;;KAEU,gBAAA,GAAmB,cAAe"}
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>, "alt" | "src"> & {
18
+ declare const AvatarImage: React$1.ForwardRefExoticComponent<Omit<React$1.ImgHTMLAttributes<HTMLImageElement>, "src" | "alt"> & {
19
19
  src: string;
20
20
  alt?: string;
21
21
  asChild?: boolean;
@@ -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>, "value" | "onChange"> & {
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>, "value" | "onChange"> & {
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,OAAA,GAAA,UAAA,CAAA,GAAA;;;EAmKgB,QAAA,CAAA,EAAA,GAAA,GAAA,IAAA;EAAA,YAAA,CAAA,EAAA,CAAA,KAAA,EAtKD,IAsKC,EAAA,EAAA,GAAA,IAAA;;;UAnKhB"}
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
- * both MESSAGE and EVENT timeline item types.
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":";;;;;;;AAUA;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;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;;;;;;AAuK7B,cA5JC,YA4JuB,EA5JX,OAAA,CAAA,yBA4JW,CA5JX,IA4JW,CA5JX,OAAA,CAAA,cA4JW,CA5JX,cA4JW,CAAA,EAAA,UAAA,CAAA,GAAA;EACd,QAAA,CAAA,EAzKlB,OAAA,CAAM,SAyKY,GAAA,CAAA,CAAA,KAAA,EAxKT,uBAwKS,EAAA,GAxKmB,OAAA,CAAM,SAwKzB,CAAA;EAArB,OAAM,CAAA,EAAA,OAAA;EADgC,SAAA,CAAA,EAAA,MAAA;EAI3B,IAAA,EAxKL,cAwKW;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
+ {"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
- * both MESSAGE and EVENT timeline item types.
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 }) => /* @__PURE__ */ jsx("a", {
101
- className: "underline hover:opacity-80",
102
- href,
103
- rel: "noopener noreferrer",
104
- target: "_blank",
105
- children
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;EA6CF,cAAA,EAAA,CAAA,KAAe,EAAA,MAAA,EAAA,GAAA,IAAA;EAAG,SAAA,EAAA,OAAA;EACK,KAAA,EA3D3B,KA2D2B,GAAA,IAAA;EAAZ,kBAAA,EA1DF,kBA0DE,GAAA,IAAA;EACS,MAAA,EA1DvB,gBA0DuB,GAAA,IAAA;EAAZ,MAAA,EAAA,OAAA;EACT,IAAA,EAAA,GAAA,GAAA,IAAA;EAAiB,KAAA,EAAA,GAAA,GAAA,IAAA;EAIf,MAAA,EAAA,GAAA,GAAA,IAED;AAqVZ,CAAA;KA/YK,WAAA,GAAc,WAgZlB,CAhZ8B,sBAgZ9B,CAAA,SAAA,CAAA,CAAA;KA9YI,iBAAA,GAAoB,WA+YxB,CAAA,SAAA,CAAA,SAAA,IAAA,GAAA,SAAA,GAAA,SAAA,GA7YE,WA6YF,CA7Yc,WA6Yd,CAAA,SAAA,CAAA,CAAA,GAAA;EACA,MAAA,EAAA,MAAA,GAAA,IAAA;CACA;AACA,KAnWW,eAAA,GAAkB,sBAmW7B,GAAA;EACA,oBAAA,EAnWsB,WAmWtB,CAnWkC,WAmWlC,CAAA,sBAAA,CAAA,CAAA,GAAA,EAAA;EACA,iBAAA,EAnWmB,WAmWnB,CAnW+B,WAmW/B,CAAA,mBAAA,CAAA,CAAA,GAAA,EAAA;EACA,OAAA,CAAA,EAnWU,iBAmWV;EACA,IAAA,EAAA,QAAA,GAAA,QAAA;CACA;AACA,cAlWY,cAkWZ,EAlW0B,KAAA,CAAA,OAkW1B,CAlW0B,sBAkW1B,GAAA,SAAA,CAAA;;;;;AA0BD;;iBArCgB,eAAA;;;;;;;;;;;;;GAab,uBAAuB,KAAA,CAAM;;;;;iBAwBhB,UAAA,CAAA,GAAc"}
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":"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"}
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":";;;;;;;;;;;;;;;;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;;AAoDtE,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,qBAC1C,UAAU,WAAW,UAAU,UAC/B,UAAU,cAAc,UAAU,UAElC,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,UAeA,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,QAAQ,aAAa,UAAU;GAC/B,WAAW,aAAa,aAAa;GACrC;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,mBACA,QACA,eACI,uBAAuB;AAE3B,OAAI,WAAW,UAAU,UACxB;AAGD,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"}
@@ -84,7 +84,7 @@ function AvatarStack({ humanAgents, aiAgents, hideBranding = false, hideDefaultA
84
84
  image: item.agent?.image,
85
85
  isAI: true,
86
86
  name: item.agent?.name || "AI",
87
- showBackground: !!item.agent?.image
87
+ showBackground: true
88
88
  })
89
89
  ]
90
90
  }, `avatar-${index}`))
@@ -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={!!item.agent?.image}\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,gBAAgB,CAAC,CAAC,KAAK,OAAO;MAC7B;;KAxBE,UAAU,QA0BE,CACjB;EACF,EACD,CACD"}
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 squircle corners when supported by the browser
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;EAoCA,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"}
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"}