diff --git a/docs/catalog/components/motion-blur.mdx b/docs/catalog/components/motion-blur.mdx index f0e5a33184..911f6d61aa 100644 --- a/docs/catalog/components/motion-blur.mdx +++ b/docs/catalog/components/motion-blur.mdx @@ -14,7 +14,7 @@ import { InstallCommand } from "/snippets/install-command.jsx"; title="Motion Blur" description="After Effects-style motion blur — integrates each element over a shutter window (shutter angle, phase, samples per frame) by sampling the GSAP timeline at sub-frame times and averaging equal-weight additive copies of the element along its real trajectory, so translation, scale, rotation, 3D rotation and skew all drive the smear" variables={[]} - meta={{"category":"Effects","badge":"Stable","codeLines":406}} + meta={{"category":"Effects","badge":"Stable","codeLines":722}} attribution={{"path":"registry/components/motion-blur","tags":["effect","motion-blur","shutter","after-effects","velocity","animation"]}} hasCode rawUrl="https://raw.githubusercontent.com/heygen-com/hyperframes/main/registry/components/motion-blur/motion-blur.html" diff --git a/docs/catalog/components/shutter-slam.mdx b/docs/catalog/components/shutter-slam.mdx index 928166cc1c..78be2c98ae 100644 --- a/docs/catalog/components/shutter-slam.mdx +++ b/docs/catalog/components/shutter-slam.mdx @@ -14,7 +14,7 @@ import { InstallCommand } from "/snippets/install-command.jsx"; title="Shutter Slam" description="One word thrown through six single-property beats under integrated shutter blur — two translate slams, a scale punch, then a full turn about each axis — on the measured easing of the After Effects export the shutter model was fitted to" variables={[{"id":"text","type":"string","role":"content","label":"Word","description":"The word driven through the six beats.","default":"MOTION"},{"id":"accent","type":"enum","role":"style","label":"Accent","description":"Word color.","default":"green","options":[{"value":"green","label":"Green"},{"value":"blue","label":"Blue"},{"value":"violet","label":"Violet"}]},{"id":"blur","type":"enum","role":"motion","label":"Blur","description":"Integrate each frame over the shutter window, or capture one instant per frame.","default":"shutter","options":[{"value":"shutter","label":"Shutter"},{"value":"none","label":"None"}]},{"id":"cues","type":"string","role":"motion","label":"Cues","description":"Comma seconds from mount start for each of the six beats; empty keeps the authored rhythm.","default":""},{"id":"exit","type":"enum","role":"motion","label":"Exit","description":"How the word leaves.","default":"none","options":[{"value":"none","label":"None"},{"value":"fade","label":"Fade"},{"value":"up","label":"Up"}]}]} - meta={{"category":"Effects","badge":"Stable","codeLines":541}} + meta={{"category":"Effects","badge":"Stable","codeLines":830}} attribution={{"path":"registry/components/shutter-slam","tags":["effect","motion-blur","shutter","after-effects","slam","animation"]}} hasCode rawUrl="https://raw.githubusercontent.com/heygen-com/hyperframes/main/registry/components/shutter-slam/shutter-slam.html" diff --git a/docs/public/catalog-index.json b/docs/public/catalog-index.json index 651d4c5584..96ac29b00e 100644 --- a/docs/public/catalog-index.json +++ b/docs/public/catalog-index.json @@ -3682,7 +3682,7 @@ "sphere" ], "href": "/catalog/blocks/orbit-card", - "preview": "https://static.heygen.ai/hyperframes-oss/docs/images/catalog/blocks/orbit-card.png" + "preview": "https://static.heygen.ai/hyperframes-oss/docs/images/catalog/blocks/orbit-card-v2.png" }, { "name": "ordered-dither-pass", diff --git a/docs/snippets/catalog-detail.jsx b/docs/snippets/catalog-detail.jsx index 5f42c75ec3..574c367722 100644 --- a/docs/snippets/catalog-detail.jsx +++ b/docs/snippets/catalog-detail.jsx @@ -62,6 +62,64 @@ export const CatalogSlot = ({ slot, children }) => ( ); +/** The Tune panel's variable list, with a pulsing cue while more rows sit below the fold. */ +export const TuneList = ({ variables, values, notes, onValues, onNotes, onTyping, control, readout }) => { + // BEGIN hasMoreBelow: true once unseen content sits past the scrolled viewport (4px epsilon). + const hasMoreBelow = (scrollHeight, scrollTop, clientHeight) => scrollHeight - scrollTop - clientHeight > 4; + // END hasMoreBelow + const listRef = useRef(null); + const [moreBelow, setMoreBelow] = useState(false); + const check = () => { + const el = listRef.current; + if (el) setMoreBelow(hasMoreBelow(el.scrollHeight, el.scrollTop, el.clientHeight)); + }; + useEffect(() => { + const el = listRef.current; + if (!el) return; + check(); + const observer = new ResizeObserver(check); + observer.observe(el); + for (const child of el.children) observer.observe(child); + return () => observer.disconnect(); + }, []); + return ( +
+
+ {variables.map((v) => ( +
+
+ + {readout(v, values[v.id])} +
+ {control( + v, + values[v.id], + (next) => onValues((prev) => ({ ...prev, [v.id]: next })), + notes[v.id], + (note) => onNotes((prev) => ({ ...prev, [v.id]: note })), + onTyping, + )} + {v.description &&

{v.description}

} +
+ ))} +
+ {moreBelow && ( + + )} +
+ ); +}; + export const CatalogDetail = ({ previewSrc, compositionId, @@ -668,6 +726,7 @@ export const CatalogDetail = ({ font-weight: 600; } .hf-ve-tune-head small { font-weight: 400; font-size: 13px; color: var(--ve-muted); } +.hf-ve-tune-list-wrap { position: relative; flex: 1; min-height: 0; display: flex; } .hf-ve-tune-list { flex: 1; min-height: 0; @@ -679,6 +738,29 @@ export const CatalogDetail = ({ align-content: start; mask-image: linear-gradient(to bottom, transparent 0, #000 12px, #000 calc(100% - 12px), transparent 100%); } +.hf-ve-tune-more { + position: absolute; + left: 50%; + bottom: 6px; + transform: translateX(-50%); + display: flex; + align-items: center; + justify-content: center; + width: 24px; + height: 24px; + border-radius: 9999px; + background: var(--ve-hover); + color: var(--ve-muted); + pointer-events: none; + animation: hf-ve-tune-more-pulse 1.6s ease-in-out infinite; +} +@keyframes hf-ve-tune-more-pulse { + 0%, 100% { opacity: 1; transform: translateX(-50%) scale(1); } + 50% { opacity: 0.55; transform: translateX(-50%) scale(1.15); } +} +@media (prefers-reduced-motion: reduce) { + .hf-ve-tune-more { animation: none; } +} .hf-ve-tune-foot { display: grid; grid-template-columns: repeat(auto-fit, minmax(120px, 1fr)); @@ -2141,25 +2223,16 @@ export const CatalogDetail = ({
Tune {variables.length} {variables.length === 1 ? "variable" : "variables"}
-
- {variables.map((v) => ( -
-
- - {readout(v, values[v.id])} -
- {control( - v, - values[v.id], - (next) => setValues((prev) => ({ ...prev, [v.id]: next })), - notes[v.id], - (note) => setNotes((prev) => ({ ...prev, [v.id]: note })), - setTyping, - )} - {v.description &&

{v.description}

} -
- ))} -
+