@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.
Files changed (162) hide show
  1. package/CHANGELOG.md +45 -0
  2. package/LICENSE +201 -0
  3. package/PUBLIC-API.md +276 -0
  4. package/README.md +152 -0
  5. package/SECURITY.md +22 -0
  6. package/SUPPORT.md +28 -0
  7. package/bin/vanillasky.js +3 -0
  8. package/dist/bg-confetti-WNQXS7ZS.js +89 -0
  9. package/dist/bg-emoji-ETI47GLS.js +150 -0
  10. package/dist/bg-media-SVUZAMGX.js +99 -0
  11. package/dist/brand-message-F3RD4P3P.js +253 -0
  12. package/dist/builtin-server-F7YXRVCH.js +8 -0
  13. package/dist/catalog-types-BIhSpOWK.d.ts +74 -0
  14. package/dist/chart-bar-CNSC7BNK.js +304 -0
  15. package/dist/chart-counter-MCRHKN77.js +299 -0
  16. package/dist/chart-progress-ring-RMI4A7ZK.js +296 -0
  17. package/dist/check-runtime.d.ts +15 -0
  18. package/dist/check-runtime.js +102 -0
  19. package/dist/chunk-2E6T633S.js +27 -0
  20. package/dist/chunk-352ZZCET.js +83 -0
  21. package/dist/chunk-73NTSFFI.js +81 -0
  22. package/dist/chunk-A2X45FET.js +46 -0
  23. package/dist/chunk-ABPKM4GK.js +187 -0
  24. package/dist/chunk-ABZACD4G.js +216 -0
  25. package/dist/chunk-BCRUUJ6A.js +756 -0
  26. package/dist/chunk-CHLGW22F.js +3551 -0
  27. package/dist/chunk-CJ7F2CE6.js +104 -0
  28. package/dist/chunk-CR7FE7BW.js +109 -0
  29. package/dist/chunk-E7CL7UPB.js +109 -0
  30. package/dist/chunk-EE6PK6XC.js +18 -0
  31. package/dist/chunk-ERY4S6QV.js +385 -0
  32. package/dist/chunk-EVG2ZYIY.js +125 -0
  33. package/dist/chunk-FNAEQ7QO.js +155 -0
  34. package/dist/chunk-FQ47WXAJ.js +38 -0
  35. package/dist/chunk-FSG2PYWG.js +336 -0
  36. package/dist/chunk-GHJEAP4O.js +504 -0
  37. package/dist/chunk-GRMUV7QY.js +262 -0
  38. package/dist/chunk-IFOW65Z6.js +205 -0
  39. package/dist/chunk-IXCUY3B3.js +786 -0
  40. package/dist/chunk-L5BTTCZV.js +97 -0
  41. package/dist/chunk-LMG3GHLL.js +107 -0
  42. package/dist/chunk-OOPIETLW.js +101 -0
  43. package/dist/chunk-P22HHFDK.js +43 -0
  44. package/dist/chunk-QAAO7OAY.js +19 -0
  45. package/dist/chunk-S7HFLYNN.js +35 -0
  46. package/dist/chunk-SPVTJH3F.js +24 -0
  47. package/dist/chunk-W2XJKG6C.js +3177 -0
  48. package/dist/chunk-XB7LH4J2.js +301 -0
  49. package/dist/chunk-XGRA2MUV.js +242 -0
  50. package/dist/chunk-XTOXFMLM.js +105 -0
  51. package/dist/chunk-YC5MFXA5.js +132 -0
  52. package/dist/cli.d.ts +1 -0
  53. package/dist/cli.js +1681 -0
  54. package/dist/compose-video-CN3VSWST.js +13 -0
  55. package/dist/cta-logo-SVA5GP2A.js +209 -0
  56. package/dist/cta-media-E7VLLK42.js +215 -0
  57. package/dist/events-_wfjbmEp.d.ts +81 -0
  58. package/dist/incoming-call-62MFZL3Z.js +320 -0
  59. package/dist/index.d.ts +15 -0
  60. package/dist/index.js +14 -0
  61. package/dist/infographic-before-after-AXCDAWND.js +460 -0
  62. package/dist/infographic-feature-list-GW5GUE6O.js +266 -0
  63. package/dist/infographic-problem-solution-7NURJANA.js +340 -0
  64. package/dist/infographic-stat-row-J7IZR6ZN.js +241 -0
  65. package/dist/infographic-steps-VSZM6F5M.js +400 -0
  66. package/dist/kit-BMNcHqa9.d.ts +79 -0
  67. package/dist/prompt-input-LOUCRXCY.js +236 -0
  68. package/dist/react.d.ts +81 -0
  69. package/dist/react.js +835 -0
  70. package/dist/reaction-QG4CYZLQ.js +119 -0
  71. package/dist/server.d.ts +141 -0
  72. package/dist/server.js +498 -0
  73. package/dist/showcase-code-RUTFZR3H.js +351 -0
  74. package/dist/showcase-phone-6CP6XEU5.js +272 -0
  75. package/dist/showcase-terminal-MGAEQWPR.js +306 -0
  76. package/dist/showcase-web-Y4W2HVRJ.js +390 -0
  77. package/dist/social-conversation-75SD2637.js +695 -0
  78. package/dist/social-milestone-XI2QE5BO.js +292 -0
  79. package/dist/social-notification-2UOHQCUY.js +244 -0
  80. package/dist/social-review-stack-FDYZGZTJ.js +239 -0
  81. package/dist/social-testimonial-OQ7N52GJ.js +284 -0
  82. package/dist/social-tweet-5OLCNPPC.js +295 -0
  83. package/dist/state-DZcKuS32.d.ts +3 -0
  84. package/dist/system-prompt-A6UXNTPK.js +8 -0
  85. package/dist/template-catalog.d.ts +24 -0
  86. package/dist/template-catalog.js +6 -0
  87. package/dist/templates.d.ts +25 -0
  88. package/dist/templates.js +19 -0
  89. package/dist/test.d.ts +149 -0
  90. package/dist/test.js +272 -0
  91. package/dist/text-stream-6XHC7J47.js +10 -0
  92. package/dist/types-B-PZR6G_.d.ts +208 -0
  93. package/dist/validate-T7GBU2YF.js +8 -0
  94. package/docs/agent-integration.md +39 -0
  95. package/docs/architecture.md +84 -0
  96. package/docs/branding-and-personalization.md +87 -0
  97. package/docs/concepts.md +106 -0
  98. package/docs/custom-templates.md +329 -0
  99. package/docs/customization.md +141 -0
  100. package/docs/errors.md +70 -0
  101. package/docs/getting-started.md +111 -0
  102. package/docs/input-and-first-scene.md +41 -0
  103. package/docs/integrate-nextjs.md +79 -0
  104. package/docs/media-and-audio.md +69 -0
  105. package/docs/motion-and-effects.md +107 -0
  106. package/docs/persistence.md +95 -0
  107. package/docs/production.md +122 -0
  108. package/docs/prompt-and-input.md +185 -0
  109. package/docs/provider-integration.md +115 -0
  110. package/docs/reference/protocol.md +96 -0
  111. package/docs/reference/provider-adapters.md +158 -0
  112. package/docs/responsive-orientation.md +37 -0
  113. package/docs/security.md +38 -0
  114. package/docs/streaming-protocol.md +15 -0
  115. package/docs/testing.md +137 -0
  116. package/docs/use-cases.md +59 -0
  117. package/examples/custom-template/README.md +19 -0
  118. package/examples/custom-template/minimal-text.tsx +82 -0
  119. package/examples/custom-template/structured-data.tsx +104 -0
  120. package/examples/custom-template/supplied-media.tsx +92 -0
  121. package/examples/nextjs-quickstart/.env.example +2 -0
  122. package/examples/nextjs-quickstart/README.md +22 -0
  123. package/examples/nextjs-quickstart/next-env.d.ts +4 -0
  124. package/examples/nextjs-quickstart/package.json +24 -0
  125. package/examples/nextjs-quickstart/src/app/api/video/route.ts +22 -0
  126. package/examples/nextjs-quickstart/src/app/layout.tsx +5 -0
  127. package/examples/nextjs-quickstart/src/app/page.tsx +31 -0
  128. package/examples/nextjs-quickstart/tsconfig.json +26 -0
  129. package/package.json +167 -0
  130. package/registry/items/backgrounds.json +52 -0
  131. package/registry/items/barChart.json +237 -0
  132. package/registry/items/beforeAfter.json +180 -0
  133. package/registry/items/bigNumber.json +225 -0
  134. package/registry/items/brandMessage.json +158 -0
  135. package/registry/items/cardList.json +206 -0
  136. package/registry/items/chatMessenger.json +151 -0
  137. package/registry/items/chatWhatsapp.json +151 -0
  138. package/registry/items/codeEditor.json +184 -0
  139. package/registry/items/confetti.json +107 -0
  140. package/registry/items/ctaLogo.json +160 -0
  141. package/registry/items/ctaMedia.json +193 -0
  142. package/registry/items/emojiBurst.json +101 -0
  143. package/registry/items/incomingCall.json +170 -0
  144. package/registry/items/media.json +179 -0
  145. package/registry/items/milestone.json +205 -0
  146. package/registry/items/motion.json +45 -0
  147. package/registry/items/notification.json +182 -0
  148. package/registry/items/phoneMockup.json +254 -0
  149. package/registry/items/problemSolution.json +186 -0
  150. package/registry/items/progressRing.json +199 -0
  151. package/registry/items/promptInput.json +148 -0
  152. package/registry/items/reaction.json +170 -0
  153. package/registry/items/reviewStack.json +224 -0
  154. package/registry/items/steps.json +207 -0
  155. package/registry/items/template-context.json +31 -0
  156. package/registry/items/terminal.json +212 -0
  157. package/registry/items/testimonial.json +164 -0
  158. package/registry/items/theme.json +47 -0
  159. package/registry/items/tripleStats.json +228 -0
  160. package/registry/items/tweet.json +203 -0
  161. package/registry/items/typography.json +47 -0
  162. 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} />`.