@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 +18 -0
- package/dist/check-runtime.js +2 -2
- package/dist/{chunk-OBRKB7PL.js → chunk-J7EGPURK.js} +291 -127
- package/dist/{chunk-24TCMDAA.js → chunk-KUCEOG2L.js} +31 -94
- package/dist/{chunk-PMJNBR4F.js → chunk-Q3LPOPHL.js} +1 -1
- package/dist/{chunk-MKMCX3AF.js → chunk-R5ZX2LJV.js} +1 -1
- package/dist/{cinema-media-BVQFZL3A.js → cinema-media-WUS7CKPC.js} +3 -4
- package/dist/{comparison-T27C7FSV.js → comparison-T4OYCOH3.js} +3 -4
- package/dist/{editorial-timeline-JADHHNEV.js → editorial-timeline-JHQZIPNE.js} +3 -4
- package/dist/{key-figure-TZQWPFMI.js → key-figure-YWN2OBQU.js} +3 -4
- package/dist/{mobile-message-GADX632R.js → mobile-message-CDKOERRZ.js} +3 -4
- package/dist/{quote-FRCEC34C.js → quote-JKABOMIG.js} +3 -4
- package/dist/react.js +8 -8
- package/dist/server.js +22 -3
- package/package.json +1 -1
- package/registry/items/backgrounds.json +2 -2
- package/starters/video-chat/README.md +27 -8
- package/starters/video-chat/package.json +1 -1
- package/starters/video-chat/server.ts +1 -6
- package/starters/video-chat/stock.ts +27 -4
- package/dist/chunk-X4RGAEL5.js +0 -207
- package/dist/scene-video-backdrop-NK2XK3XE.js +0 -7
- package/dist/{chunk-OOBT4X46.js → chunk-5JBMYQP6.js} +34 -34
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.
|
package/dist/check-runtime.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import {
|
|
2
2
|
VideoFrame
|
|
3
|
-
} from "./chunk-
|
|
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-
|
|
8
|
+
} from "./chunk-5JBMYQP6.js";
|
|
9
|
+
import {
|
|
10
|
+
resolveMediaType
|
|
11
|
+
} from "./chunk-224QNWRA.js";
|
|
8
12
|
|
|
9
|
-
// src/visual-system/
|
|
10
|
-
|
|
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:
|
|
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":
|
|
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
|
-
|
|
492
|
-
|
|
493
|
-
SceneVideoBackdrop
|
|
656
|
+
getMediaBackgroundProps,
|
|
657
|
+
SceneBackground
|
|
494
658
|
};
|