@vanillaskyai/video 0.8.8 → 0.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (173) hide show
  1. package/CHANGELOG.md +42 -0
  2. package/PUBLIC-API.md +5 -8
  3. package/dist/{builtin-server-CUYPIDE7.js → builtin-server-W4PLJUZ5.js} +2 -2
  4. package/dist/{catalog-types-BIhSpOWK.d.ts → catalog-types-WTbLP6Jh.d.ts} +5 -1
  5. package/dist/chapter-title-YRLIQY3V.js +7 -0
  6. package/dist/check-runtime.js +5 -14
  7. package/dist/{chunk-AITKH6QT.js → chunk-2HFO22K2.js} +15 -40
  8. package/dist/chunk-4YM2M62S.js +13 -0
  9. package/dist/chunk-4ZJLPHBV.js +684 -0
  10. package/dist/chunk-77VY4O7A.js +390 -0
  11. package/dist/{chunk-MMUXVA47.js → chunk-7M56IUUX.js} +9 -8
  12. package/dist/chunk-7NZOMTAL.js +63 -0
  13. package/dist/{chunk-2DOLF4QN.js → chunk-AUPC6MDK.js} +12 -14
  14. package/dist/{chunk-352ZZCET.js → chunk-EGVQODKU.js} +2 -2
  15. package/dist/{chunk-2USVB3CY.js → chunk-ELVOT2FQ.js} +284 -177
  16. package/dist/chunk-EWWCQFTI.js +18 -0
  17. package/dist/{chunk-KT3M7KDL.js → chunk-HKUI5BTB.js} +3 -3
  18. package/dist/chunk-IIN5M5HW.js +697 -0
  19. package/dist/{chunk-LMG3GHLL.js → chunk-IQMYK5DX.js} +29 -3
  20. package/dist/{chunk-OWBT45EN.js → chunk-IR44XKBI.js} +1 -1
  21. package/dist/{chunk-MXZSDGZQ.js → chunk-NMFDC2W2.js} +4 -6
  22. package/dist/{chunk-5SLENAJW.js → chunk-PAJ44TFW.js} +1 -2
  23. package/dist/{chunk-L7QGGKJ2.js → chunk-PDQFQIQW.js} +1 -1
  24. package/dist/{chunk-WJ6FLKNC.js → chunk-R3XAOMKP.js} +14 -0
  25. package/dist/{chunk-2OKB27TG.js → chunk-RXTN2CW6.js} +2 -2
  26. package/dist/{chunk-XWWLKRNU.js → chunk-TVIU23OM.js} +38 -49
  27. package/dist/chunk-XX4GXKTX.js +35 -0
  28. package/dist/chunk-Z67Q5KUW.js +16 -0
  29. package/dist/{chunk-3VRMKFM7.js → chunk-ZDJNFA52.js} +11 -24
  30. package/dist/cinema-media-C7OUPNSH.js +12 -0
  31. package/dist/cli.js +18 -9
  32. package/dist/comparison-GNZOR7MF.js +37 -0
  33. package/dist/compose-video-TBFTONUC.js +12 -0
  34. package/dist/editorial-timeline-EMV5335S.js +43 -0
  35. package/dist/{events-BQ6z264n.d.ts → events-B4YCc4vc.d.ts} +1 -1
  36. package/dist/index.d.ts +4 -6
  37. package/dist/index.js +3 -7
  38. package/dist/key-figure-P37VK425.js +30 -0
  39. package/dist/{kit-tQnCPMOM.d.ts → kit-DrRpdn0p.d.ts} +2 -2
  40. package/dist/mobile-message-OF7T62B7.js +50 -0
  41. package/dist/quote-T6WYLN7A.js +33 -0
  42. package/dist/react.d.ts +21 -11
  43. package/dist/react.js +585 -826
  44. package/dist/scene-video-backdrop-PCTARAAJ.js +7 -0
  45. package/dist/server.d.ts +7 -8
  46. package/dist/server.js +179 -84
  47. package/dist/{system-prompt-RRXIWDDD.js → system-prompt-SYRT5DWM.js} +1 -1
  48. package/dist/template-catalog.d.ts +392 -16
  49. package/dist/template-catalog.js +1 -1
  50. package/dist/templates.d.ts +5 -5
  51. package/dist/test.d.ts +3 -3
  52. package/dist/test.js +18 -18
  53. package/dist/text-stream-LD364KBI.js +9 -0
  54. package/dist/{types-DWFO6qca.d.ts → types-CdBmNTGD.d.ts} +1 -1
  55. package/dist/{types-CEh9eUFu.d.ts → types-DdZw4GRQ.d.ts} +17 -48
  56. package/dist/validate-4OUD2CLX.js +10 -0
  57. package/docs/architecture.md +1 -1
  58. package/docs/concepts.md +1 -1
  59. package/docs/custom-templates.md +13 -28
  60. package/docs/customization.md +11 -59
  61. package/docs/getting-started.md +2 -2
  62. package/docs/immersive-interface.md +3 -4
  63. package/docs/maintainers/cinematic-migration.md +38 -0
  64. package/docs/media-and-audio.md +14 -14
  65. package/docs/motion-and-effects.md +6 -14
  66. package/docs/persistence.md +3 -3
  67. package/docs/production.md +11 -9
  68. package/docs/provider-integration.md +9 -16
  69. package/docs/reference/design-system.html +1 -1
  70. package/docs/reference/protocol.md +14 -4
  71. package/docs/testing.md +1 -1
  72. package/package.json +6 -3
  73. package/registry/items/backgrounds.json +1 -1
  74. package/registry/items/chapterTitle.json +80 -0
  75. package/registry/items/cinemaMedia.json +136 -0
  76. package/registry/items/comparison.json +168 -0
  77. package/registry/items/editorialTimeline.json +172 -0
  78. package/registry/items/keyFigure.json +162 -0
  79. package/registry/items/mobileMessage.json +153 -0
  80. package/registry/items/quote.json +161 -0
  81. package/registry/items/template-context.json +1 -1
  82. package/registry/items/theme.json +1 -1
  83. package/starters/video-chat/README.md +24 -26
  84. package/starters/video-chat/package.json +1 -1
  85. package/starters/video-chat/providers/video.ts +6 -5
  86. package/starters/video-chat/stock.ts +35 -151
  87. package/styles/fonts/OFL.txt +93 -0
  88. package/styles/fonts/manifest.json +66 -0
  89. package/styles/fonts/roboto-v51-cyrillic-ext.woff2 +0 -0
  90. package/styles/fonts/roboto-v51-cyrillic.woff2 +0 -0
  91. package/styles/fonts/roboto-v51-greek-ext.woff2 +0 -0
  92. package/styles/fonts/roboto-v51-greek.woff2 +0 -0
  93. package/styles/fonts/roboto-v51-latin-ext.woff2 +0 -0
  94. package/styles/fonts/roboto-v51-latin.woff2 +0 -0
  95. package/styles/fonts/roboto-v51-math.woff2 +0 -0
  96. package/styles/fonts/roboto-v51-symbols.woff2 +0 -0
  97. package/styles/fonts/roboto-v51-vietnamese.woff2 +0 -0
  98. package/styles/fonts/roboto.css +91 -0
  99. package/styles/video-chat.css +30 -22
  100. package/dist/bg-confetti-HITCGLGD.js +0 -89
  101. package/dist/bg-emoji-VU4UZYZN.js +0 -150
  102. package/dist/bg-media-L34PDQXJ.js +0 -106
  103. package/dist/brand-message-FUJ4STFO.js +0 -256
  104. package/dist/chart-bar-EGPHJATL.js +0 -310
  105. package/dist/chart-counter-5FFWQNZO.js +0 -306
  106. package/dist/chart-progress-ring-JMXQLMLC.js +0 -302
  107. package/dist/chunk-4UNKZEAT.js +0 -3575
  108. package/dist/chunk-5CDAM24P.js +0 -31
  109. package/dist/chunk-AOWSXU2K.js +0 -18
  110. package/dist/chunk-CJ7F2CE6.js +0 -104
  111. package/dist/chunk-FRN6WKHA.js +0 -185
  112. package/dist/chunk-G5JPHBX7.js +0 -336
  113. package/dist/chunk-L5BTTCZV.js +0 -97
  114. package/dist/chunk-P22HHFDK.js +0 -43
  115. package/dist/chunk-PP3MCPN4.js +0 -1998
  116. package/dist/chunk-QAAO7OAY.js +0 -19
  117. package/dist/chunk-S7HFLYNN.js +0 -35
  118. package/dist/chunk-W3VDIBGR.js +0 -217
  119. package/dist/chunk-XGRA2MUV.js +0 -242
  120. package/dist/chunk-XTOXFMLM.js +0 -105
  121. package/dist/chunk-YJJC4N4D.js +0 -761
  122. package/dist/compose-video-2NFH4DZY.js +0 -13
  123. package/dist/cta-logo-YR7LYUR5.js +0 -212
  124. package/dist/cta-media-DQJBKUO2.js +0 -221
  125. package/dist/incoming-call-54KQ7O5A.js +0 -323
  126. package/dist/infographic-before-after-ROS52GOW.js +0 -460
  127. package/dist/infographic-feature-list-E7MGUDAN.js +0 -272
  128. package/dist/infographic-problem-solution-3S6NGO5N.js +0 -343
  129. package/dist/infographic-stat-row-SYB7AB7H.js +0 -247
  130. package/dist/infographic-steps-KCE6JWE2.js +0 -406
  131. package/dist/prompt-input-MHX4O42G.js +0 -239
  132. package/dist/reaction-SLEGR3BE.js +0 -122
  133. package/dist/scene-video-backdrop-XFPX4O3I.js +0 -10
  134. package/dist/showcase-code-2HLAVKBM.js +0 -357
  135. package/dist/showcase-phone-Q4HEO4LH.js +0 -278
  136. package/dist/showcase-terminal-DL45LUFG.js +0 -312
  137. package/dist/showcase-web-2QZO73FZ.js +0 -396
  138. package/dist/social-conversation-75SD2637.js +0 -695
  139. package/dist/social-milestone-GEFUQSBF.js +0 -296
  140. package/dist/social-notification-FJ5VHVLK.js +0 -247
  141. package/dist/social-review-stack-KXQCKFRP.js +0 -242
  142. package/dist/social-testimonial-JZ7CHC7S.js +0 -287
  143. package/dist/social-tweet-UCDXM25F.js +0 -298
  144. package/dist/text-stream-54LXABJW.js +0 -10
  145. package/dist/validate-T7GBU2YF.js +0 -8
  146. package/registry/items/barChart.json +0 -245
  147. package/registry/items/beforeAfter.json +0 -180
  148. package/registry/items/bigNumber.json +0 -232
  149. package/registry/items/brandMessage.json +0 -164
  150. package/registry/items/cardList.json +0 -214
  151. package/registry/items/chatMessenger.json +0 -151
  152. package/registry/items/chatWhatsapp.json +0 -151
  153. package/registry/items/codeEditor.json +0 -191
  154. package/registry/items/confetti.json +0 -107
  155. package/registry/items/ctaLogo.json +0 -167
  156. package/registry/items/ctaMedia.json +0 -200
  157. package/registry/items/emojiBurst.json +0 -101
  158. package/registry/items/incomingCall.json +0 -177
  159. package/registry/items/media.json +0 -187
  160. package/registry/items/milestone.json +0 -212
  161. package/registry/items/notification.json +0 -189
  162. package/registry/items/phoneMockup.json +0 -261
  163. package/registry/items/problemSolution.json +0 -193
  164. package/registry/items/progressRing.json +0 -206
  165. package/registry/items/promptInput.json +0 -155
  166. package/registry/items/reaction.json +0 -176
  167. package/registry/items/reviewStack.json +0 -231
  168. package/registry/items/steps.json +0 -215
  169. package/registry/items/terminal.json +0 -219
  170. package/registry/items/testimonial.json +0 -171
  171. package/registry/items/tripleStats.json +0 -235
  172. package/registry/items/tweet.json +0 -210
  173. package/registry/items/webMockup.json +0 -280
package/CHANGELOG.md CHANGED
@@ -4,6 +4,48 @@ VanillaSky follows semantic versioning. This changelog begins with the 0.1 beta.
4
4
 
5
5
  ## Unreleased
6
6
 
7
+ ## 0.10.0
8
+
9
+ ### Breaking
10
+
11
+ - Remove `focusCards` from the built-in catalog and generated source templates, as requested. Existing callers must replace it with narration over `cinemaMedia`, or an appropriate comparison/timeline when supported by their content. Persisted videos using that ID must be regenerated before playback with the new catalog.
12
+
13
+ ### Changed
14
+
15
+ - Opening narration advances into the first shot and body. Exact repeated introductory sentences are removed without additional model calls or startup waits.
16
+ - Prefer relevant footage over repeated graphic scenes; use no more than one explanatory overlay in a typical thirty-second answer. Comparison, key figure, quote and timeline can now use optional host-resolved media with a contrast scrim and soft text shadows. Black remains the fallback; chapter titles stay black.
17
+
18
+
19
+ ## 0.9.0
20
+
21
+ - Continue video playback with subtitles when native browser speech cannot start, without weakening generated-audio readiness or counting paused time toward the fallback.
22
+
23
+ - Show the spoken opening as a quiet chapter on black when opening media is absent or cannot load. Keep it readable until media or the film is ready, without another generation call.
24
+
25
+ - Package licensed, self-hosted Roboto fallback fonts for the fixed cinematic typography; remove unused promotional template primitives.
26
+
27
+
28
+ ### Breaking changes
29
+
30
+ - Replace the 28 promotional templates with eight cinematic templates: Full-bleed, Chapter, Focus cards, Timeline, Reach out, Comparison, Quote, and Key figure. Graphics use fixed black backgrounds and white/neutral system typography; only Full-bleed and Reach out accept media.
31
+ - Remove configurable `brand` input and persisted `style.brand`, and remove the public `VideoBrand`, `VideoBrandInput`, `VideoBackground`, and `resolveVideoBrand` exports. Persisted schema is now `0.2` and streaming protocol is `0.6`; older payloads are explicitly rejected.
32
+ - Source-owned templates declare authored reveal, hold and exit minimums. Quote and figure examples remain authoring fixtures and never become factual runtime defaults.
33
+
34
+ Before:
35
+
36
+ ```tsx
37
+ <VideoChat options={{ endpoint: "/api/video-chat", brand: { background: "twilight" } }} />
38
+ ```
39
+
40
+ ### Adoption
41
+
42
+ Remove brand options and regenerate source-owned templates from the new catalog. Re-author or regenerate saved videos from retained source material; do not rename old IDs or change their version field blindly. See [cinematic migration](docs/maintainers/cinematic-migration.md).
43
+
44
+ ```tsx
45
+ <VideoChat options={{ endpoint: "/api/video-chat" }} />
46
+ ```
47
+
48
+
7
49
  ## 0.8.8
8
50
 
9
51
  - Use equal spacing between Sessions, Settings, and Voice controls on mobile and desktop.
package/PUBLIC-API.md CHANGED
@@ -131,9 +131,9 @@ playable scenes on an interrupted plan, emitting non-fatal warnings. Explicit
131
131
  `invalidPartBehavior: "fail"` retains strict generation semantics.
132
132
 
133
133
  - A response accepts `prompt`, `mode`, `orientation`, optional bounded
134
- `conversation`, `opening`, `brand`, and `style`. `opening` is an optional
134
+ `conversation`, `opening`, and `style`. `opening` is an optional
135
135
  prewritten hook from a selected suggestion. The response returns protocol
136
- `0.5` SSE and negotiates `data.video-chat-opening`, which carries the bounded
136
+ `0.6` SSE and negotiates `data.video-chat-opening`, which carries the bounded
137
137
  6-9 word hook and optional stock-search `keyword` and `fallbackKeyword` before the first scene.
138
138
  - The planner produces that opening as the first line of the same model stream
139
139
  that produces the scenes. The separate opening-media action resolves its
@@ -216,7 +216,7 @@ export function App() {
216
216
  ```
217
217
 
218
218
  Pass the same session configuration through `options`, for example
219
- `<VideoChat options={{ endpoint, headers, templates, brand }} />`. `className`
219
+ `<VideoChat options={{ endpoint, headers, templates }} />`. `className`
220
220
  and `welcomeTitle` are the only shell-level customizations. The stylesheet is
221
221
  scoped under `.vanillasky-video-chat` and does not style the host document.
222
222
 
@@ -294,10 +294,7 @@ playback is unaffected by either. `onComplete(video)` reports that a streamed
294
294
  response finished composing; `onPlaybackEnd(video)` reports that the visible
295
295
  playhead actually reached the end for either a stream or saved replay.
296
296
 
297
- `resolveVideoBrand` fills a partial brand with the documented defaults and
298
- preset backgrounds, producing the fully resolved `VideoBrand` that `parseVideo`
299
- requires. Use it when authoring a `Video` by hand rather than copying default
300
- values into application code.
297
+ Graphic scenes use fixed black backgrounds, white/neutral typography, and the system font stack. `VideoInput.brand`, `VideoStyle.brand`, `VideoBrand`, `VideoBrandInput`, `VideoBackground`, and `resolveVideoBrand` are removed. Media retains natural color. Persisted schema `0.2` and event protocol `0.6` reject earlier versions; changing a version field alone is not a migration.
301
298
 
302
299
  ## Template authoring
303
300
 
@@ -394,7 +391,7 @@ from `/server` without crossing a React type boundary.
394
391
 
395
392
  - A completed `Video` is JSON-serializable and may be stored by the host.
396
393
  - Every completed value carries the required storage field
397
- `schemaVersion: "0.1"`; it is independent from streaming protocol `0.5`.
394
+ `schemaVersion: "0.2"`; it is independent from streaming protocol `0.6`.
398
395
  - `parseVideo(value: unknown)` is the strict universal storage boundary. It
399
396
  validates the full document and returns a detached, deeply frozen `Video`.
400
397
  - JSON serialization remains platform-native; the SDK has no redundant public
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  BUILTIN_SERVER_TEMPLATE_KIT
3
- } from "./chunk-OWBT45EN.js";
4
- import "./chunk-4UNKZEAT.js";
3
+ } from "./chunk-IR44XKBI.js";
4
+ import "./chunk-IIN5M5HW.js";
5
5
  import "./chunk-2E6T633S.js";
6
6
  export {
7
7
  BUILTIN_SERVER_TEMPLATE_KIT
@@ -36,6 +36,10 @@ type TemplateFamily = "Media & motion" | "Data & metrics" | "Product showcase" |
36
36
  interface TemplateTimingMetadata {
37
37
  /** Schema fields whose rendered content may inform a future adaptive duration. */
38
38
  readonly contentFields: readonly string[];
39
+ /** Authored motion and uninterrupted reading minimums in seconds. */
40
+ readonly revealSeconds?: number;
41
+ readonly holdSeconds?: number;
42
+ readonly exitSeconds?: number;
39
43
  /** How content in contentFields should be measured; no runtime behavior is implied. */
40
44
  readonly contentUnit: "words" | "characters" | "items";
41
45
  }
@@ -71,4 +75,4 @@ interface SceneTemplateMetadata {
71
75
  timing?: TemplateTimingMetadata;
72
76
  }
73
77
 
74
- export type { SceneTemplateMetadata as S, TemplateFamily as T, TemplateJob as a, TemplateRegister as b, TemplateTimingMetadata as c, TemplateJsonSchema as d, TemplateTransitionTiming as e, TemplateJsonSchemaProperty as f };
78
+ export type { SceneTemplateMetadata as S, TemplateJsonSchema as T, TemplateFamily as a, TemplateTimingMetadata as b, TemplateTransitionTiming as c, TemplateJsonSchemaProperty as d };
@@ -0,0 +1,7 @@
1
+ import {
2
+ TitleSceneTemplate
3
+ } from "./chunk-EWWCQFTI.js";
4
+ import "./chunk-4YM2M62S.js";
5
+ export {
6
+ TitleSceneTemplate
7
+ };
@@ -1,14 +1,10 @@
1
1
  import {
2
2
  VideoFrame
3
- } from "./chunk-2USVB3CY.js";
3
+ } from "./chunk-ELVOT2FQ.js";
4
4
  import "./chunk-224QNWRA.js";
5
- import "./chunk-5CDAM24P.js";
6
- import "./chunk-XWWLKRNU.js";
5
+ import "./chunk-TVIU23OM.js";
7
6
  import "./chunk-SPVTJH3F.js";
8
- import {
9
- resolveVideoBrand
10
- } from "./chunk-XGRA2MUV.js";
11
- import "./chunk-WJ6FLKNC.js";
7
+ import "./chunk-R3XAOMKP.js";
12
8
  import "./chunk-73NTSFFI.js";
13
9
 
14
10
  // src/cli/check-runtime.ts
@@ -48,7 +44,7 @@ async function renderTemplateChecks(input) {
48
44
  for (const orientation of ["portrait", "landscape"]) {
49
45
  const [width, height] = orientation === "portrait" ? [1080, 1920] : [1920, 1080];
50
46
  const config = {
51
- schemaVersion: "0.1",
47
+ schemaVersion: "0.2",
52
48
  orientation,
53
49
  scenes: [{
54
50
  id: "authoring-check",
@@ -56,12 +52,7 @@ async function renderTemplateChecks(input) {
56
52
  variables: input.variables,
57
53
  timing: { startTime: 0, endTime: input.duration, fixedDuration: input.duration }
58
54
  }],
59
- style: {
60
- brand: resolveVideoBrand({
61
- font: "Inter, sans-serif",
62
- colors: { primary: "#6D5EF5", secondary: "#17122F", foreground: "#FFFFFF" }
63
- })
64
- }
55
+ style: {}
65
56
  };
66
57
  for (const progress of progressPoints) {
67
58
  let first;
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  createVideoSystemPrompt
3
- } from "./chunk-5SLENAJW.js";
3
+ } from "./chunk-PAJ44TFW.js";
4
4
  import {
5
5
  PACING_PLANNER_RULES
6
6
  } from "./chunk-3O7OMMMF.js";
@@ -15,7 +15,7 @@ function getStandardMediaResolverContract(schema) {
15
15
  const url = schema.properties.mediaUrl;
16
16
  const type = schema.properties.mediaType;
17
17
  const allowedTypes = new Set(type?.enum ?? []);
18
- if (keyword?.format !== "stock-media-keyword" || url?.format !== "uri" || !["photo", "video", "gradient"].every((value) => allowedTypes.has(value))) {
18
+ if (keyword?.format !== "stock-media-keyword" || url?.format !== "uri" || !["photo", "video"].every((value) => allowedTypes.has(value))) {
19
19
  return void 0;
20
20
  }
21
21
  return {
@@ -26,6 +26,8 @@ function getStandardMediaResolverContract(schema) {
26
26
  // src/visual-system/catalog/prompt.ts
27
27
  function plannerProperty(property) {
28
28
  return {
29
+ ...property.description == null ? {} : { description: property.description },
30
+ ...property.examples == null ? {} : { examples: property.examples.slice(0, 1) },
29
31
  ...property.type == null ? {} : { type: property.type },
30
32
  ...property.format == null ? {} : { format: property.format },
31
33
  ...property.enum == null ? {} : { enum: property.enum },
@@ -60,10 +62,6 @@ function plannerSchema(schema, exposeMediaKeyword) {
60
62
  }
61
63
  };
62
64
  }
63
- function needsPlannerSchema(schema) {
64
- const visit = (property) => property.minimum != null || property.maximum != null || property.type === "object" || property.items != null && visit(property.items);
65
- return Object.values(schema.properties).some(visit);
66
- }
67
65
  function isMediaProperty(property) {
68
66
  return property?.format === "uri" || property?.format === "supplied-image" || property?.format === "stock-media-keyword";
69
67
  }
@@ -71,13 +69,6 @@ function requiresAvailableMedia(template) {
71
69
  const gates = getTemplateSchemaGates(template.schema);
72
70
  return (template.schema.required ?? []).some((name) => isMediaProperty(template.schema.properties[name])) || gates.requiredAnyOf.some((group) => group.every((name) => isMediaProperty(template.schema.properties[name])));
73
71
  }
74
- var COMMON_MEDIA_VARIABLES = {
75
- mediaUrl: "media",
76
- mediaType: "enum(auto|photo|video|gradient)",
77
- mediaPoster: "media",
78
- mediaPosition: "enum(center|top|bottom|left|right)",
79
- mediaTreatment: "enum(none|subtle|cinematic|text-safe)"
80
- };
81
72
  function plannerCatalog(templates, mediaResolverAvailable) {
82
73
  return templates.map((template) => {
83
74
  const gates = getTemplateSchemaGates(template.schema);
@@ -88,23 +79,19 @@ function plannerCatalog(templates, mediaResolverAvailable) {
88
79
  name === "mediaKeyword" ? "string{2..80}" : templateVariableNotation(property, template.schema.required?.includes(name) === true)
89
80
  ])
90
81
  );
91
- const usesCommonMedia = Object.entries(COMMON_MEDIA_VARIABLES).every(([name, notation]) => variables[name] === notation);
92
- if (usesCommonMedia) {
93
- for (const name of Object.keys(COMMON_MEDIA_VARIABLES)) delete variables[name];
94
- }
95
82
  return {
96
83
  id: template.id,
97
84
  jobs: template.jobs,
98
85
  use: template.useWhen ?? template.description,
86
+ ...template.avoidWhen ? { avoid: template.avoidWhen } : {},
99
87
  ...template.minDuration == null && template.preferredDuration == null ? {} : {
100
88
  seconds: [template.minDuration ?? null, template.preferredDuration ?? null]
101
89
  },
102
90
  ...gates.requiresStat ? { requiresStat: true } : {},
103
91
  ...gates.requiresQuote ? { requiresQuote: true } : {},
104
92
  ...gates.requiresScreenshot ? { requiresScreenshot: true } : {},
105
- ...gates.requiredAnyOf.length > 0 ? { requiredAnyOf: gates.requiredAnyOf } : {},
106
- ...needsPlannerSchema(template.schema) ? { schema: plannerSchema(template.schema, exposeMediaKeyword) } : {},
107
- ...usesCommonMedia ? { media: true } : {},
93
+ ...gates.requiredAnyOf.length > 0 ? { requiredAnyOf: gates.requiredAnyOf.map((group) => group.filter((name) => name !== "mediaKeyword" || exposeMediaKeyword)) } : {},
94
+ schema: plannerSchema(template.schema, exposeMediaKeyword),
108
95
  variables
109
96
  };
110
97
  });
@@ -112,24 +99,14 @@ function plannerCatalog(templates, mediaResolverAvailable) {
112
99
  function createTemplateSystemPrompt(options) {
113
100
  const templates = options.kit.listTemplateMetadata().filter((template) => !requiresAvailableMedia(template) || options.suppliedMediaAvailable === true || options.mediaResolverAvailable === true && getStandardMediaResolverContract(template.schema) != null);
114
101
  const resolverMediaAvailable = options.mediaResolverAvailable === true && templates.some(({ schema }) => getStandardMediaResolverContract(schema) != null);
115
- const ids = new Set(templates.map(({ id }) => id));
116
- const fields = new Set(templates.flatMap(({ schema }) => Object.keys(schema.properties)));
117
102
  const basePrompt = createVideoSystemPrompt(options.knowledgeMode, options.narrate === true).split("\n").filter(
118
103
  (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
104
  // pair as the one below it: both exist so playback can start without
120
105
  // waiting on a provider, and both go when the host owns that wait.
121
106
  !(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"))
122
107
  ).join("\n");
123
- const factRules = [
124
- ...ids.has("cardList") ? ["cardList needs two or three unused parallel facts"] : [],
125
- ...ids.has("steps") ? ["steps needs two or three unused sequential facts"] : [],
126
- ...ids.has("tripleStats") ? ["tripleStats needs exactly three unused peer values"] : []
127
- ];
128
- const mediaTemplates = ["media", "ctaMedia", "reaction"].filter((id) => ids.has(id));
129
- const mediaTemplateNames = mediaTemplates.length === 3 ? "media, ctaMedia, and reaction" : mediaTemplates.join(", ");
130
- const terminalPayoffs = ["media", "emojiBurst", "confetti"].filter((id) => ids.has(id));
131
- const terminalPayoffNames = terminalPayoffs.length < 2 ? terminalPayoffs.join("") : `${terminalPayoffs.slice(0, -1).join(", ")}, or ${terminalPayoffs.at(-1)}`;
132
- const listFields = ["items", "itemEmojis", "steps", "stepEmojis", "problemEmojis", "solutionEmojis"].filter((name) => fields.has(name));
108
+ const mediaTemplates = templates.filter(({ schema }) => getStandardMediaResolverContract(schema) != null).map(({ id }) => id);
109
+ const terminalPayoffs = templates.filter(({ jobs }) => jobs?.includes("payoff")).map(({ id }) => id);
133
110
  return [
134
111
  basePrompt.trim(),
135
112
  options.basePrompt?.trim() ? `
@@ -139,20 +116,18 @@ ${options.basePrompt.trim()}` : void 0,
139
116
  "TRUSTED TEMPLATE CATALOG",
140
117
  "Only use template IDs from this catalog. Only emit variables declared for the selected template.",
141
118
  "Variable notation is type[count]{characters}(options)! where count is list cardinality, characters is the inclusive character count for a string or each string-array item, and ! means required. Omitted ! means optional.",
142
- `seconds is [minimum, preferred]. media=true adds these optional variables: ${Object.entries(COMMON_MEDIA_VARIABLES).map(([name, notation]) => `${name}:${notation}`).join(", ")}.`,
143
119
  "Choose a template only when the permitted factual basis contains every fact it needs. Never invent peer values to complete a chart, comparison, stat set, timeline, or list.",
144
120
  ...PACING_PLANNER_RULES,
145
- ids.has("cardList") && ids.has("steps") && ids.has("tripleStats") ? "Fact availability carries across templates: cardList and steps each need two or three unused facts (parallel for cardList, sequential for steps) and must carry only the count the evidence supports, and tripleStats needs exactly three unused peer values. Never invent a fact to fill a collection, and do not fill one with facts already shown in another scene." : factRules.length > 0 ? `Fact availability carries across templates: ${factRules.join(", and ")}. Use only the count the evidence supports. Never invent a fact to fill a collection, and do not fill one with facts already shown in another scene.` : void 0,
146
- "Do not compress a list, sequence, metric set, or comparison into a general-purpose prose field when a specific catalog template can show that structure.",
121
+ "Let narration carry ordinary lists. Use a structured overlay only when a comparison, sequence, exact quote or key figure adds understanding beyond the voice. Prefer relevant full-bleed footage for the rest of the explanation.",
147
122
  "If the catalog includes a suitable ask template and the input supplies a grounded CTA or URL, keep that concise action closer as its own final scene instead of folding it into preceding content.",
148
123
  "When a grounded CTA or URL is supplied and the catalog contains jobs:[ask], emit that final closer. A brand name may accompany the action but never qualifies as an ask by itself.",
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,
124
+ terminalPayoffs.length > 0 ? `When there is no grounded action, end with a concise supported payoff using a suitable template from ${terminalPayoffs.join(", ")}. Do not repeat the hook or invent a CTA.` : void 0,
150
125
  "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.",
151
126
  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,
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,
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,
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,
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.",
127
+ mediaTemplates.length > 0 && options.mediaOnFirstScene !== true ? `The host owns immediate startup; reserve external media templates (${mediaTemplates.join(", ")}) for later beats when an asset-free opening is required.` : void 0,
128
+ "For list and event variables emit actual JSON arrays. Follow each field's content budget; never use pipe-delimited strings or invent entries.",
129
+ resolverMediaAvailable ? "Media-capable scenes may carry mediaKeyword: a literal 2\u20138 word shot description, at most 80 characters. Choose mediaSource=generate for distinctive illustrative shots and mediaSource=stock for common subjects a verified asset can depict. For generated footage, optional shotDirection gives action, framing and continuity separately from the stock query. Keep people, actions and essential details precise. Never broaden an exact identity or action into unrelated atmosphere. Supply a grounded fallbackText where declared. Only the host fills mediaUrl and mediaPoster; never invent them. If no media fits an abstract beat, choose an appropriate graphic template instead." : "Use media only when a supplied asset satisfies the scene. Never invent asset URLs. Otherwise choose a grounded graphic template.",
130
+ "When the catalog offers media, make relevant footage the default. For a typical 30-second answer use at most one structured explanatory overlay plus an optional brief chapter opening. Do not chain graphic-only beats when honest media is available. Reuse suitable supplied media behind overlays with optional media fields; retain black when no relevant asset exists. Never invent a stock match or spend beyond host generation limits.",
156
131
  "Catalog guidance describes composition; it is not a factual source and must never replace the permitted factual basis.",
157
132
  JSON.stringify(plannerCatalog(templates, resolverMediaAvailable)),
158
133
  // Last, because the catalogue is thousands of tokens of per-scene field
@@ -0,0 +1,13 @@
1
+ // src/visual-system/scene-templates/editorial-typography.ts
2
+ var editorialFont = '-apple-system, BlinkMacSystemFont, "Helvetica Neue", Roboto, Arial, sans-serif';
3
+ var fade = (value) => {
4
+ const t = Math.max(0, Math.min(1, value));
5
+ return t * t * (3 - 2 * t);
6
+ };
7
+ var editorialLabel = (unit) => ({ fontSize: unit * 0.042, fontWeight: 400, color: "#b7b7bc", lineHeight: 1.25 });
8
+
9
+ export {
10
+ editorialFont,
11
+ fade,
12
+ editorialLabel
13
+ };