@cueframe/kernel 0.2.57 → 0.2.58

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 (98) hide show
  1. package/dist/THIRD_PARTY_NOTICES.md +111 -0
  2. package/dist/_members/component-runtime/template/bind.d.ts +22 -0
  3. package/dist/_members/component-runtime/template/bind.d.ts.map +1 -0
  4. package/dist/_members/component-runtime/template/extract.d.ts +31 -0
  5. package/dist/_members/component-runtime/template/extract.d.ts.map +1 -0
  6. package/dist/_members/component-runtime/template/index.d.ts +5 -0
  7. package/dist/_members/component-runtime/template/index.d.ts.map +1 -0
  8. package/dist/_members/component-runtime/template/schema.d.ts +1580 -0
  9. package/dist/_members/component-runtime/template/schema.d.ts.map +1 -0
  10. package/dist/_members/component-runtime/template/testing/fixtures.d.ts +33 -0
  11. package/dist/_members/component-runtime/template/testing/fixtures.d.ts.map +1 -0
  12. package/dist/_members/component-runtime/template/testing/index.d.ts +3 -0
  13. package/dist/_members/component-runtime/template/testing/index.d.ts.map +1 -0
  14. package/dist/_members/component-runtime/template/testing/templateApplyContract.d.ts +54 -0
  15. package/dist/_members/component-runtime/template/testing/templateApplyContract.d.ts.map +1 -0
  16. package/dist/_members/composition/primitive-catalog/index.d.ts +2 -1
  17. package/dist/_members/composition/primitive-catalog/types.d.ts +3 -1
  18. package/dist/_members/composition/validators/brandKit.d.ts +7 -0
  19. package/dist/_members/composition/validators/brandKitBody.d.ts +543 -0
  20. package/dist/_members/errors/registry.d.ts +36 -0
  21. package/dist/_members/mcp/server.d.ts +5 -2
  22. package/dist/_members/primitives/backgrounds/dynamic-grid/config.d.ts +1 -1
  23. package/dist/_members/primitives/effects/blur/config.d.ts +1 -1
  24. package/dist/_members/primitives/effects/color-grade/config.d.ts +2 -2
  25. package/dist/_members/primitives/effects/grain/config.d.ts +3 -3
  26. package/dist/_members/primitives/effects/particles/config.d.ts +2 -2
  27. package/dist/_members/primitives/effects/stage-camera/config.d.ts +6 -6
  28. package/dist/_members/primitives/effects/vignette/config.d.ts +1 -1
  29. package/dist/_members/primitives/layouts/flex-layout/config.d.ts +3 -3
  30. package/dist/_members/primitives/layouts/picture-in-picture/config.d.ts +2 -2
  31. package/dist/_members/primitives/lib/types.d.ts +1 -1
  32. package/dist/_members/primitives/scenes/device-showcase/config.d.ts +7 -7
  33. package/dist/_members/primitives/text/cinematic-title/config.d.ts +3 -3
  34. package/dist/_members/primitives/text/karaoke/config.d.ts +1 -1
  35. package/dist/_members/primitives/text/kinetic-type/config.d.ts +3 -3
  36. package/dist/_members/primitives/text/stat-counter/config.d.ts +1 -1
  37. package/dist/_members/primitives/text/text-effect/config.d.ts +2 -2
  38. package/dist/_members/primitives/transitions/grid-pixelate-wipe/config.d.ts +1 -1
  39. package/dist/_members/primitives/ui-blocks/badge-pop/config.d.ts +3 -3
  40. package/dist/_members/primitives/ui-blocks/terminal-simulator/config.d.ts +2 -2
  41. package/dist/_members/primitives/ui-blocks/toast-notification/config.d.ts +2 -2
  42. package/dist/authoring/build-tools.js +5 -4
  43. package/dist/authoring/context.js +2 -1
  44. package/dist/authoring/tools.js +4 -3
  45. package/dist/authoring.js +7 -6
  46. package/dist/{chunk-R7G2PNEZ.js → chunk-2L33CKIY.js} +11 -13
  47. package/dist/{chunk-N54EGUVM.js → chunk-3VLDQE26.js} +1 -1
  48. package/dist/{chunk-7AZLFN26.js → chunk-4NIJWK45.js} +20 -12
  49. package/dist/{chunk-KSRFYFUZ.js → chunk-5T655LYZ.js} +1 -1
  50. package/dist/{chunk-RERR5JLM.js → chunk-6UZYUWBX.js} +1 -1
  51. package/dist/{chunk-LBN5KQC6.js → chunk-7EDLPIUN.js} +3 -3
  52. package/dist/{chunk-MZB4ZXYW.js → chunk-7NIOFHMH.js} +1 -1
  53. package/dist/{chunk-E63UEO5C.js → chunk-AK5QBHOB.js} +6 -0
  54. package/dist/{chunk-BDFAA747.js → chunk-GQ6XGAFB.js} +1 -1
  55. package/dist/chunk-IG3YAOUZ.js +418 -0
  56. package/dist/{chunk-Z4G3VM3H.js → chunk-JN3GF36B.js} +1 -1
  57. package/dist/{chunk-DV7ZFVL4.js → chunk-MFMR3LHO.js} +1 -1
  58. package/dist/{chunk-JPMVNMF3.js → chunk-MNO6AO2J.js} +1 -1
  59. package/dist/{chunk-VXGA7LQQ.js → chunk-NVXSKKRN.js} +2 -2
  60. package/dist/chunk-PWWZTY3G.js +254 -0
  61. package/dist/chunk-REST55RC.js +366 -0
  62. package/dist/chunk-TR53N6HB.js +196 -0
  63. package/dist/{chunk-4PV3UYJF.js → chunk-UXRSEMJJ.js} +201 -201
  64. package/dist/component-runtime/template/testing.d.ts +1 -0
  65. package/dist/component-runtime/template/testing.js +380 -0
  66. package/dist/component-runtime/template.d.ts +1 -0
  67. package/dist/component-runtime/template.js +99 -0
  68. package/dist/compose.js +20 -20
  69. package/dist/composition/floors.js +1 -1
  70. package/dist/composition/ops.js +2 -1
  71. package/dist/composition/primitive-catalog.js +3 -1
  72. package/dist/composition/projection.js +2 -2
  73. package/dist/composition/validators/brand-kit-body.d.ts +1 -0
  74. package/dist/composition/validators/brand-kit-body.js +17 -0
  75. package/dist/composition/validators.js +10 -6
  76. package/dist/composition-ops.js +7 -6
  77. package/dist/composition.js +12 -8
  78. package/dist/{context-6HPUO36M.js → context-4WSFPRLX.js} +2 -1
  79. package/dist/core/render.js +4 -4
  80. package/dist/core/select.js +2 -2
  81. package/dist/core.js +4 -4
  82. package/dist/director/ai-sdk.js +3 -3
  83. package/dist/director/domain.js +2 -2
  84. package/dist/director.js +3 -3
  85. package/dist/errors.js +1 -1
  86. package/dist/mcp/adapter.js +3 -3
  87. package/dist/mcp/server.js +3 -26
  88. package/dist/mcp.js +3 -26
  89. package/dist/nle-export.js +1 -1
  90. package/dist/primitives/captions.js +3 -3
  91. package/dist/primitives.js +18 -18
  92. package/dist/render-harness/cardAssetAdmission.js +2 -2
  93. package/dist/render-harness/cardFonts.js +2 -2
  94. package/dist/render-harness/renderInputAssets.js +2 -2
  95. package/dist/render-harness.js +8 -8
  96. package/dist/src-ZP6727AA.js +70 -0
  97. package/package.json +22 -10
  98. package/dist/chunk-AXRSCHQX.js +0 -661
@@ -0,0 +1,196 @@
1
+ import {
2
+ MotifMotionPreset
3
+ } from "./chunk-PWWZTY3G.js";
4
+ import {
5
+ GraphicRole
6
+ } from "./chunk-UFD4EZHM.js";
7
+
8
+ // ../composition/src/validators/brandKitBody.ts
9
+ import { z } from "zod";
10
+ var BrandFontField = z.object({
11
+ fontFamily: z.string().min(1, "fontFamily: non-empty string"),
12
+ fontWeight: z.number()
13
+ });
14
+ var BrandMotifBody = z.discriminatedUnion("kind", [
15
+ z.object({
16
+ kind: z.literal("card"),
17
+ html: z.string().min(1, "motifs[].body.html: non-empty string"),
18
+ tokens: z.record(z.string(), z.string()).optional()
19
+ }),
20
+ z.object({
21
+ kind: z.literal("overlay"),
22
+ primitiveId: z.string().min(1),
23
+ params: z.record(z.string(), z.unknown()).optional()
24
+ }),
25
+ z.object({
26
+ kind: z.literal("component"),
27
+ componentId: z.string().min(1),
28
+ props: z.record(z.string(), z.unknown()).optional()
29
+ })
30
+ ]);
31
+ var BrandMotif = z.object({
32
+ id: z.string().min(1).max(40),
33
+ observed: z.string().min(1).max(500),
34
+ body: BrandMotifBody,
35
+ motion: z.object({
36
+ preset: MotifMotionPreset,
37
+ enterMs: z.number().int().min(0).max(5e3).optional(),
38
+ exitMs: z.number().int().min(0).max(5e3).optional(),
39
+ staggerMs: z.number().int().min(0).max(2e3).optional(),
40
+ confidence: z.enum(["high", "low"]).optional()
41
+ }).optional(),
42
+ role: z.enum(GraphicRole.options),
43
+ zone: z.object({ x: z.number(), y: z.number(), w: z.number(), h: z.number() }).optional(),
44
+ evidence: z.array(z.number().int().min(0)).max(24).optional(),
45
+ diffScore: z.number().min(0).max(1).optional(),
46
+ coverage: z.enum(["reproduced", "approximated", "skipped"]),
47
+ skipReason: z.string().max(300).optional()
48
+ });
49
+ var BrandAudioPaletteRef = z.string().min(1);
50
+ var BrandAudioSchema = z.object({
51
+ sfxIntensity: z.enum(["off", "light", "standard", "dense"]).optional(),
52
+ musicEnabled: z.boolean().optional(),
53
+ musicMood: z.string().max(300).optional(),
54
+ sonicLogo: z.object({
55
+ ref: BrandAudioPaletteRef,
56
+ placement: z.enum(["intro", "outro", "both"]),
57
+ offsetMs: z.number().int().min(0).max(1e4).optional()
58
+ }).optional().describe(
59
+ "A brand sound signature bound to the intro/outro bookend. `ref` = a palette reference; bound only when the kit carries the matching bumper (intro/outro), else a non-blocking advisory."
60
+ ),
61
+ uiSoundSet: z.object({
62
+ tick: BrandAudioPaletteRef.optional(),
63
+ pop: BrandAudioPaletteRef.optional(),
64
+ impact: BrandAudioPaletteRef.optional(),
65
+ confirm: BrandAudioPaletteRef.optional(),
66
+ error: BrandAudioPaletteRef.optional(),
67
+ whoosh: BrandAudioPaletteRef.optional(),
68
+ riser: BrandAudioPaletteRef.optional()
69
+ }).optional().describe(
70
+ "Per-family SFX overrides (the brand's own tick/pop/\u2026). Highest-priority tier over the org palette + pack default in the compose SFX draw."
71
+ ),
72
+ bedStyle: z.object({
73
+ bedId: BrandAudioPaletteRef.optional(),
74
+ genre: z.string().max(120).optional(),
75
+ bpmRange: z.object({ min: z.number(), max: z.number() }).optional()
76
+ }).optional().describe(
77
+ "Preferred music bed (`bedId`) or a style hint (`genre` / `bpmRange`). Surfaced for driving agents; compose does not auto-select a pack bed."
78
+ ),
79
+ ambiencePreference: z.object({ ref: BrandAudioPaletteRef }).nullable().optional().describe(
80
+ "Preferred ambience bed (`{ref}`) or explicit null (opt-out). Surfaced for driving agents; compose has no ambience auto-placement."
81
+ )
82
+ });
83
+ var BrandIntroOutroBody = (field) => z.object({
84
+ kind: z.enum(["card", "video"]),
85
+ src: z.string().min(1, `${field}.src: non-empty string`).optional(),
86
+ title: z.string().optional(),
87
+ subtitle: z.string().optional(),
88
+ durationMs: z.number().optional()
89
+ });
90
+ var BrandKitBodyShape = {
91
+ kitId: z.string().min(1, "kitId: non-empty string required").max(200, "kitId: exceeds max length of 200").refine((v) => v.trim().length > 0, "kitId: non-empty string required"),
92
+ name: z.string().min(1, "name: non-empty string required").max(200, "name: exceeds max length of 200").refine((v) => v.trim().length > 0, "name: non-empty string required"),
93
+ description: z.string().max(2e3).optional(),
94
+ tagline: z.string().max(500).optional(),
95
+ secondaryTagline: z.string().max(500).optional(),
96
+ colors: z.object({
97
+ primary: z.string().min(1, "colors.primary: non-empty string"),
98
+ secondary: z.string().min(1, "colors.secondary: non-empty string"),
99
+ accent: z.string().min(1, "colors.accent: non-empty string"),
100
+ background: z.string().min(1, "colors.background: non-empty string"),
101
+ text: z.string().min(1, "colors.text: non-empty string")
102
+ }),
103
+ extraColors: z.array(z.object({
104
+ hex: z.string().min(1, "extraColors[].hex: non-empty string"),
105
+ label: z.string().min(1, "extraColors[].label: non-empty string"),
106
+ usage: z.string().optional()
107
+ })).optional(),
108
+ headingFont: BrandFontField,
109
+ bodyFont: BrandFontField,
110
+ voiceGuidelines: z.string().max(5e3).optional(),
111
+ syncCaptions: z.boolean().optional(),
112
+ captionProfile: z.object({
113
+ fontFamily: z.string().optional(),
114
+ fontSize: z.number().optional(),
115
+ fontWeight: z.number().optional(),
116
+ color: z.string().optional(),
117
+ highlightColor: z.string().optional(),
118
+ backgroundColor: z.string().optional(),
119
+ plate: z.enum(["glass", "solid", "none"]).optional(),
120
+ position: z.enum(["top", "center", "bottom"]).optional(),
121
+ textTransform: z.enum(["none", "uppercase"]).optional(),
122
+ entrance: z.enum(["none", "fade", "word-pop", "stagger-up"]).optional(),
123
+ entranceMs: z.number().optional(),
124
+ entranceStaggerMs: z.number().optional(),
125
+ exitMs: z.number().optional(),
126
+ emphasisColor: z.string().optional(),
127
+ emphasisScale: z.number().optional(),
128
+ emphasisFontFamily: z.string().optional(),
129
+ emphasisFontWeight: z.number().optional(),
130
+ emphasisFontStyle: z.enum(["normal", "italic"]).optional(),
131
+ emphasisTextTransform: z.enum(["none", "uppercase"]).optional(),
132
+ emphasisLetterSpacing: z.number().optional(),
133
+ emphasisPlate: z.boolean().optional(),
134
+ emphasisPlateColor: z.string().optional(),
135
+ emphasisPlateTextColor: z.string().optional()
136
+ }).optional(),
137
+ motion: z.object({
138
+ duration: z.number().optional(),
139
+ easing: z.union([
140
+ z.enum(["linear", "ease", "ease-in", "ease-out", "ease-in-out"]),
141
+ z.object({
142
+ kind: z.literal("cubic-bezier"),
143
+ x1: z.number(),
144
+ y1: z.number(),
145
+ x2: z.number(),
146
+ y2: z.number()
147
+ })
148
+ ]).optional(),
149
+ entrance: z.enum(["none", "fade", "word-pop", "stagger-up"]).optional(),
150
+ staggerMs: z.number().optional(),
151
+ exitMs: z.number().optional()
152
+ }).optional(),
153
+ logo: z.object({
154
+ src: z.string().min(1, "logo.src: non-empty string"),
155
+ alt: z.string().optional(),
156
+ placement: z.enum(["tl", "tr", "bl", "br"]).optional(),
157
+ sizePct: z.number().optional()
158
+ }).optional(),
159
+ intro: BrandIntroOutroBody("intro").optional(),
160
+ outro: BrandIntroOutroBody("outro").optional(),
161
+ watermark: z.object({
162
+ src: z.string().min(1, "watermark.src: non-empty string"),
163
+ opacity: z.number().min(0).max(1).optional(),
164
+ placement: z.enum(["tl", "tr", "bl", "br"]).optional()
165
+ }).optional(),
166
+ spacing: z.object({
167
+ base: z.number().optional(),
168
+ lg: z.number().optional(),
169
+ sm: z.number().optional()
170
+ }).optional(),
171
+ sizing: z.object({
172
+ fontSizeHeading: z.number().optional(),
173
+ fontSizeBody: z.number().optional(),
174
+ fontSizeLabel: z.number().optional(),
175
+ borderRadius: z.number().optional()
176
+ }).optional(),
177
+ audio: BrandAudioSchema.optional(),
178
+ motifs: z.array(BrandMotif).max(24).optional(),
179
+ styleRubric: z.string().max(4e3).optional(),
180
+ density: z.object({
181
+ graphicsPerMin: z.number().min(0).max(60),
182
+ maxSimultaneous: z.number().int().min(1).max(6)
183
+ }).optional(),
184
+ inspiration: z.object({
185
+ sources: z.array(z.string()).min(1).max(24),
186
+ extractedAt: z.number(),
187
+ extractionVersion: z.string().min(1).max(60)
188
+ }).optional()
189
+ };
190
+ var BrandKitBodySchema = z.strictObject(BrandKitBodyShape);
191
+
192
+ export {
193
+ BrandAudioSchema,
194
+ BrandKitBodyShape,
195
+ BrandKitBodySchema
196
+ };
@@ -3,7 +3,7 @@ import {
3
3
  projectMediaPlaybackSpans,
4
4
  sliceTranscriptWords,
5
5
  splitIntoSentences
6
- } from "./chunk-VXGA7LQQ.js";
6
+ } from "./chunk-NVXSKKRN.js";
7
7
  import {
8
8
  createHeadlessStore,
9
9
  findClipById,
@@ -40,6 +40,206 @@ import {
40
40
  // ../composition-ops/src/index.ts
41
41
  import { z } from "zod";
42
42
 
43
+ // ../composition-ops/src/opRecorder.ts
44
+ var genTrackId = (kind) => `track-${kind}-${crypto.randomUUID().slice(0, 8)}`;
45
+ function wrapWithOpRecorder(base, opts) {
46
+ const { record, getComposition } = opts;
47
+ const captionTarget = {
48
+ getState: () => ({ composition: getComposition() }),
49
+ setCaptionStyle: (style) => base.setCaptionStyle(style),
50
+ setCaptions: (segments, style) => base.setCaptions(segments, style)
51
+ };
52
+ const wrapped = {
53
+ addClip: (clip, options) => {
54
+ const before = getComposition();
55
+ const c = clip.kind ? clip : { ...clip, kind: "video" };
56
+ if (options?.newTrack) {
57
+ const newTrackId = options.newTrackId ?? genTrackId("video");
58
+ record({ type: "clip.add", clip: c, newTrack: true, newTrackId }, before);
59
+ base.addClip(c, { newTrack: true, newTrackId });
60
+ } else {
61
+ record({ type: "clip.add", clip: c }, before);
62
+ base.addClip(c);
63
+ }
64
+ },
65
+ removeClip: (id) => {
66
+ record({ type: "clip.remove", clipId: id }, getComposition());
67
+ base.removeClip(id);
68
+ },
69
+ updateClip: (id, updates) => {
70
+ record({ type: "clip.update", clipId: id, patch: updates }, getComposition());
71
+ base.updateClip(id, updates);
72
+ },
73
+ removeClipsByMediaId: (mediaId) => {
74
+ record({ type: "removeClipsByMediaId", mediaId }, getComposition());
75
+ base.removeClipsByMediaId(mediaId);
76
+ },
77
+ setClipProperties: (id, props) => {
78
+ const { playbackRate, ...patch } = props;
79
+ if (playbackRate !== void 0) {
80
+ if (opts.onUnjournalable) {
81
+ const result = base.setClipProperties(id, props);
82
+ opts.onUnjournalable("setClipProperties.playbackRate");
83
+ return result;
84
+ }
85
+ throw new Error(
86
+ "opRecorder.setClipProperties: playbackRate is not serializable as a clip.update op (source.playbackRate triggers a cross-field ripple). Use the server-authoritative ripple path."
87
+ );
88
+ }
89
+ record({ type: "clip.update", clipId: id, patch }, getComposition());
90
+ return base.setClipProperties(id, props);
91
+ },
92
+ addOverlay: (clip) => {
93
+ const before = getComposition();
94
+ const track = before.tracks.find((t) => t.kind === "overlay");
95
+ let trackId = track?.id;
96
+ if (!trackId) {
97
+ trackId = genTrackId("overlay");
98
+ record({ type: "addTrack", kind: "overlay", id: trackId }, before);
99
+ base.addTrack("overlay", void 0, trackId);
100
+ }
101
+ record({ type: "clip.add", clip, trackId }, getComposition());
102
+ base.addOverlay(clip);
103
+ },
104
+ removeOverlay: (id) => {
105
+ record({ type: "clip.remove", clipId: id }, getComposition());
106
+ base.removeOverlay(id);
107
+ },
108
+ updateOverlay: (id, updates) => {
109
+ record({ type: "clip.update", clipId: id, patch: updates }, getComposition());
110
+ base.updateOverlay(id, updates);
111
+ },
112
+ loadOverlays: (clips) => {
113
+ record({ type: "loadOverlays", clips }, getComposition());
114
+ base.loadOverlays(clips);
115
+ },
116
+ addAudioClip: (clip) => {
117
+ const before = getComposition();
118
+ const track = before.tracks.find((t) => t.kind === "audio");
119
+ let trackId = track?.id;
120
+ if (!trackId) {
121
+ trackId = genTrackId("audio");
122
+ record({ type: "addTrack", kind: "audio", id: trackId }, before);
123
+ base.addTrack("audio", void 0, trackId);
124
+ }
125
+ record({ type: "clip.add", clip, trackId }, getComposition());
126
+ base.addAudioClip(clip);
127
+ },
128
+ removeAudioClip: (id) => {
129
+ record({ type: "clip.remove", clipId: id }, getComposition());
130
+ base.removeAudioClip(id);
131
+ },
132
+ updateAudioClip: (id, updates) => {
133
+ record({ type: "clip.update", clipId: id, patch: updates }, getComposition());
134
+ base.updateAudioClip(id, updates);
135
+ },
136
+ addMarker: (marker) => {
137
+ record({ type: "addMarker", marker }, getComposition());
138
+ base.addMarker(marker);
139
+ },
140
+ removeMarker: (id) => {
141
+ record({ type: "removeMarker", markerId: id }, getComposition());
142
+ base.removeMarker(id);
143
+ },
144
+ updateMarker: (id, updates) => {
145
+ record({ type: "updateMarker", markerId: id, updates }, getComposition());
146
+ base.updateMarker(id, updates);
147
+ },
148
+ setCropIntents: (clipId, segments) => {
149
+ record({ type: "setCropIntents", clipId, segments }, getComposition());
150
+ base.setCropIntents(clipId, segments);
151
+ },
152
+ setEffects: (clipId, effects) => {
153
+ record({ type: "setEffects", clipId, effects }, getComposition());
154
+ base.setEffects(clipId, effects);
155
+ },
156
+ // The two caption writes go through the same function `applyOpToStore` uses, so the op this
157
+ // journals is exactly the op a replay applies. Calling the store's setter directly left this
158
+ // lane — Studio's composition store and the in-app agent capability are built on it — able to
159
+ // write a face the op gate refuses and to journal an op `applyOp` would refuse on replay.
160
+ // The op is applied FIRST: a refusal must leave nothing in the journal.
161
+ setCaptionStyle: (style) => {
162
+ const before = getComposition();
163
+ const op = { type: "setCaptionStyle", style };
164
+ applyCaptionOpToStore(captionTarget, op);
165
+ record(op, before);
166
+ },
167
+ setCaptions: (segments, style) => {
168
+ const before = getComposition();
169
+ const op = {
170
+ type: "setCaptions",
171
+ segments,
172
+ ...style !== void 0 ? { style } : {}
173
+ };
174
+ applyCaptionOpToStore(captionTarget, op);
175
+ record(op, before);
176
+ },
177
+ setFormat: (format) => {
178
+ record({ type: "setFormat", format }, getComposition());
179
+ base.setFormat(format);
180
+ },
181
+ addTrack: (kind, name, id) => {
182
+ const useId = id ?? genTrackId(kind);
183
+ record({ type: "addTrack", kind, ...name !== void 0 ? { name } : {}, id: useId }, getComposition());
184
+ base.addTrack(kind, name, useId);
185
+ },
186
+ removeTrack: (trackId) => {
187
+ record({ type: "removeTrack", trackId }, getComposition());
188
+ base.removeTrack(trackId);
189
+ },
190
+ updateTrack: (trackId, updates) => {
191
+ record({ type: "updateTrack", trackId, updates }, getComposition());
192
+ base.updateTrack(trackId, updates);
193
+ },
194
+ reorderTracks: (fromIndex, toIndex) => {
195
+ record({ type: "reorderTracks", fromIndex, toIndex }, getComposition());
196
+ base.reorderTracks(fromIndex, toIndex);
197
+ },
198
+ moveClipToTrack: (clipId, targetTrackId, newStartTime) => {
199
+ record(
200
+ {
201
+ type: "moveClipToTrack",
202
+ clipId,
203
+ targetTrackId,
204
+ ...newStartTime !== void 0 ? { newStartTime } : {}
205
+ },
206
+ getComposition()
207
+ );
208
+ base.moveClipToTrack(clipId, targetTrackId, newStartTime);
209
+ },
210
+ duplicateClip: (id) => {
211
+ record({ type: "duplicateClip", clipId: id }, getComposition());
212
+ return base.duplicateClip(id);
213
+ },
214
+ cutAndRipple: (start, end) => {
215
+ record({ type: "cutAndRipple", start, end }, getComposition());
216
+ base.cutAndRipple(start, end);
217
+ },
218
+ ripple: (time, amount) => {
219
+ record({ type: "ripple", time, amount }, getComposition());
220
+ base.ripple(time, amount);
221
+ },
222
+ addExcludedRange: (clipId, sourceStart, sourceEnd, reason) => {
223
+ record(
224
+ {
225
+ type: "addExcludedRange",
226
+ clipId,
227
+ sourceStart,
228
+ sourceEnd,
229
+ ...reason !== void 0 ? { reason } : {}
230
+ },
231
+ getComposition()
232
+ );
233
+ base.addExcludedRange(clipId, sourceStart, sourceEnd, reason);
234
+ },
235
+ removeExcludedRange: (clipId, rangeIdx) => {
236
+ record({ type: "removeExcludedRange", clipId, rangeIdx }, getComposition());
237
+ base.removeExcludedRange(clipId, rangeIdx);
238
+ }
239
+ };
240
+ return { ...base, ...wrapped };
241
+ }
242
+
43
243
  // ../composition-ops/src/recordingStore.ts
44
244
  function createRecordingStore(initial) {
45
245
  const base = createHeadlessStore(initial);
@@ -892,206 +1092,6 @@ function invertOp(before, op) {
892
1092
  }
893
1093
  }
894
1094
 
895
- // ../composition-ops/src/opRecorder.ts
896
- var genTrackId = (kind) => `track-${kind}-${crypto.randomUUID().slice(0, 8)}`;
897
- function wrapWithOpRecorder(base, opts) {
898
- const { record, getComposition } = opts;
899
- const captionTarget = {
900
- getState: () => ({ composition: getComposition() }),
901
- setCaptionStyle: (style) => base.setCaptionStyle(style),
902
- setCaptions: (segments, style) => base.setCaptions(segments, style)
903
- };
904
- const wrapped = {
905
- addClip: (clip, options) => {
906
- const before = getComposition();
907
- const c = clip.kind ? clip : { ...clip, kind: "video" };
908
- if (options?.newTrack) {
909
- const newTrackId = options.newTrackId ?? genTrackId("video");
910
- record({ type: "clip.add", clip: c, newTrack: true, newTrackId }, before);
911
- base.addClip(c, { newTrack: true, newTrackId });
912
- } else {
913
- record({ type: "clip.add", clip: c }, before);
914
- base.addClip(c);
915
- }
916
- },
917
- removeClip: (id) => {
918
- record({ type: "clip.remove", clipId: id }, getComposition());
919
- base.removeClip(id);
920
- },
921
- updateClip: (id, updates) => {
922
- record({ type: "clip.update", clipId: id, patch: updates }, getComposition());
923
- base.updateClip(id, updates);
924
- },
925
- removeClipsByMediaId: (mediaId) => {
926
- record({ type: "removeClipsByMediaId", mediaId }, getComposition());
927
- base.removeClipsByMediaId(mediaId);
928
- },
929
- setClipProperties: (id, props) => {
930
- const { playbackRate, ...patch } = props;
931
- if (playbackRate !== void 0) {
932
- if (opts.onUnjournalable) {
933
- const result = base.setClipProperties(id, props);
934
- opts.onUnjournalable("setClipProperties.playbackRate");
935
- return result;
936
- }
937
- throw new Error(
938
- "opRecorder.setClipProperties: playbackRate is not serializable as a clip.update op (source.playbackRate triggers a cross-field ripple). Use the server-authoritative ripple path."
939
- );
940
- }
941
- record({ type: "clip.update", clipId: id, patch }, getComposition());
942
- return base.setClipProperties(id, props);
943
- },
944
- addOverlay: (clip) => {
945
- const before = getComposition();
946
- const track = before.tracks.find((t) => t.kind === "overlay");
947
- let trackId = track?.id;
948
- if (!trackId) {
949
- trackId = genTrackId("overlay");
950
- record({ type: "addTrack", kind: "overlay", id: trackId }, before);
951
- base.addTrack("overlay", void 0, trackId);
952
- }
953
- record({ type: "clip.add", clip, trackId }, getComposition());
954
- base.addOverlay(clip);
955
- },
956
- removeOverlay: (id) => {
957
- record({ type: "clip.remove", clipId: id }, getComposition());
958
- base.removeOverlay(id);
959
- },
960
- updateOverlay: (id, updates) => {
961
- record({ type: "clip.update", clipId: id, patch: updates }, getComposition());
962
- base.updateOverlay(id, updates);
963
- },
964
- loadOverlays: (clips) => {
965
- record({ type: "loadOverlays", clips }, getComposition());
966
- base.loadOverlays(clips);
967
- },
968
- addAudioClip: (clip) => {
969
- const before = getComposition();
970
- const track = before.tracks.find((t) => t.kind === "audio");
971
- let trackId = track?.id;
972
- if (!trackId) {
973
- trackId = genTrackId("audio");
974
- record({ type: "addTrack", kind: "audio", id: trackId }, before);
975
- base.addTrack("audio", void 0, trackId);
976
- }
977
- record({ type: "clip.add", clip, trackId }, getComposition());
978
- base.addAudioClip(clip);
979
- },
980
- removeAudioClip: (id) => {
981
- record({ type: "clip.remove", clipId: id }, getComposition());
982
- base.removeAudioClip(id);
983
- },
984
- updateAudioClip: (id, updates) => {
985
- record({ type: "clip.update", clipId: id, patch: updates }, getComposition());
986
- base.updateAudioClip(id, updates);
987
- },
988
- addMarker: (marker) => {
989
- record({ type: "addMarker", marker }, getComposition());
990
- base.addMarker(marker);
991
- },
992
- removeMarker: (id) => {
993
- record({ type: "removeMarker", markerId: id }, getComposition());
994
- base.removeMarker(id);
995
- },
996
- updateMarker: (id, updates) => {
997
- record({ type: "updateMarker", markerId: id, updates }, getComposition());
998
- base.updateMarker(id, updates);
999
- },
1000
- setCropIntents: (clipId, segments) => {
1001
- record({ type: "setCropIntents", clipId, segments }, getComposition());
1002
- base.setCropIntents(clipId, segments);
1003
- },
1004
- setEffects: (clipId, effects) => {
1005
- record({ type: "setEffects", clipId, effects }, getComposition());
1006
- base.setEffects(clipId, effects);
1007
- },
1008
- // The two caption writes go through the same function `applyOpToStore` uses, so the op this
1009
- // journals is exactly the op a replay applies. Calling the store's setter directly left this
1010
- // lane — Studio's composition store and the in-app agent capability are built on it — able to
1011
- // write a face the op gate refuses and to journal an op `applyOp` would refuse on replay.
1012
- // The op is applied FIRST: a refusal must leave nothing in the journal.
1013
- setCaptionStyle: (style) => {
1014
- const before = getComposition();
1015
- const op = { type: "setCaptionStyle", style };
1016
- applyCaptionOpToStore(captionTarget, op);
1017
- record(op, before);
1018
- },
1019
- setCaptions: (segments, style) => {
1020
- const before = getComposition();
1021
- const op = {
1022
- type: "setCaptions",
1023
- segments,
1024
- ...style !== void 0 ? { style } : {}
1025
- };
1026
- applyCaptionOpToStore(captionTarget, op);
1027
- record(op, before);
1028
- },
1029
- setFormat: (format) => {
1030
- record({ type: "setFormat", format }, getComposition());
1031
- base.setFormat(format);
1032
- },
1033
- addTrack: (kind, name, id) => {
1034
- const useId = id ?? genTrackId(kind);
1035
- record({ type: "addTrack", kind, ...name !== void 0 ? { name } : {}, id: useId }, getComposition());
1036
- base.addTrack(kind, name, useId);
1037
- },
1038
- removeTrack: (trackId) => {
1039
- record({ type: "removeTrack", trackId }, getComposition());
1040
- base.removeTrack(trackId);
1041
- },
1042
- updateTrack: (trackId, updates) => {
1043
- record({ type: "updateTrack", trackId, updates }, getComposition());
1044
- base.updateTrack(trackId, updates);
1045
- },
1046
- reorderTracks: (fromIndex, toIndex) => {
1047
- record({ type: "reorderTracks", fromIndex, toIndex }, getComposition());
1048
- base.reorderTracks(fromIndex, toIndex);
1049
- },
1050
- moveClipToTrack: (clipId, targetTrackId, newStartTime) => {
1051
- record(
1052
- {
1053
- type: "moveClipToTrack",
1054
- clipId,
1055
- targetTrackId,
1056
- ...newStartTime !== void 0 ? { newStartTime } : {}
1057
- },
1058
- getComposition()
1059
- );
1060
- base.moveClipToTrack(clipId, targetTrackId, newStartTime);
1061
- },
1062
- duplicateClip: (id) => {
1063
- record({ type: "duplicateClip", clipId: id }, getComposition());
1064
- return base.duplicateClip(id);
1065
- },
1066
- cutAndRipple: (start, end) => {
1067
- record({ type: "cutAndRipple", start, end }, getComposition());
1068
- base.cutAndRipple(start, end);
1069
- },
1070
- ripple: (time, amount) => {
1071
- record({ type: "ripple", time, amount }, getComposition());
1072
- base.ripple(time, amount);
1073
- },
1074
- addExcludedRange: (clipId, sourceStart, sourceEnd, reason) => {
1075
- record(
1076
- {
1077
- type: "addExcludedRange",
1078
- clipId,
1079
- sourceStart,
1080
- sourceEnd,
1081
- ...reason !== void 0 ? { reason } : {}
1082
- },
1083
- getComposition()
1084
- );
1085
- base.addExcludedRange(clipId, sourceStart, sourceEnd, reason);
1086
- },
1087
- removeExcludedRange: (clipId, rangeIdx) => {
1088
- record({ type: "removeExcludedRange", clipId, rangeIdx }, getComposition());
1089
- base.removeExcludedRange(clipId, rangeIdx);
1090
- }
1091
- };
1092
- return { ...base, ...wrapped };
1093
- }
1094
-
1095
1095
  export {
1096
1096
  wrapWithOpRecorder,
1097
1097
  createRecordingStore,
@@ -0,0 +1 @@
1
+ export * from "../../_members/component-runtime/template/testing/index.js";