@vibeo/cli 0.1.0 → 0.1.2

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/bin/vibeo.js ADDED
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ require("../dist/index.js");
package/bin/vibeo.mjs ADDED
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ import "../dist/index.js";
@@ -1 +1 @@
1
- {"version":3,"file":"create.d.ts","sourceRoot":"","sources":["../../src/commands/create.ts"],"names":[],"mappings":"AAwFA,wBAAsB,aAAa,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CA4GjE"}
1
+ {"version":3,"file":"create.d.ts","sourceRoot":"","sources":["../../src/commands/create.ts"],"names":[],"mappings":"AA+RA,wBAAsB,aAAa,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CA0GjE"}
@@ -1,22 +1,227 @@
1
1
  import { resolve, join } from "node:path";
2
- import { mkdir, readFile, writeFile } from "node:fs/promises";
2
+ import { mkdir, writeFile } from "node:fs/promises";
3
3
  import { existsSync } from "node:fs";
4
+ // ---------------------------------------------------------------------------
5
+ // Embedded templates (so `create` works from npm without the examples/ dir)
6
+ // ---------------------------------------------------------------------------
7
+ const TEMPLATE_BASIC = `import React from "react";
8
+ import {
9
+ Composition, Sequence, VibeoRoot,
10
+ useCurrentFrame, useVideoConfig, interpolate, easeInOut,
11
+ } from "@vibeo/core";
12
+
13
+ function TitleScene() {
14
+ const frame = useCurrentFrame();
15
+ const { width, height } = useVideoConfig();
16
+ const opacity = interpolate(frame, [0, 30], [0, 1], { extrapolateRight: "clamp" });
17
+ const y = interpolate(frame, [0, 30], [40, 0], { easing: easeInOut, extrapolateRight: "clamp" });
18
+
19
+ return (
20
+ <div style={{ width, height, display: "flex", justifyContent: "center", alignItems: "center", background: "linear-gradient(135deg, #0f0c29, #302b63, #24243e)" }}>
21
+ <h1 style={{ color: "white", fontSize: 72, fontFamily: "sans-serif", opacity, transform: \`translateY(\${y}px)\` }}>Hello, Vibeo!</h1>
22
+ </div>
23
+ );
24
+ }
25
+
26
+ function ContentScene() {
27
+ const frame = useCurrentFrame();
28
+ const { width, height, fps } = useVideoConfig();
29
+ const seconds = (frame / fps).toFixed(1);
30
+ const scale = interpolate(frame, [0, 20], [0.8, 1], { easing: easeInOut, extrapolateRight: "clamp" });
31
+
32
+ return (
33
+ <div style={{ width, height, display: "flex", flexDirection: "column", justifyContent: "center", alignItems: "center", background: "#24243e" }}>
34
+ <div style={{ transform: \`scale(\${scale})\`, color: "white", fontSize: 48, fontFamily: "sans-serif", textAlign: "center" }}>
35
+ <p>Scene 2</p>
36
+ <p style={{ fontSize: 32, opacity: 0.7 }}>{seconds}s elapsed</p>
37
+ </div>
38
+ </div>
39
+ );
40
+ }
41
+
42
+ function MyVideo() {
43
+ return (
44
+ <>
45
+ <Sequence from={0} durationInFrames={75} name="Title"><TitleScene /></Sequence>
46
+ <Sequence from={75} durationInFrames={75} name="Content"><ContentScene /></Sequence>
47
+ </>
48
+ );
49
+ }
50
+
51
+ export function Root() {
52
+ return (
53
+ <VibeoRoot>
54
+ <Composition id="BasicComposition" component={MyVideo} width={1920} height={1080} fps={30} durationInFrames={150} />
55
+ </VibeoRoot>
56
+ );
57
+ }
58
+ `;
59
+ const TEMPLATE_AUDIO_REACTIVE = `import React from "react";
60
+ import { Composition, VibeoRoot, useCurrentFrame, useVideoConfig, interpolate } from "@vibeo/core";
61
+ import { Audio } from "@vibeo/audio";
62
+ import { useAudioData } from "@vibeo/effects";
63
+
64
+ const AUDIO_SRC = "/music.mp3";
65
+
66
+ function FrequencyBars() {
67
+ const { width, height } = useVideoConfig();
68
+ const audio = useAudioData(AUDIO_SRC, { fftSize: 1024 });
69
+ if (!audio) return <div style={{ width, height }} />;
70
+
71
+ const barCount = 48;
72
+ const step = Math.floor(audio.frequencies.length / barCount);
73
+ const barWidth = (width * 0.8) / barCount;
74
+ const maxBarHeight = height * 0.6;
75
+
76
+ return (
77
+ <div style={{ position: "absolute", bottom: 60, left: width * 0.1, display: "flex", alignItems: "flex-end", gap: 2 }}>
78
+ {Array.from({ length: barCount }, (_, i) => {
79
+ const db = audio.frequencies[i * step];
80
+ const normalized = Math.max(0, (db + 100) / 100);
81
+ const hue = interpolate(i, [0, barCount - 1], [220, 340]);
82
+ return (
83
+ <div key={i} style={{ width: barWidth - 2, height: Math.max(2, normalized * maxBarHeight), background: \`hsl(\${hue}, 80%, 60%)\`, borderRadius: 2 }} />
84
+ );
85
+ })}
86
+ </div>
87
+ );
88
+ }
89
+
90
+ function AudioViz() {
91
+ const frame = useCurrentFrame();
92
+ const { width, height, fps } = useVideoConfig();
93
+ const audio = useAudioData(AUDIO_SRC);
94
+ const hue = 240 + (audio ? audio.amplitude * 60 : 0);
95
+ const lightness = audio ? 8 + audio.amplitude * 12 : 8;
96
+
97
+ return (
98
+ <div style={{ width, height, background: \`radial-gradient(ellipse at center, hsl(\${hue}, 40%, \${lightness + 5}%), hsl(\${hue}, 30%, \${lightness}%))\`, position: "relative", overflow: "hidden" }}>
99
+ <div style={{ position: "absolute", top: 40, left: 40, color: "white", fontFamily: "sans-serif" }}>
100
+ <h1 style={{ fontSize: 36, margin: 0, opacity: 0.9 }}>Audio Visualizer</h1>
101
+ <p style={{ fontSize: 18, margin: "8px 0 0", opacity: 0.5 }}>{(frame / fps).toFixed(1)}s</p>
102
+ </div>
103
+ <FrequencyBars />
104
+ <Audio src={AUDIO_SRC} volume={0.8} />
105
+ </div>
106
+ );
107
+ }
108
+
109
+ export function Root() {
110
+ return (
111
+ <VibeoRoot>
112
+ <Composition id="AudioReactiveViz" component={AudioViz} width={1920} height={1080} fps={30} durationInFrames={900} />
113
+ </VibeoRoot>
114
+ );
115
+ }
116
+ `;
117
+ const TEMPLATE_TRANSITIONS = `import React from "react";
118
+ import { Composition, Sequence, VibeoRoot, useCurrentFrame, useVideoConfig, interpolate, easeOut } from "@vibeo/core";
119
+ import { Transition } from "@vibeo/effects";
120
+
121
+ function ColorScene({ title, color }: { title: string; color: string }) {
122
+ const frame = useCurrentFrame();
123
+ const { width, height } = useVideoConfig();
124
+ const opacity = interpolate(frame, [0, 20], [0, 1], { easing: easeOut, extrapolateRight: "clamp" });
125
+
126
+ return (
127
+ <div style={{ width, height, background: color, display: "flex", justifyContent: "center", alignItems: "center" }}>
128
+ <h1 style={{ color: "white", fontSize: 80, fontFamily: "sans-serif", opacity }}>{title}</h1>
129
+ </div>
130
+ );
131
+ }
132
+
133
+ function SceneA() { return <ColorScene title="Scene One" color="linear-gradient(135deg, #667eea, #764ba2)" />; }
134
+ function SceneB() { return <ColorScene title="Scene Two" color="linear-gradient(135deg, #f093fb, #f5576c)" />; }
135
+ function SceneC() { return <ColorScene title="Scene Three" color="linear-gradient(135deg, #4facfe, #00f2fe)" />; }
136
+
137
+ function TransitionDemo() {
138
+ return (
139
+ <>
140
+ <Sequence from={0} durationInFrames={85}><SceneA /></Sequence>
141
+ <Sequence from={65} durationInFrames={20}>
142
+ <Transition type="fade" durationInFrames={20}><SceneA /><SceneB /></Transition>
143
+ </Sequence>
144
+ <Sequence from={85} durationInFrames={85}><SceneB /></Sequence>
145
+ <Sequence from={150} durationInFrames={20}>
146
+ <Transition type="slide" durationInFrames={20} direction="left"><SceneB /><SceneC /></Transition>
147
+ </Sequence>
148
+ <Sequence from={170} durationInFrames={70}><SceneC /></Sequence>
149
+ </>
150
+ );
151
+ }
152
+
153
+ export function Root() {
154
+ return (
155
+ <VibeoRoot>
156
+ <Composition id="TransitionDemo" component={TransitionDemo} width={1920} height={1080} fps={30} durationInFrames={240} />
157
+ </VibeoRoot>
158
+ );
159
+ }
160
+ `;
161
+ const TEMPLATE_SUBTITLES = `import React from "react";
162
+ import { Composition, Sequence, VibeoRoot, useCurrentFrame, useVideoConfig, interpolate } from "@vibeo/core";
163
+ import { Subtitle } from "@vibeo/extras";
164
+
165
+ const SUBTITLES_SRT = \`1
166
+ 00:00:00,500 --> 00:00:03,000
167
+ Welcome to the Vibeo demo.
168
+
169
+ 2
170
+ 00:00:03,500 --> 00:00:06,000
171
+ This shows subtitle overlays.
172
+
173
+ 3
174
+ 00:00:06,500 --> 00:00:09,000
175
+ Subtitles are synced to the frame timeline.
176
+
177
+ 4
178
+ 00:00:09,500 --> 00:00:12,000
179
+ You can use <b>bold</b> and <i>italic</i> text.
180
+
181
+ 5
182
+ 00:00:13,000 --> 00:00:16,000
183
+ The end. Thanks for watching!\`;
184
+
185
+ function SubtitleVideo() {
186
+ const frame = useCurrentFrame();
187
+ const { width, height } = useVideoConfig();
188
+ const hue = interpolate(frame, [0, 480], [200, 280]);
189
+
190
+ return (
191
+ <div style={{ width, height, position: "relative" }}>
192
+ <div style={{ width, height, background: \`linear-gradient(135deg, hsl(\${hue}, 50%, 15%), hsl(\${hue + 40}, 40%, 10%))\` }} />
193
+ <div style={{ position: "absolute", top: 0, left: 0, width, height }}>
194
+ <Subtitle src={SUBTITLES_SRT} format="srt" position="bottom" fontSize={36} color="white" outlineColor="black" outlineWidth={2} style={{ padding: "0 80px 60px" }} />
195
+ </div>
196
+ </div>
197
+ );
198
+ }
199
+
200
+ export function Root() {
201
+ return (
202
+ <VibeoRoot>
203
+ <Composition id="SubtitleOverlay" component={SubtitleVideo} width={1920} height={1080} fps={30} durationInFrames={480} />
204
+ </VibeoRoot>
205
+ );
206
+ }
207
+ `;
208
+ // ---------------------------------------------------------------------------
4
209
  const TEMPLATES = {
5
210
  basic: {
6
211
  description: "Minimal composition with text animation and two scenes",
7
- example: "basic-composition.tsx",
212
+ source: TEMPLATE_BASIC,
8
213
  },
9
214
  "audio-reactive": {
10
215
  description: "Audio visualization with frequency bars and amplitude-driven effects",
11
- example: "audio-reactive-viz.tsx",
216
+ source: TEMPLATE_AUDIO_REACTIVE,
12
217
  },
13
218
  transitions: {
14
219
  description: "Scene transitions (fade, slide) between multiple scenes",
15
- example: "transition-demo.tsx",
220
+ source: TEMPLATE_TRANSITIONS,
16
221
  },
17
222
  subtitles: {
18
223
  description: "Video with SRT subtitle overlay",
19
- example: "subtitle-overlay.tsx",
224
+ source: TEMPLATE_SUBTITLES,
20
225
  },
21
226
  };
22
227
  function parseArgs(args) {
@@ -63,18 +268,6 @@ Examples:
63
268
  vibeo create intro --template transitions
64
269
  `);
65
270
  }
66
- // Find the examples directory relative to the CLI package
67
- function findExamplesDir() {
68
- // Walk up from this file to find the repo root with examples/
69
- let dir = import.meta.dir;
70
- for (let i = 0; i < 6; i++) {
71
- const candidate = join(dir, "examples");
72
- if (existsSync(candidate))
73
- return candidate;
74
- dir = resolve(dir, "..");
75
- }
76
- throw new Error("Could not find examples directory");
77
- }
78
271
  export async function createCommand(args) {
79
272
  const parsed = parseArgs(args);
80
273
  if (!parsed.name) {
@@ -98,10 +291,8 @@ export async function createCommand(args) {
98
291
  // Create project structure
99
292
  await mkdir(join(projectDir, "src"), { recursive: true });
100
293
  await mkdir(join(projectDir, "public"), { recursive: true });
101
- // Copy example as src/index.tsx
102
- const examplesDir = findExamplesDir();
103
- const exampleSrc = await readFile(join(examplesDir, template.example), "utf-8");
104
- await writeFile(join(projectDir, "src", "index.tsx"), exampleSrc);
294
+ // Write template source
295
+ await writeFile(join(projectDir, "src", "index.tsx"), template.source);
105
296
  // Write package.json
106
297
  const pkg = {
107
298
  name: parsed.name,
@@ -109,19 +300,19 @@ export async function createCommand(args) {
109
300
  private: true,
110
301
  type: "module",
111
302
  scripts: {
112
- dev: "vibeo preview --entry src/index.tsx",
113
- build: "vibeo render --entry src/index.tsx",
114
- list: "vibeo list --entry src/index.tsx",
303
+ dev: "bunx @vibeo/cli preview --entry src/index.tsx",
304
+ build: "bunx @vibeo/cli render --entry src/index.tsx",
305
+ list: "bunx @vibeo/cli list --entry src/index.tsx",
115
306
  typecheck: "bunx tsc --noEmit",
116
307
  },
117
308
  dependencies: {
118
- "@vibeo/core": "workspace:*",
119
- "@vibeo/audio": "workspace:*",
120
- "@vibeo/effects": "workspace:*",
121
- "@vibeo/extras": "workspace:*",
122
- "@vibeo/player": "workspace:*",
123
- "@vibeo/renderer": "workspace:*",
124
- "@vibeo/cli": "workspace:*",
309
+ "@vibeo/core": "^0.1.0",
310
+ "@vibeo/audio": "^0.1.0",
311
+ "@vibeo/effects": "^0.1.0",
312
+ "@vibeo/extras": "^0.1.0",
313
+ "@vibeo/player": "^0.1.0",
314
+ "@vibeo/renderer": "^0.1.0",
315
+ "@vibeo/cli": "^0.1.0",
125
316
  react: "^19.0.0",
126
317
  "react-dom": "^19.0.0",
127
318
  },
@@ -1 +1 @@
1
- {"version":3,"file":"create.js","sourceRoot":"","sources":["../../src/commands/create.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"create.js","sourceRoot":"","sources":["../../src/commands/create.ts"],"names":[],"mappings":"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"}
package/dist/index.d.ts CHANGED
@@ -1,3 +1,2 @@
1
- #!/usr/bin/env bun
2
1
  export {};
3
2
  //# sourceMappingURL=index.d.ts.map
package/dist/index.js CHANGED
@@ -1,4 +1,3 @@
1
- #!/usr/bin/env bun
2
1
  import { renderCommand } from "./commands/render.js";
3
2
  import { previewCommand } from "./commands/preview.js";
4
3
  import { listCommand } from "./commands/list.js";
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAEA,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AACrD,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AACvD,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACjD,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAErD,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;AACnC,MAAM,OAAO,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC;AAExB,SAAS,UAAU;IACjB,OAAO,CAAC,GAAG,CAAC;;;;;;;;;;;;;;;;;;;;;CAqBb,CAAC,CAAC;AACH,CAAC;AAED,KAAK,UAAU,IAAI;IACjB,IAAI,CAAC,OAAO,IAAI,OAAO,KAAK,QAAQ,IAAI,OAAO,KAAK,IAAI,EAAE,CAAC;QACzD,UAAU,EAAE,CAAC;QACb,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;IAED,QAAQ,OAAO,EAAE,CAAC;QAChB,KAAK,QAAQ;YACX,MAAM,aAAa,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;YACnC,MAAM;QACR,KAAK,QAAQ;YACX,MAAM,aAAa,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;YACnC,MAAM;QACR,KAAK,SAAS;YACZ,MAAM,cAAc,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;YACpC,MAAM;QACR,KAAK,MAAM;YACT,MAAM,WAAW,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;YACjC,MAAM;QACR;YACE,OAAO,CAAC,KAAK,CAAC,oBAAoB,OAAO,EAAE,CAAC,CAAC;YAC7C,UAAU,EAAE,CAAC;YACb,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IACpB,CAAC;AACH,CAAC;AAED,IAAI,EAAE,CAAC,KAAK,CAAC,CAAC,GAAG,EAAE,EAAE;IACnB,OAAO,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IACnB,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC,CAAC,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AACrD,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AACvD,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACjD,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAErD,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;AACnC,MAAM,OAAO,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC;AAExB,SAAS,UAAU;IACjB,OAAO,CAAC,GAAG,CAAC;;;;;;;;;;;;;;;;;;;;;CAqBb,CAAC,CAAC;AACH,CAAC;AAED,KAAK,UAAU,IAAI;IACjB,IAAI,CAAC,OAAO,IAAI,OAAO,KAAK,QAAQ,IAAI,OAAO,KAAK,IAAI,EAAE,CAAC;QACzD,UAAU,EAAE,CAAC;QACb,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;IAED,QAAQ,OAAO,EAAE,CAAC;QAChB,KAAK,QAAQ;YACX,MAAM,aAAa,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;YACnC,MAAM;QACR,KAAK,QAAQ;YACX,MAAM,aAAa,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;YACnC,MAAM;QACR,KAAK,SAAS;YACZ,MAAM,cAAc,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;YACpC,MAAM;QACR,KAAK,MAAM;YACT,MAAM,WAAW,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;YACjC,MAAM;QACR;YACE,OAAO,CAAC,KAAK,CAAC,oBAAoB,OAAO,EAAE,CAAC,CAAC;YAC7C,UAAU,EAAE,CAAC;YACb,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IACpB,CAAC;AACH,CAAC;AAED,IAAI,EAAE,CAAC,KAAK,CAAC,CAAC,GAAG,EAAE,EAAE;IACnB,OAAO,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IACnB,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC,CAAC,CAAC"}
package/package.json CHANGED
@@ -1,11 +1,11 @@
1
1
  {
2
2
  "name": "@vibeo/cli",
3
- "version": "0.1.0",
3
+ "version": "0.1.2",
4
4
  "type": "module",
5
5
  "main": "./dist/index.js",
6
6
  "types": "./dist/index.d.ts",
7
7
  "bin": {
8
- "vibeo": "./dist/index.js"
8
+ "vibeo": "./bin/vibeo.mjs"
9
9
  },
10
10
  "exports": {
11
11
  ".": {
@@ -20,6 +20,7 @@
20
20
  "@vibeo/audio": "0.1.0"
21
21
  },
22
22
  "files": [
23
+ "bin",
23
24
  "dist",
24
25
  "src"
25
26
  ],
@@ -1,23 +1,234 @@
1
- import { resolve, join, basename } from "node:path";
2
- import { cp, mkdir, readdir, readFile, writeFile } from "node:fs/promises";
1
+ import { resolve, join } from "node:path";
2
+ import { mkdir, writeFile } from "node:fs/promises";
3
3
  import { existsSync } from "node:fs";
4
4
 
5
- const TEMPLATES: Record<string, { description: string; example: string }> = {
5
+ // ---------------------------------------------------------------------------
6
+ // Embedded templates (so `create` works from npm without the examples/ dir)
7
+ // ---------------------------------------------------------------------------
8
+
9
+ const TEMPLATE_BASIC = `import React from "react";
10
+ import {
11
+ Composition, Sequence, VibeoRoot,
12
+ useCurrentFrame, useVideoConfig, interpolate, easeInOut,
13
+ } from "@vibeo/core";
14
+
15
+ function TitleScene() {
16
+ const frame = useCurrentFrame();
17
+ const { width, height } = useVideoConfig();
18
+ const opacity = interpolate(frame, [0, 30], [0, 1], { extrapolateRight: "clamp" });
19
+ const y = interpolate(frame, [0, 30], [40, 0], { easing: easeInOut, extrapolateRight: "clamp" });
20
+
21
+ return (
22
+ <div style={{ width, height, display: "flex", justifyContent: "center", alignItems: "center", background: "linear-gradient(135deg, #0f0c29, #302b63, #24243e)" }}>
23
+ <h1 style={{ color: "white", fontSize: 72, fontFamily: "sans-serif", opacity, transform: \`translateY(\${y}px)\` }}>Hello, Vibeo!</h1>
24
+ </div>
25
+ );
26
+ }
27
+
28
+ function ContentScene() {
29
+ const frame = useCurrentFrame();
30
+ const { width, height, fps } = useVideoConfig();
31
+ const seconds = (frame / fps).toFixed(1);
32
+ const scale = interpolate(frame, [0, 20], [0.8, 1], { easing: easeInOut, extrapolateRight: "clamp" });
33
+
34
+ return (
35
+ <div style={{ width, height, display: "flex", flexDirection: "column", justifyContent: "center", alignItems: "center", background: "#24243e" }}>
36
+ <div style={{ transform: \`scale(\${scale})\`, color: "white", fontSize: 48, fontFamily: "sans-serif", textAlign: "center" }}>
37
+ <p>Scene 2</p>
38
+ <p style={{ fontSize: 32, opacity: 0.7 }}>{seconds}s elapsed</p>
39
+ </div>
40
+ </div>
41
+ );
42
+ }
43
+
44
+ function MyVideo() {
45
+ return (
46
+ <>
47
+ <Sequence from={0} durationInFrames={75} name="Title"><TitleScene /></Sequence>
48
+ <Sequence from={75} durationInFrames={75} name="Content"><ContentScene /></Sequence>
49
+ </>
50
+ );
51
+ }
52
+
53
+ export function Root() {
54
+ return (
55
+ <VibeoRoot>
56
+ <Composition id="BasicComposition" component={MyVideo} width={1920} height={1080} fps={30} durationInFrames={150} />
57
+ </VibeoRoot>
58
+ );
59
+ }
60
+ `;
61
+
62
+ const TEMPLATE_AUDIO_REACTIVE = `import React from "react";
63
+ import { Composition, VibeoRoot, useCurrentFrame, useVideoConfig, interpolate } from "@vibeo/core";
64
+ import { Audio } from "@vibeo/audio";
65
+ import { useAudioData } from "@vibeo/effects";
66
+
67
+ const AUDIO_SRC = "/music.mp3";
68
+
69
+ function FrequencyBars() {
70
+ const { width, height } = useVideoConfig();
71
+ const audio = useAudioData(AUDIO_SRC, { fftSize: 1024 });
72
+ if (!audio) return <div style={{ width, height }} />;
73
+
74
+ const barCount = 48;
75
+ const step = Math.floor(audio.frequencies.length / barCount);
76
+ const barWidth = (width * 0.8) / barCount;
77
+ const maxBarHeight = height * 0.6;
78
+
79
+ return (
80
+ <div style={{ position: "absolute", bottom: 60, left: width * 0.1, display: "flex", alignItems: "flex-end", gap: 2 }}>
81
+ {Array.from({ length: barCount }, (_, i) => {
82
+ const db = audio.frequencies[i * step];
83
+ const normalized = Math.max(0, (db + 100) / 100);
84
+ const hue = interpolate(i, [0, barCount - 1], [220, 340]);
85
+ return (
86
+ <div key={i} style={{ width: barWidth - 2, height: Math.max(2, normalized * maxBarHeight), background: \`hsl(\${hue}, 80%, 60%)\`, borderRadius: 2 }} />
87
+ );
88
+ })}
89
+ </div>
90
+ );
91
+ }
92
+
93
+ function AudioViz() {
94
+ const frame = useCurrentFrame();
95
+ const { width, height, fps } = useVideoConfig();
96
+ const audio = useAudioData(AUDIO_SRC);
97
+ const hue = 240 + (audio ? audio.amplitude * 60 : 0);
98
+ const lightness = audio ? 8 + audio.amplitude * 12 : 8;
99
+
100
+ return (
101
+ <div style={{ width, height, background: \`radial-gradient(ellipse at center, hsl(\${hue}, 40%, \${lightness + 5}%), hsl(\${hue}, 30%, \${lightness}%))\`, position: "relative", overflow: "hidden" }}>
102
+ <div style={{ position: "absolute", top: 40, left: 40, color: "white", fontFamily: "sans-serif" }}>
103
+ <h1 style={{ fontSize: 36, margin: 0, opacity: 0.9 }}>Audio Visualizer</h1>
104
+ <p style={{ fontSize: 18, margin: "8px 0 0", opacity: 0.5 }}>{(frame / fps).toFixed(1)}s</p>
105
+ </div>
106
+ <FrequencyBars />
107
+ <Audio src={AUDIO_SRC} volume={0.8} />
108
+ </div>
109
+ );
110
+ }
111
+
112
+ export function Root() {
113
+ return (
114
+ <VibeoRoot>
115
+ <Composition id="AudioReactiveViz" component={AudioViz} width={1920} height={1080} fps={30} durationInFrames={900} />
116
+ </VibeoRoot>
117
+ );
118
+ }
119
+ `;
120
+
121
+ const TEMPLATE_TRANSITIONS = `import React from "react";
122
+ import { Composition, Sequence, VibeoRoot, useCurrentFrame, useVideoConfig, interpolate, easeOut } from "@vibeo/core";
123
+ import { Transition } from "@vibeo/effects";
124
+
125
+ function ColorScene({ title, color }: { title: string; color: string }) {
126
+ const frame = useCurrentFrame();
127
+ const { width, height } = useVideoConfig();
128
+ const opacity = interpolate(frame, [0, 20], [0, 1], { easing: easeOut, extrapolateRight: "clamp" });
129
+
130
+ return (
131
+ <div style={{ width, height, background: color, display: "flex", justifyContent: "center", alignItems: "center" }}>
132
+ <h1 style={{ color: "white", fontSize: 80, fontFamily: "sans-serif", opacity }}>{title}</h1>
133
+ </div>
134
+ );
135
+ }
136
+
137
+ function SceneA() { return <ColorScene title="Scene One" color="linear-gradient(135deg, #667eea, #764ba2)" />; }
138
+ function SceneB() { return <ColorScene title="Scene Two" color="linear-gradient(135deg, #f093fb, #f5576c)" />; }
139
+ function SceneC() { return <ColorScene title="Scene Three" color="linear-gradient(135deg, #4facfe, #00f2fe)" />; }
140
+
141
+ function TransitionDemo() {
142
+ return (
143
+ <>
144
+ <Sequence from={0} durationInFrames={85}><SceneA /></Sequence>
145
+ <Sequence from={65} durationInFrames={20}>
146
+ <Transition type="fade" durationInFrames={20}><SceneA /><SceneB /></Transition>
147
+ </Sequence>
148
+ <Sequence from={85} durationInFrames={85}><SceneB /></Sequence>
149
+ <Sequence from={150} durationInFrames={20}>
150
+ <Transition type="slide" durationInFrames={20} direction="left"><SceneB /><SceneC /></Transition>
151
+ </Sequence>
152
+ <Sequence from={170} durationInFrames={70}><SceneC /></Sequence>
153
+ </>
154
+ );
155
+ }
156
+
157
+ export function Root() {
158
+ return (
159
+ <VibeoRoot>
160
+ <Composition id="TransitionDemo" component={TransitionDemo} width={1920} height={1080} fps={30} durationInFrames={240} />
161
+ </VibeoRoot>
162
+ );
163
+ }
164
+ `;
165
+
166
+ const TEMPLATE_SUBTITLES = `import React from "react";
167
+ import { Composition, Sequence, VibeoRoot, useCurrentFrame, useVideoConfig, interpolate } from "@vibeo/core";
168
+ import { Subtitle } from "@vibeo/extras";
169
+
170
+ const SUBTITLES_SRT = \`1
171
+ 00:00:00,500 --> 00:00:03,000
172
+ Welcome to the Vibeo demo.
173
+
174
+ 2
175
+ 00:00:03,500 --> 00:00:06,000
176
+ This shows subtitle overlays.
177
+
178
+ 3
179
+ 00:00:06,500 --> 00:00:09,000
180
+ Subtitles are synced to the frame timeline.
181
+
182
+ 4
183
+ 00:00:09,500 --> 00:00:12,000
184
+ You can use <b>bold</b> and <i>italic</i> text.
185
+
186
+ 5
187
+ 00:00:13,000 --> 00:00:16,000
188
+ The end. Thanks for watching!\`;
189
+
190
+ function SubtitleVideo() {
191
+ const frame = useCurrentFrame();
192
+ const { width, height } = useVideoConfig();
193
+ const hue = interpolate(frame, [0, 480], [200, 280]);
194
+
195
+ return (
196
+ <div style={{ width, height, position: "relative" }}>
197
+ <div style={{ width, height, background: \`linear-gradient(135deg, hsl(\${hue}, 50%, 15%), hsl(\${hue + 40}, 40%, 10%))\` }} />
198
+ <div style={{ position: "absolute", top: 0, left: 0, width, height }}>
199
+ <Subtitle src={SUBTITLES_SRT} format="srt" position="bottom" fontSize={36} color="white" outlineColor="black" outlineWidth={2} style={{ padding: "0 80px 60px" }} />
200
+ </div>
201
+ </div>
202
+ );
203
+ }
204
+
205
+ export function Root() {
206
+ return (
207
+ <VibeoRoot>
208
+ <Composition id="SubtitleOverlay" component={SubtitleVideo} width={1920} height={1080} fps={30} durationInFrames={480} />
209
+ </VibeoRoot>
210
+ );
211
+ }
212
+ `;
213
+
214
+ // ---------------------------------------------------------------------------
215
+
216
+ const TEMPLATES: Record<string, { description: string; source: string }> = {
6
217
  basic: {
7
218
  description: "Minimal composition with text animation and two scenes",
8
- example: "basic-composition.tsx",
219
+ source: TEMPLATE_BASIC,
9
220
  },
10
221
  "audio-reactive": {
11
222
  description: "Audio visualization with frequency bars and amplitude-driven effects",
12
- example: "audio-reactive-viz.tsx",
223
+ source: TEMPLATE_AUDIO_REACTIVE,
13
224
  },
14
225
  transitions: {
15
226
  description: "Scene transitions (fade, slide) between multiple scenes",
16
- example: "transition-demo.tsx",
227
+ source: TEMPLATE_TRANSITIONS,
17
228
  },
18
229
  subtitles: {
19
230
  description: "Video with SRT subtitle overlay",
20
- example: "subtitle-overlay.tsx",
231
+ source: TEMPLATE_SUBTITLES,
21
232
  },
22
233
  };
23
234
 
@@ -74,18 +285,6 @@ Examples:
74
285
  `);
75
286
  }
76
287
 
77
- // Find the examples directory relative to the CLI package
78
- function findExamplesDir(): string {
79
- // Walk up from this file to find the repo root with examples/
80
- let dir = import.meta.dir;
81
- for (let i = 0; i < 6; i++) {
82
- const candidate = join(dir, "examples");
83
- if (existsSync(candidate)) return candidate;
84
- dir = resolve(dir, "..");
85
- }
86
- throw new Error("Could not find examples directory");
87
- }
88
-
89
288
  export async function createCommand(args: string[]): Promise<void> {
90
289
  const parsed = parseArgs(args);
91
290
 
@@ -115,10 +314,8 @@ export async function createCommand(args: string[]): Promise<void> {
115
314
  await mkdir(join(projectDir, "src"), { recursive: true });
116
315
  await mkdir(join(projectDir, "public"), { recursive: true });
117
316
 
118
- // Copy example as src/index.tsx
119
- const examplesDir = findExamplesDir();
120
- const exampleSrc = await readFile(join(examplesDir, template.example), "utf-8");
121
- await writeFile(join(projectDir, "src", "index.tsx"), exampleSrc);
317
+ // Write template source
318
+ await writeFile(join(projectDir, "src", "index.tsx"), template.source);
122
319
 
123
320
  // Write package.json
124
321
  const pkg = {
@@ -127,19 +324,19 @@ export async function createCommand(args: string[]): Promise<void> {
127
324
  private: true,
128
325
  type: "module",
129
326
  scripts: {
130
- dev: "vibeo preview --entry src/index.tsx",
131
- build: "vibeo render --entry src/index.tsx",
132
- list: "vibeo list --entry src/index.tsx",
327
+ dev: "bunx @vibeo/cli preview --entry src/index.tsx",
328
+ build: "bunx @vibeo/cli render --entry src/index.tsx",
329
+ list: "bunx @vibeo/cli list --entry src/index.tsx",
133
330
  typecheck: "bunx tsc --noEmit",
134
331
  },
135
332
  dependencies: {
136
- "@vibeo/core": "workspace:*",
137
- "@vibeo/audio": "workspace:*",
138
- "@vibeo/effects": "workspace:*",
139
- "@vibeo/extras": "workspace:*",
140
- "@vibeo/player": "workspace:*",
141
- "@vibeo/renderer": "workspace:*",
142
- "@vibeo/cli": "workspace:*",
333
+ "@vibeo/core": "^0.1.0",
334
+ "@vibeo/audio": "^0.1.0",
335
+ "@vibeo/effects": "^0.1.0",
336
+ "@vibeo/extras": "^0.1.0",
337
+ "@vibeo/player": "^0.1.0",
338
+ "@vibeo/renderer": "^0.1.0",
339
+ "@vibeo/cli": "^0.1.0",
143
340
  react: "^19.0.0",
144
341
  "react-dom": "^19.0.0",
145
342
  },
package/src/index.ts CHANGED
@@ -1,5 +1,3 @@
1
- #!/usr/bin/env bun
2
-
3
1
  import { renderCommand } from "./commands/render.js";
4
2
  import { previewCommand } from "./commands/preview.js";
5
3
  import { listCommand } from "./commands/list.js";