@wallavi/widget 1.0.0 → 1.1.0
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 +35 -2
- package/dist/index.d.ts +35 -2
- package/dist/index.js +133 -48
- package/dist/index.mjs +134 -49
- package/package.json +1 -1
package/dist/index.d.mts
CHANGED
|
@@ -21,11 +21,23 @@ type Message = {
|
|
|
21
21
|
role: "user" | "assistant";
|
|
22
22
|
parts: MessagePart[];
|
|
23
23
|
};
|
|
24
|
+
interface PageContext {
|
|
25
|
+
/** Current URL or path (e.g. "/dashboard/agent/abc/agent-studio") */
|
|
26
|
+
url?: string;
|
|
27
|
+
/** Human-readable page name (e.g. "Agent Studio") */
|
|
28
|
+
title?: string;
|
|
29
|
+
/** Route parameters relevant to the page (e.g. { agentId: "abc123" }) */
|
|
30
|
+
params?: Record<string, string>;
|
|
31
|
+
/** Any custom key-value pairs the developer wants the agent to know about */
|
|
32
|
+
vars?: Record<string, unknown>;
|
|
33
|
+
}
|
|
24
34
|
interface UserContext {
|
|
25
35
|
headers?: Record<string, string>;
|
|
26
36
|
userId?: string;
|
|
27
37
|
userName?: string;
|
|
28
38
|
userEmail?: string;
|
|
39
|
+
/** Contextual info about where the user is and what they're doing */
|
|
40
|
+
pageContext?: PageContext;
|
|
29
41
|
metadata?: Record<string, unknown>;
|
|
30
42
|
}
|
|
31
43
|
interface ChatWidgetConfig {
|
|
@@ -43,8 +55,21 @@ interface ChatWidgetConfig {
|
|
|
43
55
|
footer?: string | null;
|
|
44
56
|
theme?: "light" | "dark";
|
|
45
57
|
showThinking?: boolean;
|
|
58
|
+
regenerateMessage?: boolean;
|
|
59
|
+
/**
|
|
60
|
+
* Persist messages and threadId in sessionStorage so the conversation
|
|
61
|
+
* survives page reloads and navigations. Uses agentId as the storage key.
|
|
62
|
+
*/
|
|
63
|
+
persist?: boolean;
|
|
64
|
+
onNavigate?: (path: string) => void;
|
|
46
65
|
source?: string;
|
|
47
66
|
userContext?: UserContext;
|
|
67
|
+
/** Per-session tool overrides used by Playground (not persisted) */
|
|
68
|
+
playgroundOverrides?: {
|
|
69
|
+
ragIds?: string[];
|
|
70
|
+
mcpIds?: string[];
|
|
71
|
+
actionIds?: string[];
|
|
72
|
+
};
|
|
48
73
|
}
|
|
49
74
|
interface ChatWidgetProps extends ChatWidgetConfig {
|
|
50
75
|
className?: string;
|
|
@@ -54,13 +79,20 @@ interface ChatWidgetProps extends ChatWidgetConfig {
|
|
|
54
79
|
declare function getContrastColor(hex: string): string;
|
|
55
80
|
declare function formatToolName(name: string): string;
|
|
56
81
|
|
|
57
|
-
declare function ChatWidget({ agentId, workspaceId, agentName, displayName, profilePicture, userMessageColor, initialMessages, suggestedMessages, messagePlaceholder, watermark, watermarkLogoUrl, footer, theme, showThinking, source, className, onClose, onReset, }: ChatWidgetProps): react_jsx_runtime.JSX.Element;
|
|
82
|
+
declare function ChatWidget({ agentId, workspaceId, agentName, displayName, profilePicture, userMessageColor, initialMessages, suggestedMessages, messagePlaceholder, watermark, watermarkLogoUrl, footer, theme, showThinking, regenerateMessage, persist, onNavigate, source, userContext, playgroundOverrides, className, onClose, onReset, }: ChatWidgetProps): react_jsx_runtime.JSX.Element;
|
|
58
83
|
|
|
59
84
|
interface UseChatOptions {
|
|
60
85
|
agentId: string;
|
|
61
86
|
workspaceId?: string;
|
|
62
87
|
source?: string;
|
|
63
88
|
userContext?: UserContext;
|
|
89
|
+
persist?: boolean;
|
|
90
|
+
onNavigate?: (path: string) => void;
|
|
91
|
+
playgroundOverrides?: {
|
|
92
|
+
ragIds?: string[];
|
|
93
|
+
mcpIds?: string[];
|
|
94
|
+
actionIds?: string[];
|
|
95
|
+
};
|
|
64
96
|
}
|
|
65
97
|
interface UseChatReturn {
|
|
66
98
|
messages: Message[];
|
|
@@ -69,8 +101,9 @@ interface UseChatReturn {
|
|
|
69
101
|
streaming: boolean;
|
|
70
102
|
threadId: string;
|
|
71
103
|
send: (text?: string) => Promise<void>;
|
|
104
|
+
regenerate: () => Promise<void>;
|
|
72
105
|
reset: () => void;
|
|
73
106
|
}
|
|
74
|
-
declare function useChat({ agentId, workspaceId, source, userContext, }: UseChatOptions): UseChatReturn;
|
|
107
|
+
declare function useChat({ agentId, workspaceId, source, userContext, persist, onNavigate, playgroundOverrides, }: UseChatOptions): UseChatReturn;
|
|
75
108
|
|
|
76
109
|
export { ChatWidget, type ChatWidgetConfig, type ChatWidgetProps, type Message, type MessagePart, type ToolPart, type UserContext, formatToolName, getContrastColor, useChat };
|
package/dist/index.d.ts
CHANGED
|
@@ -21,11 +21,23 @@ type Message = {
|
|
|
21
21
|
role: "user" | "assistant";
|
|
22
22
|
parts: MessagePart[];
|
|
23
23
|
};
|
|
24
|
+
interface PageContext {
|
|
25
|
+
/** Current URL or path (e.g. "/dashboard/agent/abc/agent-studio") */
|
|
26
|
+
url?: string;
|
|
27
|
+
/** Human-readable page name (e.g. "Agent Studio") */
|
|
28
|
+
title?: string;
|
|
29
|
+
/** Route parameters relevant to the page (e.g. { agentId: "abc123" }) */
|
|
30
|
+
params?: Record<string, string>;
|
|
31
|
+
/** Any custom key-value pairs the developer wants the agent to know about */
|
|
32
|
+
vars?: Record<string, unknown>;
|
|
33
|
+
}
|
|
24
34
|
interface UserContext {
|
|
25
35
|
headers?: Record<string, string>;
|
|
26
36
|
userId?: string;
|
|
27
37
|
userName?: string;
|
|
28
38
|
userEmail?: string;
|
|
39
|
+
/** Contextual info about where the user is and what they're doing */
|
|
40
|
+
pageContext?: PageContext;
|
|
29
41
|
metadata?: Record<string, unknown>;
|
|
30
42
|
}
|
|
31
43
|
interface ChatWidgetConfig {
|
|
@@ -43,8 +55,21 @@ interface ChatWidgetConfig {
|
|
|
43
55
|
footer?: string | null;
|
|
44
56
|
theme?: "light" | "dark";
|
|
45
57
|
showThinking?: boolean;
|
|
58
|
+
regenerateMessage?: boolean;
|
|
59
|
+
/**
|
|
60
|
+
* Persist messages and threadId in sessionStorage so the conversation
|
|
61
|
+
* survives page reloads and navigations. Uses agentId as the storage key.
|
|
62
|
+
*/
|
|
63
|
+
persist?: boolean;
|
|
64
|
+
onNavigate?: (path: string) => void;
|
|
46
65
|
source?: string;
|
|
47
66
|
userContext?: UserContext;
|
|
67
|
+
/** Per-session tool overrides used by Playground (not persisted) */
|
|
68
|
+
playgroundOverrides?: {
|
|
69
|
+
ragIds?: string[];
|
|
70
|
+
mcpIds?: string[];
|
|
71
|
+
actionIds?: string[];
|
|
72
|
+
};
|
|
48
73
|
}
|
|
49
74
|
interface ChatWidgetProps extends ChatWidgetConfig {
|
|
50
75
|
className?: string;
|
|
@@ -54,13 +79,20 @@ interface ChatWidgetProps extends ChatWidgetConfig {
|
|
|
54
79
|
declare function getContrastColor(hex: string): string;
|
|
55
80
|
declare function formatToolName(name: string): string;
|
|
56
81
|
|
|
57
|
-
declare function ChatWidget({ agentId, workspaceId, agentName, displayName, profilePicture, userMessageColor, initialMessages, suggestedMessages, messagePlaceholder, watermark, watermarkLogoUrl, footer, theme, showThinking, source, className, onClose, onReset, }: ChatWidgetProps): react_jsx_runtime.JSX.Element;
|
|
82
|
+
declare function ChatWidget({ agentId, workspaceId, agentName, displayName, profilePicture, userMessageColor, initialMessages, suggestedMessages, messagePlaceholder, watermark, watermarkLogoUrl, footer, theme, showThinking, regenerateMessage, persist, onNavigate, source, userContext, playgroundOverrides, className, onClose, onReset, }: ChatWidgetProps): react_jsx_runtime.JSX.Element;
|
|
58
83
|
|
|
59
84
|
interface UseChatOptions {
|
|
60
85
|
agentId: string;
|
|
61
86
|
workspaceId?: string;
|
|
62
87
|
source?: string;
|
|
63
88
|
userContext?: UserContext;
|
|
89
|
+
persist?: boolean;
|
|
90
|
+
onNavigate?: (path: string) => void;
|
|
91
|
+
playgroundOverrides?: {
|
|
92
|
+
ragIds?: string[];
|
|
93
|
+
mcpIds?: string[];
|
|
94
|
+
actionIds?: string[];
|
|
95
|
+
};
|
|
64
96
|
}
|
|
65
97
|
interface UseChatReturn {
|
|
66
98
|
messages: Message[];
|
|
@@ -69,8 +101,9 @@ interface UseChatReturn {
|
|
|
69
101
|
streaming: boolean;
|
|
70
102
|
threadId: string;
|
|
71
103
|
send: (text?: string) => Promise<void>;
|
|
104
|
+
regenerate: () => Promise<void>;
|
|
72
105
|
reset: () => void;
|
|
73
106
|
}
|
|
74
|
-
declare function useChat({ agentId, workspaceId, source, userContext, }: UseChatOptions): UseChatReturn;
|
|
107
|
+
declare function useChat({ agentId, workspaceId, source, userContext, persist, onNavigate, playgroundOverrides, }: UseChatOptions): UseChatReturn;
|
|
75
108
|
|
|
76
109
|
export { ChatWidget, type ChatWidgetConfig, type ChatWidgetProps, type Message, type MessagePart, type ToolPart, type UserContext, formatToolName, getContrastColor, useChat };
|
package/dist/index.js
CHANGED
|
@@ -77,20 +77,59 @@ function useChat({
|
|
|
77
77
|
agentId,
|
|
78
78
|
workspaceId = "",
|
|
79
79
|
source = "playground",
|
|
80
|
-
userContext
|
|
80
|
+
userContext,
|
|
81
|
+
persist = false,
|
|
82
|
+
onNavigate,
|
|
83
|
+
playgroundOverrides
|
|
81
84
|
}) {
|
|
82
|
-
const
|
|
85
|
+
const persistKey = persist ? `wallavi_${agentId}` : null;
|
|
86
|
+
const [messages, setMessages] = react.useState(() => {
|
|
87
|
+
if (!persistKey || typeof window === "undefined") return [];
|
|
88
|
+
try {
|
|
89
|
+
const saved = sessionStorage.getItem(`${persistKey}_msgs`);
|
|
90
|
+
return saved ? JSON.parse(saved) : [];
|
|
91
|
+
} catch {
|
|
92
|
+
return [];
|
|
93
|
+
}
|
|
94
|
+
});
|
|
83
95
|
const [input, setInput] = react.useState("");
|
|
84
96
|
const [streaming, setStreaming] = react.useState(false);
|
|
85
|
-
const [threadId, setThreadId] = react.useState(() =>
|
|
97
|
+
const [threadId, setThreadId] = react.useState(() => {
|
|
98
|
+
if (persistKey && typeof window !== "undefined") {
|
|
99
|
+
const saved = sessionStorage.getItem(`${persistKey}_tid`);
|
|
100
|
+
if (saved) return saved;
|
|
101
|
+
}
|
|
102
|
+
return crypto.randomUUID();
|
|
103
|
+
});
|
|
86
104
|
const streamingMsgIdRef = react.useRef(null);
|
|
105
|
+
react.useEffect(() => {
|
|
106
|
+
if (!persistKey) return;
|
|
107
|
+
try {
|
|
108
|
+
sessionStorage.setItem(`${persistKey}_msgs`, JSON.stringify(messages));
|
|
109
|
+
} catch {
|
|
110
|
+
}
|
|
111
|
+
}, [persistKey, messages]);
|
|
112
|
+
react.useEffect(() => {
|
|
113
|
+
if (!persistKey) return;
|
|
114
|
+
try {
|
|
115
|
+
sessionStorage.setItem(`${persistKey}_tid`, threadId);
|
|
116
|
+
} catch {
|
|
117
|
+
}
|
|
118
|
+
}, [persistKey, threadId]);
|
|
87
119
|
const reset = react.useCallback(() => {
|
|
88
120
|
setMessages([]);
|
|
89
121
|
setInput("");
|
|
90
122
|
setStreaming(false);
|
|
91
123
|
setThreadId(crypto.randomUUID());
|
|
92
124
|
streamingMsgIdRef.current = null;
|
|
93
|
-
|
|
125
|
+
if (persistKey) {
|
|
126
|
+
try {
|
|
127
|
+
sessionStorage.removeItem(`${persistKey}_msgs`);
|
|
128
|
+
sessionStorage.removeItem(`${persistKey}_tid`);
|
|
129
|
+
} catch {
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
}, [persistKey]);
|
|
94
133
|
const send = react.useCallback(
|
|
95
134
|
async (text) => {
|
|
96
135
|
const userInput = (text ?? input).trim();
|
|
@@ -121,11 +160,16 @@ function useChat({
|
|
|
121
160
|
agentId,
|
|
122
161
|
workspaceId,
|
|
123
162
|
source,
|
|
163
|
+
...playgroundOverrides ? { playgroundOverrides } : {},
|
|
124
164
|
...userContext?.userName ? { userName: userContext.userName } : {},
|
|
125
165
|
...userContext?.userEmail ? { userEmail: userContext.userEmail } : {},
|
|
126
166
|
userMetadata: {
|
|
127
167
|
...userContext?.metadata ?? {},
|
|
128
|
-
...userContext?.
|
|
168
|
+
...userContext?.pageContext ? { pageContext: userContext.pageContext } : {},
|
|
169
|
+
headers: {
|
|
170
|
+
...token ? { Authorization: `Bearer ${token}` } : {},
|
|
171
|
+
...userContext?.headers ?? {}
|
|
172
|
+
}
|
|
129
173
|
}
|
|
130
174
|
})
|
|
131
175
|
});
|
|
@@ -135,6 +179,10 @@ function useChat({
|
|
|
135
179
|
}
|
|
136
180
|
if (!res.body) throw new Error("No stream body");
|
|
137
181
|
const applyEvent = (proto) => {
|
|
182
|
+
if (proto.type === "navigate") {
|
|
183
|
+
if (proto.path.startsWith("/")) onNavigate?.(proto.path);
|
|
184
|
+
return;
|
|
185
|
+
}
|
|
138
186
|
setMessages((prev) => {
|
|
139
187
|
const idx = prev.findIndex((m) => m.id === assistantMsgId);
|
|
140
188
|
if (idx === -1) return prev;
|
|
@@ -229,11 +277,24 @@ function useChat({
|
|
|
229
277
|
},
|
|
230
278
|
[input, streaming, agentId, workspaceId, source, threadId, userContext]
|
|
231
279
|
);
|
|
232
|
-
|
|
280
|
+
const regenerate = react.useCallback(async () => {
|
|
281
|
+
if (streaming) return;
|
|
282
|
+
const lastUser = [...messages].reverse().find((m) => m.role === "user");
|
|
283
|
+
const lastText = lastUser?.parts.find((p) => p.type === "text")?.text;
|
|
284
|
+
if (!lastText) return;
|
|
285
|
+
setMessages((prev) => {
|
|
286
|
+
const copy = [...prev];
|
|
287
|
+
if (copy.at(-1)?.role === "assistant") copy.pop();
|
|
288
|
+
if (copy.at(-1)?.role === "user") copy.pop();
|
|
289
|
+
return copy;
|
|
290
|
+
});
|
|
291
|
+
await send(lastText);
|
|
292
|
+
}, [streaming, messages, send]);
|
|
293
|
+
return { messages, input, setInput, streaming, threadId, send, regenerate, reset };
|
|
233
294
|
}
|
|
234
|
-
var Avatar = ({
|
|
235
|
-
var AvatarImage = AvatarPrimitive__namespace.Image;
|
|
236
|
-
var AvatarFallback = ({
|
|
295
|
+
var Avatar = ({ style, ...p }) => /* @__PURE__ */ jsxRuntime.jsx(AvatarPrimitive__namespace.Root, { style: { position: "relative", display: "flex", flexShrink: 0, overflow: "hidden", borderRadius: "9999px", ...style }, ...p });
|
|
296
|
+
var AvatarImage = ({ style, ...p }) => /* @__PURE__ */ jsxRuntime.jsx(AvatarPrimitive__namespace.Image, { style: { position: "absolute", inset: 0, width: "100%", height: "100%", objectFit: "cover", ...style }, ...p });
|
|
297
|
+
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 });
|
|
237
298
|
function ChatHeader({
|
|
238
299
|
title,
|
|
239
300
|
profilePicture,
|
|
@@ -249,11 +310,10 @@ function ChatHeader({
|
|
|
249
310
|
style: { backgroundColor: headerBg, color: headerText },
|
|
250
311
|
children: [
|
|
251
312
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2.5 min-w-0", children: [
|
|
252
|
-
/* @__PURE__ */ jsxRuntime.jsx(Avatar, {
|
|
313
|
+
/* @__PURE__ */ jsxRuntime.jsx(Avatar, { style: { width: 32, height: 32, border: `2px solid ${headerText}30` }, children: profilePicture ? /* @__PURE__ */ jsxRuntime.jsx(AvatarImage, { src: profilePicture, alt: title }) : /* @__PURE__ */ jsxRuntime.jsx(
|
|
253
314
|
AvatarFallback,
|
|
254
315
|
{
|
|
255
|
-
|
|
256
|
-
style: { backgroundColor: `${headerText}20`, color: headerText },
|
|
316
|
+
style: { backgroundColor: `${headerText}20`, color: headerText, fontSize: 11, fontWeight: 700 },
|
|
257
317
|
children: title.slice(0, 2).toUpperCase()
|
|
258
318
|
}
|
|
259
319
|
) }),
|
|
@@ -286,9 +346,9 @@ function ChatHeader({
|
|
|
286
346
|
}
|
|
287
347
|
);
|
|
288
348
|
}
|
|
289
|
-
var Avatar2 = ({
|
|
290
|
-
var AvatarImage2 = AvatarPrimitive__namespace.Image;
|
|
291
|
-
var AvatarFallback2 = ({
|
|
349
|
+
var Avatar2 = ({ style, ...p }) => /* @__PURE__ */ jsxRuntime.jsx(AvatarPrimitive__namespace.Root, { style: { position: "relative", display: "flex", flexShrink: 0, overflow: "hidden", borderRadius: "9999px", ...style }, ...p });
|
|
350
|
+
var AvatarImage2 = ({ style, ...p }) => /* @__PURE__ */ jsxRuntime.jsx(AvatarPrimitive__namespace.Image, { style: { position: "absolute", inset: 0, width: "100%", height: "100%", objectFit: "cover", ...style }, ...p });
|
|
351
|
+
var AvatarFallback2 = ({ style, ...p }) => /* @__PURE__ */ jsxRuntime.jsx(AvatarPrimitive__namespace.Fallback, { style: { display: "flex", width: "100%", height: "100%", alignItems: "center", justifyContent: "center", borderRadius: "9999px", ...style }, ...p });
|
|
292
352
|
var cn = (...inputs) => tailwindMerge.twMerge(clsx.clsx(inputs));
|
|
293
353
|
var ReactMarkdown = ReactMarkdownLib__default.default;
|
|
294
354
|
function ThinkingDots() {
|
|
@@ -384,7 +444,7 @@ function MessageBubble({
|
|
|
384
444
|
const visibleToolParts = showThinking ? toolParts : [];
|
|
385
445
|
const isEmpty = !textPart?.text && visibleToolParts.length === 0;
|
|
386
446
|
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex gap-2.5 items-start", children: [
|
|
387
|
-
/* @__PURE__ */ jsxRuntime.jsx(Avatar2, {
|
|
447
|
+
/* @__PURE__ */ jsxRuntime.jsx(Avatar2, { style: { width: 28, height: 28, marginTop: 2, border: "1px solid rgba(0,0,0,0.08)" }, children: profilePicture ? /* @__PURE__ */ jsxRuntime.jsx(AvatarImage2, { src: profilePicture, alt: agentName }) : /* @__PURE__ */ jsxRuntime.jsx(AvatarFallback2, { style: { fontSize: 10, fontWeight: 600, backgroundColor: "var(--primary, #19191c)", color: "var(--primary-foreground, #fff)" }, children: agentName.slice(0, 2).toUpperCase() }) }),
|
|
388
448
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-1.5 min-w-0 max-w-[82%]", children: [
|
|
389
449
|
showThinking && reasoningPart && /* @__PURE__ */ jsxRuntime.jsx(ReasoningBlock, { text: reasoningPart.text }),
|
|
390
450
|
visibleToolParts.map((t) => /* @__PURE__ */ jsxRuntime.jsx(ToolCallBadge, { part: t }, t.toolCallId)),
|
|
@@ -477,7 +537,9 @@ function ChatInput({
|
|
|
477
537
|
onSend,
|
|
478
538
|
streaming,
|
|
479
539
|
placeholder,
|
|
480
|
-
accentColor
|
|
540
|
+
accentColor,
|
|
541
|
+
canRegenerate = false,
|
|
542
|
+
onRegenerate
|
|
481
543
|
}) {
|
|
482
544
|
const textareaRef = react.useRef(null);
|
|
483
545
|
react.useEffect(() => {
|
|
@@ -492,39 +554,54 @@ function ChatInput({
|
|
|
492
554
|
}
|
|
493
555
|
};
|
|
494
556
|
const hasText = input.trim().length > 0;
|
|
495
|
-
return /* @__PURE__ */ jsxRuntime.
|
|
496
|
-
/* @__PURE__ */ jsxRuntime.
|
|
497
|
-
"
|
|
557
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "shrink-0 border-t bg-background px-3 py-2.5", children: [
|
|
558
|
+
canRegenerate && onRegenerate && /* @__PURE__ */ jsxRuntime.jsxs(
|
|
559
|
+
"button",
|
|
498
560
|
{
|
|
499
|
-
|
|
500
|
-
rows: 1,
|
|
501
|
-
className: "flex-1 resize-none bg-transparent text-sm outline-none placeholder:text-muted-foreground/50 leading-relaxed max-h-32 overflow-y-auto py-0.5",
|
|
502
|
-
placeholder: placeholder ?? "Send a message\u2026",
|
|
503
|
-
value: input,
|
|
504
|
-
onChange: (e) => {
|
|
505
|
-
setInput(e.target.value);
|
|
506
|
-
e.target.style.height = "auto";
|
|
507
|
-
e.target.style.height = `${Math.min(e.target.scrollHeight, 128)}px`;
|
|
508
|
-
},
|
|
509
|
-
onKeyDown: handleKeyDown,
|
|
561
|
+
onClick: onRegenerate,
|
|
510
562
|
disabled: streaming,
|
|
511
|
-
|
|
563
|
+
className: "flex items-center gap-1 text-[11px] text-muted-foreground hover:text-foreground transition-colors mb-2 disabled:opacity-40",
|
|
564
|
+
children: [
|
|
565
|
+
/* @__PURE__ */ jsxRuntime.jsx(lucideReact.RotateCcw, { className: "h-3 w-3" }),
|
|
566
|
+
"Regenerate response"
|
|
567
|
+
]
|
|
512
568
|
}
|
|
513
569
|
),
|
|
514
|
-
/* @__PURE__ */ jsxRuntime.
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
570
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-end gap-2 rounded-2xl border bg-background px-3 py-2 shadow-sm focus-within:ring-1 focus-within:ring-ring/40 transition-shadow", children: [
|
|
571
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
572
|
+
"textarea",
|
|
573
|
+
{
|
|
574
|
+
id: "wallavi-chat-input",
|
|
575
|
+
ref: textareaRef,
|
|
576
|
+
rows: 1,
|
|
577
|
+
className: "flex-1 resize-none bg-transparent text-sm outline-none placeholder:text-muted-foreground/50 leading-relaxed max-h-32 overflow-y-auto py-0.5",
|
|
578
|
+
placeholder: placeholder ?? "Send a message\u2026",
|
|
579
|
+
value: input,
|
|
580
|
+
onChange: (e) => {
|
|
581
|
+
setInput(e.target.value);
|
|
582
|
+
e.target.style.height = "auto";
|
|
583
|
+
e.target.style.height = `${Math.min(e.target.scrollHeight, 128)}px`;
|
|
584
|
+
},
|
|
585
|
+
onKeyDown: handleKeyDown,
|
|
586
|
+
disabled: streaming,
|
|
587
|
+
autoFocus: true
|
|
588
|
+
}
|
|
589
|
+
),
|
|
590
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
591
|
+
"button",
|
|
592
|
+
{
|
|
593
|
+
onClick: onSend,
|
|
594
|
+
disabled: streaming || !hasText,
|
|
595
|
+
className: cn2(
|
|
596
|
+
"h-7 w-7 shrink-0 rounded-xl flex items-center justify-center transition-all duration-200",
|
|
597
|
+
hasText || streaming ? "opacity-100 shadow-sm" : "opacity-30"
|
|
598
|
+
),
|
|
599
|
+
style: hasText || streaming ? { backgroundColor: accentColor, color: getContrastColor(accentColor) } : { backgroundColor: "transparent", color: "currentColor" },
|
|
600
|
+
children: streaming ? /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Loader2, { className: "h-3.5 w-3.5 animate-spin" }) : /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ArrowUp, { className: "h-3.5 w-3.5" })
|
|
601
|
+
}
|
|
602
|
+
)
|
|
603
|
+
] })
|
|
604
|
+
] });
|
|
528
605
|
}
|
|
529
606
|
var cn3 = (...inputs) => tailwindMerge.twMerge(clsx.clsx(inputs));
|
|
530
607
|
function ChatWidget({
|
|
@@ -542,12 +619,18 @@ function ChatWidget({
|
|
|
542
619
|
footer,
|
|
543
620
|
theme,
|
|
544
621
|
showThinking = false,
|
|
622
|
+
regenerateMessage = false,
|
|
623
|
+
persist = false,
|
|
624
|
+
onNavigate,
|
|
545
625
|
source = "playground",
|
|
626
|
+
userContext,
|
|
627
|
+
playgroundOverrides,
|
|
546
628
|
className,
|
|
547
629
|
onClose,
|
|
548
630
|
onReset
|
|
549
631
|
}) {
|
|
550
|
-
const chat = useChat({ agentId, workspaceId, source });
|
|
632
|
+
const chat = useChat({ agentId, workspaceId, source, userContext, persist, onNavigate, playgroundOverrides });
|
|
633
|
+
const canRegenerate = regenerateMessage && chat.messages.length > 0 && chat.messages.at(-1)?.role === "assistant" && !chat.streaming;
|
|
551
634
|
const title = displayName || agentName;
|
|
552
635
|
const headerBg = userMessageColor;
|
|
553
636
|
const headerText = getContrastColor(userMessageColor);
|
|
@@ -597,7 +680,9 @@ function ChatWidget({
|
|
|
597
680
|
onSend: () => chat.send(),
|
|
598
681
|
streaming: chat.streaming,
|
|
599
682
|
placeholder: messagePlaceholder,
|
|
600
|
-
accentColor: userMessageColor
|
|
683
|
+
accentColor: userMessageColor,
|
|
684
|
+
canRegenerate: !!canRegenerate,
|
|
685
|
+
onRegenerate: () => void chat.regenerate()
|
|
601
686
|
}
|
|
602
687
|
),
|
|
603
688
|
watermark && /* @__PURE__ */ jsxRuntime.jsxs("footer", { className: "shrink-0 flex items-center justify-center gap-1.5 bg-muted/50 py-1.5 border-t", children: [
|
package/dist/index.mjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { clsx } from 'clsx';
|
|
2
2
|
import { twMerge } from 'tailwind-merge';
|
|
3
|
-
import { useState, useRef,
|
|
3
|
+
import { useState, useRef, useEffect, useCallback } from 'react';
|
|
4
4
|
import { RotateCcw, X, Loader2, ArrowUp, Zap, ChevronDown, CheckCircle2, AlertCircle } from 'lucide-react';
|
|
5
5
|
import * as AvatarPrimitive from '@radix-ui/react-avatar';
|
|
6
6
|
import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
|
|
@@ -51,20 +51,59 @@ function useChat({
|
|
|
51
51
|
agentId,
|
|
52
52
|
workspaceId = "",
|
|
53
53
|
source = "playground",
|
|
54
|
-
userContext
|
|
54
|
+
userContext,
|
|
55
|
+
persist = false,
|
|
56
|
+
onNavigate,
|
|
57
|
+
playgroundOverrides
|
|
55
58
|
}) {
|
|
56
|
-
const
|
|
59
|
+
const persistKey = persist ? `wallavi_${agentId}` : null;
|
|
60
|
+
const [messages, setMessages] = useState(() => {
|
|
61
|
+
if (!persistKey || typeof window === "undefined") return [];
|
|
62
|
+
try {
|
|
63
|
+
const saved = sessionStorage.getItem(`${persistKey}_msgs`);
|
|
64
|
+
return saved ? JSON.parse(saved) : [];
|
|
65
|
+
} catch {
|
|
66
|
+
return [];
|
|
67
|
+
}
|
|
68
|
+
});
|
|
57
69
|
const [input, setInput] = useState("");
|
|
58
70
|
const [streaming, setStreaming] = useState(false);
|
|
59
|
-
const [threadId, setThreadId] = useState(() =>
|
|
71
|
+
const [threadId, setThreadId] = useState(() => {
|
|
72
|
+
if (persistKey && typeof window !== "undefined") {
|
|
73
|
+
const saved = sessionStorage.getItem(`${persistKey}_tid`);
|
|
74
|
+
if (saved) return saved;
|
|
75
|
+
}
|
|
76
|
+
return crypto.randomUUID();
|
|
77
|
+
});
|
|
60
78
|
const streamingMsgIdRef = useRef(null);
|
|
79
|
+
useEffect(() => {
|
|
80
|
+
if (!persistKey) return;
|
|
81
|
+
try {
|
|
82
|
+
sessionStorage.setItem(`${persistKey}_msgs`, JSON.stringify(messages));
|
|
83
|
+
} catch {
|
|
84
|
+
}
|
|
85
|
+
}, [persistKey, messages]);
|
|
86
|
+
useEffect(() => {
|
|
87
|
+
if (!persistKey) return;
|
|
88
|
+
try {
|
|
89
|
+
sessionStorage.setItem(`${persistKey}_tid`, threadId);
|
|
90
|
+
} catch {
|
|
91
|
+
}
|
|
92
|
+
}, [persistKey, threadId]);
|
|
61
93
|
const reset = useCallback(() => {
|
|
62
94
|
setMessages([]);
|
|
63
95
|
setInput("");
|
|
64
96
|
setStreaming(false);
|
|
65
97
|
setThreadId(crypto.randomUUID());
|
|
66
98
|
streamingMsgIdRef.current = null;
|
|
67
|
-
|
|
99
|
+
if (persistKey) {
|
|
100
|
+
try {
|
|
101
|
+
sessionStorage.removeItem(`${persistKey}_msgs`);
|
|
102
|
+
sessionStorage.removeItem(`${persistKey}_tid`);
|
|
103
|
+
} catch {
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
}, [persistKey]);
|
|
68
107
|
const send = useCallback(
|
|
69
108
|
async (text) => {
|
|
70
109
|
const userInput = (text ?? input).trim();
|
|
@@ -95,11 +134,16 @@ function useChat({
|
|
|
95
134
|
agentId,
|
|
96
135
|
workspaceId,
|
|
97
136
|
source,
|
|
137
|
+
...playgroundOverrides ? { playgroundOverrides } : {},
|
|
98
138
|
...userContext?.userName ? { userName: userContext.userName } : {},
|
|
99
139
|
...userContext?.userEmail ? { userEmail: userContext.userEmail } : {},
|
|
100
140
|
userMetadata: {
|
|
101
141
|
...userContext?.metadata ?? {},
|
|
102
|
-
...userContext?.
|
|
142
|
+
...userContext?.pageContext ? { pageContext: userContext.pageContext } : {},
|
|
143
|
+
headers: {
|
|
144
|
+
...token ? { Authorization: `Bearer ${token}` } : {},
|
|
145
|
+
...userContext?.headers ?? {}
|
|
146
|
+
}
|
|
103
147
|
}
|
|
104
148
|
})
|
|
105
149
|
});
|
|
@@ -109,6 +153,10 @@ function useChat({
|
|
|
109
153
|
}
|
|
110
154
|
if (!res.body) throw new Error("No stream body");
|
|
111
155
|
const applyEvent = (proto) => {
|
|
156
|
+
if (proto.type === "navigate") {
|
|
157
|
+
if (proto.path.startsWith("/")) onNavigate?.(proto.path);
|
|
158
|
+
return;
|
|
159
|
+
}
|
|
112
160
|
setMessages((prev) => {
|
|
113
161
|
const idx = prev.findIndex((m) => m.id === assistantMsgId);
|
|
114
162
|
if (idx === -1) return prev;
|
|
@@ -203,11 +251,24 @@ function useChat({
|
|
|
203
251
|
},
|
|
204
252
|
[input, streaming, agentId, workspaceId, source, threadId, userContext]
|
|
205
253
|
);
|
|
206
|
-
|
|
254
|
+
const regenerate = useCallback(async () => {
|
|
255
|
+
if (streaming) return;
|
|
256
|
+
const lastUser = [...messages].reverse().find((m) => m.role === "user");
|
|
257
|
+
const lastText = lastUser?.parts.find((p) => p.type === "text")?.text;
|
|
258
|
+
if (!lastText) return;
|
|
259
|
+
setMessages((prev) => {
|
|
260
|
+
const copy = [...prev];
|
|
261
|
+
if (copy.at(-1)?.role === "assistant") copy.pop();
|
|
262
|
+
if (copy.at(-1)?.role === "user") copy.pop();
|
|
263
|
+
return copy;
|
|
264
|
+
});
|
|
265
|
+
await send(lastText);
|
|
266
|
+
}, [streaming, messages, send]);
|
|
267
|
+
return { messages, input, setInput, streaming, threadId, send, regenerate, reset };
|
|
207
268
|
}
|
|
208
|
-
var Avatar = ({
|
|
209
|
-
var AvatarImage = AvatarPrimitive.Image;
|
|
210
|
-
var AvatarFallback = ({
|
|
269
|
+
var Avatar = ({ style, ...p }) => /* @__PURE__ */ jsx(AvatarPrimitive.Root, { style: { position: "relative", display: "flex", flexShrink: 0, overflow: "hidden", borderRadius: "9999px", ...style }, ...p });
|
|
270
|
+
var AvatarImage = ({ style, ...p }) => /* @__PURE__ */ jsx(AvatarPrimitive.Image, { style: { position: "absolute", inset: 0, width: "100%", height: "100%", objectFit: "cover", ...style }, ...p });
|
|
271
|
+
var AvatarFallback = ({ style, ...p }) => /* @__PURE__ */ jsx(AvatarPrimitive.Fallback, { style: { display: "flex", width: "100%", height: "100%", alignItems: "center", justifyContent: "center", borderRadius: "9999px", ...style }, ...p });
|
|
211
272
|
function ChatHeader({
|
|
212
273
|
title,
|
|
213
274
|
profilePicture,
|
|
@@ -223,11 +284,10 @@ function ChatHeader({
|
|
|
223
284
|
style: { backgroundColor: headerBg, color: headerText },
|
|
224
285
|
children: [
|
|
225
286
|
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2.5 min-w-0", children: [
|
|
226
|
-
/* @__PURE__ */ jsx(Avatar, {
|
|
287
|
+
/* @__PURE__ */ jsx(Avatar, { style: { width: 32, height: 32, border: `2px solid ${headerText}30` }, children: profilePicture ? /* @__PURE__ */ jsx(AvatarImage, { src: profilePicture, alt: title }) : /* @__PURE__ */ jsx(
|
|
227
288
|
AvatarFallback,
|
|
228
289
|
{
|
|
229
|
-
|
|
230
|
-
style: { backgroundColor: `${headerText}20`, color: headerText },
|
|
290
|
+
style: { backgroundColor: `${headerText}20`, color: headerText, fontSize: 11, fontWeight: 700 },
|
|
231
291
|
children: title.slice(0, 2).toUpperCase()
|
|
232
292
|
}
|
|
233
293
|
) }),
|
|
@@ -260,9 +320,9 @@ function ChatHeader({
|
|
|
260
320
|
}
|
|
261
321
|
);
|
|
262
322
|
}
|
|
263
|
-
var Avatar2 = ({
|
|
264
|
-
var AvatarImage2 = AvatarPrimitive.Image;
|
|
265
|
-
var AvatarFallback2 = ({
|
|
323
|
+
var Avatar2 = ({ style, ...p }) => /* @__PURE__ */ jsx(AvatarPrimitive.Root, { style: { position: "relative", display: "flex", flexShrink: 0, overflow: "hidden", borderRadius: "9999px", ...style }, ...p });
|
|
324
|
+
var AvatarImage2 = ({ style, ...p }) => /* @__PURE__ */ jsx(AvatarPrimitive.Image, { style: { position: "absolute", inset: 0, width: "100%", height: "100%", objectFit: "cover", ...style }, ...p });
|
|
325
|
+
var AvatarFallback2 = ({ style, ...p }) => /* @__PURE__ */ jsx(AvatarPrimitive.Fallback, { style: { display: "flex", width: "100%", height: "100%", alignItems: "center", justifyContent: "center", borderRadius: "9999px", ...style }, ...p });
|
|
266
326
|
var cn = (...inputs) => twMerge(clsx(inputs));
|
|
267
327
|
var ReactMarkdown = ReactMarkdownLib;
|
|
268
328
|
function ThinkingDots() {
|
|
@@ -358,7 +418,7 @@ function MessageBubble({
|
|
|
358
418
|
const visibleToolParts = showThinking ? toolParts : [];
|
|
359
419
|
const isEmpty = !textPart?.text && visibleToolParts.length === 0;
|
|
360
420
|
return /* @__PURE__ */ jsxs("div", { className: "flex gap-2.5 items-start", children: [
|
|
361
|
-
/* @__PURE__ */ jsx(Avatar2, {
|
|
421
|
+
/* @__PURE__ */ jsx(Avatar2, { style: { width: 28, height: 28, marginTop: 2, border: "1px solid rgba(0,0,0,0.08)" }, children: profilePicture ? /* @__PURE__ */ jsx(AvatarImage2, { src: profilePicture, alt: agentName }) : /* @__PURE__ */ jsx(AvatarFallback2, { style: { fontSize: 10, fontWeight: 600, backgroundColor: "var(--primary, #19191c)", color: "var(--primary-foreground, #fff)" }, children: agentName.slice(0, 2).toUpperCase() }) }),
|
|
362
422
|
/* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-1.5 min-w-0 max-w-[82%]", children: [
|
|
363
423
|
showThinking && reasoningPart && /* @__PURE__ */ jsx(ReasoningBlock, { text: reasoningPart.text }),
|
|
364
424
|
visibleToolParts.map((t) => /* @__PURE__ */ jsx(ToolCallBadge, { part: t }, t.toolCallId)),
|
|
@@ -451,7 +511,9 @@ function ChatInput({
|
|
|
451
511
|
onSend,
|
|
452
512
|
streaming,
|
|
453
513
|
placeholder,
|
|
454
|
-
accentColor
|
|
514
|
+
accentColor,
|
|
515
|
+
canRegenerate = false,
|
|
516
|
+
onRegenerate
|
|
455
517
|
}) {
|
|
456
518
|
const textareaRef = useRef(null);
|
|
457
519
|
useEffect(() => {
|
|
@@ -466,39 +528,54 @@ function ChatInput({
|
|
|
466
528
|
}
|
|
467
529
|
};
|
|
468
530
|
const hasText = input.trim().length > 0;
|
|
469
|
-
return /* @__PURE__ */
|
|
470
|
-
/* @__PURE__ */
|
|
471
|
-
"
|
|
531
|
+
return /* @__PURE__ */ jsxs("div", { className: "shrink-0 border-t bg-background px-3 py-2.5", children: [
|
|
532
|
+
canRegenerate && onRegenerate && /* @__PURE__ */ jsxs(
|
|
533
|
+
"button",
|
|
472
534
|
{
|
|
473
|
-
|
|
474
|
-
rows: 1,
|
|
475
|
-
className: "flex-1 resize-none bg-transparent text-sm outline-none placeholder:text-muted-foreground/50 leading-relaxed max-h-32 overflow-y-auto py-0.5",
|
|
476
|
-
placeholder: placeholder ?? "Send a message\u2026",
|
|
477
|
-
value: input,
|
|
478
|
-
onChange: (e) => {
|
|
479
|
-
setInput(e.target.value);
|
|
480
|
-
e.target.style.height = "auto";
|
|
481
|
-
e.target.style.height = `${Math.min(e.target.scrollHeight, 128)}px`;
|
|
482
|
-
},
|
|
483
|
-
onKeyDown: handleKeyDown,
|
|
535
|
+
onClick: onRegenerate,
|
|
484
536
|
disabled: streaming,
|
|
485
|
-
|
|
537
|
+
className: "flex items-center gap-1 text-[11px] text-muted-foreground hover:text-foreground transition-colors mb-2 disabled:opacity-40",
|
|
538
|
+
children: [
|
|
539
|
+
/* @__PURE__ */ jsx(RotateCcw, { className: "h-3 w-3" }),
|
|
540
|
+
"Regenerate response"
|
|
541
|
+
]
|
|
486
542
|
}
|
|
487
543
|
),
|
|
488
|
-
/* @__PURE__ */
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
544
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-end gap-2 rounded-2xl border bg-background px-3 py-2 shadow-sm focus-within:ring-1 focus-within:ring-ring/40 transition-shadow", children: [
|
|
545
|
+
/* @__PURE__ */ jsx(
|
|
546
|
+
"textarea",
|
|
547
|
+
{
|
|
548
|
+
id: "wallavi-chat-input",
|
|
549
|
+
ref: textareaRef,
|
|
550
|
+
rows: 1,
|
|
551
|
+
className: "flex-1 resize-none bg-transparent text-sm outline-none placeholder:text-muted-foreground/50 leading-relaxed max-h-32 overflow-y-auto py-0.5",
|
|
552
|
+
placeholder: placeholder ?? "Send a message\u2026",
|
|
553
|
+
value: input,
|
|
554
|
+
onChange: (e) => {
|
|
555
|
+
setInput(e.target.value);
|
|
556
|
+
e.target.style.height = "auto";
|
|
557
|
+
e.target.style.height = `${Math.min(e.target.scrollHeight, 128)}px`;
|
|
558
|
+
},
|
|
559
|
+
onKeyDown: handleKeyDown,
|
|
560
|
+
disabled: streaming,
|
|
561
|
+
autoFocus: true
|
|
562
|
+
}
|
|
563
|
+
),
|
|
564
|
+
/* @__PURE__ */ jsx(
|
|
565
|
+
"button",
|
|
566
|
+
{
|
|
567
|
+
onClick: onSend,
|
|
568
|
+
disabled: streaming || !hasText,
|
|
569
|
+
className: cn2(
|
|
570
|
+
"h-7 w-7 shrink-0 rounded-xl flex items-center justify-center transition-all duration-200",
|
|
571
|
+
hasText || streaming ? "opacity-100 shadow-sm" : "opacity-30"
|
|
572
|
+
),
|
|
573
|
+
style: hasText || streaming ? { backgroundColor: accentColor, color: getContrastColor(accentColor) } : { backgroundColor: "transparent", color: "currentColor" },
|
|
574
|
+
children: streaming ? /* @__PURE__ */ jsx(Loader2, { className: "h-3.5 w-3.5 animate-spin" }) : /* @__PURE__ */ jsx(ArrowUp, { className: "h-3.5 w-3.5" })
|
|
575
|
+
}
|
|
576
|
+
)
|
|
577
|
+
] })
|
|
578
|
+
] });
|
|
502
579
|
}
|
|
503
580
|
var cn3 = (...inputs) => twMerge(clsx(inputs));
|
|
504
581
|
function ChatWidget({
|
|
@@ -516,12 +593,18 @@ function ChatWidget({
|
|
|
516
593
|
footer,
|
|
517
594
|
theme,
|
|
518
595
|
showThinking = false,
|
|
596
|
+
regenerateMessage = false,
|
|
597
|
+
persist = false,
|
|
598
|
+
onNavigate,
|
|
519
599
|
source = "playground",
|
|
600
|
+
userContext,
|
|
601
|
+
playgroundOverrides,
|
|
520
602
|
className,
|
|
521
603
|
onClose,
|
|
522
604
|
onReset
|
|
523
605
|
}) {
|
|
524
|
-
const chat = useChat({ agentId, workspaceId, source });
|
|
606
|
+
const chat = useChat({ agentId, workspaceId, source, userContext, persist, onNavigate, playgroundOverrides });
|
|
607
|
+
const canRegenerate = regenerateMessage && chat.messages.length > 0 && chat.messages.at(-1)?.role === "assistant" && !chat.streaming;
|
|
525
608
|
const title = displayName || agentName;
|
|
526
609
|
const headerBg = userMessageColor;
|
|
527
610
|
const headerText = getContrastColor(userMessageColor);
|
|
@@ -571,7 +654,9 @@ function ChatWidget({
|
|
|
571
654
|
onSend: () => chat.send(),
|
|
572
655
|
streaming: chat.streaming,
|
|
573
656
|
placeholder: messagePlaceholder,
|
|
574
|
-
accentColor: userMessageColor
|
|
657
|
+
accentColor: userMessageColor,
|
|
658
|
+
canRegenerate: !!canRegenerate,
|
|
659
|
+
onRegenerate: () => void chat.regenerate()
|
|
575
660
|
}
|
|
576
661
|
),
|
|
577
662
|
watermark && /* @__PURE__ */ jsxs("footer", { className: "shrink-0 flex items-center justify-center gap-1.5 bg-muted/50 py-1.5 border-t", children: [
|
package/package.json
CHANGED