{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "thunder-loader",
  "type": "registry:ui",
  "title": "Thunder Loader",
  "description": "Animated thunderbolt loader with glow and shimmer effects, customizable sizes and colors.",
  "author": "Ahdeetai <https://aditya.is-cool.dev>",
  "registryDependencies": [],
  "dependencies": [
    "motion",
    "class-variance-authority",
    "clsx",
    "tailwind-merge"
  ],
  "files": [
    {
      "type": "registry:ui",
      "path": "components/ui/thunder-loader.tsx",
      "content": "'use client';\nimport * as React from 'react';\nimport { motion } from 'motion/react';\nimport { cva, type VariantProps } from 'class-variance-authority';\nimport { cn } from '@/lib/utils';\n\nconst thunderLoaderVariants = cva('inline-block overflow-visible', {\n  variants: {\n    size: {\n      xs: 'w-4 h-4',\n      sm: 'w-6 h-6',\n      md: 'w-8 h-8',\n      lg: 'w-12 h-12',\n      xl: 'w-16 h-16',\n      '2xl': 'w-20 h-20',\n    },\n    variant: {\n      default: '',\n      electric: '',\n      fire: '',\n      ice: '',\n      rainbow: '',\n      subtle: '',\n    },\n  },\n  defaultVariants: {\n    size: 'md',\n    variant: 'default',\n  },\n});\n\ninterface ThunderLoaderProps\n  extends\n    React.HTMLAttributes<HTMLDivElement>,\n    VariantProps<typeof thunderLoaderVariants> {\n  fillDuration?: number;\n  glowDuration?: number;\n  animateDuration?: number;\n  fillColor?: string;\n  glowColor?: string;\n  baseColor?: string;\n  strokeWidth?: number;\n  showGlow?: boolean;\n  showFill?: boolean;\n  animate?: boolean | 'thunder';\n  viewBox?: string;\n  customPath?: string;\n}\n\nconst variantColors = {\n  default: {\n    shimmer: '#60a5fa',\n    glow: '#3b82f6',\n    base: '#1e40af',\n  },\n  fire: {\n    shimmer: '#fbbf24',\n    glow: '#f59e0b',\n    base: '#d97706',\n  },\n  electric: {\n    shimmer: '#fb7185',\n    glow: '#f43f5e',\n    base: '#e11d48',\n  },\n  ice: {\n    shimmer: '#67e8f9',\n    glow: '#06b6d4',\n    base: '#0891b2',\n  },\n  rainbow: {\n    shimmer: '#a855f7',\n    glow: '#8b5cf6',\n    base: '#7c3aed',\n  },\n  subtle: {\n    shimmer: '#94a3b8',\n    glow: '#64748b',\n    base: '#475569',\n  },\n};\n\nconst defaultThunderPath =\n  'M50 10 L 35 45 L 55 45 L 40 70 L 70 35 L 50 35 L 65 10 Z';\n\nconst ThunderLoader = React.forwardRef<HTMLDivElement, ThunderLoaderProps>(\n  (\n    {\n      className,\n      size,\n      variant = 'default',\n      fillDuration = 2,\n      glowDuration = 3,\n      animateDuration = 2,\n      fillColor,\n      glowColor,\n      baseColor,\n      strokeWidth = 2,\n      showGlow = false,\n      showFill = false,\n      animate = false,\n      viewBox = '0 0 100 80',\n      customPath,\n      ...props\n    },\n    ref,\n  ) => {\n    const colors = variantColors[variant!] || variantColors.default;\n    const finalFillColor = fillColor || colors.shimmer;\n    const finalGlowColor = glowColor || colors.glow;\n    const finalBaseColor = baseColor || colors.base;\n    const thunderPath = customPath || defaultThunderPath;\n    const isThunderAnimation = animate === 'thunder';\n\n    const [gradientId] = React.useState(\n      () => `thunder-gradient-${Math.random().toString(36).substr(2, 9)}`,\n    );\n    const [filterId] = React.useState(\n      () => `thunder-filter-${Math.random().toString(36).substr(2, 9)}`,\n    );\n\n    const pathRef = React.useRef<SVGPathElement>(null);\n    const [pathLength, setPathLength] = React.useState(0);\n    const [fillProgress, setFillProgress] = React.useState(0);\n\n    React.useEffect(() => {\n      if (pathRef.current) {\n        setPathLength(pathRef.current.getTotalLength());\n      }\n    }, [thunderPath]);\n\n    React.useEffect(() => {\n      if (!showFill) return;\n      let frame: number;\n      let start: number | null = null;\n      function animateFill(ts: number) {\n        if (start === null) start = ts;\n        const elapsed = (ts - start) / 1000;\n        const fillTime = fillDuration;\n        const unfillTime = fillDuration * 1.5;\n        const total = fillTime + unfillTime;\n        const t = elapsed % total;\n        let progress;\n        if (t < fillTime) {\n          progress = t / fillTime;\n        } else {\n          progress = 1 - (t - fillTime) / unfillTime;\n        }\n        setFillProgress(progress);\n        frame = requestAnimationFrame(animateFill);\n      }\n      frame = requestAnimationFrame(animateFill);\n      return () => cancelAnimationFrame(frame);\n    }, [fillDuration, showFill]);\n\n    return (\n      <div\n        ref={ref}\n        className={cn(thunderLoaderVariants({ size, variant }), className)}\n        {...props}\n      >\n        <motion.svg\n          className='w-full h-full'\n          viewBox={viewBox}\n          fill='none'\n          initial={animate ? { opacity: 0, scale: 0.8 } : undefined}\n          animate={animate ? { opacity: 1, scale: 1 } : undefined}\n          transition={{ duration: 0.5, ease: 'easeOut' }}\n        >\n          <defs>\n            {showFill && (\n              <linearGradient id={gradientId} x1='0%' y1='100%' x2='0%' y2='0%'>\n                <stop\n                  offset='0%'\n                  stopColor={finalFillColor}\n                  stopOpacity='0.7'\n                />\n                <stop\n                  offset='100%'\n                  stopColor={finalFillColor}\n                  stopOpacity='0.1'\n                />\n              </linearGradient>\n            )}\n            {showGlow && (\n              <filter\n                id={filterId}\n                x='-100%'\n                y='-100%'\n                width='300%'\n                height='300%'\n              >\n                <feGaussianBlur stdDeviation='3' result='coloredBlur' />\n                <feMerge>\n                  <feMergeNode in='coloredBlur' />\n                  <feMergeNode in='SourceGraphic' />\n                </feMerge>\n              </filter>\n            )}\n          </defs>\n          {showGlow && (\n            <motion.path\n              d={thunderPath}\n              stroke={finalGlowColor}\n              strokeWidth={strokeWidth + 1}\n              fill='none'\n              strokeLinecap='round'\n              strokeLinejoin='round'\n              filter={`url(#${filterId})`}\n              initial={{ opacity: 0.6 }}\n              animate={{ opacity: 0.6 }}\n            >\n              <animate\n                attributeName='opacity'\n                values='0.3;0.8;0.3'\n                dur={`${glowDuration}s`}\n                repeatCount='indefinite'\n              />\n            </motion.path>\n          )}\n          <motion.path\n            ref={pathRef}\n            d={thunderPath}\n            stroke={finalBaseColor}\n            strokeWidth={strokeWidth}\n            fill='none'\n            strokeLinecap='round'\n            strokeLinejoin='round'\n            initial={\n              isThunderAnimation\n                ? false\n                : animate\n                  ? { pathLength: 0, opacity: 0 }\n                  : undefined\n            }\n            animate={\n              isThunderAnimation\n                ? {\n                    strokeDasharray: pathLength,\n                    strokeDashoffset: [pathLength, -pathLength],\n                  }\n                : animate\n                  ? { pathLength: 1, opacity: 1 }\n                  : undefined\n            }\n            transition={\n              isThunderAnimation\n                ? {\n                    repeat: Infinity,\n                    duration: animateDuration,\n                    ease: 'linear',\n                  }\n                : animate\n                  ? { duration: animateDuration, delay: 0.5, ease: 'easeInOut' }\n                  : undefined\n            }\n          />\n          {showFill && (\n            <mask id={`fill-mask-${gradientId}`}>\n              <rect\n                x='0'\n                y={80 - fillProgress * 80}\n                width='100'\n                height={fillProgress * 80}\n                fill='white'\n              />\n            </mask>\n          )}\n          {showFill && (\n            <path\n              d={thunderPath}\n              fill={`url(#${gradientId})`}\n              stroke='none'\n              mask={`url(#fill-mask-${gradientId})`}\n            />\n          )}\n          {variant === 'rainbow' && (\n            <motion.circle\n              cx='50'\n              cy='40'\n              r='1'\n              fill={finalFillColor}\n              initial={{ opacity: 0, scale: 0 }}\n              animate={{\n                opacity: [0, 1, 0],\n                scale: [0, 1.5, 0],\n              }}\n              transition={{\n                duration: 2,\n                repeat: Infinity,\n                repeatDelay: 1,\n                delay: 1,\n              }}\n            />\n          )}\n        </motion.svg>\n      </div>\n    );\n  },\n);\n\nThunderLoader.displayName = 'ThunderLoader';\n\nexport { ThunderLoader, thunderLoaderVariants, type ThunderLoaderProps };\n"
    },
    {
      "type": "registry:lib",
      "path": "lib/utils.ts",
      "content": "import { clsx, type ClassValue } from \"clsx\";\nimport { twMerge } from \"tailwind-merge\";\n\nexport function cn(...inputs: ClassValue[]) {\n  return twMerge(clsx(inputs));\n}"
    }
  ]
}
