@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
@@ -3,21 +3,128 @@ import {
3
3
  resolveMediaType
4
4
  } from "./chunk-224QNWRA.js";
5
5
  import {
6
- ExternalVideoBackdropProvider
7
- } from "./chunk-5CDAM24P.js";
8
- import {
6
+ ExternalVideoBackdropProvider,
9
7
  resolveDensity
10
- } from "./chunk-XWWLKRNU.js";
8
+ } from "./chunk-TVIU23OM.js";
11
9
  import {
12
10
  resolveVideoTimeline
13
11
  } from "./chunk-SPVTJH3F.js";
14
12
  import {
15
13
  supportsExternalVideoBackdrop
16
- } from "./chunk-WJ6FLKNC.js";
14
+ } from "./chunk-R3XAOMKP.js";
17
15
  import {
18
16
  getTemplateDefaults
19
17
  } from "./chunk-73NTSFFI.js";
20
18
 
19
+ // src/player/mounted-scene-readiness.tsx
20
+ import { createContext, useContext, useEffect, useRef } from "react";
21
+ import { jsx } from "react/jsx-runtime";
22
+ var MountedReadinessContext = createContext(void 0);
23
+ var sceneReadinessKey = (scene) => `${scene.id}\0${String(scene.variables.mediaUrl || "")}`;
24
+ function MountedSceneReadiness({ scene, playing, fallback = false }) {
25
+ const marker = useRef(null);
26
+ const report = useContext(MountedReadinessContext);
27
+ const key = sceneReadinessKey(scene);
28
+ useEffect(() => {
29
+ if (!report || !playing) return;
30
+ let stopped = false;
31
+ let frame = 0;
32
+ let callback;
33
+ let observed;
34
+ const start = performance.now();
35
+ const finish = (error, actualVideoFrame = false) => {
36
+ if (!stopped) {
37
+ stopped = true;
38
+ report(key, error, actualVideoFrame);
39
+ }
40
+ };
41
+ const check = () => {
42
+ if (stopped) return;
43
+ const root = marker.current?.closest("[data-video-frame]");
44
+ if (fallback && root?.querySelector("[data-scene-fallback]")) {
45
+ finish();
46
+ return;
47
+ }
48
+ const layer = root?.querySelector('[data-scene-layer="active"]');
49
+ const loading = layer?.querySelector("[data-template-loading]");
50
+ const mediaUrl = String(scene.variables.mediaUrl || "");
51
+ const isVideo = scene.variables.mediaType === "video" || /\.(mp4|webm|mov)(?:[?#]|$)/i.test(mediaUrl);
52
+ if (root && layer && !loading && document.fonts?.status !== "loading") {
53
+ if (!mediaUrl || scene.variables.mediaType === "gradient") {
54
+ finish();
55
+ return;
56
+ }
57
+ if (isVideo) {
58
+ const persistent = root.querySelector("[data-persistent-video-scene-id]");
59
+ const video = (persistent?.getAttribute("data-persistent-video-scene-id") === scene.id ? persistent : layer)?.querySelector("video");
60
+ if (video && video.getAttribute("src") === mediaUrl && video.readyState >= 2) {
61
+ observed = video;
62
+ if (video.requestVideoFrameCallback) {
63
+ callback = video.requestVideoFrameCallback(() => finish(void 0, true));
64
+ return;
65
+ }
66
+ finish(void 0, true);
67
+ return;
68
+ }
69
+ } else {
70
+ const image = [...layer.querySelectorAll("img") ?? []].find((element) => element.getAttribute("src") === mediaUrl);
71
+ if (image?.complete && image.naturalWidth > 0) {
72
+ finish();
73
+ return;
74
+ }
75
+ }
76
+ }
77
+ if (performance.now() - start >= 8e3) {
78
+ finish(new Error("Scene media did not become ready"));
79
+ return;
80
+ }
81
+ frame = requestAnimationFrame(check);
82
+ };
83
+ check();
84
+ const timeout = setTimeout(() => finish(new Error("Scene media did not become ready")), 8e3);
85
+ return () => {
86
+ stopped = true;
87
+ clearTimeout(timeout);
88
+ cancelAnimationFrame(frame);
89
+ if (callback !== void 0) observed?.cancelVideoFrameCallback?.(callback);
90
+ };
91
+ }, [key, report, scene, playing, fallback]);
92
+ return /* @__PURE__ */ jsx("span", { ref: marker, hidden: true });
93
+ }
94
+ function PreparedSceneReadiness({ scene }) {
95
+ const marker = useRef(null);
96
+ const report = useContext(MountedReadinessContext);
97
+ useEffect(() => {
98
+ if (!report) return;
99
+ let frame = 0;
100
+ let stopped = false;
101
+ const check = () => {
102
+ if (stopped) return;
103
+ const root = marker.current?.closest("[data-video-frame]");
104
+ const incoming = root?.querySelector("[data-scene-layer='incoming']");
105
+ const video = incoming?.getAttribute("data-layer-scene-id") === scene.id ? incoming.querySelector("video") : void 0;
106
+ if (video && video.getAttribute("src") === scene.variables.mediaUrl && video.readyState >= 2) {
107
+ report(sceneReadinessKey(scene), void 0, false, true);
108
+ return;
109
+ }
110
+ const image = [...root?.querySelectorAll("img[data-video-poster-plane='prepared']") ?? []].find((image2) => image2.getAttribute("src") === scene.variables.mediaPoster);
111
+ if (image?.complete && image.naturalWidth > 0) {
112
+ void image.decode().then(() => {
113
+ if (!stopped && image.isConnected) report(sceneReadinessKey(scene), void 0, false, true);
114
+ }).catch(() => void 0);
115
+ return;
116
+ }
117
+ frame = requestAnimationFrame(check);
118
+ };
119
+ check();
120
+ return () => {
121
+ stopped = true;
122
+ cancelAnimationFrame(frame);
123
+ };
124
+ }, [scene, report]);
125
+ return /* @__PURE__ */ jsx("span", { ref: marker, hidden: true });
126
+ }
127
+
21
128
  // src/player/video-frame.tsx
22
129
  import {
23
130
  createElement,
@@ -25,7 +132,7 @@ import {
25
132
  lazy,
26
133
  Suspense,
27
134
  useState,
28
- useEffect,
135
+ useEffect as useEffect2,
29
136
  useSyncExternalStore
30
137
  } from "react";
31
138
 
@@ -47,9 +154,9 @@ function scaleSafeZone(zone, factor) {
47
154
  }
48
155
 
49
156
  // src/player/video-frame.tsx
50
- import { jsx, jsxs } from "react/jsx-runtime";
157
+ import { jsx as jsx2, jsxs } from "react/jsx-runtime";
51
158
  function PresentedScene({ notify }) {
52
- useEffect(() => {
159
+ useEffect2(() => {
53
160
  if (!notify) return;
54
161
  const frame = requestAnimationFrame(() => {
55
162
  try {
@@ -80,9 +187,10 @@ function SafeScene({ scene, onFramePresented }) {
80
187
  overflow: "hidden"
81
188
  },
82
189
  children: [
83
- /* @__PURE__ */ jsx(PresentedScene, { notify: onFramePresented }),
84
- /* @__PURE__ */ jsx("p", { children: copy.slice(0, 600) || "Your response continues." }),
85
- /* @__PURE__ */ jsx("small", { style: { fontSize: "0.3em" }, role: "status", children: "This scene uses a simpler layout." })
190
+ /* @__PURE__ */ jsx2(MountedSceneReadiness, { scene, playing: true, fallback: true }),
191
+ /* @__PURE__ */ jsx2(PresentedScene, { notify: onFramePresented }),
192
+ /* @__PURE__ */ jsx2("p", { children: copy.slice(0, 600) || "Your response continues." }),
193
+ /* @__PURE__ */ jsx2("small", { style: { fontSize: "0.3em" }, role: "status", children: "This scene uses a simpler layout." })
86
194
  ]
87
195
  }
88
196
  );
@@ -93,11 +201,11 @@ var SceneBoundary = class extends Component {
93
201
  return { failed: true };
94
202
  }
95
203
  render() {
96
- return this.state.failed ? /* @__PURE__ */ jsx(SafeScene, { scene: this.props.scene, onFramePresented: this.props.onFramePresented }) : this.props.children;
204
+ return this.state.failed ? /* @__PURE__ */ jsx2(SafeScene, { scene: this.props.scene, onFramePresented: this.props.onFramePresented }) : this.props.children;
97
205
  }
98
206
  };
99
207
  var SCENE_TRANSITION_SECONDS = 0.3;
100
- var SceneVideoBackdrop = lazy(() => import("./scene-video-backdrop-XFPX4O3I.js").then((module) => ({ default: module.SceneVideoBackdrop })));
208
+ var SceneVideoBackdrop = lazy(() => import("./scene-video-backdrop-PCTARAAJ.js").then((module) => ({ default: module.SceneVideoBackdrop })));
101
209
  var MEDIA_PREROLL_SECONDS = 1.2;
102
210
  var CONTIGUITY_ULP_FACTOR = 4;
103
211
  var subscribeToDecoderPolicy = () => () => {
@@ -120,14 +228,6 @@ function rangesAreContiguous(left, right) {
120
228
  const ulpTolerance = Number.EPSILON * scale * CONTIGUITY_ULP_FACTOR;
121
229
  return Math.abs(left.end - right.start) <= ulpTolerance;
122
230
  }
123
- function brandBackground(config) {
124
- const background = config.style.brand.background;
125
- return background.type === "solid" ? background.color : `linear-gradient(135deg, ${background.colors[0]}, ${background.colors[1]})`;
126
- }
127
- function brandBackgroundFallback(config) {
128
- const background = config.style.brand.background;
129
- return background.type === "solid" ? background.color : background.colors[0];
130
- }
131
231
  function sceneHasBackdrop(range) {
132
232
  return String(range.scene.variables.mediaType || "auto") !== "gradient" && String(range.scene.variables.mediaUrl || "").trim() !== "";
133
233
  }
@@ -171,13 +271,13 @@ function SceneLayer({
171
271
  }) {
172
272
  const template = kit.getTemplate(range.scene.templateId);
173
273
  const duration = range.end - range.start;
174
- return /* @__PURE__ */ jsx(
274
+ return /* @__PURE__ */ jsx2(
175
275
  ExternalVideoBackdropProvider,
176
276
  {
177
277
  mode: externalVideoBackdrop,
178
278
  audioMuted: mediaAudioMuted || !playing,
179
279
  audioVolume: mediaAudioVolume,
180
- children: /* @__PURE__ */ jsx(
280
+ children: /* @__PURE__ */ jsx2(
181
281
  "div",
182
282
  {
183
283
  "data-scene-layer": layer,
@@ -197,14 +297,14 @@ function SceneLayer({
197
297
  "--vanillasky-transition-semantic-visibility": layer === "incoming" ? "hidden" : "visible",
198
298
  "--vanillasky-template-surface": externalVideoBackdrop !== false ? "transparent" : void 0
199
299
  },
200
- children: template ? /* @__PURE__ */ jsx(SceneBoundary, { scene: range.scene, onFramePresented, children: /* @__PURE__ */ jsxs(Suspense, { fallback: /* @__PURE__ */ jsx(
300
+ children: template ? /* @__PURE__ */ jsx2(SceneBoundary, { scene: range.scene, onFramePresented, children: /* @__PURE__ */ jsxs(Suspense, { fallback: /* @__PURE__ */ jsx2(
201
301
  "div",
202
302
  {
203
303
  "data-template-loading": range.scene.templateId,
204
304
  style: { position: "absolute", inset: 0 }
205
305
  }
206
306
  ), children: [
207
- /* @__PURE__ */ jsx(PresentedScene, { notify: onFramePresented }),
307
+ /* @__PURE__ */ jsx2(PresentedScene, { notify: onFramePresented }),
208
308
  createElement(template.component, {
209
309
  variables: {
210
310
  ...template.defaults ?? getTemplateDefaults(template.schema),
@@ -225,7 +325,7 @@ function SceneLayer({
225
325
  sceneDuration: duration,
226
326
  isPlaying: playing
227
327
  })
228
- ] }) }, range.scene.id) : /* @__PURE__ */ jsx(SafeScene, { scene: range.scene, onFramePresented })
328
+ ] }) }, range.scene.id) : /* @__PURE__ */ jsx2(SafeScene, { scene: range.scene, onFramePresented })
229
329
  }
230
330
  )
231
331
  }
@@ -254,24 +354,24 @@ function VideoFrame({
254
354
  const activeIndex = foundIndex >= 0 ? foundIndex : afterEnd ? timeline.length - 1 : -1;
255
355
  const active = activeIndex >= 0 ? timeline[activeIndex] : void 0;
256
356
  if (!active) {
257
- return /* @__PURE__ */ jsx(
357
+ return /* @__PURE__ */ jsx2(
258
358
  "div",
259
359
  {
260
360
  "data-video-frame": timeline.length === 0 ? "empty" : "gap",
261
361
  className,
262
- style: { width, height, background: brandBackground(config), ...style }
362
+ style: { width, height, background: "#000", ...style }
263
363
  }
264
364
  );
265
365
  }
266
366
  if (!kit.getTemplate(active.scene.templateId)) {
267
- return /* @__PURE__ */ jsx(
367
+ return /* @__PURE__ */ jsx2(
268
368
  "div",
269
369
  {
270
370
  "data-video-frame": "unsupported",
271
371
  "data-template-id": active.scene.templateId,
272
372
  className,
273
373
  style: { width, height, background: "#090712", color: "#fff", ...style },
274
- children: /* @__PURE__ */ jsx(SafeScene, { scene: active.scene, onFramePresented })
374
+ children: /* @__PURE__ */ jsx2(SafeScene, { scene: active.scene, onFramePresented })
275
375
  }
276
376
  );
277
377
  }
@@ -311,6 +411,7 @@ function VideoFrame({
311
411
  const persistentVideoKey = persistentVideoRange ? `${persistentVideoRange.scene.id}\0${String(persistentVideoRange.scene.variables.mediaUrl || "")}` : void 0;
312
412
  const firstVideoRange = timeline.find(sceneHasVideoBackdrop);
313
413
  const posterPreparationRange = activeUsesPersistentVideo ? contiguousNext && sceneHasVideoBackdrop(contiguousNext) ? contiguousNext : activeIndex === timeline.length - 1 && firstVideoRange?.scene.id !== active.scene.id ? firstVideoRange : void 0 : void 0;
414
+ const preparedReadinessRange = posterPreparationRange ?? (contiguousNext && sceneHasVideoBackdrop(contiguousNext) ? contiguousNext : void 0);
314
415
  const preparedPoster = posterPreparationRange && String(
315
416
  posterPreparationRange.scene.variables.mediaPoster || ""
316
417
  ) ? {
@@ -338,7 +439,7 @@ function VideoFrame({
338
439
  const scale = Math.min(width / canvas.width, height / canvas.height);
339
440
  const canvasLeft = (width - canvas.width * scale) / 2;
340
441
  const canvasTop = (height - canvas.height * scale) / 2;
341
- return /* @__PURE__ */ jsx(
442
+ return /* @__PURE__ */ jsxs(
342
443
  "div",
343
444
  {
344
445
  "data-video-frame": "ready",
@@ -350,163 +451,169 @@ function VideoFrame({
350
451
  height,
351
452
  position: "relative",
352
453
  overflow: "hidden",
353
- background: brandBackground(config),
454
+ background: "#000",
354
455
  ...style
355
456
  },
356
- children: /* @__PURE__ */ jsxs(
357
- "div",
358
- {
359
- "data-video-canvas": "true",
360
- style: {
361
- position: "absolute",
362
- left: canvasLeft,
363
- top: canvasTop,
364
- width: canvas.width,
365
- height: canvas.height,
366
- transform: `scale(${scale})`,
367
- transformOrigin: "top left"
368
- },
369
- children: [
370
- /* @__PURE__ */ jsx(
371
- "div",
372
- {
373
- "data-player-background": "brand",
374
- "aria-hidden": "true",
375
- style: {
376
- position: "absolute",
377
- inset: 0,
378
- background: brandBackground(config),
379
- backgroundColor: brandBackgroundFallback(config),
380
- pointerEvents: "none"
381
- }
382
- }
383
- ),
384
- persistentVideoRange && !persistentVideoFailed && /* @__PURE__ */ jsx(
385
- "div",
386
- {
387
- "data-persistent-video-scene-id": persistentVideoRange.scene.id,
388
- "aria-hidden": "true",
389
- style: { position: "absolute", inset: 0, zIndex: 0 },
390
- children: /* @__PURE__ */ jsx(Suspense, { fallback: String(persistentVideoRange.scene.variables.mediaPoster || "") ? /* @__PURE__ */ jsx(
391
- "img",
392
- {
393
- src: String(persistentVideoRange.scene.variables.mediaPoster),
394
- alt: "",
395
- "data-video-poster-plane": "loading",
396
- "data-video-poster-visible": "true",
397
- style: {
398
- position: "absolute",
399
- inset: 0,
400
- width: "100%",
401
- height: "100%",
402
- objectFit: "cover",
403
- objectPosition: String(persistentVideoRange.scene.variables.mediaPosition || "center")
404
- }
405
- }
406
- ) : null, children: /* @__PURE__ */ jsx(
407
- SceneVideoBackdrop,
408
- {
409
- mediaUrl: String(persistentVideoRange.scene.variables.mediaUrl || ""),
410
- mediaPoster: String(persistentVideoRange.scene.variables.mediaPoster || "") || void 0,
411
- mediaPosition: String(persistentVideoRange.scene.variables.mediaPosition || "center"),
412
- backgroundEffect: persistentVideoRange.scene.backgroundEffect ?? config.style.defaultBackgroundEffect,
413
- progress: persistentVideoRange.scene.id === active.scene.id ? rawProgress : 0,
414
- isPlaying: persistentVideoRange.scene.id === active.scene.id && playing,
415
- muted: mediaAudioMuted || persistentVideoRange.scene.id !== active.scene.id || !playing,
416
- volume: mediaAudioVolume,
417
- playbackId: persistentVideoRange.scene.id,
418
- retainPoster: true,
419
- persistent: true,
420
- preparedPoster: preparedPoster ? {
421
- ...preparedPoster,
422
- // The image is already decoded and costs no second video
423
- // decoder. Fade it above the outgoing video using the same
424
- // authored transition clock, then reuse that exact DOM image
425
- // as the new source's underlay at the cut.
426
- opacity: decoderConstrainedTransition ? blendProgress : 0
427
- } : void 0,
428
- onReady: () => setReadyPersistentVideo(persistentVideoKey),
429
- onError: () => setFailedPersistentVideo(persistentVideoKey)
430
- }
431
- ) })
432
- }
433
- ),
434
- mountingNext && contiguousNext ? [
435
- /* @__PURE__ */ jsx(
436
- SceneLayer,
457
+ children: [
458
+ /* @__PURE__ */ jsx2(MountedSceneReadiness, { scene: active.scene, playing }),
459
+ preparedReadinessRange && /* @__PURE__ */ jsx2(PreparedSceneReadiness, { scene: preparedReadinessRange.scene }),
460
+ /* @__PURE__ */ jsxs(
461
+ "div",
462
+ {
463
+ "data-video-canvas": "true",
464
+ style: {
465
+ position: "absolute",
466
+ left: canvasLeft,
467
+ top: canvasTop,
468
+ width: canvas.width,
469
+ height: canvas.height,
470
+ transform: `scale(${scale})`,
471
+ transformOrigin: "top left"
472
+ },
473
+ children: [
474
+ /* @__PURE__ */ jsx2(
475
+ "div",
437
476
  {
438
- onFramePresented,
439
- kit,
440
- config,
441
- range: active,
442
- progress,
443
- motionProgress,
444
- width: canvas.width,
445
- height: canvas.height,
446
- playing,
447
- mediaAudioMuted,
448
- mediaAudioVolume,
449
- layer: previewingNext ? "outgoing" : "active",
450
- opacity: 1 - blendProgress,
451
- interactive: true,
452
- zIndex: 1,
453
- externalVideoBackdrop: persistentVideoRange?.scene.id === active.scene.id ? persistentVideoMode : false
454
- },
455
- active.scene.id
477
+ "data-player-background": "black",
478
+ "aria-hidden": "true",
479
+ style: {
480
+ position: "absolute",
481
+ inset: 0,
482
+ background: "#000",
483
+ backgroundColor: "#000",
484
+ pointerEvents: "none"
485
+ }
486
+ }
456
487
  ),
457
- /* @__PURE__ */ jsx(
458
- SceneLayer,
488
+ persistentVideoRange && !persistentVideoFailed && /* @__PURE__ */ jsx2(
489
+ "div",
459
490
  {
460
- kit,
461
- config,
462
- range: contiguousNext,
463
- progress: 0,
464
- motionProgress: 0,
465
- width: canvas.width,
466
- height: canvas.height,
467
- playing: false,
468
- mediaAudioMuted,
469
- mediaAudioVolume,
470
- layer: "incoming",
471
- opacity: blendProgress,
472
- interactive: false,
473
- zIndex: 2,
474
- externalVideoBackdrop: persistentVideoRange?.scene.id === contiguousNext.scene.id ? persistentVideoMode : false
475
- },
476
- contiguousNext.scene.id
477
- )
478
- ] : [
479
- /* @__PURE__ */ jsx(
480
- SceneLayer,
481
- {
482
- onFramePresented,
483
- kit,
484
- config,
485
- range: active,
486
- progress,
487
- motionProgress,
488
- width: canvas.width,
489
- height: canvas.height,
490
- playing,
491
- mediaAudioMuted,
492
- mediaAudioVolume,
493
- layer: "active",
494
- opacity: 1,
495
- interactive: true,
496
- zIndex: 1,
497
- externalVideoBackdrop: persistentVideoRange?.scene.id === active.scene.id ? persistentVideoMode : false
498
- },
499
- active.scene.id
500
- )
491
+ "data-persistent-video-scene-id": persistentVideoRange.scene.id,
492
+ "aria-hidden": "true",
493
+ style: { position: "absolute", inset: 0, zIndex: 0 },
494
+ children: /* @__PURE__ */ jsx2(Suspense, { fallback: String(persistentVideoRange.scene.variables.mediaPoster || "") ? /* @__PURE__ */ jsx2(
495
+ "img",
496
+ {
497
+ src: String(persistentVideoRange.scene.variables.mediaPoster),
498
+ alt: "",
499
+ "data-video-poster-plane": "loading",
500
+ "data-video-poster-visible": "true",
501
+ style: {
502
+ position: "absolute",
503
+ inset: 0,
504
+ width: "100%",
505
+ height: "100%",
506
+ objectFit: "cover",
507
+ objectPosition: String(persistentVideoRange.scene.variables.mediaPosition || "center")
508
+ }
509
+ }
510
+ ) : null, children: /* @__PURE__ */ jsx2(
511
+ SceneVideoBackdrop,
512
+ {
513
+ mediaUrl: String(persistentVideoRange.scene.variables.mediaUrl || ""),
514
+ mediaPoster: String(persistentVideoRange.scene.variables.mediaPoster || "") || void 0,
515
+ mediaPosition: String(persistentVideoRange.scene.variables.mediaPosition || "center"),
516
+ backgroundEffect: persistentVideoRange.scene.backgroundEffect ?? config.style.defaultBackgroundEffect,
517
+ progress: persistentVideoRange.scene.id === active.scene.id ? rawProgress : 0,
518
+ isPlaying: persistentVideoRange.scene.id === active.scene.id && playing,
519
+ muted: mediaAudioMuted || persistentVideoRange.scene.id !== active.scene.id || !playing,
520
+ volume: mediaAudioVolume,
521
+ playbackId: persistentVideoRange.scene.id,
522
+ retainPoster: true,
523
+ persistent: true,
524
+ preparedPoster: preparedPoster ? {
525
+ ...preparedPoster,
526
+ // The image is already decoded and costs no second video
527
+ // decoder. Fade it above the outgoing video using the same
528
+ // authored transition clock, then reuse that exact DOM image
529
+ // as the new source's underlay at the cut.
530
+ opacity: decoderConstrainedTransition ? blendProgress : 0
531
+ } : void 0,
532
+ onReady: () => setReadyPersistentVideo(persistentVideoKey),
533
+ onError: () => setFailedPersistentVideo(persistentVideoKey)
534
+ }
535
+ ) })
536
+ }
537
+ ),
538
+ mountingNext && contiguousNext ? [
539
+ /* @__PURE__ */ jsx2(
540
+ SceneLayer,
541
+ {
542
+ onFramePresented,
543
+ kit,
544
+ config,
545
+ range: active,
546
+ progress,
547
+ motionProgress,
548
+ width: canvas.width,
549
+ height: canvas.height,
550
+ playing,
551
+ mediaAudioMuted,
552
+ mediaAudioVolume,
553
+ layer: previewingNext ? "outgoing" : "active",
554
+ opacity: 1 - blendProgress,
555
+ interactive: true,
556
+ zIndex: 1,
557
+ externalVideoBackdrop: persistentVideoRange?.scene.id === active.scene.id ? persistentVideoMode : false
558
+ },
559
+ active.scene.id
560
+ ),
561
+ /* @__PURE__ */ jsx2(
562
+ SceneLayer,
563
+ {
564
+ kit,
565
+ config,
566
+ range: contiguousNext,
567
+ progress: 0,
568
+ motionProgress: 0,
569
+ width: canvas.width,
570
+ height: canvas.height,
571
+ playing: false,
572
+ mediaAudioMuted,
573
+ mediaAudioVolume,
574
+ layer: "incoming",
575
+ opacity: blendProgress,
576
+ interactive: false,
577
+ zIndex: 2,
578
+ externalVideoBackdrop: persistentVideoRange?.scene.id === contiguousNext.scene.id ? persistentVideoMode : false
579
+ },
580
+ contiguousNext.scene.id
581
+ )
582
+ ] : [
583
+ /* @__PURE__ */ jsx2(
584
+ SceneLayer,
585
+ {
586
+ onFramePresented,
587
+ kit,
588
+ config,
589
+ range: active,
590
+ progress,
591
+ motionProgress,
592
+ width: canvas.width,
593
+ height: canvas.height,
594
+ playing,
595
+ mediaAudioMuted,
596
+ mediaAudioVolume,
597
+ layer: "active",
598
+ opacity: 1,
599
+ interactive: true,
600
+ zIndex: 1,
601
+ externalVideoBackdrop: persistentVideoRange?.scene.id === active.scene.id ? persistentVideoMode : false
602
+ },
603
+ active.scene.id
604
+ )
605
+ ]
501
606
  ]
502
- ]
503
- }
504
- )
607
+ }
608
+ )
609
+ ]
505
610
  }
506
611
  );
507
612
  }
508
613
 
509
614
  export {
615
+ MountedReadinessContext,
616
+ sceneReadinessKey,
510
617
  getDimensions,
511
618
  VideoFrame
512
619
  };
@@ -0,0 +1,18 @@
1
+ import {
2
+ editorialFont,
3
+ fade
4
+ } from "./chunk-4YM2M62S.js";
5
+
6
+ // src/visual-system/scene-templates/chapter-title.tsx
7
+ import { jsx } from "react/jsx-runtime";
8
+ function TitleScene({ variables, width, height, progress }) {
9
+ const title = String(variables.title ?? "A different perspective");
10
+ const unit = Math.min(width, height);
11
+ const opacity = fade(progress / 0.22) * (1 - fade((progress - 0.76) / 0.24));
12
+ return /* @__PURE__ */ jsx("div", { "data-template": "title", "data-title-treatment": "quiet-fade", style: { position: "absolute", inset: 0, background: "#000", color: "#fff", display: "flex", alignItems: "center", justifyContent: "center" }, children: /* @__PURE__ */ jsx("div", { "data-title-composition": "centered", style: { width: "76%", textAlign: "center", opacity, fontFamily: editorialFont, fontWeight: 500, fontSize: unit * 0.068, lineHeight: 1.18, letterSpacing: "-.025em", textWrap: "balance", overflowWrap: "anywhere" }, children: title }) });
13
+ }
14
+ var TitleSceneTemplate = TitleScene;
15
+
16
+ export {
17
+ TitleSceneTemplate
18
+ };
@@ -1,12 +1,12 @@
1
1
  import {
2
2
  parseVideoEvent
3
- } from "./chunk-MMUXVA47.js";
3
+ } from "./chunk-7M56IUUX.js";
4
4
  import {
5
5
  MAX_RETAINED_MEDIA_URL_LENGTH
6
- } from "./chunk-2DOLF4QN.js";
6
+ } from "./chunk-AUPC6MDK.js";
7
7
  import {
8
8
  VIDEO_PROTOCOL_VERSION
9
- } from "./chunk-AOWSXU2K.js";
9
+ } from "./chunk-7NZOMTAL.js";
10
10
 
11
11
  // src/video-chat/recovery.ts
12
12
  var MEDIA_RECOVERY_NOTICE = "Some visuals were replaced so your response can continue.";