wakz-chat-widget 2.0.0 → 2.2.0
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 +51 -24
- package/index.js +305 -413
- package/package.json +13 -6
package/index.js
CHANGED
|
@@ -1,11 +1,10 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* WAKZ Chat Widget
|
|
2
|
+
* WAKZ Chat Widget v2.2.0
|
|
3
3
|
* ─────────────────────────────────────────────────────────────────
|
|
4
4
|
* A production-grade, self-contained chat widget using Shadow DOM.
|
|
5
|
-
*
|
|
5
|
+
* Competes with Intercom & Crisp in quality.
|
|
6
6
|
*
|
|
7
7
|
* Embed: <script src="/wakz-widget.js" data-api-key="xxx" data-server="https://..." async></script>
|
|
8
|
-
* New attrs: data-icon-color="#171717" data-icon-shape="circle"
|
|
9
8
|
*
|
|
10
9
|
* ZERO external dependencies — pure vanilla JavaScript.
|
|
11
10
|
*/
|
|
@@ -51,9 +50,7 @@
|
|
|
51
50
|
if (!target && scripts.length > 0) target = scripts[scripts.length - 1];
|
|
52
51
|
return {
|
|
53
52
|
apiKey: (target && target.getAttribute('data-api-key')) || '',
|
|
54
|
-
server: (target && target.getAttribute('data-server')) || ''
|
|
55
|
-
iconColor: (target && target.getAttribute('data-icon-color')) || '',
|
|
56
|
-
iconShape: (target && target.getAttribute('data-icon-shape')) || ''
|
|
53
|
+
server: (target && target.getAttribute('data-server')) || ''
|
|
57
54
|
};
|
|
58
55
|
}
|
|
59
56
|
|
|
@@ -82,6 +79,46 @@
|
|
|
82
79
|
return el;
|
|
83
80
|
}
|
|
84
81
|
|
|
82
|
+
/** Detect device model, platform, and type using userAgentData API (Chrome 90+) with UA fallback */
|
|
83
|
+
function _getDeviceInfo() {
|
|
84
|
+
var info = {
|
|
85
|
+
device_model: '',
|
|
86
|
+
device_platform: '',
|
|
87
|
+
device_type: 'desktop'
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
// Try userAgentData API (Chrome 90+, Edge, Samsung Browser — NOT Safari/Firefox)
|
|
91
|
+
if (navigator.userAgentData) {
|
|
92
|
+
info.device_model = navigator.userAgentData.model || '';
|
|
93
|
+
info.device_platform = navigator.userAgentData.platform || '';
|
|
94
|
+
if (navigator.userAgentData.mobile === true) {
|
|
95
|
+
info.device_type = 'mobile';
|
|
96
|
+
} else if (navigator.userAgentData.mobile === false && info.device_platform === 'Android') {
|
|
97
|
+
info.device_type = 'tablet';
|
|
98
|
+
} else {
|
|
99
|
+
info.device_type = 'desktop';
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// Fallback: parse from user agent string
|
|
104
|
+
var ua = navigator.userAgent || '';
|
|
105
|
+
if (!info.device_platform) {
|
|
106
|
+
if (/iPhone|iPad|iPod/i.test(ua)) info.device_platform = 'iOS';
|
|
107
|
+
else if (/Android/i.test(ua)) info.device_platform = 'Android';
|
|
108
|
+
else if (/Windows/i.test(ua)) info.device_platform = 'Windows';
|
|
109
|
+
else if (/Mac OS X/i.test(ua)) info.device_platform = 'macOS';
|
|
110
|
+
else if (/CrOS/i.test(ua)) info.device_platform = 'Chrome OS';
|
|
111
|
+
else if (/Linux/i.test(ua)) info.device_platform = 'Linux';
|
|
112
|
+
}
|
|
113
|
+
if (info.device_type === 'desktop') {
|
|
114
|
+
if (/iPad/i.test(ua)) info.device_type = 'tablet';
|
|
115
|
+
else if (/Mobile|iPhone|iPod/i.test(ua)) info.device_type = 'mobile';
|
|
116
|
+
else if (/Android(?!.*Mobile)/i.test(ua)) info.device_type = 'tablet';
|
|
117
|
+
else if (/Android/i.test(ua)) info.device_type = 'mobile';
|
|
118
|
+
}
|
|
119
|
+
return info;
|
|
120
|
+
}
|
|
121
|
+
|
|
85
122
|
/** Fetch with an AbortController timeout (default 30s) */
|
|
86
123
|
function _fetchWithTimeout(url, options, timeoutMs) {
|
|
87
124
|
var controller = new AbortController();
|
|
@@ -95,21 +132,12 @@
|
|
|
95
132
|
════════════════════════════════════════════════════════════════ */
|
|
96
133
|
|
|
97
134
|
var _ICONS = {
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
'<svg viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">' +
|
|
101
|
-
'<rect x="3" y="3" width="18" height="18" rx="5" ry="5" stroke="currentColor" stroke-width="1.8" fill="none"/>' +
|
|
102
|
-
'<line x1="8" y1="9" x2="16" y2="9" stroke="currentColor" stroke-width="1.8" stroke-linecap="round"/>' +
|
|
103
|
-
'<line x1="8" y1="12.5" x2="14" y2="12.5" stroke="currentColor" stroke-width="1.8" stroke-linecap="round"/>' +
|
|
104
|
-
'<line x1="8" y1="16" x2="12" y2="16" stroke="currentColor" stroke-width="1.8" stroke-linecap="round"/>' +
|
|
105
|
-
'</svg>',
|
|
106
|
-
/* Send arrow */
|
|
135
|
+
chatBubble:
|
|
136
|
+
'<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><path d="M21 11.5a8.38 8.38 0 0 1-.9 3.8 8.5 8.5 0 0 1-7.6 4.7 8.38 8.38 0 0 1-3.8-.9L3 21l1.9-5.7a8.38 8.38 0 0 1-.9-3.8 8.5 8.5 0 0 1 4.7-7.6 8.38 8.38 0 0 1 3.8-.9h.5a8.48 8.48 0 0 1 8 8v.5z"/></svg>',
|
|
107
137
|
send:
|
|
108
138
|
'<svg viewBox="0 0 24 24" fill="currentColor"><path d="M2.01 21L23 12 2.01 3 2 10l15 2-15 2z"/></svg>',
|
|
109
|
-
/* Close X */
|
|
110
139
|
close:
|
|
111
|
-
'<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><line x1="18" y1="6" x2="6" y2="18"/><line x1="6" y1="6" x2="18" y2="18"/></svg>',
|
|
112
|
-
/* Error circle */
|
|
140
|
+
'<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><line x1="18" y1="6" x2="6" y2="18"/><line x1="6" y1="6" x2="18" y2="18"/></svg>',
|
|
113
141
|
error:
|
|
114
142
|
'<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><line x1="15" y1="9" x2="9" y2="15"/><line x1="9" y1="9" x2="15" y2="15"/></svg>'
|
|
115
143
|
};
|
|
@@ -169,12 +197,11 @@
|
|
|
169
197
|
botName: 'WAKZ',
|
|
170
198
|
welcomeMessage: '',
|
|
171
199
|
primaryColor: '#171717',
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
chatBg: '#FAFAFA',
|
|
200
|
+
chatBg: '#f8f9fa',
|
|
201
|
+
btnColor: '#171717',
|
|
175
202
|
widgetBg: '#ffffff',
|
|
176
203
|
position: 'bottom-right',
|
|
177
|
-
language: '
|
|
204
|
+
language: 'en',
|
|
178
205
|
showStatus: true,
|
|
179
206
|
online: true
|
|
180
207
|
};
|
|
@@ -195,8 +222,7 @@
|
|
|
195
222
|
self.apiKey = attrs.apiKey;
|
|
196
223
|
self.server = attrs.server;
|
|
197
224
|
self.visitorId = _getVisitorId();
|
|
198
|
-
self.
|
|
199
|
-
self._scriptIconShape = attrs.iconShape;
|
|
225
|
+
self._deviceInfo = _getDeviceInfo(); // Device model, platform, type — collected once
|
|
200
226
|
|
|
201
227
|
/* ── Runtime state ── */
|
|
202
228
|
self.config = Object.assign({}, _DEFAULTS);
|
|
@@ -211,12 +237,12 @@
|
|
|
211
237
|
self._lastPollTime = null;
|
|
212
238
|
self._POLL_INTERVAL = 4000; /* Poll every 4 seconds */
|
|
213
239
|
self._knownMessageIds = {}; /* Deduplication set for message IDs */
|
|
240
|
+
self._pendingReply = false; /* True while waiting for API response — pauses polling */
|
|
214
241
|
|
|
215
242
|
/* ── DOM refs (populated after mount) ── */
|
|
216
243
|
self._host = null;
|
|
217
244
|
self._shadow = null;
|
|
218
245
|
self._root = null;
|
|
219
|
-
self._overlayEl = null;
|
|
220
246
|
self._chatWindow = null;
|
|
221
247
|
self._messagesContainer = null;
|
|
222
248
|
self._inputEl = null;
|
|
@@ -235,7 +261,7 @@
|
|
|
235
261
|
}
|
|
236
262
|
|
|
237
263
|
/* ════════════════════════════════════════════════════════════════
|
|
238
|
-
CSS — Complete
|
|
264
|
+
CSS — Complete styling injected into Shadow DOM
|
|
239
265
|
════════════════════════════════════════════════════════════════ */
|
|
240
266
|
|
|
241
267
|
WAKZWidget.prototype._injectCSS = function () {
|
|
@@ -246,14 +272,18 @@
|
|
|
246
272
|
/* ── CSS Custom Properties (theming) ── */
|
|
247
273
|
':host {',
|
|
248
274
|
' --wakz-primary: #171717;',
|
|
249
|
-
' --wakz-
|
|
250
|
-
' --wakz-chat-bg: #
|
|
275
|
+
' --wakz-btn: #171717;',
|
|
276
|
+
' --wakz-chat-bg: #f8f9fa;',
|
|
251
277
|
' --wakz-widget-bg: #ffffff;',
|
|
252
|
-
' --wakz-radius:
|
|
253
|
-
' --wakz-radius-bubble:
|
|
278
|
+
' --wakz-radius-window: 16px;',
|
|
279
|
+
' --wakz-radius-bubble: 16px;',
|
|
280
|
+
' --wakz-radius-fab: 28px;',
|
|
281
|
+
' --wakz-shadow-sm: 0 1px 3px rgba(0,0,0,.08), 0 1px 2px rgba(0,0,0,.06);',
|
|
282
|
+
' --wakz-shadow-md: 0 4px 12px rgba(0,0,0,.1), 0 2px 4px rgba(0,0,0,.06);',
|
|
283
|
+
' --wakz-shadow-lg: 0 12px 40px rgba(0,0,0,.15), 0 4px 12px rgba(0,0,0,.1);',
|
|
284
|
+
' --wakz-shadow-xl: 0 20px 60px rgba(0,0,0,.2), 0 8px 20px rgba(0,0,0,.12);',
|
|
285
|
+
' --wakz-transition: 200ms ease;',
|
|
254
286
|
' --wakz-font: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;',
|
|
255
|
-
' --wakz-shadow: 0 25px 60px rgba(0,0,0,0.15), 0 8px 20px rgba(0,0,0,0.1);',
|
|
256
|
-
' --wakz-shadow-sm: 0 1px 3px rgba(0,0,0,0.06), 0 1px 2px rgba(0,0,0,0.04);',
|
|
257
287
|
' all: initial;',
|
|
258
288
|
' font-family: var(--wakz-font);',
|
|
259
289
|
'}',
|
|
@@ -271,46 +301,34 @@
|
|
|
271
301
|
'.wakz-fab {',
|
|
272
302
|
' position: fixed;',
|
|
273
303
|
' z-index: 2147483647;',
|
|
274
|
-
' width:
|
|
275
|
-
' height:
|
|
276
|
-
' border-radius:
|
|
304
|
+
' width: 56px;',
|
|
305
|
+
' height: 56px;',
|
|
306
|
+
' border-radius: var(--wakz-radius-fab);',
|
|
277
307
|
' border: none;',
|
|
278
308
|
' cursor: pointer;',
|
|
279
309
|
' display: flex;',
|
|
280
310
|
' align-items: center;',
|
|
281
311
|
' justify-content: center;',
|
|
282
|
-
' box-shadow:
|
|
283
|
-
' transition:
|
|
312
|
+
' box-shadow: var(--wakz-shadow-lg);',
|
|
313
|
+
' transition: transform var(--wakz-transition), box-shadow var(--wakz-transition);',
|
|
284
314
|
' outline: none;',
|
|
285
315
|
' -webkit-tap-highlight-color: transparent;',
|
|
286
|
-
' background: var(--wakz-
|
|
316
|
+
' background: var(--wakz-btn);',
|
|
287
317
|
'}',
|
|
288
318
|
'.wakz-fab:hover {',
|
|
289
319
|
' transform: scale(1.08);',
|
|
290
|
-
' box-shadow:
|
|
320
|
+
' box-shadow: var(--wakz-shadow-xl);',
|
|
291
321
|
'}',
|
|
292
322
|
'.wakz-fab:active {',
|
|
293
323
|
' transform: scale(0.96);',
|
|
294
324
|
'}',
|
|
295
325
|
'.wakz-fab svg {',
|
|
296
|
-
' width:
|
|
297
|
-
' height:
|
|
326
|
+
' width: 26px;',
|
|
327
|
+
' height: 26px;',
|
|
298
328
|
' color: #ffffff;',
|
|
299
329
|
' pointer-events: none;',
|
|
300
330
|
'}',
|
|
301
331
|
|
|
302
|
-
/* ── FAB Shape: rounded square ── */
|
|
303
|
-
'.wakz-fab-shape-rounded {',
|
|
304
|
-
' border-radius: 16px;',
|
|
305
|
-
'}',
|
|
306
|
-
|
|
307
|
-
/* ── FAB hidden when chat is open ── */
|
|
308
|
-
'.wakz-fab-hidden {',
|
|
309
|
-
' opacity: 0;',
|
|
310
|
-
' pointer-events: none;',
|
|
311
|
-
' transform: scale(0.8);',
|
|
312
|
-
'}',
|
|
313
|
-
|
|
314
332
|
/* ── FAB Positioning ── */
|
|
315
333
|
'.wakz-fab-pos-br { bottom: 24px; right: 24px; }',
|
|
316
334
|
'.wakz-fab-pos-bl { bottom: 24px; left: 24px; }',
|
|
@@ -331,18 +349,18 @@
|
|
|
331
349
|
══════════════════════════════════════════════════ */
|
|
332
350
|
'.wakz-fab-dot {',
|
|
333
351
|
' position: absolute;',
|
|
334
|
-
' top:
|
|
335
|
-
' right:
|
|
336
|
-
' width:
|
|
337
|
-
' height:
|
|
352
|
+
' top: 0px;',
|
|
353
|
+
' right: 0px;',
|
|
354
|
+
' width: 14px;',
|
|
355
|
+
' height: 14px;',
|
|
338
356
|
' border-radius: 50%;',
|
|
339
|
-
' border:
|
|
357
|
+
' border: 2.5px solid #ffffff;',
|
|
340
358
|
' z-index: 2;',
|
|
341
359
|
' transition: background 0.3s ease;',
|
|
342
360
|
'}',
|
|
343
361
|
'.wakz-fab-pos-bl .wakz-fab-dot {',
|
|
344
362
|
' right: auto;',
|
|
345
|
-
' left:
|
|
363
|
+
' left: 0px;',
|
|
346
364
|
'}',
|
|
347
365
|
|
|
348
366
|
/* ── Online (green pulse) ── */
|
|
@@ -363,60 +381,45 @@
|
|
|
363
381
|
' animation: none;',
|
|
364
382
|
'}',
|
|
365
383
|
|
|
366
|
-
/*
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
'
|
|
370
|
-
' position: fixed;',
|
|
371
|
-
' top: 0;',
|
|
372
|
-
' left: 0;',
|
|
373
|
-
' right: 0;',
|
|
374
|
-
' bottom: 0;',
|
|
375
|
-
' z-index: 2147483645;',
|
|
376
|
-
' background: rgba(0,0,0,0.4);',
|
|
377
|
-
' -webkit-backdrop-filter: blur(4px);',
|
|
378
|
-
' backdrop-filter: blur(4px);',
|
|
379
|
-
' opacity: 0;',
|
|
380
|
-
' pointer-events: none;',
|
|
381
|
-
' transition: opacity 0.3s cubic-bezier(0.16,1,0.3,1);',
|
|
382
|
-
'}',
|
|
383
|
-
'.wakz-overlay.wakz-visible {',
|
|
384
|
-
' opacity: 1;',
|
|
385
|
-
' pointer-events: auto;',
|
|
384
|
+
/* ── Error (red, no pulse) ── */
|
|
385
|
+
'.wakz-fab-dot.error {',
|
|
386
|
+
' background: #ef4444;',
|
|
387
|
+
' animation: none;',
|
|
386
388
|
'}',
|
|
387
389
|
|
|
388
390
|
/* ══════════════════════════════════════════════════
|
|
389
|
-
CHAT WINDOW
|
|
391
|
+
CHAT WINDOW
|
|
390
392
|
══════════════════════════════════════════════════ */
|
|
391
393
|
'.wakz-window {',
|
|
392
394
|
' position: fixed;',
|
|
393
395
|
' z-index: 2147483646;',
|
|
394
|
-
'
|
|
395
|
-
'
|
|
396
|
-
'
|
|
397
|
-
'
|
|
398
|
-
' max-height: 75vh;',
|
|
399
|
-
' border-radius: 20px;',
|
|
396
|
+
' width: 380px;',
|
|
397
|
+
' min-height: 500px;',
|
|
398
|
+
' max-height: 70vh;',
|
|
399
|
+
' border-radius: var(--wakz-radius-window);',
|
|
400
400
|
' overflow: hidden;',
|
|
401
401
|
' display: flex;',
|
|
402
402
|
' flex-direction: column;',
|
|
403
403
|
' background: var(--wakz-widget-bg);',
|
|
404
|
-
'
|
|
405
|
-
' box-shadow: 0 25px 60px rgba(0,0,0,0.15), 0 8px 20px rgba(0,0,0,0.1);',
|
|
406
|
-
' -webkit-backdrop-filter: blur(20px);',
|
|
407
|
-
' backdrop-filter: blur(20px);',
|
|
404
|
+
' box-shadow: var(--wakz-shadow-xl);',
|
|
408
405
|
' opacity: 0;',
|
|
406
|
+
' transform: translateY(20px) scale(0.95);',
|
|
409
407
|
' pointer-events: none;',
|
|
410
|
-
' transition: opacity 0.3s cubic-bezier(0.
|
|
408
|
+
' transition: opacity 0.3s cubic-bezier(0.4, 0, 0.2, 1),',
|
|
409
|
+
' transform 0.3s cubic-bezier(0.4, 0, 0.2, 1);',
|
|
411
410
|
'}',
|
|
412
411
|
'.wakz-window.wakz-visible {',
|
|
413
412
|
' opacity: 1;',
|
|
414
|
-
' transform:
|
|
413
|
+
' transform: translateY(0) scale(1);',
|
|
415
414
|
' pointer-events: auto;',
|
|
416
415
|
'}',
|
|
417
416
|
|
|
417
|
+
/* ── Window Positioning ── */
|
|
418
|
+
'.wakz-win-pos-br { bottom: 92px; right: 24px; }',
|
|
419
|
+
'.wakz-win-pos-bl { bottom: 92px; left: 24px; }',
|
|
420
|
+
|
|
418
421
|
/* ══════════════════════════════════════════════════
|
|
419
|
-
HEADER
|
|
422
|
+
HEADER
|
|
420
423
|
══════════════════════════════════════════════════ */
|
|
421
424
|
'.wakz-hdr {',
|
|
422
425
|
' flex-shrink: 0;',
|
|
@@ -424,35 +427,30 @@
|
|
|
424
427
|
' display: flex;',
|
|
425
428
|
' align-items: center;',
|
|
426
429
|
' justify-content: space-between;',
|
|
427
|
-
' color: #
|
|
430
|
+
' color: #ffffff;',
|
|
428
431
|
' cursor: default;',
|
|
429
432
|
' user-select: none;',
|
|
430
|
-
' background:
|
|
431
|
-
' -webkit-backdrop-filter: blur(20px);',
|
|
432
|
-
' backdrop-filter: blur(20px);',
|
|
433
|
-
' border-bottom: 1px solid rgba(0,0,0,0.06);',
|
|
434
|
-
' height: 60px;',
|
|
433
|
+
' background: var(--wakz-primary);',
|
|
435
434
|
'}',
|
|
436
|
-
'.wakz-hdr-
|
|
435
|
+
'.wakz-hdr-left {',
|
|
437
436
|
' display: flex;',
|
|
438
437
|
' align-items: center;',
|
|
439
438
|
' gap: 11px;',
|
|
440
439
|
' min-width: 0;',
|
|
441
440
|
'}',
|
|
442
441
|
|
|
443
|
-
/* ── Bot Avatar
|
|
442
|
+
/* ── Bot Avatar ── */
|
|
444
443
|
'.wakz-avatar {',
|
|
445
|
-
' width:
|
|
446
|
-
' height:
|
|
444
|
+
' width: 38px;',
|
|
445
|
+
' height: 38px;',
|
|
447
446
|
' border-radius: 50%;',
|
|
448
|
-
' background:
|
|
447
|
+
' background: rgba(255,255,255,0.18);',
|
|
449
448
|
' display: flex;',
|
|
450
449
|
' align-items: center;',
|
|
451
450
|
' justify-content: center;',
|
|
452
451
|
' font-weight: 700;',
|
|
453
|
-
' font-size:
|
|
452
|
+
' font-size: 16px;',
|
|
454
453
|
' flex-shrink: 0;',
|
|
455
|
-
' color: #ffffff;',
|
|
456
454
|
' letter-spacing: 0.5px;',
|
|
457
455
|
'}',
|
|
458
456
|
|
|
@@ -463,27 +461,20 @@
|
|
|
463
461
|
' min-width: 0;',
|
|
464
462
|
'}',
|
|
465
463
|
'.wakz-hdr-name {',
|
|
466
|
-
' font-size:
|
|
464
|
+
' font-size: 15px;',
|
|
467
465
|
' font-weight: 600;',
|
|
468
466
|
' line-height: 1.3;',
|
|
469
|
-
' color: #171717;',
|
|
470
467
|
' white-space: nowrap;',
|
|
471
468
|
' overflow: hidden;',
|
|
472
469
|
' text-overflow: ellipsis;',
|
|
473
470
|
'}',
|
|
474
|
-
|
|
475
|
-
/* ── Header Left: status + close ── */
|
|
476
|
-
'.wakz-hdr-left {',
|
|
477
|
-
' display: flex;',
|
|
478
|
-
' align-items: center;',
|
|
479
|
-
' gap: 10px;',
|
|
480
|
-
'}',
|
|
481
471
|
'.wakz-hdr-status {',
|
|
482
472
|
' font-size: 12px;',
|
|
483
|
-
'
|
|
473
|
+
' opacity: 0.9;',
|
|
484
474
|
' display: flex;',
|
|
485
475
|
' align-items: center;',
|
|
486
476
|
' gap: 5px;',
|
|
477
|
+
' margin-top: 2px;',
|
|
487
478
|
'}',
|
|
488
479
|
'.wakz-hdr-status-dot {',
|
|
489
480
|
' width: 7px;',
|
|
@@ -493,8 +484,8 @@
|
|
|
493
484
|
' flex-shrink: 0;',
|
|
494
485
|
' transition: background 0.3s ease;',
|
|
495
486
|
'}',
|
|
496
|
-
'.wakz-hdr-status-dot.online { background: #
|
|
497
|
-
'.wakz-hdr-status-dot.offline { background: #
|
|
487
|
+
'.wakz-hdr-status-dot.online { background: #4ade80; }',
|
|
488
|
+
'.wakz-hdr-status-dot.offline { background: #f87171; }',
|
|
498
489
|
|
|
499
490
|
/* ── Close Button ── */
|
|
500
491
|
'.wakz-close {',
|
|
@@ -502,18 +493,18 @@
|
|
|
502
493
|
' height: 32px;',
|
|
503
494
|
' border-radius: 50%;',
|
|
504
495
|
' border: none;',
|
|
505
|
-
' background:
|
|
506
|
-
' color: #
|
|
496
|
+
' background: rgba(255,255,255,0.12);',
|
|
497
|
+
' color: #ffffff;',
|
|
507
498
|
' cursor: pointer;',
|
|
508
499
|
' display: flex;',
|
|
509
500
|
' align-items: center;',
|
|
510
501
|
' justify-content: center;',
|
|
511
|
-
' transition: background
|
|
502
|
+
' transition: background var(--wakz-transition);',
|
|
512
503
|
' outline: none;',
|
|
513
504
|
' flex-shrink: 0;',
|
|
514
505
|
'}',
|
|
515
506
|
'.wakz-close:hover {',
|
|
516
|
-
' background: rgba(
|
|
507
|
+
' background: rgba(255,255,255,0.28);',
|
|
517
508
|
'}',
|
|
518
509
|
'.wakz-close svg {',
|
|
519
510
|
' width: 18px;',
|
|
@@ -527,130 +518,90 @@
|
|
|
527
518
|
' flex: 1;',
|
|
528
519
|
' overflow-y: auto;',
|
|
529
520
|
' overflow-x: hidden;',
|
|
530
|
-
' padding: 16px;',
|
|
521
|
+
' padding: 18px 16px 10px;',
|
|
531
522
|
' display: flex;',
|
|
532
523
|
' flex-direction: column;',
|
|
533
|
-
' gap:
|
|
524
|
+
' gap: 6px;',
|
|
534
525
|
' scroll-behavior: smooth;',
|
|
535
526
|
' background: var(--wakz-chat-bg);',
|
|
536
527
|
'}',
|
|
537
528
|
'.wakz-msgs::-webkit-scrollbar { width: 5px; }',
|
|
538
529
|
'.wakz-msgs::-webkit-scrollbar-track { background: transparent; }',
|
|
539
530
|
'.wakz-msgs::-webkit-scrollbar-thumb {',
|
|
540
|
-
' background: rgba(0,0,0,0.
|
|
531
|
+
' background: rgba(0,0,0,0.12);',
|
|
541
532
|
' border-radius: 10px;',
|
|
542
533
|
'}',
|
|
543
534
|
|
|
544
535
|
/* ══════════════════════════════════════════════════
|
|
545
|
-
MESSAGE
|
|
536
|
+
MESSAGE BUBBLES
|
|
546
537
|
══════════════════════════════════════════════════ */
|
|
538
|
+
'.wakz-msg-row {',
|
|
539
|
+
' display: flex;',
|
|
540
|
+
' max-width: 82%;',
|
|
541
|
+
' animation: wakz-msg-in 0.3s cubic-bezier(0.4, 0, 0.2, 1) forwards;',
|
|
542
|
+
'}',
|
|
543
|
+
'.wakz-msg-row.bot { align-self: flex-start; }',
|
|
544
|
+
'.wakz-msg-row.user { align-self: flex-end; }',
|
|
545
|
+
|
|
547
546
|
'@keyframes wakz-msg-in {',
|
|
548
547
|
' 0% { opacity: 0; transform: translateY(8px); }',
|
|
549
548
|
' 100% { opacity: 1; transform: translateY(0); }',
|
|
550
549
|
'}',
|
|
551
550
|
|
|
552
|
-
|
|
553
|
-
'
|
|
554
|
-
' animation: wakz-msg-in 0.3s cubic-bezier(0.16,1,0.3,1) forwards;',
|
|
555
|
-
'}',
|
|
556
|
-
'.wakz-msg-row.user {',
|
|
557
|
-
' align-self: flex-end;',
|
|
558
|
-
' max-width: 75%;',
|
|
559
|
-
'}',
|
|
560
|
-
'.wakz-msg-row.bot {',
|
|
561
|
-
' align-self: flex-start;',
|
|
562
|
-
' max-width: 85%;',
|
|
563
|
-
'}',
|
|
564
|
-
|
|
565
|
-
'.wakz-bubble-user {',
|
|
566
|
-
' padding: 10px 16px;',
|
|
551
|
+
'.wakz-bubble {',
|
|
552
|
+
' padding: 10px 14px;',
|
|
567
553
|
' font-size: 14px;',
|
|
568
554
|
' line-height: 1.55;',
|
|
569
555
|
' word-wrap: break-word;',
|
|
570
556
|
' overflow-wrap: break-word;',
|
|
571
557
|
' white-space: pre-wrap;',
|
|
572
|
-
' border-radius:
|
|
573
|
-
'
|
|
558
|
+
' border-radius: var(--wakz-radius-bubble);',
|
|
559
|
+
' position: relative;',
|
|
560
|
+
'}',
|
|
561
|
+
'.wakz-bubble.bot {',
|
|
562
|
+
' background: #ffffff;',
|
|
563
|
+
' color: #1a1a1a;',
|
|
564
|
+
' border-bottom-left-radius: 4px;',
|
|
565
|
+
' box-shadow: var(--wakz-shadow-sm);',
|
|
566
|
+
'}',
|
|
567
|
+
'.wakz-bubble.user {',
|
|
574
568
|
' color: #ffffff;',
|
|
569
|
+
' border-bottom-right-radius: 4px;',
|
|
570
|
+
' background: var(--wakz-primary);',
|
|
571
|
+
'}',
|
|
572
|
+
'.wakz-bubble.error-bubble {',
|
|
573
|
+
' background: #fef2f2;',
|
|
574
|
+
' color: #dc2626;',
|
|
575
|
+
' border: 1px solid #fecaca;',
|
|
576
|
+
' border-bottom-left-radius: 4px;',
|
|
577
|
+
' box-shadow: none;',
|
|
575
578
|
'}',
|
|
576
579
|
|
|
577
|
-
/* ── Timestamp ── */
|
|
580
|
+
/* ── Message Timestamp ── */
|
|
578
581
|
'.wakz-ts {',
|
|
579
582
|
' display: block;',
|
|
580
|
-
' font-size:
|
|
581
|
-
' opacity: 0.
|
|
583
|
+
' font-size: 11px;',
|
|
584
|
+
' opacity: 0.45;',
|
|
582
585
|
' margin-top: 4px;',
|
|
583
586
|
'}',
|
|
584
|
-
'.wakz-bubble
|
|
587
|
+
'.wakz-bubble.user .wakz-ts {',
|
|
585
588
|
' text-align: right;',
|
|
586
|
-
' color: rgba(255,255,255,0.7);',
|
|
587
589
|
'}',
|
|
588
590
|
|
|
589
|
-
/* ──
|
|
590
|
-
'.wakz-msg-bot-wrap {',
|
|
591
|
-
' display: flex;',
|
|
592
|
-
' flex-direction: column;',
|
|
593
|
-
' gap: 2px;',
|
|
594
|
-
'}',
|
|
595
|
-
'.wakz-msg-bot-avatar {',
|
|
596
|
-
' width: 20px;',
|
|
597
|
-
' height: 20px;',
|
|
598
|
-
' border-radius: 50%;',
|
|
599
|
-
' background: var(--wakz-primary);',
|
|
600
|
-
' display: flex;',
|
|
601
|
-
' align-items: center;',
|
|
602
|
-
' justify-content: center;',
|
|
603
|
-
' color: #ffffff;',
|
|
604
|
-
' font-weight: 700;',
|
|
605
|
-
' font-size: 9px;',
|
|
606
|
-
' flex-shrink: 0;',
|
|
607
|
-
' margin-bottom: 2px;',
|
|
608
|
-
'}',
|
|
609
|
-
'.wakz-msg-bot-content {',
|
|
610
|
-
' border-left: 3px solid rgba(23,23,23,0.2);',
|
|
611
|
-
' padding: 10px 0 10px 14px;',
|
|
612
|
-
' font-size: 14px;',
|
|
613
|
-
' line-height: 1.6;',
|
|
614
|
-
' color: #374151;',
|
|
615
|
-
' word-wrap: break-word;',
|
|
616
|
-
' overflow-wrap: break-word;',
|
|
617
|
-
' white-space: pre-wrap;',
|
|
618
|
-
'}',
|
|
619
|
-
'.wakz-msg-bot-content .wakz-ts {',
|
|
620
|
-
' color: #9CA3AF;',
|
|
621
|
-
'}',
|
|
622
|
-
|
|
623
|
-
/* ── Support / Human Agent Message ── */
|
|
624
|
-
'.wakz-msg-bot-content.wakz-human {',
|
|
625
|
-
' border-left-color: #16A34A;',
|
|
626
|
-
'}',
|
|
591
|
+
/* ── Human Agent Badge ── */
|
|
627
592
|
'.wakz-human-badge {',
|
|
628
|
-
' display: inline-
|
|
629
|
-
'
|
|
630
|
-
'
|
|
631
|
-
' background: rgba(22,163,74,0.08);',
|
|
593
|
+
' display: inline-block;',
|
|
594
|
+
' font-size: 10px;',
|
|
595
|
+
' background: rgba(22, 163, 74, 0.1);',
|
|
632
596
|
' color: #16A34A;',
|
|
633
|
-
' border-radius:
|
|
634
|
-
' padding:
|
|
635
|
-
' margin-top:
|
|
597
|
+
' border-radius: 8px;',
|
|
598
|
+
' padding: 1px 7px;',
|
|
599
|
+
' margin-top: 5px;',
|
|
636
600
|
' font-weight: 600;',
|
|
601
|
+
' letter-spacing: 0.2px;',
|
|
637
602
|
'}',
|
|
638
603
|
|
|
639
|
-
/* ──
|
|
640
|
-
'.wakz-bubble-error {',
|
|
641
|
-
' padding: 10px 14px;',
|
|
642
|
-
' font-size: 14px;',
|
|
643
|
-
' line-height: 1.55;',
|
|
644
|
-
' word-wrap: break-word;',
|
|
645
|
-
' overflow-wrap: break-word;',
|
|
646
|
-
' white-space: pre-wrap;',
|
|
647
|
-
' background: #FEF2F2;',
|
|
648
|
-
' color: #DC2626;',
|
|
649
|
-
' border: 1px solid #FECACA;',
|
|
650
|
-
' border-radius: 18px 18px 6px 18px;',
|
|
651
|
-
'}',
|
|
652
|
-
|
|
653
|
-
/* ── Retry Button (inside error) ── */
|
|
604
|
+
/* ── Retry Button (inside error bubble) ── */
|
|
654
605
|
'.wakz-retry {',
|
|
655
606
|
' display: inline-flex;',
|
|
656
607
|
' align-items: center;',
|
|
@@ -665,7 +616,7 @@
|
|
|
665
616
|
' color: #dc2626;',
|
|
666
617
|
' cursor: pointer;',
|
|
667
618
|
' font-family: var(--wakz-font);',
|
|
668
|
-
' transition: background
|
|
619
|
+
' transition: background var(--wakz-transition), border-color var(--wakz-transition);',
|
|
669
620
|
' outline: none;',
|
|
670
621
|
'}',
|
|
671
622
|
'.wakz-retry:hover {',
|
|
@@ -674,32 +625,23 @@
|
|
|
674
625
|
'}',
|
|
675
626
|
|
|
676
627
|
/* ══════════════════════════════════════════════════
|
|
677
|
-
|
|
678
|
-
══════════════════════════════════════════════════ */
|
|
679
|
-
'.wakz-welcome-wrap {',
|
|
680
|
-
' display: flex;',
|
|
681
|
-
' flex-direction: column;',
|
|
682
|
-
' gap: 2px;',
|
|
683
|
-
' max-width: 85%;',
|
|
684
|
-
' align-self: flex-start;',
|
|
685
|
-
' animation: wakz-msg-in 0.35s cubic-bezier(0.16,1,0.3,1) forwards;',
|
|
686
|
-
'}',
|
|
687
|
-
|
|
688
|
-
/* ══════════════════════════════════════════════════
|
|
689
|
-
TYPING INDICATOR (no bubble — on background)
|
|
628
|
+
TYPING INDICATOR
|
|
690
629
|
══════════════════════════════════════════════════ */
|
|
691
630
|
'.wakz-typing {',
|
|
692
631
|
' display: flex;',
|
|
693
632
|
' align-items: center;',
|
|
694
633
|
' gap: 4px;',
|
|
695
|
-
' padding:
|
|
696
|
-
'
|
|
634
|
+
' padding: 14px 18px;',
|
|
635
|
+
' background: #ffffff;',
|
|
636
|
+
' border-radius: var(--wakz-radius-bubble);',
|
|
637
|
+
' border-bottom-left-radius: 4px;',
|
|
638
|
+
' box-shadow: var(--wakz-shadow-sm);',
|
|
697
639
|
'}',
|
|
698
640
|
'.wakz-typing-dot {',
|
|
699
641
|
' width: 7px;',
|
|
700
642
|
' height: 7px;',
|
|
701
643
|
' border-radius: 50%;',
|
|
702
|
-
' background: #
|
|
644
|
+
' background: #9ca3af;',
|
|
703
645
|
' animation: wakz-bounce-dot 1.4s ease-in-out infinite;',
|
|
704
646
|
'}',
|
|
705
647
|
'.wakz-typing-dot:nth-child(2) { animation-delay: 0.16s; }',
|
|
@@ -710,33 +652,31 @@
|
|
|
710
652
|
'}',
|
|
711
653
|
|
|
712
654
|
/* ══════════════════════════════════════════════════
|
|
713
|
-
INPUT AREA
|
|
655
|
+
INPUT AREA
|
|
714
656
|
══════════════════════════════════════════════════ */
|
|
715
657
|
'.wakz-input-wrap {',
|
|
716
658
|
' flex-shrink: 0;',
|
|
717
|
-
' padding: 12px
|
|
718
|
-
' border-top: 1px solid
|
|
719
|
-
' background:
|
|
720
|
-
' -webkit-backdrop-filter: blur(12px);',
|
|
721
|
-
' backdrop-filter: blur(12px);',
|
|
659
|
+
' padding: 12px 14px;',
|
|
660
|
+
' border-top: 1px solid #e5e7eb;',
|
|
661
|
+
' background: var(--wakz-widget-bg);',
|
|
722
662
|
' display: flex;',
|
|
723
663
|
' align-items: flex-end;',
|
|
724
664
|
' gap: 8px;',
|
|
725
665
|
'}',
|
|
726
666
|
'.wakz-input {',
|
|
727
667
|
' flex: 1;',
|
|
728
|
-
' border: 1.5px solid #
|
|
729
|
-
' border-radius:
|
|
668
|
+
' border: 1.5px solid #e5e7eb;',
|
|
669
|
+
' border-radius: 22px;',
|
|
730
670
|
' padding: 10px 16px;',
|
|
731
671
|
' font-size: 14px;',
|
|
732
672
|
' line-height: 1.4;',
|
|
733
673
|
' outline: none;',
|
|
734
674
|
' font-family: var(--wakz-font);',
|
|
735
|
-
' transition: border-color
|
|
675
|
+
' transition: border-color var(--wakz-transition);',
|
|
736
676
|
' resize: none;',
|
|
737
677
|
' max-height: 100px;',
|
|
738
678
|
' overflow-y: auto;',
|
|
739
|
-
' background: #
|
|
679
|
+
' background: #f9fafb;',
|
|
740
680
|
' color: #111827;',
|
|
741
681
|
'}',
|
|
742
682
|
'.wakz-input:focus {',
|
|
@@ -747,10 +687,10 @@
|
|
|
747
687
|
' color: #9ca3af;',
|
|
748
688
|
'}',
|
|
749
689
|
|
|
750
|
-
/* ── Send Button
|
|
690
|
+
/* ── Send Button ── */
|
|
751
691
|
'.wakz-send {',
|
|
752
|
-
' width:
|
|
753
|
-
' height:
|
|
692
|
+
' width: 40px;',
|
|
693
|
+
' height: 40px;',
|
|
754
694
|
' border-radius: 50%;',
|
|
755
695
|
' border: none;',
|
|
756
696
|
' color: #ffffff;',
|
|
@@ -759,21 +699,20 @@
|
|
|
759
699
|
' align-items: center;',
|
|
760
700
|
' justify-content: center;',
|
|
761
701
|
' flex-shrink: 0;',
|
|
762
|
-
' transition: opacity
|
|
702
|
+
' transition: opacity var(--wakz-transition), transform 0.1s ease, box-shadow var(--wakz-transition);',
|
|
763
703
|
' outline: none;',
|
|
764
704
|
' background: var(--wakz-primary);',
|
|
765
|
-
' box-shadow:
|
|
705
|
+
' box-shadow: var(--wakz-shadow-sm);',
|
|
766
706
|
'}',
|
|
767
707
|
'.wakz-send:hover:not(:disabled) {',
|
|
768
|
-
'
|
|
769
|
-
' box-shadow: 0 4px 16px rgba(0,0,0,0.18);',
|
|
708
|
+
' box-shadow: var(--wakz-shadow-md);',
|
|
770
709
|
'}',
|
|
771
710
|
'.wakz-send:disabled {',
|
|
772
711
|
' opacity: 0.35;',
|
|
773
712
|
' cursor: not-allowed;',
|
|
774
713
|
'}',
|
|
775
714
|
'.wakz-send:not(:disabled):active {',
|
|
776
|
-
' transform: scale(0.
|
|
715
|
+
' transform: scale(0.88);',
|
|
777
716
|
'}',
|
|
778
717
|
'.wakz-send svg {',
|
|
779
718
|
' width: 18px;',
|
|
@@ -782,55 +721,68 @@
|
|
|
782
721
|
'}',
|
|
783
722
|
|
|
784
723
|
/* ══════════════════════════════════════════════════
|
|
785
|
-
|
|
724
|
+
WELCOME MESSAGE
|
|
786
725
|
══════════════════════════════════════════════════ */
|
|
787
|
-
'.wakz-
|
|
788
|
-
'
|
|
789
|
-
'
|
|
726
|
+
'.wakz-welcome-wrap {',
|
|
727
|
+
' display: flex;',
|
|
728
|
+
' gap: 8px;',
|
|
729
|
+
' max-width: 88%;',
|
|
730
|
+
' align-self: flex-start;',
|
|
731
|
+
' animation: wakz-msg-in 0.35s cubic-bezier(0.4, 0, 0.2, 1) forwards;',
|
|
790
732
|
'}',
|
|
791
|
-
'.wakz-
|
|
792
|
-
'
|
|
733
|
+
'.wakz-welcome-avatar {',
|
|
734
|
+
' width: 28px;',
|
|
735
|
+
' height: 28px;',
|
|
736
|
+
' border-radius: 50%;',
|
|
737
|
+
' background: var(--wakz-primary);',
|
|
738
|
+
' display: flex;',
|
|
739
|
+
' align-items: center;',
|
|
740
|
+
' justify-content: center;',
|
|
741
|
+
' color: #ffffff;',
|
|
742
|
+
' font-weight: 700;',
|
|
743
|
+
' font-size: 12px;',
|
|
744
|
+
' flex-shrink: 0;',
|
|
745
|
+
' align-self: flex-end;',
|
|
746
|
+
' margin-bottom: 2px;',
|
|
793
747
|
'}',
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
748
|
+
|
|
749
|
+
/* ══════════════════════════════════════════════════
|
|
750
|
+
RTL SUPPORT
|
|
751
|
+
══════════════════════════════════════════════════ */
|
|
752
|
+
'.wakz-rtl { direction: rtl; }',
|
|
753
|
+
'.wakz-rtl .wakz-bubble.bot {',
|
|
754
|
+
' border-bottom-left-radius: var(--wakz-radius-bubble);',
|
|
755
|
+
' border-bottom-right-radius: 4px;',
|
|
798
756
|
'}',
|
|
799
|
-
'.wakz-rtl .wakz-
|
|
800
|
-
' border-
|
|
801
|
-
' border-
|
|
757
|
+
'.wakz-rtl .wakz-bubble.user {',
|
|
758
|
+
' border-bottom-right-radius: var(--wakz-radius-bubble);',
|
|
759
|
+
' border-bottom-left-radius: 4px;',
|
|
802
760
|
'}',
|
|
803
761
|
'.wakz-rtl .wakz-typing {',
|
|
804
|
-
' border-left:
|
|
805
|
-
' border-right:
|
|
806
|
-
' padding: 10px 14px 10px 0;',
|
|
762
|
+
' border-bottom-left-radius: var(--wakz-radius-bubble);',
|
|
763
|
+
' border-bottom-right-radius: 4px;',
|
|
807
764
|
'}',
|
|
808
|
-
'.wakz-rtl .wakz-bubble-
|
|
809
|
-
'
|
|
765
|
+
'.wakz-rtl .wakz-bubble.user .wakz-ts {',
|
|
766
|
+
' text-align: left;',
|
|
810
767
|
'}',
|
|
811
768
|
|
|
812
769
|
/* ══════════════════════════════════════════════════
|
|
813
|
-
MOBILE RESPONSIVE
|
|
770
|
+
MOBILE RESPONSIVE
|
|
814
771
|
══════════════════════════════════════════════════ */
|
|
815
772
|
'@media (max-width: 480px) {',
|
|
816
|
-
' .wakz-overlay {',
|
|
817
|
-
' display: none;',
|
|
818
|
-
' }',
|
|
819
773
|
' .wakz-window {',
|
|
820
|
-
' top: auto !important;',
|
|
821
|
-
' left: 0 !important;',
|
|
822
|
-
' right: 0 !important;',
|
|
823
|
-
' bottom: 0 !important;',
|
|
824
774
|
' width: 100vw !important;',
|
|
825
775
|
' height: 100vh !important;',
|
|
776
|
+
' min-height: 100vh !important;',
|
|
826
777
|
' max-height: 100vh !important;',
|
|
827
|
-
'
|
|
828
|
-
'
|
|
829
|
-
'
|
|
830
|
-
'
|
|
778
|
+
' bottom: 0 !important;',
|
|
779
|
+
' top: 0 !important;',
|
|
780
|
+
' right: 0 !important;',
|
|
781
|
+
' left: 0 !important;',
|
|
782
|
+
' border-radius: 0 !important;',
|
|
831
783
|
' }',
|
|
832
784
|
' .wakz-window.wakz-visible {',
|
|
833
|
-
' transform: translateY(0)
|
|
785
|
+
' transform: translateY(0) scale(1);',
|
|
834
786
|
' }',
|
|
835
787
|
' .wakz-fab-pos-br { bottom: 16px; right: 16px; }',
|
|
836
788
|
' .wakz-fab-pos-bl { bottom: 16px; left: 16px; }',
|
|
@@ -847,8 +799,6 @@
|
|
|
847
799
|
var isRtl = self.config.language === 'ar';
|
|
848
800
|
var posClass = self.config.position === 'bottom-left' ? 'bl' : 'br';
|
|
849
801
|
var str = _strings(self.config.language);
|
|
850
|
-
var iconColor = self._scriptIconColor || self.config.iconColor;
|
|
851
|
-
var iconShape = self._scriptIconShape || self.config.iconShape;
|
|
852
802
|
|
|
853
803
|
/* ── Host + Shadow DOM ── */
|
|
854
804
|
self._host = _el('div');
|
|
@@ -861,16 +811,11 @@
|
|
|
861
811
|
self._shadow.appendChild(self._root);
|
|
862
812
|
|
|
863
813
|
/* ══════════════ FLOATING ACTION BUTTON ══════════════ */
|
|
864
|
-
var fabClasses = 'wakz-fab wakz-fab-pos-' + posClass;
|
|
865
|
-
if (iconShape === 'rounded') fabClasses += ' wakz-fab-shape-rounded';
|
|
866
|
-
|
|
867
814
|
self._toggleBtn = _el('button', {
|
|
868
|
-
className:
|
|
869
|
-
'aria-label': str.openChat
|
|
870
|
-
style: { '--wakz-icon-color': iconColor }
|
|
815
|
+
className: 'wakz-fab wakz-fab-pos-' + posClass,
|
|
816
|
+
'aria-label': str.openChat
|
|
871
817
|
});
|
|
872
|
-
self._toggleBtn.
|
|
873
|
-
self._toggleBtn.innerHTML = _ICONS.chatIcon;
|
|
818
|
+
self._toggleBtn.innerHTML = _ICONS.chatBubble;
|
|
874
819
|
|
|
875
820
|
/* Status dot on FAB */
|
|
876
821
|
self._statusDot = _el('span', { className: 'wakz-fab-dot ' + (self.config.online ? 'online' : 'offline') });
|
|
@@ -878,41 +823,32 @@
|
|
|
878
823
|
self._toggleBtn.appendChild(self._statusDot);
|
|
879
824
|
self._root.appendChild(self._toggleBtn);
|
|
880
825
|
|
|
881
|
-
/* ══════════════
|
|
882
|
-
self.
|
|
883
|
-
self._root.appendChild(self._overlayEl);
|
|
884
|
-
|
|
885
|
-
/* ══════════════ CHAT WINDOW (Centered Modal) ══════════════ */
|
|
886
|
-
self._chatWindow = _el('div', { className: 'wakz-window' });
|
|
826
|
+
/* ══════════════ CHAT WINDOW ══════════════ */
|
|
827
|
+
self._chatWindow = _el('div', { className: 'wakz-window wakz-win-pos-' + posClass });
|
|
887
828
|
|
|
888
829
|
/* ── Header ── */
|
|
889
830
|
var headerEl = _el('div', { className: 'wakz-hdr' });
|
|
890
831
|
|
|
891
|
-
|
|
892
|
-
var headerRight = _el('div', { className: 'wakz-hdr-right' });
|
|
832
|
+
var headerLeft = _el('div', { className: 'wakz-hdr-left' });
|
|
893
833
|
var avatarLetter = (self.config.botName || 'W')[0].toUpperCase();
|
|
894
|
-
|
|
834
|
+
headerLeft.appendChild(_el('div', { className: 'wakz-avatar' }, [avatarLetter]));
|
|
895
835
|
|
|
896
836
|
var headerInfo = _el('div', { className: 'wakz-hdr-info' });
|
|
897
837
|
headerInfo.appendChild(_el('span', { className: 'wakz-hdr-name' }, [self.config.botName]));
|
|
898
|
-
headerRight.appendChild(headerInfo);
|
|
899
|
-
headerEl.appendChild(headerRight);
|
|
900
|
-
|
|
901
|
-
/* Left side: status + close (in RTL, left side = end) */
|
|
902
|
-
var headerLeft = _el('div', { className: 'wakz-hdr-left' });
|
|
903
838
|
|
|
904
839
|
var statusLine = _el('span', { className: 'wakz-hdr-status' });
|
|
905
840
|
self._headerStatusDot = _el('span', { className: 'wakz-hdr-status-dot ' + (self.config.online ? 'online' : 'offline') });
|
|
906
841
|
if (self.config.showStatus) statusLine.appendChild(self._headerStatusDot);
|
|
907
842
|
self._headerStatusText = document.createTextNode(self.config.online ? str.online : str.offline);
|
|
908
843
|
statusLine.appendChild(self._headerStatusText);
|
|
909
|
-
|
|
844
|
+
headerInfo.appendChild(statusLine);
|
|
910
845
|
|
|
911
|
-
|
|
912
|
-
closeBtn.innerHTML = _ICONS.close;
|
|
913
|
-
headerLeft.appendChild(closeBtn);
|
|
846
|
+
headerLeft.appendChild(headerInfo);
|
|
914
847
|
headerEl.appendChild(headerLeft);
|
|
915
848
|
|
|
849
|
+
var closeBtn = _el('button', { className: 'wakz-close', 'aria-label': str.closeChat });
|
|
850
|
+
closeBtn.innerHTML = _ICONS.close;
|
|
851
|
+
headerEl.appendChild(closeBtn);
|
|
916
852
|
self._chatWindow.appendChild(headerEl);
|
|
917
853
|
|
|
918
854
|
/* ── Messages Container ── */
|
|
@@ -958,11 +894,6 @@
|
|
|
958
894
|
self.toggleChat(false);
|
|
959
895
|
});
|
|
960
896
|
|
|
961
|
-
/* Overlay backdrop click to close */
|
|
962
|
-
self._overlayEl.addEventListener('click', function () {
|
|
963
|
-
self.toggleChat(false);
|
|
964
|
-
});
|
|
965
|
-
|
|
966
897
|
/* Send button click */
|
|
967
898
|
self._sendBtn.addEventListener('click', function () {
|
|
968
899
|
self._handleSend();
|
|
@@ -1011,9 +942,6 @@
|
|
|
1011
942
|
self.isOpen = open;
|
|
1012
943
|
if (open) {
|
|
1013
944
|
self._chatWindow.classList.add('wakz-visible');
|
|
1014
|
-
self._overlayEl.classList.add('wakz-visible');
|
|
1015
|
-
/* Hide FAB when chat is open */
|
|
1016
|
-
self._toggleBtn.classList.add('wakz-fab-hidden');
|
|
1017
945
|
/* Fetch history on first open (after config is loaded) */
|
|
1018
946
|
if (self._configLoaded && !self._hasFetchedHistory) {
|
|
1019
947
|
self._fetchHistory();
|
|
@@ -1027,9 +955,6 @@
|
|
|
1027
955
|
setTimeout(function () { self._inputEl.focus(); }, 320);
|
|
1028
956
|
} else {
|
|
1029
957
|
self._chatWindow.classList.remove('wakz-visible');
|
|
1030
|
-
self._overlayEl.classList.remove('wakz-visible');
|
|
1031
|
-
/* Show FAB when chat is closed */
|
|
1032
|
-
self._toggleBtn.classList.remove('wakz-fab-hidden');
|
|
1033
958
|
/* Stop polling when chat is closed */
|
|
1034
959
|
self._stopPolling();
|
|
1035
960
|
}
|
|
@@ -1056,9 +981,6 @@
|
|
|
1056
981
|
.then(function (data) {
|
|
1057
982
|
if (data && data.success && data.config) {
|
|
1058
983
|
self.config = Object.assign({}, _DEFAULTS, data.config);
|
|
1059
|
-
/* Apply script-level overrides */
|
|
1060
|
-
if (self._scriptIconColor) self.config.iconColor = self._scriptIconColor;
|
|
1061
|
-
if (self._scriptIconShape) self.config.iconShape = self._scriptIconShape;
|
|
1062
984
|
self._configLoaded = true;
|
|
1063
985
|
self._applyConfig();
|
|
1064
986
|
} else {
|
|
@@ -1098,13 +1020,11 @@
|
|
|
1098
1020
|
var str = _strings(cfg.language);
|
|
1099
1021
|
var isRtl = cfg.language === 'ar';
|
|
1100
1022
|
var posClass = cfg.position === 'bottom-left' ? 'bl' : 'br';
|
|
1101
|
-
var iconColor = cfg.iconColor || '#171717';
|
|
1102
|
-
var iconShape = cfg.iconShape || 'circle';
|
|
1103
1023
|
|
|
1104
1024
|
/* ── Update CSS custom properties ── */
|
|
1105
1025
|
var hostStyle = self._shadow.host.style;
|
|
1106
1026
|
hostStyle.setProperty('--wakz-primary', cfg.primaryColor);
|
|
1107
|
-
hostStyle.setProperty('--wakz-
|
|
1027
|
+
hostStyle.setProperty('--wakz-btn', cfg.btnColor);
|
|
1108
1028
|
hostStyle.setProperty('--wakz-chat-bg', cfg.chatBg);
|
|
1109
1029
|
hostStyle.setProperty('--wakz-widget-bg', cfg.widgetBg);
|
|
1110
1030
|
|
|
@@ -1113,19 +1033,19 @@
|
|
|
1113
1033
|
else self._root.classList.remove('wakz-rtl');
|
|
1114
1034
|
|
|
1115
1035
|
/* ── FAB ── */
|
|
1116
|
-
|
|
1117
|
-
if (iconShape === 'rounded') fabClasses += ' wakz-fab-shape-rounded';
|
|
1118
|
-
if (self.isOpen) fabClasses += ' wakz-fab-hidden';
|
|
1119
|
-
self._toggleBtn.className = fabClasses;
|
|
1120
|
-
self._toggleBtn.style.background = iconColor;
|
|
1036
|
+
self._toggleBtn.className = 'wakz-fab wakz-fab-pos-' + posClass + ' wakz-fab-enter';
|
|
1121
1037
|
self._toggleBtn.setAttribute('aria-label', str.openChat);
|
|
1122
1038
|
|
|
1123
1039
|
/* ── FAB Status Dot ── */
|
|
1124
1040
|
self._statusDot.className = 'wakz-fab-dot ' + (cfg.online ? 'online' : 'offline');
|
|
1125
1041
|
self._statusDot.style.display = cfg.showStatus ? '' : 'none';
|
|
1126
1042
|
|
|
1127
|
-
/* ── Window ── */
|
|
1128
|
-
self._chatWindow.className = 'wakz-window' + (self.isOpen ? ' wakz-visible' : '');
|
|
1043
|
+
/* ── Window Position ── */
|
|
1044
|
+
self._chatWindow.className = 'wakz-window wakz-win-pos-' + posClass + (self.isOpen ? ' wakz-visible' : '');
|
|
1045
|
+
|
|
1046
|
+
/* ── Header ── */
|
|
1047
|
+
var headerEl = self._chatWindow.querySelector('.wakz-hdr');
|
|
1048
|
+
if (headerEl) headerEl.style.background = cfg.primaryColor;
|
|
1129
1049
|
|
|
1130
1050
|
/* ── Bot Name & Avatar ── */
|
|
1131
1051
|
var nameEl = self._chatWindow.querySelector('.wakz-hdr-name');
|
|
@@ -1227,6 +1147,8 @@
|
|
|
1227
1147
|
WAKZWidget.prototype._pollForNewMessages = function () {
|
|
1228
1148
|
var self = this;
|
|
1229
1149
|
if (!self.server || !self.apiKey || !self._lastPollTime) return;
|
|
1150
|
+
/* Skip polling while waiting for our own reply — prevents race condition duplicates */
|
|
1151
|
+
if (self._pendingReply) return;
|
|
1230
1152
|
|
|
1231
1153
|
var url = self.server + '/api/v1/embed/chat?api_key=' +
|
|
1232
1154
|
encodeURIComponent(self.apiKey) +
|
|
@@ -1273,7 +1195,7 @@
|
|
|
1273
1195
|
};
|
|
1274
1196
|
|
|
1275
1197
|
/* ════════════════════════════════════════════════════════════════
|
|
1276
|
-
APPEND WELCOME MESSAGE (bot
|
|
1198
|
+
APPEND WELCOME MESSAGE (with bot avatar)
|
|
1277
1199
|
════════════════════════════════════════════════════════════════ */
|
|
1278
1200
|
|
|
1279
1201
|
WAKZWidget.prototype._appendWelcomeMessage = function (text) {
|
|
@@ -1283,13 +1205,13 @@
|
|
|
1283
1205
|
var wrap = _el('div', { className: 'wakz-welcome-wrap' });
|
|
1284
1206
|
|
|
1285
1207
|
/* Bot avatar */
|
|
1286
|
-
var avatar = _el('div', { className: 'wakz-
|
|
1208
|
+
var avatar = _el('div', { className: 'wakz-welcome-avatar' },
|
|
1287
1209
|
[(self.config.botName || 'W')[0].toUpperCase()]);
|
|
1288
1210
|
wrap.appendChild(avatar);
|
|
1289
1211
|
|
|
1290
|
-
/*
|
|
1291
|
-
var
|
|
1292
|
-
wrap.appendChild(
|
|
1212
|
+
/* Bubble */
|
|
1213
|
+
var bubble = _el('div', { className: 'wakz-bubble bot' }, [text]);
|
|
1214
|
+
wrap.appendChild(bubble);
|
|
1293
1215
|
|
|
1294
1216
|
self._messagesContainer.appendChild(wrap);
|
|
1295
1217
|
self.messages.push({ sender: 'bot', text: text });
|
|
@@ -1303,30 +1225,41 @@
|
|
|
1303
1225
|
WAKZWidget.prototype._appendMessage = function (sender, text, isError, timestamp, isHumanAgent) {
|
|
1304
1226
|
var self = this;
|
|
1305
1227
|
|
|
1306
|
-
|
|
1307
|
-
/* ── User message: rounded bubble ── */
|
|
1308
|
-
var row = _el('div', { className: 'wakz-msg-row user' });
|
|
1309
|
-
var bubble = _el('div', { className: 'wakz-bubble-user' });
|
|
1310
|
-
bubble.appendChild(document.createTextNode(text));
|
|
1228
|
+
var row = _el('div', { className: 'wakz-msg-row ' + sender });
|
|
1311
1229
|
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1230
|
+
var bubbleClass = 'wakz-bubble ' + sender;
|
|
1231
|
+
if (isError) bubbleClass += ' error-bubble';
|
|
1232
|
+
var bubble = _el('div', { className: bubbleClass });
|
|
1315
1233
|
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
|
|
1234
|
+
bubble.appendChild(document.createTextNode(text));
|
|
1235
|
+
|
|
1236
|
+
/* Timestamp */
|
|
1237
|
+
var tsText = '';
|
|
1238
|
+
if (timestamp) {
|
|
1239
|
+
try {
|
|
1240
|
+
var d = new Date(timestamp);
|
|
1241
|
+
if (!isNaN(d.getTime())) {
|
|
1242
|
+
tsText = d.getHours().toString().padStart(2, '0') + ':' +
|
|
1243
|
+
d.getMinutes().toString().padStart(2, '0');
|
|
1244
|
+
}
|
|
1245
|
+
} catch (e) { /* ignore */ }
|
|
1321
1246
|
}
|
|
1247
|
+
if (!tsText) {
|
|
1248
|
+
var now = new Date();
|
|
1249
|
+
tsText = now.getHours().toString().padStart(2, '0') + ':' +
|
|
1250
|
+
now.getMinutes().toString().padStart(2, '0');
|
|
1251
|
+
}
|
|
1252
|
+
bubble.appendChild(_el('span', { className: 'wakz-ts' }, [tsText]));
|
|
1322
1253
|
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
var
|
|
1326
|
-
var
|
|
1327
|
-
|
|
1254
|
+
/* Human agent badge — visual distinction for admin replies */
|
|
1255
|
+
if (isHumanAgent) {
|
|
1256
|
+
var iStr = _strings(self.config.language);
|
|
1257
|
+
var badge = _el('span', { className: 'wakz-human-badge' }, [iStr.humanBadge || '👤 فريق الدعم']);
|
|
1258
|
+
bubble.appendChild(badge);
|
|
1259
|
+
}
|
|
1328
1260
|
|
|
1329
|
-
|
|
1261
|
+
/* Retry button for errors */
|
|
1262
|
+
if (isError) {
|
|
1330
1263
|
var str = _strings(self.config.language);
|
|
1331
1264
|
var retryBtn = _el('button', { className: 'wakz-retry' }, [
|
|
1332
1265
|
_ICONS.error + ' ' + str.retry
|
|
@@ -1352,71 +1285,19 @@
|
|
|
1352
1285
|
}
|
|
1353
1286
|
if (lastUserMsg) self._sendToAPI(lastUserMsg);
|
|
1354
1287
|
});
|
|
1355
|
-
})(
|
|
1356
|
-
|
|
1357
|
-
|
|
1358
|
-
errRow.appendChild(errBubble);
|
|
1359
|
-
self._messagesContainer.appendChild(errRow);
|
|
1360
|
-
self.messages.push({ sender: sender, text: text, isError: true });
|
|
1361
|
-
self._scrollToBottom();
|
|
1362
|
-
return;
|
|
1288
|
+
})(row);
|
|
1289
|
+
bubble.appendChild(retryBtn);
|
|
1363
1290
|
}
|
|
1364
1291
|
|
|
1365
|
-
|
|
1366
|
-
|
|
1367
|
-
var wrap = _el('div', { className: 'wakz-msg-bot-wrap' });
|
|
1292
|
+
row.appendChild(bubble);
|
|
1293
|
+
self._messagesContainer.appendChild(row);
|
|
1368
1294
|
|
|
1369
|
-
|
|
1370
|
-
var avatar = _el('div', { className: 'wakz-msg-bot-avatar' },
|
|
1371
|
-
[(self.config.botName || 'W')[0].toUpperCase()]);
|
|
1372
|
-
wrap.appendChild(avatar);
|
|
1373
|
-
|
|
1374
|
-
/* Flat content */
|
|
1375
|
-
var contentClass = 'wakz-msg-bot-content';
|
|
1376
|
-
if (isHumanAgent) contentClass += ' wakz-human';
|
|
1377
|
-
var content = _el('div', { className: contentClass });
|
|
1378
|
-
content.appendChild(document.createTextNode(text));
|
|
1379
|
-
|
|
1380
|
-
/* Timestamp */
|
|
1381
|
-
var botTs = self._formatTime(timestamp);
|
|
1382
|
-
content.appendChild(_el('span', { className: 'wakz-ts' }, [botTs]));
|
|
1383
|
-
|
|
1384
|
-
/* Human agent badge */
|
|
1385
|
-
if (isHumanAgent) {
|
|
1386
|
-
var iStr = _strings(self.config.language);
|
|
1387
|
-
var badge = _el('span', { className: 'wakz-human-badge' }, [iStr.humanBadge || '👤 فريق الدعم']);
|
|
1388
|
-
content.appendChild(badge);
|
|
1389
|
-
}
|
|
1390
|
-
|
|
1391
|
-
wrap.appendChild(content);
|
|
1392
|
-
botRow.appendChild(wrap);
|
|
1393
|
-
self._messagesContainer.appendChild(botRow);
|
|
1394
|
-
|
|
1395
|
-
self.messages.push({ sender: sender, text: text, isError: false, isHumanAgent: !!isHumanAgent });
|
|
1295
|
+
self.messages.push({ sender: sender, text: text, isError: !!isError });
|
|
1396
1296
|
self._scrollToBottom();
|
|
1397
1297
|
};
|
|
1398
1298
|
|
|
1399
1299
|
/* ════════════════════════════════════════════════════════════════
|
|
1400
|
-
|
|
1401
|
-
════════════════════════════════════════════════════════════════ */
|
|
1402
|
-
|
|
1403
|
-
WAKZWidget.prototype._formatTime = function (timestamp) {
|
|
1404
|
-
if (timestamp) {
|
|
1405
|
-
try {
|
|
1406
|
-
var d = new Date(timestamp);
|
|
1407
|
-
if (!isNaN(d.getTime())) {
|
|
1408
|
-
return d.getHours().toString().padStart(2, '0') + ':' +
|
|
1409
|
-
d.getMinutes().toString().padStart(2, '0');
|
|
1410
|
-
}
|
|
1411
|
-
} catch (e) { /* ignore */ }
|
|
1412
|
-
}
|
|
1413
|
-
var now = new Date();
|
|
1414
|
-
return now.getHours().toString().padStart(2, '0') + ':' +
|
|
1415
|
-
now.getMinutes().toString().padStart(2, '0');
|
|
1416
|
-
};
|
|
1417
|
-
|
|
1418
|
-
/* ════════════════════════════════════════════════════════════════
|
|
1419
|
-
TYPING INDICATOR (no bubble — on background)
|
|
1300
|
+
TYPING INDICATOR
|
|
1420
1301
|
════════════════════════════════════════════════════════════════ */
|
|
1421
1302
|
|
|
1422
1303
|
WAKZWidget.prototype._showTyping = function () {
|
|
@@ -1491,6 +1372,10 @@
|
|
|
1491
1372
|
self._sendBtn.disabled = true;
|
|
1492
1373
|
self._showTyping();
|
|
1493
1374
|
|
|
1375
|
+
/* Freeze poll time BEFORE sending — prevents polling from fetching our own messages */
|
|
1376
|
+
self._lastPollTime = new Date().toISOString();
|
|
1377
|
+
self._pendingReply = true;
|
|
1378
|
+
|
|
1494
1379
|
var payload = {
|
|
1495
1380
|
api_key: self.apiKey,
|
|
1496
1381
|
visitor_id: self.visitorId,
|
|
@@ -1499,7 +1384,10 @@
|
|
|
1499
1384
|
domain: window.location.hostname || '',
|
|
1500
1385
|
page_url: window.location.href || '',
|
|
1501
1386
|
user_agent: navigator.userAgent || '',
|
|
1502
|
-
language: navigator.language || ''
|
|
1387
|
+
language: navigator.language || '',
|
|
1388
|
+
device_model: self._deviceInfo.device_model || '',
|
|
1389
|
+
device_platform: self._deviceInfo.device_platform || '',
|
|
1390
|
+
device_type: self._deviceInfo.device_type || 'desktop'
|
|
1503
1391
|
}
|
|
1504
1392
|
};
|
|
1505
1393
|
|
|
@@ -1514,19 +1402,23 @@
|
|
|
1514
1402
|
.then(function (res) { return res.json(); })
|
|
1515
1403
|
.then(function (data) {
|
|
1516
1404
|
self._hideTyping();
|
|
1405
|
+
self._pendingReply = false;
|
|
1517
1406
|
if (data && data.success && data.reply) {
|
|
1518
1407
|
self._appendMessage('bot', data.reply);
|
|
1519
1408
|
} else {
|
|
1520
1409
|
self._appendMessage('bot', _strings(self.config.language).errorMsg, true);
|
|
1521
1410
|
}
|
|
1522
|
-
/*
|
|
1523
|
-
self._lastPollTime =
|
|
1411
|
+
/* Advance poll time past any messages created during this request */
|
|
1412
|
+
self._lastPollTime = (data && data.timestamp)
|
|
1413
|
+
? new Date(new Date(data.timestamp).getTime() + 1000).toISOString()
|
|
1414
|
+
: new Date(Date.now() + 1000).toISOString();
|
|
1524
1415
|
})
|
|
1525
1416
|
.catch(function () {
|
|
1526
1417
|
self._hideTyping();
|
|
1418
|
+
self._pendingReply = false;
|
|
1527
1419
|
self._appendMessage('bot', _strings(self.config.language).errorMsg, true);
|
|
1528
|
-
/*
|
|
1529
|
-
self._lastPollTime = new Date().toISOString();
|
|
1420
|
+
/* Advance poll time even on error */
|
|
1421
|
+
self._lastPollTime = new Date(Date.now() + 1000).toISOString();
|
|
1530
1422
|
})
|
|
1531
1423
|
.finally(function () {
|
|
1532
1424
|
self.isLoading = false;
|