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.
- package/README.md +71 -0
- package/index.js +119 -10
- 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.
|
|
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: '#
|
|
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
|
-
|
|
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) {
|
|
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
|
-
{
|
|
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
|
+
}
|