@ihoomanai/chat-widget 2.5.4 → 2.5.6

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/src/widget.ts CHANGED
@@ -439,7 +439,7 @@ function createWidget(): void {
439
439
  <div class="ihooman-history-view">
440
440
  <div class="ihooman-history-header">
441
441
  <span class="ihooman-history-title">Your Conversations</span>
442
- <button class="ihooman-history-new" style="display: inline-flex; align-items: center; gap: 4px; background: linear-gradient(135deg, ${config.gradientFrom}, ${config.gradientTo}); color: white; border: none; padding: 6px 12px; border-radius: 6px; font-size: 12px; font-weight: 500; cursor: pointer;">${icons.plus} New Chat</button>
442
+ <button class="ihooman-history-new" style="all: revert; display: inline-flex !important; align-items: center !important; gap: 4px !important; background: linear-gradient(135deg, ${config.gradientFrom}, ${config.gradientTo}) !important; color: white !important; border: none !important; padding: 6px 10px !important; border-radius: 6px !important; font-size: 12px !important; font-weight: 500 !important; cursor: pointer !important; width: auto !important; height: auto !important; min-width: 0 !important; aspect-ratio: auto !important;">${icons.plus} New Chat</button>
443
443
  </div>
444
444
  <div class="ihooman-history-list"></div>
445
445
  </div>
@@ -561,13 +561,13 @@ function addMessage(content: string, sender: 'user' | 'bot' = 'bot', metadata: M
561
561
 
562
562
  let escalationButtonsHtml = '';
563
563
  if (showEscalationButtons) {
564
- // Use inline styles to ensure buttons are rectangular and properly sized
565
- const btnBaseStyle = 'display: inline-flex; align-items: center; gap: 6px; padding: 8px 14px; border-radius: 6px; border: none; cursor: pointer; font-size: 12px; font-weight: 500; transition: all 0.2s ease; width: auto; height: auto;';
566
- const primaryStyle = `${btnBaseStyle} background: linear-gradient(135deg, ${config.gradientFrom}, ${config.gradientTo}); color: white;`;
567
- const secondaryStyle = `${btnBaseStyle} background: rgba(0,0,0,0.05); color: inherit; border: 1px solid rgba(0,0,0,0.1);`;
564
+ // Use inline styles with !important to override any external CSS
565
+ const btnBaseStyle = 'all: revert; display: inline-flex !important; align-items: center !important; justify-content: center !important; gap: 6px !important; padding: 8px 12px !important; border-radius: 6px !important; border: none !important; cursor: pointer !important; font-size: 12px !important; font-weight: 500 !important; line-height: 1.2 !important; width: auto !important; height: auto !important; min-width: 0 !important; min-height: 0 !important; max-width: none !important; max-height: none !important; aspect-ratio: auto !important; box-sizing: border-box !important;';
566
+ const primaryStyle = `${btnBaseStyle} background: linear-gradient(135deg, ${config.gradientFrom}, ${config.gradientTo}) !important; color: white !important;`;
567
+ const secondaryStyle = `${btnBaseStyle} background: rgba(0,0,0,0.05) !important; color: inherit !important; border: 1px solid rgba(0,0,0,0.1) !important;`;
568
568
 
569
569
  escalationButtonsHtml = `
570
- <div class="ihooman-escalation-actions">
570
+ <div class="ihooman-escalation-actions" style="display: flex !important; gap: 8px !important; margin-top: 10px !important; flex-wrap: wrap !important;">
571
571
  <button class="ihooman-escalation-btn primary" data-action="live-agent" style="${primaryStyle}">
572
572
  ${icons.agent}
573
573
  <span>Talk to Agent</span>
@@ -1198,6 +1198,9 @@ function connectWebSocket(chatEndpoint?: string): void {
1198
1198
  }
1199
1199
 
1200
1200
  try {
1201
+ // Stop any existing heartbeat before creating new connection
1202
+ stopHeartbeat();
1203
+
1201
1204
  ws = new WebSocket(wsUrl);
1202
1205
 
1203
1206
  ws.onopen = () => {
@@ -1205,23 +1208,35 @@ function connectWebSocket(chatEndpoint?: string): void {
1205
1208
  reconnectAttempts = 0;
1206
1209
  updateStatus(true);
1207
1210
  emit('connected');
1211
+ // Start heartbeat after connection is established
1212
+ startHeartbeat();
1208
1213
  };
1209
1214
 
1210
- ws.onclose = () => {
1215
+ ws.onclose = (event) => {
1211
1216
  state.isConnected = false;
1212
1217
  updateStatus(false);
1218
+ stopHeartbeat();
1213
1219
  emit('disconnected');
1214
1220
 
1221
+ // Log close reason for debugging
1222
+ console.log(`WebSocket closed: code=${event.code}, reason=${event.reason || 'none'}, wasClean=${event.wasClean}`);
1223
+
1215
1224
  // Don't reconnect if this was an intentional disconnect (e.g., starting new conversation)
1216
1225
  if (intentionalDisconnect) {
1217
1226
  intentionalDisconnect = false;
1218
1227
  return;
1219
1228
  }
1220
1229
 
1230
+ // Don't reconnect if the widget is closed
1231
+ if (!state.isOpen) {
1232
+ return;
1233
+ }
1234
+
1221
1235
  // Attempt reconnection with exponential backoff
1222
1236
  if (reconnectAttempts < maxReconnectAttempts) {
1223
1237
  reconnectAttempts++;
1224
1238
  const delay = Math.min(1000 * Math.pow(2, reconnectAttempts), 30000);
1239
+ console.log(`WebSocket reconnecting in ${delay}ms (attempt ${reconnectAttempts}/${maxReconnectAttempts})`);
1225
1240
  setTimeout(() => connectWebSocket(chatEndpoint), delay);
1226
1241
  } else {
1227
1242
  // Fall back to polling after max reconnect attempts
@@ -1275,9 +1290,6 @@ function connectWebSocket(chatEndpoint?: string): void {
1275
1290
  }
1276
1291
  };
1277
1292
 
1278
- // Start heartbeat to keep connection alive
1279
- startHeartbeat();
1280
-
1281
1293
  } catch {
1282
1294
  // WebSocket not supported, fall back to polling
1283
1295
  console.warn('WebSocket not supported, using polling');
@@ -1292,15 +1304,21 @@ let heartbeatInterval: ReturnType<typeof setInterval> | null = null;
1292
1304
 
1293
1305
  /**
1294
1306
  * Start heartbeat to keep WebSocket connection alive
1307
+ * Sends ping every 25 seconds to prevent Cloudflare/proxy timeouts
1295
1308
  */
1296
1309
  function startHeartbeat(): void {
1297
- if (heartbeatInterval) return;
1310
+ // Clear any existing heartbeat first
1311
+ stopHeartbeat();
1298
1312
 
1299
1313
  heartbeatInterval = setInterval(() => {
1300
1314
  if (ws && ws.readyState === WebSocket.OPEN) {
1301
- ws.send(JSON.stringify({ type: 'ping' }));
1315
+ try {
1316
+ ws.send(JSON.stringify({ type: 'ping' }));
1317
+ } catch (e) {
1318
+ console.warn('Failed to send heartbeat ping:', e);
1319
+ }
1302
1320
  }
1303
- }, 30000); // Ping every 30 seconds
1321
+ }, 25000); // Ping every 25 seconds (Cloudflare timeout is 100s, but some proxies are shorter)
1304
1322
  }
1305
1323
 
1306
1324
  /**