@pindai-ai/chat-widget 2.0.4 → 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,48 +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).
11
16
 
12
17
  ```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>
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.)
35
+
36
+ ```html
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
43
  webhookUrl: 'https://your-backend.com/webhook/chat',
21
- showLogo: false // Recommended: hide logo if you don't have a custom one
44
+ showLogo: false,
22
45
  });
23
46
  });
24
47
  </script>
25
48
  ```
26
49
 
27
- ## ✨ Features
28
-
29
- - 🎨 **Modern UI** - HubSpot-quality design with smooth animations and Pindai.ai branding
30
- - ♿ **Accessible** - WCAG 2.2 AA compliant with full keyboard navigation and screen reader support
31
- - 📱 **Mobile-First** - Optimized responsive design for all devices
32
- - 🌐 **Bilingual** - Indonesian (default) and English localization
33
- - 📎 **File Upload** - Support for PDFs, images, and documents
34
- - 💾 **Persistent** - Message history and state saved to localStorage
35
- - 🔔 **Notifications** - Unread message badges and optional sound alerts
36
- - **Quick Replies** - Suggested responses for common questions
37
- - 🔄 **Retry Logic** - Automatic retry with exponential backoff on network errors
38
- - 📡 **Offline Support** - Graceful offline detection and user messaging
39
- - ⌨️ **Keyboard Nav** - Full keyboard support (Tab, Enter, ESC)
40
- - 🎯 **Lightweight** - Only ~12KB gzipped (JS + CSS)
41
- - 🔧 **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
42
67
 
43
68
  ---
44
69
 
45
- ## 📦 Installation
70
+ ## Installation
46
71
 
47
72
  ### Via npm
48
73
 
@@ -52,574 +77,487 @@ npm install @pindai-ai/chat-widget
52
77
 
53
78
  ### Via CDN (jsDelivr)
54
79
 
55
- **Latest 2.x version (recommended):**
56
80
  ```html
57
- <link rel="stylesheet"
58
- href="https://cdn.jsdelivr.net/npm/@pindai-ai/chat-widget@2/dist/pindai-chat-widget.css">
59
- <script type="module"
60
- src="https://cdn.jsdelivr.net/npm/@pindai-ai/chat-widget@2/dist/pindai-chat-widget.js"></script>
61
- ```
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>
62
84
 
63
- **Specific version (2.0.1):**
64
- ```html
65
- <link rel="stylesheet"
66
- href="https://cdn.jsdelivr.net/npm/@pindai-ai/chat-widget@2.0.1/dist/pindai-chat-widget.css">
67
- <script type="module"
68
- 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>
69
88
  ```
70
89
 
71
- > 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
+ ---
72
91
 
73
- ### Local Development
92
+ ## Configuration Options
74
93
 
75
- ```bash
76
- # Clone repository
77
- git clone https://github.com/PindaiAI/pindai-chat-widget.git
78
- cd pindai-chat-widget
94
+ ### Required (one of two modes)
79
95
 
80
- # Install dependencies
81
- 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 |
82
102
 
83
- # Run development server
84
- npm run dev
103
+ ### Display
85
104
 
86
- # Build for production
87
- npm run build
88
- ```
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 |
89
111
 
90
- ---
112
+ ### Branding
91
113
 
92
- ## 📖 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 |
93
123
 
94
- ### Complete HTML Example
124
+ ### Theme & Layout
95
125
 
96
- ```html
97
- <!DOCTYPE html>
98
- <html lang="id">
99
- <head>
100
- <meta charset="UTF-8">
101
- <title>Your Website</title>
102
- </head>
103
- <body>
104
- <h1>Welcome to My Website</h1>
105
- <p>Your content here...</p>
106
-
107
- <!-- Pindai Chat Widget -->
108
- <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@pindai-ai/chat-widget@2/dist/pindai-chat-widget.css">
109
- <script type="module" src="https://cdn.jsdelivr.net/npm/@pindai-ai/chat-widget@2/dist/pindai-chat-widget.js"></script>
110
- <script>
111
- document.addEventListener('DOMContentLoaded', function () {
112
- window.PindaiChatWidget.init({
113
- webhookUrl: 'https://your-backend.com/webhook/chat',
114
- mode: 'widget',
115
- locale: 'id', // 'id' for Indonesian, 'en' for English
116
- title: 'Customer Support',
117
- showLogo: false // Hide logo by default (recommended)
118
- });
119
- });
120
- </script>
121
- </body>
122
- </html>
123
- ```
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'` |
124
130
 
125
- ### Using During Development
131
+ ### Footer / Branding Strip
126
132
 
127
- 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 |
128
137
 
129
- ```html
130
- <link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/YOUR-USERNAME/pindai-chat-widget@main/dist/pindai-chat-widget.css">
131
- <script type="module" src="https://cdn.jsdelivr.net/gh/YOUR-USERNAME/pindai-chat-widget@main/dist/pindai-chat-widget.js"></script>
132
- ```
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
133
143
 
134
- Replace `YOUR-USERNAME` with your GitHub username
144
+ ### Bubble
135
145
 
136
- ---
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 |
137
153
 
138
- ## ⚙️ Configuration Options
154
+ ### Visitor Info (Agent-API mode)
139
155
 
140
156
  | Option | Type | Default | Description |
141
157
  |--------|------|---------|-------------|
142
- | **Required** |
143
- | `webhookUrl` | string | - | **Required**. Your backend API endpoint (works with any service: n8n, Dify, custom API, etc.) |
144
- | **Display** |
145
- | `mode` | string | `'widget'` | Display mode: `'widget'` or `'fullscreen'` |
146
- | `locale` | string | `'id'` | Language: `'id'` (Indonesian) or `'en'` (English) |
147
- | `title` | string | Localized | Chat header title |
148
- | `initialMessage` | string | Localized | First AI message |
149
- | **Branding** |
150
- | `logoUrl` | string | `'https://pindai.ai/logo.png'` | Header logo URL (use data URI for custom logo) |
151
- | `showLogo` | boolean | `true` | Show/hide logo. **Recommended: `false`** if you don't have a custom logo |
152
- | `launcherColor` | string | `'#0066FF'` | Launcher button background color |
153
- | `launcherIconUrl` | string | Default icon | Custom launcher icon URL |
154
- | `sendButtonColor` | string | `'#0066FF'` | Send button background color |
155
- | `accentColor` | string | `'#00C896'` | Accent color for UI elements |
156
- | **File Upload** |
157
- | `enableFileUpload` | boolean | `true` | Enable file attachments |
158
- | `allowedFileTypes` | array | See below | Accepted MIME types |
159
- | `maxFileSize` | number | `10485760` | Max file size in bytes (10MB default) |
160
- | `maxFiles` | number | `5` | Max files per message |
161
- | **Features** |
162
- | `enableNotifications` | boolean | `true` | Show unread badges |
163
- | `enableSound` | boolean | `false` | Play notification sound |
164
- | `enableHistory` | boolean | `true` | Persist message history |
165
- | `maxHistoryItems` | number | `50` | Max messages to store |
166
- | `showQuickReplies` | boolean | `true` | Show quick reply buttons |
167
- | `quickReplies` | array | Localized | Custom suggested responses |
168
- | **Technical** |
169
- | `maxRetries` | number | `3` | API retry attempts |
170
- | `retryDelay` | number | `1000` | Retry delay in ms |
171
- | `requestTimeout` | number | `30000` | Request timeout in ms (30s) |
172
- | `rateLimit` | number | `5` | Messages per minute |
173
- | `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 |
174
160
 
175
- ### Default Allowed File Types
161
+ ### Streaming (Agent-API mode)
176
162
 
177
- ```javascript
178
- [
179
- 'image/jpeg', 'image/png', 'image/gif', 'image/webp',
180
- 'application/pdf',
181
- 'application/msword',
182
- 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
183
- 'application/vnd.ms-excel',
184
- 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
185
- ]
186
- ```
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 |
187
166
 
188
- ---
167
+ ### Action Indicators
189
168
 
190
- ## 🔌 Backend API Format
169
+ | Option | Type | Default | Description |
170
+ |--------|------|---------|-------------|
171
+ | `showActionIndicators` | boolean | `true` | Show "Thinking..." label in typing indicator |
191
172
 
192
- ### Request (from Widget)
173
+ ### File Upload
193
174
 
194
- 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 |
195
181
 
182
+ **Default allowed file types:**
196
183
  ```javascript
197
- {
198
- "sessionId": "web-session-1234567890-0.123",
199
- "message": "User message text",
200
- "file0": File, // Optional: uploaded files
201
- "file1": File, // Optional: multiple files supported
202
- // ...
203
- }
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']
204
189
  ```
205
190
 
206
- ### Response (from Backend)
207
-
208
- Your backend should respond with JSON:
209
-
210
- ```json
211
- {
212
- "response": "AI response text"
213
- }
214
- ```
191
+ ### Features
215
192
 
216
- ### 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 |
217
201
 
218
- <details>
219
- <summary><strong>n8n Workflow (Recommended)</strong></summary>
202
+ ### Technical
220
203
 
221
- 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 |
222
211
 
223
- **Quick Setup:**
212
+ ### Polling (human-agent handoff)
224
213
 
225
- 1. Import the workflow from [`pindai-chat-workflow.json`](./pindai-chat-workflow.json)
226
- 2. Add your OpenAI API key to the "OpenAI Chat Model" node
227
- 3. Activate the workflow
228
- 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 |
229
218
 
230
- **What's included:**
231
- - AI Agent with Indonesian system prompt
232
- - Chat memory (remembers last 10 messages per session)
233
- - Automatic response formatting
234
- - Ready for production use
219
+ ---
235
220
 
236
- For detailed setup instructions, see [N8N_WORKFLOW_GUIDE.md](./N8N_WORKFLOW_GUIDE.md)
221
+ ## Agent-API Authentication
237
222
 
238
- **Simple Test Workflow:**
223
+ The widget uses **HMAC-SHA256** to sign each request, ensuring only authorized widgets can send messages to your agents.
239
224
 
240
- 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:**
241
226
 
242
- </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
243
236
 
244
- <details>
245
- <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`.
246
238
 
247
- ```javascript
248
- const express = require('express');
249
- const app = express();
239
+ ---
250
240
 
251
- app.post('/webhook/chat', express.json(), async (req, res) => {
252
- const { sessionId, message } = req.body;
241
+ ## SSE Streaming
253
242
 
254
- // Your AI logic here
255
- 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.
256
244
 
257
- res.json({ response: aiResponse });
258
- });
245
+ ```
246
+ GET /v1/chat/{agentId}/stream?message=...&sessionId=...&embedToken=...
259
247
  ```
260
248
 
261
- </details>
262
-
263
- <details>
264
- <summary><strong>Python Flask</strong></summary>
265
-
266
- ```python
267
- from flask import Flask, request, jsonify
249
+ **SSE event format:**
250
+ ```
251
+ data: {"delta": "Hello"}
252
+ data: {"delta": " there!"}
253
+ data: {"type": "done", "status": "active"}
254
+ ```
268
255
 
269
- app = Flask(__name__)
256
+ **Note:** When files are attached, streaming is automatically disabled and a regular POST is used instead (SSE does not support request bodies).
270
257
 
271
- @app.route('/webhook/chat', methods=['POST'])
272
- def chat():
273
- data = request.get_json()
274
- session_id = data.get('sessionId')
275
- message = data.get('message')
258
+ ---
276
259
 
277
- # Your AI logic here
278
- ai_response = process_with_ai(message)
260
+ ## Human-Agent Handoff
279
261
 
280
- return jsonify({'response': ai_response})
281
- ```
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:
282
263
 
283
- </details>
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`
284
268
 
285
269
  ---
286
270
 
287
- ## 🎨 Customization Examples
271
+ ## Customization Examples
288
272
 
289
- ### Custom Branding
273
+ ### Custom Branding + Dark Theme
290
274
 
291
- **Option 1: No Logo (Recommended)**
292
275
  ```javascript
293
276
  window.PindaiChatWidget.init({
294
- webhookUrl: 'https://your-backend.com/webhook/chat',
295
- locale: 'id',
296
- title: 'Bantuan Pelanggan',
297
- showLogo: false, // No logo, cleaner header
298
- launcherColor: '#FF5733',
299
- sendButtonColor: '#4CAF50',
300
- accentColor: '#FFC107',
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',
301
287
  });
302
288
  ```
303
289
 
304
- **Option 2: Custom Logo URL**
290
+ ### Bubble + Left Alignment
291
+
305
292
  ```javascript
293
+ // Single static message
306
294
  window.PindaiChatWidget.init({
307
295
  webhookUrl: 'https://your-backend.com/webhook/chat',
308
- locale: 'id',
309
- title: 'Bantuan Pelanggan',
310
- logoUrl: 'https://yourcompany.com/logo.png',
311
- showLogo: true,
312
- launcherColor: '#FF5733',
313
- sendButtonColor: '#4CAF50',
314
- 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,
315
301
  });
316
- ```
317
302
 
318
- **Option 3: Data URI Logo (No External Request)**
319
- ```javascript
303
+ // Rotating messages great for product tours or proactive outreach
320
304
  window.PindaiChatWidget.init({
321
305
  webhookUrl: 'https://your-backend.com/webhook/chat',
322
- logoUrl: 'data:image/svg+xml;charset=UTF-8,%3csvg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32"%3e%3ccircle cx="16" cy="16" r="14" fill="%230066FF"/%3e%3ctext x="16" y="21" font-size="16" font-weight="bold" text-anchor="middle" fill="white"%3eP%3c/text%3e%3c/svg%3e',
323
- showLogo: true,
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,
324
315
  });
325
316
  ```
326
317
 
327
- ### Custom Quick Replies
318
+ ### Auto Dark/Light Theme
328
319
 
329
320
  ```javascript
330
321
  window.PindaiChatWidget.init({
331
322
  webhookUrl: 'https://your-backend.com/webhook/chat',
332
- quickReplies: [
333
- 'Cara membuat akun?',
334
- 'Lupa password',
335
- 'Hubungi customer service',
336
- 'Lihat demo produk'
337
- ],
323
+ theme: 'auto', // Follows system prefers-color-scheme
324
+ showLogo: false,
338
325
  });
339
326
  ```
340
327
 
341
- ### Fullscreen Mode (for dedicated chat pages)
328
+ ### Minimal (no uploads, no quick replies, no branding)
342
329
 
343
330
  ```javascript
344
331
  window.PindaiChatWidget.init({
345
332
  webhookUrl: 'https://your-backend.com/webhook/chat',
346
- mode: 'fullscreen', // Takes over entire page
347
- title: 'Customer Support',
333
+ enableFileUpload: false,
334
+ showQuickReplies: false,
335
+ showBranding: false,
336
+ showLogo: false,
348
337
  });
349
338
  ```
350
339
 
351
- ### Disable Features
340
+ ### Custom Quick Replies
352
341
 
353
342
  ```javascript
354
343
  window.PindaiChatWidget.init({
355
344
  webhookUrl: 'https://your-backend.com/webhook/chat',
356
- enableFileUpload: false, // Disable file uploads
357
- enableHistory: false, // Don't save chat history
358
- 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
+ ],
359
351
  });
360
352
  ```
361
353
 
362
354
  ---
363
355
 
364
- ## Accessibility
365
-
366
- This widget meets **WCAG 2.2 Level AA** compliance:
367
-
368
- ✅ Full keyboard navigation (Tab, Enter, ESC)
369
- ✅ ARIA labels and landmarks
370
- ✅ Screen reader compatible
371
- ✅ 4.5:1 color contrast ratios
372
- ✅ Focus indicators
373
- ✅ Text resizable to 200%
374
- ✅ Touch targets ≥ 44×44px
375
-
376
- ### Keyboard Shortcuts
377
-
378
- | Key | Action |
379
- |-----|--------|
380
- | `Tab` | Navigate between elements |
381
- | `Enter` | Send message / Activate button |
382
- | `ESC` | Close widget |
383
- | `Space` | Activate launcher |
384
-
385
- ---
356
+ ## Backend API Reference
386
357
 
387
- ## 🔧 Troubleshooting
358
+ ### Agent-API endpoints (auto-used in agent-api mode)
388
359
 
389
- ### Logo Not Loading / 404 Error
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 |
390
365
 
391
- **Problem:** You see a broken image or 404 error for the logo.
366
+ **POST /v1/chat/{agentId}/message** FormData fields:
392
367
 
393
- **Solution:** Set `showLogo: false` in your configuration:
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 |
394
376
 
395
- ```javascript
396
- window.PindaiChatWidget.init({
397
- webhookUrl: 'https://your-backend.com/webhook/chat',
398
- showLogo: false // This hides the logo completely
399
- });
377
+ **Response:**
378
+ ```json
379
+ {
380
+ "response": "AI response text",
381
+ "conversation_id": "uuid",
382
+ "status": "active"
383
+ }
400
384
  ```
401
385
 
402
- Or provide your own logo URL:
386
+ ### Generic webhook (legacy mode)
403
387
 
404
- ```javascript
405
- window.PindaiChatWidget.init({
406
- webhookUrl: 'https://your-backend.com/webhook/chat',
407
- logoUrl: 'https://your-domain.com/your-logo.png',
408
- showLogo: true
409
- });
388
+ **Request** (FormData POST):
389
+ ```
390
+ sessionId=web-session-...&message=Hello&file0=<File>
410
391
  ```
411
392
 
412
- ### Widget Not Appearing
413
-
414
- **Problem:** Widget doesn't show up on the page.
393
+ **Response** (JSON):
394
+ ```json
395
+ { "response": "AI response text" }
396
+ ```
415
397
 
416
- **Solutions:**
417
- 1. Check browser console for errors (F12)
418
- 2. Verify the `webhookUrl` is correct
419
- 3. Make sure the script loads after the DOM is ready
420
- 4. Check if there are CSP (Content Security Policy) restrictions
421
-
422
- ### Webhook Returns Error
423
-
424
- **Problem:** Messages fail to send or you get errors.
425
-
426
- **Solutions:**
427
- 1. Verify your n8n/backend workflow is active
428
- 2. Check the webhook URL is correct and accessible
429
- 3. Test the webhook with curl:
430
- ```bash
431
- curl -X POST https://your-webhook-url \
432
- -H "Content-Type: application/json" \
433
- -d '{"sessionId":"test","message":"Hello"}'
434
- ```
435
- 4. Check CORS settings on your backend
398
+ ---
436
399
 
437
- ### CDN Not Updating
400
+ ## Accessibility
438
401
 
439
- **Problem:** Changes not reflecting after npm publish.
402
+ WCAG 2.2 Level AA compliant:
440
403
 
441
- **Solutions:**
442
- 1. Wait 5-10 minutes for CDN cache to clear
443
- 2. Use version-specific URL: `@pindai-ai/chat-widget@2.0.3`
444
- 3. Add cache buster: `...widget.js?v=2`
445
- 4. Force CDN purge: Visit `https://purge.jsdelivr.net/npm/@pindai-ai/chat-widget@2/dist/pindai-chat-widget.js`
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
446
412
 
447
413
  ---
448
414
 
449
- ## 🌐 Browser Support
415
+ ## Browser Support
450
416
 
451
- | Browser | Version |
452
- |---------|---------|
417
+ | Browser | Minimum Version |
418
+ |---------|----------------|
453
419
  | Chrome/Edge | 90+ |
454
420
  | Firefox | 88+ |
455
421
  | Safari | 14+ |
456
422
  | iOS Safari | 14+ |
457
423
  | Android Chrome | 90+ |
458
424
 
425
+ **Required APIs:** `fetch`, `EventSource`, `crypto.subtle`, `FormData`, `localStorage`
426
+
459
427
  ---
460
428
 
461
- ## 🔧 Development
429
+ ## Development
462
430
 
463
- ### 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
+ ```
464
439
 
440
+ **Project structure:**
465
441
  ```
466
- pindai-chat/
442
+ pindai-chat-widget/
467
443
  ├── src/
468
- │ ├── main.js # Core widget logic
469
- │ ├── style.css # All styles
470
- │ └── i18n.js # Internationalization
471
- ├── dist/ # Built assets
472
- ├── images/ # Demo assets
473
- ├── index.html # Demo page
474
- ├── vite.config.js # Build configuration
475
- ├── package.json # Package metadata
476
- └── 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
477
451
  ```
478
452
 
479
- ### Build Commands
453
+ ---
480
454
 
481
- ```bash
482
- # Development server with hot reload
483
- npm run dev
455
+ ## Troubleshooting
484
456
 
485
- # Production build
486
- npm run build
457
+ ### Logo shows broken image
487
458
 
488
- # Preview production build
489
- npm run preview
459
+ Set `showLogo: false` (recommended) or provide a valid `logoUrl`:
460
+ ```javascript
461
+ window.PindaiChatWidget.init({ webhookUrl: '...', showLogo: false });
490
462
  ```
491
463
 
492
- ### Testing
493
-
494
- 1. **Start dev server:**
495
- ```bash
496
- npm run dev
497
- ```
498
-
499
- 2. **Open browser:** `http://localhost:5173`
464
+ ### Widget not appearing
500
465
 
501
- 3. **Test features:**
502
- - Click launcher to open chat
503
- - Send messages
504
- - Upload files (PDF, images)
505
- - Test quick replies
506
- - Try keyboard navigation (Tab, ESC)
507
- - Test offline mode (DevTools > Network > Offline)
508
- - Check mobile responsiveness (DevTools > Device Toolbar)
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
509
470
 
510
- 4. **Accessibility audit:**
511
- - Open DevTools > Lighthouse
512
- - Run Accessibility audit
513
- - Should score 100
471
+ ### CORS errors
514
472
 
515
- ---
473
+ Add the widget origin to your API's CORS `allow_origins`. In development, allow `http://localhost:5173`.
516
474
 
517
- ## 📝 Changelog
475
+ ### Streaming not working
518
476
 
519
- ### Version 2.0.3 (2026-02-06)
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
520
481
 
521
- **Documentation:**
522
- - Added comprehensive troubleshooting section
523
- - Clarified `showLogo: false` recommendation in Quick Start
524
- - Added multiple logo customization options (no logo, URL, data URI)
525
- - Improved configuration examples with best practices
482
+ ### CDN cache stale after publish
526
483
 
527
- ### Version 2.0.2 (2026-02-06)
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
+ ```
528
488
 
529
- **Updates:**
530
- - Published with watermark feature to npm
531
- - CDN distribution updated
489
+ Or use a version-pinned URL: `@pindai-ai/chat-widget@3.0.0`
532
490
 
533
- ### Version 2.0.1 (2026-02-06)
491
+ ---
534
492
 
535
- **Updates:**
536
- - Vendor-agnostic API: Changed `n8nUrl` to `webhookUrl` parameter
537
- - Added backward compatibility for `n8nUrl`
538
- - Added "Powered by Pindai.ai" watermark in widget footer
539
- - Updated documentation with n8n workflow examples
540
- - Included ready-to-use n8n workflow JSON files
493
+ ## Changelog
541
494
 
542
- ### Version 2.0.0 (2026-02-05)
495
+ ### Version 3.0.0
543
496
 
544
- **Major Changes:**
545
- - Complete UI/UX redesign with Pindai.ai branding
546
- - Indonesian localization (default) with English support
547
- - File upload capability (PDF, images, documents)
548
- - WCAG 2.2 AA accessibility compliance
549
- - Mobile-first responsive design
550
- - Message history persistence
551
- - Quick reply buttons
552
- - Notification badges
553
- - Enhanced error handling with retry logic
554
- - Offline detection and user messaging
555
- - Rate limiting
556
- - Keyboard navigation (Tab, ESC)
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
557
515
 
558
516
  **Technical:**
559
- - Renamed from `N8nChatWidget` to `PindaiChatWidget`
560
- - Added i18n system
561
- - CSS variables for theming
562
- - FormData for file uploads
563
- - localStorage for history/state
564
- - Backward compatibility maintained
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)
565
520
 
566
- **Breaking Changes:**
567
- - Default locale changed to Indonesian (`'id'`)
568
- - File uploads now use FormData instead of JSON
569
- - Some CSS class names updated
521
+ ### Version 2.0.4
570
522
 
571
- ### Version 1.0.0
572
-
573
- - Initial release
574
- - Basic chat functionality
575
- - n8n integration
523
+ - Bug fixes and stability improvements
576
524
 
577
- ---
525
+ ### Version 2.0.3
578
526
 
579
- ## 🤝 Contributing
527
+ - Documentation: `showLogo` best practices, troubleshooting guide
580
528
 
581
- Contributions are welcome! Please:
529
+ ### Version 2.0.2
582
530
 
583
- 1. Fork the repository
584
- 2. Create a feature branch (`git checkout -b feature/amazing-feature`)
585
- 3. Commit your changes (`git commit -m 'Add amazing feature'`)
586
- 4. Push to the branch (`git push origin feature/amazing-feature`)
587
- 5. Open a Pull Request
531
+ - Published watermark to npm/CDN
588
532
 
589
- ---
533
+ ### Version 2.0.1
590
534
 
591
- ## 📄 License
535
+ - Renamed `n8nUrl` → `webhookUrl` (backward compatible)
536
+ - Added "Powered by Pindai.ai" watermark
592
537
 
593
- MIT © [Pindai.ai](https://pindai.ai)
538
+ ### Version 2.0.0
594
539
 
595
- ---
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
596
544
 
597
- ## 🔗 Links
545
+ ### Version 1.0.0
598
546
 
599
- - **npm Package:** [npmjs.com/package/@pindai-ai/chat-widget](https://www.npmjs.com/package/@pindai-ai/chat-widget)
600
- - **jsDelivr CDN:** [jsdelivr.com/package/npm/@pindai-ai/chat-widget](https://www.jsdelivr.com/package/npm/@pindai-ai/chat-widget)
601
- - **GitHub Repository:** [github.com/pindai-ai/pindai-chat-widget](https://github.com/PindaiAI/pindai-chat-widget)
602
- - **Issues & Bugs:** [github.com/pindai-ai/pindai-chat-widget/issues](https://github.com/PindaiAI/pindai-chat-widget/issues)
603
- - **Pindai.ai Website:** [pindai.ai](https://pindai.ai)
547
+ - Initial release
604
548
 
605
549
  ---
606
550
 
607
- ## 🆘 Support
551
+ ## License
608
552
 
609
- - **Documentation:** [N8N_WORKFLOW_GUIDE.md](./N8N_WORKFLOW_GUIDE.md)
610
- - **Issues:** [GitHub Issues](https://github.com/PindaiAI/pindai-chat-widget/issues)
611
- - **Email:** support@pindai.ai
612
- - **Website:** [pindai.ai](https://pindai.ai)
553
+ MIT © [Pindai.ai](https://pindai.ai)
613
554
 
614
555
  ---
615
556
 
616
- ## 🙏 Acknowledgments
617
-
618
- - Inspired by HubSpot chat widget design patterns
619
- - Built with modern web standards and accessibility best practices
620
- - Designed for Indonesian enterprises
621
- - Powered by Pindai.ai's AI document extraction technology
622
-
623
- ---
557
+ ## Links
624
558
 
625
- **Made with ❤️ by [Pindai.ai](https://pindai.ai) for Indonesian businesses**
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)