{ "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
\n \n Loading Progress Indicator\n\n \n \n \n \n \n \n\n \n\n \n \n \n \n \n \n \n \n \n
\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(null);\n const rootRef = useRef(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 \n
\n
\n \n {currentSequence.status}...\n
\n\n
\n \n
\n {visibleLines.map((line, index) => (\n \n
\n {line.number}\n
\n\n
\n {line.text}\n
\n
\n ))}\n
\n
\n\n \n \n \n \n );\n}\n", "path": "/components/kokonutui/ai-loading.tsx", "target": "components/kokonutui/ai-loading.tsx" } ] }