stormcloud-video-player 0.7.6 → 0.7.8

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.
package/lib/index.d.cts CHANGED
@@ -263,6 +263,7 @@ declare class StormcloudVideoPlayer {
263
263
  width: number;
264
264
  height: number;
265
265
  } | null;
266
+ getCurrentHlsSegmentDurationMs(): number | null;
266
267
  get videoElement(): HTMLVideoElement;
267
268
  resize(): void;
268
269
  destroy(): void;
package/lib/index.d.ts CHANGED
@@ -263,6 +263,7 @@ declare class StormcloudVideoPlayer {
263
263
  width: number;
264
264
  height: number;
265
265
  } | null;
266
+ getCurrentHlsSegmentDurationMs(): number | null;
266
267
  get videoElement(): HTMLVideoElement;
267
268
  resize(): void;
268
269
  destroy(): void;
package/lib/index.js CHANGED
@@ -759,28 +759,9 @@ function createAdStormPlayer(contentVideo, options) {
759
759
  adVideoElement.removeAttribute("src");
760
760
  adVideoElement.load();
761
761
  }
762
- function buildVastUrl(durationSeconds, metadata) {
763
- var baseUrl = "https://adstorm.co/api-adstorm-dev/adstorm/vast/".concat(licenseKey, "/pod");
764
- var defaultMetadata = {
765
- video: {
766
- codec: "h264",
767
- width: contentVideo.videoWidth || 1280,
768
- height: contentVideo.videoHeight || 720,
769
- fps: 29.97,
770
- bitrate: 5e3,
771
- profile: "high",
772
- pix_fmt: "yuv420p",
773
- has_b_frames: 0
774
- },
775
- audio: {
776
- codec: "aac",
777
- sample_rate: 48e3,
778
- bitrate: 128
779
- }
780
- };
781
- var finalMetadata = metadata || defaultMetadata;
782
- var metadataStr = encodeURIComponent(JSON.stringify(finalMetadata));
783
- return "".concat(baseUrl, "?duration=").concat(Math.ceil(durationSeconds), "&metadata=").concat(metadataStr);
762
+ function buildVastUrl(durationSeconds) {
763
+ var baseUrl = "https://adstorm.co/api-adstorm-dev/adstorm/nab/vast/pod";
764
+ return "".concat(baseUrl, "?duration=").concat(Math.ceil(durationSeconds));
784
765
  }
785
766
  function parseVastXml(xmlString) {
786
767
  var ads = [];
@@ -5926,6 +5907,50 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
5926
5907
  return min;
5927
5908
  }
5928
5909
  },
5910
+ {
5911
+ key: "getCurrentHlsSegmentDurationMs",
5912
+ value: function getCurrentHlsSegmentDurationMs() {
5913
+ var fallbackMs = 4e3;
5914
+ if (this.nativeHlsMode) {
5915
+ return fallbackMs;
5916
+ }
5917
+ var hls = this.hls;
5918
+ if (!hls) return null;
5919
+ var levelCandidates = [
5920
+ hls.currentLevel,
5921
+ hls.nextLoadLevel,
5922
+ hls.loadLevel
5923
+ ];
5924
+ var _iteratorNormalCompletion = true, _didIteratorError = false, _iteratorError = undefined;
5925
+ try {
5926
+ for(var _iterator = levelCandidates[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true){
5927
+ var levelIndex = _step.value;
5928
+ var _hls_levels_levelIndex, _hls_levels;
5929
+ if (typeof levelIndex !== "number" || levelIndex < 0) continue;
5930
+ var details = (_hls_levels = hls.levels) === null || _hls_levels === void 0 ? void 0 : (_hls_levels_levelIndex = _hls_levels[levelIndex]) === null || _hls_levels_levelIndex === void 0 ? void 0 : _hls_levels_levelIndex.details;
5931
+ if (!details) continue;
5932
+ var targetDurationSec = typeof details.partTarget === "number" && details.partTarget > 0 ? details.partTarget : typeof details.targetduration === "number" && details.targetduration > 0 ? details.targetduration : void 0;
5933
+ if (targetDurationSec !== void 0) {
5934
+ return Math.max(800, Math.floor(targetDurationSec * 1e3));
5935
+ }
5936
+ }
5937
+ } catch (err) {
5938
+ _didIteratorError = true;
5939
+ _iteratorError = err;
5940
+ } finally{
5941
+ try {
5942
+ if (!_iteratorNormalCompletion && _iterator.return != null) {
5943
+ _iterator.return();
5944
+ }
5945
+ } finally{
5946
+ if (_didIteratorError) {
5947
+ throw _iteratorError;
5948
+ }
5949
+ }
5950
+ }
5951
+ return fallbackMs;
5952
+ }
5953
+ },
5929
5954
  {
5930
5955
  key: "videoElement",
5931
5956
  get: function get() {
@@ -7102,7 +7127,12 @@ var CRITICAL_PROPS = [
7102
7127
  var CONTROLS_HIDE_DELAY = 3e3;
7103
7128
  var DEFAULT_PLAYER_ASPECT_RATIO = 16 / 9;
7104
7129
  var DEBUG_PANEL_MARKER_LIMIT = 12;
7130
+ var AI_CONTEXT_FALLBACK_POLL_MS = 4e3;
7131
+ var AI_CONTEXT_MIN_POLL_MS = 800;
7132
+ var PANEL_BASE_RIGHT_OFFSET = 10;
7105
7133
  var StormcloudVideoPlayerComponent = React2.memo(function(props) {
7134
+ var _ref;
7135
+ var _aiLiveContext_context, _aiLiveContext_context_keywords, _aiLiveContext_context1;
7106
7136
  var src = props.src, autoplay = props.autoplay, muted = props.muted, lowLatencyMode = props.lowLatencyMode, allowNativeHls = props.allowNativeHls, driftToleranceMs = props.driftToleranceMs, immediateManifestAds = props.immediateManifestAds, debugAdTiming = props.debugAdTiming, showCustomControls = props.showCustomControls, hideLoadingIndicator = props.hideLoadingIndicator, onVolumeToggle = props.onVolumeToggle, onFullscreenToggle = props.onFullscreenToggle, onControlClick = props.onControlClick, onReady = props.onReady, wrapperClassName = props.wrapperClassName, wrapperStyle = props.wrapperStyle, className = props.className, style = props.style, controls = props.controls, playsInline = props.playsInline, preload = props.preload, poster = props.poster, children = props.children, licenseKey = props.licenseKey, minSegmentsBeforePlay = props.minSegmentsBeforePlay, disableAds = props.disableAds, disableFiller = props.disableFiller, swirlProjectId = props.swirlProjectId, restVideoAttrs = _object_without_properties(props, [
7107
7137
  "src",
7108
7138
  "autoplay",
@@ -7166,6 +7196,13 @@ var StormcloudVideoPlayerComponent = React2.memo(function(props) {
7166
7196
  var _React2_useState20 = _sliced_to_array(React2.useState(DEFAULT_PLAYER_ASPECT_RATIO), 2), playerAspectRatio = _React2_useState20[0], setPlayerAspectRatio = _React2_useState20[1];
7167
7197
  var _React2_useState21 = _sliced_to_array(React2.useState(false), 2), showDebugPanel = _React2_useState21[0], setShowDebugPanel = _React2_useState21[1];
7168
7198
  var _React2_useState22 = _sliced_to_array(React2.useState([]), 2), debugMarkers = _React2_useState22[0], setDebugMarkers = _React2_useState22[1];
7199
+ var _React2_useState23 = _sliced_to_array(React2.useState(false), 2), showAiPanel = _React2_useState23[0], setShowAiPanel = _React2_useState23[1];
7200
+ var _React2_useState24 = _sliced_to_array(React2.useState({
7201
+ context: null,
7202
+ isLoading: false,
7203
+ error: null,
7204
+ lastPolledAt: null
7205
+ }), 2), aiLiveContext = _React2_useState24[0], setAiLiveContext = _React2_useState24[1];
7169
7206
  var getResponsiveScale = function getResponsiveScale() {
7170
7207
  if (viewportWidth < 480) return 0.7;
7171
7208
  if (viewportWidth < 768) return 0.8;
@@ -7195,6 +7232,18 @@ var StormcloudVideoPlayerComponent = React2.memo(function(props) {
7195
7232
  if (typeof obj.splice_command_type === "number") return "binary splice";
7196
7233
  return "marker";
7197
7234
  };
7235
+ var formatAiRelativeTime = function formatAiRelativeTime(timestamp) {
7236
+ var epochMs = Date.parse(timestamp);
7237
+ if (!Number.isFinite(epochMs)) return "unknown";
7238
+ var diffSec = Math.max(0, Math.floor((Date.now() - epochMs) / 1e3));
7239
+ if (diffSec < 5) return "just now";
7240
+ if (diffSec < 60) return "".concat(diffSec, "s ago");
7241
+ var diffMin = Math.floor(diffSec / 60);
7242
+ if (diffMin < 60) return "".concat(diffMin, "m ago");
7243
+ var diffHr = Math.floor(diffMin / 60);
7244
+ if (diffHr < 24) return "".concat(diffHr, "h ago");
7245
+ return "".concat(Math.floor(diffHr / 24), "d ago");
7246
+ };
7198
7247
  var resetControlsTimer = useCallback2(function() {
7199
7248
  if (controlsTimerRef.current) {
7200
7249
  clearTimeout(controlsTimerRef.current);
@@ -7272,7 +7321,14 @@ var StormcloudVideoPlayerComponent = React2.memo(function(props) {
7272
7321
  };
7273
7322
  var isHlsStream = (src === null || src === void 0 ? void 0 : src.toLowerCase().includes(".m3u8")) || (src === null || src === void 0 ? void 0 : src.toLowerCase().includes("/hls/"));
7274
7323
  var shouldShowEnhancedControls = showCustomControls && (isHlsStream ? allowNativeHls : true);
7324
+ var analyzerPanelWidth = Math.min(420, Math.max(320, viewportWidth * 0.41));
7325
+ var analyzerPanelHeight = isPortrait ? "52vh" : "420px";
7326
+ var analyzerPanelMaxHeight = "60vh";
7327
+ var panelGap = Math.max(8, 12 * responsiveScale);
7328
+ var shouldStackPanels = isPortrait || viewportWidth < 980;
7275
7329
  var debugPanelBottomOffset = shouldShowEnhancedControls ? Math.max(74, 92 * responsiveScale) : Math.max(52, 58 * responsiveScale);
7330
+ var panelBaseRight = PANEL_BASE_RIGHT_OFFSET * responsiveScale;
7331
+ var debugPanelRightOffset = showAiPanel && !shouldStackPanels ? panelBaseRight + analyzerPanelWidth + panelGap : panelBaseRight;
7276
7332
  var criticalPropsKey = useMemo(function() {
7277
7333
  return CRITICAL_PROPS.map(function(prop) {
7278
7334
  return "".concat(prop, ":").concat(props[prop]);
@@ -7598,6 +7654,130 @@ var StormcloudVideoPlayerComponent = React2.memo(function(props) {
7598
7654
  debugAdTiming,
7599
7655
  criticalPropsKey
7600
7656
  ]);
7657
+ useEffect2(function() {
7658
+ if (!swirlProjectId) {
7659
+ setShowAiPanel(false);
7660
+ setAiLiveContext({
7661
+ context: null,
7662
+ isLoading: false,
7663
+ error: null,
7664
+ lastPolledAt: null
7665
+ });
7666
+ }
7667
+ }, [
7668
+ swirlProjectId
7669
+ ]);
7670
+ useEffect2(function() {
7671
+ if (!showAiPanel || !swirlProjectId) return;
7672
+ var cancelled = false;
7673
+ var pollTimeoutId = null;
7674
+ var inFlight = false;
7675
+ var pollLiveContext = function pollLiveContext1() {
7676
+ return _async_to_generator(function() {
7677
+ var response, payload, error, message, _ref, _playerRef_current, segmentPollMs, nextPollMs;
7678
+ return _ts_generator(this, function(_state) {
7679
+ switch(_state.label){
7680
+ case 0:
7681
+ if (cancelled || inFlight) return [
7682
+ 2
7683
+ ];
7684
+ inFlight = true;
7685
+ setAiLiveContext(function(prev) {
7686
+ return _object_spread_props(_object_spread({}, prev), {
7687
+ isLoading: prev.context == null,
7688
+ error: null
7689
+ });
7690
+ });
7691
+ _state.label = 1;
7692
+ case 1:
7693
+ _state.trys.push([
7694
+ 1,
7695
+ 4,
7696
+ 5,
7697
+ 6
7698
+ ]);
7699
+ return [
7700
+ 4,
7701
+ fetch("https://adstorm.co/api-adstorm-dev/adstorm/swirl/projects/".concat(swirlProjectId, "/live-context"), {
7702
+ method: "GET",
7703
+ headers: {
7704
+ Accept: "application/json"
7705
+ }
7706
+ })
7707
+ ];
7708
+ case 2:
7709
+ response = _state.sent();
7710
+ if (!response.ok) {
7711
+ throw new Error("Live context request failed (".concat(response.status, " ").concat(response.statusText, ")"));
7712
+ }
7713
+ return [
7714
+ 4,
7715
+ response.json()
7716
+ ];
7717
+ case 3:
7718
+ payload = _state.sent();
7719
+ if (cancelled) return [
7720
+ 2
7721
+ ];
7722
+ setAiLiveContext({
7723
+ context: payload,
7724
+ isLoading: false,
7725
+ error: null,
7726
+ lastPolledAt: Date.now()
7727
+ });
7728
+ return [
7729
+ 3,
7730
+ 6
7731
+ ];
7732
+ case 4:
7733
+ error = _state.sent();
7734
+ if (cancelled) return [
7735
+ 2
7736
+ ];
7737
+ message = _instanceof(error, Error) ? error.message : "Unable to load AI live context.";
7738
+ setAiLiveContext(function(prev) {
7739
+ return _object_spread_props(_object_spread({}, prev), {
7740
+ isLoading: false,
7741
+ error: message,
7742
+ lastPolledAt: Date.now()
7743
+ });
7744
+ });
7745
+ return [
7746
+ 3,
7747
+ 6
7748
+ ];
7749
+ case 5:
7750
+ inFlight = false;
7751
+ if (!cancelled) {
7752
+ ;
7753
+ ;
7754
+ segmentPollMs = (_ref = (_playerRef_current = playerRef.current) === null || _playerRef_current === void 0 ? void 0 : _playerRef_current.getCurrentHlsSegmentDurationMs()) !== null && _ref !== void 0 ? _ref : AI_CONTEXT_FALLBACK_POLL_MS;
7755
+ nextPollMs = Math.max(AI_CONTEXT_MIN_POLL_MS, segmentPollMs);
7756
+ pollTimeoutId = window.setTimeout(pollLiveContext, nextPollMs);
7757
+ }
7758
+ return [
7759
+ 7
7760
+ ];
7761
+ case 6:
7762
+ return [
7763
+ 2
7764
+ ];
7765
+ }
7766
+ });
7767
+ })();
7768
+ };
7769
+ pollLiveContext();
7770
+ return function() {
7771
+ cancelled = true;
7772
+ if (pollTimeoutId != null) {
7773
+ clearTimeout(pollTimeoutId);
7774
+ }
7775
+ };
7776
+ }, [
7777
+ showAiPanel,
7778
+ swirlProjectId,
7779
+ criticalPropsKey
7780
+ ]);
7601
7781
  var handleWrapperMouseMove = useCallback2(function() {
7602
7782
  resetControlsTimer();
7603
7783
  }, [
@@ -7616,7 +7796,7 @@ var StormcloudVideoPlayerComponent = React2.memo(function(props) {
7616
7796
  return /* @__PURE__ */ jsxs2(Fragment2, {
7617
7797
  children: [
7618
7798
  /* @__PURE__ */ jsx2("style", {
7619
- children: "\n @keyframes sc-spin {\n from { transform: rotate(0deg); }\n to { transform: rotate(360deg); }\n }\n @keyframes sc-loading-glow {\n 0%, 100% { opacity: 0.85; transform: scale(1); }\n 50% { opacity: 1; transform: scale(1.05); }\n }\n @keyframes sc-pulse {\n 0%, 100% { opacity: 1; }\n 50% { opacity: 0.6; }\n }\n @keyframes sc-fade-in {\n from { opacity: 0; transform: translateY(8px); }\n to { opacity: 1; transform: translateY(0); }\n }\n .sc-wrapper:fullscreen,\n .sc-wrapper:has(*:fullscreen) {\n border-radius: 0 !important;\n box-shadow: none !important;\n width: 100vw !important;\n height: 100vh !important;\n max-width: 100vw !important;\n max-height: 100vh !important;\n position: fixed !important;\n top: 0 !important;\n left: 0 !important;\n z-index: 999999 !important;\n background: #000 !important;\n display: flex !important;\n align-items: center !important;\n justify-content: center !important;\n }\n .sc-ctrl-btn {\n background: none;\n border: none;\n color: #fff;\n cursor: pointer;\n display: flex;\n align-items: center;\n justify-content: center;\n border-radius: 50%;\n padding: 8px;\n transition: background 0.15s ease, opacity 0.15s ease;\n opacity: 0.9;\n }\n .sc-ctrl-btn:hover {\n opacity: 1;\n background: rgba(255, 255, 255, 0.1);\n }\n .sc-ctrl-btn:active {\n opacity: 0.7;\n }\n .sc-controls-bar {\n transition: opacity 0.35s ease, transform 0.35s ease;\n }\n .sc-progress-track:hover .sc-progress-thumb {\n transform: translate(-50%, -50%) scale(1) !important;\n }\n .sc-loading-hidden .sc-loading-indicator {\n display: none !important;\n }\n .sc-debug-scroll::-webkit-scrollbar {\n width: 8px;\n }\n .sc-debug-scroll::-webkit-scrollbar-thumb {\n background: rgba(255, 255, 255, 0.22);\n border-radius: 4px;\n }\n .sc-debug-scroll {\n overflow-x: hidden !important;\n }\n "
7799
+ children: "\n @keyframes sc-spin {\n from { transform: rotate(0deg); }\n to { transform: rotate(360deg); }\n }\n @keyframes sc-loading-glow {\n 0%, 100% { opacity: 0.85; transform: scale(1); }\n 50% { opacity: 1; transform: scale(1.05); }\n }\n @keyframes sc-pulse {\n 0%, 100% { opacity: 1; }\n 50% { opacity: 0.6; }\n }\n @keyframes sc-fade-in {\n from { opacity: 0; transform: translateY(8px); }\n to { opacity: 1; transform: translateY(0); }\n }\n .sc-wrapper:fullscreen,\n .sc-wrapper:has(*:fullscreen) {\n border-radius: 0 !important;\n box-shadow: none !important;\n width: 100vw !important;\n height: 100vh !important;\n max-width: 100vw !important;\n max-height: 100vh !important;\n position: fixed !important;\n top: 0 !important;\n left: 0 !important;\n z-index: 999999 !important;\n background: #000 !important;\n display: flex !important;\n align-items: center !important;\n justify-content: center !important;\n }\n .sc-ctrl-btn {\n background: none;\n border: none;\n color: #fff;\n cursor: pointer;\n display: flex;\n align-items: center;\n justify-content: center;\n border-radius: 50%;\n padding: 8px;\n transition: background 0.15s ease, opacity 0.15s ease;\n opacity: 0.9;\n }\n .sc-ctrl-btn:hover {\n opacity: 1;\n background: rgba(255, 255, 255, 0.1);\n }\n .sc-ctrl-btn:active {\n opacity: 0.7;\n }\n .sc-controls-bar {\n transition: opacity 0.35s ease, transform 0.35s ease;\n }\n .sc-progress-track:hover .sc-progress-thumb {\n transform: translate(-50%, -50%) scale(1) !important;\n }\n .sc-loading-hidden .sc-loading-indicator {\n display: none !important;\n }\n .sc-debug-scroll::-webkit-scrollbar {\n width: 8px;\n }\n .sc-debug-scroll::-webkit-scrollbar-thumb {\n background: rgba(255, 255, 255, 0.22);\n border-radius: 4px;\n }\n .sc-debug-scroll {\n overflow-x: hidden !important;\n }\n .sc-ai-scroll::-webkit-scrollbar {\n width: 8px;\n }\n .sc-ai-scroll::-webkit-scrollbar-thumb {\n background: rgba(236, 72, 153, 0.34);\n border-radius: 4px;\n }\n .sc-ai-scroll {\n overflow-x: hidden !important;\n }\n "
7620
7800
  }),
7621
7801
  /* @__PURE__ */ jsxs2("div", {
7622
7802
  ref: wrapperRef,
@@ -7858,15 +8038,189 @@ var StormcloudVideoPlayerComponent = React2.memo(function(props) {
7858
8038
  })
7859
8039
  ]
7860
8040
  }),
8041
+ showAiPanel && !showLicenseWarning && swirlProjectId && /* @__PURE__ */ jsxs2("div", {
8042
+ style: _object_spread_props(_object_spread({
8043
+ position: "absolute",
8044
+ right: "".concat(panelBaseRight, "px")
8045
+ }, shouldStackPanels && showDebugPanel ? {
8046
+ top: "".concat(12 * responsiveScale, "px")
8047
+ } : {
8048
+ bottom: "".concat(debugPanelBottomOffset, "px")
8049
+ }), {
8050
+ width: "".concat(analyzerPanelWidth, "px"),
8051
+ maxWidth: "92vw",
8052
+ height: analyzerPanelHeight,
8053
+ maxHeight: analyzerPanelMaxHeight,
8054
+ zIndex: 61,
8055
+ background: "linear-gradient(165deg, rgba(17, 24, 39, 0.94) 0%, rgba(41, 17, 63, 0.82) 52%, rgba(17, 24, 39, 0.9) 100%)",
8056
+ border: "1px solid rgba(236, 72, 153, 0.35)",
8057
+ borderRadius: "14px",
8058
+ boxShadow: "0 18px 56px rgba(4, 7, 20, 0.58), inset 0 1px 0 rgba(255, 255, 255, 0.09)",
8059
+ backdropFilter: "blur(20px)",
8060
+ WebkitBackdropFilter: "blur(20px)",
8061
+ color: "rgba(255,255,255,0.96)",
8062
+ overflow: "hidden"
8063
+ }),
8064
+ children: [
8065
+ /* @__PURE__ */ jsxs2("div", {
8066
+ style: {
8067
+ display: "flex",
8068
+ alignItems: "center",
8069
+ justifyContent: "space-between",
8070
+ padding: "11px 13px",
8071
+ borderBottom: "1px solid rgba(255,255,255,0.12)",
8072
+ background: "linear-gradient(90deg, rgba(236, 72, 153, 0.2) 0%, rgba(168, 85, 247, 0.18) 100%)"
8073
+ },
8074
+ children: [
8075
+ /* @__PURE__ */ jsxs2("div", {
8076
+ children: [
8077
+ /* @__PURE__ */ jsx2("div", {
8078
+ style: {
8079
+ fontSize: "13px",
8080
+ fontWeight: 800,
8081
+ letterSpacing: "0.02em"
8082
+ },
8083
+ children: "AI Live Context Analyzer"
8084
+ }),
8085
+ /* @__PURE__ */ jsx2("div", {
8086
+ style: {
8087
+ marginTop: "2px",
8088
+ fontSize: "11px",
8089
+ color: "rgba(255,255,255,0.72)"
8090
+ },
8091
+ children: aiLiveContext.lastPolledAt ? "Updated ".concat(formatDebugClock(aiLiveContext.lastPolledAt)) : "Waiting for first sample..."
8092
+ })
8093
+ ]
8094
+ }),
8095
+ /* @__PURE__ */ jsx2("button", {
8096
+ className: "sc-ctrl-btn",
8097
+ onClick: function onClick() {
8098
+ return setShowAiPanel(false);
8099
+ },
8100
+ style: {
8101
+ padding: "4px",
8102
+ borderRadius: "6px",
8103
+ minWidth: "26px",
8104
+ minHeight: "26px"
8105
+ },
8106
+ title: "Close AI panel",
8107
+ children: /* @__PURE__ */ jsx2(FaTimes, {
8108
+ size: 12
8109
+ })
8110
+ })
8111
+ ]
8112
+ }),
8113
+ /* @__PURE__ */ jsxs2("div", {
8114
+ className: "sc-ai-scroll",
8115
+ style: {
8116
+ padding: "12px",
8117
+ overflowY: "auto",
8118
+ overflowX: "hidden",
8119
+ height: "calc(100% - 52px)",
8120
+ display: "grid",
8121
+ gap: "12px"
8122
+ },
8123
+ children: [
8124
+ aiLiveContext.error && /* @__PURE__ */ jsx2("div", {
8125
+ style: {
8126
+ fontSize: "12px",
8127
+ color: "#fecaca",
8128
+ background: "rgba(220, 38, 38, 0.2)",
8129
+ border: "1px solid rgba(248, 113, 113, 0.5)",
8130
+ borderRadius: "10px",
8131
+ padding: "9px 10px"
8132
+ },
8133
+ children: aiLiveContext.error
8134
+ }),
8135
+ /* @__PURE__ */ jsxs2("div", {
8136
+ style: {
8137
+ padding: "11px 12px",
8138
+ borderRadius: "10px",
8139
+ border: "1px solid rgba(236, 72, 153, 0.5)",
8140
+ background: "linear-gradient(155deg, rgba(88, 28, 135, 0.35) 0%, rgba(17, 24, 39, 0.62) 100%)",
8141
+ boxShadow: "0 8px 24px rgba(236, 72, 153, 0.12)"
8142
+ },
8143
+ children: [
8144
+ /* @__PURE__ */ jsxs2("div", {
8145
+ style: {
8146
+ display: "flex",
8147
+ alignItems: "center",
8148
+ justifyContent: "space-between",
8149
+ gap: "8px",
8150
+ marginBottom: "7px"
8151
+ },
8152
+ children: [
8153
+ /* @__PURE__ */ jsx2("div", {
8154
+ style: {
8155
+ fontSize: "11px",
8156
+ fontWeight: 800,
8157
+ textTransform: "uppercase",
8158
+ letterSpacing: "0.1em",
8159
+ color: "#f9a8d4"
8160
+ },
8161
+ children: "AI Context"
8162
+ }),
8163
+ /* @__PURE__ */ jsx2("div", {
8164
+ style: {
8165
+ fontSize: "10px",
8166
+ color: "rgba(255,255,255,0.72)",
8167
+ fontFamily: "'SF Mono', 'Cascadia Code', monospace"
8168
+ },
8169
+ children: aiLiveContext.context ? formatAiRelativeTime(aiLiveContext.context.updated_at) : "--"
8170
+ })
8171
+ ]
8172
+ }),
8173
+ /* @__PURE__ */ jsx2("div", {
8174
+ style: {
8175
+ fontSize: "13px",
8176
+ lineHeight: "1.58",
8177
+ color: "rgba(255,255,255,0.95)",
8178
+ whiteSpace: "pre-wrap"
8179
+ },
8180
+ children: (_ref = (_aiLiveContext_context = aiLiveContext.context) === null || _aiLiveContext_context === void 0 ? void 0 : _aiLiveContext_context.context) !== null && _ref !== void 0 ? _ref : aiLiveContext.isLoading ? "Analyzing live stream..." : "Waiting for AI context response."
8181
+ }),
8182
+ ((_aiLiveContext_context1 = aiLiveContext.context) === null || _aiLiveContext_context1 === void 0 ? void 0 : (_aiLiveContext_context_keywords = _aiLiveContext_context1.keywords) === null || _aiLiveContext_context_keywords === void 0 ? void 0 : _aiLiveContext_context_keywords.length) ? /* @__PURE__ */ jsx2("div", {
8183
+ style: {
8184
+ marginTop: "10px",
8185
+ display: "flex",
8186
+ flexWrap: "wrap",
8187
+ gap: "6px"
8188
+ },
8189
+ children: aiLiveContext.context.keywords.slice(0, 12).map(function(kw) {
8190
+ return /* @__PURE__ */ jsx2("span", {
8191
+ style: {
8192
+ fontSize: "10px",
8193
+ fontWeight: 600,
8194
+ padding: "4px 7px",
8195
+ borderRadius: "999px",
8196
+ background: "rgba(236, 72, 153, 0.2)",
8197
+ border: "1px solid rgba(244, 114, 182, 0.42)",
8198
+ color: "#fce7f3",
8199
+ maxWidth: "100%",
8200
+ overflow: "hidden",
8201
+ textOverflow: "ellipsis",
8202
+ whiteSpace: "nowrap"
8203
+ },
8204
+ title: kw,
8205
+ children: kw
8206
+ }, kw);
8207
+ })
8208
+ }) : null
8209
+ ]
8210
+ })
8211
+ ]
8212
+ })
8213
+ ]
8214
+ }),
7861
8215
  debugAdTiming && showDebugPanel && !showLicenseWarning && /* @__PURE__ */ jsxs2("div", {
7862
8216
  style: {
7863
8217
  position: "absolute",
7864
- right: "".concat(10 * responsiveScale, "px"),
8218
+ right: "".concat(debugPanelRightOffset, "px"),
7865
8219
  bottom: "".concat(debugPanelBottomOffset, "px"),
7866
- width: "".concat(Math.min(440, Math.max(320, viewportWidth * 0.42)), "px"),
8220
+ width: "".concat(analyzerPanelWidth, "px"),
7867
8221
  maxWidth: "92vw",
7868
- height: isPortrait ? "52vh" : "420px",
7869
- maxHeight: "58vh",
8222
+ height: analyzerPanelHeight,
8223
+ maxHeight: analyzerPanelMaxHeight,
7870
8224
  zIndex: 60,
7871
8225
  background: "rgba(10, 10, 10, 0.74)",
7872
8226
  border: "1px solid rgba(255, 255, 255, 0.14)",
@@ -8250,6 +8604,28 @@ var StormcloudVideoPlayerComponent = React2.memo(function(props) {
8250
8604
  gap: "".concat(8 * responsiveScale, "px")
8251
8605
  },
8252
8606
  children: [
8607
+ swirlProjectId && /* @__PURE__ */ jsx2("button", {
8608
+ className: "sc-ctrl-btn",
8609
+ onClick: function onClick() {
8610
+ setShowAiPanel(function(prev) {
8611
+ return !prev;
8612
+ });
8613
+ resetControlsTimer();
8614
+ },
8615
+ style: {
8616
+ padding: "".concat(8 * responsiveScale, "px"),
8617
+ borderRadius: "50%",
8618
+ minWidth: "".concat(36 * responsiveScale, "px"),
8619
+ minHeight: "".concat(36 * responsiveScale, "px"),
8620
+ background: showAiPanel ? "rgba(236, 72, 153, 0.34)" : "transparent",
8621
+ fontFamily: "'SF Mono', 'Cascadia Code', monospace",
8622
+ fontSize: "".concat(12 * responsiveScale, "px"),
8623
+ fontWeight: 700,
8624
+ letterSpacing: "0.03em"
8625
+ },
8626
+ title: showAiPanel ? "Hide AI context" : "Show AI context",
8627
+ children: "AI"
8628
+ }),
8253
8629
  debugAdTiming && /* @__PURE__ */ jsx2("button", {
8254
8630
  className: "sc-ctrl-btn",
8255
8631
  onClick: function onClick() {
@@ -8521,6 +8897,28 @@ var StormcloudVideoPlayerComponent = React2.memo(function(props) {
8521
8897
  })
8522
8898
  ]
8523
8899
  }),
8900
+ swirlProjectId && /* @__PURE__ */ jsx2("button", {
8901
+ className: "sc-ctrl-btn",
8902
+ onClick: function onClick() {
8903
+ setShowAiPanel(function(prev) {
8904
+ return !prev;
8905
+ });
8906
+ resetControlsTimer();
8907
+ },
8908
+ style: {
8909
+ padding: "".concat(8 * responsiveScale, "px"),
8910
+ borderRadius: "50%",
8911
+ minWidth: "".concat(36 * responsiveScale, "px"),
8912
+ minHeight: "".concat(36 * responsiveScale, "px"),
8913
+ background: showAiPanel ? "rgba(236, 72, 153, 0.34)" : "rgba(0, 0, 0, 0.6)",
8914
+ fontFamily: "'SF Mono', 'Cascadia Code', monospace",
8915
+ fontSize: "".concat(12 * responsiveScale, "px"),
8916
+ fontWeight: 700,
8917
+ letterSpacing: "0.03em"
8918
+ },
8919
+ title: showAiPanel ? "Hide AI context" : "Show AI context",
8920
+ children: "AI"
8921
+ }),
8524
8922
  debugAdTiming && /* @__PURE__ */ jsx2("button", {
8525
8923
  className: "sc-ctrl-btn",
8526
8924
  onClick: function onClick() {