@wabbit-dashboard/embed 1.0.10 → 1.0.12
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 +6 -2
- package/dist/wabbit-embed.cjs.js +342 -59
- package/dist/wabbit-embed.cjs.js.map +1 -1
- package/dist/wabbit-embed.d.ts +93 -5
- package/dist/wabbit-embed.esm.js +341 -60
- package/dist/wabbit-embed.esm.js.map +1 -1
- package/dist/wabbit-embed.umd.js +342 -59
- 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 +2 -2
package/dist/wabbit-embed.esm.js
CHANGED
|
@@ -19,6 +19,10 @@ function validateConfig(config) {
|
|
|
19
19
|
if (!config.chat.collectionId) {
|
|
20
20
|
throw new Error('[Wabbit] collectionId is required when chat is enabled');
|
|
21
21
|
}
|
|
22
|
+
// Validate inline mode requires a container
|
|
23
|
+
if (config.chat.mode === 'inline' && !config.chat.container) {
|
|
24
|
+
throw new Error('[Wabbit] container is required when chat mode is "inline"');
|
|
25
|
+
}
|
|
22
26
|
}
|
|
23
27
|
// Validate forms config if enabled
|
|
24
28
|
if (config.forms?.enabled) {
|
|
@@ -86,11 +90,14 @@ function mergeConfig(config) {
|
|
|
86
90
|
chat: config.chat
|
|
87
91
|
? {
|
|
88
92
|
...config.chat,
|
|
93
|
+
mode: config.chat.mode || 'widget',
|
|
89
94
|
position: config.chat.position || 'bottom-right',
|
|
90
95
|
triggerType: config.chat.triggerType || 'button',
|
|
91
96
|
theme: config.chat.theme || 'auto',
|
|
92
97
|
primaryColor: config.chat.primaryColor || '#6366f1',
|
|
93
|
-
placeholder: config.chat.placeholder || 'Type your message...'
|
|
98
|
+
placeholder: config.chat.placeholder || 'Type your message...',
|
|
99
|
+
showHeader: config.chat.showHeader ?? true,
|
|
100
|
+
headerTitle: config.chat.headerTitle || 'AI Assistant'
|
|
94
101
|
}
|
|
95
102
|
: undefined,
|
|
96
103
|
forms: config.forms
|
|
@@ -153,6 +160,11 @@ class Wabbit {
|
|
|
153
160
|
if (mergedConfig.chat?.enabled && mergedConfig.chat) {
|
|
154
161
|
// Import ChatWidget dynamically to avoid circular dependencies
|
|
155
162
|
Promise.resolve().then(function () { return ChatWidget$1; }).then(({ ChatWidget }) => {
|
|
163
|
+
// Check if instance was destroyed during async import (e.g., React StrictMode)
|
|
164
|
+
if (!Wabbit.instance) {
|
|
165
|
+
console.warn('[Wabbit] Instance was destroyed before chat widget could initialize');
|
|
166
|
+
return;
|
|
167
|
+
}
|
|
156
168
|
const chat = mergedConfig.chat;
|
|
157
169
|
const chatConfig = {
|
|
158
170
|
enabled: chat.enabled,
|
|
@@ -161,13 +173,23 @@ class Wabbit {
|
|
|
161
173
|
apiKey: chat.apiKey || mergedConfig.apiKey,
|
|
162
174
|
apiUrl: chat.apiUrl || mergedConfig.apiUrl,
|
|
163
175
|
wsUrl: mergedConfig.wsUrl || chat.wsUrl, // Use global wsUrl or chat-specific wsUrl
|
|
176
|
+
// Embedding mode
|
|
177
|
+
mode: chat.mode,
|
|
178
|
+
container: chat.container,
|
|
179
|
+
// Widget mode options
|
|
164
180
|
position: chat.position,
|
|
165
181
|
triggerType: chat.triggerType,
|
|
166
182
|
triggerDelay: chat.triggerDelay,
|
|
183
|
+
// Appearance options
|
|
167
184
|
theme: chat.theme,
|
|
168
185
|
primaryColor: chat.primaryColor,
|
|
169
186
|
welcomeMessage: chat.welcomeMessage,
|
|
170
|
-
placeholder: chat.placeholder
|
|
187
|
+
placeholder: chat.placeholder,
|
|
188
|
+
// Header customization
|
|
189
|
+
showHeader: chat.showHeader,
|
|
190
|
+
headerTitle: chat.headerTitle,
|
|
191
|
+
// Callbacks - prefer chat-specific, fallback to global
|
|
192
|
+
onChatReady: chat.onChatReady || config.onChatReady
|
|
171
193
|
};
|
|
172
194
|
const chatWidget = new ChatWidget(chatConfig);
|
|
173
195
|
chatWidget.init();
|
|
@@ -177,6 +199,11 @@ class Wabbit {
|
|
|
177
199
|
if (mergedConfig.forms?.enabled && mergedConfig.forms) {
|
|
178
200
|
// Import FormWidget dynamically to avoid circular dependencies
|
|
179
201
|
Promise.resolve().then(function () { return FormWidget$1; }).then(({ FormWidget }) => {
|
|
202
|
+
// Check if instance was destroyed during async import
|
|
203
|
+
if (!Wabbit.instance) {
|
|
204
|
+
console.warn('[Wabbit] Instance was destroyed before form widget could initialize');
|
|
205
|
+
return;
|
|
206
|
+
}
|
|
180
207
|
const forms = mergedConfig.forms;
|
|
181
208
|
const formConfig = {
|
|
182
209
|
enabled: forms.enabled,
|
|
@@ -201,6 +228,11 @@ class Wabbit {
|
|
|
201
228
|
if (mergedConfig.emailCapture?.enabled) {
|
|
202
229
|
const emailCapture = mergedConfig.emailCapture;
|
|
203
230
|
Promise.resolve().then(function () { return EmailCaptureWidget$1; }).then(({ EmailCaptureWidget }) => {
|
|
231
|
+
// Check if instance was destroyed during async import
|
|
232
|
+
if (!Wabbit.instance) {
|
|
233
|
+
console.warn('[Wabbit] Instance was destroyed before email capture widget could initialize');
|
|
234
|
+
return;
|
|
235
|
+
}
|
|
204
236
|
const emailCaptureConfig = {
|
|
205
237
|
enabled: emailCapture.enabled,
|
|
206
238
|
triggerAfterMessages: emailCapture.triggerAfterMessages,
|
|
@@ -215,7 +247,7 @@ class Wabbit {
|
|
|
215
247
|
// Connect to chat widget after it's initialized
|
|
216
248
|
// Use a small delay to ensure chat widget is ready
|
|
217
249
|
setTimeout(() => {
|
|
218
|
-
if (Wabbit.instance
|
|
250
|
+
if (Wabbit.instance?.chatWidget) {
|
|
219
251
|
const chatWidget = Wabbit.instance.chatWidget;
|
|
220
252
|
// Set WebSocket client
|
|
221
253
|
if (chatWidget.wsClient) {
|
|
@@ -235,7 +267,9 @@ class Wabbit {
|
|
|
235
267
|
});
|
|
236
268
|
}
|
|
237
269
|
// Auto-initialize forms with data-wabbit-form-id (backward compatibility)
|
|
238
|
-
Wabbit.instance
|
|
270
|
+
if (Wabbit.instance) {
|
|
271
|
+
Wabbit.instance.initLegacyForms(config);
|
|
272
|
+
}
|
|
239
273
|
// Call onReady callback if provided
|
|
240
274
|
if (config.onReady) {
|
|
241
275
|
// Wait for DOM to be ready
|
|
@@ -289,6 +323,60 @@ class Wabbit {
|
|
|
289
323
|
Wabbit.instance = null;
|
|
290
324
|
}
|
|
291
325
|
}
|
|
326
|
+
/**
|
|
327
|
+
* Get the URL for a dedicated chat page
|
|
328
|
+
*
|
|
329
|
+
* @param collectionId - The collection ID
|
|
330
|
+
* @param options - Optional parameters
|
|
331
|
+
* @returns Full URL to the chat page
|
|
332
|
+
*
|
|
333
|
+
* @example
|
|
334
|
+
* ```typescript
|
|
335
|
+
* const url = Wabbit.getChatPageUrl('abc123', {
|
|
336
|
+
* initialMessage: 'How do I get started?',
|
|
337
|
+
* theme: 'dark'
|
|
338
|
+
* });
|
|
339
|
+
* // Returns: https://platform.insourcedata.ai/c/abc123?q=How%20do%20I%20get%20started%3F&theme=dark
|
|
340
|
+
* ```
|
|
341
|
+
*/
|
|
342
|
+
static getChatPageUrl(collectionId, options) {
|
|
343
|
+
// Determine base URL
|
|
344
|
+
const baseUrl = options?.baseUrl ||
|
|
345
|
+
(typeof window !== 'undefined' && window.WABBIT_BASE_URL) ||
|
|
346
|
+
(typeof window !== 'undefined' &&
|
|
347
|
+
(window.location.hostname === 'localhost' ||
|
|
348
|
+
window.location.hostname === '127.0.0.1')
|
|
349
|
+
? 'http://localhost:3000'
|
|
350
|
+
: 'https://platform.insourcedata.ai');
|
|
351
|
+
const url = new URL(`/c/${collectionId}`, baseUrl);
|
|
352
|
+
// Add query parameters
|
|
353
|
+
if (options?.initialMessage) {
|
|
354
|
+
url.searchParams.set('q', options.initialMessage);
|
|
355
|
+
}
|
|
356
|
+
if (options?.theme && options.theme !== 'auto') {
|
|
357
|
+
url.searchParams.set('theme', options.theme);
|
|
358
|
+
}
|
|
359
|
+
if (options?.primaryColor) {
|
|
360
|
+
url.searchParams.set('color', options.primaryColor.replace('#', ''));
|
|
361
|
+
}
|
|
362
|
+
return url.toString();
|
|
363
|
+
}
|
|
364
|
+
/**
|
|
365
|
+
* Open chat in a new tab/window
|
|
366
|
+
*
|
|
367
|
+
* @param collectionId - The collection ID
|
|
368
|
+
* @param options - Optional parameters (same as getChatPageUrl)
|
|
369
|
+
*
|
|
370
|
+
* @example
|
|
371
|
+
* ```typescript
|
|
372
|
+
* // Open chat page with initial message
|
|
373
|
+
* Wabbit.openChatPage('abc123', { initialMessage: 'Hello!' });
|
|
374
|
+
* ```
|
|
375
|
+
*/
|
|
376
|
+
static openChatPage(collectionId, options) {
|
|
377
|
+
const url = Wabbit.getChatPageUrl(collectionId, options);
|
|
378
|
+
window.open(url, '_blank', 'noopener,noreferrer');
|
|
379
|
+
}
|
|
292
380
|
/**
|
|
293
381
|
* Get current configuration
|
|
294
382
|
*/
|
|
@@ -808,6 +896,8 @@ class ChatPanel {
|
|
|
808
896
|
this.sendButton = null;
|
|
809
897
|
this.messages = [];
|
|
810
898
|
this.isWaitingForResponse = false;
|
|
899
|
+
this.closeButton = null;
|
|
900
|
+
this.eventCleanup = [];
|
|
811
901
|
this.options = options;
|
|
812
902
|
}
|
|
813
903
|
/**
|
|
@@ -817,36 +907,83 @@ class ChatPanel {
|
|
|
817
907
|
if (this.element) {
|
|
818
908
|
return this.element;
|
|
819
909
|
}
|
|
820
|
-
|
|
910
|
+
const isInline = this.options.mode === 'inline';
|
|
911
|
+
// Main panel - use different class for inline mode
|
|
821
912
|
this.element = createElement('div', {
|
|
822
|
-
class:
|
|
913
|
+
class: isInline
|
|
914
|
+
? 'wabbit-chat-panel wabbit-chat-panel-inline'
|
|
915
|
+
: `wabbit-chat-panel ${this.options.position}`
|
|
823
916
|
});
|
|
824
|
-
// Header
|
|
917
|
+
// Header (conditionally shown based on showHeader option, default: true)
|
|
918
|
+
if (this.options.showHeader !== false) {
|
|
919
|
+
const header = this.createHeader(isInline);
|
|
920
|
+
this.element.appendChild(header);
|
|
921
|
+
}
|
|
922
|
+
// Messages area
|
|
923
|
+
this.messagesContainer = createElement('div', { class: 'wabbit-chat-messages' });
|
|
924
|
+
// Input area
|
|
925
|
+
const inputArea = this.createInputArea();
|
|
926
|
+
// Assemble panel
|
|
927
|
+
this.element.appendChild(this.messagesContainer);
|
|
928
|
+
this.element.appendChild(inputArea);
|
|
929
|
+
// Append to container (inline) or document.body (widget)
|
|
930
|
+
if (isInline && this.options.container) {
|
|
931
|
+
this.options.container.appendChild(this.element);
|
|
932
|
+
// In inline mode, always visible
|
|
933
|
+
this.element.style.display = 'flex';
|
|
934
|
+
}
|
|
935
|
+
else {
|
|
936
|
+
document.body.appendChild(this.element);
|
|
937
|
+
// In widget mode, initially hidden until opened
|
|
938
|
+
this.element.style.display = 'none';
|
|
939
|
+
}
|
|
940
|
+
// Show welcome message if provided
|
|
941
|
+
if (this.options.welcomeMessage) {
|
|
942
|
+
this.addSystemMessage(this.options.welcomeMessage);
|
|
943
|
+
}
|
|
944
|
+
// Ensure scroll to bottom after DOM settles (for initial load and history)
|
|
945
|
+
setTimeout(() => this.scrollToBottom(), 100);
|
|
946
|
+
return this.element;
|
|
947
|
+
}
|
|
948
|
+
/**
|
|
949
|
+
* Create the header element
|
|
950
|
+
*/
|
|
951
|
+
createHeader(isInline) {
|
|
825
952
|
const header = createElement('div', { class: 'wabbit-chat-panel-header' });
|
|
826
953
|
const headerTitle = createElement('div', { class: 'wabbit-chat-panel-header-title' });
|
|
827
954
|
const headerIcon = createElement('div', { class: 'wabbit-chat-panel-header-icon' });
|
|
828
955
|
headerIcon.textContent = 'AI';
|
|
829
956
|
const headerText = createElement('div', { class: 'wabbit-chat-panel-header-text' });
|
|
830
957
|
const headerH3 = createElement('h3');
|
|
831
|
-
headerH3.textContent = 'AI Assistant';
|
|
958
|
+
headerH3.textContent = this.options.headerTitle || 'AI Assistant';
|
|
832
959
|
const headerP = createElement('p');
|
|
833
960
|
headerP.textContent = 'Powered by Wabbit';
|
|
834
961
|
headerText.appendChild(headerH3);
|
|
835
962
|
headerText.appendChild(headerP);
|
|
836
963
|
headerTitle.appendChild(headerIcon);
|
|
837
964
|
headerTitle.appendChild(headerText);
|
|
838
|
-
const closeButton = createElement('button', {
|
|
839
|
-
class: 'wabbit-chat-panel-close',
|
|
840
|
-
'aria-label': 'Close chat',
|
|
841
|
-
type: 'button'
|
|
842
|
-
});
|
|
843
|
-
closeButton.innerHTML = '×';
|
|
844
|
-
closeButton.addEventListener('click', this.options.onClose);
|
|
845
965
|
header.appendChild(headerTitle);
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
966
|
+
// Only add close button for widget mode (not inline)
|
|
967
|
+
if (!isInline) {
|
|
968
|
+
this.closeButton = createElement('button', {
|
|
969
|
+
class: 'wabbit-chat-panel-close',
|
|
970
|
+
'aria-label': 'Close chat',
|
|
971
|
+
type: 'button'
|
|
972
|
+
});
|
|
973
|
+
this.closeButton.innerHTML = '×';
|
|
974
|
+
const closeHandler = this.options.onClose;
|
|
975
|
+
this.closeButton.addEventListener('click', closeHandler);
|
|
976
|
+
this.eventCleanup.push(() => {
|
|
977
|
+
this.closeButton?.removeEventListener('click', closeHandler);
|
|
978
|
+
});
|
|
979
|
+
header.appendChild(this.closeButton);
|
|
980
|
+
}
|
|
981
|
+
return header;
|
|
982
|
+
}
|
|
983
|
+
/**
|
|
984
|
+
* Create the input area element
|
|
985
|
+
*/
|
|
986
|
+
createInputArea() {
|
|
850
987
|
const inputArea = createElement('div', { class: 'wabbit-chat-input-area' });
|
|
851
988
|
const inputWrapper = createElement('div', { class: 'wabbit-chat-input-wrapper' });
|
|
852
989
|
this.inputElement = document.createElement('textarea');
|
|
@@ -854,17 +991,26 @@ class ChatPanel {
|
|
|
854
991
|
this.inputElement.placeholder = this.options.placeholder || 'Type your message...';
|
|
855
992
|
this.inputElement.rows = 1;
|
|
856
993
|
this.inputElement.disabled = this.options.disabled || false;
|
|
857
|
-
// Auto-resize textarea
|
|
858
|
-
|
|
994
|
+
// Auto-resize textarea and update send button state on input
|
|
995
|
+
const inputHandler = () => {
|
|
859
996
|
this.inputElement.style.height = 'auto';
|
|
860
997
|
this.inputElement.style.height = `${Math.min(this.inputElement.scrollHeight, 120)}px`;
|
|
998
|
+
this.updateSendButtonState();
|
|
999
|
+
};
|
|
1000
|
+
this.inputElement.addEventListener('input', inputHandler);
|
|
1001
|
+
this.eventCleanup.push(() => {
|
|
1002
|
+
this.inputElement?.removeEventListener('input', inputHandler);
|
|
861
1003
|
});
|
|
862
1004
|
// Send on Enter (Shift+Enter for new line)
|
|
863
|
-
|
|
1005
|
+
const keydownHandler = (e) => {
|
|
864
1006
|
if (e.key === 'Enter' && !e.shiftKey) {
|
|
865
1007
|
e.preventDefault();
|
|
866
1008
|
this.handleSend();
|
|
867
1009
|
}
|
|
1010
|
+
};
|
|
1011
|
+
this.inputElement.addEventListener('keydown', keydownHandler);
|
|
1012
|
+
this.eventCleanup.push(() => {
|
|
1013
|
+
this.inputElement?.removeEventListener('keydown', keydownHandler);
|
|
868
1014
|
});
|
|
869
1015
|
this.sendButton = createElement('button', {
|
|
870
1016
|
class: 'wabbit-chat-send-button',
|
|
@@ -877,23 +1023,16 @@ class ChatPanel {
|
|
|
877
1023
|
<polygon points="22 2 15 22 11 13 2 9 22 2"></polygon>
|
|
878
1024
|
</svg>
|
|
879
1025
|
`;
|
|
880
|
-
|
|
1026
|
+
const sendClickHandler = () => this.handleSend();
|
|
1027
|
+
this.sendButton.addEventListener('click', sendClickHandler);
|
|
1028
|
+
this.eventCleanup.push(() => {
|
|
1029
|
+
this.sendButton?.removeEventListener('click', sendClickHandler);
|
|
1030
|
+
});
|
|
881
1031
|
this.updateSendButtonState();
|
|
882
1032
|
inputWrapper.appendChild(this.inputElement);
|
|
883
1033
|
inputWrapper.appendChild(this.sendButton);
|
|
884
1034
|
inputArea.appendChild(inputWrapper);
|
|
885
|
-
|
|
886
|
-
this.element.appendChild(header);
|
|
887
|
-
this.element.appendChild(this.messagesContainer);
|
|
888
|
-
this.element.appendChild(inputArea);
|
|
889
|
-
document.body.appendChild(this.element);
|
|
890
|
-
// Initially hide the panel (it will be shown when opened)
|
|
891
|
-
this.element.style.display = 'none';
|
|
892
|
-
// Show welcome message if provided
|
|
893
|
-
if (this.options.welcomeMessage) {
|
|
894
|
-
this.addSystemMessage(this.options.welcomeMessage);
|
|
895
|
-
}
|
|
896
|
-
return this.element;
|
|
1035
|
+
return inputArea;
|
|
897
1036
|
}
|
|
898
1037
|
/**
|
|
899
1038
|
* Add a message to the panel
|
|
@@ -1016,7 +1155,12 @@ class ChatPanel {
|
|
|
1016
1155
|
return;
|
|
1017
1156
|
const hasText = this.inputElement.value.trim().length > 0;
|
|
1018
1157
|
const disabled = this.options.disabled || this.isWaitingForResponse || !hasText;
|
|
1019
|
-
|
|
1158
|
+
if (disabled) {
|
|
1159
|
+
this.sendButton.setAttribute('disabled', 'true');
|
|
1160
|
+
}
|
|
1161
|
+
else {
|
|
1162
|
+
this.sendButton.removeAttribute('disabled');
|
|
1163
|
+
}
|
|
1020
1164
|
if (this.sendButton instanceof HTMLButtonElement) {
|
|
1021
1165
|
this.sendButton.disabled = disabled;
|
|
1022
1166
|
}
|
|
@@ -1035,8 +1179,9 @@ class ChatPanel {
|
|
|
1035
1179
|
show() {
|
|
1036
1180
|
if (this.element) {
|
|
1037
1181
|
this.element.style.display = 'flex';
|
|
1038
|
-
// Focus input after a brief delay
|
|
1182
|
+
// Focus input and scroll to bottom after a brief delay
|
|
1039
1183
|
setTimeout(() => {
|
|
1184
|
+
this.scrollToBottom();
|
|
1040
1185
|
if (this.inputElement) {
|
|
1041
1186
|
this.inputElement.focus();
|
|
1042
1187
|
}
|
|
@@ -1052,15 +1197,19 @@ class ChatPanel {
|
|
|
1052
1197
|
}
|
|
1053
1198
|
}
|
|
1054
1199
|
/**
|
|
1055
|
-
* Remove the panel from DOM
|
|
1200
|
+
* Remove the panel from DOM and cleanup event listeners
|
|
1056
1201
|
*/
|
|
1057
1202
|
destroy() {
|
|
1203
|
+
// Run all event cleanup functions
|
|
1204
|
+
this.eventCleanup.forEach((cleanup) => cleanup());
|
|
1205
|
+
this.eventCleanup = [];
|
|
1058
1206
|
if (this.element) {
|
|
1059
1207
|
this.element.remove();
|
|
1060
1208
|
this.element = null;
|
|
1061
1209
|
this.messagesContainer = null;
|
|
1062
1210
|
this.inputElement = null;
|
|
1063
1211
|
this.sendButton = null;
|
|
1212
|
+
this.closeButton = null;
|
|
1064
1213
|
}
|
|
1065
1214
|
}
|
|
1066
1215
|
}
|
|
@@ -1524,6 +1673,47 @@ function injectChatStyles(primaryColor = '#6366f1', theme) {
|
|
|
1524
1673
|
.wabbit-chat-message {
|
|
1525
1674
|
animation: wabbit-fade-in 0.2s ease-out;
|
|
1526
1675
|
}
|
|
1676
|
+
|
|
1677
|
+
/* ========================================
|
|
1678
|
+
* INLINE MODE STYLES
|
|
1679
|
+
* ======================================== */
|
|
1680
|
+
|
|
1681
|
+
/* Inline Chat Panel - renders inside container instead of fixed position */
|
|
1682
|
+
.wabbit-chat-panel.wabbit-chat-panel-inline {
|
|
1683
|
+
position: relative !important;
|
|
1684
|
+
width: 100%;
|
|
1685
|
+
height: 100%;
|
|
1686
|
+
min-height: 400px;
|
|
1687
|
+
max-height: none;
|
|
1688
|
+
max-width: none;
|
|
1689
|
+
bottom: auto !important;
|
|
1690
|
+
right: auto !important;
|
|
1691
|
+
left: auto !important;
|
|
1692
|
+
border-radius: 12px;
|
|
1693
|
+
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
|
|
1694
|
+
animation: none; /* Disable slide-in animation for inline */
|
|
1695
|
+
}
|
|
1696
|
+
|
|
1697
|
+
/* Inline mode messages area fills available space */
|
|
1698
|
+
.wabbit-chat-panel-inline .wabbit-chat-messages {
|
|
1699
|
+
flex: 1;
|
|
1700
|
+
min-height: 200px;
|
|
1701
|
+
}
|
|
1702
|
+
|
|
1703
|
+
/* Full-height variant for inline mode (used in standalone chat pages) */
|
|
1704
|
+
.wabbit-chat-panel-inline.wabbit-chat-panel-fullheight {
|
|
1705
|
+
height: 100vh;
|
|
1706
|
+
max-height: 100vh;
|
|
1707
|
+
border-radius: 0;
|
|
1708
|
+
}
|
|
1709
|
+
|
|
1710
|
+
/* Mobile responsive for inline mode */
|
|
1711
|
+
@media (max-width: 480px) {
|
|
1712
|
+
.wabbit-chat-panel.wabbit-chat-panel-inline {
|
|
1713
|
+
border-radius: 0;
|
|
1714
|
+
min-height: 300px;
|
|
1715
|
+
}
|
|
1716
|
+
}
|
|
1527
1717
|
`;
|
|
1528
1718
|
const style = document.createElement('style');
|
|
1529
1719
|
style.id = styleId;
|
|
@@ -1557,7 +1747,10 @@ class ChatWidget {
|
|
|
1557
1747
|
this.isOpen = false;
|
|
1558
1748
|
this.cleanup = [];
|
|
1559
1749
|
this.themeCleanup = null;
|
|
1750
|
+
this.onChatReadyCallback = null;
|
|
1751
|
+
this.chatReadyFired = false;
|
|
1560
1752
|
this.config = config;
|
|
1753
|
+
this.onChatReadyCallback = config.onChatReady || null;
|
|
1561
1754
|
}
|
|
1562
1755
|
/**
|
|
1563
1756
|
* Initialize the chat widget
|
|
@@ -1569,6 +1762,16 @@ class ChatWidget {
|
|
|
1569
1762
|
resolve();
|
|
1570
1763
|
});
|
|
1571
1764
|
});
|
|
1765
|
+
const isInlineMode = this.config.mode === 'inline';
|
|
1766
|
+
// Resolve container for inline mode
|
|
1767
|
+
let containerElement = null;
|
|
1768
|
+
if (isInlineMode) {
|
|
1769
|
+
containerElement = this.resolveContainer();
|
|
1770
|
+
if (!containerElement) {
|
|
1771
|
+
console.error(`[Wabbit] Container not found: ${this.config.container}`);
|
|
1772
|
+
return;
|
|
1773
|
+
}
|
|
1774
|
+
}
|
|
1572
1775
|
// Inject styles with theme configuration
|
|
1573
1776
|
injectChatStyles(this.config.primaryColor || '#6366f1', this.config.theme);
|
|
1574
1777
|
// Setup theme watcher if theme is 'auto'
|
|
@@ -1579,37 +1782,63 @@ class ChatWidget {
|
|
|
1579
1782
|
);
|
|
1580
1783
|
// Set up event handlers
|
|
1581
1784
|
this.setupWebSocketHandlers();
|
|
1582
|
-
//
|
|
1583
|
-
|
|
1584
|
-
|
|
1585
|
-
|
|
1586
|
-
|
|
1587
|
-
|
|
1785
|
+
// Only create bubble for widget mode (not inline)
|
|
1786
|
+
if (!isInlineMode) {
|
|
1787
|
+
this.bubble = new ChatBubble({
|
|
1788
|
+
position: this.config.position || 'bottom-right',
|
|
1789
|
+
onClick: () => this.toggle()
|
|
1790
|
+
});
|
|
1791
|
+
}
|
|
1792
|
+
// Create panel with mode-specific options
|
|
1588
1793
|
this.panel = new ChatPanel({
|
|
1589
1794
|
position: this.config.position || 'bottom-right',
|
|
1590
1795
|
welcomeMessage: this.config.welcomeMessage,
|
|
1591
1796
|
placeholder: this.config.placeholder,
|
|
1592
1797
|
onSendMessage: (content) => this.handleSendMessage(content),
|
|
1593
1798
|
onClose: () => this.close(),
|
|
1594
|
-
disabled: true
|
|
1799
|
+
disabled: true,
|
|
1800
|
+
mode: this.config.mode || 'widget',
|
|
1801
|
+
container: containerElement || undefined,
|
|
1802
|
+
showHeader: this.config.showHeader,
|
|
1803
|
+
headerTitle: this.config.headerTitle
|
|
1595
1804
|
});
|
|
1596
1805
|
// Render components
|
|
1597
|
-
this.bubble
|
|
1806
|
+
if (this.bubble) {
|
|
1807
|
+
this.bubble.render();
|
|
1808
|
+
}
|
|
1598
1809
|
this.panel.render();
|
|
1599
|
-
//
|
|
1600
|
-
|
|
1601
|
-
|
|
1602
|
-
this.
|
|
1810
|
+
// Set initial state based on mode
|
|
1811
|
+
if (isInlineMode) {
|
|
1812
|
+
// Inline mode: always visible, mark as open
|
|
1813
|
+
this.isOpen = true;
|
|
1603
1814
|
}
|
|
1604
|
-
|
|
1605
|
-
|
|
1815
|
+
else {
|
|
1816
|
+
// Widget mode: panel hidden, bubble visible
|
|
1817
|
+
if (this.panel) {
|
|
1818
|
+
this.panel.hide();
|
|
1819
|
+
}
|
|
1820
|
+
if (this.bubble) {
|
|
1821
|
+
this.bubble.show();
|
|
1822
|
+
}
|
|
1823
|
+
this.isOpen = false;
|
|
1824
|
+
// Handle trigger types (only for widget mode)
|
|
1825
|
+
this.handleTriggerType();
|
|
1606
1826
|
}
|
|
1607
|
-
this.isOpen = false; // Ensure state is consistent
|
|
1608
|
-
// Handle trigger types
|
|
1609
|
-
this.handleTriggerType();
|
|
1610
1827
|
// Connect WebSocket
|
|
1611
1828
|
await this.wsClient.connect();
|
|
1612
1829
|
}
|
|
1830
|
+
/**
|
|
1831
|
+
* Resolve container element for inline mode
|
|
1832
|
+
*/
|
|
1833
|
+
resolveContainer() {
|
|
1834
|
+
if (!this.config.container)
|
|
1835
|
+
return null;
|
|
1836
|
+
// Try as ID first (with or without # prefix), then as CSS selector
|
|
1837
|
+
const selector = this.config.container;
|
|
1838
|
+
const idWithoutHash = selector.startsWith('#') ? selector.slice(1) : selector;
|
|
1839
|
+
return document.getElementById(idWithoutHash) ||
|
|
1840
|
+
document.querySelector(selector);
|
|
1841
|
+
}
|
|
1613
1842
|
/**
|
|
1614
1843
|
* Setup WebSocket event handlers
|
|
1615
1844
|
*/
|
|
@@ -1624,6 +1853,18 @@ class ChatWidget {
|
|
|
1624
1853
|
this.panel.addSystemMessage(message);
|
|
1625
1854
|
}
|
|
1626
1855
|
}
|
|
1856
|
+
// Fire onChatReady callback once when chat is ready
|
|
1857
|
+
// Use requestAnimationFrame to ensure the panel DOM is fully painted
|
|
1858
|
+
// before the callback runs (important for sending initial messages)
|
|
1859
|
+
if (!this.chatReadyFired && this.onChatReadyCallback) {
|
|
1860
|
+
this.chatReadyFired = true;
|
|
1861
|
+
requestAnimationFrame(() => {
|
|
1862
|
+
console.log('[Wabbit] Chat ready, firing onChatReady callback');
|
|
1863
|
+
if (this.onChatReadyCallback) {
|
|
1864
|
+
this.onChatReadyCallback();
|
|
1865
|
+
}
|
|
1866
|
+
});
|
|
1867
|
+
}
|
|
1627
1868
|
};
|
|
1628
1869
|
this.wsClient.onMessageHistory = (messages) => {
|
|
1629
1870
|
console.log('[Wabbit] Loaded message history:', messages.length);
|
|
@@ -1722,9 +1963,12 @@ class ChatWidget {
|
|
|
1722
1963
|
this.wsClient.sendMessage(content);
|
|
1723
1964
|
}
|
|
1724
1965
|
/**
|
|
1725
|
-
* Open the chat panel
|
|
1966
|
+
* Open the chat panel (no-op in inline mode)
|
|
1726
1967
|
*/
|
|
1727
1968
|
open() {
|
|
1969
|
+
// Inline mode is always open
|
|
1970
|
+
if (this.config.mode === 'inline')
|
|
1971
|
+
return;
|
|
1728
1972
|
if (this.isOpen)
|
|
1729
1973
|
return;
|
|
1730
1974
|
this.isOpen = true;
|
|
@@ -1736,9 +1980,12 @@ class ChatWidget {
|
|
|
1736
1980
|
}
|
|
1737
1981
|
}
|
|
1738
1982
|
/**
|
|
1739
|
-
* Close the chat panel
|
|
1983
|
+
* Close the chat panel (no-op in inline mode)
|
|
1740
1984
|
*/
|
|
1741
1985
|
close() {
|
|
1986
|
+
// Inline mode cannot be closed
|
|
1987
|
+
if (this.config.mode === 'inline')
|
|
1988
|
+
return;
|
|
1742
1989
|
if (!this.isOpen)
|
|
1743
1990
|
return;
|
|
1744
1991
|
this.isOpen = false;
|
|
@@ -1750,9 +1997,12 @@ class ChatWidget {
|
|
|
1750
1997
|
}
|
|
1751
1998
|
}
|
|
1752
1999
|
/**
|
|
1753
|
-
* Toggle chat panel
|
|
2000
|
+
* Toggle chat panel (no-op in inline mode)
|
|
1754
2001
|
*/
|
|
1755
2002
|
toggle() {
|
|
2003
|
+
// Inline mode cannot be toggled
|
|
2004
|
+
if (this.config.mode === 'inline')
|
|
2005
|
+
return;
|
|
1756
2006
|
if (this.isOpen) {
|
|
1757
2007
|
this.close();
|
|
1758
2008
|
}
|
|
@@ -3558,13 +3808,44 @@ function getInstance() {
|
|
|
3558
3808
|
function destroy() {
|
|
3559
3809
|
return Wabbit.destroy();
|
|
3560
3810
|
}
|
|
3811
|
+
/**
|
|
3812
|
+
* Get the URL for a dedicated chat page
|
|
3813
|
+
*
|
|
3814
|
+
* @param collectionId - The collection ID
|
|
3815
|
+
* @param options - Optional parameters
|
|
3816
|
+
* @returns Full URL to the chat page
|
|
3817
|
+
*
|
|
3818
|
+
* @example
|
|
3819
|
+
* ```typescript
|
|
3820
|
+
* const url = Wabbit.getChatPageUrl('abc123', { initialMessage: 'Hello!' });
|
|
3821
|
+
* ```
|
|
3822
|
+
*/
|
|
3823
|
+
function getChatPageUrl(collectionId, options) {
|
|
3824
|
+
return Wabbit.getChatPageUrl(collectionId, options);
|
|
3825
|
+
}
|
|
3826
|
+
/**
|
|
3827
|
+
* Open chat in a new tab/window
|
|
3828
|
+
*
|
|
3829
|
+
* @param collectionId - The collection ID
|
|
3830
|
+
* @param options - Optional parameters
|
|
3831
|
+
*
|
|
3832
|
+
* @example
|
|
3833
|
+
* ```typescript
|
|
3834
|
+
* Wabbit.openChatPage('abc123', { initialMessage: 'Hello!' });
|
|
3835
|
+
* ```
|
|
3836
|
+
*/
|
|
3837
|
+
function openChatPage(collectionId, options) {
|
|
3838
|
+
return Wabbit.openChatPage(collectionId, options);
|
|
3839
|
+
}
|
|
3561
3840
|
// Create an object as default export (for ESM/CJS: import Wabbit from '@wabbit-dashboard/embed')
|
|
3562
3841
|
// Note: For UMD, we don't use default export, but directly use named exports
|
|
3563
3842
|
const WabbitSDK = {
|
|
3564
3843
|
init,
|
|
3565
3844
|
getInstance,
|
|
3566
|
-
destroy
|
|
3845
|
+
destroy,
|
|
3846
|
+
getChatPageUrl,
|
|
3847
|
+
openChatPage
|
|
3567
3848
|
};
|
|
3568
3849
|
|
|
3569
|
-
export { ApiClient, ChatBubble, ChatPanel, ChatWebSocketClient, ChatWidget, EmailCaptureModal, EmailCaptureWidget, EventEmitter, FormRenderer, FormStyles, FormWidget, SafeStorage, Wabbit, createElement, WabbitSDK as default, destroy, detectTheme, escapeHtml, getInstance, init, mergeConfig, onDOMReady, storage, validateConfig, watchTheme };
|
|
3850
|
+
export { ApiClient, ChatBubble, ChatPanel, ChatWebSocketClient, ChatWidget, EmailCaptureModal, EmailCaptureWidget, EventEmitter, FormRenderer, FormStyles, FormWidget, SafeStorage, Wabbit, createElement, WabbitSDK as default, destroy, detectTheme, escapeHtml, getChatPageUrl, getInstance, init, mergeConfig, onDOMReady, openChatPage, storage, validateConfig, watchTheme };
|
|
3570
3851
|
//# sourceMappingURL=wabbit-embed.esm.js.map
|