@cossistant/react 0.0.29 → 0.0.30
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/_virtual/rolldown_runtime.js +9 -23
- package/hooks/index.d.ts +2 -2
- package/hooks/private/store/use-conversations-store.d.ts +2 -0
- package/hooks/private/store/use-conversations-store.d.ts.map +1 -1
- package/hooks/private/store/use-conversations-store.js +15 -8
- package/hooks/private/store/use-conversations-store.js.map +1 -1
- package/hooks/private/store/use-store-selector.d.ts +3 -0
- package/hooks/private/store/use-store-selector.d.ts.map +1 -1
- package/hooks/private/store/use-store-selector.js +4 -8
- package/hooks/private/store/use-store-selector.js.map +1 -1
- package/hooks/private/store/use-website-store.d.ts +3 -1
- package/hooks/private/store/use-website-store.d.ts.map +1 -1
- package/hooks/private/store/use-website-store.js +14 -6
- package/hooks/private/store/use-website-store.js.map +1 -1
- package/hooks/private/use-client-query.d.ts +1 -1
- package/hooks/private/use-client-query.d.ts.map +1 -1
- package/hooks/private/use-client-query.js +1 -0
- package/hooks/private/use-client-query.js.map +1 -1
- package/hooks/private/use-default-messages.d.ts +1 -1
- package/hooks/private/use-grouped-messages.d.ts +2 -2
- package/hooks/private/use-rest-client.d.ts +13 -3
- package/hooks/private/use-rest-client.d.ts.map +1 -1
- package/hooks/private/use-rest-client.js +49 -22
- package/hooks/private/use-rest-client.js.map +1 -1
- package/hooks/private/use-visitor-typing-reporter.d.ts +1 -1
- package/hooks/use-conversation-auto-seen.d.ts +1 -1
- package/hooks/use-conversation-page.d.ts +1 -1
- package/hooks/use-conversation-page.d.ts.map +1 -1
- package/hooks/use-conversation-page.js +10 -3
- package/hooks/use-conversation-page.js.map +1 -1
- package/hooks/use-conversation-preview.d.ts +3 -1
- package/hooks/use-conversation-preview.d.ts.map +1 -1
- package/hooks/use-conversation-preview.js +6 -3
- package/hooks/use-conversation-preview.js.map +1 -1
- package/hooks/use-conversation-seen.d.ts +1 -1
- package/hooks/use-conversation-timeline-items.d.ts +1 -1
- package/hooks/use-conversation-timeline-items.js +2 -3
- package/hooks/use-conversation-timeline-items.js.map +1 -1
- package/hooks/use-conversation-timeline.d.ts +1 -1
- package/hooks/use-conversation.d.ts +1 -1
- package/hooks/use-conversation.js +2 -3
- package/hooks/use-conversation.js.map +1 -1
- package/hooks/use-conversations.d.ts +1 -1
- package/hooks/use-conversations.js +5 -3
- package/hooks/use-conversations.js.map +1 -1
- package/hooks/use-create-conversation.d.ts +3 -3
- package/hooks/use-create-conversation.js +1 -0
- package/hooks/use-create-conversation.js.map +1 -1
- package/hooks/use-file-upload.d.ts +1 -1
- package/hooks/use-file-upload.js +3 -3
- package/hooks/use-file-upload.js.map +1 -1
- package/hooks/use-home-page.js +3 -3
- package/hooks/use-home-page.js.map +1 -1
- package/hooks/use-message-composer.d.ts +10 -3
- package/hooks/use-message-composer.d.ts.map +1 -1
- package/hooks/use-message-composer.js +5 -2
- package/hooks/use-message-composer.js.map +1 -1
- package/hooks/use-realtime-support.d.ts +1 -1
- package/hooks/use-send-message.d.ts +8 -2
- package/hooks/use-send-message.d.ts.map +1 -1
- package/hooks/use-send-message.js +5 -3
- package/hooks/use-send-message.js.map +1 -1
- package/hooks/use-visitor.js +2 -2
- package/hooks/use-visitor.js.map +1 -1
- package/identify-visitor.d.ts.map +1 -1
- package/identify-visitor.js +15 -1
- package/identify-visitor.js.map +1 -1
- package/index.d.ts +2 -2
- package/index.js +1 -1
- package/package.json +5 -3
- package/{conversation.d.ts → packages/types/src/api/conversation.d.ts} +365 -61
- package/packages/types/src/api/conversation.d.ts.map +1 -0
- package/packages/types/src/api/timeline-item.d.ts +460 -0
- package/packages/types/src/api/timeline-item.d.ts.map +1 -0
- package/{realtime-events.d.ts → packages/types/src/realtime-events.d.ts} +443 -45
- package/packages/types/src/realtime-events.d.ts.map +1 -0
- package/{schemas3.d.ts → packages/types/src/schemas.d.ts} +94 -18
- package/packages/types/src/schemas.d.ts.map +1 -0
- package/primitives/avatar/avatar.js +1 -1
- package/primitives/avatar/avatar.js.map +1 -1
- package/primitives/avatar/fallback.js +1 -1
- package/primitives/avatar/fallback.js.map +1 -1
- package/primitives/avatar/image.js +1 -1
- package/primitives/avatar/image.js.map +1 -1
- package/primitives/button.js +1 -1
- package/primitives/button.js.map +1 -1
- package/primitives/conversation-timeline.d.ts +1 -1
- package/primitives/conversation-timeline.js +4 -4
- package/primitives/conversation-timeline.js.map +1 -1
- package/primitives/day-separator.js +3 -3
- package/primitives/day-separator.js.map +1 -1
- package/primitives/multimodal-input.js +2 -2
- package/primitives/multimodal-input.js.map +1 -1
- package/primitives/timeline-item-attachments.d.ts +1 -1
- package/primitives/timeline-item-attachments.js +6 -7
- package/primitives/timeline-item-attachments.js.map +1 -1
- package/primitives/timeline-item-group.d.ts +1 -1
- package/primitives/timeline-item-group.js +7 -7
- package/primitives/timeline-item-group.js.map +1 -1
- package/primitives/timeline-item.d.ts +1 -1
- package/primitives/timeline-item.d.ts.map +1 -1
- package/primitives/timeline-item.js +33 -8
- package/primitives/timeline-item.js.map +1 -1
- package/primitives/trigger.js +1 -1
- package/primitives/trigger.js.map +1 -1
- package/primitives/window.js +1 -1
- package/primitives/window.js.map +1 -1
- package/provider.d.ts +4 -2
- package/provider.d.ts.map +1 -1
- package/provider.js +56 -8
- package/provider.js.map +1 -1
- package/realtime/event-filter.d.ts +4 -1
- package/realtime/event-filter.d.ts.map +1 -1
- package/realtime/event-filter.js +14 -0
- package/realtime/event-filter.js.map +1 -1
- package/realtime/provider.d.ts +1 -1
- package/realtime/provider.d.ts.map +1 -1
- package/realtime/provider.js +1 -2
- package/realtime/provider.js.map +1 -1
- package/realtime/seen-store.d.ts +2 -2
- package/realtime/support-provider.js +3 -2
- package/realtime/support-provider.js.map +1 -1
- package/realtime/typing-store.d.ts +1 -1
- package/realtime/use-realtime.d.ts +1 -1
- package/support/components/avatar-stack.d.ts.map +1 -1
- package/support/components/avatar-stack.js +32 -12
- package/support/components/avatar-stack.js.map +1 -1
- package/support/components/avatar.d.ts +34 -3
- package/support/components/avatar.d.ts.map +1 -1
- package/support/components/avatar.js +61 -8
- package/support/components/avatar.js.map +1 -1
- package/support/components/button.d.ts +3 -1
- package/support/components/button.d.ts.map +1 -1
- package/support/components/button.js +3 -3
- package/support/components/button.js.map +1 -1
- package/support/components/configuration-error.d.ts +16 -0
- package/support/components/configuration-error.d.ts.map +1 -0
- package/support/components/configuration-error.js +162 -0
- package/support/components/configuration-error.js.map +1 -0
- package/support/components/content.js +1 -2
- package/support/components/content.js.map +1 -1
- package/support/components/conversation-button-link.js +18 -23
- package/support/components/conversation-button-link.js.map +1 -1
- package/support/components/conversation-event.d.ts.map +1 -1
- package/support/components/conversation-event.js +7 -5
- package/support/components/conversation-event.js.map +1 -1
- package/support/components/conversation-timeline.d.ts +1 -1
- package/support/components/conversation-timeline.js +3 -3
- package/support/components/conversation-timeline.js.map +1 -1
- package/support/components/header.js +1 -1
- package/support/components/image-lightbox.d.ts +1 -1
- package/support/components/image-lightbox.js +1 -2
- package/support/components/image-lightbox.js.map +1 -1
- package/support/components/index.js +1 -1
- package/support/components/multimodal-input.js +0 -1
- package/support/components/multimodal-input.js.map +1 -1
- package/support/components/navigation-tab.js +1 -1
- package/support/components/online-indicator.d.ts +50 -0
- package/support/components/online-indicator.d.ts.map +1 -0
- package/support/components/online-indicator.js +65 -0
- package/support/components/online-indicator.js.map +1 -0
- package/support/components/root.js +0 -1
- package/support/components/root.js.map +1 -1
- package/support/components/timeline-identification-tool.js +4 -4
- package/support/components/timeline-identification-tool.js.map +1 -1
- package/support/components/timeline-message-group.d.ts +1 -1
- package/support/components/timeline-message-group.d.ts.map +1 -1
- package/support/components/timeline-message-group.js +6 -4
- package/support/components/timeline-message-group.js.map +1 -1
- package/support/components/timeline-message-item.d.ts +1 -1
- package/support/components/timeline-message-item.js +4 -4
- package/support/components/timeline-message-item.js.map +1 -1
- package/support/components/trigger.js +1 -2
- package/support/components/trigger.js.map +1 -1
- package/support/components/typing-indicator.js +1 -1
- package/support/components/typing-indicator.js.map +1 -1
- package/support/context/controlled-state.js +0 -1
- package/support/context/controlled-state.js.map +1 -1
- package/support/context/events.d.ts +1 -1
- package/support/context/events.js +0 -1
- package/support/context/events.js.map +1 -1
- package/support/context/handle.js +0 -1
- package/support/context/handle.js.map +1 -1
- package/support/context/identification.d.ts +33 -0
- package/support/context/identification.d.ts.map +1 -0
- package/support/context/identification.js +34 -0
- package/support/context/identification.js.map +1 -0
- package/support/context/positioning.js +0 -1
- package/support/context/positioning.js.map +1 -1
- package/support/context/slots.js +0 -1
- package/support/context/slots.js.map +1 -1
- package/support/context/websocket.d.ts +1 -1
- package/support/context/websocket.js +0 -1
- package/support/context/websocket.js.map +1 -1
- package/support/index.d.ts.map +1 -1
- package/support/index.js +51 -18
- package/support/index.js.map +1 -1
- package/support/pages/conversation-history.js +2 -1
- package/support/pages/conversation-history.js.map +1 -1
- package/support/pages/conversation.d.ts +1 -1
- package/support/pages/conversation.js +1 -1
- package/support/pages/conversation.js.map +1 -1
- package/support/pages/home.js +5 -3
- package/support/pages/home.js.map +1 -1
- package/support/router.d.ts.map +1 -1
- package/support/router.js +4 -0
- package/support/router.js.map +1 -1
- package/support/store/support-store.js +0 -1
- package/support/store/support-store.js.map +1 -1
- package/support/{support-C7Xaw-N6.css → support-DmViRaga.css} +2 -2
- package/support/{support-C7Xaw-N6.css.map → support-DmViRaga.css.map} +1 -1
- package/support/text/index.js +1 -1
- package/support/text/index.js.map +1 -1
- package/support/text/locales/en.js +1 -1
- package/support/text/locales/en.js.map +1 -1
- package/support/text/locales/es.js +1 -1
- package/support/text/locales/es.js.map +1 -1
- package/support/text/locales/fr.js +1 -1
- package/support/text/locales/fr.js.map +1 -1
- package/support/utils/index.d.ts +1 -1
- package/support-config.js +0 -1
- package/support-config.js.map +1 -1
- package/support.css +1 -1
- package/tailwind.css +1 -1
- package/utils/conversation.d.ts.map +1 -1
- package/utils/conversation.js +1 -3
- package/utils/conversation.js.map +1 -1
- package/utils/use-render-element.js +2 -2
- package/utils/use-render-element.js.map +1 -1
- package/api.d.ts +0 -71
- package/api.d.ts.map +0 -1
- package/checks.d.ts +0 -189
- package/checks.d.ts.map +0 -1
- package/clsx.d.ts +0 -7
- package/clsx.d.ts.map +0 -1
- package/coerce.d.ts +0 -9
- package/coerce.d.ts.map +0 -1
- package/conversation.d.ts.map +0 -1
- package/core.d.ts +0 -35
- package/core.d.ts.map +0 -1
- package/errors.d.ts +0 -130
- package/errors.d.ts.map +0 -1
- package/errors2.d.ts +0 -24
- package/errors2.d.ts.map +0 -1
- package/index2.d.ts +0 -4
- package/index3.d.ts +0 -1
- package/json-schema.d.ts +0 -70
- package/json-schema.d.ts.map +0 -1
- package/metadata.d.ts +0 -1
- package/openapi-generator.d.ts +0 -1
- package/openapi-generator2.d.ts +0 -1
- package/openapi-generator3.d.ts +0 -1
- package/openapi30.d.ts +0 -125
- package/openapi30.d.ts.map +0 -1
- package/openapi31.d.ts +0 -131
- package/openapi31.d.ts.map +0 -1
- package/parse.d.ts +0 -17
- package/parse.d.ts.map +0 -1
- package/realtime-events.d.ts.map +0 -1
- package/registries.d.ts +0 -32
- package/registries.d.ts.map +0 -1
- package/schemas.d.ts +0 -971
- package/schemas.d.ts.map +0 -1
- package/schemas2.d.ts +0 -345
- package/schemas2.d.ts.map +0 -1
- package/schemas3.d.ts.map +0 -1
- package/specification-extension.d.ts +0 -9
- package/specification-extension.d.ts.map +0 -1
- package/standard-schema.d.ts +0 -121
- package/standard-schema.d.ts.map +0 -1
- package/timeline-item.d.ts +0 -227
- package/timeline-item.d.ts.map +0 -1
- package/to-json-schema.d.ts +0 -96
- package/to-json-schema.d.ts.map +0 -1
- package/util.d.ts +0 -45
- package/util.d.ts.map +0 -1
- package/versions.d.ts +0 -9
- package/versions.d.ts.map +0 -1
- package/zod-extensions.d.ts +0 -39
- package/zod-extensions.d.ts.map +0 -1
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { cn } from "../utils/index.js";
|
|
4
|
+
import { CossistantLogo } from "./cossistant-branding.js";
|
|
5
|
+
import { CoButton } from "./button.js";
|
|
6
|
+
import { Icon } from "./icons.js";
|
|
7
|
+
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
8
|
+
|
|
9
|
+
//#region src/support/components/configuration-error.tsx
|
|
10
|
+
/**
|
|
11
|
+
* Full-page fallback component displayed inside the widget when misconfigured.
|
|
12
|
+
* Shows a helpful message with instructions on how to configure the API key.
|
|
13
|
+
*/
|
|
14
|
+
const ConfigurationErrorDisplay = ({ error, className }) => {
|
|
15
|
+
const docsUrl = "https://cossistant.com/docs/quickstart/api-keys";
|
|
16
|
+
const isInvalidKey = error.type === "invalid_api_key";
|
|
17
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
18
|
+
className: cn("flex h-full flex-col bg-co-background text-co-foreground", className),
|
|
19
|
+
children: [
|
|
20
|
+
/* @__PURE__ */ jsxs("div", {
|
|
21
|
+
className: cn("flex flex-col items-center justify-center px-6 pb-10"),
|
|
22
|
+
children: [
|
|
23
|
+
/* @__PURE__ */ jsx("div", {
|
|
24
|
+
className: cn("mb-4 flex h-20 w-20 items-center justify-center rounded-2xl", isInvalidKey ? "bg-co-orange/10 text-co-orange" : "bg-co-blue/10 text-co-blue"),
|
|
25
|
+
children: /* @__PURE__ */ jsx(CossistantLogo, { className: "h-10 w-10" })
|
|
26
|
+
}),
|
|
27
|
+
/* @__PURE__ */ jsx("h2", {
|
|
28
|
+
className: "font-semibold text-lg",
|
|
29
|
+
children: isInvalidKey ? "Invalid API Key" : "Setup Required"
|
|
30
|
+
}),
|
|
31
|
+
/* @__PURE__ */ jsx("p", {
|
|
32
|
+
className: "mt-1 text-center text-co-muted-foreground text-sm",
|
|
33
|
+
children: isInvalidKey ? "Your API key couldn't be verified" : "Almost there! Just add your API key"
|
|
34
|
+
})
|
|
35
|
+
]
|
|
36
|
+
}),
|
|
37
|
+
/* @__PURE__ */ jsx("div", {
|
|
38
|
+
className: "flex flex-1 flex-col gap-8 overflow-y-auto px-5 pb-4",
|
|
39
|
+
children: isInvalidKey ? /* @__PURE__ */ jsx(Fragment, { children: /* @__PURE__ */ jsxs("div", {
|
|
40
|
+
className: "rounded-lg border border-co-orange/20 bg-co-orange/5 p-4",
|
|
41
|
+
children: [/* @__PURE__ */ jsx("h4", {
|
|
42
|
+
className: "mb-2 font-medium text-co-foreground text-sm",
|
|
43
|
+
children: "Common causes:"
|
|
44
|
+
}), /* @__PURE__ */ jsxs("ul", {
|
|
45
|
+
className: "space-y-1.5 text-co-muted-foreground text-sm",
|
|
46
|
+
children: [
|
|
47
|
+
/* @__PURE__ */ jsxs("li", {
|
|
48
|
+
className: "flex items-start gap-2",
|
|
49
|
+
children: [/* @__PURE__ */ jsx("span", { className: "mt-1.5 h-1.5 w-1.5 flex-shrink-0 rounded-full bg-co-orange/60" }), "API key has been revoked or deleted"]
|
|
50
|
+
}),
|
|
51
|
+
/* @__PURE__ */ jsxs("li", {
|
|
52
|
+
className: "flex items-start gap-2",
|
|
53
|
+
children: [/* @__PURE__ */ jsx("span", { className: "mt-1.5 h-1.5 w-1.5 flex-shrink-0 rounded-full bg-co-orange/60" }), "Key has expired"]
|
|
54
|
+
}),
|
|
55
|
+
/* @__PURE__ */ jsxs("li", {
|
|
56
|
+
className: "flex items-start gap-2",
|
|
57
|
+
children: [/* @__PURE__ */ jsx("span", { className: "mt-1.5 h-1.5 w-1.5 flex-shrink-0 rounded-full bg-co-orange/60" }), "Domain not in the allowed list"]
|
|
58
|
+
}),
|
|
59
|
+
/* @__PURE__ */ jsxs("li", {
|
|
60
|
+
className: "flex items-start gap-2",
|
|
61
|
+
children: [/* @__PURE__ */ jsx("span", { className: "mt-1.5 h-1.5 w-1.5 flex-shrink-0 rounded-full bg-co-orange/60" }), "Using a test key on production"]
|
|
62
|
+
})
|
|
63
|
+
]
|
|
64
|
+
})]
|
|
65
|
+
}) }) : /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
66
|
+
/* @__PURE__ */ jsxs("div", {
|
|
67
|
+
className: "flex gap-3",
|
|
68
|
+
children: [/* @__PURE__ */ jsx("div", {
|
|
69
|
+
className: "flex h-7 w-7 flex-shrink-0 items-center justify-center rounded-full bg-co-yellow/10 font-semibold text-co-yellow text-xs",
|
|
70
|
+
children: "1"
|
|
71
|
+
}), /* @__PURE__ */ jsxs("div", {
|
|
72
|
+
className: "flex flex-1 flex-col gap-3",
|
|
73
|
+
children: [/* @__PURE__ */ jsx("h4", {
|
|
74
|
+
className: "mt-1 font-medium text-sm",
|
|
75
|
+
children: "Create an account"
|
|
76
|
+
}), /* @__PURE__ */ jsx(CoButton, {
|
|
77
|
+
asChild: true,
|
|
78
|
+
variant: "secondary",
|
|
79
|
+
children: /* @__PURE__ */ jsx("a", {
|
|
80
|
+
href: "https://cossistant.com/sign-up",
|
|
81
|
+
rel: "noopener noreferrer",
|
|
82
|
+
target: "_blank",
|
|
83
|
+
children: "Create an account"
|
|
84
|
+
})
|
|
85
|
+
})]
|
|
86
|
+
})]
|
|
87
|
+
}),
|
|
88
|
+
/* @__PURE__ */ jsxs("div", {
|
|
89
|
+
className: "flex gap-3",
|
|
90
|
+
children: [/* @__PURE__ */ jsx("div", {
|
|
91
|
+
className: "flex h-7 w-7 flex-shrink-0 items-center justify-center rounded-full bg-co-blue/10 font-semibold text-co-blue text-xs",
|
|
92
|
+
children: "2"
|
|
93
|
+
}), /* @__PURE__ */ jsxs("div", {
|
|
94
|
+
className: "mt-1 flex flex-1 flex-col gap-2",
|
|
95
|
+
children: [/* @__PURE__ */ jsx("h4", {
|
|
96
|
+
className: "font-medium text-sm",
|
|
97
|
+
children: "Get your public API key"
|
|
98
|
+
}), /* @__PURE__ */ jsxs("p", {
|
|
99
|
+
className: "mt-0.5 text-co-primary/60 text-xs",
|
|
100
|
+
children: [
|
|
101
|
+
"(Go to",
|
|
102
|
+
" ",
|
|
103
|
+
/* @__PURE__ */ jsx("span", {
|
|
104
|
+
className: "font-medium text-co-primary/60",
|
|
105
|
+
children: "Settings / Developers"
|
|
106
|
+
}),
|
|
107
|
+
" ",
|
|
108
|
+
"in your dashboard)"
|
|
109
|
+
]
|
|
110
|
+
})]
|
|
111
|
+
})]
|
|
112
|
+
}),
|
|
113
|
+
/* @__PURE__ */ jsxs("div", {
|
|
114
|
+
className: "flex gap-3",
|
|
115
|
+
children: [/* @__PURE__ */ jsx("div", {
|
|
116
|
+
className: "flex h-7 w-7 flex-shrink-0 items-center justify-center rounded-full bg-co-pink/10 font-semibold text-co-pink text-xs",
|
|
117
|
+
children: "3"
|
|
118
|
+
}), /* @__PURE__ */ jsxs("div", {
|
|
119
|
+
className: "flex flex-1 flex-col gap-3",
|
|
120
|
+
children: [/* @__PURE__ */ jsx("h4", {
|
|
121
|
+
className: "mt-1 font-medium text-sm",
|
|
122
|
+
children: "Add to your environment"
|
|
123
|
+
}), /* @__PURE__ */ jsxs("div", {
|
|
124
|
+
className: "mt-2 overflow-hidden rounded-lg border border-co-border bg-co-background-100",
|
|
125
|
+
children: [/* @__PURE__ */ jsx("div", {
|
|
126
|
+
className: "border-co-border border-b bg-co-background-50 px-3 py-1",
|
|
127
|
+
children: /* @__PURE__ */ jsx("span", {
|
|
128
|
+
className: "font-mono text-co-muted-foreground text-xs",
|
|
129
|
+
children: ".env"
|
|
130
|
+
})
|
|
131
|
+
}), /* @__PURE__ */ jsxs("div", {
|
|
132
|
+
className: "p-3 font-mono text-xs",
|
|
133
|
+
children: [/* @__PURE__ */ jsx("span", {
|
|
134
|
+
className: "text-co-foreground",
|
|
135
|
+
children: error.envVarName
|
|
136
|
+
}), /* @__PURE__ */ jsx("span", {
|
|
137
|
+
className: "text-co-muted-foreground",
|
|
138
|
+
children: "=pk_live_..."
|
|
139
|
+
})]
|
|
140
|
+
})]
|
|
141
|
+
})]
|
|
142
|
+
})]
|
|
143
|
+
})
|
|
144
|
+
] })
|
|
145
|
+
}),
|
|
146
|
+
/* @__PURE__ */ jsx("div", {
|
|
147
|
+
className: "border-co-border border-t p-4",
|
|
148
|
+
children: /* @__PURE__ */ jsxs("a", {
|
|
149
|
+
className: cn("flex w-full items-center justify-center gap-2 rounded-lg px-4 py-2.5", "bg-co-primary text-co-primary-foreground", "font-medium text-sm", "transition-all hover:opacity-90 active:scale-[0.98]"),
|
|
150
|
+
href: docsUrl,
|
|
151
|
+
rel: "noopener noreferrer",
|
|
152
|
+
target: "_blank",
|
|
153
|
+
children: ["View our docs", /* @__PURE__ */ jsx(Icon, { name: "arrow-right" })]
|
|
154
|
+
})
|
|
155
|
+
})
|
|
156
|
+
]
|
|
157
|
+
});
|
|
158
|
+
};
|
|
159
|
+
|
|
160
|
+
//#endregion
|
|
161
|
+
export { ConfigurationErrorDisplay };
|
|
162
|
+
//# sourceMappingURL=configuration-error.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"configuration-error.js","names":["ConfigurationErrorDisplay: React.FC<\n\tConfigurationErrorDisplayProps\n>"],"sources":["../../../src/support/components/configuration-error.tsx"],"sourcesContent":["\"use client\";\n\nimport { Button } from \"@cossistant/react\";\nimport type React from \"react\";\nimport type { ConfigurationError } from \"../../hooks/private/use-rest-client\";\nimport { cn } from \"../utils\";\nimport { CoButton } from \"./button\";\nimport { CossistantLogo } from \"./cossistant-branding\";\nimport { Icon } from \"./icons\";\n\ntype ConfigurationErrorDisplayProps = {\n\terror: ConfigurationError;\n\tclassName?: string;\n};\n\n/**\n * Full-page fallback component displayed inside the widget when misconfigured.\n * Shows a helpful message with instructions on how to configure the API key.\n */\nexport const ConfigurationErrorDisplay: React.FC<\n\tConfigurationErrorDisplayProps\n> = ({ error, className }) => {\n\tconst docsUrl = \"https://cossistant.com/docs/quickstart/api-keys\";\n\tconst isInvalidKey = error.type === \"invalid_api_key\";\n\n\treturn (\n\t\t<div\n\t\t\tclassName={cn(\n\t\t\t\t\"flex h-full flex-col bg-co-background text-co-foreground\",\n\t\t\t\tclassName\n\t\t\t)}\n\t\t>\n\t\t\t{/* Hero section with logo */}\n\t\t\t<div\n\t\t\t\tclassName={cn(\"flex flex-col items-center justify-center px-6 pb-10\")}\n\t\t\t>\n\t\t\t\t<div\n\t\t\t\t\tclassName={cn(\n\t\t\t\t\t\t\"mb-4 flex h-20 w-20 items-center justify-center rounded-2xl\",\n\t\t\t\t\t\tisInvalidKey\n\t\t\t\t\t\t\t? \"bg-co-orange/10 text-co-orange\"\n\t\t\t\t\t\t\t: \"bg-co-blue/10 text-co-blue\"\n\t\t\t\t\t)}\n\t\t\t\t>\n\t\t\t\t\t<CossistantLogo className=\"h-10 w-10\" />\n\t\t\t\t</div>\n\t\t\t\t<h2 className=\"font-semibold text-lg\">\n\t\t\t\t\t{isInvalidKey ? \"Invalid API Key\" : \"Setup Required\"}\n\t\t\t\t</h2>\n\t\t\t\t<p className=\"mt-1 text-center text-co-muted-foreground text-sm\">\n\t\t\t\t\t{isInvalidKey\n\t\t\t\t\t\t? \"Your API key couldn't be verified\"\n\t\t\t\t\t\t: \"Almost there! Just add your API key\"}\n\t\t\t\t</p>\n\t\t\t</div>\n\n\t\t\t{/* Content */}\n\t\t\t<div className=\"flex flex-1 flex-col gap-8 overflow-y-auto px-5 pb-4\">\n\t\t\t\t{isInvalidKey ? (\n\t\t\t\t\t<>\n\t\t\t\t\t\t{/* Error details card */}\n\t\t\t\t\t\t<div className=\"rounded-lg border border-co-orange/20 bg-co-orange/5 p-4\">\n\t\t\t\t\t\t\t<h4 className=\"mb-2 font-medium text-co-foreground text-sm\">\n\t\t\t\t\t\t\t\tCommon causes:\n\t\t\t\t\t\t\t</h4>\n\t\t\t\t\t\t\t<ul className=\"space-y-1.5 text-co-muted-foreground text-sm\">\n\t\t\t\t\t\t\t\t<li className=\"flex items-start gap-2\">\n\t\t\t\t\t\t\t\t\t<span className=\"mt-1.5 h-1.5 w-1.5 flex-shrink-0 rounded-full bg-co-orange/60\" />\n\t\t\t\t\t\t\t\t\tAPI key has been revoked or deleted\n\t\t\t\t\t\t\t\t</li>\n\t\t\t\t\t\t\t\t<li className=\"flex items-start gap-2\">\n\t\t\t\t\t\t\t\t\t<span className=\"mt-1.5 h-1.5 w-1.5 flex-shrink-0 rounded-full bg-co-orange/60\" />\n\t\t\t\t\t\t\t\t\tKey has expired\n\t\t\t\t\t\t\t\t</li>\n\t\t\t\t\t\t\t\t<li className=\"flex items-start gap-2\">\n\t\t\t\t\t\t\t\t\t<span className=\"mt-1.5 h-1.5 w-1.5 flex-shrink-0 rounded-full bg-co-orange/60\" />\n\t\t\t\t\t\t\t\t\tDomain not in the allowed list\n\t\t\t\t\t\t\t\t</li>\n\t\t\t\t\t\t\t\t<li className=\"flex items-start gap-2\">\n\t\t\t\t\t\t\t\t\t<span className=\"mt-1.5 h-1.5 w-1.5 flex-shrink-0 rounded-full bg-co-orange/60\" />\n\t\t\t\t\t\t\t\t\tUsing a test key on production\n\t\t\t\t\t\t\t\t</li>\n\t\t\t\t\t\t\t</ul>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t</>\n\t\t\t\t) : (\n\t\t\t\t\t<>\n\t\t\t\t\t\t<div className=\"flex gap-3\">\n\t\t\t\t\t\t\t<div className=\"flex h-7 w-7 flex-shrink-0 items-center justify-center rounded-full bg-co-yellow/10 font-semibold text-co-yellow text-xs\">\n\t\t\t\t\t\t\t\t1\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t<div className=\"flex flex-1 flex-col gap-3\">\n\t\t\t\t\t\t\t\t<h4 className=\"mt-1 font-medium text-sm\">Create an account</h4>\n\t\t\t\t\t\t\t\t<CoButton asChild variant=\"secondary\">\n\t\t\t\t\t\t\t\t\t<a\n\t\t\t\t\t\t\t\t\t\thref=\"https://cossistant.com/sign-up\"\n\t\t\t\t\t\t\t\t\t\trel=\"noopener noreferrer\"\n\t\t\t\t\t\t\t\t\t\ttarget=\"_blank\"\n\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\tCreate an account\n\t\t\t\t\t\t\t\t\t</a>\n\t\t\t\t\t\t\t\t</CoButton>\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t\t<div className=\"flex gap-3\">\n\t\t\t\t\t\t\t<div className=\"flex h-7 w-7 flex-shrink-0 items-center justify-center rounded-full bg-co-blue/10 font-semibold text-co-blue text-xs\">\n\t\t\t\t\t\t\t\t2\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t<div className=\"mt-1 flex flex-1 flex-col gap-2\">\n\t\t\t\t\t\t\t\t<h4 className=\"font-medium text-sm\">Get your public API key</h4>\n\t\t\t\t\t\t\t\t<p className=\"mt-0.5 text-co-primary/60 text-xs\">\n\t\t\t\t\t\t\t\t\t(Go to{\" \"}\n\t\t\t\t\t\t\t\t\t<span className=\"font-medium text-co-primary/60\">\n\t\t\t\t\t\t\t\t\t\tSettings / Developers\n\t\t\t\t\t\t\t\t\t</span>{\" \"}\n\t\t\t\t\t\t\t\t\tin your dashboard)\n\t\t\t\t\t\t\t\t</p>\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t</div>\n\n\t\t\t\t\t\t<div className=\"flex gap-3\">\n\t\t\t\t\t\t\t<div className=\"flex h-7 w-7 flex-shrink-0 items-center justify-center rounded-full bg-co-pink/10 font-semibold text-co-pink text-xs\">\n\t\t\t\t\t\t\t\t3\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t<div className=\"flex flex-1 flex-col gap-3\">\n\t\t\t\t\t\t\t\t<h4 className=\"mt-1 font-medium text-sm\">\n\t\t\t\t\t\t\t\t\tAdd to your environment\n\t\t\t\t\t\t\t\t</h4>\n\t\t\t\t\t\t\t\t<div className=\"mt-2 overflow-hidden rounded-lg border border-co-border bg-co-background-100\">\n\t\t\t\t\t\t\t\t\t<div className=\"border-co-border border-b bg-co-background-50 px-3 py-1\">\n\t\t\t\t\t\t\t\t\t\t<span className=\"font-mono text-co-muted-foreground text-xs\">\n\t\t\t\t\t\t\t\t\t\t\t.env\n\t\t\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t\t<div className=\"p-3 font-mono text-xs\">\n\t\t\t\t\t\t\t\t\t\t<span className=\"text-co-foreground\">\n\t\t\t\t\t\t\t\t\t\t\t{error.envVarName}\n\t\t\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t\t\t\t<span className=\"text-co-muted-foreground\">\n\t\t\t\t\t\t\t\t\t\t\t=pk_live_...\n\t\t\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t</>\n\t\t\t\t)}\n\t\t\t</div>\n\n\t\t\t{/* Footer with CTA */}\n\t\t\t<div className=\"border-co-border border-t p-4\">\n\t\t\t\t<a\n\t\t\t\t\tclassName={cn(\n\t\t\t\t\t\t\"flex w-full items-center justify-center gap-2 rounded-lg px-4 py-2.5\",\n\t\t\t\t\t\t\"bg-co-primary text-co-primary-foreground\",\n\t\t\t\t\t\t\"font-medium text-sm\",\n\t\t\t\t\t\t\"transition-all hover:opacity-90 active:scale-[0.98]\"\n\t\t\t\t\t)}\n\t\t\t\t\thref={docsUrl}\n\t\t\t\t\trel=\"noopener noreferrer\"\n\t\t\t\t\ttarget=\"_blank\"\n\t\t\t\t>\n\t\t\t\t\tView our docs\n\t\t\t\t\t<Icon name=\"arrow-right\" />\n\t\t\t\t</a>\n\t\t\t</div>\n\t\t</div>\n\t);\n};\n"],"mappings":";;;;;;;;;;;;;AAmBA,MAAaA,6BAER,EAAE,OAAO,gBAAgB;CAC7B,MAAM,UAAU;CAChB,MAAM,eAAe,MAAM,SAAS;AAEpC,QACC,qBAAC;EACA,WAAW,GACV,4DACA,UACA;;GAGD,qBAAC;IACA,WAAW,GAAG,uDAAuD;;KAErE,oBAAC;MACA,WAAW,GACV,+DACA,eACG,mCACA,6BACH;gBAED,oBAAC,kBAAe,WAAU,cAAc;OACnC;KACN,oBAAC;MAAG,WAAU;gBACZ,eAAe,oBAAoB;OAChC;KACL,oBAAC;MAAE,WAAU;gBACX,eACE,sCACA;OACA;;KACC;GAGN,oBAAC;IAAI,WAAU;cACb,eACA,0CAEC,qBAAC;KAAI,WAAU;gBACd,oBAAC;MAAG,WAAU;gBAA8C;OAEvD,EACL,qBAAC;MAAG,WAAU;;OACb,qBAAC;QAAG,WAAU;mBACb,oBAAC,UAAK,WAAU,kEAAkE;SAE9E;OACL,qBAAC;QAAG,WAAU;mBACb,oBAAC,UAAK,WAAU,kEAAkE;SAE9E;OACL,qBAAC;QAAG,WAAU;mBACb,oBAAC,UAAK,WAAU,kEAAkE;SAE9E;OACL,qBAAC;QAAG,WAAU;mBACb,oBAAC,UAAK,WAAU,kEAAkE;SAE9E;;OACD;MACA,GACJ,GAEH;KACC,qBAAC;MAAI,WAAU;iBACd,oBAAC;OAAI,WAAU;iBAA2H;QAEpI,EACN,qBAAC;OAAI,WAAU;kBACd,oBAAC;QAAG,WAAU;kBAA2B;SAAsB,EAC/D,oBAAC;QAAS;QAAQ,SAAQ;kBACzB,oBAAC;SACA,MAAK;SACL,KAAI;SACJ,QAAO;mBACP;UAEG;SACM;QACN;OACD;KACN,qBAAC;MAAI,WAAU;iBACd,oBAAC;OAAI,WAAU;iBAAuH;QAEhI,EACN,qBAAC;OAAI,WAAU;kBACd,oBAAC;QAAG,WAAU;kBAAsB;SAA4B,EAChE,qBAAC;QAAE,WAAU;;SAAoC;SACzC;SACP,oBAAC;UAAK,WAAU;oBAAiC;WAE1C;SAAC;SAAI;;SAET;QACC;OACD;KAEN,qBAAC;MAAI,WAAU;iBACd,oBAAC;OAAI,WAAU;iBAAuH;QAEhI,EACN,qBAAC;OAAI,WAAU;kBACd,oBAAC;QAAG,WAAU;kBAA2B;SAEpC,EACL,qBAAC;QAAI,WAAU;mBACd,oBAAC;SAAI,WAAU;mBACd,oBAAC;UAAK,WAAU;oBAA6C;WAEtD;UACF,EACN,qBAAC;SAAI,WAAU;oBACd,oBAAC;UAAK,WAAU;oBACd,MAAM;WACD,EACP,oBAAC;UAAK,WAAU;oBAA2B;WAEpC;UACF;SACD;QACD;OACD;QACJ;KAEC;GAGN,oBAAC;IAAI,WAAU;cACd,qBAAC;KACA,WAAW,GACV,wEACA,4CACA,uBACA,sDACA;KACD,MAAM;KACN,KAAI;KACJ,QAAO;gBACP,iBAEA,oBAAC,QAAK,MAAK,gBAAgB;MACxB;KACC;;GACD"}
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
|
|
3
|
-
|
|
4
3
|
import { cn } from "../utils/index.js";
|
|
5
4
|
import { useTriggerRef } from "../context/positioning.js";
|
|
6
5
|
import { useSupportConfig } from "../store/support-store.js";
|
|
@@ -245,7 +244,7 @@ const ContentInner = ({ children, containerRef, showScrollIndicator }) => {
|
|
|
245
244
|
children: header
|
|
246
245
|
}),
|
|
247
246
|
/* @__PURE__ */ jsx("div", {
|
|
248
|
-
className: cn("flex flex-1 flex-col overflow-y-auto", !hasCustomHeader && "pt-
|
|
247
|
+
className: cn("flex flex-1 flex-col overflow-y-auto", !hasCustomHeader && "pt-16"),
|
|
249
248
|
ref: containerRef,
|
|
250
249
|
children
|
|
251
250
|
}),
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"content.js","names":["React","Content: React.FC<ContentPropsType>","Primitive.Window","ContentInner: React.FC<{\n\tchildren: React.ReactNode;\n\tcontainerRef: React.RefObject<HTMLDivElement | null>;\n\tshowScrollIndicator: boolean;\n}>"],"sources":["../../../src/support/components/content.tsx"],"sourcesContent":["\"use client\";\n\nimport {\n\tautoUpdate,\n\tflip,\n\toffset,\n\ttype Placement,\n\tshift,\n\tuseFloating,\n} from \"@floating-ui/react\";\nimport { AnimatePresence, motion } from \"motion/react\";\nimport * as React from \"react\";\nimport * as Primitive from \"../../primitives\";\nimport { useTriggerRef } from \"../context/positioning\";\nimport { SlotProvider, useSlots } from \"../context/slots\";\nimport { useSupportConfig } from \"../store/support-store\";\nimport type {\n\tAlign,\n\tCollisionPadding,\n\tContentProps as ContentPropsType,\n\tSide,\n} from \"../types\";\nimport { cn } from \"../utils\";\n\nexport type { CollisionPadding, ContentProps } from \"../types\";\n\n// =============================================================================\n// Utils\n// =============================================================================\n\n/**\n * Convert side + align props to Floating UI placement\n */\nfunction getPlacement(side: Side, align: Align): Placement {\n\tif (align === \"center\") {\n\t\treturn side;\n\t}\n\treturn `${side}-${align}` as Placement;\n}\n\n/**\n * Get fallback positioning classes for when Floating UI is not available\n * (e.g., trigger ref not set, or avoidCollisions is false)\n */\nfunction getFallbackPositioningClasses(side: Side, align: Align): string {\n\tconst sideClasses: Record<Side, string> = {\n\t\ttop: \"md:bottom-full md:mb-4\",\n\t\tbottom: \"md:top-full md:mt-4\",\n\t\tleft: \"md:right-full md:mr-4\",\n\t\tright: \"md:left-full md:ml-4\",\n\t};\n\n\tconst alignClasses: Record<Side, Record<Align, string>> = {\n\t\ttop: {\n\t\t\tstart: \"md:left-0\",\n\t\t\tcenter: \"md:left-1/2 md:-translate-x-1/2\",\n\t\t\tend: \"md:right-0\",\n\t\t},\n\t\tbottom: {\n\t\t\tstart: \"md:left-0\",\n\t\t\tcenter: \"md:left-1/2 md:-translate-x-1/2\",\n\t\t\tend: \"md:right-0\",\n\t\t},\n\t\tleft: {\n\t\t\tstart: \"md:top-0\",\n\t\t\tcenter: \"md:top-1/2 md:-translate-y-1/2\",\n\t\t\tend: \"md:bottom-0\",\n\t\t},\n\t\tright: {\n\t\t\tstart: \"md:top-0\",\n\t\t\tcenter: \"md:top-1/2 md:-translate-y-1/2\",\n\t\t\tend: \"md:bottom-0\",\n\t\t},\n\t};\n\n\treturn cn(sideClasses[side], alignClasses[side][align]);\n}\n\n/**\n * Get fallback offset styles for static positioning\n */\nfunction getFallbackOffsetStyle(\n\tside: Side,\n\tsideOffset: number\n): React.CSSProperties | undefined {\n\tif (sideOffset === 16) {\n\t\treturn;\n\t}\n\n\tconst offsetMap: Record<Side, React.CSSProperties> = {\n\t\ttop: { marginBottom: sideOffset },\n\t\tbottom: { marginTop: sideOffset },\n\t\tleft: { marginRight: sideOffset },\n\t\tright: { marginLeft: sideOffset },\n\t};\n\n\treturn offsetMap[side];\n}\n\n// =============================================================================\n// Hook for responsive detection\n// =============================================================================\n\nfunction useIsMobile(): boolean {\n\tconst [isMobile, setIsMobile] = React.useState(false);\n\n\tReact.useEffect(() => {\n\t\tconst mediaQuery = window.matchMedia(\"(max-width: 767px)\");\n\t\tsetIsMobile(mediaQuery.matches);\n\n\t\tconst handler = (event: MediaQueryListEvent) => {\n\t\t\tsetIsMobile(event.matches);\n\t\t};\n\n\t\tmediaQuery.addEventListener(\"change\", handler);\n\t\treturn () => mediaQuery.removeEventListener(\"change\", handler);\n\t}, []);\n\n\treturn isMobile;\n}\n\n// =============================================================================\n// Content Component\n// =============================================================================\n\n/**\n * Content component for the support window.\n * Uses Floating UI for automatic collision detection on desktop.\n * Fullscreen on mobile, floating on desktop.\n *\n * @example\n * // Basic usage (uses defaults: side=\"top\", align=\"end\")\n * <Support.Content />\n *\n * @example\n * // Custom positioning with collision avoidance\n * <Support.Content side=\"bottom\" align=\"start\" sideOffset={24} />\n *\n * @example\n * // Disable collision avoidance for static positioning\n * <Support.Content avoidCollisions={false} />\n *\n * @example\n * // Custom collision padding\n * <Support.Content collisionPadding={{ top: 16, bottom: 32 }} />\n */\nexport const Content: React.FC<ContentPropsType> = ({\n\tclassName,\n\tchildren,\n\tside = \"top\",\n\talign = \"end\",\n\tsideOffset = 16,\n\tavoidCollisions = true,\n\tcollisionPadding = 8,\n}) => {\n\tconst [showScrollIndicator, setShowScrollIndicator] = React.useState(false);\n\tconst containerRef = React.useRef<HTMLDivElement>(null);\n\tconst hasEverPositionedRef = React.useRef(false);\n\tconst isMobile = useIsMobile();\n\tconst triggerRefContext = useTriggerRef();\n\tconst { isOpen } = useSupportConfig();\n\n\t// Set up Floating UI middleware\n\tconst middleware = React.useMemo(() => {\n\t\tconst middlewares = [offset(sideOffset)];\n\n\t\tif (avoidCollisions) {\n\t\t\tmiddlewares.push(\n\t\t\t\tflip({\n\t\t\t\t\tpadding: collisionPadding,\n\t\t\t\t\tfallbackAxisSideDirection: \"start\",\n\t\t\t\t}),\n\t\t\t\tshift({\n\t\t\t\t\tpadding: collisionPadding,\n\t\t\t\t})\n\t\t\t);\n\t\t}\n\n\t\treturn middlewares;\n\t}, [sideOffset, avoidCollisions, collisionPadding]);\n\n\t// Get trigger element from context (stored in state for reactivity)\n\tconst triggerElement = triggerRefContext?.triggerElement ?? null;\n\n\t// Initialize Floating UI with the trigger element as reference\n\t// Using strategy: 'fixed' because Content uses position: fixed (md:fixed class)\n\t// This ensures Floating UI calculates positions relative to the viewport\n\t// The `open` prop synchronizes Floating UI with visibility state for proper autoUpdate\n\tconst { refs, update, x, y, isPositioned } = useFloating({\n\t\tplacement: getPlacement(side, align),\n\t\tstrategy: \"fixed\",\n\t\tmiddleware,\n\t\twhileElementsMounted: autoUpdate,\n\t\topen: isOpen,\n\t\telements: {\n\t\t\treference: triggerElement,\n\t\t},\n\t});\n\n\t// Merge refs for the floating element - ensures proper ref forwarding with motion.div\n\tconst setFloatingRef = React.useCallback(\n\t\t(node: HTMLDivElement | null) => {\n\t\t\trefs.setFloating(node);\n\t\t},\n\t\t[refs]\n\t);\n\n\t// Force position recalculation when trigger element becomes available\n\t// This handles the case where content mounts before trigger\n\tReact.useEffect(() => {\n\t\tif (triggerElement && isOpen) {\n\t\t\t// Defer update to ensure DOM is ready\n\t\t\trequestAnimationFrame(() => {\n\t\t\t\tupdate();\n\t\t\t});\n\t\t}\n\t}, [triggerElement, isOpen, update]);\n\n\t// Determine if we should use Floating UI positioning\n\t// Only use Floating UI when trigger element is available\n\tconst useFloatingPositioning =\n\t\tavoidCollisions && !isMobile && triggerElement !== null;\n\n\t// Scroll indicator logic\n\tconst checkScroll = React.useCallback(() => {\n\t\tconst container = containerRef.current;\n\t\tif (!container) {\n\t\t\treturn;\n\t\t}\n\n\t\tconst { scrollTop, scrollHeight, clientHeight } = container;\n\t\tconst isScrollable = scrollHeight > clientHeight;\n\t\tconst isAtBottom = Math.abs(scrollHeight - scrollTop - clientHeight) < 5;\n\n\t\tsetShowScrollIndicator(isScrollable && !isAtBottom);\n\t}, []);\n\n\tReact.useEffect(() => {\n\t\tconst container = containerRef.current;\n\t\tif (!container) {\n\t\t\treturn;\n\t\t}\n\n\t\tcheckScroll();\n\n\t\tconst handleScroll = () => {\n\t\t\tcheckScroll();\n\t\t};\n\n\t\tcontainer.addEventListener(\"scroll\", handleScroll, { passive: true });\n\n\t\tconst resizeObserver = new ResizeObserver(() => {\n\t\t\tcheckScroll();\n\t\t});\n\n\t\tresizeObserver.observe(container);\n\n\t\tconst mutationObserver = new MutationObserver(() => {\n\t\t\tcheckScroll();\n\t\t});\n\n\t\tmutationObserver.observe(container, {\n\t\t\tchildList: true,\n\t\t\tsubtree: true,\n\t\t\tcharacterData: true,\n\t\t});\n\n\t\treturn () => {\n\t\t\tcontainer.removeEventListener(\"scroll\", handleScroll);\n\t\t\tresizeObserver.disconnect();\n\t\t\tmutationObserver.disconnect();\n\t\t};\n\t}, [checkScroll]);\n\n\t// Track when Floating UI has successfully positioned at least once\n\t// Using a ref to persist across renders - once positioned, always valid\n\t// This avoids the bug where (0,0) was wrongly treated as invalid\n\tif (isPositioned) {\n\t\thasEverPositionedRef.current = true;\n\t}\n\n\t// Check if Floating UI has successfully calculated valid positions\n\t// We use the ref to handle legitimate (0,0) positions correctly\n\tconst hasValidFloatingPosition = hasEverPositionedRef.current;\n\n\t// Compute styles based on positioning mode\n\t// Use raw x, y coordinates from Floating UI when available\n\tconst computedStyles = React.useMemo<React.CSSProperties>(() => {\n\t\tif (isMobile) {\n\t\t\t// Mobile: no positioning styles needed, handled by CSS classes\n\t\t\treturn {};\n\t\t}\n\n\t\tif (useFloatingPositioning && hasValidFloatingPosition) {\n\t\t\t// Desktop with Floating UI: use calculated coordinates\n\t\t\t// Using top/left instead of transform to avoid conflicts with motion animations\n\t\t\treturn {\n\t\t\t\tposition: \"fixed\" as const,\n\t\t\t\tleft: x,\n\t\t\t\ttop: y,\n\t\t\t};\n\t\t}\n\n\t\t// Desktop fallback: use static offset styles when Floating UI isn't ready\n\t\treturn getFallbackOffsetStyle(side, sideOffset) ?? {};\n\t}, [\n\t\tisMobile,\n\t\tuseFloatingPositioning,\n\t\thasValidFloatingPosition,\n\t\tx,\n\t\ty,\n\t\tside,\n\t\tsideOffset,\n\t]);\n\n\t// Compute className based on positioning mode\n\tconst computedClassName = cn(\n\t\t// Common base styles\n\t\t\"flex flex-col overflow-hidden overscroll-none bg-co-background\",\n\n\t\t// Mobile: fullscreen fixed\n\t\t\"max-md:fixed max-md:inset-0 max-md:z-[9999]\",\n\n\t\t// Desktop: floating window base styles\n\t\t\"md:z-[9999] md:aspect-[9/17] md:max-h-[calc(100vh-6rem)] md:w-[400px] md:rounded-md md:border md:border-co-border md:shadow md:dark:shadow-co-background-600/50\",\n\n\t\t// Positioning mode specific styles\n\t\t// Use fixed positioning when Floating UI has valid coordinates,\n\t\t// otherwise use fallback absolute positioning with CSS classes\n\t\tuseFloatingPositioning && hasValidFloatingPosition\n\t\t\t? \"md:fixed\"\n\t\t\t: cn(\"md:absolute\", getFallbackPositioningClasses(side, align)),\n\n\t\tclassName\n\t);\n\n\treturn (\n\t\t<SlotProvider>\n\t\t\t<Primitive.Window asChild>\n\t\t\t\t<motion.div\n\t\t\t\t\tanimate=\"visible\"\n\t\t\t\t\tclassName={computedClassName}\n\t\t\t\t\texit=\"exit\"\n\t\t\t\t\tinitial=\"hidden\"\n\t\t\t\t\tref={setFloatingRef}\n\t\t\t\t\tstyle={computedStyles}\n\t\t\t\t\ttransition={{\n\t\t\t\t\t\tdefault: { ease: \"anticipate\" },\n\t\t\t\t\t\tlayout: { duration: 0.3 },\n\t\t\t\t\t}}\n\t\t\t\t\tvariants={{\n\t\t\t\t\t\thidden: { opacity: 0, scale: 0.95, filter: \"blur(6px)\" },\n\t\t\t\t\t\tvisible: { opacity: 1, scale: 1, filter: \"blur(0px)\" },\n\t\t\t\t\t\texit: { opacity: 0, scale: 0.95, filter: \"blur(6px)\" },\n\t\t\t\t\t}}\n\t\t\t\t>\n\t\t\t\t\t<ContentInner\n\t\t\t\t\t\tcontainerRef={containerRef}\n\t\t\t\t\t\tshowScrollIndicator={showScrollIndicator}\n\t\t\t\t\t>\n\t\t\t\t\t\t{children}\n\t\t\t\t\t</ContentInner>\n\t\t\t\t</motion.div>\n\t\t\t</Primitive.Window>\n\t\t</SlotProvider>\n\t);\n};\n\n/**\n * Inner content component that consumes slots.\n * Separated to allow slot context to be established before consuming it.\n */\nconst ContentInner: React.FC<{\n\tchildren: React.ReactNode;\n\tcontainerRef: React.RefObject<HTMLDivElement | null>;\n\tshowScrollIndicator: boolean;\n}> = ({ children, containerRef, showScrollIndicator }) => {\n\tconst { header, footer, hasCustomHeader, hasCustomFooter } = useSlots();\n\n\treturn (\n\t\t<div className=\"relative flex h-full w-full flex-col\">\n\t\t\t{/* Custom header slot */}\n\t\t\t{hasCustomHeader && <div className=\"flex-shrink-0\">{header}</div>}\n\n\t\t\t<div\n\t\t\t\tclassName={cn(\n\t\t\t\t\t\"flex flex-1 flex-col overflow-y-auto\",\n\t\t\t\t\t// Only add top padding if no custom header (default header is absolute positioned)\n\t\t\t\t\t!hasCustomHeader && \"pt-18\"\n\t\t\t\t)}\n\t\t\t\tref={containerRef}\n\t\t\t>\n\t\t\t\t{children}\n\t\t\t</div>\n\n\t\t\t{/* Custom footer slot */}\n\t\t\t{hasCustomFooter && <div className=\"flex-shrink-0\">{footer}</div>}\n\n\t\t\t<AnimatePresence>\n\t\t\t\t{showScrollIndicator && (\n\t\t\t\t\t<>\n\t\t\t\t\t\t<motion.div\n\t\t\t\t\t\t\tanimate={{ opacity: 1 }}\n\t\t\t\t\t\t\tclassName=\"pointer-events-none absolute inset-x-0 bottom-0 z-5 h-32 bg-gradient-to-t from-co-background via-co-background/70 to-transparent\"\n\t\t\t\t\t\t\texit={{ opacity: 0 }}\n\t\t\t\t\t\t\tinitial={{ opacity: 0 }}\n\t\t\t\t\t\t\ttransition={{ duration: 0.3, ease: \"easeInOut\" }}\n\t\t\t\t\t\t/>\n\t\t\t\t\t\t<motion.div\n\t\t\t\t\t\t\tanimate={{ opacity: 0.6 }}\n\t\t\t\t\t\t\tclassName=\"pointer-events-none absolute inset-x-0 bottom-0 z-5 h-48 bg-gradient-to-t from-co-background/80 via-co-background/30 to-transparent\"\n\t\t\t\t\t\t\texit={{ opacity: 0 }}\n\t\t\t\t\t\t\tinitial={{ opacity: 0 }}\n\t\t\t\t\t\t\ttransition={{ duration: 0.4, ease: \"easeInOut\", delay: 0.05 }}\n\t\t\t\t\t\t/>\n\t\t\t\t\t</>\n\t\t\t\t)}\n\t\t\t</AnimatePresence>\n\t\t</div>\n\t);\n};\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"content.js","names":["React","Content: React.FC<ContentPropsType>","Primitive.Window","ContentInner: React.FC<{\n\tchildren: React.ReactNode;\n\tcontainerRef: React.RefObject<HTMLDivElement | null>;\n\tshowScrollIndicator: boolean;\n}>"],"sources":["../../../src/support/components/content.tsx"],"sourcesContent":["\"use client\";\n\nimport {\n\tautoUpdate,\n\tflip,\n\toffset,\n\ttype Placement,\n\tshift,\n\tuseFloating,\n} from \"@floating-ui/react\";\nimport { AnimatePresence, motion } from \"motion/react\";\nimport * as React from \"react\";\nimport * as Primitive from \"../../primitives\";\nimport { useTriggerRef } from \"../context/positioning\";\nimport { SlotProvider, useSlots } from \"../context/slots\";\nimport { useSupportConfig } from \"../store/support-store\";\nimport type {\n\tAlign,\n\tCollisionPadding,\n\tContentProps as ContentPropsType,\n\tSide,\n} from \"../types\";\nimport { cn } from \"../utils\";\n\nexport type { CollisionPadding, ContentProps } from \"../types\";\n\n// =============================================================================\n// Utils\n// =============================================================================\n\n/**\n * Convert side + align props to Floating UI placement\n */\nfunction getPlacement(side: Side, align: Align): Placement {\n\tif (align === \"center\") {\n\t\treturn side;\n\t}\n\treturn `${side}-${align}` as Placement;\n}\n\n/**\n * Get fallback positioning classes for when Floating UI is not available\n * (e.g., trigger ref not set, or avoidCollisions is false)\n */\nfunction getFallbackPositioningClasses(side: Side, align: Align): string {\n\tconst sideClasses: Record<Side, string> = {\n\t\ttop: \"md:bottom-full md:mb-4\",\n\t\tbottom: \"md:top-full md:mt-4\",\n\t\tleft: \"md:right-full md:mr-4\",\n\t\tright: \"md:left-full md:ml-4\",\n\t};\n\n\tconst alignClasses: Record<Side, Record<Align, string>> = {\n\t\ttop: {\n\t\t\tstart: \"md:left-0\",\n\t\t\tcenter: \"md:left-1/2 md:-translate-x-1/2\",\n\t\t\tend: \"md:right-0\",\n\t\t},\n\t\tbottom: {\n\t\t\tstart: \"md:left-0\",\n\t\t\tcenter: \"md:left-1/2 md:-translate-x-1/2\",\n\t\t\tend: \"md:right-0\",\n\t\t},\n\t\tleft: {\n\t\t\tstart: \"md:top-0\",\n\t\t\tcenter: \"md:top-1/2 md:-translate-y-1/2\",\n\t\t\tend: \"md:bottom-0\",\n\t\t},\n\t\tright: {\n\t\t\tstart: \"md:top-0\",\n\t\t\tcenter: \"md:top-1/2 md:-translate-y-1/2\",\n\t\t\tend: \"md:bottom-0\",\n\t\t},\n\t};\n\n\treturn cn(sideClasses[side], alignClasses[side][align]);\n}\n\n/**\n * Get fallback offset styles for static positioning\n */\nfunction getFallbackOffsetStyle(\n\tside: Side,\n\tsideOffset: number\n): React.CSSProperties | undefined {\n\tif (sideOffset === 16) {\n\t\treturn;\n\t}\n\n\tconst offsetMap: Record<Side, React.CSSProperties> = {\n\t\ttop: { marginBottom: sideOffset },\n\t\tbottom: { marginTop: sideOffset },\n\t\tleft: { marginRight: sideOffset },\n\t\tright: { marginLeft: sideOffset },\n\t};\n\n\treturn offsetMap[side];\n}\n\n// =============================================================================\n// Hook for responsive detection\n// =============================================================================\n\nfunction useIsMobile(): boolean {\n\tconst [isMobile, setIsMobile] = React.useState(false);\n\n\tReact.useEffect(() => {\n\t\tconst mediaQuery = window.matchMedia(\"(max-width: 767px)\");\n\t\tsetIsMobile(mediaQuery.matches);\n\n\t\tconst handler = (event: MediaQueryListEvent) => {\n\t\t\tsetIsMobile(event.matches);\n\t\t};\n\n\t\tmediaQuery.addEventListener(\"change\", handler);\n\t\treturn () => mediaQuery.removeEventListener(\"change\", handler);\n\t}, []);\n\n\treturn isMobile;\n}\n\n// =============================================================================\n// Content Component\n// =============================================================================\n\n/**\n * Content component for the support window.\n * Uses Floating UI for automatic collision detection on desktop.\n * Fullscreen on mobile, floating on desktop.\n *\n * @example\n * // Basic usage (uses defaults: side=\"top\", align=\"end\")\n * <Support.Content />\n *\n * @example\n * // Custom positioning with collision avoidance\n * <Support.Content side=\"bottom\" align=\"start\" sideOffset={24} />\n *\n * @example\n * // Disable collision avoidance for static positioning\n * <Support.Content avoidCollisions={false} />\n *\n * @example\n * // Custom collision padding\n * <Support.Content collisionPadding={{ top: 16, bottom: 32 }} />\n */\nexport const Content: React.FC<ContentPropsType> = ({\n\tclassName,\n\tchildren,\n\tside = \"top\",\n\talign = \"end\",\n\tsideOffset = 16,\n\tavoidCollisions = true,\n\tcollisionPadding = 8,\n}) => {\n\tconst [showScrollIndicator, setShowScrollIndicator] = React.useState(false);\n\tconst containerRef = React.useRef<HTMLDivElement>(null);\n\tconst hasEverPositionedRef = React.useRef(false);\n\tconst isMobile = useIsMobile();\n\tconst triggerRefContext = useTriggerRef();\n\tconst { isOpen } = useSupportConfig();\n\n\t// Set up Floating UI middleware\n\tconst middleware = React.useMemo(() => {\n\t\tconst middlewares = [offset(sideOffset)];\n\n\t\tif (avoidCollisions) {\n\t\t\tmiddlewares.push(\n\t\t\t\tflip({\n\t\t\t\t\tpadding: collisionPadding,\n\t\t\t\t\tfallbackAxisSideDirection: \"start\",\n\t\t\t\t}),\n\t\t\t\tshift({\n\t\t\t\t\tpadding: collisionPadding,\n\t\t\t\t})\n\t\t\t);\n\t\t}\n\n\t\treturn middlewares;\n\t}, [sideOffset, avoidCollisions, collisionPadding]);\n\n\t// Get trigger element from context (stored in state for reactivity)\n\tconst triggerElement = triggerRefContext?.triggerElement ?? null;\n\n\t// Initialize Floating UI with the trigger element as reference\n\t// Using strategy: 'fixed' because Content uses position: fixed (md:fixed class)\n\t// This ensures Floating UI calculates positions relative to the viewport\n\t// The `open` prop synchronizes Floating UI with visibility state for proper autoUpdate\n\tconst { refs, update, x, y, isPositioned } = useFloating({\n\t\tplacement: getPlacement(side, align),\n\t\tstrategy: \"fixed\",\n\t\tmiddleware,\n\t\twhileElementsMounted: autoUpdate,\n\t\topen: isOpen,\n\t\telements: {\n\t\t\treference: triggerElement,\n\t\t},\n\t});\n\n\t// Merge refs for the floating element - ensures proper ref forwarding with motion.div\n\tconst setFloatingRef = React.useCallback(\n\t\t(node: HTMLDivElement | null) => {\n\t\t\trefs.setFloating(node);\n\t\t},\n\t\t[refs]\n\t);\n\n\t// Force position recalculation when trigger element becomes available\n\t// This handles the case where content mounts before trigger\n\tReact.useEffect(() => {\n\t\tif (triggerElement && isOpen) {\n\t\t\t// Defer update to ensure DOM is ready\n\t\t\trequestAnimationFrame(() => {\n\t\t\t\tupdate();\n\t\t\t});\n\t\t}\n\t}, [triggerElement, isOpen, update]);\n\n\t// Determine if we should use Floating UI positioning\n\t// Only use Floating UI when trigger element is available\n\tconst useFloatingPositioning =\n\t\tavoidCollisions && !isMobile && triggerElement !== null;\n\n\t// Scroll indicator logic\n\tconst checkScroll = React.useCallback(() => {\n\t\tconst container = containerRef.current;\n\t\tif (!container) {\n\t\t\treturn;\n\t\t}\n\n\t\tconst { scrollTop, scrollHeight, clientHeight } = container;\n\t\tconst isScrollable = scrollHeight > clientHeight;\n\t\tconst isAtBottom = Math.abs(scrollHeight - scrollTop - clientHeight) < 5;\n\n\t\tsetShowScrollIndicator(isScrollable && !isAtBottom);\n\t}, []);\n\n\tReact.useEffect(() => {\n\t\tconst container = containerRef.current;\n\t\tif (!container) {\n\t\t\treturn;\n\t\t}\n\n\t\tcheckScroll();\n\n\t\tconst handleScroll = () => {\n\t\t\tcheckScroll();\n\t\t};\n\n\t\tcontainer.addEventListener(\"scroll\", handleScroll, { passive: true });\n\n\t\tconst resizeObserver = new ResizeObserver(() => {\n\t\t\tcheckScroll();\n\t\t});\n\n\t\tresizeObserver.observe(container);\n\n\t\tconst mutationObserver = new MutationObserver(() => {\n\t\t\tcheckScroll();\n\t\t});\n\n\t\tmutationObserver.observe(container, {\n\t\t\tchildList: true,\n\t\t\tsubtree: true,\n\t\t\tcharacterData: true,\n\t\t});\n\n\t\treturn () => {\n\t\t\tcontainer.removeEventListener(\"scroll\", handleScroll);\n\t\t\tresizeObserver.disconnect();\n\t\t\tmutationObserver.disconnect();\n\t\t};\n\t}, [checkScroll]);\n\n\t// Track when Floating UI has successfully positioned at least once\n\t// Using a ref to persist across renders - once positioned, always valid\n\t// This avoids the bug where (0,0) was wrongly treated as invalid\n\tif (isPositioned) {\n\t\thasEverPositionedRef.current = true;\n\t}\n\n\t// Check if Floating UI has successfully calculated valid positions\n\t// We use the ref to handle legitimate (0,0) positions correctly\n\tconst hasValidFloatingPosition = hasEverPositionedRef.current;\n\n\t// Compute styles based on positioning mode\n\t// Use raw x, y coordinates from Floating UI when available\n\tconst computedStyles = React.useMemo<React.CSSProperties>(() => {\n\t\tif (isMobile) {\n\t\t\t// Mobile: no positioning styles needed, handled by CSS classes\n\t\t\treturn {};\n\t\t}\n\n\t\tif (useFloatingPositioning && hasValidFloatingPosition) {\n\t\t\t// Desktop with Floating UI: use calculated coordinates\n\t\t\t// Using top/left instead of transform to avoid conflicts with motion animations\n\t\t\treturn {\n\t\t\t\tposition: \"fixed\" as const,\n\t\t\t\tleft: x,\n\t\t\t\ttop: y,\n\t\t\t};\n\t\t}\n\n\t\t// Desktop fallback: use static offset styles when Floating UI isn't ready\n\t\treturn getFallbackOffsetStyle(side, sideOffset) ?? {};\n\t}, [\n\t\tisMobile,\n\t\tuseFloatingPositioning,\n\t\thasValidFloatingPosition,\n\t\tx,\n\t\ty,\n\t\tside,\n\t\tsideOffset,\n\t]);\n\n\t// Compute className based on positioning mode\n\tconst computedClassName = cn(\n\t\t// Common base styles\n\t\t\"flex flex-col overflow-hidden overscroll-none bg-co-background\",\n\n\t\t// Mobile: fullscreen fixed\n\t\t\"max-md:fixed max-md:inset-0 max-md:z-[9999]\",\n\n\t\t// Desktop: floating window base styles\n\t\t\"md:z-[9999] md:aspect-[9/17] md:max-h-[calc(100vh-6rem)] md:w-[400px] md:rounded-md md:border md:border-co-border md:shadow md:dark:shadow-co-background-600/50\",\n\n\t\t// Positioning mode specific styles\n\t\t// Use fixed positioning when Floating UI has valid coordinates,\n\t\t// otherwise use fallback absolute positioning with CSS classes\n\t\tuseFloatingPositioning && hasValidFloatingPosition\n\t\t\t? \"md:fixed\"\n\t\t\t: cn(\"md:absolute\", getFallbackPositioningClasses(side, align)),\n\n\t\tclassName\n\t);\n\n\treturn (\n\t\t<SlotProvider>\n\t\t\t<Primitive.Window asChild>\n\t\t\t\t<motion.div\n\t\t\t\t\tanimate=\"visible\"\n\t\t\t\t\tclassName={computedClassName}\n\t\t\t\t\texit=\"exit\"\n\t\t\t\t\tinitial=\"hidden\"\n\t\t\t\t\tref={setFloatingRef}\n\t\t\t\t\tstyle={computedStyles}\n\t\t\t\t\ttransition={{\n\t\t\t\t\t\tdefault: { ease: \"anticipate\" },\n\t\t\t\t\t\tlayout: { duration: 0.3 },\n\t\t\t\t\t}}\n\t\t\t\t\tvariants={{\n\t\t\t\t\t\thidden: { opacity: 0, scale: 0.95, filter: \"blur(6px)\" },\n\t\t\t\t\t\tvisible: { opacity: 1, scale: 1, filter: \"blur(0px)\" },\n\t\t\t\t\t\texit: { opacity: 0, scale: 0.95, filter: \"blur(6px)\" },\n\t\t\t\t\t}}\n\t\t\t\t>\n\t\t\t\t\t<ContentInner\n\t\t\t\t\t\tcontainerRef={containerRef}\n\t\t\t\t\t\tshowScrollIndicator={showScrollIndicator}\n\t\t\t\t\t>\n\t\t\t\t\t\t{children}\n\t\t\t\t\t</ContentInner>\n\t\t\t\t</motion.div>\n\t\t\t</Primitive.Window>\n\t\t</SlotProvider>\n\t);\n};\n\n/**\n * Inner content component that consumes slots.\n * Separated to allow slot context to be established before consuming it.\n */\nconst ContentInner: React.FC<{\n\tchildren: React.ReactNode;\n\tcontainerRef: React.RefObject<HTMLDivElement | null>;\n\tshowScrollIndicator: boolean;\n}> = ({ children, containerRef, showScrollIndicator }) => {\n\tconst { header, footer, hasCustomHeader, hasCustomFooter } = useSlots();\n\n\treturn (\n\t\t<div className=\"relative flex h-full w-full flex-col\">\n\t\t\t{/* Custom header slot */}\n\t\t\t{hasCustomHeader && <div className=\"flex-shrink-0\">{header}</div>}\n\n\t\t\t<div\n\t\t\t\tclassName={cn(\n\t\t\t\t\t\"flex flex-1 flex-col overflow-y-auto\",\n\t\t\t\t\t// Only add top padding if no custom header (default header is absolute positioned)\n\t\t\t\t\t!hasCustomHeader && \"pt-16\"\n\t\t\t\t)}\n\t\t\t\tref={containerRef}\n\t\t\t>\n\t\t\t\t{children}\n\t\t\t</div>\n\n\t\t\t{/* Custom footer slot */}\n\t\t\t{hasCustomFooter && <div className=\"flex-shrink-0\">{footer}</div>}\n\n\t\t\t<AnimatePresence>\n\t\t\t\t{showScrollIndicator && (\n\t\t\t\t\t<>\n\t\t\t\t\t\t<motion.div\n\t\t\t\t\t\t\tanimate={{ opacity: 1 }}\n\t\t\t\t\t\t\tclassName=\"pointer-events-none absolute inset-x-0 bottom-0 z-5 h-32 bg-gradient-to-t from-co-background via-co-background/70 to-transparent\"\n\t\t\t\t\t\t\texit={{ opacity: 0 }}\n\t\t\t\t\t\t\tinitial={{ opacity: 0 }}\n\t\t\t\t\t\t\ttransition={{ duration: 0.3, ease: \"easeInOut\" }}\n\t\t\t\t\t\t/>\n\t\t\t\t\t\t<motion.div\n\t\t\t\t\t\t\tanimate={{ opacity: 0.6 }}\n\t\t\t\t\t\t\tclassName=\"pointer-events-none absolute inset-x-0 bottom-0 z-5 h-48 bg-gradient-to-t from-co-background/80 via-co-background/30 to-transparent\"\n\t\t\t\t\t\t\texit={{ opacity: 0 }}\n\t\t\t\t\t\t\tinitial={{ opacity: 0 }}\n\t\t\t\t\t\t\ttransition={{ duration: 0.4, ease: \"easeInOut\", delay: 0.05 }}\n\t\t\t\t\t\t/>\n\t\t\t\t\t</>\n\t\t\t\t)}\n\t\t\t</AnimatePresence>\n\t\t</div>\n\t);\n};\n"],"mappings":"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"}
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import { cn } from "../utils/index.js";
|
|
2
2
|
import { Avatar } from "./avatar.js";
|
|
3
3
|
import { BouncingDots } from "./typing-indicator.js";
|
|
4
|
-
import icons_default from "./icons.js";
|
|
5
4
|
import { coButtonVariants } from "./button.js";
|
|
5
|
+
import icons_default from "./icons.js";
|
|
6
6
|
import { useSupportText } from "../text/index.js";
|
|
7
7
|
import { useConversationPreview } from "../../hooks/use-conversation-preview.js";
|
|
8
8
|
import { ConversationStatus } from "@cossistant/types";
|
|
9
|
-
import {
|
|
9
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
10
10
|
|
|
11
11
|
//#region src/support/components/conversation-button-link.tsx
|
|
12
12
|
const STATUS_BADGE_CLASSNAMES = {
|
|
@@ -14,7 +14,6 @@ const STATUS_BADGE_CLASSNAMES = {
|
|
|
14
14
|
[ConversationStatus.RESOLVED]: "bg-co-neutral/20 text-co-neutral-foreground",
|
|
15
15
|
[ConversationStatus.SPAM]: "bg-co-warning/20 text-co-warning-foreground"
|
|
16
16
|
};
|
|
17
|
-
const DEFAULT_STATUS_BADGE_CLASSNAME = "bg-co-neutral/20 text-co-neutral-foreground";
|
|
18
17
|
const STATUS_TEXT_KEYS = {
|
|
19
18
|
[ConversationStatus.OPEN]: "component.conversationButtonLink.status.open",
|
|
20
19
|
[ConversationStatus.RESOLVED]: "component.conversationButtonLink.status.resolved",
|
|
@@ -28,42 +27,38 @@ function ConversationButtonLink({ conversation, onClick, className }) {
|
|
|
28
27
|
const preview = useConversationPreview({ conversation });
|
|
29
28
|
const text = useSupportText();
|
|
30
29
|
const { lastMessage, assignedAgent, typing } = preview;
|
|
31
|
-
|
|
30
|
+
conversation.deletedAt ? STATUS_BADGE_CLASSNAMES[ConversationStatus.RESOLVED] : STATUS_BADGE_CLASSNAMES[conversation.status];
|
|
32
31
|
const statusTextKey = conversation.deletedAt ? STATUS_TEXT_KEYS[ConversationStatus.RESOLVED] : STATUS_TEXT_KEYS[conversation.status];
|
|
33
|
-
|
|
34
|
-
const
|
|
35
|
-
|
|
36
|
-
time: lastMessage.time
|
|
37
|
-
}) }) : void 0;
|
|
32
|
+
conversation.deletedAt ? text("component.conversationButtonLink.status.closed") : statusTextKey ? text(statusTextKey) : text("common.fallbacks.unknown");
|
|
33
|
+
const displayTitle = conversation.title || preview.title;
|
|
34
|
+
const messagePreview = lastMessage?.content || null;
|
|
38
35
|
return /* @__PURE__ */ jsxs("button", {
|
|
39
36
|
className: cn(coButtonVariants({
|
|
40
37
|
variant: "secondary",
|
|
41
38
|
size: "large"
|
|
42
|
-
}), "group/btn relative gap-
|
|
39
|
+
}), "group/btn relative gap-2 border-0 border-co-border/50 border-b pr-3 pl-3 text-left transition-colors first-of-type:rounded-t last-of-type:rounded-b last-of-type:border-b-0 has-[>svg]:pl-3", className),
|
|
43
40
|
onClick,
|
|
44
41
|
type: "button",
|
|
45
42
|
children: [
|
|
46
43
|
/* @__PURE__ */ jsx(Avatar, {
|
|
47
|
-
className: "
|
|
44
|
+
className: "size-8 flex-shrink-0",
|
|
48
45
|
image: assignedAgent?.image,
|
|
46
|
+
isAI: assignedAgent?.type === "ai",
|
|
47
|
+
lastSeenAt: assignedAgent?.lastSeenAt,
|
|
49
48
|
name: assignedAgent?.name ?? text("common.fallbacks.supportTeam")
|
|
50
49
|
}),
|
|
51
|
-
/* @__PURE__ */
|
|
52
|
-
className: "flex min-w-0 flex-1 flex-col gap-0.5",
|
|
53
|
-
children:
|
|
50
|
+
/* @__PURE__ */ jsxs("div", {
|
|
51
|
+
className: "mr-6 ml-1 flex min-w-0 flex-1 flex-col gap-0.5",
|
|
52
|
+
children: [/* @__PURE__ */ jsx("div", {
|
|
54
53
|
className: "flex max-w-[90%] items-center justify-between gap-2",
|
|
55
54
|
children: /* @__PURE__ */ jsx("h3", {
|
|
56
55
|
className: "truncate font-medium text-co-primary text-sm",
|
|
57
|
-
children:
|
|
56
|
+
children: displayTitle
|
|
58
57
|
})
|
|
59
|
-
}),
|
|
60
|
-
className: "text-co-primary/60 text-
|
|
61
|
-
children:
|
|
62
|
-
}) : null]
|
|
63
|
-
}),
|
|
64
|
-
/* @__PURE__ */ jsx("div", {
|
|
65
|
-
className: cn("mr-6 inline-flex items-center rounded px-2 py-0.5 font-medium text-[9px] uppercase", statusBadgeClassName),
|
|
66
|
-
children: statusText
|
|
58
|
+
}), typing.isTyping ? /* @__PURE__ */ jsx(BouncingDots, {}) : messagePreview ? /* @__PURE__ */ jsx("p", {
|
|
59
|
+
className: "truncate text-co-primary/60 text-sm",
|
|
60
|
+
children: messagePreview
|
|
61
|
+
}) : null]
|
|
67
62
|
}),
|
|
68
63
|
/* @__PURE__ */ jsx(icons_default, {
|
|
69
64
|
className: "-translate-y-1/2 absolute top-1/2 right-4 size-3 text-co-primary/60 transition-transform duration-200 group-hover/btn:translate-x-0.5 group-hover/btn:text-co-primary",
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"conversation-button-link.js","names":["STATUS_BADGE_CLASSNAMES: Record<ConversationStatus, string>","STATUS_TEXT_KEYS: Record<ConversationStatus, SupportTextKey>","Icon"],"sources":["../../../src/support/components/conversation-button-link.tsx"],"sourcesContent":["import { type Conversation, ConversationStatus } from \"@cossistant/types\";\nimport type React from \"react\";\nimport { useConversationPreview } from \"../../hooks/use-conversation-preview\";\nimport { type SupportTextKey, useSupportText } from \"../text\";\nimport { cn } from \"../utils\";\nimport { Avatar } from \"./avatar\";\nimport { coButtonVariants } from \"./button\";\nimport Icon from \"./icons\";\nimport { BouncingDots } from \"./typing-indicator\";\n\nexport type ConversationButtonLinkProps = {\n\tconversation: Conversation;\n\tonClick?: () => void;\n\tclassName?: string;\n};\n\nconst STATUS_BADGE_CLASSNAMES: Record<ConversationStatus, string> = {\n\t[ConversationStatus.OPEN]: \"bg-co-success/20 text-co-success-foreground\",\n\t[ConversationStatus.RESOLVED]: \"bg-co-neutral/20 text-co-neutral-foreground\",\n\t[ConversationStatus.SPAM]: \"bg-co-warning/20 text-co-warning-foreground\",\n};\n\nconst DEFAULT_STATUS_BADGE_CLASSNAME =\n\t\"bg-co-neutral/20 text-co-neutral-foreground\";\n\nconst STATUS_TEXT_KEYS: Record<ConversationStatus, SupportTextKey> = {\n\t[ConversationStatus.OPEN]: \"component.conversationButtonLink.status.open\",\n\t[ConversationStatus.RESOLVED]:\n\t\t\"component.conversationButtonLink.status.resolved\",\n\t[ConversationStatus.SPAM]: \"component.conversationButtonLink.status.spam\",\n};\n\n/**\n * Renders a navigable preview card for a conversation including assigned agent\n * details, last message snippets and typing indicators.\n */\nexport function ConversationButtonLink({\n\tconversation,\n\tonClick,\n\tclassName,\n}: ConversationButtonLinkProps): React.ReactElement | null {\n\tconst preview = useConversationPreview({ conversation });\n\tconst text = useSupportText();\n\tconst { lastMessage, assignedAgent, typing } = preview;\n\n\tconst statusBadgeClassName = conversation.deletedAt\n\t\t? STATUS_BADGE_CLASSNAMES[ConversationStatus.RESOLVED]\n\t\t: (STATUS_BADGE_CLASSNAMES[conversation.status] ??\n\t\t\tDEFAULT_STATUS_BADGE_CLASSNAME);\n\n\tconst statusTextKey = conversation.deletedAt\n\t\t? STATUS_TEXT_KEYS[ConversationStatus.RESOLVED]\n\t\t: STATUS_TEXT_KEYS[conversation.status];\n\n\tconst statusText = conversation.deletedAt\n\t\t? text(\"component.conversationButtonLink.status.closed\")\n\t\t: statusTextKey\n\t\t\t? text(statusTextKey)\n\t\t\t: text(\"common.fallbacks.unknown\");\n\n\
|
|
1
|
+
{"version":3,"file":"conversation-button-link.js","names":["STATUS_BADGE_CLASSNAMES: Record<ConversationStatus, string>","STATUS_TEXT_KEYS: Record<ConversationStatus, SupportTextKey>","Icon"],"sources":["../../../src/support/components/conversation-button-link.tsx"],"sourcesContent":["import { type Conversation, ConversationStatus } from \"@cossistant/types\";\nimport type React from \"react\";\nimport { useConversationPreview } from \"../../hooks/use-conversation-preview\";\nimport { type SupportTextKey, useSupportText } from \"../text\";\nimport { cn } from \"../utils\";\nimport { Avatar } from \"./avatar\";\nimport { coButtonVariants } from \"./button\";\nimport Icon from \"./icons\";\nimport { BouncingDots } from \"./typing-indicator\";\n\nexport type ConversationButtonLinkProps = {\n\tconversation: Conversation;\n\tonClick?: () => void;\n\tclassName?: string;\n};\n\nconst STATUS_BADGE_CLASSNAMES: Record<ConversationStatus, string> = {\n\t[ConversationStatus.OPEN]: \"bg-co-success/20 text-co-success-foreground\",\n\t[ConversationStatus.RESOLVED]: \"bg-co-neutral/20 text-co-neutral-foreground\",\n\t[ConversationStatus.SPAM]: \"bg-co-warning/20 text-co-warning-foreground\",\n};\n\nconst DEFAULT_STATUS_BADGE_CLASSNAME =\n\t\"bg-co-neutral/20 text-co-neutral-foreground\";\n\nconst STATUS_TEXT_KEYS: Record<ConversationStatus, SupportTextKey> = {\n\t[ConversationStatus.OPEN]: \"component.conversationButtonLink.status.open\",\n\t[ConversationStatus.RESOLVED]:\n\t\t\"component.conversationButtonLink.status.resolved\",\n\t[ConversationStatus.SPAM]: \"component.conversationButtonLink.status.spam\",\n};\n\n/**\n * Renders a navigable preview card for a conversation including assigned agent\n * details, last message snippets and typing indicators.\n */\nexport function ConversationButtonLink({\n\tconversation,\n\tonClick,\n\tclassName,\n}: ConversationButtonLinkProps): React.ReactElement | null {\n\tconst preview = useConversationPreview({ conversation });\n\tconst text = useSupportText();\n\tconst { lastMessage, assignedAgent, typing } = preview;\n\n\tconst statusBadgeClassName = conversation.deletedAt\n\t\t? STATUS_BADGE_CLASSNAMES[ConversationStatus.RESOLVED]\n\t\t: (STATUS_BADGE_CLASSNAMES[conversation.status] ??\n\t\t\tDEFAULT_STATUS_BADGE_CLASSNAME);\n\n\tconst statusTextKey = conversation.deletedAt\n\t\t? STATUS_TEXT_KEYS[ConversationStatus.RESOLVED]\n\t\t: STATUS_TEXT_KEYS[conversation.status];\n\n\tconst statusText = conversation.deletedAt\n\t\t? text(\"component.conversationButtonLink.status.closed\")\n\t\t: statusTextKey\n\t\t\t? text(statusTextKey)\n\t\t\t: text(\"common.fallbacks.unknown\");\n\n\t// Show the actual title if it exists, otherwise use the preview title (which may fallback to message)\n\tconst displayTitle = conversation.title || preview.title;\n\n\t// Show the truncated message content as secondary text\n\tconst messagePreview = lastMessage?.content || null;\n\n\treturn (\n\t\t<button\n\t\t\tclassName={cn(\n\t\t\t\tcoButtonVariants({\n\t\t\t\t\tvariant: \"secondary\",\n\t\t\t\t\tsize: \"large\",\n\t\t\t\t}),\n\t\t\t\t\"group/btn relative gap-2 border-0 border-co-border/50 border-b pr-3 pl-3 text-left transition-colors first-of-type:rounded-t last-of-type:rounded-b last-of-type:border-b-0 has-[>svg]:pl-3\",\n\t\t\t\tclassName\n\t\t\t)}\n\t\t\tonClick={onClick}\n\t\t\ttype=\"button\"\n\t\t>\n\t\t\t<Avatar\n\t\t\t\tclassName=\"size-8 flex-shrink-0\"\n\t\t\t\timage={assignedAgent?.image}\n\t\t\t\tisAI={assignedAgent?.type === \"ai\"}\n\t\t\t\tlastSeenAt={assignedAgent?.lastSeenAt}\n\t\t\t\tname={assignedAgent?.name ?? text(\"common.fallbacks.supportTeam\")}\n\t\t\t/>\n\n\t\t\t<div className=\"mr-6 ml-1 flex min-w-0 flex-1 flex-col gap-0.5\">\n\t\t\t\t<div className=\"flex max-w-[90%] items-center justify-between gap-2\">\n\t\t\t\t\t<h3 className=\"truncate font-medium text-co-primary text-sm\">\n\t\t\t\t\t\t{displayTitle}\n\t\t\t\t\t</h3>\n\t\t\t\t</div>\n\t\t\t\t{typing.isTyping ? (\n\t\t\t\t\t<BouncingDots />\n\t\t\t\t) : messagePreview ? (\n\t\t\t\t\t<p className=\"truncate text-co-primary/60 text-sm\">\n\t\t\t\t\t\t{messagePreview}\n\t\t\t\t\t</p>\n\t\t\t\t) : null}\n\t\t\t</div>\n\n\t\t\t{/* <div\n className={cn(\n \"mr-6 inline-flex items-center rounded px-2 py-0.5 font-medium text-[9px] uppercase\",\n statusBadgeClassName,\n )}\n >\n {statusText}\n </div> */}\n\n\t\t\t<Icon\n\t\t\t\tclassName=\"-translate-y-1/2 absolute top-1/2 right-4 size-3 text-co-primary/60 transition-transform duration-200 group-hover/btn:translate-x-0.5 group-hover/btn:text-co-primary\"\n\t\t\t\tname=\"arrow-right\"\n\t\t\t\tvariant=\"default\"\n\t\t\t/>\n\t\t</button>\n\t);\n}\n"],"mappings":";;;;;;;;;;;AAgBA,MAAMA,0BAA8D;EAClE,mBAAmB,OAAO;EAC1B,mBAAmB,WAAW;EAC9B,mBAAmB,OAAO;CAC3B;AAKD,MAAMC,mBAA+D;EACnE,mBAAmB,OAAO;EAC1B,mBAAmB,WACnB;EACA,mBAAmB,OAAO;CAC3B;;;;;AAMD,SAAgB,uBAAuB,EACtC,cACA,SACA,aAC0D;CAC1D,MAAM,UAAU,uBAAuB,EAAE,cAAc,CAAC;CACxD,MAAM,OAAO,gBAAgB;CAC7B,MAAM,EAAE,aAAa,eAAe,WAAW;AAElB,cAAa,YACvC,wBAAwB,mBAAmB,YAC1C,wBAAwB,aAAa;CAGzC,MAAM,gBAAgB,aAAa,YAChC,iBAAiB,mBAAmB,YACpC,iBAAiB,aAAa;AAEd,cAAa,YAC7B,KAAK,iDAAiD,GACtD,gBACC,KAAK,cAAc,GACnB,KAAK,2BAA2B;CAGpC,MAAM,eAAe,aAAa,SAAS,QAAQ;CAGnD,MAAM,iBAAiB,aAAa,WAAW;AAE/C,QACC,qBAAC;EACA,WAAW,GACV,iBAAiB;GAChB,SAAS;GACT,MAAM;GACN,CAAC,EACF,+LACA,UACA;EACQ;EACT,MAAK;;GAEL,oBAAC;IACA,WAAU;IACV,OAAO,eAAe;IACtB,MAAM,eAAe,SAAS;IAC9B,YAAY,eAAe;IAC3B,MAAM,eAAe,QAAQ,KAAK,+BAA+B;KAChE;GAEF,qBAAC;IAAI,WAAU;eACd,oBAAC;KAAI,WAAU;eACd,oBAAC;MAAG,WAAU;gBACZ;OACG;MACA,EACL,OAAO,WACP,oBAAC,iBAAe,GACb,iBACH,oBAAC;KAAE,WAAU;eACX;MACE,GACD;KACC;GAWN,oBAACC;IACA,WAAU;IACV,MAAK;IACL,SAAQ;KACP;;GACM"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"conversation-event.d.ts","names":[],"sources":["../../../src/support/components/conversation-event.tsx"],"sourcesContent":[],"mappings":";;;;
|
|
1
|
+
{"version":3,"file":"conversation-event.d.ts","names":[],"sources":["../../../src/support/components/conversation-event.tsx"],"sourcesContent":[],"mappings":";;;;KAUY,sBAAA;SACJ;EADI,iBAAA,EAEQ,gBAFc,EAAA;EAC1B,oBAAA,EAEe,mBAFf,EAAA;EACY,SAAA,CAAA,EAAA,MAAA;CACG;AAAmB,cAI7B,iBAJ6B,EAIV,KAAA,CAAM,EAJI,CAID,sBAJC,CAAA"}
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { Avatar } from "./avatar.js";
|
|
2
|
-
import { CossistantLogo } from "./cossistant-branding.js";
|
|
3
2
|
import { useSupportText } from "../text/index.js";
|
|
4
3
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
5
4
|
import { motion } from "motion/react";
|
|
@@ -49,11 +48,14 @@ const ConversationEvent = ({ event, availableAIAgents, createdAt, availableHuman
|
|
|
49
48
|
children: [
|
|
50
49
|
/* @__PURE__ */ jsx("div", {
|
|
51
50
|
className: "flex flex-col justify-end",
|
|
52
|
-
children: isAI ? /* @__PURE__ */ jsx(
|
|
53
|
-
className: "
|
|
54
|
-
|
|
51
|
+
children: isAI ? /* @__PURE__ */ jsx(Avatar, {
|
|
52
|
+
className: "size-5 flex-shrink-0",
|
|
53
|
+
image: aiAgent?.image,
|
|
54
|
+
isAI: true,
|
|
55
|
+
name: aiAgent?.name || text("common.fallbacks.cossistant"),
|
|
56
|
+
showBackground: !!aiAgent?.image
|
|
55
57
|
}) : /* @__PURE__ */ jsx(Avatar, {
|
|
56
|
-
className: "size-5 flex-shrink-0 overflow-clip rounded-
|
|
58
|
+
className: "size-5 flex-shrink-0 overflow-clip rounded-lg",
|
|
57
59
|
image: humanAgent?.image,
|
|
58
60
|
name: humanAgent?.name || text("common.fallbacks.someone")
|
|
59
61
|
})
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"conversation-event.js","names":["ConversationEvent: React.FC<ConversationEventProps>"],"sources":["../../../src/support/components/conversation-event.tsx"],"sourcesContent":["import type {\n\tAvailableAIAgent,\n\tAvailableHumanAgent,\n\tTimelinePartEvent,\n} from \"@cossistant/types\";\nimport { motion } from \"motion/react\";\nimport type React from \"react\";\nimport { useSupportText } from \"../text\";\nimport { Avatar } from \"./avatar\";\
|
|
1
|
+
{"version":3,"file":"conversation-event.js","names":["ConversationEvent: React.FC<ConversationEventProps>"],"sources":["../../../src/support/components/conversation-event.tsx"],"sourcesContent":["import type {\n\tAvailableAIAgent,\n\tAvailableHumanAgent,\n\tTimelinePartEvent,\n} from \"@cossistant/types\";\nimport { motion } from \"motion/react\";\nimport type React from \"react\";\nimport { useSupportText } from \"../text\";\nimport { Avatar } from \"./avatar\";\n\nexport type ConversationEventProps = {\n\tevent: TimelinePartEvent;\n\tavailableAIAgents: AvailableAIAgent[];\n\tavailableHumanAgents: AvailableHumanAgent[];\n\tcreatedAt?: string;\n};\n\nexport const ConversationEvent: React.FC<ConversationEventProps> = ({\n\tevent,\n\tavailableAIAgents,\n\tcreatedAt,\n\tavailableHumanAgents,\n}) => {\n\tconst text = useSupportText();\n\tconst isAI = event.actorAiAgentId !== null;\n\tconst humanAgent = availableHumanAgents.find(\n\t\t(agent) => agent.id === event.actorUserId\n\t);\n\tconst aiAgent = availableAIAgents.find(\n\t\t(agent) => agent.id === event.actorAiAgentId\n\t);\n\n\t// Get the actor name\n\tconst actorName = isAI\n\t\t? aiAgent?.name || text(\"common.fallbacks.cossistant\")\n\t\t: humanAgent?.name || text(\"common.fallbacks.someone\");\n\n\t// Convert event type to plain English\n\tconst getEventText = () => {\n\t\tswitch (event.eventType) {\n\t\t\tcase \"assigned\":\n\t\t\t\treturn text(\"component.conversationEvent.assigned\", {\n\t\t\t\t\tactorName,\n\t\t\t\t});\n\t\t\tcase \"unassigned\":\n\t\t\t\treturn text(\"component.conversationEvent.unassigned\", {\n\t\t\t\t\tactorName,\n\t\t\t\t});\n\t\t\tcase \"participant_requested\":\n\t\t\t\treturn text(\"component.conversationEvent.participantRequested\", {\n\t\t\t\t\tactorName,\n\t\t\t\t});\n\t\t\tcase \"participant_joined\":\n\t\t\t\treturn text(\"component.conversationEvent.participantJoined\", {\n\t\t\t\t\tactorName,\n\t\t\t\t});\n\t\t\tcase \"participant_left\":\n\t\t\t\treturn text(\"component.conversationEvent.participantLeft\", {\n\t\t\t\t\tactorName,\n\t\t\t\t});\n\t\t\tcase \"status_changed\":\n\t\t\t\treturn text(\"component.conversationEvent.statusChanged\", {\n\t\t\t\t\tactorName,\n\t\t\t\t});\n\t\t\tcase \"priority_changed\":\n\t\t\t\treturn text(\"component.conversationEvent.priorityChanged\", {\n\t\t\t\t\tactorName,\n\t\t\t\t});\n\t\t\tcase \"tag_added\":\n\t\t\t\treturn text(\"component.conversationEvent.tagAdded\", {\n\t\t\t\t\tactorName,\n\t\t\t\t});\n\t\t\tcase \"tag_removed\":\n\t\t\t\treturn text(\"component.conversationEvent.tagRemoved\", {\n\t\t\t\t\tactorName,\n\t\t\t\t});\n\t\t\tcase \"resolved\":\n\t\t\t\treturn text(\"component.conversationEvent.resolved\", {\n\t\t\t\t\tactorName,\n\t\t\t\t});\n\t\t\tcase \"reopened\":\n\t\t\t\treturn text(\"component.conversationEvent.reopened\", {\n\t\t\t\t\tactorName,\n\t\t\t\t});\n\t\t\tcase \"visitor_blocked\":\n\t\t\t\treturn text(\"component.conversationEvent.visitorBlocked\", {\n\t\t\t\t\tactorName,\n\t\t\t\t});\n\t\t\tcase \"visitor_unblocked\":\n\t\t\t\treturn text(\"component.conversationEvent.visitorUnblocked\", {\n\t\t\t\t\tactorName,\n\t\t\t\t});\n\t\t\tcase \"visitor_identified\":\n\t\t\t\treturn text(\"component.conversationEvent.visitorIdentified\", {\n\t\t\t\t\tactorName,\n\t\t\t\t});\n\t\t\tdefault:\n\t\t\t\treturn text(\"component.conversationEvent.default\", {\n\t\t\t\t\tactorName,\n\t\t\t\t});\n\t\t}\n\t};\n\n\treturn (\n\t\t<motion.div\n\t\t\tanimate={{ opacity: 1, scale: 1 }}\n\t\t\tclassName=\"flex items-center justify-center pt-4 pb-8\"\n\t\t\tinitial={{ opacity: 0, scale: 0.95 }}\n\t\t\ttransition={{ duration: 0.3, ease: \"easeOut\" }}\n\t\t>\n\t\t\t<div className=\"flex items-center gap-2 text-co-muted-foreground text-xs\">\n\t\t\t\t<div className=\"flex flex-col justify-end\">\n\t\t\t\t\t{isAI ? (\n\t\t\t\t\t\t<Avatar\n\t\t\t\t\t\t\tclassName=\"size-5 flex-shrink-0\"\n\t\t\t\t\t\t\timage={aiAgent?.image}\n\t\t\t\t\t\t\tisAI\n\t\t\t\t\t\t\tname={aiAgent?.name || text(\"common.fallbacks.cossistant\")}\n\t\t\t\t\t\t\tshowBackground={!!aiAgent?.image}\n\t\t\t\t\t\t/>\n\t\t\t\t\t) : (\n\t\t\t\t\t\t<Avatar\n\t\t\t\t\t\t\tclassName=\"size-5 flex-shrink-0 overflow-clip rounded-lg\"\n\t\t\t\t\t\t\timage={humanAgent?.image}\n\t\t\t\t\t\t\tname={humanAgent?.name || text(\"common.fallbacks.someone\")}\n\t\t\t\t\t\t/>\n\t\t\t\t\t)}\n\t\t\t\t</div>\n\t\t\t\t<span className=\"px-2\">{getEventText()}</span>\n\t\t\t\t{createdAt && (\n\t\t\t\t\t<time className=\"text-[10px]\">\n\t\t\t\t\t\t{new Date(createdAt).toLocaleTimeString([], {\n\t\t\t\t\t\t\thour: \"2-digit\",\n\t\t\t\t\t\t\tminute: \"2-digit\",\n\t\t\t\t\t\t})}\n\t\t\t\t\t</time>\n\t\t\t\t)}\n\t\t\t</div>\n\t\t</motion.div>\n\t);\n};\n\nConversationEvent.displayName = \"ConversationEvent\";\n"],"mappings":";;;;;;AAiBA,MAAaA,qBAAuD,EACnE,OACA,mBACA,WACA,2BACK;CACL,MAAM,OAAO,gBAAgB;CAC7B,MAAM,OAAO,MAAM,mBAAmB;CACtC,MAAM,aAAa,qBAAqB,MACtC,UAAU,MAAM,OAAO,MAAM,YAC9B;CACD,MAAM,UAAU,kBAAkB,MAChC,UAAU,MAAM,OAAO,MAAM,eAC9B;CAGD,MAAM,YAAY,OACf,SAAS,QAAQ,KAAK,8BAA8B,GACpD,YAAY,QAAQ,KAAK,2BAA2B;CAGvD,MAAM,qBAAqB;AAC1B,UAAQ,MAAM,WAAd;GACC,KAAK,WACJ,QAAO,KAAK,wCAAwC,EACnD,WACA,CAAC;GACH,KAAK,aACJ,QAAO,KAAK,0CAA0C,EACrD,WACA,CAAC;GACH,KAAK,wBACJ,QAAO,KAAK,oDAAoD,EAC/D,WACA,CAAC;GACH,KAAK,qBACJ,QAAO,KAAK,iDAAiD,EAC5D,WACA,CAAC;GACH,KAAK,mBACJ,QAAO,KAAK,+CAA+C,EAC1D,WACA,CAAC;GACH,KAAK,iBACJ,QAAO,KAAK,6CAA6C,EACxD,WACA,CAAC;GACH,KAAK,mBACJ,QAAO,KAAK,+CAA+C,EAC1D,WACA,CAAC;GACH,KAAK,YACJ,QAAO,KAAK,wCAAwC,EACnD,WACA,CAAC;GACH,KAAK,cACJ,QAAO,KAAK,0CAA0C,EACrD,WACA,CAAC;GACH,KAAK,WACJ,QAAO,KAAK,wCAAwC,EACnD,WACA,CAAC;GACH,KAAK,WACJ,QAAO,KAAK,wCAAwC,EACnD,WACA,CAAC;GACH,KAAK,kBACJ,QAAO,KAAK,8CAA8C,EACzD,WACA,CAAC;GACH,KAAK,oBACJ,QAAO,KAAK,gDAAgD,EAC3D,WACA,CAAC;GACH,KAAK,qBACJ,QAAO,KAAK,iDAAiD,EAC5D,WACA,CAAC;GACH,QACC,QAAO,KAAK,uCAAuC,EAClD,WACA,CAAC;;;AAIL,QACC,oBAAC,OAAO;EACP,SAAS;GAAE,SAAS;GAAG,OAAO;GAAG;EACjC,WAAU;EACV,SAAS;GAAE,SAAS;GAAG,OAAO;GAAM;EACpC,YAAY;GAAE,UAAU;GAAK,MAAM;GAAW;YAE9C,qBAAC;GAAI,WAAU;;IACd,oBAAC;KAAI,WAAU;eACb,OACA,oBAAC;MACA,WAAU;MACV,OAAO,SAAS;MAChB;MACA,MAAM,SAAS,QAAQ,KAAK,8BAA8B;MAC1D,gBAAgB,CAAC,CAAC,SAAS;OAC1B,GAEF,oBAAC;MACA,WAAU;MACV,OAAO,YAAY;MACnB,MAAM,YAAY,QAAQ,KAAK,2BAA2B;OACzD;MAEE;IACN,oBAAC;KAAK,WAAU;eAAQ,cAAc;MAAQ;IAC7C,aACA,oBAAC;KAAK,WAAU;eACd,IAAI,KAAK,UAAU,CAAC,mBAAmB,EAAE,EAAE;MAC3C,MAAM;MACN,QAAQ;MACR,CAAC;MACI;;IAEH;GACM;;AAIf,kBAAkB,cAAc"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { TimelineItem } from "../../timeline-item.js";
|
|
1
|
+
import { TimelineItem } from "../../packages/types/src/api/timeline-item.js";
|
|
2
2
|
import { DaySeparatorItem } from "../../hooks/private/use-grouped-messages.js";
|
|
3
3
|
import React from "react";
|
|
4
4
|
import { AvailableAIAgent, AvailableHumanAgent } from "@cossistant/types";
|
|
@@ -67,12 +67,12 @@ const ConversationTimelineList = ({ conversationId, items: timelineItems, classN
|
|
|
67
67
|
date: item.date,
|
|
68
68
|
dateString: item.dateString,
|
|
69
69
|
children: ({ formattedDate }) => /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
70
|
-
/* @__PURE__ */ jsx(DaySeparatorLine, { className: "flex-1 border-gray-
|
|
70
|
+
/* @__PURE__ */ jsx(DaySeparatorLine, { className: "flex-1 border-gray-300/20 border-t dark:border-gray-600/20" }),
|
|
71
71
|
/* @__PURE__ */ jsx(DaySeparatorLabel, {
|
|
72
|
-
className: "px-3 text-gray-
|
|
72
|
+
className: "px-3 text-gray-400/50 text-xs dark:text-gray-500/50",
|
|
73
73
|
formattedDate
|
|
74
74
|
}),
|
|
75
|
-
/* @__PURE__ */ jsx(DaySeparatorLine, { className: "flex-1 border-gray-
|
|
75
|
+
/* @__PURE__ */ jsx(DaySeparatorLine, { className: "flex-1 border-gray-300/20 border-t dark:border-gray-600/20" })
|
|
76
76
|
] })
|
|
77
77
|
}, `day-separator-${item.dateString}`);
|
|
78
78
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"conversation-timeline.js","names":["EMPTY_SEEN_BY_IDS: readonly string[]","EMPTY_SEEN_BY_NAMES: readonly string[]","ConversationTimelineList: React.FC<ConversationTimelineProps>","names: string[]","PrimitiveConversationTimeline"],"sources":["../../../src/support/components/conversation-timeline.tsx"],"sourcesContent":["import type { AvailableAIAgent, AvailableHumanAgent } from \"@cossistant/types\";\nimport type {\n\tTimelineItem,\n\tTimelinePartEvent,\n} from \"@cossistant/types/api/timeline-item\";\nimport type React from \"react\";\nimport { useCallback, useMemo } from \"react\";\nimport type { DaySeparatorItem } from \"../../hooks/private/use-grouped-messages\";\nimport { useConversationTimeline } from \"../../hooks/use-conversation-timeline\";\nimport { useTypingSound } from \"../../hooks/use-typing-sound\";\nimport {\n\tConversationTimelineContainer,\n\tConversationTimeline as PrimitiveConversationTimeline,\n} from \"../../primitives/conversation-timeline\";\nimport {\n\tDaySeparator,\n\tDaySeparatorLabel,\n\tDaySeparatorLine,\n\tdefaultFormatDate,\n} from \"../../primitives/day-separator\";\nimport { cn } from \"../utils\";\nimport { ConversationEvent } from \"./conversation-event\";\nimport { TimelineMessageGroup } from \"./timeline-message-group\";\nimport { TypingIndicator, type TypingParticipant } from \"./typing-indicator\";\n\n// Helper to extract event part from timeline item\nfunction extractEventPart(item: TimelineItem): TimelinePartEvent | null {\n\tif (item.type !== \"event\") {\n\t\treturn null;\n\t}\n\n\tconst eventPart = item.parts.find(\n\t\t(part): part is TimelinePartEvent => part.type === \"event\"\n\t);\n\n\treturn eventPart || null;\n}\n\nconst EMPTY_SEEN_BY_IDS: readonly string[] = Object.freeze([]);\nconst EMPTY_SEEN_BY_NAMES: readonly string[] = Object.freeze([]);\n\nexport type ConversationTimelineToolProps = {\n\titem: TimelineItem;\n\tconversationId: string;\n};\n\nexport type ConversationTimelineToolDefinition = {\n\tcomponent: React.ComponentType<ConversationTimelineToolProps>;\n};\n\nexport type ConversationTimelineTools = Record<\n\tstring,\n\tConversationTimelineToolDefinition\n>;\n\nexport type ConversationTimelineProps = {\n\tconversationId: string;\n\titems: TimelineItem[];\n\tclassName?: string;\n\tavailableAIAgents: AvailableAIAgent[];\n\tavailableHumanAgents: AvailableHumanAgent[];\n\tcurrentVisitorId?: string;\n\ttools?: ConversationTimelineTools;\n\trenderDaySeparator?: (props: {\n\t\titem: DaySeparatorItem;\n\t\tformatDate: (date: Date) => string;\n\t}) => React.ReactNode;\n};\n\nexport const ConversationTimelineList: React.FC<ConversationTimelineProps> = ({\n\tconversationId,\n\titems: timelineItems,\n\tclassName,\n\tavailableAIAgents = [],\n\tavailableHumanAgents = [],\n\tcurrentVisitorId,\n\ttools,\n\trenderDaySeparator,\n}) => {\n\tconst timeline = useConversationTimeline({\n\t\tconversationId,\n\t\titems: timelineItems,\n\t\tcurrentVisitorId,\n\t});\n\n\tconst typingIndicatorParticipants = useMemo(\n\t\t() =>\n\t\t\ttimeline.typingParticipants.map<TypingParticipant>((participant) => ({\n\t\t\t\tid: participant.id,\n\t\t\t\ttype: participant.type,\n\t\t\t})),\n\t\t[timeline.typingParticipants]\n\t);\n\n\t// Play typing sound when someone is typing\n\tuseTypingSound(typingIndicatorParticipants.length > 0, {\n\t\tvolume: 1,\n\t\tplaybackRate: 1.3,\n\t});\n\n\tconst seenNameLookup = useMemo(() => {\n\t\tconst map = new Map<string, string>();\n\n\t\tfor (const agent of availableHumanAgents) {\n\t\t\tif (agent.name) {\n\t\t\t\tmap.set(agent.id, agent.name);\n\t\t\t}\n\t\t}\n\n\t\tfor (const agent of availableAIAgents) {\n\t\t\tif (agent.name) {\n\t\t\t\tmap.set(agent.id, agent.name);\n\t\t\t}\n\t\t}\n\n\t\treturn map;\n\t}, [availableHumanAgents, availableAIAgents]);\n\n\tconst getSeenByNames = useCallback(\n\t\t(ids: readonly string[] = EMPTY_SEEN_BY_IDS): readonly string[] => {\n\t\t\tif (ids.length === 0 || seenNameLookup.size === 0) {\n\t\t\t\treturn EMPTY_SEEN_BY_NAMES;\n\t\t\t}\n\n\t\t\tconst uniqueNames = new Set<string>();\n\t\t\tconst names: string[] = [];\n\n\t\t\tfor (const id of ids) {\n\t\t\t\tconst name = seenNameLookup.get(id);\n\t\t\t\tif (!name || uniqueNames.has(name)) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\tuniqueNames.add(name);\n\t\t\t\tnames.push(name);\n\t\t\t}\n\n\t\t\tif (names.length === 0) {\n\t\t\t\treturn EMPTY_SEEN_BY_NAMES;\n\t\t\t}\n\n\t\t\treturn Object.freeze(names);\n\t\t},\n\t\t[seenNameLookup]\n\t);\n\n\treturn (\n\t\t<PrimitiveConversationTimeline\n\t\t\tautoScroll={true}\n\t\t\tclassName={cn(\n\t\t\t\t\"overflow-y-scroll px-3 py-6\",\n\t\t\t\t\"co-scrollbar-thin\",\n\t\t\t\t\"h-full w-full\",\n\t\t\t\tclassName\n\t\t\t)}\n\t\t\tid=\"conversation-timeline\"\n\t\t\titems={timelineItems}\n\t\t>\n\t\t\t<ConversationTimelineContainer className=\"flex min-h-full w-full flex-col gap-5\">\n\t\t\t\t{timeline.groupedMessages.items.map((item, index) => {\n\t\t\t\t\tif (item.type === \"day_separator\") {\n\t\t\t\t\t\t// Render day separator - allow custom rendering via prop\n\t\t\t\t\t\tif (renderDaySeparator) {\n\t\t\t\t\t\t\treturn (\n\t\t\t\t\t\t\t\t<div key={`day-separator-${item.dateString}`}>\n\t\t\t\t\t\t\t\t\t{renderDaySeparator({\n\t\t\t\t\t\t\t\t\t\titem,\n\t\t\t\t\t\t\t\t\t\tformatDate: defaultFormatDate,\n\t\t\t\t\t\t\t\t\t})}\n\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// Default day separator using the primitive\n\t\t\t\t\t\treturn (\n\t\t\t\t\t\t\t<DaySeparator\n\t\t\t\t\t\t\t\tclassName=\"flex items-center justify-center py-2\"\n\t\t\t\t\t\t\t\tdate={item.date}\n\t\t\t\t\t\t\t\tdateString={item.dateString}\n\t\t\t\t\t\t\t\tkey={`day-separator-${item.dateString}`}\n\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t{({ formattedDate }) => (\n\t\t\t\t\t\t\t\t\t<>\n\t\t\t\t\t\t\t\t\t\t<DaySeparatorLine className=\"flex-1 border-gray-200 border-t dark:border-gray-700\" />\n\t\t\t\t\t\t\t\t\t\t<DaySeparatorLabel\n\t\t\t\t\t\t\t\t\t\t\tclassName=\"px-3 text-gray-500 text-xs dark:text-gray-400\"\n\t\t\t\t\t\t\t\t\t\t\tformattedDate={formattedDate}\n\t\t\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t\t\t<DaySeparatorLine className=\"flex-1 border-gray-200 border-t dark:border-gray-700\" />\n\t\t\t\t\t\t\t\t\t</>\n\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t</DaySeparator>\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\n\t\t\t\t\tif (item.type === \"timeline_event\") {\n\t\t\t\t\t\t// Extract event data from parts\n\t\t\t\t\t\tconst eventPart = extractEventPart(item.item);\n\n\t\t\t\t\t\t// Only render if we have valid event data\n\t\t\t\t\t\tif (!eventPart) {\n\t\t\t\t\t\t\treturn null;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\treturn (\n\t\t\t\t\t\t\t<ConversationEvent\n\t\t\t\t\t\t\t\tavailableAIAgents={availableAIAgents}\n\t\t\t\t\t\t\t\tavailableHumanAgents={availableHumanAgents}\n\t\t\t\t\t\t\t\tcreatedAt={item.item.createdAt}\n\t\t\t\t\t\t\t\tevent={eventPart}\n\t\t\t\t\t\t\t\tkey={item.item.id ?? `timeline-event-${item.item.createdAt}`}\n\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\n\t\t\t\t\tif (item.type === \"timeline_tool\") {\n\t\t\t\t\t\tconst toolName = item.tool ?? item.item.tool ?? item.item.type;\n\t\t\t\t\t\tconst toolDefinition = toolName ? tools?.[toolName] : undefined;\n\n\t\t\t\t\t\tif (!toolDefinition) {\n\t\t\t\t\t\t\treturn null;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tconst ToolComponent = toolDefinition.component;\n\n\t\t\t\t\t\tconst toolKey =\n\t\t\t\t\t\t\titem.item.id ?? `${toolName}-${item.item.createdAt}-${index}`;\n\n\t\t\t\t\t\treturn (\n\t\t\t\t\t\t\t<ToolComponent\n\t\t\t\t\t\t\t\tconversationId={conversationId}\n\t\t\t\t\t\t\t\titem={item.item}\n\t\t\t\t\t\t\t\tkey={toolKey}\n\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\n\t\t\t\t\t// Only show seen indicator on the LAST message group sent by the visitor\n\t\t\t\t\tconst isLastVisitorGroup =\n\t\t\t\t\t\tindex === timeline.lastVisitorMessageGroupIndex;\n\t\t\t\t\tconst seenByIds =\n\t\t\t\t\t\tisLastVisitorGroup && item.lastMessageId\n\t\t\t\t\t\t\t? timeline.groupedMessages.getMessageSeenBy(item.lastMessageId)\n\t\t\t\t\t\t\t: EMPTY_SEEN_BY_IDS;\n\t\t\t\t\tconst seenByNames =\n\t\t\t\t\t\tseenByIds.length > 0\n\t\t\t\t\t\t\t? getSeenByNames(seenByIds)\n\t\t\t\t\t\t\t: EMPTY_SEEN_BY_NAMES;\n\n\t\t\t\t\t// Use first timeline item ID as stable key\n\t\t\t\t\tconst groupKey =\n\t\t\t\t\t\titem.lastMessageId ??\n\t\t\t\t\t\titem.items?.[0]?.id ??\n\t\t\t\t\t\t`group-${item.items?.[0]?.createdAt ?? index}`;\n\n\t\t\t\t\treturn (\n\t\t\t\t\t\t<TimelineMessageGroup\n\t\t\t\t\t\t\tavailableAIAgents={availableAIAgents}\n\t\t\t\t\t\t\tavailableHumanAgents={availableHumanAgents}\n\t\t\t\t\t\t\tcurrentVisitorId={currentVisitorId}\n\t\t\t\t\t\t\titems={item.items || []}\n\t\t\t\t\t\t\tkey={groupKey}\n\t\t\t\t\t\t\tseenByIds={seenByIds}\n\t\t\t\t\t\t\tseenByNames={seenByNames}\n\t\t\t\t\t\t/>\n\t\t\t\t\t);\n\t\t\t\t})}\n\t\t\t\t<div className=\"h-6 w-full\">\n\t\t\t\t\t{typingIndicatorParticipants.length > 0 ? (\n\t\t\t\t\t\t<TypingIndicator\n\t\t\t\t\t\t\tavailableAIAgents={availableAIAgents}\n\t\t\t\t\t\t\tavailableHumanAgents={availableHumanAgents}\n\t\t\t\t\t\t\tclassName=\"mt-2\"\n\t\t\t\t\t\t\tparticipants={typingIndicatorParticipants}\n\t\t\t\t\t\t/>\n\t\t\t\t\t) : null}\n\t\t\t\t</div>\n\t\t\t</ConversationTimelineContainer>\n\t\t</PrimitiveConversationTimeline>\n\t);\n};\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"conversation-timeline.js","names":["EMPTY_SEEN_BY_IDS: readonly string[]","EMPTY_SEEN_BY_NAMES: readonly string[]","ConversationTimelineList: React.FC<ConversationTimelineProps>","names: string[]","PrimitiveConversationTimeline"],"sources":["../../../src/support/components/conversation-timeline.tsx"],"sourcesContent":["import type { AvailableAIAgent, AvailableHumanAgent } from \"@cossistant/types\";\nimport type {\n\tTimelineItem,\n\tTimelinePartEvent,\n} from \"@cossistant/types/api/timeline-item\";\nimport type React from \"react\";\nimport { useCallback, useMemo } from \"react\";\nimport type { DaySeparatorItem } from \"../../hooks/private/use-grouped-messages\";\nimport { useConversationTimeline } from \"../../hooks/use-conversation-timeline\";\nimport { useTypingSound } from \"../../hooks/use-typing-sound\";\nimport {\n\tConversationTimelineContainer,\n\tConversationTimeline as PrimitiveConversationTimeline,\n} from \"../../primitives/conversation-timeline\";\nimport {\n\tDaySeparator,\n\tDaySeparatorLabel,\n\tDaySeparatorLine,\n\tdefaultFormatDate,\n} from \"../../primitives/day-separator\";\nimport { cn } from \"../utils\";\nimport { ConversationEvent } from \"./conversation-event\";\nimport { TimelineMessageGroup } from \"./timeline-message-group\";\nimport { TypingIndicator, type TypingParticipant } from \"./typing-indicator\";\n\n// Helper to extract event part from timeline item\nfunction extractEventPart(item: TimelineItem): TimelinePartEvent | null {\n\tif (item.type !== \"event\") {\n\t\treturn null;\n\t}\n\n\tconst eventPart = item.parts.find(\n\t\t(part): part is TimelinePartEvent => part.type === \"event\"\n\t);\n\n\treturn eventPart || null;\n}\n\nconst EMPTY_SEEN_BY_IDS: readonly string[] = Object.freeze([]);\nconst EMPTY_SEEN_BY_NAMES: readonly string[] = Object.freeze([]);\n\nexport type ConversationTimelineToolProps = {\n\titem: TimelineItem;\n\tconversationId: string;\n};\n\nexport type ConversationTimelineToolDefinition = {\n\tcomponent: React.ComponentType<ConversationTimelineToolProps>;\n};\n\nexport type ConversationTimelineTools = Record<\n\tstring,\n\tConversationTimelineToolDefinition\n>;\n\nexport type ConversationTimelineProps = {\n\tconversationId: string;\n\titems: TimelineItem[];\n\tclassName?: string;\n\tavailableAIAgents: AvailableAIAgent[];\n\tavailableHumanAgents: AvailableHumanAgent[];\n\tcurrentVisitorId?: string;\n\ttools?: ConversationTimelineTools;\n\trenderDaySeparator?: (props: {\n\t\titem: DaySeparatorItem;\n\t\tformatDate: (date: Date) => string;\n\t}) => React.ReactNode;\n};\n\nexport const ConversationTimelineList: React.FC<ConversationTimelineProps> = ({\n\tconversationId,\n\titems: timelineItems,\n\tclassName,\n\tavailableAIAgents = [],\n\tavailableHumanAgents = [],\n\tcurrentVisitorId,\n\ttools,\n\trenderDaySeparator,\n}) => {\n\tconst timeline = useConversationTimeline({\n\t\tconversationId,\n\t\titems: timelineItems,\n\t\tcurrentVisitorId,\n\t});\n\n\tconst typingIndicatorParticipants = useMemo(\n\t\t() =>\n\t\t\ttimeline.typingParticipants.map<TypingParticipant>((participant) => ({\n\t\t\t\tid: participant.id,\n\t\t\t\ttype: participant.type,\n\t\t\t})),\n\t\t[timeline.typingParticipants]\n\t);\n\n\t// Play typing sound when someone is typing\n\tuseTypingSound(typingIndicatorParticipants.length > 0, {\n\t\tvolume: 1,\n\t\tplaybackRate: 1.3,\n\t});\n\n\tconst seenNameLookup = useMemo(() => {\n\t\tconst map = new Map<string, string>();\n\n\t\tfor (const agent of availableHumanAgents) {\n\t\t\tif (agent.name) {\n\t\t\t\tmap.set(agent.id, agent.name);\n\t\t\t}\n\t\t}\n\n\t\tfor (const agent of availableAIAgents) {\n\t\t\tif (agent.name) {\n\t\t\t\tmap.set(agent.id, agent.name);\n\t\t\t}\n\t\t}\n\n\t\treturn map;\n\t}, [availableHumanAgents, availableAIAgents]);\n\n\tconst getSeenByNames = useCallback(\n\t\t(ids: readonly string[] = EMPTY_SEEN_BY_IDS): readonly string[] => {\n\t\t\tif (ids.length === 0 || seenNameLookup.size === 0) {\n\t\t\t\treturn EMPTY_SEEN_BY_NAMES;\n\t\t\t}\n\n\t\t\tconst uniqueNames = new Set<string>();\n\t\t\tconst names: string[] = [];\n\n\t\t\tfor (const id of ids) {\n\t\t\t\tconst name = seenNameLookup.get(id);\n\t\t\t\tif (!name || uniqueNames.has(name)) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\tuniqueNames.add(name);\n\t\t\t\tnames.push(name);\n\t\t\t}\n\n\t\t\tif (names.length === 0) {\n\t\t\t\treturn EMPTY_SEEN_BY_NAMES;\n\t\t\t}\n\n\t\t\treturn Object.freeze(names);\n\t\t},\n\t\t[seenNameLookup]\n\t);\n\n\treturn (\n\t\t<PrimitiveConversationTimeline\n\t\t\tautoScroll={true}\n\t\t\tclassName={cn(\n\t\t\t\t\"overflow-y-scroll px-3 py-6\",\n\t\t\t\t\"co-scrollbar-thin\",\n\t\t\t\t\"h-full w-full\",\n\t\t\t\tclassName\n\t\t\t)}\n\t\t\tid=\"conversation-timeline\"\n\t\t\titems={timelineItems}\n\t\t>\n\t\t\t<ConversationTimelineContainer className=\"flex min-h-full w-full flex-col gap-5\">\n\t\t\t\t{timeline.groupedMessages.items.map((item, index) => {\n\t\t\t\t\tif (item.type === \"day_separator\") {\n\t\t\t\t\t\t// Render day separator - allow custom rendering via prop\n\t\t\t\t\t\tif (renderDaySeparator) {\n\t\t\t\t\t\t\treturn (\n\t\t\t\t\t\t\t\t<div key={`day-separator-${item.dateString}`}>\n\t\t\t\t\t\t\t\t\t{renderDaySeparator({\n\t\t\t\t\t\t\t\t\t\titem,\n\t\t\t\t\t\t\t\t\t\tformatDate: defaultFormatDate,\n\t\t\t\t\t\t\t\t\t})}\n\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// Default day separator using the primitive\n\t\t\t\t\t\treturn (\n\t\t\t\t\t\t\t<DaySeparator\n\t\t\t\t\t\t\t\tclassName=\"flex items-center justify-center py-2\"\n\t\t\t\t\t\t\t\tdate={item.date}\n\t\t\t\t\t\t\t\tdateString={item.dateString}\n\t\t\t\t\t\t\t\tkey={`day-separator-${item.dateString}`}\n\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t{({ formattedDate }) => (\n\t\t\t\t\t\t\t\t\t<>\n\t\t\t\t\t\t\t\t\t\t<DaySeparatorLine className=\"flex-1 border-gray-300/20 border-t dark:border-gray-600/20\" />\n\t\t\t\t\t\t\t\t\t\t<DaySeparatorLabel\n\t\t\t\t\t\t\t\t\t\t\tclassName=\"px-3 text-gray-400/50 text-xs dark:text-gray-500/50\"\n\t\t\t\t\t\t\t\t\t\t\tformattedDate={formattedDate}\n\t\t\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t\t\t<DaySeparatorLine className=\"flex-1 border-gray-300/20 border-t dark:border-gray-600/20\" />\n\t\t\t\t\t\t\t\t\t</>\n\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t</DaySeparator>\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\n\t\t\t\t\tif (item.type === \"timeline_event\") {\n\t\t\t\t\t\t// Extract event data from parts\n\t\t\t\t\t\tconst eventPart = extractEventPart(item.item);\n\n\t\t\t\t\t\t// Only render if we have valid event data\n\t\t\t\t\t\tif (!eventPart) {\n\t\t\t\t\t\t\treturn null;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\treturn (\n\t\t\t\t\t\t\t<ConversationEvent\n\t\t\t\t\t\t\t\tavailableAIAgents={availableAIAgents}\n\t\t\t\t\t\t\t\tavailableHumanAgents={availableHumanAgents}\n\t\t\t\t\t\t\t\tcreatedAt={item.item.createdAt}\n\t\t\t\t\t\t\t\tevent={eventPart}\n\t\t\t\t\t\t\t\tkey={item.item.id ?? `timeline-event-${item.item.createdAt}`}\n\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\n\t\t\t\t\tif (item.type === \"timeline_tool\") {\n\t\t\t\t\t\tconst toolName = item.tool ?? item.item.tool ?? item.item.type;\n\t\t\t\t\t\tconst toolDefinition = toolName ? tools?.[toolName] : undefined;\n\n\t\t\t\t\t\tif (!toolDefinition) {\n\t\t\t\t\t\t\treturn null;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tconst ToolComponent = toolDefinition.component;\n\n\t\t\t\t\t\tconst toolKey =\n\t\t\t\t\t\t\titem.item.id ?? `${toolName}-${item.item.createdAt}-${index}`;\n\n\t\t\t\t\t\treturn (\n\t\t\t\t\t\t\t<ToolComponent\n\t\t\t\t\t\t\t\tconversationId={conversationId}\n\t\t\t\t\t\t\t\titem={item.item}\n\t\t\t\t\t\t\t\tkey={toolKey}\n\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\n\t\t\t\t\t// Only show seen indicator on the LAST message group sent by the visitor\n\t\t\t\t\tconst isLastVisitorGroup =\n\t\t\t\t\t\tindex === timeline.lastVisitorMessageGroupIndex;\n\t\t\t\t\tconst seenByIds =\n\t\t\t\t\t\tisLastVisitorGroup && item.lastMessageId\n\t\t\t\t\t\t\t? timeline.groupedMessages.getMessageSeenBy(item.lastMessageId)\n\t\t\t\t\t\t\t: EMPTY_SEEN_BY_IDS;\n\t\t\t\t\tconst seenByNames =\n\t\t\t\t\t\tseenByIds.length > 0\n\t\t\t\t\t\t\t? getSeenByNames(seenByIds)\n\t\t\t\t\t\t\t: EMPTY_SEEN_BY_NAMES;\n\n\t\t\t\t\t// Use first timeline item ID as stable key\n\t\t\t\t\tconst groupKey =\n\t\t\t\t\t\titem.lastMessageId ??\n\t\t\t\t\t\titem.items?.[0]?.id ??\n\t\t\t\t\t\t`group-${item.items?.[0]?.createdAt ?? index}`;\n\n\t\t\t\t\treturn (\n\t\t\t\t\t\t<TimelineMessageGroup\n\t\t\t\t\t\t\tavailableAIAgents={availableAIAgents}\n\t\t\t\t\t\t\tavailableHumanAgents={availableHumanAgents}\n\t\t\t\t\t\t\tcurrentVisitorId={currentVisitorId}\n\t\t\t\t\t\t\titems={item.items || []}\n\t\t\t\t\t\t\tkey={groupKey}\n\t\t\t\t\t\t\tseenByIds={seenByIds}\n\t\t\t\t\t\t\tseenByNames={seenByNames}\n\t\t\t\t\t\t/>\n\t\t\t\t\t);\n\t\t\t\t})}\n\t\t\t\t<div className=\"h-6 w-full\">\n\t\t\t\t\t{typingIndicatorParticipants.length > 0 ? (\n\t\t\t\t\t\t<TypingIndicator\n\t\t\t\t\t\t\tavailableAIAgents={availableAIAgents}\n\t\t\t\t\t\t\tavailableHumanAgents={availableHumanAgents}\n\t\t\t\t\t\t\tclassName=\"mt-2\"\n\t\t\t\t\t\t\tparticipants={typingIndicatorParticipants}\n\t\t\t\t\t\t/>\n\t\t\t\t\t) : null}\n\t\t\t\t</div>\n\t\t\t</ConversationTimelineContainer>\n\t\t</PrimitiveConversationTimeline>\n\t);\n};\n"],"mappings":"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"}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { cn } from "../utils/index.js";
|
|
2
2
|
import { useSupportConfig } from "../store/support-store.js";
|
|
3
|
-
import icons_default from "./icons.js";
|
|
4
3
|
import { CoButton } from "./button.js";
|
|
4
|
+
import icons_default from "./icons.js";
|
|
5
5
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
6
6
|
|
|
7
7
|
//#region src/support/components/header.tsx
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
|
|
3
|
-
|
|
4
3
|
import { cn } from "../utils/index.js";
|
|
5
4
|
import icons_default from "./icons.js";
|
|
6
5
|
import { useCallback, useEffect, useState } from "react";
|
|
@@ -103,7 +102,7 @@ function ImageLightbox({ images, initialIndex = 0, isOpen, onClose, className })
|
|
|
103
102
|
})
|
|
104
103
|
})] }),
|
|
105
104
|
/* @__PURE__ */ jsx("img", {
|
|
106
|
-
alt: currentImage?.
|
|
105
|
+
alt: currentImage?.filename || `Image ${currentIndex + 1}`,
|
|
107
106
|
className: "max-h-[90vh] max-w-[90vw] object-contain",
|
|
108
107
|
src: currentImage?.url
|
|
109
108
|
}),
|