@makemore/agent-frontend 1.7.1 → 2.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 +262 -7
- package/dist/chat-widget.css +611 -1
- package/dist/chat-widget.js +305 -1202
- package/package.json +19 -7
- package/src/components/ChatWidget.js +259 -0
- package/src/components/Header.js +111 -0
- package/src/components/InputForm.js +95 -0
- package/src/components/Message.js +115 -0
- package/src/components/MessageList.js +106 -0
- package/src/components/ModelSelector.js +68 -0
- package/src/components/Sidebar.js +58 -0
- package/src/hooks/useChat.js +455 -0
- package/src/hooks/useModels.js +69 -0
- package/src/index.js +222 -0
- package/src/utils/api.js +90 -0
- package/src/utils/config.js +83 -0
- package/src/utils/helpers.js +83 -0
package/README.md
CHANGED
|
@@ -1,11 +1,10 @@
|
|
|
1
1
|
# Agent Frontend
|
|
2
2
|
|
|
3
|
-
A
|
|
3
|
+
A lightweight chat widget for AI agents built with Preact. Embed conversational AI into any website with a single script tag.
|
|
4
4
|
|
|
5
5
|
<p align="center">
|
|
6
|
-
<img src="https://img.shields.io/badge/
|
|
7
|
-
<img src="https://img.shields.io/badge/size-~
|
|
8
|
-
<img src="https://img.shields.io/badge/dependencies-0-blue" alt="Zero Dependencies">
|
|
6
|
+
<img src="https://img.shields.io/badge/Preact-673AB8?logo=preact&logoColor=white" alt="Preact">
|
|
7
|
+
<img src="https://img.shields.io/badge/size-~25kb-green" alt="Size">
|
|
9
8
|
<img src="https://img.shields.io/badge/license-MIT-purple" alt="MIT License">
|
|
10
9
|
</p>
|
|
11
10
|
|
|
@@ -13,10 +12,11 @@ A standalone, zero-dependency chat widget for AI agents. Embed conversational AI
|
|
|
13
12
|
|
|
14
13
|
Most chat widgets are tightly coupled to specific frameworks or require complex build setups. Agent Frontend is different:
|
|
15
14
|
|
|
16
|
-
- **
|
|
15
|
+
- **Lightweight** - Built with Preact (~3kb) for minimal bundle size
|
|
17
16
|
- **CSS isolated** - Won't conflict with your existing styles (uses `all: initial` reset)
|
|
18
17
|
- **SSE streaming** - Real-time token-by-token responses, not polling
|
|
19
18
|
- **Production ready** - Session management, error handling, conversation persistence
|
|
19
|
+
- **Component-based** - Clean architecture with hooks for state management
|
|
20
20
|
|
|
21
21
|
## Features
|
|
22
22
|
|
|
@@ -174,7 +174,126 @@ See `django-tts-example.py` for the complete Django backend implementation.
|
|
|
174
174
|
| `showTTSButton` | boolean | `true` | Show TTS toggle button in header |
|
|
175
175
|
| `showVoiceSettings` | boolean | `true` | Show voice settings button in header (works with proxy and direct API) |
|
|
176
176
|
| `showExpandButton` | boolean | `true` | Show expand/minimize button in header |
|
|
177
|
+
| `showConversationSidebar` | boolean | `true` | Show conversation history sidebar with hamburger menu |
|
|
177
178
|
| `onEvent` | function | `null` | Callback for SSE events: `(eventType, payload) => void` |
|
|
179
|
+
| `authStrategy` | string | `null` | Auth strategy: `'token'`, `'jwt'`, `'session'`, `'anonymous'`, `'none'` (auto-detected if null) |
|
|
180
|
+
| `authToken` | string | `null` | Token value for `'token'` or `'jwt'` strategies |
|
|
181
|
+
| `authHeader` | string | `null` | Custom header name (defaults based on strategy) |
|
|
182
|
+
| `authTokenPrefix` | string | `null` | Custom token prefix (defaults based on strategy) |
|
|
183
|
+
| `anonymousSessionEndpoint` | string | `null` | Endpoint for anonymous session (defaults to `apiPaths.anonymousSession`) |
|
|
184
|
+
| `anonymousTokenKey` | string | `'chat_widget_anonymous_token'` | localStorage key for anonymous token |
|
|
185
|
+
| `onAuthError` | function | `null` | Callback for auth errors: `(error) => void` |
|
|
186
|
+
|
|
187
|
+
### Authentication
|
|
188
|
+
|
|
189
|
+
The widget supports multiple authentication strategies with sensible defaults:
|
|
190
|
+
|
|
191
|
+
#### Token Authentication (Django REST Framework)
|
|
192
|
+
|
|
193
|
+
```javascript
|
|
194
|
+
ChatWidget.init({
|
|
195
|
+
backendUrl: 'https://api.example.com',
|
|
196
|
+
agentKey: 'my-agent',
|
|
197
|
+
authStrategy: 'token',
|
|
198
|
+
authToken: 'abc123...',
|
|
199
|
+
// Sends: Authorization: Token abc123...
|
|
200
|
+
});
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
#### JWT/Bearer Authentication
|
|
204
|
+
|
|
205
|
+
```javascript
|
|
206
|
+
ChatWidget.init({
|
|
207
|
+
backendUrl: 'https://api.example.com',
|
|
208
|
+
agentKey: 'my-agent',
|
|
209
|
+
authStrategy: 'jwt',
|
|
210
|
+
authToken: 'eyJ...',
|
|
211
|
+
// Sends: Authorization: Bearer eyJ...
|
|
212
|
+
});
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
#### Session-Based Authentication (Cookies)
|
|
216
|
+
|
|
217
|
+
```javascript
|
|
218
|
+
ChatWidget.init({
|
|
219
|
+
backendUrl: 'https://api.example.com',
|
|
220
|
+
agentKey: 'my-agent',
|
|
221
|
+
authStrategy: 'session',
|
|
222
|
+
// Sends requests with credentials: 'include'
|
|
223
|
+
// No auth header, relies on session cookie
|
|
224
|
+
});
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
#### Anonymous Session Tokens
|
|
228
|
+
|
|
229
|
+
```javascript
|
|
230
|
+
ChatWidget.init({
|
|
231
|
+
backendUrl: 'https://api.example.com',
|
|
232
|
+
agentKey: 'my-agent',
|
|
233
|
+
authStrategy: 'anonymous',
|
|
234
|
+
anonymousSessionEndpoint: '/api/accounts/anonymous-session/',
|
|
235
|
+
// On first request: fetches anonymous token from endpoint
|
|
236
|
+
// Persists token to localStorage
|
|
237
|
+
// Sends: X-Anonymous-Token: {token}
|
|
238
|
+
});
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
#### No Authentication (Public Endpoints)
|
|
242
|
+
|
|
243
|
+
```javascript
|
|
244
|
+
ChatWidget.init({
|
|
245
|
+
backendUrl: 'https://api.example.com',
|
|
246
|
+
agentKey: 'my-agent',
|
|
247
|
+
authStrategy: 'none',
|
|
248
|
+
// No auth headers sent
|
|
249
|
+
});
|
|
250
|
+
```
|
|
251
|
+
|
|
252
|
+
#### Custom Headers and Prefixes
|
|
253
|
+
|
|
254
|
+
```javascript
|
|
255
|
+
ChatWidget.init({
|
|
256
|
+
authStrategy: 'token',
|
|
257
|
+
authToken: 'mytoken123',
|
|
258
|
+
authHeader: 'X-API-Key', // Custom header name
|
|
259
|
+
authTokenPrefix: '', // No prefix (just the token)
|
|
260
|
+
// Sends: X-API-Key: mytoken123
|
|
261
|
+
});
|
|
262
|
+
```
|
|
263
|
+
|
|
264
|
+
#### Dynamic Token Updates
|
|
265
|
+
|
|
266
|
+
Update authentication after initialization (e.g., after user login):
|
|
267
|
+
|
|
268
|
+
```javascript
|
|
269
|
+
// After user logs in
|
|
270
|
+
ChatWidget.setAuth({
|
|
271
|
+
strategy: 'jwt',
|
|
272
|
+
token: 'new-jwt-token-after-login'
|
|
273
|
+
});
|
|
274
|
+
|
|
275
|
+
// After user logs out
|
|
276
|
+
ChatWidget.clearAuth();
|
|
277
|
+
|
|
278
|
+
// Handle token refresh on auth errors
|
|
279
|
+
ChatWidget.init({
|
|
280
|
+
authStrategy: 'jwt',
|
|
281
|
+
authToken: initialToken,
|
|
282
|
+
onAuthError: async (error) => {
|
|
283
|
+
if (error.status === 401) {
|
|
284
|
+
const newToken = await refreshToken();
|
|
285
|
+
ChatWidget.setAuth({ token: newToken });
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
});
|
|
289
|
+
```
|
|
290
|
+
|
|
291
|
+
#### Auto-Detection
|
|
292
|
+
|
|
293
|
+
If no `authStrategy` is specified, the widget auto-detects based on config:
|
|
294
|
+
- If `authToken` is provided → uses `'token'` strategy
|
|
295
|
+
- If `anonymousSessionEndpoint` or `apiPaths.anonymousSession` is configured → uses `'anonymous'` strategy
|
|
296
|
+
- Otherwise → uses `'none'`
|
|
178
297
|
|
|
179
298
|
### Event Callback
|
|
180
299
|
|
|
@@ -456,6 +575,12 @@ ChatWidget.send('Hello, I need help!');
|
|
|
456
575
|
// Clear the conversation
|
|
457
576
|
ChatWidget.clearMessages();
|
|
458
577
|
|
|
578
|
+
// Conversation sidebar controls
|
|
579
|
+
ChatWidget.toggleSidebar(); // Open/close conversation sidebar
|
|
580
|
+
ChatWidget.newConversation(); // Start a new conversation
|
|
581
|
+
ChatWidget.switchConversation('conversation-id'); // Switch to a specific conversation
|
|
582
|
+
ChatWidget.loadMoreMessages(); // Load older messages
|
|
583
|
+
|
|
459
584
|
// Text-to-speech controls
|
|
460
585
|
ChatWidget.toggleTTS(); // Toggle TTS on/off
|
|
461
586
|
ChatWidget.stopSpeech(); // Stop current speech and clear queue
|
|
@@ -477,6 +602,10 @@ ChatWidget.setAutoRunMode('automatic'); // 'automatic', 'confirm', or 'manual'
|
|
|
477
602
|
// Change auto-run delay (in milliseconds)
|
|
478
603
|
ChatWidget.setAutoRunDelay(2000);
|
|
479
604
|
|
|
605
|
+
// Authentication methods
|
|
606
|
+
ChatWidget.setAuth({ strategy: 'jwt', token: 'new-token' }); // Update auth
|
|
607
|
+
ChatWidget.clearAuth(); // Clear authentication
|
|
608
|
+
|
|
480
609
|
// Remove the widget from the page
|
|
481
610
|
ChatWidget.destroy();
|
|
482
611
|
|
|
@@ -606,9 +735,135 @@ agent-frontend/
|
|
|
606
735
|
|
|
607
736
|
Requires: `EventSource` (SSE), `fetch`, `localStorage`
|
|
608
737
|
|
|
738
|
+
## Multiple Instances
|
|
739
|
+
|
|
740
|
+
You can create multiple independent chat widgets on the same page using `createInstance()`:
|
|
741
|
+
|
|
742
|
+
### Basic Multi-Instance Setup
|
|
743
|
+
|
|
744
|
+
```html
|
|
745
|
+
<div id="chat-1" style="width: 400px; height: 500px;"></div>
|
|
746
|
+
<div id="chat-2" style="width: 400px; height: 500px;"></div>
|
|
747
|
+
|
|
748
|
+
<script>
|
|
749
|
+
// Create first widget
|
|
750
|
+
const widget1 = ChatWidget.createInstance({
|
|
751
|
+
containerId: 'chat-1',
|
|
752
|
+
backendUrl: 'https://your-api.com',
|
|
753
|
+
agentKey: 'support-agent',
|
|
754
|
+
title: 'Support Chat',
|
|
755
|
+
primaryColor: '#0066cc',
|
|
756
|
+
embedded: true,
|
|
757
|
+
});
|
|
758
|
+
|
|
759
|
+
// Create second widget
|
|
760
|
+
const widget2 = ChatWidget.createInstance({
|
|
761
|
+
containerId: 'chat-2',
|
|
762
|
+
backendUrl: 'https://your-api.com',
|
|
763
|
+
agentKey: 'sales-agent',
|
|
764
|
+
title: 'Sales Chat',
|
|
765
|
+
primaryColor: '#00cc66',
|
|
766
|
+
embedded: true,
|
|
767
|
+
});
|
|
768
|
+
</script>
|
|
769
|
+
```
|
|
770
|
+
|
|
771
|
+
### Instance Configuration Options
|
|
772
|
+
|
|
773
|
+
| Option | Type | Default | Description |
|
|
774
|
+
|--------|------|---------|-------------|
|
|
775
|
+
| `containerId` | string | `null` | ID of the container element for embedded mode |
|
|
776
|
+
| `embedded` | boolean | `false` | If true, renders inline in container instead of floating |
|
|
777
|
+
| `metadata` | object | `{}` | Custom metadata to send with each request |
|
|
778
|
+
|
|
779
|
+
### Instance Methods
|
|
780
|
+
|
|
781
|
+
Each instance returned by `createInstance()` has its own methods:
|
|
782
|
+
|
|
783
|
+
```javascript
|
|
784
|
+
const widget = ChatWidget.createInstance({ ... });
|
|
785
|
+
|
|
786
|
+
// Control the widget
|
|
787
|
+
widget.open();
|
|
788
|
+
widget.close();
|
|
789
|
+
widget.send('Hello!');
|
|
790
|
+
widget.clearMessages();
|
|
791
|
+
|
|
792
|
+
// TTS controls
|
|
793
|
+
widget.toggleTTS();
|
|
794
|
+
widget.stopSpeech();
|
|
795
|
+
|
|
796
|
+
// Authentication
|
|
797
|
+
widget.setAuth({ strategy: 'jwt', token: 'new-token' });
|
|
798
|
+
widget.clearAuth();
|
|
799
|
+
|
|
800
|
+
// Get state/config
|
|
801
|
+
const state = widget.getState();
|
|
802
|
+
const config = widget.getConfig();
|
|
803
|
+
|
|
804
|
+
// Destroy the widget
|
|
805
|
+
widget.destroy();
|
|
806
|
+
```
|
|
807
|
+
|
|
808
|
+
### Managing Multiple Instances
|
|
809
|
+
|
|
810
|
+
```javascript
|
|
811
|
+
// Get a specific instance by ID
|
|
812
|
+
const widget = ChatWidget.getInstance('cw-1');
|
|
813
|
+
|
|
814
|
+
// Get all instances
|
|
815
|
+
const allWidgets = ChatWidget.getAllInstances();
|
|
816
|
+
|
|
817
|
+
// Destroy all instances
|
|
818
|
+
ChatWidget.getAllInstances().forEach(w => w.destroy());
|
|
819
|
+
```
|
|
820
|
+
|
|
821
|
+
### Embedded vs Floating Mode
|
|
822
|
+
|
|
823
|
+
**Embedded Mode** (`embedded: true`):
|
|
824
|
+
- Widget renders inside the specified container
|
|
825
|
+
- No floating button or close button
|
|
826
|
+
- Widget is always visible
|
|
827
|
+
- Perfect for split-pane layouts, dashboards, or dedicated chat pages
|
|
828
|
+
|
|
829
|
+
**Floating Mode** (`embedded: false`, default):
|
|
830
|
+
- Widget appears as a floating button in the corner
|
|
831
|
+
- Clicking opens the chat panel
|
|
832
|
+
- Has close and expand buttons
|
|
833
|
+
- Traditional chat widget behavior
|
|
834
|
+
|
|
835
|
+
### Storage Isolation
|
|
836
|
+
|
|
837
|
+
Each embedded instance uses isolated localStorage keys based on `containerId`:
|
|
838
|
+
- `chat_widget_conversation_id_chat-1` for widget in `#chat-1`
|
|
839
|
+
- `chat_widget_conversation_id_chat-2` for widget in `#chat-2`
|
|
840
|
+
|
|
841
|
+
This ensures conversations don't get mixed up between instances.
|
|
842
|
+
|
|
609
843
|
## Version History
|
|
610
844
|
|
|
611
|
-
###
|
|
845
|
+
### v2.0.0 (Latest)
|
|
846
|
+
- 🔄 **Preact Rewrite**: Complete rewrite using Preact for better maintainability
|
|
847
|
+
- 🧩 **Component Architecture**: Modular components (ChatWidget, Header, InputForm, Message, MessageList, Sidebar)
|
|
848
|
+
- 🪝 **React-style Hooks**: useChat and useModels hooks for state management
|
|
849
|
+
- 🎛️ **Model Selector**: Built-in model selection dropdown
|
|
850
|
+
- 📦 **Smaller Bundle**: Optimized build with esbuild
|
|
851
|
+
- 🔧 **Better Developer Experience**: Watch mode, source maps, cleaner code structure
|
|
852
|
+
|
|
853
|
+
### v1.10.1
|
|
854
|
+
- 📚 **Conversation Sidebar**: Browse and switch between past conversations via hamburger menu
|
|
855
|
+
- 📜 **Message Pagination**: Load older messages with "load more" functionality
|
|
856
|
+
- 🔐 **CSRF Support**: Automatic CSRF token handling for Django session auth
|
|
857
|
+
- ➕ **New Conversation**: Start fresh conversations from the sidebar
|
|
858
|
+
- 🔄 **Auto-restore**: Automatically loads messages when returning to a conversation
|
|
859
|
+
|
|
860
|
+
### v1.5.0
|
|
861
|
+
- 🔀 **Multiple Instances**: Create multiple independent chat widgets on the same page
|
|
862
|
+
- 📦 **Embedded Mode**: Render widgets inline in containers for dashboards and split-pane layouts
|
|
863
|
+
- 🔒 **Storage Isolation**: Each instance has isolated localStorage for conversations
|
|
864
|
+
- 🎯 **Instance API**: Full control over individual widget instances
|
|
865
|
+
|
|
866
|
+
### v1.4.0
|
|
612
867
|
- ✨ **Text-to-Speech**: ElevenLabs integration with secure Django proxy support
|
|
613
868
|
- 🔊 Automatic speech for assistant and simulated user messages
|
|
614
869
|
- 🎛️ Smart speech queuing to prevent overlap
|
|
@@ -638,4 +893,4 @@ Requires: `EventSource` (SSE), `fetch`, `localStorage`
|
|
|
638
893
|
|
|
639
894
|
## License
|
|
640
895
|
|
|
641
|
-
MIT ©
|
|
896
|
+
MIT © 2025
|