@vibeo/cli 0.3.4 → 0.3.5
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/install-skills.d.ts.map +1 -1
- package/dist/commands/install-skills.js +13 -76
- package/dist/commands/install-skills.js.map +1 -1
- package/package.json +2 -1
- package/skills/vibeo-audio/SKILL.md +283 -0
- package/skills/vibeo-core/SKILL.md +380 -0
- package/skills/vibeo-effects/SKILL.md +432 -0
- package/skills/vibeo-extras/SKILL.md +457 -0
- package/skills/vibeo-rendering/SKILL.md +364 -0
- package/src/commands/install-skills.ts +16 -75
|
@@ -0,0 +1,380 @@
|
|
|
1
|
+
# Vibeo Core (`@vibeo/core`)
|
|
2
|
+
|
|
3
|
+
## Overview
|
|
4
|
+
|
|
5
|
+
`@vibeo/core` is the foundation of the Vibeo video framework. It provides the timing engine, React context providers, hooks for frame-based animation, the interpolation engine, and the core components (`Composition`, `Sequence`, `Loop`) used to structure video timelines.
|
|
6
|
+
|
|
7
|
+
**When to use**: Any time you are building a Vibeo video project. This package is always required.
|
|
8
|
+
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
## API Reference
|
|
12
|
+
|
|
13
|
+
### Components
|
|
14
|
+
|
|
15
|
+
#### `Composition<T>`
|
|
16
|
+
Registers a video composition with the framework.
|
|
17
|
+
|
|
18
|
+
```tsx
|
|
19
|
+
<Composition
|
|
20
|
+
id="MyVideo"
|
|
21
|
+
component={MyScene}
|
|
22
|
+
width={1920}
|
|
23
|
+
height={1080}
|
|
24
|
+
fps={30}
|
|
25
|
+
durationInFrames={300}
|
|
26
|
+
defaultProps={{ title: "Hello" }}
|
|
27
|
+
calculateMetadata={async (props) => ({ durationInFrames: 600 })}
|
|
28
|
+
/>
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
**Props** (`CompositionProps<T>`):
|
|
32
|
+
| Prop | Type | Description |
|
|
33
|
+
|------|------|-------------|
|
|
34
|
+
| `id` | `string` | Unique identifier for the composition |
|
|
35
|
+
| `component` | `ComponentType<T>` | The React component to render |
|
|
36
|
+
| `width` | `number` | Width in pixels |
|
|
37
|
+
| `height` | `number` | Height in pixels |
|
|
38
|
+
| `fps` | `number` | Frames per second |
|
|
39
|
+
| `durationInFrames` | `number` | Total duration in frames |
|
|
40
|
+
| `defaultProps?` | `T` | Default props passed to the component |
|
|
41
|
+
| `calculateMetadata?` | `(props: T) => Promise<Partial<VideoConfig>>` | Async function to compute metadata dynamically |
|
|
42
|
+
|
|
43
|
+
#### `Sequence`
|
|
44
|
+
Time-shifts its children on the timeline. Supports arbitrary nesting with cumulative offsets.
|
|
45
|
+
|
|
46
|
+
```tsx
|
|
47
|
+
<Sequence from={30} durationInFrames={60} name="Scene2">
|
|
48
|
+
<MyComponent />
|
|
49
|
+
</Sequence>
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
**Props**:
|
|
53
|
+
| Prop | Type | Default | Description |
|
|
54
|
+
|------|------|---------|-------------|
|
|
55
|
+
| `from?` | `number` | `0` | Frame offset where this sequence begins |
|
|
56
|
+
| `durationInFrames?` | `number` | `Infinity` | How many frames this sequence lasts |
|
|
57
|
+
| `name?` | `string` | — | Debug label |
|
|
58
|
+
| `layout?` | `"none" \| "absolute-fill"` | `"absolute-fill"` | CSS layout mode |
|
|
59
|
+
|
|
60
|
+
Inside a Sequence, `useCurrentFrame()` returns the **relative** frame (i.e., frame 0 is when the Sequence starts).
|
|
61
|
+
|
|
62
|
+
#### `Loop`
|
|
63
|
+
Repeats its children for a given duration.
|
|
64
|
+
|
|
65
|
+
```tsx
|
|
66
|
+
<Loop durationInFrames={30} times={4}>
|
|
67
|
+
<PulsingDot />
|
|
68
|
+
</Loop>
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
**Props**:
|
|
72
|
+
| Prop | Type | Default | Description |
|
|
73
|
+
|------|------|---------|-------------|
|
|
74
|
+
| `durationInFrames` | `number` | — | Duration of one loop iteration |
|
|
75
|
+
| `times?` | `number` | `Infinity` | Number of loop iterations |
|
|
76
|
+
| `layout?` | `"none" \| "absolute-fill"` | — | CSS layout mode |
|
|
77
|
+
|
|
78
|
+
#### `VibeoRoot`
|
|
79
|
+
Top-level context provider tree. Wrap your composition registration in this.
|
|
80
|
+
|
|
81
|
+
```tsx
|
|
82
|
+
<VibeoRoot>
|
|
83
|
+
<Composition id="MyVideo" ... />
|
|
84
|
+
</VibeoRoot>
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
### Hooks
|
|
88
|
+
|
|
89
|
+
#### `useCurrentFrame(): number`
|
|
90
|
+
Returns the current frame number **relative to the nearest parent Sequence**. This is the primary hook for animation.
|
|
91
|
+
|
|
92
|
+
#### `useVideoConfig(): VideoConfig`
|
|
93
|
+
Returns `{ width, height, fps, durationInFrames }` for the current composition.
|
|
94
|
+
|
|
95
|
+
#### `useTimelinePosition(): number`
|
|
96
|
+
Returns the **absolute** frame number (ignores Sequence offsets). Rarely needed directly.
|
|
97
|
+
|
|
98
|
+
#### `useSequenceContext(): SequenceContextType | null`
|
|
99
|
+
Returns the current Sequence context, or `null` if not inside a Sequence.
|
|
100
|
+
|
|
101
|
+
#### `useLoopContext(): LoopContextType | null`
|
|
102
|
+
Returns `{ iteration, durationInFrames }` for the current loop, or `null`.
|
|
103
|
+
|
|
104
|
+
### Interpolation
|
|
105
|
+
|
|
106
|
+
#### `interpolate(input, inputRange, outputRange, options?): number`
|
|
107
|
+
The core animation primitive. Maps an input value through input/output ranges with easing.
|
|
108
|
+
|
|
109
|
+
```ts
|
|
110
|
+
const opacity = interpolate(frame, [0, 30], [0, 1]);
|
|
111
|
+
const x = interpolate(frame, [0, 15, 30], [0, 200, 0], { easing: easeInOut });
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
**Parameters**:
|
|
115
|
+
| Param | Type | Description |
|
|
116
|
+
|-------|------|-------------|
|
|
117
|
+
| `input` | `number` | The current value (usually the frame) |
|
|
118
|
+
| `inputRange` | `readonly number[]` | Breakpoints for the input |
|
|
119
|
+
| `outputRange` | `readonly number[]` | Corresponding output values |
|
|
120
|
+
| `options?` | `InterpolateOptions` | Easing and extrapolation config |
|
|
121
|
+
|
|
122
|
+
**`InterpolateOptions`**:
|
|
123
|
+
| Field | Type | Default | Description |
|
|
124
|
+
|-------|------|---------|-------------|
|
|
125
|
+
| `easing?` | `(t: number) => number` | `linear` | Easing function |
|
|
126
|
+
| `extrapolateLeft?` | `ExtrapolateType` | `"extend"` | Behavior below input range |
|
|
127
|
+
| `extrapolateRight?` | `ExtrapolateType` | `"extend"` | Behavior above input range |
|
|
128
|
+
|
|
129
|
+
**`ExtrapolateType`**: `"clamp" | "extend" | "identity"`
|
|
130
|
+
|
|
131
|
+
### Easing Functions
|
|
132
|
+
|
|
133
|
+
```ts
|
|
134
|
+
import { linear, easeIn, easeOut, easeInOut, bezier, steps } from "@vibeo/core";
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
| Function | Description |
|
|
138
|
+
|----------|-------------|
|
|
139
|
+
| `linear(t)` | No easing, linear progression |
|
|
140
|
+
| `easeIn(t)` | Cubic ease-in (t^3) |
|
|
141
|
+
| `easeOut(t)` | Cubic ease-out |
|
|
142
|
+
| `easeInOut(t)` | Cubic ease-in-out |
|
|
143
|
+
| `bezier(x1, y1, x2, y2)` | Returns a custom cubic bezier easing function |
|
|
144
|
+
| `steps(n)` | Returns a step function with `n` discrete steps |
|
|
145
|
+
|
|
146
|
+
### Timing Utilities
|
|
147
|
+
|
|
148
|
+
```ts
|
|
149
|
+
import { msPerFrame, frameToTime, timeToFrame, getMediaTime } from "@vibeo/core";
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
| Function | Signature | Description |
|
|
153
|
+
|----------|-----------|-------------|
|
|
154
|
+
| `msPerFrame` | `(fps) => number` | Milliseconds per frame: `1000 / fps` |
|
|
155
|
+
| `frameToTime` | `(frame, fps) => number` | Frame to seconds: `frame * msPerFrame(fps) / 1000` |
|
|
156
|
+
| `timeToFrame` | `(time, fps) => number` | Seconds to frame: `floor(time * fps)` |
|
|
157
|
+
| `getMediaTime` | `(frame, fps, playbackRate, startFrom) => number` | Compute media time accounting for playback rate |
|
|
158
|
+
|
|
159
|
+
### Other Utilities
|
|
160
|
+
|
|
161
|
+
#### `calculateMediaDuration(options): number`
|
|
162
|
+
Computes the actual duration of media in frames, accounting for trim and playback rate.
|
|
163
|
+
|
|
164
|
+
#### `validateVideoConfig(config): void`
|
|
165
|
+
Validates a `VideoConfig` object, throwing on invalid values.
|
|
166
|
+
|
|
167
|
+
### Types
|
|
168
|
+
|
|
169
|
+
```ts
|
|
170
|
+
import type {
|
|
171
|
+
VideoConfig,
|
|
172
|
+
CompositionProps,
|
|
173
|
+
SequenceContextType,
|
|
174
|
+
LoopContextType,
|
|
175
|
+
ExtrapolateType,
|
|
176
|
+
InterpolateOptions,
|
|
177
|
+
} from "@vibeo/core";
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
### Context Providers (advanced)
|
|
181
|
+
|
|
182
|
+
- `TimelineProvider` / `TimelineContext` / `useTimelineContext` — manages the global frame state
|
|
183
|
+
- `CompositionProvider` / `CompositionContext` / `useCompositionContext` — manages composition registration
|
|
184
|
+
- `SequenceContext` / `useSequenceContext` — tracks cumulative Sequence offsets
|
|
185
|
+
- `LoopContext` / `useLoopContext` — tracks loop iteration state
|
|
186
|
+
|
|
187
|
+
---
|
|
188
|
+
|
|
189
|
+
## Key Timing Math
|
|
190
|
+
|
|
191
|
+
### Frame <-> Time
|
|
192
|
+
```
|
|
193
|
+
msPerFrame = 1000 / fps
|
|
194
|
+
timeInSeconds = frame * msPerFrame / 1000
|
|
195
|
+
frameFromTime = floor(timeInSeconds * fps)
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
### Media Time with Playback Rate
|
|
199
|
+
```
|
|
200
|
+
mediaTime(frame, fps, playbackRate, startFrom) =
|
|
201
|
+
interpolate(frame, [-1, startFrom, startFrom+1], [-1, startFrom, startFrom+playbackRate]) * (1000/fps) / 1000
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
### Sequence Relative Frame
|
|
205
|
+
```
|
|
206
|
+
relativeFrame = absoluteFrame - (cumulatedFrom + relativeFrom)
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
### Loop Iteration
|
|
210
|
+
```
|
|
211
|
+
iteration = floor(currentFrame / durationInFrames)
|
|
212
|
+
loopFrame = currentFrame % durationInFrames
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
### Interpolation Engine
|
|
216
|
+
```
|
|
217
|
+
1. Find segment: which adjacent pair in inputRange brackets input
|
|
218
|
+
2. Normalize: t = (input - inputRange[i]) / (inputRange[i+1] - inputRange[i])
|
|
219
|
+
3. Ease: t = easing(t)
|
|
220
|
+
4. Scale: output = t * (outputRange[i+1] - outputRange[i]) + outputRange[i]
|
|
221
|
+
5. Extrapolate: clamp | extend | identity beyond range ends
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
### Media Duration with Trim & Playback Rate
|
|
225
|
+
```
|
|
226
|
+
duration = trimAfter ?? totalDurationInFrames
|
|
227
|
+
duration -= trimBefore ?? 0
|
|
228
|
+
actualDuration = floor(duration / playbackRate)
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
---
|
|
232
|
+
|
|
233
|
+
## Common Patterns
|
|
234
|
+
|
|
235
|
+
### Creating a basic composition
|
|
236
|
+
|
|
237
|
+
```tsx
|
|
238
|
+
import { Composition, Sequence, useCurrentFrame, interpolate, easeInOut } from "@vibeo/core";
|
|
239
|
+
|
|
240
|
+
function MyScene() {
|
|
241
|
+
const frame = useCurrentFrame();
|
|
242
|
+
const opacity = interpolate(frame, [0, 30], [0, 1], { easing: easeInOut });
|
|
243
|
+
return <div style={{ opacity }}>Hello Vibeo</div>;
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
// Register in a VibeoRoot
|
|
247
|
+
<Composition
|
|
248
|
+
id="MyScene"
|
|
249
|
+
component={MyScene}
|
|
250
|
+
width={1920}
|
|
251
|
+
height={1080}
|
|
252
|
+
fps={30}
|
|
253
|
+
durationInFrames={150}
|
|
254
|
+
/>
|
|
255
|
+
```
|
|
256
|
+
|
|
257
|
+
### Using Sequence for scene structure
|
|
258
|
+
|
|
259
|
+
```tsx
|
|
260
|
+
function MyVideo() {
|
|
261
|
+
return (
|
|
262
|
+
<>
|
|
263
|
+
<Sequence from={0} durationInFrames={60}>
|
|
264
|
+
<IntroScene />
|
|
265
|
+
</Sequence>
|
|
266
|
+
<Sequence from={60} durationInFrames={90}>
|
|
267
|
+
<MainScene />
|
|
268
|
+
</Sequence>
|
|
269
|
+
<Sequence from={150} durationInFrames={60}>
|
|
270
|
+
<OutroScene />
|
|
271
|
+
</Sequence>
|
|
272
|
+
</>
|
|
273
|
+
);
|
|
274
|
+
}
|
|
275
|
+
```
|
|
276
|
+
|
|
277
|
+
### Nesting Loops inside Sequences
|
|
278
|
+
|
|
279
|
+
```tsx
|
|
280
|
+
<Sequence from={0} durationInFrames={120}>
|
|
281
|
+
<Loop durationInFrames={30} times={4}>
|
|
282
|
+
<BouncingBall />
|
|
283
|
+
</Loop>
|
|
284
|
+
</Sequence>
|
|
285
|
+
```
|
|
286
|
+
|
|
287
|
+
### Multi-segment interpolation
|
|
288
|
+
|
|
289
|
+
```tsx
|
|
290
|
+
const frame = useCurrentFrame();
|
|
291
|
+
// Move right, then back, then down
|
|
292
|
+
const x = interpolate(frame, [0, 30, 60, 90], [0, 200, 0, 0]);
|
|
293
|
+
const y = interpolate(frame, [0, 30, 60, 90], [0, 0, 0, 200]);
|
|
294
|
+
```
|
|
295
|
+
|
|
296
|
+
---
|
|
297
|
+
|
|
298
|
+
## Gotchas and Tips
|
|
299
|
+
|
|
300
|
+
1. **`useCurrentFrame()` is always relative to the nearest `<Sequence>`**. If you need the absolute frame, use `useTimelinePosition()`.
|
|
301
|
+
|
|
302
|
+
2. **`interpolate()` requires at least 2 input range values** and input/output ranges must have the same length.
|
|
303
|
+
|
|
304
|
+
3. **Extrapolation defaults to `"extend"`** (continues the slope beyond the range). Use `{ extrapolateLeft: "clamp", extrapolateRight: "clamp" }` to clamp values at range boundaries.
|
|
305
|
+
|
|
306
|
+
4. **`Sequence` children are only rendered when the absolute frame is within `[from, from + durationInFrames)`**. Outside that range, `Sequence` returns `null`.
|
|
307
|
+
|
|
308
|
+
5. **Loop iterations**: Inside a `<Loop>`, `useCurrentFrame()` resets to 0 each iteration. Use `useLoopContext()` to get the current `iteration` number.
|
|
309
|
+
|
|
310
|
+
6. **`calculateMetadata` is async** — use it for compositions whose duration depends on fetched data (e.g., audio file length).
|
|
311
|
+
|
|
312
|
+
7. **All timing is frame-based, not time-based**. Convert with `frameToTime(frame, fps)` when needed.
|
|
313
|
+
|
|
314
|
+
|
|
315
|
+
---
|
|
316
|
+
|
|
317
|
+
## LLM & Agent Integration
|
|
318
|
+
|
|
319
|
+
Vibeo's CLI is built with [incur](https://github.com/wevm/incur), making it natively discoverable by AI agents and LLMs.
|
|
320
|
+
|
|
321
|
+
### Discovering the API
|
|
322
|
+
|
|
323
|
+
```bash
|
|
324
|
+
# Get a compact summary of all CLI commands (ideal for LLM system prompts)
|
|
325
|
+
bunx @vibeo/cli --llms
|
|
326
|
+
|
|
327
|
+
# Get the full manifest with schemas, examples, and argument details
|
|
328
|
+
bunx @vibeo/cli --llms-full
|
|
329
|
+
|
|
330
|
+
# Get JSON Schema for a specific command (useful for structured tool calls)
|
|
331
|
+
bunx @vibeo/cli render --schema
|
|
332
|
+
bunx @vibeo/cli create --schema
|
|
333
|
+
```
|
|
334
|
+
|
|
335
|
+
### Using as an MCP Server
|
|
336
|
+
|
|
337
|
+
```bash
|
|
338
|
+
# Start Vibeo as an MCP (Model Context Protocol) server
|
|
339
|
+
bunx @vibeo/cli --mcp
|
|
340
|
+
|
|
341
|
+
# Register as a persistent MCP server for your agent
|
|
342
|
+
bunx @vibeo/cli mcp add
|
|
343
|
+
```
|
|
344
|
+
|
|
345
|
+
This lets LLMs call `create`, `render`, `preview`, and `list` as structured tool calls through the MCP protocol.
|
|
346
|
+
|
|
347
|
+
### Generating Skill Files
|
|
348
|
+
|
|
349
|
+
```bash
|
|
350
|
+
# Sync skill files to your agent's skill directory
|
|
351
|
+
bunx @vibeo/cli skills add
|
|
352
|
+
```
|
|
353
|
+
|
|
354
|
+
This generates markdown skill files that agents like Claude Code can discover and use to write Vibeo code without reading source.
|
|
355
|
+
|
|
356
|
+
### Agent-Friendly Output
|
|
357
|
+
|
|
358
|
+
```bash
|
|
359
|
+
# Output as JSON for programmatic consumption
|
|
360
|
+
bunx @vibeo/cli list --entry src/index.tsx --format json
|
|
361
|
+
|
|
362
|
+
# Output as YAML
|
|
363
|
+
bunx @vibeo/cli list --entry src/index.tsx --format yaml
|
|
364
|
+
|
|
365
|
+
# Filter output to specific keys
|
|
366
|
+
bunx @vibeo/cli list --entry src/index.tsx --filter-output compositions[0].id
|
|
367
|
+
|
|
368
|
+
# Count tokens in output (useful for context window planning)
|
|
369
|
+
bunx @vibeo/cli render --schema --token-count
|
|
370
|
+
```
|
|
371
|
+
|
|
372
|
+
### How LLMs Should Use Vibeo
|
|
373
|
+
|
|
374
|
+
1. **Discover commands**: Run `bunx @vibeo/cli --llms` to get the command manifest
|
|
375
|
+
2. **Create a project**: `bunx @vibeo/cli create my-video --template basic`
|
|
376
|
+
3. **Edit `src/index.tsx`**: Write React components using `@vibeo/core` hooks and components
|
|
377
|
+
4. **Preview**: `bunx @vibeo/cli preview --entry src/index.tsx`
|
|
378
|
+
5. **Render**: `bunx @vibeo/cli render --entry src/index.tsx --composition MyComp`
|
|
379
|
+
|
|
380
|
+
All commands accept `--format json` for structured output that LLMs can parse reliably.
|