@scenerok/cli 2.2.0 → 2.3.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/README.md +6 -6
- package/dist/commands/skills.d.ts.map +1 -1
- package/dist/commands/skills.js +31 -25
- package/dist/utils/skillsManifest.d.ts +13 -3
- package/dist/utils/skillsManifest.d.ts.map +1 -1
- package/dist/utils/skillsManifest.js +35 -12
- package/examples/system/rokmilk-chocolate-promo.vid +1 -1
- package/examples/system/voice-visual-promo.vid +1 -1
- package/package.json +3 -3
- package/skills/scenerok/SKILL.md +147 -0
- package/skills/{remote → scenerok/references}/ai-models.md +1 -2
- package/skills/{remote → scenerok/references}/animations-catalog.md +139 -2
- package/skills/{remote → scenerok/references}/animations-picker.md +12 -1
- package/skills/{remote → scenerok/references}/brand-setup.md +3 -1
- package/skills/{remote → scenerok/references}/catalog-index.json +163 -19
- package/skills/scenerok/references/editing-actions.md +518 -0
- package/skills/{remote → scenerok/references}/environments.md +23 -24
- package/skills/{remote → scenerok/references}/intent-router.md +5 -15
- package/skills/{remote → scenerok/references}/media-stdlib.md +2 -3
- package/skills/{remote → scenerok/references}/music.md +2 -21
- package/skills/{remote → scenerok/references}/sdk-guide.md +2 -2
- package/skills/{remote → scenerok/references}/shaders-filters.md +9 -6
- package/skills/{remote → scenerok/references}/text-styles.md +15 -35
- package/skills/{remote → scenerok/references}/transitions-catalog.md +2 -0
- package/skills/{remote → scenerok/references}/tts.md +4 -22
- package/skills/{remote → scenerok/references}/vidscript-guide.md +202 -55
- package/skills/scenerok/references/vidscript-sample.md +44 -0
- package/skills/{remote → scenerok/references}/vidscript-strict.md +41 -26
- package/skills/{remote → scenerok/references}/workflow.md +6 -7
- package/skills/remote/vidscript-sample.md +0 -28
- package/skills/shared/SKILL.md +0 -157
|
@@ -2,12 +2,16 @@
|
|
|
2
2
|
|
|
3
3
|
VidScript is a declarative DSL for composing short-form videos. Write a script, render an MP4.
|
|
4
4
|
|
|
5
|
+
This is the grammar reference. For task-by-task editing (layering, masks, cuts, speed, sound mixing, grading, delivery), use `editing-actions.md`; for the rules agents most often break, `vidscript-strict.md`.
|
|
6
|
+
|
|
7
|
+
Contents: Agent quick start · Program structure · Statements (input, output, let, import) · Time blocks · Including TypeScript scenes · Instructions (clips, text, grid areas, video methods, compositing, filters, motion blur, shaders, audio, plugin calls) · Reuse: functions and modules · Animations and effects · Expressions · Template placeholders · CLI commands · Best practices
|
|
8
|
+
|
|
5
9
|
## Agent quick start (read before composing)
|
|
6
10
|
|
|
7
11
|
1. **Import every package function** — use `import xai from "@scenerok/xai"`, `import cf from "@scenerok/cloudflare"`, `import eleven from "@elevenlabs/music"`, and `import motion from "@scenerok/basic-animations"` before calling `xai.*`, `cf.*`, `eleven.*`, or `motion.*`.
|
|
8
12
|
2. **Validate early** — `scenerok validate file.vid` (errors show as `Line N:C — message`).
|
|
9
13
|
3. **Music package syntax** — `import eleven from "@elevenlabs/music"`, then call `eleven.music(...)`, `eleven.generateMusic(...)`, or `eleven.composeMusic(...)`.
|
|
10
|
-
4. **Copy working examples** —
|
|
14
|
+
4. **Copy working examples** — `vidscript-sample.md`, and the `.vid` files in the `@scenerok/cli` package's `examples/system/` (e.g. `minimal-text-reel.vid`, `ecom-product-launch.vid`, `rokmilk-chocolate-promo.vid`).
|
|
11
15
|
5. **Use URL assets selectively** — if the user gives a website or product URL and browser screenshot tools are available, you can capture above-fold screenshots, product images, app screenshots, and logos for VidScript `input` assets. Do not treat screenshots as mandatory or as the only visual source.
|
|
12
16
|
6. **Probe asset type and dimensions** — before placing real assets, identify image vs video with `file`; inspect width, height, and duration with `ffprobe`, `sips`, or `identify`; then scale from the measured aspect ratio and set explicit `width`, `height`, `x`, and `y`.
|
|
13
17
|
7. **Use still images directly** — `input` supports videos and images. Place still screenshots, logos, and product photos with `video image_input`; the renderer treats images as static clips for the block duration.
|
|
@@ -59,6 +63,15 @@ input logo = "./assets/logo.png"
|
|
|
59
63
|
|
|
60
64
|
Supports HTTP(S) URLs, `/uploads/` paths, and local paths for video/image assets.
|
|
61
65
|
|
|
66
|
+
A numbered image sequence is one input: the pattern names the frames, and `count:` says how many (a local compile can find it by probing). Optional `rate:` (frames per second, default the output fps) and `first:` (number of the first file, default 0):
|
|
67
|
+
|
|
68
|
+
```vidscript
|
|
69
|
+
input frames = "shots/shot.%04d.png", rate: 12, first: 1, count: 24
|
|
70
|
+
[0s .. 2s] = frames
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
Place it by name like a video; `Trim`, `Speed`, and `Loop` work on it. `rate:`, `first:`, `count:` on a plain file are errors. On the render workers the pattern must be an `https` URL pattern (or a path on the worker).
|
|
74
|
+
|
|
62
75
|
For ads based on a supplied URL, browser tools can capture real website visuals and save product images or logos when useful. Use screenshots sparingly: favor above-fold, cropped, or focused captures that remain legible at video size. Avoid full-page or long scrolling screenshots as backgrounds because their text will usually become unreadable.
|
|
63
76
|
|
|
64
77
|
Website captures are optional grounding assets, not a ban on generated video. A strong ad may combine product/logo/app screenshots with generated lifestyle clips, motion backgrounds, transitions, and VidScript text primitives. Use generative clips when they improve storytelling, show context, or make the ad feel more polished.
|
|
@@ -75,6 +88,8 @@ identify asset.webp
|
|
|
75
88
|
Use real videos as motion footage and still images as static clips. The `video` primitive accepts both; the renderer detects image vs video:
|
|
76
89
|
|
|
77
90
|
```vidscript
|
|
91
|
+
import motion from "@scenerok/basic-animations"
|
|
92
|
+
|
|
78
93
|
input screenshot = "assets/screenshots/homepage.png"
|
|
79
94
|
[0s .. 4.5s] = video screenshot, width: 1080, height: 1920, x: 0, y: 0, animate: motion.fadeIn(0.35s)
|
|
80
95
|
```
|
|
@@ -103,10 +118,13 @@ output to "reel.mp4", resolution: "720x1280", format: "mp4", codec: "h264", bitr
|
|
|
103
118
|
|--------|------|---------|-------------|
|
|
104
119
|
| `resolution` | string | `"1080x1920"` | Width×Height or single number |
|
|
105
120
|
| `fps` | number | `30` | Frames per second |
|
|
106
|
-
| `format` | string | `"mp4"` | Container format |
|
|
107
|
-
| `codec` | string | `"h264"` | Video codec |
|
|
121
|
+
| `format` | string | `"mp4"` | Container format. Every render is MP4 today |
|
|
122
|
+
| `codec` | string | `"h264"` | Video codec. Every render is H.264 today |
|
|
108
123
|
| `bitrate` | string | `"5M"` | Encoding bitrate |
|
|
109
124
|
| `background` | string | `"#000000"` | Background color hex |
|
|
125
|
+
| `start_timecode` | string | none | Timecode of the first frame, `"HH:MM:SS:FF"` (non-drop-frame; the frame field must be below `fps`). Written to the mp4's timecode track |
|
|
126
|
+
| `burn_in` | string | none | `"timecode"` draws the running timecode (from `start_timecode`, else `00:00:00:00`) onto the picture |
|
|
127
|
+
| `alpha` | boolean | `false` | Needs `format: "mov", codec: "prores"` or `format: "webm", codec: "vp9"`. No render path delivers alpha yet (renders are H.264 mp4), so `alpha: true` is a compile error for now |
|
|
110
128
|
|
|
111
129
|
### Variable Assignment
|
|
112
130
|
|
|
@@ -139,9 +157,10 @@ Time blocks are the core of VidScript. They define when instructions execute on
|
|
|
139
157
|
### Dynamic Playhead (recommended)
|
|
140
158
|
|
|
141
159
|
```vidscript
|
|
160
|
+
input hero = "hero.mp4"
|
|
142
161
|
[-] = hero # auto-append: starts at visual cursor
|
|
143
|
-
[- 3s] = text "Title", size: 72 # auto-start, last 3 seconds
|
|
144
|
-
[- 2.5s] = filter "
|
|
162
|
+
[- 3s] = text "Title", size: 72 # auto-start, last 3 seconds
|
|
163
|
+
[- 2.5s] = filter "vignette", intensity: 0.4
|
|
145
164
|
```
|
|
146
165
|
|
|
147
166
|
VidScript keeps separate auto-playhead cursors for visual and audio content. Visual blocks (`video`, `text`, filters, shaders, visual plugin calls, and bare visual inputs) advance the visual cursor. Audio blocks (`audio`, `xai.tts`, `eleven.music`, and other audio plugin calls) advance the audio cursor. `[- duration]` advances the relevant cursor by the explicit duration.
|
|
@@ -163,19 +182,20 @@ The generated video above starts at the current visual cursor, usually `0s`, eve
|
|
|
163
182
|
### Explicit Range
|
|
164
183
|
|
|
165
184
|
```vidscript
|
|
185
|
+
input hero = "hero.mp4"
|
|
166
186
|
[0s .. 5s] = hero # absolute range
|
|
167
187
|
[0.5s .. 3s] = text "Hello" # explicit times
|
|
168
|
-
[prev + 1s .. prev + 4s] = filter "
|
|
188
|
+
[prev + 1s .. prev + 4s] = filter "vignette" # expression-based
|
|
169
189
|
```
|
|
170
190
|
|
|
171
191
|
Use `..` (not `-`) as the range separator to avoid ambiguity with subtraction.
|
|
172
192
|
|
|
173
193
|
### Time Formats
|
|
174
194
|
|
|
175
|
-
```
|
|
195
|
+
```text
|
|
176
196
|
5s # 5 seconds
|
|
177
197
|
300ms # 300 milliseconds
|
|
178
|
-
frame
|
|
198
|
+
90 frame # 90 frames at the output fps (default 30): 3 seconds
|
|
179
199
|
0:30 # 30 seconds
|
|
180
200
|
1:30:00 # 1 hour 30 minutes
|
|
181
201
|
```
|
|
@@ -186,7 +206,7 @@ frame 90 # 90 frames at 30fps = 3 seconds
|
|
|
186
206
|
|
|
187
207
|
```vidscript
|
|
188
208
|
[- 2s] = text "Hello" # plays from cursor to cursor+2s
|
|
189
|
-
[prev + 1s .. prev + 3s] =
|
|
209
|
+
[prev + 1s .. prev + 3s] = text "World" # 1s after "Hello" ends, for 2s
|
|
190
210
|
```
|
|
191
211
|
|
|
192
212
|
### Symbolic Durations (`asset.duration`)
|
|
@@ -212,7 +232,7 @@ let vo2 = xai.tts("Let's dive in", voice: "eve")
|
|
|
212
232
|
|
|
213
233
|
Import a `@scenerok/sdk` scene (`.ts` / `.js`) into VidScript with the same default-import form as plugins, then `use` it like a timeline:
|
|
214
234
|
|
|
215
|
-
```vidscript
|
|
235
|
+
```vidscript fragment
|
|
216
236
|
import intro from "./intro.ts"
|
|
217
237
|
import bump from "./bump.js"
|
|
218
238
|
|
|
@@ -231,17 +251,18 @@ Rules:
|
|
|
231
251
|
|
|
232
252
|
## Instructions (inside time blocks)
|
|
233
253
|
|
|
234
|
-
|
|
254
|
+
Each line places one thing. Several lines can share a window:
|
|
235
255
|
|
|
236
256
|
```vidscript
|
|
237
|
-
|
|
238
|
-
hero.Trim(
|
|
239
|
-
|
|
257
|
+
input hero = "hero.mp4"
|
|
258
|
+
[0s .. 5s] = hero.Trim(0s, 7.5s).Speed(1.5)
|
|
259
|
+
[0s .. 5s] = text "Hello", area: "title"
|
|
240
260
|
```
|
|
241
261
|
|
|
242
262
|
### Clip Reference
|
|
243
263
|
|
|
244
264
|
```vidscript
|
|
265
|
+
input hero = "hero.mp4"
|
|
245
266
|
[-] = hero # just play the input clip
|
|
246
267
|
```
|
|
247
268
|
|
|
@@ -268,7 +289,7 @@ hero.Speed(factor: 1.5)
|
|
|
268
289
|
| `align` | string | `left`, `center`, `right`. Default: `center` |
|
|
269
290
|
| `line_height` | number | Line height multiplier for multi-line text. Default: 1.2 |
|
|
270
291
|
| `letter_spacing` | number | Extra letter spacing in pixels. Default: 0 |
|
|
271
|
-
| `rotation` | number | Rotation in degrees. Positive
|
|
292
|
+
| `rotation` | number | Rotation in degrees. Positive turns text clockwise (video turns the other way). Default: 0 |
|
|
272
293
|
| `opacity` | number | Opacity 0–1. Default: 1 |
|
|
273
294
|
| `stroke` | string | Outline color |
|
|
274
295
|
| `stroke_width` | number | Outline thickness |
|
|
@@ -276,7 +297,8 @@ hero.Speed(factor: 1.5)
|
|
|
276
297
|
| `shadow_blur` | number | Shadow blur radius |
|
|
277
298
|
| `shadow_offset_x` | number | Shadow horizontal offset |
|
|
278
299
|
| `shadow_offset_y` | number | Shadow vertical offset |
|
|
279
|
-
| `
|
|
300
|
+
| `anchor` | string | Point the text rotates and positions around, e.g. `"top-left"` |
|
|
301
|
+
| `stagger` | string | `"word"` or `"char"`: reveal the text piece by piece over its `animate:` |
|
|
280
302
|
|
|
281
303
|
Params are optional and comma-separated. Params can be on the same line as the content.
|
|
282
304
|
|
|
@@ -311,21 +333,58 @@ t[0][s(3)] = text('Left column', { area: 'left' })
|
|
|
311
333
|
|
|
312
334
|
### Video Methods
|
|
313
335
|
|
|
314
|
-
|
|
315
|
-
hero.
|
|
316
|
-
|
|
317
|
-
hero.Speed(factor: 1.5) # playback rate
|
|
318
|
-
hero.Loop(count: 3) # loop count
|
|
319
|
-
hero.Opacity(value: 0.5, duration: 2s) # opacity with duration
|
|
320
|
-
```
|
|
336
|
+
Methods are operations on values. Capitalised names are methods; lowercase names are
|
|
337
|
+
properties (`hero.duration`). A method returns **a new value** — chain them, or bind
|
|
338
|
+
the result with `let`:
|
|
321
339
|
|
|
322
|
-
|
|
340
|
+
```vidscript
|
|
341
|
+
input beach = "beach.mp4"
|
|
342
|
+
let hero = beach.Trim(2s, 8s).Speed(1.5)
|
|
343
|
+
[0s .. 4s] = hero, opacity: 0.9
|
|
344
|
+
[4s .. 8s] = beach.Trim(0s, 2s).Loop(2).Resize(720, 1280)
|
|
345
|
+
[8s .. 10s] = beach.Opacity(0.5).Filter("sepia", intensity: 0.8)
|
|
346
|
+
[10s .. 12s] = beach.Crop(0, 0, 640, 360)
|
|
347
|
+
let framed = beach.Mask(solid("#fff", radius: 120, width: 800, height: 500), mode: "luma")
|
|
348
|
+
[12s .. 14s] = framed
|
|
349
|
+
```
|
|
350
|
+
|
|
351
|
+
| Method | Arguments | Result |
|
|
352
|
+
|--------|-----------|--------|
|
|
353
|
+
| `Trim(start, end)` | source times | Plays that source range |
|
|
354
|
+
| `Speed(factor)` | number | Playback rate; negative reverses, `0` holds a frame |
|
|
355
|
+
| `Loop(count)` | number | Repeats the range `count` times |
|
|
356
|
+
| `Resize(width, height)` | pixels | Display size |
|
|
357
|
+
| `Opacity(value)` | 0–1 | Opacity |
|
|
358
|
+
| `Filter(name, …params)` | filter name | A grade on this value only |
|
|
359
|
+
| `Crop(x, y, width, height)` | source pixels from the top-left | The rectangle, filling the display size |
|
|
360
|
+
| `Mask(matte, mode?)` | any picture; `"alpha"`, `"luma"`, `"inverted"` | Drawn through the matte |
|
|
361
|
+
| `Overlay(other, x?, y?, opacity?)` | a picture value | `other` on top at a position |
|
|
362
|
+
| `Composite(other, mode?, x?, y?, opacity?)` | a picture value, a blend mode | `other` blended on top |
|
|
363
|
+
|
|
364
|
+
Arguments can be positional or named (`hero.Trim(start: 0s, end: 5s)`).
|
|
365
|
+
|
|
366
|
+
A second `Trim` is relative to the first: `a.Trim(10s, 20s).Trim(2s, 4s)` plays 12–14 s. A second `Crop` is relative to the first the same way.
|
|
367
|
+
`Crop` keeps the display size, so the rectangle is scaled to fill it; `Resize` to the rectangle's aspect avoids stretching.
|
|
368
|
+
The `Mask` matte is any picture value — a `solid` with `radius:` or `corners:` for a shape, `text(…)`, a function, or another clip (a track matte) — drawn in the same frame at the same local time.
|
|
369
|
+
Place a masked value (`[0s .. 4s] = a.Mask(…)`); a `Mask` line after the clip is placed, an unknown mode, a masked clip as a
|
|
370
|
+
`Composite` side, and a cropped clip inside a transition are compile errors.
|
|
371
|
+
`audio x` places a sound — an audio file, or a video's own sound (`audio a.Trim(1s, 3s)`);
|
|
372
|
+
`video` on an audio file is an error.
|
|
373
|
+
|
|
374
|
+
A placed value lasts its own length (range ÷ |speed| × loops), so make its window that long.
|
|
375
|
+
Each placement is its own value: `[0s .. 4s] = a.Trim(0s, 4s)` and `[4s .. 6s] = a.Trim(10s, 12s)` are two independent cuts.
|
|
376
|
+
|
|
377
|
+
A method on its own line right after `[…] = hero` (the legacy spelling) still edits the clip that time block placed. Outside a time block it rebinds the name:
|
|
378
|
+
`bg.Loop(count: 3)` makes `bg` the looped clip for later placements.
|
|
323
379
|
|
|
324
380
|
### Compositing
|
|
325
381
|
|
|
326
382
|
```vidscript
|
|
327
|
-
hero
|
|
328
|
-
|
|
383
|
+
input hero = "hero.mp4"
|
|
384
|
+
input logo = "logo.png"
|
|
385
|
+
input texture = "light-leak.mp4"
|
|
386
|
+
[0s .. 4s] = hero.Overlay(logo, x: 50, y: 100, opacity: 0.8)
|
|
387
|
+
[4s .. 8s] = hero.Composite(texture, x: 0, y: 0, opacity: 0.5, mode: "screen")
|
|
329
388
|
```
|
|
330
389
|
|
|
331
390
|
| Param | Type | Description |
|
|
@@ -333,16 +392,36 @@ hero.Composite(overlay, x: 0, y: 0, opacity: 0.5, mode: "screen")
|
|
|
333
392
|
| `x` | number | Horizontal offset |
|
|
334
393
|
| `y` | number | Vertical offset |
|
|
335
394
|
| `opacity` | number | 0–1 transparency |
|
|
336
|
-
| `mode` | string | Blend mode for Composite |
|
|
395
|
+
| `mode` | string | Blend mode for Composite: `normal`, `multiply`, `screen`, `overlay`, `difference`, `add`, `subtract`, `darken`, `lighten` |
|
|
396
|
+
|
|
397
|
+
The other side can be a clip name, or a value: a function's picture, a `solid(…)`, a
|
|
398
|
+
`text(…)`, or a trimmed clip. A clip joins as a composite layer; anything else draws as a
|
|
399
|
+
group blended with `mode` (`x`, `y`, and `opacity` place that group). A composite is itself
|
|
400
|
+
a target, so layers stack: `let ab = a.Composite(b, mode: "screen")` then
|
|
401
|
+
`[0s .. 4s] = ab.Composite(card("x"), mode: "multiply")`. Both sides must overlap in time.
|
|
337
402
|
|
|
338
403
|
### Filters
|
|
339
404
|
|
|
340
405
|
```vidscript
|
|
341
|
-
|
|
406
|
+
input hero = "hero.mp4"
|
|
407
|
+
[0s .. 5s] = hero
|
|
408
|
+
[0s .. 2s] = filter "chromatic", offset: 0.01
|
|
342
409
|
[0s .. 5s] = filter "vignette", intensity: 0.3
|
|
410
|
+
[0s .. 5s] = filter "contrast", amount: 1.2, target: hero
|
|
343
411
|
```
|
|
344
412
|
|
|
345
|
-
Built-in filters: `monochrome`, `sepia`, `blur`, `chromatic`, `glitch`, `vignette`, `contrast`, `saturation`, `brightness`.
|
|
413
|
+
Built-in filters: `monochrome`, `sepia`, `blur`, `chromatic`, `glitch`, `vignette`, `contrast`, `saturation`, `brightness`, plus `lut` and `cdl` (below and in `editing-actions.md`) and the VFX in `shaders-filters.md`. A `filter` line grades everything in its window; `target: clip` grades one clip.
|
|
414
|
+
|
|
415
|
+
**LUT.** `filter "lut", file: "look.cube"` applies a 3D `.cube` look to the frame, to one clip with `target:`, or to a function body's picture. The compiler reads the file (relative to the `.vid`, 512 KB at most, up to 33 points per axis) and embeds the table, so render workers need nothing else. The lookup runs on sRGB-encoded values with 8-bit trilinear interpolation. `Filter("lut")` as a method is an error; use the statement.
|
|
416
|
+
|
|
417
|
+
### Motion blur
|
|
418
|
+
|
|
419
|
+
`motion_blur: true` or `motion_blur: { shutter: 180, samples: 8 }` on a video/image placement or a placed function averages `samples` sub-frames across the shutter angle (above 0, up to 360; samples 2 to 64) of that item's own `animate:` motion. Each sample is another render of the frame, so a frame with a blurred item costs `samples` times as much. It does not re-sample the video's own frames, does not blur a group's moving children (set it on them), and is an error on text, solids, captions, audio, and a clip at a transition.
|
|
420
|
+
|
|
421
|
+
```vidscript
|
|
422
|
+
input hero = "hero.mp4"
|
|
423
|
+
[0s .. 2s] = hero, animate: { type: "slideX", from: { x: 0 }, to: { x: 900 } }, motion_blur: { shutter: 360, samples: 16 }
|
|
424
|
+
```
|
|
346
425
|
|
|
347
426
|
### Shaders
|
|
348
427
|
|
|
@@ -359,7 +438,8 @@ Fragment must sample `tDiffuse`, write `gl_FragColor`, and declare uniforms matc
|
|
|
359
438
|
### Audio
|
|
360
439
|
|
|
361
440
|
```vidscript
|
|
362
|
-
|
|
441
|
+
input music = "music.mp3"
|
|
442
|
+
[0s .. 15s] = audio music, volume: 0.5, fade_in: 1s, fade_out: 2s
|
|
363
443
|
```
|
|
364
444
|
|
|
365
445
|
Audio sources can be MP3 inputs, audio plugin results, or the embedded audio stream from an input video. Video inputs are visually silent unless you explicitly add `audio clip`. Plugin-generated audio (TTS, music, and probed native audio on generated video clips) is EBU R128 loudness-normalized during render to about -16 LUFS; `volume` is a relative gain applied after that. Ordinary imported audio/video is not auto-normalized.
|
|
@@ -369,6 +449,13 @@ Audio sources can be MP3 inputs, audio plugin results, or the embedded audio str
|
|
|
369
449
|
| `volume` | number | Relative gain (0–1+). For generated audio, applied after loudness normalization. |
|
|
370
450
|
| `fade_in` | number | Fade-in duration (seconds) |
|
|
371
451
|
| `fade_out` | number | Fade-out duration (seconds) |
|
|
452
|
+
| `trim_start` | number | Start this many seconds into the file |
|
|
453
|
+
| `pan` | number | -1 (left) to 1 (right) |
|
|
454
|
+
| `duck_under` | name | Lower this track while the named track plays |
|
|
455
|
+
| `loudness` | string | `"ebu-r128"` normalizes an imported file |
|
|
456
|
+
| `animate` | object | `{ type: "volume", from: { volume: 0 }, to: { volume: 1 } }` automates the level |
|
|
457
|
+
|
|
458
|
+
Mixing recipes (J- and L-cuts, ducking, automation): `editing-actions.md` → Sound.
|
|
372
459
|
|
|
373
460
|
### Plugin Calls
|
|
374
461
|
|
|
@@ -433,7 +520,7 @@ Use `aspect_ratio: "9:16"` for vertical output, `"16:9"` for wide, `"1:1"` for s
|
|
|
433
520
|
|
|
434
521
|
**Invalid (will not validate):**
|
|
435
522
|
|
|
436
|
-
```vidscript
|
|
523
|
+
```vidscript invalid
|
|
437
524
|
[-] = video xai.imagine("...") # missing xAI import
|
|
438
525
|
[0s .. 5s] = video cf.video("...") # missing Cloudflare import
|
|
439
526
|
[0s .. 15s] = audio eleven.generateMusic("...") # missing `import eleven from "@elevenlabs/music"`
|
|
@@ -441,20 +528,92 @@ Use `aspect_ratio: "9:16"` for vertical output, `"16:9"` for wide, `"1:1"` for s
|
|
|
441
528
|
[0s .. 15s] = audio eleven.music("..."), volume: 0.5 # trailing params after direct plugin call
|
|
442
529
|
```
|
|
443
530
|
|
|
444
|
-
##
|
|
531
|
+
## Reuse: values, functions, and modules
|
|
532
|
+
|
|
533
|
+
Anything that can be placed is a value: an input, a plugin result, `text(…)` / `audio(…)` /
|
|
534
|
+
`solid(…)`, a method chain, or a function call. A time block places one.
|
|
535
|
+
|
|
536
|
+
### Functions
|
|
537
|
+
|
|
538
|
+
```vidscript
|
|
539
|
+
export fn lowerThird(title: string, color: string = "#ff5500") -> video {
|
|
540
|
+
[- 2s] = text title, color: color, size: 48
|
|
541
|
+
}
|
|
542
|
+
|
|
543
|
+
[0s .. 2s] = lowerThird("Konkan Day 1")
|
|
544
|
+
[2s .. 4s] = lowerThird(title: "Day 2", color: "#00ff00")
|
|
545
|
+
|
|
546
|
+
# one-liners: fn … = expression
|
|
547
|
+
fn label(s: string) -> text = text(s, size: 24)
|
|
548
|
+
```
|
|
549
|
+
|
|
550
|
+
| Form | Meaning |
|
|
551
|
+
|------|---------|
|
|
552
|
+
| `fn name(params) -> kind { … }` | Block body: places items when called and placed |
|
|
553
|
+
| `fn name(params) -> kind = expr` | Expression body: returns that value |
|
|
554
|
+
| `export fn …` | Exported (callable through an imported namespace) |
|
|
555
|
+
| `export timeline name(…) { … }` | Same as `export fn name(…) -> timeline { … }` |
|
|
556
|
+
|
|
557
|
+
Return kinds: `video`, `image`, `text`, `audio`, `timeline`. `-> video` is a closed unit
|
|
558
|
+
(place it with properties — `[0s .. 4s] = card("A"), x: 40, opacity: 0.5, blend: "screen"` — or
|
|
559
|
+
chain `.Opacity()`, `.Resize()`, `.Filter()`, `.Crop()`, `.Mask()` and it draws as one item);
|
|
560
|
+
`-> timeline` is open, its items join the caller. A `filter` or `shader` without `target:`
|
|
561
|
+
inside a function or timeline body grades that unit's picture, not the whole frame.
|
|
562
|
+
|
|
563
|
+
Arguments are checked at the call: a wrong kind, an unknown or missing parameter, too
|
|
564
|
+
many arguments, or a value of the wrong kind is a compile error naming the parameter.
|
|
565
|
+
Optional parameters take `name: kind = default`.
|
|
566
|
+
|
|
567
|
+
`use card at 0s .. 2s with { title: "A" }` is exactly `[0s .. 2s] = card(title: "A")` —
|
|
568
|
+
the same IR.
|
|
569
|
+
|
|
570
|
+
### Modules
|
|
571
|
+
|
|
572
|
+
```vidscript
|
|
573
|
+
import brand from "./brand.vid" # another .vid file's exports
|
|
574
|
+
|
|
575
|
+
[0s .. 2s] = brand.lowerThird("Hello")
|
|
576
|
+
[0s .. 2s] = text "x", color: brand.accent
|
|
577
|
+
use brand.card at 2s .. 4s with { title: "B" }
|
|
578
|
+
```
|
|
579
|
+
|
|
580
|
+
`brand.vid` exports with `export fn lowerThird(title: string) -> video { … }`,
|
|
581
|
+
`export timeline card(title: string) { … }`, and `export const accent = "#ff5500"`.
|
|
582
|
+
|
|
583
|
+
### Builtin values
|
|
584
|
+
|
|
585
|
+
```vidscript
|
|
586
|
+
[0s .. 2s] = solid("#1a2b3c", opacity: 0.8) # flat-colour rectangle
|
|
587
|
+
[2s .. 3s] = gap() # transparent gap (advances the cursor)
|
|
588
|
+
[4s .. 4s] = marker("act 2") # named instant (timeline data, not rendered)
|
|
589
|
+
[0s .. 8s] = captions("talk.srt", size: 32) # one text layer per SRT cue
|
|
590
|
+
```
|
|
591
|
+
|
|
592
|
+
Any visual value takes the same placement properties: `x`, `y`, `width`, `height`,
|
|
593
|
+
`opacity`, `rotation`, `radius`, `corners`, `area`, `blend`, `animate`, `enabled`.
|
|
594
|
+
`blend: "screen"` on a clip lowers to a composite layer over whatever is below the placement;
|
|
595
|
+
on text, a solid, or a function it draws as a group with that blend. A blend needs something
|
|
596
|
+
below it in the same window, or it is a compile error. More than 4 nested groups, or more
|
|
597
|
+
than 16 alive at once, is a compile warning (each group renders into its own GPU target).
|
|
598
|
+
`volume:` on a generated clip with its own sound sets that sound's level.
|
|
445
599
|
|
|
446
|
-
|
|
600
|
+
### Names must resolve
|
|
447
601
|
|
|
448
|
-
|
|
602
|
+
An unquoted word is only allowed where a value is an enumeration (`align: center`,
|
|
603
|
+
`position: top`, `style: title`, `mode: screen`). Everywhere else an unresolved name is
|
|
604
|
+
`Unknown variable 'titel'`. Quote strings; pass real variables.
|
|
449
605
|
|
|
450
|
-
|
|
451
|
-
|
|
606
|
+
## Animations and effects
|
|
607
|
+
|
|
608
|
+
`animate:` moves any text or picture over time, with `motion.*` calls or object descriptors. Filters are separate `filter` lines (above), and can be animated too.
|
|
452
609
|
|
|
453
610
|
### animate: parameter
|
|
454
611
|
|
|
455
612
|
```vidscript
|
|
456
613
|
import motion from "@scenerok/basic-animations"
|
|
457
614
|
|
|
615
|
+
input hero = "hero.mp4"
|
|
616
|
+
input app_screen = "app-screen.png"
|
|
458
617
|
[0s .. 3s] = text "Hello", x: "50%", y: "50%", animate: motion.fadeIn(0.8s)
|
|
459
618
|
|
|
460
619
|
[-] = video hero, animate: [motion.fadeIn(0.5s), motion.slideY(-40, 0, 1.2s)]
|
|
@@ -473,17 +632,19 @@ import motion from "@scenerok/basic-animations"
|
|
|
473
632
|
}
|
|
474
633
|
```
|
|
475
634
|
|
|
476
|
-
|
|
635
|
+
Easings: `linear`, `easeInQuad`, `easeOutQuad`, `easeInOutQuad`, `easeInCubic`, `easeOutCubic`, `easeInOutCubic`, `easeOutBack`, `spring`. A list of objects with absolute `start` / `end` chains segments on one property (a path with more than two points); `keyframes:` is not supported and is silently dropped.
|
|
477
636
|
|
|
478
|
-
###
|
|
637
|
+
### Animated filters
|
|
479
638
|
|
|
480
639
|
```vidscript
|
|
640
|
+
import motion from "@scenerok/basic-animations"
|
|
641
|
+
|
|
481
642
|
[0s .. 5s] = filter "vignette", intensity: 0.4
|
|
482
643
|
|
|
483
644
|
[0s .. 5s] = filter "glitch", intensity: 0.5, animate: motion.fadeIn(1s)
|
|
484
645
|
```
|
|
485
646
|
|
|
486
|
-
|
|
647
|
+
Filters are optional finishing tools; use them only when they improve the composition. Parameter names per filter: `editing-actions.md` → Color and finishing.
|
|
487
648
|
|
|
488
649
|
### Animation functions (`@scenerok/basic-animations`)
|
|
489
650
|
|
|
@@ -500,24 +661,12 @@ Built-in filters: `monochrome`, `sepia`, `blur`, `chromatic`, `glitch`, `vignett
|
|
|
500
661
|
| `motion.float(duration?, amp?)` | Gentle vertical bob |
|
|
501
662
|
| `motion.typewriter(duration?)` | Character reveal |
|
|
502
663
|
|
|
503
|
-
### Render architecture (contributors)
|
|
504
|
-
|
|
505
|
-
Implemented registry-based rendering. See `plan-v1/35-render-subsystem-architecture.md`.
|
|
506
|
-
|
|
507
|
-
| Registry | Path | Add via |
|
|
508
|
-
|----------|------|---------|
|
|
509
|
-
| Motion | `src/lib/motion/registry.ts` | `registerEasing`, `registerMotionBehavior` |
|
|
510
|
-
| Surface | `src/render/surfaces/registry.ts` | `registerSurfaceRenderer` |
|
|
511
|
-
| Effect | `src/render/effects/registry.ts` | `registerEffect` or `src/shaders/library.ts` |
|
|
512
|
-
| Engine | `src/render/engine/index.ts` | `createRenderEngine` backends |
|
|
513
|
-
|
|
514
|
-
For SceneRok platform engineering, load the **`scenerok-development`** skill (`.agents/skills/scenerok-development/`).
|
|
515
|
-
|
|
516
664
|
## Expressions
|
|
517
665
|
|
|
518
666
|
Used in time specs, `let` values, and function arguments:
|
|
519
667
|
|
|
520
668
|
```vidscript
|
|
669
|
+
let title_text = "Launch"
|
|
521
670
|
let x = 5s # Time literals
|
|
522
671
|
let y = x * 2 # Arithmetic
|
|
523
672
|
let prompt = "Scene: " + title_text # String concatenation
|
|
@@ -533,7 +682,7 @@ Operators: `+`, `-`, `*`, `/` with standard precedence.
|
|
|
533
682
|
[- 3s] = text "{{headline | Default Text}}", color: "{{color | #FFFFFF}}"
|
|
534
683
|
```
|
|
535
684
|
|
|
536
|
-
|
|
685
|
+
Templates fill each `{{name | default}}` before the script compiles; outside a template the default is used.
|
|
537
686
|
|
|
538
687
|
## CLI Commands
|
|
539
688
|
|
|
@@ -556,8 +705,6 @@ scenerok status <render-id>
|
|
|
556
705
|
# Install agent skills
|
|
557
706
|
scenerok skills install <platform>
|
|
558
707
|
|
|
559
|
-
# Set API secrets for plugins
|
|
560
|
-
scenerok secrets set ELEVENLABS_API_KEY=your-key
|
|
561
708
|
```
|
|
562
709
|
|
|
563
710
|
## Best Practices
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# VidScript sample: a 13-second product promo
|
|
2
|
+
|
|
3
|
+
A complete script to copy and adapt: two generated shots joined by a dissolve,
|
|
4
|
+
titles in named areas, a voiceover, and a music bed that ducks under it.
|
|
5
|
+
Validate it before rendering.
|
|
6
|
+
|
|
7
|
+
```vidscript
|
|
8
|
+
import xai from "@scenerok/xai"
|
|
9
|
+
import eleven from "@elevenlabs/music"
|
|
10
|
+
import motion from "@scenerok/basic-animations"
|
|
11
|
+
import tr from "@scenerok/transitions"
|
|
12
|
+
|
|
13
|
+
# Copy lives in variables so it is easy to change.
|
|
14
|
+
let product = "RokMilk"
|
|
15
|
+
let hook = "Chocolate milkshake, ready in seconds"
|
|
16
|
+
let cta = "Find it at your nearest grocery store"
|
|
17
|
+
|
|
18
|
+
# Generated media is bound with let, so its real length is known.
|
|
19
|
+
# Prompts never ask for text; all copy is added with `text` below.
|
|
20
|
+
let kitchen = xai.imagine("Chocolate milk carton on a sunlit kitchen counter, cinematic lighting, shallow depth of field, no text, no words, no letters, no captions, no logos, no watermark, no readable UI copy", aspect_ratio: "9:16", duration: 6)
|
|
21
|
+
let pour = xai.imagine("Slow pour of chocolate milkshake into a tall glass, macro, warm light, no text, no words, no letters, no captions, no logos, no watermark, no readable UI copy", aspect_ratio: "9:16", duration: 6)
|
|
22
|
+
let vo = xai.tts("Meet RokMilk. Chocolate milkshake, ready in seconds. Find it at your nearest grocery store.", voice: "eve")
|
|
23
|
+
let bed = eleven.music("Warm, upbeat acoustic bed for a food ad", duration: 13, instrumental: true)
|
|
24
|
+
|
|
25
|
+
# Picture: each [-] starts where the previous visual block ended.
|
|
26
|
+
[-] = kitchen
|
|
27
|
+
[- 0.6s] = tr.dissolve()
|
|
28
|
+
[-] = pour
|
|
29
|
+
|
|
30
|
+
# Titles go in named areas, never at guessed coordinates.
|
|
31
|
+
[0s .. 3s] = text product, style: title, area: "title", animate: motion.popIn(0.6s)
|
|
32
|
+
[3s .. 7s] = text hook, area: "lower-third", animate: motion.riseIn(0.6s)
|
|
33
|
+
[9s .. 12.6s] = text cta, area: "caption", animate: motion.fadeIn(0.5s)
|
|
34
|
+
|
|
35
|
+
# Sound: the voiceover runs for its measured length; the bed ducks under it.
|
|
36
|
+
[0s .. vo.duration] = audio vo
|
|
37
|
+
[0s .. 13s] = audio bed, volume: 0.6, duck_under: vo, fade_out: 1.5s
|
|
38
|
+
|
|
39
|
+
output to "rokmilk-promo.mp4", resolution: "1080x1920", fps: 30
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
To cut your own footage instead of generating it, replace the `let … =
|
|
43
|
+
xai.imagine(…)` lines with `input` declarations and see `editing-actions.md`.
|
|
44
|
+
The `@scenerok/cli` package ships more scripts in `examples/system/`.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# VidScript v2 — Strict Agent Reference
|
|
2
2
|
|
|
3
|
-
Follow these rules exactly.
|
|
3
|
+
Follow these rules exactly. For a complete working script, see `vidscript-sample.md`; for cuts, layering, masks, audio mixing, and grading, see `editing-actions.md`.
|
|
4
4
|
|
|
5
5
|
## Rule 1: Package imports are mandatory
|
|
6
6
|
|
|
@@ -17,11 +17,7 @@ Without the owning import, calls like `xai.imagine`, `cf.video`, `xai.tts`, `ele
|
|
|
17
17
|
|
|
18
18
|
## Rule 2: Use URL assets selectively
|
|
19
19
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
Do not use full-page or very long website screenshots as video backgrounds; their text is usually illegible at output size. Crop or frame screenshots to the specific visual proof point, and use them sparingly.
|
|
23
|
-
|
|
24
|
-
Website screenshots are optional source material, not a requirement to build the whole video from screenshots. You may combine them with still generation, text-to-video, image-to-video, reference-to-video, or extension plugins for lifestyle scenes, motion backgrounds, transitions, or product context. Do not invent claims; use website copy or ask the user.
|
|
20
|
+
A product or landing-page URL is optional source material. Capture focused, legible pieces (above-the-fold views, product shots, logos), never full-page screenshots as backgrounds, and don't invent claims. The full URL workflow is in `workflow.md`.
|
|
25
21
|
|
|
26
22
|
## Rule 2.5: Probe real asset type and dimensions before placement
|
|
27
23
|
|
|
@@ -37,6 +33,8 @@ identify asset.webp
|
|
|
37
33
|
Use real video files for motion footage and still image inputs for screenshots, logos, product photos, and brand marks. The `video` primitive accepts both videos and images; image inputs render as static clips for the block duration.
|
|
38
34
|
|
|
39
35
|
```vidscript
|
|
36
|
+
import motion from "@scenerok/basic-animations"
|
|
37
|
+
|
|
40
38
|
input screenshot = "assets/screenshots/homepage.png"
|
|
41
39
|
[0s .. 4s] = video screenshot, width: 1080, height: 1920, x: 0, y: 0, animate: motion.fadeIn(0.35s)
|
|
42
40
|
```
|
|
@@ -53,6 +51,7 @@ Add entry motion to real assets, not only text. When a static visual needs a cle
|
|
|
53
51
|
```vidscript
|
|
54
52
|
import motion from "@scenerok/basic-animations"
|
|
55
53
|
|
|
54
|
+
input product = "assets/product.png"
|
|
56
55
|
[0s .. 3.4s] = video product, width: 760, height: 615, x: 160, y: 300, animate: [motion.riseIn(0.6s, 70), motion.float(2.4s, 8)]
|
|
57
56
|
[3.4s .. 4s] = video product, width: 760, height: 615, x: 160, y: 300, animate: motion.fadeOut(0.6s)
|
|
58
57
|
```
|
|
@@ -64,8 +63,10 @@ AI-generated images and videos must not contain readable text. In every prompt f
|
|
|
64
63
|
Good prompt pattern:
|
|
65
64
|
|
|
66
65
|
```vidscript
|
|
66
|
+
import xai from "@scenerok/xai"
|
|
67
|
+
|
|
67
68
|
[-] = video xai.imagine("Clean cinematic product background, premium lighting, no text, no words, no letters, no captions, no logos, no watermark, no readable UI copy", aspect_ratio: "9:16", duration: 5)
|
|
68
|
-
[0.5s .. 3s] = text "Launch offer", style: title, color: "#FFFFFF",
|
|
69
|
+
[0.5s .. 3s] = text "Launch offer", style: title, color: "#FFFFFF", area: "title", stroke: "#000000", stroke_width: 3
|
|
69
70
|
```
|
|
70
71
|
|
|
71
72
|
Invalid: asking the generator for signs, labels, packaging text, app UI text, title cards, lower thirds, prices, CTA buttons, or any readable copy.
|
|
@@ -101,13 +102,9 @@ Do not overuse one plugin function. Choose deliberately:
|
|
|
101
102
|
| Voiceover | `xai.tts`, `xai.textToSpeech` |
|
|
102
103
|
| Music bed | `eleven.music`, `eleven.generateMusic`, `eleven.composeMusic` |
|
|
103
104
|
|
|
104
|
-
Use `imageToVideo` for one extracted screenshot/product/logo image
|
|
105
|
+
Use `imageToVideo` for one extracted screenshot/product/logo image and `referenceToVideo` for 1–7 reference images (xAI: prompt required, duration ≤ 10s). Cloudflare calls take an explicit `model:` and `aspect_ratio:`; models, signatures, and durations are in `ai-models.md` and the plugin table in `vidscript-guide.md`.
|
|
105
106
|
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
xAI TTS voices are only `eve`, `ara`, `rex`, `sal`, and `leo`.
|
|
109
|
-
|
|
110
|
-
For ElevenLabs music, import `@elevenlabs/music` and use `eleven.music(...)`, `eleven.generateMusic(...)`, or `eleven.composeMusic(...)`. Do not call `eleven.tts(...)`; ElevenLabs TTS is not registered in VidScript today. Ads, promos, tutorials, and reels should normally include either `xai.tts` voiceover, an ElevenLabs music bed, or both unless the user asks for silent output.
|
|
107
|
+
xAI TTS voices are only `eve`, `ara`, `rex`, `sal`, and `leo`. ElevenLabs is music only (`eleven.music`, `eleven.generateMusic`, `eleven.composeMusic`); there is no `eleven.tts`. Ads, promos, tutorials, and reels should normally have a voiceover, a music bed, or both unless the user asks for silence.
|
|
111
108
|
|
|
112
109
|
## Rule 4.5: `[-]` uses separate audio and visual playheads
|
|
113
110
|
|
|
@@ -129,16 +126,20 @@ Use explicit ranges when you need exact synchronization, overlaps, or cuts. Do n
|
|
|
129
126
|
|
|
130
127
|
## Rule 4.6: Never guess generated-asset durations
|
|
131
128
|
|
|
132
|
-
|
|
129
|
+
Avoid — the real TTS length is unknown at write time, so a guessed window clips or overlaps:
|
|
130
|
+
|
|
131
|
+
```vidscript avoid
|
|
132
|
+
import xai from "@scenerok/xai"
|
|
133
133
|
|
|
134
|
-
```vidscript
|
|
135
134
|
[0s .. 3s] = audio xai.tts("A sentence that may well run past three seconds", voice: "eve")
|
|
136
135
|
[3s .. 6s] = audio xai.tts("This one starts while the first is still playing", voice: "eve")
|
|
137
136
|
```
|
|
138
137
|
|
|
139
|
-
|
|
138
|
+
Do this — assign with `let` and reference the measured `duration` (arithmetic allowed), or use `[-]` auto blocks:
|
|
140
139
|
|
|
141
140
|
```vidscript
|
|
141
|
+
import xai from "@scenerok/xai"
|
|
142
|
+
|
|
142
143
|
let vo1 = xai.tts("First line", voice: "eve")
|
|
143
144
|
let vo2 = xai.tts("Second line", voice: "eve")
|
|
144
145
|
[0 .. vo1.duration] = audio vo1
|
|
@@ -207,10 +208,12 @@ let bed = eleven.music("Soft premium ad music", duration: 15, instrumental: true
|
|
|
207
208
|
|
|
208
209
|
```vidscript
|
|
209
210
|
[0s .. 15s] = filter "vignette", intensity: 0.3
|
|
210
|
-
[0s .. 15s] = filter "saturation",
|
|
211
|
+
[0s .. 15s] = filter "saturation", amount: 1.12
|
|
211
212
|
```
|
|
212
213
|
|
|
213
|
-
Filters are optional finishing tools, not required ingredients. Use them only when they improve the visual result. `saturation`
|
|
214
|
+
Filters are optional finishing tools, not required ingredients. Use them only when they improve the visual result. Each filter has its own parameter name (`saturation` takes `amount`, `blur` takes `radius`); a wrong name is ignored without an error. The table is in `editing-actions.md` → Color and finishing.
|
|
215
|
+
|
|
216
|
+
A `filter` line grades everything in its window. To grade one clip, add `target: clip` or write `clip.Filter("sepia", intensity: 0.6)`.
|
|
214
217
|
|
|
215
218
|
## Rule 10: Always end with output
|
|
216
219
|
|
|
@@ -220,6 +223,24 @@ output to "video.mp4", resolution: "1080x1920", fps: 30
|
|
|
220
223
|
|
|
221
224
|
Do **not** use legacy `config { }` blocks — they are not part of VidScript v2.
|
|
222
225
|
|
|
226
|
+
## Rule 11: Edits are values — place the result
|
|
227
|
+
|
|
228
|
+
`Trim`, `Speed`, `Loop`, `Crop`, `Resize`, `Mask`, `Filter`, `Overlay`, and `Composite` return a new value. Place the result in one line:
|
|
229
|
+
|
|
230
|
+
```vidscript
|
|
231
|
+
input hero = "hero.mp4"
|
|
232
|
+
input logo = "logo.png"
|
|
233
|
+
[0s .. 4s] = hero.Trim(2s, 6s).Crop(420, 0, 1080, 1920)
|
|
234
|
+
[4s .. 8s] = hero.Trim(6s, 10s).Overlay(logo.Resize(200, 200), x: 840, y: 60)
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
- A placed value lasts its own length (trimmed range ÷ speed × loops). Make the window that long.
|
|
238
|
+
- Put one video over another with `Overlay`, `Composite`, or `blend:`. Two plain clips in the same window have no defined order.
|
|
239
|
+
- `Mask` on its own line after the clip is placed is a compile error.
|
|
240
|
+
- Frame counts are written `48 frame` and use the output fps (30 if `output` has none).
|
|
241
|
+
|
|
242
|
+
All editing actions (masks, speed ramps, ducking, LUTs, timecode): `editing-actions.md`.
|
|
243
|
+
|
|
223
244
|
## Animations (after import)
|
|
224
245
|
|
|
225
246
|
```vidscript
|
|
@@ -240,12 +261,6 @@ Each error line includes `Line N:C — message` when the server returns location
|
|
|
240
261
|
|
|
241
262
|
## Bundled examples
|
|
242
263
|
|
|
243
|
-
|
|
244
|
-
|------|----------|
|
|
245
|
-
| `minimal-text-reel.vid` | Text-only, no API keys |
|
|
246
|
-
| `product-launch.vid` | User video input + text + vignette |
|
|
247
|
-
| `ecom-product-launch.vid` | xAI product visuals + CTA |
|
|
248
|
-
| `3-tips-fitness.vid` | Multiple `[-]` blocks + xAI per segment |
|
|
249
|
-
| `rokmilk-chocolate-promo.vid` | 15s generated-video promo pattern |
|
|
264
|
+
The `@scenerok/cli` npm package ships working scripts in `examples/system/`: `minimal-text-reel.vid` (text only), `product-launch.vid` (your own video + text), `ecom-product-launch.vid` (generated product visuals + CTA), `3-tips-fitness.vid` (several `[-]` blocks), `rokmilk-chocolate-promo.vid` (15 s generated promo).
|
|
250
265
|
|
|
251
|
-
Full grammar: `vidscript-guide.md
|
|
266
|
+
Full grammar: `vidscript-guide.md`.
|