{
  "name": "fluid-action-hub",
  "type": "registry:ui",
  "title": "Fluid Action Hub",
  "description": "Morphing expandable action hub with circular scroll-progress indicator, theme switch, quick share, and link copy.",
  "dependencies": [
    "framer-motion",
    "lucide-react"
  ],
  "files": [
    {
      "path": "registry/navigation/fluid-action-hub/fluid-action-hub.tsx",
      "type": "registry:ui",
      "target": "components/ui/fluid-action-hub.tsx",
      "content": "import React, { useState, useEffect, useRef } from \"react\";\nimport { motion, AnimatePresence } from \"framer-motion\";\nimport { \n  Settings2, \n  X, \n  Sun, \n  Moon, \n  Share2, \n  Link2, \n  Compass, \n  ArrowUp, \n  Check,\n  Monitor\n} from \"lucide-react\";\nimport { cn } from \"@/lib/utils\";\n\nexport interface FluidActionHubProps {\n  position?: \"bottom-right\" | \"bottom-left\" | \"top-right\" | \"top-left\" | \"inline\";\n  className?: string;\n  onThemeToggle?: () => void;\n  isDark?: boolean;\n}\n\nexport const FluidActionHub = ({\n  position = \"inline\",\n  className,\n  onThemeToggle,\n  isDark = false,\n}: FluidActionHubProps) => {\n  const [isOpen, setIsOpen] = useState(false);\n  const [scrollProgress, setScrollProgress] = useState(42);\n  const [copied, setCopied] = useState(false);\n  const containerRef = useRef<HTMLDivElement>(null);\n\n  useEffect(() => {\n    const handleScroll = () => {\n      const totalScroll = document.documentElement.scrollHeight - window.innerHeight;\n      if (totalScroll > 0) {\n        const progress = (window.scrollY / totalScroll) * 100;\n        setScrollProgress(Math.min(progress, 100));\n      }\n    };\n\n    window.addEventListener(\"scroll\", handleScroll);\n    return () => window.removeEventListener(\"scroll\", handleScroll);\n  }, []);\n\n  useEffect(() => {\n    const handleClickOutside = (event: MouseEvent) => {\n      if (containerRef.current && !containerRef.current.contains(event.target as Node)) {\n        setIsOpen(false);\n      }\n    };\n    if (isOpen) {\n      document.addEventListener(\"mousedown\", handleClickOutside);\n    }\n    return () => document.removeEventListener(\"mousedown\", handleClickOutside);\n  }, [isOpen]);\n\n  const handleCopyLink = () => {\n    if (typeof window !== \"undefined\") {\n      navigator.clipboard?.writeText(window.location.href);\n      setCopied(true);\n      setTimeout(() => setCopied(false), 2000);\n    }\n  };\n\n  const handleScrollToTop = () => {\n    if (typeof window !== \"undefined\") {\n      window.scrollTo({ top: 0, behavior: \"smooth\" });\n    }\n  };\n\n  const positionClasses = {\n    \"bottom-right\": \"fixed bottom-6 right-6 z-50\",\n    \"bottom-left\": \"fixed bottom-6 left-6 z-50\",\n    \"top-right\": \"fixed top-6 right-6 z-50\",\n    \"top-left\": \"fixed top-6 left-6 z-50\",\n    inline: \"relative z-30\",\n  };\n\n  const radius = 24;\n  const stroke = 2.5;\n  const normalizedRadius = radius - stroke * 2;\n  const circumference = normalizedRadius * 2 * Math.PI;\n  const strokeDashoffset = circumference - (scrollProgress / 100) * circumference;\n\n  const containerVariants = {\n    hidden: { opacity: 0 },\n    visible: {\n      opacity: 1,\n      transition: {\n        staggerChildren: 0.04,\n        delayChildren: 0.04,\n      },\n    },\n  };\n\n  const itemVariants = {\n    hidden: { opacity: 0, y: 8 },\n    visible: { opacity: 1, y: 0, transition: { type: \"spring\" as const, stiffness: 350, damping: 25 } },\n  };\n\n  return (\n    <div \n      ref={containerRef}\n      className={cn(\"font-sans select-none\", positionClasses[position], className)}\n    >\n      <motion.div\n        layout\n        initial={{ borderRadius: 28 }}\n        animate={{ \n          width: isOpen ? 290 : 52,\n          height: isOpen ? 340 : 52,\n          borderRadius: isOpen ? 20 : 26\n        }}\n        transition={{ \n          type: \"spring\", \n          stiffness: 380, \n          damping: 28,\n        }}\n        className=\"bg-white/90 dark:bg-zinc-900/90 border border-zinc-200/80 dark:border-zinc-800/80 backdrop-blur-xl shadow-xl shadow-zinc-500/10 dark:shadow-black/60 overflow-hidden flex flex-col justify-start\"\n      >\n        {/* Toggle Button wrapper */}\n        <div className=\"relative w-[52px] h-[52px] flex items-center justify-center shrink-0\">\n          <motion.button\n            onClick={() => setIsOpen(!isOpen)}\n            className=\"w-[52px] h-[52px] flex items-center justify-center relative focus:outline-none z-20 group cursor-pointer\"\n            whileHover={{ scale: 1.05 }}\n            whileTap={{ scale: 0.95 }}\n            aria-label=\"Toggle Quick Panel\"\n          >\n            <svg className=\"w-12 h-12 absolute -rotate-90 pointer-events-none\">\n              <circle\n                stroke=\"currentColor\"\n                fill=\"transparent\"\n                strokeWidth={stroke}\n                strokeDasharray={`${circumference} ${circumference}`}\n                style={{ strokeDashoffset }}\n                strokeLinecap=\"round\"\n                r={normalizedRadius}\n                cx=\"24\"\n                cy=\"24\"\n                className=\"text-indigo-600 dark:text-indigo-400 transition-all duration-150\"\n              />\n            </svg>\n\n            <AnimatePresence mode=\"wait\">\n              {!isOpen ? (\n                <motion.div\n                  key=\"settings\"\n                  initial={{ rotate: -90, opacity: 0 }}\n                  animate={{ rotate: 0, opacity: 1 }}\n                  exit={{ rotate: 90, opacity: 0 }}\n                  transition={{ duration: 0.15 }}\n                >\n                  <Settings2 className=\"w-4 h-4 text-zinc-800 dark:text-zinc-200\" />\n                </motion.div>\n              ) : (\n                <motion.div\n                  key=\"close\"\n                  initial={{ rotate: 90, opacity: 0 }}\n                  animate={{ rotate: 0, opacity: 1 }}\n                  exit={{ rotate: -90, opacity: 0 }}\n                  transition={{ duration: 0.15 }}\n                >\n                  <X className=\"w-4 h-4 text-zinc-800 dark:text-zinc-200\" />\n                </motion.div>\n              )}\n            </AnimatePresence>\n          </motion.button>\n        </div>\n\n        {/* Panel Content */}\n        <AnimatePresence>\n          {isOpen && (\n            <motion.div\n              variants={containerVariants}\n              initial=\"hidden\"\n              animate=\"visible\"\n              exit=\"hidden\"\n              className=\"px-4 pb-4 flex flex-col gap-3 text-left flex-1 overflow-hidden\"\n            >\n              {/* Header */}\n              <motion.div variants={itemVariants} className=\"flex flex-col gap-0.5 border-b border-zinc-200/60 dark:border-zinc-800/60 pb-2\">\n                <span className=\"text-[9px] font-mono uppercase tracking-widest text-zinc-400 dark:text-zinc-500\">\n                  Quick Access\n                </span>\n                <span className=\"text-xs font-semibold text-zinc-800 dark:text-zinc-100 flex items-center gap-1.5\">\n                  <Compass className=\"w-3.5 h-3.5 text-indigo-500\" /> System Hub\n                </span>\n              </motion.div>\n\n              {/* Progress Card */}\n              <motion.div \n                variants={itemVariants}\n                className=\"flex items-center justify-between bg-zinc-50 dark:bg-zinc-800/40 border border-zinc-200/60 dark:border-zinc-700/40 p-2.5 rounded-xl shadow-sm\"\n              >\n                <div className=\"flex flex-col\">\n                  <span className=\"text-[9px] font-bold text-zinc-400 dark:text-zinc-500 uppercase\">Scroll Read</span>\n                  <span className=\"text-sm font-bold font-mono tracking-tight text-zinc-800 dark:text-zinc-200\">\n                    {Math.round(scrollProgress)}% Scrolled\n                  </span>\n                </div>\n                <button\n                  onClick={handleScrollToTop}\n                  className=\"p-2 bg-zinc-900 text-white dark:bg-white dark:text-zinc-900 rounded-full hover:scale-105 active:scale-95 transition-all shadow-sm flex items-center justify-center cursor-pointer\"\n                  title=\"Scroll to Top\"\n                >\n                  <ArrowUp className=\"w-3 h-3\" />\n                </button>\n              </motion.div>\n\n              {/* Controls */}\n              <motion.div variants={itemVariants} className=\"grid grid-cols-2 gap-2\">\n                <button\n                  onClick={onThemeToggle}\n                  className=\"flex flex-col items-center justify-center p-2 rounded-xl border border-zinc-200 dark:border-zinc-800 hover:bg-zinc-50 dark:hover:bg-zinc-800/60 transition-all gap-1.5 text-center text-zinc-700 dark:text-zinc-300 cursor-pointer\"\n                >\n                  {isDark ? (\n                    <>\n                      <Sun className=\"w-3.5 h-3.5 text-amber-500\" />\n                      <span className=\"text-[9px] font-medium uppercase tracking-wider\">Light Mode</span>\n                    </>\n                  ) : (\n                    <>\n                      <Moon className=\"w-3.5 h-3.5 text-indigo-500\" />\n                      <span className=\"text-[9px] font-medium uppercase tracking-wider\">Dark Mode</span>\n                    </>\n                  )}\n                </button>\n\n                <button\n                  onClick={handleCopyLink}\n                  className=\"flex flex-col items-center justify-center p-2 rounded-xl border border-zinc-200 dark:border-zinc-800 hover:bg-zinc-50 dark:hover:bg-zinc-800/60 transition-all gap-1.5 text-center text-zinc-700 dark:text-zinc-300 cursor-pointer\"\n                >\n                  {copied ? (\n                    <>\n                      <Check className=\"w-3.5 h-3.5 text-emerald-500\" />\n                      <span className=\"text-[9px] font-medium uppercase tracking-wider text-emerald-600 dark:text-emerald-400\">Copied!</span>\n                    </>\n                  ) : (\n                    <>\n                      <Link2 className=\"w-3.5 h-3.5 text-zinc-600 dark:text-zinc-300\" />\n                      <span className=\"text-[9px] font-medium uppercase tracking-wider\">Copy Link</span>\n                    </>\n                  )}\n                </button>\n              </motion.div>\n\n              {/* Share links */}\n              <motion.div variants={itemVariants} className=\"flex gap-2\">\n                <a\n                  href={`https://twitter.com/intent/tweet?text=Cooki%20UI%20Components`}\n                  target=\"_blank\"\n                  rel=\"noreferrer\"\n                  className=\"flex-1 flex items-center justify-center gap-1.5 py-1.5 px-2.5 rounded-lg border border-zinc-200 dark:border-zinc-800 hover:bg-zinc-50 dark:hover:bg-zinc-800/60 transition-all text-[10px] font-medium text-zinc-700 dark:text-zinc-300\"\n                >\n                  <svg className=\"w-3 h-3 fill-current\" viewBox=\"0 0 24 24\">\n                    <path d=\"M18.244 2.25h3.308l-7.227 8.26 8.502 11.24H16.17l-5.214-6.817L4.99 21.75H1.68l7.73-8.835L1.254 2.25H8.08l4.713 6.231zm-1.161 17.52h1.833L7.084 4.126H5.117z\"/>\n                  </svg>\n                  Post\n                </a>\n                <button\n                  onClick={() => {\n                    if (navigator.share) {\n                      navigator.share({ title: document.title, url: window.location.href });\n                    }\n                  }}\n                  className=\"flex-1 flex items-center justify-center gap-1.5 py-1.5 px-2.5 rounded-lg border border-zinc-200 dark:border-zinc-800 hover:bg-zinc-50 dark:hover:bg-zinc-800/60 transition-all text-[10px] font-medium text-zinc-700 dark:text-zinc-300 cursor-pointer\"\n                >\n                  <Share2 className=\"w-3 h-3 text-zinc-600 dark:text-zinc-300\" />\n                  Share\n                </button>\n              </motion.div>\n\n              {/* Footer */}\n              <motion.div variants={itemVariants} className=\"flex justify-between items-center text-[9px] text-zinc-400 dark:text-zinc-500 border-t border-zinc-200/60 dark:border-zinc-800/60 pt-2 mt-auto\">\n                <span className=\"flex items-center gap-1 font-semibold\">\n                  <Monitor className=\"w-2.5 h-2.5 text-indigo-500\" /> Cooki UI\n                </span>\n                <span className=\"font-mono text-[9px]\">v2.1.0</span>\n              </motion.div>\n            </motion.div>\n          )}\n        </AnimatePresence>\n      </motion.div>\n    </div>\n  );\n};\n\nexport default FluidActionHub;\n"
    }
  ],
  "$schema": "https://www.cooki-ui.design/schema/registry-item.json"
}