@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.
Binary file
@@ -44,6 +44,8 @@ export declare class AvatarController {
44
44
  playbackMode?: DrivingServiceMode;
45
45
  });
46
46
  getCurrentConversationId(): string | null;
47
+ initializeAudioContext(): Promise<void>;
48
+ private checkAudioContextInitialized;
47
49
  start(): Promise<void>;
48
50
  send(audioData: ArrayBuffer, end?: boolean): string | null;
49
51
  close(): void;
@@ -26,6 +26,7 @@ export declare class AvatarView {
26
26
  private readonly endTransitionDurationMs;
27
27
  private cachedIdleFirstFrame;
28
28
  private idleCurrentFrameIndex;
29
+ private currentPlayingFrame;
29
30
  private characterHandle;
30
31
  private characterId;
31
32
  private isPureRenderingMode;
@@ -7173,11 +7173,11 @@ var ErrorCode = /* @__PURE__ */ ((ErrorCode2) => {
7173
7173
  ErrorCode2["failedToDownloadAvatarAssets"] = "failedToDownloadAvatarAssets";
7174
7174
  return ErrorCode2;
7175
7175
  })(ErrorCode || {});
7176
- class SPAvatarError extends Error {
7176
+ class AvatarError extends Error {
7177
7177
  constructor(message, code) {
7178
7178
  super(message);
7179
7179
  this.code = code;
7180
- this.name = "SPAvatarError";
7180
+ this.name = "AvatarError";
7181
7181
  }
7182
7182
  }
7183
7183
  {
@@ -7624,7 +7624,7 @@ const _AnimationPlayer = class _AnimationPlayer {
7624
7624
  if (this.streamingPlayer) {
7625
7625
  return;
7626
7626
  }
7627
- const { StreamingAudioPlayer } = await import("./StreamingAudioPlayer-BWsAt_s7.js");
7627
+ const { StreamingAudioPlayer } = await import("./StreamingAudioPlayer-CugAsNXV.js");
7628
7628
  const { AvatarSDK: AvatarSDK2 } = await Promise.resolve().then(() => AvatarSDK$1);
7629
7629
  const audioFormat = AvatarSDK2.getAudioFormat();
7630
7630
  this.streamingPlayer = new StreamingAudioPlayer({
@@ -8180,7 +8180,7 @@ class AvatarCoreAdapter {
8180
8180
  }
8181
8181
  async loadWASMModule() {
8182
8182
  try {
8183
- const { default: AvatarCoreModule } = await import("./avatar_core_wasm-i0Ocpx6q.js");
8183
+ const { default: AvatarCoreModule } = await import("./avatar_core_wasm-Dv943JJl.js");
8184
8184
  if (this.wasmConfig && Object.keys(this.wasmConfig).length > 0) {
8185
8185
  this.wasmModule = await AvatarCoreModule(this.wasmConfig);
8186
8186
  } else {
@@ -8961,7 +8961,7 @@ class AvatarSDK {
8961
8961
  }
8962
8962
  __publicField(AvatarSDK, "_isInitialized", false);
8963
8963
  __publicField(AvatarSDK, "_configuration", null);
8964
- __publicField(AvatarSDK, "_version", "1.0.0-beta.62");
8964
+ __publicField(AvatarSDK, "_version", "1.0.0-beta.64");
8965
8965
  __publicField(AvatarSDK, "_avatarCore", null);
8966
8966
  __publicField(AvatarSDK, "_dynamicSdkConfig", null);
8967
8967
  const AvatarSDK$1 = Object.freeze(Object.defineProperty({
@@ -10156,7 +10156,7 @@ class AnimationWebSocketClient extends EventEmitter {
10156
10156
  });
10157
10157
  }
10158
10158
  if (sdkErrorCode) {
10159
- const error = new SPAvatarError(
10159
+ const error = new AvatarError(
10160
10160
  reason || `WebSocket connection failed with code ${event.code}`,
10161
10161
  sdkErrorCode
10162
10162
  );
@@ -10313,7 +10313,7 @@ class NetworkLayer {
10313
10313
  var _a, _b, _c, _d;
10314
10314
  if (this.isConnecting) {
10315
10315
  logger.warn("[NetworkLayer] Connection already in progress, waiting for current connection to complete");
10316
- throw new SPAvatarError("Connection already in progress", "CONNECTION_IN_PROGRESS");
10316
+ throw new AvatarError("Connection already in progress", "CONNECTION_IN_PROGRESS");
10317
10317
  }
10318
10318
  (_b = (_a = this.dataController).onConnectionState) == null ? void 0 : _b.call(_a, ConnectionState.connecting);
10319
10319
  this.isFallbackMode = false;
@@ -10391,7 +10391,7 @@ class NetworkLayer {
10391
10391
  const sent = this.wsClient.sendAudioData(this.currentConversationId, audioData, isLast);
10392
10392
  if (!sent) {
10393
10393
  logger.error("[NetworkLayer] Failed to send audio data to WebSocket");
10394
- (_b = (_a = this.dataController).onError) == null ? void 0 : _b.call(_a, new SPAvatarError("Failed to send audio data to WebSocket", "SEND_FAILED"));
10394
+ (_b = (_a = this.dataController).onError) == null ? void 0 : _b.call(_a, new AvatarError("Failed to send audio data to WebSocket", "SEND_FAILED"));
10395
10395
  logEvent("send_audio_failed", "error", {
10396
10396
  req_id: this.currentConversationId || "",
10397
10397
  con_id: idManager.getConnectionId() || "",
@@ -10549,7 +10549,7 @@ class NetworkLayer {
10549
10549
  } else {
10550
10550
  sdkErrorCode = errorCodeStr || "SERVER_ERROR";
10551
10551
  }
10552
- (_b = (_a = this.dataController).onError) == null ? void 0 : _b.call(_a, new SPAvatarError(
10552
+ (_b = (_a = this.dataController).onError) == null ? void 0 : _b.call(_a, new AvatarError(
10553
10553
  errorMessage,
10554
10554
  sdkErrorCode
10555
10555
  ));
@@ -10741,38 +10741,71 @@ class AvatarController {
10741
10741
  getCurrentConversationId() {
10742
10742
  return this.getEffectiveConversationId();
10743
10743
  }
10744
- async start() {
10745
- if (!this.networkLayer) {
10746
- throw new SPAvatarError(
10747
- "Network layer not available. Use SDK mode.",
10748
- "NETWORK_LAYER_NOT_AVAILABLE"
10749
- );
10744
+ async initializeAudioContext() {
10745
+ var _a;
10746
+ if ((_a = this.animationPlayer) == null ? void 0 : _a.isStreamingReady()) {
10747
+ return;
10750
10748
  }
10751
10749
  if (!this.animationPlayer) {
10752
10750
  this.animationPlayer = new AnimationPlayer();
10751
+ }
10752
+ if (!this.animationPlayer.isStreamingReady()) {
10753
10753
  try {
10754
10754
  await this.animationPlayer.createAndInitializeStreamingPlayer();
10755
10755
  } catch (error) {
10756
10756
  const message = error instanceof Error ? error.message : String(error);
10757
- logger.error("[AvatarController] Failed to create streaming player:", message);
10758
- logEvent("character_player", "error", {
10759
- avatar_id: this.avatar.id,
10760
- event: "streaming_player_init_failed",
10761
- reason: message
10762
- });
10763
- throw error;
10757
+ logger.error("[AvatarController] Failed to initialize audio context:", message);
10758
+ throw new AvatarError(
10759
+ `Failed to initialize audio context: ${message}`,
10760
+ "AUDIO_CONTEXT_INIT_FAILED"
10761
+ );
10764
10762
  }
10765
10763
  }
10764
+ const streamingPlayer = this.animationPlayer.getStreamingPlayer();
10765
+ if (streamingPlayer) {
10766
+ const audioContext = streamingPlayer.audioContext;
10767
+ if (audioContext && audioContext.state === "suspended") {
10768
+ try {
10769
+ await audioContext.resume();
10770
+ } catch (err) {
10771
+ logger.warn("[AvatarController] Failed to resume AudioContext during initialization:", err);
10772
+ }
10773
+ }
10774
+ }
10775
+ }
10776
+ checkAudioContextInitialized() {
10777
+ var _a;
10778
+ if (!((_a = this.animationPlayer) == null ? void 0 : _a.isStreamingReady())) {
10779
+ throw new AvatarError(
10780
+ "Audio context not initialized. Call initializeAudioContext() in a user gesture context first.",
10781
+ "AUDIO_CONTEXT_NOT_INITIALIZED"
10782
+ );
10783
+ }
10784
+ }
10785
+ async start() {
10786
+ if (!this.networkLayer) {
10787
+ throw new AvatarError(
10788
+ "Network layer not available. Use SDK mode.",
10789
+ "NETWORK_LAYER_NOT_AVAILABLE"
10790
+ );
10791
+ }
10792
+ this.checkAudioContextInitialized();
10766
10793
  await this.networkLayer.connect(this.avatar.id);
10767
10794
  }
10768
10795
  send(audioData, end = false) {
10769
- var _a, _b, _c;
10796
+ var _a, _b, _c, _d;
10797
+ try {
10798
+ this.checkAudioContextInitialized();
10799
+ } catch (error) {
10800
+ (_a = this.onError) == null ? void 0 : _a.call(this, error);
10801
+ return null;
10802
+ }
10770
10803
  if (!this.networkLayer) {
10771
- (_a = this.onError) == null ? void 0 : _a.call(this, new SPAvatarError("Network layer not available", "NETWORK_LAYER_NOT_AVAILABLE"));
10804
+ (_b = this.onError) == null ? void 0 : _b.call(this, new AvatarError("Network layer not available", "NETWORK_LAYER_NOT_AVAILABLE"));
10772
10805
  return null;
10773
10806
  }
10774
10807
  if (!this.networkLayer.canSend()) {
10775
- (_b = this.onError) == null ? void 0 : _b.call(this, new SPAvatarError("Service not connected", "NOT_CONNECTED"));
10808
+ (_c = this.onError) == null ? void 0 : _c.call(this, new AvatarError("Service not connected", "NOT_CONNECTED"));
10776
10809
  logEvent("character_manager", "warning", {
10777
10810
  avatar_id: this.avatar.id,
10778
10811
  event: "send_not_connected"
@@ -10790,7 +10823,7 @@ class AvatarController {
10790
10823
  }
10791
10824
  if (!this.isPlaying && this.currentState === AvatarState.idle) {
10792
10825
  this.currentState = AvatarState.active;
10793
- (_c = this.onConversationState) == null ? void 0 : _c.call(this, this.mapToConversationState(AvatarState.active));
10826
+ (_d = this.onConversationState) == null ? void 0 : _d.call(this, this.mapToConversationState(AvatarState.active));
10794
10827
  }
10795
10828
  return this.networkLayer.getCurrentConversationId();
10796
10829
  }
@@ -10810,18 +10843,13 @@ class AvatarController {
10810
10843
  (_a = this.onConnectionState) == null ? void 0 : _a.call(this, ConnectionState.disconnected);
10811
10844
  }
10812
10845
  async playback(initialAudioChunks, initialKeyframes) {
10846
+ this.checkAudioContextInitialized();
10813
10847
  if (this.isPlaying || this.currentConversationId) {
10814
10848
  this.interrupt();
10815
10849
  }
10816
10850
  this.currentConversationId = this.generateAndLogNewConversationId();
10817
10851
  this.reqEnd = false;
10818
10852
  this.clearPlaybackData();
10819
- if (!this.animationPlayer) {
10820
- this.animationPlayer = new AnimationPlayer();
10821
- }
10822
- if (!this.animationPlayer.isStreamingReady()) {
10823
- await this.animationPlayer.createAndInitializeStreamingPlayer();
10824
- }
10825
10853
  if (initialAudioChunks && initialAudioChunks.length > 0) {
10826
10854
  this.pendingAudioChunks.push(...initialAudioChunks);
10827
10855
  }
@@ -10838,7 +10866,7 @@ class AvatarController {
10838
10866
  this.isAudioOnlyMode = true;
10839
10867
  }
10840
10868
  if (this.pendingAudioChunks.length === 0) {
10841
- throw new SPAvatarError("No audio chunks to play", "NO_AUDIO");
10869
+ throw new AvatarError("No audio chunks to play", "NO_AUDIO");
10842
10870
  }
10843
10871
  if (this.isAudioOnlyMode) {
10844
10872
  await this.startAudioOnlyPlayback();
@@ -10848,7 +10876,13 @@ class AvatarController {
10848
10876
  return this.currentConversationId;
10849
10877
  }
10850
10878
  yieldAudioData(data, isLast = false) {
10851
- var _a, _b;
10879
+ var _a, _b, _c;
10880
+ try {
10881
+ this.checkAudioContextInitialized();
10882
+ } catch (error) {
10883
+ (_a = this.onError) == null ? void 0 : _a.call(this, error);
10884
+ return null;
10885
+ }
10852
10886
  if (this.reqEnd && this.isPlaying && this.currentConversationId) {
10853
10887
  this.interrupt();
10854
10888
  this.currentConversationId = this.generateAndLogNewConversationId();
@@ -10879,12 +10913,12 @@ class AvatarController {
10879
10913
  metrics.tap2Timestamp = Date.now();
10880
10914
  }
10881
10915
  }
10882
- if (this.isPlaying && ((_a = this.animationPlayer) == null ? void 0 : _a.isStreamingReady())) {
10916
+ if (this.isPlaying && ((_b = this.animationPlayer) == null ? void 0 : _b.isStreamingReady())) {
10883
10917
  this.animationPlayer.addAudioChunk(data, isLast);
10884
10918
  } else {
10885
10919
  if (data.length > 0 || isLast) {
10886
10920
  this.pendingAudioChunks.push({ data, isLast });
10887
- (_b = this.onConversationState) == null ? void 0 : _b.call(this, this.mapToConversationState(AvatarState.active));
10921
+ (_c = this.onConversationState) == null ? void 0 : _c.call(this, this.mapToConversationState(AvatarState.active));
10888
10922
  }
10889
10923
  }
10890
10924
  return this.currentConversationId;
@@ -10988,7 +11022,7 @@ class AvatarController {
10988
11022
  var _a;
10989
11023
  this.isStartingPlayback = false;
10990
11024
  logger.error("[AvatarController] Failed to auto-start playback:", error);
10991
- (_a = this.onError) == null ? void 0 : _a.call(this, new SPAvatarError("Failed to start playback", "PLAYBACK_START_FAILED"));
11025
+ (_a = this.onError) == null ? void 0 : _a.call(this, new AvatarError("Failed to start playback", "PLAYBACK_START_FAILED"));
10992
11026
  });
10993
11027
  }
10994
11028
  }
@@ -11237,6 +11271,7 @@ class AvatarController {
11237
11271
  }
11238
11272
  async startStreamingPlaybackInternal() {
11239
11273
  var _a, _b, _c;
11274
+ this.checkAudioContextInitialized();
11240
11275
  if (this.isPlaying) {
11241
11276
  this.isStartingPlayback = false;
11242
11277
  return;
@@ -11245,30 +11280,15 @@ class AvatarController {
11245
11280
  return;
11246
11281
  }
11247
11282
  this.isStartingPlayback = true;
11248
- if (!this.animationPlayer) {
11249
- this.animationPlayer = new AnimationPlayer();
11250
- }
11251
- if (!this.animationPlayer.isStreamingReady()) {
11252
- try {
11253
- await this.animationPlayer.createAndInitializeStreamingPlayer();
11254
- } catch (error) {
11255
- this.isStartingPlayback = false;
11256
- const message = error instanceof Error ? error.message : String(error);
11257
- logger.error("[AvatarController] Failed to create streaming player:", message);
11258
- logEvent("character_player", "error", {
11259
- avatar_id: this.avatar.id,
11260
- event: "streaming_player_init_failed",
11261
- reason: message
11262
- });
11263
- throw error;
11264
- }
11265
- }
11266
11283
  if (!this.currentKeyframes || this.currentKeyframes.length === 0) {
11267
11284
  this.isStartingPlayback = false;
11268
11285
  logger.warn("[AvatarController] No animation data to play");
11269
11286
  return;
11270
11287
  }
11271
11288
  try {
11289
+ if (!this.animationPlayer) {
11290
+ throw new AvatarError("Animation player not initialized", "ANIMATION_PLAYER_NOT_INITIALIZED");
11291
+ }
11272
11292
  await this.animationPlayer.prepareStreamingPlayer(() => {
11273
11293
  var _a2, _b2;
11274
11294
  this.isPlaying = false;
@@ -11311,7 +11331,7 @@ class AvatarController {
11311
11331
  } catch (error) {
11312
11332
  const message = error instanceof Error ? error.message : String(error);
11313
11333
  logger.error("[AvatarController] Failed to start streaming playback:", message);
11314
- (_c = this.onError) == null ? void 0 : _c.call(this, new SPAvatarError("Failed to start streaming playback", "INIT_FAILED"));
11334
+ (_c = this.onError) == null ? void 0 : _c.call(this, new AvatarError("Failed to start streaming playback", "INIT_FAILED"));
11315
11335
  this.isPlaying = false;
11316
11336
  this.isStartingPlayback = false;
11317
11337
  }
@@ -11490,21 +11510,7 @@ class AvatarController {
11490
11510
  async startAudioOnlyPlayback() {
11491
11511
  var _a, _b;
11492
11512
  if (!this.animationPlayer) {
11493
- this.animationPlayer = new AnimationPlayer();
11494
- }
11495
- if (!this.animationPlayer.isStreamingReady()) {
11496
- try {
11497
- await this.animationPlayer.createAndInitializeStreamingPlayer();
11498
- } catch (error) {
11499
- const message = error instanceof Error ? error.message : String(error);
11500
- logger.error("[AvatarController] Failed to create streaming player for audio-only mode:", message);
11501
- logEvent("character_player", "error", {
11502
- avatar_id: this.avatar.id,
11503
- event: "audio_only_streaming_player_init_failed",
11504
- reason: message
11505
- });
11506
- throw error;
11507
- }
11513
+ throw new AvatarError("Animation player not initialized", "ANIMATION_PLAYER_NOT_INITIALIZED");
11508
11514
  }
11509
11515
  try {
11510
11516
  await this.animationPlayer.prepareStreamingPlayer(() => {
@@ -11541,7 +11547,7 @@ class AvatarController {
11541
11547
  } catch (error) {
11542
11548
  const message = error instanceof Error ? error.message : String(error);
11543
11549
  logger.error("[AvatarController] Failed to start audio-only playback:", message);
11544
- (_b = this.onError) == null ? void 0 : _b.call(this, new SPAvatarError("Failed to start audio-only playback", "AUDIO_ONLY_INIT_FAILED"));
11550
+ (_b = this.onError) == null ? void 0 : _b.call(this, new AvatarError("Failed to start audio-only playback", "AUDIO_ONLY_INIT_FAILED"));
11545
11551
  this.isPlaying = false;
11546
11552
  this.isAudioOnlyMode = false;
11547
11553
  throw error;
@@ -11587,7 +11593,9 @@ class AvatarController {
11587
11593
  }
11588
11594
  addAudioChunkToBuffer(data, isLast) {
11589
11595
  if (!this.animationPlayer) {
11590
- this.animationPlayer = new AnimationPlayer();
11596
+ logger.warn("[AvatarController] animationPlayer is null in addAudioChunkToBuffer, this should not happen");
11597
+ this.pendingAudioChunks.push({ data, isLast });
11598
+ return;
11591
11599
  }
11592
11600
  if (this.isPlaying && this.animationPlayer.isStreamingReady()) {
11593
11601
  this.animationPlayer.addAudioChunk(data, isLast);
@@ -12088,9 +12096,10 @@ class AvatarDownloader {
12088
12096
  const idleAnimationUrl = (_i2 = (_h = (_g = characterMeta.animations) == null ? void 0 : _g.frameIdle) == null ? void 0 : _h.resource) == null ? void 0 : _i2.remote;
12089
12097
  if (!shapeUrl || !pointCloudUrl) {
12090
12098
  const reason = "Missing required resources: shape or gsStandard (point cloud)";
12091
- logEvent("download_avatar_assets_failed", "error", {
12099
+ logEvent("character_load", "error", {
12092
12100
  avatar_id: characterMeta.characterId ?? "unknown",
12093
- description: reason
12101
+ description: "Character does not exist in target environment",
12102
+ reason
12094
12103
  });
12095
12104
  throw new Error(reason);
12096
12105
  }
@@ -12138,6 +12147,14 @@ class AvatarDownloader {
12138
12147
  return { key, success: true, size: arrayBuffer.byteLength };
12139
12148
  } catch (error) {
12140
12149
  if (!optional) {
12150
+ const errorMessage = error instanceof Error ? error.message : String(error);
12151
+ logEvent("download_avatar_assets_failed", "error", {
12152
+ avatar_id: characterMeta.characterId ?? "unknown",
12153
+ description: `Failed to download required resource: ${filename}`,
12154
+ resource: key,
12155
+ url,
12156
+ error: errorMessage
12157
+ });
12141
12158
  throw error;
12142
12159
  }
12143
12160
  logger.warn(`⚠️ Optional resource ${filename} failed to load:`, error);
@@ -12224,7 +12241,7 @@ class AvatarDownloader {
12224
12241
  avatar_id: extractedCharacterId,
12225
12242
  description: errorMessage
12226
12243
  });
12227
- error = new SPAvatarError(errorMessage, ErrorCode.avatarIDUnrecognized);
12244
+ error = new AvatarError(errorMessage, ErrorCode.avatarIDUnrecognized);
12228
12245
  } else {
12229
12246
  error = new Error(`HTTP ${response.status} ${response.statusText}`);
12230
12247
  }
@@ -12254,7 +12271,7 @@ class AvatarDownloader {
12254
12271
  return response;
12255
12272
  } catch (error) {
12256
12273
  logger.errorWithError("Failed to fetch character:", error);
12257
- if (error instanceof SPAvatarError) {
12274
+ if (error instanceof AvatarError) {
12258
12275
  logEvent("fetch_avatar_metadata_failed", "error", {
12259
12276
  avatar_id: characterId ?? "unknown",
12260
12277
  description: error.message
@@ -12380,10 +12397,6 @@ const _AvatarManager = class _AvatarManager {
12380
12397
  } catch (error) {
12381
12398
  const message = error instanceof Error ? error.message : String(error);
12382
12399
  logger.error("Failed to load avatar:", message);
12383
- logEvent("character_load", "error", {
12384
- avatar_id: id,
12385
- reason: message
12386
- });
12387
12400
  onProgress == null ? void 0 : onProgress({ type: LoadProgress.failed, error });
12388
12401
  throw error;
12389
12402
  }
@@ -13852,6 +13865,38 @@ function lerpArrays(from, to2, progress) {
13852
13865
  return result2;
13853
13866
  }
13854
13867
  const clamp01 = (x2) => Math.max(0, Math.min(1, x2));
13868
+ function linearLerp(from, to2, progress) {
13869
+ return {
13870
+ translation: lerpArrays(from.translation || [0, 0, 0], to2.translation || [0, 0, 0], progress),
13871
+ rotation: lerpArrays(from.rotation || [0, 0, 0], to2.rotation || [0, 0, 0], progress),
13872
+ neckPose: lerpArrays(from.neckPose || [0, 0, 0], to2.neckPose || [0, 0, 0], progress),
13873
+ jawPose: lerpArrays(from.jawPose || [0, 0, 0], to2.jawPose || [0, 0, 0], progress),
13874
+ eyePose: lerpArrays(from.eyePose || [0, 0, 0, 0, 0, 0], to2.eyePose || [0, 0, 0, 0, 0, 0], progress),
13875
+ eyeLid: (() => {
13876
+ const fromEyelid = from.eyeLid;
13877
+ const toEyelid = to2.eyeLid;
13878
+ if ((fromEyelid == null ? void 0 : fromEyelid.length) && (toEyelid == null ? void 0 : toEyelid.length))
13879
+ return lerpArrays(fromEyelid, toEyelid, progress);
13880
+ return fromEyelid || toEyelid || [];
13881
+ })(),
13882
+ expression: lerpArrays(from.expression || [], to2.expression || [], progress)
13883
+ };
13884
+ }
13885
+ function generateTransitionFramesLinear(from, to2, durationMs, fps = 25) {
13886
+ const steps = Math.max(1, Math.floor(durationMs / 1e3 * fps));
13887
+ const frames = Array.from({ length: steps });
13888
+ if (steps === 1) {
13889
+ frames[0] = to2;
13890
+ return frames;
13891
+ }
13892
+ for (let i2 = 0; i2 < steps; i2++) {
13893
+ const progress = i2 / (steps - 1);
13894
+ frames[i2] = linearLerp(from, to2, progress);
13895
+ }
13896
+ frames[0] = from;
13897
+ frames[frames.length - 1] = to2;
13898
+ return frames;
13899
+ }
13855
13900
  function createBezierEasing(x1, y1, x2, y2) {
13856
13901
  const cx = 3 * x1;
13857
13902
  const bx = 3 * (x2 - x1) - cx;
@@ -13955,6 +14000,7 @@ class AvatarView {
13955
14000
  __publicField(this, "endTransitionDurationMs", 1600);
13956
14001
  __publicField(this, "cachedIdleFirstFrame", null);
13957
14002
  __publicField(this, "idleCurrentFrameIndex", 0);
14003
+ __publicField(this, "currentPlayingFrame", null);
13958
14004
  __publicField(this, "characterHandle", null);
13959
14005
  __publicField(this, "characterId");
13960
14006
  __publicField(this, "isPureRenderingMode", false);
@@ -14009,9 +14055,14 @@ class AvatarView {
14009
14055
  toFixed.expression = ensureLen(toFixed.expression, exprLen);
14010
14056
  return { from: fromFixed, to: toFixed };
14011
14057
  }
14012
- generateAndAlignTransitionFrames(from, to2, durationMs) {
14058
+ generateAndAlignTransitionFrames(from, to2, durationMs, useLinearInterpolation = false) {
14013
14059
  const aligned = this.alignFlamePair(from, to2);
14014
- let keyframes = generateTransitionFrames(
14060
+ let keyframes = useLinearInterpolation ? generateTransitionFramesLinear(
14061
+ aligned.from,
14062
+ aligned.to,
14063
+ durationMs,
14064
+ APP_CONFIG.animation.fps
14065
+ ) : generateTransitionFrames(
14015
14066
  aligned.from,
14016
14067
  aligned.to,
14017
14068
  durationMs,
@@ -14378,6 +14429,7 @@ class AvatarView {
14378
14429
  const steps = this.transitionKeyframes.length;
14379
14430
  const idx = Math.min(steps - 1, Math.floor(progress * (steps - 1)));
14380
14431
  const currentFrame = this.transitionKeyframes[idx];
14432
+ this.currentPlayingFrame = currentFrame;
14381
14433
  const wasmParams = convertProtoFlameToWasmParams(currentFrame);
14382
14434
  const avatarCore = AvatarSDK.getAvatarCore();
14383
14435
  if (avatarCore) {
@@ -14450,6 +14502,7 @@ class AvatarView {
14450
14502
  this.lastRenderedFrameIndex = frameIndex;
14451
14503
  if (frameIndex >= 0 && frameIndex < this.currentKeyframes.length) {
14452
14504
  this.lastRealtimeProtoFrame = this.currentKeyframes[frameIndex];
14505
+ this.currentPlayingFrame = this.lastRealtimeProtoFrame;
14453
14506
  }
14454
14507
  }
14455
14508
  setState(newState) {
@@ -14525,12 +14578,31 @@ class AvatarView {
14525
14578
  if (this.renderingState !== "transitioningToSpeaking") {
14526
14579
  return;
14527
14580
  }
14528
- const idleParams = await avatarCore.getCurrentFrameParams(this.idleCurrentFrameIndex, this.characterId);
14529
- const idleFrameProto = convertWasmParamsToProtoFlame(idleParams);
14581
+ let fromFrame = this.currentPlayingFrame;
14582
+ if (!fromFrame) {
14583
+ if (state === "idle") {
14584
+ const idleParams = await avatarCore.getCurrentFrameParams(this.idleCurrentFrameIndex, this.characterId);
14585
+ fromFrame = convertWasmParamsToProtoFlame(idleParams);
14586
+ } else if (state === "transitioningToIdle" || state === "transitioningToSpeaking") {
14587
+ if (this.transitionKeyframes.length > 0) {
14588
+ const elapsed = performance.now() - this.transitionStartTime;
14589
+ const currentTransitionDurationMs = state === "transitioningToSpeaking" ? this.startTransitionDurationMs : this.endTransitionDurationMs;
14590
+ const progress = Math.min(1, Math.max(0, elapsed / currentTransitionDurationMs));
14591
+ const steps = this.transitionKeyframes.length;
14592
+ const idx = Math.min(steps - 1, Math.floor(progress * (steps - 1)));
14593
+ fromFrame = this.transitionKeyframes[idx];
14594
+ }
14595
+ }
14596
+ }
14597
+ if (!fromFrame) {
14598
+ logger.warn("[AvatarView] Cannot get current playing frame, fallback to idle frame");
14599
+ const idleParams = await avatarCore.getCurrentFrameParams(this.idleCurrentFrameIndex, this.characterId);
14600
+ fromFrame = convertWasmParamsToProtoFlame(idleParams);
14601
+ }
14530
14602
  await this.getCachedIdleFirstFrame();
14531
14603
  const firstSpeaking = keyframes[0];
14532
14604
  const firstSpeakingWithPostProcessing = this.avatarController.applyPostProcessingToFlame(firstSpeaking);
14533
- this.transitionKeyframes = this.generateAndAlignTransitionFrames(idleFrameProto, firstSpeakingWithPostProcessing, this.startTransitionDurationMs);
14605
+ this.transitionKeyframes = this.generateAndAlignTransitionFrames(fromFrame, firstSpeakingWithPostProcessing, this.startTransitionDurationMs, true);
14534
14606
  this.transitionStartTime = performance.now();
14535
14607
  if (this.transitionKeyframes.length === 0) {
14536
14608
  this.setState("speaking");
@@ -14564,7 +14636,10 @@ class AvatarView {
14564
14636
  if (state === "idle" || state === "transitioningToIdle") {
14565
14637
  return;
14566
14638
  }
14567
- if (state !== "speaking") {
14639
+ if (state !== "speaking" && state !== "transitioningToSpeaking") {
14640
+ if (state === "transitioningToIdle") {
14641
+ return;
14642
+ }
14568
14643
  this.setState("idle");
14569
14644
  this.stopRealtimeAnimationLoop();
14570
14645
  this.startIdleAnimationLoop();
@@ -14578,12 +14653,33 @@ class AvatarView {
14578
14653
  return;
14579
14654
  }
14580
14655
  const avatarCore = AvatarSDK.getAvatarCore();
14581
- if (avatarCore && this.currentKeyframes.length > 0) {
14582
- const lastSpeakingRaw = this.lastRealtimeProtoFrame || this.currentKeyframes[Math.max(0, this.lastRenderedFrameIndex)];
14583
- const lastSpeaking = this.avatarController.applyPostProcessingToFlame(lastSpeakingRaw);
14656
+ if (avatarCore) {
14657
+ let fromFrame = this.currentPlayingFrame;
14658
+ if (!fromFrame) {
14659
+ if (this.lastRealtimeProtoFrame) {
14660
+ fromFrame = this.lastRealtimeProtoFrame;
14661
+ } else if (this.currentKeyframes.length > 0) {
14662
+ fromFrame = this.currentKeyframes[Math.max(0, this.lastRenderedFrameIndex)];
14663
+ }
14664
+ }
14665
+ if (!fromFrame && this.transitionKeyframes.length > 0) {
14666
+ const elapsed = performance.now() - this.transitionStartTime;
14667
+ const progress = Math.min(1, Math.max(0, elapsed / this.endTransitionDurationMs));
14668
+ const steps = this.transitionKeyframes.length;
14669
+ const idx = Math.min(steps - 1, Math.floor(progress * (steps - 1)));
14670
+ fromFrame = this.transitionKeyframes[idx];
14671
+ }
14672
+ if (!fromFrame) {
14673
+ logger.warn("[AvatarView] Cannot get current playing frame for transition to idle, fallback to idle frame");
14674
+ this.setState("idle");
14675
+ this.stopRealtimeAnimationLoop();
14676
+ this.startIdleAnimationLoop();
14677
+ return;
14678
+ }
14679
+ const fromFrameWithPostProcessing = this.avatarController.applyPostProcessingToFlame(fromFrame);
14584
14680
  const idleFirstProto = await this.getCachedIdleFirstFrame();
14585
14681
  if (idleFirstProto) {
14586
- this.transitionKeyframes = this.generateAndAlignTransitionFrames(lastSpeaking, idleFirstProto, this.endTransitionDurationMs);
14682
+ this.transitionKeyframes = this.generateAndAlignTransitionFrames(fromFrameWithPostProcessing, idleFirstProto, this.endTransitionDurationMs);
14587
14683
  this.transitionStartTime = performance.now();
14588
14684
  if (this.transitionKeyframes.length > 0 && this.renderingState === "transitioningToIdle") {
14589
14685
  if (APP_CONFIG.debug)
@@ -14621,6 +14717,7 @@ class AvatarView {
14621
14717
  this.setState("idle");
14622
14718
  this.cachedIdleFirstFrame = null;
14623
14719
  this.idleCurrentFrameIndex = 0;
14720
+ this.currentPlayingFrame = null;
14624
14721
  const avatarCore = AvatarSDK.getAvatarCore();
14625
14722
  if (avatarCore && this.characterHandle) {
14626
14723
  try {
@@ -14713,7 +14810,13 @@ class AvatarView {
14713
14810
  const to2 = transitionType === "start" ? aligned.to : aligned.from;
14714
14811
  const fps = APP_CONFIG.animation.fps;
14715
14812
  const durationMs = frameCount / fps * 1e3;
14716
- const transitionFrames = generateTransitionFrames(
14813
+ const useLinear = transitionType === "start";
14814
+ const transitionFrames = useLinear ? generateTransitionFramesLinear(
14815
+ from,
14816
+ to2,
14817
+ durationMs,
14818
+ fps
14819
+ ) : generateTransitionFrames(
14717
14820
  from,
14718
14821
  to2,
14719
14822
  durationMs,
@@ -14796,7 +14899,6 @@ export {
14796
14899
  Environment as E,
14797
14900
  LogLevel as L,
14798
14901
  ResourceType as R,
14799
- SPAvatarError as S,
14800
14902
  logEvent as a,
14801
14903
  Avatar as b,
14802
14904
  AvatarController as c,
@@ -14809,5 +14911,6 @@ export {
14809
14911
  AvatarState as j,
14810
14912
  ErrorCode as k,
14811
14913
  logger as l,
14812
- extractResourceUrls as m
14914
+ AvatarError as m,
14915
+ extractResourceUrls as n
14813
14916
  };
package/dist/index.js CHANGED
@@ -1,7 +1,8 @@
1
- import { b, c, f, d, j, g, C, i, D, E, k, h, L, R, S, m } from "./index-Bhjn1nq3.js";
1
+ import { b, c, m, f, d, j, g, C, i, D, E, k, h, L, R, n } from "./index-D0sXXlqd.js";
2
2
  export {
3
3
  b as Avatar,
4
4
  c as AvatarController,
5
+ m as AvatarError,
5
6
  f as AvatarManager,
6
7
  d as AvatarSDK,
7
8
  j as AvatarState,
@@ -14,6 +15,5 @@ export {
14
15
  h as LoadProgress,
15
16
  L as LogLevel,
16
17
  R as ResourceType,
17
- S as SPAvatarError,
18
- m as extractResourceUrls
18
+ n as extractResourceUrls
19
19
  };
@@ -74,7 +74,7 @@ export declare enum ErrorCode {
74
74
 
75
75
  failedToDownloadAvatarAssets = "failedToDownloadAvatarAssets"
76
76
  }
77
- export declare class SPAvatarError extends Error {
77
+ export declare class AvatarError extends Error {
78
78
  code?: (string | ErrorCode) | undefined;
79
79
  constructor(message: string, code?: (string | ErrorCode) | undefined);
80
80
  }
@@ -1,5 +1,9 @@
1
1
  import { Flame } from '../generated/driveningress/v1/driveningress';
2
2
 
3
+ export declare function linearLerp(from: Flame, to: Flame, progress: number): Flame;
4
+
5
+ export declare function generateTransitionFramesLinear(from: Flame, to: Flame, durationMs: number, fps?: number): Flame[];
6
+
3
7
  export declare function bezierLerp(from: Flame, to: Flame, progress: number): Flame;
4
8
 
5
9
  export declare function generateTransitionFrames(from: Flame, to: Flame, durationMs: number, fps?: number): Flame[];
@@ -1,2 +1,2 @@
1
- declare const _default: import('vite').UserConfig;
1
+ declare const _default: any;
2
2
  export default _default;
package/dist/vite.d.ts ADDED
@@ -0,0 +1,4 @@
1
+ import { Plugin } from 'vite';
2
+
3
+ export declare function avatarkitVitePlugin(): Plugin;
4
+ export default avatarkitVitePlugin;