@wabbit-dashboard/embed 1.1.0 → 1.1.2
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 +30 -0
- package/dist/wabbit-embed.cjs.js +232 -115
- package/dist/wabbit-embed.cjs.js.map +1 -1
- package/dist/wabbit-embed.d.ts +49 -42
- package/dist/wabbit-embed.esm.js +232 -115
- package/dist/wabbit-embed.esm.js.map +1 -1
- package/dist/wabbit-embed.umd.js +232 -115
- 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
|
@@ -116,6 +116,143 @@ function mergeConfig(config) {
|
|
|
116
116
|
};
|
|
117
117
|
}
|
|
118
118
|
|
|
119
|
+
/**
|
|
120
|
+
* localStorage wrapper with type safety
|
|
121
|
+
*/
|
|
122
|
+
/**
|
|
123
|
+
* Memory storage implementation (clears on page reload)
|
|
124
|
+
*/
|
|
125
|
+
class MemoryStorage {
|
|
126
|
+
constructor() {
|
|
127
|
+
this.store = new Map();
|
|
128
|
+
}
|
|
129
|
+
getItem(key) {
|
|
130
|
+
return this.store.get(key) ?? null;
|
|
131
|
+
}
|
|
132
|
+
setItem(key, value) {
|
|
133
|
+
this.store.set(key, value);
|
|
134
|
+
}
|
|
135
|
+
removeItem(key) {
|
|
136
|
+
this.store.delete(key);
|
|
137
|
+
}
|
|
138
|
+
clear() {
|
|
139
|
+
this.store.clear();
|
|
140
|
+
}
|
|
141
|
+
get length() {
|
|
142
|
+
return this.store.size;
|
|
143
|
+
}
|
|
144
|
+
key(index) {
|
|
145
|
+
const keys = Array.from(this.store.keys());
|
|
146
|
+
return keys[index] ?? null;
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
/**
|
|
150
|
+
* Safe storage wrapper
|
|
151
|
+
*/
|
|
152
|
+
class SafeStorage {
|
|
153
|
+
constructor(storage = localStorage, prefix = 'wabbit_') {
|
|
154
|
+
this.storage = storage;
|
|
155
|
+
this.prefix = prefix;
|
|
156
|
+
}
|
|
157
|
+
/**
|
|
158
|
+
* Get item from storage
|
|
159
|
+
*/
|
|
160
|
+
get(key) {
|
|
161
|
+
try {
|
|
162
|
+
const item = this.storage.getItem(this.prefix + key);
|
|
163
|
+
if (item === null) {
|
|
164
|
+
return null;
|
|
165
|
+
}
|
|
166
|
+
return JSON.parse(item);
|
|
167
|
+
}
|
|
168
|
+
catch (error) {
|
|
169
|
+
console.error(`[Wabbit] Failed to get storage item "${key}":`, error);
|
|
170
|
+
return null;
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
/**
|
|
174
|
+
* Set item in storage
|
|
175
|
+
*/
|
|
176
|
+
set(key, value) {
|
|
177
|
+
try {
|
|
178
|
+
this.storage.setItem(this.prefix + key, JSON.stringify(value));
|
|
179
|
+
return true;
|
|
180
|
+
}
|
|
181
|
+
catch (error) {
|
|
182
|
+
console.error(`[Wabbit] Failed to set storage item "${key}":`, error);
|
|
183
|
+
return false;
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
/**
|
|
187
|
+
* Remove item from storage
|
|
188
|
+
*/
|
|
189
|
+
remove(key) {
|
|
190
|
+
this.storage.removeItem(this.prefix + key);
|
|
191
|
+
}
|
|
192
|
+
/**
|
|
193
|
+
* Clear all items with prefix
|
|
194
|
+
*/
|
|
195
|
+
clear() {
|
|
196
|
+
if (this.storage.length !== undefined && this.storage.key) {
|
|
197
|
+
const keys = [];
|
|
198
|
+
for (let i = 0; i < this.storage.length; i++) {
|
|
199
|
+
const key = this.storage.key(i);
|
|
200
|
+
if (key && key.startsWith(this.prefix)) {
|
|
201
|
+
keys.push(key);
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
keys.forEach((key) => this.storage.removeItem(key));
|
|
205
|
+
}
|
|
206
|
+
else {
|
|
207
|
+
// Fallback: try to clear common keys
|
|
208
|
+
const commonKeys = ['session_id', 'email_capture_dismissed'];
|
|
209
|
+
commonKeys.forEach((key) => this.remove(key));
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
// Export singleton instance (for backward compatibility)
|
|
214
|
+
const storage = new SafeStorage();
|
|
215
|
+
/**
|
|
216
|
+
* Create a storage instance with the specified backend
|
|
217
|
+
*
|
|
218
|
+
* @param persistSession - Whether to persist sessions across browser sessions
|
|
219
|
+
* @returns SafeStorage instance with appropriate backend
|
|
220
|
+
*/
|
|
221
|
+
function createStorage(persistSession = true) {
|
|
222
|
+
let storageBackend;
|
|
223
|
+
if (persistSession) {
|
|
224
|
+
// Use localStorage (persists across browser sessions)
|
|
225
|
+
storageBackend = typeof window !== 'undefined' ? window.localStorage : new MemoryStorage();
|
|
226
|
+
}
|
|
227
|
+
else {
|
|
228
|
+
// Use sessionStorage (clears when browser closes)
|
|
229
|
+
storageBackend = typeof window !== 'undefined' ? window.sessionStorage : new MemoryStorage();
|
|
230
|
+
}
|
|
231
|
+
return new SafeStorage(storageBackend);
|
|
232
|
+
}
|
|
233
|
+
/**
|
|
234
|
+
* Get item from storage (simple string getter)
|
|
235
|
+
*/
|
|
236
|
+
function getStorageItem(key) {
|
|
237
|
+
try {
|
|
238
|
+
return localStorage.getItem('wabbit_' + key);
|
|
239
|
+
}
|
|
240
|
+
catch {
|
|
241
|
+
return null;
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
/**
|
|
245
|
+
* Set item in storage (simple string setter)
|
|
246
|
+
*/
|
|
247
|
+
function setStorageItem(key, value) {
|
|
248
|
+
try {
|
|
249
|
+
localStorage.setItem('wabbit_' + key, value);
|
|
250
|
+
}
|
|
251
|
+
catch (error) {
|
|
252
|
+
console.error(`[Wabbit] Failed to set storage item "${key}":`, error);
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
|
|
119
256
|
/**
|
|
120
257
|
* Main Wabbit SDK class
|
|
121
258
|
*
|
|
@@ -132,6 +269,8 @@ class Wabbit {
|
|
|
132
269
|
this.formsWidget = null; // FormWidget | null
|
|
133
270
|
this.emailCaptureWidget = null; // EmailCapture | null
|
|
134
271
|
this.config = config;
|
|
272
|
+
// Create storage instance with appropriate backend based on persistSession
|
|
273
|
+
this.storage = createStorage(config.persistSession ?? true);
|
|
135
274
|
}
|
|
136
275
|
/**
|
|
137
276
|
* Initialize the Wabbit SDK
|
|
@@ -211,7 +350,7 @@ class Wabbit {
|
|
|
211
350
|
}
|
|
212
351
|
}
|
|
213
352
|
};
|
|
214
|
-
const chatWidget = new ChatWidget(chatConfig);
|
|
353
|
+
const chatWidget = new ChatWidget(chatConfig, Wabbit.instance.storage);
|
|
215
354
|
chatWidget.init();
|
|
216
355
|
Wabbit.instance.chatWidget = chatWidget;
|
|
217
356
|
});
|
|
@@ -534,105 +673,13 @@ class Wabbit {
|
|
|
534
673
|
}
|
|
535
674
|
Wabbit.instance = null;
|
|
536
675
|
|
|
537
|
-
/**
|
|
538
|
-
* localStorage wrapper with type safety
|
|
539
|
-
*/
|
|
540
|
-
/**
|
|
541
|
-
* Safe storage wrapper
|
|
542
|
-
*/
|
|
543
|
-
class SafeStorage {
|
|
544
|
-
constructor(storage = localStorage, prefix = 'wabbit_') {
|
|
545
|
-
this.storage = storage;
|
|
546
|
-
this.prefix = prefix;
|
|
547
|
-
}
|
|
548
|
-
/**
|
|
549
|
-
* Get item from storage
|
|
550
|
-
*/
|
|
551
|
-
get(key) {
|
|
552
|
-
try {
|
|
553
|
-
const item = this.storage.getItem(this.prefix + key);
|
|
554
|
-
if (item === null) {
|
|
555
|
-
return null;
|
|
556
|
-
}
|
|
557
|
-
return JSON.parse(item);
|
|
558
|
-
}
|
|
559
|
-
catch (error) {
|
|
560
|
-
console.error(`[Wabbit] Failed to get storage item "${key}":`, error);
|
|
561
|
-
return null;
|
|
562
|
-
}
|
|
563
|
-
}
|
|
564
|
-
/**
|
|
565
|
-
* Set item in storage
|
|
566
|
-
*/
|
|
567
|
-
set(key, value) {
|
|
568
|
-
try {
|
|
569
|
-
this.storage.setItem(this.prefix + key, JSON.stringify(value));
|
|
570
|
-
return true;
|
|
571
|
-
}
|
|
572
|
-
catch (error) {
|
|
573
|
-
console.error(`[Wabbit] Failed to set storage item "${key}":`, error);
|
|
574
|
-
return false;
|
|
575
|
-
}
|
|
576
|
-
}
|
|
577
|
-
/**
|
|
578
|
-
* Remove item from storage
|
|
579
|
-
*/
|
|
580
|
-
remove(key) {
|
|
581
|
-
this.storage.removeItem(this.prefix + key);
|
|
582
|
-
}
|
|
583
|
-
/**
|
|
584
|
-
* Clear all items with prefix
|
|
585
|
-
*/
|
|
586
|
-
clear() {
|
|
587
|
-
if (this.storage.length !== undefined && this.storage.key) {
|
|
588
|
-
const keys = [];
|
|
589
|
-
for (let i = 0; i < this.storage.length; i++) {
|
|
590
|
-
const key = this.storage.key(i);
|
|
591
|
-
if (key && key.startsWith(this.prefix)) {
|
|
592
|
-
keys.push(key);
|
|
593
|
-
}
|
|
594
|
-
}
|
|
595
|
-
keys.forEach((key) => this.storage.removeItem(key));
|
|
596
|
-
}
|
|
597
|
-
else {
|
|
598
|
-
// Fallback: try to clear common keys
|
|
599
|
-
const commonKeys = ['session_id', 'email_capture_dismissed'];
|
|
600
|
-
commonKeys.forEach((key) => this.remove(key));
|
|
601
|
-
}
|
|
602
|
-
}
|
|
603
|
-
}
|
|
604
|
-
// Export singleton instance
|
|
605
|
-
const storage = new SafeStorage();
|
|
606
|
-
/**
|
|
607
|
-
* Get item from storage (simple string getter)
|
|
608
|
-
*/
|
|
609
|
-
function getStorageItem(key) {
|
|
610
|
-
try {
|
|
611
|
-
return localStorage.getItem('wabbit_' + key);
|
|
612
|
-
}
|
|
613
|
-
catch {
|
|
614
|
-
return null;
|
|
615
|
-
}
|
|
616
|
-
}
|
|
617
|
-
/**
|
|
618
|
-
* Set item in storage (simple string setter)
|
|
619
|
-
*/
|
|
620
|
-
function setStorageItem(key, value) {
|
|
621
|
-
try {
|
|
622
|
-
localStorage.setItem('wabbit_' + key, value);
|
|
623
|
-
}
|
|
624
|
-
catch (error) {
|
|
625
|
-
console.error(`[Wabbit] Failed to set storage item "${key}":`, error);
|
|
626
|
-
}
|
|
627
|
-
}
|
|
628
|
-
|
|
629
676
|
/**
|
|
630
677
|
* WebSocket client for chat functionality
|
|
631
678
|
*
|
|
632
679
|
* Based on demo-website/src/lib/websocket.ts but without React dependencies
|
|
633
680
|
*/
|
|
634
681
|
class ChatWebSocketClient {
|
|
635
|
-
constructor(apiKey, wsUrl, sessionId = null) {
|
|
682
|
+
constructor(apiKey, wsUrl, sessionId = null, storage) {
|
|
636
683
|
this.ws = null;
|
|
637
684
|
this.status = 'disconnected';
|
|
638
685
|
this.reconnectAttempts = 0;
|
|
@@ -655,6 +702,7 @@ class ChatWebSocketClient {
|
|
|
655
702
|
this.onMessageEnd = null;
|
|
656
703
|
this.apiKey = apiKey;
|
|
657
704
|
this.wsUrl = wsUrl;
|
|
705
|
+
this.storage = storage;
|
|
658
706
|
this.sessionId = sessionId || this.getStoredSessionId();
|
|
659
707
|
}
|
|
660
708
|
setStatus(status) {
|
|
@@ -723,9 +771,9 @@ class ChatWebSocketClient {
|
|
|
723
771
|
switch (data.type) {
|
|
724
772
|
case 'welcome':
|
|
725
773
|
this.sessionId = data.session_id;
|
|
726
|
-
// Store session ID in localStorage
|
|
774
|
+
// Store session ID in storage (localStorage or sessionStorage)
|
|
727
775
|
if (this.sessionId) {
|
|
728
|
-
storage.set('session_id', this.sessionId);
|
|
776
|
+
this.storage.set('session_id', this.sessionId);
|
|
729
777
|
}
|
|
730
778
|
if (this.onWelcome) {
|
|
731
779
|
this.onWelcome(data.session_id, data.collection_id, data.message || 'Connected');
|
|
@@ -851,7 +899,7 @@ class ChatWebSocketClient {
|
|
|
851
899
|
}
|
|
852
900
|
clearSession() {
|
|
853
901
|
this.sessionId = null;
|
|
854
|
-
storage.remove('session_id');
|
|
902
|
+
this.storage.remove('session_id');
|
|
855
903
|
// Clear message queue only when user explicitly starts new session
|
|
856
904
|
// Queue should persist across reconnection attempts
|
|
857
905
|
this.messageQueue = [];
|
|
@@ -864,7 +912,7 @@ class ChatWebSocketClient {
|
|
|
864
912
|
this.messageQueue = [];
|
|
865
913
|
}
|
|
866
914
|
getStoredSessionId() {
|
|
867
|
-
return storage.get('session_id') || null;
|
|
915
|
+
return this.storage.get('session_id') || null;
|
|
868
916
|
}
|
|
869
917
|
/**
|
|
870
918
|
* Get current message queue size
|
|
@@ -999,6 +1047,7 @@ class ChatPanel {
|
|
|
999
1047
|
this.messages = [];
|
|
1000
1048
|
this.isWaitingForResponse = false;
|
|
1001
1049
|
this.closeButton = null;
|
|
1050
|
+
this.statusIndicator = null;
|
|
1002
1051
|
this.eventCleanup = [];
|
|
1003
1052
|
this.streamingMessages = new Map();
|
|
1004
1053
|
this.streamingCleanupInterval = null;
|
|
@@ -1134,6 +1183,14 @@ class ChatPanel {
|
|
|
1134
1183
|
this.sendButton?.removeEventListener('click', sendClickHandler);
|
|
1135
1184
|
});
|
|
1136
1185
|
this.updateSendButtonState();
|
|
1186
|
+
this.statusIndicator = createElement('div', {
|
|
1187
|
+
class: 'wabbit-chat-status-indicator',
|
|
1188
|
+
'data-status': 'connecting',
|
|
1189
|
+
title: 'Connecting...',
|
|
1190
|
+
'aria-label': 'Connection status: connecting',
|
|
1191
|
+
role: 'status'
|
|
1192
|
+
});
|
|
1193
|
+
inputWrapper.appendChild(this.statusIndicator);
|
|
1137
1194
|
inputWrapper.appendChild(this.inputElement);
|
|
1138
1195
|
inputWrapper.appendChild(this.sendButton);
|
|
1139
1196
|
inputArea.appendChild(inputWrapper);
|
|
@@ -1205,6 +1262,22 @@ class ChatPanel {
|
|
|
1205
1262
|
this.updateSendButtonState();
|
|
1206
1263
|
}
|
|
1207
1264
|
}
|
|
1265
|
+
/**
|
|
1266
|
+
* Update the connection status indicator
|
|
1267
|
+
*/
|
|
1268
|
+
setConnectionStatus(status) {
|
|
1269
|
+
if (!this.statusIndicator)
|
|
1270
|
+
return;
|
|
1271
|
+
this.statusIndicator.setAttribute('data-status', status);
|
|
1272
|
+
const labels = {
|
|
1273
|
+
connected: 'Connected',
|
|
1274
|
+
disconnected: 'Disconnected',
|
|
1275
|
+
connecting: 'Connecting...',
|
|
1276
|
+
reconnecting: 'Reconnecting...',
|
|
1277
|
+
};
|
|
1278
|
+
this.statusIndicator.setAttribute('title', labels[status]);
|
|
1279
|
+
this.statusIndicator.setAttribute('aria-label', `Connection status: ${labels[status].toLowerCase()}`);
|
|
1280
|
+
}
|
|
1208
1281
|
/**
|
|
1209
1282
|
* Render a single message
|
|
1210
1283
|
*/
|
|
@@ -1234,10 +1307,21 @@ class ChatPanel {
|
|
|
1234
1307
|
formatMessage(content) {
|
|
1235
1308
|
// Escape HTML first
|
|
1236
1309
|
let formatted = escapeHtml(content);
|
|
1310
|
+
// Markdown links [text](url) - must come before URL auto-linking
|
|
1311
|
+
formatted = formatted.replace(/\[([^\]]+)\]\(([^)]+)\)/g, '<a href="$2" target="_blank" rel="noopener noreferrer">$1</a>');
|
|
1237
1312
|
// Simple markdown-like formatting
|
|
1238
1313
|
formatted = formatted.replace(/\*\*(.+?)\*\*/g, '<strong>$1</strong>');
|
|
1239
1314
|
formatted = formatted.replace(/\*(.+?)\*/g, '<em>$1</em>');
|
|
1240
1315
|
formatted = formatted.replace(/`(.+?)`/g, '<code style="background: rgba(0,0,0,0.1); padding: 2px 4px; border-radius: 3px;">$1</code>');
|
|
1316
|
+
// Auto-link URLs (not already inside an href attribute)
|
|
1317
|
+
formatted = formatted.replace(/(?<!href=")(https?:\/\/[^\s<]+)/g, '<a href="$1" target="_blank" rel="noopener noreferrer">$1</a>');
|
|
1318
|
+
// Auto-link email addresses
|
|
1319
|
+
formatted = formatted.replace(/(?<!["\/])([a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,})/g, '<a href="mailto:$1">$1</a>');
|
|
1320
|
+
// Auto-link phone numbers (international format with +)
|
|
1321
|
+
formatted = formatted.replace(/(?<!["\/])(\+\d[\d\s-]{7,})/g, (_match, phone) => {
|
|
1322
|
+
const cleanPhone = phone.replace(/[\s-]/g, '');
|
|
1323
|
+
return `<a href="tel:${cleanPhone}">${phone}</a>`;
|
|
1324
|
+
});
|
|
1241
1325
|
formatted = formatted.replace(/\n/g, '<br>');
|
|
1242
1326
|
return formatted;
|
|
1243
1327
|
}
|
|
@@ -1476,6 +1560,7 @@ class ChatPanel {
|
|
|
1476
1560
|
this.inputElement = null;
|
|
1477
1561
|
this.sendButton = null;
|
|
1478
1562
|
this.closeButton = null;
|
|
1563
|
+
this.statusIndicator = null;
|
|
1479
1564
|
}
|
|
1480
1565
|
}
|
|
1481
1566
|
}
|
|
@@ -1790,6 +1875,20 @@ function injectChatStyles(primaryColor = '#6366f1', theme) {
|
|
|
1790
1875
|
line-height: 1.5;
|
|
1791
1876
|
}
|
|
1792
1877
|
|
|
1878
|
+
.wabbit-chat-message-content a {
|
|
1879
|
+
color: var(--wabbit-primary);
|
|
1880
|
+
text-decoration: underline;
|
|
1881
|
+
text-underline-offset: 2px;
|
|
1882
|
+
}
|
|
1883
|
+
|
|
1884
|
+
.wabbit-chat-message-content a:hover {
|
|
1885
|
+
opacity: 0.8;
|
|
1886
|
+
}
|
|
1887
|
+
|
|
1888
|
+
.wabbit-chat-message-user .wabbit-chat-message-content a {
|
|
1889
|
+
color: inherit;
|
|
1890
|
+
}
|
|
1891
|
+
|
|
1793
1892
|
/* Typing Indicator */
|
|
1794
1893
|
.wabbit-chat-typing {
|
|
1795
1894
|
display: flex;
|
|
@@ -1902,6 +2001,35 @@ function injectChatStyles(primaryColor = '#6366f1', theme) {
|
|
|
1902
2001
|
height: 20px;
|
|
1903
2002
|
}
|
|
1904
2003
|
|
|
2004
|
+
/* Connection Status Indicator */
|
|
2005
|
+
.wabbit-chat-status-indicator {
|
|
2006
|
+
width: 8px;
|
|
2007
|
+
height: 8px;
|
|
2008
|
+
border-radius: 50%;
|
|
2009
|
+
flex-shrink: 0;
|
|
2010
|
+
align-self: center;
|
|
2011
|
+
transition: background-color 0.3s ease;
|
|
2012
|
+
}
|
|
2013
|
+
|
|
2014
|
+
.wabbit-chat-status-indicator[data-status="connected"] {
|
|
2015
|
+
background-color: #22c55e;
|
|
2016
|
+
}
|
|
2017
|
+
|
|
2018
|
+
.wabbit-chat-status-indicator[data-status="disconnected"] {
|
|
2019
|
+
background-color: #ef4444;
|
|
2020
|
+
}
|
|
2021
|
+
|
|
2022
|
+
.wabbit-chat-status-indicator[data-status="connecting"],
|
|
2023
|
+
.wabbit-chat-status-indicator[data-status="reconnecting"] {
|
|
2024
|
+
background-color: #f59e0b;
|
|
2025
|
+
animation: wabbit-status-pulse 1.5s ease-in-out infinite;
|
|
2026
|
+
}
|
|
2027
|
+
|
|
2028
|
+
@keyframes wabbit-status-pulse {
|
|
2029
|
+
0%, 100% { opacity: 1; }
|
|
2030
|
+
50% { opacity: 0.3; }
|
|
2031
|
+
}
|
|
2032
|
+
|
|
1905
2033
|
/* Scrollbar */
|
|
1906
2034
|
.wabbit-chat-messages::-webkit-scrollbar {
|
|
1907
2035
|
width: 6px;
|
|
@@ -2025,7 +2153,7 @@ function adjustColor(color, amount) {
|
|
|
2025
2153
|
* Chat Widget - Main class that integrates all chat components
|
|
2026
2154
|
*/
|
|
2027
2155
|
class ChatWidget {
|
|
2028
|
-
constructor(config) {
|
|
2156
|
+
constructor(config, storage) {
|
|
2029
2157
|
this.wsClient = null;
|
|
2030
2158
|
this.bubble = null;
|
|
2031
2159
|
this.panel = null;
|
|
@@ -2035,6 +2163,7 @@ class ChatWidget {
|
|
|
2035
2163
|
this.onChatReadyCallback = null;
|
|
2036
2164
|
this.chatReadyFired = false;
|
|
2037
2165
|
this.config = config;
|
|
2166
|
+
this.storage = storage;
|
|
2038
2167
|
this.onChatReadyCallback = config.onChatReady || null;
|
|
2039
2168
|
}
|
|
2040
2169
|
/**
|
|
@@ -2063,8 +2192,8 @@ class ChatWidget {
|
|
|
2063
2192
|
this.setupThemeWatcher();
|
|
2064
2193
|
// Create WebSocket client
|
|
2065
2194
|
const wsUrl = this.getWebSocketUrl();
|
|
2066
|
-
this.wsClient = new ChatWebSocketClient(this.config.apiKey || '', wsUrl, null // Will use stored session if available
|
|
2067
|
-
);
|
|
2195
|
+
this.wsClient = new ChatWebSocketClient(this.config.apiKey || '', wsUrl, null, // Will use stored session if available
|
|
2196
|
+
this.storage);
|
|
2068
2197
|
// Set up event handlers
|
|
2069
2198
|
this.setupWebSocketHandlers();
|
|
2070
2199
|
// Only create bubble for widget mode (not inline)
|
|
@@ -2186,36 +2315,24 @@ class ChatWidget {
|
|
|
2186
2315
|
this.wsClient.onError = (error) => {
|
|
2187
2316
|
console.error('[Wabbit] Chat error:', error);
|
|
2188
2317
|
if (this.panel) {
|
|
2189
|
-
// Clean up any active streaming messages on error
|
|
2190
2318
|
this.panel.cancelAllStreamingMessages();
|
|
2191
|
-
this.panel.addSystemMessage(`Error: ${error}`);
|
|
2192
2319
|
this.panel.setWaitingForResponse(false);
|
|
2193
2320
|
}
|
|
2194
2321
|
};
|
|
2195
2322
|
this.wsClient.onStatusChange = (status) => {
|
|
2196
2323
|
if (this.panel) {
|
|
2197
2324
|
this.panel.setDisabled(status !== 'connected');
|
|
2325
|
+
this.panel.setConnectionStatus(status);
|
|
2198
2326
|
}
|
|
2199
2327
|
};
|
|
2200
2328
|
this.wsClient.onDisconnect = () => {
|
|
2201
2329
|
if (this.panel) {
|
|
2202
|
-
// Clean up any active streaming messages on disconnect
|
|
2203
2330
|
this.panel.cancelAllStreamingMessages();
|
|
2204
|
-
// Check if there are queued messages
|
|
2205
|
-
const queueSize = this.wsClient.getQueueSize();
|
|
2206
|
-
if (queueSize > 0) {
|
|
2207
|
-
this.panel.addSystemMessage(`Disconnected from chat service. ${queueSize} message(s) will be sent when reconnected.`);
|
|
2208
|
-
}
|
|
2209
|
-
else {
|
|
2210
|
-
this.panel.addSystemMessage('Disconnected from chat service');
|
|
2211
|
-
}
|
|
2212
2331
|
this.panel.setDisabled(true);
|
|
2213
2332
|
}
|
|
2214
2333
|
};
|
|
2215
2334
|
this.wsClient.onQueueOverflow = (message) => {
|
|
2216
|
-
|
|
2217
|
-
this.panel.addSystemMessage(message);
|
|
2218
|
-
}
|
|
2335
|
+
console.warn('[Wabbit] Queue overflow:', message);
|
|
2219
2336
|
};
|
|
2220
2337
|
}
|
|
2221
2338
|
/**
|