feat(registry): add morph-text component and text-effects catalog section (#1247)

* feat(gsap): add innerText support to GSAP inspector for counter animations (#1244)

Adds 'innerText' as a supported GSAP property so number roll-up animations
(count-up from 0 to some value) are visible and editable in the GSAP inspector
panel.

- Add 'innerText' to SUPPORTED_PROPS in gsapConstants.ts
- Add label 'Counter Value', tooltip, and step constraint (1) in
  gsapAnimationConstants.ts

The snap modifier that controls integer rounding is already preserved
verbatim via the EXTRAS_KEYS round-trip, so rounding behavior survives
edits without any additional UI changes.

Closes #1179

* feat(registry): add text-effects catalog section and morph-text component

Introduces a new "Text Effects" catalog section (below Effects) for text-focused visual components.

- Add `text-effects` BlockCategory to core registry types with violet color
- Add `text-effect` tag resolver in resolveBlockCategory (checked before generic `effect` tag)
- Tag caption-blend-difference, texture-mask-text, and morph-text with `text-effect`
- Update studio catalog order and color map to include text-effects
- Add morph-text component: gooey SVG threshold morph cycling through editable statements
  using GSAP seekable proxy pattern for deterministic/seekable rendering

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* chore(registry): add morph-text preview video

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* chore(registry): fix morph-text.html formatting

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* docs(catalog): add Text Effects section and morph-text page

Moves caption-blend-difference and texture-mask-text out of Effects into a new
"Text Effects" section below it. Adds morph-text component page with install
instructions and preview video.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* chore(registry): add demo.html for morph-text catalog preview rendering

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix(registry): address PR review feedback on morph-text and text-effects

- Restore `effect` tag on caption-blend-difference and texture-mask-text
  alongside `text-effect` so existing tag-equality searches/analytics still match
- Fix morphPause script fallback from "0.25" to "1.5" to match data attribute default
- Add Math.max(0, ...) guard to blur values (intent clarity)
- Add prefers-reduced-motion: skip morph and show first word statically
- Remove CATEGORY_ORDER record from useBlockCatalog; derive order from
  BLOCK_CATEGORIES array (single source of truth, no drift)
- Add comment to demo.html documenting its purpose (catalog preview script only)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Miguel Ángel <miguel.sierra@heygen.com>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Vance Ingalls
2026-06-07 10:55:13 -07:00
committed by GitHub
co-authored by Claude Sonnet 4.6 Miguel Ángel
parent a890d093e1
commit 48fcf4ada5
11 changed files with 558 additions and 17 deletions
+4 -1
View File
@@ -181,7 +181,8 @@ export type BlockCategory =
| "data"
| "scenes"
| "captions"
| "effects";
| "effects"
| "text-effects";
export interface BlockCategoryMeta {
id: BlockCategory;
@@ -194,6 +195,7 @@ export const BLOCK_CATEGORIES: BlockCategoryMeta[] = [
{ id: "vfx", label: "VFX", color: "purple" },
{ id: "transitions", label: "Transitions", color: "blue" },
{ id: "effects", label: "Effects", color: "rose" },
{ id: "text-effects", label: "Text Effects", color: "violet" },
{ id: "social", label: "Social", color: "pink" },
{ id: "data", label: "Data", color: "green" },
{ id: "scenes", label: "Scenes", color: "amber" },
@@ -207,6 +209,7 @@ export function resolveBlockCategory(tags: string[] | undefined): BlockCategory
if (set.has("social") || set.has("overlay")) return "social";
if (set.has("data") || set.has("chart") || set.has("map")) return "data";
if (set.has("html-in-canvas") || set.has("webgl") || set.has("shader")) return "vfx";
if (set.has("text-effect")) return "text-effects";
if (set.has("effect") || set.has("grain") || set.has("vignette")) return "effects";
return "scenes";
}
+10 -12
View File
@@ -1,6 +1,10 @@
import { useState, useEffect, useMemo } from "react";
import type { RegistryItem } from "@hyperframes/core/registry";
import { type BlockCategory, resolveBlockCategory } from "../utils/blockCategories";
import {
BLOCK_CATEGORIES,
type BlockCategory,
resolveBlockCategory,
} from "../utils/blockCategories";
export type CatalogItem = RegistryItem & {
category: BlockCategory;
@@ -15,16 +19,6 @@ export function useBlockCatalog() {
// fallow-ignore-next-line complexity
useEffect(() => {
const CATEGORY_ORDER: Record<BlockCategory, number> = {
captions: 0,
vfx: 1,
transitions: 2,
effects: 3,
social: 4,
data: 5,
scenes: 6,
};
let cancelled = false;
(async () => {
try {
@@ -34,7 +28,11 @@ export function useBlockCatalog() {
if (cancelled) return;
const items = data
.map((b) => ({ ...b, category: resolveBlockCategory(b.tags) }))
.sort((a, b) => (CATEGORY_ORDER[a.category] ?? 9) - (CATEGORY_ORDER[b.category] ?? 9));
.sort((a, b) => {
const ia = BLOCK_CATEGORIES.findIndex((c) => c.id === a.category);
const ib = BLOCK_CATEGORIES.findIndex((c) => c.id === b.category);
return (ia === -1 ? 99 : ia) - (ib === -1 ? 99 : ib);
});
setBlocks(items);
} catch (err) {
if (cancelled) return;
@@ -16,6 +16,7 @@ const COLOR_MAP: Record<BlockCategory, { bg: string; text: string; dot: string }
scenes: { bg: "bg-amber-500/15", text: "text-amber-400", dot: "bg-amber-400" },
captions: { bg: "bg-cyan-500/15", text: "text-cyan-400", dot: "bg-cyan-400" },
effects: { bg: "bg-rose-500/15", text: "text-rose-400", dot: "bg-rose-400" },
"text-effects": { bg: "bg-violet-500/15", text: "text-violet-400", dot: "bg-violet-400" },
};
export function getCategoryColors(category: BlockCategory) {