@ihoomanai/chat-widget 3.0.14 → 3.0.16
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/cdn/SRI_HASHES.md +8 -8
- package/cdn/health.json +2 -2
- package/cdn/latest/chat.js +374 -86
- package/cdn/latest/chat.js.map +1 -1
- package/cdn/latest/chat.min.js +1 -1
- package/cdn/latest/chat.min.js.map +1 -1
- package/cdn/manifest.json +9 -9
- package/cdn/v3/chat.js +374 -86
- package/cdn/v3/chat.js.map +1 -1
- package/cdn/v3/chat.min.js +1 -1
- package/cdn/v3/chat.min.js.map +1 -1
- package/cdn/{v3.0.6 → v3.0.15}/chat.js +374 -86
- package/cdn/v3.0.15/chat.js.map +1 -0
- package/cdn/v3.0.15/chat.min.js +2 -0
- package/cdn/v3.0.15/chat.min.js.map +1 -0
- package/dist/index.cjs.js +34 -5
- package/dist/index.cjs.js.map +1 -1
- package/dist/index.esm.js +34 -5
- package/dist/index.esm.js.map +1 -1
- package/dist/index.esm.min.js +1 -1
- package/dist/index.esm.min.js.map +1 -1
- package/dist/index.umd.js +34 -5
- package/dist/index.umd.js.map +1 -1
- package/dist/index.umd.min.js +1 -1
- package/dist/index.umd.min.js.map +1 -1
- package/dist/widget.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/widget.ts +39 -4
- package/cdn/v3.0.6/chat.js.map +0 -1
- package/cdn/v3.0.6/chat.min.js +0 -2
- package/cdn/v3.0.6/chat.min.js.map +0 -1
package/cdn/latest/chat.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* @ihoomanai/chat-widget v3.0.
|
|
2
|
+
* @ihoomanai/chat-widget v3.0.15
|
|
3
3
|
* Universal chat support widget for any website - secure Widget ID based initialization
|
|
4
4
|
*
|
|
5
5
|
* @license MIT
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
* Enhanced with professional features
|
|
17
17
|
* @version 3.0.0
|
|
18
18
|
*/
|
|
19
|
-
const VERSION = '3.0.
|
|
19
|
+
const VERSION = '3.0.13';
|
|
20
20
|
const STORAGE_PREFIX = 'ihooman_chat_';
|
|
21
21
|
const DEFAULT_SERVER_URL = 'https://api.ihooman.ai';
|
|
22
22
|
const defaultConfig = {
|
|
@@ -168,6 +168,29 @@
|
|
|
168
168
|
}
|
|
169
169
|
}
|
|
170
170
|
}
|
|
171
|
+
/**
|
|
172
|
+
* Track an analytics event to the server
|
|
173
|
+
*/
|
|
174
|
+
async function trackAnalyticsEvent(eventType, metadata) {
|
|
175
|
+
if (!config.widgetId || !config.serverUrl)
|
|
176
|
+
return;
|
|
177
|
+
try {
|
|
178
|
+
const url = `${config.serverUrl}/widget-analytics/track?widget_id=${encodeURIComponent(config.widgetId)}`;
|
|
179
|
+
await fetch(url, {
|
|
180
|
+
method: 'POST',
|
|
181
|
+
headers: { 'Content-Type': 'application/json' },
|
|
182
|
+
body: JSON.stringify({
|
|
183
|
+
event_type: eventType,
|
|
184
|
+
session_id: state.sessionId,
|
|
185
|
+
visitor_id: state.visitorId,
|
|
186
|
+
metadata: metadata || {},
|
|
187
|
+
}),
|
|
188
|
+
});
|
|
189
|
+
}
|
|
190
|
+
catch (e) {
|
|
191
|
+
console.debug('[IhoomanChat] Failed to track event:', eventType, e);
|
|
192
|
+
}
|
|
193
|
+
}
|
|
171
194
|
function getCurrentScrollDepth() {
|
|
172
195
|
const scrollTop = window.scrollY || document.documentElement.scrollTop;
|
|
173
196
|
const scrollHeight = document.documentElement.scrollHeight - window.innerHeight;
|
|
@@ -198,9 +221,21 @@
|
|
|
198
221
|
const positionRight = config.position?.includes('right') ?? true;
|
|
199
222
|
const positionBottom = config.position?.includes('bottom') ?? true;
|
|
200
223
|
return `
|
|
201
|
-
|
|
224
|
+
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
|
|
225
|
+
:host { all: initial; }
|
|
202
226
|
.ihooman-widget { font-family: ${fontFamily}; font-size: 14px; line-height: 1.5; color: ${textColor}; -webkit-font-smoothing: antialiased; }
|
|
203
|
-
|
|
227
|
+
button {
|
|
228
|
+
font-family: inherit;
|
|
229
|
+
font-size: 100%;
|
|
230
|
+
line-height: inherit;
|
|
231
|
+
color: inherit;
|
|
232
|
+
margin: 0;
|
|
233
|
+
padding: 0;
|
|
234
|
+
background: transparent;
|
|
235
|
+
border: none;
|
|
236
|
+
cursor: pointer;
|
|
237
|
+
outline: none;
|
|
238
|
+
}
|
|
204
239
|
.ihooman-toggle { position: fixed !important; ${positionRight ? 'right: 20px' : 'left: 20px'}; ${positionBottom ? 'bottom: 20px' : 'top: 20px'}; width: ${buttonSize}px !important; height: ${buttonSize}px !important; border-radius: 50% !important; background: linear-gradient(135deg, ${gradientFrom}, ${gradientTo}) !important; border: none !important; cursor: pointer; z-index: ${zIndex}; display: flex !important; align-items: center; justify-content: center; box-shadow: 0 4px 20px rgba(0, 174, 255, 0.35); transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1); overflow: hidden; }
|
|
205
240
|
.ihooman-toggle:hover { transform: scale(1.08); box-shadow: 0 6px 28px rgba(0, 174, 255, 0.45); }
|
|
206
241
|
.ihooman-toggle:active { transform: scale(0.95); }
|
|
@@ -267,12 +302,7 @@
|
|
|
267
302
|
.ihooman-powered { text-align: center; padding: 8px; font-size: 11px; color: ${mutedColor}; background: ${bgColor}; }
|
|
268
303
|
.ihooman-powered a { color: ${primaryColor}; text-decoration: none; }
|
|
269
304
|
.ihooman-escalation-actions { display: flex; gap: 8px; margin-top: 10px; flex-wrap: wrap; }
|
|
270
|
-
.ihooman-escalation-btn { display: inline-flex; align-items: center; justify-content: center; gap: 6px; padding: 8px 14px; border-radius: 6px; border: none; cursor: pointer; font-family: inherit; font-size: 12px; font-weight: 500; transition: all 0.2s ease; line-height: 1.4; }
|
|
271
305
|
.ihooman-escalation-btn svg { width: 14px; height: 14px; flex-shrink: 0; }
|
|
272
|
-
.ihooman-escalation-btn.primary { background: linear-gradient(135deg, ${gradientFrom}, ${gradientTo}); color: white; }
|
|
273
|
-
.ihooman-escalation-btn.primary:hover { opacity: 0.9; }
|
|
274
|
-
.ihooman-escalation-btn.secondary { background: ${isDark ? 'rgba(255,255,255,0.1)' : 'rgba(0,0,0,0.05)'}; color: ${textColor}; border: 1px solid ${borderColor}; }
|
|
275
|
-
.ihooman-escalation-btn.secondary:hover { background: ${isDark ? 'rgba(255,255,255,0.15)' : 'rgba(0,0,0,0.08)'}; }
|
|
276
306
|
.ihooman-status-bar { padding: 10px 16px; text-align: center; font-size: 13px; display: none; }
|
|
277
307
|
.ihooman-status-bar.show { display: block; }
|
|
278
308
|
.ihooman-status-bar.waiting { background: #fef3c7; color: #92400e; }
|
|
@@ -308,9 +338,7 @@
|
|
|
308
338
|
.ihooman-history-empty { padding: 40px; text-align: center; color: ${mutedColor}; font-size: 14px; }
|
|
309
339
|
.ihooman-preset-questions { padding: 10px 16px; display: flex; flex-wrap: wrap; gap: 6px; background: ${bgColor}; border-top: 1px solid ${borderColor}; }
|
|
310
340
|
.ihooman-preset-questions:empty { display: none; }
|
|
311
|
-
.ihooman-preset-questions.hidden { display: none
|
|
312
|
-
.ihooman-preset-btn { display: inline-flex; align-items: center; justify-content: center; gap: 4px; padding: 6px 10px; border-radius: 6px; border: 1px solid ${borderColor}; background: ${isDark ? 'rgba(255,255,255,0.05)' : 'rgba(0,0,0,0.02)'}; color: ${textColor}; font-size: 12px; font-weight: 500; cursor: pointer; transition: all 0.2s; white-space: nowrap; line-height: 1.4; }
|
|
313
|
-
.ihooman-preset-btn:hover { background: ${isDark ? 'rgba(255,255,255,0.1)' : 'rgba(0,0,0,0.05)'}; border-color: ${primaryColor}; }
|
|
341
|
+
.ihooman-preset-questions.hidden { display: none; }
|
|
314
342
|
.ihooman-proactive-toast { position: fixed; ${positionRight ? 'right: 20px' : 'left: 20px'}; ${positionBottom ? 'bottom: 90px' : 'top: 90px'}; max-width: 300px; padding: 16px; background: ${bgColor}; border-radius: 12px; box-shadow: 0 10px 40px rgba(0,0,0,0.15); z-index: ${(zIndex ?? 9999) - 2}; opacity: 0; visibility: hidden; transform: translateY(10px); transition: all 0.3s ease; border: 1px solid ${borderColor}; }
|
|
315
343
|
.ihooman-proactive-toast.show { opacity: 1; visibility: visible; transform: translateY(0); }
|
|
316
344
|
.ihooman-proactive-toast-content { font-size: 14px; color: ${textColor}; margin-bottom: 12px; }
|
|
@@ -333,9 +361,6 @@
|
|
|
333
361
|
.ihooman-survey-skip { display: inline-flex; align-items: center; justify-content: center; padding: 8px 16px; background: transparent; color: ${mutedColor}; border: none; font-size: 12px; cursor: pointer; line-height: 1.4; }
|
|
334
362
|
.ihooman-survey-skip:hover { color: ${textColor}; }
|
|
335
363
|
.ihooman-feedback-btns { display: flex; gap: 6px; margin-top: 6px; }
|
|
336
|
-
.ihooman-feedback-btn { display: inline-flex; align-items: center; justify-content: center; padding: 4px 6px; border: 1px solid ${borderColor}; border-radius: 4px; background: transparent; cursor: pointer; color: ${mutedColor}; transition: all 0.2s; }
|
|
337
|
-
.ihooman-feedback-btn:hover { background: ${isDark ? 'rgba(255,255,255,0.1)' : 'rgba(0,0,0,0.05)'}; }
|
|
338
|
-
.ihooman-feedback-btn.active { background: ${primaryColor}; color: white; border-color: ${primaryColor}; }
|
|
339
364
|
.ihooman-feedback-btn svg { width: 12px; height: 12px; }
|
|
340
365
|
.ihooman-carousel { display: flex; gap: 12px; overflow-x: auto; padding: 8px 0; scroll-snap-type: x mandatory; }
|
|
341
366
|
.ihooman-carousel::-webkit-scrollbar { height: 4px; }
|
|
@@ -345,24 +370,30 @@
|
|
|
345
370
|
.ihooman-carousel-card-title { font-size: 14px; font-weight: 600; color: ${textColor}; margin-bottom: 4px; }
|
|
346
371
|
.ihooman-carousel-card-desc { font-size: 12px; color: ${mutedColor}; margin-bottom: 8px; }
|
|
347
372
|
.ihooman-carousel-card-btns { display: flex; flex-direction: column; gap: 6px; }
|
|
348
|
-
.ihooman-carousel-card-btn { display: flex; align-items: center; justify-content: center; padding: 6px 10px; border: 1px solid ${borderColor}; border-radius: 6px; background: transparent; color: ${textColor}; font-size: 11px; cursor: pointer; transition: all 0.2s; text-align: center; line-height: 1.4; }
|
|
349
|
-
.ihooman-carousel-card-btn:hover { background: ${isDark ? 'rgba(255,255,255,0.1)' : 'rgba(0,0,0,0.05)'}; border-color: ${primaryColor}; }
|
|
350
373
|
.ihooman-quick-replies { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 8px; }
|
|
351
|
-
.ihooman-quick-reply { display: inline-flex; align-items: center; justify-content: center; padding: 5px 10px; border: 1px solid ${primaryColor}; border-radius: 14px; background: transparent; color: ${primaryColor}; font-size: 11px; cursor: pointer; transition: all 0.2s; line-height: 1.4; }
|
|
352
|
-
.ihooman-quick-reply:hover { background: ${primaryColor}; color: white; }
|
|
353
374
|
@media (max-width: 480px) { .ihooman-window { width: calc(100vw - 20px); height: calc(100vh - 100px); min-height: 400px; max-height: calc(100vh - 100px); left: 10px; right: 10px; bottom: 80px; } .ihooman-toggle { ${positionRight ? 'right: 16px' : 'left: 16px'}; bottom: 16px; } .ihooman-proactive-toast { left: 10px; right: 10px; max-width: none; } }
|
|
354
375
|
`;
|
|
355
376
|
}
|
|
356
377
|
// ============================================================================
|
|
357
378
|
// DOM CREATION
|
|
358
379
|
// ============================================================================
|
|
380
|
+
let shadowRoot = null;
|
|
359
381
|
function createWidget() {
|
|
382
|
+
// Create host element
|
|
383
|
+
const host = document.createElement('div');
|
|
384
|
+
host.id = 'ihooman-chat-widget-host';
|
|
385
|
+
host.style.cssText = 'all: initial !important; position: fixed !important; z-index: 2147483647 !important; pointer-events: none !important;';
|
|
386
|
+
document.body.appendChild(host);
|
|
387
|
+
// Create Shadow DOM for style isolation
|
|
388
|
+
shadowRoot = host.attachShadow({ mode: 'open' });
|
|
389
|
+
// Add styles inside shadow DOM
|
|
360
390
|
const styleEl = document.createElement('style');
|
|
361
391
|
styleEl.id = 'ihooman-widget-styles';
|
|
362
392
|
styleEl.textContent = generateStyles();
|
|
363
|
-
|
|
393
|
+
shadowRoot.appendChild(styleEl);
|
|
364
394
|
const widget = document.createElement('div');
|
|
365
395
|
widget.className = 'ihooman-widget';
|
|
396
|
+
widget.style.cssText = 'pointer-events: auto;';
|
|
366
397
|
widget.innerHTML = `
|
|
367
398
|
<button class="ihooman-toggle" aria-label="Open chat">
|
|
368
399
|
<span class="ihooman-pulse"></span>
|
|
@@ -430,7 +461,7 @@
|
|
|
430
461
|
</div>
|
|
431
462
|
</div>
|
|
432
463
|
`;
|
|
433
|
-
|
|
464
|
+
shadowRoot.appendChild(widget);
|
|
434
465
|
elements = {
|
|
435
466
|
widget,
|
|
436
467
|
toggle: widget.querySelector('.ihooman-toggle'),
|
|
@@ -502,6 +533,11 @@
|
|
|
502
533
|
const proactiveSecondaryBtn = elements.proactiveToast?.querySelector('.ihooman-proactive-toast-btn.secondary');
|
|
503
534
|
if (proactivePrimaryBtn) {
|
|
504
535
|
proactivePrimaryBtn.addEventListener('click', () => {
|
|
536
|
+
// Track click for the most recently shown proactive message
|
|
537
|
+
const lastShownId = shownProactiveIds[shownProactiveIds.length - 1];
|
|
538
|
+
if (lastShownId) {
|
|
539
|
+
trackAnalyticsEvent('proactive_clicked', { message_id: lastShownId });
|
|
540
|
+
}
|
|
505
541
|
hideProactiveToast();
|
|
506
542
|
open();
|
|
507
543
|
emit('proactive:clicked');
|
|
@@ -883,7 +919,8 @@
|
|
|
883
919
|
if (response.ok) {
|
|
884
920
|
const data = await response.json();
|
|
885
921
|
if (data.escalated) {
|
|
886
|
-
|
|
922
|
+
// Check both ticket_status and conversation_status for agent handling
|
|
923
|
+
if (data.ticket_status === 'in_progress' || data.conversation_status === 'active') {
|
|
887
924
|
updateStatusBar('connected', '🟢 Connected to live agent');
|
|
888
925
|
}
|
|
889
926
|
else if (data.ticket_status === 'open') {
|
|
@@ -948,6 +985,14 @@
|
|
|
948
985
|
comment,
|
|
949
986
|
}),
|
|
950
987
|
});
|
|
988
|
+
// Track survey submission analytics
|
|
989
|
+
trackAnalyticsEvent('survey_submitted', {
|
|
990
|
+
survey_id: config.surveyConfig?.id,
|
|
991
|
+
rating,
|
|
992
|
+
comment: comment || null,
|
|
993
|
+
session_id: state.sessionId,
|
|
994
|
+
visitor_id: state.visitorId,
|
|
995
|
+
});
|
|
951
996
|
// Mark survey as completed for this session
|
|
952
997
|
storage('survey_completed_' + state.sessionId, true);
|
|
953
998
|
emit('survey:submitted', { rating, comment });
|
|
@@ -971,6 +1016,12 @@
|
|
|
971
1016
|
// Don't show if not enough messages (at least 2 exchanges)
|
|
972
1017
|
if (state.messages.length < 4)
|
|
973
1018
|
return;
|
|
1019
|
+
// Track survey displayed analytics
|
|
1020
|
+
trackAnalyticsEvent('survey_displayed', {
|
|
1021
|
+
survey_id: config.surveyConfig.id,
|
|
1022
|
+
session_id: state.sessionId,
|
|
1023
|
+
visitor_id: state.visitorId,
|
|
1024
|
+
});
|
|
974
1025
|
// Show the survey
|
|
975
1026
|
showView('survey');
|
|
976
1027
|
emit('survey:shown', config.surveyConfig);
|
|
@@ -983,6 +1034,13 @@
|
|
|
983
1034
|
console.warn('[IhoomanChat] No survey configured');
|
|
984
1035
|
return;
|
|
985
1036
|
}
|
|
1037
|
+
// Track survey displayed analytics
|
|
1038
|
+
trackAnalyticsEvent('survey_displayed', {
|
|
1039
|
+
survey_id: config.surveyConfig.id,
|
|
1040
|
+
session_id: state.sessionId,
|
|
1041
|
+
visitor_id: state.visitorId,
|
|
1042
|
+
trigger: 'manual',
|
|
1043
|
+
});
|
|
986
1044
|
showView('survey');
|
|
987
1045
|
emit('survey:shown', config.surveyConfig);
|
|
988
1046
|
}
|
|
@@ -1014,6 +1072,12 @@
|
|
|
1014
1072
|
if (elements.sendBtn)
|
|
1015
1073
|
elements.sendBtn.disabled = true;
|
|
1016
1074
|
addMessage(content, 'user');
|
|
1075
|
+
// Track message sent analytics
|
|
1076
|
+
trackAnalyticsEvent('message_sent', {
|
|
1077
|
+
session_id: state.sessionId,
|
|
1078
|
+
visitor_id: state.visitorId,
|
|
1079
|
+
message_length: content.length,
|
|
1080
|
+
});
|
|
1017
1081
|
showTyping();
|
|
1018
1082
|
sendMessageToServer(content);
|
|
1019
1083
|
}
|
|
@@ -1051,6 +1115,14 @@
|
|
|
1051
1115
|
escalation_offered: data.escalation_offered,
|
|
1052
1116
|
quick_replies: data.quick_replies,
|
|
1053
1117
|
});
|
|
1118
|
+
// Track message received analytics
|
|
1119
|
+
trackAnalyticsEvent('message_received', {
|
|
1120
|
+
session_id: state.sessionId,
|
|
1121
|
+
visitor_id: state.visitorId,
|
|
1122
|
+
message_length: data.response.length,
|
|
1123
|
+
has_sources: !!data.sources,
|
|
1124
|
+
confidence: data.confidence,
|
|
1125
|
+
});
|
|
1054
1126
|
}
|
|
1055
1127
|
}
|
|
1056
1128
|
catch (error) {
|
|
@@ -1084,13 +1156,38 @@
|
|
|
1084
1156
|
});
|
|
1085
1157
|
target.value = '';
|
|
1086
1158
|
}
|
|
1159
|
+
// Notification sound using Web Audio API for reliable cross-browser playback
|
|
1087
1160
|
function playSound() {
|
|
1161
|
+
if (state.soundMuted)
|
|
1162
|
+
return;
|
|
1088
1163
|
try {
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1164
|
+
// Use Web Audio API for more reliable sound generation
|
|
1165
|
+
const AudioContext = window.AudioContext || window.webkitAudioContext;
|
|
1166
|
+
if (!AudioContext)
|
|
1167
|
+
return;
|
|
1168
|
+
const audioCtx = new AudioContext();
|
|
1169
|
+
const oscillator = audioCtx.createOscillator();
|
|
1170
|
+
const gainNode = audioCtx.createGain();
|
|
1171
|
+
oscillator.connect(gainNode);
|
|
1172
|
+
gainNode.connect(audioCtx.destination);
|
|
1173
|
+
// Create a pleasant notification tone (two-tone chime)
|
|
1174
|
+
oscillator.frequency.setValueAtTime(880, audioCtx.currentTime); // A5
|
|
1175
|
+
oscillator.frequency.setValueAtTime(1108.73, audioCtx.currentTime + 0.1); // C#6
|
|
1176
|
+
// Envelope for smooth sound
|
|
1177
|
+
gainNode.gain.setValueAtTime(0, audioCtx.currentTime);
|
|
1178
|
+
gainNode.gain.linearRampToValueAtTime(0.3, audioCtx.currentTime + 0.01);
|
|
1179
|
+
gainNode.gain.linearRampToValueAtTime(0.2, audioCtx.currentTime + 0.1);
|
|
1180
|
+
gainNode.gain.linearRampToValueAtTime(0, audioCtx.currentTime + 0.3);
|
|
1181
|
+
oscillator.start(audioCtx.currentTime);
|
|
1182
|
+
oscillator.stop(audioCtx.currentTime + 0.3);
|
|
1183
|
+
// Clean up
|
|
1184
|
+
oscillator.onended = () => {
|
|
1185
|
+
audioCtx.close().catch(() => { });
|
|
1186
|
+
};
|
|
1187
|
+
}
|
|
1188
|
+
catch (e) {
|
|
1189
|
+
console.debug('[IhoomanChat] Sound playback failed:', e);
|
|
1092
1190
|
}
|
|
1093
|
-
catch { /* ignore */ }
|
|
1094
1191
|
}
|
|
1095
1192
|
// ============================================================================
|
|
1096
1193
|
// PROACTIVE MESSAGES
|
|
@@ -1171,6 +1268,8 @@
|
|
|
1171
1268
|
proactiveCooldowns[pm.id] = Date.now();
|
|
1172
1269
|
storage('shown_proactive', shownProactiveIds);
|
|
1173
1270
|
storage('proactive_cooldowns', proactiveCooldowns);
|
|
1271
|
+
// Track analytics event
|
|
1272
|
+
trackAnalyticsEvent('proactive_displayed', { message_id: pm.id });
|
|
1174
1273
|
// Show toast
|
|
1175
1274
|
if (elements.proactiveToast) {
|
|
1176
1275
|
const content = elements.proactiveToast.querySelector('.ihooman-proactive-toast-content');
|
|
@@ -1285,12 +1384,35 @@
|
|
|
1285
1384
|
else if (data.type === 'message') {
|
|
1286
1385
|
hideTyping();
|
|
1287
1386
|
const sender = data.sender === 'user' ? 'user' : 'bot';
|
|
1387
|
+
// Check if this is an agent-related system message
|
|
1388
|
+
const metadata = data.metadata || {};
|
|
1389
|
+
if (metadata.agent_accepted) {
|
|
1390
|
+
// Agent has accepted the escalation - update status
|
|
1391
|
+
isLiveAgentMode = true;
|
|
1392
|
+
updateStatusBar('connected', '🟢 Connected to live agent');
|
|
1393
|
+
emit('escalation:accepted', { type: 'live_agent' });
|
|
1394
|
+
}
|
|
1395
|
+
if (metadata.closed_by_agent) {
|
|
1396
|
+
// Agent has closed the conversation - return to AI mode
|
|
1397
|
+
isLiveAgentMode = false;
|
|
1398
|
+
stopLiveAgentPolling();
|
|
1399
|
+
updateStatusBar('hidden');
|
|
1400
|
+
emit('escalation:end', { type: 'live_agent' });
|
|
1401
|
+
}
|
|
1402
|
+
// Track message received analytics
|
|
1403
|
+
trackAnalyticsEvent('message_received', {
|
|
1404
|
+
session_id: state.sessionId,
|
|
1405
|
+
visitor_id: state.visitorId,
|
|
1406
|
+
sender: data.sender,
|
|
1407
|
+
is_system_message: metadata.is_system_message || false,
|
|
1408
|
+
});
|
|
1288
1409
|
addMessage(data.content, sender, {
|
|
1289
1410
|
confidence: data.confidence,
|
|
1290
1411
|
agent_name: data.agent_name,
|
|
1291
1412
|
escalation_offered: data.escalation_offered,
|
|
1292
1413
|
escalated: data.escalated,
|
|
1293
1414
|
quick_replies: data.quick_replies,
|
|
1415
|
+
...metadata,
|
|
1294
1416
|
});
|
|
1295
1417
|
}
|
|
1296
1418
|
else if (data.type === 'typing') {
|
|
@@ -1299,6 +1421,20 @@
|
|
|
1299
1421
|
else if (data.type === 'pong') {
|
|
1300
1422
|
// Heartbeat response
|
|
1301
1423
|
}
|
|
1424
|
+
else if (data.type === 'agent_status') {
|
|
1425
|
+
// Handle agent status updates
|
|
1426
|
+
if (data.status === 'connected') {
|
|
1427
|
+
isLiveAgentMode = true;
|
|
1428
|
+
updateStatusBar('connected', '🟢 Connected to live agent');
|
|
1429
|
+
emit('escalation:accepted', { type: 'live_agent' });
|
|
1430
|
+
}
|
|
1431
|
+
else if (data.status === 'disconnected') {
|
|
1432
|
+
isLiveAgentMode = false;
|
|
1433
|
+
stopLiveAgentPolling();
|
|
1434
|
+
updateStatusBar('hidden');
|
|
1435
|
+
emit('escalation:end', { type: 'live_agent' });
|
|
1436
|
+
}
|
|
1437
|
+
}
|
|
1302
1438
|
else if (data.type === 'error') {
|
|
1303
1439
|
console.error('WebSocket server error:', data.message);
|
|
1304
1440
|
emit('error', { message: data.message });
|
|
@@ -1370,6 +1506,8 @@
|
|
|
1370
1506
|
elements.window.classList.add('open');
|
|
1371
1507
|
hideProactiveToast();
|
|
1372
1508
|
setTimeout(() => elements.input?.focus(), 300);
|
|
1509
|
+
// Track widget open event
|
|
1510
|
+
trackAnalyticsEvent('widget_open');
|
|
1373
1511
|
emit('open');
|
|
1374
1512
|
}
|
|
1375
1513
|
function close() {
|
|
@@ -1380,6 +1518,8 @@
|
|
|
1380
1518
|
elements.toggle.classList.remove('open');
|
|
1381
1519
|
if (elements.window)
|
|
1382
1520
|
elements.window.classList.remove('open');
|
|
1521
|
+
// Track widget close event
|
|
1522
|
+
trackAnalyticsEvent('widget_close');
|
|
1383
1523
|
// Check if we should show survey after closing (if conversation had enough messages)
|
|
1384
1524
|
if (config.surveyConfig && state.messages.length >= 4) {
|
|
1385
1525
|
const surveyCompleted = state.sessionId && storage('survey_completed_' + state.sessionId);
|
|
@@ -1524,85 +1664,228 @@
|
|
|
1524
1664
|
default: return 500;
|
|
1525
1665
|
}
|
|
1526
1666
|
}
|
|
1667
|
+
// Default button styles - MUST match client-dashboard/src/components/widget/ButtonStylingEditor.tsx
|
|
1668
|
+
const DEFAULT_BUTTON_STYLES = {
|
|
1669
|
+
primary: {
|
|
1670
|
+
backgroundColor: '#00aeff',
|
|
1671
|
+
textColor: '#ffffff',
|
|
1672
|
+
borderColor: 'transparent',
|
|
1673
|
+
borderWidth: 0,
|
|
1674
|
+
borderRadius: 8,
|
|
1675
|
+
paddingVertical: 8,
|
|
1676
|
+
paddingHorizontal: 16,
|
|
1677
|
+
fontSize: 13,
|
|
1678
|
+
fontWeight: 'medium',
|
|
1679
|
+
},
|
|
1680
|
+
secondary: {
|
|
1681
|
+
backgroundColor: 'transparent',
|
|
1682
|
+
textColor: '#6b7280',
|
|
1683
|
+
borderColor: '#e5e7eb',
|
|
1684
|
+
borderWidth: 1,
|
|
1685
|
+
borderRadius: 8,
|
|
1686
|
+
paddingVertical: 8,
|
|
1687
|
+
paddingHorizontal: 16,
|
|
1688
|
+
fontSize: 13,
|
|
1689
|
+
fontWeight: 'medium',
|
|
1690
|
+
},
|
|
1691
|
+
presetQuestions: {
|
|
1692
|
+
backgroundColor: 'rgba(0, 174, 255, 0.1)',
|
|
1693
|
+
textColor: '#00aeff',
|
|
1694
|
+
borderColor: 'rgba(0, 174, 255, 0.2)',
|
|
1695
|
+
borderWidth: 1,
|
|
1696
|
+
borderRadius: 20,
|
|
1697
|
+
paddingVertical: 6,
|
|
1698
|
+
paddingHorizontal: 12,
|
|
1699
|
+
fontSize: 12,
|
|
1700
|
+
fontWeight: 'medium',
|
|
1701
|
+
},
|
|
1702
|
+
quickReplies: {
|
|
1703
|
+
backgroundColor: '#f3f4f6',
|
|
1704
|
+
textColor: '#374151',
|
|
1705
|
+
borderColor: '#e5e7eb',
|
|
1706
|
+
borderWidth: 1,
|
|
1707
|
+
borderRadius: 16,
|
|
1708
|
+
paddingVertical: 5,
|
|
1709
|
+
paddingHorizontal: 10,
|
|
1710
|
+
fontSize: 11,
|
|
1711
|
+
fontWeight: 'medium',
|
|
1712
|
+
},
|
|
1713
|
+
feedback: {
|
|
1714
|
+
backgroundColor: 'transparent',
|
|
1715
|
+
textColor: '#9ca3af',
|
|
1716
|
+
borderColor: '#e5e7eb',
|
|
1717
|
+
borderWidth: 1,
|
|
1718
|
+
borderRadius: 6,
|
|
1719
|
+
paddingVertical: 4,
|
|
1720
|
+
paddingHorizontal: 8,
|
|
1721
|
+
fontSize: 12,
|
|
1722
|
+
fontWeight: 'medium',
|
|
1723
|
+
},
|
|
1724
|
+
headerActions: {
|
|
1725
|
+
backgroundColor: 'rgba(255, 255, 255, 0.1)',
|
|
1726
|
+
textColor: '#ffffff',
|
|
1727
|
+
borderColor: 'transparent',
|
|
1728
|
+
borderWidth: 0,
|
|
1729
|
+
borderRadius: 6,
|
|
1730
|
+
paddingVertical: 6,
|
|
1731
|
+
paddingHorizontal: 6,
|
|
1732
|
+
fontSize: 14,
|
|
1733
|
+
fontWeight: 'medium',
|
|
1734
|
+
},
|
|
1735
|
+
escalation: {
|
|
1736
|
+
backgroundColor: '#00aeff',
|
|
1737
|
+
textColor: '#ffffff',
|
|
1738
|
+
borderColor: 'transparent',
|
|
1739
|
+
borderWidth: 0,
|
|
1740
|
+
borderRadius: 8,
|
|
1741
|
+
paddingVertical: 8,
|
|
1742
|
+
paddingHorizontal: 14,
|
|
1743
|
+
fontSize: 12,
|
|
1744
|
+
fontWeight: 'medium',
|
|
1745
|
+
},
|
|
1746
|
+
cardActions: {
|
|
1747
|
+
backgroundColor: '#00aeff',
|
|
1748
|
+
textColor: '#ffffff',
|
|
1749
|
+
borderColor: 'transparent',
|
|
1750
|
+
borderWidth: 0,
|
|
1751
|
+
borderRadius: 6,
|
|
1752
|
+
paddingVertical: 6,
|
|
1753
|
+
paddingHorizontal: 12,
|
|
1754
|
+
fontSize: 12,
|
|
1755
|
+
fontWeight: 'medium',
|
|
1756
|
+
},
|
|
1757
|
+
toggleButton: {
|
|
1758
|
+
backgroundColor: '#00aeff',
|
|
1759
|
+
textColor: '#ffffff',
|
|
1760
|
+
borderColor: 'transparent',
|
|
1761
|
+
borderWidth: 0,
|
|
1762
|
+
borderRadius: 30,
|
|
1763
|
+
paddingVertical: 0,
|
|
1764
|
+
paddingHorizontal: 0,
|
|
1765
|
+
fontSize: 14,
|
|
1766
|
+
fontWeight: 'medium',
|
|
1767
|
+
},
|
|
1768
|
+
attachButton: {
|
|
1769
|
+
backgroundColor: 'transparent',
|
|
1770
|
+
textColor: '#6b7280',
|
|
1771
|
+
borderColor: 'transparent',
|
|
1772
|
+
borderWidth: 0,
|
|
1773
|
+
borderRadius: 8,
|
|
1774
|
+
paddingVertical: 6,
|
|
1775
|
+
paddingHorizontal: 6,
|
|
1776
|
+
fontSize: 14,
|
|
1777
|
+
fontWeight: 'medium',
|
|
1778
|
+
},
|
|
1779
|
+
};
|
|
1527
1780
|
function applyButtonStyling(buttonStyling) {
|
|
1528
1781
|
const styleId = 'ihooman-button-styling';
|
|
1529
|
-
|
|
1782
|
+
// Get the shadow root or fall back to document.head
|
|
1783
|
+
const styleContainer = shadowRoot || document.head;
|
|
1784
|
+
let styleEl = styleContainer.querySelector(`#${styleId}`);
|
|
1530
1785
|
if (!styleEl) {
|
|
1531
1786
|
styleEl = document.createElement('style');
|
|
1532
1787
|
styleEl.id = styleId;
|
|
1533
|
-
|
|
1534
|
-
}
|
|
1788
|
+
styleContainer.appendChild(styleEl);
|
|
1789
|
+
}
|
|
1790
|
+
// Merge provided styles with defaults
|
|
1791
|
+
const mergedStyles = {
|
|
1792
|
+
primary: { ...DEFAULT_BUTTON_STYLES.primary, ...(buttonStyling?.primary || {}) },
|
|
1793
|
+
secondary: { ...DEFAULT_BUTTON_STYLES.secondary, ...(buttonStyling?.secondary || {}) },
|
|
1794
|
+
presetQuestions: { ...DEFAULT_BUTTON_STYLES.presetQuestions, ...(buttonStyling?.presetQuestions || {}) },
|
|
1795
|
+
quickReplies: { ...DEFAULT_BUTTON_STYLES.quickReplies, ...(buttonStyling?.quickReplies || {}) },
|
|
1796
|
+
feedback: { ...DEFAULT_BUTTON_STYLES.feedback, ...(buttonStyling?.feedback || {}) },
|
|
1797
|
+
headerActions: { ...DEFAULT_BUTTON_STYLES.headerActions, ...(buttonStyling?.headerActions || {}) },
|
|
1798
|
+
escalation: { ...DEFAULT_BUTTON_STYLES.escalation, ...(buttonStyling?.escalation || {}) },
|
|
1799
|
+
cardActions: { ...DEFAULT_BUTTON_STYLES.cardActions, ...(buttonStyling?.cardActions || {}) },
|
|
1800
|
+
toggleButton: { ...DEFAULT_BUTTON_STYLES.toggleButton, ...(buttonStyling?.toggleButton || {}) },
|
|
1801
|
+
attachButton: { ...DEFAULT_BUTTON_STYLES.attachButton, ...(buttonStyling?.attachButton || {}) },
|
|
1802
|
+
};
|
|
1535
1803
|
const generateButtonCSS = (selector, style) => {
|
|
1536
|
-
|
|
1537
|
-
|
|
1538
|
-
const
|
|
1539
|
-
const
|
|
1540
|
-
const
|
|
1541
|
-
const
|
|
1542
|
-
const
|
|
1543
|
-
const
|
|
1544
|
-
const
|
|
1545
|
-
|
|
1546
|
-
const fontWeight = getFontWeightValue(style.fontWeight || 'medium');
|
|
1547
|
-
// Use 'background' instead of 'background-color' to override gradients
|
|
1804
|
+
const bg = style.backgroundColor;
|
|
1805
|
+
const color = style.textColor;
|
|
1806
|
+
const borderColor = style.borderColor;
|
|
1807
|
+
const borderWidth = style.borderWidth;
|
|
1808
|
+
const borderRadius = style.borderRadius;
|
|
1809
|
+
const paddingV = style.paddingVertical;
|
|
1810
|
+
const paddingH = style.paddingHorizontal;
|
|
1811
|
+
const fontSize = style.fontSize;
|
|
1812
|
+
const fontWeight = getFontWeightValue(style.fontWeight);
|
|
1813
|
+
// Shadow DOM provides isolation, so we don't need !important or high specificity
|
|
1548
1814
|
return `
|
|
1549
|
-
|
|
1550
|
-
|
|
1551
|
-
|
|
1552
|
-
border: ${borderWidth}px solid ${borderColor}
|
|
1553
|
-
border-radius: ${borderRadius}px
|
|
1554
|
-
padding: ${paddingV}px ${paddingH}px
|
|
1555
|
-
|
|
1556
|
-
font-
|
|
1557
|
-
|
|
1558
|
-
|
|
1559
|
-
width:
|
|
1560
|
-
height:
|
|
1561
|
-
|
|
1562
|
-
|
|
1563
|
-
|
|
1815
|
+
${selector} {
|
|
1816
|
+
background: ${bg};
|
|
1817
|
+
color: ${color};
|
|
1818
|
+
border: ${borderWidth}px solid ${borderColor};
|
|
1819
|
+
border-radius: ${borderRadius}px;
|
|
1820
|
+
padding: ${paddingV}px ${paddingH}px;
|
|
1821
|
+
margin: 0;
|
|
1822
|
+
font-family: inherit;
|
|
1823
|
+
font-size: ${fontSize}px;
|
|
1824
|
+
font-weight: ${fontWeight};
|
|
1825
|
+
min-width: unset;
|
|
1826
|
+
min-height: unset;
|
|
1827
|
+
width: auto;
|
|
1828
|
+
height: auto;
|
|
1829
|
+
max-width: none;
|
|
1830
|
+
max-height: none;
|
|
1831
|
+
display: inline-flex;
|
|
1832
|
+
align-items: center;
|
|
1833
|
+
justify-content: center;
|
|
1834
|
+
gap: 6px;
|
|
1835
|
+
line-height: 1.4;
|
|
1836
|
+
text-transform: none;
|
|
1837
|
+
letter-spacing: normal;
|
|
1838
|
+
box-shadow: none;
|
|
1839
|
+
text-decoration: none;
|
|
1840
|
+
white-space: nowrap;
|
|
1841
|
+
box-sizing: border-box;
|
|
1842
|
+
cursor: pointer;
|
|
1843
|
+
transition: all 0.2s ease;
|
|
1564
1844
|
}
|
|
1565
1845
|
`;
|
|
1566
1846
|
};
|
|
1567
1847
|
let css = '';
|
|
1568
1848
|
// Primary buttons (send, submit, etc)
|
|
1569
|
-
|
|
1570
|
-
|
|
1571
|
-
|
|
1572
|
-
|
|
1573
|
-
|
|
1849
|
+
css += generateButtonCSS('.ihooman-input-btn.send', mergedStyles.primary);
|
|
1850
|
+
css += generateButtonCSS('.ihooman-proactive-toast-btn.primary', mergedStyles.primary);
|
|
1851
|
+
css += generateButtonCSS('.ihooman-survey-submit', mergedStyles.primary);
|
|
1852
|
+
css += generateButtonCSS('.ihooman-ticket-submit', mergedStyles.primary);
|
|
1853
|
+
css += generateButtonCSS('.ihooman-history-new', mergedStyles.primary);
|
|
1574
1854
|
// Secondary buttons
|
|
1575
|
-
|
|
1576
|
-
|
|
1577
|
-
|
|
1578
|
-
css += generateButtonCSS('.ihooman-ticket-back', buttonStyling.secondary);
|
|
1579
|
-
}
|
|
1855
|
+
css += generateButtonCSS('.ihooman-proactive-toast-btn.secondary', mergedStyles.secondary);
|
|
1856
|
+
css += generateButtonCSS('.ihooman-survey-skip', mergedStyles.secondary);
|
|
1857
|
+
css += generateButtonCSS('.ihooman-ticket-back', mergedStyles.secondary);
|
|
1580
1858
|
// Preset question buttons
|
|
1581
|
-
|
|
1582
|
-
css += generateButtonCSS('.ihooman-preset-btn', buttonStyling.presetQuestions);
|
|
1583
|
-
}
|
|
1859
|
+
css += generateButtonCSS('.ihooman-preset-btn', mergedStyles.presetQuestions);
|
|
1584
1860
|
// Quick reply buttons
|
|
1585
|
-
|
|
1586
|
-
css += generateButtonCSS('.ihooman-quick-reply', buttonStyling.quickReplies);
|
|
1587
|
-
}
|
|
1861
|
+
css += generateButtonCSS('.ihooman-quick-reply', mergedStyles.quickReplies);
|
|
1588
1862
|
// Feedback buttons (thumbs up/down)
|
|
1589
|
-
|
|
1590
|
-
css += generateButtonCSS('.ihooman-feedback-btn', buttonStyling.feedback);
|
|
1591
|
-
}
|
|
1863
|
+
css += generateButtonCSS('.ihooman-feedback-btn', mergedStyles.feedback);
|
|
1592
1864
|
// Header action buttons
|
|
1593
|
-
|
|
1594
|
-
css += generateButtonCSS('.ihooman-header-btn', buttonStyling.headerActions);
|
|
1595
|
-
}
|
|
1865
|
+
css += generateButtonCSS('.ihooman-header-btn', mergedStyles.headerActions);
|
|
1596
1866
|
// Escalation buttons (Talk to Agent, Create Ticket)
|
|
1597
|
-
|
|
1598
|
-
|
|
1599
|
-
|
|
1600
|
-
css += generateButtonCSS('.ihooman-escalation-btn.secondary', buttonStyling.escalation);
|
|
1601
|
-
}
|
|
1867
|
+
css += generateButtonCSS('.ihooman-escalation-btn', mergedStyles.escalation);
|
|
1868
|
+
css += generateButtonCSS('.ihooman-escalation-btn.primary', mergedStyles.escalation);
|
|
1869
|
+
css += generateButtonCSS('.ihooman-escalation-btn.secondary', mergedStyles.secondary);
|
|
1602
1870
|
// Card action buttons
|
|
1603
|
-
|
|
1604
|
-
|
|
1605
|
-
|
|
1871
|
+
css += generateButtonCSS('.ihooman-carousel-card-btn', mergedStyles.cardActions);
|
|
1872
|
+
// Toggle button (floating chat bubble) - special handling for circular button
|
|
1873
|
+
const toggleStyle = mergedStyles.toggleButton;
|
|
1874
|
+
const toggleBg = toggleStyle.backgroundColor;
|
|
1875
|
+
const toggleColor = toggleStyle.textColor;
|
|
1876
|
+
const toggleBorderColor = toggleStyle.borderColor;
|
|
1877
|
+
const toggleBorderWidth = toggleStyle.borderWidth;
|
|
1878
|
+
const toggleBorderRadius = toggleStyle.borderRadius;
|
|
1879
|
+
css += `
|
|
1880
|
+
.ihooman-toggle {
|
|
1881
|
+
background: linear-gradient(135deg, ${toggleBg}, ${toggleBg}) !important;
|
|
1882
|
+
color: ${toggleColor};
|
|
1883
|
+
border: ${toggleBorderWidth}px solid ${toggleBorderColor};
|
|
1884
|
+
border-radius: ${toggleBorderRadius}px;
|
|
1885
|
+
}
|
|
1886
|
+
`;
|
|
1887
|
+
// Attach button (file attachment)
|
|
1888
|
+
css += generateButtonCSS('.ihooman-input-btn.attach', mergedStyles.attachButton);
|
|
1606
1889
|
console.debug('[IhoomanChat] Applied button styling CSS:', css.length, 'chars');
|
|
1607
1890
|
styleEl.textContent = css;
|
|
1608
1891
|
}
|
|
@@ -1705,6 +1988,10 @@
|
|
|
1705
1988
|
if (configResponse.success && configResponse.config) {
|
|
1706
1989
|
applyServerConfig(configResponse.config);
|
|
1707
1990
|
chatEndpoint = configResponse.chatEndpoint;
|
|
1991
|
+
// Update serverUrl with apiBaseUrl from server response
|
|
1992
|
+
if (configResponse.apiBaseUrl) {
|
|
1993
|
+
config.serverUrl = configResponse.apiBaseUrl;
|
|
1994
|
+
}
|
|
1708
1995
|
}
|
|
1709
1996
|
else if (configResponse.error) {
|
|
1710
1997
|
console.warn('IhoomanChat: Could not fetch server config:', configResponse.error);
|
|
@@ -1713,6 +2000,7 @@
|
|
|
1713
2000
|
}
|
|
1714
2001
|
}
|
|
1715
2002
|
createWidget();
|
|
2003
|
+
applyButtonStyling(config.buttonStyling);
|
|
1716
2004
|
renderPresetQuestions();
|
|
1717
2005
|
state.messages.forEach((msg) => addMessage(msg.content, msg.sender === 'user' ? 'user' : 'bot', msg.metadata));
|
|
1718
2006
|
if (state.messages.length === 0 && config.welcomeMessage) {
|