Commit Graph

1801 Commits

Author SHA1 Message Date
Miguel Angel Simon Sierra ff99e673ca fix(core): skip implicit transform from-correction when an earlier explicit tween owns the property
The U-final CSS-cascade correction picked the earliest IMPLICIT tween per
(target, property), so an implicit fade tween stacked after an explicit
burst tween (e.g. apple-money-count's confetti: translateY [0, y] then an
implicit drift-and-fade) was rewritten to the page-load identity value,
discarding anime.js's correct chain from the burst's end value and
collapsing the burst mid-flight (22.0dB at t=4.42s vs the GSAP baseline).

Correct an implicit tween only when it is the first tween of ANY kind on
its (target, property) pair. Verified by full re-render: apple-money-count
t=4.42s recovers to 50.0dB and vfx-liquid-glass's t=0.2-0.6s low band
recovers to 45.1dB; the product-promo stacked-implicit case that motivated
the earliest-implicit narrowing keeps its behavior (unit-tested).
2026-07-11 03:43:38 -04:00
Miguel Angel Simon Sierra 981378684b test(producer): linux docker goldens for animejs determinism fixtures and refreshed adapter golden 2026-07-11 02:41:13 -04:00
Miguel Angel Simon Sierra 2f529315a8 feat(player): snappier controls reveal and immediate first probe tick 2026-07-10 20:08:55 -04:00
Miguel Angel Simon Sierra 3a0049a1cd fix(core): mute priming-seek callbacks in anime.js adapter
primeAnimeInstance() seeks every registered timeline to its full
duration and back to 0 at registration time, purely to force
lazily-rendered CSS onto elements before the composition's first real
seek. anime.js fires tl.call()/onComplete callbacks as a side effect
of any seek that crosses their position, including this internal
priming seek -- so a composition using tl.call() (or a raw onComplete
tween) renders as if it had already reached that callback's end state
from frame 0 onward, regardless of the time actually requested later.

anime.js's own Timer.prototype.seek(time, muteCallbacks) already
suppresses onBegin/onComplete/onUpdate/onLoop/onRender for a given
seek; pass true on both priming seeks instead of reimplementing that
suppression. Widened RuntimeAnimeInstance.seek to accept the optional
second argument.

Confirmed against liquid-glass-widgets (a tl.call() status-text swap
scheduled at t=5.2s was rendering already-fired at t=1.434s) and
flowchart/flowchart-vertical (their addTimelineCall()/onComplete
helper had the whole diagram, cursor, and text-correction sequence
visible from frame 0). Added a regression test exercising the same
tl.call()-during-priming scenario directly against the adapter.
2026-07-10 14:17:53 -04:00
Miguel Angel Simon Sierra ec83da1479 Merge branch 'fix-open-items-b' into worktree-feat-animejs-first-party 2026-07-10 13:37:02 -04:00
Miguel Angel Simon Sierra 0684ff71f1 fix(cli): enable CanvasDrawElement in snapshot Chrome to match render pipeline
The engine's browserManager launches render Chrome with
--enable-features=CanvasDrawElement globally, but the snapshot command's own
puppeteer launch omitted it, so any composition using ctx.drawElementImage()
(vfx-shatter, vfx-portal, ios26-liquid-glass, ...) showed its 'API required'
placeholder in snapshots while rendering fine in the real pipeline.
2026-07-10 11:02:46 -04:00
Miguel Angel Simon Sierra 404c4f66f4 fix(player): pre-parse runtime injection for standalone anime compositions
<hyperframes-player> couldn't play a raw anime.js composition from src: the
inline hyperframesAnime.register() call throws before the runtime exists,
and the existing late-injection fallback only fires for GSAP's __timelines
and appends the runtime after parse, which is too late for a call that
already ran.

Add a runtime-src attribute to override the runtime URL, and a pre-parse
path in the probe: once it observes a same-origin composition that
references hyperframesAnime but never got a runtime/timeline/registry,
fetch the original src, inject the runtime script + a <base> tag ahead of
the composition's own scripts, and reload via srcdoc (once per src).
shouldInjectRuntime now also refuses to late-inject once an anime registry
is present, since that path can never help these compositions.
2026-07-10 02:35:43 -04:00
Miguel Angel Simon Sierra 268a530443 fix(core): only correct the earliest implicit transform tween per target/property (U-final)
The prior implicit-transform-from fix corrected EVERY implicit-from tween
matching a decomposed transform property, not just the first one on a given
target. Confirmed via a real render + PSNR A/B test against the commit
before that fix: registry/examples/product-promo's nested
scene1-logo-intro.html animates each Figma logo piece with two tweens on
the same target+property (an instant duration:0 jump off-screen, then an
implicit-from "converge to center" tween) -- the second tween correctly
inherits its "from" from the first tween's just-set value inside anime.js,
but the prior fix overwrote it with the element's true CSS/page-load
identity value instead, making the piece appear already-assembled at the
center from frame 0. This regressed product-promo's PSNR from ~50.5dB to
~47.0dB.

Only apply the CSS-cascade correction to the tween with the smallest
`_absoluteStartTime` for each distinct (target, property) pair; every later
implicit-from tween on that same pair is left untouched, since anime.js
already chains it correctly from the preceding tween. Verified: the
product-promo regression is resolved (51.2dB, back above baseline) while
flowchart/kinetic-type retain their original fix's improvement.
2026-07-09 20:59:16 -04:00
Miguel Angel Simon Sierra 7818e6a9c0 fix(core): rewrite anime.js clip-path "none" resets after a circle() shape (U-final)
anime.js v4.5.0 cannot correctly resolve a literal "none" clip-path target
when an earlier tween on the same element used a structured circle()
shape. Confirmed via internal tween inspection: it silently re-templates
"none" as a zeroed copy of the preceding circle()'s numeric parameters
(`_toNumbers` become `[0, 0, 0]`, reusing the prior string template),
rendering as `circle(0% at 0% 0%)` -- a near-fully-clipped, effectively
black/empty box -- instead of the intended fully-revealed state. This is
baked in at `.add()`-call time, before any seek ever runs, so it cannot be
fixed by mutating tween fields afterward.

Patch every timeline's `.add()` to rewrite a literal `clipPath: "none"`
into an equivalent fully-open `circle(150% at X% Y%)` (same shape function
anime already interpolates correctly, radius large enough to cover any
aspect ratio) whenever a tracked preceding value for that element was a
circle() shape. Leaves "none" untouched when there is no preceding
structured shape to conflict with (anime resolves that case correctly on
its own), and leaves every other property/shape untouched.

Verified against registry/blocks/transitions-light and
registry/blocks/transitions-radial (the confirmed "Family B" black/hidden
content defect from the U-final adjudication) with real render+compare-item
PSNR improvement; no regression on the other Family B members
(vfx-portal/vfx-shatter/play-mode), which do not use this clip-path pattern
and remain open with a different, not-yet-identified root cause.
2026-07-09 20:35:37 -04:00
Miguel Angel Simon Sierra 20e25341e9 fix(core): correct anime.js implicit transform from-values against CSS cascade (U-final)
anime.js v4.5.0 does not read an element's CSS-cascaded `transform` value
when resolving an implicit "from" for decomposed transform sub-properties
(translateX/Y, scale, scaleX/Y, rotate) authored without an explicit
`[from, to]` array. It silently defaults to identity instead, so any
element relying on a stylesheet rule like
`.node { transform: translate(-50%, -50%) scale(0); }` to start hidden
renders at (or near) its final position/scale from the very first frame.
Confirmed via isolated puppeteer reproduction: this is baked into anime's
internal tween node (`_fromNumber`) at `.add()` time, independent of seek
order or priming.

Correct each affected tween's `_fromNumber`/`_number` with the true
CSS-cascaded value (decomposed via DOMMatrixReadOnly) before the instance's
first-ever seek, in the existing per-instance priming pass. Verified
against registry/blocks/flowchart and registry/examples/kinetic-type
(same "content resolves ahead of schedule" defect family from the U-final
adjudication) with real render+compare-item PSNR improvement.
2026-07-09 20:27:14 -04:00
Miguel Angel Simon Sierra 3d87d63bc2 fix(core): recognize scoped hyperframesAnime self-registrations in sub-composition readiness poll 2026-07-09 18:11:12 -04:00
Miguel Angel Simon Sierra 66b4e0cc9b fix(core): recognize hyperframesAnime registrations in sub-composition readiness poll 2026-07-09 17:25:27 -04:00
Miguel Angel Simon Sierra 792bcff2a8 fix(core): restore priming-stranded style properties by keyframe timing not a fixed allowlist (U10z) 2026-07-09 16:33:49 -04:00
Miguel Angel Simon Sierra 8c9e17ec52 fix(core): seek nested anime.js compositions with local, offset time (U10z)
The anime.js adapter registers every composition and sub-composition as a
flat entry in window.__hfAnime and seeked ALL of them with the same raw
global time. Unlike GSAP (where sub-composition timelines are nested inside
a parent timeline and inherit correct local time from that nesting), each
anime.js registration needs its own composition's cumulative start time
subtracted before seeking, or a sub-composition mounted at a nonzero
data-start renders far ahead of where it should be (confirmed: at global
t=1.6s with data-start=1.5s, the child received seek(1600ms) instead of the
correct seek(100ms)).

Resolve each registration's host via data-composition-id, subtract its
cumulative start (reusing the existing runtime start-time resolver), and
clamp to the authored/host duration before seeking.

Verified: registry/examples/product-promo average PSNR vs GSAP baseline
20.5dB -> wait, 33.3dB -> 50.5dB (damaged checkpoints 77 -> 32) with no
regressions in the other items spot-checked. Full regression suite passes
(82 files, 1168 tests) plus a new red/green test for the nested-seek case.
2026-07-09 14:39:46 -04:00
Miguel Angel Simon Sierra b83d07ec38 fix(core): preserve primed anime numeric styles 2026-07-09 12:37:01 -04:00
Miguel Angel Simon Sierra bffd500479 fix(core): stop anime.js priming from stranding visibility set() calls (U10y) 2026-07-09 12:13:46 -04:00
Miguel Angel Simon Sierra 2d34320575 fix(studio,parsers,core): browser-safe subpath exports for classifier and ease map
The studio browser bundle imported the parsers and core package roots
(via classifyAnimationRuntime and resolveEase), dragging in node-only
assetPaths through the barrel under vite's import condition. Adds
animation-runtime-detection and ease-map subpath exports and points the
browser imports at them; repo-wide oxfmt pass for files agents missed.
2026-07-09 07:43:56 -04:00
Miguel Angel Simon Sierra c73138633f docs: anime.js first-party across docs and agent surfaces (U16)
Split the animation guide into anime.js default and GSAP adapter paths.
Align docs, README, and agent templates on the new registration contract.
2026-07-09 01:16:46 -04:00
Miguel Angel Simon Sierra 8ceec2aae9 feat(studio): anime.js client editing behind the engine-neutral interface (U14) 2026-07-08 23:56:25 -04:00
Miguel Angel Simon Sierra 6d4ad62d2a feat(studio-server,studio): anime.js edit path behind runtime detection (U13)
Anime parse/mutation routes alongside untouched /gsap-* routes;
classifier-backed engine detection replacing divergent ad hoc regexes;
anime Studio-motion and manual-edit render scripts registering via
hyperframesAnime; engine-routed soft reload with a new anime path;
mixed-file property-collision edits rejected. U5 characterization
snapshots pass unmodified.
2026-07-08 22:52:51 -04:00
Miguel Angel Simon Sierra 85fdcef034 feat(scripts): extend gsap-to-anime coverage and fix silent-data-loss classes (U9)
Round 2: forEach over named const arrays, direct window.__timelines
member registration, no-op .call labels. Fixes four silent-data-loss
bugs: mutated arrays trusted at initial literal, helper declarations
dropped without expansion, bare reassignment and loop counters treated
as constants (which also overcounted round-1 conversions; 9 files
honestly demoted to manual). Final: 28 converted / 2 warnings / 117
manual. Shared finitePositiveNumber extracted in eval scripts.
2026-07-08 20:27:04 -04:00
Miguel Angel Simon Sierra c938b7b3d0 fix(core,producer): prime wrappers without duration; forbid cross-instance property ownership
Gate re-run passes 8/8. Duck-typed anime wrappers lacking a readable
duration now prime via a large clamped seek. The animejs-adapter fixture
had two registered timelines animating scale on the same element, which
is order-dependent under independent seeks; corrected per the new
one-property-owner contract rule recorded in U3-GATE-RESULT.md. Eval
scripts get the scripts-convention fallow suppressions.
2026-07-08 19:46:57 -04:00
Miguel Angel Simon Sierra 9e16f191d1 Merge branch 'animejs-u19' into worktree-feat-animejs-first-party
# Conflicts:
#	package.json
2026-07-08 19:01:00 -04:00
Miguel Angel Simon Sierra ac83a74ef2 feat(eval): gsap-main baseline renderer and psnr compare cli (U19) 2026-07-08 19:00:25 -04:00
Miguel Angel Simon Sierra 64a6907475 chore(parsers,cli): apply twin-parser fallow conventions to anime parser (U12) 2026-07-08 18:59:40 -04:00
Miguel Angel Simon Sierra 1355baf7b6 Merge branch 'animejs-u12' into worktree-feat-animejs-first-party 2026-07-08 18:55:44 -04:00
Miguel Angel Simon Sierra 5f4183d88d fix(core): prime anime.js timelines at registration for cold-seek correctness (U1)
Anime.js 4.5.0 timeline children added at position greater than zero do
not render their from-values until the timeline has been sought past
that position once; a cold seek to frame 0 left them untouched. Prime
each instance once at registration (seek to duration, back to 0),
mirroring the GSAP adapter's totalTime nudge. Covers keyed and legacy
array registrations. Found by the U3 determinism gate.
2026-07-08 18:54:27 -04:00
Miguel Angel Simon Sierra de7c30902a chore: merge U8 anime defaults and pay down fallow debt
Concludes the animejs-u8 merge (generators, templates, CLI defaults,
figma emitters, registry runtime metadata) and clears all introduced
fallow findings: refactors stripJavaScriptComments/tagHasAttribute/
findInlineScriptBlocks in runtime detection, shared top-level scanner
and rawText module in the codemod, scan-state transition split, test
clone dedup, index/report cycle break, workspace deps declared.
2026-07-08 18:53:14 -04:00
Miguel Angel Simon Sierra e593b6b201 fix(core): emit native anime cubic bezier eases 2026-07-08 17:47:59 -04:00
Miguel Angel Simon Sierra 004f2bc560 feat(parsers,cli): anime.js acorn parser/writer and keyframes depth (U12) 2026-07-08 17:36:46 -04:00
Miguel Angel Simon Sierra 3f13d42d6b feat(core,cli): anime.js default generators, templates, and registry runtime metadata (U8) 2026-07-08 17:35:15 -04:00
Miguel Angel Simon Sierra 6ab514ba70 Merge branch 'animejs-u3' into worktree-feat-animejs-first-party 2026-07-08 17:30:10 -04:00
Miguel Angel Simon Sierra a3021afb40 Merge branch 'animejs-u7' into worktree-feat-animejs-first-party 2026-07-08 17:27:14 -04:00
Miguel Angel Simon Sierra f35e7d8216 feat(producer,studio-server,player): anime.js dependency plumbing (U7) 2026-07-08 17:23:34 -04:00
Miguel Angel Simon Sierra 7844e82883 fix(producer): stop nested named functions breaking page.evaluate in determinism gate (U3) 2026-07-08 17:09:25 -04:00
Miguel Angel Simon Sierra 6b35f4defd Merge branch 'animejs-u4' into worktree-feat-animejs-first-party 2026-07-08 17:03:19 -04:00
Miguel Angel Simon Sierra 2eb0697def fix(producer): narrow anime engine activity check type (U3) 2026-07-08 17:03:03 -04:00
Miguel Angel Simon Sierra 9f2e8f56f8 feat(core): anime.js sub-composition scoping (U4) 2026-07-08 17:02:00 -04:00
Miguel Angel Simon Sierra d6567fee06 Merge branch 'animejs-u6' into worktree-feat-animejs-first-party 2026-07-08 17:01:55 -04:00
Miguel Angel Simon Sierra f905f67d54 feat(lint): anime.js first-party lint rules (U6)
Allow timeline registry lint to accept hyperframesAnime.register() and __hfAnime.
Treat registered finite anime instances as duration sources.
Add anime.js registration, autoplay, and infinite-loop diagnostics.
Make the validate CDN-timeout hint engine-neutral.
2026-07-08 17:00:45 -04:00
Miguel Angel Simon Sierra c3d1b79b07 Merge branch 'animejs-u18' into worktree-feat-animejs-first-party 2026-07-08 16:59:31 -04:00
Miguel Angel Simon Sierra 3d1898e5ec feat(parsers): shared animation runtime classifier (U18) 2026-07-08 16:58:07 -04:00
Miguel Angel Simon Sierra 78e4582d01 test(producer): anime.js 4.5.0 determinism gate fixtures (U3)
Adds focused anime.js 4.5 determinism fixtures plus a Puppeteer gate that compiles and serves each fixture through the producer runtime path, then checks repeat seeks, out-of-order seeks, backward seeks, bounds behavior, and anime engine activity from DOM snapshots.
2026-07-08 16:58:05 -04:00
Miguel Angel Simon Sierra 58bf96752a Merge branch 'animejs-u2' into worktree-feat-animejs-first-party 2026-07-08 16:38:14 -04:00
Miguel Angel Simon Sierra 127b62a39f feat(core): gsap-to-anime ease map and CustomEase shim (U2) 2026-07-08 16:36:24 -04:00
Miguel Angel Simon Sierra 3e1ecf77bb feat(core): anime.js first-party runtime contract (U1) 2026-07-08 16:30:08 -04:00
kiritowoo 81884a7495 fix(cli,skills): install workflow skills on demand instead of re-pulling the full set (#2012)
* fix(cli,skills): install workflow skills on demand instead of re-pulling the full set

Users report every init re-pulls all 21 skills into ~/.agents/skills
whenever anything is stale or missing - heavy, noisy, and it re-expands
deliberate partial installs.

Split the set into two tiers:

- core: the /hyperframes router + hyperframes-* domain skills +
  media-use, which every workflow references structurally. init and
  bare 'skills update' keep these (plus anything already installed)
  fresh, and never expand the install.
- on demand: the end-user workflow skills (and figma). They install at
  trigger time via 'skills update <name...>' - positional names are
  the only way update expands an install: one targeted
  'skills add --skill <name>' covering only stale/missing targets, a
  fast no-op when current, presence-verified after install, exit 1 on
  unknown names, and a presence-only degrade when GitHub is
  unreachable.

The /hyperframes router now runs 'skills update <workflow>' after
routing and before reading the workflow skill, so a routed workflow is
guaranteed present even on a machine that only has the core set. Each
on-demand skill also opens with the same self-maintenance step (run
'npx hyperframes skills update <name>' silently), so a workflow
triggered directly - without the router - still refreshes itself and
restores any missing core skill before relying on it.

When the manifest is unreachable (offline / rate-limited) the engine
degrades honestly instead of claiming success: named runs presence-check
the request plus a pinned fallback core list (unit-pinned to skills/)
and blind-install whatever is absent; a bare strict update fails loudly
so the 'check || update' chain can't pass while everything stays stale;
init reports the skipped freshness check. --json emits structured
errors on failure paths.

skills check still lists every skill, but exits non-zero only for
stale installed skills, an incomplete core set, or removed leftovers -
workflow skills not yet installed are reported as available on demand.
Bare 'hyperframes skills' (and 'skills add --all') remain the explicit
full-set installs.

Verified end-to-end with a sandboxed $HOME: fresh init installs the 9
core skills only; 'skills update slideshow' adds exactly that skill
(no-op on re-run, exit 1 on unknown names); bare update refreshes
without expanding; a live Claude Code run routed PR-to-video, executed
the router's update step, and the workflow skill appeared before use;
and a second live run triggered an installed workflow directly, whose
opening maintenance step restored a deliberately removed core skill.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* docs(skills): clarify update-engine contracts + document lazy-install model

- skills.ts: note the UpdateSkillsResult.unknown strict-mode contract,
  verifyInstalled's non-strict (warn-not-throw) intent, and that a
  partial install stays "refreshed but never expanded" (review nits).
- docs/guides/skills.mdx: add a "Keeping skills current" section covering
  the core-eager / workflow-on-demand model and the skills check|update
  commands, per the repo's catalog-maintenance rule.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: kiritowoo <295860553+kiritowoo@users.noreply.github.com>
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Co-authored-by: Miao Yang <miao.yang@heygen.com>
2026-07-09 01:30:51 +08:00
Vance Ingalls e52bfc246a Merge pull request #2053 from heygen-com/vi/figma-loop-fixes
fix(core,cli,lint): close the figma brand-token loop — runtime CSS variables, --name, snippet lint
2026-07-08 09:27:19 -07:00
Vance Ingalls d9368ec051 fix(core): address PR feedback — ReDoS-safe slug trim, getVariables cleanups
- slugify: replace the anchored alternated trim regex (/^-+|-+$/g) with a
  character-scan trim — CodeQL js/polynomial-redos blocker.
- readRenderOverrides: fold the readOverrides wrapper into the exported
  function (one name, no pass-through).
- getVariables: deduplicate declarers with a Set, matching
  injectCompositionCssVariables.
- Move the tokenSlug import to the top of the file.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-08 02:34:18 -07:00
Vance Ingalls e2c88ef689 fix(core,producer): composition CSS variables reach the render path at eval time
Live testing of the compile-time variable emission surfaced four gaps:

- The producer render path never emitted the compile-time stylesheet (only
  the preview bundler did), so eval-time reads — GSAP .from immediateRender,
  top-level getComputedStyle — saw undefined vars in rendered output. The
  producer's inlineSubCompositions now calls the shared
  emitRootCompositionVariableStyles and passes the variable hooks.
- --variables overrides weren't visible at eval time. They now thread from
  the orchestrator / distributed plan through compileStage into the emitted
  rules (window.__hfVariables still covers script reads).
- Per-declarer rules anchored on data-composition-id, which two inlined
  instances of one sub-composition share — instance A's rule restyled
  instance B, and a rule directly on the declarer defeated the host's
  inherited data-variable-values. Rules now anchor on per-instance
  data-hf-var-scope markers and layer nearest-host values over declared
  defaults, mirroring the runtime loader.
- Emission ignored authored CSS; a declared default now yields to a var
  already defined in an authored <style> block (define-if-absent, matching
  the runtime injection).

Also: the figma importer emits background-color (longhand) for solid fills.
GSAP backgroundColor tweens cannot read a var() through the background
shorthand — its pending-substitution longhands serialize empty, so .from
captured nothing and settled on transparent (pre-existing GSAP interaction,
reproduced with no composition variables involved).

Validated live: eval-time default + override, .from + override, two-instance
host branding, authored :root precedence, SDS brand-loop pixel parity.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-08 02:28:16 -07:00