{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "word-push",
  "title": "Word Push",
  "description": "A phrase builds from its first word centered: each next word drives in from the right and pushes the line left, easing into the new center.",
  "dependencies": ["remotion"],
  "files": [
    {
      "path": "registry/remocn/word-push/index.tsx",
      "content": "\"use client\";\n\nimport { Easing, interpolate, useCurrentFrame } from \"remotion\";\n\nexport interface WordPushProps {\n  text: string;\n  fontSize?: number;\n  color?: string;\n  fontWeight?: number;\n  wordGap?: number;\n  accel?: number;\n  speed?: number;\n  className?: string;\n}\n\nconst REVEAL_FRAMES = 2;\nconst SLIDE_FRAMES = 12;\nconst OVERSHOOT_EM = 0.5;\nconst CHAR_WIDTH_EM = 0.5;\nconst SPACE_WIDTH_EM = 0.22;\n\nfunction revealFrame(index: number, wordGap: number, accel: number): number {\n  if (accel === 1) return index * wordGap;\n  return (wordGap * (1 - accel ** index)) / (1 - accel);\n}\n\nexport function wordPushLength(\n  text: string,\n  wordGap = 10,\n  accel = 0.8,\n): number {\n  const words = text.trim().split(/\\s+/).filter(Boolean);\n  if (words.length === 0) return 0;\n  return (\n    Math.ceil(revealFrame(words.length - 1, wordGap, accel)) + SLIDE_FRAMES\n  );\n}\n\nconst pushEasing = Easing.out(Easing.cubic);\nconst driveEasing = Easing.bezier(0.15, 0.7, 0.25, 1);\n\nexport function WordPush({\n  text,\n  fontSize = 72,\n  color = \"#171717\",\n  fontWeight = 400,\n  wordGap = 10,\n  accel = 0.8,\n  speed = 1,\n  className,\n}: WordPushProps) {\n  const frame = useCurrentFrame() * speed;\n\n  const words = text.trim().split(/\\s+/).filter(Boolean);\n\n  let lineX = interpolate(\n    frame,\n    [0, SLIDE_FRAMES],\n    [-OVERSHOOT_EM * fontSize, 0],\n    {\n      extrapolateLeft: \"clamp\",\n      extrapolateRight: \"clamp\",\n      easing: driveEasing,\n    },\n  );\n  for (let j = 1; j < words.length; j++) {\n    const wordWidth =\n      (words[j].length * CHAR_WIDTH_EM + SPACE_WIDTH_EM) * fontSize;\n    const revealAt = revealFrame(j, wordGap, accel);\n    const pushed = interpolate(\n      frame,\n      [revealAt + 1, revealAt + 1 + SLIDE_FRAMES],\n      [0, 1],\n      {\n        extrapolateLeft: \"clamp\",\n        extrapolateRight: \"clamp\",\n        easing: pushEasing,\n      },\n    );\n    lineX += (wordWidth / 2) * (1 - pushed);\n  }\n\n  return (\n    <div\n      style={{\n        position: \"absolute\",\n        inset: 0,\n        display: \"flex\",\n        alignItems: \"center\",\n        justifyContent: \"center\",\n        whiteSpace: \"nowrap\",\n        translate: `${lineX}px 0`,\n        background: \"transparent\",\n      }}\n    >\n      <span\n        className={className}\n        style={{\n          fontSize,\n          fontWeight,\n          color,\n          letterSpacing: \"-0.02em\",\n          fontFamily:\n            \"var(--font-geist-sans, -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif)\",\n        }}\n      >\n        {words.map((word, j) => {\n          const revealAt = revealFrame(j, wordGap, accel);\n\n          const opacity = interpolate(\n            frame,\n            [revealAt, revealAt + REVEAL_FRAMES],\n            [0, 1],\n            {\n              extrapolateLeft: \"clamp\",\n              extrapolateRight: \"clamp\",\n            },\n          );\n\n          const driven =\n            j === 0\n              ? 1\n              : interpolate(\n                  frame,\n                  [revealAt, revealAt + SLIDE_FRAMES],\n                  [0, 1],\n                  {\n                    extrapolateLeft: \"clamp\",\n                    extrapolateRight: \"clamp\",\n                    easing: driveEasing,\n                  },\n                );\n\n          return (\n            <span\n              key={j}\n              style={{\n                display: \"inline-block\",\n                whiteSpace: \"pre\",\n                marginRight: j < words.length - 1 ? \"0.22em\" : undefined,\n                opacity,\n                translate: `${OVERSHOOT_EM * fontSize * (1 - driven)}px 0`,\n              }}\n            >\n              {word}\n            </span>\n          );\n        })}\n      </span>\n    </div>\n  );\n}\n",
      "type": "registry:component",
      "target": "components/remocn/word-push.tsx"
    }
  ],
  "type": "registry:component"
}
