@vanillaskyai/video 0.3.0 → 0.3.2
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 +39 -0
- package/PUBLIC-API.md +4 -0
- package/README.md +3 -3
- package/dist/{bg-confetti-WNQXS7ZS.js → bg-confetti-KTHLUJPO.js} +3 -3
- package/dist/{bg-emoji-ETI47GLS.js → bg-emoji-AW7374DZ.js} +3 -3
- package/dist/{bg-media-SVUZAMGX.js → bg-media-KGYE6UVS.js} +9 -4
- package/dist/{brand-message-F3RD4P3P.js → brand-message-AY43ZQR2.js} +4 -3
- package/dist/{chart-bar-CNSC7BNK.js → chart-bar-Z42RDRQT.js} +8 -4
- package/dist/{chart-counter-MCRHKN77.js → chart-counter-X7NAOQ2C.js} +10 -5
- package/dist/{chart-progress-ring-RMI4A7ZK.js → chart-progress-ring-7NSNNHL4.js} +8 -4
- package/dist/check-runtime.js +2 -2
- package/dist/chunk-35LIYILY.js +25 -0
- package/dist/{chunk-IFOW65Z6.js → chunk-4AODC6MS.js} +76 -36
- package/dist/{chunk-FSG2PYWG.js → chunk-G5JPHBX7.js} +1 -1
- package/dist/{chunk-QTMHS7JD.js → chunk-HXSV6RHF.js} +1 -1
- package/dist/{chunk-ABZACD4G.js → chunk-IJM4B2ZN.js} +1 -1
- package/dist/{chunk-XYZOJ5NZ.js → chunk-LHFADNWJ.js} +9 -8
- package/dist/{chunk-BCRUUJ6A.js → chunk-TCEHK2BW.js} +6 -2
- package/dist/{chunk-FNAEQ7QO.js → chunk-XWWLKRNU.js} +3 -1
- package/dist/{compose-video-TQOHDXNW.js → compose-video-6UQ33DOO.js} +1 -1
- package/dist/control-visibility-NQOWTKOQ.js +258 -0
- package/dist/{cta-logo-SVA5GP2A.js → cta-logo-KNBLXUJU.js} +4 -3
- package/dist/{cta-media-E7VLLK42.js → cta-media-NY4ASBGG.js} +8 -4
- package/dist/{events-CTIsANzz.d.ts → events-tQ0x-VaL.d.ts} +1 -1
- package/dist/{incoming-call-62MFZL3Z.js → incoming-call-KWOXLGJN.js} +4 -3
- package/dist/index.d.ts +2 -2
- package/dist/{infographic-before-after-AXCDAWND.js → infographic-before-after-OEM4DSTZ.js} +3 -3
- package/dist/{infographic-feature-list-GW5GUE6O.js → infographic-feature-list-ZZ25JRYQ.js} +8 -4
- package/dist/{infographic-problem-solution-7NURJANA.js → infographic-problem-solution-L3RLFDPE.js} +5 -4
- package/dist/{infographic-stat-row-J7IZR6ZN.js → infographic-stat-row-3OL3TNSH.js} +8 -4
- package/dist/{infographic-steps-VSZM6F5M.js → infographic-steps-LMBSW22P.js} +8 -4
- package/dist/{kit-DA2cfJ96.d.ts → kit-DlUSg8lA.d.ts} +1 -1
- package/dist/preload-media-KIYBFYRH.js +30 -0
- package/dist/{prompt-input-LOUCRXCY.js → prompt-input-XCQ6TS2L.js} +4 -3
- package/dist/react.d.ts +3 -3
- package/dist/react.js +215 -104
- package/dist/{reaction-QG4CYZLQ.js → reaction-YL6YPGPB.js} +5 -4
- package/dist/server.d.ts +3 -3
- package/dist/server.js +2 -2
- package/dist/{showcase-code-RUTFZR3H.js → showcase-code-EFVHRJUM.js} +8 -4
- package/dist/{showcase-phone-6CP6XEU5.js → showcase-phone-CTHXHEBT.js} +9 -5
- package/dist/{showcase-terminal-MGAEQWPR.js → showcase-terminal-XAMPADHN.js} +8 -4
- package/dist/{showcase-web-Y4W2HVRJ.js → showcase-web-LAKHYXSD.js} +9 -5
- package/dist/{social-milestone-XI2QE5BO.js → social-milestone-EOBEN7CX.js} +6 -4
- package/dist/{social-notification-2UOHQCUY.js → social-notification-MELRWIB6.js} +4 -3
- package/dist/{social-review-stack-FDYZGZTJ.js → social-review-stack-H3U437VT.js} +4 -3
- package/dist/{social-testimonial-OQ7N52GJ.js → social-testimonial-GNNMKMDD.js} +4 -3
- package/dist/{social-tweet-5OLCNPPC.js → social-tweet-PBUKKPT3.js} +4 -3
- package/dist/templates.d.ts +3 -3
- package/dist/test.d.ts +2 -2
- package/dist/test.js +1 -1
- package/dist/{types-CkO2EYr4.d.ts → types-CVMb6QEq.d.ts} +2 -2
- package/docs/concepts.md +2 -1
- package/docs/customization.md +6 -3
- package/docs/getting-started.md +2 -2
- package/docs/input-and-first-scene.md +16 -4
- package/docs/integrate-nextjs.md +2 -2
- package/docs/media-and-audio.md +5 -1
- package/docs/prompt-and-input.md +3 -2
- package/examples/nextjs-quickstart/package.json +1 -1
- package/package.json +1 -1
- package/registry/items/barChart.json +9 -3
- package/registry/items/beforeAfter.json +1 -1
- package/registry/items/bigNumber.json +10 -4
- package/registry/items/brandMessage.json +7 -1
- package/registry/items/cardList.json +9 -3
- package/registry/items/codeEditor.json +9 -3
- package/registry/items/confetti.json +1 -1
- package/registry/items/ctaLogo.json +7 -1
- package/registry/items/ctaMedia.json +9 -3
- package/registry/items/emojiBurst.json +1 -1
- package/registry/items/incomingCall.json +7 -1
- package/registry/items/media.json +9 -3
- package/registry/items/milestone.json +8 -2
- package/registry/items/notification.json +7 -1
- package/registry/items/phoneMockup.json +9 -3
- package/registry/items/problemSolution.json +8 -2
- package/registry/items/progressRing.json +9 -3
- package/registry/items/promptInput.json +7 -1
- package/registry/items/reaction.json +8 -2
- package/registry/items/reviewStack.json +7 -1
- package/registry/items/steps.json +9 -3
- package/registry/items/terminal.json +9 -3
- package/registry/items/testimonial.json +7 -1
- package/registry/items/theme.json +1 -1
- package/registry/items/tripleStats.json +9 -3
- package/registry/items/tweet.json +7 -1
- package/registry/items/webMockup.json +9 -3
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,45 @@ VanillaSky follows semantic versioning. This changelog begins with the 0.1 beta.
|
|
|
4
4
|
|
|
5
5
|
## Unreleased
|
|
6
6
|
|
|
7
|
+
## 0.3.2
|
|
8
|
+
|
|
9
|
+
- Rebuilds text legibility over photo and video backdrops. Scrims now ramp off
|
|
10
|
+
an eased curve instead of a two-stop linear fade, so they no longer leave a
|
|
11
|
+
visible band across the frame, and they are shaped to where each template's
|
|
12
|
+
copy actually sits rather than washing the whole picture. Type over media
|
|
13
|
+
carries its own halo, which lets the scrims stay lighter: the footage keeps
|
|
14
|
+
its contrast and highlights while the headline stays readable.
|
|
15
|
+
- Stops darkening the brand gradient for media that never arrives. A dead,
|
|
16
|
+
blocked, or unresolved `mediaUrl` used to leave the full scrim stack over
|
|
17
|
+
the gradient fallback, so the scene rendered as a muddy, vignetted version
|
|
18
|
+
of the gradient scenes beside it. The fallback is now the clean gradient it
|
|
19
|
+
was always documented to be.
|
|
20
|
+
- Holds a media scene's scrim back until the backdrop actually paints, so a
|
|
21
|
+
photo or video that is still loading shows the clean brand gradient instead
|
|
22
|
+
of a gradient wearing an overlay meant for footage. The scrim and the
|
|
23
|
+
picture now arrive on the same frame.
|
|
24
|
+
- Preloads scene backdrops as soon as their URL is known — including the
|
|
25
|
+
`asset.patch` that carries a resolved stock lookup — so the loading window
|
|
26
|
+
is usually gone before the scene plays. Video posters are warmed; video
|
|
27
|
+
streams deliberately are not.
|
|
28
|
+
- Hides playback controls while a video is playing until the viewer hovers,
|
|
29
|
+
focuses, or taps the player, and keeps paused and completed controls visible.
|
|
30
|
+
- Makes fullscreen usable on mobile browsers through prefixed fullscreen APIs
|
|
31
|
+
and a viewport-filling fallback when native fullscreen is unavailable.
|
|
32
|
+
- Restores configured soundtrack volume and end-of-video fades on iPhone Safari
|
|
33
|
+
by using a Web Audio gain stage for same-origin audio when element volume is
|
|
34
|
+
device-controlled, while preserving direct playback for cross-origin tracks.
|
|
35
|
+
|
|
36
|
+
## 0.3.1
|
|
37
|
+
|
|
38
|
+
- Redesigns player controls for each playback state with responsive circular
|
|
39
|
+
icon controls, a sound-first start action, and a dimmed replay treatment that
|
|
40
|
+
leaves the completed poster frame visible.
|
|
41
|
+
- Lets applications pass `opening: false` to omit the deterministic opening
|
|
42
|
+
scene and render their own transient loading UI while the first generated
|
|
43
|
+
scene is planned. This keeps loading state out of completed video JSON and
|
|
44
|
+
does not force `media` into the planner's template capabilities.
|
|
45
|
+
|
|
7
46
|
## 0.3.0
|
|
8
47
|
|
|
9
48
|
- Documents closer eligibility: a template may close a video only when its
|
package/PUBLIC-API.md
CHANGED
|
@@ -183,6 +183,10 @@ mounted player autoplay with sound after the first successful viewer start.
|
|
|
183
183
|
`manual`, `muted-autoplay`, and `autoplay-with-sound` cover the other browser
|
|
184
184
|
startup policies.
|
|
185
185
|
|
|
186
|
+
`VideoInput.opening` accepts custom copy, uses the deterministic fallback when
|
|
187
|
+
omitted, and accepts `false` when the application owns transient loading UI and
|
|
188
|
+
wants the completed video to begin with the first generated scene.
|
|
189
|
+
|
|
186
190
|
Saved-video playback performs no generation request. `VideoPlayerBinding` and
|
|
187
191
|
the internal reducer state are not public types.
|
|
188
192
|
|
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Give your AI a video output
|
|
2
2
|
|
|
3
|
-

|
|
4
4
|
|
|
5
5
|
**VanillaSky is the open-source video response layer.** Turn text, structured
|
|
6
6
|
data, and live application context into personalized video responses that start
|
|
@@ -17,7 +17,7 @@ the planning prompt, trusted templates, validation, streaming, and player.
|
|
|
17
17
|
For humans:
|
|
18
18
|
|
|
19
19
|
```bash
|
|
20
|
-
npm install @vanillaskyai/video@0.3.
|
|
20
|
+
npm install @vanillaskyai/video@0.3.2 ai @ai-sdk/openai
|
|
21
21
|
```
|
|
22
22
|
|
|
23
23
|
For coding agents:
|
|
@@ -106,7 +106,7 @@ shows each complete, validated scene as soon as it is ready and returns a
|
|
|
106
106
|
deterministic `Video` object when generation finishes.
|
|
107
107
|
|
|
108
108
|
A copy-and-run app is in
|
|
109
|
-
[`examples/nextjs-quickstart`](https://github.com/VanillaSkyAi/video/tree/v0.3.
|
|
109
|
+
[`examples/nextjs-quickstart`](https://github.com/VanillaSkyAi/video/tree/v0.3.2/examples/nextjs-quickstart).
|
|
110
110
|
|
|
111
111
|
## Shape the response
|
|
112
112
|
|
|
@@ -4,15 +4,15 @@ import {
|
|
|
4
4
|
import "./chunk-QAAO7OAY.js";
|
|
5
5
|
import {
|
|
6
6
|
TemplateText
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-TCEHK2BW.js";
|
|
8
8
|
import "./chunk-L5BTTCZV.js";
|
|
9
9
|
import {
|
|
10
10
|
BrandGradientOverlay
|
|
11
|
-
} from "./chunk-
|
|
11
|
+
} from "./chunk-IJM4B2ZN.js";
|
|
12
12
|
import "./chunk-XTOXFMLM.js";
|
|
13
13
|
import {
|
|
14
14
|
resolveTokens
|
|
15
|
-
} from "./chunk-
|
|
15
|
+
} from "./chunk-XWWLKRNU.js";
|
|
16
16
|
|
|
17
17
|
// src/visual-system/scene-templates/bg-confetti.tsx
|
|
18
18
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
@@ -3,17 +3,17 @@ import {
|
|
|
3
3
|
} from "./chunk-QAAO7OAY.js";
|
|
4
4
|
import {
|
|
5
5
|
TemplateText
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-TCEHK2BW.js";
|
|
7
7
|
import {
|
|
8
8
|
Emoji
|
|
9
9
|
} from "./chunk-L5BTTCZV.js";
|
|
10
10
|
import {
|
|
11
11
|
BrandGradientOverlay
|
|
12
|
-
} from "./chunk-
|
|
12
|
+
} from "./chunk-IJM4B2ZN.js";
|
|
13
13
|
import "./chunk-XTOXFMLM.js";
|
|
14
14
|
import {
|
|
15
15
|
resolveTokens
|
|
16
|
-
} from "./chunk-
|
|
16
|
+
} from "./chunk-XWWLKRNU.js";
|
|
17
17
|
|
|
18
18
|
// src/visual-system/scene-templates/bg-emoji.tsx
|
|
19
19
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
@@ -1,20 +1,23 @@
|
|
|
1
1
|
import {
|
|
2
2
|
SceneBackground,
|
|
3
3
|
getMediaBackgroundProps
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-4AODC6MS.js";
|
|
5
|
+
import {
|
|
6
|
+
hasSceneMedia
|
|
7
|
+
} from "./chunk-35LIYILY.js";
|
|
5
8
|
import {
|
|
6
9
|
ConfettiLayer
|
|
7
10
|
} from "./chunk-CJ7F2CE6.js";
|
|
8
11
|
import "./chunk-QAAO7OAY.js";
|
|
9
12
|
import {
|
|
10
13
|
TemplateText
|
|
11
|
-
} from "./chunk-
|
|
14
|
+
} from "./chunk-TCEHK2BW.js";
|
|
12
15
|
import "./chunk-L5BTTCZV.js";
|
|
13
|
-
import "./chunk-
|
|
16
|
+
import "./chunk-IJM4B2ZN.js";
|
|
14
17
|
import "./chunk-XTOXFMLM.js";
|
|
15
18
|
import {
|
|
16
19
|
resolveTokens
|
|
17
|
-
} from "./chunk-
|
|
20
|
+
} from "./chunk-XWWLKRNU.js";
|
|
18
21
|
|
|
19
22
|
// src/visual-system/scene-templates/bg-media.tsx
|
|
20
23
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
@@ -55,6 +58,7 @@ var BgMediaTemplate = ({
|
|
|
55
58
|
width,
|
|
56
59
|
height,
|
|
57
60
|
...getMediaBackgroundProps(variables),
|
|
61
|
+
textAnchor: "center",
|
|
58
62
|
backgroundEffect,
|
|
59
63
|
seed: String(variables.texts || ""),
|
|
60
64
|
isPlaying,
|
|
@@ -74,6 +78,7 @@ var BgMediaTemplate = ({
|
|
|
74
78
|
/* @__PURE__ */ jsx(
|
|
75
79
|
TemplateText,
|
|
76
80
|
{
|
|
81
|
+
overMedia: hasSceneMedia(variables),
|
|
77
82
|
motionProgress,
|
|
78
83
|
typeTreatment: resolveTokens(style).preset.type,
|
|
79
84
|
archetype: textArchetype ?? "subtle",
|
|
@@ -4,15 +4,16 @@ import {
|
|
|
4
4
|
import {
|
|
5
5
|
SceneBackground,
|
|
6
6
|
getMediaBackgroundProps
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-4AODC6MS.js";
|
|
8
|
+
import "./chunk-35LIYILY.js";
|
|
8
9
|
import {
|
|
9
10
|
renderWithEmoji
|
|
10
11
|
} from "./chunk-L5BTTCZV.js";
|
|
11
|
-
import "./chunk-
|
|
12
|
+
import "./chunk-IJM4B2ZN.js";
|
|
12
13
|
import {
|
|
13
14
|
interpolate
|
|
14
15
|
} from "./chunk-XTOXFMLM.js";
|
|
15
|
-
import "./chunk-
|
|
16
|
+
import "./chunk-XWWLKRNU.js";
|
|
16
17
|
|
|
17
18
|
// src/visual-system/primitives/typography/BrandMessageBubble.tsx
|
|
18
19
|
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
@@ -1,15 +1,18 @@
|
|
|
1
1
|
import {
|
|
2
2
|
SceneBackground,
|
|
3
3
|
getMediaBackgroundProps
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-4AODC6MS.js";
|
|
5
|
+
import {
|
|
6
|
+
hasSceneMedia
|
|
7
|
+
} from "./chunk-35LIYILY.js";
|
|
5
8
|
import {
|
|
6
9
|
TemplateText
|
|
7
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-TCEHK2BW.js";
|
|
8
11
|
import "./chunk-L5BTTCZV.js";
|
|
9
12
|
import {
|
|
10
13
|
TOP_TEXT_AREA_RATIO,
|
|
11
14
|
withOpacity
|
|
12
|
-
} from "./chunk-
|
|
15
|
+
} from "./chunk-IJM4B2ZN.js";
|
|
13
16
|
import {
|
|
14
17
|
SPRING_SNAPPY,
|
|
15
18
|
interpolate,
|
|
@@ -17,7 +20,7 @@ import {
|
|
|
17
20
|
} from "./chunk-XTOXFMLM.js";
|
|
18
21
|
import {
|
|
19
22
|
resolveTokens
|
|
20
|
-
} from "./chunk-
|
|
23
|
+
} from "./chunk-XWWLKRNU.js";
|
|
21
24
|
|
|
22
25
|
// src/visual-system/primitives/charts/BarChart.tsx
|
|
23
26
|
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
@@ -279,6 +282,7 @@ var ChartBarTemplate = ({
|
|
|
279
282
|
/* @__PURE__ */ jsx2("div", { style: { position: "absolute", top: 0, left: 0, right: 0, height: height * TOP_TEXT_AREA_RATIO, overflow: "visible" }, children: /* @__PURE__ */ jsx2(
|
|
280
283
|
TemplateText,
|
|
281
284
|
{
|
|
285
|
+
overMedia: hasSceneMedia(variables),
|
|
282
286
|
motionProgress,
|
|
283
287
|
typeTreatment: resolveTokens(style).preset.type,
|
|
284
288
|
archetype: "subtle",
|
|
@@ -4,27 +4,31 @@ import {
|
|
|
4
4
|
import {
|
|
5
5
|
SceneBackground,
|
|
6
6
|
getMediaBackgroundProps
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-4AODC6MS.js";
|
|
8
|
+
import {
|
|
9
|
+
hasSceneMedia
|
|
10
|
+
} from "./chunk-35LIYILY.js";
|
|
8
11
|
import {
|
|
9
12
|
ConfettiLayer
|
|
10
13
|
} from "./chunk-CJ7F2CE6.js";
|
|
11
14
|
import "./chunk-QAAO7OAY.js";
|
|
12
15
|
import {
|
|
13
16
|
TemplateText
|
|
14
|
-
} from "./chunk-
|
|
17
|
+
} from "./chunk-TCEHK2BW.js";
|
|
15
18
|
import "./chunk-L5BTTCZV.js";
|
|
16
19
|
import {
|
|
17
20
|
TOP_TEXT_AREA_RATIO,
|
|
18
21
|
withOpacity
|
|
19
|
-
} from "./chunk-
|
|
22
|
+
} from "./chunk-IJM4B2ZN.js";
|
|
20
23
|
import {
|
|
21
24
|
interpolate,
|
|
22
25
|
spring
|
|
23
26
|
} from "./chunk-XTOXFMLM.js";
|
|
24
27
|
import {
|
|
28
|
+
MEDIA_TEXT_SHADOW,
|
|
25
29
|
TOKEN_DEFAULTS,
|
|
26
30
|
resolveTokens
|
|
27
|
-
} from "./chunk-
|
|
31
|
+
} from "./chunk-XWWLKRNU.js";
|
|
28
32
|
|
|
29
33
|
// src/visual-system/primitives/typography/CountUpNumber.tsx
|
|
30
34
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
@@ -93,7 +97,7 @@ var CountUpNumber = ({
|
|
|
93
97
|
[0, 1],
|
|
94
98
|
{ extrapolateLeft: "clamp", extrapolateRight: "clamp" }
|
|
95
99
|
);
|
|
96
|
-
const mediaTextShadow = hasMediaShadow ?
|
|
100
|
+
const mediaTextShadow = hasMediaShadow ? MEDIA_TEXT_SHADOW : void 0;
|
|
97
101
|
const finalLabelFontSize = labelFontSize ?? 36 * s * 1.3;
|
|
98
102
|
return /* @__PURE__ */ jsxs(
|
|
99
103
|
"div",
|
|
@@ -253,6 +257,7 @@ var ChartCounterTemplate = ({
|
|
|
253
257
|
/* @__PURE__ */ jsx2("div", { style: { position: "absolute", top: 0, left: 0, right: 0, height: height * TOP_TEXT_AREA_RATIO, overflow: "visible" }, children: /* @__PURE__ */ jsx2(
|
|
254
258
|
TemplateText,
|
|
255
259
|
{
|
|
260
|
+
overMedia: hasSceneMedia(variables),
|
|
256
261
|
motionProgress,
|
|
257
262
|
typeTreatment: resolveTokens(style).preset.type,
|
|
258
263
|
archetype: "subtle",
|
|
@@ -5,22 +5,25 @@ import {
|
|
|
5
5
|
import {
|
|
6
6
|
SceneBackground,
|
|
7
7
|
getMediaBackgroundProps
|
|
8
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-4AODC6MS.js";
|
|
9
|
+
import {
|
|
10
|
+
hasSceneMedia
|
|
11
|
+
} from "./chunk-35LIYILY.js";
|
|
9
12
|
import {
|
|
10
13
|
TemplateText
|
|
11
|
-
} from "./chunk-
|
|
14
|
+
} from "./chunk-TCEHK2BW.js";
|
|
12
15
|
import "./chunk-L5BTTCZV.js";
|
|
13
16
|
import {
|
|
14
17
|
TOP_TEXT_AREA_RATIO,
|
|
15
18
|
withOpacity
|
|
16
|
-
} from "./chunk-
|
|
19
|
+
} from "./chunk-IJM4B2ZN.js";
|
|
17
20
|
import {
|
|
18
21
|
interpolate
|
|
19
22
|
} from "./chunk-XTOXFMLM.js";
|
|
20
23
|
import {
|
|
21
24
|
TOKEN_DEFAULTS,
|
|
22
25
|
resolveTokens
|
|
23
|
-
} from "./chunk-
|
|
26
|
+
} from "./chunk-XWWLKRNU.js";
|
|
24
27
|
|
|
25
28
|
// src/visual-system/primitives/charts/ProgressRing.tsx
|
|
26
29
|
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
@@ -254,6 +257,7 @@ var ChartProgressRingTemplate = ({
|
|
|
254
257
|
/* @__PURE__ */ jsx2("div", { style: { position: "absolute", top: 0, left: 0, right: 0, height: height * TOP_TEXT_AREA_RATIO, overflow: "visible" }, children: /* @__PURE__ */ jsx2(
|
|
255
258
|
TemplateText,
|
|
256
259
|
{
|
|
260
|
+
overMedia: hasSceneMedia(variables),
|
|
257
261
|
motionProgress,
|
|
258
262
|
typeTreatment: resolveTokens(style).preset.type,
|
|
259
263
|
archetype: "subtle",
|
package/dist/check-runtime.js
CHANGED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
// src/visual-system/scene-templates/media-source.ts
|
|
2
|
+
var VIDEO_EXTENSIONS = [".mp4", ".webm", ".mov", ".m4v", ".avi"];
|
|
3
|
+
function isVideoUrl(url) {
|
|
4
|
+
try {
|
|
5
|
+
const pathname = new URL(url).pathname.toLowerCase();
|
|
6
|
+
return VIDEO_EXTENSIONS.some((ext) => pathname.endsWith(ext));
|
|
7
|
+
} catch {
|
|
8
|
+
const lower = url.toLowerCase();
|
|
9
|
+
return VIDEO_EXTENSIONS.some((ext) => lower.endsWith(ext));
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
function resolveMediaType(mediaType, mediaUrl) {
|
|
13
|
+
if (mediaType === "gradient") return "gradient";
|
|
14
|
+
if (mediaType === "video") return "video";
|
|
15
|
+
if (mediaType === "photo") return "photo";
|
|
16
|
+
return mediaUrl && isVideoUrl(mediaUrl) ? "video" : "photo";
|
|
17
|
+
}
|
|
18
|
+
function hasSceneMedia(variables) {
|
|
19
|
+
return String(variables.mediaUrl || "").trim() !== "" && String(variables.mediaType || "auto") !== "gradient";
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export {
|
|
23
|
+
resolveMediaType,
|
|
24
|
+
hasSceneMedia
|
|
25
|
+
};
|
|
@@ -1,21 +1,14 @@
|
|
|
1
|
+
import {
|
|
2
|
+
resolveMediaType
|
|
3
|
+
} from "./chunk-35LIYILY.js";
|
|
1
4
|
import {
|
|
2
5
|
BrandGradientOverlay,
|
|
3
6
|
getBackgroundTransform
|
|
4
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-IJM4B2ZN.js";
|
|
5
8
|
|
|
6
9
|
// src/visual-system/scene-templates/scene-background.tsx
|
|
7
|
-
import { useEffect, useRef } from "react";
|
|
10
|
+
import { useEffect, useRef, useState } from "react";
|
|
8
11
|
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
9
|
-
var VIDEO_EXTENSIONS = [".mp4", ".webm", ".mov", ".m4v", ".avi"];
|
|
10
|
-
function isVideoUrl(url) {
|
|
11
|
-
try {
|
|
12
|
-
const pathname = new URL(url).pathname.toLowerCase();
|
|
13
|
-
return VIDEO_EXTENSIONS.some((ext) => pathname.endsWith(ext));
|
|
14
|
-
} catch {
|
|
15
|
-
const lower = url.toLowerCase();
|
|
16
|
-
return VIDEO_EXTENSIONS.some((ext) => lower.endsWith(ext));
|
|
17
|
-
}
|
|
18
|
-
}
|
|
19
12
|
var MEDIA_POSITIONS = {
|
|
20
13
|
center: "center center",
|
|
21
14
|
top: "center top",
|
|
@@ -29,38 +22,57 @@ function resolveMediaPosition(value) {
|
|
|
29
22
|
function resolveMediaTreatment(value) {
|
|
30
23
|
return value === "subtle" || value === "text-safe" ? value : "cinematic";
|
|
31
24
|
}
|
|
32
|
-
|
|
25
|
+
var SCRIM_STOP_COUNT = 7;
|
|
26
|
+
function smoothstep(t) {
|
|
27
|
+
return t * t * (3 - 2 * t);
|
|
28
|
+
}
|
|
29
|
+
function easedStops(peakAlpha, start, end, direction) {
|
|
30
|
+
const alphaAt = (t) => {
|
|
31
|
+
const eased = direction === "fade-out" ? 1 - smoothstep(t) : smoothstep(t);
|
|
32
|
+
return `rgba(0,0,0,${Number((peakAlpha * eased).toFixed(3))})`;
|
|
33
|
+
};
|
|
34
|
+
const stops = [];
|
|
35
|
+
if (start > 0) stops.push(`${alphaAt(0)} 0%`);
|
|
36
|
+
for (let i = 0; i < SCRIM_STOP_COUNT; i += 1) {
|
|
37
|
+
const t = i / (SCRIM_STOP_COUNT - 1);
|
|
38
|
+
const position = Number((start + (end - start) * t).toFixed(2));
|
|
39
|
+
stops.push(`${alphaAt(t)} ${position}%`);
|
|
40
|
+
}
|
|
41
|
+
if (end < 100) stops.push(`${alphaAt(1)} 100%`);
|
|
42
|
+
return stops.join(", ");
|
|
43
|
+
}
|
|
44
|
+
function getMediaTreatmentLayers(value, anchor = "full") {
|
|
33
45
|
const treatment = resolveMediaTreatment(value);
|
|
34
46
|
const vignette = {
|
|
35
47
|
id: "vignette",
|
|
36
|
-
background: treatment === "subtle" ?
|
|
48
|
+
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")})`
|
|
37
49
|
};
|
|
38
50
|
if (treatment === "subtle") return [vignette];
|
|
39
|
-
const
|
|
40
|
-
|
|
41
|
-
|
|
51
|
+
const textSafe = treatment === "text-safe";
|
|
52
|
+
const layers = [vignette];
|
|
53
|
+
if (anchor !== "bottom") {
|
|
54
|
+
layers.push({
|
|
42
55
|
id: "center-scrim",
|
|
43
|
-
background:
|
|
44
|
-
}
|
|
45
|
-
|
|
56
|
+
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")})`
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
if (anchor !== "center") {
|
|
60
|
+
layers.push({
|
|
46
61
|
id: "bottom-scrim",
|
|
47
|
-
background:
|
|
62
|
+
background: `linear-gradient(to top, ${easedStops(textSafe ? 0.64 : 0.5, 8, 100, "fade-out")})`,
|
|
48
63
|
style: { top: "55%" }
|
|
49
|
-
}
|
|
50
|
-
];
|
|
51
|
-
if (treatment === "text-safe") {
|
|
52
|
-
cinematic.splice(1, 0, {
|
|
53
|
-
id: "full-wash",
|
|
54
|
-
background: "rgba(0,0,0,0.30)"
|
|
55
64
|
});
|
|
56
65
|
}
|
|
57
|
-
return
|
|
66
|
+
return layers;
|
|
58
67
|
}
|
|
59
|
-
function
|
|
60
|
-
if (
|
|
61
|
-
if (
|
|
62
|
-
if (
|
|
63
|
-
|
|
68
|
+
function initialMediaPaint(wantsMedia, resolved, mediaUrl, mediaPoster) {
|
|
69
|
+
if (typeof window === "undefined") return "ready";
|
|
70
|
+
if (!wantsMedia) return "ready";
|
|
71
|
+
if (resolved === "video") return mediaPoster ? "ready" : "pending";
|
|
72
|
+
if (typeof Image === "undefined") return "ready";
|
|
73
|
+
const cached = new Image();
|
|
74
|
+
cached.src = mediaUrl;
|
|
75
|
+
return cached.complete && cached.naturalWidth > 0 ? "ready" : "pending";
|
|
64
76
|
}
|
|
65
77
|
function getMediaBackgroundProps(variables) {
|
|
66
78
|
return {
|
|
@@ -83,6 +95,7 @@ var SceneBackground = ({
|
|
|
83
95
|
mediaPoster,
|
|
84
96
|
mediaPosition = "center",
|
|
85
97
|
mediaTreatment = "cinematic",
|
|
98
|
+
textAnchor = "full",
|
|
86
99
|
backgroundEffect,
|
|
87
100
|
seed,
|
|
88
101
|
isPlaying = true,
|
|
@@ -91,10 +104,35 @@ var SceneBackground = ({
|
|
|
91
104
|
void _width;
|
|
92
105
|
void _height;
|
|
93
106
|
const resolved = resolveMediaType(mediaType, mediaUrl);
|
|
94
|
-
const
|
|
107
|
+
const wantsMedia = resolved !== "gradient" && !!mediaUrl;
|
|
108
|
+
const [mediaPaint, setMediaPaint] = useState(
|
|
109
|
+
() => initialMediaPaint(wantsMedia, resolved, mediaUrl, mediaPoster)
|
|
110
|
+
);
|
|
111
|
+
useEffect(() => {
|
|
112
|
+
setMediaPaint(initialMediaPaint(wantsMedia, resolved, mediaUrl, mediaPoster));
|
|
113
|
+
if (!wantsMedia || resolved !== "photo") return;
|
|
114
|
+
if (typeof Image === "undefined") return;
|
|
115
|
+
let cancelled = false;
|
|
116
|
+
const probe = new Image();
|
|
117
|
+
probe.onload = () => {
|
|
118
|
+
if (!cancelled) setMediaPaint("ready");
|
|
119
|
+
};
|
|
120
|
+
probe.onerror = () => {
|
|
121
|
+
if (!cancelled) setMediaPaint("failed");
|
|
122
|
+
};
|
|
123
|
+
probe.src = mediaUrl;
|
|
124
|
+
if (probe.complete) setMediaPaint(probe.naturalWidth > 0 ? "ready" : "failed");
|
|
125
|
+
return () => {
|
|
126
|
+
cancelled = true;
|
|
127
|
+
probe.onload = null;
|
|
128
|
+
probe.onerror = null;
|
|
129
|
+
};
|
|
130
|
+
}, [mediaUrl, mediaPoster, resolved, wantsMedia]);
|
|
131
|
+
const showMedia = wantsMedia && mediaPaint !== "failed";
|
|
132
|
+
const showTreatment = wantsMedia && mediaPaint === "ready";
|
|
95
133
|
const resolvedPosition = resolveMediaPosition(mediaPosition);
|
|
96
134
|
const resolvedTreatment = resolveMediaTreatment(mediaTreatment);
|
|
97
|
-
const treatmentLayers = getMediaTreatmentLayers(resolvedTreatment);
|
|
135
|
+
const treatmentLayers = getMediaTreatmentLayers(resolvedTreatment, textAnchor);
|
|
98
136
|
const gradSeed = typeof seed === "number" ? seed : typeof seed === "string" ? seed.split("").reduce((acc, c) => acc + c.charCodeAt(0), 0) : 0;
|
|
99
137
|
const bgTransform = getBackgroundTransform(
|
|
100
138
|
backgroundEffect,
|
|
@@ -154,6 +192,8 @@ var SceneBackground = ({
|
|
|
154
192
|
loop: true,
|
|
155
193
|
playsInline: true,
|
|
156
194
|
preload: "auto",
|
|
195
|
+
onLoadedData: () => setMediaPaint("ready"),
|
|
196
|
+
onError: () => setMediaPaint("failed"),
|
|
157
197
|
"data-media-position": mediaPosition,
|
|
158
198
|
style: {
|
|
159
199
|
position: "absolute",
|
|
@@ -181,7 +221,7 @@ var SceneBackground = ({
|
|
|
181
221
|
}
|
|
182
222
|
}
|
|
183
223
|
)),
|
|
184
|
-
|
|
224
|
+
showTreatment && treatmentLayers.map((layer) => /* @__PURE__ */ jsx(
|
|
185
225
|
"div",
|
|
186
226
|
{
|
|
187
227
|
"data-media-treatment": resolvedTreatment,
|
|
@@ -97,7 +97,7 @@ function buildVideoUserPrompt(input, openingDurationSec = 0) {
|
|
|
97
97
|
"Compose a video response from the structured customer input below.",
|
|
98
98
|
`Knowledge mode: ${input.knowledgeMode ?? "input-only"}.`,
|
|
99
99
|
`Maximum duration: ${input.maxDurationSec ?? 30} seconds, including the supplied opening.`,
|
|
100
|
-
input.opening
|
|
100
|
+
typeof input.opening === "string" && input.opening.trim() ? `The host has already added the opening scene, which consumes ${openingDurationSec} seconds. Continue after it and do not repeat or rewrite it.` : "Add the first grounded scene as soon as it is complete.",
|
|
101
101
|
"The first generated body scene must be fully playable without external media. Use a content-fit text, data, comparison, list, or device-free template with no media URL or keyword.",
|
|
102
102
|
"Never use media, ctaMedia, or reaction as the first generated body template, even with mediaType=gradient. Choose a non-media template first.",
|
|
103
103
|
"Add that scene before resolving any stock or supplied asset. Media belongs on later body scenes and must arrive without blocking scene additions.",
|
|
@@ -139,10 +139,11 @@ var DEFAULT_OPENING_TEXT = "Creating your video...";
|
|
|
139
139
|
function resolveVideoInput(input) {
|
|
140
140
|
return {
|
|
141
141
|
...input,
|
|
142
|
-
opening: input.opening?.trim() || DEFAULT_OPENING_TEXT
|
|
142
|
+
opening: input.opening === false ? false : input.opening?.trim() || DEFAULT_OPENING_TEXT
|
|
143
143
|
};
|
|
144
144
|
}
|
|
145
|
-
function resolveStreamCapabilities(capabilities) {
|
|
145
|
+
function resolveStreamCapabilities(capabilities, hasRuntimeOpening) {
|
|
146
|
+
if (!hasRuntimeOpening) return capabilities;
|
|
146
147
|
if (capabilities?.templates == null) return capabilities;
|
|
147
148
|
return {
|
|
148
149
|
...capabilities,
|
|
@@ -174,7 +175,7 @@ function safeAbortReason(value) {
|
|
|
174
175
|
return safePublicDiagnostic(value instanceof Error ? value.message : value, "Request aborted");
|
|
175
176
|
}
|
|
176
177
|
function buildInitialConfig(input, audio, snapshotRetention, closerReserveSec, getTemplatePacing) {
|
|
177
|
-
const openingText = input.opening
|
|
178
|
+
const openingText = typeof input.opening === "string" ? input.opening.trim() : void 0;
|
|
178
179
|
const rawScenes = openingText ? [{
|
|
179
180
|
id: "supplied-opening",
|
|
180
181
|
templateId: "media",
|
|
@@ -238,7 +239,7 @@ function validateInput(input) {
|
|
|
238
239
|
if (input.maxDurationSec != null && (!Number.isFinite(input.maxDurationSec) || input.maxDurationSec < 5 || input.maxDurationSec > 120)) {
|
|
239
240
|
throw new Error("Video response maximum duration must be between 5 and 120 seconds");
|
|
240
241
|
}
|
|
241
|
-
if (input.opening != null && !input.opening.trim()) {
|
|
242
|
+
if (input.opening !== false && input.opening != null && !input.opening.trim()) {
|
|
242
243
|
throw new Error("Video response opening must be a non-empty string");
|
|
243
244
|
}
|
|
244
245
|
if (input.audio && !input.audio.src.trim()) {
|
|
@@ -331,7 +332,7 @@ function createSceneQualityWarnings(scene) {
|
|
|
331
332
|
}
|
|
332
333
|
function createVideo(rawInput, options) {
|
|
333
334
|
validateInput(rawInput);
|
|
334
|
-
const
|
|
335
|
+
const requiresGeneratedScene = rawInput.opening == null || rawInput.opening === false;
|
|
335
336
|
const input = resolveVideoInput(rawInput);
|
|
336
337
|
const requestId = options.requestId ?? createId("request");
|
|
337
338
|
const runId = options.runId ?? createId("run");
|
|
@@ -446,7 +447,7 @@ function createVideo(rawInput, options) {
|
|
|
446
447
|
format: { orientation: initialConfig.orientation ?? "portrait" },
|
|
447
448
|
style: initialConfig.style,
|
|
448
449
|
meta: initialConfig.meta,
|
|
449
|
-
capabilities: resolveStreamCapabilities(options.capabilities)
|
|
450
|
+
capabilities: resolveStreamCapabilities(options.capabilities, initialConfig.scenes.length > 0)
|
|
450
451
|
}));
|
|
451
452
|
if (initialConfig.audio) yield emit(events.create("audio.set", { audio: initialConfig.audio }));
|
|
452
453
|
for (const warning of initial.warnings) {
|
|
@@ -784,7 +785,7 @@ function createVideo(rawInput, options) {
|
|
|
784
785
|
warning: createIncompletePlanWarning()
|
|
785
786
|
}));
|
|
786
787
|
}
|
|
787
|
-
if (generatedSceneCount === 0 &&
|
|
788
|
+
if (generatedSceneCount === 0 && requiresGeneratedScene) {
|
|
788
789
|
throw new Error("The planner completed without adding a scene");
|
|
789
790
|
}
|
|
790
791
|
const snapshot = parseVideo(state.config);
|
|
@@ -9,6 +9,9 @@ import {
|
|
|
9
9
|
interpolate,
|
|
10
10
|
spring
|
|
11
11
|
} from "./chunk-XTOXFMLM.js";
|
|
12
|
+
import {
|
|
13
|
+
MEDIA_TEXT_SHADOW
|
|
14
|
+
} from "./chunk-XWWLKRNU.js";
|
|
12
15
|
|
|
13
16
|
// src/visual-system/scene-templates/text-archetypes.ts
|
|
14
17
|
var TEXT_ARCHETYPES = [
|
|
@@ -526,7 +529,8 @@ var TemplateText = ({
|
|
|
526
529
|
safeZone = DEFAULT_SAFE_ZONE,
|
|
527
530
|
font = DEFAULT_FONT,
|
|
528
531
|
color = "#FFFFFF",
|
|
529
|
-
beatIntensity = 0
|
|
532
|
+
beatIntensity = 0,
|
|
533
|
+
overMedia = false
|
|
530
534
|
}) => {
|
|
531
535
|
const textSafe = typeof textRaw === "string" ? textRaw : "";
|
|
532
536
|
const isDevelopment = import.meta.env?.DEV;
|
|
@@ -568,7 +572,7 @@ var TemplateText = ({
|
|
|
568
572
|
} : baseTypo;
|
|
569
573
|
const presetWeight = tt ? Math.min(900, Math.max(100, fontWeight + tt.weightDelta)) : fontWeight;
|
|
570
574
|
const presetSize = tt ? fontSize * tt.sizeScale : fontSize;
|
|
571
|
-
const textShadow = dropShadowFor(color);
|
|
575
|
+
const textShadow = overMedia ? MEDIA_TEXT_SHADOW : dropShadowFor(color);
|
|
572
576
|
const containerStyle = {
|
|
573
577
|
position: "absolute",
|
|
574
578
|
left: 0,
|