wakz-chat-widget 1.1.0 → 2.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +51 -24
- package/index.js +61 -6
- package/package.json +13 -6
package/README.md
CHANGED
|
@@ -1,43 +1,70 @@
|
|
|
1
1
|
# wakz-chat-widget
|
|
2
2
|
|
|
3
|
-
|
|
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)
|
|
4
22
|
|
|
5
23
|
## Installation
|
|
6
24
|
|
|
7
|
-
|
|
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:
|
|
8
32
|
|
|
9
33
|
```html
|
|
10
|
-
|
|
11
|
-
<script
|
|
12
|
-
src="https://unpkg.com/wakz-chat-widget@latest/index.js"
|
|
34
|
+
<script src="/wakz-widget.js"
|
|
13
35
|
data-api-key="YOUR_API_KEY"
|
|
14
|
-
data-server="https://your-
|
|
36
|
+
data-server="https://your-server.com"
|
|
15
37
|
async>
|
|
16
38
|
</script>
|
|
17
39
|
```
|
|
18
40
|
|
|
19
|
-
|
|
20
|
-
- `YOUR_API_KEY` — Your WAKZ embed API key
|
|
21
|
-
- `https://your-wakz-server.com` — Your WAKZ server URL
|
|
41
|
+
## Configuration
|
|
22
42
|
|
|
23
|
-
|
|
43
|
+
The widget fetches its configuration from your server's `/api/v1/embed/config` endpoint. Configurable options include:
|
|
24
44
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
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 |
|
|
34
57
|
|
|
35
|
-
##
|
|
58
|
+
## Version History
|
|
36
59
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
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
|
|
41
68
|
|
|
42
69
|
## License
|
|
43
70
|
|
package/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* WAKZ Chat Widget v2.
|
|
2
|
+
* WAKZ Chat Widget v2.2.0
|
|
3
3
|
* ─────────────────────────────────────────────────────────────────
|
|
4
4
|
* A production-grade, self-contained chat widget using Shadow DOM.
|
|
5
5
|
* Competes with Intercom & Crisp in quality.
|
|
@@ -79,6 +79,46 @@
|
|
|
79
79
|
return el;
|
|
80
80
|
}
|
|
81
81
|
|
|
82
|
+
/** Detect device model, platform, and type using userAgentData API (Chrome 90+) with UA fallback */
|
|
83
|
+
function _getDeviceInfo() {
|
|
84
|
+
var info = {
|
|
85
|
+
device_model: '',
|
|
86
|
+
device_platform: '',
|
|
87
|
+
device_type: 'desktop'
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
// Try userAgentData API (Chrome 90+, Edge, Samsung Browser — NOT Safari/Firefox)
|
|
91
|
+
if (navigator.userAgentData) {
|
|
92
|
+
info.device_model = navigator.userAgentData.model || '';
|
|
93
|
+
info.device_platform = navigator.userAgentData.platform || '';
|
|
94
|
+
if (navigator.userAgentData.mobile === true) {
|
|
95
|
+
info.device_type = 'mobile';
|
|
96
|
+
} else if (navigator.userAgentData.mobile === false && info.device_platform === 'Android') {
|
|
97
|
+
info.device_type = 'tablet';
|
|
98
|
+
} else {
|
|
99
|
+
info.device_type = 'desktop';
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// Fallback: parse from user agent string
|
|
104
|
+
var ua = navigator.userAgent || '';
|
|
105
|
+
if (!info.device_platform) {
|
|
106
|
+
if (/iPhone|iPad|iPod/i.test(ua)) info.device_platform = 'iOS';
|
|
107
|
+
else if (/Android/i.test(ua)) info.device_platform = 'Android';
|
|
108
|
+
else if (/Windows/i.test(ua)) info.device_platform = 'Windows';
|
|
109
|
+
else if (/Mac OS X/i.test(ua)) info.device_platform = 'macOS';
|
|
110
|
+
else if (/CrOS/i.test(ua)) info.device_platform = 'Chrome OS';
|
|
111
|
+
else if (/Linux/i.test(ua)) info.device_platform = 'Linux';
|
|
112
|
+
}
|
|
113
|
+
if (info.device_type === 'desktop') {
|
|
114
|
+
if (/iPad/i.test(ua)) info.device_type = 'tablet';
|
|
115
|
+
else if (/Mobile|iPhone|iPod/i.test(ua)) info.device_type = 'mobile';
|
|
116
|
+
else if (/Android(?!.*Mobile)/i.test(ua)) info.device_type = 'tablet';
|
|
117
|
+
else if (/Android/i.test(ua)) info.device_type = 'mobile';
|
|
118
|
+
}
|
|
119
|
+
return info;
|
|
120
|
+
}
|
|
121
|
+
|
|
82
122
|
/** Fetch with an AbortController timeout (default 30s) */
|
|
83
123
|
function _fetchWithTimeout(url, options, timeoutMs) {
|
|
84
124
|
var controller = new AbortController();
|
|
@@ -182,6 +222,7 @@
|
|
|
182
222
|
self.apiKey = attrs.apiKey;
|
|
183
223
|
self.server = attrs.server;
|
|
184
224
|
self.visitorId = _getVisitorId();
|
|
225
|
+
self._deviceInfo = _getDeviceInfo(); // Device model, platform, type — collected once
|
|
185
226
|
|
|
186
227
|
/* ── Runtime state ── */
|
|
187
228
|
self.config = Object.assign({}, _DEFAULTS);
|
|
@@ -196,6 +237,7 @@
|
|
|
196
237
|
self._lastPollTime = null;
|
|
197
238
|
self._POLL_INTERVAL = 4000; /* Poll every 4 seconds */
|
|
198
239
|
self._knownMessageIds = {}; /* Deduplication set for message IDs */
|
|
240
|
+
self._pendingReply = false; /* True while waiting for API response — pauses polling */
|
|
199
241
|
|
|
200
242
|
/* ── DOM refs (populated after mount) ── */
|
|
201
243
|
self._host = null;
|
|
@@ -1105,6 +1147,8 @@
|
|
|
1105
1147
|
WAKZWidget.prototype._pollForNewMessages = function () {
|
|
1106
1148
|
var self = this;
|
|
1107
1149
|
if (!self.server || !self.apiKey || !self._lastPollTime) return;
|
|
1150
|
+
/* Skip polling while waiting for our own reply — prevents race condition duplicates */
|
|
1151
|
+
if (self._pendingReply) return;
|
|
1108
1152
|
|
|
1109
1153
|
var url = self.server + '/api/v1/embed/chat?api_key=' +
|
|
1110
1154
|
encodeURIComponent(self.apiKey) +
|
|
@@ -1328,6 +1372,10 @@
|
|
|
1328
1372
|
self._sendBtn.disabled = true;
|
|
1329
1373
|
self._showTyping();
|
|
1330
1374
|
|
|
1375
|
+
/* Freeze poll time BEFORE sending — prevents polling from fetching our own messages */
|
|
1376
|
+
self._lastPollTime = new Date().toISOString();
|
|
1377
|
+
self._pendingReply = true;
|
|
1378
|
+
|
|
1331
1379
|
var payload = {
|
|
1332
1380
|
api_key: self.apiKey,
|
|
1333
1381
|
visitor_id: self.visitorId,
|
|
@@ -1336,7 +1384,10 @@
|
|
|
1336
1384
|
domain: window.location.hostname || '',
|
|
1337
1385
|
page_url: window.location.href || '',
|
|
1338
1386
|
user_agent: navigator.userAgent || '',
|
|
1339
|
-
language: navigator.language || ''
|
|
1387
|
+
language: navigator.language || '',
|
|
1388
|
+
device_model: self._deviceInfo.device_model || '',
|
|
1389
|
+
device_platform: self._deviceInfo.device_platform || '',
|
|
1390
|
+
device_type: self._deviceInfo.device_type || 'desktop'
|
|
1340
1391
|
}
|
|
1341
1392
|
};
|
|
1342
1393
|
|
|
@@ -1351,19 +1402,23 @@
|
|
|
1351
1402
|
.then(function (res) { return res.json(); })
|
|
1352
1403
|
.then(function (data) {
|
|
1353
1404
|
self._hideTyping();
|
|
1405
|
+
self._pendingReply = false;
|
|
1354
1406
|
if (data && data.success && data.reply) {
|
|
1355
1407
|
self._appendMessage('bot', data.reply);
|
|
1356
1408
|
} else {
|
|
1357
1409
|
self._appendMessage('bot', _strings(self.config.language).errorMsg, true);
|
|
1358
1410
|
}
|
|
1359
|
-
/*
|
|
1360
|
-
self._lastPollTime =
|
|
1411
|
+
/* Advance poll time past any messages created during this request */
|
|
1412
|
+
self._lastPollTime = (data && data.timestamp)
|
|
1413
|
+
? new Date(new Date(data.timestamp).getTime() + 1000).toISOString()
|
|
1414
|
+
: new Date(Date.now() + 1000).toISOString();
|
|
1361
1415
|
})
|
|
1362
1416
|
.catch(function () {
|
|
1363
1417
|
self._hideTyping();
|
|
1418
|
+
self._pendingReply = false;
|
|
1364
1419
|
self._appendMessage('bot', _strings(self.config.language).errorMsg, true);
|
|
1365
|
-
/*
|
|
1366
|
-
self._lastPollTime = new Date().toISOString();
|
|
1420
|
+
/* Advance poll time even on error */
|
|
1421
|
+
self._lastPollTime = new Date(Date.now() + 1000).toISOString();
|
|
1367
1422
|
})
|
|
1368
1423
|
.finally(function () {
|
|
1369
1424
|
self.isLoading = false;
|
package/package.json
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "wakz-chat-widget",
|
|
3
|
-
"version": "
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "2.2.0",
|
|
4
|
+
"description": "Premium AI chat widget by WAKZ — Production-grade, Shadow DOM, zero dependencies, multi-language support, device detection, RTL support.",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"scripts": {
|
|
7
|
-
"prepublishOnly": "echo 'Ready to publish wakz-chat-widget'"
|
|
7
|
+
"prepublishOnly": "echo 'Ready to publish wakz-chat-widget v2.2.0'"
|
|
8
8
|
},
|
|
9
9
|
"keywords": [
|
|
10
10
|
"chat",
|
|
@@ -16,9 +16,13 @@
|
|
|
16
16
|
"shadow-dom",
|
|
17
17
|
"customer-support",
|
|
18
18
|
"live-chat",
|
|
19
|
-
"saas"
|
|
19
|
+
"saas",
|
|
20
|
+
"rtl",
|
|
21
|
+
"arabic",
|
|
22
|
+
"device-detection",
|
|
23
|
+
"zero-dependencies"
|
|
20
24
|
],
|
|
21
|
-
"author": "
|
|
25
|
+
"author": "mohamed64 <mohamed01140251843sayed@gmail.com>",
|
|
22
26
|
"license": "MIT",
|
|
23
27
|
"repository": {
|
|
24
28
|
"type": "git",
|
|
@@ -27,5 +31,8 @@
|
|
|
27
31
|
"files": [
|
|
28
32
|
"index.js",
|
|
29
33
|
"README.md"
|
|
30
|
-
]
|
|
34
|
+
],
|
|
35
|
+
"engines": {
|
|
36
|
+
"node": ">=12.0.0"
|
|
37
|
+
}
|
|
31
38
|
}
|