@vanillaskyai/video 0.10.7 → 0.10.9

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/CHANGELOG.md CHANGED
@@ -4,6 +4,24 @@ VanillaSky follows semantic versioning. This changelog begins with the 0.1 beta.
4
4
 
5
5
  ## Unreleased
6
6
 
7
+ ## 0.10.9
8
+
9
+ - Use essential subject hints and explicit exclusions when selecting starter Pexels footage, and isolate cached selections by those hints. Metadata-free results remain unverified provider-ranked fallbacks.
10
+
11
+ - Forward bounded optional subject/activity hints to Pexels resolvers from the existing planning stream, without adding fields to emitted scenes or AI-video requests.
12
+
13
+ - Clarify beginner instructions and condition-dependent advice, and retain essential subjects and activities in Pexels search planning.
14
+
15
+ ## 0.10.8
16
+
17
+ - Reassert a requested pause if native video playback starts late, preserving footage during delayed narration and keeping viewer pauses in place.
18
+
19
+ - Distinguish decode errors, frame-readiness timeouts and stalled footage in the local chat diagnostic log without retaining media URLs or user content.
20
+
21
+ - Correct starter guidance for separate footage modes and remove obsolete planner logs that included raw model output.
22
+ - Prepare the next compatible mobile video while the current scene plays, retaining its decoded element across cuts and limiting mounted footage to the active and next scenes.
23
+ - Observe cached video frames on mount so a missed loading event cannot delay genuine stall recovery.
24
+
7
25
  ## 0.10.7
8
26
 
9
27
  - Reuse the first presented video frame at scene handoffs so a second readiness observation cannot briefly interrupt continuous narration.
@@ -1,8 +1,8 @@
1
1
  import {
2
2
  VideoFrame
3
- } from "./chunk-24TCMDAA.js";
3
+ } from "./chunk-KUCEOG2L.js";
4
+ import "./chunk-5JBMYQP6.js";
4
5
  import "./chunk-224QNWRA.js";
5
- import "./chunk-OOBT4X46.js";
6
6
  import "./chunk-SPVTJH3F.js";
7
7
  import "./chunk-R3XAOMKP.js";
8
8
  import "./chunk-73NTSFFI.js";
@@ -1,13 +1,60 @@
1
1
  import {
2
2
  darken,
3
3
  resolveTokens,
4
+ useExternalVideoBackdrop,
4
5
  useMediaAudio,
5
6
  useMediaFailure,
6
7
  useNarrationPreroll
7
- } from "./chunk-OOBT4X46.js";
8
+ } from "./chunk-5JBMYQP6.js";
9
+ import {
10
+ resolveMediaType
11
+ } from "./chunk-224QNWRA.js";
8
12
 
9
- // src/visual-system/scene-templates/scene-video-backdrop.tsx
10
- import { useCallback, useEffect, useRef, useState } from "react";
13
+ // src/visual-system/motion/curves.ts
14
+ function interpolate(value, inputRange, outputRange, options) {
15
+ const { easing, extrapolateLeft = "extend", extrapolateRight = "extend" } = options ?? {};
16
+ let i = 0;
17
+ for (; i < inputRange.length - 2; i++) {
18
+ if (value < inputRange[i + 1]) break;
19
+ }
20
+ const inputMin = inputRange[i];
21
+ const inputMax = inputRange[i + 1];
22
+ const outputMin = outputRange[i];
23
+ const outputMax = outputRange[i + 1];
24
+ let t = inputMax === inputMin ? 0 : (value - inputMin) / (inputMax - inputMin);
25
+ if (t < 0 && extrapolateLeft === "clamp") t = 0;
26
+ if (t > 1 && extrapolateRight === "clamp") t = 1;
27
+ if (easing && t >= 0 && t <= 1) {
28
+ t = easing(t);
29
+ }
30
+ return outputMin + t * (outputMax - outputMin);
31
+ }
32
+ function spring(progress, config) {
33
+ if (progress <= 0) return 0;
34
+ if (progress >= 1) {
35
+ const { damping: damping2 = 26 } = config ?? {};
36
+ if (damping2 >= 20) return 1;
37
+ }
38
+ const { damping = 26, stiffness = 170, mass = 1 } = config ?? {};
39
+ const omega = Math.sqrt(stiffness / mass);
40
+ const zeta = damping / (2 * Math.sqrt(stiffness * mass));
41
+ const t = progress * 3.5;
42
+ let value;
43
+ if (zeta < 1) {
44
+ const omegaD = omega * Math.sqrt(1 - zeta * zeta);
45
+ value = 1 - Math.exp(-zeta * omega * t) * (Math.cos(omegaD * t) + zeta * omega / omegaD * Math.sin(omegaD * t));
46
+ } else if (zeta === 1) {
47
+ value = 1 - Math.exp(-omega * t) * (1 + omega * t);
48
+ } else {
49
+ const s1 = -omega * (zeta + Math.sqrt(zeta * zeta - 1));
50
+ const s2 = -omega * (zeta - Math.sqrt(zeta * zeta - 1));
51
+ value = 1 + (s1 * Math.exp(s2 * t) - s2 * Math.exp(s1 * t)) / (s2 - s1);
52
+ }
53
+ return value;
54
+ }
55
+
56
+ // src/visual-system/scene-templates/scene-background.tsx
57
+ import { useEffect as useEffect2, useState as useState2 } from "react";
11
58
 
12
59
  // src/visual-system/scene-templates/color-utils.ts
13
60
  import React from "react";
@@ -83,49 +130,6 @@ var BrandGradientOverlay = ({ style: globalStyle, progress, sceneDuration, seed
83
130
  });
84
131
  };
85
132
 
86
- // src/visual-system/motion/curves.ts
87
- function interpolate(value, inputRange, outputRange, options) {
88
- const { easing, extrapolateLeft = "extend", extrapolateRight = "extend" } = options ?? {};
89
- let i = 0;
90
- for (; i < inputRange.length - 2; i++) {
91
- if (value < inputRange[i + 1]) break;
92
- }
93
- const inputMin = inputRange[i];
94
- const inputMax = inputRange[i + 1];
95
- const outputMin = outputRange[i];
96
- const outputMax = outputRange[i + 1];
97
- let t = inputMax === inputMin ? 0 : (value - inputMin) / (inputMax - inputMin);
98
- if (t < 0 && extrapolateLeft === "clamp") t = 0;
99
- if (t > 1 && extrapolateRight === "clamp") t = 1;
100
- if (easing && t >= 0 && t <= 1) {
101
- t = easing(t);
102
- }
103
- return outputMin + t * (outputMax - outputMin);
104
- }
105
- function spring(progress, config) {
106
- if (progress <= 0) return 0;
107
- if (progress >= 1) {
108
- const { damping: damping2 = 26 } = config ?? {};
109
- if (damping2 >= 20) return 1;
110
- }
111
- const { damping = 26, stiffness = 170, mass = 1 } = config ?? {};
112
- const omega = Math.sqrt(stiffness / mass);
113
- const zeta = damping / (2 * Math.sqrt(stiffness * mass));
114
- const t = progress * 3.5;
115
- let value;
116
- if (zeta < 1) {
117
- const omegaD = omega * Math.sqrt(1 - zeta * zeta);
118
- value = 1 - Math.exp(-zeta * omega * t) * (Math.cos(omegaD * t) + zeta * omega / omegaD * Math.sin(omegaD * t));
119
- } else if (zeta === 1) {
120
- value = 1 - Math.exp(-omega * t) * (1 + omega * t);
121
- } else {
122
- const s1 = -omega * (zeta + Math.sqrt(zeta * zeta - 1));
123
- const s2 = -omega * (zeta - Math.sqrt(zeta * zeta - 1));
124
- value = 1 + (s1 * Math.exp(s2 * t) - s2 * Math.exp(s1 * t)) / (s2 - s1);
125
- }
126
- return value;
127
- }
128
-
129
133
  // src/visual-system/scene-templates/background-effect.ts
130
134
  var DIRECTIONS = ["right", "left", "up", "down"];
131
135
  function getKenBurnsTransform(progress, direction) {
@@ -223,6 +227,7 @@ function resolveMediaPosition(value) {
223
227
  }
224
228
 
225
229
  // src/visual-system/scene-templates/scene-video-backdrop.tsx
230
+ import { useCallback, useEffect, useRef, useState } from "react";
226
231
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
227
232
  var SceneVideoBackdrop = ({
228
233
  mediaUrl,
@@ -237,9 +242,6 @@ var SceneVideoBackdrop = ({
237
242
  muted,
238
243
  volume,
239
244
  playbackId = mediaUrl,
240
- retainPoster = false,
241
- persistent = false,
242
- preparedPoster,
243
245
  onReady,
244
246
  onError
245
247
  }) => {
@@ -261,11 +263,11 @@ var SceneVideoBackdrop = ({
261
263
  const videoPresentationKey = `${playbackId}\0${mediaUrl}`;
262
264
  const presentationRef = useRef({ key: videoPresentationKey, playing: isPlaying });
263
265
  presentationRef.current = { key: videoPresentationKey, playing: isPlaying };
264
- const unavailable = () => {
266
+ const unavailable = (reason = "playback-error") => {
265
267
  if (presentationRef.current.key === videoPresentationKey && presentationRef.current.playing) {
266
268
  setExhaustedKey(videoPresentationKey);
267
269
  onError?.();
268
- reportMediaFailure?.();
270
+ reportMediaFailure?.(reason);
269
271
  }
270
272
  };
271
273
  useEffect(() => {
@@ -306,7 +308,7 @@ var SceneVideoBackdrop = ({
306
308
  const fail = () => {
307
309
  if (!stopped) {
308
310
  stopped = true;
309
- unavailable();
311
+ unavailable(awaitingFirstFrame ? "frame-readiness-timeout" : "stalled-media");
310
312
  }
311
313
  };
312
314
  let deadline = setTimeout(fail, awaitingFirstFrame ? 8e3 : 1e3);
@@ -318,6 +320,39 @@ var SceneVideoBackdrop = ({
318
320
  if (frame !== void 0) video.cancelVideoFrameCallback?.(frame);
319
321
  };
320
322
  }, [waitingKey, videoPresentationKey, isPlaying]);
323
+ const onReadyRef = useRef(onReady);
324
+ onReadyRef.current = onReady;
325
+ useEffect(() => {
326
+ const video = videoRef.current;
327
+ if (!video) return;
328
+ let stopped = false;
329
+ let frame;
330
+ const markPresented = () => {
331
+ if (stopped || !video.isConnected || presentationRef.current.key !== videoPresentationKey || video.getAttribute("src") !== mediaUrl || video.currentSrc !== video.src) return false;
332
+ presentedVideoUrl.current = mediaUrl;
333
+ video.dispatchEvent(new Event("vanillasky:video-frame-presented", { bubbles: true }));
334
+ onReadyRef.current?.();
335
+ setDecodedVideoUrl(mediaUrl);
336
+ stopped = true;
337
+ return true;
338
+ };
339
+ const observe = () => {
340
+ if (stopped || frame !== void 0) return;
341
+ if (video.requestVideoFrameCallback) {
342
+ frame = video.requestVideoFrameCallback(() => {
343
+ frame = void 0;
344
+ if (!markPresented()) observe();
345
+ });
346
+ } else if (video.readyState >= HTMLMediaElement.HAVE_CURRENT_DATA) markPresented();
347
+ };
348
+ video.addEventListener("loadeddata", observe);
349
+ observe();
350
+ return () => {
351
+ stopped = true;
352
+ video.removeEventListener("loadeddata", observe);
353
+ if (frame !== void 0) video.cancelVideoFrameCallback?.(frame);
354
+ };
355
+ }, [mediaUrl, videoPresentationKey]);
321
356
  const fitDuration = useCallback((video) => {
322
357
  video.playbackRate = (resolvedMuted || video.preservesPitch === true) && sceneDuration && Number.isFinite(video.duration) && video.duration > 0 ? Math.max(0.75, Math.min(1, video.duration / (sceneDuration + 0.2))) : 1;
323
358
  }, [resolvedMuted, sceneDuration]);
@@ -331,7 +366,7 @@ var SceneVideoBackdrop = ({
331
366
  return;
332
367
  }
333
368
  video.currentTime = 0;
334
- void video.play().catch(unavailable);
369
+ void video.play().catch(() => unavailable());
335
370
  };
336
371
  useEffect(() => {
337
372
  const video = videoRef.current;
@@ -366,16 +401,21 @@ var SceneVideoBackdrop = ({
366
401
  }
367
402
  if (startedPlaybackId.current === playbackId) {
368
403
  if (video.ended) continueMotion(video);
369
- else void video.play().catch(unavailable);
404
+ else void video.play().catch(() => unavailable());
370
405
  return;
371
406
  }
372
407
  const changingSource = startedVideoUrl.current !== void 0 && startedVideoUrl.current !== mediaUrl;
373
408
  fitDuration(video);
374
409
  if (!changingSource && video.currentTime > 0) video.currentTime = 0;
375
- video.play().catch(unavailable);
410
+ video.play().catch(() => unavailable());
376
411
  startedVideoUrl.current = mediaUrl;
377
412
  startedPlaybackId.current = playbackId;
378
413
  }, [isPlaying, mediaUrl, playbackId, rewindPreroll]);
414
+ const enforceRequestedPause = (event) => {
415
+ if (presentationRef.current.playing) return;
416
+ event.currentTarget.pause();
417
+ if (rewindPreroll && event.currentTarget.currentTime > 0) event.currentTarget.currentTime = 0;
418
+ };
379
419
  const mediaStyle = {
380
420
  position: "absolute",
381
421
  inset: 0,
@@ -384,59 +424,9 @@ var SceneVideoBackdrop = ({
384
424
  objectFit: "cover",
385
425
  objectPosition: resolvedPosition,
386
426
  transform: bgTransform.transform,
387
- transformOrigin: bgTransform.transformOrigin,
388
- zIndex: persistent ? 1 : void 0
427
+ transformOrigin: bgTransform.transformOrigin
389
428
  };
390
- const preparedPosition = preparedPoster ? resolveMediaPosition(preparedPoster.mediaPosition) : resolvedPosition;
391
- const preparedTransform = getBackgroundTransform(preparedPoster?.backgroundEffect, 0, 0);
392
- const posterPlanes = [
393
- ...persistent && mediaPoster ? [{
394
- presentationKey: videoPresentationKey,
395
- mediaPoster,
396
- mediaPosition: resolvedPosition,
397
- transform: bgTransform.transform,
398
- transformOrigin: bgTransform.transformOrigin,
399
- opacity: 1,
400
- zIndex: 0,
401
- role: "current"
402
- }] : [],
403
- ...preparedPoster && preparedPoster.presentationKey !== videoPresentationKey ? [{
404
- presentationKey: preparedPoster.presentationKey,
405
- mediaPoster: preparedPoster.mediaPoster,
406
- mediaPosition: preparedPosition,
407
- transform: preparedTransform.transform,
408
- transformOrigin: preparedTransform.transformOrigin,
409
- opacity: preparedPoster.opacity ?? 0,
410
- zIndex: 2,
411
- role: "prepared"
412
- }] : []
413
- ];
414
429
  return /* @__PURE__ */ jsxs(Fragment, { children: [
415
- posterPlanes.map((posterPlane) => /* @__PURE__ */ jsx(
416
- "img",
417
- {
418
- src: posterPlane.mediaPoster,
419
- alt: "",
420
- "aria-hidden": "true",
421
- draggable: false,
422
- "data-video-poster-plane": posterPlane.role,
423
- "data-video-poster-visible": posterPlane.opacity > 0 ? "true" : "false",
424
- style: {
425
- position: "absolute",
426
- inset: 0,
427
- width: "100%",
428
- height: "100%",
429
- objectFit: "cover",
430
- objectPosition: posterPlane.mediaPosition,
431
- transform: posterPlane.transform,
432
- transformOrigin: posterPlane.transformOrigin,
433
- zIndex: posterPlane.zIndex,
434
- opacity: posterPlane.opacity,
435
- pointerEvents: "none"
436
- }
437
- },
438
- posterPlane.presentationKey
439
- )),
440
430
  exhaustedKey === videoPresentationKey && /* @__PURE__ */ jsx(
441
431
  "div",
442
432
  {
@@ -451,44 +441,218 @@ var SceneVideoBackdrop = ({
451
441
  {
452
442
  ref: videoRef,
453
443
  src: mediaUrl,
454
- poster: retainPoster || decodedVideoUrl !== mediaUrl ? mediaPoster || void 0 : void 0,
444
+ poster: decodedVideoUrl !== mediaUrl ? mediaPoster || void 0 : void 0,
455
445
  muted: resolvedMuted,
456
446
  loop: false,
457
447
  playsInline: true,
458
448
  preload: "auto",
459
449
  onLoadedMetadata: (event) => fitDuration(event.currentTarget),
460
450
  onEnded: (event) => continueMotion(event.currentTarget),
451
+ onPlay: enforceRequestedPause,
452
+ onPlaying: enforceRequestedPause,
461
453
  onWaiting: () => {
462
454
  if (isPlaying) setWaitingKey(videoPresentationKey);
463
455
  },
464
- onLoadedData: (event) => {
465
- const video = event.currentTarget;
466
- const markPresented = () => {
467
- if (!video.isConnected || presentationRef.current.key !== videoPresentationKey || video.getAttribute("src") !== mediaUrl || video.currentSrc !== video.src) return;
468
- presentedVideoUrl.current = mediaUrl;
469
- video.dispatchEvent(new Event("vanillasky:video-frame-presented", { bubbles: true }));
470
- onReady?.();
471
- if (!retainPoster) setDecodedVideoUrl(mediaUrl);
472
- };
473
- if (video.requestVideoFrameCallback) {
474
- video.requestVideoFrameCallback(markPresented);
475
- return;
476
- }
477
- markPresented();
478
- },
479
456
  onError,
480
457
  "data-media-position": mediaPosition,
481
- "data-video-backdrop": persistent ? "persistent" : "scene",
458
+ "data-video-backdrop": "scene",
482
459
  style: { ...mediaStyle, visibility: exhaustedKey === videoPresentationKey ? "hidden" : void 0 }
483
460
  }
484
461
  )
485
462
  ] });
486
463
  };
487
464
 
465
+ // src/visual-system/scene-templates/scene-background.tsx
466
+ import { Fragment as Fragment2, jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
467
+ function resolveMediaTreatment(value) {
468
+ return value === "none" || value === "subtle" || value === "text-safe" ? value : "cinematic";
469
+ }
470
+ var SCRIM_STOP_COUNT = 7;
471
+ function smoothstep(t) {
472
+ return t * t * (3 - 2 * t);
473
+ }
474
+ function easedStops(peakAlpha, start, end, direction) {
475
+ const alphaAt = (t) => {
476
+ const eased = direction === "fade-out" ? 1 - smoothstep(t) : smoothstep(t);
477
+ return `rgba(0,0,0,${Number((peakAlpha * eased).toFixed(3))})`;
478
+ };
479
+ const stops = [];
480
+ if (start > 0) stops.push(`${alphaAt(0)} 0%`);
481
+ for (let i = 0; i < SCRIM_STOP_COUNT; i += 1) {
482
+ const t = i / (SCRIM_STOP_COUNT - 1);
483
+ const position = Number((start + (end - start) * t).toFixed(2));
484
+ stops.push(`${alphaAt(t)} ${position}%`);
485
+ }
486
+ if (end < 100) stops.push(`${alphaAt(1)} 100%`);
487
+ return stops.join(", ");
488
+ }
489
+ function getMediaTreatmentLayers(value, anchor = "full") {
490
+ const treatment = resolveMediaTreatment(value);
491
+ if (treatment === "none") return [];
492
+ const vignette = {
493
+ id: "vignette",
494
+ background: treatment === "subtle" ? `radial-gradient(ellipse at center, ${easedStops(0.28, 45, 100, "fade-in")})` : `radial-gradient(ellipse at center, ${easedStops(0.72, 32, 100, "fade-in")})`
495
+ };
496
+ if (treatment === "subtle") return [vignette];
497
+ const textSafe = treatment === "text-safe";
498
+ const layers = [vignette];
499
+ if (anchor !== "bottom") {
500
+ layers.push({
501
+ id: "center-scrim",
502
+ background: textSafe ? `radial-gradient(ellipse 92% 58% at 50% 50%, ${easedStops(0.46, 34, 90, "fade-out")})` : `radial-gradient(ellipse 88% 52% at 50% 50%, ${easedStops(0.26, 30, 88, "fade-out")})`
503
+ });
504
+ }
505
+ if (anchor !== "center") {
506
+ layers.push({
507
+ id: "bottom-scrim",
508
+ background: `linear-gradient(to top, ${easedStops(textSafe ? 0.64 : 0.5, 8, 100, "fade-out")})`,
509
+ style: { top: "55%" }
510
+ });
511
+ }
512
+ return layers;
513
+ }
514
+ function initialMediaPaint(wantsMedia, resolved, mediaUrl, mediaPoster) {
515
+ if (typeof window === "undefined") return "ready";
516
+ if (!wantsMedia) return "ready";
517
+ if (resolved === "video") return mediaPoster ? "ready" : "pending";
518
+ if (typeof Image === "undefined") return "ready";
519
+ const cached = new Image();
520
+ cached.src = mediaUrl;
521
+ return cached.complete && cached.naturalWidth > 0 ? "ready" : "pending";
522
+ }
523
+ function getMediaBackgroundProps(variables) {
524
+ return {
525
+ mediaUrl: String(variables.mediaUrl || ""),
526
+ mediaType: String(variables.mediaType || "auto"),
527
+ mediaPoster: String(variables.mediaPoster || ""),
528
+ mediaPosition: String(variables.mediaPosition || "center"),
529
+ mediaTreatment: String(variables.mediaTreatment || "cinematic")
530
+ };
531
+ }
532
+ var SceneBackground = ({
533
+ style,
534
+ progress,
535
+ sceneDuration,
536
+ width: _width,
537
+ // accepted for symmetry; not currently used in render
538
+ height: _height,
539
+ mediaUrl = "",
540
+ mediaType = "auto",
541
+ mediaPoster,
542
+ mediaPosition = "center",
543
+ mediaTreatment = "cinematic",
544
+ textAnchor = "full",
545
+ backgroundEffect,
546
+ seed,
547
+ isPlaying = true,
548
+ beatIntensity = 0
549
+ }) => {
550
+ void _width;
551
+ void _height;
552
+ const resolved = resolveMediaType(mediaType, mediaUrl);
553
+ const wantsMedia = resolved !== "gradient" && !!mediaUrl;
554
+ const externalVideoBackdrop = useExternalVideoBackdrop();
555
+ const hasExternalVideoBackdrop = externalVideoBackdrop !== false && resolved === "video";
556
+ const externalVideoFailed = externalVideoBackdrop === "fallback" && resolved === "video";
557
+ const externalVideoReady = externalVideoBackdrop === "ready" && resolved === "video";
558
+ const [mediaPaint, setMediaPaint] = useState2(
559
+ () => initialMediaPaint(wantsMedia, resolved, mediaUrl, mediaPoster)
560
+ );
561
+ useEffect2(() => {
562
+ setMediaPaint(initialMediaPaint(wantsMedia, resolved, mediaUrl, mediaPoster));
563
+ if (!wantsMedia || resolved !== "photo") return;
564
+ if (typeof Image === "undefined") return;
565
+ let cancelled = false;
566
+ const probe = new Image();
567
+ probe.onload = () => {
568
+ if (!cancelled) setMediaPaint("ready");
569
+ };
570
+ probe.onerror = () => {
571
+ if (!cancelled) setMediaPaint("failed");
572
+ };
573
+ probe.src = mediaUrl;
574
+ if (probe.complete) setMediaPaint(probe.naturalWidth > 0 ? "ready" : "failed");
575
+ return () => {
576
+ cancelled = true;
577
+ probe.onload = null;
578
+ probe.onerror = null;
579
+ };
580
+ }, [mediaUrl, mediaPoster, resolved, wantsMedia]);
581
+ const showMedia = wantsMedia && mediaPaint !== "failed";
582
+ const showTreatment = wantsMedia && mediaPaint === "ready";
583
+ const resolvedPosition = resolveMediaPosition(mediaPosition);
584
+ const resolvedTreatment = resolveMediaTreatment(mediaTreatment);
585
+ const treatmentLayers = getMediaTreatmentLayers(resolvedTreatment, textAnchor);
586
+ const gradSeed = typeof seed === "number" ? seed : typeof seed === "string" ? seed.split("").reduce((acc, c) => acc + c.charCodeAt(0), 0) : 0;
587
+ const bgTransform = getBackgroundTransform(
588
+ backgroundEffect,
589
+ progress,
590
+ beatIntensity
591
+ );
592
+ return /* @__PURE__ */ jsxs2(Fragment2, { children: [
593
+ (!hasExternalVideoBackdrop || externalVideoFailed) && /* @__PURE__ */ jsx2(
594
+ BrandGradientOverlay,
595
+ {
596
+ style,
597
+ progress,
598
+ sceneDuration,
599
+ seed: gradSeed
600
+ }
601
+ ),
602
+ showMedia && !hasExternalVideoBackdrop && (resolved === "video" ? /* @__PURE__ */ jsx2(
603
+ SceneVideoBackdrop,
604
+ {
605
+ mediaUrl,
606
+ mediaPoster,
607
+ mediaPosition,
608
+ backgroundEffect,
609
+ progress,
610
+ sceneDuration,
611
+ beatIntensity,
612
+ isPlaying,
613
+ onReady: () => setMediaPaint("ready"),
614
+ onError: () => setMediaPaint("failed")
615
+ }
616
+ ) : /* @__PURE__ */ jsx2(
617
+ "img",
618
+ {
619
+ src: mediaUrl,
620
+ alt: "",
621
+ "aria-hidden": "true",
622
+ draggable: false,
623
+ "data-media-position": mediaPosition,
624
+ style: {
625
+ position: "absolute",
626
+ inset: 0,
627
+ transform: bgTransform.transform,
628
+ transformOrigin: bgTransform.transformOrigin,
629
+ width: "100%",
630
+ height: "100%",
631
+ objectFit: "cover",
632
+ objectPosition: resolvedPosition
633
+ }
634
+ }
635
+ )),
636
+ (hasExternalVideoBackdrop ? externalVideoReady : showTreatment) && !externalVideoFailed && treatmentLayers.map((layer) => /* @__PURE__ */ jsx2(
637
+ "div",
638
+ {
639
+ "data-media-treatment": resolvedTreatment,
640
+ "data-media-overlay": layer.id,
641
+ style: {
642
+ position: "absolute",
643
+ inset: 0,
644
+ background: layer.background,
645
+ pointerEvents: "none",
646
+ ...layer.style
647
+ }
648
+ },
649
+ layer.id
650
+ ))
651
+ ] });
652
+ };
653
+
488
654
  export {
489
- BrandGradientOverlay,
490
655
  spring,
491
- getBackgroundTransform,
492
- resolveMediaPosition,
493
- SceneVideoBackdrop
656
+ getMediaBackgroundProps,
657
+ SceneBackground
494
658
  };