wizz-method 2.16.0 → 2.17.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (60) hide show
  1. package/THIRD-PARTY-NOTICES.md +6 -0
  2. package/package.json +1 -1
  3. package/skills-registry.yaml +18 -6
  4. package/src/modules/wizz/_shared/video-pipeline.md +1 -0
  5. package/src/skills-lib/wizz-router/references/routing-table-flat.md +1 -0
  6. package/src/skills-lib/wizz-video-styles/SKILL.md +66 -0
  7. package/src/skills-lib/wizz-video-styles/assets/audio/CREDITS.md +54 -0
  8. package/src/skills-lib/wizz-video-styles/assets/audio/LIBRARIES.md +31 -0
  9. package/src/skills-lib/wizz-video-styles/assets/audio/kit.json +182 -0
  10. package/src/skills-lib/wizz-video-styles/assets/audio/music/acoustic-lifestyle.mp3 +0 -0
  11. package/src/skills-lib/wizz-video-styles/assets/audio/music/cinematic-tense.mp3 +0 -0
  12. package/src/skills-lib/wizz-video-styles/assets/audio/music/classical-light.mp3 +0 -0
  13. package/src/skills-lib/wizz-video-styles/assets/audio/music/corporate-light.mp3 +0 -0
  14. package/src/skills-lib/wizz-video-styles/assets/audio/music/lofi-chill.mp3 +0 -0
  15. package/src/skills-lib/wizz-video-styles/assets/audio/music/meme-beat.mp3 +0 -0
  16. package/src/skills-lib/wizz-video-styles/assets/audio/music/piano-editorial.mp3 +0 -0
  17. package/src/skills-lib/wizz-video-styles/assets/audio/music/tech-pulse.mp3 +0 -0
  18. package/src/skills-lib/wizz-video-styles/assets/audio/sfx/boom-impact.mp3 +0 -0
  19. package/src/skills-lib/wizz-video-styles/assets/audio/sfx/click-mouse.mp3 +0 -0
  20. package/src/skills-lib/wizz-video-styles/assets/audio/sfx/ding-notify.mp3 +0 -0
  21. package/src/skills-lib/wizz-video-styles/assets/audio/sfx/glitch-short.mp3 +0 -0
  22. package/src/skills-lib/wizz-video-styles/assets/audio/sfx/ka-ching.mp3 +0 -0
  23. package/src/skills-lib/wizz-video-styles/assets/audio/sfx/pop-bubble.mp3 +0 -0
  24. package/src/skills-lib/wizz-video-styles/assets/audio/sfx/record-scratch.mp3 +0 -0
  25. package/src/skills-lib/wizz-video-styles/assets/audio/sfx/riser-short.mp3 +0 -0
  26. package/src/skills-lib/wizz-video-styles/assets/audio/sfx/swoosh-text.mp3 +0 -0
  27. package/src/skills-lib/wizz-video-styles/assets/audio/sfx/typing-short.mp3 +0 -0
  28. package/src/skills-lib/wizz-video-styles/assets/audio/sfx/whoosh-fast.mp3 +0 -0
  29. package/src/skills-lib/wizz-video-styles/assets/audio/sfx/whoosh-soft.mp3 +0 -0
  30. package/src/skills-lib/wizz-video-styles/assets/remotion/editorial/EditorialReel.tsx +111 -0
  31. package/src/skills-lib/wizz-video-styles/assets/remotion/editorial/README.md +77 -0
  32. package/src/skills-lib/wizz-video-styles/assets/remotion/editorial/captions.tsx +56 -0
  33. package/src/skills-lib/wizz-video-styles/assets/remotion/editorial/example-words.json +285 -0
  34. package/src/skills-lib/wizz-video-styles/assets/remotion/editorial/primitives.tsx +241 -0
  35. package/src/skills-lib/wizz-video-styles/assets/remotion/editorial/scenes.tsx +113 -0
  36. package/src/skills-lib/wizz-video-styles/assets/remotion/editorial/theme.ts +68 -0
  37. package/src/skills-lib/wizz-video-styles/assets/remotion/editorial/timeline.ts +65 -0
  38. package/src/skills-lib/wizz-video-styles/data/recipes.json +341 -0
  39. package/src/skills-lib/wizz-video-styles/data/references.json +3397 -0
  40. package/src/skills-lib/wizz-video-styles/data/styles.json +726 -0
  41. package/src/skills-lib/wizz-video-styles/references/audio.md +54 -0
  42. package/src/skills-lib/wizz-video-styles/references/measured-patterns.md +38 -0
  43. package/src/skills-lib/wizz-video-styles/references/research-method.md +33 -0
  44. package/src/skills-lib/wizz-video-styles/references/style-picker.md +51 -0
  45. package/src/skills-lib/wizz-video-styles/references/styles/camera-no-setup.md +60 -0
  46. package/src/skills-lib/wizz-video-styles/references/styles/desafio-numero.md +60 -0
  47. package/src/skills-lib/wizz-video-styles/references/styles/esquete.md +57 -0
  48. package/src/skills-lib/wizz-video-styles/references/styles/experimento.md +56 -0
  49. package/src/skills-lib/wizz-video-styles/references/styles/explicador-animado.md +62 -0
  50. package/src/skills-lib/wizz-video-styles/references/styles/macro-asmr.md +61 -0
  51. package/src/skills-lib/wizz-video-styles/references/styles/meme-rapido.md +65 -0
  52. package/src/skills-lib/wizz-video-styles/references/styles/motion-editorial.md +69 -0
  53. package/src/skills-lib/wizz-video-styles/references/styles/podcast-clip.md +61 -0
  54. package/src/skills-lib/wizz-video-styles/references/styles/pov-vlog.md +57 -0
  55. package/src/skills-lib/wizz-video-styles/references/styles/quadro-branco.md +60 -0
  56. package/src/skills-lib/wizz-video-styles/references/styles/talking-head-cards.md +62 -0
  57. package/src/skills-lib/wizz-video-styles/references/styles/talking-head-legenda.md +62 -0
  58. package/src/skills-lib/wizz-video-styles/references/styles/tour.md +59 -0
  59. package/src/skills-lib/wizz-video-styles/scripts/analyze.sh +66 -0
  60. package/src/skills-lib/wizz-video-styles/scripts/build_catalog.py +128 -0
@@ -136,3 +136,9 @@ the `<Font>-OFL.txt` file in the same folder.
136
136
  - Path: `src/skills-lib/wizz-supabase-postgres-best-practices/`
137
137
  - Origin: [supabase/agent-skills](https://github.com/supabase/agent-skills)
138
138
  - License: MIT (full text at `src/skills-lib/wizz-supabase-postgres-best-practices/LICENSE`, copyright (c) 2026 Supabase)
139
+
140
+ ## wizz-video-styles (audio kit)
141
+
142
+ - Path: `src/skills-lib/wizz-video-styles/assets/audio/music/cinematic-tense.mp3`, `src/skills-lib/wizz-video-styles/assets/audio/music/meme-beat.mp3`
143
+ - Origin: "Hitman" and "Monkeys Spinning Monkeys" by Kevin MacLeod ([incompetech.com](https://incompetech.com)), via Wikimedia Commons
144
+ - License: Creative Commons Attribution 4.0 (CC BY 4.0). Both tracks were trimmed, faded and loudness-normalised; the exact changes and the credit text to paste are in `src/skills-lib/wizz-video-styles/assets/audio/CREDITS.md`.
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "$schema": "https://json.schemastore.org/package.json",
3
3
  "name": "wizz-method",
4
- "version": "2.16.0",
4
+ "version": "2.17.0",
5
5
  "description": "Wizz Method: método de agência orientado por IA em PT-BR",
6
6
  "keywords": [
7
7
  "agile",
@@ -171,7 +171,7 @@ areas:
171
171
  door: motion
172
172
  when: "MONTAR o filme: vídeo em React (Remotion) com áudio, SFX, legenda e timing com precisão de frame. No pipeline de vídeo é o Remotion que faz a montagem final; o hyperframes só desenha as telas mudas (regra: HyperFrames desenha, Remotion monta — ver _shared/video-pipeline.md do módulo wizz)."
173
173
  rel:
174
- after: [buttercut, ctc-align, get-brolls, hyperframes, wizz-motion-3d-director, wizz-video-effects]
174
+ after: [buttercut, ctc-align, get-brolls, hyperframes, wizz-motion-3d-director, wizz-video-effects, wizz-video-styles]
175
175
  - id: wizz-canvas-design
176
176
  door: motion
177
177
  when: "Gerar PNG/PDF editável por código (carrossel, quote card, infográfico) com fonte/cor/logo sempre certos. Traz canvas-fonts."
@@ -186,7 +186,7 @@ areas:
186
186
  door: motion
187
187
  when: "Timing por forced alignment: timestamps palavra a palavra de narração TTS para sincronizar legenda, corte de cena e animação com a fala. Fonte única de timing no pipeline de vídeo; substitui Whisper e estimativa manual."
188
188
  rel:
189
- after: [voicebox]
189
+ after: [voicebox, wizz-video-styles]
190
190
  before: [wizz-remotion-best-practices, wizz-video-effects]
191
191
  # Motor de decisão de efeitos (Python) portado do sistema de efeitos do
192
192
  # trafego em 2026-09-24: orçamento anti-excesso, som por evento semântico,
@@ -199,15 +199,27 @@ areas:
199
199
  - "Montar o filme em React (sequência de cenas, legenda, <Audio>): é wizz-remotion-best-practices; esta skill decide os efeitos que ele monta."
200
200
  - "Filme de lançamento de produto com trilha e direção visual: é wizz-huashu-design."
201
201
  rel:
202
- after: [ctc-align, hyperframes]
202
+ after: [ctc-align, hyperframes, wizz-video-styles]
203
203
  before: [wizz-remotion-best-practices]
204
204
  pairs_with: [wizz-ad-creative]
205
+ # Catálogo de estilos de vídeo curto medido em 99 shorts reais: decide QUAL
206
+ # estilo copiar antes de voz, timing, efeitos e montagem.
207
+ - id: wizz-video-styles
208
+ door: motion
209
+ when: "Decidir QUAL estilo de vídeo curto copiar a partir do inventário do material (pessoa real, só voz, b-roll, telas, produto) e entregar a receita medida em 99 shorts reais (ritmo de corte, legenda, áudio, zoom, gancho), para qualquer nicho. Gatilhos: 'estilo do vídeo', 'editar no estilo de', 'copiar a edição de', 'que estilo usar no reel', 'o vídeo ficou amador'."
210
+ not_when:
211
+ - "Decidir efeitos e SFX por evento, volume, LUFS e portões de zona segura: é wizz-video-effects; esta skill escolhe o estilo que ele depois refina."
212
+ - "Montar o filme em React (cenas, legenda, <Audio>): é wizz-remotion-best-practices."
213
+ rel:
214
+ before: [ctc-align, wizz-video-effects, wizz-remotion-best-practices]
215
+ pairs_with: [wizz-social-content, get-brolls, hyperframes]
205
216
  clis:
206
217
  - id: hyperframes
207
218
  when: "DESENHAR telas de vídeo em HTML/CSS (HTML→MP4, agent-native, 20+ skills): cena isolada e MUDA — maqueta de WhatsApp/ChatGPT, gráfico animado, título, loop. NÃO usar para montagem com áudio/SFX/legenda sincronizada (posicionador de áudio falhou em produção, ~2,76s de drift; CSS vaza entre telas): a montagem final é do Remotion (regra: HyperFrames desenha, Remotion monta — ver _shared/video-pipeline.md do módulo wizz). Precisa ffmpeg + Node 22 + Chrome headless."
208
219
  not_when: "Montagem com áudio/SFX/legenda sincronizada (posicionador de áudio falhou em produção, ~2,76s de drift; CSS vaza entre telas)."
209
220
  rel:
210
221
  before: [wizz-remotion-best-practices, wizz-video-effects]
222
+ pairs_with: [wizz-video-styles]
211
223
  platform: [darwin, linux]
212
224
  # O check confere o SHA fixado, não só a existência da pasta: o bundle é
213
225
  # copiado (não clonado), então o pin é gravado num marcador no install.
@@ -248,7 +260,7 @@ areas:
248
260
  rel:
249
261
  after: [wizz-social-content]
250
262
  before: [buttercut, wizz-remotion-best-practices]
251
- pairs_with: [claude-video]
263
+ pairs_with: [claude-video, wizz-video-styles]
252
264
  # macOS apenas: o upstream declara macOS/Windows (Linux não é testado lá)
253
265
  # e o `install` daqui é POSIX. Windows fica fora porque o installer do
254
266
  # Wizz não roda nele — quem estiver no Windows segue o README do upstream.
@@ -296,7 +308,7 @@ areas:
296
308
  - id: ctc-align
297
309
  when: "Timestamps palavra a palavra CONFIÁVEIS de narração TTS (texto conhecido): sincronizar legenda, corte de cena, animação e elemento gráfico com a fala. Fonte única de timing em vídeo — substitui timestamps do Whisper e estimativa manual. Skill da lib: ctc-align (instalação + helper align.py + regras de âncora monotônica e duração mínima). Roda em CPU, sem CUDA."
298
310
  rel:
299
- after: [voicebox]
311
+ after: [voicebox, wizz-video-styles]
300
312
  before: [wizz-remotion-best-practices, wizz-video-effects]
301
313
  platform: [darwin, linux]
302
314
  # O pip grava o commit instalado no direct_url.json: o check compara com o pin.
@@ -580,7 +592,7 @@ areas:
580
592
  - "Pedir SÓ o asset de um Reel/vídeo — b-roll, 'imagens de apoio', 'cenas de apoio', imagem de corte, footage — sem que o texto ou o roteiro façam parte do pedido: a captação é get-brolls, na área designer. Esta entrada entra quando o TEXTO ou o ROTEIRO é parte do que se pede; se o pedido junta roteiro E captação, são 2 áreas."
581
593
  rel:
582
594
  before: [buffer, get-brolls]
583
- pairs_with: [wizz-canvas-design, wizz-content-strategy, wizz-copy-editing, wizz-launch-strategy]
595
+ pairs_with: [wizz-canvas-design, wizz-content-strategy, wizz-copy-editing, wizz-launch-strategy, wizz-video-styles]
584
596
  - id: wizz-content-strategy
585
597
  when: "Decidir O QUE produzir de conteúdo (topic clusters, calendário, pilares)."
586
598
  rel:
@@ -21,6 +21,7 @@
21
21
  | Short video WITHOUT synced audio (loop, overlay, silent motion graphic) | `hyperframes` |
22
22
  | Assemble the film: sequence of scenes + narration + SFX + captions + frame timing | **Remotion** (skill `wizz-remotion-best-practices`) |
23
23
  | Captions passing behind the person on a recorded talking head (matte) | embedded-captions (HyperFrames bundle), then the clip goes to Remotion |
24
+ | Pick WHICH short-video style to copy (from the inventory of the material) and get its measured recipe, before voice, timing and effects | `wizz-video-styles` |
24
25
  | Word-by-word timestamps of the TTS narration (sync captions/cuts/animation) | `ctc-align` |
25
26
  | Decide which effects and SFX go where (and where not), then gate the render: safe zone (ad or organic), density, excess, rhythm, loudness | `wizz-video-effects` |
26
27
  | Narration / voice / TTS / voice cloning | `voicebox` |
@@ -55,6 +55,7 @@ Use this table to map skills/CLIs/MCPs directly **outside a Wizz project** (no `
55
55
  | **Video with audio, narration, SFX, or synced captions** (film assembly) | `wizz-remotion-best-practices` (Remotion assembles; frame timing). Pipeline rule: HyperFrames draws the mute screens, Remotion assembles the film — don't use hyperframes to position audio/captions (~2.76s drift and CSS leaking between screens in production) | 1 |
56
56
  | Draw a video SCREEN/scene in HTML/CSS, mute visual (app mockup, animated chart, title, loop; HTML→MP4) | CLI `hyperframes` (designer registry; `npx skills add heygen-com/hyperframes`) — mute screens only; assembly with audio goes to Remotion (line above) | 2 |
57
57
  | Word-by-word timestamps for TTS narration (sync captions/cuts/animation with speech) | `ctc-align` (designer registry; single source of timing, replaces Whisper) | 2 |
58
+ | Which short-video STYLE to copy, from the inventory of the material (real person, voice only, B-roll, screens, product), with the recipe measured on 99 real shorts; "edit it in the style of", "the video looks amateur" | `wizz-video-styles` (designer registry; picks the style before voice, timing, effects and assembly) | 2 |
58
59
  | Effects and sound of a short vertical video (ad or Reels): which effect goes where and when not, which SFX for each event and at what level, ducking, master loudness, safe-zone/density/excess/rhythm gates | `wizz-video-effects` (designer registry; decides the effects, Remotion assembles them) | 2 |
59
60
  | **Find B-roll / inserts / cutaways / supporting footage** for a video or Reel (real footage of the fact, person, product, news item or screen the script names; YouTube/Instagram/TikTok/Pexels/Pixabay/Commons/NASA) | CLI `get-brolls` (designer registry; **macOS**, global `skills add` + its own `install.sh`; needs ffmpeg with drawtext, Node 22+, Python 3.11+). Ends in a Storyboard the HUMAN approves — the agent never self-approves. Stock only when the user explicitly asks for stock | 1 |
60
61
  | **Edit/cut video** (cuts, selects, roughcut) | CLI `buttercut` (designer registry; **Apple Silicon only**, clone-and-run in the project, heavy deps) | 1 |
@@ -0,0 +1,66 @@
1
+ ---
2
+ name: wizz-video-styles
3
+ description: Catalogue of short vertical video styles (Reels, Shorts, TikTok) measured on real high-reach videos, for any niche. Takes an inventory of the material the project actually has (a real person on camera, voice only, b-roll, app screens, a physical product), crosses it with niche and goal, picks the style to copy and hands over its recipe - rhythm, captions, palette, typography, motion vocabulary, music and SFX rules - plus a Remotion template when one exists and a licensed audio kit. Use when a video is about to be edited or planned and nobody has decided what it should look like, when someone says a video "looks amateur" or asks to copy a creator's style, or before wizz-video-effects and Remotion take over. Triggers - estilo do vídeo, editar no estilo de, copiar a edição de, que estilo usar no reel, referência de edição, catálogo de estilos, o vídeo ficou amador. Does not decide per-event SFX or gates (that is wizz-video-effects) and does not assemble the film (that is Remotion).
4
+ ---
5
+
6
+ # wizz-video-styles: which style to copy, and its recipe
7
+
8
+ Every number in this skill was measured on a real public short ([references/research-method.md](references/research-method.md)). A style that has no measured reference does not enter the catalogue. When a number below was not measured, the page says so.
9
+
10
+ ## Where it sits in the video pipeline
11
+
12
+ 1. Script exists (social area, `wizz-social-content`).
13
+ 2. **This skill:** inventory of the material, then style, then recipe.
14
+ 3. Narration and word timing (the person's own recording or TTS, then `ctc-align`).
15
+ 4. Footage and screens: `get-brolls` for b-roll, HyperFrames for silent screens, real screenshots for app screens.
16
+ 5. Effects and sound per event, plus gates: `wizz-video-effects`. Pass it this style's SFX density ceiling and music level.
17
+ 6. Assembly in Remotion: invoke `wizz-remotion-best-practices`; copy the style's template from `assets/remotion/` when it has one.
18
+
19
+ It can also run at step 1, so the script is written for the style (room for b-roll, a hook that the style can show).
20
+
21
+ ## Per piece
22
+
23
+ 1. **Inventory first, always.** Ask or read from the plan, one line each:
24
+ - Is a real person on camera? Which shots exist (face to camera, podcast, stage, street, hands only)?
25
+ - Voice: the person's own recording, a podcast/interview track, TTS, or none?
26
+ - Screens: app or site screenshots, terminal, documents? (Capture screens as screenshots; a browser screen *recording* can come out stretched.)
27
+ - B-roll or stock, a physical product, food, a place?
28
+ - Niche, goal of the piece (follow, comment, save, click), duration.
29
+ Never pick a style the material cannot feed. "Talking head" needs a person on camera; "motion editorial" needs nothing but a voice; "macro / ASMR" needs a physical product filmed close.
30
+ 2. **Pick the style** with [references/style-picker.md](references/style-picker.md): material decides which styles are possible, niche and goal rank them. Mixed is allowed and common: person on camera for the hook and the CTA, another style in the middle.
31
+ 3. **Open the style card** in `references/styles/` and follow it: rhythm, captions, palette, typography, motion, music, SFX, hook, CTA, and its "do not" list.
32
+ 4. **Watch two of its references** (links in the card) before editing. Reading a card is not watching a video.
33
+ 5. **Audio:** music and SFX choices from [references/audio.md](references/audio.md); the redistributable kit is in `assets/audio/` (credits in `assets/audio/CREDITS.md`).
34
+ 6. **Hand over** to `wizz-video-effects` with the card's SFX ceiling and music level, then to Remotion.
35
+ 7. **Look at the final file**: extract frames and compare them side by side with a contact sheet of one reference.
36
+
37
+ ## What the measurements say across every style
38
+
39
+ - **SFX are rare.** Across 99 measured shorts, sound effects landed on a cut in a small minority of cuts (per-style counts in each card). Putting a sound on every cut is the most common amateur tell.
40
+ - **Music bed: style-dependent.** Fast montage, animated explainers and travel POV carry a continuous bed; talking heads and podcast clips mostly have none or a faint one.
41
+ - **Captions** in reach-heavy styles are 1 to 3 words at a time, bold, lower-centre, with one keyword in an accent colour; editorial and explainer styles keep them small or drop them.
42
+ - **Rhythm is a style property, not a virtue.** The highest-reach videos in the set range from single takes with no detected cut to a cut every second.
43
+
44
+ Details and the per-style table: [references/measured-patterns.md](references/measured-patterns.md).
45
+
46
+ ## References
47
+
48
+ | File | Answers |
49
+ |---|---|
50
+ | [references/style-picker.md](references/style-picker.md) | **start here.** Material and niche → which styles |
51
+ | [references/styles/](references/styles/) | one card per style: recipe, references, do-not list |
52
+ | [references/measured-patterns.md](references/measured-patterns.md) | the numbers behind the cards, per style and per niche |
53
+ | [references/audio.md](references/audio.md) | music and SFX per style, the kit, libraries and licences |
54
+ | [references/research-method.md](references/research-method.md) | how the catalogue was measured, its limits, how to add a niche or a style |
55
+ | `data/references.json` | every measured short: link, channel, views, niche, style, metrics |
56
+ | `scripts/analyze.sh` | measures one short (cuts, music bed, SFX heuristics, loudness, contact sheet) |
57
+ | `assets/remotion/editorial/` | Remotion template of the motion editorial style |
58
+ | `assets/audio/` | licensed music beds and SFX that may ship with a project |
59
+
60
+ ## Out of scope
61
+
62
+ - Which SFX plays on which event, ducking, loudness and safe-zone gates: `wizz-video-effects`.
63
+ - Assembling the film and the `<Audio>` API: `wizz-remotion-best-practices`.
64
+ - Script, hook wording, CTA wording: `wizz-social-content` and the copy area.
65
+ - Sourcing b-roll: `get-brolls`. Drawing silent screens: HyperFrames.
66
+ - Launch films with art direction: `wizz-huashu-design`.
@@ -0,0 +1,54 @@
1
+ # Audio kit credits
2
+
3
+ Licence checked on each source's page on 2026-10-04. Only CC0 / public domain or CC BY 4.0 files are included.
4
+ Freesound source: downloading the original requires login, so the public HQ mp3 preview that the page itself exposes was used (lower quality than the original; re-encoded at 160 kbps).
5
+ Kenney: CC0 licence confirmed on each pack's page and in the zip's `License.txt`.
6
+
7
+ | File | Original title | Author | Licence | Source page | Checked on | Changes |
8
+ |---|---|---|---|---|---|---|
9
+ | `music/piano-editorial.mp3` | Erik Satie - Gymnopédies - la 1ère. Lent et douloureux | Robin Alciatore (Musopen); composition by Erik Satie | [Public domain (dedicated by the recording's author)](https://commons.wikimedia.org/wiki/File:Erik_Satie_-_gymnopedies_-_la_1_ere._lent_et_douloureux.ogg) | https://commons.wikimedia.org/wiki/File:Erik_Satie_-_gymnopedies_-_la_1_ere._lent_et_douloureux.ogg | 2026-10-04 | cut 0-70 s, fade-in 0.5 s, fade-out 2 s, loudnorm -20 LUFS, mp3 160 kbps |
10
+ | `music/lofi-chill.mp3` | Lofi Hip Hop Loop | omfgdude | [CC0 1.0](https://creativecommons.org/publicdomain/zero/1.0/) | https://opengameart.org/content/lofi-hip-hop-loop | 2026-10-04 | cut 0-70 s, fades, loudnorm -20 LUFS, mp3 160 kbps |
11
+ | `music/tech-pulse.mp3` | Synthwave House Loop | Fupi | [CC0 1.0](https://creativecommons.org/publicdomain/zero/1.0/) | https://opengameart.org/content/synthwave-house-loop | 2026-10-04 | original of 50.5 s repeated 2x and cut to 70 s (seam at the loop point), fades, loudnorm -20 LUFS, mp3 160 kbps |
12
+ | `music/corporate-light.mp3` | Feel good slow ambient track with a beat. | annandistance | [CC0 1.0](https://creativecommons.org/publicdomain/zero/1.0/) | https://opengameart.org/content/feel-good-slow-ambient-track-with-a-beat | 2026-10-04 | cut 0-70 s, fades, loudnorm -20 LUFS, mp3 160 kbps |
13
+ | `music/cinematic-tense.mp3` | Hitman | Kevin MacLeod (incompetech.com) | [CC BY 4.0](https://creativecommons.org/licenses/by/4.0/) | https://commons.wikimedia.org/wiki/File:Hitman_by_Kevin_MacLeod.ogg | 2026-10-04 | cut 0-70 s, fades, loudnorm -20 LUFS, mp3 160 kbps |
14
+ | `music/acoustic-lifestyle.mp3` | Sunset Plains | Yoiyami | [CC0 1.0](https://creativecommons.org/publicdomain/zero/1.0/) | https://opengameart.org/content/sunset-plains | 2026-10-04 | cut 10-80 s of the original WAV, fades, loudnorm -20 LUFS, mp3 160 kbps |
15
+ | `music/classical-light.mp3` | Chopin Nocturne No. 2 in E-flat Major, Op. 9 | Peter Johnston (Musopen); composition by Frédéric Chopin | [CC0 1.0](https://creativecommons.org/publicdomain/zero/1.0/) | https://commons.wikimedia.org/wiki/File:Chopin_Nocturne_No._2_in_E_Flat_Major,_Op._9.ogg | 2026-10-04 | cut 0-70 s, fades, loudnorm -20 LUFS, mp3 160 kbps |
16
+ | `music/meme-beat.mp3` | Monkeys Spinning Monkeys | Kevin MacLeod (incompetech.com) | [CC BY 4.0 (the page offers CC BY 3.0 or 4.0 at the user's choice; used under 4.0)](https://creativecommons.org/licenses/by/4.0/) | https://commons.wikimedia.org/wiki/File:Kevin_MacLeod_~_Monkeys_Spinning_Monkeys.ogg | 2026-10-04 | cut 0-70 s, fades, loudnorm -20 LUFS, mp3 160 kbps |
17
+ | `sfx/whoosh-soft.mp3` | Whoosh | qubodup | [CC0 1.0](https://creativecommons.org/publicdomain/zero/1.0/) | https://freesound.org/people/qubodup/sounds/60013/ | 2026-10-04 | public HQ mp3 preview (the original download requires login); 0.45 s + 0.15 s of silence, loudnorm -16 LUFS, re-encoded to mp3 160 kbps |
18
+ | `sfx/whoosh-fast.mp3` | swoosh.wav | Garysguff | [CC0 1.0](https://creativecommons.org/publicdomain/zero/1.0/) | https://freesound.org/people/Garysguff/sounds/575836/ | 2026-10-04 | public HQ mp3 preview; trim 0.30-0.75 s (first swoosh), loudnorm -16 LUFS (peak-limited), mp3 160 kbps |
19
+ | `sfx/swoosh-text.mp3` | swish-3.wav (Swishes Sound Pack) | artisticdude | [CC0 1.0](https://creativecommons.org/publicdomain/zero/1.0/) | https://opengameart.org/content/swishes-sound-pack | 2026-10-04 | 0.146 s + silence padded to 0.6 s, loudnorm -16 LUFS, mp3 160 kbps |
20
+ | `sfx/pop-bubble.mp3` | drop_003.ogg (Interface Sounds) | Kenney (kenney.nl) | [CC0 1.0](https://creativecommons.org/publicdomain/zero/1.0/) | https://kenney.nl/assets/interface-sounds | 2026-10-04 | silence padded to 0.6 s, loudnorm -16 LUFS (peak-limited), mp3 160 kbps |
21
+ | `sfx/click-mouse.mp3` | mouseclick1.ogg (UI Audio) | Kenney (kenney.nl) | [CC0 1.0](https://creativecommons.org/publicdomain/zero/1.0/) | https://kenney.nl/assets/ui-audio | 2026-10-04 | silence padded to 0.6 s, loudnorm -16 LUFS (peak-limited), mp3 160 kbps |
22
+ | `sfx/typing-short.mp3` | typing soundeffect | johndekale | [CC0 1.0](https://creativecommons.org/publicdomain/zero/1.0/) | https://opengameart.org/content/typing-soundeffect | 2026-10-04 | cut 0-1.6 s, loudnorm -16 LUFS (peak-limited), mp3 160 kbps |
23
+ | `sfx/ding-notify.mp3` | confirmation_002.ogg (Interface Sounds) | Kenney (kenney.nl) | [CC0 1.0](https://creativecommons.org/publicdomain/zero/1.0/) | https://kenney.nl/assets/interface-sounds | 2026-10-04 | silence padded to 0.6 s, loudnorm -16 LUFS, mp3 160 kbps |
24
+ | `sfx/boom-impact.mp3` | impactPunch_heavy_000.ogg (Impact Sounds) | Kenney (kenney.nl) | [CC0 1.0](https://creativecommons.org/publicdomain/zero/1.0/) | https://kenney.nl/assets/impact-sounds | 2026-10-04 | loudnorm -16 LUFS (peak-limited), mp3 160 kbps |
25
+ | `sfx/riser-short.mp3` | phaserUp3.ogg (Digital Audio) | Kenney (kenney.nl) | [CC0 1.0](https://creativecommons.org/publicdomain/zero/1.0/) | https://kenney.nl/assets/digital-audio | 2026-10-04 | silence padded to 0.6 s, loudnorm -16 LUFS, mp3 160 kbps |
26
+ | `sfx/glitch-short.mp3` | lowRandom.ogg (Digital Audio) | Kenney (kenney.nl) | [CC0 1.0](https://creativecommons.org/publicdomain/zero/1.0/) | https://kenney.nl/assets/digital-audio | 2026-10-04 | silence padded to 0.6 s, loudnorm -16 LUFS, mp3 160 kbps |
27
+ | `sfx/ka-ching.mp3` | Cha Ching.wav | deleted_user_96253 (deleted account) | [CC0 1.0](https://creativecommons.org/publicdomain/zero/1.0/) | https://freesound.org/people/deleted_user_96253/sounds/351304/ | 2026-10-04 | public HQ mp3 preview; cut 0-2.2 s, loudnorm -16 LUFS, mp3 160 kbps |
28
+ | `sfx/record-scratch.mp3` | Vinyl Backspin and Beat Scratch.wav | ZeSoundResearchInc. | [CC0 1.0](https://creativecommons.org/publicdomain/zero/1.0/) | https://freesound.org/people/ZeSoundResearchInc./sounds/117511/ | 2026-10-04 | public HQ mp3 preview; trim 0-0.65 s (first scratch), loudnorm -16 LUFS, mp3 160 kbps |
29
+
30
+ ## Required attribution (CC BY 4.0)
31
+
32
+ Paste into the video credits or description when you use these files. The other files (CC0 and public domain) require no credit.
33
+
34
+ - `music/cinematic-tense.mp3`:
35
+
36
+ ```
37
+ "Hitman" Kevin MacLeod (incompetech.com)
38
+ Licensed under Creative Commons: By Attribution 4.0 License
39
+ http://creativecommons.org/licenses/by/4.0/
40
+ Changes: trimmed to 70 s, fade in/out, loudness normalised to -20 LUFS.
41
+ ```
42
+
43
+ - `music/meme-beat.mp3`:
44
+
45
+ ```
46
+ "Monkeys Spinning Monkeys" Kevin MacLeod (incompetech.com)
47
+ Licensed under Creative Commons: By Attribution 4.0 License
48
+ http://creativecommons.org/licenses/by/4.0/
49
+ Changes: trimmed to 70 s, fade in/out, loudness normalised to -20 LUFS.
50
+ ```
51
+
52
+ Licence link: https://creativecommons.org/licenses/by/4.0/
53
+
54
+ The Musopen recordings (Satie, Chopin) are public domain/CC0; crediting the performer is optional, not required.
@@ -0,0 +1,31 @@
1
+ # Bibliotecas para buscar mais áudio
2
+
3
+ Resumos de licença abaixo vêm da checagem de 2026-10-04 (páginas dos arquivos do kit) ou, onde marcado "não verificado", do conhecimento geral. Confira a licença na página de cada arquivo antes de usar.
4
+
5
+ ## Pode ir no pacote (redistribuição permitida)
6
+
7
+ | Biblioteca | Licença | Pode | Não pode / cuidado |
8
+ |---|---|---|---|
9
+ | [Kenney](https://kenney.nl/assets/category:Audio) | CC0 (confirmado) | Tudo, inclusive redistribuir e vender | nada; crédito opcional |
10
+ | [OpenGameArt](https://opengameart.org/art-search-advanced?field_art_type_tid%5B%5D=12&field_art_licenses_tid%5B%5D=4) (filtrar CC0) | Varia por arquivo: CC0, CC-BY 3.0/4.0, OGA-BY, GPL (confirmado que varia) | CC0 sem restrição | Só CC0 ou CC-BY 4.0 no pacote; CC-BY 3.0, GPL e CC-BY-SA não entram (a pasta do kit só aceita CC0/4.0) |
11
+ | [Freesound](https://freesound.org/search/?f=license%3A%22Creative+Commons+0%22) (filtro "Creative Commons 0") | Varia por arquivo: CC0, CC-BY, CC-BY-NC | CC0 sem restrição | Baixar o original exige login; o preview HQ mp3 é público. CC-BY-NC nunca entra |
12
+ | [Wikimedia Commons - Musopen](https://commons.wikimedia.org/wiki/Category:Musopen) | Domínio público / CC0 por arquivo (confirmado nos arquivos do kit) | Clássicos (Satie, Chopin, Bach) | Ler a licença de cada arquivo: há versões de outros intérpretes com licença diferente |
13
+ | [incompetech (Kevin MacLeod)](https://incompetech.com/music/royalty-free/) | CC BY 4.0 nas faixas atuais; arquivos antigos na Commons estão em CC BY 3.0 (confirmado na Commons) | Redistribuir com atribuição e indicar alterações | Exige a linha de crédito; só entra no pacote a faixa confirmada em 4.0. A página é JS, confirme na página da faixa |
14
+ | [Free Music Archive](https://freemusicarchive.org/search?license=CC0) (filtro CC0) | Varia: CC0, CC-BY, NC, ND | CC0 sem restrição | Não verificado nesta rodada. NC/ND nunca entram |
15
+ | [ccMixter](https://ccmixter.org/) | CC0 ou CC-BY por faixa | Idem | Não verificado nesta rodada |
16
+
17
+ FreePD (freepd.com) mostrava aviso de encerramento em 2026-10-04: não usar como fonte nova. Arquivos antigos de lá só se a licença for confirmada em outra página.
18
+
19
+ ## Só usar no projeto (não redistribuir o arquivo)
20
+
21
+ Servem para a edição de um vídeo, mas o arquivo não pode ir dentro de pacote ou repositório público. Dê só o link.
22
+
23
+ | Biblioteca | Uso permitido (resumo, não verificado) | Por que fica fora do pacote |
24
+ |---|---|---|
25
+ | [Pixabay](https://pixabay.com/music/) | Uso comercial em projetos sem crédito | A Pixabay Content License proíbe redistribuir o arquivo isolado/como conteúdo autônomo |
26
+ | [YouTube Audio Library](https://studio.youtube.com/channel/UC/music) | Uso em vídeos | Termos ligados à conta e ao YouTube; sem redistribuição |
27
+ | [Uppbeat](https://uppbeat.io/) | Plano grátis com crédito e limite mensal | Licença por conta/uso, sem redistribuição |
28
+ | [Epidemic Sound](https://www.epidemicsound.com/) | Assinatura, uso em canais licenciados | Sem redistribuição; some se a assinatura acabar |
29
+ | [Artlist](https://artlist.io/) | Assinatura, licença perpétua para os vídeos feitos | Sem redistribuição |
30
+ | [Mixkit](https://mixkit.co/free-stock-music/) | Uso em vídeos com a Mixkit License | Não verificado; não redistribuir como arquivo avulso |
31
+ | [Bensound](https://www.bensound.com/) | Grátis com crédito | Licença própria, sem redistribuição |
@@ -0,0 +1,182 @@
1
+ [
2
+ {
3
+ "file": "music/piano-editorial.mp3",
4
+ "tipo": "music",
5
+ "clima_ou_uso": "piano calmo / editorial",
6
+ "duracao_s": 70.0,
7
+ "lufs": -20.4,
8
+ "licenca": "Public domain (dedicado pelo autor da gravação)",
9
+ "fonte": "https://commons.wikimedia.org/wiki/File:Erik_Satie_-_gymnopedies_-_la_1_ere._lent_et_douloureux.ogg"
10
+ },
11
+ {
12
+ "file": "music/lofi-chill.mp3",
13
+ "tipo": "music",
14
+ "clima_ou_uso": "lo-fi / chill beat",
15
+ "duracao_s": 70.0,
16
+ "lufs": -20.5,
17
+ "licenca": "CC0 1.0",
18
+ "fonte": "https://opengameart.org/content/lofi-hip-hop-loop"
19
+ },
20
+ {
21
+ "file": "music/tech-pulse.mp3",
22
+ "tipo": "music",
23
+ "clima_ou_uso": "eletrônica pulsante / tech (synthwave-house 114 BPM)",
24
+ "duracao_s": 70.0,
25
+ "lufs": -20.4,
26
+ "licenca": "CC0 1.0",
27
+ "fonte": "https://opengameart.org/content/synthwave-house-loop"
28
+ },
29
+ {
30
+ "file": "music/corporate-light.mp3",
31
+ "tipo": "music",
32
+ "clima_ou_uso": "corporativo leve / otimista (ambient com batida lenta; aproximação)",
33
+ "duracao_s": 70.0,
34
+ "lufs": -20.4,
35
+ "licenca": "CC0 1.0",
36
+ "fonte": "https://opengameart.org/content/feel-good-slow-ambient-track-with-a-beat"
37
+ },
38
+ {
39
+ "file": "music/cinematic-tense.mp3",
40
+ "tipo": "music",
41
+ "clima_ou_uso": "cinematográfico / tenso (mistério, crime)",
42
+ "duracao_s": 70.0,
43
+ "lufs": -20.4,
44
+ "licenca": "CC BY 4.0",
45
+ "fonte": "https://commons.wikimedia.org/wiki/File:Hitman_by_Kevin_MacLeod.ogg"
46
+ },
47
+ {
48
+ "file": "music/acoustic-lifestyle.mp3",
49
+ "tipo": "music",
50
+ "clima_ou_uso": "acústico / lifestyle (violão suave + pads)",
51
+ "duracao_s": 70.0,
52
+ "lufs": -20.4,
53
+ "licenca": "CC0 1.0",
54
+ "fonte": "https://opengameart.org/content/sunset-plains"
55
+ },
56
+ {
57
+ "file": "music/classical-light.mp3",
58
+ "tipo": "music",
59
+ "clima_ou_uso": "clássico leve (piano romântico)",
60
+ "duracao_s": 70.0,
61
+ "lufs": -20.4,
62
+ "licenca": "CC0 1.0",
63
+ "fonte": "https://commons.wikimedia.org/wiki/File:Chopin_Nocturne_No._2_in_E_Flat_Major,_Op._9.ogg"
64
+ },
65
+ {
66
+ "file": "music/meme-beat.mp3",
67
+ "tipo": "music",
68
+ "clima_ou_uso": "humor / meme (pizzicato e flautas, 144 BPM)",
69
+ "duracao_s": 70.0,
70
+ "lufs": -20.4,
71
+ "licenca": "CC BY 4.0",
72
+ "fonte": "https://commons.wikimedia.org/wiki/File:Kevin_MacLeod_~_Monkeys_Spinning_Monkeys.ogg"
73
+ },
74
+ {
75
+ "file": "sfx/whoosh-soft.mp3",
76
+ "tipo": "sfx",
77
+ "clima_ou_uso": "whoosh suave",
78
+ "duracao_s": 0.6,
79
+ "lufs": -16.8,
80
+ "licenca": "CC0 1.0",
81
+ "fonte": "https://freesound.org/people/qubodup/sounds/60013/"
82
+ },
83
+ {
84
+ "file": "sfx/whoosh-fast.mp3",
85
+ "tipo": "sfx",
86
+ "clima_ou_uso": "whoosh rápido",
87
+ "duracao_s": 0.6,
88
+ "lufs": -19.3,
89
+ "licenca": "CC0 1.0",
90
+ "fonte": "https://freesound.org/people/Garysguff/sounds/575836/"
91
+ },
92
+ {
93
+ "file": "sfx/swoosh-text.mp3",
94
+ "tipo": "sfx",
95
+ "clima_ou_uso": "swoosh de texto (entrada de título)",
96
+ "duracao_s": 0.6,
97
+ "lufs": -16.4,
98
+ "licenca": "CC0 1.0",
99
+ "fonte": "https://opengameart.org/content/swishes-sound-pack"
100
+ },
101
+ {
102
+ "file": "sfx/pop-bubble.mp3",
103
+ "tipo": "sfx",
104
+ "clima_ou_uso": "pop / bolha",
105
+ "duracao_s": 0.6,
106
+ "lufs": -19.9,
107
+ "licenca": "CC0 1.0",
108
+ "fonte": "https://kenney.nl/assets/interface-sounds"
109
+ },
110
+ {
111
+ "file": "sfx/click-mouse.mp3",
112
+ "tipo": "sfx",
113
+ "clima_ou_uso": "clique de mouse",
114
+ "duracao_s": 0.6,
115
+ "lufs": -25.1,
116
+ "licenca": "CC0 1.0",
117
+ "fonte": "https://kenney.nl/assets/ui-audio"
118
+ },
119
+ {
120
+ "file": "sfx/typing-short.mp3",
121
+ "tipo": "sfx",
122
+ "clima_ou_uso": "digitação curta",
123
+ "duracao_s": 1.6,
124
+ "lufs": -19.0,
125
+ "licenca": "CC0 1.0",
126
+ "fonte": "https://opengameart.org/content/typing-soundeffect"
127
+ },
128
+ {
129
+ "file": "sfx/ding-notify.mp3",
130
+ "tipo": "sfx",
131
+ "clima_ou_uso": "ding / notificação",
132
+ "duracao_s": 0.6,
133
+ "lufs": -16.4,
134
+ "licenca": "CC0 1.0",
135
+ "fonte": "https://kenney.nl/assets/interface-sounds"
136
+ },
137
+ {
138
+ "file": "sfx/boom-impact.mp3",
139
+ "tipo": "sfx",
140
+ "clima_ou_uso": "impacto grave (boom)",
141
+ "duracao_s": 0.6,
142
+ "lufs": -17.1,
143
+ "licenca": "CC0 1.0",
144
+ "fonte": "https://kenney.nl/assets/impact-sounds"
145
+ },
146
+ {
147
+ "file": "sfx/riser-short.mp3",
148
+ "tipo": "sfx",
149
+ "clima_ou_uso": "riser curto",
150
+ "duracao_s": 0.6,
151
+ "lufs": -16.4,
152
+ "licenca": "CC0 1.0",
153
+ "fonte": "https://kenney.nl/assets/digital-audio"
154
+ },
155
+ {
156
+ "file": "sfx/glitch-short.mp3",
157
+ "tipo": "sfx",
158
+ "clima_ou_uso": "glitch curto (bipes aleatórios; aproximação)",
159
+ "duracao_s": 0.6,
160
+ "lufs": -16.4,
161
+ "licenca": "CC0 1.0",
162
+ "fonte": "https://kenney.nl/assets/digital-audio"
163
+ },
164
+ {
165
+ "file": "sfx/ka-ching.mp3",
166
+ "tipo": "sfx",
167
+ "clima_ou_uso": "ka-ching / caixa registradora",
168
+ "duracao_s": 2.2,
169
+ "lufs": -16.4,
170
+ "licenca": "CC0 1.0",
171
+ "fonte": "https://freesound.org/people/deleted_user_96253/sounds/351304/"
172
+ },
173
+ {
174
+ "file": "sfx/record-scratch.mp3",
175
+ "tipo": "sfx",
176
+ "clima_ou_uso": "record scratch (meme)",
177
+ "duracao_s": 0.7,
178
+ "lufs": -16.5,
179
+ "licenca": "CC0 1.0",
180
+ "fonte": "https://freesound.org/people/ZeSoundResearchInc./sounds/117511/"
181
+ }
182
+ ]
@@ -0,0 +1,111 @@
1
+ import React, { useMemo } from "react";
2
+ import { AbsoluteFill, Audio, Sequence, interpolate, staticFile } from "remotion";
3
+ import { Captions } from "./captions";
4
+ import { Backdrop, Stage, clamp } from "./primitives";
5
+ import { SceneView, type SceneDef } from "./scenes";
6
+ import { ThemeContext, mergeTheme, type EditorialTheme } from "./theme";
7
+ import { at, buildScenes, toFrames, totalSeconds, type WordsData } from "./timeline";
8
+ import exampleWords from "./example-words.json";
9
+
10
+ // Composição genérica do estilo "motion editorial": um objeto por vez no centro,
11
+ // troca por transformação (cena sobe enquanto a próxima entra), música baixa contínua,
12
+ // SFX raro, legenda pequena. Tudo que muda de vídeo para vídeo entra por props.
13
+
14
+ export type SfxDef = { atWord: string; offset?: number; src: string; volume?: number; durationSec?: number };
15
+
16
+ export type AudioProps = {
17
+ voice?: string; // staticFile, ex.: "editorial/voice.wav"
18
+ music?: string; // cama contínua
19
+ musicVolume?: number; // padrão 0.2
20
+ sfx?: SfxDef[]; // em ordem de aparição na fala; no máximo ~1 a cada 10 s
21
+ };
22
+
23
+ export type EditorialReelProps = {
24
+ words: WordsData;
25
+ scenes: SceneDef[];
26
+ theme?: Partial<EditorialTheme>;
27
+ audio?: AudioProps;
28
+ overlap?: number; // s de sobreposição entre cenas
29
+ tailSeconds?: number; // respiro depois da última palavra
30
+ };
31
+
32
+ export const exampleScenes: SceneDef[] = [
33
+ { kind: "hook", at: "I", a: "I shipped an app", b: "in one weekend." },
34
+ { kind: "prompt", at: "Here", a: "I only tell", b: "the agent what I want.", prompt: "build the signup page", marks: { typed: "want" }, typedMark: "typed" },
35
+ { kind: "phone", at: "Look", scale: 1 },
36
+ {
37
+ kind: "numbers", at: "In", chip: "48 hours",
38
+ marks: { c1: "31", c2: "12" },
39
+ counters: [{ to: 31, label: "commits", mark: "c1" }, { to: 12, label: "tests", mark: "c2" }],
40
+ },
41
+ { kind: "steps", at: "Three", prompt: "three agents", items: ["writes", "tests", "reviews"], marks: { typed: "agents" }, typedMark: "typed" },
42
+ {
43
+ kind: "doc", at: "First", title: "spec.md", altTitle: "agent coding",
44
+ marks: { code: "then", review: "code" },
45
+ pages: [
46
+ { lines: ["# Signup page", "", "- email and password", "- validate on submit", "- show errors inline", "- redirect to dashboard"] },
47
+ { mono: true, mark: "code", lines: ["export async function signUp(", " input: SignUpInput", ") {", " const data = schema.parse(input)", " return createUser(data)", "}"] },
48
+ ],
49
+ reviewers: { labels: ["correctness", "security", "regression", "scope"], mark: "review" },
50
+ },
51
+ { kind: "cta", at: "Comment", a: "Comment", b: "SHIP", keyword: "SHIP", footer: "and I send you the prompt", hideCaptions: true },
52
+ ];
53
+
54
+ export const exampleProps: EditorialReelProps = {
55
+ words: exampleWords as WordsData,
56
+ scenes: exampleScenes,
57
+ // Áudio entra por staticFile (arquivos em public/): descomente e troque os nomes.
58
+ // audio: { voice: "editorial/voice.wav", music: "editorial/music.mp3", sfx: [{ atWord: "Here", offset: -0.15, src: "editorial/swoosh.mp3", volume: 0.16 }] },
59
+ };
60
+
61
+ export const exampleSeconds = totalSeconds(exampleProps.words);
62
+
63
+ export const EditorialReel: React.FC<EditorialReelProps> = ({
64
+ words, scenes, theme, audio, overlap = 0.12, tailSeconds = 1.4,
65
+ }) => {
66
+ const merged = useMemo(() => mergeTheme(theme), [theme]);
67
+ const end = totalSeconds(words, tailSeconds);
68
+ const timed = useMemo(() => buildScenes(words.words, scenes, { end, overlap }), [words, scenes, end, overlap]);
69
+ const ctaStart = timed.find((s) => s.def.kind === "cta" && s.def.hideCaptions)?.from;
70
+ const sfx = useMemo(
71
+ () =>
72
+ (audio?.sfx ?? []).reduce<{ cursor: number; items: { from: number; def: SfxDef }[] }>(
73
+ (acc, def) => {
74
+ const t = at(words.words, def.atWord, acc.cursor);
75
+ return { cursor: t, items: [...acc.items, { from: Math.max(0, t + (def.offset ?? 0)), def }] };
76
+ },
77
+ { cursor: 0, items: [] },
78
+ ).items,
79
+ [audio, words],
80
+ );
81
+ const endFrame = toFrames(end);
82
+ const fadeOutFrame = toFrames(end - 2);
83
+ const musicVolume = audio?.musicVolume ?? 0.2;
84
+ return (
85
+ <ThemeContext.Provider value={merged}>
86
+ <AbsoluteFill>
87
+ <Backdrop />
88
+ {audio?.music ? (
89
+ <Audio
90
+ src={staticFile(audio.music)}
91
+ volume={(f) => interpolate(f, [0, 15, fadeOutFrame, endFrame], [0, musicVolume, musicVolume, 0], clamp)}
92
+ />
93
+ ) : null}
94
+ {audio?.voice ? <Audio src={staticFile(audio.voice)} /> : null}
95
+ {timed.map((s, i) => (
96
+ <Sequence key={i} from={toFrames(s.from)} durationInFrames={Math.max(1, toFrames(s.dur))}>
97
+ <Stage dur={s.dur}>
98
+ <SceneView scene={s.def} ctx={{ dur: s.dur, mark: (name, fb = 0) => (name ? s.marks[name] ?? fb : fb) }} />
99
+ </Stage>
100
+ </Sequence>
101
+ ))}
102
+ {sfx.map((s, i) => (
103
+ <Sequence key={`sfx${i}`} from={toFrames(s.from)} durationInFrames={toFrames(s.def.durationSec ?? 1)}>
104
+ <Audio src={staticFile(s.def.src)} volume={s.def.volume ?? 0.16} />
105
+ </Sequence>
106
+ ))}
107
+ <Captions words={words.words} hideFrom={ctaStart === undefined ? undefined : ctaStart} />
108
+ </AbsoluteFill>
109
+ </ThemeContext.Provider>
110
+ );
111
+ };