@contractspec/lib.video-gen 0.0.0-canary-20260224194731

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 (171) hide show
  1. package/README.md +37 -0
  2. package/dist/browser/compositions/api-overview.js +645 -0
  3. package/dist/browser/compositions/index.js +1133 -0
  4. package/dist/browser/compositions/primitives/animated-text.js +144 -0
  5. package/dist/browser/compositions/primitives/brand-frame.js +181 -0
  6. package/dist/browser/compositions/primitives/code-block.js +226 -0
  7. package/dist/browser/compositions/primitives/index.js +656 -0
  8. package/dist/browser/compositions/primitives/progress-bar.js +59 -0
  9. package/dist/browser/compositions/primitives/terminal.js +265 -0
  10. package/dist/browser/compositions/primitives/transition.js +98 -0
  11. package/dist/browser/compositions/social-clip.js +500 -0
  12. package/dist/browser/compositions/terminal-demo.js +558 -0
  13. package/dist/browser/design/index.js +155 -0
  14. package/dist/browser/design/layouts.js +50 -0
  15. package/dist/browser/design/motion.js +43 -0
  16. package/dist/browser/design/tokens.js +28 -0
  17. package/dist/browser/design/typography.js +61 -0
  18. package/dist/browser/docs/compositions.docblock.js +182 -0
  19. package/dist/browser/docs/design.docblock.js +187 -0
  20. package/dist/browser/docs/generators.docblock.js +187 -0
  21. package/dist/browser/docs/rendering.docblock.js +197 -0
  22. package/dist/browser/docs/video-gen.docblock.js +141 -0
  23. package/dist/browser/generators/index.js +825 -0
  24. package/dist/browser/generators/scene-planner.js +709 -0
  25. package/dist/browser/generators/script-generator.js +657 -0
  26. package/dist/browser/generators/video-generator.js +824 -0
  27. package/dist/browser/i18n/catalogs/en.js +160 -0
  28. package/dist/browser/i18n/catalogs/es.js +160 -0
  29. package/dist/browser/i18n/catalogs/fr.js +160 -0
  30. package/dist/browser/i18n/catalogs/index.js +462 -0
  31. package/dist/browser/i18n/index.js +534 -0
  32. package/dist/browser/i18n/keys.js +54 -0
  33. package/dist/browser/i18n/locale.js +21 -0
  34. package/dist/browser/i18n/messages.js +474 -0
  35. package/dist/browser/index.js +1959 -0
  36. package/dist/browser/player/demo-player.js +1136 -0
  37. package/dist/browser/player/index.js +1136 -0
  38. package/dist/browser/remotion/Root.js +1189 -0
  39. package/dist/browser/remotion/index.js +1190 -0
  40. package/dist/browser/renderers/config.js +40 -0
  41. package/dist/browser/renderers/index.js +160 -0
  42. package/dist/browser/renderers/local.js +156 -0
  43. package/dist/browser/types.js +13 -0
  44. package/dist/compositions/api-overview.d.ts +16 -0
  45. package/dist/compositions/api-overview.js +640 -0
  46. package/dist/compositions/index.d.ts +7 -0
  47. package/dist/compositions/index.js +1128 -0
  48. package/dist/compositions/primitives/animated-text.d.ts +22 -0
  49. package/dist/compositions/primitives/animated-text.js +139 -0
  50. package/dist/compositions/primitives/brand-frame.d.ts +14 -0
  51. package/dist/compositions/primitives/brand-frame.js +176 -0
  52. package/dist/compositions/primitives/code-block.d.ts +18 -0
  53. package/dist/compositions/primitives/code-block.js +221 -0
  54. package/dist/compositions/primitives/index.d.ts +12 -0
  55. package/dist/compositions/primitives/index.js +651 -0
  56. package/dist/compositions/primitives/progress-bar.d.ts +12 -0
  57. package/dist/compositions/primitives/progress-bar.js +54 -0
  58. package/dist/compositions/primitives/terminal.d.ts +24 -0
  59. package/dist/compositions/primitives/terminal.js +260 -0
  60. package/dist/compositions/primitives/transition.d.ts +14 -0
  61. package/dist/compositions/primitives/transition.js +93 -0
  62. package/dist/compositions/social-clip.d.ts +16 -0
  63. package/dist/compositions/social-clip.js +495 -0
  64. package/dist/compositions/terminal-demo.d.ts +17 -0
  65. package/dist/compositions/terminal-demo.js +553 -0
  66. package/dist/design/index.d.ts +4 -0
  67. package/dist/design/index.js +150 -0
  68. package/dist/design/layouts.d.ts +69 -0
  69. package/dist/design/layouts.js +45 -0
  70. package/dist/design/motion.d.ts +72 -0
  71. package/dist/design/motion.js +38 -0
  72. package/dist/design/tokens.d.ts +31 -0
  73. package/dist/design/tokens.js +23 -0
  74. package/dist/design/typography.d.ts +61 -0
  75. package/dist/design/typography.js +56 -0
  76. package/dist/docs/compositions.docblock.d.ts +1 -0
  77. package/dist/docs/compositions.docblock.js +183 -0
  78. package/dist/docs/design.docblock.d.ts +1 -0
  79. package/dist/docs/design.docblock.js +188 -0
  80. package/dist/docs/generators.docblock.d.ts +1 -0
  81. package/dist/docs/generators.docblock.js +188 -0
  82. package/dist/docs/rendering.docblock.d.ts +1 -0
  83. package/dist/docs/rendering.docblock.js +198 -0
  84. package/dist/docs/video-gen.docblock.d.ts +1 -0
  85. package/dist/docs/video-gen.docblock.js +142 -0
  86. package/dist/generators/index.d.ts +3 -0
  87. package/dist/generators/index.js +820 -0
  88. package/dist/generators/scene-planner.d.ts +25 -0
  89. package/dist/generators/scene-planner.js +704 -0
  90. package/dist/generators/script-generator.d.ts +51 -0
  91. package/dist/generators/script-generator.js +652 -0
  92. package/dist/generators/video-generator.d.ts +21 -0
  93. package/dist/generators/video-generator.js +819 -0
  94. package/dist/i18n/catalogs/en.d.ts +8 -0
  95. package/dist/i18n/catalogs/en.js +155 -0
  96. package/dist/i18n/catalogs/es.d.ts +6 -0
  97. package/dist/i18n/catalogs/es.js +155 -0
  98. package/dist/i18n/catalogs/fr.d.ts +6 -0
  99. package/dist/i18n/catalogs/fr.js +155 -0
  100. package/dist/i18n/catalogs/index.d.ts +8 -0
  101. package/dist/i18n/catalogs/index.js +457 -0
  102. package/dist/i18n/i18n.test.d.ts +1 -0
  103. package/dist/i18n/index.d.ts +22 -0
  104. package/dist/i18n/index.js +529 -0
  105. package/dist/i18n/keys.d.ts +116 -0
  106. package/dist/i18n/keys.js +49 -0
  107. package/dist/i18n/locale.d.ts +8 -0
  108. package/dist/i18n/locale.js +16 -0
  109. package/dist/i18n/messages.d.ts +14 -0
  110. package/dist/i18n/messages.js +469 -0
  111. package/dist/index.d.ts +6 -0
  112. package/dist/index.js +1954 -0
  113. package/dist/node/compositions/api-overview.js +640 -0
  114. package/dist/node/compositions/index.js +1128 -0
  115. package/dist/node/compositions/primitives/animated-text.js +139 -0
  116. package/dist/node/compositions/primitives/brand-frame.js +176 -0
  117. package/dist/node/compositions/primitives/code-block.js +221 -0
  118. package/dist/node/compositions/primitives/index.js +651 -0
  119. package/dist/node/compositions/primitives/progress-bar.js +54 -0
  120. package/dist/node/compositions/primitives/terminal.js +260 -0
  121. package/dist/node/compositions/primitives/transition.js +93 -0
  122. package/dist/node/compositions/social-clip.js +495 -0
  123. package/dist/node/compositions/terminal-demo.js +553 -0
  124. package/dist/node/design/index.js +150 -0
  125. package/dist/node/design/layouts.js +45 -0
  126. package/dist/node/design/motion.js +38 -0
  127. package/dist/node/design/tokens.js +23 -0
  128. package/dist/node/design/typography.js +56 -0
  129. package/dist/node/docs/compositions.docblock.js +182 -0
  130. package/dist/node/docs/design.docblock.js +187 -0
  131. package/dist/node/docs/generators.docblock.js +187 -0
  132. package/dist/node/docs/rendering.docblock.js +197 -0
  133. package/dist/node/docs/video-gen.docblock.js +141 -0
  134. package/dist/node/generators/index.js +820 -0
  135. package/dist/node/generators/scene-planner.js +704 -0
  136. package/dist/node/generators/script-generator.js +652 -0
  137. package/dist/node/generators/video-generator.js +819 -0
  138. package/dist/node/i18n/catalogs/en.js +155 -0
  139. package/dist/node/i18n/catalogs/es.js +155 -0
  140. package/dist/node/i18n/catalogs/fr.js +155 -0
  141. package/dist/node/i18n/catalogs/index.js +457 -0
  142. package/dist/node/i18n/index.js +529 -0
  143. package/dist/node/i18n/keys.js +49 -0
  144. package/dist/node/i18n/locale.js +16 -0
  145. package/dist/node/i18n/messages.js +469 -0
  146. package/dist/node/index.js +1954 -0
  147. package/dist/node/player/demo-player.js +1131 -0
  148. package/dist/node/player/index.js +1131 -0
  149. package/dist/node/remotion/Root.js +1184 -0
  150. package/dist/node/remotion/index.js +1185 -0
  151. package/dist/node/renderers/config.js +35 -0
  152. package/dist/node/renderers/index.js +155 -0
  153. package/dist/node/renderers/local.js +151 -0
  154. package/dist/node/types.js +8 -0
  155. package/dist/player/demo-player.d.ts +55 -0
  156. package/dist/player/demo-player.js +1131 -0
  157. package/dist/player/index.d.ts +2 -0
  158. package/dist/player/index.js +1131 -0
  159. package/dist/remotion/Root.d.ts +2 -0
  160. package/dist/remotion/Root.js +1184 -0
  161. package/dist/remotion/index.d.ts +1 -0
  162. package/dist/remotion/index.js +1185 -0
  163. package/dist/renderers/config.d.ts +28 -0
  164. package/dist/renderers/config.js +35 -0
  165. package/dist/renderers/index.d.ts +3 -0
  166. package/dist/renderers/index.js +155 -0
  167. package/dist/renderers/local.d.ts +17 -0
  168. package/dist/renderers/local.js +151 -0
  169. package/dist/types.d.ts +74 -0
  170. package/dist/types.js +8 -0
  171. package/package.json +780 -0
package/README.md ADDED
@@ -0,0 +1,37 @@
1
+ # @contractspec/lib.video-gen
2
+
3
+ Website: https://contractspec.io/
4
+
5
+ Programmatic video generation using Remotion. Follows the same generator pattern as `@contractspec/lib.content-gen` and consumes contracts from video provider integrations. All generators work deterministically by default -- LLM integration is optional and enhances output when available.
6
+
7
+ ## Modules
8
+
9
+ - **Types** -- `VideoBrief`, `VideoGeneratorOptions`, `GeneratedVideo`, `ScenePlan`, `PlannedScene`, plus re-exported contract types (`VideoProject`, `RenderConfig`, `VideoFormat`)
10
+ - **Design** -- Video-specific design tokens extending the design system: color tokens, motion/easing curves, typography scale (1920x1080), and layout safe zones with format variants (landscape/square/portrait)
11
+ - **Compositions** -- Remotion compositions for rendering video scenes:
12
+ - **Primitives**: `AnimatedText`, `BrandFrame`, `CodeBlock`, `ProgressBar`, `Terminal`, `SceneTransitionWrapper`
13
+ - **Full**: `ApiOverview` (spec to API surface visualization), `SocialClip` (marketing content, adaptive formats), `TerminalDemo` (CLI walkthrough)
14
+ - **Generators** -- `VideoGenerator` (main orchestrator), `ScenePlanner` (breaks briefs into scenes), `ScriptGenerator` (narration with professional/casual/technical styles)
15
+ - **Renderers** -- `LocalRenderer` wrapping `@remotion/renderer` with quality presets (draft/standard/high)
16
+ - **Player** -- `DemoPlayer` component for embedding in React apps via `@remotion/player`
17
+
18
+ Deterministic by default: every generator produces consistent output without an LLM. When an LLM is provided via the constructor, generators produce richer, more varied content. On LLM failure, the system falls back to deterministic output automatically.
19
+
20
+ ## Quickstart
21
+
22
+ ```typescript
23
+ import { VideoGenerator } from "@contractspec/lib.video-gen/generators";
24
+ import type { VideoBrief } from "@contractspec/lib.video-gen/types";
25
+
26
+ const generator = new VideoGenerator(); // no LLM needed
27
+
28
+ const brief: VideoBrief = {
29
+ title: "Ship APIs 10x Faster",
30
+ summary: "See how ContractSpec generates production-ready APIs from specs.",
31
+ format: "landscape",
32
+ };
33
+
34
+ const project = await generator.generate(brief);
35
+ ```
36
+
37
+ Compositions are pure React components -- same props always produce the same visual output, with frame-based timing and no side effects.
@@ -0,0 +1,645 @@
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
+ export {
644
+ ApiOverview
645
+ };