@aiassist-secure/react 1.0.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.mjs ADDED
@@ -0,0 +1,1396 @@
1
+ // src/AiAssistChat.tsx
2
+ import {
3
+ useState as useState2,
4
+ useEffect as useEffect2,
5
+ useRef as useRef2,
6
+ useCallback as useCallback2,
7
+ useMemo as useMemo2
8
+ } from "react";
9
+
10
+ // src/context.tsx
11
+ import { createContext, useContext, useMemo } from "react";
12
+ import { jsx } from "react/jsx-runtime";
13
+ var DEFAULT_THEME = {
14
+ primary: "#00D4FF",
15
+ background: "#0A0A0B",
16
+ surface: "#1A1A1B",
17
+ text: "#FFFFFF",
18
+ textMuted: "rgba(255,255,255,0.5)",
19
+ border: "rgba(255,255,255,0.1)",
20
+ radius: "16px",
21
+ fontFamily: "-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif"
22
+ };
23
+ var ThemeContext = createContext(null);
24
+ function AiAssistThemeProvider({
25
+ theme,
26
+ children
27
+ }) {
28
+ const mergedTheme = useMemo(
29
+ () => ({ ...DEFAULT_THEME, ...theme }),
30
+ [theme]
31
+ );
32
+ const cssVars = useMemo(
33
+ () => ({
34
+ "--aa-primary": mergedTheme.primary,
35
+ "--aa-bg": mergedTheme.background,
36
+ "--aa-surface": mergedTheme.surface,
37
+ "--aa-text": mergedTheme.text,
38
+ "--aa-text-muted": mergedTheme.textMuted,
39
+ "--aa-border": mergedTheme.border,
40
+ "--aa-radius": mergedTheme.radius,
41
+ "--aa-font": mergedTheme.fontFamily
42
+ }),
43
+ [mergedTheme]
44
+ );
45
+ const value = useMemo(() => ({ theme: mergedTheme, cssVars }), [mergedTheme, cssVars]);
46
+ return /* @__PURE__ */ jsx(ThemeContext.Provider, { value, children });
47
+ }
48
+ function useAiAssistTheme() {
49
+ const context = useContext(ThemeContext);
50
+ if (!context) {
51
+ return { theme: DEFAULT_THEME, cssVars: {} };
52
+ }
53
+ return context;
54
+ }
55
+
56
+ // src/hooks.ts
57
+ import { useRef, useCallback, useState } from "react";
58
+ var CLIENT_ID_KEY = "aiassist_client_id";
59
+ function generateUUID() {
60
+ return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, (c) => {
61
+ const r = Math.random() * 16 | 0;
62
+ const v = c === "x" ? r : r & 3 | 8;
63
+ return v.toString(16);
64
+ });
65
+ }
66
+ function getOrCreateClientId() {
67
+ if (typeof window === "undefined") {
68
+ return `temp-${Date.now()}-${Math.random().toString(36).substring(2, 11)}`;
69
+ }
70
+ try {
71
+ let clientId = localStorage.getItem(CLIENT_ID_KEY);
72
+ if (!clientId) {
73
+ clientId = generateUUID();
74
+ localStorage.setItem(CLIENT_ID_KEY, clientId);
75
+ }
76
+ return clientId;
77
+ } catch {
78
+ return `temp-${Date.now()}-${Math.random().toString(36).substring(2, 11)}`;
79
+ }
80
+ }
81
+ function useClientId() {
82
+ const [clientId] = useState(() => getOrCreateClientId());
83
+ return clientId;
84
+ }
85
+ function useThrottle(fn, delay) {
86
+ const lastCall = useRef(0);
87
+ return useCallback(
88
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
89
+ ((...args) => {
90
+ const now = Date.now();
91
+ if (now - lastCall.current >= delay) {
92
+ lastCall.current = now;
93
+ fn(...args);
94
+ }
95
+ }),
96
+ [fn, delay]
97
+ );
98
+ }
99
+ function normalizeRole(role) {
100
+ if (role === "manager") return "human";
101
+ return role;
102
+ }
103
+
104
+ // src/api.ts
105
+ var DEFAULT_ENDPOINT = "https://api.aiassist.net";
106
+ var AiAssistAPI = class {
107
+ constructor(config) {
108
+ this.apiKey = config.apiKey;
109
+ this.endpoint = config.endpoint || DEFAULT_ENDPOINT;
110
+ }
111
+ async request(path, options = {}) {
112
+ const res = await fetch(`${this.endpoint}${path}`, {
113
+ ...options,
114
+ headers: {
115
+ "Content-Type": "application/json",
116
+ Authorization: `Bearer ${this.apiKey}`,
117
+ ...options.headers
118
+ }
119
+ });
120
+ if (!res.ok) {
121
+ const error = await res.text().catch(() => "Request failed");
122
+ throw new Error(error);
123
+ }
124
+ return res.json();
125
+ }
126
+ async createWorkspace(options) {
127
+ const data = await this.request("/api/workspaces", {
128
+ method: "POST",
129
+ body: JSON.stringify({
130
+ initial_message: options.initialMessage,
131
+ system_prompt: options.systemPrompt,
132
+ context: options.context,
133
+ client_id: options.clientId
134
+ })
135
+ });
136
+ return {
137
+ workspace: data.workspace,
138
+ messages: data.messages.map((m) => ({
139
+ id: m.id,
140
+ role: normalizeRole(m.role),
141
+ content: m.content,
142
+ workspace_id: m.workspace_id,
143
+ created_at: m.created_at
144
+ }))
145
+ };
146
+ }
147
+ async sendMessage(workspaceId, content) {
148
+ const data = await this.request(`/api/workspaces/${workspaceId}/messages`, {
149
+ method: "POST",
150
+ body: JSON.stringify({ content })
151
+ });
152
+ return {
153
+ user_message: {
154
+ id: data.user_message.id,
155
+ role: "user",
156
+ content: data.user_message.content,
157
+ created_at: data.user_message.created_at
158
+ },
159
+ responses: data.responses.map((r) => ({
160
+ id: r.id,
161
+ role: normalizeRole(r.role),
162
+ content: r.content,
163
+ created_at: r.created_at
164
+ })),
165
+ mode: data.mode,
166
+ pending_approval: data.pending_approval
167
+ };
168
+ }
169
+ async getWorkspace(workspaceId) {
170
+ const data = await this.request(`/api/workspaces/${workspaceId}`);
171
+ return data.workspace || { id: data.id, mode: data.mode };
172
+ }
173
+ async getMessages(workspaceId) {
174
+ const data = await this.request(`/api/workspaces/${workspaceId}/messages`);
175
+ return data.messages.map((m) => ({
176
+ id: m.id,
177
+ role: normalizeRole(m.role),
178
+ content: m.content,
179
+ created_at: m.created_at
180
+ }));
181
+ }
182
+ async sendTypingPreview(workspaceId, text) {
183
+ await this.request(`/api/workspaces/${workspaceId}/typing`, {
184
+ method: "POST",
185
+ body: JSON.stringify({ text })
186
+ }).catch(() => {
187
+ });
188
+ }
189
+ async endConversation(workspaceId) {
190
+ await this.request(`/api/workspaces/${workspaceId}/end`, {
191
+ method: "POST"
192
+ }).catch(() => {
193
+ });
194
+ }
195
+ async getWorkspaceByClientId(clientId) {
196
+ const data = await this.request(`/api/workspaces/by-client/${clientId}`);
197
+ return {
198
+ workspace: data.workspace,
199
+ messages: (data.messages || []).map((m) => ({
200
+ id: m.id,
201
+ role: normalizeRole(m.role),
202
+ content: m.content,
203
+ created_at: m.created_at
204
+ })),
205
+ exists: data.exists
206
+ };
207
+ }
208
+ };
209
+
210
+ // src/AiAssistChat.tsx
211
+ import { jsx as jsx2, jsxs } from "react/jsx-runtime";
212
+ var LEAD_STORAGE_KEY = "aai_lead_captured";
213
+ var WAITING_MSG = {
214
+ id: "waiting-human",
215
+ role: "system",
216
+ content: "You've been connected to our support team. A specialist will be with you shortly. Thank you for your patience."
217
+ };
218
+ var DEFAULT_SUGGESTIONS = [
219
+ "How can you help me?",
220
+ "Tell me about your services",
221
+ "I have a question",
222
+ "Connect me to support"
223
+ ];
224
+ function UserIcon({ className }) {
225
+ return /* @__PURE__ */ jsxs(
226
+ "svg",
227
+ {
228
+ className,
229
+ viewBox: "0 0 24 24",
230
+ fill: "none",
231
+ stroke: "currentColor",
232
+ strokeWidth: "2",
233
+ children: [
234
+ /* @__PURE__ */ jsx2("path", { d: "M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2" }),
235
+ /* @__PURE__ */ jsx2("circle", { cx: "12", cy: "7", r: "4" })
236
+ ]
237
+ }
238
+ );
239
+ }
240
+ function SparklesIcon({ className }) {
241
+ return /* @__PURE__ */ jsxs(
242
+ "svg",
243
+ {
244
+ className,
245
+ viewBox: "0 0 24 24",
246
+ fill: "none",
247
+ stroke: "currentColor",
248
+ strokeWidth: "2",
249
+ children: [
250
+ /* @__PURE__ */ jsx2("path", { d: "M12 3l1.5 4.5L18 9l-4.5 1.5L12 15l-1.5-4.5L6 9l4.5-1.5L12 3z" }),
251
+ /* @__PURE__ */ jsx2("path", { d: "M19 15l.5 1.5L21 17l-1.5.5L19 19l-.5-1.5L17 17l1.5-.5L19 15z" }),
252
+ /* @__PURE__ */ jsx2("path", { d: "M5 17l.5 1.5L7 19l-1.5.5L5 21l-.5-1.5L3 19l1.5-.5L5 17z" })
253
+ ]
254
+ }
255
+ );
256
+ }
257
+ function SendIcon({ className }) {
258
+ return /* @__PURE__ */ jsx2(
259
+ "svg",
260
+ {
261
+ className,
262
+ viewBox: "0 0 24 24",
263
+ fill: "none",
264
+ stroke: "currentColor",
265
+ strokeWidth: "2",
266
+ children: /* @__PURE__ */ jsx2("path", { d: "M12 19V5M5 12l7-7 7 7" })
267
+ }
268
+ );
269
+ }
270
+ function LockIcon({ className }) {
271
+ return /* @__PURE__ */ jsx2("svg", { className, viewBox: "0 0 24 24", fill: "currentColor", children: /* @__PURE__ */ jsx2("path", { d: "M18 8h-1V6c0-2.76-2.24-5-5-5S7 3.24 7 6v2H6c-1.1 0-2 .9-2 2v10c0 1.1.9 2 2 2h12c1.1 0 2-.9 2-2V10c0-1.1-.9-2-2-2zm-6 9c-1.1 0-2-.9-2-2s.9-2 2-2 2 .9 2 2-.9 2-2 2zm3.1-9H8.9V6c0-1.71 1.39-3.1 3.1-3.1 1.71 0 3.1 1.39 3.1 3.1v2z" }) });
272
+ }
273
+ function MailIcon({ className }) {
274
+ return /* @__PURE__ */ jsx2("svg", { className, viewBox: "0 0 24 24", fill: "currentColor", children: /* @__PURE__ */ jsx2("path", { d: "M20 4H4c-1.1 0-2 .9-2 2v12c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V6c0-1.1-.9-2-2-2zm0 4l-8 5-8-5V6l8 5 8-5v2z" }) });
275
+ }
276
+ function ChatInner({
277
+ config,
278
+ onClose,
279
+ className = "",
280
+ showHeader = true,
281
+ showSuggestions = true,
282
+ suggestions = DEFAULT_SUGGESTIONS,
283
+ placeholder,
284
+ title = "AiAssist",
285
+ subtitle
286
+ }) {
287
+ const { theme, cssVars } = useAiAssistTheme();
288
+ const [messages, setMessages] = useState2([]);
289
+ const [input, setInput] = useState2("");
290
+ const [isTyping, setIsTyping] = useState2(false);
291
+ const [mode, setMode] = useState2("ai");
292
+ const [workspaceId, setWorkspaceId] = useState2(null);
293
+ const [showWelcome, setShowWelcome] = useState2(true);
294
+ const [showWaitingMessage, setShowWaitingMessage] = useState2(false);
295
+ const [awaitingApproval, setAwaitingApproval] = useState2(false);
296
+ const [staffAvailability, setStaffAvailability] = useState2("online");
297
+ const [availabilityMessage, setAvailabilityMessage] = useState2(null);
298
+ const [autoOfflineMessage, setAutoOfflineMessage] = useState2(null);
299
+ const humanMsgCountAtTakeover = useRef2(0);
300
+ const lastKnownHumanCount = useRef2(0);
301
+ const scrollRef = useRef2(null);
302
+ const prevModeRef = useRef2("ai");
303
+ const shouldAutoScroll = useRef2(true);
304
+ const scrollToNewAiMessage = useRef2(false);
305
+ const lastAiMessageRef = useRef2(null);
306
+ const requireEmail = config.requireEmail !== false;
307
+ const [isLeadCaptured, setIsLeadCaptured] = useState2(!requireEmail);
308
+ const [leadInfo, setLeadInfo] = useState2(null);
309
+ const [emailInput, setEmailInput] = useState2("");
310
+ const [emailError, setEmailError] = useState2("");
311
+ const [isCapturingEmail, setIsCapturingEmail] = useState2(false);
312
+ const api = useMemo2(() => new AiAssistAPI(config), [config]);
313
+ const clientId = useClientId();
314
+ useEffect2(() => {
315
+ if (!requireEmail) {
316
+ setIsLeadCaptured(true);
317
+ return;
318
+ }
319
+ try {
320
+ const stored = localStorage.getItem(LEAD_STORAGE_KEY);
321
+ if (stored) {
322
+ const data = JSON.parse(stored);
323
+ if (data.leadId && data.email) {
324
+ setIsLeadCaptured(true);
325
+ setLeadInfo(data);
326
+ }
327
+ }
328
+ } catch {
329
+ }
330
+ if (clientId) {
331
+ checkLeadOnServer();
332
+ }
333
+ }, [clientId, requireEmail]);
334
+ useEffect2(() => {
335
+ const fetchAvailability = async () => {
336
+ try {
337
+ const res = await fetch(`${config.endpoint || "https://api.aiassist.net"}/v1/availability`, {
338
+ headers: { "Authorization": `Bearer ${config.apiKey}` }
339
+ });
340
+ if (res.ok) {
341
+ const data = await res.json();
342
+ setStaffAvailability(data.availability || "online");
343
+ setAvailabilityMessage(data.message || null);
344
+ setAutoOfflineMessage(data.auto_offline_message || null);
345
+ }
346
+ } catch (e) {
347
+ console.log("[AiAssist] Failed to fetch availability:", e);
348
+ }
349
+ };
350
+ fetchAvailability();
351
+ }, [config.apiKey, config.endpoint]);
352
+ const checkLeadOnServer = async () => {
353
+ if (!clientId) return;
354
+ try {
355
+ const res = await fetch(`${config.endpoint || "https://api.aiassist.net"}/api/leads/check/${clientId}`, {
356
+ headers: { "X-API-Key": config.apiKey }
357
+ });
358
+ if (res.ok) {
359
+ const data = await res.json();
360
+ if (data.captured && data.leadId && data.email) {
361
+ setIsLeadCaptured(true);
362
+ setLeadInfo({ leadId: data.leadId, email: data.email });
363
+ localStorage.setItem(LEAD_STORAGE_KEY, JSON.stringify({ leadId: data.leadId, email: data.email }));
364
+ }
365
+ }
366
+ } catch {
367
+ }
368
+ };
369
+ const handleEmailCapture = async (e) => {
370
+ e.preventDefault();
371
+ if (!emailInput.trim()) {
372
+ setEmailError("Please enter your email");
373
+ return;
374
+ }
375
+ const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
376
+ if (!emailRegex.test(emailInput)) {
377
+ setEmailError("Please enter a valid email");
378
+ return;
379
+ }
380
+ setIsCapturingEmail(true);
381
+ setEmailError("");
382
+ try {
383
+ const res = await fetch(`${config.endpoint || "https://api.aiassist.net"}/api/leads/capture`, {
384
+ method: "POST",
385
+ headers: { "Content-Type": "application/json", "X-API-Key": config.apiKey },
386
+ body: JSON.stringify({ email: emailInput.trim(), client_id: clientId, source: "sdk" })
387
+ });
388
+ if (!res.ok) throw new Error("Failed");
389
+ const data = await res.json();
390
+ const lead = { leadId: data.lead_id, email: emailInput.trim() };
391
+ setIsLeadCaptured(true);
392
+ setLeadInfo(lead);
393
+ localStorage.setItem(LEAD_STORAGE_KEY, JSON.stringify(lead));
394
+ config.onLeadCapture?.(lead);
395
+ } catch {
396
+ setEmailError("Something went wrong. Try again.");
397
+ } finally {
398
+ setIsCapturingEmail(false);
399
+ }
400
+ };
401
+ useEffect2(() => {
402
+ if (!clientId) return;
403
+ const loadExistingChat = async () => {
404
+ try {
405
+ const data = await api.getWorkspaceByClientId(clientId);
406
+ if (data.exists && data.workspace) {
407
+ setWorkspaceId(data.workspace.id);
408
+ setShowWelcome(false);
409
+ if (data.messages.length > 0) {
410
+ setMessages(data.messages);
411
+ }
412
+ const isHumanMode = data.workspace.mode === "human" || data.workspace.mode === "takeover";
413
+ if (isHumanMode) {
414
+ setMode("human");
415
+ const hasHumanResponse = data.messages.some((m) => m.role === "human");
416
+ setShowWaitingMessage(!hasHumanResponse);
417
+ }
418
+ }
419
+ } catch (error) {
420
+ console.warn("AiAssist: Failed to load existing chat", error);
421
+ }
422
+ };
423
+ loadExistingChat();
424
+ }, [clientId, api]);
425
+ const sendTypingPreview = useCallback2(
426
+ async (wsId, text) => {
427
+ await api.sendTypingPreview(wsId, text);
428
+ },
429
+ [api]
430
+ );
431
+ const throttledTypingPreview = useThrottle(sendTypingPreview, 300);
432
+ const checkIfNearBottom = useCallback2(() => {
433
+ if (!scrollRef.current) return true;
434
+ const { scrollTop, scrollHeight, clientHeight } = scrollRef.current;
435
+ return scrollHeight - scrollTop - clientHeight < 100;
436
+ }, []);
437
+ const handleScroll = useCallback2(() => {
438
+ shouldAutoScroll.current = checkIfNearBottom();
439
+ }, [checkIfNearBottom]);
440
+ useEffect2(() => {
441
+ if (scrollToNewAiMessage.current && lastAiMessageRef.current && scrollRef.current) {
442
+ const messageTop = lastAiMessageRef.current.offsetTop - 20;
443
+ scrollRef.current.scrollTop = messageTop;
444
+ scrollToNewAiMessage.current = false;
445
+ } else if (scrollRef.current && shouldAutoScroll.current && isTyping) {
446
+ scrollRef.current.scrollTop = scrollRef.current.scrollHeight;
447
+ }
448
+ }, [messages, isTyping]);
449
+ useEffect2(() => {
450
+ if (!workspaceId) return;
451
+ const pollWorkspace = async () => {
452
+ try {
453
+ const [workspace, serverMsgs] = await Promise.all([
454
+ api.getWorkspace(workspaceId),
455
+ api.getMessages(workspaceId)
456
+ ]);
457
+ const currentHumanMsgCount = serverMsgs.filter(
458
+ (m) => m.role === "human"
459
+ ).length;
460
+ const previousCount = lastKnownHumanCount.current;
461
+ lastKnownHumanCount.current = currentHumanMsgCount;
462
+ const isHumanMode = workspace.mode === "human" || workspace.mode === "takeover";
463
+ if (isHumanMode && mode === "ai") {
464
+ setMode("human");
465
+ humanMsgCountAtTakeover.current = previousCount;
466
+ if (currentHumanMsgCount > previousCount) {
467
+ setShowWaitingMessage(false);
468
+ } else {
469
+ setShowWaitingMessage(true);
470
+ }
471
+ } else if (!isHumanMode && mode === "human") {
472
+ setMode("ai");
473
+ setShowWaitingMessage(false);
474
+ humanMsgCountAtTakeover.current = 0;
475
+ } else if (isHumanMode && mode === "human" && showWaitingMessage) {
476
+ if (currentHumanMsgCount > humanMsgCountAtTakeover.current) {
477
+ setShowWaitingMessage(false);
478
+ }
479
+ }
480
+ setMessages((prev) => {
481
+ const hasNewAiMessage = serverMsgs.some(
482
+ (m) => (m.role === "ai" || m.role === "human") && !prev.some((prevMsg) => prevMsg.id === m.id)
483
+ );
484
+ if (hasNewAiMessage) {
485
+ setAwaitingApproval(false);
486
+ setIsTyping(false);
487
+ }
488
+ const serverIds = new Set(serverMsgs.map((m) => m.id));
489
+ const merged = /* @__PURE__ */ new Map();
490
+ serverMsgs.forEach((m) => merged.set(m.id, m));
491
+ prev.forEach((m) => {
492
+ if (!serverIds.has(m.id) && m.id.startsWith("temp-")) {
493
+ merged.set(m.id, m);
494
+ }
495
+ });
496
+ return Array.from(merged.values()).sort((a, b) => {
497
+ if (a.created_at && b.created_at) {
498
+ return new Date(a.created_at).getTime() - new Date(b.created_at).getTime();
499
+ }
500
+ return 0;
501
+ });
502
+ });
503
+ } catch (error) {
504
+ console.error("Failed to poll workspace:", error);
505
+ config.onError?.(error);
506
+ }
507
+ };
508
+ pollWorkspace();
509
+ const interval = setInterval(pollWorkspace, 3e3);
510
+ return () => clearInterval(interval);
511
+ }, [workspaceId, mode, showWaitingMessage, api, config]);
512
+ useEffect2(() => {
513
+ if (prevModeRef.current !== mode) {
514
+ config.onModeChange?.(mode);
515
+ prevModeRef.current = mode;
516
+ }
517
+ }, [mode, config]);
518
+ const startConversation = async (initialMessage) => {
519
+ if (!initialMessage.trim()) return;
520
+ setShowWelcome(false);
521
+ setIsTyping(true);
522
+ const tempUserMsg = {
523
+ id: Date.now().toString(),
524
+ role: "user",
525
+ content: initialMessage
526
+ };
527
+ setMessages([tempUserMsg]);
528
+ scrollToNewAiMessage.current = true;
529
+ try {
530
+ const data = await api.createWorkspace({
531
+ initialMessage,
532
+ systemPrompt: config.systemPrompt,
533
+ context: config.context,
534
+ clientId
535
+ });
536
+ setWorkspaceId(data.workspace.id);
537
+ const isHumanMode = data.workspace.mode === "human" || data.workspace.mode === "takeover";
538
+ if (isHumanMode) {
539
+ setMode("human");
540
+ const hasHumanResponse = data.messages.some((m) => m.role === "human");
541
+ setShowWaitingMessage(!hasHumanResponse);
542
+ }
543
+ setMessages(data.messages);
544
+ data.messages.forEach((m) => config.onMessage?.(m));
545
+ } catch (error) {
546
+ console.error("Failed to start conversation:", error);
547
+ config.onError?.(error);
548
+ setMessages([
549
+ tempUserMsg,
550
+ {
551
+ id: Date.now().toString() + "-fallback",
552
+ role: "ai",
553
+ content: "I apologize, but I'm having trouble connecting. Please try again in a moment."
554
+ }
555
+ ]);
556
+ } finally {
557
+ setIsTyping(false);
558
+ }
559
+ };
560
+ const handleSend = async () => {
561
+ if (!input.trim() || !isLeadCaptured) return;
562
+ if (!workspaceId) {
563
+ const msg = input;
564
+ setInput("");
565
+ await startConversation(msg);
566
+ return;
567
+ }
568
+ const userContent = input;
569
+ setInput("");
570
+ if (workspaceId) {
571
+ sendTypingPreview(workspaceId, "");
572
+ }
573
+ const tempUserMsg = {
574
+ id: `temp-${Date.now()}`,
575
+ role: "user",
576
+ content: userContent
577
+ };
578
+ setMessages((prev) => [...prev, tempUserMsg]);
579
+ setIsTyping(true);
580
+ scrollToNewAiMessage.current = true;
581
+ try {
582
+ const data = await api.sendMessage(workspaceId, userContent);
583
+ const wasAiMode = mode === "ai";
584
+ const isHumanMode = data.mode === "human" || data.mode === "takeover";
585
+ if (isHumanMode && wasAiMode) {
586
+ setMode("human");
587
+ humanMsgCountAtTakeover.current = messages.filter(
588
+ (m) => m.role === "human"
589
+ ).length;
590
+ }
591
+ const hasHumanResponseNow = data.responses.some(
592
+ (r) => r.role === "human"
593
+ );
594
+ if (data.pending_approval) {
595
+ setAwaitingApproval(true);
596
+ } else {
597
+ setAwaitingApproval(false);
598
+ setIsTyping(false);
599
+ }
600
+ setMessages((prev) => {
601
+ const merged = /* @__PURE__ */ new Map();
602
+ prev.filter((m) => m.id !== tempUserMsg.id).forEach((m) => merged.set(m.id, m));
603
+ merged.set(data.user_message.id, data.user_message);
604
+ data.responses.forEach((r) => merged.set(r.id, r));
605
+ if (hasHumanResponseNow) {
606
+ setShowWaitingMessage(false);
607
+ } else if (isHumanMode && wasAiMode) {
608
+ setShowWaitingMessage(true);
609
+ }
610
+ return Array.from(merged.values());
611
+ });
612
+ config.onMessage?.(data.user_message);
613
+ data.responses.forEach((r) => config.onMessage?.(r));
614
+ } catch (error) {
615
+ console.error("Failed to send message:", error);
616
+ config.onError?.(error);
617
+ setAwaitingApproval(false);
618
+ setMessages((prev) => [
619
+ ...prev,
620
+ {
621
+ id: Date.now().toString(),
622
+ role: "ai",
623
+ content: "I apologize, but I'm having trouble connecting. Please try again in a moment."
624
+ }
625
+ ]);
626
+ setIsTyping(false);
627
+ }
628
+ };
629
+ const currentPlaceholder = placeholder || (mode === "ai" ? "Type your message..." : "A specialist is listening...");
630
+ const currentSubtitle = subtitle || (mode === "ai" ? "AI Assistant" : "Human Support");
631
+ return /* @__PURE__ */ jsxs(
632
+ "div",
633
+ {
634
+ className: `aa-chat ${className}`,
635
+ style: {
636
+ ...cssVars,
637
+ display: "flex",
638
+ flexDirection: "column",
639
+ height: "100%",
640
+ backgroundColor: theme.background,
641
+ color: theme.text,
642
+ fontFamily: theme.fontFamily,
643
+ borderRadius: theme.radius,
644
+ overflow: "hidden",
645
+ position: "relative"
646
+ },
647
+ "data-testid": "aiassist-chat",
648
+ children: [
649
+ showHeader && /* @__PURE__ */ jsxs(
650
+ "div",
651
+ {
652
+ style: {
653
+ padding: "16px 20px",
654
+ backgroundColor: theme.surface,
655
+ borderBottom: `1px solid ${theme.border}`,
656
+ display: "flex",
657
+ alignItems: "center",
658
+ justifyContent: "space-between"
659
+ },
660
+ "data-testid": "chat-header",
661
+ children: [
662
+ /* @__PURE__ */ jsxs("div", { style: { display: "flex", alignItems: "center", gap: "12px" }, children: [
663
+ /* @__PURE__ */ jsx2(
664
+ "div",
665
+ {
666
+ style: {
667
+ width: "40px",
668
+ height: "40px",
669
+ borderRadius: "50%",
670
+ backgroundColor: mode === "ai" ? `${theme.primary}20` : `${theme.text}10`,
671
+ border: `1px solid ${mode === "ai" ? `${theme.primary}40` : theme.border}`,
672
+ display: "flex",
673
+ alignItems: "center",
674
+ justifyContent: "center"
675
+ },
676
+ children: mode === "ai" ? /* @__PURE__ */ jsx2(
677
+ SparklesIcon,
678
+ {
679
+ className: "aa-icon"
680
+ }
681
+ ) : /* @__PURE__ */ jsx2(UserIcon, { className: "aa-icon" })
682
+ }
683
+ ),
684
+ /* @__PURE__ */ jsxs("div", { children: [
685
+ /* @__PURE__ */ jsx2("div", { style: { fontWeight: 600, fontSize: "14px" }, children: title }),
686
+ /* @__PURE__ */ jsxs(
687
+ "div",
688
+ {
689
+ style: {
690
+ fontSize: "12px",
691
+ color: theme.textMuted,
692
+ display: "flex",
693
+ alignItems: "center",
694
+ gap: "6px"
695
+ },
696
+ "data-testid": "status-staff-availability",
697
+ children: [
698
+ /* @__PURE__ */ jsx2(
699
+ "span",
700
+ {
701
+ style: {
702
+ width: "6px",
703
+ height: "6px",
704
+ borderRadius: "50%",
705
+ backgroundColor: staffAvailability === "offline" ? "#6b7280" : staffAvailability === "away" ? "#f59e0b" : mode === "ai" ? theme.primary : "#22c55e"
706
+ }
707
+ }
708
+ ),
709
+ staffAvailability === "away" && availabilityMessage ? availabilityMessage : staffAvailability === "offline" && availabilityMessage ? availabilityMessage : currentSubtitle
710
+ ]
711
+ }
712
+ ),
713
+ staffAvailability === "offline" && autoOfflineMessage && /* @__PURE__ */ jsx2(
714
+ "div",
715
+ {
716
+ style: {
717
+ fontSize: "11px",
718
+ color: theme.textMuted,
719
+ marginTop: "4px"
720
+ },
721
+ "data-testid": "status-offline-message",
722
+ children: autoOfflineMessage
723
+ }
724
+ )
725
+ ] })
726
+ ] }),
727
+ onClose && /* @__PURE__ */ jsx2(
728
+ "button",
729
+ {
730
+ onClick: onClose,
731
+ style: {
732
+ background: "transparent",
733
+ border: "none",
734
+ color: theme.textMuted,
735
+ cursor: "pointer",
736
+ padding: "8px",
737
+ fontSize: "20px",
738
+ lineHeight: 1
739
+ },
740
+ "data-testid": "button-close",
741
+ children: "\xD7"
742
+ }
743
+ )
744
+ ]
745
+ }
746
+ ),
747
+ /* @__PURE__ */ jsxs(
748
+ "div",
749
+ {
750
+ ref: scrollRef,
751
+ onScroll: handleScroll,
752
+ style: {
753
+ flex: 1,
754
+ overflowY: "auto",
755
+ padding: "20px"
756
+ },
757
+ "data-testid": "chat-messages",
758
+ children: [
759
+ showWelcome && showSuggestions && /* @__PURE__ */ jsxs(
760
+ "div",
761
+ {
762
+ style: {
763
+ display: "flex",
764
+ flexDirection: "column",
765
+ alignItems: "center",
766
+ justifyContent: "center",
767
+ height: "100%",
768
+ textAlign: "center",
769
+ gap: "24px"
770
+ },
771
+ children: [
772
+ /* @__PURE__ */ jsx2(
773
+ "div",
774
+ {
775
+ style: {
776
+ width: "64px",
777
+ height: "64px",
778
+ borderRadius: "50%",
779
+ backgroundColor: `${theme.primary}15`,
780
+ border: `1px solid ${theme.primary}30`,
781
+ display: "flex",
782
+ alignItems: "center",
783
+ justifyContent: "center"
784
+ },
785
+ children: /* @__PURE__ */ jsx2(SparklesIcon, { className: "aa-icon-lg" })
786
+ }
787
+ ),
788
+ /* @__PURE__ */ jsxs("div", { children: [
789
+ /* @__PURE__ */ jsx2(
790
+ "h2",
791
+ {
792
+ style: {
793
+ fontSize: "24px",
794
+ fontWeight: 700,
795
+ marginBottom: "8px"
796
+ },
797
+ children: config.greeting || "How can I help you today?"
798
+ }
799
+ ),
800
+ /* @__PURE__ */ jsx2("p", { style: { color: theme.textMuted, fontSize: "14px" }, children: "Ask me anything or choose a suggestion below" })
801
+ ] }),
802
+ /* @__PURE__ */ jsx2(
803
+ "div",
804
+ {
805
+ style: {
806
+ display: "grid",
807
+ gridTemplateColumns: "repeat(2, 1fr)",
808
+ gap: "8px",
809
+ width: "100%",
810
+ maxWidth: "400px"
811
+ },
812
+ children: suggestions.map((suggestion, i) => /* @__PURE__ */ jsx2(
813
+ "button",
814
+ {
815
+ onClick: () => setInput(suggestion),
816
+ style: {
817
+ padding: "12px 16px",
818
+ textAlign: "left",
819
+ fontSize: "13px",
820
+ color: theme.textMuted,
821
+ backgroundColor: `${theme.text}05`,
822
+ border: `1px solid ${theme.border}`,
823
+ borderRadius: "12px",
824
+ cursor: "pointer"
825
+ },
826
+ "data-testid": `button-suggestion-${i}`,
827
+ children: suggestion
828
+ },
829
+ i
830
+ ))
831
+ }
832
+ )
833
+ ]
834
+ }
835
+ ),
836
+ messages.map((msg, idx) => {
837
+ const isLastAiMessage = msg.role !== "user" && msg.role !== "system" && !messages.slice(idx + 1).some((m) => m.role !== "user" && m.role !== "system");
838
+ return /* @__PURE__ */ jsxs(
839
+ "div",
840
+ {
841
+ ref: isLastAiMessage ? (el) => {
842
+ lastAiMessageRef.current = el;
843
+ } : void 0,
844
+ style: {
845
+ display: "flex",
846
+ gap: "12px",
847
+ marginBottom: "16px",
848
+ justifyContent: msg.role === "user" ? "flex-end" : "flex-start"
849
+ },
850
+ "data-testid": `message-${msg.role}-${msg.id}`,
851
+ children: [
852
+ msg.role !== "user" && msg.role !== "system" && /* @__PURE__ */ jsx2(
853
+ "div",
854
+ {
855
+ style: {
856
+ width: "32px",
857
+ height: "32px",
858
+ borderRadius: "50%",
859
+ backgroundColor: msg.role === "human" ? `${theme.text}10` : `${theme.primary}15`,
860
+ border: `1px solid ${msg.role === "human" ? theme.border : `${theme.primary}30`}`,
861
+ display: "flex",
862
+ alignItems: "center",
863
+ justifyContent: "center",
864
+ flexShrink: 0
865
+ },
866
+ children: msg.role === "human" ? /* @__PURE__ */ jsx2(UserIcon, { className: "aa-icon-sm" }) : /* @__PURE__ */ jsx2(SparklesIcon, { className: "aa-icon-sm" })
867
+ }
868
+ ),
869
+ msg.role === "system" ? /* @__PURE__ */ jsxs(
870
+ "div",
871
+ {
872
+ style: {
873
+ display: "flex",
874
+ alignItems: "center",
875
+ gap: "12px",
876
+ width: "100%",
877
+ justifyContent: "center",
878
+ color: theme.textMuted,
879
+ fontSize: "12px"
880
+ },
881
+ children: [
882
+ /* @__PURE__ */ jsx2(
883
+ "div",
884
+ {
885
+ style: { height: "1px", width: "32px", backgroundColor: theme.border }
886
+ }
887
+ ),
888
+ msg.content,
889
+ /* @__PURE__ */ jsx2(
890
+ "div",
891
+ {
892
+ style: { height: "1px", width: "32px", backgroundColor: theme.border }
893
+ }
894
+ )
895
+ ]
896
+ }
897
+ ) : /* @__PURE__ */ jsx2(
898
+ "div",
899
+ {
900
+ style: {
901
+ maxWidth: "80%",
902
+ padding: "12px 16px",
903
+ borderRadius: "16px",
904
+ backgroundColor: msg.role === "user" ? theme.primary : theme.surface,
905
+ color: msg.role === "user" ? "#000" : theme.text,
906
+ fontSize: "14px",
907
+ lineHeight: 1.5
908
+ },
909
+ children: msg.content
910
+ }
911
+ )
912
+ ]
913
+ },
914
+ msg.id
915
+ );
916
+ }),
917
+ showWaitingMessage && /* @__PURE__ */ jsxs(
918
+ "div",
919
+ {
920
+ style: {
921
+ display: "flex",
922
+ alignItems: "center",
923
+ justifyContent: "center",
924
+ gap: "12px",
925
+ color: theme.textMuted,
926
+ fontSize: "12px",
927
+ padding: "16px 0"
928
+ },
929
+ children: [
930
+ /* @__PURE__ */ jsx2(
931
+ "div",
932
+ {
933
+ style: { height: "1px", width: "32px", backgroundColor: theme.border }
934
+ }
935
+ ),
936
+ WAITING_MSG.content,
937
+ /* @__PURE__ */ jsx2(
938
+ "div",
939
+ {
940
+ style: { height: "1px", width: "32px", backgroundColor: theme.border }
941
+ }
942
+ )
943
+ ]
944
+ }
945
+ ),
946
+ (isTyping || awaitingApproval) && /* @__PURE__ */ jsxs(
947
+ "div",
948
+ {
949
+ style: {
950
+ display: "flex",
951
+ gap: "12px",
952
+ marginBottom: "16px"
953
+ },
954
+ children: [
955
+ /* @__PURE__ */ jsx2(
956
+ "div",
957
+ {
958
+ style: {
959
+ width: "32px",
960
+ height: "32px",
961
+ borderRadius: "50%",
962
+ backgroundColor: awaitingApproval ? "rgba(245, 158, 11, 0.1)" : mode === "human" ? `${theme.text}10` : `${theme.primary}15`,
963
+ border: `1px solid ${awaitingApproval ? "rgba(245, 158, 11, 0.2)" : mode === "human" ? theme.border : `${theme.primary}30`}`,
964
+ display: "flex",
965
+ alignItems: "center",
966
+ justifyContent: "center"
967
+ },
968
+ children: /* @__PURE__ */ jsx2(
969
+ "span",
970
+ {
971
+ style: {
972
+ width: "6px",
973
+ height: "6px",
974
+ borderRadius: "50%",
975
+ backgroundColor: awaitingApproval ? "#f59e0b" : theme.primary,
976
+ animation: "pulse 1s infinite"
977
+ }
978
+ }
979
+ )
980
+ }
981
+ ),
982
+ awaitingApproval ? /* @__PURE__ */ jsx2(
983
+ "div",
984
+ {
985
+ style: {
986
+ display: "flex",
987
+ alignItems: "center",
988
+ padding: "12px 16px"
989
+ },
990
+ children: /* @__PURE__ */ jsx2("span", { style: { color: "rgba(245, 158, 11, 0.8)", fontSize: "14px" }, children: "Hang tight! Someone will be with you in just a moment." })
991
+ }
992
+ ) : /* @__PURE__ */ jsxs(
993
+ "div",
994
+ {
995
+ style: {
996
+ display: "flex",
997
+ gap: "4px",
998
+ alignItems: "center",
999
+ padding: "12px 16px"
1000
+ },
1001
+ children: [
1002
+ /* @__PURE__ */ jsx2(
1003
+ "span",
1004
+ {
1005
+ style: {
1006
+ width: "6px",
1007
+ height: "6px",
1008
+ borderRadius: "50%",
1009
+ backgroundColor: theme.textMuted,
1010
+ animation: "bounce 1s infinite 0ms"
1011
+ }
1012
+ }
1013
+ ),
1014
+ /* @__PURE__ */ jsx2(
1015
+ "span",
1016
+ {
1017
+ style: {
1018
+ width: "6px",
1019
+ height: "6px",
1020
+ borderRadius: "50%",
1021
+ backgroundColor: theme.textMuted,
1022
+ animation: "bounce 1s infinite 200ms"
1023
+ }
1024
+ }
1025
+ ),
1026
+ /* @__PURE__ */ jsx2(
1027
+ "span",
1028
+ {
1029
+ style: {
1030
+ width: "6px",
1031
+ height: "6px",
1032
+ borderRadius: "50%",
1033
+ backgroundColor: theme.textMuted,
1034
+ animation: "bounce 1s infinite 400ms"
1035
+ }
1036
+ }
1037
+ )
1038
+ ]
1039
+ }
1040
+ )
1041
+ ]
1042
+ }
1043
+ )
1044
+ ]
1045
+ }
1046
+ ),
1047
+ /* @__PURE__ */ jsxs(
1048
+ "div",
1049
+ {
1050
+ style: {
1051
+ padding: "16px 20px",
1052
+ borderTop: `1px solid ${theme.border}`,
1053
+ position: "relative"
1054
+ },
1055
+ children: [
1056
+ !isLeadCaptured && /* @__PURE__ */ jsxs(
1057
+ "div",
1058
+ {
1059
+ style: {
1060
+ position: "absolute",
1061
+ inset: 0,
1062
+ backgroundColor: "rgba(0,0,0,0.8)",
1063
+ backdropFilter: "blur(4px)",
1064
+ display: "flex",
1065
+ alignItems: "center",
1066
+ justifyContent: "center",
1067
+ padding: "12px",
1068
+ zIndex: 10
1069
+ },
1070
+ "data-testid": "email-gate",
1071
+ children: [
1072
+ /* @__PURE__ */ jsxs(
1073
+ "form",
1074
+ {
1075
+ onSubmit: handleEmailCapture,
1076
+ style: { display: "flex", alignItems: "center", gap: "8px", width: "100%" },
1077
+ children: [
1078
+ /* @__PURE__ */ jsx2(
1079
+ "div",
1080
+ {
1081
+ style: {
1082
+ width: "32px",
1083
+ height: "32px",
1084
+ borderRadius: "50%",
1085
+ backgroundColor: `${theme.primary}20`,
1086
+ border: `1px solid ${theme.primary}30`,
1087
+ display: "flex",
1088
+ alignItems: "center",
1089
+ justifyContent: "center",
1090
+ flexShrink: 0
1091
+ },
1092
+ children: /* @__PURE__ */ jsx2(LockIcon, { className: "aa-icon-sm" })
1093
+ }
1094
+ ),
1095
+ /* @__PURE__ */ jsxs("div", { style: { flex: 1, position: "relative" }, children: [
1096
+ /* @__PURE__ */ jsx2(
1097
+ MailIcon,
1098
+ {
1099
+ className: "aa-icon-sm"
1100
+ }
1101
+ ),
1102
+ /* @__PURE__ */ jsx2(
1103
+ "input",
1104
+ {
1105
+ type: "email",
1106
+ value: emailInput,
1107
+ onChange: (e) => {
1108
+ setEmailInput(e.target.value);
1109
+ setEmailError("");
1110
+ },
1111
+ placeholder: "Enter email to unlock chat",
1112
+ "data-testid": "input-email-gate",
1113
+ disabled: isCapturingEmail,
1114
+ style: {
1115
+ width: "100%",
1116
+ padding: "10px 10px 10px 32px",
1117
+ backgroundColor: "rgba(255,255,255,0.1)",
1118
+ border: "1px solid rgba(255,255,255,0.2)",
1119
+ borderRadius: "10px",
1120
+ color: theme.text,
1121
+ fontSize: "13px",
1122
+ fontFamily: theme.fontFamily,
1123
+ outline: "none"
1124
+ }
1125
+ }
1126
+ )
1127
+ ] }),
1128
+ /* @__PURE__ */ jsx2(
1129
+ "button",
1130
+ {
1131
+ type: "submit",
1132
+ disabled: isCapturingEmail || !emailInput.trim(),
1133
+ "data-testid": "button-unlock-chat",
1134
+ style: {
1135
+ width: "36px",
1136
+ height: "36px",
1137
+ borderRadius: "10px",
1138
+ border: "none",
1139
+ backgroundColor: theme.primary,
1140
+ cursor: isCapturingEmail || !emailInput.trim() ? "not-allowed" : "pointer",
1141
+ opacity: isCapturingEmail || !emailInput.trim() ? 0.5 : 1,
1142
+ display: "flex",
1143
+ alignItems: "center",
1144
+ justifyContent: "center",
1145
+ flexShrink: 0
1146
+ },
1147
+ children: /* @__PURE__ */ jsx2(SendIcon, { className: "aa-icon-sm" })
1148
+ }
1149
+ )
1150
+ ]
1151
+ }
1152
+ ),
1153
+ emailError && /* @__PURE__ */ jsx2("p", { style: { position: "absolute", bottom: "4px", fontSize: "11px", color: "#ff6b6b" }, children: emailError })
1154
+ ]
1155
+ }
1156
+ ),
1157
+ /* @__PURE__ */ jsxs(
1158
+ "div",
1159
+ {
1160
+ style: {
1161
+ display: "flex",
1162
+ gap: "8px",
1163
+ alignItems: "flex-end",
1164
+ backgroundColor: theme.surface,
1165
+ borderRadius: "16px",
1166
+ padding: "8px 12px",
1167
+ border: `1px solid ${theme.border}`
1168
+ },
1169
+ children: [
1170
+ /* @__PURE__ */ jsx2(
1171
+ "textarea",
1172
+ {
1173
+ value: input,
1174
+ onChange: (e) => {
1175
+ setInput(e.target.value);
1176
+ if (workspaceId) {
1177
+ throttledTypingPreview(workspaceId, e.target.value);
1178
+ }
1179
+ },
1180
+ onKeyDown: (e) => {
1181
+ if (e.key === "Enter" && !e.shiftKey) {
1182
+ e.preventDefault();
1183
+ handleSend();
1184
+ }
1185
+ },
1186
+ onBlur: () => {
1187
+ if (workspaceId) {
1188
+ sendTypingPreview(workspaceId, "");
1189
+ }
1190
+ },
1191
+ placeholder: currentPlaceholder,
1192
+ disabled: isTyping || !isLeadCaptured,
1193
+ style: {
1194
+ flex: 1,
1195
+ backgroundColor: "transparent",
1196
+ border: "none",
1197
+ color: theme.text,
1198
+ fontSize: "14px",
1199
+ resize: "none",
1200
+ minHeight: "40px",
1201
+ maxHeight: "120px",
1202
+ outline: "none",
1203
+ fontFamily: theme.fontFamily
1204
+ },
1205
+ "data-testid": "input-message"
1206
+ }
1207
+ ),
1208
+ /* @__PURE__ */ jsx2(
1209
+ "button",
1210
+ {
1211
+ onClick: handleSend,
1212
+ disabled: !input.trim() || isTyping || !isLeadCaptured,
1213
+ style: {
1214
+ width: "36px",
1215
+ height: "36px",
1216
+ borderRadius: "50%",
1217
+ border: "none",
1218
+ backgroundColor: input.trim() && !isTyping && isLeadCaptured ? theme.primary : theme.border,
1219
+ color: input.trim() && !isTyping && isLeadCaptured ? "#000" : theme.textMuted,
1220
+ cursor: input.trim() && !isTyping && isLeadCaptured ? "pointer" : "not-allowed",
1221
+ display: "flex",
1222
+ alignItems: "center",
1223
+ justifyContent: "center",
1224
+ flexShrink: 0
1225
+ },
1226
+ "data-testid": "button-send",
1227
+ children: /* @__PURE__ */ jsx2(SendIcon, { className: "aa-icon-sm" })
1228
+ }
1229
+ )
1230
+ ]
1231
+ }
1232
+ )
1233
+ ]
1234
+ }
1235
+ ),
1236
+ /* @__PURE__ */ jsx2("style", { children: `
1237
+ .aa-chat .aa-icon {
1238
+ width: 20px;
1239
+ height: 20px;
1240
+ color: ${theme.primary};
1241
+ }
1242
+ .aa-chat .aa-icon-lg {
1243
+ width: 28px;
1244
+ height: 28px;
1245
+ color: ${theme.primary};
1246
+ }
1247
+ .aa-chat .aa-icon-sm {
1248
+ width: 14px;
1249
+ height: 14px;
1250
+ color: currentColor;
1251
+ }
1252
+ @keyframes bounce {
1253
+ 0%, 80%, 100% { transform: translateY(0); }
1254
+ 40% { transform: translateY(-4px); }
1255
+ }
1256
+ @keyframes pulse {
1257
+ 0%, 100% { opacity: 1; }
1258
+ 50% { opacity: 0.5; }
1259
+ }
1260
+ ` })
1261
+ ]
1262
+ }
1263
+ );
1264
+ }
1265
+ function AiAssistChat(props) {
1266
+ return /* @__PURE__ */ jsx2(AiAssistThemeProvider, { theme: props.theme, children: /* @__PURE__ */ jsx2(ChatInner, { ...props }) });
1267
+ }
1268
+
1269
+ // src/AiAssistChatWidget.tsx
1270
+ import { useState as useState3 } from "react";
1271
+ import { jsx as jsx3, jsxs as jsxs2 } from "react/jsx-runtime";
1272
+ function DefaultBubbleIcon() {
1273
+ return /* @__PURE__ */ jsx3("svg", { viewBox: "0 0 24 24", fill: "currentColor", width: "28", height: "28", children: /* @__PURE__ */ jsx3("path", { d: "M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10c1.85 0 3.58-.5 5.07-1.38L22 22l-1.62-4.93A9.96 9.96 0 0 0 22 12c0-5.52-4.48-10-10-10zm-1 15h-1v-2h1v2zm2.07-4.75l-.9.92c-.5.51-.86.97-1.04 1.69-.08.32-.13.68-.13 1.14h-2v-.5c0-.46.08-.9.22-1.31.2-.58.53-1.1.95-1.52l1.24-1.26c.46-.44.68-1.1.55-1.8-.13-.72-.69-1.33-1.39-1.53-1.11-.31-2.14.32-2.47 1.27-.12.37-.43.65-.82.65h-.3c-.5 0-.84-.44-.75-.93.34-1.72 1.85-3.05 3.72-3.05 2.08 0 3.78 1.69 3.78 3.78 0 .88-.36 1.68-.93 2.25h.07z" }) });
1274
+ }
1275
+ function WidgetInner({
1276
+ config,
1277
+ theme,
1278
+ onClose,
1279
+ className,
1280
+ showHeader = true,
1281
+ showSuggestions = true,
1282
+ suggestions,
1283
+ placeholder,
1284
+ title,
1285
+ subtitle,
1286
+ position = "bottom-right",
1287
+ bubbleIcon,
1288
+ defaultOpen = false
1289
+ }) {
1290
+ const { theme: resolvedTheme } = useAiAssistTheme();
1291
+ const [isOpen, setIsOpen] = useState3(defaultOpen);
1292
+ const [isHovered, setIsHovered] = useState3(false);
1293
+ const positionStyles = {
1294
+ "bottom-right": { bottom: "20px", right: "20px" },
1295
+ "bottom-left": { bottom: "20px", left: "20px" },
1296
+ "top-right": { top: "20px", right: "20px" },
1297
+ "top-left": { top: "20px", left: "20px" }
1298
+ };
1299
+ const handleClose = () => {
1300
+ setIsOpen(false);
1301
+ onClose?.();
1302
+ };
1303
+ return /* @__PURE__ */ jsxs2(
1304
+ "div",
1305
+ {
1306
+ style: {
1307
+ position: "fixed",
1308
+ zIndex: 999999,
1309
+ ...positionStyles[position]
1310
+ },
1311
+ "data-testid": "aiassist-widget",
1312
+ children: [
1313
+ isOpen && /* @__PURE__ */ jsx3(
1314
+ "div",
1315
+ {
1316
+ style: {
1317
+ width: "380px",
1318
+ height: "600px",
1319
+ maxHeight: "calc(100vh - 100px)",
1320
+ marginBottom: "16px",
1321
+ boxShadow: "0 10px 50px rgba(0,0,0,0.5)",
1322
+ borderRadius: resolvedTheme.radius,
1323
+ overflow: "hidden"
1324
+ },
1325
+ "data-testid": "widget-chat-container",
1326
+ children: /* @__PURE__ */ jsx3(
1327
+ AiAssistChat,
1328
+ {
1329
+ config,
1330
+ theme,
1331
+ onClose: handleClose,
1332
+ className,
1333
+ showHeader,
1334
+ showSuggestions,
1335
+ suggestions,
1336
+ placeholder,
1337
+ title,
1338
+ subtitle
1339
+ }
1340
+ )
1341
+ }
1342
+ ),
1343
+ /* @__PURE__ */ jsx3(
1344
+ "button",
1345
+ {
1346
+ onClick: () => setIsOpen(!isOpen),
1347
+ onMouseEnter: () => setIsHovered(true),
1348
+ onMouseLeave: () => setIsHovered(false),
1349
+ style: {
1350
+ width: "60px",
1351
+ height: "60px",
1352
+ borderRadius: "50%",
1353
+ backgroundColor: resolvedTheme.primary,
1354
+ border: "none",
1355
+ cursor: "pointer",
1356
+ display: "flex",
1357
+ alignItems: "center",
1358
+ justifyContent: "center",
1359
+ boxShadow: isHovered ? `0 6px 30px ${resolvedTheme.primary}80` : `0 4px 20px ${resolvedTheme.primary}66`,
1360
+ transform: isHovered ? "scale(1.1)" : "scale(1)",
1361
+ transition: "transform 0.2s, box-shadow 0.2s",
1362
+ color: "#000"
1363
+ },
1364
+ "data-testid": "button-toggle-widget",
1365
+ children: isOpen ? /* @__PURE__ */ jsx3("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", width: "24", height: "24", children: /* @__PURE__ */ jsx3("path", { d: "M6 18L18 6M6 6l12 12" }) }) : bubbleIcon || /* @__PURE__ */ jsx3(DefaultBubbleIcon, {})
1366
+ }
1367
+ ),
1368
+ /* @__PURE__ */ jsx3("style", { children: `
1369
+ @media (max-width: 440px) {
1370
+ [data-testid="widget-chat-container"] {
1371
+ width: calc(100vw - 40px) !important;
1372
+ height: calc(100vh - 120px) !important;
1373
+ position: fixed !important;
1374
+ left: 10px !important;
1375
+ right: 10px !important;
1376
+ bottom: 80px !important;
1377
+ }
1378
+ }
1379
+ ` })
1380
+ ]
1381
+ }
1382
+ );
1383
+ }
1384
+ function AiAssistChatWidget(props) {
1385
+ return /* @__PURE__ */ jsx3(AiAssistThemeProvider, { theme: props.theme, children: /* @__PURE__ */ jsx3(WidgetInner, { ...props }) });
1386
+ }
1387
+ export {
1388
+ AiAssistAPI,
1389
+ AiAssistChat,
1390
+ AiAssistChatWidget,
1391
+ AiAssistThemeProvider,
1392
+ DEFAULT_THEME,
1393
+ normalizeRole,
1394
+ useAiAssistTheme,
1395
+ useThrottle
1396
+ };