{
  "name": "text-effect-exit",
  "type": "registry:ui",
  "componentName": "text-effect",
  "description": "Text effect with custom exit animations.",
  "files": [
    {
      "path": "text-effect-exit.tsx",
      "content": "'use client';\nimport { useState, useEffect } from 'react';\nimport { TextEffect } from '@/components/core/text-effect';\n\nexport function TextEffectWithExit() {\n  const [trigger, setTrigger] = useState(true);\n\n  useEffect(() => {\n    const interval = setInterval(() => {\n      setTrigger((prev) => !prev);\n    }, 2000);\n\n    return () => clearInterval(interval);\n  }, []);\n  const blurSlideVariants = {\n    container: {\n      hidden: { opacity: 0 },\n      visible: {\n        opacity: 1,\n        transition: { staggerChildren: 0.01 },\n      },\n      exit: {\n        transition: { staggerChildren: 0.01, staggerDirection: 1 },\n      },\n    },\n    item: {\n      hidden: {\n        opacity: 0,\n        filter: 'blur(10px) brightness(0%)',\n        y: 0,\n      },\n      visible: {\n        opacity: 1,\n        y: 0,\n        filter: 'blur(0px) brightness(100%)',\n        transition: {\n          duration: 0.4,\n        },\n      },\n      exit: {\n        opacity: 0,\n        y: -30,\n        filter: 'blur(10px) brightness(0%)',\n        transition: {\n          duration: 0.4,\n        },\n      },\n    },\n  };\n\n  return (\n    <TextEffect\n      className='inline-flex'\n      per='char'\n      variants={blurSlideVariants}\n      trigger={trigger}\n    >\n      Animate your ideas with components\n    </TextEffect>\n  );\n}\n",
      "type": "registry:component"
    },
    {
      "path": "components/core/text-effect.tsx",
      "content": "'use client';\nimport { cn } from '@/lib/utils';\nimport { AnimatePresence, motion } from 'motion/react';\nimport type {\n  TargetAndTransition,\n  Transition,\n  Variant,\n  Variants,\n} from 'motion/react';\nimport React from 'react';\n\nexport type PresetType = 'blur' | 'fade-in-blur' | 'scale' | 'fade' | 'slide';\n\nexport type PerType = 'word' | 'char' | 'line';\n\nexport type TextEffectProps = {\n  children: string;\n  per?: PerType;\n  as?: keyof React.JSX.IntrinsicElements;\n  variants?: {\n    container?: Variants;\n    item?: Variants;\n  };\n  className?: string;\n  preset?: PresetType;\n  delay?: number;\n  speedReveal?: number;\n  speedSegment?: number;\n  trigger?: boolean;\n  onAnimationComplete?: () => void;\n  onAnimationStart?: () => void;\n  segmentWrapperClassName?: string;\n  containerTransition?: Transition;\n  segmentTransition?: Transition;\n  style?: React.CSSProperties;\n};\n\nconst defaultStaggerTimes: Record<PerType, number> = {\n  char: 0.03,\n  word: 0.05,\n  line: 0.1,\n};\n\nconst defaultContainerVariants: Variants = {\n  hidden: { opacity: 0 },\n  visible: {\n    opacity: 1,\n    transition: {\n      staggerChildren: 0.05,\n    },\n  },\n  exit: {\n    transition: { staggerChildren: 0.05, staggerDirection: -1 },\n  },\n};\n\nconst defaultItemVariants: Variants = {\n  hidden: { opacity: 0 },\n  visible: {\n    opacity: 1,\n  },\n  exit: { opacity: 0 },\n};\n\nconst presetVariants: Record<\n  PresetType,\n  { container: Variants; item: Variants }\n> = {\n  blur: {\n    container: defaultContainerVariants,\n    item: {\n      hidden: { opacity: 0, filter: 'blur(12px)' },\n      visible: { opacity: 1, filter: 'blur(0px)' },\n      exit: { opacity: 0, filter: 'blur(12px)' },\n    },\n  },\n  'fade-in-blur': {\n    container: defaultContainerVariants,\n    item: {\n      hidden: { opacity: 0, y: 20, filter: 'blur(12px)' },\n      visible: { opacity: 1, y: 0, filter: 'blur(0px)' },\n      exit: { opacity: 0, y: 20, filter: 'blur(12px)' },\n    },\n  },\n  scale: {\n    container: defaultContainerVariants,\n    item: {\n      hidden: { opacity: 0, scale: 0 },\n      visible: { opacity: 1, scale: 1 },\n      exit: { opacity: 0, scale: 0 },\n    },\n  },\n  fade: {\n    container: defaultContainerVariants,\n    item: {\n      hidden: { opacity: 0 },\n      visible: { opacity: 1 },\n      exit: { opacity: 0 },\n    },\n  },\n  slide: {\n    container: defaultContainerVariants,\n    item: {\n      hidden: { opacity: 0, y: 20 },\n      visible: { opacity: 1, y: 0 },\n      exit: { opacity: 0, y: 20 },\n    },\n  },\n};\n\nconst AnimationComponent: React.FC<{\n  segment: string;\n  variants: Variants;\n  per: 'line' | 'word' | 'char';\n  segmentWrapperClassName?: string;\n}> = React.memo(({ segment, variants, per, segmentWrapperClassName }) => {\n  const content =\n    per === 'line' ? (\n      <motion.span variants={variants} className='block'>\n        {segment}\n      </motion.span>\n    ) : per === 'word' ? (\n      <motion.span\n        aria-hidden='true'\n        variants={variants}\n        className='inline-block whitespace-pre'\n      >\n        {segment}\n      </motion.span>\n    ) : (\n      <motion.span className='inline-block whitespace-pre'>\n        {segment.split('').map((char, charIndex) => (\n          <motion.span\n            key={`char-${charIndex}`}\n            aria-hidden='true'\n            variants={variants}\n            className='inline-block whitespace-pre'\n          >\n            {char}\n          </motion.span>\n        ))}\n      </motion.span>\n    );\n\n  if (!segmentWrapperClassName) {\n    return content;\n  }\n\n  const defaultWrapperClassName = per === 'line' ? 'block' : 'inline-block';\n\n  return (\n    <span className={cn(defaultWrapperClassName, segmentWrapperClassName)}>\n      {content}\n    </span>\n  );\n});\n\nAnimationComponent.displayName = 'AnimationComponent';\n\nconst splitText = (text: string, per: PerType) => {\n  if (per === 'line') return text.split('\\n');\n  return text.split(/(\\s+)/);\n};\n\nconst hasTransition = (\n  variant?: Variant\n): variant is TargetAndTransition & { transition?: Transition } => {\n  if (!variant) return false;\n  return typeof variant === 'object' && 'transition' in variant;\n};\n\nconst createVariantsWithTransition = (\n  baseVariants: Variants,\n  transition?: Transition & { exit?: Transition }\n): Variants => {\n  if (!transition) return baseVariants;\n\n  const mainTransition: Transition & { exit?: Transition } = {\n    ...transition,\n  };\n  delete mainTransition.exit;\n\n  return {\n    ...baseVariants,\n    visible: {\n      ...baseVariants.visible,\n      transition: {\n        ...(hasTransition(baseVariants.visible)\n          ? baseVariants.visible.transition\n          : {}),\n        ...mainTransition,\n      },\n    },\n    exit: {\n      ...baseVariants.exit,\n      transition: {\n        ...(hasTransition(baseVariants.exit)\n          ? baseVariants.exit.transition\n          : {}),\n        ...mainTransition,\n        staggerDirection: -1,\n      },\n    },\n  };\n};\n\nexport function TextEffect({\n  children,\n  per = 'word',\n  as = 'p',\n  variants,\n  className,\n  preset = 'fade',\n  delay = 0,\n  speedReveal = 1,\n  speedSegment = 1,\n  trigger = true,\n  onAnimationComplete,\n  onAnimationStart,\n  segmentWrapperClassName,\n  containerTransition,\n  segmentTransition,\n  style,\n}: TextEffectProps) {\n  const segments = splitText(children, per);\n  const MotionTag = motion[as as keyof typeof motion] as typeof motion.div;\n\n  const baseVariants = preset\n    ? presetVariants[preset]\n    : { container: defaultContainerVariants, item: defaultItemVariants };\n\n  const stagger = defaultStaggerTimes[per] / speedReveal;\n\n  const baseDuration = 0.3 / speedSegment;\n\n  const customStagger = hasTransition(variants?.container?.visible ?? {})\n    ? (variants?.container?.visible as TargetAndTransition).transition\n        ?.staggerChildren\n    : undefined;\n\n  const customDelay = hasTransition(variants?.container?.visible ?? {})\n    ? (variants?.container?.visible as TargetAndTransition).transition\n        ?.delayChildren\n    : undefined;\n\n  const computedVariants = {\n    container: createVariantsWithTransition(\n      variants?.container || baseVariants.container,\n      {\n        staggerChildren: customStagger ?? stagger,\n        delayChildren: customDelay ?? delay,\n        ...containerTransition,\n        exit: {\n          staggerChildren: customStagger ?? stagger,\n          staggerDirection: -1,\n        },\n      }\n    ),\n    item: createVariantsWithTransition(variants?.item || baseVariants.item, {\n      duration: baseDuration,\n      ...segmentTransition,\n    }),\n  };\n\n  return (\n    <AnimatePresence mode='popLayout'>\n      {trigger && (\n        <MotionTag\n          initial='hidden'\n          animate='visible'\n          exit='exit'\n          variants={computedVariants.container}\n          className={className}\n          onAnimationComplete={onAnimationComplete}\n          onAnimationStart={onAnimationStart}\n          style={style}\n        >\n          {per !== 'line' ? <span className='sr-only'>{children}</span> : null}\n          {segments.map((segment, index) => (\n            <AnimationComponent\n              key={`${per}-${index}-${segment}`}\n              segment={segment}\n              variants={computedVariants.item}\n              per={per}\n              segmentWrapperClassName={segmentWrapperClassName}\n            />\n          ))}\n        </MotionTag>\n      )}\n    </AnimatePresence>\n  );\n}\n",
      "type": "registry:ui"
    }
  ]
}