Contrato de autoría de presentación de diapositivas
Una presentación de diapositivas HyperFrames es una composición HyperFrames normal (escenas, clips, líneas de tiempo GSAP) con un ingrediente adicional: una isla JSON que declara qué escenas son diapositivas y cómo se conectan. El SlideshowController del reproductor lee la isla y convierte la línea de tiempo GSAP continua en un deck discreto y navegable.
Lee /hyperframes-core primero para el contrato base de composición (clips, pistas, atributos data-*, reglas de determinismo). Esta skill cubre solo lo nuevo: el esquema de la isla, reglas de escritura de diapositivas, fragmentos, ramificaciones, validación y el componente envolvente.
Salida: un deck navegable, no un MP4 lineal
La salida de una presentación de diapositivas es el deck en ejecución: sírvelo con hyperframes present <directorio-del-proyecto> (o el modo presentación de Studio); el SlideshowController del reproductor lee la isla y maneja la navegación, fragmentos, ramificaciones y el modo presentador. Consulta Presentación y entrega más abajo.
No uses hyperframes render en una presentación de diapositivas para generar un solo MP4. Un deck se crea como varias composiciones de escena de nivel superior (un data-composition-id por diapositiva) sin una composición raíz maestra que las envuelva, por lo que render resuelve solo la primera composición y emite un MP4 silenciosamente truncado (p. ej., 6 segundos de un deck de 40 segundos). Una exportación lineal de la línea principal (solo diapositivas principales, excluyendo secuencias ramificadas) está diferida; hasta que se implemente, las salidas compatibles son el deck present en vivo y las imágenes fijas (snapshot) por diapositiva. Si un usuario necesita un MP4 lineal hoy, indica esta limitación en lugar de apuntar render al deck.
Confirmación de intención
Si el usuario pide explícitamente una presentación de diapositivas, pase de diapositivas o presentación HyperFrames, procede con esta skill.
Si la skill se activa a partir de una solicitud relacionada, como "presentación", "pitch deck", "deck", "deck interactivo" o "convierte esta página", haz una pausa antes de crear el contenido y presenta la opción antes de pedir confirmación. Explica brevemente que una presentación de diapositivas HyperFrames significa un deck ejecutable con diapositivas discretas, navegación incorporada y modo presentador, notas del orador editables, manejo compartido de medios y validación antes de la entrega. Para conversiones de páginas de origen, menciona también que el objetivo es preservar el diseño visual, las interacciones, el movimiento y el comportamiento multimedia de la página original, mientras se traduce el movimiento de página a transiciones entre diapositivas.
Luego haz una breve pregunta de confirmación:
¿Quieres esto como una presentación de diapositivas HyperFrames?
Usa una interfaz de opción sí/no cuando el entorno lo proporcione; de lo contrario, haz la pregunta en texto plano.
No implementes la presentación de diapositivas hasta que el usuario responda que sí. Si dice que no, deja de usar esta skill y cambia al flujo de trabajo que no sea de presentación de diapositivas.
Las dos partes
1. Escenas: declaradas de la manera normal
Cada diapositiva está respaldada por una escena. Declara las escenas con data-composition-id, data-start, data-duration y data-label:
<div
data-composition-id="problem"
data-start="0"
data-duration="8"
data-label="The problem"
data-width="1920"
data-height="1080"
>
<!-- los clips van aquí -->
</div>
Las diapositivas ramificadas (accesibles solo a través de una zona interactiva, excluidas de la línea principal) se declaran exactamente de la misma manera; solo aparecen en una entrada slideSequences en la isla, no en el array slides principal.
2. La isla JSON: un bloque de script por composición
Agrega exactamente un bloque <script type="application/hyperframes-slideshow+json"> al HTML de la composición. Contiene todos los metadatos de la presentación de diapositivas:
<script type="application/hyperframes-slideshow+json">
{
"slides": [...],
"slideSequences": [...]
}
</script>
La isla es la fuente única de verdad para el orden de las diapositivas, notas, puntos de espera de fragmentos, zonas interactivas y secuencias ramificadas. Colócala cerca de la parte superior del <body>, antes de los divs de escena, para que sea fácil de encontrar.
No escondas el manifiesto de la presentación de diapositivas detrás de un bloque <script type="application/json"> alternativo más código en tiempo de ejecución que cree la isla. El comando present lee el HTML de la composición estáticamente y espera que la isla real application/hyperframes-slideshow+json ya esté presente.
Esquema
SlideshowManifest (el objeto isla de nivel superior)
{
"slides": [
/* SlideRef[] — la línea principal, en orden */
],
"slideSequences": [
/* SlideSequence[] — secuencias ramificadas fuera de línea */
]
}
SlideRef
{
"sceneId": "problem",
"notes": "Empieza con el dolor, no con la empresa.",
"fragments": [3.5, 5.2, 7.0],
"hotspots": [
/* SlideHotspot[] */
],
"ttsScript": null,
"ttsAudioUrl": null,
"ttsDurationMs": null
}
| Campo | Requerido | Notas |
|---|---|---|
sceneId | sí | Debe coincidir exactamente con el data-composition-id de una escena (o proporcionar startTime/endTime explícitos). La regla de lint resuelve escenas por data-composition-id. |
notes | no | Texto solo para el presentador. Nunca se muestra a la audiencia. |
fragments | no | Array de tiempos (segundos) dentro del rango [start, end] de la diapositiva — consulta Fragmentos más abajo. |
hotspots | no | Superposiciones interactivas que desencadenan una rama — consulta Ramificación más abajo. |
startTime | no | Opcional. Sobrescribe los límites de tiempo de la escena coincidente; por defecto usa el inicio/fin de la escena. |
endTime | no | Opcional. Sobrescribe los límites de tiempo de la escena coincidente; por defecto usa el inicio/fin de la escena. |
ttsScript, ttsAudioUrl, ttsDurationMs | no | Reservados. Los campos del esquema existen, pero la reproducción de TTS aún no está conectada. Omítelos a menos que estés precargando para una compilación futura. |
SlideHotspot
{
"id": "h1",
"label": "¿Cómo calculamos esto?",
"target": "market-deep-dive",
"region": { "x": 60, "y": 10, "w": 35, "h": 20 }
}
| Campo | Requerido | Notas |
|---|---|---|
id | sí | Único dentro de la diapositiva. |
label | sí | Texto de información sobre herramientas / botón que se muestra a la audiencia. |
target | sí | Debe coincidir con un SlideSequence.id en slideSequences. |
region | no | Cuadro delimitador en porcentaje de la diapositiva: {x, y, w, h} en 0–100. Omítelo para representar la zona interactiva como un botón etiquetado de diapositiva completa en su lugar. |
SlideSequence
{
"id": "market-deep-dive",
"label": "Metodología de dimensionamiento de mercado",
"slides": [{ "sceneId": "mkt-1" }, { "sceneId": "mkt-2" }]
}
slides dentro de una secuencia usa la misma forma SlideRef que la línea principal. Se permiten fragmentos y zonas interactivas anidadas.
Reglas de escritura de diapositivas
Estas son restricciones estrictas, no sugerencias. Una diapositiva que las viole será reemplazada directamente cuando un revisor la vea.
- El titular es una afirmación en oración completa, no una etiqueta. Escribe "Las PYMES dedican 14 horas a la semana a la programación manual", no "Problema de programación". La oración debe ser comprensible por sí sola si se ignora lo visual.
- Una idea + un elemento visual por diapositiva. Si te sientes tentado a agregar un segundo grupo de viñetas o un segundo gráfico, divide la diapositiva.
- Empieza con la conclusión. El punto más fuerte va primero, tanto en la diapositiva como en el orden del deck. Los inversores leen de izquierda a derecha, de arriba abajo, y se detienen.
- Solo dimensionamiento de mercado ascendente. Nunca escribas "TAM de 50 mil millones de dólares" sin mostrar los cálculos. Construye a partir de la economía unitaria: cuentas × ACV, o transacciones × tasa de descuento.
- Tamaño de fuente mínimo equivalente a 30pt. En 1920×1080, un titular es de 72–96px; el cuerpo del texto es de 48px. Nunca uses menos de 40px para cualquier texto que la audiencia deba leer.
Portar páginas de origen
Al convertir una página existente en una presentación de diapositivas, la fidelidad al origen es parte del contrato. No reemplaces widgets específicos del origen con aproximaciones simplificadas a menos que el usuario pida explícitamente un rediseño.
- Preserva el diseño visual, el lenguaje de movimiento, el comportamiento interactivo, el comportamiento multimedia y las capacidades de presentación de la página original lo más fielmente posible. Cuando el sistema de presentación de diapositivas admita el modo presentador, incluye notas del orador utilizando el comportamiento compartido de notas editables, en lugar de una implementación específica del deck.
- Porta los elementos visuales mecánicos del DOM/CSS/JS del origen con la mayor precisión posible: reproductores personalizados, visualizadores de canvas, líneas de tiempo, cabezales de reproducción, stems, círculos expansivos, estados de hover y otros detalles interactivos deben sobrevivir a la conversión.
- Trata los elementos
<video>/<audio>nativos como la fuente de verdad para cualquier marco multimedia personalizado, visualizador de canvas, forma de onda, cuadrícula de ritmo o cabezal de reproducción. Conecta los eventos multimedia del origen (play,pause,timeupdate,seeking,seeked,ended,ratechange,volumechange) y deriva el estado visual demedia.currentTime; no ejecutes un temporizador separado que pueda desviarse de la reproducción real. - Cada
<video>o<audio>copiado consrcdebe tener atributos de temporización de HyperFrames antes del lint:data-startydata-duration, además dedata-has-audio="true"cuando se deba conservar el audio nativo audible. Usa el rango de tiempo de la escena para medios específicos de la diapositiva; para videos de evidencia controlados por el usuario que se puedan reproducir desde múltiples diapositivas enfocadas, usa un rango de todo el deck. No dejespreload="none"en los medios; usametadataoauto. - Resuelve los tokens de fuente del origen antes de la validación. Si se conservan fuentes personalizadas del origen, agrega reglas
@font-facepara archivos de fuente locales/capturados. Si se usan fuentes de reemplazo del sistema, reemplaza declaraciones tokenizadas comofont-family: var(--f-body)con pilas concretas seguras para renderizar, comosystem-ui, sans-serifoui-monospace, monospace; no dejesvar(...)como el valor de la familia de fuentes. - Audita el origen en busca de movimientos de página atípicos, especialmente comportamientos impulsados por desplazamiento, rueda, toque, estado de hash, cambio de tamaño o un bucle requestAnimationFrame. Trata los viewports fijos con capas de "mundo" traducidas/escaladas, paralaje, paneles fijos, desplazadores horizontales, líneas de tiempo desplazadas, ajuste de secciones y cámaras de zoom a elementos como comportamiento del origen. El desplazamiento suele ser el desencadenante de transición del origen, por lo que debes preservar la transición extrayendo sus puntos de progreso, suavizado y estados de cámara/enfoque, y luego reubicar ese movimiento en la navegación de la presentación de diapositivas a través de posiciones de la línea de tiempo, fragmentos o un enlace de reproductor/herramienta reutilizable. Los envoltorios independientes que saltan a puntos de espera de diapositivas aún necesitan un enlace explícito de transición de cámara de navegación; no es suficiente calcular las transformaciones de cámara por diapositiva. No simules una transición literal de desplazamiento hacia abajo de la página dentro de la diapositiva; el espectador debe sentir el viaje/zoom de la cámara de un punto focal a otro, no ver una página web que se desplaza. Mantén cada movimiento de cámara entre diapositivas continuo: evita paradas de ruta intermedias que inviertan la dirección x/y o el zoom a menos que el origen lo haga visiblemente en el mismo límite. Una transición que se mueve erráticamente antes de aterrizar es peor que un movimiento focal directo más simple.
- Preserva la semántica de recorte de medios del origen. Trata las capturas de pantalla, publicaciones en redes sociales, capturas de UI de productos, gráficos, documentos, código, tablas de clasificación y cualquier imagen con texto legible como evidencia de contenido, no como medio decorativo: usa la relación de aspecto del origen (
height: auto) oobject-fit: containdentro de un marco estable. Usaobject-fit: coversolo cuando el origen lo haya hecho, o para miniaturas intencionalmente decorativas/de fondo/cinematográficas. Después de ajustar estas capturas en una diapositiva, inspecciona los cuatro bordes en busca de texto truncado, logotipos, controles o leyendas; un recorte visible en contenido significativo es un error, a menos que el propio origen lo haya recortado. - Si un comportamiento es genérico para las presentaciones de diapositivas, colócalo en el reproductor/controlador o en un fragmento de skill reutilizable. No lo resuelvas con scripts de deck únicos.
- Los marcos de escena apilados nunca deben bloquear la interacción en la diapositiva activa. Los marcos ocultos necesitan tanto ocultación visual como restricción de eventos:
.scene-frame {
opacity: 0;
visibility: hidden;
pointer-events: none;
}
.scene-frame.is-active {
opacity: 1;
visibility: visible;
pointer-events: auto;
}
Si la visibilidad se controla de manera imperativa, establece las tres propiedades (opacity, visibility y pointerEvents) en el controlador de visibilidad. opacity: 0 por sí solo aún deja una capa invisible que puede interceptar clics.
Fragmentos: puntos de espera de revelación dentro de una diapositiva
Un fragmento es un tiempo absoluto de la línea de tiempo de la composición (segundos) dentro del rango [start, end] de una diapositiva donde el controlador debe mantener un estado de revelación.
Cómo funciona:
- El reproductor entra en una diapositiva fragmentada: busca directamente
fragments[0]y se mantiene allí. - El usuario presiona Siguiente (o →): el controlador busca
fragments[1]y se mantiene. - Después del último fragmento, Siguiente avanza a la siguiente diapositiva.
- Una diapositiva sin fragmentos entra en un fotograma de reposo dentro de la diapositiva, generalmente su punto medio, no exactamente en
slide.end.
Los tiempos de fragmento deben estar dentro de [start, end]
… [truncado para traducción]