@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 CHANGED
@@ -1,47 +1,73 @@
1
1
  # Pindai Chat Widget
2
2
 
3
- > Modern, accessible chat widget for Pindai.ai - AI-powered document extraction for Indonesian enterprises
3
+ > Modern, accessible, embeddable chat widget seamlessly integrated with **Pindai Agent-API** or any generic HTTP backend.
4
4
 
5
5
  [![npm version](https://img.shields.io/npm/v/@pindai-ai/chat-widget.svg)](https://www.npmjs.com/package/@pindai-ai/chat-widget)
6
6
  [![npm downloads](https://img.shields.io/npm/dm/@pindai-ai/chat-widget.svg)](https://www.npmjs.com/package/@pindai-ai/chat-widget)
7
7
  [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT)
8
- [![jsDelivr hits](https://img.shields.io/jsdelivr/npm/hm/@pindai-ai/chat-widget)](https://www.jsdelivr.com/package/npm/@pindai-ai/chat-widget)
9
8
 
10
- ## 🚀 Quick Start
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
- <!-- Add this to your HTML -->
14
- <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@pindai-ai/chat-widget@2/dist/pindai-chat-widget.css">
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
- ## ✨ Features
27
-
28
- - 🎨 **Modern UI** - HubSpot-quality design with smooth animations and Pindai.ai branding
29
- - ♿ **Accessible** - WCAG 2.2 AA compliant with full keyboard navigation and screen reader support
30
- - 📱 **Mobile-First** - Optimized responsive design for all devices
31
- - 🌐 **Bilingual** - Indonesian (default) and English localization
32
- - 📎 **File Upload** - Support for PDFs, images, and documents
33
- - 💾 **Persistent** - Message history and state saved to localStorage
34
- - 🔔 **Notifications** - Unread message badges and optional sound alerts
35
- - **Quick Replies** - Suggested responses for common questions
36
- - 🔄 **Retry Logic** - Automatic retry with exponential backoff on network errors
37
- - 📡 **Offline Support** - Graceful offline detection and user messaging
38
- - ⌨️ **Keyboard Nav** - Full keyboard support (Tab, Enter, ESC)
39
- - 🎯 **Lightweight** - Only ~12KB gzipped (JS + CSS)
40
- - 🔧 **Backend Agnostic** - Works with n8n, Dify, custom APIs, or any HTTP endpoint
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
- ## 📦 Installation
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
- <link rel="stylesheet"
57
- href="https://cdn.jsdelivr.net/npm/@pindai-ai/chat-widget@2/dist/pindai-chat-widget.css">
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
- **Specific version (2.0.1):**
63
- ```html
64
- <link rel="stylesheet"
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
- > Using `@2` automatically gives you the latest 2.x.x version with bug fixes and improvements. Use `@2.0.1` if you need a specific version.
90
+ ---
71
91
 
72
- ### Local Development
92
+ ## Configuration Options
73
93
 
74
- ```bash
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
- # Install dependencies
80
- npm install
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
- # Run development server
83
- npm run dev
103
+ ### Display
84
104
 
85
- # Build for production
86
- npm run build
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
- ## 📖 Usage Examples
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
- ### Complete HTML Example
124
+ ### Theme & Layout
94
125
 
95
- ```html
96
- <!DOCTYPE html>
97
- <html lang="id">
98
- <head>
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
- ### Using During Development
131
+ ### Footer / Branding Strip
123
132
 
124
- If the widget isn't on npm yet, use the GitHub CDN:
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
- ```html
127
- <link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/YOUR-USERNAME/pindai-chat-widget@main/dist/pindai-chat-widget.css">
128
- <script type="module" src="https://cdn.jsdelivr.net/gh/YOUR-USERNAME/pindai-chat-widget@main/dist/pindai-chat-widget.js"></script>
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
- Replace `YOUR-USERNAME` with your GitHub username
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
- ## ⚙️ Configuration Options
154
+ ### Visitor Info (Agent-API mode)
136
155
 
137
156
  | Option | Type | Default | Description |
138
157
  |--------|------|---------|-------------|
139
- | **Required** |
140
- | `webhookUrl` | string | - | **Required**. Your backend API endpoint (works with any service: n8n, Dify, custom API, etc.) |
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
- ### Default Allowed File Types
161
+ ### Streaming (Agent-API mode)
173
162
 
174
- ```javascript
175
- [
176
- 'image/jpeg', 'image/png', 'image/gif', 'image/webp',
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
- ## 🔌 Backend API Format
169
+ | Option | Type | Default | Description |
170
+ |--------|------|---------|-------------|
171
+ | `showActionIndicators` | boolean | `true` | Show "Thinking..." label in typing indicator |
188
172
 
189
- ### Request (from Widget)
173
+ ### File Upload
190
174
 
191
- The widget sends a POST request with FormData containing:
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
- "sessionId": "web-session-1234567890-0.123",
196
- "message": "User message text",
197
- "file0": File, // Optional: uploaded files
198
- "file1": File, // Optional: multiple files supported
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
- ### Response (from Backend)
204
-
205
- Your backend should respond with JSON:
191
+ ### Features
206
192
 
207
- ```json
208
- {
209
- "response": "AI response text"
210
- }
211
- ```
212
-
213
- ### Example Backend Implementations
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
- <details>
216
- <summary><strong>n8n Workflow (Recommended)</strong></summary>
202
+ ### Technical
217
203
 
218
- We provide a ready-to-use n8n workflow with AI chat memory!
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
- **Quick Setup:**
212
+ ### Polling (human-agent handoff)
221
213
 
222
- 1. Import the workflow from [`pindai-chat-workflow.json`](./pindai-chat-workflow.json)
223
- 2. Add your OpenAI API key to the "OpenAI Chat Model" node
224
- 3. Activate the workflow
225
- 4. Copy the webhook URL and use it in your widget
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
- **What's included:**
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
- For detailed setup instructions, see [N8N_WORKFLOW_GUIDE.md](./N8N_WORKFLOW_GUIDE.md)
221
+ ## Agent-API Authentication
234
222
 
235
- **Simple Test Workflow:**
223
+ The widget uses **HMAC-SHA256** to sign each request, ensuring only authorized widgets can send messages to your agents.
236
224
 
237
- If you want to test without AI first, use [`pindai-chat-workflow-simple.json`](./pindai-chat-workflow-simple.json) - no API keys needed!
225
+ **How it works:**
238
226
 
239
- </details>
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
- <details>
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
- ```javascript
245
- const express = require('express');
246
- const app = express();
239
+ ---
247
240
 
248
- app.post('/webhook/chat', express.json(), async (req, res) => {
249
- const { sessionId, message } = req.body;
241
+ ## SSE Streaming
250
242
 
251
- // Your AI logic here
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
- res.json({ response: aiResponse });
255
- });
245
+ ```
246
+ GET /v1/chat/{agentId}/stream?message=...&sessionId=...&embedToken=...
256
247
  ```
257
248
 
258
- </details>
249
+ **SSE event format:**
250
+ ```
251
+ data: {"delta": "Hello"}
252
+ data: {"delta": " there!"}
253
+ data: {"type": "done", "status": "active"}
254
+ ```
259
255
 
260
- <details>
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
- ```python
264
- from flask import Flask, request, jsonify
258
+ ---
265
259
 
266
- app = Flask(__name__)
260
+ ## Human-Agent Handoff
267
261
 
268
- @app.route('/webhook/chat', methods=['POST'])
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
- # Your AI logic here
275
- ai_response = process_with_ai(message)
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
- return jsonify({'response': ai_response})
278
- ```
269
+ ---
279
270
 
280
- </details>
271
+ ## Customization Examples
281
272
 
282
- ---
273
+ ### Custom Branding + Dark Theme
283
274
 
284
- ## 🎨 Customization Examples
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
- ### Custom Branding
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
- locale: 'id',
292
- title: 'Bantuan Pelanggan',
293
- logoUrl: 'https://yourcompany.com/logo.png',
294
- launcherColor: '#FF5733',
295
- sendButtonColor: '#4CAF50',
296
- accentColor: '#FFC107',
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
- ### Custom Quick Replies
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
- quickReplies: [
306
- 'Cara membuat akun?',
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
- ### Fullscreen Mode (for dedicated chat pages)
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
- mode: 'fullscreen', // Takes over entire page
320
- title: 'Customer Support',
333
+ enableFileUpload: false,
334
+ showQuickReplies: false,
335
+ showBranding: false,
336
+ showLogo: false,
321
337
  });
322
338
  ```
323
339
 
324
- ### Disable Features
340
+ ### Custom Quick Replies
325
341
 
326
342
  ```javascript
327
343
  window.PindaiChatWidget.init({
328
344
  webhookUrl: 'https://your-backend.com/webhook/chat',
329
- enableFileUpload: false, // Disable file uploads
330
- enableHistory: false, // Don't save chat history
331
- showQuickReplies: false, // Hide quick reply buttons
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
- ## Accessibility
356
+ ## Backend API Reference
338
357
 
339
- This widget meets **WCAG 2.2 Level AA** compliance:
358
+ ### Agent-API endpoints (auto-used in agent-api mode)
340
359
 
341
- Full keyboard navigation (Tab, Enter, ESC)
342
- ✅ ARIA labels and landmarks
343
- Screen reader compatible
344
- 4.5:1 color contrast ratios
345
- Focus indicators
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
- ### Keyboard Shortcuts
366
+ **POST /v1/chat/{agentId}/message** — FormData fields:
350
367
 
351
- | Key | Action |
352
- |-----|--------|
353
- | `Tab` | Navigate between elements |
354
- | `Enter` | Send message / Activate button |
355
- | `ESC` | Close widget |
356
- | `Space` | Activate launcher |
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
- ## 🌐 Browser Support
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
- ## 🔧 Development
429
+ ## Development
373
430
 
374
- ### Project Structure
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 # Core widget logic
380
- │ ├── style.css # All styles
381
- │ └── i18n.js # Internationalization
382
- ├── dist/ # Built assets
383
- ├── images/ # Demo assets
384
- ├── index.html # Demo page
385
- ├── vite.config.js # Build configuration
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
- ### Build Commands
453
+ ---
391
454
 
392
- ```bash
393
- # Development server with hot reload
394
- npm run dev
455
+ ## Troubleshooting
395
456
 
396
- # Production build
397
- npm run build
457
+ ### Logo shows broken image
398
458
 
399
- # Preview production build
400
- npm run preview
459
+ Set `showLogo: false` (recommended) or provide a valid `logoUrl`:
460
+ ```javascript
461
+ window.PindaiChatWidget.init({ webhookUrl: '...', showLogo: false });
401
462
  ```
402
463
 
403
- ### Testing
464
+ ### Widget not appearing
404
465
 
405
- 1. **Start dev server:**
406
- ```bash
407
- npm run dev
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
- 2. **Open browser:** `http://localhost:5173`
471
+ ### CORS errors
411
472
 
412
- 3. **Test features:**
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
- 4. **Accessibility audit:**
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
- ## 📝 Changelog
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
- **Technical:**
456
- - Renamed from `N8nChatWidget` to `PindaiChatWidget`
457
- - Added i18n system
458
- - CSS variables for theming
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
- **Breaking Changes:**
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
- ### Version 1.0.0
491
+ ---
469
492
 
470
- - Initial release
471
- - Basic chat functionality
472
- - n8n integration
493
+ ## Changelog
473
494
 
474
- ---
495
+ ### Version 3.0.0
475
496
 
476
- ## 🤝 Contributing
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
- Contributions are welcome! Please:
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
- 1. Fork the repository
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
- ## 📄 License
525
+ ### Version 2.0.3
489
526
 
490
- MIT © [Pindai.ai](https://pindai.ai)
527
+ - Documentation: `showLogo` best practices, troubleshooting guide
491
528
 
492
- ---
529
+ ### Version 2.0.2
493
530
 
494
- ## 🔗 Links
531
+ - Published watermark to npm/CDN
495
532
 
496
- - **npm Package:** [npmjs.com/package/@pindai-ai/chat-widget](https://www.npmjs.com/package/@pindai-ai/chat-widget)
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
- ## 🆘 Support
538
+ ### Version 2.0.0
505
539
 
506
- - **Documentation:** [N8N_WORKFLOW_GUIDE.md](./N8N_WORKFLOW_GUIDE.md)
507
- - **Issues:** [GitHub Issues](https://github.com/pindai-ai/pindai-chat-widget/issues)
508
- - **Email:** support@pindai.ai
509
- - **Website:** [pindai.ai](https://pindai.ai)
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
- ## 🙏 Acknowledgments
551
+ ## License
514
552
 
515
- - Inspired by HubSpot chat widget design patterns
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
- **Made with ❤️ by [Pindai.ai](https://pindai.ai) for Indonesian businesses**
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)