@vanillaskyai/video 0.1.1 → 0.2.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 (47) hide show
  1. package/CHANGELOG.md +19 -0
  2. package/PUBLIC-API.md +20 -2
  3. package/README.md +7 -3
  4. package/dist/{builtin-server-F7YXRVCH.js → builtin-server-BRTZN4Q7.js} +2 -2
  5. package/dist/check-runtime.js +1 -1
  6. package/dist/{chunk-EVG2ZYIY.js → chunk-3O7OMMMF.js} +7 -4
  7. package/dist/{chunk-GRMUV7QY.js → chunk-BKF3A357.js} +1 -1
  8. package/dist/{chunk-A2X45FET.js → chunk-I5YWVQ5R.js} +1 -1
  9. package/dist/{chunk-ERY4S6QV.js → chunk-JW47XCRL.js} +6 -1
  10. package/dist/{chunk-FQ47WXAJ.js → chunk-K5SRF7D3.js} +4 -0
  11. package/dist/{chunk-YC5MFXA5.js → chunk-Q2DDQKEG.js} +34 -12
  12. package/dist/{chunk-XB7LH4J2.js → chunk-QTMHS7JD.js} +2 -1
  13. package/dist/{chunk-W2XJKG6C.js → chunk-RBQN3VSY.js} +2 -2
  14. package/dist/{chunk-OOPIETLW.js → chunk-S5ZU5WUU.js} +1 -1
  15. package/dist/{chunk-ABPKM4GK.js → chunk-STIFILQG.js} +1 -1
  16. package/dist/{chunk-IXCUY3B3.js → chunk-TGFO454Q.js} +169 -45
  17. package/dist/{chunk-CHLGW22F.js → chunk-XAMUOSX7.js} +3 -3
  18. package/dist/cli.js +5 -5
  19. package/dist/{compose-video-CN3VSWST.js → compose-video-34GRZIF2.js} +4 -4
  20. package/dist/{events-_wfjbmEp.d.ts → events-B-28kERX.d.ts} +2 -2
  21. package/dist/index.d.ts +2 -2
  22. package/dist/{kit-BMNcHqa9.d.ts → kit-CBedbj1n.d.ts} +1 -1
  23. package/dist/react.d.ts +7 -4
  24. package/dist/react.js +226 -30
  25. package/dist/server.d.ts +21 -4
  26. package/dist/server.js +193 -14
  27. package/dist/{system-prompt-A6UXNTPK.js → system-prompt-GYTYGWGO.js} +1 -1
  28. package/dist/template-catalog.js +1 -1
  29. package/dist/templates.d.ts +3 -3
  30. package/dist/test.d.ts +3 -2
  31. package/dist/test.js +3 -3
  32. package/dist/{text-stream-6XHC7J47.js → text-stream-UPGUD2TD.js} +2 -2
  33. package/dist/{types-B-PZR6G_.d.ts → types-_t4bduwX.d.ts} +1 -0
  34. package/docs/concepts.md +7 -0
  35. package/docs/custom-templates.md +3 -3
  36. package/docs/errors.md +3 -1
  37. package/docs/getting-started.md +8 -6
  38. package/docs/input-and-first-scene.md +7 -4
  39. package/docs/integrate-nextjs.md +2 -2
  40. package/docs/media-and-audio.md +73 -14
  41. package/docs/prompt-and-input.md +7 -0
  42. package/docs/provider-integration.md +7 -0
  43. package/docs/reference/protocol.md +9 -0
  44. package/examples/nextjs-quickstart/package.json +1 -1
  45. package/package.json +1 -1
  46. package/registry/items/brandMessage.json +1 -2
  47. package/registry/items/media.json +2 -1
@@ -5,11 +5,11 @@ import {
5
5
  applyVideoEvent,
6
6
  checksumVideo,
7
7
  createVideoState
8
- } from "./chunk-ABPKM4GK.js";
8
+ } from "./chunk-STIFILQG.js";
9
9
  import {
10
10
  parseVideoPlanPart,
11
11
  safePublicDiagnostic
12
- } from "./chunk-ERY4S6QV.js";
12
+ } from "./chunk-JW47XCRL.js";
13
13
  import {
14
14
  MAX_RETAINED_INSTRUCTIONS_LENGTH,
15
15
  MAX_RETAINED_MEDIA_URLS,
@@ -26,7 +26,7 @@ import {
26
26
  getCloserReserve,
27
27
  getReadableSceneDuration,
28
28
  paceScene
29
- } from "./chunk-EVG2ZYIY.js";
29
+ } from "./chunk-3O7OMMMF.js";
30
30
  import {
31
31
  resolveVideoBrand
32
32
  } from "./chunk-XGRA2MUV.js";
@@ -105,9 +105,13 @@ function buildVideoUserPrompt(input, openingDurationSec = 0) {
105
105
  "For a long source, summarize instead of attempting to represent every fact, unless the creative instructions explicitly request complete fact coverage that fits the duration.",
106
106
  "Preserve exact wording and numbers for the takeaways you select. Preserve qualifiers, units, denominators, ranges, and comparison direction; for example, do not shorten 4.8 out of 5 to 4.8.",
107
107
  "Choose the scene count from the distinct grounded material and the duration budget. Use fewer scenes for sparse input; continue beyond five when rich input warrants it and timing allows.",
108
+ "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.",
109
+ "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.",
110
+ "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.",
111
+ "After the first playable body scene, emit exactly one scene.add with placement closer. Generate a short grounded conclusion that answers the story's so-what; it must not repeat hook language or imply another scene follows.",
108
112
  "Use a different suitable template for each body scene when the catalog supports it.",
109
113
  "Never add filler to satisfy a count or diversity target.",
110
- input.suppliedMedia?.length ? "Select zero or more relevant opaque supplied-media references for visible later scenes. Never invent or transform a reference, and never emit mediaKeyword without a resolved mediaUrl." : "Use asset-free templates. No media URL has been supplied or resolved. Never emit mediaKeyword without a resolved mediaUrl.",
114
+ input.suppliedMedia?.length ? "Select zero or more relevant opaque supplied-media references for visible later scenes. Never invent or transform a reference. Only emit mediaKeyword when the trusted system catalog explicitly exposes it, and only on later scenes. Never invent mediaUrl or mediaPoster." : "No supplied media URL is available. Use asset-free templates unless the trusted system catalog explicitly permits host-resolved media intent. Only emit mediaKeyword when the trusted system catalog explicitly exposes it, and only on later scenes. Never invent mediaUrl or mediaPoster.",
111
115
  "",
112
116
  "RAW INPUT",
113
117
  input.input.trim(),
@@ -158,12 +162,12 @@ function buildInitialConfig(input, audio, snapshotRetention, closerReserveSec, g
158
162
  const openingText = input.opening?.trim();
159
163
  const rawScenes = openingText ? [{
160
164
  id: "supplied-opening",
161
- templateId: "notification",
165
+ templateId: "media",
162
166
  variables: {
163
- appName: input.brand?.name ?? "",
164
- message: openingText
167
+ texts: openingText,
168
+ mediaType: "gradient"
165
169
  },
166
- timing: { fixedDuration: 5 }
170
+ timing: { fixedDuration: 3 }
167
171
  }] : [];
168
172
  const scenes = [];
169
173
  const warnings = [];
@@ -252,11 +256,42 @@ function createOmittedForCloserWarning(sceneId) {
252
256
  return {
253
257
  code: "scene_omitted_for_closer",
254
258
  category: "readability",
255
- message: "Scene was omitted because the final action must remain the last scene.",
259
+ message: "Scene was omitted because the reserved closer must remain the last scene.",
256
260
  sceneId,
257
261
  recoverable: true
258
262
  };
259
263
  }
264
+ function createDuplicateCloserWarning() {
265
+ return {
266
+ code: "scene_omitted_for_closer",
267
+ category: "readability",
268
+ message: "An additional closer was omitted because the first valid closer is already reserved.",
269
+ recoverable: true
270
+ };
271
+ }
272
+ function createIncompletePlanWarning() {
273
+ return {
274
+ code: "plan_incomplete",
275
+ category: "provider",
276
+ message: "The planner stopped at a length limit; some requested scenes or the ending may be missing.",
277
+ recoverable: true
278
+ };
279
+ }
280
+ function createMissingCloserWarning() {
281
+ return {
282
+ code: "plan_missing_closer",
283
+ category: "provider",
284
+ message: "The planner completed without a valid closer; the video may end on a body scene.",
285
+ recoverable: true
286
+ };
287
+ }
288
+ function pendingCloserReserve(scene, getTemplatePacing) {
289
+ const metadata = getTemplatePacing?.(scene.templateId);
290
+ const readableMinimum = getReadableSceneDuration(scene, metadata);
291
+ const explicitRange = scene.timing.startTime != null && scene.timing.endTime != null ? Math.max(0, scene.timing.endTime - scene.timing.startTime) : void 0;
292
+ const requested = explicitRange ?? scene.timing.fixedDuration ?? metadata?.preferredDuration ?? readableMinimum;
293
+ return Math.max(readableMinimum, requested);
294
+ }
260
295
  function sceneSlotDuration(scene) {
261
296
  return Math.max(0, (scene.timing.endTime ?? 0) - (scene.timing.startTime ?? 0));
262
297
  }
@@ -278,8 +313,8 @@ function createSceneQualityWarnings(scene) {
278
313
  }
279
314
  function createVideo(input, options) {
280
315
  validateInput(input);
281
- if (input.opening?.trim() && options.capabilities?.templates != null && !options.capabilities.templates.includes("notification")) {
282
- throw new Error("Scene template notification was not negotiated");
316
+ if (input.opening?.trim() && options.capabilities?.templates != null && !options.capabilities.templates.includes("media")) {
317
+ throw new Error("Scene template media was not negotiated");
283
318
  }
284
319
  const requestId = options.requestId ?? createId("request");
285
320
  const runId = options.runId ?? createId("run");
@@ -379,6 +414,15 @@ function createVideo(input, options) {
379
414
  resolveResult(finalState);
380
415
  }
381
416
  };
417
+ let planCompleted = false;
418
+ let generatedSceneCount = 0;
419
+ let runtimeDurationLimited = false;
420
+ let pendingCloser;
421
+ const deferredForCloser = [];
422
+ let plannerReportedLength = false;
423
+ let closerCommitted = false;
424
+ let missingCloser = false;
425
+ let finishReason = "stop";
382
426
  try {
383
427
  yield emit(events.create("response.start", {
384
428
  requestId,
@@ -398,14 +442,7 @@ function createVideo(input, options) {
398
442
  revision: 0
399
443
  }));
400
444
  }
401
- let planCompleted = false;
402
- let generatedSceneCount = 0;
403
- let runtimeDurationLimited = false;
404
- let askAccepted = false;
405
- const deferredForCloser = [];
406
- let plannerReportedLength = false;
407
- let finishReason = "stop";
408
- const systemPrompt = options.systemPrompt ?? (await import("./system-prompt-A6UXNTPK.js")).DEFAULT_VIDEO_SYSTEM_PROMPT;
445
+ const systemPrompt = options.systemPrompt ?? (await import("./system-prompt-GYTYGWGO.js")).DEFAULT_VIDEO_SYSTEM_PROMPT;
409
446
  const context = { request, systemPrompt, userPrompt, initialConfig, signal: controller.signal };
410
447
  attachGenerationLifecycleSink(context, lifecycle);
411
448
  for await (const untrustedPart of options.generate(context)) {
@@ -421,21 +458,29 @@ function createVideo(input, options) {
421
458
  if (options.capabilities?.templates != null && !options.capabilities.templates.includes(part.scene.templateId)) {
422
459
  throw new Error(`Scene template ${part.scene.templateId} was not negotiated`);
423
460
  }
424
- if (askAccepted) {
425
- runtimeDurationLimited = true;
426
- rejectedSceneCount += 1;
427
- yield emit(events.create("response.warning", {
428
- warning: createOmittedForCloserWarning(part.scene.id)
429
- }));
430
- continue;
431
- }
432
461
  options.validateScene?.(part.scene, {
433
462
  input,
434
463
  previousScenes: state.config?.scenes ?? []
435
464
  });
436
465
  const templatePacing = options.getTemplatePacing?.(part.scene.templateId);
437
466
  const isAskCandidate = templatePacing?.jobs?.includes("ask") === true;
438
- if (deferredForCloser.length && !isAskCandidate) {
467
+ const isPayoffCandidate = templatePacing?.jobs?.includes("payoff") === true;
468
+ if (part.placement === "closer" && templatePacing && !isAskCandidate && !isPayoffCandidate) {
469
+ throw new Error(`Scene template ${part.scene.templateId} cannot be used as a closer`);
470
+ }
471
+ const isCloserCandidate = part.placement === "closer" || isAskCandidate;
472
+ if (isCloserCandidate) {
473
+ if (pendingCloser) {
474
+ rejectedSceneCount += 1;
475
+ yield emit(events.create("response.warning", {
476
+ warning: createDuplicateCloserWarning()
477
+ }));
478
+ } else {
479
+ pendingCloser = part.scene;
480
+ }
481
+ continue;
482
+ }
483
+ if (deferredForCloser.length) {
439
484
  deferredForCloser.push(part.scene);
440
485
  continue;
441
486
  }
@@ -443,7 +488,7 @@ function createVideo(input, options) {
443
488
  previousScenes: state.config?.scenes ?? [],
444
489
  audio: state.config?.audio,
445
490
  maxDurationSec: input.maxDurationSec ?? 30,
446
- closerReserveSec,
491
+ closerReserveSec: pendingCloser ? pendingCloserReserve(pendingCloser, options.getTemplatePacing) : closerReserveSec,
447
492
  getTemplatePacing: options.getTemplatePacing
448
493
  });
449
494
  if (!paced.scene) {
@@ -459,16 +504,6 @@ function createVideo(input, options) {
459
504
  continue;
460
505
  }
461
506
  const scene = paced.scene;
462
- const isAsk = isAskCandidate;
463
- if (isAsk && deferredForCloser.length) {
464
- runtimeDurationLimited = true;
465
- for (const deferred of deferredForCloser.splice(0)) {
466
- rejectedSceneCount += 1;
467
- yield emit(events.create("response.warning", {
468
- warning: createOmittedForCloserWarning(deferred.id)
469
- }));
470
- }
471
- }
472
507
  for (const warning of paced.warnings) {
473
508
  yield emit(events.create("response.warning", { warning }));
474
509
  }
@@ -480,13 +515,24 @@ function createVideo(input, options) {
480
515
  position: state.config?.scenes.length ?? 0,
481
516
  revision: 0
482
517
  }));
483
- if (isAsk) {
484
- askAccepted = true;
485
- }
486
518
  generatedSceneCount += 1;
487
519
  acceptedSceneCount += 1;
488
520
  timeToFirstSceneMs ??= Math.max(0, now() - startedAt);
489
521
  } else if (part.type === "scene.patch") {
522
+ if (pendingCloser?.id === part.sceneId) {
523
+ const nextPendingCloser = {
524
+ ...pendingCloser,
525
+ ...part.patch,
526
+ variables: part.patch.variables ? { ...pendingCloser.variables, ...part.patch.variables } : pendingCloser.variables,
527
+ timing: part.patch.timing ? { ...pendingCloser.timing, ...part.patch.timing } : pendingCloser.timing
528
+ };
529
+ options.validateScene?.(nextPendingCloser, {
530
+ input,
531
+ previousScenes: state.config?.scenes ?? []
532
+ });
533
+ pendingCloser = nextPendingCloser;
534
+ continue;
535
+ }
490
536
  const scenes = state.config?.scenes ?? [];
491
537
  const sceneIndex = scenes.findIndex((scene) => scene.id === part.sceneId);
492
538
  if (sceneIndex < 0) throw new Error(`Scene ${part.sceneId} does not exist`);
@@ -521,7 +567,7 @@ function createVideo(input, options) {
521
567
  previousScenes: scenes.slice(0, sceneIndex),
522
568
  audio: state.config?.audio,
523
569
  maxDurationSec: input.maxDurationSec ?? 30,
524
- closerReserveSec,
570
+ closerReserveSec: pendingCloser ? pendingCloserReserve(pendingCloser, options.getTemplatePacing) : closerReserveSec,
525
571
  getTemplatePacing: options.getTemplatePacing
526
572
  });
527
573
  if (!pacedPatch.scene) {
@@ -561,6 +607,18 @@ function createVideo(input, options) {
561
607
  patch
562
608
  }));
563
609
  } else if (part.type === "asset.patch") {
610
+ if (pendingCloser?.id === part.sceneId) {
611
+ const nextPendingCloser = {
612
+ ...pendingCloser,
613
+ variables: { ...pendingCloser.variables, ...part.variables }
614
+ };
615
+ options.validateScene?.(nextPendingCloser, {
616
+ input,
617
+ previousScenes: state.config?.scenes ?? []
618
+ });
619
+ pendingCloser = nextPendingCloser;
620
+ continue;
621
+ }
564
622
  const scenes = state.config?.scenes ?? [];
565
623
  const scene = scenes.find((item) => item.id === part.sceneId);
566
624
  if (!scene) throw new Error(`Scene ${part.sceneId} does not exist`);
@@ -604,7 +662,39 @@ function createVideo(input, options) {
604
662
  yield errorEvent;
605
663
  } else if (part.type === "plan.complete") {
606
664
  if (planCompleted) throw new Error("The planner emitted plan.complete more than once");
607
- if (!askAccepted && deferredForCloser.length) {
665
+ if (pendingCloser) {
666
+ if (deferredForCloser.length) runtimeDurationLimited = true;
667
+ for (const deferred of deferredForCloser.splice(0)) {
668
+ rejectedSceneCount += 1;
669
+ yield emit(events.create("response.warning", {
670
+ warning: createOmittedForCloserWarning(deferred.id)
671
+ }));
672
+ }
673
+ const pacedCloser = paceScene(pendingCloser, {
674
+ previousScenes: state.config?.scenes ?? [],
675
+ audio: state.config?.audio,
676
+ maxDurationSec: input.maxDurationSec ?? 30,
677
+ closerReserveSec: 0,
678
+ getTemplatePacing: options.getTemplatePacing
679
+ });
680
+ for (const warning of pacedCloser.warnings) {
681
+ yield emit(events.create("response.warning", { warning }));
682
+ }
683
+ if (pacedCloser.scene) {
684
+ yield emit(events.create("scene.add", {
685
+ scene: pacedCloser.scene,
686
+ position: state.config?.scenes.length ?? 0,
687
+ revision: 0
688
+ }));
689
+ generatedSceneCount += 1;
690
+ acceptedSceneCount += 1;
691
+ timeToFirstSceneMs ??= Math.max(0, now() - startedAt);
692
+ closerCommitted = true;
693
+ } else {
694
+ runtimeDurationLimited = true;
695
+ rejectedSceneCount += 1;
696
+ }
697
+ } else if (deferredForCloser.length) {
608
698
  for (const deferred of deferredForCloser.splice(0)) {
609
699
  const recovered = paceScene(deferred, {
610
700
  previousScenes: state.config?.scenes ?? [],
@@ -634,8 +724,15 @@ function createVideo(input, options) {
634
724
  timeToFirstSceneMs ??= Math.max(0, now() - startedAt);
635
725
  }
636
726
  }
727
+ if (options.requireCloser && !closerCommitted) {
728
+ missingCloser = true;
729
+ yield emit(events.create("response.warning", {
730
+ warning: createMissingCloserWarning()
731
+ }));
732
+ }
637
733
  planCompleted = true;
638
- finishReason = runtimeDurationLimited ? "length" : part.finishReason ?? "stop";
734
+ const reportedFinishReason = part.finishReason ?? "stop";
735
+ finishReason = runtimeDurationLimited ? "length" : reportedFinishReason !== "stop" ? reportedFinishReason : missingCloser ? "other" : "stop";
639
736
  plannerReportedLength = part.finishReason === "length";
640
737
  continue;
641
738
  }
@@ -665,6 +762,11 @@ function createVideo(input, options) {
665
762
  if (plannerReportedLength && generatedSceneCount === 0) {
666
763
  throw new Error("The planner was truncated before adding a generated scene");
667
764
  }
765
+ if (plannerReportedLength) {
766
+ yield emit(events.create("response.warning", {
767
+ warning: createIncompletePlanWarning()
768
+ }));
769
+ }
668
770
  if (!state.config?.scenes.length) throw new Error("The planner completed without adding a scene");
669
771
  const snapshot = parseVideo(state.config);
670
772
  const completeEvent = emit(events.create("response.complete", {
@@ -699,6 +801,28 @@ function createVideo(input, options) {
699
801
  return;
700
802
  }
701
803
  try {
804
+ if (pendingCloser && !closerCommitted && state.config?.scenes.length) {
805
+ const pacedCloser = paceScene(pendingCloser, {
806
+ previousScenes: state.config.scenes,
807
+ audio: state.config.audio,
808
+ maxDurationSec: input.maxDurationSec ?? 30,
809
+ closerReserveSec: 0,
810
+ getTemplatePacing: options.getTemplatePacing
811
+ });
812
+ for (const warning of pacedCloser.warnings) {
813
+ yield emit(events.create("response.warning", { warning }));
814
+ }
815
+ if (pacedCloser.scene) {
816
+ yield emit(events.create("scene.add", {
817
+ scene: pacedCloser.scene,
818
+ position: state.config.scenes.length,
819
+ revision: 0
820
+ }));
821
+ generatedSceneCount += 1;
822
+ acceptedSceneCount += 1;
823
+ closerCommitted = true;
824
+ }
825
+ }
702
826
  const provider = await settleProviderLifecycle();
703
827
  for (const warning of provider.warnings) {
704
828
  if (!state.warnings.some((existing) => existing.code === warning.code && existing.message === warning.message)) {
@@ -7,7 +7,8 @@ var GENERATED_BUILTIN_TEMPLATE_CATALOG = [
7
7
  "family": "Media & motion",
8
8
  "jobs": [
9
9
  "setup",
10
- "atmosphere"
10
+ "atmosphere",
11
+ "payoff"
11
12
  ],
12
13
  "register": "motion-led",
13
14
  "useWhen": "A visual hook, scenic beat, or concrete product-context line benefits from immersive media.",
@@ -2405,8 +2406,7 @@ var GENERATED_BUILTIN_TEMPLATE_CATALOG = [
2405
2406
  "description": "A single iMessage-style outgoing bubble floats over media or a brand gradient.",
2406
2407
  "family": "Social & messaging",
2407
2408
  "jobs": [
2408
- "claim",
2409
- "payoff"
2409
+ "claim"
2410
2410
  ],
2411
2411
  "register": "card-led",
2412
2412
  "useWhen": "A direct one-to-one thank-you, invitation, promise, or concise launch note is the point.",
package/dist/cli.js CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
- import "./chunk-YC5MFXA5.js";
3
- import "./chunk-FQ47WXAJ.js";
4
- import "./chunk-EVG2ZYIY.js";
2
+ import "./chunk-Q2DDQKEG.js";
3
+ import "./chunk-K5SRF7D3.js";
4
+ import "./chunk-3O7OMMMF.js";
5
5
  import {
6
6
  isTemplatePropertyFormatSupported,
7
7
  validateTemplateSchemaValue
@@ -9,13 +9,13 @@ import {
9
9
  import {
10
10
  getBuiltinTemplateMetadata,
11
11
  listBuiltinTemplateMetadata
12
- } from "./chunk-CHLGW22F.js";
12
+ } from "./chunk-XAMUOSX7.js";
13
13
  import {
14
14
  assertTemplateTransitionMetadata
15
15
  } from "./chunk-2E6T633S.js";
16
16
  import {
17
17
  BUILTIN_TEMPLATE_MANIFEST
18
- } from "./chunk-W2XJKG6C.js";
18
+ } from "./chunk-RBQN3VSY.js";
19
19
  import {
20
20
  getTemplateDefaults,
21
21
  isMeaningfullyPresentTemplateValue,
@@ -1,12 +1,12 @@
1
1
  import {
2
2
  createVideo
3
- } from "./chunk-IXCUY3B3.js";
3
+ } from "./chunk-TGFO454Q.js";
4
4
  import "./chunk-E7CL7UPB.js";
5
- import "./chunk-ABPKM4GK.js";
6
- import "./chunk-ERY4S6QV.js";
5
+ import "./chunk-STIFILQG.js";
6
+ import "./chunk-JW47XCRL.js";
7
7
  import "./chunk-GHJEAP4O.js";
8
8
  import "./chunk-EE6PK6XC.js";
9
- import "./chunk-EVG2ZYIY.js";
9
+ import "./chunk-3O7OMMMF.js";
10
10
  import "./chunk-XGRA2MUV.js";
11
11
  export {
12
12
  createVideo
@@ -1,7 +1,7 @@
1
- import { e as VideoOrientation, g as VideoStyle, V as Video, j as VideoCapabilities, a as VideoAudio, f as VideoScene, k as VIDEO_PROTOCOL_VERSION } from './types-B-PZR6G_.js';
1
+ import { e as VideoOrientation, g as VideoStyle, V as Video, j as VideoCapabilities, a as VideoAudio, f as VideoScene, k as VIDEO_PROTOCOL_VERSION } from './types-_t4bduwX.js';
2
2
 
3
3
  type VideoWarningCategory = "validation" | "readability" | "grounding" | "provider" | "media" | "protocol";
4
- type VideoWarningCode = "scene_duration_adjusted" | "scene_omitted_unreadable" | "scene_omitted_for_closer" | "scene_patch_rejected_readability" | "chart_scale_imbalance" | "provider_warning" | "provider_diagnostics_unavailable";
4
+ type VideoWarningCode = "scene_duration_adjusted" | "scene_omitted_unreadable" | "scene_omitted_for_closer" | "scene_patch_rejected_readability" | "chart_scale_imbalance" | "plan_incomplete" | "plan_missing_closer" | "provider_warning" | "provider_diagnostics_unavailable";
5
5
  /** A bounded, client-safe diagnostic that never includes provider payloads. */
6
6
  interface VideoWarning {
7
7
  code: VideoWarningCode;
package/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- import { V as Video } from './types-B-PZR6G_.js';
2
- export { a as VideoAudio, b as VideoBackground, c as VideoBrand, d as VideoInput, e as VideoOrientation, f as VideoScene, g as VideoStyle, h as VideoStyleOptions, i as VideoSuppliedMedia } from './types-B-PZR6G_.js';
1
+ import { V as Video } from './types-_t4bduwX.js';
2
+ export { a as VideoAudio, b as VideoBackground, c as VideoBrand, d as VideoInput, e as VideoOrientation, f as VideoScene, g as VideoStyle, h as VideoStyleOptions, i as VideoSuppliedMedia } from './types-_t4bduwX.js';
3
3
  export { V as VideoStatus } from './state-DZcKuS32.js';
4
4
 
5
5
  type VideoValidationErrorCode = "invalid_video" | "unsupported_video_version";
@@ -1,4 +1,4 @@
1
- import { g as VideoStyle, j as VideoCapabilities } from './types-B-PZR6G_.js';
1
+ import { g as VideoStyle, j as VideoCapabilities } from './types-_t4bduwX.js';
2
2
  import { ComponentType } from 'react';
3
3
  import { S as SceneTemplateMetadata, d as TemplateJsonSchema, f as TemplateJsonSchemaProperty } from './catalog-types-BIhSpOWK.js';
4
4
 
package/dist/react.d.ts CHANGED
@@ -1,11 +1,14 @@
1
1
  import { CSSProperties, ReactElement } from 'react';
2
- import { V as VideoEvent, a as VideoWarning } from './events-_wfjbmEp.js';
3
- import { e as VideoOrientation, V as Video, d as VideoInput } from './types-B-PZR6G_.js';
4
- import { T as TemplateRegistry } from './kit-BMNcHqa9.js';
2
+ import { V as VideoEvent, a as VideoWarning } from './events-B-28kERX.js';
3
+ import { e as VideoOrientation, V as Video, d as VideoInput } from './types-_t4bduwX.js';
4
+ import { T as TemplateRegistry } from './kit-CBedbj1n.js';
5
5
  import { V as VideoStatus } from './state-DZcKuS32.js';
6
6
  import './catalog-types-BIhSpOWK.js';
7
7
 
8
+ type VideoPlaybackMode = "manual" | "muted-autoplay" | "autoplay-with-sound" | "autoplay-after-interaction";
8
9
  interface VideoPlayerSharedProps {
10
+ /** High-level startup policy. When set, this overrides autoPlay and startMuted. */
11
+ playbackMode?: VideoPlaybackMode;
9
12
  /** Initial playback state. Reduced-motion preferences take precedence. */
10
13
  autoPlay?: boolean;
11
14
  startMuted?: boolean;
@@ -78,4 +81,4 @@ interface UseVideoResult {
78
81
  }
79
82
  declare function useVideo(options?: UseVideoOptions): UseVideoResult;
80
83
 
81
- export { type UseVideoOptions, type UseVideoResult, VideoError, type VideoErrorOptions, VideoPlayer, type VideoPlayerProps, useVideo };
84
+ export { type UseVideoOptions, type UseVideoResult, VideoError, type VideoErrorOptions, type VideoPlaybackMode, VideoPlayer, type VideoPlayerProps, useVideo };