@vanillaskyai/video 0.5.8 → 0.7.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 (135) hide show
  1. package/CHANGELOG.md +178 -0
  2. package/PUBLIC-API.md +129 -9
  3. package/README.md +70 -108
  4. package/dist/{bg-confetti-SHQI7ATB.js → bg-confetti-HITCGLGD.js} +1 -1
  5. package/dist/{bg-emoji-XYFRA63Q.js → bg-emoji-VU4UZYZN.js} +1 -1
  6. package/dist/{bg-media-FEBATBT2.js → bg-media-L34PDQXJ.js} +4 -4
  7. package/dist/{brand-message-P56WEJPG.js → brand-message-FUJ4STFO.js} +3 -3
  8. package/dist/{builtin-server-YHEZ2JRF.js → builtin-server-KB7FKF6A.js} +2 -2
  9. package/dist/{chart-bar-ZB45ZRKH.js → chart-bar-EGPHJATL.js} +4 -4
  10. package/dist/{chart-counter-HFYNE4HT.js → chart-counter-5FFWQNZO.js} +4 -4
  11. package/dist/{chart-progress-ring-ZKMLRGSB.js → chart-progress-ring-JMXQLMLC.js} +4 -4
  12. package/dist/check-runtime.js +2 -2
  13. package/dist/{chunk-66MNRUCR.js → chunk-2PYO6VAC.js} +51 -3
  14. package/dist/{chunk-ZQQQAAKP.js → chunk-2XT4MZ76.js} +33 -2
  15. package/dist/chunk-5CDAM24P.js +31 -0
  16. package/dist/{chunk-PNS52FL4.js → chunk-5SLENAJW.js} +19 -6
  17. package/dist/{chunk-EFL34TXF.js → chunk-AITKH6QT.js} +24 -8
  18. package/dist/{chunk-HT6BGERF.js → chunk-FRN6WKHA.js} +28 -10
  19. package/dist/{chunk-FZHMQFG3.js → chunk-GAIOHGNR.js} +5 -21
  20. package/dist/{chunk-3RV4YKB3.js → chunk-JNN3EYVP.js} +51 -26
  21. package/dist/{chunk-NGY3TNND.js → chunk-K746NUIP.js} +70 -50
  22. package/dist/{chunk-HVCPEAQF.js → chunk-LKBZX7GV.js} +9 -6
  23. package/dist/{chunk-G66Z5CWR.js → chunk-MMUXVA47.js} +27 -3
  24. package/dist/{chunk-6M6DATLW.js → chunk-MXZSDGZQ.js} +6 -5
  25. package/dist/{chunk-3OU7HIEB.js → chunk-RE4IMWJR.js} +15 -31
  26. package/dist/{chunk-FVTMYS6U.js → chunk-RGF452LL.js} +1 -1
  27. package/dist/{chunk-34O5BY6X.js → chunk-RXHW4EP4.js} +21 -4
  28. package/dist/chunk-SKRGRKHY.js +142 -0
  29. package/dist/{chunk-TCEHK2BW.js → chunk-YJJC4N4D.js} +2 -1
  30. package/dist/cli.js +438 -30
  31. package/dist/{compose-video-JWIEDTEJ.js → compose-video-PD6LKKRK.js} +4 -4
  32. package/dist/{cta-logo-S4OMTNXD.js → cta-logo-YR7LYUR5.js} +3 -3
  33. package/dist/{cta-media-EROTHDRR.js → cta-media-DQJBKUO2.js} +4 -4
  34. package/dist/{events-BBP30j3c.d.ts → events-B6qS1Lsb.d.ts} +2 -2
  35. package/dist/{incoming-call-5IWJ2TAS.js → incoming-call-54KQ7O5A.js} +3 -3
  36. package/dist/index.d.ts +89 -3
  37. package/dist/index.js +12 -1
  38. package/dist/{infographic-before-after-2I7YZVDD.js → infographic-before-after-ROS52GOW.js} +1 -1
  39. package/dist/{infographic-feature-list-7SK65U3K.js → infographic-feature-list-E7MGUDAN.js} +4 -4
  40. package/dist/{infographic-problem-solution-IW35S6BR.js → infographic-problem-solution-3S6NGO5N.js} +4 -4
  41. package/dist/{infographic-stat-row-XHUZZPDM.js → infographic-stat-row-SYB7AB7H.js} +4 -4
  42. package/dist/{infographic-steps-JDDEDI7X.js → infographic-steps-KCE6JWE2.js} +4 -4
  43. package/dist/{kit-DEG3fcaL.d.ts → kit-8g46H2RZ.d.ts} +1 -1
  44. package/dist/{prompt-input-6ZWXEL3V.js → prompt-input-MHX4O42G.js} +3 -3
  45. package/dist/react.d.ts +200 -6
  46. package/dist/react.js +1859 -43
  47. package/dist/{reaction-Z3JIM3MQ.js → reaction-SLEGR3BE.js} +4 -4
  48. package/dist/{scene-video-backdrop-IL4F2SVD.js → scene-video-backdrop-XFPX4O3I.js} +2 -1
  49. package/dist/server.d.ts +104 -4
  50. package/dist/server.js +976 -26
  51. package/dist/{showcase-code-NWQAAJIA.js → showcase-code-2HLAVKBM.js} +4 -4
  52. package/dist/{showcase-phone-5UW45ATT.js → showcase-phone-Q4HEO4LH.js} +4 -4
  53. package/dist/{showcase-terminal-UVOD25UF.js → showcase-terminal-DL45LUFG.js} +4 -4
  54. package/dist/{showcase-web-3AW6VHIG.js → showcase-web-2QZO73FZ.js} +4 -4
  55. package/dist/{social-milestone-KPKHFOB5.js → social-milestone-GEFUQSBF.js} +3 -3
  56. package/dist/{social-notification-EDOU6TZB.js → social-notification-FJ5VHVLK.js} +3 -3
  57. package/dist/{social-review-stack-EZOO33SV.js → social-review-stack-KXQCKFRP.js} +3 -3
  58. package/dist/{social-testimonial-DLOINMMY.js → social-testimonial-JZ7CHC7S.js} +3 -3
  59. package/dist/{social-tweet-6JUM323G.js → social-tweet-UCDXM25F.js} +3 -3
  60. package/dist/{system-prompt-4I6Z5HK3.js → system-prompt-RRXIWDDD.js} +5 -3
  61. package/dist/template-catalog.js +1 -1
  62. package/dist/templates.d.ts +3 -3
  63. package/dist/test.d.ts +2 -2
  64. package/dist/test.js +3 -3
  65. package/dist/{text-stream-J6EDDJP4.js → text-stream-FW4BLBAL.js} +2 -2
  66. package/dist/{types-BV9IqExh.d.ts → types-2wHBqtg8.d.ts} +29 -1
  67. package/dist/types-DrABlRa7.d.ts +46 -0
  68. package/docs/agent-integration.md +49 -25
  69. package/docs/architecture.md +37 -22
  70. package/docs/branding-and-personalization.md +1 -1
  71. package/docs/concepts.md +14 -0
  72. package/docs/custom-templates.md +9 -9
  73. package/docs/customization.md +41 -5
  74. package/docs/getting-started.md +81 -84
  75. package/docs/media-and-audio.md +113 -117
  76. package/docs/persistence.md +1 -1
  77. package/docs/production.md +81 -92
  78. package/docs/prompt-and-input.md +71 -161
  79. package/docs/provider-integration.md +91 -49
  80. package/docs/responsive-orientation.md +1 -1
  81. package/docs/security.md +6 -5
  82. package/docs/streaming-protocol.md +1 -1
  83. package/docs/testing.md +51 -43
  84. package/examples/custom-template/README.md +1 -1
  85. package/package.json +20 -21
  86. package/registry/items/backgrounds.json +2 -2
  87. package/registry/items/barChart.json +6 -4
  88. package/registry/items/beforeAfter.json +1 -1
  89. package/registry/items/bigNumber.json +4 -3
  90. package/registry/items/brandMessage.json +3 -2
  91. package/registry/items/cardList.json +6 -4
  92. package/registry/items/codeEditor.json +4 -3
  93. package/registry/items/confetti.json +1 -1
  94. package/registry/items/ctaLogo.json +3 -2
  95. package/registry/items/ctaMedia.json +4 -3
  96. package/registry/items/emojiBurst.json +1 -1
  97. package/registry/items/incomingCall.json +3 -2
  98. package/registry/items/media.json +4 -3
  99. package/registry/items/milestone.json +3 -2
  100. package/registry/items/notification.json +3 -2
  101. package/registry/items/phoneMockup.json +4 -3
  102. package/registry/items/problemSolution.json +4 -3
  103. package/registry/items/progressRing.json +4 -3
  104. package/registry/items/promptInput.json +3 -2
  105. package/registry/items/reaction.json +2 -2
  106. package/registry/items/reviewStack.json +3 -2
  107. package/registry/items/steps.json +6 -4
  108. package/registry/items/terminal.json +5 -4
  109. package/registry/items/testimonial.json +3 -2
  110. package/registry/items/tripleStats.json +4 -3
  111. package/registry/items/tweet.json +3 -2
  112. package/registry/items/webMockup.json +4 -3
  113. package/starters/video-chat/.env.example +16 -0
  114. package/starters/video-chat/README.md +63 -0
  115. package/starters/video-chat/index.html +12 -0
  116. package/starters/video-chat/package.json +28 -0
  117. package/starters/video-chat/server.ts +152 -0
  118. package/starters/video-chat/src/main.tsx +8 -0
  119. package/starters/video-chat/stock.ts +139 -0
  120. package/starters/video-chat/tsconfig.json +20 -0
  121. package/starters/video-chat/vite.config.ts +81 -0
  122. package/styles/video-chat.css +713 -0
  123. package/dist/chunk-C6WVCZRW.js +0 -19
  124. package/docs/input-and-first-scene.md +0 -64
  125. package/docs/integrate-nextjs.md +0 -86
  126. package/docs/live-channels.md +0 -149
  127. package/docs/use-cases.md +0 -59
  128. package/examples/nextjs-quickstart/.env.example +0 -2
  129. package/examples/nextjs-quickstart/README.md +0 -27
  130. package/examples/nextjs-quickstart/next-env.d.ts +0 -4
  131. package/examples/nextjs-quickstart/package.json +0 -25
  132. package/examples/nextjs-quickstart/src/app/api/video/route.ts +0 -24
  133. package/examples/nextjs-quickstart/src/app/layout.tsx +0 -5
  134. package/examples/nextjs-quickstart/src/app/page.tsx +0 -31
  135. package/examples/nextjs-quickstart/tsconfig.json +0 -26
@@ -24,28 +24,28 @@ List the effective catalog, including project-owned templates, before choosing
24
24
  what to build or copy:
25
25
 
26
26
  ```bash
27
- npx vanillasky list
27
+ npx vanillasky templates list
28
28
  ```
29
29
 
30
30
  Create an original template:
31
31
 
32
32
  ```bash
33
- npx vanillasky create customer-health
34
- npx vanillasky describe customer-health
33
+ npx vanillasky templates create customer-health
34
+ npx vanillasky templates describe customer-health
35
35
  ```
36
36
 
37
37
  Or copy a built-in when its behavior is already close:
38
38
 
39
39
  ```bash
40
- npx vanillasky add bigNumber
40
+ npx vanillasky templates add bigNumber
41
41
  ```
42
42
 
43
43
  Then edit the owned `.tsx` file, regenerate the two small registries, and check
44
44
  the complete contract:
45
45
 
46
46
  ```bash
47
- npx vanillasky sync
48
- npx vanillasky check
47
+ npx vanillasky templates sync
48
+ npx vanillasky templates check
49
49
  ```
50
50
 
51
51
  For an original template, the source is
@@ -62,8 +62,8 @@ registry parity.
62
62
  Preview either operation without applying the proposed file writes:
63
63
 
64
64
  ```bash
65
- npx vanillasky add bigNumber --dry-run
66
- npx vanillasky add bigNumber --diff
65
+ npx vanillasky templates add bigNumber --dry-run
66
+ npx vanillasky templates add bigNumber --diff
67
67
  ```
68
68
 
69
69
  `--dry-run` lists every proposed file and `--diff` shows its content changes,
@@ -79,7 +79,7 @@ An edited file is never replaced unless you explicitly pass `--overwrite`.
79
79
 
80
80
  ## One file is the contract
81
81
 
82
- The file created by `vanillasky create` is a complete working template. Keep
82
+ The file created by `vanillasky templates create` is a complete working template. Keep
83
83
  these concerns together:
84
84
 
85
85
  - `useWhen` and `avoidWhen` tell the AI when the visual is appropriate;
@@ -2,6 +2,42 @@
2
2
 
3
3
  # Customization
4
4
 
5
+ ## Video chat interface
6
+
7
+ The default `VideoChat` interface is already responsive and accessible. Pass a
8
+ custom welcome heading and a root class when the application needs its own copy
9
+ or chrome colors:
10
+
11
+ ```tsx
12
+ <VideoChat
13
+ className="acme-chat"
14
+ welcomeTitle={<>Ask Acme<br />See the answer</>}
15
+ options={{ brand: acmeBrand }}
16
+ />
17
+ ```
18
+
19
+ Override the scoped custom properties after importing
20
+ `@vanillaskyai/video/video-chat.css`; the selectors and values stay local to
21
+ that instance:
22
+
23
+ ```css
24
+ .acme-chat {
25
+ --vs-accent: #0057ff;
26
+ --vs-accent-deep: #003fb8;
27
+ --vs-voice: #e11d74;
28
+ --vs-font: "Inter", sans-serif;
29
+ --vs-radius-lg: 1rem;
30
+ }
31
+ ```
32
+
33
+ Use `options` for the endpoint, templates, orientation, visual brand, request
34
+ headers, and an optional custom voice. Provider capabilities are discovered
35
+ from the server. Use `useVideoChat()` only when the application needs to own the
36
+ entire interface.
37
+
38
+ The sections below customize lower-level, one-shot video composition. They are
39
+ not required for the default chat.
40
+
5
41
  ## Background and semantic brand
6
42
 
7
43
  Omit brand configuration to use the standard `cosmic` background. Prefer a
@@ -125,16 +161,16 @@ Pass `audio: { src }` for a specific track, omit `audio` to let the server choos
125
161
  synchronously from a preloaded catalog, or pass `audio: false` for silence.
126
162
  VanillaSky infers deterministic duration, volume, beat, and fade-out metadata.
127
163
  The soundtrack should continue across visual generation gaps and finish with
128
- the final scene. Narration, TTS, and speech synchronization are not part of the
129
- 0.1 SDK contract.
164
+ the final scene. `VideoChat` separately owns narration, TTS integration, browser
165
+ voice fallback, and speech synchronization for conversational responses.
130
166
 
131
167
  ## Custom templates
132
168
 
133
169
  The built-in catalog needs no setup. Only source-owned templates need the
134
170
  optional local TSX compiler; install it once with `npm install --save-dev tsx`.
135
- Then use `npx vanillasky create <id>` for an original one-file template or
136
- `npx vanillasky add <builtin>` to copy a close built-in. Edit the owned file,
137
- run `npx vanillasky sync`, then run `npx vanillasky check` before committing.
171
+ Then use `npx vanillasky templates create <id>` for an original one-file template or
172
+ `npx vanillasky templates add <builtin>` to copy a close built-in. Edit the owned file,
173
+ run `npx vanillasky templates sync`, then run `npx vanillasky templates check` before committing.
138
174
  Pass the generated registry to the server and browser; project-owned IDs
139
175
  replace matching built-ins and new IDs extend the catalog.
140
176
 
@@ -1,109 +1,106 @@
1
- [← Documentation home](../README.md) · [Previous: Prompt and input](prompt-and-input.md) · [Next: Next.js →](integrate-nextjs.md)
1
+ [← Documentation home](../README.md) · [Next: Provider integration →](provider-integration.md)
2
2
 
3
3
  # Getting started
4
4
 
5
- Install VanillaSky:
5
+ The fastest VanillaSky integration is the complete, general-purpose video chat.
6
+ It starts with packaged templates and browser voice, then turns on optional
7
+ speech, stock, transcription, and generated video when their server keys exist.
6
8
 
7
- ```bash
8
- npm install @vanillaskyai/video
9
- ```
9
+ ## Create the app
10
10
 
11
- VanillaSky does not select a provider or model during installation. Configure
12
- that separately on the server. The route below assumes your application exports
13
- an AI SDK `LanguageModel` as `videoModel`; if it does not, follow
14
- [Provider integration](provider-integration.md) first.
15
-
16
- Create one authenticated server route with `createVideoHandler`. Connect the
17
- app-owned model through `streamText`; keep credentials, authentication, rate
18
- limits, and media policy on the server:
19
-
20
- ```ts
21
- import { streamText } from "ai";
22
- import { createVideoHandler } from "@vanillaskyai/video/server";
23
- import { videoModel } from "@/lib/video-model";
24
-
25
- const handle = createVideoHandler({
26
- // Local development only. Replace with your session check before deploying.
27
- authorize: (request) => {
28
- if (process.env.VANILLASKY_LOCAL_DEMO !== "1") return false;
29
- const hostname = new URL(request.url).hostname;
30
- return hostname === "localhost" || hostname === "127.0.0.1";
31
- },
32
- streamText: ({ systemPrompt, userPrompt, signal }) => streamText({
33
- model: videoModel,
34
- system: systemPrompt,
35
- prompt: userPrompt,
36
- abortSignal: signal,
37
- }),
38
- });
39
-
40
- export const POST = handle;
41
- export const OPTIONS = handle;
11
+ Start in an empty folder:
12
+
13
+ ```bash
14
+ npx @vanillaskyai/video init
42
15
  ```
43
16
 
44
- The local bypass is intentionally fail-closed: the packaged development command
45
- sets its marker only for `next dev`, and it accepts only localhost. Every
46
- production request is denied. Replace it with your real session validation
47
- before deploying. For literal files and commands,
48
- use the tested [`examples/nextjs-quickstart` directory](../examples/nextjs-quickstart).
17
+ Init installs the exact SDK version that ran it, creates a small application
18
+ shell, and installs its provider dependencies.
19
+ It does not copy VanillaSky's template tree. The important generated files are:
49
20
 
50
- `videoModel` can come from any AI SDK provider, registry, gateway, compatible
51
- API, or custom implementation. The application can choose a cheaper, faster,
52
- or higher-quality model per request without changing VanillaSky.
21
+ | File | Your application owns |
22
+ | --- | --- |
23
+ | `src/main.tsx` | The mount point for the SDK-owned chat |
24
+ | `server.ts` | Provider choices and callbacks |
25
+ | `stock.ts` | Optional stock search policy |
26
+ | `vite.config.ts` | Local UI and `/api/video-chat` endpoint |
27
+ | `.env.local` | Ignored server-only credentials |
53
28
 
54
- Call that route from React and render the player:
29
+ The generated browser entry is intentionally tiny:
55
30
 
56
31
  ```tsx
57
- "use client";
32
+ import { StrictMode } from "react";
33
+ import { createRoot } from "react-dom/client";
34
+ import { VideoChat } from "@vanillaskyai/video/react";
35
+ import "@vanillaskyai/video/video-chat.css";
36
+
37
+ createRoot(document.getElementById("root")!).render(
38
+ <StrictMode><VideoChat /></StrictMode>,
39
+ );
40
+ ```
58
41
 
59
- import { VideoPlayer, useVideo } from "@vanillaskyai/video/react";
42
+ The SDK owns the responsive interface, conversation state, suggestions, voice
43
+ input, narration pacing, streaming player, and packaged templates. Your shell
44
+ stays responsible for providers, keys, authorization, limits, storage,
45
+ branding, and product copy.
60
46
 
61
- export function GeneratedVideo({ input }: { input: string }) {
62
- const video = useVideo();
47
+ ## Add the one required key
63
48
 
64
- return <>
65
- <button onClick={() => { void video.generate({ input }); }}>Generate video</button>
66
- {video.error && <p role="alert">Video generation failed.</p>}
67
- <VideoPlayer {...video.playerProps} />
68
- </>;
69
- }
70
- ```
49
+ Add the text-provider key to the generated, ignored `.env.local`:
71
50
 
72
- No template setup is required. VanillaSky advertises the trusted built-in
73
- catalog to your LLM, validates its selected scenes, and lazy-loads only the
74
- renderers the video uses.
51
+ ```dotenv
52
+ ANTHROPIC_API_KEY=
53
+ ```
75
54
 
76
- `useVideo()` uses `/api/video` by default. Pass `endpoint` to use another route.
77
- `generate()` returns a `Promise<Video>` if you need the completed config directly:
55
+ Fill the value locally; do not expose it through a client-prefixed environment
56
+ variable. Then inspect the setup without calling any provider:
78
57
 
79
- ```ts
80
- const completedVideo = await video.generate({ input });
58
+ ```bash
59
+ npx vanillasky doctor
81
60
  ```
82
61
 
83
- The promise resolves only after successful completion. It rejects on terminal
84
- generation errors and aborts; `video.error` contains the same typed error for
85
- reactive UI.
62
+ The base experience reports `templates + browser voice`. A ready text key makes
63
+ the chat answer. Optional keys progressively add capabilities:
86
64
 
87
- `video.status` is `idle`, `streaming`, `complete`, `error`, or `aborted`.
88
- `video.video` is the latest deterministic video, and `video.warnings` contains
89
- bounded typed diagnostics safe to show or branch on. A playable response that
90
- stops at a planner length limit includes a `plan_incomplete` warning because
91
- requested scenes or the ending may be missing. Provider finish reasons and
92
- content-filter details remain available to the server through the `onComplete`
93
- summary; surface that server-owned state separately when completeness matters
94
- to your product.
65
+ ```dotenv
66
+ # Optional generated speech
67
+ XAI_API_KEY=
95
68
 
96
- Persist a completed `Video` as JSON and play it later without another model
97
- request:
69
+ # Optional generated video and voice transcription
70
+ FAL_KEY=
98
71
 
99
- ```tsx
100
- <VideoPlayer video={savedVideo} />
72
+ # Optional stock media
73
+ PEXELS_API_KEY=
101
74
  ```
102
75
 
103
- Built-in templates work without additional props. Pass `templates` when the
104
- saved video uses customer-owned templates.
76
+ Doctor reports only key names and readiness, never values. Adding or removing
77
+ an optional key changes the available modes after a server restart; the client
78
+ does not need to change.
79
+
80
+ ## Run and verify
81
+
82
+ ```bash
83
+ npm run dev
84
+ ```
105
85
 
106
- All other input controls are optional. Continue with [input and opening
107
- scenes](input-and-first-scene.md), [branding and personalization](branding-and-personalization.md),
108
- or [media and soundtrack audio](media-and-audio.md). To edit or create visual
109
- building blocks, see [custom templates](custom-templates.md).
86
+ Open the reported localhost URL. Try one explanatory question and one unrelated
87
+ creative request in the same conversation. Confirm each response starts with a
88
+ spoken hook from the response stream before the full plan is complete, holds its opening until the first
89
+ scene is ready, reaches its final frame, and leaves the composer ready for
90
+ another turn. With stock media enabled, click a welcome or follow-up card and
91
+ confirm its footage carries directly into that opening. With generated video
92
+ enabled, confirm the only choices are Templates and Full AI video and that the
93
+ first generated shot continues the spoken hook without repeating it.
94
+
95
+ The generated local authorization accepts localhost only. Replace it with your
96
+ real session check, rate limits, and usage policy before deploying.
97
+
98
+ ## Continue
99
+
100
+ - Change providers or add media capabilities in [Provider integration](provider-integration.md).
101
+ - Brand or reshape the default experience in [Customization](customization.md).
102
+ - Use a fully custom UI with the headless chat hook described in
103
+ [Provider integration](provider-integration.md#custom-interface).
104
+ - Copy and edit a visual only when needed in [Custom templates](custom-templates.md).
105
+ - Apply production authorization and key handling from
106
+ [Production](production.md) and [Security](security.md).
@@ -1,139 +1,135 @@
1
1
  [← Documentation home](../README.md) · [Previous: Branding and personalization](branding-and-personalization.md) · [Next: Custom templates →](custom-templates.md)
2
2
 
3
- # Media and soundtrack audio
3
+ # Media, voice, and audio
4
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.
5
+ VanillaSky keeps provider choice in the application. The SDK defines small
6
+ server callbacks, advertises only the capabilities you configure, and keeps
7
+ all credentials out of React and the browser bundle.
8
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.
9
+ ## Two visual modes
12
10
 
13
- Only send source URLs you trust. Pass known approved assets through
14
- `suppliedMedia`, or configure the server-only `resolveMedia` callback for
15
- planner-selected backgrounds. Preload the next scene's asset before it becomes
16
- active. Keep provider credentials on the server.
11
+ The chat exposes only two clear choices:
17
12
 
18
- Supplied URLs and data URIs are not copied into the LLM prompt. The model sees
19
- an optional pool of opaque HTTPS-shaped references plus safe descriptive
20
- metadata, selects only relevant assets, and the SDK restores their original
21
- addresses on the server before scene validation. Supplying an asset does not
22
- require the completed video to use it.
13
+ | Mode | Visual source | Required callback |
14
+ | --- | --- | --- |
15
+ | `templates` | Trusted rendered templates, optionally with stock footage | none; `searchMedia` is optional |
16
+ | `full` | A generated clip for every visual beat | `generateVideo` |
23
17
 
24
- Audio timing, volume, beat markers, and fade-out remain part of the serialized
25
- output video, so replay and export stay deterministic. The input stays at the
26
- intent level: provide only the source URL and the SDK infers those playback
27
- defaults. Hosting and licensing are the application's responsibility.
18
+ Templates are always available and are the fast, inexpensive fallback. The
19
+ `full` mode is advertised only when `generateVideo` exists. VanillaSky does not
20
+ offer a mixed mode that generates only some scenes.
28
21
 
29
- Host tracks in your application's public assets, object storage, or CDN, then
30
- pass their URL through the normal video input. For example, a file at
31
- `public/audio/calm.mp3` can be used without an SDK audio package:
22
+ ## Stock media for templates
23
+
24
+ Add `searchMedia` when template answers, the welcome screen, and follow-up
25
+ cards should use approved photography or footage:
32
26
 
33
27
  ```ts
34
- const input = {
35
- input: "Grounded source material",
36
- audio: { src: "/audio/calm.mp3" },
37
- maxDurationSec: 24,
38
- };
28
+ createVideoChatHandler({
29
+ authorize: verifySession,
30
+ streamText: planWithYourModel,
31
+ generateText: runSmallTextTask,
32
+ searchMedia: async (query, { purpose, orientation, signal }) => {
33
+ const asset = await searchApprovedCatalog({
34
+ query,
35
+ purpose,
36
+ orientation,
37
+ signal,
38
+ });
39
+ return asset
40
+ ? { url: asset.url, type: asset.type, posterUrl: asset.posterUrl }
41
+ : null;
42
+ },
43
+ });
39
44
  ```
40
45
 
41
- The normalized output uses `trackId: "soundtrack"`, the video duration,
42
- default beat detection, an empty beat-marker list, full volume, and a
43
- three-second fade-out. Omit `audio` to let the server's synchronous
44
- `selectAudio` callback choose from an app-owned catalog. Pass `audio: false`
45
- to guarantee a silent video.
46
+ The planner emits a short semantic keyword, not a URL. The callback returns an
47
+ application-approved image or video URL, and the SDK validates it before it
48
+ reaches a scene. Return `null` when no licensed, safe, relevant asset exists;
49
+ the template falls back to its built-in treatment.
46
50
 
47
- Keep the catalog and files in your application so you control caching,
48
- licensing, and deployment. The SDK continues to handle playback, timing,
49
- serialization, replay, and export from the supplied URL.
51
+ For Pexels, keep `PEXELS_API_KEY` on the server, enforce a deadline, filter for
52
+ orientation, and return only validated Pexels asset domains. Licensing,
53
+ attribution, caching, MIME checks, and byte limits remain application-owned.
50
54
 
51
- ## Start with sound
55
+ ## Full generated video
52
56
 
53
- Browsers block audible autoplay unless the viewer has already interacted with
54
- the page. For a sound-first experience, keep the branded generation intro
55
- visible and let the player's start control provide that interaction:
56
-
57
- ```tsx
58
- <VideoPlayer
59
- {...video.playerProps}
60
- playbackMode="autoplay-after-interaction"
61
- />
62
- ```
63
-
64
- The generation cover immediately includes a centered **Play with sound**
65
- button. A click starts the soundtrack and holds that branded cover as a
66
- three-second generation intro while planning continues. Once both the intro
67
- and the first validated scene are ready, the generated timeline begins from
68
- time zero. The generation intro remains visible indefinitely if the viewer has
69
- not clicked yet, even when the complete generated video is already ready. The
70
- server keeps the generated first scene on screen for at least three more
71
- seconds when the duration budget permits. After that first successful sound start,
72
- replacement streams on the same mounted player autoplay the same generation
73
- intro with sound and fall back to a start control if the browser blocks them.
74
-
75
- When `VideoInput.opening` is supplied, its asset-free gradient `media` scene
76
- replaces the generic generation cover as soon as it is available. It remains
77
- as the static start poster until the viewer clicks, then begins the actual
78
- timeline with sound; there is no additional generic pre-roll before it.
79
-
80
- Use `playbackMode="manual"` to require the button on every run,
81
- `playbackMode="muted-autoplay"` for browser-safe muted autoplay, or
82
- `playbackMode="autoplay-with-sound"` to try audible autoplay immediately. The
83
- lower-level `autoPlay` and `startMuted` props remain available when no playback
84
- mode is set. For a chat response that should try audible autoplay without the
85
- SDK generation intro, pass `opening: false`, wait to mount the player until a
86
- generated scene exists, and render it with `autoPlay` and `startMuted={false}`.
87
- If the browser blocks the audible start, the player returns to the first frame
88
- and exposes its sound-start control.
89
-
90
- For a saved video rendered with `<VideoPlayer video={video} loop />`, the
91
- soundtrack loops too. A track shorter than the visual timeline repeats without
92
- a silent gap, and the player restarts it from the beginning when the visual
93
- timeline wraps. This does not bypass browser autoplay policy: use muted autoplay
94
- for unattended playback and let a viewer unmute, or start audible playback from
95
- a user interaction.
96
-
97
- ## Media providers
98
-
99
- VanillaSky is provider-independent. When `resolveMedia` is configured, the
100
- built-in planner may emit a bounded semantic query for later media-capable
101
- scenes. The SDK calls the application-owned resolver on the server, replaces
102
- the query with the approved URL, type, and optional poster, then validates the
103
- scene before emitting it. Without the callback, media intent stays hidden from
104
- the planner. The first accepted generated scene remains asset-free.
57
+ Add `generateVideo` to enable full AI video. It receives the planned visual
58
+ subject plus the generated look so every clip can follow the same direction:
105
59
 
106
60
  ```ts
107
- import { createVideoHandler } from "@vanillaskyai/video/server";
108
-
109
- createVideoHandler({
110
- authorize: checkSession,
61
+ createVideoChatHandler({
62
+ authorize: verifySession,
111
63
  streamText: planWithYourModel,
112
- resolveMedia: async (query, { preferredType, signal }) => {
113
- const asset = await searchYourApprovedCatalog({ query, preferredType, signal });
114
- return asset
115
- ? { url: asset.url, type: asset.type, posterUrl: asset.posterUrl }
116
- : null;
64
+ generateText: runSmallTextTask,
65
+ generateVideo: async (subject, {
66
+ generatedLook,
67
+ orientation,
68
+ requestId,
69
+ scene,
70
+ signal,
71
+ }) => {
72
+ const asset = await generateAndStore({
73
+ subject,
74
+ generatedLook,
75
+ orientation,
76
+ requestId,
77
+ sceneId: scene?.id,
78
+ signal,
79
+ maxRetries: 0,
80
+ });
81
+ return asset ? { url: asset.url, type: "video" } : null;
117
82
  },
118
83
  });
119
84
  ```
120
85
 
121
- The resolver query is 2–80 characters and at most eight words. Return `null`
122
- when no licensed, safe, relevant asset exists; media-capable templates fall
123
- back to the brand gradient when their schema permits it. The browser never
124
- receives `mediaKeyword`, provider keys, or raw provider metadata.
125
-
126
- `allowMediaUrl` is an authorization hook for applications with their own custom
127
- stream adapter. It validates a final URL; it does not search for, fetch, or
128
- resolve media. The default 0.1 path needs no callback because every planner URL
129
- must already be present in `suppliedMedia`.
130
-
131
- Do not expose provider keys to React or allow arbitrary planner URLs. Templates
132
- describe visual building blocks; the application owns media retrieval,
133
- caching, licensing, and delivery.
134
-
135
- For Pexels, keep `PEXELS_API_KEY` on the server and implement `resolveMedia`
136
- with the Pexels API. Return only validated `images.pexels.com` or
137
- `videos.pexels.com` results. The application remains responsible for
138
- attribution, search, orientation filtering, MIME checks, timeouts, caching,
139
- and fallback behavior.
86
+ The first streamed object reserves the first generated shot while the same
87
+ model call continues planning later scenes. Use `requestId` and `scene.id` as
88
+ an idempotency key, because generated clips are billable. Keep `maxRetries: 0`
89
+ inside provider calls, honour `signal`, and make retries an explicit product
90
+ decision with a known budget.
91
+
92
+ VanillaSky does not depend on a video model or storage service. The application
93
+ owns the provider key, model, spend, generated bytes, retention, and delivery.
94
+
95
+ ## Voice and transcription
96
+
97
+ Without `generateSpeech`, `VideoChat` uses the browser voice. Add a speech
98
+ callback for a consistent generated voice:
99
+
100
+ ```ts
101
+ generateSpeech: async ({ text, signal }) => {
102
+ const speech = await synthesize(text, { signal });
103
+ return { audio: speech.bytes, mediaType: speech.mediaType };
104
+ },
105
+ ```
106
+
107
+ The SDK measures or estimates each line, keeps narration synchronized with the
108
+ picture, and prevents a new scene from replacing speech that is still playing.
109
+ If generated speech fails, the interface can fall back to browser speech.
110
+
111
+ Add `transcribe` for server-side microphone transcription when browser speech
112
+ recognition is unavailable. Set `maxAudioBytes`, validate the media type, and
113
+ apply a provider deadline.
114
+
115
+ ## Native clip audio and soundtrack
116
+
117
+ Generated clips can contain diegetic audio. The chat player keeps that audio
118
+ separate from narration and uses one master mute control. Avoid generated
119
+ voiceover or music inside clips so it does not compete with the answer voice.
120
+
121
+ A serialized `Video` can also contain an application-owned soundtrack for
122
+ replay or custom playback. Soundtrack files, licenses, beat markers, volume,
123
+ and fade-out remain host-owned; narration and speech synchronization remain
124
+ the chat layer's responsibility. Browser autoplay rules still require a viewer
125
+ interaction before audible playback on many devices.
126
+
127
+ ## Safety rules
128
+
129
+ - Keep every provider key in server-only environment variables.
130
+ - Never let a planner return arbitrary final media URLs.
131
+ - Bound query length, response size, duration, concurrency, and generated spend.
132
+ - Preload the next asset and keep the current visual when media is late.
133
+ - Return a safe fallback instead of leaving the response waiting forever.
134
+
135
+ [← Documentation home](../README.md) · [Previous: Branding and personalization](branding-and-personalization.md) · [Next: Custom templates →](custom-templates.md)
@@ -1,4 +1,4 @@
1
- [← Documentation home](../README.md) · [Previous: Core concepts](concepts.md) · [Next: Live channels →](live-channels.md)
1
+ [← Documentation home](../README.md) · [Previous: Core concepts](concepts.md) · [Next: Streaming protocol →](streaming-protocol.md)
2
2
 
3
3
  # Persistence and replay
4
4