@wabbit-dashboard/embed 1.0.15 → 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/dist/wabbit-embed.cjs.js +99 -90
- package/dist/wabbit-embed.cjs.js.map +1 -1
- package/dist/wabbit-embed.d.ts +1 -1
- package/dist/wabbit-embed.esm.js +99 -90
- package/dist/wabbit-embed.esm.js.map +1 -1
- package/dist/wabbit-embed.umd.js +99 -90
- package/dist/wabbit-embed.umd.js.map +1 -1
- package/dist/wabbit-embed.umd.min.js +1 -1
- package/dist/wabbit-embed.umd.min.js.map +1 -1
- package/package.json +1 -1
package/dist/wabbit-embed.cjs.js
CHANGED
|
@@ -161,99 +161,41 @@ class Wabbit {
|
|
|
161
161
|
const mergedConfig = mergeConfig(config);
|
|
162
162
|
Wabbit.instance = new Wabbit(mergedConfig);
|
|
163
163
|
// Store email capture widget reference for use in callbacks
|
|
164
|
-
// This allows chat widget to trigger email capture without tight coupling
|
|
165
164
|
let emailCaptureWidgetRef = null;
|
|
166
|
-
// Track if email capture is enabled (determined by server config)
|
|
167
|
-
let emailCaptureEnabled = false;
|
|
168
165
|
// Initialize widgets based on merged config
|
|
169
166
|
if (mergedConfig.chat?.enabled && mergedConfig.chat) {
|
|
170
167
|
const chat = mergedConfig.chat;
|
|
171
168
|
const collectionId = chat.collectionId;
|
|
169
|
+
const userOnChatReady = chat.onChatReady || config.onChatReady;
|
|
172
170
|
// Derive chat service API URL from WebSocket URL
|
|
173
|
-
// The email capture config endpoint is on the chat service, not the dashboard
|
|
174
171
|
const wsUrl = mergedConfig.wsUrl || chat.wsUrl || 'wss://chat.insourcedata.ai/ws/chat';
|
|
175
172
|
const chatServiceUrl = wsUrl
|
|
176
173
|
.replace(/^wss:\/\//, 'https://')
|
|
177
174
|
.replace(/^ws:\/\//, 'http://')
|
|
178
|
-
.replace(/\/ws\/chat.*$/, '');
|
|
179
|
-
//
|
|
180
|
-
|
|
181
|
-
.then(res => res.json())
|
|
182
|
-
.then(data => {
|
|
183
|
-
if (data.success && data.config?.enabled) {
|
|
184
|
-
return data.config;
|
|
185
|
-
}
|
|
186
|
-
return null;
|
|
187
|
-
})
|
|
188
|
-
.catch(err => {
|
|
189
|
-
console.warn('[Wabbit] Failed to fetch email capture config:', err);
|
|
190
|
-
return null;
|
|
191
|
-
});
|
|
192
|
-
// Import ChatWidget dynamically to avoid circular dependencies
|
|
193
|
-
Promise.resolve().then(function () { return ChatWidget$1; }).then(async ({ ChatWidget }) => {
|
|
194
|
-
// Check if instance was destroyed during async import (e.g., React StrictMode)
|
|
175
|
+
.replace(/\/ws\/chat.*$/, '');
|
|
176
|
+
// Import and initialize ChatWidget immediately (don't wait for email capture config)
|
|
177
|
+
Promise.resolve().then(function () { return ChatWidget$1; }).then(({ ChatWidget }) => {
|
|
195
178
|
if (!Wabbit.instance) {
|
|
196
179
|
console.warn('[Wabbit] Instance was destroyed before chat widget could initialize');
|
|
197
180
|
return;
|
|
198
181
|
}
|
|
199
|
-
// Wait for email capture config to be fetched
|
|
200
|
-
const serverEmailConfig = await emailCaptureConfigPromise;
|
|
201
|
-
emailCaptureEnabled = !!serverEmailConfig;
|
|
202
|
-
// Initialize email capture widget if server config says it's enabled
|
|
203
|
-
if (serverEmailConfig) {
|
|
204
|
-
const { EmailCaptureWidget } = await Promise.resolve().then(function () { return EmailCaptureWidget$1; });
|
|
205
|
-
if (!Wabbit.instance) {
|
|
206
|
-
console.warn('[Wabbit] Instance was destroyed before email capture widget could initialize');
|
|
207
|
-
return;
|
|
208
|
-
}
|
|
209
|
-
// Convert server config format to widget config format
|
|
210
|
-
const triggerAfterMessages = serverEmailConfig.trigger?.type === 'message_count'
|
|
211
|
-
? serverEmailConfig.trigger.messageCount
|
|
212
|
-
: 3;
|
|
213
|
-
// Build fields array from server config
|
|
214
|
-
const fields = ['email'];
|
|
215
|
-
if (serverEmailConfig.fields?.name?.enabled)
|
|
216
|
-
fields.push('name');
|
|
217
|
-
if (serverEmailConfig.fields?.company?.enabled)
|
|
218
|
-
fields.push('company');
|
|
219
|
-
const emailCaptureConfig = {
|
|
220
|
-
enabled: true,
|
|
221
|
-
triggerAfterMessages,
|
|
222
|
-
title: serverEmailConfig.modal?.title,
|
|
223
|
-
description: serverEmailConfig.modal?.description,
|
|
224
|
-
fields,
|
|
225
|
-
onCapture: mergedConfig.emailCapture?.onCapture
|
|
226
|
-
};
|
|
227
|
-
const emailCaptureWidget = new EmailCaptureWidget(emailCaptureConfig);
|
|
228
|
-
emailCaptureWidget.init();
|
|
229
|
-
Wabbit.instance.emailCaptureWidget = emailCaptureWidget;
|
|
230
|
-
emailCaptureWidgetRef = emailCaptureWidget;
|
|
231
|
-
console.log('[Wabbit] Email capture initialized from server config');
|
|
232
|
-
}
|
|
233
|
-
const userOnChatReady = chat.onChatReady || config.onChatReady;
|
|
234
182
|
const chatConfig = {
|
|
235
183
|
enabled: chat.enabled,
|
|
236
184
|
collectionId: chat.collectionId,
|
|
237
|
-
// Use chat-specific apiKey/apiUrl if provided, otherwise inherit from WabbitConfig
|
|
238
185
|
apiKey: chat.apiKey || mergedConfig.apiKey,
|
|
239
186
|
apiUrl: chat.apiUrl || mergedConfig.apiUrl,
|
|
240
|
-
wsUrl: mergedConfig.wsUrl || chat.wsUrl,
|
|
241
|
-
// Embedding mode
|
|
187
|
+
wsUrl: mergedConfig.wsUrl || chat.wsUrl,
|
|
242
188
|
mode: chat.mode,
|
|
243
189
|
container: chat.container,
|
|
244
|
-
// Widget mode options
|
|
245
190
|
position: chat.position,
|
|
246
191
|
triggerType: chat.triggerType,
|
|
247
192
|
triggerDelay: chat.triggerDelay,
|
|
248
|
-
// Appearance options
|
|
249
193
|
theme: chat.theme,
|
|
250
194
|
primaryColor: chat.primaryColor,
|
|
251
195
|
welcomeMessage: chat.welcomeMessage,
|
|
252
196
|
placeholder: chat.placeholder,
|
|
253
|
-
// Header customization
|
|
254
197
|
showHeader: chat.showHeader,
|
|
255
198
|
headerTitle: chat.headerTitle,
|
|
256
|
-
// Callbacks
|
|
257
199
|
onChatReady: () => {
|
|
258
200
|
// Set WebSocket client on email capture widget when chat is ready
|
|
259
201
|
if (emailCaptureWidgetRef && Wabbit.instance?.chatWidget) {
|
|
@@ -262,24 +204,68 @@ class Wabbit {
|
|
|
262
204
|
emailCaptureWidgetRef.setWebSocketClient(chatWidget.wsClient);
|
|
263
205
|
}
|
|
264
206
|
}
|
|
265
|
-
// Call user's callback
|
|
266
207
|
if (userOnChatReady) {
|
|
267
208
|
userOnChatReady();
|
|
268
209
|
}
|
|
269
210
|
},
|
|
270
|
-
//
|
|
271
|
-
onUserMessage:
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
emailCaptureWidgetRef.handleMessage();
|
|
275
|
-
}
|
|
211
|
+
// Always set onUserMessage - it checks if emailCaptureWidgetRef exists at call time
|
|
212
|
+
onUserMessage: () => {
|
|
213
|
+
if (emailCaptureWidgetRef) {
|
|
214
|
+
emailCaptureWidgetRef.handleMessage();
|
|
276
215
|
}
|
|
277
|
-
|
|
216
|
+
}
|
|
278
217
|
};
|
|
279
218
|
const chatWidget = new ChatWidget(chatConfig);
|
|
280
219
|
chatWidget.init();
|
|
281
220
|
Wabbit.instance.chatWidget = chatWidget;
|
|
282
221
|
});
|
|
222
|
+
// Fetch email capture config from server (in parallel with chat init)
|
|
223
|
+
fetch(`${chatServiceUrl}/api/email-capture/config/${collectionId}`)
|
|
224
|
+
.then(res => res.json())
|
|
225
|
+
.then(async (data) => {
|
|
226
|
+
if (!data.success || !data.config?.enabled) {
|
|
227
|
+
return; // Email capture not enabled
|
|
228
|
+
}
|
|
229
|
+
if (!Wabbit.instance) {
|
|
230
|
+
return; // Instance was destroyed
|
|
231
|
+
}
|
|
232
|
+
const serverConfig = data.config;
|
|
233
|
+
const { EmailCaptureWidget } = await Promise.resolve().then(function () { return EmailCaptureWidget$1; });
|
|
234
|
+
if (!Wabbit.instance) {
|
|
235
|
+
return;
|
|
236
|
+
}
|
|
237
|
+
// Convert server config to widget config
|
|
238
|
+
const triggerAfterMessages = serverConfig.trigger?.type === 'message_count'
|
|
239
|
+
? serverConfig.trigger.messageCount
|
|
240
|
+
: 3;
|
|
241
|
+
const fields = ['email'];
|
|
242
|
+
if (serverConfig.fields?.name?.enabled)
|
|
243
|
+
fields.push('name');
|
|
244
|
+
if (serverConfig.fields?.company?.enabled)
|
|
245
|
+
fields.push('company');
|
|
246
|
+
const emailCaptureConfig = {
|
|
247
|
+
enabled: true,
|
|
248
|
+
triggerAfterMessages,
|
|
249
|
+
title: serverConfig.modal?.title,
|
|
250
|
+
description: serverConfig.modal?.description,
|
|
251
|
+
fields,
|
|
252
|
+
onCapture: mergedConfig.emailCapture?.onCapture
|
|
253
|
+
};
|
|
254
|
+
const emailCaptureWidget = new EmailCaptureWidget(emailCaptureConfig);
|
|
255
|
+
emailCaptureWidget.init();
|
|
256
|
+
Wabbit.instance.emailCaptureWidget = emailCaptureWidget;
|
|
257
|
+
emailCaptureWidgetRef = emailCaptureWidget;
|
|
258
|
+
// If chat is already ready, set the WebSocket client now
|
|
259
|
+
if (Wabbit.instance.chatWidget) {
|
|
260
|
+
const chatWidget = Wabbit.instance.chatWidget;
|
|
261
|
+
if (chatWidget.wsClient) {
|
|
262
|
+
emailCaptureWidget.setWebSocketClient(chatWidget.wsClient);
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
})
|
|
266
|
+
.catch(err => {
|
|
267
|
+
console.warn('[Wabbit] Failed to fetch email capture config:', err);
|
|
268
|
+
});
|
|
283
269
|
}
|
|
284
270
|
if (mergedConfig.forms?.enabled && mergedConfig.forms) {
|
|
285
271
|
// Import FormWidget dynamically to avoid circular dependencies
|
|
@@ -985,8 +971,8 @@ class ChatPanel {
|
|
|
985
971
|
if (this.options.welcomeMessage) {
|
|
986
972
|
this.addSystemMessage(this.options.welcomeMessage);
|
|
987
973
|
}
|
|
988
|
-
// Ensure scroll to bottom after DOM settles
|
|
989
|
-
|
|
974
|
+
// Ensure scroll to bottom after DOM settles
|
|
975
|
+
this.scrollToBottom();
|
|
990
976
|
return this.element;
|
|
991
977
|
}
|
|
992
978
|
/**
|
|
@@ -1094,8 +1080,7 @@ class ChatPanel {
|
|
|
1094
1080
|
if (this.messagesContainer) {
|
|
1095
1081
|
this.messagesContainer.innerHTML = '';
|
|
1096
1082
|
messages.forEach((msg) => this.renderMessage(msg));
|
|
1097
|
-
|
|
1098
|
-
setTimeout(() => this.scrollToBottom(), 100);
|
|
1083
|
+
this.scrollToBottom();
|
|
1099
1084
|
}
|
|
1100
1085
|
}
|
|
1101
1086
|
/**
|
|
@@ -1129,8 +1114,9 @@ class ChatPanel {
|
|
|
1129
1114
|
<div class="wabbit-chat-typing-dot"></div>
|
|
1130
1115
|
`;
|
|
1131
1116
|
this.messagesContainer.appendChild(typing);
|
|
1132
|
-
this.scrollToBottom();
|
|
1133
1117
|
}
|
|
1118
|
+
// Always scroll after changing typing indicator state
|
|
1119
|
+
this.scrollToBottom();
|
|
1134
1120
|
}
|
|
1135
1121
|
this.updateSendButtonState();
|
|
1136
1122
|
}
|
|
@@ -1212,12 +1198,38 @@ class ChatPanel {
|
|
|
1212
1198
|
}
|
|
1213
1199
|
}
|
|
1214
1200
|
/**
|
|
1215
|
-
* Scroll to bottom of messages
|
|
1201
|
+
* Scroll to bottom of messages - forces scroll with multiple attempts
|
|
1216
1202
|
*/
|
|
1217
1203
|
scrollToBottom() {
|
|
1218
|
-
if (this.messagesContainer)
|
|
1219
|
-
|
|
1220
|
-
|
|
1204
|
+
if (!this.messagesContainer)
|
|
1205
|
+
return;
|
|
1206
|
+
const container = this.messagesContainer;
|
|
1207
|
+
const forceScroll = () => {
|
|
1208
|
+
// Force the container to recognize its scroll height
|
|
1209
|
+
const scrollHeight = container.scrollHeight;
|
|
1210
|
+
const clientHeight = container.clientHeight;
|
|
1211
|
+
// Only scroll if there's content to scroll
|
|
1212
|
+
if (scrollHeight > clientHeight) {
|
|
1213
|
+
container.scrollTop = scrollHeight;
|
|
1214
|
+
}
|
|
1215
|
+
else {
|
|
1216
|
+
// If heights are equal, set to a large value to force bottom
|
|
1217
|
+
container.scrollTop = 999999;
|
|
1218
|
+
}
|
|
1219
|
+
};
|
|
1220
|
+
// Immediate
|
|
1221
|
+
forceScroll();
|
|
1222
|
+
// After next frame
|
|
1223
|
+
requestAnimationFrame(() => {
|
|
1224
|
+
forceScroll();
|
|
1225
|
+
requestAnimationFrame(forceScroll);
|
|
1226
|
+
});
|
|
1227
|
+
// Multiple delayed attempts
|
|
1228
|
+
setTimeout(forceScroll, 10);
|
|
1229
|
+
setTimeout(forceScroll, 50);
|
|
1230
|
+
setTimeout(forceScroll, 100);
|
|
1231
|
+
setTimeout(forceScroll, 200);
|
|
1232
|
+
setTimeout(forceScroll, 500);
|
|
1221
1233
|
}
|
|
1222
1234
|
/**
|
|
1223
1235
|
* Show the panel
|
|
@@ -1225,13 +1237,10 @@ class ChatPanel {
|
|
|
1225
1237
|
show() {
|
|
1226
1238
|
if (this.element) {
|
|
1227
1239
|
this.element.style.display = 'flex';
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
this.
|
|
1231
|
-
|
|
1232
|
-
this.inputElement.focus();
|
|
1233
|
-
}
|
|
1234
|
-
}, 100);
|
|
1240
|
+
this.scrollToBottom();
|
|
1241
|
+
if (this.inputElement) {
|
|
1242
|
+
this.inputElement.focus();
|
|
1243
|
+
}
|
|
1235
1244
|
}
|
|
1236
1245
|
}
|
|
1237
1246
|
/**
|
|
@@ -1742,8 +1751,9 @@ function injectChatStyles(primaryColor = '#6366f1', theme) {
|
|
|
1742
1751
|
|
|
1743
1752
|
/* Inline mode messages area fills available space */
|
|
1744
1753
|
.wabbit-chat-panel-inline .wabbit-chat-messages {
|
|
1745
|
-
flex: 1;
|
|
1746
|
-
min-height:
|
|
1754
|
+
flex: 1 1 0; /* flex-grow, flex-shrink, flex-basis: 0 for proper sizing */
|
|
1755
|
+
min-height: 0; /* Critical for nested flex scroll to work */
|
|
1756
|
+
overflow-y: auto !important;
|
|
1747
1757
|
}
|
|
1748
1758
|
|
|
1749
1759
|
/* Full-height variant for inline mode (used in standalone chat pages) */
|
|
@@ -3731,7 +3741,6 @@ class EmailCaptureWidget {
|
|
|
3731
3741
|
if (this.emailCaptured) {
|
|
3732
3742
|
return;
|
|
3733
3743
|
}
|
|
3734
|
-
// Only count user messages
|
|
3735
3744
|
this.messageCount++;
|
|
3736
3745
|
const triggerAfter = this.config.triggerAfterMessages || 3;
|
|
3737
3746
|
if (this.messageCount >= triggerAfter) {
|