{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "loader-glitch-text-01",
  "title": "Loader Glitch Text",
  "description": "Loading text that scrambles and splits into color channels, at three intensities and as a heading",
  "dependencies": [],
  "registryDependencies": [
    "https://ui.cortexcn.dev/r/utils.json"
  ],
  "files": [
    {
      "path": "registry/blocks/loader-glitch-text-01/components/loader-glitch-text-demo.tsx",
      "content": "\"use client\";\n\nimport type { ReactNode } from \"react\";\n\nimport { LoaderGlitchText } from \"./loader-glitch-text\";\n\n// Cortexcn item \"loader-glitch-text-01\", revision d36f60404a2f, MIT license.\n// https://ui.cortexcn.dev/r/v/loader-glitch-text-01/d36f60404a2f.json\n\nconst EXAMPLES: { label: string; loader: ReactNode }[] = [\n  { label: \"Medium (default)\", loader: <LoaderGlitchText /> },\n  {\n    label: \"Subtle\",\n    loader: <LoaderGlitchText intensity=\"subtle\" text=\"SYNCING\" />,\n  },\n  {\n    label: \"Heavy\",\n    loader: <LoaderGlitchText intensity=\"heavy\" text=\"DECRYPTING\" />,\n  },\n  {\n    label: \"As a heading\",\n    loader: (\n      <LoaderGlitchText\n        className=\"font-bold text-3xl tracking-tight\"\n        text=\"BOOTING UP\"\n      />\n    ),\n  },\n];\n\n/** The glitch text loader four ways: its default, softer, harder, and large. */\nexport function LoaderGlitchTextDemo() {\n  return (\n    <div className=\"grid w-full max-w-3xl gap-3 sm:grid-cols-2\">\n      {EXAMPLES.map((example) => (\n        <figure\n          className=\"flex aspect-4/3 flex-col items-center justify-center gap-6 rounded-xl border bg-background p-6\"\n          key={example.label}\n        >\n          <div className=\"flex flex-1 items-center\">{example.loader}</div>\n          <figcaption className=\"font-mono text-muted-foreground text-xs\">\n            {example.label}\n          </figcaption>\n        </figure>\n      ))}\n    </div>\n  );\n}\n",
      "type": "registry:component",
      "target": "components/loader-glitch-text-demo.tsx"
    },
    {
      "path": "registry/blocks/loader-glitch-text-01/components/loader-glitch-text.tsx",
      "content": "\"use client\";\n\nimport type { CSSProperties } from \"react\";\nimport { useCallback, useEffect, useRef, useState } from \"react\";\n\nimport { cn } from \"@/lib/utils\";\n\n// Cortexcn item \"loader-glitch-text-01\", revision d36f60404a2f, MIT license.\n// https://ui.cortexcn.dev/r/v/loader-glitch-text-01/d36f60404a2f.json\n\nconst GLITCH_CHARS = \"!@#$%^&*<>{}[]|/~\";\n\ninterface LoaderGlitchTextProps {\n  text?: string;\n  intensity?: \"subtle\" | \"medium\" | \"heavy\";\n  className?: string;\n}\n\nexport type { LoaderGlitchTextProps };\n\nconst INTENSITY = {\n  subtle: {\n    translateScale: 1,\n    duration: \"4s\",\n    interval: 300,\n    scrambleChars: 1,\n  },\n  medium: {\n    translateScale: 1,\n    duration: \"2s\",\n    interval: 150,\n    scrambleChars: 2,\n  },\n  heavy: {\n    translateScale: 1.5,\n    duration: \"1s\",\n    interval: 80,\n    scrambleChars: 3,\n  },\n} as const;\n\nexport function LoaderGlitchText({\n  text = \"LOADING\",\n  intensity = \"medium\",\n  className,\n}: LoaderGlitchTextProps) {\n  const [displayText, setDisplayText] = useState(text);\n  const textRef = useRef(text);\n  const rafRef = useRef<number>(0);\n  const lastScrambleRef = useRef<number>(0);\n  const scrambleTimeoutRef = useRef<ReturnType<typeof setTimeout>>(undefined);\n\n  textRef.current = text;\n\n  const intensityConfig = INTENSITY[intensity];\n\n  const scrambleText = useCallback(() => {\n    const chars = textRef.current.split(\"\");\n    const indices = new Set<number>();\n\n    while (\n      indices.size < Math.min(intensityConfig.scrambleChars, chars.length)\n    ) {\n      indices.add(Math.floor(Math.random() * chars.length));\n    }\n\n    const scrambled = chars.map((char, i) =>\n      indices.has(i) && char !== \" \"\n        ? GLITCH_CHARS[Math.floor(Math.random() * GLITCH_CHARS.length)]\n        : char,\n    );\n\n    setDisplayText(scrambled.join(\"\"));\n\n    scrambleTimeoutRef.current = setTimeout(\n      () => {\n        setDisplayText(textRef.current);\n      },\n      50 + Math.random() * 50,\n    );\n  }, [intensityConfig.scrambleChars]);\n\n  useEffect(() => {\n    setDisplayText(text);\n  }, [text]);\n\n  useEffect(() => {\n    let running = true;\n\n    const animate = (timestamp: number) => {\n      if (!running) {\n        return;\n      }\n\n      if (timestamp - lastScrambleRef.current >= intensityConfig.interval) {\n        lastScrambleRef.current = timestamp;\n        scrambleText();\n      }\n\n      rafRef.current = requestAnimationFrame(animate);\n    };\n\n    rafRef.current = requestAnimationFrame(animate);\n\n    return () => {\n      running = false;\n      if (rafRef.current) {\n        cancelAnimationFrame(rafRef.current);\n      }\n      if (scrambleTimeoutRef.current) {\n        clearTimeout(scrambleTimeoutRef.current);\n      }\n    };\n  }, [intensityConfig.interval, scrambleText]);\n\n  return (\n    <>\n      <style>{`\n        @keyframes glitch-clip-1 {\n          0% { clip-path: inset(40% 0 61% 0); transform: translate(calc(-2px * var(--translate-scale)), calc(-1px * var(--translate-scale))); }\n          20% { clip-path: inset(92% 0 1% 0); transform: translate(calc(1px * var(--translate-scale)), calc(2px * var(--translate-scale))); }\n          40% { clip-path: inset(43% 0 1% 0); transform: translate(calc(-1px * var(--translate-scale)), calc(3px * var(--translate-scale))); }\n          60% { clip-path: inset(25% 0 58% 0); transform: translate(calc(3px * var(--translate-scale)), calc(-1px * var(--translate-scale))); }\n          80% { clip-path: inset(54% 0 7% 0); transform: translate(calc(-2px * var(--translate-scale)), calc(2px * var(--translate-scale))); }\n          100% { clip-path: inset(58% 0 43% 0); transform: translate(calc(2px * var(--translate-scale)), calc(-2px * var(--translate-scale))); }\n        }\n\n        @keyframes glitch-clip-2 {\n          0% { clip-path: inset(65% 0 13% 0); transform: translate(calc(2px * var(--translate-scale)), calc(1px * var(--translate-scale))); }\n          20% { clip-path: inset(79% 0 14% 0); transform: translate(calc(-3px * var(--translate-scale)), 0px); }\n          40% { clip-path: inset(32% 0 52% 0); transform: translate(calc(1px * var(--translate-scale)), calc(-2px * var(--translate-scale))); }\n          60% { clip-path: inset(8% 0 76% 0); transform: translate(calc(-1px * var(--translate-scale)), calc(3px * var(--translate-scale))); }\n          80% { clip-path: inset(71% 0 2% 0); transform: translate(calc(3px * var(--translate-scale)), calc(1px * var(--translate-scale))); }\n          100% { clip-path: inset(15% 0 62% 0); transform: translate(calc(-2px * var(--translate-scale)), calc(-1px * var(--translate-scale))); }\n        }\n      `}</style>\n      <output\n        aria-label={text}\n        aria-live=\"polite\"\n        className={cn(\"relative inline-block font-mono\", className)}\n        data-slot=\"loader-glitch-text\"\n        style={\n          {\n            \"--translate-scale\": intensityConfig.translateScale,\n          } as CSSProperties\n        }\n      >\n        <span className=\"sr-only\">{text}</span>\n        <span aria-hidden=\"true\" className=\"relative text-foreground\">\n          {displayText}\n        </span>\n        <span\n          aria-hidden=\"true\"\n          className=\"absolute inset-0 text-foreground/70\"\n          style={{\n            color: \"oklch(0.65 0.2 25)\",\n            animation: `glitch-clip-1 ${intensityConfig.duration} infinite linear alternate-reverse`,\n          }}\n        >\n          {displayText}\n        </span>\n        <span\n          aria-hidden=\"true\"\n          className=\"absolute inset-0 text-foreground/70\"\n          style={{\n            color: \"oklch(0.7 0.15 200)\",\n            animation: `glitch-clip-2 ${intensityConfig.duration} infinite linear alternate-reverse`,\n          }}\n        >\n          {displayText}\n        </span>\n        <span\n          aria-hidden=\"true\"\n          className=\"pointer-events-none absolute inset-0\"\n          style={{\n            background:\n              \"repeating-linear-gradient(0deg, transparent, transparent 2px, oklch(0 0 0 / 0.03) 2px, oklch(0 0 0 / 0.03) 4px)\",\n          }}\n        />\n      </output>\n    </>\n  );\n}\n",
      "type": "registry:component",
      "target": "components/loader-glitch-text.tsx"
    }
  ],
  "type": "registry:block",
  "author": "Cortexcn (https://ui.cortexcn.dev)",
  "meta": {
    "license": "MIT",
    "revision": "d36f60404a2f"
  }
}