@cossistant/react 0.0.26 → 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-create-conversation.d.ts.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-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/avatar/image.d.ts +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/multimodal-input.d.ts +2 -2
- package/primitives/multimodal-input.d.ts.map +1 -1
- 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 +40 -1
- 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/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
|
@@ -0,0 +1,282 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
import { cn } from "../utils/index.js";
|
|
5
|
+
import { useTriggerRef } from "../context/positioning.js";
|
|
6
|
+
import { useSupportConfig } from "../store/support-store.js";
|
|
7
|
+
import { SupportWindow } from "../../primitives/window.js";
|
|
8
|
+
import { SlotProvider, useSlots } from "../context/slots.js";
|
|
9
|
+
import * as React$1 from "react";
|
|
10
|
+
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
11
|
+
import { autoUpdate, flip, offset, shift, useFloating } from "@floating-ui/react";
|
|
12
|
+
import { AnimatePresence, motion } from "motion/react";
|
|
13
|
+
|
|
14
|
+
//#region src/support/components/content.tsx
|
|
15
|
+
/**
|
|
16
|
+
* Convert side + align props to Floating UI placement
|
|
17
|
+
*/
|
|
18
|
+
function getPlacement(side, align) {
|
|
19
|
+
if (align === "center") return side;
|
|
20
|
+
return `${side}-${align}`;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Get fallback positioning classes for when Floating UI is not available
|
|
24
|
+
* (e.g., trigger ref not set, or avoidCollisions is false)
|
|
25
|
+
*/
|
|
26
|
+
function getFallbackPositioningClasses(side, align) {
|
|
27
|
+
return cn({
|
|
28
|
+
top: "md:bottom-full md:mb-4",
|
|
29
|
+
bottom: "md:top-full md:mt-4",
|
|
30
|
+
left: "md:right-full md:mr-4",
|
|
31
|
+
right: "md:left-full md:ml-4"
|
|
32
|
+
}[side], {
|
|
33
|
+
top: {
|
|
34
|
+
start: "md:left-0",
|
|
35
|
+
center: "md:left-1/2 md:-translate-x-1/2",
|
|
36
|
+
end: "md:right-0"
|
|
37
|
+
},
|
|
38
|
+
bottom: {
|
|
39
|
+
start: "md:left-0",
|
|
40
|
+
center: "md:left-1/2 md:-translate-x-1/2",
|
|
41
|
+
end: "md:right-0"
|
|
42
|
+
},
|
|
43
|
+
left: {
|
|
44
|
+
start: "md:top-0",
|
|
45
|
+
center: "md:top-1/2 md:-translate-y-1/2",
|
|
46
|
+
end: "md:bottom-0"
|
|
47
|
+
},
|
|
48
|
+
right: {
|
|
49
|
+
start: "md:top-0",
|
|
50
|
+
center: "md:top-1/2 md:-translate-y-1/2",
|
|
51
|
+
end: "md:bottom-0"
|
|
52
|
+
}
|
|
53
|
+
}[side][align]);
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Get fallback offset styles for static positioning
|
|
57
|
+
*/
|
|
58
|
+
function getFallbackOffsetStyle(side, sideOffset) {
|
|
59
|
+
if (sideOffset === 16) return;
|
|
60
|
+
return {
|
|
61
|
+
top: { marginBottom: sideOffset },
|
|
62
|
+
bottom: { marginTop: sideOffset },
|
|
63
|
+
left: { marginRight: sideOffset },
|
|
64
|
+
right: { marginLeft: sideOffset }
|
|
65
|
+
}[side];
|
|
66
|
+
}
|
|
67
|
+
function useIsMobile() {
|
|
68
|
+
const [isMobile, setIsMobile] = React$1.useState(false);
|
|
69
|
+
React$1.useEffect(() => {
|
|
70
|
+
const mediaQuery = window.matchMedia("(max-width: 767px)");
|
|
71
|
+
setIsMobile(mediaQuery.matches);
|
|
72
|
+
const handler = (event) => {
|
|
73
|
+
setIsMobile(event.matches);
|
|
74
|
+
};
|
|
75
|
+
mediaQuery.addEventListener("change", handler);
|
|
76
|
+
return () => mediaQuery.removeEventListener("change", handler);
|
|
77
|
+
}, []);
|
|
78
|
+
return isMobile;
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Content component for the support window.
|
|
82
|
+
* Uses Floating UI for automatic collision detection on desktop.
|
|
83
|
+
* Fullscreen on mobile, floating on desktop.
|
|
84
|
+
*
|
|
85
|
+
* @example
|
|
86
|
+
* // Basic usage (uses defaults: side="top", align="end")
|
|
87
|
+
* <Support.Content />
|
|
88
|
+
*
|
|
89
|
+
* @example
|
|
90
|
+
* // Custom positioning with collision avoidance
|
|
91
|
+
* <Support.Content side="bottom" align="start" sideOffset={24} />
|
|
92
|
+
*
|
|
93
|
+
* @example
|
|
94
|
+
* // Disable collision avoidance for static positioning
|
|
95
|
+
* <Support.Content avoidCollisions={false} />
|
|
96
|
+
*
|
|
97
|
+
* @example
|
|
98
|
+
* // Custom collision padding
|
|
99
|
+
* <Support.Content collisionPadding={{ top: 16, bottom: 32 }} />
|
|
100
|
+
*/
|
|
101
|
+
const Content = ({ className, children, side = "top", align = "end", sideOffset = 16, avoidCollisions = true, collisionPadding = 8 }) => {
|
|
102
|
+
const [showScrollIndicator, setShowScrollIndicator] = React$1.useState(false);
|
|
103
|
+
const containerRef = React$1.useRef(null);
|
|
104
|
+
const hasEverPositionedRef = React$1.useRef(false);
|
|
105
|
+
const isMobile = useIsMobile();
|
|
106
|
+
const triggerRefContext = useTriggerRef();
|
|
107
|
+
const { isOpen } = useSupportConfig();
|
|
108
|
+
const middleware = React$1.useMemo(() => {
|
|
109
|
+
const middlewares = [offset(sideOffset)];
|
|
110
|
+
if (avoidCollisions) middlewares.push(flip({
|
|
111
|
+
padding: collisionPadding,
|
|
112
|
+
fallbackAxisSideDirection: "start"
|
|
113
|
+
}), shift({ padding: collisionPadding }));
|
|
114
|
+
return middlewares;
|
|
115
|
+
}, [
|
|
116
|
+
sideOffset,
|
|
117
|
+
avoidCollisions,
|
|
118
|
+
collisionPadding
|
|
119
|
+
]);
|
|
120
|
+
const triggerElement = triggerRefContext?.triggerElement ?? null;
|
|
121
|
+
const { refs, update, x, y, isPositioned } = useFloating({
|
|
122
|
+
placement: getPlacement(side, align),
|
|
123
|
+
strategy: "fixed",
|
|
124
|
+
middleware,
|
|
125
|
+
whileElementsMounted: autoUpdate,
|
|
126
|
+
open: isOpen,
|
|
127
|
+
elements: { reference: triggerElement }
|
|
128
|
+
});
|
|
129
|
+
const setFloatingRef = React$1.useCallback((node) => {
|
|
130
|
+
refs.setFloating(node);
|
|
131
|
+
}, [refs]);
|
|
132
|
+
React$1.useEffect(() => {
|
|
133
|
+
if (triggerElement && isOpen) requestAnimationFrame(() => {
|
|
134
|
+
update();
|
|
135
|
+
});
|
|
136
|
+
}, [
|
|
137
|
+
triggerElement,
|
|
138
|
+
isOpen,
|
|
139
|
+
update
|
|
140
|
+
]);
|
|
141
|
+
const useFloatingPositioning = avoidCollisions && !isMobile && triggerElement !== null;
|
|
142
|
+
const checkScroll = React$1.useCallback(() => {
|
|
143
|
+
const container = containerRef.current;
|
|
144
|
+
if (!container) return;
|
|
145
|
+
const { scrollTop, scrollHeight, clientHeight } = container;
|
|
146
|
+
const isScrollable = scrollHeight > clientHeight;
|
|
147
|
+
const isAtBottom = Math.abs(scrollHeight - scrollTop - clientHeight) < 5;
|
|
148
|
+
setShowScrollIndicator(isScrollable && !isAtBottom);
|
|
149
|
+
}, []);
|
|
150
|
+
React$1.useEffect(() => {
|
|
151
|
+
const container = containerRef.current;
|
|
152
|
+
if (!container) return;
|
|
153
|
+
checkScroll();
|
|
154
|
+
const handleScroll = () => {
|
|
155
|
+
checkScroll();
|
|
156
|
+
};
|
|
157
|
+
container.addEventListener("scroll", handleScroll, { passive: true });
|
|
158
|
+
const resizeObserver = new ResizeObserver(() => {
|
|
159
|
+
checkScroll();
|
|
160
|
+
});
|
|
161
|
+
resizeObserver.observe(container);
|
|
162
|
+
const mutationObserver = new MutationObserver(() => {
|
|
163
|
+
checkScroll();
|
|
164
|
+
});
|
|
165
|
+
mutationObserver.observe(container, {
|
|
166
|
+
childList: true,
|
|
167
|
+
subtree: true,
|
|
168
|
+
characterData: true
|
|
169
|
+
});
|
|
170
|
+
return () => {
|
|
171
|
+
container.removeEventListener("scroll", handleScroll);
|
|
172
|
+
resizeObserver.disconnect();
|
|
173
|
+
mutationObserver.disconnect();
|
|
174
|
+
};
|
|
175
|
+
}, [checkScroll]);
|
|
176
|
+
if (isPositioned) hasEverPositionedRef.current = true;
|
|
177
|
+
const hasValidFloatingPosition = hasEverPositionedRef.current;
|
|
178
|
+
const computedStyles = React$1.useMemo(() => {
|
|
179
|
+
if (isMobile) return {};
|
|
180
|
+
if (useFloatingPositioning && hasValidFloatingPosition) return {
|
|
181
|
+
position: "fixed",
|
|
182
|
+
left: x,
|
|
183
|
+
top: y
|
|
184
|
+
};
|
|
185
|
+
return getFallbackOffsetStyle(side, sideOffset) ?? {};
|
|
186
|
+
}, [
|
|
187
|
+
isMobile,
|
|
188
|
+
useFloatingPositioning,
|
|
189
|
+
hasValidFloatingPosition,
|
|
190
|
+
x,
|
|
191
|
+
y,
|
|
192
|
+
side,
|
|
193
|
+
sideOffset
|
|
194
|
+
]);
|
|
195
|
+
const computedClassName = cn("flex flex-col overflow-hidden overscroll-none bg-co-background", "max-md:fixed max-md:inset-0 max-md:z-[9999]", "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", useFloatingPositioning && hasValidFloatingPosition ? "md:fixed" : cn("md:absolute", getFallbackPositioningClasses(side, align)), className);
|
|
196
|
+
return /* @__PURE__ */ jsx(SlotProvider, { children: /* @__PURE__ */ jsx(SupportWindow, {
|
|
197
|
+
asChild: true,
|
|
198
|
+
children: /* @__PURE__ */ jsx(motion.div, {
|
|
199
|
+
animate: "visible",
|
|
200
|
+
className: computedClassName,
|
|
201
|
+
exit: "exit",
|
|
202
|
+
initial: "hidden",
|
|
203
|
+
ref: setFloatingRef,
|
|
204
|
+
style: computedStyles,
|
|
205
|
+
transition: {
|
|
206
|
+
default: { ease: "anticipate" },
|
|
207
|
+
layout: { duration: .3 }
|
|
208
|
+
},
|
|
209
|
+
variants: {
|
|
210
|
+
hidden: {
|
|
211
|
+
opacity: 0,
|
|
212
|
+
scale: .95,
|
|
213
|
+
filter: "blur(6px)"
|
|
214
|
+
},
|
|
215
|
+
visible: {
|
|
216
|
+
opacity: 1,
|
|
217
|
+
scale: 1,
|
|
218
|
+
filter: "blur(0px)"
|
|
219
|
+
},
|
|
220
|
+
exit: {
|
|
221
|
+
opacity: 0,
|
|
222
|
+
scale: .95,
|
|
223
|
+
filter: "blur(6px)"
|
|
224
|
+
}
|
|
225
|
+
},
|
|
226
|
+
children: /* @__PURE__ */ jsx(ContentInner, {
|
|
227
|
+
containerRef,
|
|
228
|
+
showScrollIndicator,
|
|
229
|
+
children
|
|
230
|
+
})
|
|
231
|
+
})
|
|
232
|
+
}) });
|
|
233
|
+
};
|
|
234
|
+
/**
|
|
235
|
+
* Inner content component that consumes slots.
|
|
236
|
+
* Separated to allow slot context to be established before consuming it.
|
|
237
|
+
*/
|
|
238
|
+
const ContentInner = ({ children, containerRef, showScrollIndicator }) => {
|
|
239
|
+
const { header, footer, hasCustomHeader, hasCustomFooter } = useSlots();
|
|
240
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
241
|
+
className: "relative flex h-full w-full flex-col",
|
|
242
|
+
children: [
|
|
243
|
+
hasCustomHeader && /* @__PURE__ */ jsx("div", {
|
|
244
|
+
className: "flex-shrink-0",
|
|
245
|
+
children: header
|
|
246
|
+
}),
|
|
247
|
+
/* @__PURE__ */ jsx("div", {
|
|
248
|
+
className: cn("flex flex-1 flex-col overflow-y-auto", !hasCustomHeader && "pt-18"),
|
|
249
|
+
ref: containerRef,
|
|
250
|
+
children
|
|
251
|
+
}),
|
|
252
|
+
hasCustomFooter && /* @__PURE__ */ jsx("div", {
|
|
253
|
+
className: "flex-shrink-0",
|
|
254
|
+
children: footer
|
|
255
|
+
}),
|
|
256
|
+
/* @__PURE__ */ jsx(AnimatePresence, { children: showScrollIndicator && /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx(motion.div, {
|
|
257
|
+
animate: { opacity: 1 },
|
|
258
|
+
className: "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",
|
|
259
|
+
exit: { opacity: 0 },
|
|
260
|
+
initial: { opacity: 0 },
|
|
261
|
+
transition: {
|
|
262
|
+
duration: .3,
|
|
263
|
+
ease: "easeInOut"
|
|
264
|
+
}
|
|
265
|
+
}), /* @__PURE__ */ jsx(motion.div, {
|
|
266
|
+
animate: { opacity: .6 },
|
|
267
|
+
className: "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",
|
|
268
|
+
exit: { opacity: 0 },
|
|
269
|
+
initial: { opacity: 0 },
|
|
270
|
+
transition: {
|
|
271
|
+
duration: .4,
|
|
272
|
+
ease: "easeInOut",
|
|
273
|
+
delay: .05
|
|
274
|
+
}
|
|
275
|
+
})] }) })
|
|
276
|
+
]
|
|
277
|
+
});
|
|
278
|
+
};
|
|
279
|
+
|
|
280
|
+
//#endregion
|
|
281
|
+
export { Content };
|
|
282
|
+
//# sourceMappingURL=content.js.map
|
|
@@ -0,0 +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,8 +1,8 @@
|
|
|
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 { coButtonVariants } from "./button.js";
|
|
5
4
|
import icons_default from "./icons.js";
|
|
5
|
+
import { coButtonVariants } from "./button.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";
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { cn } from "../utils/index.js";
|
|
2
2
|
import { TypingIndicator } from "./typing-indicator.js";
|
|
3
3
|
import { ConversationTimeline, ConversationTimelineContainer } from "../../primitives/conversation-timeline.js";
|
|
4
|
-
import { useConversationTimeline } from "../../hooks/use-conversation-timeline.js";
|
|
5
4
|
import { useTypingSound } from "../../hooks/use-typing-sound.js";
|
|
5
|
+
import { useConversationTimeline } from "../../hooks/use-conversation-timeline.js";
|
|
6
6
|
import { ConversationEvent } from "./conversation-event.js";
|
|
7
7
|
import { TimelineMessageGroup } from "./timeline-message-group.js";
|
|
8
8
|
import { useCallback, useMemo } from "react";
|
|
@@ -50,11 +50,11 @@ const ConversationTimelineList = ({ conversationId, items: timelineItems, classN
|
|
|
50
50
|
}, [seenNameLookup]);
|
|
51
51
|
return /* @__PURE__ */ jsx(ConversationTimeline, {
|
|
52
52
|
autoScroll: true,
|
|
53
|
-
className: cn("overflow-y-scroll
|
|
53
|
+
className: cn("overflow-y-scroll px-3 py-6", "co-scrollbar-thin", "h-full w-full", className),
|
|
54
54
|
id: "conversation-timeline",
|
|
55
55
|
items: timelineItems,
|
|
56
56
|
children: /* @__PURE__ */ jsxs(ConversationTimelineContainer, {
|
|
57
|
-
className: "flex min-h-full w-full flex-col gap-
|
|
57
|
+
className: "flex min-h-full w-full flex-col gap-5",
|
|
58
58
|
children: [timeline.groupedMessages.items.map((item, index) => {
|
|
59
59
|
if (item.type === "timeline_event") {
|
|
60
60
|
const eventPart = extractEventPart(item.item);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"conversation-timeline.js","names":["EMPTY_SEEN_BY_IDS: readonly string[]","EMPTY_SEEN_BY_NAMES: readonly string[]","ConversationTimelineList: React.FC<ConversationTimelineProps>","names: string[]","PrimitiveConversationTimeline"],"sources":["../../../src/support/components/conversation-timeline.tsx"],"sourcesContent":["import type { AvailableAIAgent, AvailableHumanAgent } from \"@cossistant/types\";\nimport type {\n\tTimelineItem,\n\tTimelinePartEvent,\n} from \"@cossistant/types/api/timeline-item\";\nimport type React from \"react\";\nimport { useCallback, useMemo } from \"react\";\nimport { useConversationTimeline } from \"../../hooks/use-conversation-timeline\";\nimport { useTypingSound } from \"../../hooks/use-typing-sound\";\nimport {\n\tConversationTimelineContainer,\n\tConversationTimeline as PrimitiveConversationTimeline,\n} from \"../../primitives/conversation-timeline\";\nimport { cn } from \"../utils\";\nimport { ConversationEvent } from \"./conversation-event\";\nimport { TimelineMessageGroup } from \"./timeline-message-group\";\nimport { TypingIndicator, type TypingParticipant } from \"./typing-indicator\";\n\n// Helper to extract event part from timeline item\nfunction extractEventPart(item: TimelineItem): TimelinePartEvent | null {\n\tif (item.type !== \"event\") {\n\t\treturn null;\n\t}\n\n\tconst eventPart = item.parts.find(\n\t\t(part): part is TimelinePartEvent => part.type === \"event\"\n\t);\n\n\treturn eventPart || null;\n}\n\nconst EMPTY_SEEN_BY_IDS: readonly string[] = Object.freeze([]);\nconst EMPTY_SEEN_BY_NAMES: readonly string[] = Object.freeze([]);\n\nexport type ConversationTimelineToolProps = {\n\titem: TimelineItem;\n\tconversationId: string;\n};\n\nexport type ConversationTimelineToolDefinition = {\n\tcomponent: React.ComponentType<ConversationTimelineToolProps>;\n};\n\nexport type ConversationTimelineTools = Record<\n\tstring,\n\tConversationTimelineToolDefinition\n>;\n\nexport type ConversationTimelineProps = {\n\tconversationId: string;\n\titems: TimelineItem[];\n\tclassName?: string;\n\tavailableAIAgents: AvailableAIAgent[];\n\tavailableHumanAgents: AvailableHumanAgent[];\n\tcurrentVisitorId?: string;\n\ttools?: ConversationTimelineTools;\n};\n\nexport const ConversationTimelineList: React.FC<ConversationTimelineProps> = ({\n\tconversationId,\n\titems: timelineItems,\n\tclassName,\n\tavailableAIAgents = [],\n\tavailableHumanAgents = [],\n\tcurrentVisitorId,\n\ttools,\n}) => {\n\tconst timeline = useConversationTimeline({\n\t\tconversationId,\n\t\titems: timelineItems,\n\t\tcurrentVisitorId,\n\t});\n\n\tconst typingIndicatorParticipants = useMemo(\n\t\t() =>\n\t\t\ttimeline.typingParticipants.map<TypingParticipant>((participant) => ({\n\t\t\t\tid: participant.id,\n\t\t\t\ttype: participant.type,\n\t\t\t})),\n\t\t[timeline.typingParticipants]\n\t);\n\n\t// Play typing sound when someone is typing\n\tuseTypingSound(typingIndicatorParticipants.length > 0, {\n\t\tvolume: 1,\n\t\tplaybackRate: 1.3,\n\t});\n\n\tconst seenNameLookup = useMemo(() => {\n\t\tconst map = new Map<string, string>();\n\n\t\tfor (const agent of availableHumanAgents) {\n\t\t\tif (agent.name) {\n\t\t\t\tmap.set(agent.id, agent.name);\n\t\t\t}\n\t\t}\n\n\t\tfor (const agent of availableAIAgents) {\n\t\t\tif (agent.name) {\n\t\t\t\tmap.set(agent.id, agent.name);\n\t\t\t}\n\t\t}\n\n\t\treturn map;\n\t}, [availableHumanAgents, availableAIAgents]);\n\n\tconst getSeenByNames = useCallback(\n\t\t(ids: readonly string[] = EMPTY_SEEN_BY_IDS): readonly string[] => {\n\t\t\tif (ids.length === 0 || seenNameLookup.size === 0) {\n\t\t\t\treturn EMPTY_SEEN_BY_NAMES;\n\t\t\t}\n\n\t\t\tconst uniqueNames = new Set<string>();\n\t\t\tconst names: string[] = [];\n\n\t\t\tfor (const id of ids) {\n\t\t\t\tconst name = seenNameLookup.get(id);\n\t\t\t\tif (!name || uniqueNames.has(name)) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\tuniqueNames.add(name);\n\t\t\t\tnames.push(name);\n\t\t\t}\n\n\t\t\tif (names.length === 0) {\n\t\t\t\treturn EMPTY_SEEN_BY_NAMES;\n\t\t\t}\n\n\t\t\treturn Object.freeze(names);\n\t\t},\n\t\t[seenNameLookup]\n\t);\n\n\treturn (\n\t\t<PrimitiveConversationTimeline\n\t\t\tautoScroll={true}\n\t\t\tclassName={cn(\n\t\t\t\t\"overflow-y-scroll
|
|
1
|
+
{"version":3,"file":"conversation-timeline.js","names":["EMPTY_SEEN_BY_IDS: readonly string[]","EMPTY_SEEN_BY_NAMES: readonly string[]","ConversationTimelineList: React.FC<ConversationTimelineProps>","names: string[]","PrimitiveConversationTimeline"],"sources":["../../../src/support/components/conversation-timeline.tsx"],"sourcesContent":["import type { AvailableAIAgent, AvailableHumanAgent } from \"@cossistant/types\";\nimport type {\n\tTimelineItem,\n\tTimelinePartEvent,\n} from \"@cossistant/types/api/timeline-item\";\nimport type React from \"react\";\nimport { useCallback, useMemo } from \"react\";\nimport { useConversationTimeline } from \"../../hooks/use-conversation-timeline\";\nimport { useTypingSound } from \"../../hooks/use-typing-sound\";\nimport {\n\tConversationTimelineContainer,\n\tConversationTimeline as PrimitiveConversationTimeline,\n} from \"../../primitives/conversation-timeline\";\nimport { cn } from \"../utils\";\nimport { ConversationEvent } from \"./conversation-event\";\nimport { TimelineMessageGroup } from \"./timeline-message-group\";\nimport { TypingIndicator, type TypingParticipant } from \"./typing-indicator\";\n\n// Helper to extract event part from timeline item\nfunction extractEventPart(item: TimelineItem): TimelinePartEvent | null {\n\tif (item.type !== \"event\") {\n\t\treturn null;\n\t}\n\n\tconst eventPart = item.parts.find(\n\t\t(part): part is TimelinePartEvent => part.type === \"event\"\n\t);\n\n\treturn eventPart || null;\n}\n\nconst EMPTY_SEEN_BY_IDS: readonly string[] = Object.freeze([]);\nconst EMPTY_SEEN_BY_NAMES: readonly string[] = Object.freeze([]);\n\nexport type ConversationTimelineToolProps = {\n\titem: TimelineItem;\n\tconversationId: string;\n};\n\nexport type ConversationTimelineToolDefinition = {\n\tcomponent: React.ComponentType<ConversationTimelineToolProps>;\n};\n\nexport type ConversationTimelineTools = Record<\n\tstring,\n\tConversationTimelineToolDefinition\n>;\n\nexport type ConversationTimelineProps = {\n\tconversationId: string;\n\titems: TimelineItem[];\n\tclassName?: string;\n\tavailableAIAgents: AvailableAIAgent[];\n\tavailableHumanAgents: AvailableHumanAgent[];\n\tcurrentVisitorId?: string;\n\ttools?: ConversationTimelineTools;\n};\n\nexport const ConversationTimelineList: React.FC<ConversationTimelineProps> = ({\n\tconversationId,\n\titems: timelineItems,\n\tclassName,\n\tavailableAIAgents = [],\n\tavailableHumanAgents = [],\n\tcurrentVisitorId,\n\ttools,\n}) => {\n\tconst timeline = useConversationTimeline({\n\t\tconversationId,\n\t\titems: timelineItems,\n\t\tcurrentVisitorId,\n\t});\n\n\tconst typingIndicatorParticipants = useMemo(\n\t\t() =>\n\t\t\ttimeline.typingParticipants.map<TypingParticipant>((participant) => ({\n\t\t\t\tid: participant.id,\n\t\t\t\ttype: participant.type,\n\t\t\t})),\n\t\t[timeline.typingParticipants]\n\t);\n\n\t// Play typing sound when someone is typing\n\tuseTypingSound(typingIndicatorParticipants.length > 0, {\n\t\tvolume: 1,\n\t\tplaybackRate: 1.3,\n\t});\n\n\tconst seenNameLookup = useMemo(() => {\n\t\tconst map = new Map<string, string>();\n\n\t\tfor (const agent of availableHumanAgents) {\n\t\t\tif (agent.name) {\n\t\t\t\tmap.set(agent.id, agent.name);\n\t\t\t}\n\t\t}\n\n\t\tfor (const agent of availableAIAgents) {\n\t\t\tif (agent.name) {\n\t\t\t\tmap.set(agent.id, agent.name);\n\t\t\t}\n\t\t}\n\n\t\treturn map;\n\t}, [availableHumanAgents, availableAIAgents]);\n\n\tconst getSeenByNames = useCallback(\n\t\t(ids: readonly string[] = EMPTY_SEEN_BY_IDS): readonly string[] => {\n\t\t\tif (ids.length === 0 || seenNameLookup.size === 0) {\n\t\t\t\treturn EMPTY_SEEN_BY_NAMES;\n\t\t\t}\n\n\t\t\tconst uniqueNames = new Set<string>();\n\t\t\tconst names: string[] = [];\n\n\t\t\tfor (const id of ids) {\n\t\t\t\tconst name = seenNameLookup.get(id);\n\t\t\t\tif (!name || uniqueNames.has(name)) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\tuniqueNames.add(name);\n\t\t\t\tnames.push(name);\n\t\t\t}\n\n\t\t\tif (names.length === 0) {\n\t\t\t\treturn EMPTY_SEEN_BY_NAMES;\n\t\t\t}\n\n\t\t\treturn Object.freeze(names);\n\t\t},\n\t\t[seenNameLookup]\n\t);\n\n\treturn (\n\t\t<PrimitiveConversationTimeline\n\t\t\tautoScroll={true}\n\t\t\tclassName={cn(\n\t\t\t\t\"overflow-y-scroll 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 === \"timeline_event\") {\n\t\t\t\t\t\t// Extract event data from parts\n\t\t\t\t\t\tconst eventPart = extractEventPart(item.item);\n\n\t\t\t\t\t\t// Only render if we have valid event data\n\t\t\t\t\t\tif (!eventPart) {\n\t\t\t\t\t\t\treturn null;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\treturn (\n\t\t\t\t\t\t\t<ConversationEvent\n\t\t\t\t\t\t\t\tavailableAIAgents={availableAIAgents}\n\t\t\t\t\t\t\t\tavailableHumanAgents={availableHumanAgents}\n\t\t\t\t\t\t\t\tcreatedAt={item.item.createdAt}\n\t\t\t\t\t\t\t\tevent={eventPart}\n\t\t\t\t\t\t\t\tkey={item.item.id ?? `timeline-event-${item.item.createdAt}`}\n\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\n\t\t\t\t\tif (item.type === \"timeline_tool\") {\n\t\t\t\t\t\tconst toolName = item.tool ?? item.item.tool ?? item.item.type;\n\t\t\t\t\t\tconst toolDefinition = toolName ? tools?.[toolName] : undefined;\n\n\t\t\t\t\t\tif (!toolDefinition) {\n\t\t\t\t\t\t\treturn null;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tconst ToolComponent = toolDefinition.component;\n\n\t\t\t\t\t\tconst toolKey =\n\t\t\t\t\t\t\titem.item.id ?? `${toolName}-${item.item.createdAt}-${index}`;\n\n\t\t\t\t\t\treturn (\n\t\t\t\t\t\t\t<ToolComponent\n\t\t\t\t\t\t\t\tconversationId={conversationId}\n\t\t\t\t\t\t\t\titem={item.item}\n\t\t\t\t\t\t\t\tkey={toolKey}\n\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\n\t\t\t\t\t// Only show seen indicator on the LAST message group sent by the visitor\n\t\t\t\t\tconst isLastVisitorGroup =\n\t\t\t\t\t\tindex === timeline.lastVisitorMessageGroupIndex;\n\t\t\t\t\tconst seenByIds =\n\t\t\t\t\t\tisLastVisitorGroup && item.lastMessageId\n\t\t\t\t\t\t\t? timeline.groupedMessages.getMessageSeenBy(item.lastMessageId)\n\t\t\t\t\t\t\t: EMPTY_SEEN_BY_IDS;\n\t\t\t\t\tconst seenByNames =\n\t\t\t\t\t\tseenByIds.length > 0\n\t\t\t\t\t\t\t? getSeenByNames(seenByIds)\n\t\t\t\t\t\t\t: EMPTY_SEEN_BY_NAMES;\n\n\t\t\t\t\t// Use first timeline item ID as stable key\n\t\t\t\t\tconst groupKey =\n\t\t\t\t\t\titem.lastMessageId ??\n\t\t\t\t\t\titem.items?.[0]?.id ??\n\t\t\t\t\t\t`group-${item.items?.[0]?.createdAt ?? index}`;\n\n\t\t\t\t\treturn (\n\t\t\t\t\t\t<TimelineMessageGroup\n\t\t\t\t\t\t\tavailableAIAgents={availableAIAgents}\n\t\t\t\t\t\t\tavailableHumanAgents={availableHumanAgents}\n\t\t\t\t\t\t\tcurrentVisitorId={currentVisitorId}\n\t\t\t\t\t\t\titems={item.items || []}\n\t\t\t\t\t\t\tkey={groupKey}\n\t\t\t\t\t\t\tseenByIds={seenByIds}\n\t\t\t\t\t\t\tseenByNames={seenByNames}\n\t\t\t\t\t\t/>\n\t\t\t\t\t);\n\t\t\t\t})}\n\t\t\t\t<div className=\"h-6 w-full\">\n\t\t\t\t\t{typingIndicatorParticipants.length > 0 ? (\n\t\t\t\t\t\t<TypingIndicator\n\t\t\t\t\t\t\tavailableAIAgents={availableAIAgents}\n\t\t\t\t\t\t\tavailableHumanAgents={availableHumanAgents}\n\t\t\t\t\t\t\tclassName=\"mt-2\"\n\t\t\t\t\t\t\tparticipants={typingIndicatorParticipants}\n\t\t\t\t\t\t/>\n\t\t\t\t\t) : null}\n\t\t\t\t</div>\n\t\t\t</ConversationTimelineContainer>\n\t\t</PrimitiveConversationTimeline>\n\t);\n};\n"],"mappings":";;;;;;;;;;;AAmBA,SAAS,iBAAiB,MAA8C;AACvE,KAAI,KAAK,SAAS,QACjB,QAAO;AAOR,QAJkB,KAAK,MAAM,MAC3B,SAAoC,KAAK,SAAS,QACnD,IAEmB;;AAGrB,MAAMA,oBAAuC,OAAO,OAAO,EAAE,CAAC;AAC9D,MAAMC,sBAAyC,OAAO,OAAO,EAAE,CAAC;AA0BhE,MAAaC,4BAAiE,EAC7E,gBACA,OAAO,eACP,WACA,oBAAoB,EAAE,EACtB,uBAAuB,EAAE,EACzB,kBACA,YACK;CACL,MAAM,WAAW,wBAAwB;EACxC;EACA,OAAO;EACP;EACA,CAAC;CAEF,MAAM,8BAA8B,cAElC,SAAS,mBAAmB,KAAwB,iBAAiB;EACpE,IAAI,YAAY;EAChB,MAAM,YAAY;EAClB,EAAE,EACJ,CAAC,SAAS,mBAAmB,CAC7B;AAGD,gBAAe,4BAA4B,SAAS,GAAG;EACtD,QAAQ;EACR,cAAc;EACd,CAAC;CAEF,MAAM,iBAAiB,cAAc;EACpC,MAAM,sBAAM,IAAI,KAAqB;AAErC,OAAK,MAAM,SAAS,qBACnB,KAAI,MAAM,KACT,KAAI,IAAI,MAAM,IAAI,MAAM,KAAK;AAI/B,OAAK,MAAM,SAAS,kBACnB,KAAI,MAAM,KACT,KAAI,IAAI,MAAM,IAAI,MAAM,KAAK;AAI/B,SAAO;IACL,CAAC,sBAAsB,kBAAkB,CAAC;CAE7C,MAAM,iBAAiB,aACrB,MAAyB,sBAAyC;AAClE,MAAI,IAAI,WAAW,KAAK,eAAe,SAAS,EAC/C,QAAO;EAGR,MAAM,8BAAc,IAAI,KAAa;EACrC,MAAMC,QAAkB,EAAE;AAE1B,OAAK,MAAM,MAAM,KAAK;GACrB,MAAM,OAAO,eAAe,IAAI,GAAG;AACnC,OAAI,CAAC,QAAQ,YAAY,IAAI,KAAK,CACjC;AAGD,eAAY,IAAI,KAAK;AACrB,SAAM,KAAK,KAAK;;AAGjB,MAAI,MAAM,WAAW,EACpB,QAAO;AAGR,SAAO,OAAO,OAAO,MAAM;IAE5B,CAAC,eAAe,CAChB;AAED,QACC,oBAACC;EACA,YAAY;EACZ,WAAW,GACV,+BACA,qBACA,iBACA,UACA;EACD,IAAG;EACH,OAAO;YAEP,qBAAC;GAA8B,WAAU;cACvC,SAAS,gBAAgB,MAAM,KAAK,MAAM,UAAU;AACpD,QAAI,KAAK,SAAS,kBAAkB;KAEnC,MAAM,YAAY,iBAAiB,KAAK,KAAK;AAG7C,SAAI,CAAC,UACJ,QAAO;AAGR,YACC,oBAAC;MACmB;MACG;MACtB,WAAW,KAAK,KAAK;MACrB,OAAO;QACF,KAAK,KAAK,MAAM,kBAAkB,KAAK,KAAK,YAChD;;AAIJ,QAAI,KAAK,SAAS,iBAAiB;KAClC,MAAM,WAAW,KAAK,QAAQ,KAAK,KAAK,QAAQ,KAAK,KAAK;KAC1D,MAAM,iBAAiB,WAAW,QAAQ,YAAY;AAEtD,SAAI,CAAC,eACJ,QAAO;KAGR,MAAM,gBAAgB,eAAe;KAErC,MAAM,UACL,KAAK,KAAK,MAAM,GAAG,SAAS,GAAG,KAAK,KAAK,UAAU,GAAG;AAEvD,YACC,oBAAC;MACgB;MAChB,MAAM,KAAK;QACN,QACJ;;IAOJ,MAAM,YADL,UAAU,SAAS,gCAEG,KAAK,gBACxB,SAAS,gBAAgB,iBAAiB,KAAK,cAAc,GAC7D;IACJ,MAAM,cACL,UAAU,SAAS,IAChB,eAAe,UAAU,GACzB;IAGJ,MAAM,WACL,KAAK,iBACL,KAAK,QAAQ,IAAI,MACjB,SAAS,KAAK,QAAQ,IAAI,aAAa;AAExC,WACC,oBAAC;KACmB;KACG;KACJ;KAClB,OAAO,KAAK,SAAS,EAAE;KAEZ;KACE;OAFR,SAGJ;KAEF,EACF,oBAAC;IAAI,WAAU;cACb,4BAA4B,SAAS,IACrC,oBAAC;KACmB;KACG;KACtB,WAAU;KACV,cAAc;MACb,GACC;KACC;IACyB;GACD"}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { cn } from "../utils/index.js";
|
|
2
2
|
import { useSupportConfig } from "../store/support-store.js";
|
|
3
|
-
import { CoButton } from "./button.js";
|
|
4
3
|
import icons_default from "./icons.js";
|
|
4
|
+
import { CoButton } from "./button.js";
|
|
5
5
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
6
6
|
|
|
7
7
|
//#region src/support/components/header.tsx
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { TimelinePartImage } from "../../timeline-item.js";
|
|
2
|
+
import * as React$1 from "react";
|
|
3
|
+
|
|
4
|
+
//#region src/support/components/image-lightbox.d.ts
|
|
5
|
+
type ImageLightboxProps = {
|
|
6
|
+
/**
|
|
7
|
+
* Array of images to display in the lightbox.
|
|
8
|
+
*/
|
|
9
|
+
images: TimelinePartImage[];
|
|
10
|
+
/**
|
|
11
|
+
* Index of the initially selected image.
|
|
12
|
+
*/
|
|
13
|
+
initialIndex?: number;
|
|
14
|
+
/**
|
|
15
|
+
* Whether the lightbox is open.
|
|
16
|
+
*/
|
|
17
|
+
isOpen: boolean;
|
|
18
|
+
/**
|
|
19
|
+
* Callback when the lightbox should close.
|
|
20
|
+
*/
|
|
21
|
+
onClose: () => void;
|
|
22
|
+
/**
|
|
23
|
+
* Optional className for the overlay.
|
|
24
|
+
*/
|
|
25
|
+
className?: string;
|
|
26
|
+
};
|
|
27
|
+
/**
|
|
28
|
+
* Simple image lightbox/modal for viewing full-size images.
|
|
29
|
+
* Supports keyboard navigation (Escape to close, Arrow keys to navigate).
|
|
30
|
+
*/
|
|
31
|
+
declare function ImageLightbox({
|
|
32
|
+
images,
|
|
33
|
+
initialIndex,
|
|
34
|
+
isOpen,
|
|
35
|
+
onClose,
|
|
36
|
+
className
|
|
37
|
+
}: ImageLightboxProps): React$1.ReactElement | null;
|
|
38
|
+
/**
|
|
39
|
+
* Hook to manage lightbox state.
|
|
40
|
+
*/
|
|
41
|
+
declare function useLightbox(): {
|
|
42
|
+
isOpen: boolean;
|
|
43
|
+
selectedIndex: number;
|
|
44
|
+
openLightbox: (index?: number) => void;
|
|
45
|
+
closeLightbox: () => void;
|
|
46
|
+
};
|
|
47
|
+
//#endregion
|
|
48
|
+
export { ImageLightbox, ImageLightboxProps, useLightbox };
|
|
49
|
+
//# sourceMappingURL=image-lightbox.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"image-lightbox.d.ts","names":[],"sources":["../../../src/support/components/image-lightbox.tsx"],"sourcesContent":[],"mappings":";;;;KASY,kBAAA;;AAAZ;AA2BA;EACC,MAAA,EAxBQ,iBAwBR,EAAA;EACA;;;EAGA,YAAA,CAAA,EAAA,MAAA;EACE;;;EAuJa,MAAA,EAAA,OAAW;;;;;;;;;;;;;;iBA7JX,aAAA;;;;;;GAMb,qBAAqB,OAAA,CAAM;;;;iBAuJd,WAAA,CAAA"}
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
import { cn } from "../utils/index.js";
|
|
5
|
+
import icons_default from "./icons.js";
|
|
6
|
+
import { useCallback, useEffect, useState } from "react";
|
|
7
|
+
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
8
|
+
import { createPortal } from "react-dom";
|
|
9
|
+
|
|
10
|
+
//#region src/support/components/image-lightbox.tsx
|
|
11
|
+
/**
|
|
12
|
+
* Simple image lightbox/modal for viewing full-size images.
|
|
13
|
+
* Supports keyboard navigation (Escape to close, Arrow keys to navigate).
|
|
14
|
+
*/
|
|
15
|
+
function ImageLightbox({ images, initialIndex = 0, isOpen, onClose, className }) {
|
|
16
|
+
const [currentIndex, setCurrentIndex] = useState(initialIndex);
|
|
17
|
+
const [mounted, setMounted] = useState(false);
|
|
18
|
+
useEffect(() => {
|
|
19
|
+
setMounted(true);
|
|
20
|
+
}, []);
|
|
21
|
+
useEffect(() => {
|
|
22
|
+
if (isOpen) setCurrentIndex(initialIndex);
|
|
23
|
+
}, [isOpen, initialIndex]);
|
|
24
|
+
const handleKeyDown = useCallback((event) => {
|
|
25
|
+
if (!isOpen) return;
|
|
26
|
+
switch (event.key) {
|
|
27
|
+
case "Escape":
|
|
28
|
+
onClose();
|
|
29
|
+
break;
|
|
30
|
+
case "ArrowLeft":
|
|
31
|
+
setCurrentIndex((prev) => prev > 0 ? prev - 1 : images.length - 1);
|
|
32
|
+
break;
|
|
33
|
+
case "ArrowRight":
|
|
34
|
+
setCurrentIndex((prev) => prev < images.length - 1 ? prev + 1 : 0);
|
|
35
|
+
break;
|
|
36
|
+
default: break;
|
|
37
|
+
}
|
|
38
|
+
}, [
|
|
39
|
+
isOpen,
|
|
40
|
+
images.length,
|
|
41
|
+
onClose
|
|
42
|
+
]);
|
|
43
|
+
useEffect(() => {
|
|
44
|
+
document.addEventListener("keydown", handleKeyDown);
|
|
45
|
+
return () => {
|
|
46
|
+
document.removeEventListener("keydown", handleKeyDown);
|
|
47
|
+
};
|
|
48
|
+
}, [handleKeyDown]);
|
|
49
|
+
useEffect(() => {
|
|
50
|
+
if (isOpen) document.body.style.overflow = "hidden";
|
|
51
|
+
else document.body.style.overflow = "";
|
|
52
|
+
return () => {
|
|
53
|
+
document.body.style.overflow = "";
|
|
54
|
+
};
|
|
55
|
+
}, [isOpen]);
|
|
56
|
+
if (!(mounted && isOpen) || images.length === 0) return null;
|
|
57
|
+
const currentImage = images[currentIndex];
|
|
58
|
+
const hasMultiple = images.length > 1;
|
|
59
|
+
const handlePrevious = () => {
|
|
60
|
+
setCurrentIndex((prev) => prev > 0 ? prev - 1 : images.length - 1);
|
|
61
|
+
};
|
|
62
|
+
const handleNext = () => {
|
|
63
|
+
setCurrentIndex((prev) => prev < images.length - 1 ? prev + 1 : 0);
|
|
64
|
+
};
|
|
65
|
+
const handleBackdropClick = (event) => {
|
|
66
|
+
if (event.target === event.currentTarget) onClose();
|
|
67
|
+
};
|
|
68
|
+
return createPortal(/* @__PURE__ */ jsxs("div", {
|
|
69
|
+
"aria-label": "Image viewer",
|
|
70
|
+
"aria-modal": "true",
|
|
71
|
+
className: cn("fixed inset-0 z-[99999] flex items-center justify-center bg-black/90 p-4", className),
|
|
72
|
+
onClick: handleBackdropClick,
|
|
73
|
+
onKeyDown: (e) => e.key === "Escape" && onClose(),
|
|
74
|
+
role: "dialog",
|
|
75
|
+
children: [
|
|
76
|
+
/* @__PURE__ */ jsx("button", {
|
|
77
|
+
"aria-label": "Close lightbox",
|
|
78
|
+
className: "absolute top-4 right-4 flex h-10 w-10 items-center justify-center rounded-full bg-white/10 text-white transition-colors hover:bg-white/20 focus:outline-none focus:ring-2 focus:ring-white/50",
|
|
79
|
+
onClick: onClose,
|
|
80
|
+
type: "button",
|
|
81
|
+
children: /* @__PURE__ */ jsx(icons_default, {
|
|
82
|
+
className: "h-6 w-6",
|
|
83
|
+
name: "close"
|
|
84
|
+
})
|
|
85
|
+
}),
|
|
86
|
+
hasMultiple && /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx("button", {
|
|
87
|
+
"aria-label": "Previous image",
|
|
88
|
+
className: "absolute left-4 flex h-10 w-10 items-center justify-center rounded-full bg-white/10 text-white transition-colors hover:bg-white/20 focus:outline-none focus:ring-2 focus:ring-white/50",
|
|
89
|
+
onClick: handlePrevious,
|
|
90
|
+
type: "button",
|
|
91
|
+
children: /* @__PURE__ */ jsx(icons_default, {
|
|
92
|
+
className: "h-6 w-6",
|
|
93
|
+
name: "arrow-left"
|
|
94
|
+
})
|
|
95
|
+
}), /* @__PURE__ */ jsx("button", {
|
|
96
|
+
"aria-label": "Next image",
|
|
97
|
+
className: "absolute right-4 flex h-10 w-10 items-center justify-center rounded-full bg-white/10 text-white transition-colors hover:bg-white/20 focus:outline-none focus:ring-2 focus:ring-white/50",
|
|
98
|
+
onClick: handleNext,
|
|
99
|
+
type: "button",
|
|
100
|
+
children: /* @__PURE__ */ jsx(icons_default, {
|
|
101
|
+
className: "h-6 w-6",
|
|
102
|
+
name: "arrow-right"
|
|
103
|
+
})
|
|
104
|
+
})] }),
|
|
105
|
+
/* @__PURE__ */ jsx("img", {
|
|
106
|
+
alt: currentImage?.fileName || `Image ${currentIndex + 1}`,
|
|
107
|
+
className: "max-h-[90vh] max-w-[90vw] object-contain",
|
|
108
|
+
src: currentImage?.url
|
|
109
|
+
}),
|
|
110
|
+
hasMultiple && /* @__PURE__ */ jsxs("div", {
|
|
111
|
+
className: "-translate-x-1/2 absolute bottom-4 left-1/2 rounded-full bg-black/50 px-3 py-1 text-sm text-white",
|
|
112
|
+
children: [
|
|
113
|
+
currentIndex + 1,
|
|
114
|
+
" / ",
|
|
115
|
+
images.length
|
|
116
|
+
]
|
|
117
|
+
})
|
|
118
|
+
]
|
|
119
|
+
}), document.body);
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* Hook to manage lightbox state.
|
|
123
|
+
*/
|
|
124
|
+
function useLightbox() {
|
|
125
|
+
const [isOpen, setIsOpen] = useState(false);
|
|
126
|
+
const [selectedIndex, setSelectedIndex] = useState(0);
|
|
127
|
+
return {
|
|
128
|
+
isOpen,
|
|
129
|
+
selectedIndex,
|
|
130
|
+
openLightbox: useCallback((index = 0) => {
|
|
131
|
+
setSelectedIndex(index);
|
|
132
|
+
setIsOpen(true);
|
|
133
|
+
}, []),
|
|
134
|
+
closeLightbox: useCallback(() => {
|
|
135
|
+
setIsOpen(false);
|
|
136
|
+
}, [])
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
//#endregion
|
|
141
|
+
export { ImageLightbox, useLightbox };
|
|
142
|
+
//# sourceMappingURL=image-lightbox.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"image-lightbox.js","names":["Icon"],"sources":["../../../src/support/components/image-lightbox.tsx"],"sourcesContent":["\"use client\";\n\nimport type { TimelinePartImage } from \"@cossistant/types/api/timeline-item\";\nimport type * as React from \"react\";\nimport { useCallback, useEffect, useState } from \"react\";\nimport { createPortal } from \"react-dom\";\nimport { cn } from \"../utils\";\nimport Icon from \"./icons\";\n\nexport type ImageLightboxProps = {\n\t/**\n\t * Array of images to display in the lightbox.\n\t */\n\timages: TimelinePartImage[];\n\t/**\n\t * Index of the initially selected image.\n\t */\n\tinitialIndex?: number;\n\t/**\n\t * Whether the lightbox is open.\n\t */\n\tisOpen: boolean;\n\t/**\n\t * Callback when the lightbox should close.\n\t */\n\tonClose: () => void;\n\t/**\n\t * Optional className for the overlay.\n\t */\n\tclassName?: string;\n};\n\n/**\n * Simple image lightbox/modal for viewing full-size images.\n * Supports keyboard navigation (Escape to close, Arrow keys to navigate).\n */\nexport function ImageLightbox({\n\timages,\n\tinitialIndex = 0,\n\tisOpen,\n\tonClose,\n\tclassName,\n}: ImageLightboxProps): React.ReactElement | null {\n\tconst [currentIndex, setCurrentIndex] = useState(initialIndex);\n\tconst [mounted, setMounted] = useState(false);\n\n\t// SSR safety: only render portal after component mounts on client\n\tuseEffect(() => {\n\t\tsetMounted(true);\n\t}, []);\n\n\t// Reset index when lightbox opens with new initial index\n\tuseEffect(() => {\n\t\tif (isOpen) {\n\t\t\tsetCurrentIndex(initialIndex);\n\t\t}\n\t}, [isOpen, initialIndex]);\n\n\t// Handle keyboard navigation\n\tconst handleKeyDown = useCallback(\n\t\t(event: KeyboardEvent) => {\n\t\t\tif (!isOpen) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tswitch (event.key) {\n\t\t\t\tcase \"Escape\":\n\t\t\t\t\tonClose();\n\t\t\t\t\tbreak;\n\t\t\t\tcase \"ArrowLeft\":\n\t\t\t\t\tsetCurrentIndex((prev) => (prev > 0 ? prev - 1 : images.length - 1));\n\t\t\t\t\tbreak;\n\t\t\t\tcase \"ArrowRight\":\n\t\t\t\t\tsetCurrentIndex((prev) => (prev < images.length - 1 ? prev + 1 : 0));\n\t\t\t\t\tbreak;\n\t\t\t\tdefault:\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t},\n\t\t[isOpen, images.length, onClose]\n\t);\n\n\tuseEffect(() => {\n\t\tdocument.addEventListener(\"keydown\", handleKeyDown);\n\t\treturn () => {\n\t\t\tdocument.removeEventListener(\"keydown\", handleKeyDown);\n\t\t};\n\t}, [handleKeyDown]);\n\n\t// Prevent body scroll when lightbox is open\n\tuseEffect(() => {\n\t\tif (isOpen) {\n\t\t\tdocument.body.style.overflow = \"hidden\";\n\t\t} else {\n\t\t\tdocument.body.style.overflow = \"\";\n\t\t}\n\t\treturn () => {\n\t\t\tdocument.body.style.overflow = \"\";\n\t\t};\n\t}, [isOpen]);\n\n\t// Don't render until mounted (SSR safety) or if not open/no images\n\tif (!(mounted && isOpen) || images.length === 0) {\n\t\treturn null;\n\t}\n\n\tconst currentImage = images[currentIndex];\n\tconst hasMultiple = images.length > 1;\n\n\tconst handlePrevious = () => {\n\t\tsetCurrentIndex((prev) => (prev > 0 ? prev - 1 : images.length - 1));\n\t};\n\n\tconst handleNext = () => {\n\t\tsetCurrentIndex((prev) => (prev < images.length - 1 ? prev + 1 : 0));\n\t};\n\n\tconst handleBackdropClick = (event: React.MouseEvent) => {\n\t\tif (event.target === event.currentTarget) {\n\t\t\tonClose();\n\t\t}\n\t};\n\n\t// Render via portal to document.body to escape any CSS containing blocks\n\t// (e.g., transforms on widget containers that break position: fixed)\n\treturn createPortal(\n\t\t// biome-ignore lint/a11y/noNoninteractiveElementInteractions: Dialog backdrop needs click handler for closing\n\t\t<div\n\t\t\taria-label=\"Image viewer\"\n\t\t\taria-modal=\"true\"\n\t\t\tclassName={cn(\n\t\t\t\t\"fixed inset-0 z-[99999] flex items-center justify-center bg-black/90 p-4\",\n\t\t\t\tclassName\n\t\t\t)}\n\t\t\tonClick={handleBackdropClick}\n\t\t\tonKeyDown={(e) => e.key === \"Escape\" && onClose()}\n\t\t\trole=\"dialog\"\n\t\t>\n\t\t\t{/* Close button */}\n\t\t\t<button\n\t\t\t\taria-label=\"Close lightbox\"\n\t\t\t\tclassName=\"absolute top-4 right-4 flex h-10 w-10 items-center justify-center rounded-full bg-white/10 text-white transition-colors hover:bg-white/20 focus:outline-none focus:ring-2 focus:ring-white/50\"\n\t\t\t\tonClick={onClose}\n\t\t\t\ttype=\"button\"\n\t\t\t>\n\t\t\t\t<Icon className=\"h-6 w-6\" name=\"close\" />\n\t\t\t</button>\n\n\t\t\t{/* Navigation buttons */}\n\t\t\t{hasMultiple && (\n\t\t\t\t<>\n\t\t\t\t\t<button\n\t\t\t\t\t\taria-label=\"Previous image\"\n\t\t\t\t\t\tclassName=\"absolute left-4 flex h-10 w-10 items-center justify-center rounded-full bg-white/10 text-white transition-colors hover:bg-white/20 focus:outline-none focus:ring-2 focus:ring-white/50\"\n\t\t\t\t\t\tonClick={handlePrevious}\n\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t>\n\t\t\t\t\t\t<Icon className=\"h-6 w-6\" name=\"arrow-left\" />\n\t\t\t\t\t</button>\n\t\t\t\t\t<button\n\t\t\t\t\t\taria-label=\"Next image\"\n\t\t\t\t\t\tclassName=\"absolute right-4 flex h-10 w-10 items-center justify-center rounded-full bg-white/10 text-white transition-colors hover:bg-white/20 focus:outline-none focus:ring-2 focus:ring-white/50\"\n\t\t\t\t\t\tonClick={handleNext}\n\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t>\n\t\t\t\t\t\t<Icon className=\"h-6 w-6\" name=\"arrow-right\" />\n\t\t\t\t\t</button>\n\t\t\t\t</>\n\t\t\t)}\n\n\t\t\t{/* Image */}\n\t\t\t{/* biome-ignore lint/performance/noImgElement: React package, not Next.js specific */}\n\t\t\t{/* biome-ignore lint/nursery/useImageSize: Dynamic image dimensions not known at render time */}\n\t\t\t<img\n\t\t\t\talt={currentImage?.fileName || `Image ${currentIndex + 1}`}\n\t\t\t\tclassName=\"max-h-[90vh] max-w-[90vw] object-contain\"\n\t\t\t\tsrc={currentImage?.url}\n\t\t\t/>\n\n\t\t\t{/* Image counter */}\n\t\t\t{hasMultiple && (\n\t\t\t\t<div className=\"-translate-x-1/2 absolute bottom-4 left-1/2 rounded-full bg-black/50 px-3 py-1 text-sm text-white\">\n\t\t\t\t\t{currentIndex + 1} / {images.length}\n\t\t\t\t</div>\n\t\t\t)}\n\t\t</div>,\n\t\tdocument.body\n\t);\n}\n\n/**\n * Hook to manage lightbox state.\n */\nexport function useLightbox() {\n\tconst [isOpen, setIsOpen] = useState(false);\n\tconst [selectedIndex, setSelectedIndex] = useState(0);\n\n\tconst openLightbox = useCallback((index = 0) => {\n\t\tsetSelectedIndex(index);\n\t\tsetIsOpen(true);\n\t}, []);\n\n\tconst closeLightbox = useCallback(() => {\n\t\tsetIsOpen(false);\n\t}, []);\n\n\treturn {\n\t\tisOpen,\n\t\tselectedIndex,\n\t\topenLightbox,\n\t\tcloseLightbox,\n\t};\n}\n"],"mappings":";;;;;;;;;;;;;;AAoCA,SAAgB,cAAc,EAC7B,QACA,eAAe,GACf,QACA,SACA,aACiD;CACjD,MAAM,CAAC,cAAc,mBAAmB,SAAS,aAAa;CAC9D,MAAM,CAAC,SAAS,cAAc,SAAS,MAAM;AAG7C,iBAAgB;AACf,aAAW,KAAK;IACd,EAAE,CAAC;AAGN,iBAAgB;AACf,MAAI,OACH,iBAAgB,aAAa;IAE5B,CAAC,QAAQ,aAAa,CAAC;CAG1B,MAAM,gBAAgB,aACpB,UAAyB;AACzB,MAAI,CAAC,OACJ;AAGD,UAAQ,MAAM,KAAd;GACC,KAAK;AACJ,aAAS;AACT;GACD,KAAK;AACJ,qBAAiB,SAAU,OAAO,IAAI,OAAO,IAAI,OAAO,SAAS,EAAG;AACpE;GACD,KAAK;AACJ,qBAAiB,SAAU,OAAO,OAAO,SAAS,IAAI,OAAO,IAAI,EAAG;AACpE;GACD,QACC;;IAGH;EAAC;EAAQ,OAAO;EAAQ;EAAQ,CAChC;AAED,iBAAgB;AACf,WAAS,iBAAiB,WAAW,cAAc;AACnD,eAAa;AACZ,YAAS,oBAAoB,WAAW,cAAc;;IAErD,CAAC,cAAc,CAAC;AAGnB,iBAAgB;AACf,MAAI,OACH,UAAS,KAAK,MAAM,WAAW;MAE/B,UAAS,KAAK,MAAM,WAAW;AAEhC,eAAa;AACZ,YAAS,KAAK,MAAM,WAAW;;IAE9B,CAAC,OAAO,CAAC;AAGZ,KAAI,EAAE,WAAW,WAAW,OAAO,WAAW,EAC7C,QAAO;CAGR,MAAM,eAAe,OAAO;CAC5B,MAAM,cAAc,OAAO,SAAS;CAEpC,MAAM,uBAAuB;AAC5B,mBAAiB,SAAU,OAAO,IAAI,OAAO,IAAI,OAAO,SAAS,EAAG;;CAGrE,MAAM,mBAAmB;AACxB,mBAAiB,SAAU,OAAO,OAAO,SAAS,IAAI,OAAO,IAAI,EAAG;;CAGrE,MAAM,uBAAuB,UAA4B;AACxD,MAAI,MAAM,WAAW,MAAM,cAC1B,UAAS;;AAMX,QAAO,aAEN,qBAAC;EACA,cAAW;EACX,cAAW;EACX,WAAW,GACV,4EACA,UACA;EACD,SAAS;EACT,YAAY,MAAM,EAAE,QAAQ,YAAY,SAAS;EACjD,MAAK;;GAGL,oBAAC;IACA,cAAW;IACX,WAAU;IACV,SAAS;IACT,MAAK;cAEL,oBAACA;KAAK,WAAU;KAAU,MAAK;MAAU;KACjC;GAGR,eACA,4CACC,oBAAC;IACA,cAAW;IACX,WAAU;IACV,SAAS;IACT,MAAK;cAEL,oBAACA;KAAK,WAAU;KAAU,MAAK;MAAe;KACtC,EACT,oBAAC;IACA,cAAW;IACX,WAAU;IACV,SAAS;IACT,MAAK;cAEL,oBAACA;KAAK,WAAU;KAAU,MAAK;MAAgB;KACvC,IACP;GAMJ,oBAAC;IACA,KAAK,cAAc,YAAY,SAAS,eAAe;IACvD,WAAU;IACV,KAAK,cAAc;KAClB;GAGD,eACA,qBAAC;IAAI,WAAU;;KACb,eAAe;KAAE;KAAI,OAAO;;KACxB;;GAEF,EACN,SAAS,KACT;;;;;AAMF,SAAgB,cAAc;CAC7B,MAAM,CAAC,QAAQ,aAAa,SAAS,MAAM;CAC3C,MAAM,CAAC,eAAe,oBAAoB,SAAS,EAAE;AAWrD,QAAO;EACN;EACA;EACA,cAZoB,aAAa,QAAQ,MAAM;AAC/C,oBAAiB,MAAM;AACvB,aAAU,KAAK;KACb,EAAE,CAAC;EAUL,eARqB,kBAAkB;AACvC,aAAU,MAAM;KACd,EAAE,CAAC;EAOL"}
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import { TypingIndicator, TypingIndicatorProps, TypingParticipant, TypingParticipantType } from "./typing-indicator.js";
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
2
|
+
import { ContentProps } from "../types.js";
|
|
3
|
+
import { Content } from "./content.js";
|
|
4
4
|
import { ConversationEvent, ConversationEventProps } from "./conversation-event.js";
|
|
5
5
|
import { ConversationTimelineList, ConversationTimelineProps } from "./conversation-timeline.js";
|
|
6
6
|
import { Icon, IconName, IconProps, IconVariant } from "./icons.js";
|
|
7
7
|
import { SendButton } from "./multimodal-input.js";
|
|
8
|
-
import {
|
|
8
|
+
import { Root, RootProps } from "./root.js";
|
|
9
9
|
import { TimelineMessageGroup, TimelineMessageGroupProps } from "./timeline-message-group.js";
|
|
10
10
|
import { TimelineMessageItem, TimelineMessageItemProps } from "./timeline-message-item.js";
|
|
11
|
-
|
|
11
|
+
import { DefaultTrigger, DefaultTriggerProps } from "./trigger.js";
|
|
12
|
+
export { Content, type ContentProps, ConversationEvent, type ConversationEventProps, ConversationTimelineList, type ConversationTimelineProps, DefaultTrigger, type DefaultTriggerProps, Icon, type IconName, type IconProps, type IconVariant, Root, type RootProps, SendButton, TimelineMessageGroup, type TimelineMessageGroupProps, TimelineMessageItem, type TimelineMessageItemProps, TypingIndicator, type TypingIndicatorProps, type TypingParticipant, type TypingParticipantType };
|