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
@@ -7,6 +7,11 @@ import { fal } from "@fal-ai/client";
7
7
  import type { JobStatusUpdate, ProviderConfig } from "../core/schema/types";
8
8
  import { BaseProvider, ensureUrl } from "./base";
9
9
 
10
+ const falApiKey = process.env.FAL_API_KEY ?? process.env.FAL_KEY;
11
+ if (falApiKey) {
12
+ fal.config({ credentials: falApiKey });
13
+ }
14
+
10
15
  export class FalProvider extends BaseProvider {
11
16
  readonly name = "fal";
12
17
 
@@ -369,6 +374,197 @@ export class FalProvider extends BaseProvider {
369
374
  console.log("[fal] completed!");
370
375
  return result;
371
376
  }
377
+
378
+ // ============================================================================
379
+ // Grok Imagine Video methods (xAI)
380
+ // ============================================================================
381
+
382
+ /**
383
+ * Generate video from text using Grok Imagine Video
384
+ * Supports 1-15 second videos at 480p or 720p resolution
385
+ */
386
+ async grokTextToVideo(args: {
387
+ prompt: string;
388
+ duration?: number;
389
+ aspectRatio?: "16:9" | "4:3" | "3:2" | "1:1" | "2:3" | "3:4" | "9:16";
390
+ resolution?: "480p" | "720p";
391
+ }) {
392
+ const modelId = "xai/grok-imagine-video/text-to-video";
393
+
394
+ console.log(`[fal] starting grok text-to-video: ${modelId}`);
395
+ console.log(`[fal] prompt: ${args.prompt}`);
396
+
397
+ const result = await fal.subscribe(modelId, {
398
+ input: {
399
+ prompt: args.prompt,
400
+ duration: args.duration ?? 6,
401
+ aspect_ratio: args.aspectRatio ?? "16:9",
402
+ resolution: args.resolution ?? "720p",
403
+ },
404
+ logs: true,
405
+ onQueueUpdate: (update) => {
406
+ if (update.status === "IN_PROGRESS") {
407
+ console.log(
408
+ `[fal] ${update.logs?.map((l) => l.message).join(" ") || "processing..."}`,
409
+ );
410
+ }
411
+ },
412
+ });
413
+
414
+ console.log("[fal] completed!");
415
+ return result;
416
+ }
417
+
418
+ /**
419
+ * Generate video from image using Grok Imagine Video
420
+ * Supports 1-15 second videos at 480p or 720p resolution
421
+ */
422
+ async grokImageToVideo(args: {
423
+ prompt: string;
424
+ imageUrl: string;
425
+ duration?: number;
426
+ aspectRatio?:
427
+ | "auto"
428
+ | "16:9"
429
+ | "4:3"
430
+ | "3:2"
431
+ | "1:1"
432
+ | "2:3"
433
+ | "3:4"
434
+ | "9:16";
435
+ resolution?: "480p" | "720p";
436
+ }) {
437
+ const modelId = "xai/grok-imagine-video/image-to-video";
438
+
439
+ console.log(`[fal] starting grok image-to-video: ${modelId}`);
440
+ console.log(`[fal] prompt: ${args.prompt}`);
441
+
442
+ const imageUrl = await ensureUrl(args.imageUrl, (buffer) =>
443
+ this.uploadFile(buffer),
444
+ );
445
+
446
+ const result = await fal.subscribe(modelId, {
447
+ input: {
448
+ prompt: args.prompt,
449
+ image_url: imageUrl,
450
+ duration: args.duration ?? 6,
451
+ aspect_ratio: args.aspectRatio ?? "auto",
452
+ resolution: args.resolution ?? "720p",
453
+ },
454
+ logs: true,
455
+ onQueueUpdate: (update) => {
456
+ if (update.status === "IN_PROGRESS") {
457
+ console.log(
458
+ `[fal] ${update.logs?.map((l) => l.message).join(" ") || "processing..."}`,
459
+ );
460
+ }
461
+ },
462
+ });
463
+
464
+ console.log("[fal] completed!");
465
+ return result;
466
+ }
467
+
468
+ /**
469
+ * Edit video using Grok Imagine Video
470
+ * Video will be resized to max 854x480 and truncated to 8 seconds
471
+ */
472
+ async grokEditVideo(args: {
473
+ prompt: string;
474
+ videoUrl: string;
475
+ resolution?: "auto" | "480p" | "720p";
476
+ }) {
477
+ const modelId = "xai/grok-imagine-video/edit-video";
478
+
479
+ console.log(`[fal] starting grok edit-video: ${modelId}`);
480
+ console.log(`[fal] prompt: ${args.prompt}`);
481
+
482
+ const videoUrl = await ensureUrl(args.videoUrl, (buffer) =>
483
+ this.uploadFile(buffer),
484
+ );
485
+
486
+ const result = await fal.subscribe(modelId, {
487
+ input: {
488
+ prompt: args.prompt,
489
+ video_url: videoUrl,
490
+ resolution: args.resolution ?? "auto",
491
+ },
492
+ logs: true,
493
+ onQueueUpdate: (update) => {
494
+ if (update.status === "IN_PROGRESS") {
495
+ console.log(
496
+ `[fal] ${update.logs?.map((l) => l.message).join(" ") || "processing..."}`,
497
+ );
498
+ }
499
+ },
500
+ });
501
+
502
+ console.log("[fal] completed!");
503
+ return result;
504
+ }
505
+
506
+ // ============================================================================
507
+ // Qwen Image Edit 2511 Multiple Angles
508
+ // ============================================================================
509
+
510
+ /**
511
+ * Adjust camera angle of an image using Qwen Image Edit 2511 Multiple Angles
512
+ * Generates same scene from different angles (azimuth/elevation)
513
+ */
514
+ async qwenMultipleAngles(args: {
515
+ imageUrl: string;
516
+ horizontalAngle?: number;
517
+ verticalAngle?: number;
518
+ zoom?: number;
519
+ additionalPrompt?: string;
520
+ loraScale?: number;
521
+ imageSize?: string | { width: number; height: number };
522
+ guidanceScale?: number;
523
+ numInferenceSteps?: number;
524
+ acceleration?: "none" | "regular";
525
+ negativePrompt?: string;
526
+ seed?: number;
527
+ outputFormat?: "png" | "jpeg" | "webp";
528
+ numImages?: number;
529
+ }) {
530
+ const modelId = "fal-ai/qwen-image-edit-2511-multiple-angles";
531
+
532
+ console.log(`[fal] starting qwen multiple angles: ${modelId}`);
533
+
534
+ const imageUrl = await ensureUrl(args.imageUrl, (buffer) =>
535
+ this.uploadFile(buffer),
536
+ );
537
+
538
+ const result = await fal.subscribe(modelId, {
539
+ input: {
540
+ image_urls: [imageUrl],
541
+ horizontal_angle: args.horizontalAngle ?? 0,
542
+ vertical_angle: args.verticalAngle ?? 0,
543
+ zoom: args.zoom ?? 5,
544
+ additional_prompt: args.additionalPrompt,
545
+ lora_scale: args.loraScale ?? 1,
546
+ image_size: args.imageSize,
547
+ guidance_scale: args.guidanceScale ?? 4.5,
548
+ num_inference_steps: args.numInferenceSteps ?? 28,
549
+ acceleration: args.acceleration ?? "regular",
550
+ negative_prompt: args.negativePrompt ?? "",
551
+ seed: args.seed,
552
+ output_format: args.outputFormat ?? "png",
553
+ num_images: args.numImages ?? 1,
554
+ },
555
+ logs: true,
556
+ onQueueUpdate: (update) => {
557
+ if (update.status === "IN_PROGRESS") {
558
+ console.log(
559
+ `[fal] ${update.logs?.map((l) => l.message).join(" ") || "processing..."}`,
560
+ );
561
+ }
562
+ },
563
+ });
564
+
565
+ console.log("[fal] completed!");
566
+ return result;
567
+ }
372
568
  }
373
569
 
374
570
  // Export singleton instance
@@ -0,0 +1,9 @@
1
+ export const assets = {
2
+ characters: {
3
+ orangeGirl: "https://s3.varg.ai/uploads/images/1_0475e227.png",
4
+ },
5
+ backgrounds: {
6
+ orangeGradient:
7
+ "https://s3.varg.ai/uploads/images/xyearp51qvve-zi3nrcve-zbno2hfgt5gergjrof_995f553d.png",
8
+ },
9
+ } as const;
@@ -1,5 +1,4 @@
1
1
  import type {
2
- AnimateProps,
3
2
  CaptionsProps,
4
3
  ClipProps,
5
4
  ImageProps,
@@ -71,10 +70,6 @@ export function Video(props: VideoProps): VargElement<"video"> {
71
70
  return createElement("video", props as Record<string, unknown>, undefined);
72
71
  }
73
72
 
74
- export function Animate(props: AnimateProps): VargElement<"animate"> {
75
- return createElement("animate", props as Record<string, unknown>, undefined);
76
- }
77
-
78
73
  export function Speech(props: SpeechProps): VargElement<"speech"> {
79
74
  return createElement(
80
75
  "speech",
@@ -1,6 +1,6 @@
1
1
  import { elevenlabs } from "../../ai-sdk/providers/elevenlabs";
2
2
  import { fal } from "../../ai-sdk/providers/fal";
3
- import { Animate, Clip, Image, Render, Speech, Title } from "..";
3
+ import { Clip, Image, Render, Speech, Title, Video } from "..";
4
4
 
5
5
  // Non-linear tree: multiple clips with independent branches
6
6
  // Clip 1: TalkingHead (Image -> Animate + Speech)
@@ -35,10 +35,12 @@ export default (
35
35
  <Render width={1080} height={1920}>
36
36
  {/* Clip 1: Talking head intro */}
37
37
  <Clip duration={5}>
38
- <Animate
39
- image={character}
38
+ <Video
39
+ prompt={{
40
+ text: "talking naturally, slight head movements, friendly expression",
41
+ images: [character],
42
+ }}
40
43
  model={fal.videoModel("wan-2.5")}
41
- motion="talking naturally, slight head movements, friendly expression"
42
44
  />
43
45
  <Speech voice="adam" model={elevenlabs.speechModel("turbo")}>
44
46
  Hey everyone! Today we're looking at the biggest smartphone upgrade of
@@ -1,6 +1,6 @@
1
1
  import { elevenlabs } from "../../ai-sdk/providers/elevenlabs";
2
2
  import { fal } from "../../ai-sdk/providers/fal";
3
- import { Animate, Clip, Image, Music, Render, render } from "..";
3
+ import { Clip, Image, Music, Render, render, Video } from "..";
4
4
 
5
5
  const MADI_REF =
6
6
  "https://s3.varg.ai/fellowers/madi/character_shots/madi_shot_03_closeup.png";
@@ -54,16 +54,19 @@ async function main() {
54
54
 
55
55
  {SCENES.map((scene) => (
56
56
  <Clip key={scene.prompt} duration={2}>
57
- <Animate
58
- image={Image({
59
- prompt: { text: scene.prompt, images: [MADI_REF] },
60
- model: fal.imageModel("nano-banana-pro/edit"),
61
- aspectRatio: "9:16",
62
- resize: "cover",
63
- })}
64
- motion={scene.motion}
57
+ <Video
58
+ prompt={{
59
+ text: scene.motion,
60
+ images: [
61
+ Image({
62
+ prompt: { text: scene.prompt, images: [MADI_REF] },
63
+ model: fal.imageModel("nano-banana-pro/edit"),
64
+ aspectRatio: "9:16",
65
+ resize: "cover",
66
+ }),
67
+ ],
68
+ }}
65
69
  model={fal.videoModel("wan-2.5")}
66
- duration={5}
67
70
  />
68
71
  </Clip>
69
72
  ))}
@@ -0,0 +1,19 @@
1
+ import { fal } from "../../ai-sdk/providers/fal";
2
+ import { Clip, Image, Render, Video } from "..";
3
+
4
+ export default (
5
+ <Render width={1080} height={1920}>
6
+ <Clip duration={3}>
7
+ <Image src="media/cyberpunk-street.png" />
8
+ </Clip>
9
+ <Clip duration={3}>
10
+ <Video
11
+ prompt={{
12
+ text: "camera pans across the scene",
13
+ images: [Image({ src: "media/fal-coffee-shop.png" })],
14
+ }}
15
+ model={fal.videoModel("kling-v2.5")}
16
+ />
17
+ </Clip>
18
+ </Render>
19
+ );
@@ -0,0 +1,25 @@
1
+ import { fal } from "../../ai-sdk/providers/fal";
2
+ import { Clip, Render, Video } from "..";
3
+
4
+ export default (
5
+ <Render width={1248} height={704}>
6
+ <Clip>
7
+ <Video
8
+ prompt={{
9
+ text: "Camera slowly dollies in toward her face, city lights flicker",
10
+ images: [
11
+ "https://storage.googleapis.com/falserverless/example_inputs/ltxv-2-i2v-input.jpg",
12
+ ],
13
+ }}
14
+ model={fal.videoModel("ltx-2-19b-distilled")}
15
+ keepAudio
16
+ providerOptions={{
17
+ fal: {
18
+ generate_audio: true,
19
+ camera_lora: "dolly_in",
20
+ },
21
+ }}
22
+ />
23
+ </Clip>
24
+ </Render>
25
+ );
@@ -1,6 +1,6 @@
1
1
  import { elevenlabs } from "../../ai-sdk/providers/elevenlabs";
2
2
  import { fal } from "../../ai-sdk/providers/fal";
3
- import { Animate, Clip, Image, Music, Render } from "..";
3
+ import { Clip, Image, Music, Render, Video } from "..";
4
4
 
5
5
  const MADI_REF =
6
6
  "https://s3.varg.ai/fellowers/madi/character_shots/madi_shot_03_closeup.png";
@@ -43,16 +43,19 @@ export default (
43
43
 
44
44
  {SCENES.map((scene) => (
45
45
  <Clip key={scene.prompt} duration={2}>
46
- <Animate
47
- image={Image({
48
- prompt: { text: scene.prompt, images: [MADI_REF] },
49
- model: fal.imageModel("nano-banana-pro/edit"),
50
- aspectRatio: "9:16",
51
- resize: "cover",
52
- })}
53
- motion={scene.motion}
46
+ <Video
47
+ prompt={{
48
+ text: scene.motion,
49
+ images: [
50
+ Image({
51
+ prompt: { text: scene.prompt, images: [MADI_REF] },
52
+ model: fal.imageModel("nano-banana-pro/edit"),
53
+ aspectRatio: "9:16",
54
+ resize: "cover",
55
+ }),
56
+ ],
57
+ }}
54
58
  model={fal.videoModel("wan-2.5")}
55
- duration={5}
56
59
  />
57
60
  </Clip>
58
61
  ))}
@@ -0,0 +1,40 @@
1
+ import { elevenlabs } from "../../ai-sdk/providers/elevenlabs";
2
+ import { fal } from "../../ai-sdk/providers/fal";
3
+ import { Clip, Music, Render, render, Title, Video } from "..";
4
+
5
+ export default (
6
+ <Render>
7
+ <Clip duration={4}>
8
+ <Video prompt="A sophisticated tabby cat wearing a tailored tiny business suit and small round glasses, standing upright at a McDonald's counter. One paw raised pointing assertively at the illuminated menu board above. The cat has an extremely serious, concentrated expression with furrowed brows. Cinematic lighting with warm McDonald's interior ambiance, shallow depth of field focusing on the cat's determined face, photorealistic fur texture with fine detail. Professional business atmosphere meets fast food chaos." />
9
+ </Clip>
10
+
11
+ <Clip duration={4}>
12
+ <Video prompt="Absolute mayhem - four to five cats in a chaotic pile fight. Orange tabby, black and white tuxedo cat, calico with patches, and fluffy gray cat all scrambling, paws flailing, tumbling over each other in exaggerated cartoon-style motion. They're all desperately reaching for a single perfect golden McDonald's french fry sitting on a red plastic tray in the center. Wide-eyed expressions, mouths open mid-meow, dynamic motion blur, comedic timing. Fast food restaurant background slightly blurred. Playful, over-the-top energy." />
13
+ </Clip>
14
+
15
+ <Clip duration={3}>
16
+ <Video prompt="Proud orange tabby cat wearing an official McDonald's crew member visor and name tag, standing upright behind a restaurant register counter. Front paws crossed confidently across chest, chin lifted with a smug, self-satisfied expression. Perfect posture, professional demeanor. Bright McDonald's interior lighting, red and yellow color scheme in background. The cat radiates 'employee of the month' energy. Crisp, clean, professional fast food aesthetic." />
17
+ <Title position="bottom">McMeow's: NOW HIRING</Title>
18
+ </Clip>
19
+
20
+ <Music
21
+ model={elevenlabs.musicModel()}
22
+ prompt="playful upbeat comedy music with quirky pizzicato strings and light percussion, funny corporate training video vibes"
23
+ duration={11}
24
+ />
25
+ </Render>
26
+ );
27
+
28
+ async function main() {
29
+ const component = await import("./mcmeows.tsx").then((m) => m.default);
30
+ await render(component, {
31
+ output: "output/mcmeows.mp4",
32
+ cache: ".cache/ai",
33
+ verbose: true,
34
+ defaults: {
35
+ video: fal.videoModel("wan-2.5"),
36
+ },
37
+ });
38
+ }
39
+
40
+ main().catch(console.error);
@@ -0,0 +1,24 @@
1
+ import { elevenlabs } from "../../ai-sdk/providers/elevenlabs";
2
+ import { Clip, Image, Music, Render, render } from "..";
3
+
4
+ export default (
5
+ <Render width={1920} height={1080}>
6
+ <Clip duration={5}>
7
+ <Image src="media/cyberpunk-street.png" />
8
+ </Clip>
9
+ <Music prompt="calm ambient electronic music" duration={5} />
10
+ </Render>
11
+ );
12
+
13
+ async function main() {
14
+ const component = await import("./music-defaults.tsx").then((m) => m.default);
15
+ await render(component, {
16
+ output: "output/music-defaults.mp4",
17
+ defaults: {
18
+ music: elevenlabs.musicModel(),
19
+ },
20
+ });
21
+ console.log("done! check output/music-defaults.mp4");
22
+ }
23
+
24
+ main().catch(console.error);
@@ -0,0 +1,101 @@
1
+ /**
2
+ * Quickstart Test - Verification script for video generation setup
3
+ *
4
+ * This minimal test confirms your FAL_API_KEY is working correctly.
5
+ * It generates a simple 3-second animated image.
6
+ *
7
+ * Run: bun run src/react/examples/quickstart-test.tsx
8
+ * Output: output/quickstart-test.mp4
9
+ */
10
+
11
+ import { fal } from "../../ai-sdk/providers/fal";
12
+ import { Clip, Image, Render, render, Video } from "..";
13
+
14
+ const quickstartVideo = (
15
+ <Render width={720} height={720}>
16
+ <Clip duration={3}>
17
+ <Video
18
+ prompt={{
19
+ text: "robot waves hello, friendly gesture, slight head tilt",
20
+ images: [
21
+ Image({
22
+ prompt:
23
+ "a friendly robot waving hello, simple cartoon style, blue and white colors, clean background",
24
+ model: fal.imageModel("flux-schnell"),
25
+ aspectRatio: "1:1",
26
+ }),
27
+ ],
28
+ }}
29
+ model={fal.videoModel("wan-2.5")}
30
+ />
31
+ </Clip>
32
+ </Render>
33
+ );
34
+
35
+ export default quickstartVideo;
36
+
37
+ async function main() {
38
+ console.log("=== Varg Video Generation - Setup Verification ===\n");
39
+
40
+ // Check for FAL_API_KEY
41
+ if (!process.env.FAL_API_KEY) {
42
+ console.error("ERROR: FAL_API_KEY not found in environment");
43
+ console.error("\nTo fix this:");
44
+ console.error("1. Get an API key at: https://fal.ai/dashboard/keys");
45
+ console.error("2. Add to .env file: FAL_API_KEY=fal_xxxxx");
46
+ console.error("3. Run this test again");
47
+ process.exit(1);
48
+ }
49
+
50
+ console.log("FAL_API_KEY found");
51
+
52
+ // Check for optional keys
53
+ const hasElevenLabs = !!process.env.ELEVENLABS_API_KEY;
54
+ const hasReplicate = !!process.env.REPLICATE_API_TOKEN;
55
+ const hasGroq = !!process.env.GROQ_API_KEY;
56
+
57
+ console.log("\nOptional keys:");
58
+ console.log(
59
+ ` ELEVENLABS_API_KEY: ${hasElevenLabs ? "found" : "not found (music/voice unavailable)"}`,
60
+ );
61
+ console.log(
62
+ ` REPLICATE_API_TOKEN: ${hasReplicate ? "found" : "not found (lipsync unavailable)"}`,
63
+ );
64
+ console.log(
65
+ ` GROQ_API_KEY: ${hasGroq ? "found" : "not found (transcription unavailable)"}`,
66
+ );
67
+
68
+ console.log("\n--- Running verification test ---\n");
69
+ console.log("Generating a simple 3-second animation...");
70
+ console.log("This may take 30-60 seconds on first run.\n");
71
+
72
+ try {
73
+ const buffer = await render(quickstartVideo, {
74
+ output: "output/quickstart-test.mp4",
75
+ cache: ".cache/ai",
76
+ });
77
+
78
+ console.log("\n=== SUCCESS ===");
79
+ console.log(
80
+ `Output: output/quickstart-test.mp4 (${(buffer.byteLength / 1024 / 1024).toFixed(2)} MB)`,
81
+ );
82
+ console.log("\nYour setup is working! You can now:");
83
+ console.log("1. Try the templates in .claude/skills/video-generation.md");
84
+ console.log(
85
+ "2. Run existing examples: bun run src/react/examples/madi.tsx",
86
+ );
87
+ console.log("3. Create your own videos using the React engine");
88
+ } catch (error) {
89
+ console.error("\n=== VERIFICATION FAILED ===");
90
+ console.error("Error:", error instanceof Error ? error.message : error);
91
+ console.error("\nCommon fixes:");
92
+ console.error("- Check FAL_API_KEY is correct (no extra spaces)");
93
+ console.error("- Ensure you have credits at fal.ai");
94
+ console.error("- Try running: bun install");
95
+ process.exit(1);
96
+ }
97
+ }
98
+
99
+ if (import.meta.main) {
100
+ main();
101
+ }
@@ -0,0 +1,72 @@
1
+ /**
2
+ * Qwen Image Edit 2511 Multiple Angles Test
3
+ *
4
+ * Demonstrates camera angle adjustment using Qwen model.
5
+ * Generates the same scene from different perspectives (azimuth/elevation).
6
+ *
7
+ * Run with: bun run src/cli/index.ts render src/react/examples/qwen-angles-test.tsx
8
+ */
9
+
10
+ import { fal } from "../../ai-sdk/providers/fal";
11
+ import { Clip, Image, Render } from "..";
12
+
13
+ // Source image - replace with your own image URL or local path
14
+ const sourceImage =
15
+ "https://v3b.fal.media/files/b/0a8973cb/qUbVwDCcMlvX4drBGYB1H.png";
16
+
17
+ export default (
18
+ <Render width={1024} height={1024}>
19
+ {/* Original image - front view */}
20
+ <Clip duration={2}>
21
+ <Image src={sourceImage} resize="cover" />
22
+ </Clip>
23
+
24
+ {/* Right side view (90 degrees horizontal) */}
25
+ <Clip duration={2}>
26
+ <Image
27
+ prompt={{ text: "", images: [sourceImage] }}
28
+ model={fal.imageModel("qwen-angles")}
29
+ aspectRatio="1:1"
30
+ providerOptions={{
31
+ fal: {
32
+ horizontal_angle: 90,
33
+ vertical_angle: 0,
34
+ zoom: 5,
35
+ },
36
+ }}
37
+ />
38
+ </Clip>
39
+
40
+ {/* Bird's eye view (60 degrees vertical) */}
41
+ <Clip duration={2}>
42
+ <Image
43
+ prompt={{ text: "", images: [sourceImage] }}
44
+ model={fal.imageModel("qwen-angles")}
45
+ aspectRatio="1:1"
46
+ providerOptions={{
47
+ fal: {
48
+ horizontal_angle: 0,
49
+ vertical_angle: 60,
50
+ zoom: 5,
51
+ },
52
+ }}
53
+ />
54
+ </Clip>
55
+
56
+ {/* Close-up from 45 degree angle */}
57
+ <Clip duration={2}>
58
+ <Image
59
+ prompt={{ text: "", images: [sourceImage] }}
60
+ model={fal.imageModel("qwen-angles")}
61
+ aspectRatio="1:1"
62
+ providerOptions={{
63
+ fal: {
64
+ horizontal_angle: 45,
65
+ vertical_angle: 30,
66
+ zoom: 8,
67
+ },
68
+ }}
69
+ />
70
+ </Clip>
71
+ </Render>
72
+ );
@@ -1,6 +1,7 @@
1
+ export type { CacheStorage } from "../ai-sdk/cache";
1
2
  export type { SizeValue } from "../ai-sdk/providers/editly/types";
3
+ export { assets } from "./assets";
2
4
  export {
3
- Animate,
4
5
  Captions,
5
6
  Clip,
6
7
  Image,
@@ -17,10 +18,9 @@ export {
17
18
  Title,
18
19
  Video,
19
20
  } from "./elements";
20
- export { Grid, SplitLayout } from "./layouts";
21
+ export { Grid, Slot, SplitLayout } from "./layouts";
21
22
  export { render, renderStream } from "./render";
22
23
  export type {
23
- AnimateProps,
24
24
  CaptionsProps,
25
25
  ClipProps,
26
26
  ImageProps,
@@ -21,7 +21,7 @@ export const Grid = ({
21
21
  top: `${(Math.floor(i / cols) / rowCount) * 100}%`,
22
22
  width: `${(1 / cols) * 100}%`,
23
23
  height: `${(1 / rowCount) * 100}%`,
24
- resize,
24
+ resize: (el.props as Record<string, unknown>).resize ?? resize,
25
25
  },
26
26
  }));
27
27
  return <>{positioned}</>;
@@ -1,2 +1,3 @@
1
1
  export { Grid } from "./grid";
2
- export { SplitLayout } from "./split";
2
+ export { Slot } from "./slot";
3
+ export { Split, SplitLayout } from "./split";