@pindai-ai/chat-widget 2.0.3 → 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 +387 -346
- package/dist/pindai-chat-widget.css +1 -1
- package/dist/pindai-chat-widget.js +797 -22
- package/dist/pindai-chat-widget.js.map +1 -1
- package/dist/pindai-chat-widget.umd.js +46 -0
- package/dist/pindai-chat-widget.umd.js.map +1 -0
- package/package.json +13 -5
- package/src/i18n.js +18 -0
- package/src/main.js +785 -299
- package/src/style.css +272 -5
package/README.md
CHANGED
|
@@ -1,47 +1,73 @@
|
|
|
1
1
|
# Pindai Chat Widget
|
|
2
2
|
|
|
3
|
-
> Modern, accessible chat widget
|
|
3
|
+
> Modern, accessible, embeddable chat widget — seamlessly integrated with **Pindai Agent-API** or any generic HTTP backend.
|
|
4
4
|
|
|
5
5
|
[](https://www.npmjs.com/package/@pindai-ai/chat-widget)
|
|
6
6
|
[](https://www.npmjs.com/package/@pindai-ai/chat-widget)
|
|
7
7
|
[](https://opensource.org/licenses/MIT)
|
|
8
|
-
[](https://www.jsdelivr.com/package/npm/@pindai-ai/chat-widget)
|
|
9
8
|
|
|
10
|
-
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
## Quick Start
|
|
12
|
+
|
|
13
|
+
### Option A — Pindai Agent-API (recommended)
|
|
14
|
+
|
|
15
|
+
Get your `agentId`, `embedSecret`, and `apiBaseUrl` from the Pindai dashboard (Chatbot → Embed tab).
|
|
16
|
+
|
|
17
|
+
```html
|
|
18
|
+
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@pindai-ai/chat-widget@3/dist/pindai-chat-widget.css">
|
|
19
|
+
<script type="module" src="https://cdn.jsdelivr.net/npm/@pindai-ai/chat-widget@3/dist/pindai-chat-widget.js"></script>
|
|
20
|
+
|
|
21
|
+
<script>
|
|
22
|
+
document.addEventListener('DOMContentLoaded', () => {
|
|
23
|
+
window.PindaiChatWidget.init({
|
|
24
|
+
agentId: 'YOUR_AGENT_UUID',
|
|
25
|
+
embedSecret: 'YOUR_EMBED_SECRET_HEX',
|
|
26
|
+
apiBaseUrl: 'https://api.yourcompany.com',
|
|
27
|
+
title: 'Customer Support',
|
|
28
|
+
showLogo: false,
|
|
29
|
+
});
|
|
30
|
+
});
|
|
31
|
+
</script>
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
### Option B — Generic Webhook (n8n, Dify, Express, FastAPI, etc.)
|
|
11
35
|
|
|
12
36
|
```html
|
|
13
|
-
|
|
14
|
-
<
|
|
15
|
-
<script type="module" src="https://cdn.jsdelivr.net/npm/@pindai-ai/chat-widget@2/dist/pindai-chat-widget.js"></script>
|
|
37
|
+
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@pindai-ai/chat-widget@3/dist/pindai-chat-widget.css">
|
|
38
|
+
<script type="module" src="https://cdn.jsdelivr.net/npm/@pindai-ai/chat-widget@3/dist/pindai-chat-widget.js"></script>
|
|
16
39
|
|
|
17
40
|
<script>
|
|
18
41
|
document.addEventListener('DOMContentLoaded', () => {
|
|
19
42
|
window.PindaiChatWidget.init({
|
|
20
|
-
webhookUrl: 'https://your-backend.com/webhook/chat'
|
|
43
|
+
webhookUrl: 'https://your-backend.com/webhook/chat',
|
|
44
|
+
showLogo: false,
|
|
21
45
|
});
|
|
22
46
|
});
|
|
23
47
|
</script>
|
|
24
48
|
```
|
|
25
49
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
-
|
|
31
|
-
-
|
|
32
|
-
-
|
|
33
|
-
-
|
|
34
|
-
-
|
|
35
|
-
-
|
|
36
|
-
-
|
|
37
|
-
-
|
|
38
|
-
-
|
|
39
|
-
-
|
|
40
|
-
-
|
|
50
|
+
---
|
|
51
|
+
|
|
52
|
+
## Features
|
|
53
|
+
|
|
54
|
+
- **Pindai Agent-API integration** — HMAC-SHA256 auth, SSE streaming, human-agent handoff
|
|
55
|
+
- **Modern UI** — smooth animations, mobile-first responsive design
|
|
56
|
+
- **Fully customizable** — avatar, bubble text, colors, dark/light/auto theme, button alignment, custom footer
|
|
57
|
+
- **Bilingual** — Indonesian (default) and English
|
|
58
|
+
- **File uploads** — PDF, images, Office docs (up to 10 MB, 5 files)
|
|
59
|
+
- **Message history** — localStorage persistence
|
|
60
|
+
- **Notification badge** — unread message count
|
|
61
|
+
- **Quick replies** — configurable suggested responses
|
|
62
|
+
- **Offline detection** — graceful network status handling
|
|
63
|
+
- **Retry logic** — exponential backoff on network errors
|
|
64
|
+
- **WCAG 2.2 AA** — full keyboard navigation, ARIA, 4.5:1 contrast
|
|
65
|
+
- **Zero runtime dependencies** — ~14 KB gzipped
|
|
66
|
+
- **Backward compatible** — existing `webhookUrl` integrations work unchanged
|
|
41
67
|
|
|
42
68
|
---
|
|
43
69
|
|
|
44
|
-
##
|
|
70
|
+
## Installation
|
|
45
71
|
|
|
46
72
|
### Via npm
|
|
47
73
|
|
|
@@ -51,472 +77,487 @@ npm install @pindai-ai/chat-widget
|
|
|
51
77
|
|
|
52
78
|
### Via CDN (jsDelivr)
|
|
53
79
|
|
|
54
|
-
**Latest 2.x version (recommended):**
|
|
55
80
|
```html
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
<script type="module"
|
|
59
|
-
src="https://cdn.jsdelivr.net/npm/@pindai-ai/chat-widget@2/dist/pindai-chat-widget.js"></script>
|
|
60
|
-
```
|
|
81
|
+
<!-- Latest 3.x -->
|
|
82
|
+
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@pindai-ai/chat-widget@3/dist/pindai-chat-widget.css">
|
|
83
|
+
<script type="module" src="https://cdn.jsdelivr.net/npm/@pindai-ai/chat-widget@3/dist/pindai-chat-widget.js"></script>
|
|
61
84
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
<
|
|
65
|
-
href="https://cdn.jsdelivr.net/npm/@pindai-ai/chat-widget@2.0.1/dist/pindai-chat-widget.css">
|
|
66
|
-
<script type="module"
|
|
67
|
-
src="https://cdn.jsdelivr.net/npm/@pindai-ai/chat-widget@2.0.1/dist/pindai-chat-widget.js"></script>
|
|
85
|
+
<!-- Specific version -->
|
|
86
|
+
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@pindai-ai/chat-widget@3.0.0/dist/pindai-chat-widget.css">
|
|
87
|
+
<script type="module" src="https://cdn.jsdelivr.net/npm/@pindai-ai/chat-widget@3.0.0/dist/pindai-chat-widget.js"></script>
|
|
68
88
|
```
|
|
69
89
|
|
|
70
|
-
|
|
90
|
+
---
|
|
71
91
|
|
|
72
|
-
|
|
92
|
+
## Configuration Options
|
|
73
93
|
|
|
74
|
-
|
|
75
|
-
# Clone repository
|
|
76
|
-
git clone https://github.com/pindai-ai/pindai-chat-widget.git
|
|
77
|
-
cd pindai-chat-widget
|
|
94
|
+
### Required (one of two modes)
|
|
78
95
|
|
|
79
|
-
|
|
80
|
-
|
|
96
|
+
| Option | Mode | Type | Description |
|
|
97
|
+
|--------|------|------|-------------|
|
|
98
|
+
| `agentId` | Agent-API | string | Agent UUID from Pindai dashboard |
|
|
99
|
+
| `embedSecret` | Agent-API | string | 32-byte hex embed secret from dashboard |
|
|
100
|
+
| `apiBaseUrl` | Agent-API | string | Base URL of your agent-api (no trailing slash) |
|
|
101
|
+
| `webhookUrl` | Legacy | string | Any HTTP POST endpoint |
|
|
81
102
|
|
|
82
|
-
|
|
83
|
-
npm run dev
|
|
103
|
+
### Display
|
|
84
104
|
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
105
|
+
| Option | Type | Default | Description |
|
|
106
|
+
|--------|------|---------|-------------|
|
|
107
|
+
| `mode` | string | `'widget'` | `'widget'` (floating button) or `'fullscreen'` |
|
|
108
|
+
| `locale` | string | `'id'` | `'id'` (Indonesian) or `'en'` (English) |
|
|
109
|
+
| `title` | string | Localized | Chat header title |
|
|
110
|
+
| `initialMessage` | string | Localized | First AI message |
|
|
88
111
|
|
|
89
|
-
|
|
112
|
+
### Branding
|
|
90
113
|
|
|
91
|
-
|
|
114
|
+
| Option | Type | Default | Description |
|
|
115
|
+
|--------|------|---------|-------------|
|
|
116
|
+
| `logoUrl` | string | Pindai logo | Header logo URL or data URI |
|
|
117
|
+
| `showLogo` | boolean | `true` | Show/hide header logo. **Recommended: `false`** unless you have a custom logo |
|
|
118
|
+
| `avatarUrl` | string | `null` | Circular avatar image in header (overrides logo) |
|
|
119
|
+
| `launcherIconUrl` | string | Chat SVG | Custom launcher button icon URL |
|
|
120
|
+
| `launcherColor` | string | `'#2563eb'` | Launcher button color |
|
|
121
|
+
| `sendButtonColor` | string | `'#2563eb'` | Send button color |
|
|
122
|
+
| `accentColor` | string | `'#2563eb'` | Accent color for UI elements |
|
|
92
123
|
|
|
93
|
-
###
|
|
124
|
+
### Theme & Layout
|
|
94
125
|
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
<meta charset="UTF-8">
|
|
100
|
-
<title>Your Website</title>
|
|
101
|
-
</head>
|
|
102
|
-
<body>
|
|
103
|
-
<h1>Welcome to My Website</h1>
|
|
104
|
-
<p>Your content here...</p>
|
|
105
|
-
|
|
106
|
-
<!-- Pindai Chat Widget -->
|
|
107
|
-
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@pindai-ai/chat-widget@2/dist/pindai-chat-widget.css">
|
|
108
|
-
<script type="module" src="https://cdn.jsdelivr.net/npm/@pindai-ai/chat-widget@2/dist/pindai-chat-widget.js"></script>
|
|
109
|
-
<script>
|
|
110
|
-
document.addEventListener('DOMContentLoaded', function () {
|
|
111
|
-
window.PindaiChatWidget.init({
|
|
112
|
-
webhookUrl: 'https://your-backend.com/webhook/chat',
|
|
113
|
-
title: 'Customer Support',
|
|
114
|
-
locale: 'id' // 'id' for Indonesian, 'en' for English
|
|
115
|
-
});
|
|
116
|
-
});
|
|
117
|
-
</script>
|
|
118
|
-
</body>
|
|
119
|
-
</html>
|
|
120
|
-
```
|
|
126
|
+
| Option | Type | Default | Description |
|
|
127
|
+
|--------|------|---------|-------------|
|
|
128
|
+
| `theme` | string | `'light'` | `'light'`, `'dark'`, or `'auto'` (follows system preference) |
|
|
129
|
+
| `buttonAlignment` | string | `'bottom-right'` | `'bottom-right'` or `'bottom-left'` |
|
|
121
130
|
|
|
122
|
-
###
|
|
131
|
+
### Footer / Branding Strip
|
|
123
132
|
|
|
124
|
-
|
|
133
|
+
| Option | Type | Default | Description |
|
|
134
|
+
|--------|------|---------|-------------|
|
|
135
|
+
| `showBranding` | boolean | `true` | Show "Powered by Pindai.ai" in footer |
|
|
136
|
+
| `customFooter` | string | `null` | Additional footer text (supports `[text](url)` links). Appended after branding |
|
|
125
137
|
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
138
|
+
**Footer examples:**
|
|
139
|
+
- `showBranding: true, customFooter: null` → `Powered by Pindai.ai`
|
|
140
|
+
- `showBranding: true, customFooter: 'Daniel'` → `Powered by Pindai.ai | Daniel`
|
|
141
|
+
- `showBranding: false, customFooter: '[My Company](https://myco.com)'` → `My Company` (linked)
|
|
142
|
+
- `showBranding: false, customFooter: null` → no footer
|
|
130
143
|
|
|
131
|
-
|
|
144
|
+
### Bubble
|
|
132
145
|
|
|
133
|
-
|
|
146
|
+
| Option | Type | Default | Description |
|
|
147
|
+
|--------|------|---------|-------------|
|
|
148
|
+
| `bubbleText` | string | `null` | Shorthand for a single bubble message. Equivalent to `bubbleMessages: ['your text']` |
|
|
149
|
+
| `bubbleMessages` | string[] | `null` | One or more messages shown in the speech bubble above the launcher. When multiple messages are provided, they rotate automatically |
|
|
150
|
+
| `bubbleDelay` | number | `3000` | Milliseconds to wait before the bubble first appears |
|
|
151
|
+
| `bubbleInterval` | number | `5000` | Milliseconds between rotating messages (only used when `bubbleMessages` has more than one entry) |
|
|
152
|
+
| `showBubbleOnce` | boolean | `true` | If `true`, the bubble is permanently hidden after the user dismisses it (stored in localStorage). Set to `false` to always show on page load |
|
|
134
153
|
|
|
135
|
-
|
|
154
|
+
### Visitor Info (Agent-API mode)
|
|
136
155
|
|
|
137
156
|
| Option | Type | Default | Description |
|
|
138
157
|
|--------|------|---------|-------------|
|
|
139
|
-
|
|
|
140
|
-
| `
|
|
141
|
-
| **Display** |
|
|
142
|
-
| `mode` | string | `'widget'` | Display mode: `'widget'` or `'fullscreen'` |
|
|
143
|
-
| `locale` | string | `'id'` | Language: `'id'` (Indonesian) or `'en'` (English) |
|
|
144
|
-
| `title` | string | Localized | Chat header title |
|
|
145
|
-
| `initialMessage` | string | Localized | First AI message |
|
|
146
|
-
| **Branding** |
|
|
147
|
-
| `logoUrl` | string | `'https://pindai.ai/logo.png'` | Header logo URL |
|
|
148
|
-
| `showLogo` | boolean | `true` | Show/hide logo |
|
|
149
|
-
| `launcherColor` | string | `'#0066FF'` | Launcher button background color |
|
|
150
|
-
| `launcherIconUrl` | string | Default icon | Custom launcher icon URL |
|
|
151
|
-
| `sendButtonColor` | string | `'#0066FF'` | Send button background color |
|
|
152
|
-
| `accentColor` | string | `'#00C896'` | Accent color for UI elements |
|
|
153
|
-
| **File Upload** |
|
|
154
|
-
| `enableFileUpload` | boolean | `true` | Enable file attachments |
|
|
155
|
-
| `allowedFileTypes` | array | See below | Accepted MIME types |
|
|
156
|
-
| `maxFileSize` | number | `10485760` | Max file size in bytes (10MB default) |
|
|
157
|
-
| `maxFiles` | number | `5` | Max files per message |
|
|
158
|
-
| **Features** |
|
|
159
|
-
| `enableNotifications` | boolean | `true` | Show unread badges |
|
|
160
|
-
| `enableSound` | boolean | `false` | Play notification sound |
|
|
161
|
-
| `enableHistory` | boolean | `true` | Persist message history |
|
|
162
|
-
| `maxHistoryItems` | number | `50` | Max messages to store |
|
|
163
|
-
| `showQuickReplies` | boolean | `true` | Show quick reply buttons |
|
|
164
|
-
| `quickReplies` | array | Localized | Custom suggested responses |
|
|
165
|
-
| **Technical** |
|
|
166
|
-
| `maxRetries` | number | `3` | API retry attempts |
|
|
167
|
-
| `retryDelay` | number | `1000` | Retry delay in ms |
|
|
168
|
-
| `requestTimeout` | number | `30000` | Request timeout in ms (30s) |
|
|
169
|
-
| `rateLimit` | number | `5` | Messages per minute |
|
|
170
|
-
| `rateLimitWindow` | number | `60000` | Rate limit window in ms |
|
|
158
|
+
| `visitorName` | string | `null` | Visitor name forwarded to the API |
|
|
159
|
+
| `visitorEmail` | string | `null` | Visitor email forwarded to the API |
|
|
171
160
|
|
|
172
|
-
###
|
|
161
|
+
### Streaming (Agent-API mode)
|
|
173
162
|
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
'application/pdf',
|
|
178
|
-
'application/msword',
|
|
179
|
-
'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
|
|
180
|
-
'application/vnd.ms-excel',
|
|
181
|
-
'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
|
|
182
|
-
]
|
|
183
|
-
```
|
|
163
|
+
| Option | Type | Default | Description |
|
|
164
|
+
|--------|------|---------|-------------|
|
|
165
|
+
| `enableStreaming` | boolean | `true` | Use SSE streaming for real-time response rendering. Falls back to POST when files are attached |
|
|
184
166
|
|
|
185
|
-
|
|
167
|
+
### Action Indicators
|
|
186
168
|
|
|
187
|
-
|
|
169
|
+
| Option | Type | Default | Description |
|
|
170
|
+
|--------|------|---------|-------------|
|
|
171
|
+
| `showActionIndicators` | boolean | `true` | Show "Thinking..." label in typing indicator |
|
|
188
172
|
|
|
189
|
-
###
|
|
173
|
+
### File Upload
|
|
190
174
|
|
|
191
|
-
|
|
175
|
+
| Option | Type | Default | Description |
|
|
176
|
+
|--------|------|---------|-------------|
|
|
177
|
+
| `enableFileUpload` | boolean | `true` | Enable file attachments |
|
|
178
|
+
| `allowedFileTypes` | array | See below | Accepted MIME types |
|
|
179
|
+
| `maxFileSize` | number | `10485760` | Max file size in bytes (10 MB) |
|
|
180
|
+
| `maxFiles` | number | `5` | Max files per message |
|
|
192
181
|
|
|
182
|
+
**Default allowed file types:**
|
|
193
183
|
```javascript
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
// ...
|
|
200
|
-
}
|
|
184
|
+
['image/jpeg', 'image/png', 'image/gif', 'image/webp',
|
|
185
|
+
'application/pdf', 'application/msword',
|
|
186
|
+
'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
|
|
187
|
+
'application/vnd.ms-excel',
|
|
188
|
+
'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet']
|
|
201
189
|
```
|
|
202
190
|
|
|
203
|
-
###
|
|
204
|
-
|
|
205
|
-
Your backend should respond with JSON:
|
|
191
|
+
### Features
|
|
206
192
|
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
193
|
+
| Option | Type | Default | Description |
|
|
194
|
+
|--------|------|---------|-------------|
|
|
195
|
+
| `enableNotifications` | boolean | `true` | Show unread message badge |
|
|
196
|
+
| `enableSound` | boolean | `false` | Notification sound (not yet implemented) |
|
|
197
|
+
| `enableHistory` | boolean | `true` | Persist messages to localStorage |
|
|
198
|
+
| `maxHistoryItems` | number | `50` | Max stored messages |
|
|
199
|
+
| `showQuickReplies` | boolean | `true` | Show quick reply buttons |
|
|
200
|
+
| `quickReplies` | array | Localized | Custom suggested responses |
|
|
214
201
|
|
|
215
|
-
|
|
216
|
-
<summary><strong>n8n Workflow (Recommended)</strong></summary>
|
|
202
|
+
### Technical
|
|
217
203
|
|
|
218
|
-
|
|
204
|
+
| Option | Type | Default | Description |
|
|
205
|
+
|--------|------|---------|-------------|
|
|
206
|
+
| `maxRetries` | number | `3` | Retry attempts on 5xx errors |
|
|
207
|
+
| `retryDelay` | number | `1000` | Base retry delay in ms (multiplied per attempt) |
|
|
208
|
+
| `requestTimeout` | number | `30000` | Request timeout in ms |
|
|
209
|
+
| `rateLimit` | number | `5` | Max messages per `rateLimitWindow` |
|
|
210
|
+
| `rateLimitWindow` | number | `60000` | Rate limit window in ms |
|
|
219
211
|
|
|
220
|
-
|
|
212
|
+
### Polling (human-agent handoff)
|
|
221
213
|
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
214
|
+
| Option | Type | Default | Description |
|
|
215
|
+
|--------|------|---------|-------------|
|
|
216
|
+
| `pollingInterval` | number | `3000` | Polling interval in ms |
|
|
217
|
+
| `pollingUrl` | string | `null` | **Legacy only.** In Agent-API mode, polling URL is auto-derived |
|
|
226
218
|
|
|
227
|
-
|
|
228
|
-
- AI Agent with Indonesian system prompt
|
|
229
|
-
- Chat memory (remembers last 10 messages per session)
|
|
230
|
-
- Automatic response formatting
|
|
231
|
-
- Ready for production use
|
|
219
|
+
---
|
|
232
220
|
|
|
233
|
-
|
|
221
|
+
## Agent-API Authentication
|
|
234
222
|
|
|
235
|
-
**
|
|
223
|
+
The widget uses **HMAC-SHA256** to sign each request, ensuring only authorized widgets can send messages to your agents.
|
|
236
224
|
|
|
237
|
-
|
|
225
|
+
**How it works:**
|
|
238
226
|
|
|
239
|
-
|
|
227
|
+
1. The dashboard generates a per-agent `embedSecret` (32-byte hex string)
|
|
228
|
+
2. On each browser session, the widget generates a random `sessionId`
|
|
229
|
+
3. The widget computes:
|
|
230
|
+
```
|
|
231
|
+
embedToken = HMAC-SHA256(embedSecret, "{agentId}:{sessionId}")
|
|
232
|
+
```
|
|
233
|
+
using native `crypto.subtle` — no external libraries needed
|
|
234
|
+
4. The token is sent with every request as `embedToken` in FormData
|
|
235
|
+
5. The agent-api server verifies it using the same formula
|
|
240
236
|
|
|
241
|
-
|
|
242
|
-
<summary><strong>Express.js Server</strong></summary>
|
|
237
|
+
**To rotate the secret** (invalidates all existing tokens): use the "Rotate Secret" button in your dashboard or call `POST /v1/agents/{agentId}/rotate-secret`.
|
|
243
238
|
|
|
244
|
-
|
|
245
|
-
const express = require('express');
|
|
246
|
-
const app = express();
|
|
239
|
+
---
|
|
247
240
|
|
|
248
|
-
|
|
249
|
-
const { sessionId, message } = req.body;
|
|
241
|
+
## SSE Streaming
|
|
250
242
|
|
|
251
|
-
|
|
252
|
-
const aiResponse = await processWithAI(message);
|
|
243
|
+
When `enableStreaming: true` (default in Agent-API mode), the widget connects to the SSE endpoint and renders the response incrementally as it arrives — similar to ChatGPT's streaming UI.
|
|
253
244
|
|
|
254
|
-
|
|
255
|
-
}
|
|
245
|
+
```
|
|
246
|
+
GET /v1/chat/{agentId}/stream?message=...&sessionId=...&embedToken=...
|
|
256
247
|
```
|
|
257
248
|
|
|
258
|
-
|
|
249
|
+
**SSE event format:**
|
|
250
|
+
```
|
|
251
|
+
data: {"delta": "Hello"}
|
|
252
|
+
data: {"delta": " there!"}
|
|
253
|
+
data: {"type": "done", "status": "active"}
|
|
254
|
+
```
|
|
259
255
|
|
|
260
|
-
|
|
261
|
-
<summary><strong>Python Flask</strong></summary>
|
|
256
|
+
**Note:** When files are attached, streaming is automatically disabled and a regular POST is used instead (SSE does not support request bodies).
|
|
262
257
|
|
|
263
|
-
|
|
264
|
-
from flask import Flask, request, jsonify
|
|
258
|
+
---
|
|
265
259
|
|
|
266
|
-
|
|
260
|
+
## Human-Agent Handoff
|
|
267
261
|
|
|
268
|
-
|
|
269
|
-
def chat():
|
|
270
|
-
data = request.get_json()
|
|
271
|
-
session_id = data.get('sessionId')
|
|
272
|
-
message = data.get('message')
|
|
262
|
+
When your agent triggers a handoff (e.g., via `handoff_condition` in the dashboard), the conversation status changes from `active` to `pending` or `assigned`. The widget automatically:
|
|
273
263
|
|
|
274
|
-
|
|
275
|
-
|
|
264
|
+
1. Starts polling `GET /v1/chat/{agentId}/messages?since=...` every 3 seconds
|
|
265
|
+
2. Displays messages from human agents in the chat window
|
|
266
|
+
3. Shows a "A team member has joined the conversation." notification
|
|
267
|
+
4. Stops polling when status returns to `active` or `resolved`
|
|
276
268
|
|
|
277
|
-
|
|
278
|
-
```
|
|
269
|
+
---
|
|
279
270
|
|
|
280
|
-
|
|
271
|
+
## Customization Examples
|
|
281
272
|
|
|
282
|
-
|
|
273
|
+
### Custom Branding + Dark Theme
|
|
283
274
|
|
|
284
|
-
|
|
275
|
+
```javascript
|
|
276
|
+
window.PindaiChatWidget.init({
|
|
277
|
+
agentId: 'YOUR_AGENT_UUID',
|
|
278
|
+
embedSecret: 'YOUR_SECRET',
|
|
279
|
+
apiBaseUrl: 'https://api.yourcompany.com',
|
|
280
|
+
title: 'Support Center',
|
|
281
|
+
avatarUrl: 'https://yourcompany.com/avatar.png',
|
|
282
|
+
launcherColor: '#7c3aed',
|
|
283
|
+
sendButtonColor: '#7c3aed',
|
|
284
|
+
theme: 'dark',
|
|
285
|
+
showBranding: false,
|
|
286
|
+
customFooter: 'Powered by Acme Corp',
|
|
287
|
+
});
|
|
288
|
+
```
|
|
285
289
|
|
|
286
|
-
###
|
|
290
|
+
### Bubble + Left Alignment
|
|
287
291
|
|
|
288
292
|
```javascript
|
|
293
|
+
// Single static message
|
|
289
294
|
window.PindaiChatWidget.init({
|
|
290
295
|
webhookUrl: 'https://your-backend.com/webhook/chat',
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
296
|
+
bubbleText: 'Hey! Need help? Ask us anything 👋',
|
|
297
|
+
bubbleDelay: 3000, // appears after 3 seconds
|
|
298
|
+
showBubbleOnce: true, // won't show again after user dismisses
|
|
299
|
+
buttonAlignment: 'bottom-left',
|
|
300
|
+
showLogo: false,
|
|
301
|
+
});
|
|
302
|
+
|
|
303
|
+
// Rotating messages — great for product tours or proactive outreach
|
|
304
|
+
window.PindaiChatWidget.init({
|
|
305
|
+
webhookUrl: 'https://your-backend.com/webhook/chat',
|
|
306
|
+
bubbleMessages: [
|
|
307
|
+
'Need help? We reply instantly! 👋',
|
|
308
|
+
'Ask me anything about our products!',
|
|
309
|
+
'Get support 24/7 — no waiting.',
|
|
310
|
+
],
|
|
311
|
+
bubbleDelay: 2000, // first message after 2s
|
|
312
|
+
bubbleInterval: 5000, // rotate every 5s
|
|
313
|
+
showBubbleOnce: false, // always show on page load
|
|
314
|
+
showLogo: false,
|
|
297
315
|
});
|
|
298
316
|
```
|
|
299
317
|
|
|
300
|
-
###
|
|
318
|
+
### Auto Dark/Light Theme
|
|
301
319
|
|
|
302
320
|
```javascript
|
|
303
321
|
window.PindaiChatWidget.init({
|
|
304
322
|
webhookUrl: 'https://your-backend.com/webhook/chat',
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
'Lupa password',
|
|
308
|
-
'Hubungi customer service',
|
|
309
|
-
'Lihat demo produk'
|
|
310
|
-
],
|
|
323
|
+
theme: 'auto', // Follows system prefers-color-scheme
|
|
324
|
+
showLogo: false,
|
|
311
325
|
});
|
|
312
326
|
```
|
|
313
327
|
|
|
314
|
-
###
|
|
328
|
+
### Minimal (no uploads, no quick replies, no branding)
|
|
315
329
|
|
|
316
330
|
```javascript
|
|
317
331
|
window.PindaiChatWidget.init({
|
|
318
332
|
webhookUrl: 'https://your-backend.com/webhook/chat',
|
|
319
|
-
|
|
320
|
-
|
|
333
|
+
enableFileUpload: false,
|
|
334
|
+
showQuickReplies: false,
|
|
335
|
+
showBranding: false,
|
|
336
|
+
showLogo: false,
|
|
321
337
|
});
|
|
322
338
|
```
|
|
323
339
|
|
|
324
|
-
###
|
|
340
|
+
### Custom Quick Replies
|
|
325
341
|
|
|
326
342
|
```javascript
|
|
327
343
|
window.PindaiChatWidget.init({
|
|
328
344
|
webhookUrl: 'https://your-backend.com/webhook/chat',
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
345
|
+
quickReplies: [
|
|
346
|
+
'How do I reset my password?',
|
|
347
|
+
'Track my order',
|
|
348
|
+
'Contact sales',
|
|
349
|
+
'View documentation',
|
|
350
|
+
],
|
|
332
351
|
});
|
|
333
352
|
```
|
|
334
353
|
|
|
335
354
|
---
|
|
336
355
|
|
|
337
|
-
##
|
|
356
|
+
## Backend API Reference
|
|
338
357
|
|
|
339
|
-
|
|
358
|
+
### Agent-API endpoints (auto-used in agent-api mode)
|
|
340
359
|
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
✅ Text resizable to 200%
|
|
347
|
-
✅ Touch targets ≥ 44×44px
|
|
360
|
+
| Endpoint | Method | Purpose |
|
|
361
|
+
|----------|--------|---------|
|
|
362
|
+
| `/v1/chat/{agentId}/message` | POST | Send a user message |
|
|
363
|
+
| `/v1/chat/{agentId}/stream` | GET (SSE) | Stream AI response |
|
|
364
|
+
| `/v1/chat/{agentId}/messages` | GET | Poll for human-agent replies |
|
|
348
365
|
|
|
349
|
-
|
|
366
|
+
**POST /v1/chat/{agentId}/message** — FormData fields:
|
|
350
367
|
|
|
351
|
-
|
|
|
352
|
-
|
|
353
|
-
| `
|
|
354
|
-
| `
|
|
355
|
-
| `
|
|
356
|
-
| `
|
|
368
|
+
| Field | Required | Description |
|
|
369
|
+
|-------|----------|-------------|
|
|
370
|
+
| `sessionId` | Yes | Widget-generated session identifier |
|
|
371
|
+
| `message` | Yes | User message text |
|
|
372
|
+
| `embedToken` | Yes | HMAC-SHA256 embed token |
|
|
373
|
+
| `visitor_name` | No | Pre-filled visitor name |
|
|
374
|
+
| `visitor_email` | No | Pre-filled visitor email |
|
|
375
|
+
| `file0`...`fileN` | No | Uploaded files |
|
|
376
|
+
|
|
377
|
+
**Response:**
|
|
378
|
+
```json
|
|
379
|
+
{
|
|
380
|
+
"response": "AI response text",
|
|
381
|
+
"conversation_id": "uuid",
|
|
382
|
+
"status": "active"
|
|
383
|
+
}
|
|
384
|
+
```
|
|
385
|
+
|
|
386
|
+
### Generic webhook (legacy mode)
|
|
387
|
+
|
|
388
|
+
**Request** (FormData POST):
|
|
389
|
+
```
|
|
390
|
+
sessionId=web-session-...&message=Hello&file0=<File>
|
|
391
|
+
```
|
|
392
|
+
|
|
393
|
+
**Response** (JSON):
|
|
394
|
+
```json
|
|
395
|
+
{ "response": "AI response text" }
|
|
396
|
+
```
|
|
397
|
+
|
|
398
|
+
---
|
|
399
|
+
|
|
400
|
+
## Accessibility
|
|
401
|
+
|
|
402
|
+
WCAG 2.2 Level AA compliant:
|
|
403
|
+
|
|
404
|
+
- Full keyboard navigation (Tab, Shift+Tab, Enter, ESC)
|
|
405
|
+
- ARIA labels on all interactive elements
|
|
406
|
+
- `aria-live` region for chat messages
|
|
407
|
+
- Focus trap within the chat dialog
|
|
408
|
+
- 4.5:1 minimum color contrast ratios
|
|
409
|
+
- 44×44 px minimum touch targets
|
|
410
|
+
- Respects `prefers-reduced-motion`
|
|
411
|
+
- Screen reader compatible
|
|
357
412
|
|
|
358
413
|
---
|
|
359
414
|
|
|
360
|
-
##
|
|
415
|
+
## Browser Support
|
|
361
416
|
|
|
362
|
-
| Browser | Version |
|
|
363
|
-
|
|
417
|
+
| Browser | Minimum Version |
|
|
418
|
+
|---------|----------------|
|
|
364
419
|
| Chrome/Edge | 90+ |
|
|
365
420
|
| Firefox | 88+ |
|
|
366
421
|
| Safari | 14+ |
|
|
367
422
|
| iOS Safari | 14+ |
|
|
368
423
|
| Android Chrome | 90+ |
|
|
369
424
|
|
|
425
|
+
**Required APIs:** `fetch`, `EventSource`, `crypto.subtle`, `FormData`, `localStorage`
|
|
426
|
+
|
|
370
427
|
---
|
|
371
428
|
|
|
372
|
-
##
|
|
429
|
+
## Development
|
|
373
430
|
|
|
374
|
-
|
|
431
|
+
```bash
|
|
432
|
+
git clone https://github.com/PindaiAI/pindai-chat-widget.git
|
|
433
|
+
cd pindai-chat-widget
|
|
434
|
+
npm install
|
|
435
|
+
npm run dev # Dev server at http://localhost:5173
|
|
436
|
+
npm run build # Production build → dist/
|
|
437
|
+
npm run preview # Preview production build
|
|
438
|
+
```
|
|
375
439
|
|
|
440
|
+
**Project structure:**
|
|
376
441
|
```
|
|
377
|
-
pindai-chat/
|
|
442
|
+
pindai-chat-widget/
|
|
378
443
|
├── src/
|
|
379
|
-
│ ├── main.js
|
|
380
|
-
│ ├── style.css
|
|
381
|
-
│ └── i18n.js
|
|
382
|
-
├── dist/
|
|
383
|
-
├──
|
|
384
|
-
├──
|
|
385
|
-
|
|
386
|
-
├── package.json # Package metadata
|
|
387
|
-
└── README.md # Documentation
|
|
444
|
+
│ ├── main.js # Widget class (all logic)
|
|
445
|
+
│ ├── style.css # All styles (design system + components)
|
|
446
|
+
│ └── i18n.js # Indonesian + English translations
|
|
447
|
+
├── dist/ # Built assets (committed for CDN)
|
|
448
|
+
├── index.html # Interactive demo page
|
|
449
|
+
├── vite.config.js # Build config
|
|
450
|
+
└── package.json
|
|
388
451
|
```
|
|
389
452
|
|
|
390
|
-
|
|
453
|
+
---
|
|
391
454
|
|
|
392
|
-
|
|
393
|
-
# Development server with hot reload
|
|
394
|
-
npm run dev
|
|
455
|
+
## Troubleshooting
|
|
395
456
|
|
|
396
|
-
|
|
397
|
-
npm run build
|
|
457
|
+
### Logo shows broken image
|
|
398
458
|
|
|
399
|
-
|
|
400
|
-
|
|
459
|
+
Set `showLogo: false` (recommended) or provide a valid `logoUrl`:
|
|
460
|
+
```javascript
|
|
461
|
+
window.PindaiChatWidget.init({ webhookUrl: '...', showLogo: false });
|
|
401
462
|
```
|
|
402
463
|
|
|
403
|
-
###
|
|
464
|
+
### Widget not appearing
|
|
404
465
|
|
|
405
|
-
1.
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
466
|
+
1. Check browser console for errors (F12)
|
|
467
|
+
2. Verify `agentId`/`embedSecret`/`apiBaseUrl` or `webhookUrl` is correct
|
|
468
|
+
3. Ensure script loads after DOM is ready (`DOMContentLoaded`)
|
|
469
|
+
4. Check for CSP (Content Security Policy) restrictions
|
|
409
470
|
|
|
410
|
-
|
|
471
|
+
### CORS errors
|
|
411
472
|
|
|
412
|
-
|
|
413
|
-
- Click launcher to open chat
|
|
414
|
-
- Send messages
|
|
415
|
-
- Upload files (PDF, images)
|
|
416
|
-
- Test quick replies
|
|
417
|
-
- Try keyboard navigation (Tab, ESC)
|
|
418
|
-
- Test offline mode (DevTools > Network > Offline)
|
|
419
|
-
- Check mobile responsiveness (DevTools > Device Toolbar)
|
|
473
|
+
Add the widget origin to your API's CORS `allow_origins`. In development, allow `http://localhost:5173`.
|
|
420
474
|
|
|
421
|
-
|
|
422
|
-
- Open DevTools > Lighthouse
|
|
423
|
-
- Run Accessibility audit
|
|
424
|
-
- Should score 100
|
|
475
|
+
### Streaming not working
|
|
425
476
|
|
|
426
|
-
|
|
477
|
+
1. Verify your agent-api version supports SSE (`GET /v1/chat/{agentId}/stream`)
|
|
478
|
+
2. Check browser support: all modern browsers support `EventSource`
|
|
479
|
+
3. NGINX/proxy: ensure SSE headers are not buffered — add `proxy_buffering off;`
|
|
480
|
+
4. Disable streaming: `enableStreaming: false` to fall back to POST
|
|
427
481
|
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
### Version 2.0.1 (2026-02-06)
|
|
431
|
-
|
|
432
|
-
**Updates:**
|
|
433
|
-
- Vendor-agnostic API: Changed `n8nUrl` to `webhookUrl` parameter
|
|
434
|
-
- Added backward compatibility for `n8nUrl`
|
|
435
|
-
- Added "Powered by Pindai.ai" watermark in widget footer
|
|
436
|
-
- Updated documentation with n8n workflow examples
|
|
437
|
-
- Included ready-to-use n8n workflow JSON files
|
|
438
|
-
|
|
439
|
-
### Version 2.0.0 (2026-02-05)
|
|
440
|
-
|
|
441
|
-
**Major Changes:**
|
|
442
|
-
- Complete UI/UX redesign with Pindai.ai branding
|
|
443
|
-
- Indonesian localization (default) with English support
|
|
444
|
-
- File upload capability (PDF, images, documents)
|
|
445
|
-
- WCAG 2.2 AA accessibility compliance
|
|
446
|
-
- Mobile-first responsive design
|
|
447
|
-
- Message history persistence
|
|
448
|
-
- Quick reply buttons
|
|
449
|
-
- Notification badges
|
|
450
|
-
- Enhanced error handling with retry logic
|
|
451
|
-
- Offline detection and user messaging
|
|
452
|
-
- Rate limiting
|
|
453
|
-
- Keyboard navigation (Tab, ESC)
|
|
482
|
+
### CDN cache stale after publish
|
|
454
483
|
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
-
|
|
458
|
-
|
|
459
|
-
- FormData for file uploads
|
|
460
|
-
- localStorage for history/state
|
|
461
|
-
- Backward compatibility maintained
|
|
484
|
+
```bash
|
|
485
|
+
# Force purge jsDelivr cache
|
|
486
|
+
curl https://purge.jsdelivr.net/npm/@pindai-ai/chat-widget@3/dist/pindai-chat-widget.js
|
|
487
|
+
```
|
|
462
488
|
|
|
463
|
-
|
|
464
|
-
- Default locale changed to Indonesian (`'id'`)
|
|
465
|
-
- File uploads now use FormData instead of JSON
|
|
466
|
-
- Some CSS class names updated
|
|
489
|
+
Or use a version-pinned URL: `@pindai-ai/chat-widget@3.0.0`
|
|
467
490
|
|
|
468
|
-
|
|
491
|
+
---
|
|
469
492
|
|
|
470
|
-
|
|
471
|
-
- Basic chat functionality
|
|
472
|
-
- n8n integration
|
|
493
|
+
## Changelog
|
|
473
494
|
|
|
474
|
-
|
|
495
|
+
### Version 3.0.0
|
|
475
496
|
|
|
476
|
-
|
|
497
|
+
**Breaking Changes:**
|
|
498
|
+
- CDN paths use `@3` tag (old `@2` still works for 2.x users)
|
|
499
|
+
- Launcher is now inside `.n8n-chat-launcher-wrapper` (affects custom CSS targeting `.n8n-chat-launcher` for positioning)
|
|
500
|
+
|
|
501
|
+
**New Features:**
|
|
502
|
+
- **Pindai Agent-API integration**: `agentId`, `embedSecret`, `apiBaseUrl` props
|
|
503
|
+
- **HMAC-SHA256 auth**: per-session embed tokens via native `crypto.subtle`
|
|
504
|
+
- **SSE streaming**: real-time incremental response rendering with blinking cursor
|
|
505
|
+
- **Human-agent polling**: auto-derived from `apiBaseUrl` in agent mode
|
|
506
|
+
- **Dark theme**: `theme: 'dark'` or `theme: 'auto'`
|
|
507
|
+
- **Avatar**: `avatarUrl` prop for circular header avatar
|
|
508
|
+
- **Bubble text**: `bubbleText` speech bubble above launcher with dismiss button
|
|
509
|
+
- **Button alignment**: `buttonAlignment: 'bottom-left'` support
|
|
510
|
+
- **Custom footer**: `customFooter` prop with safe Markdown link rendering
|
|
511
|
+
- **Show branding toggle**: `showBranding: false` hides "Powered by Pindai.ai"
|
|
512
|
+
- **Visitor info**: `visitorName`, `visitorEmail` forwarded to API
|
|
513
|
+
- **Action indicators**: `showActionIndicators` with "Thinking..." typing label
|
|
514
|
+
- **Exports field**: proper `package.json` `exports` map for Node.js
|
|
477
515
|
|
|
478
|
-
|
|
516
|
+
**Technical:**
|
|
517
|
+
- `package.json` version → `3.0.0`
|
|
518
|
+
- All private methods prefixed with `_` (public aliases kept for compatibility)
|
|
519
|
+
- `historyKey`/`stateKey` now uses `agentId` in agent mode (prevents cross-agent history collisions)
|
|
479
520
|
|
|
480
|
-
|
|
481
|
-
2. Create a feature branch (`git checkout -b feature/amazing-feature`)
|
|
482
|
-
3. Commit your changes (`git commit -m 'Add amazing feature'`)
|
|
483
|
-
4. Push to the branch (`git push origin feature/amazing-feature`)
|
|
484
|
-
5. Open a Pull Request
|
|
521
|
+
### Version 2.0.4
|
|
485
522
|
|
|
486
|
-
|
|
523
|
+
- Bug fixes and stability improvements
|
|
487
524
|
|
|
488
|
-
|
|
525
|
+
### Version 2.0.3
|
|
489
526
|
|
|
490
|
-
|
|
527
|
+
- Documentation: `showLogo` best practices, troubleshooting guide
|
|
491
528
|
|
|
492
|
-
|
|
529
|
+
### Version 2.0.2
|
|
493
530
|
|
|
494
|
-
|
|
531
|
+
- Published watermark to npm/CDN
|
|
495
532
|
|
|
496
|
-
|
|
497
|
-
- **jsDelivr CDN:** [jsdelivr.com/package/npm/@pindai-ai/chat-widget](https://www.jsdelivr.com/package/npm/@pindai-ai/chat-widget)
|
|
498
|
-
- **GitHub Repository:** [github.com/pindai-ai/pindai-chat-widget](https://github.com/pindai-ai/pindai-chat-widget)
|
|
499
|
-
- **Issues & Bugs:** [github.com/pindai-ai/pindai-chat-widget/issues](https://github.com/pindai-ai/pindai-chat-widget/issues)
|
|
500
|
-
- **Pindai.ai Website:** [pindai.ai](https://pindai.ai)
|
|
533
|
+
### Version 2.0.1
|
|
501
534
|
|
|
502
|
-
|
|
535
|
+
- Renamed `n8nUrl` → `webhookUrl` (backward compatible)
|
|
536
|
+
- Added "Powered by Pindai.ai" watermark
|
|
503
537
|
|
|
504
|
-
|
|
538
|
+
### Version 2.0.0
|
|
505
539
|
|
|
506
|
-
-
|
|
507
|
-
-
|
|
508
|
-
-
|
|
509
|
-
-
|
|
540
|
+
- Complete UI/UX redesign
|
|
541
|
+
- Indonesian localization (default)
|
|
542
|
+
- File upload, WCAG 2.2 AA, mobile-first
|
|
543
|
+
- Quick replies, notification badge, retry logic, offline detection
|
|
544
|
+
|
|
545
|
+
### Version 1.0.0
|
|
546
|
+
|
|
547
|
+
- Initial release
|
|
510
548
|
|
|
511
549
|
---
|
|
512
550
|
|
|
513
|
-
##
|
|
551
|
+
## License
|
|
514
552
|
|
|
515
|
-
|
|
516
|
-
- Built with modern web standards and accessibility best practices
|
|
517
|
-
- Designed for Indonesian enterprises
|
|
518
|
-
- Powered by Pindai.ai's AI document extraction technology
|
|
553
|
+
MIT © [Pindai.ai](https://pindai.ai)
|
|
519
554
|
|
|
520
555
|
---
|
|
521
556
|
|
|
522
|
-
|
|
557
|
+
## Links
|
|
558
|
+
|
|
559
|
+
- **npm:** [npmjs.com/package/@pindai-ai/chat-widget](https://www.npmjs.com/package/@pindai-ai/chat-widget)
|
|
560
|
+
- **CDN:** [jsdelivr.com/package/npm/@pindai-ai/chat-widget](https://www.jsdelivr.com/package/npm/@pindai-ai/chat-widget)
|
|
561
|
+
- **GitHub:** [github.com/PindaiAI/pindai-chat-widget](https://github.com/PindaiAI/pindai-chat-widget)
|
|
562
|
+
- **Issues:** [github.com/PindaiAI/pindai-chat-widget/issues](https://github.com/PindaiAI/pindai-chat-widget/issues)
|
|
563
|
+
- **Pindai.ai:** [pindai.ai](https://pindai.ai)
|