wakz-chat-widget 1.1.0 → 2.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.
Files changed (2) hide show
  1. package/index.js +408 -245
  2. package/package.json +2 -2
package/index.js CHANGED
@@ -1,10 +1,11 @@
1
1
  /**
2
- * WAKZ Chat Widget v2.1.0
2
+ * WAKZ Chat Widget v3.0.0
3
3
  * ─────────────────────────────────────────────────────────────────
4
4
  * A production-grade, self-contained chat widget using Shadow DOM.
5
- * Competes with Intercom & Crisp in quality.
5
+ * Premium glassmorphism design matching the WAKZ site design system.
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"
8
9
  *
9
10
  * ZERO external dependencies — pure vanilla JavaScript.
10
11
  */
@@ -50,7 +51,9 @@
50
51
  if (!target && scripts.length > 0) target = scripts[scripts.length - 1];
51
52
  return {
52
53
  apiKey: (target && target.getAttribute('data-api-key')) || '',
53
- server: (target && target.getAttribute('data-server')) || ''
54
+ server: (target && target.getAttribute('data-server')) || '',
55
+ iconColor: (target && target.getAttribute('data-icon-color')) || '',
56
+ iconShape: (target && target.getAttribute('data-icon-shape')) || ''
54
57
  };
55
58
  }
56
59
 
@@ -92,12 +95,21 @@
92
95
  ════════════════════════════════════════════════════════════════ */
93
96
 
94
97
  var _ICONS = {
95
- chatBubble:
96
- '<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>',
98
+ /* Modern chat icon: rounded square with 3 horizontal chat lines */
99
+ chatIcon:
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 */
97
107
  send:
98
108
  '<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 */
99
110
  close:
100
- '<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>',
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 */
101
113
  error:
102
114
  '<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>'
103
115
  };
@@ -157,11 +169,12 @@
157
169
  botName: 'WAKZ',
158
170
  welcomeMessage: '',
159
171
  primaryColor: '#171717',
160
- chatBg: '#f8f9fa',
161
- btnColor: '#171717',
172
+ iconColor: '#171717',
173
+ iconShape: 'circle',
174
+ chatBg: '#FAFAFA',
162
175
  widgetBg: '#ffffff',
163
176
  position: 'bottom-right',
164
- language: 'en',
177
+ language: 'ar',
165
178
  showStatus: true,
166
179
  online: true
167
180
  };
@@ -182,6 +195,8 @@
182
195
  self.apiKey = attrs.apiKey;
183
196
  self.server = attrs.server;
184
197
  self.visitorId = _getVisitorId();
198
+ self._scriptIconColor = attrs.iconColor;
199
+ self._scriptIconShape = attrs.iconShape;
185
200
 
186
201
  /* ── Runtime state ── */
187
202
  self.config = Object.assign({}, _DEFAULTS);
@@ -201,6 +216,7 @@
201
216
  self._host = null;
202
217
  self._shadow = null;
203
218
  self._root = null;
219
+ self._overlayEl = null;
204
220
  self._chatWindow = null;
205
221
  self._messagesContainer = null;
206
222
  self._inputEl = null;
@@ -219,7 +235,7 @@
219
235
  }
220
236
 
221
237
  /* ════════════════════════════════════════════════════════════════
222
- CSS — Complete styling injected into Shadow DOM
238
+ CSS — Complete premium styling injected into Shadow DOM
223
239
  ════════════════════════════════════════════════════════════════ */
224
240
 
225
241
  WAKZWidget.prototype._injectCSS = function () {
@@ -230,18 +246,14 @@
230
246
  /* ── CSS Custom Properties (theming) ── */
231
247
  ':host {',
232
248
  ' --wakz-primary: #171717;',
233
- ' --wakz-btn: #171717;',
234
- ' --wakz-chat-bg: #f8f9fa;',
249
+ ' --wakz-icon-color: #171717;',
250
+ ' --wakz-chat-bg: #FAFAFA;',
235
251
  ' --wakz-widget-bg: #ffffff;',
236
- ' --wakz-radius-window: 16px;',
237
- ' --wakz-radius-bubble: 16px;',
238
- ' --wakz-radius-fab: 28px;',
239
- ' --wakz-shadow-sm: 0 1px 3px rgba(0,0,0,.08), 0 1px 2px rgba(0,0,0,.06);',
240
- ' --wakz-shadow-md: 0 4px 12px rgba(0,0,0,.1), 0 2px 4px rgba(0,0,0,.06);',
241
- ' --wakz-shadow-lg: 0 12px 40px rgba(0,0,0,.15), 0 4px 12px rgba(0,0,0,.1);',
242
- ' --wakz-shadow-xl: 0 20px 60px rgba(0,0,0,.2), 0 8px 20px rgba(0,0,0,.12);',
243
- ' --wakz-transition: 200ms ease;',
252
+ ' --wakz-radius: 20px;',
253
+ ' --wakz-radius-bubble: 18px;',
244
254
  ' --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);',
245
257
  ' all: initial;',
246
258
  ' font-family: var(--wakz-font);',
247
259
  '}',
@@ -259,34 +271,46 @@
259
271
  '.wakz-fab {',
260
272
  ' position: fixed;',
261
273
  ' z-index: 2147483647;',
262
- ' width: 56px;',
263
- ' height: 56px;',
264
- ' border-radius: var(--wakz-radius-fab);',
274
+ ' width: 52px;',
275
+ ' height: 52px;',
276
+ ' border-radius: 50%;',
265
277
  ' border: none;',
266
278
  ' cursor: pointer;',
267
279
  ' display: flex;',
268
280
  ' align-items: center;',
269
281
  ' justify-content: center;',
270
- ' box-shadow: var(--wakz-shadow-lg);',
271
- ' transition: transform var(--wakz-transition), box-shadow var(--wakz-transition);',
282
+ ' box-shadow: 0 12px 40px rgba(0,0,0,0.15), 0 4px 12px rgba(0,0,0,0.1);',
283
+ ' transition: opacity 0.3s cubic-bezier(0.16,1,0.3,1), transform 0.3s cubic-bezier(0.16,1,0.3,1), box-shadow 0.2s ease;',
272
284
  ' outline: none;',
273
285
  ' -webkit-tap-highlight-color: transparent;',
274
- ' background: var(--wakz-btn);',
286
+ ' background: var(--wakz-icon-color, #171717);',
275
287
  '}',
276
288
  '.wakz-fab:hover {',
277
289
  ' transform: scale(1.08);',
278
- ' box-shadow: var(--wakz-shadow-xl);',
290
+ ' box-shadow: 0 20px 60px rgba(0,0,0,0.2), 0 8px 20px rgba(0,0,0,0.12);',
279
291
  '}',
280
292
  '.wakz-fab:active {',
281
293
  ' transform: scale(0.96);',
282
294
  '}',
283
295
  '.wakz-fab svg {',
284
- ' width: 26px;',
285
- ' height: 26px;',
296
+ ' width: 24px;',
297
+ ' height: 24px;',
286
298
  ' color: #ffffff;',
287
299
  ' pointer-events: none;',
288
300
  '}',
289
301
 
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
+
290
314
  /* ── FAB Positioning ── */
291
315
  '.wakz-fab-pos-br { bottom: 24px; right: 24px; }',
292
316
  '.wakz-fab-pos-bl { bottom: 24px; left: 24px; }',
@@ -307,18 +331,18 @@
307
331
  ══════════════════════════════════════════════════ */
308
332
  '.wakz-fab-dot {',
309
333
  ' position: absolute;',
310
- ' top: 0px;',
311
- ' right: 0px;',
312
- ' width: 14px;',
313
- ' height: 14px;',
334
+ ' top: -1px;',
335
+ ' right: -1px;',
336
+ ' width: 12px;',
337
+ ' height: 12px;',
314
338
  ' border-radius: 50%;',
315
- ' border: 2.5px solid #ffffff;',
339
+ ' border: 2px solid #ffffff;',
316
340
  ' z-index: 2;',
317
341
  ' transition: background 0.3s ease;',
318
342
  '}',
319
343
  '.wakz-fab-pos-bl .wakz-fab-dot {',
320
344
  ' right: auto;',
321
- ' left: 0px;',
345
+ ' left: -1px;',
322
346
  '}',
323
347
 
324
348
  /* ── Online (green pulse) ── */
@@ -339,45 +363,60 @@
339
363
  ' animation: none;',
340
364
  '}',
341
365
 
342
- /* ── Error (red, no pulse) ── */
343
- '.wakz-fab-dot.error {',
344
- ' background: #ef4444;',
345
- ' animation: none;',
366
+ /* ══════════════════════════════════════════════════
367
+ OVERLAY BACKDROP
368
+ ══════════════════════════════════════════════════ */
369
+ '.wakz-overlay {',
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;',
346
386
  '}',
347
387
 
348
388
  /* ══════════════════════════════════════════════════
349
- CHAT WINDOW
389
+ CHAT WINDOW — Centered Modal
350
390
  ══════════════════════════════════════════════════ */
351
391
  '.wakz-window {',
352
392
  ' position: fixed;',
353
393
  ' z-index: 2147483646;',
354
- ' width: 380px;',
355
- ' min-height: 500px;',
356
- ' max-height: 70vh;',
357
- ' border-radius: var(--wakz-radius-window);',
394
+ ' top: 50%;',
395
+ ' left: 50%;',
396
+ ' transform: translate(-50%, -50%) scale(0.95);',
397
+ ' width: 420px;',
398
+ ' max-height: 75vh;',
399
+ ' border-radius: 20px;',
358
400
  ' overflow: hidden;',
359
401
  ' display: flex;',
360
402
  ' flex-direction: column;',
361
403
  ' background: var(--wakz-widget-bg);',
362
- ' box-shadow: var(--wakz-shadow-xl);',
404
+ ' border: 1px solid #E5E5E5;',
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);',
363
408
  ' opacity: 0;',
364
- ' transform: translateY(20px) scale(0.95);',
365
409
  ' pointer-events: none;',
366
- ' transition: opacity 0.3s cubic-bezier(0.4, 0, 0.2, 1),',
367
- ' transform 0.3s cubic-bezier(0.4, 0, 0.2, 1);',
410
+ ' transition: opacity 0.3s cubic-bezier(0.16,1,0.3,1), transform 0.3s cubic-bezier(0.16,1,0.3,1);',
368
411
  '}',
369
412
  '.wakz-window.wakz-visible {',
370
413
  ' opacity: 1;',
371
- ' transform: translateY(0) scale(1);',
414
+ ' transform: translate(-50%, -50%) scale(1);',
372
415
  ' pointer-events: auto;',
373
416
  '}',
374
417
 
375
- /* ── Window Positioning ── */
376
- '.wakz-win-pos-br { bottom: 92px; right: 24px; }',
377
- '.wakz-win-pos-bl { bottom: 92px; left: 24px; }',
378
-
379
418
  /* ══════════════════════════════════════════════════
380
- HEADER
419
+ HEADER — Glassmorphism (matching floatingGlassShell)
381
420
  ══════════════════════════════════════════════════ */
382
421
  '.wakz-hdr {',
383
422
  ' flex-shrink: 0;',
@@ -385,30 +424,35 @@
385
424
  ' display: flex;',
386
425
  ' align-items: center;',
387
426
  ' justify-content: space-between;',
388
- ' color: #ffffff;',
427
+ ' color: #171717;',
389
428
  ' cursor: default;',
390
429
  ' user-select: none;',
391
- ' background: var(--wakz-primary);',
430
+ ' background: rgba(255,255,255,0.85);',
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;',
392
435
  '}',
393
- '.wakz-hdr-left {',
436
+ '.wakz-hdr-right {',
394
437
  ' display: flex;',
395
438
  ' align-items: center;',
396
439
  ' gap: 11px;',
397
440
  ' min-width: 0;',
398
441
  '}',
399
442
 
400
- /* ── Bot Avatar ── */
443
+ /* ── Bot Avatar (header) ── */
401
444
  '.wakz-avatar {',
402
- ' width: 38px;',
403
- ' height: 38px;',
445
+ ' width: 36px;',
446
+ ' height: 36px;',
404
447
  ' border-radius: 50%;',
405
- ' background: rgba(255,255,255,0.18);',
448
+ ' background: var(--wakz-primary);',
406
449
  ' display: flex;',
407
450
  ' align-items: center;',
408
451
  ' justify-content: center;',
409
452
  ' font-weight: 700;',
410
- ' font-size: 16px;',
453
+ ' font-size: 15px;',
411
454
  ' flex-shrink: 0;',
455
+ ' color: #ffffff;',
412
456
  ' letter-spacing: 0.5px;',
413
457
  '}',
414
458
 
@@ -419,20 +463,27 @@
419
463
  ' min-width: 0;',
420
464
  '}',
421
465
  '.wakz-hdr-name {',
422
- ' font-size: 15px;',
466
+ ' font-size: 16px;',
423
467
  ' font-weight: 600;',
424
468
  ' line-height: 1.3;',
469
+ ' color: #171717;',
425
470
  ' white-space: nowrap;',
426
471
  ' overflow: hidden;',
427
472
  ' text-overflow: ellipsis;',
428
473
  '}',
474
+
475
+ /* ── Header Left: status + close ── */
476
+ '.wakz-hdr-left {',
477
+ ' display: flex;',
478
+ ' align-items: center;',
479
+ ' gap: 10px;',
480
+ '}',
429
481
  '.wakz-hdr-status {',
430
482
  ' font-size: 12px;',
431
- ' opacity: 0.9;',
483
+ ' color: #171717;',
432
484
  ' display: flex;',
433
485
  ' align-items: center;',
434
486
  ' gap: 5px;',
435
- ' margin-top: 2px;',
436
487
  '}',
437
488
  '.wakz-hdr-status-dot {',
438
489
  ' width: 7px;',
@@ -442,8 +493,8 @@
442
493
  ' flex-shrink: 0;',
443
494
  ' transition: background 0.3s ease;',
444
495
  '}',
445
- '.wakz-hdr-status-dot.online { background: #4ade80; }',
446
- '.wakz-hdr-status-dot.offline { background: #f87171; }',
496
+ '.wakz-hdr-status-dot.online { background: #22c55e; }',
497
+ '.wakz-hdr-status-dot.offline { background: #ef4444; }',
447
498
 
448
499
  /* ── Close Button ── */
449
500
  '.wakz-close {',
@@ -451,18 +502,18 @@
451
502
  ' height: 32px;',
452
503
  ' border-radius: 50%;',
453
504
  ' border: none;',
454
- ' background: rgba(255,255,255,0.12);',
455
- ' color: #ffffff;',
505
+ ' background: transparent;',
506
+ ' color: #171717;',
456
507
  ' cursor: pointer;',
457
508
  ' display: flex;',
458
509
  ' align-items: center;',
459
510
  ' justify-content: center;',
460
- ' transition: background var(--wakz-transition);',
511
+ ' transition: background 0.2s ease;',
461
512
  ' outline: none;',
462
513
  ' flex-shrink: 0;',
463
514
  '}',
464
515
  '.wakz-close:hover {',
465
- ' background: rgba(255,255,255,0.28);',
516
+ ' background: rgba(0,0,0,0.06);',
466
517
  '}',
467
518
  '.wakz-close svg {',
468
519
  ' width: 18px;',
@@ -476,90 +527,130 @@
476
527
  ' flex: 1;',
477
528
  ' overflow-y: auto;',
478
529
  ' overflow-x: hidden;',
479
- ' padding: 18px 16px 10px;',
530
+ ' padding: 16px;',
480
531
  ' display: flex;',
481
532
  ' flex-direction: column;',
482
- ' gap: 6px;',
533
+ ' gap: 8px;',
483
534
  ' scroll-behavior: smooth;',
484
535
  ' background: var(--wakz-chat-bg);',
485
536
  '}',
486
537
  '.wakz-msgs::-webkit-scrollbar { width: 5px; }',
487
538
  '.wakz-msgs::-webkit-scrollbar-track { background: transparent; }',
488
539
  '.wakz-msgs::-webkit-scrollbar-thumb {',
489
- ' background: rgba(0,0,0,0.12);',
540
+ ' background: rgba(0,0,0,0.08);',
490
541
  ' border-radius: 10px;',
491
542
  '}',
492
543
 
493
544
  /* ══════════════════════════════════════════════════
494
- MESSAGE BUBBLES
545
+ MESSAGE STYLES
495
546
  ══════════════════════════════════════════════════ */
496
- '.wakz-msg-row {',
497
- ' display: flex;',
498
- ' max-width: 82%;',
499
- ' animation: wakz-msg-in 0.3s cubic-bezier(0.4, 0, 0.2, 1) forwards;',
500
- '}',
501
- '.wakz-msg-row.bot { align-self: flex-start; }',
502
- '.wakz-msg-row.user { align-self: flex-end; }',
503
-
504
547
  '@keyframes wakz-msg-in {',
505
548
  ' 0% { opacity: 0; transform: translateY(8px); }',
506
549
  ' 100% { opacity: 1; transform: translateY(0); }',
507
550
  '}',
508
551
 
509
- '.wakz-bubble {',
510
- ' padding: 10px 14px;',
552
+ /* ── User Message (bubble, right-aligned LTR / left-aligned RTL) ── */
553
+ '.wakz-msg-row {',
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;',
511
567
  ' font-size: 14px;',
512
568
  ' line-height: 1.55;',
513
569
  ' word-wrap: break-word;',
514
570
  ' overflow-wrap: break-word;',
515
571
  ' white-space: pre-wrap;',
516
- ' border-radius: var(--wakz-radius-bubble);',
517
- ' position: relative;',
518
- '}',
519
- '.wakz-bubble.bot {',
520
- ' background: #ffffff;',
521
- ' color: #1a1a1a;',
522
- ' border-bottom-left-radius: 4px;',
523
- ' box-shadow: var(--wakz-shadow-sm);',
524
- '}',
525
- '.wakz-bubble.user {',
526
- ' color: #ffffff;',
527
- ' border-bottom-right-radius: 4px;',
572
+ ' border-radius: 18px 18px 6px 18px;',
528
573
  ' background: var(--wakz-primary);',
529
- '}',
530
- '.wakz-bubble.error-bubble {',
531
- ' background: #fef2f2;',
532
- ' color: #dc2626;',
533
- ' border: 1px solid #fecaca;',
534
- ' border-bottom-left-radius: 4px;',
535
- ' box-shadow: none;',
574
+ ' color: #ffffff;',
536
575
  '}',
537
576
 
538
- /* ── Message Timestamp ── */
577
+ /* ── Timestamp ── */
539
578
  '.wakz-ts {',
540
579
  ' display: block;',
541
- ' font-size: 11px;',
542
- ' opacity: 0.45;',
580
+ ' font-size: 10px;',
581
+ ' opacity: 0.6;',
543
582
  ' margin-top: 4px;',
544
583
  '}',
545
- '.wakz-bubble.user .wakz-ts {',
584
+ '.wakz-bubble-user .wakz-ts {',
546
585
  ' text-align: right;',
586
+ ' color: rgba(255,255,255,0.7);',
587
+ '}',
588
+
589
+ /* ── Bot Message (no bubble, flat on bg with left accent border) ── */
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;',
547
621
  '}',
548
622
 
549
- /* ── Human Agent Badge ── */
623
+ /* ── Support / Human Agent Message ── */
624
+ '.wakz-msg-bot-content.wakz-human {',
625
+ ' border-left-color: #16A34A;',
626
+ '}',
550
627
  '.wakz-human-badge {',
551
- ' display: inline-block;',
552
- ' font-size: 10px;',
553
- ' background: rgba(22, 163, 74, 0.1);',
628
+ ' display: inline-flex;',
629
+ ' align-items: center;',
630
+ ' font-size: 11px;',
631
+ ' background: rgba(22,163,74,0.08);',
554
632
  ' color: #16A34A;',
555
- ' border-radius: 8px;',
556
- ' padding: 1px 7px;',
557
- ' margin-top: 5px;',
633
+ ' border-radius: 20px;',
634
+ ' padding: 2px 8px;',
635
+ ' margin-top: 6px;',
558
636
  ' font-weight: 600;',
559
- ' letter-spacing: 0.2px;',
560
637
  '}',
561
638
 
562
- /* ── Retry Button (inside error bubble) ── */
639
+ /* ── Error bubble ── */
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) ── */
563
654
  '.wakz-retry {',
564
655
  ' display: inline-flex;',
565
656
  ' align-items: center;',
@@ -574,7 +665,7 @@
574
665
  ' color: #dc2626;',
575
666
  ' cursor: pointer;',
576
667
  ' font-family: var(--wakz-font);',
577
- ' transition: background var(--wakz-transition), border-color var(--wakz-transition);',
668
+ ' transition: background 0.2s ease, border-color 0.2s ease;',
578
669
  ' outline: none;',
579
670
  '}',
580
671
  '.wakz-retry:hover {',
@@ -583,23 +674,32 @@
583
674
  '}',
584
675
 
585
676
  /* ══════════════════════════════════════════════════
586
- TYPING INDICATOR
677
+ WELCOME MESSAGE
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)
587
690
  ══════════════════════════════════════════════════ */
588
691
  '.wakz-typing {',
589
692
  ' display: flex;',
590
693
  ' align-items: center;',
591
694
  ' gap: 4px;',
592
- ' padding: 14px 18px;',
593
- ' background: #ffffff;',
594
- ' border-radius: var(--wakz-radius-bubble);',
595
- ' border-bottom-left-radius: 4px;',
596
- ' box-shadow: var(--wakz-shadow-sm);',
695
+ ' padding: 10px 0 10px 14px;',
696
+ ' border-left: 3px solid rgba(23,23,23,0.2);',
597
697
  '}',
598
698
  '.wakz-typing-dot {',
599
699
  ' width: 7px;',
600
700
  ' height: 7px;',
601
701
  ' border-radius: 50%;',
602
- ' background: #9ca3af;',
702
+ ' background: #9CA3AF;',
603
703
  ' animation: wakz-bounce-dot 1.4s ease-in-out infinite;',
604
704
  '}',
605
705
  '.wakz-typing-dot:nth-child(2) { animation-delay: 0.16s; }',
@@ -610,31 +710,33 @@
610
710
  '}',
611
711
 
612
712
  /* ══════════════════════════════════════════════════
613
- INPUT AREA
713
+ INPUT AREA — Glass Effect
614
714
  ══════════════════════════════════════════════════ */
615
715
  '.wakz-input-wrap {',
616
716
  ' flex-shrink: 0;',
617
- ' padding: 12px 14px;',
618
- ' border-top: 1px solid #e5e7eb;',
619
- ' background: var(--wakz-widget-bg);',
717
+ ' padding: 12px 16px;',
718
+ ' border-top: 1px solid rgba(0,0,0,0.06);',
719
+ ' background: rgba(255,255,255,0.7);',
720
+ ' -webkit-backdrop-filter: blur(12px);',
721
+ ' backdrop-filter: blur(12px);',
620
722
  ' display: flex;',
621
723
  ' align-items: flex-end;',
622
724
  ' gap: 8px;',
623
725
  '}',
624
726
  '.wakz-input {',
625
727
  ' flex: 1;',
626
- ' border: 1.5px solid #e5e7eb;',
627
- ' border-radius: 22px;',
728
+ ' border: 1.5px solid #E5E5E5;',
729
+ ' border-radius: 14px;',
628
730
  ' padding: 10px 16px;',
629
731
  ' font-size: 14px;',
630
732
  ' line-height: 1.4;',
631
733
  ' outline: none;',
632
734
  ' font-family: var(--wakz-font);',
633
- ' transition: border-color var(--wakz-transition);',
735
+ ' transition: border-color 0.2s ease, background 0.2s ease;',
634
736
  ' resize: none;',
635
737
  ' max-height: 100px;',
636
738
  ' overflow-y: auto;',
637
- ' background: #f9fafb;',
739
+ ' background: #F9FAFB;',
638
740
  ' color: #111827;',
639
741
  '}',
640
742
  '.wakz-input:focus {',
@@ -645,10 +747,10 @@
645
747
  ' color: #9ca3af;',
646
748
  '}',
647
749
 
648
- /* ── Send Button ── */
750
+ /* ── Send Button (floating circle, separate from textarea) ── */
649
751
  '.wakz-send {',
650
- ' width: 40px;',
651
- ' height: 40px;',
752
+ ' width: 44px;',
753
+ ' height: 44px;',
652
754
  ' border-radius: 50%;',
653
755
  ' border: none;',
654
756
  ' color: #ffffff;',
@@ -657,20 +759,21 @@
657
759
  ' align-items: center;',
658
760
  ' justify-content: center;',
659
761
  ' flex-shrink: 0;',
660
- ' transition: opacity var(--wakz-transition), transform 0.1s ease, box-shadow var(--wakz-transition);',
762
+ ' transition: opacity 0.2s ease, transform 0.15s ease, box-shadow 0.2s ease;',
661
763
  ' outline: none;',
662
764
  ' background: var(--wakz-primary);',
663
- ' box-shadow: var(--wakz-shadow-sm);',
765
+ ' box-shadow: 0 2px 8px rgba(0,0,0,0.12);',
664
766
  '}',
665
767
  '.wakz-send:hover:not(:disabled) {',
666
- ' box-shadow: var(--wakz-shadow-md);',
768
+ ' transform: scale(1.05);',
769
+ ' box-shadow: 0 4px 16px rgba(0,0,0,0.18);',
667
770
  '}',
668
771
  '.wakz-send:disabled {',
669
772
  ' opacity: 0.35;',
670
773
  ' cursor: not-allowed;',
671
774
  '}',
672
775
  '.wakz-send:not(:disabled):active {',
673
- ' transform: scale(0.88);',
776
+ ' transform: scale(0.92);',
674
777
  '}',
675
778
  '.wakz-send svg {',
676
779
  ' width: 18px;',
@@ -678,69 +781,56 @@
678
781
  ' pointer-events: none;',
679
782
  '}',
680
783
 
681
- /* ══════════════════════════════════════════════════
682
- WELCOME MESSAGE
683
- ══════════════════════════════════════════════════ */
684
- '.wakz-welcome-wrap {',
685
- ' display: flex;',
686
- ' gap: 8px;',
687
- ' max-width: 88%;',
688
- ' align-self: flex-start;',
689
- ' animation: wakz-msg-in 0.35s cubic-bezier(0.4, 0, 0.2, 1) forwards;',
690
- '}',
691
- '.wakz-welcome-avatar {',
692
- ' width: 28px;',
693
- ' height: 28px;',
694
- ' border-radius: 50%;',
695
- ' background: var(--wakz-primary);',
696
- ' display: flex;',
697
- ' align-items: center;',
698
- ' justify-content: center;',
699
- ' color: #ffffff;',
700
- ' font-weight: 700;',
701
- ' font-size: 12px;',
702
- ' flex-shrink: 0;',
703
- ' align-self: flex-end;',
704
- ' margin-bottom: 2px;',
705
- '}',
706
-
707
784
  /* ══════════════════════════════════════════════════
708
785
  RTL SUPPORT
709
786
  ══════════════════════════════════════════════════ */
710
787
  '.wakz-rtl { direction: rtl; }',
711
- '.wakz-rtl .wakz-bubble.bot {',
712
- ' border-bottom-left-radius: var(--wakz-radius-bubble);',
713
- ' border-bottom-right-radius: 4px;',
788
+ '.wakz-rtl .wakz-bubble-user {',
789
+ ' border-radius: 18px 18px 18px 6px;',
790
+ '}',
791
+ '.wakz-rtl .wakz-bubble-user .wakz-ts {',
792
+ ' text-align: left;',
793
+ '}',
794
+ '.wakz-rtl .wakz-msg-bot-content {',
795
+ ' border-left: none;',
796
+ ' border-right: 3px solid rgba(23,23,23,0.2);',
797
+ ' padding: 10px 14px 10px 0;',
714
798
  '}',
715
- '.wakz-rtl .wakz-bubble.user {',
716
- ' border-bottom-right-radius: var(--wakz-radius-bubble);',
717
- ' border-bottom-left-radius: 4px;',
799
+ '.wakz-rtl .wakz-msg-bot-content.wakz-human {',
800
+ ' border-left: none;',
801
+ ' border-right-color: #16A34A;',
718
802
  '}',
719
803
  '.wakz-rtl .wakz-typing {',
720
- ' border-bottom-left-radius: var(--wakz-radius-bubble);',
721
- ' border-bottom-right-radius: 4px;',
804
+ ' border-left: none;',
805
+ ' border-right: 3px solid rgba(23,23,23,0.2);',
806
+ ' padding: 10px 14px 10px 0;',
722
807
  '}',
723
- '.wakz-rtl .wakz-bubble.user .wakz-ts {',
724
- ' text-align: left;',
808
+ '.wakz-rtl .wakz-bubble-error {',
809
+ ' border-radius: 18px 18px 18px 6px;',
725
810
  '}',
726
811
 
727
812
  /* ══════════════════════════════════════════════════
728
- MOBILE RESPONSIVE
813
+ MOBILE RESPONSIVE (< 480px)
729
814
  ══════════════════════════════════════════════════ */
730
815
  '@media (max-width: 480px) {',
816
+ ' .wakz-overlay {',
817
+ ' display: none;',
818
+ ' }',
731
819
  ' .wakz-window {',
820
+ ' top: auto !important;',
821
+ ' left: 0 !important;',
822
+ ' right: 0 !important;',
823
+ ' bottom: 0 !important;',
732
824
  ' width: 100vw !important;',
733
825
  ' height: 100vh !important;',
734
- ' min-height: 100vh !important;',
735
826
  ' max-height: 100vh !important;',
736
- ' bottom: 0 !important;',
737
- ' top: 0 !important;',
738
- ' right: 0 !important;',
739
- ' left: 0 !important;',
740
- ' border-radius: 0 !important;',
827
+ ' border-radius: 20px 20px 0 0 !important;',
828
+ ' transform: translateY(100%) !important;',
829
+ ' border: none !important;',
830
+ ' border-top: 1px solid #E5E5E5 !important;',
741
831
  ' }',
742
832
  ' .wakz-window.wakz-visible {',
743
- ' transform: translateY(0) scale(1);',
833
+ ' transform: translateY(0) !important;',
744
834
  ' }',
745
835
  ' .wakz-fab-pos-br { bottom: 16px; right: 16px; }',
746
836
  ' .wakz-fab-pos-bl { bottom: 16px; left: 16px; }',
@@ -757,6 +847,8 @@
757
847
  var isRtl = self.config.language === 'ar';
758
848
  var posClass = self.config.position === 'bottom-left' ? 'bl' : 'br';
759
849
  var str = _strings(self.config.language);
850
+ var iconColor = self._scriptIconColor || self.config.iconColor;
851
+ var iconShape = self._scriptIconShape || self.config.iconShape;
760
852
 
761
853
  /* ── Host + Shadow DOM ── */
762
854
  self._host = _el('div');
@@ -769,11 +861,16 @@
769
861
  self._shadow.appendChild(self._root);
770
862
 
771
863
  /* ══════════════ FLOATING ACTION BUTTON ══════════════ */
864
+ var fabClasses = 'wakz-fab wakz-fab-pos-' + posClass;
865
+ if (iconShape === 'rounded') fabClasses += ' wakz-fab-shape-rounded';
866
+
772
867
  self._toggleBtn = _el('button', {
773
- className: 'wakz-fab wakz-fab-pos-' + posClass,
774
- 'aria-label': str.openChat
868
+ className: fabClasses,
869
+ 'aria-label': str.openChat,
870
+ style: { '--wakz-icon-color': iconColor }
775
871
  });
776
- self._toggleBtn.innerHTML = _ICONS.chatBubble;
872
+ self._toggleBtn.style.background = iconColor;
873
+ self._toggleBtn.innerHTML = _ICONS.chatIcon;
777
874
 
778
875
  /* Status dot on FAB */
779
876
  self._statusDot = _el('span', { className: 'wakz-fab-dot ' + (self.config.online ? 'online' : 'offline') });
@@ -781,32 +878,41 @@
781
878
  self._toggleBtn.appendChild(self._statusDot);
782
879
  self._root.appendChild(self._toggleBtn);
783
880
 
784
- /* ══════════════ CHAT WINDOW ══════════════ */
785
- self._chatWindow = _el('div', { className: 'wakz-window wakz-win-pos-' + posClass });
881
+ /* ══════════════ OVERLAY BACKDROP ══════════════ */
882
+ self._overlayEl = _el('div', { className: 'wakz-overlay' });
883
+ self._root.appendChild(self._overlayEl);
884
+
885
+ /* ══════════════ CHAT WINDOW (Centered Modal) ══════════════ */
886
+ self._chatWindow = _el('div', { className: 'wakz-window' });
786
887
 
787
888
  /* ── Header ── */
788
889
  var headerEl = _el('div', { className: 'wakz-hdr' });
789
890
 
790
- var headerLeft = _el('div', { className: 'wakz-hdr-left' });
891
+ /* Right side: avatar + name (in RTL, right side = start) */
892
+ var headerRight = _el('div', { className: 'wakz-hdr-right' });
791
893
  var avatarLetter = (self.config.botName || 'W')[0].toUpperCase();
792
- headerLeft.appendChild(_el('div', { className: 'wakz-avatar' }, [avatarLetter]));
894
+ headerRight.appendChild(_el('div', { className: 'wakz-avatar' }, [avatarLetter]));
793
895
 
794
896
  var headerInfo = _el('div', { className: 'wakz-hdr-info' });
795
897
  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' });
796
903
 
797
904
  var statusLine = _el('span', { className: 'wakz-hdr-status' });
798
905
  self._headerStatusDot = _el('span', { className: 'wakz-hdr-status-dot ' + (self.config.online ? 'online' : 'offline') });
799
906
  if (self.config.showStatus) statusLine.appendChild(self._headerStatusDot);
800
907
  self._headerStatusText = document.createTextNode(self.config.online ? str.online : str.offline);
801
908
  statusLine.appendChild(self._headerStatusText);
802
- headerInfo.appendChild(statusLine);
803
-
804
- headerLeft.appendChild(headerInfo);
805
- headerEl.appendChild(headerLeft);
909
+ headerLeft.appendChild(statusLine);
806
910
 
807
911
  var closeBtn = _el('button', { className: 'wakz-close', 'aria-label': str.closeChat });
808
912
  closeBtn.innerHTML = _ICONS.close;
809
- headerEl.appendChild(closeBtn);
913
+ headerLeft.appendChild(closeBtn);
914
+ headerEl.appendChild(headerLeft);
915
+
810
916
  self._chatWindow.appendChild(headerEl);
811
917
 
812
918
  /* ── Messages Container ── */
@@ -852,6 +958,11 @@
852
958
  self.toggleChat(false);
853
959
  });
854
960
 
961
+ /* Overlay backdrop click to close */
962
+ self._overlayEl.addEventListener('click', function () {
963
+ self.toggleChat(false);
964
+ });
965
+
855
966
  /* Send button click */
856
967
  self._sendBtn.addEventListener('click', function () {
857
968
  self._handleSend();
@@ -900,6 +1011,9 @@
900
1011
  self.isOpen = open;
901
1012
  if (open) {
902
1013
  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');
903
1017
  /* Fetch history on first open (after config is loaded) */
904
1018
  if (self._configLoaded && !self._hasFetchedHistory) {
905
1019
  self._fetchHistory();
@@ -913,6 +1027,9 @@
913
1027
  setTimeout(function () { self._inputEl.focus(); }, 320);
914
1028
  } else {
915
1029
  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');
916
1033
  /* Stop polling when chat is closed */
917
1034
  self._stopPolling();
918
1035
  }
@@ -939,6 +1056,9 @@
939
1056
  .then(function (data) {
940
1057
  if (data && data.success && data.config) {
941
1058
  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;
942
1062
  self._configLoaded = true;
943
1063
  self._applyConfig();
944
1064
  } else {
@@ -978,11 +1098,13 @@
978
1098
  var str = _strings(cfg.language);
979
1099
  var isRtl = cfg.language === 'ar';
980
1100
  var posClass = cfg.position === 'bottom-left' ? 'bl' : 'br';
1101
+ var iconColor = cfg.iconColor || '#171717';
1102
+ var iconShape = cfg.iconShape || 'circle';
981
1103
 
982
1104
  /* ── Update CSS custom properties ── */
983
1105
  var hostStyle = self._shadow.host.style;
984
1106
  hostStyle.setProperty('--wakz-primary', cfg.primaryColor);
985
- hostStyle.setProperty('--wakz-btn', cfg.btnColor);
1107
+ hostStyle.setProperty('--wakz-icon-color', iconColor);
986
1108
  hostStyle.setProperty('--wakz-chat-bg', cfg.chatBg);
987
1109
  hostStyle.setProperty('--wakz-widget-bg', cfg.widgetBg);
988
1110
 
@@ -991,19 +1113,19 @@
991
1113
  else self._root.classList.remove('wakz-rtl');
992
1114
 
993
1115
  /* ── FAB ── */
994
- self._toggleBtn.className = 'wakz-fab wakz-fab-pos-' + posClass + ' wakz-fab-enter';
1116
+ var fabClasses = 'wakz-fab wakz-fab-pos-' + posClass + ' wakz-fab-enter';
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;
995
1121
  self._toggleBtn.setAttribute('aria-label', str.openChat);
996
1122
 
997
1123
  /* ── FAB Status Dot ── */
998
1124
  self._statusDot.className = 'wakz-fab-dot ' + (cfg.online ? 'online' : 'offline');
999
1125
  self._statusDot.style.display = cfg.showStatus ? '' : 'none';
1000
1126
 
1001
- /* ── Window Position ── */
1002
- self._chatWindow.className = 'wakz-window wakz-win-pos-' + posClass + (self.isOpen ? ' wakz-visible' : '');
1003
-
1004
- /* ── Header ── */
1005
- var headerEl = self._chatWindow.querySelector('.wakz-hdr');
1006
- if (headerEl) headerEl.style.background = cfg.primaryColor;
1127
+ /* ── Window ── */
1128
+ self._chatWindow.className = 'wakz-window' + (self.isOpen ? ' wakz-visible' : '');
1007
1129
 
1008
1130
  /* ── Bot Name & Avatar ── */
1009
1131
  var nameEl = self._chatWindow.querySelector('.wakz-hdr-name');
@@ -1151,7 +1273,7 @@
1151
1273
  };
1152
1274
 
1153
1275
  /* ════════════════════════════════════════════════════════════════
1154
- APPEND WELCOME MESSAGE (with bot avatar)
1276
+ APPEND WELCOME MESSAGE (bot style — no bubble, flat on bg)
1155
1277
  ════════════════════════════════════════════════════════════════ */
1156
1278
 
1157
1279
  WAKZWidget.prototype._appendWelcomeMessage = function (text) {
@@ -1161,13 +1283,13 @@
1161
1283
  var wrap = _el('div', { className: 'wakz-welcome-wrap' });
1162
1284
 
1163
1285
  /* Bot avatar */
1164
- var avatar = _el('div', { className: 'wakz-welcome-avatar' },
1286
+ var avatar = _el('div', { className: 'wakz-msg-bot-avatar' },
1165
1287
  [(self.config.botName || 'W')[0].toUpperCase()]);
1166
1288
  wrap.appendChild(avatar);
1167
1289
 
1168
- /* Bubble */
1169
- var bubble = _el('div', { className: 'wakz-bubble bot' }, [text]);
1170
- wrap.appendChild(bubble);
1290
+ /* Flat content with accent border */
1291
+ var content = _el('div', { className: 'wakz-msg-bot-content' }, [text]);
1292
+ wrap.appendChild(content);
1171
1293
 
1172
1294
  self._messagesContainer.appendChild(wrap);
1173
1295
  self.messages.push({ sender: 'bot', text: text });
@@ -1181,41 +1303,30 @@
1181
1303
  WAKZWidget.prototype._appendMessage = function (sender, text, isError, timestamp, isHumanAgent) {
1182
1304
  var self = this;
1183
1305
 
1184
- var row = _el('div', { className: 'wakz-msg-row ' + sender });
1306
+ if (sender === 'user') {
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));
1185
1311
 
1186
- var bubbleClass = 'wakz-bubble ' + sender;
1187
- if (isError) bubbleClass += ' error-bubble';
1188
- var bubble = _el('div', { className: bubbleClass });
1189
-
1190
- bubble.appendChild(document.createTextNode(text));
1191
-
1192
- /* Timestamp */
1193
- var tsText = '';
1194
- if (timestamp) {
1195
- try {
1196
- var d = new Date(timestamp);
1197
- if (!isNaN(d.getTime())) {
1198
- tsText = d.getHours().toString().padStart(2, '0') + ':' +
1199
- d.getMinutes().toString().padStart(2, '0');
1200
- }
1201
- } catch (e) { /* ignore */ }
1202
- }
1203
- if (!tsText) {
1204
- var now = new Date();
1205
- tsText = now.getHours().toString().padStart(2, '0') + ':' +
1206
- now.getMinutes().toString().padStart(2, '0');
1207
- }
1208
- bubble.appendChild(_el('span', { className: 'wakz-ts' }, [tsText]));
1312
+ /* Timestamp */
1313
+ var tsText = self._formatTime(timestamp);
1314
+ bubble.appendChild(_el('span', { className: 'wakz-ts' }, [tsText]));
1209
1315
 
1210
- /* Human agent badge — visual distinction for admin replies */
1211
- if (isHumanAgent) {
1212
- var iStr = _strings(self.config.language);
1213
- var badge = _el('span', { className: 'wakz-human-badge' }, [iStr.humanBadge || '👤 فريق الدعم']);
1214
- bubble.appendChild(badge);
1316
+ row.appendChild(bubble);
1317
+ self._messagesContainer.appendChild(row);
1318
+ self.messages.push({ sender: sender, text: text, isError: !!isError });
1319
+ self._scrollToBottom();
1320
+ return;
1215
1321
  }
1216
1322
 
1217
- /* Retry button for errors */
1218
1323
  if (isError) {
1324
+ /* ── Error message: red-tinted bubble ── */
1325
+ var errRow = _el('div', { className: 'wakz-msg-row bot' });
1326
+ var errBubble = _el('div', { className: 'wakz-bubble-error' });
1327
+ errBubble.appendChild(document.createTextNode(text));
1328
+
1329
+ /* Retry button for errors */
1219
1330
  var str = _strings(self.config.language);
1220
1331
  var retryBtn = _el('button', { className: 'wakz-retry' }, [
1221
1332
  _ICONS.error + ' ' + str.retry
@@ -1241,19 +1352,71 @@
1241
1352
  }
1242
1353
  if (lastUserMsg) self._sendToAPI(lastUserMsg);
1243
1354
  });
1244
- })(row);
1245
- bubble.appendChild(retryBtn);
1355
+ })(errRow);
1356
+ errBubble.appendChild(retryBtn);
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;
1246
1363
  }
1247
1364
 
1248
- row.appendChild(bubble);
1249
- self._messagesContainer.appendChild(row);
1365
+ /* ── Bot / Support message: flat on bg with accent border ── */
1366
+ var botRow = _el('div', { className: 'wakz-msg-row bot' });
1367
+ var wrap = _el('div', { className: 'wakz-msg-bot-wrap' });
1250
1368
 
1251
- self.messages.push({ sender: sender, text: text, isError: !!isError });
1369
+ /* Small bot avatar */
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 });
1252
1396
  self._scrollToBottom();
1253
1397
  };
1254
1398
 
1255
1399
  /* ════════════════════════════════════════════════════════════════
1256
- TYPING INDICATOR
1400
+ FORMAT TIME
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)
1257
1420
  ════════════════════════════════════════════════════════════════ */
1258
1421
 
1259
1422
  WAKZWidget.prototype._showTyping = function () {
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "wakz-chat-widget",
3
- "version": "1.1.0",
4
- "description": "Production-grade AI chat widget by WAKZ — Shadow DOM isolated, zero dependencies, Intercom/Crisp quality.",
3
+ "version": "2.0.0",
4
+ "description": "Premium AI chat widget by WAKZ — Glassmorphism UI, Shadow DOM, zero deps, centered modal design.",
5
5
  "main": "index.js",
6
6
  "scripts": {
7
7
  "prepublishOnly": "echo 'Ready to publish wakz-chat-widget'"