vargai 0.4.0-alpha3 → 0.4.0-alpha5

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/package.json CHANGED
@@ -63,7 +63,7 @@
63
63
  "replicate": "^1.4.0",
64
64
  "zod": "^4.2.1"
65
65
  },
66
- "version": "0.4.0-alpha3",
66
+ "version": "0.4.0-alpha5",
67
67
  "exports": {
68
68
  ".": "./src/index.ts",
69
69
  "./ai": "./src/ai-sdk/index.ts",
@@ -3,4 +3,5 @@ export { helpCmd, showHelp } from "./help.tsx";
3
3
  export { listCmd, showListHelp } from "./list.tsx";
4
4
  export { renderCmd } from "./render.tsx";
5
5
  export { runCmd, showRunHelp, showTargetHelp } from "./run.tsx";
6
+ export { studioCmd } from "./studio.tsx";
6
7
  export { showWhichHelp, whichCmd } from "./which.tsx";
@@ -1,7 +1,44 @@
1
+ import { existsSync, mkdirSync } from "node:fs";
2
+ import { dirname, resolve } from "node:path";
1
3
  import { defineCommand } from "citty";
2
4
  import { render } from "../../react/render";
3
5
  import type { RenderMode, VargElement } from "../../react/types";
4
6
 
7
+ const AUTO_IMPORTS = `import { Animate, Captions, Clip, Image, Music, Overlay, Packshot, Render, Slider, Speech, Split, Subtitle, Swipe, TalkingHead, Title, Video, Grid, SplitLayout } from "vargai/react";
8
+ import { fal, elevenlabs, replicate } from "vargai/ai";
9
+ `;
10
+
11
+ async function loadComponent(filePath: string): Promise<VargElement> {
12
+ const resolvedPath = resolve(filePath);
13
+ const source = await Bun.file(resolvedPath).text();
14
+
15
+ const hasImports =
16
+ source.includes("from 'vargai") ||
17
+ source.includes('from "vargai') ||
18
+ source.includes("from '@vargai") ||
19
+ source.includes('from "@vargai');
20
+
21
+ if (hasImports) {
22
+ const mod = await import(resolvedPath);
23
+ return mod.default;
24
+ }
25
+
26
+ const tmpDir = ".cache/varg-render";
27
+ if (!existsSync(tmpDir)) {
28
+ mkdirSync(tmpDir, { recursive: true });
29
+ }
30
+
31
+ const tmpFile = `${tmpDir}/${Date.now()}.tsx`;
32
+ await Bun.write(tmpFile, AUTO_IMPORTS + source);
33
+
34
+ try {
35
+ const mod = await import(resolve(tmpFile));
36
+ return mod.default;
37
+ } finally {
38
+ (await Bun.file(tmpFile).exists()) && (await Bun.write(tmpFile, ""));
39
+ }
40
+ }
41
+
5
42
  export const renderCmd = defineCommand({
6
43
  meta: {
7
44
  name: "render",
@@ -49,21 +86,18 @@ export const renderCmd = defineCommand({
49
86
  process.exit(1);
50
87
  }
51
88
 
52
- const resolvedPath = Bun.resolveSync(file, process.cwd());
53
- const mod = await import(resolvedPath);
54
- const component: VargElement = mod.default;
89
+ const component = await loadComponent(file);
55
90
 
56
91
  if (!component || component.type !== "render") {
57
92
  console.error("error: default export must be a <Render> element");
58
93
  process.exit(1);
59
94
  }
60
95
 
61
- const outputPath =
62
- args.output ??
63
- `output/${file
64
- .replace(/\.tsx?$/, "")
65
- .split("/")
66
- .pop()}.mp4`;
96
+ const basename = file
97
+ .replace(/\.tsx?$/, "")
98
+ .split("/")
99
+ .pop();
100
+ const outputPath = args.output ?? `output/${basename}.mp4`;
67
101
 
68
102
  const mode: RenderMode = args.strict
69
103
  ? "strict"
@@ -0,0 +1,47 @@
1
+ import { resolve } from "node:path";
2
+ import { defineCommand } from "citty";
3
+ import { createStudioServer } from "../../studio/server";
4
+
5
+ export const studioCmd = defineCommand({
6
+ meta: {
7
+ name: "studio",
8
+ description: "launch varg studio - visual editor for workflows",
9
+ },
10
+ args: {
11
+ file: {
12
+ type: "positional",
13
+ description: "initial file to open",
14
+ required: false,
15
+ },
16
+ cache: {
17
+ type: "string",
18
+ description: "cache directory",
19
+ default: ".cache/ai",
20
+ },
21
+ port: {
22
+ type: "string",
23
+ description: "port to run on",
24
+ default: "8282",
25
+ },
26
+ },
27
+ run: async ({ args }) => {
28
+ const initialFile = args.file ? resolve(args.file) : undefined;
29
+ const cacheDir = args.cache;
30
+ const port = Number.parseInt(args.port, 10);
31
+
32
+ console.log("varg studio starting...");
33
+ console.log(`cache folder: ${cacheDir}`);
34
+ if (initialFile) {
35
+ console.log(`initial file: ${initialFile}`);
36
+ }
37
+
38
+ const server = createStudioServer({ cacheDir, initialFile, port });
39
+
40
+ console.log(`\nopen http://localhost:${server.port}`);
41
+ console.log(" /editor - code editor");
42
+ console.log(" /cache - cache viewer");
43
+
44
+ // Keep process alive
45
+ await new Promise(() => {});
46
+ },
47
+ });
package/src/cli/index.ts CHANGED
@@ -23,6 +23,7 @@ import {
23
23
  showRunHelp,
24
24
  showTargetHelp,
25
25
  showWhichHelp,
26
+ studioCmd,
26
27
  whichCmd,
27
28
  } from "./commands";
28
29
 
@@ -92,15 +93,18 @@ if (hasHelp) {
92
93
  }
93
94
  }
94
95
 
96
+ const pkg = await import("../../package.json");
97
+
95
98
  const main = defineCommand({
96
99
  meta: {
97
100
  name: "varg",
98
- version: "0.3.0",
101
+ version: pkg.version,
99
102
  description: "ai video infrastructure from your terminal",
100
103
  },
101
104
  subCommands: {
102
105
  run: runCmd,
103
106
  render: renderCmd,
107
+ studio: studioCmd,
104
108
  list: listCmd,
105
109
  ls: listCmd,
106
110
  find: findCmd,
@@ -32,9 +32,15 @@ type PendingLayer =
32
32
  | { type: "sync"; layer: Layer }
33
33
  | { type: "async"; promise: Promise<Layer> };
34
34
 
35
+ interface ClipLayerOptions {
36
+ cutFrom?: number;
37
+ cutTo?: number;
38
+ }
39
+
35
40
  async function renderClipLayers(
36
41
  children: VargNode[],
37
42
  ctx: RenderContext,
43
+ clipOptions?: ClipLayerOptions,
38
44
  ): Promise<Layer[]> {
39
45
  const pending: PendingLayer[] = [];
40
46
 
@@ -86,8 +92,9 @@ async function renderClipLayers(
86
92
  type: "video",
87
93
  path,
88
94
  resizeMode: props.resize,
89
- cutFrom: props.cutFrom,
90
- cutTo: props.cutTo,
95
+ // Video-level cutFrom/cutTo take precedence over clip-level
96
+ cutFrom: props.cutFrom ?? clipOptions?.cutFrom,
97
+ cutTo: props.cutTo ?? clipOptions?.cutTo,
91
98
  mixVolume: props.keepAudio ? (props.volume ?? 1) : 0,
92
99
  left: props.left,
93
100
  top: props.top,
@@ -220,7 +227,10 @@ export async function renderClip(
220
227
  ctx: RenderContext,
221
228
  ): Promise<Clip> {
222
229
  const props = element.props as ClipProps;
223
- const layers = await renderClipLayers(element.children, ctx);
230
+ const layers = await renderClipLayers(element.children, ctx, {
231
+ cutFrom: props.cutFrom,
232
+ cutTo: props.cutTo,
233
+ });
224
234
 
225
235
  const isOverlayVideo = (l: Layer) =>
226
236
  l.type === "video" &&
@@ -75,6 +75,10 @@ export interface RenderProps extends BaseProps {
75
75
  export interface ClipProps extends BaseProps {
76
76
  duration?: number | "auto";
77
77
  transition?: TransitionOptions;
78
+ /** Start trim point in seconds (e.g., 1 to start from 1 second) */
79
+ cutFrom?: number;
80
+ /** End trim point in seconds (e.g., 3 to end at 3 seconds) */
81
+ cutTo?: number;
78
82
  children?: VargNode;
79
83
  }
80
84
 
package/src/react/cli.ts DELETED
@@ -1,52 +0,0 @@
1
- #!/usr/bin/env bun
2
-
3
- import { parseArgs } from "node:util";
4
- import { render } from "./render";
5
- import type { VargElement } from "./types";
6
-
7
- const { values, positionals } = parseArgs({
8
- args: Bun.argv.slice(2),
9
- options: {
10
- output: { type: "string", short: "o" },
11
- cache: { type: "string", short: "c", default: ".cache/ai" },
12
- quiet: { type: "boolean", short: "q", default: false },
13
- },
14
- allowPositionals: true,
15
- });
16
-
17
- const [file] = positionals;
18
-
19
- if (!file) {
20
- console.error("usage: bun react/cli.ts <component.tsx> [-o output.mp4]");
21
- process.exit(1);
22
- }
23
-
24
- const resolvedPath = Bun.resolveSync(file, process.cwd());
25
- const mod = await import(resolvedPath);
26
- const component: VargElement = mod.default;
27
-
28
- if (!component || component.type !== "render") {
29
- console.error("error: default export must be a <Render> element");
30
- process.exit(1);
31
- }
32
-
33
- const outputPath =
34
- values.output ??
35
- `output/${file
36
- .replace(/\.tsx?$/, "")
37
- .split("/")
38
- .pop()}.mp4`;
39
-
40
- if (!values.quiet) {
41
- console.log(`rendering ${file} → ${outputPath}`);
42
- }
43
-
44
- const buffer = await render(component, {
45
- output: outputPath,
46
- cache: values.cache,
47
- quiet: values.quiet,
48
- });
49
-
50
- if (!values.quiet) {
51
- console.log(`done! ${buffer.byteLength} bytes → ${outputPath}`);
52
- }