@scenerok/sdk 0.6.0 → 0.8.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 CHANGED
@@ -8,6 +8,16 @@ TypeScript-first authoring SDK for SceneRok. Agents use this for **complex, data
8
8
  npm install @scenerok/sdk
9
9
  ```
10
10
 
11
+ ## Agent skill
12
+
13
+ The package includes the scenerok agent skill at `skills/scenerok/` (`SKILL.md` plus `references/`): how to write, validate, and render SceneRok scenes in TypeScript or VidScript. Copy that directory into your agent's skills folder, for example:
14
+
15
+ ```bash
16
+ cp -r node_modules/@scenerok/sdk/skills/scenerok ~/.claude/skills/
17
+ ```
18
+
19
+ or install it with the CLI: `npx @scenerok/cli skills install claude`.
20
+
11
21
  ## Quick start
12
22
 
13
23
  ```ts
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@scenerok/sdk",
3
- "version": "0.6.0",
3
+ "version": "0.8.0",
4
4
  "description": "TypeScript-first SceneRok authoring SDK — build IRTimeline for complex agent-authored scenes",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -37,9 +37,11 @@
37
37
  },
38
38
  "files": [
39
39
  "dist",
40
+ "skills",
40
41
  "README.md"
41
42
  ],
42
43
  "scripts": {
44
+ "prebuild": "node ../../scripts/copy-scenerok-skill.mjs .",
43
45
  "build": "tsc -p tsconfig.json",
44
46
  "dev": "tsc --watch",
45
47
  "test": "node --import tsx --test src/**/*.test.ts"
@@ -0,0 +1,147 @@
1
+ ---
2
+ name: scenerok
3
+ description: >-
4
+ Writes, validates, and renders SceneRok videos from VidScript (.vid) or the
5
+ TypeScript @scenerok/sdk: promos, reels, explainers, social cards, motion
6
+ graphics, and edits of existing footage (trims, speed changes, picture-in-picture,
7
+ blend modes, masks, crops, color grades, LUTs, audio mixing and ducking, captions,
8
+ timecode). Use when the user wants to make or edit a video with SceneRok, or
9
+ mentions VidScript, a .vid file, @scenerok/sdk, the scenerok CLI, or the SceneRok
10
+ MCP tools.
11
+ ---
12
+
13
+ # SceneRok
14
+
15
+ SceneRok turns a script into an MP4. Write the script in VidScript or in
16
+ TypeScript with `@scenerok/sdk`; both compile to the same timeline. Then
17
+ validate it, render it, and check the rendered file.
18
+
19
+ ## Choose the language
20
+
21
+ | | VidScript (`.vid`) | TypeScript (`@scenerok/sdk`, `.ts`) |
22
+ | --- | --- | --- |
23
+ | Use for | Promos, reels, templates, and every editing action: masks, blends, crops, speed ramps, ducking, LUTs, timecode | Timelines built by code: loops over data, computed timing across many clips |
24
+ | Start with | [vidscript-strict.md](references/vidscript-strict.md) | [sdk-guide.md](references/sdk-guide.md) |
25
+
26
+ The SDK has no masks, blends, crops, ducking, or delivery settings. When a
27
+ scene needs both, write it in VidScript and `import` the SDK scene into it.
28
+
29
+ ## Workflow
30
+
31
+ Copy this checklist into your notes and work through it:
32
+
33
+ ```text
34
+ - [ ] 1. Identify the environment (web editor, CLI, ChatGPT, MCP, chat without tools)
35
+ - [ ] 2. Classify the request; read only the references it needs (table below)
36
+ - [ ] 3. Branded promo with unknown colors or logo: ask once
37
+ - [ ] 4. Probe real assets: type, width, height, duration
38
+ - [ ] 5. Write the script
39
+ - [ ] 6. Validate. Fix every error and validate again until clean
40
+ - [ ] 7. Uses TTS or generated media: deep-validate and fix the report (CLI only)
41
+ - [ ] 8. Render
42
+ - [ ] 9. Check the rendered MP4; fix and repeat from step 6 if it reports problems (CLI only)
43
+ ```
44
+
45
+ With the CLI:
46
+
47
+ ```bash
48
+ scenerok validate script.vid # or scene.ts
49
+ scenerok validate script.vid --generate-assets --report-json # billed; VidScript only
50
+ scenerok render script.vid --watch
51
+ scenerok lint-video <render-id> --cost medium --json # billed
52
+ ```
53
+
54
+ `scenerok render script.vid --validate` renders and checks the MP4 in one step.
55
+
56
+ The deep validation generates the real speech and media (cached for the
57
+ render) and reports text that runs off-canvas or was shrunk (`offCanvas`,
58
+ `wasShrunk`), overlapping elements (`collisions`), and voice-over-voice
59
+ overlaps (`audio.overlaps`; music under voice is fine). The web editor,
60
+ ChatGPT, and MCP have their own validate and render calls but no deep
61
+ validation or MP4 check, and can't read local files:
62
+ [environments.md](references/environments.md).
63
+
64
+ ## Rules that prevent most failures
65
+
66
+ 1. **Import before calling.** `import xai from "@scenerok/xai"`, then
67
+ `xai.tts(…)`. The same for `@scenerok/cloudflare`, `@elevenlabs/music`,
68
+ `@scenerok/basic-animations`, `@scenerok/animations`,
69
+ `@scenerok/transitions`, and `@scenerok/media`.
70
+ 2. **Never guess a duration.** Bind generated media and use its length:
71
+ `let vo = xai.tts(…)` then `[0 .. vo.duration] = audio vo`.
72
+ 3. **Never guess text coordinates.** Use `area: "title"` (also `stage`,
73
+ `lower-third`, `caption`, or your own `grid`).
74
+ 4. **No text in generated images or video.** Every prompt asks for no text,
75
+ letters, logos, or watermarks; put all copy in `text` lines.
76
+ 5. **Edits return values; place the result.**
77
+ `[0s .. 4s] = clip.Trim(2s, 6s).Crop(420, 0, 1080, 1920)`. Put one video
78
+ over another with `Overlay`, `Composite`, or `blend:`.
79
+ 6. **Use only documented syntax.** Don't invent functions or parameters. A
80
+ validation error names the problem; fix that line rather than rewriting the
81
+ script.
82
+
83
+ ## References
84
+
85
+ Read the ones the task needs. Paths are relative to this file.
86
+
87
+ | Task | Read |
88
+ | --- | --- |
89
+ | Any VidScript | [vidscript-strict.md](references/vidscript-strict.md) (always) |
90
+ | Editing and compositing: trims, speed, reverse, freeze, picture-in-picture, blend modes, masks, crops, split screens, audio mixing and ducking, grading and LUTs, captions, timecode | [editing-actions.md](references/editing-actions.md) |
91
+ | Full grammar | [vidscript-guide.md](references/vidscript-guide.md) |
92
+ | A complete commented promo script | [vidscript-sample.md](references/vidscript-sample.md) |
93
+ | TypeScript SDK | [sdk-guide.md](references/sdk-guide.md) |
94
+ | Probing media, grabbing frames, beats | [media-stdlib.md](references/media-stdlib.md) |
95
+ | Generated images and video | [ai-models.md](references/ai-models.md) |
96
+ | Voiceover | [tts.md](references/tts.md) |
97
+ | Music beds | [music.md](references/music.md) |
98
+ | Animated blocks: lower thirds, code, social cards, text animations, custom HTML | [animations-picker.md](references/animations-picker.md), then [animations-catalog.md](references/animations-catalog.md) for every parameter |
99
+ | Titles animated with motion primitives | [text-styles.md](references/text-styles.md) |
100
+ | Scene transitions | [transitions-catalog.md](references/transitions-catalog.md) |
101
+ | Filters, VFX, custom shaders | [shaders-filters.md](references/shaders-filters.md) |
102
+ | A promo from a product URL | [workflow.md](references/workflow.md), [brand-setup.md](references/brand-setup.md) |
103
+ | Which workflow fits the request | [intent-router.md](references/intent-router.md) |
104
+ | Where you are running and what you can call | [environments.md](references/environments.md) |
105
+ | Every catalog item as data | [catalog-index.json](references/catalog-index.json) |
106
+
107
+ Without local files (for example, only this text was pasted into a chat),
108
+ fetch any reference from
109
+ `https://scenerok.com/.well-known/agent-skills/<filename>`. Setup guides for
110
+ chat apps are there too: `consumer-chat-apps.md`,
111
+ `chatgpt-gpt-instructions.md`, `gemini-gem-instructions.md`, `grok-skill.md`.
112
+ Previews of every catalog item: https://scenerok.com/catalog.
113
+
114
+ ## Minimal VidScript
115
+
116
+ ```vidscript
117
+ import motion from "@scenerok/basic-animations"
118
+
119
+ input hero = "https://cdn.example.com/clip.mp4"
120
+ [0s .. 4s] = hero.Trim(1s, 5s)
121
+ [0s .. 3s] = text "Hello", style: title, color: "#FFFFFF", area: "title", animate: motion.fadeIn(0.8s)
122
+ output to "video.mp4", resolution: "1080x1920", fps: 30
123
+ ```
124
+
125
+ ## Minimal SDK scene
126
+
127
+ ```ts
128
+ import { timeline, text, s, fadeIn } from '@scenerok/sdk'
129
+
130
+ export default function main() {
131
+ const t = timeline({ width: 1080, height: 1920, fps: 30 })
132
+ t.grid({
133
+ rows: '16% 1fr 18% 10%',
134
+ areas: ['title', 'stage', 'lower-third', 'caption'],
135
+ })
136
+ t[0][s(3)] = text('SceneRok', { size: 72, area: 'title' }).animate(fadeIn(s(0.4)))
137
+ t[3][5] = text('Text to video', { size: 40, area: 'lower-third' })
138
+ return t.toIR()
139
+ }
140
+ ```
141
+
142
+ Validate and render it like a `.vid`: `scenerok validate scene.ts`.
143
+
144
+ ## Keeping the skill current
145
+
146
+ `scenerok skills check` compares the installed copy with scenerok.com;
147
+ `scenerok skills update` downloads what changed.
@@ -0,0 +1,64 @@
1
+ # AI Models — Video and Image Generation
2
+
3
+ ## Modality picker
4
+
5
+ | Intent | API | When |
6
+ | --- | --- | --- |
7
+ | Clean still / key visual | `xai.image(...)` · `cf.image(...)` | Product shot, poster frame |
8
+ | Text-to-video | `xai.imagine(...)` · `xai.genVideo(...)` · `cf.video(...)` | B-roll, lifestyle, cinematic |
9
+ | Animate one image | `xai.imageToVideo(img, prompt, ...)` · `cf.imageToVideo(...)` | Product photo → motion |
10
+ | Multi-reference scene | `xai.referenceToVideo([imgs], prompt, ...)` | Brand elements guide scene |
11
+ | Extend existing clip | `xai.extendVideo(...)` · `cf.extendVideo(...)` | Continue generated clip |
12
+
13
+ **Always import:** `import xai from "@scenerok/xai"` and/or `import cf from "@scenerok/cloudflare"`.
14
+
15
+ Generative prompts must require: no text, no words, no letters, no captions, no logos, no watermarks, no readable UI copy.
16
+
17
+ ## Examples
18
+
19
+ ### Grok genVideo()
20
+
21
+ Explicit duration, resolution, and aspect ratio for text-to-video.
22
+
23
+ **Landscape 8 s**
24
+
25
+ ```vidscript
26
+ import xai from "@scenerok/xai"
27
+
28
+ [- 3s] = video xai.genVideo("Timelapse of clouds over desert mesas, wide cinematic, anamorphic flare", duration: 3, aspect_ratio: "16:9", resolution: "720p")
29
+
30
+ [prev .. prev + 0.8s] = text "genVideo()", font: "Inter Bold", size: 44, color: "#FFFFFF", shadow_blur: 10, align: "center", y: "88%"
31
+ ```
32
+
33
+ **Vertical 6 s reel**
34
+
35
+ ```vidscript
36
+ import xai from "@scenerok/xai"
37
+
38
+ [- 3s] = video xai.genVideo("Drone shot over a futuristic city at sunset, lens flare, cinematic", duration: 3, aspect_ratio: "9:16", resolution: "720p")
39
+ ```
40
+
41
+ ### Grok imagine()
42
+
43
+ One-call text-to-video. Default 5 s, 720p, 9:16.
44
+
45
+ **Ocean drone**
46
+
47
+ ```vidscript
48
+ import xai from "@scenerok/xai"
49
+
50
+ [- 3s] = video xai.imagine("Slow aerial drone over turquoise ocean waves, golden hour, cinematic", aspect_ratio: "16:9", resolution: "720p")
51
+
52
+ [prev .. prev + 0.8s] = text "imagine()", font: "Inter Bold", size: 48, color: "#FFFFFF", align: "center", y: "85%"
53
+ ```
54
+
55
+ **Street market**
56
+
57
+ ```vidscript
58
+ import xai from "@scenerok/xai"
59
+
60
+ [- 3s] = video xai.imagine("A neon-lit street market at night, cinematic, volumetric light", aspect_ratio: "9:16", resolution: "720p")
61
+
62
+ [prev .. prev + 0.8s] = text "Grok imagine()", font: "Inter Bold", size: 56, color: "#FFFFFF", shadow_color: "#000000", shadow_blur: 12, position: "bottom", align: "center"
63
+ ```
64
+
@@ -0,0 +1,226 @@
1
+ # Animations Catalog
2
+
3
+ All `animations.<fn>(...)` blocks from `@scenerok/animations`. Total: **77** entries.
4
+
5
+ ```vidscript
6
+ import animations from "@scenerok/animations"
7
+ ```
8
+
9
+ | fn | family | verified | defaultDuration | params |
10
+ | --- | --- | --- | --- | --- |
11
+ | `app_showcase` | other | yes | 6s | positional: `imageUrl` · named: `imageUrl, userName, userRole, tagline, accentColor` |
12
+ | `apple_money_count` | other | yes | 5s | positional: `amount` · named: `amount, currency, accentColor` |
13
+ | `blend_caption` | lower-third | yes | 5s | positional: `text` · named: `text, color` |
14
+ | `blue_sweater_intro_video` | other | yes | 12s | positional: `creatorName` · named: `headline, creatorName, creatorHandle, creatorBio, avatarUrl` |
15
+ | `code_3d_extrude` | code-anim | yes | 8s | positional: `code` · named: `code, lang, theme, filename` |
16
+ | `code_diff` | code-anim | yes | 6s | positional: `from, to` · named: `from, to, lang, theme` |
17
+ | `code_highlight` | code-anim | yes | 5s | positional: `code` · named: `code, lang, theme, filename` |
18
+ | `code_morph` | code-anim | yes | 7s | positional: `from, to` · named: `from, to, lang, theme` |
19
+ | `code_particle_assemble` | code-anim | yes | 8s | positional: `code` · named: `code, lang, theme, filename` |
20
+ | `code_scroll` | code-anim | yes | 6s | positional: `code` · named: `code, lang, theme, filename` |
21
+ | `code_shader_dissolve` | code-anim | yes | 7s | positional: `code` · named: `code, lang, theme, filename` |
22
+ | `code_snippet_apple_terminal_basic` | code-snippet | yes | 12s | positional: `command` · named: `command, output, prompt, title` |
23
+ | `code_snippet_apple_terminal_clear_dark` | code-snippet | yes | 12s | positional: `command` · named: `command, output, prompt, title` |
24
+ | `code_snippet_apple_terminal_clear_light` | code-snippet | yes | 12s | positional: `command` · named: `command, output, prompt, title` |
25
+ | `code_snippet_apple_terminal_grass` | code-snippet | yes | 12s | positional: `command` · named: `command, output, prompt, title` |
26
+ | `code_snippet_apple_terminal_homebrew` | code-snippet | yes | 12s | positional: `command` · named: `command, output, prompt, title` |
27
+ | `code_snippet_apple_terminal_man_page` | code-snippet | yes | 12s | positional: `command` · named: `command, output, prompt, title` |
28
+ | `code_snippet_apple_terminal_novel` | code-snippet | yes | 12s | positional: `command` · named: `command, output, prompt, title` |
29
+ | `code_snippet_apple_terminal_ocean` | code-snippet | yes | 12s | positional: `command` · named: `command, output, prompt, title` |
30
+ | `code_snippet_apple_terminal_pro` | code-snippet | yes | 12s | positional: `command` · named: `command, output, prompt, title` |
31
+ | `code_snippet_apple_terminal_red_sands` | code-snippet | yes | 12s | positional: `command` · named: `command, output, prompt, title` |
32
+ | `code_snippet_apple_terminal_silver_aerogel` | code-snippet | yes | 12s | positional: `command` · named: `command, output, prompt, title` |
33
+ | `code_snippet_apple_terminal_solid_colors` | code-snippet | yes | 12s | positional: `command` · named: `command, output, prompt, title` |
34
+ | `code_snippet_dark_2026` | code-snippet | yes | 11s | positional: `code` · named: `code, filename, lang` |
35
+ | `code_snippet_dark_modern` | code-snippet | yes | 11s | positional: `code` · named: `code, filename, lang` |
36
+ | `code_snippet_dark_plus` | code-snippet | yes | 11s | positional: `code` · named: `code, filename, lang` |
37
+ | `code_snippet_flight` | code-snippet | yes | 6s | positional: `code` · named: `code, lang, theme, filename` |
38
+ | `code_snippet_high_contrast` | code-snippet | yes | 11s | positional: `code` · named: `code, filename, lang` |
39
+ | `code_snippet_high_contrast_light` | code-snippet | yes | 11s | positional: `code` · named: `code, filename, lang` |
40
+ | `code_snippet_light_2026` | code-snippet | yes | 11s | positional: `code` · named: `code, filename, lang` |
41
+ | `code_snippet_light_modern` | code-snippet | yes | 11s | positional: `code` · named: `code, filename, lang` |
42
+ | `code_snippet_light_plus` | code-snippet | yes | 11s | positional: `code` · named: `code, filename, lang` |
43
+ | `code_snippet_monokai` | code-snippet | yes | 11s | positional: `code` · named: `code, filename, lang` |
44
+ | `code_snippet_solarized_light` | code-snippet | yes | 11s | positional: `code` · named: `code, filename, lang` |
45
+ | `code_snippet_visual_studio_dark` | code-snippet | yes | 11s | positional: `code` · named: `code, filename, lang` |
46
+ | `code_snippet_visual_studio_light` | code-snippet | yes | 11s | positional: `code` · named: `code, filename, lang` |
47
+ | `code_typing` | code-anim | yes | 5s | positional: `code` · named: `code, lang, theme, filename` |
48
+ | `instagram_follow` | social-card | yes | 5s | positional: `displayName, handle` · named: `displayName, handle, followers, avatarUrl, url` |
49
+ | `ios26_liquid_glass` | other | yes | 15s | positional: `(none)` · named: `(none)` |
50
+ | `liquid_glass_context_menu` | other | yes | 8s | positional: `fileName` · named: `fileName, fileMeta` |
51
+ | `liquid_glass_media_controls` | other | yes | 8s | positional: `trackTitle` · named: `trackTitle, artist` |
52
+ | `liquid_glass_notification` | other | yes | 8s | positional: `title` · named: `title, body, messageText` |
53
+ | `liquid_glass_widgets` | other | yes | 8s | positional: `showcaseTitle` · named: `showcaseTitle, showcaseDescription` |
54
+ | `logo_outro` | brand | yes | 6s | positional: `tagline` · named: `tagline, url, logoUrl, brand, accent` |
55
+ | `lower_third` | lower-third | yes | 5s | positional: `title, subtitle` · named: `title, subtitle` |
56
+ | `lower_third_bild` | lower-third | yes | 8s | positional: `title, subtitle` · named: `title, subtitle, accent, bg` |
57
+ | `lt_accent_underline` | lower-third | yes | 5s | positional: `title, subtitle` · named: `title, subtitle, accent, bg` |
58
+ | `lt_bold_block` | lower-third | yes | 5s | positional: `title, subtitle` · named: `title, subtitle, accent, bg` |
59
+ | `lt_clean_bar` | lower-third | yes | 5s | positional: `title, subtitle` · named: `title, subtitle, accent, bg` |
60
+ | `lt_color_block` | lower-third | yes | 5s | positional: `title, subtitle` · named: `title, subtitle, accent, bg` |
61
+ | `lt_dark_card` | lower-third | yes | 5s | positional: `title, subtitle` · named: `title, subtitle, accent, bg` |
62
+ | `lt_kicker_name` | lower-third | yes | 5s | positional: `kicker, title` · named: `kicker, title, accent` |
63
+ | `lt_mask_reveal` | lower-third | yes | 5s | positional: `title, subtitle` · named: `title, subtitle, accent, bg` |
64
+ | `lt_side_rule` | lower-third | yes | 5s | positional: `title, subtitle` · named: `title, subtitle, accent, bg` |
65
+ | `lt_soft_pill` | lower-third | yes | 5s | positional: `title, subtitle` · named: `title, subtitle, accent, bg` |
66
+ | `lt_stack_bars` | lower-third | yes | 5s | positional: `title, subtitle` · named: `title, subtitle, accent, bg` |
67
+ | `macos_notification` | social-card | yes | 5s | positional: `appName, title` · named: `appName, timeLabel, title, body` |
68
+ | `macos_tahoe_liquid_glass` | other | yes | 15s | positional: `windowTitle` · named: `wallpaperUrl, windowTitle, toastBody, weatherLocation` |
69
+ | `news_ticker` | social-card | yes | 7s | positional: `headline` · named: `desk, time, label, headline, headlineAccent, ticker` |
70
+ | `reddit_post` | social-card | yes | 5s | positional: `subreddit, title` · named: `subreddit, author, title, body, votes` |
71
+ | `spotify_card` | social-card | yes | 5s | positional: `track, artist` · named: `track, artist` |
72
+ | `text_counter_stat` | text-anim | yes | 5s | positional: `value, label` · named: `value, label, prefix, suffix, color, accent` |
73
+ | `text_glitch_title` | text-anim | yes | 5s | positional: `text` · named: `text, color` |
74
+ | `text_gradient_sweep` | text-anim | yes | 5s | positional: `text` · named: `text, from, to` |
75
+ | `text_kinetic_words` | text-anim | yes | 5s | positional: `text` · named: `text, color, accent` |
76
+ | `text_line_stack` | text-anim | yes | 5s | positional: `line1, line2` · named: `line1, line2, line3, color, accent` |
77
+ | `text_neon_flicker` | text-anim | yes | 5s | positional: `text` · named: `text, color` |
78
+ | `text_scramble_decode` | text-anim | yes | 5s | positional: `text` · named: `text, color, accent` |
79
+ | `text_typewriter_quote` | text-anim | yes | 5s | positional: `text, author` · named: `text, author, color, accent` |
80
+ | `text_word_cascade` | text-anim | yes | 5s | positional: `text` · named: `text, color, accent` |
81
+ | `tiktok_follow` | social-card | yes | 5s | positional: `displayName, handle` · named: `displayName, handle, followers, avatarUrl, url` |
82
+ | `ui_3d_reveal` | other | yes | 13s | positional: `heroTitle` · named: `heroTitle, outroUrl` |
83
+ | `vfx_iphone_device` | other | yes | 15s | positional: `imageUrl` · named: `imageUrl` |
84
+ | `vpn_youtube_spot` | other | yes | 7s | positional: `headlineLine1` · named: `headlineLine1, headlineLine2, subtext, appTitle, appSubtitle` |
85
+ | `x_post` | social-card | yes | 5s | positional: `displayName, handle` · named: `displayName, handle, body, hashtag, timestamp` |
86
+ | `yt_lower_third` | lower-third | yes | 5s | positional: `channel, subscribers` · named: `channel, subscribers, avatarUrl` |
87
+
88
+ ## Unverified blocks
89
+
90
+ Blocks with `verified: no` vendor upstream demo content and may ignore user params. `scenerok validate` warns but does not error.
91
+
92
+ ## Text animation blocks (`text-anim` family)
93
+
94
+ Hand-authored, fully parameterized text/typography animations — staggered word reveals,
95
+ scramble decode, kinetic word-per-beat typography, gradient sweeps, typed quotes, masked
96
+ line stacks, count-up stats, RGB-split glitch, and neon flicker. All are transparent
97
+ overlays (composite over video/scenes), honor every param, and scale to any output size.
98
+ Prefer these over hand-authoring when a headline/caption/stat treatment fits; see the
99
+ `text-anim` rows in `animations-picker.md` for intent mapping.
100
+
101
+ ## Custom HTML (bring your own animation)
102
+
103
+ `animations.custom_html(...)` renders **your own HTML/CSS/JS** through the same HyperFrames
104
+ pipeline as the catalog blocks. Use it when no catalog block fits and you want to hand-author
105
+ an animation (or an agent generates one) using standard web tech.
106
+
107
+ ```vidscript
108
+ import animations from "@scenerok/animations"
109
+
110
+ # Each call takes exactly ONE source:
111
+ [0s .. 5s] = animations.custom_html(html: "<!doctype html>...") # inline HTML
112
+ [5s .. 10s] = animations.custom_html(src: "https://cdn.example.com/intro.html") # remote URL
113
+ [10s .. 15s] = animations.custom_html(src: "./anim/hero.html") # local file
114
+ [15s .. 20s] = animations.custom_html(src: "scenerok://asset/123") # uploaded asset
115
+
116
+ # Sizing, timing, and author params. designWidth/designHeight: native authoring size
117
+ # (default = output size). timelineId: default "custom-html". Any other named arg
118
+ # (title, accent) is forwarded to window.__BLOCK.
119
+ [20s .. 24s] = animations.custom_html(src: "scenerok://asset/123", designWidth: 1920, designHeight: 1080, timelineId: "my-anim", title: "Q3 Launch", accent: "#3b82f6")
120
+ ```
121
+
122
+ Reserved named args (consumed by custom_html, NOT forwarded to `__BLOCK`): `src, html, mode,
123
+ designWidth, designHeight, width, height, fit, duration, timelineId, zBand, x, y, opacity,
124
+ responsive`. Every other named arg is forwarded to `window.__BLOCK[name]` (values become strings).
125
+
126
+ ### Author contract (required)
127
+
128
+ Custom HTML is mounted in a **sandboxed cross-origin iframe** and driven one frame at a time.
129
+ Your HTML MUST:
130
+
131
+ 1. Render on a **transparent** background, sized to its declared design dimensions
132
+ (e.g. `html,body { width:1920px; height:1080px; background:transparent; }`).
133
+ 2. Register a **paused** GSAP timeline at `window.__timelines['<timelineId>']` (default
134
+ `custom-html`). The host calls `tl.seek(t, false)` per frame.
135
+ 3. Treat `window.__HF_DURATION` (seconds) as "stay PAUSED — I am in the seek-driven host".
136
+ Auto-play only when it is `undefined`: `if (typeof window.__HF_DURATION === "undefined") tl.play();`.
137
+ 4. Read author params from `window.__BLOCK` (e.g. `window.__BLOCK.title`).
138
+ 5. Load libraries from the pinned allowlist below (or inline their source). GSAP + Three.js
139
+ are allowlisted; anything else must be pasted inline in a `<script>` tag.
140
+
141
+ ### Allowlisted libraries (loadable via `<script src>` / `<link>`)
142
+
143
+ | Library | Pinned URL |
144
+ | --- | --- |
145
+ | GSAP | `https://cdn.jsdelivr.net/npm/gsap@3.14.2/dist/gsap.min.js` |
146
+ | Three.js (jsdelivr) | `https://cdn.jsdelivr.net/npm/three@0.147.0/build/three.min.js` (+ `examples/js/…`) |
147
+ | Three.js (cdnjs) | `https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js` |
148
+ | Google Fonts | `https://fonts.googleapis.com/css2?family=…` (files from `fonts.gstatic.com`) |
149
+
150
+ Any **seekable** library works if you register it at `window.__timelines[id]` with a
151
+ `.seek(seconds, false)` method. anime.js needs a unit adapter: `window.__timelines={x:{seek:t=>anim.seek(t*1000)}}`.
152
+ Purely rAF-loop libraries with no time-scrubbing will NOT render frame-by-frame.
153
+
154
+ ### Minimal working template (GSAP)
155
+
156
+ Copy-paste starting point that satisfies the whole contract — agents authoring custom
157
+ animations should start from this skeleton and only change the markup, styles, and tweens:
158
+
159
+ ```html
160
+ <!doctype html>
161
+ <html lang="en">
162
+ <head>
163
+ <meta charset="UTF-8" />
164
+ <meta name="viewport" content="width=1920, height=1080" />
165
+ <script src="https://cdn.jsdelivr.net/npm/gsap@3.14.2/dist/gsap.min.js"></script>
166
+ <style>
167
+ * { margin: 0; padding: 0; box-sizing: border-box; }
168
+ html, body { width: 1920px; height: 1080px; overflow: hidden; background: transparent; }
169
+ #hero { position: absolute; inset: 0; display: flex; align-items: center;
170
+ justify-content: center; font: 900 160px system-ui; color: #fff; }
171
+ </style>
172
+ </head>
173
+ <body>
174
+ <div id="hero"></div>
175
+ <script>
176
+ (function () {
177
+ // 1. Params arrive via window.__BLOCK (every extra named arg, stringified).
178
+ var B = window.__BLOCK || {};
179
+ document.getElementById('hero').textContent = B.title || 'Hello';
180
+
181
+ // 2. Build a PAUSED timeline; the host drives it with tl.seek(t, false) per frame.
182
+ window.__timelines = window.__timelines || {};
183
+ var tl = gsap.timeline({ paused: true });
184
+ tl.from('#hero', { opacity: 0, y: 80, duration: 0.8, ease: 'power3.out' }, 0);
185
+ tl.to('#hero', { letterSpacing: '12px', duration: 4, ease: 'none' }, 0.4);
186
+
187
+ // Optional: window.__HF_DURATION (seconds) = the mounted [start .. end] window,
188
+ // injected before this script runs — use it to pace beats across the full window.
189
+
190
+ window.__timelines['custom-html'] = tl; // key must match timelineId (default 'custom-html')
191
+
192
+ // 3. Standalone preview only — inside the host __HF_DURATION exists, stay paused.
193
+ if (typeof window.__HF_DURATION === 'undefined') tl.play();
194
+ })();
195
+ </script>
196
+ </body>
197
+ </html>
198
+ ```
199
+
200
+ Gotchas: CSS `animation`/`transition` run on the wall clock and are NOT seek-driven — frames
201
+ render wrong or frozen; drive every visual change through the registered timeline (use finite
202
+ `repeat:` counts for loops, never `repeat: -1` — an infinite timeline breaks seeking). Avoid
203
+ `Math.random()` per frame unless keyed off seek time — the same `t` must always draw the same
204
+ frame (renders can revisit frames out of order).
205
+
206
+ ### Security constraints
207
+
208
+ - Cross-origin sandboxed: author JS cannot reach the parent page, other surfaces, or the canvas.
209
+ - Strict CSP: `connect-src 'none'` — no `fetch`/`XHR`/`WebSocket` at runtime. Pass data via
210
+ `__BLOCK` params. Images from `data:`/`https:`. External `<script src>` blocked except the
211
+ allowlist above (inline other libraries). DRACO-compressed glTF unsupported (decoder fetch blocked).
212
+ - `mode: "inline"` is not supported (cannot be sandboxed) — it is a compile error. Blend/backdrop
213
+ overlays stay catalog-only.
214
+ - Max HTML size 512 KB.
215
+
216
+ ## Usage
217
+
218
+ ```vidscript
219
+ import animations from "@scenerok/animations"
220
+
221
+ [0s .. 6s] = animations.code_typing("const x = 1;")
222
+ [0s .. 5s] = animations.text_word_cascade("Make videos from plain text", accent: "#60a5fa")
223
+ [5s .. 9s] = animations.text_counter_stat("12500", "Downloads", suffix: "+")
224
+ ```
225
+
226
+ See `animations-picker.md` for intent-based selection. Preview samples: https://scenerok.com/catalog#animations
@@ -0,0 +1,88 @@
1
+ # Animation Block Picker
2
+
3
+ `import animations from "@scenerok/animations"` surfaces the vendored HyperFrames block catalog as positionable, time-windowed VidScript surfaces. Each `animations.<fn>(...)` call resolves to a `hyperframes` IR surface; no LLM runs at render time.
4
+
5
+ **Reuse-first:** prefer `animations.<fn>(...)` over hand-authoring composition HTML. Full per-fn param shapes: `animations-catalog.md`.
6
+
7
+ ## Intent → `animations.<fn>(...)`
8
+
9
+ | Intent | Family | `animations.<fn>(…)` | Positional args (in order) |
10
+ | --- | --- | --- | --- |
11
+ | typing / live code walk | code-anim | `code_typing` | `code`, `lang`, `theme`, `filename` |
12
+ | scroll through a file | code-anim | `code_scroll` | `code`, `lang`, `theme` |
13
+ | spotlight a line | code-anim | `code_highlight` | `code`, `lang`, `theme` |
14
+ | 3D extrude / shader dissolve / particle title | code-anim | `code_3d_extrude` · `code_shader_dissolve` · `code_particle_assemble` | `code`, `theme` |
15
+ | assemble snippets in flight | code-anim | `code_snippet_flight` | `code`, `theme` |
16
+ | before/after code change | code-anim | `code_morph` · `code_diff` | `from`, `to`, `lang`, `theme` |
17
+ | themed VS Code window | code-snippet | 24 `code_snippet_*` themes | `code`, `filename` |
18
+ | Apple Terminal command + output | code-snippet | 12 `code_snippet_apple_terminal_*` | `command`, `output`, `prompt`, `title` |
19
+ | broadcast name / title bar | lower-third | `lt_clean_bar` · `lt_accent_underline` · `lt_kicker_name` · `lt_mask_reveal` · `lt_stack_bars` | `title`, `subtitle`, `accent?` |
20
+ | lower-third with kicker | lower-third | `lt_kicker_name` · `lt_side_rule` | `kicker`, `title`, `accent?` |
21
+ | YouTube channel card | lower-third | `yt_lower_third` | `channel`, `subscribers`, `avatarUrl?` |
22
+ | Bild-style brand accent | lower-third | `lower_third_bild` | `title`, `subtitle`, `accent?` |
23
+ | inline text over footage | text-overlay | `lower_third` · `blend_caption` | inline `{{title}}` / `{{subtitle}}` tokens |
24
+ | X / Twitter post | social-card | `x_post` | `displayName`, `handle`, `body?`, `hashtag?`, `timestamp?` |
25
+ | Reddit thread | social-card | `reddit_post` | `subreddit`, `title`, `body?`, `author?`, `votes?` |
26
+ | TikTok / Instagram follow | social-card | `tiktok_follow` · `instagram_follow` | `displayName`, `handle`, `avatarUrl?`, `url?` |
27
+ | Spotify now-playing | social-card | `spotify_card` | `track`, `artist` |
28
+ | macOS notification | social-card | `macos_notification` | `appName`, `title`, `body?`, `timeLabel?` |
29
+ | news ticker / headline crawl | social-card | `news_ticker` | `headline`, `headlineAccent?`, `ticker?`, `desk?`, `time?` |
30
+ | logo outro / brand sting | brand | `logo_outro` | `tagline`, `url?`, `brand?` (`"scenerok"` preset) or `logoUrl?`, `accent?` |
31
+ | staggered word-reveal headline | text-anim | `text_word_cascade` | `text`, `color?`, `accent?` (accent tints last word) |
32
+ | matrix-style decode / decrypt title | text-anim | `text_scramble_decode` | `text`, `color?`, `accent?` |
33
+ | word-per-beat kinetic typography | text-anim | `text_kinetic_words` | `text` (space-separated words), `color?`, `accent?` |
34
+ | animated gradient headline | text-anim | `text_gradient_sweep` | `text`, `from?`, `to?` (gradient colors) |
35
+ | typed quote with attribution | text-anim | `text_typewriter_quote` | `text`, `author`, `color?`, `accent?` |
36
+ | 2–3 line masked title stack | text-anim | `text_line_stack` | `line1`, `line2`, `line3?`, `accent?` |
37
+ | count-up stat / metric | text-anim | `text_counter_stat` | `value`, `label`, `prefix?`, `suffix?`, `accent?` |
38
+ | RGB-split glitch title | text-anim | `text_glitch_title` | `text`, `color?` |
39
+ | neon sign flicker-on | text-anim | `text_neon_flicker` | `text`, `color?` (glow color) |
40
+ | punchy kinetic title (no block) | kinetic-type | (use `text` + `motion.*` — see `text-styles.md`) | — |
41
+ | no catalog block fits / author your own animation (HTML+CSS with GSAP, Three.js, or any seekable JS lib) | custom | `custom_html` | `src` or `html` — author contract, allowlisted libraries, and a minimal GSAP template in `animations-catalog.md` → "Custom HTML" |
42
+
43
+ Param resolution: **named > positional > default**.
44
+
45
+ ## Gotchas
46
+
47
+ | Block / family | Issue | Fix |
48
+ | --- | --- | --- |
49
+ | `code_diff` | Line indices may be 0-based in adapter | Match sample in `animations-catalog.md` |
50
+ | Unverified blocks | `scenerok validate` warns `verified:false` | Accept demo render or switch to verified family |
51
+ | Maps / liquid-glass / app-showcase | Ignore user params | Use for demo aesthetic only, or hand-author |
52
+ | Adapter blocks | No inline audio/text siblings | Put `audio`, `text`, `filter` on separate time blocks |
53
+ | `logo_outro` | Renders upstream demo's Figma-style mark when no logo param given | Pass `brand: "scenerok"` or `logoUrl: "<image url>"` |
54
+
55
+ ## Two-phase compose recipe
56
+
57
+ **Part 1 — classify (before probing assets):**
58
+
59
+ 1. Decide intent family from the table above.
60
+ 2. Pick `animations.<fn>` (or `text` + `motion.*` for kinetic-type).
61
+ 3. Note param values from the user request.
62
+ 4. Draft time blocks: `[<start>s .. <end>s] = animations.<fn>(...)`.
63
+
64
+ **Part 2 — compose (after probing assets):**
65
+
66
+ 1. Probe assets (logo, avatar, code) — never guess dimensions.
67
+ 2. Resolve params: named override positional; defaults fill the rest.
68
+ 3. Emit VidScript call.
69
+ 4. Adjacent `audio`, `text`, `filter` on **separate** time blocks.
70
+ 5. Run `scenerok validate script.vid`.
71
+
72
+ ## Demo script
73
+
74
+ ```vidscript
75
+ import animations from "@scenerok/animations"
76
+ import tr from "@scenerok/transitions"
77
+ output to "demo.mp4", resolution: "1920x1080", fps: 30
78
+
79
+ [0s .. 6s] = animations.code_typing("let abc = 23; abc += 10; console.log(abc);")
80
+ [6s .. 6.6s] = tr.dissolve()
81
+ [6.6s .. 13s] = animations.code_morph("const a = 1", "const a = 2")
82
+ [13s .. 19s] = animations.code_typing(code: "const z = 5;", duration: 6, filename: "app.ts", theme: "monokai")
83
+ [19s .. 24s] = animations.lt_clean_bar("Ada Lovelace", "Engineer", accent: "#00ff00")
84
+ [24s .. 29s] = animations.x_post("SceneRok", "@scenerok", body: "Shipped adapters.", hashtag: "#v1")
85
+ [29s .. 34s] = animations.news_ticker("Tech Desk", time: "15:30 UTC",
86
+ headline: "SceneRok ships", headlineAccent: "53 verified blocks",
87
+ ticker: "Verified count: 53/78\nBeta opens July")
88
+ ```
@@ -0,0 +1,71 @@
1
+ # Brand Setup for Agents
2
+
3
+ Collect brand context **only when it improves the video** and **only when your runtime allows asking the user or discovering assets**. Never block composition on a long questionnaire.
4
+
5
+ ## When to ask (vs infer)
6
+
7
+ | Situation | Action |
8
+ | --- | --- |
9
+ | User gave product/company URL + you can browse | Extract colors, logo, hero imagery from the site first |
10
+ | User mentioned a brand by name with no assets | Ask for logo URL and primary color (one message, ≤3 fields) |
11
+ | Template has `color` placeholders | Use template defaults; offer to customize |
12
+ | Motion graphic / code demo with no brand | Skip brand questions |
13
+ | User already specified hex colors / logo | Use as-is; do not re-ask |
14
+
15
+ **Ask at most one short brand message** with combined fields, not a multi-step wizard.
16
+
17
+ ## What to collect (priority order)
18
+
19
+ 1. **Primary brand color** — hex, e.g. `#6366F1` (for text `color:`, lower-third `accent:`, animation `accentColor:`)
20
+ 2. **Logo** — HTTPS URL or uploaded asset (editor library / ChatGPT import / local path on CLI)
21
+ 3. **Secondary / text color** — default white `#FFFFFF` on dark or dark `#0F172A` on light
22
+ 4. **Font preference** — only if user cares; default `Inter` / system stacks in examples
23
+ 5. **Tagline or CTA** — for promos and `logo_outro`
24
+
25
+ ## Where brand lands in VidScript
26
+
27
+ ```vidscript
28
+ import motion from "@scenerok/basic-animations"
29
+ import animations from "@scenerok/animations"
30
+
31
+ input logo = "https://cdn.example.com/brand-logo.png"
32
+
33
+ let brandColor = "#6366F1"
34
+
35
+ [0s .. 3s] = text "Launch Week", color: brandColor, style: title, animate: motion.popIn(0.6s)
36
+ [3s .. 8s] = animations.lt_clean_bar("Product Name", "Tagline here", accent: brandColor)
37
+ [8s .. 14s] = animations.logo_outro("Thanks for watching", url: "scenerok.com", logoUrl: logo)
38
+ ```
39
+
40
+ Use `accent`, `accentColor`, and `color` params consistently across text and animation blocks.
41
+
42
+ **`logo_outro` branding:** without `logoUrl` or `brand`, the outro renders the upstream demo's colorful Figma-style mark — never ship that on a branded video. Pass `logoUrl:` (user's logo image) or `brand: "scenerok"` (built-in SceneRok preset, also tints the glow), plus `accent:` to override the glow color.
43
+
44
+ ## Per-environment collection
45
+
46
+ | Environment | How to get brand assets |
47
+ | --- | --- |
48
+ | **Web editor** | Ask in chat; user can upload to asset library and paste URLs into script |
49
+ | **CLI / desktop agent** | Ask in terminal; accept local paths; user may run `scenerok auth login` for cloud assets |
50
+ | **ChatGPT** | Ask in conversation; user uploads logo/screenshot → `importOpenAIConversationFiles` → use returned `asset.url` |
51
+ | **URL-only (no uploads)** | Screenshot logo from site; probe dimensions before placement |
52
+
53
+ ## Website-derived brand (preferred when URL given)
54
+
55
+ 1. Visit the URL (browser tool or user-provided screenshots).
56
+ 2. Note dominant accent from buttons, links, or hero — sample one hex.
57
+ 3. Save logo and product stills; **probe width/height** before composing.
58
+ 4. Do not invent claims, pricing, or testimonials — extract or ask.
59
+
60
+ See `workflow.md` for the full URL asset pipeline.
61
+
62
+ ## Defaults when user declines
63
+
64
+ - Accent: `#6366F1` (SceneRok indigo) or high-contrast pair white on `#0B1020`
65
+ - No logo: kinetic `text` + `motion.popIn` instead of `logo_outro`
66
+ - Music: neutral instrumental bed from `music.md`
67
+
68
+ ## Privacy
69
+
70
+ - Do not paste API tokens or OAuth secrets into VidScript or chat logs.
71
+ - Use only assets the user provided or that are public on their stated URL.