@spatialwalk/avatarkit 1.0.0-beta.92 → 1.0.0-beta.93
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 +12 -0
- package/README.md +57 -25
- package/dist/{StreamingAudioPlayer-eQ2RRq5U.js → StreamingAudioPlayer-DWVeTI7D.js} +1 -1
- package/dist/avatar_core_wasm-BY3MuXDA.js +1 -1
- package/dist/{index-C2higMlc.js → index-CDywZ8iv.js} +55 -11
- package/dist/index.js +1 -1
- package/package.json +1 -1
- package/dist/avatar_core_wasm-9834c91c.wasm +0 -0
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,18 @@ All notable changes to this project will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [1.0.0-beta.93]
|
|
9
|
+
|
|
10
|
+
### 🐛 Bugfixes
|
|
11
|
+
|
|
12
|
+
- **WebGPU Tab Switch Rendering Fix** — Fixed avatar rendering corruption (split/misaligned) when switching back to a tab. Root cause: WebGPU context was not reconfigured after canvas resize, causing surface size and screenSize mismatch in the shader.
|
|
13
|
+
|
|
14
|
+
### ✨ Features
|
|
15
|
+
|
|
16
|
+
- **WebSocket Close Code Handling** — Server-defined custom close codes (4010 auth failure, 4001 insufficient balance, 4002 session timeout, 4003 concurrent limit) are now properly recognized and mapped to `ErrorCode`. Non-recoverable errors (4010/4001/4003) no longer trigger reconnection attempts.
|
|
17
|
+
- **Error Transparency** — All non-normal WebSocket close events are now propagated to `onError` callback and reported to PostHog telemetry. Previously, some close codes (e.g. 1006) were only logged internally without notifying the application.
|
|
18
|
+
- **New ErrorCode Values** — Added `insufficientBalance`, `sessionTimeout`, `concurrentLimitExceeded` to the `ErrorCode` enum.
|
|
19
|
+
|
|
8
20
|
## [1.0.0-beta.92]
|
|
9
21
|
|
|
10
22
|
### ✨ Features
|
package/README.md
CHANGED
|
@@ -234,9 +234,10 @@ const configuration: Configuration = {
|
|
|
234
234
|
// - LogLevel.error: Only error logs
|
|
235
235
|
// - LogLevel.warning: Warning and error logs
|
|
236
236
|
// - LogLevel.all: All logs (info, warning, error)
|
|
237
|
-
audioFormat: { //
|
|
237
|
+
audioFormat: { // Default is { channelCount: 1, sampleRate: 16000 }
|
|
238
238
|
channelCount: 1, // Fixed to 1 (mono)
|
|
239
239
|
sampleRate: 16000 // Supported: 8000, 16000, 22050, 24000, 32000, 44100, 48000 Hz
|
|
240
|
+
// ⚠️ Must match your actual audio sample rate. Mismatched sample rate will cause playback issues.
|
|
240
241
|
}
|
|
241
242
|
// characterApiBaseUrl: 'https://custom-api.example.com' // Optional, internal debug config, can be ignored
|
|
242
243
|
}
|
|
@@ -269,9 +270,18 @@ button.addEventListener('click', async () => {
|
|
|
269
270
|
await avatarView.controller.initializeAudioContext()
|
|
270
271
|
|
|
271
272
|
// 5. Start real-time communication (SDK mode only)
|
|
273
|
+
// Note: start() initiates the WebSocket connection asynchronously.
|
|
274
|
+
// Wait for onConnectionState === 'connected' before calling send().
|
|
272
275
|
await avatarView.controller.start()
|
|
273
|
-
|
|
274
|
-
// 6.
|
|
276
|
+
|
|
277
|
+
// 6. Wait for connection to be ready
|
|
278
|
+
await new Promise<void>((resolve) => {
|
|
279
|
+
avatarView.controller.onConnectionState = (state) => {
|
|
280
|
+
if (state === ConnectionState.connected) resolve()
|
|
281
|
+
}
|
|
282
|
+
})
|
|
283
|
+
|
|
284
|
+
// 7. Send audio data (SDK mode, must be mono PCM16 format matching configured sample rate)
|
|
275
285
|
// audioData: ArrayBuffer or Uint8Array containing PCM16 audio samples
|
|
276
286
|
// - PCM files: Can be directly read as ArrayBuffer
|
|
277
287
|
// - WAV files: Extract PCM data from WAV format (may require resampling)
|
|
@@ -592,21 +602,25 @@ avatarView.transform = { x, y, scale }
|
|
|
592
602
|
avatarView.dispose()
|
|
593
603
|
```
|
|
594
604
|
|
|
595
|
-
**
|
|
605
|
+
**Switching Avatars:**
|
|
606
|
+
|
|
607
|
+
To switch avatars, dispose the old view and create a new one. Do NOT attempt to reuse or reset an existing AvatarView.
|
|
608
|
+
- `AvatarSDK.initialize()` and session token do not need to be called again.
|
|
609
|
+
- The old AvatarView's internal state is fully cleaned up by `dispose()`.
|
|
596
610
|
|
|
597
611
|
```typescript
|
|
598
|
-
//
|
|
612
|
+
// 1. Dispose old avatar
|
|
599
613
|
if (currentAvatarView) {
|
|
600
614
|
currentAvatarView.dispose()
|
|
601
615
|
}
|
|
602
616
|
|
|
603
|
-
// Load new avatar
|
|
604
|
-
const newAvatar = await
|
|
617
|
+
// 2. Load new avatar (SDK is already initialized, token is still valid)
|
|
618
|
+
const newAvatar = await AvatarManager.shared.load('new-character-id')
|
|
605
619
|
|
|
606
|
-
// Create new AvatarView
|
|
620
|
+
// 3. Create new AvatarView
|
|
607
621
|
currentAvatarView = new AvatarView(newAvatar, container)
|
|
608
622
|
|
|
609
|
-
//
|
|
623
|
+
// 4. Start connection if SDK mode
|
|
610
624
|
await currentAvatarView.controller.start()
|
|
611
625
|
```
|
|
612
626
|
|
|
@@ -634,7 +648,7 @@ button.addEventListener('click', async () => {
|
|
|
634
648
|
const conversationId = avatarView.controller.send(audioData: ArrayBuffer, end: boolean)
|
|
635
649
|
// Returns: conversationId - Conversation ID for this conversation session
|
|
636
650
|
// end: false (default) - Continue sending audio data for current conversation
|
|
637
|
-
// 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
|
|
651
|
+
// end: true - Mark the end of audio input for current conversation round. The avatar will continue playing remaining animation until finished, then automatically return to idle (notified via onConversationState). After end=true, sending new audio data will interrupt any ongoing playback from the previous conversation round
|
|
638
652
|
})
|
|
639
653
|
|
|
640
654
|
// Close service
|
|
@@ -706,7 +720,7 @@ const currentVolume = avatarView.controller.getVolume() // Get current volume (
|
|
|
706
720
|
// Set event callbacks
|
|
707
721
|
avatarView.controller.onConnectionState = (state: ConnectionState) => {} // SDK mode only
|
|
708
722
|
avatarView.controller.onConversationState = (state: ConversationState) => {}
|
|
709
|
-
avatarView.controller.onError = (error:
|
|
723
|
+
avatarView.controller.onError = (error: AvatarError) => {} // Includes error.code for specific error type
|
|
710
724
|
```
|
|
711
725
|
|
|
712
726
|
#### Avatar Transform Methods
|
|
@@ -866,23 +880,41 @@ try {
|
|
|
866
880
|
```typescript
|
|
867
881
|
import { AvatarError } from '@spatialwalk/avatarkit'
|
|
868
882
|
|
|
869
|
-
avatarView.controller.onError = (error:
|
|
870
|
-
|
|
871
|
-
console.error('AvatarController error:', error.message, error.code)
|
|
872
|
-
return
|
|
873
|
-
}
|
|
874
|
-
|
|
875
|
-
console.error('AvatarController unknown error:', error)
|
|
883
|
+
avatarView.controller.onError = (error: AvatarError) => {
|
|
884
|
+
console.error('Error:', error.code, error.message)
|
|
876
885
|
}
|
|
877
886
|
```
|
|
878
887
|
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
888
|
+
`error.code` values (from `ErrorCode` enum):
|
|
889
|
+
|
|
890
|
+
| Code | Description | Trigger |
|
|
891
|
+
|------|-------------|---------|
|
|
892
|
+
| **Authentication & Authorization** | | |
|
|
893
|
+
| `appIDUnrecognized` | App ID not recognized | Reserved |
|
|
894
|
+
| `sessionTokenInvalid` | Token invalid or appId mismatch | WebSocket close code 4010 |
|
|
895
|
+
| `sessionTokenExpired` | Token expired | WebSocket close code 4010 |
|
|
896
|
+
| `insufficientBalance` | Insufficient balance | WebSocket close code 4001 |
|
|
897
|
+
| `concurrentLimitExceeded` | Concurrent connection limit exceeded | WebSocket close code 4003 |
|
|
898
|
+
| **Resource Loading** | | |
|
|
899
|
+
| `avatarIDUnrecognized` | Avatar ID not found | Server error |
|
|
900
|
+
| `failedToFetchAvatarMetadata` | Metadata fetch failed | Network/server error |
|
|
901
|
+
| `failedToDownloadAvatarAssets` | Asset download failed | Network/server error |
|
|
902
|
+
| **Connection** | | |
|
|
903
|
+
| `websocketError` | WebSocket handshake or network error | Connection failure |
|
|
904
|
+
| `websocketClosedAbnormally` | Connection closed abnormally | Close code 1006 |
|
|
905
|
+
| `websocketClosedUnexpected` | Unexpected close code | Unknown close code |
|
|
906
|
+
| `sessionTimeout` | Session timeout | WebSocket close code 4002 |
|
|
907
|
+
| `connectionInProgress` | Connection already in progress | Duplicate `start()` call |
|
|
908
|
+
| **Playback** | | |
|
|
909
|
+
| `networkLayerNotAvailable` | Network layer not available | `send()` in host mode |
|
|
910
|
+
| `playbackStartFailed` | Failed to start playback | Internal error |
|
|
911
|
+
| `playbackInitFailed` | Playback initialization failed | Internal error |
|
|
912
|
+
| `audioOnlyInitFailed` | Audio-only playback init failed | Fallback mode error |
|
|
913
|
+
| `noAudio` | No audio data to play | Empty audio input |
|
|
914
|
+
| `audioContextNotInitialized` | Audio context not initialized | `send()` before `initializeAudioContext()` |
|
|
915
|
+
| `animationPlayerNotInitialized` | Animation player not initialized | Internal error |
|
|
916
|
+
| **Server** | | |
|
|
917
|
+
| `serverError` | Server-side error | Server MESSAGE_SERVER_ERROR |
|
|
886
918
|
|
|
887
919
|
## 🔄 Resource Management
|
|
888
920
|
|
|
@@ -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-
|
|
4
|
+
import { A as APP_CONFIG, l as logger, e as errorToMessage, a as logEvent } from "./index-CDywZ8iv.js";
|
|
5
5
|
class StreamingAudioPlayer {
|
|
6
6
|
// Mark if AudioContext is being resumed, avoid concurrent resume requests
|
|
7
7
|
constructor(options) {
|