HyperFrames-Gradle : Trasforma l'AsciiDoc in Video MP4
Pubblicato il 31 May 2026
Due settimane fa, ho lanciato`capsule-gradle`— un plugin che trasforma un deck reveal.js in una capsula video WebM con TTS. Oggi, aggiungo un nuovo plugin: hyperframes-gradle. Fa qualcosa che capsule-gradle non fa : trasformare l’AsciiDoc direttamente in video MP4, senza passare da reveal.js.
Ecco perché questo plugin esiste, come si articola con l’esistente, e ciò che il motore HyperFrames di HeyGen apporta al pipeline di produzione del contenuto
il buco nel pipeline video
Il mio pipeline documentario produce due tipi di video:
-
Capsula di revisione (capsule-gradle) — un deck reveal.js catturato in WebM con
narrazione TTS. Perfetto per rivedere un modulo.
-
Slides d’animation (slider-gradle) — un deck reveal.js HTML interattivo per
Il formatore in presenza
Ma manca un terzo formato : il video standalone
caso d’uso |
Soluzione attuale |
Mancanza |
Slides formatore |
slider-gradle → reveal.js |
Capsula revisione |
capsule-gradle → WebM |
�✅ |
Video teaser |
nessuna |
❌ |
Demo tecnica animata |
nessuna |
�❌ |
Da documento a video |
Nessuna |
❌ |
Il primo use case è critico: come generare automaticamente un video di 60 secondi che presenta un progetto o un modulo, con animazioni GSAP, narration TTS, e sottofondo musicale — il tutto dallo stesso file AsciiDoc cosa le slide ?
Capsule-gradle non può rispondere: gli serve già un deck reveal.js. slider-gradle produce bene questo deck, ma non è progettato per il rendering video. Era necessario avere un pipeline direct : AsciiDoc → video.
HyperFrames : Il Motore
Ho scopertohttps://github.com/heygen-com/hyperframes[HyperFrames] il y a alcuni giorni. È un framework open-source (Apache 2.0) creato da HeyGen, l’unicorno del video IA. 22 700 stelle su GitHub. Il pitch contiene in una frase :
Scrivi HTML. Renderizza video. Realizzato per gli agenti.
Il principio: voi scrivete HTML con dei`data-*`attributi, fate riferimento un’animazione GSAP (o CSS, Lottie, Three.js…), e il motore rende tutto in MP4 via Puppeteer (Chrome headless) + FFmpeg. Risultato deterministico: stesso input, stessi frame, stesso video
<div id="stage" data-composition-id="intro" data-start="0" data-width="1920" data-height="1080">
<video class="clip" data-start="0" data-duration="6" data-track-index="0"
src="background.mp4" muted playsinline></video>
<h1 id="title" class="clip" data-start="1" data-duration="4" data-track-index="1">
Formation Docker & Kubernetes
</h1>
<audio data-start="0" data-duration="6" data-track-index="2"
data-volume="0.3" src="music.wav"></audio>
<script src="https://cdn.jsdelivr.net/npm/gsap@3/dist/gsap.min.js"></script>
<script>
const tl = gsap.timeline({ paused: true });
tl.from("#title", { opacity: 0, y: 40, duration: 0.8 }, 1);
window.__timelines = window.__timelines || {};
window.__timelines.intro = tl;
</script>
</div>
Che cosa rende HyperFrames diverso da Remotion? Nessun React, nessun bundler, senza JSX. HTML grezzo che gli agenti IA sanno scrivere nativamente. È tagliato esattamente per il mio pipeline : planner-gradle con deepseek può generare questo HTML a partire da un piano SPG/SPD, e un plugin Gradle può convertirlo in MP4.
L’architettura del plugin
hyperframes-gradle segue lo stesso pattern di tutti i miei plugin : il pattern « Plugin Indipendente + Radice Consumatore ».
hyperframes-gradle/
├── settings.gradle.kts ← racine consommateur (dogfood)
├── build.gradle.kts ← 3 lignes : apply plugin hyperframes
├── hyperframes-plugin/ ← BUILD INDÉPENDANT
│ ├── gradlew ← son propre wrapper
│ ├── build.gradle.kts ← java-gradle-plugin
│ ├── src/main/kotlin/education/cccp/hyperframes/
│ │ ├── HyperframesPlugin.kt
│ │ ├── GenerateHyperframesHtmlTask.kt
│ │ ├── RenderHyperframesTask.kt
│ │ └── HyperframesExtension.kt
│ ├── .agents/ ← gouvernance agent
│ └── doc/
│ └── HYPERFRAMES_ARCHITECTURE.adoc
└── video.yml ← configuration dogfood
Il pipeline in quattro fasi
@startuml skinparam backgroundColor #FEFEFE skinparam defaultFontSize 11 title Pipeline hyperframes-gradle rectangle "AsciiDoc\nsorgente" as adoc #LightYellow rectangle "AsciidoctorJ" as asciidoctorj #LightBlue rectangle "HTML HyperFrames\n(attr. data-*)" as html #LightGreen rectangle "HyperFrames CLI\n(Node.js 22+)" as cli #Lavender rectangle "video.mp4" as mp4 #Gold adoc -right-> asciidoctorj : parse blocs customs asciidoctorj -right-> html : génère HTML html -right-> cli : ProcessBuilder\nnpx hyperframes render cli -right-> mp4 : Puppeteer + FFmpeg @enduml
-
AsciidoctorJ analizza il sorgente AsciiDoc e estrae i blocchi personalizzati
[hyperframes-composition], [hyperframes-track], [hyperframes-animation].
-
GenerateHyperframesHtmlTask genera l’HTML HyperFrames con le
`data-*`attributi corrispondenti.
-
RenderHyperframesTask appelle la CLI HyperFrames via
ProcessBuilder:
npx hyperframes render --input index.html --output video.mp4.
-
Il MP4 è distribuito in`output/`con un`metadata.json`per l’orchestrazione.
Il DSL AsciiDoc
Il vero valore aggiunto del plugin è il DSL AsciiDoc. L’utente Non scrive mai HTML. Annota il suo documento AsciiDoc esistente:
= Formation Docker & Kubernetes
:hyperframes-width: 1920
:hyperframes-height: 1080
:hyperframes-fps: 30
[hyperframes-composition, id="intro"]
== Introduction
Le titre apparaît avec un fondu GSAP sur fond vidéo.
[hyperframes-track, index=0, start=0, duration=6]
video::assets/background.mp4[muted, playsinline]
[hyperframes-track, index=1, start=1, duration=4]
Formation Docker & Kubernetes
[hyperframes-animation, type=gsap]
const tl = gsap.timeline({ paused: true }); tl.from("#title", { opacity: 0, y: 40, duration: 0.8 }, 1); window.timelines = window.timelines || {}; window.__timelines.intro = tl;
Gli attributi docinfo`:hyperframes-width:`) configurano le dimensioni. I blocchi personalizzati definiscono le composizioni e le tracce. `[hyperframes-animation]`contiene il codice GSAP grezzo — che l’agente IA può generare a partire da una descrizione in linguaggio naturale.
Il Bridge Node.js
L’unico punto di attrito tecnico: HyperFrames è in Node.js/TypeScript, e i miei plugin sono in Kotlin/JVM. La soluzione è un`ProcessBuilder`:
class RenderHyperframesTask : DefaultTask() {
@TaskAction
fun render() {
val process = ProcessBuilder(
"npx", "hyperframes", "render",
"--input", inputHtml.absolutePath,
"--output", outputMp4.absolutePath
)
.inheritIO()
.start()
val exitCode = process.waitFor()
require(exitCode == 0) {
"HyperFrames render failed with exit code $exitCode"
}
}
}
Zero accoppiamento tra il codice JVM e Node.js. Il contratto è via riga di comando. e file su disco. È lo stesso modello che`plantuml-gradle` # Gradle Integration JBake can be integrated into Gradle builds using the JBake Gradle plugin or by calling the JBake CLI directly:
----
----
tasks.register<JavaExec>("bake") {
mainClass.set("org.jbake.launcher.Main")
classpath = configurations["jbake"]
args = listOf(projectDir.absolutePath, "$buildDir/jbake")
}`slider-gradle`(chi chiama AsciidoctorJ). Niente di nuovo sotto il sole — semplicemente applicato a uno strumento più recente.
== Due Plugins Video, Due Usi
|===
| |capsula-gradle |hyperframes-gradle |Source |Deck reveal.js (HTML esistante) |AsciiDoc → HyperFrames HTML |Caso d'uso |Capsula di revisione |Video standalone esplicativa |Natura |Diapositive → video |Documento → video |reso |Playwright Java → WebM |HyperFrames CLI → MP4 |Animazione |Transizioni reveal.js nativi |GSAP/CSS attributi data |pila |100% JVM (Kotlin) |### Integrazione Gradle
JBake può essere integrato nelle build Gradle utilizzando il plugin JBake per Gradle oppure richiamando direttamente la CLI di JBake:
----
tasks.register<JavaExec>("bake") { mainClass.set("org.jbake.launcher.Main") classpath = configurations["jbake"] args = listOf(projectDir.absolutePath, "$buildDir/jbake") }
|===
|===
|EPIC |Description |Priorità |HF-0 |Bootstrap governance + inquadramento architettura |TERMINATO |HF-1 |Plugin Gradle stub + integrazione AsciidoctorJ |P0 |HF-2 |### Integrazione Gradle JBake può essere integrato nelle build Gradle utilizzando il plugin JBake Gradle oppure chiamando direttamente la CLI di JBake:
----
tasks.register<JavaExec>("bake") { mainClass.set("org.jbake.launcher.Main") classpath = configurations["jbake"] args = listOf(projectDir.absolutePath, "$buildDir/jbake") } |P1 |HF-3 |DSL AsciiDoc abitudini (blocchi, docinfo, template) |P1 |HF-4 |Integrazione runner-gradle (metadata.json) |P2 |HF-5 |Template pronti all'uso (title-card, code-diff, captions) |P3 |HF-6 |CI + pubblicazione Maven Central / Gradle Portal |P3
|===
|===
AsciiDoc ──→ slider-gradle ──→ deck reveal.js ──→ capsule-gradle ──→ WebM (capsula ripasso)
AsciiDoc ──→ hyperframes-gradle ──→ MP4 (video standalone)
L'utente scrive un singolo file AsciiDoc. slider-gradle produce le slide. capsule-gradle produce la capsula di revisione. hyperframes-gradle produce il video di teasing e le demo tecniche.
== I 6 EPIC della Roadmap
Il plugin è strutturato in 6 EPICs:
|===
|EPIC |Descrizione |Priorità |HF-0 |Bootstrap governance + inquadramento architettura |✅ COMPLETATO |HF-1 |Plugin Gradle stub + integrazione AsciidoctorJ |P0 |HF-2 |Bridge CLI HyperFrames (ProcessBuilder → npx → MP4) |P1 |HF-3 |Personalizzazioni DSL AsciiDoc (blocchi, docinfo, template) |P1 |HF-4 |Integrazione runner-gradle (metadata.json) |P2 |HF-5 |Template pronti all'uso (title-card, code-diff, captions) |P3 |HF-6 |CI + pubblicazione Maven Central / Gradle Portal |P3
|===
La session 000 (bootstrap) è già terminata. La session 001 attaccherà HF-1 : lo stub del plugin Gradle e l'integrazione AsciidoctorJ.
== Perché Funziona
Tre ragioni:
1. *HyperFrames è "built for agents"*. La mia pipeline si basa su agenti IA
(planner-gradle + deepseek) che generano contenuti. HyperFrames accetta del HTML puro — il formato che i LLM padroneggiano meglio. Niente React, niente di JSX, nessuna curva di apprendimento per l'agente.
1. *Il DSL di AsciiDoc è naturale*. Tutto il mio ecosistema parla AsciiDoc.
slider-gradle, codex-gradle, training-gradle — consumano tutti del`.adoc`Please provide the text you would like me to translate. Aggiungere blocchi custom`[hyperframes-composition]`è un'estensione logica, non una rottura.
1. *Il bridge Node.js è un pattern collaudato*. ProcessBuilder fuori dalla JVM,
è quello che faccio già per PlantUML, Graphviz, Piper. Aggiungere HyperFrames non cambiare nulla nell'architettura — è un ulteriore strumento esterno, gestito da Gradle.
[NOTE]
====
La vera forza del pattern è che non c'è *alcuna* dipendenza npm in il build Gradle. Il plugin non importa codice Node.js. Esegue una comando shell. Se HyperFrames evolve o si rompe, il plugin non è accoppiato — basta aggiornare il comando.
====
== Conclusione: L'Anello Mancante
Con `hyperframes-gradle`, la mia pipeline video è completa. Ho:
* Le *slide interattive* (slider-gradle)
* Le *capsule di revisione* (capsule-gradle)
* I *video standalone* (hyperframes-gradle)
Tre formati. Un unico file sorgente: l'AsciiDoc.
Il prossimo obiettivo è HF-1: far compilare il plugin e generare un primi HTML HyperFrames a partire da un documento AsciiDoc annotato. La sessione 001 è già inquadrata.
hyperframes-gradle esiste. La governance è in atto. Il backlog è scritto. L'architettura è documentata. Il plugin è nato.
== Riferimenti
* xref:0124_architecture_plugin_gradle_racine_consommateur_independant_post.adoc[Articolo 0124 — L'Architettura Plugin Indipendente + Radice Consumatore]
* https://github.com/heygen-com/hyperframes[HyperFrames — GitHub]
* https://hyperframes.heygen.com/introduction[HyperFrames — Documentazione]
* `foundry/public/hyperframes-gradle/hyperframes-plugin/doc/HYPERFRAMES_ARCHITECTURE.adoc`— Architettura dettagliata