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.cjs CHANGED
@@ -928,28 +928,9 @@ function createAdStormPlayer(contentVideo, options) {
928
928
  adVideoElement.removeAttribute("src");
929
929
  adVideoElement.load();
930
930
  }
931
- function buildVastUrl(durationSeconds, metadata) {
932
- var baseUrl = "https://adstorm.co/api-adstorm-dev/adstorm/vast/".concat(licenseKey, "/pod");
933
- var defaultMetadata = {
934
- video: {
935
- codec: "h264",
936
- width: contentVideo.videoWidth || 1280,
937
- height: contentVideo.videoHeight || 720,
938
- fps: 29.97,
939
- bitrate: 5e3,
940
- profile: "high",
941
- pix_fmt: "yuv420p",
942
- has_b_frames: 0
943
- },
944
- audio: {
945
- codec: "aac",
946
- sample_rate: 48e3,
947
- bitrate: 128
948
- }
949
- };
950
- var finalMetadata = metadata || defaultMetadata;
951
- var metadataStr = encodeURIComponent(JSON.stringify(finalMetadata));
952
- return "".concat(baseUrl, "?duration=").concat(Math.ceil(durationSeconds), "&metadata=").concat(metadataStr);
931
+ function buildVastUrl(durationSeconds) {
932
+ var baseUrl = "https://adstorm.co/api-adstorm-dev/adstorm/nab/vast/pod";
933
+ return "".concat(baseUrl, "?duration=").concat(Math.ceil(durationSeconds));
953
934
  }
954
935
  function parseVastXml(xmlString) {
955
936
  var ads = [];
@@ -6092,6 +6073,50 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
6092
6073
  return min;
6093
6074
  }
6094
6075
  },
6076
+ {
6077
+ key: "getCurrentHlsSegmentDurationMs",
6078
+ value: function getCurrentHlsSegmentDurationMs() {
6079
+ var fallbackMs = 4e3;
6080
+ if (this.nativeHlsMode) {
6081
+ return fallbackMs;
6082
+ }
6083
+ var hls = this.hls;
6084
+ if (!hls) return null;
6085
+ var levelCandidates = [
6086
+ hls.currentLevel,
6087
+ hls.nextLoadLevel,
6088
+ hls.loadLevel
6089
+ ];
6090
+ var _iteratorNormalCompletion = true, _didIteratorError = false, _iteratorError = undefined;
6091
+ try {
6092
+ for(var _iterator = levelCandidates[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true){
6093
+ var levelIndex = _step.value;
6094
+ var _hls_levels_levelIndex, _hls_levels;
6095
+ if (typeof levelIndex !== "number" || levelIndex < 0) continue;
6096
+ 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;
6097
+ if (!details) continue;
6098
+ var targetDurationSec = typeof details.partTarget === "number" && details.partTarget > 0 ? details.partTarget : typeof details.targetduration === "number" && details.targetduration > 0 ? details.targetduration : void 0;
6099
+ if (targetDurationSec !== void 0) {
6100
+ return Math.max(800, Math.floor(targetDurationSec * 1e3));
6101
+ }
6102
+ }
6103
+ } catch (err) {
6104
+ _didIteratorError = true;
6105
+ _iteratorError = err;
6106
+ } finally{
6107
+ try {
6108
+ if (!_iteratorNormalCompletion && _iterator.return != null) {
6109
+ _iterator.return();
6110
+ }
6111
+ } finally{
6112
+ if (_didIteratorError) {
6113
+ throw _iteratorError;
6114
+ }
6115
+ }
6116
+ }
6117
+ return fallbackMs;
6118
+ }
6119
+ },
6095
6120
  {
6096
6121
  key: "videoElement",
6097
6122
  get: function get() {
@@ -7268,7 +7293,12 @@ var CRITICAL_PROPS = [
7268
7293
  var CONTROLS_HIDE_DELAY = 3e3;
7269
7294
  var DEFAULT_PLAYER_ASPECT_RATIO = 16 / 9;
7270
7295
  var DEBUG_PANEL_MARKER_LIMIT = 12;
7296
+ var AI_CONTEXT_FALLBACK_POLL_MS = 4e3;
7297
+ var AI_CONTEXT_MIN_POLL_MS = 800;
7298
+ var PANEL_BASE_RIGHT_OFFSET = 10;
7271
7299
  var StormcloudVideoPlayerComponent = import_react2.default.memo(function(props) {
7300
+ var _ref;
7301
+ var _aiLiveContext_context, _aiLiveContext_context_keywords, _aiLiveContext_context1;
7272
7302
  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, [
7273
7303
  "src",
7274
7304
  "autoplay",
@@ -7332,6 +7362,13 @@ var StormcloudVideoPlayerComponent = import_react2.default.memo(function(props)
7332
7362
  var _import_react2_default_useState20 = _sliced_to_array(import_react2.default.useState(DEFAULT_PLAYER_ASPECT_RATIO), 2), playerAspectRatio = _import_react2_default_useState20[0], setPlayerAspectRatio = _import_react2_default_useState20[1];
7333
7363
  var _import_react2_default_useState21 = _sliced_to_array(import_react2.default.useState(false), 2), showDebugPanel = _import_react2_default_useState21[0], setShowDebugPanel = _import_react2_default_useState21[1];
7334
7364
  var _import_react2_default_useState22 = _sliced_to_array(import_react2.default.useState([]), 2), debugMarkers = _import_react2_default_useState22[0], setDebugMarkers = _import_react2_default_useState22[1];
7365
+ var _import_react2_default_useState23 = _sliced_to_array(import_react2.default.useState(false), 2), showAiPanel = _import_react2_default_useState23[0], setShowAiPanel = _import_react2_default_useState23[1];
7366
+ var _import_react2_default_useState24 = _sliced_to_array(import_react2.default.useState({
7367
+ context: null,
7368
+ isLoading: false,
7369
+ error: null,
7370
+ lastPolledAt: null
7371
+ }), 2), aiLiveContext = _import_react2_default_useState24[0], setAiLiveContext = _import_react2_default_useState24[1];
7335
7372
  var getResponsiveScale = function getResponsiveScale() {
7336
7373
  if (viewportWidth < 480) return 0.7;
7337
7374
  if (viewportWidth < 768) return 0.8;
@@ -7361,6 +7398,18 @@ var StormcloudVideoPlayerComponent = import_react2.default.memo(function(props)
7361
7398
  if (typeof obj.splice_command_type === "number") return "binary splice";
7362
7399
  return "marker";
7363
7400
  };
7401
+ var formatAiRelativeTime = function formatAiRelativeTime(timestamp) {
7402
+ var epochMs = Date.parse(timestamp);
7403
+ if (!Number.isFinite(epochMs)) return "unknown";
7404
+ var diffSec = Math.max(0, Math.floor((Date.now() - epochMs) / 1e3));
7405
+ if (diffSec < 5) return "just now";
7406
+ if (diffSec < 60) return "".concat(diffSec, "s ago");
7407
+ var diffMin = Math.floor(diffSec / 60);
7408
+ if (diffMin < 60) return "".concat(diffMin, "m ago");
7409
+ var diffHr = Math.floor(diffMin / 60);
7410
+ if (diffHr < 24) return "".concat(diffHr, "h ago");
7411
+ return "".concat(Math.floor(diffHr / 24), "d ago");
7412
+ };
7364
7413
  var resetControlsTimer = (0, import_react2.useCallback)(function() {
7365
7414
  if (controlsTimerRef.current) {
7366
7415
  clearTimeout(controlsTimerRef.current);
@@ -7438,7 +7487,14 @@ var StormcloudVideoPlayerComponent = import_react2.default.memo(function(props)
7438
7487
  };
7439
7488
  var isHlsStream = (src === null || src === void 0 ? void 0 : src.toLowerCase().includes(".m3u8")) || (src === null || src === void 0 ? void 0 : src.toLowerCase().includes("/hls/"));
7440
7489
  var shouldShowEnhancedControls = showCustomControls && (isHlsStream ? allowNativeHls : true);
7490
+ var analyzerPanelWidth = Math.min(420, Math.max(320, viewportWidth * 0.41));
7491
+ var analyzerPanelHeight = isPortrait ? "52vh" : "420px";
7492
+ var analyzerPanelMaxHeight = "60vh";
7493
+ var panelGap = Math.max(8, 12 * responsiveScale);
7494
+ var shouldStackPanels = isPortrait || viewportWidth < 980;
7441
7495
  var debugPanelBottomOffset = shouldShowEnhancedControls ? Math.max(74, 92 * responsiveScale) : Math.max(52, 58 * responsiveScale);
7496
+ var panelBaseRight = PANEL_BASE_RIGHT_OFFSET * responsiveScale;
7497
+ var debugPanelRightOffset = showAiPanel && !shouldStackPanels ? panelBaseRight + analyzerPanelWidth + panelGap : panelBaseRight;
7442
7498
  var criticalPropsKey = (0, import_react2.useMemo)(function() {
7443
7499
  return CRITICAL_PROPS.map(function(prop) {
7444
7500
  return "".concat(prop, ":").concat(props[prop]);
@@ -7764,6 +7820,130 @@ var StormcloudVideoPlayerComponent = import_react2.default.memo(function(props)
7764
7820
  debugAdTiming,
7765
7821
  criticalPropsKey
7766
7822
  ]);
7823
+ (0, import_react2.useEffect)(function() {
7824
+ if (!swirlProjectId) {
7825
+ setShowAiPanel(false);
7826
+ setAiLiveContext({
7827
+ context: null,
7828
+ isLoading: false,
7829
+ error: null,
7830
+ lastPolledAt: null
7831
+ });
7832
+ }
7833
+ }, [
7834
+ swirlProjectId
7835
+ ]);
7836
+ (0, import_react2.useEffect)(function() {
7837
+ if (!showAiPanel || !swirlProjectId) return;
7838
+ var cancelled = false;
7839
+ var pollTimeoutId = null;
7840
+ var inFlight = false;
7841
+ var pollLiveContext = function pollLiveContext1() {
7842
+ return _async_to_generator(function() {
7843
+ var response, payload, error, message, _ref, _playerRef_current, segmentPollMs, nextPollMs;
7844
+ return _ts_generator(this, function(_state) {
7845
+ switch(_state.label){
7846
+ case 0:
7847
+ if (cancelled || inFlight) return [
7848
+ 2
7849
+ ];
7850
+ inFlight = true;
7851
+ setAiLiveContext(function(prev) {
7852
+ return _object_spread_props(_object_spread({}, prev), {
7853
+ isLoading: prev.context == null,
7854
+ error: null
7855
+ });
7856
+ });
7857
+ _state.label = 1;
7858
+ case 1:
7859
+ _state.trys.push([
7860
+ 1,
7861
+ 4,
7862
+ 5,
7863
+ 6
7864
+ ]);
7865
+ return [
7866
+ 4,
7867
+ fetch("https://adstorm.co/api-adstorm-dev/adstorm/swirl/projects/".concat(swirlProjectId, "/live-context"), {
7868
+ method: "GET",
7869
+ headers: {
7870
+ Accept: "application/json"
7871
+ }
7872
+ })
7873
+ ];
7874
+ case 2:
7875
+ response = _state.sent();
7876
+ if (!response.ok) {
7877
+ throw new Error("Live context request failed (".concat(response.status, " ").concat(response.statusText, ")"));
7878
+ }
7879
+ return [
7880
+ 4,
7881
+ response.json()
7882
+ ];
7883
+ case 3:
7884
+ payload = _state.sent();
7885
+ if (cancelled) return [
7886
+ 2
7887
+ ];
7888
+ setAiLiveContext({
7889
+ context: payload,
7890
+ isLoading: false,
7891
+ error: null,
7892
+ lastPolledAt: Date.now()
7893
+ });
7894
+ return [
7895
+ 3,
7896
+ 6
7897
+ ];
7898
+ case 4:
7899
+ error = _state.sent();
7900
+ if (cancelled) return [
7901
+ 2
7902
+ ];
7903
+ message = _instanceof(error, Error) ? error.message : "Unable to load AI live context.";
7904
+ setAiLiveContext(function(prev) {
7905
+ return _object_spread_props(_object_spread({}, prev), {
7906
+ isLoading: false,
7907
+ error: message,
7908
+ lastPolledAt: Date.now()
7909
+ });
7910
+ });
7911
+ return [
7912
+ 3,
7913
+ 6
7914
+ ];
7915
+ case 5:
7916
+ inFlight = false;
7917
+ if (!cancelled) {
7918
+ ;
7919
+ ;
7920
+ 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;
7921
+ nextPollMs = Math.max(AI_CONTEXT_MIN_POLL_MS, segmentPollMs);
7922
+ pollTimeoutId = window.setTimeout(pollLiveContext, nextPollMs);
7923
+ }
7924
+ return [
7925
+ 7
7926
+ ];
7927
+ case 6:
7928
+ return [
7929
+ 2
7930
+ ];
7931
+ }
7932
+ });
7933
+ })();
7934
+ };
7935
+ pollLiveContext();
7936
+ return function() {
7937
+ cancelled = true;
7938
+ if (pollTimeoutId != null) {
7939
+ clearTimeout(pollTimeoutId);
7940
+ }
7941
+ };
7942
+ }, [
7943
+ showAiPanel,
7944
+ swirlProjectId,
7945
+ criticalPropsKey
7946
+ ]);
7767
7947
  var handleWrapperMouseMove = (0, import_react2.useCallback)(function() {
7768
7948
  resetControlsTimer();
7769
7949
  }, [
@@ -7782,7 +7962,7 @@ var StormcloudVideoPlayerComponent = import_react2.default.memo(function(props)
7782
7962
  return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_jsx_runtime2.Fragment, {
7783
7963
  children: [
7784
7964
  /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("style", {
7785
- 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 "
7965
+ 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 "
7786
7966
  }),
7787
7967
  /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", {
7788
7968
  ref: wrapperRef,
@@ -8024,15 +8204,189 @@ var StormcloudVideoPlayerComponent = import_react2.default.memo(function(props)
8024
8204
  })
8025
8205
  ]
8026
8206
  }),
8207
+ showAiPanel && !showLicenseWarning && swirlProjectId && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", {
8208
+ style: _object_spread_props(_object_spread({
8209
+ position: "absolute",
8210
+ right: "".concat(panelBaseRight, "px")
8211
+ }, shouldStackPanels && showDebugPanel ? {
8212
+ top: "".concat(12 * responsiveScale, "px")
8213
+ } : {
8214
+ bottom: "".concat(debugPanelBottomOffset, "px")
8215
+ }), {
8216
+ width: "".concat(analyzerPanelWidth, "px"),
8217
+ maxWidth: "92vw",
8218
+ height: analyzerPanelHeight,
8219
+ maxHeight: analyzerPanelMaxHeight,
8220
+ zIndex: 61,
8221
+ 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%)",
8222
+ border: "1px solid rgba(236, 72, 153, 0.35)",
8223
+ borderRadius: "14px",
8224
+ boxShadow: "0 18px 56px rgba(4, 7, 20, 0.58), inset 0 1px 0 rgba(255, 255, 255, 0.09)",
8225
+ backdropFilter: "blur(20px)",
8226
+ WebkitBackdropFilter: "blur(20px)",
8227
+ color: "rgba(255,255,255,0.96)",
8228
+ overflow: "hidden"
8229
+ }),
8230
+ children: [
8231
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", {
8232
+ style: {
8233
+ display: "flex",
8234
+ alignItems: "center",
8235
+ justifyContent: "space-between",
8236
+ padding: "11px 13px",
8237
+ borderBottom: "1px solid rgba(255,255,255,0.12)",
8238
+ background: "linear-gradient(90deg, rgba(236, 72, 153, 0.2) 0%, rgba(168, 85, 247, 0.18) 100%)"
8239
+ },
8240
+ children: [
8241
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", {
8242
+ children: [
8243
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", {
8244
+ style: {
8245
+ fontSize: "13px",
8246
+ fontWeight: 800,
8247
+ letterSpacing: "0.02em"
8248
+ },
8249
+ children: "AI Live Context Analyzer"
8250
+ }),
8251
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", {
8252
+ style: {
8253
+ marginTop: "2px",
8254
+ fontSize: "11px",
8255
+ color: "rgba(255,255,255,0.72)"
8256
+ },
8257
+ children: aiLiveContext.lastPolledAt ? "Updated ".concat(formatDebugClock(aiLiveContext.lastPolledAt)) : "Waiting for first sample..."
8258
+ })
8259
+ ]
8260
+ }),
8261
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("button", {
8262
+ className: "sc-ctrl-btn",
8263
+ onClick: function onClick() {
8264
+ return setShowAiPanel(false);
8265
+ },
8266
+ style: {
8267
+ padding: "4px",
8268
+ borderRadius: "6px",
8269
+ minWidth: "26px",
8270
+ minHeight: "26px"
8271
+ },
8272
+ title: "Close AI panel",
8273
+ children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_fa.FaTimes, {
8274
+ size: 12
8275
+ })
8276
+ })
8277
+ ]
8278
+ }),
8279
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", {
8280
+ className: "sc-ai-scroll",
8281
+ style: {
8282
+ padding: "12px",
8283
+ overflowY: "auto",
8284
+ overflowX: "hidden",
8285
+ height: "calc(100% - 52px)",
8286
+ display: "grid",
8287
+ gap: "12px"
8288
+ },
8289
+ children: [
8290
+ aiLiveContext.error && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", {
8291
+ style: {
8292
+ fontSize: "12px",
8293
+ color: "#fecaca",
8294
+ background: "rgba(220, 38, 38, 0.2)",
8295
+ border: "1px solid rgba(248, 113, 113, 0.5)",
8296
+ borderRadius: "10px",
8297
+ padding: "9px 10px"
8298
+ },
8299
+ children: aiLiveContext.error
8300
+ }),
8301
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", {
8302
+ style: {
8303
+ padding: "11px 12px",
8304
+ borderRadius: "10px",
8305
+ border: "1px solid rgba(236, 72, 153, 0.5)",
8306
+ background: "linear-gradient(155deg, rgba(88, 28, 135, 0.35) 0%, rgba(17, 24, 39, 0.62) 100%)",
8307
+ boxShadow: "0 8px 24px rgba(236, 72, 153, 0.12)"
8308
+ },
8309
+ children: [
8310
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", {
8311
+ style: {
8312
+ display: "flex",
8313
+ alignItems: "center",
8314
+ justifyContent: "space-between",
8315
+ gap: "8px",
8316
+ marginBottom: "7px"
8317
+ },
8318
+ children: [
8319
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", {
8320
+ style: {
8321
+ fontSize: "11px",
8322
+ fontWeight: 800,
8323
+ textTransform: "uppercase",
8324
+ letterSpacing: "0.1em",
8325
+ color: "#f9a8d4"
8326
+ },
8327
+ children: "AI Context"
8328
+ }),
8329
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", {
8330
+ style: {
8331
+ fontSize: "10px",
8332
+ color: "rgba(255,255,255,0.72)",
8333
+ fontFamily: "'SF Mono', 'Cascadia Code', monospace"
8334
+ },
8335
+ children: aiLiveContext.context ? formatAiRelativeTime(aiLiveContext.context.updated_at) : "--"
8336
+ })
8337
+ ]
8338
+ }),
8339
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", {
8340
+ style: {
8341
+ fontSize: "13px",
8342
+ lineHeight: "1.58",
8343
+ color: "rgba(255,255,255,0.95)",
8344
+ whiteSpace: "pre-wrap"
8345
+ },
8346
+ 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."
8347
+ }),
8348
+ ((_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__ */ (0, import_jsx_runtime2.jsx)("div", {
8349
+ style: {
8350
+ marginTop: "10px",
8351
+ display: "flex",
8352
+ flexWrap: "wrap",
8353
+ gap: "6px"
8354
+ },
8355
+ children: aiLiveContext.context.keywords.slice(0, 12).map(function(kw) {
8356
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", {
8357
+ style: {
8358
+ fontSize: "10px",
8359
+ fontWeight: 600,
8360
+ padding: "4px 7px",
8361
+ borderRadius: "999px",
8362
+ background: "rgba(236, 72, 153, 0.2)",
8363
+ border: "1px solid rgba(244, 114, 182, 0.42)",
8364
+ color: "#fce7f3",
8365
+ maxWidth: "100%",
8366
+ overflow: "hidden",
8367
+ textOverflow: "ellipsis",
8368
+ whiteSpace: "nowrap"
8369
+ },
8370
+ title: kw,
8371
+ children: kw
8372
+ }, kw);
8373
+ })
8374
+ }) : null
8375
+ ]
8376
+ })
8377
+ ]
8378
+ })
8379
+ ]
8380
+ }),
8027
8381
  debugAdTiming && showDebugPanel && !showLicenseWarning && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", {
8028
8382
  style: {
8029
8383
  position: "absolute",
8030
- right: "".concat(10 * responsiveScale, "px"),
8384
+ right: "".concat(debugPanelRightOffset, "px"),
8031
8385
  bottom: "".concat(debugPanelBottomOffset, "px"),
8032
- width: "".concat(Math.min(440, Math.max(320, viewportWidth * 0.42)), "px"),
8386
+ width: "".concat(analyzerPanelWidth, "px"),
8033
8387
  maxWidth: "92vw",
8034
- height: isPortrait ? "52vh" : "420px",
8035
- maxHeight: "58vh",
8388
+ height: analyzerPanelHeight,
8389
+ maxHeight: analyzerPanelMaxHeight,
8036
8390
  zIndex: 60,
8037
8391
  background: "rgba(10, 10, 10, 0.74)",
8038
8392
  border: "1px solid rgba(255, 255, 255, 0.14)",
@@ -8416,6 +8770,28 @@ var StormcloudVideoPlayerComponent = import_react2.default.memo(function(props)
8416
8770
  gap: "".concat(8 * responsiveScale, "px")
8417
8771
  },
8418
8772
  children: [
8773
+ swirlProjectId && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("button", {
8774
+ className: "sc-ctrl-btn",
8775
+ onClick: function onClick() {
8776
+ setShowAiPanel(function(prev) {
8777
+ return !prev;
8778
+ });
8779
+ resetControlsTimer();
8780
+ },
8781
+ style: {
8782
+ padding: "".concat(8 * responsiveScale, "px"),
8783
+ borderRadius: "50%",
8784
+ minWidth: "".concat(36 * responsiveScale, "px"),
8785
+ minHeight: "".concat(36 * responsiveScale, "px"),
8786
+ background: showAiPanel ? "rgba(236, 72, 153, 0.34)" : "transparent",
8787
+ fontFamily: "'SF Mono', 'Cascadia Code', monospace",
8788
+ fontSize: "".concat(12 * responsiveScale, "px"),
8789
+ fontWeight: 700,
8790
+ letterSpacing: "0.03em"
8791
+ },
8792
+ title: showAiPanel ? "Hide AI context" : "Show AI context",
8793
+ children: "AI"
8794
+ }),
8419
8795
  debugAdTiming && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("button", {
8420
8796
  className: "sc-ctrl-btn",
8421
8797
  onClick: function onClick() {
@@ -8687,6 +9063,28 @@ var StormcloudVideoPlayerComponent = import_react2.default.memo(function(props)
8687
9063
  })
8688
9064
  ]
8689
9065
  }),
9066
+ swirlProjectId && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("button", {
9067
+ className: "sc-ctrl-btn",
9068
+ onClick: function onClick() {
9069
+ setShowAiPanel(function(prev) {
9070
+ return !prev;
9071
+ });
9072
+ resetControlsTimer();
9073
+ },
9074
+ style: {
9075
+ padding: "".concat(8 * responsiveScale, "px"),
9076
+ borderRadius: "50%",
9077
+ minWidth: "".concat(36 * responsiveScale, "px"),
9078
+ minHeight: "".concat(36 * responsiveScale, "px"),
9079
+ background: showAiPanel ? "rgba(236, 72, 153, 0.34)" : "rgba(0, 0, 0, 0.6)",
9080
+ fontFamily: "'SF Mono', 'Cascadia Code', monospace",
9081
+ fontSize: "".concat(12 * responsiveScale, "px"),
9082
+ fontWeight: 700,
9083
+ letterSpacing: "0.03em"
9084
+ },
9085
+ title: showAiPanel ? "Hide AI context" : "Show AI context",
9086
+ children: "AI"
9087
+ }),
8690
9088
  debugAdTiming && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("button", {
8691
9089
  className: "sc-ctrl-btn",
8692
9090
  onClick: function onClick() {