@depths/waves 0.1.0 → 0.3.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.
package/dist/index.js CHANGED
@@ -31,7 +31,6 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
31
31
  var index_exports = {};
32
32
  __export(index_exports, {
33
33
  ComponentRegistry: () => ComponentRegistry,
34
- ComponentSchemas: () => ComponentSchemas,
35
34
  IRValidator: () => IRValidator,
36
35
  VideoIRSchema: () => VideoIRSchema,
37
36
  WavesEngine: () => WavesEngine,
@@ -48,7 +47,7 @@ __export(index_exports, {
48
47
  module.exports = __toCommonJS(index_exports);
49
48
 
50
49
  // src/version.ts
51
- var __wavesVersion = "0.1.0";
50
+ var __wavesVersion = "0.2.0";
52
51
 
53
52
  // src/core/engine.ts
54
53
  var import_bundler = require("@remotion/bundler");
@@ -77,12 +76,23 @@ var ComponentRegistry = class {
77
76
  getTypes() {
78
77
  return Array.from(this.components.keys());
79
78
  }
79
+ getTypesForLLM(options) {
80
+ const includeInternal = options?.includeInternal ?? false;
81
+ const out = [];
82
+ for (const [type, reg] of this.components) {
83
+ if (!includeInternal && reg.metadata.internal) continue;
84
+ out.push(type);
85
+ }
86
+ return out;
87
+ }
80
88
  has(type) {
81
89
  return this.components.has(type);
82
90
  }
83
- getJSONSchemaForLLM() {
91
+ getJSONSchemaForLLM(options) {
92
+ const includeInternal = options?.includeInternal ?? false;
84
93
  const schemas = {};
85
94
  for (const [type, registration] of this.components) {
95
+ if (!includeInternal && registration.metadata.internal) continue;
86
96
  schemas[type] = {
87
97
  schema: zodSchemaToJsonSchema(registration.propsSchema),
88
98
  metadata: registration.metadata
@@ -164,14 +174,16 @@ var WavesEngine = class {
164
174
  onProgress: () => void 0
165
175
  });
166
176
  const compositionId = validatedIR.video.id ?? "main";
177
+ const inputProps = options.inputProps ?? {};
167
178
  const composition = await (0, import_renderer.selectComposition)({
168
179
  serveUrl: bundleLocation,
169
180
  id: compositionId,
170
- inputProps: {}
181
+ inputProps
171
182
  });
172
183
  await (0, import_renderer.renderMedia)({
173
184
  composition,
174
185
  serveUrl: bundleLocation,
186
+ inputProps,
175
187
  codec: options.codec ?? "h264",
176
188
  outputLocation: options.outputPath,
177
189
  crf: options.crf ?? null,
@@ -184,11 +196,75 @@ var WavesEngine = class {
184
196
  await import_promises.default.rm(tmpDir, { recursive: true, force: true });
185
197
  }
186
198
  }
199
+ async renderStills(ir, options) {
200
+ if (this.registry !== globalRegistry) {
201
+ throw new WavesRenderError("WavesEngine currently requires using globalRegistry", {
202
+ hint: "Use `registerBuiltInComponents()` + `globalRegistry` for both validation and rendering."
203
+ });
204
+ }
205
+ const validationResult = this.validator.validate(ir);
206
+ if (!validationResult.success) {
207
+ throw new WavesRenderError("IR validation failed", { errors: validationResult.errors });
208
+ }
209
+ const validatedIR = validationResult.data;
210
+ const requiredTypes = collectComponentTypes(validatedIR);
211
+ for (const type of requiredTypes) {
212
+ if (!this.registry.has(type)) {
213
+ throw new WavesRenderError("Unknown component type", { type });
214
+ }
215
+ }
216
+ const rootDir = options.rootDir ?? process.cwd();
217
+ const tmpDir = await import_promises.default.mkdtemp(import_node_path.default.join(rootDir, ".waves-tmp-"));
218
+ const entryPoint = import_node_path.default.join(tmpDir, "entry.tsx");
219
+ try {
220
+ await import_promises.default.writeFile(entryPoint, generateEntryPoint(validatedIR, options), "utf-8");
221
+ await import_promises.default.mkdir(options.outputDir, { recursive: true });
222
+ const bundleLocation = await (0, import_bundler.bundle)({
223
+ entryPoint,
224
+ rootDir,
225
+ publicDir: options.publicDir ?? null,
226
+ onProgress: () => void 0
227
+ });
228
+ const compositionId = validatedIR.video.id ?? "main";
229
+ const inputProps = options.inputProps ?? {};
230
+ const composition = await (0, import_renderer.selectComposition)({
231
+ serveUrl: bundleLocation,
232
+ id: compositionId,
233
+ inputProps
234
+ });
235
+ const frames = Array.from(new Set(options.frames)).sort((a, b) => a - b);
236
+ const written = [];
237
+ for (const frame of frames) {
238
+ const clamped = Math.max(0, Math.min(composition.durationInFrames - 1, Math.floor(frame)));
239
+ const imageFormat = options.imageFormat ?? "png";
240
+ const filename = `${compositionId}-frame-${String(clamped).padStart(6, "0")}.${imageFormat}`;
241
+ const outPath = import_node_path.default.join(options.outputDir, filename);
242
+ const stillOptions = {
243
+ composition,
244
+ serveUrl: bundleLocation,
245
+ inputProps,
246
+ frame: clamped,
247
+ output: outPath,
248
+ imageFormat,
249
+ scale: options.scale ?? 1,
250
+ overwrite: true,
251
+ ...imageFormat === "jpeg" ? { jpegQuality: options.jpegQuality ?? 80 } : {}
252
+ };
253
+ await (0, import_renderer.renderStill)(stillOptions);
254
+ written.push(outPath);
255
+ }
256
+ return written;
257
+ } catch (error) {
258
+ throw new WavesRenderError("Rendering failed", { originalError: error });
259
+ } finally {
260
+ await import_promises.default.rm(tmpDir, { recursive: true, force: true });
261
+ }
262
+ }
187
263
  };
188
264
  function collectComponentTypes(ir) {
189
265
  const types = /* @__PURE__ */ new Set();
190
- for (const scene of ir.scenes) {
191
- walkComponent(scene, types);
266
+ for (const node of ir.timeline) {
267
+ walkComponent(node, types);
192
268
  }
193
269
  return types;
194
270
  }
@@ -218,8 +294,10 @@ registerBuiltInComponents();
218
294
  const ir = ${JSON.stringify(ir, null, 2)};
219
295
  const compositionId = ${JSON.stringify(compositionId)};
220
296
 
221
- const Root = () => {
222
- return <WavesComposition ir={ir} registry={globalRegistry} />;
297
+ const Root = (props) => {
298
+ const debugBounds = Boolean(props?.__wavesDebugBounds);
299
+ const debugLabels = Boolean(props?.__wavesDebugLabels);
300
+ return <WavesComposition ir={ir} registry={globalRegistry} debug={{ bounds: debugBounds, labels: debugLabels }} />;
223
301
  };
224
302
 
225
303
  export const RemotionRoot = () => {
@@ -263,70 +341,128 @@ var BackgroundSpecSchema = import_zod2.z.discriminatedUnion("type", [
263
341
  value: AssetPathSchema
264
342
  })
265
343
  ]);
266
- var BaseComponentIRSchema = import_zod2.z.object({
267
- id: import_zod2.z.string().min(1).max(100).describe("Unique component instance ID"),
268
- type: import_zod2.z.string().min(1).describe("Component type identifier"),
269
- timing: TimingSpecSchema,
270
- metadata: import_zod2.z.record(import_zod2.z.string(), import_zod2.z.unknown()).optional().describe("Optional metadata")
271
- });
272
- var TextComponentIRSchema = BaseComponentIRSchema.extend({
273
- type: import_zod2.z.literal("Text"),
274
- props: import_zod2.z.object({
275
- content: import_zod2.z.string().min(1).max(1e3),
276
- fontSize: import_zod2.z.number().int().min(12).max(200).default(48),
277
- color: import_zod2.z.string().regex(/^#[0-9A-Fa-f]{6}$/).default("#FFFFFF"),
278
- position: import_zod2.z.enum(["top", "center", "bottom", "left", "right"]).default("center"),
279
- animation: import_zod2.z.enum(["none", "fade", "slide", "zoom"]).default("fade")
280
- })
344
+ var VideoConfigSchema = import_zod2.z.object({
345
+ id: import_zod2.z.string().default("main"),
346
+ width: import_zod2.z.number().int().min(360).max(7680),
347
+ height: import_zod2.z.number().int().min(360).max(4320),
348
+ fps: import_zod2.z.number().int().min(1).max(120).default(30),
349
+ durationInFrames: import_zod2.z.number().int().positive()
281
350
  });
282
- var AudioComponentIRSchema = BaseComponentIRSchema.extend({
283
- type: import_zod2.z.literal("Audio"),
284
- props: import_zod2.z.object({
285
- src: AssetPathSchema,
286
- volume: import_zod2.z.number().min(0).max(1).default(1),
287
- startFrom: import_zod2.z.number().int().min(0).default(0).describe("Start playback from frame N"),
288
- fadeIn: import_zod2.z.number().int().min(0).default(0).describe("Fade in duration in frames"),
289
- fadeOut: import_zod2.z.number().int().min(0).default(0).describe("Fade out duration in frames")
290
- })
351
+ var AudioSpecSchema = import_zod2.z.object({
352
+ background: AssetPathSchema.optional(),
353
+ volume: import_zod2.z.number().min(0).max(1).default(0.5)
354
+ }).optional();
355
+ var TransitionSpecSchema = import_zod2.z.object({
356
+ type: import_zod2.z.string().min(1),
357
+ durationInFrames: import_zod2.z.number().int().positive(),
358
+ props: import_zod2.z.record(import_zod2.z.string(), import_zod2.z.unknown()).optional()
291
359
  });
292
- function getComponentIRSchema() {
293
- return ComponentIRSchema;
294
- }
295
- var SceneComponentIRSchema = BaseComponentIRSchema.extend({
296
- type: import_zod2.z.literal("Scene"),
297
- props: import_zod2.z.object({
298
- background: BackgroundSpecSchema
299
- }),
300
- children: import_zod2.z.lazy(() => import_zod2.z.array(getComponentIRSchema())).optional()
360
+ var ComponentNodeSchema = import_zod2.z.lazy(
361
+ () => import_zod2.z.object({
362
+ id: import_zod2.z.string().min(1).max(100).describe("Unique component instance ID"),
363
+ type: import_zod2.z.string().min(1).describe("Registered component type identifier"),
364
+ timing: TimingSpecSchema.optional().describe("Optional timing (frames)"),
365
+ props: import_zod2.z.record(import_zod2.z.string(), import_zod2.z.unknown()).optional().describe("Component props object"),
366
+ metadata: import_zod2.z.record(import_zod2.z.string(), import_zod2.z.unknown()).optional().describe("Optional metadata"),
367
+ children: import_zod2.z.array(ComponentNodeSchema).optional().describe("Nested children nodes")
368
+ })
369
+ );
370
+ var SegmentSchema = import_zod2.z.object({
371
+ id: import_zod2.z.string().min(1).max(100),
372
+ durationInFrames: import_zod2.z.number().int().positive(),
373
+ root: ComponentNodeSchema,
374
+ transitionToNext: TransitionSpecSchema.optional()
301
375
  });
302
- var ComponentIRSchema = import_zod2.z.discriminatedUnion("type", [
303
- SceneComponentIRSchema,
304
- TextComponentIRSchema,
305
- AudioComponentIRSchema
306
- ]);
307
- var VideoIRSchema = import_zod2.z.object({
308
- version: import_zod2.z.literal("1.0").describe("IR schema version"),
309
- video: import_zod2.z.object({
310
- id: import_zod2.z.string().default("main"),
311
- width: import_zod2.z.number().int().min(360).max(7680),
312
- height: import_zod2.z.number().int().min(360).max(4320),
313
- fps: import_zod2.z.number().int().min(1).max(120).default(30),
314
- durationInFrames: import_zod2.z.number().int().positive()
315
- }),
316
- audio: import_zod2.z.object({
317
- background: AssetPathSchema.optional(),
318
- volume: import_zod2.z.number().min(0).max(1).default(0.5)
319
- }).optional(),
320
- scenes: import_zod2.z.array(SceneComponentIRSchema).min(1)
376
+ var VideoIRv2Schema = import_zod2.z.object({
377
+ version: import_zod2.z.literal("2.0").describe("IR schema version"),
378
+ video: VideoConfigSchema,
379
+ audio: AudioSpecSchema,
380
+ segments: import_zod2.z.array(SegmentSchema).min(1).optional(),
381
+ timeline: import_zod2.z.array(ComponentNodeSchema).min(1).optional()
382
+ }).superRefine((v, ctx) => {
383
+ const hasSegments = Array.isArray(v.segments);
384
+ const hasTimeline = Array.isArray(v.timeline);
385
+ if (hasSegments === hasTimeline) {
386
+ ctx.addIssue({
387
+ code: import_zod2.z.ZodIssueCode.custom,
388
+ message: 'Exactly one of "segments" or "timeline" must be provided',
389
+ path: []
390
+ });
391
+ }
321
392
  });
322
- var ComponentSchemas = {
323
- Scene: SceneComponentIRSchema,
324
- Text: TextComponentIRSchema,
325
- Audio: AudioComponentIRSchema
326
- };
393
+ var VideoIRv2AuthoringSchema = import_zod2.z.object({
394
+ version: import_zod2.z.literal("2.0").describe("IR schema version"),
395
+ video: VideoConfigSchema,
396
+ audio: AudioSpecSchema,
397
+ segments: import_zod2.z.array(SegmentSchema).min(1),
398
+ timeline: import_zod2.z.never().optional()
399
+ }).describe("Preferred authoring format: sequential segments with optional transitions");
400
+ var VideoIRSchema = VideoIRv2Schema;
401
+
402
+ // src/ir/migrations.ts
403
+ function fillNodeTiming(node, parentDurationInFrames) {
404
+ const timing = node.timing ?? { from: 0, durationInFrames: parentDurationInFrames };
405
+ const children = node.children?.map((c) => fillNodeTiming(c, timing.durationInFrames));
406
+ return {
407
+ id: node.id,
408
+ type: node.type,
409
+ timing,
410
+ props: node.props,
411
+ metadata: node.metadata,
412
+ children: children && children.length ? children : void 0
413
+ };
414
+ }
415
+ function compileToRenderGraph(v2) {
416
+ const parsed = VideoIRv2Schema.parse(v2);
417
+ if (parsed.timeline) {
418
+ const timeline2 = parsed.timeline.map((n) => {
419
+ const timing = n.timing ?? { from: 0, durationInFrames: parsed.video.durationInFrames };
420
+ return fillNodeTiming({ ...n, timing }, parsed.video.durationInFrames);
421
+ });
422
+ return {
423
+ version: "2.0",
424
+ video: parsed.video,
425
+ audio: parsed.audio,
426
+ timeline: timeline2
427
+ };
428
+ }
429
+ const segments = parsed.segments ?? [];
430
+ const timeline = [];
431
+ let start = 0;
432
+ let pendingEnter;
433
+ for (let i = 0; i < segments.length; i++) {
434
+ const seg = segments[i];
435
+ const hasNext = i < segments.length - 1;
436
+ const exit = hasNext ? seg.transitionToNext : void 0;
437
+ const enter = pendingEnter;
438
+ const rootFilled = fillNodeTiming(seg.root, seg.durationInFrames);
439
+ timeline.push({
440
+ id: `segment:${seg.id}`,
441
+ type: "Segment",
442
+ timing: { from: start, durationInFrames: seg.durationInFrames },
443
+ props: {
444
+ enterTransition: enter,
445
+ exitTransition: exit
446
+ },
447
+ children: [rootFilled]
448
+ });
449
+ pendingEnter = exit;
450
+ const overlap = exit?.durationInFrames ?? 0;
451
+ start += seg.durationInFrames - overlap;
452
+ }
453
+ return {
454
+ version: "2.0",
455
+ video: parsed.video,
456
+ audio: parsed.audio,
457
+ timeline
458
+ };
459
+ }
327
460
 
328
461
  // src/core/validator.ts
329
462
  var IRValidator = class {
463
+ constructor(registry) {
464
+ this.registry = registry;
465
+ }
330
466
  validateSchema(ir) {
331
467
  const result = VideoIRSchema.safeParse(ir);
332
468
  if (!result.success) {
@@ -341,41 +477,75 @@ var IRValidator = class {
341
477
  }
342
478
  return { success: true, data: result.data };
343
479
  }
344
- validateSemantics(ir) {
480
+ validateSemantics(latest) {
345
481
  const errors = [];
346
- const totalSceneDuration = ir.scenes.reduce((sum, scene) => sum + scene.timing.durationInFrames, 0);
347
- if (totalSceneDuration !== ir.video.durationInFrames) {
482
+ if (latest.segments) {
483
+ for (const [i, seg] of latest.segments.entries()) {
484
+ const exit = seg.transitionToNext;
485
+ const overlap = exit?.durationInFrames ?? 0;
486
+ if (exit && i === latest.segments.length - 1) {
487
+ errors.push({
488
+ path: ["segments", String(i), "transitionToNext"],
489
+ message: "transitionToNext is not allowed on the last segment",
490
+ code: "TRANSITION_ON_LAST_SEGMENT"
491
+ });
492
+ continue;
493
+ }
494
+ if (overlap > 0 && i < latest.segments.length - 1) {
495
+ const next = latest.segments[i + 1];
496
+ if (overlap > seg.durationInFrames) {
497
+ errors.push({
498
+ path: ["segments", String(i), "transitionToNext", "durationInFrames"],
499
+ message: `Transition overlap (${overlap}) exceeds segment duration (${seg.durationInFrames})`,
500
+ code: "TRANSITION_OVERLAP_TOO_LARGE"
501
+ });
502
+ }
503
+ if (overlap > next.durationInFrames) {
504
+ errors.push({
505
+ path: ["segments", String(i + 1), "durationInFrames"],
506
+ message: `Transition overlap (${overlap}) exceeds next segment duration (${next.durationInFrames})`,
507
+ code: "TRANSITION_OVERLAP_TOO_LARGE"
508
+ });
509
+ }
510
+ }
511
+ }
512
+ }
513
+ const compiled = compileToRenderGraph(latest);
514
+ const maxEnd = getMaxEnd(compiled.timeline);
515
+ if (maxEnd !== compiled.video.durationInFrames) {
348
516
  errors.push({
349
- path: ["scenes"],
350
- message: `Sum of scene durations (${totalSceneDuration}) does not match video duration (${ir.video.durationInFrames})`,
517
+ path: ["video", "durationInFrames"],
518
+ message: `Max timeline end (${maxEnd}) does not match video duration (${compiled.video.durationInFrames})`,
351
519
  code: "DURATION_MISMATCH"
352
520
  });
353
521
  }
354
- let expectedFrame = 0;
355
- for (const [i, scene] of ir.scenes.entries()) {
356
- if (scene.timing.from !== expectedFrame) {
522
+ for (const [i, root] of compiled.timeline.entries()) {
523
+ const end = root.timing.from + root.timing.durationInFrames;
524
+ if (root.timing.from < 0 || root.timing.durationInFrames <= 0) {
357
525
  errors.push({
358
- path: ["scenes", String(i), "timing", "from"],
359
- message: `Scene ${i} starts at frame ${scene.timing.from}, expected ${expectedFrame}`,
360
- code: "TIMING_GAP_OR_OVERLAP"
526
+ path: ["timeline", String(i), "timing"],
527
+ message: "Timing must have from>=0 and durationInFrames>0",
528
+ code: "INVALID_TIMING"
361
529
  });
362
530
  }
363
- expectedFrame += scene.timing.durationInFrames;
364
- }
365
- for (const [i, scene] of ir.scenes.entries()) {
366
- if (scene.children && scene.children.length > 0) {
367
- this.validateComponentTimingRecursive(
368
- scene.children,
369
- scene.timing.durationInFrames,
370
- ["scenes", String(i)],
371
- errors
372
- );
531
+ if (end > compiled.video.durationInFrames) {
532
+ errors.push({
533
+ path: ["timeline", String(i), "timing"],
534
+ message: `Root node exceeds video duration (${end} > ${compiled.video.durationInFrames})`,
535
+ code: "COMPONENT_EXCEEDS_VIDEO"
536
+ });
537
+ }
538
+ if (root.children?.length) {
539
+ this.validateComponentTimingRecursive(root.children, root.timing.durationInFrames, ["timeline", String(i)], errors);
373
540
  }
374
541
  }
542
+ if (this.registry) {
543
+ this.validateRegistryContracts(compiled, errors);
544
+ }
375
545
  if (errors.length > 0) {
376
546
  return { success: false, errors };
377
547
  }
378
- return { success: true };
548
+ return { success: true, data: compiled };
379
549
  }
380
550
  validate(ir) {
381
551
  const schemaResult = this.validateSchema(ir);
@@ -386,7 +556,7 @@ var IRValidator = class {
386
556
  if (!semanticsResult.success) {
387
557
  return { success: false, errors: semanticsResult.errors ?? [] };
388
558
  }
389
- return schemaResult;
559
+ return semanticsResult;
390
560
  }
391
561
  validateComponentTimingRecursive(components, parentDuration, pathPrefix, errors) {
392
562
  for (const [i, component] of components.entries()) {
@@ -398,7 +568,7 @@ var IRValidator = class {
398
568
  code: "COMPONENT_EXCEEDS_PARENT"
399
569
  });
400
570
  }
401
- if (component.type === "Scene" && component.children && component.children.length > 0) {
571
+ if (component.children && component.children.length > 0) {
402
572
  this.validateComponentTimingRecursive(
403
573
  component.children,
404
574
  component.timing.durationInFrames,
@@ -408,7 +578,87 @@ var IRValidator = class {
408
578
  }
409
579
  }
410
580
  }
581
+ validateRegistryContracts(compiled, errors) {
582
+ const registry = this.registry;
583
+ const stack = compiled.timeline.map((n, i) => ({
584
+ node: n,
585
+ path: ["timeline", String(i)]
586
+ }));
587
+ while (stack.length) {
588
+ const { node, path: path2 } = stack.pop();
589
+ const reg = registry.get(node.type);
590
+ if (!reg) {
591
+ errors.push({
592
+ path: [...path2, "type"],
593
+ message: `Unknown component type: ${node.type}`,
594
+ code: "UNKNOWN_COMPONENT_TYPE"
595
+ });
596
+ continue;
597
+ }
598
+ const rawProps = node.props ?? {};
599
+ if (rawProps === null || typeof rawProps !== "object") {
600
+ errors.push({
601
+ path: [...path2, "props"],
602
+ message: "Component props must be an object",
603
+ code: "PROPS_NOT_OBJECT"
604
+ });
605
+ } else {
606
+ const propsValidation = registry.validateProps(node.type, rawProps);
607
+ if (!propsValidation.success) {
608
+ for (const issue of propsValidation.error.issues) {
609
+ errors.push({
610
+ path: [...path2, "props", ...issue.path.map((p) => String(p))],
611
+ message: issue.message,
612
+ code: "PROPS_INVALID"
613
+ });
614
+ }
615
+ } else {
616
+ node.props = propsValidation.data;
617
+ }
618
+ }
619
+ const childCount = node.children?.length ?? 0;
620
+ const meta = reg.metadata;
621
+ const acceptsChildren = Boolean(meta.acceptsChildren);
622
+ if (childCount > 0 && !acceptsChildren) {
623
+ errors.push({
624
+ path: [...path2, "children"],
625
+ message: `Component type "${node.type}" does not accept children`,
626
+ code: "CHILDREN_NOT_ALLOWED"
627
+ });
628
+ }
629
+ if (typeof meta.minChildren === "number" && childCount < meta.minChildren) {
630
+ errors.push({
631
+ path: [...path2, "children"],
632
+ message: `Component requires at least ${meta.minChildren} children`,
633
+ code: "TOO_FEW_CHILDREN"
634
+ });
635
+ }
636
+ if (typeof meta.maxChildren === "number" && childCount > meta.maxChildren) {
637
+ errors.push({
638
+ path: [...path2, "children"],
639
+ message: `Component allows at most ${meta.maxChildren} children`,
640
+ code: "TOO_MANY_CHILDREN"
641
+ });
642
+ }
643
+ if (node.children?.length) {
644
+ for (let i = 0; i < node.children.length; i++) {
645
+ stack.push({ node: node.children[i], path: [...path2, "children", String(i)] });
646
+ }
647
+ }
648
+ }
649
+ }
411
650
  };
651
+ function getMaxEnd(nodes) {
652
+ let max = 0;
653
+ const stack = [...nodes];
654
+ while (stack.length) {
655
+ const n = stack.pop();
656
+ const end = n.timing.from + n.timing.durationInFrames;
657
+ if (end > max) max = end;
658
+ if (n.children?.length) stack.push(...n.children);
659
+ }
660
+ return max;
661
+ }
412
662
 
413
663
  // src/components/primitives/Audio.tsx
414
664
  var import_remotion = require("remotion");
@@ -464,57 +714,555 @@ var Audio = ({
464
714
  );
465
715
  };
466
716
  var AudioComponentMetadata = {
467
- category: "primitive",
717
+ kind: "primitive",
718
+ category: "media",
468
719
  description: "Plays an audio file with optional trimming and fade in/out",
469
720
  llmGuidance: "Use for background music or sound effects. Prefer short clips for SFX. Use fadeIn/fadeOut (in frames) for smoother audio starts/ends."
470
721
  };
471
722
 
472
- // src/components/primitives/Scene.tsx
473
- var import_remotion2 = require("remotion");
723
+ // src/components/primitives/Box.tsx
724
+ var import_react = __toESM(require("react"));
474
725
  var import_zod4 = require("zod");
726
+
727
+ // src/remotion/Fill.tsx
475
728
  var import_jsx_runtime2 = require("react/jsx-runtime");
476
- var ScenePropsSchema = import_zod4.z.object({
729
+ var Fill = ({ style, children }) => {
730
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
731
+ "div",
732
+ {
733
+ style: {
734
+ width: "100%",
735
+ height: "100%",
736
+ position: "relative",
737
+ boxSizing: "border-box",
738
+ ...style ?? {}
739
+ },
740
+ children
741
+ }
742
+ );
743
+ };
744
+
745
+ // src/components/primitives/Box.tsx
746
+ var import_jsx_runtime3 = require("react/jsx-runtime");
747
+ var BoxPropsSchema = import_zod4.z.object({
748
+ width: import_zod4.z.number().positive().optional().describe("Width in pixels (optional)"),
749
+ height: import_zod4.z.number().positive().optional().describe("Height in pixels (optional)"),
750
+ padding: import_zod4.z.number().min(0).default(0).describe("Padding in pixels"),
751
+ backgroundColor: import_zod4.z.string().optional().describe("CSS color (e.g. #RRGGBB, rgba())"),
752
+ borderRadius: import_zod4.z.number().min(0).default(0).describe("Border radius in pixels"),
753
+ opacity: import_zod4.z.number().min(0).max(1).default(1).describe("Opacity 0..1")
754
+ });
755
+ var Box = ({ width, height, padding, backgroundColor, borderRadius, opacity, children }) => {
756
+ const layers = import_react.default.Children.toArray(children);
757
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
758
+ Fill,
759
+ {
760
+ style: {
761
+ width,
762
+ height,
763
+ padding,
764
+ backgroundColor,
765
+ borderRadius,
766
+ opacity
767
+ },
768
+ children: layers.map((child, i) => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { style: { position: "absolute", inset: 0 }, children: child }, i))
769
+ }
770
+ );
771
+ };
772
+ var BoxComponentMetadata = {
773
+ kind: "primitive",
774
+ category: "layout",
775
+ acceptsChildren: true,
776
+ description: "Flow container for layout and backgrounds (layout-safe)",
777
+ llmGuidance: "Use Box as a container inside Grid/Stack. Box participates in layout flow. For x/y positioning, use Frame."
778
+ };
779
+
780
+ // src/components/primitives/Frame.tsx
781
+ var import_react2 = __toESM(require("react"));
782
+ var import_zod5 = require("zod");
783
+ var import_jsx_runtime4 = require("react/jsx-runtime");
784
+ var FramePropsSchema = import_zod5.z.object({
785
+ x: import_zod5.z.number().default(0).describe("Left offset in pixels"),
786
+ y: import_zod5.z.number().default(0).describe("Top offset in pixels"),
787
+ width: import_zod5.z.number().positive().optional().describe("Width in pixels (optional)"),
788
+ height: import_zod5.z.number().positive().optional().describe("Height in pixels (optional)"),
789
+ padding: import_zod5.z.number().min(0).default(0).describe("Padding in pixels"),
790
+ backgroundColor: import_zod5.z.string().optional().describe("CSS color (e.g. #RRGGBB, rgba())"),
791
+ borderRadius: import_zod5.z.number().min(0).default(0).describe("Border radius in pixels"),
792
+ opacity: import_zod5.z.number().min(0).max(1).default(1).describe("Opacity 0..1")
793
+ });
794
+ var Frame = ({
795
+ x,
796
+ y,
797
+ width,
798
+ height,
799
+ padding,
800
+ backgroundColor,
801
+ borderRadius,
802
+ opacity,
803
+ children
804
+ }) => {
805
+ const layers = import_react2.default.Children.toArray(children);
806
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
807
+ "div",
808
+ {
809
+ style: {
810
+ position: "absolute",
811
+ left: x,
812
+ top: y,
813
+ width,
814
+ height,
815
+ padding,
816
+ backgroundColor,
817
+ borderRadius,
818
+ opacity,
819
+ boxSizing: "border-box"
820
+ },
821
+ children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Fill, { children: layers.map((child, i) => /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { style: { position: "absolute", inset: 0 }, children: child }, i)) })
822
+ }
823
+ );
824
+ };
825
+ var FrameComponentMetadata = {
826
+ kind: "primitive",
827
+ category: "layout",
828
+ acceptsChildren: true,
829
+ description: "Absolute-positioned container (x/y placement)",
830
+ llmGuidance: "Use Frame for precise pixel placement (x/y). Use Box for normal layout flow inside Grid/Stack."
831
+ };
832
+
833
+ // src/components/primitives/Grid.tsx
834
+ var import_zod6 = require("zod");
835
+ var import_jsx_runtime5 = require("react/jsx-runtime");
836
+ var GridPropsSchema = import_zod6.z.object({
837
+ columns: import_zod6.z.number().int().min(1).max(12).default(2),
838
+ rows: import_zod6.z.number().int().min(1).max(12).default(1),
839
+ gap: import_zod6.z.number().min(0).default(24),
840
+ padding: import_zod6.z.number().min(0).default(0),
841
+ align: import_zod6.z.enum(["start", "center", "end", "stretch"]).default("stretch"),
842
+ justify: import_zod6.z.enum(["start", "center", "end", "stretch"]).default("stretch")
843
+ });
844
+ var mapAlign = (a) => {
845
+ if (a === "start") return "start";
846
+ if (a === "end") return "end";
847
+ if (a === "center") return "center";
848
+ return "stretch";
849
+ };
850
+ var mapJustify = (j) => {
851
+ if (j === "start") return "start";
852
+ if (j === "end") return "end";
853
+ if (j === "center") return "center";
854
+ return "stretch";
855
+ };
856
+ var Grid = ({ columns, rows, gap, padding, align, justify, children }) => {
857
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
858
+ Fill,
859
+ {
860
+ style: {
861
+ display: "grid",
862
+ gridTemplateColumns: `repeat(${columns}, 1fr)`,
863
+ gridTemplateRows: `repeat(${rows}, 1fr)`,
864
+ gap,
865
+ padding,
866
+ alignItems: mapAlign(align),
867
+ justifyItems: mapJustify(justify),
868
+ boxSizing: "border-box"
869
+ },
870
+ children
871
+ }
872
+ );
873
+ };
874
+ var GridComponentMetadata = {
875
+ kind: "primitive",
876
+ category: "layout",
877
+ acceptsChildren: true,
878
+ description: "Grid layout container with configurable rows/columns",
879
+ llmGuidance: "Use Grid for photo collages and dashboards. Provide exactly rows*columns children when possible."
880
+ };
881
+
882
+ // src/components/primitives/Image.tsx
883
+ var import_remotion2 = require("remotion");
884
+ var import_zod7 = require("zod");
885
+ var import_jsx_runtime6 = require("react/jsx-runtime");
886
+ var ImagePropsSchema = import_zod7.z.object({
887
+ src: import_zod7.z.string().min(1),
888
+ fit: import_zod7.z.enum(["cover", "contain"]).default("cover"),
889
+ borderRadius: import_zod7.z.number().min(0).default(0),
890
+ opacity: import_zod7.z.number().min(0).max(1).default(1)
891
+ });
892
+ var resolveAsset = (value) => isRemoteAssetPath(value) ? value : (0, import_remotion2.staticFile)(staticFileInputFromAssetPath(value));
893
+ var Image = ({ src, fit, borderRadius, opacity }) => {
894
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(Fill, { style: { opacity }, children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
895
+ import_remotion2.Img,
896
+ {
897
+ src: resolveAsset(src),
898
+ style: { width: "100%", height: "100%", objectFit: fit, borderRadius }
899
+ }
900
+ ) });
901
+ };
902
+ var ImageComponentMetadata = {
903
+ kind: "primitive",
904
+ category: "image",
905
+ description: "Full-frame image with object-fit options",
906
+ llmGuidance: 'Use Image for pictures and backgrounds. Use fit="cover" for full-bleed, fit="contain" to avoid cropping.'
907
+ };
908
+
909
+ // src/components/primitives/Layer.tsx
910
+ var import_react3 = __toESM(require("react"));
911
+ var import_zod8 = require("zod");
912
+ var import_jsx_runtime7 = require("react/jsx-runtime");
913
+ var LayerPropsSchema = import_zod8.z.object({
914
+ zIndex: import_zod8.z.number().int().default(0).describe("Higher zIndex renders on top"),
915
+ inset: import_zod8.z.number().min(0).default(0).describe("Inset from all sides in pixels"),
916
+ opacity: import_zod8.z.number().min(0).max(1).default(1),
917
+ pointerEvents: import_zod8.z.enum(["none", "auto"]).default("none")
918
+ });
919
+ var Layer = ({ zIndex, inset, opacity, pointerEvents, children }) => {
920
+ const layers = import_react3.default.Children.toArray(children);
921
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
922
+ "div",
923
+ {
924
+ style: {
925
+ position: "absolute",
926
+ left: inset,
927
+ right: inset,
928
+ top: inset,
929
+ bottom: inset,
930
+ zIndex,
931
+ opacity,
932
+ pointerEvents,
933
+ boxSizing: "border-box"
934
+ },
935
+ children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(Fill, { children: layers.map((child, i) => /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { style: { position: "absolute", inset: 0 }, children: child }, i)) })
936
+ }
937
+ );
938
+ };
939
+ var LayerComponentMetadata = {
940
+ kind: "primitive",
941
+ category: "layout",
942
+ acceptsChildren: true,
943
+ minChildren: 1,
944
+ description: "One overlay layer with explicit zIndex inside Layers",
945
+ llmGuidance: "Use Layer inside Layers to control stacking. Put exactly one child in a Layer (recommended)."
946
+ };
947
+
948
+ // src/components/primitives/Layers.tsx
949
+ var import_zod9 = require("zod");
950
+ var import_jsx_runtime8 = require("react/jsx-runtime");
951
+ var LayersPropsSchema = import_zod9.z.object({
952
+ overflow: import_zod9.z.enum(["visible", "hidden"]).default("visible").describe("Clip layers to bounds")
953
+ });
954
+ var Layers = ({ overflow, children }) => {
955
+ return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(Fill, { style: { overflow }, children });
956
+ };
957
+ var LayersComponentMetadata = {
958
+ kind: "primitive",
959
+ category: "layout",
960
+ acceptsChildren: true,
961
+ minChildren: 1,
962
+ description: "Overlay container for stacking children (use Layer for zIndex)",
963
+ llmGuidance: "Use Layers to stack background/content/overlays. Prefer Layer children with explicit zIndex."
964
+ };
965
+
966
+ // src/components/primitives/Scene.tsx
967
+ var import_react4 = __toESM(require("react"));
968
+ var import_remotion3 = require("remotion");
969
+ var import_zod10 = require("zod");
970
+ var import_jsx_runtime9 = require("react/jsx-runtime");
971
+ var ScenePropsSchema = import_zod10.z.object({
477
972
  background: BackgroundSpecSchema
478
973
  });
479
- var resolveAsset = (value) => {
480
- return isRemoteAssetPath(value) ? value : (0, import_remotion2.staticFile)(staticFileInputFromAssetPath(value));
974
+ var resolveAsset2 = (value) => {
975
+ return isRemoteAssetPath(value) ? value : (0, import_remotion3.staticFile)(staticFileInputFromAssetPath(value));
481
976
  };
482
977
  var Scene = ({ background, children }) => {
483
- return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_remotion2.AbsoluteFill, { children: [
484
- background.type === "color" ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_remotion2.AbsoluteFill, { style: { backgroundColor: background.value } }) : background.type === "image" ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
485
- import_remotion2.Img,
978
+ const layers = import_react4.default.Children.toArray(children);
979
+ return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(import_remotion3.AbsoluteFill, { children: [
980
+ background.type === "color" ? /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_remotion3.AbsoluteFill, { style: { backgroundColor: background.value } }) : background.type === "image" ? /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
981
+ import_remotion3.Img,
486
982
  {
487
- src: resolveAsset(background.value),
983
+ src: resolveAsset2(background.value),
488
984
  style: { width: "100%", height: "100%", objectFit: "cover" }
489
985
  }
490
- ) : /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
491
- import_remotion2.Video,
986
+ ) : /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
987
+ import_remotion3.Video,
492
988
  {
493
- src: resolveAsset(background.value),
989
+ src: resolveAsset2(background.value),
494
990
  style: { width: "100%", height: "100%", objectFit: "cover" }
495
991
  }
496
992
  ),
497
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_remotion2.AbsoluteFill, { children })
993
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_remotion3.AbsoluteFill, { children: layers.map((child, i) => /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_remotion3.AbsoluteFill, { children: child }, i)) })
498
994
  ] });
499
995
  };
500
996
  var SceneComponentMetadata = {
501
- category: "primitive",
997
+ kind: "primitive",
998
+ category: "layout",
999
+ acceptsChildren: true,
502
1000
  description: "Scene container with a background and nested children",
503
1001
  llmGuidance: "Use Scene to define a segment of the video. Scene timings must be sequential with no gaps. Put Text and Audio as children."
504
1002
  };
505
1003
 
1004
+ // src/components/primitives/Segment.tsx
1005
+ var import_remotion4 = require("remotion");
1006
+ var import_zod11 = require("zod");
1007
+ var import_jsx_runtime10 = require("react/jsx-runtime");
1008
+ var TransitionPropsSchema = import_zod11.z.object({
1009
+ type: import_zod11.z.string().min(1),
1010
+ durationInFrames: import_zod11.z.number().int().positive(),
1011
+ props: import_zod11.z.record(import_zod11.z.string(), import_zod11.z.unknown()).optional()
1012
+ });
1013
+ var SegmentPropsSchema = import_zod11.z.object({
1014
+ enterTransition: TransitionPropsSchema.optional(),
1015
+ exitTransition: TransitionPropsSchema.optional()
1016
+ });
1017
+ function getSlideOptions(raw) {
1018
+ const parsed = import_zod11.z.object({
1019
+ direction: import_zod11.z.enum(["left", "right", "up", "down"]).default("left"),
1020
+ distance: import_zod11.z.number().int().min(1).max(2e3).default(80)
1021
+ }).safeParse(raw ?? {});
1022
+ if (parsed.success) return parsed.data;
1023
+ return { direction: "left", distance: 80 };
1024
+ }
1025
+ function getZoomOptions(raw) {
1026
+ const parsed = import_zod11.z.object({
1027
+ type: import_zod11.z.enum(["zoomIn", "zoomOut"]).default("zoomIn")
1028
+ }).safeParse(raw ?? {});
1029
+ if (parsed.success) return parsed.data;
1030
+ return { type: "zoomIn" };
1031
+ }
1032
+ function getWipeOptions(raw) {
1033
+ const parsed = import_zod11.z.object({
1034
+ direction: import_zod11.z.enum(["left", "right", "up", "down", "diagonal"]).default("right"),
1035
+ softEdge: import_zod11.z.boolean().default(false)
1036
+ }).safeParse(raw ?? {});
1037
+ if (parsed.success) return parsed.data;
1038
+ return { direction: "right", softEdge: false };
1039
+ }
1040
+ function clipFor(direction, p) {
1041
+ if (direction === "left") return `inset(0 ${100 * (1 - p)}% 0 0)`;
1042
+ if (direction === "right") return `inset(0 0 0 ${100 * (1 - p)}%)`;
1043
+ if (direction === "up") return `inset(0 0 ${100 * (1 - p)}% 0)`;
1044
+ if (direction === "down") return `inset(${100 * (1 - p)}% 0 0 0)`;
1045
+ return `polygon(0 0, ${100 * p}% 0, 0 ${100 * p}%)`;
1046
+ }
1047
+ function getCircularOptions(raw) {
1048
+ const parsed = import_zod11.z.object({
1049
+ direction: import_zod11.z.enum(["open", "close"]).default("open"),
1050
+ center: import_zod11.z.object({
1051
+ x: import_zod11.z.number().min(0).max(1).default(0.5),
1052
+ y: import_zod11.z.number().min(0).max(1).default(0.5)
1053
+ }).default({ x: 0.5, y: 0.5 })
1054
+ }).safeParse(raw ?? {});
1055
+ if (parsed.success) return parsed.data;
1056
+ return { direction: "open", center: { x: 0.5, y: 0.5 } };
1057
+ }
1058
+ var Segment = ({ enterTransition, exitTransition, children, __wavesDurationInFrames }) => {
1059
+ const frame = (0, import_remotion4.useCurrentFrame)();
1060
+ const durationInFrames = __wavesDurationInFrames ?? 0;
1061
+ let opacity = 1;
1062
+ let translateX = 0;
1063
+ let translateY = 0;
1064
+ let scale = 1;
1065
+ let clipPath;
1066
+ let filter;
1067
+ if (enterTransition) {
1068
+ const d = enterTransition.durationInFrames;
1069
+ if (enterTransition.type === "FadeTransition") {
1070
+ opacity *= (0, import_remotion4.interpolate)(frame, [0, d], [0, 1], { extrapolateLeft: "clamp", extrapolateRight: "clamp" });
1071
+ }
1072
+ if (enterTransition.type === "SlideTransition") {
1073
+ const opts = getSlideOptions(enterTransition.props);
1074
+ const t = (0, import_remotion4.interpolate)(frame, [0, d], [1, 0], { extrapolateLeft: "clamp", extrapolateRight: "clamp" });
1075
+ const delta = opts.distance * t;
1076
+ if (opts.direction === "left") translateX += -delta;
1077
+ if (opts.direction === "right") translateX += delta;
1078
+ if (opts.direction === "up") translateY += -delta;
1079
+ if (opts.direction === "down") translateY += delta;
1080
+ opacity *= (0, import_remotion4.interpolate)(frame, [0, d], [0, 1], { extrapolateLeft: "clamp", extrapolateRight: "clamp" });
1081
+ }
1082
+ if (enterTransition.type === "ZoomTransition") {
1083
+ const opts = getZoomOptions(enterTransition.props);
1084
+ const fromScale = opts.type === "zoomIn" ? 1.2 : 0.85;
1085
+ const t = (0, import_remotion4.interpolate)(frame, [0, d], [0, 1], { extrapolateLeft: "clamp", extrapolateRight: "clamp" });
1086
+ scale *= fromScale + (1 - fromScale) * t;
1087
+ opacity *= t;
1088
+ }
1089
+ if (enterTransition.type === "WipeTransition") {
1090
+ const opts = getWipeOptions(enterTransition.props);
1091
+ const t = (0, import_remotion4.interpolate)(frame, [0, d], [0, 1], { extrapolateLeft: "clamp", extrapolateRight: "clamp" });
1092
+ clipPath = clipFor(opts.direction, t);
1093
+ filter = opts.softEdge ? "blur(0.4px)" : void 0;
1094
+ }
1095
+ if (enterTransition.type === "CircularReveal") {
1096
+ const opts = getCircularOptions(enterTransition.props);
1097
+ const open = opts.direction === "open";
1098
+ const t = (0, import_remotion4.interpolate)(frame, [0, d], [0, 1], { extrapolateLeft: "clamp", extrapolateRight: "clamp" });
1099
+ const radiusPct = open ? 150 * t : 150 * (1 - t);
1100
+ clipPath = `circle(${radiusPct}% at ${Math.round(opts.center.x * 100)}% ${Math.round(opts.center.y * 100)}%)`;
1101
+ }
1102
+ }
1103
+ if (exitTransition && durationInFrames > 0) {
1104
+ const d = exitTransition.durationInFrames;
1105
+ const start = Math.max(0, durationInFrames - d);
1106
+ if (exitTransition.type === "FadeTransition") {
1107
+ opacity *= (0, import_remotion4.interpolate)(frame, [start, durationInFrames], [1, 0], { extrapolateLeft: "clamp", extrapolateRight: "clamp" });
1108
+ }
1109
+ if (exitTransition.type === "SlideTransition") {
1110
+ const opts = getSlideOptions(exitTransition.props);
1111
+ const t = (0, import_remotion4.interpolate)(frame, [start, durationInFrames], [0, 1], { extrapolateLeft: "clamp", extrapolateRight: "clamp" });
1112
+ const delta = opts.distance * t;
1113
+ if (opts.direction === "left") translateX += delta;
1114
+ if (opts.direction === "right") translateX += -delta;
1115
+ if (opts.direction === "up") translateY += delta;
1116
+ if (opts.direction === "down") translateY += -delta;
1117
+ opacity *= (0, import_remotion4.interpolate)(frame, [start, durationInFrames], [1, 0], { extrapolateLeft: "clamp", extrapolateRight: "clamp" });
1118
+ }
1119
+ if (exitTransition.type === "ZoomTransition") {
1120
+ const opts = getZoomOptions(exitTransition.props);
1121
+ const toScale = opts.type === "zoomIn" ? 1.25 : 0.75;
1122
+ const t = (0, import_remotion4.interpolate)(frame, [start, durationInFrames], [0, 1], { extrapolateLeft: "clamp", extrapolateRight: "clamp" });
1123
+ scale *= 1 + (toScale - 1) * t;
1124
+ opacity *= 1 - t;
1125
+ }
1126
+ if (exitTransition.type === "WipeTransition") {
1127
+ const opts = getWipeOptions(exitTransition.props);
1128
+ const t = (0, import_remotion4.interpolate)(frame, [start, durationInFrames], [1, 0], { extrapolateLeft: "clamp", extrapolateRight: "clamp" });
1129
+ clipPath = clipFor(opts.direction, t);
1130
+ filter = opts.softEdge ? "blur(0.4px)" : void 0;
1131
+ }
1132
+ if (exitTransition.type === "CircularReveal") {
1133
+ const opts = getCircularOptions(exitTransition.props);
1134
+ const open = opts.direction === "open";
1135
+ const t = (0, import_remotion4.interpolate)(frame, [start, durationInFrames], [0, 1], { extrapolateLeft: "clamp", extrapolateRight: "clamp" });
1136
+ const radiusPct = open ? 150 * (1 - t) : 150 * t;
1137
+ clipPath = `circle(${radiusPct}% at ${Math.round(opts.center.x * 100)}% ${Math.round(opts.center.y * 100)}%)`;
1138
+ }
1139
+ }
1140
+ const transform = `translate(${translateX}px, ${translateY}px) scale(${scale})`;
1141
+ return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_remotion4.AbsoluteFill, { style: { opacity, transform, clipPath, filter }, children });
1142
+ };
1143
+ var SegmentComponentMetadata = {
1144
+ kind: "primitive",
1145
+ category: "layout",
1146
+ internal: true,
1147
+ acceptsChildren: true,
1148
+ minChildren: 1,
1149
+ maxChildren: 1,
1150
+ description: "Internal segment wrapper (used by v2 segments compiler)"
1151
+ };
1152
+
1153
+ // src/components/primitives/Shape.tsx
1154
+ var import_zod12 = require("zod");
1155
+ var import_jsx_runtime11 = require("react/jsx-runtime");
1156
+ var ShapePropsSchema = import_zod12.z.object({
1157
+ shape: import_zod12.z.enum(["rect", "circle"]).default("rect"),
1158
+ x: import_zod12.z.number().default(0),
1159
+ y: import_zod12.z.number().default(0),
1160
+ width: import_zod12.z.number().positive().default(100),
1161
+ height: import_zod12.z.number().positive().default(100),
1162
+ fill: import_zod12.z.string().default("#FFFFFF"),
1163
+ strokeColor: import_zod12.z.string().optional(),
1164
+ strokeWidth: import_zod12.z.number().min(0).default(0),
1165
+ opacity: import_zod12.z.number().min(0).max(1).default(1)
1166
+ });
1167
+ var Shape = ({
1168
+ shape,
1169
+ x,
1170
+ y,
1171
+ width,
1172
+ height,
1173
+ fill,
1174
+ strokeColor,
1175
+ strokeWidth,
1176
+ opacity
1177
+ }) => {
1178
+ return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
1179
+ "div",
1180
+ {
1181
+ style: {
1182
+ position: "absolute",
1183
+ left: x,
1184
+ top: y,
1185
+ width,
1186
+ height,
1187
+ backgroundColor: fill,
1188
+ borderRadius: shape === "circle" ? 9999 : 0,
1189
+ border: strokeWidth > 0 ? `${strokeWidth}px solid ${strokeColor ?? fill}` : void 0,
1190
+ opacity
1191
+ }
1192
+ }
1193
+ );
1194
+ };
1195
+ var ShapeComponentMetadata = {
1196
+ kind: "primitive",
1197
+ category: "layout",
1198
+ description: "Simple rect/circle shape for UI accents",
1199
+ llmGuidance: "Use Shape for lines, badges, and simple UI blocks. Use circle for dots and rings."
1200
+ };
1201
+
1202
+ // src/components/primitives/Stack.tsx
1203
+ var import_zod13 = require("zod");
1204
+ var import_jsx_runtime12 = require("react/jsx-runtime");
1205
+ var StackPropsSchema = import_zod13.z.object({
1206
+ direction: import_zod13.z.enum(["row", "column"]).default("column"),
1207
+ gap: import_zod13.z.number().min(0).default(24),
1208
+ padding: import_zod13.z.number().min(0).default(0),
1209
+ align: import_zod13.z.enum(["start", "center", "end", "stretch"]).default("center"),
1210
+ justify: import_zod13.z.enum(["start", "center", "end", "between"]).default("center")
1211
+ });
1212
+ var mapAlign2 = (a) => {
1213
+ if (a === "start") return "flex-start";
1214
+ if (a === "end") return "flex-end";
1215
+ if (a === "stretch") return "stretch";
1216
+ return "center";
1217
+ };
1218
+ var mapJustify2 = (j) => {
1219
+ if (j === "start") return "flex-start";
1220
+ if (j === "end") return "flex-end";
1221
+ if (j === "between") return "space-between";
1222
+ return "center";
1223
+ };
1224
+ var Stack = ({ direction, gap, padding, align, justify, children }) => {
1225
+ return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
1226
+ Fill,
1227
+ {
1228
+ style: {
1229
+ display: "flex",
1230
+ flexDirection: direction,
1231
+ gap,
1232
+ padding,
1233
+ alignItems: mapAlign2(align),
1234
+ justifyContent: mapJustify2(justify),
1235
+ boxSizing: "border-box"
1236
+ },
1237
+ children
1238
+ }
1239
+ );
1240
+ };
1241
+ var StackComponentMetadata = {
1242
+ kind: "primitive",
1243
+ category: "layout",
1244
+ acceptsChildren: true,
1245
+ description: "Flexbox stack layout (row/column) with gap and alignment",
1246
+ llmGuidance: "Use Stack to arrange child components in a row or column without manual positioning."
1247
+ };
1248
+
506
1249
  // src/components/primitives/Text.tsx
507
- var import_remotion3 = require("remotion");
508
- var import_zod5 = require("zod");
509
- var import_jsx_runtime3 = require("react/jsx-runtime");
510
- var TextPropsSchema = import_zod5.z.object({
511
- content: import_zod5.z.string(),
512
- fontSize: import_zod5.z.number().default(48),
513
- color: import_zod5.z.string().default("#FFFFFF"),
514
- position: import_zod5.z.enum(["top", "center", "bottom", "left", "right"]).default("center"),
515
- animation: import_zod5.z.enum(["none", "fade", "slide", "zoom"]).default("fade")
516
- });
517
- var getPositionStyles = (position) => {
1250
+ var import_remotion5 = require("remotion");
1251
+ var import_zod14 = require("zod");
1252
+ var import_jsx_runtime13 = require("react/jsx-runtime");
1253
+ var TextPropsSchema = import_zod14.z.object({
1254
+ content: import_zod14.z.string(),
1255
+ fontSize: import_zod14.z.number().default(48),
1256
+ color: import_zod14.z.string().default("#FFFFFF"),
1257
+ fontFamily: import_zod14.z.string().default("Inter"),
1258
+ position: import_zod14.z.enum(["top", "center", "bottom", "left", "right"]).default("center"),
1259
+ animation: import_zod14.z.enum(["none", "fade", "slide", "zoom"]).default("fade"),
1260
+ maxWidthPct: import_zod14.z.number().min(0.1).max(1).default(0.9),
1261
+ safeInsetPct: import_zod14.z.number().min(0).max(0.25).default(0.06),
1262
+ textAlign: import_zod14.z.enum(["left", "center", "right"]).default("center")
1263
+ });
1264
+ var getPositionStyles = (position, safeInsetPct) => {
1265
+ const inset = `${(safeInsetPct * 100).toFixed(2)}%`;
518
1266
  const base = {
519
1267
  display: "flex",
520
1268
  justifyContent: "center",
@@ -522,13 +1270,13 @@ var getPositionStyles = (position) => {
522
1270
  };
523
1271
  switch (position) {
524
1272
  case "top":
525
- return { ...base, alignItems: "flex-start", paddingTop: 60 };
1273
+ return { ...base, alignItems: "flex-start", paddingTop: inset };
526
1274
  case "bottom":
527
- return { ...base, alignItems: "flex-end", paddingBottom: 60 };
1275
+ return { ...base, alignItems: "flex-end", paddingBottom: inset };
528
1276
  case "left":
529
- return { ...base, justifyContent: "flex-start", paddingLeft: 60 };
1277
+ return { ...base, justifyContent: "flex-start", paddingLeft: inset };
530
1278
  case "right":
531
- return { ...base, justifyContent: "flex-end", paddingRight: 60 };
1279
+ return { ...base, justifyContent: "flex-end", paddingRight: inset };
532
1280
  default:
533
1281
  return base;
534
1282
  }
@@ -537,29 +1285,29 @@ var getAnimationStyle = (frame, animation) => {
537
1285
  const animDuration = 30;
538
1286
  switch (animation) {
539
1287
  case "fade": {
540
- const opacity = (0, import_remotion3.interpolate)(frame, [0, animDuration], [0, 1], {
1288
+ const opacity = (0, import_remotion5.interpolate)(frame, [0, animDuration], [0, 1], {
541
1289
  extrapolateLeft: "clamp",
542
1290
  extrapolateRight: "clamp"
543
1291
  });
544
1292
  return { opacity };
545
1293
  }
546
1294
  case "slide": {
547
- const translateY = (0, import_remotion3.interpolate)(frame, [0, animDuration], [50, 0], {
1295
+ const translateY = (0, import_remotion5.interpolate)(frame, [0, animDuration], [50, 0], {
548
1296
  extrapolateLeft: "clamp",
549
1297
  extrapolateRight: "clamp"
550
1298
  });
551
- const opacity = (0, import_remotion3.interpolate)(frame, [0, animDuration], [0, 1], {
1299
+ const opacity = (0, import_remotion5.interpolate)(frame, [0, animDuration], [0, 1], {
552
1300
  extrapolateLeft: "clamp",
553
1301
  extrapolateRight: "clamp"
554
1302
  });
555
1303
  return { transform: `translateY(${translateY}px)`, opacity };
556
1304
  }
557
1305
  case "zoom": {
558
- const scale = (0, import_remotion3.interpolate)(frame, [0, animDuration], [0.8, 1], {
1306
+ const scale = (0, import_remotion5.interpolate)(frame, [0, animDuration], [0.8, 1], {
559
1307
  extrapolateLeft: "clamp",
560
1308
  extrapolateRight: "clamp"
561
1309
  });
562
- const opacity = (0, import_remotion3.interpolate)(frame, [0, animDuration], [0, 1], {
1310
+ const opacity = (0, import_remotion5.interpolate)(frame, [0, animDuration], [0, 1], {
563
1311
  extrapolateLeft: "clamp",
564
1312
  extrapolateRight: "clamp"
565
1313
  });
@@ -569,18 +1317,32 @@ var getAnimationStyle = (frame, animation) => {
569
1317
  return {};
570
1318
  }
571
1319
  };
572
- var Text = ({ content, fontSize, color, position, animation }) => {
573
- const frame = (0, import_remotion3.useCurrentFrame)();
574
- const positionStyles = getPositionStyles(position);
1320
+ var Text = ({
1321
+ content,
1322
+ fontSize,
1323
+ color,
1324
+ fontFamily,
1325
+ position,
1326
+ animation,
1327
+ maxWidthPct,
1328
+ safeInsetPct,
1329
+ textAlign
1330
+ }) => {
1331
+ const frame = (0, import_remotion5.useCurrentFrame)();
1332
+ const positionStyles6 = getPositionStyles(position, safeInsetPct);
575
1333
  const animationStyles = getAnimationStyle(frame, animation);
576
- return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_remotion3.AbsoluteFill, { style: positionStyles, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
1334
+ return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(Fill, { style: positionStyles6, children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
577
1335
  "div",
578
1336
  {
579
1337
  style: {
580
1338
  fontSize,
581
1339
  color,
1340
+ fontFamily,
582
1341
  fontWeight: 600,
583
- textAlign: "center",
1342
+ textAlign,
1343
+ maxWidth: `${Math.round(maxWidthPct * 100)}%`,
1344
+ overflowWrap: "anywhere",
1345
+ wordBreak: "break-word",
584
1346
  ...animationStyles
585
1347
  },
586
1348
  children: content
@@ -588,7 +1350,8 @@ var Text = ({ content, fontSize, color, position, animation }) => {
588
1350
  ) });
589
1351
  };
590
1352
  var TextComponentMetadata = {
591
- category: "primitive",
1353
+ kind: "primitive",
1354
+ category: "text",
592
1355
  description: "Displays animated text with positioning and animation options",
593
1356
  llmGuidance: 'Use for titles, subtitles, captions. Keep content under 100 characters for readability. Position "center" works best for titles.',
594
1357
  examples: [
@@ -609,48 +1372,2468 @@ var TextComponentMetadata = {
609
1372
  ]
610
1373
  };
611
1374
 
612
- // src/components/registry.ts
613
- function registerBuiltInComponents() {
614
- if (!globalRegistry.has("Scene")) {
615
- globalRegistry.register({
616
- type: "Scene",
617
- component: Scene,
618
- propsSchema: ScenePropsSchema,
619
- metadata: SceneComponentMetadata
620
- });
621
- }
622
- if (!globalRegistry.has("Text")) {
623
- globalRegistry.register({
624
- type: "Text",
625
- component: Text,
626
- propsSchema: TextPropsSchema,
627
- metadata: TextComponentMetadata
628
- });
629
- }
630
- if (!globalRegistry.has("Audio")) {
631
- globalRegistry.register({
632
- type: "Audio",
633
- component: Audio,
634
- propsSchema: AudioPropsSchema,
635
- metadata: AudioComponentMetadata
636
- });
637
- }
638
- }
1375
+ // src/components/primitives/Video.tsx
1376
+ var import_remotion6 = require("remotion");
1377
+ var import_zod15 = require("zod");
1378
+ var import_jsx_runtime14 = require("react/jsx-runtime");
1379
+ var VideoPropsSchema = import_zod15.z.object({
1380
+ src: import_zod15.z.string().min(1),
1381
+ fit: import_zod15.z.enum(["cover", "contain"]).default("cover"),
1382
+ borderRadius: import_zod15.z.number().min(0).default(0),
1383
+ opacity: import_zod15.z.number().min(0).max(1).default(1),
1384
+ muted: import_zod15.z.boolean().default(true)
1385
+ });
1386
+ var resolveAsset3 = (value) => isRemoteAssetPath(value) ? value : (0, import_remotion6.staticFile)(staticFileInputFromAssetPath(value));
1387
+ var Video2 = ({ src, fit, borderRadius, opacity, muted }) => {
1388
+ return /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(Fill, { style: { opacity }, children: /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
1389
+ import_remotion6.Video,
1390
+ {
1391
+ src: resolveAsset3(src),
1392
+ muted,
1393
+ style: { width: "100%", height: "100%", objectFit: fit, borderRadius }
1394
+ }
1395
+ ) });
1396
+ };
1397
+ var VideoComponentMetadata = {
1398
+ kind: "primitive",
1399
+ category: "media",
1400
+ description: "Full-frame video with object-fit options",
1401
+ llmGuidance: "Use Video for B-roll. Keep videos short and muted unless you intentionally want audio."
1402
+ };
639
1403
 
640
- // src/llm/prompt.ts
641
- var RULES = {
642
- timing: [
643
- "All timing is in frames (integers).",
644
- "Every component has timing: { from, durationInFrames }.",
645
- "Scenes must be sequential: scene[0].timing.from = 0 and each next scene starts where the previous ends (no gaps/overlaps).",
646
- "Sum of all scene durations must equal video.durationInFrames.",
647
- "Child components inside a Scene must fit within the Scene duration."
648
- ],
649
- assets: [
650
- 'Asset paths must be either full URLs (http:// or https://) or absolute public paths starting with "/".',
651
- 'When using absolute public paths ("/assets/..."), the renderer resolves them relative to the --publicDir passed at render time.'
652
- ],
653
- ids: [
1404
+ // src/components/composites/AnimatedCounter.tsx
1405
+ var import_remotion7 = require("remotion");
1406
+ var import_zod16 = require("zod");
1407
+ var import_jsx_runtime15 = require("react/jsx-runtime");
1408
+ var AnimatedCounterPropsSchema = import_zod16.z.object({
1409
+ from: import_zod16.z.number().default(0),
1410
+ to: import_zod16.z.number().default(100),
1411
+ fontSize: import_zod16.z.number().min(8).max(300).default(96),
1412
+ color: import_zod16.z.string().default("#FFFFFF"),
1413
+ fontFamily: import_zod16.z.string().default("Inter"),
1414
+ fontWeight: import_zod16.z.number().int().min(100).max(900).default(700),
1415
+ icon: import_zod16.z.string().optional(),
1416
+ suffix: import_zod16.z.string().optional(),
1417
+ animationType: import_zod16.z.enum(["spring", "linear"]).default("spring")
1418
+ });
1419
+ var AnimatedCounter = ({
1420
+ from,
1421
+ to,
1422
+ fontSize,
1423
+ color,
1424
+ fontFamily,
1425
+ fontWeight,
1426
+ icon,
1427
+ suffix,
1428
+ animationType,
1429
+ __wavesDurationInFrames
1430
+ }) => {
1431
+ const frame = (0, import_remotion7.useCurrentFrame)();
1432
+ const { fps } = (0, import_remotion7.useVideoConfig)();
1433
+ const total = Math.max(1, __wavesDurationInFrames ?? 1);
1434
+ const progress = animationType === "spring" ? (0, import_remotion7.spring)({ frame, fps, config: { damping: 14, stiffness: 110, mass: 0.9 } }) : (0, import_remotion7.interpolate)(frame, [0, total], [0, 1], { extrapolateLeft: "clamp", extrapolateRight: "clamp" });
1435
+ const value = from + (to - from) * Math.max(0, Math.min(1, progress));
1436
+ const rounded = Math.round(value);
1437
+ const label = `${rounded.toLocaleString()}${suffix ?? ""}`;
1438
+ return /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(Fill, { style: { display: "flex", alignItems: "center", justifyContent: "center" }, children: /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { style: { textAlign: "center" }, children: [
1439
+ icon ? /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("div", { style: { fontSize: Math.round(fontSize * 0.5), marginBottom: 18 }, children: icon }) : null,
1440
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("div", { style: { fontSize, color, fontFamily, fontWeight, lineHeight: 1 }, children: label })
1441
+ ] }) });
1442
+ };
1443
+ var AnimatedCounterComponentMetadata = {
1444
+ kind: "composite",
1445
+ category: "data",
1446
+ description: "Animated numeric counter (spring or linear), optionally with an icon and suffix",
1447
+ llmGuidance: 'Use for big stats. animationType="spring" feels natural. suffix for units (%, K, M).',
1448
+ examples: [
1449
+ { from: 0, to: 100, suffix: "%", icon: "\u{1F4C8}" },
1450
+ { from: 0, to: 25e3, suffix: "+", animationType: "linear" }
1451
+ ]
1452
+ };
1453
+
1454
+ // src/components/composites/BarChart.tsx
1455
+ var import_remotion8 = require("remotion");
1456
+ var import_zod17 = require("zod");
1457
+ var import_jsx_runtime16 = require("react/jsx-runtime");
1458
+ var BarChartPropsSchema = import_zod17.z.object({
1459
+ data: import_zod17.z.array(import_zod17.z.object({
1460
+ label: import_zod17.z.string().min(1),
1461
+ value: import_zod17.z.number(),
1462
+ color: import_zod17.z.string().optional()
1463
+ })).min(2).max(8),
1464
+ orientation: import_zod17.z.enum(["horizontal", "vertical"]).default("vertical"),
1465
+ showValues: import_zod17.z.boolean().default(true),
1466
+ showGrid: import_zod17.z.boolean().default(false),
1467
+ maxValue: import_zod17.z.number().optional()
1468
+ });
1469
+ var BarChart = ({ data, orientation, showValues, showGrid, maxValue }) => {
1470
+ const frame = (0, import_remotion8.useCurrentFrame)();
1471
+ const { fps } = (0, import_remotion8.useVideoConfig)();
1472
+ const max = maxValue ?? Math.max(...data.map((d) => d.value));
1473
+ return /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(Fill, { style: { padding: 90, boxSizing: "border-box" }, children: [
1474
+ showGrid ? /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("div", { style: { position: "absolute", left: 90, right: 90, top: 90, bottom: 90, opacity: 0.15, backgroundImage: "linear-gradient(#fff 1px, transparent 1px)", backgroundSize: "100% 60px" } }) : null,
1475
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
1476
+ "div",
1477
+ {
1478
+ style: {
1479
+ display: "flex",
1480
+ flexDirection: orientation === "vertical" ? "row" : "column",
1481
+ gap: 24,
1482
+ width: "100%",
1483
+ height: "100%",
1484
+ alignItems: orientation === "vertical" ? "flex-end" : "stretch",
1485
+ justifyContent: "space-between"
1486
+ },
1487
+ children: data.map((d, i) => {
1488
+ const p = (0, import_remotion8.spring)({ frame: frame - i * 4, fps, config: { damping: 14, stiffness: 120, mass: 0.9 } });
1489
+ const ratio = max === 0 ? 0 : d.value / max * p;
1490
+ const color = d.color ?? "#0A84FF";
1491
+ return /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("div", { style: { flex: 1, display: "flex", flexDirection: "column", alignItems: "center", gap: 10 }, children: [
1492
+ orientation === "vertical" ? /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("div", { style: { width: "100%", flex: 1, display: "flex", alignItems: "flex-end" }, children: /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("div", { style: { width: "100%", height: `${Math.round(ratio * 100)}%`, backgroundColor: color, borderRadius: 12 } }) }) : /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("div", { style: { width: "100%", display: "flex", alignItems: "center", gap: 12 }, children: /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("div", { style: { flex: 1, height: 18, backgroundColor: "rgba(255,255,255,0.15)", borderRadius: 9999, overflow: "hidden" }, children: /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("div", { style: { width: `${Math.round(ratio * 100)}%`, height: "100%", backgroundColor: color } }) }) }),
1493
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("div", { style: { color: "#FFFFFF", fontWeight: 700, fontSize: 22, opacity: 0.9 }, children: d.label }),
1494
+ showValues ? /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("div", { style: { color: "#FFFFFF", fontWeight: 800, fontSize: 26 }, children: Math.round(d.value).toLocaleString() }) : null
1495
+ ] }, d.label);
1496
+ })
1497
+ }
1498
+ )
1499
+ ] });
1500
+ };
1501
+ var BarChartComponentMetadata = {
1502
+ kind: "composite",
1503
+ category: "data",
1504
+ description: "Animated bar chart (vertical or horizontal)",
1505
+ llmGuidance: "Use 2-6 bars. Provide maxValue to lock scale across multiple charts."
1506
+ };
1507
+
1508
+ // src/components/composites/CardStack.tsx
1509
+ var import_remotion9 = require("remotion");
1510
+ var import_zod18 = require("zod");
1511
+ var import_jsx_runtime17 = require("react/jsx-runtime");
1512
+ var CardSchema = import_zod18.z.object({
1513
+ title: import_zod18.z.string().min(1),
1514
+ content: import_zod18.z.string().min(1),
1515
+ backgroundColor: import_zod18.z.string().optional()
1516
+ });
1517
+ var CardStackPropsSchema = import_zod18.z.object({
1518
+ cards: import_zod18.z.array(CardSchema).min(2).max(5),
1519
+ transition: import_zod18.z.enum(["flip", "slide", "fade"]).default("flip"),
1520
+ displayDuration: import_zod18.z.number().int().min(30).max(150).default(90)
1521
+ });
1522
+ var CardStack = ({ cards, transition, displayDuration }) => {
1523
+ const frame = (0, import_remotion9.useCurrentFrame)();
1524
+ const { fps } = (0, import_remotion9.useVideoConfig)();
1525
+ const index = Math.min(cards.length - 1, Math.floor(frame / displayDuration));
1526
+ const local = frame - index * displayDuration;
1527
+ const p = (0, import_remotion9.spring)({ frame: local, fps, config: { damping: 14, stiffness: 120, mass: 0.9 } });
1528
+ const card = cards[index];
1529
+ const bg = card.backgroundColor ?? "rgba(255,255,255,0.1)";
1530
+ const opacity = transition === "fade" ? p : 1;
1531
+ const slideX = transition === "slide" ? (0, import_remotion9.interpolate)(p, [0, 1], [80, 0]) : 0;
1532
+ const rotateY = transition === "flip" ? (0, import_remotion9.interpolate)(p, [0, 1], [70, 0]) : 0;
1533
+ return /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(Fill, { style: { display: "flex", justifyContent: "center", alignItems: "center" }, children: /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(
1534
+ "div",
1535
+ {
1536
+ style: {
1537
+ width: 980,
1538
+ height: 520,
1539
+ borderRadius: 28,
1540
+ padding: 60,
1541
+ boxSizing: "border-box",
1542
+ backgroundColor: bg,
1543
+ boxShadow: "0 30px 90px rgba(0,0,0,0.35)",
1544
+ color: "#FFFFFF",
1545
+ transform: `translateX(${slideX}px) rotateY(${rotateY}deg)`,
1546
+ transformStyle: "preserve-3d",
1547
+ opacity
1548
+ },
1549
+ children: [
1550
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("div", { style: { fontSize: 56, fontWeight: 900, marginBottom: 22 }, children: card.title }),
1551
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("div", { style: { fontSize: 30, fontWeight: 700, opacity: 0.9, lineHeight: 1.3 }, children: card.content })
1552
+ ]
1553
+ }
1554
+ ) });
1555
+ };
1556
+ var CardStackComponentMetadata = {
1557
+ kind: "composite",
1558
+ category: "layout",
1559
+ description: "Sequential stacked cards (2-5) with flip/slide/fade transitions",
1560
+ llmGuidance: "Use for steps/features. displayDuration is frames per card."
1561
+ };
1562
+
1563
+ // src/components/composites/CircularReveal.tsx
1564
+ var import_react5 = __toESM(require("react"));
1565
+ var import_remotion10 = require("remotion");
1566
+ var import_zod19 = require("zod");
1567
+ var import_jsx_runtime18 = require("react/jsx-runtime");
1568
+ var CenterSchema = import_zod19.z.object({
1569
+ x: import_zod19.z.number().min(0).max(1).default(0.5),
1570
+ y: import_zod19.z.number().min(0).max(1).default(0.5)
1571
+ });
1572
+ var CircularRevealPropsSchema = import_zod19.z.object({
1573
+ durationInFrames: import_zod19.z.number().int().min(10).max(60).default(30),
1574
+ direction: import_zod19.z.enum(["open", "close"]).default("open"),
1575
+ center: CenterSchema.optional(),
1576
+ phase: import_zod19.z.enum(["in", "out", "inOut"]).default("inOut")
1577
+ });
1578
+ var CircularReveal = ({
1579
+ durationInFrames,
1580
+ direction,
1581
+ center,
1582
+ phase,
1583
+ children,
1584
+ __wavesDurationInFrames
1585
+ }) => {
1586
+ const layers = import_react5.default.Children.toArray(children);
1587
+ const frame = (0, import_remotion10.useCurrentFrame)();
1588
+ const total = Math.max(1, __wavesDurationInFrames ?? 1);
1589
+ const d = Math.min(durationInFrames, total);
1590
+ const c = center ?? { x: 0.5, y: 0.5 };
1591
+ const open = direction === "open";
1592
+ let radiusPct = open ? 0 : 150;
1593
+ if (phase === "in" || phase === "inOut") {
1594
+ const t = (0, import_remotion10.interpolate)(frame, [0, d], [0, 1], { extrapolateLeft: "clamp", extrapolateRight: "clamp" });
1595
+ radiusPct = open ? 150 * t : 150 * (1 - t);
1596
+ }
1597
+ if (phase === "out" || phase === "inOut") {
1598
+ const start = Math.max(0, total - d);
1599
+ const t = (0, import_remotion10.interpolate)(frame, [start, total], [0, 1], { extrapolateLeft: "clamp", extrapolateRight: "clamp" });
1600
+ radiusPct = open ? 150 * (1 - t) : 150 * t;
1601
+ }
1602
+ return /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(Fill, { style: { clipPath: `circle(${radiusPct}% at ${Math.round(c.x * 100)}% ${Math.round(c.y * 100)}%)` }, children: layers.map((child, i) => /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("div", { style: { position: "absolute", inset: 0 }, children: child }, i)) });
1603
+ };
1604
+ var CircularRevealComponentMetadata = {
1605
+ kind: "composite",
1606
+ category: "transition",
1607
+ acceptsChildren: true,
1608
+ minChildren: 1,
1609
+ description: "Circular iris reveal/hide transition wrapper",
1610
+ llmGuidance: 'direction="open" reveals from center, direction="close" hides to a point. center controls origin.'
1611
+ };
1612
+
1613
+ // src/components/composites/CountUpText.tsx
1614
+ var import_remotion11 = require("remotion");
1615
+ var import_zod20 = require("zod");
1616
+ var import_jsx_runtime19 = require("react/jsx-runtime");
1617
+ var CountUpTextPropsSchema = import_zod20.z.object({
1618
+ from: import_zod20.z.number().default(0),
1619
+ to: import_zod20.z.number().default(100),
1620
+ fontSize: import_zod20.z.number().min(8).max(240).default(72),
1621
+ color: import_zod20.z.string().default("#FFFFFF"),
1622
+ fontFamily: import_zod20.z.string().default("Inter"),
1623
+ fontWeight: import_zod20.z.number().int().min(100).max(900).default(700),
1624
+ position: import_zod20.z.enum(["top", "center", "bottom"]).default("center"),
1625
+ format: import_zod20.z.enum(["integer", "decimal", "currency", "percentage"]).default("integer"),
1626
+ decimals: import_zod20.z.number().int().min(0).max(4).default(0),
1627
+ prefix: import_zod20.z.string().optional(),
1628
+ suffix: import_zod20.z.string().optional()
1629
+ });
1630
+ var positionStyles = (position) => {
1631
+ if (position === "top") return { justifyContent: "center", alignItems: "flex-start", paddingTop: 90 };
1632
+ if (position === "bottom") return { justifyContent: "center", alignItems: "flex-end", paddingBottom: 90 };
1633
+ return { justifyContent: "center", alignItems: "center" };
1634
+ };
1635
+ function formatValue(v, format, decimals) {
1636
+ if (format === "integer") return Math.round(v).toLocaleString();
1637
+ if (format === "decimal") return v.toFixed(decimals);
1638
+ if (format === "currency") return `$${v.toFixed(decimals).toLocaleString()}`;
1639
+ return `${(v * 100).toFixed(decimals)}%`;
1640
+ }
1641
+ var CountUpText = ({
1642
+ from,
1643
+ to,
1644
+ fontSize,
1645
+ color,
1646
+ fontFamily,
1647
+ fontWeight,
1648
+ position,
1649
+ format,
1650
+ decimals,
1651
+ prefix,
1652
+ suffix,
1653
+ __wavesDurationInFrames
1654
+ }) => {
1655
+ const frame = (0, import_remotion11.useCurrentFrame)();
1656
+ const { fps } = (0, import_remotion11.useVideoConfig)();
1657
+ const total = Math.max(1, __wavesDurationInFrames ?? 1);
1658
+ const p = (0, import_remotion11.spring)({ frame, fps, config: { damping: 14, stiffness: 110, mass: 0.9 } });
1659
+ const progress = Math.max(0, Math.min(1, p));
1660
+ const value = from + (to - from) * progress;
1661
+ const fade = (0, import_remotion11.interpolate)(frame, [0, Math.min(12, total)], [0, 1], { extrapolateLeft: "clamp", extrapolateRight: "clamp" });
1662
+ const label = `${prefix ?? ""}${formatValue(value, format, decimals)}${suffix ?? ""}`;
1663
+ return /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(Fill, { style: { display: "flex", ...positionStyles(position) }, children: /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("div", { style: { fontSize, color, fontFamily, fontWeight, opacity: fade, lineHeight: 1 }, children: label }) });
1664
+ };
1665
+ var CountUpTextComponentMetadata = {
1666
+ kind: "composite",
1667
+ category: "text",
1668
+ description: "Counts from a start value to an end value with formatting options",
1669
+ llmGuidance: 'Use for metrics. format="currency" adds $, format="percentage" multiplies by 100 and adds %.'
1670
+ };
1671
+
1672
+ // src/components/composites/FadeTransition.tsx
1673
+ var import_react6 = __toESM(require("react"));
1674
+ var import_remotion12 = require("remotion");
1675
+ var import_zod21 = require("zod");
1676
+ var import_jsx_runtime20 = require("react/jsx-runtime");
1677
+ var EasingSchema = import_zod21.z.enum(["linear", "easeIn", "easeOut", "easeInOut"]);
1678
+ function ease(t, easing) {
1679
+ const x = Math.max(0, Math.min(1, t));
1680
+ if (easing === "linear") return x;
1681
+ if (easing === "easeIn") return x * x;
1682
+ if (easing === "easeOut") return 1 - (1 - x) * (1 - x);
1683
+ return x < 0.5 ? 2 * x * x : 1 - 2 * (1 - x) * (1 - x);
1684
+ }
1685
+ var FadeTransitionPropsSchema = import_zod21.z.object({
1686
+ durationInFrames: import_zod21.z.number().int().min(10).max(60).default(30),
1687
+ easing: EasingSchema.default("easeInOut"),
1688
+ phase: import_zod21.z.enum(["in", "out", "inOut"]).default("inOut")
1689
+ });
1690
+ var FadeTransition = ({
1691
+ durationInFrames,
1692
+ easing,
1693
+ phase,
1694
+ children,
1695
+ __wavesDurationInFrames
1696
+ }) => {
1697
+ const layers = import_react6.default.Children.toArray(children);
1698
+ const frame = (0, import_remotion12.useCurrentFrame)();
1699
+ const total = Math.max(1, __wavesDurationInFrames ?? 1);
1700
+ const d = Math.min(durationInFrames, total);
1701
+ let opacity = 1;
1702
+ if (phase === "in" || phase === "inOut") {
1703
+ const t = (0, import_remotion12.interpolate)(frame, [0, d], [0, 1], { extrapolateLeft: "clamp", extrapolateRight: "clamp" });
1704
+ opacity *= ease(t, easing);
1705
+ }
1706
+ if (phase === "out" || phase === "inOut") {
1707
+ const start = Math.max(0, total - d);
1708
+ const t = (0, import_remotion12.interpolate)(frame, [start, total], [0, 1], { extrapolateLeft: "clamp", extrapolateRight: "clamp" });
1709
+ opacity *= 1 - ease(t, easing);
1710
+ }
1711
+ return /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(Fill, { style: { opacity }, children: layers.map((child, i) => /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("div", { style: { position: "absolute", inset: 0 }, children: child }, i)) });
1712
+ };
1713
+ var FadeTransitionComponentMetadata = {
1714
+ kind: "composite",
1715
+ category: "transition",
1716
+ acceptsChildren: true,
1717
+ minChildren: 1,
1718
+ description: "Fade in/out wrapper (used for segment transitions and overlays)",
1719
+ llmGuidance: "Use for gentle transitions. durationInFrames ~30 is standard."
1720
+ };
1721
+
1722
+ // src/components/composites/GlitchText.tsx
1723
+ var import_remotion13 = require("remotion");
1724
+ var import_zod22 = require("zod");
1725
+ var import_jsx_runtime21 = require("react/jsx-runtime");
1726
+ var GlitchTextPropsSchema = import_zod22.z.object({
1727
+ content: import_zod22.z.string().max(100),
1728
+ fontSize: import_zod22.z.number().min(8).max(240).default(72),
1729
+ color: import_zod22.z.string().default("#FFFFFF"),
1730
+ fontFamily: import_zod22.z.string().default("monospace"),
1731
+ position: import_zod22.z.enum(["top", "center", "bottom"]).default("center"),
1732
+ intensity: import_zod22.z.number().int().min(1).max(10).default(5),
1733
+ glitchDuration: import_zod22.z.number().int().min(5).max(30).default(10)
1734
+ });
1735
+ var positionStyles2 = (position) => {
1736
+ if (position === "top") return { justifyContent: "center", alignItems: "flex-start", paddingTop: 90 };
1737
+ if (position === "bottom") return { justifyContent: "center", alignItems: "flex-end", paddingBottom: 90 };
1738
+ return { justifyContent: "center", alignItems: "center" };
1739
+ };
1740
+ function pseudoRandom(n) {
1741
+ const x = Math.sin(n * 12.9898) * 43758.5453;
1742
+ return x - Math.floor(x);
1743
+ }
1744
+ var GlitchText = ({
1745
+ content,
1746
+ fontSize,
1747
+ color,
1748
+ fontFamily,
1749
+ position,
1750
+ intensity,
1751
+ glitchDuration
1752
+ }) => {
1753
+ const frame = (0, import_remotion13.useCurrentFrame)();
1754
+ const active = frame < glitchDuration;
1755
+ const seed = frame + 1;
1756
+ const jitter = active ? (pseudoRandom(seed) - 0.5) * intensity * 6 : 0;
1757
+ const jitterY = active ? (pseudoRandom(seed + 99) - 0.5) * intensity * 3 : 0;
1758
+ const baseStyle = {
1759
+ position: "absolute",
1760
+ fontSize,
1761
+ fontFamily,
1762
+ fontWeight: 800,
1763
+ letterSpacing: 1,
1764
+ textTransform: "uppercase",
1765
+ textAlign: "center"
1766
+ };
1767
+ return /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(Fill, { style: { display: "flex", ...positionStyles2(position) }, children: /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("div", { style: { position: "relative" }, children: [
1768
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("div", { style: { ...baseStyle, color, transform: `translate(${jitter}px, ${jitterY}px)` }, children: content }),
1769
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("div", { style: { ...baseStyle, color: "#FF3B30", transform: `translate(${jitter + 4}px, ${jitterY}px)`, mixBlendMode: "screen", opacity: active ? 0.9 : 0 }, children: content }),
1770
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("div", { style: { ...baseStyle, color: "#0A84FF", transform: `translate(${jitter - 4}px, ${jitterY}px)`, mixBlendMode: "screen", opacity: active ? 0.9 : 0 }, children: content })
1771
+ ] }) });
1772
+ };
1773
+ var GlitchTextComponentMetadata = {
1774
+ kind: "composite",
1775
+ category: "text",
1776
+ description: "Cyberpunk-style glitch text with RGB split jitter",
1777
+ llmGuidance: "Use for tech/error moments. intensity 1-3 subtle, 7-10 extreme. glitchDuration is frames at start."
1778
+ };
1779
+
1780
+ // src/components/composites/GridLayout.tsx
1781
+ var import_zod23 = require("zod");
1782
+ var import_jsx_runtime22 = require("react/jsx-runtime");
1783
+ var GridLayoutPropsSchema = import_zod23.z.object({
1784
+ columns: import_zod23.z.number().int().min(1).max(4).default(2),
1785
+ rows: import_zod23.z.number().int().min(1).max(4).default(2),
1786
+ gap: import_zod23.z.number().min(0).max(50).default(20),
1787
+ padding: import_zod23.z.number().min(0).max(100).default(40)
1788
+ });
1789
+ var GridLayout = ({ columns, rows, gap, padding, children }) => {
1790
+ return /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(Fill, { style: { padding, boxSizing: "border-box" }, children: /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
1791
+ "div",
1792
+ {
1793
+ style: {
1794
+ display: "grid",
1795
+ gridTemplateColumns: `repeat(${columns}, 1fr)`,
1796
+ gridTemplateRows: `repeat(${rows}, 1fr)`,
1797
+ gap,
1798
+ width: "100%",
1799
+ height: "100%"
1800
+ },
1801
+ children
1802
+ }
1803
+ ) });
1804
+ };
1805
+ var GridLayoutComponentMetadata = {
1806
+ kind: "composite",
1807
+ category: "layout",
1808
+ acceptsChildren: true,
1809
+ minChildren: 1,
1810
+ description: "Simple responsive grid layout for child components",
1811
+ llmGuidance: "Use for dashboards and collages. 2x2 is a good default for 4 items."
1812
+ };
1813
+
1814
+ // src/components/composites/ImageCollage.tsx
1815
+ var import_remotion14 = require("remotion");
1816
+ var import_zod24 = require("zod");
1817
+ var import_jsx_runtime23 = require("react/jsx-runtime");
1818
+ var ImageCollagePropsSchema = import_zod24.z.object({
1819
+ images: import_zod24.z.array(import_zod24.z.object({ src: import_zod24.z.string().min(1), caption: import_zod24.z.string().optional() })).min(2).max(9),
1820
+ layout: import_zod24.z.enum(["grid", "stack", "scatter"]).default("grid"),
1821
+ stagger: import_zod24.z.number().int().min(2).max(10).default(5)
1822
+ });
1823
+ var resolveAsset4 = (value) => isRemoteAssetPath(value) ? value : (0, import_remotion14.staticFile)(staticFileInputFromAssetPath(value));
1824
+ var ImageCollage = ({ images, layout, stagger }) => {
1825
+ const frame = (0, import_remotion14.useCurrentFrame)();
1826
+ const { fps } = (0, import_remotion14.useVideoConfig)();
1827
+ const n = images.length;
1828
+ const cols = Math.ceil(Math.sqrt(n));
1829
+ const rows = Math.ceil(n / cols);
1830
+ if (layout === "grid") {
1831
+ return /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(Fill, { style: { padding: 80, boxSizing: "border-box" }, children: /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
1832
+ "div",
1833
+ {
1834
+ style: {
1835
+ display: "grid",
1836
+ gridTemplateColumns: `repeat(${cols}, 1fr)`,
1837
+ gridTemplateRows: `repeat(${rows}, 1fr)`,
1838
+ gap: 24,
1839
+ width: "100%",
1840
+ height: "100%"
1841
+ },
1842
+ children: images.map((img, i) => {
1843
+ const p = (0, import_remotion14.spring)({ frame: frame - i * stagger, fps, config: { damping: 14, stiffness: 120, mass: 0.9 } });
1844
+ return /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)("div", { style: { position: "relative", overflow: "hidden", borderRadius: 18, opacity: p, transform: `scale(${0.92 + 0.08 * p})` }, children: [
1845
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(import_remotion14.Img, { src: resolveAsset4(img.src), style: { width: "100%", height: "100%", objectFit: "cover" } }),
1846
+ img.caption ? /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("div", { style: { position: "absolute", left: 0, right: 0, bottom: 0, padding: 12, background: "linear-gradient(transparent, rgba(0,0,0,0.7))", color: "#fff", fontWeight: 700 }, children: img.caption }) : null
1847
+ ] }, i);
1848
+ })
1849
+ }
1850
+ ) });
1851
+ }
1852
+ return /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(Fill, { children: images.map((img, i) => {
1853
+ const p = (0, import_remotion14.spring)({ frame: frame - i * stagger, fps, config: { damping: 14, stiffness: 120, mass: 0.9 } });
1854
+ const baseRotate = layout === "stack" ? (i - (n - 1) / 2) * 4 : i * 17 % 20 - 10;
1855
+ const baseX = layout === "scatter" ? i * 137 % 300 - 150 : 0;
1856
+ const baseY = layout === "scatter" ? (i + 3) * 89 % 200 - 100 : 0;
1857
+ return /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
1858
+ "div",
1859
+ {
1860
+ style: {
1861
+ position: "absolute",
1862
+ left: "50%",
1863
+ top: "50%",
1864
+ width: 520,
1865
+ height: 360,
1866
+ transform: `translate(-50%, -50%) translate(${baseX}px, ${baseY}px) rotate(${baseRotate}deg) scale(${0.85 + 0.15 * p})`,
1867
+ opacity: p,
1868
+ borderRadius: 18,
1869
+ overflow: "hidden",
1870
+ boxShadow: "0 20px 60px rgba(0,0,0,0.35)"
1871
+ },
1872
+ children: /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(import_remotion14.Img, { src: resolveAsset4(img.src), style: { width: "100%", height: "100%", objectFit: "cover" } })
1873
+ },
1874
+ i
1875
+ );
1876
+ }) });
1877
+ };
1878
+ var ImageCollageComponentMetadata = {
1879
+ kind: "composite",
1880
+ category: "image",
1881
+ description: "Collage of multiple images in a grid/stack/scatter layout with staggered entrances",
1882
+ llmGuidance: 'Use 2-6 images for best results. layout="grid" is clean; "scatter" is energetic.'
1883
+ };
1884
+
1885
+ // src/components/composites/ImageReveal.tsx
1886
+ var import_remotion15 = require("remotion");
1887
+ var import_zod25 = require("zod");
1888
+ var import_jsx_runtime24 = require("react/jsx-runtime");
1889
+ var ImageRevealPropsSchema = import_zod25.z.object({
1890
+ src: import_zod25.z.string().min(1),
1891
+ direction: import_zod25.z.enum(["left", "right", "top", "bottom", "center"]).default("left"),
1892
+ revealType: import_zod25.z.enum(["wipe", "expand", "iris"]).default("wipe")
1893
+ });
1894
+ var resolveAsset5 = (value) => isRemoteAssetPath(value) ? value : (0, import_remotion15.staticFile)(staticFileInputFromAssetPath(value));
1895
+ var ImageReveal = ({ src, direction, revealType, __wavesDurationInFrames }) => {
1896
+ const frame = (0, import_remotion15.useCurrentFrame)();
1897
+ const total = Math.max(1, __wavesDurationInFrames ?? 1);
1898
+ const d = Math.min(30, total);
1899
+ const p = (0, import_remotion15.interpolate)(frame, [0, d], [0, 1], { extrapolateLeft: "clamp", extrapolateRight: "clamp" });
1900
+ let clipPath;
1901
+ let transform = "none";
1902
+ let transformOrigin = "center center";
1903
+ if (revealType === "wipe") {
1904
+ if (direction === "left") clipPath = `inset(0 ${100 * (1 - p)}% 0 0)`;
1905
+ if (direction === "right") clipPath = `inset(0 0 0 ${100 * (1 - p)}%)`;
1906
+ if (direction === "top") clipPath = `inset(0 0 ${100 * (1 - p)}% 0)`;
1907
+ if (direction === "bottom") clipPath = `inset(${100 * (1 - p)}% 0 0 0)`;
1908
+ if (direction === "center") clipPath = `inset(${50 * (1 - p)}% ${50 * (1 - p)}% ${50 * (1 - p)}% ${50 * (1 - p)}%)`;
1909
+ }
1910
+ if (revealType === "expand") {
1911
+ const s = 0.85 + 0.15 * p;
1912
+ transform = `scale(${s})`;
1913
+ if (direction === "left") transformOrigin = "left center";
1914
+ if (direction === "right") transformOrigin = "right center";
1915
+ if (direction === "top") transformOrigin = "center top";
1916
+ if (direction === "bottom") transformOrigin = "center bottom";
1917
+ }
1918
+ if (revealType === "iris") {
1919
+ clipPath = `circle(${Math.round(150 * p)}% at 50% 50%)`;
1920
+ }
1921
+ const opacity = revealType === "expand" ? p : 1;
1922
+ return /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(Fill, { children: /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
1923
+ import_remotion15.Img,
1924
+ {
1925
+ src: resolveAsset5(src),
1926
+ style: {
1927
+ width: "100%",
1928
+ height: "100%",
1929
+ objectFit: "cover",
1930
+ clipPath,
1931
+ transform,
1932
+ transformOrigin,
1933
+ opacity
1934
+ }
1935
+ }
1936
+ ) });
1937
+ };
1938
+ var ImageRevealComponentMetadata = {
1939
+ kind: "composite",
1940
+ category: "image",
1941
+ description: "Reveals an image with wipe/expand/iris entrance effects",
1942
+ llmGuidance: "Use wipe for directional reveals, expand for subtle pop-in, iris for circular mask openings."
1943
+ };
1944
+
1945
+ // src/components/composites/ImageSequence.tsx
1946
+ var import_remotion16 = require("remotion");
1947
+ var import_zod26 = require("zod");
1948
+ var import_jsx_runtime25 = require("react/jsx-runtime");
1949
+ var ImageSequencePropsSchema = import_zod26.z.object({
1950
+ basePath: import_zod26.z.string().min(1),
1951
+ frameCount: import_zod26.z.number().int().positive(),
1952
+ filePattern: import_zod26.z.string().default("frame_{frame}.png"),
1953
+ fps: import_zod26.z.number().int().min(1).max(120).default(30)
1954
+ });
1955
+ function joinPath(base, file) {
1956
+ if (base.endsWith("/")) return `${base}${file}`;
1957
+ return `${base}/${file}`;
1958
+ }
1959
+ var resolveAsset6 = (value) => isRemoteAssetPath(value) ? value : (0, import_remotion16.staticFile)(staticFileInputFromAssetPath(value));
1960
+ var ImageSequence = ({ basePath, frameCount, filePattern, fps }) => {
1961
+ const frame = (0, import_remotion16.useCurrentFrame)();
1962
+ const { fps: compFps } = (0, import_remotion16.useVideoConfig)();
1963
+ const index = Math.min(frameCount - 1, Math.max(0, Math.floor(frame * fps / compFps)));
1964
+ const file = filePattern.replace("{frame}", String(index));
1965
+ const src = joinPath(basePath, file);
1966
+ return /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(Fill, { children: /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(import_remotion16.Img, { src: resolveAsset6(src), style: { width: "100%", height: "100%", objectFit: "cover" } }) });
1967
+ };
1968
+ var ImageSequenceComponentMetadata = {
1969
+ kind: "composite",
1970
+ category: "image",
1971
+ description: "Plays a numbered image sequence (frame-by-frame)",
1972
+ llmGuidance: "Use for exported sprite sequences. basePath can be /assets/seq and filePattern like img_{frame}.png."
1973
+ };
1974
+
1975
+ // src/components/composites/ImageWithCaption.tsx
1976
+ var import_remotion17 = require("remotion");
1977
+ var import_zod27 = require("zod");
1978
+ var import_jsx_runtime26 = require("react/jsx-runtime");
1979
+ var CaptionStyleSchema = import_zod27.z.object({
1980
+ fontSize: import_zod27.z.number().min(12).max(80).default(32),
1981
+ color: import_zod27.z.string().default("#FFFFFF"),
1982
+ backgroundColor: import_zod27.z.string().default("rgba(0,0,0,0.7)")
1983
+ });
1984
+ var ImageWithCaptionPropsSchema = import_zod27.z.object({
1985
+ src: import_zod27.z.string().min(1),
1986
+ caption: import_zod27.z.string().max(200),
1987
+ captionPosition: import_zod27.z.enum(["top", "bottom", "overlay"]).default("bottom"),
1988
+ captionStyle: CaptionStyleSchema.optional()
1989
+ });
1990
+ var resolveAsset7 = (value) => isRemoteAssetPath(value) ? value : (0, import_remotion17.staticFile)(staticFileInputFromAssetPath(value));
1991
+ var ImageWithCaption = ({ src, caption, captionPosition, captionStyle }) => {
1992
+ const frame = (0, import_remotion17.useCurrentFrame)();
1993
+ const p = (0, import_remotion17.interpolate)(frame, [8, 24], [0, 1], { extrapolateLeft: "clamp", extrapolateRight: "clamp" });
1994
+ const style = captionStyle ?? { fontSize: 32, color: "#FFFFFF", backgroundColor: "rgba(0,0,0,0.7)" };
1995
+ const captionBox = /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(
1996
+ "div",
1997
+ {
1998
+ style: {
1999
+ width: "100%",
2000
+ padding: 22,
2001
+ boxSizing: "border-box",
2002
+ backgroundColor: style.backgroundColor,
2003
+ color: style.color,
2004
+ fontWeight: 800,
2005
+ fontSize: style.fontSize,
2006
+ opacity: p
2007
+ },
2008
+ children: caption
2009
+ }
2010
+ );
2011
+ if (captionPosition === "top" || captionPosition === "bottom") {
2012
+ return /* @__PURE__ */ (0, import_jsx_runtime26.jsxs)(Fill, { style: { display: "flex", flexDirection: "column" }, children: [
2013
+ captionPosition === "top" ? captionBox : null,
2014
+ /* @__PURE__ */ (0, import_jsx_runtime26.jsx)("div", { style: { flex: 1, position: "relative" }, children: /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(import_remotion17.Img, { src: resolveAsset7(src), style: { width: "100%", height: "100%", objectFit: "cover" } }) }),
2015
+ captionPosition === "bottom" ? captionBox : null
2016
+ ] });
2017
+ }
2018
+ return /* @__PURE__ */ (0, import_jsx_runtime26.jsxs)(Fill, { children: [
2019
+ /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(import_remotion17.Img, { src: resolveAsset7(src), style: { width: "100%", height: "100%", objectFit: "cover" } }),
2020
+ /* @__PURE__ */ (0, import_jsx_runtime26.jsx)("div", { style: { position: "absolute", left: 0, right: 0, bottom: 0 }, children: captionBox })
2021
+ ] });
2022
+ };
2023
+ var ImageWithCaptionComponentMetadata = {
2024
+ kind: "composite",
2025
+ category: "image",
2026
+ description: "Image with a caption strip (top/bottom) or overlay caption",
2027
+ llmGuidance: "Use overlay for quotes/testimonials over photos. Use bottom for standard captions."
2028
+ };
2029
+
2030
+ // src/components/composites/InstagramStory.tsx
2031
+ var import_remotion18 = require("remotion");
2032
+ var import_zod28 = require("zod");
2033
+ var import_jsx_runtime27 = require("react/jsx-runtime");
2034
+ var InstagramStoryPropsSchema = import_zod28.z.object({
2035
+ backgroundImage: import_zod28.z.string().optional(),
2036
+ backgroundColor: import_zod28.z.string().default("#000000"),
2037
+ profilePic: import_zod28.z.string().optional(),
2038
+ username: import_zod28.z.string().optional(),
2039
+ text: import_zod28.z.string().max(100).optional(),
2040
+ sticker: import_zod28.z.enum(["none", "poll", "question", "countdown"]).default("none"),
2041
+ musicTrack: import_zod28.z.string().optional()
2042
+ });
2043
+ var resolveAsset8 = (value) => isRemoteAssetPath(value) ? value : (0, import_remotion18.staticFile)(staticFileInputFromAssetPath(value));
2044
+ var InstagramStory = ({
2045
+ backgroundImage,
2046
+ backgroundColor,
2047
+ profilePic,
2048
+ username,
2049
+ text,
2050
+ sticker,
2051
+ musicTrack
2052
+ }) => {
2053
+ const frame = (0, import_remotion18.useCurrentFrame)();
2054
+ const fade = (0, import_remotion18.interpolate)(frame, [0, 20], [0, 1], { extrapolateLeft: "clamp", extrapolateRight: "clamp" });
2055
+ return /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)(Fill, { style: { backgroundColor }, children: [
2056
+ musicTrack ? /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(import_remotion18.Audio, { src: resolveAsset8(musicTrack), volume: 0.6 }) : null,
2057
+ backgroundImage ? /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(import_remotion18.Img, { src: resolveAsset8(backgroundImage), style: { width: "100%", height: "100%", objectFit: "cover", opacity: fade } }) : null,
2058
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)(Fill, { style: { padding: 60, boxSizing: "border-box" }, children: [
2059
+ profilePic || username ? /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)("div", { style: { display: "flex", alignItems: "center", gap: 18 }, children: [
2060
+ profilePic ? /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(import_remotion18.Img, { src: resolveAsset8(profilePic), style: { width: 78, height: 78, borderRadius: 9999, objectFit: "cover" } }) : /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("div", { style: { width: 78, height: 78, borderRadius: 9999, backgroundColor: "rgba(255,255,255,0.2)" } }),
2061
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("div", { style: { color: "#FFFFFF", fontWeight: 800, fontSize: 34 }, children: username ?? "username" })
2062
+ ] }) : null,
2063
+ text ? /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(
2064
+ "div",
2065
+ {
2066
+ style: {
2067
+ marginTop: 120,
2068
+ color: "#FFFFFF",
2069
+ fontSize: 54,
2070
+ fontWeight: 900,
2071
+ textShadow: "0 8px 30px rgba(0,0,0,0.6)",
2072
+ maxWidth: 900
2073
+ },
2074
+ children: text
2075
+ }
2076
+ ) : null,
2077
+ sticker !== "none" ? /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(
2078
+ "div",
2079
+ {
2080
+ style: {
2081
+ position: "absolute",
2082
+ left: 60,
2083
+ bottom: 180,
2084
+ width: 520,
2085
+ padding: 28,
2086
+ borderRadius: 22,
2087
+ backgroundColor: "rgba(255,255,255,0.9)",
2088
+ color: "#111",
2089
+ fontWeight: 900,
2090
+ fontSize: 30
2091
+ },
2092
+ children: sticker === "poll" ? "POLL" : sticker === "question" ? "QUESTION" : "COUNTDOWN"
2093
+ }
2094
+ ) : null
2095
+ ] })
2096
+ ] });
2097
+ };
2098
+ var InstagramStoryComponentMetadata = {
2099
+ kind: "composite",
2100
+ category: "social",
2101
+ description: "Instagram story-style layout with profile header, text overlay, and optional sticker",
2102
+ llmGuidance: "Best with 1080x1920 (9:16). Use backgroundImage + short text + optional sticker for mobile-style content."
2103
+ };
2104
+
2105
+ // src/components/composites/IntroScene.tsx
2106
+ var import_remotion19 = require("remotion");
2107
+ var import_zod29 = require("zod");
2108
+ var import_jsx_runtime28 = require("react/jsx-runtime");
2109
+ var IntroScenePropsSchema = import_zod29.z.object({
2110
+ logoSrc: import_zod29.z.string().min(1),
2111
+ companyName: import_zod29.z.string().min(1),
2112
+ tagline: import_zod29.z.string().optional(),
2113
+ backgroundColor: import_zod29.z.string().default("#000000"),
2114
+ primaryColor: import_zod29.z.string().default("#FFFFFF"),
2115
+ musicTrack: import_zod29.z.string().optional()
2116
+ });
2117
+ var resolveAsset9 = (value) => isRemoteAssetPath(value) ? value : (0, import_remotion19.staticFile)(staticFileInputFromAssetPath(value));
2118
+ var IntroScene = ({
2119
+ logoSrc,
2120
+ companyName,
2121
+ tagline,
2122
+ backgroundColor,
2123
+ primaryColor,
2124
+ musicTrack,
2125
+ __wavesDurationInFrames
2126
+ }) => {
2127
+ const frame = (0, import_remotion19.useCurrentFrame)();
2128
+ const { fps } = (0, import_remotion19.useVideoConfig)();
2129
+ const total = Math.max(1, __wavesDurationInFrames ?? 1);
2130
+ const logoP = (0, import_remotion19.spring)({ frame, fps, config: { damping: 14, stiffness: 120, mass: 0.9 } });
2131
+ const nameOpacity = (0, import_remotion19.interpolate)(frame, [20, 60], [0, 1], { extrapolateLeft: "clamp", extrapolateRight: "clamp" });
2132
+ const taglineY = (0, import_remotion19.interpolate)(frame, [50, 80], [20, 0], { extrapolateLeft: "clamp", extrapolateRight: "clamp" });
2133
+ const outroFade = (0, import_remotion19.interpolate)(frame, [Math.max(0, total - 20), total], [1, 0], { extrapolateLeft: "clamp", extrapolateRight: "clamp" });
2134
+ return /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)(Fill, { style: { backgroundColor, display: "flex", justifyContent: "center", alignItems: "center" }, children: [
2135
+ musicTrack ? /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(import_remotion19.Audio, { src: resolveAsset9(musicTrack), volume: 0.7 }) : null,
2136
+ /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)("div", { style: { textAlign: "center", opacity: outroFade }, children: [
2137
+ /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(
2138
+ import_remotion19.Img,
2139
+ {
2140
+ src: resolveAsset9(logoSrc),
2141
+ style: { width: 280, height: 280, objectFit: "contain", transform: `scale(${logoP})` }
2142
+ }
2143
+ ),
2144
+ /* @__PURE__ */ (0, import_jsx_runtime28.jsx)("div", { style: { marginTop: 24, color: primaryColor, fontSize: 64, fontWeight: 900, opacity: nameOpacity }, children: companyName }),
2145
+ tagline ? /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(
2146
+ "div",
2147
+ {
2148
+ style: {
2149
+ marginTop: 12,
2150
+ color: primaryColor,
2151
+ fontSize: 32,
2152
+ fontWeight: 700,
2153
+ opacity: nameOpacity,
2154
+ transform: `translateY(${taglineY}px)`
2155
+ },
2156
+ children: tagline
2157
+ }
2158
+ ) : null
2159
+ ] })
2160
+ ] });
2161
+ };
2162
+ var IntroSceneComponentMetadata = {
2163
+ kind: "composite",
2164
+ category: "branding",
2165
+ description: "Branded intro scene (logo + company name + optional tagline)",
2166
+ llmGuidance: "Use as the first segment. Works best at 3-5 seconds. musicTrack can add ambience."
2167
+ };
2168
+
2169
+ // src/components/composites/KenBurnsImage.tsx
2170
+ var import_remotion20 = require("remotion");
2171
+ var import_zod30 = require("zod");
2172
+ var import_jsx_runtime29 = require("react/jsx-runtime");
2173
+ var KenBurnsImagePropsSchema = import_zod30.z.object({
2174
+ src: import_zod30.z.string().min(1),
2175
+ startScale: import_zod30.z.number().min(1).max(2).default(1),
2176
+ endScale: import_zod30.z.number().min(1).max(2).default(1.2),
2177
+ panDirection: import_zod30.z.enum(["none", "left", "right", "up", "down"]).default("none"),
2178
+ panAmount: import_zod30.z.number().min(0).max(100).default(50)
2179
+ });
2180
+ var resolveAsset10 = (value) => isRemoteAssetPath(value) ? value : (0, import_remotion20.staticFile)(staticFileInputFromAssetPath(value));
2181
+ var KenBurnsImage = ({
2182
+ src,
2183
+ startScale,
2184
+ endScale,
2185
+ panDirection,
2186
+ panAmount,
2187
+ __wavesDurationInFrames
2188
+ }) => {
2189
+ const frame = (0, import_remotion20.useCurrentFrame)();
2190
+ const duration = Math.max(1, __wavesDurationInFrames ?? 1);
2191
+ const t = (0, import_remotion20.interpolate)(frame, [0, duration], [0, 1], { extrapolateLeft: "clamp", extrapolateRight: "clamp" });
2192
+ const scale = startScale + (endScale - startScale) * t;
2193
+ const dx = panDirection === "left" ? -panAmount * t : panDirection === "right" ? panAmount * t : 0;
2194
+ const dy = panDirection === "up" ? -panAmount * t : panDirection === "down" ? panAmount * t : 0;
2195
+ return /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(Fill, { children: /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(
2196
+ import_remotion20.Img,
2197
+ {
2198
+ src: resolveAsset10(src),
2199
+ style: {
2200
+ width: "110%",
2201
+ height: "110%",
2202
+ objectFit: "cover",
2203
+ transform: `translate(${dx}px, ${dy}px) scale(${scale})`,
2204
+ transformOrigin: "center center"
2205
+ }
2206
+ }
2207
+ ) });
2208
+ };
2209
+ var KenBurnsImageComponentMetadata = {
2210
+ kind: "composite",
2211
+ category: "image",
2212
+ description: "Slow zoom and pan (Ken Burns effect) for a still image",
2213
+ llmGuidance: "Classic documentary-style motion. startScale 1 -> endScale 1.2 is subtle; add panDirection for extra movement."
2214
+ };
2215
+
2216
+ // src/components/composites/KineticTypography.tsx
2217
+ var import_remotion21 = require("remotion");
2218
+ var import_zod31 = require("zod");
2219
+ var import_jsx_runtime30 = require("react/jsx-runtime");
2220
+ var WordSchema = import_zod31.z.object({
2221
+ text: import_zod31.z.string().min(1),
2222
+ emphasis: import_zod31.z.enum(["normal", "bold", "giant"]).default("normal")
2223
+ });
2224
+ var KineticTypographyPropsSchema = import_zod31.z.object({
2225
+ words: import_zod31.z.array(WordSchema).min(1).max(50),
2226
+ fontSize: import_zod31.z.number().min(12).max(140).default(48),
2227
+ color: import_zod31.z.string().default("#FFFFFF"),
2228
+ fontFamily: import_zod31.z.string().default("Inter"),
2229
+ timing: import_zod31.z.array(import_zod31.z.number().int().min(0)).min(1).describe("Frame timing for each word"),
2230
+ transition: import_zod31.z.enum(["fade", "scale", "slideLeft", "slideRight"]).default("scale")
2231
+ });
2232
+ var KineticTypography = ({
2233
+ words,
2234
+ fontSize,
2235
+ color,
2236
+ fontFamily,
2237
+ timing,
2238
+ transition
2239
+ }) => {
2240
+ const frame = (0, import_remotion21.useCurrentFrame)();
2241
+ const { fps } = (0, import_remotion21.useVideoConfig)();
2242
+ const starts = (() => {
2243
+ if (timing.length >= words.length) return timing.slice(0, words.length);
2244
+ const last = timing.length ? timing[timing.length - 1] : 0;
2245
+ const extra = Array.from({ length: words.length - timing.length }, () => last + 15);
2246
+ return [...timing, ...extra];
2247
+ })();
2248
+ let activeIndex = 0;
2249
+ for (let i = 0; i < words.length; i++) {
2250
+ if (frame >= (starts[i] ?? 0)) activeIndex = i;
2251
+ }
2252
+ const word = words[activeIndex];
2253
+ const start = starts[activeIndex] ?? 0;
2254
+ const p = (0, import_remotion21.spring)({ frame: frame - start, fps, config: { damping: 14, stiffness: 120, mass: 0.9 } });
2255
+ const progress = Math.max(0, Math.min(1, p));
2256
+ const scaleBase = word.emphasis === "giant" ? 2 : word.emphasis === "bold" ? 1.3 : 1;
2257
+ const opacity = transition === "fade" ? progress : 1;
2258
+ const scale = transition === "scale" ? (0.85 + 0.15 * progress) * scaleBase : 1 * scaleBase;
2259
+ const tx = transition === "slideLeft" ? 40 * (1 - progress) : transition === "slideRight" ? -40 * (1 - progress) : 0;
2260
+ return /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(Fill, { style: { display: "flex", justifyContent: "center", alignItems: "center" }, children: /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(
2261
+ "div",
2262
+ {
2263
+ style: {
2264
+ fontSize,
2265
+ color,
2266
+ fontFamily,
2267
+ fontWeight: word.emphasis === "normal" ? 800 : 900,
2268
+ textTransform: "uppercase",
2269
+ letterSpacing: 1,
2270
+ opacity,
2271
+ transform: `translateX(${tx}px) scale(${scale})`
2272
+ },
2273
+ children: word.text
2274
+ }
2275
+ ) });
2276
+ };
2277
+ var KineticTypographyComponentMetadata = {
2278
+ kind: "composite",
2279
+ category: "text",
2280
+ description: "Rhythmic single-word kinetic typography driven by a timing array",
2281
+ llmGuidance: 'Provide timing frames for when each word appears. Use emphasis="giant" sparingly for impact.'
2282
+ };
2283
+
2284
+ // src/components/composites/LineGraph.tsx
2285
+ var import_remotion22 = require("remotion");
2286
+ var import_zod32 = require("zod");
2287
+ var import_jsx_runtime31 = require("react/jsx-runtime");
2288
+ var PointSchema = import_zod32.z.object({ x: import_zod32.z.number(), y: import_zod32.z.number() });
2289
+ var LineGraphPropsSchema = import_zod32.z.object({
2290
+ data: import_zod32.z.array(PointSchema).min(2).max(50),
2291
+ color: import_zod32.z.string().default("#00FF00"),
2292
+ strokeWidth: import_zod32.z.number().min(1).max(10).default(3),
2293
+ showDots: import_zod32.z.boolean().default(true),
2294
+ fillArea: import_zod32.z.boolean().default(false),
2295
+ animate: import_zod32.z.enum(["draw", "reveal"]).default("draw")
2296
+ });
2297
+ function normalize(data, w, h, pad) {
2298
+ const xs = data.map((d) => d.x);
2299
+ const ys = data.map((d) => d.y);
2300
+ const minX = Math.min(...xs);
2301
+ const maxX = Math.max(...xs);
2302
+ const minY = Math.min(...ys);
2303
+ const maxY = Math.max(...ys);
2304
+ return data.map((d) => ({
2305
+ x: pad + (maxX === minX ? (w - 2 * pad) / 2 : (d.x - minX) / (maxX - minX) * (w - 2 * pad)),
2306
+ y: pad + (maxY === minY ? (h - 2 * pad) / 2 : (1 - (d.y - minY) / (maxY - minY)) * (h - 2 * pad))
2307
+ }));
2308
+ }
2309
+ function toPath(points) {
2310
+ return points.reduce((acc, p, i) => i === 0 ? `M ${p.x} ${p.y}` : `${acc} L ${p.x} ${p.y}`, "");
2311
+ }
2312
+ var LineGraph = ({
2313
+ data,
2314
+ color,
2315
+ strokeWidth,
2316
+ showDots,
2317
+ fillArea,
2318
+ animate,
2319
+ __wavesDurationInFrames
2320
+ }) => {
2321
+ const frame = (0, import_remotion22.useCurrentFrame)();
2322
+ const total = Math.max(1, __wavesDurationInFrames ?? 1);
2323
+ const progress = (0, import_remotion22.interpolate)(frame, [0, total], [0, 1], { extrapolateLeft: "clamp", extrapolateRight: "clamp" });
2324
+ const w = 1e3;
2325
+ const h = 520;
2326
+ const pad = 30;
2327
+ const pts = normalize(data, w, h, pad);
2328
+ const d = toPath(pts);
2329
+ const dash = 3e3;
2330
+ const dashOffset = dash * (1 - progress);
2331
+ return /* @__PURE__ */ (0, import_jsx_runtime31.jsx)(Fill, { style: { display: "flex", justifyContent: "center", alignItems: "center" }, children: /* @__PURE__ */ (0, import_jsx_runtime31.jsxs)("svg", { viewBox: `0 0 ${w} ${h}`, style: { width: "100%", height: "100%" }, children: [
2332
+ /* @__PURE__ */ (0, import_jsx_runtime31.jsx)("defs", { children: /* @__PURE__ */ (0, import_jsx_runtime31.jsx)("clipPath", { id: "waves-line-reveal", children: /* @__PURE__ */ (0, import_jsx_runtime31.jsx)("rect", { x: "0", y: "0", width: w * progress, height: h }) }) }),
2333
+ fillArea ? /* @__PURE__ */ (0, import_jsx_runtime31.jsx)(
2334
+ "path",
2335
+ {
2336
+ d: `${d} L ${pts[pts.length - 1].x} ${h - pad} L ${pts[0].x} ${h - pad} Z`,
2337
+ fill: color,
2338
+ opacity: 0.12,
2339
+ clipPath: animate === "reveal" ? "url(#waves-line-reveal)" : void 0
2340
+ }
2341
+ ) : null,
2342
+ /* @__PURE__ */ (0, import_jsx_runtime31.jsx)(
2343
+ "path",
2344
+ {
2345
+ d,
2346
+ fill: "none",
2347
+ stroke: color,
2348
+ strokeWidth,
2349
+ strokeLinecap: "round",
2350
+ strokeLinejoin: "round",
2351
+ strokeDasharray: animate === "draw" ? dash : void 0,
2352
+ strokeDashoffset: animate === "draw" ? dashOffset : void 0,
2353
+ clipPath: animate === "reveal" ? "url(#waves-line-reveal)" : void 0
2354
+ }
2355
+ ),
2356
+ showDots ? /* @__PURE__ */ (0, import_jsx_runtime31.jsx)("g", { clipPath: animate === "reveal" ? "url(#waves-line-reveal)" : void 0, children: pts.map((p, i) => /* @__PURE__ */ (0, import_jsx_runtime31.jsx)("circle", { cx: p.x, cy: p.y, r: 6, fill: color }, i)) }) : null
2357
+ ] }) });
2358
+ };
2359
+ var LineGraphComponentMetadata = {
2360
+ kind: "composite",
2361
+ category: "data",
2362
+ description: "Animated line graph (SVG) with draw/reveal modes",
2363
+ llmGuidance: 'Use 5-20 points. animate="draw" traces the line; animate="reveal" wipes it left-to-right.'
2364
+ };
2365
+
2366
+ // src/components/composites/LogoReveal.tsx
2367
+ var import_remotion23 = require("remotion");
2368
+ var import_zod33 = require("zod");
2369
+ var import_jsx_runtime32 = require("react/jsx-runtime");
2370
+ var LogoRevealPropsSchema = import_zod33.z.object({
2371
+ logoSrc: import_zod33.z.string().min(1),
2372
+ effect: import_zod33.z.enum(["fade", "scale", "rotate", "slide"]).default("scale"),
2373
+ backgroundColor: import_zod33.z.string().default("#000000"),
2374
+ soundEffect: import_zod33.z.string().optional()
2375
+ });
2376
+ var resolveAsset11 = (value) => isRemoteAssetPath(value) ? value : (0, import_remotion23.staticFile)(staticFileInputFromAssetPath(value));
2377
+ var LogoReveal = ({ logoSrc, effect, backgroundColor, soundEffect }) => {
2378
+ const frame = (0, import_remotion23.useCurrentFrame)();
2379
+ const { fps } = (0, import_remotion23.useVideoConfig)();
2380
+ const p = (0, import_remotion23.spring)({ frame, fps, config: { damping: 14, stiffness: 110, mass: 0.9 } });
2381
+ const opacity = effect === "fade" ? p : Math.min(1, Math.max(0, p));
2382
+ const scale = effect === "scale" ? p : 1;
2383
+ const rotate = effect === "rotate" ? (1 - p) * 360 : 0;
2384
+ const translateY = effect === "slide" ? -200 * (1 - p) : 0;
2385
+ return /* @__PURE__ */ (0, import_jsx_runtime32.jsxs)(Fill, { style: { backgroundColor, display: "flex", justifyContent: "center", alignItems: "center" }, children: [
2386
+ soundEffect ? /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(import_remotion23.Audio, { src: resolveAsset11(soundEffect), volume: 1 }) : null,
2387
+ /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(
2388
+ import_remotion23.Img,
2389
+ {
2390
+ src: resolveAsset11(logoSrc),
2391
+ style: {
2392
+ width: 320,
2393
+ height: 320,
2394
+ objectFit: "contain",
2395
+ opacity,
2396
+ transform: `translateY(${translateY}px) scale(${scale}) rotate(${rotate}deg)`
2397
+ }
2398
+ }
2399
+ )
2400
+ ] });
2401
+ };
2402
+ var LogoRevealComponentMetadata = {
2403
+ kind: "composite",
2404
+ category: "branding",
2405
+ description: "Logo intro animation (fade/scale/rotate/slide), optionally with a sound effect",
2406
+ llmGuidance: "Use for intros/outros. Keep the logo high-contrast and centered. soundEffect can be a short sting."
2407
+ };
2408
+
2409
+ // src/components/composites/OutroScene.tsx
2410
+ var import_remotion24 = require("remotion");
2411
+ var import_zod34 = require("zod");
2412
+ var import_jsx_runtime33 = require("react/jsx-runtime");
2413
+ var CtaSchema = import_zod34.z.object({ text: import_zod34.z.string().min(1), icon: import_zod34.z.string().optional() });
2414
+ var HandleSchema = import_zod34.z.object({
2415
+ platform: import_zod34.z.enum(["twitter", "instagram", "youtube", "linkedin"]),
2416
+ handle: import_zod34.z.string().min(1)
2417
+ });
2418
+ var OutroScenePropsSchema = import_zod34.z.object({
2419
+ logoSrc: import_zod34.z.string().min(1),
2420
+ message: import_zod34.z.string().default("Thank You"),
2421
+ ctaButtons: import_zod34.z.array(CtaSchema).max(3).optional(),
2422
+ socialHandles: import_zod34.z.array(HandleSchema).max(4).optional(),
2423
+ backgroundColor: import_zod34.z.string().default("#000000")
2424
+ });
2425
+ var resolveAsset12 = (value) => isRemoteAssetPath(value) ? value : (0, import_remotion24.staticFile)(staticFileInputFromAssetPath(value));
2426
+ var OutroScene = ({ logoSrc, message, ctaButtons, socialHandles, backgroundColor }) => {
2427
+ const frame = (0, import_remotion24.useCurrentFrame)();
2428
+ const { fps } = (0, import_remotion24.useVideoConfig)();
2429
+ const logoP = (0, import_remotion24.spring)({ frame, fps, config: { damping: 14, stiffness: 120, mass: 0.9 } });
2430
+ const msgP = (0, import_remotion24.spring)({ frame: frame - 18, fps, config: { damping: 14, stiffness: 120, mass: 0.9 } });
2431
+ const ctaP = (0, import_remotion24.spring)({ frame: frame - 40, fps, config: { damping: 14, stiffness: 120, mass: 0.9 } });
2432
+ return /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(Fill, { style: { backgroundColor, display: "flex", justifyContent: "center", alignItems: "center", padding: 80, boxSizing: "border-box" }, children: /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)("div", { style: { textAlign: "center" }, children: [
2433
+ /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(import_remotion24.Img, { src: resolveAsset12(logoSrc), style: { width: 220, height: 220, objectFit: "contain", transform: `scale(${logoP})` } }),
2434
+ /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("div", { style: { marginTop: 24, color: "#FFFFFF", fontSize: 72, fontWeight: 1e3, opacity: msgP }, children: message }),
2435
+ ctaButtons?.length ? /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("div", { style: { marginTop: 34, display: "flex", gap: 18, justifyContent: "center", opacity: ctaP }, children: ctaButtons.map((b, i) => /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)(
2436
+ "div",
2437
+ {
2438
+ style: {
2439
+ padding: "18px 28px",
2440
+ borderRadius: 18,
2441
+ backgroundColor: "rgba(255,255,255,0.12)",
2442
+ color: "#FFFFFF",
2443
+ fontSize: 28,
2444
+ fontWeight: 900
2445
+ },
2446
+ children: [
2447
+ b.icon ? `${b.icon} ` : "",
2448
+ b.text
2449
+ ]
2450
+ },
2451
+ i
2452
+ )) }) : null,
2453
+ socialHandles?.length ? /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("div", { style: { marginTop: 50, display: "flex", flexDirection: "column", gap: 10, opacity: ctaP }, children: socialHandles.map((h, i) => /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)("div", { style: { color: "rgba(255,255,255,0.85)", fontSize: 26, fontWeight: 800 }, children: [
2454
+ h.platform,
2455
+ ": ",
2456
+ h.handle
2457
+ ] }, i)) }) : null
2458
+ ] }) });
2459
+ };
2460
+ var OutroSceneComponentMetadata = {
2461
+ kind: "composite",
2462
+ category: "branding",
2463
+ description: "End screen with logo, message, optional CTA buttons and social handles",
2464
+ llmGuidance: "Use as the last segment. Keep CTAs <=3 for clarity."
2465
+ };
2466
+
2467
+ // src/components/composites/OutlineText.tsx
2468
+ var import_remotion25 = require("remotion");
2469
+ var import_zod35 = require("zod");
2470
+ var import_jsx_runtime34 = require("react/jsx-runtime");
2471
+ var OutlineTextPropsSchema = import_zod35.z.object({
2472
+ content: import_zod35.z.string().max(50),
2473
+ fontSize: import_zod35.z.number().min(8).max(240).default(96),
2474
+ outlineColor: import_zod35.z.string().default("#FFFFFF"),
2475
+ fillColor: import_zod35.z.string().default("#000000"),
2476
+ fontFamily: import_zod35.z.string().default("Inter"),
2477
+ fontWeight: import_zod35.z.number().int().min(100).max(900).default(800),
2478
+ position: import_zod35.z.enum(["top", "center", "bottom"]).default("center"),
2479
+ animation: import_zod35.z.enum(["draw", "fill"]).default("draw"),
2480
+ strokeWidth: import_zod35.z.number().min(1).max(10).default(3)
2481
+ });
2482
+ var positionStyles3 = (position) => {
2483
+ if (position === "top") return { justifyContent: "center", alignItems: "flex-start", paddingTop: 90 };
2484
+ if (position === "bottom") return { justifyContent: "center", alignItems: "flex-end", paddingBottom: 90 };
2485
+ return { justifyContent: "center", alignItems: "center" };
2486
+ };
2487
+ var OutlineText = ({
2488
+ content,
2489
+ fontSize,
2490
+ outlineColor,
2491
+ fillColor,
2492
+ fontFamily,
2493
+ fontWeight,
2494
+ position,
2495
+ animation,
2496
+ strokeWidth
2497
+ }) => {
2498
+ const frame = (0, import_remotion25.useCurrentFrame)();
2499
+ const t = (0, import_remotion25.interpolate)(frame, [0, 24], [0, 1], { extrapolateLeft: "clamp", extrapolateRight: "clamp" });
2500
+ const strokeOpacity = animation === "draw" ? t : 1;
2501
+ const fillOpacity = animation === "fill" ? t : 0;
2502
+ return /* @__PURE__ */ (0, import_jsx_runtime34.jsxs)(Fill, { style: { display: "flex", ...positionStyles3(position) }, children: [
2503
+ /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(
2504
+ "div",
2505
+ {
2506
+ style: {
2507
+ fontSize,
2508
+ fontFamily,
2509
+ fontWeight,
2510
+ color: fillColor,
2511
+ opacity: fillOpacity,
2512
+ WebkitTextStroke: `${strokeWidth}px ${outlineColor}`,
2513
+ textShadow: `0 0 1px ${outlineColor}`,
2514
+ textAlign: "center",
2515
+ lineHeight: 1
2516
+ },
2517
+ children: content
2518
+ }
2519
+ ),
2520
+ /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(
2521
+ "div",
2522
+ {
2523
+ style: {
2524
+ position: "absolute",
2525
+ fontSize,
2526
+ fontFamily,
2527
+ fontWeight,
2528
+ color: "transparent",
2529
+ opacity: strokeOpacity,
2530
+ WebkitTextStroke: `${strokeWidth}px ${outlineColor}`,
2531
+ textAlign: "center",
2532
+ lineHeight: 1
2533
+ },
2534
+ children: content
2535
+ }
2536
+ )
2537
+ ] });
2538
+ };
2539
+ var OutlineTextComponentMetadata = {
2540
+ kind: "composite",
2541
+ category: "text",
2542
+ description: "Outlined title text with simple draw/fill animation",
2543
+ llmGuidance: 'Use for bold titles. animation="draw" emphasizes the outline; animation="fill" reveals the fill color.'
2544
+ };
2545
+
2546
+ // src/components/composites/ProgressBar.tsx
2547
+ var import_remotion26 = require("remotion");
2548
+ var import_zod36 = require("zod");
2549
+ var import_jsx_runtime35 = require("react/jsx-runtime");
2550
+ var ProgressBarPropsSchema = import_zod36.z.object({
2551
+ label: import_zod36.z.string().optional(),
2552
+ color: import_zod36.z.string().default("#00FF00"),
2553
+ backgroundColor: import_zod36.z.string().default("rgba(255,255,255,0.2)"),
2554
+ height: import_zod36.z.number().min(5).max(50).default(10),
2555
+ position: import_zod36.z.enum(["top", "bottom"]).default("bottom"),
2556
+ showPercentage: import_zod36.z.boolean().default(true)
2557
+ });
2558
+ var ProgressBar = ({
2559
+ label,
2560
+ color,
2561
+ backgroundColor,
2562
+ height,
2563
+ position,
2564
+ showPercentage,
2565
+ __wavesDurationInFrames
2566
+ }) => {
2567
+ const frame = (0, import_remotion26.useCurrentFrame)();
2568
+ const total = Math.max(1, __wavesDurationInFrames ?? 1);
2569
+ const p = (0, import_remotion26.interpolate)(frame, [0, total], [0, 1], { extrapolateLeft: "clamp", extrapolateRight: "clamp" });
2570
+ const pct = Math.round(p * 100);
2571
+ const yStyle = position === "top" ? { top: 50 } : { bottom: 50 };
2572
+ return /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(Fill, { children: /* @__PURE__ */ (0, import_jsx_runtime35.jsxs)("div", { style: { position: "absolute", left: 80, right: 80, ...yStyle }, children: [
2573
+ label || showPercentage ? /* @__PURE__ */ (0, import_jsx_runtime35.jsxs)("div", { style: { display: "flex", justifyContent: "space-between", marginBottom: 10, color: "#FFFFFF", fontWeight: 700 }, children: [
2574
+ /* @__PURE__ */ (0, import_jsx_runtime35.jsx)("span", { children: label ?? "" }),
2575
+ /* @__PURE__ */ (0, import_jsx_runtime35.jsx)("span", { children: showPercentage ? `${pct}%` : "" })
2576
+ ] }) : null,
2577
+ /* @__PURE__ */ (0, import_jsx_runtime35.jsx)("div", { style: { width: "100%", height, backgroundColor, borderRadius: 9999, overflow: "hidden" }, children: /* @__PURE__ */ (0, import_jsx_runtime35.jsx)("div", { style: { width: `${pct}%`, height: "100%", backgroundColor: color } }) })
2578
+ ] }) });
2579
+ };
2580
+ var ProgressBarComponentMetadata = {
2581
+ kind: "composite",
2582
+ category: "data",
2583
+ description: "Animated progress bar that fills over the component duration",
2584
+ llmGuidance: "Use for loading/countdowns. showPercentage=true is helpful for clarity."
2585
+ };
2586
+
2587
+ // src/components/composites/ProgressRing.tsx
2588
+ var import_remotion27 = require("remotion");
2589
+ var import_zod37 = require("zod");
2590
+ var import_jsx_runtime36 = require("react/jsx-runtime");
2591
+ var ProgressRingPropsSchema = import_zod37.z.object({
2592
+ percentage: import_zod37.z.number().min(0).max(100),
2593
+ size: import_zod37.z.number().min(100).max(500).default(200),
2594
+ strokeWidth: import_zod37.z.number().min(5).max(50).default(20),
2595
+ color: import_zod37.z.string().default("#00FF00"),
2596
+ backgroundColor: import_zod37.z.string().default("rgba(255,255,255,0.2)"),
2597
+ showLabel: import_zod37.z.boolean().default(true)
2598
+ });
2599
+ var ProgressRing = ({
2600
+ percentage,
2601
+ size,
2602
+ strokeWidth,
2603
+ color,
2604
+ backgroundColor,
2605
+ showLabel,
2606
+ __wavesDurationInFrames
2607
+ }) => {
2608
+ const frame = (0, import_remotion27.useCurrentFrame)();
2609
+ const total = Math.max(1, __wavesDurationInFrames ?? 1);
2610
+ const p = (0, import_remotion27.interpolate)(frame, [0, total], [0, 1], { extrapolateLeft: "clamp", extrapolateRight: "clamp" });
2611
+ const current = percentage * p;
2612
+ const r = (size - strokeWidth) / 2;
2613
+ const c = 2 * Math.PI * r;
2614
+ const dash = c;
2615
+ const offset = c - current / 100 * c;
2616
+ return /* @__PURE__ */ (0, import_jsx_runtime36.jsxs)(Fill, { style: { display: "flex", justifyContent: "center", alignItems: "center" }, children: [
2617
+ /* @__PURE__ */ (0, import_jsx_runtime36.jsxs)("svg", { width: size, height: size, viewBox: `0 0 ${size} ${size}`, children: [
2618
+ /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(
2619
+ "circle",
2620
+ {
2621
+ cx: size / 2,
2622
+ cy: size / 2,
2623
+ r,
2624
+ stroke: backgroundColor,
2625
+ strokeWidth,
2626
+ fill: "transparent"
2627
+ }
2628
+ ),
2629
+ /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(
2630
+ "circle",
2631
+ {
2632
+ cx: size / 2,
2633
+ cy: size / 2,
2634
+ r,
2635
+ stroke: color,
2636
+ strokeWidth,
2637
+ fill: "transparent",
2638
+ strokeDasharray: dash,
2639
+ strokeDashoffset: offset,
2640
+ strokeLinecap: "round",
2641
+ transform: `rotate(-90 ${size / 2} ${size / 2})`
2642
+ }
2643
+ )
2644
+ ] }),
2645
+ showLabel ? /* @__PURE__ */ (0, import_jsx_runtime36.jsxs)("div", { style: { position: "absolute", color: "#FFFFFF", fontWeight: 800, fontSize: Math.round(size * 0.22) }, children: [
2646
+ Math.round(current),
2647
+ "%"
2648
+ ] }) : null
2649
+ ] });
2650
+ };
2651
+ var ProgressRingComponentMetadata = {
2652
+ kind: "composite",
2653
+ category: "data",
2654
+ description: "Circular progress indicator (SVG) that animates from 0 to percentage over duration",
2655
+ llmGuidance: "Use for completion and goals. size 160-260 is typical. showLabel displays the percentage."
2656
+ };
2657
+
2658
+ // src/components/composites/SlideTransition.tsx
2659
+ var import_react7 = __toESM(require("react"));
2660
+ var import_remotion28 = require("remotion");
2661
+ var import_zod38 = require("zod");
2662
+ var import_jsx_runtime37 = require("react/jsx-runtime");
2663
+ var SlideTransitionPropsSchema = import_zod38.z.object({
2664
+ durationInFrames: import_zod38.z.number().int().min(10).max(60).default(30),
2665
+ direction: import_zod38.z.enum(["left", "right", "up", "down"]).default("left"),
2666
+ distance: import_zod38.z.number().int().min(1).max(2e3).default(160),
2667
+ phase: import_zod38.z.enum(["in", "out", "inOut"]).default("inOut")
2668
+ });
2669
+ function translateFor(direction, delta) {
2670
+ if (direction === "left") return { x: -delta, y: 0 };
2671
+ if (direction === "right") return { x: delta, y: 0 };
2672
+ if (direction === "up") return { x: 0, y: -delta };
2673
+ return { x: 0, y: delta };
2674
+ }
2675
+ var SlideTransition = ({
2676
+ durationInFrames,
2677
+ direction,
2678
+ distance,
2679
+ phase,
2680
+ children,
2681
+ __wavesDurationInFrames
2682
+ }) => {
2683
+ const layers = import_react7.default.Children.toArray(children);
2684
+ const frame = (0, import_remotion28.useCurrentFrame)();
2685
+ const total = Math.max(1, __wavesDurationInFrames ?? 1);
2686
+ const d = Math.min(durationInFrames, total);
2687
+ let opacity = 1;
2688
+ let tx = 0;
2689
+ let ty = 0;
2690
+ if (phase === "in" || phase === "inOut") {
2691
+ const t = (0, import_remotion28.interpolate)(frame, [0, d], [1, 0], { extrapolateLeft: "clamp", extrapolateRight: "clamp" });
2692
+ const { x, y } = translateFor(direction, distance * t);
2693
+ tx += x;
2694
+ ty += y;
2695
+ opacity *= (0, import_remotion28.interpolate)(frame, [0, d], [0, 1], { extrapolateLeft: "clamp", extrapolateRight: "clamp" });
2696
+ }
2697
+ if (phase === "out" || phase === "inOut") {
2698
+ const start = Math.max(0, total - d);
2699
+ const t = (0, import_remotion28.interpolate)(frame, [start, total], [0, 1], { extrapolateLeft: "clamp", extrapolateRight: "clamp" });
2700
+ const opposite = direction === "left" ? "right" : direction === "right" ? "left" : direction === "up" ? "down" : "up";
2701
+ const { x, y } = translateFor(opposite, distance * t);
2702
+ tx += x;
2703
+ ty += y;
2704
+ opacity *= (0, import_remotion28.interpolate)(frame, [start, total], [1, 0], { extrapolateLeft: "clamp", extrapolateRight: "clamp" });
2705
+ }
2706
+ return /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(Fill, { style: { opacity, transform: `translate(${tx}px, ${ty}px)` }, children: layers.map((child, i) => /* @__PURE__ */ (0, import_jsx_runtime37.jsx)("div", { style: { position: "absolute", inset: 0 }, children: child }, i)) });
2707
+ };
2708
+ var SlideTransitionComponentMetadata = {
2709
+ kind: "composite",
2710
+ category: "transition",
2711
+ acceptsChildren: true,
2712
+ minChildren: 1,
2713
+ description: "Slide in/out wrapper (used for segment transitions and overlays)",
2714
+ llmGuidance: "Use for more dynamic transitions. direction controls where content enters from."
2715
+ };
2716
+
2717
+ // src/components/composites/SplitScreen.tsx
2718
+ var import_react8 = __toESM(require("react"));
2719
+ var import_zod39 = require("zod");
2720
+ var import_jsx_runtime38 = require("react/jsx-runtime");
2721
+ var SplitScreenPropsSchema = import_zod39.z.object({
2722
+ orientation: import_zod39.z.enum(["vertical", "horizontal"]).default("vertical"),
2723
+ split: import_zod39.z.number().min(0.1).max(0.9).default(0.5),
2724
+ gap: import_zod39.z.number().min(0).default(48),
2725
+ padding: import_zod39.z.number().min(0).default(80),
2726
+ dividerColor: import_zod39.z.string().optional()
2727
+ });
2728
+ var SplitScreen = ({
2729
+ orientation,
2730
+ split,
2731
+ gap,
2732
+ padding,
2733
+ dividerColor,
2734
+ children
2735
+ }) => {
2736
+ const items = import_react8.default.Children.toArray(children);
2737
+ const first = items[0] ?? null;
2738
+ const second = items[1] ?? null;
2739
+ const isVertical = orientation === "vertical";
2740
+ const flexDirection = isVertical ? "row" : "column";
2741
+ return /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(Fill, { style: { padding, boxSizing: "border-box" }, children: /* @__PURE__ */ (0, import_jsx_runtime38.jsxs)("div", { style: { display: "flex", flexDirection, gap, width: "100%", height: "100%" }, children: [
2742
+ /* @__PURE__ */ (0, import_jsx_runtime38.jsx)("div", { style: { flex: split, position: "relative" }, children: first }),
2743
+ dividerColor ? /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(
2744
+ "div",
2745
+ {
2746
+ style: {
2747
+ backgroundColor: dividerColor,
2748
+ width: isVertical ? 2 : "100%",
2749
+ height: isVertical ? "100%" : 2,
2750
+ opacity: 0.7
2751
+ }
2752
+ }
2753
+ ) : null,
2754
+ /* @__PURE__ */ (0, import_jsx_runtime38.jsx)("div", { style: { flex: 1 - split, position: "relative" }, children: second })
2755
+ ] }) });
2756
+ };
2757
+ var SplitScreenComponentMetadata = {
2758
+ kind: "composite",
2759
+ category: "layout",
2760
+ acceptsChildren: true,
2761
+ minChildren: 2,
2762
+ maxChildren: 2,
2763
+ description: "Two-panel split screen layout",
2764
+ llmGuidance: 'Provide exactly 2 children. Use orientation="vertical" for left/right and "horizontal" for top/bottom.'
2765
+ };
2766
+
2767
+ // src/components/composites/SplitText.tsx
2768
+ var import_remotion29 = require("remotion");
2769
+ var import_zod40 = require("zod");
2770
+ var import_jsx_runtime39 = require("react/jsx-runtime");
2771
+ var SplitTextPropsSchema = import_zod40.z.object({
2772
+ content: import_zod40.z.string().max(200),
2773
+ fontSize: import_zod40.z.number().min(8).max(200).default(48),
2774
+ color: import_zod40.z.string().default("#FFFFFF"),
2775
+ fontFamily: import_zod40.z.string().default("Inter"),
2776
+ position: import_zod40.z.enum(["top", "center", "bottom"]).default("center"),
2777
+ splitBy: import_zod40.z.enum(["word", "letter"]).default("word"),
2778
+ stagger: import_zod40.z.number().int().min(1).max(10).default(3),
2779
+ animation: import_zod40.z.enum(["fade", "slideUp", "slideDown", "scale", "rotate"]).default("slideUp"),
2780
+ maxWidthPct: import_zod40.z.number().min(0.1).max(1).default(0.9),
2781
+ safeInsetPct: import_zod40.z.number().min(0).max(0.25).default(0.06)
2782
+ });
2783
+ var positionStyles4 = (position, safeInsetPct) => {
2784
+ const inset = `${(safeInsetPct * 100).toFixed(2)}%`;
2785
+ if (position === "top") return { justifyContent: "center", alignItems: "flex-start", paddingTop: inset };
2786
+ if (position === "bottom") return { justifyContent: "center", alignItems: "flex-end", paddingBottom: inset };
2787
+ return { justifyContent: "center", alignItems: "center" };
2788
+ };
2789
+ function getItemStyle(progress, animation) {
2790
+ const clamped = Math.max(0, Math.min(1, progress));
2791
+ switch (animation) {
2792
+ case "fade":
2793
+ return { opacity: clamped };
2794
+ case "slideDown":
2795
+ return { opacity: clamped, transform: `translateY(${-20 * (1 - clamped)}px)` };
2796
+ case "scale":
2797
+ return { opacity: clamped, transform: `scale(${0.9 + 0.1 * clamped})` };
2798
+ case "rotate":
2799
+ return { opacity: clamped, transform: `rotate(${(-10 * (1 - clamped)).toFixed(2)}deg)` };
2800
+ case "slideUp":
2801
+ default:
2802
+ return { opacity: clamped, transform: `translateY(${20 * (1 - clamped)}px)` };
2803
+ }
2804
+ }
2805
+ var SplitText = ({
2806
+ content,
2807
+ fontSize,
2808
+ color,
2809
+ fontFamily,
2810
+ position,
2811
+ splitBy,
2812
+ stagger,
2813
+ animation,
2814
+ maxWidthPct,
2815
+ safeInsetPct
2816
+ }) => {
2817
+ const frame = (0, import_remotion29.useCurrentFrame)();
2818
+ const { fps } = (0, import_remotion29.useVideoConfig)();
2819
+ const items = splitBy === "letter" ? content.split("") : content.trim().split(/\s+/);
2820
+ return /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(Fill, { style: { display: "flex", ...positionStyles4(position, safeInsetPct) }, children: /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(
2821
+ "div",
2822
+ {
2823
+ style: {
2824
+ display: "flex",
2825
+ flexWrap: "wrap",
2826
+ justifyContent: "center",
2827
+ gap: splitBy === "letter" ? 0 : 12,
2828
+ maxWidth: `${Math.round(maxWidthPct * 100)}%`,
2829
+ fontSize,
2830
+ color,
2831
+ fontFamily,
2832
+ fontWeight: 700,
2833
+ textAlign: "center",
2834
+ overflowWrap: "anywhere",
2835
+ wordBreak: "break-word"
2836
+ },
2837
+ children: items.map((t, i) => {
2838
+ const progress = (0, import_remotion29.spring)({
2839
+ frame: frame - i * stagger,
2840
+ fps,
2841
+ config: { damping: 14, stiffness: 120, mass: 0.9 }
2842
+ });
2843
+ return /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(
2844
+ "span",
2845
+ {
2846
+ style: {
2847
+ display: "inline-block",
2848
+ whiteSpace: t === " " ? "pre" : "pre-wrap",
2849
+ ...getItemStyle(progress, animation)
2850
+ },
2851
+ children: splitBy === "word" ? `${t} ` : t
2852
+ },
2853
+ `${i}-${t}`
2854
+ );
2855
+ })
2856
+ }
2857
+ ) });
2858
+ };
2859
+ var SplitTextComponentMetadata = {
2860
+ kind: "composite",
2861
+ category: "text",
2862
+ description: "Animated text where each word or letter enters with a staggered effect",
2863
+ llmGuidance: 'Use for titles. splitBy="word" is best for phrases; splitBy="letter" is dramatic for short words.',
2864
+ examples: [
2865
+ { content: "Welcome to Waves", splitBy: "word", stagger: 3, animation: "slideUp" },
2866
+ { content: "HELLO", splitBy: "letter", stagger: 2, animation: "scale" }
2867
+ ]
2868
+ };
2869
+
2870
+ // src/components/composites/SubtitleText.tsx
2871
+ var import_remotion30 = require("remotion");
2872
+ var import_zod41 = require("zod");
2873
+ var import_jsx_runtime40 = require("react/jsx-runtime");
2874
+ var SubtitleTextPropsSchema = import_zod41.z.object({
2875
+ text: import_zod41.z.string().max(200),
2876
+ fontSize: import_zod41.z.number().min(12).max(80).default(36),
2877
+ color: import_zod41.z.string().default("#FFFFFF"),
2878
+ backgroundColor: import_zod41.z.string().default("rgba(0,0,0,0.7)"),
2879
+ fontFamily: import_zod41.z.string().default("Inter"),
2880
+ position: import_zod41.z.enum(["top", "bottom"]).default("bottom"),
2881
+ maxWidth: import_zod41.z.number().min(200).max(1200).default(800),
2882
+ padding: import_zod41.z.number().min(0).max(80).default(20),
2883
+ highlightWords: import_zod41.z.array(import_zod41.z.string()).optional()
2884
+ });
2885
+ function normalizeWord(w) {
2886
+ return w.toLowerCase().replace(/[^a-z0-9]/g, "");
2887
+ }
2888
+ var SubtitleText = ({
2889
+ text,
2890
+ fontSize,
2891
+ color,
2892
+ backgroundColor,
2893
+ fontFamily,
2894
+ position,
2895
+ maxWidth,
2896
+ padding,
2897
+ highlightWords,
2898
+ __wavesDurationInFrames
2899
+ }) => {
2900
+ const frame = (0, import_remotion30.useCurrentFrame)();
2901
+ const total = Math.max(1, __wavesDurationInFrames ?? 1);
2902
+ const inFade = (0, import_remotion30.interpolate)(frame, [0, 10], [0, 1], { extrapolateLeft: "clamp", extrapolateRight: "clamp" });
2903
+ const outFade = (0, import_remotion30.interpolate)(frame, [Math.max(0, total - 10), total], [1, 0], { extrapolateLeft: "clamp", extrapolateRight: "clamp" });
2904
+ const opacity = inFade * outFade;
2905
+ const highlights = new Set((highlightWords ?? []).map(normalizeWord));
2906
+ const tokens = text.split(/\s+/);
2907
+ const yStyle = position === "top" ? { top: 70 } : { bottom: 90 };
2908
+ return /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(Fill, { children: /* @__PURE__ */ (0, import_jsx_runtime40.jsx)("div", { style: { position: "absolute", left: "50%", transform: "translateX(-50%)", maxWidth, width: "100%", ...yStyle }, children: /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(
2909
+ "div",
2910
+ {
2911
+ style: {
2912
+ display: "inline-block",
2913
+ backgroundColor,
2914
+ padding,
2915
+ borderRadius: 18,
2916
+ opacity,
2917
+ color,
2918
+ fontSize,
2919
+ fontFamily,
2920
+ fontWeight: 800,
2921
+ lineHeight: 1.2,
2922
+ textAlign: "center"
2923
+ },
2924
+ children: tokens.map((t, i) => {
2925
+ const n = normalizeWord(t);
2926
+ const isHighlighted = highlights.has(n);
2927
+ return /* @__PURE__ */ (0, import_jsx_runtime40.jsx)("span", { style: { color: isHighlighted ? "#FFD60A" : color, marginRight: 8 }, children: t }, i);
2928
+ })
2929
+ }
2930
+ ) }) });
2931
+ };
2932
+ var SubtitleTextComponentMetadata = {
2933
+ kind: "composite",
2934
+ category: "text",
2935
+ description: "Caption/subtitle box with fade in/out and optional highlighted words",
2936
+ llmGuidance: "Use for narration/captions. highlightWords helps emphasize key terms."
2937
+ };
2938
+
2939
+ // src/components/composites/TikTokCaption.tsx
2940
+ var import_remotion31 = require("remotion");
2941
+ var import_zod42 = require("zod");
2942
+ var import_jsx_runtime41 = require("react/jsx-runtime");
2943
+ var TikTokCaptionPropsSchema = import_zod42.z.object({
2944
+ text: import_zod42.z.string().max(150),
2945
+ fontSize: import_zod42.z.number().min(12).max(120).default(48),
2946
+ color: import_zod42.z.string().default("#FFFFFF"),
2947
+ strokeColor: import_zod42.z.string().default("#000000"),
2948
+ strokeWidth: import_zod42.z.number().min(0).max(10).default(3),
2949
+ position: import_zod42.z.enum(["center", "bottom"]).default("center"),
2950
+ highlightStyle: import_zod42.z.enum(["word", "bounce", "none"]).default("word"),
2951
+ maxWidthPct: import_zod42.z.number().min(0.1).max(1).default(0.92),
2952
+ safeInsetPct: import_zod42.z.number().min(0).max(0.25).default(0.06)
2953
+ });
2954
+ var TikTokCaption = ({
2955
+ text,
2956
+ fontSize,
2957
+ color,
2958
+ strokeColor,
2959
+ strokeWidth,
2960
+ position,
2961
+ highlightStyle,
2962
+ maxWidthPct,
2963
+ safeInsetPct,
2964
+ __wavesDurationInFrames
2965
+ }) => {
2966
+ const frame = (0, import_remotion31.useCurrentFrame)();
2967
+ const total = Math.max(1, __wavesDurationInFrames ?? 1);
2968
+ const p = (0, import_remotion31.interpolate)(frame, [0, total], [0, 1], { extrapolateLeft: "clamp", extrapolateRight: "clamp" });
2969
+ const words = text.trim().split(/\s+/);
2970
+ const idx = highlightStyle === "none" ? -1 : Math.min(words.length - 1, Math.floor(p * words.length));
2971
+ const inset = `${(safeInsetPct * 100).toFixed(2)}%`;
2972
+ const yStyle = position === "bottom" ? { alignItems: "flex-end", paddingBottom: inset } : { alignItems: "center" };
2973
+ return /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(Fill, { style: { display: "flex", justifyContent: "center", ...yStyle }, children: /* @__PURE__ */ (0, import_jsx_runtime41.jsx)("div", { style: { textAlign: "center", paddingLeft: inset, paddingRight: inset, maxWidth: `${Math.round(maxWidthPct * 100)}%`, fontWeight: 900, lineHeight: 1.1, overflowWrap: "anywhere", wordBreak: "break-word" }, children: words.map((w, i) => {
2974
+ const isActive = i === idx && highlightStyle !== "none";
2975
+ const bounce = isActive && highlightStyle === "bounce" ? (0, import_remotion31.interpolate)(frame % 18, [0, 9, 18], [1, 1.08, 1], { extrapolateLeft: "clamp", extrapolateRight: "clamp" }) : 1;
2976
+ return /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(
2977
+ "span",
2978
+ {
2979
+ style: {
2980
+ display: "inline-block",
2981
+ marginRight: 12,
2982
+ fontSize,
2983
+ color: isActive ? "#FFD60A" : color,
2984
+ WebkitTextStroke: `${strokeWidth}px ${strokeColor}`,
2985
+ transform: `scale(${bounce})`
2986
+ },
2987
+ children: w
2988
+ },
2989
+ `${i}-${w}`
2990
+ );
2991
+ }) }) });
2992
+ };
2993
+ var TikTokCaptionComponentMetadata = {
2994
+ kind: "composite",
2995
+ category: "social",
2996
+ description: "TikTok-style captions with stroke and optional word highlighting",
2997
+ llmGuidance: 'Always keep strokeWidth>=2 for readability. highlightStyle="word" or "bounce" makes captions feel dynamic.'
2998
+ };
2999
+
3000
+ // src/components/composites/ThirdLowerBanner.tsx
3001
+ var import_remotion32 = require("remotion");
3002
+ var import_zod43 = require("zod");
3003
+ var import_jsx_runtime42 = require("react/jsx-runtime");
3004
+ var ThirdLowerBannerPropsSchema = import_zod43.z.object({
3005
+ name: import_zod43.z.string().max(50),
3006
+ title: import_zod43.z.string().max(100),
3007
+ backgroundColor: import_zod43.z.string().default("rgba(0,0,0,0.8)"),
3008
+ primaryColor: import_zod43.z.string().default("#FFFFFF"),
3009
+ secondaryColor: import_zod43.z.string().default("#CCCCCC"),
3010
+ accentColor: import_zod43.z.string().default("#FF0000"),
3011
+ showAvatar: import_zod43.z.boolean().default(false),
3012
+ avatarSrc: import_zod43.z.string().optional()
3013
+ });
3014
+ var resolveAsset13 = (value) => isRemoteAssetPath(value) ? value : (0, import_remotion32.staticFile)(staticFileInputFromAssetPath(value));
3015
+ var ThirdLowerBanner = ({
3016
+ name,
3017
+ title,
3018
+ backgroundColor,
3019
+ primaryColor,
3020
+ secondaryColor,
3021
+ accentColor,
3022
+ showAvatar,
3023
+ avatarSrc
3024
+ }) => {
3025
+ const frame = (0, import_remotion32.useCurrentFrame)();
3026
+ const slide = (0, import_remotion32.interpolate)(frame, [0, 18], [-600, 0], { extrapolateLeft: "clamp", extrapolateRight: "clamp" });
3027
+ const opacity = (0, import_remotion32.interpolate)(frame, [0, 10], [0, 1], { extrapolateLeft: "clamp", extrapolateRight: "clamp" });
3028
+ const avatar = showAvatar && typeof avatarSrc === "string" && avatarSrc.length > 0 ? avatarSrc : null;
3029
+ return /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(Fill, { children: /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)(
3030
+ "div",
3031
+ {
3032
+ style: {
3033
+ position: "absolute",
3034
+ left: 80,
3035
+ bottom: 80,
3036
+ width: 980,
3037
+ height: 160,
3038
+ transform: `translateX(${slide}px)`,
3039
+ opacity,
3040
+ display: "flex",
3041
+ overflow: "hidden",
3042
+ borderRadius: 18,
3043
+ backgroundColor
3044
+ },
3045
+ children: [
3046
+ /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("div", { style: { width: 10, backgroundColor: accentColor } }),
3047
+ avatar ? /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("div", { style: { width: 160, display: "flex", alignItems: "center", justifyContent: "center" }, children: /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(
3048
+ import_remotion32.Img,
3049
+ {
3050
+ src: resolveAsset13(avatar),
3051
+ style: { width: 110, height: 110, borderRadius: 9999, objectFit: "cover" }
3052
+ }
3053
+ ) }) : null,
3054
+ /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)("div", { style: { padding: "28px 36px", display: "flex", flexDirection: "column", justifyContent: "center" }, children: [
3055
+ /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("div", { style: { color: primaryColor, fontSize: 54, fontWeight: 800, lineHeight: 1 }, children: name }),
3056
+ /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("div", { style: { marginTop: 10, color: secondaryColor, fontSize: 28, fontWeight: 600 }, children: title })
3057
+ ] })
3058
+ ]
3059
+ }
3060
+ ) });
3061
+ };
3062
+ var ThirdLowerBannerComponentMetadata = {
3063
+ kind: "composite",
3064
+ category: "layout",
3065
+ description: "Broadcast-style lower-third banner with name/title and optional avatar",
3066
+ llmGuidance: "Use for speaker introductions. name = big label, title = smaller subtitle. showAvatar + avatarSrc for profile image.",
3067
+ examples: [
3068
+ { name: "Alex Chen", title: "Product Designer", accentColor: "#FF3B30" },
3069
+ { name: "Depths AI", title: "Waves v0.2.0", showAvatar: false }
3070
+ ]
3071
+ };
3072
+
3073
+ // src/components/composites/TypewriterText.tsx
3074
+ var import_remotion33 = require("remotion");
3075
+ var import_zod44 = require("zod");
3076
+ var import_jsx_runtime43 = require("react/jsx-runtime");
3077
+ var TypewriterTextPropsSchema = import_zod44.z.object({
3078
+ content: import_zod44.z.string().max(500),
3079
+ fontSize: import_zod44.z.number().min(8).max(200).default(48),
3080
+ color: import_zod44.z.string().default("#FFFFFF"),
3081
+ fontFamily: import_zod44.z.string().default("Inter"),
3082
+ position: import_zod44.z.enum(["top", "center", "bottom"]).default("center"),
3083
+ speed: import_zod44.z.number().min(0.5).max(5).default(2),
3084
+ showCursor: import_zod44.z.boolean().default(true),
3085
+ cursorColor: import_zod44.z.string().default("#FFFFFF"),
3086
+ maxWidthPct: import_zod44.z.number().min(0.1).max(1).default(0.9),
3087
+ safeInsetPct: import_zod44.z.number().min(0).max(0.25).default(0.06)
3088
+ });
3089
+ var positionStyles5 = (position, safeInsetPct) => {
3090
+ const inset = `${(safeInsetPct * 100).toFixed(2)}%`;
3091
+ if (position === "top") return { justifyContent: "center", alignItems: "flex-start", paddingTop: inset };
3092
+ if (position === "bottom") return { justifyContent: "center", alignItems: "flex-end", paddingBottom: inset };
3093
+ return { justifyContent: "center", alignItems: "center" };
3094
+ };
3095
+ var TypewriterText = ({
3096
+ content,
3097
+ fontSize,
3098
+ color,
3099
+ fontFamily,
3100
+ position,
3101
+ speed,
3102
+ showCursor,
3103
+ cursorColor,
3104
+ maxWidthPct,
3105
+ safeInsetPct
3106
+ }) => {
3107
+ const frame = (0, import_remotion33.useCurrentFrame)();
3108
+ const charCount = Math.min(content.length, Math.max(0, Math.floor(frame * speed)));
3109
+ const shown = content.slice(0, charCount);
3110
+ const cursorVisible = showCursor && charCount < content.length ? frame % 30 < 15 : false;
3111
+ const cursorOpacity = cursorVisible ? 1 : 0;
3112
+ const cursorFade = (0, import_remotion33.interpolate)(frame % 30, [0, 5], [0, 1], { extrapolateLeft: "clamp", extrapolateRight: "clamp" });
3113
+ return /* @__PURE__ */ (0, import_jsx_runtime43.jsx)(Fill, { style: { display: "flex", ...positionStyles5(position, safeInsetPct) }, children: /* @__PURE__ */ (0, import_jsx_runtime43.jsxs)("div", { style: { fontSize, color, fontFamily, fontWeight: 600, textAlign: "center", whiteSpace: "pre-wrap", maxWidth: `${Math.round(maxWidthPct * 100)}%`, overflowWrap: "anywhere", wordBreak: "break-word" }, children: [
3114
+ shown,
3115
+ showCursor ? /* @__PURE__ */ (0, import_jsx_runtime43.jsx)("span", { style: { color: cursorColor, opacity: cursorOpacity * cursorFade }, children: "|" }) : null
3116
+ ] }) });
3117
+ };
3118
+ var TypewriterTextComponentMetadata = {
3119
+ kind: "composite",
3120
+ category: "text",
3121
+ description: "Character-by-character text reveal with optional blinking cursor",
3122
+ llmGuidance: "Use for dramatic reveals and terminal-style text. speed ~1-2 is readable; 3-5 is fast.",
3123
+ examples: [
3124
+ { content: "Hello Waves", speed: 2, position: "center", fontSize: 72 },
3125
+ { content: 'console.log("hi")', speed: 1.5, fontFamily: "monospace", position: "top" }
3126
+ ]
3127
+ };
3128
+
3129
+ // src/components/composites/TwitterCard.tsx
3130
+ var import_remotion34 = require("remotion");
3131
+ var import_zod45 = require("zod");
3132
+ var import_jsx_runtime44 = require("react/jsx-runtime");
3133
+ var TwitterCardPropsSchema = import_zod45.z.object({
3134
+ author: import_zod45.z.string().min(1),
3135
+ handle: import_zod45.z.string().min(1),
3136
+ avatarSrc: import_zod45.z.string().optional(),
3137
+ tweet: import_zod45.z.string().max(280),
3138
+ image: import_zod45.z.string().optional(),
3139
+ timestamp: import_zod45.z.string().optional(),
3140
+ verified: import_zod45.z.boolean().default(false)
3141
+ });
3142
+ var resolveAsset14 = (value) => isRemoteAssetPath(value) ? value : (0, import_remotion34.staticFile)(staticFileInputFromAssetPath(value));
3143
+ var TwitterCard = ({ author, handle, avatarSrc, tweet, image, timestamp, verified }) => {
3144
+ return /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(Fill, { style: { backgroundColor: "#0B0F14", display: "flex", justifyContent: "center", alignItems: "center" }, children: /* @__PURE__ */ (0, import_jsx_runtime44.jsxs)(
3145
+ "div",
3146
+ {
3147
+ style: {
3148
+ width: 1100,
3149
+ borderRadius: 28,
3150
+ backgroundColor: "#FFFFFF",
3151
+ padding: 48,
3152
+ boxSizing: "border-box",
3153
+ boxShadow: "0 30px 90px rgba(0,0,0,0.35)"
3154
+ },
3155
+ children: [
3156
+ /* @__PURE__ */ (0, import_jsx_runtime44.jsxs)("div", { style: { display: "flex", gap: 18, alignItems: "center" }, children: [
3157
+ avatarSrc ? /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(import_remotion34.Img, { src: resolveAsset14(avatarSrc), style: { width: 78, height: 78, borderRadius: 9999, objectFit: "cover" } }) : /* @__PURE__ */ (0, import_jsx_runtime44.jsx)("div", { style: { width: 78, height: 78, borderRadius: 9999, backgroundColor: "#E5E7EB" } }),
3158
+ /* @__PURE__ */ (0, import_jsx_runtime44.jsxs)("div", { style: { display: "flex", flexDirection: "column" }, children: [
3159
+ /* @__PURE__ */ (0, import_jsx_runtime44.jsxs)("div", { style: { display: "flex", gap: 10, alignItems: "center" }, children: [
3160
+ /* @__PURE__ */ (0, import_jsx_runtime44.jsx)("div", { style: { fontSize: 34, fontWeight: 900, color: "#111827" }, children: author }),
3161
+ verified ? /* @__PURE__ */ (0, import_jsx_runtime44.jsx)("div", { style: { fontSize: 26, color: "#1D9BF0", fontWeight: 900 }, children: "\u2713" }) : null
3162
+ ] }),
3163
+ /* @__PURE__ */ (0, import_jsx_runtime44.jsx)("div", { style: { fontSize: 26, color: "#6B7280", fontWeight: 800 }, children: handle })
3164
+ ] })
3165
+ ] }),
3166
+ /* @__PURE__ */ (0, import_jsx_runtime44.jsx)("div", { style: { marginTop: 28, fontSize: 36, lineHeight: 1.25, color: "#111827", fontWeight: 700 }, children: tweet }),
3167
+ image ? /* @__PURE__ */ (0, import_jsx_runtime44.jsx)("div", { style: { marginTop: 28, width: "100%", height: 520, overflow: "hidden", borderRadius: 22, backgroundColor: "#F3F4F6" }, children: /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(import_remotion34.Img, { src: resolveAsset14(image), style: { width: "100%", height: "100%", objectFit: "cover" } }) }) : null,
3168
+ timestamp ? /* @__PURE__ */ (0, import_jsx_runtime44.jsx)("div", { style: { marginTop: 22, fontSize: 22, color: "#6B7280", fontWeight: 700 }, children: timestamp }) : null
3169
+ ]
3170
+ }
3171
+ ) });
3172
+ };
3173
+ var TwitterCardComponentMetadata = {
3174
+ kind: "composite",
3175
+ category: "social",
3176
+ description: "Twitter/X post card layout with author header and optional image",
3177
+ llmGuidance: "Use for announcements/testimonials. Keep tweet short for readability."
3178
+ };
3179
+
3180
+ // src/components/composites/Watermark.tsx
3181
+ var import_remotion35 = require("remotion");
3182
+ var import_zod46 = require("zod");
3183
+ var import_jsx_runtime45 = require("react/jsx-runtime");
3184
+ var WatermarkPropsSchema = import_zod46.z.object({
3185
+ type: import_zod46.z.enum(["logo", "text"]).default("logo"),
3186
+ src: import_zod46.z.string().optional(),
3187
+ text: import_zod46.z.string().optional(),
3188
+ position: import_zod46.z.enum(["topLeft", "topRight", "bottomLeft", "bottomRight"]).default("bottomRight"),
3189
+ opacity: import_zod46.z.number().min(0.1).max(1).default(0.5),
3190
+ size: import_zod46.z.number().min(30).max(150).default(60),
3191
+ color: import_zod46.z.string().default("#FFFFFF")
3192
+ });
3193
+ var resolveAsset15 = (value) => isRemoteAssetPath(value) ? value : (0, import_remotion35.staticFile)(staticFileInputFromAssetPath(value));
3194
+ function posStyle(position) {
3195
+ const offset = 30;
3196
+ if (position === "topLeft") return { left: offset, top: offset };
3197
+ if (position === "topRight") return { right: offset, top: offset };
3198
+ if (position === "bottomLeft") return { left: offset, bottom: offset };
3199
+ return { right: offset, bottom: offset };
3200
+ }
3201
+ var Watermark = ({ type, src, text, position, opacity, size, color }) => {
3202
+ const style = {
3203
+ position: "absolute",
3204
+ ...posStyle(position),
3205
+ opacity,
3206
+ pointerEvents: "none"
3207
+ };
3208
+ return /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(Fill, { children: type === "text" ? /* @__PURE__ */ (0, import_jsx_runtime45.jsx)("div", { style: { ...style, fontSize: Math.round(size * 0.6), color, fontWeight: 700 }, children: text ?? "" }) : src ? /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(
3209
+ import_remotion35.Img,
3210
+ {
3211
+ src: resolveAsset15(src),
3212
+ style: { ...style, width: size, height: size, objectFit: "contain" }
3213
+ }
3214
+ ) : null });
3215
+ };
3216
+ var WatermarkComponentMetadata = {
3217
+ kind: "composite",
3218
+ category: "branding",
3219
+ description: "Persistent logo/text watermark in a corner",
3220
+ llmGuidance: "Use subtle opacity (0.3-0.6). bottomRight is standard.",
3221
+ examples: [
3222
+ { type: "text", text: "@depths.ai", position: "bottomRight", opacity: 0.4, size: 60 },
3223
+ { type: "logo", src: "/assets/logo.svg", position: "topLeft", opacity: 0.5, size: 70 }
3224
+ ]
3225
+ };
3226
+
3227
+ // src/components/composites/WipeTransition.tsx
3228
+ var import_react9 = __toESM(require("react"));
3229
+ var import_remotion36 = require("remotion");
3230
+ var import_zod47 = require("zod");
3231
+ var import_jsx_runtime46 = require("react/jsx-runtime");
3232
+ var WipeTransitionPropsSchema = import_zod47.z.object({
3233
+ durationInFrames: import_zod47.z.number().int().min(10).max(60).default(30),
3234
+ direction: import_zod47.z.enum(["left", "right", "up", "down", "diagonal"]).default("right"),
3235
+ softEdge: import_zod47.z.boolean().default(false),
3236
+ phase: import_zod47.z.enum(["in", "out", "inOut"]).default("inOut")
3237
+ });
3238
+ function clipFor2(direction, p) {
3239
+ if (direction === "left") return `inset(0 ${100 * (1 - p)}% 0 0)`;
3240
+ if (direction === "right") return `inset(0 0 0 ${100 * (1 - p)}%)`;
3241
+ if (direction === "up") return `inset(0 0 ${100 * (1 - p)}% 0)`;
3242
+ if (direction === "down") return `inset(${100 * (1 - p)}% 0 0 0)`;
3243
+ return `polygon(0 0, ${100 * p}% 0, 0 ${100 * p}%)`;
3244
+ }
3245
+ var WipeTransition = ({
3246
+ durationInFrames,
3247
+ direction,
3248
+ softEdge,
3249
+ phase,
3250
+ children,
3251
+ __wavesDurationInFrames
3252
+ }) => {
3253
+ const layers = import_react9.default.Children.toArray(children);
3254
+ const frame = (0, import_remotion36.useCurrentFrame)();
3255
+ const total = Math.max(1, __wavesDurationInFrames ?? 1);
3256
+ const d = Math.min(durationInFrames, total);
3257
+ let clipPath;
3258
+ if (phase === "in" || phase === "inOut") {
3259
+ const t = (0, import_remotion36.interpolate)(frame, [0, d], [0, 1], { extrapolateLeft: "clamp", extrapolateRight: "clamp" });
3260
+ clipPath = clipFor2(direction, t);
3261
+ }
3262
+ if (phase === "out" || phase === "inOut") {
3263
+ const start = Math.max(0, total - d);
3264
+ const t = (0, import_remotion36.interpolate)(frame, [start, total], [1, 0], { extrapolateLeft: "clamp", extrapolateRight: "clamp" });
3265
+ clipPath = clipFor2(direction, t);
3266
+ }
3267
+ return /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(Fill, { style: { clipPath, filter: softEdge ? "blur(0.4px)" : void 0 }, children: layers.map((child, i) => /* @__PURE__ */ (0, import_jsx_runtime46.jsx)("div", { style: { position: "absolute", inset: 0 }, children: child }, i)) });
3268
+ };
3269
+ var WipeTransitionComponentMetadata = {
3270
+ kind: "composite",
3271
+ category: "transition",
3272
+ acceptsChildren: true,
3273
+ minChildren: 1,
3274
+ description: "Directional wipe reveal/hide wrapper transition",
3275
+ llmGuidance: "Use as a more stylized reveal. softEdge can make it feel less harsh."
3276
+ };
3277
+
3278
+ // src/components/composites/YouTubeThumbnail.tsx
3279
+ var import_remotion37 = require("remotion");
3280
+ var import_zod48 = require("zod");
3281
+ var import_jsx_runtime47 = require("react/jsx-runtime");
3282
+ var YouTubeThumbnailPropsSchema = import_zod48.z.object({
3283
+ backgroundImage: import_zod48.z.string().min(1),
3284
+ title: import_zod48.z.string().max(60),
3285
+ subtitle: import_zod48.z.string().max(40).optional(),
3286
+ thumbnailFace: import_zod48.z.string().optional(),
3287
+ accentColor: import_zod48.z.string().default("#FF0000"),
3288
+ style: import_zod48.z.enum(["bold", "minimal", "dramatic"]).default("bold")
3289
+ });
3290
+ var resolveAsset16 = (value) => isRemoteAssetPath(value) ? value : (0, import_remotion37.staticFile)(staticFileInputFromAssetPath(value));
3291
+ var YouTubeThumbnail = ({
3292
+ backgroundImage,
3293
+ title,
3294
+ subtitle,
3295
+ thumbnailFace,
3296
+ accentColor,
3297
+ style
3298
+ }) => {
3299
+ return /* @__PURE__ */ (0, import_jsx_runtime47.jsxs)(Fill, { children: [
3300
+ /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(import_remotion37.Img, { src: resolveAsset16(backgroundImage), style: { width: "100%", height: "100%", objectFit: "cover" } }),
3301
+ style === "dramatic" ? /* @__PURE__ */ (0, import_jsx_runtime47.jsx)("div", { style: { position: "absolute", inset: 0, backgroundColor: "rgba(0,0,0,0.35)" } }) : null,
3302
+ thumbnailFace ? /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(
3303
+ import_remotion37.Img,
3304
+ {
3305
+ src: resolveAsset16(thumbnailFace),
3306
+ style: { position: "absolute", right: 60, bottom: 0, width: 520, height: 720, objectFit: "cover" }
3307
+ }
3308
+ ) : null,
3309
+ /* @__PURE__ */ (0, import_jsx_runtime47.jsxs)("div", { style: { position: "absolute", left: 70, top: 90, width: 1100 }, children: [
3310
+ /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(
3311
+ "div",
3312
+ {
3313
+ style: {
3314
+ fontSize: style === "minimal" ? 86 : 110,
3315
+ fontWeight: 1e3,
3316
+ color: "#FFFFFF",
3317
+ textTransform: "uppercase",
3318
+ lineHeight: 0.95,
3319
+ WebkitTextStroke: style === "minimal" ? "0px transparent" : "10px #000000",
3320
+ textShadow: style === "minimal" ? "0 10px 40px rgba(0,0,0,0.6)" : void 0
3321
+ },
3322
+ children: title
3323
+ }
3324
+ ),
3325
+ subtitle ? /* @__PURE__ */ (0, import_jsx_runtime47.jsx)("div", { style: { marginTop: 26, fontSize: 52, fontWeight: 900, color: accentColor, textShadow: "0 10px 30px rgba(0,0,0,0.6)" }, children: subtitle }) : null
3326
+ ] }),
3327
+ style !== "minimal" ? /* @__PURE__ */ (0, import_jsx_runtime47.jsx)("div", { style: { position: "absolute", left: 70, bottom: 80, width: 260, height: 18, backgroundColor: accentColor, borderRadius: 9999 } }) : null
3328
+ ] });
3329
+ };
3330
+ var YouTubeThumbnailComponentMetadata = {
3331
+ kind: "composite",
3332
+ category: "social",
3333
+ description: "YouTube-style thumbnail layout (16:9) with bold title and optional face cutout",
3334
+ llmGuidance: 'Use 1280x720 video size. Keep title short and high-contrast. style="bold" is classic thumbnail.'
3335
+ };
3336
+
3337
+ // src/components/composites/VideoWithOverlay.tsx
3338
+ var import_remotion38 = require("remotion");
3339
+ var import_zod49 = require("zod");
3340
+ var import_jsx_runtime48 = require("react/jsx-runtime");
3341
+ var OverlaySchema = import_zod49.z.object({
3342
+ type: import_zod49.z.enum(["text", "logo", "gradient"]),
3343
+ content: import_zod49.z.string().optional(),
3344
+ opacity: import_zod49.z.number().min(0).max(1).default(0.3),
3345
+ position: import_zod49.z.enum(["top", "bottom", "center", "full"]).default("bottom")
3346
+ });
3347
+ var VideoWithOverlayPropsSchema = import_zod49.z.object({
3348
+ src: import_zod49.z.string().min(1),
3349
+ overlay: OverlaySchema.optional(),
3350
+ volume: import_zod49.z.number().min(0).max(1).default(1),
3351
+ playbackRate: import_zod49.z.number().min(0.5).max(2).default(1)
3352
+ });
3353
+ var resolveAsset17 = (value) => isRemoteAssetPath(value) ? value : (0, import_remotion38.staticFile)(staticFileInputFromAssetPath(value));
3354
+ var VideoWithOverlay = ({ src, overlay, volume, playbackRate }) => {
3355
+ const frame = (0, import_remotion38.useCurrentFrame)();
3356
+ const fade = (0, import_remotion38.interpolate)(frame, [0, 20], [0, 1], { extrapolateLeft: "clamp", extrapolateRight: "clamp" });
3357
+ const overlayFill = { position: "absolute", inset: 0 };
3358
+ const overlayNode = overlay ? overlay.type === "gradient" ? /* @__PURE__ */ (0, import_jsx_runtime48.jsx)(
3359
+ "div",
3360
+ {
3361
+ style: {
3362
+ ...overlayFill,
3363
+ opacity: overlay.opacity,
3364
+ background: overlay.position === "top" ? "linear-gradient(rgba(0,0,0,0.85), transparent)" : overlay.position === "bottom" ? "linear-gradient(transparent, rgba(0,0,0,0.85))" : "linear-gradient(rgba(0,0,0,0.6), rgba(0,0,0,0.6))"
3365
+ }
3366
+ }
3367
+ ) : overlay.type === "logo" && overlay.content ? /* @__PURE__ */ (0, import_jsx_runtime48.jsx)("div", { style: { ...overlayFill, display: "flex", justifyContent: "center", alignItems: "center", opacity: overlay.opacity }, children: /* @__PURE__ */ (0, import_jsx_runtime48.jsx)(import_remotion38.Img, { src: resolveAsset17(overlay.content), style: { width: 220, height: 220, objectFit: "contain" } }) }) : overlay.type === "text" && overlay.content ? /* @__PURE__ */ (0, import_jsx_runtime48.jsx)("div", { style: { ...overlayFill, display: "flex", justifyContent: "center", alignItems: "center", opacity: overlay.opacity }, children: /* @__PURE__ */ (0, import_jsx_runtime48.jsx)("div", { style: { color: "#FFFFFF", fontSize: 56, fontWeight: 900, textAlign: "center", padding: "0 80px" }, children: overlay.content }) }) : null : null;
3368
+ return /* @__PURE__ */ (0, import_jsx_runtime48.jsxs)(Fill, { children: [
3369
+ /* @__PURE__ */ (0, import_jsx_runtime48.jsx)(
3370
+ import_remotion38.Video,
3371
+ {
3372
+ src: resolveAsset17(src),
3373
+ style: { width: "100%", height: "100%", objectFit: "cover", opacity: fade },
3374
+ muted: volume === 0,
3375
+ playbackRate
3376
+ }
3377
+ ),
3378
+ overlayNode
3379
+ ] });
3380
+ };
3381
+ var VideoWithOverlayComponentMetadata = {
3382
+ kind: "composite",
3383
+ category: "media",
3384
+ description: "Video background with an optional overlay (text/logo/gradient)",
3385
+ llmGuidance: "Use gradient overlay to improve text readability. Set volume=0 to mute."
3386
+ };
3387
+
3388
+ // src/components/composites/ZoomTransition.tsx
3389
+ var import_react10 = __toESM(require("react"));
3390
+ var import_remotion39 = require("remotion");
3391
+ var import_zod50 = require("zod");
3392
+ var import_jsx_runtime49 = require("react/jsx-runtime");
3393
+ var ZoomTransitionPropsSchema = import_zod50.z.object({
3394
+ durationInFrames: import_zod50.z.number().int().min(10).max(60).default(30),
3395
+ type: import_zod50.z.enum(["zoomIn", "zoomOut"]).default("zoomIn"),
3396
+ phase: import_zod50.z.enum(["in", "out", "inOut"]).default("inOut")
3397
+ });
3398
+ var ZoomTransition = ({
3399
+ durationInFrames,
3400
+ type,
3401
+ phase,
3402
+ children,
3403
+ __wavesDurationInFrames
3404
+ }) => {
3405
+ const layers = import_react10.default.Children.toArray(children);
3406
+ const frame = (0, import_remotion39.useCurrentFrame)();
3407
+ const total = Math.max(1, __wavesDurationInFrames ?? 1);
3408
+ const d = Math.min(durationInFrames, total);
3409
+ let opacity = 1;
3410
+ let scale = 1;
3411
+ const enterFrom = type === "zoomIn" ? 1.2 : 0.85;
3412
+ const exitTo = type === "zoomIn" ? 1.25 : 0.75;
3413
+ if (phase === "in" || phase === "inOut") {
3414
+ const t = (0, import_remotion39.interpolate)(frame, [0, d], [0, 1], { extrapolateLeft: "clamp", extrapolateRight: "clamp" });
3415
+ scale *= enterFrom + (1 - enterFrom) * t;
3416
+ opacity *= t;
3417
+ }
3418
+ if (phase === "out" || phase === "inOut") {
3419
+ const start = Math.max(0, total - d);
3420
+ const t = (0, import_remotion39.interpolate)(frame, [start, total], [0, 1], { extrapolateLeft: "clamp", extrapolateRight: "clamp" });
3421
+ scale *= 1 + (exitTo - 1) * t;
3422
+ opacity *= 1 - t;
3423
+ }
3424
+ return /* @__PURE__ */ (0, import_jsx_runtime49.jsx)(Fill, { style: { opacity, transform: `scale(${scale})` }, children: layers.map((child, i) => /* @__PURE__ */ (0, import_jsx_runtime49.jsx)("div", { style: { position: "absolute", inset: 0 }, children: child }, i)) });
3425
+ };
3426
+ var ZoomTransitionComponentMetadata = {
3427
+ kind: "composite",
3428
+ category: "transition",
3429
+ acceptsChildren: true,
3430
+ minChildren: 1,
3431
+ description: "Zoom in/out wrapper transition",
3432
+ llmGuidance: 'Use for energetic cuts. type="zoomIn" feels punchy; type="zoomOut" feels calmer.'
3433
+ };
3434
+
3435
+ // src/components/registry.ts
3436
+ function registerBuiltInComponents() {
3437
+ if (!globalRegistry.has("Segment")) {
3438
+ globalRegistry.register({
3439
+ type: "Segment",
3440
+ component: Segment,
3441
+ propsSchema: SegmentPropsSchema,
3442
+ metadata: SegmentComponentMetadata
3443
+ });
3444
+ }
3445
+ if (!globalRegistry.has("Box")) {
3446
+ globalRegistry.register({
3447
+ type: "Box",
3448
+ component: Box,
3449
+ propsSchema: BoxPropsSchema,
3450
+ metadata: BoxComponentMetadata
3451
+ });
3452
+ }
3453
+ if (!globalRegistry.has("Frame")) {
3454
+ globalRegistry.register({
3455
+ type: "Frame",
3456
+ component: Frame,
3457
+ propsSchema: FramePropsSchema,
3458
+ metadata: FrameComponentMetadata
3459
+ });
3460
+ }
3461
+ if (!globalRegistry.has("Stack")) {
3462
+ globalRegistry.register({
3463
+ type: "Stack",
3464
+ component: Stack,
3465
+ propsSchema: StackPropsSchema,
3466
+ metadata: StackComponentMetadata
3467
+ });
3468
+ }
3469
+ if (!globalRegistry.has("Grid")) {
3470
+ globalRegistry.register({
3471
+ type: "Grid",
3472
+ component: Grid,
3473
+ propsSchema: GridPropsSchema,
3474
+ metadata: GridComponentMetadata
3475
+ });
3476
+ }
3477
+ if (!globalRegistry.has("Layers")) {
3478
+ globalRegistry.register({
3479
+ type: "Layers",
3480
+ component: Layers,
3481
+ propsSchema: LayersPropsSchema,
3482
+ metadata: LayersComponentMetadata
3483
+ });
3484
+ }
3485
+ if (!globalRegistry.has("Layer")) {
3486
+ globalRegistry.register({
3487
+ type: "Layer",
3488
+ component: Layer,
3489
+ propsSchema: LayerPropsSchema,
3490
+ metadata: LayerComponentMetadata
3491
+ });
3492
+ }
3493
+ if (!globalRegistry.has("Image")) {
3494
+ globalRegistry.register({
3495
+ type: "Image",
3496
+ component: Image,
3497
+ propsSchema: ImagePropsSchema,
3498
+ metadata: ImageComponentMetadata
3499
+ });
3500
+ }
3501
+ if (!globalRegistry.has("Video")) {
3502
+ globalRegistry.register({
3503
+ type: "Video",
3504
+ component: Video2,
3505
+ propsSchema: VideoPropsSchema,
3506
+ metadata: VideoComponentMetadata
3507
+ });
3508
+ }
3509
+ if (!globalRegistry.has("Shape")) {
3510
+ globalRegistry.register({
3511
+ type: "Shape",
3512
+ component: Shape,
3513
+ propsSchema: ShapePropsSchema,
3514
+ metadata: ShapeComponentMetadata
3515
+ });
3516
+ }
3517
+ if (!globalRegistry.has("TypewriterText")) {
3518
+ globalRegistry.register({
3519
+ type: "TypewriterText",
3520
+ component: TypewriterText,
3521
+ propsSchema: TypewriterTextPropsSchema,
3522
+ metadata: TypewriterTextComponentMetadata
3523
+ });
3524
+ }
3525
+ if (!globalRegistry.has("SplitText")) {
3526
+ globalRegistry.register({
3527
+ type: "SplitText",
3528
+ component: SplitText,
3529
+ propsSchema: SplitTextPropsSchema,
3530
+ metadata: SplitTextComponentMetadata
3531
+ });
3532
+ }
3533
+ if (!globalRegistry.has("KenBurnsImage")) {
3534
+ globalRegistry.register({
3535
+ type: "KenBurnsImage",
3536
+ component: KenBurnsImage,
3537
+ propsSchema: KenBurnsImagePropsSchema,
3538
+ metadata: KenBurnsImageComponentMetadata
3539
+ });
3540
+ }
3541
+ if (!globalRegistry.has("FadeTransition")) {
3542
+ globalRegistry.register({
3543
+ type: "FadeTransition",
3544
+ component: FadeTransition,
3545
+ propsSchema: FadeTransitionPropsSchema,
3546
+ metadata: FadeTransitionComponentMetadata
3547
+ });
3548
+ }
3549
+ if (!globalRegistry.has("SlideTransition")) {
3550
+ globalRegistry.register({
3551
+ type: "SlideTransition",
3552
+ component: SlideTransition,
3553
+ propsSchema: SlideTransitionPropsSchema,
3554
+ metadata: SlideTransitionComponentMetadata
3555
+ });
3556
+ }
3557
+ if (!globalRegistry.has("SplitScreen")) {
3558
+ globalRegistry.register({
3559
+ type: "SplitScreen",
3560
+ component: SplitScreen,
3561
+ propsSchema: SplitScreenPropsSchema,
3562
+ metadata: SplitScreenComponentMetadata
3563
+ });
3564
+ }
3565
+ if (!globalRegistry.has("ThirdLowerBanner")) {
3566
+ globalRegistry.register({
3567
+ type: "ThirdLowerBanner",
3568
+ component: ThirdLowerBanner,
3569
+ propsSchema: ThirdLowerBannerPropsSchema,
3570
+ metadata: ThirdLowerBannerComponentMetadata
3571
+ });
3572
+ }
3573
+ if (!globalRegistry.has("AnimatedCounter")) {
3574
+ globalRegistry.register({
3575
+ type: "AnimatedCounter",
3576
+ component: AnimatedCounter,
3577
+ propsSchema: AnimatedCounterPropsSchema,
3578
+ metadata: AnimatedCounterComponentMetadata
3579
+ });
3580
+ }
3581
+ if (!globalRegistry.has("LogoReveal")) {
3582
+ globalRegistry.register({
3583
+ type: "LogoReveal",
3584
+ component: LogoReveal,
3585
+ propsSchema: LogoRevealPropsSchema,
3586
+ metadata: LogoRevealComponentMetadata
3587
+ });
3588
+ }
3589
+ if (!globalRegistry.has("Watermark")) {
3590
+ globalRegistry.register({
3591
+ type: "Watermark",
3592
+ component: Watermark,
3593
+ propsSchema: WatermarkPropsSchema,
3594
+ metadata: WatermarkComponentMetadata
3595
+ });
3596
+ }
3597
+ if (!globalRegistry.has("GlitchText")) {
3598
+ globalRegistry.register({
3599
+ type: "GlitchText",
3600
+ component: GlitchText,
3601
+ propsSchema: GlitchTextPropsSchema,
3602
+ metadata: GlitchTextComponentMetadata
3603
+ });
3604
+ }
3605
+ if (!globalRegistry.has("OutlineText")) {
3606
+ globalRegistry.register({
3607
+ type: "OutlineText",
3608
+ component: OutlineText,
3609
+ propsSchema: OutlineTextPropsSchema,
3610
+ metadata: OutlineTextComponentMetadata
3611
+ });
3612
+ }
3613
+ if (!globalRegistry.has("ImageReveal")) {
3614
+ globalRegistry.register({
3615
+ type: "ImageReveal",
3616
+ component: ImageReveal,
3617
+ propsSchema: ImageRevealPropsSchema,
3618
+ metadata: ImageRevealComponentMetadata
3619
+ });
3620
+ }
3621
+ if (!globalRegistry.has("ImageCollage")) {
3622
+ globalRegistry.register({
3623
+ type: "ImageCollage",
3624
+ component: ImageCollage,
3625
+ propsSchema: ImageCollagePropsSchema,
3626
+ metadata: ImageCollageComponentMetadata
3627
+ });
3628
+ }
3629
+ if (!globalRegistry.has("ProgressBar")) {
3630
+ globalRegistry.register({
3631
+ type: "ProgressBar",
3632
+ component: ProgressBar,
3633
+ propsSchema: ProgressBarPropsSchema,
3634
+ metadata: ProgressBarComponentMetadata
3635
+ });
3636
+ }
3637
+ if (!globalRegistry.has("ProgressRing")) {
3638
+ globalRegistry.register({
3639
+ type: "ProgressRing",
3640
+ component: ProgressRing,
3641
+ propsSchema: ProgressRingPropsSchema,
3642
+ metadata: ProgressRingComponentMetadata
3643
+ });
3644
+ }
3645
+ if (!globalRegistry.has("BarChart")) {
3646
+ globalRegistry.register({
3647
+ type: "BarChart",
3648
+ component: BarChart,
3649
+ propsSchema: BarChartPropsSchema,
3650
+ metadata: BarChartComponentMetadata
3651
+ });
3652
+ }
3653
+ if (!globalRegistry.has("InstagramStory")) {
3654
+ globalRegistry.register({
3655
+ type: "InstagramStory",
3656
+ component: InstagramStory,
3657
+ propsSchema: InstagramStoryPropsSchema,
3658
+ metadata: InstagramStoryComponentMetadata
3659
+ });
3660
+ }
3661
+ if (!globalRegistry.has("TikTokCaption")) {
3662
+ globalRegistry.register({
3663
+ type: "TikTokCaption",
3664
+ component: TikTokCaption,
3665
+ propsSchema: TikTokCaptionPropsSchema,
3666
+ metadata: TikTokCaptionComponentMetadata
3667
+ });
3668
+ }
3669
+ if (!globalRegistry.has("IntroScene")) {
3670
+ globalRegistry.register({
3671
+ type: "IntroScene",
3672
+ component: IntroScene,
3673
+ propsSchema: IntroScenePropsSchema,
3674
+ metadata: IntroSceneComponentMetadata
3675
+ });
3676
+ }
3677
+ if (!globalRegistry.has("CountUpText")) {
3678
+ globalRegistry.register({
3679
+ type: "CountUpText",
3680
+ component: CountUpText,
3681
+ propsSchema: CountUpTextPropsSchema,
3682
+ metadata: CountUpTextComponentMetadata
3683
+ });
3684
+ }
3685
+ if (!globalRegistry.has("KineticTypography")) {
3686
+ globalRegistry.register({
3687
+ type: "KineticTypography",
3688
+ component: KineticTypography,
3689
+ propsSchema: KineticTypographyPropsSchema,
3690
+ metadata: KineticTypographyComponentMetadata
3691
+ });
3692
+ }
3693
+ if (!globalRegistry.has("SubtitleText")) {
3694
+ globalRegistry.register({
3695
+ type: "SubtitleText",
3696
+ component: SubtitleText,
3697
+ propsSchema: SubtitleTextPropsSchema,
3698
+ metadata: SubtitleTextComponentMetadata
3699
+ });
3700
+ }
3701
+ if (!globalRegistry.has("ImageSequence")) {
3702
+ globalRegistry.register({
3703
+ type: "ImageSequence",
3704
+ component: ImageSequence,
3705
+ propsSchema: ImageSequencePropsSchema,
3706
+ metadata: ImageSequenceComponentMetadata
3707
+ });
3708
+ }
3709
+ if (!globalRegistry.has("ImageWithCaption")) {
3710
+ globalRegistry.register({
3711
+ type: "ImageWithCaption",
3712
+ component: ImageWithCaption,
3713
+ propsSchema: ImageWithCaptionPropsSchema,
3714
+ metadata: ImageWithCaptionComponentMetadata
3715
+ });
3716
+ }
3717
+ if (!globalRegistry.has("VideoWithOverlay")) {
3718
+ globalRegistry.register({
3719
+ type: "VideoWithOverlay",
3720
+ component: VideoWithOverlay,
3721
+ propsSchema: VideoWithOverlayPropsSchema,
3722
+ metadata: VideoWithOverlayComponentMetadata
3723
+ });
3724
+ }
3725
+ if (!globalRegistry.has("ZoomTransition")) {
3726
+ globalRegistry.register({
3727
+ type: "ZoomTransition",
3728
+ component: ZoomTransition,
3729
+ propsSchema: ZoomTransitionPropsSchema,
3730
+ metadata: ZoomTransitionComponentMetadata
3731
+ });
3732
+ }
3733
+ if (!globalRegistry.has("WipeTransition")) {
3734
+ globalRegistry.register({
3735
+ type: "WipeTransition",
3736
+ component: WipeTransition,
3737
+ propsSchema: WipeTransitionPropsSchema,
3738
+ metadata: WipeTransitionComponentMetadata
3739
+ });
3740
+ }
3741
+ if (!globalRegistry.has("CircularReveal")) {
3742
+ globalRegistry.register({
3743
+ type: "CircularReveal",
3744
+ component: CircularReveal,
3745
+ propsSchema: CircularRevealPropsSchema,
3746
+ metadata: CircularRevealComponentMetadata
3747
+ });
3748
+ }
3749
+ if (!globalRegistry.has("CardStack")) {
3750
+ globalRegistry.register({
3751
+ type: "CardStack",
3752
+ component: CardStack,
3753
+ propsSchema: CardStackPropsSchema,
3754
+ metadata: CardStackComponentMetadata
3755
+ });
3756
+ }
3757
+ if (!globalRegistry.has("GridLayout")) {
3758
+ globalRegistry.register({
3759
+ type: "GridLayout",
3760
+ component: GridLayout,
3761
+ propsSchema: GridLayoutPropsSchema,
3762
+ metadata: GridLayoutComponentMetadata
3763
+ });
3764
+ }
3765
+ if (!globalRegistry.has("LineGraph")) {
3766
+ globalRegistry.register({
3767
+ type: "LineGraph",
3768
+ component: LineGraph,
3769
+ propsSchema: LineGraphPropsSchema,
3770
+ metadata: LineGraphComponentMetadata
3771
+ });
3772
+ }
3773
+ if (!globalRegistry.has("YouTubeThumbnail")) {
3774
+ globalRegistry.register({
3775
+ type: "YouTubeThumbnail",
3776
+ component: YouTubeThumbnail,
3777
+ propsSchema: YouTubeThumbnailPropsSchema,
3778
+ metadata: YouTubeThumbnailComponentMetadata
3779
+ });
3780
+ }
3781
+ if (!globalRegistry.has("TwitterCard")) {
3782
+ globalRegistry.register({
3783
+ type: "TwitterCard",
3784
+ component: TwitterCard,
3785
+ propsSchema: TwitterCardPropsSchema,
3786
+ metadata: TwitterCardComponentMetadata
3787
+ });
3788
+ }
3789
+ if (!globalRegistry.has("OutroScene")) {
3790
+ globalRegistry.register({
3791
+ type: "OutroScene",
3792
+ component: OutroScene,
3793
+ propsSchema: OutroScenePropsSchema,
3794
+ metadata: OutroSceneComponentMetadata
3795
+ });
3796
+ }
3797
+ if (!globalRegistry.has("Scene")) {
3798
+ globalRegistry.register({
3799
+ type: "Scene",
3800
+ component: Scene,
3801
+ propsSchema: ScenePropsSchema,
3802
+ metadata: SceneComponentMetadata
3803
+ });
3804
+ }
3805
+ if (!globalRegistry.has("Text")) {
3806
+ globalRegistry.register({
3807
+ type: "Text",
3808
+ component: Text,
3809
+ propsSchema: TextPropsSchema,
3810
+ metadata: TextComponentMetadata
3811
+ });
3812
+ }
3813
+ if (!globalRegistry.has("Audio")) {
3814
+ globalRegistry.register({
3815
+ type: "Audio",
3816
+ component: Audio,
3817
+ propsSchema: AudioPropsSchema,
3818
+ metadata: AudioComponentMetadata
3819
+ });
3820
+ }
3821
+ }
3822
+
3823
+ // src/llm/prompt.ts
3824
+ var RULES = {
3825
+ timing: [
3826
+ "All timing is in frames (integers).",
3827
+ 'Prefer authoring using "segments" (high-level). Each segment has { durationInFrames, root } and start times are derived by order.',
3828
+ 'Use "timeline" only for advanced cases (explicit timings, overlaps).',
3829
+ "If you provide timing on nodes, timing is { from, durationInFrames } and children timing is relative to the parent duration.",
3830
+ "Total duration must match video.durationInFrames."
3831
+ ],
3832
+ assets: [
3833
+ 'Asset paths must be either full URLs (http:// or https://) or absolute public paths starting with "/".',
3834
+ 'When using absolute public paths ("/assets/..."), the renderer resolves them relative to the --publicDir passed at render time.'
3835
+ ],
3836
+ ids: [
654
3837
  "Every component must have a stable string id.",
655
3838
  "Ids should be unique within the whole IR."
656
3839
  ],
@@ -661,9 +3844,10 @@ var RULES = {
661
3844
  ]
662
3845
  };
663
3846
  function getSystemPrompt(registeredTypes) {
664
- const typesLine = registeredTypes.length ? registeredTypes.join(", ") : "(none)";
3847
+ const maxInline = 40;
3848
+ const typesLine = registeredTypes.length <= maxInline ? registeredTypes.length ? registeredTypes.join(", ") : "(none)" : `${registeredTypes.length} types (see schemas.components keys). Example: ${registeredTypes.slice(0, 20).join(", ")}, ...`;
665
3849
  return [
666
- "You generate JSON for @depths/waves Video IR (version 1.0).",
3850
+ "You generate JSON for @depths/waves Video IR (version 2.0).",
667
3851
  "",
668
3852
  "Goal:",
669
3853
  "- Produce a single JSON object that conforms to the provided Video IR JSON Schema and uses only registered component types.",
@@ -684,14 +3868,35 @@ function getSystemPrompt(registeredTypes) {
684
3868
  function getPromptPayload(options) {
685
3869
  registerBuiltInComponents();
686
3870
  const registry = options?.registry ?? globalRegistry;
687
- const types = registry.getTypes().sort();
3871
+ const types = registry.getTypesForLLM().sort();
3872
+ const categories = {};
3873
+ const items = [];
3874
+ for (const t of types) {
3875
+ const reg = registry.get(t);
3876
+ if (!reg) continue;
3877
+ const cat = reg.metadata.category;
3878
+ if (!categories[cat]) categories[cat] = [];
3879
+ categories[cat].push(t);
3880
+ const llmGuidance = reg.metadata.llmGuidance;
3881
+ items.push({
3882
+ type: t,
3883
+ kind: reg.metadata.kind,
3884
+ category: cat,
3885
+ description: reg.metadata.description,
3886
+ ...typeof llmGuidance === "string" ? { llmGuidance } : {}
3887
+ });
3888
+ }
688
3889
  return {
689
3890
  package: "@depths/waves",
690
3891
  version: __wavesVersion,
691
- irVersion: "1.0",
3892
+ irVersion: "2.0",
692
3893
  systemPrompt: getSystemPrompt(types),
3894
+ catalog: {
3895
+ categories,
3896
+ items
3897
+ },
693
3898
  schemas: {
694
- videoIR: zodSchemaToJsonSchema(VideoIRSchema),
3899
+ videoIR: zodSchemaToJsonSchema(VideoIRv2AuthoringSchema),
695
3900
  components: registry.getJSONSchemaForLLM()
696
3901
  },
697
3902
  rules: {
@@ -706,13 +3911,12 @@ function getPromptPayload(options) {
706
3911
  // src/index.ts
707
3912
  async function renderVideo(ir, options) {
708
3913
  registerBuiltInComponents();
709
- const engine = new WavesEngine(globalRegistry, new IRValidator());
3914
+ const engine = new WavesEngine(globalRegistry, new IRValidator(globalRegistry));
710
3915
  await engine.render(ir, options);
711
3916
  }
712
3917
  // Annotate the CommonJS export names for ESM import in node:
713
3918
  0 && (module.exports = {
714
3919
  ComponentRegistry,
715
- ComponentSchemas,
716
3920
  IRValidator,
717
3921
  VideoIRSchema,
718
3922
  WavesEngine,