@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.
- package/README.md +270 -0
- package/dist/ChatWidget-BM2lXfeE.cjs +2 -0
- package/dist/ChatWidget-BM2lXfeE.cjs.map +1 -0
- package/dist/ChatWidget-Cljd-Tfm.js +1356 -0
- package/dist/ChatWidget-Cljd-Tfm.js.map +1 -0
- package/dist/adapters/vue/index.d.ts +7 -0
- package/dist/adapters/vue/index.d.ts.map +1 -0
- package/dist/adapters/vue/useChatMode.d.ts +21 -0
- package/dist/adapters/vue/useChatMode.d.ts.map +1 -0
- package/dist/components/ChatWidget.d.ts +39 -0
- package/dist/components/ChatWidget.d.ts.map +1 -0
- package/dist/composables/useChatWidget.d.ts +35 -0
- package/dist/composables/useChatWidget.d.ts.map +1 -0
- package/dist/core/stateManager.d.ts +136 -0
- package/dist/core/stateManager.d.ts.map +1 -0
- package/dist/core/types.d.ts +64 -0
- package/dist/core/types.d.ts.map +1 -0
- package/dist/entry/next.d.ts +9 -0
- package/dist/entry/next.d.ts.map +1 -0
- package/dist/entry/nuxt.d.ts +12 -0
- package/dist/entry/nuxt.d.ts.map +1 -0
- package/dist/entry/react.d.ts +10 -0
- package/dist/entry/react.d.ts.map +1 -0
- package/dist/entry/vanilla.d.ts +33 -0
- package/dist/entry/vanilla.d.ts.map +1 -0
- package/dist/entry/vite.d.ts +11 -0
- package/dist/entry/vite.d.ts.map +1 -0
- package/dist/entry/vue.d.ts +12 -0
- package/dist/entry/vue.d.ts.map +1 -0
- package/dist/hooks/useChatMode.d.ts +17 -0
- package/dist/hooks/useChatMode.d.ts.map +1 -0
- package/dist/index.cjs.js +2 -0
- package/dist/index.cjs.js.map +1 -0
- package/dist/index.d.ts +21 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.esm.js +1383 -0
- package/dist/index.esm.js.map +1 -0
- package/dist/nuxt.cjs.js +2 -0
- package/dist/nuxt.cjs.js.map +1 -0
- package/dist/nuxt.esm.js +9 -0
- package/dist/nuxt.esm.js.map +1 -0
- package/dist/sanitize-C8MB41vY.cjs +4 -0
- package/dist/sanitize-C8MB41vY.cjs.map +1 -0
- package/dist/sanitize-Cm1kskSD.js +1842 -0
- package/dist/sanitize-Cm1kskSD.js.map +1 -0
- package/dist/services/chatService.d.ts +144 -0
- package/dist/services/chatService.d.ts.map +1 -0
- package/dist/services/dialogflowBackendService.d.ts +36 -0
- package/dist/services/dialogflowBackendService.d.ts.map +1 -0
- package/dist/services/dialogflowClient.d.ts +36 -0
- package/dist/services/dialogflowClient.d.ts.map +1 -0
- package/dist/services/sessionManager.d.ts +13 -0
- package/dist/services/sessionManager.d.ts.map +1 -0
- package/dist/styles.css +1 -0
- package/dist/types.d.ts +5 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/utils/dialogflowHandler.d.ts +31 -0
- package/dist/utils/dialogflowHandler.d.ts.map +1 -0
- package/dist/utils/frameworkDetector.d.ts +17 -0
- package/dist/utils/frameworkDetector.d.ts.map +1 -0
- package/dist/utils/sanitize.d.ts +25 -0
- package/dist/utils/sanitize.d.ts.map +1 -0
- package/dist/utils/ssr.d.ts +35 -0
- package/dist/utils/ssr.d.ts.map +1 -0
- package/dist/vue.cjs.js +2 -0
- package/dist/vue.cjs.js.map +1 -0
- package/dist/vue.esm.js +9 -0
- package/dist/vue.esm.js.map +1 -0
- 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
|