@usereq/widget 0.2.9 → 0.2.10
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 +110 -3530
- package/package.json +1 -1
- 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 +39 -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,248 +0,0 @@
|
|
|
1
|
-
"use client";
|
|
2
|
-
|
|
3
|
-
import * as React from "react";
|
|
4
|
-
import { motion } from "motion/react";
|
|
5
|
-
import { ChevronDown, Search, Sparkles } from "lucide-react";
|
|
6
|
-
import { cn } from "@usereq/ui/lib/utils";
|
|
7
|
-
import { getChatWidgetGradientStyle } from "../chat-widget-appearance";
|
|
8
|
-
import {
|
|
9
|
-
DEFAULT_AGENT_NAME_CHATBAR,
|
|
10
|
-
DEFAULT_LAUNCHER_LABEL,
|
|
11
|
-
DEFAULT_POWERED_BY,
|
|
12
|
-
DEFAULT_SPOTLIGHT_MESSAGES,
|
|
13
|
-
} from "../chat-widget-defaults";
|
|
14
|
-
import {
|
|
15
|
-
ChatWidgetComposer,
|
|
16
|
-
ChatWidgetHeader,
|
|
17
|
-
ChatWidgetMessages,
|
|
18
|
-
ChatWidgetPanel,
|
|
19
|
-
ChatWidgetPoweredBy,
|
|
20
|
-
useIsChatWidgetFullscreenViewport,
|
|
21
|
-
useSpotlightTyping,
|
|
22
|
-
useStableSpotlightMessages,
|
|
23
|
-
type ChatWidgetDataProps,
|
|
24
|
-
} from "../components/chat-widget-primitives";
|
|
25
|
-
|
|
26
|
-
const CHATBAR_WIDTH = 560;
|
|
27
|
-
const CHATBAR_MIN_WIDTH = 320;
|
|
28
|
-
const BAR_HEIGHT = 48;
|
|
29
|
-
/** When open, bar shrinks to this size (full rounded chevron) */
|
|
30
|
-
const BAR_OPEN_SIZE = 48;
|
|
31
|
-
/** Same as Bubble/Box for consistent embed spacing */
|
|
32
|
-
const PANEL_GAP = 10;
|
|
33
|
-
|
|
34
|
-
const barTransition = { type: "spring" as const, stiffness: 400, damping: 35 };
|
|
35
|
-
|
|
36
|
-
export function ChatWidgetChatBar({
|
|
37
|
-
agentName = DEFAULT_AGENT_NAME_CHATBAR,
|
|
38
|
-
agentAvatarUrl,
|
|
39
|
-
appearance,
|
|
40
|
-
spotlightMessages = [],
|
|
41
|
-
messages = [],
|
|
42
|
-
poweredBy = DEFAULT_POWERED_BY,
|
|
43
|
-
headerStatus = "Online",
|
|
44
|
-
errorMessage,
|
|
45
|
-
status = "idle",
|
|
46
|
-
sendDisabled = false,
|
|
47
|
-
launcherLabel = DEFAULT_LAUNCHER_LABEL,
|
|
48
|
-
onSendMessage,
|
|
49
|
-
isTyping,
|
|
50
|
-
onSuggestion,
|
|
51
|
-
onLauncherClick,
|
|
52
|
-
className,
|
|
53
|
-
open,
|
|
54
|
-
defaultOpen = false,
|
|
55
|
-
onOpenChange,
|
|
56
|
-
previewOnly = false,
|
|
57
|
-
onStopConversation,
|
|
58
|
-
stopDisabled,
|
|
59
|
-
onConfirmationDecision,
|
|
60
|
-
onLinkClick,
|
|
61
|
-
emptyAction,
|
|
62
|
-
portalContainer,
|
|
63
|
-
}: ChatWidgetDataProps & {
|
|
64
|
-
/** Label on the closed bar pill (e.g. "Ask") */
|
|
65
|
-
launcherLabel?: string;
|
|
66
|
-
open?: boolean;
|
|
67
|
-
className?: string;
|
|
68
|
-
defaultOpen?: boolean;
|
|
69
|
-
onOpenChange?: (open: boolean) => void;
|
|
70
|
-
/** When true, launcher only (no open state); for layout editor. */
|
|
71
|
-
previewOnly?: boolean;
|
|
72
|
-
portalContainer?: React.ComponentProps<
|
|
73
|
-
typeof ChatWidgetPanel
|
|
74
|
-
>["portalContainer"];
|
|
75
|
-
}) {
|
|
76
|
-
const [uncontrolledOpen, setUncontrolledOpen] = React.useState(
|
|
77
|
-
previewOnly ? false : defaultOpen,
|
|
78
|
-
);
|
|
79
|
-
const [draftValue, setDraftValue] = React.useState("");
|
|
80
|
-
const effectiveOpen = previewOnly ? false : (open ?? uncontrolledOpen);
|
|
81
|
-
const isFullscreenViewport = useIsChatWidgetFullscreenViewport();
|
|
82
|
-
const hideLauncher = isFullscreenViewport && effectiveOpen;
|
|
83
|
-
const handleOpenChange = React.useCallback(
|
|
84
|
-
(nextOpen: boolean) => {
|
|
85
|
-
if (onOpenChange) {
|
|
86
|
-
onOpenChange(nextOpen);
|
|
87
|
-
return;
|
|
88
|
-
}
|
|
89
|
-
setUncontrolledOpen(nextOpen);
|
|
90
|
-
},
|
|
91
|
-
[onOpenChange],
|
|
92
|
-
);
|
|
93
|
-
const stableSpotlightMessages = useStableSpotlightMessages(spotlightMessages);
|
|
94
|
-
const spotlightForTyping =
|
|
95
|
-
stableSpotlightMessages.length > 0
|
|
96
|
-
? stableSpotlightMessages
|
|
97
|
-
: [...DEFAULT_SPOTLIGHT_MESSAGES];
|
|
98
|
-
const barPlaceholder =
|
|
99
|
-
useSpotlightTyping(spotlightForTyping) ||
|
|
100
|
-
spotlightForTyping[0] ||
|
|
101
|
-
DEFAULT_SPOTLIGHT_MESSAGES[0];
|
|
102
|
-
const barWrapperRef = React.useRef<HTMLDivElement>(null);
|
|
103
|
-
const [barWidth, setBarWidth] = React.useState(CHATBAR_WIDTH);
|
|
104
|
-
|
|
105
|
-
React.useEffect(() => {
|
|
106
|
-
if (effectiveOpen) return;
|
|
107
|
-
const el = barWrapperRef.current;
|
|
108
|
-
if (!el) return;
|
|
109
|
-
const ro = new ResizeObserver((entries) => {
|
|
110
|
-
const w = entries[0]?.contentRect.width;
|
|
111
|
-
if (typeof w === "number") setBarWidth(w);
|
|
112
|
-
});
|
|
113
|
-
ro.observe(el);
|
|
114
|
-
return () => ro.disconnect();
|
|
115
|
-
}, [effectiveOpen]);
|
|
116
|
-
|
|
117
|
-
return (
|
|
118
|
-
<div
|
|
119
|
-
className={cn("relative w-full max-w-full shrink-0", className)}
|
|
120
|
-
style={{
|
|
121
|
-
maxWidth: CHATBAR_WIDTH,
|
|
122
|
-
minWidth: CHATBAR_MIN_WIDTH,
|
|
123
|
-
minHeight: BAR_HEIGHT,
|
|
124
|
-
}}
|
|
125
|
-
>
|
|
126
|
-
{/* Chat panel: full-screen on mobile, floating on desktop (hidden when previewOnly) */}
|
|
127
|
-
{!previewOnly && (
|
|
128
|
-
<ChatWidgetPanel
|
|
129
|
-
open={effectiveOpen}
|
|
130
|
-
onOpenChange={handleOpenChange}
|
|
131
|
-
portalContainer={portalContainer}
|
|
132
|
-
desktopClassName={cn(
|
|
133
|
-
"absolute left-0 right-0 mx-auto overflow-hidden rounded-2xl border bg-white/90 shadow-lg backdrop-blur-xl dark:bg-background/90",
|
|
134
|
-
)}
|
|
135
|
-
desktopStyle={{
|
|
136
|
-
width: "100%",
|
|
137
|
-
minWidth: CHATBAR_MIN_WIDTH,
|
|
138
|
-
maxWidth: CHATBAR_WIDTH,
|
|
139
|
-
bottom: BAR_HEIGHT + PANEL_GAP,
|
|
140
|
-
}}
|
|
141
|
-
>
|
|
142
|
-
<div className="flex shrink-0 border-b p-4">
|
|
143
|
-
<ChatWidgetHeader
|
|
144
|
-
agentName={agentName}
|
|
145
|
-
agentAvatarUrl={agentAvatarUrl}
|
|
146
|
-
appearance={appearance}
|
|
147
|
-
statusText={headerStatus}
|
|
148
|
-
onStopConversation={onStopConversation ?? undefined}
|
|
149
|
-
stopDisabled={stopDisabled}
|
|
150
|
-
onClose={() => handleOpenChange(false)}
|
|
151
|
-
/>
|
|
152
|
-
</div>
|
|
153
|
-
<div className="flex min-h-0 flex-1 flex-col overflow-hidden p-4 md:p-2 md:max-h-85">
|
|
154
|
-
<ChatWidgetMessages
|
|
155
|
-
messages={messages}
|
|
156
|
-
isTyping={isTyping ?? status === "sending"}
|
|
157
|
-
status={status}
|
|
158
|
-
errorMessage={errorMessage}
|
|
159
|
-
appearance={appearance}
|
|
160
|
-
onConfirmationDecision={onConfirmationDecision}
|
|
161
|
-
onLinkClick={onLinkClick}
|
|
162
|
-
onSuggestion={(value: string) => {
|
|
163
|
-
setDraftValue(value);
|
|
164
|
-
onSuggestion?.(value);
|
|
165
|
-
}}
|
|
166
|
-
emptyAction={emptyAction}
|
|
167
|
-
/>
|
|
168
|
-
</div>
|
|
169
|
-
{!(emptyAction && messages.length === 0 && !isTyping) && (
|
|
170
|
-
<div className="border-t p-3 shrink-0">
|
|
171
|
-
<ChatWidgetComposer
|
|
172
|
-
value={draftValue}
|
|
173
|
-
onChange={setDraftValue}
|
|
174
|
-
onSubmit={onSendMessage}
|
|
175
|
-
appearance={appearance}
|
|
176
|
-
disabled={sendDisabled || status === "booting"}
|
|
177
|
-
/>
|
|
178
|
-
<ChatWidgetPoweredBy text={poweredBy} className="mt-2" />
|
|
179
|
-
</div>
|
|
180
|
-
)}
|
|
181
|
-
</ChatWidgetPanel>
|
|
182
|
-
)}
|
|
183
|
-
|
|
184
|
-
{/* Bar: full width when closed, shrinks to full-rounded chevron when open */}
|
|
185
|
-
{!hideLauncher && (
|
|
186
|
-
<div className="absolute bottom-0 left-0 right-0 z-0 flex w-full justify-center">
|
|
187
|
-
<div
|
|
188
|
-
ref={barWrapperRef}
|
|
189
|
-
className="flex w-full max-w-full justify-center"
|
|
190
|
-
>
|
|
191
|
-
<motion.button
|
|
192
|
-
type="button"
|
|
193
|
-
onClick={
|
|
194
|
-
previewOnly
|
|
195
|
-
? undefined
|
|
196
|
-
: () => {
|
|
197
|
-
if (!effectiveOpen) {
|
|
198
|
-
onLauncherClick?.();
|
|
199
|
-
}
|
|
200
|
-
handleOpenChange(!effectiveOpen);
|
|
201
|
-
}
|
|
202
|
-
}
|
|
203
|
-
className={cn(
|
|
204
|
-
"flex shrink-0 items-center gap-2.5 rounded-full border bg-white/80 px-4 py-3 shadow-lg backdrop-blur-xl dark:bg-white/10",
|
|
205
|
-
effectiveOpen && "justify-center",
|
|
206
|
-
previewOnly && "pointer-events-none",
|
|
207
|
-
"focus:outline-none focus-visible:ring-2 focus-visible:ring-ring",
|
|
208
|
-
)}
|
|
209
|
-
style={{ height: BAR_HEIGHT }}
|
|
210
|
-
initial={false}
|
|
211
|
-
animate={{
|
|
212
|
-
width: effectiveOpen ? BAR_OPEN_SIZE : barWidth,
|
|
213
|
-
borderRadius: 9999,
|
|
214
|
-
}}
|
|
215
|
-
transition={barTransition}
|
|
216
|
-
aria-label={effectiveOpen ? "Close chat" : "Open chat"}
|
|
217
|
-
aria-expanded={effectiveOpen}
|
|
218
|
-
whileHover={previewOnly ? undefined : { scale: 1.02 }}
|
|
219
|
-
whileTap={previewOnly ? undefined : { scale: 0.98 }}
|
|
220
|
-
>
|
|
221
|
-
{effectiveOpen ? (
|
|
222
|
-
<ChevronDown className="size-5 shrink-0 text-foreground" />
|
|
223
|
-
) : (
|
|
224
|
-
<>
|
|
225
|
-
<Search className="size-5 shrink-0 text-muted-foreground" />
|
|
226
|
-
<span className="min-w-0 flex-1 truncate text-left text-sm text-muted-foreground">
|
|
227
|
-
{barPlaceholder}
|
|
228
|
-
</span>
|
|
229
|
-
<div
|
|
230
|
-
className="flex shrink-0 items-center gap-1.5 rounded-full px-2.5 py-1 text-xs font-medium text-white"
|
|
231
|
-
style={
|
|
232
|
-
appearance
|
|
233
|
-
? getChatWidgetGradientStyle(appearance)
|
|
234
|
-
: undefined
|
|
235
|
-
}
|
|
236
|
-
>
|
|
237
|
-
<Sparkles className="size-4" />
|
|
238
|
-
{appearance?.actionText?.trim() || launcherLabel}
|
|
239
|
-
</div>
|
|
240
|
-
</>
|
|
241
|
-
)}
|
|
242
|
-
</motion.button>
|
|
243
|
-
</div>
|
|
244
|
-
</div>
|
|
245
|
-
)}
|
|
246
|
-
</div>
|
|
247
|
-
);
|
|
248
|
-
}
|