@spatialwalk/avatarkit 1.0.0-beta.62 → 1.0.0-beta.64

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/CHANGELOG.md CHANGED
@@ -2,6 +2,46 @@
2
2
 
3
3
  All notable changes to this project will be documented in this file.
4
4
 
5
+ ## [1.0.0-beta.64] - 2026-01-14
6
+
7
+ ### ✨ New Features
8
+ - **Vite Plugin for WASM Configuration** - Added `avatarkitVitePlugin` to automate WASM file configuration
9
+ - Automatically handles WASM MIME types in dev server
10
+ - Copies WASM files to dist/assets/ during build
11
+ - Generates `_headers` file for Cloudflare Pages deployment
12
+ - Configures Vite's optimizeDeps, assetsInclude, and assetsInlineLimit
13
+
14
+ ### 🔧 Improvements
15
+ - **Unified Transition Logic** - All transitions now start from the current playing frame, ensuring smooth transitions from any state (Idle, Speaking, or Transitioning)
16
+ - **Idle State Performance** - Optimized idle state to avoid unnecessary frame parameter fetching on every frame
17
+ - **Transition Animation** - Idle->Speaking uses linear interpolation, Speaking->Idle uses Bezier curve easing
18
+
19
+ ### 🐛 Bugfixes
20
+ - Fixed vanilla demo audio context initialization issue
21
+ - Fixed recording button only working once in vanilla demo
22
+ - Fixed host mode data loading to use local files instead of API endpoint
23
+
24
+ ### 📚 Documentation
25
+ - Added transition animation technical specification document for cross-platform alignment
26
+
27
+ ## [1.0.0-beta.63] - 2026-01-14
28
+
29
+ ### ✨ New Features
30
+ - **Audio Context Initialization API** - Added `initializeAudioContext()` method to `AvatarController`
31
+ - Must be called in a user gesture context (click, touchstart, etc.) before any audio operations
32
+ - Ensures AudioContext is created and initialized in a user gesture context, preventing browser security policy issues
33
+ - All audio operations (`send()`, `yieldAudioData()`, `start()`, `playback()`, etc.) now require prior initialization
34
+
35
+ ### 🔧 Improvements
36
+ - **Initialization Flow** - Removed all lazy initialization logic for audio context
37
+ - Audio context initialization is now centralized in `initializeAudioContext()` method
38
+ - All audio operations check for initialization before proceeding
39
+ - Clear error messages when audio operations are attempted without initialization
40
+
41
+ ### 🐛 Bugfixes
42
+ - **Audio Context User Gesture Requirement** - Fixed issue where AudioContext could not be properly initialized when external applications request recording permissions
43
+ - Audio context must now be initialized in user gesture context, ensuring browser security policies are satisfied
44
+
5
45
  ## [1.0.0-beta.62] - 2026-01-14
6
46
 
7
47
  ### ✨ New Features
package/README.md CHANGED
@@ -18,8 +18,76 @@ Real-time virtual avatar rendering SDK based on 3D Gaussian Splatting, supportin
18
18
  npm install @spatialwalk/avatarkit
19
19
  ```
20
20
 
21
+ ## 🔧 Vite 配置(推荐)
22
+
23
+ 如果你使用 Vite 作为构建工具,强烈推荐使用我们的 Vite 插件来自动处理 WASM 文件配置。插件会自动处理所有必要的配置,让你无需手动设置。
24
+
25
+ ### 使用插件
26
+
27
+ 在 `vite.config.ts` 中添加插件:
28
+
29
+ ```typescript
30
+ import { defineConfig } from 'vite'
31
+ import { avatarkitVitePlugin } from '@spatialwalk/avatarkit/vite'
32
+
33
+ export default defineConfig({
34
+ plugins: [
35
+ avatarkitVitePlugin(), // 添加这一行即可
36
+ ],
37
+ })
38
+ ```
39
+
40
+ ### 插件功能
41
+
42
+ 插件会自动处理:
43
+
44
+ - ✅ **开发服务器**:自动设置 WASM 文件的正确 MIME 类型 (`application/wasm`)
45
+ - ✅ **构建时**:自动复制 WASM 文件到 `dist/assets/` 目录
46
+ - ✅ **Cloudflare Pages**:自动生成 `_headers` 文件
47
+ - ✅ **Vite 配置**:自动配置 `optimizeDeps`、`assetsInclude`、`assetsInlineLimit` 等选项
48
+
49
+ ### 手动配置(不使用插件)
50
+
51
+ 如果你不使用 Vite 插件,需要手动配置以下内容:
52
+
53
+ ```typescript
54
+ // vite.config.ts
55
+ export default defineConfig({
56
+ optimizeDeps: {
57
+ exclude: ['@spatialwalk/avatarkit'],
58
+ },
59
+ assetsInclude: ['**/*.wasm'],
60
+ build: {
61
+ assetsInlineLimit: 0,
62
+ rollupOptions: {
63
+ output: {
64
+ assetFileNames: (assetInfo) => {
65
+ if (assetInfo.name?.endsWith('.wasm')) {
66
+ return 'assets/[name][extname]'
67
+ }
68
+ return 'assets/[name]-[hash][extname]'
69
+ },
70
+ },
71
+ },
72
+ },
73
+ // 开发服务器需要手动配置中间件设置 WASM MIME 类型
74
+ configureServer(server) {
75
+ server.middlewares.use((req, res, next) => {
76
+ if (req.url?.endsWith('.wasm')) {
77
+ res.setHeader('Content-Type', 'application/wasm')
78
+ }
79
+ next()
80
+ })
81
+ },
82
+ })
83
+ ```
84
+
21
85
  ## 🎯 Quick Start
22
86
 
87
+ ### ⚠️ Important: Audio Context Initialization
88
+
89
+ **Before using any audio-related features, you MUST initialize the audio context in a user gesture context** (e.g., `click`, `touchstart` event handlers). This is required by browser security policies. Calling `initializeAudioContext()` outside a user gesture will fail.
90
+
23
91
  ### Basic Usage
24
92
 
25
93
  ```typescript
@@ -70,13 +138,21 @@ const avatar = await avatarManager.load('character-id', (progress) => {
70
138
  const container = document.getElementById('avatar-container')
71
139
  const avatarView = new AvatarView(avatar, container)
72
140
 
73
- // 4. Start real-time communication (SDK mode only)
74
- await avatarView.avatarController.start()
75
-
76
- // 5. Send audio data (SDK mode, must be mono PCM16 format matching configured sample rate)
77
- const audioData = new ArrayBuffer(1024) // Example: PCM16 audio data at configured sample rate
78
- avatarView.avatarController.send(audioData, false) // Send audio data
79
- avatarView.avatarController.send(audioData, true) // end=true marks the end of current conversation round
141
+ // 4. ⚠️ CRITICAL: Initialize audio context (MUST be called in user gesture context)
142
+ // This method MUST be called within a user gesture event handler (click, touchstart, etc.)
143
+ // to satisfy browser security policies. Calling it outside a user gesture will fail.
144
+ button.addEventListener('click', async () => {
145
+ // Initialize audio context - MUST be in user gesture context
146
+ await avatarView.controller.initializeAudioContext()
147
+
148
+ // 5. Start real-time communication (SDK mode only)
149
+ await avatarView.controller.start()
150
+
151
+ // 6. Send audio data (SDK mode, must be mono PCM16 format matching configured sample rate)
152
+ const audioData = new ArrayBuffer(1024) // Example: PCM16 audio data at configured sample rate
153
+ avatarView.controller.send(audioData, false) // Send audio data
154
+ avatarView.controller.send(audioData, true) // end=true marks the end of current conversation round
155
+ })
80
156
  ```
81
157
 
82
158
  ### Host Mode Example
@@ -89,10 +165,17 @@ avatarView.avatarController.send(audioData, true) // end=true marks the end of c
89
165
  const container = document.getElementById('avatar-container')
90
166
  const avatarView = new AvatarView(avatar, container)
91
167
 
92
- // 4. Host Mode Workflow:
93
- // Send audio data first to get conversationId, then use it to send animation data
94
- const conversationId = avatarView.avatarController.yieldAudioData(audioData, false)
95
- avatarView.avatarController.yieldFramesData(animationDataArray, conversationId) // animationDataArray: (Uint8Array | ArrayBuffer)[]
168
+ // 4. ⚠️ CRITICAL: Initialize audio context (MUST be called in user gesture context)
169
+ // This method MUST be called within a user gesture event handler (click, touchstart, etc.)
170
+ // to satisfy browser security policies. Calling it outside a user gesture will fail.
171
+ button.addEventListener('click', async () => {
172
+ // Initialize audio context - MUST be in user gesture context
173
+ await avatarView.controller.initializeAudioContext()
174
+
175
+ // 5. Host Mode Workflow:
176
+ // Send audio data first to get conversationId, then use it to send animation data
177
+ const conversationId = avatarView.controller.yieldAudioData(audioData, false)
178
+ avatarView.controller.yieldFramesData(animationDataArray, conversationId) // animationDataArray: (Uint8Array | ArrayBuffer)[]
96
179
  ```
97
180
 
98
181
  ### Complete Examples
@@ -350,34 +433,52 @@ Audio/animation playback controller (playback layer), manages synchronized playb
350
433
  #### SDK Mode Methods
351
434
 
352
435
  ```typescript
353
- // Start WebSocket service
354
- await avatarView.avatarController.start()
355
-
356
- // Send audio data (must be 16kHz mono PCM16 format)
357
- const conversationId = avatarView.avatarController.send(audioData: ArrayBuffer, end: boolean)
358
- // Returns: conversationId - Conversation ID for this conversation session
359
- // end: false (default) - Continue sending audio data for current conversation
360
- // end: true - Mark the end of current conversation round. After end=true, sending new audio data will interrupt any ongoing playback from the previous conversation round
436
+ // ⚠️ CRITICAL: Initialize audio context first (MUST be called in user gesture context)
437
+ // This method MUST be called within a user gesture event handler (click, touchstart, etc.)
438
+ // to satisfy browser security policies. Calling it outside a user gesture will fail.
439
+ // All audio operations (start, send, etc.) require prior initialization.
440
+ button.addEventListener('click', async () => {
441
+ // Initialize audio context - MUST be in user gesture context
442
+ await avatarView.controller.initializeAudioContext()
443
+
444
+ // Start WebSocket service
445
+ await avatarView.controller.start()
446
+
447
+ // Send audio data (must be 16kHz mono PCM16 format)
448
+ const conversationId = avatarView.controller.send(audioData: ArrayBuffer, end: boolean)
449
+ // Returns: conversationId - Conversation ID for this conversation session
450
+ // end: false (default) - Continue sending audio data for current conversation
451
+ // end: true - Mark the end of current conversation round. After end=true, sending new audio data will interrupt any ongoing playback from the previous conversation round
452
+ })
361
453
 
362
454
  // Close WebSocket service
363
- avatarView.avatarController.close()
455
+ avatarView.controller.close()
364
456
  ```
365
457
 
366
458
  #### Host Mode Methods
367
459
 
368
460
  ```typescript
369
- // Stream audio chunks (must be 16kHz mono PCM16 format)
370
- const conversationId = avatarView.avatarController.yieldAudioData(
371
- data: Uint8Array, // Audio chunk data
372
- isLast: boolean = false // Whether this is the last chunk
373
- )
374
- // Returns: conversationId - Conversation ID for this audio session
375
-
376
- // Stream animation keyframes (requires conversationId from audio data)
377
- avatarView.avatarController.yieldFramesData(
378
- keyframesDataArray: (Uint8Array | ArrayBuffer)[], // Animation keyframes binary data array (each element is a protobuf encoded Message)
379
- conversationId: string // Conversation ID (required)
380
- )
461
+ // ⚠️ CRITICAL: Initialize audio context first (MUST be called in user gesture context)
462
+ // This method MUST be called within a user gesture event handler (click, touchstart, etc.)
463
+ // to satisfy browser security policies. Calling it outside a user gesture will fail.
464
+ // All audio operations (yieldAudioData, yieldFramesData, etc.) require prior initialization.
465
+ button.addEventListener('click', async () => {
466
+ // Initialize audio context - MUST be in user gesture context
467
+ await avatarView.controller.initializeAudioContext()
468
+
469
+ // Stream audio chunks (must be 16kHz mono PCM16 format)
470
+ const conversationId = avatarView.controller.yieldAudioData(
471
+ data: Uint8Array, // Audio chunk data
472
+ isLast: boolean = false // Whether this is the last chunk
473
+ )
474
+ // Returns: conversationId - Conversation ID for this audio session
475
+
476
+ // Stream animation keyframes (requires conversationId from audio data)
477
+ avatarView.controller.yieldFramesData(
478
+ keyframesDataArray: (Uint8Array | ArrayBuffer)[], // Animation keyframes binary data array (each element is a protobuf encoded Message)
479
+ conversationId: string // Conversation ID (required)
480
+ )
481
+ })
381
482
  ```
382
483
 
383
484
  **⚠️ Important: Conversation ID (conversationId) Management**
@@ -555,17 +656,17 @@ The rendering system automatically selects the best backend, no manual configura
555
656
 
556
657
  ## 🚨 Error Handling
557
658
 
558
- ### SPAvatarError
659
+ ### AvatarError
559
660
 
560
661
  The SDK uses custom error types, providing more detailed error information:
561
662
 
562
663
  ```typescript
563
- import { SPAvatarError } from '@spatialwalk/avatarkit'
664
+ import { AvatarError } from '@spatialwalk/avatarkit'
564
665
 
565
666
  try {
566
667
  await avatarView.avatarController.start()
567
668
  } catch (error) {
568
- if (error instanceof SPAvatarError) {
669
+ if (error instanceof AvatarError) {
569
670
  console.error('SDK Error:', error.message, error.code)
570
671
  } else {
571
672
  console.error('Unknown error:', error)
@@ -1,7 +1,7 @@
1
1
  var __defProp = Object.defineProperty;
2
2
  var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
3
3
  var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
4
- import { A as APP_CONFIG, l as logger, e as errorToMessage, a as logEvent } from "./index-Bhjn1nq3.js";
4
+ import { A as APP_CONFIG, l as logger, e as errorToMessage, a as logEvent } from "./index-D0sXXlqd.js";
5
5
  class StreamingAudioPlayer {
6
6
  constructor(options) {
7
7
  __publicField(this, "audioContext", null);