feat(registry): bring back the video-primitive moves (#3169)

Restores the 208 catalog items reverted after their previews 404'd in
production, this time on the payload mechanism rather than the .html files
that caused the outage.

The generator no longer writes a preview document to docs/public. That writer,
and the machinery under it, existed only to produce files the docs host
discards, so it is gone rather than bypassed. Items now embed the composition
itself via a payload, which is what the previous change already does for the
items that were already in the catalog.

The variables explorer is parked, not restored: it drove its preview through
the same unpublished .html path, so it would have shown an empty frame. Items
that declare variables get the live player plus the static variables table, and
reconnecting the explorer to payloads is a follow-up.
This commit is contained in:
Miguel Ángel
2026-08-10 18:46:03 -04:00
committed by GitHub
parent 0f76305191
commit 9734578e60
1383 changed files with 255573 additions and 5760 deletions
+174
View File
@@ -0,0 +1,174 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=1920, height=1080" />
<title>Zoom Through Transition - Demo</title>
<script src="https://cdn.jsdelivr.net/npm/gsap@3.14.2/dist/gsap.min.js"></script>
<style>
* {
box-sizing: border-box;
}
html,
body {
margin: 0;
width: 1920px;
height: 1080px;
overflow: hidden;
}
#demo {
width: 1920px;
height: 1080px;
display: grid;
place-items: center;
background: #f7f7f8;
font-family: Inter, system-ui, sans-serif;
}
.demo-frame {
display: grid;
place-items: center;
padding: 52px;
/* ponytail: scale non-animated wrapper to fill ~65% of 1920×1080 */
transform: scale(1.7);
}
.hf-remocn-zoom-through-transition {
--hf-accent: var(--accent, rgba(255, 255, 255, 0.28));
--hf-l1: rgba(255, 255, 255, 0.72);
--hf-l3: rgba(255, 255, 255, 0.18);
--hf-l4: rgba(255, 255, 255, 0.08);
--hf-hair: rgba(255, 255, 255, 0.14);
--hf-ink: #111827;
--hf-muted: #6b7280;
--hf-surface: #ffffff;
color: var(--hf-ink);
font-family: Inter, system-ui, sans-serif;
}
.hf-remocn-zoom-through-transition {
position: relative;
width: 760px;
height: 428px;
overflow: hidden;
border-radius: 30px;
background: #111827;
box-shadow: 0 28px 80px rgba(15, 23, 42, 0.2);
}
.hf-remocn-zoom-through-transition .scene {
position: absolute;
inset: 0;
display: grid;
place-items: center;
font-weight: 900;
font-size: 58px;
letter-spacing: -0.05em;
color: #fff;
}
.hf-remocn-zoom-through-transition .from {
background:
repeating-linear-gradient(90deg, rgba(255, 255, 255, 0.04) 0 1px, transparent 1px 4px),
var(--hf-l4);
transform: translate(var(--hf-from-x, 0px), var(--hf-from-y, 0px))
scale(var(--hf-from-scale, 1));
filter: blur(var(--hf-from-blur, 0px));
}
.hf-remocn-zoom-through-transition .to {
background:
repeating-linear-gradient(90deg, rgba(255, 255, 255, 0.05) 0 1px, transparent 1px 4px),
var(--hf-l3);
box-shadow: inset 0 0 0 2px var(--hf-accent);
transform: translate(var(--hf-to-x, 0px), var(--hf-to-y, 0px)) scale(var(--hf-to-scale, 1));
clip-path: inset(
var(--hf-clip-top, 0%) var(--hf-clip-right, 100%) var(--hf-clip-bottom, 0%)
var(--hf-clip-left, 0%)
);
}
.hf-remocn-zoom-through-transition .fx {
position: absolute;
inset: 0;
background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.75), transparent);
mix-blend-mode: screen;
transform: translateX(var(--hf-fx-x, -110%));
}
.hf-remocn-zoom-through-transition .rgb {
position: absolute;
inset: 0;
pointer-events: none;
opacity: var(--hf-rgb-opacity, 0);
background: linear-gradient(
90deg,
rgba(239, 68, 68, 0.35),
transparent,
rgba(34, 211, 238, 0.35)
);
transform: translateX(var(--hf-rgb-x, 0px));
}
</style>
</head>
<body>
<div
id="demo"
data-composition-id="zoom-through-transition-demo"
data-start="0"
data-width="1920"
data-height="1080"
data-duration="5"
>
<div class="demo-frame">
<div class="hf-remocn-zoom-through-transition">
<div class="scene from">Before</div>
<div class="scene to">Zoom Through Transition</div>
<div class="fx"></div>
<div class="rgb"></div>
</div>
</div>
<script>
const tl = gsap.timeline({ paused: true });
const startTime = 0.5;
tl.fromTo(
".hf-remocn-zoom-through-transition .to",
{ "--hf-clip-right": "100%", "--hf-to-x": "60px", "--hf-to-scale": 1.04 },
{
"--hf-clip-right": "0%",
"--hf-to-x": "0px",
"--hf-to-scale": 1,
duration: 0.9,
ease: "power3.inOut",
},
startTime,
);
tl.to(
".hf-remocn-zoom-through-transition .from",
{
"--hf-from-x": "-70px",
"--hf-from-scale": 0.96,
"--hf-from-blur": "5px",
duration: 0.9,
ease: "power3.inOut",
},
startTime,
);
tl.fromTo(
".hf-remocn-zoom-through-transition .fx",
{ "--hf-fx-x": "-110%" },
{ "--hf-fx-x": "110%", duration: 0.58, ease: "power2.inOut" },
startTime + 0.18,
);
tl.fromTo(
".hf-remocn-zoom-through-transition .rgb",
{ "--hf-rgb-opacity": 0, "--hf-rgb-x": "-16px" },
{
"--hf-rgb-opacity": 1,
"--hf-rgb-x": "16px",
repeat: 3,
yoyo: true,
duration: 0.07,
ease: "steps(1)",
},
startTime + 0.32,
);
tl.set({}, {}, 5);
window.__timelines = window.__timelines || {};
window.__timelines["zoom-through-transition-demo"] = tl;
</script>
</div>
</body>
</html>
@@ -0,0 +1,80 @@
{
"$schema": "https://hyperframes.heygen.com/schema/registry-item.json",
"name": "zoom-through-transition",
"type": "hyperframes:component",
"title": "Zoom Through Transition",
"description": "A transition that zooms through a focal card into the next scene.",
"tags": ["transition-primitive", "remocn-port"],
"variables": [
{
"id": "from-label",
"type": "string",
"role": "content",
"label": "Outgoing label",
"description": "Caption on the outgoing scene, the one on screen when the transition starts.",
"default": "Before"
},
{
"id": "to-label",
"type": "string",
"role": "content",
"label": "Incoming label",
"description": "Caption on the incoming scene, the one the timeline wipes into frame.",
"default": "Zoom Through Transition"
},
{
"id": "depth",
"type": "enum",
"role": "motion",
"label": "Depth",
"description": "Multiplies how far both scenes travel and how hard the outgoing one blurs, so the push covers a shorter or longer run in the same time.",
"default": "standard",
"options": [
{
"value": "close",
"label": "Close"
},
{
"value": "standard",
"label": "Standard"
},
{
"value": "far",
"label": "Far"
}
]
},
{
"id": "accent",
"type": "enum",
"role": "style",
"label": "Accent",
"description": "Theme token for the ring marking the incoming scene. Blue rides --accent, green --brand, violet --accent-2; each falls back to a neutral hairline.",
"default": "blue",
"options": [
{
"value": "blue",
"label": "Blue"
},
{
"value": "green",
"label": "Green"
},
{
"value": "violet",
"label": "Violet"
}
]
}
],
"files": [
{
"path": "zoom-through-transition.html",
"target": "compositions/components/zoom-through-transition.html",
"type": "hyperframes:snippet"
}
],
"preview": {
"poster": "https://static.heygen.ai/hyperframes-oss/docs/images/catalog/components/zoom-through-transition.png"
}
}
@@ -0,0 +1,284 @@
<!doctype html>
<!--
Zoom Through Transition - a scene push that wipes the incoming scene through
the outgoing one while the outgoing one drifts back and blurs.
Mount contract: MOUNTABLE SUB-COMPOSITION. The runtime clones only <template>
contents; #root fills the host box (inset: 0, container-type: size), and the
file registers one paused timeline under the literal "zoom-through-transition"
key. Variables come from window.__hyperframes.getVariables().
This file owns its motion. It previously shipped as a fragment whose only
executable timeline lived in demo.html, so an installed copy rendered the
finished pose and never moved.
Variables (declared in data-composition-variables below):
- from-label (string, default "Before"): caption on the outgoing scene.
- to-label (string, default "Zoom Through Transition"): caption on the
incoming scene, clipped out of frame until the wipe brings it in.
- depth (close | standard | far, default standard): multiplies how far both
scenes travel and how hard the outgoing one blurs. Every input it
multiplies is zero at rest, so it collapses to the shipped pose there. It
deliberately leaves the two scale tracks alone: the timeline drives those
from their own start values, which a multiplier would distort.
- accent (blue | green | violet, default blue): ink of the ring that marks
the incoming scene. Both scenes are monochrome placeholder material; the
ring is the one accent-coloured element. blue rides --accent, green rides
--brand, violet rides --accent-2, and each falls back to a neutral
hairline so an unthemed composition renders in black and white.
Envelope: the push starts at 0.35s (clamped to fit shorter durations), runs
0.9s, then holds the incoming scene for whatever is left.
-->
<html
lang="en"
data-composition-id="zoom-through-transition"
data-composition-duration="3"
data-composition-variables='[
{ "id": "from-label", "type": "string", "role": "content", "label": "Outgoing label", "description": "Caption on the outgoing scene, the one on screen when the transition starts.", "default": "Before" },
{ "id": "to-label", "type": "string", "role": "content", "label": "Incoming label", "description": "Caption on the incoming scene, the one the timeline wipes into frame.", "default": "Zoom Through Transition" },
{ "id": "depth", "type": "enum", "role": "motion", "label": "Depth", "description": "Multiplies how far both scenes travel and how hard the outgoing one blurs, so the push covers a shorter or longer run in the same time.", "default": "standard", "options": [{ "value": "close", "label": "Close" }, { "value": "standard", "label": "Standard" }, { "value": "far", "label": "Far" }] },
{ "id": "accent", "type": "enum", "role": "style", "label": "Accent", "description": "Theme token for the ring marking the incoming scene. Blue rides --accent, green --brand, violet --accent-2; each falls back to a neutral hairline.", "default": "blue", "options": [{ "value": "blue", "label": "Blue" }, { "value": "green", "label": "Green" }, { "value": "violet", "label": "Violet" }] }
]'
>
<head>
<meta charset="UTF-8" />
<title>Zoom Through Transition</title>
</head>
<body>
<template>
<div id="root" data-composition-id="zoom-through-transition" data-duration="3" data-fps="30">
<style>
*,
*::before,
*::after {
box-sizing: border-box;
}
/* Root fills the host-owned box; the scenes measure against it. */
#root {
position: absolute;
inset: 0;
container-type: size;
isolation: isolate;
overflow: hidden;
background: #111827;
}
.hf-remocn-zoom-through-transition {
--hf-accent: var(--accent, rgba(255, 255, 255, 0.28));
--hf-l1: rgba(255, 255, 255, 0.72);
--hf-l3: rgba(255, 255, 255, 0.18);
--hf-l4: rgba(255, 255, 255, 0.08);
--hf-hair: rgba(255, 255, 255, 0.14);
--hf-ink: #111827;
--hf-muted: #6b7280;
--hf-surface: #ffffff;
color: var(--hf-ink);
font-family: Inter, system-ui, sans-serif;
}
.hf-remocn-zoom-through-transition {
position: absolute;
inset: 0;
overflow: hidden;
background: #111827;
}
.hf-remocn-zoom-through-transition .scene {
position: absolute;
inset: 0;
display: grid;
place-items: center;
font-weight: 900;
font-size: 13.5cqmin;
letter-spacing: -0.05em;
color: #fff;
}
.hf-remocn-zoom-through-transition .from {
background:
repeating-linear-gradient(
90deg,
rgba(255, 255, 255, 0.04) 0 1px,
transparent 1px 4px
),
var(--hf-l4);
transform: translate(
calc(var(--hf-from-x, 0px) * var(--hf-depth, 1)),
calc(var(--hf-from-y, 0px) * var(--hf-depth, 1))
)
scale(var(--hf-from-scale, 1));
filter: blur(calc(var(--hf-from-blur, 0px) * var(--hf-depth, 1)));
}
.hf-remocn-zoom-through-transition .to {
background:
repeating-linear-gradient(
90deg,
rgba(255, 255, 255, 0.05) 0 1px,
transparent 1px 4px
),
var(--hf-l3);
box-shadow: inset 0 0 0 2px var(--hf-accent);
transform: translate(
calc(var(--hf-to-x, 0px) * var(--hf-depth, 1)),
calc(var(--hf-to-y, 0px) * var(--hf-depth, 1))
)
scale(var(--hf-to-scale, 1));
clip-path: inset(
var(--hf-clip-top, 0%) var(--hf-clip-right, 100%) var(--hf-clip-bottom, 0%)
var(--hf-clip-left, 0%)
);
}
.hf-remocn-zoom-through-transition .fx {
position: absolute;
inset: 0;
background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.75), transparent);
mix-blend-mode: screen;
transform: translateX(var(--hf-fx-x, -110%));
}
.hf-remocn-zoom-through-transition .rgb {
position: absolute;
inset: 0;
pointer-events: none;
opacity: var(--hf-rgb-opacity, 0);
background: linear-gradient(
90deg,
rgba(239, 68, 68, 0.35),
transparent,
rgba(34, 211, 238, 0.35)
);
transform: translateX(var(--hf-rgb-x, 0px));
}
</style>
<div class="hf-remocn-zoom-through-transition">
>
<div class="scene from">Before</div>
<div class="scene to">Zoom Through Transition</div>
<div class="fx"></div>
<div class="rgb"></div>
</div>
<script src="https://cdn.jsdelivr.net/npm/gsap@3.14.2/dist/gsap.min.js"></script>
<script>
(function () {
"use strict";
var vars =
window.__hyperframes && window.__hyperframes.getVariables
? window.__hyperframes.getVariables()
: {};
// Unrecognised overrides return to their declared defaults.
var depths = { close: 0.55, standard: 1, far: 1.7 };
// Each choice routes to a different contract token so the variable stays
// meaningful under a theme rather than collapsing into one themed accent.
var HAIRLINE = "rgba(255, 255, 255, 0.28)";
var accents = {
blue: "var(--accent, " + HAIRLINE + ")",
green: "var(--brand, " + HAIRLINE + ")",
violet: "var(--accent-2, " + HAIRLINE + ")",
};
function pick(table, value, fallback) {
return Object.prototype.hasOwnProperty.call(table, value) ? value : fallback;
}
function label(value) {
return typeof value === "string" && value !== "" ? value : null;
}
var depth = depths[pick(depths, vars.depth, "standard")];
var accent = accents[pick(accents, vars.accent, "blue")];
var fromLabel = label(vars["from-label"]);
var toLabel = label(vars["to-label"]);
var roots = document.querySelectorAll(".hf-remocn-zoom-through-transition");
var mountRoot = document.getElementById("root");
for (var i = 0; i < roots.length; i += 1) {
var root = roots[i];
root.style.setProperty("--hf-depth", String(depth));
root.style.setProperty("--hf-accent", accent);
var fromScene = root.querySelector(".from");
var toScene = root.querySelector(".to");
if (fromLabel !== null && fromScene) {
fromScene.textContent = fromLabel;
}
if (toLabel !== null && toScene) {
toScene.textContent = toLabel;
}
}
// The motion used to live only in this file's demo page, so an installed
// copy rendered the finished pose and never moved. The timeline belongs
// with the markup it drives: build it here, paused, and register it under
// this composition's own id so the runtime seeks it frame by frame.
if (!mountRoot || !window.gsap) return;
var SELF = ".hf-remocn-zoom-through-transition";
var duration = Math.max(0.001, parseFloat(mountRoot.dataset.duration || "3"));
// The push reads as a transition only with a beat of the outgoing scene
// before it. Clamped so the wipe always lands inside the duration.
var PUSH = 0.9;
var START = Math.max(0, Math.min(0.35, duration - PUSH));
var tl = window.gsap.timeline({ paused: true });
tl.fromTo(
SELF + " .to",
{ "--hf-clip-right": "100%", "--hf-to-x": "60px", "--hf-to-scale": 1.04 },
{
"--hf-clip-right": "0%",
"--hf-to-x": "0px",
"--hf-to-scale": 1,
duration: PUSH,
ease: "power3.inOut",
},
START,
);
tl.fromTo(
SELF + " .from",
{ "--hf-from-x": "0px", "--hf-from-scale": 1, "--hf-from-blur": "0px" },
{
"--hf-from-x": "-70px",
"--hf-from-scale": 0.96,
"--hf-from-blur": "5px",
duration: PUSH,
ease: "power3.inOut",
},
START,
);
tl.fromTo(
SELF + " .fx",
{ "--hf-fx-x": "-110%" },
{ "--hf-fx-x": "110%", duration: 0.58, ease: "power2.inOut" },
START + 0.18,
);
// repeat/yoyo resolves from absolute time on every seek, so the strobe is
// reproducible at any frame rather than accumulated across ticks.
tl.fromTo(
SELF + " .rgb",
{ "--hf-rgb-opacity": 0, "--hf-rgb-x": "-16px" },
{
"--hf-rgb-opacity": 1,
"--hf-rgb-x": "16px",
repeat: 3,
yoyo: true,
duration: 0.07,
ease: "steps(1)",
},
START + 0.32,
);
// HOLD: the incoming scene rests for whatever is left of the duration.
tl.set({}, {}, duration);
tl.seek(0);
window.__timelines = window.__timelines || {};
window.__timelines["zoom-through-transition"] = tl;
})();
</script>
</div>
</template>
</body>
</html>