Compose
Cet atome gère l'intégralité du pipeline d'assemblage vidéo. Tu reçois les outputs bruts de l'étape capture et tu produis un artefact unique et poli. Suis le pipeline ci-dessous étape par étape.
Inputs
L'étape de commande ou capture devrait avoir fourni une passation avec deux sections :
Mécanique (structurée)
- clips: chemins vers les enregistrements bruts (.cast, .mp4, .webm, .png)
- driver: tuistory | true-input | agent-browser
- layout:
single|side-by-side - labels: texte pour chaque clip (ex. "AVANT (dev)", "APRÈS (PR)")
- speed: multiplicateur (défaut 3x)
- fidelity:
auto|compact|standard|inspect(optionnel ; auto => side-by-side=inspect, single=standard) - title: texte de la carte titre
- subtitle: résumé d'une phrase
- sections: bannières texte pour les chapitres
[{t, title}](optionnel) - keys: événements frappe clavier
[{t, label, dur?}](si surimpression demandée) - showcase: nom du preset --
macos,minimal,hero,presentation,factory,factory-hero - effects tier:
utilitarian|full|none(voir « Choisir les effets au moment de la composition » ci-dessous) - output: chemin de sortie désiré
Créatif (langage naturel)
Orientation en texte libre sur ce qu'il faut mettre en avant : quels moments maintenir, ce que la carte titre doit transmettre, si des cartes de phase sont justifiées, comment découper le temps mort. Utilise ceci -- ainsi que le tier d'effets -- pour les décisions éditoriales, incluant le choix d'effets spécifiques à appliquer.
Pipeline
1. Construire les props → construire les props JSON Showcase
2. Rendu → render-showcase.sh (convertit .cast, prépare les clips, rend, nettoie)
3. Finaliser → vérifier et sortir
Remotion gère toute la composition en un seul passage — cartes titre, transitions, chrome de fenêtre, arrière-plans, surimpressions de touches, projecteurs, particules, bruit et correction de couleur sont tous automatiques. Tu construis le JSON props ; le moteur fait le reste.
Projet Remotion et script helper
REMOTION_DIR=${DROID_PLUGIN_ROOT}/remotion
RENDER=${DROID_PLUGIN_ROOT}/scripts/render-showcase.sh
Mode Showcase vs mode Demo
Les deux utilisent le même pipeline Remotion mais ciblent des registres visuels différents.
| Showcase | Demo | |
|---|---|---|
| Objectif | Matériel marketing cinématographique et hautement poli | Démonstration claire et utilitaire — simple ou comparative, selon l'histoire |
| Preset | factory, factory-hero, ou hero |
macos, minimal, ou presentation |
| Tier d'effets | Full -- projecteur, zoom, callout, surimpression de touches. Vas-y à fond. | Utilitarian -- zoom pour la lisibilité, surimpression de touches pour les actions utilisateur |
| Audience | Externe — landing pages, réseaux sociaux, marketing | Interne — révisions de PR, docs, QA |
Règle de décision : Si la vidéo sera vue en dehors de l'équipe eng, utilise le mode Showcase. Si c'est pour une description de PR, une démo interne ou un embed de documentation, utilise le mode Demo. Les couches de polish visuel (lueur chaude, particules, correction de couleur, motion blur) sont toujours présentes mais leur intensité est pilotée par la palette — les presets Factory produisent une chaleur cinématographique riche tandis que les presets Catppuccin restent subtils et froids.
Choisir les effets au moment de la composition
L'étape de commande a confirmé un effects tier (utilitarian, full, ou none). Maintenant que tu as les enregistrements réels, choisis les effets spécifiques :
- Utilitarian : Ajoute des effets de zoom pour tout texte petit ou difficile à lire. Ajoute la surimpression de touches si les actions utilisateur ont été capturées. Saute le projecteur et le callout sauf si quelque chose est réellement difficile à trouver à l'écran.
- Full : Utilise la palette complète. Mets en avant les points de preuve clés. Zoome sur les détails. Ajoute des annotations callout où l'UI n'est pas auto-explicative. Applique la surimpression de touches partout. Vise le cinématographique -- le spectateur devrait se sentir guidé, pas obligé de scanner.
- None : Passe
"effects": []dans props. La surimpression de touches est quand même autorisée si confirmée séparément.
Étape 1 : Choisir la fidélité et le rythme
render-showcase.sh sélectionne automatiquement inspect pour les layouts side-by-side et standard pour les layouts mono-clip quand fidelity est omis ou défini à auto.
| Fidélité | Taille de sortie par défaut | Encodage Remotion | Surimpressions polish | Meilleur pour |
|---|---|---|---|---|
compact |
1920x1080 | H.264 CRF 21, frames JPEG | grain complet + grade | Petits embeds |
standard |
1920x1080 | H.264 CRF 18, frames JPEG | grain réduit + grade | Démos mono-panel |
inspect |
2560x1440 | H.264 CRF 14, frames PNG | grain minimal + grade | Comparaisons side-by-side / texte minuscule |
Comportement de conversion .cast
render-showcase.sh convertit les inputs .cast via agg -> gif -> ffmpeg -> mp4 avant le rendu Remotion, en utilisant les cols/rows du asciicast et les métriques de police fixes pour que les positions des éléments restent stables sur les profils de fidélité.
CRITIQUE : agg remplace TOUTES les 16 couleurs ANSI par la palette du thème. Le script de rendu utilise un thème Droid CLI personnalisé. Si tu lances agg manuellement, ne jamais omettre --theme et ne jamais utiliser les thèmes intégrés comme monokai ou dracula.
Le flag --theme accepte une chaîne hex séparée par des virgules (pas de préfixe #) : bg,fg,color0..color7 (10 valeurs) ou bg,fg,color0..color7,color8..color15 (18 valeurs pour les variantes lumineuses).
Note : les enregistrements tuistory de la Droid CLI émettent généralement AUCUN code d'échappement de couleur -- la CLI utilise le rendu direct d'Ink qui ne produit pas de séquences SGR ANSI standard dans la sortie cast. Le bg du thème (première valeur : 181818) et fg (deuxième valeur : e0d0c0, blanc chaud) sont les seules couleurs qui affecteront la sortie. Le fg blanc-chaud évite l'aspect froid bleu-gris des thèmes par défaut.
Pour d'autres terminaux qui ÉMETTENT des codes de couleur ANSI, construis la chaîne de thème complète à partir des paramètres de couleur réels du terminal.
Rythme : Cible la durée vidéo finale, pas un facteur de vitesse. Un multiplicateur aveugle rend soit le texte illisible, soit laisse du silence.
| Type de démo | Durée cible | Pourquoi |
|---|---|---|
| Fonctionnalité simple, 3-5 étapes | 30-45s | Le spectateur regarde le tout d'une traite |
| Comparaison avant/après, side-by-side | 45-75s | Chaque panel a besoin de temps pour se poser ; les contrastes figé-vs-actif ont besoin d'une pause |
| Flux multi-phase ou complexe | 60-120s | Les cartes de phase donnent au spectateur des points de réinitialisation ; se presser en défait l'intérêt |
Définis le prop speed pour atteindre la cible : si l'enregistrement brut dure 3 minutes et la cible est 60s, utilise "speed": 3. S'il dure déjà 40s brut, utilise "speed": 1 ou découpe le temps mort à la place. Découpe d'abord, accélère ensuite -- coupe les pauses de réflexion LLM, les attentes de build et les délais réseau du .cast avec asciinema cut ou en scindant les segments, puis applique une accélération douce seulement si tu es toujours au-dessus de la cible.
Ajustement du timing des touches : Si une liste de frappe a été émise pendant la capture, ses timestamps sont en temps d'enregistrement brut. Quand tu appliques un multiplicateur de vitesse, tu dois diviser chaque timestamp par le facteur de vitesse avant de le passer aux props Remotion. Une frappe à t=6.0s brut dans une vidéo 3x devrait apparaître à t=2.0s.
Clips non-.cast
Les clips .mp4, .webm et .png sont passés à Remotion inchangés sauf préparation dans public/. Re-encode les clips non-.cast manuellement seulement si leur format pixel ou dimensions sont invalides.
Rapport d'aspect du clip (vérification obligatoire pour les captures navigateur)
À la sortie 1920×1080 par défaut avec les marges du preset factory, les panels ressortent à environ :
| Layout | Aspect du panel |
|---|---|
single |
~1760×920 (≈16:9 paysage) |
side-by-side |
~872×920 par panel (≈8:9, quasi-carré / léger portrait) |
Les conversions .cast ciblent l'aspect du panel automatiquement. Les clips .mp4 / .webm pré-enregistrés ne le font pas -- si l'aspect du clip ne correspond pas à l'aspect du panel, le clip aura des bandes noires (avec le défaut objectFit: "contain") ou sera recadré (avec "cover").
Pièce commune : les captures navigateur sont généralement 16:9 paysage (ex. 1280×720). Mises dans un layout side-by-side elles se rendent comme une bande fine avec d'énormes barres noires au-dessus et au-dessous.
Deux solutions, par ordre de priorité :
- Re-capture à une viewport friendly-panel -- reviens à l'étape capture et définis viewport à ~960×1000 pour
side-by-side, ~1280×720 poursingle. - Passe
"objectFit": "cover"dans props -- recadre les bords du clip pour remplir le panel. Acceptable quand l'UI pertinente est centrée et les bords sont dépensables. Pas acceptable si le contenu recadré importe (ex. UI sidebar coupée).
Les clips .cast ont rarement besoin de ceci puisque leur aspect rendu dérive de cols/rows ; c'est presque toujours une préoccupation de capture navigateur.
Point de contrôle de durée (obligatoire, avant de procéder)
Vérifie si le facteur de vitesse planifié produit une durée finale dans la plage cible du tableau de rythme :
final_duration = clip_duration / speed_factor
| Si final_duration est... | Action |
|---|---|
| Dans la plage cible | Procède |
| Sous le minimum | Réduis le facteur de vitesse jusqu'à ce que la cible soit atteinte. Si même à 1x le clip est sous le minimum, l'enregistrement est trop court -- reviens à capture et ajoute plus d'étapes d'interaction. |
| Au-dessus du maximum | Découpe d'abord le temps mort (asciinema cut), puis augmente le prop speed |
Ce point de contrôle n'est pas optionnel. Une vidéo qui sort de la plage cible échoue la vérification.
Étape 2 : Construire les props
Choisir le layout
Par défaut : single. Un clip de l'état cible/final. Les nouvelles fonctionnalités, les preuves de correction de bug, les walkthroughs et les héros README appartiennent tous à celui-ci.
Utilise side-by-side seulement quand l'histoire est fondamentalement une comparaison : régression (cassé vs réparé), refactorisation préservant le comportement, ou une demande utilisateur explicite. Ne fabrique jamais un clip « avant » pour justifier la forme side-by-side.
Sauvegarde le JSON showcaseSchema dans un fichier temporaire :
DEMO_TMP="$(mktemp -d /tmp/droid-demo-XXXXXX)"
PROPS="${DEMO_TMP}/showcase-props.json"
cat > "$PROPS" << 'EOF'
{
"clips": ["demo.cast"],
"layout": "single",
"fidelity": "auto",
"labels": [],
"speed": 3,
"title": "PR #11621 — Prevent session freezes",
"subtitle": "Bash Mode blocks interactive commands and supports ESC cancellation",
"preset": "factory",
"keys": [
{"t": 2.0, "label": "vim"},
{"t": 5.5, "label": "sleep 100"},
{"t": 8.0, "label": "Esc"}
],
"sections": [],
"effects": [],
"speedNote": "3x speed",
"windowTitle": "droid demo"
}
EOF
Pour un flux de comparaison, remplace "clips" par deux chemins, "layout" par "side-by-side", et peuple "labels" (ex. ["AVANT (main)", "APRÈS (PR #11621)"]).
Utilise un chemin props à portée de run comme $PROPS ; ne réutilise pas un /tmp/showcase-props.json global dans les rerenders ou démos concurrentes.
CRITIQUE : gestion de clipDuration. Le script de rendu auto-détecte la durée du clip via ffprobe quand clipDuration est omis des props. Si tu le définis manuellement, il doit correspondre à la durée réelle du clip ou tu obtiens des frames blanches (trop long) ou une troncature (trop court). En cas de doute, omets-le et laisse le script auto-détecter.
Référence des props
| Prop | Type | Requis | Description |
|---|---|---|---|
clips |
string[] |
oui | Noms de fichiers (basenames seulement -- le script de rendu gère la préparation) |
layout |
"single" \| "side-by-side" |
oui | Layout de composition |
labels |
string[] |
oui | Labels pour chaque clip (visibles en side-by-side ; passe [] pour single) |
fidelity |
"compact" \| "standard" \| "inspect" |
non | Profil de qualité/compression de sortie. Omets pour auto-sélection par layout. |
speed |
number |
non | Vitesse de lecture pour la conversion .cast -> agg. |
title |
string |
oui | Heading de la carte titre |
subtitle |
string |
oui | Subheading de la carte titre |
preset |
nom de preset | oui | Preset visuel -- voir tableau ci-dessous |
keys |
Keystroke[] |
oui | Événements surimpression de touches (passe [] pour aucun) |
sections |
Section[] |
non | Bannières de section pour marquer les chapitres (passe [] pour aucun) |
effects |
Effect[] |
oui | Timeline d'effets (passe [] pour aucun) |
clipDuration |
number |
non | Durée du clip en secondes. Auto-détectée par le script de rendu si omise. |
speedNote |
string |
non | Affiché sur la carte titre (ex. "3x speed") |
windowTitle |
string |
non | Texte dans la barre de titre de la fenêtre |
width |
number |
non | Largeur de sortie (défaut : 2560 pour inspect, sinon 1920) |
height |
number |
non | Hauteur de sortie (défaut : 1440 pour inspect, sinon 1080) |
objectFit |
"contain" \| "cover" \| "fill" |
non | Comment chaque clip s'adapte à son panel. Par défaut "contain" (letterbox pour préserver l'aspect). Utilise "cover" quand l'aspect du clip ne correspond pas à l'aspect du panel et tu préfères recadrer plutôt que voir des barres noires. Voir « Rapport d'aspect du clip » ci-dessous. |
codeAnnotations |
CodeAnnotation[] |
non | Overlays de code chronométrés avec coloration syntaxique affichés pendant la séquence de contenu principal. Voir « Annotations de code » ci-dessous. |
transitionStyle |
"motion-blur" \| "flash" \| "whip-pan" \| "light-leak" \| "glitch-lite" |
non | Présentation utilisée pour les transitions titre→contenu et contenu→outro. Par défaut "motion-blur" préserve l'esthétique existante. Voir « Styles de transition » ci-dessous. |
Référence rapide des presets
| Preset | Look | Palette | Meilleur pour |
|---|---|---|---|
factory |
Bg noir chaud, feux tricolores, 12px radius, 80px margin | Factory (chaud) | Contenu Factory officiel |
factory-hero |
Identique à factory + gradient bg | Factory (chaud) | Pages landing Factory, réseaux sociaux |
hero |
Gradient bg, marges généreuses, ombre proéminente | Catppuccin (froid) | Marketing non-Factory |
macos |
Bg sombre, feux tricolores, frame propre | Catppuccin (froid) | Démos générales |
presentation |
Bg noir, marges généreuses | Catppuccin (froid) | Decks de présentation, talks |
minimal |
Pas de barre de fenêtre, tiny radius, marges serrées | Catppuccin (froid) | Embeds docs, clips inline |
Schéma de frappe clavier
{ t: number, label: string, dur?: number }
t: Temps en secondes relatif au début du clip (pas la carte titre). Ajuste pour le facteur de vitesse.label: Texte d'affichage (ex."Ctrl+C","vim main.rs")dur: Durée d'affichage en secondes (défaut : 1.2s). Auto-coupée quand la frappe suivante commence.
Schéma de section
{ "t": 2.0, "title": "Testing basic echo" }
t: Temps en secondes relatif au début du clip (pas la carte titre). Ajuste pour le facteur de vitesse.title: Texte d'affichage pour le header de section. Reste visible jusqu'au début de la section suivante.
Types d'effet
| Effet | Props | Description |
|---|---|---|
fade-in |
t, dur |
Fondu au noir |
fade-out |
t, dur |
Fondu vers le noir |
zoom |
t, dur, to: {x,y,w,h} |
Zoom dirigé vers une région cible (30% in, 40% hold, 30% out) |
spotlight |
t, dur, on: {x,y,w,h}, dim? |
Assombrit tout sauf une région (dim : 0–1, défaut 0.6) |
callout |
t, dur, text, at: {x,y} |
Surimpression texte ancrée à un point |
Les régions utilisent des chaînes de pourcentage (ex. "25%") relatives aux dimensions vidéo.
Quand utiliser les effets
| Effet | Utilise quand… | Ne pas utiliser quand… |
|---|---|---|
spotlight |
Attirer l'attention sur une région spécifique (erreur, changement de statut) | Toute la frame est pertinente |
zoom |
Texte petit ou détail difficile à lire à pleine échelle | Le contenu est déjà lisible |
callout |
Annoter quelque chose que le spectateur ne reconnaît peut-être pas | L'UI est auto-explicative |
| Surimpression de touches | Montrer les actions utilisateur (saisie, appuis sur touches) | Aucune interaction utilisateur dans le clip |
Moins c'est plus. Un spotlight bien chronométré a plus d'impact que cinq effets qui se chevauchent.
Annotations de code
Carte de code chronométrée avec coloration syntaxique superposée sur la vidéo capturée. Utilise pour les démos PR où le changement source décisif a besoin de s'asseoir à côté de la preuve d'exécution.
| Champ | Type | Requis | Description |
|---|---|---|---|
t |
number |
oui | Temps de début en secondes, relatif au début du clip. Ajuste pour le facteur speed (même règle que keys[].t). |
dur |
number |
oui | Combien de temps la carte reste visible, en secondes. |
code |
string |
oui | Texte source ; \n pour multilignes ; pas de newline finale. |
language |
string |
non | Identifiant de langage Prism (tsx, ts, py, rust, bash, ...). Défaut tsx. |
title |
string |
non | Petite caption au-dessus du code (généralement un chemin de fichier). |
highlight |
[{start,end}] |
non | Plages de lignes 1-indexed inclusives avec fond accentué + bordure gauche. |
focus |
[{start,end}] |
non | Plages de lignes 1-indexed inclusives gardées à opacité complète ; les autres sont assombries/floutées. |
position |
"top-right" \| "center" \| "bottom-left" |
non | Par défaut "top-right". Déplace à "bottom-left" si le haut-droit capturé est chargé. |
Garde-le court -- vise ≤ 15 lignes par carte, maintiens-le pendant 3–6 secondes.
Styles de transition
transitionStyle sélectionne la présentation du crossfade titre→contenu et contenu→outro. Les deux transitions dans un même rendu partagent le même style. flash et light-leak tirent leur teinte de la palette du preset. Par défaut motion-blur est toujours sûr ; les conseils au tier des presets vivent dans showcase/SKILL.md.
| Style | Sensation | Utilise quand… |
|---|---|---|
motion-blur |
Dolly subtile, blur + opacity crossfade | Par défaut pour démos PR, contenu Factory, la plupart du travail showcase |
flash |
Flash rapide teinté de palette au milieu | Preuves de correction de bug où l'état « après » devrait sembler soudain |
whip-pan |
Pan horizontal + motion blur | Showcase énergique / marketing quand le rythme est rapide |
light-leak |
Balayage dégradé chaud | Clips Factory-branded landing/social |
glitch-lite |
Décalage de canal RGB + bande horizontale | Preuve de sécurité/vulnérabilité, esthétique terminal ; jamais par défaut, jamais deux fois |
Étape 3 : Rendu
Utilise le script de rendu -- il gère la préparation des clips, la détection de durée, le rendu et le nettoyage :
RENDER=${DROID_PLUGIN_ROOT}/scripts/render-showcase.sh
# Rendu basique
$RENDER --props "$PROPS" --output /tmp/demo.mp4 \
/tmp/before.cast /tmp/after.cast
# Ou avec props inline (utile pour les cas simples)
$RENDER --props-inline '{"clips":["clip.mp4"],"layout":"single","labels":[],"title":"Demo","subtitle":"Test","preset":"macos","keys":[],"effects":[]}' \
--output /tmp/demo.mp4 /tmp/clip.mp4
Le script :
- Convertit les inputs
.casten.mp4en utilisant le profil de fidélité sélectionné - Copie les fichiers de clip dans
${REMOTION_DIR}/public/ - Auto-détecte
clipDurationvia ffprobe si absent des props - Exécute
npx remotion render Showcaseavec les flags d'encodage spécifiques au profil - Nettoie les clips préparés et générés
Vérification rapide de frame (sanity-check du layout avant rendu complet) :
cd ${REMOTION_DIR}
npx remotion still Showcase --props="$(cat "$PROPS")" --frame=30 --scale=0.5 /tmp/check.png
Temps de rendu : Attends ~1-3 minutes pour une vidéo de 30-60s à 1920x1080. Définis les timeouts des workers en conséquence (5 minutes est sûr).
Étape 4 : Finaliser
Vérifie le résultat :
ffprobe -v quiet -print_format json -show_format -show_streams /tmp/demo.mp4
Confirme :
- La résolution est 1920x1080 (ou correspond à la sortie attendue)
- La durée est raisonnable (pas 0s, pas des heures)
- La taille du fichier est gérable (moins de 5 MB pour les embeds GitHub, limite dure 25 MB)
- Le format pixel est yuv420p (lecture universelle)
Outputs
Transmet à l'étape verify :
## Compose outputs
- video: /tmp/demo-pr-11621.mp4
- resolution: 1920x1080
- duration: 42s
- size: 3.2 MB
- preset: factory
- keystrokes: 3 events overlaid
- effects: 1 spotlight
- engine: remotion
Artefacts screenshot-only (preuves, QA)
Pas tous les livrables sont une vidéo. Pour les workflows de preuve et QA, compose peut juste organiser les screenshots et snapshots :
Ensemble de screenshots annoté
ffmpeg -y -i before.png -i after.png \
-filter_complex "
[0:v]scale=960:-1[left];
[1:v]scale=960:-1[right];
[left][right]hstack=inputs=2[out]" \
-map "[out]" comparison.png
Rapport Markdown avec preuve embarquée
Pour les livrables textuels, organise la preuve en un rapport structuré plutôt qu'une vidéo. L'étape verify gère ceci.