@usereq/widget 0.2.9 → 0.2.11
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/dist/widget.js +137 -3530
- package/package.json +2 -2
- package/src/chat-widget/chat-widget-defaults.tsx +1 -40
- package/src/chat-widget/message-reveal.ts +52 -0
- package/src/custom-element/agent-widget-element.tsx +18 -2
- package/src/styles/widget.css.ts +66 -10
- package/dist/widget.css +0 -2
- package/src/chat-widget/components/chat-widget-frame.tsx +0 -56
- package/src/chat-widget/components/chat-widget-layout.ts +0 -6
- package/src/chat-widget/components/chat-widget-primitives.tsx +0 -739
- package/src/chat-widget/components/confirmation.tsx +0 -155
- package/src/chat-widget/components/conversation.tsx +0 -110
- package/src/chat-widget/components/message.tsx +0 -325
- package/src/chat-widget/index.ts +0 -19
- package/src/chat-widget/styles/chat-widget-box.tsx +0 -258
- package/src/chat-widget/styles/chat-widget-bubble.tsx +0 -238
- package/src/chat-widget/styles/chat-widget-chatbar.tsx +0 -248
- package/src/chat-widget/styles/chat-widget-messenger.tsx +0 -573
|
@@ -1,258 +0,0 @@
|
|
|
1
|
-
"use client";
|
|
2
|
-
|
|
3
|
-
import * as React from "react";
|
|
4
|
-
import { motion } from "motion/react";
|
|
5
|
-
import { cn } from "@usereq/ui/lib/utils";
|
|
6
|
-
import {
|
|
7
|
-
Avatar,
|
|
8
|
-
AvatarFallback,
|
|
9
|
-
AvatarImage,
|
|
10
|
-
} from "@usereq/ui/components/avatar";
|
|
11
|
-
import { Button } from "@usereq/ui/components/button";
|
|
12
|
-
import { ChevronDown } from "lucide-react";
|
|
13
|
-
import {
|
|
14
|
-
getChatWidgetAvatarStyle,
|
|
15
|
-
getChatWidgetGradientStyle,
|
|
16
|
-
} from "../chat-widget-appearance";
|
|
17
|
-
import {
|
|
18
|
-
DEFAULT_AGENT_NAME_BOX,
|
|
19
|
-
DEFAULT_POWERED_BY,
|
|
20
|
-
DEFAULT_SPOTLIGHT_MESSAGES,
|
|
21
|
-
DEFAULT_START_CHAT_LABEL,
|
|
22
|
-
} from "../chat-widget-defaults";
|
|
23
|
-
import {
|
|
24
|
-
ChatWidgetComposer,
|
|
25
|
-
ChatWidgetHeader,
|
|
26
|
-
ChatWidgetMessages,
|
|
27
|
-
ChatWidgetPanel,
|
|
28
|
-
ChatWidgetPoweredBy,
|
|
29
|
-
useIsChatWidgetFullscreenViewport,
|
|
30
|
-
useSpotlightTyping,
|
|
31
|
-
useStableSpotlightMessages,
|
|
32
|
-
type ChatWidgetDataProps,
|
|
33
|
-
} from "../components/chat-widget-primitives";
|
|
34
|
-
|
|
35
|
-
const PANEL_WIDTH = 360;
|
|
36
|
-
const PANEL_GAP = 12;
|
|
37
|
-
const LAUNCHER_CARD_WIDTH = 280;
|
|
38
|
-
const LAUNCHER_CARD_HEIGHT = 120;
|
|
39
|
-
/** When panel is open, launcher is just the close button (square). */
|
|
40
|
-
const LAUNCHER_BUTTON_SIZE = 56;
|
|
41
|
-
|
|
42
|
-
const panelTransition = {
|
|
43
|
-
type: "spring" as const,
|
|
44
|
-
stiffness: 400,
|
|
45
|
-
damping: 35,
|
|
46
|
-
};
|
|
47
|
-
|
|
48
|
-
/**
|
|
49
|
-
* ChatWidgetBox — closed: intro message + Start chat button; open: same panel as Bubble + chevron to close.
|
|
50
|
-
*/
|
|
51
|
-
export function ChatWidgetBox({
|
|
52
|
-
agentName = DEFAULT_AGENT_NAME_BOX,
|
|
53
|
-
agentAvatarUrl,
|
|
54
|
-
appearance,
|
|
55
|
-
spotlightMessages = [],
|
|
56
|
-
messages = [],
|
|
57
|
-
poweredBy = DEFAULT_POWERED_BY,
|
|
58
|
-
headerStatus = "Online",
|
|
59
|
-
errorMessage,
|
|
60
|
-
status = "idle",
|
|
61
|
-
sendDisabled = false,
|
|
62
|
-
startChatLabel = DEFAULT_START_CHAT_LABEL,
|
|
63
|
-
onSendMessage,
|
|
64
|
-
isTyping,
|
|
65
|
-
onSuggestion,
|
|
66
|
-
onLauncherClick,
|
|
67
|
-
className,
|
|
68
|
-
open,
|
|
69
|
-
defaultOpen = false,
|
|
70
|
-
onOpenChange,
|
|
71
|
-
previewOnly = false,
|
|
72
|
-
onStopConversation,
|
|
73
|
-
stopDisabled,
|
|
74
|
-
onConfirmationDecision,
|
|
75
|
-
onLinkClick,
|
|
76
|
-
emptyAction,
|
|
77
|
-
portalContainer,
|
|
78
|
-
}: ChatWidgetDataProps & {
|
|
79
|
-
/** Button label on closed card */
|
|
80
|
-
startChatLabel?: string;
|
|
81
|
-
open?: boolean;
|
|
82
|
-
className?: string;
|
|
83
|
-
defaultOpen?: boolean;
|
|
84
|
-
onOpenChange?: (open: boolean) => void;
|
|
85
|
-
/** When true, launcher only (no open state); for layout editor. */
|
|
86
|
-
previewOnly?: boolean;
|
|
87
|
-
portalContainer?: React.ComponentProps<
|
|
88
|
-
typeof ChatWidgetPanel
|
|
89
|
-
>["portalContainer"];
|
|
90
|
-
}) {
|
|
91
|
-
const [uncontrolledOpen, setUncontrolledOpen] = React.useState(
|
|
92
|
-
previewOnly ? false : defaultOpen,
|
|
93
|
-
);
|
|
94
|
-
const [draftValue, setDraftValue] = React.useState("");
|
|
95
|
-
const effectiveOpen = previewOnly ? false : (open ?? uncontrolledOpen);
|
|
96
|
-
const handleOpenChange = React.useCallback(
|
|
97
|
-
(nextOpen: boolean) => {
|
|
98
|
-
if (onOpenChange) {
|
|
99
|
-
onOpenChange(nextOpen);
|
|
100
|
-
return;
|
|
101
|
-
}
|
|
102
|
-
setUncontrolledOpen(nextOpen);
|
|
103
|
-
},
|
|
104
|
-
[onOpenChange],
|
|
105
|
-
);
|
|
106
|
-
const stableSpotlightMessages = useStableSpotlightMessages(spotlightMessages);
|
|
107
|
-
const spotlightForTyping =
|
|
108
|
-
stableSpotlightMessages.length > 0
|
|
109
|
-
? stableSpotlightMessages
|
|
110
|
-
: [...DEFAULT_SPOTLIGHT_MESSAGES];
|
|
111
|
-
const introTextTyping = useSpotlightTyping(spotlightForTyping);
|
|
112
|
-
const introText = introTextTyping || spotlightForTyping[0] || DEFAULT_SPOTLIGHT_MESSAGES[0];
|
|
113
|
-
|
|
114
|
-
const launcherHeight = effectiveOpen
|
|
115
|
-
? LAUNCHER_BUTTON_SIZE
|
|
116
|
-
: LAUNCHER_CARD_HEIGHT;
|
|
117
|
-
const rootWidth = effectiveOpen ? LAUNCHER_BUTTON_SIZE : LAUNCHER_CARD_WIDTH;
|
|
118
|
-
const isFullscreenViewport = useIsChatWidgetFullscreenViewport();
|
|
119
|
-
const hideLauncher = isFullscreenViewport && effectiveOpen;
|
|
120
|
-
const closeButtonStyle: React.CSSProperties = {
|
|
121
|
-
width: LAUNCHER_BUTTON_SIZE,
|
|
122
|
-
height: LAUNCHER_BUTTON_SIZE,
|
|
123
|
-
...(appearance ? getChatWidgetGradientStyle(appearance) : {}),
|
|
124
|
-
pointerEvents: effectiveOpen ? "auto" : "none",
|
|
125
|
-
};
|
|
126
|
-
|
|
127
|
-
return (
|
|
128
|
-
<div
|
|
129
|
-
className={cn("relative", className)}
|
|
130
|
-
style={{ width: rootWidth, height: launcherHeight }}
|
|
131
|
-
>
|
|
132
|
-
{/* Chat panel: full-screen on mobile, floating on desktop (hidden when previewOnly) */}
|
|
133
|
-
{!previewOnly && (
|
|
134
|
-
<ChatWidgetPanel
|
|
135
|
-
open={effectiveOpen}
|
|
136
|
-
onOpenChange={handleOpenChange}
|
|
137
|
-
portalContainer={portalContainer}
|
|
138
|
-
desktopClassName="absolute right-0 overflow-hidden rounded-2xl border bg-background shadow-xl"
|
|
139
|
-
desktopStyle={{
|
|
140
|
-
width: PANEL_WIDTH,
|
|
141
|
-
bottom: launcherHeight + PANEL_GAP,
|
|
142
|
-
}}
|
|
143
|
-
>
|
|
144
|
-
<div className="flex shrink-0 border-b p-4">
|
|
145
|
-
<ChatWidgetHeader
|
|
146
|
-
agentName={agentName}
|
|
147
|
-
agentAvatarUrl={agentAvatarUrl}
|
|
148
|
-
appearance={appearance}
|
|
149
|
-
statusText={headerStatus}
|
|
150
|
-
onStopConversation={onStopConversation ?? undefined}
|
|
151
|
-
stopDisabled={stopDisabled}
|
|
152
|
-
onClose={() => handleOpenChange(false)}
|
|
153
|
-
/>
|
|
154
|
-
</div>
|
|
155
|
-
<div className="flex min-h-0 flex-1 flex-col overflow-hidden p-4 md:max-h-90">
|
|
156
|
-
<ChatWidgetMessages
|
|
157
|
-
messages={messages}
|
|
158
|
-
isTyping={isTyping ?? status === "sending"}
|
|
159
|
-
status={status}
|
|
160
|
-
errorMessage={errorMessage}
|
|
161
|
-
appearance={appearance}
|
|
162
|
-
onConfirmationDecision={onConfirmationDecision}
|
|
163
|
-
onLinkClick={onLinkClick}
|
|
164
|
-
onSuggestion={(value: string) => {
|
|
165
|
-
setDraftValue(value);
|
|
166
|
-
onSuggestion?.(value);
|
|
167
|
-
}}
|
|
168
|
-
emptyAction={emptyAction}
|
|
169
|
-
/>
|
|
170
|
-
</div>
|
|
171
|
-
{!(emptyAction && messages.length === 0 && !isTyping) && (
|
|
172
|
-
<div className="border-t p-3 shrink-0">
|
|
173
|
-
<ChatWidgetComposer
|
|
174
|
-
value={draftValue}
|
|
175
|
-
onChange={setDraftValue}
|
|
176
|
-
onSubmit={onSendMessage}
|
|
177
|
-
appearance={appearance}
|
|
178
|
-
disabled={sendDisabled || status === "booting"}
|
|
179
|
-
/>
|
|
180
|
-
<ChatWidgetPoweredBy text={poweredBy} className="mt-2" />
|
|
181
|
-
</div>
|
|
182
|
-
)}
|
|
183
|
-
</ChatWidgetPanel>
|
|
184
|
-
)}
|
|
185
|
-
|
|
186
|
-
{/* Closed: intro message + Start chat button */}
|
|
187
|
-
{!effectiveOpen && (
|
|
188
|
-
<motion.div
|
|
189
|
-
className="absolute bottom-0 right-0 flex flex-col gap-4 rounded-2xl border bg-background p-4 shadow-xl"
|
|
190
|
-
style={{ width: LAUNCHER_CARD_WIDTH, height: LAUNCHER_CARD_HEIGHT }}
|
|
191
|
-
initial={false}
|
|
192
|
-
animate={{ opacity: 1 }}
|
|
193
|
-
>
|
|
194
|
-
<div className="flex flex-1 items-start gap-2">
|
|
195
|
-
<Avatar
|
|
196
|
-
className="size-9 shrink-0"
|
|
197
|
-
style={
|
|
198
|
-
appearance ? getChatWidgetAvatarStyle(appearance) : undefined
|
|
199
|
-
}
|
|
200
|
-
>
|
|
201
|
-
{agentAvatarUrl ? (
|
|
202
|
-
<AvatarImage src={agentAvatarUrl} alt={agentName} />
|
|
203
|
-
) : null}
|
|
204
|
-
<AvatarFallback className="bg-transparent text-white text-xs">
|
|
205
|
-
{agentName.slice(0, 2).toUpperCase()}
|
|
206
|
-
</AvatarFallback>
|
|
207
|
-
</Avatar>
|
|
208
|
-
{introText ? (
|
|
209
|
-
<p className="min-w-0 flex-1 text-sm leading-snug text-foreground">
|
|
210
|
-
{introText}
|
|
211
|
-
</p>
|
|
212
|
-
) : null}
|
|
213
|
-
</div>
|
|
214
|
-
<Button
|
|
215
|
-
className={cn(
|
|
216
|
-
"w-full rounded-full",
|
|
217
|
-
previewOnly && "pointer-events-none",
|
|
218
|
-
)}
|
|
219
|
-
onClick={
|
|
220
|
-
previewOnly
|
|
221
|
-
? undefined
|
|
222
|
-
: () => {
|
|
223
|
-
onLauncherClick?.();
|
|
224
|
-
handleOpenChange(true);
|
|
225
|
-
}
|
|
226
|
-
}
|
|
227
|
-
style={
|
|
228
|
-
appearance ? getChatWidgetGradientStyle(appearance) : undefined
|
|
229
|
-
}
|
|
230
|
-
>
|
|
231
|
-
{appearance?.actionText?.trim() || startChatLabel}
|
|
232
|
-
</Button>
|
|
233
|
-
</motion.div>
|
|
234
|
-
)}
|
|
235
|
-
|
|
236
|
-
{/* Open: square close button (matches launcher width/height when open) */}
|
|
237
|
-
{!previewOnly && !hideLauncher && (
|
|
238
|
-
<motion.button
|
|
239
|
-
type="button"
|
|
240
|
-
onClick={() => handleOpenChange(false)}
|
|
241
|
-
className="absolute bottom-0 right-0 flex items-center justify-center rounded-full text-primary-foreground shadow-lg"
|
|
242
|
-
style={closeButtonStyle as any}
|
|
243
|
-
aria-label="Close chat"
|
|
244
|
-
initial={false}
|
|
245
|
-
animate={{
|
|
246
|
-
opacity: effectiveOpen ? 1 : 0,
|
|
247
|
-
visibility: effectiveOpen ? "visible" : "hidden",
|
|
248
|
-
}}
|
|
249
|
-
transition={panelTransition}
|
|
250
|
-
whileHover={effectiveOpen ? { scale: 1.02 } : undefined}
|
|
251
|
-
whileTap={effectiveOpen ? { scale: 0.98 } : undefined}
|
|
252
|
-
>
|
|
253
|
-
<ChevronDown className="size-6" />
|
|
254
|
-
</motion.button>
|
|
255
|
-
)}
|
|
256
|
-
</div>
|
|
257
|
-
);
|
|
258
|
-
}
|
|
@@ -1,238 +0,0 @@
|
|
|
1
|
-
"use client";
|
|
2
|
-
|
|
3
|
-
import * as React from "react";
|
|
4
|
-
import { AnimatePresence, motion } from "motion/react";
|
|
5
|
-
import { cn } from "@usereq/ui/lib/utils";
|
|
6
|
-
import {
|
|
7
|
-
Avatar,
|
|
8
|
-
AvatarFallback,
|
|
9
|
-
AvatarImage,
|
|
10
|
-
} from "@usereq/ui/components/avatar";
|
|
11
|
-
import { ChevronDown } from "lucide-react";
|
|
12
|
-
import {
|
|
13
|
-
DEFAULT_AGENT_NAME_BUBBLE,
|
|
14
|
-
DEFAULT_POWERED_BY,
|
|
15
|
-
DEFAULT_SPOTLIGHT_MESSAGES,
|
|
16
|
-
} from "../chat-widget-defaults";
|
|
17
|
-
import {
|
|
18
|
-
ChatWidgetComposer,
|
|
19
|
-
ChatWidgetHeader,
|
|
20
|
-
ChatWidgetMessages,
|
|
21
|
-
ChatWidgetPanel,
|
|
22
|
-
ChatWidgetPoweredBy,
|
|
23
|
-
useSpotlightFade,
|
|
24
|
-
useIsChatWidgetFullscreenViewport,
|
|
25
|
-
type ChatWidgetDataProps,
|
|
26
|
-
} from "../components/chat-widget-primitives";
|
|
27
|
-
import {
|
|
28
|
-
getChatWidgetAvatarStyle,
|
|
29
|
-
getChatWidgetGradientStyle,
|
|
30
|
-
} from "../chat-widget-appearance";
|
|
31
|
-
|
|
32
|
-
const PANEL_WIDTH = 360;
|
|
33
|
-
const LAUNCHER_SIZE = 56;
|
|
34
|
-
const SPOTLIGHT_GAP = 8;
|
|
35
|
-
/** Gap between launcher and panel for embed-friendly spacing */
|
|
36
|
-
const PANEL_GAP = 12;
|
|
37
|
-
|
|
38
|
-
const fadeTransition = { duration: 0.4 };
|
|
39
|
-
|
|
40
|
-
export function ChatWidgetBubble({
|
|
41
|
-
agentName = DEFAULT_AGENT_NAME_BUBBLE,
|
|
42
|
-
agentAvatarUrl,
|
|
43
|
-
appearance,
|
|
44
|
-
spotlightMessages = [],
|
|
45
|
-
messages = [],
|
|
46
|
-
poweredBy = DEFAULT_POWERED_BY,
|
|
47
|
-
headerStatus = "Online",
|
|
48
|
-
errorMessage,
|
|
49
|
-
status = "idle",
|
|
50
|
-
sendDisabled = false,
|
|
51
|
-
onSendMessage,
|
|
52
|
-
isTyping,
|
|
53
|
-
onSuggestion,
|
|
54
|
-
onLauncherClick,
|
|
55
|
-
className,
|
|
56
|
-
open,
|
|
57
|
-
defaultOpen = false,
|
|
58
|
-
onOpenChange,
|
|
59
|
-
previewOnly = false,
|
|
60
|
-
onStopConversation,
|
|
61
|
-
stopDisabled,
|
|
62
|
-
onConfirmationDecision,
|
|
63
|
-
onLinkClick,
|
|
64
|
-
emptyAction,
|
|
65
|
-
portalContainer,
|
|
66
|
-
}: ChatWidgetDataProps & {
|
|
67
|
-
open?: boolean;
|
|
68
|
-
className?: string;
|
|
69
|
-
defaultOpen?: boolean;
|
|
70
|
-
onOpenChange?: (open: boolean) => void;
|
|
71
|
-
onLauncherClick?: () => void;
|
|
72
|
-
/** When true, launcher only (no open state); for layout editor. */
|
|
73
|
-
previewOnly?: boolean;
|
|
74
|
-
portalContainer?: React.ComponentProps<
|
|
75
|
-
typeof ChatWidgetPanel
|
|
76
|
-
>["portalContainer"];
|
|
77
|
-
}) {
|
|
78
|
-
const [uncontrolledOpen, setUncontrolledOpen] = React.useState(
|
|
79
|
-
previewOnly ? false : defaultOpen,
|
|
80
|
-
);
|
|
81
|
-
const [draftValue, setDraftValue] = React.useState("");
|
|
82
|
-
const spotlightForTyping =
|
|
83
|
-
spotlightMessages.length > 0
|
|
84
|
-
? spotlightMessages
|
|
85
|
-
: [...DEFAULT_SPOTLIGHT_MESSAGES];
|
|
86
|
-
const spotlightText = useSpotlightFade(spotlightForTyping, 3000);
|
|
87
|
-
const effectiveOpen = previewOnly ? false : (open ?? uncontrolledOpen);
|
|
88
|
-
const handleOpenChange = React.useCallback(
|
|
89
|
-
(nextOpen: boolean) => {
|
|
90
|
-
if (onOpenChange) {
|
|
91
|
-
onOpenChange(nextOpen);
|
|
92
|
-
return;
|
|
93
|
-
}
|
|
94
|
-
setUncontrolledOpen(nextOpen);
|
|
95
|
-
},
|
|
96
|
-
[onOpenChange],
|
|
97
|
-
);
|
|
98
|
-
const isFullscreenViewport = useIsChatWidgetFullscreenViewport();
|
|
99
|
-
const hideLauncher = isFullscreenViewport && effectiveOpen;
|
|
100
|
-
|
|
101
|
-
return (
|
|
102
|
-
<div
|
|
103
|
-
className={cn("relative", className)}
|
|
104
|
-
style={{
|
|
105
|
-
width: spotlightForTyping.length > 0 ? 260 : LAUNCHER_SIZE,
|
|
106
|
-
height:
|
|
107
|
-
spotlightForTyping.length > 0
|
|
108
|
-
? LAUNCHER_SIZE + SPOTLIGHT_GAP + 56
|
|
109
|
-
: LAUNCHER_SIZE,
|
|
110
|
-
}}
|
|
111
|
-
>
|
|
112
|
-
{/* Spotlight: rotating messages above bubble (fade in/out), only when closed */}
|
|
113
|
-
{spotlightForTyping.length > 0 && !effectiveOpen && (
|
|
114
|
-
<AnimatePresence mode="wait">
|
|
115
|
-
<motion.div
|
|
116
|
-
key={spotlightText}
|
|
117
|
-
initial={{ opacity: 0 }}
|
|
118
|
-
animate={{ opacity: 1 }}
|
|
119
|
-
exit={{ opacity: 0 }}
|
|
120
|
-
transition={fadeTransition}
|
|
121
|
-
className="absolute right-0 bottom-[calc(56px+SPOTLIGHT_GAP)] flex w-full max-w-65 flex-col items-end"
|
|
122
|
-
>
|
|
123
|
-
<div className="rounded-2xl border bg-background/95 px-4 py-3 text-right shadow-lg backdrop-blur-sm dark:bg-background/90">
|
|
124
|
-
<p className="text-sm leading-relaxed text-foreground">
|
|
125
|
-
{spotlightText}
|
|
126
|
-
</p>
|
|
127
|
-
</div>
|
|
128
|
-
<div className="flex w-full justify-end pr-6" aria-hidden>
|
|
129
|
-
<span className="border-[6px] border-transparent border-t-background/95 bg-transparent dark:border-t-background/90" />
|
|
130
|
-
</div>
|
|
131
|
-
</motion.div>
|
|
132
|
-
</AnimatePresence>
|
|
133
|
-
)}
|
|
134
|
-
|
|
135
|
-
{/* Chat panel: full-screen on mobile, floating on desktop (hidden when previewOnly) */}
|
|
136
|
-
{!previewOnly && (
|
|
137
|
-
<ChatWidgetPanel
|
|
138
|
-
open={effectiveOpen}
|
|
139
|
-
onOpenChange={handleOpenChange}
|
|
140
|
-
portalContainer={portalContainer}
|
|
141
|
-
desktopClassName="absolute right-0 overflow-hidden rounded-2xl border bg-background shadow-xl"
|
|
142
|
-
desktopStyle={{
|
|
143
|
-
width: PANEL_WIDTH,
|
|
144
|
-
bottom: LAUNCHER_SIZE + PANEL_GAP,
|
|
145
|
-
}}
|
|
146
|
-
>
|
|
147
|
-
<div className="flex shrink-0 border-b p-4">
|
|
148
|
-
<ChatWidgetHeader
|
|
149
|
-
agentName={agentName}
|
|
150
|
-
agentAvatarUrl={agentAvatarUrl}
|
|
151
|
-
appearance={appearance}
|
|
152
|
-
statusText={headerStatus}
|
|
153
|
-
onStopConversation={onStopConversation ?? undefined}
|
|
154
|
-
stopDisabled={stopDisabled}
|
|
155
|
-
onClose={() => handleOpenChange(false)}
|
|
156
|
-
/>
|
|
157
|
-
</div>
|
|
158
|
-
<div className="flex min-h-0 flex-1 flex-col p-4 overflow-hidden">
|
|
159
|
-
<ChatWidgetMessages
|
|
160
|
-
messages={messages}
|
|
161
|
-
isTyping={isTyping ?? status === "sending"}
|
|
162
|
-
status={status}
|
|
163
|
-
suggestions={spotlightForTyping}
|
|
164
|
-
errorMessage={errorMessage}
|
|
165
|
-
appearance={appearance}
|
|
166
|
-
onConfirmationDecision={onConfirmationDecision}
|
|
167
|
-
onLinkClick={onLinkClick}
|
|
168
|
-
onSuggestion={(value: string) => {
|
|
169
|
-
setDraftValue(value);
|
|
170
|
-
onSuggestion?.(value);
|
|
171
|
-
}}
|
|
172
|
-
emptyAction={emptyAction}
|
|
173
|
-
/>
|
|
174
|
-
</div>
|
|
175
|
-
{!(emptyAction && messages.length === 0 && !isTyping) && (
|
|
176
|
-
<div className="shrink-0 border-t p-3">
|
|
177
|
-
<ChatWidgetComposer
|
|
178
|
-
value={draftValue}
|
|
179
|
-
onChange={setDraftValue}
|
|
180
|
-
onSubmit={onSendMessage}
|
|
181
|
-
appearance={appearance}
|
|
182
|
-
disabled={sendDisabled || status === "booting"}
|
|
183
|
-
/>
|
|
184
|
-
<ChatWidgetPoweredBy text={poweredBy} className="mt-2" />
|
|
185
|
-
</div>
|
|
186
|
-
)}
|
|
187
|
-
</ChatWidgetPanel>
|
|
188
|
-
)}
|
|
189
|
-
|
|
190
|
-
{/* Launcher: absolute bottom-right anchor for embed */}
|
|
191
|
-
{!hideLauncher && (
|
|
192
|
-
<motion.button
|
|
193
|
-
type="button"
|
|
194
|
-
onClick={
|
|
195
|
-
previewOnly
|
|
196
|
-
? undefined
|
|
197
|
-
: () => {
|
|
198
|
-
onLauncherClick?.();
|
|
199
|
-
handleOpenChange(!effectiveOpen);
|
|
200
|
-
}
|
|
201
|
-
}
|
|
202
|
-
className={cn(
|
|
203
|
-
"absolute bottom-0 right-0 flex size-14 shrink-0 items-center justify-center rounded-full text-white shadow-lg",
|
|
204
|
-
previewOnly && "pointer-events-none",
|
|
205
|
-
)}
|
|
206
|
-
style={
|
|
207
|
-
appearance
|
|
208
|
-
? (getChatWidgetGradientStyle(appearance) as any)
|
|
209
|
-
: undefined
|
|
210
|
-
}
|
|
211
|
-
aria-label={effectiveOpen ? "Close chat" : "Open chat"}
|
|
212
|
-
aria-expanded={effectiveOpen}
|
|
213
|
-
animate={{ scale: 1 }}
|
|
214
|
-
whileHover={previewOnly ? undefined : { scale: 1.02 }}
|
|
215
|
-
whileTap={previewOnly ? undefined : { scale: 0.98 }}
|
|
216
|
-
>
|
|
217
|
-
{effectiveOpen ? (
|
|
218
|
-
<ChevronDown className="size-6" />
|
|
219
|
-
) : (
|
|
220
|
-
<Avatar
|
|
221
|
-
className="size-12"
|
|
222
|
-
style={
|
|
223
|
-
appearance ? getChatWidgetAvatarStyle(appearance) : undefined
|
|
224
|
-
}
|
|
225
|
-
>
|
|
226
|
-
{agentAvatarUrl ? (
|
|
227
|
-
<AvatarImage src={agentAvatarUrl} alt={agentName} />
|
|
228
|
-
) : null}
|
|
229
|
-
<AvatarFallback className="bg-transparent text-white">
|
|
230
|
-
{agentName.slice(0, 2).toUpperCase()}
|
|
231
|
-
</AvatarFallback>
|
|
232
|
-
</Avatar>
|
|
233
|
-
)}
|
|
234
|
-
</motion.button>
|
|
235
|
-
)}
|
|
236
|
-
</div>
|
|
237
|
-
);
|
|
238
|
-
}
|