{
	"$schema": "https://ui.shadcn.com/schema/registry-item.json",
	"name": "TrueFocus-TS-CSS",
	"title": "TrueFocus",
	"description": "Applies dynamic blur / clarity based over a series of words in order.",
	"type": "registry:component",
	"files": [
		{
			"type": "registry:file",
			"path": "TrueFocus.css",
			"target": "@components/TrueFocus.css",
			"content": ".focus-container {\n  position: relative;\n  display: flex;\n  gap: 1em;\n  justify-content: center;\n  align-items: center;\n  flex-wrap: wrap;\n  outline: none;\n  user-select: none;\n}\n\n.focus-word {\n  position: relative;\n  font-size: 3rem;\n  font-weight: 900;\n  cursor: pointer;\n  transition:\n    filter 0.3s ease,\n    color 0.3s ease;\n  outline: none;\n  user-select: none;\n}\n\n.focus-word.active {\n  filter: blur(0);\n}\n\n.focus-frame {\n  position: absolute;\n  top: 0;\n  left: 0;\n  pointer-events: none;\n  box-sizing: content-box;\n  border: none;\n}\n\n.corner {\n  position: absolute;\n  width: 1rem;\n  height: 1rem;\n  border: 3px solid var(--border-color, #fff);\n  filter: drop-shadow(0px 0px 4px var(--border-color, #fff));\n  border-radius: 3px;\n  transition: none;\n}\n\n.top-left {\n  top: -10px;\n  left: -10px;\n  border-right: none;\n  border-bottom: none;\n}\n\n.top-right {\n  top: -10px;\n  right: -10px;\n  border-left: none;\n  border-bottom: none;\n}\n\n.bottom-left {\n  bottom: -10px;\n  left: -10px;\n  border-right: none;\n  border-top: none;\n}\n\n.bottom-right {\n  bottom: -10px;\n  right: -10px;\n  border-left: none;\n  border-top: none;\n}\n"
		},
		{
			"type": "registry:component",
			"path": "TrueFocus.tsx",
			"content": "import { useEffect, useRef, useState, type RefObject } from 'react';\nimport { motion } from 'motion/react';\nimport './TrueFocus.css';\n\ninterface TrueFocusProps {\n  sentence?: string;\n  separator?: string;\n  manualMode?: boolean;\n  blurAmount?: number;\n  borderColor?: string;\n  glowColor?: string;\n  animationDuration?: number;\n  pauseBetweenAnimations?: number;\n}\n\ninterface FocusRect {\n  x: number;\n  y: number;\n  width: number;\n  height: number;\n}\n\nconst TrueFocus: React.FC<TrueFocusProps> = ({\n  sentence = 'True Focus',\n  separator = ' ',\n  manualMode = false,\n  blurAmount = 5,\n  borderColor = 'green',\n  glowColor = 'rgba(0, 255, 0, 0.6)',\n  animationDuration = 0.5,\n  pauseBetweenAnimations = 1\n}) => {\n  const words = sentence.split(separator);\n  const [currentIndex, setCurrentIndex] = useState<number>(0);\n  const [lastActiveIndex, setLastActiveIndex] = useState<number | null>(null);\n  const containerRef = useRef<HTMLDivElement>(null);\n  const wordRefs: React.MutableRefObject<(HTMLSpanElement | null)[]> = useRef([]);\n  const [focusRect, setFocusRect] = useState<FocusRect>({\n    x: 0,\n    y: 0,\n    width: 0,\n    height: 0\n  });\n\n  useEffect(() => {\n    if (!manualMode) {\n      const interval = setInterval(\n        () => {\n          setCurrentIndex(prev => (prev + 1) % words.length);\n        },\n        (animationDuration + pauseBetweenAnimations) * 1000\n      );\n\n      return () => clearInterval(interval);\n    }\n  }, [manualMode, animationDuration, pauseBetweenAnimations, words.length]);\n\n  useEffect(() => {\n    if (currentIndex === null || currentIndex === -1) return;\n\n    if (!wordRefs.current[currentIndex] || !containerRef.current) return;\n\n    const parentRect = containerRef.current.getBoundingClientRect();\n    const activeRect = wordRefs.current[currentIndex]!.getBoundingClientRect();\n\n    setFocusRect({\n      x: activeRect.left - parentRect.left,\n      y: activeRect.top - parentRect.top,\n      width: activeRect.width,\n      height: activeRect.height\n    });\n  }, [currentIndex, words.length]);\n\n  const handleMouseEnter = (index: number) => {\n    if (manualMode) {\n      setLastActiveIndex(index);\n      setCurrentIndex(index);\n    }\n  };\n\n  const handleMouseLeave = () => {\n    if (manualMode) {\n      setCurrentIndex(lastActiveIndex ?? 0);\n    }\n  };\n\n  return (\n    <div className=\"focus-container\" ref={containerRef}>\n      {words.map((word, index) => {\n        const isActive = index === currentIndex;\n        return (\n          <span\n            key={index}\n            ref={el => {\n              if (el) {\n                wordRefs.current[index] = el;\n              }\n            }}\n            className={`focus-word ${manualMode ? 'manual' : ''} ${isActive && !manualMode ? 'active' : ''}`}\n            style={\n              {\n                filter: manualMode\n                  ? isActive\n                    ? `blur(0px)`\n                    : `blur(${blurAmount}px)`\n                  : isActive\n                    ? `blur(0px)`\n                    : `blur(${blurAmount}px)`,\n                transition: `filter ${animationDuration}s ease`,\n                '--border-color': borderColor,\n                '--glow-color': glowColor\n              } as React.CSSProperties\n            }\n            onMouseEnter={() => handleMouseEnter(index)}\n            onMouseLeave={handleMouseLeave}\n          >\n            {word}\n          </span>\n        );\n      })}\n\n      <motion.div\n        className=\"focus-frame\"\n        animate={{\n          x: focusRect.x,\n          y: focusRect.y,\n          width: focusRect.width,\n          height: focusRect.height,\n          opacity: currentIndex >= 0 ? 1 : 0\n        }}\n        transition={{\n          duration: animationDuration\n        }}\n        style={\n          {\n            '--border-color': borderColor,\n            '--glow-color': glowColor\n          } as React.CSSProperties\n        }\n      >\n        <span className=\"corner top-left\"></span>\n        <span className=\"corner top-right\"></span>\n        <span className=\"corner bottom-left\"></span>\n        <span className=\"corner bottom-right\"></span>\n      </motion.div>\n    </div>\n  );\n};\n\nexport default TrueFocus;\n"
		}
	],
	"registryDependencies": [],
	"dependencies": [
		"motion@^12.23.12"
	]
}