@spatialwalk/avatarkit 1.0.0-beta.63 → 1.0.0-beta.65

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
@@ -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-CO9WTktN.js");
7627
+ const { StreamingAudioPlayer } = await import("./StreamingAudioPlayer-GEsQnXc9.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.63");
8964
+ __publicField(AvatarSDK, "_version", "1.0.0-beta.65");
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
  ));
@@ -10755,7 +10755,7 @@ class AvatarController {
10755
10755
  } catch (error) {
10756
10756
  const message = error instanceof Error ? error.message : String(error);
10757
10757
  logger.error("[AvatarController] Failed to initialize audio context:", message);
10758
- throw new SPAvatarError(
10758
+ throw new AvatarError(
10759
10759
  `Failed to initialize audio context: ${message}`,
10760
10760
  "AUDIO_CONTEXT_INIT_FAILED"
10761
10761
  );
@@ -10776,7 +10776,7 @@ class AvatarController {
10776
10776
  checkAudioContextInitialized() {
10777
10777
  var _a;
10778
10778
  if (!((_a = this.animationPlayer) == null ? void 0 : _a.isStreamingReady())) {
10779
- throw new SPAvatarError(
10779
+ throw new AvatarError(
10780
10780
  "Audio context not initialized. Call initializeAudioContext() in a user gesture context first.",
10781
10781
  "AUDIO_CONTEXT_NOT_INITIALIZED"
10782
10782
  );
@@ -10784,7 +10784,7 @@ class AvatarController {
10784
10784
  }
10785
10785
  async start() {
10786
10786
  if (!this.networkLayer) {
10787
- throw new SPAvatarError(
10787
+ throw new AvatarError(
10788
10788
  "Network layer not available. Use SDK mode.",
10789
10789
  "NETWORK_LAYER_NOT_AVAILABLE"
10790
10790
  );
@@ -10801,11 +10801,11 @@ class AvatarController {
10801
10801
  return null;
10802
10802
  }
10803
10803
  if (!this.networkLayer) {
10804
- (_b = this.onError) == null ? void 0 : _b.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"));
10805
10805
  return null;
10806
10806
  }
10807
10807
  if (!this.networkLayer.canSend()) {
10808
- (_c = this.onError) == null ? void 0 : _c.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"));
10809
10809
  logEvent("character_manager", "warning", {
10810
10810
  avatar_id: this.avatar.id,
10811
10811
  event: "send_not_connected"
@@ -10866,7 +10866,7 @@ class AvatarController {
10866
10866
  this.isAudioOnlyMode = true;
10867
10867
  }
10868
10868
  if (this.pendingAudioChunks.length === 0) {
10869
- throw new SPAvatarError("No audio chunks to play", "NO_AUDIO");
10869
+ throw new AvatarError("No audio chunks to play", "NO_AUDIO");
10870
10870
  }
10871
10871
  if (this.isAudioOnlyMode) {
10872
10872
  await this.startAudioOnlyPlayback();
@@ -11022,7 +11022,7 @@ class AvatarController {
11022
11022
  var _a;
11023
11023
  this.isStartingPlayback = false;
11024
11024
  logger.error("[AvatarController] Failed to auto-start playback:", error);
11025
- (_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"));
11026
11026
  });
11027
11027
  }
11028
11028
  }
@@ -11287,7 +11287,7 @@ class AvatarController {
11287
11287
  }
11288
11288
  try {
11289
11289
  if (!this.animationPlayer) {
11290
- throw new SPAvatarError("Animation player not initialized", "ANIMATION_PLAYER_NOT_INITIALIZED");
11290
+ throw new AvatarError("Animation player not initialized", "ANIMATION_PLAYER_NOT_INITIALIZED");
11291
11291
  }
11292
11292
  await this.animationPlayer.prepareStreamingPlayer(() => {
11293
11293
  var _a2, _b2;
@@ -11331,7 +11331,7 @@ class AvatarController {
11331
11331
  } catch (error) {
11332
11332
  const message = error instanceof Error ? error.message : String(error);
11333
11333
  logger.error("[AvatarController] Failed to start streaming playback:", message);
11334
- (_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"));
11335
11335
  this.isPlaying = false;
11336
11336
  this.isStartingPlayback = false;
11337
11337
  }
@@ -11510,7 +11510,7 @@ class AvatarController {
11510
11510
  async startAudioOnlyPlayback() {
11511
11511
  var _a, _b;
11512
11512
  if (!this.animationPlayer) {
11513
- throw new SPAvatarError("Animation player not initialized", "ANIMATION_PLAYER_NOT_INITIALIZED");
11513
+ throw new AvatarError("Animation player not initialized", "ANIMATION_PLAYER_NOT_INITIALIZED");
11514
11514
  }
11515
11515
  try {
11516
11516
  await this.animationPlayer.prepareStreamingPlayer(() => {
@@ -11547,7 +11547,7 @@ class AvatarController {
11547
11547
  } catch (error) {
11548
11548
  const message = error instanceof Error ? error.message : String(error);
11549
11549
  logger.error("[AvatarController] Failed to start audio-only playback:", message);
11550
- (_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"));
11551
11551
  this.isPlaying = false;
11552
11552
  this.isAudioOnlyMode = false;
11553
11553
  throw error;
@@ -12096,9 +12096,10 @@ class AvatarDownloader {
12096
12096
  const idleAnimationUrl = (_i2 = (_h = (_g = characterMeta.animations) == null ? void 0 : _g.frameIdle) == null ? void 0 : _h.resource) == null ? void 0 : _i2.remote;
12097
12097
  if (!shapeUrl || !pointCloudUrl) {
12098
12098
  const reason = "Missing required resources: shape or gsStandard (point cloud)";
12099
- logEvent("download_avatar_assets_failed", "error", {
12099
+ logEvent("character_load", "error", {
12100
12100
  avatar_id: characterMeta.characterId ?? "unknown",
12101
- description: reason
12101
+ description: "Character does not exist in target environment",
12102
+ reason
12102
12103
  });
12103
12104
  throw new Error(reason);
12104
12105
  }
@@ -12146,6 +12147,14 @@ class AvatarDownloader {
12146
12147
  return { key, success: true, size: arrayBuffer.byteLength };
12147
12148
  } catch (error) {
12148
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
+ });
12149
12158
  throw error;
12150
12159
  }
12151
12160
  logger.warn(`⚠️ Optional resource ${filename} failed to load:`, error);
@@ -12232,7 +12241,7 @@ class AvatarDownloader {
12232
12241
  avatar_id: extractedCharacterId,
12233
12242
  description: errorMessage
12234
12243
  });
12235
- error = new SPAvatarError(errorMessage, ErrorCode.avatarIDUnrecognized);
12244
+ error = new AvatarError(errorMessage, ErrorCode.avatarIDUnrecognized);
12236
12245
  } else {
12237
12246
  error = new Error(`HTTP ${response.status} ${response.statusText}`);
12238
12247
  }
@@ -12262,7 +12271,7 @@ class AvatarDownloader {
12262
12271
  return response;
12263
12272
  } catch (error) {
12264
12273
  logger.errorWithError("Failed to fetch character:", error);
12265
- if (error instanceof SPAvatarError) {
12274
+ if (error instanceof AvatarError) {
12266
12275
  logEvent("fetch_avatar_metadata_failed", "error", {
12267
12276
  avatar_id: characterId ?? "unknown",
12268
12277
  description: error.message
@@ -12388,10 +12397,6 @@ const _AvatarManager = class _AvatarManager {
12388
12397
  } catch (error) {
12389
12398
  const message = error instanceof Error ? error.message : String(error);
12390
12399
  logger.error("Failed to load avatar:", message);
12391
- logEvent("character_load", "error", {
12392
- avatar_id: id,
12393
- reason: message
12394
- });
12395
12400
  onProgress == null ? void 0 : onProgress({ type: LoadProgress.failed, error });
12396
12401
  throw error;
12397
12402
  }
@@ -13860,6 +13865,38 @@ function lerpArrays(from, to2, progress) {
13860
13865
  return result2;
13861
13866
  }
13862
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
+ }
13863
13900
  function createBezierEasing(x1, y1, x2, y2) {
13864
13901
  const cx = 3 * x1;
13865
13902
  const bx = 3 * (x2 - x1) - cx;
@@ -13963,6 +14000,7 @@ class AvatarView {
13963
14000
  __publicField(this, "endTransitionDurationMs", 1600);
13964
14001
  __publicField(this, "cachedIdleFirstFrame", null);
13965
14002
  __publicField(this, "idleCurrentFrameIndex", 0);
14003
+ __publicField(this, "currentPlayingFrame", null);
13966
14004
  __publicField(this, "characterHandle", null);
13967
14005
  __publicField(this, "characterId");
13968
14006
  __publicField(this, "isPureRenderingMode", false);
@@ -14017,9 +14055,14 @@ class AvatarView {
14017
14055
  toFixed.expression = ensureLen(toFixed.expression, exprLen);
14018
14056
  return { from: fromFixed, to: toFixed };
14019
14057
  }
14020
- generateAndAlignTransitionFrames(from, to2, durationMs) {
14058
+ generateAndAlignTransitionFrames(from, to2, durationMs, useLinearInterpolation = false) {
14021
14059
  const aligned = this.alignFlamePair(from, to2);
14022
- let keyframes = generateTransitionFrames(
14060
+ let keyframes = useLinearInterpolation ? generateTransitionFramesLinear(
14061
+ aligned.from,
14062
+ aligned.to,
14063
+ durationMs,
14064
+ APP_CONFIG.animation.fps
14065
+ ) : generateTransitionFrames(
14023
14066
  aligned.from,
14024
14067
  aligned.to,
14025
14068
  durationMs,
@@ -14386,6 +14429,7 @@ class AvatarView {
14386
14429
  const steps = this.transitionKeyframes.length;
14387
14430
  const idx = Math.min(steps - 1, Math.floor(progress * (steps - 1)));
14388
14431
  const currentFrame = this.transitionKeyframes[idx];
14432
+ this.currentPlayingFrame = currentFrame;
14389
14433
  const wasmParams = convertProtoFlameToWasmParams(currentFrame);
14390
14434
  const avatarCore = AvatarSDK.getAvatarCore();
14391
14435
  if (avatarCore) {
@@ -14458,6 +14502,7 @@ class AvatarView {
14458
14502
  this.lastRenderedFrameIndex = frameIndex;
14459
14503
  if (frameIndex >= 0 && frameIndex < this.currentKeyframes.length) {
14460
14504
  this.lastRealtimeProtoFrame = this.currentKeyframes[frameIndex];
14505
+ this.currentPlayingFrame = this.lastRealtimeProtoFrame;
14461
14506
  }
14462
14507
  }
14463
14508
  setState(newState) {
@@ -14475,6 +14520,7 @@ class AvatarView {
14475
14520
  this.lastRealtimeProtoFrame = null;
14476
14521
  this.transitionKeyframes = [];
14477
14522
  this.transitionStartTime = 0;
14523
+ this.currentPlayingFrame = null;
14478
14524
  }
14479
14525
  }
14480
14526
  get isRealtimePlaying() {
@@ -14533,13 +14579,33 @@ class AvatarView {
14533
14579
  if (this.renderingState !== "transitioningToSpeaking") {
14534
14580
  return;
14535
14581
  }
14536
- const idleParams = await avatarCore.getCurrentFrameParams(this.idleCurrentFrameIndex, this.characterId);
14537
- const idleFrameProto = convertWasmParamsToProtoFlame(idleParams);
14582
+ let fromFrame = this.currentPlayingFrame;
14583
+ if (!fromFrame) {
14584
+ if (state === "idle") {
14585
+ const idleParams = await avatarCore.getCurrentFrameParams(this.idleCurrentFrameIndex, this.characterId);
14586
+ fromFrame = convertWasmParamsToProtoFlame(idleParams);
14587
+ } else if (state === "transitioningToIdle" || state === "transitioningToSpeaking") {
14588
+ if (this.transitionKeyframes.length > 0) {
14589
+ const elapsed = performance.now() - this.transitionStartTime;
14590
+ const currentTransitionDurationMs = state === "transitioningToSpeaking" ? this.startTransitionDurationMs : this.endTransitionDurationMs;
14591
+ const progress = Math.min(1, Math.max(0, elapsed / currentTransitionDurationMs));
14592
+ const steps = this.transitionKeyframes.length;
14593
+ const idx = Math.min(steps - 1, Math.floor(progress * (steps - 1)));
14594
+ fromFrame = this.transitionKeyframes[idx];
14595
+ }
14596
+ }
14597
+ }
14598
+ if (!fromFrame) {
14599
+ logger.warn("[AvatarView] Cannot get current playing frame, fallback to idle frame");
14600
+ const idleParams = await avatarCore.getCurrentFrameParams(this.idleCurrentFrameIndex, this.characterId);
14601
+ fromFrame = convertWasmParamsToProtoFlame(idleParams);
14602
+ }
14538
14603
  await this.getCachedIdleFirstFrame();
14539
14604
  const firstSpeaking = keyframes[0];
14540
14605
  const firstSpeakingWithPostProcessing = this.avatarController.applyPostProcessingToFlame(firstSpeaking);
14541
- this.transitionKeyframes = this.generateAndAlignTransitionFrames(idleFrameProto, firstSpeakingWithPostProcessing, this.startTransitionDurationMs);
14606
+ this.transitionKeyframes = this.generateAndAlignTransitionFrames(fromFrame, firstSpeakingWithPostProcessing, this.startTransitionDurationMs, true);
14542
14607
  this.transitionStartTime = performance.now();
14608
+ this.currentPlayingFrame = null;
14543
14609
  if (this.transitionKeyframes.length === 0) {
14544
14610
  this.setState("speaking");
14545
14611
  this.avatarController.onTransitionComplete();
@@ -14572,7 +14638,10 @@ class AvatarView {
14572
14638
  if (state === "idle" || state === "transitioningToIdle") {
14573
14639
  return;
14574
14640
  }
14575
- if (state !== "speaking") {
14641
+ if (state !== "speaking" && state !== "transitioningToSpeaking") {
14642
+ if (state === "transitioningToIdle") {
14643
+ return;
14644
+ }
14576
14645
  this.setState("idle");
14577
14646
  this.stopRealtimeAnimationLoop();
14578
14647
  this.startIdleAnimationLoop();
@@ -14586,13 +14655,35 @@ class AvatarView {
14586
14655
  return;
14587
14656
  }
14588
14657
  const avatarCore = AvatarSDK.getAvatarCore();
14589
- if (avatarCore && this.currentKeyframes.length > 0) {
14590
- const lastSpeakingRaw = this.lastRealtimeProtoFrame || this.currentKeyframes[Math.max(0, this.lastRenderedFrameIndex)];
14591
- const lastSpeaking = this.avatarController.applyPostProcessingToFlame(lastSpeakingRaw);
14658
+ if (avatarCore) {
14659
+ let fromFrame = this.currentPlayingFrame;
14660
+ if (!fromFrame) {
14661
+ if (this.lastRealtimeProtoFrame) {
14662
+ fromFrame = this.lastRealtimeProtoFrame;
14663
+ } else if (this.currentKeyframes.length > 0) {
14664
+ fromFrame = this.currentKeyframes[Math.max(0, this.lastRenderedFrameIndex)];
14665
+ }
14666
+ }
14667
+ if (!fromFrame && this.transitionKeyframes.length > 0) {
14668
+ const elapsed = performance.now() - this.transitionStartTime;
14669
+ const progress = Math.min(1, Math.max(0, elapsed / this.endTransitionDurationMs));
14670
+ const steps = this.transitionKeyframes.length;
14671
+ const idx = Math.min(steps - 1, Math.floor(progress * (steps - 1)));
14672
+ fromFrame = this.transitionKeyframes[idx];
14673
+ }
14674
+ if (!fromFrame) {
14675
+ logger.warn("[AvatarView] Cannot get current playing frame for transition to idle, fallback to idle frame");
14676
+ this.setState("idle");
14677
+ this.stopRealtimeAnimationLoop();
14678
+ this.startIdleAnimationLoop();
14679
+ return;
14680
+ }
14681
+ const fromFrameWithPostProcessing = this.avatarController.applyPostProcessingToFlame(fromFrame);
14592
14682
  const idleFirstProto = await this.getCachedIdleFirstFrame();
14593
14683
  if (idleFirstProto) {
14594
- this.transitionKeyframes = this.generateAndAlignTransitionFrames(lastSpeaking, idleFirstProto, this.endTransitionDurationMs);
14684
+ this.transitionKeyframes = this.generateAndAlignTransitionFrames(fromFrameWithPostProcessing, idleFirstProto, this.endTransitionDurationMs);
14595
14685
  this.transitionStartTime = performance.now();
14686
+ this.currentPlayingFrame = null;
14596
14687
  if (this.transitionKeyframes.length > 0 && this.renderingState === "transitioningToIdle") {
14597
14688
  if (APP_CONFIG.debug)
14598
14689
  logger.log("[AvatarView] Return transition started:", this.transitionKeyframes.length, "frames");
@@ -14629,6 +14720,7 @@ class AvatarView {
14629
14720
  this.setState("idle");
14630
14721
  this.cachedIdleFirstFrame = null;
14631
14722
  this.idleCurrentFrameIndex = 0;
14723
+ this.currentPlayingFrame = null;
14632
14724
  const avatarCore = AvatarSDK.getAvatarCore();
14633
14725
  if (avatarCore && this.characterHandle) {
14634
14726
  try {
@@ -14721,7 +14813,13 @@ class AvatarView {
14721
14813
  const to2 = transitionType === "start" ? aligned.to : aligned.from;
14722
14814
  const fps = APP_CONFIG.animation.fps;
14723
14815
  const durationMs = frameCount / fps * 1e3;
14724
- const transitionFrames = generateTransitionFrames(
14816
+ const useLinear = transitionType === "start";
14817
+ const transitionFrames = useLinear ? generateTransitionFramesLinear(
14818
+ from,
14819
+ to2,
14820
+ durationMs,
14821
+ fps
14822
+ ) : generateTransitionFrames(
14725
14823
  from,
14726
14824
  to2,
14727
14825
  durationMs,
@@ -14804,7 +14902,6 @@ export {
14804
14902
  Environment as E,
14805
14903
  LogLevel as L,
14806
14904
  ResourceType as R,
14807
- SPAvatarError as S,
14808
14905
  logEvent as a,
14809
14906
  Avatar as b,
14810
14907
  AvatarController as c,
@@ -14817,5 +14914,6 @@ export {
14817
14914
  AvatarState as j,
14818
14915
  ErrorCode as k,
14819
14916
  logger as l,
14820
- extractResourceUrls as m
14917
+ AvatarError as m,
14918
+ extractResourceUrls as n
14821
14919
  };
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-C1md-jKJ.js";
1
+ import { b, c, m, f, d, j, g, C, i, D, E, k, h, L, R, n } from "./index-xTAIRBMF.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;
package/package.json CHANGED
@@ -1,7 +1,8 @@
1
1
  {
2
2
  "name": "@spatialwalk/avatarkit",
3
3
  "type": "module",
4
- "version": "1.0.0-beta.63",
4
+ "version": "1.0.0-beta.65",
5
+ "packageManager": "pnpm@10.18.2",
5
6
  "description": "SPAvatar SDK - 3D Gaussian Splatting Avatar Rendering SDK",
6
7
  "author": "SPAvatar Team",
7
8
  "license": "MIT",
@@ -18,6 +19,10 @@
18
19
  "types": "./dist/index.d.ts",
19
20
  "import": "./dist/index.js"
20
21
  },
22
+ "./vite": {
23
+ "types": "./vite.d.ts",
24
+ "import": "./vite.js"
25
+ },
21
26
  "./*": {
22
27
  "types": "./dist/*.d.ts",
23
28
  "import": "./dist/*.js"
@@ -29,10 +34,24 @@
29
34
  "files": [
30
35
  "README.md",
31
36
  "CHANGELOG.md",
32
- "dist"
37
+ "dist",
38
+ "vite.js",
39
+ "vite.d.ts"
33
40
  ],
41
+ "scripts": {
42
+ "build": "SDK_BUILD=true vite build --mode library && npm run build:vite-plugin",
43
+ "build:vite-plugin": "tsc vite.ts --outDir . --module esnext --target es2020 --moduleResolution bundler --esModuleInterop --skipLibCheck --declaration --declarationMap",
44
+ "dev": "vite build --mode library --watch",
45
+ "clean": "rm -rf dist",
46
+ "typecheck": "tsc --noEmit",
47
+ "test": "cd tests && pnpm test",
48
+ "test:watch": "cd tests && pnpm run test:watch",
49
+ "test:e2e": "cd tests && pnpm run test:e2e",
50
+ "test:perf": "cd tests && pnpm run test:perf"
51
+ },
34
52
  "peerDependencies": {
35
- "@webgpu/types": "*"
53
+ "@webgpu/types": "*",
54
+ "vite": "^5.0.0"
36
55
  },
37
56
  "dependencies": {
38
57
  "@bufbuild/protobuf": "^2.10.0",
@@ -47,15 +66,5 @@
47
66
  "typescript": "^5.0.0",
48
67
  "vite": "^5.0.0",
49
68
  "vite-plugin-dts": "^4.5.4"
50
- },
51
- "scripts": {
52
- "build": "SDK_BUILD=true vite build --mode library",
53
- "dev": "vite build --mode library --watch",
54
- "clean": "rm -rf dist",
55
- "typecheck": "tsc --noEmit",
56
- "test": "cd tests && pnpm test",
57
- "test:watch": "cd tests && pnpm run test:watch",
58
- "test:e2e": "cd tests && pnpm run test:e2e",
59
- "test:perf": "cd tests && pnpm run test:perf"
60
69
  }
61
- }
70
+ }
package/vite.d.ts ADDED
@@ -0,0 +1,20 @@
1
+ import type { Plugin } from 'vite';
2
+ /**
3
+ * Vite plugin for @spatialwalk/avatarkit
4
+ * Automatically handles WASM file configuration for development and production builds
5
+ *
6
+ * @example
7
+ * ```ts
8
+ * import { defineConfig } from 'vite'
9
+ * import { avatarkitVitePlugin } from '@spatialwalk/avatarkit/vite'
10
+ *
11
+ * export default defineConfig({
12
+ * plugins: [
13
+ * avatarkitVitePlugin()
14
+ * ]
15
+ * })
16
+ * ```
17
+ */
18
+ export declare function avatarkitVitePlugin(): Plugin;
19
+ export default avatarkitVitePlugin;
20
+ //# sourceMappingURL=vite.d.ts.map