saltfish 0.2.70 → 0.2.72

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.
@@ -1419,6 +1419,12 @@ const saltfishStore = createStore()(
1419
1419
  state.backendPlaylists = playlists;
1420
1420
  });
1421
1421
  },
1422
+ // Set admin flag from backend
1423
+ setIsAdmin: (isAdmin) => {
1424
+ set2((state) => {
1425
+ state.isAdmin = isAdmin;
1426
+ });
1427
+ },
1422
1428
  completePlaylist: () => {
1423
1429
  set2((state) => {
1424
1430
  state.currentState = transitionState({ type: "COMPLETE_PLAYLIST" });
@@ -1767,7 +1773,7 @@ __publicField(ErrorHandler, "DEFAULT_OPTIONS", {
1767
1773
  shouldTriggerEvent: false,
1768
1774
  shouldDestroy: false
1769
1775
  });
1770
- const VERSION = "0.2.70";
1776
+ const VERSION = "0.2.71";
1771
1777
  class PlayerInitializationService {
1772
1778
  constructor(managers) {
1773
1779
  __publicField(this, "managers");
@@ -1834,8 +1840,8 @@ class PlayerInitializationService {
1834
1840
  const store = useSaltfishStore.getState();
1835
1841
  store.initialize(updatedConfig);
1836
1842
  this.managers.analyticsManager.initialize(config, this.managers.sessionManager.getSessionId());
1837
- if (data.isAdmin) {
1838
- store.isAdmin = true;
1843
+ if (data.isAdmin && store.setIsAdmin) {
1844
+ store.setIsAdmin(true);
1839
1845
  log("[PlayerInitializationService.initialize] Admin token detected");
1840
1846
  }
1841
1847
  if (data.playlists && Array.isArray(data.playlists) && store.setBackendPlaylists) {
@@ -3757,7 +3763,7 @@ const componentsControlsCss = "/* \n * Controls component styles for the Saltfis
3757
3763
  const componentsTranscriptCss = "/* \n * Transcript component styles for the Saltfish Playlist Player\n * Following BEM naming convention\n */\n\n/* Transcript container */\n.sf-transcript {\n position: absolute;\n bottom: 40px; /* Above the progress bar */\n left: 0;\n right: 0;\n max-height: 200px;\n background: transparent;\n margin: 0 var(--sf-spacing-md);\n overflow: hidden;\n z-index: var(--sf-z-index-overlay);\n opacity: 0;\n transform: translateY(20px);\n transition: all 0.3s ease-out;\n pointer-events: none;\n}\n\n/* Visible state */\n.sf-transcript--visible {\n opacity: 1;\n transform: translateY(0);\n pointer-events: auto;\n}\n\n/* Transcript content */\n.sf-transcript__content {\n max-height: 180px;\n overflow: visible;\n padding: 0;\n}\n\n/* Webkit scrollbar styling */\n.sf-transcript__content::-webkit-scrollbar {\n width: 4px;\n}\n\n.sf-transcript__content::-webkit-scrollbar-track {\n background: transparent;\n}\n\n.sf-transcript__content::-webkit-scrollbar-thumb {\n background: rgba(255, 255, 255, 0.3);\n border-radius: 2px;\n}\n\n.sf-transcript__content::-webkit-scrollbar-thumb:hover {\n background: rgba(255, 255, 255, 0.5);\n}\n\n/* Word-by-word display container */\n.sf-transcript__word-container {\n display: flex;\n justify-content: center;\n align-items: center;\n gap: 0; /* Remove flex gap - spacing handled by margin on preview word */\n min-height: 60px;\n padding: 0;\n}\n\n/* Individual word styling */\n.sf-transcript__word {\n color: rgba(255, 255, 255, 0.9);\n font-size: var(--sf-font-size-lg);\n font-weight: 600;\n line-height: 1.2;\n transition: all 0.4s cubic-bezier(0.4, 0, 0.2, 1);\n transform: translateY(0);\n filter: blur(0px);\n opacity: 1;\n text-shadow: 0 1px 3px rgba(0, 0, 0, 0.5);\n}\n\n/* Active word (currently speaking) */\n.sf-transcript__word--active {\n opacity: 1;\n filter: blur(0px);\n transform: translateY(0) scale(1);\n color: rgba(255, 255, 255, 1);\n font-weight: 600;\n}\n\n/* Preview word (clean, unblurred when shown) */\n.sf-transcript__word--preview {\n opacity: 0.8;\n filter: blur(0px);\n transform: translateY(0) scale(0.98);\n color: rgba(255, 255, 255, 0.8);\n font-weight: 400;\n margin-left: 0.2em; /* Minimal spacing - just enough for a natural gap */\n}\n\n/* Legacy segment styling (kept for compatibility) */\n.sf-transcript__segment {\n color: rgba(255, 255, 255);\n font-size: var(--sf-font-size-sm);\n line-height: 1.4;\n padding: var(--sf-spacing-xs) 0;\n cursor: pointer;\n transition: all 0.2s ease;\n padding-left: var(--sf-spacing-xs);\n padding-right: var(--sf-spacing-xs);\n}\n\n/* CC button styling removed - now handled via icon toggling */\n\n/* Mobile responsive styles */\n@media (max-width: 768px) {\n .sf-transcript {\n bottom: 50px; /* More space on mobile */\n margin: 0 var(--sf-spacing-sm);\n max-height: 150px; /* Smaller on mobile */\n }\n \n .sf-transcript__content {\n max-height: 130px;\n padding: var(--sf-spacing-sm);\n }\n \n .sf-transcript__word-container {\n min-height: 50px;\n padding: var(--sf-spacing-sm);\n gap: var(--sf-spacing-xs);\n }\n \n .sf-transcript__word {\n font-size: var(--sf-font-size-md);\n font-weight: 500;\n }\n \n .sf-transcript__segment {\n font-size: var(--sf-font-size-xs);\n padding: var(--sf-spacing-xs) var(--sf-spacing-sm);\n }\n}\n\n/* Touch device optimizations */\n@media (pointer: coarse) {\n .sf-transcript__segment {\n padding: var(--sf-spacing-sm) var(--sf-spacing-xs);\n min-height: 44px; /* Larger touch target */\n display: flex;\n align-items: center;\n }\n}\n\n/* Hide transcript in minimized state */\n.sf-player--minimized .sf-transcript {\n display: none !important;\n}\n\n/* Hide transcript in autoplayBlocked and idleMode states */\n.sf-player--autoplayBlocked .sf-transcript,\n.sf-player--idleMode .sf-transcript {\n display: none !important;\n}\n\n/* Animation for transcript appearance */\n@keyframes transcriptFadeIn {\n from {\n opacity: 0;\n transform: translateY(20px);\n }\n to {\n opacity: 1;\n transform: translateY(0);\n }\n}\n\n@keyframes transcriptFadeOut {\n from {\n opacity: 1;\n transform: translateY(0);\n }\n to {\n opacity: 0;\n transform: translateY(20px);\n }\n}";
3758
3764
  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}";
3759
3765
  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}";
3760
- 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 #ff7614;\n background: rgba(255, 118, 20, 0.1);\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";
3766
+ 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 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";
3761
3767
  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} ";
3762
3768
  const getPlayerStyles = () => `
3763
3769
  ${baseResetCss}
@@ -6146,8 +6152,8 @@ class CursorManager {
6146
6152
  pointer-events: none;
6147
6153
  display: none;
6148
6154
  z-index: 9999998;
6149
- border: 2px solid #ff7614;
6150
- background: rgba(255, 118, 20, 0.1);
6155
+ border: 2px solid var(--sf-selection-color, #ff7614);
6156
+ background: var(--sf-selection-bg-color, rgba(255, 118, 20, 0.1));
6151
6157
  left: var(--sf-selection-left, 0);
6152
6158
  top: var(--sf-selection-top, 0);
6153
6159
  width: var(--sf-selection-width, 0);
@@ -6906,7 +6912,7 @@ class CursorManager {
6906
6912
  document.documentElement.style.removeProperty("--sf-cursor-y");
6907
6913
  }
6908
6914
  /**
6909
- * Sets the color of the cursor SVG
6915
+ * Sets the color of the cursor SVG and selection highlight
6910
6916
  * @param color - The color to set (hex, rgb, etc)
6911
6917
  */
6912
6918
  setColor(color) {
@@ -6926,6 +6932,32 @@ class CursorManager {
6926
6932
  } else {
6927
6933
  console.warn("[CursorManager.setColor] No cursor element");
6928
6934
  }
6935
+ if (this.selectionElement) {
6936
+ this.selectionElement.style.setProperty("--sf-selection-color", color);
6937
+ const rgbaBackground = this.colorToRgba(color, 0.1);
6938
+ this.selectionElement.style.setProperty("--sf-selection-bg-color", rgbaBackground);
6939
+ }
6940
+ }
6941
+ /**
6942
+ * Converts a color string (hex, rgb, rgba) to rgba format with specified opacity
6943
+ * @param color - The color to convert
6944
+ * @param opacity - The opacity value (0-1)
6945
+ * @returns rgba color string
6946
+ */
6947
+ colorToRgba(color, opacity) {
6948
+ const tempElement = document.createElement("div");
6949
+ tempElement.style.color = color;
6950
+ document.body.appendChild(tempElement);
6951
+ const computedColor = window.getComputedStyle(tempElement).color;
6952
+ document.body.removeChild(tempElement);
6953
+ const rgbMatch = computedColor.match(/rgba?\((\d+),\s*(\d+),\s*(\d+)/);
6954
+ if (rgbMatch) {
6955
+ const r = rgbMatch[1];
6956
+ const g = rgbMatch[2];
6957
+ const b = rgbMatch[3];
6958
+ return `rgba(${r}, ${g}, ${b}, ${opacity})`;
6959
+ }
6960
+ return color;
6929
6961
  }
6930
6962
  }
6931
6963
  class InteractionManager {
@@ -10716,7 +10748,7 @@ const SaltfishPlayer$1 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.de
10716
10748
  __proto__: null,
10717
10749
  SaltfishPlayer
10718
10750
  }, Symbol.toStringTag, { value: "Module" }));
10719
- const version = "0.2.70";
10751
+ const version = "0.2.72";
10720
10752
  const packageJson = {
10721
10753
  version
10722
10754
  };