@vanillaskyai/video 0.2.0 → 0.3.1
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 +26 -1
- package/PUBLIC-API.md +5 -1
- package/README.md +10 -6
- package/dist/{chunk-Q2DDQKEG.js → chunk-GR4I3MN2.js} +6 -6
- package/dist/{chunk-TGFO454Q.js → chunk-LHFADNWJ.js} +33 -13
- package/dist/{chunk-K5SRF7D3.js → chunk-WHPZED7X.js} +21 -6
- package/dist/cli.js +2 -2
- package/dist/{compose-video-34GRZIF2.js → compose-video-6UQ33DOO.js} +1 -1
- package/dist/{events-B-28kERX.d.ts → events-tQ0x-VaL.d.ts} +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/{kit-CBedbj1n.d.ts → kit-DlUSg8lA.d.ts} +1 -1
- package/dist/react.d.ts +3 -3
- package/dist/react.js +150 -61
- package/dist/server.d.ts +3 -3
- package/dist/server.js +16 -8
- package/dist/system-prompt-NMYHLAMJ.js +10 -0
- package/dist/templates.d.ts +3 -3
- package/dist/test.d.ts +2 -2
- package/dist/test.js +1 -1
- package/dist/{types-_t4bduwX.d.ts → types-CVMb6QEq.d.ts} +8 -5
- package/docs/concepts.md +5 -1
- package/docs/custom-templates.md +31 -3
- package/docs/customization.md +8 -4
- package/docs/getting-started.md +7 -6
- package/docs/input-and-first-scene.md +31 -11
- package/docs/integrate-nextjs.md +6 -3
- package/docs/media-and-audio.md +5 -1
- package/docs/prompt-and-input.md +23 -7
- package/docs/provider-integration.md +38 -2
- package/examples/nextjs-quickstart/README.md +3 -1
- package/examples/nextjs-quickstart/package.json +3 -2
- package/examples/nextjs-quickstart/src/app/api/video/route.ts +1 -1
- package/package.json +1 -1
- package/dist/system-prompt-GYTYGWGO.js +0 -8
package/dist/react.js
CHANGED
|
@@ -152,6 +152,51 @@ function savedVideoState(video) {
|
|
|
152
152
|
config: video
|
|
153
153
|
};
|
|
154
154
|
}
|
|
155
|
+
function PlayerIcon({ name, size = 22 }) {
|
|
156
|
+
const shared = {
|
|
157
|
+
width: size,
|
|
158
|
+
height: size,
|
|
159
|
+
viewBox: "0 0 24 24",
|
|
160
|
+
fill: "none",
|
|
161
|
+
stroke: "currentColor",
|
|
162
|
+
strokeWidth: 2,
|
|
163
|
+
strokeLinecap: "round",
|
|
164
|
+
strokeLinejoin: "round",
|
|
165
|
+
"aria-hidden": true,
|
|
166
|
+
focusable: false
|
|
167
|
+
};
|
|
168
|
+
if (name === "play") {
|
|
169
|
+
return /* @__PURE__ */ jsx("svg", { ...shared, children: /* @__PURE__ */ jsx("path", { d: "M8 5v14l11-7z", fill: "currentColor", stroke: "none" }) });
|
|
170
|
+
}
|
|
171
|
+
if (name === "pause") {
|
|
172
|
+
return /* @__PURE__ */ jsxs("svg", { ...shared, children: [
|
|
173
|
+
/* @__PURE__ */ jsx("rect", { x: "7", y: "5", width: "3.5", height: "14", rx: "1", fill: "currentColor", stroke: "none" }),
|
|
174
|
+
/* @__PURE__ */ jsx("rect", { x: "13.5", y: "5", width: "3.5", height: "14", rx: "1", fill: "currentColor", stroke: "none" })
|
|
175
|
+
] });
|
|
176
|
+
}
|
|
177
|
+
if (name === "replay") {
|
|
178
|
+
return /* @__PURE__ */ jsxs("svg", { ...shared, children: [
|
|
179
|
+
/* @__PURE__ */ jsx("path", { d: "M4.5 9A8 8 0 1 1 5 16" }),
|
|
180
|
+
/* @__PURE__ */ jsx("path", { d: "M4.5 4.5V9H9" })
|
|
181
|
+
] });
|
|
182
|
+
}
|
|
183
|
+
if (name === "volume-off") {
|
|
184
|
+
return /* @__PURE__ */ jsxs("svg", { ...shared, children: [
|
|
185
|
+
/* @__PURE__ */ jsx("path", { d: "M11 5 6.5 9H3v6h3.5L11 19z" }),
|
|
186
|
+
/* @__PURE__ */ jsx("path", { d: "m16 9 5 5M21 9l-5 5" })
|
|
187
|
+
] });
|
|
188
|
+
}
|
|
189
|
+
if (name === "volume") {
|
|
190
|
+
return /* @__PURE__ */ jsxs("svg", { ...shared, children: [
|
|
191
|
+
/* @__PURE__ */ jsx("path", { d: "M11 5 6.5 9H3v6h3.5L11 19z" }),
|
|
192
|
+
/* @__PURE__ */ jsx("path", { d: "M15 9.5a4 4 0 0 1 0 5M17.8 7a7.5 7.5 0 0 1 0 10" })
|
|
193
|
+
] });
|
|
194
|
+
}
|
|
195
|
+
if (name === "exit-fullscreen") {
|
|
196
|
+
return /* @__PURE__ */ jsx("svg", { ...shared, children: /* @__PURE__ */ jsx("path", { d: "M9 4v5H4M15 4v5h5M9 20v-5H4M15 20v-5h5" }) });
|
|
197
|
+
}
|
|
198
|
+
return /* @__PURE__ */ jsx("svg", { ...shared, children: /* @__PURE__ */ jsx("path", { d: "M8 3H3v5M16 3h5v5M8 21H3v-5M16 21h5v-5" }) });
|
|
199
|
+
}
|
|
155
200
|
function VideoPlayerRuntime({
|
|
156
201
|
kit,
|
|
157
202
|
stream,
|
|
@@ -469,33 +514,38 @@ function VideoPlayerRuntime({
|
|
|
469
514
|
if (document.fullscreenElement === container) await document.exitFullscreen?.();
|
|
470
515
|
else await container.requestFullscreen?.();
|
|
471
516
|
};
|
|
517
|
+
const controlSize = Math.max(40, Math.min(52, Math.round(displayWidth * 0.15)));
|
|
518
|
+
const controlInset = Math.max(10, Math.min(20, Math.round(displayWidth * 0.056)));
|
|
472
519
|
const controlButtonStyle = {
|
|
473
520
|
display: "inline-grid",
|
|
474
521
|
placeItems: "center",
|
|
475
522
|
flex: "0 0 auto",
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
523
|
+
width: controlSize,
|
|
524
|
+
height: controlSize,
|
|
525
|
+
minWidth: controlSize,
|
|
526
|
+
minHeight: controlSize,
|
|
527
|
+
padding: 0,
|
|
528
|
+
border: "1px solid rgba(255, 255, 255, 0.08)",
|
|
529
|
+
borderRadius: 999,
|
|
530
|
+
backgroundColor: "rgba(255, 255, 255, 0.16)",
|
|
482
531
|
color: "#ffffff",
|
|
483
|
-
|
|
532
|
+
boxShadow: "0 5px 18px rgba(0, 0, 0, 0.16)",
|
|
533
|
+
backdropFilter: "blur(12px)",
|
|
484
534
|
cursor: "pointer"
|
|
485
535
|
};
|
|
486
536
|
const startButtonStyle = {
|
|
487
537
|
display: "inline-flex",
|
|
488
538
|
alignItems: "center",
|
|
489
539
|
gap: 10,
|
|
490
|
-
minHeight:
|
|
491
|
-
padding: "
|
|
492
|
-
border: "1px solid rgba(
|
|
540
|
+
minHeight: 60,
|
|
541
|
+
padding: "14px 22px",
|
|
542
|
+
border: "1px solid rgba(9, 7, 18, 0.08)",
|
|
493
543
|
borderRadius: 999,
|
|
494
|
-
backgroundColor: "
|
|
495
|
-
color: "#
|
|
496
|
-
boxShadow: "0 8px 28px rgba(0, 0, 0, 0.
|
|
497
|
-
|
|
498
|
-
|
|
544
|
+
backgroundColor: "#ffffff",
|
|
545
|
+
color: "#090712",
|
|
546
|
+
boxShadow: "0 8px 28px rgba(0, 0, 0, 0.24)",
|
|
547
|
+
font: "700 16px/1 system-ui, sans-serif",
|
|
548
|
+
whiteSpace: "nowrap",
|
|
499
549
|
cursor: "pointer"
|
|
500
550
|
};
|
|
501
551
|
const startButtonPositionStyle = {
|
|
@@ -579,7 +629,7 @@ function VideoPlayerRuntime({
|
|
|
579
629
|
onClick: armPlayback,
|
|
580
630
|
style: { ...startButtonStyle, ...startButtonPositionStyle },
|
|
581
631
|
children: [
|
|
582
|
-
/* @__PURE__ */ jsx(
|
|
632
|
+
/* @__PURE__ */ jsx(PlayerIcon, { name: "play", size: 20 }),
|
|
583
633
|
config?.audio && !isMuted ? "Play with sound" : "Play video"
|
|
584
634
|
]
|
|
585
635
|
}
|
|
@@ -615,7 +665,7 @@ function VideoPlayerRuntime({
|
|
|
615
665
|
...startButtonPositionStyle
|
|
616
666
|
},
|
|
617
667
|
children: [
|
|
618
|
-
/* @__PURE__ */ jsx(
|
|
668
|
+
/* @__PURE__ */ jsx(PlayerIcon, { name: "play", size: 20 }),
|
|
619
669
|
config?.audio && !isMuted ? "Play with sound" : "Play video"
|
|
620
670
|
]
|
|
621
671
|
}
|
|
@@ -631,60 +681,99 @@ function VideoPlayerRuntime({
|
|
|
631
681
|
loop: state.status === "streaming" || introPlaying
|
|
632
682
|
}
|
|
633
683
|
) : null,
|
|
634
|
-
|
|
684
|
+
ended ? /* @__PURE__ */ jsx(
|
|
685
|
+
"div",
|
|
686
|
+
{
|
|
687
|
+
"data-testid": "video-ended-scrim",
|
|
688
|
+
"aria-hidden": "true",
|
|
689
|
+
style: {
|
|
690
|
+
position: "absolute",
|
|
691
|
+
inset: 0,
|
|
692
|
+
zIndex: 1,
|
|
693
|
+
pointerEvents: "none",
|
|
694
|
+
background: "rgba(4, 3, 18, 0.52)",
|
|
695
|
+
backdropFilter: "blur(4px)"
|
|
696
|
+
}
|
|
697
|
+
}
|
|
698
|
+
) : null,
|
|
699
|
+
ended ? /* @__PURE__ */ jsxs(
|
|
700
|
+
"button",
|
|
701
|
+
{
|
|
702
|
+
type: "button",
|
|
703
|
+
"data-testid": "video-replay-button",
|
|
704
|
+
"aria-label": "Replay video response",
|
|
705
|
+
onClick: togglePlayback,
|
|
706
|
+
style: {
|
|
707
|
+
...startButtonStyle,
|
|
708
|
+
...startButtonPositionStyle,
|
|
709
|
+
zIndex: 3
|
|
710
|
+
},
|
|
711
|
+
children: [
|
|
712
|
+
/* @__PURE__ */ jsx(PlayerIcon, { name: "replay", size: 21 }),
|
|
713
|
+
"Replay"
|
|
714
|
+
]
|
|
715
|
+
}
|
|
716
|
+
) : null,
|
|
717
|
+
!generationCoverVisible && !showStartPoster && config?.scenes.length ? /* @__PURE__ */ jsxs(
|
|
635
718
|
"div",
|
|
636
719
|
{
|
|
637
720
|
"data-testid": "video-controls",
|
|
721
|
+
"data-layout": "split",
|
|
638
722
|
style: {
|
|
639
723
|
position: "absolute",
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
display: "flex",
|
|
644
|
-
alignItems: "center",
|
|
645
|
-
gap: 6,
|
|
646
|
-
minHeight: 48,
|
|
647
|
-
padding: "6px 8px",
|
|
648
|
-
border: "1px solid rgba(255, 255, 255, 0.2)",
|
|
649
|
-
borderRadius: 12,
|
|
650
|
-
backgroundColor: "rgba(9, 7, 18, 0.82)",
|
|
651
|
-
backdropFilter: "blur(12px)",
|
|
652
|
-
color: "#ffffff",
|
|
653
|
-
boxShadow: "0 4px 20px rgba(0, 0, 0, 0.35)"
|
|
724
|
+
inset: 0,
|
|
725
|
+
zIndex: 4,
|
|
726
|
+
pointerEvents: "none"
|
|
654
727
|
},
|
|
655
728
|
children: [
|
|
656
|
-
|
|
657
|
-
"
|
|
658
|
-
{
|
|
659
|
-
type: "button",
|
|
660
|
-
"aria-label": ended ? "Replay video response" : isPlaying ? "Pause video response" : "Play video response",
|
|
661
|
-
onClick: togglePlayback,
|
|
662
|
-
style: controlButtonStyle,
|
|
663
|
-
children: ended ? "\u21BB Replay" : isPlaying ? "\u2161" : "\u25B6"
|
|
664
|
-
}
|
|
665
|
-
) : null,
|
|
666
|
-
config?.audio ? /* @__PURE__ */ jsx(
|
|
667
|
-
"button",
|
|
729
|
+
/* @__PURE__ */ jsx(
|
|
730
|
+
"div",
|
|
668
731
|
{
|
|
669
|
-
|
|
670
|
-
"
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
732
|
+
"data-testid": "video-primary-controls",
|
|
733
|
+
style: { position: "absolute", left: controlInset, bottom: controlInset, display: "flex", pointerEvents: "auto" },
|
|
734
|
+
children: /* @__PURE__ */ jsx(
|
|
735
|
+
"button",
|
|
736
|
+
{
|
|
737
|
+
type: "button",
|
|
738
|
+
"aria-label": ended ? "Play video response from beginning" : isPlaying ? "Pause video response" : "Play video response",
|
|
739
|
+
onClick: togglePlayback,
|
|
740
|
+
style: controlButtonStyle,
|
|
741
|
+
children: /* @__PURE__ */ jsx(PlayerIcon, { name: isPlaying ? "pause" : "play" })
|
|
742
|
+
}
|
|
743
|
+
)
|
|
678
744
|
}
|
|
679
|
-
)
|
|
680
|
-
/* @__PURE__ */
|
|
681
|
-
"
|
|
745
|
+
),
|
|
746
|
+
/* @__PURE__ */ jsxs(
|
|
747
|
+
"div",
|
|
682
748
|
{
|
|
683
|
-
|
|
684
|
-
"
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
749
|
+
"data-testid": "video-secondary-controls",
|
|
750
|
+
style: { position: "absolute", right: controlInset, bottom: controlInset, display: "flex", gap: 10, pointerEvents: "auto" },
|
|
751
|
+
children: [
|
|
752
|
+
config?.audio ? /* @__PURE__ */ jsx(
|
|
753
|
+
"button",
|
|
754
|
+
{
|
|
755
|
+
type: "button",
|
|
756
|
+
"aria-label": isMuted ? "Unmute video response" : "Mute video response",
|
|
757
|
+
"aria-pressed": !isMuted,
|
|
758
|
+
onClick: () => setIsMuted((muted) => {
|
|
759
|
+
if (muted) setAudioUnlocked(true);
|
|
760
|
+
return !muted;
|
|
761
|
+
}),
|
|
762
|
+
style: controlButtonStyle,
|
|
763
|
+
children: /* @__PURE__ */ jsx(PlayerIcon, { name: isMuted ? "volume-off" : "volume" })
|
|
764
|
+
}
|
|
765
|
+
) : null,
|
|
766
|
+
/* @__PURE__ */ jsx(
|
|
767
|
+
"button",
|
|
768
|
+
{
|
|
769
|
+
type: "button",
|
|
770
|
+
"aria-label": isFullscreen ? "Exit fullscreen" : "Enter fullscreen",
|
|
771
|
+
onClick: () => void toggleFullscreen(),
|
|
772
|
+
style: controlButtonStyle,
|
|
773
|
+
children: /* @__PURE__ */ jsx(PlayerIcon, { name: isFullscreen ? "exit-fullscreen" : "enter-fullscreen" })
|
|
774
|
+
}
|
|
775
|
+
)
|
|
776
|
+
]
|
|
688
777
|
}
|
|
689
778
|
)
|
|
690
779
|
]
|
package/dist/server.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { b as VideoFinishReason, a as VideoWarning, V as VideoEvent } from './events-
|
|
3
|
-
export { c as VideoWarningCategory } from './events-
|
|
1
|
+
import { m as VideoGenerationContext, n as VideoPlanner, o as VideoRequest, k as VideoCapabilities, d as VideoInput, a as VideoAudio, p as VideoSceneValidator, q as VideoTemplatePacing, r as VideoSnapshotRetention, s as VideoResumeCursor, t as VideoSceneValidationContext, g as VideoScene } from './types-CVMb6QEq.js';
|
|
2
|
+
import { b as VideoFinishReason, a as VideoWarning, V as VideoEvent } from './events-tQ0x-VaL.js';
|
|
3
|
+
export { c as VideoWarningCategory } from './events-tQ0x-VaL.js';
|
|
4
4
|
import { S as SceneTemplateMetadata } from './catalog-types-BIhSpOWK.js';
|
|
5
5
|
|
|
6
6
|
interface VideoProviderUsage {
|
package/dist/server.js
CHANGED
|
@@ -3,7 +3,7 @@ import {
|
|
|
3
3
|
} from "./chunk-BKF3A357.js";
|
|
4
4
|
import {
|
|
5
5
|
createVideo
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-LHFADNWJ.js";
|
|
7
7
|
import "./chunk-E7CL7UPB.js";
|
|
8
8
|
import {
|
|
9
9
|
BUILTIN_SERVER_TEMPLATE_KIT,
|
|
@@ -31,10 +31,8 @@ import {
|
|
|
31
31
|
import {
|
|
32
32
|
createTemplateSystemPrompt,
|
|
33
33
|
getStandardMediaResolverContract
|
|
34
|
-
} from "./chunk-
|
|
35
|
-
import
|
|
36
|
-
DEFAULT_VIDEO_SYSTEM_PROMPT
|
|
37
|
-
} from "./chunk-K5SRF7D3.js";
|
|
34
|
+
} from "./chunk-GR4I3MN2.js";
|
|
35
|
+
import "./chunk-WHPZED7X.js";
|
|
38
36
|
import "./chunk-3O7OMMMF.js";
|
|
39
37
|
import "./chunk-352ZZCET.js";
|
|
40
38
|
import "./chunk-XAMUOSX7.js";
|
|
@@ -131,6 +129,7 @@ function parseVideoRequest(value) {
|
|
|
131
129
|
const input = record(request.input, "request.input");
|
|
132
130
|
allowedKeys(input, [
|
|
133
131
|
"input",
|
|
132
|
+
"knowledgeMode",
|
|
134
133
|
"instructions",
|
|
135
134
|
"maxDurationSec",
|
|
136
135
|
"orientation",
|
|
@@ -142,6 +141,14 @@ function parseVideoRequest(value) {
|
|
|
142
141
|
"audio"
|
|
143
142
|
], "request.input");
|
|
144
143
|
string(input.input, "request.input.input");
|
|
144
|
+
if (input.knowledgeMode != null) {
|
|
145
|
+
enumValue(
|
|
146
|
+
input.knowledgeMode,
|
|
147
|
+
["input-only", "general"],
|
|
148
|
+
"input-only or general",
|
|
149
|
+
"request.input.knowledgeMode"
|
|
150
|
+
);
|
|
151
|
+
}
|
|
145
152
|
if (input.instructions != null) string(input.instructions, "request.input.instructions");
|
|
146
153
|
if (input.maxDurationSec != null) {
|
|
147
154
|
numberBetween(input.maxDurationSec, 5, 120, "request.input.maxDurationSec");
|
|
@@ -154,7 +161,7 @@ function parseVideoRequest(value) {
|
|
|
154
161
|
"request.input.orientation"
|
|
155
162
|
);
|
|
156
163
|
}
|
|
157
|
-
if (input.opening != null) string(input.opening, "request.input.opening");
|
|
164
|
+
if (input.opening != null && input.opening !== false) string(input.opening, "request.input.opening");
|
|
158
165
|
if (input.audio != null && input.audio !== false) {
|
|
159
166
|
const soundtrack = record(input.audio, "request.input.audio");
|
|
160
167
|
allowedKeys(soundtrack, ["src"], "request.input.audio");
|
|
@@ -402,7 +409,7 @@ function createVideoStreamHandler(options) {
|
|
|
402
409
|
onError: options.onError,
|
|
403
410
|
onWarning: options.onWarning,
|
|
404
411
|
onComplete: options.onComplete,
|
|
405
|
-
systemPrompt
|
|
412
|
+
systemPrompt,
|
|
406
413
|
selectAudio: options.selectAudio,
|
|
407
414
|
snapshotRetention: options.snapshotRetention,
|
|
408
415
|
signal: request.signal,
|
|
@@ -651,12 +658,13 @@ function createVideoHandler(options) {
|
|
|
651
658
|
approveUrl,
|
|
652
659
|
isOpeningReady: (input) => openingReadyInputs.has(input)
|
|
653
660
|
}),
|
|
654
|
-
systemPrompt: ({ capabilities: capabilities2 }) => {
|
|
661
|
+
systemPrompt: ({ request, capabilities: capabilities2 }) => {
|
|
655
662
|
const selectedIds = capabilities2?.templates == null ? void 0 : new Set(capabilities2.templates);
|
|
656
663
|
const selectedTemplates = selectedIds == null ? templates.listTemplateMetadata() : templates.listTemplateMetadata().filter(({ id }) => selectedIds.has(id));
|
|
657
664
|
return createTemplateSystemPrompt({
|
|
658
665
|
kit: { listTemplateMetadata: () => selectedTemplates },
|
|
659
666
|
basePrompt,
|
|
667
|
+
knowledgeMode: request.input.knowledgeMode,
|
|
660
668
|
mediaResolverAvailable: resolveMedia != null
|
|
661
669
|
});
|
|
662
670
|
},
|
package/dist/templates.d.ts
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import { I as InferTemplateJsonSchema, S as SceneTemplateProps, a as SceneTemplate } from './kit-
|
|
2
|
-
export { T as TemplateRegistry, c as createTemplateRegistry } from './kit-
|
|
1
|
+
import { I as InferTemplateJsonSchema, S as SceneTemplateProps, a as SceneTemplate } from './kit-DlUSg8lA.js';
|
|
2
|
+
export { T as TemplateRegistry, c as createTemplateRegistry } from './kit-DlUSg8lA.js';
|
|
3
3
|
import { ComponentType } from 'react';
|
|
4
4
|
import { d as TemplateJsonSchema, S as SceneTemplateMetadata } from './catalog-types-BIhSpOWK.js';
|
|
5
5
|
export { T as TemplateFamily, c as TemplateTimingMetadata, e as TemplateTransitionTiming } from './catalog-types-BIhSpOWK.js';
|
|
6
|
-
import './types-
|
|
6
|
+
import './types-CVMb6QEq.js';
|
|
7
7
|
|
|
8
8
|
interface TemplateExample<Variables extends Record<string, unknown> = Record<string, unknown>> {
|
|
9
9
|
name: string;
|
package/dist/test.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { a as VideoWarning, b as VideoFinishReason } from './events-
|
|
1
|
+
import { g as VideoScene, f as VideoOrientation, h as VideoStyle, V as Video, a as VideoAudio, d as VideoInput } from './types-CVMb6QEq.js';
|
|
2
|
+
import { a as VideoWarning, b as VideoFinishReason } from './events-tQ0x-VaL.js';
|
|
3
3
|
|
|
4
4
|
type MockVideoStreamPart = {
|
|
5
5
|
type: "scene.add";
|
package/dist/test.js
CHANGED
|
@@ -233,7 +233,7 @@ async function* simulateVideoStream(parts, options = {}) {
|
|
|
233
233
|
if (timeoutMs != null && (!Number.isFinite(timeoutMs) || timeoutMs < 0)) {
|
|
234
234
|
throw new Error("Simulation timeoutMs must be a non-negative finite number");
|
|
235
235
|
}
|
|
236
|
-
const { createVideo } = await import("./compose-video-
|
|
236
|
+
const { createVideo } = await import("./compose-video-6UQ33DOO.js");
|
|
237
237
|
const { createTextDeltaVideoPlanner } = await import("./text-stream-UPGUD2TD.js");
|
|
238
238
|
const { BUILTIN_SERVER_TEMPLATE_KIT } = await import("./builtin-server-BRTZN4Q7.js");
|
|
239
239
|
const { createTemplateSceneValidator } = await import("./validate-T7GBU2YF.js");
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
declare const VIDEO_PROTOCOL_VERSION: "0.4";
|
|
2
2
|
declare const VIDEO_SCHEMA_VERSION: "0.1";
|
|
3
3
|
type VideoOrientation = "portrait" | "landscape";
|
|
4
|
+
type VideoKnowledgeMode = "input-only" | "general";
|
|
4
5
|
interface VideoBrandColors {
|
|
5
6
|
primary: string;
|
|
6
7
|
secondary: string;
|
|
@@ -114,16 +115,18 @@ interface VideoSuppliedMedia {
|
|
|
114
115
|
role?: "product" | "proof" | "background" | "logo";
|
|
115
116
|
}
|
|
116
117
|
interface VideoInput {
|
|
117
|
-
/** Raw
|
|
118
|
+
/** Raw source, request, or question for the video. */
|
|
118
119
|
input: string;
|
|
119
|
-
/**
|
|
120
|
+
/** Keep claims source-grounded by default, or allow stable model knowledge. */
|
|
121
|
+
knowledgeMode?: VideoKnowledgeMode;
|
|
122
|
+
/** Optional creative direction. It never changes the knowledge mode. */
|
|
120
123
|
instructions?: string;
|
|
121
124
|
maxDurationSec?: number;
|
|
122
125
|
orientation?: VideoOrientation;
|
|
123
126
|
/** Optional global visual direction. Omit for VanillaSky defaults. */
|
|
124
127
|
style?: VideoStyleOptions;
|
|
125
|
-
/**
|
|
126
|
-
opening?: string;
|
|
128
|
+
/** Custom opening copy, or false to let the host show loading UI instead. Omit for the deterministic fallback. */
|
|
129
|
+
opening?: string | false;
|
|
127
130
|
brand?: VideoBrandInput;
|
|
128
131
|
/** Viewer or account context that may appear verbatim. It is data, never instructions. */
|
|
129
132
|
personalization?: Record<string, unknown>;
|
|
@@ -206,4 +209,4 @@ interface VideoSnapshotRetention {
|
|
|
206
209
|
suppliedMediaUrls?: boolean;
|
|
207
210
|
}
|
|
208
211
|
|
|
209
|
-
export { type Video as V, type VideoAudio as a, type VideoBackground as b, type VideoBrand as c, type VideoInput as d, type
|
|
212
|
+
export { type Video as V, type VideoAudio as a, type VideoBackground as b, type VideoBrand as c, type VideoInput as d, type VideoKnowledgeMode as e, type VideoOrientation as f, type VideoScene as g, type VideoStyle as h, type VideoStyleOptions as i, type VideoSuppliedMedia as j, type VideoCapabilities as k, VIDEO_PROTOCOL_VERSION as l, type VideoGenerationContext as m, type VideoPlanner as n, type VideoRequest as o, type VideoSceneValidator as p, type VideoTemplatePacing as q, type VideoSnapshotRetention as r, type VideoResumeCursor as s, type VideoSceneValidationContext as t };
|
package/docs/concepts.md
CHANGED
|
@@ -21,8 +21,12 @@ or export pipeline when an encoded file is required.
|
|
|
21
21
|
curated set of articles, metrics, events, notes, or an AI answer. It may be
|
|
22
22
|
short, but the planner is designed to distill larger sources into a concise
|
|
23
23
|
video rather than represent every fact;
|
|
24
|
+
- `knowledgeMode`: `input-only` by default, or `general` when stable model
|
|
25
|
+
knowledge may supplement the request;
|
|
24
26
|
- `instructions`: optional creative direction that cannot override facts;
|
|
25
|
-
- `opening`: optional copy for
|
|
27
|
+
- `opening`: optional custom copy for the deterministic opening; omission uses
|
|
28
|
+
`Creating your video...`, while `false` lets the host render loading UI
|
|
29
|
+
without adding an opening scene to the video;
|
|
26
30
|
- `personalization`: application-defined fields such as name, role, account,
|
|
27
31
|
period, goal, or onboarding partner;
|
|
28
32
|
- `brand`: an optional background preset plus name, logo, font, surfaces, and
|
package/docs/custom-templates.md
CHANGED
|
@@ -83,6 +83,8 @@ The file created by `vanillasky create` is a complete working template. Keep
|
|
|
83
83
|
these concerns together:
|
|
84
84
|
|
|
85
85
|
- `useWhen` and `avoidWhen` tell the AI when the visual is appropriate;
|
|
86
|
+
- `jobs` declares the narrative roles the template can fill, and decides
|
|
87
|
+
whether it may close a video;
|
|
86
88
|
- `schema` defines allowed variables, validation, defaults, labels, and
|
|
87
89
|
grounding formats;
|
|
88
90
|
- named `examples` provide complete deterministic preview values;
|
|
@@ -90,6 +92,31 @@ these concerns together:
|
|
|
90
92
|
- raw `progress`, dimensions, and `safeZone` make semantic state deterministic
|
|
91
93
|
and layout safe in portrait and landscape.
|
|
92
94
|
|
|
95
|
+
### Which templates may close a video
|
|
96
|
+
|
|
97
|
+
Every complete plan ends on exactly one closer, and the runtime only accepts a
|
|
98
|
+
closer whose `jobs` include `"ask"` or `"payoff"`. Marking any other template as
|
|
99
|
+
the closer rejects that scene with `Scene template <id> cannot be used as a
|
|
100
|
+
closer`, and the reserved closing time is given to a template that qualifies.
|
|
101
|
+
|
|
102
|
+
Declare `jobs: ["ask"]` on a call to action and `jobs: ["payoff"]` on a
|
|
103
|
+
celebration or resolution beat. A template that only presents evidence keeps
|
|
104
|
+
`jobs: ["proof"]` and is never asked to close.
|
|
105
|
+
|
|
106
|
+
The same rule reads the built-in catalog, so an application that wants to
|
|
107
|
+
constrain how its videos end can compute the eligible IDs instead of guessing:
|
|
108
|
+
|
|
109
|
+
```ts
|
|
110
|
+
import { builtinTemplates } from "@vanillaskyai/video/templates/catalog";
|
|
111
|
+
|
|
112
|
+
const closerTemplateIds = builtinTemplates
|
|
113
|
+
.filter((template) => template.jobs.some((job) => job === "ask" || job === "payoff"))
|
|
114
|
+
.map((template) => template.id);
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
Pacing uses the same signal: the planner reserves time for the closer whenever
|
|
118
|
+
the selected catalog contains a template with one of those two jobs.
|
|
119
|
+
|
|
93
120
|
Customer templates hard-cut by default. Opt into renderer-owned fades only
|
|
94
121
|
for scenes that use the standard media-background variables, and only after
|
|
95
122
|
both timing points are visually tested in portrait and landscape. Shared brand
|
|
@@ -194,7 +221,7 @@ import { templates } from "../vanillasky/server";
|
|
|
194
221
|
export const handleVideo = createVideoHandler({
|
|
195
222
|
templates,
|
|
196
223
|
authorize: (request) => {
|
|
197
|
-
if (process.env.
|
|
224
|
+
if (process.env.VANILLASKY_LOCAL_DEMO !== "1") return false;
|
|
198
225
|
const hostname = new URL(request.url).hostname;
|
|
199
226
|
return hostname === "localhost" || hostname === "127.0.0.1";
|
|
200
227
|
},
|
|
@@ -207,8 +234,9 @@ export const handleVideo = createVideoHandler({
|
|
|
207
234
|
});
|
|
208
235
|
```
|
|
209
236
|
|
|
210
|
-
The local-only authorization above is intentionally narrow.
|
|
211
|
-
|
|
237
|
+
The local-only authorization above is intentionally narrow. Supply its marker
|
|
238
|
+
only from the development command; replace it with your application's session
|
|
239
|
+
check before deployment, as shown in
|
|
212
240
|
[Getting started](getting-started.md).
|
|
213
241
|
|
|
214
242
|
Use the browser registry for generation and playback:
|
package/docs/customization.md
CHANGED
|
@@ -76,15 +76,19 @@ appear in the story.
|
|
|
76
76
|
first validated scene arrives. The cover is player state, not video content: it
|
|
77
77
|
is never written to the event log, replay, or export.
|
|
78
78
|
|
|
79
|
-
|
|
79
|
+
The opening is deterministic and should not wait for an LLM or remote media.
|
|
80
|
+
Omit it to use `Creating your video...`, or supply one concise custom sentence:
|
|
80
81
|
|
|
81
82
|
```ts
|
|
82
|
-
opening: "
|
|
83
|
+
opening: "Your Q2 customer impact recap is ready."
|
|
83
84
|
```
|
|
84
85
|
|
|
86
|
+
Pass `opening: false` to omit the persisted opening and show application-owned
|
|
87
|
+
loading UI until the first generated scene arrives.
|
|
88
|
+
|
|
85
89
|
Use `opening` only for a genuine opening that should remain in the completed
|
|
86
|
-
response. VanillaSky infers the scene ID, `
|
|
87
|
-
and
|
|
90
|
+
response. VanillaSky infers the scene ID, `media` template, gradient variables,
|
|
91
|
+
and three-second timing. Keep generic loading state in the host UI instead.
|
|
88
92
|
|
|
89
93
|
## Aspect ratio and responsive layout
|
|
90
94
|
|
package/docs/getting-started.md
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
Install VanillaSky:
|
|
6
6
|
|
|
7
7
|
```bash
|
|
8
|
-
npm install @vanillaskyai/video@0.
|
|
8
|
+
npm install @vanillaskyai/video@0.3.1 ai @ai-sdk/openai
|
|
9
9
|
```
|
|
10
10
|
|
|
11
11
|
Set your provider key in `.env.local` (never commit it):
|
|
@@ -29,7 +29,7 @@ const model = openai(process.env.OPENAI_MODEL ?? "gpt-4.1");
|
|
|
29
29
|
const handle = createVideoHandler({
|
|
30
30
|
// Local development only. Replace with your session check before deploying.
|
|
31
31
|
authorize: (request) => {
|
|
32
|
-
if (process.env.
|
|
32
|
+
if (process.env.VANILLASKY_LOCAL_DEMO !== "1") return false;
|
|
33
33
|
const hostname = new URL(request.url).hostname;
|
|
34
34
|
return hostname === "localhost" || hostname === "127.0.0.1";
|
|
35
35
|
},
|
|
@@ -45,11 +45,12 @@ export const POST = handle;
|
|
|
45
45
|
export const OPTIONS = handle;
|
|
46
46
|
```
|
|
47
47
|
|
|
48
|
-
The local bypass is intentionally fail-closed:
|
|
49
|
-
|
|
50
|
-
|
|
48
|
+
The local bypass is intentionally fail-closed: the packaged development command
|
|
49
|
+
sets its marker only for `next dev`, and it accepts only localhost. Every
|
|
50
|
+
production request is denied. Replace it with your real session validation
|
|
51
|
+
before deploying. For literal files and commands,
|
|
51
52
|
use the tested
|
|
52
|
-
[`examples/nextjs-quickstart` directory](https://github.com/VanillaSkyAi/video/tree/v0.
|
|
53
|
+
[`examples/nextjs-quickstart` directory](https://github.com/VanillaSkyAi/video/tree/v0.3.1/examples/nextjs-quickstart).
|
|
53
54
|
|
|
54
55
|
`model` can come from any AI SDK provider, registry, gateway, compatible API,
|
|
55
56
|
or custom implementation. The application can choose a cheaper or faster model
|
|
@@ -8,10 +8,11 @@
|
|
|
8
8
|
import type { VideoInput } from "@vanillaskyai/video";
|
|
9
9
|
|
|
10
10
|
const input: VideoInput = {
|
|
11
|
-
input: "
|
|
11
|
+
input: "Maya completed 142 customer conversations in Q2.",
|
|
12
|
+
knowledgeMode: "input-only",
|
|
12
13
|
instructions: "Celebrate the result. Never alter a metric.",
|
|
13
|
-
opening: "
|
|
14
|
-
personalization: { firstName: "
|
|
14
|
+
opening: "Maya, your Q2 customer impact recap is ready.",
|
|
15
|
+
personalization: { firstName: "Maya", period: "Q2", role: "Product leader" },
|
|
15
16
|
brand,
|
|
16
17
|
suppliedMedia,
|
|
17
18
|
audio: { src: "/audio/calm.mp3" },
|
|
@@ -22,18 +23,24 @@ const input: VideoInput = {
|
|
|
22
23
|
|
|
23
24
|
## Raw source
|
|
24
25
|
|
|
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.
|
|
26
32
|
|
|
27
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.
|
|
28
34
|
|
|
29
35
|
## Opening
|
|
30
36
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
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:
|
|
37
44
|
|
|
38
45
|
- be personal or situational enough to feel intentional;
|
|
39
46
|
- require no network media lookup;
|
|
@@ -41,4 +48,17 @@ should:
|
|
|
41
48
|
- be one concise sentence that fits comfortably in both supported orientations;
|
|
42
49
|
- be part of the final story, not a spinner disguised as a scene.
|
|
43
50
|
|
|
44
|
-
|
|
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.
|