vargai 0.4.0-alpha4 → 0.4.0-alpha40

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 (114) hide show
  1. package/.env.example +6 -0
  2. package/README.md +483 -61
  3. package/assets/fonts/TikTokSans-Bold.ttf +0 -0
  4. package/examples/grok-imagine-test.tsx +155 -0
  5. package/launch-videos/06-kawaii-fruits.tsx +93 -0
  6. package/launch-videos/07-ugc-weight-loss.tsx +132 -0
  7. package/launch-videos/08-talking-head-varg.tsx +107 -0
  8. package/launch-videos/09-girl.tsx +160 -0
  9. package/launch-videos/README.md +42 -0
  10. package/package.json +10 -4
  11. package/pipeline/cookbooks/round-video-character.md +1 -1
  12. package/skills/varg-video-generation/SKILL.md +224 -0
  13. package/skills/varg-video-generation/references/templates.md +380 -0
  14. package/skills/varg-video-generation/scripts/setup.ts +265 -0
  15. package/src/ai-sdk/cache.ts +1 -3
  16. package/src/ai-sdk/examples/google-image.ts +62 -0
  17. package/src/ai-sdk/index.ts +10 -0
  18. package/src/ai-sdk/middleware/wrap-image-model.ts +4 -21
  19. package/src/ai-sdk/middleware/wrap-music-model.ts +4 -16
  20. package/src/ai-sdk/middleware/wrap-video-model.ts +5 -17
  21. package/src/ai-sdk/providers/CONTRIBUTING.md +457 -0
  22. package/src/ai-sdk/providers/editly/backends/index.ts +8 -0
  23. package/src/ai-sdk/providers/editly/backends/local.ts +94 -0
  24. package/src/ai-sdk/providers/editly/backends/types.ts +74 -0
  25. package/src/ai-sdk/providers/editly/editly.test.ts +49 -1
  26. package/src/ai-sdk/providers/editly/index.ts +164 -80
  27. package/src/ai-sdk/providers/editly/layers.ts +58 -6
  28. package/src/ai-sdk/providers/editly/rendi/editly-with-rendi-backend.test.ts +335 -0
  29. package/src/ai-sdk/providers/editly/rendi/index.ts +289 -0
  30. package/src/ai-sdk/providers/editly/rendi/rendi.test.ts +35 -0
  31. package/src/ai-sdk/providers/editly/types.ts +30 -0
  32. package/src/ai-sdk/providers/elevenlabs.ts +10 -2
  33. package/src/ai-sdk/providers/fal.test.ts +214 -0
  34. package/src/ai-sdk/providers/fal.ts +435 -40
  35. package/src/ai-sdk/providers/google.ts +423 -0
  36. package/src/ai-sdk/providers/together.ts +191 -0
  37. package/src/cli/commands/find.tsx +1 -0
  38. package/src/cli/commands/frame.tsx +616 -0
  39. package/src/cli/commands/hello.ts +85 -0
  40. package/src/cli/commands/help.tsx +18 -30
  41. package/src/cli/commands/index.ts +11 -2
  42. package/src/cli/commands/init.tsx +570 -0
  43. package/src/cli/commands/list.tsx +1 -0
  44. package/src/cli/commands/render.tsx +322 -76
  45. package/src/cli/commands/run.tsx +1 -0
  46. package/src/cli/commands/storyboard.tsx +1714 -0
  47. package/src/cli/commands/which.tsx +1 -0
  48. package/src/cli/index.ts +23 -4
  49. package/src/cli/ui/components/Badge.tsx +1 -0
  50. package/src/cli/ui/components/DataTable.tsx +1 -0
  51. package/src/cli/ui/components/Header.tsx +1 -0
  52. package/src/cli/ui/components/HelpBlock.tsx +1 -0
  53. package/src/cli/ui/components/KeyValue.tsx +1 -0
  54. package/src/cli/ui/components/OptionRow.tsx +1 -0
  55. package/src/cli/ui/components/Separator.tsx +1 -0
  56. package/src/cli/ui/components/StatusBox.tsx +1 -0
  57. package/src/cli/ui/components/VargBox.tsx +1 -0
  58. package/src/cli/ui/components/VargProgress.tsx +1 -0
  59. package/src/cli/ui/components/VargSpinner.tsx +1 -0
  60. package/src/cli/ui/components/VargText.tsx +1 -0
  61. package/src/definitions/actions/grok-edit.ts +133 -0
  62. package/src/definitions/actions/index.ts +16 -0
  63. package/src/definitions/actions/qwen-angles.ts +218 -0
  64. package/src/index.ts +1 -0
  65. package/src/providers/fal.ts +196 -0
  66. package/src/react/assets.ts +9 -0
  67. package/src/react/elements.ts +0 -5
  68. package/src/react/examples/branching.tsx +6 -4
  69. package/src/react/examples/character-video.tsx +13 -10
  70. package/src/react/examples/local-files-test.tsx +19 -0
  71. package/src/react/examples/ltx2-test.tsx +25 -0
  72. package/src/react/examples/madi.tsx +13 -10
  73. package/src/react/examples/mcmeows.tsx +40 -0
  74. package/src/react/examples/music-defaults.tsx +24 -0
  75. package/src/react/examples/quickstart-test.tsx +101 -0
  76. package/src/react/examples/qwen-angles-test.tsx +72 -0
  77. package/src/react/index.ts +3 -3
  78. package/src/react/layouts/grid.tsx +1 -1
  79. package/src/react/layouts/index.ts +2 -1
  80. package/src/react/layouts/slot.tsx +85 -0
  81. package/src/react/layouts/split.tsx +18 -0
  82. package/src/react/react.test.ts +60 -11
  83. package/src/react/renderers/burn-captions.ts +95 -0
  84. package/src/react/renderers/cache.test.ts +182 -0
  85. package/src/react/renderers/captions.ts +25 -6
  86. package/src/react/renderers/clip.ts +56 -25
  87. package/src/react/renderers/context.ts +5 -2
  88. package/src/react/renderers/image.ts +5 -2
  89. package/src/react/renderers/index.ts +0 -1
  90. package/src/react/renderers/music.ts +8 -3
  91. package/src/react/renderers/packshot/blinking-button.ts +413 -0
  92. package/src/react/renderers/packshot.ts +170 -8
  93. package/src/react/renderers/progress.ts +4 -3
  94. package/src/react/renderers/render.ts +127 -71
  95. package/src/react/renderers/speech.ts +2 -2
  96. package/src/react/renderers/split.ts +34 -13
  97. package/src/react/renderers/utils.test.ts +80 -0
  98. package/src/react/renderers/utils.ts +37 -1
  99. package/src/react/renderers/video.ts +47 -9
  100. package/src/react/types.ts +70 -17
  101. package/src/studio/stages.ts +40 -39
  102. package/src/studio/step-renderer.ts +14 -24
  103. package/src/studio/ui/index.html +2 -2
  104. package/src/tests/all.test.ts +4 -4
  105. package/src/tests/index.ts +1 -1
  106. package/test-slot-grid.tsx +19 -0
  107. package/test-slot-userland.tsx +30 -0
  108. package/test-sync-v2.ts +30 -0
  109. package/test-sync-v2.tsx +29 -0
  110. package/tsconfig.json +1 -1
  111. package/video.tsx +7 -0
  112. package/src/ai-sdk/providers/editly/ffmpeg.ts +0 -60
  113. package/src/react/renderers/animate.ts +0 -59
  114. /package/src/cli/commands/{studio.tsx → studio.ts} +0 -0
@@ -1,94 +1,340 @@
1
+ /** @jsxImportSource react */
2
+
3
+ import { existsSync, mkdirSync } from "node:fs";
4
+ import { resolve } from "node:path";
1
5
  import { defineCommand } from "citty";
6
+ import { Box, Text } from "ink";
2
7
  import { render } from "../../react/render";
3
- import type { RenderMode, VargElement } from "../../react/types";
8
+ import type { DefaultModels, RenderMode, VargElement } from "../../react/types";
9
+ import { Header, HelpBlock, VargBox, VargText } from "../ui/index.ts";
10
+ import { renderStatic } from "../ui/render.ts";
11
+
12
+ const AUTO_IMPORTS = `/** @jsxImportSource vargai */
13
+ import { Captions, Clip, Image, Music, Overlay, Packshot, Render, Slider, Speech, Split, Subtitle, Swipe, TalkingHead, Title, Video, Grid, SplitLayout } from "vargai/react";
14
+ import { fal, elevenlabs, replicate } from "vargai/ai";
15
+ `;
16
+
17
+ async function detectDefaultModels(): Promise<DefaultModels | undefined> {
18
+ const defaults: DefaultModels = {};
19
+
20
+ const falKey = process.env.FAL_API_KEY ?? process.env.FAL_KEY;
21
+ if (falKey) {
22
+ const { fal } = await import("../../ai-sdk/providers/fal");
23
+ defaults.image = fal.imageModel("flux-schnell");
24
+ defaults.video = fal.videoModel("wan-2.5");
25
+ }
26
+
27
+ if (process.env.ELEVENLABS_API_KEY) {
28
+ const { elevenlabs } = await import("../../ai-sdk/providers/elevenlabs");
29
+ defaults.speech = elevenlabs.speechModel("eleven_multilingual_v2");
30
+ defaults.music = elevenlabs.musicModel("music_v1");
31
+ }
32
+
33
+ return Object.keys(defaults).length > 0 ? defaults : undefined;
34
+ }
35
+
36
+ async function loadComponent(filePath: string): Promise<VargElement> {
37
+ const resolvedPath = resolve(filePath);
38
+ const source = await Bun.file(resolvedPath).text();
39
+
40
+ const hasVargaiImport =
41
+ source.includes("from 'vargai") ||
42
+ source.includes('from "vargai') ||
43
+ source.includes("@jsxImportSource vargai");
44
+
45
+ const hasRelativeImport =
46
+ source.includes("from './") || source.includes('from "./');
47
+
48
+ const pkgDir = new URL("../../..", import.meta.url).pathname;
49
+ const tmpDir = `${pkgDir}/.cache/varg-render`;
50
+
51
+ if (!existsSync(tmpDir)) {
52
+ mkdirSync(tmpDir, { recursive: true });
53
+ }
54
+
55
+ if (hasRelativeImport) {
56
+ const mod = await import(resolvedPath);
57
+ return mod.default;
58
+ }
59
+
60
+ if (hasVargaiImport) {
61
+ const tmpFile = `${tmpDir}/${Date.now()}.tsx`;
62
+ await Bun.write(tmpFile, source);
63
+
64
+ try {
65
+ const mod = await import(tmpFile);
66
+ return mod.default;
67
+ } finally {
68
+ (await Bun.file(tmpFile).exists()) && (await Bun.write(tmpFile, ""));
69
+ }
70
+ }
71
+
72
+ const hasAnyImport = source.includes(" from ");
73
+ if (hasAnyImport) {
74
+ const mod = await import(resolvedPath);
75
+ return mod.default;
76
+ }
77
+
78
+ const tmpFile = `${tmpDir}/${Date.now()}.tsx`;
79
+ await Bun.write(tmpFile, AUTO_IMPORTS + source);
80
+
81
+ try {
82
+ const mod = await import(tmpFile);
83
+ return mod.default;
84
+ } finally {
85
+ (await Bun.file(tmpFile).exists()) && (await Bun.write(tmpFile, ""));
86
+ }
87
+ }
88
+
89
+ const sharedArgs = {
90
+ file: {
91
+ type: "positional" as const,
92
+ description: "component file (.tsx)",
93
+ required: true,
94
+ },
95
+ output: {
96
+ type: "string" as const,
97
+ alias: "o",
98
+ description: "output path",
99
+ },
100
+ cache: {
101
+ type: "string" as const,
102
+ alias: "c",
103
+ description: "cache directory",
104
+ default: ".cache/ai",
105
+ },
106
+ quiet: {
107
+ type: "boolean" as const,
108
+ alias: "q",
109
+ description: "minimal output",
110
+ default: false,
111
+ },
112
+ "no-cache": {
113
+ type: "boolean" as const,
114
+ description: "disable cache (don't read or write)",
115
+ default: false,
116
+ },
117
+ verbose: {
118
+ type: "boolean" as const,
119
+ alias: "v",
120
+ description: "show ffmpeg commands",
121
+ default: false,
122
+ },
123
+ open: {
124
+ type: "boolean" as const,
125
+ description: "open video after generation",
126
+ default: false,
127
+ },
128
+ };
129
+
130
+ async function runRender(
131
+ args: Record<string, unknown>,
132
+ mode: RenderMode,
133
+ commandName: string,
134
+ ) {
135
+ const file = args.file as string;
136
+
137
+ if (!file) {
138
+ console.error(`usage: varg ${commandName} <component.tsx> [-o output.mp4]`);
139
+ process.exit(1);
140
+ }
141
+
142
+ const component = await loadComponent(file);
143
+
144
+ if (!component || component.type !== "render") {
145
+ console.error("error: default export must be a <Render> element");
146
+ process.exit(1);
147
+ }
148
+
149
+ const basename = file
150
+ .replace(/\.tsx?$/, "")
151
+ .split("/")
152
+ .pop();
153
+ const outputPath = (args.output as string) ?? `output/${basename}.mp4`;
154
+
155
+ if (!args.quiet) {
156
+ const modeLabel = mode === "preview" ? " (fast)" : "";
157
+ console.log(`rendering ${file} → ${outputPath}${modeLabel}`);
158
+ }
159
+
160
+ const useCache = !args["no-cache"] && mode !== "preview";
161
+
162
+ const defaults = await detectDefaultModels();
163
+
164
+ const buffer = await render(component, {
165
+ output: outputPath,
166
+ cache: useCache ? (args.cache as string) : undefined,
167
+ mode,
168
+ defaults,
169
+ verbose: args.verbose as boolean,
170
+ });
171
+
172
+ if (!args.quiet) {
173
+ console.log(`done! ${buffer.byteLength} bytes → ${outputPath}`);
174
+ }
175
+
176
+ if (args.open) {
177
+ const { $ } = await import("bun");
178
+ await $`open ${outputPath}`.quiet();
179
+ }
180
+ }
4
181
 
5
182
  export const renderCmd = defineCommand({
6
183
  meta: {
7
184
  name: "render",
8
- description: "render a react component to video",
185
+ description: "render to video (strict mode - fails on errors)",
9
186
  },
10
- args: {
11
- file: {
12
- type: "positional",
13
- description: "component file (.tsx)",
14
- required: true,
15
- },
16
- output: {
17
- type: "string",
18
- alias: "o",
19
- description: "output path",
187
+ args: sharedArgs,
188
+ async run({ args }) {
189
+ await runRender(args, "strict", "render");
190
+ },
191
+ });
192
+
193
+ export const previewCmd = defineCommand({
194
+ meta: {
195
+ name: "preview",
196
+ description: "render with all placeholders (no generation)",
197
+ },
198
+ args: sharedArgs,
199
+ async run({ args }) {
200
+ await runRender(args, "preview", "preview");
201
+ },
202
+ });
203
+
204
+ function RenderHelpView() {
205
+ const examples = [
206
+ {
207
+ command: "varg render video.tsx",
208
+ description: "render component to output/video.mp4",
20
209
  },
21
- cache: {
22
- type: "string",
23
- alias: "c",
24
- description: "cache directory",
25
- default: ".cache/ai",
210
+ {
211
+ command: "varg render video.tsx -o my-video.mp4",
212
+ description: "custom output path",
26
213
  },
27
- quiet: {
28
- type: "boolean",
29
- alias: "q",
30
- description: "minimal output",
31
- default: false,
214
+ {
215
+ command: "varg preview video.tsx",
216
+ description: "fast preview with placeholders",
32
217
  },
33
- strict: {
34
- type: "boolean",
35
- description: "fail on provider errors (no fallback)",
36
- default: false,
218
+ ];
219
+
220
+ return (
221
+ <VargBox title="varg render">
222
+ <Box marginBottom={1}>
223
+ <Text>
224
+ render jsx components to video. the react engine for ai video.
225
+ </Text>
226
+ </Box>
227
+
228
+ <Header>USAGE</Header>
229
+ <Box paddingLeft={2} marginBottom={1}>
230
+ <VargText variant="accent">
231
+ varg render {"<file.tsx>"} [options]
232
+ </VargText>
233
+ </Box>
234
+
235
+ <Header>OPTIONS</Header>
236
+ <Box flexDirection="column" paddingLeft={2} marginBottom={1}>
237
+ <Text>
238
+ <VargText variant="accent">-o, --output </VargText>output path
239
+ (default: output/{"<name>"}.mp4)
240
+ </Text>
241
+ <Text>
242
+ <VargText variant="accent">-c, --cache </VargText>cache directory
243
+ (default: .cache/ai)
244
+ </Text>
245
+ <Text>
246
+ <VargText variant="accent">--no-cache </VargText>disable cache
247
+ </Text>
248
+ <Text>
249
+ <VargText variant="accent">-q, --quiet </VargText>minimal output
250
+ </Text>
251
+ <Text>
252
+ <VargText variant="accent">-v, --verbose </VargText>show ffmpeg
253
+ commands
254
+ </Text>
255
+ <Text>
256
+ <VargText variant="accent">--open </VargText>open video after
257
+ generation
258
+ </Text>
259
+ </Box>
260
+
261
+ <Header>COMPONENTS</Header>
262
+ <Box flexDirection="column" paddingLeft={2} marginBottom={1}>
263
+ <Text>{"<Render>"} root container (width, height, fps)</Text>
264
+ <Text>{"<Clip>"} time segment with duration</Text>
265
+ <Text>{"<Video>"} ai-generated or source video</Text>
266
+ <Text>{"<Image>"} ai-generated or static image</Text>
267
+ <Text>{"<Speech>"} text-to-speech audio</Text>
268
+ <Text>{"<Music>"} background music</Text>
269
+ <Text>{"<Captions>"} auto-generated subtitles</Text>
270
+ </Box>
271
+
272
+ <Header>EXAMPLES</Header>
273
+ <Box marginTop={1}>
274
+ <HelpBlock examples={examples} />
275
+ </Box>
276
+ </VargBox>
277
+ );
278
+ }
279
+
280
+ function PreviewHelpView() {
281
+ const examples = [
282
+ {
283
+ command: "varg preview video.tsx",
284
+ description: "quick test without ai calls",
37
285
  },
38
- preview: {
39
- type: "boolean",
40
- description: "skip all generation, use placeholders only",
41
- default: false,
286
+ {
287
+ command: "varg preview video.tsx -o test.mp4",
288
+ description: "preview to custom path",
42
289
  },
43
- },
44
- async run({ args }) {
45
- const file = args.file as string;
290
+ ];
46
291
 
47
- if (!file) {
48
- console.error("usage: varg render <component.tsx> [-o output.mp4]");
49
- process.exit(1);
50
- }
292
+ return (
293
+ <VargBox title="varg preview">
294
+ <Box marginBottom={1}>
295
+ <Text>
296
+ fast preview mode - uses placeholders instead of ai generation.
297
+ </Text>
298
+ </Box>
51
299
 
52
- const resolvedPath = Bun.resolveSync(file, process.cwd());
53
- const mod = await import(resolvedPath);
54
- const component: VargElement = mod.default;
300
+ <Header>USAGE</Header>
301
+ <Box paddingLeft={2} marginBottom={1}>
302
+ <VargText variant="accent">
303
+ varg preview {"<file.tsx>"} [options]
304
+ </VargText>
305
+ </Box>
55
306
 
56
- if (!component || component.type !== "render") {
57
- console.error("error: default export must be a <Render> element");
58
- process.exit(1);
59
- }
307
+ <Header>OPTIONS</Header>
308
+ <Box flexDirection="column" paddingLeft={2} marginBottom={1}>
309
+ <Text>
310
+ <VargText variant="accent">-o, --output </VargText>output path
311
+ (default: output/{"<name>"}.mp4)
312
+ </Text>
313
+ <Text>
314
+ <VargText variant="accent">-q, --quiet </VargText>minimal output
315
+ </Text>
316
+ <Text>
317
+ <VargText variant="accent">-v, --verbose </VargText>show ffmpeg
318
+ commands
319
+ </Text>
320
+ <Text>
321
+ <VargText variant="accent">--open </VargText>open video after
322
+ generation
323
+ </Text>
324
+ </Box>
60
325
 
61
- const outputPath =
62
- args.output ??
63
- `output/${file
64
- .replace(/\.tsx?$/, "")
65
- .split("/")
66
- .pop()}.mp4`;
67
-
68
- const mode: RenderMode = args.strict
69
- ? "strict"
70
- : args.preview
71
- ? "preview"
72
- : "default";
73
-
74
- if (!args.quiet) {
75
- const modeLabel =
76
- mode === "preview"
77
- ? " (preview)"
78
- : mode === "strict"
79
- ? " (strict)"
80
- : "";
81
- console.log(`rendering ${file} → ${outputPath}${modeLabel}`);
82
- }
326
+ <Header>EXAMPLES</Header>
327
+ <Box marginTop={1}>
328
+ <HelpBlock examples={examples} />
329
+ </Box>
330
+ </VargBox>
331
+ );
332
+ }
83
333
 
84
- const buffer = await render(component, {
85
- output: outputPath,
86
- cache: args.cache,
87
- mode,
88
- });
334
+ export function showRenderHelp() {
335
+ renderStatic(<RenderHelpView />);
336
+ }
89
337
 
90
- if (!args.quiet) {
91
- console.log(`done! ${buffer.byteLength} bytes → ${outputPath}`);
92
- }
93
- },
94
- });
338
+ export function showPreviewHelp() {
339
+ renderStatic(<PreviewHelpView />);
340
+ }
@@ -1,3 +1,4 @@
1
+ /** @jsxImportSource react */
1
2
  /**
2
3
  * varg run command
3
4
  * Ink-based execution with live status updates