@contractspec/lib.video-gen 1.42.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (137) hide show
  1. package/dist/browser/compositions/api-overview.js +645 -0
  2. package/dist/browser/compositions/index.js +1133 -0
  3. package/dist/browser/compositions/primitives/animated-text.js +144 -0
  4. package/dist/browser/compositions/primitives/brand-frame.js +181 -0
  5. package/dist/browser/compositions/primitives/code-block.js +226 -0
  6. package/dist/browser/compositions/primitives/index.js +656 -0
  7. package/dist/browser/compositions/primitives/progress-bar.js +59 -0
  8. package/dist/browser/compositions/primitives/terminal.js +265 -0
  9. package/dist/browser/compositions/primitives/transition.js +98 -0
  10. package/dist/browser/compositions/social-clip.js +500 -0
  11. package/dist/browser/compositions/terminal-demo.js +558 -0
  12. package/dist/browser/design/index.js +155 -0
  13. package/dist/browser/design/layouts.js +50 -0
  14. package/dist/browser/design/motion.js +43 -0
  15. package/dist/browser/design/tokens.js +28 -0
  16. package/dist/browser/design/typography.js +61 -0
  17. package/dist/browser/docs/compositions.docblock.js +182 -0
  18. package/dist/browser/docs/design.docblock.js +187 -0
  19. package/dist/browser/docs/generators.docblock.js +187 -0
  20. package/dist/browser/docs/rendering.docblock.js +197 -0
  21. package/dist/browser/docs/video-gen.docblock.js +141 -0
  22. package/dist/browser/generators/index.js +416 -0
  23. package/dist/browser/generators/scene-planner.js +205 -0
  24. package/dist/browser/generators/script-generator.js +147 -0
  25. package/dist/browser/generators/video-generator.js +414 -0
  26. package/dist/browser/index.js +1550 -0
  27. package/dist/browser/player/demo-player.js +1136 -0
  28. package/dist/browser/player/index.js +1136 -0
  29. package/dist/browser/remotion/Root.js +1189 -0
  30. package/dist/browser/remotion/index.js +1190 -0
  31. package/dist/browser/renderers/config.js +40 -0
  32. package/dist/browser/renderers/index.js +160 -0
  33. package/dist/browser/renderers/local.js +156 -0
  34. package/dist/browser/types.js +13 -0
  35. package/dist/compositions/api-overview.d.ts +16 -0
  36. package/dist/compositions/api-overview.js +640 -0
  37. package/dist/compositions/index.d.ts +7 -0
  38. package/dist/compositions/index.js +1128 -0
  39. package/dist/compositions/primitives/animated-text.d.ts +22 -0
  40. package/dist/compositions/primitives/animated-text.js +139 -0
  41. package/dist/compositions/primitives/brand-frame.d.ts +14 -0
  42. package/dist/compositions/primitives/brand-frame.js +176 -0
  43. package/dist/compositions/primitives/code-block.d.ts +18 -0
  44. package/dist/compositions/primitives/code-block.js +221 -0
  45. package/dist/compositions/primitives/index.d.ts +12 -0
  46. package/dist/compositions/primitives/index.js +651 -0
  47. package/dist/compositions/primitives/progress-bar.d.ts +12 -0
  48. package/dist/compositions/primitives/progress-bar.js +54 -0
  49. package/dist/compositions/primitives/terminal.d.ts +24 -0
  50. package/dist/compositions/primitives/terminal.js +260 -0
  51. package/dist/compositions/primitives/transition.d.ts +14 -0
  52. package/dist/compositions/primitives/transition.js +93 -0
  53. package/dist/compositions/social-clip.d.ts +16 -0
  54. package/dist/compositions/social-clip.js +495 -0
  55. package/dist/compositions/terminal-demo.d.ts +17 -0
  56. package/dist/compositions/terminal-demo.js +553 -0
  57. package/dist/design/index.d.ts +4 -0
  58. package/dist/design/index.js +150 -0
  59. package/dist/design/layouts.d.ts +69 -0
  60. package/dist/design/layouts.js +45 -0
  61. package/dist/design/motion.d.ts +72 -0
  62. package/dist/design/motion.js +38 -0
  63. package/dist/design/tokens.d.ts +31 -0
  64. package/dist/design/tokens.js +23 -0
  65. package/dist/design/typography.d.ts +61 -0
  66. package/dist/design/typography.js +56 -0
  67. package/dist/docs/compositions.docblock.d.ts +1 -0
  68. package/dist/docs/compositions.docblock.js +183 -0
  69. package/dist/docs/design.docblock.d.ts +1 -0
  70. package/dist/docs/design.docblock.js +188 -0
  71. package/dist/docs/generators.docblock.d.ts +1 -0
  72. package/dist/docs/generators.docblock.js +188 -0
  73. package/dist/docs/rendering.docblock.d.ts +1 -0
  74. package/dist/docs/rendering.docblock.js +198 -0
  75. package/dist/docs/video-gen.docblock.d.ts +1 -0
  76. package/dist/docs/video-gen.docblock.js +142 -0
  77. package/dist/generators/index.d.ts +5 -0
  78. package/dist/generators/index.js +411 -0
  79. package/dist/generators/scene-planner.d.ts +23 -0
  80. package/dist/generators/scene-planner.js +200 -0
  81. package/dist/generators/script-generator.d.ts +49 -0
  82. package/dist/generators/script-generator.js +142 -0
  83. package/dist/generators/video-generator.d.ts +20 -0
  84. package/dist/generators/video-generator.js +409 -0
  85. package/dist/index.d.ts +6 -0
  86. package/dist/index.js +1545 -0
  87. package/dist/node/compositions/api-overview.js +640 -0
  88. package/dist/node/compositions/index.js +1128 -0
  89. package/dist/node/compositions/primitives/animated-text.js +139 -0
  90. package/dist/node/compositions/primitives/brand-frame.js +176 -0
  91. package/dist/node/compositions/primitives/code-block.js +221 -0
  92. package/dist/node/compositions/primitives/index.js +651 -0
  93. package/dist/node/compositions/primitives/progress-bar.js +54 -0
  94. package/dist/node/compositions/primitives/terminal.js +260 -0
  95. package/dist/node/compositions/primitives/transition.js +93 -0
  96. package/dist/node/compositions/social-clip.js +495 -0
  97. package/dist/node/compositions/terminal-demo.js +553 -0
  98. package/dist/node/design/index.js +150 -0
  99. package/dist/node/design/layouts.js +45 -0
  100. package/dist/node/design/motion.js +38 -0
  101. package/dist/node/design/tokens.js +23 -0
  102. package/dist/node/design/typography.js +56 -0
  103. package/dist/node/docs/compositions.docblock.js +182 -0
  104. package/dist/node/docs/design.docblock.js +187 -0
  105. package/dist/node/docs/generators.docblock.js +187 -0
  106. package/dist/node/docs/rendering.docblock.js +197 -0
  107. package/dist/node/docs/video-gen.docblock.js +141 -0
  108. package/dist/node/generators/index.js +411 -0
  109. package/dist/node/generators/scene-planner.js +200 -0
  110. package/dist/node/generators/script-generator.js +142 -0
  111. package/dist/node/generators/video-generator.js +409 -0
  112. package/dist/node/index.js +1545 -0
  113. package/dist/node/player/demo-player.js +1131 -0
  114. package/dist/node/player/index.js +1131 -0
  115. package/dist/node/remotion/Root.js +1184 -0
  116. package/dist/node/remotion/index.js +1185 -0
  117. package/dist/node/renderers/config.js +35 -0
  118. package/dist/node/renderers/index.js +155 -0
  119. package/dist/node/renderers/local.js +151 -0
  120. package/dist/node/types.js +8 -0
  121. package/dist/player/demo-player.d.ts +55 -0
  122. package/dist/player/demo-player.js +1131 -0
  123. package/dist/player/index.d.ts +2 -0
  124. package/dist/player/index.js +1131 -0
  125. package/dist/remotion/Root.d.ts +2 -0
  126. package/dist/remotion/Root.js +1184 -0
  127. package/dist/remotion/index.d.ts +1 -0
  128. package/dist/remotion/index.js +1185 -0
  129. package/dist/renderers/config.d.ts +28 -0
  130. package/dist/renderers/config.js +35 -0
  131. package/dist/renderers/index.d.ts +3 -0
  132. package/dist/renderers/index.js +155 -0
  133. package/dist/renderers/local.d.ts +17 -0
  134. package/dist/renderers/local.js +151 -0
  135. package/dist/types.d.ts +63 -0
  136. package/dist/types.js +8 -0
  137. package/package.json +637 -0
@@ -0,0 +1,1550 @@
1
+ var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
2
+ get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
3
+ }) : x)(function(x) {
4
+ if (typeof require !== "undefined")
5
+ return require.apply(this, arguments);
6
+ throw Error('Dynamic require of "' + x + '" is not supported');
7
+ });
8
+
9
+ // src/design/motion.ts
10
+ import { Easing } from "remotion";
11
+ var videoEasing = {
12
+ entrance: Easing.out(Easing.exp),
13
+ exit: Easing.in(Easing.exp),
14
+ emphasis: Easing.out(Easing.back(1.4)),
15
+ linear: Easing.linear,
16
+ gentle: Easing.bezier(0.25, 0.1, 0.25, 1),
17
+ spring: Easing.out(Easing.back(1.7))
18
+ };
19
+ var videoDurations = {
20
+ sceneTransition: 20,
21
+ textEntrance: 15,
22
+ textExit: 12,
23
+ codeTypingPerChar: 2,
24
+ sectionPause: 30,
25
+ emphasisPause: 15,
26
+ brandReveal: 25,
27
+ minScene: 60,
28
+ shortScene: 60,
29
+ mediumScene: 120,
30
+ longScene: 240
31
+ };
32
+ var videoTransitions = {
33
+ fade: { type: "fade", durationInFrames: 20 },
34
+ slideLeft: { type: "slide-left", durationInFrames: 20 },
35
+ slideRight: { type: "slide-right", durationInFrames: 20 },
36
+ wipe: { type: "wipe", durationInFrames: 15 },
37
+ none: { type: "none", durationInFrames: 0 }
38
+ };
39
+
40
+ // src/design/tokens.ts
41
+ import { defaultTokens } from "@contractspec/lib.design-system";
42
+ var defaultVideoColors = {
43
+ canvasBackground: defaultTokens.colors.background,
44
+ codeBackground: "#1e1e2e",
45
+ terminalBackground: "#0d1117",
46
+ terminalForeground: "#c9d1d9",
47
+ highlight: defaultTokens.colors.accent,
48
+ gradientStart: defaultTokens.colors.primary,
49
+ gradientEnd: defaultTokens.colors.accent
50
+ };
51
+ var defaultVideoTheme = {
52
+ ...defaultTokens,
53
+ video: defaultVideoColors
54
+ };
55
+
56
+ // src/design/layouts.ts
57
+ import { VIDEO_FORMATS } from "@contractspec/lib.contracts-integrations/integrations/providers/video";
58
+ var DEFAULT_FPS = 30;
59
+ var videoSafeZone = {
60
+ horizontal: 120,
61
+ vertical: 80,
62
+ contentWidth: 1680,
63
+ contentHeight: 920
64
+ };
65
+ function scaleSafeZone(format) {
66
+ const scaleX = format.width / 1920;
67
+ const scaleY = format.height / 1080;
68
+ return {
69
+ horizontal: Math.round(videoSafeZone.horizontal * scaleX),
70
+ vertical: Math.round(videoSafeZone.vertical * scaleY),
71
+ contentWidth: Math.round(videoSafeZone.contentWidth * scaleX),
72
+ contentHeight: Math.round(videoSafeZone.contentHeight * scaleY)
73
+ };
74
+ }
75
+ var videoPositions = {
76
+ center: { x: 960, y: 540 },
77
+ topLeft: { x: 120, y: 80 },
78
+ topRight: { x: 1800, y: 80 },
79
+ bottomLeft: { x: 120, y: 1000 },
80
+ bottomRight: { x: 1800, y: 1000 },
81
+ bottomCenter: { x: 960, y: 960 }
82
+ };
83
+ function getAllFormatVariants() {
84
+ return [
85
+ VIDEO_FORMATS.landscape,
86
+ VIDEO_FORMATS.square,
87
+ VIDEO_FORMATS.portrait
88
+ ];
89
+ }
90
+
91
+ // src/compositions/primitives/brand-frame.tsx
92
+ import { interpolate, useCurrentFrame, useVideoConfig } from "remotion";
93
+ import { jsxDEV } from "react/jsx-dev-runtime";
94
+ var BrandFrame = ({
95
+ styleOverrides,
96
+ showBranding = true,
97
+ animateEntrance = true,
98
+ variant = "gradient",
99
+ children
100
+ }) => {
101
+ const frame = useCurrentFrame();
102
+ const { width, height } = useVideoConfig();
103
+ const theme = defaultVideoTheme;
104
+ const safeZone = scaleSafeZone({ type: "custom", width, height });
105
+ const primaryColor = styleOverrides?.primaryColor ?? theme.colors.primary;
106
+ const _accentColor = styleOverrides?.accentColor ?? theme.colors.accent;
107
+ const isDark = styleOverrides?.darkMode ?? true;
108
+ let background;
109
+ switch (variant) {
110
+ case "solid":
111
+ background = isDark ? "#0a0a0a" : theme.colors.background;
112
+ break;
113
+ case "gradient":
114
+ background = isDark ? `linear-gradient(135deg, #0a0a14 0%, #0f172a 50%, #0a0a14 100%)` : `linear-gradient(135deg, ${theme.colors.background} 0%, ${theme.colors.muted} 100%)`;
115
+ break;
116
+ case "dark":
117
+ background = "#000000";
118
+ break;
119
+ }
120
+ const entranceOpacity = animateEntrance ? interpolate(frame, [0, 15], [0, 1], {
121
+ extrapolateLeft: "clamp",
122
+ extrapolateRight: "clamp",
123
+ easing: videoEasing.entrance
124
+ }) : 1;
125
+ const brandOpacity = showBranding ? interpolate(frame, [videoDurations.brandReveal, videoDurations.brandReveal + 15], [0, 0.3], {
126
+ extrapolateLeft: "clamp",
127
+ extrapolateRight: "clamp"
128
+ }) : 0;
129
+ return /* @__PURE__ */ jsxDEV("div", {
130
+ style: {
131
+ width,
132
+ height,
133
+ background,
134
+ position: "relative",
135
+ overflow: "hidden",
136
+ opacity: entranceOpacity
137
+ },
138
+ children: [
139
+ variant === "gradient" && /* @__PURE__ */ jsxDEV("div", {
140
+ style: {
141
+ position: "absolute",
142
+ top: "-20%",
143
+ right: "-10%",
144
+ width: "50%",
145
+ height: "50%",
146
+ background: `radial-gradient(circle, ${primaryColor}15 0%, transparent 70%)`,
147
+ borderRadius: "50%",
148
+ pointerEvents: "none"
149
+ }
150
+ }, undefined, false, undefined, this),
151
+ /* @__PURE__ */ jsxDEV("div", {
152
+ style: {
153
+ position: "absolute",
154
+ left: safeZone.horizontal,
155
+ top: safeZone.vertical,
156
+ width: safeZone.contentWidth,
157
+ height: safeZone.contentHeight,
158
+ display: "flex",
159
+ flexDirection: "column"
160
+ },
161
+ children
162
+ }, undefined, false, undefined, this),
163
+ showBranding && /* @__PURE__ */ jsxDEV("div", {
164
+ style: {
165
+ position: "absolute",
166
+ bottom: safeZone.vertical / 2,
167
+ right: safeZone.horizontal,
168
+ opacity: brandOpacity,
169
+ color: isDark ? "#ffffff" : "#000000",
170
+ fontSize: Math.round(16 * (width / 1920)),
171
+ fontWeight: 500,
172
+ letterSpacing: 1
173
+ },
174
+ children: "ContractSpec"
175
+ }, undefined, false, undefined, this)
176
+ ]
177
+ }, undefined, true, undefined, this);
178
+ };
179
+
180
+ // src/design/typography.ts
181
+ var videoTypography = {
182
+ title: {
183
+ fontSize: 72,
184
+ lineHeight: 1.1,
185
+ fontWeight: 700,
186
+ letterSpacing: -1
187
+ },
188
+ heading: {
189
+ fontSize: 56,
190
+ lineHeight: 1.2,
191
+ fontWeight: 600,
192
+ letterSpacing: -0.5
193
+ },
194
+ subheading: {
195
+ fontSize: 40,
196
+ lineHeight: 1.25,
197
+ fontWeight: 500
198
+ },
199
+ body: {
200
+ fontSize: 32,
201
+ lineHeight: 1.5,
202
+ fontWeight: 400
203
+ },
204
+ code: {
205
+ fontSize: 28,
206
+ lineHeight: 1.6,
207
+ fontWeight: 400
208
+ },
209
+ caption: {
210
+ fontSize: 24,
211
+ lineHeight: 1.4,
212
+ fontWeight: 400
213
+ },
214
+ label: {
215
+ fontSize: 20,
216
+ lineHeight: 1.3,
217
+ fontWeight: 600,
218
+ letterSpacing: 1
219
+ }
220
+ };
221
+ function scaleTypography(style, targetWidth) {
222
+ const scale = targetWidth / 1920;
223
+ return {
224
+ ...style,
225
+ fontSize: Math.round(style.fontSize * scale),
226
+ letterSpacing: style.letterSpacing ? Math.round(style.letterSpacing * scale * 10) / 10 : undefined
227
+ };
228
+ }
229
+
230
+ // src/compositions/primitives/animated-text.tsx
231
+ import { interpolate as interpolate2, useCurrentFrame as useCurrentFrame2, useVideoConfig as useVideoConfig2 } from "remotion";
232
+ import { jsxDEV as jsxDEV2 } from "react/jsx-dev-runtime";
233
+ var AnimatedText = ({
234
+ text,
235
+ variant = "body",
236
+ style: styleOverride,
237
+ enterAt = 0,
238
+ exitAt,
239
+ color = "#ffffff",
240
+ align = "left"
241
+ }) => {
242
+ const frame = useCurrentFrame2();
243
+ const { width } = useVideoConfig2();
244
+ const baseStyle = videoTypography[variant];
245
+ const scaled = scaleTypography(baseStyle, width);
246
+ const finalStyle = { ...scaled, ...styleOverride };
247
+ const enterProgress = interpolate2(frame, [enterAt, enterAt + videoDurations.textEntrance], [0, 1], { extrapolateLeft: "clamp", extrapolateRight: "clamp" });
248
+ const enterOpacity = interpolate2(enterProgress, [0, 1], [0, 1], {
249
+ easing: videoEasing.entrance
250
+ });
251
+ const enterTranslateY = interpolate2(enterProgress, [0, 1], [30, 0], {
252
+ easing: videoEasing.entrance
253
+ });
254
+ let exitOpacity = 1;
255
+ let exitTranslateY = 0;
256
+ if (exitAt !== undefined) {
257
+ const exitProgress = interpolate2(frame, [exitAt, exitAt + videoDurations.textExit], [0, 1], { extrapolateLeft: "clamp", extrapolateRight: "clamp" });
258
+ exitOpacity = interpolate2(exitProgress, [0, 1], [1, 0], {
259
+ easing: videoEasing.exit
260
+ });
261
+ exitTranslateY = interpolate2(exitProgress, [0, 1], [0, -20], {
262
+ easing: videoEasing.exit
263
+ });
264
+ }
265
+ const opacity = enterOpacity * exitOpacity;
266
+ const translateY = enterTranslateY + exitTranslateY;
267
+ return /* @__PURE__ */ jsxDEV2("div", {
268
+ style: {
269
+ fontSize: finalStyle.fontSize,
270
+ lineHeight: finalStyle.lineHeight,
271
+ fontWeight: finalStyle.fontWeight,
272
+ letterSpacing: finalStyle.letterSpacing,
273
+ color,
274
+ textAlign: align,
275
+ opacity,
276
+ transform: `translateY(${translateY}px)`,
277
+ willChange: "opacity, transform"
278
+ },
279
+ children: text
280
+ }, undefined, false, undefined, this);
281
+ };
282
+
283
+ // src/compositions/primitives/code-block.tsx
284
+ import { interpolate as interpolate3, useCurrentFrame as useCurrentFrame3, useVideoConfig as useVideoConfig3 } from "remotion";
285
+ import { jsxDEV as jsxDEV3 } from "react/jsx-dev-runtime";
286
+ var CodeBlock = ({
287
+ code,
288
+ language = "typescript",
289
+ startAt = 0,
290
+ typeAnimation = true,
291
+ backgroundColor = defaultVideoColors.codeBackground,
292
+ textColor = "#abb2bf",
293
+ filename
294
+ }) => {
295
+ const frame = useCurrentFrame3();
296
+ const { width } = useVideoConfig3();
297
+ const codeStyle = scaleTypography(videoTypography.code, width);
298
+ const totalChars = code.length;
299
+ const typingDuration = totalChars * videoDurations.codeTypingPerChar;
300
+ const charsVisible = typeAnimation ? Math.floor(interpolate3(frame, [startAt, startAt + typingDuration], [0, totalChars], {
301
+ extrapolateLeft: "clamp",
302
+ extrapolateRight: "clamp"
303
+ })) : totalChars;
304
+ const visibleCode = code.slice(0, charsVisible);
305
+ const opacity = interpolate3(frame, [startAt, startAt + 10], [0, 1], {
306
+ extrapolateLeft: "clamp",
307
+ extrapolateRight: "clamp",
308
+ easing: videoEasing.entrance
309
+ });
310
+ const showCursor = typeAnimation && charsVisible < totalChars && frame % 16 < 10;
311
+ return /* @__PURE__ */ jsxDEV3("div", {
312
+ style: {
313
+ backgroundColor,
314
+ borderRadius: 16,
315
+ padding: 0,
316
+ opacity,
317
+ overflow: "hidden",
318
+ boxShadow: "0 8px 32px rgba(0,0,0,0.3)"
319
+ },
320
+ children: [
321
+ /* @__PURE__ */ jsxDEV3("div", {
322
+ style: {
323
+ display: "flex",
324
+ alignItems: "center",
325
+ padding: "12px 20px",
326
+ backgroundColor: "rgba(0,0,0,0.2)",
327
+ gap: 8
328
+ },
329
+ children: [
330
+ /* @__PURE__ */ jsxDEV3("div", {
331
+ style: { display: "flex", gap: 8 },
332
+ children: [
333
+ /* @__PURE__ */ jsxDEV3("div", {
334
+ style: {
335
+ width: 12,
336
+ height: 12,
337
+ borderRadius: "50%",
338
+ backgroundColor: "#ff5f57"
339
+ }
340
+ }, undefined, false, undefined, this),
341
+ /* @__PURE__ */ jsxDEV3("div", {
342
+ style: {
343
+ width: 12,
344
+ height: 12,
345
+ borderRadius: "50%",
346
+ backgroundColor: "#febc2e"
347
+ }
348
+ }, undefined, false, undefined, this),
349
+ /* @__PURE__ */ jsxDEV3("div", {
350
+ style: {
351
+ width: 12,
352
+ height: 12,
353
+ borderRadius: "50%",
354
+ backgroundColor: "#28c840"
355
+ }
356
+ }, undefined, false, undefined, this)
357
+ ]
358
+ }, undefined, true, undefined, this),
359
+ /* @__PURE__ */ jsxDEV3("div", {
360
+ style: {
361
+ flex: 1,
362
+ textAlign: "center",
363
+ color: "#666",
364
+ fontSize: codeStyle.fontSize * 0.7,
365
+ fontFamily: "monospace"
366
+ },
367
+ children: filename ?? language
368
+ }, undefined, false, undefined, this)
369
+ ]
370
+ }, undefined, true, undefined, this),
371
+ /* @__PURE__ */ jsxDEV3("div", {
372
+ style: { padding: "24px 32px" },
373
+ children: /* @__PURE__ */ jsxDEV3("pre", {
374
+ style: {
375
+ margin: 0,
376
+ fontFamily: "'SF Mono', 'Fira Code', 'JetBrains Mono', monospace",
377
+ fontSize: codeStyle.fontSize,
378
+ lineHeight: codeStyle.lineHeight,
379
+ color: textColor,
380
+ whiteSpace: "pre-wrap",
381
+ wordBreak: "break-word"
382
+ },
383
+ children: [
384
+ visibleCode,
385
+ showCursor && /* @__PURE__ */ jsxDEV3("span", {
386
+ style: {
387
+ backgroundColor: textColor,
388
+ width: "2px",
389
+ display: "inline-block",
390
+ height: "1.2em",
391
+ verticalAlign: "text-bottom"
392
+ },
393
+ children: " "
394
+ }, undefined, false, undefined, this)
395
+ ]
396
+ }, undefined, true, undefined, this)
397
+ }, undefined, false, undefined, this)
398
+ ]
399
+ }, undefined, true, undefined, this);
400
+ };
401
+
402
+ // src/compositions/primitives/progress-bar.tsx
403
+ import { useCurrentFrame as useCurrentFrame4, useVideoConfig as useVideoConfig4 } from "remotion";
404
+ import { jsxDEV as jsxDEV4 } from "react/jsx-dev-runtime";
405
+ var ProgressBar = ({
406
+ height = 4,
407
+ color = defaultVideoTheme.colors.primary,
408
+ backgroundColor = "rgba(255,255,255,0.1)",
409
+ position = "bottom"
410
+ }) => {
411
+ const frame = useCurrentFrame4();
412
+ const { durationInFrames, width } = useVideoConfig4();
413
+ const progress = frame / durationInFrames;
414
+ return /* @__PURE__ */ jsxDEV4("div", {
415
+ style: {
416
+ position: "absolute",
417
+ [position]: 0,
418
+ left: 0,
419
+ width,
420
+ height,
421
+ backgroundColor,
422
+ zIndex: 100
423
+ },
424
+ children: /* @__PURE__ */ jsxDEV4("div", {
425
+ style: {
426
+ width: `${progress * 100}%`,
427
+ height: "100%",
428
+ backgroundColor: color,
429
+ transition: "none"
430
+ }
431
+ }, undefined, false, undefined, this)
432
+ }, undefined, false, undefined, this);
433
+ };
434
+
435
+ // src/compositions/api-overview.tsx
436
+ import {
437
+ AbsoluteFill,
438
+ Sequence,
439
+ interpolate as interpolate4,
440
+ useCurrentFrame as useCurrentFrame5,
441
+ useVideoConfig as useVideoConfig5
442
+ } from "remotion";
443
+ import { jsxDEV as jsxDEV5 } from "react/jsx-dev-runtime";
444
+ var ApiOverview = ({
445
+ specName,
446
+ method = "POST",
447
+ endpoint = "/api/users",
448
+ specCode,
449
+ generatedOutputs = [
450
+ "REST Endpoint",
451
+ "GraphQL Mutation",
452
+ "Prisma Model",
453
+ "TypeScript SDK",
454
+ "MCP Tool",
455
+ "OpenAPI Spec"
456
+ ],
457
+ tagline = "One spec. Every surface."
458
+ }) => {
459
+ const { durationInFrames } = useVideoConfig5();
460
+ const theme = defaultVideoTheme;
461
+ const INTRO_END = 60;
462
+ const CODE_START = 45;
463
+ const CODE_END = INTRO_END + 150;
464
+ const OUTPUTS_START = CODE_END - 30;
465
+ const TAGLINE_START = durationInFrames - 90;
466
+ return /* @__PURE__ */ jsxDEV5(AbsoluteFill, {
467
+ children: [
468
+ /* @__PURE__ */ jsxDEV5(BrandFrame, {
469
+ variant: "gradient",
470
+ showBranding: true,
471
+ children: [
472
+ /* @__PURE__ */ jsxDEV5(Sequence, {
473
+ from: 0,
474
+ durationInFrames: CODE_END,
475
+ children: /* @__PURE__ */ jsxDEV5("div", {
476
+ style: {
477
+ flex: 1,
478
+ display: "flex",
479
+ flexDirection: "column",
480
+ gap: 16
481
+ },
482
+ children: [
483
+ /* @__PURE__ */ jsxDEV5("div", {
484
+ style: { display: "flex", alignItems: "center", gap: 16 },
485
+ children: [
486
+ /* @__PURE__ */ jsxDEV5(MethodBadge, {
487
+ method,
488
+ enterAt: 10
489
+ }, undefined, false, undefined, this),
490
+ /* @__PURE__ */ jsxDEV5(AnimatedText, {
491
+ text: endpoint,
492
+ variant: "subheading",
493
+ enterAt: 15,
494
+ color: theme.colors.mutedForeground
495
+ }, undefined, false, undefined, this)
496
+ ]
497
+ }, undefined, true, undefined, this),
498
+ /* @__PURE__ */ jsxDEV5(AnimatedText, {
499
+ text: specName,
500
+ variant: "title",
501
+ enterAt: 5,
502
+ color: "#ffffff"
503
+ }, undefined, false, undefined, this),
504
+ /* @__PURE__ */ jsxDEV5("div", {
505
+ style: { flex: 1, marginTop: 24 },
506
+ children: /* @__PURE__ */ jsxDEV5(CodeBlock, {
507
+ code: specCode,
508
+ language: "typescript",
509
+ filename: `${specName.toLowerCase()}.contract.ts`,
510
+ startAt: CODE_START,
511
+ typeAnimation: true
512
+ }, undefined, false, undefined, this)
513
+ }, undefined, false, undefined, this)
514
+ ]
515
+ }, undefined, true, undefined, this)
516
+ }, undefined, false, undefined, this),
517
+ /* @__PURE__ */ jsxDEV5(Sequence, {
518
+ from: OUTPUTS_START,
519
+ durationInFrames: durationInFrames - OUTPUTS_START,
520
+ children: /* @__PURE__ */ jsxDEV5("div", {
521
+ style: {
522
+ flex: 1,
523
+ display: "flex",
524
+ flexDirection: "column",
525
+ justifyContent: "center",
526
+ alignItems: "center",
527
+ gap: 24
528
+ },
529
+ children: [
530
+ /* @__PURE__ */ jsxDEV5(AnimatedText, {
531
+ text: "Generates:",
532
+ variant: "heading",
533
+ enterAt: 0,
534
+ color: "#ffffff",
535
+ align: "center"
536
+ }, undefined, false, undefined, this),
537
+ /* @__PURE__ */ jsxDEV5("div", {
538
+ style: {
539
+ display: "flex",
540
+ flexWrap: "wrap",
541
+ gap: 16,
542
+ justifyContent: "center",
543
+ maxWidth: "80%",
544
+ marginTop: 32
545
+ },
546
+ children: generatedOutputs.map((output, i) => /* @__PURE__ */ jsxDEV5(OutputChip, {
547
+ label: output,
548
+ index: i,
549
+ startFrame: 20 + i * 8
550
+ }, output, false, undefined, this))
551
+ }, undefined, false, undefined, this),
552
+ /* @__PURE__ */ jsxDEV5(Sequence, {
553
+ from: TAGLINE_START - OUTPUTS_START,
554
+ children: /* @__PURE__ */ jsxDEV5("div", {
555
+ style: { marginTop: 48 },
556
+ children: /* @__PURE__ */ jsxDEV5(AnimatedText, {
557
+ text: tagline,
558
+ variant: "heading",
559
+ enterAt: 0,
560
+ color: theme.colors.accent,
561
+ align: "center"
562
+ }, undefined, false, undefined, this)
563
+ }, undefined, false, undefined, this)
564
+ }, undefined, false, undefined, this)
565
+ ]
566
+ }, undefined, true, undefined, this)
567
+ }, undefined, false, undefined, this)
568
+ ]
569
+ }, undefined, true, undefined, this),
570
+ /* @__PURE__ */ jsxDEV5(ProgressBar, {}, undefined, false, undefined, this)
571
+ ]
572
+ }, undefined, true, undefined, this);
573
+ };
574
+ var MethodBadge = ({
575
+ method,
576
+ enterAt
577
+ }) => {
578
+ const frame = useCurrentFrame5();
579
+ const opacity = interpolate4(frame, [enterAt, enterAt + 10], [0, 1], {
580
+ extrapolateLeft: "clamp",
581
+ extrapolateRight: "clamp",
582
+ easing: videoEasing.entrance
583
+ });
584
+ const scale = interpolate4(frame, [enterAt, enterAt + 12], [0.8, 1], {
585
+ extrapolateLeft: "clamp",
586
+ extrapolateRight: "clamp",
587
+ easing: videoEasing.emphasis
588
+ });
589
+ const methodColors = {
590
+ GET: "#61afef",
591
+ POST: "#98c379",
592
+ PUT: "#e5c07b",
593
+ PATCH: "#d19a66",
594
+ DELETE: "#e06c75"
595
+ };
596
+ return /* @__PURE__ */ jsxDEV5("div", {
597
+ style: {
598
+ opacity,
599
+ transform: `scale(${scale})`,
600
+ backgroundColor: methodColors[method] ?? "#61afef",
601
+ color: "#000",
602
+ padding: "8px 20px",
603
+ borderRadius: 8,
604
+ fontSize: 24,
605
+ fontWeight: 700,
606
+ fontFamily: "monospace",
607
+ letterSpacing: 1
608
+ },
609
+ children: method
610
+ }, undefined, false, undefined, this);
611
+ };
612
+ var OutputChip = ({ label, startFrame }) => {
613
+ const frame = useCurrentFrame5();
614
+ const opacity = interpolate4(frame, [startFrame, startFrame + 12], [0, 1], {
615
+ extrapolateLeft: "clamp",
616
+ extrapolateRight: "clamp"
617
+ });
618
+ const translateY = interpolate4(frame, [startFrame, startFrame + 15], [20, 0], {
619
+ extrapolateLeft: "clamp",
620
+ extrapolateRight: "clamp",
621
+ easing: videoEasing.emphasis
622
+ });
623
+ const scale = interpolate4(frame, [startFrame, startFrame + 15], [0.9, 1], {
624
+ extrapolateLeft: "clamp",
625
+ extrapolateRight: "clamp",
626
+ easing: videoEasing.emphasis
627
+ });
628
+ return /* @__PURE__ */ jsxDEV5("div", {
629
+ style: {
630
+ opacity,
631
+ transform: `translateY(${translateY}px) scale(${scale})`,
632
+ backgroundColor: "rgba(255,255,255,0.08)",
633
+ border: "1px solid rgba(255,255,255,0.15)",
634
+ color: "#ffffff",
635
+ padding: "16px 32px",
636
+ borderRadius: 12,
637
+ fontSize: 28,
638
+ fontWeight: 500
639
+ },
640
+ children: label
641
+ }, undefined, false, undefined, this);
642
+ };
643
+
644
+ // src/compositions/primitives/terminal.tsx
645
+ import { interpolate as interpolate5, useCurrentFrame as useCurrentFrame6, useVideoConfig as useVideoConfig6 } from "remotion";
646
+ import { jsxDEV as jsxDEV6 } from "react/jsx-dev-runtime";
647
+ var LINE_TYPE_COLORS = {
648
+ command: "#c9d1d9",
649
+ output: "#8b949e",
650
+ error: "#f85149",
651
+ success: "#3fb950",
652
+ comment: "#6e7681"
653
+ };
654
+ var Terminal = ({
655
+ lines,
656
+ startAt = 0,
657
+ prompt = "$ ",
658
+ title = "Terminal",
659
+ backgroundColor = defaultVideoColors.terminalBackground,
660
+ typingSpeed = videoDurations.codeTypingPerChar
661
+ }) => {
662
+ const frame = useCurrentFrame6();
663
+ const { width } = useVideoConfig6();
664
+ const codeStyle = scaleTypography(videoTypography.code, width);
665
+ const lineTimings = [];
666
+ let currentFrame = startAt;
667
+ for (const line of lines) {
668
+ const delay = line.delay ?? 10;
669
+ const lineStart = currentFrame + delay;
670
+ if (line.type === "command") {
671
+ const typeDuration = line.text.length * typingSpeed;
672
+ lineTimings.push({
673
+ startFrame: lineStart,
674
+ endFrame: lineStart + typeDuration
675
+ });
676
+ currentFrame = lineStart + typeDuration;
677
+ } else {
678
+ lineTimings.push({ startFrame: lineStart, endFrame: lineStart + 5 });
679
+ currentFrame = lineStart + 5;
680
+ }
681
+ }
682
+ const opacity = interpolate5(frame, [startAt, startAt + 10], [0, 1], {
683
+ extrapolateLeft: "clamp",
684
+ extrapolateRight: "clamp",
685
+ easing: videoEasing.entrance
686
+ });
687
+ return /* @__PURE__ */ jsxDEV6("div", {
688
+ style: {
689
+ backgroundColor,
690
+ borderRadius: 16,
691
+ overflow: "hidden",
692
+ opacity,
693
+ boxShadow: "0 8px 32px rgba(0,0,0,0.3)"
694
+ },
695
+ children: [
696
+ /* @__PURE__ */ jsxDEV6("div", {
697
+ style: {
698
+ display: "flex",
699
+ alignItems: "center",
700
+ padding: "12px 20px",
701
+ backgroundColor: "rgba(255,255,255,0.05)",
702
+ gap: 8
703
+ },
704
+ children: [
705
+ /* @__PURE__ */ jsxDEV6("div", {
706
+ style: { display: "flex", gap: 8 },
707
+ children: [
708
+ /* @__PURE__ */ jsxDEV6("div", {
709
+ style: {
710
+ width: 12,
711
+ height: 12,
712
+ borderRadius: "50%",
713
+ backgroundColor: "#ff5f57"
714
+ }
715
+ }, undefined, false, undefined, this),
716
+ /* @__PURE__ */ jsxDEV6("div", {
717
+ style: {
718
+ width: 12,
719
+ height: 12,
720
+ borderRadius: "50%",
721
+ backgroundColor: "#febc2e"
722
+ }
723
+ }, undefined, false, undefined, this),
724
+ /* @__PURE__ */ jsxDEV6("div", {
725
+ style: {
726
+ width: 12,
727
+ height: 12,
728
+ borderRadius: "50%",
729
+ backgroundColor: "#28c840"
730
+ }
731
+ }, undefined, false, undefined, this)
732
+ ]
733
+ }, undefined, true, undefined, this),
734
+ /* @__PURE__ */ jsxDEV6("div", {
735
+ style: {
736
+ flex: 1,
737
+ textAlign: "center",
738
+ color: "#484f58",
739
+ fontSize: codeStyle.fontSize * 0.7,
740
+ fontFamily: "monospace"
741
+ },
742
+ children: title
743
+ }, undefined, false, undefined, this)
744
+ ]
745
+ }, undefined, true, undefined, this),
746
+ /* @__PURE__ */ jsxDEV6("div", {
747
+ style: { padding: "24px 32px", minHeight: 200 },
748
+ children: lines.map((line, i) => {
749
+ const timing = lineTimings[i];
750
+ if (!timing || frame < timing.startFrame)
751
+ return null;
752
+ const isCommand = line.type === "command";
753
+ const lineColor = LINE_TYPE_COLORS[line.type];
754
+ let visibleText = line.text;
755
+ if (isCommand) {
756
+ const typingProgress = interpolate5(frame, [timing.startFrame, timing.endFrame], [0, line.text.length], { extrapolateLeft: "clamp", extrapolateRight: "clamp" });
757
+ visibleText = line.text.slice(0, Math.floor(typingProgress));
758
+ }
759
+ const lineOpacity = isCommand ? 1 : interpolate5(frame, [timing.startFrame, timing.startFrame + 5], [0, 1], {
760
+ extrapolateLeft: "clamp",
761
+ extrapolateRight: "clamp"
762
+ });
763
+ const isTyping = isCommand && frame >= timing.startFrame && frame <= timing.endFrame;
764
+ const showCursor = isTyping && frame % 16 < 10;
765
+ return /* @__PURE__ */ jsxDEV6("div", {
766
+ style: {
767
+ fontFamily: "'SF Mono', 'Fira Code', 'JetBrains Mono', monospace",
768
+ fontSize: codeStyle.fontSize,
769
+ lineHeight: 1.8,
770
+ color: lineColor,
771
+ opacity: lineOpacity,
772
+ whiteSpace: "pre-wrap"
773
+ },
774
+ children: [
775
+ isCommand && /* @__PURE__ */ jsxDEV6("span", {
776
+ style: { color: "#3fb950" },
777
+ children: prompt
778
+ }, undefined, false, undefined, this),
779
+ line.type === "comment" && /* @__PURE__ */ jsxDEV6("span", {
780
+ style: { color: LINE_TYPE_COLORS.comment },
781
+ children: "# "
782
+ }, undefined, false, undefined, this),
783
+ visibleText,
784
+ showCursor && /* @__PURE__ */ jsxDEV6("span", {
785
+ style: {
786
+ backgroundColor: "#c9d1d9",
787
+ width: "2px",
788
+ display: "inline-block",
789
+ height: "1.2em",
790
+ verticalAlign: "text-bottom"
791
+ },
792
+ children: " "
793
+ }, undefined, false, undefined, this)
794
+ ]
795
+ }, `${i}-${line.text.slice(0, 20)}`, true, undefined, this);
796
+ })
797
+ }, undefined, false, undefined, this)
798
+ ]
799
+ }, undefined, true, undefined, this);
800
+ };
801
+
802
+ // src/compositions/primitives/transition.tsx
803
+ import { interpolate as interpolate6, useCurrentFrame as useCurrentFrame7 } from "remotion";
804
+ import { jsxDEV as jsxDEV7, Fragment } from "react/jsx-dev-runtime";
805
+ var SceneTransitionWrapper = ({
806
+ type,
807
+ durationInFrames,
808
+ direction,
809
+ startAt = 0,
810
+ children
811
+ }) => {
812
+ const frame = useCurrentFrame7();
813
+ if (type === "none" || durationInFrames === 0) {
814
+ return /* @__PURE__ */ jsxDEV7(Fragment, {
815
+ children
816
+ }, undefined, false, undefined, this);
817
+ }
818
+ const progress = interpolate6(frame, [startAt, startAt + durationInFrames], direction === "in" ? [0, 1] : [1, 0], { extrapolateLeft: "clamp", extrapolateRight: "clamp" });
819
+ const styles = getTransitionStyles(type, progress);
820
+ return /* @__PURE__ */ jsxDEV7("div", {
821
+ style: styles,
822
+ children
823
+ }, undefined, false, undefined, this);
824
+ };
825
+ function getTransitionStyles(type, progress) {
826
+ const eased = videoEasing.entrance(progress);
827
+ switch (type) {
828
+ case "fade":
829
+ return {
830
+ opacity: eased,
831
+ width: "100%",
832
+ height: "100%"
833
+ };
834
+ case "slide-left":
835
+ return {
836
+ opacity: eased,
837
+ transform: `translateX(${(1 - eased) * 100}%)`,
838
+ width: "100%",
839
+ height: "100%"
840
+ };
841
+ case "slide-right":
842
+ return {
843
+ opacity: eased,
844
+ transform: `translateX(${(1 - eased) * -100}%)`,
845
+ width: "100%",
846
+ height: "100%"
847
+ };
848
+ case "wipe":
849
+ return {
850
+ clipPath: `inset(0 ${(1 - eased) * 100}% 0 0)`,
851
+ width: "100%",
852
+ height: "100%"
853
+ };
854
+ default:
855
+ return { width: "100%", height: "100%" };
856
+ }
857
+ }
858
+ // src/compositions/social-clip.tsx
859
+ import {
860
+ AbsoluteFill as AbsoluteFill2,
861
+ Sequence as Sequence2,
862
+ interpolate as interpolate7,
863
+ useCurrentFrame as useCurrentFrame8,
864
+ useVideoConfig as useVideoConfig7
865
+ } from "remotion";
866
+ import { jsxDEV as jsxDEV8 } from "react/jsx-dev-runtime";
867
+ var SocialClip = ({
868
+ hook,
869
+ message,
870
+ points = [],
871
+ cta = "Learn more",
872
+ ctaUrl = "contractspec.dev",
873
+ accentColor
874
+ }) => {
875
+ const { durationInFrames, width, height } = useVideoConfig7();
876
+ const theme = defaultVideoTheme;
877
+ const accent = accentColor ?? theme.colors.accent;
878
+ const isPortrait = height > width;
879
+ const HOOK_DURATION = 75;
880
+ const MESSAGE_START = 60;
881
+ const POINTS_START = MESSAGE_START + 60;
882
+ const CTA_START = durationInFrames - 90;
883
+ return /* @__PURE__ */ jsxDEV8(AbsoluteFill2, {
884
+ children: [
885
+ /* @__PURE__ */ jsxDEV8(BrandFrame, {
886
+ variant: "gradient",
887
+ showBranding: true,
888
+ children: /* @__PURE__ */ jsxDEV8("div", {
889
+ style: {
890
+ flex: 1,
891
+ display: "flex",
892
+ flexDirection: "column",
893
+ justifyContent: "center",
894
+ alignItems: "center",
895
+ gap: isPortrait ? 48 : 32,
896
+ textAlign: "center",
897
+ padding: isPortrait ? "0 20px" : 0
898
+ },
899
+ children: [
900
+ /* @__PURE__ */ jsxDEV8(Sequence2, {
901
+ from: 0,
902
+ durationInFrames: HOOK_DURATION + 30,
903
+ children: /* @__PURE__ */ jsxDEV8("div", {
904
+ style: { width: "100%" },
905
+ children: /* @__PURE__ */ jsxDEV8(AnimatedText, {
906
+ text: hook,
907
+ variant: "title",
908
+ enterAt: 5,
909
+ exitAt: HOOK_DURATION,
910
+ color: "#ffffff",
911
+ align: "center"
912
+ }, undefined, false, undefined, this)
913
+ }, undefined, false, undefined, this)
914
+ }, undefined, false, undefined, this),
915
+ /* @__PURE__ */ jsxDEV8(Sequence2, {
916
+ from: MESSAGE_START,
917
+ children: /* @__PURE__ */ jsxDEV8("div", {
918
+ style: { width: "100%" },
919
+ children: /* @__PURE__ */ jsxDEV8(AnimatedText, {
920
+ text: message,
921
+ variant: isPortrait ? "subheading" : "heading",
922
+ enterAt: 0,
923
+ color: "#ffffff",
924
+ align: "center"
925
+ }, undefined, false, undefined, this)
926
+ }, undefined, false, undefined, this)
927
+ }, undefined, false, undefined, this),
928
+ points.length > 0 && /* @__PURE__ */ jsxDEV8(Sequence2, {
929
+ from: POINTS_START,
930
+ children: /* @__PURE__ */ jsxDEV8("div", {
931
+ style: {
932
+ display: "flex",
933
+ flexDirection: "column",
934
+ gap: 16,
935
+ alignItems: isPortrait ? "flex-start" : "center",
936
+ width: "100%",
937
+ maxWidth: isPortrait ? "100%" : "80%",
938
+ marginTop: 16
939
+ },
940
+ children: points.map((point, i) => /* @__PURE__ */ jsxDEV8(PointItem, {
941
+ text: point,
942
+ index: i,
943
+ startFrame: i * 15,
944
+ accent
945
+ }, point, false, undefined, this))
946
+ }, undefined, false, undefined, this)
947
+ }, undefined, false, undefined, this),
948
+ /* @__PURE__ */ jsxDEV8(Sequence2, {
949
+ from: CTA_START,
950
+ children: /* @__PURE__ */ jsxDEV8(CTABlock, {
951
+ cta,
952
+ url: ctaUrl,
953
+ accent
954
+ }, undefined, false, undefined, this)
955
+ }, undefined, false, undefined, this)
956
+ ]
957
+ }, undefined, true, undefined, this)
958
+ }, undefined, false, undefined, this),
959
+ /* @__PURE__ */ jsxDEV8(ProgressBar, {
960
+ color: accent
961
+ }, undefined, false, undefined, this)
962
+ ]
963
+ }, undefined, true, undefined, this);
964
+ };
965
+ var PointItem = ({ text, startFrame, accent }) => {
966
+ const frame = useCurrentFrame8();
967
+ const opacity = interpolate7(frame, [startFrame, startFrame + 12], [0, 1], {
968
+ extrapolateLeft: "clamp",
969
+ extrapolateRight: "clamp"
970
+ });
971
+ const translateX = interpolate7(frame, [startFrame, startFrame + 15], [-30, 0], {
972
+ extrapolateLeft: "clamp",
973
+ extrapolateRight: "clamp",
974
+ easing: videoEasing.entrance
975
+ });
976
+ return /* @__PURE__ */ jsxDEV8("div", {
977
+ style: {
978
+ opacity,
979
+ transform: `translateX(${translateX}px)`,
980
+ display: "flex",
981
+ alignItems: "center",
982
+ gap: 16,
983
+ fontSize: 28,
984
+ color: "#ffffff"
985
+ },
986
+ children: [
987
+ /* @__PURE__ */ jsxDEV8("div", {
988
+ style: {
989
+ width: 8,
990
+ height: 8,
991
+ borderRadius: "50%",
992
+ backgroundColor: accent,
993
+ flexShrink: 0
994
+ }
995
+ }, undefined, false, undefined, this),
996
+ text
997
+ ]
998
+ }, undefined, true, undefined, this);
999
+ };
1000
+ var CTABlock = ({ cta, url, accent }) => {
1001
+ const frame = useCurrentFrame8();
1002
+ const opacity = interpolate7(frame, [0, 15], [0, 1], {
1003
+ extrapolateLeft: "clamp",
1004
+ extrapolateRight: "clamp"
1005
+ });
1006
+ const scale = interpolate7(frame, [0, 18], [0.9, 1], {
1007
+ extrapolateLeft: "clamp",
1008
+ extrapolateRight: "clamp",
1009
+ easing: videoEasing.emphasis
1010
+ });
1011
+ return /* @__PURE__ */ jsxDEV8("div", {
1012
+ style: {
1013
+ opacity,
1014
+ transform: `scale(${scale})`,
1015
+ display: "flex",
1016
+ flexDirection: "column",
1017
+ alignItems: "center",
1018
+ gap: 12,
1019
+ marginTop: 24
1020
+ },
1021
+ children: [
1022
+ /* @__PURE__ */ jsxDEV8("div", {
1023
+ style: {
1024
+ backgroundColor: accent,
1025
+ color: "#ffffff",
1026
+ padding: "16px 48px",
1027
+ borderRadius: 12,
1028
+ fontSize: 28,
1029
+ fontWeight: 600
1030
+ },
1031
+ children: cta
1032
+ }, undefined, false, undefined, this),
1033
+ /* @__PURE__ */ jsxDEV8("div", {
1034
+ style: { color: "rgba(255,255,255,0.5)", fontSize: 20 },
1035
+ children: url
1036
+ }, undefined, false, undefined, this)
1037
+ ]
1038
+ }, undefined, true, undefined, this);
1039
+ };
1040
+
1041
+ // src/compositions/terminal-demo.tsx
1042
+ import { AbsoluteFill as AbsoluteFill3, Sequence as Sequence3, useVideoConfig as useVideoConfig8 } from "remotion";
1043
+ import { jsxDEV as jsxDEV9 } from "react/jsx-dev-runtime";
1044
+ var TerminalDemo = ({
1045
+ title,
1046
+ subtitle,
1047
+ lines,
1048
+ terminalTitle = "Terminal",
1049
+ prompt = "$ ",
1050
+ summary
1051
+ }) => {
1052
+ const { durationInFrames } = useVideoConfig8();
1053
+ const theme = defaultVideoTheme;
1054
+ const TERMINAL_START = 40;
1055
+ const SUMMARY_START = durationInFrames - 90;
1056
+ return /* @__PURE__ */ jsxDEV9(AbsoluteFill3, {
1057
+ children: [
1058
+ /* @__PURE__ */ jsxDEV9(BrandFrame, {
1059
+ variant: "dark",
1060
+ showBranding: true,
1061
+ children: /* @__PURE__ */ jsxDEV9("div", {
1062
+ style: {
1063
+ flex: 1,
1064
+ display: "flex",
1065
+ flexDirection: "column",
1066
+ gap: 24
1067
+ },
1068
+ children: [
1069
+ /* @__PURE__ */ jsxDEV9("div", {
1070
+ children: [
1071
+ /* @__PURE__ */ jsxDEV9(AnimatedText, {
1072
+ text: title,
1073
+ variant: "heading",
1074
+ enterAt: 5,
1075
+ color: "#ffffff"
1076
+ }, undefined, false, undefined, this),
1077
+ subtitle && /* @__PURE__ */ jsxDEV9("div", {
1078
+ style: { marginTop: 8 },
1079
+ children: /* @__PURE__ */ jsxDEV9(AnimatedText, {
1080
+ text: subtitle,
1081
+ variant: "body",
1082
+ enterAt: 15,
1083
+ color: theme.colors.mutedForeground
1084
+ }, undefined, false, undefined, this)
1085
+ }, undefined, false, undefined, this)
1086
+ ]
1087
+ }, undefined, true, undefined, this),
1088
+ /* @__PURE__ */ jsxDEV9(Sequence3, {
1089
+ from: TERMINAL_START,
1090
+ children: /* @__PURE__ */ jsxDEV9("div", {
1091
+ style: { flex: 1 },
1092
+ children: /* @__PURE__ */ jsxDEV9(Terminal, {
1093
+ lines,
1094
+ startAt: 0,
1095
+ prompt,
1096
+ title: terminalTitle
1097
+ }, undefined, false, undefined, this)
1098
+ }, undefined, false, undefined, this)
1099
+ }, undefined, false, undefined, this),
1100
+ summary && /* @__PURE__ */ jsxDEV9(Sequence3, {
1101
+ from: SUMMARY_START,
1102
+ children: /* @__PURE__ */ jsxDEV9("div", {
1103
+ style: {
1104
+ marginTop: "auto",
1105
+ paddingTop: 24
1106
+ },
1107
+ children: /* @__PURE__ */ jsxDEV9(AnimatedText, {
1108
+ text: summary,
1109
+ variant: "subheading",
1110
+ enterAt: 0,
1111
+ color: theme.colors.accent,
1112
+ align: "center"
1113
+ }, undefined, false, undefined, this)
1114
+ }, undefined, false, undefined, this)
1115
+ }, undefined, false, undefined, this)
1116
+ ]
1117
+ }, undefined, true, undefined, this)
1118
+ }, undefined, false, undefined, this),
1119
+ /* @__PURE__ */ jsxDEV9(ProgressBar, {}, undefined, false, undefined, this)
1120
+ ]
1121
+ }, undefined, true, undefined, this);
1122
+ };
1123
+ // src/generators/scene-planner.ts
1124
+ class ScenePlanner {
1125
+ llm;
1126
+ model;
1127
+ temperature;
1128
+ fps;
1129
+ constructor(options) {
1130
+ this.llm = options?.llm;
1131
+ this.model = options?.model;
1132
+ this.temperature = options?.temperature ?? 0.3;
1133
+ this.fps = options?.fps ?? DEFAULT_FPS;
1134
+ }
1135
+ async plan(brief) {
1136
+ if (this.llm) {
1137
+ return this.planWithLlm(brief);
1138
+ }
1139
+ return this.planDeterministic(brief);
1140
+ }
1141
+ planDeterministic(brief) {
1142
+ const { content } = brief;
1143
+ const scenes = [];
1144
+ const fps = this.fps;
1145
+ scenes.push({
1146
+ compositionId: "SocialClip",
1147
+ props: {
1148
+ hook: content.title,
1149
+ message: content.summary,
1150
+ points: content.solutions.slice(0, 3),
1151
+ cta: content.callToAction ?? "Learn more"
1152
+ },
1153
+ durationInFrames: 3 * fps,
1154
+ narrationText: `${content.title}. ${content.summary}`
1155
+ });
1156
+ if (content.problems.length > 0) {
1157
+ scenes.push({
1158
+ compositionId: "SocialClip",
1159
+ props: {
1160
+ hook: "The Problem",
1161
+ message: content.problems[0] ?? "",
1162
+ points: content.problems.slice(1, 4)
1163
+ },
1164
+ durationInFrames: 4 * fps,
1165
+ narrationText: `The problem: ${content.problems.join(". ")}`
1166
+ });
1167
+ }
1168
+ if (content.solutions.length > 0) {
1169
+ scenes.push({
1170
+ compositionId: "SocialClip",
1171
+ props: {
1172
+ hook: "The Solution",
1173
+ message: content.solutions[0] ?? "",
1174
+ points: content.solutions.slice(1, 4)
1175
+ },
1176
+ durationInFrames: 5 * fps,
1177
+ narrationText: `The solution: ${content.solutions.join(". ")}`
1178
+ });
1179
+ }
1180
+ if (content.metrics && content.metrics.length > 0) {
1181
+ scenes.push({
1182
+ compositionId: "SocialClip",
1183
+ props: {
1184
+ hook: "Results",
1185
+ message: content.metrics[0] ?? "",
1186
+ points: content.metrics.slice(1, 3)
1187
+ },
1188
+ durationInFrames: 3 * fps,
1189
+ narrationText: content.metrics.join(". ")
1190
+ });
1191
+ }
1192
+ if (content.callToAction) {
1193
+ scenes.push({
1194
+ compositionId: "SocialClip",
1195
+ props: {
1196
+ hook: content.callToAction,
1197
+ message: "",
1198
+ cta: content.callToAction
1199
+ },
1200
+ durationInFrames: 2 * fps,
1201
+ narrationText: content.callToAction
1202
+ });
1203
+ }
1204
+ if (brief.targetDurationSeconds) {
1205
+ const targetFrames = brief.targetDurationSeconds * fps;
1206
+ const currentFrames = scenes.reduce((sum, s) => sum + s.durationInFrames, 0);
1207
+ const ratio = targetFrames / currentFrames;
1208
+ for (const scene of scenes) {
1209
+ scene.durationInFrames = Math.round(scene.durationInFrames * ratio);
1210
+ }
1211
+ }
1212
+ const totalDuration = scenes.reduce((sum, s) => sum + s.durationInFrames, 0);
1213
+ const narrationScript = scenes.filter((s) => s.narrationText).map((s) => s.narrationText).join(" ");
1214
+ return {
1215
+ scenes,
1216
+ estimatedDurationSeconds: totalDuration / fps,
1217
+ narrationScript
1218
+ };
1219
+ }
1220
+ async planWithLlm(brief) {
1221
+ const messages = [
1222
+ {
1223
+ role: "system",
1224
+ content: [
1225
+ {
1226
+ type: "text",
1227
+ text: `You are a video scene planner for ContractSpec marketing/documentation videos.
1228
+ Given a content brief, break it into video scenes.
1229
+
1230
+ Each scene must have:
1231
+ - compositionId: one of "ApiOverview", "SocialClip", "TerminalDemo"
1232
+ - props: the input props for that composition (see type definitions)
1233
+ - durationInFrames: duration at ${this.fps}fps
1234
+ - narrationText: what the narrator says during this scene
1235
+
1236
+ Return a JSON object with shape:
1237
+ {
1238
+ "scenes": [{ "compositionId": string, "props": object, "durationInFrames": number, "narrationText": string }],
1239
+ "narrationScript": string
1240
+ }
1241
+
1242
+ Keep the total duration around ${brief.targetDurationSeconds ?? 30} seconds.
1243
+ Prioritize clarity and pacing. Each scene should communicate one idea.`
1244
+ }
1245
+ ]
1246
+ },
1247
+ {
1248
+ role: "user",
1249
+ content: [
1250
+ {
1251
+ type: "text",
1252
+ text: JSON.stringify(brief.content)
1253
+ }
1254
+ ]
1255
+ }
1256
+ ];
1257
+ if (!this.llm) {
1258
+ return this.planDeterministic(brief);
1259
+ }
1260
+ try {
1261
+ const response = await this.llm.chat(messages, {
1262
+ model: this.model,
1263
+ temperature: this.temperature,
1264
+ responseFormat: "json"
1265
+ });
1266
+ const text = response.message.content.find((p) => p.type === "text");
1267
+ if (!text || text.type !== "text") {
1268
+ return this.planDeterministic(brief);
1269
+ }
1270
+ const parsed = JSON.parse(text.text);
1271
+ const totalDuration = parsed.scenes.reduce((sum, s) => sum + s.durationInFrames, 0);
1272
+ return {
1273
+ scenes: parsed.scenes,
1274
+ estimatedDurationSeconds: totalDuration / this.fps,
1275
+ narrationScript: parsed.narrationScript
1276
+ };
1277
+ } catch {
1278
+ return this.planDeterministic(brief);
1279
+ }
1280
+ }
1281
+ }
1282
+
1283
+ // src/generators/script-generator.ts
1284
+ class ScriptGenerator {
1285
+ llm;
1286
+ model;
1287
+ temperature;
1288
+ constructor(options) {
1289
+ this.llm = options?.llm;
1290
+ this.model = options?.model;
1291
+ this.temperature = options?.temperature ?? 0.5;
1292
+ }
1293
+ async generate(brief, config) {
1294
+ const style = config?.style ?? "professional";
1295
+ if (this.llm) {
1296
+ return this.generateWithLlm(brief, style);
1297
+ }
1298
+ return this.generateDeterministic(brief, style);
1299
+ }
1300
+ generateDeterministic(brief, style) {
1301
+ const segments = [];
1302
+ const intro = this.formatForStyle(`${brief.title}. ${brief.summary}`, style);
1303
+ segments.push({
1304
+ sceneId: "intro",
1305
+ text: intro,
1306
+ estimatedDurationSeconds: this.estimateDuration(intro)
1307
+ });
1308
+ if (brief.problems.length > 0) {
1309
+ const problemText = this.formatForStyle(`The challenge: ${brief.problems.join(". ")}`, style);
1310
+ segments.push({
1311
+ sceneId: "problems",
1312
+ text: problemText,
1313
+ estimatedDurationSeconds: this.estimateDuration(problemText)
1314
+ });
1315
+ }
1316
+ if (brief.solutions.length > 0) {
1317
+ const solutionText = this.formatForStyle(`The solution: ${brief.solutions.join(". ")}`, style);
1318
+ segments.push({
1319
+ sceneId: "solutions",
1320
+ text: solutionText,
1321
+ estimatedDurationSeconds: this.estimateDuration(solutionText)
1322
+ });
1323
+ }
1324
+ if (brief.metrics && brief.metrics.length > 0) {
1325
+ const metricsText = this.formatForStyle(`The results: ${brief.metrics.join(". ")}`, style);
1326
+ segments.push({
1327
+ sceneId: "metrics",
1328
+ text: metricsText,
1329
+ estimatedDurationSeconds: this.estimateDuration(metricsText)
1330
+ });
1331
+ }
1332
+ if (brief.callToAction) {
1333
+ const ctaText = this.formatForStyle(brief.callToAction, style);
1334
+ segments.push({
1335
+ sceneId: "cta",
1336
+ text: ctaText,
1337
+ estimatedDurationSeconds: this.estimateDuration(ctaText)
1338
+ });
1339
+ }
1340
+ const fullText = segments.map((s) => s.text).join(" ");
1341
+ const totalDuration = segments.reduce((sum, s) => sum + s.estimatedDurationSeconds, 0);
1342
+ return {
1343
+ fullText,
1344
+ segments,
1345
+ estimatedDurationSeconds: totalDuration,
1346
+ style
1347
+ };
1348
+ }
1349
+ async generateWithLlm(brief, style) {
1350
+ const styleGuide = {
1351
+ professional: "Use a clear, authoritative, professional tone. Be concise and direct.",
1352
+ casual: "Use a friendly, conversational tone. Be approachable and relatable.",
1353
+ technical: "Use precise technical language. Be detailed and accurate."
1354
+ };
1355
+ const styleKey = style ?? "professional";
1356
+ const messages = [
1357
+ {
1358
+ role: "system",
1359
+ content: [
1360
+ {
1361
+ type: "text",
1362
+ text: `You are a video narration script writer.
1363
+ Write a narration script for a short video (30-60 seconds).
1364
+ ${styleGuide[styleKey]}
1365
+
1366
+ Return JSON with shape:
1367
+ {
1368
+ "segments": [{ "sceneId": string, "text": string }],
1369
+ "fullText": string
1370
+ }
1371
+
1372
+ Scene IDs should be: "intro", "problems", "solutions", "metrics", "cta".
1373
+ Only include segments that are relevant to the brief content.`
1374
+ }
1375
+ ]
1376
+ },
1377
+ {
1378
+ role: "user",
1379
+ content: [{ type: "text", text: JSON.stringify(brief) }]
1380
+ }
1381
+ ];
1382
+ if (!this.llm) {
1383
+ return this.generateDeterministic(brief, style);
1384
+ }
1385
+ try {
1386
+ const response = await this.llm.chat(messages, {
1387
+ model: this.model,
1388
+ temperature: this.temperature,
1389
+ responseFormat: "json"
1390
+ });
1391
+ const text = response.message.content.find((p) => p.type === "text");
1392
+ if (!text || text.type !== "text") {
1393
+ return this.generateDeterministic(brief, style);
1394
+ }
1395
+ const parsed = JSON.parse(text.text);
1396
+ const segments = parsed.segments.map((s) => ({
1397
+ sceneId: s.sceneId,
1398
+ text: s.text,
1399
+ estimatedDurationSeconds: this.estimateDuration(s.text)
1400
+ }));
1401
+ return {
1402
+ fullText: parsed.fullText,
1403
+ segments,
1404
+ estimatedDurationSeconds: segments.reduce((sum, s) => sum + s.estimatedDurationSeconds, 0),
1405
+ style
1406
+ };
1407
+ } catch {
1408
+ return this.generateDeterministic(brief, style);
1409
+ }
1410
+ }
1411
+ formatForStyle(text, _style) {
1412
+ return text;
1413
+ }
1414
+ estimateDuration(text) {
1415
+ const wordCount = text.split(/\s+/).length;
1416
+ return Math.ceil(wordCount / 150 * 60);
1417
+ }
1418
+ }
1419
+
1420
+ // src/generators/video-generator.ts
1421
+ import { VIDEO_FORMATS as VIDEO_FORMATS2 } from "@contractspec/lib.contracts-integrations/integrations/providers/video";
1422
+ class VideoGenerator {
1423
+ scenePlanner;
1424
+ scriptGenerator;
1425
+ voice;
1426
+ defaultVoiceId;
1427
+ fps;
1428
+ constructor(options) {
1429
+ this.fps = options?.fps ?? DEFAULT_FPS;
1430
+ this.voice = options?.voice;
1431
+ this.defaultVoiceId = options?.defaultVoiceId;
1432
+ this.scenePlanner = new ScenePlanner({
1433
+ llm: options?.llm,
1434
+ model: options?.model,
1435
+ temperature: options?.temperature,
1436
+ fps: this.fps
1437
+ });
1438
+ this.scriptGenerator = new ScriptGenerator({
1439
+ llm: options?.llm,
1440
+ model: options?.model,
1441
+ temperature: options?.temperature
1442
+ });
1443
+ }
1444
+ async generate(brief) {
1445
+ const scenePlan = await this.scenePlanner.plan(brief);
1446
+ let narrationAudio;
1447
+ if (brief.narration?.enabled && this.voice) {
1448
+ const script = await this.scriptGenerator.generate(brief.content, brief.narration);
1449
+ const voiceId = brief.narration.voiceId ?? this.defaultVoiceId;
1450
+ if (voiceId && script.fullText) {
1451
+ try {
1452
+ const result = await this.voice.synthesize({
1453
+ text: script.fullText,
1454
+ voiceId,
1455
+ format: "mp3"
1456
+ });
1457
+ narrationAudio = {
1458
+ data: result.audio,
1459
+ format: "mp3",
1460
+ durationSeconds: result.durationSeconds ?? script.estimatedDurationSeconds,
1461
+ volume: 1
1462
+ };
1463
+ } catch {}
1464
+ }
1465
+ }
1466
+ const format = brief.format ?? VIDEO_FORMATS2.landscape;
1467
+ const scenes = scenePlan.scenes.map((planned, i) => ({
1468
+ id: `scene-${i}`,
1469
+ compositionId: planned.compositionId,
1470
+ props: planned.props,
1471
+ durationInFrames: planned.durationInFrames,
1472
+ narrationText: planned.narrationText
1473
+ }));
1474
+ const totalDurationInFrames = scenes.reduce((sum, s) => sum + s.durationInFrames, 0);
1475
+ const project = {
1476
+ id: generateProjectId(),
1477
+ scenes,
1478
+ totalDurationInFrames,
1479
+ fps: this.fps,
1480
+ format,
1481
+ audio: narrationAudio ? { narration: narrationAudio } : undefined
1482
+ };
1483
+ return project;
1484
+ }
1485
+ }
1486
+ function generateProjectId() {
1487
+ const timestamp = Date.now().toString(36);
1488
+ const random = Math.random().toString(36).slice(2, 8);
1489
+ return `vp_${timestamp}_${random}`;
1490
+ }
1491
+ // src/types.ts
1492
+ import { VIDEO_FORMATS as VIDEO_FORMATS3 } from "@contractspec/lib.contracts-integrations/integrations/providers/video";
1493
+
1494
+ // src/renderers/config.ts
1495
+ var defaultRenderConfig = {
1496
+ codec: "h264",
1497
+ outputFormat: "mp4",
1498
+ crf: 18,
1499
+ pixelFormat: "yuv420p"
1500
+ };
1501
+ var codecFormatMap = {
1502
+ h264: "mp4",
1503
+ h265: "mp4",
1504
+ vp8: "webm",
1505
+ vp9: "webm"
1506
+ };
1507
+ var qualityPresets = {
1508
+ draft: { crf: 28, concurrency: 1 },
1509
+ standard: { crf: 18, concurrency: undefined },
1510
+ high: { crf: 12, concurrency: undefined }
1511
+ };
1512
+ function resolveRenderConfig(userConfig, preset) {
1513
+ const presetValues = preset ? qualityPresets[preset] : {};
1514
+ return {
1515
+ ...defaultRenderConfig,
1516
+ ...presetValues,
1517
+ ...userConfig
1518
+ };
1519
+ }
1520
+ export {
1521
+ videoTypography,
1522
+ videoTransitions,
1523
+ videoSafeZone,
1524
+ videoPositions,
1525
+ videoEasing,
1526
+ videoDurations,
1527
+ scaleTypography,
1528
+ scaleSafeZone,
1529
+ resolveRenderConfig,
1530
+ qualityPresets,
1531
+ getAllFormatVariants,
1532
+ defaultVideoTheme,
1533
+ defaultVideoColors,
1534
+ defaultTokens,
1535
+ defaultRenderConfig,
1536
+ codecFormatMap,
1537
+ VideoGenerator,
1538
+ TerminalDemo,
1539
+ Terminal,
1540
+ SocialClip,
1541
+ ScriptGenerator,
1542
+ SceneTransitionWrapper,
1543
+ ScenePlanner,
1544
+ ProgressBar,
1545
+ DEFAULT_FPS,
1546
+ CodeBlock,
1547
+ BrandFrame,
1548
+ ApiOverview,
1549
+ AnimatedText
1550
+ };