@vanillaskyai/video 0.7.1 → 0.8.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 +59 -0
- package/PUBLIC-API.md +83 -79
- package/README.md +16 -15
- package/dist/{builtin-server-KB7FKF6A.js → builtin-server-CUYPIDE7.js} +2 -2
- package/dist/check-runtime.js +1 -1
- package/dist/{chunk-2PYO6VAC.js → chunk-2DOLF4QN.js} +1 -43
- package/dist/{chunk-GAIOHGNR.js → chunk-2OKB27TG.js} +42 -45
- package/dist/{chunk-K746NUIP.js → chunk-2USVB3CY.js} +80 -29
- package/dist/{chunk-RE4IMWJR.js → chunk-3VRMKFM7.js} +87 -14
- package/dist/{chunk-JNN3EYVP.js → chunk-4UNKZEAT.js} +46 -46
- package/dist/{chunk-2XT4MZ76.js → chunk-APQKY6P3.js} +36 -3
- package/dist/{chunk-LLPMVDJZ.js → chunk-L7QGGKJ2.js} +69 -38
- package/dist/{chunk-RGF452LL.js → chunk-OWBT45EN.js} +1 -1
- package/dist/{chunk-LKBZX7GV.js → chunk-PP3MCPN4.js} +4 -4
- package/dist/chunk-ZD2VTUYR.js +28 -0
- package/dist/cli.js +131 -24
- package/dist/{compose-video-PD6LKKRK.js → compose-video-2NFH4DZY.js} +3 -3
- package/dist/{events-B6qS1Lsb.d.ts → events-BQ6z264n.d.ts} +1 -1
- package/dist/index.d.ts +5 -67
- package/dist/index.js +2 -4
- package/dist/{kit-8g46H2RZ.d.ts → kit-tQnCPMOM.d.ts} +1 -1
- package/dist/react.d.ts +33 -53
- package/dist/react.js +642 -536
- package/dist/server.d.ts +11 -13
- package/dist/server.js +170 -81
- 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-ROTNGSD4.js → text-stream-54LXABJW.js} +1 -1
- package/dist/{types-2wHBqtg8.d.ts → types-CEh9eUFu.d.ts} +1 -1
- package/dist/{types-DrABlRa7.d.ts → types-DWFO6qca.d.ts} +1 -1
- package/docs/agent-integration.md +9 -3
- package/docs/architecture.md +8 -10
- package/docs/concepts.md +16 -40
- package/docs/custom-templates.md +27 -23
- package/docs/customization.md +12 -49
- package/docs/errors.md +58 -68
- package/docs/getting-started.md +21 -14
- package/docs/media-and-audio.md +2 -2
- package/docs/performance.md +69 -0
- package/docs/persistence.md +12 -27
- package/docs/production.md +2 -2
- package/docs/provider-integration.md +11 -5
- package/docs/reference/provider-adapters.md +37 -89
- package/docs/security.md +1 -1
- package/docs/streaming-protocol.md +2 -2
- package/examples/custom-template/README.md +6 -6
- package/package.json +6 -7
- package/registry/items/barChart.json +2 -2
- package/registry/items/bigNumber.json +2 -2
- package/registry/items/brandMessage.json +2 -2
- package/registry/items/cardList.json +2 -2
- package/registry/items/codeEditor.json +2 -2
- package/registry/items/ctaLogo.json +2 -2
- package/registry/items/ctaMedia.json +2 -2
- package/registry/items/incomingCall.json +2 -2
- package/registry/items/media.json +2 -2
- package/registry/items/milestone.json +2 -2
- package/registry/items/notification.json +2 -2
- package/registry/items/phoneMockup.json +2 -2
- package/registry/items/problemSolution.json +2 -2
- package/registry/items/progressRing.json +2 -2
- package/registry/items/promptInput.json +2 -2
- package/registry/items/reaction.json +2 -2
- package/registry/items/reviewStack.json +2 -2
- package/registry/items/steps.json +2 -2
- package/registry/items/terminal.json +2 -2
- package/registry/items/testimonial.json +2 -2
- package/registry/items/tripleStats.json +2 -2
- package/registry/items/tweet.json +2 -2
- package/registry/items/webMockup.json +2 -2
- package/starters/video-chat/.env.example +4 -2
- package/starters/video-chat/README.md +17 -5
- package/starters/video-chat/package.json +2 -4
- package/starters/video-chat/providers/speech.ts +20 -0
- package/starters/video-chat/providers/video.ts +66 -0
- package/starters/video-chat/providers.ts +3 -0
- package/starters/video-chat/server.ts +4 -80
- package/starters/video-chat/stock.ts +114 -99
- package/dist/chunk-SKRGRKHY.js +0 -142
- package/dist/state-DZcKuS32.d.ts +0 -3
- package/docs/branding-and-personalization.md +0 -87
- package/examples/custom-template/supplied-media.tsx +0 -92
|
@@ -21,9 +21,11 @@ import {
|
|
|
21
21
|
// src/player/video-frame.tsx
|
|
22
22
|
import {
|
|
23
23
|
createElement,
|
|
24
|
+
Component,
|
|
24
25
|
lazy,
|
|
25
26
|
Suspense,
|
|
26
27
|
useState,
|
|
28
|
+
useEffect,
|
|
27
29
|
useSyncExternalStore
|
|
28
30
|
} from "react";
|
|
29
31
|
|
|
@@ -46,6 +48,54 @@ function scaleSafeZone(zone, factor) {
|
|
|
46
48
|
|
|
47
49
|
// src/player/video-frame.tsx
|
|
48
50
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
51
|
+
function PresentedScene({ notify }) {
|
|
52
|
+
useEffect(() => {
|
|
53
|
+
if (!notify) return;
|
|
54
|
+
const frame = requestAnimationFrame(() => {
|
|
55
|
+
try {
|
|
56
|
+
void Promise.resolve(notify()).catch(() => void 0);
|
|
57
|
+
} catch {
|
|
58
|
+
}
|
|
59
|
+
});
|
|
60
|
+
return () => cancelAnimationFrame(frame);
|
|
61
|
+
}, [notify]);
|
|
62
|
+
return null;
|
|
63
|
+
}
|
|
64
|
+
function SafeScene({ scene, onFramePresented }) {
|
|
65
|
+
const copy = scene.narration?.trim() || Object.values(scene.variables).filter((value) => typeof value === "string" && !/^https?:/i.test(value)).join(" ");
|
|
66
|
+
return /* @__PURE__ */ jsxs(
|
|
67
|
+
"div",
|
|
68
|
+
{
|
|
69
|
+
"data-scene-fallback": "true",
|
|
70
|
+
style: {
|
|
71
|
+
width: "100%",
|
|
72
|
+
height: "100%",
|
|
73
|
+
display: "grid",
|
|
74
|
+
placeContent: "center",
|
|
75
|
+
boxSizing: "border-box",
|
|
76
|
+
padding: "8%",
|
|
77
|
+
background: "#090712",
|
|
78
|
+
color: "#fff",
|
|
79
|
+
font: "500 clamp(20px, 4vw, 64px)/1.3 system-ui",
|
|
80
|
+
overflow: "hidden"
|
|
81
|
+
},
|
|
82
|
+
children: [
|
|
83
|
+
/* @__PURE__ */ jsx(PresentedScene, { notify: onFramePresented }),
|
|
84
|
+
/* @__PURE__ */ jsx("p", { children: copy.slice(0, 600) || "Your response continues." }),
|
|
85
|
+
/* @__PURE__ */ jsx("small", { style: { fontSize: "0.3em" }, role: "status", children: "This scene uses a simpler layout." })
|
|
86
|
+
]
|
|
87
|
+
}
|
|
88
|
+
);
|
|
89
|
+
}
|
|
90
|
+
var SceneBoundary = class extends Component {
|
|
91
|
+
state = { failed: false };
|
|
92
|
+
static getDerivedStateFromError() {
|
|
93
|
+
return { failed: true };
|
|
94
|
+
}
|
|
95
|
+
render() {
|
|
96
|
+
return this.state.failed ? /* @__PURE__ */ jsx(SafeScene, { scene: this.props.scene, onFramePresented: this.props.onFramePresented }) : this.props.children;
|
|
97
|
+
}
|
|
98
|
+
};
|
|
49
99
|
var SCENE_TRANSITION_SECONDS = 0.3;
|
|
50
100
|
var SceneVideoBackdrop = lazy(() => import("./scene-video-backdrop-XFPX4O3I.js").then((module) => ({ default: module.SceneVideoBackdrop })));
|
|
51
101
|
var MEDIA_PREROLL_SECONDS = 1.2;
|
|
@@ -102,6 +152,7 @@ function sceneBackgroundChanges(left, right) {
|
|
|
102
152
|
return mediaUrl(left) !== mediaUrl(right);
|
|
103
153
|
}
|
|
104
154
|
function SceneLayer({
|
|
155
|
+
onFramePresented,
|
|
105
156
|
kit,
|
|
106
157
|
config,
|
|
107
158
|
range,
|
|
@@ -146,41 +197,42 @@ function SceneLayer({
|
|
|
146
197
|
"--vanillasky-transition-semantic-visibility": layer === "incoming" ? "hidden" : "visible",
|
|
147
198
|
"--vanillasky-template-surface": externalVideoBackdrop !== false ? "transparent" : void 0
|
|
148
199
|
},
|
|
149
|
-
children: template ? /* @__PURE__ */ jsx(Suspense, { fallback: /* @__PURE__ */ jsx(
|
|
200
|
+
children: template ? /* @__PURE__ */ jsx(SceneBoundary, { scene: range.scene, onFramePresented, children: /* @__PURE__ */ jsxs(Suspense, { fallback: /* @__PURE__ */ jsx(
|
|
150
201
|
"div",
|
|
151
202
|
{
|
|
152
203
|
"data-template-loading": range.scene.templateId,
|
|
153
204
|
style: { position: "absolute", inset: 0 }
|
|
154
205
|
}
|
|
155
|
-
), children:
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
] })
|
|
206
|
+
), children: [
|
|
207
|
+
/* @__PURE__ */ jsx(PresentedScene, { notify: onFramePresented }),
|
|
208
|
+
createElement(template.component, {
|
|
209
|
+
variables: {
|
|
210
|
+
...template.defaults ?? getTemplateDefaults(template.schema),
|
|
211
|
+
...range.scene.variables
|
|
212
|
+
},
|
|
213
|
+
style: config.style,
|
|
214
|
+
progress,
|
|
215
|
+
motionProgress,
|
|
216
|
+
beatIntensity: 0,
|
|
217
|
+
width,
|
|
218
|
+
height,
|
|
219
|
+
textArchetype: range.scene.textArchetype ?? config.style.defaultTextArchetype,
|
|
220
|
+
backgroundEffect: range.scene.backgroundEffect ?? config.style.defaultBackgroundEffect,
|
|
221
|
+
safeZone: scaleSafeZone(
|
|
222
|
+
getSafeZone(config.orientation),
|
|
223
|
+
resolveDensity(config.style.density).safeZoneScale
|
|
224
|
+
),
|
|
225
|
+
sceneDuration: duration,
|
|
226
|
+
isPlaying: playing
|
|
227
|
+
})
|
|
228
|
+
] }) }, range.scene.id) : /* @__PURE__ */ jsx(SafeScene, { scene: range.scene, onFramePresented })
|
|
178
229
|
}
|
|
179
230
|
)
|
|
180
231
|
}
|
|
181
232
|
);
|
|
182
233
|
}
|
|
183
234
|
function VideoFrame({
|
|
235
|
+
onFramePresented,
|
|
184
236
|
kit,
|
|
185
237
|
config,
|
|
186
238
|
time,
|
|
@@ -212,17 +264,14 @@ function VideoFrame({
|
|
|
212
264
|
);
|
|
213
265
|
}
|
|
214
266
|
if (!kit.getTemplate(active.scene.templateId)) {
|
|
215
|
-
return /* @__PURE__ */
|
|
267
|
+
return /* @__PURE__ */ jsx(
|
|
216
268
|
"div",
|
|
217
269
|
{
|
|
218
270
|
"data-video-frame": "unsupported",
|
|
219
271
|
"data-template-id": active.scene.templateId,
|
|
220
272
|
className,
|
|
221
273
|
style: { width, height, background: "#090712", color: "#fff", ...style },
|
|
222
|
-
children:
|
|
223
|
-
"Unsupported template: ",
|
|
224
|
-
active.scene.templateId
|
|
225
|
-
]
|
|
274
|
+
children: /* @__PURE__ */ jsx(SafeScene, { scene: active.scene, onFramePresented })
|
|
226
275
|
}
|
|
227
276
|
);
|
|
228
277
|
}
|
|
@@ -386,6 +435,7 @@ function VideoFrame({
|
|
|
386
435
|
/* @__PURE__ */ jsx(
|
|
387
436
|
SceneLayer,
|
|
388
437
|
{
|
|
438
|
+
onFramePresented,
|
|
389
439
|
kit,
|
|
390
440
|
config,
|
|
391
441
|
range: active,
|
|
@@ -429,6 +479,7 @@ function VideoFrame({
|
|
|
429
479
|
/* @__PURE__ */ jsx(
|
|
430
480
|
SceneLayer,
|
|
431
481
|
{
|
|
482
|
+
onFramePresented,
|
|
432
483
|
kit,
|
|
433
484
|
config,
|
|
434
485
|
range: active,
|
|
@@ -3,9 +3,10 @@ import {
|
|
|
3
3
|
} from "./chunk-E7CL7UPB.js";
|
|
4
4
|
import {
|
|
5
5
|
applyVideoEvent,
|
|
6
|
-
|
|
6
|
+
checksumVideo,
|
|
7
|
+
createVideoEventFactory,
|
|
7
8
|
createVideoState
|
|
8
|
-
} from "./chunk-
|
|
9
|
+
} from "./chunk-2OKB27TG.js";
|
|
9
10
|
import {
|
|
10
11
|
parseVideoPlanPart,
|
|
11
12
|
safePublicDiagnostic
|
|
@@ -15,10 +16,8 @@ import {
|
|
|
15
16
|
MAX_RETAINED_MEDIA_URLS,
|
|
16
17
|
MAX_RETAINED_MEDIA_URL_LENGTH,
|
|
17
18
|
MAX_RETAINED_SOURCE_LENGTH,
|
|
18
|
-
checksumVideo,
|
|
19
|
-
createVideoEventFactory,
|
|
20
19
|
parseVideo
|
|
21
|
-
} from "./chunk-
|
|
20
|
+
} from "./chunk-2DOLF4QN.js";
|
|
22
21
|
import {
|
|
23
22
|
VIDEO_SCHEMA_VERSION,
|
|
24
23
|
createVideoRequest
|
|
@@ -32,6 +31,45 @@ import {
|
|
|
32
31
|
resolveVideoBrand
|
|
33
32
|
} from "./chunk-XGRA2MUV.js";
|
|
34
33
|
|
|
34
|
+
// src/replayable-stream.ts
|
|
35
|
+
function createReplayableStream(source, onError) {
|
|
36
|
+
const buffered = [];
|
|
37
|
+
const waiters = /* @__PURE__ */ new Set();
|
|
38
|
+
let done = false;
|
|
39
|
+
let streamError;
|
|
40
|
+
const notify = () => {
|
|
41
|
+
for (const waiter of waiters) waiter();
|
|
42
|
+
waiters.clear();
|
|
43
|
+
};
|
|
44
|
+
void (async () => {
|
|
45
|
+
try {
|
|
46
|
+
for await (const item of source) {
|
|
47
|
+
buffered.push(item);
|
|
48
|
+
notify();
|
|
49
|
+
}
|
|
50
|
+
} catch (cause) {
|
|
51
|
+
streamError = cause;
|
|
52
|
+
onError(cause);
|
|
53
|
+
} finally {
|
|
54
|
+
done = true;
|
|
55
|
+
notify();
|
|
56
|
+
}
|
|
57
|
+
})();
|
|
58
|
+
return {
|
|
59
|
+
async *[Symbol.asyncIterator]() {
|
|
60
|
+
let index = 0;
|
|
61
|
+
while (true) {
|
|
62
|
+
while (index < buffered.length) yield buffered[index++];
|
|
63
|
+
if (done) {
|
|
64
|
+
if (streamError) throw streamError;
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
await new Promise((resolve) => waiters.add(resolve));
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
|
|
35
73
|
// src/server/prompts/user-prompt.ts
|
|
36
74
|
var SUPPLIED_MEDIA_REFERENCE_BASE = "https://vanillasky.invalid/supplied/";
|
|
37
75
|
function suppliedMediaReferences(input) {
|
|
@@ -543,14 +581,18 @@ function createId(prefix) {
|
|
|
543
581
|
}
|
|
544
582
|
function createProviderLifecycle() {
|
|
545
583
|
const results = [];
|
|
584
|
+
const reportedWarnings = [];
|
|
546
585
|
const sink = {
|
|
586
|
+
reportWarning(warning) {
|
|
587
|
+
reportedWarnings.push(warning);
|
|
588
|
+
},
|
|
547
589
|
registerProviderResult(result) {
|
|
548
590
|
results.push(result);
|
|
549
591
|
}
|
|
550
592
|
};
|
|
551
593
|
const settle = async () => {
|
|
552
594
|
const resolved = await Promise.all(results);
|
|
553
|
-
const warnings = resolved.flatMap((result) => result.warnings);
|
|
595
|
+
const warnings = [...reportedWarnings, ...resolved.flatMap((result) => result.warnings)];
|
|
554
596
|
const uniqueWarnings = [...new Map(warnings.map((warning) => [
|
|
555
597
|
`${warning.code}\0${warning.category}\0${warning.message}\0${warning.sceneId ?? ""}`,
|
|
556
598
|
warning
|
|
@@ -605,6 +647,14 @@ function createVideo(rawInput, options) {
|
|
|
605
647
|
const eventSource = (async function* () {
|
|
606
648
|
let state = createVideoState();
|
|
607
649
|
let composition = createCompositionSession();
|
|
650
|
+
lifecycle.recoverGeneratedParts = options.invalidPartBehavior === "drop";
|
|
651
|
+
lifecycle.rejectPart = (error) => {
|
|
652
|
+
reportError(error);
|
|
653
|
+
if (options.invalidPartBehavior !== "drop") return false;
|
|
654
|
+
composition = rejectCompositionScene(composition);
|
|
655
|
+
lifecycle.reportWarning?.({ code: "provider_warning", category: "provider", message: "Some generated content was skipped.", recoverable: true });
|
|
656
|
+
return true;
|
|
657
|
+
};
|
|
608
658
|
const compositionOptions = {
|
|
609
659
|
maxDurationSec: input.maxDurationSec ?? 30,
|
|
610
660
|
closerReserveSec,
|
|
@@ -686,13 +736,8 @@ function createVideo(rawInput, options) {
|
|
|
686
736
|
}
|
|
687
737
|
reportError(error);
|
|
688
738
|
if ((options.invalidPartBehavior ?? "fail") === "fail") throw error;
|
|
689
|
-
yield emit(events.create("response.
|
|
690
|
-
|
|
691
|
-
code: "invalid_generated_part",
|
|
692
|
-
message: "Generated content was skipped",
|
|
693
|
-
recoverable: true
|
|
694
|
-
},
|
|
695
|
-
terminal: false
|
|
739
|
+
yield emit(events.create("response.warning", {
|
|
740
|
+
warning: { code: "provider_warning", category: "provider", message: "Some generated content was skipped.", recoverable: true }
|
|
696
741
|
}));
|
|
697
742
|
}
|
|
698
743
|
}
|
|
@@ -749,7 +794,7 @@ function createVideo(rawInput, options) {
|
|
|
749
794
|
}
|
|
750
795
|
try {
|
|
751
796
|
if (state.config) {
|
|
752
|
-
const recovery = recoverComposition(composition, state.config, compositionOptions);
|
|
797
|
+
const recovery = options.invalidPartBehavior === "drop" && !composition.planCompleted ? advanceComposition(composition, { type: "plan.complete", finishReason: "other" }, state.config, compositionOptions) : recoverComposition(composition, state.config, compositionOptions);
|
|
753
798
|
for (const action of recovery.actions) yield emitCompositionAction(action);
|
|
754
799
|
composition = recovery.session;
|
|
755
800
|
}
|
|
@@ -763,6 +808,33 @@ function createVideo(rawInput, options) {
|
|
|
763
808
|
}
|
|
764
809
|
const error = cause instanceof Error ? cause : new Error(String(cause));
|
|
765
810
|
reportError(error);
|
|
811
|
+
if (options.invalidPartBehavior === "drop" && state.config?.scenes.length && (composition.acceptedSceneCount > 0 || !requiresGeneratedScene)) {
|
|
812
|
+
const snapshot = parseVideo(state.config);
|
|
813
|
+
yield emit(events.create("response.warning", {
|
|
814
|
+
warning: { code: "plan_incomplete", category: "provider", message: "The response is ready; some content could not be completed.", recoverable: true }
|
|
815
|
+
}));
|
|
816
|
+
const completeEvent = emit(events.create("response.complete", {
|
|
817
|
+
finishReason: "other",
|
|
818
|
+
snapshot,
|
|
819
|
+
checksum: checksumVideo(snapshot)
|
|
820
|
+
}));
|
|
821
|
+
finish(state);
|
|
822
|
+
invokeIsolated(options.onComplete, {
|
|
823
|
+
finishReason: "other",
|
|
824
|
+
...provider.usage ? { usage: provider.usage } : {},
|
|
825
|
+
...provider.providerMetadata !== void 0 ? { providerMetadata: provider.providerMetadata } : {},
|
|
826
|
+
...provider.requestedModelId ? { requestedModelId: provider.requestedModelId } : {},
|
|
827
|
+
...provider.resolvedModelId ? { resolvedModelId: provider.resolvedModelId } : {},
|
|
828
|
+
...timeToFirstSceneMs != null ? { timeToFirstSceneMs } : {},
|
|
829
|
+
totalDurationMs: Math.max(0, monotonicNow() - startedAt),
|
|
830
|
+
acceptedSceneCount: composition.acceptedSceneCount,
|
|
831
|
+
rejectedSceneCount: composition.rejectedSceneCount,
|
|
832
|
+
videoDurationSec: snapshot.scenes.at(-1)?.timing.endTime ?? 0,
|
|
833
|
+
warnings: state.warnings.map(cloneWarning)
|
|
834
|
+
});
|
|
835
|
+
yield completeEvent;
|
|
836
|
+
return;
|
|
837
|
+
}
|
|
766
838
|
const emptyResult = error.message === "The planner completed without adding a scene";
|
|
767
839
|
const errorEvent = emit(events.create("response.error", {
|
|
768
840
|
error: {
|
|
@@ -805,5 +877,6 @@ function createVideo(rawInput, options) {
|
|
|
805
877
|
}
|
|
806
878
|
|
|
807
879
|
export {
|
|
880
|
+
invokeIsolated,
|
|
808
881
|
createVideo
|
|
809
882
|
};
|