saltfish 0.3.31 → 0.3.34

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.
@@ -903,8 +903,8 @@ const playerStateMachineConfig = {
903
903
  "TRANSITION_TO_STEP": { target: "playing" },
904
904
  "MINIMIZE": { target: "minimized" }
905
905
  },
906
- entry: ["logStateEntry", "startMutedLoopedVideo", "hideVideoControls", "showPlayButton", "enablePlayButtonProminent"],
907
- exit: ["disablePlayButtonProminent", "showVideoControls"]
906
+ entry: ["logStateEntry", "enterCompactMode", "startMutedLoopedVideo", "hideVideoControls", "showPlayButton", "enablePlayButtonProminent"],
907
+ exit: ["disablePlayButtonProminent", "showVideoControls", "exitCompactMode"]
908
908
  },
909
909
  "idleMode": {
910
910
  on: {
@@ -913,7 +913,7 @@ const playerStateMachineConfig = {
913
913
  "MINIMIZE": { target: "minimized" }
914
914
  },
915
915
  entry: ["logStateEntry", "startIdleModeVideo", "hideVideoControls", "showPlayButton", "enablePlayButtonProminent"],
916
- exit: ["disablePlayButtonProminent", "showVideoControls"]
916
+ exit: ["disablePlayButtonProminent", "showVideoControls", "exitCompactMode"]
917
917
  },
918
918
  "error": {
919
919
  on: {
@@ -2732,7 +2732,7 @@ class PlaylistOrchestrator {
2732
2732
  { component: "PlaylistOrchestrator", method: "startPlaylist", playlistId }
2733
2733
  );
2734
2734
  }
2735
- const isPlaylistRunning = store.manifest && (store.currentState === "playing" || store.currentState === "paused" || store.currentState === "loading" || store.currentState === "waitingForInteraction" || store.currentState === "autoplayBlocked" || store.currentState === "minimized");
2735
+ const isPlaylistRunning = store.manifest && (store.currentState === "playing" || store.currentState === "paused" || store.currentState === "loading" || store.currentState === "waitingForInteraction" || store.currentState === "autoplayBlocked" || store.currentState === "minimized" || store.currentState === "idleMode");
2736
2736
  if (isPlaylistRunning) {
2737
2737
  log("PlaylistOrchestrator: Starting new playlist while another is running, resetting state");
2738
2738
  if (this.managerOrchestrator) {
@@ -2843,15 +2843,15 @@ class PlaylistOrchestrator {
2843
2843
  this.managers.cursorManager.setColor(updatedStore.manifest.cursorColor);
2844
2844
  }
2845
2845
  const isTriggeredAutomatically = finalOptions._triggeredByTriggerManager === true;
2846
- const isFirstStep = updatedStore.currentStepId === ((_e = updatedStore.manifest.steps[0]) == null ? void 0 : _e.id);
2847
- if (updatedStore.manifest.compactFirstStep && isFirstStep && isTriggeredAutomatically) {
2848
- const playerElement = this.managers.uiManager.getPlayerElement();
2849
- playerElement == null ? void 0 : playerElement.classList.add("sf-player--compact");
2850
- if (updatedStore.manifest.compactLabel) {
2851
- this.managers.uiManager.showCompactLabel(updatedStore.manifest.compactLabel);
2852
- }
2853
- }
2854
2846
  if (updatedStore.manifest.idleMode && isTriggeredAutomatically) {
2847
+ const isFirstStep = updatedStore.currentStepId === ((_e = updatedStore.manifest.steps[0]) == null ? void 0 : _e.id);
2848
+ if (updatedStore.manifest.compactFirstStep && isFirstStep) {
2849
+ const playerElement = this.managers.uiManager.getPlayerElement();
2850
+ playerElement == null ? void 0 : playerElement.classList.add("sf-player--compact");
2851
+ if (updatedStore.manifest.compactLabel) {
2852
+ this.managers.uiManager.showCompactLabel(updatedStore.manifest.compactLabel);
2853
+ }
2854
+ }
2855
2855
  store.setIdleMode();
2856
2856
  } else {
2857
2857
  store.play();
@@ -2912,6 +2912,9 @@ class StateMachineActionHandler {
2912
2912
  constructor(managers) {
2913
2913
  __publicField(this, "managers");
2914
2914
  __publicField(this, "destroyCallback", null);
2915
+ __publicField(this, "cursorAnimationListener", null);
2916
+ __publicField(this, "cursorAnimationVideoElement", null);
2917
+ __publicField(this, "cursorAnimationStepId", null);
2915
2918
  this.managers = managers;
2916
2919
  }
2917
2920
  /**
@@ -2971,6 +2974,12 @@ class StateMachineActionHandler {
2971
2974
  disablePlayButtonProminent: () => {
2972
2975
  this.handleDisablePlayButtonProminent();
2973
2976
  },
2977
+ enterCompactMode: () => {
2978
+ this.handleEnterCompactMode();
2979
+ },
2980
+ exitCompactMode: () => {
2981
+ this.handleExitCompactMode();
2982
+ },
2974
2983
  triggerPlaylistDismissed: () => {
2975
2984
  this.handleTriggerPlaylistDismissed();
2976
2985
  },
@@ -3044,6 +3053,87 @@ class StateMachineActionHandler {
3044
3053
  return null;
3045
3054
  }
3046
3055
  destroy() {
3056
+ this.cleanupCursorAnimationListener();
3057
+ }
3058
+ /**
3059
+ * Cleans up the active cursor animation time listener
3060
+ * @param stepId - Optional step ID to verify we're cleaning up the right listener
3061
+ */
3062
+ cleanupCursorAnimationListener(stepId) {
3063
+ if (stepId && this.cursorAnimationStepId && stepId !== this.cursorAnimationStepId) {
3064
+ log(`StateMachineActionHandler: Skipping cleanup - stepId mismatch (requested: ${stepId}, current: ${this.cursorAnimationStepId})`);
3065
+ return;
3066
+ }
3067
+ if (this.cursorAnimationListener && this.cursorAnimationVideoElement) {
3068
+ this.cursorAnimationVideoElement.removeEventListener("timeupdate", this.cursorAnimationListener);
3069
+ this.cursorAnimationListener = null;
3070
+ this.cursorAnimationVideoElement = null;
3071
+ this.cursorAnimationStepId = null;
3072
+ }
3073
+ }
3074
+ /**
3075
+ * Schedules a cursor animation to run either immediately or at a specific video time
3076
+ * Note: Works for both video and audio-only steps (audio files use the same video element)
3077
+ */
3078
+ scheduleCursorAnimation(animation, stepId) {
3079
+ this.cleanupCursorAnimationListener();
3080
+ let showAtSeconds = animation.showAtSeconds ?? 0;
3081
+ if (typeof showAtSeconds !== "number" || !isFinite(showAtSeconds)) {
3082
+ showAtSeconds = 0;
3083
+ }
3084
+ if (showAtSeconds < 0) {
3085
+ showAtSeconds = 0;
3086
+ }
3087
+ if (showAtSeconds <= 0) {
3088
+ this.managers.cursorManager.animate(animation);
3089
+ } else {
3090
+ const videoElement = this.managers.videoManager.getVideoElement();
3091
+ if (!videoElement) {
3092
+ return;
3093
+ }
3094
+ let animationTriggered = false;
3095
+ let warningLogged = false;
3096
+ const timeUpdateHandler = () => {
3097
+ if (animationTriggered) {
3098
+ return;
3099
+ }
3100
+ const currentTime = videoElement.currentTime;
3101
+ const duration = videoElement.duration;
3102
+ if (duration && !isNaN(duration) && showAtSeconds > duration && !warningLogged) {
3103
+ warningLogged = true;
3104
+ animationTriggered = true;
3105
+ this.cleanupCursorAnimationListener();
3106
+ this.managers.cursorManager.animate(animation);
3107
+ return;
3108
+ }
3109
+ if (currentTime >= showAtSeconds) {
3110
+ animationTriggered = true;
3111
+ this.cleanupCursorAnimationListener();
3112
+ this.managers.cursorManager.animate(animation);
3113
+ }
3114
+ };
3115
+ const endedHandler = () => {
3116
+ if (!animationTriggered) {
3117
+ const store = getSaltfishStore();
3118
+ if (store.currentStepId !== stepId) {
3119
+ log(`StateMachineActionHandler: Video ended but step changed (was ${stepId}, now ${store.currentStepId}). Not triggering cursor animation.`);
3120
+ this.cleanupCursorAnimationListener(stepId);
3121
+ return;
3122
+ }
3123
+ videoElement.duration;
3124
+ animationTriggered = true;
3125
+ this.managers.cursorManager.animate(animation);
3126
+ this.cleanupCursorAnimationListener();
3127
+ }
3128
+ };
3129
+ this.cursorAnimationListener = () => {
3130
+ timeUpdateHandler();
3131
+ };
3132
+ this.cursorAnimationVideoElement = videoElement;
3133
+ this.cursorAnimationStepId = stepId;
3134
+ videoElement.addEventListener("timeupdate", this.cursorAnimationListener);
3135
+ videoElement.addEventListener("ended", endedHandler, { once: true });
3136
+ }
3047
3137
  }
3048
3138
  /**
3049
3139
  * Validates URL requirement for a specific step with retry logic
@@ -3110,9 +3200,6 @@ class StateMachineActionHandler {
3110
3200
  }
3111
3201
  }
3112
3202
  this.managers.uiManager.updatePosition();
3113
- const playerElement = this.managers.uiManager.getPlayerElement();
3114
- playerElement == null ? void 0 : playerElement.classList.remove("sf-player--compact");
3115
- this.managers.uiManager.hideCompactLabel();
3116
3203
  this.managers.uiManager.showPlayer();
3117
3204
  const videoUrl = this.getVideoUrl(currentStep);
3118
3205
  const isAudioFallback = this.isUsingAudioFallback(currentStep);
@@ -3134,17 +3221,7 @@ class StateMachineActionHandler {
3134
3221
  if (currentStep.buttons) {
3135
3222
  this.managers.interactionManager.createButtons(currentStep.buttons);
3136
3223
  }
3137
- log(`StateMachineActionHandler: Processing cursor animations for step ${currentStep.id}`);
3138
3224
  log(`StateMachineActionHandler: Step has cursor animations: ${!!(currentStep.cursorAnimations && currentStep.cursorAnimations.length > 0)}`);
3139
- if (currentStep.cursorAnimations && currentStep.cursorAnimations.length > 0) {
3140
- log(`StateMachineActionHandler: Setting cursor visibility to true for step ${currentStep.id}`);
3141
- this.managers.cursorManager.setShouldShowCursor(true);
3142
- log(`StateMachineActionHandler: Starting cursor animation for step ${currentStep.id} with target: ${currentStep.cursorAnimations[0].targetSelector || "no target"}`);
3143
- this.managers.cursorManager.animate(currentStep.cursorAnimations[0]);
3144
- } else {
3145
- log(`StateMachineActionHandler: Setting cursor visibility to false for step ${currentStep.id} - step has no cursor animations`);
3146
- this.managers.cursorManager.setShouldShowCursor(false);
3147
- }
3148
3225
  const hasSpecialTransitions = currentStep.buttons && currentStep.buttons.length > 0 || currentStep.transitions.some(
3149
3226
  (t) => t.type === "dom-click" || t.type === "url-path" || t.type === "dom-element-visible"
3150
3227
  );
@@ -3233,6 +3310,14 @@ class StateMachineActionHandler {
3233
3310
  log("StateMachineActionHandler: Video loaded successfully, playing");
3234
3311
  this.managers.uiManager.hideError();
3235
3312
  loadTranscriptForStep();
3313
+ if (currentStep.cursorAnimations && currentStep.cursorAnimations.length > 0) {
3314
+ log(`StateMachineActionHandler: Setting cursor visibility and scheduling animation for step ${currentStep.id}`);
3315
+ this.managers.cursorManager.setShouldShowCursor(true);
3316
+ this.scheduleCursorAnimation(currentStep.cursorAnimations[0], currentStep.id);
3317
+ } else {
3318
+ log(`StateMachineActionHandler: Setting cursor visibility to false for step ${currentStep.id} - step has no cursor animations`);
3319
+ this.managers.cursorManager.setShouldShowCursor(false);
3320
+ }
3236
3321
  if (isAudioFallback) {
3237
3322
  this.managers.videoManager.startAudioVisualization();
3238
3323
  } else {
@@ -3378,6 +3463,24 @@ class StateMachineActionHandler {
3378
3463
  handleDisablePlayButtonProminent() {
3379
3464
  this.managers.uiManager.disablePlayButtonProminent();
3380
3465
  }
3466
+ handleEnterCompactMode() {
3467
+ var _a, _b, _c, _d;
3468
+ const store = getSaltfishStore();
3469
+ const isFirstStep = store.manifest && store.currentStepId === ((_a = store.manifest.steps[0]) == null ? void 0 : _a.id);
3470
+ const shouldBeCompact = isFirstStep && ((_b = store.manifest) == null ? void 0 : _b.compactFirstStep) && ((_c = store.playlistOptions) == null ? void 0 : _c._triggeredByTriggerManager);
3471
+ if (shouldBeCompact) {
3472
+ const playerElement = this.managers.uiManager.getPlayerElement();
3473
+ playerElement == null ? void 0 : playerElement.classList.add("sf-player--compact");
3474
+ if ((_d = store.manifest) == null ? void 0 : _d.compactLabel) {
3475
+ this.managers.uiManager.showCompactLabel(store.manifest.compactLabel);
3476
+ }
3477
+ }
3478
+ }
3479
+ handleExitCompactMode() {
3480
+ const playerElement = this.managers.uiManager.getPlayerElement();
3481
+ playerElement == null ? void 0 : playerElement.classList.remove("sf-player--compact");
3482
+ this.managers.uiManager.hideCompactLabel();
3483
+ }
3381
3484
  handleTriggerPlaylistDismissed() {
3382
3485
  const store = getSaltfishStore();
3383
3486
  if (store.manifest && this.managers.eventManager) {
@@ -3655,6 +3758,9 @@ const _ManagerOrchestrator = class _ManagerOrchestrator {
3655
3758
  // Store updater unsubscribe functions
3656
3759
  __publicField(this, "uiUpdaterUnsubscribe", null);
3657
3760
  __publicField(this, "eventUpdaterUnsubscribe", null);
3761
+ __publicField(this, "cursorAnimationListener", null);
3762
+ __publicField(this, "cursorAnimationVideoElement", null);
3763
+ __publicField(this, "cursorAnimationStepId", null);
3658
3764
  // Initialization state
3659
3765
  __publicField(this, "isInitialized", false);
3660
3766
  this.managers = managers;
@@ -3699,6 +3805,86 @@ const _ManagerOrchestrator = class _ManagerOrchestrator {
3699
3805
  stepTimeoutUnsubscribe();
3700
3806
  };
3701
3807
  }
3808
+ /**
3809
+ * Cleans up the active cursor animation time listener
3810
+ * @param stepId - Optional step ID to verify we're cleaning up the right listener
3811
+ */
3812
+ cleanupCursorAnimationListener(stepId) {
3813
+ if (stepId && this.cursorAnimationStepId && stepId !== this.cursorAnimationStepId) {
3814
+ log(`ManagerOrchestrator: Skipping cleanup - stepId mismatch (requested: ${stepId}, current: ${this.cursorAnimationStepId})`);
3815
+ return;
3816
+ }
3817
+ if (this.cursorAnimationListener && this.cursorAnimationVideoElement) {
3818
+ this.cursorAnimationVideoElement.removeEventListener("timeupdate", this.cursorAnimationListener);
3819
+ this.cursorAnimationListener = null;
3820
+ this.cursorAnimationVideoElement = null;
3821
+ this.cursorAnimationStepId = null;
3822
+ }
3823
+ }
3824
+ /**
3825
+ * Schedules a cursor animation to run either immediately or at a specific video time
3826
+ * Note: Works for both video and audio-only steps (audio files use the same video element)
3827
+ */
3828
+ scheduleCursorAnimation(animation, stepId) {
3829
+ this.cleanupCursorAnimationListener();
3830
+ let showAtSeconds = animation.showAtSeconds ?? 0;
3831
+ if (typeof showAtSeconds !== "number" || !isFinite(showAtSeconds)) {
3832
+ showAtSeconds = 0;
3833
+ }
3834
+ if (showAtSeconds < 0) {
3835
+ showAtSeconds = 0;
3836
+ }
3837
+ if (showAtSeconds <= 0) {
3838
+ this.managers.cursorManager.animate(animation);
3839
+ } else {
3840
+ const videoElement = this.managers.videoManager.getVideoElement();
3841
+ if (!videoElement) {
3842
+ return;
3843
+ }
3844
+ let animationTriggered = false;
3845
+ let warningLogged = false;
3846
+ const timeUpdateHandler = () => {
3847
+ if (animationTriggered) {
3848
+ return;
3849
+ }
3850
+ const currentTime = videoElement.currentTime;
3851
+ const duration = videoElement.duration;
3852
+ if (duration && !isNaN(duration) && showAtSeconds > duration && !warningLogged) {
3853
+ warningLogged = true;
3854
+ animationTriggered = true;
3855
+ this.cleanupCursorAnimationListener();
3856
+ this.managers.cursorManager.animate(animation);
3857
+ return;
3858
+ }
3859
+ if (currentTime >= showAtSeconds) {
3860
+ animationTriggered = true;
3861
+ this.cleanupCursorAnimationListener();
3862
+ this.managers.cursorManager.animate(animation);
3863
+ }
3864
+ };
3865
+ const endedHandler = () => {
3866
+ if (!animationTriggered) {
3867
+ const store = getSaltfishStore();
3868
+ if (store.currentStepId !== stepId) {
3869
+ log(`ManagerOrchestrator: Video ended but step changed (was ${stepId}, now ${store.currentStepId}). Not triggering cursor animation.`);
3870
+ this.cleanupCursorAnimationListener(stepId);
3871
+ return;
3872
+ }
3873
+ videoElement.duration;
3874
+ animationTriggered = true;
3875
+ this.managers.cursorManager.animate(animation);
3876
+ this.cleanupCursorAnimationListener();
3877
+ }
3878
+ };
3879
+ this.cursorAnimationListener = () => {
3880
+ timeUpdateHandler();
3881
+ };
3882
+ this.cursorAnimationVideoElement = videoElement;
3883
+ this.cursorAnimationStepId = stepId;
3884
+ videoElement.addEventListener("timeupdate", this.cursorAnimationListener);
3885
+ videoElement.addEventListener("ended", endedHandler, { once: true });
3886
+ }
3887
+ }
3702
3888
  /**
3703
3889
  * Handle store state changes
3704
3890
  */
@@ -3730,7 +3916,7 @@ const _ManagerOrchestrator = class _ManagerOrchestrator {
3730
3916
  const currentStep = manifest == null ? void 0 : manifest.steps.find((step) => step.id === store.currentStepId);
3731
3917
  if ((currentStep == null ? void 0 : currentStep.cursorAnimations) && currentStep.cursorAnimations.length > 0) {
3732
3918
  this.managers.cursorManager.setShouldShowCursor(true);
3733
- this.managers.cursorManager.animate(currentStep.cursorAnimations[0]);
3919
+ this.scheduleCursorAnimation(currentStep.cursorAnimations[0], store.currentStepId || "unknown");
3734
3920
  }
3735
3921
  } else if (store.currentState === "completed" || store.currentState === "closing") {
3736
3922
  this.cleanupPlaylist();
@@ -3749,6 +3935,7 @@ const _ManagerOrchestrator = class _ManagerOrchestrator {
3749
3935
  */
3750
3936
  cleanupCurrentPlaylist() {
3751
3937
  try {
3938
+ this.cleanupCursorAnimationListener();
3752
3939
  if (this.eventUpdaterUnsubscribe) {
3753
3940
  this.eventUpdaterUnsubscribe();
3754
3941
  this.eventUpdaterUnsubscribe = null;
@@ -3840,6 +4027,7 @@ const _ManagerOrchestrator = class _ManagerOrchestrator {
3840
4027
  isMinimized: store.isMinimized,
3841
4028
  manifestId: (_a = store.manifest) == null ? void 0 : _a.id
3842
4029
  });
4030
+ this.cleanupCursorAnimationListener();
3843
4031
  if (this.uiUpdaterUnsubscribe) {
3844
4032
  this.uiUpdaterUnsubscribe();
3845
4033
  this.uiUpdaterUnsubscribe = null;
@@ -11584,7 +11772,7 @@ const SaltfishPlayer$1 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.de
11584
11772
  __proto__: null,
11585
11773
  SaltfishPlayer
11586
11774
  }, Symbol.toStringTag, { value: "Module" }));
11587
- const version = "0.3.31";
11775
+ const version = "0.3.34";
11588
11776
  const packageJson = {
11589
11777
  version
11590
11778
  };