@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.umd.js
CHANGED
|
@@ -122,6 +122,143 @@
|
|
|
122
122
|
};
|
|
123
123
|
}
|
|
124
124
|
|
|
125
|
+
/**
|
|
126
|
+
* localStorage wrapper with type safety
|
|
127
|
+
*/
|
|
128
|
+
/**
|
|
129
|
+
* Memory storage implementation (clears on page reload)
|
|
130
|
+
*/
|
|
131
|
+
class MemoryStorage {
|
|
132
|
+
constructor() {
|
|
133
|
+
this.store = new Map();
|
|
134
|
+
}
|
|
135
|
+
getItem(key) {
|
|
136
|
+
return this.store.get(key) ?? null;
|
|
137
|
+
}
|
|
138
|
+
setItem(key, value) {
|
|
139
|
+
this.store.set(key, value);
|
|
140
|
+
}
|
|
141
|
+
removeItem(key) {
|
|
142
|
+
this.store.delete(key);
|
|
143
|
+
}
|
|
144
|
+
clear() {
|
|
145
|
+
this.store.clear();
|
|
146
|
+
}
|
|
147
|
+
get length() {
|
|
148
|
+
return this.store.size;
|
|
149
|
+
}
|
|
150
|
+
key(index) {
|
|
151
|
+
const keys = Array.from(this.store.keys());
|
|
152
|
+
return keys[index] ?? null;
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
/**
|
|
156
|
+
* Safe storage wrapper
|
|
157
|
+
*/
|
|
158
|
+
class SafeStorage {
|
|
159
|
+
constructor(storage = localStorage, prefix = 'wabbit_') {
|
|
160
|
+
this.storage = storage;
|
|
161
|
+
this.prefix = prefix;
|
|
162
|
+
}
|
|
163
|
+
/**
|
|
164
|
+
* Get item from storage
|
|
165
|
+
*/
|
|
166
|
+
get(key) {
|
|
167
|
+
try {
|
|
168
|
+
const item = this.storage.getItem(this.prefix + key);
|
|
169
|
+
if (item === null) {
|
|
170
|
+
return null;
|
|
171
|
+
}
|
|
172
|
+
return JSON.parse(item);
|
|
173
|
+
}
|
|
174
|
+
catch (error) {
|
|
175
|
+
console.error(`[Wabbit] Failed to get storage item "${key}":`, error);
|
|
176
|
+
return null;
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
/**
|
|
180
|
+
* Set item in storage
|
|
181
|
+
*/
|
|
182
|
+
set(key, value) {
|
|
183
|
+
try {
|
|
184
|
+
this.storage.setItem(this.prefix + key, JSON.stringify(value));
|
|
185
|
+
return true;
|
|
186
|
+
}
|
|
187
|
+
catch (error) {
|
|
188
|
+
console.error(`[Wabbit] Failed to set storage item "${key}":`, error);
|
|
189
|
+
return false;
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
/**
|
|
193
|
+
* Remove item from storage
|
|
194
|
+
*/
|
|
195
|
+
remove(key) {
|
|
196
|
+
this.storage.removeItem(this.prefix + key);
|
|
197
|
+
}
|
|
198
|
+
/**
|
|
199
|
+
* Clear all items with prefix
|
|
200
|
+
*/
|
|
201
|
+
clear() {
|
|
202
|
+
if (this.storage.length !== undefined && this.storage.key) {
|
|
203
|
+
const keys = [];
|
|
204
|
+
for (let i = 0; i < this.storage.length; i++) {
|
|
205
|
+
const key = this.storage.key(i);
|
|
206
|
+
if (key && key.startsWith(this.prefix)) {
|
|
207
|
+
keys.push(key);
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
keys.forEach((key) => this.storage.removeItem(key));
|
|
211
|
+
}
|
|
212
|
+
else {
|
|
213
|
+
// Fallback: try to clear common keys
|
|
214
|
+
const commonKeys = ['session_id', 'email_capture_dismissed'];
|
|
215
|
+
commonKeys.forEach((key) => this.remove(key));
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
// Export singleton instance (for backward compatibility)
|
|
220
|
+
const storage = new SafeStorage();
|
|
221
|
+
/**
|
|
222
|
+
* Create a storage instance with the specified backend
|
|
223
|
+
*
|
|
224
|
+
* @param persistSession - Whether to persist sessions across browser sessions
|
|
225
|
+
* @returns SafeStorage instance with appropriate backend
|
|
226
|
+
*/
|
|
227
|
+
function createStorage(persistSession = true) {
|
|
228
|
+
let storageBackend;
|
|
229
|
+
if (persistSession) {
|
|
230
|
+
// Use localStorage (persists across browser sessions)
|
|
231
|
+
storageBackend = typeof window !== 'undefined' ? window.localStorage : new MemoryStorage();
|
|
232
|
+
}
|
|
233
|
+
else {
|
|
234
|
+
// Use sessionStorage (clears when browser closes)
|
|
235
|
+
storageBackend = typeof window !== 'undefined' ? window.sessionStorage : new MemoryStorage();
|
|
236
|
+
}
|
|
237
|
+
return new SafeStorage(storageBackend);
|
|
238
|
+
}
|
|
239
|
+
/**
|
|
240
|
+
* Get item from storage (simple string getter)
|
|
241
|
+
*/
|
|
242
|
+
function getStorageItem(key) {
|
|
243
|
+
try {
|
|
244
|
+
return localStorage.getItem('wabbit_' + key);
|
|
245
|
+
}
|
|
246
|
+
catch {
|
|
247
|
+
return null;
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
/**
|
|
251
|
+
* Set item in storage (simple string setter)
|
|
252
|
+
*/
|
|
253
|
+
function setStorageItem(key, value) {
|
|
254
|
+
try {
|
|
255
|
+
localStorage.setItem('wabbit_' + key, value);
|
|
256
|
+
}
|
|
257
|
+
catch (error) {
|
|
258
|
+
console.error(`[Wabbit] Failed to set storage item "${key}":`, error);
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
|
|
125
262
|
/**
|
|
126
263
|
* Main Wabbit SDK class
|
|
127
264
|
*
|
|
@@ -138,6 +275,8 @@
|
|
|
138
275
|
this.formsWidget = null; // FormWidget | null
|
|
139
276
|
this.emailCaptureWidget = null; // EmailCapture | null
|
|
140
277
|
this.config = config;
|
|
278
|
+
// Create storage instance with appropriate backend based on persistSession
|
|
279
|
+
this.storage = createStorage(config.persistSession ?? true);
|
|
141
280
|
}
|
|
142
281
|
/**
|
|
143
282
|
* Initialize the Wabbit SDK
|
|
@@ -217,7 +356,7 @@
|
|
|
217
356
|
}
|
|
218
357
|
}
|
|
219
358
|
};
|
|
220
|
-
const chatWidget = new ChatWidget(chatConfig);
|
|
359
|
+
const chatWidget = new ChatWidget(chatConfig, Wabbit.instance.storage);
|
|
221
360
|
chatWidget.init();
|
|
222
361
|
Wabbit.instance.chatWidget = chatWidget;
|
|
223
362
|
});
|
|
@@ -540,105 +679,13 @@
|
|
|
540
679
|
}
|
|
541
680
|
Wabbit.instance = null;
|
|
542
681
|
|
|
543
|
-
/**
|
|
544
|
-
* localStorage wrapper with type safety
|
|
545
|
-
*/
|
|
546
|
-
/**
|
|
547
|
-
* Safe storage wrapper
|
|
548
|
-
*/
|
|
549
|
-
class SafeStorage {
|
|
550
|
-
constructor(storage = localStorage, prefix = 'wabbit_') {
|
|
551
|
-
this.storage = storage;
|
|
552
|
-
this.prefix = prefix;
|
|
553
|
-
}
|
|
554
|
-
/**
|
|
555
|
-
* Get item from storage
|
|
556
|
-
*/
|
|
557
|
-
get(key) {
|
|
558
|
-
try {
|
|
559
|
-
const item = this.storage.getItem(this.prefix + key);
|
|
560
|
-
if (item === null) {
|
|
561
|
-
return null;
|
|
562
|
-
}
|
|
563
|
-
return JSON.parse(item);
|
|
564
|
-
}
|
|
565
|
-
catch (error) {
|
|
566
|
-
console.error(`[Wabbit] Failed to get storage item "${key}":`, error);
|
|
567
|
-
return null;
|
|
568
|
-
}
|
|
569
|
-
}
|
|
570
|
-
/**
|
|
571
|
-
* Set item in storage
|
|
572
|
-
*/
|
|
573
|
-
set(key, value) {
|
|
574
|
-
try {
|
|
575
|
-
this.storage.setItem(this.prefix + key, JSON.stringify(value));
|
|
576
|
-
return true;
|
|
577
|
-
}
|
|
578
|
-
catch (error) {
|
|
579
|
-
console.error(`[Wabbit] Failed to set storage item "${key}":`, error);
|
|
580
|
-
return false;
|
|
581
|
-
}
|
|
582
|
-
}
|
|
583
|
-
/**
|
|
584
|
-
* Remove item from storage
|
|
585
|
-
*/
|
|
586
|
-
remove(key) {
|
|
587
|
-
this.storage.removeItem(this.prefix + key);
|
|
588
|
-
}
|
|
589
|
-
/**
|
|
590
|
-
* Clear all items with prefix
|
|
591
|
-
*/
|
|
592
|
-
clear() {
|
|
593
|
-
if (this.storage.length !== undefined && this.storage.key) {
|
|
594
|
-
const keys = [];
|
|
595
|
-
for (let i = 0; i < this.storage.length; i++) {
|
|
596
|
-
const key = this.storage.key(i);
|
|
597
|
-
if (key && key.startsWith(this.prefix)) {
|
|
598
|
-
keys.push(key);
|
|
599
|
-
}
|
|
600
|
-
}
|
|
601
|
-
keys.forEach((key) => this.storage.removeItem(key));
|
|
602
|
-
}
|
|
603
|
-
else {
|
|
604
|
-
// Fallback: try to clear common keys
|
|
605
|
-
const commonKeys = ['session_id', 'email_capture_dismissed'];
|
|
606
|
-
commonKeys.forEach((key) => this.remove(key));
|
|
607
|
-
}
|
|
608
|
-
}
|
|
609
|
-
}
|
|
610
|
-
// Export singleton instance
|
|
611
|
-
const storage = new SafeStorage();
|
|
612
|
-
/**
|
|
613
|
-
* Get item from storage (simple string getter)
|
|
614
|
-
*/
|
|
615
|
-
function getStorageItem(key) {
|
|
616
|
-
try {
|
|
617
|
-
return localStorage.getItem('wabbit_' + key);
|
|
618
|
-
}
|
|
619
|
-
catch {
|
|
620
|
-
return null;
|
|
621
|
-
}
|
|
622
|
-
}
|
|
623
|
-
/**
|
|
624
|
-
* Set item in storage (simple string setter)
|
|
625
|
-
*/
|
|
626
|
-
function setStorageItem(key, value) {
|
|
627
|
-
try {
|
|
628
|
-
localStorage.setItem('wabbit_' + key, value);
|
|
629
|
-
}
|
|
630
|
-
catch (error) {
|
|
631
|
-
console.error(`[Wabbit] Failed to set storage item "${key}":`, error);
|
|
632
|
-
}
|
|
633
|
-
}
|
|
634
|
-
|
|
635
682
|
/**
|
|
636
683
|
* WebSocket client for chat functionality
|
|
637
684
|
*
|
|
638
685
|
* Based on demo-website/src/lib/websocket.ts but without React dependencies
|
|
639
686
|
*/
|
|
640
687
|
class ChatWebSocketClient {
|
|
641
|
-
constructor(apiKey, wsUrl, sessionId = null) {
|
|
688
|
+
constructor(apiKey, wsUrl, sessionId = null, storage) {
|
|
642
689
|
this.ws = null;
|
|
643
690
|
this.status = 'disconnected';
|
|
644
691
|
this.reconnectAttempts = 0;
|
|
@@ -661,6 +708,7 @@
|
|
|
661
708
|
this.onMessageEnd = null;
|
|
662
709
|
this.apiKey = apiKey;
|
|
663
710
|
this.wsUrl = wsUrl;
|
|
711
|
+
this.storage = storage;
|
|
664
712
|
this.sessionId = sessionId || this.getStoredSessionId();
|
|
665
713
|
}
|
|
666
714
|
setStatus(status) {
|
|
@@ -729,9 +777,9 @@
|
|
|
729
777
|
switch (data.type) {
|
|
730
778
|
case 'welcome':
|
|
731
779
|
this.sessionId = data.session_id;
|
|
732
|
-
// Store session ID in localStorage
|
|
780
|
+
// Store session ID in storage (localStorage or sessionStorage)
|
|
733
781
|
if (this.sessionId) {
|
|
734
|
-
storage.set('session_id', this.sessionId);
|
|
782
|
+
this.storage.set('session_id', this.sessionId);
|
|
735
783
|
}
|
|
736
784
|
if (this.onWelcome) {
|
|
737
785
|
this.onWelcome(data.session_id, data.collection_id, data.message || 'Connected');
|
|
@@ -857,7 +905,7 @@
|
|
|
857
905
|
}
|
|
858
906
|
clearSession() {
|
|
859
907
|
this.sessionId = null;
|
|
860
|
-
storage.remove('session_id');
|
|
908
|
+
this.storage.remove('session_id');
|
|
861
909
|
// Clear message queue only when user explicitly starts new session
|
|
862
910
|
// Queue should persist across reconnection attempts
|
|
863
911
|
this.messageQueue = [];
|
|
@@ -870,7 +918,7 @@
|
|
|
870
918
|
this.messageQueue = [];
|
|
871
919
|
}
|
|
872
920
|
getStoredSessionId() {
|
|
873
|
-
return storage.get('session_id') || null;
|
|
921
|
+
return this.storage.get('session_id') || null;
|
|
874
922
|
}
|
|
875
923
|
/**
|
|
876
924
|
* Get current message queue size
|
|
@@ -1005,6 +1053,7 @@
|
|
|
1005
1053
|
this.messages = [];
|
|
1006
1054
|
this.isWaitingForResponse = false;
|
|
1007
1055
|
this.closeButton = null;
|
|
1056
|
+
this.statusIndicator = null;
|
|
1008
1057
|
this.eventCleanup = [];
|
|
1009
1058
|
this.streamingMessages = new Map();
|
|
1010
1059
|
this.streamingCleanupInterval = null;
|
|
@@ -1140,6 +1189,14 @@
|
|
|
1140
1189
|
this.sendButton?.removeEventListener('click', sendClickHandler);
|
|
1141
1190
|
});
|
|
1142
1191
|
this.updateSendButtonState();
|
|
1192
|
+
this.statusIndicator = createElement('div', {
|
|
1193
|
+
class: 'wabbit-chat-status-indicator',
|
|
1194
|
+
'data-status': 'connecting',
|
|
1195
|
+
title: 'Connecting...',
|
|
1196
|
+
'aria-label': 'Connection status: connecting',
|
|
1197
|
+
role: 'status'
|
|
1198
|
+
});
|
|
1199
|
+
inputWrapper.appendChild(this.statusIndicator);
|
|
1143
1200
|
inputWrapper.appendChild(this.inputElement);
|
|
1144
1201
|
inputWrapper.appendChild(this.sendButton);
|
|
1145
1202
|
inputArea.appendChild(inputWrapper);
|
|
@@ -1211,6 +1268,22 @@
|
|
|
1211
1268
|
this.updateSendButtonState();
|
|
1212
1269
|
}
|
|
1213
1270
|
}
|
|
1271
|
+
/**
|
|
1272
|
+
* Update the connection status indicator
|
|
1273
|
+
*/
|
|
1274
|
+
setConnectionStatus(status) {
|
|
1275
|
+
if (!this.statusIndicator)
|
|
1276
|
+
return;
|
|
1277
|
+
this.statusIndicator.setAttribute('data-status', status);
|
|
1278
|
+
const labels = {
|
|
1279
|
+
connected: 'Connected',
|
|
1280
|
+
disconnected: 'Disconnected',
|
|
1281
|
+
connecting: 'Connecting...',
|
|
1282
|
+
reconnecting: 'Reconnecting...',
|
|
1283
|
+
};
|
|
1284
|
+
this.statusIndicator.setAttribute('title', labels[status]);
|
|
1285
|
+
this.statusIndicator.setAttribute('aria-label', `Connection status: ${labels[status].toLowerCase()}`);
|
|
1286
|
+
}
|
|
1214
1287
|
/**
|
|
1215
1288
|
* Render a single message
|
|
1216
1289
|
*/
|
|
@@ -1240,10 +1313,21 @@
|
|
|
1240
1313
|
formatMessage(content) {
|
|
1241
1314
|
// Escape HTML first
|
|
1242
1315
|
let formatted = escapeHtml(content);
|
|
1316
|
+
// Markdown links [text](url) - must come before URL auto-linking
|
|
1317
|
+
formatted = formatted.replace(/\[([^\]]+)\]\(([^)]+)\)/g, '<a href="$2" target="_blank" rel="noopener noreferrer">$1</a>');
|
|
1243
1318
|
// Simple markdown-like formatting
|
|
1244
1319
|
formatted = formatted.replace(/\*\*(.+?)\*\*/g, '<strong>$1</strong>');
|
|
1245
1320
|
formatted = formatted.replace(/\*(.+?)\*/g, '<em>$1</em>');
|
|
1246
1321
|
formatted = formatted.replace(/`(.+?)`/g, '<code style="background: rgba(0,0,0,0.1); padding: 2px 4px; border-radius: 3px;">$1</code>');
|
|
1322
|
+
// Auto-link URLs (not already inside an href attribute)
|
|
1323
|
+
formatted = formatted.replace(/(?<!href=")(https?:\/\/[^\s<]+)/g, '<a href="$1" target="_blank" rel="noopener noreferrer">$1</a>');
|
|
1324
|
+
// Auto-link email addresses
|
|
1325
|
+
formatted = formatted.replace(/(?<!["\/])([a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,})/g, '<a href="mailto:$1">$1</a>');
|
|
1326
|
+
// Auto-link phone numbers (international format with +)
|
|
1327
|
+
formatted = formatted.replace(/(?<!["\/])(\+\d[\d\s-]{7,})/g, (_match, phone) => {
|
|
1328
|
+
const cleanPhone = phone.replace(/[\s-]/g, '');
|
|
1329
|
+
return `<a href="tel:${cleanPhone}">${phone}</a>`;
|
|
1330
|
+
});
|
|
1247
1331
|
formatted = formatted.replace(/\n/g, '<br>');
|
|
1248
1332
|
return formatted;
|
|
1249
1333
|
}
|
|
@@ -1482,6 +1566,7 @@
|
|
|
1482
1566
|
this.inputElement = null;
|
|
1483
1567
|
this.sendButton = null;
|
|
1484
1568
|
this.closeButton = null;
|
|
1569
|
+
this.statusIndicator = null;
|
|
1485
1570
|
}
|
|
1486
1571
|
}
|
|
1487
1572
|
}
|
|
@@ -1796,6 +1881,20 @@
|
|
|
1796
1881
|
line-height: 1.5;
|
|
1797
1882
|
}
|
|
1798
1883
|
|
|
1884
|
+
.wabbit-chat-message-content a {
|
|
1885
|
+
color: var(--wabbit-primary);
|
|
1886
|
+
text-decoration: underline;
|
|
1887
|
+
text-underline-offset: 2px;
|
|
1888
|
+
}
|
|
1889
|
+
|
|
1890
|
+
.wabbit-chat-message-content a:hover {
|
|
1891
|
+
opacity: 0.8;
|
|
1892
|
+
}
|
|
1893
|
+
|
|
1894
|
+
.wabbit-chat-message-user .wabbit-chat-message-content a {
|
|
1895
|
+
color: inherit;
|
|
1896
|
+
}
|
|
1897
|
+
|
|
1799
1898
|
/* Typing Indicator */
|
|
1800
1899
|
.wabbit-chat-typing {
|
|
1801
1900
|
display: flex;
|
|
@@ -1908,6 +2007,35 @@
|
|
|
1908
2007
|
height: 20px;
|
|
1909
2008
|
}
|
|
1910
2009
|
|
|
2010
|
+
/* Connection Status Indicator */
|
|
2011
|
+
.wabbit-chat-status-indicator {
|
|
2012
|
+
width: 8px;
|
|
2013
|
+
height: 8px;
|
|
2014
|
+
border-radius: 50%;
|
|
2015
|
+
flex-shrink: 0;
|
|
2016
|
+
align-self: center;
|
|
2017
|
+
transition: background-color 0.3s ease;
|
|
2018
|
+
}
|
|
2019
|
+
|
|
2020
|
+
.wabbit-chat-status-indicator[data-status="connected"] {
|
|
2021
|
+
background-color: #22c55e;
|
|
2022
|
+
}
|
|
2023
|
+
|
|
2024
|
+
.wabbit-chat-status-indicator[data-status="disconnected"] {
|
|
2025
|
+
background-color: #ef4444;
|
|
2026
|
+
}
|
|
2027
|
+
|
|
2028
|
+
.wabbit-chat-status-indicator[data-status="connecting"],
|
|
2029
|
+
.wabbit-chat-status-indicator[data-status="reconnecting"] {
|
|
2030
|
+
background-color: #f59e0b;
|
|
2031
|
+
animation: wabbit-status-pulse 1.5s ease-in-out infinite;
|
|
2032
|
+
}
|
|
2033
|
+
|
|
2034
|
+
@keyframes wabbit-status-pulse {
|
|
2035
|
+
0%, 100% { opacity: 1; }
|
|
2036
|
+
50% { opacity: 0.3; }
|
|
2037
|
+
}
|
|
2038
|
+
|
|
1911
2039
|
/* Scrollbar */
|
|
1912
2040
|
.wabbit-chat-messages::-webkit-scrollbar {
|
|
1913
2041
|
width: 6px;
|
|
@@ -2031,7 +2159,7 @@
|
|
|
2031
2159
|
* Chat Widget - Main class that integrates all chat components
|
|
2032
2160
|
*/
|
|
2033
2161
|
class ChatWidget {
|
|
2034
|
-
constructor(config) {
|
|
2162
|
+
constructor(config, storage) {
|
|
2035
2163
|
this.wsClient = null;
|
|
2036
2164
|
this.bubble = null;
|
|
2037
2165
|
this.panel = null;
|
|
@@ -2041,6 +2169,7 @@
|
|
|
2041
2169
|
this.onChatReadyCallback = null;
|
|
2042
2170
|
this.chatReadyFired = false;
|
|
2043
2171
|
this.config = config;
|
|
2172
|
+
this.storage = storage;
|
|
2044
2173
|
this.onChatReadyCallback = config.onChatReady || null;
|
|
2045
2174
|
}
|
|
2046
2175
|
/**
|
|
@@ -2069,8 +2198,8 @@
|
|
|
2069
2198
|
this.setupThemeWatcher();
|
|
2070
2199
|
// Create WebSocket client
|
|
2071
2200
|
const wsUrl = this.getWebSocketUrl();
|
|
2072
|
-
this.wsClient = new ChatWebSocketClient(this.config.apiKey || '', wsUrl, null // Will use stored session if available
|
|
2073
|
-
);
|
|
2201
|
+
this.wsClient = new ChatWebSocketClient(this.config.apiKey || '', wsUrl, null, // Will use stored session if available
|
|
2202
|
+
this.storage);
|
|
2074
2203
|
// Set up event handlers
|
|
2075
2204
|
this.setupWebSocketHandlers();
|
|
2076
2205
|
// Only create bubble for widget mode (not inline)
|
|
@@ -2192,36 +2321,24 @@
|
|
|
2192
2321
|
this.wsClient.onError = (error) => {
|
|
2193
2322
|
console.error('[Wabbit] Chat error:', error);
|
|
2194
2323
|
if (this.panel) {
|
|
2195
|
-
// Clean up any active streaming messages on error
|
|
2196
2324
|
this.panel.cancelAllStreamingMessages();
|
|
2197
|
-
this.panel.addSystemMessage(`Error: ${error}`);
|
|
2198
2325
|
this.panel.setWaitingForResponse(false);
|
|
2199
2326
|
}
|
|
2200
2327
|
};
|
|
2201
2328
|
this.wsClient.onStatusChange = (status) => {
|
|
2202
2329
|
if (this.panel) {
|
|
2203
2330
|
this.panel.setDisabled(status !== 'connected');
|
|
2331
|
+
this.panel.setConnectionStatus(status);
|
|
2204
2332
|
}
|
|
2205
2333
|
};
|
|
2206
2334
|
this.wsClient.onDisconnect = () => {
|
|
2207
2335
|
if (this.panel) {
|
|
2208
|
-
// Clean up any active streaming messages on disconnect
|
|
2209
2336
|
this.panel.cancelAllStreamingMessages();
|
|
2210
|
-
// Check if there are queued messages
|
|
2211
|
-
const queueSize = this.wsClient.getQueueSize();
|
|
2212
|
-
if (queueSize > 0) {
|
|
2213
|
-
this.panel.addSystemMessage(`Disconnected from chat service. ${queueSize} message(s) will be sent when reconnected.`);
|
|
2214
|
-
}
|
|
2215
|
-
else {
|
|
2216
|
-
this.panel.addSystemMessage('Disconnected from chat service');
|
|
2217
|
-
}
|
|
2218
2337
|
this.panel.setDisabled(true);
|
|
2219
2338
|
}
|
|
2220
2339
|
};
|
|
2221
2340
|
this.wsClient.onQueueOverflow = (message) => {
|
|
2222
|
-
|
|
2223
|
-
this.panel.addSystemMessage(message);
|
|
2224
|
-
}
|
|
2341
|
+
console.warn('[Wabbit] Queue overflow:', message);
|
|
2225
2342
|
};
|
|
2226
2343
|
}
|
|
2227
2344
|
/**
|