{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "wavy-text-block",
  "title": "Wavy Text Block",
  "description": "A scroll-triggered wavy text animation component that creates flowing motion effects using Framer Motion and scroll context.",
  "dependencies": [
    "react",
    "motion"
  ],
  "files": [
    {
      "path": "registry/gammaui/wavy-text-block.tsx",
      "content": "\"use client\"\n\nimport React from \"react\"\nimport {\n  HTMLMotionProps,\n  motion,\n  MotionValue,\n  SpringOptions,\n  useMotionValue,\n  useReducedMotion,\n  useScroll,\n  useSpring,\n} from \"motion/react\"\n\ninterface WavyTextsConfig {\n  baseOffsetFactor: number\n  baseExtra: number\n  baseAmplitude: number\n  lengthEffect: number\n  frequency: number\n  progressScale: number\n  phaseShiftDeg: number\n  spring: SpringOptions\n}\ninterface WavyBlockItemProps extends HTMLMotionProps<\"div\"> {\n  index: number\n  config?: WavyTextsConfig\n}\ninterface WavyBlockContextValue {\n  scrollYProgress: MotionValue<number>\n  maxLen: number\n}\n\nconst WavyBlockContext = React.createContext<WavyBlockContextValue | undefined>(\n  undefined\n)\n\nfunction useWavyBlockContext() {\n  const context = React.useContext(WavyBlockContext)\n  if (context === undefined) {\n    throw new Error(\"useWavyBlockContext must be used within a WavyBlock\")\n  }\n  return context\n}\nconst toRadian = (deg: number) => (deg * Math.PI) / 180\n\nexport function WavyBlockItem({\n  index,\n  config = {\n    baseOffsetFactor: 0.1,\n    baseExtra: 0,\n    baseAmplitude: 160,\n    lengthEffect: 0.6,\n    frequency: 35,\n    progressScale: 6,\n    phaseShiftDeg: -180,\n    spring: { damping: 22, stiffness: 300 },\n  },\n  style,\n  ...props\n}: WavyBlockItemProps) {\n  const { scrollYProgress, maxLen } = useWavyBlockContext()\n  const reducedMotion = useReducedMotion()\n  const lengthFactor = Math.min(1, Math.max(0, maxLen / (maxLen || 1)))\n\n  const [isMounted, setIsMounted] = React.useState<boolean>(false)\n\n  const calculateX = React.useCallback(\n    (p: number, windowWidth?: number) => {\n      const phase = config.progressScale * p\n\n      const width =\n        windowWidth ??\n        (typeof window !== \"undefined\" ? window.innerWidth : 1200)\n      const baseOffset = config.baseOffsetFactor * width + config.baseExtra\n\n      const amplitudeScale = 1 - config.lengthEffect * lengthFactor\n      const amplitude = config.baseAmplitude * amplitudeScale\n\n      const angle =\n        toRadian(config.frequency * index) +\n        phase +\n        toRadian(config.phaseShiftDeg)\n\n      return baseOffset + amplitude * Math.cos(angle)\n    },\n    [config, lengthFactor, index]\n  )\n\n  const initialX = calculateX(0, 1200)\n  const rawX = useMotionValue(initialX)\n  const springX = useSpring(rawX, config.spring)\n  const x = reducedMotion ? rawX : springX\n\n  React.useLayoutEffect(() => {\n    setIsMounted(true)\n  }, [])\n\n  React.useEffect(() => {\n    if (!scrollYProgress || !isMounted) return\n\n    const unsub = scrollYProgress.onChange((p) => {\n      const windowWidth =\n        typeof window !== \"undefined\" ? window.innerWidth : 1200\n      const newX = calculateX(p, windowWidth)\n      rawX.set(newX)\n    })\n\n    return () => {\n      if (unsub) unsub()\n    }\n  }, [scrollYProgress, rawX, calculateX, isMounted])\n\n  return (\n    <motion.div style={{ x, ...style }} suppressHydrationWarning {...props} />\n  )\n}\n\nexport function WavyBlock({\n  offset = [\"start end\", \"end start\"],\n  ...props\n}: React.ComponentPropsWithRef<\"div\"> & { offset?: any }) {\n  const containerRef = React.useRef<HTMLDivElement>(null)\n  const { current } = containerRef\n\n  const maxLen = React.useMemo(() => {\n    if (!current?.children || current.children.length === 0) return 1\n    const childrenArray = Array.from(current.children)\n    return Math.max(\n      ...childrenArray.map((child) => (child ? String(child).length : 0))\n    )\n  }, [current?.children])\n\n  const { scrollYProgress } = useScroll({\n    target: containerRef,\n    offset: offset,\n  })\n  return (\n    <WavyBlockContext.Provider value={{ scrollYProgress, maxLen }}>\n      <div ref={containerRef} {...props} />\n    </WavyBlockContext.Provider>\n  )\n}\n",
      "type": "registry:ui"
    }
  ],
  "type": "registry:ui"
}