@scenerok/cli 2.2.0 → 2.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.
Files changed (32) hide show
  1. package/README.md +6 -6
  2. package/dist/commands/skills.d.ts.map +1 -1
  3. package/dist/commands/skills.js +31 -25
  4. package/dist/utils/skillsManifest.d.ts +13 -3
  5. package/dist/utils/skillsManifest.d.ts.map +1 -1
  6. package/dist/utils/skillsManifest.js +35 -12
  7. package/examples/system/rokmilk-chocolate-promo.vid +1 -1
  8. package/examples/system/voice-visual-promo.vid +1 -1
  9. package/package.json +3 -3
  10. package/skills/scenerok/SKILL.md +150 -0
  11. package/skills/{remote → scenerok/references}/ai-models.md +1 -2
  12. package/skills/{remote → scenerok/references}/animations-catalog.md +139 -2
  13. package/skills/{remote → scenerok/references}/animations-picker.md +12 -1
  14. package/skills/{remote → scenerok/references}/brand-setup.md +3 -1
  15. package/skills/{remote → scenerok/references}/catalog-index.json +163 -19
  16. package/skills/scenerok/references/editing-actions.md +560 -0
  17. package/skills/{remote → scenerok/references}/environments.md +23 -24
  18. package/skills/{remote → scenerok/references}/intent-router.md +5 -15
  19. package/skills/{remote → scenerok/references}/media-stdlib.md +2 -3
  20. package/skills/{remote → scenerok/references}/music.md +2 -21
  21. package/skills/scenerok/references/sdk-guide.md +301 -0
  22. package/skills/{remote → scenerok/references}/shaders-filters.md +9 -6
  23. package/skills/{remote → scenerok/references}/text-styles.md +15 -35
  24. package/skills/{remote → scenerok/references}/transitions-catalog.md +2 -0
  25. package/skills/{remote → scenerok/references}/tts.md +4 -22
  26. package/skills/{remote → scenerok/references}/vidscript-guide.md +250 -67
  27. package/skills/scenerok/references/vidscript-sample.md +44 -0
  28. package/skills/{remote → scenerok/references}/vidscript-strict.md +41 -26
  29. package/skills/{remote → scenerok/references}/workflow.md +6 -7
  30. package/skills/remote/sdk-guide.md +0 -201
  31. package/skills/remote/vidscript-sample.md +0 -28
  32. package/skills/shared/SKILL.md +0 -157
@@ -1,8 +1,6 @@
1
1
  # SceneRok Intent Router
2
2
 
3
- Load after **`vidscript-strict.md`** and **`environments.md`** when the user wants a video.
4
-
5
- **Remote base:** `https://scenerok.com/.well-known/agent-skills/`
3
+ Pick the workflow for a video request, then read only the references it lists.
6
4
 
7
5
  ## Step 0 — Detect environment
8
6
 
@@ -23,6 +21,7 @@ Defaults unless the user says otherwise:
23
21
  | Product URL + CTA + lifestyle b-roll | Product promo |
24
22
  | Tweet / Reddit / Spotify / follow card | Social proof card |
25
23
  | Multiple clips + cross-fades / whips | Scene montage |
24
+ | User's own footage to cut, trim, speed up, layer, mask, grade, or mix | Edit existing footage |
26
25
  | Voiceover explainer | Narrated explainer |
27
26
  | PR / code walk / terminal | Code demo |
28
27
 
@@ -33,6 +32,7 @@ Defaults unless the user says otherwise:
33
32
  | **Product promo** | `ai-models.md`, `text-styles.md`, `music.md`, `workflow.md`, `brand-setup.md` (+ `sdk-guide.md` if TS) | `xai.imagine` / `await xai.genVideo(...)`, `motion.popIn` / `fadeIn`, `eleven.music` |
34
33
  | **Motion graphic** | `animations-picker.md` (+ `animations-catalog.md` if needed) | `animations.*` (VidScript HyperFrames; prefer VidScript for catalog blocks) |
35
34
  | **Social proof card** | `animations-picker.md` | `animations.x_post`, `reddit_post`, … (VidScript) |
35
+ | **Edit existing footage** | `editing-actions.md` (+ `media-stdlib.md` to probe the clips, `transitions-catalog.md` for cuts) | `Trim` / `Speed` / `Loop` / `Crop` / `Mask` / `Overlay` / `Composite`, `blend:`, `duck_under:`, `filter "lut"` (VidScript only) |
36
36
  | **Scene montage** | `transitions-catalog.md`, `ai-models.md` (+ `sdk-guide.md` if TS loops) | `tr.dissolve` · SDK: `import tr from '@scenerok/transitions'` + `t[start][end] = tr.dissolve()` |
37
37
  | **Narrated explainer** | `tts.md`, `text-styles.md`, `music.md`, `workflow.md` (+ `sdk-guide.md` if TS) | `xai.tts` · `await xai.tts(...)` + `t.push(vo)` |
38
38
  | **Code demo / PR** | `animations-picker.md` (code rows) | `code_typing`, `code_diff` (VidScript) |
@@ -44,7 +44,7 @@ Defaults unless the user says otherwise:
44
44
  ## Staged routing
45
45
 
46
46
  1. **Classify** — workflow from table above
47
- 2. **Choose authoring** — short/human → VidScript; complex/agent (loops, computed timing, multi-clip arithmetic) → `@scenerok/sdk` (`sdk-guide.md`)
47
+ 2. **Choose authoring** — VidScript by default and for every editing action (masks, blends, crops, ducking, LUTs, timecode); `@scenerok/sdk` (`sdk-guide.md`) only for loops or computed timing (it has none of the editing actions)
48
48
  3. **Load catalogs** — only what that workflow needs (conditional loading)
49
49
  4. **Name block or API** — VidScript: pick `animations.<fn>` from `animations-picker.md`. SDK: factories (`video` / `loadVideo`, `text`, `fadeIn`) + AI imports (`@scenerok/xai`, …)
50
50
  5. **Brand** — if promo and missing colors/logo, one message per `brand-setup.md`
@@ -57,21 +57,11 @@ Defaults unless the user says otherwise:
57
57
 
58
58
  - `animations.<fn>(...)` over hand-authored HTML when a block fits
59
59
  - Text/headline treatments: try `animations.text_*` (text-anim family) before `motion.*` primitives or custom HTML
60
- - `tr.<fn>()` for scene transitions; `filter "…"` for single-clip VFX
60
+ - `tr.<fn>()` for scene transitions; `filter "…"` for grades and VFX
61
61
  - No matching block → `text` + `animate: motion.<primitive>`, or `animations.custom_html(...)` to author your own HTML/CSS + GSAP / Three.js / any seekable JS animation lib (full author contract + minimal template in `animations-catalog.md` → "Custom HTML")
62
62
  - Unverified blocks (`verified: no`) ship demo content — prefer verified families
63
63
  - Complex procedural timelines → TypeScript `@scenerok/sdk` (`sdk-guide.md`) instead of exploding VidScript
64
64
 
65
- ## Conditional loading
66
-
67
- - Skip `animations-catalog.md` unless you need full param tables
68
- - Skip `transitions-catalog.md` unless multiple clips or explicit transition ask
69
- - Always `vidscript-strict.md` before VidScript composition
70
- - Load `sdk-guide.md` before TypeScript SDK composition (scheduling, `t.grid`, AI plugin imports, `async main`)
71
- - Load `media-stdlib.md` when probing durations, grabbing frames, `loadVideo`, or layout-fitting assets
72
- - Load `ai-models.md` / `tts.md` / `music.md` for generative media in **either** language (examples cover both)
73
- - `vidscript-guide.md` only for grammar questions
74
-
75
65
  ## Reference
76
66
 
77
67
  - Grammar: `vidscript-guide.md` or https://scenerok.com/.well-known/agent-skills/vidscript-guide.md
@@ -35,10 +35,10 @@ import media from "@scenerok/media"
35
35
  input hero = "./hero.mp4"
36
36
  let d = media.length(hero)
37
37
  let loudness = media.loudness(hero)
38
- let thumb = media.grabFrame(hero, d / 2)
38
+ let thumb = media.grabFrame(hero, d / 2) # data URL of the middle frame, to look at
39
39
 
40
40
  [0s .. d] = video hero
41
- [0s .. 2s] = video thumb, width: 400
41
+ [d .. d + 2s] = hero.Trim(d / 2, d / 2 + 1s).Speed(0) # hold the middle frame for 2 s
42
42
  ```
43
43
 
44
44
  `media.length` returns a time value usable in arithmetic and `[start .. end]` ranges.
@@ -63,4 +63,3 @@ let thumb = media.grabFrame(hero, d / 2)
63
63
  | `fitContain` / `fitCover` | yes | `media.fitContain` | `{ width, height, x, y }` |
64
64
  | `parseTime` / `timecode` / `beats` / `clampTime` | yes | yes | time helpers |
65
65
 
66
- Implementation: `src/lib/media/stdlib/`. VidScript plugin: `src/plugins/media/`.
@@ -2,34 +2,15 @@
2
2
 
3
3
  `import eleven from "@elevenlabs/music"`
4
4
 
5
- ### VidScript
6
-
7
5
  ```vidscript
8
6
  import eleven from "@elevenlabs/music"
9
7
  let bed = eleven.music("Warm premium launch bed", duration: 15, instrumental: true)
10
8
  [0s .. 15s] = audio bed, volume: 0.35, fade_out: 2s
11
9
  ```
12
10
 
13
- ### TypeScript SDK
14
-
15
- ```ts
16
- import { timeline } from '@scenerok/sdk'
17
- import eleven from '@elevenlabs/music'
18
-
19
- export default async function main() {
20
- const t = timeline({ width: 1080, height: 1920, fps: 30 })
21
- const bed = await eleven.music('Warm premium launch bed', {
22
- duration: 15,
23
- force_instrumental: true,
24
- })
25
- t.push(bed.with({ volume: 0.35, fadeOut: 2 }))
26
- return t.toIR()
27
- }
28
- ```
29
-
30
- Use a binding (`let bed` / `const bed`) when you need volume or fades. Do not call `eleven.tts` — use `xai.tts` for voiceover.
11
+ Use `let bed = eleven.music(...)` then `audio bed, volume: ...` for fades. Do not call `eleven.tts`.
31
12
 
32
- ## Examples (VidScript)
13
+ ## Examples
33
14
 
34
15
  **Drone underscore**
35
16
 
@@ -0,0 +1,301 @@
1
+ # @scenerok/sdk — TypeScript authoring for complex scenes
2
+
3
+ Use **TypeScript + `@scenerok/sdk`** when the scene needs loops, branches, arithmetic timing, or data-driven composition. Keep **VidScript** for short human-authored promos and templates.
4
+
5
+ Both paths emit the same `IRTimeline`, and the SDK follows VidScript's rules: a value placed with the same properties lowers to the same IR. Preview and render (Pass 2) are unchanged.
6
+
7
+ ## When to choose which
8
+
9
+ | Signal | Language |
10
+ |--------|----------|
11
+ | Short promo, human edit, template placeholders | VidScript (`.vid`) |
12
+ | Loop over clips, computed `start`/`end`, agent-authored complex timeline | TypeScript + `@scenerok/sdk` (`.ts`) |
13
+ | Mostly an edit, with one data-driven part | VidScript that imports a `.ts` module ([below](#use-typescript-from-vidscript)) |
14
+
15
+ ## Schedule syntax
16
+
17
+ JavaScript has no `t[0, end]` dual key (comma operator collapses to one index). Use dual-index assignment:
18
+
19
+ ```ts
20
+ import { timeline, loadVideo, text, audio, s, fadeIn } from '@scenerok/sdk'
21
+
22
+ export default async function main() {
23
+ const t = timeline({ width: 1080, height: 1920, fps: 30 })
24
+ const scene1 = await loadVideo('./hero.mp4')
25
+
26
+ t[0][scene1.duration] = scene1.animate(fadeIn(s(0.4)))
27
+ t['0s']['3s'] = text('Hello', { size: 72 })
28
+ t[scene1.duration][scene1.duration + s(2)] = text('Next')
29
+ t.push(audio('./bed.mp3', { duration: 7, volume: 0.4 }))
30
+
31
+ return t.toIR()
32
+ }
33
+ ```
34
+
35
+ | Form | Meaning |
36
+ |------|---------|
37
+ | `t[start][end] = value` | Preferred dual-index schedule |
38
+ | `t.range(start, end, value)` | Explicit call (no Proxy) |
39
+ | `t['-'] = value` / `t.push(value, duration?)` | Auto-append at the playhead |
40
+
41
+ Times are numbers (seconds), `s(3)` / `ms(300)`, `t.frames(48)`, or strings: `'3s'`, `'300ms'`, `'1:30'`, `'48f'` (frames of the timeline's fps), `'01:00:00:00'` (timecode at the timeline's fps, or `'01:00:00:00@24'`). Full JS arithmetic works. Like VidScript, every window snaps to the timeline's frame grid; `fps: 23.976` and the other NTSC rates are exact (`ir.timeBase`).
42
+
43
+ A video or audio value whose length is known ends when it stops playing, even inside a longer window. Without a known length it fills the window.
44
+
45
+ Use `loadVideo(path)` / `loadAudio(path)` to probe the length through the media stdlib, or pass `duration` when you know it:
46
+
47
+ ```ts
48
+ const clip = await loadVideo('./hero.mp4')
49
+ t[0][clip.duration] = clip
50
+ t.push(video('./b.mp4', { duration: 4 }))
51
+ ```
52
+
53
+ ## Values
54
+
55
+ | Factory | VidScript | Notes |
56
+ |---------|-----------|-------|
57
+ | `video(src, opts)` / `loadVideo` | `input a = "a.mp4"` | `rate:` interprets the footage at a frame rate |
58
+ | `imageSequence('shot.%04d.png', { count, rate?, first? })` | `input frames = "shot.%04d.png", count: …` | Plays `count / rate` seconds |
59
+ | `image(src, opts)` | an image input | Fills its window; 3 s when pushed |
60
+ | `text(content, opts)` | `text "…"` | `stagger: 'word' \| 'char'` for `typewriter()` |
61
+ | `audio(src, opts)` / `loadAudio` | `audio x` | `trimStart`, `pan`, `duckUnder`, `loudness: 'ebu-r128'`, `animate: volumeRamp(…)` |
62
+ | `solid(color, opts)` | `solid("#101820")` | Full frame unless sized; with `radius`/`corners` it is a shape matte |
63
+ | `gap()` | `gap()` | `t.push(gap(), 2)` moves the playhead 2 s |
64
+ | `marker(name, metadata?)` | `marker("act 2")` | `t[4][4] = marker('act 2')`; timeline data, never drawn |
65
+ | `filter(name, params)` / `shader(glsl, params)` | `filter "glow"` | Grades the frame (inside a nested timeline: only that timeline) |
66
+
67
+ Values are immutable. Methods return a copy, so one clip can be cut many ways:
68
+
69
+ ```ts
70
+ const beach = video('./beach.mp4', { duration: 60 })
71
+ t.push(beach.trim(10, 20).trim(2, 4)) // source 12–14 s: a second trim is relative to the first
72
+ t.push(beach.trim(30, 36).speed(1.5).resize(960, 540))
73
+ t.push(beach.trim(40, 44).crop(0, 0, 1280, 720).filter('sepia', { intensity: 0.6 }))
74
+ ```
75
+
76
+ | Method | On | Meaning |
77
+ |--------|----|---------|
78
+ | `trim(start, end?)` | video, image, audio | Keep a source range |
79
+ | `speed(x)` / `loop(n)` | video | Retiming; the value's `duration` follows |
80
+ | `resize(w, h)` / `opacity(x)` | video, image, timeline | Drawn size, opacity |
81
+ | `filter(name, params)` | video, image, timeline | Grade only this value (pointwise colour filters on clips) |
82
+ | `crop(x, y, w, h)` | video, image, timeline | Source pixels from the top-left; a second crop is relative |
83
+ | `mask(matte, { mode })` | video, image, timeline | `alpha` (default), `luma`, `inverted`; the matte is any picture |
84
+ | `animate(…)` / `with({ … })` | all pictures | Add animations / merge options |
85
+
86
+ ## Placement properties
87
+
88
+ Every picture takes the same options as a VidScript placement:
89
+
90
+ | Option | Meaning |
91
+ |--------|---------|
92
+ | `x`, `y`, `width`, `height` | Top-left and drawn size in output pixels |
93
+ | `area` | A grid area; explicit `x`/`y`/`width`/`height` win |
94
+ | `opacity`, `rotation` | Constant opacity and rotation (degrees) over the window |
95
+ | `radius`, `corners` | Rounded corners; four `[x, y]` corner pins (TL → TR → BR → BL) |
96
+ | `blend` | `multiply`, `screen`, `overlay`, `difference`, `add`, `subtract`, `darken`, `lighten` over what is already placed in the window |
97
+ | `motionBlur` | `true` or `{ shutter, samples }`: blurs the value's own `animate` motion |
98
+ | `enabled` | `false` skips the placement |
99
+ | `animate` | One animation or a list |
100
+
101
+ ```ts
102
+ t[0][6] = video('./city.mp4')
103
+ t[0][6] = video('./flare.mp4', { blend: 'screen', opacity: 0.7 })
104
+ t[1][4] = image('./logo.png', { width: 240, height: 240, x: 60, y: 60, radius: 120 })
105
+ t[0][6] = video('./portrait.mp4').mask(solid('#fff', { width: 800, height: 800, x: 560, y: 140, radius: 400 }))
106
+ ```
107
+
108
+ A blend needs something below it in the window; nothing below is an error, as in VidScript.
109
+
110
+ ## Animations
111
+
112
+ `fadeIn`, `fadeOut`, `slideY`, `slideX`, `scale`, `popIn`, `riseIn`, `swingIn`, `glitchIn`, `float`, `typewriter`, `rotate` — the same set and defaults as `@scenerok/basic-animations`. Any other type: `animation('kenBurns', { scale: 1 }, { scale: 1.1 }, 0)` (a duration of 0 spans the window). Gain automation on audio: `volumeRamp(0, 1, '2s')`.
113
+
114
+ ## Nested timelines and groups
115
+
116
+ A timeline is a value. Placed bare, its items join the parent shifted to the window start — like VidScript `use`. A nested timeline without its own size takes its parent's canvas.
117
+
118
+ ```ts
119
+ function card(title: string) {
120
+ const c = timeline({ name: 'card' })
121
+ c[0][3] = solid('#101820', { height: 200, y: 880, opacity: 0.85 })
122
+ c[0][3] = text(title, { size: 48, y: '88%' })
123
+ return c
124
+ }
125
+
126
+ t[2][5] = card('Konkan Day 1') // flat
127
+ t[6][9] = card('Day 2').opacity(0.8).place({ x: 40, y: -120 }) // one group
128
+ t[9][12] = card('Day 3').filter('grayscale') // the grade covers only the card
129
+ ```
130
+
131
+ `place(opts)`, `opacity`, `resize`, `filter`, `crop`, and `mask` draw the timeline as one `IRGroup`, as does a frame `filter` inside it. A group lasts for its window; a longer body is cut off. Transitions cannot be inside a group.
132
+
133
+ ## Grid areas
134
+
135
+ Same CSS-grid regions as VidScript. Declare a grid on the timeline, then place with `area:` (text centres in the cell; videos, images, and solids fill it):
136
+
137
+ ```ts
138
+ const t = timeline({ width: 1080, height: 1920, fps: 30 })
139
+ t.grid({
140
+ rows: '20% 1fr 30%',
141
+ cols: '1fr 1fr',
142
+ areas: ['title title', 'left right', 'caption caption'],
143
+ })
144
+ t[0][s(3)] = text('BIG SALE', { size: 72, area: 'title' })
145
+ t[0][s(3)] = image('./shoe.png', { area: 'left' })
146
+ ```
147
+
148
+ Without `t.grid(...)`, `area: 'title' | 'stage' | 'lower-third' | 'caption'` uses the built-in social-video preset (portrait vs landscape).
149
+
150
+ ## Transitions
151
+
152
+ Same `@scenerok/transitions` package as VidScript. Schedule a transition window between abutting clips; `toIR()` binds `fromId` / `toId`:
153
+
154
+ ```ts
155
+ import { timeline, video } from '@scenerok/sdk'
156
+ import tr from '@scenerok/transitions'
157
+
158
+ export default function main() {
159
+ const t = timeline({ width: 1080, height: 1920, fps: 30 })
160
+ t[0][5] = video('./a.mp4', { duration: 5 })
161
+ t[5][5.6] = tr.dissolve({ inOffset: 0.3, outOffset: 0.3 })
162
+ t[5.6][10] = video('./b.mp4', { duration: 4.4 })
163
+ return t.toIR()
164
+ }
165
+ ```
166
+
167
+ Common options: `direction`, `ease`, `strength`, `mode` (`roll` | `freeze`), and the handles `inOffset` / `outOffset` (source seconds read past each cut). Full catalog: `transitions-catalog.md`.
168
+
169
+ ## Output
170
+
171
+ ```ts
172
+ timeline({
173
+ width: 1920, height: 1080, fps: 23.976,
174
+ output: { path: 'reel.mp4', startTimecode: '01:00:00:00', burnIn: 'timecode' },
175
+ })
176
+ ```
177
+
178
+ ## Entry contract
179
+
180
+ Export `default async function main()` (or sync `main`) that returns either `t.toIR()`, the timeline, or an `IRTimeline` object. `async main` lets you `await loadVideo(...)`, `await getVideoLength(...)`, or `await grabFrame(...)`.
181
+
182
+ ## Use TypeScript from VidScript
183
+
184
+ A `.vid` can import a TypeScript module and place what it exports. Export values instead of (or beside) a default scene:
185
+
186
+ ```ts
187
+ // kit.ts
188
+ import { timeline, video, text, audio, solid } from '@scenerok/sdk'
189
+
190
+ export const accent = '#ff5500'
191
+ export const hero = video('beach.mp4', { duration: 12 }).trim(2, 8)
192
+ export const title = text('Konkan Day 1', { size: 72, area: 'title' })
193
+ export const bed = audio('bed.mp3', { duration: 30, volume: 0.4 })
194
+
195
+ export function lowerThird(name: string, opts: { color?: string } = {}) {
196
+ const t = timeline()
197
+ t[0][3] = solid(opts.color ?? accent, { height: 160, y: 880 })
198
+ t[0][3] = text(name, { size: 48, y: '88%' })
199
+ return t
200
+ }
201
+ ```
202
+
203
+ ```vidscript fragment
204
+ import kit from "./kit.ts"
205
+
206
+ [0s .. 6s] = kit.hero
207
+ [1s .. 4s] = kit.title
208
+ [0s .. 10s] = audio kit.bed
209
+ [3s .. 6s] = kit.lowerThird("Nidheesh", color: kit.accent)
210
+
211
+ output to "reel.mp4", resolution: "1920x1080"
212
+ ```
213
+
214
+ - `timeline()` without a size takes the importing script's output size and fps.
215
+ - Named arguments reach a function as one trailing options object; `use kit.lowerThird at 6s .. 9s with { color: "#0a8" }` calls `lowerThird({ color: "#0a8" })`.
216
+ - A default-exported scene still works: `use kit at 0s .. 0s with {}`, and `kit.duration` is its length.
217
+ - Filters, shaders, and transitions are not exportable values; use them inside an exported timeline.
218
+ - The module is one file (the CLI uploads it next to the `.vid`); top-level `await` is not supported, but an exported promise is awaited.
219
+
220
+ See `vidscript-guide.md` → Import TypeScript modules.
221
+
222
+ ## Validate / render
223
+
224
+ ```bash
225
+ scenerok validate scene.ts
226
+ scenerok render scene.ts --watch
227
+ ```
228
+
229
+ MCP:
230
+
231
+ - `scenerok_validate_vidscript` with `language: "typescript"`
232
+ - `scenerok_render_video` with `language: "typescript"`
233
+ - `scenerok_get_sdk_guide` for this document
234
+
235
+ ## AI plugins (xAI / Cloudflare / ElevenLabs)
236
+
237
+ Same packages as VidScript. Import them in TypeScript; the SceneRok compile host runs the real plugins and returns SDK `video` / `audio` values (a generated clip with sound brings its sound along; set its level with `volume`):
238
+
239
+ ```ts
240
+ import { timeline } from '@scenerok/sdk'
241
+ import xai from '@scenerok/xai'
242
+ import cf from '@scenerok/cloudflare'
243
+ import eleven from '@elevenlabs/music'
244
+
245
+ export default async function main() {
246
+ const t = timeline({ width: 1080, height: 1920, fps: 30 })
247
+ const clip = await xai.genVideo('Cinematic drone over mountains', {
248
+ duration: 5,
249
+ aspect_ratio: '9:16',
250
+ resolution: '720p',
251
+ })
252
+ const still = await cf.image('Product on seamless backdrop', { aspect_ratio: '9:16' })
253
+ const vo = await xai.tts('Welcome to SceneRok', { voice: 'eve' })
254
+ const bed = await eleven.music('Warm lo-fi bed', { duration: 12 })
255
+
256
+ t[0][clip.duration] = clip
257
+ t[clip.duration][clip.duration + still.duration] = still
258
+ t.push(vo)
259
+ t.push(bed.with({ volume: 0.3, duckUnder: vo }))
260
+ return t.toIR()
261
+ }
262
+ ```
263
+
264
+ | Package | Import | Key functions |
265
+ |---------|--------|---------------|
266
+ | `@scenerok/xai` | `import xai from '@scenerok/xai'` | `imagine`, `genVideo`, `image`, `imageToVideo`, `referenceToVideo`, `extendVideo`, `tts`, `listVoices` |
267
+ | `@scenerok/cloudflare` | `import cf from '@scenerok/cloudflare'` | `video` / `imagine`, `image`, `imageToVideo`, `extendVideo`, `listModels` |
268
+ | `@elevenlabs/music` | `import eleven from '@elevenlabs/music'` | `music`, `generateMusic`, `composeMusic` |
269
+
270
+ Requires the same env secrets as VidScript (`XAI_API_KEY`, `ELEVENLABS_API_KEY`, Cloudflare AI Gateway credentials). Generation is billed on render the same way as VidScript plugins.
271
+
272
+ ## VidScript only
273
+
274
+ - `filter "lut", file: "look.cube"` and `captions("talk.srt")` read files at compile time.
275
+ - Several `output` statements (two canvases) and `allow_offline: true`.
276
+ - `--generate-assets` (SDK scripts generate assets during `scenerok render` when plugins are called).
277
+
278
+ Import the TypeScript part into a `.vid` when a scene needs these.
279
+
280
+ ## Media stdlib
281
+
282
+ ```ts
283
+ import { loadVideo, getVideoLength, grabFrame, beats } from '@scenerok/sdk'
284
+
285
+ export default async function main() {
286
+ const clip = await loadVideo('./hero.mp4')
287
+ const d = await getVideoLength('./hero.mp4')
288
+ const thumb = await grabFrame('./hero.mp4', d / 2)
289
+ // ...
290
+ }
291
+ ```
292
+
293
+ Same functions are available in VidScript via `import media from "@scenerok/media"`.
294
+
295
+ See `media-stdlib.md` and `ai-models.md` / `tts.md` / `music.md` for prompt guidance.
296
+
297
+ ## Related
298
+
299
+ - VidScript grammar: `vidscript-guide.md`
300
+ - Intent routing: `intent-router.md`
301
+ - Package README: `packages/scenerok-sdk/README.md`
@@ -2,7 +2,7 @@
2
2
 
3
3
  ## Built-in filters
4
4
 
5
- Apply on a time block with the clip active:
5
+ A `filter` line grades everything on screen during its window. Add `target: clip` (or use `clip.Filter(…)`) to grade one clip; see `editing-actions.md` → Color and finishing for which filters can target a clip, plus LUTs and CDL.
6
6
 
7
7
  ```vidscript
8
8
  [0s .. 5s] = filter "vignette", intensity: 0.4
@@ -23,11 +23,12 @@ Apply on a time block with the clip active:
23
23
 
24
24
  Use filters as optional finishing tools — do not add by default.
25
25
 
26
- ## VFX filters (single-clip)
26
+ ## VFX filters
27
27
 
28
- Native VFX via `filter` keyword on one clip:
28
+ Native VFX through the same `filter` keyword. They work on the whole frame in their window:
29
29
 
30
30
  ```vidscript
31
+ input clip = "clip.mp4"
31
32
  [0s .. 5s] = video clip
32
33
  [0s .. 2s] = filter "liquid_glass", intensity: 0.6
33
34
  ```
@@ -52,7 +53,7 @@ import shader glow from "./shaders/glow.glsl"
52
53
 
53
54
  Also works with npm package paths under `node_modules`:
54
55
 
55
- ```vidscript
56
+ ```vidscript fragment
56
57
  import shader glow from "shader-pack/shaders/glow.glsl"
57
58
  ```
58
59
 
@@ -84,14 +85,16 @@ The compiler reads the file at compile time and embeds the GLSL into IR (`IRShad
84
85
 
85
86
  ## Scene transitions
86
87
 
87
- Scene transitions use `@scenerok/transitions` — see `transitions-catalog.md`. They blend **two** clips at window boundaries; filters operate on **one** clip.
88
+ Scene transitions use `@scenerok/transitions` — see `transitions-catalog.md`. They blend **two** clips at window boundaries; filters grade the picture.
88
89
 
89
90
  ```vidscript
90
91
  import tr from "@scenerok/transitions"
91
92
 
93
+ input clip_a = "clip-a.mp4"
94
+ input clip_b = "clip-b.mp4"
92
95
  [0s .. 5s] = video clip_a
93
96
  [5s .. 5.6s] = tr.dissolve()
94
97
  [5.6s .. 10s] = video clip_b
95
98
  ```
96
99
 
97
- Do not confuse `tr.dissolve()` (two-texture blend) with `filter "vignette"` (single-clip post).
100
+ Do not confuse `tr.dissolve()` (a blend between two clips) with `filter "vignette"` (a grade).
@@ -1,43 +1,23 @@
1
1
  # Text Styles and Motion Primitives
2
2
 
3
- `import motion from "@scenerok/basic-animations"` — use with `animate:` on VidScript `text` blocks.
4
-
5
- In **TypeScript SDK**, use the built-in motion helpers from `@scenerok/sdk` (`fadeIn`, `fadeOut`, `slideY`, `slideX`, `scale`, `popIn`) and place text with `area` / `t.grid` (see `sdk-guide.md`).
3
+ `import motion from "@scenerok/basic-animations"` — use with `animate:` on `text` blocks.
6
4
 
7
5
  ## Motion primitives
8
6
 
9
- | Primitive | Suits | Phase | SDK helper |
10
- | --- | --- | --- | --- |
11
- | `motion.fadeIn` | subtle entrance | entry | `fadeIn(s(0.4))` |
12
- | `motion.fadeOut` | ending / cut-to-black | exit | `fadeOut(s(0.4))` |
13
- | `motion.slideX` | horizontal reveal | entry | `slideX(...)` |
14
- | `motion.slideY` | vertical rise / fall | entry | `slideY(...)` |
15
- | `motion.popIn` | impact / hero reveal | entry | `popIn(...)` |
16
- | `motion.riseIn` | title build-up | entry | — (VidScript) |
17
- | `motion.swingIn` | playful entrance | entry | — (VidScript) |
18
- | `motion.glitchIn` | energetic entrance | entry | — (VidScript) |
19
- | `motion.float` | idle ambient drift | idle | — (VidScript) |
20
- | `motion.typewriter` | technical typing reveal | entry | — (VidScript) |
21
-
22
- ## Grid areas (both languages)
23
-
24
- Prefer named regions over hand-picked `x`/`y`:
25
-
26
- ```vidscript
27
- [0 .. 3] = text "BIG SALE", area: "title", animate: motion.popIn(0.8)
28
- ```
29
-
30
- ```ts
31
- import { timeline, text, s, popIn } from '@scenerok/sdk'
32
-
33
- const t = timeline({ width: 1080, height: 1920, fps: 30 })
34
- t.grid({ rows: '16% 1fr 18% 10%', areas: ['title', 'stage', 'lower-third', 'caption'] })
35
- t[0][s(3)] = text('BIG SALE', { size: 72, area: 'title' }).animate(popIn(s(0.8)))
36
- ```
37
-
38
- Built-in areas (no grid declaration): `title`, `stage`, `lower-third`, `caption`.
39
-
40
- ## Examples (VidScript)
7
+ | Primitive | Suits | Phase |
8
+ | --- | --- | --- |
9
+ | `motion.fadeIn` | subtle entrance | entry |
10
+ | `motion.fadeOut` | ending / cut-to-black | exit |
11
+ | `motion.slideX` | horizontal reveal | entry |
12
+ | `motion.slideY` | vertical rise / fall | entry |
13
+ | `motion.popIn` | impact / hero reveal | entry |
14
+ | `motion.riseIn` | title build-up | entry |
15
+ | `motion.swingIn` | playful entrance | entry |
16
+ | `motion.glitchIn` | energetic entrance | entry |
17
+ | `motion.float` | idle ambient drift | idle |
18
+ | `motion.typewriter` | technical typing reveal | entry |
19
+
20
+ ## Examples
41
21
 
42
22
  ### Bold hero headline
43
23
 
@@ -9,6 +9,8 @@ A transition window `[Ts .. Te]` binds the clip active at **Ts** (from) and **Te
9
9
  ```vidscript
10
10
  import tr from "@scenerok/transitions"
11
11
 
12
+ input clip_a = "clip-a.mp4"
13
+ input clip_b = "clip-b.mp4"
12
14
  [0s .. 5s] = video clip_a
13
15
  [5s .. 5.6s] = tr.dissolve()
14
16
  [5.6s .. 10s] = video clip_b
@@ -1,38 +1,20 @@
1
1
  # Text-to-Speech
2
2
 
3
- `import xai from "@scenerok/xai"` — voiceover via `xai.tts(...)`.
3
+ `import xai from "@scenerok/xai"` — voiceover via `audio xai.tts(...)`.
4
4
 
5
5
  Voice IDs: `eve` (upbeat), `ara` (warm), `rex` (business), `sal` (balanced), `leo` (authoritative).
6
- Catalog samples may use `Marin`, `Hudson` — check `xai.listVoices()` for current IDs.
7
-
8
- ### VidScript
9
-
10
6
  ```vidscript
11
7
  import xai from "@scenerok/xai"
12
8
  let vo = xai.tts("Welcome to SceneRok", voice: "eve")
13
9
  [0 .. vo.duration] = audio vo
14
10
  ```
15
11
 
16
- ### TypeScript SDK
17
-
18
- ```ts
19
- import { timeline, text, s } from '@scenerok/sdk'
20
- import xai from '@scenerok/xai'
21
-
22
- export default async function main() {
23
- const t = timeline({ width: 1080, height: 1920, fps: 30 })
24
- const vo = await xai.tts('Welcome to SceneRok', { voice: 'eve' })
25
- t.push(vo)
26
- t[0][vo.duration] = text('Welcome', { size: 64, area: 'title' })
27
- return t.toIR()
28
- }
29
- ```
30
-
31
- **Never guess a TTS window.** You cannot know the audio length while writing the script. VidScript: assign with `let` and use `vo.duration`, or `[-] = audio xai.tts(...)`. SDK: `await xai.tts(...)` then use `vo.duration` / `t.push(vo)`. Guessed ranges like `[0s .. 3s] = audio xai.tts("long sentence...")` clip or collide when the real audio runs longer.
12
+ **Never guess a TTS window.** You cannot know the audio length while writing the script. Either assign with `let` and use `vo.duration` in time expressions (measured from the generated file), or use `[-] = audio xai.tts(...)` blocks, which auto-size and never overlap each other. Guessed ranges like `[0s .. 3s] = audio xai.tts("long sentence...")` clip or collide when the real audio runs longer.
32
13
 
33
14
  Chain multiple lines with symbolic arithmetic:
34
15
 
35
16
  ```vidscript
17
+ import xai from "@scenerok/xai"
36
18
  let vo1 = xai.tts("First line", voice: "eve")
37
19
  let vo2 = xai.tts("Second line", voice: "eve")
38
20
  [0 .. vo1.duration] = audio vo1
@@ -40,7 +22,7 @@ let vo2 = xai.tts("Second line", voice: "eve")
40
22
  [0 .. vo1.duration] = text "First headline", area: "title" # visual spans the narration
41
23
  ```
42
24
 
43
- ## Examples (VidScript)
25
+ ## Examples
44
26
 
45
27
  Catalog samples below use fixed example windows; in real scripts prefer `vo.duration` or `[-]` blocks as shown above.
46
28