wakz-chat-widget 4.0.0 → 4.0.2

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 (3) hide show
  1. package/README.md +71 -0
  2. package/index.js +119 -10
  3. package/package.json +41 -1
package/README.md ADDED
@@ -0,0 +1,71 @@
1
+ # wakz-chat-widget
2
+
3
+ **Premium AI chat widget by WAKZ** — A production-grade, self-contained chat widget using Shadow DOM.
4
+
5
+ > Competes with Intercom & Crisp in quality. **Zero external dependencies** — pure vanilla JavaScript.
6
+
7
+ ## Features
8
+
9
+ - Shadow DOM isolation (no CSS conflicts)
10
+ - Multi-language support (English, Arabic, French) with full RTL
11
+ - Device detection (model, platform, type)
12
+ - Online/offline status with animated indicators
13
+ - Message polling with deduplication
14
+ - Mobile responsive (full-screen on small devices)
15
+ - Typing indicator animations
16
+ - Welcome messages
17
+ - Human agent badge support
18
+ - Auto-resizing textarea input
19
+ - Keyboard shortcuts (Enter to send, Shift+Enter for newline, Escape to close)
20
+ - Smooth animations and transitions
21
+ - Configurable theming (colors, position)
22
+
23
+ ## Installation
24
+
25
+ ```bash
26
+ npm install wakz-chat-widget
27
+ ```
28
+
29
+ ## Usage
30
+
31
+ Copy `index.js` from `node_modules/wakz-chat-widget/` to your public folder and include it:
32
+
33
+ ```html
34
+ <script src="/wakz-widget.js"
35
+ data-api-key="YOUR_API_KEY"
36
+ data-server="https://your-server.com"
37
+ async>
38
+ </script>
39
+ ```
40
+
41
+ ## Configuration
42
+
43
+ The widget fetches its configuration from your server's `/api/v1/embed/config` endpoint. Configurable options include:
44
+
45
+ | Option | Type | Default | Description |
46
+ |--------|------|---------|-------------|
47
+ | `botName` | string | `'WAKZ'` | Bot display name |
48
+ | `welcomeMessage` | string | `''` | Initial welcome message |
49
+ | `primaryColor` | string | `'#171717'` | Primary theme color |
50
+ | `chatBg` | string | `'#f8f9fa'` | Chat area background |
51
+ | `btnColor` | string | `'#171717'` | FAB button color |
52
+ | `widgetBg` | string | `'#ffffff'` | Widget background |
53
+ | `position` | string | `'bottom-right'` | Widget position |
54
+ | `language` | string | `'en'` | Language (`en`, `ar`, `fr`) |
55
+ | `showStatus` | boolean | `true` | Show online/offline dot |
56
+ | `online` | boolean | `true` | Initial status |
57
+
58
+ ## Version History
59
+
60
+ ### v2.2.0
61
+ - Added device model, platform, and type detection (User-Agent Client Hints API + UA fallback)
62
+ - Device info sent with session data for dashboard analytics
63
+
64
+ ### v2.0.0
65
+ - Complete rewrite with Shadow DOM architecture
66
+ - Multi-language support (EN/AR/FR) with RTL
67
+ - Production-grade UI with animations
68
+
69
+ ## License
70
+
71
+ MIT
package/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  /**
2
- * WAKZ Chat Widget v4.0.0
2
+ * WAKZ Chat Widget v4.0.2
3
3
  * ─────────────────────────────────────────────────────────────────
4
4
  * A production-grade, self-contained chat widget using Shadow DOM.
5
5
  * Liquid Glass design — pill header, floating input, dark user bubbles.
@@ -146,7 +146,13 @@
146
146
  close:
147
147
  '<svg width="14" height="14" viewBox="0 0 24 24" fill="none"><line x1="18" y1="6" x2="6" y2="18" stroke="currentColor" stroke-width="2.5" stroke-linecap="round"/><line x1="6" y1="6" x2="18" y2="18" stroke="currentColor" stroke-width="2.5" stroke-linecap="round"/></svg>',
148
148
  error:
149
- '<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>'
149
+ '<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>',
150
+ message:
151
+ '<svg width="26" height="26" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><path d="M7.9 20A9 9 0 1 0 4 16.1L2 22z"/></svg>',
152
+ whatsapp:
153
+ '<svg width="26" height="26" viewBox="0 0 24 24" fill="currentColor"><path d="M17.472 14.382c-.297-.149-1.758-.867-2.03-.967-.273-.099-.471-.148-.67.15-.197.297-.767.966-.94 1.164-.173.199-.347.223-.644.075-.297-.15-1.255-.463-2.39-1.475-.883-.788-1.48-1.761-1.653-2.059-.173-.297-.018-.458.13-.606.134-.133.298-.347.446-.52.149-.174.198-.298.298-.497.099-.198.05-.371-.025-.52-.075-.149-.669-1.612-.916-2.207-.242-.579-.487-.5-.669-.51-.173-.008-.371-.01-.57-.01-.198 0-.52.074-.792.372-.272.297-1.04 1.016-1.04 2.479 0 1.462 1.065 2.875 1.213 3.074.149.198 2.096 3.2 5.077 4.487.709.306 1.262.489 1.694.625.712.227 1.36.195 1.871.118.571-.085 1.758-.719 2.006-1.413.248-.694.248-1.289.173-1.413-.074-.124-.272-.198-.57-.347m-5.421 7.403h-.004a9.87 9.87 0 01-5.031-1.378l-.361-.214-3.741.982.998-3.648-.235-.374a9.86 9.86 0 01-1.51-5.26c.001-5.45 4.436-9.884 9.888-9.884 2.64 0 5.122 1.03 6.988 2.898a9.825 9.825 0 012.893 6.994c-.003 5.45-4.437 9.884-9.885 9.884m8.413-18.297A11.815 11.815 0 0012.05 0C5.495 0 .16 5.335.157 11.892c0 2.096.547 4.142 1.588 5.945L.057 24l6.305-1.654a11.882 11.882 0 005.683 1.448h.005c6.554 0 11.89-5.335 11.893-11.893a11.821 11.821 0 00-3.48-8.413z"/></svg>',
154
+ headset:
155
+ '<svg width="26" height="26" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><path d="M3 18v-6a9 9 0 0 1 18 0v6"/><path d="M21 19a2 2 0 0 1-2 2h-1a2 2 0 0 1-2-2v-3a2 2 0 0 1 2-2h3ZM3 19a2 2 0 0 0 2 2h3a2 2 0 0 0 2-2v-3a2 2 0 0 0-2-2H5"/></svg>'
150
156
  };
151
157
 
152
158
  /* ════════════════════════════════════════════════════════════════
@@ -210,11 +216,23 @@
210
216
  primaryColor: '#111111',
211
217
  chatBg: '#ffffff',
212
218
  btnColor: '#111111',
213
- widgetBg: '#ffffff',
219
+ widgetBg: '#f5f5f5',
214
220
  position: 'bottom-right',
215
221
  language: 'en',
216
222
  showStatus: true,
217
- online: true
223
+ online: true,
224
+ /* v4.0.1 — Advanced customization */
225
+ fabShape: 'circle',
226
+ fabIcon: 'chat',
227
+ triggerMode: 'manual',
228
+ triggerDelay: 5,
229
+ displayMode: 'modal',
230
+ botBubbleColor: '#ffffff',
231
+ botTextColor: '#333333',
232
+ userBubbleColor: '#111111',
233
+ userTextColor: '#ffffff',
234
+ headerColor: '',
235
+ headerTextColor: ''
218
236
  };
219
237
 
220
238
  /* ════════════════════════════════════════════════════════════════
@@ -992,6 +1010,28 @@
992
1010
  ' border-bottom-right-radius: 4px;',
993
1011
  '}',
994
1012
 
1013
+ /* ══════════════════════════════════════════════════
1014
+ FULLSCREEN MODE — overrides window to fill viewport
1015
+ ══════════════════════════════════════════════════ */
1016
+ '.wakz-mode-fullscreen {',
1017
+ ' width: 100vw !important;',
1018
+ ' height: 100vh !important;',
1019
+ ' max-width: 100vw !important;',
1020
+ ' max-height: 100vh !important;',
1021
+ ' top: 0 !important;',
1022
+ ' left: 0 !important;',
1023
+ ' transform: none !important;',
1024
+ ' border-radius: 0 !important;',
1025
+ ' box-shadow: none !important;',
1026
+ '}',
1027
+ '.wakz-mode-fullscreen.wakz-visible {',
1028
+ ' transform: none !important;',
1029
+ '}',
1030
+ /* Hide overlay in fullscreen mode — not needed */
1031
+ '.wakz-mode-fullscreen ~ .wakz-overlay {',
1032
+ ' display: none !important;',
1033
+ '}',
1034
+
995
1035
  /* ══════════════════════════════════════════════════
996
1036
  RESPONSIVE
997
1037
  ══════════════════════════════════════════════════ */
@@ -1067,7 +1107,8 @@
1067
1107
 
1068
1108
  /* ══════════════ FLOATING HEADER (pill, absolute over messages) ══════════════ */
1069
1109
  var headerWrap = _el('div', { className: 'wakz-header-wrap' });
1070
- var header = _el('div', { className: 'wakz-header' });
1110
+ self._headerEl = _el('div', { className: 'wakz-header' });
1111
+ var header = self._headerEl;
1071
1112
 
1072
1113
  /* Specular highlight (real DOM element, not ::before) */
1073
1114
  header.appendChild(_el('div', { className: 'wakz-specular' }));
@@ -1224,6 +1265,8 @@
1224
1265
  self._chatWindow.classList.add('wakz-visible');
1225
1266
  self._overlay.classList.add('wakz-visible');
1226
1267
  self._toggleBtn.classList.add('wakz-fab-hidden');
1268
+ /* Re-fetch config on every open to get latest customizations */
1269
+ self._fetchConfig();
1227
1270
  /* Fetch history on first open (after config is loaded) */
1228
1271
  if (self._configLoaded && !self._hasFetchedHistory) {
1229
1272
  self._fetchHistory();
@@ -1261,27 +1304,38 @@
1261
1304
  method: 'GET',
1262
1305
  headers: { 'Accept': 'application/json' }
1263
1306
  }, 30000)
1264
- .then(function (res) { return res.json(); })
1307
+ .then(function (res) {
1308
+ if (!res.ok) {
1309
+ self._handleConfigError(true);
1310
+ return null;
1311
+ }
1312
+ return res.json();
1313
+ })
1265
1314
  .then(function (data) {
1266
1315
  if (data && data.success && data.config) {
1267
1316
  self.config = Object.assign({}, _DEFAULTS, data.config);
1268
1317
  self._configLoaded = true;
1269
1318
  self._applyConfig();
1270
- } else {
1271
- self._handleConfigError();
1319
+ } else if (data !== null) {
1320
+ self._handleConfigError(true);
1272
1321
  }
1273
1322
  })
1274
1323
  .catch(function (err) {
1275
- self._handleConfigError();
1324
+ self._handleConfigError(false);
1276
1325
  });
1277
1326
  };
1278
1327
 
1279
1328
  /** Handle config fetch failure — show offline state */
1280
- WAKZWidget.prototype._handleConfigError = function () {
1329
+ WAKZWidget.prototype._handleConfigError = function (isPermanent) {
1281
1330
  var self = this;
1282
1331
  self._configError = true;
1283
1332
  self.config.online = false;
1284
1333
 
1334
+ /* Hide FAB entirely if key is permanently invalid */
1335
+ if (isPermanent && self._toggleBtn) {
1336
+ self._toggleBtn.style.display = 'none';
1337
+ }
1338
+
1285
1339
  /* Update FAB dot to red */
1286
1340
  if (self._statusDot) {
1287
1341
  self._statusDot.className = 'wakz-fab-dot offline';
@@ -1314,6 +1368,10 @@
1314
1368
  hostStyle.setProperty('--wakz-btn', cfg.btnColor);
1315
1369
  hostStyle.setProperty('--wakz-chat-bg', cfg.chatBg);
1316
1370
  hostStyle.setProperty('--wakz-widget-bg', cfg.widgetBg);
1371
+ hostStyle.setProperty('--wakz-bot-bubble', cfg.botBubbleColor || '#ffffff');
1372
+ hostStyle.setProperty('--wakz-bot-text', cfg.botTextColor || '#333333');
1373
+ hostStyle.setProperty('--wakz-user-bubble', cfg.userBubbleColor || '#111111');
1374
+ hostStyle.setProperty('--wakz-user-text', cfg.userTextColor || '#ffffff');
1317
1375
 
1318
1376
  /* ── RTL ── */
1319
1377
  if (isRtl) self._root.classList.add('wakz-rtl');
@@ -1363,6 +1421,57 @@
1363
1421
  /* ── Send button label ── */
1364
1422
  self._sendBtn.setAttribute('aria-label', str.sendMessage);
1365
1423
 
1424
+ /* ── FAB Icon ── */
1425
+ var iconKey = cfg.fabIcon || 'chat';
1426
+ if (iconKey === 'chatBubble') iconKey = 'chat';
1427
+ if (_ICONS[iconKey]) {
1428
+ self._toggleBtn.innerHTML = _ICONS[iconKey];
1429
+ } else {
1430
+ self._toggleBtn.innerHTML = _ICONS.chatBubble;
1431
+ }
1432
+ /* Re-append status dot after innerHTML change */
1433
+ if (self._statusDot && self._statusDot.parentNode !== self._toggleBtn) {
1434
+ self._toggleBtn.appendChild(self._statusDot);
1435
+ }
1436
+
1437
+ /* ── FAB Shape ── */
1438
+ if (cfg.fabShape === 'pill') {
1439
+ self._toggleBtn.style.borderRadius = '9999px';
1440
+ self._toggleBtn.style.width = 'auto';
1441
+ self._toggleBtn.style.padding = '0 20px';
1442
+ } else {
1443
+ self._toggleBtn.style.borderRadius = '50%';
1444
+ self._toggleBtn.style.width = '56px';
1445
+ self._toggleBtn.style.padding = '0';
1446
+ }
1447
+
1448
+ /* ── Display Mode ── */
1449
+ if (cfg.displayMode === 'fullscreen') {
1450
+ self._chatWindow.classList.add('wakz-mode-fullscreen');
1451
+ self._chatWindow.classList.remove('wakz-mode-modal');
1452
+ } else {
1453
+ self._chatWindow.classList.add('wakz-mode-modal');
1454
+ self._chatWindow.classList.remove('wakz-mode-fullscreen');
1455
+ }
1456
+
1457
+ /* ── Auto Trigger ── */
1458
+ if (cfg.triggerMode === 'auto' && !self.isOpen) {
1459
+ var autoDelay = Math.max(1, Math.min(60, parseInt(cfg.triggerDelay) || 5)) * 1000;
1460
+ setTimeout(function () {
1461
+ if (!self.isOpen) self.toggleChat(true);
1462
+ }, autoDelay);
1463
+ }
1464
+
1465
+ /* ── Header Colors (only apply if explicitly customized) ── */
1466
+ if (cfg.headerColor) {
1467
+ self._headerEl.style.background = cfg.headerColor;
1468
+ }
1469
+ if (cfg.headerTextColor) {
1470
+ self._headerTitleEl.style.color = cfg.headerTextColor;
1471
+ self._headerSubtitleEl.style.color = cfg.headerTextColor + 'aa';
1472
+ self._closeBtn.style.color = cfg.headerTextColor;
1473
+ }
1474
+
1366
1475
  /* ── If chat is already open, show welcome message ── */
1367
1476
  if (self.isOpen && self.messages.length === 0 && cfg.welcomeMessage) {
1368
1477
  self._appendWelcomeMessage(cfg.welcomeMessage);
package/package.json CHANGED
@@ -1 +1,41 @@
1
- { "name": "wakz-chat-widget", "version": "4.0.0", "description": "WAKZ Chat Widget — Liquid Glass design with Shadow DOM isolation", "main": "index.js", "keywords": ["chat", "widget", "wakz", "liquid-glass", "shadow-dom"], "author": "WAKZ", "license": "MIT" }
1
+ {
2
+ "name": "wakz-chat-widget",
3
+ "version": "4.0.2",
4
+ "description": "WAKZ Chat Widget — Liquid Glass design with Shadow DOM isolation, CSS Variables, Data-Driven customization, zero dependencies.",
5
+ "main": "index.js",
6
+ "scripts": {
7
+ "prepublishOnly": "echo 'Ready to publish wakz-chat-widget v4.0.1'"
8
+ },
9
+ "keywords": [
10
+ "chat",
11
+ "widget",
12
+ "ai",
13
+ "chatbot",
14
+ "wakz",
15
+ "embed",
16
+ "shadow-dom",
17
+ "customer-support",
18
+ "live-chat",
19
+ "saas",
20
+ "rtl",
21
+ "arabic",
22
+ "device-detection",
23
+ "zero-dependencies",
24
+ "css-variables",
25
+ "data-driven",
26
+ "liquid-glass"
27
+ ],
28
+ "author": "mohamed64 <mohamed01140251843sayed@gmail.com>",
29
+ "license": "MIT",
30
+ "repository": {
31
+ "type": "git",
32
+ "url": ""
33
+ },
34
+ "files": [
35
+ "index.js",
36
+ "README.md"
37
+ ],
38
+ "engines": {
39
+ "node": ">=12.0.0"
40
+ }
41
+ }