saltfish 0.3.18 → 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.
@@ -2843,6 +2843,9 @@ class PlaylistOrchestrator {
2843
2843
  if (updatedStore.manifest.compactFirstStep && isFirstStep && isTriggeredAutomatically) {
2844
2844
  const playerElement = this.managers.uiManager.getPlayerElement();
2845
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
+ }
2846
2849
  }
2847
2850
  if (updatedStore.manifest.idleMode && isTriggeredAutomatically) {
2848
2851
  store.setIdleMode();
@@ -3105,6 +3108,7 @@ class StateMachineActionHandler {
3105
3108
  this.managers.uiManager.updatePosition();
3106
3109
  const playerElement = this.managers.uiManager.getPlayerElement();
3107
3110
  playerElement == null ? void 0 : playerElement.classList.remove("sf-player--compact");
3111
+ this.managers.uiManager.hideCompactLabel();
3108
3112
  this.managers.uiManager.showPlayer();
3109
3113
  const videoUrl = this.getVideoUrl(currentStep);
3110
3114
  const isAudioFallback = this.isUsingAudioFallback(currentStep);
@@ -3905,6 +3909,7 @@ const componentsTranscriptCss = "/* \n * Transcript component styles for the Sal
3905
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}";
3906
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}";
3907
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";
3908
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} ";
3909
3914
  const getPlayerStyles = () => `
3910
3915
  ${baseResetCss}
@@ -3917,6 +3922,7 @@ const getPlayerStyles = () => `
3917
3922
  ${componentsErrorCss}
3918
3923
  ${componentsLoadingCss}
3919
3924
  ${componentsCursorCss}
3925
+ ${componentsCompactLabelCss}
3920
3926
 
3921
3927
  ${animationsTransitionsCss}
3922
3928
  `;
@@ -10565,6 +10571,7 @@ class UIManager {
10565
10571
  __publicField(this, "playPauseButton", null);
10566
10572
  __publicField(this, "errorDisplay", null);
10567
10573
  __publicField(this, "loadingSpinner", null);
10574
+ __publicField(this, "compactLabel", null);
10568
10575
  // Button management properties (from ButtonManager)
10569
10576
  __publicField(this, "playbackButtonsVisible", false);
10570
10577
  __publicField(this, "playButton", null);
@@ -10952,6 +10959,73 @@ class UIManager {
10952
10959
  }
10953
10960
  });
10954
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
+ }
10955
11029
  /**
10956
11030
  * Resets the UI manager to initial state for reuse
10957
11031
  */
@@ -10976,6 +11050,7 @@ class UIManager {
10976
11050
  this.loadingSpinner.destroy();
10977
11051
  this.loadingSpinner = null;
10978
11052
  }
11053
+ this.hideCompactLabel();
10979
11054
  if (this.playerElement && this.playerElement.parentNode) {
10980
11055
  this.playerElement.parentNode.removeChild(this.playerElement);
10981
11056
  }
@@ -11378,7 +11453,7 @@ const SaltfishPlayer$1 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.de
11378
11453
  __proto__: null,
11379
11454
  SaltfishPlayer
11380
11455
  }, Symbol.toStringTag, { value: "Module" }));
11381
- const version = "0.3.18";
11456
+ const version = "0.3.19";
11382
11457
  const packageJson = {
11383
11458
  version
11384
11459
  };