@cossistant/react 0.0.25 → 0.0.28
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/api.d.ts +1 -1
- package/api.d.ts.map +1 -1
- package/checks.d.ts +1 -1
- package/checks.d.ts.map +1 -1
- package/clsx.d.ts +1 -1
- package/clsx.d.ts.map +1 -1
- package/coerce.d.ts +1 -1
- package/coerce.d.ts.map +1 -1
- package/conversation.d.ts +3 -0
- package/conversation.d.ts.map +1 -1
- package/core.d.ts +1 -1
- package/core.d.ts.map +1 -1
- package/errors.d.ts +1 -1
- package/errors.d.ts.map +1 -1
- package/errors2.d.ts +1 -1
- package/errors2.d.ts.map +1 -1
- package/hooks/index.d.ts +2 -1
- package/hooks/index.js +6 -5
- package/hooks/private/store/use-website-store.js +2 -1
- package/hooks/private/store/use-website-store.js.map +1 -1
- package/hooks/private/use-client-query.d.ts +6 -0
- package/hooks/private/use-client-query.d.ts.map +1 -1
- package/hooks/private/use-client-query.js +26 -3
- package/hooks/private/use-client-query.js.map +1 -1
- package/hooks/private/use-multimodal-input.d.ts.map +1 -1
- package/hooks/private/use-multimodal-input.js +7 -5
- package/hooks/private/use-multimodal-input.js.map +1 -1
- package/hooks/private/use-visitor-typing-reporter.d.ts +18 -1
- package/hooks/private/use-visitor-typing-reporter.d.ts.map +1 -1
- package/hooks/private/use-visitor-typing-reporter.js +34 -4
- package/hooks/private/use-visitor-typing-reporter.js.map +1 -1
- package/hooks/use-conversation-page.d.ts +1 -0
- package/hooks/use-conversation-page.d.ts.map +1 -1
- package/hooks/use-conversation-page.js +6 -1
- package/hooks/use-conversation-page.js.map +1 -1
- package/hooks/use-conversation-preview.d.ts +2 -1
- package/hooks/use-conversation-preview.d.ts.map +1 -1
- package/hooks/use-conversation-preview.js +1 -1
- package/hooks/use-conversation-preview.js.map +1 -1
- package/hooks/use-conversation-timeline-items.js +2 -1
- package/hooks/use-conversation-timeline-items.js.map +1 -1
- package/hooks/use-conversation.js +2 -1
- package/hooks/use-conversation.js.map +1 -1
- package/hooks/use-conversations.js +1 -0
- package/hooks/use-conversations.js.map +1 -1
- package/hooks/use-file-upload.d.ts +55 -0
- package/hooks/use-file-upload.d.ts.map +1 -0
- package/hooks/use-file-upload.js +100 -0
- package/hooks/use-file-upload.js.map +1 -0
- package/hooks/use-message-composer.d.ts +11 -0
- package/hooks/use-message-composer.d.ts.map +1 -1
- package/hooks/use-message-composer.js +7 -3
- package/hooks/use-message-composer.js.map +1 -1
- package/hooks/use-realtime-support.d.ts.map +1 -1
- package/hooks/use-send-message.d.ts +1 -0
- package/hooks/use-send-message.d.ts.map +1 -1
- package/hooks/use-send-message.js +63 -11
- package/hooks/use-send-message.js.map +1 -1
- package/index.d.ts +6 -3
- package/index.js +13 -10
- package/openapi30.d.ts +1 -1
- package/openapi30.d.ts.map +1 -1
- package/openapi31.d.ts +1 -1
- package/openapi31.d.ts.map +1 -1
- package/package.json +4 -3
- package/parse.d.ts +1 -1
- package/parse.d.ts.map +1 -1
- package/primitives/conversation-timeline.d.ts.map +1 -1
- package/primitives/conversation-timeline.js +10 -5
- package/primitives/conversation-timeline.js.map +1 -1
- package/primitives/index.d.ts +4 -3
- package/primitives/index.js +12 -5
- package/primitives/index.parts.d.ts +3 -2
- package/primitives/index.parts.js +4 -3
- package/primitives/timeline-item-attachments.d.ts +100 -0
- package/primitives/timeline-item-attachments.d.ts.map +1 -0
- package/primitives/timeline-item-attachments.js +151 -0
- package/primitives/timeline-item-attachments.js.map +1 -0
- package/primitives/trigger.d.ts +91 -0
- package/primitives/trigger.d.ts.map +1 -0
- package/primitives/trigger.js +74 -0
- package/primitives/trigger.js.map +1 -0
- package/primitives/window.d.ts +22 -1
- package/primitives/window.d.ts.map +1 -1
- package/primitives/window.js +91 -5
- package/primitives/window.js.map +1 -1
- package/provider.d.ts.map +1 -1
- package/provider.js +8 -3
- package/provider.js.map +1 -1
- package/realtime/index.js +1 -1
- package/realtime/provider.js +1 -1
- package/realtime/support-provider.js +1 -1
- package/realtime/support-provider.js.map +1 -1
- package/realtime-events.d.ts +39 -0
- package/realtime-events.d.ts.map +1 -1
- package/registries.d.ts +1 -1
- package/registries.d.ts.map +1 -1
- package/schemas.d.ts +1 -1
- package/schemas.d.ts.map +1 -1
- package/schemas2.d.ts +1 -1
- package/schemas2.d.ts.map +1 -1
- package/schemas3.d.ts +1 -0
- package/schemas3.d.ts.map +1 -1
- package/specification-extension.d.ts +1 -1
- package/specification-extension.d.ts.map +1 -1
- package/standard-schema.d.ts +1 -1
- package/standard-schema.d.ts.map +1 -1
- package/support/components/button.d.ts +1 -1
- package/support/components/content.d.ts +30 -0
- package/support/components/content.d.ts.map +1 -0
- package/support/components/content.js +282 -0
- package/support/components/content.js.map +1 -0
- package/support/components/conversation-button-link.js +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 +49 -0
- package/support/components/image-lightbox.d.ts.map +1 -0
- package/support/components/image-lightbox.js +142 -0
- package/support/components/image-lightbox.js.map +1 -0
- package/support/components/index.d.ts +5 -4
- package/support/components/index.js +4 -4
- package/support/components/multimodal-input.d.ts +4 -1
- package/support/components/multimodal-input.d.ts.map +1 -1
- package/support/components/multimodal-input.js +71 -45
- package/support/components/multimodal-input.js.map +1 -1
- package/support/components/navigation-tab.js +1 -1
- package/support/components/root.d.ts +23 -0
- package/support/components/root.d.ts.map +1 -0
- package/support/components/root.js +36 -0
- package/support/components/root.js.map +1 -0
- package/support/components/timeline-message-item.d.ts.map +1 -1
- package/support/components/timeline-message-item.js +82 -18
- package/support/components/timeline-message-item.js.map +1 -1
- package/support/components/trigger.d.ts +14 -0
- package/support/components/trigger.d.ts.map +1 -0
- package/support/components/{bubble.js → trigger.js} +16 -12
- package/support/components/trigger.js.map +1 -0
- package/support/components/typing-indicator.d.ts.map +1 -1
- package/support/components/typing-indicator.js +1 -0
- package/support/components/typing-indicator.js.map +1 -1
- package/support/context/controlled-state.d.ts +46 -0
- package/support/context/controlled-state.d.ts.map +1 -0
- package/support/context/controlled-state.js +34 -0
- package/support/context/controlled-state.js.map +1 -0
- package/support/context/events.d.ts +103 -0
- package/support/context/events.d.ts.map +1 -0
- package/support/context/events.js +139 -0
- package/support/context/events.js.map +1 -0
- package/support/context/handle.d.ts +90 -0
- package/support/context/handle.d.ts.map +1 -0
- package/support/context/handle.js +79 -0
- package/support/context/handle.js.map +1 -0
- package/support/context/positioning.d.ts +17 -0
- package/support/context/positioning.d.ts.map +1 -0
- package/support/context/positioning.js +26 -0
- package/support/context/positioning.js.map +1 -0
- package/support/context/slots.d.ts +85 -0
- package/support/context/slots.d.ts.map +1 -0
- package/support/context/slots.js +115 -0
- package/support/context/slots.js.map +1 -0
- package/support/context/websocket.d.ts +8 -1
- package/support/context/websocket.d.ts.map +1 -1
- package/support/context/websocket.js +8 -1
- package/support/context/websocket.js.map +1 -1
- package/support/index.d.ts +239 -54
- package/support/index.d.ts.map +1 -1
- package/support/index.js +254 -33
- package/support/index.js.map +1 -1
- package/support/pages/articles.d.ts.map +1 -1
- package/support/pages/articles.js +3 -4
- package/support/pages/articles.js.map +1 -1
- package/support/pages/conversation-history.js +2 -2
- package/support/pages/conversation.js +6 -5
- package/support/pages/conversation.js.map +1 -1
- package/support/pages/home.js +2 -2
- package/support/router.d.ts +52 -12
- package/support/router.d.ts.map +1 -1
- package/support/router.js +78 -30
- package/support/router.js.map +1 -1
- package/support/store/index.d.ts +2 -2
- package/support/store/support-store.d.ts +26 -20
- package/support/store/support-store.d.ts.map +1 -1
- package/support/store/support-store.js +47 -6
- package/support/store/support-store.js.map +1 -1
- package/support/{support-D2EgfIts.css → support-C7Xaw-N6.css} +1 -2
- package/support/support-C7Xaw-N6.css.map +1 -0
- package/support/text/index.js.map +1 -1
- package/support/types.d.ts +75 -12
- package/support/types.d.ts.map +1 -1
- package/support.css +2 -2
- package/tailwind.css +0 -1
- package/timeline-item.d.ts +68 -2
- package/timeline-item.d.ts.map +1 -1
- package/util.d.ts +1 -1
- package/util.d.ts.map +1 -1
- package/utils/index.d.ts +2 -1
- package/utils/index.js +2 -1
- package/utils/merge-refs.d.ts +30 -0
- package/utils/merge-refs.d.ts.map +1 -0
- package/utils/merge-refs.js +46 -0
- package/utils/merge-refs.js.map +1 -0
- package/utils/use-render-element.d.ts.map +1 -1
- package/utils/use-render-element.js +20 -7
- package/utils/use-render-element.js.map +1 -1
- package/versions.d.ts +1 -1
- package/versions.d.ts.map +1 -1
- package/zod-extensions.d.ts +1 -1
- package/zod-extensions.d.ts.map +1 -1
- package/primitives/bubble.d.ts +0 -38
- package/primitives/bubble.d.ts.map +0 -1
- package/primitives/bubble.js +0 -57
- package/primitives/bubble.js.map +0 -1
- package/support/components/bubble.d.ts +0 -10
- package/support/components/bubble.d.ts.map +0 -1
- package/support/components/bubble.js.map +0 -1
- package/support/components/container.d.ts +0 -13
- package/support/components/container.d.ts.map +0 -1
- package/support/components/container.js +0 -109
- package/support/components/container.js.map +0 -1
- package/support/components/support-content.d.ts +0 -22
- package/support/components/support-content.d.ts.map +0 -1
- package/support/components/support-content.js +0 -48
- package/support/components/support-content.js.map +0 -1
- package/support/support-D2EgfIts.css.map +0 -1
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import { TypingIndicator } from "./typing-indicator.js";
|
|
2
|
-
import {
|
|
2
|
+
import { Content } from "./content.js";
|
|
3
|
+
import { Root } from "./root.js";
|
|
3
4
|
import icons_default from "./icons.js";
|
|
5
|
+
import { DefaultTrigger } from "./trigger.js";
|
|
4
6
|
import { ConversationEvent } from "./conversation-event.js";
|
|
5
7
|
import { TimelineMessageItem } from "./timeline-message-item.js";
|
|
6
8
|
import { TimelineMessageGroup } from "./timeline-message-group.js";
|
|
7
9
|
import { ConversationTimelineList } from "./conversation-timeline.js";
|
|
8
10
|
import { SendButton } from "./multimodal-input.js";
|
|
9
|
-
import { Bubble } from "./bubble.js";
|
|
10
|
-
import { SupportContent } from "./support-content.js";
|
|
11
11
|
|
|
12
|
-
export {
|
|
12
|
+
export { Content, ConversationEvent, ConversationTimelineList, DefaultTrigger, icons_default as Icon, Root, SendButton, TimelineMessageGroup, TimelineMessageItem, TypingIndicator };
|
|
@@ -10,17 +10,20 @@ type MultimodalInputProps = {
|
|
|
10
10
|
placeholder?: string;
|
|
11
11
|
disabled?: boolean;
|
|
12
12
|
isSubmitting?: boolean;
|
|
13
|
+
isUploading?: boolean;
|
|
14
|
+
uploadProgress?: number;
|
|
13
15
|
error?: Error | null;
|
|
14
16
|
files?: File[];
|
|
15
17
|
onRemoveFile?: (index: number) => void;
|
|
16
18
|
maxFiles?: number;
|
|
17
19
|
maxFileSize?: number;
|
|
18
|
-
allowedFileTypes?: string
|
|
20
|
+
allowedFileTypes?: string;
|
|
19
21
|
};
|
|
20
22
|
declare const MultimodalInput: React.FC<MultimodalInputProps>;
|
|
21
23
|
type SendButtonProps = {
|
|
22
24
|
className?: string;
|
|
23
25
|
disabled?: boolean;
|
|
26
|
+
isUploading?: boolean;
|
|
24
27
|
};
|
|
25
28
|
declare const SendButton: React.FC<SendButtonProps>;
|
|
26
29
|
//#endregion
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"multimodal-input.d.ts","names":[],"sources":["../../../src/support/components/multimodal-input.tsx"],"sourcesContent":[],"mappings":";;;
|
|
1
|
+
{"version":3,"file":"multimodal-input.d.ts","names":[],"sources":["../../../src/support/components/multimodal-input.tsx"],"sourcesContent":[],"mappings":";;;KAiBY,oBAAA;;EAAA,KAAA,EAAA,MAAA;EAKY,QAAA,EAAA,CAAA,KAAA,EAAA,MAAA,EAAA,GAAA,IAAA;EAMf,QAAA,EAAA,GAAA,GAAA,IAAA;EACA,YAAA,CAAA,EAAA,CAAA,KAAA,EAPe,IAOf,EAAA,EAAA,GAAA,IAAA;EAAI,WAAA,CAAA,EAAA,MAAA;EAOA,QAAA,CAAA,EAAA,OA6KZ;EAEW,YAAA,CAAA,EAAA,OAAe;EAMd,WAAA,CAAA,EAmBZ,OAAA;;UAhNQ;UACA;;;;;;cAOI,iBAAiB,KAAA,CAAM,GAAG;KA+K3B,eAAA;;;;;cAMC,YAAY,KAAA,CAAM,GAAG"}
|
|
@@ -3,28 +3,25 @@
|
|
|
3
3
|
|
|
4
4
|
import { cn } from "../utils/index.js";
|
|
5
5
|
import { Button } from "../../primitives/button.js";
|
|
6
|
-
import { MultimodalInput as MultimodalInput$1 } from "../../primitives/multimodal-input.js";
|
|
6
|
+
import { FileInput, MultimodalInput as MultimodalInput$1 } from "../../primitives/multimodal-input.js";
|
|
7
7
|
import icons_default from "./icons.js";
|
|
8
8
|
import { useSupportText } from "../text/index.js";
|
|
9
9
|
import { useComposerRefocus } from "../../hooks/use-composer-refocus.js";
|
|
10
10
|
import { Watermark } from "./watermark.js";
|
|
11
11
|
import { useRef } from "react";
|
|
12
|
-
import {
|
|
12
|
+
import { FILE_INPUT_ACCEPT, MAX_FILES_PER_MESSAGE, MAX_FILE_SIZE, formatFileSize } from "@cossistant/core";
|
|
13
|
+
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
13
14
|
|
|
14
15
|
//#region src/support/components/multimodal-input.tsx
|
|
15
|
-
const MultimodalInput = ({ className, value, onChange, onSubmit, onFileSelect, placeholder, disabled = false, isSubmitting = false, error, files = [], onRemoveFile, maxFiles =
|
|
16
|
-
|
|
17
|
-
"application/pdf",
|
|
18
|
-
"text/*"
|
|
19
|
-
] }) => {
|
|
20
|
-
useRef(null);
|
|
16
|
+
const MultimodalInput = ({ className, value, onChange, onSubmit, onFileSelect, placeholder, disabled = false, isSubmitting = false, isUploading = false, uploadProgress = 0, error, files = [], onRemoveFile, maxFiles = MAX_FILES_PER_MESSAGE, maxFileSize = MAX_FILE_SIZE, allowedFileTypes = FILE_INPUT_ACCEPT }) => {
|
|
17
|
+
const fileInputRef = useRef(null);
|
|
21
18
|
const hasContent = value.trim().length > 0 || files.length > 0;
|
|
22
19
|
const { focusComposer, inputRef } = useComposerRefocus({
|
|
23
20
|
disabled,
|
|
24
21
|
hasContent,
|
|
25
|
-
isSubmitting
|
|
22
|
+
isSubmitting: isSubmitting || isUploading
|
|
26
23
|
});
|
|
27
|
-
const canSubmit = !disabled && hasContent;
|
|
24
|
+
const canSubmit = !disabled && hasContent && !isUploading;
|
|
28
25
|
const text = useSupportText();
|
|
29
26
|
const resolvedPlaceholder = placeholder ?? text("component.multimodalInput.placeholder");
|
|
30
27
|
const handleSubmit = () => {
|
|
@@ -39,10 +36,8 @@ const MultimodalInput = ({ className, value, onChange, onSubmit, onFileSelect, p
|
|
|
39
36
|
e.preventDefault();
|
|
40
37
|
handleSubmit();
|
|
41
38
|
};
|
|
42
|
-
const
|
|
43
|
-
if (
|
|
44
|
-
if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;
|
|
45
|
-
return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
|
|
39
|
+
const handleAttachClick = () => {
|
|
40
|
+
if (files.length < maxFiles) fileInputRef.current?.click();
|
|
46
41
|
};
|
|
47
42
|
return /* @__PURE__ */ jsxs("form", {
|
|
48
43
|
className: "flex flex-col gap-2",
|
|
@@ -53,35 +48,47 @@ const MultimodalInput = ({ className, value, onChange, onSubmit, onFileSelect, p
|
|
|
53
48
|
id: "multimodal-input-error",
|
|
54
49
|
children: error.message
|
|
55
50
|
}),
|
|
56
|
-
files.length > 0 && /* @__PURE__ */
|
|
57
|
-
className: "flex flex-
|
|
58
|
-
children:
|
|
59
|
-
className: "flex items-center gap-2
|
|
60
|
-
children: [
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
onRemoveFile && /* @__PURE__ */ jsx("button", {
|
|
74
|
-
"aria-label": text("common.actions.removeFile", { fileName: file.name }),
|
|
75
|
-
className: "ml-1 hover:text-co-destructive",
|
|
76
|
-
onClick: () => onRemoveFile(index),
|
|
77
|
-
type: "button",
|
|
78
|
-
children: /* @__PURE__ */ jsx(icons_default, {
|
|
51
|
+
files.length > 0 && /* @__PURE__ */ jsxs("div", {
|
|
52
|
+
className: "flex flex-col gap-2 p-2",
|
|
53
|
+
children: [isUploading && /* @__PURE__ */ jsxs("div", {
|
|
54
|
+
className: "flex items-center gap-2 text-co-muted-foreground text-xs",
|
|
55
|
+
children: [/* @__PURE__ */ jsx("div", {
|
|
56
|
+
className: "h-1 flex-1 overflow-hidden rounded-full bg-co-muted",
|
|
57
|
+
children: /* @__PURE__ */ jsx("div", {
|
|
58
|
+
className: "h-full bg-co-primary transition-all duration-300",
|
|
59
|
+
style: { width: `${uploadProgress}%` }
|
|
60
|
+
})
|
|
61
|
+
}), /* @__PURE__ */ jsxs("span", { children: [uploadProgress, "%"] })]
|
|
62
|
+
}), /* @__PURE__ */ jsx("div", {
|
|
63
|
+
className: "flex flex-wrap gap-2",
|
|
64
|
+
children: files.map((file, index) => /* @__PURE__ */ jsxs("div", {
|
|
65
|
+
className: cn("flex items-center gap-2 rounded-md bg-co-muted px-2 py-1 text-xs", isUploading && "opacity-70"),
|
|
66
|
+
children: [
|
|
67
|
+
/* @__PURE__ */ jsx(icons_default, {
|
|
79
68
|
className: "h-3 w-3",
|
|
80
|
-
name: "
|
|
69
|
+
name: "attachment"
|
|
70
|
+
}),
|
|
71
|
+
/* @__PURE__ */ jsx("span", {
|
|
72
|
+
className: "max-w-[150px] truncate",
|
|
73
|
+
children: file.name
|
|
74
|
+
}),
|
|
75
|
+
/* @__PURE__ */ jsx("span", {
|
|
76
|
+
className: "text-co-muted-foreground",
|
|
77
|
+
children: formatFileSize(file.size)
|
|
78
|
+
}),
|
|
79
|
+
onRemoveFile && !isUploading && /* @__PURE__ */ jsx("button", {
|
|
80
|
+
"aria-label": text("common.actions.removeFile", { fileName: file.name }),
|
|
81
|
+
className: "ml-1 hover:text-co-destructive",
|
|
82
|
+
onClick: () => onRemoveFile(index),
|
|
83
|
+
type: "button",
|
|
84
|
+
children: /* @__PURE__ */ jsx(icons_default, {
|
|
85
|
+
className: "h-3 w-3",
|
|
86
|
+
name: "close"
|
|
87
|
+
})
|
|
81
88
|
})
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
}
|
|
89
|
+
]
|
|
90
|
+
}, `${file.name}-${index}`))
|
|
91
|
+
})]
|
|
85
92
|
}),
|
|
86
93
|
/* @__PURE__ */ jsxs("div", {
|
|
87
94
|
className: "group/multimodal-input flex flex-col rounded border border-co-border bg-co-background ring-offset-2 focus-within:ring-1 focus-within:ring-co-primary/10 dark:bg-co-background-200",
|
|
@@ -98,20 +105,39 @@ const MultimodalInput = ({ className, value, onChange, onSubmit, onFileSelect, p
|
|
|
98
105
|
value
|
|
99
106
|
}), /* @__PURE__ */ jsxs("div", {
|
|
100
107
|
className: "flex items-center justify-between py-1 pr-1 pl-3",
|
|
101
|
-
children: [/* @__PURE__ */ jsx(Watermark, {}), /* @__PURE__ */
|
|
108
|
+
children: [/* @__PURE__ */ jsx(Watermark, {}), /* @__PURE__ */ jsxs("div", {
|
|
102
109
|
className: "flex items-center gap-0.5",
|
|
103
|
-
children: /* @__PURE__ */
|
|
110
|
+
children: [onFileSelect && /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx("button", {
|
|
111
|
+
"aria-label": text("common.actions.attachFiles"),
|
|
112
|
+
className: cn("group flex h-8 w-8 items-center justify-center rounded-md text-co-muted-foreground hover:bg-co-muted hover:text-co-foreground disabled:cursor-not-allowed disabled:opacity-50", files.length >= maxFiles && "opacity-50"),
|
|
113
|
+
disabled: disabled || isSubmitting || files.length >= maxFiles,
|
|
114
|
+
onClick: handleAttachClick,
|
|
115
|
+
type: "button",
|
|
116
|
+
children: /* @__PURE__ */ jsx(icons_default, {
|
|
117
|
+
className: "h-4 w-4",
|
|
118
|
+
name: "attachment"
|
|
119
|
+
})
|
|
120
|
+
}), /* @__PURE__ */ jsx(FileInput, {
|
|
121
|
+
accept: allowedFileTypes,
|
|
122
|
+
className: "hidden",
|
|
123
|
+
disabled: disabled || isSubmitting || files.length >= maxFiles,
|
|
124
|
+
onFileSelect,
|
|
125
|
+
ref: fileInputRef
|
|
126
|
+
})] }), /* @__PURE__ */ jsx(SendButton, {
|
|
127
|
+
disabled: !canSubmit,
|
|
128
|
+
isUploading
|
|
129
|
+
})]
|
|
104
130
|
})]
|
|
105
131
|
})]
|
|
106
132
|
})
|
|
107
133
|
]
|
|
108
134
|
});
|
|
109
135
|
};
|
|
110
|
-
const SendButton = ({ className, disabled = false }) => /* @__PURE__ */ jsx(Button, {
|
|
136
|
+
const SendButton = ({ className, disabled = false, isUploading = false }) => /* @__PURE__ */ jsx(Button, {
|
|
111
137
|
className: cn("group flex h-8 w-8 items-center justify-center rounded-md text-co-primary hover:bg-co-muted disabled:cursor-not-allowed disabled:opacity-50", className),
|
|
112
138
|
disabled,
|
|
113
139
|
type: "submit",
|
|
114
|
-
children: /* @__PURE__ */ jsx(icons_default, {
|
|
140
|
+
children: isUploading ? /* @__PURE__ */ jsx("div", { className: "h-4 w-4 animate-spin rounded-full border-2 border-co-primary border-t-transparent" }) : /* @__PURE__ */ jsx(icons_default, {
|
|
115
141
|
className: "h-4 w-4",
|
|
116
142
|
filledOnHover: true,
|
|
117
143
|
name: "send"
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"multimodal-input.js","names":["MultimodalInput: React.FC<MultimodalInputProps>","Icon","Primitive.MultimodalInput","SendButton: React.FC<SendButtonProps>","Primitive.Button"],"sources":["../../../src/support/components/multimodal-input.tsx"],"sourcesContent":["\"use client\";\n\nimport type React from \"react\";\nimport { useRef } from \"react\";\nimport { useComposerRefocus } from \"../../hooks/use-composer-refocus\";\nimport * as Primitive from \"../../primitives\";\nimport { useSupportText } from \"../text\";\nimport { cn } from \"../utils\";\nimport Icon from \"./icons\";\nimport { Watermark } from \"./watermark\";\n\nexport type MultimodalInputProps = {\n\tclassName?: string;\n\tvalue: string;\n\tonChange: (value: string) => void;\n\tonSubmit: () => void;\n\tonFileSelect?: (files: File[]) => void;\n\tplaceholder?: string;\n\tdisabled?: boolean;\n\tisSubmitting?: boolean;\n\terror?: Error | null;\n\tfiles?: File[];\n\tonRemoveFile?: (index: number) => void;\n\tmaxFiles?: number;\n\tmaxFileSize?: number;\n\tallowedFileTypes?: string[];\n};\n\nexport const MultimodalInput: React.FC<MultimodalInputProps> = ({\n\tclassName,\n\tvalue,\n\tonChange,\n\tonSubmit,\n\tonFileSelect,\n\tplaceholder,\n\tdisabled = false,\n\tisSubmitting = false,\n\terror,\n\tfiles = [],\n\tonRemoveFile,\n\tmaxFiles = 5,\n\tmaxFileSize = 10 * 1024 * 1024, // 10MB\n\tallowedFileTypes = [\"image/*\", \"application/pdf\", \"text/*\"],\n}) => {\n\tconst fileInputRef = useRef<HTMLInputElement>(null);\n\tconst hasContent = value.trim().length > 0 || files.length > 0;\n\tconst { focusComposer, inputRef } = useComposerRefocus({\n\t\tdisabled,\n\t\thasContent,\n\t\tisSubmitting,\n\t});\n\tconst canSubmit = !disabled && hasContent;\n\tconst text = useSupportText();\n\tconst resolvedPlaceholder =\n\t\tplaceholder ?? text(\"component.multimodalInput.placeholder\");\n\n\tconst handleSubmit = () => {\n\t\tif (!canSubmit) {\n\t\t\treturn;\n\t\t}\n\n\t\tonSubmit();\n\t\t// Try focusing immediately for optimistic submission UX, then ensure focus\n\t\t// sticks after the submit button handles the click.\n\t\tfocusComposer();\n\t\trequestAnimationFrame(() => {\n\t\t\tfocusComposer();\n\t\t});\n\t};\n\n\tconst handleFormSubmit = (e: React.FormEvent) => {\n\t\te.preventDefault();\n\t\thandleSubmit();\n\t};\n\n\tconst handleAttachClick = () => {\n\t\tif (files.length < maxFiles) {\n\t\t\tfileInputRef.current?.click();\n\t\t}\n\t};\n\n\tconst formatFileSize = (bytes: number) => {\n\t\tif (bytes < 1024) {\n\t\t\treturn `${bytes} B`;\n\t\t}\n\t\tif (bytes < 1024 * 1024) {\n\t\t\treturn `${(bytes / 1024).toFixed(1)} KB`;\n\t\t}\n\t\treturn `${(bytes / (1024 * 1024)).toFixed(1)} MB`;\n\t};\n\n\treturn (\n\t\t<form className=\"flex flex-col gap-2\" onSubmit={handleFormSubmit}>\n\t\t\t{/* Error message */}\n\t\t\t{error && (\n\t\t\t\t<div\n\t\t\t\t\tclassName=\"rounded-md bg-co-destructive-muted p-2 text-co-destructive text-xs\"\n\t\t\t\t\tid=\"multimodal-input-error\"\n\t\t\t\t>\n\t\t\t\t\t{error.message}\n\t\t\t\t</div>\n\t\t\t)}\n\n\t\t\t{/* File attachments */}\n\t\t\t{files.length > 0 && (\n\t\t\t\t<div className=\"flex flex-wrap gap-2 p-2\">\n\t\t\t\t\t{files.map((file, index) => (\n\t\t\t\t\t\t<div\n\t\t\t\t\t\t\tclassName=\"flex items-center gap-2 rounded-md bg-co-muted px-2 py-1 text-xs\"\n\t\t\t\t\t\t\tkey={`${file.name}-${index}`}\n\t\t\t\t\t\t>\n\t\t\t\t\t\t\t<Icon className=\"h-3 w-3\" name=\"attachment\" />\n\t\t\t\t\t\t\t<span className=\"max-w-[150px] truncate\">{file.name}</span>\n\t\t\t\t\t\t\t<span className=\"text-co-muted-foreground\">\n\t\t\t\t\t\t\t\t{formatFileSize(file.size)}\n\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t{onRemoveFile && (\n\t\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\t\taria-label={text(\"common.actions.removeFile\", {\n\t\t\t\t\t\t\t\t\t\tfileName: file.name,\n\t\t\t\t\t\t\t\t\t})}\n\t\t\t\t\t\t\t\t\tclassName=\"ml-1 hover:text-co-destructive\"\n\t\t\t\t\t\t\t\t\tonClick={() => onRemoveFile(index)}\n\t\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t<Icon className=\"h-3 w-3\" name=\"close\" />\n\t\t\t\t\t\t\t\t</button>\n\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t</div>\n\t\t\t\t\t))}\n\t\t\t\t</div>\n\t\t\t)}\n\n\t\t\t{/* Input area */}\n\t\t\t<div className=\"group/multimodal-input flex flex-col rounded border border-co-border bg-co-background ring-offset-2 focus-within:ring-1 focus-within:ring-co-primary/10 dark:bg-co-background-200\">\n\t\t\t\t<Primitive.MultimodalInput\n\t\t\t\t\tautoFocus\n\t\t\t\t\tclassName={cn(\n\t\t\t\t\t\t\"flex-1 resize-none overflow-hidden p-3 text-co-foreground text-sm placeholder:text-co-primary/50 focus-visible:outline-none\",\n\t\t\t\t\t\tclassName\n\t\t\t\t\t)}\n\t\t\t\t\tdisabled={disabled}\n\t\t\t\t\terror={error}\n\t\t\t\t\tonChange={onChange}\n\t\t\t\t\tonFileSelect={onFileSelect}\n\t\t\t\t\tonSubmit={handleSubmit}\n\t\t\t\t\tplaceholder={resolvedPlaceholder}\n\t\t\t\t\tref={inputRef}\n\t\t\t\t\tvalue={value}\n\t\t\t\t/>\n\n\t\t\t\t<div className=\"flex items-center justify-between py-1 pr-1 pl-3\">\n\t\t\t\t\t<Watermark />\n\n\t\t\t\t\t<div className=\"flex items-center gap-0.5\">\n\t\t\t\t\t\t{/* File attachment button */}\n\t\t\t\t\t\t{/* {onFileSelect && (\n\t\t\t\t\t\t\t<>\n\t\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\t\taria-label={text(\"common.actions.attachFiles\")}\n\t\t\t\t\t\t\t\t\tclassName={cn(\n\t\t\t\t\t\t\t\t\t\t\"group flex h-8 w-8 items-center justify-center rounded-md text-co-muted-foreground hover:bg-co-muted hover:text-co-foreground disabled:cursor-not-allowed disabled:opacity-50\",\n\t\t\t\t\t\t\t\t\t\tfiles.length >= maxFiles && \"opacity-50\"\n\t\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t\t\tdisabled={\n\t\t\t\t\t\t\t\t\t\tdisabled || isSubmitting || files.length >= maxFiles\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\tonClick={handleAttachClick}\n\t\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t<Icon className=\"h-4 w-4\" name=\"attachment\" />\n\t\t\t\t\t\t\t\t</button>\n\n\t\t\t\t\t\t\t\t<Primitive.FileInput\n\t\t\t\t\t\t\t\t\taccept={allowedFileTypes.join(\",\")}\n\t\t\t\t\t\t\t\t\tclassName=\"hidden\"\n\t\t\t\t\t\t\t\t\tdisabled={\n\t\t\t\t\t\t\t\t\t\tdisabled || isSubmitting || files.length >= maxFiles\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\tonFileSelect={onFileSelect}\n\t\t\t\t\t\t\t\t\tref={fileInputRef}\n\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t</>\n\t\t\t\t\t\t)} */}\n\n\t\t\t\t\t\t{/* Send button */}\n\t\t\t\t\t\t<SendButton disabled={!canSubmit} />\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t</form>\n\t);\n};\n\nexport type SendButtonProps = {\n\tclassName?: string;\n\tdisabled?: boolean;\n};\n\nexport const SendButton: React.FC<SendButtonProps> = ({\n\tclassName,\n\tdisabled = false,\n}) => (\n\t<Primitive.Button\n\t\tclassName={cn(\n\t\t\t\"group flex h-8 w-8 items-center justify-center rounded-md text-co-primary hover:bg-co-muted disabled:cursor-not-allowed disabled:opacity-50\",\n\t\t\tclassName\n\t\t)}\n\t\tdisabled={disabled}\n\t\ttype=\"submit\"\n\t>\n\t\t<Icon className=\"h-4 w-4\" filledOnHover name=\"send\" />\n\t</Primitive.Button>\n);\n"],"mappings":";;;;;;;;;;;;;;AA4BA,MAAaA,mBAAmD,EAC/D,WACA,OACA,UACA,UACA,cACA,aACA,WAAW,OACX,eAAe,OACf,OACA,QAAQ,EAAE,EACV,cACA,WAAW,GACX,cAAc,KAAK,OAAO,MAC1B,mBAAmB;CAAC;CAAW;CAAmB;CAAS,OACtD;AACgB,QAAyB,KAAK;CACnD,MAAM,aAAa,MAAM,MAAM,CAAC,SAAS,KAAK,MAAM,SAAS;CAC7D,MAAM,EAAE,eAAe,aAAa,mBAAmB;EACtD;EACA;EACA;EACA,CAAC;CACF,MAAM,YAAY,CAAC,YAAY;CAC/B,MAAM,OAAO,gBAAgB;CAC7B,MAAM,sBACL,eAAe,KAAK,wCAAwC;CAE7D,MAAM,qBAAqB;AAC1B,MAAI,CAAC,UACJ;AAGD,YAAU;AAGV,iBAAe;AACf,8BAA4B;AAC3B,kBAAe;IACd;;CAGH,MAAM,oBAAoB,MAAuB;AAChD,IAAE,gBAAgB;AAClB,gBAAc;;CASf,MAAM,kBAAkB,UAAkB;AACzC,MAAI,QAAQ,KACX,QAAO,GAAG,MAAM;AAEjB,MAAI,QAAQ,OAAO,KAClB,QAAO,IAAI,QAAQ,MAAM,QAAQ,EAAE,CAAC;AAErC,SAAO,IAAI,SAAS,OAAO,OAAO,QAAQ,EAAE,CAAC;;AAG9C,QACC,qBAAC;EAAK,WAAU;EAAsB,UAAU;;GAE9C,SACA,oBAAC;IACA,WAAU;IACV,IAAG;cAEF,MAAM;KACF;GAIN,MAAM,SAAS,KACf,oBAAC;IAAI,WAAU;cACb,MAAM,KAAK,MAAM,UACjB,qBAAC;KACA,WAAU;;MAGV,oBAACC;OAAK,WAAU;OAAU,MAAK;QAAe;MAC9C,oBAAC;OAAK,WAAU;iBAA0B,KAAK;QAAY;MAC3D,oBAAC;OAAK,WAAU;iBACd,eAAe,KAAK,KAAK;QACpB;MACN,gBACA,oBAAC;OACA,cAAY,KAAK,6BAA6B,EAC7C,UAAU,KAAK,MACf,CAAC;OACF,WAAU;OACV,eAAe,aAAa,MAAM;OAClC,MAAK;iBAEL,oBAACA;QAAK,WAAU;QAAU,MAAK;SAAU;QACjC;;OAjBL,GAAG,KAAK,KAAK,GAAG,QAmBhB,CACL;KACG;GAIP,qBAAC;IAAI,WAAU;eACd,oBAACC;KACA;KACA,WAAW,GACV,+HACA,UACA;KACS;KACH;KACG;KACI;KACd,UAAU;KACV,aAAa;KACb,KAAK;KACE;MACN,EAEF,qBAAC;KAAI,WAAU;gBACd,oBAAC,cAAY,EAEb,oBAAC;MAAI,WAAU;gBAgCd,oBAAC,cAAW,UAAU,CAAC,YAAa;OAC/B;MACD;KACD;;GACA;;AAST,MAAaC,cAAyC,EACrD,WACA,WAAW,YAEX,oBAACC;CACA,WAAW,GACV,+IACA,UACA;CACS;CACV,MAAK;WAEL,oBAACH;EAAK,WAAU;EAAU;EAAc,MAAK;GAAS;EACpC"}
|
|
1
|
+
{"version":3,"file":"multimodal-input.js","names":["MultimodalInput: React.FC<MultimodalInputProps>","Icon","Primitive.MultimodalInput","Primitive.FileInput","SendButton: React.FC<SendButtonProps>","Primitive.Button"],"sources":["../../../src/support/components/multimodal-input.tsx"],"sourcesContent":["\"use client\";\n\nimport {\n\tFILE_INPUT_ACCEPT,\n\tformatFileSize,\n\tMAX_FILE_SIZE,\n\tMAX_FILES_PER_MESSAGE,\n} from \"@cossistant/core\";\nimport type React from \"react\";\nimport { useRef } from \"react\";\nimport { useComposerRefocus } from \"../../hooks/use-composer-refocus\";\nimport * as Primitive from \"../../primitives\";\nimport { useSupportText } from \"../text\";\nimport { cn } from \"../utils\";\nimport Icon from \"./icons\";\nimport { Watermark } from \"./watermark\";\n\nexport type MultimodalInputProps = {\n\tclassName?: string;\n\tvalue: string;\n\tonChange: (value: string) => void;\n\tonSubmit: () => void;\n\tonFileSelect?: (files: File[]) => void;\n\tplaceholder?: string;\n\tdisabled?: boolean;\n\tisSubmitting?: boolean;\n\tisUploading?: boolean;\n\tuploadProgress?: number;\n\terror?: Error | null;\n\tfiles?: File[];\n\tonRemoveFile?: (index: number) => void;\n\tmaxFiles?: number;\n\tmaxFileSize?: number;\n\tallowedFileTypes?: string;\n};\n\nexport const MultimodalInput: React.FC<MultimodalInputProps> = ({\n\tclassName,\n\tvalue,\n\tonChange,\n\tonSubmit,\n\tonFileSelect,\n\tplaceholder,\n\tdisabled = false,\n\tisSubmitting = false,\n\tisUploading = false,\n\tuploadProgress = 0,\n\terror,\n\tfiles = [],\n\tonRemoveFile,\n\tmaxFiles = MAX_FILES_PER_MESSAGE,\n\tmaxFileSize = MAX_FILE_SIZE,\n\tallowedFileTypes = FILE_INPUT_ACCEPT,\n}) => {\n\tconst fileInputRef = useRef<HTMLInputElement>(null);\n\tconst hasContent = value.trim().length > 0 || files.length > 0;\n\tconst { focusComposer, inputRef } = useComposerRefocus({\n\t\tdisabled,\n\t\thasContent,\n\t\tisSubmitting: isSubmitting || isUploading,\n\t});\n\tconst canSubmit = !disabled && hasContent && !isUploading;\n\tconst text = useSupportText();\n\tconst resolvedPlaceholder =\n\t\tplaceholder ?? text(\"component.multimodalInput.placeholder\");\n\n\tconst handleSubmit = () => {\n\t\tif (!canSubmit) {\n\t\t\treturn;\n\t\t}\n\n\t\tonSubmit();\n\t\t// Try focusing immediately for optimistic submission UX, then ensure focus\n\t\t// sticks after the submit button handles the click.\n\t\tfocusComposer();\n\t\trequestAnimationFrame(() => {\n\t\t\tfocusComposer();\n\t\t});\n\t};\n\n\tconst handleFormSubmit = (e: React.FormEvent) => {\n\t\te.preventDefault();\n\t\thandleSubmit();\n\t};\n\n\tconst handleAttachClick = () => {\n\t\tif (files.length < maxFiles) {\n\t\t\tfileInputRef.current?.click();\n\t\t}\n\t};\n\n\treturn (\n\t\t<form className=\"flex flex-col gap-2\" onSubmit={handleFormSubmit}>\n\t\t\t{/* Error message */}\n\t\t\t{error && (\n\t\t\t\t<div\n\t\t\t\t\tclassName=\"rounded-md bg-co-destructive-muted p-2 text-co-destructive text-xs\"\n\t\t\t\t\tid=\"multimodal-input-error\"\n\t\t\t\t>\n\t\t\t\t\t{error.message}\n\t\t\t\t</div>\n\t\t\t)}\n\n\t\t\t{/* File attachments */}\n\t\t\t{files.length > 0 && (\n\t\t\t\t<div className=\"flex flex-col gap-2 p-2\">\n\t\t\t\t\t{/* Upload progress indicator */}\n\t\t\t\t\t{isUploading && (\n\t\t\t\t\t\t<div className=\"flex items-center gap-2 text-co-muted-foreground text-xs\">\n\t\t\t\t\t\t\t<div className=\"h-1 flex-1 overflow-hidden rounded-full bg-co-muted\">\n\t\t\t\t\t\t\t\t<div\n\t\t\t\t\t\t\t\t\tclassName=\"h-full bg-co-primary transition-all duration-300\"\n\t\t\t\t\t\t\t\t\tstyle={{ width: `${uploadProgress}%` }}\n\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t<span>{uploadProgress}%</span>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t)}\n\t\t\t\t\t<div className=\"flex flex-wrap gap-2\">\n\t\t\t\t\t\t{files.map((file, index) => (\n\t\t\t\t\t\t\t<div\n\t\t\t\t\t\t\t\tclassName={cn(\n\t\t\t\t\t\t\t\t\t\"flex items-center gap-2 rounded-md bg-co-muted px-2 py-1 text-xs\",\n\t\t\t\t\t\t\t\t\tisUploading && \"opacity-70\"\n\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t\tkey={`${file.name}-${index}`}\n\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t<Icon className=\"h-3 w-3\" name=\"attachment\" />\n\t\t\t\t\t\t\t\t<span className=\"max-w-[150px] truncate\">{file.name}</span>\n\t\t\t\t\t\t\t\t<span className=\"text-co-muted-foreground\">\n\t\t\t\t\t\t\t\t\t{formatFileSize(file.size)}\n\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t\t{onRemoveFile && !isUploading && (\n\t\t\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\t\t\taria-label={text(\"common.actions.removeFile\", {\n\t\t\t\t\t\t\t\t\t\t\tfileName: file.name,\n\t\t\t\t\t\t\t\t\t\t})}\n\t\t\t\t\t\t\t\t\t\tclassName=\"ml-1 hover:text-co-destructive\"\n\t\t\t\t\t\t\t\t\t\tonClick={() => onRemoveFile(index)}\n\t\t\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t<Icon className=\"h-3 w-3\" name=\"close\" />\n\t\t\t\t\t\t\t\t\t</button>\n\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t))}\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\t\t\t)}\n\n\t\t\t{/* Input area */}\n\t\t\t<div className=\"group/multimodal-input flex flex-col rounded border border-co-border bg-co-background ring-offset-2 focus-within:ring-1 focus-within:ring-co-primary/10 dark:bg-co-background-200\">\n\t\t\t\t<Primitive.MultimodalInput\n\t\t\t\t\tautoFocus\n\t\t\t\t\tclassName={cn(\n\t\t\t\t\t\t\"flex-1 resize-none overflow-hidden p-3 text-co-foreground text-sm placeholder:text-co-primary/50 focus-visible:outline-none\",\n\t\t\t\t\t\tclassName\n\t\t\t\t\t)}\n\t\t\t\t\tdisabled={disabled}\n\t\t\t\t\terror={error}\n\t\t\t\t\tonChange={onChange}\n\t\t\t\t\tonFileSelect={onFileSelect}\n\t\t\t\t\tonSubmit={handleSubmit}\n\t\t\t\t\tplaceholder={resolvedPlaceholder}\n\t\t\t\t\tref={inputRef}\n\t\t\t\t\tvalue={value}\n\t\t\t\t/>\n\n\t\t\t\t<div className=\"flex items-center justify-between py-1 pr-1 pl-3\">\n\t\t\t\t\t<Watermark />\n\n\t\t\t\t\t<div className=\"flex items-center gap-0.5\">\n\t\t\t\t\t\t{/* File attachment button */}\n\t\t\t\t\t\t{onFileSelect && (\n\t\t\t\t\t\t\t<>\n\t\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\t\taria-label={text(\"common.actions.attachFiles\")}\n\t\t\t\t\t\t\t\t\tclassName={cn(\n\t\t\t\t\t\t\t\t\t\t\"group flex h-8 w-8 items-center justify-center rounded-md text-co-muted-foreground hover:bg-co-muted hover:text-co-foreground disabled:cursor-not-allowed disabled:opacity-50\",\n\t\t\t\t\t\t\t\t\t\tfiles.length >= maxFiles && \"opacity-50\"\n\t\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t\t\tdisabled={\n\t\t\t\t\t\t\t\t\t\tdisabled || isSubmitting || files.length >= maxFiles\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\tonClick={handleAttachClick}\n\t\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t<Icon className=\"h-4 w-4\" name=\"attachment\" />\n\t\t\t\t\t\t\t\t</button>\n\n\t\t\t\t\t\t\t\t<Primitive.FileInput\n\t\t\t\t\t\t\t\t\taccept={allowedFileTypes}\n\t\t\t\t\t\t\t\t\tclassName=\"hidden\"\n\t\t\t\t\t\t\t\t\tdisabled={\n\t\t\t\t\t\t\t\t\t\tdisabled || isSubmitting || files.length >= maxFiles\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\tonFileSelect={onFileSelect}\n\t\t\t\t\t\t\t\t\tref={fileInputRef}\n\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t</>\n\t\t\t\t\t\t)}\n\n\t\t\t\t\t\t{/* Send button */}\n\t\t\t\t\t\t<SendButton disabled={!canSubmit} isUploading={isUploading} />\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t</form>\n\t);\n};\n\nexport type SendButtonProps = {\n\tclassName?: string;\n\tdisabled?: boolean;\n\tisUploading?: boolean;\n};\n\nexport const SendButton: React.FC<SendButtonProps> = ({\n\tclassName,\n\tdisabled = false,\n\tisUploading = false,\n}) => (\n\t<Primitive.Button\n\t\tclassName={cn(\n\t\t\t\"group flex h-8 w-8 items-center justify-center rounded-md text-co-primary hover:bg-co-muted disabled:cursor-not-allowed disabled:opacity-50\",\n\t\t\tclassName\n\t\t)}\n\t\tdisabled={disabled}\n\t\ttype=\"submit\"\n\t>\n\t\t{isUploading ? (\n\t\t\t<div className=\"h-4 w-4 animate-spin rounded-full border-2 border-co-primary border-t-transparent\" />\n\t\t) : (\n\t\t\t<Icon className=\"h-4 w-4\" filledOnHover name=\"send\" />\n\t\t)}\n\t</Primitive.Button>\n);\n"],"mappings":";;;;;;;;;;;;;;;AAoCA,MAAaA,mBAAmD,EAC/D,WACA,OACA,UACA,UACA,cACA,aACA,WAAW,OACX,eAAe,OACf,cAAc,OACd,iBAAiB,GACjB,OACA,QAAQ,EAAE,EACV,cACA,WAAW,uBACX,cAAc,eACd,mBAAmB,wBACd;CACL,MAAM,eAAe,OAAyB,KAAK;CACnD,MAAM,aAAa,MAAM,MAAM,CAAC,SAAS,KAAK,MAAM,SAAS;CAC7D,MAAM,EAAE,eAAe,aAAa,mBAAmB;EACtD;EACA;EACA,cAAc,gBAAgB;EAC9B,CAAC;CACF,MAAM,YAAY,CAAC,YAAY,cAAc,CAAC;CAC9C,MAAM,OAAO,gBAAgB;CAC7B,MAAM,sBACL,eAAe,KAAK,wCAAwC;CAE7D,MAAM,qBAAqB;AAC1B,MAAI,CAAC,UACJ;AAGD,YAAU;AAGV,iBAAe;AACf,8BAA4B;AAC3B,kBAAe;IACd;;CAGH,MAAM,oBAAoB,MAAuB;AAChD,IAAE,gBAAgB;AAClB,gBAAc;;CAGf,MAAM,0BAA0B;AAC/B,MAAI,MAAM,SAAS,SAClB,cAAa,SAAS,OAAO;;AAI/B,QACC,qBAAC;EAAK,WAAU;EAAsB,UAAU;;GAE9C,SACA,oBAAC;IACA,WAAU;IACV,IAAG;cAEF,MAAM;KACF;GAIN,MAAM,SAAS,KACf,qBAAC;IAAI,WAAU;eAEb,eACA,qBAAC;KAAI,WAAU;gBACd,oBAAC;MAAI,WAAU;gBACd,oBAAC;OACA,WAAU;OACV,OAAO,EAAE,OAAO,GAAG,eAAe,IAAI;QACrC;OACG,EACN,qBAAC,qBAAM,gBAAe,OAAQ;MACzB,EAEP,oBAAC;KAAI,WAAU;eACb,MAAM,KAAK,MAAM,UACjB,qBAAC;MACA,WAAW,GACV,oEACA,eAAe,aACf;;OAGD,oBAACC;QAAK,WAAU;QAAU,MAAK;SAAe;OAC9C,oBAAC;QAAK,WAAU;kBAA0B,KAAK;SAAY;OAC3D,oBAAC;QAAK,WAAU;kBACd,eAAe,KAAK,KAAK;SACpB;OACN,gBAAgB,CAAC,eACjB,oBAAC;QACA,cAAY,KAAK,6BAA6B,EAC7C,UAAU,KAAK,MACf,CAAC;QACF,WAAU;QACV,eAAe,aAAa,MAAM;QAClC,MAAK;kBAEL,oBAACA;SAAK,WAAU;SAAU,MAAK;UAAU;SACjC;;QAjBL,GAAG,KAAK,KAAK,GAAG,QAmBhB,CACL;MACG;KACD;GAIP,qBAAC;IAAI,WAAU;eACd,oBAACC;KACA;KACA,WAAW,GACV,+HACA,UACA;KACS;KACH;KACG;KACI;KACd,UAAU;KACV,aAAa;KACb,KAAK;KACE;MACN,EAEF,qBAAC;KAAI,WAAU;gBACd,oBAAC,cAAY,EAEb,qBAAC;MAAI,WAAU;iBAEb,gBACA,4CACC,oBAAC;OACA,cAAY,KAAK,6BAA6B;OAC9C,WAAW,GACV,iLACA,MAAM,UAAU,YAAY,aAC5B;OACD,UACC,YAAY,gBAAgB,MAAM,UAAU;OAE7C,SAAS;OACT,MAAK;iBAEL,oBAACD;QAAK,WAAU;QAAU,MAAK;SAAe;QACtC,EAET,oBAACE;OACA,QAAQ;OACR,WAAU;OACV,UACC,YAAY,gBAAgB,MAAM,UAAU;OAE/B;OACd,KAAK;QACJ,IACA,EAIJ,oBAAC;OAAW,UAAU,CAAC;OAAwB;QAAe;OACzD;MACD;KACD;;GACA;;AAUT,MAAaC,cAAyC,EACrD,WACA,WAAW,OACX,cAAc,YAEd,oBAACC;CACA,WAAW,GACV,+IACA,UACA;CACS;CACV,MAAK;WAEJ,cACA,oBAAC,SAAI,WAAU,sFAAsF,GAErG,oBAACJ;EAAK,WAAU;EAAU;EAAc,MAAK;GAAS;EAErC"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { useSupportNavigation } from "../store/support-store.js";
|
|
2
|
-
import { CoButton } from "./button.js";
|
|
3
2
|
import icons_default from "./icons.js";
|
|
3
|
+
import { CoButton } from "./button.js";
|
|
4
4
|
import { Text } from "../text/index.js";
|
|
5
5
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
6
6
|
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import * as React$1 from "react";
|
|
2
|
+
|
|
3
|
+
//#region src/support/components/root.d.ts
|
|
4
|
+
type RootProps = {
|
|
5
|
+
className?: string;
|
|
6
|
+
children: React$1.ReactNode;
|
|
7
|
+
};
|
|
8
|
+
/**
|
|
9
|
+
* Root wrapper component that provides the positioning context.
|
|
10
|
+
* Contains the trigger and content as siblings.
|
|
11
|
+
*
|
|
12
|
+
* @example
|
|
13
|
+
* <Support.Root>
|
|
14
|
+
* <Support.Trigger>Help</Support.Trigger>
|
|
15
|
+
* <Support.Content>
|
|
16
|
+
* <Support.Router />
|
|
17
|
+
* </Support.Content>
|
|
18
|
+
* </Support.Root>
|
|
19
|
+
*/
|
|
20
|
+
declare const Root: React$1.FC<RootProps>;
|
|
21
|
+
//#endregion
|
|
22
|
+
export { Root, RootProps };
|
|
23
|
+
//# sourceMappingURL=root.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"root.d.ts","names":[],"sources":["../../../src/support/components/root.tsx"],"sourcesContent":[],"mappings":";;;KAOY,SAAA;;EAAA,QAAA,EAED,OAAA,CAAM,SAAN;AAeX,CAAA;;;;;;;;;;;;;cAAa,MAAM,OAAA,CAAM,GAAG"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
import { cn } from "../utils/index.js";
|
|
5
|
+
import { TriggerRefProvider } from "../context/positioning.js";
|
|
6
|
+
import { jsx } from "react/jsx-runtime";
|
|
7
|
+
import { motion } from "motion/react";
|
|
8
|
+
|
|
9
|
+
//#region src/support/components/root.tsx
|
|
10
|
+
/**
|
|
11
|
+
* Root wrapper component that provides the positioning context.
|
|
12
|
+
* Contains the trigger and content as siblings.
|
|
13
|
+
*
|
|
14
|
+
* @example
|
|
15
|
+
* <Support.Root>
|
|
16
|
+
* <Support.Trigger>Help</Support.Trigger>
|
|
17
|
+
* <Support.Content>
|
|
18
|
+
* <Support.Router />
|
|
19
|
+
* </Support.Content>
|
|
20
|
+
* </Support.Root>
|
|
21
|
+
*/
|
|
22
|
+
const Root = ({ className, children }) => /* @__PURE__ */ jsx(TriggerRefProvider, { children: /* @__PURE__ */ jsx(motion.div, {
|
|
23
|
+
animate: { opacity: 1 },
|
|
24
|
+
className: cn("cossistant relative", className),
|
|
25
|
+
initial: { opacity: 0 },
|
|
26
|
+
layout: "position",
|
|
27
|
+
transition: {
|
|
28
|
+
default: { ease: "anticipate" },
|
|
29
|
+
layout: { duration: .3 }
|
|
30
|
+
},
|
|
31
|
+
children
|
|
32
|
+
}) });
|
|
33
|
+
|
|
34
|
+
//#endregion
|
|
35
|
+
export { Root };
|
|
36
|
+
//# sourceMappingURL=root.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"root.js","names":["Root: React.FC<RootProps>"],"sources":["../../../src/support/components/root.tsx"],"sourcesContent":["\"use client\";\n\nimport { motion } from \"motion/react\";\nimport type * as React from \"react\";\nimport { TriggerRefProvider } from \"../context/positioning\";\nimport { cn } from \"../utils\";\n\nexport type RootProps = {\n\tclassName?: string;\n\tchildren: React.ReactNode;\n};\n\n/**\n * Root wrapper component that provides the positioning context.\n * Contains the trigger and content as siblings.\n *\n * @example\n * <Support.Root>\n * <Support.Trigger>Help</Support.Trigger>\n * <Support.Content>\n * <Support.Router />\n * </Support.Content>\n * </Support.Root>\n */\nexport const Root: React.FC<RootProps> = ({ className, children }) => (\n\t<TriggerRefProvider>\n\t\t<motion.div\n\t\t\tanimate={{ opacity: 1 }}\n\t\t\tclassName={cn(\"cossistant relative\", className)}\n\t\t\tinitial={{ opacity: 0 }}\n\t\t\tlayout=\"position\"\n\t\t\ttransition={{\n\t\t\t\tdefault: { ease: \"anticipate\" },\n\t\t\t\tlayout: { duration: 0.3 },\n\t\t\t}}\n\t\t>\n\t\t\t{children}\n\t\t</motion.div>\n\t</TriggerRefProvider>\n);\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;AAwBA,MAAaA,QAA6B,EAAE,WAAW,eACtD,oBAAC,gCACA,oBAAC,OAAO;CACP,SAAS,EAAE,SAAS,GAAG;CACvB,WAAW,GAAG,uBAAuB,UAAU;CAC/C,SAAS,EAAE,SAAS,GAAG;CACvB,QAAO;CACP,YAAY;EACX,SAAS,EAAE,MAAM,cAAc;EAC/B,QAAQ,EAAE,UAAU,IAAK;EACzB;CAEA;EACW,GACO"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"timeline-message-item.d.ts","names":[],"sources":["../../../src/support/components/timeline-message-item.tsx"],"sourcesContent":[],"mappings":";;;;
|
|
1
|
+
{"version":3,"file":"timeline-message-item.d.ts","names":[],"sources":["../../../src/support/components/timeline-message-item.tsx"],"sourcesContent":[],"mappings":";;;;KAkBY,wBAAA;QACL;EADK,MAAA,CAAA,EAAA,OAAA;EAUI,cAAA,CAAA,EAAA,OAAmB;CAClC;;;;;AAG+C,iBAJhC,mBAAA,CAIgC;EAAA,IAAA;EAAA,MAAA;EAAA;AAAA,CAAA,EAA7C,wBAA6C,CAAA,EAAlB,KAAA,CAAM,YAAY"}
|
|
@@ -1,6 +1,11 @@
|
|
|
1
1
|
import { cn } from "../utils/index.js";
|
|
2
2
|
import { TimelineItem, TimelineItemContent, TimelineItemTimestamp } from "../../primitives/timeline-item.js";
|
|
3
|
+
import { extractFileParts, extractImageParts } from "../../primitives/timeline-item-attachments.js";
|
|
4
|
+
import icons_default from "./icons.js";
|
|
3
5
|
import { useSupportText } from "../text/index.js";
|
|
6
|
+
import { ImageLightbox } from "./image-lightbox.js";
|
|
7
|
+
import { useState } from "react";
|
|
8
|
+
import { formatFileSize } from "@cossistant/core";
|
|
4
9
|
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
5
10
|
|
|
6
11
|
//#region src/support/components/timeline-message-item.tsx
|
|
@@ -10,7 +15,17 @@ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
|
10
15
|
*/
|
|
11
16
|
function TimelineMessageItem({ item, isLast = false, isSentByViewer = false }) {
|
|
12
17
|
const text = useSupportText();
|
|
13
|
-
|
|
18
|
+
const [lightboxOpen, setLightboxOpen] = useState(false);
|
|
19
|
+
const [lightboxIndex, setLightboxIndex] = useState(0);
|
|
20
|
+
const images = extractImageParts(item.parts);
|
|
21
|
+
const files = extractFileParts(item.parts);
|
|
22
|
+
const hasAttachments = images.length > 0 || files.length > 0;
|
|
23
|
+
const hasText = item.text && item.text.trim().length > 0;
|
|
24
|
+
const openLightbox = (index) => {
|
|
25
|
+
setLightboxIndex(index);
|
|
26
|
+
setLightboxOpen(true);
|
|
27
|
+
};
|
|
28
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx(TimelineItem, {
|
|
14
29
|
item,
|
|
15
30
|
children: ({ isAI, timestamp }) => {
|
|
16
31
|
const isSentByViewerFinal = isSentByViewer;
|
|
@@ -18,27 +33,76 @@ function TimelineMessageItem({ item, isLast = false, isSentByViewer = false }) {
|
|
|
18
33
|
className: cn("flex w-full gap-2", isSentByViewerFinal && "flex-row-reverse", !isSentByViewerFinal && "flex-row"),
|
|
19
34
|
children: /* @__PURE__ */ jsxs("div", {
|
|
20
35
|
className: cn("flex w-full min-w-0 flex-1 flex-col gap-1", isSentByViewerFinal && "items-end"),
|
|
21
|
-
children: [
|
|
22
|
-
|
|
23
|
-
"
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
36
|
+
children: [
|
|
37
|
+
hasText && /* @__PURE__ */ jsx(TimelineItemContent, {
|
|
38
|
+
className: cn("block min-w-0 max-w-[300px] whitespace-pre-wrap break-words rounded-lg px-3.5 py-2.5 text-sm", {
|
|
39
|
+
"bg-co-background-300 text-co-foreground dark:bg-co-background-600": !isSentByViewerFinal,
|
|
40
|
+
"bg-co-primary text-co-primary-foreground": isSentByViewerFinal,
|
|
41
|
+
"rounded-br-sm": isLast && isSentByViewerFinal && !hasAttachments,
|
|
42
|
+
"rounded-bl-sm": isLast && !isSentByViewerFinal && !hasAttachments
|
|
43
|
+
}),
|
|
44
|
+
renderMarkdown: true,
|
|
45
|
+
text: item.text
|
|
27
46
|
}),
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
47
|
+
images.length > 0 && /* @__PURE__ */ jsx("div", {
|
|
48
|
+
className: cn("flex flex-wrap gap-2", isSentByViewerFinal && "justify-end"),
|
|
49
|
+
children: images.map((image, index) => /* @__PURE__ */ jsx("button", {
|
|
50
|
+
className: "group relative overflow-hidden rounded-lg focus:outline-none focus:ring-2 focus:ring-co-primary/50",
|
|
51
|
+
onClick: () => openLightbox(index),
|
|
52
|
+
type: "button",
|
|
53
|
+
children: /* @__PURE__ */ jsx("img", {
|
|
54
|
+
alt: image.fileName || `Image ${index + 1}`,
|
|
55
|
+
className: "max-h-[150px] max-w-[200px] cursor-pointer rounded-lg object-cover transition-transform group-hover:scale-105",
|
|
56
|
+
loading: "lazy",
|
|
57
|
+
src: image.url
|
|
58
|
+
})
|
|
59
|
+
}, image.url))
|
|
60
|
+
}),
|
|
61
|
+
files.length > 0 && /* @__PURE__ */ jsx("div", {
|
|
62
|
+
className: "flex flex-col gap-1",
|
|
63
|
+
children: files.map((file) => /* @__PURE__ */ jsxs("a", {
|
|
64
|
+
className: cn("flex items-center gap-2 rounded-lg px-3 py-2 text-xs transition-colors", {
|
|
65
|
+
"bg-co-background-300 text-co-foreground hover:bg-co-background-400 dark:bg-co-background-600 dark:hover:bg-co-background-500": !isSentByViewerFinal,
|
|
66
|
+
"bg-co-primary/80 text-co-primary-foreground hover:bg-co-primary": isSentByViewerFinal
|
|
67
|
+
}),
|
|
68
|
+
download: file.fileName,
|
|
69
|
+
href: file.url,
|
|
70
|
+
rel: "noopener noreferrer",
|
|
71
|
+
target: "_blank",
|
|
72
|
+
children: [
|
|
73
|
+
/* @__PURE__ */ jsx(icons_default, {
|
|
74
|
+
className: "h-4 w-4 shrink-0",
|
|
75
|
+
name: "file"
|
|
76
|
+
}),
|
|
77
|
+
/* @__PURE__ */ jsx("span", {
|
|
78
|
+
className: "flex-1 truncate font-medium",
|
|
79
|
+
children: file.fileName || "Download file"
|
|
80
|
+
}),
|
|
81
|
+
file.size && /* @__PURE__ */ jsx("span", {
|
|
82
|
+
className: "text-co-muted-foreground opacity-70",
|
|
83
|
+
children: formatFileSize(file.size)
|
|
84
|
+
})
|
|
85
|
+
]
|
|
86
|
+
}, file.url))
|
|
87
|
+
}),
|
|
88
|
+
isLast && /* @__PURE__ */ jsx(TimelineItemTimestamp, {
|
|
89
|
+
className: "px-1 text-co-muted-foreground text-xs",
|
|
90
|
+
timestamp,
|
|
91
|
+
children: () => /* @__PURE__ */ jsxs(Fragment, { children: [timestamp.toLocaleTimeString([], {
|
|
92
|
+
hour: "2-digit",
|
|
93
|
+
minute: "2-digit"
|
|
94
|
+
}), isAI && ` ${text("component.message.timestamp.aiIndicator")}`] })
|
|
95
|
+
})
|
|
96
|
+
]
|
|
38
97
|
})
|
|
39
98
|
});
|
|
40
99
|
}
|
|
41
|
-
})
|
|
100
|
+
}), images.length > 0 && /* @__PURE__ */ jsx(ImageLightbox, {
|
|
101
|
+
images,
|
|
102
|
+
initialIndex: lightboxIndex,
|
|
103
|
+
isOpen: lightboxOpen,
|
|
104
|
+
onClose: () => setLightboxOpen(false)
|
|
105
|
+
})] });
|
|
42
106
|
}
|
|
43
107
|
|
|
44
108
|
//#endregion
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"timeline-message-item.js","names":["PrimitiveTimelineItem"],"sources":["../../../src/support/components/timeline-message-item.tsx"],"sourcesContent":["import type { TimelineItem } from \"@cossistant/types/api/timeline-item\";\nimport type React from \"react\";\nimport {\n\tTimelineItem as PrimitiveTimelineItem,\n\tTimelineItemContent,\n\tTimelineItemTimestamp,\n} from \"../../primitives/timeline-item\";\nimport { useSupportText } from \"../text\";\nimport { cn } from \"../utils\";\n\nexport type TimelineMessageItemProps = {\n\titem: TimelineItem;\n\tisLast?: boolean;\n\tisSentByViewer?: boolean;\n};\n\n/**\n * Message bubble renderer that adapts layout depending on whether the visitor\n * or an agent sent the message.\n */\nexport function TimelineMessageItem({\n\titem,\n\tisLast = false,\n\tisSentByViewer = false,\n}: TimelineMessageItemProps): React.ReactElement {\n\tconst text = useSupportText();\n\treturn (\n\t\t<PrimitiveTimelineItem item={item}>\n\t\t\t{({ isAI, timestamp }) => {\n\t\t\t\t// isSentByViewer defaults to false, meaning messages are treated as received\n\t\t\t\t// (left side with background) unless explicitly marked as sent by viewer\n\t\t\t\tconst isSentByViewerFinal = isSentByViewer;\n\n\t\t\t\treturn (\n\t\t\t\t\t<div\n\t\t\t\t\t\tclassName={cn(\n\t\t\t\t\t\t\t\"flex w-full gap-2\",\n\t\t\t\t\t\t\tisSentByViewerFinal && \"flex-row-reverse\",\n\t\t\t\t\t\t\t!isSentByViewerFinal && \"flex-row\"\n\t\t\t\t\t\t)}\n\t\t\t\t\t>\n\t\t\t\t\t\t<div\n\t\t\t\t\t\t\tclassName={cn(\n\t\t\t\t\t\t\t\t\"flex w-full min-w-0 flex-1 flex-col gap-1\",\n\t\t\t\t\t\t\t\tisSentByViewerFinal && \"items-end\"\n\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t>\n\t\t\t\t\t\t\t<TimelineItemContent\n\t\t\t\t\t\t\t\tclassName={cn(\n\t\t\t\t\t\t\t\t\t\"block min-w-0 max-w-[300px] whitespace-pre-wrap break-words rounded-lg px-3.5 py-2.5 text-sm\",\n\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\t\"bg-co-background-300 text-co-foreground dark:bg-co-background-600\":\n\t\t\t\t\t\t\t\t\t\t\t!isSentByViewerFinal,\n\t\t\t\t\t\t\t\t\t\t\"bg-co-primary text-co-primary-foreground\":\n\t\t\t\t\t\t\t\t\t\t\tisSentByViewerFinal,\n\t\t\t\t\t\t\t\t\t\t\"rounded-br-sm\"
|
|
1
|
+
{"version":3,"file":"timeline-message-item.js","names":["PrimitiveTimelineItem","Icon"],"sources":["../../../src/support/components/timeline-message-item.tsx"],"sourcesContent":["import { formatFileSize } from \"@cossistant/core\";\nimport type { TimelineItem } from \"@cossistant/types/api/timeline-item\";\nimport type React from \"react\";\nimport { useState } from \"react\";\nimport {\n\tTimelineItem as PrimitiveTimelineItem,\n\tTimelineItemContent,\n\tTimelineItemTimestamp,\n} from \"../../primitives/timeline-item\";\nimport {\n\textractFileParts,\n\textractImageParts,\n} from \"../../primitives/timeline-item-attachments\";\nimport { useSupportText } from \"../text\";\nimport { cn } from \"../utils\";\nimport Icon from \"./icons\";\nimport { ImageLightbox } from \"./image-lightbox\";\n\nexport type TimelineMessageItemProps = {\n\titem: TimelineItem;\n\tisLast?: boolean;\n\tisSentByViewer?: boolean;\n};\n\n/**\n * Message bubble renderer that adapts layout depending on whether the visitor\n * or an agent sent the message.\n */\nexport function TimelineMessageItem({\n\titem,\n\tisLast = false,\n\tisSentByViewer = false,\n}: TimelineMessageItemProps): React.ReactElement {\n\tconst text = useSupportText();\n\tconst [lightboxOpen, setLightboxOpen] = useState(false);\n\tconst [lightboxIndex, setLightboxIndex] = useState(0);\n\n\t// Extract image and file parts\n\tconst images = extractImageParts(item.parts);\n\tconst files = extractFileParts(item.parts);\n\tconst hasAttachments = images.length > 0 || files.length > 0;\n\tconst hasText = item.text && item.text.trim().length > 0;\n\n\tconst openLightbox = (index: number) => {\n\t\tsetLightboxIndex(index);\n\t\tsetLightboxOpen(true);\n\t};\n\n\treturn (\n\t\t<>\n\t\t\t<PrimitiveTimelineItem item={item}>\n\t\t\t\t{({ isAI, timestamp }) => {\n\t\t\t\t\t// isSentByViewer defaults to false, meaning messages are treated as received\n\t\t\t\t\t// (left side with background) unless explicitly marked as sent by viewer\n\t\t\t\t\tconst isSentByViewerFinal = isSentByViewer;\n\n\t\t\t\t\treturn (\n\t\t\t\t\t\t<div\n\t\t\t\t\t\t\tclassName={cn(\n\t\t\t\t\t\t\t\t\"flex w-full gap-2\",\n\t\t\t\t\t\t\t\tisSentByViewerFinal && \"flex-row-reverse\",\n\t\t\t\t\t\t\t\t!isSentByViewerFinal && \"flex-row\"\n\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t>\n\t\t\t\t\t\t\t<div\n\t\t\t\t\t\t\t\tclassName={cn(\n\t\t\t\t\t\t\t\t\t\"flex w-full min-w-0 flex-1 flex-col gap-1\",\n\t\t\t\t\t\t\t\t\tisSentByViewerFinal && \"items-end\"\n\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t{/* Text content */}\n\t\t\t\t\t\t\t\t{hasText && (\n\t\t\t\t\t\t\t\t\t<TimelineItemContent\n\t\t\t\t\t\t\t\t\t\tclassName={cn(\n\t\t\t\t\t\t\t\t\t\t\t\"block min-w-0 max-w-[300px] whitespace-pre-wrap break-words rounded-lg px-3.5 py-2.5 text-sm\",\n\t\t\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\t\t\t\"bg-co-background-300 text-co-foreground dark:bg-co-background-600\":\n\t\t\t\t\t\t\t\t\t\t\t\t\t!isSentByViewerFinal,\n\t\t\t\t\t\t\t\t\t\t\t\t\"bg-co-primary text-co-primary-foreground\":\n\t\t\t\t\t\t\t\t\t\t\t\t\tisSentByViewerFinal,\n\t\t\t\t\t\t\t\t\t\t\t\t\"rounded-br-sm\":\n\t\t\t\t\t\t\t\t\t\t\t\t\tisLast && isSentByViewerFinal && !hasAttachments,\n\t\t\t\t\t\t\t\t\t\t\t\t\"rounded-bl-sm\":\n\t\t\t\t\t\t\t\t\t\t\t\t\tisLast && !isSentByViewerFinal && !hasAttachments,\n\t\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t\t\t\trenderMarkdown\n\t\t\t\t\t\t\t\t\t\ttext={item.text}\n\t\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t)}\n\n\t\t\t\t\t\t\t\t{/* Image attachments */}\n\t\t\t\t\t\t\t\t{images.length > 0 && (\n\t\t\t\t\t\t\t\t\t<div\n\t\t\t\t\t\t\t\t\t\tclassName={cn(\n\t\t\t\t\t\t\t\t\t\t\t\"flex flex-wrap gap-2\",\n\t\t\t\t\t\t\t\t\t\t\tisSentByViewerFinal && \"justify-end\"\n\t\t\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t{images.map((image, index) => (\n\t\t\t\t\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\t\t\t\t\tclassName=\"group relative overflow-hidden rounded-lg focus:outline-none focus:ring-2 focus:ring-co-primary/50\"\n\t\t\t\t\t\t\t\t\t\t\t\tkey={image.url}\n\t\t\t\t\t\t\t\t\t\t\t\tonClick={() => openLightbox(index)}\n\t\t\t\t\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t\t{/* biome-ignore lint/performance/noImgElement: React package, not Next.js specific */}\n\t\t\t\t\t\t\t\t\t\t\t\t{/* biome-ignore lint/nursery/useImageSize: Dynamic image dimensions not known at render time */}\n\t\t\t\t\t\t\t\t\t\t\t\t<img\n\t\t\t\t\t\t\t\t\t\t\t\t\talt={image.fileName || `Image ${index + 1}`}\n\t\t\t\t\t\t\t\t\t\t\t\t\tclassName=\"max-h-[150px] max-w-[200px] cursor-pointer rounded-lg object-cover transition-transform group-hover:scale-105\"\n\t\t\t\t\t\t\t\t\t\t\t\t\tloading=\"lazy\"\n\t\t\t\t\t\t\t\t\t\t\t\t\tsrc={image.url}\n\t\t\t\t\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t\t\t\t</button>\n\t\t\t\t\t\t\t\t\t\t))}\n\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t)}\n\n\t\t\t\t\t\t\t\t{/* File attachments */}\n\t\t\t\t\t\t\t\t{files.length > 0 && (\n\t\t\t\t\t\t\t\t\t<div className=\"flex flex-col gap-1\">\n\t\t\t\t\t\t\t\t\t\t{files.map((file) => (\n\t\t\t\t\t\t\t\t\t\t\t<a\n\t\t\t\t\t\t\t\t\t\t\t\tclassName={cn(\n\t\t\t\t\t\t\t\t\t\t\t\t\t\"flex items-center gap-2 rounded-lg px-3 py-2 text-xs transition-colors\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"bg-co-background-300 text-co-foreground hover:bg-co-background-400 dark:bg-co-background-600 dark:hover:bg-co-background-500\":\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t!isSentByViewerFinal,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"bg-co-primary/80 text-co-primary-foreground hover:bg-co-primary\":\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tisSentByViewerFinal,\n\t\t\t\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t\t\t\t\t\tdownload={file.fileName}\n\t\t\t\t\t\t\t\t\t\t\t\thref={file.url}\n\t\t\t\t\t\t\t\t\t\t\t\tkey={file.url}\n\t\t\t\t\t\t\t\t\t\t\t\trel=\"noopener noreferrer\"\n\t\t\t\t\t\t\t\t\t\t\t\ttarget=\"_blank\"\n\t\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t\t<Icon className=\"h-4 w-4 shrink-0\" name=\"file\" />\n\t\t\t\t\t\t\t\t\t\t\t\t<span className=\"flex-1 truncate font-medium\">\n\t\t\t\t\t\t\t\t\t\t\t\t\t{file.fileName || \"Download file\"}\n\t\t\t\t\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t\t\t\t\t\t{file.size && (\n\t\t\t\t\t\t\t\t\t\t\t\t\t<span className=\"text-co-muted-foreground opacity-70\">\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t{formatFileSize(file.size)}\n\t\t\t\t\t\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t\t\t\t\t</a>\n\t\t\t\t\t\t\t\t\t\t))}\n\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t)}\n\n\t\t\t\t\t\t\t\t{isLast && (\n\t\t\t\t\t\t\t\t\t<TimelineItemTimestamp\n\t\t\t\t\t\t\t\t\t\tclassName=\"px-1 text-co-muted-foreground text-xs\"\n\t\t\t\t\t\t\t\t\t\ttimestamp={timestamp}\n\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t{() => (\n\t\t\t\t\t\t\t\t\t\t\t<>\n\t\t\t\t\t\t\t\t\t\t\t\t{timestamp.toLocaleTimeString([], {\n\t\t\t\t\t\t\t\t\t\t\t\t\thour: \"2-digit\",\n\t\t\t\t\t\t\t\t\t\t\t\t\tminute: \"2-digit\",\n\t\t\t\t\t\t\t\t\t\t\t\t})}\n\t\t\t\t\t\t\t\t\t\t\t\t{isAI &&\n\t\t\t\t\t\t\t\t\t\t\t\t\t` ${text(\"component.message.timestamp.aiIndicator\")}`}\n\t\t\t\t\t\t\t\t\t\t\t</>\n\t\t\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t\t\t</TimelineItemTimestamp>\n\t\t\t\t\t\t\t\t)}\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</PrimitiveTimelineItem>\n\n\t\t\t{/* Lightbox for images */}\n\t\t\t{images.length > 0 && (\n\t\t\t\t<ImageLightbox\n\t\t\t\t\timages={images}\n\t\t\t\t\tinitialIndex={lightboxIndex}\n\t\t\t\t\tisOpen={lightboxOpen}\n\t\t\t\t\tonClose={() => setLightboxOpen(false)}\n\t\t\t\t/>\n\t\t\t)}\n\t\t</>\n\t);\n}\n"],"mappings":";;;;;;;;;;;;;;;AA4BA,SAAgB,oBAAoB,EACnC,MACA,SAAS,OACT,iBAAiB,SAC+B;CAChD,MAAM,OAAO,gBAAgB;CAC7B,MAAM,CAAC,cAAc,mBAAmB,SAAS,MAAM;CACvD,MAAM,CAAC,eAAe,oBAAoB,SAAS,EAAE;CAGrD,MAAM,SAAS,kBAAkB,KAAK,MAAM;CAC5C,MAAM,QAAQ,iBAAiB,KAAK,MAAM;CAC1C,MAAM,iBAAiB,OAAO,SAAS,KAAK,MAAM,SAAS;CAC3D,MAAM,UAAU,KAAK,QAAQ,KAAK,KAAK,MAAM,CAAC,SAAS;CAEvD,MAAM,gBAAgB,UAAkB;AACvC,mBAAiB,MAAM;AACvB,kBAAgB,KAAK;;AAGtB,QACC,4CACC,oBAACA;EAA4B;aAC1B,EAAE,MAAM,gBAAgB;GAGzB,MAAM,sBAAsB;AAE5B,UACC,oBAAC;IACA,WAAW,GACV,qBACA,uBAAuB,oBACvB,CAAC,uBAAuB,WACxB;cAED,qBAAC;KACA,WAAW,GACV,6CACA,uBAAuB,YACvB;;MAGA,WACA,oBAAC;OACA,WAAW,GACV,gGACA;QACC,qEACC,CAAC;QACF,4CACC;QACD,iBACC,UAAU,uBAAuB,CAAC;QACnC,iBACC,UAAU,CAAC,uBAAuB,CAAC;QACpC,CACD;OACD;OACA,MAAM,KAAK;QACV;MAIF,OAAO,SAAS,KAChB,oBAAC;OACA,WAAW,GACV,wBACA,uBAAuB,cACvB;iBAEA,OAAO,KAAK,OAAO,UACnB,oBAAC;QACA,WAAU;QAEV,eAAe,aAAa,MAAM;QAClC,MAAK;kBAIL,oBAAC;SACA,KAAK,MAAM,YAAY,SAAS,QAAQ;SACxC,WAAU;SACV,SAAQ;SACR,KAAK,MAAM;UACV;UAXG,MAAM,IAYH,CACR;QACG;MAIN,MAAM,SAAS,KACf,oBAAC;OAAI,WAAU;iBACb,MAAM,KAAK,SACX,qBAAC;QACA,WAAW,GACV,0EACA;SACC,gIACC,CAAC;SACF,mEACC;SACD,CACD;QACD,UAAU,KAAK;QACf,MAAM,KAAK;QAEX,KAAI;QACJ,QAAO;;SAEP,oBAACC;UAAK,WAAU;UAAmB,MAAK;WAAS;SACjD,oBAAC;UAAK,WAAU;oBACd,KAAK,YAAY;WACZ;SACN,KAAK,QACL,oBAAC;UAAK,WAAU;oBACd,eAAe,KAAK,KAAK;WACpB;;UAXH,KAAK,IAaP,CACH;QACG;MAGN,UACA,oBAAC;OACA,WAAU;OACC;uBAGV,4CACE,UAAU,mBAAmB,EAAE,EAAE;QACjC,MAAM;QACN,QAAQ;QACR,CAAC,EACD,QACA,IAAI,KAAK,0CAA0C,MAClD;QAEmB;;MAEpB;KACD;;GAGe,EAGvB,OAAO,SAAS,KAChB,oBAAC;EACQ;EACR,cAAc;EACd,QAAQ;EACR,eAAe,gBAAgB,MAAM;GACpC,IAED"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import * as React$1 from "react";
|
|
2
|
+
|
|
3
|
+
//#region src/support/components/trigger.d.ts
|
|
4
|
+
type DefaultTriggerProps = {
|
|
5
|
+
className?: string;
|
|
6
|
+
};
|
|
7
|
+
/**
|
|
8
|
+
* Default styled trigger button.
|
|
9
|
+
* Used internally when no custom trigger is provided.
|
|
10
|
+
*/
|
|
11
|
+
declare const DefaultTrigger: React$1.FC<DefaultTriggerProps>;
|
|
12
|
+
//#endregion
|
|
13
|
+
export { DefaultTrigger, DefaultTriggerProps };
|
|
14
|
+
//# sourceMappingURL=trigger.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"trigger.d.ts","names":[],"sources":["../../../src/support/components/trigger.tsx"],"sourcesContent":[],"mappings":";;;KA2HY,mBAAA;;AAAZ,CAAA;AAQA;;;;cAAa,gBAAgB,OAAA,CAAM,GAAG"}
|
|
@@ -3,26 +3,26 @@
|
|
|
3
3
|
|
|
4
4
|
import { cn } from "../utils/index.js";
|
|
5
5
|
import { BouncingDots } from "./typing-indicator.js";
|
|
6
|
-
import {
|
|
7
|
-
import icons_default from "./icons.js";
|
|
6
|
+
import { SupportTrigger } from "../../primitives/trigger.js";
|
|
8
7
|
import { useNewMessageSound } from "../../hooks/use-new-message-sound.js";
|
|
9
8
|
import { useTypingSound } from "../../hooks/use-typing-sound.js";
|
|
10
|
-
import
|
|
9
|
+
import icons_default from "./icons.js";
|
|
10
|
+
import * as React$1 from "react";
|
|
11
11
|
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
12
12
|
import { AnimatePresence, motion } from "motion/react";
|
|
13
13
|
|
|
14
|
-
//#region src/support/components/
|
|
15
|
-
const
|
|
14
|
+
//#region src/support/components/trigger.tsx
|
|
15
|
+
const TriggerContent = ({ isOpen, unreadCount, isTyping }) => {
|
|
16
16
|
const playNewMessageSound = useNewMessageSound({
|
|
17
17
|
volume: .7,
|
|
18
18
|
playbackRate: 1
|
|
19
19
|
});
|
|
20
|
-
const previousUnreadCountRef = useRef(0);
|
|
20
|
+
const previousUnreadCountRef = React$1.useRef(0);
|
|
21
21
|
useTypingSound(!isOpen && isTyping, {
|
|
22
22
|
volume: 1,
|
|
23
23
|
playbackRate: 1.3
|
|
24
24
|
});
|
|
25
|
-
useEffect(() => {
|
|
25
|
+
React$1.useEffect(() => {
|
|
26
26
|
if (unreadCount > previousUnreadCountRef.current) playNewMessageSound();
|
|
27
27
|
previousUnreadCountRef.current = unreadCount;
|
|
28
28
|
}, [unreadCount, playNewMessageSound]);
|
|
@@ -127,10 +127,14 @@ const BubbleContent = ({ isOpen, unreadCount, isTyping }) => {
|
|
|
127
127
|
}
|
|
128
128
|
})] });
|
|
129
129
|
};
|
|
130
|
-
|
|
130
|
+
/**
|
|
131
|
+
* Default styled trigger button.
|
|
132
|
+
* Used internally when no custom trigger is provided.
|
|
133
|
+
*/
|
|
134
|
+
const DefaultTrigger = ({ className }) => /* @__PURE__ */ jsx(SupportTrigger, {
|
|
131
135
|
asChild: true,
|
|
132
136
|
children: ({ isOpen, unreadCount, isTyping }) => /* @__PURE__ */ jsx(motion.button, {
|
|
133
|
-
className: cn("relative flex size-14 cursor-pointer items-center justify-center rounded-full bg-co-primary text-co-primary-foreground transition-colors hover:bg-co-primary/90 data-[open=true]:bg-co-primary/90", className),
|
|
137
|
+
className: cn("relative z-[9999] flex size-14 cursor-pointer items-center justify-center rounded-full bg-co-primary text-co-primary-foreground transition-colors hover:bg-co-primary/90 data-[open=true]:bg-co-primary/90", className),
|
|
134
138
|
"data-open": isOpen,
|
|
135
139
|
transition: {
|
|
136
140
|
type: "spring",
|
|
@@ -139,7 +143,7 @@ const Bubble = ({ className }) => /* @__PURE__ */ jsx(SupportBubble, {
|
|
|
139
143
|
},
|
|
140
144
|
type: "button",
|
|
141
145
|
whileTap: { scale: .95 },
|
|
142
|
-
children: /* @__PURE__ */ jsx(
|
|
146
|
+
children: /* @__PURE__ */ jsx(TriggerContent, {
|
|
143
147
|
isOpen,
|
|
144
148
|
isTyping,
|
|
145
149
|
unreadCount
|
|
@@ -148,5 +152,5 @@ const Bubble = ({ className }) => /* @__PURE__ */ jsx(SupportBubble, {
|
|
|
148
152
|
});
|
|
149
153
|
|
|
150
154
|
//#endregion
|
|
151
|
-
export {
|
|
152
|
-
//# sourceMappingURL=
|
|
155
|
+
export { DefaultTrigger };
|
|
156
|
+
//# sourceMappingURL=trigger.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"trigger.js","names":["TriggerContent: React.FC<TriggerContentProps>","React","Icon","DefaultTrigger: React.FC<DefaultTriggerProps>","Primitive.Trigger"],"sources":["../../../src/support/components/trigger.tsx"],"sourcesContent":["\"use client\";\n\nimport { AnimatePresence, motion } from \"motion/react\";\nimport * as React from \"react\";\nimport { useNewMessageSound } from \"../../hooks/use-new-message-sound\";\nimport { useTypingSound } from \"../../hooks/use-typing-sound\";\nimport * as Primitive from \"../../primitives\";\nimport type { TriggerRenderProps } from \"../types\";\nimport { cn } from \"../utils\";\nimport Icon from \"./icons\";\nimport { BouncingDots } from \"./typing-indicator\";\n\ntype TriggerContentProps = {\n\tisOpen: boolean;\n\tunreadCount: number;\n\tisTyping: boolean;\n};\n\nconst TriggerContent: React.FC<TriggerContentProps> = ({\n\tisOpen,\n\tunreadCount,\n\tisTyping,\n}) => {\n\tconst playNewMessageSound = useNewMessageSound({\n\t\tvolume: 0.7,\n\t\tplaybackRate: 1.0,\n\t});\n\tconst previousUnreadCountRef = React.useRef(0);\n\n\tuseTypingSound(!isOpen && isTyping, {\n\t\tvolume: 1,\n\t\tplaybackRate: 1.3,\n\t});\n\n\tReact.useEffect(() => {\n\t\tif (unreadCount > previousUnreadCountRef.current) {\n\t\t\tplayNewMessageSound();\n\t\t}\n\t\tpreviousUnreadCountRef.current = unreadCount;\n\t}, [unreadCount, playNewMessageSound]);\n\n\treturn (\n\t\t<>\n\t\t\t<AnimatePresence mode=\"wait\">\n\t\t\t\t{isOpen ? (\n\t\t\t\t\t<motion.div\n\t\t\t\t\t\tanimate={{\n\t\t\t\t\t\t\tscale: 1,\n\t\t\t\t\t\t\trotate: 0,\n\t\t\t\t\t\t\topacity: 1,\n\t\t\t\t\t\t\ttransition: { duration: 0.2, ease: \"easeOut\" },\n\t\t\t\t\t\t}}\n\t\t\t\t\t\tclassName=\"flex items-center justify-center\"\n\t\t\t\t\t\texit={{\n\t\t\t\t\t\t\tscale: 0.9,\n\t\t\t\t\t\t\trotate: -45,\n\t\t\t\t\t\t\topacity: 0,\n\t\t\t\t\t\t\ttransition: { duration: 0.1, ease: \"easeIn\" },\n\t\t\t\t\t\t}}\n\t\t\t\t\t\tinitial={{ scale: 0.9, rotate: 45, opacity: 0 }}\n\t\t\t\t\t\tkey=\"chevron\"\n\t\t\t\t\t>\n\t\t\t\t\t\t<Icon className=\"size-5\" name=\"chevron-down\" />\n\t\t\t\t\t</motion.div>\n\t\t\t\t) : isTyping ? (\n\t\t\t\t\t<motion.span\n\t\t\t\t\t\tanimate={{\n\t\t\t\t\t\t\topacity: 1,\n\t\t\t\t\t\t\tscale: 1,\n\t\t\t\t\t\t\ttransition: {\n\t\t\t\t\t\t\t\tduration: 0.2,\n\t\t\t\t\t\t\t\tease: \"easeOut\",\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t}}\n\t\t\t\t\t\tclassName=\"pointer-events-none flex items-center rounded-full text-co-primary\"\n\t\t\t\t\t\texit={{\n\t\t\t\t\t\t\topacity: 0,\n\t\t\t\t\t\t\tscale: 0.9,\n\t\t\t\t\t\t\ttransition: {\n\t\t\t\t\t\t\t\tduration: 0.1,\n\t\t\t\t\t\t\t\tease: \"easeIn\",\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t}}\n\t\t\t\t\t\tinitial={{ opacity: 0, scale: 0.9 }}\n\t\t\t\t\t\tkey=\"typing-indicator\"\n\t\t\t\t\t>\n\t\t\t\t\t\t<BouncingDots className=\"bg-co-primary-foreground\" />\n\t\t\t\t\t</motion.span>\n\t\t\t\t) : (\n\t\t\t\t\t<motion.div\n\t\t\t\t\t\tanimate={{\n\t\t\t\t\t\t\tscale: 1,\n\t\t\t\t\t\t\trotate: 0,\n\t\t\t\t\t\t\topacity: 1,\n\t\t\t\t\t\t\ttransition: { duration: 0.2, ease: \"easeOut\" },\n\t\t\t\t\t\t}}\n\t\t\t\t\t\tclassName=\"flex items-center justify-center\"\n\t\t\t\t\t\texit={{\n\t\t\t\t\t\t\tscale: 0.9,\n\t\t\t\t\t\t\trotate: 45,\n\t\t\t\t\t\t\topacity: 0,\n\t\t\t\t\t\t\ttransition: { duration: 0.1, ease: \"easeIn\" },\n\t\t\t\t\t\t}}\n\t\t\t\t\t\tinitial={{ scale: 0.9, rotate: -45, opacity: 0 }}\n\t\t\t\t\t\tkey=\"chat\"\n\t\t\t\t\t>\n\t\t\t\t\t\t<Icon className=\"size-6.5\" name=\"chat\" variant=\"filled\" />\n\t\t\t\t\t</motion.div>\n\t\t\t\t)}\n\t\t\t</AnimatePresence>\n\n\t\t\t{unreadCount > 0 && (\n\t\t\t\t<motion.div\n\t\t\t\t\tanimate={{ scale: 1, opacity: 1 }}\n\t\t\t\t\tclassName=\"absolute top-0.5 right-0.5 flex size-2 items-center justify-center rounded-full bg-co-destructive font-medium text-[10px] text-co-destructive-foreground text-white text-xs\"\n\t\t\t\t\texit={{ scale: 0, opacity: 0 }}\n\t\t\t\t\tinitial={{ scale: 0, opacity: 0 }}\n\t\t\t\t/>\n\t\t\t)}\n\t\t</>\n\t);\n};\n\nexport type DefaultTriggerProps = {\n\tclassName?: string;\n};\n\n/**\n * Default styled trigger button.\n * Used internally when no custom trigger is provided.\n */\nexport const DefaultTrigger: React.FC<DefaultTriggerProps> = ({\n\tclassName,\n}) => (\n\t<Primitive.Trigger asChild>\n\t\t{({ isOpen, unreadCount, isTyping }: TriggerRenderProps) => (\n\t\t\t<motion.button\n\t\t\t\tclassName={cn(\n\t\t\t\t\t\"relative z-[9999] flex size-14 cursor-pointer items-center justify-center rounded-full bg-co-primary text-co-primary-foreground transition-colors hover:bg-co-primary/90 data-[open=true]:bg-co-primary/90\",\n\t\t\t\t\tclassName\n\t\t\t\t)}\n\t\t\t\tdata-open={isOpen}\n\t\t\t\ttransition={{\n\t\t\t\t\ttype: \"spring\",\n\t\t\t\t\tstiffness: 800,\n\t\t\t\t\tdamping: 17,\n\t\t\t\t}}\n\t\t\t\ttype=\"button\"\n\t\t\t\twhileTap={{ scale: 0.95 }}\n\t\t\t>\n\t\t\t\t<TriggerContent\n\t\t\t\t\tisOpen={isOpen}\n\t\t\t\t\tisTyping={isTyping}\n\t\t\t\t\tunreadCount={unreadCount}\n\t\t\t\t/>\n\t\t\t</motion.button>\n\t\t)}\n\t</Primitive.Trigger>\n);\n"],"mappings":";;;;;;;;;;;;;;AAkBA,MAAMA,kBAAiD,EACtD,QACA,aACA,eACK;CACL,MAAM,sBAAsB,mBAAmB;EAC9C,QAAQ;EACR,cAAc;EACd,CAAC;CACF,MAAM,yBAAyBC,QAAM,OAAO,EAAE;AAE9C,gBAAe,CAAC,UAAU,UAAU;EACnC,QAAQ;EACR,cAAc;EACd,CAAC;AAEF,SAAM,gBAAgB;AACrB,MAAI,cAAc,uBAAuB,QACxC,sBAAqB;AAEtB,yBAAuB,UAAU;IAC/B,CAAC,aAAa,oBAAoB,CAAC;AAEtC,QACC,4CACC,oBAAC;EAAgB,MAAK;YACpB,SACA,oBAAC,OAAO;GACP,SAAS;IACR,OAAO;IACP,QAAQ;IACR,SAAS;IACT,YAAY;KAAE,UAAU;KAAK,MAAM;KAAW;IAC9C;GACD,WAAU;GACV,MAAM;IACL,OAAO;IACP,QAAQ;IACR,SAAS;IACT,YAAY;KAAE,UAAU;KAAK,MAAM;KAAU;IAC7C;GACD,SAAS;IAAE,OAAO;IAAK,QAAQ;IAAI,SAAS;IAAG;aAG/C,oBAACC;IAAK,WAAU;IAAS,MAAK;KAAiB;KAF3C,UAGQ,GACV,WACH,oBAAC,OAAO;GACP,SAAS;IACR,SAAS;IACT,OAAO;IACP,YAAY;KACX,UAAU;KACV,MAAM;KACN;IACD;GACD,WAAU;GACV,MAAM;IACL,SAAS;IACT,OAAO;IACP,YAAY;KACX,UAAU;KACV,MAAM;KACN;IACD;GACD,SAAS;IAAE,SAAS;IAAG,OAAO;IAAK;aAGnC,oBAAC,gBAAa,WAAU,6BAA6B;KAFjD,mBAGS,GAEd,oBAAC,OAAO;GACP,SAAS;IACR,OAAO;IACP,QAAQ;IACR,SAAS;IACT,YAAY;KAAE,UAAU;KAAK,MAAM;KAAW;IAC9C;GACD,WAAU;GACV,MAAM;IACL,OAAO;IACP,QAAQ;IACR,SAAS;IACT,YAAY;KAAE,UAAU;KAAK,MAAM;KAAU;IAC7C;GACD,SAAS;IAAE,OAAO;IAAK,QAAQ;IAAK,SAAS;IAAG;aAGhD,oBAACA;IAAK,WAAU;IAAW,MAAK;IAAO,SAAQ;KAAW;KAFtD,OAGQ;GAEG,EAEjB,cAAc,KACd,oBAAC,OAAO;EACP,SAAS;GAAE,OAAO;GAAG,SAAS;GAAG;EACjC,WAAU;EACV,MAAM;GAAE,OAAO;GAAG,SAAS;GAAG;EAC9B,SAAS;GAAE,OAAO;GAAG,SAAS;GAAG;GAChC,IAED;;;;;;AAYL,MAAaC,kBAAiD,EAC7D,gBAEA,oBAACC;CAAkB;YAChB,EAAE,QAAQ,aAAa,eACxB,oBAAC,OAAO;EACP,WAAW,GACV,8MACA,UACA;EACD,aAAW;EACX,YAAY;GACX,MAAM;GACN,WAAW;GACX,SAAS;GACT;EACD,MAAK;EACL,UAAU,EAAE,OAAO,KAAM;YAEzB,oBAAC;GACQ;GACE;GACG;IACZ;GACa;EAEE"}
|