saltfish 0.3.17 → 0.3.19

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.
@@ -924,7 +924,8 @@ const playerStateMachineConfig = {
924
924
  },
925
925
  "AUTOPLAY_FALLBACK": { target: "autoplayBlocked" }
926
926
  },
927
- entry: ["logStateEntry", "handleError", "showPlayButton"]
927
+ entry: ["logStateEntry", "handleError", "showPlayButton"],
928
+ exit: ["hideError"]
928
929
  },
929
930
  "completedWaitingForInteraction": {
930
931
  on: {
@@ -2842,6 +2843,9 @@ class PlaylistOrchestrator {
2842
2843
  if (updatedStore.manifest.compactFirstStep && isFirstStep && isTriggeredAutomatically) {
2843
2844
  const playerElement = this.managers.uiManager.getPlayerElement();
2844
2845
  playerElement == null ? void 0 : playerElement.classList.add("sf-player--compact");
2846
+ if (updatedStore.manifest.compactLabel) {
2847
+ this.managers.uiManager.showCompactLabel(updatedStore.manifest.compactLabel);
2848
+ }
2845
2849
  }
2846
2850
  if (updatedStore.manifest.idleMode && isTriggeredAutomatically) {
2847
2851
  store.setIdleMode();
@@ -2936,6 +2940,9 @@ class StateMachineActionHandler {
2936
2940
  handleError: (context) => {
2937
2941
  this.handleError(context);
2938
2942
  },
2943
+ hideError: () => {
2944
+ this.handleHideError();
2945
+ },
2939
2946
  showLoadingState: () => {
2940
2947
  this.handleShowLoadingState();
2941
2948
  },
@@ -3101,6 +3108,7 @@ class StateMachineActionHandler {
3101
3108
  this.managers.uiManager.updatePosition();
3102
3109
  const playerElement = this.managers.uiManager.getPlayerElement();
3103
3110
  playerElement == null ? void 0 : playerElement.classList.remove("sf-player--compact");
3111
+ this.managers.uiManager.hideCompactLabel();
3104
3112
  this.managers.uiManager.showPlayer();
3105
3113
  const videoUrl = this.getVideoUrl(currentStep);
3106
3114
  const isAudioFallback = this.isUsingAudioFallback(currentStep);
@@ -3219,6 +3227,7 @@ class StateMachineActionHandler {
3219
3227
  };
3220
3228
  this.managers.videoManager.loadVideo(videoUrl).then(() => {
3221
3229
  log("StateMachineActionHandler: Video loaded successfully, playing");
3230
+ this.managers.uiManager.hideError();
3222
3231
  loadTranscriptForStep();
3223
3232
  if (isAudioFallback) {
3224
3233
  this.managers.videoManager.startAudioVisualization();
@@ -3286,6 +3295,7 @@ class StateMachineActionHandler {
3286
3295
  this.managers.videoManager.hideAudioFallbackOverlay();
3287
3296
  }
3288
3297
  this.managers.videoManager.loadVideo(videoUrl).then(() => {
3298
+ this.managers.uiManager.hideError();
3289
3299
  if (isAudioFallback) {
3290
3300
  this.managers.videoManager.startAudioVisualization();
3291
3301
  }
@@ -3335,6 +3345,9 @@ class StateMachineActionHandler {
3335
3345
  });
3336
3346
  }
3337
3347
  }
3348
+ handleHideError() {
3349
+ this.managers.uiManager.hideError();
3350
+ }
3338
3351
  handleShowLoadingState() {
3339
3352
  this.managers.uiManager.showLoading("Loading...");
3340
3353
  }
@@ -3896,6 +3909,7 @@ const componentsTranscriptCss = "/* \n * Transcript component styles for the Sal
3896
3909
  const componentsErrorCss = "/* \n * Error Display component styles for the Saltfish playlist Player\n * Clean and subtle full-widget error overlay\n */\n\n/* Error display overlay - covers full widget */\n.sf-error-display {\n position: absolute;\n top: 0;\n left: 0;\n right: 0;\n bottom: 0;\n background-color: rgba(0, 0, 0, 0.75);\n backdrop-filter: blur(6px);\n -webkit-backdrop-filter: blur(6px);\n display: flex;\n align-items: center;\n justify-content: center;\n z-index: calc(var(--sf-z-index-controls) + 30);\n border-radius: var(--sf-border-radius-lg);\n opacity: 0;\n transition: opacity var(--sf-transition-slow);\n pointer-events: auto;\n}\n\n/* Error display visible state */\n.sf-error-display--visible {\n opacity: 1;\n}\n\n/* Error content container */\n.sf-error-display__content {\n background-color: rgba(20, 20, 20, 0.9);\n border-radius: var(--sf-border-radius-md);\n padding: var(--sf-spacing-lg) var(--sf-spacing-xl);\n backdrop-filter: blur(10px);\n -webkit-backdrop-filter: blur(10px);\n border: 1px solid rgba(255, 255, 255, 0.08);\n transform: translateY(8px);\n transition: transform var(--sf-transition-slow);\n max-width: 85%;\n text-align: center;\n}\n\n/* Error content visible animation */\n.sf-error-display--visible .sf-error-display__content {\n transform: translateY(0);\n}\n\n/* Error message */\n.sf-error-display__message {\n color: rgba(255, 255, 255, 0.95);\n font-size: var(--sf-font-size-md);\n line-height: 1.5;\n margin: 0;\n text-align: center;\n font-weight: 500;\n}\n\n/* Mobile responsive adjustments */\n@media (max-width: 768px) {\n .sf-error-display__content {\n padding: var(--sf-spacing-md) var(--sf-spacing-lg);\n max-width: 90%;\n }\n\n .sf-error-display__message {\n font-size: var(--sf-font-size-sm);\n }\n}\n\n/* Minimized player state adjustments */\n.sf-player--minimized .sf-error-display__content {\n padding: var(--sf-spacing-sm) var(--sf-spacing-md);\n max-width: 80%;\n}\n\n.sf-player--minimized .sf-error-display__message {\n font-size: var(--sf-font-size-sm);\n font-weight: 400;\n}\n\n/* High contrast mode support */\n@media (prefers-contrast: high) {\n .sf-error-display__content {\n background-color: rgba(0, 0, 0, 0.95);\n border: 2px solid rgba(255, 255, 255, 0.3);\n }\n\n .sf-error-display__message {\n color: rgba(255, 255, 255, 0.95);\n }\n}\n\n/* Reduced motion support */\n@media (prefers-reduced-motion: reduce) {\n .sf-error-display,\n .sf-error-display__content {\n transition: none;\n }\n}";
3897
3910
  const componentsLoadingCss = "/**\n * Loading spinner styles\n */\n\n.sf-loading-spinner {\n position: absolute;\n top: 0;\n left: 0;\n right: 0;\n bottom: 0;\n display: flex;\n align-items: center;\n justify-content: center;\n background-color: rgba(255, 255, 255, 0.95);\n backdrop-filter: blur(2px);\n z-index: 100;\n border-radius: 12px;\n opacity: 1 !important; /* Always visible when shown, overrides parent opacity */\n}\n\n.sf-loading-spinner__content {\n display: flex;\n flex-direction: column;\n align-items: center;\n gap: 12px;\n padding: 24px;\n}\n\n.sf-loading-spinner__icon {\n display: flex;\n align-items: center;\n justify-content: center;\n color: #000000;\n opacity: 0.9;\n}\n\n.sf-loading-spinner__icon svg {\n width: 60px;\n height: 60px;\n /* Remove the rotation animation since we now have internal SVG animation */\n}\n\n.sf-loading-spinner__text {\n color: #333333;\n font-size: 14px;\n font-weight: 500;\n text-align: center;\n opacity: 0.8;\n letter-spacing: 0.5px;\n}\n\n/* CSS keyframe animation removed - using SVG animateTransform instead */\n\n/* Responsive adjustments */\n@media (max-width: 480px) {\n .sf-loading-spinner__content {\n padding: 20px;\n gap: 10px;\n }\n \n .sf-loading-spinner__icon svg {\n width: 50px;\n height: 50px;\n }\n \n .sf-loading-spinner__text {\n font-size: 13px;\n }\n}";
3898
3911
  const componentsCursorCss = "/*\n * Cursor animation component styles for the Saltfish playlist Player\n * CSP-compliant: All styles use CSS classes and CSS custom properties instead of inline styles\n */\n\n/* Base cursor element */\n.sf-cursor {\n position: fixed;\n top: 0;\n left: 0;\n width: 36px;\n height: 36px;\n z-index: 9999999;\n pointer-events: none;\n display: none;\n will-change: transform;\n transform: var(--sf-cursor-transform, translate(0, 0));\n opacity: var(--sf-cursor-opacity, 1);\n}\n\n.sf-cursor--visible {\n display: block;\n}\n\n/* Selection element */\n.sf-selection {\n position: fixed;\n pointer-events: none;\n display: none;\n z-index: 9999998;\n border: 2px solid var(--sf-selection-color, #ff7614);\n background: var(--sf-selection-bg-color, rgba(255, 118, 20, 0.1));\n border-radius: 4px;\n left: var(--sf-selection-left, 0);\n top: var(--sf-selection-top, 0);\n width: var(--sf-selection-width, 0);\n height: var(--sf-selection-height, 0);\n}\n\n.sf-selection--visible {\n display: block;\n}\n\n/* Flashlight overlay */\n.sf-flashlight-overlay {\n position: fixed;\n top: 0;\n left: 0;\n width: 100vw;\n height: 100vh;\n pointer-events: none;\n z-index: 999997;\n display: none;\n background: var(--sf-flashlight-bg, radial-gradient(circle 150px at 50% 50%, transparent 0%, rgba(0, 0, 0, 0.4) 100%));\n clip-path: var(--sf-flashlight-clip, none);\n}\n\n.sf-flashlight-overlay--visible {\n display: block;\n}\n";
3912
+ const componentsCompactLabelCss = "/**\n * Compact Label Styles\n * Label that appears next to the player when in compact first step mode\n * Position adapts based on player placement (left/right)\n */\n\n.sf-compact-label {\n position: absolute;\n top: 50%;\n transform: translateY(-50%);\n\n /* Typography */\n font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;\n font-size: 14px;\n font-weight: 500;\n line-height: 1.4;\n color: #ffffff;\n\n /* Visual styling */\n background: rgba(0, 0, 0, 0.85);\n backdrop-filter: blur(10px);\n padding: 10px 16px;\n border-radius: 8px;\n box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3),\n 0 0 0 1px rgba(255, 255, 255, 0.1);\n\n /* Ensure it doesn't wrap */\n white-space: nowrap;\n\n /* Layering */\n z-index: 10;\n\n /* Pointer events - will be enabled via JS when clickable */\n pointer-events: none;\n user-select: none;\n\n /* Smooth transitions for hover effects */\n transition: transform 0.2s cubic-bezier(0.25, 0.8, 0.25, 1),\n box-shadow 0.2s cubic-bezier(0.25, 0.8, 0.25, 1),\n background 0.2s cubic-bezier(0.25, 0.8, 0.25, 1);\n}\n\n/* Position to the LEFT of player (when player is on the right side) */\n.sf-compact-label--left {\n right: calc(100% + 16px); /* 16px spacing from player edge */\n animation: sf-compact-label-enter-from-left 0.4s cubic-bezier(0.25, 0.8, 0.25, 1) forwards;\n}\n\n/* Position to the RIGHT of player (when player is on the left side) */\n.sf-compact-label--right {\n left: calc(100% + 16px); /* 16px spacing from player edge */\n animation: sf-compact-label-enter-from-right 0.4s cubic-bezier(0.25, 0.8, 0.25, 1) forwards;\n}\n\n/* Entrance animation from LEFT - slide in from left with fade */\n@keyframes sf-compact-label-enter-from-left {\n 0% {\n opacity: 0;\n transform: translateY(-50%) translateX(-20px);\n }\n 100% {\n opacity: 1;\n transform: translateY(-50%) translateX(0);\n }\n}\n\n/* Entrance animation from RIGHT - slide in from right with fade */\n@keyframes sf-compact-label-enter-from-right {\n 0% {\n opacity: 0;\n transform: translateY(-50%) translateX(20px);\n }\n 100% {\n opacity: 1;\n transform: translateY(-50%) translateX(0);\n }\n}\n\n/* Hover effect - only applies when label is clickable (pointer-events: auto) */\n.sf-compact-label--left:hover,\n.sf-compact-label--right:hover {\n background: rgba(0, 0, 0, 0.95);\n box-shadow: 0 6px 20px rgba(0, 0, 0, 0.4),\n 0 0 0 1px rgba(255, 255, 255, 0.2),\n 0 0 25px rgba(255, 255, 255, 0.08);\n}\n\n.sf-compact-label--left:hover {\n transform: translateY(-50%) translateX(-2px);\n}\n\n.sf-compact-label--right:hover {\n transform: translateY(-50%) translateX(2px);\n}\n\n/* Active/click state */\n.sf-compact-label--left:active,\n.sf-compact-label--right:active {\n transform: translateY(-50%) scale(0.98);\n}\n\n/* Mobile adjustments */\n@media (max-width: 768px) {\n .sf-compact-label {\n font-size: 12px;\n padding: 8px 12px;\n }\n\n .sf-compact-label--left {\n right: calc(100% + 12px); /* Slightly less spacing on mobile */\n }\n\n .sf-compact-label--right {\n left: calc(100% + 12px); /* Slightly less spacing on mobile */\n }\n}\n\n/* Very small screens - position below instead of to the side */\n@media (max-width: 480px) {\n .sf-compact-label--left,\n .sf-compact-label--right {\n left: 50%;\n right: auto;\n top: calc(100% + 12px);\n transform: translateX(-50%);\n animation: sf-compact-label-enter-below 0.4s cubic-bezier(0.25, 0.8, 0.25, 1) forwards;\n }\n\n @keyframes sf-compact-label-enter-below {\n 0% {\n opacity: 0;\n transform: translateX(-50%) translateY(-10px);\n }\n 100% {\n opacity: 1;\n transform: translateX(-50%) translateY(0);\n }\n }\n}\n";
3899
3913
  const animationsTransitionsCss = "/* \n * Transitions and animations for Saltfish playlist Player\n */\n\n/* Fade in animation */\n@keyframes sf-fade-in {\n from { opacity: 0; }\n to { opacity: 1; }\n}\n\n.sf-fade-in {\n animation: sf-fade-in 0.3s ease-in-out forwards;\n}\n\n/* Slide in from bottom animation */\n@keyframes sf-slide-in-bottom {\n from { transform: translateY(100%); opacity: 0; }\n to { transform: translateY(0); opacity: 1; }\n}\n\n.sf-slide-in-bottom {\n animation: sf-slide-in-bottom 0.3s cubic-bezier(0.25, 0.8, 0.25, 1) forwards;\n}\n\n/* Slide in from right animation */\n@keyframes sf-slide-in-right {\n from { transform: translateX(100%); opacity: 0; }\n to { transform: translateX(0); opacity: 1; }\n}\n\n.sf-slide-in-right {\n animation: sf-slide-in-right 0.3s cubic-bezier(0.25, 0.8, 0.25, 1) forwards;\n}\n\n/* Scale in animation */\n@keyframes sf-scale-in {\n from { transform: scale(0.8); opacity: 0; }\n to { transform: scale(1); opacity: 1; }\n}\n\n.sf-scale-in {\n animation: sf-scale-in 0.3s cubic-bezier(0.25, 0.8, 0.25, 1) forwards;\n}\n\n/* Scale out animation */\n@keyframes sf-scale-out {\n from { transform: scale(1); opacity: 1; }\n to { transform: scale(0.8); opacity: 0; }\n}\n\n.sf-scale-out {\n animation: sf-scale-out 0.3s cubic-bezier(0.25, 0.8, 0.25, 1) forwards;\n} ";
3900
3914
  const getPlayerStyles = () => `
3901
3915
  ${baseResetCss}
@@ -3908,6 +3922,7 @@ const getPlayerStyles = () => `
3908
3922
  ${componentsErrorCss}
3909
3923
  ${componentsLoadingCss}
3910
3924
  ${componentsCursorCss}
3925
+ ${componentsCompactLabelCss}
3911
3926
 
3912
3927
  ${animationsTransitionsCss}
3913
3928
  `;
@@ -5497,7 +5512,10 @@ class VideoManager {
5497
5512
  */
5498
5513
  __publicField(this, "handleVideoError", (event) => {
5499
5514
  const video = event.target;
5500
- console.error("VideoManager: Video error", video.error);
5515
+ const activeVideo = this.getActiveVideo();
5516
+ if (video === activeVideo) {
5517
+ console.error("VideoManager: Video error", video.error);
5518
+ }
5501
5519
  });
5502
5520
  /**
5503
5521
  * Handles video ended event for automatic completion policy
@@ -10553,6 +10571,7 @@ class UIManager {
10553
10571
  __publicField(this, "playPauseButton", null);
10554
10572
  __publicField(this, "errorDisplay", null);
10555
10573
  __publicField(this, "loadingSpinner", null);
10574
+ __publicField(this, "compactLabel", null);
10556
10575
  // Button management properties (from ButtonManager)
10557
10576
  __publicField(this, "playbackButtonsVisible", false);
10558
10577
  __publicField(this, "playButton", null);
@@ -10940,6 +10959,73 @@ class UIManager {
10940
10959
  }
10941
10960
  });
10942
10961
  }
10962
+ /**
10963
+ * Shows the compact label next to the player with the provided text
10964
+ */
10965
+ showCompactLabel(labelText) {
10966
+ var _a;
10967
+ if (!this.playerRoot || !labelText) {
10968
+ return;
10969
+ }
10970
+ this.hideCompactLabel();
10971
+ const store = useSaltfishStore.getState();
10972
+ let positionToUse = ((_a = store.playlistOptions) == null ? void 0 : _a.position) || "bottom-right";
10973
+ if (store.currentStepId && store.manifest) {
10974
+ const currentStep = store.manifest.steps.find((step) => step.id === store.currentStepId);
10975
+ if (currentStep == null ? void 0 : currentStep.position) {
10976
+ positionToUse = currentStep.position;
10977
+ }
10978
+ }
10979
+ const isPlayerOnRight = positionToUse === "bottom-right";
10980
+ const labelPositionClass = isPlayerOnRight ? "sf-compact-label--left" : "sf-compact-label--right";
10981
+ this.compactLabel = document.createElement("div");
10982
+ this.compactLabel.className = `sf-compact-label ${labelPositionClass}`;
10983
+ this.compactLabel.textContent = labelText;
10984
+ this.compactLabel.style.cursor = "pointer";
10985
+ this.compactLabel.style.pointerEvents = "auto";
10986
+ this.compactLabel.addEventListener("click", (e) => {
10987
+ var _a2;
10988
+ e.stopPropagation();
10989
+ e.preventDefault();
10990
+ const currentStore = useSaltfishStore.getState();
10991
+ if (currentStore.currentState === "autoplayBlocked" || currentStore.currentState === "idleMode") {
10992
+ currentStore.currentState === "autoplayBlocked" ? "autoplay fallback" : "idle";
10993
+ if (this.videoManager) {
10994
+ this.videoManager.markUserInteraction();
10995
+ this.videoManager.setMuted(false);
10996
+ const videoElement = this.videoManager.getVideoElement();
10997
+ if (videoElement) {
10998
+ videoElement.loop = false;
10999
+ videoElement.currentTime = 0;
11000
+ }
11001
+ } else {
11002
+ const videoElement = (_a2 = this.playerElement) == null ? void 0 : _a2.querySelector(".sf-video-container__video");
11003
+ if (videoElement) {
11004
+ videoElement.muted = false;
11005
+ videoElement.loop = false;
11006
+ videoElement.currentTime = 0;
11007
+ }
11008
+ }
11009
+ } else {
11010
+ if (this.videoManager) {
11011
+ this.videoManager.markUserInteraction();
11012
+ }
11013
+ }
11014
+ if (currentStore.currentState === "paused" || currentStore.currentState === "waitingForInteraction" || currentStore.currentState === "completedWaitingForInteraction" || currentStore.currentState === "autoplayBlocked" || currentStore.currentState === "idleMode" || currentStore.currentState === "error") {
11015
+ currentStore.play();
11016
+ }
11017
+ });
11018
+ this.playerRoot.appendChild(this.compactLabel);
11019
+ }
11020
+ /**
11021
+ * Hides and removes the compact label
11022
+ */
11023
+ hideCompactLabel() {
11024
+ if (this.compactLabel && this.compactLabel.parentNode) {
11025
+ this.compactLabel.parentNode.removeChild(this.compactLabel);
11026
+ this.compactLabel = null;
11027
+ }
11028
+ }
10943
11029
  /**
10944
11030
  * Resets the UI manager to initial state for reuse
10945
11031
  */
@@ -10964,6 +11050,7 @@ class UIManager {
10964
11050
  this.loadingSpinner.destroy();
10965
11051
  this.loadingSpinner = null;
10966
11052
  }
11053
+ this.hideCompactLabel();
10967
11054
  if (this.playerElement && this.playerElement.parentNode) {
10968
11055
  this.playerElement.parentNode.removeChild(this.playerElement);
10969
11056
  }
@@ -11366,7 +11453,7 @@ const SaltfishPlayer$1 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.de
11366
11453
  __proto__: null,
11367
11454
  SaltfishPlayer
11368
11455
  }, Symbol.toStringTag, { value: "Module" }));
11369
- const version = "0.3.17";
11456
+ const version = "0.3.19";
11370
11457
  const packageJson = {
11371
11458
  version
11372
11459
  };