@vanillaskyai/video 0.6.0 → 0.7.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.
Files changed (130) hide show
  1. package/CHANGELOG.md +164 -0
  2. package/PUBLIC-API.md +123 -9
  3. package/README.md +69 -115
  4. package/dist/{bg-confetti-SHQI7ATB.js → bg-confetti-HITCGLGD.js} +1 -1
  5. package/dist/{bg-emoji-XYFRA63Q.js → bg-emoji-VU4UZYZN.js} +1 -1
  6. package/dist/{bg-media-G4XOQEAS.js → bg-media-L34PDQXJ.js} +2 -2
  7. package/dist/{brand-message-XHONEDNW.js → brand-message-FUJ4STFO.js} +1 -1
  8. package/dist/{builtin-server-YHEZ2JRF.js → builtin-server-KB7FKF6A.js} +2 -2
  9. package/dist/{chart-bar-HRHD57ML.js → chart-bar-EGPHJATL.js} +2 -2
  10. package/dist/{chart-counter-KFPV44RB.js → chart-counter-5FFWQNZO.js} +2 -2
  11. package/dist/{chart-progress-ring-OR2VOIHV.js → chart-progress-ring-JMXQLMLC.js} +2 -2
  12. package/dist/{chunk-66MNRUCR.js → chunk-2PYO6VAC.js} +51 -3
  13. package/dist/{chunk-ZQQQAAKP.js → chunk-2XT4MZ76.js} +33 -2
  14. package/dist/{chunk-PNS52FL4.js → chunk-5SLENAJW.js} +19 -6
  15. package/dist/{chunk-EFL34TXF.js → chunk-AITKH6QT.js} +24 -8
  16. package/dist/{chunk-FZHMQFG3.js → chunk-GAIOHGNR.js} +5 -21
  17. package/dist/{chunk-3RV4YKB3.js → chunk-JNN3EYVP.js} +51 -26
  18. package/dist/{chunk-HVCPEAQF.js → chunk-LKBZX7GV.js} +9 -6
  19. package/dist/{chunk-34O5BY6X.js → chunk-LLPMVDJZ.js} +21 -4
  20. package/dist/{chunk-G66Z5CWR.js → chunk-MMUXVA47.js} +27 -3
  21. package/dist/{chunk-7BGAU6C3.js → chunk-MXZSDGZQ.js} +2 -1
  22. package/dist/{chunk-3OU7HIEB.js → chunk-RE4IMWJR.js} +15 -31
  23. package/dist/{chunk-FVTMYS6U.js → chunk-RGF452LL.js} +1 -1
  24. package/dist/chunk-SKRGRKHY.js +142 -0
  25. package/dist/{chunk-TCEHK2BW.js → chunk-YJJC4N4D.js} +2 -1
  26. package/dist/cli.js +438 -30
  27. package/dist/{compose-video-JWIEDTEJ.js → compose-video-PD6LKKRK.js} +4 -4
  28. package/dist/{cta-logo-JJOGK4UC.js → cta-logo-YR7LYUR5.js} +1 -1
  29. package/dist/{cta-media-IQ3MAWEH.js → cta-media-DQJBKUO2.js} +2 -2
  30. package/dist/{events-BBP30j3c.d.ts → events-B6qS1Lsb.d.ts} +2 -2
  31. package/dist/{incoming-call-KXF67OT2.js → incoming-call-54KQ7O5A.js} +1 -1
  32. package/dist/index.d.ts +89 -3
  33. package/dist/index.js +12 -1
  34. package/dist/{infographic-before-after-2I7YZVDD.js → infographic-before-after-ROS52GOW.js} +1 -1
  35. package/dist/{infographic-feature-list-QDM4I7XE.js → infographic-feature-list-E7MGUDAN.js} +2 -2
  36. package/dist/{infographic-problem-solution-FMLJCPG6.js → infographic-problem-solution-3S6NGO5N.js} +2 -2
  37. package/dist/{infographic-stat-row-VFTIXS5F.js → infographic-stat-row-SYB7AB7H.js} +2 -2
  38. package/dist/{infographic-steps-L7W24Z35.js → infographic-steps-KCE6JWE2.js} +2 -2
  39. package/dist/{kit-DEG3fcaL.d.ts → kit-8g46H2RZ.d.ts} +1 -1
  40. package/dist/{prompt-input-F3CYU2XH.js → prompt-input-MHX4O42G.js} +1 -1
  41. package/dist/react.d.ts +194 -6
  42. package/dist/react.js +1849 -40
  43. package/dist/{reaction-2XCLOJOK.js → reaction-SLEGR3BE.js} +2 -2
  44. package/dist/server.d.ts +102 -4
  45. package/dist/server.js +972 -27
  46. package/dist/{showcase-code-KHSMIPG6.js → showcase-code-2HLAVKBM.js} +2 -2
  47. package/dist/{showcase-phone-M4CEANIW.js → showcase-phone-Q4HEO4LH.js} +2 -2
  48. package/dist/{showcase-terminal-26I3DYF7.js → showcase-terminal-DL45LUFG.js} +2 -2
  49. package/dist/{showcase-web-TY6CZV2Z.js → showcase-web-2QZO73FZ.js} +2 -2
  50. package/dist/{social-milestone-TF6MX7ET.js → social-milestone-GEFUQSBF.js} +1 -1
  51. package/dist/{social-notification-2RJDAZSF.js → social-notification-FJ5VHVLK.js} +1 -1
  52. package/dist/{social-review-stack-2ICKJQG7.js → social-review-stack-KXQCKFRP.js} +1 -1
  53. package/dist/{social-testimonial-X7OGADA3.js → social-testimonial-JZ7CHC7S.js} +1 -1
  54. package/dist/{social-tweet-TK7SH2F6.js → social-tweet-UCDXM25F.js} +1 -1
  55. package/dist/{system-prompt-4I6Z5HK3.js → system-prompt-RRXIWDDD.js} +5 -3
  56. package/dist/template-catalog.js +1 -1
  57. package/dist/templates.d.ts +3 -3
  58. package/dist/test.d.ts +2 -2
  59. package/dist/test.js +3 -3
  60. package/dist/{text-stream-J6EDDJP4.js → text-stream-ROTNGSD4.js} +2 -2
  61. package/dist/{types-BV9IqExh.d.ts → types-2wHBqtg8.d.ts} +29 -1
  62. package/dist/types-DrABlRa7.d.ts +46 -0
  63. package/docs/agent-integration.md +49 -25
  64. package/docs/architecture.md +37 -22
  65. package/docs/branding-and-personalization.md +1 -1
  66. package/docs/concepts.md +14 -0
  67. package/docs/custom-templates.md +9 -9
  68. package/docs/customization.md +41 -5
  69. package/docs/getting-started.md +81 -84
  70. package/docs/media-and-audio.md +103 -166
  71. package/docs/persistence.md +1 -1
  72. package/docs/production.md +81 -92
  73. package/docs/prompt-and-input.md +71 -161
  74. package/docs/provider-integration.md +91 -49
  75. package/docs/responsive-orientation.md +1 -1
  76. package/docs/security.md +6 -5
  77. package/docs/streaming-protocol.md +1 -1
  78. package/docs/testing.md +51 -43
  79. package/examples/custom-template/README.md +1 -1
  80. package/package.json +20 -22
  81. package/registry/items/barChart.json +6 -4
  82. package/registry/items/beforeAfter.json +1 -1
  83. package/registry/items/bigNumber.json +4 -3
  84. package/registry/items/brandMessage.json +3 -2
  85. package/registry/items/cardList.json +6 -4
  86. package/registry/items/codeEditor.json +4 -3
  87. package/registry/items/confetti.json +1 -1
  88. package/registry/items/ctaLogo.json +3 -2
  89. package/registry/items/ctaMedia.json +4 -3
  90. package/registry/items/emojiBurst.json +1 -1
  91. package/registry/items/incomingCall.json +3 -2
  92. package/registry/items/media.json +4 -3
  93. package/registry/items/milestone.json +3 -2
  94. package/registry/items/notification.json +3 -2
  95. package/registry/items/phoneMockup.json +4 -3
  96. package/registry/items/problemSolution.json +4 -3
  97. package/registry/items/progressRing.json +4 -3
  98. package/registry/items/promptInput.json +3 -2
  99. package/registry/items/reaction.json +2 -2
  100. package/registry/items/reviewStack.json +3 -2
  101. package/registry/items/steps.json +6 -4
  102. package/registry/items/terminal.json +5 -4
  103. package/registry/items/testimonial.json +3 -2
  104. package/registry/items/tripleStats.json +4 -3
  105. package/registry/items/tweet.json +3 -2
  106. package/registry/items/webMockup.json +4 -3
  107. package/starters/video-chat/.env.example +16 -0
  108. package/starters/video-chat/README.md +63 -0
  109. package/starters/video-chat/index.html +12 -0
  110. package/starters/video-chat/package.json +28 -0
  111. package/starters/video-chat/server.ts +152 -0
  112. package/starters/video-chat/src/main.tsx +8 -0
  113. package/starters/video-chat/stock.ts +139 -0
  114. package/starters/video-chat/tsconfig.json +20 -0
  115. package/starters/video-chat/vite.config.ts +81 -0
  116. package/styles/video-chat.css +713 -0
  117. package/docs/input-and-first-scene.md +0 -64
  118. package/docs/integrate-nextjs.md +0 -86
  119. package/docs/live-channels.md +0 -149
  120. package/docs/use-cases.md +0 -59
  121. package/examples/nextjs-quickstart/.env.example +0 -2
  122. package/examples/nextjs-quickstart/README.md +0 -27
  123. package/examples/nextjs-quickstart/next-env.d.ts +0 -4
  124. package/examples/nextjs-quickstart/package.json +0 -25
  125. package/examples/nextjs-quickstart/src/app/api/video/route.ts +0 -33
  126. package/examples/nextjs-quickstart/src/app/layout.tsx +0 -5
  127. package/examples/nextjs-quickstart/src/app/page.tsx +0 -31
  128. package/examples/nextjs-quickstart/tsconfig.json +0 -26
  129. package/examples/server-integrations/README.md +0 -20
  130. package/examples/server-integrations/src/ai-sdk-media.ts +0 -90
@@ -40,8 +40,8 @@ function createBackgroundMediaSchemaProperties(options = {}) {
40
40
  mediaTreatment: {
41
41
  type: "string",
42
42
  title: "Background contrast treatment",
43
- description: "subtle preserves a visual hero; cinematic adds balanced contrast; text-safe adds a stronger wash for copy-heavy scenes.",
44
- enum: ["subtle", "cinematic", "text-safe"],
43
+ description: "none leaves the picture untouched, for a scene carrying no type; subtle preserves a visual hero; cinematic adds balanced contrast; text-safe adds a stronger wash for copy-heavy scenes.",
44
+ enum: ["none", "subtle", "cinematic", "text-safe"],
45
45
  default: "cinematic"
46
46
  }
47
47
  };
@@ -254,7 +254,8 @@ var BUILTIN_TEMPLATE_SCHEMAS = {
254
254
  "type": "string",
255
255
  "title": "Text",
256
256
  "description": "Title text shown above the chart",
257
- "default": "Revenue up 300%."
257
+ "default": "Revenue up 300%.",
258
+ "maxLength": 48
258
259
  },
259
260
  "bars": {
260
261
  "type": "array",
@@ -603,7 +604,7 @@ var BUILTIN_TEMPLATE_SCHEMAS = {
603
604
  "description": "Command typed character by character. Keep the meaningful invocation within 80 characters; the terminal deliberately ellipsizes longer display lines.",
604
605
  "minLength": 1,
605
606
  "maxLength": 80,
606
- "default": "npx vanillasky check"
607
+ "default": "npx vanillasky templates check"
607
608
  },
608
609
  "output": {
609
610
  "type": "array",
@@ -1346,7 +1347,8 @@ var BUILTIN_TEMPLATE_SCHEMAS = {
1346
1347
  "type": "string",
1347
1348
  "title": "Text",
1348
1349
  "description": "Title text",
1349
- "default": "What you get."
1350
+ "default": "What you get.",
1351
+ "maxLength": 48
1350
1352
  },
1351
1353
  "items": {
1352
1354
  "type": "array",
@@ -1398,7 +1400,8 @@ var BUILTIN_TEMPLATE_SCHEMAS = {
1398
1400
  "type": "string",
1399
1401
  "title": "Text",
1400
1402
  "description": "Title text",
1401
- "default": "How it works."
1403
+ "default": "How it works.",
1404
+ "maxLength": 48
1402
1405
  },
1403
1406
  "steps": {
1404
1407
  "type": "array",
@@ -6,7 +6,7 @@ import {
6
6
  } from "./chunk-E7CL7UPB.js";
7
7
  import {
8
8
  parseVideoPlanPart
9
- } from "./chunk-G66Z5CWR.js";
9
+ } from "./chunk-MMUXVA47.js";
10
10
 
11
11
  // src/server/model/truncated-json.ts
12
12
  function isConfidentlyTruncatedJson(source) {
@@ -48,11 +48,28 @@ function parsePlanLine(line) {
48
48
  if (value && typeof value === "object" && !Array.isArray(value)) {
49
49
  const object = value;
50
50
  const scene = object.scene;
51
- if (object.type === "scene.add" && object.timing != null && scene && typeof scene === "object" && !Array.isArray(scene) && !("timing" in scene)) {
52
- const { timing, ...planPart } = object;
51
+ if (object.type === "scene.add" && scene && typeof scene === "object" && !Array.isArray(scene)) {
52
+ const planPart = { ...object };
53
+ const repairedScene = { ...scene };
54
+ let repaired = false;
55
+ if (object.placement != null && object.placement !== "closer") {
56
+ delete planPart.placement;
57
+ repaired = true;
58
+ }
59
+ if (object.timing != null && !("timing" in scene)) {
60
+ repairedScene.timing = object.timing;
61
+ delete planPart.timing;
62
+ repaired = true;
63
+ }
64
+ if (object.narration != null) {
65
+ if (!("narration" in scene)) repairedScene.narration = object.narration;
66
+ delete planPart.narration;
67
+ repaired = true;
68
+ }
69
+ if (!repaired) return parseVideoPlanPart(value);
53
70
  return parseVideoPlanPart({
54
71
  ...planPart,
55
- scene: { ...scene, timing }
72
+ scene: repairedScene
56
73
  });
57
74
  }
58
75
  }
@@ -11,11 +11,13 @@ var VIDEO_WARNING_CATEGORIES = {
11
11
  scene_duration_adjusted: "readability",
12
12
  scene_omitted_unreadable: "readability",
13
13
  scene_omitted_for_closer: "readability",
14
+ scene_variable_clipped: "readability",
14
15
  chart_scale_imbalance: "readability",
15
16
  plan_incomplete: "provider",
16
17
  plan_missing_closer: "provider",
17
18
  provider_warning: "provider",
18
- provider_diagnostics_unavailable: "provider"
19
+ provider_diagnostics_unavailable: "provider",
20
+ media_budget_reached: "media"
19
21
  };
20
22
  var MAX_PUBLIC_DIAGNOSTIC_LENGTH = 160;
21
23
  var SECRET_ASSIGNMENT = /\b(?:api[_-]?key|authorization|secret|token|password)\s*[:=]\s*\S+/gi;
@@ -32,6 +34,22 @@ function safePublicDiagnostic(value, fallback) {
32
34
  if (redacted.length <= MAX_PUBLIC_DIAGNOSTIC_LENGTH) return redacted;
33
35
  return `${redacted.slice(0, MAX_PUBLIC_DIAGNOSTIC_LENGTH - 1).trimEnd()}\u2026`;
34
36
  }
37
+ function createClippedVariableWarning(templateId, fields) {
38
+ return {
39
+ code: "scene_variable_clipped",
40
+ category: VIDEO_WARNING_CATEGORIES.scene_variable_clipped,
41
+ message: `Trimmed ${fields.join(", ")} on ${templateId} to the length the template declares.`,
42
+ recoverable: true
43
+ };
44
+ }
45
+ function createMediaBudgetWarning(limit) {
46
+ return {
47
+ code: "media_budget_reached",
48
+ category: VIDEO_WARNING_CATEGORIES.media_budget_reached,
49
+ message: `Resolved media for ${limit} scene${limit === 1 ? "" : "s"}; later scenes keep their copy on the brand gradient.`,
50
+ recoverable: true
51
+ };
52
+ }
35
53
 
36
54
  // src/protocol/json-validation.ts
37
55
  var DANGEROUS_KEYS = /* @__PURE__ */ new Set(["__proto__", "constructor", "prototype"]);
@@ -125,7 +143,8 @@ function scene(value, path) {
125
143
  "variables",
126
144
  "textArchetype",
127
145
  "backgroundEffect",
128
- "timing"
146
+ "timing",
147
+ "narration"
129
148
  ], path);
130
149
  string(result.id, `${path}.id`);
131
150
  string(result.templateId, `${path}.templateId`);
@@ -134,6 +153,7 @@ function scene(value, path) {
134
153
  timing(result.timing, `${path}.timing`);
135
154
  if (result.textArchetype != null) string(result.textArchetype, `${path}.textArchetype`);
136
155
  if (result.backgroundEffect != null) string(result.backgroundEffect, `${path}.backgroundEffect`);
156
+ if (result.narration != null) string(result.narration, `${path}.narration`);
137
157
  return result;
138
158
  }
139
159
  function capabilities(value, path) {
@@ -161,9 +181,11 @@ function style(value, path) {
161
181
  "defaultTextArchetype",
162
182
  "defaultTransition",
163
183
  "density",
164
- "motion"
184
+ "motion",
185
+ "generatedLook"
165
186
  ], path);
166
187
  validateVideoBrand(result.brand, `${path}.brand`);
188
+ if (result.generatedLook != null) string(result.generatedLook, `${path}.generatedLook`);
167
189
  return result;
168
190
  }
169
191
  function audio(value, path) {
@@ -338,6 +360,8 @@ function parseVideoPlanPart(value) {
338
360
 
339
361
  export {
340
362
  safePublicDiagnostic,
363
+ createClippedVariableWarning,
364
+ createMediaBudgetWarning,
341
365
  record,
342
366
  allowedKeys,
343
367
  string,
@@ -17,7 +17,7 @@ import {
17
17
  import { useEffect, useState } from "react";
18
18
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
19
19
  function resolveMediaTreatment(value) {
20
- return value === "subtle" || value === "text-safe" ? value : "cinematic";
20
+ return value === "none" || value === "subtle" || value === "text-safe" ? value : "cinematic";
21
21
  }
22
22
  var SCRIM_STOP_COUNT = 7;
23
23
  function smoothstep(t) {
@@ -40,6 +40,7 @@ function easedStops(peakAlpha, start, end, direction) {
40
40
  }
41
41
  function getMediaTreatmentLayers(value, anchor = "full") {
42
42
  const treatment = resolveMediaTreatment(value);
43
+ if (treatment === "none") return [];
43
44
  const vignette = {
44
45
  id: "vignette",
45
46
  background: treatment === "subtle" ? `radial-gradient(ellipse at center, ${easedStops(0.28, 45, 100, "fade-in")})` : `radial-gradient(ellipse at center, ${easedStops(0.72, 32, 100, "fade-in")})`
@@ -3,23 +3,23 @@ import {
3
3
  } from "./chunk-E7CL7UPB.js";
4
4
  import {
5
5
  applyVideoEvent,
6
- checksumVideo,
7
6
  createReplayableStream,
8
7
  createVideoState
9
- } from "./chunk-FZHMQFG3.js";
8
+ } from "./chunk-GAIOHGNR.js";
10
9
  import {
11
10
  parseVideoPlanPart,
12
11
  safePublicDiagnostic
13
- } from "./chunk-G66Z5CWR.js";
12
+ } from "./chunk-MMUXVA47.js";
14
13
  import {
15
14
  MAX_RETAINED_INSTRUCTIONS_LENGTH,
16
15
  MAX_RETAINED_MEDIA_URLS,
17
16
  MAX_RETAINED_MEDIA_URL_LENGTH,
18
17
  MAX_RETAINED_SOURCE_LENGTH,
18
+ checksumVideo,
19
+ createVideoEventFactory,
19
20
  parseVideo
20
- } from "./chunk-66MNRUCR.js";
21
+ } from "./chunk-2PYO6VAC.js";
21
22
  import {
22
- VIDEO_PROTOCOL_VERSION,
23
23
  VIDEO_SCHEMA_VERSION,
24
24
  createVideoRequest
25
25
  } from "./chunk-AOWSXU2K.js";
@@ -32,25 +32,6 @@ import {
32
32
  resolveVideoBrand
33
33
  } from "./chunk-XGRA2MUV.js";
34
34
 
35
- // src/protocol/events.ts
36
- function createVideoEventFactory(options) {
37
- let sequence = options.initialSequence ?? 0;
38
- return {
39
- create(type, data) {
40
- const currentSequence = sequence;
41
- sequence += 1;
42
- return {
43
- protocolVersion: VIDEO_PROTOCOL_VERSION,
44
- runId: options.runId,
45
- sequence: currentSequence,
46
- eventId: `${options.runId}:${currentSequence}`,
47
- type,
48
- data
49
- };
50
- }
51
- };
52
- }
53
-
54
35
  // src/server/prompts/user-prompt.ts
55
36
  var SUPPLIED_MEDIA_REFERENCE_BASE = "https://vanillasky.invalid/supplied/";
56
37
  function suppliedMediaReferences(input) {
@@ -92,10 +73,12 @@ function buildVideoUserPrompt(input, openingDurationSec = 0) {
92
73
  "Compose a video response from the structured customer input below.",
93
74
  `Knowledge mode: ${input.knowledgeMode ?? "input-only"}.`,
94
75
  `Maximum duration: ${input.maxDurationSec ?? 30} seconds, including the supplied opening.`,
95
- typeof input.opening === "string" && input.opening.trim() ? `The host has already added the opening scene, which consumes ${openingDurationSec} seconds. Continue after it and do not repeat or rewrite it.` : "Add the first grounded scene as soon as it is complete.",
96
- "The first generated body scene must be fully playable without external media. Use a content-fit text, data, comparison, list, or device-free template with no media URL or keyword.",
97
- "Never use media, ctaMedia, or reaction as the first generated body template, even with mediaType=gradient. Choose a non-media template first.",
98
- "Add that scene before resolving any stock or supplied asset. Resolve media as part of each complete later body scene.",
76
+ input.opening === false ? "The host owns the opening wait. Add the first grounded scene as soon as it is complete; it may use host-resolved media." : typeof input.opening === "string" && input.opening.trim() ? `The host has already added the opening scene, which consumes ${openingDurationSec} seconds. Continue after it and do not repeat or rewrite it.` : "Add the first grounded scene as soon as it is complete.",
77
+ ...input.opening === false ? [] : [
78
+ "The first generated body scene must be fully playable without external media. Use a content-fit text, data, comparison, list, or device-free template with no media URL or keyword.",
79
+ "Never use media, ctaMedia, or reaction as the first generated body template, even with mediaType=gradient. Choose a non-media template first.",
80
+ "Add that scene before resolving any stock or supplied asset. Resolve media as part of each complete later body scene."
81
+ ],
99
82
  "Use only claims supported by the factual basis permitted by the trusted system prompt.",
100
83
  "Select the most decision-relevant grounded takeaways that fit the duration; represent each selected takeaway once before completing the response.",
101
84
  "For a long source, summarize instead of attempting to represent every fact, unless the creative instructions explicitly request complete fact coverage that fits the duration.",
@@ -107,7 +90,7 @@ function buildVideoUserPrompt(input, openingDurationSec = 0) {
107
90
  "After the first playable body scene, emit exactly one scene.add with placement closer. Generate a short grounded conclusion that answers the story's so-what; it must not repeat hook language or imply another scene follows.",
108
91
  "Use a different suitable template for each body scene when the catalog supports it.",
109
92
  "Never add filler to satisfy a count or diversity target.",
110
- input.suppliedMedia?.length ? "Select zero or more relevant opaque supplied-media references for visible later scenes. Never invent or transform a reference. Only emit mediaKeyword when the trusted system catalog explicitly exposes it, and only on later scenes. Never invent mediaUrl or mediaPoster." : "No supplied media URL is available. Use asset-free templates unless the trusted system catalog explicitly permits host-resolved media intent. Only emit mediaKeyword when the trusted system catalog explicitly exposes it, and only on later scenes. Never invent mediaUrl or mediaPoster.",
93
+ input.suppliedMedia?.length ? input.opening === false ? "Select zero or more relevant opaque supplied-media references for visible scenes. Never invent or transform a reference. Only emit mediaKeyword when the trusted system catalog explicitly exposes it. Never invent mediaUrl or mediaPoster." : "Select zero or more relevant opaque supplied-media references for visible later scenes. Never invent or transform a reference. Only emit mediaKeyword when the trusted system catalog explicitly exposes it, and only on later scenes. Never invent mediaUrl or mediaPoster." : input.opening === false ? "No supplied media URL is available. Use asset-free templates unless the trusted system catalog explicitly permits host-resolved media intent, which may begin on the first scene. Only emit mediaKeyword when the trusted system catalog explicitly exposes it. Never invent mediaUrl or mediaPoster." : "No supplied media URL is available. Use asset-free templates unless the trusted system catalog explicitly permits host-resolved media intent. Only emit mediaKeyword when the trusted system catalog explicitly exposes it, and only on later scenes. Never invent mediaUrl or mediaPoster.",
111
94
  "",
112
95
  "RAW INPUT",
113
96
  input.input.trim(),
@@ -225,7 +208,8 @@ function buildInitialComposition(input, audio, snapshotRetention, closerReserveS
225
208
  motion: input.style?.motion ?? "normal",
226
209
  defaultBackgroundEffect: input.style?.backgroundEffect ?? "static",
227
210
  defaultTextArchetype: input.style?.textArchetype ?? "subtle",
228
- defaultTransition: "crossfade"
211
+ defaultTransition: "crossfade",
212
+ ...input.style?.generatedLook ? { generatedLook: input.style.generatedLook } : {}
229
213
  },
230
214
  meta
231
215
  },
@@ -670,7 +654,7 @@ function createVideo(rawInput, options) {
670
654
  position
671
655
  }));
672
656
  }
673
- const systemPrompt = options.systemPrompt ?? (await import("./system-prompt-4I6Z5HK3.js")).createVideoSystemPrompt(input.knowledgeMode);
657
+ const systemPrompt = options.systemPrompt ?? (await import("./system-prompt-RRXIWDDD.js")).createVideoSystemPrompt(input.knowledgeMode);
674
658
  const context = { request, systemPrompt, userPrompt, initialConfig, signal: controller.signal };
675
659
  attachGenerationLifecycleSink(context, lifecycle);
676
660
  for await (const untrustedPart of options.generate(context)) {
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  BUILTIN_TEMPLATE_CATALOG
3
- } from "./chunk-3RV4YKB3.js";
3
+ } from "./chunk-JNN3EYVP.js";
4
4
  import {
5
5
  assertTemplateTransitionMetadata
6
6
  } from "./chunk-2E6T633S.js";
@@ -0,0 +1,142 @@
1
+ import {
2
+ checksumVideo,
3
+ createVideoEventFactory
4
+ } from "./chunk-2PYO6VAC.js";
5
+ import {
6
+ VIDEO_SCHEMA_VERSION
7
+ } from "./chunk-AOWSXU2K.js";
8
+ import {
9
+ getReadableSceneDuration
10
+ } from "./chunk-3O7OMMMF.js";
11
+
12
+ // src/protocol/scene-timeline.ts
13
+ function createQueue() {
14
+ const buffered = [];
15
+ const waiting = [];
16
+ let closed = false;
17
+ return {
18
+ push(value) {
19
+ const next = waiting.shift();
20
+ if (next) next({ value, done: false });
21
+ else buffered.push(value);
22
+ },
23
+ close() {
24
+ closed = true;
25
+ let next = waiting.shift();
26
+ while (next) {
27
+ next({ value: void 0, done: true });
28
+ next = waiting.shift();
29
+ }
30
+ },
31
+ iterable: {
32
+ [Symbol.asyncIterator]() {
33
+ return {
34
+ next() {
35
+ const buffer = buffered.shift();
36
+ if (buffer !== void 0) return Promise.resolve({ value: buffer, done: false });
37
+ if (closed) return Promise.resolve({ value: void 0, done: true });
38
+ return new Promise((resolve) => waiting.push(resolve));
39
+ }
40
+ };
41
+ }
42
+ }
43
+ };
44
+ }
45
+ var runCounter = 0;
46
+ function createSceneTimeline(options) {
47
+ const queue = createQueue();
48
+ const runId = options.runId ?? `composed-${runCounter += 1}`;
49
+ const events = createVideoEventFactory({ runId });
50
+ const orientation = options.orientation ?? "landscape";
51
+ const snapshot = {
52
+ schemaVersion: VIDEO_SCHEMA_VERSION,
53
+ orientation,
54
+ scenes: [],
55
+ style: options.style
56
+ };
57
+ let position = 0;
58
+ let finished = false;
59
+ const held = [];
60
+ let openForScenes = !options.awaitAudio;
61
+ const emit = (event) => {
62
+ if (finished) return;
63
+ queue.push(event);
64
+ };
65
+ emit(events.create("response.start", {
66
+ requestId: options.requestId ?? runId,
67
+ format: { orientation },
68
+ style: options.style
69
+ }));
70
+ if (options.audio) {
71
+ snapshot.audio = options.audio;
72
+ emit(events.create("audio.set", { audio: options.audio }));
73
+ }
74
+ const release = () => {
75
+ const pending = held.splice(0, held.length);
76
+ for (const scene of pending) {
77
+ snapshot.scenes.push(scene);
78
+ emit(events.create("scene.add", { scene, position: position++ }));
79
+ }
80
+ };
81
+ return {
82
+ stream: queue.iterable,
83
+ add(scene) {
84
+ if (finished) return;
85
+ if (!openForScenes) {
86
+ held.push(scene);
87
+ return;
88
+ }
89
+ snapshot.scenes.push(scene);
90
+ emit(events.create("scene.add", { scene, position: position++ }));
91
+ },
92
+ setAudio(audio) {
93
+ if (finished || openForScenes) return;
94
+ if (audio) {
95
+ snapshot.audio = audio;
96
+ emit(events.create("audio.set", { audio }));
97
+ }
98
+ openForScenes = true;
99
+ release();
100
+ },
101
+ complete() {
102
+ if (finished) return;
103
+ if (!openForScenes) {
104
+ openForScenes = true;
105
+ release();
106
+ }
107
+ emit(events.create("response.complete", {
108
+ finishReason: "stop",
109
+ snapshot,
110
+ checksum: checksumVideo(snapshot)
111
+ }));
112
+ finished = true;
113
+ queue.close();
114
+ }
115
+ };
116
+ }
117
+
118
+ // src/protocol/scene-duration.ts
119
+ var NARRATION_WORDS_PER_SECOND = 2.6;
120
+ var NARRATION_TAIL_SECONDS = 0.6;
121
+ function getSceneDurationBounds(scene, metadata) {
122
+ return {
123
+ minimum: Math.max(1e-3, metadata?.minDuration ?? 1),
124
+ readable: getReadableSceneDuration(scene, metadata)
125
+ };
126
+ }
127
+ function getSpokenDuration(narration) {
128
+ const words = narration.trim().split(/\s+/u).filter(Boolean).length;
129
+ if (words === 0) return 0;
130
+ return words / NARRATION_WORDS_PER_SECOND + NARRATION_TAIL_SECONDS;
131
+ }
132
+ function getSceneDuration(scene, metadata) {
133
+ const { readable } = getSceneDurationBounds(scene, metadata);
134
+ return Math.max(readable, scene.narration ? getSpokenDuration(scene.narration) : 0);
135
+ }
136
+
137
+ export {
138
+ createSceneTimeline,
139
+ getSceneDurationBounds,
140
+ getSpokenDuration,
141
+ getSceneDuration
142
+ };
@@ -572,6 +572,7 @@ var TemplateText = ({
572
572
  } : baseTypo;
573
573
  const presetWeight = tt ? Math.min(900, Math.max(100, fontWeight + tt.weightDelta)) : fontWeight;
574
574
  const presetSize = tt ? fontSize * tt.sizeScale : fontSize;
575
+ const overMediaColor = overMedia && isLikelyDark(color) ? "#FFFFFF" : color;
575
576
  const textShadow = overMedia ? MEDIA_TEXT_SHADOW : dropShadowFor(color);
576
577
  const containerStyle = {
577
578
  position: "absolute",
@@ -580,7 +581,7 @@ var TemplateText = ({
580
581
  display: "flex",
581
582
  justifyContent: "center",
582
583
  padding: `0 ${safeZone.right}px 0 ${safeZone.left}px`,
583
- color,
584
+ color: overMediaColor,
584
585
  fontFamily: font,
585
586
  fontWeight: presetWeight,
586
587
  fontSize: presetSize,