@baseportal/chat-widget 0.1.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 ADDED
@@ -0,0 +1,333 @@
1
+ # @baseportal/chat-widget
2
+
3
+ Embeddable chat widget for Baseportal. Lightweight (~60KB gzipped) with Preact, Ably realtime, and multi-language support.
4
+
5
+ ## Installation
6
+
7
+ ### Script tag (IIFE)
8
+
9
+ ```html
10
+ <script src="https://your-domain.com/baseportal-chat.iife.js"></script>
11
+ <script>
12
+ const chat = new BaseportalChat({
13
+ channelToken: 'your-channel-token',
14
+ })
15
+ </script>
16
+ ```
17
+
18
+ ### NPM / PNPM
19
+
20
+ ```bash
21
+ pnpm add @baseportal/chat-widget
22
+ ```
23
+
24
+ ```typescript
25
+ import { BaseportalChat } from '@baseportal/chat-widget'
26
+
27
+ const chat = new BaseportalChat({
28
+ channelToken: 'your-channel-token',
29
+ })
30
+ ```
31
+
32
+ ## Configuration
33
+
34
+ ```typescript
35
+ const chat = new BaseportalChat({
36
+ // Required
37
+ channelToken: 'your-channel-token',
38
+
39
+ // Optional
40
+ apiUrl: 'https://api.baseportal.io', // Custom API URL
41
+ position: 'bottom-right', // 'bottom-right' | 'bottom-left'
42
+ locale: 'pt', // 'pt' | 'en' | 'es'
43
+ hideOnLoad: false, // Start hidden (no bubble)
44
+ theme: {
45
+ primaryColor: '#6366f1', // Custom primary color
46
+ },
47
+ container: document.getElementById('chat'), // Mount inside a specific element
48
+ visitor: { // Pre-identify the visitor
49
+ name: 'John Doe',
50
+ email: 'john@example.com',
51
+ hash: 'hmac-sha256-hash', // Required if identity verification is enabled
52
+ metadata: { plan: 'pro' },
53
+ },
54
+ })
55
+ ```
56
+
57
+ ### Configuration Options
58
+
59
+ | Option | Type | Default | Description |
60
+ |--------|------|---------|-------------|
61
+ | `channelToken` | `string` | — | **(Required)** Channel token from Baseportal dashboard |
62
+ | `apiUrl` | `string` | `https://api.baseportal.io` | API base URL |
63
+ | `position` | `'bottom-right' \| 'bottom-left'` | `'bottom-right'` | Widget position on the page |
64
+ | `locale` | `'pt' \| 'en' \| 'es'` | `'pt'` | UI language |
65
+ | `hideOnLoad` | `boolean` | `false` | If `true`, widget starts hidden (no bubble) |
66
+ | `theme` | `{ primaryColor?: string }` | — | Custom theme overrides |
67
+ | `container` | `HTMLElement` | — | Mount inside a specific DOM element instead of `document.body` |
68
+ | `visitor` | `VisitorData` | — | Pre-identify the visitor (see Identity Verification) |
69
+
70
+ ## API Methods
71
+
72
+ ### Visibility
73
+
74
+ ```typescript
75
+ chat.open() // Open the chat window
76
+ chat.close() // Close the chat window
77
+ chat.toggle() // Toggle open/close
78
+ chat.show() // Show the widget (bubble + window)
79
+ chat.hide() // Hide the widget entirely
80
+ chat.isOpen() // Returns true if chat window is open
81
+ ```
82
+
83
+ ### Visitor Identity
84
+
85
+ ```typescript
86
+ // Identify a visitor (enables conversation history)
87
+ chat.identify({
88
+ email: 'john@example.com',
89
+ name: 'John Doe',
90
+ hash: 'hmac-sha256-hash', // Required if identity verification is enabled
91
+ metadata: { plan: 'pro' },
92
+ })
93
+
94
+ // Update visitor data
95
+ chat.updateVisitor({
96
+ name: 'Jane Doe',
97
+ metadata: { plan: 'enterprise' },
98
+ })
99
+
100
+ // Clear visitor data and reset
101
+ chat.clearVisitor()
102
+ ```
103
+
104
+ ### Actions
105
+
106
+ ```typescript
107
+ // Send a message programmatically
108
+ chat.sendMessage('Hello!')
109
+
110
+ // Open a specific conversation
111
+ chat.setConversationId('conversation-uuid')
112
+
113
+ // Start a new conversation
114
+ chat.newConversation()
115
+ ```
116
+
117
+ ### Configuration (Runtime)
118
+
119
+ ```typescript
120
+ chat.setTheme({ primaryColor: '#10b981' })
121
+ chat.setPosition('bottom-left')
122
+ chat.setLocale('en')
123
+ ```
124
+
125
+ ### Lifecycle
126
+
127
+ ```typescript
128
+ chat.destroy() // Unmount widget and clean up all listeners
129
+ ```
130
+
131
+ ## Events
132
+
133
+ ```typescript
134
+ chat.on('ready', () => {
135
+ console.log('Widget initialized')
136
+ })
137
+
138
+ chat.on('open', () => {
139
+ console.log('Chat window opened')
140
+ })
141
+
142
+ chat.on('close', () => {
143
+ console.log('Chat window closed')
144
+ })
145
+
146
+ chat.on('message:sent', (message) => {
147
+ console.log('Message sent:', message)
148
+ })
149
+
150
+ chat.on('message:received', (message) => {
151
+ console.log('Message received:', message)
152
+ })
153
+
154
+ chat.on('conversation:started', (conversation) => {
155
+ console.log('New conversation:', conversation)
156
+ })
157
+
158
+ chat.on('conversation:closed', (conversation) => {
159
+ console.log('Conversation closed:', conversation)
160
+ })
161
+
162
+ chat.on('identified', (visitor) => {
163
+ console.log('Visitor identified:', visitor)
164
+ })
165
+
166
+ // Remove listener
167
+ const handler = (msg) => console.log(msg)
168
+ chat.on('message:received', handler)
169
+ chat.off('message:received', handler)
170
+ ```
171
+
172
+ ### Available Events
173
+
174
+ | Event | Payload | Description |
175
+ |-------|---------|-------------|
176
+ | `ready` | — | Widget initialized and mounted |
177
+ | `open` | — | Chat window opened |
178
+ | `close` | — | Chat window closed |
179
+ | `show` | — | Widget made visible |
180
+ | `hide` | — | Widget hidden |
181
+ | `message:sent` | `Message` | Visitor sent a message |
182
+ | `message:received` | `Message` | Agent/bot message received |
183
+ | `conversation:started` | `Conversation` | New conversation created |
184
+ | `conversation:closed` | `Conversation` | Conversation closed by agent |
185
+ | `identified` | `VisitorData` | Visitor identified via `identify()` |
186
+
187
+ ## Identity Verification
188
+
189
+ For authenticated users, identity verification ensures visitors can only access their own conversation history. It uses HMAC-SHA256 with a secret key configured in the Baseportal dashboard.
190
+
191
+ ### Server-side hash generation
192
+
193
+ ```javascript
194
+ // Node.js
195
+ const crypto = require('crypto')
196
+ const hash = crypto
197
+ .createHmac('sha256', 'your-identity-verification-secret')
198
+ .update(userEmail)
199
+ .digest('hex')
200
+ ```
201
+
202
+ ```python
203
+ # Python
204
+ import hmac, hashlib
205
+ hash = hmac.new(
206
+ b'your-identity-verification-secret',
207
+ user_email.encode(),
208
+ hashlib.sha256
209
+ ).hexdigest()
210
+ ```
211
+
212
+ ```php
213
+ // PHP
214
+ $hash = hash_hmac('sha256', $userEmail, 'your-identity-verification-secret');
215
+ ```
216
+
217
+ ```ruby
218
+ # Ruby
219
+ hash = OpenSSL::HMAC.hexdigest('SHA256', 'your-identity-verification-secret', user_email)
220
+ ```
221
+
222
+ ### Client-side usage
223
+
224
+ ```typescript
225
+ const chat = new BaseportalChat({
226
+ channelToken: 'your-channel-token',
227
+ visitor: {
228
+ email: 'john@example.com',
229
+ name: 'John Doe',
230
+ hash: 'server-generated-hmac-hash',
231
+ },
232
+ })
233
+ ```
234
+
235
+ When identity verification is enabled on the channel:
236
+ - `visitor.email` and `visitor.hash` are required
237
+ - The visitor can view their conversation history
238
+ - Conversations are linked to the visitor's email across sessions
239
+
240
+ ## File Uploads
241
+
242
+ The widget supports file uploads directly in the chat. Visitors can attach files by clicking the paperclip icon in the composer.
243
+
244
+ **Supported file types:** Images, videos (MP4), audio, PDF, Word, Excel, and text files.
245
+
246
+ **Limits:**
247
+ - Max file size: 25 MB
248
+ - Max files per conversation: 20
249
+ - Rate limit: 10 uploads per minute
250
+
251
+ **Media rendering:**
252
+ - **Images** — displayed inline with click-to-expand lightbox
253
+ - **Videos** — native HTML5 video player
254
+ - **Other files** — download card with file name and icon
255
+
256
+ ## Container Mode
257
+
258
+ Mount the widget inside a specific element instead of showing the floating bubble:
259
+
260
+ ```html
261
+ <div id="chat-container" style="width: 400px; height: 600px;"></div>
262
+
263
+ <script>
264
+ const chat = new BaseportalChat({
265
+ channelToken: 'your-channel-token',
266
+ container: document.getElementById('chat-container'),
267
+ })
268
+ </script>
269
+ ```
270
+
271
+ In container mode:
272
+ - No floating bubble is rendered
273
+ - The chat window fills the container element
274
+ - `show()` / `hide()` / `open()` / `close()` still work
275
+
276
+ ## Channel Configuration
277
+
278
+ Features are configured per channel in the Baseportal dashboard:
279
+
280
+ | Feature | Description |
281
+ |---------|-------------|
282
+ | `requireName` | Require visitor name before starting a conversation |
283
+ | `requireEmail` | Require visitor email before starting a conversation |
284
+ | `allowViewHistory` | Allow authenticated visitors to see past conversations |
285
+ | `allowReopenConversation` | Allow visitors to reopen closed conversations |
286
+ | `privacyPolicyUrl` | Display a link to your privacy policy |
287
+ | Identity Verification | Enable HMAC verification for visitor identity |
288
+
289
+ ## Development
290
+
291
+ ```bash
292
+ cd packages/chat-widget
293
+
294
+ # Install dependencies
295
+ pnpm install
296
+
297
+ # Build
298
+ pnpm build
299
+
300
+ # Watch mode
301
+ pnpm dev
302
+ ```
303
+
304
+ ### Build outputs
305
+
306
+ | File | Format | Usage |
307
+ |------|--------|-------|
308
+ | `dist/index.iife.js` | IIFE | Script tag (global `BaseportalChat`) |
309
+ | `dist/index.esm.js` | ESM | `import` in bundlers |
310
+ | `dist/index.cjs.js` | CJS | `require()` in Node.js |
311
+ | `dist/index.d.ts` | TypeScript | Type definitions |
312
+
313
+ ### Deploy to client
314
+
315
+ After building, copy the IIFE bundle to the client's public directory:
316
+
317
+ ```bash
318
+ cp dist/index.iife.js ../../baseportal-client/public/baseportal-chat.iife.js
319
+ ```
320
+
321
+ ## TypeScript
322
+
323
+ All types are exported:
324
+
325
+ ```typescript
326
+ import type {
327
+ BaseportalChatConfig,
328
+ ChannelInfo,
329
+ Conversation,
330
+ Message,
331
+ VisitorData,
332
+ } from '@baseportal/chat-widget'
333
+ ```