@vanillaskyai/video 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (162) hide show
  1. package/CHANGELOG.md +45 -0
  2. package/LICENSE +201 -0
  3. package/PUBLIC-API.md +276 -0
  4. package/README.md +152 -0
  5. package/SECURITY.md +22 -0
  6. package/SUPPORT.md +28 -0
  7. package/bin/vanillasky.js +3 -0
  8. package/dist/bg-confetti-WNQXS7ZS.js +89 -0
  9. package/dist/bg-emoji-ETI47GLS.js +150 -0
  10. package/dist/bg-media-SVUZAMGX.js +99 -0
  11. package/dist/brand-message-F3RD4P3P.js +253 -0
  12. package/dist/builtin-server-F7YXRVCH.js +8 -0
  13. package/dist/catalog-types-BIhSpOWK.d.ts +74 -0
  14. package/dist/chart-bar-CNSC7BNK.js +304 -0
  15. package/dist/chart-counter-MCRHKN77.js +299 -0
  16. package/dist/chart-progress-ring-RMI4A7ZK.js +296 -0
  17. package/dist/check-runtime.d.ts +15 -0
  18. package/dist/check-runtime.js +102 -0
  19. package/dist/chunk-2E6T633S.js +27 -0
  20. package/dist/chunk-352ZZCET.js +83 -0
  21. package/dist/chunk-73NTSFFI.js +81 -0
  22. package/dist/chunk-A2X45FET.js +46 -0
  23. package/dist/chunk-ABPKM4GK.js +187 -0
  24. package/dist/chunk-ABZACD4G.js +216 -0
  25. package/dist/chunk-BCRUUJ6A.js +756 -0
  26. package/dist/chunk-CHLGW22F.js +3551 -0
  27. package/dist/chunk-CJ7F2CE6.js +104 -0
  28. package/dist/chunk-CR7FE7BW.js +109 -0
  29. package/dist/chunk-E7CL7UPB.js +109 -0
  30. package/dist/chunk-EE6PK6XC.js +18 -0
  31. package/dist/chunk-ERY4S6QV.js +385 -0
  32. package/dist/chunk-EVG2ZYIY.js +125 -0
  33. package/dist/chunk-FNAEQ7QO.js +155 -0
  34. package/dist/chunk-FQ47WXAJ.js +38 -0
  35. package/dist/chunk-FSG2PYWG.js +336 -0
  36. package/dist/chunk-GHJEAP4O.js +504 -0
  37. package/dist/chunk-GRMUV7QY.js +262 -0
  38. package/dist/chunk-IFOW65Z6.js +205 -0
  39. package/dist/chunk-IXCUY3B3.js +786 -0
  40. package/dist/chunk-L5BTTCZV.js +97 -0
  41. package/dist/chunk-LMG3GHLL.js +107 -0
  42. package/dist/chunk-OOPIETLW.js +101 -0
  43. package/dist/chunk-P22HHFDK.js +43 -0
  44. package/dist/chunk-QAAO7OAY.js +19 -0
  45. package/dist/chunk-S7HFLYNN.js +35 -0
  46. package/dist/chunk-SPVTJH3F.js +24 -0
  47. package/dist/chunk-W2XJKG6C.js +3177 -0
  48. package/dist/chunk-XB7LH4J2.js +301 -0
  49. package/dist/chunk-XGRA2MUV.js +242 -0
  50. package/dist/chunk-XTOXFMLM.js +105 -0
  51. package/dist/chunk-YC5MFXA5.js +132 -0
  52. package/dist/cli.d.ts +1 -0
  53. package/dist/cli.js +1681 -0
  54. package/dist/compose-video-CN3VSWST.js +13 -0
  55. package/dist/cta-logo-SVA5GP2A.js +209 -0
  56. package/dist/cta-media-E7VLLK42.js +215 -0
  57. package/dist/events-_wfjbmEp.d.ts +81 -0
  58. package/dist/incoming-call-62MFZL3Z.js +320 -0
  59. package/dist/index.d.ts +15 -0
  60. package/dist/index.js +14 -0
  61. package/dist/infographic-before-after-AXCDAWND.js +460 -0
  62. package/dist/infographic-feature-list-GW5GUE6O.js +266 -0
  63. package/dist/infographic-problem-solution-7NURJANA.js +340 -0
  64. package/dist/infographic-stat-row-J7IZR6ZN.js +241 -0
  65. package/dist/infographic-steps-VSZM6F5M.js +400 -0
  66. package/dist/kit-BMNcHqa9.d.ts +79 -0
  67. package/dist/prompt-input-LOUCRXCY.js +236 -0
  68. package/dist/react.d.ts +81 -0
  69. package/dist/react.js +835 -0
  70. package/dist/reaction-QG4CYZLQ.js +119 -0
  71. package/dist/server.d.ts +141 -0
  72. package/dist/server.js +498 -0
  73. package/dist/showcase-code-RUTFZR3H.js +351 -0
  74. package/dist/showcase-phone-6CP6XEU5.js +272 -0
  75. package/dist/showcase-terminal-MGAEQWPR.js +306 -0
  76. package/dist/showcase-web-Y4W2HVRJ.js +390 -0
  77. package/dist/social-conversation-75SD2637.js +695 -0
  78. package/dist/social-milestone-XI2QE5BO.js +292 -0
  79. package/dist/social-notification-2UOHQCUY.js +244 -0
  80. package/dist/social-review-stack-FDYZGZTJ.js +239 -0
  81. package/dist/social-testimonial-OQ7N52GJ.js +284 -0
  82. package/dist/social-tweet-5OLCNPPC.js +295 -0
  83. package/dist/state-DZcKuS32.d.ts +3 -0
  84. package/dist/system-prompt-A6UXNTPK.js +8 -0
  85. package/dist/template-catalog.d.ts +24 -0
  86. package/dist/template-catalog.js +6 -0
  87. package/dist/templates.d.ts +25 -0
  88. package/dist/templates.js +19 -0
  89. package/dist/test.d.ts +149 -0
  90. package/dist/test.js +272 -0
  91. package/dist/text-stream-6XHC7J47.js +10 -0
  92. package/dist/types-B-PZR6G_.d.ts +208 -0
  93. package/dist/validate-T7GBU2YF.js +8 -0
  94. package/docs/agent-integration.md +39 -0
  95. package/docs/architecture.md +84 -0
  96. package/docs/branding-and-personalization.md +87 -0
  97. package/docs/concepts.md +106 -0
  98. package/docs/custom-templates.md +329 -0
  99. package/docs/customization.md +141 -0
  100. package/docs/errors.md +70 -0
  101. package/docs/getting-started.md +111 -0
  102. package/docs/input-and-first-scene.md +41 -0
  103. package/docs/integrate-nextjs.md +79 -0
  104. package/docs/media-and-audio.md +69 -0
  105. package/docs/motion-and-effects.md +107 -0
  106. package/docs/persistence.md +95 -0
  107. package/docs/production.md +122 -0
  108. package/docs/prompt-and-input.md +185 -0
  109. package/docs/provider-integration.md +115 -0
  110. package/docs/reference/protocol.md +96 -0
  111. package/docs/reference/provider-adapters.md +158 -0
  112. package/docs/responsive-orientation.md +37 -0
  113. package/docs/security.md +38 -0
  114. package/docs/streaming-protocol.md +15 -0
  115. package/docs/testing.md +137 -0
  116. package/docs/use-cases.md +59 -0
  117. package/examples/custom-template/README.md +19 -0
  118. package/examples/custom-template/minimal-text.tsx +82 -0
  119. package/examples/custom-template/structured-data.tsx +104 -0
  120. package/examples/custom-template/supplied-media.tsx +92 -0
  121. package/examples/nextjs-quickstart/.env.example +2 -0
  122. package/examples/nextjs-quickstart/README.md +22 -0
  123. package/examples/nextjs-quickstart/next-env.d.ts +4 -0
  124. package/examples/nextjs-quickstart/package.json +24 -0
  125. package/examples/nextjs-quickstart/src/app/api/video/route.ts +22 -0
  126. package/examples/nextjs-quickstart/src/app/layout.tsx +5 -0
  127. package/examples/nextjs-quickstart/src/app/page.tsx +31 -0
  128. package/examples/nextjs-quickstart/tsconfig.json +26 -0
  129. package/package.json +167 -0
  130. package/registry/items/backgrounds.json +52 -0
  131. package/registry/items/barChart.json +237 -0
  132. package/registry/items/beforeAfter.json +180 -0
  133. package/registry/items/bigNumber.json +225 -0
  134. package/registry/items/brandMessage.json +158 -0
  135. package/registry/items/cardList.json +206 -0
  136. package/registry/items/chatMessenger.json +151 -0
  137. package/registry/items/chatWhatsapp.json +151 -0
  138. package/registry/items/codeEditor.json +184 -0
  139. package/registry/items/confetti.json +107 -0
  140. package/registry/items/ctaLogo.json +160 -0
  141. package/registry/items/ctaMedia.json +193 -0
  142. package/registry/items/emojiBurst.json +101 -0
  143. package/registry/items/incomingCall.json +170 -0
  144. package/registry/items/media.json +179 -0
  145. package/registry/items/milestone.json +205 -0
  146. package/registry/items/motion.json +45 -0
  147. package/registry/items/notification.json +182 -0
  148. package/registry/items/phoneMockup.json +254 -0
  149. package/registry/items/problemSolution.json +186 -0
  150. package/registry/items/progressRing.json +199 -0
  151. package/registry/items/promptInput.json +148 -0
  152. package/registry/items/reaction.json +170 -0
  153. package/registry/items/reviewStack.json +224 -0
  154. package/registry/items/steps.json +207 -0
  155. package/registry/items/template-context.json +31 -0
  156. package/registry/items/terminal.json +212 -0
  157. package/registry/items/testimonial.json +164 -0
  158. package/registry/items/theme.json +47 -0
  159. package/registry/items/tripleStats.json +228 -0
  160. package/registry/items/tweet.json +203 -0
  161. package/registry/items/typography.json +47 -0
  162. package/registry/items/webMockup.json +273 -0
package/dist/test.js ADDED
@@ -0,0 +1,272 @@
1
+ // src/test/clone.ts
2
+ function cloneValue(value) {
3
+ if (Array.isArray(value)) return value.map((item) => cloneValue(item));
4
+ if (value && typeof value === "object") {
5
+ return Object.fromEntries(
6
+ Object.entries(value).map(([key, child]) => [key, cloneValue(child)])
7
+ );
8
+ }
9
+ return value;
10
+ }
11
+ function deepFreeze(value) {
12
+ if (!value || typeof value !== "object" || Object.isFrozen(value)) return value;
13
+ for (const child of Object.values(value)) deepFreeze(child);
14
+ return Object.freeze(value);
15
+ }
16
+
17
+ // src/test/fixtures.ts
18
+ var portrait = {
19
+ input: {
20
+ input: "VanillaSky summarizes a grounded product update.",
21
+ orientation: "portrait"
22
+ },
23
+ parts: [
24
+ {
25
+ type: "scene.add",
26
+ scene: {
27
+ id: "portrait-summary",
28
+ templateId: "notification",
29
+ variables: { appName: "VanillaSky", message: "Grounded product update" },
30
+ timing: { fixedDuration: 4 }
31
+ }
32
+ },
33
+ { type: "plan.complete" }
34
+ ]
35
+ };
36
+ var landscape = {
37
+ input: {
38
+ input: "Revenue reached 42 million.",
39
+ orientation: "landscape"
40
+ },
41
+ parts: [
42
+ {
43
+ type: "scene.add",
44
+ scene: {
45
+ id: "landscape-metric",
46
+ templateId: "bigNumber",
47
+ variables: { texts: "Revenue", value: 42, label: "million" },
48
+ timing: { fixedDuration: 4 }
49
+ }
50
+ },
51
+ { type: "plan.complete" }
52
+ ]
53
+ };
54
+ var delayed = [
55
+ { type: "mock.delay", durationMs: 25 },
56
+ ...portrait.parts
57
+ ];
58
+ var truncated = [
59
+ {
60
+ type: "scene.add",
61
+ scene: {
62
+ id: "truncated-partial",
63
+ templateId: "notification",
64
+ variables: { appName: "VanillaSky", message: "Playable partial result" },
65
+ timing: { fixedDuration: 4 }
66
+ }
67
+ },
68
+ { type: "mock.raw", text: '{"type":"scene.add","scene":{"id":"unfinished' }
69
+ ];
70
+ var invalidScene = [
71
+ {
72
+ type: "scene.add",
73
+ scene: {
74
+ id: "invalid-scene",
75
+ templateId: "not-installed",
76
+ variables: {},
77
+ timing: { fixedDuration: 3 }
78
+ }
79
+ },
80
+ {
81
+ type: "scene.add",
82
+ scene: {
83
+ id: "valid-after-invalid",
84
+ templateId: "notification",
85
+ variables: { appName: "VanillaSky", message: "Recovered after invalid content" },
86
+ timing: { fixedDuration: 4 }
87
+ }
88
+ },
89
+ { type: "plan.complete" }
90
+ ];
91
+ var providerFailure = [
92
+ {
93
+ type: "mock.error",
94
+ message: "Provider failed with authorization=fixture-private-value"
95
+ }
96
+ ];
97
+ var contentFilter = [
98
+ {
99
+ type: "scene.add",
100
+ scene: {
101
+ id: "content-filter-partial",
102
+ templateId: "notification",
103
+ variables: { appName: "VanillaSky", message: "Safe partial result" },
104
+ timing: { fixedDuration: 4 }
105
+ }
106
+ },
107
+ { type: "plan.complete", finishReason: "content-filter" }
108
+ ];
109
+ var abort = [
110
+ {
111
+ type: "scene.add",
112
+ scene: {
113
+ id: "abort-partial",
114
+ templateId: "notification",
115
+ variables: { appName: "VanillaSky", message: "Partial result before cancellation" },
116
+ timing: { fixedDuration: 4 }
117
+ }
118
+ },
119
+ { type: "mock.wait-for-abort" }
120
+ ];
121
+ var timeout = [
122
+ {
123
+ type: "scene.add",
124
+ scene: {
125
+ id: "timeout-partial",
126
+ templateId: "notification",
127
+ variables: { appName: "VanillaSky", message: "Partial result before timeout" },
128
+ timing: { fixedDuration: 4 }
129
+ }
130
+ },
131
+ { type: "mock.wait-for-abort" }
132
+ ];
133
+ var videoFixtures = deepFreeze({
134
+ portrait,
135
+ landscape,
136
+ scenarios: {
137
+ success: portrait.parts,
138
+ delayed,
139
+ truncated,
140
+ invalidScene,
141
+ providerFailure,
142
+ contentFilter,
143
+ abort,
144
+ timeout
145
+ }
146
+ });
147
+
148
+ // src/test/mock-video-planner.ts
149
+ function abortError(signal) {
150
+ return signal.reason ?? new Error("Request aborted");
151
+ }
152
+ function wait(durationMs, signal) {
153
+ if (signal.aborted) return Promise.reject(abortError(signal));
154
+ return new Promise((resolve, reject) => {
155
+ const timer = setTimeout(() => {
156
+ signal.removeEventListener("abort", onAbort);
157
+ resolve();
158
+ }, durationMs);
159
+ const onAbort = () => {
160
+ clearTimeout(timer);
161
+ signal.removeEventListener("abort", onAbort);
162
+ reject(abortError(signal));
163
+ };
164
+ signal.addEventListener("abort", onAbort, { once: true });
165
+ });
166
+ }
167
+ function waitForAbort(signal) {
168
+ if (signal.aborted) return Promise.reject(abortError(signal));
169
+ return new Promise((_, reject) => {
170
+ signal.addEventListener("abort", () => reject(abortError(signal)), { once: true });
171
+ });
172
+ }
173
+ function fixtureParts(options) {
174
+ if (options.parts) return options.parts;
175
+ return videoFixtures.scenarios[options.scenario ?? "success"];
176
+ }
177
+ function providerFinishReason(parts) {
178
+ if (parts.some((part) => part.type === "mock.raw")) return "length";
179
+ let completed;
180
+ for (let index = parts.length - 1; index >= 0; index -= 1) {
181
+ if (parts[index].type === "plan.complete") {
182
+ completed = parts[index];
183
+ break;
184
+ }
185
+ }
186
+ if (completed?.type === "plan.complete" && completed.finishReason === "content-filter") {
187
+ return "content-filter";
188
+ }
189
+ if (completed?.type === "plan.complete" && completed.finishReason === "length") return "length";
190
+ return "stop";
191
+ }
192
+ function createMockVideoPlanner(options = {}) {
193
+ const delayMs = options.delayMs ?? 0;
194
+ if (!Number.isFinite(delayMs) || delayMs < 0) {
195
+ throw new Error("Mock planner delayMs must be a non-negative finite number");
196
+ }
197
+ const configured = cloneValue(fixtureParts(options));
198
+ const finishReason = providerFinishReason(configured);
199
+ return ({ signal }) => {
200
+ const parts = cloneValue(configured);
201
+ return {
202
+ textStream: (async function* () {
203
+ for (const part of parts) {
204
+ if (part.type === "mock.delay") {
205
+ await wait(part.durationMs, signal);
206
+ continue;
207
+ }
208
+ if (part.type === "mock.wait-for-abort") {
209
+ await waitForAbort(signal);
210
+ }
211
+ if (part.type === "mock.error") throw new Error(part.message);
212
+ if (delayMs > 0) await wait(delayMs, signal);
213
+ if (part.type === "mock.raw") yield part.text;
214
+ else yield `${JSON.stringify(part)}
215
+ `;
216
+ }
217
+ })(),
218
+ finishReason: Promise.resolve(finishReason)
219
+ };
220
+ };
221
+ }
222
+
223
+ // src/test/simulate-video-stream.ts
224
+ var DEFAULT_REQUEST_ID = "test-request";
225
+ var DEFAULT_RUN_ID = "test-run";
226
+ function timeoutError() {
227
+ const error = new Error("Simulation timeout detail");
228
+ error.name = "TimeoutError";
229
+ return error;
230
+ }
231
+ async function* simulateVideoStream(parts, options = {}) {
232
+ const timeoutMs = options.timeoutMs;
233
+ if (timeoutMs != null && (!Number.isFinite(timeoutMs) || timeoutMs < 0)) {
234
+ throw new Error("Simulation timeoutMs must be a non-negative finite number");
235
+ }
236
+ const { createVideo } = await import("./compose-video-CN3VSWST.js");
237
+ const { createTextDeltaVideoPlanner } = await import("./text-stream-6XHC7J47.js");
238
+ const { BUILTIN_SERVER_TEMPLATE_KIT } = await import("./builtin-server-F7YXRVCH.js");
239
+ const { createTemplateSceneValidator } = await import("./validate-T7GBU2YF.js");
240
+ const controller = new AbortController();
241
+ const forwardAbort = () => controller.abort(options.signal?.reason);
242
+ if (options.signal?.aborted) forwardAbort();
243
+ else options.signal?.addEventListener("abort", forwardAbort, { once: true });
244
+ const timeout2 = timeoutMs == null ? void 0 : setTimeout(() => controller.abort(timeoutError()), timeoutMs);
245
+ const streamText = createMockVideoPlanner({ parts: cloneValue(parts) });
246
+ const generate = createTextDeltaVideoPlanner({ streamText });
247
+ const input = cloneValue(options.input ?? videoFixtures.portrait.input);
248
+ const run = createVideo(input, {
249
+ requestId: options.requestId ?? DEFAULT_REQUEST_ID,
250
+ runId: options.runId ?? DEFAULT_RUN_ID,
251
+ capabilities: cloneValue(BUILTIN_SERVER_TEMPLATE_KIT.capabilities),
252
+ validateScene: createTemplateSceneValidator({ kit: BUILTIN_SERVER_TEMPLATE_KIT }),
253
+ getTemplatePacing: (templateId) => BUILTIN_SERVER_TEMPLATE_KIT.getTemplateMetadata(templateId),
254
+ invalidPartBehavior: options.invalidPartBehavior ?? "drop",
255
+ signal: controller.signal,
256
+ generate
257
+ });
258
+ try {
259
+ for await (const event of run.stream) {
260
+ yield cloneValue(event);
261
+ }
262
+ } finally {
263
+ if (timeout2) clearTimeout(timeout2);
264
+ options.signal?.removeEventListener("abort", forwardAbort);
265
+ if (!controller.signal.aborted) run.abort("Simulation stopped");
266
+ }
267
+ }
268
+ export {
269
+ createMockVideoPlanner,
270
+ simulateVideoStream,
271
+ videoFixtures
272
+ };
@@ -0,0 +1,10 @@
1
+ import {
2
+ createTextDeltaVideoPlanner
3
+ } from "./chunk-GRMUV7QY.js";
4
+ import "./chunk-E7CL7UPB.js";
5
+ import "./chunk-ERY4S6QV.js";
6
+ import "./chunk-EE6PK6XC.js";
7
+ import "./chunk-XGRA2MUV.js";
8
+ export {
9
+ createTextDeltaVideoPlanner
10
+ };
@@ -0,0 +1,208 @@
1
+ declare const VIDEO_PROTOCOL_VERSION: "0.4";
2
+ declare const VIDEO_SCHEMA_VERSION: "0.1";
3
+ type VideoOrientation = "portrait" | "landscape";
4
+ interface VideoBrandColors {
5
+ primary: string;
6
+ secondary: string;
7
+ foreground: string;
8
+ surface: string;
9
+ surfaceElevated: string;
10
+ muted: string;
11
+ }
12
+ type VideoBackgroundPreset = "cosmic" | "horizon" | "twilight" | "meadow" | "velvet" | "flamingo" | "peach" | "saffron" | "black" | "midnight" | "aubergine" | "coal" | "navy";
13
+ type VideoBackgroundInput = VideoBackgroundPreset | {
14
+ color: string;
15
+ } | {
16
+ colors: [string, string];
17
+ };
18
+ type VideoBackground = {
19
+ type: "solid";
20
+ color: string;
21
+ } | {
22
+ type: "gradient";
23
+ colors: [string, string];
24
+ };
25
+ interface VideoBrandInput {
26
+ name?: string;
27
+ logoUrl?: string;
28
+ font?: string;
29
+ scriptFont?: string;
30
+ background?: VideoBackgroundInput;
31
+ colors?: Partial<VideoBrandColors>;
32
+ }
33
+ interface VideoBrand {
34
+ name?: string;
35
+ logoUrl?: string;
36
+ font: string;
37
+ scriptFont: string;
38
+ background: VideoBackground;
39
+ colors: VideoBrandColors;
40
+ }
41
+ interface VideoStyle {
42
+ brand: VideoBrand;
43
+ preset?: string;
44
+ defaultBackgroundEffect?: string;
45
+ defaultTextArchetype?: string;
46
+ defaultTransition?: string;
47
+ density?: string;
48
+ motion?: string;
49
+ }
50
+ interface VideoStyleOptions {
51
+ density?: "airy" | "normal" | "packed";
52
+ motion?: "calm" | "normal" | "punchy";
53
+ textArchetype?: "subtle" | "typewriter" | "wordStagger" | "slam" | "cinematic" | "heroWord";
54
+ backgroundEffect?: "static" | "slow-zoom-in" | "slow-zoom-out" | "ken-burns" | "drift" | "pulse" | "breathe" | "slow-tilt" | "camera-shake";
55
+ }
56
+ interface VideoTiming {
57
+ beatStart?: number;
58
+ beatEnd?: number;
59
+ startTime?: number;
60
+ endTime?: number;
61
+ fixedDuration?: number;
62
+ durationWeight?: number;
63
+ }
64
+ interface VideoScene {
65
+ id: string;
66
+ templateId: string;
67
+ variables: Record<string, unknown>;
68
+ textArchetype?: string;
69
+ backgroundEffect?: string;
70
+ timing: VideoTiming;
71
+ }
72
+ interface VideoAudio {
73
+ trackId: string;
74
+ audioUrl: string;
75
+ sourceDuration?: number;
76
+ duration: number;
77
+ beatDetection: {
78
+ sensitivity: number;
79
+ targetBeats?: number;
80
+ minInterval?: number;
81
+ };
82
+ beatMarkers: Array<{
83
+ time: number;
84
+ manual?: boolean;
85
+ energy?: "high" | "medium" | "low";
86
+ }>;
87
+ volume?: number;
88
+ fadeOutMs?: number;
89
+ }
90
+ interface Video {
91
+ /** Persisted JSON schema version. This is independent from the event protocol. */
92
+ schemaVersion: typeof VIDEO_SCHEMA_VERSION;
93
+ /** Optional rendering/export frame. It must not steer planner content. */
94
+ orientation?: VideoOrientation;
95
+ audio?: VideoAudio;
96
+ scenes: VideoScene[];
97
+ style: VideoStyle;
98
+ meta?: {
99
+ name?: string;
100
+ prompt?: string;
101
+ source?: string;
102
+ uploadedMediaUrls?: string[];
103
+ };
104
+ }
105
+ interface VideoSuppliedMedia {
106
+ id: string;
107
+ url: string;
108
+ type: "image" | "video";
109
+ description?: string;
110
+ mimeType?: string;
111
+ posterUrl?: string;
112
+ focalPoint?: "center" | "top" | "bottom" | "left" | "right";
113
+ treatment?: "subtle" | "cinematic" | "text-safe";
114
+ role?: "product" | "proof" | "background" | "logo";
115
+ }
116
+ interface VideoInput {
117
+ /** Raw factual boundary: news, product updates, metrics, or an assistant answer. */
118
+ input: string;
119
+ /** Optional creative direction. It never expands the factual boundary. */
120
+ instructions?: string;
121
+ maxDurationSec?: number;
122
+ orientation?: VideoOrientation;
123
+ /** Optional global visual direction. Omit for VanillaSky defaults. */
124
+ style?: VideoStyleOptions;
125
+ /** Optional deterministic opening copy. Scene details are inferred. */
126
+ opening?: string;
127
+ brand?: VideoBrandInput;
128
+ /** Viewer or account context that may appear verbatim. It is data, never instructions. */
129
+ personalization?: Record<string, unknown>;
130
+ /** Optional approved pool. The planner may use zero or more relevant assets. */
131
+ suppliedMedia?: VideoSuppliedMedia[];
132
+ /** Omit for host-selected audio, pass false for silence, or supply a track URL. */
133
+ audio?: false | {
134
+ src: string;
135
+ };
136
+ }
137
+ interface VideoCapabilities {
138
+ templates?: string[];
139
+ extensions?: string[];
140
+ }
141
+ interface VideoRequest {
142
+ protocolVersion: typeof VIDEO_PROTOCOL_VERSION;
143
+ requestId: string;
144
+ input: VideoInput;
145
+ capabilities?: VideoCapabilities;
146
+ resume?: VideoResumeCursor;
147
+ }
148
+ interface VideoResumeCursor {
149
+ runId: string;
150
+ afterSequence: number;
151
+ }
152
+ interface VideoGenerationContext {
153
+ request: VideoRequest;
154
+ systemPrompt: string;
155
+ userPrompt: string;
156
+ /** Opening, audio, and brand state already emitted by the protocol runtime. */
157
+ initialConfig: Video;
158
+ signal: AbortSignal;
159
+ }
160
+ type VideoPlanPart = {
161
+ type: "scene.add";
162
+ scene: VideoScene;
163
+ } | {
164
+ type: "scene.patch";
165
+ sceneId: string;
166
+ patch: Partial<Omit<VideoScene, "id" | "templateId">>;
167
+ } | {
168
+ type: "asset.patch";
169
+ sceneId: string;
170
+ variables: Record<string, unknown>;
171
+ } | {
172
+ type: "plan.complete";
173
+ finishReason?: "stop" | "length" | "content-filter" | "other";
174
+ } | {
175
+ type: "plan.error";
176
+ error: {
177
+ message: string;
178
+ code?: string;
179
+ recoverable?: boolean;
180
+ };
181
+ };
182
+ type VideoPlanner = (context: VideoGenerationContext) => AsyncIterable<VideoPlanPart>;
183
+ interface VideoSceneValidationContext {
184
+ input: VideoInput;
185
+ previousScenes: readonly VideoScene[];
186
+ }
187
+ interface VideoTemplatePacing {
188
+ jobs?: readonly string[];
189
+ schema?: {
190
+ readonly properties: Readonly<Record<string, {
191
+ readonly default?: unknown;
192
+ }>>;
193
+ };
194
+ minDuration?: number;
195
+ preferredDuration?: number;
196
+ timing?: {
197
+ readonly contentFields: readonly string[];
198
+ readonly contentUnit: "words" | "characters" | "items";
199
+ };
200
+ }
201
+ type VideoSceneValidator = (scene: VideoScene, context: VideoSceneValidationContext) => void;
202
+ interface VideoSnapshotRetention {
203
+ source?: boolean;
204
+ instructions?: boolean;
205
+ suppliedMediaUrls?: boolean;
206
+ }
207
+
208
+ export { type Video as V, type VideoAudio as a, type VideoBackground as b, type VideoBrand as c, type VideoInput as d, type VideoOrientation as e, type VideoScene as f, type VideoStyle as g, type VideoStyleOptions as h, type VideoSuppliedMedia as i, type VideoCapabilities as j, VIDEO_PROTOCOL_VERSION as k, type VideoGenerationContext as l, type VideoPlanner as m, type VideoRequest as n, type VideoSceneValidator as o, type VideoTemplatePacing as p, type VideoSnapshotRetention as q, type VideoResumeCursor as r, type VideoSceneValidationContext as s };
@@ -0,0 +1,8 @@
1
+ import {
2
+ createTemplateSceneValidator
3
+ } from "./chunk-LMG3GHLL.js";
4
+ import "./chunk-352ZZCET.js";
5
+ import "./chunk-73NTSFFI.js";
6
+ export {
7
+ createTemplateSceneValidator
8
+ };
@@ -0,0 +1,39 @@
1
+ [← Documentation home](../README.md) · [Next: Getting started →](getting-started.md)
2
+
3
+ # Agent integration
4
+
5
+ Use the same public path a human developer uses. The optional repository skill
6
+ packages these instructions for coding agents:
7
+
8
+ ```bash
9
+ npx skills add VanillaSkyAi/video@vanillasky
10
+ ```
11
+
12
+ ## Build the first response
13
+
14
+ 1. Install `@vanillaskyai/video`, `ai`, and one model provider.
15
+ 2. Put the provider key in an ignored `.env.local` file.
16
+ 3. Connect the application's LLM with `createVideoHandler` in an authenticated
17
+ server route.
18
+ 4. Pass grounded text or structured data as `input`.
19
+ 5. Call `useVideo()` and render `VideoPlayer` in React.
20
+ 6. Verify one complete response in a browser before adding optional features.
21
+
22
+ The root README and `examples/nextjs-quickstart` are the canonical working
23
+ reference. Built-in templates require no registry setup.
24
+
25
+ ## Keep responsibilities clear
26
+
27
+ VanillaSky owns the planning prompt, trusted templates, validation, streaming
28
+ protocol, and player. The application owns its LLM, credentials,
29
+ authentication, source retrieval, media licensing, persistence, and product
30
+ UI.
31
+
32
+ Use `input` for facts the video may claim, `instructions` for presentation
33
+ direction, and `personalization` for viewer or account context. Treat all three
34
+ as untrusted application data. Add brand, media, audio, persistence, or custom
35
+ templates only when the requested experience needs them.
36
+
37
+ If a required credential is missing, ask the developer to add it to the ignored
38
+ environment file. Never invent another provider or request secret values in
39
+ chat.
@@ -0,0 +1,84 @@
1
+ # Architecture
2
+
3
+ VanillaSky turns context into a visual response. Your application and its AI
4
+ decide what matters; VanillaSky turns that decision into a grounded, validated,
5
+ embedded video that starts playing while it is still being composed.
6
+
7
+ ## The shortest mental model
8
+
9
+ ```text
10
+ application context
11
+ → app-owned model
12
+ → trusted scene plan
13
+ → validation and streaming protocol
14
+ → embedded React player
15
+ ```
16
+
17
+ VanillaSky does not choose your model, retrieve your application context, or
18
+ generate executable UI code. It supplies the video-planning prompt, trusted
19
+ visual vocabulary, validation, streaming lifecycle, and player.
20
+
21
+ ## Repository map
22
+
23
+ | Location | Purpose |
24
+ | --- | --- |
25
+ | `src/server/create-video-handler.ts` | Public server integration and provider adapter boundary |
26
+ | `src/server/prompts/` | System and user prompts sent to the app-owned model |
27
+ | `src/server/model/` | Converts provider text deltas into typed video plan parts |
28
+ | `src/protocol/` | Shared request, event, validation, checksum, and SSE contract |
29
+ | `src/player/` | Browser stream client, `useVideo`, timeline, and React player |
30
+ | `src/visual-system/catalog/` | Template metadata, schemas, loading, and planner catalog |
31
+ | `src/visual-system/scene-templates/` | Complete scenes the model may select |
32
+ | `src/visual-system/primitives/` | Reusable visual components used inside scenes |
33
+ | `src/visual-system/backgrounds/` | Standalone background renderers |
34
+ | `src/visual-system/motion/` | Animation functions and timing behavior |
35
+ | `src/visual-system/theme/` | Color and design tokens |
36
+ | `src/cli/` | `vanillasky create`, `add`, `sync`, `check`, `list`, and `describe` for customer-owned templates |
37
+ | `registry/items/` | Generated distributable copies installed into customer projects |
38
+ | `src/index.ts`, `src/server.ts`, `src/react.ts`, `src/templates.ts`, `src/template-catalog.ts`, `src/test.ts` | The six small public package entry points |
39
+
40
+ The source of truth for built-in visuals is `src/visual-system`. The JSON files
41
+ in `registry/items` are distribution artifacts, kept flat so the CLI can address
42
+ every installable item by a stable name. Their `meta.vanillasky.layer` and
43
+ `category` fields distinguish full templates, primitives, effects, and shared
44
+ support code. Run `npm run registry:sync` after changing canonical visual source.
45
+
46
+ Customer applications do not edit those internal locations. Their source of
47
+ truth is one file per visual under `vanillasky/templates/`; `vanillasky sync`
48
+ derives a browser registry in `vanillasky/index.ts` and a React-free
49
+ prompt/validation registry in `vanillasky/server.ts`.
50
+
51
+ ## Request flow
52
+
53
+ 1. `useVideo().generate(...)` sends grounded input and live application context
54
+ to the authenticated application route.
55
+ 2. `createVideoHandler(...)` builds the prompts and calls the application's
56
+ `streamText` adapter. This is where OpenAI, Anthropic, or another model is
57
+ connected.
58
+ 3. The system prompt combines the composition rules with the trusted template
59
+ catalog, including generated metadata for customer-owned templates. The user
60
+ prompt serializes the factual input, instructions,
61
+ personalization, brand, and approved media.
62
+ 4. The model streams NDJSON plan parts. The server parses and validates complete
63
+ scenes before emitting versioned video events.
64
+ 5. The browser reduces those events into a deterministic `Video`; `VideoPlayer`
65
+ starts as soon as its first playable scene arrives.
66
+
67
+ ## Where to change common behavior
68
+
69
+ - Model connection: `src/server/create-video-handler.ts`
70
+ - Base planning rules: `src/server/prompts/system-prompt.ts`
71
+ - Per-request context formatting: `src/server/prompts/user-prompt.ts`
72
+ - Template-specific prompt catalog: `src/visual-system/catalog/prompt.ts`
73
+ - Wire contract: `src/protocol/types.ts` and `src/protocol/events.ts`
74
+ - Scene rendering: `src/visual-system/scene-templates/`
75
+ - Background effects: `src/visual-system/backgrounds/` and
76
+ `src/visual-system/scene-templates/background-effect.ts`
77
+ - Text effects: `src/visual-system/scene-templates/text-archetypes.ts`
78
+ - Gradients and design tokens: `src/visual-system/theme/`
79
+
80
+ ## Public vocabulary
81
+
82
+ Use `Video`, `VideoInput`, `useVideo`, and `VideoPlayer` for the product API.
83
+ Use “video response” when describing the lifecycle or output category. Use
84
+ “motion” only for animation behavior inside the visual system.