@cueframe/kernel 0.2.51 → 0.2.53

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 (74) hide show
  1. package/dist/_members/composition/fonts/bundledFaces.d.ts +9 -15
  2. package/dist/_members/composition/fonts/captionFaces.d.ts +2 -3
  3. package/dist/_members/composition/ops/captionOps.d.ts +11 -0
  4. package/dist/_members/composition/ops/compositionOps.d.ts +20 -19
  5. package/dist/_members/composition/validators/composition.d.ts +664 -0
  6. package/dist/_members/composition/validators/registry.d.ts +1 -1
  7. package/dist/_members/composition/validators/render.d.ts +294 -0
  8. package/dist/_members/composition-ops/index.d.ts +86 -0
  9. package/dist/_members/core/render/compositionAudioAndDepth.d.ts +9 -3
  10. package/dist/_members/core/render/compositionRender.d.ts +14 -0
  11. package/dist/_members/core/render/index.d.ts +2 -2
  12. package/dist/_members/core/render/types.d.ts +4 -1
  13. package/dist/_members/plane/contract.d.ts +1 -1
  14. package/dist/_members/primitives/captions/Captions.d.ts +18 -1
  15. package/dist/_members/primitives/captions/index.d.ts +1 -1
  16. package/dist/animate.js +2 -2
  17. package/dist/authoring/build-tools.js +6 -6
  18. package/dist/authoring/context.js +5 -5
  19. package/dist/authoring/tools.js +5 -5
  20. package/dist/authoring.js +9 -9
  21. package/dist/{chunk-MYBK6DKF.js → chunk-2BVKNYRB.js} +1 -1
  22. package/dist/{chunk-2LUAUF46.js → chunk-46DGDPXR.js} +303 -47
  23. package/dist/{chunk-KY3QBG7O.js → chunk-4DM63RTI.js} +4 -4
  24. package/dist/{chunk-IHWC75AN.js → chunk-5E4YP44R.js} +1 -1
  25. package/dist/{chunk-MOUWO53S.js → chunk-7AUHC2X7.js} +1 -1
  26. package/dist/{chunk-AVTEPAWI.js → chunk-7RGDRDAR.js} +17 -13
  27. package/dist/{chunk-7XZW26U2.js → chunk-AONWKKXK.js} +1 -1
  28. package/dist/{chunk-O3SJTX6S.js → chunk-C6ONUJIO.js} +5 -5
  29. package/dist/{chunk-LO3DH776.js → chunk-CJMUTCJW.js} +2 -2
  30. package/dist/{chunk-CZOVEX2D.js → chunk-CR3G2JQP.js} +1 -1
  31. package/dist/{chunk-2PU2TSUG.js → chunk-D5E5XFHK.js} +1 -1
  32. package/dist/{chunk-6WQHVYJD.js → chunk-G7ZSFSQG.js} +1 -1
  33. package/dist/{chunk-7SXY6NFW.js → chunk-GAACUQAB.js} +4 -4
  34. package/dist/{chunk-4CNJGY7P.js → chunk-LPUQTBKU.js} +147 -27
  35. package/dist/{chunk-YKXOAGY3.js → chunk-P3DYA5NA.js} +8 -8
  36. package/dist/{chunk-Z6NY6MCL.js → chunk-RMLGMYEJ.js} +1 -1
  37. package/dist/{chunk-NLXQ7WTH.js → chunk-TPKPU4PE.js} +56 -38
  38. package/dist/{chunk-LZEX2GAY.js → chunk-UFNIB3JL.js} +144 -5
  39. package/dist/{chunk-ACURZCPN.js → chunk-UKKY7HWN.js} +1 -1
  40. package/dist/{chunk-M665ZQYB.js → chunk-VTM7NEJC.js} +38 -6
  41. package/dist/{chunk-FSA3UBVR.js → chunk-ZFW35DLG.js} +1 -1
  42. package/dist/{chunk-OVV35ZS2.js → chunk-ZIPGDGBR.js} +1 -1
  43. package/dist/component-runtime/render.js +2 -2
  44. package/dist/component-runtime.js +3 -3
  45. package/dist/compose.js +23 -23
  46. package/dist/composition/fonts.js +1 -1
  47. package/dist/composition/graphics-plan.js +3 -3
  48. package/dist/composition/ops.js +4 -4
  49. package/dist/composition/projection.js +3 -3
  50. package/dist/composition/validators.js +17 -3
  51. package/dist/composition-ops.js +8 -8
  52. package/dist/composition.js +19 -5
  53. package/dist/{context-AIUJG7R4.js → context-UAS5WJRB.js} +5 -5
  54. package/dist/core/render.js +6 -4
  55. package/dist/core.js +4 -4
  56. package/dist/mcp/adapter.js +2 -2
  57. package/dist/mcp/core.js +1 -1
  58. package/dist/mcp/scene-shot.js +2 -2
  59. package/dist/mcp/server.js +12 -12
  60. package/dist/mcp.js +12 -12
  61. package/dist/primitives/captions.js +195 -32
  62. package/dist/primitives/effects.js +4 -4
  63. package/dist/primitives/hero-text.js +2 -2
  64. package/dist/primitives/layouts.js +3 -3
  65. package/dist/primitives/scenes.js +4 -4
  66. package/dist/primitives/text.js +2 -2
  67. package/dist/primitives/ui-blocks.js +4 -4
  68. package/dist/primitives.js +23 -23
  69. package/dist/render-harness/cardFonts.js +2 -2
  70. package/dist/render-harness.js +12 -12
  71. package/package.json +12 -12
  72. package/dist/{chunk-RL5SO653.js → chunk-7SQOXIWR.js} +3 -3
  73. package/dist/{chunk-B3GSTQLR.js → chunk-VLJOIUHL.js} +6 -6
  74. package/dist/{chunk-RBWS5XHE.js → chunk-X34KW2SL.js} +4 -4
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  Composition
3
- } from "./chunk-LZEX2GAY.js";
3
+ } from "./chunk-UFNIB3JL.js";
4
4
  import {
5
5
  expandToLinkGroup
6
6
  } from "./chunk-KYRDKK2D.js";
@@ -132,6 +132,56 @@ function pruneSelectionInDraft(draft, removedClipIds) {
132
132
  draft.selectedOverlayId = patch.selectedOverlayId;
133
133
  }
134
134
 
135
+ // ../composition/src/ops/captionOps.ts
136
+ function createCaptionOps(set) {
137
+ return {
138
+ setCaptionStyle: (style) => {
139
+ set((draft) => {
140
+ const clean = Object.fromEntries(
141
+ Object.entries(style).filter(([, v]) => v !== void 0)
142
+ );
143
+ if (Object.keys(clean).length === 0) return;
144
+ const existing = draft.composition.captions;
145
+ if (existing) {
146
+ existing.style = { ...existing.style ?? {}, ...clean };
147
+ } else {
148
+ draft.composition.captions = { source: "authored", segments: [], style: clean };
149
+ }
150
+ });
151
+ },
152
+ setCaptionLayouts: (layouts) => {
153
+ set((draft) => {
154
+ if (!draft.composition.captions) throw new Error("setCaptionLayouts requires saved transcript captions");
155
+ if (layouts.length === 0) delete draft.composition.captions.layouts;
156
+ else draft.composition.captions.layouts = layouts;
157
+ });
158
+ },
159
+ setCaptionDepth: (zPlane) => {
160
+ set((draft) => {
161
+ const existing = draft.composition.captions;
162
+ if (!existing) return;
163
+ if (zPlane === "front") {
164
+ delete existing.zPlane;
165
+ } else {
166
+ existing.zPlane = zPlane;
167
+ }
168
+ });
169
+ },
170
+ setCaptions: (segments, style, derivedFrom) => {
171
+ set((draft) => {
172
+ const existing = draft.composition.captions;
173
+ const mergedStyle = style ? {
174
+ ...existing?.style ?? {},
175
+ ...Object.fromEntries(Object.entries(style).filter(([, v]) => v !== void 0))
176
+ } : existing?.style;
177
+ const styleField = mergedStyle && Object.keys(mergedStyle).length > 0 ? { style: mergedStyle } : {};
178
+ const depthField = existing?.zPlane ? { zPlane: existing.zPlane } : {};
179
+ draft.composition.captions = derivedFrom ? { source: "derived", from: derivedFrom, segments, ...styleField, ...depthField } : { source: "authored", segments, ...styleField, ...depthField };
180
+ });
181
+ }
182
+ };
183
+ }
184
+
135
185
  // ../composition/src/ops/compositionOps.ts
136
186
  function createCompositionOps(get, set) {
137
187
  return {
@@ -447,6 +497,7 @@ function createCompositionOps(get, set) {
447
497
  if (!Number.isFinite(start) || !Number.isFinite(end) || start >= end) return;
448
498
  const span = end - start;
449
499
  set((draft) => {
500
+ if (draft.composition.captions) delete draft.composition.captions.layouts;
450
501
  for (const track of draft.composition.tracks) {
451
502
  const next = [];
452
503
  for (const item of track.contents) {
@@ -581,6 +632,7 @@ function createCompositionOps(get, set) {
581
632
  });
582
633
  }
583
634
  if (draft.composition.captions) {
635
+ delete draft.composition.captions.layouts;
584
636
  for (const seg of draft.composition.captions.segments) {
585
637
  seg.words = seg.words.flatMap((w) => {
586
638
  const r = cutTimedRange(w.startMs, w.endMs, tStart * 1e3, tEnd * 1e3);
@@ -631,6 +683,7 @@ function createCompositionOps(get, set) {
631
683
  }
632
684
  }
633
685
  if (draft.composition.captions) {
686
+ delete draft.composition.captions.layouts;
634
687
  for (const segment of draft.composition.captions.segments) {
635
688
  for (const word of segment.words) {
636
689
  const shifted = shiftTimedRange(word.startMs, word.endMs, insertAt * 1e3, restored * 1e3);
@@ -667,45 +720,10 @@ function createCompositionOps(get, set) {
667
720
  }
668
721
  });
669
722
  },
670
- setCaptionStyle: (style) => {
671
- set((draft) => {
672
- const clean = Object.fromEntries(
673
- Object.entries(style).filter(([, v]) => v !== void 0)
674
- );
675
- if (Object.keys(clean).length === 0) return;
676
- const existing = draft.composition.captions;
677
- if (existing) {
678
- existing.style = { ...existing.style ?? {}, ...clean };
679
- } else {
680
- draft.composition.captions = { source: "authored", segments: [], style: clean };
681
- }
682
- });
683
- },
684
- setCaptionDepth: (zPlane) => {
685
- set((draft) => {
686
- const existing = draft.composition.captions;
687
- if (!existing) return;
688
- if (zPlane === "front") {
689
- delete existing.zPlane;
690
- } else {
691
- existing.zPlane = zPlane;
692
- }
693
- });
694
- },
695
- setCaptions: (segments, style, derivedFrom) => {
696
- set((draft) => {
697
- const existing = draft.composition.captions;
698
- const mergedStyle = style ? {
699
- ...existing?.style ?? {},
700
- ...Object.fromEntries(Object.entries(style).filter(([, v]) => v !== void 0))
701
- } : existing?.style;
702
- const styleField = mergedStyle && Object.keys(mergedStyle).length > 0 ? { style: mergedStyle } : {};
703
- const depthField = existing?.zPlane ? { zPlane: existing.zPlane } : {};
704
- draft.composition.captions = derivedFrom ? { source: "derived", from: derivedFrom, segments, ...styleField, ...depthField } : { source: "authored", segments, ...styleField, ...depthField };
705
- });
706
- },
723
+ ...createCaptionOps(set),
707
724
  ripple: (time, amount) => {
708
725
  set((draft) => {
726
+ if (draft.composition.captions) delete draft.composition.captions.layouts;
709
727
  for (const track of draft.composition.tracks) {
710
728
  for (const item of track.contents) {
711
729
  if (!("source" in item)) continue;
@@ -3,7 +3,7 @@ import {
3
3
  } from "./chunk-B7J5UUUV.js";
4
4
  import {
5
5
  captionFaceIssues
6
- } from "./chunk-AVTEPAWI.js";
6
+ } from "./chunk-7RGDRDAR.js";
7
7
 
8
8
  // ../composition/src/validators/opaqueId.ts
9
9
  import { z } from "zod";
@@ -11,6 +11,8 @@ var OpaqueId = z.string();
11
11
 
12
12
  // ../composition/src/validators/composition.ts
13
13
  import { z as z2 } from "zod";
14
+ import { sha256 } from "@noble/hashes/sha256";
15
+ import { bytesToHex } from "@noble/hashes/utils";
14
16
  var Format = z2.object({
15
17
  aspectRatio: z2.enum(["16:9", "9:16", "1:1", "4:5"]),
16
18
  fps: z2.union([z2.literal(24), z2.literal(25), z2.literal(30), z2.literal(60)]).optional(),
@@ -124,6 +126,7 @@ var CaptionRegion = Region.refine(
124
126
  "captions style.region must fit within the normalized frame"
125
127
  );
126
128
  var ClipFit = z2.enum(["cover", "contain"]);
129
+ var CaptionVisibility = z2.enum(["visible", "hidden"]);
127
130
  var PerClipEffect = z2.object({
128
131
  primitiveId: z2.string().min(1),
129
132
  params: z2.record(z2.string(), z2.unknown()).optional()
@@ -166,6 +169,9 @@ var TimelineClip = z2.object({
166
169
  position: Position.optional(),
167
170
  region: Region.optional(),
168
171
  fit: ClipFit.optional(),
172
+ captionVisibility: CaptionVisibility.optional().describe(
173
+ "Video media clip only. Hide native captions while this clip is the visible full-frame picture; caption words and timings remain stored and resume when another visible clip takes over."
174
+ ),
169
175
  effects: z2.array(PerClipEffect).optional(),
170
176
  transitionIn: Transition.optional(),
171
177
  linkGroupId: z2.string().min(1).optional(),
@@ -284,6 +290,101 @@ var CaptionStyle = z2.object({
284
290
  annotationColor: z2.string().optional(),
285
291
  maxWidthPercent: z2.number().positive().max(100, "captions style.maxWidthPercent is % of composition width (max 100)").optional()
286
292
  });
293
+ var CaptionLayoutStyle = CaptionStyle.pick({
294
+ fontFamily: true,
295
+ fontSize: true,
296
+ fontWeight: true,
297
+ color: true,
298
+ highlightColor: true,
299
+ backgroundColor: true,
300
+ plate: true,
301
+ textTransform: true,
302
+ emphasisColor: true,
303
+ emphasisScale: true,
304
+ emphasisFontFamily: true,
305
+ emphasisFontWeight: true,
306
+ emphasisTextTransform: true,
307
+ emphasisLetterSpacing: true,
308
+ emphasisFontStyle: true,
309
+ emphasisPlate: true,
310
+ emphasisPlateColor: true,
311
+ emphasisPlateTextColor: true,
312
+ emphasisPlateAltColor: true,
313
+ emphasisPlateAltTextColor: true,
314
+ emphasisPlateRadius: true,
315
+ emphasisPlatePadX: true,
316
+ emphasisPlatePadY: true,
317
+ annotationColor: true,
318
+ entrance: true,
319
+ entranceMs: true,
320
+ entranceStaggerMs: true,
321
+ exitMs: true
322
+ }).strict();
323
+ var CaptionLayoutInput = z2.object({
324
+ segmentIndex: z2.number().int().nonnegative(),
325
+ wordStartIndex: z2.number().int().nonnegative(),
326
+ wordEndIndex: z2.number().int().positive().describe("Exclusive word index in the saved caption segment."),
327
+ region: CaptionRegion,
328
+ scale: z2.number().min(0.25).max(4).optional(),
329
+ rotation: z2.number().min(-45).max(45).optional(),
330
+ style: CaptionLayoutStyle.optional(),
331
+ holdThroughWordIndex: z2.number().int().nonnegative().optional().describe(
332
+ "Inclusive saved-word index in this segment. The layout stays visible until that word ends."
333
+ ),
334
+ wordMarks: z2.array(z2.object({
335
+ wordIndex: z2.number().int().nonnegative(),
336
+ emphasis: z2.boolean().optional(),
337
+ annotate: z2.enum(["circle", "underline"]).nullable().optional()
338
+ }).strict()).optional()
339
+ }).strict();
340
+ var CaptionLayout = CaptionLayoutInput.extend({
341
+ wordSignature: z2.string().regex(/^[a-f0-9]{64}$/)
342
+ });
343
+ function captionSegmentSignature(words) {
344
+ const encoded = JSON.stringify(words.map((word) => ({
345
+ text: word.text,
346
+ startMs: word.startMs,
347
+ endMs: word.endMs,
348
+ emphasis: word.emphasis ?? null,
349
+ annotate: word.annotate ?? null
350
+ })));
351
+ return bytesToHex(sha256(new TextEncoder().encode(encoded)));
352
+ }
353
+ function captionLayoutIssues(segments, layouts, checkSignature = false) {
354
+ const issues = [];
355
+ const occupied = /* @__PURE__ */ new Set();
356
+ layouts.forEach((layout, index) => {
357
+ const words = segments[layout.segmentIndex]?.words;
358
+ if (!words) {
359
+ issues.push({ index, field: "segmentIndex", message: "caption layout segment does not exist" });
360
+ return;
361
+ }
362
+ if (layout.wordStartIndex >= layout.wordEndIndex || layout.wordEndIndex > words.length) {
363
+ issues.push({ index, field: "wordEndIndex", message: "caption layout word range is outside its segment" });
364
+ return;
365
+ }
366
+ const hold = layout.holdThroughWordIndex ?? layout.wordEndIndex - 1;
367
+ if (hold < layout.wordEndIndex - 1 || hold >= words.length) {
368
+ issues.push({ index, field: "holdThroughWordIndex", message: "caption layout hold must reference this or a later word in the same segment" });
369
+ }
370
+ const marked = /* @__PURE__ */ new Set();
371
+ for (const mark of layout.wordMarks ?? []) {
372
+ if (mark.wordIndex < layout.wordStartIndex || mark.wordIndex >= layout.wordEndIndex || marked.has(mark.wordIndex)) {
373
+ issues.push({ index, field: "wordMarks", message: "caption layout word marks must be unique words inside the layout range" });
374
+ }
375
+ marked.add(mark.wordIndex);
376
+ }
377
+ for (let word = layout.wordStartIndex; word < layout.wordEndIndex; word++) {
378
+ const key = `${layout.segmentIndex}:${word}`;
379
+ if (occupied.has(key)) issues.push({ index, field: "wordStartIndex", message: "caption layout word ranges must not overlap" });
380
+ occupied.add(key);
381
+ }
382
+ if (checkSignature && "wordSignature" in layout && layout.wordSignature !== captionSegmentSignature(words)) {
383
+ issues.push({ index, field: "wordSignature", message: "caption layout is stale for these saved transcript words; reapply setCaptionLayouts" });
384
+ }
385
+ });
386
+ return issues;
387
+ }
287
388
  var CaptionSegment = z2.object({
288
389
  words: z2.array(CaptionWord)
289
390
  });
@@ -294,18 +395,29 @@ var CompositionCaptions = z2.discriminatedUnion("source", [
294
395
  from: z2.object({ clipId: z2.string() }),
295
396
  segments: z2.array(CaptionSegment),
296
397
  style: CaptionStyle.optional(),
398
+ layouts: z2.array(CaptionLayout).optional(),
297
399
  zPlane: CaptionDepth
298
400
  }),
299
401
  z2.object({
300
402
  source: z2.literal("authored"),
301
403
  segments: z2.array(CaptionSegment),
302
404
  style: CaptionStyle.optional(),
405
+ layouts: z2.array(CaptionLayout).optional(),
303
406
  zPlane: CaptionDepth
304
407
  })
305
408
  ]).superRefine((captions, ctx) => {
306
- if (!captions.style) return;
307
- for (const issue of captionFaceIssues(captions.style)) {
308
- ctx.addIssue({ code: "custom", path: ["style", issue.field], message: issue.message });
409
+ if (captions.style) {
410
+ for (const issue of captionFaceIssues(captions.style)) {
411
+ ctx.addIssue({ code: "custom", path: ["style", issue.field], message: issue.message });
412
+ }
413
+ }
414
+ for (const issue of captionLayoutIssues(captions.segments, captions.layouts ?? [], true)) {
415
+ ctx.addIssue({ code: "custom", path: ["layouts", issue.index, issue.field], message: issue.message });
416
+ }
417
+ for (const [index, layout] of (captions.layouts ?? []).entries()) {
418
+ for (const issue of captionFaceIssues({ ...captions.style, ...layout.style })) {
419
+ ctx.addIssue({ code: "custom", path: ["layouts", index, "style", issue.field], message: issue.message });
420
+ }
309
421
  }
310
422
  });
311
423
  function captionsAreBehindSubject(captions) {
@@ -314,6 +426,18 @@ function captionsAreBehindSubject(captions) {
314
426
  function captionWords(c) {
315
427
  return c.segments.flatMap((s) => s.words);
316
428
  }
429
+ function captionVisibilityIssues(composition) {
430
+ const issues = [];
431
+ composition.tracks.forEach((track, trackIndex) => {
432
+ track.contents.forEach((item, contentIndex) => {
433
+ if (!("source" in item) || item.captionVisibility === void 0) return;
434
+ if (track.kind !== "video" || item.source.kind !== "media") {
435
+ issues.push({ trackIndex, contentIndex });
436
+ }
437
+ });
438
+ });
439
+ return issues;
440
+ }
317
441
  var Composition = z2.object({
318
442
  v: z2.literal(1, {
319
443
  error: (issue) => issue.input === void 0 ? 'Missing version envelope: add top-level "v": 1 to the composition.' : `Invalid version envelope: top-level "v" must be the literal 1 (got ${JSON.stringify(issue.input)}).`
@@ -332,7 +456,15 @@ var Composition = z2.object({
332
456
  accessibility: CompositionAccessibility.optional(),
333
457
  ownerOrgId: z2.string().optional(),
334
458
  acl: AccessControl.optional()
335
- }).strict();
459
+ }).strict().superRefine((composition, ctx) => {
460
+ for (const issue of captionVisibilityIssues(composition)) {
461
+ ctx.addIssue({
462
+ code: "custom",
463
+ path: ["tracks", issue.trackIndex, "contents", issue.contentIndex, "captionVisibility"],
464
+ message: "captionVisibility is supported only on video media clips"
465
+ });
466
+ }
467
+ });
336
468
  var CompositionAuthorKind = z2.enum(["user", "agent", "import"]);
337
469
  var CompositionVersion = z2.object({
338
470
  id: z2.string().min(1),
@@ -502,6 +634,7 @@ export {
502
634
  Region,
503
635
  CaptionRegion,
504
636
  ClipFit,
637
+ CaptionVisibility,
505
638
  PerClipEffect,
506
639
  KeyframeEasing,
507
640
  Keyframe,
@@ -515,10 +648,16 @@ export {
515
648
  Marker,
516
649
  CaptionWord,
517
650
  CaptionStyle,
651
+ CaptionLayoutStyle,
652
+ CaptionLayoutInput,
653
+ CaptionLayout,
654
+ captionSegmentSignature,
655
+ captionLayoutIssues,
518
656
  CaptionSegment,
519
657
  CompositionCaptions,
520
658
  captionsAreBehindSubject,
521
659
  captionWords,
660
+ captionVisibilityIssues,
522
661
  Composition,
523
662
  CompositionAuthorKind,
524
663
  CompositionVersion,
@@ -5,7 +5,7 @@ import {
5
5
  bundledFontFamilyFaces,
6
6
  isGenericOnlyStack,
7
7
  parseFontStack
8
- } from "./chunk-AVTEPAWI.js";
8
+ } from "./chunk-7RGDRDAR.js";
9
9
 
10
10
  // ../execution/render-harness/src/cardFonts.ts
11
11
  import parseCss from "css-tree/parser";
@@ -3,24 +3,29 @@ import {
3
3
  projectMediaPlaybackSpans,
4
4
  sliceTranscriptWords,
5
5
  splitIntoSentences
6
- } from "./chunk-4CNJGY7P.js";
6
+ } from "./chunk-LPUQTBKU.js";
7
7
  import {
8
8
  createHeadlessStore,
9
9
  findClipById,
10
10
  findMarkerById,
11
11
  findTrackContainingClipId
12
- } from "./chunk-NLXQ7WTH.js";
12
+ } from "./chunk-TPKPU4PE.js";
13
13
  import {
14
+ CaptionLayoutInput,
14
15
  CaptionSegment,
15
16
  CaptionStyle,
16
17
  Composition,
18
+ CompositionCaptions,
17
19
  Format,
18
20
  Marker,
19
21
  PerClipEffect,
20
22
  TimelineClip,
21
23
  Track,
22
- Transition
23
- } from "./chunk-LZEX2GAY.js";
24
+ Transition,
25
+ captionLayoutIssues,
26
+ captionSegmentSignature,
27
+ captionVisibilityIssues
28
+ } from "./chunk-UFNIB3JL.js";
24
29
  import {
25
30
  ReframeSegment
26
31
  } from "./chunk-B7J5UUUV.js";
@@ -30,7 +35,7 @@ import {
30
35
  } from "./chunk-CL5Y6V6K.js";
31
36
  import {
32
37
  introducedCaptionFaceIssues
33
- } from "./chunk-AVTEPAWI.js";
38
+ } from "./chunk-7RGDRDAR.js";
34
39
 
35
40
  // ../composition-ops/src/index.ts
36
41
  import { z } from "zod";
@@ -479,6 +484,9 @@ var CompositionOp = z.discriminatedUnion("type", [
479
484
  z.object({ type: z.literal("setCaptions"), segments: z.array(CaptionSegment), style: CaptionStyle.partial().optional() }).strict().describe(
480
485
  "Replace the caption SEGMENTS wholesale \u2014 author the word-timed caption layer directly (each segment = one sentence/page of `words` with timeline-ms `startMs`/`endMs`, optional `emphasis`). This is how a client with its OWN word timings (e.g. a generated VO take) gets word-synced captions without a source transcript; the compose lane's transcript slice writes the same layer. `style`, when given, merges with setCaptionStyle semantics; an omitted style keeps the existing one. Renderer paginates within a segment and never pools words across two \u2014 keep segments sentence-bounded."
481
486
  ),
487
+ z.object({ type: z.literal("setCaptionLayouts"), layouts: z.array(CaptionLayoutInput) }).strict().describe(
488
+ "Replace native caption placements without changing transcript words or timings. Each layout references a half-open word-index range in one saved segment; a one-word range is valid. region places that group independently, optional bounded scale/rotation/style changes its look, and holdThroughWordIndex keeps it visible through a later saved word in the same segment. All words in a placed range occupy one page; rendering refuses a range that cannot fit its box, so enlarge region or split the range. The box reserves horizontal space of 1.6 font sizes and vertical space of 0.8 font sizes for padding, with at most three lines. Unreferenced words keep the default caption layout. Word ranges must not overlap. Reapply after caption regeneration or timeline cuts."
489
+ ),
482
490
  z.object({ type: z.literal("applyMotionPreset"), clipId: z.string(), preset: z.string(), params: z.object({ enterMs: z.number().optional(), exitMs: z.number().optional(), staggerMs: z.number().optional() }).strict().optional() }).strict().describe(
483
491
  "Shell a STATIC `card` clip with a frame-driven MOTION preset \u2014 the public card-shelling op (the layout renders VERBATIM; only the container animates in/out). The server wraps the card in the chosen `preset` (fade-up / slide-in-left / slide-in-right / scale-pop / stagger-reveal), transpiles it, mints it as a LIVE component, and swaps the clip source to that component (the bakeless live lane \u2014 no box). `params` tunes timing (`enterMs`/`exitMs`, `staggerMs` for stagger-reveal). The target must be a static card (components already carry motion; library overlays already move); re-run overwrites idempotently ONLY while the target is still a card (an uncompilable shell); once shelled, the clip is a COMPONENT \u2014 to change its motion, remove and re-add the card, then re-shell. Honored under `dry_run` (validates preset + target without transpiling or persisting). ORDERING: the preset resolves its target against the PERSISTED composition \u2014 a card added in the SAME batch is not yet visible to it. Persist the clip.add batch first, then shell in a following call."
484
492
  ),
@@ -588,7 +596,12 @@ function applyCaptionOpToStore(store, op) {
588
596
  function applyOp(composition, op) {
589
597
  const store = createHeadlessStore(composition);
590
598
  applyOpToStore(store, op);
591
- return store.getState().composition;
599
+ const next = store.getState().composition;
600
+ const issue = captionVisibilityIssues(next)[0];
601
+ if (issue) {
602
+ throw new Error(`tracks[${issue.trackIndex}].contents[${issue.contentIndex}].captionVisibility: captionVisibility is supported only on video media clips`);
603
+ }
604
+ return next;
592
605
  }
593
606
  function applyOpToStore(store, op) {
594
607
  const composition = store.getState().composition;
@@ -659,6 +672,20 @@ function applyOpToStore(store, op) {
659
672
  case "setCaptions":
660
673
  applyCaptionOpToStore(store, op);
661
674
  break;
675
+ case "setCaptionLayouts": {
676
+ const captions = composition.captions;
677
+ if (!captions) throw new Error("setCaptionLayouts requires saved transcript captions");
678
+ const issue = captionLayoutIssues(captions.segments, op.layouts)[0];
679
+ if (issue) throw new Error(`setCaptionLayouts.layouts[${issue.index}].${issue.field}: ${issue.message}`);
680
+ const layouts = op.layouts.map((layout) => ({
681
+ ...layout,
682
+ wordSignature: captionSegmentSignature(captions.segments[layout.segmentIndex].words)
683
+ }));
684
+ const parsed = CompositionCaptions.safeParse({ ...captions, layouts });
685
+ if (!parsed.success) throw new Error(`setCaptionLayouts: ${parsed.error.issues[0]?.message}`);
686
+ store.setCaptionLayouts(layouts);
687
+ break;
688
+ }
662
689
  case "applyMotionPreset":
663
690
  throw new Error(
664
691
  `applyOp: 'applyMotionPreset' is route-materialized \u2014 it needs a director transpile of the generated motion shell and is rewritten to a clip.update at /composition/apply before reaching the pure engine.`
@@ -795,6 +822,11 @@ function invertOp(before, op) {
795
822
  }
796
823
  case "setCaptionDepth":
797
824
  return { type: "setCaptionDepth", zPlane: before.captions?.zPlane ?? "front" };
825
+ case "setCaptionLayouts":
826
+ return {
827
+ type: "setCaptionLayouts",
828
+ layouts: (before.captions?.layouts ?? []).map(({ wordSignature: _signature, ...layout }) => layout)
829
+ };
798
830
  case "addTrack":
799
831
  if (op.id === void 0) {
800
832
  throw new Error("invertOp(addTrack): no pinned `id` \u2014 cannot target the minted track");
@@ -5,7 +5,7 @@ import {
5
5
  isOk,
6
6
  opById,
7
7
  safeParse
8
- } from "./chunk-2LUAUF46.js";
8
+ } from "./chunk-46DGDPXR.js";
9
9
 
10
10
  // ../mcp/src/wait.ts
11
11
  var defaultSleep = (ms) => new Promise((r) => setTimeout(r, ms));
@@ -2,7 +2,7 @@ import {
2
2
  selectAllClips,
3
3
  selectAllOverlays,
4
4
  selectSceneMarkers
5
- } from "./chunk-NLXQ7WTH.js";
5
+ } from "./chunk-TPKPU4PE.js";
6
6
 
7
7
  // ../authoring/src/context/transcript.ts
8
8
  var MAX_TRANSCRIPT_CHARS = 6e4;
@@ -26,7 +26,7 @@ import {
26
26
  stagedBrowserLocator,
27
27
  verifyLocalStagedBrowser,
28
28
  vp9AlphaFfmpegOverride
29
- } from "../chunk-7XZW26U2.js";
29
+ } from "../chunk-AONWKKXK.js";
30
30
  import "../chunk-WB2VFDQZ.js";
31
31
  import "../chunk-EXY4JJPH.js";
32
32
  import "../chunk-DAARYRNT.js";
@@ -35,7 +35,7 @@ import "../chunk-JWMWVP64.js";
35
35
  import "../chunk-EC6VFQE6.js";
36
36
  import "../chunk-CL5Y6V6K.js";
37
37
  import "../chunk-2L7PYYJK.js";
38
- import "../chunk-AVTEPAWI.js";
38
+ import "../chunk-7RGDRDAR.js";
39
39
  import "../chunk-2ESYSVXG.js";
40
40
  export {
41
41
  COMPONENT_BROWSER_CLEANUP_TIMEOUT_MS,
@@ -7,7 +7,7 @@ import {
7
7
  sourceIdentityForBake,
8
8
  startComponentAssetServer,
9
9
  verifyComponentOutput
10
- } from "./chunk-MYBK6DKF.js";
10
+ } from "./chunk-2BVKNYRB.js";
11
11
  import {
12
12
  COMPONENT_MEDIA_HEADROOM,
13
13
  COMPONENT_MEDIA_TIERS,
@@ -52,7 +52,7 @@ import {
52
52
  stagedBrowserLocator,
53
53
  verifyLocalStagedBrowser,
54
54
  vp9AlphaFfmpegOverride
55
- } from "./chunk-7XZW26U2.js";
55
+ } from "./chunk-AONWKKXK.js";
56
56
  import "./chunk-WB2VFDQZ.js";
57
57
  import {
58
58
  bundleIdentityFields,
@@ -131,7 +131,7 @@ import {
131
131
  } from "./chunk-EC6VFQE6.js";
132
132
  import "./chunk-CL5Y6V6K.js";
133
133
  import "./chunk-2L7PYYJK.js";
134
- import "./chunk-AVTEPAWI.js";
134
+ import "./chunk-7RGDRDAR.js";
135
135
  import "./chunk-2ESYSVXG.js";
136
136
  export {
137
137
  CANVAS2D_COMPONENT_MANIFEST_V2,
package/dist/compose.js CHANGED
@@ -5,42 +5,42 @@ import "./chunk-GULDS3VE.js";
5
5
  import {
6
6
  isContrastCritical
7
7
  } from "./chunk-QDBVYZSF.js";
8
- import {
9
- catalog
10
- } from "./chunk-RBWS5XHE.js";
11
- import "./chunk-DLF4WVSE.js";
12
- import "./chunk-U4V4FZGE.js";
13
- import {
14
- catalog as catalog3
15
- } from "./chunk-FVDFPJHT.js";
16
8
  import {
17
9
  catalog as catalog6
18
- } from "./chunk-B3GSTQLR.js";
19
- import {
20
- catalog as catalog5
21
- } from "./chunk-RAXHCRVV.js";
10
+ } from "./chunk-VLJOIUHL.js";
22
11
  import {
23
12
  catalog as catalog2
24
- } from "./chunk-7SXY6NFW.js";
13
+ } from "./chunk-GAACUQAB.js";
25
14
  import {
26
15
  catalog as catalog8
27
- } from "./chunk-RL5SO653.js";
28
- import "./chunk-LT4PWSR2.js";
29
- import "./chunk-FWSJLHSV.js";
30
- import "./chunk-ZFEXATWN.js";
16
+ } from "./chunk-7SQOXIWR.js";
31
17
  import {
32
18
  catalog as catalog4
33
- } from "./chunk-CZOVEX2D.js";
34
- import "./chunk-XHGZ2E77.js";
19
+ } from "./chunk-CR3G2JQP.js";
35
20
  import {
36
21
  catalog as catalog7
37
- } from "./chunk-O3SJTX6S.js";
38
- import "./chunk-QGAWDLYX.js";
22
+ } from "./chunk-C6ONUJIO.js";
39
23
  import "./chunk-S5H6GH6M.js";
40
24
  import "./chunk-QAGGJHAF.js";
41
25
  import "./chunk-MFHXCHRI.js";
26
+ import {
27
+ catalog
28
+ } from "./chunk-X34KW2SL.js";
29
+ import "./chunk-DLF4WVSE.js";
30
+ import "./chunk-U4V4FZGE.js";
31
+ import "./chunk-LT4PWSR2.js";
32
+ import "./chunk-FWSJLHSV.js";
33
+ import {
34
+ catalog as catalog3
35
+ } from "./chunk-FVDFPJHT.js";
36
+ import "./chunk-QGAWDLYX.js";
37
+ import {
38
+ catalog as catalog5
39
+ } from "./chunk-RAXHCRVV.js";
40
+ import "./chunk-XHGZ2E77.js";
41
+ import "./chunk-ZFEXATWN.js";
42
42
  import "./chunk-W2245F6G.js";
43
- import "./chunk-2PU2TSUG.js";
43
+ import "./chunk-D5E5XFHK.js";
44
44
  import "./chunk-XV2K3AQT.js";
45
45
  import {
46
46
  getPrimitiveSubtextParam,
@@ -58,7 +58,7 @@ import {
58
58
  } from "./chunk-VG2ND6PX.js";
59
59
  import "./chunk-CL5Y6V6K.js";
60
60
  import "./chunk-2L7PYYJK.js";
61
- import "./chunk-AVTEPAWI.js";
61
+ import "./chunk-7RGDRDAR.js";
62
62
  import "./chunk-2ESYSVXG.js";
63
63
 
64
64
  // ../execution/compose/src/primitiveRegistry.ts
@@ -34,7 +34,7 @@ import {
34
34
  resolveCaptionFaces,
35
35
  unrenderableFaceField,
36
36
  vendoredFontCatalog
37
- } from "../chunk-AVTEPAWI.js";
37
+ } from "../chunk-7RGDRDAR.js";
38
38
  import "../chunk-2ESYSVXG.js";
39
39
  export {
40
40
  ALIASED_FONT_FAMILIES,
@@ -20,17 +20,17 @@ import {
20
20
  PlanRect,
21
21
  StyleRole,
22
22
  ZPlane
23
- } from "../chunk-LZEX2GAY.js";
23
+ } from "../chunk-UFNIB3JL.js";
24
24
  import "../chunk-WPZKOVYF.js";
25
25
  import "../chunk-B7J5UUUV.js";
26
26
  import {
27
27
  placeInLegalArea,
28
28
  resolveBrandRefsDeep
29
- } from "../chunk-Z6NY6MCL.js";
29
+ } from "../chunk-RMLGMYEJ.js";
30
30
  import "../chunk-WREYUH2E.js";
31
31
  import "../chunk-ILO4NEZ3.js";
32
32
  import "../chunk-VG2ND6PX.js";
33
- import "../chunk-AVTEPAWI.js";
33
+ import "../chunk-7RGDRDAR.js";
34
34
  import "../chunk-2ESYSVXG.js";
35
35
 
36
36
  // ../composition/src/graphics-plan/solve.ts
@@ -20,14 +20,14 @@ import {
20
20
  selectMediaClipAt,
21
21
  selectSceneMarkers,
22
22
  selectTracksByKind
23
- } from "../chunk-NLXQ7WTH.js";
24
- import "../chunk-LO3DH776.js";
25
- import "../chunk-LZEX2GAY.js";
23
+ } from "../chunk-TPKPU4PE.js";
24
+ import "../chunk-CJMUTCJW.js";
25
+ import "../chunk-UFNIB3JL.js";
26
26
  import "../chunk-WPZKOVYF.js";
27
27
  import "../chunk-B7J5UUUV.js";
28
28
  import "../chunk-KYRDKK2D.js";
29
29
  import "../chunk-WREYUH2E.js";
30
- import "../chunk-AVTEPAWI.js";
30
+ import "../chunk-7RGDRDAR.js";
31
31
  import "../chunk-2ESYSVXG.js";
32
32
  export {
33
33
  compositionWithSourceClip,