@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.
- package/dist/browser/compositions/api-overview.js +645 -0
- package/dist/browser/compositions/index.js +1133 -0
- package/dist/browser/compositions/primitives/animated-text.js +144 -0
- package/dist/browser/compositions/primitives/brand-frame.js +181 -0
- package/dist/browser/compositions/primitives/code-block.js +226 -0
- package/dist/browser/compositions/primitives/index.js +656 -0
- package/dist/browser/compositions/primitives/progress-bar.js +59 -0
- package/dist/browser/compositions/primitives/terminal.js +265 -0
- package/dist/browser/compositions/primitives/transition.js +98 -0
- package/dist/browser/compositions/social-clip.js +500 -0
- package/dist/browser/compositions/terminal-demo.js +558 -0
- package/dist/browser/design/index.js +155 -0
- package/dist/browser/design/layouts.js +50 -0
- package/dist/browser/design/motion.js +43 -0
- package/dist/browser/design/tokens.js +28 -0
- package/dist/browser/design/typography.js +61 -0
- package/dist/browser/docs/compositions.docblock.js +182 -0
- package/dist/browser/docs/design.docblock.js +187 -0
- package/dist/browser/docs/generators.docblock.js +187 -0
- package/dist/browser/docs/rendering.docblock.js +197 -0
- package/dist/browser/docs/video-gen.docblock.js +141 -0
- package/dist/browser/generators/index.js +416 -0
- package/dist/browser/generators/scene-planner.js +205 -0
- package/dist/browser/generators/script-generator.js +147 -0
- package/dist/browser/generators/video-generator.js +414 -0
- package/dist/browser/index.js +1550 -0
- package/dist/browser/player/demo-player.js +1136 -0
- package/dist/browser/player/index.js +1136 -0
- package/dist/browser/remotion/Root.js +1189 -0
- package/dist/browser/remotion/index.js +1190 -0
- package/dist/browser/renderers/config.js +40 -0
- package/dist/browser/renderers/index.js +160 -0
- package/dist/browser/renderers/local.js +156 -0
- package/dist/browser/types.js +13 -0
- package/dist/compositions/api-overview.d.ts +16 -0
- package/dist/compositions/api-overview.js +640 -0
- package/dist/compositions/index.d.ts +7 -0
- package/dist/compositions/index.js +1128 -0
- package/dist/compositions/primitives/animated-text.d.ts +22 -0
- package/dist/compositions/primitives/animated-text.js +139 -0
- package/dist/compositions/primitives/brand-frame.d.ts +14 -0
- package/dist/compositions/primitives/brand-frame.js +176 -0
- package/dist/compositions/primitives/code-block.d.ts +18 -0
- package/dist/compositions/primitives/code-block.js +221 -0
- package/dist/compositions/primitives/index.d.ts +12 -0
- package/dist/compositions/primitives/index.js +651 -0
- package/dist/compositions/primitives/progress-bar.d.ts +12 -0
- package/dist/compositions/primitives/progress-bar.js +54 -0
- package/dist/compositions/primitives/terminal.d.ts +24 -0
- package/dist/compositions/primitives/terminal.js +260 -0
- package/dist/compositions/primitives/transition.d.ts +14 -0
- package/dist/compositions/primitives/transition.js +93 -0
- package/dist/compositions/social-clip.d.ts +16 -0
- package/dist/compositions/social-clip.js +495 -0
- package/dist/compositions/terminal-demo.d.ts +17 -0
- package/dist/compositions/terminal-demo.js +553 -0
- package/dist/design/index.d.ts +4 -0
- package/dist/design/index.js +150 -0
- package/dist/design/layouts.d.ts +69 -0
- package/dist/design/layouts.js +45 -0
- package/dist/design/motion.d.ts +72 -0
- package/dist/design/motion.js +38 -0
- package/dist/design/tokens.d.ts +31 -0
- package/dist/design/tokens.js +23 -0
- package/dist/design/typography.d.ts +61 -0
- package/dist/design/typography.js +56 -0
- package/dist/docs/compositions.docblock.d.ts +1 -0
- package/dist/docs/compositions.docblock.js +183 -0
- package/dist/docs/design.docblock.d.ts +1 -0
- package/dist/docs/design.docblock.js +188 -0
- package/dist/docs/generators.docblock.d.ts +1 -0
- package/dist/docs/generators.docblock.js +188 -0
- package/dist/docs/rendering.docblock.d.ts +1 -0
- package/dist/docs/rendering.docblock.js +198 -0
- package/dist/docs/video-gen.docblock.d.ts +1 -0
- package/dist/docs/video-gen.docblock.js +142 -0
- package/dist/generators/index.d.ts +5 -0
- package/dist/generators/index.js +411 -0
- package/dist/generators/scene-planner.d.ts +23 -0
- package/dist/generators/scene-planner.js +200 -0
- package/dist/generators/script-generator.d.ts +49 -0
- package/dist/generators/script-generator.js +142 -0
- package/dist/generators/video-generator.d.ts +20 -0
- package/dist/generators/video-generator.js +409 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.js +1545 -0
- package/dist/node/compositions/api-overview.js +640 -0
- package/dist/node/compositions/index.js +1128 -0
- package/dist/node/compositions/primitives/animated-text.js +139 -0
- package/dist/node/compositions/primitives/brand-frame.js +176 -0
- package/dist/node/compositions/primitives/code-block.js +221 -0
- package/dist/node/compositions/primitives/index.js +651 -0
- package/dist/node/compositions/primitives/progress-bar.js +54 -0
- package/dist/node/compositions/primitives/terminal.js +260 -0
- package/dist/node/compositions/primitives/transition.js +93 -0
- package/dist/node/compositions/social-clip.js +495 -0
- package/dist/node/compositions/terminal-demo.js +553 -0
- package/dist/node/design/index.js +150 -0
- package/dist/node/design/layouts.js +45 -0
- package/dist/node/design/motion.js +38 -0
- package/dist/node/design/tokens.js +23 -0
- package/dist/node/design/typography.js +56 -0
- package/dist/node/docs/compositions.docblock.js +182 -0
- package/dist/node/docs/design.docblock.js +187 -0
- package/dist/node/docs/generators.docblock.js +187 -0
- package/dist/node/docs/rendering.docblock.js +197 -0
- package/dist/node/docs/video-gen.docblock.js +141 -0
- package/dist/node/generators/index.js +411 -0
- package/dist/node/generators/scene-planner.js +200 -0
- package/dist/node/generators/script-generator.js +142 -0
- package/dist/node/generators/video-generator.js +409 -0
- package/dist/node/index.js +1545 -0
- package/dist/node/player/demo-player.js +1131 -0
- package/dist/node/player/index.js +1131 -0
- package/dist/node/remotion/Root.js +1184 -0
- package/dist/node/remotion/index.js +1185 -0
- package/dist/node/renderers/config.js +35 -0
- package/dist/node/renderers/index.js +155 -0
- package/dist/node/renderers/local.js +151 -0
- package/dist/node/types.js +8 -0
- package/dist/player/demo-player.d.ts +55 -0
- package/dist/player/demo-player.js +1131 -0
- package/dist/player/index.d.ts +2 -0
- package/dist/player/index.js +1131 -0
- package/dist/remotion/Root.d.ts +2 -0
- package/dist/remotion/Root.js +1184 -0
- package/dist/remotion/index.d.ts +1 -0
- package/dist/remotion/index.js +1185 -0
- package/dist/renderers/config.d.ts +28 -0
- package/dist/renderers/config.js +35 -0
- package/dist/renderers/index.d.ts +3 -0
- package/dist/renderers/index.js +155 -0
- package/dist/renderers/local.d.ts +17 -0
- package/dist/renderers/local.js +151 -0
- package/dist/types.d.ts +63 -0
- package/dist/types.js +8 -0
- package/package.json +637 -0
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
// @bun
|
|
2
|
+
// src/docs/design.docblock.ts
|
|
3
|
+
import { registerDocBlocks } from "@contractspec/lib.contracts-spec/docs";
|
|
4
|
+
var designDocBlocks = [
|
|
5
|
+
{
|
|
6
|
+
id: "docs.video-gen.design",
|
|
7
|
+
title: "Video Design System",
|
|
8
|
+
summary: "Design tokens, motion primitives, typography scale, and layout system optimized for programmatic video.",
|
|
9
|
+
kind: "reference",
|
|
10
|
+
visibility: "public",
|
|
11
|
+
route: "/docs/video-gen/design",
|
|
12
|
+
tags: ["video", "design-tokens", "motion", "typography", "layout"],
|
|
13
|
+
owners: ["@contractspec/lib.video-gen"],
|
|
14
|
+
body: `# Video Design System
|
|
15
|
+
|
|
16
|
+
The design layer bridges \`@contractspec/lib.design-system\` brand tokens with video-specific extensions for motion, typography, and spatial layout. All values are optimized for 1920x1080 (landscape) and scale proportionally for other formats.
|
|
17
|
+
|
|
18
|
+
\`\`\`ts
|
|
19
|
+
import {
|
|
20
|
+
defaultVideoTheme,
|
|
21
|
+
videoEasing,
|
|
22
|
+
videoDurations,
|
|
23
|
+
videoTypography,
|
|
24
|
+
videoSafeZone,
|
|
25
|
+
scaleSafeZone,
|
|
26
|
+
} from "@contractspec/lib.video-gen/design";
|
|
27
|
+
\`\`\`
|
|
28
|
+
|
|
29
|
+
## Tokens
|
|
30
|
+
|
|
31
|
+
### Brand Bridge
|
|
32
|
+
|
|
33
|
+
\`VideoThemeTokens\` extends the design-system \`ThemeTokens\` with a \`video\` namespace for video-specific colors:
|
|
34
|
+
|
|
35
|
+
\`\`\`ts
|
|
36
|
+
import { defaultVideoTheme } from "@contractspec/lib.video-gen/design/tokens";
|
|
37
|
+
|
|
38
|
+
// Brand tokens (from @contractspec/lib.design-system)
|
|
39
|
+
defaultVideoTheme.colors.primary; // brand primary
|
|
40
|
+
defaultVideoTheme.colors.accent; // brand accent
|
|
41
|
+
|
|
42
|
+
// Video-specific extensions
|
|
43
|
+
defaultVideoTheme.video.canvasBackground; // frame background
|
|
44
|
+
defaultVideoTheme.video.codeBackground; // "#1e1e2e"
|
|
45
|
+
defaultVideoTheme.video.terminalBackground; // "#0d1117"
|
|
46
|
+
defaultVideoTheme.video.terminalForeground; // "#c9d1d9"
|
|
47
|
+
defaultVideoTheme.video.highlight; // accent color
|
|
48
|
+
defaultVideoTheme.video.gradientStart; // primary
|
|
49
|
+
defaultVideoTheme.video.gradientEnd; // accent
|
|
50
|
+
\`\`\`
|
|
51
|
+
|
|
52
|
+
> Do not duplicate brand color values. Import and extend from \`@contractspec/lib.design-system\`.
|
|
53
|
+
|
|
54
|
+
## Motion
|
|
55
|
+
|
|
56
|
+
### Easing Functions
|
|
57
|
+
|
|
58
|
+
Pre-configured easing curves for use with Remotion's \`interpolate()\`:
|
|
59
|
+
|
|
60
|
+
| Key | Easing | Use Case |
|
|
61
|
+
|-----|--------|----------|
|
|
62
|
+
| \`entrance\` | \`Easing.out(Easing.exp)\` | Objects appearing |
|
|
63
|
+
| \`exit\` | \`Easing.in(Easing.exp)\` | Objects disappearing |
|
|
64
|
+
| \`emphasis\` | \`Easing.out(Easing.back(1.4))\` | Drawing attention, bounce |
|
|
65
|
+
| \`linear\` | \`Easing.linear\` | Progress bars, typing |
|
|
66
|
+
| \`gentle\` | \`Easing.bezier(0.25, 0.1, 0.25, 1)\` | Subtle movements |
|
|
67
|
+
| \`spring\` | \`Easing.out(Easing.back(1.7))\` | Playful movements |
|
|
68
|
+
|
|
69
|
+
\`\`\`ts
|
|
70
|
+
import { interpolate } from "remotion";
|
|
71
|
+
import { videoEasing } from "@contractspec/lib.video-gen/design/motion";
|
|
72
|
+
|
|
73
|
+
const opacity = interpolate(frame, [0, 15], [0, 1], {
|
|
74
|
+
easing: videoEasing.entrance,
|
|
75
|
+
extrapolateLeft: "clamp",
|
|
76
|
+
extrapolateRight: "clamp",
|
|
77
|
+
});
|
|
78
|
+
\`\`\`
|
|
79
|
+
|
|
80
|
+
### Durations (frames at 30fps)
|
|
81
|
+
|
|
82
|
+
| Key | Frames | Seconds | Use Case |
|
|
83
|
+
|-----|--------|---------|----------|
|
|
84
|
+
| \`sceneTransition\` | 20 | 0.67s | Between scenes |
|
|
85
|
+
| \`textEntrance\` | 15 | 0.5s | Text slide-in |
|
|
86
|
+
| \`textExit\` | 12 | 0.4s | Text slide-out |
|
|
87
|
+
| \`codeTypingPerChar\` | 2 | 0.07s | Code typing speed |
|
|
88
|
+
| \`sectionPause\` | 30 | 1.0s | Pause after concept |
|
|
89
|
+
| \`emphasisPause\` | 15 | 0.5s | Brief emphasis |
|
|
90
|
+
| \`brandReveal\` | 25 | 0.83s | Logo/watermark |
|
|
91
|
+
| \`minScene\` | 60 | 2.0s | Minimum scene length |
|
|
92
|
+
| \`shortScene\` | 60 | 2.0s | Short scene |
|
|
93
|
+
| \`mediumScene\` | 120 | 4.0s | Medium scene |
|
|
94
|
+
| \`longScene\` | 240 | 8.0s | Long scene |
|
|
95
|
+
|
|
96
|
+
### Transition Presets
|
|
97
|
+
|
|
98
|
+
\`\`\`ts
|
|
99
|
+
import { videoTransitions } from "@contractspec/lib.video-gen/design/motion";
|
|
100
|
+
|
|
101
|
+
// { type: "fade", durationInFrames: 20 }
|
|
102
|
+
videoTransitions.fade;
|
|
103
|
+
|
|
104
|
+
// { type: "slide-left", durationInFrames: 20 }
|
|
105
|
+
videoTransitions.slideLeft;
|
|
106
|
+
\`\`\`
|
|
107
|
+
|
|
108
|
+
## Typography
|
|
109
|
+
|
|
110
|
+
### Type Scale (1920x1080 baseline)
|
|
111
|
+
|
|
112
|
+
| Key | Size | Weight | Use Case |
|
|
113
|
+
|-----|------|--------|----------|
|
|
114
|
+
| \`title\` | 72px | 700 | Main title |
|
|
115
|
+
| \`heading\` | 56px | 600 | Section heading |
|
|
116
|
+
| \`subheading\` | 40px | 500 | Subheading |
|
|
117
|
+
| \`body\` | 32px | 400 | Body text |
|
|
118
|
+
| \`code\` | 28px | 400 | Monospace code |
|
|
119
|
+
| \`caption\` | 24px | 400 | Small caption |
|
|
120
|
+
| \`label\` | 20px | 600 | Badge / label |
|
|
121
|
+
|
|
122
|
+
### Scaling for Other Formats
|
|
123
|
+
|
|
124
|
+
Use \`scaleTypography()\` to proportionally scale for non-landscape formats:
|
|
125
|
+
|
|
126
|
+
\`\`\`ts
|
|
127
|
+
import {
|
|
128
|
+
videoTypography,
|
|
129
|
+
scaleTypography,
|
|
130
|
+
} from "@contractspec/lib.video-gen/design/typography";
|
|
131
|
+
|
|
132
|
+
// Scale heading for 1080x1080 (square)
|
|
133
|
+
const squareHeading = scaleTypography(videoTypography.heading, 1080);
|
|
134
|
+
// -> fontSize: 32, lineHeight: 1.2, fontWeight: 600
|
|
135
|
+
\`\`\`
|
|
136
|
+
|
|
137
|
+
## Layouts
|
|
138
|
+
|
|
139
|
+
### Safe Zones
|
|
140
|
+
|
|
141
|
+
Content-safe padding for text within video frames (1920x1080 baseline):
|
|
142
|
+
|
|
143
|
+
\`\`\`ts
|
|
144
|
+
import {
|
|
145
|
+
videoSafeZone,
|
|
146
|
+
scaleSafeZone,
|
|
147
|
+
} from "@contractspec/lib.video-gen/design/layouts";
|
|
148
|
+
|
|
149
|
+
videoSafeZone.horizontal; // 120px
|
|
150
|
+
videoSafeZone.vertical; // 80px
|
|
151
|
+
videoSafeZone.contentWidth; // 1680px
|
|
152
|
+
videoSafeZone.contentHeight; // 920px
|
|
153
|
+
|
|
154
|
+
// Scale for portrait (1080x1920)
|
|
155
|
+
const portrait = scaleSafeZone({ type: "portrait", width: 1080, height: 1920 });
|
|
156
|
+
\`\`\`
|
|
157
|
+
|
|
158
|
+
### Standard Positions
|
|
159
|
+
|
|
160
|
+
\`\`\`ts
|
|
161
|
+
import { videoPositions } from "@contractspec/lib.video-gen/design/layouts";
|
|
162
|
+
|
|
163
|
+
videoPositions.center; // { x: 960, y: 540 }
|
|
164
|
+
videoPositions.topLeft; // { x: 120, y: 80 }
|
|
165
|
+
videoPositions.bottomRight; // { x: 1800, y: 1000 } -- logos, watermarks
|
|
166
|
+
videoPositions.bottomCenter; // { x: 960, y: 960 } -- captions
|
|
167
|
+
\`\`\`
|
|
168
|
+
|
|
169
|
+
### Format Variants
|
|
170
|
+
|
|
171
|
+
\`\`\`ts
|
|
172
|
+
import {
|
|
173
|
+
VIDEO_FORMATS,
|
|
174
|
+
getAllFormatVariants,
|
|
175
|
+
DEFAULT_FPS,
|
|
176
|
+
} from "@contractspec/lib.video-gen/design/layouts";
|
|
177
|
+
|
|
178
|
+
VIDEO_FORMATS.landscape; // { type: "landscape", width: 1920, height: 1080 }
|
|
179
|
+
VIDEO_FORMATS.portrait; // { type: "portrait", width: 1080, height: 1920 }
|
|
180
|
+
VIDEO_FORMATS.square; // { type: "square", width: 1080, height: 1080 }
|
|
181
|
+
|
|
182
|
+
getAllFormatVariants(); // [landscape, square, portrait]
|
|
183
|
+
DEFAULT_FPS; // 30
|
|
184
|
+
\`\`\`
|
|
185
|
+
`
|
|
186
|
+
}
|
|
187
|
+
];
|
|
188
|
+
registerDocBlocks(designDocBlocks);
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
// @bun
|
|
2
|
+
// src/docs/generators.docblock.ts
|
|
3
|
+
import { registerDocBlocks } from "@contractspec/lib.contracts-spec/docs";
|
|
4
|
+
var generatorsDocBlocks = [
|
|
5
|
+
{
|
|
6
|
+
id: "docs.video-gen.generators",
|
|
7
|
+
title: "Video Generation Pipeline",
|
|
8
|
+
summary: "VideoGenerator, ScenePlanner, and ScriptGenerator -- from content brief to video project with optional LLM enhancement.",
|
|
9
|
+
kind: "reference",
|
|
10
|
+
visibility: "public",
|
|
11
|
+
route: "/docs/video-gen/generators",
|
|
12
|
+
tags: [
|
|
13
|
+
"video",
|
|
14
|
+
"generators",
|
|
15
|
+
"scene-planner",
|
|
16
|
+
"script-generator",
|
|
17
|
+
"llm",
|
|
18
|
+
"content-pipeline"
|
|
19
|
+
],
|
|
20
|
+
owners: ["@contractspec/lib.video-gen"],
|
|
21
|
+
body: `# Video Generation Pipeline
|
|
22
|
+
|
|
23
|
+
The generators layer converts a \`VideoBrief\` (content brief + video config) into a fully specified \`VideoProject\` (scene graph ready for rendering). It follows the \`@contractspec/lib.content-gen\` pattern: optional LLM, deterministic fallback.
|
|
24
|
+
|
|
25
|
+
\`\`\`
|
|
26
|
+
VideoBrief
|
|
27
|
+
|
|
|
28
|
+
v
|
|
29
|
+
ScenePlanner.plan(brief) --> ScenePlan (scenes + durations)
|
|
30
|
+
|
|
|
31
|
+
v
|
|
32
|
+
ScriptGenerator.generate(brief) --> NarrationScript (text + segments)
|
|
33
|
+
|
|
|
34
|
+
v
|
|
35
|
+
VoiceProvider.synthesize(text) --> AudioTrack (optional)
|
|
36
|
+
|
|
|
37
|
+
v
|
|
38
|
+
VideoGenerator.generate(brief) --> VideoProject (complete scene graph)
|
|
39
|
+
\`\`\`
|
|
40
|
+
|
|
41
|
+
## VideoGenerator
|
|
42
|
+
|
|
43
|
+
The main orchestrator. Wires ScenePlanner, ScriptGenerator, and optional VoiceProvider into a single pipeline.
|
|
44
|
+
|
|
45
|
+
\`\`\`ts
|
|
46
|
+
import { VideoGenerator } from "@contractspec/lib.video-gen/generators";
|
|
47
|
+
import type { VideoBrief } from "@contractspec/lib.video-gen/types";
|
|
48
|
+
|
|
49
|
+
// Minimal (deterministic, no LLM, no voice)
|
|
50
|
+
const generator = new VideoGenerator({ fps: 30 });
|
|
51
|
+
|
|
52
|
+
// Full (with LLM for richer scenes + voice narration)
|
|
53
|
+
const generator = new VideoGenerator({
|
|
54
|
+
llm: myLLMProvider,
|
|
55
|
+
voice: myVoiceProvider,
|
|
56
|
+
model: "gpt-4o",
|
|
57
|
+
temperature: 0.4,
|
|
58
|
+
defaultVoiceId: "rachel",
|
|
59
|
+
fps: 30,
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
const project = await generator.generate(brief);
|
|
63
|
+
\`\`\`
|
|
64
|
+
|
|
65
|
+
### Pipeline Steps
|
|
66
|
+
|
|
67
|
+
1. **Scene planning** -- \`ScenePlanner.plan(brief)\` breaks the brief into concrete \`PlannedScene[]\` with composition IDs, props, and durations.
|
|
68
|
+
2. **Script generation** -- If \`brief.narration.enabled\`, \`ScriptGenerator.generate()\` produces a \`NarrationScript\` with per-scene text segments.
|
|
69
|
+
3. **Voice synthesis** -- If a \`VoiceProvider\` is configured and narration is enabled, synthesizes audio via \`voice.synthesize()\`.
|
|
70
|
+
4. **Assembly** -- Combines scenes, audio, and metadata into a \`VideoProject\`.
|
|
71
|
+
|
|
72
|
+
### Constructor Options
|
|
73
|
+
|
|
74
|
+
| Option | Type | Default | Description |
|
|
75
|
+
|--------|------|---------|-------------|
|
|
76
|
+
| \`llm\` | \`LLMProvider\` | -- | Optional LLM for enhanced generation |
|
|
77
|
+
| \`voice\` | \`VoiceProvider\` | -- | Optional voice synthesis provider |
|
|
78
|
+
| \`model\` | \`string\` | -- | LLM model override |
|
|
79
|
+
| \`temperature\` | \`number\` | \`0.4\` | LLM temperature (lower = more deterministic) |
|
|
80
|
+
| \`defaultVoiceId\` | \`string\` | -- | Default voice for narration |
|
|
81
|
+
| \`fps\` | \`number\` | \`30\` | Frames per second |
|
|
82
|
+
|
|
83
|
+
## ScenePlanner
|
|
84
|
+
|
|
85
|
+
Breaks a \`VideoBrief\` into concrete scenes mapped to registered compositions.
|
|
86
|
+
|
|
87
|
+
\`\`\`ts
|
|
88
|
+
import { ScenePlanner } from "@contractspec/lib.video-gen/generators";
|
|
89
|
+
|
|
90
|
+
const planner = new ScenePlanner({ fps: 30 });
|
|
91
|
+
const plan = await planner.plan(brief);
|
|
92
|
+
|
|
93
|
+
plan.scenes; // PlannedScene[]
|
|
94
|
+
plan.estimatedDurationSeconds; // total estimated duration
|
|
95
|
+
plan.narrationScript; // combined narration text
|
|
96
|
+
\`\`\`
|
|
97
|
+
|
|
98
|
+
### Deterministic Path (no LLM)
|
|
99
|
+
|
|
100
|
+
Maps brief sections to \`SocialClip\` compositions:
|
|
101
|
+
|
|
102
|
+
| Brief Section | Scene | Duration |
|
|
103
|
+
|---------------|-------|----------|
|
|
104
|
+
| \`title\` + \`summary\` | Hook / title | 3s |
|
|
105
|
+
| \`problems\` | Problem statement | 4s |
|
|
106
|
+
| \`solutions\` | Solution showcase | 5s |
|
|
107
|
+
| \`metrics\` | Proof / results | 3s |
|
|
108
|
+
| \`callToAction\` | CTA | 2s |
|
|
109
|
+
|
|
110
|
+
If \`brief.targetDurationSeconds\` is set, all scene durations are scaled proportionally.
|
|
111
|
+
|
|
112
|
+
### LLM-Enhanced Path
|
|
113
|
+
|
|
114
|
+
With an \`LLMProvider\`, the planner sends the brief to the LLM and requests a scene breakdown as JSON. The LLM can choose from \`ApiOverview\`, \`SocialClip\`, or \`TerminalDemo\` compositions. Falls back to deterministic on any failure.
|
|
115
|
+
|
|
116
|
+
## ScriptGenerator
|
|
117
|
+
|
|
118
|
+
Produces narration text from a \`ContentBrief\` with style control.
|
|
119
|
+
|
|
120
|
+
\`\`\`ts
|
|
121
|
+
import { ScriptGenerator } from "@contractspec/lib.video-gen/generators";
|
|
122
|
+
|
|
123
|
+
const scriptGen = new ScriptGenerator({ temperature: 0.5 });
|
|
124
|
+
const script = await scriptGen.generate(brief.content, brief.narration);
|
|
125
|
+
|
|
126
|
+
script.fullText; // complete narration
|
|
127
|
+
script.segments; // NarrationSegment[] (per-scene text)
|
|
128
|
+
script.estimatedDurationSeconds; // at ~150 words/min
|
|
129
|
+
script.style; // "professional" | "casual" | "technical"
|
|
130
|
+
\`\`\`
|
|
131
|
+
|
|
132
|
+
### Narration Styles
|
|
133
|
+
|
|
134
|
+
| Style | Tone |
|
|
135
|
+
|-------|------|
|
|
136
|
+
| \`professional\` | Clear, authoritative, concise |
|
|
137
|
+
| \`casual\` | Friendly, conversational, approachable |
|
|
138
|
+
| \`technical\` | Precise, detailed, accurate |
|
|
139
|
+
|
|
140
|
+
### NarrationSegment
|
|
141
|
+
|
|
142
|
+
Each segment maps to a scene and provides timing estimates:
|
|
143
|
+
|
|
144
|
+
\`\`\`ts
|
|
145
|
+
interface NarrationSegment {
|
|
146
|
+
sceneId: string; // "intro", "problems", "solutions", "metrics", "cta"
|
|
147
|
+
text: string; // narration text for this segment
|
|
148
|
+
estimatedDurationSeconds: number; // at ~150 words/min
|
|
149
|
+
}
|
|
150
|
+
\`\`\`
|
|
151
|
+
|
|
152
|
+
## Input Types
|
|
153
|
+
|
|
154
|
+
### VideoBrief
|
|
155
|
+
|
|
156
|
+
\`\`\`ts
|
|
157
|
+
interface VideoBrief {
|
|
158
|
+
content: ContentBrief; // from @contractspec/lib.content-gen
|
|
159
|
+
format: VideoFormat; // landscape, portrait, square, or custom
|
|
160
|
+
targetDurationSeconds?: number; // auto-calculated if omitted
|
|
161
|
+
narration?: NarrationConfig; // { enabled, voiceId, language, style }
|
|
162
|
+
style?: VideoStyleOverrides; // colors, fonts, dark mode
|
|
163
|
+
compositionId?: string; // force a specific composition
|
|
164
|
+
}
|
|
165
|
+
\`\`\`
|
|
166
|
+
|
|
167
|
+
### PlannedScene
|
|
168
|
+
|
|
169
|
+
\`\`\`ts
|
|
170
|
+
interface PlannedScene {
|
|
171
|
+
compositionId: string; // maps to a registered Remotion composition
|
|
172
|
+
props: Record<string, unknown>; // input props for the composition
|
|
173
|
+
durationInFrames: number; // scene duration
|
|
174
|
+
narrationText?: string; // narrator text for this scene
|
|
175
|
+
notes?: string; // planning notes (LLM path only)
|
|
176
|
+
}
|
|
177
|
+
\`\`\`
|
|
178
|
+
|
|
179
|
+
## Guardrails
|
|
180
|
+
|
|
181
|
+
- Generators are **stateless** -- no side effects, no caching. Call \`generate()\` / \`plan()\` for each video.
|
|
182
|
+
- LLM responses are parsed as JSON; any parse failure falls back to deterministic.
|
|
183
|
+
- Temperature defaults are conservative (0.3-0.5) for reproducibility.
|
|
184
|
+
- Duration estimates use 150 words/min speaking rate.
|
|
185
|
+
`
|
|
186
|
+
}
|
|
187
|
+
];
|
|
188
|
+
registerDocBlocks(generatorsDocBlocks);
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
// @bun
|
|
2
|
+
// src/docs/rendering.docblock.ts
|
|
3
|
+
import { registerDocBlocks } from "@contractspec/lib.contracts-spec/docs";
|
|
4
|
+
var renderingDocBlocks = [
|
|
5
|
+
{
|
|
6
|
+
id: "docs.video-gen.rendering",
|
|
7
|
+
title: "Video Rendering & Playback",
|
|
8
|
+
summary: "LocalRenderer for MP4 output, render configuration, quality presets, DemoPlayer for web embedding, and Remotion Studio setup.",
|
|
9
|
+
kind: "reference",
|
|
10
|
+
visibility: "public",
|
|
11
|
+
route: "/docs/video-gen/rendering",
|
|
12
|
+
tags: [
|
|
13
|
+
"video",
|
|
14
|
+
"rendering",
|
|
15
|
+
"remotion",
|
|
16
|
+
"player",
|
|
17
|
+
"mp4",
|
|
18
|
+
"quality-presets"
|
|
19
|
+
],
|
|
20
|
+
owners: ["@contractspec/lib.video-gen"],
|
|
21
|
+
body: `# Video Rendering & Playback
|
|
22
|
+
|
|
23
|
+
The rendering layer wraps \`@remotion/renderer\` for MP4/WebM output and \`@remotion/player\` for interactive web embedding. It implements the \`VideoProvider\` contract from \`@contractspec/lib.contracts-integrations\`.
|
|
24
|
+
|
|
25
|
+
## LocalRenderer
|
|
26
|
+
|
|
27
|
+
Renders a \`VideoProject\` to a video file using the local Remotion renderer. Requires **Node.js** (not Bun-compatible).
|
|
28
|
+
|
|
29
|
+
\`\`\`ts
|
|
30
|
+
import { LocalRenderer } from "@contractspec/lib.video-gen/renderers/local";
|
|
31
|
+
|
|
32
|
+
const renderer = new LocalRenderer({
|
|
33
|
+
entryPoint: "./src/remotion/index.ts",
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
const result = await renderer.render(project, {
|
|
37
|
+
outputPath: "out/video.mp4",
|
|
38
|
+
codec: "h264",
|
|
39
|
+
crf: 18,
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
result.outputPath; // "out/video.mp4"
|
|
43
|
+
result.format; // "mp4"
|
|
44
|
+
result.durationSeconds; // total duration
|
|
45
|
+
result.fileSizeBytes; // file size
|
|
46
|
+
result.dimensions; // { width: 1920, height: 1080 }
|
|
47
|
+
\`\`\`
|
|
48
|
+
|
|
49
|
+
> **Important**: Import \`LocalRenderer\` from the \`/renderers/local\` subpath, not from the main entry. It dynamically imports \`@remotion/bundler\` and \`@remotion/renderer\` which are Node.js-only.
|
|
50
|
+
|
|
51
|
+
### Auto-Variants
|
|
52
|
+
|
|
53
|
+
Set \`autoVariants: true\` to generate landscape + square + portrait versions:
|
|
54
|
+
|
|
55
|
+
\`\`\`ts
|
|
56
|
+
const result = await renderer.render(project, {
|
|
57
|
+
outputPath: "out/video.mp4",
|
|
58
|
+
autoVariants: true,
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
result.variants; // [
|
|
62
|
+
// { outputPath: "out/video-square.mp4", dimensions: { width: 1080, height: 1080 } },
|
|
63
|
+
// { outputPath: "out/video-portrait.mp4", dimensions: { width: 1080, height: 1920 } },
|
|
64
|
+
// ]
|
|
65
|
+
\`\`\`
|
|
66
|
+
|
|
67
|
+
## Render Configuration
|
|
68
|
+
|
|
69
|
+
### Defaults
|
|
70
|
+
|
|
71
|
+
\`\`\`ts
|
|
72
|
+
import {
|
|
73
|
+
defaultRenderConfig,
|
|
74
|
+
resolveRenderConfig,
|
|
75
|
+
qualityPresets,
|
|
76
|
+
codecFormatMap,
|
|
77
|
+
} from "@contractspec/lib.video-gen/renderers/config";
|
|
78
|
+
|
|
79
|
+
defaultRenderConfig.codec; // "h264"
|
|
80
|
+
defaultRenderConfig.outputFormat; // "mp4"
|
|
81
|
+
defaultRenderConfig.crf; // 18
|
|
82
|
+
defaultRenderConfig.pixelFormat; // "yuv420p"
|
|
83
|
+
\`\`\`
|
|
84
|
+
|
|
85
|
+
### RenderConfig Options
|
|
86
|
+
|
|
87
|
+
| Option | Type | Default | Description |
|
|
88
|
+
|--------|------|---------|-------------|
|
|
89
|
+
| \`outputPath\` | \`string\` | -- | **Required**. Output file path |
|
|
90
|
+
| \`codec\` | \`"h264" \\| "h265" \\| "vp8" \\| "vp9"\` | \`"h264"\` | Video codec |
|
|
91
|
+
| \`outputFormat\` | \`"mp4" \\| "webm" \\| "gif"\` | \`"mp4"\` | Container format |
|
|
92
|
+
| \`crf\` | \`number\` | \`18\` | Constant Rate Factor (lower = better quality) |
|
|
93
|
+
| \`pixelFormat\` | \`string\` | \`"yuv420p"\` | Pixel format |
|
|
94
|
+
| \`concurrency\` | \`number\` | CPU count | Rendering threads |
|
|
95
|
+
| \`autoVariants\` | \`boolean\` | \`false\` | Generate format variants |
|
|
96
|
+
|
|
97
|
+
### Quality Presets
|
|
98
|
+
|
|
99
|
+
\`\`\`ts
|
|
100
|
+
import { resolveRenderConfig } from "@contractspec/lib.video-gen/renderers/config";
|
|
101
|
+
|
|
102
|
+
// Draft (fastest, for previews)
|
|
103
|
+
resolveRenderConfig({ outputPath: "out/preview.mp4" }, "draft");
|
|
104
|
+
// -> crf: 28, concurrency: 1
|
|
105
|
+
|
|
106
|
+
// Standard (balanced)
|
|
107
|
+
resolveRenderConfig({ outputPath: "out/video.mp4" }, "standard");
|
|
108
|
+
// -> crf: 18
|
|
109
|
+
|
|
110
|
+
// High (best quality, for final output)
|
|
111
|
+
resolveRenderConfig({ outputPath: "out/final.mp4" }, "high");
|
|
112
|
+
// -> crf: 12
|
|
113
|
+
\`\`\`
|
|
114
|
+
|
|
115
|
+
### Codec-to-Format Mapping
|
|
116
|
+
|
|
117
|
+
| Codec | Format |
|
|
118
|
+
|-------|--------|
|
|
119
|
+
| \`h264\` | \`mp4\` |
|
|
120
|
+
| \`h265\` | \`mp4\` |
|
|
121
|
+
| \`vp8\` | \`webm\` |
|
|
122
|
+
| \`vp9\` | \`webm\` |
|
|
123
|
+
|
|
124
|
+
## DemoPlayer (Web Embedding)
|
|
125
|
+
|
|
126
|
+
Embeddable Remotion Player for interactive video demos in React apps. Wraps \`@remotion/player\` with ContractSpec compositions.
|
|
127
|
+
|
|
128
|
+
\`\`\`tsx
|
|
129
|
+
import { DemoPlayer } from "@contractspec/lib.video-gen/player";
|
|
130
|
+
|
|
131
|
+
<DemoPlayer
|
|
132
|
+
compositionId="ApiOverview"
|
|
133
|
+
inputProps={{
|
|
134
|
+
specName: "CreateUser",
|
|
135
|
+
specCode: "export const createUser = defineCommand({...})",
|
|
136
|
+
}}
|
|
137
|
+
controls
|
|
138
|
+
autoPlay
|
|
139
|
+
loop
|
|
140
|
+
width="100%"
|
|
141
|
+
clickToPlay
|
|
142
|
+
doubleClickToFullscreen
|
|
143
|
+
/>
|
|
144
|
+
\`\`\`
|
|
145
|
+
|
|
146
|
+
### DemoPlayer Props
|
|
147
|
+
|
|
148
|
+
| Prop | Type | Default | Description |
|
|
149
|
+
|------|------|---------|-------------|
|
|
150
|
+
| \`compositionId\` | \`"ApiOverview" \\| "SocialClip" \\| "TerminalDemo"\` | -- | Composition to play |
|
|
151
|
+
| \`inputProps\` | composition props type | -- | Props for the selected composition |
|
|
152
|
+
| \`controls\` | \`boolean\` | \`true\` | Show playback controls |
|
|
153
|
+
| \`autoPlay\` | \`boolean\` | \`false\` | Auto-play on mount |
|
|
154
|
+
| \`loop\` | \`boolean\` | \`false\` | Loop playback |
|
|
155
|
+
| \`width\` | \`string \\| number\` | \`"100%"\` | Player width |
|
|
156
|
+
| \`height\` | \`string \\| number\` | \`"auto"\` | Player height |
|
|
157
|
+
| \`clickToPlay\` | \`boolean\` | \`true\` | Click to toggle playback |
|
|
158
|
+
| \`doubleClickToFullscreen\` | \`boolean\` | \`true\` | Double-click for fullscreen |
|
|
159
|
+
|
|
160
|
+
> \`@remotion/player\` is a peer dependency. Install it in your app if you use \`DemoPlayer\`.
|
|
161
|
+
|
|
162
|
+
## Remotion Studio
|
|
163
|
+
|
|
164
|
+
The \`@contractspec/app.video-studio\` package provides a Remotion Studio entry point for previewing compositions interactively.
|
|
165
|
+
|
|
166
|
+
\`\`\`bash
|
|
167
|
+
# Start Remotion Studio
|
|
168
|
+
bun run dev:video
|
|
169
|
+
|
|
170
|
+
# Render a specific composition
|
|
171
|
+
npx remotion render src/index.ts ApiOverview out/api-overview.mp4
|
|
172
|
+
|
|
173
|
+
# Render all compositions
|
|
174
|
+
bun run render:all
|
|
175
|
+
\`\`\`
|
|
176
|
+
|
|
177
|
+
### Registered Compositions
|
|
178
|
+
|
|
179
|
+
| ID | Component | Dimensions | Duration | Description |
|
|
180
|
+
|----|-----------|------------|----------|-------------|
|
|
181
|
+
| \`ApiOverview\` | \`ApiOverview\` | 1920x1080 | 450 frames (15s) | Homepage API demo |
|
|
182
|
+
| \`SocialClip\` | \`SocialClip\` | 1920x1080 | 300 frames (10s) | Landscape social clip |
|
|
183
|
+
| \`SocialClipSquare\` | \`SocialClip\` | 1080x1080 | 300 frames (10s) | Square social clip |
|
|
184
|
+
| \`SocialClipPortrait\` | \`SocialClip\` | 1080x1920 | 300 frames (10s) | Portrait social clip |
|
|
185
|
+
| \`TerminalDemo\` | \`TerminalDemo\` | 1920x1080 | 600 frames (20s) | CLI walkthrough |
|
|
186
|
+
|
|
187
|
+
All compositions run at 30fps.
|
|
188
|
+
|
|
189
|
+
## Guardrails
|
|
190
|
+
|
|
191
|
+
- \`LocalRenderer\` requires Node.js -- do not attempt to use it in browser or Bun environments.
|
|
192
|
+
- Import \`LocalRenderer\` from the \`/renderers/local\` subpath to avoid bundling \`@remotion/renderer\` in browser builds.
|
|
193
|
+
- The \`remotion\` entry point (\`@contractspec/lib.video-gen/remotion\`) is a **side-effect module** that calls \`registerRoot()\`. Only import it from Remotion Studio or render scripts.
|
|
194
|
+
- Use quality presets for consistency: \`draft\` for development, \`standard\` for CI, \`high\` for releases.
|
|
195
|
+
`
|
|
196
|
+
}
|
|
197
|
+
];
|
|
198
|
+
registerDocBlocks(renderingDocBlocks);
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|