@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
|
@@ -4,17 +4,17 @@ import {
|
|
|
4
4
|
import {
|
|
5
5
|
SceneBackground,
|
|
6
6
|
getMediaBackgroundProps
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-MXZSDGZQ.js";
|
|
8
8
|
import {
|
|
9
9
|
renderWithEmoji
|
|
10
10
|
} from "./chunk-L5BTTCZV.js";
|
|
11
|
-
import "./chunk-
|
|
11
|
+
import "./chunk-FRN6WKHA.js";
|
|
12
12
|
import "./chunk-W3VDIBGR.js";
|
|
13
13
|
import {
|
|
14
14
|
interpolate
|
|
15
15
|
} from "./chunk-XTOXFMLM.js";
|
|
16
|
-
import "./chunk-C6WVCZRW.js";
|
|
17
16
|
import "./chunk-224QNWRA.js";
|
|
17
|
+
import "./chunk-5CDAM24P.js";
|
|
18
18
|
import "./chunk-XWWLKRNU.js";
|
|
19
19
|
|
|
20
20
|
// src/visual-system/primitives/typography/BrandMessageBubble.tsx
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import {
|
|
2
2
|
SceneBackground,
|
|
3
3
|
getMediaBackgroundProps
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-MXZSDGZQ.js";
|
|
5
5
|
import {
|
|
6
6
|
TemplateText
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-YJJC4N4D.js";
|
|
8
8
|
import "./chunk-L5BTTCZV.js";
|
|
9
|
-
import "./chunk-
|
|
9
|
+
import "./chunk-FRN6WKHA.js";
|
|
10
10
|
import {
|
|
11
11
|
TOP_TEXT_AREA_RATIO,
|
|
12
12
|
withOpacity
|
|
@@ -16,10 +16,10 @@ import {
|
|
|
16
16
|
interpolate,
|
|
17
17
|
spring
|
|
18
18
|
} from "./chunk-XTOXFMLM.js";
|
|
19
|
-
import "./chunk-C6WVCZRW.js";
|
|
20
19
|
import {
|
|
21
20
|
hasSceneMedia
|
|
22
21
|
} from "./chunk-224QNWRA.js";
|
|
22
|
+
import "./chunk-5CDAM24P.js";
|
|
23
23
|
import {
|
|
24
24
|
resolveTokens
|
|
25
25
|
} from "./chunk-XWWLKRNU.js";
|
|
@@ -8,12 +8,12 @@ import "./chunk-QAAO7OAY.js";
|
|
|
8
8
|
import {
|
|
9
9
|
SceneBackground,
|
|
10
10
|
getMediaBackgroundProps
|
|
11
|
-
} from "./chunk-
|
|
11
|
+
} from "./chunk-MXZSDGZQ.js";
|
|
12
12
|
import {
|
|
13
13
|
TemplateText
|
|
14
|
-
} from "./chunk-
|
|
14
|
+
} from "./chunk-YJJC4N4D.js";
|
|
15
15
|
import "./chunk-L5BTTCZV.js";
|
|
16
|
-
import "./chunk-
|
|
16
|
+
import "./chunk-FRN6WKHA.js";
|
|
17
17
|
import {
|
|
18
18
|
TOP_TEXT_AREA_RATIO,
|
|
19
19
|
withOpacity
|
|
@@ -22,10 +22,10 @@ import {
|
|
|
22
22
|
interpolate,
|
|
23
23
|
spring
|
|
24
24
|
} from "./chunk-XTOXFMLM.js";
|
|
25
|
-
import "./chunk-C6WVCZRW.js";
|
|
26
25
|
import {
|
|
27
26
|
hasSceneMedia
|
|
28
27
|
} from "./chunk-224QNWRA.js";
|
|
28
|
+
import "./chunk-5CDAM24P.js";
|
|
29
29
|
import {
|
|
30
30
|
MEDIA_TEXT_SHADOW,
|
|
31
31
|
TOKEN_DEFAULTS,
|
|
@@ -5,12 +5,12 @@ import {
|
|
|
5
5
|
import {
|
|
6
6
|
SceneBackground,
|
|
7
7
|
getMediaBackgroundProps
|
|
8
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-MXZSDGZQ.js";
|
|
9
9
|
import {
|
|
10
10
|
TemplateText
|
|
11
|
-
} from "./chunk-
|
|
11
|
+
} from "./chunk-YJJC4N4D.js";
|
|
12
12
|
import "./chunk-L5BTTCZV.js";
|
|
13
|
-
import "./chunk-
|
|
13
|
+
import "./chunk-FRN6WKHA.js";
|
|
14
14
|
import {
|
|
15
15
|
TOP_TEXT_AREA_RATIO,
|
|
16
16
|
withOpacity
|
|
@@ -18,10 +18,10 @@ import {
|
|
|
18
18
|
import {
|
|
19
19
|
interpolate
|
|
20
20
|
} from "./chunk-XTOXFMLM.js";
|
|
21
|
-
import "./chunk-C6WVCZRW.js";
|
|
22
21
|
import {
|
|
23
22
|
hasSceneMedia
|
|
24
23
|
} from "./chunk-224QNWRA.js";
|
|
24
|
+
import "./chunk-5CDAM24P.js";
|
|
25
25
|
import {
|
|
26
26
|
TOKEN_DEFAULTS,
|
|
27
27
|
resolveTokens
|
package/dist/check-runtime.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import {
|
|
2
|
+
VIDEO_PROTOCOL_VERSION,
|
|
2
3
|
VIDEO_SCHEMA_VERSION
|
|
3
4
|
} from "./chunk-AOWSXU2K.js";
|
|
4
5
|
import {
|
|
@@ -273,7 +274,8 @@ function validateScene(value, path) {
|
|
|
273
274
|
"variables",
|
|
274
275
|
"textArchetype",
|
|
275
276
|
"backgroundEffect",
|
|
276
|
-
"timing"
|
|
277
|
+
"timing",
|
|
278
|
+
"narration"
|
|
277
279
|
], path);
|
|
278
280
|
nonEmptyString(requiredField(result, "id", `${path}.id`), `${path}.id`, 128);
|
|
279
281
|
nonEmptyString(requiredField(result, "templateId", `${path}.templateId`), `${path}.templateId`, 128);
|
|
@@ -285,6 +287,8 @@ function validateScene(value, path) {
|
|
|
285
287
|
const field = ownField(result, key, `${path}.${key}`);
|
|
286
288
|
if (field.present) nonEmptyString(field.value, `${path}.${key}`, 128);
|
|
287
289
|
}
|
|
290
|
+
const narration = ownField(result, "narration", `${path}.narration`);
|
|
291
|
+
if (narration.present) nonEmptyString(narration.value, `${path}.narration`, 4e3);
|
|
288
292
|
return result;
|
|
289
293
|
}
|
|
290
294
|
function validateStyle(value, path) {
|
|
@@ -296,7 +300,8 @@ function validateStyle(value, path) {
|
|
|
296
300
|
"defaultTextArchetype",
|
|
297
301
|
"defaultTransition",
|
|
298
302
|
"density",
|
|
299
|
-
"motion"
|
|
303
|
+
"motion",
|
|
304
|
+
"generatedLook"
|
|
300
305
|
], path);
|
|
301
306
|
const brandValue = requiredField(result, "brand", `${path}.brand`);
|
|
302
307
|
const brand = record(brandValue, `${path}.brand`);
|
|
@@ -305,6 +310,8 @@ function validateStyle(value, path) {
|
|
|
305
310
|
if (field.present) nonEmptyString(field.value, `${path}.brand.${key}`);
|
|
306
311
|
}
|
|
307
312
|
validateVideoBrand(brandValue, `${path}.brand`);
|
|
313
|
+
const look = ownField(result, "generatedLook", `${path}.generatedLook`);
|
|
314
|
+
if (look.present) nonEmptyString(look.value, `${path}.generatedLook`, 1e3);
|
|
308
315
|
for (const key of [
|
|
309
316
|
"preset",
|
|
310
317
|
"defaultBackgroundEffect",
|
|
@@ -494,11 +501,52 @@ function parseVideo(value) {
|
|
|
494
501
|
}
|
|
495
502
|
}
|
|
496
503
|
|
|
504
|
+
// src/protocol/stable-json.ts
|
|
505
|
+
function stableJson(value) {
|
|
506
|
+
if (value == null || typeof value !== "object") return JSON.stringify(value);
|
|
507
|
+
if (Array.isArray(value)) return `[${value.map(stableJson).join(",")}]`;
|
|
508
|
+
const entries = Object.entries(value).filter(([, child]) => child !== void 0).sort(([left], [right]) => left < right ? -1 : left > right ? 1 : 0);
|
|
509
|
+
return `{${entries.map(([key, child]) => `${JSON.stringify(key)}:${stableJson(child)}`).join(",")}}`;
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
// src/protocol/checksum.ts
|
|
513
|
+
function checksumVideo(config) {
|
|
514
|
+
const input = stableJson(config);
|
|
515
|
+
let hash = 2166136261;
|
|
516
|
+
for (let index = 0; index < input.length; index += 1) {
|
|
517
|
+
hash ^= input.charCodeAt(index);
|
|
518
|
+
hash = Math.imul(hash, 16777619) >>> 0;
|
|
519
|
+
}
|
|
520
|
+
return `fnv1a32:${hash.toString(16).padStart(8, "0")}`;
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
// src/protocol/events.ts
|
|
524
|
+
function createVideoEventFactory(options) {
|
|
525
|
+
let sequence = options.initialSequence ?? 0;
|
|
526
|
+
return {
|
|
527
|
+
create(type, data) {
|
|
528
|
+
const currentSequence = sequence;
|
|
529
|
+
sequence += 1;
|
|
530
|
+
return {
|
|
531
|
+
protocolVersion: VIDEO_PROTOCOL_VERSION,
|
|
532
|
+
runId: options.runId,
|
|
533
|
+
sequence: currentSequence,
|
|
534
|
+
eventId: `${options.runId}:${currentSequence}`,
|
|
535
|
+
type,
|
|
536
|
+
data
|
|
537
|
+
};
|
|
538
|
+
}
|
|
539
|
+
};
|
|
540
|
+
}
|
|
541
|
+
|
|
497
542
|
export {
|
|
498
543
|
MAX_RETAINED_SOURCE_LENGTH,
|
|
499
544
|
MAX_RETAINED_INSTRUCTIONS_LENGTH,
|
|
500
545
|
MAX_RETAINED_MEDIA_URLS,
|
|
501
546
|
MAX_RETAINED_MEDIA_URL_LENGTH,
|
|
502
547
|
VideoValidationError,
|
|
503
|
-
parseVideo
|
|
548
|
+
parseVideo,
|
|
549
|
+
stableJson,
|
|
550
|
+
checksumVideo,
|
|
551
|
+
createVideoEventFactory
|
|
504
552
|
};
|
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
import {
|
|
2
2
|
parseVideoEvent
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-MMUXVA47.js";
|
|
4
|
+
import {
|
|
5
|
+
MAX_RETAINED_MEDIA_URL_LENGTH
|
|
6
|
+
} from "./chunk-2PYO6VAC.js";
|
|
4
7
|
import {
|
|
5
8
|
VIDEO_PROTOCOL_VERSION
|
|
6
9
|
} from "./chunk-AOWSXU2K.js";
|
|
@@ -92,10 +95,38 @@ function videoSseHeaders(headers) {
|
|
|
92
95
|
return result;
|
|
93
96
|
}
|
|
94
97
|
|
|
98
|
+
// src/video-chat/media.ts
|
|
99
|
+
function sanitizeVideoChatMedia(value) {
|
|
100
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return null;
|
|
101
|
+
const media = value;
|
|
102
|
+
if (media.type !== "image" && media.type !== "video") return null;
|
|
103
|
+
if (typeof media.url !== "string") return null;
|
|
104
|
+
const url = media.url.trim();
|
|
105
|
+
if (!safeMediaUrl(url)) return null;
|
|
106
|
+
if (media.posterUrl != null && typeof media.posterUrl !== "string") return null;
|
|
107
|
+
const posterUrl = typeof media.posterUrl === "string" ? media.posterUrl.trim() : "";
|
|
108
|
+
if (posterUrl && !safeMediaUrl(posterUrl)) return null;
|
|
109
|
+
return {
|
|
110
|
+
url,
|
|
111
|
+
type: media.type,
|
|
112
|
+
...posterUrl ? { posterUrl } : {}
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
function safeMediaUrl(value) {
|
|
116
|
+
if (!value || value.length > MAX_RETAINED_MEDIA_URL_LENGTH) return false;
|
|
117
|
+
try {
|
|
118
|
+
const protocol = new URL(value).protocol;
|
|
119
|
+
return protocol === "https:" || protocol === "http:";
|
|
120
|
+
} catch {
|
|
121
|
+
return false;
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
95
125
|
export {
|
|
96
126
|
VIDEO_STREAM_HEADER,
|
|
97
127
|
VIDEO_STREAM_CONTENT_TYPE,
|
|
98
128
|
encodeVideoSseEvent,
|
|
99
129
|
decodeVideoSse,
|
|
100
|
-
videoSseHeaders
|
|
130
|
+
videoSseHeaders,
|
|
131
|
+
sanitizeVideoChatMedia
|
|
101
132
|
};
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
// src/visual-system/scene-templates/external-video-backdrop.tsx
|
|
2
|
+
import React from "react";
|
|
3
|
+
import { jsx } from "react/jsx-runtime";
|
|
4
|
+
var DEFAULT = { mode: false, audioMuted: true, audioVolume: 1 };
|
|
5
|
+
var sharedContext = globalThis;
|
|
6
|
+
var BackdropContext = sharedContext.__vanillaskyVideoBackdropContext ??= React.createContext(DEFAULT);
|
|
7
|
+
function ExternalVideoBackdropProvider({
|
|
8
|
+
mode,
|
|
9
|
+
audioMuted = true,
|
|
10
|
+
audioVolume = 1,
|
|
11
|
+
children
|
|
12
|
+
}) {
|
|
13
|
+
const value = React.useMemo(
|
|
14
|
+
() => ({ mode, audioMuted, audioVolume }),
|
|
15
|
+
[mode, audioMuted, audioVolume]
|
|
16
|
+
);
|
|
17
|
+
return /* @__PURE__ */ jsx(BackdropContext.Provider, { value, children });
|
|
18
|
+
}
|
|
19
|
+
function useExternalVideoBackdrop() {
|
|
20
|
+
return React.useContext(BackdropContext).mode;
|
|
21
|
+
}
|
|
22
|
+
function useMediaAudio() {
|
|
23
|
+
const { audioMuted, audioVolume } = React.useContext(BackdropContext);
|
|
24
|
+
return { muted: audioMuted, volume: audioVolume };
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export {
|
|
28
|
+
ExternalVideoBackdropProvider,
|
|
29
|
+
useExternalVideoBackdrop,
|
|
30
|
+
useMediaAudio
|
|
31
|
+
};
|
|
@@ -1,19 +1,29 @@
|
|
|
1
1
|
// src/server/prompts/system-prompt.ts
|
|
2
|
-
|
|
2
|
+
function videoPlanInstruction(narrate = false) {
|
|
3
|
+
const narration = narrate ? `,"narration":"one sentence said aloud over this scene"` : "";
|
|
4
|
+
return `Wire format: newline-delimited JSON (NDJSON), exactly one complete JSON object per line.
|
|
3
5
|
Allowed plan parts:
|
|
4
|
-
{"type":"scene.add","scene":{"id":"stable-id","templateId":"trusted-template-id","variables":{},"timing":{"fixedDuration":4}}}
|
|
5
|
-
{"type":"scene.add","placement":"closer","scene":{"id":"closer","templateId":"trusted-closer-template-id","variables":{},"timing":{"fixedDuration":3}}}
|
|
6
|
+
{"type":"scene.add","scene":{"id":"stable-id","templateId":"trusted-template-id","variables":{},"timing":{"fixedDuration":4}${narration}}}
|
|
7
|
+
{"type":"scene.add","placement":"closer","scene":{"id":"closer","templateId":"trusted-closer-template-id","variables":{},"timing":{"fixedDuration":3}${narration}}}
|
|
6
8
|
{"type":"plan.complete","finishReason":"stop"}
|
|
7
9
|
Do not emit protocol envelopes, Markdown fences, comments, prose, partial objects, audio, generated source, or any part type not listed above.`;
|
|
10
|
+
}
|
|
11
|
+
var VIDEO_PLAN_INSTRUCTION = videoPlanInstruction();
|
|
8
12
|
function knowledgeRules(mode) {
|
|
9
13
|
return mode === "general" ? `- This request uses general knowledge mode.
|
|
10
14
|
- Use stable general knowledge to answer or develop the supplied request. Prefer broadly established, non-current information.
|
|
11
15
|
- Answer the request directly. Do not make missing source detail the subject when stable general knowledge can provide a useful answer; for broad questions, provide a practical, broadly applicable framework.
|
|
12
|
-
- For financial, medical, or legal topics, keep guidance general and
|
|
16
|
+
- For financial, medical, or legal topics, keep guidance general and informational. Never present it as personalized professional advice.
|
|
13
17
|
- Treat claims in the supplied input as authoritative. Never invent citations, quotations, URLs, personal details, live facts, guarantees, or precise claims that require a source.` : `- This request uses input-only knowledge mode.
|
|
14
18
|
- The supplied input is the complete factual basis. Do not add outside claims.`;
|
|
15
19
|
}
|
|
16
|
-
|
|
20
|
+
var NARRATION_RULES = `
|
|
21
|
+
Narration rules:
|
|
22
|
+
- Every scene.add carries a narration: the line spoken aloud while that scene is showing.
|
|
23
|
+
- One sentence, 10-16 words, plain spoken English. It is heard, never drawn.
|
|
24
|
+
- Say what the scene shows and why it matters. Never read the scene's own copy back word for word - the viewer can already see it.
|
|
25
|
+
- The lines are one continuous explanation: each follows from the ones before it. Never mention scenes, videos, slides, or yourself.`;
|
|
26
|
+
function createVideoSystemPrompt(knowledgeMode = "input-only", narrate = false) {
|
|
17
27
|
return `You are a video director.
|
|
18
28
|
|
|
19
29
|
Turn the supplied input into a concise, coherent sequence using trusted scene templates. Never return prose as the deliverable and never generate HTML, React, JavaScript, CSS, or animation source.
|
|
@@ -43,11 +53,14 @@ Streaming rules:
|
|
|
43
53
|
- End explicitly with plan.complete. A truncated stream is never treated as complete.
|
|
44
54
|
- Return only plan parts accepted by the provided schema.
|
|
45
55
|
|
|
46
|
-
${
|
|
56
|
+
${narrate ? NARRATION_RULES : ""}
|
|
57
|
+
|
|
58
|
+
${videoPlanInstruction(narrate)}`;
|
|
47
59
|
}
|
|
48
60
|
var DEFAULT_VIDEO_SYSTEM_PROMPT = createVideoSystemPrompt();
|
|
49
61
|
|
|
50
62
|
export {
|
|
63
|
+
videoPlanInstruction,
|
|
51
64
|
VIDEO_PLAN_INSTRUCTION,
|
|
52
65
|
createVideoSystemPrompt,
|
|
53
66
|
DEFAULT_VIDEO_SYSTEM_PROMPT
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
createVideoSystemPrompt
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-5SLENAJW.js";
|
|
4
4
|
import {
|
|
5
5
|
PACING_PLANNER_RULES
|
|
6
6
|
} from "./chunk-3O7OMMMF.js";
|
|
@@ -64,12 +64,19 @@ function needsPlannerSchema(schema) {
|
|
|
64
64
|
const visit = (property) => property.minimum != null || property.maximum != null || property.type === "object" || property.items != null && visit(property.items);
|
|
65
65
|
return Object.values(schema.properties).some(visit);
|
|
66
66
|
}
|
|
67
|
+
function isMediaProperty(property) {
|
|
68
|
+
return property?.format === "uri" || property?.format === "supplied-image" || property?.format === "stock-media-keyword";
|
|
69
|
+
}
|
|
70
|
+
function requiresAvailableMedia(template) {
|
|
71
|
+
const gates = getTemplateSchemaGates(template.schema);
|
|
72
|
+
return (template.schema.required ?? []).some((name) => isMediaProperty(template.schema.properties[name])) || gates.requiredAnyOf.some((group) => group.every((name) => isMediaProperty(template.schema.properties[name])));
|
|
73
|
+
}
|
|
67
74
|
var COMMON_MEDIA_VARIABLES = {
|
|
68
75
|
mediaUrl: "media",
|
|
69
76
|
mediaType: "enum(auto|photo|video|gradient)",
|
|
70
77
|
mediaPoster: "media",
|
|
71
78
|
mediaPosition: "enum(center|top|bottom|left|right)",
|
|
72
|
-
mediaTreatment: "enum(subtle|cinematic|text-safe)"
|
|
79
|
+
mediaTreatment: "enum(none|subtle|cinematic|text-safe)"
|
|
73
80
|
};
|
|
74
81
|
function plannerCatalog(templates, mediaResolverAvailable) {
|
|
75
82
|
return templates.map((template) => {
|
|
@@ -103,12 +110,15 @@ function plannerCatalog(templates, mediaResolverAvailable) {
|
|
|
103
110
|
});
|
|
104
111
|
}
|
|
105
112
|
function createTemplateSystemPrompt(options) {
|
|
106
|
-
const templates = options.kit.listTemplateMetadata();
|
|
113
|
+
const templates = options.kit.listTemplateMetadata().filter((template) => !requiresAvailableMedia(template) || options.suppliedMediaAvailable === true || options.mediaResolverAvailable === true && getStandardMediaResolverContract(template.schema) != null);
|
|
107
114
|
const resolverMediaAvailable = options.mediaResolverAvailable === true && templates.some(({ schema }) => getStandardMediaResolverContract(schema) != null);
|
|
108
115
|
const ids = new Set(templates.map(({ id }) => id));
|
|
109
116
|
const fields = new Set(templates.flatMap(({ schema }) => Object.keys(schema.properties)));
|
|
110
|
-
const basePrompt = createVideoSystemPrompt(options.knowledgeMode).split("\n").filter(
|
|
111
|
-
(line) => !line.includes("Never use media, ctaMedia, or reaction as the first generated body template") &&
|
|
117
|
+
const basePrompt = createVideoSystemPrompt(options.knowledgeMode, options.narrate === true).split("\n").filter(
|
|
118
|
+
(line) => !line.includes("Never use media, ctaMedia, or reaction as the first generated body template") && // The rule that keeps the opening beat asset-free belongs to the same
|
|
119
|
+
// pair as the one below it: both exist so playback can start without
|
|
120
|
+
// waiting on a provider, and both go when the host owns that wait.
|
|
121
|
+
!(options.mediaOnFirstScene === true && line.includes("The first generated body scene must be asset-free")) && !(resolverMediaAvailable && line.includes("Never expose a loading placeholder or unresolved media keyword"))
|
|
112
122
|
).join("\n");
|
|
113
123
|
const factRules = [
|
|
114
124
|
...ids.has("cardList") ? ["cardList needs two or three unused parallel facts"] : [],
|
|
@@ -139,12 +149,18 @@ ${options.basePrompt.trim()}` : void 0,
|
|
|
139
149
|
terminalPayoffs.length > 0 ? `When the input has no clear grounded action, never invent one and do not use jobs:[ask]. End with a grounded payoff using ${terminalPayoffNames}. Its concise 6\u201312 words of declarative copy must answer the story's so-what without repeating hook or setup language. Use media for an honest visual payoff, emojiBurst for playful delight, and confetti only for a real celebration, win, or milestone.` : void 0,
|
|
140
150
|
"requiresQuote is a hard gate: use those templates only for exact quoted words and attribution present in raw input. Never turn a role, relationship, or summary into speech. requiresScreenshot likewise needs an actual supplied screenshot URL.",
|
|
141
151
|
templates.some(({ schema }) => (schema["x-vanillasky"]?.requiredAnyOf?.length ?? 0) > 0) ? "requiredAnyOf is a hard presence gate: satisfy every listed group with at least one non-empty value." : void 0,
|
|
142
|
-
mediaTemplates.length > 0 ? `For streaming startup, ${mediaTemplateNames} ${mediaTemplates.length === 1 ? "is" : "are"} forbidden as the first generated body template, even when mediaType is gradient. Start with a content-fit non-media template.` : void 0,
|
|
152
|
+
mediaTemplates.length > 0 && options.mediaOnFirstScene !== true ? `For streaming startup, ${mediaTemplateNames} ${mediaTemplates.length === 1 ? "is" : "are"} forbidden as the first generated body template, even when mediaType is gradient. Start with a content-fit non-media template.` : void 0,
|
|
143
153
|
listFields.length > 0 ? `For list variables (${listFields.join(", ")}), emit actual JSON arrays. Keep list labels to 1\u20133 words. Keep step labels to 1\u20132 words and at most 18 characters. Do not use pipes or newlines as list delimiters.` : void 0,
|
|
144
154
|
fields.has("bars") ? 'For bars, emit an actual JSON array of 2\u20136 grounded objects with "label" and "value" fields. Use comparable units and avoid a largest-to-smallest positive ratio above 20. Do not encode bars as a string.' : void 0,
|
|
145
|
-
resolverMediaAvailable ?
|
|
155
|
+
resolverMediaAvailable ? `Prefer a relevant resolved image or video background on ${options.mediaOnFirstScene === true ? "every media-capable scene, the first included," : "later media-capable scenes"} whenever the permitted factual basis names a concrete person, place, product context, activity, or outcome that can be depicted honestly. Emit mediaKeyword on scene.add as a specific 2\u20138 word semantic query of at most 80 characters. The host removes mediaKeyword before the complete scene reaches the browser and replaces it with an approved asset. Use mediaType=gradient for abstract, sensitive, unsafe, or visually ambiguous material. Never emit or invent mediaUrl or mediaPoster.` : mediaTemplates.length > 0 ? `Use exact grounded values for required fields. mediaUrl must come verbatim from supplied input. Stock queries are not available. Do not select ${mediaTemplateNames} without a supplied mediaUrl; if another template has no mediaUrl, explicitly use mediaType=gradient. mediaType auto detects a URL, while gradient deliberately uses no external asset.` : "Use exact grounded values for required fields.",
|
|
146
156
|
"Catalog guidance describes composition; it is not a factual source and must never replace the permitted factual basis.",
|
|
147
|
-
JSON.stringify(plannerCatalog(templates, resolverMediaAvailable))
|
|
157
|
+
JSON.stringify(plannerCatalog(templates, resolverMediaAvailable)),
|
|
158
|
+
// Last, because the catalogue is thousands of tokens of per-scene field
|
|
159
|
+
// lists and it is what the model matches when it emits a scene. The wire
|
|
160
|
+
// format above says narration belongs there; said only above, a third of
|
|
161
|
+
// the way through, it loses to the schema the model has just finished
|
|
162
|
+
// reading, and every scene comes back silent.
|
|
163
|
+
options.narrate === true ? '\nThe catalog lists variables only. Every scene.add also carries "narration" beside "variables" and "timing", as specified in the wire format above. A scene without it is incomplete.' : void 0
|
|
148
164
|
].filter((line) => line != null).join("\n");
|
|
149
165
|
}
|
|
150
166
|
|
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
import {
|
|
2
2
|
getBackgroundTransform
|
|
3
3
|
} from "./chunk-W3VDIBGR.js";
|
|
4
|
+
import {
|
|
5
|
+
useMediaAudio
|
|
6
|
+
} from "./chunk-5CDAM24P.js";
|
|
4
7
|
|
|
5
8
|
// src/visual-system/scene-templates/scene-video-backdrop.tsx
|
|
6
9
|
import { useEffect, useRef, useState } from "react";
|
|
@@ -27,6 +30,8 @@ var SceneVideoBackdrop = ({
|
|
|
27
30
|
progress,
|
|
28
31
|
beatIntensity = 0,
|
|
29
32
|
isPlaying,
|
|
33
|
+
muted,
|
|
34
|
+
volume,
|
|
30
35
|
playbackId = mediaUrl,
|
|
31
36
|
retainPoster = false,
|
|
32
37
|
persistent = false,
|
|
@@ -34,6 +39,9 @@ var SceneVideoBackdrop = ({
|
|
|
34
39
|
onReady,
|
|
35
40
|
onError
|
|
36
41
|
}) => {
|
|
42
|
+
const inheritedAudio = useMediaAudio();
|
|
43
|
+
const resolvedMuted = muted ?? inheritedAudio.muted;
|
|
44
|
+
const resolvedVolume = volume ?? inheritedAudio.volume;
|
|
37
45
|
const resolvedPosition = resolveMediaPosition(mediaPosition);
|
|
38
46
|
const bgTransform = getBackgroundTransform(backgroundEffect, progress, beatIntensity);
|
|
39
47
|
const [decodedVideoUrl, setDecodedVideoUrl] = useState();
|
|
@@ -41,6 +49,25 @@ var SceneVideoBackdrop = ({
|
|
|
41
49
|
const startedVideoUrl = useRef(void 0);
|
|
42
50
|
const startedPlaybackId = useRef(void 0);
|
|
43
51
|
const videoPresentationKey = `${playbackId}\0${mediaUrl}`;
|
|
52
|
+
useEffect(() => {
|
|
53
|
+
const video = videoRef.current;
|
|
54
|
+
if (!video) return;
|
|
55
|
+
if (video.getAttribute("src") !== mediaUrl) {
|
|
56
|
+
video.setAttribute("src", mediaUrl);
|
|
57
|
+
video.load();
|
|
58
|
+
}
|
|
59
|
+
return () => {
|
|
60
|
+
video.pause();
|
|
61
|
+
video.removeAttribute("src");
|
|
62
|
+
video.load();
|
|
63
|
+
startedVideoUrl.current = void 0;
|
|
64
|
+
startedPlaybackId.current = void 0;
|
|
65
|
+
};
|
|
66
|
+
}, [mediaUrl]);
|
|
67
|
+
useEffect(() => {
|
|
68
|
+
const video = videoRef.current;
|
|
69
|
+
if (video) video.volume = resolvedVolume;
|
|
70
|
+
}, [resolvedVolume]);
|
|
44
71
|
useEffect(() => {
|
|
45
72
|
const video = videoRef.current;
|
|
46
73
|
if (!video) return;
|
|
@@ -59,15 +86,6 @@ var SceneVideoBackdrop = ({
|
|
|
59
86
|
startedVideoUrl.current = mediaUrl;
|
|
60
87
|
startedPlaybackId.current = playbackId;
|
|
61
88
|
}, [isPlaying, mediaUrl, playbackId]);
|
|
62
|
-
useEffect(() => {
|
|
63
|
-
const video = videoRef.current;
|
|
64
|
-
return () => {
|
|
65
|
-
if (!video) return;
|
|
66
|
-
video.pause();
|
|
67
|
-
video.removeAttribute("src");
|
|
68
|
-
video.load();
|
|
69
|
-
};
|
|
70
|
-
}, []);
|
|
71
89
|
const mediaStyle = {
|
|
72
90
|
position: "absolute",
|
|
73
91
|
inset: 0,
|
|
@@ -135,7 +153,7 @@ var SceneVideoBackdrop = ({
|
|
|
135
153
|
ref: videoRef,
|
|
136
154
|
src: mediaUrl,
|
|
137
155
|
poster: retainPoster || decodedVideoUrl !== mediaUrl ? mediaPoster || void 0 : void 0,
|
|
138
|
-
muted:
|
|
156
|
+
muted: resolvedMuted,
|
|
139
157
|
loop: true,
|
|
140
158
|
playsInline: true,
|
|
141
159
|
preload: "auto",
|
|
@@ -1,29 +1,14 @@
|
|
|
1
1
|
import {
|
|
2
2
|
parseVideoEvent
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-MMUXVA47.js";
|
|
4
|
+
import {
|
|
5
|
+
checksumVideo,
|
|
6
|
+
stableJson
|
|
7
|
+
} from "./chunk-2PYO6VAC.js";
|
|
4
8
|
import {
|
|
5
9
|
VIDEO_SCHEMA_VERSION
|
|
6
10
|
} from "./chunk-AOWSXU2K.js";
|
|
7
11
|
|
|
8
|
-
// src/protocol/stable-json.ts
|
|
9
|
-
function stableJson(value) {
|
|
10
|
-
if (value == null || typeof value !== "object") return JSON.stringify(value);
|
|
11
|
-
if (Array.isArray(value)) return `[${value.map(stableJson).join(",")}]`;
|
|
12
|
-
const entries = Object.entries(value).filter(([, child]) => child !== void 0).sort(([left], [right]) => left < right ? -1 : left > right ? 1 : 0);
|
|
13
|
-
return `{${entries.map(([key, child]) => `${JSON.stringify(key)}:${stableJson(child)}`).join(",")}}`;
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
// src/protocol/checksum.ts
|
|
17
|
-
function checksumVideo(config) {
|
|
18
|
-
const input = stableJson(config);
|
|
19
|
-
let hash = 2166136261;
|
|
20
|
-
for (let index = 0; index < input.length; index += 1) {
|
|
21
|
-
hash ^= input.charCodeAt(index);
|
|
22
|
-
hash = Math.imul(hash, 16777619) >>> 0;
|
|
23
|
-
}
|
|
24
|
-
return `fnv1a32:${hash.toString(16).padStart(8, "0")}`;
|
|
25
|
-
}
|
|
26
|
-
|
|
27
12
|
// src/protocol/state.ts
|
|
28
13
|
function createVideoState() {
|
|
29
14
|
return {
|
|
@@ -188,7 +173,6 @@ function createReplayableStream(source, onError) {
|
|
|
188
173
|
}
|
|
189
174
|
|
|
190
175
|
export {
|
|
191
|
-
checksumVideo,
|
|
192
176
|
createVideoState,
|
|
193
177
|
applyVideoEvent,
|
|
194
178
|
createReplayableStream
|