Spaces:
Sleeping
Sleeping
import { useState, useEffect } from "react"; | |
/** | |
* Enhanced loading component with visual feedback | |
* | |
* Features: | |
* - Progress indicator with percentage display | |
* - Animated loading spinner | |
* - Progress bar visualization | |
* - Contextual status messages | |
* - Animated ellipsis for active feedback | |
* | |
* @param progress - Current progress percentage (0-100) | |
* @param showDetails - Whether to show detailed status messages | |
*/ | |
function Loading({ progress = 0, showDetails = false }: { progress?: number; showDetails?: boolean }) { | |
const [dots, setDots] = useState(""); | |
// Animated dots for the loading text (provides visual feedback even when progress is unknown) | |
useEffect(() => { | |
const interval = setInterval(() => { | |
setDots(prev => (prev.length >= 3 ? "" : prev + ".")); | |
}, 400); | |
return () => clearInterval(interval); | |
}, []); | |
// Get appropriate status message based on current progress | |
const getStatusMessage = () => { | |
if (progress < 25) return "Analyzing request..."; | |
if (progress < 50) return "Generating content..."; | |
if (progress < 75) return "Processing changes..."; | |
if (progress < 100) return "Finalizing..."; | |
return "Complete!"; | |
}; | |
return ( | |
<div className="absolute left-0 top-0 h-full w-full flex flex-col items-center justify-center bg-gray-950/90 backdrop-blur-sm z-20"> | |
<div className="relative"> | |
{/* Progress circle with animated spin */} | |
<svg | |
className="size-16 animate-spin text-purple-400" | |
xmlns="http://www.w3.org/2000/svg" | |
fill="none" | |
viewBox="0 0 24 24" | |
> | |
<circle | |
className="opacity-25" | |
cx="12" | |
cy="12" | |
r="10" | |
stroke="currentColor" | |
strokeWidth="4" | |
></circle> | |
<path | |
className="opacity-75" | |
fill="currentColor" | |
d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z" | |
></path> | |
</svg> | |
{/* Progress percentage in center - only shown when progress is known */} | |
{progress > 0 && ( | |
<div className="absolute inset-0 flex items-center justify-center"> | |
<span className="text-sm font-bold text-white">{progress}%</span> | |
</div> | |
)} | |
</div> | |
<div className="mt-4 flex flex-col items-center"> | |
{/* Main loading message with animated dots */} | |
<p className="text-lg font-bold text-purple-200 mb-1">AI is thinking{dots}</p> | |
{/* Horizontal progress bar - only shown when progress is known */} | |
{progress > 0 && ( | |
<div className="w-48 h-1.5 bg-gray-700 rounded-full overflow-hidden mt-2"> | |
<div | |
className="h-full bg-gradient-to-r from-purple-400 to-pink-500 transition-all duration-300 ease-out" | |
style={{ width: `${progress}%` }} | |
/> | |
</div> | |
)} | |
{/* Optional detailed status message - shows processing stage */} | |
{showDetails && ( | |
<p className="mt-2 text-xs text-purple-300/80 max-w-xs text-center animate-pulse"> | |
{getStatusMessage()} | |
</p> | |
)} | |
</div> | |
</div> | |
); | |
} | |
export default Loading; | |