@nickle/chatbot-react 0.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/README.md +191 -0
- package/dist/index.d.mts +169 -0
- package/dist/index.d.ts +169 -0
- package/dist/index.js +1651 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +1633 -0
- package/dist/index.mjs.map +1 -0
- package/dist/styles.css +1 -0
- package/package.json +58 -0
- package/src/chatbot/components/awaiting-dots.tsx +9 -0
- package/src/chatbot/components/chatbot-page-config.tsx +17 -0
- package/src/chatbot/components/chatbot-widget.tsx +739 -0
- package/src/chatbot/components/message-markdown.tsx +17 -0
- package/src/chatbot/context/chatbot-context.tsx +580 -0
- package/src/chatbot/hooks/use-chatbot-session.ts +14 -0
- package/src/chatbot/hooks/use-chatbot.ts +5 -0
- package/src/chatbot/index.ts +22 -0
- package/src/chatbot/lib/adapter.ts +127 -0
- package/src/chatbot/lib/defaults.ts +48 -0
- package/src/chatbot/lib/id.ts +7 -0
- package/src/chatbot/lib/session.ts +36 -0
- package/src/chatbot/lib/theme.ts +76 -0
- package/src/chatbot/lib/transport.ts +211 -0
- package/src/chatbot/types/index.ts +165 -0
- package/src/index.ts +1 -0
- package/src/styles.css +257 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,1651 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __export = (target, all) => {
|
|
9
|
+
for (var name in all)
|
|
10
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
11
|
+
};
|
|
12
|
+
var __copyProps = (to, from, except, desc) => {
|
|
13
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
14
|
+
for (let key of __getOwnPropNames(from))
|
|
15
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
16
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
17
|
+
}
|
|
18
|
+
return to;
|
|
19
|
+
};
|
|
20
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
21
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
22
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
23
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
24
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
25
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
26
|
+
mod
|
|
27
|
+
));
|
|
28
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
29
|
+
|
|
30
|
+
// src/index.ts
|
|
31
|
+
var index_exports = {};
|
|
32
|
+
__export(index_exports, {
|
|
33
|
+
ChatbotPageConfig: () => ChatbotPageConfig,
|
|
34
|
+
ChatbotProvider: () => ChatbotProvider,
|
|
35
|
+
ChatbotWidget: () => ChatbotWidget,
|
|
36
|
+
useChatbot: () => useChatbot,
|
|
37
|
+
useChatbotSession: () => useChatbotSession
|
|
38
|
+
});
|
|
39
|
+
module.exports = __toCommonJS(index_exports);
|
|
40
|
+
|
|
41
|
+
// src/chatbot/context/chatbot-context.tsx
|
|
42
|
+
var import_react = require("react");
|
|
43
|
+
|
|
44
|
+
// src/chatbot/lib/defaults.ts
|
|
45
|
+
var import_lucide_react = require("lucide-react");
|
|
46
|
+
var DEFAULT_THEME = {
|
|
47
|
+
primary: "#ffffff",
|
|
48
|
+
primaryForeground: "#111111",
|
|
49
|
+
background: "#0b0b0b",
|
|
50
|
+
surface: "#111111",
|
|
51
|
+
surfaceForeground: "#f5f5f5",
|
|
52
|
+
muted: "#262626",
|
|
53
|
+
mutedForeground: "#a3a3a3",
|
|
54
|
+
border: "#2f2f2f",
|
|
55
|
+
ring: "#737373",
|
|
56
|
+
userBubble: "#ffffff",
|
|
57
|
+
userText: "#111111",
|
|
58
|
+
assistantBubble: "#2a2a2a",
|
|
59
|
+
assistantText: "#f5f5f5",
|
|
60
|
+
radius: "14px",
|
|
61
|
+
fontFamily: "Inter, var(--font-sans), ui-sans-serif, system-ui, -apple-system, Segoe UI, sans-serif",
|
|
62
|
+
shadowBubble: "0 18px 40px -16px rgba(0, 0, 0, 0.7)",
|
|
63
|
+
shadowPanel: "0 34px 80px -36px rgba(0, 0, 0, 0.85)"
|
|
64
|
+
};
|
|
65
|
+
var DEFAULT_ICONS = {
|
|
66
|
+
launcher: import_lucide_react.MessageSquare,
|
|
67
|
+
launcherClosed: import_lucide_react.MessageSquare,
|
|
68
|
+
launcherOpen: import_lucide_react.ChevronDown,
|
|
69
|
+
close: import_lucide_react.X,
|
|
70
|
+
send: import_lucide_react.SendHorizontal,
|
|
71
|
+
attach: import_lucide_react.Paperclip,
|
|
72
|
+
bot: import_lucide_react.Bot,
|
|
73
|
+
menu: import_lucide_react.Ellipsis,
|
|
74
|
+
expand: import_lucide_react.Maximize2,
|
|
75
|
+
download: import_lucide_react.Download
|
|
76
|
+
};
|
|
77
|
+
var DEFAULT_AGENT_NAME = "Assistant";
|
|
78
|
+
var DEFAULT_ASSISTANT_NOTE = "Here to help";
|
|
79
|
+
|
|
80
|
+
// src/chatbot/lib/adapter.ts
|
|
81
|
+
function extractFromRecord(record) {
|
|
82
|
+
const keys = ["message", "text", "output", "response", "answer", "content"];
|
|
83
|
+
for (const key of keys) {
|
|
84
|
+
const value = record[key];
|
|
85
|
+
if (typeof value === "string" && value.trim().length > 0) {
|
|
86
|
+
return value;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
return void 0;
|
|
90
|
+
}
|
|
91
|
+
function defaultBuildRequest(input) {
|
|
92
|
+
const { files, metadata, message, headers, sessionId } = input;
|
|
93
|
+
if (files.length > 0) {
|
|
94
|
+
const formData = new FormData();
|
|
95
|
+
formData.set("message", message);
|
|
96
|
+
formData.set("sessionId", sessionId);
|
|
97
|
+
formData.set("metadata", JSON.stringify(metadata));
|
|
98
|
+
files.forEach((file) => {
|
|
99
|
+
formData.append("files[]", file);
|
|
100
|
+
});
|
|
101
|
+
return {
|
|
102
|
+
method: "POST",
|
|
103
|
+
headers,
|
|
104
|
+
body: formData
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
return {
|
|
108
|
+
method: "POST",
|
|
109
|
+
headers: {
|
|
110
|
+
"Content-Type": "application/json",
|
|
111
|
+
...headers
|
|
112
|
+
},
|
|
113
|
+
body: JSON.stringify({
|
|
114
|
+
message,
|
|
115
|
+
sessionId,
|
|
116
|
+
metadata
|
|
117
|
+
})
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
function defaultParseJsonResponse(raw) {
|
|
121
|
+
if (typeof raw === "string") {
|
|
122
|
+
return { message: raw };
|
|
123
|
+
}
|
|
124
|
+
if (Array.isArray(raw)) {
|
|
125
|
+
const merged = raw.map((item) => {
|
|
126
|
+
if (typeof item === "string") {
|
|
127
|
+
return item;
|
|
128
|
+
}
|
|
129
|
+
if (item && typeof item === "object") {
|
|
130
|
+
return extractFromRecord(item);
|
|
131
|
+
}
|
|
132
|
+
return void 0;
|
|
133
|
+
}).filter(Boolean).join("\n");
|
|
134
|
+
return { message: merged };
|
|
135
|
+
}
|
|
136
|
+
if (raw && typeof raw === "object") {
|
|
137
|
+
const record = raw;
|
|
138
|
+
if (record.delta && typeof record.delta === "string") {
|
|
139
|
+
return {
|
|
140
|
+
delta: record.delta,
|
|
141
|
+
done: Boolean(record.done)
|
|
142
|
+
};
|
|
143
|
+
}
|
|
144
|
+
const text = extractFromRecord(record);
|
|
145
|
+
if (text) {
|
|
146
|
+
return { message: text, done: true };
|
|
147
|
+
}
|
|
148
|
+
if (record.data && typeof record.data === "object") {
|
|
149
|
+
const nested = extractFromRecord(record.data);
|
|
150
|
+
if (nested) {
|
|
151
|
+
return { message: nested, done: true };
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
return { message: "", done: true };
|
|
156
|
+
}
|
|
157
|
+
function defaultParseStreamChunk(chunk) {
|
|
158
|
+
const trimmed = chunk.trim();
|
|
159
|
+
if (!trimmed) {
|
|
160
|
+
return {};
|
|
161
|
+
}
|
|
162
|
+
if (trimmed === "[DONE]") {
|
|
163
|
+
return { done: true };
|
|
164
|
+
}
|
|
165
|
+
try {
|
|
166
|
+
const parsed = JSON.parse(trimmed);
|
|
167
|
+
return defaultParseJsonResponse(parsed);
|
|
168
|
+
} catch {
|
|
169
|
+
return { delta: trimmed };
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
function withDefaultAdapter(adapter) {
|
|
173
|
+
return {
|
|
174
|
+
buildRequest: adapter?.buildRequest ?? defaultBuildRequest,
|
|
175
|
+
parseJsonResponse: adapter?.parseJsonResponse ?? defaultParseJsonResponse,
|
|
176
|
+
parseStreamChunk: adapter?.parseStreamChunk ?? defaultParseStreamChunk
|
|
177
|
+
};
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
// src/chatbot/lib/transport.ts
|
|
181
|
+
function looksLikeStream(contentType) {
|
|
182
|
+
if (!contentType) {
|
|
183
|
+
return false;
|
|
184
|
+
}
|
|
185
|
+
const normalized = contentType.toLowerCase();
|
|
186
|
+
return normalized.includes("text/event-stream") || normalized.includes("application/x-ndjson") || normalized.includes("application/stream");
|
|
187
|
+
}
|
|
188
|
+
function parseAndAppend(rawChunk, adapter, onChunk) {
|
|
189
|
+
const parsed = adapter.parseStreamChunk(rawChunk);
|
|
190
|
+
onChunk(rawChunk);
|
|
191
|
+
if (parsed.done && parsed.message) {
|
|
192
|
+
return { text: parsed.message, done: true };
|
|
193
|
+
}
|
|
194
|
+
if (parsed.delta) {
|
|
195
|
+
return { text: parsed.delta, done: Boolean(parsed.done) };
|
|
196
|
+
}
|
|
197
|
+
if (parsed.message) {
|
|
198
|
+
return { text: parsed.message, done: Boolean(parsed.done) };
|
|
199
|
+
}
|
|
200
|
+
return { text: "", done: Boolean(parsed.done) };
|
|
201
|
+
}
|
|
202
|
+
async function consumeEventStream(response, adapter, onDelta, onChunk) {
|
|
203
|
+
const reader = response.body?.getReader();
|
|
204
|
+
if (!reader) {
|
|
205
|
+
return "";
|
|
206
|
+
}
|
|
207
|
+
const decoder = new TextDecoder();
|
|
208
|
+
let buffer = "";
|
|
209
|
+
let fullText = "";
|
|
210
|
+
let done = false;
|
|
211
|
+
while (!done) {
|
|
212
|
+
const result = await reader.read();
|
|
213
|
+
if (result.done) {
|
|
214
|
+
break;
|
|
215
|
+
}
|
|
216
|
+
buffer += decoder.decode(result.value, { stream: true });
|
|
217
|
+
while (buffer.includes("\n\n")) {
|
|
218
|
+
const index = buffer.indexOf("\n\n");
|
|
219
|
+
const block = buffer.slice(0, index);
|
|
220
|
+
buffer = buffer.slice(index + 2);
|
|
221
|
+
const lines = block.split("\n").filter((line) => line.startsWith("data:"));
|
|
222
|
+
for (const line of lines) {
|
|
223
|
+
const data = line.slice(5).trim();
|
|
224
|
+
const parsed = parseAndAppend(data, adapter, onChunk);
|
|
225
|
+
if (parsed.text) {
|
|
226
|
+
fullText += parsed.text;
|
|
227
|
+
onDelta(parsed.text);
|
|
228
|
+
}
|
|
229
|
+
if (parsed.done) {
|
|
230
|
+
done = true;
|
|
231
|
+
break;
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
if (buffer.trim()) {
|
|
237
|
+
const fallback = parseAndAppend(buffer.trim(), adapter, onChunk);
|
|
238
|
+
if (fallback.text) {
|
|
239
|
+
fullText += fallback.text;
|
|
240
|
+
onDelta(fallback.text);
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
return fullText;
|
|
244
|
+
}
|
|
245
|
+
async function consumeLineStream(response, adapter, onDelta, onChunk) {
|
|
246
|
+
const reader = response.body?.getReader();
|
|
247
|
+
if (!reader) {
|
|
248
|
+
return "";
|
|
249
|
+
}
|
|
250
|
+
const decoder = new TextDecoder();
|
|
251
|
+
let buffer = "";
|
|
252
|
+
let fullText = "";
|
|
253
|
+
let done = false;
|
|
254
|
+
while (!done) {
|
|
255
|
+
const result = await reader.read();
|
|
256
|
+
if (result.done) {
|
|
257
|
+
break;
|
|
258
|
+
}
|
|
259
|
+
buffer += decoder.decode(result.value, { stream: true });
|
|
260
|
+
while (buffer.includes("\n")) {
|
|
261
|
+
const index = buffer.indexOf("\n");
|
|
262
|
+
const line = buffer.slice(0, index).trim();
|
|
263
|
+
buffer = buffer.slice(index + 1);
|
|
264
|
+
if (!line) {
|
|
265
|
+
continue;
|
|
266
|
+
}
|
|
267
|
+
const parsed = parseAndAppend(line, adapter, onChunk);
|
|
268
|
+
if (parsed.text) {
|
|
269
|
+
fullText += parsed.text;
|
|
270
|
+
onDelta(parsed.text);
|
|
271
|
+
}
|
|
272
|
+
if (parsed.done) {
|
|
273
|
+
done = true;
|
|
274
|
+
break;
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
if (buffer.trim()) {
|
|
279
|
+
const parsed = parseAndAppend(buffer.trim(), adapter, onChunk);
|
|
280
|
+
if (parsed.text) {
|
|
281
|
+
fullText += parsed.text;
|
|
282
|
+
onDelta(parsed.text);
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
return fullText;
|
|
286
|
+
}
|
|
287
|
+
async function parseNonStreaming(response, adapter) {
|
|
288
|
+
const contentType = response.headers.get("content-type") || "";
|
|
289
|
+
if (contentType.includes("application/json")) {
|
|
290
|
+
const payload = await response.json();
|
|
291
|
+
const parsed = adapter.parseJsonResponse(payload);
|
|
292
|
+
return parsed.message ?? parsed.delta ?? "";
|
|
293
|
+
}
|
|
294
|
+
const rawText = await response.text();
|
|
295
|
+
try {
|
|
296
|
+
const parsedPayload = JSON.parse(rawText);
|
|
297
|
+
const parsed = adapter.parseJsonResponse(parsedPayload);
|
|
298
|
+
return parsed.message ?? parsed.delta ?? rawText;
|
|
299
|
+
} catch {
|
|
300
|
+
const parsed = adapter.parseJsonResponse(rawText);
|
|
301
|
+
return parsed.message ?? rawText;
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
async function readAssistantResponse({
|
|
305
|
+
response,
|
|
306
|
+
adapter,
|
|
307
|
+
streamingMode,
|
|
308
|
+
onDelta,
|
|
309
|
+
onChunk,
|
|
310
|
+
onRecoverableError
|
|
311
|
+
}) {
|
|
312
|
+
if (!response.ok) {
|
|
313
|
+
const body = await response.text();
|
|
314
|
+
throw new Error(`Webhook request failed (${response.status}): ${body}`);
|
|
315
|
+
}
|
|
316
|
+
const contentType = response.headers.get("content-type");
|
|
317
|
+
const streamEligible = streamingMode !== "off" && Boolean(response.body);
|
|
318
|
+
const shouldStream = streamEligible && (streamingMode === "on" || looksLikeStream(contentType));
|
|
319
|
+
if (!shouldStream) {
|
|
320
|
+
return parseNonStreaming(response, adapter);
|
|
321
|
+
}
|
|
322
|
+
try {
|
|
323
|
+
if ((contentType || "").toLowerCase().includes("text/event-stream")) {
|
|
324
|
+
return await consumeEventStream(response, adapter, onDelta, onChunk);
|
|
325
|
+
}
|
|
326
|
+
return await consumeLineStream(response, adapter, onDelta, onChunk);
|
|
327
|
+
} catch (error) {
|
|
328
|
+
onRecoverableError(error);
|
|
329
|
+
return "";
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
// src/chatbot/lib/id.ts
|
|
334
|
+
function createId(prefix = "cb") {
|
|
335
|
+
if (typeof crypto !== "undefined" && "randomUUID" in crypto) {
|
|
336
|
+
return `${prefix}-${crypto.randomUUID()}`;
|
|
337
|
+
}
|
|
338
|
+
return `${prefix}-${Date.now()}-${Math.random().toString(36).slice(2, 9)}`;
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
// src/chatbot/lib/session.ts
|
|
342
|
+
var SESSION_KEY = "cb:session-id";
|
|
343
|
+
function createSessionId() {
|
|
344
|
+
if (typeof crypto !== "undefined" && "randomUUID" in crypto) {
|
|
345
|
+
return crypto.randomUUID();
|
|
346
|
+
}
|
|
347
|
+
return `cb-${Date.now()}-${Math.random().toString(36).slice(2, 10)}`;
|
|
348
|
+
}
|
|
349
|
+
function getOrCreateSessionId(storage) {
|
|
350
|
+
if (!storage) {
|
|
351
|
+
return createSessionId();
|
|
352
|
+
}
|
|
353
|
+
const existing = storage.getItem(SESSION_KEY);
|
|
354
|
+
if (existing) {
|
|
355
|
+
return existing;
|
|
356
|
+
}
|
|
357
|
+
const created = createSessionId();
|
|
358
|
+
storage.setItem(SESSION_KEY, created);
|
|
359
|
+
return created;
|
|
360
|
+
}
|
|
361
|
+
function resetSessionId(storage) {
|
|
362
|
+
const next = createSessionId();
|
|
363
|
+
if (storage) {
|
|
364
|
+
storage.setItem(SESSION_KEY, next);
|
|
365
|
+
}
|
|
366
|
+
return next;
|
|
367
|
+
}
|
|
368
|
+
function historyKey(sessionId) {
|
|
369
|
+
return `cb:history:${sessionId}`;
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
// src/chatbot/lib/theme.ts
|
|
373
|
+
var HOST_TOKEN_MAP = {
|
|
374
|
+
primary: ["--color-primary", "--primary"],
|
|
375
|
+
primaryForeground: ["--color-primary-foreground", "--primary-foreground"],
|
|
376
|
+
background: ["--cb-background", "--color-background", "--color-bg", "--background"],
|
|
377
|
+
surface: ["--cb-surface", "--color-surface", "--color-card", "--card"],
|
|
378
|
+
surfaceForeground: ["--cb-surface-foreground", "--color-text", "--color-foreground", "--foreground"],
|
|
379
|
+
muted: ["--color-muted", "--muted"],
|
|
380
|
+
mutedForeground: ["--color-muted-foreground", "--muted-foreground"],
|
|
381
|
+
border: ["--color-border", "--border"],
|
|
382
|
+
ring: ["--color-ring", "--ring"],
|
|
383
|
+
userBubble: ["--cb-user-bubble"],
|
|
384
|
+
userText: ["--cb-user-text"],
|
|
385
|
+
assistantBubble: ["--cb-assistant-bubble", "--color-muted", "--muted"],
|
|
386
|
+
assistantText: ["--cb-assistant-text", "--color-text", "--foreground"],
|
|
387
|
+
radius: ["--radius-md", "--radius", "--rounded"],
|
|
388
|
+
fontFamily: ["--font-sans", "--font-family"],
|
|
389
|
+
shadowBubble: ["--shadow-bubble"],
|
|
390
|
+
shadowPanel: ["--shadow-panel"]
|
|
391
|
+
};
|
|
392
|
+
function readRootVar(name) {
|
|
393
|
+
if (typeof window === "undefined") {
|
|
394
|
+
return void 0;
|
|
395
|
+
}
|
|
396
|
+
const styles = getComputedStyle(document.documentElement);
|
|
397
|
+
const value = styles.getPropertyValue(name).trim();
|
|
398
|
+
return value || void 0;
|
|
399
|
+
}
|
|
400
|
+
function resolveThemeTokens(explicit) {
|
|
401
|
+
const resolved = { ...DEFAULT_THEME };
|
|
402
|
+
Object.keys(DEFAULT_THEME).forEach((key) => {
|
|
403
|
+
if (explicit?.[key]) {
|
|
404
|
+
resolved[key] = explicit[key];
|
|
405
|
+
return;
|
|
406
|
+
}
|
|
407
|
+
const hostCandidates = HOST_TOKEN_MAP[key] ?? [];
|
|
408
|
+
for (const candidate of hostCandidates) {
|
|
409
|
+
const value = readRootVar(candidate);
|
|
410
|
+
if (value) {
|
|
411
|
+
resolved[key] = value;
|
|
412
|
+
break;
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
});
|
|
416
|
+
return resolved;
|
|
417
|
+
}
|
|
418
|
+
function toThemeCssVars(tokens) {
|
|
419
|
+
return {
|
|
420
|
+
"--cb-primary": tokens.primary,
|
|
421
|
+
"--cb-primary-foreground": tokens.primaryForeground,
|
|
422
|
+
"--cb-background": tokens.background,
|
|
423
|
+
"--cb-surface": tokens.surface,
|
|
424
|
+
"--cb-surface-foreground": tokens.surfaceForeground,
|
|
425
|
+
"--cb-muted": tokens.muted,
|
|
426
|
+
"--cb-muted-foreground": tokens.mutedForeground,
|
|
427
|
+
"--cb-border": tokens.border,
|
|
428
|
+
"--cb-ring": tokens.ring,
|
|
429
|
+
"--cb-user-bubble": tokens.userBubble,
|
|
430
|
+
"--cb-user-text": tokens.userText,
|
|
431
|
+
"--cb-assistant-bubble": tokens.assistantBubble,
|
|
432
|
+
"--cb-assistant-text": tokens.assistantText,
|
|
433
|
+
"--cb-radius": tokens.radius,
|
|
434
|
+
"--cb-font-family": tokens.fontFamily,
|
|
435
|
+
"--cb-shadow-bubble": tokens.shadowBubble,
|
|
436
|
+
"--cb-shadow-panel": tokens.shadowPanel
|
|
437
|
+
};
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
// src/chatbot/context/chatbot-context.tsx
|
|
441
|
+
var import_jsx_runtime = require("react/jsx-runtime");
|
|
442
|
+
var ChatbotContext = (0, import_react.createContext)(void 0);
|
|
443
|
+
var WELCOME_DELAY_MS = 1500;
|
|
444
|
+
var DEFAULT_LOCALE = "en";
|
|
445
|
+
var LOCALE_COPY = {
|
|
446
|
+
en: {
|
|
447
|
+
agentName: DEFAULT_AGENT_NAME,
|
|
448
|
+
assistantNote: DEFAULT_ASSISTANT_NOTE,
|
|
449
|
+
welcomeMessage: "Hi, how can I help you?",
|
|
450
|
+
disclaimer: "AI can make mistakes."
|
|
451
|
+
},
|
|
452
|
+
de: {
|
|
453
|
+
agentName: "Assistent",
|
|
454
|
+
assistantNote: "Wie kann ich helfen?",
|
|
455
|
+
welcomeMessage: "Hallo, wie kann ich Ihnen helfen?",
|
|
456
|
+
disclaimer: "KI kann Fehler machen."
|
|
457
|
+
},
|
|
458
|
+
es: {
|
|
459
|
+
agentName: "Asistente",
|
|
460
|
+
assistantNote: "Como puedo ayudarte?",
|
|
461
|
+
welcomeMessage: "Hola, como puedo ayudarte?",
|
|
462
|
+
disclaimer: "La IA puede cometer errores."
|
|
463
|
+
},
|
|
464
|
+
fr: {
|
|
465
|
+
agentName: "Assistant",
|
|
466
|
+
assistantNote: "Comment puis-je vous aider ?",
|
|
467
|
+
welcomeMessage: "Bonjour, comment puis-je vous aider ?",
|
|
468
|
+
disclaimer: "L'IA peut faire des erreurs."
|
|
469
|
+
},
|
|
470
|
+
it: {
|
|
471
|
+
agentName: "Assistente",
|
|
472
|
+
assistantNote: "Come posso aiutarti?",
|
|
473
|
+
welcomeMessage: "Ciao, come posso aiutarti?",
|
|
474
|
+
disclaimer: "L'IA puo commettere errori."
|
|
475
|
+
},
|
|
476
|
+
nl: {
|
|
477
|
+
agentName: "Assistent",
|
|
478
|
+
assistantNote: "Hoe kan ik je helpen?",
|
|
479
|
+
welcomeMessage: "Hallo, hoe kan ik je helpen?",
|
|
480
|
+
disclaimer: "AI kan fouten maken."
|
|
481
|
+
},
|
|
482
|
+
pt: {
|
|
483
|
+
agentName: "Assistente",
|
|
484
|
+
assistantNote: "Como posso ajudar?",
|
|
485
|
+
welcomeMessage: "Ola, como posso ajudar?",
|
|
486
|
+
disclaimer: "A IA pode cometer erros."
|
|
487
|
+
},
|
|
488
|
+
pl: {
|
|
489
|
+
agentName: "Asystent",
|
|
490
|
+
assistantNote: "Jak moge pomoc?",
|
|
491
|
+
welcomeMessage: "Czesc, jak moge pomoc?",
|
|
492
|
+
disclaimer: "AI moze popelniac bledy."
|
|
493
|
+
},
|
|
494
|
+
tr: {
|
|
495
|
+
agentName: "Asistan",
|
|
496
|
+
assistantNote: "Nasil yardimci olabilirim?",
|
|
497
|
+
welcomeMessage: "Merhaba, nasil yardimci olabilirim?",
|
|
498
|
+
disclaimer: "Yapay zeka hata yapabilir."
|
|
499
|
+
},
|
|
500
|
+
sv: {
|
|
501
|
+
agentName: "Assistent",
|
|
502
|
+
assistantNote: "Hur kan jag hjalpa dig?",
|
|
503
|
+
welcomeMessage: "Hej, hur kan jag hjalpa dig?",
|
|
504
|
+
disclaimer: "AI kan gora misstag."
|
|
505
|
+
},
|
|
506
|
+
da: {
|
|
507
|
+
agentName: "Assistent",
|
|
508
|
+
assistantNote: "Hvordan kan jeg hjaelpe dig?",
|
|
509
|
+
welcomeMessage: "Hej, hvordan kan jeg hjaelpe dig?",
|
|
510
|
+
disclaimer: "AI kan lave fejl."
|
|
511
|
+
},
|
|
512
|
+
no: {
|
|
513
|
+
agentName: "Assistent",
|
|
514
|
+
assistantNote: "Hvordan kan jeg hjelpe deg?",
|
|
515
|
+
welcomeMessage: "Hei, hvordan kan jeg hjelpe deg?",
|
|
516
|
+
disclaimer: "AI kan gj\xF8re feil."
|
|
517
|
+
},
|
|
518
|
+
fi: {
|
|
519
|
+
agentName: "Avustaja",
|
|
520
|
+
assistantNote: "Miten voin auttaa?",
|
|
521
|
+
welcomeMessage: "Hei, miten voin auttaa?",
|
|
522
|
+
disclaimer: "Tekoaly voi tehda virheita."
|
|
523
|
+
}
|
|
524
|
+
};
|
|
525
|
+
function normalizeLocale(input) {
|
|
526
|
+
if (!input) {
|
|
527
|
+
return DEFAULT_LOCALE;
|
|
528
|
+
}
|
|
529
|
+
return input.trim().toLowerCase().split("-")[0] || DEFAULT_LOCALE;
|
|
530
|
+
}
|
|
531
|
+
function getBrowserLocale() {
|
|
532
|
+
if (typeof navigator === "undefined") {
|
|
533
|
+
return DEFAULT_LOCALE;
|
|
534
|
+
}
|
|
535
|
+
return navigator.language || DEFAULT_LOCALE;
|
|
536
|
+
}
|
|
537
|
+
function getLocaleCopy(locale) {
|
|
538
|
+
return LOCALE_COPY[locale] ?? LOCALE_COPY[DEFAULT_LOCALE];
|
|
539
|
+
}
|
|
540
|
+
function readHistory(storage, sessionId) {
|
|
541
|
+
if (!storage) {
|
|
542
|
+
return [];
|
|
543
|
+
}
|
|
544
|
+
const raw = storage.getItem(historyKey(sessionId));
|
|
545
|
+
if (!raw) {
|
|
546
|
+
return [];
|
|
547
|
+
}
|
|
548
|
+
try {
|
|
549
|
+
const parsed = JSON.parse(raw);
|
|
550
|
+
return Array.isArray(parsed) ? parsed : [];
|
|
551
|
+
} catch {
|
|
552
|
+
return [];
|
|
553
|
+
}
|
|
554
|
+
}
|
|
555
|
+
function ChatbotProvider({
|
|
556
|
+
children,
|
|
557
|
+
webhookUrl,
|
|
558
|
+
locale,
|
|
559
|
+
agentName,
|
|
560
|
+
assistantNote,
|
|
561
|
+
enableUploads = false,
|
|
562
|
+
streamingMode = "auto",
|
|
563
|
+
headers,
|
|
564
|
+
theme,
|
|
565
|
+
icons,
|
|
566
|
+
adapter,
|
|
567
|
+
events,
|
|
568
|
+
initialOpen = false,
|
|
569
|
+
position = "bottom-right",
|
|
570
|
+
classNames
|
|
571
|
+
}) {
|
|
572
|
+
const [isHydrated, setIsHydrated] = (0, import_react.useState)(false);
|
|
573
|
+
const [sessionId, setSessionId] = (0, import_react.useState)("cb:pending");
|
|
574
|
+
const [messages, setMessages] = (0, import_react.useState)([]);
|
|
575
|
+
const [isOpen, setIsOpen] = (0, import_react.useState)(initialOpen);
|
|
576
|
+
const [isAwaiting, setIsAwaiting] = (0, import_react.useState)(false);
|
|
577
|
+
const [pageConfigs, setPageConfigs] = (0, import_react.useState)([]);
|
|
578
|
+
const messagesRef = (0, import_react.useRef)([]);
|
|
579
|
+
(0, import_react.useEffect)(() => {
|
|
580
|
+
messagesRef.current = messages;
|
|
581
|
+
}, [messages]);
|
|
582
|
+
const activePageConfig = pageConfigs.length > 0 ? pageConfigs[pageConfigs.length - 1].config : void 0;
|
|
583
|
+
const mergedTheme = (0, import_react.useMemo)(
|
|
584
|
+
() => ({
|
|
585
|
+
...theme ?? {},
|
|
586
|
+
...activePageConfig?.theme ?? {}
|
|
587
|
+
}),
|
|
588
|
+
[activePageConfig?.theme, theme]
|
|
589
|
+
);
|
|
590
|
+
const resolvedTheme = (0, import_react.useMemo)(() => {
|
|
591
|
+
if (!isHydrated) {
|
|
592
|
+
return { ...DEFAULT_THEME, ...mergedTheme ?? {} };
|
|
593
|
+
}
|
|
594
|
+
return resolveThemeTokens(mergedTheme);
|
|
595
|
+
}, [isHydrated, mergedTheme]);
|
|
596
|
+
const themeVars = (0, import_react.useMemo)(() => toThemeCssVars(resolvedTheme), [resolvedTheme]);
|
|
597
|
+
const mergedIcons = (0, import_react.useMemo)(() => {
|
|
598
|
+
const merged = { ...DEFAULT_ICONS, ...icons ?? {} };
|
|
599
|
+
merged.launcherClosed = icons?.launcherClosed ?? icons?.launcher ?? DEFAULT_ICONS.launcherClosed;
|
|
600
|
+
merged.launcherOpen = icons?.launcherOpen ?? DEFAULT_ICONS.launcherOpen;
|
|
601
|
+
return merged;
|
|
602
|
+
}, [icons]);
|
|
603
|
+
const resolvedLocale = (0, import_react.useMemo)(() => normalizeLocale(locale ?? getBrowserLocale()), [locale]);
|
|
604
|
+
const localeCopy = (0, import_react.useMemo)(() => getLocaleCopy(resolvedLocale), [resolvedLocale]);
|
|
605
|
+
const welcomeAssistantMessage = localeCopy.welcomeMessage;
|
|
606
|
+
const disclaimerText = localeCopy.disclaimer;
|
|
607
|
+
const resolvedAgentName = activePageConfig?.agentName ?? agentName ?? localeCopy.agentName;
|
|
608
|
+
const resolvedAssistantNote = activePageConfig?.assistantNote ?? assistantNote ?? localeCopy.assistantNote;
|
|
609
|
+
const uploadEnabled = activePageConfig?.enableUploads ?? enableUploads;
|
|
610
|
+
const widgetEnabled = activePageConfig?.enabled ?? true;
|
|
611
|
+
(0, import_react.useEffect)(() => {
|
|
612
|
+
if (typeof window === "undefined") {
|
|
613
|
+
return;
|
|
614
|
+
}
|
|
615
|
+
const storage = window.sessionStorage;
|
|
616
|
+
const nextSessionId = getOrCreateSessionId(storage);
|
|
617
|
+
setSessionId(nextSessionId);
|
|
618
|
+
setMessages(readHistory(storage, nextSessionId));
|
|
619
|
+
setIsHydrated(true);
|
|
620
|
+
}, []);
|
|
621
|
+
(0, import_react.useEffect)(() => {
|
|
622
|
+
if (!isHydrated || typeof window === "undefined") {
|
|
623
|
+
return;
|
|
624
|
+
}
|
|
625
|
+
const storage = window.sessionStorage;
|
|
626
|
+
storage.setItem(historyKey(sessionId), JSON.stringify(messages));
|
|
627
|
+
}, [isHydrated, messages, sessionId]);
|
|
628
|
+
(0, import_react.useEffect)(() => {
|
|
629
|
+
if (!isHydrated || !isOpen || !widgetEnabled || typeof window === "undefined") {
|
|
630
|
+
return;
|
|
631
|
+
}
|
|
632
|
+
let timeoutId;
|
|
633
|
+
setMessages((current) => {
|
|
634
|
+
if (current.length > 0) {
|
|
635
|
+
return current;
|
|
636
|
+
}
|
|
637
|
+
const welcomeId = createId("msg");
|
|
638
|
+
timeoutId = window.setTimeout(() => {
|
|
639
|
+
setMessages(
|
|
640
|
+
(next) => next.map(
|
|
641
|
+
(message) => message.id === welcomeId ? {
|
|
642
|
+
...message,
|
|
643
|
+
content: welcomeAssistantMessage,
|
|
644
|
+
status: "complete"
|
|
645
|
+
} : message
|
|
646
|
+
)
|
|
647
|
+
);
|
|
648
|
+
}, WELCOME_DELAY_MS);
|
|
649
|
+
return [
|
|
650
|
+
...current,
|
|
651
|
+
{
|
|
652
|
+
id: welcomeId,
|
|
653
|
+
role: "assistant",
|
|
654
|
+
content: "",
|
|
655
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
656
|
+
status: "streaming"
|
|
657
|
+
}
|
|
658
|
+
];
|
|
659
|
+
});
|
|
660
|
+
return () => {
|
|
661
|
+
if (timeoutId) {
|
|
662
|
+
window.clearTimeout(timeoutId);
|
|
663
|
+
}
|
|
664
|
+
};
|
|
665
|
+
}, [isHydrated, isOpen, welcomeAssistantMessage, widgetEnabled]);
|
|
666
|
+
const open = (0, import_react.useCallback)(() => {
|
|
667
|
+
setIsOpen(true);
|
|
668
|
+
events?.onOpen?.();
|
|
669
|
+
}, [events]);
|
|
670
|
+
const close = (0, import_react.useCallback)(() => {
|
|
671
|
+
setIsOpen(false);
|
|
672
|
+
events?.onClose?.();
|
|
673
|
+
}, [events]);
|
|
674
|
+
const toggle = (0, import_react.useCallback)(() => {
|
|
675
|
+
setIsOpen((current) => {
|
|
676
|
+
const next = !current;
|
|
677
|
+
if (next) {
|
|
678
|
+
events?.onOpen?.();
|
|
679
|
+
} else {
|
|
680
|
+
events?.onClose?.();
|
|
681
|
+
}
|
|
682
|
+
return next;
|
|
683
|
+
});
|
|
684
|
+
}, [events]);
|
|
685
|
+
const clearConversation = (0, import_react.useCallback)(() => {
|
|
686
|
+
setMessages([]);
|
|
687
|
+
if (typeof window !== "undefined") {
|
|
688
|
+
const storage = window.sessionStorage;
|
|
689
|
+
storage.removeItem(historyKey(sessionId));
|
|
690
|
+
}
|
|
691
|
+
}, [sessionId]);
|
|
692
|
+
const resetSession = (0, import_react.useCallback)(() => {
|
|
693
|
+
const storage = typeof window !== "undefined" ? window.sessionStorage : void 0;
|
|
694
|
+
const next = resetSessionId(storage);
|
|
695
|
+
setSessionId(next);
|
|
696
|
+
setMessages([]);
|
|
697
|
+
}, []);
|
|
698
|
+
const registerPageConfig = (0, import_react.useCallback)((id, config) => {
|
|
699
|
+
setPageConfigs((current) => {
|
|
700
|
+
const filtered = current.filter((entry) => entry.id !== id);
|
|
701
|
+
return [...filtered, { id, config }];
|
|
702
|
+
});
|
|
703
|
+
}, []);
|
|
704
|
+
const unregisterPageConfig = (0, import_react.useCallback)((id) => {
|
|
705
|
+
setPageConfigs((current) => current.filter((entry) => entry.id !== id));
|
|
706
|
+
}, []);
|
|
707
|
+
const sendMessage = (0, import_react.useCallback)(
|
|
708
|
+
async ({ text, files = [] }) => {
|
|
709
|
+
const trimmed = text.trim();
|
|
710
|
+
if (!trimmed && files.length === 0) {
|
|
711
|
+
return;
|
|
712
|
+
}
|
|
713
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
714
|
+
const attachmentDtos = files.map((file) => ({
|
|
715
|
+
id: createId("att"),
|
|
716
|
+
name: file.name,
|
|
717
|
+
size: file.size,
|
|
718
|
+
type: file.type,
|
|
719
|
+
file
|
|
720
|
+
}));
|
|
721
|
+
if (attachmentDtos.length > 0) {
|
|
722
|
+
events?.onAttachmentAdded?.(attachmentDtos);
|
|
723
|
+
}
|
|
724
|
+
const userMessage = {
|
|
725
|
+
id: createId("msg"),
|
|
726
|
+
role: "user",
|
|
727
|
+
content: trimmed,
|
|
728
|
+
createdAt: now,
|
|
729
|
+
status: "complete",
|
|
730
|
+
attachments: attachmentDtos
|
|
731
|
+
};
|
|
732
|
+
const ensureWelcomeMessage = (input) => {
|
|
733
|
+
if (input.some((message) => message.role === "assistant" && message.content === welcomeAssistantMessage)) {
|
|
734
|
+
return input;
|
|
735
|
+
}
|
|
736
|
+
const firstStreamingAssistantIndex = input.findIndex(
|
|
737
|
+
(message) => message.role === "assistant" && message.status === "streaming" && !message.content
|
|
738
|
+
);
|
|
739
|
+
if (firstStreamingAssistantIndex >= 0) {
|
|
740
|
+
return input.map(
|
|
741
|
+
(message, index) => index === firstStreamingAssistantIndex ? {
|
|
742
|
+
...message,
|
|
743
|
+
content: welcomeAssistantMessage,
|
|
744
|
+
status: "complete"
|
|
745
|
+
} : message
|
|
746
|
+
);
|
|
747
|
+
}
|
|
748
|
+
if (input.some((message) => message.role === "assistant")) {
|
|
749
|
+
return input;
|
|
750
|
+
}
|
|
751
|
+
const welcomeMessage = {
|
|
752
|
+
id: createId("msg"),
|
|
753
|
+
role: "assistant",
|
|
754
|
+
content: welcomeAssistantMessage,
|
|
755
|
+
createdAt: now,
|
|
756
|
+
status: "complete"
|
|
757
|
+
};
|
|
758
|
+
return [...input, welcomeMessage];
|
|
759
|
+
};
|
|
760
|
+
const normalizedHistory = ensureWelcomeMessage(messagesRef.current);
|
|
761
|
+
const requestHistory = [...normalizedHistory, userMessage].map((message) => ({
|
|
762
|
+
id: message.id,
|
|
763
|
+
role: message.role,
|
|
764
|
+
content: message.content,
|
|
765
|
+
createdAt: message.createdAt
|
|
766
|
+
}));
|
|
767
|
+
setMessages((current) => [...ensureWelcomeMessage(current), userMessage]);
|
|
768
|
+
events?.onMessageSent?.(userMessage);
|
|
769
|
+
const assistantId = createId("msg");
|
|
770
|
+
setMessages((current) => [
|
|
771
|
+
...current,
|
|
772
|
+
{
|
|
773
|
+
id: assistantId,
|
|
774
|
+
role: "assistant",
|
|
775
|
+
content: "",
|
|
776
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
777
|
+
status: "streaming"
|
|
778
|
+
}
|
|
779
|
+
]);
|
|
780
|
+
setIsAwaiting(true);
|
|
781
|
+
const resolvedAdapter = withDefaultAdapter(adapter);
|
|
782
|
+
try {
|
|
783
|
+
const metadata = {
|
|
784
|
+
sessionId,
|
|
785
|
+
pageUrl: typeof window !== "undefined" ? window.location.href : "",
|
|
786
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
787
|
+
agentName: resolvedAgentName,
|
|
788
|
+
locale: resolvedLocale,
|
|
789
|
+
history: requestHistory
|
|
790
|
+
};
|
|
791
|
+
const requestInit = resolvedAdapter.buildRequest({
|
|
792
|
+
webhookUrl,
|
|
793
|
+
message: trimmed,
|
|
794
|
+
sessionId,
|
|
795
|
+
agentName: resolvedAgentName,
|
|
796
|
+
files: uploadEnabled ? files : [],
|
|
797
|
+
metadata,
|
|
798
|
+
headers
|
|
799
|
+
});
|
|
800
|
+
const url = requestInit.url ?? webhookUrl;
|
|
801
|
+
const response = await fetch(url, requestInit);
|
|
802
|
+
let streamedContent = "";
|
|
803
|
+
const responseText = await readAssistantResponse({
|
|
804
|
+
response,
|
|
805
|
+
adapter: resolvedAdapter,
|
|
806
|
+
streamingMode,
|
|
807
|
+
onDelta: (delta) => {
|
|
808
|
+
streamedContent += delta;
|
|
809
|
+
setMessages(
|
|
810
|
+
(current) => current.map(
|
|
811
|
+
(message) => message.id === assistantId ? {
|
|
812
|
+
...message,
|
|
813
|
+
content: `${message.content}${delta}`,
|
|
814
|
+
status: "streaming"
|
|
815
|
+
} : message
|
|
816
|
+
)
|
|
817
|
+
);
|
|
818
|
+
},
|
|
819
|
+
onChunk: (chunk) => {
|
|
820
|
+
events?.onChunkReceived?.(chunk);
|
|
821
|
+
},
|
|
822
|
+
onRecoverableError: (error) => {
|
|
823
|
+
events?.onError?.(error);
|
|
824
|
+
}
|
|
825
|
+
});
|
|
826
|
+
setMessages((current) => {
|
|
827
|
+
const next = current.map((message) => {
|
|
828
|
+
if (message.id !== assistantId) {
|
|
829
|
+
return message;
|
|
830
|
+
}
|
|
831
|
+
const hasStreamed = streamedContent.length > 0;
|
|
832
|
+
const content = hasStreamed ? message.content : responseText;
|
|
833
|
+
return {
|
|
834
|
+
...message,
|
|
835
|
+
content,
|
|
836
|
+
status: "complete"
|
|
837
|
+
};
|
|
838
|
+
});
|
|
839
|
+
const completeMessage = next.find((message) => message.id === assistantId);
|
|
840
|
+
if (completeMessage) {
|
|
841
|
+
events?.onResponseComplete?.(completeMessage);
|
|
842
|
+
}
|
|
843
|
+
return next;
|
|
844
|
+
});
|
|
845
|
+
} catch (error) {
|
|
846
|
+
const typedError = error instanceof Error ? error : new Error(String(error));
|
|
847
|
+
events?.onError?.(typedError);
|
|
848
|
+
setMessages(
|
|
849
|
+
(current) => current.map(
|
|
850
|
+
(message) => message.id === assistantId ? {
|
|
851
|
+
...message,
|
|
852
|
+
content: message.content || "Sorry, I ran into an issue while generating a response.",
|
|
853
|
+
status: "error"
|
|
854
|
+
} : message
|
|
855
|
+
)
|
|
856
|
+
);
|
|
857
|
+
} finally {
|
|
858
|
+
setIsAwaiting(false);
|
|
859
|
+
}
|
|
860
|
+
},
|
|
861
|
+
[
|
|
862
|
+
adapter,
|
|
863
|
+
events,
|
|
864
|
+
headers,
|
|
865
|
+
resolvedAgentName,
|
|
866
|
+
resolvedLocale,
|
|
867
|
+
sessionId,
|
|
868
|
+
streamingMode,
|
|
869
|
+
uploadEnabled,
|
|
870
|
+
welcomeAssistantMessage,
|
|
871
|
+
webhookUrl
|
|
872
|
+
]
|
|
873
|
+
);
|
|
874
|
+
const value = (0, import_react.useMemo)(
|
|
875
|
+
() => ({
|
|
876
|
+
isOpen,
|
|
877
|
+
isAwaiting,
|
|
878
|
+
messages,
|
|
879
|
+
sessionId,
|
|
880
|
+
uploadEnabled,
|
|
881
|
+
agentName: resolvedAgentName,
|
|
882
|
+
assistantNote: resolvedAssistantNote,
|
|
883
|
+
disclaimerText,
|
|
884
|
+
position,
|
|
885
|
+
themeVars,
|
|
886
|
+
icons: mergedIcons,
|
|
887
|
+
classNames: classNames ?? {},
|
|
888
|
+
open,
|
|
889
|
+
close,
|
|
890
|
+
toggle,
|
|
891
|
+
sendMessage,
|
|
892
|
+
clearConversation,
|
|
893
|
+
registerPageConfig,
|
|
894
|
+
unregisterPageConfig,
|
|
895
|
+
widgetEnabled,
|
|
896
|
+
resetSession
|
|
897
|
+
}),
|
|
898
|
+
[
|
|
899
|
+
classNames,
|
|
900
|
+
clearConversation,
|
|
901
|
+
close,
|
|
902
|
+
disclaimerText,
|
|
903
|
+
isAwaiting,
|
|
904
|
+
isOpen,
|
|
905
|
+
mergedIcons,
|
|
906
|
+
messages,
|
|
907
|
+
open,
|
|
908
|
+
position,
|
|
909
|
+
registerPageConfig,
|
|
910
|
+
resolvedAgentName,
|
|
911
|
+
resolvedAssistantNote,
|
|
912
|
+
sendMessage,
|
|
913
|
+
sessionId,
|
|
914
|
+
themeVars,
|
|
915
|
+
toggle,
|
|
916
|
+
unregisterPageConfig,
|
|
917
|
+
uploadEnabled,
|
|
918
|
+
widgetEnabled,
|
|
919
|
+
resetSession
|
|
920
|
+
]
|
|
921
|
+
);
|
|
922
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ChatbotContext.Provider, { value, children });
|
|
923
|
+
}
|
|
924
|
+
function useChatbotContext() {
|
|
925
|
+
const context = (0, import_react.useContext)(ChatbotContext);
|
|
926
|
+
if (!context) {
|
|
927
|
+
throw new Error("useChatbotContext must be used within ChatbotProvider");
|
|
928
|
+
}
|
|
929
|
+
return context;
|
|
930
|
+
}
|
|
931
|
+
|
|
932
|
+
// src/chatbot/components/chatbot-widget.tsx
|
|
933
|
+
var import_clsx = __toESM(require("clsx"));
|
|
934
|
+
var import_react2 = require("react");
|
|
935
|
+
|
|
936
|
+
// src/chatbot/components/awaiting-dots.tsx
|
|
937
|
+
var import_jsx_runtime2 = require("react/jsx-runtime");
|
|
938
|
+
function AwaitingDots() {
|
|
939
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "cb-inline-flex cb-items-center cb-gap-1 cb-px-1", "aria-label": "Assistant is typing", children: [
|
|
940
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "cb-h-1.5 cb-w-1.5 cb-rounded-full cb-bg-[var(--cb-muted-foreground)] cb-opacity-70 cb-animate-cb-dots [animation-delay:-0.2s]" }),
|
|
941
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "cb-h-1.5 cb-w-1.5 cb-rounded-full cb-bg-[var(--cb-muted-foreground)] cb-opacity-70 cb-animate-cb-dots [animation-delay:-0.1s]" }),
|
|
942
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "cb-h-1.5 cb-w-1.5 cb-rounded-full cb-bg-[var(--cb-muted-foreground)] cb-opacity-70 cb-animate-cb-dots" })
|
|
943
|
+
] });
|
|
944
|
+
}
|
|
945
|
+
|
|
946
|
+
// src/chatbot/components/message-markdown.tsx
|
|
947
|
+
var import_react_markdown = __toESM(require("react-markdown"));
|
|
948
|
+
var import_rehype_sanitize = __toESM(require("rehype-sanitize"));
|
|
949
|
+
var import_remark_gfm = __toESM(require("remark-gfm"));
|
|
950
|
+
var import_jsx_runtime3 = require("react/jsx-runtime");
|
|
951
|
+
function MessageMarkdown({ content }) {
|
|
952
|
+
return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "cb-markdown cb-message-text cb-text-base cb-leading-6", children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react_markdown.default, { remarkPlugins: [import_remark_gfm.default], rehypePlugins: [import_rehype_sanitize.default], children: content }) });
|
|
953
|
+
}
|
|
954
|
+
|
|
955
|
+
// src/chatbot/components/chatbot-widget.tsx
|
|
956
|
+
var import_jsx_runtime4 = require("react/jsx-runtime");
|
|
957
|
+
var PANEL_INSET_REM = 1.5;
|
|
958
|
+
var EXPANDED_MAX_WIDTH_PX = 630;
|
|
959
|
+
var MOBILE_BREAKPOINT_PX = 768;
|
|
960
|
+
var ROOT_BOTTOM_OFFSET_REM = 1.5;
|
|
961
|
+
var PANEL_TO_BUBBLE_GAP_REM = 0.75;
|
|
962
|
+
var BUBBLE_SIZE_REM = 3.5;
|
|
963
|
+
var EXPANDED_BOTTOM_OFFSET_REM = ROOT_BOTTOM_OFFSET_REM + PANEL_TO_BUBBLE_GAP_REM + BUBBLE_SIZE_REM;
|
|
964
|
+
var EXPANDED_TOTAL_VERTICAL_INSET_REM = PANEL_INSET_REM + EXPANDED_BOTTOM_OFFSET_REM;
|
|
965
|
+
function formatFileSize(size) {
|
|
966
|
+
if (size < 1024) {
|
|
967
|
+
return `${size} B`;
|
|
968
|
+
}
|
|
969
|
+
if (size < 1024 * 1024) {
|
|
970
|
+
return `${(size / 1024).toFixed(1)} KB`;
|
|
971
|
+
}
|
|
972
|
+
return `${(size / (1024 * 1024)).toFixed(1)} MB`;
|
|
973
|
+
}
|
|
974
|
+
function formatGmtOffset(date) {
|
|
975
|
+
const total = -date.getTimezoneOffset();
|
|
976
|
+
const sign = total >= 0 ? "+" : "-";
|
|
977
|
+
const abs = Math.abs(total);
|
|
978
|
+
const hh = String(Math.floor(abs / 60)).padStart(2, "0");
|
|
979
|
+
const mm = String(abs % 60).padStart(2, "0");
|
|
980
|
+
return `GMT${sign}${hh}${mm}`;
|
|
981
|
+
}
|
|
982
|
+
function readTimeZoneName(date, mode) {
|
|
983
|
+
const parts = new Intl.DateTimeFormat(void 0, { timeZoneName: mode }).formatToParts(date);
|
|
984
|
+
return parts.find((part) => part.type === "timeZoneName")?.value ?? "";
|
|
985
|
+
}
|
|
986
|
+
function formatTranscriptDateTime(date) {
|
|
987
|
+
const datePart = new Intl.DateTimeFormat(void 0, {
|
|
988
|
+
month: "long",
|
|
989
|
+
day: "numeric",
|
|
990
|
+
year: "numeric"
|
|
991
|
+
}).format(date);
|
|
992
|
+
const timePart = new Intl.DateTimeFormat(void 0, {
|
|
993
|
+
hour: "2-digit",
|
|
994
|
+
minute: "2-digit",
|
|
995
|
+
hour12: true
|
|
996
|
+
}).format(date);
|
|
997
|
+
const zoneLong = readTimeZoneName(date, "long");
|
|
998
|
+
const zoneShort = readTimeZoneName(date, "short");
|
|
999
|
+
const gmtOffset = formatGmtOffset(date);
|
|
1000
|
+
return `${datePart} at ${timePart} ${zoneLong} time ${zoneShort} (${gmtOffset})`.replace(/\s+/g, " ").trim();
|
|
1001
|
+
}
|
|
1002
|
+
function formatTranscriptMessageTime(value) {
|
|
1003
|
+
const date = new Date(value);
|
|
1004
|
+
if (Number.isNaN(date.getTime())) {
|
|
1005
|
+
return value;
|
|
1006
|
+
}
|
|
1007
|
+
return new Intl.DateTimeFormat(void 0, {
|
|
1008
|
+
hour: "2-digit",
|
|
1009
|
+
minute: "2-digit",
|
|
1010
|
+
hour12: true
|
|
1011
|
+
}).format(date);
|
|
1012
|
+
}
|
|
1013
|
+
function toTranscript(messages, agentName) {
|
|
1014
|
+
const exportedAt = /* @__PURE__ */ new Date();
|
|
1015
|
+
const firstDate = messages.length > 0 ? new Date(messages[0].createdAt) : exportedAt;
|
|
1016
|
+
const startedAt = Number.isNaN(firstDate.getTime()) ? exportedAt : firstDate;
|
|
1017
|
+
const lines = [
|
|
1018
|
+
`Conversation with ${agentName}`,
|
|
1019
|
+
`Started on ${formatTranscriptDateTime(startedAt)}`,
|
|
1020
|
+
"",
|
|
1021
|
+
"---",
|
|
1022
|
+
""
|
|
1023
|
+
];
|
|
1024
|
+
messages.forEach((message) => {
|
|
1025
|
+
const role = message.role === "user" ? "Visitor" : message.role === "assistant" ? agentName : "System";
|
|
1026
|
+
const content = message.content.trim();
|
|
1027
|
+
if (!content) {
|
|
1028
|
+
return;
|
|
1029
|
+
}
|
|
1030
|
+
lines.push(`${formatTranscriptMessageTime(message.createdAt)} | ${role}: ${content}`);
|
|
1031
|
+
lines.push("");
|
|
1032
|
+
});
|
|
1033
|
+
lines.push("---");
|
|
1034
|
+
lines.push(`Exported from ${agentName} on ${formatTranscriptDateTime(exportedAt)}`);
|
|
1035
|
+
return lines.join("\n");
|
|
1036
|
+
}
|
|
1037
|
+
function toTranscriptFilename(date) {
|
|
1038
|
+
const yyyy = date.getFullYear();
|
|
1039
|
+
const mm = String(date.getMonth() + 1).padStart(2, "0");
|
|
1040
|
+
const dd = String(date.getDate()).padStart(2, "0");
|
|
1041
|
+
const hh = String(date.getHours()).padStart(2, "0");
|
|
1042
|
+
const min = String(date.getMinutes()).padStart(2, "0");
|
|
1043
|
+
const ss = String(date.getSeconds()).padStart(2, "0");
|
|
1044
|
+
return `chat-transcript-${yyyy}${mm}${dd}-${hh}${min}${ss}.txt`;
|
|
1045
|
+
}
|
|
1046
|
+
function ChatbotWidget() {
|
|
1047
|
+
const {
|
|
1048
|
+
isOpen,
|
|
1049
|
+
isAwaiting,
|
|
1050
|
+
messages,
|
|
1051
|
+
uploadEnabled,
|
|
1052
|
+
position,
|
|
1053
|
+
agentName,
|
|
1054
|
+
assistantNote,
|
|
1055
|
+
disclaimerText,
|
|
1056
|
+
themeVars,
|
|
1057
|
+
icons,
|
|
1058
|
+
classNames,
|
|
1059
|
+
close,
|
|
1060
|
+
open,
|
|
1061
|
+
sendMessage,
|
|
1062
|
+
widgetEnabled
|
|
1063
|
+
} = useChatbotContext();
|
|
1064
|
+
const [inputValue, setInputValue] = (0, import_react2.useState)("");
|
|
1065
|
+
const [queuedFiles, setQueuedFiles] = (0, import_react2.useState)([]);
|
|
1066
|
+
const [isMenuOpen, setIsMenuOpen] = (0, import_react2.useState)(false);
|
|
1067
|
+
const [isExpanded, setIsExpanded] = (0, import_react2.useState)(false);
|
|
1068
|
+
const [isMobileViewport, setIsMobileViewport] = (0, import_react2.useState)(false);
|
|
1069
|
+
const panelRef = (0, import_react2.useRef)(null);
|
|
1070
|
+
const messagesScrollRef = (0, import_react2.useRef)(null);
|
|
1071
|
+
const inputRef = (0, import_react2.useRef)(null);
|
|
1072
|
+
const fileInputRef = (0, import_react2.useRef)(null);
|
|
1073
|
+
const menuRef = (0, import_react2.useRef)(null);
|
|
1074
|
+
const LauncherClosedIcon = icons.launcherClosed;
|
|
1075
|
+
const LauncherOpenIcon = icons.launcherOpen;
|
|
1076
|
+
const BotIcon = icons.bot;
|
|
1077
|
+
const MenuIcon = icons.menu;
|
|
1078
|
+
const CloseIcon = icons.close;
|
|
1079
|
+
const SendIcon = icons.send;
|
|
1080
|
+
const AttachIcon = icons.attach;
|
|
1081
|
+
const ExpandIcon = icons.expand;
|
|
1082
|
+
const DownloadIcon = icons.download;
|
|
1083
|
+
const alignment = position === "bottom-left" ? "cb-left-6" : "cb-right-6";
|
|
1084
|
+
const stackAlignment = position === "bottom-left" ? "cb-items-start" : "cb-items-end";
|
|
1085
|
+
const panelOrigin = position === "bottom-left" ? "cb-origin-bottom-left" : "cb-origin-bottom-right";
|
|
1086
|
+
const panelLayoutMode = isMobileViewport ? "mobile" : isExpanded ? "desktop-expanded" : "desktop-collapsed";
|
|
1087
|
+
const showExpandAction = !isMobileViewport;
|
|
1088
|
+
const showDownloadAction = messages.length > 0;
|
|
1089
|
+
const hasMenuActions = showExpandAction || showDownloadAction;
|
|
1090
|
+
const canSend = (0, import_react2.useMemo)(() => {
|
|
1091
|
+
return !isAwaiting && (inputValue.trim().length > 0 || queuedFiles.length > 0);
|
|
1092
|
+
}, [inputValue, isAwaiting, queuedFiles.length]);
|
|
1093
|
+
const resizeComposer = () => {
|
|
1094
|
+
const textarea = inputRef.current;
|
|
1095
|
+
if (!textarea) {
|
|
1096
|
+
return;
|
|
1097
|
+
}
|
|
1098
|
+
if (textarea.value.length === 0) {
|
|
1099
|
+
textarea.style.height = "38px";
|
|
1100
|
+
textarea.style.overflowY = "hidden";
|
|
1101
|
+
return;
|
|
1102
|
+
}
|
|
1103
|
+
const maxHeightPx = 168;
|
|
1104
|
+
textarea.style.height = "auto";
|
|
1105
|
+
const nextHeight = Math.min(textarea.scrollHeight, maxHeightPx);
|
|
1106
|
+
textarea.style.height = `${nextHeight}px`;
|
|
1107
|
+
textarea.style.overflowY = textarea.scrollHeight > maxHeightPx ? "auto" : "hidden";
|
|
1108
|
+
};
|
|
1109
|
+
const scrollMessagesToBottom = () => {
|
|
1110
|
+
const container = messagesScrollRef.current;
|
|
1111
|
+
if (!container) {
|
|
1112
|
+
return;
|
|
1113
|
+
}
|
|
1114
|
+
container.scrollTop = container.scrollHeight;
|
|
1115
|
+
};
|
|
1116
|
+
const panelSize = (0, import_react2.useMemo)(() => {
|
|
1117
|
+
const inset = `${PANEL_INSET_REM}rem`;
|
|
1118
|
+
if (panelLayoutMode === "mobile") {
|
|
1119
|
+
return {
|
|
1120
|
+
className: "cb-fixed cb-max-h-none cb-max-w-none",
|
|
1121
|
+
style: {
|
|
1122
|
+
top: inset,
|
|
1123
|
+
right: inset,
|
|
1124
|
+
bottom: `${EXPANDED_BOTTOM_OFFSET_REM}rem`,
|
|
1125
|
+
left: inset
|
|
1126
|
+
}
|
|
1127
|
+
};
|
|
1128
|
+
}
|
|
1129
|
+
if (panelLayoutMode === "desktop-expanded") {
|
|
1130
|
+
return {
|
|
1131
|
+
className: "cb-relative cb-max-h-none cb-max-w-none",
|
|
1132
|
+
style: {
|
|
1133
|
+
width: `min(${EXPANDED_MAX_WIDTH_PX}px, calc(100vw - ${PANEL_INSET_REM * 2}rem))`,
|
|
1134
|
+
height: `calc(100vh - ${EXPANDED_TOTAL_VERTICAL_INSET_REM}rem)`
|
|
1135
|
+
}
|
|
1136
|
+
};
|
|
1137
|
+
}
|
|
1138
|
+
return {
|
|
1139
|
+
className: "cb-relative cb-h-[min(680px,calc(100vh-7.5rem))] cb-w-[min(92vw,420px)]",
|
|
1140
|
+
style: void 0
|
|
1141
|
+
};
|
|
1142
|
+
}, [panelLayoutMode]);
|
|
1143
|
+
(0, import_react2.useEffect)(() => {
|
|
1144
|
+
if (typeof window === "undefined") {
|
|
1145
|
+
return;
|
|
1146
|
+
}
|
|
1147
|
+
const mediaQuery = window.matchMedia(`(max-width: ${MOBILE_BREAKPOINT_PX}px)`);
|
|
1148
|
+
const syncViewportMode = () => {
|
|
1149
|
+
setIsMobileViewport(mediaQuery.matches);
|
|
1150
|
+
};
|
|
1151
|
+
syncViewportMode();
|
|
1152
|
+
if (typeof mediaQuery.addEventListener === "function") {
|
|
1153
|
+
mediaQuery.addEventListener("change", syncViewportMode);
|
|
1154
|
+
return () => mediaQuery.removeEventListener("change", syncViewportMode);
|
|
1155
|
+
}
|
|
1156
|
+
mediaQuery.addListener(syncViewportMode);
|
|
1157
|
+
return () => mediaQuery.removeListener(syncViewportMode);
|
|
1158
|
+
}, []);
|
|
1159
|
+
(0, import_react2.useEffect)(() => {
|
|
1160
|
+
if (!isOpen) {
|
|
1161
|
+
setIsMenuOpen(false);
|
|
1162
|
+
return;
|
|
1163
|
+
}
|
|
1164
|
+
inputRef.current?.focus();
|
|
1165
|
+
resizeComposer();
|
|
1166
|
+
}, [isOpen]);
|
|
1167
|
+
(0, import_react2.useEffect)(() => {
|
|
1168
|
+
resizeComposer();
|
|
1169
|
+
}, [inputValue]);
|
|
1170
|
+
(0, import_react2.useEffect)(() => {
|
|
1171
|
+
if (!isOpen) {
|
|
1172
|
+
return;
|
|
1173
|
+
}
|
|
1174
|
+
const rafId = window.requestAnimationFrame(() => {
|
|
1175
|
+
scrollMessagesToBottom();
|
|
1176
|
+
});
|
|
1177
|
+
return () => window.cancelAnimationFrame(rafId);
|
|
1178
|
+
}, [isOpen, messages, isAwaiting]);
|
|
1179
|
+
(0, import_react2.useEffect)(() => {
|
|
1180
|
+
if (!isOpen) {
|
|
1181
|
+
return;
|
|
1182
|
+
}
|
|
1183
|
+
const onPointerDown = (event) => {
|
|
1184
|
+
const target = event.target;
|
|
1185
|
+
if (menuRef.current && !menuRef.current.contains(target)) {
|
|
1186
|
+
setIsMenuOpen(false);
|
|
1187
|
+
}
|
|
1188
|
+
};
|
|
1189
|
+
window.addEventListener("mousedown", onPointerDown);
|
|
1190
|
+
return () => window.removeEventListener("mousedown", onPointerDown);
|
|
1191
|
+
}, [isOpen]);
|
|
1192
|
+
(0, import_react2.useEffect)(() => {
|
|
1193
|
+
if (!isOpen) {
|
|
1194
|
+
return;
|
|
1195
|
+
}
|
|
1196
|
+
const onKeyDown = (event) => {
|
|
1197
|
+
if (event.key === "Escape") {
|
|
1198
|
+
if (isMenuOpen) {
|
|
1199
|
+
setIsMenuOpen(false);
|
|
1200
|
+
return;
|
|
1201
|
+
}
|
|
1202
|
+
close();
|
|
1203
|
+
}
|
|
1204
|
+
};
|
|
1205
|
+
window.addEventListener("keydown", onKeyDown);
|
|
1206
|
+
return () => window.removeEventListener("keydown", onKeyDown);
|
|
1207
|
+
}, [close, isMenuOpen, isOpen]);
|
|
1208
|
+
(0, import_react2.useEffect)(() => {
|
|
1209
|
+
setIsMenuOpen(false);
|
|
1210
|
+
}, [isMobileViewport]);
|
|
1211
|
+
(0, import_react2.useEffect)(() => {
|
|
1212
|
+
if (!hasMenuActions) {
|
|
1213
|
+
setIsMenuOpen(false);
|
|
1214
|
+
}
|
|
1215
|
+
}, [hasMenuActions]);
|
|
1216
|
+
const trapFocus = (event) => {
|
|
1217
|
+
if (event.key !== "Tab") {
|
|
1218
|
+
return;
|
|
1219
|
+
}
|
|
1220
|
+
const root = panelRef.current;
|
|
1221
|
+
if (!root) {
|
|
1222
|
+
return;
|
|
1223
|
+
}
|
|
1224
|
+
const focusables = root.querySelectorAll(
|
|
1225
|
+
"button, [href], textarea, input, select, [tabindex]:not([tabindex='-1'])"
|
|
1226
|
+
);
|
|
1227
|
+
if (focusables.length === 0) {
|
|
1228
|
+
return;
|
|
1229
|
+
}
|
|
1230
|
+
const first = focusables[0];
|
|
1231
|
+
const last = focusables[focusables.length - 1];
|
|
1232
|
+
if (event.shiftKey && document.activeElement === first) {
|
|
1233
|
+
last.focus();
|
|
1234
|
+
event.preventDefault();
|
|
1235
|
+
} else if (!event.shiftKey && document.activeElement === last) {
|
|
1236
|
+
first.focus();
|
|
1237
|
+
event.preventDefault();
|
|
1238
|
+
}
|
|
1239
|
+
};
|
|
1240
|
+
const onSubmit = async (event) => {
|
|
1241
|
+
event.preventDefault();
|
|
1242
|
+
if (!canSend) {
|
|
1243
|
+
return;
|
|
1244
|
+
}
|
|
1245
|
+
const submittedText = inputValue;
|
|
1246
|
+
setInputValue("");
|
|
1247
|
+
scrollMessagesToBottom();
|
|
1248
|
+
await sendMessage({
|
|
1249
|
+
text: submittedText,
|
|
1250
|
+
files: uploadEnabled ? queuedFiles : []
|
|
1251
|
+
});
|
|
1252
|
+
setQueuedFiles([]);
|
|
1253
|
+
if (fileInputRef.current) {
|
|
1254
|
+
fileInputRef.current.value = "";
|
|
1255
|
+
}
|
|
1256
|
+
};
|
|
1257
|
+
const onPickFiles = (event) => {
|
|
1258
|
+
const list = event.target.files;
|
|
1259
|
+
if (!list || !uploadEnabled) {
|
|
1260
|
+
return;
|
|
1261
|
+
}
|
|
1262
|
+
const files = Array.from(list);
|
|
1263
|
+
setQueuedFiles((current) => {
|
|
1264
|
+
const seen = new Set(current.map((file) => `${file.name}:${file.size}:${file.lastModified}`));
|
|
1265
|
+
const next = [...current];
|
|
1266
|
+
files.forEach((file) => {
|
|
1267
|
+
const key = `${file.name}:${file.size}:${file.lastModified}`;
|
|
1268
|
+
if (!seen.has(key)) {
|
|
1269
|
+
seen.add(key);
|
|
1270
|
+
next.push(file);
|
|
1271
|
+
}
|
|
1272
|
+
});
|
|
1273
|
+
return next;
|
|
1274
|
+
});
|
|
1275
|
+
};
|
|
1276
|
+
const createIdFromFile = (file) => `${file.name}:${file.size}:${file.lastModified}`;
|
|
1277
|
+
const removeQueuedFile = (fileId) => {
|
|
1278
|
+
setQueuedFiles((current) => current.filter((file) => createIdFromFile(file) !== fileId));
|
|
1279
|
+
};
|
|
1280
|
+
const downloadTranscript = () => {
|
|
1281
|
+
if (messages.length === 0) {
|
|
1282
|
+
return;
|
|
1283
|
+
}
|
|
1284
|
+
const now = /* @__PURE__ */ new Date();
|
|
1285
|
+
const fileContent = toTranscript(messages, agentName);
|
|
1286
|
+
const blob = new Blob([fileContent], { type: "text/plain;charset=utf-8" });
|
|
1287
|
+
const objectUrl = URL.createObjectURL(blob);
|
|
1288
|
+
const anchor = document.createElement("a");
|
|
1289
|
+
anchor.href = objectUrl;
|
|
1290
|
+
anchor.download = toTranscriptFilename(now);
|
|
1291
|
+
document.body.appendChild(anchor);
|
|
1292
|
+
anchor.click();
|
|
1293
|
+
document.body.removeChild(anchor);
|
|
1294
|
+
URL.revokeObjectURL(objectUrl);
|
|
1295
|
+
setIsMenuOpen(false);
|
|
1296
|
+
};
|
|
1297
|
+
const toggleExpanded = () => {
|
|
1298
|
+
if (isMobileViewport) {
|
|
1299
|
+
return;
|
|
1300
|
+
}
|
|
1301
|
+
setIsExpanded((current) => !current);
|
|
1302
|
+
setIsMenuOpen(false);
|
|
1303
|
+
};
|
|
1304
|
+
if (!widgetEnabled) {
|
|
1305
|
+
return null;
|
|
1306
|
+
}
|
|
1307
|
+
return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
1308
|
+
"div",
|
|
1309
|
+
{
|
|
1310
|
+
className: (0, import_clsx.default)(
|
|
1311
|
+
"cb-fixed cb-bottom-6 cb-z-[2147483640] cb-text-[var(--cb-surface-foreground)]",
|
|
1312
|
+
alignment,
|
|
1313
|
+
classNames.root
|
|
1314
|
+
),
|
|
1315
|
+
style: themeVars,
|
|
1316
|
+
"data-chatbot-root": true,
|
|
1317
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: (0, import_clsx.default)("cb-pointer-events-none cb-relative cb-flex cb-flex-col cb-gap-3", stackAlignment), children: [
|
|
1318
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
|
|
1319
|
+
"div",
|
|
1320
|
+
{
|
|
1321
|
+
ref: panelRef,
|
|
1322
|
+
className: (0, import_clsx.default)(
|
|
1323
|
+
"cb-pointer-events-auto cb-flex cb-flex-col cb-overflow-hidden cb-rounded-[var(--cb-radius)] cb-border cb-border-[var(--cb-border)] cb-bg-[var(--cb-surface)] cb-shadow-[var(--cb-shadow-panel)] cb-transition-[opacity,transform,width,height,top,left,right,bottom] cb-duration-[380ms] cb-ease-[cubic-bezier(0.22,1,0.36,1)]",
|
|
1324
|
+
panelOrigin,
|
|
1325
|
+
panelSize.className,
|
|
1326
|
+
isOpen ? "cb-translate-y-0 cb-scale-100 cb-opacity-100" : "cb-translate-y-3 cb-scale-90 cb-opacity-0 cb-pointer-events-none",
|
|
1327
|
+
classNames.panel
|
|
1328
|
+
),
|
|
1329
|
+
style: panelSize.style,
|
|
1330
|
+
"aria-hidden": !isOpen,
|
|
1331
|
+
onKeyDown: trapFocus,
|
|
1332
|
+
children: [
|
|
1333
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
|
|
1334
|
+
"header",
|
|
1335
|
+
{
|
|
1336
|
+
className: (0, import_clsx.default)(
|
|
1337
|
+
"cb-flex cb-items-center cb-justify-between cb-gap-2 cb-border-b cb-border-[var(--cb-border)] cb-bg-[var(--cb-surface)] cb-px-4 cb-py-3.5",
|
|
1338
|
+
classNames.header
|
|
1339
|
+
),
|
|
1340
|
+
style: {
|
|
1341
|
+
borderBottomWidth: "1px",
|
|
1342
|
+
borderBottomStyle: "solid",
|
|
1343
|
+
borderBottomColor: "var(--cb-border)"
|
|
1344
|
+
},
|
|
1345
|
+
children: [
|
|
1346
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: (0, import_clsx.default)("cb-flex cb-min-w-0 cb-items-center cb-gap-2.5", classNames.headerMeta), children: [
|
|
1347
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "cb-inline-flex cb-h-9 cb-w-9 cb-shrink-0 cb-items-center cb-justify-center cb-self-center cb-text-[var(--cb-surface-foreground)]", children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(BotIcon, { size: 34 }) }),
|
|
1348
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "cb-min-w-0 cb-space-y-0", children: [
|
|
1349
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("p", { className: "cb-m-0 cb-truncate cb-text-lg cb-font-semibold cb-leading-6 cb-text-[var(--cb-surface-foreground)]", children: agentName }),
|
|
1350
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("p", { className: "cb-m-0 cb-text-base cb-leading-5 cb-text-[var(--cb-muted-foreground)]", children: assistantNote })
|
|
1351
|
+
] })
|
|
1352
|
+
] }),
|
|
1353
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { ref: menuRef, className: "cb-relative cb-flex cb-items-center cb-gap-1", children: [
|
|
1354
|
+
hasMenuActions && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
1355
|
+
"button",
|
|
1356
|
+
{
|
|
1357
|
+
type: "button",
|
|
1358
|
+
onClick: () => setIsMenuOpen((current) => !current),
|
|
1359
|
+
className: (0, import_clsx.default)(
|
|
1360
|
+
"cb-flex cb-h-9 cb-w-9 cb-shrink-0 cb-items-center cb-justify-center cb-rounded-lg cb-bg-transparent cb-p-0 cb-text-[var(--cb-muted-foreground)] cb-transition hover:cb-bg-[var(--cb-muted)] hover:cb-text-white focus-visible:cb-bg-[var(--cb-muted)] focus-visible:cb-text-white",
|
|
1361
|
+
isMenuOpen && "cb-bg-[var(--cb-muted)] cb-text-white"
|
|
1362
|
+
),
|
|
1363
|
+
"aria-label": "More options",
|
|
1364
|
+
"aria-expanded": isMenuOpen,
|
|
1365
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(MenuIcon, { size: 18 })
|
|
1366
|
+
}
|
|
1367
|
+
),
|
|
1368
|
+
isMenuOpen && hasMenuActions && /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
|
|
1369
|
+
"div",
|
|
1370
|
+
{
|
|
1371
|
+
className: (0, import_clsx.default)(
|
|
1372
|
+
"cb-absolute cb-right-0 cb-top-full cb-z-20 cb-mt-2 cb-flex cb-min-w-[220px] cb-flex-col cb-gap-1 cb-rounded-xl cb-border cb-border-[var(--cb-border)] cb-bg-[var(--cb-surface)] cb-p-1.5 cb-shadow-[var(--cb-shadow-panel)]",
|
|
1373
|
+
classNames.menu
|
|
1374
|
+
),
|
|
1375
|
+
children: [
|
|
1376
|
+
showExpandAction && /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
|
|
1377
|
+
"button",
|
|
1378
|
+
{
|
|
1379
|
+
type: "button",
|
|
1380
|
+
onClick: toggleExpanded,
|
|
1381
|
+
className: (0, import_clsx.default)(
|
|
1382
|
+
"cb-flex cb-w-full cb-items-center cb-gap-2 cb-rounded-lg cb-bg-transparent cb-px-3.5 cb-py-2.5 cb-text-left cb-text-sm cb-text-[var(--cb-muted-foreground)] cb-transition hover:cb-bg-[var(--cb-muted)] hover:cb-text-white focus-visible:cb-bg-[var(--cb-muted)] focus-visible:cb-text-white",
|
|
1383
|
+
classNames.menuItem
|
|
1384
|
+
),
|
|
1385
|
+
children: [
|
|
1386
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(ExpandIcon, { size: 16 }),
|
|
1387
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { children: isExpanded ? "Restore window" : "Expand window" })
|
|
1388
|
+
]
|
|
1389
|
+
}
|
|
1390
|
+
),
|
|
1391
|
+
showDownloadAction && /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
|
|
1392
|
+
"button",
|
|
1393
|
+
{
|
|
1394
|
+
type: "button",
|
|
1395
|
+
onClick: downloadTranscript,
|
|
1396
|
+
className: (0, import_clsx.default)(
|
|
1397
|
+
"cb-flex cb-w-full cb-items-center cb-gap-2 cb-rounded-lg cb-bg-transparent cb-px-3.5 cb-py-2.5 cb-text-left cb-text-sm cb-text-[var(--cb-muted-foreground)] cb-transition hover:cb-bg-[var(--cb-muted)] hover:cb-text-white focus-visible:cb-bg-[var(--cb-muted)] focus-visible:cb-text-white",
|
|
1398
|
+
classNames.menuItem
|
|
1399
|
+
),
|
|
1400
|
+
children: [
|
|
1401
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(DownloadIcon, { size: 16 }),
|
|
1402
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { children: "Download transcript" })
|
|
1403
|
+
]
|
|
1404
|
+
}
|
|
1405
|
+
)
|
|
1406
|
+
]
|
|
1407
|
+
}
|
|
1408
|
+
),
|
|
1409
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
1410
|
+
"button",
|
|
1411
|
+
{
|
|
1412
|
+
type: "button",
|
|
1413
|
+
onClick: close,
|
|
1414
|
+
className: "cb-flex cb-h-9 cb-w-9 cb-shrink-0 cb-items-center cb-justify-center cb-rounded-lg cb-bg-transparent cb-p-0 cb-text-[var(--cb-muted-foreground)] cb-transition hover:cb-bg-[var(--cb-muted)] hover:cb-text-white focus-visible:cb-bg-[var(--cb-muted)] focus-visible:cb-text-white",
|
|
1415
|
+
"aria-label": "Close chatbot",
|
|
1416
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(CloseIcon, { size: 18 })
|
|
1417
|
+
}
|
|
1418
|
+
)
|
|
1419
|
+
] })
|
|
1420
|
+
]
|
|
1421
|
+
}
|
|
1422
|
+
),
|
|
1423
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
1424
|
+
"section",
|
|
1425
|
+
{
|
|
1426
|
+
ref: messagesScrollRef,
|
|
1427
|
+
className: (0, import_clsx.default)(
|
|
1428
|
+
"cb-chat-scroll cb-flex-1 cb-overflow-y-auto cb-scroll-smooth cb-bg-[var(--cb-background)] cb-px-4 cb-pt-4 cb-pb-36",
|
|
1429
|
+
classNames.body
|
|
1430
|
+
),
|
|
1431
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "cb-space-y-3.5", children: [
|
|
1432
|
+
messages.length === 0 && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "cb-rounded-xl cb-bg-[var(--cb-muted)] cb-p-3 cb-text-sm cb-text-[var(--cb-muted-foreground)]", children: "Ask anything about this company and the assistant will answer using your RAG knowledge base." }),
|
|
1433
|
+
messages.map((message) => {
|
|
1434
|
+
const isUser = message.role === "user";
|
|
1435
|
+
return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
1436
|
+
"article",
|
|
1437
|
+
{
|
|
1438
|
+
className: (0, import_clsx.default)(
|
|
1439
|
+
"cb-flex",
|
|
1440
|
+
isUser ? "cb-justify-end cb-animate-cb-slide-fade-in-right" : "cb-justify-start cb-animate-cb-slide-fade-in-left"
|
|
1441
|
+
),
|
|
1442
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
|
|
1443
|
+
"div",
|
|
1444
|
+
{
|
|
1445
|
+
className: (0, import_clsx.default)(
|
|
1446
|
+
"cb-max-w-[85%] cb-rounded-2xl cb-px-4 cb-py-2",
|
|
1447
|
+
isUser ? "cb-rounded-br-md cb-bg-[var(--cb-user-bubble)] cb-text-[var(--cb-user-text)]" : "cb-rounded-bl-md cb-bg-[var(--cb-assistant-bubble)] cb-text-[var(--cb-assistant-text)]"
|
|
1448
|
+
),
|
|
1449
|
+
children: [
|
|
1450
|
+
message.role === "assistant" && message.status === "streaming" && !message.content ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(AwaitingDots, {}) : message.role === "assistant" ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(MessageMarkdown, { content: message.content }) : /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("p", { className: "cb-message-text cb-m-0 cb-whitespace-pre-wrap cb-text-base cb-leading-6", children: message.content }),
|
|
1451
|
+
message.attachments && message.attachments.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "cb-mt-2 cb-flex cb-flex-wrap cb-gap-2", children: message.attachments.map((attachment) => /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
|
|
1452
|
+
"span",
|
|
1453
|
+
{
|
|
1454
|
+
className: "cb-inline-flex cb-items-center cb-gap-2 cb-rounded-full cb-bg-black/10 cb-px-2 cb-py-1 cb-text-xs",
|
|
1455
|
+
children: [
|
|
1456
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { children: attachment.name }),
|
|
1457
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "cb-opacity-70", children: formatFileSize(attachment.size) })
|
|
1458
|
+
]
|
|
1459
|
+
},
|
|
1460
|
+
attachment.id
|
|
1461
|
+
)) })
|
|
1462
|
+
]
|
|
1463
|
+
}
|
|
1464
|
+
)
|
|
1465
|
+
},
|
|
1466
|
+
message.id
|
|
1467
|
+
);
|
|
1468
|
+
}),
|
|
1469
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", {})
|
|
1470
|
+
] })
|
|
1471
|
+
}
|
|
1472
|
+
),
|
|
1473
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
|
|
1474
|
+
"footer",
|
|
1475
|
+
{
|
|
1476
|
+
className: (0, import_clsx.default)(
|
|
1477
|
+
"cb-absolute cb-bottom-4 cb-left-4 cb-right-4 cb-z-10 cb-bg-transparent cb-p-0",
|
|
1478
|
+
classNames.footer
|
|
1479
|
+
),
|
|
1480
|
+
children: [
|
|
1481
|
+
uploadEnabled && queuedFiles.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "cb-mb-2 cb-flex cb-flex-wrap cb-gap-2", children: queuedFiles.map((file) => {
|
|
1482
|
+
const fileId = createIdFromFile(file);
|
|
1483
|
+
return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
|
|
1484
|
+
"button",
|
|
1485
|
+
{
|
|
1486
|
+
type: "button",
|
|
1487
|
+
className: "cb-inline-flex cb-items-center cb-gap-2 cb-rounded-full cb-bg-[var(--cb-muted)] cb-px-3 cb-py-1 cb-text-xs cb-text-[var(--cb-muted-foreground)]",
|
|
1488
|
+
onClick: () => removeQueuedFile(fileId),
|
|
1489
|
+
"aria-label": `Remove ${file.name}`,
|
|
1490
|
+
children: [
|
|
1491
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "cb-max-w-[140px] cb-truncate", children: file.name }),
|
|
1492
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { children: "x" })
|
|
1493
|
+
]
|
|
1494
|
+
},
|
|
1495
|
+
fileId
|
|
1496
|
+
);
|
|
1497
|
+
}) }),
|
|
1498
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("form", { onSubmit, children: [
|
|
1499
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
|
|
1500
|
+
"div",
|
|
1501
|
+
{
|
|
1502
|
+
className: (0, import_clsx.default)(
|
|
1503
|
+
"cb-flex cb-items-end cb-gap-2 cb-rounded-2xl cb-bg-[var(--cb-surface)] cb-p-2 cb-shadow-[0_18px_35px_-24px_rgba(0,0,0,0.85)] cb-transition",
|
|
1504
|
+
classNames.composer
|
|
1505
|
+
),
|
|
1506
|
+
children: [
|
|
1507
|
+
uploadEnabled && /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_jsx_runtime4.Fragment, { children: [
|
|
1508
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
1509
|
+
"input",
|
|
1510
|
+
{
|
|
1511
|
+
type: "file",
|
|
1512
|
+
multiple: true,
|
|
1513
|
+
ref: fileInputRef,
|
|
1514
|
+
className: "cb-hidden",
|
|
1515
|
+
onChange: onPickFiles,
|
|
1516
|
+
"aria-label": "Add attachments"
|
|
1517
|
+
}
|
|
1518
|
+
),
|
|
1519
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
1520
|
+
"button",
|
|
1521
|
+
{
|
|
1522
|
+
type: "button",
|
|
1523
|
+
className: "cb-flex cb-h-10 cb-w-10 cb-shrink-0 cb-items-center cb-justify-center cb-rounded-lg cb-bg-transparent cb-p-0 cb-text-[var(--cb-muted-foreground)] cb-transition hover:cb-bg-[var(--cb-muted)] hover:cb-text-white focus-visible:cb-bg-[var(--cb-muted)] focus-visible:cb-text-white",
|
|
1524
|
+
onClick: () => fileInputRef.current?.click(),
|
|
1525
|
+
"aria-label": "Attach files",
|
|
1526
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(AttachIcon, { size: 18 })
|
|
1527
|
+
}
|
|
1528
|
+
)
|
|
1529
|
+
] }),
|
|
1530
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "cb-flex-1", children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
1531
|
+
"textarea",
|
|
1532
|
+
{
|
|
1533
|
+
ref: inputRef,
|
|
1534
|
+
value: inputValue,
|
|
1535
|
+
onChange: (event) => setInputValue(event.target.value),
|
|
1536
|
+
onKeyDown: (event) => {
|
|
1537
|
+
if (event.key === "Enter" && !event.shiftKey) {
|
|
1538
|
+
event.preventDefault();
|
|
1539
|
+
void onSubmit(event);
|
|
1540
|
+
}
|
|
1541
|
+
},
|
|
1542
|
+
rows: 1,
|
|
1543
|
+
placeholder: "Ask a question...",
|
|
1544
|
+
className: (0, import_clsx.default)(
|
|
1545
|
+
"cb-composer-input cb-min-h-[38px] cb-w-full cb-resize-none cb-bg-transparent cb-px-2 cb-py-1.5 cb-text-sm cb-leading-6 cb-text-[var(--cb-surface-foreground)] cb-outline-none placeholder:cb-text-[var(--cb-muted-foreground)]",
|
|
1546
|
+
classNames.input
|
|
1547
|
+
)
|
|
1548
|
+
}
|
|
1549
|
+
) }),
|
|
1550
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
1551
|
+
"button",
|
|
1552
|
+
{
|
|
1553
|
+
type: "submit",
|
|
1554
|
+
disabled: !canSend,
|
|
1555
|
+
className: (0, import_clsx.default)(
|
|
1556
|
+
"cb-flex cb-h-10 cb-w-10 cb-shrink-0 cb-items-center cb-justify-center cb-rounded-full cb-bg-white cb-p-0 cb-text-[#111111] cb-transition cb-duration-200 hover:cb-scale-105 hover:cb-brightness-95 hover:cb-text-[#111111] active:cb-scale-95 focus-visible:cb-text-[#111111] disabled:cb-cursor-not-allowed disabled:cb-bg-white/70 disabled:cb-text-[#111111] disabled:cb-opacity-100",
|
|
1557
|
+
classNames.sendButton
|
|
1558
|
+
),
|
|
1559
|
+
"aria-label": "Send message",
|
|
1560
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(SendIcon, { size: 18 })
|
|
1561
|
+
}
|
|
1562
|
+
)
|
|
1563
|
+
]
|
|
1564
|
+
}
|
|
1565
|
+
),
|
|
1566
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("p", { className: "cb-m-0 cb-mt-1 cb-px-1 cb-text-center cb-text-xs cb-leading-4 cb-text-[var(--cb-muted-foreground)]", children: disclaimerText })
|
|
1567
|
+
] })
|
|
1568
|
+
]
|
|
1569
|
+
}
|
|
1570
|
+
)
|
|
1571
|
+
]
|
|
1572
|
+
}
|
|
1573
|
+
),
|
|
1574
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
1575
|
+
"button",
|
|
1576
|
+
{
|
|
1577
|
+
type: "button",
|
|
1578
|
+
onClick: isOpen ? close : open,
|
|
1579
|
+
className: (0, import_clsx.default)(
|
|
1580
|
+
"cb-pointer-events-auto cb-group cb-flex cb-h-14 cb-w-14 cb-items-center cb-justify-center cb-rounded-full cb-border cb-border-[var(--cb-border)] cb-bg-[#111111] cb-text-[#f5f5f5] cb-shadow-[var(--cb-shadow-bubble)] cb-transition cb-duration-300 hover:cb-scale-110 hover:cb-bg-[#262626] hover:cb-shadow-[0_24px_50px_-18px_rgba(0,0,0,0.9)] active:cb-scale-95",
|
|
1581
|
+
classNames.bubble
|
|
1582
|
+
),
|
|
1583
|
+
"aria-label": isOpen ? "Close chatbot" : "Open chatbot",
|
|
1584
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("span", { className: "cb-relative cb-flex cb-h-6 cb-w-6 cb-items-center cb-justify-center", children: [
|
|
1585
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
1586
|
+
LauncherClosedIcon,
|
|
1587
|
+
{
|
|
1588
|
+
size: 22,
|
|
1589
|
+
className: (0, import_clsx.default)(
|
|
1590
|
+
"cb-absolute cb-fill-current cb-transition-all cb-duration-300 cb-ease-out",
|
|
1591
|
+
isOpen ? "cb-rotate-90 cb-opacity-0" : "cb-rotate-0 cb-opacity-100"
|
|
1592
|
+
)
|
|
1593
|
+
}
|
|
1594
|
+
),
|
|
1595
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
1596
|
+
LauncherOpenIcon,
|
|
1597
|
+
{
|
|
1598
|
+
size: 22,
|
|
1599
|
+
className: (0, import_clsx.default)(
|
|
1600
|
+
"cb-absolute cb-transition-all cb-duration-300 cb-ease-out",
|
|
1601
|
+
isOpen ? "cb-rotate-0 cb-opacity-100" : "-cb-rotate-90 cb-opacity-0"
|
|
1602
|
+
)
|
|
1603
|
+
}
|
|
1604
|
+
)
|
|
1605
|
+
] })
|
|
1606
|
+
}
|
|
1607
|
+
)
|
|
1608
|
+
] })
|
|
1609
|
+
}
|
|
1610
|
+
);
|
|
1611
|
+
}
|
|
1612
|
+
|
|
1613
|
+
// src/chatbot/components/chatbot-page-config.tsx
|
|
1614
|
+
var import_react3 = require("react");
|
|
1615
|
+
function ChatbotPageConfig(props) {
|
|
1616
|
+
const { registerPageConfig, unregisterPageConfig } = useChatbotContext();
|
|
1617
|
+
const configId = (0, import_react3.useRef)(createId("pagecfg"));
|
|
1618
|
+
const stableConfig = (0, import_react3.useMemo)(() => props, [props]);
|
|
1619
|
+
(0, import_react3.useEffect)(() => {
|
|
1620
|
+
registerPageConfig(configId.current, stableConfig);
|
|
1621
|
+
return () => unregisterPageConfig(configId.current);
|
|
1622
|
+
}, [registerPageConfig, stableConfig, unregisterPageConfig]);
|
|
1623
|
+
return null;
|
|
1624
|
+
}
|
|
1625
|
+
|
|
1626
|
+
// src/chatbot/hooks/use-chatbot.ts
|
|
1627
|
+
function useChatbot() {
|
|
1628
|
+
return useChatbotContext();
|
|
1629
|
+
}
|
|
1630
|
+
|
|
1631
|
+
// src/chatbot/hooks/use-chatbot-session.ts
|
|
1632
|
+
var import_react4 = require("react");
|
|
1633
|
+
function useChatbotSession() {
|
|
1634
|
+
const { sessionId, resetSession } = useChatbotContext();
|
|
1635
|
+
return (0, import_react4.useMemo)(
|
|
1636
|
+
() => ({
|
|
1637
|
+
sessionId,
|
|
1638
|
+
resetSession
|
|
1639
|
+
}),
|
|
1640
|
+
[resetSession, sessionId]
|
|
1641
|
+
);
|
|
1642
|
+
}
|
|
1643
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
1644
|
+
0 && (module.exports = {
|
|
1645
|
+
ChatbotPageConfig,
|
|
1646
|
+
ChatbotProvider,
|
|
1647
|
+
ChatbotWidget,
|
|
1648
|
+
useChatbot,
|
|
1649
|
+
useChatbotSession
|
|
1650
|
+
});
|
|
1651
|
+
//# sourceMappingURL=index.js.map
|