@wallavi/widget 1.3.6 → 1.3.7
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/index.d.mts +17 -17
- package/dist/index.d.ts +17 -17
- package/dist/index.js +158 -180
- package/dist/index.mjs +158 -180
- package/package.json +1 -1
package/dist/index.d.mts
CHANGED
|
@@ -81,6 +81,10 @@ interface ChatWidgetProps extends ChatWidgetConfig {
|
|
|
81
81
|
className?: string;
|
|
82
82
|
onClose?: () => void;
|
|
83
83
|
onReset?: () => void;
|
|
84
|
+
/** Called when the user clicks the expand/minimize button in the header. Omit to hide the button. */
|
|
85
|
+
onExpand?: () => void;
|
|
86
|
+
/** Controls which icon the expand button shows (expand vs minimize). */
|
|
87
|
+
expanded?: boolean;
|
|
84
88
|
}
|
|
85
89
|
declare function getContrastColor(hex: string): string;
|
|
86
90
|
declare function formatToolName(name: string): string;
|
|
@@ -88,41 +92,37 @@ declare function formatToolName(name: string): string;
|
|
|
88
92
|
interface BubbleWidgetProps extends ChatWidgetConfig {
|
|
89
93
|
/** Where the bubble is anchored. Default: "bottom-right" */
|
|
90
94
|
position?: "bottom-right" | "bottom-left";
|
|
91
|
-
/**
|
|
95
|
+
/** Chat panel width in px. Default: 360 */
|
|
92
96
|
width?: number;
|
|
93
|
-
/**
|
|
97
|
+
/** Chat panel height in px. Default: 580 */
|
|
94
98
|
height?: number;
|
|
95
|
-
/**
|
|
99
|
+
/** Expanded panel width in px. Default: 640 */
|
|
96
100
|
expandedWidth?: number;
|
|
97
|
-
/**
|
|
101
|
+
/** Expanded panel height. Default: "calc(100vh - 100px)" */
|
|
98
102
|
expandedHeight?: string;
|
|
99
|
-
/** Enable Cmd/Ctrl
|
|
103
|
+
/** Enable Cmd/Ctrl+shortcutKey to toggle. Default: false */
|
|
100
104
|
keyboardShortcut?: boolean;
|
|
101
|
-
/** Key
|
|
105
|
+
/** Key used with Cmd/Ctrl. Default: "k" */
|
|
102
106
|
shortcutKey?: string;
|
|
103
107
|
/** Open the widget automatically on mount. Default: false */
|
|
104
108
|
autoOpen?: boolean;
|
|
105
|
-
/**
|
|
106
|
-
* Image URL to show inside the bubble button.
|
|
107
|
-
* Falls back to the Wallavi logo if not provided.
|
|
108
|
-
*/
|
|
109
|
+
/** URL of the image shown inside the bubble button */
|
|
109
110
|
bubbleIconUrl?: string;
|
|
110
|
-
/** Size of the bubble
|
|
111
|
+
/** Size of the bubble button in px. Default: 52 */
|
|
111
112
|
bubbleSize?: number;
|
|
112
|
-
/** className applied to the chat panel */
|
|
113
|
+
/** Extra className applied to the chat panel wrapper */
|
|
113
114
|
panelClassName?: string;
|
|
114
115
|
/**
|
|
115
|
-
* Auto-fetch
|
|
116
|
-
*
|
|
117
|
-
*
|
|
118
|
-
* Pass `autoConfig={false}` if you are providing all props manually.
|
|
116
|
+
* Auto-fetch agent config from the Wallavi dashboard and apply it as defaults.
|
|
117
|
+
* Colors, images, messages, autoOpen, keyboardShortcut, position — all self-configure.
|
|
118
|
+
* Clients only need to pass `agentId`. Set to `false` to supply all props manually.
|
|
119
119
|
* @default true
|
|
120
120
|
*/
|
|
121
121
|
autoConfig?: boolean;
|
|
122
122
|
}
|
|
123
123
|
declare function BubbleWidget({ position: positionProp, width, height, expandedWidth, expandedHeight, keyboardShortcut: keyboardShortcutProp, shortcutKey, autoOpen: autoOpenProp, bubbleIconUrl: bubbleIconUrlProp, bubbleSize, panelClassName, autoConfig, ...chatProps }: BubbleWidgetProps): react_jsx_runtime.JSX.Element;
|
|
124
124
|
|
|
125
|
-
declare function ChatWidget({ agentId, workspaceId, agentName, displayName, profilePicture, userMessageColor, initialMessages, suggestedMessages, messagePlaceholder, watermark, watermarkLogoUrl, footer, theme, showThinking, regenerateMessage, persist, onNavigate, hideCloseButton, source, userContext, playgroundOverrides, className, onClose, onReset, }: ChatWidgetProps): react_jsx_runtime.JSX.Element;
|
|
125
|
+
declare function ChatWidget({ agentId, workspaceId, agentName, displayName, profilePicture, userMessageColor, initialMessages, suggestedMessages, messagePlaceholder, watermark, watermarkLogoUrl, footer, theme, showThinking, regenerateMessage, persist, onNavigate, hideCloseButton, source, userContext, playgroundOverrides, className, onClose, onReset, onExpand, expanded, }: ChatWidgetProps): react_jsx_runtime.JSX.Element;
|
|
126
126
|
|
|
127
127
|
interface UseChatOptions {
|
|
128
128
|
agentId: string;
|
package/dist/index.d.ts
CHANGED
|
@@ -81,6 +81,10 @@ interface ChatWidgetProps extends ChatWidgetConfig {
|
|
|
81
81
|
className?: string;
|
|
82
82
|
onClose?: () => void;
|
|
83
83
|
onReset?: () => void;
|
|
84
|
+
/** Called when the user clicks the expand/minimize button in the header. Omit to hide the button. */
|
|
85
|
+
onExpand?: () => void;
|
|
86
|
+
/** Controls which icon the expand button shows (expand vs minimize). */
|
|
87
|
+
expanded?: boolean;
|
|
84
88
|
}
|
|
85
89
|
declare function getContrastColor(hex: string): string;
|
|
86
90
|
declare function formatToolName(name: string): string;
|
|
@@ -88,41 +92,37 @@ declare function formatToolName(name: string): string;
|
|
|
88
92
|
interface BubbleWidgetProps extends ChatWidgetConfig {
|
|
89
93
|
/** Where the bubble is anchored. Default: "bottom-right" */
|
|
90
94
|
position?: "bottom-right" | "bottom-left";
|
|
91
|
-
/**
|
|
95
|
+
/** Chat panel width in px. Default: 360 */
|
|
92
96
|
width?: number;
|
|
93
|
-
/**
|
|
97
|
+
/** Chat panel height in px. Default: 580 */
|
|
94
98
|
height?: number;
|
|
95
|
-
/**
|
|
99
|
+
/** Expanded panel width in px. Default: 640 */
|
|
96
100
|
expandedWidth?: number;
|
|
97
|
-
/**
|
|
101
|
+
/** Expanded panel height. Default: "calc(100vh - 100px)" */
|
|
98
102
|
expandedHeight?: string;
|
|
99
|
-
/** Enable Cmd/Ctrl
|
|
103
|
+
/** Enable Cmd/Ctrl+shortcutKey to toggle. Default: false */
|
|
100
104
|
keyboardShortcut?: boolean;
|
|
101
|
-
/** Key
|
|
105
|
+
/** Key used with Cmd/Ctrl. Default: "k" */
|
|
102
106
|
shortcutKey?: string;
|
|
103
107
|
/** Open the widget automatically on mount. Default: false */
|
|
104
108
|
autoOpen?: boolean;
|
|
105
|
-
/**
|
|
106
|
-
* Image URL to show inside the bubble button.
|
|
107
|
-
* Falls back to the Wallavi logo if not provided.
|
|
108
|
-
*/
|
|
109
|
+
/** URL of the image shown inside the bubble button */
|
|
109
110
|
bubbleIconUrl?: string;
|
|
110
|
-
/** Size of the bubble
|
|
111
|
+
/** Size of the bubble button in px. Default: 52 */
|
|
111
112
|
bubbleSize?: number;
|
|
112
|
-
/** className applied to the chat panel */
|
|
113
|
+
/** Extra className applied to the chat panel wrapper */
|
|
113
114
|
panelClassName?: string;
|
|
114
115
|
/**
|
|
115
|
-
* Auto-fetch
|
|
116
|
-
*
|
|
117
|
-
*
|
|
118
|
-
* Pass `autoConfig={false}` if you are providing all props manually.
|
|
116
|
+
* Auto-fetch agent config from the Wallavi dashboard and apply it as defaults.
|
|
117
|
+
* Colors, images, messages, autoOpen, keyboardShortcut, position — all self-configure.
|
|
118
|
+
* Clients only need to pass `agentId`. Set to `false` to supply all props manually.
|
|
119
119
|
* @default true
|
|
120
120
|
*/
|
|
121
121
|
autoConfig?: boolean;
|
|
122
122
|
}
|
|
123
123
|
declare function BubbleWidget({ position: positionProp, width, height, expandedWidth, expandedHeight, keyboardShortcut: keyboardShortcutProp, shortcutKey, autoOpen: autoOpenProp, bubbleIconUrl: bubbleIconUrlProp, bubbleSize, panelClassName, autoConfig, ...chatProps }: BubbleWidgetProps): react_jsx_runtime.JSX.Element;
|
|
124
124
|
|
|
125
|
-
declare function ChatWidget({ agentId, workspaceId, agentName, displayName, profilePicture, userMessageColor, initialMessages, suggestedMessages, messagePlaceholder, watermark, watermarkLogoUrl, footer, theme, showThinking, regenerateMessage, persist, onNavigate, hideCloseButton, source, userContext, playgroundOverrides, className, onClose, onReset, }: ChatWidgetProps): react_jsx_runtime.JSX.Element;
|
|
125
|
+
declare function ChatWidget({ agentId, workspaceId, agentName, displayName, profilePicture, userMessageColor, initialMessages, suggestedMessages, messagePlaceholder, watermark, watermarkLogoUrl, footer, theme, showThinking, regenerateMessage, persist, onNavigate, hideCloseButton, source, userContext, playgroundOverrides, className, onClose, onReset, onExpand, expanded, }: ChatWidgetProps): react_jsx_runtime.JSX.Element;
|
|
126
126
|
|
|
127
127
|
interface UseChatOptions {
|
|
128
128
|
agentId: string;
|
package/dist/index.js
CHANGED
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
var react = require('react');
|
|
4
|
-
var
|
|
4
|
+
var lucideReact = require('lucide-react');
|
|
5
5
|
var clsx = require('clsx');
|
|
6
6
|
var tailwindMerge = require('tailwind-merge');
|
|
7
|
-
var lucideReact = require('lucide-react');
|
|
8
7
|
var AvatarPrimitive = require('@radix-ui/react-avatar');
|
|
9
8
|
var jsxRuntime = require('react/jsx-runtime');
|
|
10
9
|
var ReactMarkdownLib = require('react-markdown');
|
|
@@ -293,6 +292,42 @@ function useChat({
|
|
|
293
292
|
}, [streaming, messages, send]);
|
|
294
293
|
return { messages, input, setInput, streaming, threadId, send, regenerate, reset };
|
|
295
294
|
}
|
|
295
|
+
function DefaultIcon() {
|
|
296
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("svg", { viewBox: "0 0 24 24", fill: "none", style: { width: 26, height: 26 }, children: [
|
|
297
|
+
/* @__PURE__ */ jsxRuntime.jsx("circle", { cx: "12", cy: "12", r: "12", fill: "currentColor", opacity: 0.12 }),
|
|
298
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
299
|
+
"path",
|
|
300
|
+
{
|
|
301
|
+
d: "M4 8.5C4 6.57 5.57 5 7.5 5h9C18.43 5 20 6.57 20 8.5v5c0 1.93-1.57 3.5-3.5 3.5H13l-3 2.5V17H7.5C5.57 17 4 15.43 4 13.5v-5Z",
|
|
302
|
+
fill: "currentColor"
|
|
303
|
+
}
|
|
304
|
+
)
|
|
305
|
+
] });
|
|
306
|
+
}
|
|
307
|
+
function ExpandIcon() {
|
|
308
|
+
return /* @__PURE__ */ jsxRuntime.jsx("svg", { viewBox: "0 0 24 24", fill: "none", style: { width: 11, height: 11 }, children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
309
|
+
"path",
|
|
310
|
+
{
|
|
311
|
+
d: "M15 3h6v6M9 21H3v-6M21 3l-7 7M3 21l7-7",
|
|
312
|
+
stroke: "currentColor",
|
|
313
|
+
strokeWidth: 2,
|
|
314
|
+
strokeLinecap: "round",
|
|
315
|
+
strokeLinejoin: "round"
|
|
316
|
+
}
|
|
317
|
+
) });
|
|
318
|
+
}
|
|
319
|
+
function MinimizeIcon() {
|
|
320
|
+
return /* @__PURE__ */ jsxRuntime.jsx("svg", { viewBox: "0 0 24 24", fill: "none", style: { width: 11, height: 11 }, children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
321
|
+
"path",
|
|
322
|
+
{
|
|
323
|
+
d: "M4 14h6v6M20 10h-6V4M14 10l7-7M3 21l7-7",
|
|
324
|
+
stroke: "currentColor",
|
|
325
|
+
strokeWidth: 2,
|
|
326
|
+
strokeLinecap: "round",
|
|
327
|
+
strokeLinejoin: "round"
|
|
328
|
+
}
|
|
329
|
+
) });
|
|
330
|
+
}
|
|
296
331
|
var Avatar = ({ style, ...p }) => /* @__PURE__ */ jsxRuntime.jsx(AvatarPrimitive__namespace.Root, { style: { position: "relative", display: "flex", flexShrink: 0, overflow: "hidden", borderRadius: "9999px", ...style }, ...p });
|
|
297
332
|
var AvatarImage = ({ style, ...p }) => /* @__PURE__ */ jsxRuntime.jsx(AvatarPrimitive__namespace.Image, { style: { position: "absolute", inset: 0, width: "100%", height: "100%", objectFit: "cover", ...style }, ...p });
|
|
298
333
|
var AvatarFallback = ({ style, ...p }) => /* @__PURE__ */ jsxRuntime.jsx(AvatarPrimitive__namespace.Fallback, { style: { display: "flex", width: "100%", height: "100%", alignItems: "center", justifyContent: "center", borderRadius: "9999px", ...style }, ...p });
|
|
@@ -302,7 +337,9 @@ function ChatHeader({
|
|
|
302
337
|
headerBg,
|
|
303
338
|
headerText,
|
|
304
339
|
onReset,
|
|
305
|
-
onClose
|
|
340
|
+
onClose,
|
|
341
|
+
onExpand,
|
|
342
|
+
expanded
|
|
306
343
|
}) {
|
|
307
344
|
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
308
345
|
"header",
|
|
@@ -324,6 +361,15 @@ function ChatHeader({
|
|
|
324
361
|
] })
|
|
325
362
|
] }),
|
|
326
363
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-1", children: [
|
|
364
|
+
onExpand && /* @__PURE__ */ jsxRuntime.jsx(
|
|
365
|
+
"button",
|
|
366
|
+
{
|
|
367
|
+
onClick: onExpand,
|
|
368
|
+
className: "rounded-full p-1.5 transition-colors hover:bg-white/10",
|
|
369
|
+
title: expanded ? "Minimize" : "Expand",
|
|
370
|
+
children: expanded ? /* @__PURE__ */ jsxRuntime.jsx(MinimizeIcon, {}) : /* @__PURE__ */ jsxRuntime.jsx(ExpandIcon, {})
|
|
371
|
+
}
|
|
372
|
+
),
|
|
327
373
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
328
374
|
"button",
|
|
329
375
|
{
|
|
@@ -629,7 +675,9 @@ function ChatWidget({
|
|
|
629
675
|
playgroundOverrides,
|
|
630
676
|
className,
|
|
631
677
|
onClose,
|
|
632
|
-
onReset
|
|
678
|
+
onReset,
|
|
679
|
+
onExpand,
|
|
680
|
+
expanded
|
|
633
681
|
}) {
|
|
634
682
|
const chat = useChat({ agentId, workspaceId, source, userContext, persist, onNavigate, playgroundOverrides });
|
|
635
683
|
const canRegenerate = regenerateMessage && chat.messages.length > 0 && chat.messages.at(-1)?.role === "assistant" && !chat.streaming;
|
|
@@ -657,7 +705,9 @@ function ChatWidget({
|
|
|
657
705
|
headerBg,
|
|
658
706
|
headerText,
|
|
659
707
|
onReset: handleReset,
|
|
660
|
-
onClose: hideCloseButton ? void 0 : onClose
|
|
708
|
+
onClose: hideCloseButton ? void 0 : onClose,
|
|
709
|
+
onExpand,
|
|
710
|
+
expanded
|
|
661
711
|
}
|
|
662
712
|
),
|
|
663
713
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
@@ -710,29 +760,63 @@ function ChatWidget({
|
|
|
710
760
|
}
|
|
711
761
|
);
|
|
712
762
|
}
|
|
713
|
-
var cn4 = (...inputs) => tailwindMerge.twMerge(clsx.clsx(inputs));
|
|
714
763
|
var WALLAVI_PUBLIC_API = "https://wallavi-production.up.railway.app";
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
}
|
|
733
|
-
|
|
734
|
-
|
|
764
|
+
var EMPTY = {
|
|
765
|
+
remoteConfig: {},
|
|
766
|
+
bubbleIconUrl: void 0,
|
|
767
|
+
autoOpen: false,
|
|
768
|
+
keyboardShortcut: false,
|
|
769
|
+
position: "bottom-right",
|
|
770
|
+
loading: false
|
|
771
|
+
};
|
|
772
|
+
function useAutoConfig(agentId, enabled) {
|
|
773
|
+
const [result, setResult] = react.useState(() => ({
|
|
774
|
+
...EMPTY,
|
|
775
|
+
loading: enabled && Boolean(agentId)
|
|
776
|
+
}));
|
|
777
|
+
react.useEffect(() => {
|
|
778
|
+
if (!enabled || !agentId) {
|
|
779
|
+
setResult(EMPTY);
|
|
780
|
+
return;
|
|
781
|
+
}
|
|
782
|
+
let cancelled = false;
|
|
783
|
+
fetch(`${WALLAVI_PUBLIC_API}/api/public/widget/${agentId}`).then((r) => r.json()).then((body) => {
|
|
784
|
+
if (cancelled) return;
|
|
785
|
+
const cfg = body?.data ?? {};
|
|
786
|
+
const remote = {};
|
|
787
|
+
if (cfg.profilePicture != null) remote.profilePicture = cfg.profilePicture;
|
|
788
|
+
if (cfg.displayName != null) remote.displayName = cfg.displayName;
|
|
789
|
+
if (cfg.theme) remote.theme = cfg.theme;
|
|
790
|
+
if (cfg.userMessageColor) remote.userMessageColor = cfg.userMessageColor;
|
|
791
|
+
if (Array.isArray(cfg.initialMessages) && cfg.initialMessages.length > 0)
|
|
792
|
+
remote.initialMessages = cfg.initialMessages;
|
|
793
|
+
if (Array.isArray(cfg.suggestedMessages))
|
|
794
|
+
remote.suggestedMessages = cfg.suggestedMessages;
|
|
795
|
+
if (cfg.messagePlaceholder != null) remote.messagePlaceholder = cfg.messagePlaceholder;
|
|
796
|
+
if (cfg.watermark != null) remote.watermark = cfg.watermark;
|
|
797
|
+
if (cfg.footer != null) remote.footer = cfg.footer;
|
|
798
|
+
if (cfg.showThinking != null) remote.showThinking = cfg.showThinking;
|
|
799
|
+
if (cfg.regenerateMessage != null) remote.regenerateMessage = cfg.regenerateMessage;
|
|
800
|
+
setResult({
|
|
801
|
+
remoteConfig: remote,
|
|
802
|
+
bubbleIconUrl: cfg.chatIcon || cfg.profilePicture || void 0,
|
|
803
|
+
autoOpen: Boolean(cfg.autoOpen),
|
|
804
|
+
keyboardShortcut: Boolean(cfg.keyboardShortcut),
|
|
805
|
+
position: cfg.alignChatBubbleButton === "left" ? "bottom-left" : "bottom-right",
|
|
806
|
+
loading: false
|
|
807
|
+
});
|
|
808
|
+
}).catch(() => {
|
|
809
|
+
if (!cancelled) setResult((r) => ({ ...r, loading: false }));
|
|
810
|
+
});
|
|
811
|
+
return () => {
|
|
812
|
+
cancelled = true;
|
|
813
|
+
};
|
|
814
|
+
}, [agentId, enabled]);
|
|
815
|
+
return result;
|
|
735
816
|
}
|
|
817
|
+
var cn4 = (...inputs) => tailwindMerge.twMerge(clsx.clsx(inputs));
|
|
818
|
+
var KEY_EXPANDED = "wallavi_bubble_expanded";
|
|
819
|
+
var KEY_DISMISSED = "wallavi_bubble_dismissed";
|
|
736
820
|
function BubbleWidget({
|
|
737
821
|
position: positionProp,
|
|
738
822
|
width = 360,
|
|
@@ -746,21 +830,27 @@ function BubbleWidget({
|
|
|
746
830
|
bubbleSize = 52,
|
|
747
831
|
panelClassName,
|
|
748
832
|
autoConfig = true,
|
|
749
|
-
// ChatWidgetConfig props
|
|
750
833
|
...chatProps
|
|
751
834
|
}) {
|
|
752
835
|
const [open, setOpen] = react.useState(false);
|
|
753
|
-
const [expanded, setExpanded] = react.useState(
|
|
754
|
-
() => typeof window !== "undefined" && localStorage.getItem("wallavi_bubble_expanded") === "true"
|
|
755
|
-
);
|
|
756
|
-
const [controlsPos, setControlsPos] = react.useState(null);
|
|
836
|
+
const [expanded, setExpanded] = react.useState(false);
|
|
757
837
|
const panelRef = react.useRef(null);
|
|
758
838
|
const autoOpenedRef = react.useRef(false);
|
|
759
|
-
|
|
839
|
+
react.useEffect(() => {
|
|
840
|
+
if (localStorage.getItem(KEY_EXPANDED) === "true") setExpanded(true);
|
|
841
|
+
}, []);
|
|
842
|
+
const remote = useAutoConfig(chatProps.agentId, autoConfig);
|
|
760
843
|
const [resolvedBubbleIcon, setResolvedBubbleIcon] = react.useState(bubbleIconUrlProp);
|
|
761
844
|
const [resolvedAutoOpen, setResolvedAutoOpen] = react.useState(autoOpenProp);
|
|
762
845
|
const [resolvedKeyboardShortcut, setResolvedKeyboardShortcut] = react.useState(keyboardShortcutProp);
|
|
763
846
|
const [resolvedPosition, setResolvedPosition] = react.useState(positionProp ?? "bottom-right");
|
|
847
|
+
react.useEffect(() => {
|
|
848
|
+
if (remote.loading) return;
|
|
849
|
+
if (!bubbleIconUrlProp) setResolvedBubbleIcon(remote.bubbleIconUrl);
|
|
850
|
+
if (!autoOpenProp) setResolvedAutoOpen(remote.autoOpen);
|
|
851
|
+
if (!keyboardShortcutProp) setResolvedKeyboardShortcut(remote.keyboardShortcut);
|
|
852
|
+
if (!positionProp) setResolvedPosition(remote.position);
|
|
853
|
+
}, [remote.loading]);
|
|
764
854
|
react.useEffect(() => {
|
|
765
855
|
if (autoOpenProp) setResolvedAutoOpen(true);
|
|
766
856
|
}, [autoOpenProp]);
|
|
@@ -773,38 +863,18 @@ function BubbleWidget({
|
|
|
773
863
|
react.useEffect(() => {
|
|
774
864
|
if (positionProp) setResolvedPosition(positionProp);
|
|
775
865
|
}, [positionProp]);
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
if (Array.isArray(cfg.initialMessages) && cfg.initialMessages.length > 0) remote.initialMessages = cfg.initialMessages;
|
|
786
|
-
if (Array.isArray(cfg.suggestedMessages)) remote.suggestedMessages = cfg.suggestedMessages;
|
|
787
|
-
if (cfg.messagePlaceholder != null) remote.messagePlaceholder = cfg.messagePlaceholder;
|
|
788
|
-
if (cfg.watermark != null) remote.watermark = cfg.watermark;
|
|
789
|
-
if (cfg.footer != null) remote.footer = cfg.footer;
|
|
790
|
-
if (cfg.showThinking != null) remote.showThinking = cfg.showThinking;
|
|
791
|
-
if (cfg.regenerateMessage != null) remote.regenerateMessage = cfg.regenerateMessage;
|
|
792
|
-
setRemoteConfig(remote);
|
|
793
|
-
if (!bubbleIconUrlProp) {
|
|
794
|
-
const icon = cfg.chatIcon || cfg.profilePicture;
|
|
795
|
-
if (icon) setResolvedBubbleIcon(icon);
|
|
796
|
-
}
|
|
797
|
-
if (!autoOpenProp && cfg.autoOpen) setResolvedAutoOpen(true);
|
|
798
|
-
if (!keyboardShortcutProp && cfg.keyboardShortcut) setResolvedKeyboardShortcut(true);
|
|
799
|
-
if (!positionProp && cfg.alignChatBubbleButton) {
|
|
800
|
-
setResolvedPosition(cfg.alignChatBubbleButton === "left" ? "bottom-left" : "bottom-right");
|
|
801
|
-
}
|
|
802
|
-
}).catch(() => {
|
|
803
|
-
});
|
|
804
|
-
}, [autoConfig, chatProps.agentId]);
|
|
866
|
+
const definedChatProps = Object.fromEntries(
|
|
867
|
+
Object.entries(chatProps).filter(([, v]) => v !== void 0)
|
|
868
|
+
);
|
|
869
|
+
const mergedConfig = {
|
|
870
|
+
...remote.remoteConfig,
|
|
871
|
+
...definedChatProps,
|
|
872
|
+
agentId: chatProps.agentId,
|
|
873
|
+
agentName: chatProps.agentName
|
|
874
|
+
};
|
|
805
875
|
react.useEffect(() => {
|
|
806
876
|
if (!resolvedAutoOpen || autoOpenedRef.current) return;
|
|
807
|
-
const dismissedUntil = Number(localStorage.getItem(
|
|
877
|
+
const dismissedUntil = Number(localStorage.getItem(KEY_DISMISSED) ?? 0);
|
|
808
878
|
if (dismissedUntil < Date.now()) {
|
|
809
879
|
autoOpenedRef.current = true;
|
|
810
880
|
setOpen(true);
|
|
@@ -812,63 +882,35 @@ function BubbleWidget({
|
|
|
812
882
|
}, [resolvedAutoOpen]);
|
|
813
883
|
react.useEffect(() => {
|
|
814
884
|
if (!resolvedKeyboardShortcut) return;
|
|
815
|
-
const
|
|
885
|
+
const onKey = (e) => {
|
|
816
886
|
if ((e.metaKey || e.ctrlKey) && e.key === shortcutKey) {
|
|
817
887
|
e.preventDefault();
|
|
818
888
|
setOpen((v) => !v);
|
|
819
889
|
}
|
|
820
890
|
};
|
|
821
|
-
window.addEventListener("keydown",
|
|
822
|
-
return () => window.removeEventListener("keydown",
|
|
891
|
+
window.addEventListener("keydown", onKey);
|
|
892
|
+
return () => window.removeEventListener("keydown", onKey);
|
|
823
893
|
}, [resolvedKeyboardShortcut, shortcutKey]);
|
|
824
894
|
react.useEffect(() => {
|
|
825
895
|
if (!open) return;
|
|
826
|
-
const
|
|
827
|
-
panelRef.current?.querySelector("textarea")?.focus()
|
|
828
|
-
|
|
829
|
-
|
|
896
|
+
const t = setTimeout(
|
|
897
|
+
() => panelRef.current?.querySelector("textarea")?.focus(),
|
|
898
|
+
50
|
|
899
|
+
);
|
|
900
|
+
return () => clearTimeout(t);
|
|
830
901
|
}, [open]);
|
|
831
|
-
const definedChatProps = Object.fromEntries(
|
|
832
|
-
Object.entries(chatProps).filter(([, v]) => v !== void 0)
|
|
833
|
-
);
|
|
834
|
-
const mergedConfig = {
|
|
835
|
-
...remoteConfig,
|
|
836
|
-
...definedChatProps,
|
|
837
|
-
// Required fields must always come from chatProps
|
|
838
|
-
agentId: chatProps.agentId,
|
|
839
|
-
agentName: chatProps.agentName
|
|
840
|
-
};
|
|
841
902
|
const handleClose = () => {
|
|
842
903
|
setOpen(false);
|
|
843
|
-
|
|
844
|
-
localStorage.setItem("wallavi_bubble_dismissed", String(expires));
|
|
904
|
+
localStorage.setItem(KEY_DISMISSED, String(Date.now() + 24 * 60 * 60 * 1e3));
|
|
845
905
|
};
|
|
846
906
|
const toggleExpanded = () => setExpanded((v) => {
|
|
847
907
|
const next = !v;
|
|
848
|
-
localStorage.setItem(
|
|
908
|
+
localStorage.setItem(KEY_EXPANDED, String(next));
|
|
849
909
|
return next;
|
|
850
910
|
});
|
|
851
911
|
const isLeft = resolvedPosition === "bottom-left";
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
setControlsPos(null);
|
|
855
|
-
return;
|
|
856
|
-
}
|
|
857
|
-
const update = () => {
|
|
858
|
-
const rect = panelRef.current?.getBoundingClientRect();
|
|
859
|
-
if (!rect) return;
|
|
860
|
-
setControlsPos({
|
|
861
|
-
top: rect.top - 12,
|
|
862
|
-
side: isLeft ? rect.left - 12 : window.innerWidth - rect.right - 12
|
|
863
|
-
});
|
|
864
|
-
};
|
|
865
|
-
const t = setTimeout(update, 16);
|
|
866
|
-
window.addEventListener("resize", update);
|
|
867
|
-
return () => {
|
|
868
|
-
clearTimeout(t);
|
|
869
|
-
window.removeEventListener("resize", update);
|
|
870
|
-
};
|
|
871
|
-
}, [open, isLeft, expanded]);
|
|
912
|
+
const panelWidth = expanded ? Math.min(expandedWidth, typeof window !== "undefined" ? window.innerWidth - 40 : expandedWidth) : width;
|
|
913
|
+
const panelHeight = expanded ? expandedHeight : height;
|
|
872
914
|
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
873
915
|
"div",
|
|
874
916
|
{
|
|
@@ -888,88 +930,24 @@ function BubbleWidget({
|
|
|
888
930
|
{
|
|
889
931
|
ref: panelRef,
|
|
890
932
|
"aria-hidden": !open,
|
|
891
|
-
style: {
|
|
933
|
+
style: {
|
|
934
|
+
display: open ? "block" : "none",
|
|
935
|
+
width: panelWidth,
|
|
936
|
+
height: panelHeight,
|
|
937
|
+
transition: "width 0.3s ease, height 0.3s ease"
|
|
938
|
+
},
|
|
892
939
|
children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
893
|
-
|
|
940
|
+
ChatWidget,
|
|
894
941
|
{
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
901
|
-
ChatWidget,
|
|
902
|
-
{
|
|
903
|
-
...mergedConfig,
|
|
904
|
-
hideCloseButton: true,
|
|
905
|
-
className: cn4("shadow-2xl h-full", panelClassName)
|
|
906
|
-
}
|
|
907
|
-
)
|
|
942
|
+
...mergedConfig,
|
|
943
|
+
onClose: handleClose,
|
|
944
|
+
onExpand: toggleExpanded,
|
|
945
|
+
expanded,
|
|
946
|
+
className: cn4("shadow-2xl h-full", panelClassName)
|
|
908
947
|
}
|
|
909
948
|
)
|
|
910
949
|
}
|
|
911
950
|
),
|
|
912
|
-
open && controlsPos && typeof document !== "undefined" && reactDom.createPortal(
|
|
913
|
-
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
914
|
-
"div",
|
|
915
|
-
{
|
|
916
|
-
style: {
|
|
917
|
-
position: "fixed",
|
|
918
|
-
top: controlsPos.top,
|
|
919
|
-
[isLeft ? "left" : "right"]: controlsPos.side,
|
|
920
|
-
zIndex: 10001,
|
|
921
|
-
display: "flex",
|
|
922
|
-
alignItems: "center",
|
|
923
|
-
gap: 4
|
|
924
|
-
},
|
|
925
|
-
children: [
|
|
926
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
927
|
-
"button",
|
|
928
|
-
{
|
|
929
|
-
onClick: toggleExpanded,
|
|
930
|
-
title: expanded ? "Minimize" : "Expand",
|
|
931
|
-
style: {
|
|
932
|
-
width: 24,
|
|
933
|
-
height: 24,
|
|
934
|
-
borderRadius: "50%",
|
|
935
|
-
border: "1px solid rgba(0,0,0,0.1)",
|
|
936
|
-
background: "var(--background, #fff)",
|
|
937
|
-
boxShadow: "0 1px 4px rgba(0,0,0,0.12)",
|
|
938
|
-
display: "flex",
|
|
939
|
-
alignItems: "center",
|
|
940
|
-
justifyContent: "center",
|
|
941
|
-
cursor: "pointer",
|
|
942
|
-
color: "var(--muted-foreground, #888)"
|
|
943
|
-
},
|
|
944
|
-
children: expanded ? /* @__PURE__ */ jsxRuntime.jsx(MinimizeIcon, {}) : /* @__PURE__ */ jsxRuntime.jsx(ExpandIcon, {})
|
|
945
|
-
}
|
|
946
|
-
),
|
|
947
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
948
|
-
"button",
|
|
949
|
-
{
|
|
950
|
-
onClick: handleClose,
|
|
951
|
-
title: "Close",
|
|
952
|
-
style: {
|
|
953
|
-
width: 24,
|
|
954
|
-
height: 24,
|
|
955
|
-
borderRadius: "50%",
|
|
956
|
-
border: "1px solid rgba(0,0,0,0.1)",
|
|
957
|
-
background: "var(--background, #fff)",
|
|
958
|
-
boxShadow: "0 1px 4px rgba(0,0,0,0.12)",
|
|
959
|
-
display: "flex",
|
|
960
|
-
alignItems: "center",
|
|
961
|
-
justifyContent: "center",
|
|
962
|
-
cursor: "pointer",
|
|
963
|
-
color: "var(--muted-foreground, #888)"
|
|
964
|
-
},
|
|
965
|
-
children: /* @__PURE__ */ jsxRuntime.jsx(CloseIcon, {})
|
|
966
|
-
}
|
|
967
|
-
)
|
|
968
|
-
]
|
|
969
|
-
}
|
|
970
|
-
),
|
|
971
|
-
document.body
|
|
972
|
-
),
|
|
973
951
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
974
952
|
"button",
|
|
975
953
|
{
|
|
@@ -990,11 +968,11 @@ function BubbleWidget({
|
|
|
990
968
|
background: open ? "var(--foreground, #19191c)" : "var(--background, #fff)",
|
|
991
969
|
color: open ? "var(--background, #fff)" : "var(--foreground, #19191c)"
|
|
992
970
|
},
|
|
993
|
-
children: open ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
971
|
+
children: open ? /* @__PURE__ */ jsxRuntime.jsx(lucideReact.X, { style: { width: 20, height: 20 } }) : resolvedBubbleIcon ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
994
972
|
"img",
|
|
995
973
|
{
|
|
996
974
|
src: resolvedBubbleIcon,
|
|
997
|
-
alt: "
|
|
975
|
+
alt: "",
|
|
998
976
|
style: { width: "100%", height: "100%", objectFit: "cover", display: "block" }
|
|
999
977
|
}
|
|
1000
978
|
) : /* @__PURE__ */ jsxRuntime.jsx(DefaultIcon, {})
|
package/dist/index.mjs
CHANGED
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
import { useState, useRef, useEffect, useCallback } from 'react';
|
|
2
|
-
import {
|
|
2
|
+
import { X, RotateCcw, Loader2, ArrowUp, Zap, ChevronDown, CheckCircle2, AlertCircle } from 'lucide-react';
|
|
3
3
|
import { clsx } from 'clsx';
|
|
4
4
|
import { twMerge } from 'tailwind-merge';
|
|
5
|
-
import { RotateCcw, X, Loader2, ArrowUp, Zap, ChevronDown, CheckCircle2, AlertCircle } from 'lucide-react';
|
|
6
5
|
import * as AvatarPrimitive from '@radix-ui/react-avatar';
|
|
7
6
|
import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
|
|
8
7
|
import ReactMarkdownLib from 'react-markdown';
|
|
@@ -267,6 +266,42 @@ function useChat({
|
|
|
267
266
|
}, [streaming, messages, send]);
|
|
268
267
|
return { messages, input, setInput, streaming, threadId, send, regenerate, reset };
|
|
269
268
|
}
|
|
269
|
+
function DefaultIcon() {
|
|
270
|
+
return /* @__PURE__ */ jsxs("svg", { viewBox: "0 0 24 24", fill: "none", style: { width: 26, height: 26 }, children: [
|
|
271
|
+
/* @__PURE__ */ jsx("circle", { cx: "12", cy: "12", r: "12", fill: "currentColor", opacity: 0.12 }),
|
|
272
|
+
/* @__PURE__ */ jsx(
|
|
273
|
+
"path",
|
|
274
|
+
{
|
|
275
|
+
d: "M4 8.5C4 6.57 5.57 5 7.5 5h9C18.43 5 20 6.57 20 8.5v5c0 1.93-1.57 3.5-3.5 3.5H13l-3 2.5V17H7.5C5.57 17 4 15.43 4 13.5v-5Z",
|
|
276
|
+
fill: "currentColor"
|
|
277
|
+
}
|
|
278
|
+
)
|
|
279
|
+
] });
|
|
280
|
+
}
|
|
281
|
+
function ExpandIcon() {
|
|
282
|
+
return /* @__PURE__ */ jsx("svg", { viewBox: "0 0 24 24", fill: "none", style: { width: 11, height: 11 }, children: /* @__PURE__ */ jsx(
|
|
283
|
+
"path",
|
|
284
|
+
{
|
|
285
|
+
d: "M15 3h6v6M9 21H3v-6M21 3l-7 7M3 21l7-7",
|
|
286
|
+
stroke: "currentColor",
|
|
287
|
+
strokeWidth: 2,
|
|
288
|
+
strokeLinecap: "round",
|
|
289
|
+
strokeLinejoin: "round"
|
|
290
|
+
}
|
|
291
|
+
) });
|
|
292
|
+
}
|
|
293
|
+
function MinimizeIcon() {
|
|
294
|
+
return /* @__PURE__ */ jsx("svg", { viewBox: "0 0 24 24", fill: "none", style: { width: 11, height: 11 }, children: /* @__PURE__ */ jsx(
|
|
295
|
+
"path",
|
|
296
|
+
{
|
|
297
|
+
d: "M4 14h6v6M20 10h-6V4M14 10l7-7M3 21l7-7",
|
|
298
|
+
stroke: "currentColor",
|
|
299
|
+
strokeWidth: 2,
|
|
300
|
+
strokeLinecap: "round",
|
|
301
|
+
strokeLinejoin: "round"
|
|
302
|
+
}
|
|
303
|
+
) });
|
|
304
|
+
}
|
|
270
305
|
var Avatar = ({ style, ...p }) => /* @__PURE__ */ jsx(AvatarPrimitive.Root, { style: { position: "relative", display: "flex", flexShrink: 0, overflow: "hidden", borderRadius: "9999px", ...style }, ...p });
|
|
271
306
|
var AvatarImage = ({ style, ...p }) => /* @__PURE__ */ jsx(AvatarPrimitive.Image, { style: { position: "absolute", inset: 0, width: "100%", height: "100%", objectFit: "cover", ...style }, ...p });
|
|
272
307
|
var AvatarFallback = ({ style, ...p }) => /* @__PURE__ */ jsx(AvatarPrimitive.Fallback, { style: { display: "flex", width: "100%", height: "100%", alignItems: "center", justifyContent: "center", borderRadius: "9999px", ...style }, ...p });
|
|
@@ -276,7 +311,9 @@ function ChatHeader({
|
|
|
276
311
|
headerBg,
|
|
277
312
|
headerText,
|
|
278
313
|
onReset,
|
|
279
|
-
onClose
|
|
314
|
+
onClose,
|
|
315
|
+
onExpand,
|
|
316
|
+
expanded
|
|
280
317
|
}) {
|
|
281
318
|
return /* @__PURE__ */ jsxs(
|
|
282
319
|
"header",
|
|
@@ -298,6 +335,15 @@ function ChatHeader({
|
|
|
298
335
|
] })
|
|
299
336
|
] }),
|
|
300
337
|
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1", children: [
|
|
338
|
+
onExpand && /* @__PURE__ */ jsx(
|
|
339
|
+
"button",
|
|
340
|
+
{
|
|
341
|
+
onClick: onExpand,
|
|
342
|
+
className: "rounded-full p-1.5 transition-colors hover:bg-white/10",
|
|
343
|
+
title: expanded ? "Minimize" : "Expand",
|
|
344
|
+
children: expanded ? /* @__PURE__ */ jsx(MinimizeIcon, {}) : /* @__PURE__ */ jsx(ExpandIcon, {})
|
|
345
|
+
}
|
|
346
|
+
),
|
|
301
347
|
/* @__PURE__ */ jsx(
|
|
302
348
|
"button",
|
|
303
349
|
{
|
|
@@ -603,7 +649,9 @@ function ChatWidget({
|
|
|
603
649
|
playgroundOverrides,
|
|
604
650
|
className,
|
|
605
651
|
onClose,
|
|
606
|
-
onReset
|
|
652
|
+
onReset,
|
|
653
|
+
onExpand,
|
|
654
|
+
expanded
|
|
607
655
|
}) {
|
|
608
656
|
const chat = useChat({ agentId, workspaceId, source, userContext, persist, onNavigate, playgroundOverrides });
|
|
609
657
|
const canRegenerate = regenerateMessage && chat.messages.length > 0 && chat.messages.at(-1)?.role === "assistant" && !chat.streaming;
|
|
@@ -631,7 +679,9 @@ function ChatWidget({
|
|
|
631
679
|
headerBg,
|
|
632
680
|
headerText,
|
|
633
681
|
onReset: handleReset,
|
|
634
|
-
onClose: hideCloseButton ? void 0 : onClose
|
|
682
|
+
onClose: hideCloseButton ? void 0 : onClose,
|
|
683
|
+
onExpand,
|
|
684
|
+
expanded
|
|
635
685
|
}
|
|
636
686
|
),
|
|
637
687
|
/* @__PURE__ */ jsx(
|
|
@@ -684,29 +734,63 @@ function ChatWidget({
|
|
|
684
734
|
}
|
|
685
735
|
);
|
|
686
736
|
}
|
|
687
|
-
var cn4 = (...inputs) => twMerge(clsx(inputs));
|
|
688
737
|
var WALLAVI_PUBLIC_API = "https://wallavi-production.up.railway.app";
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
}
|
|
707
|
-
|
|
708
|
-
|
|
738
|
+
var EMPTY = {
|
|
739
|
+
remoteConfig: {},
|
|
740
|
+
bubbleIconUrl: void 0,
|
|
741
|
+
autoOpen: false,
|
|
742
|
+
keyboardShortcut: false,
|
|
743
|
+
position: "bottom-right",
|
|
744
|
+
loading: false
|
|
745
|
+
};
|
|
746
|
+
function useAutoConfig(agentId, enabled) {
|
|
747
|
+
const [result, setResult] = useState(() => ({
|
|
748
|
+
...EMPTY,
|
|
749
|
+
loading: enabled && Boolean(agentId)
|
|
750
|
+
}));
|
|
751
|
+
useEffect(() => {
|
|
752
|
+
if (!enabled || !agentId) {
|
|
753
|
+
setResult(EMPTY);
|
|
754
|
+
return;
|
|
755
|
+
}
|
|
756
|
+
let cancelled = false;
|
|
757
|
+
fetch(`${WALLAVI_PUBLIC_API}/api/public/widget/${agentId}`).then((r) => r.json()).then((body) => {
|
|
758
|
+
if (cancelled) return;
|
|
759
|
+
const cfg = body?.data ?? {};
|
|
760
|
+
const remote = {};
|
|
761
|
+
if (cfg.profilePicture != null) remote.profilePicture = cfg.profilePicture;
|
|
762
|
+
if (cfg.displayName != null) remote.displayName = cfg.displayName;
|
|
763
|
+
if (cfg.theme) remote.theme = cfg.theme;
|
|
764
|
+
if (cfg.userMessageColor) remote.userMessageColor = cfg.userMessageColor;
|
|
765
|
+
if (Array.isArray(cfg.initialMessages) && cfg.initialMessages.length > 0)
|
|
766
|
+
remote.initialMessages = cfg.initialMessages;
|
|
767
|
+
if (Array.isArray(cfg.suggestedMessages))
|
|
768
|
+
remote.suggestedMessages = cfg.suggestedMessages;
|
|
769
|
+
if (cfg.messagePlaceholder != null) remote.messagePlaceholder = cfg.messagePlaceholder;
|
|
770
|
+
if (cfg.watermark != null) remote.watermark = cfg.watermark;
|
|
771
|
+
if (cfg.footer != null) remote.footer = cfg.footer;
|
|
772
|
+
if (cfg.showThinking != null) remote.showThinking = cfg.showThinking;
|
|
773
|
+
if (cfg.regenerateMessage != null) remote.regenerateMessage = cfg.regenerateMessage;
|
|
774
|
+
setResult({
|
|
775
|
+
remoteConfig: remote,
|
|
776
|
+
bubbleIconUrl: cfg.chatIcon || cfg.profilePicture || void 0,
|
|
777
|
+
autoOpen: Boolean(cfg.autoOpen),
|
|
778
|
+
keyboardShortcut: Boolean(cfg.keyboardShortcut),
|
|
779
|
+
position: cfg.alignChatBubbleButton === "left" ? "bottom-left" : "bottom-right",
|
|
780
|
+
loading: false
|
|
781
|
+
});
|
|
782
|
+
}).catch(() => {
|
|
783
|
+
if (!cancelled) setResult((r) => ({ ...r, loading: false }));
|
|
784
|
+
});
|
|
785
|
+
return () => {
|
|
786
|
+
cancelled = true;
|
|
787
|
+
};
|
|
788
|
+
}, [agentId, enabled]);
|
|
789
|
+
return result;
|
|
709
790
|
}
|
|
791
|
+
var cn4 = (...inputs) => twMerge(clsx(inputs));
|
|
792
|
+
var KEY_EXPANDED = "wallavi_bubble_expanded";
|
|
793
|
+
var KEY_DISMISSED = "wallavi_bubble_dismissed";
|
|
710
794
|
function BubbleWidget({
|
|
711
795
|
position: positionProp,
|
|
712
796
|
width = 360,
|
|
@@ -720,21 +804,27 @@ function BubbleWidget({
|
|
|
720
804
|
bubbleSize = 52,
|
|
721
805
|
panelClassName,
|
|
722
806
|
autoConfig = true,
|
|
723
|
-
// ChatWidgetConfig props
|
|
724
807
|
...chatProps
|
|
725
808
|
}) {
|
|
726
809
|
const [open, setOpen] = useState(false);
|
|
727
|
-
const [expanded, setExpanded] = useState(
|
|
728
|
-
() => typeof window !== "undefined" && localStorage.getItem("wallavi_bubble_expanded") === "true"
|
|
729
|
-
);
|
|
730
|
-
const [controlsPos, setControlsPos] = useState(null);
|
|
810
|
+
const [expanded, setExpanded] = useState(false);
|
|
731
811
|
const panelRef = useRef(null);
|
|
732
812
|
const autoOpenedRef = useRef(false);
|
|
733
|
-
|
|
813
|
+
useEffect(() => {
|
|
814
|
+
if (localStorage.getItem(KEY_EXPANDED) === "true") setExpanded(true);
|
|
815
|
+
}, []);
|
|
816
|
+
const remote = useAutoConfig(chatProps.agentId, autoConfig);
|
|
734
817
|
const [resolvedBubbleIcon, setResolvedBubbleIcon] = useState(bubbleIconUrlProp);
|
|
735
818
|
const [resolvedAutoOpen, setResolvedAutoOpen] = useState(autoOpenProp);
|
|
736
819
|
const [resolvedKeyboardShortcut, setResolvedKeyboardShortcut] = useState(keyboardShortcutProp);
|
|
737
820
|
const [resolvedPosition, setResolvedPosition] = useState(positionProp ?? "bottom-right");
|
|
821
|
+
useEffect(() => {
|
|
822
|
+
if (remote.loading) return;
|
|
823
|
+
if (!bubbleIconUrlProp) setResolvedBubbleIcon(remote.bubbleIconUrl);
|
|
824
|
+
if (!autoOpenProp) setResolvedAutoOpen(remote.autoOpen);
|
|
825
|
+
if (!keyboardShortcutProp) setResolvedKeyboardShortcut(remote.keyboardShortcut);
|
|
826
|
+
if (!positionProp) setResolvedPosition(remote.position);
|
|
827
|
+
}, [remote.loading]);
|
|
738
828
|
useEffect(() => {
|
|
739
829
|
if (autoOpenProp) setResolvedAutoOpen(true);
|
|
740
830
|
}, [autoOpenProp]);
|
|
@@ -747,38 +837,18 @@ function BubbleWidget({
|
|
|
747
837
|
useEffect(() => {
|
|
748
838
|
if (positionProp) setResolvedPosition(positionProp);
|
|
749
839
|
}, [positionProp]);
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
if (Array.isArray(cfg.initialMessages) && cfg.initialMessages.length > 0) remote.initialMessages = cfg.initialMessages;
|
|
760
|
-
if (Array.isArray(cfg.suggestedMessages)) remote.suggestedMessages = cfg.suggestedMessages;
|
|
761
|
-
if (cfg.messagePlaceholder != null) remote.messagePlaceholder = cfg.messagePlaceholder;
|
|
762
|
-
if (cfg.watermark != null) remote.watermark = cfg.watermark;
|
|
763
|
-
if (cfg.footer != null) remote.footer = cfg.footer;
|
|
764
|
-
if (cfg.showThinking != null) remote.showThinking = cfg.showThinking;
|
|
765
|
-
if (cfg.regenerateMessage != null) remote.regenerateMessage = cfg.regenerateMessage;
|
|
766
|
-
setRemoteConfig(remote);
|
|
767
|
-
if (!bubbleIconUrlProp) {
|
|
768
|
-
const icon = cfg.chatIcon || cfg.profilePicture;
|
|
769
|
-
if (icon) setResolvedBubbleIcon(icon);
|
|
770
|
-
}
|
|
771
|
-
if (!autoOpenProp && cfg.autoOpen) setResolvedAutoOpen(true);
|
|
772
|
-
if (!keyboardShortcutProp && cfg.keyboardShortcut) setResolvedKeyboardShortcut(true);
|
|
773
|
-
if (!positionProp && cfg.alignChatBubbleButton) {
|
|
774
|
-
setResolvedPosition(cfg.alignChatBubbleButton === "left" ? "bottom-left" : "bottom-right");
|
|
775
|
-
}
|
|
776
|
-
}).catch(() => {
|
|
777
|
-
});
|
|
778
|
-
}, [autoConfig, chatProps.agentId]);
|
|
840
|
+
const definedChatProps = Object.fromEntries(
|
|
841
|
+
Object.entries(chatProps).filter(([, v]) => v !== void 0)
|
|
842
|
+
);
|
|
843
|
+
const mergedConfig = {
|
|
844
|
+
...remote.remoteConfig,
|
|
845
|
+
...definedChatProps,
|
|
846
|
+
agentId: chatProps.agentId,
|
|
847
|
+
agentName: chatProps.agentName
|
|
848
|
+
};
|
|
779
849
|
useEffect(() => {
|
|
780
850
|
if (!resolvedAutoOpen || autoOpenedRef.current) return;
|
|
781
|
-
const dismissedUntil = Number(localStorage.getItem(
|
|
851
|
+
const dismissedUntil = Number(localStorage.getItem(KEY_DISMISSED) ?? 0);
|
|
782
852
|
if (dismissedUntil < Date.now()) {
|
|
783
853
|
autoOpenedRef.current = true;
|
|
784
854
|
setOpen(true);
|
|
@@ -786,63 +856,35 @@ function BubbleWidget({
|
|
|
786
856
|
}, [resolvedAutoOpen]);
|
|
787
857
|
useEffect(() => {
|
|
788
858
|
if (!resolvedKeyboardShortcut) return;
|
|
789
|
-
const
|
|
859
|
+
const onKey = (e) => {
|
|
790
860
|
if ((e.metaKey || e.ctrlKey) && e.key === shortcutKey) {
|
|
791
861
|
e.preventDefault();
|
|
792
862
|
setOpen((v) => !v);
|
|
793
863
|
}
|
|
794
864
|
};
|
|
795
|
-
window.addEventListener("keydown",
|
|
796
|
-
return () => window.removeEventListener("keydown",
|
|
865
|
+
window.addEventListener("keydown", onKey);
|
|
866
|
+
return () => window.removeEventListener("keydown", onKey);
|
|
797
867
|
}, [resolvedKeyboardShortcut, shortcutKey]);
|
|
798
868
|
useEffect(() => {
|
|
799
869
|
if (!open) return;
|
|
800
|
-
const
|
|
801
|
-
panelRef.current?.querySelector("textarea")?.focus()
|
|
802
|
-
|
|
803
|
-
|
|
870
|
+
const t = setTimeout(
|
|
871
|
+
() => panelRef.current?.querySelector("textarea")?.focus(),
|
|
872
|
+
50
|
|
873
|
+
);
|
|
874
|
+
return () => clearTimeout(t);
|
|
804
875
|
}, [open]);
|
|
805
|
-
const definedChatProps = Object.fromEntries(
|
|
806
|
-
Object.entries(chatProps).filter(([, v]) => v !== void 0)
|
|
807
|
-
);
|
|
808
|
-
const mergedConfig = {
|
|
809
|
-
...remoteConfig,
|
|
810
|
-
...definedChatProps,
|
|
811
|
-
// Required fields must always come from chatProps
|
|
812
|
-
agentId: chatProps.agentId,
|
|
813
|
-
agentName: chatProps.agentName
|
|
814
|
-
};
|
|
815
876
|
const handleClose = () => {
|
|
816
877
|
setOpen(false);
|
|
817
|
-
|
|
818
|
-
localStorage.setItem("wallavi_bubble_dismissed", String(expires));
|
|
878
|
+
localStorage.setItem(KEY_DISMISSED, String(Date.now() + 24 * 60 * 60 * 1e3));
|
|
819
879
|
};
|
|
820
880
|
const toggleExpanded = () => setExpanded((v) => {
|
|
821
881
|
const next = !v;
|
|
822
|
-
localStorage.setItem(
|
|
882
|
+
localStorage.setItem(KEY_EXPANDED, String(next));
|
|
823
883
|
return next;
|
|
824
884
|
});
|
|
825
885
|
const isLeft = resolvedPosition === "bottom-left";
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
setControlsPos(null);
|
|
829
|
-
return;
|
|
830
|
-
}
|
|
831
|
-
const update = () => {
|
|
832
|
-
const rect = panelRef.current?.getBoundingClientRect();
|
|
833
|
-
if (!rect) return;
|
|
834
|
-
setControlsPos({
|
|
835
|
-
top: rect.top - 12,
|
|
836
|
-
side: isLeft ? rect.left - 12 : window.innerWidth - rect.right - 12
|
|
837
|
-
});
|
|
838
|
-
};
|
|
839
|
-
const t = setTimeout(update, 16);
|
|
840
|
-
window.addEventListener("resize", update);
|
|
841
|
-
return () => {
|
|
842
|
-
clearTimeout(t);
|
|
843
|
-
window.removeEventListener("resize", update);
|
|
844
|
-
};
|
|
845
|
-
}, [open, isLeft, expanded]);
|
|
886
|
+
const panelWidth = expanded ? Math.min(expandedWidth, typeof window !== "undefined" ? window.innerWidth - 40 : expandedWidth) : width;
|
|
887
|
+
const panelHeight = expanded ? expandedHeight : height;
|
|
846
888
|
return /* @__PURE__ */ jsxs(
|
|
847
889
|
"div",
|
|
848
890
|
{
|
|
@@ -862,88 +904,24 @@ function BubbleWidget({
|
|
|
862
904
|
{
|
|
863
905
|
ref: panelRef,
|
|
864
906
|
"aria-hidden": !open,
|
|
865
|
-
style: {
|
|
907
|
+
style: {
|
|
908
|
+
display: open ? "block" : "none",
|
|
909
|
+
width: panelWidth,
|
|
910
|
+
height: panelHeight,
|
|
911
|
+
transition: "width 0.3s ease, height 0.3s ease"
|
|
912
|
+
},
|
|
866
913
|
children: /* @__PURE__ */ jsx(
|
|
867
|
-
|
|
914
|
+
ChatWidget,
|
|
868
915
|
{
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
children: /* @__PURE__ */ jsx(
|
|
875
|
-
ChatWidget,
|
|
876
|
-
{
|
|
877
|
-
...mergedConfig,
|
|
878
|
-
hideCloseButton: true,
|
|
879
|
-
className: cn4("shadow-2xl h-full", panelClassName)
|
|
880
|
-
}
|
|
881
|
-
)
|
|
916
|
+
...mergedConfig,
|
|
917
|
+
onClose: handleClose,
|
|
918
|
+
onExpand: toggleExpanded,
|
|
919
|
+
expanded,
|
|
920
|
+
className: cn4("shadow-2xl h-full", panelClassName)
|
|
882
921
|
}
|
|
883
922
|
)
|
|
884
923
|
}
|
|
885
924
|
),
|
|
886
|
-
open && controlsPos && typeof document !== "undefined" && createPortal(
|
|
887
|
-
/* @__PURE__ */ jsxs(
|
|
888
|
-
"div",
|
|
889
|
-
{
|
|
890
|
-
style: {
|
|
891
|
-
position: "fixed",
|
|
892
|
-
top: controlsPos.top,
|
|
893
|
-
[isLeft ? "left" : "right"]: controlsPos.side,
|
|
894
|
-
zIndex: 10001,
|
|
895
|
-
display: "flex",
|
|
896
|
-
alignItems: "center",
|
|
897
|
-
gap: 4
|
|
898
|
-
},
|
|
899
|
-
children: [
|
|
900
|
-
/* @__PURE__ */ jsx(
|
|
901
|
-
"button",
|
|
902
|
-
{
|
|
903
|
-
onClick: toggleExpanded,
|
|
904
|
-
title: expanded ? "Minimize" : "Expand",
|
|
905
|
-
style: {
|
|
906
|
-
width: 24,
|
|
907
|
-
height: 24,
|
|
908
|
-
borderRadius: "50%",
|
|
909
|
-
border: "1px solid rgba(0,0,0,0.1)",
|
|
910
|
-
background: "var(--background, #fff)",
|
|
911
|
-
boxShadow: "0 1px 4px rgba(0,0,0,0.12)",
|
|
912
|
-
display: "flex",
|
|
913
|
-
alignItems: "center",
|
|
914
|
-
justifyContent: "center",
|
|
915
|
-
cursor: "pointer",
|
|
916
|
-
color: "var(--muted-foreground, #888)"
|
|
917
|
-
},
|
|
918
|
-
children: expanded ? /* @__PURE__ */ jsx(MinimizeIcon, {}) : /* @__PURE__ */ jsx(ExpandIcon, {})
|
|
919
|
-
}
|
|
920
|
-
),
|
|
921
|
-
/* @__PURE__ */ jsx(
|
|
922
|
-
"button",
|
|
923
|
-
{
|
|
924
|
-
onClick: handleClose,
|
|
925
|
-
title: "Close",
|
|
926
|
-
style: {
|
|
927
|
-
width: 24,
|
|
928
|
-
height: 24,
|
|
929
|
-
borderRadius: "50%",
|
|
930
|
-
border: "1px solid rgba(0,0,0,0.1)",
|
|
931
|
-
background: "var(--background, #fff)",
|
|
932
|
-
boxShadow: "0 1px 4px rgba(0,0,0,0.12)",
|
|
933
|
-
display: "flex",
|
|
934
|
-
alignItems: "center",
|
|
935
|
-
justifyContent: "center",
|
|
936
|
-
cursor: "pointer",
|
|
937
|
-
color: "var(--muted-foreground, #888)"
|
|
938
|
-
},
|
|
939
|
-
children: /* @__PURE__ */ jsx(CloseIcon, {})
|
|
940
|
-
}
|
|
941
|
-
)
|
|
942
|
-
]
|
|
943
|
-
}
|
|
944
|
-
),
|
|
945
|
-
document.body
|
|
946
|
-
),
|
|
947
925
|
/* @__PURE__ */ jsx(
|
|
948
926
|
"button",
|
|
949
927
|
{
|
|
@@ -964,11 +942,11 @@ function BubbleWidget({
|
|
|
964
942
|
background: open ? "var(--foreground, #19191c)" : "var(--background, #fff)",
|
|
965
943
|
color: open ? "var(--background, #fff)" : "var(--foreground, #19191c)"
|
|
966
944
|
},
|
|
967
|
-
children: open ? /* @__PURE__ */ jsx(
|
|
945
|
+
children: open ? /* @__PURE__ */ jsx(X, { style: { width: 20, height: 20 } }) : resolvedBubbleIcon ? /* @__PURE__ */ jsx(
|
|
968
946
|
"img",
|
|
969
947
|
{
|
|
970
948
|
src: resolvedBubbleIcon,
|
|
971
|
-
alt: "
|
|
949
|
+
alt: "",
|
|
972
950
|
style: { width: "100%", height: "100%", objectFit: "cover", display: "block" }
|
|
973
951
|
}
|
|
974
952
|
) : /* @__PURE__ */ jsx(DefaultIcon, {})
|