@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
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  validateTemplateSchemaValue
3
- } from "./chunk-352ZZCET.js";
3
+ } from "./chunk-EGVQODKU.js";
4
4
  import {
5
5
  getTemplateSchemaGates,
6
6
  isMeaningfullyPresentTemplateValue
@@ -29,7 +29,7 @@ function schemaValues(schema, variables) {
29
29
  }
30
30
  return values;
31
31
  }
32
- function validateSchema(schema, value) {
32
+ function validateTemplateSceneStructure(schema, value, deferMediaUrls = false) {
33
33
  if (!value || typeof value !== "object" || Array.isArray(value)) {
34
34
  throw new Error("Template variable variables must be object");
35
35
  }
@@ -45,6 +45,7 @@ function validateSchema(schema, value) {
45
45
  }
46
46
  for (const [name, property] of Object.entries(schema.properties)) {
47
47
  if (Object.hasOwn(object, name)) validateTemplateSchemaValue(property, object[name], name, {
48
+ deferMediaUrls,
48
49
  errorPrefix: "Template variable ",
49
50
  includeTypeArticle: true,
50
51
  allowEmptyOptionalMedia: property.default === "",
@@ -66,6 +67,24 @@ function suppliedUrls(input) {
66
67
  }
67
68
  return urls;
68
69
  }
70
+ function containsSuppliedQuantity(input, value) {
71
+ if (typeof value !== "string" && typeof value !== "number") return false;
72
+ const normalize = (text) => text.normalize("NFKC").replace(/\u2212/g, "-").replace(/\s+/gu, " ").replace(/([\p{Sc}]) (?=[+−-]?\d)/gu, "$1").replace(/(\d) (?=[%‰])/gu, "$1").trim();
73
+ const quantity = normalize(String(value));
74
+ if (!/\d/u.test(quantity)) return false;
75
+ const source = normalize(input);
76
+ let offset = source.indexOf(quantity);
77
+ while (offset !== -1) {
78
+ const before = source.slice(0, offset);
79
+ const after = source.slice(offset + quantity.length);
80
+ const beginsInside = /[\p{L}\p{N}\p{Sc}%‰+−-]$/u.test(before) || /\d[.,]$/u.test(before);
81
+ const endsInside = /^[\p{L}\p{N}\p{Sc}%‰]/u.test(after) || /^[.,]\d/u.test(after);
82
+ const dropsUnit = /\d$/u.test(quantity) && /^ (?:%|‰|percent\b|per cent\b|thousand\b|million\b|billion\b|trillion\b|usd\b|eur\b|gbp\b|dollars?\b|euros?\b|pounds?\b)/iu.test(after);
83
+ if (!beginsInside && !endsInside && !dropsUnit) return true;
84
+ offset = source.indexOf(quantity, offset + 1);
85
+ }
86
+ return false;
87
+ }
69
88
  function createTemplateSceneValidator(options) {
70
89
  return (scene, context) => {
71
90
  const template = options.kit.getTemplateMetadata(scene.templateId);
@@ -73,7 +92,7 @@ function createTemplateSceneValidator(options) {
73
92
  const schema = template.schema;
74
93
  const gates = getTemplateSchemaGates(schema);
75
94
  validateConditionalPresence(scene.templateId, gates.requiredAnyOf, scene.variables);
76
- validateSchema(schema, scene.variables);
95
+ validateTemplateSceneStructure(schema, scene.variables);
77
96
  const values = schemaValues(schema, scene.variables);
78
97
  const allowedUrls = suppliedUrls(context.input);
79
98
  const suppliedImageUrls = new Set((context.input.suppliedMedia ?? []).filter(({ type }) => type === "image").map(({ url }) => url));
@@ -93,6 +112,12 @@ function createTemplateSceneValidator(options) {
93
112
  });
94
113
  if (!permitted) throw new Error(`Template variable ${path} must use supplied media`);
95
114
  }
115
+ if (gates.requiresStat) {
116
+ const quantities = values.filter(({ property }) => property.format === "grounded-stat");
117
+ if (!quantities.length || quantities.some(({ value }) => !containsSuppliedQuantity(context.input.input, value))) {
118
+ throw new Error(`Template ${scene.templateId} requires an exact supplied quantity, preserving its unit`);
119
+ }
120
+ }
96
121
  if (gates.requiresQuote) {
97
122
  const quoteValues = values.filter(({ property, value }) => property.format === "grounded-quote" && typeof value === "string" && value.trim()).map(({ value }) => String(value).trim());
98
123
  if (!quoteValues.length || quoteValues.some((quote) => !context.input.input.includes(quote))) {
@@ -103,5 +128,6 @@ function createTemplateSceneValidator(options) {
103
128
  }
104
129
 
105
130
  export {
131
+ validateTemplateSceneStructure,
106
132
  createTemplateSceneValidator
107
133
  };
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  BUILTIN_TEMPLATE_CATALOG
3
- } from "./chunk-4UNKZEAT.js";
3
+ } from "./chunk-IIN5M5HW.js";
4
4
  import {
5
5
  assertTemplateTransitionMetadata
6
6
  } from "./chunk-2E6T633S.js";
@@ -1,17 +1,15 @@
1
1
  import {
2
+ BrandGradientOverlay,
2
3
  SceneVideoBackdrop,
4
+ getBackgroundTransform,
3
5
  resolveMediaPosition
4
- } from "./chunk-FRN6WKHA.js";
5
- import {
6
- BrandGradientOverlay,
7
- getBackgroundTransform
8
- } from "./chunk-W3VDIBGR.js";
6
+ } from "./chunk-77VY4O7A.js";
9
7
  import {
10
8
  resolveMediaType
11
9
  } from "./chunk-224QNWRA.js";
12
10
  import {
13
11
  useExternalVideoBackdrop
14
- } from "./chunk-5CDAM24P.js";
12
+ } from "./chunk-TVIU23OM.js";
15
13
 
16
14
  // src/visual-system/scene-templates/scene-background.tsx
17
15
  import { useEffect, useState } from "react";
@@ -42,13 +42,12 @@ Composition rules:
42
42
  - Before emitting, assign each supported fact to at most one scene. The supplied opening counts: once a fact is visible, treat it as unavailable to later scenes. Finish when the supported material is covered instead of padding the response.
43
43
  - Do not infer that something is scheduled, ready, triggered, enabled, automatic, causal, or available unless the permitted factual basis supports it.
44
44
  - The first generated body scene must be asset-free and fully playable before any external media resolves.
45
- - Never use media, ctaMedia, or reaction as the first generated body template, including in gradient mode.
46
45
  - Use only media URLs present in the supplied input or already resolved by the host. Never expose a loading placeholder or unresolved media keyword. Audio is optional and must never delay the first scene.
47
46
  - Audio is selected by the host before generation. Never emit audio.
48
47
 
49
48
  Streaming rules:
50
49
  - Emit every scene once as a complete scene.add.
51
- - Emit exactly one closer immediately after the first playable body scene using placement:"closer". The runtime holds that closer and appends it last while later body scenes continue streaming.
50
+ - Emit exactly one closer using placement:"closer". It is held and appended last; preserve the intended story order.
52
51
  - Prefer resolved media on scene.add. Scenes are immutable after emission.
53
52
  - End explicitly with plan.complete. A truncated stream is never treated as complete.
54
53
  - Return only plan parts accepted by the provided schema.
@@ -6,7 +6,7 @@ import {
6
6
  } from "./chunk-E7CL7UPB.js";
7
7
  import {
8
8
  parseVideoPlanPart
9
- } from "./chunk-MMUXVA47.js";
9
+ } from "./chunk-7M56IUUX.js";
10
10
 
11
11
  // src/server/model/truncated-json.ts
12
12
  function isConfidentlyTruncatedJson(source) {
@@ -1,5 +1,18 @@
1
1
  // src/visual-system/catalog/video-backdrop-capability.ts
2
2
  var EXTERNAL_VIDEO_BACKDROP_CAPABILITY = "__vanillaskyExternalVideoBackdrop";
3
+ function markExternalVideoBackdropTemplate(template) {
4
+ const component = template.component;
5
+ if ((typeof component !== "object" || component === null) && typeof component !== "function") {
6
+ throw new Error("External video backdrop templates require a component");
7
+ }
8
+ Object.defineProperty(component, EXTERNAL_VIDEO_BACKDROP_CAPABILITY, {
9
+ value: true,
10
+ enumerable: false,
11
+ configurable: false,
12
+ writable: false
13
+ });
14
+ return template;
15
+ }
3
16
  function supportsExternalVideoBackdrop(template) {
4
17
  if (typeof template !== "object" || template === null) return false;
5
18
  const component = template.component;
@@ -10,6 +23,7 @@ function externalVideoBackdropCapabilitySource() {
10
23
  }
11
24
 
12
25
  export {
26
+ markExternalVideoBackdropTemplate,
13
27
  supportsExternalVideoBackdrop,
14
28
  externalVideoBackdropCapabilitySource
15
29
  };
@@ -1,10 +1,10 @@
1
1
  import {
2
2
  parseVideoEvent
3
- } from "./chunk-MMUXVA47.js";
3
+ } from "./chunk-7M56IUUX.js";
4
4
  import {
5
5
  VIDEO_PROTOCOL_VERSION,
6
6
  VIDEO_SCHEMA_VERSION
7
- } from "./chunk-AOWSXU2K.js";
7
+ } from "./chunk-7NZOMTAL.js";
8
8
 
9
9
  // src/protocol/stable-json.ts
10
10
  function stableJson(value) {
@@ -1,23 +1,30 @@
1
- // src/visual-system/scene-templates/tokens.ts
2
- var TOKEN_DEFAULTS = {
3
- /** Primary brand colour CTA, highlights. */
4
- primary: "#00E5A0",
5
- /** Deepest background surface. */
6
- surface: "#0A0A14",
7
- /** Elevated card / panel surface. */
8
- surfaceElevated: "#14152A",
9
- /** Primary text color. */
10
- foreground: "#FFFFFF",
11
- /** Muted text — labels, footers, supporting copy. */
12
- muted: "#A7A6B0",
13
- /** Primary sans font family (first name of the stack). */
14
- font: "Inter",
15
- /** Script accent font family for handwritten callouts. */
16
- scriptFont: "Caveat"
17
- };
18
- function fontStack(styleFont) {
19
- return `${(styleFont || "").split(",")[0].trim() || TOKEN_DEFAULTS.font}, -apple-system, BlinkMacSystemFont, "Helvetica Neue", Helvetica, Arial, sans-serif`;
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;
20
21
  }
22
+ function useMediaAudio() {
23
+ const { audioMuted, audioVolume } = React.useContext(BackdropContext);
24
+ return { muted: audioMuted, volume: audioVolume };
25
+ }
26
+
27
+ // src/visual-system/scene-templates/tokens.ts
21
28
  var STYLE_PRESETS = {
22
29
  bold: {
23
30
  id: "bold",
@@ -94,7 +101,6 @@ function resolveMotion(id) {
94
101
  return id && MOTION_SCALES[id] || MOTION_SCALES[DEFAULT_MOTION_ID];
95
102
  }
96
103
  function resolveTokens(style) {
97
- const brand = style.brand;
98
104
  const preset = resolvePreset(style.preset);
99
105
  const density = resolveDensity(style.density);
100
106
  const motion = resolveMotion(style.motion);
@@ -107,17 +113,15 @@ function resolveTokens(style) {
107
113
  }
108
114
  };
109
115
  return {
110
- primary: brand.colors.primary,
111
- secondary: brand.colors.secondary,
112
- background: brand.background,
113
- surface: brand.colors.surface,
114
- surfaceElevated: brand.colors.surfaceElevated,
115
- foreground: brand.colors.foreground,
116
- muted: brand.colors.muted,
117
- font: fontStack(brand.font),
118
- scriptFont: brand.scriptFont,
119
- logoUrl: brand.logoUrl,
120
- name: brand.name,
116
+ primary: "#FFFFFF",
117
+ secondary: "#FFFFFF",
118
+ background: { type: "solid", color: "#000000" },
119
+ surface: "#000000",
120
+ surfaceElevated: "#171717",
121
+ foreground: "#FFFFFF",
122
+ muted: "#B7B7BC",
123
+ font: '-apple-system, BlinkMacSystemFont, "Helvetica Neue", Roboto, Arial, sans-serif',
124
+ scriptFont: "Georgia",
121
125
  preset: composedPreset,
122
126
  density,
123
127
  motion
@@ -131,27 +135,12 @@ function darken(hex, factor) {
131
135
  const ch = (i) => Math.max(0, Math.round(parseInt(full.slice(i, i + 2), 16) * (1 - factor))).toString(16).padStart(2, "0");
132
136
  return `#${ch(1)}${ch(3)}${ch(5)}`;
133
137
  }
134
- function lighten(hex, factor) {
135
- if (!hex.startsWith("#") || hex.length !== 4 && hex.length !== 7) {
136
- return hex;
137
- }
138
- const full = hex.length === 4 ? `#${hex[1]}${hex[1]}${hex[2]}${hex[2]}${hex[3]}${hex[3]}` : hex;
139
- const r = parseInt(full.slice(1, 3), 16);
140
- const g = parseInt(full.slice(3, 5), 16);
141
- const b = parseInt(full.slice(5, 7), 16);
142
- const lr = Math.min(255, Math.round(r + (255 - r) * factor));
143
- const lg = Math.min(255, Math.round(g + (255 - g) * factor));
144
- const lb = Math.min(255, Math.round(b + (255 - b) * factor));
145
- return `#${lr.toString(16).padStart(2, "0")}${lg.toString(16).padStart(2, "0")}${lb.toString(16).padStart(2, "0")}`;
146
- }
147
- var MEDIA_TEXT_SHADOW = "0 2px 8px rgba(0,0,0,0.55), 0 6px 16px rgba(0,0,0,0.35)";
148
138
 
149
139
  export {
150
- TOKEN_DEFAULTS,
151
- fontStack,
152
140
  resolveDensity,
153
141
  resolveTokens,
154
142
  darken,
155
- lighten,
156
- MEDIA_TEXT_SHADOW
143
+ ExternalVideoBackdropProvider,
144
+ useExternalVideoBackdrop,
145
+ useMediaAudio
157
146
  };
@@ -0,0 +1,35 @@
1
+ import {
2
+ SceneBackground,
3
+ getMediaBackgroundProps
4
+ } from "./chunk-NMFDC2W2.js";
5
+ import {
6
+ editorialFont
7
+ } from "./chunk-4YM2M62S.js";
8
+ import {
9
+ hasSceneMedia
10
+ } from "./chunk-224QNWRA.js";
11
+
12
+ // src/visual-system/scene-templates/editorial-background.tsx
13
+ import { jsx, jsxs } from "react/jsx-runtime";
14
+ function EditorialSurface({ children, template, ...props }) {
15
+ const { variables, style, width, height, progress, sceneDuration, isPlaying } = props;
16
+ const overMedia = hasSceneMedia(variables), u = Math.min(width, height);
17
+ const surface = {
18
+ position: "absolute",
19
+ inset: 0,
20
+ overflow: "hidden",
21
+ background: overMedia ? "var(--vanillasky-template-surface, #000)" : "#000",
22
+ fontFamily: editorialFont,
23
+ color: "#fff",
24
+ // Two soft, scale-aware shadows preserve local contrast without outlining type.
25
+ textShadow: overMedia ? `0 ${u * 2e-3}px ${u * 8e-3}px rgba(0,0,0,.65), 0 ${u * 6e-3}px ${u * 0.016}px rgba(0,0,0,.4)` : void 0
26
+ };
27
+ return /* @__PURE__ */ jsxs("div", { "data-template": template, style: surface, children: [
28
+ overMedia && /* @__PURE__ */ jsx(SceneBackground, { style, width, height, progress, sceneDuration, ...getMediaBackgroundProps(variables), mediaTreatment: "cinematic", textAnchor: "center", backgroundEffect: "none", isPlaying }),
29
+ children
30
+ ] });
31
+ }
32
+
33
+ export {
34
+ EditorialSurface
35
+ };
@@ -0,0 +1,16 @@
1
+ import {
2
+ SceneBackground,
3
+ getMediaBackgroundProps
4
+ } from "./chunk-NMFDC2W2.js";
5
+
6
+ // src/visual-system/scene-templates/cinema-media.tsx
7
+ import { jsx } from "react/jsx-runtime";
8
+ function MediaScene({ variables, style, width, height, progress, sceneDuration, isPlaying }) {
9
+ return /* @__PURE__ */ jsx("div", { "data-template": "cinemaMedia", style: { width, height, position: "relative", overflow: "hidden" }, children: /* @__PURE__ */ jsx(SceneBackground, { style, width, height, progress, sceneDuration, ...getMediaBackgroundProps(variables), mediaTreatment: "none", backgroundEffect: "none", isPlaying }) });
10
+ }
11
+ var MediaSceneTemplate = MediaScene;
12
+
13
+ export {
14
+ MediaScene,
15
+ MediaSceneTemplate
16
+ };
@@ -6,30 +6,27 @@ import {
6
6
  checksumVideo,
7
7
  createVideoEventFactory,
8
8
  createVideoState
9
- } from "./chunk-2OKB27TG.js";
9
+ } from "./chunk-RXTN2CW6.js";
10
10
  import {
11
11
  parseVideoPlanPart,
12
12
  safePublicDiagnostic
13
- } from "./chunk-MMUXVA47.js";
13
+ } from "./chunk-7M56IUUX.js";
14
14
  import {
15
15
  MAX_RETAINED_INSTRUCTIONS_LENGTH,
16
16
  MAX_RETAINED_MEDIA_URLS,
17
17
  MAX_RETAINED_MEDIA_URL_LENGTH,
18
18
  MAX_RETAINED_SOURCE_LENGTH,
19
19
  parseVideo
20
- } from "./chunk-2DOLF4QN.js";
20
+ } from "./chunk-AUPC6MDK.js";
21
21
  import {
22
22
  VIDEO_SCHEMA_VERSION,
23
23
  createVideoRequest
24
- } from "./chunk-AOWSXU2K.js";
24
+ } from "./chunk-7NZOMTAL.js";
25
25
  import {
26
26
  getCloserReserve,
27
27
  getReadableSceneDuration,
28
28
  paceScene
29
29
  } from "./chunk-3O7OMMMF.js";
30
- import {
31
- resolveVideoBrand
32
- } from "./chunk-XGRA2MUV.js";
33
30
 
34
31
  // src/replayable-stream.ts
35
32
  function createReplayableStream(source, onError) {
@@ -114,7 +111,6 @@ function buildVideoUserPrompt(input, openingDurationSec = 0) {
114
111
  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.",
115
112
  ...input.opening === false ? [] : [
116
113
  "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.",
117
- "Never use media, ctaMedia, or reaction as the first generated body template, even with mediaType=gradient. Choose a non-media template first.",
118
114
  "Add that scene before resolving any stock or supplied asset. Resolve media as part of each complete later body scene."
119
115
  ],
120
116
  "Use only claims supported by the factual basis permitted by the trusted system prompt.",
@@ -125,7 +121,7 @@ function buildVideoUserPrompt(input, openingDurationSec = 0) {
125
121
  "If the creative instructions explicitly require one separate scene per named item, release, section, or list entry, do not merge, group, or omit those required items. Keep related required scenes adjacent in a coherent progression while preserving each item as its own scene.",
126
122
  "Before emitting, verify that the explicitly requested structure can fit readably within the maximum duration. If it cannot, preserve readability and the requested separation for the scenes that fit, then finish with plan.complete using finishReason length rather than silently changing the structure.",
127
123
  "For ordinary multi-fact input, form at least three distinct beats: hook, comprehension, and payoff. Use only one or two beats when the source genuinely contains no more than two independent grounded takeaways. This is a narrative guideline, never permission to repeat facts or add filler.",
128
- "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.",
124
+ "Emit exactly one final 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.",
129
125
  "Use a different suitable template for each body scene when the catalog supports it.",
130
126
  "Never add filler to satisfy a count or diversity target.",
131
127
  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.",
@@ -140,13 +136,7 @@ function buildVideoUserPrompt(input, openingDurationSec = 0) {
140
136
  JSON.stringify(input.personalization ?? {}),
141
137
  "",
142
138
  "SUPPLIED MEDIA",
143
- JSON.stringify(suppliedMediaReferences(input)),
144
- "",
145
- "BRAND",
146
- JSON.stringify({
147
- ...input.brand?.name?.trim() ? { name: input.brand.name.trim() } : {},
148
- hasLogo: Boolean(input.brand?.logoUrl?.trim())
149
- })
139
+ JSON.stringify(suppliedMediaReferences(input))
150
140
  ].join("\n");
151
141
  }
152
142
 
@@ -162,7 +152,7 @@ function resolveStreamCapabilities(capabilities, hasRuntimeOpening) {
162
152
  if (!hasRuntimeOpening || capabilities?.templates == null) return capabilities;
163
153
  return {
164
154
  ...capabilities,
165
- templates: [.../* @__PURE__ */ new Set(["media", ...capabilities.templates])]
155
+ templates: [.../* @__PURE__ */ new Set(["chapterTitle", ...capabilities.templates])]
166
156
  };
167
157
  }
168
158
  function validateVideoInput(input) {
@@ -190,16 +180,14 @@ function validateVideoInput(input) {
190
180
  throw new Error(`Video response supplied media ${index} poster URL must be at most ${MAX_RETAINED_MEDIA_URL_LENGTH} characters`);
191
181
  }
192
182
  }
193
- resolveVideoBrand(input.brand);
194
183
  }
195
184
  function buildInitialComposition(input, audio, snapshotRetention, closerReserveSec, getTemplatePacing) {
196
185
  const openingText = typeof input.opening === "string" ? input.opening.trim() : void 0;
197
186
  const openingScenes = openingText ? [{
198
187
  id: "supplied-opening",
199
- templateId: "media",
188
+ templateId: "chapterTitle",
200
189
  variables: {
201
- texts: openingText,
202
- mediaType: "gradient"
190
+ title: openingText
203
191
  },
204
192
  timing: { fixedDuration: 3 }
205
193
  }] : [];
@@ -223,7 +211,7 @@ function buildInitialComposition(input, audio, snapshotRetention, closerReserveS
223
211
  warnings.push(...paced.warnings);
224
212
  }
225
213
  const meta = {
226
- name: input.brand?.name?.trim() || "Video response"
214
+ name: "Video response"
227
215
  };
228
216
  if (snapshotRetention?.source) {
229
217
  meta.source = input.input.trim().slice(0, MAX_RETAINED_SOURCE_LENGTH);
@@ -241,7 +229,6 @@ function buildInitialComposition(input, audio, snapshotRetention, closerReserveS
241
229
  scenes,
242
230
  ...audio ? { audio } : {},
243
231
  style: {
244
- brand: resolveVideoBrand(input.brand),
245
232
  density: input.style?.density ?? "normal",
246
233
  motion: input.style?.motion ?? "normal",
247
234
  defaultBackgroundEffect: input.style?.backgroundEffect ?? "static",
@@ -704,7 +691,7 @@ function createVideo(rawInput, options) {
704
691
  position
705
692
  }));
706
693
  }
707
- const systemPrompt = options.systemPrompt ?? (await import("./system-prompt-RRXIWDDD.js")).createVideoSystemPrompt(input.knowledgeMode);
694
+ const systemPrompt = options.systemPrompt ?? (await import("./system-prompt-SYRT5DWM.js")).createVideoSystemPrompt(input.knowledgeMode);
708
695
  const context = { request, systemPrompt, userPrompt, initialConfig, signal: controller.signal };
709
696
  attachGenerationLifecycleSink(context, lifecycle);
710
697
  for await (const untrustedPart of options.generate(context)) {
@@ -0,0 +1,12 @@
1
+ import {
2
+ MediaScene,
3
+ MediaSceneTemplate
4
+ } from "./chunk-Z67Q5KUW.js";
5
+ import "./chunk-NMFDC2W2.js";
6
+ import "./chunk-77VY4O7A.js";
7
+ import "./chunk-224QNWRA.js";
8
+ import "./chunk-TVIU23OM.js";
9
+ export {
10
+ MediaScene,
11
+ MediaSceneTemplate
12
+ };
package/dist/cli.js CHANGED
@@ -1,24 +1,24 @@
1
1
  #!/usr/bin/env node
2
- import "./chunk-AITKH6QT.js";
3
- import "./chunk-5SLENAJW.js";
2
+ import "./chunk-2HFO22K2.js";
3
+ import "./chunk-PAJ44TFW.js";
4
4
  import {
5
5
  isTemplatePropertyFormatSupported,
6
6
  validateTemplateSchemaValue
7
- } from "./chunk-352ZZCET.js";
7
+ } from "./chunk-EGVQODKU.js";
8
8
  import {
9
9
  getBuiltinTemplateMetadata,
10
10
  listBuiltinTemplateMetadata
11
- } from "./chunk-4UNKZEAT.js";
11
+ } from "./chunk-IIN5M5HW.js";
12
12
  import "./chunk-3O7OMMMF.js";
13
13
  import {
14
14
  assertTemplateTransitionMetadata
15
15
  } from "./chunk-2E6T633S.js";
16
16
  import {
17
17
  BUILTIN_TEMPLATE_MANIFEST
18
- } from "./chunk-PP3MCPN4.js";
18
+ } from "./chunk-4ZJLPHBV.js";
19
19
  import {
20
20
  externalVideoBackdropCapabilitySource
21
- } from "./chunk-WJ6FLKNC.js";
21
+ } from "./chunk-R3XAOMKP.js";
22
22
  import {
23
23
  getTemplateDefaults,
24
24
  isMeaningfullyPresentTemplateValue,
@@ -1006,8 +1006,12 @@ function validateDiscoveredTemplate(template, cwd) {
1006
1006
  validateSchema(metadata.schema);
1007
1007
  if (metadata.timing !== void 0) {
1008
1008
  if (!metadata.timing || typeof metadata.timing !== "object" || Array.isArray(metadata.timing)) throw new Error("timing must be an object");
1009
- for (const key of Object.keys(metadata.timing)) if (!(/* @__PURE__ */ new Set(["contentFields", "contentUnit"])).has(key)) throw new Error(`timing.${key} is not supported`);
1010
- if (!Array.isArray(metadata.timing.contentFields) || metadata.timing.contentFields.length === 0) throw new Error("timing.contentFields must contain at least one schema property");
1009
+ for (const key of Object.keys(metadata.timing)) if (!(/* @__PURE__ */ new Set(["contentFields", "contentUnit", "revealSeconds", "holdSeconds", "exitSeconds"])).has(key)) throw new Error(`timing.${key} is not supported`);
1010
+ if (!Array.isArray(metadata.timing.contentFields)) throw new Error("timing.contentFields must be an array of schema properties");
1011
+ for (const key of ["revealSeconds", "holdSeconds", "exitSeconds"]) {
1012
+ const value = metadata.timing[key];
1013
+ if (value !== void 0 && (typeof value !== "number" || !Number.isFinite(value) || value < 0)) throw new Error(`timing.${key} must be a finite non-negative number`);
1014
+ }
1011
1015
  const timingFields = /* @__PURE__ */ new Set();
1012
1016
  for (const field of metadata.timing.contentFields) {
1013
1017
  if (typeof field !== "string" || !Object.hasOwn(metadata.schema.properties, field)) throw new Error(`timing.contentFields references undeclared property ${JSON.stringify(field)}`);
@@ -1017,6 +1021,11 @@ function validateDiscoveredTemplate(template, cwd) {
1017
1021
  if (!CONTENT_UNITS.has(metadata.timing.contentUnit)) throw new Error("timing.contentUnit contains an unsupported value");
1018
1022
  }
1019
1023
  const defaults = getTemplateDefaults(metadata.schema);
1024
+ const smokeVariables = { ...defaults };
1025
+ for (const field of metadata.schema.required ?? []) {
1026
+ const example = metadata.schema.properties[field]?.examples?.[0];
1027
+ if (!(field in smokeVariables) && example !== void 0) smokeVariables[field] = example;
1028
+ }
1020
1029
  if (template.examples !== void 0 && !Array.isArray(template.examples)) throw new Error("examples must be an array");
1021
1030
  const supplied = template.examples ?? [];
1022
1031
  for (const [index, value] of supplied.entries()) {
@@ -1031,7 +1040,7 @@ function validateDiscoveredTemplate(template, cwd) {
1031
1040
  name,
1032
1041
  variables: { ...defaults, ...variables },
1033
1042
  enforceRequiredAnyOf: true
1034
- })) : [{ name: "defaults", variables: defaults, enforceRequiredAnyOf: false }];
1043
+ })) : [{ name: "defaults", variables: smokeVariables, enforceRequiredAnyOf: false }];
1035
1044
  const names = /* @__PURE__ */ new Set();
1036
1045
  for (const example of examples) {
1037
1046
  if (typeof example.name !== "string" || !example.name.trim()) throw new Error("example names must be non-empty strings");
@@ -0,0 +1,37 @@
1
+ import {
2
+ EditorialSurface
3
+ } from "./chunk-XX4GXKTX.js";
4
+ import "./chunk-NMFDC2W2.js";
5
+ import "./chunk-77VY4O7A.js";
6
+ import {
7
+ editorialLabel,
8
+ fade
9
+ } from "./chunk-4YM2M62S.js";
10
+ import {
11
+ hasSceneMedia
12
+ } from "./chunk-224QNWRA.js";
13
+ import "./chunk-TVIU23OM.js";
14
+
15
+ // src/visual-system/scene-templates/comparison.tsx
16
+ import { jsx, jsxs } from "react/jsx-runtime";
17
+ function ComparisonScene(props) {
18
+ const { variables, width, height, progress, motionProgress = progress, safeZone } = props;
19
+ const overMedia = hasSceneMedia(variables);
20
+ const portrait = height > width, u = Math.min(width, height);
21
+ const side = Math.max(width * 0.12, safeZone.left, safeZone.right);
22
+ const top = Math.max(height * 0.2, safeZone.top), bottom = height - Math.max(height * 0.23, safeZone.bottom);
23
+ const area = bottom - top, cy = (top + bottom) / 2, available = width - side * 2;
24
+ const entries = [{ label: String(variables.leftLabel ?? ""), text: String(variables.leftText ?? "") }, { label: String(variables.rightLabel ?? ""), text: String(variables.rightText ?? "") }];
25
+ const textWidth = portrait ? available : available * 0.39;
26
+ return /* @__PURE__ */ jsxs(EditorialSurface, { ...props, template: "comparison", children: [
27
+ /* @__PURE__ */ jsx("div", { "aria-hidden": "true", style: { position: "absolute", left: portrait ? width * 0.32 : width / 2, top: portrait ? cy : top + area * 0.18, width: portrait ? width * 0.36 : u * 1e-3, height: portrait ? u * 1e-3 : area * 0.64, background: "#fff", opacity: 0.22 * fade((motionProgress - 0.16) / 0.18) } }),
28
+ entries.map((entry, i) => /* @__PURE__ */ jsxs("div", { "data-comparison-side": i === 0 ? "left" : "right", style: { position: "absolute", left: portrait ? side : side + available * (i === 0 ? 0.055 : 0.555), top: portrait ? top + area * (i === 0 ? 0.25 : 0.75) : cy, width: textWidth, transform: "translateY(-50%)", textAlign: "center", opacity: fade((motionProgress - 0.04 - i * 0.22) / 0.2) }, children: [
29
+ entry.label && /* @__PURE__ */ jsx("div", { style: { ...editorialLabel(u), color: overMedia ? "#fff" : "#b7b7bc", marginBottom: u * 0.028 }, children: entry.label }),
30
+ /* @__PURE__ */ jsx("div", { style: { fontSize: u * 0.058, fontWeight: 500, lineHeight: 1.22, letterSpacing: "-.025em", textWrap: "balance", overflowWrap: "anywhere" }, children: entry.text })
31
+ ] }, i))
32
+ ] });
33
+ }
34
+ var ComparisonSceneTemplate = ComparisonScene;
35
+ export {
36
+ ComparisonSceneTemplate
37
+ };
@@ -0,0 +1,12 @@
1
+ import {
2
+ createVideo
3
+ } from "./chunk-ZDJNFA52.js";
4
+ import "./chunk-E7CL7UPB.js";
5
+ import "./chunk-RXTN2CW6.js";
6
+ import "./chunk-7M56IUUX.js";
7
+ import "./chunk-AUPC6MDK.js";
8
+ import "./chunk-7NZOMTAL.js";
9
+ import "./chunk-3O7OMMMF.js";
10
+ export {
11
+ createVideo
12
+ };
@@ -0,0 +1,43 @@
1
+ import {
2
+ EditorialSurface
3
+ } from "./chunk-XX4GXKTX.js";
4
+ import "./chunk-NMFDC2W2.js";
5
+ import "./chunk-77VY4O7A.js";
6
+ import {
7
+ fade
8
+ } from "./chunk-4YM2M62S.js";
9
+ import "./chunk-224QNWRA.js";
10
+ import "./chunk-TVIU23OM.js";
11
+
12
+ // src/visual-system/scene-templates/editorial-timeline.tsx
13
+ import { jsx, jsxs } from "react/jsx-runtime";
14
+ var timelineTiming = (index) => ({ nodeStart: index === 0 ? 0.06 : 0.23 + (index - 1) * 0.11, lineStart: 0.14 + index * 0.11, lineDuration: 0.09, entranceDuration: 0.16 });
15
+ function TimelineScene(props) {
16
+ const { variables, progress, motionProgress = progress, width, height, safeZone } = props;
17
+ const events = (Array.isArray(variables.events) ? variables.events : []).slice(0, 5).map((e) => ({ label: String(e?.label ?? "") }));
18
+ const portrait = height > width, u = Math.min(width, height), n = Math.max(1, events.length);
19
+ const side = Math.max(width * 0.13, safeZone.left, safeZone.right), available = width - side * 2;
20
+ const top = Math.max(height * 0.2, safeZone.top), bottom = height - Math.max(height * 0.24, safeZone.bottom);
21
+ const slot = portrait ? (bottom - top) / n : available / n;
22
+ const points = events.map((_, i) => ({ x: portrait ? side : side + slot * (i + 0.5), y: portrait ? top + slot * (i + 0.5) : height * 0.43 }));
23
+ const radius = u * 45e-4;
24
+ return /* @__PURE__ */ jsxs(EditorialSurface, { ...props, template: "timeline", children: [
25
+ points.slice(0, -1).map((point, i) => {
26
+ const next = points[i + 1], timing = timelineTiming(i), line = fade((motionProgress - timing.lineStart) / timing.lineDuration);
27
+ return /* @__PURE__ */ jsx("div", { "data-step-connector": "true", style: { position: "absolute", left: point.x, top: point.y, width: portrait ? u * 1e-3 : next.x - point.x, height: portrait ? next.y - point.y : u * 1e-3, background: "#fff", opacity: 0.3, transform: portrait ? `scaleY(${line})` : `scaleX(${line})`, transformOrigin: portrait ? "top" : "left" } }, `line-${i}`);
28
+ }),
29
+ points.map((point, i) => {
30
+ const timing = timelineTiming(i), event = events[i], opacity = fade((motionProgress - timing.nodeStart) / timing.entranceDuration);
31
+ const textW = portrait ? available - u * 0.055 : slot * 0.86;
32
+ return /* @__PURE__ */ jsxs("div", { "data-template-item": "steps", style: { opacity }, children: [
33
+ /* @__PURE__ */ jsx("div", { "aria-hidden": "true", style: { position: "absolute", left: point.x - radius, top: point.y - radius, width: radius * 2, height: radius * 2, borderRadius: "50%", background: "#fff" } }),
34
+ /* @__PURE__ */ jsx("div", { style: { position: "absolute", left: portrait ? point.x + u * 0.055 : point.x - textW / 2, top: portrait ? point.y : point.y + u * 0.05, width: textW, transform: portrait ? "translateY(-50%)" : void 0, textAlign: portrait ? "left" : "center" }, children: /* @__PURE__ */ jsx("div", { style: { fontSize: u * (portrait ? 0.052 : 0.041), fontWeight: 500, lineHeight: 1.22, letterSpacing: "-.025em", textWrap: "balance", overflowWrap: "anywhere" }, children: event.label }) })
35
+ ] }, i);
36
+ })
37
+ ] });
38
+ }
39
+ var TimelineSceneTemplate = TimelineScene;
40
+ export {
41
+ TimelineSceneTemplate,
42
+ timelineTiming
43
+ };