mirror of
https://github.com/heygen-com/hyperframes.git
synced 2026-10-30 09:16:55 +00:00
fix(skills): keep changelog-video's stamped seams off the clip elements (#3946)
The committed changelog-video scaffold no longer survives its own gate. Three independent breaks, all reproduced against origin/main: 1. `gsap_animates_clip_element` (error severity, shipped this week) flags visibility/display/autoAlpha on any clip element. The skeleton's slides are `class="slide clip"` and every seam seam-stamp emits writes `autoAlpha` onto the ledger's selectors, so a film built from the scaffold fails `hyperframes check` before a scene is authored. Per the rule's own fixHint, each slide now holds an inner `<div class="swrap">`; the `opacity: 0` base moves off `.slide` onto `.swrap`, and the ledger names `#w-*`. 2. The title seam then fails as `exit-visible … op 0.00` while `exit-vector` PASSES. seam-stamp emits a zero-duration `tl.set(autoAlpha: 0)` at the cut, which immediate-renders at build time, so the exit `.to()` captures 0 as its start and tweens 0 → 0. Every other wrapper is saved by its stamped entry `fromTo(..., immediateRender: false)`; the title has no stamped entry. The scaffold now ships that entry, and build-spec says why the flag is required. 3. `seam-gate.mjs` defaulted `--server-cmd` to `npx --yes hyperframes preview` — the published package — while SKILL.md forbids `npx hyperframes@latest` for gate 1 and calls the repo-local CLI the contract. The two gates could measure different runtimes. It now prefers the repo-local bin, falling back to npx only for a copy of the skill living outside the repo. Also fixes gate 1's own command: `bun run --cwd packages/cli hyperframes` exits "Script not found" because `hyperframes` is a bin, not a script. Both mirror dirs updated; `check-skill-mirror` confirms byte-identity. Reported by the hyperframes session after the Sep 07-14 weekly build. Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
a0a6244be7
commit
e8cbf0ca85
@@ -147,7 +147,7 @@ scaffold.
|
||||
|
||||
### 6 · Gates (all green before presenting)
|
||||
|
||||
1. `bun run --cwd packages/cli hyperframes check --caption-zone "x0=0;y0=.90;x1=1;y1=1;severity=error;seek=.02,.06,.10,.14,.18,.22,.26,.30,.34,.38,.42,.46,.50,.54,.58,.62,.66,.70,.74,.78,.82,.86,.90,.94,.98"` (or the installed
|
||||
1. `node packages/cli/bin/hyperframes.mjs check --caption-zone "x0=0;y0=.90;x1=1;y1=1;severity=error;seek=.02,.06,.10,.14,.18,.22,.26,.30,.34,.38,.42,.46,.50,.54,.58,.62,.66,.70,.74,.78,.82,.86,.90,.94,.98"` (or the installed
|
||||
`hyperframes` CLI from the repo-local `skills/hyperframes-cli/` skill) —
|
||||
0 errors (contrast: dim text ≥ .66 alpha; scene content stays above the
|
||||
caption rail). Do NOT reach for
|
||||
|
||||
@@ -23,7 +23,11 @@
|
||||
border: 1px solid rgba(255,255,255,.14); border-radius: 12px; padding: 10px 18px; }
|
||||
#progress { top: 58px; right: 76px; display: flex; gap: 10px; }
|
||||
#progress .dot { width: 8px; height: 8px; border-radius: 50%; background: rgba(245,246,244,.18); }
|
||||
.slide { position: absolute; inset: 0; opacity: 0; }
|
||||
/* The clip element itself carries NO opacity base and is never targeted by GSAP:
|
||||
the framework owns a clip's visibility window, and `gsap_animates_clip_element`
|
||||
is an ERROR-severity lint rule. Seams animate the inner .swrap instead. */
|
||||
.slide { position: absolute; inset: 0; }
|
||||
.swrap { position: absolute; inset: 0; opacity: 0; }
|
||||
.sec-chip { position: absolute; top: 128px; left: 76px; font-family: 'TT Norms Mono', monospace;
|
||||
font-size: 22px; letter-spacing: .05em; color: rgba(245,246,244,.72);
|
||||
background: rgba(255,255,255,.05); border: 1px solid rgba(255,255,255,.14);
|
||||
@@ -52,17 +56,23 @@
|
||||
|
||||
<!-- Title (film open — authors its OWN entry; exit is stamped) -->
|
||||
<div id="s-title" class="slide clip" data-start="0" data-duration="<CUT1>" data-track-index="1">
|
||||
<!-- mono kicker date · ABC Solar h1 · green rule -->
|
||||
<div id="w-title" class="swrap">
|
||||
<!-- mono kicker date · ABC Solar h1 · green rule -->
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- One slide per theme. data-start = EXACTLY the ledger cut time.
|
||||
Every slide holds ONE inner .swrap (#w-*) and the ledger's exit/entry
|
||||
selectors name THAT wrapper, never the #s-* clip element.
|
||||
Shell composed at local t=0; internal beats ≥0.4s after the cut,
|
||||
done ≥0.45s before the next. Put data-layout-allow-overlap only on
|
||||
the specific text blocks that deliberately layer. -->
|
||||
<div id="s-t1" class="slide clip" data-start="<CUT1>" data-duration="<DUR1>" data-track-index="1">
|
||||
<div class="sec-chip">01 · <THEME></div>
|
||||
<div class="sec-head"><HEADLINE></div>
|
||||
<!-- the mock, from references/visualization-registry.md, y ∈ [288, 944] -->
|
||||
<div id="w-t1" class="swrap">
|
||||
<div class="sec-chip">01 · <THEME></div>
|
||||
<div class="sec-head"><HEADLINE></div>
|
||||
<!-- the mock, from references/visualization-registry.md, y ∈ [288, 944] -->
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- s-t2..tN, then outro (kicker · "See what shipped." · green rule · URL chip) -->
|
||||
@@ -81,6 +91,15 @@
|
||||
tl.to('#kicker-chip', { autoAlpha: 1, y: 0, duration: 0.5, ease: 'power3.out' }, 0.1);
|
||||
// tl.set('#dot1', { backgroundColor: '#f5f6f4' }, <CUT1>); …
|
||||
|
||||
// Title entry — authored HERE, never stamped (seam-stamp owns every other
|
||||
// wrapper entry). immediateRender:false is load-bearing: seam-stamp emits a
|
||||
// zero-duration tl.set(#w-title, autoAlpha:0) at the title's cut, and a
|
||||
// zero-duration GSAP tween immediate-renders at BUILD time. Without the flag
|
||||
// this fromTo captures that 0 as its start value and tweens 0 → 0, which the
|
||||
// seam gate reports as `exit-visible … op 0.00` while exit-vector PASSES.
|
||||
tl.fromTo('#w-title', { autoAlpha: 0 },
|
||||
{ autoAlpha: 1, duration: 0.30, ease: 'power2.out', immediateRender: false }, 0);
|
||||
|
||||
// ══ per-scene internal beats ══
|
||||
// init states: gsap.set(...) at build time; motion: sequential tl.to(...) only.
|
||||
// Every beat lands on a vo-words.json timestamp (local = master − scene start).
|
||||
|
||||
@@ -81,11 +81,18 @@ otherwise) and must stay flat 2D (no 3D ancestors).
|
||||
## Seams + internal life (doctrine mechanics)
|
||||
|
||||
- `ledger.json`: every ordinary seam `cut-the-curve LEFT` (x, dir −1), exit
|
||||
and entry selectors = the slide wrappers. Outro entry `travel: 8`.
|
||||
and entry selectors = the inner `.swrap` wrappers (`#w-*`), NEVER the `#s-*`
|
||||
clip element — `gsap_animates_clip_element` is an error-severity lint rule and
|
||||
every stamped seam writes `autoAlpha`. Outro entry `travel: 8`.
|
||||
- `seam-stamp.mjs --ledger ledger.json --write index.html` owns ALL wrapper
|
||||
entries/exits — author none yourself. Title (film open) authors its own
|
||||
entry only.
|
||||
- Slides: CSS `opacity: 0` base; `data-start` = exactly the cut time.
|
||||
entry only, and that entry MUST pass `immediateRender: false`: the stamped
|
||||
zero-duration `tl.set(autoAlpha: 0)` at the title's cut renders at BUILD time,
|
||||
so an entry without the flag captures 0 as its start and tweens 0 → 0. The
|
||||
symptom is `exit-visible … op 0.00` with `exit-vector` PASSING — moving but
|
||||
invisible. Neither a CSS opacity base nor a longer clip window fixes it.
|
||||
- Slides: the `.swrap` carries the CSS `opacity: 0` base, not the `.slide` clip
|
||||
element; `data-start` = exactly the cut time.
|
||||
- Each scene's shell (chip, headline, mock chrome, initial state) is
|
||||
COMPOSED at local t=0 — the wrapper flies it in. Internal reveals start
|
||||
≥0.4s after the cut and end ≥0.45s before the next cut (stamped exits
|
||||
|
||||
@@ -73,9 +73,27 @@ async function ensureServer() {
|
||||
delete env.HYPERFRAME_RUNTIME_URL; // wrong value fails silently as 200 HTML
|
||||
// `preview` backgrounds itself when stdin/stdout aren't TTYs, which they never are here: the
|
||||
// launcher would exit 0 before the server is up and detach it out of our process group.
|
||||
// Default to the REPO-LOCAL CLI whenever this skill is running from its repo
|
||||
// checkout. The gate this script backs is defined against that build, and the
|
||||
// skill forbids `npx hyperframes@latest` for it, so defaulting to the published
|
||||
// package here would let the two gates measure different runtimes. npx stays the
|
||||
// fallback for a copy of the skill living outside the repo.
|
||||
const repoCli = join(
|
||||
import.meta.dirname,
|
||||
"..",
|
||||
"..",
|
||||
"..",
|
||||
"..",
|
||||
"packages",
|
||||
"cli",
|
||||
"bin",
|
||||
"hyperframes.mjs",
|
||||
);
|
||||
const cmd = flag(
|
||||
"server-cmd",
|
||||
`npx --yes hyperframes preview --foreground --no-open --port ${port}`,
|
||||
existsSync(repoCli)
|
||||
? `node "${repoCli}" preview --foreground --no-open --port ${port}`
|
||||
: `npx --yes hyperframes preview --foreground --no-open --port ${port}`,
|
||||
);
|
||||
const child = spawn("sh", ["-c", cmd.replace(/\{port\}/g, String(port))], {
|
||||
cwd: project,
|
||||
|
||||
@@ -147,7 +147,7 @@ scaffold.
|
||||
|
||||
### 6 · Gates (all green before presenting)
|
||||
|
||||
1. `bun run --cwd packages/cli hyperframes check --caption-zone "x0=0;y0=.90;x1=1;y1=1;severity=error;seek=.02,.06,.10,.14,.18,.22,.26,.30,.34,.38,.42,.46,.50,.54,.58,.62,.66,.70,.74,.78,.82,.86,.90,.94,.98"` (or the installed
|
||||
1. `node packages/cli/bin/hyperframes.mjs check --caption-zone "x0=0;y0=.90;x1=1;y1=1;severity=error;seek=.02,.06,.10,.14,.18,.22,.26,.30,.34,.38,.42,.46,.50,.54,.58,.62,.66,.70,.74,.78,.82,.86,.90,.94,.98"` (or the installed
|
||||
`hyperframes` CLI from the repo-local `skills/hyperframes-cli/` skill) —
|
||||
0 errors (contrast: dim text ≥ .66 alpha; scene content stays above the
|
||||
caption rail). Do NOT reach for
|
||||
|
||||
@@ -23,7 +23,11 @@
|
||||
border: 1px solid rgba(255,255,255,.14); border-radius: 12px; padding: 10px 18px; }
|
||||
#progress { top: 58px; right: 76px; display: flex; gap: 10px; }
|
||||
#progress .dot { width: 8px; height: 8px; border-radius: 50%; background: rgba(245,246,244,.18); }
|
||||
.slide { position: absolute; inset: 0; opacity: 0; }
|
||||
/* The clip element itself carries NO opacity base and is never targeted by GSAP:
|
||||
the framework owns a clip's visibility window, and `gsap_animates_clip_element`
|
||||
is an ERROR-severity lint rule. Seams animate the inner .swrap instead. */
|
||||
.slide { position: absolute; inset: 0; }
|
||||
.swrap { position: absolute; inset: 0; opacity: 0; }
|
||||
.sec-chip { position: absolute; top: 128px; left: 76px; font-family: 'TT Norms Mono', monospace;
|
||||
font-size: 22px; letter-spacing: .05em; color: rgba(245,246,244,.72);
|
||||
background: rgba(255,255,255,.05); border: 1px solid rgba(255,255,255,.14);
|
||||
@@ -52,17 +56,23 @@
|
||||
|
||||
<!-- Title (film open — authors its OWN entry; exit is stamped) -->
|
||||
<div id="s-title" class="slide clip" data-start="0" data-duration="<CUT1>" data-track-index="1">
|
||||
<!-- mono kicker date · ABC Solar h1 · green rule -->
|
||||
<div id="w-title" class="swrap">
|
||||
<!-- mono kicker date · ABC Solar h1 · green rule -->
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- One slide per theme. data-start = EXACTLY the ledger cut time.
|
||||
Every slide holds ONE inner .swrap (#w-*) and the ledger's exit/entry
|
||||
selectors name THAT wrapper, never the #s-* clip element.
|
||||
Shell composed at local t=0; internal beats ≥0.4s after the cut,
|
||||
done ≥0.45s before the next. Put data-layout-allow-overlap only on
|
||||
the specific text blocks that deliberately layer. -->
|
||||
<div id="s-t1" class="slide clip" data-start="<CUT1>" data-duration="<DUR1>" data-track-index="1">
|
||||
<div class="sec-chip">01 · <THEME></div>
|
||||
<div class="sec-head"><HEADLINE></div>
|
||||
<!-- the mock, from references/visualization-registry.md, y ∈ [288, 944] -->
|
||||
<div id="w-t1" class="swrap">
|
||||
<div class="sec-chip">01 · <THEME></div>
|
||||
<div class="sec-head"><HEADLINE></div>
|
||||
<!-- the mock, from references/visualization-registry.md, y ∈ [288, 944] -->
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- s-t2..tN, then outro (kicker · "See what shipped." · green rule · URL chip) -->
|
||||
@@ -81,6 +91,15 @@
|
||||
tl.to('#kicker-chip', { autoAlpha: 1, y: 0, duration: 0.5, ease: 'power3.out' }, 0.1);
|
||||
// tl.set('#dot1', { backgroundColor: '#f5f6f4' }, <CUT1>); …
|
||||
|
||||
// Title entry — authored HERE, never stamped (seam-stamp owns every other
|
||||
// wrapper entry). immediateRender:false is load-bearing: seam-stamp emits a
|
||||
// zero-duration tl.set(#w-title, autoAlpha:0) at the title's cut, and a
|
||||
// zero-duration GSAP tween immediate-renders at BUILD time. Without the flag
|
||||
// this fromTo captures that 0 as its start value and tweens 0 → 0, which the
|
||||
// seam gate reports as `exit-visible … op 0.00` while exit-vector PASSES.
|
||||
tl.fromTo('#w-title', { autoAlpha: 0 },
|
||||
{ autoAlpha: 1, duration: 0.30, ease: 'power2.out', immediateRender: false }, 0);
|
||||
|
||||
// ══ per-scene internal beats ══
|
||||
// init states: gsap.set(...) at build time; motion: sequential tl.to(...) only.
|
||||
// Every beat lands on a vo-words.json timestamp (local = master − scene start).
|
||||
|
||||
@@ -81,11 +81,18 @@ otherwise) and must stay flat 2D (no 3D ancestors).
|
||||
## Seams + internal life (doctrine mechanics)
|
||||
|
||||
- `ledger.json`: every ordinary seam `cut-the-curve LEFT` (x, dir −1), exit
|
||||
and entry selectors = the slide wrappers. Outro entry `travel: 8`.
|
||||
and entry selectors = the inner `.swrap` wrappers (`#w-*`), NEVER the `#s-*`
|
||||
clip element — `gsap_animates_clip_element` is an error-severity lint rule and
|
||||
every stamped seam writes `autoAlpha`. Outro entry `travel: 8`.
|
||||
- `seam-stamp.mjs --ledger ledger.json --write index.html` owns ALL wrapper
|
||||
entries/exits — author none yourself. Title (film open) authors its own
|
||||
entry only.
|
||||
- Slides: CSS `opacity: 0` base; `data-start` = exactly the cut time.
|
||||
entry only, and that entry MUST pass `immediateRender: false`: the stamped
|
||||
zero-duration `tl.set(autoAlpha: 0)` at the title's cut renders at BUILD time,
|
||||
so an entry without the flag captures 0 as its start and tweens 0 → 0. The
|
||||
symptom is `exit-visible … op 0.00` with `exit-vector` PASSING — moving but
|
||||
invisible. Neither a CSS opacity base nor a longer clip window fixes it.
|
||||
- Slides: the `.swrap` carries the CSS `opacity: 0` base, not the `.slide` clip
|
||||
element; `data-start` = exactly the cut time.
|
||||
- Each scene's shell (chip, headline, mock chrome, initial state) is
|
||||
COMPOSED at local t=0 — the wrapper flies it in. Internal reveals start
|
||||
≥0.4s after the cut and end ≥0.45s before the next cut (stamped exits
|
||||
|
||||
@@ -73,9 +73,27 @@ async function ensureServer() {
|
||||
delete env.HYPERFRAME_RUNTIME_URL; // wrong value fails silently as 200 HTML
|
||||
// `preview` backgrounds itself when stdin/stdout aren't TTYs, which they never are here: the
|
||||
// launcher would exit 0 before the server is up and detach it out of our process group.
|
||||
// Default to the REPO-LOCAL CLI whenever this skill is running from its repo
|
||||
// checkout. The gate this script backs is defined against that build, and the
|
||||
// skill forbids `npx hyperframes@latest` for it, so defaulting to the published
|
||||
// package here would let the two gates measure different runtimes. npx stays the
|
||||
// fallback for a copy of the skill living outside the repo.
|
||||
const repoCli = join(
|
||||
import.meta.dirname,
|
||||
"..",
|
||||
"..",
|
||||
"..",
|
||||
"..",
|
||||
"packages",
|
||||
"cli",
|
||||
"bin",
|
||||
"hyperframes.mjs",
|
||||
);
|
||||
const cmd = flag(
|
||||
"server-cmd",
|
||||
`npx --yes hyperframes preview --foreground --no-open --port ${port}`,
|
||||
existsSync(repoCli)
|
||||
? `node "${repoCli}" preview --foreground --no-open --port ${port}`
|
||||
: `npx --yes hyperframes preview --foreground --no-open --port ${port}`,
|
||||
);
|
||||
const child = spawn("sh", ["-c", cmd.replace(/\{port\}/g, String(port))], {
|
||||
cwd: project,
|
||||
|
||||
Reference in New Issue
Block a user