{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "loading-state",
  "title": "Loading State",
  "description": "A pixel-grid loader with a shimmering label and live elapsed timer.",
  "files": [
    {
      "path": "components/spectrumui/blocks/ai-assistants/loading-state.tsx",
      "content": "\"use client\";\n\nimport { useEffect, useState } from \"react\";\nimport { cn } from \"@/lib/utils\";\n\nconst KEYFRAMES = `\n@keyframes su-pixel-on { 0%, 100% { opacity: 0.15 } 40% { opacity: 1 } }\n@keyframes su-shimmer-text { 0% { background-position: 200% 0 } 100% { background-position: -200% 0 } }\n`;\n\nconst chevron = Array.from({ length: 9 }, (_, i) => {\n  const r = Math.floor(i / 3),\n    c = i % 3;\n  return (c + Math.abs(r - 1)) * 90;\n});\n\nconst ORBIT_ORDER = [0, 1, 2, 5, 8, 7, 6, 3];\nconst orbit = Array.from({ length: 9 }, (_, i) => {\n  const k = ORBIT_ORDER.indexOf(i);\n  return k === -1 ? null : k * 110;\n});\n\nexport type LoadingStateVariant = \"Drive\" | \"Dots\" | \"Orbit\";\n\nconst PATTERNS: Record<\n  LoadingStateVariant,\n  { delays: (number | null)[]; dur: number; round: boolean }\n> = {\n  Drive: { delays: chevron, dur: 650, round: false },\n  Dots: { delays: chevron, dur: 650, round: true },\n  Orbit: { delays: orbit, dur: 950, round: false },\n};\n\nfunction useElapsed() {\n  const [ds, setDs] = useState(0);\n  useEffect(() => {\n    const t = setInterval(() => setDs((d) => d + 1), 100);\n    return () => clearInterval(t);\n  }, []);\n  const total = ds / 10;\n  if (total < 60) return `${total.toFixed(1)}s`;\n  return `${Math.floor(total / 60)}m ${(total % 60).toFixed(1)}s`;\n}\n\nexport interface LoadingStateProps {\n  label?: string;\n  variant?: LoadingStateVariant;\n  className?: string;\n}\n\nexport function LoadingState({\n  label = \"Churning\",\n  variant = \"Drive\",\n  className,\n}: LoadingStateProps) {\n  const elapsed = useElapsed();\n  const { delays, dur, round } = PATTERNS[variant] ?? PATTERNS.Drive;\n\n  return (\n    <div\n      className={cn(\n        \"flex w-fit items-center gap-2.5 text-neutral-900 dark:text-neutral-100\",\n        className,\n      )}\n    >\n      <style dangerouslySetInnerHTML={{ __html: KEYFRAMES }} />\n\n      <span aria-hidden className=\"grid grid-cols-[repeat(3,4px)] gap-[1.5px]\">\n        {delays.map((d, i) => (\n          <span\n            key={i}\n            className={cn(\n              \"size-[4px] bg-current motion-reduce:!animate-none\",\n              round ? \"rounded-full\" : \"rounded-[1px]\",\n            )}\n            style={{\n              opacity: d === null ? 0.07 : 0.15,\n              animation:\n                d === null\n                  ? \"none\"\n                  : `su-pixel-on ${dur}ms ease-in-out ${d}ms infinite`,\n            }}\n          />\n        ))}\n      </span>\n\n      <span\n        className=\"bg-clip-text text-[13px] font-medium text-transparent motion-reduce:!animate-none motion-reduce:!text-current\"\n        style={{\n          backgroundImage:\n            \"linear-gradient(90deg, #a3a3a3 35%, currentColor 50%, #a3a3a3 65%)\",\n          backgroundSize: \"200% 100%\",\n          animation: \"su-shimmer-text 1.4s linear infinite\",\n        }}\n      >\n        {label}\n      </span>\n\n      <span\n        role=\"timer\"\n        aria-label={`Elapsed time ${elapsed}`}\n        className=\"font-mono text-[12px] tabular-nums text-neutral-400 dark:text-neutral-500\"\n      >\n        {elapsed}\n      </span>\n    </div>\n  );\n}\n\nexport default LoadingState;\n",
      "type": "registry:block",
      "target": "components/spectrumui/blocks/ai-assistants/loading-state.tsx"
    }
  ],
  "type": "registry:block"
}
