@wabbit-dashboard/embed 1.0.9 → 1.0.11
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 +315 -57
- package/dist/wabbit-embed.cjs.js.map +1 -1
- package/dist/wabbit-embed.d.ts +89 -5
- package/dist/wabbit-embed.esm.js +314 -58
- package/dist/wabbit-embed.esm.js.map +1 -1
- package/dist/wabbit-embed.umd.js +315 -57
- 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/README.md
CHANGED
|
@@ -33,12 +33,14 @@ import { Wabbit } from '@wabbit-dashboard/embed';
|
|
|
33
33
|
|
|
34
34
|
### Using CDN
|
|
35
35
|
|
|
36
|
-
**Production:**
|
|
36
|
+
**Production (deployed website):**
|
|
37
37
|
```html
|
|
38
38
|
<script src="https://unpkg.com/@wabbit-dashboard/embed@1/dist/wabbit-embed.umd.min.js"></script>
|
|
39
39
|
<script>
|
|
40
40
|
Wabbit.init({
|
|
41
41
|
apiKey: 'pk_live_xxx',
|
|
42
|
+
apiUrl: 'https://platform.insourcedata.ai',
|
|
43
|
+
wsUrl: 'wss://chat.insourcedata.ai/ws/chat',
|
|
42
44
|
chat: {
|
|
43
45
|
enabled: true,
|
|
44
46
|
collectionId: 'abc123',
|
|
@@ -48,7 +50,9 @@ import { Wabbit } from '@wabbit-dashboard/embed';
|
|
|
48
50
|
</script>
|
|
49
51
|
```
|
|
50
52
|
|
|
51
|
-
**
|
|
53
|
+
> **Note**: The `apiUrl` and `wsUrl` are required when your website runs on localhost but connects to production services. For fully deployed production websites, these can be omitted as the SDK auto-detects the correct URLs.
|
|
54
|
+
|
|
55
|
+
**Local Development (local services):**
|
|
52
56
|
```html
|
|
53
57
|
<script src="http://localhost:3000/sdk/wabbit-embed.umd.js"></script>
|
|
54
58
|
<script>
|
package/dist/wabbit-embed.cjs.js
CHANGED
|
@@ -23,6 +23,10 @@ function validateConfig(config) {
|
|
|
23
23
|
if (!config.chat.collectionId) {
|
|
24
24
|
throw new Error('[Wabbit] collectionId is required when chat is enabled');
|
|
25
25
|
}
|
|
26
|
+
// Validate inline mode requires a container
|
|
27
|
+
if (config.chat.mode === 'inline' && !config.chat.container) {
|
|
28
|
+
throw new Error('[Wabbit] container is required when chat mode is "inline"');
|
|
29
|
+
}
|
|
26
30
|
}
|
|
27
31
|
// Validate forms config if enabled
|
|
28
32
|
if (config.forms?.enabled) {
|
|
@@ -90,11 +94,14 @@ function mergeConfig(config) {
|
|
|
90
94
|
chat: config.chat
|
|
91
95
|
? {
|
|
92
96
|
...config.chat,
|
|
97
|
+
mode: config.chat.mode || 'widget',
|
|
93
98
|
position: config.chat.position || 'bottom-right',
|
|
94
99
|
triggerType: config.chat.triggerType || 'button',
|
|
95
100
|
theme: config.chat.theme || 'auto',
|
|
96
101
|
primaryColor: config.chat.primaryColor || '#6366f1',
|
|
97
|
-
placeholder: config.chat.placeholder || 'Type your message...'
|
|
102
|
+
placeholder: config.chat.placeholder || 'Type your message...',
|
|
103
|
+
showHeader: config.chat.showHeader ?? true,
|
|
104
|
+
headerTitle: config.chat.headerTitle || 'AI Assistant'
|
|
98
105
|
}
|
|
99
106
|
: undefined,
|
|
100
107
|
forms: config.forms
|
|
@@ -157,6 +164,11 @@ class Wabbit {
|
|
|
157
164
|
if (mergedConfig.chat?.enabled && mergedConfig.chat) {
|
|
158
165
|
// Import ChatWidget dynamically to avoid circular dependencies
|
|
159
166
|
Promise.resolve().then(function () { return ChatWidget$1; }).then(({ ChatWidget }) => {
|
|
167
|
+
// Check if instance was destroyed during async import (e.g., React StrictMode)
|
|
168
|
+
if (!Wabbit.instance) {
|
|
169
|
+
console.warn('[Wabbit] Instance was destroyed before chat widget could initialize');
|
|
170
|
+
return;
|
|
171
|
+
}
|
|
160
172
|
const chat = mergedConfig.chat;
|
|
161
173
|
const chatConfig = {
|
|
162
174
|
enabled: chat.enabled,
|
|
@@ -165,13 +177,21 @@ class Wabbit {
|
|
|
165
177
|
apiKey: chat.apiKey || mergedConfig.apiKey,
|
|
166
178
|
apiUrl: chat.apiUrl || mergedConfig.apiUrl,
|
|
167
179
|
wsUrl: mergedConfig.wsUrl || chat.wsUrl, // Use global wsUrl or chat-specific wsUrl
|
|
180
|
+
// Embedding mode
|
|
181
|
+
mode: chat.mode,
|
|
182
|
+
container: chat.container,
|
|
183
|
+
// Widget mode options
|
|
168
184
|
position: chat.position,
|
|
169
185
|
triggerType: chat.triggerType,
|
|
170
186
|
triggerDelay: chat.triggerDelay,
|
|
187
|
+
// Appearance options
|
|
171
188
|
theme: chat.theme,
|
|
172
189
|
primaryColor: chat.primaryColor,
|
|
173
190
|
welcomeMessage: chat.welcomeMessage,
|
|
174
|
-
placeholder: chat.placeholder
|
|
191
|
+
placeholder: chat.placeholder,
|
|
192
|
+
// Header customization
|
|
193
|
+
showHeader: chat.showHeader,
|
|
194
|
+
headerTitle: chat.headerTitle
|
|
175
195
|
};
|
|
176
196
|
const chatWidget = new ChatWidget(chatConfig);
|
|
177
197
|
chatWidget.init();
|
|
@@ -181,6 +201,11 @@ class Wabbit {
|
|
|
181
201
|
if (mergedConfig.forms?.enabled && mergedConfig.forms) {
|
|
182
202
|
// Import FormWidget dynamically to avoid circular dependencies
|
|
183
203
|
Promise.resolve().then(function () { return FormWidget$1; }).then(({ FormWidget }) => {
|
|
204
|
+
// Check if instance was destroyed during async import
|
|
205
|
+
if (!Wabbit.instance) {
|
|
206
|
+
console.warn('[Wabbit] Instance was destroyed before form widget could initialize');
|
|
207
|
+
return;
|
|
208
|
+
}
|
|
184
209
|
const forms = mergedConfig.forms;
|
|
185
210
|
const formConfig = {
|
|
186
211
|
enabled: forms.enabled,
|
|
@@ -205,6 +230,11 @@ class Wabbit {
|
|
|
205
230
|
if (mergedConfig.emailCapture?.enabled) {
|
|
206
231
|
const emailCapture = mergedConfig.emailCapture;
|
|
207
232
|
Promise.resolve().then(function () { return EmailCaptureWidget$1; }).then(({ EmailCaptureWidget }) => {
|
|
233
|
+
// Check if instance was destroyed during async import
|
|
234
|
+
if (!Wabbit.instance) {
|
|
235
|
+
console.warn('[Wabbit] Instance was destroyed before email capture widget could initialize');
|
|
236
|
+
return;
|
|
237
|
+
}
|
|
208
238
|
const emailCaptureConfig = {
|
|
209
239
|
enabled: emailCapture.enabled,
|
|
210
240
|
triggerAfterMessages: emailCapture.triggerAfterMessages,
|
|
@@ -219,7 +249,7 @@ class Wabbit {
|
|
|
219
249
|
// Connect to chat widget after it's initialized
|
|
220
250
|
// Use a small delay to ensure chat widget is ready
|
|
221
251
|
setTimeout(() => {
|
|
222
|
-
if (Wabbit.instance
|
|
252
|
+
if (Wabbit.instance?.chatWidget) {
|
|
223
253
|
const chatWidget = Wabbit.instance.chatWidget;
|
|
224
254
|
// Set WebSocket client
|
|
225
255
|
if (chatWidget.wsClient) {
|
|
@@ -239,7 +269,9 @@ class Wabbit {
|
|
|
239
269
|
});
|
|
240
270
|
}
|
|
241
271
|
// Auto-initialize forms with data-wabbit-form-id (backward compatibility)
|
|
242
|
-
Wabbit.instance
|
|
272
|
+
if (Wabbit.instance) {
|
|
273
|
+
Wabbit.instance.initLegacyForms(config);
|
|
274
|
+
}
|
|
243
275
|
// Call onReady callback if provided
|
|
244
276
|
if (config.onReady) {
|
|
245
277
|
// Wait for DOM to be ready
|
|
@@ -293,6 +325,60 @@ class Wabbit {
|
|
|
293
325
|
Wabbit.instance = null;
|
|
294
326
|
}
|
|
295
327
|
}
|
|
328
|
+
/**
|
|
329
|
+
* Get the URL for a dedicated chat page
|
|
330
|
+
*
|
|
331
|
+
* @param collectionId - The collection ID
|
|
332
|
+
* @param options - Optional parameters
|
|
333
|
+
* @returns Full URL to the chat page
|
|
334
|
+
*
|
|
335
|
+
* @example
|
|
336
|
+
* ```typescript
|
|
337
|
+
* const url = Wabbit.getChatPageUrl('abc123', {
|
|
338
|
+
* initialMessage: 'How do I get started?',
|
|
339
|
+
* theme: 'dark'
|
|
340
|
+
* });
|
|
341
|
+
* // Returns: https://platform.insourcedata.ai/c/abc123?q=How%20do%20I%20get%20started%3F&theme=dark
|
|
342
|
+
* ```
|
|
343
|
+
*/
|
|
344
|
+
static getChatPageUrl(collectionId, options) {
|
|
345
|
+
// Determine base URL
|
|
346
|
+
const baseUrl = options?.baseUrl ||
|
|
347
|
+
(typeof window !== 'undefined' && window.WABBIT_BASE_URL) ||
|
|
348
|
+
(typeof window !== 'undefined' &&
|
|
349
|
+
(window.location.hostname === 'localhost' ||
|
|
350
|
+
window.location.hostname === '127.0.0.1')
|
|
351
|
+
? 'http://localhost:3000'
|
|
352
|
+
: 'https://platform.insourcedata.ai');
|
|
353
|
+
const url = new URL(`/c/${collectionId}`, baseUrl);
|
|
354
|
+
// Add query parameters
|
|
355
|
+
if (options?.initialMessage) {
|
|
356
|
+
url.searchParams.set('q', options.initialMessage);
|
|
357
|
+
}
|
|
358
|
+
if (options?.theme && options.theme !== 'auto') {
|
|
359
|
+
url.searchParams.set('theme', options.theme);
|
|
360
|
+
}
|
|
361
|
+
if (options?.primaryColor) {
|
|
362
|
+
url.searchParams.set('color', options.primaryColor.replace('#', ''));
|
|
363
|
+
}
|
|
364
|
+
return url.toString();
|
|
365
|
+
}
|
|
366
|
+
/**
|
|
367
|
+
* Open chat in a new tab/window
|
|
368
|
+
*
|
|
369
|
+
* @param collectionId - The collection ID
|
|
370
|
+
* @param options - Optional parameters (same as getChatPageUrl)
|
|
371
|
+
*
|
|
372
|
+
* @example
|
|
373
|
+
* ```typescript
|
|
374
|
+
* // Open chat page with initial message
|
|
375
|
+
* Wabbit.openChatPage('abc123', { initialMessage: 'Hello!' });
|
|
376
|
+
* ```
|
|
377
|
+
*/
|
|
378
|
+
static openChatPage(collectionId, options) {
|
|
379
|
+
const url = Wabbit.getChatPageUrl(collectionId, options);
|
|
380
|
+
window.open(url, '_blank', 'noopener,noreferrer');
|
|
381
|
+
}
|
|
296
382
|
/**
|
|
297
383
|
* Get current configuration
|
|
298
384
|
*/
|
|
@@ -812,6 +898,8 @@ class ChatPanel {
|
|
|
812
898
|
this.sendButton = null;
|
|
813
899
|
this.messages = [];
|
|
814
900
|
this.isWaitingForResponse = false;
|
|
901
|
+
this.closeButton = null;
|
|
902
|
+
this.eventCleanup = [];
|
|
815
903
|
this.options = options;
|
|
816
904
|
}
|
|
817
905
|
/**
|
|
@@ -821,36 +909,81 @@ class ChatPanel {
|
|
|
821
909
|
if (this.element) {
|
|
822
910
|
return this.element;
|
|
823
911
|
}
|
|
824
|
-
|
|
912
|
+
const isInline = this.options.mode === 'inline';
|
|
913
|
+
// Main panel - use different class for inline mode
|
|
825
914
|
this.element = createElement('div', {
|
|
826
|
-
class:
|
|
915
|
+
class: isInline
|
|
916
|
+
? 'wabbit-chat-panel wabbit-chat-panel-inline'
|
|
917
|
+
: `wabbit-chat-panel ${this.options.position}`
|
|
827
918
|
});
|
|
828
|
-
// Header
|
|
919
|
+
// Header (conditionally shown based on showHeader option, default: true)
|
|
920
|
+
if (this.options.showHeader !== false) {
|
|
921
|
+
const header = this.createHeader(isInline);
|
|
922
|
+
this.element.appendChild(header);
|
|
923
|
+
}
|
|
924
|
+
// Messages area
|
|
925
|
+
this.messagesContainer = createElement('div', { class: 'wabbit-chat-messages' });
|
|
926
|
+
// Input area
|
|
927
|
+
const inputArea = this.createInputArea();
|
|
928
|
+
// Assemble panel
|
|
929
|
+
this.element.appendChild(this.messagesContainer);
|
|
930
|
+
this.element.appendChild(inputArea);
|
|
931
|
+
// Append to container (inline) or document.body (widget)
|
|
932
|
+
if (isInline && this.options.container) {
|
|
933
|
+
this.options.container.appendChild(this.element);
|
|
934
|
+
// In inline mode, always visible
|
|
935
|
+
this.element.style.display = 'flex';
|
|
936
|
+
}
|
|
937
|
+
else {
|
|
938
|
+
document.body.appendChild(this.element);
|
|
939
|
+
// In widget mode, initially hidden until opened
|
|
940
|
+
this.element.style.display = 'none';
|
|
941
|
+
}
|
|
942
|
+
// Show welcome message if provided
|
|
943
|
+
if (this.options.welcomeMessage) {
|
|
944
|
+
this.addSystemMessage(this.options.welcomeMessage);
|
|
945
|
+
}
|
|
946
|
+
return this.element;
|
|
947
|
+
}
|
|
948
|
+
/**
|
|
949
|
+
* Create the header element
|
|
950
|
+
*/
|
|
951
|
+
createHeader(isInline) {
|
|
829
952
|
const header = createElement('div', { class: 'wabbit-chat-panel-header' });
|
|
830
953
|
const headerTitle = createElement('div', { class: 'wabbit-chat-panel-header-title' });
|
|
831
954
|
const headerIcon = createElement('div', { class: 'wabbit-chat-panel-header-icon' });
|
|
832
955
|
headerIcon.textContent = 'AI';
|
|
833
956
|
const headerText = createElement('div', { class: 'wabbit-chat-panel-header-text' });
|
|
834
957
|
const headerH3 = createElement('h3');
|
|
835
|
-
headerH3.textContent = 'AI Assistant';
|
|
958
|
+
headerH3.textContent = this.options.headerTitle || 'AI Assistant';
|
|
836
959
|
const headerP = createElement('p');
|
|
837
960
|
headerP.textContent = 'Powered by Wabbit';
|
|
838
961
|
headerText.appendChild(headerH3);
|
|
839
962
|
headerText.appendChild(headerP);
|
|
840
963
|
headerTitle.appendChild(headerIcon);
|
|
841
964
|
headerTitle.appendChild(headerText);
|
|
842
|
-
const closeButton = createElement('button', {
|
|
843
|
-
class: 'wabbit-chat-panel-close',
|
|
844
|
-
'aria-label': 'Close chat',
|
|
845
|
-
type: 'button'
|
|
846
|
-
});
|
|
847
|
-
closeButton.innerHTML = '×';
|
|
848
|
-
closeButton.addEventListener('click', this.options.onClose);
|
|
849
965
|
header.appendChild(headerTitle);
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
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() {
|
|
854
987
|
const inputArea = createElement('div', { class: 'wabbit-chat-input-area' });
|
|
855
988
|
const inputWrapper = createElement('div', { class: 'wabbit-chat-input-wrapper' });
|
|
856
989
|
this.inputElement = document.createElement('textarea');
|
|
@@ -858,17 +991,26 @@ class ChatPanel {
|
|
|
858
991
|
this.inputElement.placeholder = this.options.placeholder || 'Type your message...';
|
|
859
992
|
this.inputElement.rows = 1;
|
|
860
993
|
this.inputElement.disabled = this.options.disabled || false;
|
|
861
|
-
// Auto-resize textarea
|
|
862
|
-
|
|
994
|
+
// Auto-resize textarea and update send button state on input
|
|
995
|
+
const inputHandler = () => {
|
|
863
996
|
this.inputElement.style.height = 'auto';
|
|
864
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);
|
|
865
1003
|
});
|
|
866
1004
|
// Send on Enter (Shift+Enter for new line)
|
|
867
|
-
|
|
1005
|
+
const keydownHandler = (e) => {
|
|
868
1006
|
if (e.key === 'Enter' && !e.shiftKey) {
|
|
869
1007
|
e.preventDefault();
|
|
870
1008
|
this.handleSend();
|
|
871
1009
|
}
|
|
1010
|
+
};
|
|
1011
|
+
this.inputElement.addEventListener('keydown', keydownHandler);
|
|
1012
|
+
this.eventCleanup.push(() => {
|
|
1013
|
+
this.inputElement?.removeEventListener('keydown', keydownHandler);
|
|
872
1014
|
});
|
|
873
1015
|
this.sendButton = createElement('button', {
|
|
874
1016
|
class: 'wabbit-chat-send-button',
|
|
@@ -881,23 +1023,16 @@ class ChatPanel {
|
|
|
881
1023
|
<polygon points="22 2 15 22 11 13 2 9 22 2"></polygon>
|
|
882
1024
|
</svg>
|
|
883
1025
|
`;
|
|
884
|
-
|
|
1026
|
+
const sendClickHandler = () => this.handleSend();
|
|
1027
|
+
this.sendButton.addEventListener('click', sendClickHandler);
|
|
1028
|
+
this.eventCleanup.push(() => {
|
|
1029
|
+
this.sendButton?.removeEventListener('click', sendClickHandler);
|
|
1030
|
+
});
|
|
885
1031
|
this.updateSendButtonState();
|
|
886
1032
|
inputWrapper.appendChild(this.inputElement);
|
|
887
1033
|
inputWrapper.appendChild(this.sendButton);
|
|
888
1034
|
inputArea.appendChild(inputWrapper);
|
|
889
|
-
|
|
890
|
-
this.element.appendChild(header);
|
|
891
|
-
this.element.appendChild(this.messagesContainer);
|
|
892
|
-
this.element.appendChild(inputArea);
|
|
893
|
-
document.body.appendChild(this.element);
|
|
894
|
-
// Initially hide the panel (it will be shown when opened)
|
|
895
|
-
this.element.style.display = 'none';
|
|
896
|
-
// Show welcome message if provided
|
|
897
|
-
if (this.options.welcomeMessage) {
|
|
898
|
-
this.addSystemMessage(this.options.welcomeMessage);
|
|
899
|
-
}
|
|
900
|
-
return this.element;
|
|
1035
|
+
return inputArea;
|
|
901
1036
|
}
|
|
902
1037
|
/**
|
|
903
1038
|
* Add a message to the panel
|
|
@@ -1056,15 +1191,19 @@ class ChatPanel {
|
|
|
1056
1191
|
}
|
|
1057
1192
|
}
|
|
1058
1193
|
/**
|
|
1059
|
-
* Remove the panel from DOM
|
|
1194
|
+
* Remove the panel from DOM and cleanup event listeners
|
|
1060
1195
|
*/
|
|
1061
1196
|
destroy() {
|
|
1197
|
+
// Run all event cleanup functions
|
|
1198
|
+
this.eventCleanup.forEach((cleanup) => cleanup());
|
|
1199
|
+
this.eventCleanup = [];
|
|
1062
1200
|
if (this.element) {
|
|
1063
1201
|
this.element.remove();
|
|
1064
1202
|
this.element = null;
|
|
1065
1203
|
this.messagesContainer = null;
|
|
1066
1204
|
this.inputElement = null;
|
|
1067
1205
|
this.sendButton = null;
|
|
1206
|
+
this.closeButton = null;
|
|
1068
1207
|
}
|
|
1069
1208
|
}
|
|
1070
1209
|
}
|
|
@@ -1528,6 +1667,47 @@ function injectChatStyles(primaryColor = '#6366f1', theme) {
|
|
|
1528
1667
|
.wabbit-chat-message {
|
|
1529
1668
|
animation: wabbit-fade-in 0.2s ease-out;
|
|
1530
1669
|
}
|
|
1670
|
+
|
|
1671
|
+
/* ========================================
|
|
1672
|
+
* INLINE MODE STYLES
|
|
1673
|
+
* ======================================== */
|
|
1674
|
+
|
|
1675
|
+
/* Inline Chat Panel - renders inside container instead of fixed position */
|
|
1676
|
+
.wabbit-chat-panel.wabbit-chat-panel-inline {
|
|
1677
|
+
position: relative !important;
|
|
1678
|
+
width: 100%;
|
|
1679
|
+
height: 100%;
|
|
1680
|
+
min-height: 400px;
|
|
1681
|
+
max-height: none;
|
|
1682
|
+
max-width: none;
|
|
1683
|
+
bottom: auto !important;
|
|
1684
|
+
right: auto !important;
|
|
1685
|
+
left: auto !important;
|
|
1686
|
+
border-radius: 12px;
|
|
1687
|
+
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
|
|
1688
|
+
animation: none; /* Disable slide-in animation for inline */
|
|
1689
|
+
}
|
|
1690
|
+
|
|
1691
|
+
/* Inline mode messages area fills available space */
|
|
1692
|
+
.wabbit-chat-panel-inline .wabbit-chat-messages {
|
|
1693
|
+
flex: 1;
|
|
1694
|
+
min-height: 200px;
|
|
1695
|
+
}
|
|
1696
|
+
|
|
1697
|
+
/* Full-height variant for inline mode (used in standalone chat pages) */
|
|
1698
|
+
.wabbit-chat-panel-inline.wabbit-chat-panel-fullheight {
|
|
1699
|
+
height: 100vh;
|
|
1700
|
+
max-height: 100vh;
|
|
1701
|
+
border-radius: 0;
|
|
1702
|
+
}
|
|
1703
|
+
|
|
1704
|
+
/* Mobile responsive for inline mode */
|
|
1705
|
+
@media (max-width: 480px) {
|
|
1706
|
+
.wabbit-chat-panel.wabbit-chat-panel-inline {
|
|
1707
|
+
border-radius: 0;
|
|
1708
|
+
min-height: 300px;
|
|
1709
|
+
}
|
|
1710
|
+
}
|
|
1531
1711
|
`;
|
|
1532
1712
|
const style = document.createElement('style');
|
|
1533
1713
|
style.id = styleId;
|
|
@@ -1573,6 +1753,16 @@ class ChatWidget {
|
|
|
1573
1753
|
resolve();
|
|
1574
1754
|
});
|
|
1575
1755
|
});
|
|
1756
|
+
const isInlineMode = this.config.mode === 'inline';
|
|
1757
|
+
// Resolve container for inline mode
|
|
1758
|
+
let containerElement = null;
|
|
1759
|
+
if (isInlineMode) {
|
|
1760
|
+
containerElement = this.resolveContainer();
|
|
1761
|
+
if (!containerElement) {
|
|
1762
|
+
console.error(`[Wabbit] Container not found: ${this.config.container}`);
|
|
1763
|
+
return;
|
|
1764
|
+
}
|
|
1765
|
+
}
|
|
1576
1766
|
// Inject styles with theme configuration
|
|
1577
1767
|
injectChatStyles(this.config.primaryColor || '#6366f1', this.config.theme);
|
|
1578
1768
|
// Setup theme watcher if theme is 'auto'
|
|
@@ -1583,37 +1773,63 @@ class ChatWidget {
|
|
|
1583
1773
|
);
|
|
1584
1774
|
// Set up event handlers
|
|
1585
1775
|
this.setupWebSocketHandlers();
|
|
1586
|
-
//
|
|
1587
|
-
|
|
1588
|
-
|
|
1589
|
-
|
|
1590
|
-
|
|
1591
|
-
|
|
1776
|
+
// Only create bubble for widget mode (not inline)
|
|
1777
|
+
if (!isInlineMode) {
|
|
1778
|
+
this.bubble = new ChatBubble({
|
|
1779
|
+
position: this.config.position || 'bottom-right',
|
|
1780
|
+
onClick: () => this.toggle()
|
|
1781
|
+
});
|
|
1782
|
+
}
|
|
1783
|
+
// Create panel with mode-specific options
|
|
1592
1784
|
this.panel = new ChatPanel({
|
|
1593
1785
|
position: this.config.position || 'bottom-right',
|
|
1594
1786
|
welcomeMessage: this.config.welcomeMessage,
|
|
1595
1787
|
placeholder: this.config.placeholder,
|
|
1596
1788
|
onSendMessage: (content) => this.handleSendMessage(content),
|
|
1597
1789
|
onClose: () => this.close(),
|
|
1598
|
-
disabled: true
|
|
1790
|
+
disabled: true,
|
|
1791
|
+
mode: this.config.mode || 'widget',
|
|
1792
|
+
container: containerElement || undefined,
|
|
1793
|
+
showHeader: this.config.showHeader,
|
|
1794
|
+
headerTitle: this.config.headerTitle
|
|
1599
1795
|
});
|
|
1600
1796
|
// Render components
|
|
1601
|
-
this.bubble
|
|
1797
|
+
if (this.bubble) {
|
|
1798
|
+
this.bubble.render();
|
|
1799
|
+
}
|
|
1602
1800
|
this.panel.render();
|
|
1603
|
-
//
|
|
1604
|
-
|
|
1605
|
-
|
|
1606
|
-
this.
|
|
1801
|
+
// Set initial state based on mode
|
|
1802
|
+
if (isInlineMode) {
|
|
1803
|
+
// Inline mode: always visible, mark as open
|
|
1804
|
+
this.isOpen = true;
|
|
1607
1805
|
}
|
|
1608
|
-
|
|
1609
|
-
|
|
1806
|
+
else {
|
|
1807
|
+
// Widget mode: panel hidden, bubble visible
|
|
1808
|
+
if (this.panel) {
|
|
1809
|
+
this.panel.hide();
|
|
1810
|
+
}
|
|
1811
|
+
if (this.bubble) {
|
|
1812
|
+
this.bubble.show();
|
|
1813
|
+
}
|
|
1814
|
+
this.isOpen = false;
|
|
1815
|
+
// Handle trigger types (only for widget mode)
|
|
1816
|
+
this.handleTriggerType();
|
|
1610
1817
|
}
|
|
1611
|
-
this.isOpen = false; // Ensure state is consistent
|
|
1612
|
-
// Handle trigger types
|
|
1613
|
-
this.handleTriggerType();
|
|
1614
1818
|
// Connect WebSocket
|
|
1615
1819
|
await this.wsClient.connect();
|
|
1616
1820
|
}
|
|
1821
|
+
/**
|
|
1822
|
+
* Resolve container element for inline mode
|
|
1823
|
+
*/
|
|
1824
|
+
resolveContainer() {
|
|
1825
|
+
if (!this.config.container)
|
|
1826
|
+
return null;
|
|
1827
|
+
// Try as ID first (with or without # prefix), then as CSS selector
|
|
1828
|
+
const selector = this.config.container;
|
|
1829
|
+
const idWithoutHash = selector.startsWith('#') ? selector.slice(1) : selector;
|
|
1830
|
+
return document.getElementById(idWithoutHash) ||
|
|
1831
|
+
document.querySelector(selector);
|
|
1832
|
+
}
|
|
1617
1833
|
/**
|
|
1618
1834
|
* Setup WebSocket event handlers
|
|
1619
1835
|
*/
|
|
@@ -1726,9 +1942,12 @@ class ChatWidget {
|
|
|
1726
1942
|
this.wsClient.sendMessage(content);
|
|
1727
1943
|
}
|
|
1728
1944
|
/**
|
|
1729
|
-
* Open the chat panel
|
|
1945
|
+
* Open the chat panel (no-op in inline mode)
|
|
1730
1946
|
*/
|
|
1731
1947
|
open() {
|
|
1948
|
+
// Inline mode is always open
|
|
1949
|
+
if (this.config.mode === 'inline')
|
|
1950
|
+
return;
|
|
1732
1951
|
if (this.isOpen)
|
|
1733
1952
|
return;
|
|
1734
1953
|
this.isOpen = true;
|
|
@@ -1740,9 +1959,12 @@ class ChatWidget {
|
|
|
1740
1959
|
}
|
|
1741
1960
|
}
|
|
1742
1961
|
/**
|
|
1743
|
-
* Close the chat panel
|
|
1962
|
+
* Close the chat panel (no-op in inline mode)
|
|
1744
1963
|
*/
|
|
1745
1964
|
close() {
|
|
1965
|
+
// Inline mode cannot be closed
|
|
1966
|
+
if (this.config.mode === 'inline')
|
|
1967
|
+
return;
|
|
1746
1968
|
if (!this.isOpen)
|
|
1747
1969
|
return;
|
|
1748
1970
|
this.isOpen = false;
|
|
@@ -1754,9 +1976,12 @@ class ChatWidget {
|
|
|
1754
1976
|
}
|
|
1755
1977
|
}
|
|
1756
1978
|
/**
|
|
1757
|
-
* Toggle chat panel
|
|
1979
|
+
* Toggle chat panel (no-op in inline mode)
|
|
1758
1980
|
*/
|
|
1759
1981
|
toggle() {
|
|
1982
|
+
// Inline mode cannot be toggled
|
|
1983
|
+
if (this.config.mode === 'inline')
|
|
1984
|
+
return;
|
|
1760
1985
|
if (this.isOpen) {
|
|
1761
1986
|
this.close();
|
|
1762
1987
|
}
|
|
@@ -3562,12 +3787,43 @@ function getInstance() {
|
|
|
3562
3787
|
function destroy() {
|
|
3563
3788
|
return Wabbit.destroy();
|
|
3564
3789
|
}
|
|
3790
|
+
/**
|
|
3791
|
+
* Get the URL for a dedicated chat page
|
|
3792
|
+
*
|
|
3793
|
+
* @param collectionId - The collection ID
|
|
3794
|
+
* @param options - Optional parameters
|
|
3795
|
+
* @returns Full URL to the chat page
|
|
3796
|
+
*
|
|
3797
|
+
* @example
|
|
3798
|
+
* ```typescript
|
|
3799
|
+
* const url = Wabbit.getChatPageUrl('abc123', { initialMessage: 'Hello!' });
|
|
3800
|
+
* ```
|
|
3801
|
+
*/
|
|
3802
|
+
function getChatPageUrl(collectionId, options) {
|
|
3803
|
+
return Wabbit.getChatPageUrl(collectionId, options);
|
|
3804
|
+
}
|
|
3805
|
+
/**
|
|
3806
|
+
* Open chat in a new tab/window
|
|
3807
|
+
*
|
|
3808
|
+
* @param collectionId - The collection ID
|
|
3809
|
+
* @param options - Optional parameters
|
|
3810
|
+
*
|
|
3811
|
+
* @example
|
|
3812
|
+
* ```typescript
|
|
3813
|
+
* Wabbit.openChatPage('abc123', { initialMessage: 'Hello!' });
|
|
3814
|
+
* ```
|
|
3815
|
+
*/
|
|
3816
|
+
function openChatPage(collectionId, options) {
|
|
3817
|
+
return Wabbit.openChatPage(collectionId, options);
|
|
3818
|
+
}
|
|
3565
3819
|
// Create an object as default export (for ESM/CJS: import Wabbit from '@wabbit-dashboard/embed')
|
|
3566
3820
|
// Note: For UMD, we don't use default export, but directly use named exports
|
|
3567
3821
|
const WabbitSDK = {
|
|
3568
3822
|
init,
|
|
3569
3823
|
getInstance,
|
|
3570
|
-
destroy
|
|
3824
|
+
destroy,
|
|
3825
|
+
getChatPageUrl,
|
|
3826
|
+
openChatPage
|
|
3571
3827
|
};
|
|
3572
3828
|
|
|
3573
3829
|
exports.ApiClient = ApiClient;
|
|
@@ -3588,10 +3844,12 @@ exports.default = WabbitSDK;
|
|
|
3588
3844
|
exports.destroy = destroy;
|
|
3589
3845
|
exports.detectTheme = detectTheme;
|
|
3590
3846
|
exports.escapeHtml = escapeHtml;
|
|
3847
|
+
exports.getChatPageUrl = getChatPageUrl;
|
|
3591
3848
|
exports.getInstance = getInstance;
|
|
3592
3849
|
exports.init = init;
|
|
3593
3850
|
exports.mergeConfig = mergeConfig;
|
|
3594
3851
|
exports.onDOMReady = onDOMReady;
|
|
3852
|
+
exports.openChatPage = openChatPage;
|
|
3595
3853
|
exports.storage = storage;
|
|
3596
3854
|
exports.validateConfig = validateConfig;
|
|
3597
3855
|
exports.watchTheme = watchTheme;
|