@vanillaskyai/video 0.1.0
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 +45 -0
- package/LICENSE +201 -0
- package/PUBLIC-API.md +276 -0
- package/README.md +152 -0
- package/SECURITY.md +22 -0
- package/SUPPORT.md +28 -0
- package/bin/vanillasky.js +3 -0
- package/dist/bg-confetti-WNQXS7ZS.js +89 -0
- package/dist/bg-emoji-ETI47GLS.js +150 -0
- package/dist/bg-media-SVUZAMGX.js +99 -0
- package/dist/brand-message-F3RD4P3P.js +253 -0
- package/dist/builtin-server-F7YXRVCH.js +8 -0
- package/dist/catalog-types-BIhSpOWK.d.ts +74 -0
- package/dist/chart-bar-CNSC7BNK.js +304 -0
- package/dist/chart-counter-MCRHKN77.js +299 -0
- package/dist/chart-progress-ring-RMI4A7ZK.js +296 -0
- package/dist/check-runtime.d.ts +15 -0
- package/dist/check-runtime.js +102 -0
- package/dist/chunk-2E6T633S.js +27 -0
- package/dist/chunk-352ZZCET.js +83 -0
- package/dist/chunk-73NTSFFI.js +81 -0
- package/dist/chunk-A2X45FET.js +46 -0
- package/dist/chunk-ABPKM4GK.js +187 -0
- package/dist/chunk-ABZACD4G.js +216 -0
- package/dist/chunk-BCRUUJ6A.js +756 -0
- package/dist/chunk-CHLGW22F.js +3551 -0
- package/dist/chunk-CJ7F2CE6.js +104 -0
- package/dist/chunk-CR7FE7BW.js +109 -0
- package/dist/chunk-E7CL7UPB.js +109 -0
- package/dist/chunk-EE6PK6XC.js +18 -0
- package/dist/chunk-ERY4S6QV.js +385 -0
- package/dist/chunk-EVG2ZYIY.js +125 -0
- package/dist/chunk-FNAEQ7QO.js +155 -0
- package/dist/chunk-FQ47WXAJ.js +38 -0
- package/dist/chunk-FSG2PYWG.js +336 -0
- package/dist/chunk-GHJEAP4O.js +504 -0
- package/dist/chunk-GRMUV7QY.js +262 -0
- package/dist/chunk-IFOW65Z6.js +205 -0
- package/dist/chunk-IXCUY3B3.js +786 -0
- package/dist/chunk-L5BTTCZV.js +97 -0
- package/dist/chunk-LMG3GHLL.js +107 -0
- package/dist/chunk-OOPIETLW.js +101 -0
- package/dist/chunk-P22HHFDK.js +43 -0
- package/dist/chunk-QAAO7OAY.js +19 -0
- package/dist/chunk-S7HFLYNN.js +35 -0
- package/dist/chunk-SPVTJH3F.js +24 -0
- package/dist/chunk-W2XJKG6C.js +3177 -0
- package/dist/chunk-XB7LH4J2.js +301 -0
- package/dist/chunk-XGRA2MUV.js +242 -0
- package/dist/chunk-XTOXFMLM.js +105 -0
- package/dist/chunk-YC5MFXA5.js +132 -0
- package/dist/cli.d.ts +1 -0
- package/dist/cli.js +1681 -0
- package/dist/compose-video-CN3VSWST.js +13 -0
- package/dist/cta-logo-SVA5GP2A.js +209 -0
- package/dist/cta-media-E7VLLK42.js +215 -0
- package/dist/events-_wfjbmEp.d.ts +81 -0
- package/dist/incoming-call-62MFZL3Z.js +320 -0
- package/dist/index.d.ts +15 -0
- package/dist/index.js +14 -0
- package/dist/infographic-before-after-AXCDAWND.js +460 -0
- package/dist/infographic-feature-list-GW5GUE6O.js +266 -0
- package/dist/infographic-problem-solution-7NURJANA.js +340 -0
- package/dist/infographic-stat-row-J7IZR6ZN.js +241 -0
- package/dist/infographic-steps-VSZM6F5M.js +400 -0
- package/dist/kit-BMNcHqa9.d.ts +79 -0
- package/dist/prompt-input-LOUCRXCY.js +236 -0
- package/dist/react.d.ts +81 -0
- package/dist/react.js +835 -0
- package/dist/reaction-QG4CYZLQ.js +119 -0
- package/dist/server.d.ts +141 -0
- package/dist/server.js +498 -0
- package/dist/showcase-code-RUTFZR3H.js +351 -0
- package/dist/showcase-phone-6CP6XEU5.js +272 -0
- package/dist/showcase-terminal-MGAEQWPR.js +306 -0
- package/dist/showcase-web-Y4W2HVRJ.js +390 -0
- package/dist/social-conversation-75SD2637.js +695 -0
- package/dist/social-milestone-XI2QE5BO.js +292 -0
- package/dist/social-notification-2UOHQCUY.js +244 -0
- package/dist/social-review-stack-FDYZGZTJ.js +239 -0
- package/dist/social-testimonial-OQ7N52GJ.js +284 -0
- package/dist/social-tweet-5OLCNPPC.js +295 -0
- package/dist/state-DZcKuS32.d.ts +3 -0
- package/dist/system-prompt-A6UXNTPK.js +8 -0
- package/dist/template-catalog.d.ts +24 -0
- package/dist/template-catalog.js +6 -0
- package/dist/templates.d.ts +25 -0
- package/dist/templates.js +19 -0
- package/dist/test.d.ts +149 -0
- package/dist/test.js +272 -0
- package/dist/text-stream-6XHC7J47.js +10 -0
- package/dist/types-B-PZR6G_.d.ts +208 -0
- package/dist/validate-T7GBU2YF.js +8 -0
- package/docs/agent-integration.md +39 -0
- package/docs/architecture.md +84 -0
- package/docs/branding-and-personalization.md +87 -0
- package/docs/concepts.md +106 -0
- package/docs/custom-templates.md +329 -0
- package/docs/customization.md +141 -0
- package/docs/errors.md +70 -0
- package/docs/getting-started.md +111 -0
- package/docs/input-and-first-scene.md +41 -0
- package/docs/integrate-nextjs.md +79 -0
- package/docs/media-and-audio.md +69 -0
- package/docs/motion-and-effects.md +107 -0
- package/docs/persistence.md +95 -0
- package/docs/production.md +122 -0
- package/docs/prompt-and-input.md +185 -0
- package/docs/provider-integration.md +115 -0
- package/docs/reference/protocol.md +96 -0
- package/docs/reference/provider-adapters.md +158 -0
- package/docs/responsive-orientation.md +37 -0
- package/docs/security.md +38 -0
- package/docs/streaming-protocol.md +15 -0
- package/docs/testing.md +137 -0
- package/docs/use-cases.md +59 -0
- package/examples/custom-template/README.md +19 -0
- package/examples/custom-template/minimal-text.tsx +82 -0
- package/examples/custom-template/structured-data.tsx +104 -0
- package/examples/custom-template/supplied-media.tsx +92 -0
- package/examples/nextjs-quickstart/.env.example +2 -0
- package/examples/nextjs-quickstart/README.md +22 -0
- package/examples/nextjs-quickstart/next-env.d.ts +4 -0
- package/examples/nextjs-quickstart/package.json +24 -0
- package/examples/nextjs-quickstart/src/app/api/video/route.ts +22 -0
- package/examples/nextjs-quickstart/src/app/layout.tsx +5 -0
- package/examples/nextjs-quickstart/src/app/page.tsx +31 -0
- package/examples/nextjs-quickstart/tsconfig.json +26 -0
- package/package.json +167 -0
- package/registry/items/backgrounds.json +52 -0
- package/registry/items/barChart.json +237 -0
- package/registry/items/beforeAfter.json +180 -0
- package/registry/items/bigNumber.json +225 -0
- package/registry/items/brandMessage.json +158 -0
- package/registry/items/cardList.json +206 -0
- package/registry/items/chatMessenger.json +151 -0
- package/registry/items/chatWhatsapp.json +151 -0
- package/registry/items/codeEditor.json +184 -0
- package/registry/items/confetti.json +107 -0
- package/registry/items/ctaLogo.json +160 -0
- package/registry/items/ctaMedia.json +193 -0
- package/registry/items/emojiBurst.json +101 -0
- package/registry/items/incomingCall.json +170 -0
- package/registry/items/media.json +179 -0
- package/registry/items/milestone.json +205 -0
- package/registry/items/motion.json +45 -0
- package/registry/items/notification.json +182 -0
- package/registry/items/phoneMockup.json +254 -0
- package/registry/items/problemSolution.json +186 -0
- package/registry/items/progressRing.json +199 -0
- package/registry/items/promptInput.json +148 -0
- package/registry/items/reaction.json +170 -0
- package/registry/items/reviewStack.json +224 -0
- package/registry/items/steps.json +207 -0
- package/registry/items/template-context.json +31 -0
- package/registry/items/terminal.json +212 -0
- package/registry/items/testimonial.json +164 -0
- package/registry/items/theme.json +47 -0
- package/registry/items/tripleStats.json +228 -0
- package/registry/items/tweet.json +203 -0
- package/registry/items/typography.json +47 -0
- package/registry/items/webMockup.json +273 -0
|
@@ -0,0 +1,262 @@
|
|
|
1
|
+
import {
|
|
2
|
+
boundProviderValue,
|
|
3
|
+
getGenerationLifecycleSink,
|
|
4
|
+
normalizeModelId,
|
|
5
|
+
normalizeProviderUsage
|
|
6
|
+
} from "./chunk-E7CL7UPB.js";
|
|
7
|
+
import {
|
|
8
|
+
parseVideoPlanPart
|
|
9
|
+
} from "./chunk-ERY4S6QV.js";
|
|
10
|
+
|
|
11
|
+
// src/server/model/truncated-json.ts
|
|
12
|
+
function isConfidentlyTruncatedJson(source) {
|
|
13
|
+
const stack = [];
|
|
14
|
+
let inString = false;
|
|
15
|
+
let escaped = false;
|
|
16
|
+
for (const character of source) {
|
|
17
|
+
if (inString) {
|
|
18
|
+
if (escaped) escaped = false;
|
|
19
|
+
else if (character === "\\") escaped = true;
|
|
20
|
+
else if (character === '"') inString = false;
|
|
21
|
+
continue;
|
|
22
|
+
}
|
|
23
|
+
if (character === '"') inString = true;
|
|
24
|
+
else if (character === "{" || character === "[") stack.push(character);
|
|
25
|
+
else if (character === "}" || character === "]") {
|
|
26
|
+
const expected = character === "}" ? "{" : "[";
|
|
27
|
+
if (stack.pop() !== expected) return false;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
if (!inString && stack.length === 0) return false;
|
|
31
|
+
let completed = source;
|
|
32
|
+
if (inString && !escaped) completed += '"';
|
|
33
|
+
else if (inString && escaped) return false;
|
|
34
|
+
completed += stack.reverse().map((open) => open === "{" ? "}" : "]").join("");
|
|
35
|
+
try {
|
|
36
|
+
JSON.parse(completed);
|
|
37
|
+
return true;
|
|
38
|
+
} catch {
|
|
39
|
+
return false;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// src/server/model/text-stream.ts
|
|
44
|
+
var PROVIDER_CODE_FENCE = /^```(?:json|ndjson)?$/i;
|
|
45
|
+
function parsePlanLine(line) {
|
|
46
|
+
if (!line || PROVIDER_CODE_FENCE.test(line) || !line.startsWith("{")) return void 0;
|
|
47
|
+
const value = JSON.parse(line);
|
|
48
|
+
if (value && typeof value === "object" && !Array.isArray(value)) {
|
|
49
|
+
const object = value;
|
|
50
|
+
const scene = object.scene;
|
|
51
|
+
if (object.type === "scene.add" && object.timing != null && scene && typeof scene === "object" && !Array.isArray(scene) && !("timing" in scene)) {
|
|
52
|
+
const { timing, ...planPart } = object;
|
|
53
|
+
return parseVideoPlanPart({
|
|
54
|
+
...planPart,
|
|
55
|
+
scene: { ...scene, timing }
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
return parseVideoPlanPart(value);
|
|
60
|
+
}
|
|
61
|
+
function isEnrichedSource(source) {
|
|
62
|
+
return typeof source === "object" && source != null && "textStream" in source;
|
|
63
|
+
}
|
|
64
|
+
function normalizeFinishReason(reason) {
|
|
65
|
+
if (!reason) return void 0;
|
|
66
|
+
const normalized = reason.toLowerCase().replaceAll("_", "-");
|
|
67
|
+
if (["stop", "end-turn", "end", "completed"].includes(normalized)) return "stop";
|
|
68
|
+
if (["length", "max-tokens", "max-output-tokens", "token-limit"].includes(normalized)) return "length";
|
|
69
|
+
if (["content-filter", "content-filtered", "safety"].includes(normalized)) return "content-filter";
|
|
70
|
+
if (["error", "failed", "failure"].includes(normalized)) return "provider-error";
|
|
71
|
+
if (["tool-calls", "tool-call", "tool-use", "tool-uses"].includes(normalized)) return "tool-calls";
|
|
72
|
+
return "other";
|
|
73
|
+
}
|
|
74
|
+
function record(value) {
|
|
75
|
+
return value && typeof value === "object" && !Array.isArray(value) ? value : void 0;
|
|
76
|
+
}
|
|
77
|
+
function valueAt(value, path) {
|
|
78
|
+
let current = value;
|
|
79
|
+
for (const key of path) {
|
|
80
|
+
current = record(current)?.[key];
|
|
81
|
+
}
|
|
82
|
+
return current;
|
|
83
|
+
}
|
|
84
|
+
function modelFromResponse(value) {
|
|
85
|
+
return normalizeModelId(valueAt(value, ["modelId"]) ?? valueAt(value, ["model"]));
|
|
86
|
+
}
|
|
87
|
+
function modelFromStep(value) {
|
|
88
|
+
return modelFromResponse(valueAt(value, ["response"])) ?? normalizeModelId(valueAt(value, ["modelId"]));
|
|
89
|
+
}
|
|
90
|
+
function requestedModelFromStep(value) {
|
|
91
|
+
return normalizeModelId(valueAt(value, ["model", "modelId"]));
|
|
92
|
+
}
|
|
93
|
+
function providerWarning(code, message) {
|
|
94
|
+
return { code, category: "provider", message, recoverable: true };
|
|
95
|
+
}
|
|
96
|
+
var PROVIDER_RESULT_ABORTED = /* @__PURE__ */ Symbol("provider-result-aborted");
|
|
97
|
+
function settle(value) {
|
|
98
|
+
return Promise.resolve(value).then(
|
|
99
|
+
(resolved) => ({ value: resolved, rejected: false }),
|
|
100
|
+
() => ({ rejected: true })
|
|
101
|
+
);
|
|
102
|
+
}
|
|
103
|
+
function providerResultOrAbort(result, signal) {
|
|
104
|
+
return new Promise((resolve, reject) => {
|
|
105
|
+
let completed = false;
|
|
106
|
+
const cleanup = () => signal.removeEventListener("abort", onAbort);
|
|
107
|
+
const onAbort = () => {
|
|
108
|
+
if (completed) return;
|
|
109
|
+
completed = true;
|
|
110
|
+
cleanup();
|
|
111
|
+
resolve(PROVIDER_RESULT_ABORTED);
|
|
112
|
+
};
|
|
113
|
+
signal.addEventListener("abort", onAbort, { once: true });
|
|
114
|
+
void result.then(
|
|
115
|
+
(value) => {
|
|
116
|
+
if (completed) return;
|
|
117
|
+
completed = true;
|
|
118
|
+
cleanup();
|
|
119
|
+
resolve(value);
|
|
120
|
+
},
|
|
121
|
+
(cause) => {
|
|
122
|
+
if (completed) return;
|
|
123
|
+
completed = true;
|
|
124
|
+
cleanup();
|
|
125
|
+
reject(cause);
|
|
126
|
+
}
|
|
127
|
+
);
|
|
128
|
+
if (signal.aborted) onAbort();
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
function inspectProviderSource(source, includeRawProviderData) {
|
|
132
|
+
const finish = settle(source.finishReason);
|
|
133
|
+
const rawFinish = settle(source.rawFinishReason);
|
|
134
|
+
const usage = settle(source.totalUsage ?? source.usage);
|
|
135
|
+
const metadata = settle(source.providerMetadata);
|
|
136
|
+
const warnings = settle(source.warnings);
|
|
137
|
+
const response = settle(source.response);
|
|
138
|
+
const finalStep = settle(source.finalStep ?? source.lastStep);
|
|
139
|
+
const steps = settle(source.steps);
|
|
140
|
+
const requestedModel = settle(source.requestedModelId ?? source.modelId);
|
|
141
|
+
const resolvedModel = settle(source.resolvedModelId);
|
|
142
|
+
return Promise.all([
|
|
143
|
+
finish,
|
|
144
|
+
rawFinish,
|
|
145
|
+
usage,
|
|
146
|
+
metadata,
|
|
147
|
+
warnings,
|
|
148
|
+
response,
|
|
149
|
+
finalStep,
|
|
150
|
+
steps,
|
|
151
|
+
requestedModel,
|
|
152
|
+
resolvedModel
|
|
153
|
+
]).then(([
|
|
154
|
+
finishResult,
|
|
155
|
+
rawFinishResult,
|
|
156
|
+
usageResult,
|
|
157
|
+
metadataResult,
|
|
158
|
+
warningResult,
|
|
159
|
+
responseResult,
|
|
160
|
+
finalStepResult,
|
|
161
|
+
stepsResult,
|
|
162
|
+
requestedResult,
|
|
163
|
+
resolvedResult
|
|
164
|
+
]) => {
|
|
165
|
+
const finishObject = record(finishResult.value);
|
|
166
|
+
const finishReason = typeof finishResult.value === "string" ? finishResult.value : typeof finishObject?.unified === "string" ? finishObject.unified : void 0;
|
|
167
|
+
const rawReason = typeof rawFinishResult.value === "string" ? rawFinishResult.value : typeof finishObject?.raw === "string" ? finishObject.raw : void 0;
|
|
168
|
+
const providerWarnings = [];
|
|
169
|
+
if (Array.isArray(warningResult.value) && warningResult.value.length > 0) {
|
|
170
|
+
providerWarnings.push(providerWarning("provider_warning", "The model provider reported a warning."));
|
|
171
|
+
}
|
|
172
|
+
if ([
|
|
173
|
+
finishResult,
|
|
174
|
+
rawFinishResult,
|
|
175
|
+
usageResult,
|
|
176
|
+
metadataResult,
|
|
177
|
+
warningResult,
|
|
178
|
+
responseResult,
|
|
179
|
+
finalStepResult,
|
|
180
|
+
stepsResult,
|
|
181
|
+
requestedResult,
|
|
182
|
+
resolvedResult
|
|
183
|
+
].some(({ rejected }) => rejected)) {
|
|
184
|
+
providerWarnings.push(providerWarning(
|
|
185
|
+
"provider_diagnostics_unavailable",
|
|
186
|
+
"Some provider diagnostics were unavailable."
|
|
187
|
+
));
|
|
188
|
+
}
|
|
189
|
+
const stepList = Array.isArray(stepsResult.value) ? stepsResult.value : void 0;
|
|
190
|
+
const finalStepValue = finalStepResult.value ?? stepList?.at(-1);
|
|
191
|
+
const normalizedUsage = normalizeProviderUsage(usageResult.value, includeRawProviderData);
|
|
192
|
+
const requestedModelId = requestedModelFromStep(finalStepValue) ?? normalizeModelId(requestedResult.value);
|
|
193
|
+
const resolvedModelId = modelFromResponse(responseResult.value) ?? modelFromStep(finalStepValue) ?? normalizeModelId(resolvedResult.value);
|
|
194
|
+
return {
|
|
195
|
+
...finishReason ? { finishReason } : {},
|
|
196
|
+
...rawReason ? { rawFinishReason: rawReason } : {},
|
|
197
|
+
...normalizedUsage ? { usage: normalizedUsage } : {},
|
|
198
|
+
...includeRawProviderData && metadataResult.value !== void 0 ? { providerMetadata: boundProviderValue(metadataResult.value) } : {},
|
|
199
|
+
...requestedModelId ? { requestedModelId } : {},
|
|
200
|
+
...resolvedModelId ? { resolvedModelId } : {},
|
|
201
|
+
warnings: providerWarnings
|
|
202
|
+
};
|
|
203
|
+
});
|
|
204
|
+
}
|
|
205
|
+
function createTextDeltaVideoPlanner(options) {
|
|
206
|
+
return async function* plan(context) {
|
|
207
|
+
const source = options.streamText(context);
|
|
208
|
+
const enriched = isEnrichedSource(source) ? source : void 0;
|
|
209
|
+
const providerResult = enriched ? inspectProviderSource(enriched, options.includeRawProviderData ?? false) : void 0;
|
|
210
|
+
if (providerResult) getGenerationLifecycleSink(context)?.registerProviderResult(providerResult);
|
|
211
|
+
const textStream = enriched?.textStream ?? source;
|
|
212
|
+
let buffer = "";
|
|
213
|
+
let explicitlyCompleted = false;
|
|
214
|
+
for await (const delta of textStream) {
|
|
215
|
+
if (typeof delta !== "string") throw new Error("The LLM adapter returned a non-text delta");
|
|
216
|
+
buffer += delta;
|
|
217
|
+
let newline = buffer.indexOf("\n");
|
|
218
|
+
while (newline >= 0) {
|
|
219
|
+
const line = buffer.slice(0, newline).trim();
|
|
220
|
+
buffer = buffer.slice(newline + 1);
|
|
221
|
+
const part = parsePlanLine(line);
|
|
222
|
+
if (part) {
|
|
223
|
+
if (part.type === "plan.complete") explicitlyCompleted = true;
|
|
224
|
+
yield part;
|
|
225
|
+
}
|
|
226
|
+
newline = buffer.indexOf("\n");
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
const tail = buffer.trim();
|
|
230
|
+
try {
|
|
231
|
+
const part = parsePlanLine(tail);
|
|
232
|
+
if (part) {
|
|
233
|
+
if (part.type === "plan.complete") explicitlyCompleted = true;
|
|
234
|
+
yield part;
|
|
235
|
+
}
|
|
236
|
+
} catch (error) {
|
|
237
|
+
if (!tail.startsWith("{") || !isConfidentlyTruncatedJson(tail)) throw error;
|
|
238
|
+
yield { type: "plan.complete", finishReason: "length" };
|
|
239
|
+
explicitlyCompleted = true;
|
|
240
|
+
}
|
|
241
|
+
if (enriched) {
|
|
242
|
+
const provider = await providerResultOrAbort(providerResult, context.signal);
|
|
243
|
+
if (provider === PROVIDER_RESULT_ABORTED) return;
|
|
244
|
+
const standardReason = normalizeFinishReason(provider.finishReason);
|
|
245
|
+
const rawReason = normalizeFinishReason(provider.rawFinishReason);
|
|
246
|
+
const providerReason = standardReason && standardReason !== "other" ? standardReason : rawReason ?? standardReason;
|
|
247
|
+
if (providerReason === "provider-error") {
|
|
248
|
+
throw new Error("The model provider finished with an error");
|
|
249
|
+
}
|
|
250
|
+
if (providerReason === "tool-calls") {
|
|
251
|
+
throw new Error("The model provider requested unsupported tool calls");
|
|
252
|
+
}
|
|
253
|
+
if (!explicitlyCompleted && (providerReason === "length" || providerReason === "content-filter" || providerReason === "other")) {
|
|
254
|
+
yield { type: "plan.complete", finishReason: providerReason };
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
};
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
export {
|
|
261
|
+
createTextDeltaVideoPlanner
|
|
262
|
+
};
|
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
import {
|
|
2
|
+
BrandGradientOverlay,
|
|
3
|
+
getBackgroundTransform
|
|
4
|
+
} from "./chunk-ABZACD4G.js";
|
|
5
|
+
|
|
6
|
+
// src/visual-system/scene-templates/scene-background.tsx
|
|
7
|
+
import { useEffect, useRef } from "react";
|
|
8
|
+
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
|
+
var MEDIA_POSITIONS = {
|
|
20
|
+
center: "center center",
|
|
21
|
+
top: "center top",
|
|
22
|
+
bottom: "center bottom",
|
|
23
|
+
left: "left center",
|
|
24
|
+
right: "right center"
|
|
25
|
+
};
|
|
26
|
+
function resolveMediaPosition(value) {
|
|
27
|
+
return MEDIA_POSITIONS[value] ?? MEDIA_POSITIONS.center;
|
|
28
|
+
}
|
|
29
|
+
function resolveMediaTreatment(value) {
|
|
30
|
+
return value === "subtle" || value === "text-safe" ? value : "cinematic";
|
|
31
|
+
}
|
|
32
|
+
function getMediaTreatmentLayers(value) {
|
|
33
|
+
const treatment = resolveMediaTreatment(value);
|
|
34
|
+
const vignette = {
|
|
35
|
+
id: "vignette",
|
|
36
|
+
background: treatment === "subtle" ? "radial-gradient(ellipse at center, transparent 45%, rgba(0,0,0,0.28) 100%)" : "radial-gradient(ellipse at center, transparent 30%, rgba(0,0,0,0.55) 80%, rgba(0,0,0,0.75) 100%)"
|
|
37
|
+
};
|
|
38
|
+
if (treatment === "subtle") return [vignette];
|
|
39
|
+
const cinematic = [
|
|
40
|
+
vignette,
|
|
41
|
+
{
|
|
42
|
+
id: "center-scrim",
|
|
43
|
+
background: treatment === "text-safe" ? "radial-gradient(ellipse 90% 56% at 50% 50%, rgba(0,0,0,0.36) 0%, rgba(0,0,0,0.18) 55%, transparent 84%)" : "radial-gradient(ellipse 85% 50% at 50% 50%, rgba(0,0,0,0.22) 0%, rgba(0,0,0,0.10) 50%, transparent 80%)"
|
|
44
|
+
},
|
|
45
|
+
{
|
|
46
|
+
id: "bottom-scrim",
|
|
47
|
+
background: treatment === "text-safe" ? "linear-gradient(to top, rgba(0,0,0,0.68) 0%, transparent 100%)" : "linear-gradient(to top, rgba(0,0,0,0.5) 0%, transparent 100%)",
|
|
48
|
+
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
|
+
});
|
|
56
|
+
}
|
|
57
|
+
return cinematic;
|
|
58
|
+
}
|
|
59
|
+
function resolveMediaType(mediaType, mediaUrl) {
|
|
60
|
+
if (mediaType === "gradient") return "gradient";
|
|
61
|
+
if (mediaType === "video") return "video";
|
|
62
|
+
if (mediaType === "photo") return "photo";
|
|
63
|
+
return mediaUrl && isVideoUrl(mediaUrl) ? "video" : "photo";
|
|
64
|
+
}
|
|
65
|
+
function getMediaBackgroundProps(variables) {
|
|
66
|
+
return {
|
|
67
|
+
mediaUrl: String(variables.mediaUrl || ""),
|
|
68
|
+
mediaType: String(variables.mediaType || "auto"),
|
|
69
|
+
mediaPoster: String(variables.mediaPoster || ""),
|
|
70
|
+
mediaPosition: String(variables.mediaPosition || "center"),
|
|
71
|
+
mediaTreatment: String(variables.mediaTreatment || "cinematic")
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
var SceneBackground = ({
|
|
75
|
+
style,
|
|
76
|
+
progress,
|
|
77
|
+
sceneDuration,
|
|
78
|
+
width: _width,
|
|
79
|
+
// accepted for symmetry; not currently used in render
|
|
80
|
+
height: _height,
|
|
81
|
+
mediaUrl = "",
|
|
82
|
+
mediaType = "auto",
|
|
83
|
+
mediaPoster,
|
|
84
|
+
mediaPosition = "center",
|
|
85
|
+
mediaTreatment = "cinematic",
|
|
86
|
+
backgroundEffect,
|
|
87
|
+
seed,
|
|
88
|
+
isPlaying = true,
|
|
89
|
+
beatIntensity = 0
|
|
90
|
+
}) => {
|
|
91
|
+
void _width;
|
|
92
|
+
void _height;
|
|
93
|
+
const resolved = resolveMediaType(mediaType, mediaUrl);
|
|
94
|
+
const showMedia = resolved !== "gradient" && !!mediaUrl;
|
|
95
|
+
const resolvedPosition = resolveMediaPosition(mediaPosition);
|
|
96
|
+
const resolvedTreatment = resolveMediaTreatment(mediaTreatment);
|
|
97
|
+
const treatmentLayers = getMediaTreatmentLayers(resolvedTreatment);
|
|
98
|
+
const gradSeed = typeof seed === "number" ? seed : typeof seed === "string" ? seed.split("").reduce((acc, c) => acc + c.charCodeAt(0), 0) : 0;
|
|
99
|
+
const bgTransform = getBackgroundTransform(
|
|
100
|
+
backgroundEffect,
|
|
101
|
+
progress,
|
|
102
|
+
beatIntensity
|
|
103
|
+
);
|
|
104
|
+
const videoRef = useRef(null);
|
|
105
|
+
const lastProgress = useRef(progress);
|
|
106
|
+
const videoStarted = useRef(false);
|
|
107
|
+
useEffect(() => {
|
|
108
|
+
const vid = videoRef.current;
|
|
109
|
+
if (!vid) return;
|
|
110
|
+
if (!isPlaying) {
|
|
111
|
+
vid.pause();
|
|
112
|
+
videoStarted.current = false;
|
|
113
|
+
return;
|
|
114
|
+
}
|
|
115
|
+
const progressChanged = Math.abs(progress - lastProgress.current) > 1e-3;
|
|
116
|
+
lastProgress.current = progress;
|
|
117
|
+
if (progressChanged && !videoStarted.current) {
|
|
118
|
+
vid.playbackRate = 1;
|
|
119
|
+
vid.currentTime = 0;
|
|
120
|
+
vid.play().catch(() => {
|
|
121
|
+
});
|
|
122
|
+
videoStarted.current = true;
|
|
123
|
+
} else if (!progressChanged && videoStarted.current) {
|
|
124
|
+
vid.pause();
|
|
125
|
+
videoStarted.current = false;
|
|
126
|
+
}
|
|
127
|
+
}, [progress, isPlaying]);
|
|
128
|
+
useEffect(() => {
|
|
129
|
+
const vid = videoRef.current;
|
|
130
|
+
return () => {
|
|
131
|
+
if (!vid) return;
|
|
132
|
+
vid.pause();
|
|
133
|
+
vid.removeAttribute("src");
|
|
134
|
+
vid.load();
|
|
135
|
+
};
|
|
136
|
+
}, []);
|
|
137
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
138
|
+
/* @__PURE__ */ jsx(
|
|
139
|
+
BrandGradientOverlay,
|
|
140
|
+
{
|
|
141
|
+
style,
|
|
142
|
+
progress,
|
|
143
|
+
sceneDuration,
|
|
144
|
+
seed: gradSeed
|
|
145
|
+
}
|
|
146
|
+
),
|
|
147
|
+
showMedia && (resolved === "video" ? /* @__PURE__ */ jsx(
|
|
148
|
+
"video",
|
|
149
|
+
{
|
|
150
|
+
ref: videoRef,
|
|
151
|
+
src: mediaUrl,
|
|
152
|
+
poster: mediaPoster || void 0,
|
|
153
|
+
muted: true,
|
|
154
|
+
loop: true,
|
|
155
|
+
playsInline: true,
|
|
156
|
+
preload: "auto",
|
|
157
|
+
"data-media-position": mediaPosition,
|
|
158
|
+
style: {
|
|
159
|
+
position: "absolute",
|
|
160
|
+
inset: 0,
|
|
161
|
+
width: "100%",
|
|
162
|
+
height: "100%",
|
|
163
|
+
objectFit: "cover",
|
|
164
|
+
objectPosition: resolvedPosition,
|
|
165
|
+
transform: bgTransform.transform,
|
|
166
|
+
transformOrigin: bgTransform.transformOrigin
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
) : /* @__PURE__ */ jsx(
|
|
170
|
+
"div",
|
|
171
|
+
{
|
|
172
|
+
"data-media-position": mediaPosition,
|
|
173
|
+
style: {
|
|
174
|
+
position: "absolute",
|
|
175
|
+
inset: 0,
|
|
176
|
+
transform: bgTransform.transform,
|
|
177
|
+
transformOrigin: bgTransform.transformOrigin,
|
|
178
|
+
backgroundImage: `url(${mediaUrl})`,
|
|
179
|
+
backgroundSize: "cover",
|
|
180
|
+
backgroundPosition: resolvedPosition
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
)),
|
|
184
|
+
showMedia && treatmentLayers.map((layer) => /* @__PURE__ */ jsx(
|
|
185
|
+
"div",
|
|
186
|
+
{
|
|
187
|
+
"data-media-treatment": resolvedTreatment,
|
|
188
|
+
"data-media-overlay": layer.id,
|
|
189
|
+
style: {
|
|
190
|
+
position: "absolute",
|
|
191
|
+
inset: 0,
|
|
192
|
+
background: layer.background,
|
|
193
|
+
pointerEvents: "none",
|
|
194
|
+
...layer.style
|
|
195
|
+
}
|
|
196
|
+
},
|
|
197
|
+
layer.id
|
|
198
|
+
))
|
|
199
|
+
] });
|
|
200
|
+
};
|
|
201
|
+
|
|
202
|
+
export {
|
|
203
|
+
getMediaBackgroundProps,
|
|
204
|
+
SceneBackground
|
|
205
|
+
};
|