@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/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