@vibeo/cli 0.3.5 → 0.4.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/commands/create.d.ts.map +1 -1
- package/dist/commands/create.js +1 -0
- package/dist/commands/create.js.map +1 -1
- package/dist/commands/editor.d.ts +2 -0
- package/dist/commands/editor.d.ts.map +1 -0
- package/dist/commands/editor.js +18 -0
- package/dist/commands/editor.js.map +1 -0
- package/dist/commands/install-skills.js +1 -1
- package/dist/commands/install-skills.js.map +1 -1
- package/dist/index.js +14 -0
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/skills/vibeo-core/SKILL.md +99 -37
- package/skills/vibeo-editor/SKILL.md +348 -0
- package/skills/vibeo-effects/SKILL.md +162 -14
- package/skills/vibeo-rendering/SKILL.md +8 -5
- package/skills/vibeo-tiktok/SKILL.md +319 -0
- package/src/commands/create.ts +1 -0
- package/src/commands/editor.ts +23 -0
- package/src/commands/install-skills.ts +1 -1
- package/src/index.ts +15 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"create.d.ts","sourceRoot":"","sources":["../../src/commands/create.ts"],"names":[],"mappings":"AA8NA,eAAO,MAAM,cAAc,UAAyB,CAAC;AAErD,wBAAsB,aAAa,CACjC,IAAI,EAAE,MAAM,EACZ,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,EAAE,CAAA;CAAE,CAAC,
|
|
1
|
+
{"version":3,"file":"create.d.ts","sourceRoot":"","sources":["../../src/commands/create.ts"],"names":[],"mappings":"AA8NA,eAAO,MAAM,cAAc,UAAyB,CAAC;AAErD,wBAAsB,aAAa,CACjC,IAAI,EAAE,MAAM,EACZ,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,EAAE,CAAA;CAAE,CAAC,CAgEjE"}
|
package/dist/commands/create.js
CHANGED
|
@@ -232,6 +232,7 @@ export async function createProject(name, template) {
|
|
|
232
232
|
dev: "bun vibeo preview --entry src/index.tsx",
|
|
233
233
|
build: "bun vibeo render --entry src/index.tsx",
|
|
234
234
|
list: "bun vibeo list --entry src/index.tsx",
|
|
235
|
+
editor: "bun vibeo editor --entry src/index.tsx",
|
|
235
236
|
typecheck: "bunx tsc --noEmit",
|
|
236
237
|
},
|
|
237
238
|
dependencies: {
|
|
@@ -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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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"editor.d.ts","sourceRoot":"","sources":["../../src/commands/editor.ts"],"names":[],"mappings":"AAEA,wBAAsB,WAAW,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAoB5E"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { bundleForEditor } from "@vibeo/renderer";
|
|
2
|
+
export async function startEditor(entry, port) {
|
|
3
|
+
console.log(`Starting editor server...`);
|
|
4
|
+
console.log(` Entry: ${entry}`);
|
|
5
|
+
const bundleResult = await bundleForEditor(entry, port);
|
|
6
|
+
console.log(`\n Editor running at ${bundleResult.url}`);
|
|
7
|
+
console.log(` Press Ctrl+C to stop\n`);
|
|
8
|
+
const shutdown = async () => {
|
|
9
|
+
console.log("\nShutting down editor server...");
|
|
10
|
+
await bundleResult.cleanup();
|
|
11
|
+
process.exit(0);
|
|
12
|
+
};
|
|
13
|
+
process.on("SIGINT", shutdown);
|
|
14
|
+
process.on("SIGTERM", shutdown);
|
|
15
|
+
// Block forever
|
|
16
|
+
await new Promise(() => { });
|
|
17
|
+
}
|
|
18
|
+
//# sourceMappingURL=editor.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"editor.js","sourceRoot":"","sources":["../../src/commands/editor.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAElD,MAAM,CAAC,KAAK,UAAU,WAAW,CAAC,KAAa,EAAE,IAAY;IAC3D,OAAO,CAAC,GAAG,CAAC,2BAA2B,CAAC,CAAC;IACzC,OAAO,CAAC,GAAG,CAAC,YAAY,KAAK,EAAE,CAAC,CAAC;IAEjC,MAAM,YAAY,GAAG,MAAM,eAAe,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC;IAExD,OAAO,CAAC,GAAG,CAAC,yBAAyB,YAAY,CAAC,GAAG,EAAE,CAAC,CAAC;IACzD,OAAO,CAAC,GAAG,CAAC,0BAA0B,CAAC,CAAC;IAExC,MAAM,QAAQ,GAAG,KAAK,IAAI,EAAE;QAC1B,OAAO,CAAC,GAAG,CAAC,kCAAkC,CAAC,CAAC;QAChD,MAAM,YAAY,CAAC,OAAO,EAAE,CAAC;QAC7B,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC,CAAC;IAEF,OAAO,CAAC,EAAE,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC;IAC/B,OAAO,CAAC,EAAE,CAAC,SAAS,EAAE,QAAQ,CAAC,CAAC;IAEhC,gBAAgB;IAChB,MAAM,IAAI,OAAO,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC;AAC9B,CAAC"}
|
|
@@ -6,7 +6,7 @@ import { homedir } from "node:os";
|
|
|
6
6
|
// ---------------------------------------------------------------------------
|
|
7
7
|
// Skill loader — resolves from the CLI package's own skills/ directory
|
|
8
8
|
// ---------------------------------------------------------------------------
|
|
9
|
-
const SKILL_NAMES = ["vibeo-core", "vibeo-audio", "vibeo-effects", "vibeo-extras", "vibeo-rendering"];
|
|
9
|
+
const SKILL_NAMES = ["vibeo-core", "vibeo-audio", "vibeo-effects", "vibeo-extras", "vibeo-rendering", "vibeo-editor", "vibeo-tiktok"];
|
|
10
10
|
async function loadSkills() {
|
|
11
11
|
// Resolve from this file's location — skills/ is shipped alongside dist/ in the npm package
|
|
12
12
|
const thisFile = fileURLToPath(import.meta.url);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"install-skills.js","sourceRoot":"","sources":["../../src/commands/install-skills.ts"],"names":[],"mappings":"AAAA,OAAO,EAAW,IAAI,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACnD,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AACzC,OAAO,EAAE,KAAK,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAC9D,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AACrC,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAElC,8EAA8E;AAC9E,uEAAuE;AACvE,8EAA8E;AAE9E,MAAM,WAAW,GAAG,CAAC,YAAY,EAAE,aAAa,EAAE,eAAe,EAAE,cAAc,EAAE,iBAAiB,CAAC,CAAC;
|
|
1
|
+
{"version":3,"file":"install-skills.js","sourceRoot":"","sources":["../../src/commands/install-skills.ts"],"names":[],"mappings":"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"}
|
package/dist/index.js
CHANGED
|
@@ -6,6 +6,7 @@ import { startPreview } from "./commands/preview.js";
|
|
|
6
6
|
import { listCompositions } from "./commands/list.js";
|
|
7
7
|
import { renderVideo } from "./commands/render.js";
|
|
8
8
|
import { installSkills } from "./commands/install-skills.js";
|
|
9
|
+
import { startEditor } from "./commands/editor.js";
|
|
9
10
|
const cli = Cli.create("vibeo", {
|
|
10
11
|
description: "React-based programmatic video framework CLI",
|
|
11
12
|
sync: {
|
|
@@ -102,6 +103,19 @@ cli.command("list", {
|
|
|
102
103
|
return { compositions };
|
|
103
104
|
},
|
|
104
105
|
});
|
|
106
|
+
cli.command("editor", {
|
|
107
|
+
description: "Open the visual video editor in the browser",
|
|
108
|
+
options: z.object({
|
|
109
|
+
entry: z.string().describe("Path to the root file with compositions"),
|
|
110
|
+
port: z.number().default(3001).describe("Port for the editor server"),
|
|
111
|
+
}),
|
|
112
|
+
examples: [
|
|
113
|
+
{ options: { entry: "src/index.tsx" }, description: "Open editor on default port" },
|
|
114
|
+
],
|
|
115
|
+
async run(c) {
|
|
116
|
+
await startEditor(resolve(c.options.entry), c.options.port);
|
|
117
|
+
},
|
|
118
|
+
});
|
|
105
119
|
cli.command("install-skills", {
|
|
106
120
|
description: "Install Vibeo skill/rule files for all supported LLM coding tools (Claude, Codex, Cursor, Gemini, OpenCode, Aider)",
|
|
107
121
|
options: z.object({
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,GAAG,EAAE,CAAC,EAAE,MAAM,OAAO,CAAC;AAC/B,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,oBAAoB,EAAE,MAAM,SAAS,CAAC;AAC/C,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AACrD,OAAO,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAC;AACrD,OAAO,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AACtD,OAAO,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AACnD,OAAO,EAAE,aAAa,EAAE,MAAM,8BAA8B,CAAC;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -230,65 +230,124 @@ actualDuration = floor(duration / playbackRate)
|
|
|
230
230
|
|
|
231
231
|
---
|
|
232
232
|
|
|
233
|
-
##
|
|
233
|
+
## Platform Format Presets
|
|
234
234
|
|
|
235
|
-
|
|
235
|
+
Use these when the user mentions a platform. **Any time the user says "Short", "Reel", "TikTok", or "vertical" — use 1080x1920 (9:16), not landscape.**
|
|
236
236
|
|
|
237
|
-
|
|
238
|
-
|
|
237
|
+
| Format | Width | Height | FPS | Max Duration | Aliases |
|
|
238
|
+
|--------|-------|--------|-----|-------------|---------|
|
|
239
|
+
| **YouTube** | 1920 | 1080 | 30 | — | landscape, standard |
|
|
240
|
+
| **YouTube 4K** | 3840 | 2160 | 30-60 | — | 4K |
|
|
241
|
+
| **YouTube Short** | 1080 | 1920 | 30-60 | 3 min | vertical |
|
|
242
|
+
| **TikTok** | 1080 | 1920 | 30 | 10 min | |
|
|
243
|
+
| **Instagram Reel** | 1080 | 1920 | 30 | 20 min | |
|
|
244
|
+
| **Instagram Post** | 1080 | 1080 | 30 | 60s | square |
|
|
245
|
+
| **Twitter/X** | 1920 | 1080 | 30 | 2m 20s | 512MB max |
|
|
246
|
+
| **Twitter/X Short** | 1080 | 1920 | 30 | 2m 20s | vertical tweet |
|
|
239
247
|
|
|
240
|
-
|
|
241
|
-
const frame = useCurrentFrame();
|
|
242
|
-
const opacity = interpolate(frame, [0, 30], [0, 1], { easing: easeInOut });
|
|
243
|
-
return <div style={{ opacity }}>Hello Vibeo</div>;
|
|
244
|
-
}
|
|
248
|
+
### Vertical video (9:16) layout tips
|
|
245
249
|
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
250
|
+
- **Code blocks**: max ~900px wide, font size 24-28 (larger than landscape)
|
|
251
|
+
- **No side-by-side**: use top/bottom stacks, not left/right splits
|
|
252
|
+
- **Text**: minimum 36px body, 64px+ titles
|
|
253
|
+
- **Safe zones**: avoid top 100px (status bar) and bottom 150px (nav gestures)
|
|
254
|
+
- **Single focus**: one idea per screen, no multi-column layouts
|
|
255
|
+
|
|
256
|
+
---
|
|
257
|
+
|
|
258
|
+
## Common Patterns
|
|
259
|
+
|
|
260
|
+
### Multi-file project structure (use for 3+ scenes)
|
|
261
|
+
|
|
262
|
+
```
|
|
263
|
+
src/
|
|
264
|
+
├── index.tsx # Root + Composition registration
|
|
265
|
+
├── Video.tsx # Scene orchestrator (Sequences)
|
|
266
|
+
├── scenes/
|
|
267
|
+
│ ├── Intro.tsx
|
|
268
|
+
│ ├── Problem.tsx
|
|
269
|
+
│ ├── Solution.tsx
|
|
270
|
+
│ └── Outro.tsx
|
|
271
|
+
└── components/
|
|
272
|
+
├── CodeBlock.tsx
|
|
273
|
+
└── AnimatedCard.tsx
|
|
255
274
|
```
|
|
256
275
|
|
|
257
|
-
###
|
|
276
|
+
### Centralized scene timing (best practice)
|
|
277
|
+
|
|
278
|
+
Define all timing in one place — never hardcode frame numbers in `<Sequence>`:
|
|
258
279
|
|
|
259
280
|
```tsx
|
|
281
|
+
const SCENES = {
|
|
282
|
+
intro: { from: 0, duration: 120 },
|
|
283
|
+
problem: { from: 120, duration: 300 },
|
|
284
|
+
solution: { from: 420, duration: 450 },
|
|
285
|
+
outro: { from: 870, duration: 90 },
|
|
286
|
+
} as const;
|
|
287
|
+
|
|
288
|
+
const TOTAL = SCENES.outro.from + SCENES.outro.duration;
|
|
289
|
+
|
|
260
290
|
function MyVideo() {
|
|
261
291
|
return (
|
|
262
292
|
<>
|
|
263
|
-
<Sequence from={
|
|
293
|
+
<Sequence from={SCENES.intro.from} durationInFrames={SCENES.intro.duration}>
|
|
264
294
|
<IntroScene />
|
|
265
295
|
</Sequence>
|
|
266
|
-
<Sequence from={
|
|
267
|
-
<
|
|
268
|
-
</Sequence>
|
|
269
|
-
<Sequence from={150} durationInFrames={60}>
|
|
270
|
-
<OutroScene />
|
|
296
|
+
<Sequence from={SCENES.problem.from} durationInFrames={SCENES.problem.duration}>
|
|
297
|
+
<ProblemScene />
|
|
271
298
|
</Sequence>
|
|
299
|
+
{/* ... */}
|
|
272
300
|
</>
|
|
273
301
|
);
|
|
274
302
|
}
|
|
275
303
|
```
|
|
276
304
|
|
|
277
|
-
###
|
|
305
|
+
### Looping/pulsing animation
|
|
306
|
+
|
|
307
|
+
Use `frame % N` for repeating effects (pulse, glow, rotate):
|
|
308
|
+
|
|
309
|
+
```tsx
|
|
310
|
+
const frame = useCurrentFrame();
|
|
311
|
+
const pulse = interpolate(frame % 60, [0, 30, 60], [0.3, 0.6, 0.3]);
|
|
312
|
+
const rotation = (frame % 90) * 4; // 360° every 3 seconds
|
|
313
|
+
```
|
|
314
|
+
|
|
315
|
+
### Staggered list/card animation
|
|
316
|
+
|
|
317
|
+
Animate N items with increasing delay:
|
|
318
|
+
|
|
319
|
+
```tsx
|
|
320
|
+
const frame = useCurrentFrame();
|
|
321
|
+
const items = ["Feature 1", "Feature 2", "Feature 3"];
|
|
322
|
+
|
|
323
|
+
{items.map((item, i) => {
|
|
324
|
+
const delay = 10 + i * 8;
|
|
325
|
+
const opacity = interpolate(frame, [delay, delay + 20], [0, 1], {
|
|
326
|
+
extrapolateLeft: "clamp", extrapolateRight: "clamp",
|
|
327
|
+
});
|
|
328
|
+
const y = interpolate(frame, [delay, delay + 20], [30, 0], {
|
|
329
|
+
easing: easeOut, extrapolateLeft: "clamp", extrapolateRight: "clamp",
|
|
330
|
+
});
|
|
331
|
+
return <div key={i} style={{ opacity, transform: `translateY(${y}px)` }}>{item}</div>;
|
|
332
|
+
})}
|
|
333
|
+
```
|
|
334
|
+
|
|
335
|
+
### Overlapping Sequences for manual transitions
|
|
336
|
+
|
|
337
|
+
Alternative to `<Transition>` when you need custom per-scene blend control:
|
|
278
338
|
|
|
279
339
|
```tsx
|
|
280
|
-
<Sequence from={0} durationInFrames={
|
|
281
|
-
<
|
|
282
|
-
|
|
283
|
-
|
|
340
|
+
<Sequence from={0} durationInFrames={90}>
|
|
341
|
+
<SceneA /> {/* fade out in last 15 frames */}
|
|
342
|
+
</Sequence>
|
|
343
|
+
<Sequence from={75} durationInFrames={90}>
|
|
344
|
+
<SceneB /> {/* fade in during first 15 frames */}
|
|
284
345
|
</Sequence>
|
|
285
346
|
```
|
|
286
347
|
|
|
287
348
|
### Multi-segment interpolation
|
|
288
349
|
|
|
289
350
|
```tsx
|
|
290
|
-
const frame = useCurrentFrame();
|
|
291
|
-
// Move right, then back, then down
|
|
292
351
|
const x = interpolate(frame, [0, 30, 60, 90], [0, 200, 0, 0]);
|
|
293
352
|
const y = interpolate(frame, [0, 30, 60, 90], [0, 0, 0, 200]);
|
|
294
353
|
```
|
|
@@ -371,10 +430,13 @@ bunx @vibeo/cli render --schema --token-count
|
|
|
371
430
|
|
|
372
431
|
### How LLMs Should Use Vibeo
|
|
373
432
|
|
|
374
|
-
1. **
|
|
375
|
-
2. **
|
|
376
|
-
3. **
|
|
377
|
-
4. **
|
|
378
|
-
5. **
|
|
433
|
+
1. **Create a project**: `bunx @vibeo/cli create my-video --template basic`
|
|
434
|
+
2. **Install deps**: `cd my-video && bun install`
|
|
435
|
+
3. **Install Playwright** (required for render/list): `bunx playwright install chromium`
|
|
436
|
+
4. **Edit `src/index.tsx`**: Write React components using `@vibeo/core` hooks and components
|
|
437
|
+
5. **Preview**: `bunx @vibeo/cli preview --entry src/index.tsx`
|
|
438
|
+
6. **Render**: `bunx @vibeo/cli render --entry src/index.tsx --composition MyComp`
|
|
439
|
+
|
|
440
|
+
Step 3 is mandatory — `vibeo render` and `vibeo list` will fail without Playwright browsers installed.
|
|
379
441
|
|
|
380
442
|
All commands accept `--format json` for structured output that LLMs can parse reliably.
|
|
@@ -0,0 +1,348 @@
|
|
|
1
|
+
# Vibeo Editor (`@vibeo/editor`)
|
|
2
|
+
|
|
3
|
+
## Overview
|
|
4
|
+
|
|
5
|
+
`@vibeo/editor` is a visual video editing studio for Vibeo. It provides a dark-themed, multi-track timeline editor with canvas preview, property editing, drag-and-drop clip manipulation, audio waveforms, subtitle editing, and export controls.
|
|
6
|
+
|
|
7
|
+
**When to use**: When you need a visual editing interface for Vibeo compositions, or when building a custom video editor UI on top of Vibeo.
|
|
8
|
+
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
## Quick Start
|
|
12
|
+
|
|
13
|
+
### CLI (recommended)
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
# Open the editor for a Vibeo project
|
|
17
|
+
bunx @vibeo/cli editor --entry src/index.tsx
|
|
18
|
+
|
|
19
|
+
# With custom port
|
|
20
|
+
bunx @vibeo/cli editor --entry src/index.tsx --port 8080
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
In a scaffolded project (`bunx @vibeo/cli create my-video`), just run:
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
bun run editor
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
### Programmatic
|
|
30
|
+
|
|
31
|
+
```tsx
|
|
32
|
+
import { Editor } from "@vibeo/editor";
|
|
33
|
+
|
|
34
|
+
const compositions = [
|
|
35
|
+
{
|
|
36
|
+
id: "MyComp",
|
|
37
|
+
name: "My Composition",
|
|
38
|
+
component: MyVideoComponent,
|
|
39
|
+
width: 1920,
|
|
40
|
+
height: 1080,
|
|
41
|
+
fps: 30,
|
|
42
|
+
durationInFrames: 300,
|
|
43
|
+
},
|
|
44
|
+
];
|
|
45
|
+
|
|
46
|
+
function App() {
|
|
47
|
+
return <Editor compositions={compositions} />;
|
|
48
|
+
}
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
---
|
|
52
|
+
|
|
53
|
+
## API Reference
|
|
54
|
+
|
|
55
|
+
### `Editor`
|
|
56
|
+
|
|
57
|
+
The root editor component. Renders the full editor UI.
|
|
58
|
+
|
|
59
|
+
```tsx
|
|
60
|
+
<Editor compositions={compositions} />
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
**Props:**
|
|
64
|
+
- `compositions` — Array of composition entries:
|
|
65
|
+
- `id: string` — unique composition identifier
|
|
66
|
+
- `name: string` — display name
|
|
67
|
+
- `component: React.ComponentType` — the React component to render
|
|
68
|
+
- `width: number` — composition width in pixels
|
|
69
|
+
- `height: number` — composition height in pixels
|
|
70
|
+
- `fps: number` — frames per second
|
|
71
|
+
- `durationInFrames: number` — total duration in frames
|
|
72
|
+
|
|
73
|
+
### `EditorProvider`
|
|
74
|
+
|
|
75
|
+
Context provider wrapping the editor state. Use if you need to embed editor components individually.
|
|
76
|
+
|
|
77
|
+
```tsx
|
|
78
|
+
import { EditorProvider } from "@vibeo/editor";
|
|
79
|
+
|
|
80
|
+
<EditorProvider>
|
|
81
|
+
{/* editor components */}
|
|
82
|
+
</EditorProvider>
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
---
|
|
86
|
+
|
|
87
|
+
## Editor Layout
|
|
88
|
+
|
|
89
|
+
```
|
|
90
|
+
┌─────────────────────────────────────────────────────────────┐
|
|
91
|
+
│ Toolbar: [⟲ Undo] [⟳ Redo] CompositionName 100% │
|
|
92
|
+
├──────────┬─────────────────────────────────┬────────────────┤
|
|
93
|
+
│ SCENES │ │ PROPERTIES │
|
|
94
|
+
│ & TRACKS │ Canvas Preview │ Canvas: W × H │
|
|
95
|
+
│ │ (live composition with │ Duration: N │
|
|
96
|
+
│ 🎬 Scene1│ checkerboard background) │ FPS: 30 │
|
|
97
|
+
│ 🎵 Audio │ │ │
|
|
98
|
+
│ 📝 Subs │ │ EXPORT │
|
|
99
|
+
│ │ │ Codec: H.264 │
|
|
100
|
+
│ [+Add] ├──── |◀ ▶ ▶| 00:00 / 03:33 1x ──│ [Render video] │
|
|
101
|
+
├──────────┴─────────────────────────────────┴────────────────┤
|
|
102
|
+
│ Timeline: ruler with time markers [- zoom +] │
|
|
103
|
+
│ Track 1: [████ Scene A ████][████ Scene B ████] │
|
|
104
|
+
│ Track 2: [████████ Audio Track ████████] │
|
|
105
|
+
│ Track 3: [Sub1] [Sub2] [Sub3] │
|
|
106
|
+
│ ▲ playhead cursor (draggable) │
|
|
107
|
+
└─────────────────────────────────────────────────────────────┘
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
### Panels
|
|
111
|
+
|
|
112
|
+
| Panel | Location | Purpose |
|
|
113
|
+
|-------|----------|---------|
|
|
114
|
+
| Toolbar | Top | Undo/redo, composition name, zoom % |
|
|
115
|
+
| Scene List | Left sidebar | Track list with visibility/mute toggles, "+ Add Track" |
|
|
116
|
+
| Canvas | Center top | Live preview of active composition |
|
|
117
|
+
| Playback Controls | Center middle | Play/pause, frame step, timecode, rate, loop |
|
|
118
|
+
| Timeline | Center bottom | Multi-track clip editor with ruler and playhead |
|
|
119
|
+
| Properties | Right sidebar | Editable props for selected clip, composition info, export |
|
|
120
|
+
|
|
121
|
+
All panels are resizable via drag handles between them.
|
|
122
|
+
|
|
123
|
+
---
|
|
124
|
+
|
|
125
|
+
## State Management
|
|
126
|
+
|
|
127
|
+
The editor uses `useReducer` with an immutable state model and undo/redo history.
|
|
128
|
+
|
|
129
|
+
### EditorState
|
|
130
|
+
|
|
131
|
+
```ts
|
|
132
|
+
interface EditorState {
|
|
133
|
+
tracks: Track[];
|
|
134
|
+
selectedTrackId: string | null;
|
|
135
|
+
selectedClipId: string | null;
|
|
136
|
+
playing: boolean;
|
|
137
|
+
frame: number;
|
|
138
|
+
fps: number;
|
|
139
|
+
durationInFrames: number;
|
|
140
|
+
compositionWidth: number;
|
|
141
|
+
compositionHeight: number;
|
|
142
|
+
playbackRate: number;
|
|
143
|
+
zoom: number;
|
|
144
|
+
scrollX: number;
|
|
145
|
+
}
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
### Track & Clip Types
|
|
149
|
+
|
|
150
|
+
```ts
|
|
151
|
+
type TrackType = "scene" | "audio" | "subtitle";
|
|
152
|
+
|
|
153
|
+
interface Track {
|
|
154
|
+
id: string;
|
|
155
|
+
name: string;
|
|
156
|
+
type: TrackType;
|
|
157
|
+
clips: Clip[];
|
|
158
|
+
visible: boolean;
|
|
159
|
+
muted: boolean;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
interface Clip {
|
|
163
|
+
id: string;
|
|
164
|
+
trackId: string;
|
|
165
|
+
name: string;
|
|
166
|
+
from: number; // start frame
|
|
167
|
+
durationInFrames: number;
|
|
168
|
+
type: TrackType;
|
|
169
|
+
data: any; // type-specific data
|
|
170
|
+
}
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
### Actions
|
|
174
|
+
|
|
175
|
+
| Action | Description |
|
|
176
|
+
|--------|-------------|
|
|
177
|
+
| `ADD_TRACK` | Add a new track (scene/audio/subtitle) |
|
|
178
|
+
| `REMOVE_TRACK` | Remove a track and all its clips |
|
|
179
|
+
| `ADD_CLIP` | Add a clip to a track |
|
|
180
|
+
| `REMOVE_CLIP` | Remove a clip |
|
|
181
|
+
| `MOVE_CLIP` | Change a clip's `from` position |
|
|
182
|
+
| `RESIZE_CLIP` | Change a clip's `from` and/or `durationInFrames` |
|
|
183
|
+
| `SELECT_CLIP` | Select a clip (highlights in timeline, shows in properties) |
|
|
184
|
+
| `SET_FRAME` | Set the current playhead frame |
|
|
185
|
+
| `SET_PLAYING` | Toggle playback |
|
|
186
|
+
| `SET_ZOOM` | Set timeline zoom level |
|
|
187
|
+
| `UNDO` | Undo last action |
|
|
188
|
+
| `REDO` | Redo undone action |
|
|
189
|
+
|
|
190
|
+
---
|
|
191
|
+
|
|
192
|
+
## Timeline
|
|
193
|
+
|
|
194
|
+
### Clip Colors
|
|
195
|
+
|
|
196
|
+
| Type | Color | Hex |
|
|
197
|
+
|------|-------|-----|
|
|
198
|
+
| Scene | Purple | `#8b5cf6` |
|
|
199
|
+
| Audio | Green | `#22c55e` |
|
|
200
|
+
| Subtitle | Yellow | `#eab308` |
|
|
201
|
+
|
|
202
|
+
### Timeline Math
|
|
203
|
+
|
|
204
|
+
```
|
|
205
|
+
pixelsPerFrame = basePixelsPerFrame * zoom (base = 2)
|
|
206
|
+
clipLeft = clip.from * pixelsPerFrame
|
|
207
|
+
clipWidth = clip.durationInFrames * pixelsPerFrame
|
|
208
|
+
cursorLeft = currentFrame * pixelsPerFrame
|
|
209
|
+
totalWidth = durationInFrames * pixelsPerFrame
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
### Drag-and-Drop
|
|
213
|
+
|
|
214
|
+
- **Move clip**: drag the clip body to change its `from` position
|
|
215
|
+
- **Resize clip**: drag left/right edges to trim `from`/`durationInFrames`
|
|
216
|
+
- **Minimum**: 1 frame
|
|
217
|
+
- **Snap**: rounds to nearest integer frame
|
|
218
|
+
|
|
219
|
+
---
|
|
220
|
+
|
|
221
|
+
## Keyboard Shortcuts
|
|
222
|
+
|
|
223
|
+
| Key | Action |
|
|
224
|
+
|-----|--------|
|
|
225
|
+
| Space | Play / Pause |
|
|
226
|
+
| ArrowLeft | Previous frame |
|
|
227
|
+
| ArrowRight | Next frame |
|
|
228
|
+
| Shift+ArrowLeft | -10 frames |
|
|
229
|
+
| Shift+ArrowRight | +10 frames |
|
|
230
|
+
| Cmd/Ctrl+Z | Undo |
|
|
231
|
+
| Cmd/Ctrl+Shift+Z | Redo |
|
|
232
|
+
| Delete / Backspace | Remove selected clip |
|
|
233
|
+
| Escape | Deselect |
|
|
234
|
+
| ? | Show keyboard shortcuts |
|
|
235
|
+
|
|
236
|
+
---
|
|
237
|
+
|
|
238
|
+
## Dark Theme
|
|
239
|
+
|
|
240
|
+
All colors are defined in `@vibeo/editor/theme/colors`:
|
|
241
|
+
|
|
242
|
+
| Token | Value | Usage |
|
|
243
|
+
|-------|-------|-------|
|
|
244
|
+
| `bg` | `#0d1117` | Darkest background |
|
|
245
|
+
| `surface` | `#161b22` | Panel backgrounds |
|
|
246
|
+
| `surfaceHover` | `#1c2333` | Hover states |
|
|
247
|
+
| `border` | `#30363d` | Borders |
|
|
248
|
+
| `text` | `#e6edf3` | Primary text |
|
|
249
|
+
| `textMuted` | `#7d8590` | Secondary text |
|
|
250
|
+
| `accent` | `#58a6ff` | Playhead, selection, focus rings |
|
|
251
|
+
| `scene` | `#8b5cf6` | Scene clip color |
|
|
252
|
+
| `audio` | `#22c55e` | Audio clip color |
|
|
253
|
+
| `subtitle` | `#eab308` | Subtitle clip color |
|
|
254
|
+
| `danger` | `#f85149` | Delete, errors |
|
|
255
|
+
|
|
256
|
+
---
|
|
257
|
+
|
|
258
|
+
## Common Patterns
|
|
259
|
+
|
|
260
|
+
### Adding a scene track with a clip
|
|
261
|
+
|
|
262
|
+
```ts
|
|
263
|
+
dispatch({ type: "ADD_TRACK", payload: { id: "track-1", name: "Main Scene", type: "scene" } });
|
|
264
|
+
dispatch({
|
|
265
|
+
type: "ADD_CLIP",
|
|
266
|
+
payload: {
|
|
267
|
+
id: "clip-1",
|
|
268
|
+
trackId: "track-1",
|
|
269
|
+
name: "Intro",
|
|
270
|
+
from: 0,
|
|
271
|
+
durationInFrames: 90,
|
|
272
|
+
type: "scene",
|
|
273
|
+
data: { component: IntroScene },
|
|
274
|
+
},
|
|
275
|
+
});
|
|
276
|
+
```
|
|
277
|
+
|
|
278
|
+
### Adding an audio track
|
|
279
|
+
|
|
280
|
+
```ts
|
|
281
|
+
dispatch({ type: "ADD_TRACK", payload: { id: "track-2", name: "Background Music", type: "audio" } });
|
|
282
|
+
dispatch({
|
|
283
|
+
type: "ADD_CLIP",
|
|
284
|
+
payload: {
|
|
285
|
+
id: "clip-2",
|
|
286
|
+
trackId: "track-2",
|
|
287
|
+
name: "Music",
|
|
288
|
+
from: 0,
|
|
289
|
+
durationInFrames: 300,
|
|
290
|
+
type: "audio",
|
|
291
|
+
data: { src: "/music.mp3", volume: 0.8 },
|
|
292
|
+
},
|
|
293
|
+
});
|
|
294
|
+
```
|
|
295
|
+
|
|
296
|
+
### Adding subtitle cues
|
|
297
|
+
|
|
298
|
+
```ts
|
|
299
|
+
dispatch({ type: "ADD_TRACK", payload: { id: "track-3", name: "Subtitles", type: "subtitle" } });
|
|
300
|
+
dispatch({
|
|
301
|
+
type: "ADD_CLIP",
|
|
302
|
+
payload: {
|
|
303
|
+
id: "sub-1",
|
|
304
|
+
trackId: "track-3",
|
|
305
|
+
name: "Welcome",
|
|
306
|
+
from: 15,
|
|
307
|
+
durationInFrames: 75,
|
|
308
|
+
type: "subtitle",
|
|
309
|
+
data: { text: "Welcome to the demo", fontSize: 36, color: "white", position: "bottom" },
|
|
310
|
+
},
|
|
311
|
+
});
|
|
312
|
+
```
|
|
313
|
+
|
|
314
|
+
---
|
|
315
|
+
|
|
316
|
+
## Gotchas
|
|
317
|
+
|
|
318
|
+
1. **Editor creates its own providers** — it wraps content in `VibeoRoot`, `TimelineProvider`, etc. Don't nest it inside another `VibeoRoot` manually.
|
|
319
|
+
|
|
320
|
+
2. **Compositions must export a `Root` function** — the CLI editor command imports `{ Root }` from your entry file to register compositions.
|
|
321
|
+
|
|
322
|
+
3. **The editor renders the Player internally** — it uses `@vibeo/player` to show the canvas preview. Frame sync is handled automatically.
|
|
323
|
+
|
|
324
|
+
4. **Undo/redo is state-level** — every dispatched action pushes to the history stack. `UNDO`/`REDO` actions navigate the stack.
|
|
325
|
+
|
|
326
|
+
5. **Timeline zoom range** — 0.1x to 10x. Default is 1x where `pixelsPerFrame = 2`.
|
|
327
|
+
|
|
328
|
+
6. **Audio waveform** — rendered as canvas bars inside audio clips. If audio analysis isn't available, shows flat placeholder bars.
|
|
329
|
+
|
|
330
|
+
7. **Subtitle inline editing** — double-click a subtitle cue in the timeline to edit text inline. Press Escape or click outside to save.
|
|
331
|
+
|
|
332
|
+
---
|
|
333
|
+
|
|
334
|
+
## LLM & Agent Integration
|
|
335
|
+
|
|
336
|
+
```bash
|
|
337
|
+
# Get CLI docs including editor command
|
|
338
|
+
bunx @vibeo/cli --llms
|
|
339
|
+
|
|
340
|
+
# Get editor command schema
|
|
341
|
+
bunx @vibeo/cli editor --schema
|
|
342
|
+
|
|
343
|
+
# Install skills for all LLM tools
|
|
344
|
+
bunx @vibeo/cli install-skills
|
|
345
|
+
|
|
346
|
+
# Run as MCP server (includes editor command)
|
|
347
|
+
bunx @vibeo/cli --mcp
|
|
348
|
+
```
|
|
@@ -218,24 +218,27 @@ const scale = useKeyframes(frame, { 0: 1, 15: 1.2, 30: 1 }, { easing: easeInOut
|
|
|
218
218
|
|
|
219
219
|
## Spring Animation Guide
|
|
220
220
|
|
|
221
|
-
###
|
|
222
|
-
```ts
|
|
223
|
-
useSpring({ from: 0, to: 1, config: { stiffness: 300, damping: 30 } })
|
|
224
|
-
```
|
|
221
|
+
### Named Spring Presets
|
|
225
222
|
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
223
|
+
| Name | stiffness | damping | mass | Use case |
|
|
224
|
+
|------|-----------|---------|------|----------|
|
|
225
|
+
| **snappy** | 300 | 30 | 1 | UI elements, quick responses |
|
|
226
|
+
| **bouncy** | 400 | 10 | 1 | Playful entrances, logos |
|
|
227
|
+
| **gentle** | 100 | 26 | 1 | Subtle movements, text |
|
|
228
|
+
| **heavy** | 50 | 20 | 3 | Large elements, dramatic reveals |
|
|
229
|
+
| **elastic** | 200 | 8 | 1 | Overshoot effects, attention grabbers |
|
|
230
230
|
|
|
231
|
-
### Slow, heavy drag
|
|
232
231
|
```ts
|
|
232
|
+
// Snappy
|
|
233
|
+
useSpring({ from: 0, to: 1, config: { stiffness: 300, damping: 30, mass: 1 } })
|
|
234
|
+
// Bouncy
|
|
235
|
+
useSpring({ from: 0, to: 1, config: { stiffness: 400, damping: 10, mass: 1 } })
|
|
236
|
+
// Gentle
|
|
237
|
+
useSpring({ from: 0, to: 1, config: { stiffness: 100, damping: 26, mass: 1 } })
|
|
238
|
+
// Heavy
|
|
233
239
|
useSpring({ from: 0, to: 1, config: { stiffness: 50, damping: 20, mass: 3 } })
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
### Gentle ease
|
|
237
|
-
```ts
|
|
238
|
-
useSpring({ from: 0, to: 1, config: { stiffness: 100, damping: 26 } })
|
|
240
|
+
// Elastic
|
|
241
|
+
useSpring({ from: 0, to: 1, config: { stiffness: 200, damping: 8, mass: 1 } })
|
|
239
242
|
```
|
|
240
243
|
|
|
241
244
|
**Tuning guide**:
|
|
@@ -349,6 +352,151 @@ function ReactiveBackground({ children }: { children: React.ReactNode }) {
|
|
|
349
352
|
|
|
350
353
|
---
|
|
351
354
|
|
|
355
|
+
## CodeBlock Recipe (for developer/tutorial content)
|
|
356
|
+
|
|
357
|
+
The most common component in programming videos. Takes a plain string + optional highlight words:
|
|
358
|
+
|
|
359
|
+
```tsx
|
|
360
|
+
function CodeBlock({
|
|
361
|
+
code,
|
|
362
|
+
highlights = [],
|
|
363
|
+
fontSize = 24,
|
|
364
|
+
startFrame = 0,
|
|
365
|
+
lineDelay = 3,
|
|
366
|
+
}: {
|
|
367
|
+
code: string;
|
|
368
|
+
highlights?: { word: string; color: string }[];
|
|
369
|
+
fontSize?: number;
|
|
370
|
+
startFrame?: number;
|
|
371
|
+
lineDelay?: number;
|
|
372
|
+
}) {
|
|
373
|
+
const frame = useCurrentFrame();
|
|
374
|
+
const lines = code.split("\n");
|
|
375
|
+
|
|
376
|
+
return (
|
|
377
|
+
<div style={{
|
|
378
|
+
background: "#1e1e2e",
|
|
379
|
+
borderRadius: 12,
|
|
380
|
+
padding: "24px 32px",
|
|
381
|
+
fontFamily: "'SF Mono', 'Cascadia Code', monospace",
|
|
382
|
+
fontSize,
|
|
383
|
+
lineHeight: 1.6,
|
|
384
|
+
overflow: "hidden",
|
|
385
|
+
}}>
|
|
386
|
+
{lines.map((line, i) => {
|
|
387
|
+
const lineFrame = startFrame + i * lineDelay;
|
|
388
|
+
const opacity = interpolate(frame, [lineFrame, lineFrame + 10], [0, 1], {
|
|
389
|
+
extrapolateLeft: "clamp", extrapolateRight: "clamp",
|
|
390
|
+
});
|
|
391
|
+
|
|
392
|
+
let html = line.replace(/&/g, "&").replace(/</g, "<");
|
|
393
|
+
for (const h of highlights) {
|
|
394
|
+
html = html.replaceAll(h.word, `<span style="color:${h.color}">${h.word}</span>`);
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
return (
|
|
398
|
+
<div key={i} style={{ opacity, whiteSpace: "pre" }} dangerouslySetInnerHTML={{ __html: html || " " }} />
|
|
399
|
+
);
|
|
400
|
+
})}
|
|
401
|
+
</div>
|
|
402
|
+
);
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
// Usage:
|
|
406
|
+
<CodeBlock
|
|
407
|
+
code={`function Foo() {\n return <div>hello</div>;\n}`}
|
|
408
|
+
highlights={[{ word: "function", color: "#c678dd" }, { word: "return", color: "#c678dd" }]}
|
|
409
|
+
startFrame={10}
|
|
410
|
+
/>
|
|
411
|
+
```
|
|
412
|
+
|
|
413
|
+
---
|
|
414
|
+
|
|
415
|
+
## SVG Animation Recipes
|
|
416
|
+
|
|
417
|
+
### Spring-scale an SVG (logo entrance)
|
|
418
|
+
|
|
419
|
+
```tsx
|
|
420
|
+
function AnimatedLogo() {
|
|
421
|
+
const frame = useCurrentFrame();
|
|
422
|
+
const scale = useSpring({ from: 0, to: 1, frame, fps: 30, config: { stiffness: 400, damping: 10 } });
|
|
423
|
+
const rotation = interpolate(frame, [0, 30], [180, 0], { extrapolateRight: "clamp" });
|
|
424
|
+
|
|
425
|
+
return (
|
|
426
|
+
<svg width={200} height={200} style={{
|
|
427
|
+
transform: `scale(${scale}) rotate(${rotation}deg)`,
|
|
428
|
+
filter: `drop-shadow(0 0 ${20 * scale}px rgba(97, 218, 251, 0.6))`,
|
|
429
|
+
}}>
|
|
430
|
+
{/* SVG content */}
|
|
431
|
+
</svg>
|
|
432
|
+
);
|
|
433
|
+
}
|
|
434
|
+
```
|
|
435
|
+
|
|
436
|
+
### Rotating SVG with glow
|
|
437
|
+
|
|
438
|
+
```tsx
|
|
439
|
+
const frame = useCurrentFrame();
|
|
440
|
+
const rotation = (frame % 90) * 4; // full rotation every 3s at 30fps
|
|
441
|
+
const glowIntensity = interpolate(frame % 60, [0, 30, 60], [10, 25, 10]);
|
|
442
|
+
|
|
443
|
+
<svg style={{
|
|
444
|
+
transform: `rotate(${rotation}deg)`,
|
|
445
|
+
filter: `drop-shadow(0 0 ${glowIntensity}px cyan)`,
|
|
446
|
+
}} />
|
|
447
|
+
```
|
|
448
|
+
|
|
449
|
+
---
|
|
450
|
+
|
|
451
|
+
## Split-Screen / Comparison Layout
|
|
452
|
+
|
|
453
|
+
Before/after code comparison — common in tutorial content:
|
|
454
|
+
|
|
455
|
+
```tsx
|
|
456
|
+
function SplitScreen({
|
|
457
|
+
left,
|
|
458
|
+
right,
|
|
459
|
+
dividerLabel = "VS",
|
|
460
|
+
}: {
|
|
461
|
+
left: React.ReactNode;
|
|
462
|
+
right: React.ReactNode;
|
|
463
|
+
dividerLabel?: string;
|
|
464
|
+
}) {
|
|
465
|
+
const frame = useCurrentFrame();
|
|
466
|
+
const { width, height } = useVideoConfig();
|
|
467
|
+
|
|
468
|
+
// Stagger: left appears first, divider, then right
|
|
469
|
+
const leftOpacity = interpolate(frame, [0, 15], [0, 1], { extrapolateRight: "clamp" });
|
|
470
|
+
const rightOpacity = interpolate(frame, [20, 35], [0, 1], { extrapolateRight: "clamp" });
|
|
471
|
+
const dividerOpacity = interpolate(frame, [10, 20], [0, 1], { extrapolateRight: "clamp" });
|
|
472
|
+
|
|
473
|
+
return (
|
|
474
|
+
<div style={{ display: "flex", width, height }}>
|
|
475
|
+
<div style={{ flex: 1, opacity: leftOpacity, padding: 40 }}>{left}</div>
|
|
476
|
+
<div style={{
|
|
477
|
+
width: 60, display: "flex", alignItems: "center", justifyContent: "center",
|
|
478
|
+
opacity: dividerOpacity, color: "#888", fontSize: 20, fontWeight: 700,
|
|
479
|
+
}}>
|
|
480
|
+
{dividerLabel}
|
|
481
|
+
</div>
|
|
482
|
+
<div style={{ flex: 1, opacity: rightOpacity, padding: 40 }}>{right}</div>
|
|
483
|
+
</div>
|
|
484
|
+
);
|
|
485
|
+
}
|
|
486
|
+
```
|
|
487
|
+
|
|
488
|
+
For vertical video (9:16), stack top/bottom instead of left/right:
|
|
489
|
+
|
|
490
|
+
```tsx
|
|
491
|
+
<div style={{ display: "flex", flexDirection: "column", width, height }}>
|
|
492
|
+
<div style={{ flex: 1 }}>{before}</div>
|
|
493
|
+
<div style={{ height: 4, background: "#333" }} />
|
|
494
|
+
<div style={{ flex: 1 }}>{after}</div>
|
|
495
|
+
</div>
|
|
496
|
+
```
|
|
497
|
+
|
|
498
|
+
---
|
|
499
|
+
|
|
352
500
|
## Gotchas and Tips
|
|
353
501
|
|
|
354
502
|
1. **`useAudioData` returns `null` initially** — always handle the loading state.
|
|
@@ -355,10 +355,13 @@ bunx @vibeo/cli render --schema --token-count
|
|
|
355
355
|
|
|
356
356
|
### How LLMs Should Use Vibeo
|
|
357
357
|
|
|
358
|
-
1. **
|
|
359
|
-
2. **
|
|
360
|
-
3. **
|
|
361
|
-
4. **
|
|
362
|
-
5. **
|
|
358
|
+
1. **Create a project**: `bunx @vibeo/cli create my-video --template basic`
|
|
359
|
+
2. **Install deps**: `cd my-video && bun install`
|
|
360
|
+
3. **Install Playwright** (REQUIRED for render/list): `bunx playwright install chromium`
|
|
361
|
+
4. **Edit `src/index.tsx`**: Write React components using `@vibeo/core` hooks and components
|
|
362
|
+
5. **Preview**: `bunx @vibeo/cli preview --entry src/index.tsx`
|
|
363
|
+
6. **Render**: `bunx @vibeo/cli render --entry src/index.tsx --composition MyComp`
|
|
364
|
+
|
|
365
|
+
**Step 3 is mandatory** — `vibeo render` and `vibeo list` will fail with a cryptic Playwright error without it. `vibeo preview` works without it (uses Bun.serve, not Playwright).
|
|
363
366
|
|
|
364
367
|
All commands accept `--format json` for structured output that LLMs can parse reliably.
|
|
@@ -0,0 +1,319 @@
|
|
|
1
|
+
# Vibeo TikTok / Short-Form Video Generator
|
|
2
|
+
|
|
3
|
+
Generate TikTok, YouTube Shorts, Instagram Reels, and other vertical (9:16) videos using Vibeo.
|
|
4
|
+
|
|
5
|
+
**Trigger**: user says "TikTok", "Short", "Reel", "vertical video", "short-form", or asks for content for social media platforms.
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## Format: ALWAYS 1080x1920 (9:16)
|
|
10
|
+
|
|
11
|
+
```tsx
|
|
12
|
+
<Composition
|
|
13
|
+
id="MyTikTok"
|
|
14
|
+
component={TikTokVideo}
|
|
15
|
+
width={1080}
|
|
16
|
+
height={1920}
|
|
17
|
+
fps={30}
|
|
18
|
+
durationInFrames={450} // 15 seconds
|
|
19
|
+
/>
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
### Duration Guidelines
|
|
23
|
+
|
|
24
|
+
| Platform | Sweet Spot | Max | Frames @ 30fps |
|
|
25
|
+
|----------|-----------|-----|-----------------|
|
|
26
|
+
| **TikTok** | 15-60s | 10 min | 450-1800 |
|
|
27
|
+
| **YouTube Short** | 30-60s | 3 min | 900-1800 |
|
|
28
|
+
| **Instagram Reel** | 15-30s | 20 min | 450-900 |
|
|
29
|
+
| **Twitter/X** | 15-45s | 2m20s | 450-1350 |
|
|
30
|
+
|
|
31
|
+
---
|
|
32
|
+
|
|
33
|
+
## Vertical Layout Rules
|
|
34
|
+
|
|
35
|
+
### DO
|
|
36
|
+
|
|
37
|
+
```tsx
|
|
38
|
+
// Stack vertically — top to bottom flow
|
|
39
|
+
<div style={{
|
|
40
|
+
width: 1080, height: 1920,
|
|
41
|
+
display: "flex", flexDirection: "column",
|
|
42
|
+
justifyContent: "center", alignItems: "center",
|
|
43
|
+
padding: "100px 60px 150px", // safe zones: top status bar + bottom nav
|
|
44
|
+
}}>
|
|
45
|
+
<h1 style={{ fontSize: 72, textAlign: "center" }}>Title</h1>
|
|
46
|
+
<div style={{ marginTop: 40 }}>
|
|
47
|
+
{/* Content */}
|
|
48
|
+
</div>
|
|
49
|
+
</div>
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
### DON'T
|
|
53
|
+
|
|
54
|
+
- No side-by-side layouts (too narrow at 1080px)
|
|
55
|
+
- No font size < 36px (unreadable on phones)
|
|
56
|
+
- No content in top 100px (status bar) or bottom 150px (nav gestures)
|
|
57
|
+
- No multi-column grids
|
|
58
|
+
- No landscape aspect ratios embedded inside
|
|
59
|
+
|
|
60
|
+
---
|
|
61
|
+
|
|
62
|
+
## Complete TikTok Template
|
|
63
|
+
|
|
64
|
+
Hook → Content → CTA pattern (the standard viral format):
|
|
65
|
+
|
|
66
|
+
```tsx
|
|
67
|
+
import React from "react";
|
|
68
|
+
import {
|
|
69
|
+
Composition, Sequence, VibeoRoot,
|
|
70
|
+
useCurrentFrame, useVideoConfig, interpolate, easeOut, easeInOut,
|
|
71
|
+
} from "@vibeo/core";
|
|
72
|
+
|
|
73
|
+
// ---- Scene Timing (centralized) ----
|
|
74
|
+
const SCENES = {
|
|
75
|
+
hook: { from: 0, duration: 90 }, // 3s — attention grabber
|
|
76
|
+
content: { from: 90, duration: 270 }, // 9s — main content
|
|
77
|
+
cta: { from: 360, duration: 90 }, // 3s — call to action
|
|
78
|
+
} as const;
|
|
79
|
+
const TOTAL = 450; // 15s
|
|
80
|
+
|
|
81
|
+
// ---- Hook Scene (first 3 seconds = make or break) ----
|
|
82
|
+
function HookScene({ text }: { text: string }) {
|
|
83
|
+
const frame = useCurrentFrame();
|
|
84
|
+
const { width, height } = useVideoConfig();
|
|
85
|
+
|
|
86
|
+
const scale = interpolate(frame, [0, 15], [0.5, 1], {
|
|
87
|
+
easing: easeOut, extrapolateRight: "clamp",
|
|
88
|
+
});
|
|
89
|
+
const opacity = interpolate(frame, [0, 10], [0, 1], { extrapolateRight: "clamp" });
|
|
90
|
+
|
|
91
|
+
// Pulsing glow effect
|
|
92
|
+
const glow = interpolate(frame % 30, [0, 15, 30], [0, 15, 0]);
|
|
93
|
+
|
|
94
|
+
return (
|
|
95
|
+
<div style={{
|
|
96
|
+
width, height, display: "flex", justifyContent: "center", alignItems: "center",
|
|
97
|
+
background: "linear-gradient(180deg, #0a0a0a, #1a0a2e)",
|
|
98
|
+
padding: "100px 60px 150px",
|
|
99
|
+
}}>
|
|
100
|
+
<h1 style={{
|
|
101
|
+
fontSize: 80, fontWeight: 900, color: "white",
|
|
102
|
+
textAlign: "center", lineHeight: 1.2,
|
|
103
|
+
opacity, transform: `scale(${scale})`,
|
|
104
|
+
textShadow: `0 0 ${glow}px rgba(138, 92, 246, 0.8)`,
|
|
105
|
+
}}>
|
|
106
|
+
{text}
|
|
107
|
+
</h1>
|
|
108
|
+
</div>
|
|
109
|
+
);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// ---- Content Scene (staggered points) ----
|
|
113
|
+
function ContentScene({ points }: { points: string[] }) {
|
|
114
|
+
const frame = useCurrentFrame();
|
|
115
|
+
const { width, height } = useVideoConfig();
|
|
116
|
+
|
|
117
|
+
return (
|
|
118
|
+
<div style={{
|
|
119
|
+
width, height, display: "flex", flexDirection: "column",
|
|
120
|
+
justifyContent: "center", padding: "100px 60px 150px",
|
|
121
|
+
background: "#0a0a0a", gap: 24,
|
|
122
|
+
}}>
|
|
123
|
+
{points.map((point, i) => {
|
|
124
|
+
const delay = 15 + i * 20;
|
|
125
|
+
const opacity = interpolate(frame, [delay, delay + 15], [0, 1], {
|
|
126
|
+
extrapolateLeft: "clamp", extrapolateRight: "clamp",
|
|
127
|
+
});
|
|
128
|
+
const x = interpolate(frame, [delay, delay + 15], [-40, 0], {
|
|
129
|
+
easing: easeOut, extrapolateLeft: "clamp", extrapolateRight: "clamp",
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
return (
|
|
133
|
+
<div key={i} style={{
|
|
134
|
+
opacity, transform: `translateX(${x}px)`,
|
|
135
|
+
display: "flex", alignItems: "center", gap: 16,
|
|
136
|
+
}}>
|
|
137
|
+
<div style={{
|
|
138
|
+
width: 48, height: 48, borderRadius: 12,
|
|
139
|
+
background: "linear-gradient(135deg, #8b5cf6, #06b6d4)",
|
|
140
|
+
display: "flex", alignItems: "center", justifyContent: "center",
|
|
141
|
+
fontSize: 24, fontWeight: 700, color: "white",
|
|
142
|
+
}}>
|
|
143
|
+
{i + 1}
|
|
144
|
+
</div>
|
|
145
|
+
<span style={{ fontSize: 40, color: "white", fontWeight: 600 }}>
|
|
146
|
+
{point}
|
|
147
|
+
</span>
|
|
148
|
+
</div>
|
|
149
|
+
);
|
|
150
|
+
})}
|
|
151
|
+
</div>
|
|
152
|
+
);
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
// ---- CTA Scene ----
|
|
156
|
+
function CTAScene({ text, subtext }: { text: string; subtext: string }) {
|
|
157
|
+
const frame = useCurrentFrame();
|
|
158
|
+
const { width, height } = useVideoConfig();
|
|
159
|
+
|
|
160
|
+
const scale = interpolate(frame, [0, 20], [0.8, 1], {
|
|
161
|
+
easing: easeOut, extrapolateRight: "clamp",
|
|
162
|
+
});
|
|
163
|
+
// Pulsing button effect
|
|
164
|
+
const btnScale = interpolate(frame % 30, [0, 15, 30], [1, 1.05, 1]);
|
|
165
|
+
|
|
166
|
+
return (
|
|
167
|
+
<div style={{
|
|
168
|
+
width, height, display: "flex", flexDirection: "column",
|
|
169
|
+
justifyContent: "center", alignItems: "center",
|
|
170
|
+
background: "linear-gradient(180deg, #0a0a0a, #1a0a2e)",
|
|
171
|
+
padding: "100px 60px 150px", transform: `scale(${scale})`,
|
|
172
|
+
}}>
|
|
173
|
+
<h1 style={{ fontSize: 72, fontWeight: 900, color: "white", textAlign: "center" }}>
|
|
174
|
+
{text}
|
|
175
|
+
</h1>
|
|
176
|
+
<div style={{
|
|
177
|
+
marginTop: 40, padding: "20px 60px", borderRadius: 50,
|
|
178
|
+
background: "linear-gradient(135deg, #8b5cf6, #06b6d4)",
|
|
179
|
+
fontSize: 36, fontWeight: 700, color: "white",
|
|
180
|
+
transform: `scale(${btnScale})`,
|
|
181
|
+
}}>
|
|
182
|
+
{subtext}
|
|
183
|
+
</div>
|
|
184
|
+
</div>
|
|
185
|
+
);
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
// ---- Main Video ----
|
|
189
|
+
function TikTokVideo() {
|
|
190
|
+
return (
|
|
191
|
+
<>
|
|
192
|
+
<Sequence from={SCENES.hook.from} durationInFrames={SCENES.hook.duration}>
|
|
193
|
+
<HookScene text="3 things you didn't know about React" />
|
|
194
|
+
</Sequence>
|
|
195
|
+
<Sequence from={SCENES.content.from} durationInFrames={SCENES.content.duration}>
|
|
196
|
+
<ContentScene points={[
|
|
197
|
+
"Server Components are default",
|
|
198
|
+
"use() replaces useEffect",
|
|
199
|
+
"Actions replace forms",
|
|
200
|
+
]} />
|
|
201
|
+
</Sequence>
|
|
202
|
+
<Sequence from={SCENES.cta.from} durationInFrames={SCENES.cta.duration}>
|
|
203
|
+
<CTAScene text="Follow for more" subtext="Like & Share →" />
|
|
204
|
+
</Sequence>
|
|
205
|
+
</>
|
|
206
|
+
);
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
// ---- Root ----
|
|
210
|
+
export function Root() {
|
|
211
|
+
return (
|
|
212
|
+
<VibeoRoot>
|
|
213
|
+
<Composition
|
|
214
|
+
id="TikTokVideo"
|
|
215
|
+
component={TikTokVideo}
|
|
216
|
+
width={1080}
|
|
217
|
+
height={1920}
|
|
218
|
+
fps={30}
|
|
219
|
+
durationInFrames={TOTAL}
|
|
220
|
+
/>
|
|
221
|
+
</VibeoRoot>
|
|
222
|
+
);
|
|
223
|
+
}
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
---
|
|
227
|
+
|
|
228
|
+
## TikTok Content Patterns
|
|
229
|
+
|
|
230
|
+
### 1. "Did You Know" / Listicle (most common)
|
|
231
|
+
|
|
232
|
+
```
|
|
233
|
+
Hook (3s): Bold question or surprising claim
|
|
234
|
+
Content (9-20s): 3-5 staggered points with numbers
|
|
235
|
+
CTA (3s): Follow / Like / Share
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
### 2. Before/After
|
|
239
|
+
|
|
240
|
+
```
|
|
241
|
+
Hook (2s): "Before vs After"
|
|
242
|
+
Before (5s): Show the "bad" version (top half)
|
|
243
|
+
After (5s): Reveal the "good" version (bottom half) — use slide transition
|
|
244
|
+
CTA (3s): "Try it yourself"
|
|
245
|
+
```
|
|
246
|
+
|
|
247
|
+
For vertical before/after, stack top/bottom:
|
|
248
|
+
```tsx
|
|
249
|
+
<div style={{ display: "flex", flexDirection: "column", width: 1080, height: 1920 }}>
|
|
250
|
+
<div style={{ flex: 1 }}>{before}</div>
|
|
251
|
+
<div style={{ height: 4, background: "#333" }} />
|
|
252
|
+
<div style={{ flex: 1 }}>{after}</div>
|
|
253
|
+
</div>
|
|
254
|
+
```
|
|
255
|
+
|
|
256
|
+
### 3. Code Tutorial
|
|
257
|
+
|
|
258
|
+
```
|
|
259
|
+
Hook (3s): "This one trick..."
|
|
260
|
+
Code (10-15s): Animated code block, line by line reveal
|
|
261
|
+
Result (5s): Show the output/demo
|
|
262
|
+
CTA (3s): "Link in bio"
|
|
263
|
+
```
|
|
264
|
+
|
|
265
|
+
Use the CodeBlock recipe from vibeo-effects skill, but with larger font (28px) for vertical.
|
|
266
|
+
|
|
267
|
+
### 4. Product/Feature Showcase
|
|
268
|
+
|
|
269
|
+
```
|
|
270
|
+
Hook (3s): Pain point question
|
|
271
|
+
Demo (10s): Screen recording or animated mockup
|
|
272
|
+
Features (5s): 3 key bullets, staggered
|
|
273
|
+
CTA (3s): "Download now"
|
|
274
|
+
```
|
|
275
|
+
|
|
276
|
+
---
|
|
277
|
+
|
|
278
|
+
## Animation Rules for Short-Form
|
|
279
|
+
|
|
280
|
+
1. **First 3 seconds are everything** — the hook must grab attention immediately. Use scale-in, glow, or spring entrance.
|
|
281
|
+
|
|
282
|
+
2. **Constant motion** — never have a static frame for more than 1 second. Add subtle pulse, breathing, or background movement.
|
|
283
|
+
|
|
284
|
+
3. **Text on screen** — most viewers watch without sound. Every key point should have on-screen text.
|
|
285
|
+
|
|
286
|
+
4. **Fast transitions** — 10-15 frame transitions (0.3-0.5s). No slow fades.
|
|
287
|
+
|
|
288
|
+
5. **Pulsing/breathing effects** — use `frame % N` for constant subtle animation:
|
|
289
|
+
```tsx
|
|
290
|
+
const pulse = interpolate(frame % 60, [0, 30, 60], [1, 1.03, 1]);
|
|
291
|
+
```
|
|
292
|
+
|
|
293
|
+
6. **Number badges** — always number your points (1, 2, 3). It signals structure and keeps viewers watching.
|
|
294
|
+
|
|
295
|
+
7. **Bold gradients** — dark backgrounds with vibrant gradient accents. Avoid flat solid colors.
|
|
296
|
+
|
|
297
|
+
---
|
|
298
|
+
|
|
299
|
+
## Rendering for Platforms
|
|
300
|
+
|
|
301
|
+
```bash
|
|
302
|
+
# TikTok / Reels (H.264, most compatible)
|
|
303
|
+
bunx @vibeo/cli render --entry src/index.tsx --composition TikTokVideo --codec h264
|
|
304
|
+
|
|
305
|
+
# High quality (H.265, smaller file)
|
|
306
|
+
bunx @vibeo/cli render --entry src/index.tsx --composition TikTokVideo --codec h265
|
|
307
|
+
|
|
308
|
+
# Twitter/X (needs smaller file, use lower quality)
|
|
309
|
+
bunx @vibeo/cli render --entry src/index.tsx --composition TikTokVideo --codec h264 --quality 60
|
|
310
|
+
```
|
|
311
|
+
|
|
312
|
+
### Platform upload limits
|
|
313
|
+
|
|
314
|
+
| Platform | Max File Size | Recommended Codec |
|
|
315
|
+
|----------|--------------|-------------------|
|
|
316
|
+
| TikTok | 287 MB | H.264 |
|
|
317
|
+
| YouTube Shorts | 256 MB | H.264 |
|
|
318
|
+
| Instagram Reels | 650 MB | H.264 |
|
|
319
|
+
| Twitter/X | 512 MB | H.264 |
|
package/src/commands/create.ts
CHANGED
|
@@ -246,6 +246,7 @@ export async function createProject(
|
|
|
246
246
|
dev: "bun vibeo preview --entry src/index.tsx",
|
|
247
247
|
build: "bun vibeo render --entry src/index.tsx",
|
|
248
248
|
list: "bun vibeo list --entry src/index.tsx",
|
|
249
|
+
editor: "bun vibeo editor --entry src/index.tsx",
|
|
249
250
|
typecheck: "bunx tsc --noEmit",
|
|
250
251
|
},
|
|
251
252
|
dependencies: {
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { bundleForEditor } from "@vibeo/renderer";
|
|
2
|
+
|
|
3
|
+
export async function startEditor(entry: string, port: number): Promise<void> {
|
|
4
|
+
console.log(`Starting editor server...`);
|
|
5
|
+
console.log(` Entry: ${entry}`);
|
|
6
|
+
|
|
7
|
+
const bundleResult = await bundleForEditor(entry, port);
|
|
8
|
+
|
|
9
|
+
console.log(`\n Editor running at ${bundleResult.url}`);
|
|
10
|
+
console.log(` Press Ctrl+C to stop\n`);
|
|
11
|
+
|
|
12
|
+
const shutdown = async () => {
|
|
13
|
+
console.log("\nShutting down editor server...");
|
|
14
|
+
await bundleResult.cleanup();
|
|
15
|
+
process.exit(0);
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
process.on("SIGINT", shutdown);
|
|
19
|
+
process.on("SIGTERM", shutdown);
|
|
20
|
+
|
|
21
|
+
// Block forever
|
|
22
|
+
await new Promise(() => {});
|
|
23
|
+
}
|
|
@@ -8,7 +8,7 @@ import { homedir } from "node:os";
|
|
|
8
8
|
// Skill loader — resolves from the CLI package's own skills/ directory
|
|
9
9
|
// ---------------------------------------------------------------------------
|
|
10
10
|
|
|
11
|
-
const SKILL_NAMES = ["vibeo-core", "vibeo-audio", "vibeo-effects", "vibeo-extras", "vibeo-rendering"];
|
|
11
|
+
const SKILL_NAMES = ["vibeo-core", "vibeo-audio", "vibeo-effects", "vibeo-extras", "vibeo-rendering", "vibeo-editor", "vibeo-tiktok"];
|
|
12
12
|
|
|
13
13
|
async function loadSkills(): Promise<Record<string, string>> {
|
|
14
14
|
// Resolve from this file's location — skills/ is shipped alongside dist/ in the npm package
|
package/src/index.ts
CHANGED
|
@@ -6,6 +6,7 @@ import { startPreview } from "./commands/preview.js";
|
|
|
6
6
|
import { listCompositions } from "./commands/list.js";
|
|
7
7
|
import { renderVideo } from "./commands/render.js";
|
|
8
8
|
import { installSkills } from "./commands/install-skills.js";
|
|
9
|
+
import { startEditor } from "./commands/editor.js";
|
|
9
10
|
|
|
10
11
|
const cli = Cli.create("vibeo", {
|
|
11
12
|
description: "React-based programmatic video framework CLI",
|
|
@@ -108,6 +109,20 @@ cli.command("list", {
|
|
|
108
109
|
},
|
|
109
110
|
});
|
|
110
111
|
|
|
112
|
+
cli.command("editor", {
|
|
113
|
+
description: "Open the visual video editor in the browser",
|
|
114
|
+
options: z.object({
|
|
115
|
+
entry: z.string().describe("Path to the root file with compositions"),
|
|
116
|
+
port: z.number().default(3001).describe("Port for the editor server"),
|
|
117
|
+
}),
|
|
118
|
+
examples: [
|
|
119
|
+
{ options: { entry: "src/index.tsx" }, description: "Open editor on default port" },
|
|
120
|
+
],
|
|
121
|
+
async run(c) {
|
|
122
|
+
await startEditor(resolve(c.options.entry), c.options.port);
|
|
123
|
+
},
|
|
124
|
+
});
|
|
125
|
+
|
|
111
126
|
cli.command("install-skills", {
|
|
112
127
|
description:
|
|
113
128
|
"Install Vibeo skill/rule files for all supported LLM coding tools (Claude, Codex, Cursor, Gemini, OpenCode, Aider)",
|