@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
package/docs/errors.md
ADDED
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
[← Documentation home](../README.md) · [Previous: Security](security.md) · [Next: Production →](production.md)
|
|
2
|
+
|
|
3
|
+
# Errors and recovery
|
|
4
|
+
|
|
5
|
+
Render errors in normal application UI outside the video. Never show stack traces, provider details, raw deltas, or protocol diagnostics inside a scene.
|
|
6
|
+
|
|
7
|
+
| Boundary | Example codes | Host response |
|
|
8
|
+
| --- | --- | --- |
|
|
9
|
+
| HTTP | `unauthorized`, `origin_forbidden`, `body_too_large` | Fix session/policy; do not retry blindly |
|
|
10
|
+
| Input | `invalid_json`, `invalid_request`, `secret_field` | Correct client request |
|
|
11
|
+
| Protocol | invalid sequence/checksum/scene | Stop the stream and record a safe diagnostic |
|
|
12
|
+
| Generation | `generation_failed` | Hold current scene, end cleanly, offer retry |
|
|
13
|
+
| Generated part | `invalid_generated_part` | Keep accepted scenes; inspect the private server log |
|
|
14
|
+
| Resume | `invalid_cursor`, `resume_unavailable` | Restart only before visible output or from valid storage |
|
|
15
|
+
|
|
16
|
+
```tsx
|
|
17
|
+
import { VideoError } from "@vanillaskyai/video/react";
|
|
18
|
+
|
|
19
|
+
try {
|
|
20
|
+
const completed = await video.generate({ input });
|
|
21
|
+
await saveVideo(completed);
|
|
22
|
+
} catch (error) {
|
|
23
|
+
if (error instanceof VideoError) {
|
|
24
|
+
logSafeFailure({
|
|
25
|
+
code: error.code,
|
|
26
|
+
status: error.status,
|
|
27
|
+
requestId: error.requestId,
|
|
28
|
+
runId: error.runId,
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
`generate()` resolves only for `response.complete`. A terminal
|
|
35
|
+
`response.error` or `response.abort` rejects it. The hook still retains the
|
|
36
|
+
latest validated `video.video`, and its player stream still receives terminal
|
|
37
|
+
events, so an application may keep already accepted scenes visible while it
|
|
38
|
+
offers a retry.
|
|
39
|
+
|
|
40
|
+
Both a server `response.abort` and an explicit `video.abort(reason)` keep
|
|
41
|
+
`video.status` at `aborted`; `video.error` contains the same safe typed abort
|
|
42
|
+
reported by the rejected `generate()` promise.
|
|
43
|
+
|
|
44
|
+
`VideoError` preserves only actionable public context: the server's safe
|
|
45
|
+
`code` and `message`, HTTP `status` when available, `requestId`, `runId`, and
|
|
46
|
+
`recoverable`. Do not replace those fields with raw provider errors.
|
|
47
|
+
|
|
48
|
+
Abort replaced compositions and client disconnects. Retry provider throttles or transient failures only within an explicit request/time budget and only before visible output unless you have validated resume storage. Never silently restart after the viewer has begun watching.
|
|
49
|
+
|
|
50
|
+
Log safe codes with request/run IDs and latency. Keep private provider messages in server logs through `onError`; redact source, personalization, keys, tokens, and signed URLs.
|
|
51
|
+
|
|
52
|
+
Live acceptance artifacts use stable failure categories: `network`,
|
|
53
|
+
`authentication`, `model_not_found`, `rate_limit`, `provider`, `planner_parse`,
|
|
54
|
+
`scene_validation`, or `unknown`. Before diagnosing the SDK, probe the provider
|
|
55
|
+
with one minimal request using the same server-only credential and model. Check
|
|
56
|
+
DNS/proxy access, credential validity, model availability, account/rate limits,
|
|
57
|
+
and timeout settings in that order. Never copy raw provider payloads, streamed
|
|
58
|
+
deltas, prompts, or credentials into browser errors or CI artifacts.
|
|
59
|
+
|
|
60
|
+
Server handlers drop invalid generated parts by default. Each rejection calls
|
|
61
|
+
`onError` with the full internal reason, while the browser receives only the
|
|
62
|
+
recoverable `invalid_generated_part` diagnostic. Set `invalidPartBehavior: "fail"`
|
|
63
|
+
only when fail-fast generation is an intentional compatibility requirement.
|
|
64
|
+
|
|
65
|
+
`onComplete` runs exactly once only after `response.complete`. Terminal errors,
|
|
66
|
+
client disconnects, explicit aborts, and host timeouts do not call it;
|
|
67
|
+
`onError` receives internal failures, while cancellation remains a safe
|
|
68
|
+
`response.abort`. Abort and timeout signals always propagate to `streamText`.
|
|
69
|
+
The host owns timeout construction and retry policy. Do not retry invisibly
|
|
70
|
+
after any scene has reached the viewer.
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
[← Documentation home](../README.md) · [Previous: Prompt and input](prompt-and-input.md) · [Next: Next.js →](integrate-nextjs.md)
|
|
2
|
+
|
|
3
|
+
# Getting started
|
|
4
|
+
|
|
5
|
+
Install VanillaSky:
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install @vanillaskyai/video@0.1.0 ai @ai-sdk/openai
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
Set your provider key in `.env.local` (never commit it):
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
OPENAI_API_KEY=your-key
|
|
15
|
+
OPENAI_MODEL=gpt-4.1
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
Create one authenticated server route with `createVideoHandler`. Connect your
|
|
19
|
+
provider through `streamText`; keep the model, credentials, authentication, rate
|
|
20
|
+
limits, and media policy on the server:
|
|
21
|
+
|
|
22
|
+
```ts
|
|
23
|
+
import { openai } from "@ai-sdk/openai";
|
|
24
|
+
import { streamText } from "ai";
|
|
25
|
+
import { createVideoHandler } from "@vanillaskyai/video/server";
|
|
26
|
+
|
|
27
|
+
const model = openai(process.env.OPENAI_MODEL ?? "gpt-4.1");
|
|
28
|
+
|
|
29
|
+
const handle = createVideoHandler({
|
|
30
|
+
// Local development only. Replace with your session check before deploying.
|
|
31
|
+
authorize: (request) => {
|
|
32
|
+
if (process.env.NODE_ENV !== "development") return false;
|
|
33
|
+
const hostname = new URL(request.url).hostname;
|
|
34
|
+
return hostname === "localhost" || hostname === "127.0.0.1";
|
|
35
|
+
},
|
|
36
|
+
streamText: ({ systemPrompt, userPrompt, signal }) => streamText({
|
|
37
|
+
model,
|
|
38
|
+
system: systemPrompt,
|
|
39
|
+
prompt: userPrompt,
|
|
40
|
+
abortSignal: signal,
|
|
41
|
+
}),
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
export const POST = handle;
|
|
45
|
+
export const OPTIONS = handle;
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
The local bypass is intentionally fail-closed: it accepts only localhost while
|
|
49
|
+
Next.js is in development and denies every production request. Replace it with
|
|
50
|
+
your real session validation before deploying. For literal files and commands,
|
|
51
|
+
use the tested
|
|
52
|
+
[`examples/nextjs-quickstart` directory](https://github.com/VanillaSkyAi/video/tree/v0.1.0/examples/nextjs-quickstart).
|
|
53
|
+
|
|
54
|
+
`model` can come from any AI SDK provider, registry, gateway, compatible API,
|
|
55
|
+
or custom implementation. The application can choose a cheaper or faster model
|
|
56
|
+
per request without changing VanillaSky.
|
|
57
|
+
|
|
58
|
+
Call that route from React and render the player:
|
|
59
|
+
|
|
60
|
+
```tsx
|
|
61
|
+
"use client";
|
|
62
|
+
|
|
63
|
+
import { VideoPlayer, useVideo } from "@vanillaskyai/video/react";
|
|
64
|
+
|
|
65
|
+
export function GeneratedVideo({ input }: { input: string }) {
|
|
66
|
+
const video = useVideo();
|
|
67
|
+
|
|
68
|
+
return <>
|
|
69
|
+
<button onClick={() => { void video.generate({ input }); }}>Generate video</button>
|
|
70
|
+
{video.error && <p role="alert">Video generation failed.</p>}
|
|
71
|
+
<VideoPlayer {...video.playerProps} />
|
|
72
|
+
</>;
|
|
73
|
+
}
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
No template setup is required. VanillaSky advertises the trusted built-in
|
|
77
|
+
catalog to your LLM, validates its selected scenes, and lazy-loads only the
|
|
78
|
+
renderers the video uses.
|
|
79
|
+
|
|
80
|
+
`useVideo()` uses `/api/video` by default. Pass `endpoint` to use another route.
|
|
81
|
+
`generate()` returns a `Promise<Video>` if you need the completed config directly:
|
|
82
|
+
|
|
83
|
+
```ts
|
|
84
|
+
const completedVideo = await video.generate({ input });
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
The promise resolves only after successful completion. It rejects on terminal
|
|
88
|
+
generation errors and aborts; `video.error` contains the same typed error for
|
|
89
|
+
reactive UI.
|
|
90
|
+
|
|
91
|
+
`video.status` is `idle`, `streaming`, `complete`, `error`, or `aborted`.
|
|
92
|
+
`video.video` is the latest deterministic video, and `video.warnings` contains
|
|
93
|
+
bounded typed diagnostics safe to show or branch on. Provider limits and
|
|
94
|
+
content filters are available to the server through the `onComplete` summary;
|
|
95
|
+
surface that server-owned state separately if completeness matters to your
|
|
96
|
+
product.
|
|
97
|
+
|
|
98
|
+
Persist a completed `Video` as JSON and play it later without another model
|
|
99
|
+
request:
|
|
100
|
+
|
|
101
|
+
```tsx
|
|
102
|
+
<VideoPlayer video={savedVideo} />
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
Built-in templates work without additional props. Pass `templates` when the
|
|
106
|
+
saved video uses customer-owned templates.
|
|
107
|
+
|
|
108
|
+
All other input controls are optional. Continue with [input and opening
|
|
109
|
+
scenes](input-and-first-scene.md), [branding and personalization](branding-and-personalization.md),
|
|
110
|
+
or [media and soundtrack audio](media-and-audio.md). To edit or create visual
|
|
111
|
+
building blocks, see [custom templates](custom-templates.md).
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
[← Documentation home](../README.md) · [Previous: Provider integration](provider-integration.md) · [Next: Branding and personalization →](branding-and-personalization.md)
|
|
2
|
+
|
|
3
|
+
# Raw input and the opening
|
|
4
|
+
|
|
5
|
+
`VideoInput` is the small boundary between application truth and creative planning.
|
|
6
|
+
|
|
7
|
+
```ts
|
|
8
|
+
import type { VideoInput } from "@vanillaskyai/video";
|
|
9
|
+
|
|
10
|
+
const input: VideoInput = {
|
|
11
|
+
input: "Joris completed 142 customer conversations in Q2.",
|
|
12
|
+
instructions: "Celebrate the result. Never alter a metric.",
|
|
13
|
+
opening: "Joris, your Q2 recap is ready.",
|
|
14
|
+
personalization: { firstName: "Joris", period: "Q2" },
|
|
15
|
+
brand,
|
|
16
|
+
suppliedMedia,
|
|
17
|
+
audio: { src: "/audio/calm.mp3" },
|
|
18
|
+
orientation: "portrait",
|
|
19
|
+
maxDurationSec: 24,
|
|
20
|
+
};
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
## Raw source
|
|
24
|
+
|
|
25
|
+
Put every fact the response may claim in `input`. Use plain text, compact JSON, or a server-produced digest. Include units, periods, comparison bases, and provenance identifiers where ambiguity is possible. Keep creative direction in `instructions`; it may change emphasis and tone but cannot expand the factual boundary.
|
|
26
|
+
|
|
27
|
+
Bound request bytes and reject secret-shaped fields on the server. Do not pass provider keys, authorization headers, internal prompt fragments, or storage credentials as source material.
|
|
28
|
+
|
|
29
|
+
## Opening
|
|
30
|
+
|
|
31
|
+
When supplied, `opening` becomes a deterministic five-second `notification`
|
|
32
|
+
scene emitted before provider work. The SDK owns its scene ID, template, variables,
|
|
33
|
+
and timing so callers only provide the copy. It should:
|
|
34
|
+
|
|
35
|
+
- be personal or situational enough to feel intentional;
|
|
36
|
+
- require no network media lookup;
|
|
37
|
+
- remain true if generation later fails;
|
|
38
|
+
- fit comfortably in both supported orientations;
|
|
39
|
+
- be part of the final story, not a spinner disguised as a scene.
|
|
40
|
+
|
|
41
|
+
Omit `opening` when the generated story should begin with its first planned scene.
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
[← Documentation home](../README.md) · [Previous: Getting started](getting-started.md) · [Next: Provider integration →](provider-integration.md)
|
|
2
|
+
|
|
3
|
+
# Next.js integration
|
|
4
|
+
|
|
5
|
+
Install VanillaSky and one AI SDK provider:
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install @vanillaskyai/video@0.1.0 ai @ai-sdk/openai
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
Create an ignored `.env.local`:
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
OPENAI_API_KEY=your-key
|
|
15
|
+
OPENAI_MODEL=gpt-4.1
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
Create `app/api/video/route.ts`:
|
|
19
|
+
|
|
20
|
+
```ts
|
|
21
|
+
import { openai } from "@ai-sdk/openai";
|
|
22
|
+
import { streamText } from "ai";
|
|
23
|
+
import { createVideoHandler } from "@vanillaskyai/video/server";
|
|
24
|
+
|
|
25
|
+
const model = openai(process.env.OPENAI_MODEL ?? "gpt-4.1");
|
|
26
|
+
|
|
27
|
+
const handle = createVideoHandler({
|
|
28
|
+
// Local development only. Replace with your session check before deploying.
|
|
29
|
+
authorize: (request) => {
|
|
30
|
+
if (process.env.NODE_ENV !== "development") return false;
|
|
31
|
+
const hostname = new URL(request.url).hostname;
|
|
32
|
+
return hostname === "localhost" || hostname === "127.0.0.1";
|
|
33
|
+
},
|
|
34
|
+
streamText: ({ systemPrompt, userPrompt, signal }) => streamText({
|
|
35
|
+
model,
|
|
36
|
+
system: systemPrompt,
|
|
37
|
+
prompt: userPrompt,
|
|
38
|
+
abortSignal: signal,
|
|
39
|
+
}),
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
export const POST = handle;
|
|
43
|
+
export const OPTIONS = handle;
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
Create a Client Component:
|
|
47
|
+
|
|
48
|
+
```tsx
|
|
49
|
+
"use client";
|
|
50
|
+
|
|
51
|
+
import { VideoPlayer, useVideo } from "@vanillaskyai/video/react";
|
|
52
|
+
|
|
53
|
+
export function Video() {
|
|
54
|
+
const video = useVideo();
|
|
55
|
+
return <>
|
|
56
|
+
<button onClick={() => { void video.generate({
|
|
57
|
+
input: "Activation increased from 41% to 58%.",
|
|
58
|
+
personalization: { firstName: "Maya" },
|
|
59
|
+
}); }}>
|
|
60
|
+
Generate
|
|
61
|
+
</button>
|
|
62
|
+
{video.error && <p role="alert">Could not generate the video.</p>}
|
|
63
|
+
<VideoPlayer {...video.playerProps} />
|
|
64
|
+
</>;
|
|
65
|
+
}
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
That is the complete first path. Built-in templates need no registry setup.
|
|
69
|
+
The development authorization accepts only local requests and denies production
|
|
70
|
+
requests; replace it with your application's session validation before
|
|
71
|
+
deploying.
|
|
72
|
+
|
|
73
|
+
The copy-and-run app is in the
|
|
74
|
+
[`examples/nextjs-quickstart` directory](https://github.com/VanillaSkyAi/video/tree/v0.1.0/examples/nextjs-quickstart).
|
|
75
|
+
|
|
76
|
+
For another LLM, replace `openai(...)` with the matching AI SDK model. The route
|
|
77
|
+
shape and React code stay the same. See [Provider integration](provider-integration.md)
|
|
78
|
+
for adapters, authentication, diagnostics, and production controls. Add media,
|
|
79
|
+
audio, persistence, or custom templates only after the default path works.
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
[← Documentation home](../README.md) · [Previous: Branding and personalization](branding-and-personalization.md) · [Next: Custom templates →](custom-templates.md)
|
|
2
|
+
|
|
3
|
+
# Media and soundtrack audio
|
|
4
|
+
|
|
5
|
+
Applications own media and soundtrack audio. VanillaSky accepts ordinary
|
|
6
|
+
authorized URLs in the deterministic video model; it does not bundle tracks or
|
|
7
|
+
couple your app to a stock-media provider.
|
|
8
|
+
|
|
9
|
+
The 0.1 SDK does not provide narration, TTS, or speech synchronization.
|
|
10
|
+
If a product needs spoken audio, the application must create and synchronize
|
|
11
|
+
that experience outside this contract.
|
|
12
|
+
|
|
13
|
+
Only send source URLs you trust. Resolve provider results before generation,
|
|
14
|
+
pass approved assets through `suppliedMedia`, and preload the next scene's asset
|
|
15
|
+
before it becomes active. Keep provider credentials on the server.
|
|
16
|
+
|
|
17
|
+
Supplied URLs and data URIs are not copied into the LLM prompt. The model sees
|
|
18
|
+
an optional pool of opaque HTTPS-shaped references plus safe descriptive
|
|
19
|
+
metadata, selects only relevant assets, and the SDK restores their original
|
|
20
|
+
addresses on the server before scene validation. Supplying an asset does not
|
|
21
|
+
require the completed video to use it.
|
|
22
|
+
|
|
23
|
+
Audio timing, volume, beat markers, and fade-out remain part of the serialized
|
|
24
|
+
output video, so replay and export stay deterministic. The input stays at the
|
|
25
|
+
intent level: provide only the source URL and the SDK infers those playback
|
|
26
|
+
defaults. Hosting and licensing are the application's responsibility.
|
|
27
|
+
|
|
28
|
+
Host tracks in your application's public assets, object storage, or CDN, then
|
|
29
|
+
pass their URL through the normal video input. For example, a file at
|
|
30
|
+
`public/audio/calm.mp3` can be used without an SDK audio package:
|
|
31
|
+
|
|
32
|
+
```ts
|
|
33
|
+
const input = {
|
|
34
|
+
input: "Grounded source material",
|
|
35
|
+
audio: { src: "/audio/calm.mp3" },
|
|
36
|
+
maxDurationSec: 24,
|
|
37
|
+
};
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
The normalized output uses `trackId: "soundtrack"`, the video duration,
|
|
41
|
+
default beat detection, an empty beat-marker list, full volume, and a
|
|
42
|
+
three-second fade-out. Omit `audio` to let the server's synchronous
|
|
43
|
+
`selectAudio` callback choose from an app-owned catalog. Pass `audio: false`
|
|
44
|
+
to guarantee a silent video.
|
|
45
|
+
|
|
46
|
+
Keep the catalog and files in your application so you control caching,
|
|
47
|
+
licensing, and deployment. The SDK continues to handle playback, timing,
|
|
48
|
+
serialization, replay, and export from the supplied URL.
|
|
49
|
+
|
|
50
|
+
## Media providers
|
|
51
|
+
|
|
52
|
+
VanillaSky is provider-independent. Resolve image or video searches in your
|
|
53
|
+
server application before generation and pass the approved results through
|
|
54
|
+
`suppliedMedia`. The built-in planner sees opaque references and safe metadata;
|
|
55
|
+
it does not call a stock-media provider or turn `mediaKeyword` into a URL.
|
|
56
|
+
|
|
57
|
+
`allowMediaUrl` is an authorization hook for applications with their own custom
|
|
58
|
+
stream adapter. It validates a final URL; it does not search for, fetch, or
|
|
59
|
+
resolve media. The default 0.1 path needs no callback because every planner URL
|
|
60
|
+
must already be present in `suppliedMedia`.
|
|
61
|
+
|
|
62
|
+
Do not expose provider keys to React, allow arbitrary planner URLs, or add a
|
|
63
|
+
provider abstraction to the template API. Templates describe visual building
|
|
64
|
+
blocks; the application owns media retrieval, caching, licensing, and delivery.
|
|
65
|
+
|
|
66
|
+
For Pexels, keep `PEXELS_API_KEY` on the server, search before generation, and
|
|
67
|
+
pass only validated `images.pexels.com` or `videos.pexels.com` results through
|
|
68
|
+
`suppliedMedia`. The application remains responsible for attribution, search,
|
|
69
|
+
orientation filtering, MIME checks, timeouts, caching, and fallback behavior.
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
[← Documentation home](../README.md) · [Previous: Custom templates](custom-templates.md) · [Next: Production →](production.md)
|
|
2
|
+
|
|
3
|
+
# Motion and effects
|
|
4
|
+
|
|
5
|
+
VanillaSky motion is deterministic. The model selects from controlled names;
|
|
6
|
+
the SDK calculates the same frame from the same video JSON and playback time.
|
|
7
|
+
No generated CSS, animation code, or arbitrary transition is executed.
|
|
8
|
+
|
|
9
|
+
## Text archetypes
|
|
10
|
+
|
|
11
|
+
Text archetypes own a complete entrance, hold, and exit lifecycle:
|
|
12
|
+
|
|
13
|
+
- `subtle` — quiet supporting copy;
|
|
14
|
+
- `typewriter` — character-by-character reveal;
|
|
15
|
+
- `wordStagger` — sequential word build;
|
|
16
|
+
- `slam` — short, high-energy impact;
|
|
17
|
+
- `cinematic` — depth-led trailer movement;
|
|
18
|
+
- `heroWord` — one dominant word at a time.
|
|
19
|
+
|
|
20
|
+
Templates declare whether they have a tight or open text canvas. VanillaSky
|
|
21
|
+
normalizes incompatible or unknown values to a safe default.
|
|
22
|
+
|
|
23
|
+
## Background effects
|
|
24
|
+
|
|
25
|
+
Templates that support background motion can use:
|
|
26
|
+
|
|
27
|
+
- `static`;
|
|
28
|
+
- `slow-zoom-in`;
|
|
29
|
+
- `slow-zoom-out`;
|
|
30
|
+
- `ken-burns`;
|
|
31
|
+
- `drift`;
|
|
32
|
+
- `pulse`;
|
|
33
|
+
- `breathe`;
|
|
34
|
+
- `slow-tilt`;
|
|
35
|
+
- `camera-shake`.
|
|
36
|
+
|
|
37
|
+
`slow-zoom-in` is the default. Use `static` as the explicit opt-out.
|
|
38
|
+
|
|
39
|
+
## Scene continuity
|
|
40
|
+
|
|
41
|
+
`style.defaultTransition` accepts `crossfade` or `fade`. The player applies it
|
|
42
|
+
only when two ranges are contiguous (allowing floating-point arithmetic noise)
|
|
43
|
+
and both templates declare `usesGlobalTransition: true` with valid
|
|
44
|
+
`transitionTiming` metadata. It is also conditional on the effective backdrop:
|
|
45
|
+
the 300 ms outer crossfade runs only when the resolved background media changes.
|
|
46
|
+
Scenes that share the brand gradient, or the same resolved media backdrop, do
|
|
47
|
+
not crossfade. This keeps one stable background visible while each template
|
|
48
|
+
plays its own entrance, hold, and exit choreography.
|
|
49
|
+
|
|
50
|
+
Animated brand gradients use a closed, eased loop per scene. Every gradient
|
|
51
|
+
family reaches the exact same zero-velocity frame at progress `0` and `1`, and
|
|
52
|
+
content-seeded variation converges before the boundary. A same-gradient cut
|
|
53
|
+
therefore changes only the foreground template; the backdrop cannot jump or
|
|
54
|
+
flash between scene-specific phases.
|
|
55
|
+
|
|
56
|
+
The player owns a persistent brand-color backdrop beneath every scene. Built-in
|
|
57
|
+
renderers preload through the same component state used for playback, so their
|
|
58
|
+
first frame does not suspend when a new template type appears. If a genuinely
|
|
59
|
+
cold custom renderer does suspend, its transparent loading frame reveals the
|
|
60
|
+
brand backdrop rather than a black canvas. Scene media still belongs to the
|
|
61
|
+
scene and covers that base only while the media scene is active.
|
|
62
|
+
|
|
63
|
+
During a changed-media overlap, the current scene continues to its exact end.
|
|
64
|
+
The incoming component may be pre-mounted for media readiness, but remains
|
|
65
|
+
frozen at its true initial frame (`progress === motionProgress === 0`) until its
|
|
66
|
+
declared range begins. The current scene remains interactive and exposed to
|
|
67
|
+
assistive technology until the exact timeline boundary; the preview layer
|
|
68
|
+
remains inert throughout the overlap.
|
|
69
|
+
|
|
70
|
+
Templates always receive raw semantic scene time as `progress`. Grounded
|
|
71
|
+
numbers, media time, screen sequences, and other content state must use that
|
|
72
|
+
clock. An opted-in template also receives `motionProgress`; throughout active
|
|
73
|
+
playback it is the same complete `0→1` clock. The player never pre-advances,
|
|
74
|
+
caps, rewinds, or skips a template's entrance, internal motion, exit, or
|
|
75
|
+
terminal frame. `transitionTiming` records audited entry-ready and readable
|
|
76
|
+
checkpoints for verification, but does not remap runtime time. Use
|
|
77
|
+
`motionProgress` only for presentation and fall back to `progress` when it is
|
|
78
|
+
absent. Templates opt out by default.
|
|
79
|
+
|
|
80
|
+
An incoming changed-media fade can expose the template's initial frame while
|
|
81
|
+
its raw semantic clock is still zero.
|
|
82
|
+
Do not show a synthetic `0%`, `0x`, empty total, or another value that could be
|
|
83
|
+
mistaken for sourced content. Keep the grounded frame and CTA visible, but mark
|
|
84
|
+
only a transient value wrapper with
|
|
85
|
+
`visibility: var(--vanillasky-transition-semantic-visibility, visible)`. The
|
|
86
|
+
player hides that wrapper for the incoming preview and reveals it as soon as
|
|
87
|
+
the same mounted scene becomes active. This guard does not change `progress`,
|
|
88
|
+
media time, the component lifecycle, or final values.
|
|
89
|
+
|
|
90
|
+
Undefined or unknown transition names preserve a hard cut and unmodified local
|
|
91
|
+
motion. Overlapping ranges also hard-cut. A timeline gap renders the owned brand
|
|
92
|
+
background instead of replaying an earlier scene.
|
|
93
|
+
|
|
94
|
+
## Reduced motion
|
|
95
|
+
|
|
96
|
+
The player respects `prefers-reduced-motion`. Applications should keep a
|
|
97
|
+
visible playback control and must not rely on motion alone to communicate a
|
|
98
|
+
fact or state.
|
|
99
|
+
|
|
100
|
+
## Preview the catalog
|
|
101
|
+
|
|
102
|
+
The public [motion and effects gallery](https://vanillasky.ai/motion/) renders
|
|
103
|
+
the real SDK effects with their exact configuration. Use it to choose a
|
|
104
|
+
controlled effect, then keep the initial integration on the defaults unless a
|
|
105
|
+
specific editorial need calls for an override.
|
|
106
|
+
|
|
107
|
+
[← Documentation home](../README.md) · [Previous: Custom templates](custom-templates.md) · [Next: Production →](production.md)
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
[← Documentation home](../README.md) · [Previous: Core concepts](concepts.md) · [Next: Streaming protocol →](streaming-protocol.md)
|
|
2
|
+
|
|
3
|
+
# Persistence and replay
|
|
4
|
+
|
|
5
|
+
A completed `Video` is ordinary JSON owned by your application. VanillaSky
|
|
6
|
+
does not provide a database or hosted media store. Save the value returned by
|
|
7
|
+
`await video.generate(...)` with the platform-native `JSON.stringify`; there is
|
|
8
|
+
no SDK serializer.
|
|
9
|
+
|
|
10
|
+
Every stored video has `schemaVersion: "0.1"`. This storage version is separate
|
|
11
|
+
from streaming protocol `0.4`. The 0.1 policy supports the current storage
|
|
12
|
+
schema only: there are no compatibility aliases or implicit coercions.
|
|
13
|
+
|
|
14
|
+
## Load at the storage boundary
|
|
15
|
+
|
|
16
|
+
Treat values loaded from a database, object store, API, or file as `unknown`.
|
|
17
|
+
Parse them before using them in application code:
|
|
18
|
+
|
|
19
|
+
<!-- verify:persistence-example:start -->
|
|
20
|
+
```tsx
|
|
21
|
+
import { getVideoDuration, parseVideo } from "@vanillaskyai/video";
|
|
22
|
+
import { VideoPlayer } from "@vanillaskyai/video/react";
|
|
23
|
+
|
|
24
|
+
export function SavedVideo({ storedJson }: { storedJson: string }) {
|
|
25
|
+
const savedVideo = parseVideo(JSON.parse(storedJson));
|
|
26
|
+
|
|
27
|
+
return <>
|
|
28
|
+
<p>{getVideoDuration(savedVideo)} seconds</p>
|
|
29
|
+
<VideoPlayer video={savedVideo} autoPlay={false} />
|
|
30
|
+
</>;
|
|
31
|
+
}
|
|
32
|
+
```
|
|
33
|
+
<!-- verify:persistence-example:end -->
|
|
34
|
+
|
|
35
|
+
The release verifier compiles this exact documented snippet against the packed
|
|
36
|
+
SDK artifact, including its root and React subpath imports.
|
|
37
|
+
|
|
38
|
+
`parseVideo(value: unknown)` validates the complete shape, known fields,
|
|
39
|
+
resolved brand and style, audio, metadata, unique scenes, timing, and JSON-safe
|
|
40
|
+
template variables. It returns a detached, deeply frozen `Video`, so later
|
|
41
|
+
changes to the loaded object cannot mutate player state.
|
|
42
|
+
|
|
43
|
+
Invalid data throws `VideoValidationError` with `code: "invalid_video"`.
|
|
44
|
+
Unknown or future storage versions throw the same error class with
|
|
45
|
+
`code: "unsupported_video_version"`. `<VideoPlayer video={value} />` repeats
|
|
46
|
+
this boundary validation and rejects the entire value before any renderer
|
|
47
|
+
runs; it never renders a partial future document.
|
|
48
|
+
|
|
49
|
+
## Retention
|
|
50
|
+
|
|
51
|
+
By default, completed snapshots omit raw source, creative instructions, and
|
|
52
|
+
the supplied-media URL index. A customer-owned route may retain individual
|
|
53
|
+
fields only when its privacy and deletion policy allows it:
|
|
54
|
+
|
|
55
|
+
```ts
|
|
56
|
+
createVideoHandler({
|
|
57
|
+
authorize: verifySession,
|
|
58
|
+
streamText,
|
|
59
|
+
snapshotRetention: {
|
|
60
|
+
source: true,
|
|
61
|
+
instructions: true,
|
|
62
|
+
suppliedMediaUrls: true,
|
|
63
|
+
},
|
|
64
|
+
});
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
Opt-in values are bounded in the snapshot:
|
|
68
|
+
|
|
69
|
+
- source: 16,384 characters;
|
|
70
|
+
- instructions: 4,096 characters;
|
|
71
|
+
- supplied-media index: 16 URLs, each at most 2,048 characters.
|
|
72
|
+
|
|
73
|
+
These metadata limits do not replace a host retention policy. Renderable scene
|
|
74
|
+
variables may still contain a media URL when that asset is necessary for
|
|
75
|
+
replay. Store only approved assets and avoid signed URLs whose lifetime is
|
|
76
|
+
shorter than the replay window.
|
|
77
|
+
|
|
78
|
+
## Storage ownership
|
|
79
|
+
|
|
80
|
+
The host owns the database, object storage, tenant authorization, encryption,
|
|
81
|
+
deletion schedule, backups, quotas, and media URL expiry. Persist the final
|
|
82
|
+
`Video` document atomically with your own tenant and record identifiers. Do not
|
|
83
|
+
use the protocol checksum as an authorization or tenancy control.
|
|
84
|
+
|
|
85
|
+
The checksum on `response.complete` is a deterministic, non-cryptographic
|
|
86
|
+
drift detector. It is not proof of authenticity and is not a signature. Use
|
|
87
|
+
normal authenticated storage and a cryptographic integrity mechanism when
|
|
88
|
+
those properties are required.
|
|
89
|
+
|
|
90
|
+
Saved replay makes zero generation endpoint or model-provider requests. It is
|
|
91
|
+
not necessarily zero network traffic: audio, images, videos, fonts, and
|
|
92
|
+
customer-owned renderers may make separate media network requests.
|
|
93
|
+
|
|
94
|
+
When a saved video uses project-owned templates, provide the matching browser
|
|
95
|
+
registry: `<VideoPlayer video={savedVideo} templates={templates} />`.
|