@scenerok/sdk 0.9.0 → 0.10.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 +63 -1
- package/dist/describe.d.ts +87 -0
- package/dist/describe.d.ts.map +1 -0
- package/dist/describe.js +210 -0
- package/dist/duration.d.ts +9 -3
- package/dist/duration.d.ts.map +1 -1
- package/dist/duration.js +22 -5
- package/dist/frame-rate.d.ts +30 -0
- package/dist/frame-rate.d.ts.map +1 -0
- package/dist/frame-rate.js +73 -0
- package/dist/index.d.ts +9 -6
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +7 -4
- package/dist/ir-builder.d.ts +61 -8
- package/dist/ir-builder.d.ts.map +1 -1
- package/dist/ir-builder.js +714 -256
- package/dist/ir.d.ts +203 -2
- package/dist/ir.d.ts.map +1 -1
- package/dist/ir.js +4 -2
- package/dist/media.d.ts +49 -5
- package/dist/media.d.ts.map +1 -1
- package/dist/media.js +223 -33
- package/dist/motion-tracks.d.ts +10 -0
- package/dist/motion-tracks.d.ts.map +1 -0
- package/dist/motion-tracks.js +150 -0
- package/dist/motion.d.ts +34 -0
- package/dist/motion.d.ts.map +1 -1
- package/dist/motion.js +51 -11
- package/dist/plugins/invoke.d.ts.map +1 -1
- package/dist/plugins/invoke.js +5 -2
- package/dist/timeline.d.ts +3 -0
- package/dist/timeline.d.ts.map +1 -1
- package/dist/timeline.js +101 -41
- package/dist/types.d.ts +214 -28
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +8 -9
- package/package.json +2 -2
- package/skills/scenerok/SKILL.md +5 -2
- package/skills/scenerok/references/catalog-index.json +1 -1
- package/skills/scenerok/references/sdk-guide.md +149 -49
- package/skills/scenerok/references/vidscript-guide.md +45 -12
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
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
4
|
|
|
5
|
-
Both paths emit the same `IRTimeline
|
|
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
6
|
|
|
7
7
|
## When to choose which
|
|
8
8
|
|
|
@@ -10,6 +10,7 @@ Both paths emit the same `IRTimeline`. Preview and render (Pass 2) are unchanged
|
|
|
10
10
|
|--------|----------|
|
|
11
11
|
| Short promo, human edit, template placeholders | VidScript (`.vid`) |
|
|
12
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)) |
|
|
13
14
|
|
|
14
15
|
## Schedule syntax
|
|
15
16
|
|
|
@@ -35,53 +36,120 @@ export default async function main() {
|
|
|
35
36
|
|------|---------|
|
|
36
37
|
| `t[start][end] = value` | Preferred dual-index schedule |
|
|
37
38
|
| `t.range(start, end, value)` | Explicit call (no Proxy) |
|
|
38
|
-
| `t['-'] = value` / `t.push(value)` | Auto-append at playhead |
|
|
39
|
+
| `t['-'] = value` / `t.push(value, duration?)` | Auto-append at the playhead |
|
|
39
40
|
|
|
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`).
|
|
41
42
|
|
|
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.
|
|
43
44
|
|
|
44
|
-
Use `loadVideo(path)` / `loadAudio(path)` to probe
|
|
45
|
+
Use `loadVideo(path)` / `loadAudio(path)` to probe the length through the media stdlib, or pass `duration` when you know it:
|
|
45
46
|
|
|
46
47
|
```ts
|
|
47
48
|
const clip = await loadVideo('./hero.mp4')
|
|
48
49
|
t[0][clip.duration] = clip
|
|
50
|
+
t.push(video('./b.mp4', { duration: 4 }))
|
|
49
51
|
```
|
|
50
52
|
|
|
51
|
-
|
|
53
|
+
## Values
|
|
52
54
|
|
|
53
|
-
|
|
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) |
|
|
54
66
|
|
|
55
|
-
|
|
67
|
+
Values are immutable. Methods return a copy, so one clip can be cut many ways:
|
|
56
68
|
|
|
57
69
|
```ts
|
|
58
|
-
|
|
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
|
+
```
|
|
59
75
|
|
|
60
|
-
|
|
61
|
-
|
|
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 |
|
|
62
100
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
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
|
+
```
|
|
68
107
|
|
|
69
|
-
|
|
70
|
-
t[0][s(3)] = text('Left column', { size: 40, area: 'left' })
|
|
108
|
+
A blend needs something below it in the window; nothing below is an error, as in VidScript.
|
|
71
109
|
|
|
72
|
-
|
|
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
|
|
73
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
|
|
74
129
|
```
|
|
75
130
|
|
|
76
|
-
|
|
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.
|
|
77
132
|
|
|
78
|
-
|
|
133
|
+
## Grid areas
|
|
79
134
|
|
|
80
|
-
Same
|
|
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).
|
|
81
149
|
|
|
82
150
|
## Transitions
|
|
83
151
|
|
|
84
|
-
Same `@scenerok/transitions` package as VidScript. Schedule a transition window between abutting clips; `toIR()` binds `fromId` / `toId
|
|
152
|
+
Same `@scenerok/transitions` package as VidScript. Schedule a transition window between abutting clips; `toIR()` binds `fromId` / `toId`:
|
|
85
153
|
|
|
86
154
|
```ts
|
|
87
155
|
import { timeline, video } from '@scenerok/sdk'
|
|
@@ -89,28 +157,67 @@ import tr from '@scenerok/transitions'
|
|
|
89
157
|
|
|
90
158
|
export default function main() {
|
|
91
159
|
const t = timeline({ width: 1080, height: 1920, fps: 30 })
|
|
92
|
-
|
|
93
160
|
t[0][5] = video('./a.mp4', { duration: 5 })
|
|
94
|
-
t[5][5.6] = tr.dissolve()
|
|
161
|
+
t[5][5.6] = tr.dissolve({ inOffset: 0.3, outOffset: 0.3 })
|
|
95
162
|
t[5.6][10] = video('./b.mp4', { duration: 4.4 })
|
|
96
|
-
|
|
97
|
-
// Or auto-append with catalog default (0.6s):
|
|
98
|
-
// t.push(tr.push({ direction: 'right' }))
|
|
99
|
-
|
|
100
163
|
return t.toIR()
|
|
101
164
|
}
|
|
102
165
|
```
|
|
103
166
|
|
|
104
|
-
Common options: `direction`, `ease`, `strength`, `mode` (`roll` | `freeze`). Full catalog: `transitions-catalog.md`.
|
|
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
|
+
```
|
|
105
177
|
|
|
106
178
|
## Entry contract
|
|
107
179
|
|
|
108
|
-
Export `default async function main()` (or sync `main`) that returns either
|
|
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:
|
|
109
185
|
|
|
110
|
-
|
|
111
|
-
|
|
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"
|
|
112
205
|
|
|
113
|
-
|
|
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.
|
|
114
221
|
|
|
115
222
|
## Validate / render
|
|
116
223
|
|
|
@@ -119,16 +226,6 @@ scenerok validate scene.ts
|
|
|
119
226
|
scenerok render scene.ts --watch
|
|
120
227
|
```
|
|
121
228
|
|
|
122
|
-
You can also **include** an SDK scene from VidScript:
|
|
123
|
-
|
|
124
|
-
```vidscript fragment
|
|
125
|
-
import intro from "./intro.ts"
|
|
126
|
-
use intro at 0s .. 0s with {}
|
|
127
|
-
output to "reel.mp4"
|
|
128
|
-
```
|
|
129
|
-
|
|
130
|
-
See `vidscript-guide.md` → Include TypeScript / JavaScript scenes.
|
|
131
|
-
|
|
132
229
|
MCP:
|
|
133
230
|
|
|
134
231
|
- `scenerok_validate_vidscript` with `language: "typescript"`
|
|
@@ -137,7 +234,7 @@ MCP:
|
|
|
137
234
|
|
|
138
235
|
## AI plugins (xAI / Cloudflare / ElevenLabs)
|
|
139
236
|
|
|
140
|
-
Same packages as VidScript. Import them in TypeScript; the SceneRok compile host runs the real plugins and returns SDK `video` / `audio`
|
|
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`):
|
|
141
238
|
|
|
142
239
|
```ts
|
|
143
240
|
import { timeline } from '@scenerok/sdk'
|
|
@@ -159,7 +256,7 @@ export default async function main() {
|
|
|
159
256
|
t[0][clip.duration] = clip
|
|
160
257
|
t[clip.duration][clip.duration + still.duration] = still
|
|
161
258
|
t.push(vo)
|
|
162
|
-
t.push(bed)
|
|
259
|
+
t.push(bed.with({ volume: 0.3, duckUnder: vo }))
|
|
163
260
|
return t.toIR()
|
|
164
261
|
}
|
|
165
262
|
```
|
|
@@ -172,10 +269,13 @@ export default async function main() {
|
|
|
172
269
|
|
|
173
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.
|
|
174
271
|
|
|
175
|
-
##
|
|
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).
|
|
176
277
|
|
|
177
|
-
|
|
178
|
-
- `--generate-assets` remains VidScript-only (SDK scripts generate assets during `scenerok render` when plugins are called)
|
|
278
|
+
Import the TypeScript part into a `.vid` when a scene needs these.
|
|
179
279
|
|
|
180
280
|
## Media stdlib
|
|
181
281
|
|
|
@@ -231,26 +231,59 @@ let vo2 = xai.tts("Let's dive in", voice: "eve")
|
|
|
231
231
|
- Exported timelines expose `.duration` too: `use scene1 at 0s .. 0s with {}` then `[scene1.duration .. scene1.duration + 4] = ...`. A `use` also advances the playhead past its content, so a following `[-]` block never overlaps it.
|
|
232
232
|
- This replaces guessing: `[0s .. 5s] = audio xai.tts(...)` clips or overlaps when the real audio is longer. For simple back-to-back narration, consecutive `[-] = audio ...` blocks also never overlap.
|
|
233
233
|
|
|
234
|
-
##
|
|
234
|
+
## Import TypeScript modules
|
|
235
|
+
|
|
236
|
+
`import kit from "./kit.ts"` (or `.js`) runs the module with `@scenerok/sdk` and binds its exports as a namespace, like a `.vid` module. Each export is a VidScript value of the matching kind:
|
|
237
|
+
|
|
238
|
+
| TypeScript export | In VidScript |
|
|
239
|
+
| --- | --- |
|
|
240
|
+
| `video(…)`, `loadVideo(…)`, `imageSequence(…)` | a video value: `[0s .. 5s] = kit.hero`, `kit.hero.Trim(1s, 3s)` |
|
|
241
|
+
| `image(…)` | an image value |
|
|
242
|
+
| `text(…)` | a text value |
|
|
243
|
+
| `audio(…)`, `loadAudio(…)` | an audio value: `[0s .. 10s] = audio kit.bed` |
|
|
244
|
+
| `solid(…)`, `gap()`, `marker(…)` | the builtin values `solid()`, `gap()`, `marker()` return |
|
|
245
|
+
| `timeline()` | a timeline value: flat when placed bare, one group with properties or methods |
|
|
246
|
+
| a number, string, array, or plain object | that data: `color: kit.accent` |
|
|
247
|
+
| a function returning any of the above | a function: `kit.lowerThird("Konkan", color: "#f50")` |
|
|
248
|
+
| `export default` scene (`main()` returning `t.toIR()`) | `use kit at 0s .. 0s with {}`; `kit.duration` is its length |
|
|
235
249
|
|
|
236
|
-
|
|
250
|
+
```ts
|
|
251
|
+
// kit.ts
|
|
252
|
+
import { timeline, video, image, text, solid, fadeIn } from '@scenerok/sdk'
|
|
253
|
+
|
|
254
|
+
export const accent = '#ff5500'
|
|
255
|
+
export const hero = video('beach.mp4', { duration: 12 }).trim(2, 8)
|
|
256
|
+
export const logo = image('logo.png', { width: 200, height: 200, x: 40, y: 40 })
|
|
257
|
+
export const title = text('Konkan Day 1', { size: 72, area: 'title', animate: fadeIn(0.4) })
|
|
258
|
+
|
|
259
|
+
export function lowerThird(name: string, opts: { color?: string } = {}) {
|
|
260
|
+
const t = timeline()
|
|
261
|
+
t[0][3] = solid(opts.color ?? accent, { height: 160, y: 880 })
|
|
262
|
+
t[0][3] = text(name, { size: 48, y: '88%' })
|
|
263
|
+
return t
|
|
264
|
+
}
|
|
265
|
+
```
|
|
237
266
|
|
|
238
267
|
```vidscript fragment
|
|
239
|
-
import
|
|
240
|
-
import bump from "./bump.js"
|
|
268
|
+
import kit from "./kit.ts"
|
|
241
269
|
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
[
|
|
270
|
+
[0s .. 6s] = kit.hero
|
|
271
|
+
[0s .. 6s] = kit.logo, opacity: 0.9
|
|
272
|
+
[1s .. 4s] = kit.title
|
|
273
|
+
[3s .. 6s] = kit.lowerThird("Nidheesh", color: kit.accent), blend: "screen"
|
|
274
|
+
use kit.lowerThird at 6s .. 9s with { color: "#00aa88" }
|
|
245
275
|
|
|
246
|
-
output to "reel.mp4", resolution: "
|
|
276
|
+
output to "reel.mp4", resolution: "1920x1080"
|
|
247
277
|
```
|
|
248
278
|
|
|
249
279
|
Rules:
|
|
250
|
-
-
|
|
251
|
-
-
|
|
252
|
-
-
|
|
253
|
-
-
|
|
280
|
+
- A `timeline()` with no size takes this script's output size and fps, so it lays out on the same canvas.
|
|
281
|
+
- Exported values keep their own properties; placement properties override them (`kit.logo, opacity: 0.9`).
|
|
282
|
+
- Place an audio export as `audio kit.bed`. In a `[-]` block a bare `kit.bed` starts at the picture playhead, because the block's channel is decided before the module runs.
|
|
283
|
+
- Function calls pass positional arguments in order and named arguments as one trailing options object: `kit.lowerThird("A", color: "#f50")` calls `lowerThird("A", { color: "#f50" })`; `use kit.f at … with { … }` calls `f({ … })`. Arguments are data (strings, numbers, booleans, lists, objects); VidScript clips and timelines cannot be passed into TypeScript.
|
|
284
|
+
- An exported promise is awaited (`export const clip = loadVideo("hero.mp4")`). Top-level `await` is not supported.
|
|
285
|
+
- Filters, shaders, and transitions are not exportable values; use them inside an exported timeline, or as VidScript statements.
|
|
286
|
+
- Relative paths only (`./` / `../`). The module is one file: the CLI uploads it with the `.vid`, so it cannot import other local files.
|
|
254
287
|
|
|
255
288
|
## Instructions (inside time blocks)
|
|
256
289
|
|