wakz-chat-widget 2.2.0 → 3.0.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 +1 -71
- package/index.js +442 -287
- package/package.json +5 -30
package/index.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* WAKZ Chat Widget
|
|
2
|
+
* WAKZ Chat Widget v3.0.0
|
|
3
3
|
* ─────────────────────────────────────────────────────────────────
|
|
4
4
|
* A production-grade, self-contained chat widget using Shadow DOM.
|
|
5
|
-
*
|
|
5
|
+
* Complete UI/UX revamp — glass morphism, centered modal, modern design.
|
|
6
6
|
*
|
|
7
7
|
* Embed: <script src="/wakz-widget.js" data-api-key="xxx" data-server="https://..." async></script>
|
|
8
8
|
*
|
|
@@ -222,7 +222,7 @@
|
|
|
222
222
|
self.apiKey = attrs.apiKey;
|
|
223
223
|
self.server = attrs.server;
|
|
224
224
|
self.visitorId = _getVisitorId();
|
|
225
|
-
self._deviceInfo = _getDeviceInfo();
|
|
225
|
+
self._deviceInfo = _getDeviceInfo();
|
|
226
226
|
|
|
227
227
|
/* ── Runtime state ── */
|
|
228
228
|
self.config = Object.assign({}, _DEFAULTS);
|
|
@@ -235,22 +235,26 @@
|
|
|
235
235
|
self._configError = false;
|
|
236
236
|
self._pollTimer = null;
|
|
237
237
|
self._lastPollTime = null;
|
|
238
|
-
self._POLL_INTERVAL = 4000;
|
|
239
|
-
self._knownMessageIds = {};
|
|
240
|
-
self._pendingReply = false;
|
|
238
|
+
self._POLL_INTERVAL = 4000;
|
|
239
|
+
self._knownMessageIds = {};
|
|
240
|
+
self._pendingReply = false;
|
|
241
241
|
|
|
242
242
|
/* ── DOM refs (populated after mount) ── */
|
|
243
243
|
self._host = null;
|
|
244
244
|
self._shadow = null;
|
|
245
245
|
self._root = null;
|
|
246
|
+
self._overlay = null;
|
|
246
247
|
self._chatWindow = null;
|
|
247
248
|
self._messagesContainer = null;
|
|
249
|
+
self._inputArea = null;
|
|
248
250
|
self._inputEl = null;
|
|
249
251
|
self._sendBtn = null;
|
|
250
252
|
self._toggleBtn = null;
|
|
251
253
|
self._statusDot = null;
|
|
252
254
|
self._headerStatusDot = null;
|
|
253
255
|
self._headerStatusText = null;
|
|
256
|
+
self._headerAvatarEl = null;
|
|
257
|
+
self._headerNameEl = null;
|
|
254
258
|
|
|
255
259
|
/* ── Bootstrap ── */
|
|
256
260
|
self._injectCSS();
|
|
@@ -261,7 +265,7 @@
|
|
|
261
265
|
}
|
|
262
266
|
|
|
263
267
|
/* ════════════════════════════════════════════════════════════════
|
|
264
|
-
CSS — Complete styling injected into Shadow DOM
|
|
268
|
+
CSS — Complete styling injected into Shadow DOM (v3.0.0 revamp)
|
|
265
269
|
════════════════════════════════════════════════════════════════ */
|
|
266
270
|
|
|
267
271
|
WAKZWidget.prototype._injectCSS = function () {
|
|
@@ -275,15 +279,12 @@
|
|
|
275
279
|
' --wakz-btn: #171717;',
|
|
276
280
|
' --wakz-chat-bg: #f8f9fa;',
|
|
277
281
|
' --wakz-widget-bg: #ffffff;',
|
|
278
|
-
' --wakz-radius
|
|
279
|
-
' --wakz-radius-bubble: 16px;',
|
|
280
|
-
' --wakz-radius-fab: 28px;',
|
|
282
|
+
' --wakz-icon-radius: 50%;',
|
|
281
283
|
' --wakz-shadow-sm: 0 1px 3px rgba(0,0,0,.08), 0 1px 2px rgba(0,0,0,.06);',
|
|
282
284
|
' --wakz-shadow-md: 0 4px 12px rgba(0,0,0,.1), 0 2px 4px rgba(0,0,0,.06);',
|
|
283
|
-
' --wakz-shadow-lg: 0
|
|
284
|
-
' --wakz-shadow-xl: 0 20px 60px rgba(0,0,0,.2), 0 8px 20px rgba(0,0,0,.12);',
|
|
285
|
+
' --wakz-shadow-lg: 0 25px 60px rgba(0,0,0,.15), 0 10px 20px rgba(0,0,0,.1);',
|
|
285
286
|
' --wakz-transition: 200ms ease;',
|
|
286
|
-
' --wakz-font: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;',
|
|
287
|
+
' --wakz-font: \'Inter\', -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;',
|
|
287
288
|
' all: initial;',
|
|
288
289
|
' font-family: var(--wakz-font);',
|
|
289
290
|
'}',
|
|
@@ -296,35 +297,35 @@
|
|
|
296
297
|
'}',
|
|
297
298
|
|
|
298
299
|
/* ══════════════════════════════════════════════════
|
|
299
|
-
FLOATING ACTION BUTTON (FAB)
|
|
300
|
+
FLOATING ACTION BUTTON (FAB) — 48px, independent color
|
|
300
301
|
══════════════════════════════════════════════════ */
|
|
301
302
|
'.wakz-fab {',
|
|
302
303
|
' position: fixed;',
|
|
303
304
|
' z-index: 2147483647;',
|
|
304
|
-
' width:
|
|
305
|
-
' height:
|
|
306
|
-
' border-radius: var(--wakz-radius
|
|
305
|
+
' width: 48px;',
|
|
306
|
+
' height: 48px;',
|
|
307
|
+
' border-radius: var(--wakz-icon-radius);',
|
|
307
308
|
' border: none;',
|
|
308
309
|
' cursor: pointer;',
|
|
309
310
|
' display: flex;',
|
|
310
311
|
' align-items: center;',
|
|
311
312
|
' justify-content: center;',
|
|
312
|
-
' box-shadow:
|
|
313
|
+
' box-shadow: 0 4px 14px rgba(0,0,0,.15), 0 2px 6px rgba(0,0,0,.1);',
|
|
313
314
|
' transition: transform var(--wakz-transition), box-shadow var(--wakz-transition);',
|
|
314
315
|
' outline: none;',
|
|
315
316
|
' -webkit-tap-highlight-color: transparent;',
|
|
316
317
|
' background: var(--wakz-btn);',
|
|
317
318
|
'}',
|
|
318
319
|
'.wakz-fab:hover {',
|
|
319
|
-
' transform: scale(1.
|
|
320
|
-
' box-shadow:
|
|
320
|
+
' transform: scale(1.06);',
|
|
321
|
+
' box-shadow: 0 8px 24px rgba(0,0,0,.2), 0 4px 10px rgba(0,0,0,.12);',
|
|
321
322
|
'}',
|
|
322
323
|
'.wakz-fab:active {',
|
|
323
|
-
' transform: scale(0.
|
|
324
|
+
' transform: scale(0.94);',
|
|
324
325
|
'}',
|
|
325
326
|
'.wakz-fab svg {',
|
|
326
|
-
' width:
|
|
327
|
-
' height:
|
|
327
|
+
' width: 22px;',
|
|
328
|
+
' height: 22px;',
|
|
328
329
|
' color: #ffffff;',
|
|
329
330
|
' pointer-events: none;',
|
|
330
331
|
'}',
|
|
@@ -333,6 +334,12 @@
|
|
|
333
334
|
'.wakz-fab-pos-br { bottom: 24px; right: 24px; }',
|
|
334
335
|
'.wakz-fab-pos-bl { bottom: 24px; left: 24px; }',
|
|
335
336
|
|
|
337
|
+
/* ── Hide FAB when chat is open ── */
|
|
338
|
+
'.wakz-window.wakz-visible ~ .wakz-fab,',
|
|
339
|
+
'.wakz-fab.wakz-fab-hidden {',
|
|
340
|
+
' display: none !important;',
|
|
341
|
+
'}',
|
|
342
|
+
|
|
336
343
|
/* ── FAB Entrance Bounce ── */
|
|
337
344
|
'@keyframes wakz-fab-enter {',
|
|
338
345
|
' 0% { opacity: 0; transform: scale(0.3); }',
|
|
@@ -345,22 +352,22 @@
|
|
|
345
352
|
'}',
|
|
346
353
|
|
|
347
354
|
/* ══════════════════════════════════════════════════
|
|
348
|
-
STATUS DOT (on FAB)
|
|
355
|
+
STATUS DOT (on FAB) — 10px, top-right
|
|
349
356
|
══════════════════════════════════════════════════ */
|
|
350
357
|
'.wakz-fab-dot {',
|
|
351
358
|
' position: absolute;',
|
|
352
|
-
' top:
|
|
353
|
-
' right:
|
|
354
|
-
' width:
|
|
355
|
-
' height:
|
|
359
|
+
' top: -1px;',
|
|
360
|
+
' right: -1px;',
|
|
361
|
+
' width: 10px;',
|
|
362
|
+
' height: 10px;',
|
|
356
363
|
' border-radius: 50%;',
|
|
357
|
-
' border:
|
|
364
|
+
' border: 2px solid #ffffff;',
|
|
358
365
|
' z-index: 2;',
|
|
359
366
|
' transition: background 0.3s ease;',
|
|
360
367
|
'}',
|
|
361
368
|
'.wakz-fab-pos-bl .wakz-fab-dot {',
|
|
362
369
|
' right: auto;',
|
|
363
|
-
' left:
|
|
370
|
+
' left: -1px;',
|
|
364
371
|
'}',
|
|
365
372
|
|
|
366
373
|
/* ── Online (green pulse) ── */
|
|
@@ -369,7 +376,7 @@
|
|
|
369
376
|
'}',
|
|
370
377
|
'@keyframes wakz-dot-pulse {',
|
|
371
378
|
' 0%, 100% { box-shadow: 0 0 0 0 rgba(34,197,94,0.5); }',
|
|
372
|
-
' 50% { box-shadow: 0 0 0
|
|
379
|
+
' 50% { box-shadow: 0 0 0 5px rgba(34,197,94,0); }',
|
|
373
380
|
'}',
|
|
374
381
|
'.wakz-fab-dot.online {',
|
|
375
382
|
' animation: wakz-dot-pulse 2s ease-in-out infinite;',
|
|
@@ -381,77 +388,100 @@
|
|
|
381
388
|
' animation: none;',
|
|
382
389
|
'}',
|
|
383
390
|
|
|
384
|
-
/*
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
'
|
|
391
|
+
/* ══════════════════════════════════════════════════
|
|
392
|
+
OVERLAY / BACKDROP — glass blur behind modal
|
|
393
|
+
══════════════════════════════════════════════════ */
|
|
394
|
+
'.wakz-overlay {',
|
|
395
|
+
' position: fixed;',
|
|
396
|
+
' inset: 0;',
|
|
397
|
+
' z-index: 2147483645;',
|
|
398
|
+
' background: rgba(0,0,0,0.25);',
|
|
399
|
+
' backdrop-filter: blur(4px);',
|
|
400
|
+
' -webkit-backdrop-filter: blur(4px);',
|
|
401
|
+
' opacity: 0;',
|
|
402
|
+
' pointer-events: none;',
|
|
403
|
+
' transition: opacity 0.3s cubic-bezier(0.4, 0, 0.2, 1);',
|
|
404
|
+
'}',
|
|
405
|
+
'.wakz-overlay.wakz-visible {',
|
|
406
|
+
' opacity: 1;',
|
|
407
|
+
' pointer-events: auto;',
|
|
388
408
|
'}',
|
|
389
409
|
|
|
390
410
|
/* ══════════════════════════════════════════════════
|
|
391
|
-
CHAT WINDOW
|
|
411
|
+
CHAT WINDOW — Centered Modal
|
|
392
412
|
══════════════════════════════════════════════════ */
|
|
393
413
|
'.wakz-window {',
|
|
394
414
|
' position: fixed;',
|
|
395
415
|
' z-index: 2147483646;',
|
|
396
|
-
'
|
|
397
|
-
'
|
|
398
|
-
'
|
|
399
|
-
'
|
|
416
|
+
' top: 50%;',
|
|
417
|
+
' left: 50%;',
|
|
418
|
+
' transform: translate(-50%, -50%) scale(0.95);',
|
|
419
|
+
' width: 390px;',
|
|
420
|
+
' height: min(580px, 80vh);',
|
|
421
|
+
' border-radius: 16px;',
|
|
400
422
|
' overflow: hidden;',
|
|
401
423
|
' display: flex;',
|
|
402
424
|
' flex-direction: column;',
|
|
403
425
|
' background: var(--wakz-widget-bg);',
|
|
404
|
-
' box-shadow:
|
|
426
|
+
' box-shadow: 0 25px 60px rgba(0,0,0,0.15), 0 10px 20px rgba(0,0,0,0.1);',
|
|
405
427
|
' opacity: 0;',
|
|
406
|
-
' transform: translateY(20px) scale(0.95);',
|
|
407
428
|
' pointer-events: none;',
|
|
408
429
|
' transition: opacity 0.3s cubic-bezier(0.4, 0, 0.2, 1),',
|
|
409
430
|
' transform 0.3s cubic-bezier(0.4, 0, 0.2, 1);',
|
|
410
431
|
'}',
|
|
411
432
|
'.wakz-window.wakz-visible {',
|
|
412
433
|
' opacity: 1;',
|
|
413
|
-
' transform:
|
|
434
|
+
' transform: translate(-50%, -50%) scale(1);',
|
|
414
435
|
' pointer-events: auto;',
|
|
415
436
|
'}',
|
|
416
437
|
|
|
417
|
-
/* ── Window Positioning ── */
|
|
418
|
-
'.wakz-win-pos-br { bottom: 92px; right: 24px; }',
|
|
419
|
-
'.wakz-win-pos-bl { bottom: 92px; left: 24px; }',
|
|
420
|
-
|
|
421
438
|
/* ══════════════════════════════════════════════════
|
|
422
|
-
HEADER
|
|
439
|
+
HEADER — Glass effect matching main site
|
|
423
440
|
══════════════════════════════════════════════════ */
|
|
424
441
|
'.wakz-hdr {',
|
|
425
442
|
' flex-shrink: 0;',
|
|
426
|
-
'
|
|
443
|
+
' height: 52px;',
|
|
444
|
+
' padding: 0 16px;',
|
|
427
445
|
' display: flex;',
|
|
428
446
|
' align-items: center;',
|
|
429
447
|
' justify-content: space-between;',
|
|
430
|
-
' color: #
|
|
448
|
+
' color: #171717;',
|
|
431
449
|
' cursor: default;',
|
|
432
450
|
' user-select: none;',
|
|
433
|
-
' background:
|
|
451
|
+
' background: rgba(255,255,255,0.85);',
|
|
452
|
+
' backdrop-filter: blur(24px);',
|
|
453
|
+
' -webkit-backdrop-filter: blur(24px);',
|
|
454
|
+
' border-bottom: 1px solid rgba(229,229,229,0.6);',
|
|
455
|
+
' box-shadow: 0 4px 12px rgba(0,0,0,0.05);',
|
|
456
|
+
' border-radius: 16px 16px 0 0;',
|
|
434
457
|
'}',
|
|
435
458
|
'.wakz-hdr-left {',
|
|
436
459
|
' display: flex;',
|
|
437
460
|
' align-items: center;',
|
|
438
|
-
' gap:
|
|
461
|
+
' gap: 10px;',
|
|
439
462
|
' min-width: 0;',
|
|
463
|
+
' flex: 1;',
|
|
464
|
+
'}',
|
|
465
|
+
'.wakz-hdr-right {',
|
|
466
|
+
' display: flex;',
|
|
467
|
+
' align-items: center;',
|
|
468
|
+
' gap: 8px;',
|
|
469
|
+
' flex-shrink: 0;',
|
|
440
470
|
'}',
|
|
441
471
|
|
|
442
|
-
/* ── Bot Avatar ── */
|
|
472
|
+
/* ── Bot Avatar (32px circle) ── */
|
|
443
473
|
'.wakz-avatar {',
|
|
444
|
-
' width:
|
|
445
|
-
' height:
|
|
474
|
+
' width: 32px;',
|
|
475
|
+
' height: 32px;',
|
|
446
476
|
' border-radius: 50%;',
|
|
447
|
-
' background: rgba(
|
|
477
|
+
' background: rgba(23,23,23,0.1);',
|
|
448
478
|
' display: flex;',
|
|
449
479
|
' align-items: center;',
|
|
450
480
|
' justify-content: center;',
|
|
451
481
|
' font-weight: 700;',
|
|
452
|
-
' font-size:
|
|
482
|
+
' font-size: 14px;',
|
|
453
483
|
' flex-shrink: 0;',
|
|
454
|
-
'
|
|
484
|
+
' color: var(--wakz-primary);',
|
|
455
485
|
'}',
|
|
456
486
|
|
|
457
487
|
/* ── Header Info ── */
|
|
@@ -464,139 +494,186 @@
|
|
|
464
494
|
' font-size: 15px;',
|
|
465
495
|
' font-weight: 600;',
|
|
466
496
|
' line-height: 1.3;',
|
|
497
|
+
' color: #171717;',
|
|
467
498
|
' white-space: nowrap;',
|
|
468
499
|
' overflow: hidden;',
|
|
469
500
|
' text-overflow: ellipsis;',
|
|
470
501
|
'}',
|
|
471
502
|
'.wakz-hdr-status {',
|
|
472
|
-
' font-size:
|
|
473
|
-
'
|
|
503
|
+
' font-size: 11px;',
|
|
504
|
+
' color: #A3A3A3;',
|
|
474
505
|
' display: flex;',
|
|
475
506
|
' align-items: center;',
|
|
476
|
-
' gap:
|
|
477
|
-
' margin-top:
|
|
507
|
+
' gap: 4px;',
|
|
508
|
+
' margin-top: 1px;',
|
|
478
509
|
'}',
|
|
510
|
+
|
|
511
|
+
/* ── Header Status Dot (6px) ── */
|
|
479
512
|
'.wakz-hdr-status-dot {',
|
|
480
|
-
' width:
|
|
481
|
-
' height:
|
|
513
|
+
' width: 6px;',
|
|
514
|
+
' height: 6px;',
|
|
482
515
|
' border-radius: 50%;',
|
|
483
516
|
' display: inline-block;',
|
|
484
517
|
' flex-shrink: 0;',
|
|
485
518
|
' transition: background 0.3s ease;',
|
|
486
519
|
'}',
|
|
487
|
-
'.wakz-hdr-status-dot.online { background: #
|
|
488
|
-
'.wakz-hdr-status-dot.offline { background: #
|
|
520
|
+
'.wakz-hdr-status-dot.online { background: #22c55e; }',
|
|
521
|
+
'.wakz-hdr-status-dot.offline { background: #ef4444; }',
|
|
489
522
|
|
|
490
|
-
/* ── Close Button ── */
|
|
523
|
+
/* ── Close Button (28px circle, dark) ── */
|
|
491
524
|
'.wakz-close {',
|
|
492
|
-
' width:
|
|
493
|
-
' height:
|
|
525
|
+
' width: 28px;',
|
|
526
|
+
' height: 28px;',
|
|
494
527
|
' border-radius: 50%;',
|
|
495
528
|
' border: none;',
|
|
496
|
-
' background: rgba(
|
|
497
|
-
' color: #
|
|
529
|
+
' background: rgba(23,23,23,0.05);',
|
|
530
|
+
' color: #525252;',
|
|
498
531
|
' cursor: pointer;',
|
|
499
532
|
' display: flex;',
|
|
500
533
|
' align-items: center;',
|
|
501
534
|
' justify-content: center;',
|
|
502
|
-
' transition: background var(--wakz-transition);',
|
|
535
|
+
' transition: background var(--wakz-transition), color var(--wakz-transition);',
|
|
503
536
|
' outline: none;',
|
|
504
537
|
' flex-shrink: 0;',
|
|
505
538
|
'}',
|
|
506
539
|
'.wakz-close:hover {',
|
|
507
|
-
' background: rgba(
|
|
540
|
+
' background: rgba(23,23,23,0.1);',
|
|
541
|
+
' color: #171717;',
|
|
508
542
|
'}',
|
|
509
543
|
'.wakz-close svg {',
|
|
510
|
-
' width:
|
|
511
|
-
' height:
|
|
544
|
+
' width: 16px;',
|
|
545
|
+
' height: 16px;',
|
|
512
546
|
'}',
|
|
513
547
|
|
|
514
548
|
/* ══════════════════════════════════════════════════
|
|
515
|
-
MESSAGES AREA
|
|
549
|
+
MESSAGES AREA — scrollable, padding-bottom for input
|
|
516
550
|
══════════════════════════════════════════════════ */
|
|
517
551
|
'.wakz-msgs {',
|
|
518
552
|
' flex: 1;',
|
|
519
553
|
' overflow-y: auto;',
|
|
520
554
|
' overflow-x: hidden;',
|
|
521
|
-
' padding:
|
|
555
|
+
' padding: 16px 14px 88px;',
|
|
522
556
|
' display: flex;',
|
|
523
557
|
' flex-direction: column;',
|
|
524
|
-
' gap:
|
|
558
|
+
' gap: 4px;',
|
|
525
559
|
' scroll-behavior: smooth;',
|
|
526
560
|
' background: var(--wakz-chat-bg);',
|
|
561
|
+
' position: relative;',
|
|
527
562
|
'}',
|
|
528
|
-
'.wakz-msgs::-webkit-scrollbar { width:
|
|
563
|
+
'.wakz-msgs::-webkit-scrollbar { width: 4px; }',
|
|
529
564
|
'.wakz-msgs::-webkit-scrollbar-track { background: transparent; }',
|
|
530
565
|
'.wakz-msgs::-webkit-scrollbar-thumb {',
|
|
531
|
-
' background: rgba(0,0,0,0.
|
|
566
|
+
' background: rgba(0,0,0,0.1);',
|
|
532
567
|
' border-radius: 10px;',
|
|
533
568
|
'}',
|
|
534
569
|
|
|
535
570
|
/* ══════════════════════════════════════════════════
|
|
536
|
-
MESSAGE
|
|
571
|
+
MESSAGE ROWS — new v3 styles
|
|
537
572
|
══════════════════════════════════════════════════ */
|
|
538
573
|
'.wakz-msg-row {',
|
|
539
574
|
' display: flex;',
|
|
540
|
-
' max-width: 82%;',
|
|
541
575
|
' animation: wakz-msg-in 0.3s cubic-bezier(0.4, 0, 0.2, 1) forwards;',
|
|
542
576
|
'}',
|
|
543
|
-
'.wakz-msg-row.bot {
|
|
544
|
-
'
|
|
577
|
+
'.wakz-msg-row.bot {',
|
|
578
|
+
' align-self: flex-start;',
|
|
579
|
+
' align-items: flex-start;',
|
|
580
|
+
' gap: 8px;',
|
|
581
|
+
' max-width: 80%;',
|
|
582
|
+
'}',
|
|
583
|
+
'.wakz-msg-row.user {',
|
|
584
|
+
' align-self: flex-end;',
|
|
585
|
+
' max-width: 75%;',
|
|
586
|
+
'}',
|
|
545
587
|
|
|
546
588
|
'@keyframes wakz-msg-in {',
|
|
547
589
|
' 0% { opacity: 0; transform: translateY(8px); }',
|
|
548
590
|
' 100% { opacity: 1; transform: translateY(0); }',
|
|
549
591
|
'}',
|
|
550
592
|
|
|
551
|
-
|
|
593
|
+
/* ── User Bubbles ── */
|
|
594
|
+
'.wakz-bubble.user {',
|
|
552
595
|
' padding: 10px 14px;',
|
|
553
596
|
' font-size: 14px;',
|
|
554
597
|
' line-height: 1.55;',
|
|
555
598
|
' word-wrap: break-word;',
|
|
556
599
|
' overflow-wrap: break-word;',
|
|
557
600
|
' white-space: pre-wrap;',
|
|
558
|
-
' border-radius:
|
|
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 {',
|
|
568
|
-
' color: #ffffff;',
|
|
601
|
+
' border-radius: 14px;',
|
|
569
602
|
' border-bottom-right-radius: 4px;',
|
|
570
603
|
' background: var(--wakz-primary);',
|
|
604
|
+
' color: #ffffff;',
|
|
605
|
+
' box-shadow: 0 2px 8px rgba(0,0,0,0.08);',
|
|
571
606
|
'}',
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
'
|
|
575
|
-
'
|
|
576
|
-
'
|
|
577
|
-
'
|
|
607
|
+
|
|
608
|
+
/* ── Bot Messages — NO bubble, text directly on background ── */
|
|
609
|
+
'.wakz-bot-content {',
|
|
610
|
+
' display: flex;',
|
|
611
|
+
' flex-direction: column;',
|
|
612
|
+
' gap: 2px;',
|
|
613
|
+
' min-width: 0;',
|
|
614
|
+
'}',
|
|
615
|
+
'.wakz-bot-text {',
|
|
616
|
+
' font-size: 14px;',
|
|
617
|
+
' line-height: 1.6;',
|
|
618
|
+
' color: #404040;',
|
|
619
|
+
' word-wrap: break-word;',
|
|
620
|
+
' overflow-wrap: break-word;',
|
|
621
|
+
' white-space: pre-wrap;',
|
|
622
|
+
'}',
|
|
623
|
+
'.wakz-bot-ts {',
|
|
624
|
+
' font-size: 10px;',
|
|
625
|
+
' color: #A3A3A3;',
|
|
626
|
+
'}',
|
|
627
|
+
|
|
628
|
+
/* ── Bot Avatar (22px, inline with message) ── */
|
|
629
|
+
'.wakz-bot-avatar {',
|
|
630
|
+
' width: 22px;',
|
|
631
|
+
' height: 22px;',
|
|
632
|
+
' border-radius: 50%;',
|
|
633
|
+
' background: rgba(23,23,23,0.08);',
|
|
634
|
+
' display: flex;',
|
|
635
|
+
' align-items: center;',
|
|
636
|
+
' justify-content: center;',
|
|
637
|
+
' font-weight: 700;',
|
|
638
|
+
' font-size: 10px;',
|
|
639
|
+
' flex-shrink: 0;',
|
|
640
|
+
' color: var(--wakz-primary);',
|
|
641
|
+
' margin-top: 2px;',
|
|
578
642
|
'}',
|
|
579
643
|
|
|
580
|
-
/* ──
|
|
644
|
+
/* ── User Timestamp ── */
|
|
581
645
|
'.wakz-ts {',
|
|
582
646
|
' display: block;',
|
|
583
|
-
' font-size:
|
|
584
|
-
' opacity: 0.
|
|
585
|
-
' margin-top:
|
|
586
|
-
'}',
|
|
587
|
-
'.wakz-bubble.user .wakz-ts {',
|
|
647
|
+
' font-size: 10px;',
|
|
648
|
+
' opacity: 0.7;',
|
|
649
|
+
' margin-top: 3px;',
|
|
588
650
|
' text-align: right;',
|
|
589
651
|
'}',
|
|
590
652
|
|
|
653
|
+
/* ── Error Messages ── */
|
|
654
|
+
'.wakz-bubble.error-bubble {',
|
|
655
|
+
' padding: 10px 14px;',
|
|
656
|
+
' font-size: 14px;',
|
|
657
|
+
' line-height: 1.55;',
|
|
658
|
+
' word-wrap: break-word;',
|
|
659
|
+
' overflow-wrap: break-word;',
|
|
660
|
+
' white-space: pre-wrap;',
|
|
661
|
+
' border-radius: 14px;',
|
|
662
|
+
' border-bottom-left-radius: 4px;',
|
|
663
|
+
' background: #FEF2F2;',
|
|
664
|
+
' color: #DC2626;',
|
|
665
|
+
' border: 1px solid #FECACA;',
|
|
666
|
+
'}',
|
|
667
|
+
|
|
591
668
|
/* ── Human Agent Badge ── */
|
|
592
669
|
'.wakz-human-badge {',
|
|
593
670
|
' display: inline-block;',
|
|
594
671
|
' font-size: 10px;',
|
|
595
|
-
' background: rgba(22, 163, 74, 0.
|
|
672
|
+
' background: rgba(22, 163, 74, 0.08);',
|
|
596
673
|
' color: #16A34A;',
|
|
597
|
-
' border-radius:
|
|
598
|
-
' padding:
|
|
599
|
-
' margin-top:
|
|
674
|
+
' border-radius: 6px;',
|
|
675
|
+
' padding: 2px 8px;',
|
|
676
|
+
' margin-top: 4px;',
|
|
600
677
|
' font-weight: 600;',
|
|
601
678
|
' letter-spacing: 0.2px;',
|
|
602
679
|
'}',
|
|
@@ -625,23 +702,26 @@
|
|
|
625
702
|
'}',
|
|
626
703
|
|
|
627
704
|
/* ══════════════════════════════════════════════════
|
|
628
|
-
TYPING INDICATOR
|
|
705
|
+
TYPING INDICATOR — no bubble, just dots on background
|
|
629
706
|
══════════════════════════════════════════════════ */
|
|
707
|
+
'.wakz-typing-row {',
|
|
708
|
+
' display: flex;',
|
|
709
|
+
' align-items: flex-start;',
|
|
710
|
+
' gap: 8px;',
|
|
711
|
+
' align-self: flex-start;',
|
|
712
|
+
' animation: wakz-msg-in 0.3s cubic-bezier(0.4, 0, 0.2, 1) forwards;',
|
|
713
|
+
'}',
|
|
630
714
|
'.wakz-typing {',
|
|
631
715
|
' display: flex;',
|
|
632
716
|
' align-items: center;',
|
|
633
717
|
' gap: 4px;',
|
|
634
|
-
' padding:
|
|
635
|
-
' background: #ffffff;',
|
|
636
|
-
' border-radius: var(--wakz-radius-bubble);',
|
|
637
|
-
' border-bottom-left-radius: 4px;',
|
|
638
|
-
' box-shadow: var(--wakz-shadow-sm);',
|
|
718
|
+
' padding: 6px 0;',
|
|
639
719
|
'}',
|
|
640
720
|
'.wakz-typing-dot {',
|
|
641
|
-
' width:
|
|
642
|
-
' height:
|
|
721
|
+
' width: 6px;',
|
|
722
|
+
' height: 6px;',
|
|
643
723
|
' border-radius: 50%;',
|
|
644
|
-
' background: #
|
|
724
|
+
' background: #A3A3A3;',
|
|
645
725
|
' animation: wakz-bounce-dot 1.4s ease-in-out infinite;',
|
|
646
726
|
'}',
|
|
647
727
|
'.wakz-typing-dot:nth-child(2) { animation-delay: 0.16s; }',
|
|
@@ -652,45 +732,53 @@
|
|
|
652
732
|
'}',
|
|
653
733
|
|
|
654
734
|
/* ══════════════════════════════════════════════════
|
|
655
|
-
INPUT AREA
|
|
735
|
+
INPUT AREA — Floating, glass effect, inside messages
|
|
656
736
|
══════════════════════════════════════════════════ */
|
|
657
|
-
'.wakz-input-
|
|
658
|
-
'
|
|
659
|
-
'
|
|
660
|
-
'
|
|
661
|
-
'
|
|
737
|
+
'.wakz-input-area {',
|
|
738
|
+
' position: absolute;',
|
|
739
|
+
' bottom: 12px;',
|
|
740
|
+
' left: 12px;',
|
|
741
|
+
' right: 12px;',
|
|
662
742
|
' display: flex;',
|
|
663
743
|
' align-items: flex-end;',
|
|
664
744
|
' gap: 8px;',
|
|
745
|
+
' padding: 8px 10px 8px 14px;',
|
|
746
|
+
' border-radius: 12px;',
|
|
747
|
+
' background: rgba(255,255,255,0.85);',
|
|
748
|
+
' backdrop-filter: blur(24px);',
|
|
749
|
+
' -webkit-backdrop-filter: blur(24px);',
|
|
750
|
+
' border: 1px solid rgba(229,229,229,0.6);',
|
|
751
|
+
' box-shadow: 0 4px 12px rgba(0,0,0,0.05);',
|
|
752
|
+
' z-index: 10;',
|
|
665
753
|
'}',
|
|
666
754
|
'.wakz-input {',
|
|
667
755
|
' flex: 1;',
|
|
668
|
-
' border:
|
|
669
|
-
' border-radius:
|
|
670
|
-
' padding:
|
|
756
|
+
' border: none;',
|
|
757
|
+
' border-radius: 0;',
|
|
758
|
+
' padding: 6px 4px;',
|
|
671
759
|
' font-size: 14px;',
|
|
672
760
|
' line-height: 1.4;',
|
|
673
761
|
' outline: none;',
|
|
674
762
|
' font-family: var(--wakz-font);',
|
|
675
|
-
' transition: border-color var(--wakz-transition);',
|
|
676
763
|
' resize: none;',
|
|
677
|
-
' max-height:
|
|
764
|
+
' max-height: 80px;',
|
|
765
|
+
' min-height: 22px;',
|
|
678
766
|
' overflow-y: auto;',
|
|
679
|
-
' background:
|
|
680
|
-
' color: #
|
|
767
|
+
' background: transparent;',
|
|
768
|
+
' color: #171717;',
|
|
681
769
|
'}',
|
|
682
770
|
'.wakz-input:focus {',
|
|
683
|
-
'
|
|
684
|
-
' background: #ffffff;',
|
|
771
|
+
' outline: none;',
|
|
685
772
|
'}',
|
|
686
773
|
'.wakz-input::placeholder {',
|
|
687
|
-
' color: #
|
|
774
|
+
' color: #A3A3A3;',
|
|
688
775
|
'}',
|
|
689
776
|
|
|
690
|
-
/* ── Send Button ── */
|
|
777
|
+
/* ── Send Button (36px floating circle, separate from input) ── */
|
|
691
778
|
'.wakz-send {',
|
|
692
|
-
' width:
|
|
693
|
-
' height:
|
|
779
|
+
' width: 36px;',
|
|
780
|
+
' height: 36px;',
|
|
781
|
+
' min-width: 36px;',
|
|
694
782
|
' border-radius: 50%;',
|
|
695
783
|
' border: none;',
|
|
696
784
|
' color: #ffffff;',
|
|
@@ -699,99 +787,88 @@
|
|
|
699
787
|
' align-items: center;',
|
|
700
788
|
' justify-content: center;',
|
|
701
789
|
' flex-shrink: 0;',
|
|
702
|
-
' transition:
|
|
790
|
+
' transition: transform var(--wakz-transition), opacity var(--wakz-transition), box-shadow var(--wakz-transition);',
|
|
703
791
|
' outline: none;',
|
|
704
792
|
' background: var(--wakz-primary);',
|
|
705
|
-
' box-shadow:
|
|
793
|
+
' box-shadow: 0 2px 8px rgba(0,0,0,0.12);',
|
|
706
794
|
'}',
|
|
707
795
|
'.wakz-send:hover:not(:disabled) {',
|
|
708
|
-
'
|
|
796
|
+
' transform: scale(1.05);',
|
|
797
|
+
' box-shadow: 0 4px 14px rgba(0,0,0,0.18);',
|
|
709
798
|
'}',
|
|
710
799
|
'.wakz-send:disabled {',
|
|
711
|
-
' opacity: 0.
|
|
800
|
+
' opacity: 0.3;',
|
|
712
801
|
' cursor: not-allowed;',
|
|
713
802
|
'}',
|
|
714
803
|
'.wakz-send:not(:disabled):active {',
|
|
715
|
-
' transform: scale(0.
|
|
804
|
+
' transform: scale(0.92);',
|
|
716
805
|
'}',
|
|
717
806
|
'.wakz-send svg {',
|
|
718
|
-
' width:
|
|
719
|
-
' height:
|
|
807
|
+
' width: 16px;',
|
|
808
|
+
' height: 16px;',
|
|
720
809
|
' pointer-events: none;',
|
|
721
810
|
'}',
|
|
722
811
|
|
|
723
812
|
/* ══════════════════════════════════════════════════
|
|
724
|
-
WELCOME MESSAGE
|
|
813
|
+
WELCOME MESSAGE — same as bot (no bubble)
|
|
725
814
|
══════════════════════════════════════════════════ */
|
|
726
815
|
'.wakz-welcome-wrap {',
|
|
727
816
|
' display: flex;',
|
|
817
|
+
' align-items: flex-start;',
|
|
728
818
|
' gap: 8px;',
|
|
729
|
-
' max-width:
|
|
819
|
+
' max-width: 80%;',
|
|
730
820
|
' align-self: flex-start;',
|
|
731
821
|
' animation: wakz-msg-in 0.35s cubic-bezier(0.4, 0, 0.2, 1) forwards;',
|
|
732
822
|
'}',
|
|
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;',
|
|
747
|
-
'}',
|
|
748
823
|
|
|
749
824
|
/* ══════════════════════════════════════════════════
|
|
750
825
|
RTL SUPPORT
|
|
751
826
|
══════════════════════════════════════════════════ */
|
|
752
827
|
'.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;',
|
|
756
|
-
'}',
|
|
757
828
|
'.wakz-rtl .wakz-bubble.user {',
|
|
758
|
-
' border-bottom-right-radius:
|
|
829
|
+
' border-bottom-right-radius: 14px;',
|
|
759
830
|
' border-bottom-left-radius: 4px;',
|
|
760
831
|
'}',
|
|
761
|
-
'.wakz-rtl .wakz-
|
|
762
|
-
' border-bottom-left-radius:
|
|
832
|
+
'.wakz-rtl .wakz-bubble.error-bubble {',
|
|
833
|
+
' border-bottom-left-radius: 14px;',
|
|
763
834
|
' border-bottom-right-radius: 4px;',
|
|
764
835
|
'}',
|
|
765
|
-
'.wakz-rtl .wakz-
|
|
836
|
+
'.wakz-rtl .wakz-ts {',
|
|
766
837
|
' text-align: left;',
|
|
767
838
|
'}',
|
|
768
839
|
|
|
769
840
|
/* ══════════════════════════════════════════════════
|
|
770
|
-
MOBILE RESPONSIVE
|
|
841
|
+
MOBILE RESPONSIVE — centered modal, NOT fullscreen
|
|
771
842
|
══════════════════════════════════════════════════ */
|
|
772
843
|
'@media (max-width: 480px) {',
|
|
773
844
|
' .wakz-window {',
|
|
774
|
-
' width: 100vw !important;',
|
|
775
|
-
' height:
|
|
776
|
-
' min-height: 100vh !important;',
|
|
777
|
-
' max-height: 100vh !important;',
|
|
778
|
-
' bottom: 0 !important;',
|
|
779
|
-
' top: 0 !important;',
|
|
780
|
-
' right: 0 !important;',
|
|
781
|
-
' left: 0 !important;',
|
|
782
|
-
' border-radius: 0 !important;',
|
|
845
|
+
' width: calc(100vw - 32px) !important;',
|
|
846
|
+
' height: min(500px, 75vh) !important;',
|
|
783
847
|
' }',
|
|
784
848
|
' .wakz-window.wakz-visible {',
|
|
785
|
-
' transform:
|
|
849
|
+
' transform: translate(-50%, -50%) scale(1);',
|
|
850
|
+
' }',
|
|
851
|
+
' .wakz-fab {',
|
|
852
|
+
' width: 44px;',
|
|
853
|
+
' height: 44px;',
|
|
786
854
|
' }',
|
|
787
855
|
' .wakz-fab-pos-br { bottom: 16px; right: 16px; }',
|
|
788
856
|
' .wakz-fab-pos-bl { bottom: 16px; left: 16px; }',
|
|
857
|
+
' .wakz-input-area {',
|
|
858
|
+
' left: 10px;',
|
|
859
|
+
' right: 10px;',
|
|
860
|
+
' bottom: 10px;',
|
|
861
|
+
' padding: 6px 8px 6px 12px;',
|
|
862
|
+
' }',
|
|
863
|
+
' .wakz-msgs {',
|
|
864
|
+
' padding: 14px 12px 84px;',
|
|
865
|
+
' }',
|
|
789
866
|
'}'
|
|
790
867
|
].join('\n');
|
|
791
868
|
};
|
|
792
869
|
|
|
793
870
|
/* ════════════════════════════════════════════════════════════════
|
|
794
|
-
DOM CREATION
|
|
871
|
+
DOM CREATION — v3.0.0 new structure
|
|
795
872
|
════════════════════════════════════════════════════════════════ */
|
|
796
873
|
|
|
797
874
|
WAKZWidget.prototype._createDOM = function () {
|
|
@@ -810,31 +887,38 @@
|
|
|
810
887
|
self._shadow.appendChild(self._styleEl);
|
|
811
888
|
self._shadow.appendChild(self._root);
|
|
812
889
|
|
|
813
|
-
/* ══════════════ FLOATING ACTION BUTTON ══════════════ */
|
|
890
|
+
/* ══════════════ FLOATING ACTION BUTTON (FAB) ══════════════ */
|
|
814
891
|
self._toggleBtn = _el('button', {
|
|
815
892
|
className: 'wakz-fab wakz-fab-pos-' + posClass,
|
|
816
893
|
'aria-label': str.openChat
|
|
817
894
|
});
|
|
818
895
|
self._toggleBtn.innerHTML = _ICONS.chatBubble;
|
|
819
896
|
|
|
820
|
-
/* Status dot on FAB */
|
|
897
|
+
/* Status dot on FAB (10px) */
|
|
821
898
|
self._statusDot = _el('span', { className: 'wakz-fab-dot ' + (self.config.online ? 'online' : 'offline') });
|
|
822
899
|
self._statusDot.style.display = self.config.showStatus ? '' : 'none';
|
|
823
900
|
self._toggleBtn.appendChild(self._statusDot);
|
|
824
901
|
self._root.appendChild(self._toggleBtn);
|
|
825
902
|
|
|
826
|
-
/* ══════════════
|
|
827
|
-
self.
|
|
903
|
+
/* ══════════════ OVERLAY / BACKDROP ══════════════ */
|
|
904
|
+
self._overlay = _el('div', { className: 'wakz-overlay' });
|
|
905
|
+
self._root.appendChild(self._overlay);
|
|
906
|
+
|
|
907
|
+
/* ══════════════ CHAT WINDOW (Centered Modal) ══════════════ */
|
|
908
|
+
self._chatWindow = _el('div', { className: 'wakz-window' });
|
|
828
909
|
|
|
829
|
-
/* ── Header ── */
|
|
910
|
+
/* ── Header (glass effect, dark text) ── */
|
|
830
911
|
var headerEl = _el('div', { className: 'wakz-hdr' });
|
|
831
912
|
|
|
913
|
+
/* Left side: avatar + name + status */
|
|
832
914
|
var headerLeft = _el('div', { className: 'wakz-hdr-left' });
|
|
833
915
|
var avatarLetter = (self.config.botName || 'W')[0].toUpperCase();
|
|
834
|
-
|
|
916
|
+
self._headerAvatarEl = _el('div', { className: 'wakz-avatar' }, [avatarLetter]);
|
|
917
|
+
headerLeft.appendChild(self._headerAvatarEl);
|
|
835
918
|
|
|
836
919
|
var headerInfo = _el('div', { className: 'wakz-hdr-info' });
|
|
837
|
-
|
|
920
|
+
self._headerNameEl = _el('span', { className: 'wakz-hdr-name' }, [self.config.botName]);
|
|
921
|
+
headerInfo.appendChild(self._headerNameEl);
|
|
838
922
|
|
|
839
923
|
var statusLine = _el('span', { className: 'wakz-hdr-status' });
|
|
840
924
|
self._headerStatusDot = _el('span', { className: 'wakz-hdr-status-dot ' + (self.config.online ? 'online' : 'offline') });
|
|
@@ -846,17 +930,20 @@
|
|
|
846
930
|
headerLeft.appendChild(headerInfo);
|
|
847
931
|
headerEl.appendChild(headerLeft);
|
|
848
932
|
|
|
933
|
+
/* Right side: status dot + text + close button */
|
|
934
|
+
var headerRight = _el('div', { className: 'wakz-hdr-right' });
|
|
849
935
|
var closeBtn = _el('button', { className: 'wakz-close', 'aria-label': str.closeChat });
|
|
850
936
|
closeBtn.innerHTML = _ICONS.close;
|
|
851
|
-
|
|
937
|
+
headerRight.appendChild(closeBtn);
|
|
938
|
+
headerEl.appendChild(headerRight);
|
|
939
|
+
|
|
852
940
|
self._chatWindow.appendChild(headerEl);
|
|
853
941
|
|
|
854
|
-
/* ── Messages Container ── */
|
|
942
|
+
/* ── Messages Container (with floating input area inside) ── */
|
|
855
943
|
self._messagesContainer = _el('div', { className: 'wakz-msgs' });
|
|
856
|
-
self._chatWindow.appendChild(self._messagesContainer);
|
|
857
944
|
|
|
858
|
-
/* ── Input Area ── */
|
|
859
|
-
|
|
945
|
+
/* ── Floating Input Area (inside messages container) ── */
|
|
946
|
+
self._inputArea = _el('div', { className: 'wakz-input-area' });
|
|
860
947
|
|
|
861
948
|
self._inputEl = _el('textarea', {
|
|
862
949
|
className: 'wakz-input',
|
|
@@ -864,15 +951,17 @@
|
|
|
864
951
|
rows: 1,
|
|
865
952
|
dir: isRtl ? 'rtl' : 'ltr'
|
|
866
953
|
});
|
|
867
|
-
|
|
954
|
+
self._inputArea.appendChild(self._inputEl);
|
|
868
955
|
|
|
869
956
|
self._sendBtn = _el('button', {
|
|
870
957
|
className: 'wakz-send',
|
|
871
958
|
'aria-label': str.sendMessage
|
|
872
959
|
});
|
|
873
960
|
self._sendBtn.innerHTML = _ICONS.send;
|
|
874
|
-
|
|
875
|
-
|
|
961
|
+
self._inputArea.appendChild(self._sendBtn);
|
|
962
|
+
|
|
963
|
+
self._messagesContainer.appendChild(self._inputArea);
|
|
964
|
+
self._chatWindow.appendChild(self._messagesContainer);
|
|
876
965
|
|
|
877
966
|
self._root.appendChild(self._chatWindow);
|
|
878
967
|
};
|
|
@@ -889,6 +978,11 @@
|
|
|
889
978
|
self.toggleChat(!self.isOpen);
|
|
890
979
|
});
|
|
891
980
|
|
|
981
|
+
/* Overlay click to close */
|
|
982
|
+
self._overlay.addEventListener('click', function () {
|
|
983
|
+
self.toggleChat(false);
|
|
984
|
+
});
|
|
985
|
+
|
|
892
986
|
/* Close button click */
|
|
893
987
|
self._chatWindow.querySelector('.wakz-close').addEventListener('click', function () {
|
|
894
988
|
self.toggleChat(false);
|
|
@@ -907,10 +1001,10 @@
|
|
|
907
1001
|
}
|
|
908
1002
|
});
|
|
909
1003
|
|
|
910
|
-
/* Auto-resize textarea */
|
|
1004
|
+
/* Auto-resize textarea (max 80px) */
|
|
911
1005
|
self._inputEl.addEventListener('input', function () {
|
|
912
1006
|
self._inputEl.style.height = 'auto';
|
|
913
|
-
self._inputEl.style.height = Math.min(self._inputEl.scrollHeight,
|
|
1007
|
+
self._inputEl.style.height = Math.min(self._inputEl.scrollHeight, 80) + 'px';
|
|
914
1008
|
});
|
|
915
1009
|
|
|
916
1010
|
/* Escape to close */
|
|
@@ -925,7 +1019,6 @@
|
|
|
925
1019
|
|
|
926
1020
|
WAKZWidget.prototype._playFabEntrance = function () {
|
|
927
1021
|
var self = this;
|
|
928
|
-
/* Start invisible, then animate after a brief delay */
|
|
929
1022
|
self._toggleBtn.style.opacity = '0';
|
|
930
1023
|
setTimeout(function () {
|
|
931
1024
|
self._toggleBtn.style.opacity = '';
|
|
@@ -942,6 +1035,8 @@
|
|
|
942
1035
|
self.isOpen = open;
|
|
943
1036
|
if (open) {
|
|
944
1037
|
self._chatWindow.classList.add('wakz-visible');
|
|
1038
|
+
self._overlay.classList.add('wakz-visible');
|
|
1039
|
+
self._toggleBtn.classList.add('wakz-fab-hidden');
|
|
945
1040
|
/* Fetch history on first open (after config is loaded) */
|
|
946
1041
|
if (self._configLoaded && !self._hasFetchedHistory) {
|
|
947
1042
|
self._fetchHistory();
|
|
@@ -955,6 +1050,8 @@
|
|
|
955
1050
|
setTimeout(function () { self._inputEl.focus(); }, 320);
|
|
956
1051
|
} else {
|
|
957
1052
|
self._chatWindow.classList.remove('wakz-visible');
|
|
1053
|
+
self._overlay.classList.remove('wakz-visible');
|
|
1054
|
+
self._toggleBtn.classList.remove('wakz-fab-hidden');
|
|
958
1055
|
/* Stop polling when chat is closed */
|
|
959
1056
|
self._stopPolling();
|
|
960
1057
|
}
|
|
@@ -988,7 +1085,6 @@
|
|
|
988
1085
|
}
|
|
989
1086
|
})
|
|
990
1087
|
.catch(function (err) {
|
|
991
|
-
/* AbortError means timeout */
|
|
992
1088
|
self._handleConfigError();
|
|
993
1089
|
});
|
|
994
1090
|
};
|
|
@@ -1027,31 +1123,29 @@
|
|
|
1027
1123
|
hostStyle.setProperty('--wakz-btn', cfg.btnColor);
|
|
1028
1124
|
hostStyle.setProperty('--wakz-chat-bg', cfg.chatBg);
|
|
1029
1125
|
hostStyle.setProperty('--wakz-widget-bg', cfg.widgetBg);
|
|
1126
|
+
hostStyle.setProperty('--wakz-icon-radius', cfg.iconRadius || '50%');
|
|
1030
1127
|
|
|
1031
1128
|
/* ── RTL ── */
|
|
1032
1129
|
if (isRtl) self._root.classList.add('wakz-rtl');
|
|
1033
1130
|
else self._root.classList.remove('wakz-rtl');
|
|
1034
1131
|
|
|
1035
1132
|
/* ── FAB ── */
|
|
1036
|
-
self._toggleBtn.className = 'wakz-fab wakz-fab-pos-' + posClass + ' wakz-fab-enter';
|
|
1133
|
+
self._toggleBtn.className = 'wakz-fab wakz-fab-pos-' + posClass + ' wakz-fab-enter' + (self.isOpen ? ' wakz-fab-hidden' : '');
|
|
1037
1134
|
self._toggleBtn.setAttribute('aria-label', str.openChat);
|
|
1038
1135
|
|
|
1039
1136
|
/* ── FAB Status Dot ── */
|
|
1040
1137
|
self._statusDot.className = 'wakz-fab-dot ' + (cfg.online ? 'online' : 'offline');
|
|
1041
1138
|
self._statusDot.style.display = cfg.showStatus ? '' : 'none';
|
|
1042
1139
|
|
|
1043
|
-
/* ── Window
|
|
1044
|
-
self._chatWindow.className = 'wakz-window
|
|
1140
|
+
/* ── Window ── */
|
|
1141
|
+
self._chatWindow.className = 'wakz-window' + (self.isOpen ? ' wakz-visible' : '');
|
|
1045
1142
|
|
|
1046
|
-
/* ──
|
|
1047
|
-
|
|
1048
|
-
if (headerEl) headerEl.style.background = cfg.primaryColor;
|
|
1143
|
+
/* ── Overlay ── */
|
|
1144
|
+
self._overlay.className = 'wakz-overlay' + (self.isOpen ? ' wakz-visible' : '');
|
|
1049
1145
|
|
|
1050
1146
|
/* ── Bot Name & Avatar ── */
|
|
1051
|
-
|
|
1052
|
-
if (
|
|
1053
|
-
var avatarEl = self._chatWindow.querySelector('.wakz-avatar');
|
|
1054
|
-
if (avatarEl) avatarEl.textContent = (cfg.botName || 'W')[0].toUpperCase();
|
|
1147
|
+
if (self._headerNameEl) self._headerNameEl.textContent = cfg.botName;
|
|
1148
|
+
if (self._headerAvatarEl) self._headerAvatarEl.textContent = (cfg.botName || 'W')[0].toUpperCase();
|
|
1055
1149
|
|
|
1056
1150
|
/* ── Header Status ── */
|
|
1057
1151
|
if (self._headerStatusDot) {
|
|
@@ -1098,7 +1192,6 @@
|
|
|
1098
1192
|
.then(function (res) { return res.json(); })
|
|
1099
1193
|
.then(function (data) {
|
|
1100
1194
|
if (data && data.success && data.messages && data.messages.length > 0) {
|
|
1101
|
-
/* Clear any existing messages (e.g., welcome) and render history */
|
|
1102
1195
|
self._clearMessages();
|
|
1103
1196
|
self._knownMessageIds = {};
|
|
1104
1197
|
var msgs = data.messages;
|
|
@@ -1109,7 +1202,6 @@
|
|
|
1109
1202
|
var role = m.role === 'user' ? 'user' : 'bot';
|
|
1110
1203
|
self._appendMessage(role, m.content, false, m.createdAt, m.role === 'human_agent');
|
|
1111
1204
|
}
|
|
1112
|
-
/* Set last poll time to the most recent message */
|
|
1113
1205
|
var lastMsg = msgs[msgs.length - 1];
|
|
1114
1206
|
if (lastMsg && lastMsg.createdAt) {
|
|
1115
1207
|
self._lastPollTime = lastMsg.createdAt;
|
|
@@ -1127,7 +1219,7 @@
|
|
|
1127
1219
|
|
|
1128
1220
|
WAKZWidget.prototype._startPolling = function () {
|
|
1129
1221
|
var self = this;
|
|
1130
|
-
self._stopPolling();
|
|
1222
|
+
self._stopPolling();
|
|
1131
1223
|
if (!self._lastPollTime) {
|
|
1132
1224
|
self._lastPollTime = new Date().toISOString();
|
|
1133
1225
|
}
|
|
@@ -1147,7 +1239,7 @@
|
|
|
1147
1239
|
WAKZWidget.prototype._pollForNewMessages = function () {
|
|
1148
1240
|
var self = this;
|
|
1149
1241
|
if (!self.server || !self.apiKey || !self._lastPollTime) return;
|
|
1150
|
-
/* Skip polling while waiting for our own reply
|
|
1242
|
+
/* Skip polling while waiting for our own reply */
|
|
1151
1243
|
if (self._pendingReply) return;
|
|
1152
1244
|
|
|
1153
1245
|
var url = self.server + '/api/v1/embed/chat?api_key=' +
|
|
@@ -1171,7 +1263,6 @@
|
|
|
1171
1263
|
var isHumanAgent = m.role === 'human_agent';
|
|
1172
1264
|
self._appendMessage(role, m.content, false, m.createdAt, isHumanAgent);
|
|
1173
1265
|
}
|
|
1174
|
-
/* Update last poll time */
|
|
1175
1266
|
var lastMsg = msgs[msgs.length - 1];
|
|
1176
1267
|
if (lastMsg && lastMsg.createdAt) {
|
|
1177
1268
|
self._lastPollTime = lastMsg.createdAt;
|
|
@@ -1189,13 +1280,19 @@
|
|
|
1189
1280
|
|
|
1190
1281
|
WAKZWidget.prototype._clearMessages = function () {
|
|
1191
1282
|
var self = this;
|
|
1192
|
-
|
|
1283
|
+
/* Remove all children except the input area */
|
|
1284
|
+
var children = Array.prototype.slice.call(self._messagesContainer.children);
|
|
1285
|
+
for (var i = 0; i < children.length; i++) {
|
|
1286
|
+
if (children[i] !== self._inputArea) {
|
|
1287
|
+
self._messagesContainer.removeChild(children[i]);
|
|
1288
|
+
}
|
|
1289
|
+
}
|
|
1193
1290
|
self.messages = [];
|
|
1194
1291
|
self._knownMessageIds = {};
|
|
1195
1292
|
};
|
|
1196
1293
|
|
|
1197
1294
|
/* ════════════════════════════════════════════════════════════════
|
|
1198
|
-
APPEND WELCOME MESSAGE
|
|
1295
|
+
APPEND WELCOME MESSAGE — no bubble style, matching bot messages
|
|
1199
1296
|
════════════════════════════════════════════════════════════════ */
|
|
1200
1297
|
|
|
1201
1298
|
WAKZWidget.prototype._appendWelcomeMessage = function (text) {
|
|
@@ -1204,36 +1301,30 @@
|
|
|
1204
1301
|
|
|
1205
1302
|
var wrap = _el('div', { className: 'wakz-welcome-wrap' });
|
|
1206
1303
|
|
|
1207
|
-
/* Bot avatar */
|
|
1208
|
-
var avatar = _el('div', { className: 'wakz-
|
|
1304
|
+
/* Bot avatar (22px) */
|
|
1305
|
+
var avatar = _el('div', { className: 'wakz-bot-avatar' },
|
|
1209
1306
|
[(self.config.botName || 'W')[0].toUpperCase()]);
|
|
1210
1307
|
wrap.appendChild(avatar);
|
|
1211
1308
|
|
|
1212
|
-
/*
|
|
1213
|
-
var
|
|
1214
|
-
|
|
1309
|
+
/* Bot text content — no bubble */
|
|
1310
|
+
var content = _el('div', { className: 'wakz-bot-content' });
|
|
1311
|
+
content.appendChild(_el('div', { className: 'wakz-bot-text' }, [text]));
|
|
1312
|
+
wrap.appendChild(content);
|
|
1215
1313
|
|
|
1216
|
-
|
|
1314
|
+
/* Insert before input area */
|
|
1315
|
+
self._messagesContainer.insertBefore(wrap, self._inputArea);
|
|
1217
1316
|
self.messages.push({ sender: 'bot', text: text });
|
|
1218
1317
|
self._scrollToBottom();
|
|
1219
1318
|
};
|
|
1220
1319
|
|
|
1221
1320
|
/* ════════════════════════════════════════════════════════════════
|
|
1222
|
-
APPEND MESSAGE TO CHAT
|
|
1321
|
+
APPEND MESSAGE TO CHAT — v3 rendering
|
|
1223
1322
|
════════════════════════════════════════════════════════════════ */
|
|
1224
1323
|
|
|
1225
1324
|
WAKZWidget.prototype._appendMessage = function (sender, text, isError, timestamp, isHumanAgent) {
|
|
1226
1325
|
var self = this;
|
|
1227
1326
|
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
var bubbleClass = 'wakz-bubble ' + sender;
|
|
1231
|
-
if (isError) bubbleClass += ' error-bubble';
|
|
1232
|
-
var bubble = _el('div', { className: bubbleClass });
|
|
1233
|
-
|
|
1234
|
-
bubble.appendChild(document.createTextNode(text));
|
|
1235
|
-
|
|
1236
|
-
/* Timestamp */
|
|
1327
|
+
/* ── Format timestamp ── */
|
|
1237
1328
|
var tsText = '';
|
|
1238
1329
|
if (timestamp) {
|
|
1239
1330
|
try {
|
|
@@ -1249,69 +1340,135 @@
|
|
|
1249
1340
|
tsText = now.getHours().toString().padStart(2, '0') + ':' +
|
|
1250
1341
|
now.getMinutes().toString().padStart(2, '0');
|
|
1251
1342
|
}
|
|
1252
|
-
bubble.appendChild(_el('span', { className: 'wakz-ts' }, [tsText]));
|
|
1253
1343
|
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
var
|
|
1257
|
-
|
|
1258
|
-
bubble
|
|
1259
|
-
|
|
1344
|
+
if (sender === 'user') {
|
|
1345
|
+
/* ── USER MESSAGE: colored bubble with tail ── */
|
|
1346
|
+
var row = _el('div', { className: 'wakz-msg-row user' });
|
|
1347
|
+
|
|
1348
|
+
var bubbleClass = 'wakz-bubble user';
|
|
1349
|
+
if (isError) bubbleClass += ' error-bubble';
|
|
1350
|
+
var bubble = _el('div', { className: bubbleClass });
|
|
1351
|
+
bubble.appendChild(document.createTextNode(text));
|
|
1352
|
+
bubble.appendChild(_el('span', { className: 'wakz-ts' }, [tsText]));
|
|
1353
|
+
|
|
1354
|
+
/* Retry button for error user messages */
|
|
1355
|
+
if (isError) {
|
|
1356
|
+
var str = _strings(self.config.language);
|
|
1357
|
+
var retryBtn = _el('button', { className: 'wakz-retry' }, [
|
|
1358
|
+
_ICONS.error + ' ' + str.retry
|
|
1359
|
+
]);
|
|
1360
|
+
(function (rowCapture) {
|
|
1361
|
+
retryBtn.addEventListener('click', function () {
|
|
1362
|
+
if (rowCapture.parentNode) rowCapture.parentNode.removeChild(rowCapture);
|
|
1363
|
+
for (var i = self.messages.length - 1; i >= 0; i--) {
|
|
1364
|
+
if (self.messages[i].text === text && self.messages[i].isError) {
|
|
1365
|
+
self.messages.splice(i, 1);
|
|
1366
|
+
break;
|
|
1367
|
+
}
|
|
1368
|
+
}
|
|
1369
|
+
var lastUserMsg = null;
|
|
1370
|
+
for (var j = self.messages.length - 1; j >= 0; j--) {
|
|
1371
|
+
if (self.messages[j].sender === 'user') {
|
|
1372
|
+
lastUserMsg = self.messages[j].text;
|
|
1373
|
+
break;
|
|
1374
|
+
}
|
|
1375
|
+
}
|
|
1376
|
+
if (lastUserMsg) self._sendToAPI(lastUserMsg);
|
|
1377
|
+
});
|
|
1378
|
+
})(row);
|
|
1379
|
+
bubble.appendChild(retryBtn);
|
|
1380
|
+
}
|
|
1260
1381
|
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
|
|
1382
|
+
row.appendChild(bubble);
|
|
1383
|
+
self._messagesContainer.insertBefore(row, self._inputArea);
|
|
1384
|
+
self.messages.push({ sender: sender, text: text, isError: !!isError });
|
|
1385
|
+
} else {
|
|
1386
|
+
/* ── BOT / HUMAN AGENT MESSAGE: no bubble, text on background ── */
|
|
1387
|
+
var botRow = _el('div', { className: 'wakz-msg-row bot' });
|
|
1388
|
+
|
|
1389
|
+
/* Small bot avatar (22px) */
|
|
1390
|
+
var botAvatar = _el('div', { className: 'wakz-bot-avatar' },
|
|
1391
|
+
[(self.config.botName || 'W')[0].toUpperCase()]);
|
|
1392
|
+
botRow.appendChild(botAvatar);
|
|
1393
|
+
|
|
1394
|
+
/* Content wrapper */
|
|
1395
|
+
var botContent = _el('div', { className: 'wakz-bot-content' });
|
|
1396
|
+
|
|
1397
|
+
if (isError) {
|
|
1398
|
+
/* Error: subtle red area */
|
|
1399
|
+
var errBubble = _el('div', { className: 'wakz-bubble error-bubble' });
|
|
1400
|
+
errBubble.appendChild(document.createTextNode(text));
|
|
1401
|
+
|
|
1402
|
+
var errStr = _strings(self.config.language);
|
|
1403
|
+
var errRetryBtn = _el('button', { className: 'wakz-retry' }, [
|
|
1404
|
+
_ICONS.error + ' ' + errStr.retry
|
|
1405
|
+
]);
|
|
1406
|
+
(function (rowCapture, errText) {
|
|
1407
|
+
errRetryBtn.addEventListener('click', function () {
|
|
1408
|
+
if (rowCapture.parentNode) rowCapture.parentNode.removeChild(rowCapture);
|
|
1409
|
+
for (var i = self.messages.length - 1; i >= 0; i--) {
|
|
1410
|
+
if (self.messages[i].text === errText && self.messages[i].isError) {
|
|
1411
|
+
self.messages.splice(i, 1);
|
|
1412
|
+
break;
|
|
1413
|
+
}
|
|
1276
1414
|
}
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
break;
|
|
1415
|
+
var lastUserMsg = null;
|
|
1416
|
+
for (var j = self.messages.length - 1; j >= 0; j--) {
|
|
1417
|
+
if (self.messages[j].sender === 'user') {
|
|
1418
|
+
lastUserMsg = self.messages[j].text;
|
|
1419
|
+
break;
|
|
1420
|
+
}
|
|
1284
1421
|
}
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
});
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
|
|
1422
|
+
if (lastUserMsg) self._sendToAPI(lastUserMsg);
|
|
1423
|
+
});
|
|
1424
|
+
})(botRow, text);
|
|
1425
|
+
errBubble.appendChild(errRetryBtn);
|
|
1426
|
+
botContent.appendChild(errBubble);
|
|
1427
|
+
} else {
|
|
1428
|
+
/* Normal bot/human message: no bubble */
|
|
1429
|
+
botContent.appendChild(_el('div', { className: 'wakz-bot-text' }, [text]));
|
|
1291
1430
|
|
|
1292
|
-
|
|
1293
|
-
|
|
1431
|
+
/* Timestamp */
|
|
1432
|
+
botContent.appendChild(_el('span', { className: 'wakz-bot-ts' }, [tsText]));
|
|
1433
|
+
|
|
1434
|
+
/* Human agent badge */
|
|
1435
|
+
if (isHumanAgent) {
|
|
1436
|
+
var iStr = _strings(self.config.language);
|
|
1437
|
+
var badge = _el('span', { className: 'wakz-human-badge' }, [iStr.humanBadge || '👤 فريق الدعم']);
|
|
1438
|
+
botContent.appendChild(badge);
|
|
1439
|
+
}
|
|
1440
|
+
}
|
|
1441
|
+
|
|
1442
|
+
botRow.appendChild(botContent);
|
|
1443
|
+
self._messagesContainer.insertBefore(botRow, self._inputArea);
|
|
1444
|
+
self.messages.push({ sender: sender, text: text, isError: !!isError });
|
|
1445
|
+
}
|
|
1294
1446
|
|
|
1295
|
-
self.messages.push({ sender: sender, text: text, isError: !!isError });
|
|
1296
1447
|
self._scrollToBottom();
|
|
1297
1448
|
};
|
|
1298
1449
|
|
|
1299
1450
|
/* ════════════════════════════════════════════════════════════════
|
|
1300
|
-
TYPING INDICATOR
|
|
1451
|
+
TYPING INDICATOR — no bubble, dots on background with avatar
|
|
1301
1452
|
════════════════════════════════════════════════════════════════ */
|
|
1302
1453
|
|
|
1303
1454
|
WAKZWidget.prototype._showTyping = function () {
|
|
1304
1455
|
var self = this;
|
|
1305
1456
|
if (self._typingEl) return;
|
|
1306
1457
|
|
|
1307
|
-
self._typingEl = _el('div', { className: 'wakz-
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1458
|
+
self._typingEl = _el('div', { className: 'wakz-typing-row' });
|
|
1459
|
+
|
|
1460
|
+
/* Small avatar for consistency */
|
|
1461
|
+
self._typingEl.appendChild(_el('div', { className: 'wakz-bot-avatar' },
|
|
1462
|
+
[(self.config.botName || 'W')[0].toUpperCase()]));
|
|
1463
|
+
|
|
1464
|
+
/* Bouncing dots — no bubble wrapper */
|
|
1465
|
+
var typingDots = _el('div', { className: 'wakz-typing' });
|
|
1466
|
+
typingDots.appendChild(_el('span', { className: 'wakz-typing-dot' }));
|
|
1467
|
+
typingDots.appendChild(_el('span', { className: 'wakz-typing-dot' }));
|
|
1468
|
+
typingDots.appendChild(_el('span', { className: 'wakz-typing-dot' }));
|
|
1469
|
+
self._typingEl.appendChild(typingDots);
|
|
1470
|
+
|
|
1471
|
+
self._messagesContainer.insertBefore(self._typingEl, self._inputArea);
|
|
1315
1472
|
self._scrollToBottom();
|
|
1316
1473
|
};
|
|
1317
1474
|
|
|
@@ -1372,7 +1529,7 @@
|
|
|
1372
1529
|
self._sendBtn.disabled = true;
|
|
1373
1530
|
self._showTyping();
|
|
1374
1531
|
|
|
1375
|
-
/* Freeze poll time BEFORE sending
|
|
1532
|
+
/* Freeze poll time BEFORE sending */
|
|
1376
1533
|
self._lastPollTime = new Date().toISOString();
|
|
1377
1534
|
self._pendingReply = true;
|
|
1378
1535
|
|
|
@@ -1408,7 +1565,6 @@
|
|
|
1408
1565
|
} else {
|
|
1409
1566
|
self._appendMessage('bot', _strings(self.config.language).errorMsg, true);
|
|
1410
1567
|
}
|
|
1411
|
-
/* Advance poll time past any messages created during this request */
|
|
1412
1568
|
self._lastPollTime = (data && data.timestamp)
|
|
1413
1569
|
? new Date(new Date(data.timestamp).getTime() + 1000).toISOString()
|
|
1414
1570
|
: new Date(Date.now() + 1000).toISOString();
|
|
@@ -1417,7 +1573,6 @@
|
|
|
1417
1573
|
self._hideTyping();
|
|
1418
1574
|
self._pendingReply = false;
|
|
1419
1575
|
self._appendMessage('bot', _strings(self.config.language).errorMsg, true);
|
|
1420
|
-
/* Advance poll time even on error */
|
|
1421
1576
|
self._lastPollTime = new Date(Date.now() + 1000).toISOString();
|
|
1422
1577
|
})
|
|
1423
1578
|
.finally(function () {
|
|
@@ -1432,7 +1587,7 @@
|
|
|
1432
1587
|
════════════════════════════════════════════════════════════════ */
|
|
1433
1588
|
|
|
1434
1589
|
function boot() {
|
|
1435
|
-
try { new WAKZWidget(); } catch (e) { console.error('[WAKZ Widget] Init error:', e); }
|
|
1590
|
+
try { new WAKZWidget(); } catch (e) { console.error('[WAKZ Widget v3.0.0] Init error:', e); }
|
|
1436
1591
|
}
|
|
1437
1592
|
|
|
1438
1593
|
if (document.readyState === 'loading') {
|