@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.
- package/CHANGELOG.md +178 -0
- package/PUBLIC-API.md +129 -9
- package/README.md +70 -108
- package/dist/{bg-confetti-SHQI7ATB.js → bg-confetti-HITCGLGD.js} +1 -1
- package/dist/{bg-emoji-XYFRA63Q.js → bg-emoji-VU4UZYZN.js} +1 -1
- package/dist/{bg-media-FEBATBT2.js → bg-media-L34PDQXJ.js} +4 -4
- package/dist/{brand-message-P56WEJPG.js → brand-message-FUJ4STFO.js} +3 -3
- package/dist/{builtin-server-YHEZ2JRF.js → builtin-server-KB7FKF6A.js} +2 -2
- package/dist/{chart-bar-ZB45ZRKH.js → chart-bar-EGPHJATL.js} +4 -4
- package/dist/{chart-counter-HFYNE4HT.js → chart-counter-5FFWQNZO.js} +4 -4
- package/dist/{chart-progress-ring-ZKMLRGSB.js → chart-progress-ring-JMXQLMLC.js} +4 -4
- package/dist/check-runtime.js +2 -2
- package/dist/{chunk-66MNRUCR.js → chunk-2PYO6VAC.js} +51 -3
- package/dist/{chunk-ZQQQAAKP.js → chunk-2XT4MZ76.js} +33 -2
- package/dist/chunk-5CDAM24P.js +31 -0
- package/dist/{chunk-PNS52FL4.js → chunk-5SLENAJW.js} +19 -6
- package/dist/{chunk-EFL34TXF.js → chunk-AITKH6QT.js} +24 -8
- package/dist/{chunk-HT6BGERF.js → chunk-FRN6WKHA.js} +28 -10
- package/dist/{chunk-FZHMQFG3.js → chunk-GAIOHGNR.js} +5 -21
- package/dist/{chunk-3RV4YKB3.js → chunk-JNN3EYVP.js} +51 -26
- package/dist/{chunk-NGY3TNND.js → chunk-K746NUIP.js} +70 -50
- package/dist/{chunk-HVCPEAQF.js → chunk-LKBZX7GV.js} +9 -6
- package/dist/{chunk-G66Z5CWR.js → chunk-MMUXVA47.js} +27 -3
- package/dist/{chunk-6M6DATLW.js → chunk-MXZSDGZQ.js} +6 -5
- package/dist/{chunk-3OU7HIEB.js → chunk-RE4IMWJR.js} +15 -31
- package/dist/{chunk-FVTMYS6U.js → chunk-RGF452LL.js} +1 -1
- package/dist/{chunk-34O5BY6X.js → chunk-RXHW4EP4.js} +21 -4
- package/dist/chunk-SKRGRKHY.js +142 -0
- package/dist/{chunk-TCEHK2BW.js → chunk-YJJC4N4D.js} +2 -1
- package/dist/cli.js +438 -30
- package/dist/{compose-video-JWIEDTEJ.js → compose-video-PD6LKKRK.js} +4 -4
- package/dist/{cta-logo-S4OMTNXD.js → cta-logo-YR7LYUR5.js} +3 -3
- package/dist/{cta-media-EROTHDRR.js → cta-media-DQJBKUO2.js} +4 -4
- package/dist/{events-BBP30j3c.d.ts → events-B6qS1Lsb.d.ts} +2 -2
- package/dist/{incoming-call-5IWJ2TAS.js → incoming-call-54KQ7O5A.js} +3 -3
- package/dist/index.d.ts +89 -3
- package/dist/index.js +12 -1
- package/dist/{infographic-before-after-2I7YZVDD.js → infographic-before-after-ROS52GOW.js} +1 -1
- package/dist/{infographic-feature-list-7SK65U3K.js → infographic-feature-list-E7MGUDAN.js} +4 -4
- package/dist/{infographic-problem-solution-IW35S6BR.js → infographic-problem-solution-3S6NGO5N.js} +4 -4
- package/dist/{infographic-stat-row-XHUZZPDM.js → infographic-stat-row-SYB7AB7H.js} +4 -4
- package/dist/{infographic-steps-JDDEDI7X.js → infographic-steps-KCE6JWE2.js} +4 -4
- package/dist/{kit-DEG3fcaL.d.ts → kit-8g46H2RZ.d.ts} +1 -1
- package/dist/{prompt-input-6ZWXEL3V.js → prompt-input-MHX4O42G.js} +3 -3
- package/dist/react.d.ts +200 -6
- package/dist/react.js +1859 -43
- package/dist/{reaction-Z3JIM3MQ.js → reaction-SLEGR3BE.js} +4 -4
- package/dist/{scene-video-backdrop-IL4F2SVD.js → scene-video-backdrop-XFPX4O3I.js} +2 -1
- package/dist/server.d.ts +104 -4
- package/dist/server.js +976 -26
- package/dist/{showcase-code-NWQAAJIA.js → showcase-code-2HLAVKBM.js} +4 -4
- package/dist/{showcase-phone-5UW45ATT.js → showcase-phone-Q4HEO4LH.js} +4 -4
- package/dist/{showcase-terminal-UVOD25UF.js → showcase-terminal-DL45LUFG.js} +4 -4
- package/dist/{showcase-web-3AW6VHIG.js → showcase-web-2QZO73FZ.js} +4 -4
- package/dist/{social-milestone-KPKHFOB5.js → social-milestone-GEFUQSBF.js} +3 -3
- package/dist/{social-notification-EDOU6TZB.js → social-notification-FJ5VHVLK.js} +3 -3
- package/dist/{social-review-stack-EZOO33SV.js → social-review-stack-KXQCKFRP.js} +3 -3
- package/dist/{social-testimonial-DLOINMMY.js → social-testimonial-JZ7CHC7S.js} +3 -3
- package/dist/{social-tweet-6JUM323G.js → social-tweet-UCDXM25F.js} +3 -3
- package/dist/{system-prompt-4I6Z5HK3.js → system-prompt-RRXIWDDD.js} +5 -3
- package/dist/template-catalog.js +1 -1
- package/dist/templates.d.ts +3 -3
- package/dist/test.d.ts +2 -2
- package/dist/test.js +3 -3
- package/dist/{text-stream-J6EDDJP4.js → text-stream-FW4BLBAL.js} +2 -2
- package/dist/{types-BV9IqExh.d.ts → types-2wHBqtg8.d.ts} +29 -1
- package/dist/types-DrABlRa7.d.ts +46 -0
- package/docs/agent-integration.md +49 -25
- package/docs/architecture.md +37 -22
- package/docs/branding-and-personalization.md +1 -1
- package/docs/concepts.md +14 -0
- package/docs/custom-templates.md +9 -9
- package/docs/customization.md +41 -5
- package/docs/getting-started.md +81 -84
- package/docs/media-and-audio.md +113 -117
- package/docs/persistence.md +1 -1
- package/docs/production.md +81 -92
- package/docs/prompt-and-input.md +71 -161
- package/docs/provider-integration.md +91 -49
- package/docs/responsive-orientation.md +1 -1
- package/docs/security.md +6 -5
- package/docs/streaming-protocol.md +1 -1
- package/docs/testing.md +51 -43
- package/examples/custom-template/README.md +1 -1
- package/package.json +20 -21
- package/registry/items/backgrounds.json +2 -2
- package/registry/items/barChart.json +6 -4
- package/registry/items/beforeAfter.json +1 -1
- package/registry/items/bigNumber.json +4 -3
- package/registry/items/brandMessage.json +3 -2
- package/registry/items/cardList.json +6 -4
- package/registry/items/codeEditor.json +4 -3
- package/registry/items/confetti.json +1 -1
- package/registry/items/ctaLogo.json +3 -2
- package/registry/items/ctaMedia.json +4 -3
- package/registry/items/emojiBurst.json +1 -1
- package/registry/items/incomingCall.json +3 -2
- package/registry/items/media.json +4 -3
- package/registry/items/milestone.json +3 -2
- package/registry/items/notification.json +3 -2
- package/registry/items/phoneMockup.json +4 -3
- package/registry/items/problemSolution.json +4 -3
- package/registry/items/progressRing.json +4 -3
- package/registry/items/promptInput.json +3 -2
- package/registry/items/reaction.json +2 -2
- package/registry/items/reviewStack.json +3 -2
- package/registry/items/steps.json +6 -4
- package/registry/items/terminal.json +5 -4
- package/registry/items/testimonial.json +3 -2
- package/registry/items/tripleStats.json +4 -3
- package/registry/items/tweet.json +3 -2
- package/registry/items/webMockup.json +4 -3
- package/starters/video-chat/.env.example +16 -0
- package/starters/video-chat/README.md +63 -0
- package/starters/video-chat/index.html +12 -0
- package/starters/video-chat/package.json +28 -0
- package/starters/video-chat/server.ts +152 -0
- package/starters/video-chat/src/main.tsx +8 -0
- package/starters/video-chat/stock.ts +139 -0
- package/starters/video-chat/tsconfig.json +20 -0
- package/starters/video-chat/vite.config.ts +81 -0
- package/styles/video-chat.css +713 -0
- package/dist/chunk-C6WVCZRW.js +0 -19
- package/docs/input-and-first-scene.md +0 -64
- package/docs/integrate-nextjs.md +0 -86
- package/docs/live-channels.md +0 -149
- package/docs/use-cases.md +0 -59
- package/examples/nextjs-quickstart/.env.example +0 -2
- package/examples/nextjs-quickstart/README.md +0 -27
- package/examples/nextjs-quickstart/next-env.d.ts +0 -4
- package/examples/nextjs-quickstart/package.json +0 -25
- package/examples/nextjs-quickstart/src/app/api/video/route.ts +0 -24
- package/examples/nextjs-quickstart/src/app/layout.tsx +0 -5
- package/examples/nextjs-quickstart/src/app/page.tsx +0 -31
- package/examples/nextjs-quickstart/tsconfig.json +0 -26
package/dist/chunk-C6WVCZRW.js
DELETED
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
// src/visual-system/scene-templates/external-video-backdrop.tsx
|
|
2
|
-
import React from "react";
|
|
3
|
-
import { jsx } from "react/jsx-runtime";
|
|
4
|
-
var sharedContext = globalThis;
|
|
5
|
-
var ExternalVideoBackdropContext = sharedContext.__vanillaskyExternalVideoBackdropContext ??= React.createContext(false);
|
|
6
|
-
function ExternalVideoBackdropProvider({
|
|
7
|
-
mode,
|
|
8
|
-
children
|
|
9
|
-
}) {
|
|
10
|
-
return /* @__PURE__ */ jsx(ExternalVideoBackdropContext.Provider, { value: mode, children });
|
|
11
|
-
}
|
|
12
|
-
function useExternalVideoBackdrop() {
|
|
13
|
-
return React.useContext(ExternalVideoBackdropContext);
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
export {
|
|
17
|
-
ExternalVideoBackdropProvider,
|
|
18
|
-
useExternalVideoBackdrop
|
|
19
|
-
};
|
|
@@ -1,64 +0,0 @@
|
|
|
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: "Maya completed 142 customer conversations in Q2.",
|
|
12
|
-
knowledgeMode: "input-only",
|
|
13
|
-
instructions: "Celebrate the result. Never alter a metric.",
|
|
14
|
-
opening: "Maya, your Q2 customer impact recap is ready.",
|
|
15
|
-
personalization: { firstName: "Maya", period: "Q2", role: "Product leader" },
|
|
16
|
-
brand,
|
|
17
|
-
suppliedMedia,
|
|
18
|
-
audio: { src: "/audio/calm.mp3" },
|
|
19
|
-
orientation: "portrait",
|
|
20
|
-
maxDurationSec: 24,
|
|
21
|
-
};
|
|
22
|
-
```
|
|
23
|
-
|
|
24
|
-
## Raw source
|
|
25
|
-
|
|
26
|
-
In the default `input-only` mode, put every fact the response may claim in
|
|
27
|
-
`input`. Use plain text, compact JSON, or a server-produced digest. Include
|
|
28
|
-
units, periods, comparison bases, and provenance identifiers where ambiguity
|
|
29
|
-
is possible. Choose `knowledgeMode: "general"` only when the model should use
|
|
30
|
-
stable general knowledge to answer a question or develop content. Creative
|
|
31
|
-
direction in `instructions` never changes that setting.
|
|
32
|
-
|
|
33
|
-
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.
|
|
34
|
-
|
|
35
|
-
## Opening
|
|
36
|
-
|
|
37
|
-
`opening` becomes a deterministic `media` scene emitted before provider work.
|
|
38
|
-
When callers omit it, the SDK uses `Creating your video...`; a supplied value
|
|
39
|
-
replaces that fallback. The scene contains only that sentence and explicitly
|
|
40
|
-
uses the brand gradient without stock media. The SDK requests a three-second
|
|
41
|
-
opening, then applies its normal readability and overall-duration budget. It
|
|
42
|
-
owns the scene ID, template, variables, and timing so callers only provide
|
|
43
|
-
optional custom copy. That copy should:
|
|
44
|
-
|
|
45
|
-
- be personal or situational enough to feel intentional;
|
|
46
|
-
- require no network media lookup;
|
|
47
|
-
- remain true if generation later fails;
|
|
48
|
-
- be one concise sentence that fits comfortably in both supported orientations;
|
|
49
|
-
- be part of the final story, not a spinner disguised as a scene.
|
|
50
|
-
|
|
51
|
-
Pass `opening: false` when the application should own the waiting experience:
|
|
52
|
-
|
|
53
|
-
```ts
|
|
54
|
-
opening: false
|
|
55
|
-
```
|
|
56
|
-
|
|
57
|
-
The SDK then starts with an empty timeline and emits the first validated,
|
|
58
|
-
generated scene as soon as it is ready. Render a transient loading state in the
|
|
59
|
-
host application until `video.video?.scenes.length` is non-zero. That loading
|
|
60
|
-
state is not persisted in the event stream or completed video. A generated
|
|
61
|
-
scene is still required; a planner that completes without one fails the run.
|
|
62
|
-
|
|
63
|
-
The deterministic opening is runtime-owned, so it remains available even when `templateIds`
|
|
64
|
-
does not let the planner select `media` for generated body scenes.
|
package/docs/integrate-nextjs.md
DELETED
|
@@ -1,86 +0,0 @@
|
|
|
1
|
-
[← Documentation home](../README.md) · [Previous: Getting started](getting-started.md) · [Next: Provider integration →](provider-integration.md)
|
|
2
|
-
|
|
3
|
-
# Next.js integration
|
|
4
|
-
|
|
5
|
-
After installing VanillaSky, choose the provider and model for your server. This
|
|
6
|
-
complete example uses Anthropic and asks you to select a current Claude Sonnet
|
|
7
|
-
model explicitly instead of baking a model ID into the SDK docs:
|
|
8
|
-
|
|
9
|
-
```bash
|
|
10
|
-
npm install @vanillaskyai/video ai @ai-sdk/anthropic
|
|
11
|
-
```
|
|
12
|
-
|
|
13
|
-
Create an ignored `.env.local`:
|
|
14
|
-
|
|
15
|
-
```bash
|
|
16
|
-
ANTHROPIC_API_KEY=your-key
|
|
17
|
-
ANTHROPIC_MODEL=your-current-sonnet-model
|
|
18
|
-
```
|
|
19
|
-
|
|
20
|
-
Create `app/api/video/route.ts`:
|
|
21
|
-
|
|
22
|
-
```ts
|
|
23
|
-
import { anthropic } from "@ai-sdk/anthropic";
|
|
24
|
-
import { streamText } from "ai";
|
|
25
|
-
import { createVideoHandler } from "@vanillaskyai/video/server";
|
|
26
|
-
|
|
27
|
-
const modelId = process.env.ANTHROPIC_MODEL;
|
|
28
|
-
if (!modelId) throw new Error("Set ANTHROPIC_MODEL in the server environment");
|
|
29
|
-
const model = anthropic(modelId);
|
|
30
|
-
|
|
31
|
-
const handle = createVideoHandler({
|
|
32
|
-
// Local development only. Replace with your session check before deploying.
|
|
33
|
-
authorize: (request) => {
|
|
34
|
-
if (process.env.VANILLASKY_LOCAL_DEMO !== "1") return false;
|
|
35
|
-
const hostname = new URL(request.url).hostname;
|
|
36
|
-
return hostname === "localhost" || hostname === "127.0.0.1";
|
|
37
|
-
},
|
|
38
|
-
streamText: ({ systemPrompt, userPrompt, signal }) => streamText({
|
|
39
|
-
model,
|
|
40
|
-
system: systemPrompt,
|
|
41
|
-
prompt: userPrompt,
|
|
42
|
-
abortSignal: signal,
|
|
43
|
-
}),
|
|
44
|
-
});
|
|
45
|
-
|
|
46
|
-
export const POST = handle;
|
|
47
|
-
export const OPTIONS = handle;
|
|
48
|
-
```
|
|
49
|
-
|
|
50
|
-
The packaged `npm run dev` command supplies this non-secret marker only to
|
|
51
|
-
`next dev`. Production builds and `next start` never receive it.
|
|
52
|
-
|
|
53
|
-
Create a Client Component:
|
|
54
|
-
|
|
55
|
-
```tsx
|
|
56
|
-
"use client";
|
|
57
|
-
|
|
58
|
-
import { VideoPlayer, useVideo } from "@vanillaskyai/video/react";
|
|
59
|
-
|
|
60
|
-
export function Video() {
|
|
61
|
-
const video = useVideo();
|
|
62
|
-
return <>
|
|
63
|
-
<button onClick={() => { void video.generate({
|
|
64
|
-
input: "Activation increased from 41% to 58%.",
|
|
65
|
-
personalization: { firstName: "Maya" },
|
|
66
|
-
}); }}>
|
|
67
|
-
Generate
|
|
68
|
-
</button>
|
|
69
|
-
{video.error && <p role="alert">Could not generate the video.</p>}
|
|
70
|
-
<VideoPlayer {...video.playerProps} />
|
|
71
|
-
</>;
|
|
72
|
-
}
|
|
73
|
-
```
|
|
74
|
-
|
|
75
|
-
That is the complete first path. Built-in templates need no registry setup.
|
|
76
|
-
The development authorization accepts only local requests and denies production
|
|
77
|
-
requests; replace it with your application's session validation before
|
|
78
|
-
deploying.
|
|
79
|
-
|
|
80
|
-
The copy-and-run app is in the
|
|
81
|
-
[`examples/nextjs-quickstart` directory](../examples/nextjs-quickstart).
|
|
82
|
-
|
|
83
|
-
For another LLM, replace `anthropic(...)` with the matching AI SDK model. The route
|
|
84
|
-
shape and React code stay the same. See [Provider integration](provider-integration.md)
|
|
85
|
-
for adapters, authentication, diagnostics, and production controls. Add media,
|
|
86
|
-
audio, persistence, or custom templates only after the default path works.
|
package/docs/live-channels.md
DELETED
|
@@ -1,149 +0,0 @@
|
|
|
1
|
-
[← Documentation home](../README.md) · [Previous: Persistence and replay](persistence.md) · [Next: Streaming protocol →](streaming-protocol.md)
|
|
2
|
-
|
|
3
|
-
# Live channels
|
|
4
|
-
|
|
5
|
-
A live channel is a video that plays continuously and rebuilds itself on a
|
|
6
|
-
schedule: a market ticker, a news wall, a status board in an office, a loop
|
|
7
|
-
behind a conference stand. Nobody presses play, nobody watches from the start,
|
|
8
|
-
and the content changes on its own.
|
|
9
|
-
|
|
10
|
-
This is a different shape from a generated response. A response is produced for
|
|
11
|
-
one person, once, in reply to something they asked. A channel is produced ahead
|
|
12
|
-
of time for nobody in particular, and then played to whoever is looking.
|
|
13
|
-
|
|
14
|
-
That difference is what makes it cheap. Playback of a saved `Video` performs no
|
|
15
|
-
generation request and no model call — the player is DOM and CSS advanced by a
|
|
16
|
-
`requestAnimationFrame` clock — so a channel that loops all day costs exactly as
|
|
17
|
-
much as a channel that loops once. All of the cost lives in building the
|
|
18
|
-
configuration, which happens on your schedule rather than per viewer.
|
|
19
|
-
|
|
20
|
-
## The shape
|
|
21
|
-
|
|
22
|
-
```
|
|
23
|
-
scheduled job browser
|
|
24
|
-
───────────── ───────
|
|
25
|
-
fetch your data ┌────────► fetch the stored Video
|
|
26
|
-
map it to scenes │ parseVideo(...)
|
|
27
|
-
parseVideo(...) ────────┘ <VideoPlayer video={...} loop />
|
|
28
|
-
store the JSON plays forever
|
|
29
|
-
```
|
|
30
|
-
|
|
31
|
-
The job owns the data and the mapping. The player owns nothing but playback.
|
|
32
|
-
|
|
33
|
-
## Play without stopping
|
|
34
|
-
|
|
35
|
-
Pass `loop` alongside a saved video. The player restarts from the beginning
|
|
36
|
-
instead of showing its replay affordance and loops its soundtrack continuously.
|
|
37
|
-
A track shorter than the channel repeats as soon as it ends; when the visual
|
|
38
|
-
timeline wraps, the soundtrack restarts from the beginning with it.
|
|
39
|
-
|
|
40
|
-
<!-- verify:live-channel-example:start -->
|
|
41
|
-
```tsx
|
|
42
|
-
import { useEffect, useState } from "react";
|
|
43
|
-
import { parseVideo, type Video, type VideoScene } from "@vanillaskyai/video";
|
|
44
|
-
import { VideoPlayer } from "@vanillaskyai/video/react";
|
|
45
|
-
|
|
46
|
-
export function Channel({ endpoint }: { endpoint: string }) {
|
|
47
|
-
const [video, setVideo] = useState<Video | undefined>(undefined);
|
|
48
|
-
const [onScreen, setOnScreen] = useState<VideoScene | undefined>(undefined);
|
|
49
|
-
|
|
50
|
-
const load = async () => {
|
|
51
|
-
const stored: unknown = await fetch(endpoint).then((response) => response.json());
|
|
52
|
-
setVideo(parseVideo(stored));
|
|
53
|
-
};
|
|
54
|
-
|
|
55
|
-
useEffect(() => { void load(); }, []);
|
|
56
|
-
if (!video) return null;
|
|
57
|
-
|
|
58
|
-
return <>
|
|
59
|
-
<VideoPlayer
|
|
60
|
-
video={video}
|
|
61
|
-
loop
|
|
62
|
-
autoPlay
|
|
63
|
-
playbackMode="muted-autoplay"
|
|
64
|
-
onSceneChange={(scene, index) => {
|
|
65
|
-
setOnScreen(scene);
|
|
66
|
-
// Wrapping back to the start is a natural moment to pick up a rebuild,
|
|
67
|
-
// so a screen left running all day never serves a stale channel.
|
|
68
|
-
if (index === 0) void load();
|
|
69
|
-
}}
|
|
70
|
-
/>
|
|
71
|
-
<p>{onScreen ? onScreen.templateId : "starting"}</p>
|
|
72
|
-
</>;
|
|
73
|
-
}
|
|
74
|
-
```
|
|
75
|
-
<!-- verify:live-channel-example:end -->
|
|
76
|
-
|
|
77
|
-
`loop` applies to saved videos. A streaming response still ends when the stream
|
|
78
|
-
ends, because there is nothing to loop back to until it completes.
|
|
79
|
-
|
|
80
|
-
## Follow what is on screen
|
|
81
|
-
|
|
82
|
-
`onSceneChange` fires whenever the scene under the playhead changes, and again
|
|
83
|
-
on index `0` each time a loop wraps. Use it to drive anything that has to stay
|
|
84
|
-
in step with the video: a caption rail, a source panel, chapter markers, an
|
|
85
|
-
analytics event per scene.
|
|
86
|
-
|
|
87
|
-
Do not run your own timer for this. A parallel clock drifts from the player's
|
|
88
|
-
as soon as playback is paused, throttled in a background tab, or delayed by a
|
|
89
|
-
slow asset, and the drift is silent.
|
|
90
|
-
|
|
91
|
-
`onComplete` is not an alternative here: it reports the end of a *stream*, so it
|
|
92
|
-
never fires for a saved video.
|
|
93
|
-
|
|
94
|
-
## Build the configuration yourself
|
|
95
|
-
|
|
96
|
-
A channel's scenes usually come from your own data rather than from a model.
|
|
97
|
-
`resolveVideoBrand` fills a partial brand with the documented defaults, so a
|
|
98
|
-
hand-authored `Video` satisfies `parseVideo` without copying a defaults blob
|
|
99
|
-
into your application:
|
|
100
|
-
|
|
101
|
-
```ts
|
|
102
|
-
import { parseVideo, resolveVideoBrand } from "@vanillaskyai/video";
|
|
103
|
-
|
|
104
|
-
const channel = parseVideo({
|
|
105
|
-
schemaVersion: "0.1",
|
|
106
|
-
orientation: "landscape",
|
|
107
|
-
scenes: rows.map((row, index) => ({
|
|
108
|
-
id: `row-${index}`,
|
|
109
|
-
templateId: "bigNumber",
|
|
110
|
-
variables: { texts: row.label, value: row.value, unit: row.unit },
|
|
111
|
-
timing: { fixedDuration: 5 },
|
|
112
|
-
})),
|
|
113
|
-
style: { brand: resolveVideoBrand({ name: "Acme", background: "midnight" }) },
|
|
114
|
-
});
|
|
115
|
-
```
|
|
116
|
-
|
|
117
|
-
Validate with `parseVideo` inside the job, before storing. A channel that fails
|
|
118
|
-
validation in the browser is a channel nobody can watch; one that fails in the
|
|
119
|
-
job can simply leave the previous version in place.
|
|
120
|
-
|
|
121
|
-
## Keep motion independent of scene length
|
|
122
|
-
|
|
123
|
-
Templates receive `sceneDuration`. Express motion in seconds against it rather
|
|
124
|
-
than as a fraction of `progress`, or lengthening a scene to give viewers more
|
|
125
|
-
reading time will stretch its animation instead and give them none.
|
|
126
|
-
|
|
127
|
-
## Practical notes
|
|
128
|
-
|
|
129
|
-
- **Rebuild on a schedule, not per request.** Viewers should read a stored
|
|
130
|
-
configuration. If every viewer triggers generation, a channel becomes the most
|
|
131
|
-
expensive surface you own rather than the cheapest.
|
|
132
|
-
- **Never replace a good channel with a broken one.** Validate first, and on
|
|
133
|
-
failure leave the previous configuration in place.
|
|
134
|
-
- **Autoplay needs `playbackMode="muted-autoplay"`.** Browsers refuse unmuted
|
|
135
|
-
autoplay. A channel can still carry a looping soundtrack: it starts muted and
|
|
136
|
-
becomes audible when a viewer uses the player's sound control.
|
|
137
|
-
- **Host your media.** Loops re-request assets over long sessions; serving them
|
|
138
|
-
from your own origin keeps a content policy simple and avoids depending on
|
|
139
|
-
someone else's CDN.
|
|
140
|
-
- **Long channels are ordinary videos.** Ten minutes of scenes is one `Video`
|
|
141
|
-
with more entries in `scenes`; there is no separate playlist concept.
|
|
142
|
-
|
|
143
|
-
## Related
|
|
144
|
-
|
|
145
|
-
- [Persistence and replay](persistence.md) — the storage boundary these
|
|
146
|
-
configurations pass through
|
|
147
|
-
- [Custom templates](custom-templates.md) — templates for your own data shapes
|
|
148
|
-
- [Responsive orientation](responsive-orientation.md) — the same channel on a
|
|
149
|
-
portrait screen
|
package/docs/use-cases.md
DELETED
|
@@ -1,59 +0,0 @@
|
|
|
1
|
-
[← Documentation home](../README.md)
|
|
2
|
-
|
|
3
|
-
# Video response use cases
|
|
4
|
-
|
|
5
|
-
These four inputs show the primary product shape: AI answers and live
|
|
6
|
-
application context become embedded video responses. The application decides
|
|
7
|
-
which context matters and keeps it current. VanillaSky grounds, validates,
|
|
8
|
-
composes, and plays the visual response.
|
|
9
|
-
|
|
10
|
-
## News summary
|
|
11
|
-
|
|
12
|
-
```ts
|
|
13
|
-
video.generate({
|
|
14
|
-
input: `Headline: North Sea wind output reached a record on August 17.
|
|
15
|
-
Source summary: Grid operators reported 18.2 GW at 14:00 CET, 12% above the
|
|
16
|
-
previous record. Operators said lower demand helped reduce wholesale prices.`,
|
|
17
|
-
instructions: "Lead with the record, explain the comparison, then the impact.",
|
|
18
|
-
maxDurationSec: 24,
|
|
19
|
-
});
|
|
20
|
-
```
|
|
21
|
-
|
|
22
|
-
## Contextual help
|
|
23
|
-
|
|
24
|
-
```ts
|
|
25
|
-
video.generate({
|
|
26
|
-
input: `The user is configuring an alert. The current threshold is 80%.
|
|
27
|
-
Alerts evaluate every 15 minutes. Email and Slack are enabled. The Save button
|
|
28
|
-
applies the rule to 42 selected accounts.`,
|
|
29
|
-
instructions: "Explain what will happen if the user saves this configuration.",
|
|
30
|
-
opening: "Here is what this alert will do.",
|
|
31
|
-
maxDurationSec: 24,
|
|
32
|
-
});
|
|
33
|
-
```
|
|
34
|
-
|
|
35
|
-
## Personalized briefing
|
|
36
|
-
|
|
37
|
-
```ts
|
|
38
|
-
video.generate({
|
|
39
|
-
input: `The Acme account has 1,240 weekly active users, up 18% month over
|
|
40
|
-
month. Three renewals are due in September. Support response time improved from
|
|
41
|
-
4.2 hours to 2.8 hours.`,
|
|
42
|
-
personalization: { firstName: "Maya", accountName: "Acme" },
|
|
43
|
-
instructions: "Create a concise morning account briefing.",
|
|
44
|
-
maxDurationSec: 30,
|
|
45
|
-
});
|
|
46
|
-
```
|
|
47
|
-
|
|
48
|
-
## Dynamic personalized page content
|
|
49
|
-
|
|
50
|
-
```ts
|
|
51
|
-
video.generate({
|
|
52
|
-
input: `The page shows the Q2 onboarding funnel. 8,420 users started setup,
|
|
53
|
-
6,315 connected a data source, and 4,884 invited a teammate. The largest drop
|
|
54
|
-
is between connecting a data source and inviting a teammate.`,
|
|
55
|
-
instructions: "Respond to the visible funnel and focus on the largest drop.",
|
|
56
|
-
orientation: "landscape",
|
|
57
|
-
maxDurationSec: 24,
|
|
58
|
-
});
|
|
59
|
-
```
|
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
# Next.js quickstart
|
|
2
|
-
|
|
3
|
-
This is the smallest complete VanillaSky app: one server route, one React page,
|
|
4
|
-
and the built-in templates.
|
|
5
|
-
|
|
6
|
-
<!-- verify:start -->
|
|
7
|
-
```bash
|
|
8
|
-
npm install
|
|
9
|
-
cp .env.example .env.local
|
|
10
|
-
npm run build
|
|
11
|
-
npm run dev
|
|
12
|
-
```
|
|
13
|
-
<!-- verify:end -->
|
|
14
|
-
|
|
15
|
-
Before selecting **Generate video**, add your Anthropic API key and replace the
|
|
16
|
-
model placeholder with a current Claude Sonnet model ID available to your
|
|
17
|
-
account. Provider and model selection happen here, after SDK installation, so
|
|
18
|
-
VanillaSky never silently chooses either one.
|
|
19
|
-
|
|
20
|
-
Open <http://localhost:3000> and select **Generate video**.
|
|
21
|
-
|
|
22
|
-
The packaged development command supplies a non-secret marker only to
|
|
23
|
-
`next dev`, so the example authorizes local development and denies production
|
|
24
|
-
requests.
|
|
25
|
-
Replace the local-only authorization before deploying. See the
|
|
26
|
-
[Next.js guide](../../docs/integrate-nextjs.md) for production and optional
|
|
27
|
-
configuration.
|
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "vanillasky-nextjs-quickstart",
|
|
3
|
-
"private": true,
|
|
4
|
-
"version": "0.0.0",
|
|
5
|
-
"scripts": {
|
|
6
|
-
"dev": "cross-env VANILLASKY_LOCAL_DEMO=1 next dev",
|
|
7
|
-
"build": "next build",
|
|
8
|
-
"start": "next start"
|
|
9
|
-
},
|
|
10
|
-
"dependencies": {
|
|
11
|
-
"@ai-sdk/anthropic": "^4.0.41",
|
|
12
|
-
"@vanillaskyai/video": "0.5.8",
|
|
13
|
-
"ai": "^7.0.77",
|
|
14
|
-
"next": "16.3.2",
|
|
15
|
-
"react": "19.2.8",
|
|
16
|
-
"react-dom": "19.2.8"
|
|
17
|
-
},
|
|
18
|
-
"devDependencies": {
|
|
19
|
-
"@types/node": "^24.10.1",
|
|
20
|
-
"@types/react": "^19",
|
|
21
|
-
"@types/react-dom": "^19",
|
|
22
|
-
"cross-env": "10.1.0",
|
|
23
|
-
"typescript": "^5.9.3"
|
|
24
|
-
}
|
|
25
|
-
}
|
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
import { anthropic } from "@ai-sdk/anthropic";
|
|
2
|
-
import { streamText } from "ai";
|
|
3
|
-
import { createVideoHandler } from "@vanillaskyai/video/server";
|
|
4
|
-
|
|
5
|
-
const modelId = process.env.ANTHROPIC_MODEL;
|
|
6
|
-
if (!modelId) throw new Error("Set ANTHROPIC_MODEL in the server environment");
|
|
7
|
-
const model = anthropic(modelId);
|
|
8
|
-
|
|
9
|
-
const handle = createVideoHandler({
|
|
10
|
-
authorize: (request) => {
|
|
11
|
-
if (process.env.VANILLASKY_LOCAL_DEMO !== "1") return false;
|
|
12
|
-
const hostname = new URL(request.url).hostname;
|
|
13
|
-
return hostname === "localhost" || hostname === "127.0.0.1";
|
|
14
|
-
},
|
|
15
|
-
streamText: ({ systemPrompt, userPrompt, signal }) => streamText({
|
|
16
|
-
model,
|
|
17
|
-
system: systemPrompt,
|
|
18
|
-
prompt: userPrompt,
|
|
19
|
-
abortSignal: signal,
|
|
20
|
-
}),
|
|
21
|
-
});
|
|
22
|
-
|
|
23
|
-
export const POST = handle;
|
|
24
|
-
export const OPTIONS = handle;
|
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
"use client";
|
|
2
|
-
|
|
3
|
-
import { useState } from "react";
|
|
4
|
-
import { VideoPlayer, useVideo } from "@vanillaskyai/video/react";
|
|
5
|
-
|
|
6
|
-
export default function Page() {
|
|
7
|
-
const [input, setInput] = useState(
|
|
8
|
-
"Activation increased from 41% to 58% after guided onboarding.",
|
|
9
|
-
);
|
|
10
|
-
const video = useVideo();
|
|
11
|
-
|
|
12
|
-
return <main>
|
|
13
|
-
<h1>VanillaSky quickstart</h1>
|
|
14
|
-
<label>
|
|
15
|
-
Grounded input
|
|
16
|
-
<textarea value={input} onChange={(event) => setInput(event.target.value)} />
|
|
17
|
-
</label>
|
|
18
|
-
<button
|
|
19
|
-
disabled={!input.trim() || video.status === "streaming"}
|
|
20
|
-
onClick={() => { void video.generate({
|
|
21
|
-
input,
|
|
22
|
-
personalization: { firstName: "Maya" },
|
|
23
|
-
}); }}
|
|
24
|
-
>
|
|
25
|
-
Generate video
|
|
26
|
-
</button>
|
|
27
|
-
<output data-testid="status">{video.status}</output>
|
|
28
|
-
{video.error && <p role="alert">{video.error.message}</p>}
|
|
29
|
-
<VideoPlayer {...video.playerProps} />
|
|
30
|
-
</main>;
|
|
31
|
-
}
|
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"compilerOptions": {
|
|
3
|
-
"target": "ES2017",
|
|
4
|
-
"lib": ["dom", "dom.iterable", "esnext"],
|
|
5
|
-
"allowJs": true,
|
|
6
|
-
"skipLibCheck": true,
|
|
7
|
-
"strict": true,
|
|
8
|
-
"noEmit": true,
|
|
9
|
-
"incremental": true,
|
|
10
|
-
"esModuleInterop": true,
|
|
11
|
-
"module": "esnext",
|
|
12
|
-
"moduleResolution": "bundler",
|
|
13
|
-
"resolveJsonModule": true,
|
|
14
|
-
"isolatedModules": true,
|
|
15
|
-
"jsx": "react-jsx",
|
|
16
|
-
"plugins": [{ "name": "next" }]
|
|
17
|
-
},
|
|
18
|
-
"include": [
|
|
19
|
-
"next-env.d.ts",
|
|
20
|
-
"**/*.ts",
|
|
21
|
-
"**/*.tsx",
|
|
22
|
-
".next/types/**/*.ts",
|
|
23
|
-
".next/dev/types/**/*.ts"
|
|
24
|
-
],
|
|
25
|
-
"exclude": ["node_modules"]
|
|
26
|
-
}
|