{
  "name": "ai-loading",
  "type": "registry:component",
  "dependencies": [
    "motion"
  ],
  "files": [
    {
      "type": "registry:component",
      "content": "\"use client\";\n\n/**\n * @author: @kokonutui\n * @description: AI Loading State\n * @version: 1.0.0\n * @date: 2025-06-26\n * @license: MIT\n * @website: https://kokonutui.com\n * @github: https://github.com/kokonut-labs/kokonutui\n */\n\nimport { useEffect, useRef, useState } from \"react\";\n\nconst TASK_SEQUENCES = [\n  {\n    status: \"Searching the web\",\n    lines: [\n      \"Initializing web search...\",\n      \"Scanning web pages...\",\n      \"Visiting 5 websites...\",\n      \"Analyzing content...\",\n      \"Generating summary...\",\n    ],\n  },\n  {\n    status: \"Analyzing results\",\n    lines: [\n      \"Analyzing search results...\",\n      \"Generating summary...\",\n      \"Checking for relevant information...\",\n      \"Finalizing analysis...\",\n      \"Setting up lazy loading...\",\n      \"Configuring caching strategies...\",\n      \"Running performance tests...\",\n      \"Finalizing optimizations...\",\n    ],\n  },\n  {\n    status: \"Enhancing UI/UX\",\n    lines: [\n      \"Initializing UI enhancement scan...\",\n      \"Checking accessibility compliance...\",\n      \"Analyzing component animations...\",\n      \"Reviewing loading states...\",\n      \"Testing responsive layouts...\",\n      \"Optimizing user interactions...\",\n      \"Validating color contrast...\",\n      \"Checking motion preferences...\",\n      \"Finalizing UI improvements...\",\n    ],\n  },\n];\n\nconst LoadingAnimation = ({ progress }: { progress: number }) => (\n  <div className=\"relative h-6 w-6\">\n    <svg\n      aria-label={`Loading progress: ${Math.round(progress)}%`}\n      className=\"h-full w-full\"\n      fill=\"none\"\n      viewBox=\"0 0 240 240\"\n      xmlns=\"http://www.w3.org/2000/svg\"\n    >\n      <title>Loading Progress Indicator</title>\n\n      <defs>\n        <mask id=\"progress-mask\">\n          <rect fill=\"black\" height=\"240\" width=\"240\" />\n          <circle\n            cx=\"120\"\n            cy=\"120\"\n            fill=\"white\"\n            r=\"120\"\n            strokeDasharray={`${(progress / 100) * 754}, 754`}\n            transform=\"rotate(-90 120 120)\"\n          />\n        </mask>\n      </defs>\n\n      <style>\n        {`\n                    @keyframes rotate-cw {\n                        from { transform: rotate(0deg); }\n                        to { transform: rotate(360deg); }\n                    }\n                    @keyframes rotate-ccw {\n                        from { transform: rotate(360deg); }\n                        to { transform: rotate(0deg); }\n                    }\n                    .g-spin circle {\n                        transform-origin: 120px 120px;\n                    }\n                    .g-spin circle:nth-child(1) { animation: rotate-cw 8s linear infinite; }\n                    .g-spin circle:nth-child(2) { animation: rotate-ccw 8s linear infinite; }\n                    .g-spin circle:nth-child(3) { animation: rotate-cw 8s linear infinite; }\n                    .g-spin circle:nth-child(4) { animation: rotate-ccw 8s linear infinite; }\n                    .g-spin circle:nth-child(5) { animation: rotate-cw 8s linear infinite; }\n                    .g-spin circle:nth-child(6) { animation: rotate-ccw 8s linear infinite; }\n\n                    .g-spin circle:nth-child(2n) { animation-delay: 0.2s; }\n                    .g-spin circle:nth-child(3n) { animation-delay: 0.3s; }\n                `}\n      </style>\n\n      <g\n        className=\"g-spin\"\n        mask=\"url(#progress-mask)\"\n        strokeDasharray=\"18% 40%\"\n        strokeWidth=\"16\"\n      >\n        <circle cx=\"120\" cy=\"120\" opacity=\"0.95\" r=\"150\" stroke=\"#FF2E7E\" />\n        <circle cx=\"120\" cy=\"120\" opacity=\"0.95\" r=\"130\" stroke=\"#00E5FF\" />\n        <circle cx=\"120\" cy=\"120\" opacity=\"0.95\" r=\"110\" stroke=\"#4ADE80\" />\n        <circle cx=\"120\" cy=\"120\" opacity=\"0.95\" r=\"90\" stroke=\"#FFA726\" />\n        <circle cx=\"120\" cy=\"120\" opacity=\"0.95\" r=\"70\" stroke=\"#FFEB3B\" />\n        <circle cx=\"120\" cy=\"120\" opacity=\"0.95\" r=\"50\" stroke=\"#FF4081\" />\n      </g>\n    </svg>\n  </div>\n);\n\nexport default function AILoadingState() {\n  const [sequenceIndex, setSequenceIndex] = useState(0);\n  const [visibleLines, setVisibleLines] = useState<\n    Array<{ text: string; number: number }>\n  >([]);\n  const [scrollPosition, setScrollPosition] = useState(0);\n  const codeContainerRef = useRef<HTMLDivElement>(null);\n  const rootRef = useRef<HTMLDivElement>(null);\n  const [isVisible, setIsVisible] = useState(true);\n  const lineHeight = 28;\n\n  // Only animate while on screen — an off-screen interval keeps waking the\n  // main thread and re-rendering for a component nobody can see.\n  useEffect(() => {\n    const element = rootRef.current;\n    if (!element) {\n      return;\n    }\n\n    const observer = new IntersectionObserver(\n      ([entry]) => setIsVisible(entry.isIntersecting),\n      { rootMargin: \"100px\" }\n    );\n    observer.observe(element);\n\n    return () => observer.disconnect();\n  }, []);\n\n  const currentSequence = TASK_SEQUENCES[sequenceIndex];\n  const totalLines = currentSequence.lines.length;\n\n  useEffect(() => {\n    const initialLines = [];\n    for (let i = 0; i < Math.min(5, totalLines); i++) {\n      initialLines.push({\n        text: currentSequence.lines[i],\n        number: i + 1,\n      });\n    }\n    setVisibleLines(initialLines);\n    setScrollPosition(0);\n  }, [sequenceIndex, currentSequence.lines, totalLines]);\n\n  // Handle line advancement\n  useEffect(() => {\n    if (!isVisible) {\n      return;\n    }\n\n    const advanceTimer = setInterval(() => {\n      // Get the current first visible line index\n      const firstVisibleLineIndex = Math.floor(scrollPosition / lineHeight);\n      const nextLineIndex = (firstVisibleLineIndex + 3) % totalLines;\n\n      // If we're about to wrap around, move to next sequence\n      if (nextLineIndex < firstVisibleLineIndex && nextLineIndex !== 0) {\n        setSequenceIndex(\n          (prevIndex) => (prevIndex + 1) % TASK_SEQUENCES.length\n        );\n        return;\n      }\n\n      // Add the next line if needed\n      if (nextLineIndex >= visibleLines.length && nextLineIndex < totalLines) {\n        setVisibleLines((prevLines) => [\n          ...prevLines,\n          {\n            text: currentSequence.lines[nextLineIndex],\n            number: nextLineIndex + 1,\n          },\n        ]);\n      }\n\n      // Scroll to the next line\n      setScrollPosition((prevPosition) => prevPosition + lineHeight);\n    }, 2000); // Slightly slower than the example for better readability\n\n    return () => clearInterval(advanceTimer);\n  }, [\n    isVisible,\n    scrollPosition,\n    visibleLines,\n    totalLines,\n    sequenceIndex,\n    currentSequence.lines,\n    lineHeight,\n  ]);\n\n  // Apply scroll position\n  useEffect(() => {\n    if (codeContainerRef.current) {\n      codeContainerRef.current.scrollTop = scrollPosition;\n    }\n  }, [scrollPosition]);\n\n  return (\n    <div\n      className=\"flex min-h-full w-full items-center justify-center\"\n      ref={rootRef}\n    >\n      <div className=\"w-auto space-y-4\">\n        <div className=\"ml-2 flex items-center space-x-2 font-medium text-gray-600 dark:text-gray-300\">\n          <LoadingAnimation\n            progress={(sequenceIndex / TASK_SEQUENCES.length) * 100}\n          />\n          <span className=\"text-sm\">{currentSequence.status}...</span>\n        </div>\n\n        <div className=\"relative\">\n          <div\n            className=\"relative h-[84px] w-full overflow-hidden rounded-lg font-mono text-xs\"\n            ref={codeContainerRef}\n            style={{ scrollBehavior: \"smooth\" }}\n          >\n            <div>\n              {visibleLines.map((line, index) => (\n                <div\n                  className=\"flex h-[28px] items-center px-2\"\n                  key={`${line.number}-${line.text}`}\n                >\n                  <div className=\"w-6 select-none pr-3 text-right text-gray-400 dark:text-gray-500\">\n                    {line.number}\n                  </div>\n\n                  <div className=\"ml-1 flex-1 text-gray-800 dark:text-gray-200\">\n                    {line.text}\n                  </div>\n                </div>\n              ))}\n            </div>\n          </div>\n\n          <div\n            className=\"pointer-events-none absolute top-0 right-0 bottom-0 left-0 rounded-lg from-white/90 via-white/50 to-transparent dark:from-black/90 dark:via-black/50 dark:to-transparent\"\n            style={{\n              background:\n                \"linear-gradient(to bottom, var(--tw-gradient-from) 0%, var(--tw-gradient-via) 30%, var(--tw-gradient-to) 100%)\",\n            }}\n          />\n        </div>\n      </div>\n    </div>\n  );\n}\n",
      "path": "/components/kokonutui/ai-loading.tsx",
      "target": "components/kokonutui/ai-loading.tsx"
    }
  ]
}