@vanillaskyai/video 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +45 -0
- package/LICENSE +201 -0
- package/PUBLIC-API.md +276 -0
- package/README.md +152 -0
- package/SECURITY.md +22 -0
- package/SUPPORT.md +28 -0
- package/bin/vanillasky.js +3 -0
- package/dist/bg-confetti-WNQXS7ZS.js +89 -0
- package/dist/bg-emoji-ETI47GLS.js +150 -0
- package/dist/bg-media-SVUZAMGX.js +99 -0
- package/dist/brand-message-F3RD4P3P.js +253 -0
- package/dist/builtin-server-F7YXRVCH.js +8 -0
- package/dist/catalog-types-BIhSpOWK.d.ts +74 -0
- package/dist/chart-bar-CNSC7BNK.js +304 -0
- package/dist/chart-counter-MCRHKN77.js +299 -0
- package/dist/chart-progress-ring-RMI4A7ZK.js +296 -0
- package/dist/check-runtime.d.ts +15 -0
- package/dist/check-runtime.js +102 -0
- package/dist/chunk-2E6T633S.js +27 -0
- package/dist/chunk-352ZZCET.js +83 -0
- package/dist/chunk-73NTSFFI.js +81 -0
- package/dist/chunk-A2X45FET.js +46 -0
- package/dist/chunk-ABPKM4GK.js +187 -0
- package/dist/chunk-ABZACD4G.js +216 -0
- package/dist/chunk-BCRUUJ6A.js +756 -0
- package/dist/chunk-CHLGW22F.js +3551 -0
- package/dist/chunk-CJ7F2CE6.js +104 -0
- package/dist/chunk-CR7FE7BW.js +109 -0
- package/dist/chunk-E7CL7UPB.js +109 -0
- package/dist/chunk-EE6PK6XC.js +18 -0
- package/dist/chunk-ERY4S6QV.js +385 -0
- package/dist/chunk-EVG2ZYIY.js +125 -0
- package/dist/chunk-FNAEQ7QO.js +155 -0
- package/dist/chunk-FQ47WXAJ.js +38 -0
- package/dist/chunk-FSG2PYWG.js +336 -0
- package/dist/chunk-GHJEAP4O.js +504 -0
- package/dist/chunk-GRMUV7QY.js +262 -0
- package/dist/chunk-IFOW65Z6.js +205 -0
- package/dist/chunk-IXCUY3B3.js +786 -0
- package/dist/chunk-L5BTTCZV.js +97 -0
- package/dist/chunk-LMG3GHLL.js +107 -0
- package/dist/chunk-OOPIETLW.js +101 -0
- package/dist/chunk-P22HHFDK.js +43 -0
- package/dist/chunk-QAAO7OAY.js +19 -0
- package/dist/chunk-S7HFLYNN.js +35 -0
- package/dist/chunk-SPVTJH3F.js +24 -0
- package/dist/chunk-W2XJKG6C.js +3177 -0
- package/dist/chunk-XB7LH4J2.js +301 -0
- package/dist/chunk-XGRA2MUV.js +242 -0
- package/dist/chunk-XTOXFMLM.js +105 -0
- package/dist/chunk-YC5MFXA5.js +132 -0
- package/dist/cli.d.ts +1 -0
- package/dist/cli.js +1681 -0
- package/dist/compose-video-CN3VSWST.js +13 -0
- package/dist/cta-logo-SVA5GP2A.js +209 -0
- package/dist/cta-media-E7VLLK42.js +215 -0
- package/dist/events-_wfjbmEp.d.ts +81 -0
- package/dist/incoming-call-62MFZL3Z.js +320 -0
- package/dist/index.d.ts +15 -0
- package/dist/index.js +14 -0
- package/dist/infographic-before-after-AXCDAWND.js +460 -0
- package/dist/infographic-feature-list-GW5GUE6O.js +266 -0
- package/dist/infographic-problem-solution-7NURJANA.js +340 -0
- package/dist/infographic-stat-row-J7IZR6ZN.js +241 -0
- package/dist/infographic-steps-VSZM6F5M.js +400 -0
- package/dist/kit-BMNcHqa9.d.ts +79 -0
- package/dist/prompt-input-LOUCRXCY.js +236 -0
- package/dist/react.d.ts +81 -0
- package/dist/react.js +835 -0
- package/dist/reaction-QG4CYZLQ.js +119 -0
- package/dist/server.d.ts +141 -0
- package/dist/server.js +498 -0
- package/dist/showcase-code-RUTFZR3H.js +351 -0
- package/dist/showcase-phone-6CP6XEU5.js +272 -0
- package/dist/showcase-terminal-MGAEQWPR.js +306 -0
- package/dist/showcase-web-Y4W2HVRJ.js +390 -0
- package/dist/social-conversation-75SD2637.js +695 -0
- package/dist/social-milestone-XI2QE5BO.js +292 -0
- package/dist/social-notification-2UOHQCUY.js +244 -0
- package/dist/social-review-stack-FDYZGZTJ.js +239 -0
- package/dist/social-testimonial-OQ7N52GJ.js +284 -0
- package/dist/social-tweet-5OLCNPPC.js +295 -0
- package/dist/state-DZcKuS32.d.ts +3 -0
- package/dist/system-prompt-A6UXNTPK.js +8 -0
- package/dist/template-catalog.d.ts +24 -0
- package/dist/template-catalog.js +6 -0
- package/dist/templates.d.ts +25 -0
- package/dist/templates.js +19 -0
- package/dist/test.d.ts +149 -0
- package/dist/test.js +272 -0
- package/dist/text-stream-6XHC7J47.js +10 -0
- package/dist/types-B-PZR6G_.d.ts +208 -0
- package/dist/validate-T7GBU2YF.js +8 -0
- package/docs/agent-integration.md +39 -0
- package/docs/architecture.md +84 -0
- package/docs/branding-and-personalization.md +87 -0
- package/docs/concepts.md +106 -0
- package/docs/custom-templates.md +329 -0
- package/docs/customization.md +141 -0
- package/docs/errors.md +70 -0
- package/docs/getting-started.md +111 -0
- package/docs/input-and-first-scene.md +41 -0
- package/docs/integrate-nextjs.md +79 -0
- package/docs/media-and-audio.md +69 -0
- package/docs/motion-and-effects.md +107 -0
- package/docs/persistence.md +95 -0
- package/docs/production.md +122 -0
- package/docs/prompt-and-input.md +185 -0
- package/docs/provider-integration.md +115 -0
- package/docs/reference/protocol.md +96 -0
- package/docs/reference/provider-adapters.md +158 -0
- package/docs/responsive-orientation.md +37 -0
- package/docs/security.md +38 -0
- package/docs/streaming-protocol.md +15 -0
- package/docs/testing.md +137 -0
- package/docs/use-cases.md +59 -0
- package/examples/custom-template/README.md +19 -0
- package/examples/custom-template/minimal-text.tsx +82 -0
- package/examples/custom-template/structured-data.tsx +104 -0
- package/examples/custom-template/supplied-media.tsx +92 -0
- package/examples/nextjs-quickstart/.env.example +2 -0
- package/examples/nextjs-quickstart/README.md +22 -0
- package/examples/nextjs-quickstart/next-env.d.ts +4 -0
- package/examples/nextjs-quickstart/package.json +24 -0
- package/examples/nextjs-quickstart/src/app/api/video/route.ts +22 -0
- package/examples/nextjs-quickstart/src/app/layout.tsx +5 -0
- package/examples/nextjs-quickstart/src/app/page.tsx +31 -0
- package/examples/nextjs-quickstart/tsconfig.json +26 -0
- package/package.json +167 -0
- package/registry/items/backgrounds.json +52 -0
- package/registry/items/barChart.json +237 -0
- package/registry/items/beforeAfter.json +180 -0
- package/registry/items/bigNumber.json +225 -0
- package/registry/items/brandMessage.json +158 -0
- package/registry/items/cardList.json +206 -0
- package/registry/items/chatMessenger.json +151 -0
- package/registry/items/chatWhatsapp.json +151 -0
- package/registry/items/codeEditor.json +184 -0
- package/registry/items/confetti.json +107 -0
- package/registry/items/ctaLogo.json +160 -0
- package/registry/items/ctaMedia.json +193 -0
- package/registry/items/emojiBurst.json +101 -0
- package/registry/items/incomingCall.json +170 -0
- package/registry/items/media.json +179 -0
- package/registry/items/milestone.json +205 -0
- package/registry/items/motion.json +45 -0
- package/registry/items/notification.json +182 -0
- package/registry/items/phoneMockup.json +254 -0
- package/registry/items/problemSolution.json +186 -0
- package/registry/items/progressRing.json +199 -0
- package/registry/items/promptInput.json +148 -0
- package/registry/items/reaction.json +170 -0
- package/registry/items/reviewStack.json +224 -0
- package/registry/items/steps.json +207 -0
- package/registry/items/template-context.json +31 -0
- package/registry/items/terminal.json +212 -0
- package/registry/items/testimonial.json +164 -0
- package/registry/items/theme.json +47 -0
- package/registry/items/tripleStats.json +228 -0
- package/registry/items/tweet.json +203 -0
- package/registry/items/typography.json +47 -0
- package/registry/items/webMockup.json +273 -0
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
[← Documentation home](../README.md) · [Previous: Input and first scene](input-and-first-scene.md) · [Next: Media and audio →](media-and-audio.md)
|
|
2
|
+
|
|
3
|
+
# Branding and personalization
|
|
4
|
+
|
|
5
|
+
Brand values style the whole response. Personalization shapes who the story is for.
|
|
6
|
+
|
|
7
|
+
## Background: standard, preset, or custom
|
|
8
|
+
|
|
9
|
+
Do nothing to use the standard `cosmic` background:
|
|
10
|
+
|
|
11
|
+
```ts
|
|
12
|
+
video.generate({ input });
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
Choose a curated gradient or solid by name when the standard does not fit:
|
|
16
|
+
|
|
17
|
+
```ts
|
|
18
|
+
video.generate({
|
|
19
|
+
input,
|
|
20
|
+
brand: { background: "twilight" },
|
|
21
|
+
});
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
Curated gradients are `cosmic`, `horizon`, `twilight`, `meadow`, `velvet`,
|
|
25
|
+
`flamingo`, `peach`, and `saffron`. Curated solids are `black`, `midnight`,
|
|
26
|
+
`aubergine`, `coal`, and `navy`.
|
|
27
|
+
|
|
28
|
+
Use exact colors only when a real brand or campaign requires them:
|
|
29
|
+
|
|
30
|
+
```ts
|
|
31
|
+
brand: { background: { colors: ["#112233", "#334455"] } } // gradient
|
|
32
|
+
brand: { background: { color: "#070B20" } } // solid
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
These choices set the background colors used by every trusted gradient-backed
|
|
36
|
+
template and by the automatic generation cover. Templates own their
|
|
37
|
+
composition and type treatment, so there is no separate visual-preset choice.
|
|
38
|
+
|
|
39
|
+
## Semantic brand tokens
|
|
40
|
+
|
|
41
|
+
Most applications can stop at `brand.background` and `brand.font`. Supply
|
|
42
|
+
semantic colors only when a real design system requires exact values:
|
|
43
|
+
|
|
44
|
+
```ts
|
|
45
|
+
const brand = {
|
|
46
|
+
name: "Acme Cloud",
|
|
47
|
+
logoUrl: approvedLogoUrl,
|
|
48
|
+
font: "Inter",
|
|
49
|
+
scriptFont: "Caveat",
|
|
50
|
+
background: "twilight",
|
|
51
|
+
colors: {
|
|
52
|
+
primary: "#7C5CFC",
|
|
53
|
+
secondary: "#3D2A78",
|
|
54
|
+
foreground: "#FFFFFF",
|
|
55
|
+
surface: "#161229",
|
|
56
|
+
surfaceElevated: "#211A38",
|
|
57
|
+
muted: "#AAA5B8",
|
|
58
|
+
},
|
|
59
|
+
};
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
Every generated video contains a fully resolved brand. `colors` may be partial
|
|
63
|
+
on input; omitted values receive deterministic defaults. When
|
|
64
|
+
`colors.foreground` is omitted, every named or custom background is paired
|
|
65
|
+
deterministically with black or white. The resolver checks the full rendered
|
|
66
|
+
sRGB gradient ramp, including interior blends rather than only its endpoints.
|
|
67
|
+
If neither foreground reaches 4.5:1 everywhere, resolution rejects the
|
|
68
|
+
background and asks for a consistently readable choice.
|
|
69
|
+
|
|
70
|
+
An explicitly supplied foreground is preserved, never silently replaced. It
|
|
71
|
+
must reach 4.5:1 across the solid background or entire gradient ramp; otherwise
|
|
72
|
+
input resolution and replay validation reject it with the failing brand path
|
|
73
|
+
and minimum contrast. Surface tokens remain independent. Templates that put
|
|
74
|
+
text on `surface` or `surfaceElevated` internally keep an accessible preferred
|
|
75
|
+
semantic text color or derive black/white for that surface.
|
|
76
|
+
|
|
77
|
+
Use an approved public or signed URL for logos. Fonts must be licensed and
|
|
78
|
+
loaded by the host application before playback. Always test both orientations
|
|
79
|
+
and forced browser zoom.
|
|
80
|
+
|
|
81
|
+
`personalization` accepts JSON-safe viewer or account context such as
|
|
82
|
+
`firstName`, `role`, `accountName`, `locale`, `goal`, or `onboardingPartner`.
|
|
83
|
+
These values may appear verbatim in the response, but are always data, never
|
|
84
|
+
instructions. Put product, news, metric, and quoted claims in `input`; do not
|
|
85
|
+
duplicate ordinary viewer context there.
|
|
86
|
+
|
|
87
|
+
Only include information the authenticated viewer is allowed to see. Avoid sensitive HR, health, payment, or customer attributes in event logs and analytics. If a locale changes number/date formatting, format facts server-side so the planner cannot guess.
|
package/docs/concepts.md
ADDED
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
[← Documentation home](../README.md) · [Previous: Getting started](getting-started.md) · [Next: Streaming protocol →](streaming-protocol.md)
|
|
2
|
+
|
|
3
|
+
# Core concepts
|
|
4
|
+
|
|
5
|
+
## Video response
|
|
6
|
+
|
|
7
|
+
A video response is a short, responsive animated experience assembled from
|
|
8
|
+
trusted templates. It begins before the full plan is available and ends as an
|
|
9
|
+
editable deterministic configuration.
|
|
10
|
+
|
|
11
|
+
It is not an encoded video stream. The browser renders normal React components
|
|
12
|
+
from validated scene instructions. The 0.1 SDK does not include MP4 or WebM
|
|
13
|
+
encoding; pass the completed deterministic JSON to an application-owned render
|
|
14
|
+
or export pipeline when an encoded file is required.
|
|
15
|
+
|
|
16
|
+
## Input
|
|
17
|
+
|
|
18
|
+
`VideoInput` is the factual and creative boundary:
|
|
19
|
+
|
|
20
|
+
- `input`: raw source material such as a roadmap update, launch brief, article,
|
|
21
|
+
curated set of articles, metrics, events, notes, or an AI answer. It may be
|
|
22
|
+
short, but the planner is designed to distill larger sources into a concise
|
|
23
|
+
video rather than represent every fact;
|
|
24
|
+
- `instructions`: optional creative direction that cannot override facts;
|
|
25
|
+
- `opening`: optional copy for a deterministic opening that can play immediately;
|
|
26
|
+
- `personalization`: application-defined fields such as name, role, account,
|
|
27
|
+
period, goal, or onboarding partner;
|
|
28
|
+
- `brand`: an optional background preset plus name, logo, font, surfaces, and
|
|
29
|
+
advanced exact tokens;
|
|
30
|
+
- `suppliedMedia`: approved images or videos with a role and description;
|
|
31
|
+
- `audio`: omit for automatic selection, pass `{ src }`, or use `false` for silence;
|
|
32
|
+
- `orientation` and `maxDurationSec`: composition constraints.
|
|
33
|
+
|
|
34
|
+
## Template kit
|
|
35
|
+
|
|
36
|
+
VanillaSky uses its trusted built-in kit by default. Applications only configure
|
|
37
|
+
a kit when they need project-owned templates. Those templates replace matching
|
|
38
|
+
built-in IDs and add new IDs while every untouched built-in remains available.
|
|
39
|
+
A kit supplies three things together:
|
|
40
|
+
|
|
41
|
+
1. React components for the player;
|
|
42
|
+
2. advertised template capabilities for protocol negotiation;
|
|
43
|
+
3. an LLM catalog describing when and how each template may be used.
|
|
44
|
+
|
|
45
|
+
The planner cannot legitimately select a template outside the active kit.
|
|
46
|
+
|
|
47
|
+
## Planner
|
|
48
|
+
|
|
49
|
+
The server-side planner turns the input into small plan parts such as
|
|
50
|
+
`scene.add`, `scene.patch`, `asset.patch`, and `plan.complete`. A planner may be
|
|
51
|
+
recorded, deterministic, backed by OpenAI or Anthropic, or implemented with
|
|
52
|
+
another streaming text model.
|
|
53
|
+
|
|
54
|
+
The planner does not create public event IDs, checksums, revisions, or final
|
|
55
|
+
snapshots. The runtime owns those guarantees.
|
|
56
|
+
|
|
57
|
+
| Boundary | Type | Owner |
|
|
58
|
+
| --- | --- | --- |
|
|
59
|
+
| Planner parts | Internal validated plan data | Server/provider adapter |
|
|
60
|
+
| Values read from `response.stream` | `VideoEvent` | SDK runtime |
|
|
61
|
+
| Terminal editable result | `Video` via `response.result` | SDK reducer |
|
|
62
|
+
|
|
63
|
+
A planner must never yield `VideoEvent` envelopes. It yields plan
|
|
64
|
+
parts; the runtime validates them and creates the public event metadata.
|
|
65
|
+
|
|
66
|
+
## Event stream
|
|
67
|
+
|
|
68
|
+
The runtime emits ordered protocol events:
|
|
69
|
+
|
|
70
|
+
```text
|
|
71
|
+
response.start
|
|
72
|
+
audio.set optional, before any scene
|
|
73
|
+
scene.add supplied opening
|
|
74
|
+
scene.add generated body
|
|
75
|
+
scene.add generated body
|
|
76
|
+
response.complete exact terminal snapshot
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
Network transport uses SSE. Direct in-process integrations expose the same
|
|
80
|
+
events as an async iterable.
|
|
81
|
+
|
|
82
|
+
## Playback and buffering
|
|
83
|
+
|
|
84
|
+
The player renders the first committed scene and continues through the known
|
|
85
|
+
timeline. Audio can loop during a generation gap. A media-bearing scene should
|
|
86
|
+
not be committed until its asset is ready; start generated content with a
|
|
87
|
+
typography-led scene so useful playback does not wait on media lookup.
|
|
88
|
+
|
|
89
|
+
Played scenes cannot be patched. This keeps playback deterministic and avoids a
|
|
90
|
+
scene changing after the viewer already saw it.
|
|
91
|
+
|
|
92
|
+
The same player accepts a completed value directly. After persisting the JSON,
|
|
93
|
+
load it through `parseVideo(JSON.parse(storedJson))`, then render
|
|
94
|
+
`<VideoPlayer video={savedVideo} />` to replay it without opening a stream or
|
|
95
|
+
calling the model again. Supply `templates` only when that video uses
|
|
96
|
+
customer-owned templates. See [Persistence and replay](persistence.md) for the
|
|
97
|
+
complete storage contract.
|
|
98
|
+
|
|
99
|
+
## Completion snapshot
|
|
100
|
+
|
|
101
|
+
`response.complete` contains the exact reduced `Video` plus a
|
|
102
|
+
checksum. Persist the snapshot when you need replay, resume, export, analytics,
|
|
103
|
+
or an editing handoff. The event log can also be persisted and revalidated.
|
|
104
|
+
|
|
105
|
+
Read the complete [Protocol 0.4](reference/protocol.md) and the separate
|
|
106
|
+
[persisted Video 0.1 contract](persistence.md).
|
|
@@ -0,0 +1,329 @@
|
|
|
1
|
+
[← Documentation home](../README.md) · [Previous: Branding and personalization](branding-and-personalization.md) · [Next: Motion and effects →](motion-and-effects.md)
|
|
2
|
+
|
|
3
|
+
# Custom templates
|
|
4
|
+
|
|
5
|
+
VanillaSky turns context into a visual response. Your AI determines what matters
|
|
6
|
+
from the available context; a template defines how one kind of grounded answer
|
|
7
|
+
appears in motion. Custom templates let your application own that visual
|
|
8
|
+
vocabulary without letting the model generate React, HTML, or CSS.
|
|
9
|
+
|
|
10
|
+
You edit one source file. VanillaSky derives the model-facing description,
|
|
11
|
+
server validation, browser registry, and TypeScript variable shape from it.
|
|
12
|
+
|
|
13
|
+
## The shortest path
|
|
14
|
+
|
|
15
|
+
The playback and server package does not install a TypeScript compiler. Install
|
|
16
|
+
the optional compiler only for source-owned templates and the template
|
|
17
|
+
ownership commands below:
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
npm install --save-dev tsx
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
List the effective catalog, including project-owned templates, before choosing
|
|
24
|
+
what to build or copy:
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
npx vanillasky list
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
Create an original template:
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
npx vanillasky create customer-health
|
|
34
|
+
npx vanillasky describe customer-health
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
Or copy a built-in when its behavior is already close:
|
|
38
|
+
|
|
39
|
+
```bash
|
|
40
|
+
npx vanillasky add bigNumber
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
Then edit the owned `.tsx` file, regenerate the two small registries, and check
|
|
44
|
+
the complete contract:
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
npx vanillasky sync
|
|
48
|
+
npx vanillasky check
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
For an original template, the source is
|
|
52
|
+
`vanillasky/templates/customer-health.tsx`. For the copied built-in, it is
|
|
53
|
+
`vanillasky/templates/bigNumber.tsx`. These are application source: commit them,
|
|
54
|
+
review them, and change them like any other React component.
|
|
55
|
+
|
|
56
|
+
`create` and `add` run `sync` once, so the generated entry points exist
|
|
57
|
+
immediately. Run `sync` again after every source edit. Run `check` before every
|
|
58
|
+
commit. `check` validates metadata, schema/default validity, named examples, deterministic
|
|
59
|
+
renders at progress boundaries in portrait and landscape, and browser/server
|
|
60
|
+
registry parity.
|
|
61
|
+
|
|
62
|
+
Preview either operation without applying the proposed file writes:
|
|
63
|
+
|
|
64
|
+
```bash
|
|
65
|
+
npx vanillasky add bigNumber --dry-run
|
|
66
|
+
npx vanillasky add bigNumber --diff
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
`--dry-run` lists every proposed file and `--diff` shows its content changes,
|
|
70
|
+
including the generated browser and server registries. The CLI does not apply
|
|
71
|
+
any proposed file write. When project templates already exist, these previews
|
|
72
|
+
execute that trusted application source to derive the complete registry plan.
|
|
73
|
+
Because trusted project source can have its own side effects, preview execution is
|
|
74
|
+
resource-bounded but is not a sandbox and does not guarantee that the entire
|
|
75
|
+
project remains byte-identical. Review project template code before running
|
|
76
|
+
preview commands.
|
|
77
|
+
|
|
78
|
+
An edited file is never replaced unless you explicitly pass `--overwrite`.
|
|
79
|
+
|
|
80
|
+
## One file is the contract
|
|
81
|
+
|
|
82
|
+
The file created by `vanillasky create` is a complete working template. Keep
|
|
83
|
+
these concerns together:
|
|
84
|
+
|
|
85
|
+
- `useWhen` and `avoidWhen` tell the AI when the visual is appropriate;
|
|
86
|
+
- `schema` defines allowed variables, validation, defaults, labels, and
|
|
87
|
+
grounding formats;
|
|
88
|
+
- named `examples` provide complete deterministic preview values;
|
|
89
|
+
- `component` receives only validated variables and render context;
|
|
90
|
+
- raw `progress`, dimensions, and `safeZone` make semantic state deterministic
|
|
91
|
+
and layout safe in portrait and landscape.
|
|
92
|
+
|
|
93
|
+
Customer templates hard-cut by default. Opt into renderer-owned fades only
|
|
94
|
+
for scenes that use the standard media-background variables, and only after
|
|
95
|
+
both timing points are visually tested in portrait and landscape. Shared brand
|
|
96
|
+
gradients and unchanged media do not crossfade:
|
|
97
|
+
|
|
98
|
+
```tsx
|
|
99
|
+
export default defineTemplate({
|
|
100
|
+
// ...the normal one-file contract
|
|
101
|
+
usesGlobalTransition: true,
|
|
102
|
+
transitionTiming: { entryReadyProgress: 0.2, holdProgress: 0.7 },
|
|
103
|
+
component: ({ progress, motionProgress = progress, ...props }) => {
|
|
104
|
+
// Both clocks cover the complete declared 0→1 scene duration.
|
|
105
|
+
// Use progress for facts/media/screens; motionProgress for presentation.
|
|
106
|
+
return <CustomerScene {...props} dataProgress={progress} motionProgress={motionProgress} />;
|
|
107
|
+
},
|
|
108
|
+
});
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
`entryReadyProgress` must show recognizable content in verification captures.
|
|
112
|
+
`holdProgress` is the audited complete-readable checkpoint before the
|
|
113
|
+
template's own exit begins. These values document and test the choreography;
|
|
114
|
+
they never advance or shorten the runtime clock. The SDK validates
|
|
115
|
+
`0 <= entryReadyProgress < holdProgress <= 1`.
|
|
116
|
+
|
|
117
|
+
When changed media crossfades, the incoming layer can be partly visible while
|
|
118
|
+
raw `progress` is exactly zero. A custom template that counts or types a value
|
|
119
|
+
from a synthetic placeholder must hide only that transient wrapper during the
|
|
120
|
+
crossfade. Compare the formatted display value to the formatted final value so
|
|
121
|
+
a sourced zero is not mistaken for a placeholder:
|
|
122
|
+
|
|
123
|
+
<!-- verify:transition-semantic-value:start -->
|
|
124
|
+
```tsx
|
|
125
|
+
import type { CSSProperties } from "react";
|
|
126
|
+
|
|
127
|
+
interface TransitionSemanticValueProps {
|
|
128
|
+
displayValue: string;
|
|
129
|
+
finalValue: string;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
export function TransitionSemanticValue({
|
|
133
|
+
displayValue,
|
|
134
|
+
finalValue,
|
|
135
|
+
}: TransitionSemanticValueProps) {
|
|
136
|
+
const isTransient = displayValue !== finalValue;
|
|
137
|
+
|
|
138
|
+
return <span
|
|
139
|
+
data-transition-semantic={isTransient ? "transient" : undefined}
|
|
140
|
+
style={isTransient ? {
|
|
141
|
+
visibility: "var(--vanillasky-transition-semantic-visibility, visible)" as CSSProperties["visibility"],
|
|
142
|
+
} : undefined}
|
|
143
|
+
>
|
|
144
|
+
{displayValue}
|
|
145
|
+
</span>;
|
|
146
|
+
}
|
|
147
|
+
```
|
|
148
|
+
<!-- verify:transition-semantic-value:end -->
|
|
149
|
+
|
|
150
|
+
The variable defaults to `visible`, so direct rendering and hard cuts are
|
|
151
|
+
unchanged. Do not apply it to grounded headings, CTA copy, supplied media, or
|
|
152
|
+
the whole scene: those must remain recognizable at `entryReadyProgress`.
|
|
153
|
+
|
|
154
|
+
The only SDK import an original one-file template needs is public:
|
|
155
|
+
|
|
156
|
+
```tsx
|
|
157
|
+
import { defineTemplate } from "@vanillaskyai/video/templates";
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
Start from the generated file or one of the packaged references:
|
|
161
|
+
|
|
162
|
+
| Content shape | Reference |
|
|
163
|
+
| --- | --- |
|
|
164
|
+
| One headline and supporting line | [Minimal text](../examples/custom-template/minimal-text.tsx) |
|
|
165
|
+
| One exact metric and its change | [Structured data](../examples/custom-template/structured-data.tsx) |
|
|
166
|
+
| An application-supplied image | [Supplied media](../examples/custom-template/supplied-media.tsx) |
|
|
167
|
+
|
|
168
|
+
The [reference comparison](../examples/custom-template/README.md) explains when
|
|
169
|
+
to choose each. Copy a file into `vanillasky/templates/`, change its ID and
|
|
170
|
+
content contract, then run `sync` and `check`.
|
|
171
|
+
|
|
172
|
+
## Connect the generated registries
|
|
173
|
+
|
|
174
|
+
`sync` generates two files from the owned templates:
|
|
175
|
+
|
|
176
|
+
```text
|
|
177
|
+
vanillasky/
|
|
178
|
+
├── templates/
|
|
179
|
+
│ └── customer-health.tsx # source you edit
|
|
180
|
+
├── index.ts # browser registry, generated
|
|
181
|
+
└── server.ts # React-free model/validation registry, generated
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
The server registry connects custom template metadata to the prompt, model
|
|
185
|
+
selection, and validator. Import it in the route that connects your model:
|
|
186
|
+
|
|
187
|
+
```ts
|
|
188
|
+
// src/video-route.ts
|
|
189
|
+
import { openai } from "@ai-sdk/openai";
|
|
190
|
+
import { streamText } from "ai";
|
|
191
|
+
import { createVideoHandler } from "@vanillaskyai/video/server";
|
|
192
|
+
import { templates } from "../vanillasky/server";
|
|
193
|
+
|
|
194
|
+
export const handleVideo = createVideoHandler({
|
|
195
|
+
templates,
|
|
196
|
+
authorize: (request) => {
|
|
197
|
+
if (process.env.NODE_ENV !== "development") return false;
|
|
198
|
+
const hostname = new URL(request.url).hostname;
|
|
199
|
+
return hostname === "localhost" || hostname === "127.0.0.1";
|
|
200
|
+
},
|
|
201
|
+
streamText: ({ systemPrompt, userPrompt, signal }) => streamText({
|
|
202
|
+
model: openai(process.env.OPENAI_MODEL ?? "gpt-4.1"),
|
|
203
|
+
system: systemPrompt,
|
|
204
|
+
prompt: userPrompt,
|
|
205
|
+
abortSignal: signal,
|
|
206
|
+
}),
|
|
207
|
+
});
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
The local-only authorization above is intentionally narrow. Replace it with
|
|
211
|
+
your application's session check before deployment, as shown in
|
|
212
|
+
[Getting started](getting-started.md).
|
|
213
|
+
|
|
214
|
+
Use the browser registry for generation and playback:
|
|
215
|
+
|
|
216
|
+
```tsx
|
|
217
|
+
// src/video-composer.tsx
|
|
218
|
+
import { VideoPlayer, useVideo } from "@vanillaskyai/video/react";
|
|
219
|
+
import { templates } from "../vanillasky";
|
|
220
|
+
|
|
221
|
+
export function VideoComposer() {
|
|
222
|
+
const video = useVideo({ templates });
|
|
223
|
+
|
|
224
|
+
return <>
|
|
225
|
+
<button onClick={() => void video.generate({ input: "Grounded answer" })}>
|
|
226
|
+
Generate
|
|
227
|
+
</button>
|
|
228
|
+
<VideoPlayer {...video.playerProps} />
|
|
229
|
+
</>;
|
|
230
|
+
}
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
Adjust relative paths for your application layout. Do not edit `index.ts` or
|
|
234
|
+
`server.ts`; `sync` replaces them. A matching ID replaces that built-in on both
|
|
235
|
+
sides, a new ID extends the catalog, and every other built-in remains available.
|
|
236
|
+
|
|
237
|
+
## Preview without an LLM
|
|
238
|
+
|
|
239
|
+
Template rendering is deterministic. Preview a saved `Video` directly in your
|
|
240
|
+
application so iteration uses your actual CSS, fonts, container, React version,
|
|
241
|
+
and browser. This path calls no model and needs no server route:
|
|
242
|
+
|
|
243
|
+
<!-- verify:custom-template-preview:start -->
|
|
244
|
+
```tsx
|
|
245
|
+
import type { Video } from "@vanillaskyai/video";
|
|
246
|
+
import { VideoPlayer } from "@vanillaskyai/video/react";
|
|
247
|
+
import { templates } from "../vanillasky";
|
|
248
|
+
|
|
249
|
+
const savedVideo: Video = {
|
|
250
|
+
schemaVersion: "0.1",
|
|
251
|
+
orientation: "portrait",
|
|
252
|
+
scenes: [{
|
|
253
|
+
id: "customer-health-preview",
|
|
254
|
+
templateId: "customer-health",
|
|
255
|
+
variables: {
|
|
256
|
+
title: "Customer health is improving",
|
|
257
|
+
subtitle: "Activation increased after guided onboarding.",
|
|
258
|
+
},
|
|
259
|
+
timing: { fixedDuration: 5 },
|
|
260
|
+
}],
|
|
261
|
+
style: {
|
|
262
|
+
brand: {
|
|
263
|
+
font: "Inter",
|
|
264
|
+
scriptFont: "Caveat",
|
|
265
|
+
background: { type: "gradient", colors: ["#8711C1", "#2167E3"] },
|
|
266
|
+
colors: {
|
|
267
|
+
primary: "#00E5A0",
|
|
268
|
+
secondary: "#006BE5",
|
|
269
|
+
foreground: "#FFFFFF",
|
|
270
|
+
surface: "#0A0A14",
|
|
271
|
+
surfaceElevated: "#14152A",
|
|
272
|
+
muted: "#A7A6B0",
|
|
273
|
+
},
|
|
274
|
+
},
|
|
275
|
+
},
|
|
276
|
+
};
|
|
277
|
+
|
|
278
|
+
export function TemplatePreview() {
|
|
279
|
+
return <VideoPlayer video={savedVideo} templates={templates} autoPlay={false} />;
|
|
280
|
+
}
|
|
281
|
+
```
|
|
282
|
+
<!-- verify:custom-template-preview:end -->
|
|
283
|
+
|
|
284
|
+
Change the saved variables to each named example, inspect both orientations,
|
|
285
|
+
and play the scene to inspect its full progress range. This is also the
|
|
286
|
+
production replay path for a completed video saved from
|
|
287
|
+
`await video.generate(...)`.
|
|
288
|
+
|
|
289
|
+
## Schema and grounding
|
|
290
|
+
|
|
291
|
+
Use JSON Schema as the single source of truth.
|
|
292
|
+
|
|
293
|
+
Property defaults are optional authoring and renderer smoke values; they do not
|
|
294
|
+
need to form a complete scene. Named examples must resolve to complete valid
|
|
295
|
+
scenes. They may inherit omitted values from property defaults. Never invent
|
|
296
|
+
media URLs or actions just to supply a default. Put complete scene examples in
|
|
297
|
+
the top-level `examples` array, and do not maintain parallel variable or default
|
|
298
|
+
maps.
|
|
299
|
+
|
|
300
|
+
Useful formats add grounding behavior:
|
|
301
|
+
|
|
302
|
+
- `grounded-stat` marks numeric statistical evidence for the planner; it does
|
|
303
|
+
not compare the value against raw input at runtime;
|
|
304
|
+
- `grounded-quote` requires the quote to exist verbatim in the input;
|
|
305
|
+
- `supplied-image` requires an image URL listed in `VideoInput.suppliedMedia`;
|
|
306
|
+
- `uri` applies the supplied-media or server URL policy to an approved URL;
|
|
307
|
+
- `stock-media-keyword` is only for hosts that resolve stock media before a
|
|
308
|
+
scene is committed.
|
|
309
|
+
|
|
310
|
+
Templates whose core proof needs a real statistic can add
|
|
311
|
+
`"x-vanillasky": { "requiresStat": true }`. See the structured-data and
|
|
312
|
+
supplied-media references for complete examples.
|
|
313
|
+
|
|
314
|
+
The automatic `opening` uses the built-in `notification` variables (`appName`
|
|
315
|
+
and `message`). If you replace `notification` while using automatic openings,
|
|
316
|
+
keep that variable contract.
|
|
317
|
+
|
|
318
|
+
## What belongs where
|
|
319
|
+
|
|
320
|
+
Your application owns the model, credentials, live context, custom template
|
|
321
|
+
source, authentication, media authorization, and saved `Video` values.
|
|
322
|
+
VanillaSky owns the generated planning instructions, streaming protocol,
|
|
323
|
+
runtime validation, built-in template catalog, and player. The model selects a
|
|
324
|
+
trusted template and fills its declared schema; it never writes executable UI
|
|
325
|
+
code.
|
|
326
|
+
|
|
327
|
+
For the exact request path and source locations, see
|
|
328
|
+
[Architecture](architecture.md). For media policies and supplied-media input,
|
|
329
|
+
see [Media and audio](media-and-audio.md).
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
[← Documentation home](../README.md)
|
|
2
|
+
|
|
3
|
+
# Customization
|
|
4
|
+
|
|
5
|
+
## Background and semantic brand
|
|
6
|
+
|
|
7
|
+
Omit brand configuration to use the standard `cosmic` background. Prefer a
|
|
8
|
+
named curated choice over raw color work:
|
|
9
|
+
|
|
10
|
+
```ts
|
|
11
|
+
const brand = {
|
|
12
|
+
name: "Acme",
|
|
13
|
+
logoUrl: "https://cdn.acme.com/logo.svg",
|
|
14
|
+
font: "Inter",
|
|
15
|
+
scriptFont: "Caveat",
|
|
16
|
+
background: "twilight",
|
|
17
|
+
colors: {
|
|
18
|
+
primary: "#6D5EF5",
|
|
19
|
+
secondary: "#3D2A78",
|
|
20
|
+
foreground: "#FFFFFF",
|
|
21
|
+
surface: "#17122F",
|
|
22
|
+
surfaceElevated: "#231B42",
|
|
23
|
+
muted: "#A7A6B0",
|
|
24
|
+
},
|
|
25
|
+
};
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
Gradient presets: `cosmic`, `horizon`, `twilight`, `meadow`, `velvet`,
|
|
29
|
+
`flamingo`, `peach`, `saffron`. Solid presets: `black`, `midnight`,
|
|
30
|
+
`aubergine`, `coal`, `navy`. When a named choice genuinely cannot express the
|
|
31
|
+
brand, use `{ colors: ["#112233", "#334455"] }` for a custom gradient or
|
|
32
|
+
`{ color: "#070B20" }` for a custom solid.
|
|
33
|
+
|
|
34
|
+
`colors` may be partial; the resolver fills every semantic token before the
|
|
35
|
+
video is emitted. With no foreground, named and custom backgrounds
|
|
36
|
+
deterministically select black or white for at least 4.5:1 contrast across the
|
|
37
|
+
full rendered sRGB ramp, including gradient interiors. A ramp that neither can
|
|
38
|
+
cover is rejected. An explicit foreground is preserved and validated by the
|
|
39
|
+
same invariant during input resolution and replay; low-contrast values are
|
|
40
|
+
rejected with the failing path and minimum ratio. Elevated surfaces derive an
|
|
41
|
+
accessible internal text treatment without changing the semantic foreground.
|
|
42
|
+
Use an approved public or signed URL for logos, and never put a private storage
|
|
43
|
+
credential in the config.
|
|
44
|
+
|
|
45
|
+
## Global visual direction
|
|
46
|
+
|
|
47
|
+
Leave visual direction unset to use VanillaSky's defaults, or set one coherent
|
|
48
|
+
look for the completed video:
|
|
49
|
+
|
|
50
|
+
```ts
|
|
51
|
+
style: {
|
|
52
|
+
density: "airy", // airy | normal | packed
|
|
53
|
+
motion: "calm", // calm | normal | punchy
|
|
54
|
+
textArchetype: "cinematic",
|
|
55
|
+
backgroundEffect: "slow-zoom-out",
|
|
56
|
+
}
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
These are defaults, not generated CSS. A validated scene may still select a
|
|
60
|
+
more appropriate text or background treatment when its trusted template allows
|
|
61
|
+
it.
|
|
62
|
+
|
|
63
|
+
## Personalization
|
|
64
|
+
|
|
65
|
+
`personalization` accepts JSON-safe application fields. Include only values the
|
|
66
|
+
viewer is allowed to see. The system prompt treats them as context, not as
|
|
67
|
+
instructions.
|
|
68
|
+
|
|
69
|
+
Good fields include `firstName`, `role`, `accountName`, `period`, `goal`,
|
|
70
|
+
`locale`, and `onboardingPartner`. Keep facts in `input` as well when they must
|
|
71
|
+
appear in the story.
|
|
72
|
+
|
|
73
|
+
## Opening
|
|
74
|
+
|
|
75
|
+
`VideoPlayer` automatically shows a brand-colored generation cover until the
|
|
76
|
+
first validated scene arrives. The cover is player state, not video content: it
|
|
77
|
+
is never written to the event log, replay, or export.
|
|
78
|
+
|
|
79
|
+
An optional opening is deterministic and should not wait for an LLM or remote media:
|
|
80
|
+
|
|
81
|
+
```ts
|
|
82
|
+
opening: "Joris, your Q2 recap is ready."
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
Use `opening` only for a genuine opening that should remain in the completed
|
|
86
|
+
response. VanillaSky infers the scene ID, `notification` template, variables,
|
|
87
|
+
and five-second timing. You do not need to create a generic loading scene.
|
|
88
|
+
|
|
89
|
+
## Aspect ratio and responsive layout
|
|
90
|
+
|
|
91
|
+
The player is responsive by default: it fills its container width. Templates
|
|
92
|
+
and copy must work at either aspect ratio; orientation is not an AI-planning
|
|
93
|
+
input and must not influence the selected templates or wording.
|
|
94
|
+
|
|
95
|
+
`portrait` reserves a 9:16 response/export frame and `landscape` reserves 16:9.
|
|
96
|
+
This input setting remains stable in the completed config. For an embed that
|
|
97
|
+
should display landscape on desktop and portrait on mobile without changing the
|
|
98
|
+
saved response, pass `orientation="auto"` to `VideoPlayer`; it responds
|
|
99
|
+
to its container width. See [responsive orientation](responsive-orientation.md).
|
|
100
|
+
|
|
101
|
+
## Supplied media
|
|
102
|
+
|
|
103
|
+
Provide approved media with semantic descriptions:
|
|
104
|
+
|
|
105
|
+
```ts
|
|
106
|
+
suppliedMedia: [{
|
|
107
|
+
id: "product-dashboard",
|
|
108
|
+
url: "https://cdn.example.com/dashboard.png",
|
|
109
|
+
type: "image",
|
|
110
|
+
description: "Activation dashboard after the Q2 release",
|
|
111
|
+
role: "product",
|
|
112
|
+
}]
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
Resolve searched media before generation and pass the approved result through
|
|
116
|
+
`suppliedMedia`. See [media providers](media-and-audio.md#media-providers).
|
|
117
|
+
|
|
118
|
+
## Soundtrack audio
|
|
119
|
+
|
|
120
|
+
Pass `audio: { src }` for a specific track, omit `audio` to let the server choose
|
|
121
|
+
synchronously from a preloaded catalog, or pass `audio: false` for silence.
|
|
122
|
+
VanillaSky infers deterministic duration, volume, beat, and fade-out metadata.
|
|
123
|
+
The soundtrack should continue across visual generation gaps and finish with
|
|
124
|
+
the final scene. Narration, TTS, and speech synchronization are not part of the
|
|
125
|
+
0.1 SDK contract.
|
|
126
|
+
|
|
127
|
+
## Custom templates
|
|
128
|
+
|
|
129
|
+
The built-in catalog needs no setup. Only source-owned templates need the
|
|
130
|
+
optional local TSX compiler; install it once with `npm install --save-dev tsx`.
|
|
131
|
+
Then use `npx vanillasky create <id>` for an original one-file template or
|
|
132
|
+
`npx vanillasky add <builtin>` to copy a close built-in. Edit the owned file,
|
|
133
|
+
run `npx vanillasky sync`, then run `npx vanillasky check` before committing.
|
|
134
|
+
Pass the generated registry to the server and browser; project-owned IDs
|
|
135
|
+
replace matching built-ins and new IDs extend the catalog.
|
|
136
|
+
|
|
137
|
+
The model sees selection guidance and a schema, not component source. It chooses
|
|
138
|
+
a trusted template and fills validated variables. Never evaluate model-authored
|
|
139
|
+
React, HTML, CSS, or JavaScript on the live path.
|
|
140
|
+
|
|
141
|
+
See [custom templates](custom-templates.md).
|