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