{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "typewriter-loop",
  "title": "Typewriter Loop",
  "description": "A looping typewriter CTA with a gradient support, premium touch.",
  "dependencies": [
    "motion"
  ],
  "files": [
    {
      "path": "packages/typewriter-loop/typewriter-loop.tsx",
      "content": "\"use client\";\r\n\r\nimport { useEffect, useState, useCallback } from \"react\";\r\nimport { motion, AnimatePresence, Transition } from \"motion/react\";\r\nimport { cn } from \"@/lib/utils\";\r\n\r\ninterface TypewriterLoopProps {\r\n  LeadText?: string;\r\n  morphingText?: string[];\r\n  className?: string;\r\n  interval?: number;\r\n  transition?: Transition;\r\n  LeadTextClassName?: string;\r\n  morphingTextClassName?: string;\r\n  backgroundClassName?: string;\r\n  cursorClassName?: string;\r\n  staticColor?: \"violet\" | \"blue\" | \"emerald\" | \"amber\" | \"rose\" | \"fuchsia\";\r\n  darkGradientBackgroundLock?: boolean; // New prop\r\n}\r\n\r\nconst TypewriterLoop = ({\r\n  LeadText = \"Design\",\r\n  morphingText = [\"Limitless\", \"Timeless\", \"Flawless\"],\r\n  className,\r\n  interval = 4000,\r\n  transition = { duration: 0.8, ease: \"easeInOut\" },\r\n  LeadTextClassName,\r\n  morphingTextClassName,\r\n  backgroundClassName,\r\n  cursorClassName,\r\n  staticColor,\r\n  darkGradientBackgroundLock = false, // New prop with default false\r\n}: TypewriterLoopProps) => {\r\n  const [index, setIndex] = useState(0);\r\n  const [colorIndex, setColorIndex] = useState(0);\r\n  const [nextIndex, setNextIndex] = useState(1);\r\n\r\n  const gradientColors = [\r\n    \"from-violet-400 to-violet-800 dark:from-violet-400 dark:to-violet-600\",\r\n    \"from-blue-400 to-blue-800 dark:from-blue-400 dark:to-blue-600\",\r\n    \"from-emerald-400 to-emerald-800 dark:from-emerald-400 dark:to-emerald-600\",\r\n    \"from-amber-400 to-amber-800 dark:from-amber-400 dark:to-amber-600\",\r\n    \"from-rose-400 to-rose-800 dark:from-rose-400 dark:to-rose-600\",\r\n    \"from-fuchsia-400 to-fuchsia-800 dark:from-fuchsia-400 dark:to-fuchsia-600\",\r\n  ];\r\n\r\n  const backgroundColors = [\r\n    \"from-transparent via-purple-200/30 to-purple-200 dark:from-transparent dark:via-violet-950/30 dark:to-violet-950/60\",\r\n    \"from-transparent via-blue-200/30 to-blue-200 dark:from-transparent dark:via-blue-950/30 dark:to-blue-950/60\",\r\n    \"from-transparent via-emerald-200/30 to-emerald-200 dark:from-transparent dark:via-emerald-950/30 dark:to-emerald-950/60\",\r\n    \"from-transparent via-amber-200/30 to-amber-200 dark:from-transparent dark:via-amber-950/30 dark:to-amber-950/60\",\r\n    \"from-transparent via-rose-200/30 to-rose-200 dark:from-transparent dark:via-rose-950/30 dark:to-rose-950/60\",\r\n    \"from-transparent via-fuchsia-200/30 to-fuchsia-200 dark:from-transparent dark:via-fuchsia-950/30 dark:to-fuchsia-950/60\",\r\n  ];\r\n\r\n  // Dark-only background colors (no light mode variants)\r\n  const darkOnlyBackgroundColors = [\r\n    \"from-transparent via-violet-950/30 to-violet-950/60\",\r\n    \"from-transparent via-blue-950/30 to-blue-950/60\",\r\n    \"from-transparent via-emerald-950/30 to-emerald-950/60\",\r\n    \"from-transparent via-amber-950/30 to-amber-950/60\",\r\n    \"from-transparent via-rose-950/30 to-rose-950/60\",\r\n    \"from-transparent via-fuchsia-950/30 to-fuchsia-950/60\",\r\n  ];\r\n\r\n  const cursorColors = [\r\n    \"bg-violet-500\",\r\n    \"bg-blue-500\",\r\n    \"bg-emerald-500\",\r\n    \"bg-amber-500\",\r\n    \"bg-rose-500\",\r\n    \"bg-fuchsia-500\",\r\n  ];\r\n\r\n  // Color mapping for static colors\r\n  const staticGradientMap = {\r\n    violet:\r\n      \"from-violet-400 to-violet-800 dark:from-violet-400 dark:to-violet-600\",\r\n    blue: \"from-blue-400 to-blue-800 dark:from-blue-400 dark:to-blue-600\",\r\n    emerald:\r\n      \"from-emerald-400 to-emerald-800 dark:from-emerald-400 dark:to-emerald-600\",\r\n    amber: \"from-amber-400 to-amber-800 dark:from-amber-400 dark:to-amber-600\",\r\n    rose: \"from-rose-400 to-rose-800 dark:from-rose-400 dark:to-rose-600\",\r\n    fuchsia:\r\n      \"from-fuchsia-400 to-fuchsia-800 dark:from-fuchsia-400 dark:to-fuchsia-600\",\r\n  };\r\n\r\n  const staticBackgroundMap = {\r\n    violet:\r\n      \"from-transparent via-purple-200/30 to-purple-200 dark:from-transparent dark:via-violet-950/30 dark:to-violet-950/60\",\r\n    blue: \"from-transparent via-blue-200/30 to-blue-200 dark:from-transparent dark:via-blue-950/30 dark:to-blue-950/60\",\r\n    emerald:\r\n      \"from-transparent via-emerald-200/30 to-emerald-200 dark:from-transparent dark:via-emerald-950/30 dark:to-emerald-950/60\",\r\n    amber:\r\n      \"from-transparent via-amber-200/30 to-amber-200 dark:from-transparent dark:via-amber-950/30 dark:to-amber-950/60\",\r\n    rose: \"from-transparent via-rose-200/30 to-rose-200 dark:from-transparent dark:via-rose-950/30 dark:to-rose-950/60\",\r\n    fuchsia:\r\n      \"from-transparent via-fuchsia-200/30 to-fuchsia-200 dark:from-transparent dark:via-fuchsia-950/30 dark:to-fuchsia-950/60\",\r\n  };\r\n\r\n  // Dark-only static background map\r\n  const staticDarkOnlyBackgroundMap = {\r\n    violet: \"from-transparent via-violet-950/30 to-violet-950/60\",\r\n    blue: \"from-transparent via-blue-950/30 to-blue-950/60\",\r\n    emerald: \"from-transparent via-emerald-950/30 to-emerald-950/60\",\r\n    amber: \"from-transparent via-amber-950/30 to-amber-950/60\",\r\n    rose: \"from-transparent via-rose-950/30 to-rose-950/60\",\r\n    fuchsia: \"from-transparent via-fuchsia-950/30 to-fuchsia-950/60\",\r\n  };\r\n\r\n  const staticCursorMap = {\r\n    violet: \"bg-violet-500\",\r\n    blue: \"bg-blue-500\",\r\n    emerald: \"bg-emerald-500\",\r\n    amber: \"bg-amber-500\",\r\n    rose: \"bg-rose-500\",\r\n    fuchsia: \"bg-fuchsia-500\",\r\n  };\r\n\r\n  // Determine which gradient to use\r\n  const getGradientColor = () => {\r\n    if (staticColor) {\r\n      return staticGradientMap[staticColor];\r\n    }\r\n    return gradientColors[colorIndex];\r\n  };\r\n\r\n  const getBackgroundColor = () => {\r\n    // If dark lock is enabled, use dark-only backgrounds\r\n    if (darkGradientBackgroundLock) {\r\n      if (staticColor) {\r\n        return staticDarkOnlyBackgroundMap[staticColor];\r\n      }\r\n      return darkOnlyBackgroundColors[colorIndex];\r\n    }\r\n\r\n    // Otherwise use normal backgrounds with both light and dark variants\r\n    if (staticColor) {\r\n      return staticBackgroundMap[staticColor];\r\n    }\r\n    return backgroundColors[colorIndex];\r\n  };\r\n\r\n  const getCursorColor = () => {\r\n    if (staticColor) {\r\n      return staticCursorMap[staticColor];\r\n    }\r\n    return cursorColors[colorIndex];\r\n  };\r\n\r\n  // Pre-calculate next index\r\n  useEffect(() => {\r\n    setNextIndex((index + 1) % morphingText.length);\r\n  }, [index, morphingText.length]);\r\n\r\n  useEffect(() => {\r\n    const timer = setInterval(() => {\r\n      setIndex((prev) => (prev + 1) % morphingText.length);\r\n    }, interval);\r\n    return () => clearInterval(timer);\r\n  }, [morphingText.length, interval]);\r\n\r\n  const handleExitComplete = useCallback(() => {\r\n    // Only cycle colors if staticColor is not provided\r\n    if (!staticColor) {\r\n      setColorIndex((prev) => (prev + 1) % gradientColors.length);\r\n    }\r\n  }, [gradientColors.length, staticColor]);\r\n\r\n  return (\r\n    <div\r\n      className={cn(\r\n        \"flex flex-wrap items-center justify-start w-fit gap-x-2 md:gap-x-3 gap-y-1 text-3xl md:text-7xl font-medium tracking-tight\",\r\n        className,\r\n      )}\r\n    >\r\n      <span\r\n        className={cn(\"whitespace-nowrap text-foreground\", LeadTextClassName)}\r\n      >\r\n        {LeadText}\r\n      </span>\r\n      <div className=\"relative flex items-center\">\r\n        <AnimatePresence mode=\"wait\" onExitComplete={handleExitComplete}>\r\n          <motion.div\r\n            key={morphingText[index]}\r\n            initial={{ width: 0, opacity: 0 }}\r\n            animate={{ width: \"auto\", opacity: 1 }}\r\n            exit={{ width: 0, opacity: 0 }}\r\n            transition={transition}\r\n            className=\"overflow-hidden whitespace-nowrap relative\"\r\n          >\r\n            {/* Background gradient box */}\r\n            <div\r\n              className={cn(\r\n                \"absolute inset-0\",\r\n                \"bg-gradient-to-r\",\r\n                getBackgroundColor(),\r\n                backgroundClassName,\r\n              )}\r\n            />\r\n\r\n            <span\r\n              className={cn(\r\n                \"relative bg-clip-text text-transparent\",\r\n                \"bg-gradient-to-r\",\r\n                getGradientColor(),\r\n                \"pr-1\",\r\n                morphingTextClassName,\r\n              )}\r\n            >\r\n              {morphingText[index]}\r\n            </span>\r\n          </motion.div>\r\n        </AnimatePresence>\r\n\r\n        {/* Cursor Line */}\r\n        <motion.div\r\n          key={staticColor ? `cursor-${staticColor}` : `cursor-${colorIndex}`}\r\n          className={cn(\r\n            \"w-[3px] md:w-[4px] h-[1.10em] sm:h-[1em]\",\r\n            getCursorColor(),\r\n            cursorClassName,\r\n          )}\r\n          animate={{ opacity: [1, 0.5] }}\r\n          transition={{\r\n            duration: 0.8,\r\n            repeat: Infinity,\r\n            repeatType: \"reverse\",\r\n          }}\r\n        />\r\n      </div>\r\n    </div>\r\n  );\r\n};\r\n\r\nexport default TypewriterLoop;\r\n",
      "type": "registry:component",
      "target": "components/satori-ui/typewriter-loop.tsx"
    }
  ],
  "type": "registry:component"
}