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