gfx-html

À utiliser lorsque l'utilisateur demande un graphique à l'écran, un titre ou une superposition. Couvre le contrat de template HTML, le rendu Chrome headless, l'encodage vidéo avec préservation de la transparence, et le bloc overlays dans project.json.

Qu'est-ce que gfx-html ?

Ce skill permet de créer des graphiques animés (comme des bandeaux, titres, incrustations) rendus par Chrome headless, image par image, puis encodés en une vidéo avec transparence. Il est conçu pour les plateformes d'édition vidéo comme celles utilisant le format de projet décrit dans `project.json`. L'approche repose sur une règle fondamentale : chaque graphique est une fonction pure du temps (`?t=<secondes>`), garantissant un rendu déterministe, mise en cache efficace, et une intégration fluide dans le flux de montage. Le skill explique comment choisir ou écrire un template HTML, le rendu avec Chrome (en s'assurant de la transparence via le flag `--default-background-color=00000000`), l'encodage au format QuickTime RLE (qtrle) pour préserver l'alpha, et l'ancrage des overlays dans la timeline via des `segment_id` et `offset_frames` plutôt que des positions absolues. Il inclut également des étapes de vérification (sonde alpha, détection de page blanche, test de déterminisme) pour garantir un rendu correct.

Compatible avec~Claude Code~Codex CLI~Cursor
npx skills add https://github.com/businessbaum/vedit/tree/HEAD/.agents/skills/gfx-html

Installed? Explore more Développement et programmation skills: steipete/bluebubbles, steipete/eightctl, steipete/blucli · View all 6 →

Demander à votre IA préférée

Ouvre une nouvelle conversation avec cette compétence d'agent déjà préchargée.

Documentation

Que fait gfx-html ?

Les graphiques sont des pages HTML rendues une image à la fois par Chrome headless et encodées en une vidéo avec transparence. La requête complète se trouve dans $ARGUMENTS ; le premier token est généralement le slug du projet. Les noms de verbes suivent le contrat de build ; python engine/ve.py --help l'emporte en cas de conflit.

La règle qui fait fonctionner ce canal

Chaque graphique est une fonction pure de ?t=<secondes>. Pas d'animations CSS, pas de timelines GSAP, pas de requestAnimationFrame, pas de transitions. La page lit t depuis location.search, calcule sa mise en page et rend une seule image statique.

Ce n'est pas une préférence de style. chrome --headless --screenshot se déclenche "quand la page semble chargée" - il n'y a pas d'horloge virtuelle dans ce build, pas de Emulation.setVirtualTimePolicy, pas de beginFrame. Une page animée par CSS échantillonnée de cette façon renvoie un pixel différent à chaque exécution. Cela détruit la mise en cache par contenu (même clé, pixels différents) et produit un saccade visible.

Une fonction pure de t échantillonnée par la même boucle naïve est déterministe et mise en cache possible, et c'est toute la différence.

Le budget, annoncé d'avance

Chrome rend à ~430-470 ms par image en régime permanent, avec un démarrage à froid de 7,9 s. Un graphique de 5 secondes à 30 images/s donne 150 images, soit environ 65 secondes en mono-thread. Le moteur parallélise quatre processus Chrome et met en cache les PNG par image, donc un graphique inchangé se re-rend gratuitement - mais un premier rendu n'est pas instantané.

Informez l'utilisateur du nombre d'images et de l'estimation avant de lancer un long rendu. Privilégiez les graphiques courts : un bandeau bas qui tient 6 secondes n'a pas besoin de 6 secondes d'animation, il lui faut 0,4 s d'entrée, un maintien, et 0,4 s de sortie.

Étape 1 - choisir ou écrire un template

Les templates se trouvent dans presets/graphics/*.html. Réutilisez avant d'écrire. Un template est un seul fichier HTML autonome qui :

  • Lit t (secondes) et ses propriétés depuis location.search.
  • Calcule toutes les positions/opacités à partir de t avec de l'arithmétique simple. Les fonctions d'easing sont acceptables - elles sont pures.
  • Rendu sur un corps transparent. Ne jamais peindre une couleur de fond.
  • Utilise les polices de assets/fonts/ via @font-face avec une URL file:///. Ne pas compter sur les polices système - ce que Chrome résout sur cette machine n'est pas ce que le contrôle de rendu QC suppose.
  • Est lisible en 1920x1080 et à l'intérieur de la zone de sécurité des sous-titres de la plateforme. Vérifiez output.platform : un bandeau bas qui se trouve dans le tiers inférieur est sous l'UI TikTok.

Les détails de mise en page, les conventions d'easing et le contrat de propriétés sont dans docs/graphics.md. Ne les répétez pas ici.

Étape 2 - rendre les images

python engine/ve.py gfx <slug>

Pour chaque image, le moteur invoque Chrome comme une liste d'arguments, approximativement :

chrome.exe --headless=new --disable-gpu --no-sandbox --hide-scrollbars
           --user-data-dir=<win-path>\prof --default-background-color=00000000
           --window-size=1920,1080 --screenshot=<win-path>\frame_0007.png
           "file:///C:/.../template.html?t=0.233&title=..."

Deux flags portent tout le canal :

  • --default-background-color=00000000 est ce qui produit un véritable alpha. Vérifié : le pixel du coin lit 0 0 0 0. Sans cela, vous obtenez un noir opaque et la superposition couvre la vidéo.
  • L'URL file:/// doit être un vrai chemin Windows. Un chemin Git Bash $PWD échoue silencieusement - trois fichiers HTML différents ont produit le même md5 d'une page d'erreur vierge en mode sombre, exit 0, taille de fichier plausible. C'est pourquoi le contrôle QC (g) compare avec le hash de page vierge connu.

Les images sont mises en cache sous .cache/html/ indexées par sha256(html_content + t + params + renderer + renderer_version). Modifier le template invalide chaque image, ce qui est correct.

Étape 3 - encoder avec alpha intact

Même commande. ve gfx rend les images et les encode en un seul passage - il n'y a pas de verbe d'encodage séparé, car une séquence PNG sans film à côté est un artefact à moitié fini que rien en aval ne peut utiliser.

Séquence PNG vers QuickTime RLE (qtrle, argb). ProRes 4444 et une séquence PNG brute préservent également l'alpha correctement. VP9 dans WebM ignore silencieusement l'alpha dans ce build, même avec -metadata alpha_mode=1 - ne l'utilisez pas, et ne l'acceptez pas non plus d'un fichier utilisateur sans sondage.

La sortie va dans .cache/ovl/<xx>/<key>.mov avec un fichier annexe <key>.json.

Étape 4 - ancrer la superposition

Écrivez l'entrée overlays[] : id, kind: "html", template, props, anchor, duration_frames, layer, render.

anchor est toujours {segment_id, offset_frames} - jamais une position absolue dans la timeline. Les positions dans la timeline sont dérivées d'une somme cumulée sur les segments activés. Dès que quelqu'un désactive un segment en amont, chaque position absolue est erronée et chaque graphique dérive. Une superposition ancrée se déplace automatiquement avec son segment, ce qui est le comportement attendu par un monteur.

layer ordonne la composition ; plus haut est au-dessus. Les sous-titres sont rendus en dernier et se placent au-dessus des graphiques.

Étape 5 - vérifier avant de déclarer terminé

Trois vérifications, toutes peu coûteuses :

  1. Sonde alpha. Lisez un pixel rgba brut depuis une image de coin et vérifiez que a = 0. QC (d).
  2. Vérification de page blanche. Vérifiez que les images ne sont pas chacune une couleur unie. QC (g). Cela détecte une mauvaise URL file:///, une police qui n'a pas pu se charger et une erreur JS, situations qui sinon sortent avec un exit 0 et un fichier plausible. Elle échantillonne la première, la troisième et la troisième-quart images et échoue seulement si toutes sont uniformes - chaque template ici s'ouvre à opacité 0, donc l'image 0 d'un graphique fonctionnel est légitimement vierge et juger uniquement sur celle-ci ferait échouer un rendu correct.

ve gfx exécute les deux automatiquement sur ce qu'il vient de produire et sort avec exit 2 si l'une ou l'autre échoue. 3. Déterminisme. Rendez une image deux fois et comparez les hashs. S'ils diffèrent, quelque chose dans le template n'est pas une fonction pure de t - trouvez-le. Cette vérification existe car l'échec qu'elle détecte est invisible jusqu'à ce qu'un rendu mis en cache soit en désaccord avec un rendu frais.

Ensuite, montrez à l'utilisateur une image fixe au point médian. Ne décrivez pas un graphique en prose quand une capture d'écran est à une commande.

Modes d'échec

SymptômeCauseCorrection
La superposition couvre la vidéo en noir--default-background-color manquant, ou le template peint un fondAjoutez le flag ; rendez le corps transparent
Chaque image identique, taille de fichier plausiblePage d'erreur vierge due à une mauvaise URL file:///Utilisez un vrai chemin Windows ; testez par rapport au hash de page vierge
Même clé, pixels différents entre exécutionsLe template n'est pas une fonction pure de tSupprimez toute animation CSS et rAF
Le texte s'affiche avec une police incorrectePolice système de secours@font-face depuis assets/fonts/ avec une URL file:///
Alpha perdu après encodageVP9/WebMUtilisez qtrle argb ou ProRes 4444
Le graphique dérive après une modification en amontAncrage absolu dans la timelineAncrez à {segment_id, offset_frames}
Le rendu prend des minutes430 ms/image est le minimumRaccourcissez le graphique ou acceptez-le ; le cache rend la deuxième exécution gratuite

Individual skills in this repo

This repo contains 4 individual skills — each has its own dedicated page.

businessbaum/vedit

This skill should be used when the user asks for subtitles or on-screen text of what is being said - "brenn die Untertitel ein", "mach Untertitel", "Untertitel drauf", "leg Untertitel drüber", "brenn die Subs ein", "mach die Captions", "Wort für Wort Untertitel", "Karaoke-Untertitel", "korrigier den Untertitel-Text", "burn in the captions", "add subtitles", "burn the subs", "word-by-word captions", "fix a caption typo". Covers ASS generation, the pixel-width grouping rule for German, the font table, and burn-in through libass.

businessbaum/vedit

This skill should be used when the user asks for mechanical tightening of material that is already selected - "mach den Rohschnitt", "bau den Rohschnitt", "schneid die Ähms raus", "nimm die Füllwörter raus", "entferne die Pausen", "kürz die Pausen", "schneid die Versprecher raus", "mach den Radioschnitt", "straff das", "build the rough cut", "cut the fillers", "remove the ums", "tighten the pauses", "trim the dead air", "cut the retakes", "radio edit". Turns kept story blocks into enabled segments in project.json. Does not decide what the video is about - that is cut-story.

businessbaum/vedit

This skill should be used when the user asks to decide what the video is actually about before any mechanical cutting - "bau die Story", "was soll ins Video", "was kann raus", "welche Themen bleiben", "kürz das auf 6 Minuten", "mach daraus ein kurzes Video", "wo ist der Hook", "sortier die Themen", "worum geht es hier eigentlich", "build the story", "decide what stays", "structure this", "cut this down to N minutes", "find the hook", "reorder the topics". This is the CONTENT-selection pass that writes the "story" block in project.json; it runs after transcribe and before cut-rough.

businessbaum/vedit

This skill should be used when the user asks for the finished video file - "exportier das", "render das fertig", "mach das final", "renderte das Video", "mach den Endexport", "bau das Video zusammen", "mach es fertig für YouTube", "gib mir die MP4", "export the final", "render the final cut", "make the master", "export for TikTok", "give me the mp4". Covers segment rendering with the cache, guarded concat, the one-pass audio, loudness normalisation and the single mux.

Skills associés