@scenerok/cli 2.2.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 +173 -0
- package/dist/commands/auth.d.ts +3 -0
- package/dist/commands/auth.d.ts.map +1 -0
- package/dist/commands/auth.js +94 -0
- package/dist/commands/cache.d.ts +3 -0
- package/dist/commands/cache.d.ts.map +1 -0
- package/dist/commands/cache.js +65 -0
- package/dist/commands/lint-video.d.ts +21 -0
- package/dist/commands/lint-video.d.ts.map +1 -0
- package/dist/commands/lint-video.js +122 -0
- package/dist/commands/project.d.ts +3 -0
- package/dist/commands/project.d.ts.map +1 -0
- package/dist/commands/project.js +173 -0
- package/dist/commands/render.d.ts +4 -0
- package/dist/commands/render.d.ts.map +1 -0
- package/dist/commands/render.js +242 -0
- package/dist/commands/skills.d.ts +3 -0
- package/dist/commands/skills.d.ts.map +1 -0
- package/dist/commands/skills.js +208 -0
- package/dist/commands/validate.d.ts +3 -0
- package/dist/commands/validate.d.ts.map +1 -0
- package/dist/commands/validate.js +254 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +51 -0
- package/dist/lib/api.d.ts +268 -0
- package/dist/lib/api.d.ts.map +1 -0
- package/dist/lib/api.js +160 -0
- package/dist/lib/config.d.ts +13 -0
- package/dist/lib/config.d.ts.map +1 -0
- package/dist/lib/config.js +53 -0
- package/dist/lib/format-api-error.d.ts +2 -0
- package/dist/lib/format-api-error.d.ts.map +1 -0
- package/dist/lib/format-api-error.js +24 -0
- package/dist/lib/language.d.ts +3 -0
- package/dist/lib/language.d.ts.map +1 -0
- package/dist/lib/language.js +9 -0
- package/dist/lib/logger.d.ts +8 -0
- package/dist/lib/logger.d.ts.map +1 -0
- package/dist/lib/logger.js +51 -0
- package/dist/lib/project.d.ts +29 -0
- package/dist/lib/project.d.ts.map +1 -0
- package/dist/lib/project.js +156 -0
- package/dist/lib/sdk-scene-modules.d.ts +7 -0
- package/dist/lib/sdk-scene-modules.d.ts.map +1 -0
- package/dist/lib/sdk-scene-modules.js +35 -0
- package/dist/utils/skillsManifest.d.ts +42 -0
- package/dist/utils/skillsManifest.d.ts.map +1 -0
- package/dist/utils/skillsManifest.js +122 -0
- package/examples/system/3-tips-fitness.vid +45 -0
- package/examples/system/ecom-product-launch.vid +31 -0
- package/examples/system/glitch-title.vid +17 -0
- package/examples/system/meme-remix.vid +15 -0
- package/examples/system/minimal-text-reel.vid +11 -0
- package/examples/system/product-launch.vid +18 -0
- package/examples/system/rokmilk-chocolate-promo.vid +25 -0
- package/examples/system/testimonial-cut.vid +16 -0
- package/examples/system/ugc-testimonial-voiceover.vid +33 -0
- package/examples/system/voice-visual-promo.vid +21 -0
- package/package.json +51 -0
- package/skills/remote/ai-models.md +65 -0
- package/skills/remote/animations-catalog.md +89 -0
- package/skills/remote/animations-picker.md +77 -0
- package/skills/remote/brand-setup.md +69 -0
- package/skills/remote/catalog-index.json +1810 -0
- package/skills/remote/environments.md +73 -0
- package/skills/remote/intent-router.md +81 -0
- package/skills/remote/media-stdlib.md +66 -0
- package/skills/remote/music.md +69 -0
- package/skills/remote/sdk-guide.md +201 -0
- package/skills/remote/shaders-filters.md +97 -0
- package/skills/remote/text-styles.md +108 -0
- package/skills/remote/transitions-catalog.md +55 -0
- package/skills/remote/tts.md +82 -0
- package/skills/remote/vidscript-guide.md +574 -0
- package/skills/remote/vidscript-sample.md +28 -0
- package/skills/remote/vidscript-strict.md +251 -0
- package/skills/remote/workflow.md +72 -0
- package/skills/shared/SKILL.md +157 -0
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
# Agent Runtime Environments
|
|
2
|
+
|
|
3
|
+
SceneRok skills are shared across several runtimes. **Detect where you are running** before choosing tools, asking questions, or fetching docs.
|
|
4
|
+
|
|
5
|
+
## Quick detection
|
|
6
|
+
|
|
7
|
+
| Signal | Environment | Primary actions |
|
|
8
|
+
| --- | --- | --- |
|
|
9
|
+
| Embedded in SceneRok web editor; `patch_vidscript` tool available; no shell | **Web editor** | Patch in-place; use editor asset library URLs |
|
|
10
|
+
| `scenerok` CLI on user's machine; file system access | **Desktop agent (CLI)** | `scenerok validate` / `render`; local asset paths |
|
|
11
|
+
| ChatGPT Actions, OAuth to SceneRok, `openaiFileIdRefs` | **ChatGPT GPT** | `validateVidscript` → `renderVideo`; import uploads first |
|
|
12
|
+
| Gemini Gem or Grok Skill; may lack HTTP tools | **Consumer chat (limited)** | Compose VidScript; user renders elsewhere if no API |
|
|
13
|
+
| `scenerok-mcp` tools (`scenerok_validate_vidscript`, `scenerok_render_video`, …) or remote `https://mcp.scenerok.com/mcp` | **MCP host** | MCP tools instead of raw CLI |
|
|
14
|
+
| Reading bundled `SKILL.md` only | **Router-only** | Fetch remote docs from index (next section) |
|
|
15
|
+
|
|
16
|
+
When unsure, ask one clarifying question: *"Are we in the SceneRok editor, your terminal, or a chat app like ChatGPT?"*
|
|
17
|
+
|
|
18
|
+
## Remote doc loading (router-only / desktop)
|
|
19
|
+
|
|
20
|
+
1. Fetch `https://scenerok.com/.well-known/agent-skills/index.json`
|
|
21
|
+
2. **Always load** `vidscript-strict.md` and `intent-router.md`
|
|
22
|
+
3. Load `environments.md` (this file) on first turn
|
|
23
|
+
4. Conditionally load catalogs per `intent-router.md`
|
|
24
|
+
5. Offline: `scenerok skills sync` then read local skill directory
|
|
25
|
+
6. Freshness: `scenerok skills check` · update: `scenerok skills update`
|
|
26
|
+
|
|
27
|
+
Base URL: `https://scenerok.com/.well-known/agent-skills/{filename}`
|
|
28
|
+
|
|
29
|
+
## Capability matrix
|
|
30
|
+
|
|
31
|
+
| Capability | Web editor | CLI / MCP | ChatGPT Action | Gemini / Grok (no API) |
|
|
32
|
+
| --- | --- | --- | --- | --- |
|
|
33
|
+
| Edit VidScript in place | `patch_vidscript` | Write `.vid` / `.ts` file | Return script to user | Return script |
|
|
34
|
+
| Validate | Editor API (VidScript) | `scenerok validate` (`.vid` or `.ts`) | `validateVidscript` (+ `language` for SDK) | Explain only |
|
|
35
|
+
| Render | Editor UI / API (VidScript) | `scenerok render` (`.vid` or `.ts`) | `renderVideo` + poll | Instruct user |
|
|
36
|
+
| Shell / ffprobe / sips | No | Yes | No | No |
|
|
37
|
+
| Browse product URL | If browser tool | If browser tool | Limited | Limited |
|
|
38
|
+
| User asset library | Yes (logged in) | Via URLs / uploads | Via `importOpenAIConversationFiles` | Ask for public URLs |
|
|
39
|
+
| Ask brand questions | Yes (chat) | Yes (terminal) | Yes (chat) | Yes (chat) |
|
|
40
|
+
| OAuth / API tokens | Session cookie | `scenerok auth login` | OAuth per user | Manual token fallback |
|
|
41
|
+
|
|
42
|
+
## Environment-specific rules
|
|
43
|
+
|
|
44
|
+
### Web editor
|
|
45
|
+
- Do **not** instruct the user to run CLI commands — the editor validates and renders in-app.
|
|
46
|
+
- Prefer `patch_vidscript` for small edits; full fenced `vidscript` blocks for rewrites.
|
|
47
|
+
- Use asset URLs from the user's SceneRok library when shown in context.
|
|
48
|
+
- Load `brand-setup.md` when colors/logo are unknown and the user is building a branded promo.
|
|
49
|
+
|
|
50
|
+
### Desktop agent (CLI / Cursor / Claude Code / Codex)
|
|
51
|
+
- Run `scenerok validate` before every render (`.vid` or `.ts`).
|
|
52
|
+
- Prefer `@scenerok/sdk` for agent-authored complex scenes; VidScript for short human edits.
|
|
53
|
+
- Probe local assets with `loadVideo` / media stdlib, or `file` / `ffprobe` / `sips` / `identify` before placement.
|
|
54
|
+
- Install router: `scenerok skills install <platform> [--sync]`
|
|
55
|
+
- Full offline bundle: `scenerok skills sync`
|
|
56
|
+
|
|
57
|
+
### ChatGPT GPT
|
|
58
|
+
- Load `consumer-chat-apps.md` and `chatgpt-gpt-instructions.md` for OAuth and file import.
|
|
59
|
+
- Never use `/mnt/data/...` paths — import via `importOpenAIConversationFiles` first.
|
|
60
|
+
- Always validate before render; poll `getRenderStatus` until complete.
|
|
61
|
+
|
|
62
|
+
### Consumer chat without API (Gemini Gem, Grok Skill)
|
|
63
|
+
- Compose and explain VidScript; tell the user to render via SceneRok web, CLI, or ChatGPT Action.
|
|
64
|
+
- Load `gemini-gem-instructions.md` or `grok-skill.md` as appropriate.
|
|
65
|
+
|
|
66
|
+
## What to ignore per environment
|
|
67
|
+
|
|
68
|
+
| Instruction in other docs | Web editor | ChatGPT |
|
|
69
|
+
| --- | --- | --- |
|
|
70
|
+
| `scenerok render ...` | Use editor render | Use `renderVideo` action |
|
|
71
|
+
| `scenerok auth login` | N/A (session) | OAuth instead |
|
|
72
|
+
| Fetch remote skill URLs | Already inlined locally | Fetch if network tool exists |
|
|
73
|
+
| Local file paths in VidScript | Use library HTTPS URLs | Use imported asset URLs |
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
# SceneRok Intent Router
|
|
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/`
|
|
6
|
+
|
|
7
|
+
## Step 0 — Detect environment
|
|
8
|
+
|
|
9
|
+
Follow `environments.md`. Web editor → patch tool, no CLI. ChatGPT → Actions + file import. Desktop → CLI validate/render.
|
|
10
|
+
|
|
11
|
+
## Disambiguation (≤2 questions)
|
|
12
|
+
|
|
13
|
+
Defaults unless the user says otherwise:
|
|
14
|
+
|
|
15
|
+
- **16:9** — YouTube, desktop, explainers
|
|
16
|
+
- **9:16** — TikTok, Reels, Stories
|
|
17
|
+
- **English** copy and VO
|
|
18
|
+
- **3–15 s** social hooks · **15–60 s** promos
|
|
19
|
+
|
|
20
|
+
| Signal | Workflow |
|
|
21
|
+
| --- | --- |
|
|
22
|
+
| Data/code/stats/maps, motion-first, <10 s | Motion graphic |
|
|
23
|
+
| Product URL + CTA + lifestyle b-roll | Product promo |
|
|
24
|
+
| Tweet / Reddit / Spotify / follow card | Social proof card |
|
|
25
|
+
| Multiple clips + cross-fades / whips | Scene montage |
|
|
26
|
+
| Voiceover explainer | Narrated explainer |
|
|
27
|
+
| PR / code walk / terminal | Code demo |
|
|
28
|
+
|
|
29
|
+
## Workflow → docs → APIs
|
|
30
|
+
|
|
31
|
+
| Workflow | Load these slugs | Primary APIs (VidScript · SDK) |
|
|
32
|
+
| --- | --- | --- |
|
|
33
|
+
| **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
|
+
| **Motion graphic** | `animations-picker.md` (+ `animations-catalog.md` if needed) | `animations.*` (VidScript HyperFrames; prefer VidScript for catalog blocks) |
|
|
35
|
+
| **Social proof card** | `animations-picker.md` | `animations.x_post`, `reddit_post`, … (VidScript) |
|
|
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
|
+
| **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
|
+
| **Code demo / PR** | `animations-picker.md` (code rows) | `code_typing`, `code_diff` (VidScript) |
|
|
39
|
+
| **Text animation / headline / stat** | `animations-picker.md` (text-anim rows) | `animations.text_*` (VidScript) · SDK kinetic: `text` + `fadeIn` / `popIn` |
|
|
40
|
+
| **Kinetic title only** | `text-styles.md` (+ `sdk-guide.md` if TS) | `text` + `motion.*` · SDK: `text(..., { area })` + `.animate(fadeIn(...))` |
|
|
41
|
+
| **Custom-authored animation (no block fits)** | `animations-catalog.md` → "Custom HTML" | `animations.custom_html` (VidScript) |
|
|
42
|
+
| **Complex / data-driven timeline** | `sdk-guide.md`, `media-stdlib.md` | `@scenerok/sdk` — `timeline`, `t.grid`, `loadVideo`, AI plugins |
|
|
43
|
+
|
|
44
|
+
## Staged routing
|
|
45
|
+
|
|
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`)
|
|
48
|
+
3. **Load catalogs** — only what that workflow needs (conditional loading)
|
|
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
|
+
5. **Brand** — if promo and missing colors/logo, one message per `brand-setup.md`
|
|
51
|
+
6. **Probe assets** — dimensions / duration (`media-stdlib.md`, `loadVideo`, or CLI `ffprobe`)
|
|
52
|
+
7. **Place text** — VidScript `area:` / `grid`; SDK `t.grid({...})` + `text(..., { area })`
|
|
53
|
+
8. **Compose** — VidScript or SDK TS + validate
|
|
54
|
+
9. **Render** — per environment (`environments.md`)
|
|
55
|
+
|
|
56
|
+
## Reuse-first
|
|
57
|
+
|
|
58
|
+
- `animations.<fn>(...)` over hand-authored HTML when a block fits
|
|
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
|
|
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
|
+
- Unverified blocks (`verified: no`) ship demo content — prefer verified families
|
|
63
|
+
- Complex procedural timelines → TypeScript `@scenerok/sdk` (`sdk-guide.md`) instead of exploding VidScript
|
|
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
|
+
## Reference
|
|
76
|
+
|
|
77
|
+
- Grammar: `vidscript-guide.md` or https://scenerok.com/.well-known/agent-skills/vidscript-guide.md
|
|
78
|
+
- SDK: `sdk-guide.md` or https://scenerok.com/.well-known/agent-skills/sdk-guide.md
|
|
79
|
+
- Media stdlib: `media-stdlib.md`
|
|
80
|
+
- Machine-readable: `catalog-index.json`
|
|
81
|
+
- Browse UI: https://scenerok.com/catalog
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
# Media standard library
|
|
2
|
+
|
|
3
|
+
Shared Pass-1 host APIs for probing media and extracting frames. Available in **both** authoring languages.
|
|
4
|
+
|
|
5
|
+
Prefer `loadVideo` / `loadAudio` from `@scenerok/sdk` when you only need duration for scheduling — they call the stdlib under the hood.
|
|
6
|
+
|
|
7
|
+
## TypeScript (`@scenerok/stdlib` or `@scenerok/sdk`)
|
|
8
|
+
|
|
9
|
+
```ts
|
|
10
|
+
import { timeline, loadVideo, text, s } from '@scenerok/sdk'
|
|
11
|
+
import { getVideoLength, grabFrame, getDimensions, getLoudness, beats } from '@scenerok/stdlib'
|
|
12
|
+
|
|
13
|
+
export default async function main() {
|
|
14
|
+
const path = './hero.mp4'
|
|
15
|
+
const clip = await loadVideo(path) // fills duration via stdlib
|
|
16
|
+
const d = await getVideoLength(path)
|
|
17
|
+
const { width, height } = await getDimensions(path)
|
|
18
|
+
const loudness = await getLoudness(path)
|
|
19
|
+
const thumb = await grabFrame(path, d / 2, { width: 720 })
|
|
20
|
+
|
|
21
|
+
const t = timeline({ width: 1080, height: 1920, fps: 30 })
|
|
22
|
+
t[0][clip.duration] = clip
|
|
23
|
+
t[0][s(2)] = text(`${width}x${height}`, { size: 40, area: 'caption' })
|
|
24
|
+
t[0][beats(120, 4)] = text(`${loudness.integratedLufs} LUFS`, { size: 32, area: 'lower-third' })
|
|
25
|
+
// thumb is a data URL — schedule with video(thumb, { duration: 2 }) if needed
|
|
26
|
+
return t.toIR()
|
|
27
|
+
}
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
## VidScript (`@scenerok/media`)
|
|
31
|
+
|
|
32
|
+
```vidscript
|
|
33
|
+
import media from "@scenerok/media"
|
|
34
|
+
|
|
35
|
+
input hero = "./hero.mp4"
|
|
36
|
+
let d = media.length(hero)
|
|
37
|
+
let loudness = media.loudness(hero)
|
|
38
|
+
let thumb = media.grabFrame(hero, d / 2)
|
|
39
|
+
|
|
40
|
+
[0s .. d] = video hero
|
|
41
|
+
[0s .. 2s] = video thumb, width: 400
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
`media.length` returns a time value usable in arithmetic and `[start .. end]` ranges.
|
|
45
|
+
`media.info(hero).duration` / `.width` work via property access on the data result.
|
|
46
|
+
`media.loudness(hero).integratedLufs` returns measured loudness data from ffmpeg (`truePeakDb`, `loudnessRange`, and `duration` are also available).
|
|
47
|
+
|
|
48
|
+
## API surface
|
|
49
|
+
|
|
50
|
+
| Function | TS | VidScript | Returns |
|
|
51
|
+
|----------|----|-----------|---------|
|
|
52
|
+
| `getVideoLength` / `length` | yes | `media.length` | seconds |
|
|
53
|
+
| `getAudioLength` | yes | `media.getAudioLength` | seconds |
|
|
54
|
+
| `getMediaInfo` / `info` | yes | `media.info` | `{ duration, width, height, fps, hasAudio, kind, ... }` |
|
|
55
|
+
| `getDimensions` / `dimensions` | yes | `media.dimensions` | `{ width, height }` |
|
|
56
|
+
| `getFps` / `fps` | yes | `media.fps` | number \| null |
|
|
57
|
+
| `hasAudio` | yes | `media.hasAudio` | boolean |
|
|
58
|
+
| `getLoudness` / `loudness` | yes | `media.loudness` | `{ integratedLufs, truePeakDb, loudnessRange, duration }` |
|
|
59
|
+
| `mediaKind` / `kind` | yes | `media.kind` | `video` \| `audio` \| `image` |
|
|
60
|
+
| `grabFrame` | yes | `media.grabFrame` | data URL (or uploaded URL) |
|
|
61
|
+
| `grabFrameAtFraction` | yes | `media.grabFrameAtFraction` | data URL |
|
|
62
|
+
| `measureText` | yes | `media.measureText` | `{ width, height, lines }` |
|
|
63
|
+
| `fitContain` / `fitCover` | yes | `media.fitContain` | `{ width, height, x, y }` |
|
|
64
|
+
| `parseTime` / `timecode` / `beats` / `clampTime` | yes | yes | time helpers |
|
|
65
|
+
|
|
66
|
+
Implementation: `src/lib/media/stdlib/`. VidScript plugin: `src/plugins/media/`.
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
# Generative Music
|
|
2
|
+
|
|
3
|
+
`import eleven from "@elevenlabs/music"`
|
|
4
|
+
|
|
5
|
+
### VidScript
|
|
6
|
+
|
|
7
|
+
```vidscript
|
|
8
|
+
import eleven from "@elevenlabs/music"
|
|
9
|
+
let bed = eleven.music("Warm premium launch bed", duration: 15, instrumental: true)
|
|
10
|
+
[0s .. 15s] = audio bed, volume: 0.35, fade_out: 2s
|
|
11
|
+
```
|
|
12
|
+
|
|
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.
|
|
31
|
+
|
|
32
|
+
## Examples (VidScript)
|
|
33
|
+
|
|
34
|
+
**Drone underscore**
|
|
35
|
+
|
|
36
|
+
```vidscript
|
|
37
|
+
import eleven from "@elevenlabs/music"
|
|
38
|
+
|
|
39
|
+
[0s .. 3s] = text "drift", font: "Inter", size: 120, color: "#6B7280", align: "center", letter_spacing: 12
|
|
40
|
+
[0s .. 3s] = audio eleven.music("dark ambient drone, minimal, cinematic tension, no percussion", duration: 3, force_instrumental: true)
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
**Padscape**
|
|
44
|
+
|
|
45
|
+
```vidscript
|
|
46
|
+
import eleven from "@elevenlabs/music"
|
|
47
|
+
|
|
48
|
+
[0s .. 3s] = text "ambient", font: "Inter", size: 140, color: "#9FB6FF", align: "center", letter_spacing: 8
|
|
49
|
+
[0s .. 3s] = audio eleven.music("ambient cinematic padscape, slow evolving, contemplative", duration: 3, force_instrumental: true)
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
**4 s sting**
|
|
53
|
+
|
|
54
|
+
```vidscript
|
|
55
|
+
import eleven from "@elevenlabs/music"
|
|
56
|
+
|
|
57
|
+
[0s .. 3s] = text "▶", font: "Inter Black", size: 200, color: "#FFD23F", align: "center"
|
|
58
|
+
[0s .. 3s] = audio eleven.music("short upbeat logo sting, claps, bright", duration: 3, force_instrumental: true)
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
**Bright brand cue**
|
|
62
|
+
|
|
63
|
+
```vidscript
|
|
64
|
+
import eleven from "@elevenlabs/music"
|
|
65
|
+
|
|
66
|
+
[0s .. 3s] = text "UPBEAT", font: "Inter Black", size: 180, color: "#FFFFFF", align: "center"
|
|
67
|
+
[0s .. 3s] = audio eleven.music("uplifting corporate brand cue, claps, bright synth, optimistic", duration: 3, force_instrumental: true)
|
|
68
|
+
```
|
|
69
|
+
|
|
@@ -0,0 +1,201 @@
|
|
|
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`. 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
|
+
|
|
14
|
+
## Schedule syntax
|
|
15
|
+
|
|
16
|
+
JavaScript has no `t[0, end]` dual key (comma operator collapses to one index). Use dual-index assignment:
|
|
17
|
+
|
|
18
|
+
```ts
|
|
19
|
+
import { timeline, loadVideo, text, audio, s, fadeIn } from '@scenerok/sdk'
|
|
20
|
+
|
|
21
|
+
export default async function main() {
|
|
22
|
+
const t = timeline({ width: 1080, height: 1920, fps: 30 })
|
|
23
|
+
const scene1 = await loadVideo('./hero.mp4')
|
|
24
|
+
|
|
25
|
+
t[0][scene1.duration] = scene1.animate(fadeIn(s(0.4)))
|
|
26
|
+
t['0s']['3s'] = text('Hello', { size: 72 })
|
|
27
|
+
t[scene1.duration][scene1.duration + s(2)] = text('Next')
|
|
28
|
+
t.push(audio('./bed.mp3', { duration: 7, volume: 0.4 }))
|
|
29
|
+
|
|
30
|
+
return t.toIR()
|
|
31
|
+
}
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
| Form | Meaning |
|
|
35
|
+
|------|---------|
|
|
36
|
+
| `t[start][end] = value` | Preferred dual-index schedule |
|
|
37
|
+
| `t.range(start, end, value)` | Explicit call (no Proxy) |
|
|
38
|
+
| `t['-'] = value` / `t.push(value)` | Auto-append at playhead |
|
|
39
|
+
|
|
40
|
+
`start` / `end` accept numbers (seconds), `s(3)` / `ms(300)`, or strings (`'3s'`, `'1:30'`). Full JS arithmetic works.
|
|
41
|
+
|
|
42
|
+
No `video` / `text` keywords after `=` — factories return typed values and the runtime infers IR kind.
|
|
43
|
+
|
|
44
|
+
Use `loadVideo(path)` / `loadAudio(path)` to probe duration through the media stdlib when `duration` is omitted:
|
|
45
|
+
|
|
46
|
+
```ts
|
|
47
|
+
const clip = await loadVideo('./hero.mp4')
|
|
48
|
+
t[0][clip.duration] = clip
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
Use the sync `video(path, { duration })` / `audio(path, { duration })` factories when you already know the timing.
|
|
52
|
+
|
|
53
|
+
## Grid areas
|
|
54
|
+
|
|
55
|
+
Same CSS-grid regions as VidScript. Declare a grid on the timeline, then place text with `area:`:
|
|
56
|
+
|
|
57
|
+
```ts
|
|
58
|
+
import { timeline, text, s } from '@scenerok/sdk'
|
|
59
|
+
|
|
60
|
+
export default function main() {
|
|
61
|
+
const t = timeline({ width: 1080, height: 1920, fps: 30 })
|
|
62
|
+
|
|
63
|
+
t.grid({
|
|
64
|
+
rows: '20% 1fr 30%',
|
|
65
|
+
cols: '1fr 1fr',
|
|
66
|
+
areas: ['title title', 'left right', 'caption caption'],
|
|
67
|
+
})
|
|
68
|
+
|
|
69
|
+
t[0][s(3)] = text('BIG SALE', { size: 72, area: 'title' })
|
|
70
|
+
t[0][s(3)] = text('Left column', { size: 40, area: 'left' })
|
|
71
|
+
|
|
72
|
+
return t.toIR()
|
|
73
|
+
}
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
Without `t.grid(...)`, `area: 'title' | 'stage' | 'lower-third' | 'caption'` uses the built-in social-video preset (portrait vs landscape). Explicit `x` / `y` / `maxWidth` / `maxHeight` override the area.
|
|
77
|
+
|
|
78
|
+
Helpers also available: `resolveGridAreas`, `defaultGridSpec`, `placeInArea`.
|
|
79
|
+
|
|
80
|
+
Same semantics as VidScript `grid rows: ..., areas: [...]` — see `vidscript-guide.md` → Grid Areas.
|
|
81
|
+
|
|
82
|
+
## Transitions
|
|
83
|
+
|
|
84
|
+
Same `@scenerok/transitions` package as VidScript. Schedule a transition window between abutting clips; `toIR()` binds `fromId` / `toId` automatically:
|
|
85
|
+
|
|
86
|
+
```ts
|
|
87
|
+
import { timeline, video } from '@scenerok/sdk'
|
|
88
|
+
import tr from '@scenerok/transitions'
|
|
89
|
+
|
|
90
|
+
export default function main() {
|
|
91
|
+
const t = timeline({ width: 1080, height: 1920, fps: 30 })
|
|
92
|
+
|
|
93
|
+
t[0][5] = video('./a.mp4', { duration: 5 })
|
|
94
|
+
t[5][5.6] = tr.dissolve()
|
|
95
|
+
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
|
+
return t.toIR()
|
|
101
|
+
}
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
Common options: `direction`, `ease`, `strength`, `mode` (`roll` | `freeze`). Full catalog: `transitions-catalog.md`.
|
|
105
|
+
|
|
106
|
+
## Entry contract
|
|
107
|
+
|
|
108
|
+
Export `default async function main()` (or sync `main`) that returns either:
|
|
109
|
+
|
|
110
|
+
- `t.toIR()`, or
|
|
111
|
+
- an `IRTimeline` object
|
|
112
|
+
|
|
113
|
+
`async main` is supported so you can `await loadVideo(...)`, `await getVideoLength(...)`, or `await grabFrame(...)`.
|
|
114
|
+
|
|
115
|
+
## Validate / render
|
|
116
|
+
|
|
117
|
+
```bash
|
|
118
|
+
scenerok validate scene.ts
|
|
119
|
+
scenerok render scene.ts --watch
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
You can also **include** an SDK scene from VidScript:
|
|
123
|
+
|
|
124
|
+
```vidscript
|
|
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
|
+
MCP:
|
|
133
|
+
|
|
134
|
+
- `scenerok_validate_vidscript` with `language: "typescript"`
|
|
135
|
+
- `scenerok_render_video` with `language: "typescript"`
|
|
136
|
+
- `scenerok_get_sdk_guide` for this document
|
|
137
|
+
|
|
138
|
+
## AI plugins (xAI / Cloudflare / ElevenLabs)
|
|
139
|
+
|
|
140
|
+
Same packages as VidScript. Import them in TypeScript; the SceneRok compile host runs the real plugins and returns SDK `video` / `audio` nodes:
|
|
141
|
+
|
|
142
|
+
```ts
|
|
143
|
+
import { timeline } from '@scenerok/sdk'
|
|
144
|
+
import xai from '@scenerok/xai'
|
|
145
|
+
import cf from '@scenerok/cloudflare'
|
|
146
|
+
import eleven from '@elevenlabs/music'
|
|
147
|
+
|
|
148
|
+
export default async function main() {
|
|
149
|
+
const t = timeline({ width: 1080, height: 1920, fps: 30 })
|
|
150
|
+
const clip = await xai.genVideo('Cinematic drone over mountains', {
|
|
151
|
+
duration: 5,
|
|
152
|
+
aspect_ratio: '9:16',
|
|
153
|
+
resolution: '720p',
|
|
154
|
+
})
|
|
155
|
+
const still = await cf.image('Product on seamless backdrop', { aspect_ratio: '9:16' })
|
|
156
|
+
const vo = await xai.tts('Welcome to SceneRok', { voice: 'eve' })
|
|
157
|
+
const bed = await eleven.music('Warm lo-fi bed', { duration: 12 })
|
|
158
|
+
|
|
159
|
+
t[0][clip.duration] = clip
|
|
160
|
+
t[clip.duration][clip.duration + still.duration] = still
|
|
161
|
+
t.push(vo)
|
|
162
|
+
t.push(bed)
|
|
163
|
+
return t.toIR()
|
|
164
|
+
}
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
| Package | Import | Key functions |
|
|
168
|
+
|---------|--------|---------------|
|
|
169
|
+
| `@scenerok/xai` | `import xai from '@scenerok/xai'` | `imagine`, `genVideo`, `image`, `imageToVideo`, `referenceToVideo`, `extendVideo`, `tts`, `listVoices` |
|
|
170
|
+
| `@scenerok/cloudflare` | `import cf from '@scenerok/cloudflare'` | `video` / `imagine`, `image`, `imageToVideo`, `extendVideo`, `listModels` |
|
|
171
|
+
| `@elevenlabs/music` | `import eleven from '@elevenlabs/music'` | `music`, `generateMusic`, `composeMusic` |
|
|
172
|
+
|
|
173
|
+
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
|
+
|
|
175
|
+
## MVP limits
|
|
176
|
+
|
|
177
|
+
- Prefer `getVideoLength(path)` from `@scenerok/stdlib` instead of hard-coding `duration:` when assets exist on disk
|
|
178
|
+
- `--generate-assets` remains VidScript-only (SDK scripts generate assets during `scenerok render` when plugins are called)
|
|
179
|
+
|
|
180
|
+
## Media stdlib
|
|
181
|
+
|
|
182
|
+
```ts
|
|
183
|
+
import { loadVideo, getVideoLength, grabFrame, beats } from '@scenerok/sdk'
|
|
184
|
+
|
|
185
|
+
export default async function main() {
|
|
186
|
+
const clip = await loadVideo('./hero.mp4')
|
|
187
|
+
const d = await getVideoLength('./hero.mp4')
|
|
188
|
+
const thumb = await grabFrame('./hero.mp4', d / 2)
|
|
189
|
+
// ...
|
|
190
|
+
}
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
Same functions are available in VidScript via `import media from "@scenerok/media"`.
|
|
194
|
+
|
|
195
|
+
See `skills/remote/media-stdlib.md` and `ai-models.md` / `tts.md` / `music.md` for prompt guidance.
|
|
196
|
+
|
|
197
|
+
## Related
|
|
198
|
+
|
|
199
|
+
- VidScript grammar: `vidscript-guide.md`
|
|
200
|
+
- Intent routing: `intent-router.md`
|
|
201
|
+
- Package README: `packages/scenerok-sdk/README.md`
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
# Shaders and Filters
|
|
2
|
+
|
|
3
|
+
## Built-in filters
|
|
4
|
+
|
|
5
|
+
Apply on a time block with the clip active:
|
|
6
|
+
|
|
7
|
+
```vidscript
|
|
8
|
+
[0s .. 5s] = filter "vignette", intensity: 0.4
|
|
9
|
+
[2s .. 4s] = filter "sepia", intensity: 0.6
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
| Filter | Typical use |
|
|
13
|
+
| --- | --- |
|
|
14
|
+
| `monochrome` | Desaturated / B&W look |
|
|
15
|
+
| `sepia` | Warm vintage tone |
|
|
16
|
+
| `blur` | Soft focus / depth |
|
|
17
|
+
| `chromatic` | Lens aberration / glitch accent |
|
|
18
|
+
| `glitch` | Digital distortion |
|
|
19
|
+
| `vignette` | Edge darkening — finishing pass |
|
|
20
|
+
| `contrast` | Punch up midtones |
|
|
21
|
+
| `saturation` | Color intensity |
|
|
22
|
+
| `brightness` | Exposure lift/drop |
|
|
23
|
+
|
|
24
|
+
Use filters as optional finishing tools — do not add by default.
|
|
25
|
+
|
|
26
|
+
## VFX filters (single-clip)
|
|
27
|
+
|
|
28
|
+
Native VFX via `filter` keyword on one clip:
|
|
29
|
+
|
|
30
|
+
```vidscript
|
|
31
|
+
[0s .. 5s] = video clip
|
|
32
|
+
[0s .. 2s] = filter "liquid_glass", intensity: 0.6
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
| Filter | Reach for it when… |
|
|
36
|
+
| --- | --- |
|
|
37
|
+
| `liquid_glass` | iOS-style glass morphism over footage |
|
|
38
|
+
| `magnetic` | UI magnet / snap aesthetic |
|
|
39
|
+
| `portal` | Sci-fi portal / warp hole |
|
|
40
|
+
| `text_cursor` | Typing / terminal cursor overlay |
|
|
41
|
+
|
|
42
|
+
## Custom shaders
|
|
43
|
+
|
|
44
|
+
Author fragment GLSL in a `.glsl` file and import it — same idea as `animations.custom_html(src:)` for HTML, but via the first-class shader import:
|
|
45
|
+
|
|
46
|
+
```vidscript
|
|
47
|
+
import shader glow from "./shaders/glow.glsl"
|
|
48
|
+
|
|
49
|
+
[0s .. 5s] = text "Hello"
|
|
50
|
+
[0s .. 5s] = shader glow, intensity: 0.8
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
Also works with npm package paths under `node_modules`:
|
|
54
|
+
|
|
55
|
+
```vidscript
|
|
56
|
+
import shader glow from "shader-pack/shaders/glow.glsl"
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
| Use | Keyword |
|
|
60
|
+
| --- | --- |
|
|
61
|
+
| Built-in / VFX from the library | `filter "name", …` |
|
|
62
|
+
| Your own `.glsl` file | `import shader` + `shader name, …` |
|
|
63
|
+
|
|
64
|
+
**Fragment contract** (EffectComposer post-process):
|
|
65
|
+
|
|
66
|
+
- Fragment only — the engine supplies a shared vertex shader with `vUv`
|
|
67
|
+
- Declare `uniform sampler2D tDiffuse` and sample it
|
|
68
|
+
- Write `gl_FragColor`
|
|
69
|
+
- VidScript params become uniforms by **exact name** (`intensity: 0.8` → `uniform float intensity`)
|
|
70
|
+
- Optional: declare `uniform float uTime` and/or `uniform vec2 uResolution` — the renderer injects and updates them automatically
|
|
71
|
+
|
|
72
|
+
```glsl
|
|
73
|
+
uniform sampler2D tDiffuse;
|
|
74
|
+
uniform float intensity;
|
|
75
|
+
varying vec2 vUv;
|
|
76
|
+
|
|
77
|
+
void main() {
|
|
78
|
+
vec4 texel = texture2D(tDiffuse, vUv);
|
|
79
|
+
gl_FragColor = texel * (1.0 + intensity);
|
|
80
|
+
}
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
The compiler reads the file at compile time and embeds the GLSL into IR (`IRShaderEffect`). Relative paths resolve against the `.vid` directory (`workingDirectory` / `assetBaseDir`).
|
|
84
|
+
|
|
85
|
+
## Scene transitions
|
|
86
|
+
|
|
87
|
+
Scene transitions use `@scenerok/transitions` — see `transitions-catalog.md`. They blend **two** clips at window boundaries; filters operate on **one** clip.
|
|
88
|
+
|
|
89
|
+
```vidscript
|
|
90
|
+
import tr from "@scenerok/transitions"
|
|
91
|
+
|
|
92
|
+
[0s .. 5s] = video clip_a
|
|
93
|
+
[5s .. 5.6s] = tr.dissolve()
|
|
94
|
+
[5.6s .. 10s] = video clip_b
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
Do not confuse `tr.dissolve()` (two-texture blend) with `filter "vignette"` (single-clip post).
|