compose

Par factory-ai · factory-plugins

Connaissances de base pour les workflows de contrôle de droïde — non invoqué directement. Assemblage vidéo via Remotion — cartons de titre, mise en page, transitions, effets et finitions de présentation.

npx skills add https://github.com/factory-ai/factory-plugins --skill compose

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é :

  1. Re-capture à une viewport friendly-panel -- reviens à l'étape capture et définis viewport à ~960×1000 pour side-by-side, ~1280×720 pour single.
  2. 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 :

  1. Convertit les inputs .cast en .mp4 en utilisant le profil de fidélité sélectionné
  2. Copie les fichiers de clip dans ${REMOTION_DIR}/public/
  3. Auto-détecte clipDuration via ffprobe si absent des props
  4. Exécute npx remotion render Showcase avec les flags d'encodage spécifiques au profil
  5. 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.

Skills similaires