@myned-ai/avatar-chat-widget 0.0.3 → 0.2.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
@@ -6,7 +6,7 @@
6
6
 
7
7
  ---
8
8
 
9
- ## Installation
9
+ ## Quick Start
10
10
 
11
11
  ### Script Tag (No Build Required)
12
12
 
@@ -20,7 +20,6 @@ Add directly to any HTML page or website builder:
20
20
  <script src="https://cdn.jsdelivr.net/npm/@myned-ai/avatar-chat-widget"></script>
21
21
 
22
22
  <script>
23
- // Initialize the widget
24
23
  AvatarChat.init({
25
24
  container: '#avatar-chat',
26
25
  serverUrl: 'wss://your-backend-server.com/ws',
@@ -30,9 +29,16 @@ Add directly to any HTML page or website builder:
30
29
  </script>
31
30
  ```
32
31
 
33
- ### Programmatic Control
32
+ ### NPM Package
33
+
34
+ ```bash
35
+ npm install @myned-ai/avatar-chat-widget
36
+ ```
34
37
 
35
38
  ```typescript
39
+ import { AvatarChat } from '@myned-ai/avatar-chat-widget';
40
+ import '@myned-ai/avatar-chat-widget/style.css';
41
+
36
42
  const chat = AvatarChat.init({
37
43
  container: '#avatar-chat',
38
44
  serverUrl: 'wss://your-backend-server.com/ws',
@@ -50,62 +56,84 @@ chat.destroy(); // Cleanup
50
56
 
51
57
  ---
52
58
 
53
- ## Configuration Options
59
+ ## Configuration
54
60
 
55
61
  | Option | Type | Default | Description |
56
62
  |--------|------|---------|-------------|
57
63
  | `container` | `string \| HTMLElement` | **required** | CSS selector or DOM element |
58
- | `serverUrl` | `string` | **required** | WebSocket server URL |
59
- | `position` | `string` | `'bottom-right'` | Position: `bottom-right`, `bottom-left`, `top-right`, `top-left`, `inline` |
60
- | `theme` | `string` | `'light'` | Theme: `light`, `dark`, `auto` |
64
+ | `serverUrl` | `string` | **required** | WebSocket server URL (ws:// or wss://) |
65
+ | `position` | `string` | `'bottom-right'` | `bottom-right`, `bottom-left`, `top-right`, `top-left`, `inline` |
66
+ | `theme` | `string` | `'light'` | `light`, `dark`, `auto` (follows system preference) |
61
67
  | `startCollapsed` | `boolean` | `true` | Start minimized as bubble |
62
- | `width` | `number` | `380` | Widget width in pixels |
63
- | `height` | `number` | `550` | Widget height in pixels |
64
- | `logLevel` | `string` | `'error'` | Log level: `none`, `error`, `warn`, `info`, `debug` |
65
- | `customStyles` | `string` | `undefined` | Custom CSS to inject |
66
- | `authEnabled` | `boolean` | `true` | Enable HMAC token authentication |
68
+ | `width` | `number` | `380` | Widget width (200-2000px) |
69
+ | `height` | `number` | `550` | Widget height (300-2000px) |
70
+ | `enableVoice` | `boolean` | `true` | Enable voice chat |
71
+ | `enableText` | `boolean` | `true` | Enable text chat |
72
+ | `logLevel` | `string` | `'error'` | `none`, `error`, `warn`, `info`, `debug` |
73
+ | `customStyles` | `string` | `undefined` | Custom CSS to inject into Shadow DOM |
74
+ | `authEnabled` | `boolean` | `true` | Enable HMAC authentication (disable for dev) |
75
+ | `avatarUrl` | `string` | auto-detected | URL to avatar ZIP file |
67
76
 
68
77
  ### Callbacks
69
78
 
70
79
  | Callback | Type | Description |
71
80
  |----------|------|-------------|
72
- | `onReady` | `() => void` | Called when widget is initialized |
73
- | `onConnectionChange` | `(connected: boolean) => void` | Connection status changes |
74
- | `onMessage` | `(msg: {role, text}) => void` | Message received |
81
+ | `onReady` | `() => void` | Widget initialized and ready |
82
+ | `onConnectionChange` | `(connected: boolean) => void` | WebSocket connection status changed |
83
+ | `onMessage` | `(msg: {role, text}) => void` | Message received from server |
75
84
  | `onError` | `(error: Error) => void` | Error occurred |
76
85
 
77
86
  ---
78
87
 
79
- ## Development
88
+ ## Customization
80
89
 
81
- ### Local Development
90
+ ### Changing Colors
82
91
 
83
- ```bash
84
- # Install dependencies
85
- npm install
92
+ The widget uses CSS variables that you can override with the `customStyles` option:
86
93
 
87
- # Start development server (SPA mode)
88
- npm run dev
89
-
90
- # Build library for distribution
91
- npm run build:lib
92
-
93
- # Build both SPA and library
94
- npm run build:all
95
- ```
96
-
97
- ### Test with Sample Backend Server
98
-
99
- ```bash
100
- # Clone the avatar chat server
101
- git clone https://github.com/myned-ai/avatar-chat-server.git
102
- cd avatar-chat-server
103
-
104
- # Follow the instructions in the repository to start the Docker container
105
- docker-compose up
94
+ ```typescript
95
+ AvatarChat.init({
96
+ container: '#avatar-chat',
97
+ serverUrl: 'wss://your-server.com/ws',
98
+ customStyles: `
99
+ /* Primary brand colors (gradient, buttons, user messages) */
100
+ .chat-bubble,
101
+ .chat-header {
102
+ background: linear-gradient(135deg, #ff6b6b 0%, #ee5a6f 100%) !important;
103
+ }
104
+
105
+ .input-button {
106
+ background: #ff6b6b !important;
107
+ }
108
+
109
+ .input-button:hover:not(:disabled) {
110
+ background: #ee5a6f !important;
111
+ }
112
+
113
+ .message.user .message-bubble {
114
+ background: #ff6b6b !important;
115
+ }
116
+
117
+ /* Avatar border */
118
+ .avatar-circle {
119
+ border-color: #ff6b6b !important;
120
+ }
121
+
122
+ /* Input focus color */
123
+ #chatInput:focus {
124
+ border-color: #ff6b6b !important;
125
+ }
126
+ `
127
+ });
106
128
  ```
107
129
 
108
- The server will start on `ws://localhost:8765` by default.
130
+ **Common color targets:**
131
+ - `.chat-bubble` - Minimized bubble
132
+ - `.chat-header` - Header gradient
133
+ - `.avatar-circle` - Avatar border
134
+ - `.input-button` - Send & mic buttons
135
+ - `.message.user .message-bubble` - User message bubbles
136
+ - `#chatInput:focus` - Input field focus state
109
137
 
110
138
  ---
111
139
 
@@ -115,181 +143,133 @@ The server will start on `ws://localhost:8765` by default.
115
143
  - **3D Avatar** - Gaussian Splatting rendering with 52 ARKit blendshapes
116
144
  - **Synchronized Animation** - Audio and facial expressions in perfect sync
117
145
  - **Auto-reconnection** - Resilient WebSocket with exponential backoff
118
- - **Shadow DOM** - CSS isolated, no style conflicts
119
- - **Accessible** - WCAG 2.1 AA compliant
146
+ - **Shadow DOM** - Complete CSS isolation, no style conflicts
147
+ - **Framework Agnostic** - Works with React, Vue, Angular, or vanilla HTML
148
+ - **Accessible** - WCAG 2.1 AA compliant with ARIA labels
120
149
 
121
150
  ---
122
151
 
123
- ## Avatar Rendering Engine
124
-
125
- This widget uses [@myned-ai/gsplat-flame-avatar-renderer](https://www.npmjs.com/package/@myned-ai/gsplat-flame-avatar-renderer), a specialized Gaussian Splatting library for rendering animated 3D avatars.
152
+ ## WebSocket Protocol
126
153
 
127
- ### Animation States
154
+ ### Client → Server
128
155
 
129
- | State | Animation Index | Description |
130
- |-------|-----------------|-------------|
131
- | **Idle** | 1 | Subtle breathing and micro-movements |
132
- | **Hello** | 2 | Attentive greeting posture |
133
- | **Responding** | 6 | Speaking body movements (head sway, gestures) |
156
+ ```json
157
+ { "type": "text", "data": "Hello", "userId": "user_123", "timestamp": 1234567890 }
158
+ { "type": "audio", "data": "<ArrayBuffer>", "format": "audio/webm" }
159
+ ```
134
160
 
135
- ### Eye Blink Behavior
161
+ ### Server Client
136
162
 
137
- The widget handles all eye blinking on the frontend for consistent behavior across states. Blink intervals vary by avatar state:
163
+ ```json
164
+ { "type": "audio_start", "sessionId": "abc", "sampleRate": 24000 }
165
+ { "type": "audio_chunk", "data": "<ArrayBuffer>", "timestamp": 1234567890 }
166
+ { "type": "blendshape", "weights": {...}, "timestamp": 1234567890 }
167
+ { "type": "audio_end", "sessionId": "abc" }
168
+ { "type": "text", "data": "Hello", "timestamp": 1234567890 }
169
+ ```
138
170
 
139
- | State | Interval | Description |
140
- |-------|----------|-------------|
141
- | **Idle** | 2-4 seconds | Relaxed, natural blinking |
142
- | **Hello** | 1.8-3.5 seconds | Attentive, slightly more frequent |
143
- | **Responding** | 1.3-3.3 seconds | Natural speaking rate |
171
+ ---
144
172
 
145
- Each blink uses randomized patterns and intensity (80-100%) for natural variation.
173
+ ## Authentication
146
174
 
147
- ### Avatar Asset Format
175
+ ### Disabling Auth (Development)
148
176
 
149
- The widget loads a modified LAM based avatar from ZIP file containing:
177
+ For local testing without an auth server:
150
178
 
151
- ```
152
- avatar.zip
153
- ├── avatar/
154
- │ ├── offset.ply # Gaussian splats point cloud
155
- │ ├── animation.glb # Animation clips
156
- │ ├── skin.glb # Skinning/skeleton data
157
- │ ├── vertex_order.json # Vertex ordering
158
- │ └── iris_occlusion.json # Iris occlusion ranges (optional)
179
+ ```typescript
180
+ AvatarChat.init({
181
+ container: '#avatar-chat',
182
+ serverUrl: 'ws://localhost:8080/ws',
183
+ authEnabled: false // Disable authentication
184
+ });
159
185
  ```
160
186
 
161
- ### Browser Requirements
187
+ ### Production Setup
162
188
 
163
- For production deployment, your server must send these headers to enable SharedArrayBuffer (used for high-performance sorting):
189
+ The widget uses HMAC-SHA256 token authentication for secure connections:
164
190
 
165
- ```
166
- Cross-Origin-Embedder-Policy: require-corp
167
- Cross-Origin-Opener-Policy: same-origin
168
- ```
169
-
170
- ---
191
+ 1. Widget requests token from `POST /api/auth/token`
192
+ 2. Server validates origin and returns signed token
193
+ 3. Widget connects with token: `wss://server/ws?token=...`
194
+ 4. Server verifies signature and expiration
171
195
 
172
- ## Directory Structure
173
-
174
- | Path | Files | Description |
175
- |------|-------|-------------|
176
- | `src/` | 3 | Entry points (`widget.ts`, `main.ts`, `config.ts`) |
177
- | `src/avatar/` | 3 | Avatar rendering (`GaussianAvatar`, `LazyAvatar`) |
178
- | `src/constants/` | 2 | ARKit blendshape constants |
179
- | `src/managers/` | 2 | Chat orchestration (`ChatManager`) |
180
- | `src/services/` | 8 | Core services (audio, WebSocket, blendshapes) |
181
- | `src/types/` | 4 | TypeScript type definitions |
182
- | `src/utils/` | 9 | Shared utilities (logging, buffers, protocols) |
183
-
184
- ### Key Components
185
-
186
- | Component | Description |
187
- |-----------|-------------|
188
- | `widget.ts` | Main entry point, `AvatarChat.init()` API |
189
- | `ChatManager` | Orchestrates services, handles state transitions |
190
- | `GaussianAvatar` | Wrapper for gsplat-flame-avatar-renderer |
191
- | `SocketService` | WebSocket connection with auto-reconnect |
192
- | `AudioInput` | Microphone capture (PCM16 for OpenAI Realtime API) |
193
- | `AudioOutput` | Audio playback with Web Audio API |
194
- | `SyncPlayback` | Synchronized audio + blendshape playback |
195
- | `BlendshapeBuffer` | Frame buffer for smooth animation |
196
- | `AuthService` | HMAC token authentication |
197
- | `Logger` | Centralized logging with levels |
196
+ **Security features:**
197
+ - Origin validation (whitelist domains)
198
+ - Time-limited tokens with auto-refresh
199
+ - Rate limiting per domain/session
198
200
 
199
201
  ---
200
202
 
201
- ## Architecture
202
-
203
- ```
204
- Frontend (This Widget) Backend (Your Server)
205
- ┌─────────────────────┐ ┌──────────────────────┐
206
- │ Widget (Shadow) │◄────►│ WebSocket Server │
207
- │ ├─ Chat UI │ │ ├─ AI/LLM │
208
- │ ├─ Voice I/O │ │ ├─ TTS │
209
- │ └─ Avatar │ │ └─ Blendshape Gen │
210
- └─────────────────────┘ └──────────────────────┘
211
- ```
203
+ ## Development
212
204
 
213
- ---
205
+ ### Local Setup
214
206
 
215
- ## WebSocket Protocol
207
+ ```bash
208
+ # Install dependencies
209
+ npm install
216
210
 
217
- **Client to Server:**
218
- ```json
219
- { "type": "text", "data": "Hello", "userId": "user_123", "timestamp": 123 }
220
- { "type": "audio", "data": "<ArrayBuffer>", "format": "audio/webm" }
221
- ```
211
+ # Start dev server with hot reload
212
+ npm run dev
222
213
 
223
- **Server to Client:**
224
- ```json
225
- { "type": "audio_start", "sessionId": "abc", "sampleRate": 24000 }
226
- { "type": "audio_chunk", "data": "<ArrayBuffer>", "timestamp": 124 }
227
- { "type": "blendshape", "weights": {...}, "timestamp": 124 }
228
- { "type": "audio_end", "sessionId": "abc" }
214
+ # Build for production
215
+ npm run build:lib
229
216
  ```
230
217
 
231
- ---
218
+ ### Testing with Backend
232
219
 
233
- ## Authentication
220
+ ```bash
221
+ # Clone sample server
222
+ git clone https://github.com/myned-ai/avatar-chat-server.git
223
+ cd avatar-chat-server
234
224
 
235
- The widget uses HMAC token authentication by default for secure WebSocket connections.
225
+ # Start with Docker
226
+ docker-compose up
227
+ ```
236
228
 
237
- ### Disabling Authentication (Development)
229
+ Server runs on `ws://localhost:8765` by default.
238
230
 
239
- For local development without an auth server:
231
+ ---
240
232
 
241
- ```typescript
242
- AvatarChat.init({
243
- container: '#avatar-chat',
244
- serverUrl: 'ws://localhost:8080/ws',
245
- authEnabled: false // Disable for local testing
246
- });
247
- ```
233
+ ## Browser Requirements
248
234
 
249
- ### How It Works
235
+ **Supported browsers:**
236
+ - Chrome/Edge 90+
237
+ - Firefox 89+
238
+ - Safari 15.2+
250
239
 
251
- 1. Widget requests a token from `POST /api/auth/token`
252
- 2. Server validates origin and returns HMAC-signed token
253
- 3. Widget connects to WebSocket with token: `ws://server/ws?token=...`
254
- 4. Server verifies token signature and expiration
240
+ **Production deployment:**
255
241
 
256
- ### Authentication Flow
242
+ For optimal performance, your server must send these headers to enable SharedArrayBuffer:
257
243
 
258
244
  ```
259
- Widget Server
260
- │ │
261
- │ POST /api/auth/token │
262
- │ Origin: https://yoursite.com │
263
- │──────────────────────────────►│
264
- │ │ Validate origin
265
- │ {token, ttl, origin} │ Generate HMAC token
266
- │◄──────────────────────────────│
267
- │ │
268
- │ WebSocket /ws?token=... │
269
- │──────────────────────────────►│
270
- │ │ Verify token
271
- │ Connection OK │ Check rate limits
272
- │◄──────────────────────────────│
245
+ Cross-Origin-Embedder-Policy: require-corp
246
+ Cross-Origin-Opener-Policy: same-origin
273
247
  ```
274
248
 
275
- ### Security Features
249
+ ---
250
+
251
+ ## Troubleshooting
276
252
 
277
- - **Origin validation** - Only whitelisted domains can connect
278
- - **HMAC-SHA256 tokens** - Cryptographically signed, time-limited
279
- - **Rate limiting** - Per-domain and per-session limits
280
- - **Auto-refresh** - Tokens refresh automatically before expiry
253
+ ### Widget not loading
254
+ - Check browser console for errors
255
+ - Verify `serverUrl` is correct WebSocket URL (ws:// or wss://)
256
+ - Ensure container element exists before calling `init()`
281
257
 
282
- ### Server Requirements
258
+ ### Avatar not rendering
259
+ - Check server CORS headers (COEP/COOP)
260
+ - Verify avatar ZIP file is accessible
261
+ - Check `logLevel: 'debug'` for detailed logs
283
262
 
284
- The backend must implement:
285
- - `POST /api/auth/token` - Returns `{token, ttl, origin}`
286
- - WebSocket token verification via query parameter
263
+ ### Voice not working
264
+ - Microphone permission required
265
+ - HTTPS required for production (getUserMedia)
266
+ - Check browser compatibility
287
267
 
288
268
  ---
289
269
 
290
- ### Acknowledgement
270
+ ## Acknowledgements
291
271
 
292
- This work is built on many amazing research works and open-source projects:
272
+ Built on amazing open-source research:
293
273
  - [OpenLRM](https://github.com/3DTopia/OpenLRM)
294
274
  - [GAGAvatar](https://github.com/xg-chu/GAGAvatar)
295
275
  - [GaussianAvatars](https://github.com/ShenhanQian/GaussianAvatars)
Binary file
@@ -1,3 +1,6 @@
1
+ import { AvatarChatConfig } from './widget/types';
2
+ import { AvatarChatInstance } from './widget/types';
3
+
1
4
  /**
2
5
  * AvatarChat - Public Widget API
3
6
  */
@@ -6,6 +9,11 @@ declare const AvatarChat: {
6
9
  version: string;
7
10
  /** Active instance */
8
11
  _instance: AvatarChatElement | null;
12
+ /**
13
+ * Get the URL for the default included avatar
14
+ * Auto-detects CDN usage and returns the appropriate URL
15
+ */
16
+ getDefaultAvatarUrl(): string;
9
17
  /**
10
18
  * Initialize and mount the widget
11
19
  */
@@ -22,72 +30,7 @@ declare const AvatarChat: {
22
30
  export { AvatarChat }
23
31
  export default AvatarChat;
24
32
 
25
- /**
26
- * Avatar Chat Widget - Embeddable Web Component
27
- *
28
- * A real-time voice/text chat widget with 3D avatar animation.
29
- * Uses Shadow DOM for complete CSS isolation from host page.
30
- *
31
- * @example Script Tag (Wix, WordPress, HTML)
32
- * ```html
33
- * <div id="avatar-chat"></div>
34
- * <script src="https://cdn.jsdelivr.net/npm/@myned-ai/avatar-chat-widget"></script>
35
- * <script>
36
- * AvatarChat.init({
37
- * container: '#avatar-chat',
38
- * serverUrl: 'wss://your-server.com/ws'
39
- * });
40
- * </script>
41
- * ```
42
- *
43
- * @example NPM Package
44
- * ```typescript
45
- * import { AvatarChat } from 'avatar-chat-widget';
46
- * const widget = AvatarChat.init({ container: '#chat', serverUrl: 'wss://...' });
47
- * ```
48
- */
49
- /**
50
- * Widget configuration options passed at runtime
51
- */
52
- export declare interface AvatarChatConfig {
53
- /** CSS selector or HTMLElement for the widget container (required) */
54
- container: string | HTMLElement;
55
- /** WebSocket server URL (required) */
56
- serverUrl: string;
57
- /** Widget position when using floating mode */
58
- position?: 'bottom-right' | 'bottom-left' | 'top-right' | 'top-left' | 'inline';
59
- /** UI theme */
60
- theme?: 'light' | 'dark' | 'auto';
61
- /** Start in collapsed state (bubble only) */
62
- startCollapsed?: boolean;
63
- /** Widget width in pixels (default: 380) */
64
- width?: number;
65
- /** Widget height in pixels (default: 550) */
66
- height?: number;
67
- /** Enable/disable voice input (default: true) */
68
- enableVoice?: boolean;
69
- /** Enable/disable text input (default: true) */
70
- enableText?: boolean;
71
- /** Path to avatar model (default: './asset/nyx.zip') */
72
- avatarUrl?: string;
73
- /** Enable authentication (default: false) */
74
- authEnabled?: boolean;
75
- /** Log level for debugging */
76
- logLevel?: 'none' | 'error' | 'warn' | 'info' | 'debug';
77
- /** Custom CSS to inject (optional) */
78
- customStyles?: string;
79
- /** Callback when widget is ready */
80
- onReady?: () => void;
81
- /** Callback when connection state changes */
82
- onConnectionChange?: (connected: boolean) => void;
83
- /** Callback when a message is received */
84
- onMessage?: (message: {
85
- role: 'user' | 'assistant';
86
- text: string;
87
- }) => void;
88
- /** Callback on error */
89
- onError?: (error: Error) => void;
90
- }
33
+ export { AvatarChatConfig }
91
34
 
92
35
  declare class AvatarChatElement extends HTMLElement {
93
36
  private shadow;
@@ -97,6 +40,8 @@ declare class AvatarChatElement extends HTMLElement {
97
40
  private _isMounted;
98
41
  private _isConnected;
99
42
  private _isCollapsed;
43
+ private themeMediaQuery;
44
+ private themeChangeHandler;
100
45
  constructor();
101
46
  /**
102
47
  * Configure the widget (call before mount)
@@ -127,7 +72,7 @@ declare class AvatarChatElement extends HTMLElement {
127
72
  */
128
73
  private setupUIEvents;
129
74
  /**
130
- * Update connection status UI
75
+ * Update connection status (stub - connection indicator removed from UI)
131
76
  */
132
77
  private updateConnectionStatus;
133
78
  /**
@@ -158,34 +103,17 @@ declare class AvatarChatElement extends HTMLElement {
158
103
  * Check if connected to server
159
104
  */
160
105
  isServerConnected(): boolean;
106
+ /**
107
+ * Manually reconnect to the server
108
+ * Useful after network changes or connection failures
109
+ */
110
+ reconnect(): Promise<void>;
161
111
  /**
162
112
  * Cleanup
163
113
  */
164
114
  destroy(): void;
165
115
  }
166
116
 
167
- /**
168
- * Widget instance returned by init()
169
- */
170
- export declare interface AvatarChatInstance {
171
- /** Send a text message */
172
- sendMessage(text: string): void;
173
- /** Mount widget to DOM (called automatically by init) */
174
- mount(): void;
175
- /** Destroy and cleanup widget */
176
- destroy(): void;
177
- /** Show the widget */
178
- show(): void;
179
- /** Hide the widget */
180
- hide(): void;
181
- /** Expand from collapsed state */
182
- expand(): void;
183
- /** Collapse to bubble */
184
- collapse(): void;
185
- /** Check if widget is mounted */
186
- isMounted(): boolean;
187
- /** Check if connected to server */
188
- isConnected(): boolean;
189
- }
117
+ export { AvatarChatInstance }
190
118
 
191
119
  export { }