mirror of
https://github.com/heygen-com/hyperframes.git
synced 2026-11-06 14:47:29 +00:00
cloudrender/distributed-map
237
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
ab06445439 |
chore(catalog): publish generated catalog (#5500)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> |
||
|
|
37a79e9371 |
fix(registry): clamp filmed-screen's curve and vignette (#5501)
* fix(registry): clamp filmed-screen's curve and vignette Post-merge review of #5499: - curve is clamped at 0: a negative value samples outside the frame and folds over below about -0.32. - the vignette factor is clamped at 0: above vignette ~1.11 it went negative in the corners. - README: a "previous look" line with the pre-#5499 values, noting that the LED fade-in threshold and the trace of fringing on the focused element are hard-coded. The demo renders the same: at the defaults neither clamp is reached. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(registry): point filmed-screen's catalog page at its recorded preview The catalog builds this item's payload as unsupported (it needs canvas-draw-element), and without preview.video the page fell back to the flag notice: "Needs chrome://flags/#canvas-draw-element to render live" under a "Recorded preview" caption, with no video, whatever the visitor's flags. The manifest now names the CDN video and poster the way the liquid-glass blocks do; the regenerated page carries video= and poster= instead of needsFlag. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
3886aaec28 |
feat(registry): filmed-screen curves, orbits and follows typing by default (#5499)
New options, with defaults tuned on a white page filmed the way Vox shoots screens: - vignette (1.0, was a fixed 0.42) and aberration (2.2x, with a quarter of it kept on the focused text so it still reads as glass) - curve (0.15): a barrel bend normalised so the frame's corners stay put - led (0.45): the LED grid now only fades in once a cell spans ~4 output px; finer, it aliased into moire on light pages - tiltTo per shot: the tilt eases between the two, for an orbit - rack 0.3 (was 0.7), fast enough to follow text as it is typed, and shot blur 0.45 (was 0.9) The demo is now a white search homepage for a made-up engine, "lumen": a query types letter by letter while the camera follows each new letter and orbits from one side of the screen to the other, all on defaults. Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
4f941de0da |
chore(catalog): publish generated catalog (#5498)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> |
||
|
|
071ce8044f |
feat(registry): add filmed-screen component (#5487)
* feat(registry): add filmed-screen component Films a live-DOM scene like a camera pointed at a monitor: a tilted screen plane in perspective, depth of field that follows a named element, chromatic fringing, LED sub-pixels, per-shot palettes and cut tears. The focused element stays sharp. Ported from ff-tracking's lens.sksl, with each colour channel weighted by its own sample in the bokeh average. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(registry): land the filmed-screen catalog poster on the title The catalog poster is captured at min(3 s, 0.6 × duration), which was the demo's first cut, so it showed a tear over a blurred title. The first cut moves to 3.8 s and the first shot racks back to the title before it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(registry): filmed-screen glides between shots and racks focus Shots now glide into each other by default: camera, palette and colour mode ease over `glide` seconds, and `transition: "cut"` keeps the hard cut with its tear. Focus racks instead of snapping: the focus box is a Hann-weighted average of where it was over the last `rack` seconds, so a new target eases in on an S-curve and every seek order still renders the same frame. The demo now films three scenes in the style of other compositions, a code editor, an analytics card and a chat thread, one shot and grade each. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(registry): filmed-screen shares the composite slot and films each instance Review on #5487: - The composite hooks no longer cycle with data-vfx-chain. Both re-arm every seek, and core's resolver reads its prior when called, so a stale wrapper of ours is re-entered through it. Each wrapper now keeps its own prior and returns at once when re-entered, so the chain stays a constant depth. The slot is only claimed once a lens is mounted. - A second instance films its own canvas. mount() takes the snippet's caller-scoped query result and picks the first canvas not already filmed; the library's own document belongs to whichever copy loaded first. - No WebGL2 now steps aside like no HTML-in-Canvas. mount() returns { destroy() }, a lens whose canvas leaves the page is dropped, and a lens whose composition is not on screen skips its capture instead of logging. - Docs: fadeOut and zIndex, the rack comment, and the description and tags now describe glides instead of cuts. filmed-screen.test.mjs runs both arm orders against core's arming pattern, two mounts over two canvases, and that the demo pastes the snippet's library. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * test(registry): filmed-screen test passes oxlint The vfx stand-in returned `true || prior`, which oxlint rejects as a constant-truthiness expression. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * test(registry): match script tags case-insensitively in the filmed-screen test CodeQL reads the extraction regexps as HTML filters (js/bad-tag-filter). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(registry): filmed-screen waits for the scene's first paint instead of erroring In Studio's preview the first draw can run before Chrome has painted the layoutsubtree scene once (the paint wait gives up after 250 ms on a busy machine). drawElementImage then throws "No cached paint record", which was logged with console.error and surfaced as a preview console error. That error is now a wait: the lens requests a paint and draws again when it lands. Other capture failures still log. Render output is unchanged: the demo renders byte-identical to before. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
d0b551d033 |
fix(registry): clear the product-promo example's lint errors (#5481)
The example's own `hyperframes lint` failed with four gsap_css_transform_conflict errors: CSS set an initial transform on .dragged-card, .stamped-cards and .checkmark, and GSAP then tweens the same properties. Move those initial values into gsap.set() calls next to the scene's other initial states. Rendered frames are unchanged. |
||
|
|
faa4913c9e |
chore(catalog): publish generated catalog (#4340)
* chore(catalog): publish generated catalog * chore(catalog): publish generated catalog * chore(catalog): publish generated catalog * chore(catalog): publish generated catalog * chore(catalog): publish generated catalog * chore(catalog): publish generated catalog * chore(catalog): publish generated catalog * chore(catalog): publish generated catalog * chore(catalog): publish generated catalog * chore(catalog): publish generated catalog * chore(catalog): publish generated catalog --------- Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> |
||
|
|
7b85b28406 |
fix(ci): keep the catalog publish PR when main changes nothing it generates (#5372)
* fix(ci): keep the catalog publish PR when main changes nothing it generates The bot compared the whole branch tree with main, so every main push rewrote the standing PR and dismissed its approval. It now compares only generated catalog files, with search vectors judged by the drift check's cosine rule. * fix(ci): republish when main edits generated files or the branch carries others * docs(registry): motion-blur copies explain their inline styles in three lines * refactor(ci): split the kept-snapshot check into named conditions |
||
|
|
b059600083 | feat(registry): seven more WebGPU shader backgrounds, from sun burst to color wheel (#5341) | ||
|
|
9dbbca7459 |
feat(registry): seven more WebGPU shader backgrounds, from ripples to thin film (#5190)
* feat(registry): seven more WebGPU shader backgrounds, from ripples to thin film * fix(registry): describe the three shader orbs as orbs |
||
|
|
46f6cb3567 |
feat(registry): eight more WebGPU shader backgrounds, from blob to fractal noise (#5192)
* feat(registry): eight more WebGPU shader backgrounds and a bundle with thirty new shaders * refactor(registry): simpler bundle copy step in the shader build * test(registry): every shader block ships the same bundle and names a shader in it * refactor(registry): one block loader for the shader block tests * test(registry): name the stale block and match only shader names * feat(registry): eight more WebGPU shader backgrounds, from blob to fractal noise * chore(registry): shorter licence line at the top of the shader bundle * chore(registry): bring in the latest shader bundle branch * chore(registry): shorter licence line at the top of the shader bundle * test(registry): every shader block installs the bundle's licences |
||
|
|
63a7ec692f |
feat(registry): eight more WebGPU shader backgrounds and a bundle with thirty new shaders (#5191)
* feat(registry): eight more WebGPU shader backgrounds and a bundle with thirty new shaders * refactor(registry): simpler bundle copy step in the shader build * test(registry): every shader block ships the same bundle and names a shader in it * refactor(registry): one block loader for the shader block tests * test(registry): name the stale block and match only shader names * chore(registry): shorter licence line at the top of the shader bundle * test(registry): every shader block installs the bundle's licences |
||
|
|
f6b3821626 | fix(catalog): run block scripts inline in the docs player, and pages for six shader blocks (#5139) | ||
|
|
7cf042d74f | feat(registry): six WebGPU shader backgrounds from the Shaders library (#5138) | ||
|
|
b50dead1da |
feat(registry): gooey split block with metaball goo and directional motion blur (#5123)
* feat(registry): gooey split block with metaball goo and directional motion blur A dot splits into a three-dot menu through a gooey neck, spins into a swirl mark under shutter motion blur, breaks back into three dots and merges into one. Timing, spacing and eases are measured off a reference recording frame by frame. The motion-blur snippet gains an opt-in `sharp: 0` option that hides the crisp frame-time copy while an element moves, leaving only the shutter average. The default is unchanged; the inlined copies carry the same text. * feat(registry): grow gooey-split blades out of swelling dots * refactor(registry): tidy gooey-split pose code and document sharp 0 limits * fix(registry): hide a sharp 0 motion-blur target by marker, not its visibility Hiding through the element's own visibility fought timelines that write it (autoAlpha): a target faded in after attach stayed hidden at rest. A marker attribute and one !important rule now do the hiding, the default path writes nothing, and a declarative sharp other than 0 or 1 is refused like any other bad option. * fix(registry): smear a motion-blur target that was hidden when blur attached Copies froze the target's computed visibility at attach, so a target entering with autoAlpha never smeared (and vanished entirely under sharp 0). The group now carries the target's current visibility each frame, read with the sharp 0 marker lifted, and a copy keeps a descendant's visibility only where it differs from its parent's. * test(registry): pin a copy keeping a descendant's own visibility * fix(registry): keep a child's own hiding in the smear of a target hidden at attach * refactor(registry): restore a lifted visibility in one call and lift any hidden form * fix(registry): restore a lifted visibility even when copying styles throws * fix(registry): let motion-blur copies hide with a clip that has ended The smear group set an explicit visibility on every moving frame, and copies used to freeze an explicit visible, so an ancestor clip hiding after the last update could not hide them: copies from a 0-4 s sub-composition stayed on screen at 16.4 s. The group now sets a visibility only where the element's differs from its parent's. Also regenerates skills-manifest.json for the motion-blur skill doc. |
||
|
|
d2398d686a |
feat(registry): eight ad templates show their video preview in the catalog (#4979)
* feat(registry): eight ad templates show their video preview in the catalog * refactor(scripts): the preview backfill reuses the shared entrypoint guard |
||
|
|
04acbb4d9e |
feat(registry): add 4 community components (#4906)
* feat(registry): add cinemagraph — a still photograph with one living region * feat(registry): add ai-answer-rank — a mock AI answer that re-ranks to put your brand in it A 1080x1080, 9s block for answer-engine and AI-visibility marketing. A prompt types into a chat card, the assistant answers with a ranked list of three picks, then re-ranks: the promoted brand slides into its slot (brandRank 1-3), the picks below step down, the third fades out of the list, and the brand row lands with the one accent highlight and a drawn check. Monochrome placeholder UI; prompt, lead-in, pick names, brand, rank, accent, ink and ground are variables. * feat(registry): add caret swap text effect * feat(registry): add pendulum-swing component Add a motion-primitive component that hangs content from a fixed pivot and lets it settle with a damped oscillation, like a hanging sign coming to rest on its hook. The swing rides a single unitless custom property (--hf-swing-angle) that the timeline eases 1 -> 0, so only rotation animates and it stays sub-pixel under the seek-by-frame capture engine. The demo drives it with a damped-oscillation ease (a cosine inside an exponential decay, normalised to land exactly upright) so the sign passes through center several times, each pass smaller, before it rests. Four enum variables: pivot (top-center/left/right), amplitude (gentle/standard/wide), direction (right/left), tone (ink/paper/accent, each a bg/fg pair to keep AA contrast on any frame). The snippet is recipe-only and does not own its timeline; its demo carries the same variable declaration. Ran npx hyperframes lint/validate on both files, regenerated registry.json via scripts/generate-registry-items.ts and the docs via scripts/generate-catalog-pages.ts. * fix(registry): drop hand-added cinemagraph index entry; catalog-publish adds it * style(registry): format pendulum-swing with oxfmt * fix(registry): keep pendulum-swing demo caption clear of the swinging sign * fix(registry): ship the ad-template editing contract for ai-answer-rank --------- Co-authored-by: Carlos Miranda Levy <carlosmirandalevy@gmail.com> Co-authored-by: tugrawork-creator <319556794+tugrawork-creator@users.noreply.github.com> Co-authored-by: Akshar Patel <aksharcommit@gmail.com> Co-authored-by: rottenpen <12029924+rottenpen@users.noreply.github.com> |
||
|
|
f2eb6d9cc1 |
fix(lint): flag seek-unsafe CSS transitions (#3797)
* fix(lint): flag seek-unsafe CSS transitions * fix(lint): allow zero-duration transition resets in css_transition_used The registry's reduced-motion and render-lock resets use transition-duration: 0s and transition-delay: 0s, which never run on the browser clock. Also drops the one real seek-unsafe transition the rule finds in the product-promo example. * refactor(lint): split the zero-time check out of isSeekUnsafeTransition * fix(lint): report seek-unsafe CSS transitions as a warning --------- Co-authored-by: Miguel Angel Simon Sierra <miguel.sierra@heygen.com> |
||
|
|
37f30b1d06 |
feat: keep a video's sound on the clip by default (1/8) (#4813)
* feat(cli): one owner for init media placeholders, audio stays on the video Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(cli): init keeps a video's sound on the clip; silent files scaffold muted Supersedes the patchVideoSrc changes in #3977 and #3942 and carries their tests. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Co-Authored-By: miga-heygen <miga-heygen@users.noreply.github.com> Co-Authored-By: kapelame <kapelame@users.noreply.github.com> * feat(registry): example templates keep the a-roll's sound on the video Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(lint): video audio rules recommend keeping sound on the video Update video_missing_muted and video_audio_double_source message and fixHint text to recommend the new default: keep audio on the video with data-has-audio="true" (not muted). Tests verify the new wording is in place. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(parsers): a video is either audible (data-has-audio) or muted, never both Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(studio): follow the compiler's audio rule in the audio-track toggle Confirm before background removal drops a video's sound. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * docs(skills): hyperframes-core teaches sound on the video by default Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * docs(skills): workflows and scaffold guidance keep a video's sound on the clip Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * docs: footage with sound keeps it on the video clip Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(cli): init from-file a-roll is timed, muted strip is attribute-exact The from-file a-roll video gets data-start="0" so a scaffold lints clean, and the orphaned audio-only slot comment is dropped. setVideoAudio now walks the tag attribute by attribute, so a class value containing the word muted survives. The ffprobe-missing warning says the video is assumed to have sound and how to switch to muted, and DEFAULT_META notes the failure surfaces at render until lint support lands. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(studio): keep has-audio in step with the muted toggle, test cutout confirm Muting a video now clears has-audio and unmuting sets it to true, in both the Design and flat media panels, matching the Has audio track toggle. Audio elements are unchanged. Adds coverage for both panels plus the cutout window.confirm guard: declined leaves src alone, accepted swaps src and mutes, a silent video never prompts. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(parsers): updateElementInHtml hasAudio only applies to videos, true or false An audio element or an undefined hasAudio no longer picks up a muted attribute; only an explicit false on a video writes muted. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * docs: video_missing_muted rule text, silent-footage notes, consistent audible examples lint.mdx and cli.mdx describe the current video_missing_muted rule instead of the retired unmuted-video wording. The editing recipes say how to adapt for silent footage and require the outgoing clip's sound to end at a J-cut. The Remotion media example shows the audible mapping consistently, data-attributes states that has-audio is required unless muted, and the hairline frame note no longer claims the video is muted. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(cli): encode init media filenames and probe --audio duration Function replacers stop dollar-sign pattern expansion in filenames; src is percent-encoded. --audio now passes the probed track length as the composition duration in both init paths. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(lint): audio-state-aware media rules and time-aware double-source check video_missing_muted honors any explicit data-has-audio. video_audio_double_source only considers audible videos and only fires when the same-src clips overlap in time. duplicate_audio_track now covers audible videos. audio_file_without_element no longer claims silence when an audible video carries the sound. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * docs(skills): audio-stream check for remotion ports, followable J-cut recipe Remotion Video maps to data-has-audio only when the file has an audio stream. The J/L-cut recipe now shows both shots with muted pictures and separate audio. embedded-captions removes the audio.mp3 scratch file it extracts. skills-manifest.json regenerated. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * refactor(lint): trim comments to satisfy the comment ratchet Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(studio): atomic muted edits, cutout keeps sound on a linked audio Muted and Has audio track commit one batched write (one persist, one undo entry) and remove data-has-audio instead of writing an empty marker. Unmuting a video probes its src: audible -> true, silent -> false, probe failed -> absent. Remove background on an audible video inserts a linked sibling <audio> with the original sound in the same write, replacing the drop-the-sound confirm. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(producer): skip hidden videos when auto-adding video audio A video hidden in Studio is dropped by the mixer, so its src never reached existingAudioSrcs and the render re-added it at full volume. The video parser now records the shared hidden predicate and the auto-detect step skips hidden videos. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(studio): keep useDomEditSession under the 600-line cap Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> Co-authored-by: miga-heygen <miga-heygen@users.noreply.github.com> Co-authored-by: kapelame <kapelame@users.noreply.github.com> |
||
|
|
590c192498 |
feat(catalog): add 3D objects, cursors, snow, starfield and paper transitions (#4349)
* feat(catalog): shelve 3D objects and cursors in their own catalog sections Carries the generator grouping rules from #4310 and #4308. * feat(registry): add a starfield ambient overlay From #4313. * feat(registry): add a falling snow ambient overlay From #4331. * feat(registry): add a torn-paper-rip transition From #4321. * feat(registry): add a paper-collage-reveal transition From #4322. * feat(registry): add a curtain-reveal transition From #4323. * feat(registry): add 3D objects: product turntable, glass hero, exploded stack, extruded logo From #4310. * feat(registry): add eight cursor animations and the dart cursor From #4308. * fix(catalog): find items by everyday editor words and accept a bare search word From #4311. * chore(registry): remove the simulated-cursor component The Cursors section's new pointer animations replace it (from #4308). * fix(registry): take cursor-zoom-follow's screenshot from runtime variables only The declared default is empty, so page markup never becomes an image URL. Clears the code-scanning alert raised on #4308. * docs(cli): show a catalog search example that finds items "smoke" matches nothing in the catalog; "crossfade" finds fade-through. * chore(ci): allow the simulated-cursor removal in the deletion guard * fix(ci): skip tracked files missing from disk in the player CDN pin check Catalog regeneration removes a deleted item's pages before the publish PR untracks them, so the check crashed on every catalog deletion PR. * chore(cli): drop comments that narrate catalog search history * fix(registry): drag-drop keeps the family arrow and shows the drop slot only while hovering * fix(registry): agent cursor labels its steps in an accent tag, no glow or sparkles, in 4.8 s * fix(registry): annotate cursor draws a tapered marker stroke and clears the circled value * fix(registry): show real file thumbnails and end the context menu demo sooner * fix(registry): give marquee select real photos, a Finder-blue marquee and a tighter loop * fix(registry): centre the text-select toolbar on the selection and tighten its timing * fix(registry): selection cursors default to the macOS system blue * style(registry): format the agent and annotate cursor sources * fix(registry): dart-cursor stops covering the choice it makes and shows the export finishing * fix(registry): frame the product turntable fully and light it with two studio softboxes * fix(registry): touch-indicator taps like a real phone and the post it taps gets liked * fix(registry): press-ripple confirms with a check and small click rings at the cursor * fix(registry): exploded stack separates at once and marks each layer in blue as it is labelled * fix(registry): extruded logo settles facing the camera, set in Geist Bold with a neutral rim * fix(registry): magnetic cursor docks its toolbar on the selection and ends after the press * style(registry): format the exploded stack source * fix(registry): glyph trail becomes a tutorial cursor path with numbered steps on a settings screen * fix(registry): make glass-hero a clear lens that shows the headline through it * fix(registry): snow reads as real snowfall with bokeh flakes over a winter title card * fix(registry): oversized cursor is a 3.5x arrow that clicks a real editor toggle on * fix(registry): starfield reads as a drifting night sky over a real title card * style(registry): format the glass hero source * fix(registry): make the paper tear show a white fibrous edge and lift the torn sheet away * fix(registry): curtain reveal draws velvet folds that gather onto a title card * fix(registry): make every zoom-follow click change the screen and read at zoom * fix(registry): paper collage pieces stay opaque, tear with deckled grain and lift away on an arc * refactor(registry): drop dead code and correct stale comments in reworked items * fix(registry): agent cursor scrubs cleanly, docs match press-ripple, fictional demo names * docs(registry): catalog notes say oversized-cursor exits and how it differs from press-ripple * docs(registry): plain lowercase comments in oversized-cursor and touch-indicator * docs(registry): oversized-cursor names CLICK_AT as the click cue |
||
|
|
8798e40da8 |
fix(registry): carousel cards fall back to their sample image inside the assets folder (#4543)
* fix(registry): carousel cards fall back to their sample image inside the assets folder When an image variable resolves to nothing, the 25 carousel blocks loaded the sample card by bare file name, which 404s beside the block and draws a white card. The fallback now keeps assets/carousel-images/, the folder every card image is installed into. A guard test fails on any block with a bare fallback. * test(registry): the carousel fallback guard covers every block that uses it The guard now picks each block that reads an image variable and fails when its fallback no longer matches, instead of silently skipping it, and ignores stray files in the blocks folder. |
||
|
|
ec7f473d84 |
fix(registry): cuboid-carousel plays from the start when mounted (#4455)
* feat(core): a composition's script can load its own files with __hyperframes.assetUrl * test(core): a template composition inside a mounted file resolves assets from that file * docs: assetUrl is for paths beside the composition, not project-root paths * docs: say assetUrl misses a project-root path * fix(core): a mounted composition's import map and module scripts run as such * refactor(core): scope a mounted module script under the same condition as a classic one * fix(core): a mounted module script loaded by src stays a module * fix(core): a mounted import map keeps the trailing slash of a prefix address * fix(registry): six blocks load their own files and variables when mounted * fix(registry): keep frost's variable declarations on its html element * fix(registry): cuboid-carousel plays from the start when mounted * fix(registry): orbit-card uses its own variables; frost font and catalog maps follow * chore(core): keep the module-script helpers' comment share at the base * fix(catalog): frost's payload runs its driving script, not the asset-base one * test(catalog): check import-map keys by membership, not a built regex |
||
|
|
b25fd4f388 |
fix(registry): three blocks keep their settings when mounted (#4463)
* fix(sdk): read a composition's variables from its root when its html has none * fix(registry): three blocks declare their variables on their own root so they work mounted * fix: composition metadata and lint also read variables declared on the root * fix(sdk): keep root declarations on the root through undo and prefer them like the runtime * fix(sdk): read variables from both <html> and the root, editing each where declared |
||
|
|
7c634426fd |
docs(cli): explain how catalog search works and which model it downloads (#4462)
* docs(cli): explain how catalog search works and which model it downloads * docs(cli): name the model catalog search was chosen against * docs(cli): keep only live rules in the word-search header comment * docs(cli): correct the hosted vector size in the local vector build comment |
||
|
|
53e2e20a15 |
fix(registry): six blocks load their own files and variables when mounted (#4452)
* feat(core): a composition's script can load its own files with __hyperframes.assetUrl * test(core): a template composition inside a mounted file resolves assets from that file * docs: assetUrl is for paths beside the composition, not project-root paths * docs: say assetUrl misses a project-root path * fix(core): a mounted composition's import map and module scripts run as such * refactor(core): scope a mounted module script under the same condition as a classic one * fix(core): a mounted module script loaded by src stays a module * fix(core): a mounted import map keeps the trailing slash of a prefix address * fix(registry): six blocks load their own files and variables when mounted * fix(registry): keep frost's variable declarations on its html element * fix(registry): orbit-card uses its own variables; frost font and catalog maps follow * chore(core): keep the module-script helpers' comment share at the base * fix(catalog): frost's payload runs its driving script, not the asset-base one * test(catalog): check import-map keys by membership, not a built regex |
||
|
|
571c99ce9e |
fix(sdk): read a composition's variables from its root when its html has none (#4454)
* fix(sdk): read a composition's variables from its root when its html has none * fix: composition metadata and lint also read variables declared on the root * fix(sdk): keep root declarations on the root through undo and prefer them like the runtime * fix(sdk): read variables from both <html> and the root, editing each where declared |
||
|
|
8dabc8af63 |
fix(registry): glass-shard-title renders when mounted, and 4 skipped blocks install again (#4423)
* fix(registry): glass-shard-title renders when mounted, and 4 skipped blocks install again * fix(registry): glass-shard-title loads in Studio and ?play, and its docs payload fails loudly * test(scripts): pin the glass payload's hdr guard for any quote style |
||
|
|
8055961740 |
feat(registry): a Lottie character walk-in, and looping Lottie that keeps playing (#4316)
* fix(core): looping Lottie animations keep cycling instead of freezing after one pass * feat(registry): add a Lottie character walk-in and document the character route * chore(registry): index lottie-character-walk in the catalog and search vectors * chore(docs): list lottie-character-walk in the catalog gallery * docs(skills): say that lottie-web loops when loop is left out * fix(core): a finished Lottie holds its last frame instead of disappearing * fix(core): hold a finished Lottie on its exact last frame * chore(skills): refresh the manifest hash for the Lottie skill edits * fix(core): wrap looping Lottie by frame so a loop never holds at its seam * fix(core): a looping Lottie starts each cycle on frame 0 despite float error |
||
|
|
0945de83c7 |
feat(registry): text-match-cut (#4288)
* feat(registry): add text-match-cut — one text becomes another mid-move on a single 85 % curve Video primitive reversed from an After Effects tutorial (Editing Snapped, "The Match Cut Technique Every After Effects User Needs"), measured per frame at 60 fps. Both texts ride one keyframe pair, cubic-bezier(.85, 0, .15, 1) over 2 s, and hard-swap at the speed peak (1.0 s). A `mode` variable picks the property that carries the cut: scale down (the tutorial), scale up, tracking in, or a same-direction slide in four directions. Optional cyan-to-blue gradient sweep on its own 87 % curve, navy or transparent background. * chore(registry): integrate text-match-cut with current main --------- Co-authored-by: Jake Moran <jakemoran2011@gmail.com> |
||
|
|
b22bc2c979 |
feat(registry): extended-keyframe (#4281)
* feat(registry): add extended-keyframe, the never-fully-lands arrival with motion blur
A hyperframes:component. An element covers most of its move in the first fifth of the
duration and creeps the rest to the last frame (After Effects' extended-last-keyframe
technique), exits along the vector it arrived on, and carries shutter motion blur:
slides via the bundled feOffset filter smear, scale beats via a velocity-driven blur.
var a = attachExtendedArrival("#word", tl, { at: 0.3, from: { x: -1400 } });
attachExtendedArrival("#next", tl, { at: a.end, from: { scale: 0.35 } });
Direction and property agnostic (x / y / scale). The ease is exposed on its own as
EK.ease() for any tween, with EK.next() for sequencing and EK.gate() as a numeric check
(single velocity peak, no rises after it, tail above zero to the last frame).
registry/registry.json, docs/docs.json and docs/public/catalog-index.json regenerated
with the two generator scripts; they also pick up drift already present on main.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
* feat(registry): publish live extended keyframe payload
* fix(registry): tear down extended keyframe blur
* fix(catalog): refresh extended keyframe payload
* fix(catalog): preserve merged catalog entries
* fix(catalog): regenerate catalog index
* fix(registry): trim extended-keyframe comments
* chore(registry): retrigger checks
* fix(registry): refresh extended-keyframe artifacts
* fix(catalog): refresh extended-keyframe payload
* fix(registry): refresh extended-keyframe artifacts against current main
---------
Co-authored-by: Jake Moran <jakemoran2011@gmail.com>
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
|
||
|
|
05d09a7452 |
chore(registry): remove low-use catalog items and variant tails (#4274)
* chore(registry): remove low-use catalog items and variant tails * fix(registry): remove stale references to deleted catalog items * style: format catalog fixture * fix(ci): use surviving catalog preview canary * chore(ci): allow intentional catalog deletions * fix(ci): scope fallow audit for catalog fixtures * chore(catalog): regenerate local search vectors * chore(catalog): update vector revision * fix(catalog): validate registry set delta |
||
|
|
01734fae3c |
feat(registry): colorama-wipe (#4277)
* feat(registry): add colorama-wipe, a colour-mapped feathered wipe reveal for headlines
A hyperframes:component recreating the After Effects "shiny wipe": masked text +
Compound Blur + Colorama over a moving gradient, with a crisp duplicate fading on top.
One call per text element:
attachColoramaWipe("#headline", tl, { at, in, hold, out, cycle, feather, tilt, blur })
adds a single linear clock tween to the host timeline and paints the effect on two
canvases over the element's box; the element is hidden (visibility) with its layout
kept. All sizes scale from the element's font size. Deterministic: hash noise keyed by
frame, no Math.random, no clocks; seeks outside the window paint an empty stage.
registry/registry.json, docs/docs.json and docs/public/catalog-index.json were
regenerated with the two generator scripts as the contributing guide asks; they also
pick up drift already present on main.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
* feat(registry): publish live colorama wipe payload
* style(registry): format colorama wipe demo
* fix(registry): tear down colorama wipe on timeline kill
* fix(catalog): refresh colorama wipe payload
* fix(catalog): preserve merged catalog entries
* fix(catalog): regenerate catalog index
---------
Co-authored-by: Jake Moran <jakemoran2011@gmail.com>
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
|
||
|
|
d5e00c0e66 |
feat(registry): week in merges leaderboard block (#4264)
* feat(registry): add a week-in-merges leaderboard block Authors race across the row at a speed set by their merge count, bouncing at both edges. * chore(registry): cover week-in-merges in the catalog search index * style(registry): format the week-in-merges block Wrap the lines oxfmt rejects so the format check passes. * fix(registry): regenerate the week-in-merges catalog payload The format pass left the committed payload off the generator output. * feat(registry): cap week-in-merges at twelve rows The block races at most rows_max authors, highest merge count first, and writes one line for everyone left out. The default is 12, so a long list cannot shrink the rows. * fix(registry): make week-in-merges speed follow merge count A row now travels the leader's pace times its merges over the leader's count. Only a zero stays on the floor, so a near-tie no longer looks like last place. |
||
|
|
3091dcb787 |
feat(cli): route media search through media-use (#4230)
* feat(cli): move media-use engine into the CLI * fix(cli): replace media-use compatibility asset * fix(cli): replace moved media-use tree * fix(cli): copy moved media-use files cleanly * fix(cli): copy media-use engine files individually * fix(cli): keep audio helper compatibility import * fix(cli): clear copied skill destinations * test(cli): preserve moved media-use test paths * fix(cli): preserve media-use script compatibility imports * fix(cli): preserve media-use telemetry shim * fix(cli): resolve packaged media-use LUT assets * fix(cli): resolve packaged bundled SFX assets * chore(skills): regenerate media-use manifest * fix(cli): preserve media-use parity coverage * fix(cli): cover relocated media-use tests * chore(skills): regenerate media-use manifest * fix(cli): format bundled media provider * fix(ci): run relocated media-use tests with node * fix(ci): preserve fallow script exemptions * fix(ci): scope media-use fallow exemption * refactor(cli): trim media-use essay comments * feat(cli): index media-use assets for catalog search * feat(catalog): commit media-use search vectors * chore(catalog): track media vector metadata * fix(catalog): validate media vector row order * fix(catalog): validate media artifact freshness * fix(catalog): cover media metadata and credits * fix(cli): reject malformed media rows * fix(catalog): compare complete media rows * feat(cli): route media search through media-use * refactor(cli): trim relocated media-use comments * fix(cli): make media search runtime-safe * fix(cli): preserve sfx fallback diagnostics * fix(cli): probe sfx fallback provider explicitly * chore(skills): regenerate manifest * refactor(cli): keep typed media ranker source * fix(cli): align media search checks with local-first flow * fix(cli): scope vector audit exceptions * fix(cli): ship resolver registry beside skill * fix(cli): keep media-use skill scripts standalone * test(cli): cover local vector validation * test(cli): correct media vector fixtures * fix(cli): prefer exact media keys * fix(cli): stem exact media keys * test(cli): skip media lint for snippets without media * fix(cli): align skills test discovery Keep the CI skills job's bash discovery as the single source of truth so its empty-list guard covers the exact files Node executes. Make hasMediaVectors private because it has no external callers and needs no Fallow suppression. * fix(cli): close media-use review gaps Prioritize literal ids before stemmed ids and cover the collision alongside the plural regression. Add command seam tests for engine discovery, actionable failure, and argv passthrough. Add a deterministic parity check for standalone media-use copies, with documented allowlists for the standalone media-fetch shim and self-contained npx resolver. Keep the bash-discovered skills test list as the CI source of truth. * fix(cli): lower parity check complexity Split missing-copy and unallowlisted-drift checks so the parity guard keeps its behavior without a threshold CRAP score. * fix(cli): close media-use review seams Run the parity test in the skills job so skills-only edits exercise the same check. Export the verb mapping and inject command dispatch for direct coverage. Keep missing-copy diagnostics separate from drift reads. |
||
|
|
f24ff83834 |
feat(catalog): add the seven 3D-motion pieces as installable registry blocks (#4056)
* feat(catalog): add the seven 3D-motion pieces as installable registry blocks Each piece ships as a registry block with a CDN poster and preview, a live catalog detail page and its source. Scripts travel inside the catalog payload (shared vendor libraries fetched once, item scripts inline, the one HDR as a data URI) because the docs host only serves fonts, images and JSON. NO_ESSAY_COMMENTS_OK: the long comment blocks are in vendored libraries and the ported frost source under registry/blocks, not hand-written here. HF_MAX_NONLFS_KB=6000: four hosted texture PNGs under docs/public/catalog/items are runtime assets the docs host must serve as files. fallow skipped locally: it audits against origin/main and this branch is stacked on an older main. * chore(catalog): regenerate gallery data and nav after rebasing onto main * fix(catalog): read the install manifest through the bounded project reader * fix(catalog): a script the inliner missed fails the payload instead of shipping a dead URL * refactor(catalog): the leftover-script check reuses the reference finder * fix(catalog): frost and glass textures load from the CDN and the iteration presets are gone * chore(catalog): regenerate the frost and glass payloads and nav * fix(catalog): frost and glass textures are hosted through the registry url field, not hardcoded * chore(catalog): regenerate the frost and glass payloads against the hosted texture URLs * fix(catalog): the hosted-reference rewrite refuses a bare name it cannot safely replace * ci(catalog): verify changed catalog payloads in headless Chrome and check the docs catalog * feat(catalog): every item page follows the shared layout with a Tune panel and tabs * fix(catalog): let the texture-mask item host its masks so its tile plays live * chore(catalog): regenerate item pages, gallery data and the texture-mask payload * chore(catalog): regenerate item pages after merging main * fix(catalog): declared-only variables get the Tune panel, Code and Docs tabs capped * feat(catalog): give the blend-difference caption a demo scene so its tile plays live * chore(catalog): live payload for the blend-difference caption * perf(catalog): heavy 3d pieces compile and draw before ready, not on the first visible frame * feat(catalog): the avatar promo card hosts its videos, audio and fonts on the CDN * feat(catalog): the avatar promo card tile plays live from its CDN-hosted media * chore(catalog): the avatar promo card plays live, no allowlist left * chore(catalog): shorten two comment blocks * chore(catalog): trim first-party comment blocks to the comment-check limits * chore(catalog): shorten two frost source comments * fix(catalog): the Docs tab renders as prose again, and the detail page honours reduced motion Frame-mode pages carry no prose wrapper, and the detail component sat inside not-prose, so the Docs slot's headings, paragraphs, links and lists had no styling at all. The slots now carry their own prose classes and the chrome around them keeps not-prose. The recorded clip also gets the reduced-motion guard the docs-snippet check requires, and the generator reads stability from the source metadata type so the scripts typecheck passes. * fix(catalog): docs-tab paragraphs render as blocks Mintlify emits paragraphs as inline spans, so two paragraphs in the Docs slot ran together. * fix(ci): drop the duplicate docs_catalog job output git's line-based auto-merge silently duplicated the outputs key when main and this branch each added it on non-adjacent lines during the rebase; oxfmt's yaml parser rejects the repeated map key. * fix(catalog): close four CodeQL alerts in the frost source * chore(catalog): allowlist the avatar-promo-card CDN asset deletions, format sync-docs-catalog check-no-main-deletions.mjs was rejecting 11 local mp4/font files this branch already deleted in favor of CDN hosting; scripts/sync-docs-catalog.mjs had one line over the 100-col print width from an earlier edit in this branch. * fix(catalog): detail tabs use docs prose spacing, Install first; previews skip no-WebGPU runners * fix(catalog): the docs tab scrolls with the page instead of a capped inner box * refactor(catalog): split the payload asset and verify scripts into single-purpose helpers * refactor(catalog): split the preview generator into single-purpose helpers * ci(catalog): fail when the committed catalog differs from generator output The docs host serves the committed catalog and nothing regenerates it, so a stale copy shipped silently. The catalog payloads job now generates into a temp dir and compares it with the committed tree. * chore(catalog): regenerate docs/public/catalog with the refactored scripts One full regeneration from an empty output tree with the current scripts. The tree grows from 58.4 MB to 65.1 MB. Two hosted assets are about 2 MB each (an avatar png and a wav); the same bytes are already committed under assets/, so HF_MAX_NONLFS_KB is raised for this commit only. * fix(catalog): run the catalog script tests under the runner they are written for The rebase put three node:test files in the vitest half of test:scripts, where they report "No test suite found". They now sit in the node --test list. * fix(catalog): a WebGPU item without an adapter shows its recorded clip and says why * ci(catalog): the drift check runs from a committed fetch mirror with no network Generation now serves every font and CDN script fetch from scripts/catalog-fetch-mirror and fails when a fetch is not in it, even if the caller swallowed the error. CATALOG_FETCH_MIRROR=record refreshes the mirror from the live network. The docs_catalog filter also watches packages/core, packages/producer and the mirror, and the 13 re-hashed font files are listed in ALLOWED_DELETIONS. * chore(catalog): clean up the temp font cache and sort the mirror index without a locale * fix(catalog): hide Tune when the clip stands in, time out the adapter request, share one check * fix(catalog): the adapter probe survives a sync throw and a hang; test the clip guard * fix(catalog): keep the adapter probe inside the component, Mintlify evaluates only exports * fix(catalog): run the detail and page generator tests in CI; no Tune flash while probing WebGPU * docs(catalog): credit vendored libraries and make each SKILL.md list what the block installs CREDITS.md names every vendored dependency with its upstream and licence, and flags the two leaf textures whose origin is still to be confirmed. Each SKILL.md Files table now comes from the block manifest and its install and render paths point at compositions/<name>/. A test checks that every path a SKILL.md names is a manifest file that exists. * fix(catalog): a declared WebGPU item passes verify by its manifest tag, not by console wording verify-catalog-payloads forgave a missing adapter only by matching the markup and the message, which library-reported errors and widgets never satisfy. The webgpu tag in the item manifest is now the owner: a tagged item ignores its page and console errors, an untagged one gets no forgiveness, and a marker payload with no html is listed instead of crashing the run. * docs(catalog): correct the credits per block and guard the mount and render paths CREDITS.md states the three.js version and vendored-or-CDN status per block, adds d3-delaunay, Clipper on the CDN and the Google Fonts loads, and lists every texture with no known origin. The SKILL.md test now also checks the mount and render paths against the manifest composition. * test(catalog): read each block manifest once in the SKILL.md file test The two checks shared a copied manifest read, which fallow flagged as a clone with a high score. * fix(catalog): commit the hosted item files a gitignore rule kept out, and check git not the disk The compositions/ and *-ad/ ignore rules matched folder names under docs/public/catalog/items, so 33 hosted fonts, textures and marks were generated but never committed and would 404 on the docs host. The rules now have a negation for that tree and the files are added. The drift check read the checkout, where git-ignored files still exist, so it passed locally and failed only in CI. It now unpacks HEAD with git archive and compares that. * fix(catalog): a tile keeps its poster until the composition has painted * fix(catalog): a tagged WebGPU item forgives only the adapter-absent error, and test the wiring The manifest webgpu tag now decides whether an item may lack an adapter, and the error wording decides which lines are that absence. Any other page or console error on the item, and every failed request, still fails. The per-item decision is one exported function the tests drive, marker payloads are parsed by a tested helper, and the SKILL.md variable counts are checked against their tables (cuboid-carousel said forty-nine for forty). * fix(catalog): the pinned group's posters load eagerly at high priority * fix(catalog): type the tile reveal test so the scripts typecheck passes * fix(catalog): stop the adapter signature matching an unrelated failed adapter request failed to request .*adapter also matched errors about other adapters. It now matches only a request for the WebGPU adapter, so a tagged item cannot hide an unrelated failure behind it. * test(catalog): a tagged item still fails on an error about some other adapter * feat(catalog): the install command sits above the preview, as on the live catalog * fix(catalog): find the install slot through Mintlify's child wrapper * fix(docs): catalog copy buttons keep their size when the label swaps to Copied * refactor(docs): name the shown copy label once in the catalog copy button * fix(docs): primary catalog button keeps a readable label on hover and after copy * fix(docs): inset the catalog tab strip and tab content to match the boxed panels |
||
|
|
752b552789 |
feat(registry): blur an element by marking it, not by calling a function (#4045)
attachMotionBlur(el, tl) has an ordering contract: it has to run after every tween is defined and before the composition registers its timeline. Getting it wrong makes the blur silently do nothing, which is the worst shape a failure can take in a renderer nobody watches. An attribute has no order to get wrong. data-hf-motion-blur on any element is now the contract. Empty means defaults; anything else is a JSON object of the same options. The element finds its own timeline: its nearest [data-composition-id] ancestor names the key, and the snippet reads window.__timelines[key]. attachMotionBlur stays exported for a target built after the page has settled. claim(el) is the only thing that attaches, and two triggers call it. An accessor on window.__timelines wraps the registry in a Proxy whose set trap sweeps on every per-key registration, which is what attaches before the renderer captures frame zero. A bounded poll, 32 ms by 250 ticks, reads the registry directly and needs no cooperation from whoever wrote it. The poll is not belt and braces: a registry reference captured before the snippet installs bypasses the trap outright, and a composition that mounts its DOM after registering its timeline has no marked element to sweep at trap time. It runs its whole budget every time rather than stopping the first tick nothing is pending, because on an ordinary page the top-level target is claimed on the registration write, so an early exit kills the poll one tick in, before any late composition arrives. Every refusal is by name, once, and never silent: a value that is not JSON, a value that parses but is not an options object, an option name that is not one of the four, an option value that is not a finite number, a target inside another target, and a second timeline registered under one key. Parsing runs before the timeline lookup, so a malformed value is reported as malformed rather than as a composition that never registered. Two properties of the primitive this change made load-bearing: - Frame rate is resolved from the target's own composition root, not from the first root in the document. The document-wide lookup was survivable while every caller could pass fps explicitly; the declarative form has no per-call argument. Two compositions on one page at different rates now each get their own shutter window, and one call naming both says it cannot. - An attach is all-or-nothing. The mark, the copies and the resize observer go in together and come back out together, so a throw partway through leaves the element untouched rather than marked blurred with copies in the page and no tracker driving them. The sweep is also bounded per element, because the write trap runs it inside the author's own registration statement. Copies are stripped of data-hf-motion-blur, data-composition-id and data-fps, on the clone and every descendant: the first would make a copy a target of its own, and the other two would let a copy win the fps lookup, since copies are inserted before the original. skills/hyperframes-animation/references/motion-blur.md is the agent-facing half, mirroring how colour grading is surfaced. It leads with when NOT to blur, because the attribute makes blurring everything trivial and a smear on motion that was never fast enough reads as a soft, cheap render. Co-authored-by: Miguel Ángel <miguel.sierra@heygen.com> Co-authored-by: Vance Ingalls <vance@heygen.com> Co-authored-by: Claude Code <noreply@anthropic.com> |
||
|
|
f2976536de |
fix(skills): mount music-to-video motion primitives as sub-compositions (#4136)
* fix(skills): mount music-to-video motion primitives as sub-compositions Each primitive's root now only mounts scene.html, so the root is built from a sub-composition and the structure lint has nothing to flag. Renders match the originals (31 of 37 identical, the rest above 54 dB PSNR). Registry examples mark their caption layer with data-track-kind=captions. * fix(skills): keep primitive variables and page styles where the runtime reads them Composition variables move onto the sub-composition element, the dead #scene rule is dropped, and the frame-worker doc names where page styles live. * fix(skills): shorten a carried-over comment in the kinetic-letter-in scene |
||
|
|
a2a5af8e19 |
fix(registry): stop gsap tweens from clobbering baked-in css transforms (#4090)
* fix(registry): stop gsap tweens from clobbering baked-in css transforms transitions-cover, transitions-other, and us-map-bubble each set a static css transform (translate/scale) on an element, then animate x/y/scale on it with gsap. gsap owns the full transform matrix once it touches an element, so the initial css value is a static risk the runtime StaticGuard flags. establish the initial state via gsap.set() instead, matching the pattern already used elsewhere in the registry. us-map-bubble had a real visible defect: bubbles rendered stuck near scale-0 instead of animating in. the other two items render pixel- identical before and after since gsap already decomposes their pure single-axis transforms correctly on first touch; fixed anyway since the lint rule and StaticGuard are correct that it is a latent risk. * style(registry): run oxfmt on transitions-cover |
||
|
|
9bb4f5cd57 |
feat(catalog): browse the catalog as a live grid, not a static index (#4051)
* feat(catalog): browse the catalog as a live grid, not a static index
The Catalog overview page now mounts a browse grid (posters, tags,
per-category rows, expandable sections) generated over the existing
~380 registry items, instead of one static hero video plus two links.
docs/snippets/catalog-gallery.jsx and docs/catalog-gallery.css are
ported from a reference implementation of this same UX; the CSS keeps
its original comments as-is (NO_ESSAY_COMMENTS_OK: vendored file).
scripts/sync-docs-catalog.mjs and build-docs-gallery-nav.mjs are new,
generating the gallery's data and the Catalog tab's nav directly from
this repo's registry/*/registry-item.json and docs.json, so no sparse
clone or snapshot of another project is needed. scripts/check-docs-catalog.mjs
proves every catalog page from before this change still exists, on
disk and in the sidebar, with an identical page list.
No catalog page or snippet is removed; the previous overview content
(catalog-overview-player.jsx) is unused on this page but stays in the
repo.
* refactor(catalog): share the docs-catalog helpers across the sync scripts
Three independent adversarial reviews (reuse, quality, efficiency) all
converged on the same shape: sync-docs-catalog.mjs, build-docs-gallery-nav.mjs
and check-docs-catalog.mjs each hand-wrote the same docs-root resolution,
the same catalog-gallery-data.mdx parse, and the same Catalog-tab lookup.
Extracted into scripts/docs-catalog-shared.mjs.
sync-docs-catalog.mjs also ran build-docs-gallery-nav.mjs as a dynamic
import purely for its side effect, forcing a same-process re-read and
re-parse of the data file it had just written. build-docs-gallery-nav.mjs
now exports buildNav(docs), called directly with the path already
resolved; its CLI entry is guarded for standalone use.
slug() dropped the /^-+|-+$/ trim in favor of the character-scan trim
packages/core/src/tokenSlug.ts already uses for the same reason (CodeQL
flags that regex as polynomial ReDoS, js/polynomial-redos); not imported
directly since tokenSlug.ts is compiled TS with a different domain
(CSS variable naming) and no existing scripts/*.mjs imports packages/core
source at plain node runtime.
Fixed one bug found while re-verifying after the refactor: the recursive
nav walk wasn't idempotent against its own generated output — re-running
sync-docs-catalog.mjs a second time saw the "Catalog" wrapper buildNav()
adds and misattributed every flat, single-section group under it, 9
groups collapsing to 7. walk() now treats "Catalog" as transparent, the
same way it already treated "Overview". Verified: three consecutive runs
from a fresh origin/main checkout now produce identical 9 groups / 400
items each time, and check-docs-catalog.mjs still passes.
* chore(catalog): scrub an internal decision code from a code comment
* fix(catalog): stop losing a section's label when its nav is flattened
An independent adversarial review found real data loss: build-docs-gallery-nav.mjs
collapsed a group with exactly one section into a flat page list (a clean sidebar
for the common case), but sync-docs-catalog.mjs's walk() derives an item's section
from nav depth, so a second sync run over that flattened nav could no longer
recover the section name and silently fell back to the group's own label. Data &
charts / Data was the one real casualty in this catalog; Carousels and Blocks
happened to already have a section named the same as their group, so the loss
was invisible there.
Fix: only flatten when the section label equals the group label, so nothing
non-recoverable is thrown away. Verified with a deliberate mutation (reverted the
condition, reproduced the exact bug from a clean origin/main checkout, confirmed
the new check-docs-catalog.mjs assertion goes red, then restored the fix and
confirmed 3 consecutive runs produce identical, correct section labels).
check-docs-catalog.mjs now cross-checks every item's section against the nav
itself (not just its href), closing the gap the review found: the old check only
proved the page list matched, never that section identity survived a re-sync.
Also deletes docs/snippets/catalog-overview-player.jsx: unreferenced anywhere in
the repo now that catalog/index.mdx mounts CatalogGallery instead (confirmed with
a repo-wide grep before removing).
* chore(catalog): allowlist the deliberate catalog-overview-player.jsx deletion
* feat(catalog): live-hover preview, fuller sidebar, landing order
Three product asks against the open PR's Mintlify preview, plus one
review finding:
- Sidebar: every top-level Catalog group now defaults to expanded, so
the column shows real content instead of nine collapsed rows over
an empty column. Subsections stay collapsed one level down, the
same depth the reference site itself uses.
- Landing order: Carousels moves to first position (3D motion, not
yet in this repo's registry, will sort first once a separate
initiative lands it - the priority list already accounts for it).
Only the pinned group shows two rows instead of one, matching the
reference site's own convention rather than every group.
- Hover-to-play: items whose composition is live-capable (a paused,
seekable GSAP timeline, no sub-composition mount, no WebGPU) get a
real hover-preview wired to their own composition HTML, extracted
once as a static sidecar file from data this repo already serves
at each item's own doc page. Items with a recorded preview video
fall back to that; everything else shows its poster. Verified no
WebGL/three.js item is ever marked live (238 live, 30 video, 132
poster-only of 400).
- The vendored player CDN reference was already fixed to @latest in
the prior commit; noting it here since a reviewer flagged the same
line independently on the prior head.
docs/public/catalog/gallery-live/**/*.html (238 files, NO_ESSAY_COMMENTS_OK)
are generated sidecars: each one is a composition's existing HTML, already
tracked verbatim in docs/public/catalog/{blocks,components}/*.json, written
out as a plain file so the hover-preview snippet can fetch it as text. Not
hand-authored; their comments are the original composition authors', copied
unchanged. Regenerated by scripts/sync-docs-catalog.mjs on every run.
* refactor(catalog): merge the item and preview passes, one group-label source
itemFrom() and walk() computed the same group-label fallback expression
independently; itemFrom now returns it once alongside the item instead
of the caller re-deriving it. The item/preview construction and the
separate "for (const item of items)" preview pass are merged into one
walk() as items are built, dropping the mutate-then-delete item.live
scratch field. Byte-identical generated output, confirmed by diff.
* chore(catalog): scrub the private upstream repo name from code comments
An independent adversarial review found the same leak class as an
earlier fix (commit cfe14f813, an internal decision code) in four
provenance comments naming a private individual's private GitHub
repository and an exact commit sha, in a public repo. Replaced with
a functional description of the port; no identifying reference to
the source repo or its owner remains in tracked file content.
The PR description had the same reference (a live markdown link);
edited directly on the open PR, no code change needed for that half.
* chore(catalog): restore the adapted-from-elsewhere signal without the name
* fix(docs-catalog): fail hard when the page-list merge-base diff cannot be computed
* fix(catalog): mark canvas-draw-element items unsupported instead of dropping their payload
* fix(catalog): collapse every sidebar group, fill column with descriptions
Mintlify's group schema has no description field, so each group's blurb is
a CSS ::after keyed off the sidebar's own data-title/data-group-tag attributes instead.
NO_ESSAY_COMMENTS_OK: false positive.
The real /* */ block is 3 lines. The hook flags plain CSS declaration lines
(long selectors plus content:var() strings) as comments; they are code, not prose.
* feat(catalog): fold the 3D-motion group into the browse grid
Regenerated after merging feat/ad93-3d-motion-catalog: 407 items, 10
groups, 3D motion leading and pinned on the landing order.
* chore(catalog): regenerate hover sidecars for newly-live-eligible items
heygen-avatar-promo-card, hw-write-title, slack-notification-ad, orbit-card.
NO_ESSAY_COMMENTS_OK: generated composition HTML, not hand-authored.
* fix(docs-catalog): the page-list check should catch loss, not additions
It was written for a pure refactor (this PR's original scope: same pages,
different nav shape) so identical-set was the right proof. Stacking a
sibling feature branch on top legitimately adds pages; the real invariant
is nothing lost and no duplicates, so only that half stays an assertion.
* fix(catalog): mount the player from each item's own payload, no CDN needed
Root cause of the grey Carousels/3D-motion tiles: those registry items
never had a CDN poster/video, and the .html hover sidecar 404s on the
deployed docs host (Mintlify drops .html/.js/.css from docs/public with
no error). Cards now fetch the same repo-served .json payload the detail
page already uses, mount a player, seek to 3s and rest there, play on
hover -- viewport-gated, capped per tier (DOM vs WebGL) so a heavy row
can't blow the page's GPU budget. Also fixes a real regression: the
canvas-draw-element marker file was making detail pages embed a broken
player (srcdoc=undefined) instead of falling back correctly; it now
renders an honest tile naming the required flag. Deletes the now-dead
.html sidecar mechanism and its vendored gating helper.
* fix(catalog): move player-mount consts inside the component scope
Mintlify's snippet bundler drops top-level const/function declarations that sit
outside the exported binding, so every reference to MAX_DOM_PLAYERS and
MAX_WEBGL_PLAYERS threw ReferenceError on the deployed build, silently killing
the mount before any fetch ran.
Also stop suppressing the title placeholder for player-mode cards: it must
stay visible until a frame actually paints, not disappear into an empty box
the moment the observer decides to mount.
* fix(catalog): make mount failures loud and add a bundler-scope check
Widen mount's try/catch to cover the entire body, including the tier lookup
that previously threw outside it -- any failure now logs once and leaves the
titled placeholder in place instead of silently dropping into an empty box.
Add a check-docs-catalog.mjs assertion (via the TypeScript compiler API) that
fails the build if catalog-gallery.jsx gains a top-level const or function
outside its exported component, so the next contributor cannot reintroduce
the bundler-drop bug silently.
* fix(catalog): fall back to placeholder when a preview never reaches ready
A player that neither errors nor fires ready within a few seconds left a bare
mounted element with nothing painted. Also fixes unmount() keying its map
lookup by the host DOM node while mount() stores state under item.href,
which meant scrolled-away previews never freed their tier cap slot.
* docs(catalog): trim an essay-length comment in docs-catalog-shared.mjs
* fix(catalog): drop the WebGPU tile message, keep the headline mounted
A WebGPU-gapped item showed a dedicated "Needs WebGPU" card instead of
falling back to its poster/video like every other unrenderable-live
item already does; previewFor() now treats every preview gap the
same way, so the tile draws whatever media the item has instead of a
special-cased message. Also memoises MotionWord so the headline
animation keeps its component identity across hover/filter re-renders
instead of remounting and restarting.
* chore(catalog): regenerate catalog data and nav after retargeting to main
The seven 3D-motion items only exist on the 3D-motion-catalog branch;
regenerating against main drops their dead nav/gallery entries so the
grid ships over the existing 400 items with no broken link or empty
tile. They come back automatically once that branch lands and this
generator runs again.
* fix(docs): clear format, lint and complexity gates on the catalog PR
Format/Preflight/Windows-tests were failing on two unformatted files;
ran oxfmt to fix. Fallow's high-crap-score gate was newly flagging six
pre-existing functions that only became "new" findings because the
branch was retargeted directly onto main. Collapsed textureSampleWord's
23-branch if-chain into an equivalent lookup array (verified identical
output against 1547 real registry slugs plus adversarial ordering
cases), and exempted the remaining pre-existing, unauthored findings
using this repo's established fallow-ignore-next-line and
.fallowrc.jsonc health.ignore conventions.
* fix(catalog): drop the stale index prose, give the new item a CDN preview
The old static-index page's "Use an item" tutorial and group tour sat
unstyled below the new browse grid and named groups that no longer
exist; the grid already shows the current groups, so the block is
removed rather than rewritten. Kept the required Related topics
section.
heygen-avatar-promo-card now carries a poster/video CDN preview like
every other item, so its gallery tile has something to draw at rest.
* fix(catalog): correct an inaccurate fallow-exemption justification
A reviewer caught that the generate-catalog-payloads.ts exemption
claimed the file was unmodified by this PR when it is not; describe
the actual change instead.
* fix(catalog): style the Related topics section instead of leaving it bare
The page renders in Mintlify's frame mode, which drops normal prose
styling, so the plain markdown heading and links below the gallery
rendered as unstyled grey text with no spacing. Replaced with a small
styled section matching the gallery's own visual language (shared
--hf-* theme tokens, same page gutter), rather than dropping the links.
* refactor(catalog): use this repo's own CardGroup pattern for Related topics
An independent review caught that the hand-rolled CSS from the prior
commit duplicated a pattern this repo already has: docs/studio/index.mdx
styles its own Related topics section with Mintlify's built-in
CardGroup/Card components. Switched to that instead and dropped the
custom CSS. Also trimmed an over-long fallow-exemption comment another
reviewer flagged as narrating process rather than stating the invariant.
* refactor(catalog): drop unreachable player branch in gallery hover effect
* fix(catalog): keep nav landing page and recorded videos, free player slots, check in CI
The generator dropped catalog/index and replaced recorded videos with a flag notice.
A card scrolled away before ready released its player slot twice.
check-docs-catalog now runs in CI.
* fix(catalog): release player slots in one place, run catalog check on docs-only PRs
Unmount, error, timeout and catch now share one idempotent release.
The Lint job also runs when only docs or registry files change.
* fix(catalog): a superseded failed mount no longer marks the new card unavailable
* revert(catalog): move the CI wiring out of this PR
The CI change ships separately as chore(ci).
* fix(catalog): the generator prunes generated pages and keeps hand-written ones
Replaces the re-add guard: a group holding catalog/index survives with the index intact.
|
||
|
|
d6a0c407d7 | fix(registry): scope the 12 vscode-theme blocks' root id and fix their asset path (#4094) | ||
|
|
2279333990 |
fix(registry): register 4 async-built timelines only after they are built (#4086)
code-3d-extrude, code-particle-assemble, code-shader-dissolve and code-snippet-flight each registered window.__timelines[id] with an empty, paused timeline before document.fonts.ready populated it. The sub-composition readiness gate treats key presence as ready and nests the child once, so mounting any of these as a sub-composition rendered blank. Moved registration to the end of the fonts.ready callback and added the hfForceTimelineRebind re-nest call, matching the pattern already used by code-highlight, code-typing, code-morph and code-scroll. |
||
|
|
81ca5f4187 |
fix(registry): vfx-iphone-device stops gating its screens on rAF (#4083)
* fix(registry): vfx-iphone-device stops gating its screens on rAF The device screens only started rendering after two requestAnimationFrame ticks, which run on wall-clock time and are not guaranteed to fire in every render environment, risking a render that never registers its timeline. Swapped in the same canvas.requestPaint()/setTimeout paint-wait helper the liquid-glass items already use for the same one-time screen-capture handoff. * fix(registry): wait for both capture canvases on vfx-iphone-device The raf-desync fix only waited on the laptop canvas before capture, leaving the phone canvas with no explicit paint confirmation. |
||
|
|
637ce57994 |
fix(registry): svg dash animations measure the path after its d is set (#4082)
getTotalLength() was called on a path whose d attribute was set through a fresh getElementById(...).setAttribute(...) chain rather than the variable being measured. this is behavior-neutral at runtime (same DOM node either way, measured synchronously in the same task), but the lint's static rule can only verify a d assignment happened when it traces the same variable. hw-path-text and hw-text-cloud now assign d through the same variable they measure, matching how every other registry item with a drawn path already does it. |
||
|
|
2679ebdd0d |
fix(registry): corner brackets fade in on cue in north-korea-locked-down (#4081)
The corner brackets sat at CSS opacity:0 and the intro gsap.from() also targeted opacity:0, so it animated 0 to 0 (a no-op) instead of revealing them. They only appeared later via an unrelated tween, losing the intended synchronized pop-in with the scale animation. |
||
|
|
82f49262ec |
feat(registry): blur every transform driver, and ship the reference case as shutter-slam (#4038)
The motion-blur snippet integrated translation only. Its output stage was one SVG filter per target whose copies were feOffset, and feOffset only translates, so a beat that only scaled or only turned sampled a displacement of zero and rendered sharp. No SVG filter primitive can give a copy its own affine transform, so the sampling loop was never the problem: the output stage was. Copies are now DOM clones of the element in an isolation:isolate group, each at 1/N opacity with mix-blend-mode plus-lighter, each carrying the element's resolved transform at its sample time. plus-lighter adds premultiplied colour, so N + 1 copies at 1/N sum to the average the shutter integral asks for; the isolation is load-bearing rather than tidy, because without a transparent backdrop of its own the first copy adds onto the page and blows a light background out to white. Reading the resolved matrix instead of a list of named GSAP properties is what makes every driver work at once: translation, scale, rotation, 3D rotation and skew arrive in the same one value. Measured against the After Effects export the shutter model was fitted to, per beat, previous stage to this one: scale +0.62 dB, rotate X +0.43, rotate Y +0.84, rotate Z +1.15, translate X -0.18, translate Y -0.04. The two small translate regressions are inherent and not tunable: the filter offset one rasterisation so every copy was pixel-identical, where each copy is now rasterised independently at its own fractional transform with its own subpixel antialiasing. Three consequences of the new stage, each carrying its own fix: - mix-blend-mode flattens preserve-3d, so a copy cannot inherit the parent's 3D context. Each copy carries the parent's perspective as its own first transform function instead. - A copy is styled by nothing that selected the original, since dropping the id also drops every #id rule that gave it size, colour and font. Each copy carries its own resolved style inline, read once and replayed. Those values are px, so a container-relative element needs them again when its box changes: a ResizeObserver re-snapshots rather than paying a subtree walk every frame. - A beat that moved and faded at once kept a full-strength smear behind a vanishing element. The element's opacity now rides the group; the 1/N weight stays on the copies. The axis option is gone. It selected which of x and y to sample, a question the resolved matrix does not have. shutter-slam is the new component: one word through six single-property beats, two translate slams, a scale punch, then a full turn about each axis, which exercises every driver the model covers in one mount. Easing is power2.inOut because it measured best, not because it was the default. A bezier fitted to the reference's own bbox centres looks like the better curve and renders worse: a blurred bbox centre is the shutter window's average rather than the instant, so on an accelerating segment the fit measures its own blur. Co-authored-by: Miguel Ángel <miguel.sierra@heygen.com> |
||
|
|
9aa60a844f |
fix(registry): make motion-blur match After Effects shutter-based blur (#4008)
* fix(registry): make motion-blur match After Effects shutter-based blur The motion-blur catalog snippet estimated speed from the x/y delta between consecutive timeline updates and drew three one-sided ghost copies with decaying alpha plus a Gaussian on top. After Effects does temporal supersampling: each frame integrates the layer over a shutter window (shutter angle, shutter phase, samples per frame), producing a symmetric box smear of length speed x shutter time. The snippet now does the same inside the page. After every timeline update a deferred pass seeks the GSAP timeline to N sub-frame times across the shutter window (events suppressed), reads each target's x/y, restores the frame time, and drives an SVG filter that averages N feOffset copies with equal weight (feComposite arithmetic, 1/N, sRGB). Defaults follow AE: shutterAngle 180, shutterPhase -90, samplesPerFrame 16; fps comes from the render config, then the root data-fps, then 30. Displacements are mapped through the inverse of the element's own scale/rotation; percent x/y values are resolved to px; SVG targets use their bbox; an existing inline filter is kept and the blur appended to it. The pass runs in a microtask rather than inside the tracker tween's onUpdate, because seeking GSAP from within its own render reads stale tween state and suppresses the composition's callbacks on forward seeks. The heuristic options blurScale/blurMax/stretchScale/stretchMax are removed. Reference: an AE-algorithm reconstruction of the same composition rendered without the component at an effective 960 fps and averaged over the 16 sub-frames of the 180 degree window per output frame. Against it (1920x1080, 30 fps, solid + text translating linear then power2.inOut): old mean PSNR 53.2 dB (min 26.7), SSIM 0.9930; new mean PSNR 72.4 dB (min 52.6), SSIM 0.9998. The 10-90 edge ramp at 66.7 px/frame is 26.8 px in the reference, 27.5 px new, 14.5/6.3 px (one-sided) old; at the ease peak 79.3 vs 79.5 vs 7.9/17.7 px, with smear centres matching to 0.1 px. Co-Authored-By: Miguel Ángel <miguel.sierra@heygen.com> * fix(registry): drop render-config fps probe from motion-blur and refresh its docs The snippet resolved fps by reconstructing the renderer's private seek config key so the compiler's inline-script guard would not strip it. A public catalog snippet must not depend on that key, so fps is now the explicit option, then the root data-fps, then 30; renders with an fps override pass fps explicitly. The catalog page's Usage, How it works and Options sections still described the old velocity heuristic and are rewritten for the shutter model. The 1/N weight keeps full precision. Co-Authored-By: Miguel Ángel <miguel.sierra@heygen.com> * fix(registry): match the After Effects reference export's shutter window The shutter model landed on this branch was built against a reconstruction of After Effects' algorithm, not against After Effects. A real 1920x1080 / 30 fps export of translating text disagrees with it on three points, all measurable off the export itself. The window is two frames wide, not half a frame. On the measured frame the outermost duplicate on the trailing side sits 128 px back while the previous frame is 125.8 px back, and the outermost leading duplicate sits 189 px ahead while the next frame is 184.9 px ahead: the window ends land on the neighbouring frames. Defaults become shutterAngle 720, shutterPhase -360. Duplicates sit on the sub-interval boundaries, both ends included, not at sub-interval centres. The staircase across a stroke steps every 15.6 px on a 125.8 px displacement and every 23.0 px on a 184.9 px one, i.e. an eighth of a frame, with the run closing exactly on the neighbouring frame's position. samplesPerFrame now means sub-intervals of the window, so there are N+1 copies at k/N rather than N copies at (k+0.5)/N. The frame-time instance is drawn sharp over the smear. At full ink the core bounding box is the whole word at every frame and total ink rises to 1.44x the unblurred frame's, neither of which an average can produce. A final feComposite over of SourceGraphic reproduces it. Each duplicate stays at 1/N: the staircase plateaus step by 0.063 +- 0.002 of the sharp text intensity, flat, with no taper toward the window edges, and triangle weighting scores 1.6 dB worse against the export. Verified by replaying the export's translating section as a composition whose sprite is the export's own rest-frame ink and whose eight legs follow its own measured per-frame positions, rendered at 1920x1080 / 30 fps and compared frame by frame over the text band. Mean PSNR against the export rises from 24.19 dB (min 17.07) to 32.51 dB (min 27.57) across 72 frames, and from 19.83 dB (min 17.07) to 32.03 dB (min 27.57) across the 26 frames that actually move. Ghost extents match to a few px and the rendered staircase plateaus match the export's to two decimal places. The export's later scale, skew, flip and 3D rotation sections are out of reach here: integrating anything but translation inside the shutter is engine work tracked in issue #4010. The filter region now clamps its bounds through zero. It was derived from the duplicate offsets alone, which was correct while the output was nothing but duplicates; the sharp instance draws at offset zero, so a window sitting entirely on one side of the frame time - any shutterPhase above 0 or below -shutterAngle - produced a region starting inside the element's own box and cut the instance off. The defaults straddle the frame time and were never affected, so the measured numbers above are unchanged. Co-Authored-By: Miguel Ángel <miguel.sierra@heygen.com> --------- Co-authored-by: Miguel Ángel <miguel.sierra@heygen.com> |
||
|
|
ff749b8705 |
fix(security): verify CDN script integrity before inlining (#3822)
* fix(security): verify CDN script integrity before inlining * fix(producer): normalize SRI algorithm case * fix(producer): abort compilation on integrity mismatch * fix(compiler): preserve nested script integrity requirements * fix(compiler): defer local inlining until integrity is known |
||
|
|
c6010f2eb8 | fix(registry): authenticate deck sound effect messages (#3819) | ||
|
|
3a7fcd10e0 |
feat(registry): add 25 image carousel blocks (#3790)
* feat(registry): add 25 image carousel blocks (5 families × 5 variants) Five carousel families, each with 5 style variants: - Orbit (1–5): image cards on a spinning 3D Fibonacci sphere - Path (1–5): cards following animated CSS motion paths - Circle (1–5): circular carousel layouts - Vision (1–5): Apple Vision-style spatial presentations - Text Circle (1–5): circular carousels with text overlays All blocks are 1920×1080 at 6s, with 12–24 configurable image slots. Includes catalog preview thumbnails for each block. Co-Authored-By: Jake Moran <jake.moran@heygen.com> * refactor(registry): host carousel block images on the CDN The registry is served straight out of this repository (DEFAULT_REGISTRY_URL points at raw.githubusercontent.com), so every byte a block ships is permanent history. The 25 carousel blocks added 421 JPEGs, 27.7 MiB in a checkout, and made the diff 472 files. Only 23 of those images were distinct: the same 12-24 placeholders were copied into every block. files[] entries gain an optional `url`. When set, the installer fetches the bytes from there instead of joining the registry base. `path` does not change and still says where the file lands relative to the item, so composition HTML, target mirroring and `hyperframes add` behave exactly as before. Keys are content-addressed, so the 396 manifest entries resolve to 23 objects, and a changed image gets a new URL rather than a stale one cached behind `immutable, max-age=31536000`. The catalog preview renderer copies an item's directory and renders it, so it needs the same materialisation step. Without it the preview draws every card blank and reports success, which is worse than failing. Also drops registry/catalog/, 25 hand-made thumbnails referenced by nothing; catalog previews are rendered by CI and served from docs/images/catalog. Verified: all 23 objects return 200 from the CDN with hashes matching their keys; `hyperframes add carousel-orbit-1` against a local registry installs 24 real JPEGs; the preview render produces the album art, and produces blank cards when the fetch step is removed. * style(registry): format the carousel composition HTML `oxfmt --check .` covers the whole tree, and these 25 files were never run through it. The pre-commit hook only formats staged files, so nothing local caught it. * feat(catalog): publish the carousel blocks without republishing their images These 25 blocks had no Catalog page. Every other item in the registry has one, so they shipped invisible: installable by name, unfindable by browsing. Generating them naively undid the change they were added by. The Catalog payload copies an item's assets into docs/public/, which is tracked, so the 396 images this PR just removed came back as 43 MB one directory over — worse than the 3 MB they started as, because each block got its own copy. The copy exists because these compositions assemble `img.src` at run time out of a variable value, so there is no `src="..."` in the markup for the payload's asset scan to resolve. An unpredictable path can only be satisfied by serving every file beside it, which is what `needsOwnDirectory` asks for. An absolute URL needs no directory: the scan already skips any `https:` reference. So for the payload path only, hosted files are left undownloaded and the composition's variable defaults are rewritten to their URLs. The preview renderer still downloads them, because it paints real frames and a missing file is a blank card. The explorer posts every value to the preview frame on mount, including untouched ones, so the page's variable list carries the URLs too. Left as local paths they would have overridden the payload's own defaults and asked the frame for a file that was deliberately never published. Result: 25 pages, 25 payloads, zero bytes of image added. Verified: a spike item declaring no assets at all rendered its 24 covers from the CDN, proving the variable-default path; payload generation for a carousel block now writes no item directory and no shared asset; the preview render still produces the album art. mint validate and mint broken-links pass on the new pages. test:scripts is green. * refactor(catalog): split the hosted-asset step out of prepareProjectDir Two functions rather than one: finding the composition and rewriting its variable defaults are separate jobs, and inlining the mode branch pushed prepareProjectDir past the complexity gate it was already sitting on. Behaviour is unchanged. Re-verified both paths after the split: the payload for a carousel block still writes no item directory and no shared asset, and the preview render still produces the album art. * feat(catalog): give the carousels their own shelf 25 image carousels landed in Showcases and were 53% of it, so the scenes that shelf exists for disappeared underneath them. That is the same shape the 24 editor themes made, and it gets the same fix they got. Keyed on the first tag, which is this file's stated grouping rule, rather than on the name. `screen-flow-carousel` leads with `product-demo` and stays on the shelf that says what it is for; a future carousel that is not named `carousel-*` still lands here. Showcases 47 -> 22, Carousels 25, and no existing item changed shelf. * fix(registry): centre the circle-5 carousel path in its composition Its ring was centred at x=3832.6 in a 3840-wide composition, so it sat on the right edge and most of it fell outside the frame. Only a few cards were ever visible, cropped, with two thirds of the composition empty. The exported path carried absolute coordinates from a layout that was never recentred. Shifting the four vertices by (-1912.597, -4.340) puts the ring on the composition centre. Handles are relative, so only the anchor points move and the shape is unchanged. carousel-text-circle-5 shares the identical path and had the identical fault. The other three circle variants sit within 12% of centre, which reads as authored placement rather than the same bug, so they are left alone. * fix(catalog): rebuild the circle-5 payloads after recentring the path The Catalog preview plays the payload, not the composition on disk, so recentring the source changed nothing a reader sees. The payload still carried the old vertices and the ring still hung off the right edge of the frame. Verified the consumer this time, not just the producer: both payloads now resolve to a path centre of x=1920. The other 23 rebuild byte-identical, so the formatting pass did not reach them. * feat(catalog): promote Carousels to its own section It was a shelf inside Scenes & demos, which is where a scene type belongs by kind but not by weight. At 25 items it is larger than Data & charts (17) and Blocks (13), each of which is already a section holding a single shelf, so the catalog's own precedent puts it one level up. Pulling it out also takes the largest section in the catalog from 120 items to 95, which is the reason the shelf was added in the first place. The two circle-5 pages change because their embedded source block carries the recentred path; nothing else in them moved. --------- Co-authored-by: Jake Moran <jake.moran@heygen.com> Co-authored-by: Miguel Angel Simon Sierra <miguel.sierra@heygen.com> |
||
|
|
f498be94c2 |
feat(registry): add kinetic center build component (#3269)
* feat(registry): add kinetic center build component * chore: regenerate search vectors for kinetic-center-build Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(registry): load all kinetic center build font weights * chore(catalog): regenerate local search vectors --------- Co-authored-by: miga-heygen <miguel.sierra_miga@heygen.com> Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> Co-authored-by: Miguel Ángel <miguel.sierra@heygen.com> |