@theonexai/blockspark-chat-widget 1.0.17

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.
Files changed (69) hide show
  1. package/README.md +270 -0
  2. package/dist/ChatWidget-BM2lXfeE.cjs +2 -0
  3. package/dist/ChatWidget-BM2lXfeE.cjs.map +1 -0
  4. package/dist/ChatWidget-Cljd-Tfm.js +1356 -0
  5. package/dist/ChatWidget-Cljd-Tfm.js.map +1 -0
  6. package/dist/adapters/vue/index.d.ts +7 -0
  7. package/dist/adapters/vue/index.d.ts.map +1 -0
  8. package/dist/adapters/vue/useChatMode.d.ts +21 -0
  9. package/dist/adapters/vue/useChatMode.d.ts.map +1 -0
  10. package/dist/components/ChatWidget.d.ts +39 -0
  11. package/dist/components/ChatWidget.d.ts.map +1 -0
  12. package/dist/composables/useChatWidget.d.ts +35 -0
  13. package/dist/composables/useChatWidget.d.ts.map +1 -0
  14. package/dist/core/stateManager.d.ts +136 -0
  15. package/dist/core/stateManager.d.ts.map +1 -0
  16. package/dist/core/types.d.ts +64 -0
  17. package/dist/core/types.d.ts.map +1 -0
  18. package/dist/entry/next.d.ts +9 -0
  19. package/dist/entry/next.d.ts.map +1 -0
  20. package/dist/entry/nuxt.d.ts +12 -0
  21. package/dist/entry/nuxt.d.ts.map +1 -0
  22. package/dist/entry/react.d.ts +10 -0
  23. package/dist/entry/react.d.ts.map +1 -0
  24. package/dist/entry/vanilla.d.ts +33 -0
  25. package/dist/entry/vanilla.d.ts.map +1 -0
  26. package/dist/entry/vite.d.ts +11 -0
  27. package/dist/entry/vite.d.ts.map +1 -0
  28. package/dist/entry/vue.d.ts +12 -0
  29. package/dist/entry/vue.d.ts.map +1 -0
  30. package/dist/hooks/useChatMode.d.ts +17 -0
  31. package/dist/hooks/useChatMode.d.ts.map +1 -0
  32. package/dist/index.cjs.js +2 -0
  33. package/dist/index.cjs.js.map +1 -0
  34. package/dist/index.d.ts +21 -0
  35. package/dist/index.d.ts.map +1 -0
  36. package/dist/index.esm.js +1383 -0
  37. package/dist/index.esm.js.map +1 -0
  38. package/dist/nuxt.cjs.js +2 -0
  39. package/dist/nuxt.cjs.js.map +1 -0
  40. package/dist/nuxt.esm.js +9 -0
  41. package/dist/nuxt.esm.js.map +1 -0
  42. package/dist/sanitize-C8MB41vY.cjs +4 -0
  43. package/dist/sanitize-C8MB41vY.cjs.map +1 -0
  44. package/dist/sanitize-Cm1kskSD.js +1842 -0
  45. package/dist/sanitize-Cm1kskSD.js.map +1 -0
  46. package/dist/services/chatService.d.ts +144 -0
  47. package/dist/services/chatService.d.ts.map +1 -0
  48. package/dist/services/dialogflowBackendService.d.ts +36 -0
  49. package/dist/services/dialogflowBackendService.d.ts.map +1 -0
  50. package/dist/services/dialogflowClient.d.ts +36 -0
  51. package/dist/services/dialogflowClient.d.ts.map +1 -0
  52. package/dist/services/sessionManager.d.ts +13 -0
  53. package/dist/services/sessionManager.d.ts.map +1 -0
  54. package/dist/styles.css +1 -0
  55. package/dist/types.d.ts +5 -0
  56. package/dist/types.d.ts.map +1 -0
  57. package/dist/utils/dialogflowHandler.d.ts +31 -0
  58. package/dist/utils/dialogflowHandler.d.ts.map +1 -0
  59. package/dist/utils/frameworkDetector.d.ts +17 -0
  60. package/dist/utils/frameworkDetector.d.ts.map +1 -0
  61. package/dist/utils/sanitize.d.ts +25 -0
  62. package/dist/utils/sanitize.d.ts.map +1 -0
  63. package/dist/utils/ssr.d.ts +35 -0
  64. package/dist/utils/ssr.d.ts.map +1 -0
  65. package/dist/vue.cjs.js +2 -0
  66. package/dist/vue.cjs.js.map +1 -0
  67. package/dist/vue.esm.js +9 -0
  68. package/dist/vue.esm.js.map +1 -0
  69. package/package.json +114 -0
@@ -0,0 +1,1356 @@
1
+ import { ref, computed, onUnmounted, watch, defineComponent, nextTick, onMounted, openBlock, createElementBlock, mergeProps, unref, createElementVNode, toDisplayString, withModifiers, createCommentVNode, createTextVNode, Fragment, renderList, normalizeClass } from "vue";
2
+ import { c as createChatService, a as createDialogflowSession, C as ChatResolvedError, s as sendDialogflowMessage, b as safeLinkifyText } from "./sanitize-Cm1kskSD.js";
3
+ class WidgetStateManager {
4
+ constructor(config) {
5
+ this.listeners = /* @__PURE__ */ new Set();
6
+ this.chatMode = "ai";
7
+ this.chatId = null;
8
+ this.supportSessionId = null;
9
+ this.chatService = null;
10
+ this.collectingUserInfo = false;
11
+ this.userInfoStep = null;
12
+ this.collectedUserName = "";
13
+ this.collectedUserEmail = "";
14
+ this.collectedUserMobile = "";
15
+ this.wsConnected = false;
16
+ this.agentTyping = false;
17
+ this.currentAgent = { name: "Agent" };
18
+ this.isConnectingToAgent = false;
19
+ this.agentAccepted = false;
20
+ this.chatResolved = false;
21
+ this.historyLoaded = null;
22
+ this.typingTimeout = null;
23
+ this.agentTypingTimeout = null;
24
+ this.config = config;
25
+ this.state = {
26
+ isOpen: false,
27
+ showWelcomePopup: false,
28
+ messages: [],
29
+ inputValue: "",
30
+ isLoading: false,
31
+ error: null,
32
+ sessionId: null,
33
+ chatMode: "ai"
34
+ };
35
+ this.chatService = createChatService({
36
+ baseUrl: this.config.backendBaseUrl || "http://localhost:8012",
37
+ wsUrl: this.config.backendWsUrl || "ws://localhost:8012",
38
+ debug: this.config.debug || false
39
+ });
40
+ if (typeof window !== "undefined" && window.localStorage) {
41
+ const savedMode = localStorage.getItem("blockspark_chat_mode");
42
+ this.chatMode = savedMode === "HUMAN" ? "human" : "ai";
43
+ this.state.chatMode = this.chatMode;
44
+ this.chatId = localStorage.getItem("blockspark_chat_id");
45
+ this.supportSessionId = localStorage.getItem("blockspark_session_id");
46
+ }
47
+ }
48
+ /**
49
+ * Subscribe to state changes
50
+ */
51
+ subscribe(listener) {
52
+ this.listeners.add(listener);
53
+ return () => {
54
+ this.listeners.delete(listener);
55
+ };
56
+ }
57
+ /**
58
+ * Get current state
59
+ */
60
+ getState() {
61
+ return { ...this.state };
62
+ }
63
+ /**
64
+ * Update state and notify listeners
65
+ */
66
+ setState(updates) {
67
+ this.state = { ...this.state, ...updates };
68
+ this.listeners.forEach((listener) => listener(this.getState()));
69
+ }
70
+ /**
71
+ * Update configuration
72
+ */
73
+ updateConfig(config) {
74
+ this.config = { ...this.config, ...config };
75
+ }
76
+ /**
77
+ * Open chat
78
+ */
79
+ async openChat() {
80
+ this.setState({ isOpen: true });
81
+ if (this.state.showWelcomePopup) {
82
+ this.setState({ showWelcomePopup: false });
83
+ }
84
+ if (this.state.chatMode === "ai" && !this.state.sessionId && this.config.dfProjectId && this.config.dfAgentId) {
85
+ try {
86
+ this.setState({ isLoading: true });
87
+ const dialogflowConfig = {
88
+ dfProjectId: this.config.dfProjectId,
89
+ dfLocation: this.config.dfLocation || "us-central1",
90
+ dfAgentId: this.config.dfAgentId,
91
+ languageCode: this.config.languageCode || "en",
92
+ backendBaseUrl: this.config.backendBaseUrl || "http://localhost:8012"
93
+ };
94
+ const session = await createDialogflowSession(dialogflowConfig);
95
+ this.setState({
96
+ sessionId: session.session_id,
97
+ isLoading: false
98
+ });
99
+ if (session.message) {
100
+ const welcomeMessage = {
101
+ id: `welcome-${Date.now()}`,
102
+ text: session.message,
103
+ sender: "bot",
104
+ timestamp: /* @__PURE__ */ new Date(),
105
+ richContent: session.richContent
106
+ };
107
+ this.setState({
108
+ messages: [welcomeMessage]
109
+ });
110
+ }
111
+ } catch (error) {
112
+ console.error("Error initializing Dialogflow session:", error);
113
+ this.setState({
114
+ isLoading: false,
115
+ error: error.message || "Failed to initialize chat"
116
+ });
117
+ const fallbackMessage = {
118
+ id: `fallback-${Date.now()}`,
119
+ text: this.config.fallbackWelcomeMessage || "Hello! How can I help you today?",
120
+ sender: "bot",
121
+ timestamp: /* @__PURE__ */ new Date()
122
+ };
123
+ this.setState({
124
+ messages: [fallbackMessage]
125
+ });
126
+ }
127
+ }
128
+ }
129
+ /**
130
+ * Close chat
131
+ */
132
+ closeChat() {
133
+ this.setState({ isOpen: false });
134
+ }
135
+ /**
136
+ * Close welcome popup
137
+ */
138
+ closeWelcomePopup() {
139
+ this.setState({ showWelcomePopup: false });
140
+ }
141
+ /**
142
+ * Toggle chat
143
+ */
144
+ toggleChat() {
145
+ const willBeOpen = !this.state.isOpen;
146
+ this.setState({ isOpen: willBeOpen });
147
+ if (willBeOpen && this.state.showWelcomePopup) {
148
+ this.setState({ showWelcomePopup: false });
149
+ }
150
+ }
151
+ /**
152
+ * Set input value
153
+ */
154
+ setInputValue(value) {
155
+ this.setState({ inputValue: value });
156
+ }
157
+ /**
158
+ * Clear error
159
+ */
160
+ clearError() {
161
+ this.setState({ error: null });
162
+ }
163
+ /**
164
+ * Send message
165
+ */
166
+ async sendMessage(text, skipUserMessage = false) {
167
+ if (!text.trim() || this.state.isLoading) {
168
+ return;
169
+ }
170
+ if (this.collectingUserInfo) {
171
+ const userMessage = {
172
+ id: `user-${Date.now()}`,
173
+ text: text.trim(),
174
+ sender: "user",
175
+ timestamp: /* @__PURE__ */ new Date()
176
+ };
177
+ this.setState({
178
+ messages: [...this.state.messages, userMessage],
179
+ inputValue: "",
180
+ isLoading: false,
181
+ error: null
182
+ });
183
+ await this.handleUserInfoCollection(text);
184
+ return;
185
+ }
186
+ if (!skipUserMessage) {
187
+ const userMessage = {
188
+ id: `user-${Date.now()}`,
189
+ text: text.trim(),
190
+ sender: "user",
191
+ timestamp: /* @__PURE__ */ new Date()
192
+ };
193
+ this.setState({
194
+ messages: [...this.state.messages, userMessage],
195
+ inputValue: "",
196
+ isLoading: true,
197
+ error: null
198
+ });
199
+ } else {
200
+ this.setState({
201
+ inputValue: "",
202
+ isLoading: true,
203
+ error: null
204
+ });
205
+ }
206
+ try {
207
+ if (this.state.chatMode === "human") {
208
+ await this.sendHumanMessage(text);
209
+ } else {
210
+ await this.sendAIMessage(text);
211
+ }
212
+ } catch (error) {
213
+ console.error("Error sending message:", error);
214
+ if (error instanceof ChatResolvedError || error?.name === "ChatResolvedError" || error?.message === "chat_resolved") {
215
+ this.enterResolvedState(this.chatId);
216
+ return;
217
+ }
218
+ const errorMessage = {
219
+ id: `error-${Date.now()}`,
220
+ text: this.config.debug ? `Error: ${error.message || "Failed to send message"}` : error.message?.includes("CORS") || error.message?.includes("Failed to fetch") ? "Unable to connect to Dialogflow. Please check your configuration and network." : "Sorry, I'm having trouble processing your message. Please try again.",
221
+ sender: "bot",
222
+ timestamp: /* @__PURE__ */ new Date()
223
+ };
224
+ this.setState({
225
+ messages: [...this.state.messages, errorMessage],
226
+ error: error.message || "Failed to send message",
227
+ isLoading: false
228
+ });
229
+ }
230
+ }
231
+ /**
232
+ * Send message to Dialogflow
233
+ */
234
+ async sendAIMessage(text) {
235
+ if (!this.config.dfProjectId || !this.config.dfAgentId) {
236
+ throw new Error("Dialogflow configuration is missing");
237
+ }
238
+ const dialogflowConfig = {
239
+ dfProjectId: this.config.dfProjectId,
240
+ dfLocation: this.config.dfLocation || "us-central1",
241
+ dfAgentId: this.config.dfAgentId,
242
+ languageCode: this.config.languageCode || "en",
243
+ backendBaseUrl: this.config.backendBaseUrl || "http://localhost:8012"
244
+ };
245
+ if (!this.state.sessionId) {
246
+ const session = await createDialogflowSession(dialogflowConfig);
247
+ this.setState({ sessionId: session.session_id });
248
+ }
249
+ if (this.config.debug) {
250
+ console.log("Sending message to Dialogflow:", {
251
+ message: text,
252
+ sessionId: this.state.sessionId,
253
+ hasConfig: !!dialogflowConfig
254
+ });
255
+ }
256
+ const response = await sendDialogflowMessage(
257
+ text,
258
+ this.state.sessionId,
259
+ dialogflowConfig
260
+ );
261
+ if (this.config.debug) {
262
+ console.log("Dialogflow response:", {
263
+ response: response.response,
264
+ hasRichContent: !!response.richContent,
265
+ richContent: response.richContent
266
+ });
267
+ }
268
+ if (response.handoff === true) {
269
+ const botMessage2 = {
270
+ id: `bot-${Date.now()}`,
271
+ text: response.response || this.config.fallbackWelcomeMessage || "No response",
272
+ sender: "bot",
273
+ timestamp: /* @__PURE__ */ new Date(),
274
+ richContent: response.richContent
275
+ };
276
+ this.setState({
277
+ messages: [...this.state.messages, botMessage2],
278
+ isLoading: false
279
+ });
280
+ this.startUserInfoCollection();
281
+ return;
282
+ }
283
+ const botMessage = {
284
+ id: `bot-${Date.now()}`,
285
+ text: response.response || this.config.fallbackWelcomeMessage || "No response",
286
+ sender: "bot",
287
+ timestamp: /* @__PURE__ */ new Date(),
288
+ richContent: response.richContent
289
+ };
290
+ this.setState({
291
+ messages: [...this.state.messages, botMessage],
292
+ isLoading: false
293
+ });
294
+ }
295
+ /**
296
+ * Send message to human support
297
+ */
298
+ async sendHumanMessage(text) {
299
+ if (!this.chatId || !this.supportSessionId) {
300
+ const errorMessage = {
301
+ id: `error-${Date.now()}`,
302
+ text: "Chat session not initialized. Please try again.",
303
+ sender: "bot",
304
+ timestamp: /* @__PURE__ */ new Date()
305
+ };
306
+ this.setState({
307
+ messages: [...this.state.messages, errorMessage],
308
+ isLoading: false
309
+ });
310
+ return;
311
+ }
312
+ if (!this.chatService) {
313
+ throw new Error("Chat service not initialized");
314
+ }
315
+ this.chatService.sendTypingIndicator("typing_stop");
316
+ if (this.typingTimeout) {
317
+ clearTimeout(this.typingTimeout);
318
+ this.typingTimeout = null;
319
+ }
320
+ try {
321
+ const sentViaWs = this.chatService.sendMessageViaWebSocket(text.trim());
322
+ if (!sentViaWs) {
323
+ await this.chatService.sendMessageToAgent(this.chatId, this.supportSessionId, text.trim());
324
+ }
325
+ this.setState({ isLoading: false });
326
+ } catch (error) {
327
+ if (error instanceof ChatResolvedError || error?.name === "ChatResolvedError" || error?.message === "chat_resolved") {
328
+ this.enterResolvedState(this.chatId);
329
+ return;
330
+ }
331
+ if (error.message?.includes("Chat not found") || error.message?.includes("unauthorized") || error.message?.includes("401") || error.message?.includes("404")) {
332
+ if (this.config.debug) {
333
+ console.log("⚠️ Chat expired. Re-initializing...");
334
+ }
335
+ this.chatId = null;
336
+ this.supportSessionId = null;
337
+ if (typeof window !== "undefined" && window.localStorage) {
338
+ localStorage.removeItem("blockspark_chat_id");
339
+ localStorage.removeItem("blockspark_session_id");
340
+ }
341
+ try {
342
+ const newSession = await this.chatService.startSupportChat(
343
+ this.state.sessionId || null,
344
+ null,
345
+ null,
346
+ null
347
+ );
348
+ this.chatId = newSession.chat_id;
349
+ this.supportSessionId = newSession.session_id;
350
+ if (typeof window !== "undefined" && window.localStorage) {
351
+ localStorage.setItem("blockspark_chat_id", this.chatId);
352
+ localStorage.setItem("blockspark_session_id", this.supportSessionId);
353
+ }
354
+ if (this.chatId && this.supportSessionId) {
355
+ await this.chatService.sendMessageToAgent(
356
+ this.chatId,
357
+ this.supportSessionId,
358
+ text.trim()
359
+ );
360
+ }
361
+ this.setState({ isLoading: false });
362
+ return;
363
+ } catch (retryError) {
364
+ if (retryError instanceof ChatResolvedError || retryError?.message === "chat_resolved") {
365
+ this.enterResolvedState(this.chatId);
366
+ return;
367
+ }
368
+ throw retryError;
369
+ }
370
+ } else {
371
+ throw error;
372
+ }
373
+ }
374
+ }
375
+ /**
376
+ * Switch to human mode
377
+ */
378
+ switchToHumanMode() {
379
+ this.chatMode = "human";
380
+ this.setState({ chatMode: "human" });
381
+ if (typeof window !== "undefined" && window.localStorage) {
382
+ localStorage.setItem("blockspark_chat_mode", "HUMAN");
383
+ }
384
+ }
385
+ /**
386
+ * Switch to AI mode
387
+ */
388
+ switchToBotMode() {
389
+ this.chatMode = "ai";
390
+ this.setState({ chatMode: "ai" });
391
+ if (typeof window !== "undefined" && window.localStorage) {
392
+ localStorage.setItem("blockspark_chat_mode", "BOT");
393
+ }
394
+ }
395
+ /**
396
+ * Initialize welcome popup
397
+ */
398
+ initializeWelcomePopup() {
399
+ if (this.config.showWelcomePopup !== false) {
400
+ const delay = this.config.welcomePopupDelay || 1500;
401
+ setTimeout(() => {
402
+ if (!this.state.isOpen) {
403
+ this.setState({ showWelcomePopup: true });
404
+ }
405
+ }, delay);
406
+ }
407
+ }
408
+ /**
409
+ * Start user info collection for handoff
410
+ */
411
+ startUserInfoCollection() {
412
+ this.collectingUserInfo = true;
413
+ this.userInfoStep = "name";
414
+ this.collectedUserName = "";
415
+ this.collectedUserEmail = "";
416
+ this.collectedUserMobile = "";
417
+ const namePrompt = {
418
+ id: `prompt-${Date.now()}`,
419
+ text: "To connect you with a human agent, I'll need some information. Please provide your name:",
420
+ sender: "bot",
421
+ timestamp: /* @__PURE__ */ new Date()
422
+ };
423
+ this.setState({
424
+ messages: [...this.state.messages, namePrompt]
425
+ });
426
+ }
427
+ /**
428
+ * Handle user info collection
429
+ */
430
+ async handleUserInfoCollection(text) {
431
+ if (this.userInfoStep === "name") {
432
+ const name = text.trim();
433
+ this.collectedUserName = name;
434
+ this.userInfoStep = "email";
435
+ const emailPrompt = {
436
+ id: `prompt-${Date.now()}`,
437
+ text: "Thank you! Now please provide your email address:",
438
+ sender: "bot",
439
+ timestamp: /* @__PURE__ */ new Date()
440
+ };
441
+ this.setState({
442
+ messages: [...this.state.messages, emailPrompt],
443
+ inputValue: ""
444
+ });
445
+ return;
446
+ } else if (this.userInfoStep === "email") {
447
+ const email = text.trim();
448
+ const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
449
+ if (!emailRegex.test(email)) {
450
+ const invalidEmailMessage = {
451
+ id: `prompt-${Date.now()}`,
452
+ text: "Please provide a valid email address:",
453
+ sender: "bot",
454
+ timestamp: /* @__PURE__ */ new Date()
455
+ };
456
+ this.setState({
457
+ messages: [...this.state.messages, invalidEmailMessage],
458
+ inputValue: ""
459
+ });
460
+ return;
461
+ }
462
+ this.collectedUserEmail = email;
463
+ this.userInfoStep = "mobile";
464
+ const mobilePrompt = {
465
+ id: `prompt-${Date.now()}`,
466
+ text: "Thank you! Now please provide your mobile number:",
467
+ sender: "bot",
468
+ timestamp: /* @__PURE__ */ new Date()
469
+ };
470
+ this.setState({
471
+ messages: [...this.state.messages, mobilePrompt],
472
+ inputValue: ""
473
+ });
474
+ return;
475
+ } else if (this.userInfoStep === "mobile") {
476
+ const mobile = text.trim();
477
+ const mobileRegex = /^[\+]?[(]?[0-9]{1,4}[)]?[-\s\.]?[(]?[0-9]{1,4}[)]?[-\s\.]?[0-9]{1,9}$/;
478
+ if (!mobileRegex.test(mobile) || mobile.length < 10) {
479
+ const invalidMobileMessage = {
480
+ id: `prompt-${Date.now()}`,
481
+ text: "Please provide a valid mobile number (e.g., +1234567890):",
482
+ sender: "bot",
483
+ timestamp: /* @__PURE__ */ new Date()
484
+ };
485
+ this.setState({
486
+ messages: [...this.state.messages, invalidMobileMessage],
487
+ inputValue: ""
488
+ });
489
+ return;
490
+ }
491
+ this.collectedUserMobile = mobile;
492
+ this.collectingUserInfo = false;
493
+ this.userInfoStep = null;
494
+ await this.handleHandoff(this.collectedUserName, this.collectedUserEmail, mobile);
495
+ this.setState({ inputValue: "" });
496
+ }
497
+ }
498
+ /**
499
+ * Handle handoff from Dialogflow to human support
500
+ */
501
+ async handleHandoff(customerName, customerEmail, customerMobile) {
502
+ if (!this.chatService) {
503
+ throw new Error("Chat service not initialized");
504
+ }
505
+ try {
506
+ this.isConnectingToAgent = true;
507
+ const dialogflowSessionId = this.state.sessionId;
508
+ const session = await this.chatService.ensureChatInitialized(
509
+ this.chatId,
510
+ this.supportSessionId,
511
+ dialogflowSessionId || null,
512
+ customerName || null,
513
+ customerEmail || null,
514
+ customerMobile || null
515
+ );
516
+ if (!session || !session.chat_id) {
517
+ throw new Error("Failed to initialize chat session");
518
+ }
519
+ const currentChatId = session.chat_id;
520
+ const currentSupportSessionId = session.session_id;
521
+ if (currentChatId !== this.chatId) {
522
+ this.chatId = currentChatId;
523
+ this.supportSessionId = currentSupportSessionId;
524
+ if (typeof window !== "undefined" && window.localStorage) {
525
+ localStorage.setItem("blockspark_chat_id", this.chatId);
526
+ localStorage.setItem("blockspark_session_id", this.supportSessionId);
527
+ }
528
+ if (this.config.debug) {
529
+ console.log("✅ Chat initialized:", { chatId: currentChatId, sessionId: currentSupportSessionId });
530
+ }
531
+ }
532
+ try {
533
+ await this.chatService.requestHandoff(
534
+ currentChatId,
535
+ currentSupportSessionId,
536
+ "Customer requested human agent",
537
+ dialogflowSessionId || null,
538
+ customerName || null,
539
+ customerEmail || null,
540
+ customerMobile || null
541
+ );
542
+ if (this.config.debug) {
543
+ console.log("✅ Handoff requested successfully");
544
+ }
545
+ } catch (handoffError) {
546
+ if (handoffError.message?.includes("Invalid chat_id") || handoffError.message?.includes("Chat not found") || handoffError.message?.includes("unauthorized") || handoffError.message?.includes("400") || handoffError.message?.includes("401") || handoffError.message?.includes("404") || handoffError.message?.includes("expired")) {
547
+ if (this.config.debug) {
548
+ console.log("⚠️ Chat expired or not found. Re-initializing chat...");
549
+ }
550
+ this.chatId = null;
551
+ this.supportSessionId = null;
552
+ if (typeof window !== "undefined" && window.localStorage) {
553
+ localStorage.removeItem("blockspark_chat_id");
554
+ localStorage.removeItem("blockspark_session_id");
555
+ }
556
+ const newSession = await this.chatService.startSupportChat(
557
+ dialogflowSessionId || null,
558
+ customerName || null,
559
+ customerEmail || null,
560
+ customerMobile || null
561
+ );
562
+ if (!newSession || !newSession.chat_id) {
563
+ throw new Error("Failed to re-initialize chat session");
564
+ }
565
+ this.chatId = newSession.chat_id;
566
+ this.supportSessionId = newSession.session_id;
567
+ if (typeof window !== "undefined" && window.localStorage) {
568
+ localStorage.setItem("blockspark_chat_id", this.chatId);
569
+ localStorage.setItem("blockspark_session_id", this.supportSessionId);
570
+ }
571
+ await this.chatService.requestHandoff(
572
+ this.chatId,
573
+ this.supportSessionId,
574
+ "Customer requested human agent",
575
+ dialogflowSessionId || null,
576
+ customerName || null,
577
+ customerEmail || null,
578
+ customerMobile || null
579
+ );
580
+ if (this.config.debug) {
581
+ console.log("✅ Handoff requested successfully after retry");
582
+ }
583
+ } else {
584
+ throw handoffError;
585
+ }
586
+ }
587
+ this.switchToHumanMode();
588
+ this.chatResolved = false;
589
+ this.agentAccepted = false;
590
+ const connectingMessage = {
591
+ id: `connecting-${Date.now()}`,
592
+ text: "Connecting you to a human agent...",
593
+ sender: "bot",
594
+ timestamp: /* @__PURE__ */ new Date()
595
+ };
596
+ this.setState({
597
+ messages: [...this.state.messages, connectingMessage]
598
+ });
599
+ if (currentChatId && currentSupportSessionId) {
600
+ this.chatService.connectWebSocket(
601
+ currentChatId,
602
+ currentSupportSessionId,
603
+ (message) => {
604
+ this.handleWebSocketMessage(message);
605
+ },
606
+ (connected) => {
607
+ this.wsConnected = connected;
608
+ }
609
+ );
610
+ }
611
+ this.isConnectingToAgent = false;
612
+ } catch (error) {
613
+ console.error("Error handling handoff:", error);
614
+ const errorMessage = {
615
+ id: `error-${Date.now()}`,
616
+ text: this.config.debug ? `Handoff error: ${error.message}` : "Failed to connect to agent. Please try again.",
617
+ sender: "bot",
618
+ timestamp: /* @__PURE__ */ new Date()
619
+ };
620
+ this.setState({
621
+ messages: [...this.state.messages, errorMessage]
622
+ });
623
+ this.isConnectingToAgent = false;
624
+ }
625
+ }
626
+ /**
627
+ * Handle WebSocket messages
628
+ */
629
+ handleWebSocketMessage(message) {
630
+ switch (message.type) {
631
+ case "message":
632
+ if (message.content) {
633
+ if (message.sender_type === "agent" || !message.sender_type) {
634
+ const agentMessage = {
635
+ id: message.id || `agent-${Date.now()}`,
636
+ text: message.content,
637
+ sender: "agent",
638
+ timestamp: new Date(message.timestamp || Date.now())
639
+ };
640
+ const existingIds2 = new Set(this.state.messages.map((m) => m.id));
641
+ if (!existingIds2.has(agentMessage.id)) {
642
+ this.setState({
643
+ messages: [...this.state.messages, agentMessage]
644
+ });
645
+ }
646
+ this.agentTyping = false;
647
+ if (this.agentTypingTimeout) {
648
+ clearTimeout(this.agentTypingTimeout);
649
+ this.agentTypingTimeout = null;
650
+ }
651
+ }
652
+ }
653
+ break;
654
+ case "typing_start":
655
+ if (message.sender_type === "agent") {
656
+ this.agentTyping = true;
657
+ if (this.agentTypingTimeout) {
658
+ clearTimeout(this.agentTypingTimeout);
659
+ }
660
+ this.agentTypingTimeout = setTimeout(() => {
661
+ this.agentTyping = false;
662
+ }, 3e3);
663
+ }
664
+ break;
665
+ case "typing_stop":
666
+ if (message.sender_type === "agent") {
667
+ this.agentTyping = false;
668
+ if (this.agentTypingTimeout) {
669
+ clearTimeout(this.agentTypingTimeout);
670
+ this.agentTypingTimeout = null;
671
+ }
672
+ }
673
+ break;
674
+ case "agent_changed":
675
+ if (message.to_agent) {
676
+ this.currentAgent = {
677
+ id: message.to_agent_id,
678
+ name: message.to_agent
679
+ };
680
+ const systemMessage = {
681
+ id: `system-${Date.now()}`,
682
+ text: message.from_agent ? `Chat has been transferred from ${message.from_agent} to ${message.to_agent}` : `Chat has been transferred to ${message.to_agent}`,
683
+ sender: "bot",
684
+ timestamp: /* @__PURE__ */ new Date()
685
+ };
686
+ this.setState({
687
+ messages: [...this.state.messages, systemMessage]
688
+ });
689
+ }
690
+ break;
691
+ case "agent_accepted":
692
+ this.agentAccepted = true;
693
+ this.isConnectingToAgent = false;
694
+ const acceptedMessage = {
695
+ id: message.id || `agent-accepted-${Date.now()}`,
696
+ text: "You can chat now, the agent has accepted your request.",
697
+ sender: "bot",
698
+ timestamp: message.timestamp ? new Date(message.timestamp) : /* @__PURE__ */ new Date()
699
+ };
700
+ const existingIds = new Set(this.state.messages.map((m) => m.id));
701
+ if (!existingIds.has(acceptedMessage.id)) {
702
+ this.setState({
703
+ messages: [...this.state.messages, acceptedMessage]
704
+ });
705
+ }
706
+ if (message.to_agent) {
707
+ this.currentAgent = {
708
+ name: message.to_agent,
709
+ id: message.to_agent_id
710
+ };
711
+ }
712
+ break;
713
+ case "chat_resolved":
714
+ case "chat_ended":
715
+ this.enterResolvedState(message.chat_id || null);
716
+ break;
717
+ case "chat_info":
718
+ if (message.status === "active") {
719
+ this.isConnectingToAgent = false;
720
+ this.agentAccepted = true;
721
+ } else if (message.status === "resolved" || message.status === "ended") {
722
+ this.enterResolvedState(message.chat_id || null);
723
+ }
724
+ if (message.agent_id) {
725
+ this.currentAgent = {
726
+ ...this.currentAgent,
727
+ id: message.agent_id
728
+ };
729
+ }
730
+ break;
731
+ case "error":
732
+ console.error("WebSocket error:", message.error);
733
+ const errorMessage = {
734
+ id: `error-${Date.now()}`,
735
+ text: message.error || "An error occurred. Please try again.",
736
+ sender: "bot",
737
+ timestamp: /* @__PURE__ */ new Date()
738
+ };
739
+ this.setState({
740
+ messages: [...this.state.messages, errorMessage]
741
+ });
742
+ break;
743
+ }
744
+ }
745
+ /**
746
+ * Enter resolved state
747
+ */
748
+ enterResolvedState(resolvedChatId) {
749
+ this.chatResolved = true;
750
+ this.isConnectingToAgent = false;
751
+ this.agentAccepted = false;
752
+ this.agentTyping = false;
753
+ if (this.agentTypingTimeout) {
754
+ clearTimeout(this.agentTypingTimeout);
755
+ this.agentTypingTimeout = null;
756
+ }
757
+ if (this.chatService) {
758
+ this.chatService.disconnectWebSocket();
759
+ }
760
+ this.chatId = null;
761
+ this.supportSessionId = null;
762
+ if (typeof window !== "undefined" && window.localStorage) {
763
+ localStorage.removeItem("blockspark_chat_id");
764
+ localStorage.removeItem("blockspark_session_id");
765
+ }
766
+ const thankYouMessage = {
767
+ id: `resolved-${Date.now()}`,
768
+ text: "Thank you for contacting us!",
769
+ sender: "bot",
770
+ timestamp: /* @__PURE__ */ new Date()
771
+ };
772
+ this.setState({
773
+ messages: [...this.state.messages, thankYouMessage]
774
+ });
775
+ setTimeout(async () => {
776
+ this.switchToBotMode();
777
+ this.chatResolved = false;
778
+ this.setState({
779
+ messages: [],
780
+ sessionId: null
781
+ // Clear session ID to force recreation
782
+ });
783
+ this.collectingUserInfo = false;
784
+ this.userInfoStep = null;
785
+ this.collectedUserName = "";
786
+ this.collectedUserEmail = "";
787
+ this.collectedUserMobile = "";
788
+ if (this.config.dfProjectId && this.config.dfAgentId && this.state.isOpen) {
789
+ try {
790
+ this.setState({ isLoading: true });
791
+ const dialogflowConfig = {
792
+ dfProjectId: this.config.dfProjectId,
793
+ dfLocation: this.config.dfLocation || "us-central1",
794
+ dfAgentId: this.config.dfAgentId,
795
+ languageCode: this.config.languageCode || "en",
796
+ backendBaseUrl: this.config.backendBaseUrl || "http://localhost:8012"
797
+ };
798
+ const session = await createDialogflowSession(dialogflowConfig);
799
+ this.setState({
800
+ sessionId: session.session_id,
801
+ isLoading: false
802
+ });
803
+ if (session.message) {
804
+ const welcomeMessage = {
805
+ id: `welcome-${Date.now()}`,
806
+ text: session.message,
807
+ sender: "bot",
808
+ timestamp: /* @__PURE__ */ new Date(),
809
+ richContent: session.richContent
810
+ };
811
+ this.setState({
812
+ messages: [welcomeMessage]
813
+ });
814
+ }
815
+ } catch (error) {
816
+ console.error("Error recreating Dialogflow session after handoff:", error);
817
+ this.setState({
818
+ isLoading: false,
819
+ error: error.message || "Failed to initialize chat"
820
+ });
821
+ const fallbackMessage = {
822
+ id: `fallback-${Date.now()}`,
823
+ text: this.config.fallbackWelcomeMessage || "Hello! How can I help you today?",
824
+ sender: "bot",
825
+ timestamp: /* @__PURE__ */ new Date()
826
+ };
827
+ this.setState({
828
+ messages: [fallbackMessage]
829
+ });
830
+ }
831
+ }
832
+ }, 2e3);
833
+ }
834
+ /**
835
+ * Load message history
836
+ */
837
+ async loadMessageHistory(preserveExisting = false) {
838
+ if (!this.chatService || !this.chatId || !this.supportSessionId) {
839
+ return;
840
+ }
841
+ try {
842
+ if (this.chatId && this.supportSessionId) {
843
+ const history = await this.chatService.loadMessageHistory(this.chatId, this.supportSessionId);
844
+ const historyMessages = history.map((msg) => ({
845
+ id: msg.id || `history-${Date.now()}-${Math.random()}`,
846
+ text: msg.content,
847
+ sender: msg.sender_type === "agent" ? "agent" : "user",
848
+ timestamp: new Date(msg.timestamp)
849
+ }));
850
+ if (preserveExisting) {
851
+ const existingIds = new Set(this.state.messages.map((m) => m.id));
852
+ const newMessages = historyMessages.filter((m) => !existingIds.has(m.id));
853
+ const combined = [...this.state.messages, ...newMessages].sort(
854
+ (a, b) => a.timestamp.getTime() - b.timestamp.getTime()
855
+ );
856
+ this.setState({
857
+ messages: combined
858
+ });
859
+ } else {
860
+ this.setState({
861
+ messages: historyMessages
862
+ });
863
+ }
864
+ }
865
+ } catch (error) {
866
+ console.error("Error loading message history:", error);
867
+ if (this.config.debug) {
868
+ const errorMessage = {
869
+ id: `error-${Date.now()}`,
870
+ text: `Failed to load chat history: ${error.message}`,
871
+ sender: "bot",
872
+ timestamp: /* @__PURE__ */ new Date()
873
+ };
874
+ this.setState({
875
+ messages: [...this.state.messages, errorMessage]
876
+ });
877
+ }
878
+ }
879
+ }
880
+ /**
881
+ * Get additional state for UI (not in WidgetState but needed by components)
882
+ */
883
+ getAdditionalState() {
884
+ return {
885
+ wsConnected: this.wsConnected,
886
+ agentTyping: this.agentTyping,
887
+ currentAgent: this.currentAgent,
888
+ isConnectingToAgent: this.isConnectingToAgent,
889
+ agentAccepted: this.agentAccepted,
890
+ chatResolved: this.chatResolved
891
+ };
892
+ }
893
+ /**
894
+ * Cleanup
895
+ */
896
+ destroy() {
897
+ if (this.typingTimeout) {
898
+ clearTimeout(this.typingTimeout);
899
+ }
900
+ if (this.agentTypingTimeout) {
901
+ clearTimeout(this.agentTypingTimeout);
902
+ }
903
+ if (this.chatService) {
904
+ this.chatService.disconnectWebSocket();
905
+ }
906
+ this.listeners.clear();
907
+ }
908
+ }
909
+ function useChatWidget(config) {
910
+ const manager = new WidgetStateManager(config);
911
+ const state = ref(manager.getState());
912
+ const wsConnected = ref(false);
913
+ const agentTyping = ref(false);
914
+ const currentAgent = ref({ name: "Agent" });
915
+ const isConnectingToAgent = ref(false);
916
+ const unsubscribe = manager.subscribe((newState) => {
917
+ state.value = { ...newState };
918
+ const additional = manager.getAdditionalState();
919
+ wsConnected.value = additional.wsConnected;
920
+ agentTyping.value = additional.agentTyping;
921
+ currentAgent.value = additional.currentAgent;
922
+ isConnectingToAgent.value = additional.isConnectingToAgent;
923
+ });
924
+ const isOpen = computed(() => state.value.isOpen);
925
+ const messages = computed(() => state.value.messages);
926
+ const isLoading = computed(() => state.value.isLoading);
927
+ const error = computed(() => state.value.error);
928
+ const chatMode = computed(() => state.value.chatMode);
929
+ const openChat = async () => {
930
+ await manager.openChat();
931
+ };
932
+ const closeChat = () => {
933
+ manager.closeChat();
934
+ };
935
+ const sendMessage = async (text) => {
936
+ await manager.sendMessage(text);
937
+ };
938
+ const toggleChat = async () => {
939
+ await manager.toggleChat();
940
+ };
941
+ const setInputValue = (value) => {
942
+ manager.setInputValue(value);
943
+ };
944
+ const clearError = () => {
945
+ manager.clearError();
946
+ };
947
+ onUnmounted(() => {
948
+ unsubscribe();
949
+ manager.destroy();
950
+ });
951
+ watch(
952
+ () => config,
953
+ (newConfig) => {
954
+ manager.updateConfig(newConfig);
955
+ },
956
+ { deep: true }
957
+ );
958
+ return {
959
+ state,
960
+ isOpen,
961
+ messages,
962
+ isLoading,
963
+ error,
964
+ chatMode,
965
+ wsConnected,
966
+ agentTyping,
967
+ currentAgent,
968
+ isConnectingToAgent,
969
+ openChat,
970
+ closeChat,
971
+ sendMessage,
972
+ toggleChat,
973
+ setInputValue,
974
+ clearError,
975
+ manager
976
+ };
977
+ }
978
+ const _hoisted_1 = { class: "custom-welcome-header" };
979
+ const _hoisted_2 = { class: "custom-welcome-title" };
980
+ const _hoisted_3 = { class: "custom-welcome-message" };
981
+ const _hoisted_4 = { class: "custom-welcome-cta" };
982
+ const _hoisted_5 = {
983
+ key: 2,
984
+ class: "custom-chat-window"
985
+ };
986
+ const _hoisted_6 = { class: "custom-chat-header" };
987
+ const _hoisted_7 = { class: "custom-chat-header-content" };
988
+ const _hoisted_8 = { class: "custom-chat-title" };
989
+ const _hoisted_9 = { class: "custom-chat-subtitle" };
990
+ const _hoisted_10 = {
991
+ key: 0,
992
+ class: "custom-mode-indicator"
993
+ };
994
+ const _hoisted_11 = {
995
+ key: 0,
996
+ class: "custom-mode-badge"
997
+ };
998
+ const _hoisted_12 = {
999
+ key: 1,
1000
+ class: "custom-agent-info"
1001
+ };
1002
+ const _hoisted_13 = { class: "custom-agent-name" };
1003
+ const _hoisted_14 = {
1004
+ key: 2,
1005
+ class: "custom-mode-badge"
1006
+ };
1007
+ const _hoisted_15 = {
1008
+ key: 0,
1009
+ class: "custom-chat-empty"
1010
+ };
1011
+ const _hoisted_16 = {
1012
+ key: 1,
1013
+ class: "custom-chat-empty"
1014
+ };
1015
+ const _hoisted_17 = ["innerHTML"];
1016
+ const _hoisted_18 = {
1017
+ key: 0,
1018
+ class: "custom-chips-container"
1019
+ };
1020
+ const _hoisted_19 = {
1021
+ key: 0,
1022
+ class: "custom-chips-group"
1023
+ };
1024
+ const _hoisted_20 = ["onClick"];
1025
+ const _hoisted_21 = {
1026
+ key: 0,
1027
+ class: "custom-chips-group"
1028
+ };
1029
+ const _hoisted_22 = ["onClick"];
1030
+ const _hoisted_23 = { class: "custom-message-time" };
1031
+ const _hoisted_24 = {
1032
+ key: 2,
1033
+ class: "custom-message custom-message-bot"
1034
+ };
1035
+ const _hoisted_25 = {
1036
+ key: 3,
1037
+ class: "custom-message custom-message-bot"
1038
+ };
1039
+ const _hoisted_26 = {
1040
+ key: 4,
1041
+ class: "custom-agent-typing-indicator"
1042
+ };
1043
+ const _hoisted_27 = ["value", "placeholder", "disabled"];
1044
+ const _hoisted_28 = ["disabled"];
1045
+ const _sfc_main = /* @__PURE__ */ defineComponent({
1046
+ ...{
1047
+ inheritAttrs: false
1048
+ // We handle attrs manually via v-bind="$attrs"
1049
+ },
1050
+ __name: "ChatWidget",
1051
+ props: {
1052
+ dfProjectId: {},
1053
+ dfLocation: {},
1054
+ dfAgentId: {},
1055
+ languageCode: {},
1056
+ backendBaseUrl: {},
1057
+ backendWsUrl: {},
1058
+ title: { default: "💬 BlockSpark AI Assistant" },
1059
+ subtitle: { default: "We're here to help" },
1060
+ welcomeTitle: { default: "👋 Welcome to BlockSpark" },
1061
+ welcomeMessage: { default: "My name is BlockSpark AI Assistant and I'll guide you." },
1062
+ welcomeCta: { default: "💬 Click here to start chatting!" },
1063
+ showWelcomePopup: { type: Boolean, default: true },
1064
+ welcomePopupDelay: { default: 1500 },
1065
+ fallbackWelcomeMessage: { default: "Hello! I'm BlockSpark AI Assistant. How can I help you today?" },
1066
+ inputPlaceholder: { default: "Type your message..." },
1067
+ emptyStateMessage: { default: "Hi! I'm BlockSpark AI Assistant. How can I help you today?" },
1068
+ debug: { type: Boolean, default: false }
1069
+ },
1070
+ setup(__props) {
1071
+ const props = __props;
1072
+ const {
1073
+ state,
1074
+ isOpen,
1075
+ messages,
1076
+ isLoading,
1077
+ error,
1078
+ chatMode,
1079
+ wsConnected,
1080
+ agentTyping,
1081
+ currentAgent,
1082
+ isConnectingToAgent: isConnectingToAgentFromComposable,
1083
+ openChat,
1084
+ closeChat,
1085
+ sendMessage,
1086
+ toggleChat,
1087
+ setInputValue,
1088
+ clearError,
1089
+ manager
1090
+ } = useChatWidget(props);
1091
+ const messagesEndRef = ref(null);
1092
+ const messagesContainer = ref(null);
1093
+ const config = computed(() => props);
1094
+ const isInitializing = computed(() => {
1095
+ return state.value.isLoading && state.value.messages.length === 0 && !state.value.sessionId;
1096
+ });
1097
+ const isStartingNewChat = computed(() => {
1098
+ return false;
1099
+ });
1100
+ const isConnectingToAgent = isConnectingToAgentFromComposable;
1101
+ watch(
1102
+ () => messages.value.length,
1103
+ () => {
1104
+ nextTick(() => {
1105
+ messagesEndRef.value?.scrollIntoView({ behavior: "smooth" });
1106
+ });
1107
+ }
1108
+ );
1109
+ const handleOpenChat = async () => {
1110
+ if (!state.value.isOpen) {
1111
+ state.value.isOpen = true;
1112
+ state.value.showWelcomePopup = false;
1113
+ }
1114
+ await openChat();
1115
+ };
1116
+ const handleCloseChat = () => {
1117
+ closeChat();
1118
+ };
1119
+ const handleCloseWelcomePopup = () => {
1120
+ manager.closeWelcomePopup();
1121
+ };
1122
+ const handleInput = (event) => {
1123
+ const target = event.target;
1124
+ setInputValue(target.value);
1125
+ };
1126
+ const handleSubmit = async (event) => {
1127
+ event.preventDefault();
1128
+ if (state.value.inputValue.trim()) {
1129
+ await sendMessage(state.value.inputValue);
1130
+ }
1131
+ };
1132
+ const handleChipClick = async (chipText, payload) => {
1133
+ const messageToSend = chipText || payload || "";
1134
+ await sendMessage(messageToSend);
1135
+ };
1136
+ const formatTime = (date) => {
1137
+ return date.toLocaleTimeString([], {
1138
+ hour: "2-digit",
1139
+ minute: "2-digit"
1140
+ });
1141
+ };
1142
+ const isHandoffMessage = (text) => {
1143
+ return text.includes("👤") || text.includes("being connected") || text.includes("live support agent") || text.includes("transfer your conversation");
1144
+ };
1145
+ onMounted(() => {
1146
+ if (config.value.showWelcomePopup && typeof window !== "undefined") {
1147
+ manager.initializeWelcomePopup();
1148
+ }
1149
+ });
1150
+ return (_ctx, _cache) => {
1151
+ return openBlock(), createElementBlock("div", mergeProps({ class: "custom-chat-widget" }, _ctx.$attrs), [
1152
+ unref(state).showWelcomePopup && !unref(state).isOpen ? (openBlock(), createElementBlock("div", {
1153
+ key: 0,
1154
+ class: "custom-welcome-popup",
1155
+ onClick: handleOpenChat
1156
+ }, [
1157
+ createElementVNode("div", _hoisted_1, [
1158
+ createElementVNode("div", _hoisted_2, toDisplayString(config.value.welcomeTitle), 1),
1159
+ createElementVNode("button", {
1160
+ class: "custom-close-popup",
1161
+ onClick: withModifiers(handleCloseWelcomePopup, ["stop"]),
1162
+ "aria-label": "Close welcome popup"
1163
+ }, " × ")
1164
+ ]),
1165
+ createElementVNode("div", _hoisted_3, toDisplayString(config.value.welcomeMessage), 1),
1166
+ createElementVNode("div", _hoisted_4, toDisplayString(config.value.welcomeCta), 1)
1167
+ ])) : createCommentVNode("", true),
1168
+ !unref(state).isOpen ? (openBlock(), createElementBlock("button", {
1169
+ key: 1,
1170
+ class: "custom-chat-toggle-btn",
1171
+ onClick: handleOpenChat,
1172
+ "aria-label": "Open chat"
1173
+ }, [..._cache[0] || (_cache[0] = [
1174
+ createElementVNode("svg", {
1175
+ width: "24",
1176
+ height: "24",
1177
+ viewBox: "0 0 24 24",
1178
+ fill: "none",
1179
+ stroke: "currentColor",
1180
+ "stroke-width": "2",
1181
+ "stroke-linecap": "round",
1182
+ "stroke-linejoin": "round"
1183
+ }, [
1184
+ createElementVNode("path", { d: "M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z" })
1185
+ ], -1)
1186
+ ])])) : createCommentVNode("", true),
1187
+ unref(state).isOpen ? (openBlock(), createElementBlock("div", _hoisted_5, [
1188
+ createElementVNode("div", _hoisted_6, [
1189
+ createElementVNode("div", _hoisted_7, [
1190
+ createElementVNode("div", _hoisted_8, toDisplayString(config.value.title), 1),
1191
+ createElementVNode("div", _hoisted_9, [
1192
+ createTextVNode(toDisplayString(config.value.subtitle) + " ", 1),
1193
+ unref(state).chatMode === "human" ? (openBlock(), createElementBlock("span", _hoisted_10, " • " + toDisplayString(unref(wsConnected) ? "🟢 Connected" : "🟡 Connecting..."), 1)) : createCommentVNode("", true)
1194
+ ]),
1195
+ unref(state).chatMode === "human" ? (openBlock(), createElementBlock("div", _hoisted_11, " Human Support Mode ")) : createCommentVNode("", true),
1196
+ unref(state).chatMode === "human" ? (openBlock(), createElementBlock("div", _hoisted_12, [
1197
+ _cache[1] || (_cache[1] = createElementVNode("span", { class: "custom-agent-label" }, "Agent:", -1)),
1198
+ createElementVNode("span", _hoisted_13, toDisplayString(unref(currentAgent).name), 1)
1199
+ ])) : createCommentVNode("", true),
1200
+ unref(state).chatMode === "ai" ? (openBlock(), createElementBlock("div", _hoisted_14, " Bot Mode ")) : createCommentVNode("", true)
1201
+ ]),
1202
+ createElementVNode("button", {
1203
+ class: "custom-chat-close-btn",
1204
+ onClick: handleCloseChat,
1205
+ "aria-label": "Close chat"
1206
+ }, " × ")
1207
+ ]),
1208
+ createElementVNode("div", {
1209
+ class: "custom-chat-messages",
1210
+ ref_key: "messagesContainer",
1211
+ ref: messagesContainer
1212
+ }, [
1213
+ isInitializing.value && unref(state).messages.length === 0 ? (openBlock(), createElementBlock("div", _hoisted_15, [..._cache[2] || (_cache[2] = [
1214
+ createElementVNode("div", { class: "custom-typing-indicator" }, [
1215
+ createElementVNode("span"),
1216
+ createElementVNode("span"),
1217
+ createElementVNode("span")
1218
+ ], -1),
1219
+ createElementVNode("p", null, "Initializing chat...", -1)
1220
+ ])])) : !isInitializing.value && unref(state).messages.length === 0 ? (openBlock(), createElementBlock("div", _hoisted_16, [
1221
+ _cache[3] || (_cache[3] = createElementVNode("div", { class: "custom-chat-empty-icon" }, "👋", -1)),
1222
+ createElementVNode("p", null, toDisplayString(config.value.emptyStateMessage), 1)
1223
+ ])) : createCommentVNode("", true),
1224
+ (openBlock(true), createElementBlock(Fragment, null, renderList(unref(state).messages, (message) => {
1225
+ return openBlock(), createElementBlock("div", {
1226
+ key: message.id,
1227
+ class: normalizeClass([
1228
+ "custom-message",
1229
+ `custom-message-${message.sender}`,
1230
+ { "custom-handoff-message": isHandoffMessage(message.text) }
1231
+ ])
1232
+ }, [
1233
+ createElementVNode("div", {
1234
+ class: normalizeClass(["custom-message-content", { "custom-handoff-content": isHandoffMessage(message.text) }]),
1235
+ innerHTML: unref(safeLinkifyText)(message.text).replace(/\n/g, "<br>")
1236
+ }, null, 10, _hoisted_17),
1237
+ message.richContent && Array.isArray(message.richContent) && message.richContent.length > 0 ? (openBlock(), createElementBlock("div", _hoisted_18, [
1238
+ (openBlock(true), createElementBlock(Fragment, null, renderList(message.richContent, (contentGroup, groupIndex) => {
1239
+ return openBlock(), createElementBlock(Fragment, { key: groupIndex }, [
1240
+ !Array.isArray(contentGroup) ? (openBlock(), createElementBlock(Fragment, { key: 0 }, [
1241
+ contentGroup.type === "chips" && contentGroup.options ? (openBlock(), createElementBlock("div", _hoisted_19, [
1242
+ (openBlock(true), createElementBlock(Fragment, null, renderList(contentGroup.options, (chip, chipIndex) => {
1243
+ return openBlock(), createElementBlock("button", {
1244
+ key: chipIndex,
1245
+ class: "custom-chip-button",
1246
+ type: "button",
1247
+ onClick: ($event) => handleChipClick(chip.text, chip.payload)
1248
+ }, toDisplayString(chip.text), 9, _hoisted_20);
1249
+ }), 128))
1250
+ ])) : createCommentVNode("", true)
1251
+ ], 64)) : (openBlock(true), createElementBlock(Fragment, { key: 1 }, renderList(contentGroup, (content, contentIndex) => {
1252
+ return openBlock(), createElementBlock(Fragment, {
1253
+ key: `${groupIndex}-${contentIndex}`
1254
+ }, [
1255
+ content.type === "chips" && content.options ? (openBlock(), createElementBlock("div", _hoisted_21, [
1256
+ (openBlock(true), createElementBlock(Fragment, null, renderList(content.options, (chip, chipIndex) => {
1257
+ return openBlock(), createElementBlock("button", {
1258
+ key: chipIndex,
1259
+ class: "custom-chip-button",
1260
+ type: "button",
1261
+ onClick: ($event) => handleChipClick(chip.text, chip.payload)
1262
+ }, toDisplayString(chip.text), 9, _hoisted_22);
1263
+ }), 128))
1264
+ ])) : createCommentVNode("", true)
1265
+ ], 64);
1266
+ }), 128))
1267
+ ], 64);
1268
+ }), 128))
1269
+ ])) : createCommentVNode("", true),
1270
+ createElementVNode("div", _hoisted_23, toDisplayString(formatTime(message.timestamp)), 1)
1271
+ ], 2);
1272
+ }), 128)),
1273
+ unref(state).isLoading ? (openBlock(), createElementBlock("div", _hoisted_24, [..._cache[4] || (_cache[4] = [
1274
+ createElementVNode("div", { class: "custom-typing-indicator" }, [
1275
+ createElementVNode("span"),
1276
+ createElementVNode("span"),
1277
+ createElementVNode("span")
1278
+ ], -1)
1279
+ ])])) : createCommentVNode("", true),
1280
+ unref(isConnectingToAgent) ? (openBlock(), createElementBlock("div", _hoisted_25, [..._cache[5] || (_cache[5] = [
1281
+ createElementVNode("div", { class: "custom-typing-indicator" }, [
1282
+ createElementVNode("span"),
1283
+ createElementVNode("span"),
1284
+ createElementVNode("span")
1285
+ ], -1),
1286
+ createElementVNode("div", { class: "custom-message-content" }, " Connecting to agent... ", -1)
1287
+ ])])) : createCommentVNode("", true),
1288
+ unref(state).chatMode === "human" && unref(agentTyping) ? (openBlock(), createElementBlock("div", _hoisted_26, [..._cache[6] || (_cache[6] = [
1289
+ createElementVNode("span", { class: "custom-typing-dots" }, [
1290
+ createElementVNode("span"),
1291
+ createElementVNode("span"),
1292
+ createElementVNode("span")
1293
+ ], -1),
1294
+ createElementVNode("span", { class: "custom-typing-text" }, "Agent is typing...", -1)
1295
+ ])])) : createCommentVNode("", true),
1296
+ createElementVNode("div", {
1297
+ ref_key: "messagesEndRef",
1298
+ ref: messagesEndRef
1299
+ }, null, 512)
1300
+ ], 512),
1301
+ createElementVNode("form", {
1302
+ class: "custom-chat-input-form",
1303
+ onSubmit: withModifiers(handleSubmit, ["prevent"])
1304
+ }, [
1305
+ createElementVNode("input", {
1306
+ type: "text",
1307
+ class: "custom-chat-input",
1308
+ value: unref(state).inputValue,
1309
+ onInput: handleInput,
1310
+ placeholder: config.value.inputPlaceholder,
1311
+ disabled: unref(state).isLoading || isInitializing.value || isStartingNewChat.value
1312
+ }, null, 40, _hoisted_27),
1313
+ createElementVNode("button", {
1314
+ type: "submit",
1315
+ class: "custom-chat-send-btn",
1316
+ disabled: !unref(state).inputValue.trim() || unref(state).isLoading || isInitializing.value || isStartingNewChat.value
1317
+ }, [..._cache[7] || (_cache[7] = [
1318
+ createElementVNode("svg", {
1319
+ width: "20",
1320
+ height: "20",
1321
+ viewBox: "0 0 24 24",
1322
+ fill: "none",
1323
+ stroke: "currentColor",
1324
+ "stroke-width": "2",
1325
+ "stroke-linecap": "round",
1326
+ "stroke-linejoin": "round"
1327
+ }, [
1328
+ createElementVNode("line", {
1329
+ x1: "22",
1330
+ y1: "2",
1331
+ x2: "11",
1332
+ y2: "13"
1333
+ }),
1334
+ createElementVNode("polygon", { points: "22 2 15 22 11 13 2 9 22 2" })
1335
+ ], -1)
1336
+ ])], 8, _hoisted_28)
1337
+ ], 32)
1338
+ ])) : createCommentVNode("", true)
1339
+ ], 16);
1340
+ };
1341
+ }
1342
+ });
1343
+ const _export_sfc = (sfc, props) => {
1344
+ const target = sfc.__vccOpts || sfc;
1345
+ for (const [key, val] of props) {
1346
+ target[key] = val;
1347
+ }
1348
+ return target;
1349
+ };
1350
+ const ChatWidgetComponent = /* @__PURE__ */ _export_sfc(_sfc_main, [["__scopeId", "data-v-0d32fcf4"]]);
1351
+ export {
1352
+ ChatWidgetComponent as C,
1353
+ WidgetStateManager as W,
1354
+ useChatWidget as u
1355
+ };
1356
+ //# sourceMappingURL=ChatWidget-Cljd-Tfm.js.map