{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "predictive-text-base",
  "type": "registry:component",
  "title": "Predictive Text (base)",
  "description": "Theme-ready base variant of Suggests next words in real time while users type naturally..",
  "dependencies": [
    "lucide-react",
    "motion"
  ],
  "files": [
    {
      "path": "components/watermelon/predictive-text.tsx",
      "type": "registry:component",
      "content": "'use client';\n\nimport React, {\n  useState,\n  useEffect,\n  useRef,\n  useCallback,\n  useMemo,\n} from 'react';\nimport { motion, AnimatePresence } from 'motion/react';\nimport { ArrowUp, ImageIcon, Mic } from 'lucide-react';\n\ninterface PredictiveInputProps {\n  dictionary?: string[];\n  placeholder?: string;\n  onSend?: (text: string) => void;\n  className?: string;\n}\n\nconst DEFAULT_WORDS = [\n  'what',\n  'whatever',\n  \"what's\",\n  'bright',\n  'brighter',\n  'brigade',\n  'sunny',\n  'sunset',\n  'sun',\n  'day',\n  'dance',\n  'data',\n  'a',\n  'an',\n  'any',\n];\n\nexport const PredictiveText: React.FC<PredictiveInputProps> = ({\n  dictionary = DEFAULT_WORDS,\n  placeholder = 'Write a message',\n  onSend,\n  className = '',\n}) => {\n  const [text, setText] = useState('');\n\n  const [activeSuggestionIndex, setActiveSuggestionIndex] =\n    useState<number>(-1);\n  const [wordFrequency, setWordFrequency] = useState<Record<string, number>>(\n    {},\n  );\n  const inputRef = useRef<HTMLInputElement>(null);\n\n  const enrichedDictionary = useCallback(() => {\n    const freqWords = Object.keys(wordFrequency).sort(\n      (a, b) => wordFrequency[b] - wordFrequency[a],\n    );\n    return Array.from(new Set([...freqWords, ...dictionary]));\n  }, [dictionary, wordFrequency]);\n\n  const lastWord = useMemo(() => {\n    const words = text.split(/\\s+/);\n    return words[words.length - 1].toLowerCase();\n  }, [text]);\n\n  const suggestions = useMemo(() => {\n    if (lastWord.length > 0) {\n      const dict = enrichedDictionary();\n      return dict\n        .filter(\n          (word) =>\n            word.toLowerCase().startsWith(lastWord) &&\n            word.toLowerCase() !== lastWord,\n        )\n        .slice(0, 3);\n    }\n    return [];\n  }, [lastWord, enrichedDictionary]);\n\n  useEffect(() => {\n    requestAnimationFrame(() => setActiveSuggestionIndex(-1));\n  }, [text]);\n\n  const applySuggestion = useCallback(\n    (suggestion: string) => {\n      const words = text.split(/\\s+/);\n      words[words.length - 1] = suggestion;\n      const newText = words.join(' ') + ' ';\n      setText(newText);\n      setActiveSuggestionIndex(-1);\n      inputRef.current?.focus();\n    },\n    [text],\n  );\n\n  const handleSend = useCallback(() => {\n    if (!text.trim()) return;\n\n    const usedWords = text.trim().toLowerCase().split(/\\s+/);\n    setWordFrequency((prev) => {\n      const updated = { ...prev };\n      usedWords.forEach((w) => {\n        updated[w] = (updated[w] ?? 0) + 1;\n      });\n      return updated;\n    });\n\n    onSend?.(text);\n    setText('');\n  }, [text, onSend]);\n\n  const handleKeyDown = (e: React.KeyboardEvent<HTMLInputElement>) => {\n    switch (e.key) {\n      case 'Enter': {\n        if (activeSuggestionIndex >= 0 && suggestions[activeSuggestionIndex]) {\n          e.preventDefault();\n          applySuggestion(suggestions[activeSuggestionIndex]);\n        } else {\n          e.preventDefault();\n          handleSend();\n        }\n        break;\n      }\n\n      case 'Tab': {\n        if (suggestions.length > 0) {\n          e.preventDefault();\n          const next = (activeSuggestionIndex + 1) % suggestions.length;\n          setActiveSuggestionIndex(next);\n        }\n        break;\n      }\n\n      case 'ArrowRight': {\n        const input = inputRef.current;\n        if (\n          suggestions.length > 0 &&\n          input &&\n          input.selectionStart === text.length\n        ) {\n          e.preventDefault();\n          applySuggestion(suggestions[0]);\n        }\n        break;\n      }\n\n      case 'Escape': {\n        if (text.length > 0) {\n          e.preventDefault();\n          setText('');\n        }\n        break;\n      }\n\n      default:\n        break;\n    }\n  };\n\n  return (\n    <div\n      className={`theme-injected flex w-full flex-col items-center justify-center p-4 antialiased select-none sm:p-6 ${className}`}\n    >\n      <div className=\"relative mb-10 flex w-full max-w-[95%] flex-col items-start sm:mb-20 sm:max-w-md\">\n        <div className=\"mb-3 flex h-10 w-full items-center justify-start sm:h-12\">\n          <AnimatePresence>\n            {suggestions.length > 0 && (\n              <motion.div\n                initial={{ opacity: 0, y: 10, scale: 0.95 }}\n                animate={{ opacity: 1, y: 0, scale: 1 }}\n                exit={{ opacity: 0, y: 10, scale: 0.95 }}\n                transition={{ type: 'spring', stiffness: 500, damping: 30 }}\n                className=\"bg-card border-border flex items-center gap-0.5 rounded-lg border px-1 py-1 shadow-sm transition-colors\"\n              >\n                {suggestions.map((word, i) => (\n                  <button\n                    key={word}\n                    onClick={() => applySuggestion(word)}\n                    className={`px-3 py-1 text-xs font-bold whitespace-nowrap transition-colors sm:px-4 sm:text-sm ${\n                      i === activeSuggestionIndex\n                        ? 'text-primary'\n                        : 'text-muted-foreground hover:text-foreground'\n                    } ${i !== 0 ? 'border-border border-l pl-3 sm:pl-4' : ''} `}\n                  >\n                    {word}\n                  </button>\n                ))}\n              </motion.div>\n            )}\n          </AnimatePresence>\n        </div>\n\n        <div className=\"group relative w-full\">\n          <input\n            ref={inputRef}\n            type=\"text\"\n            value={text}\n            onChange={(e) => setText(e.target.value)}\n            onKeyDown={handleKeyDown}\n            placeholder={placeholder}\n            className=\"bg-card text-foreground border border-border placeholder:text-muted-foreground focus:ring-ring w-full rounded-lg border-none px-5 py-3.5 pr-20 text-sm font-bold tracking-wide shadow-sm transition-all outline-none focus:ring-1 sm:px-6 sm:py-4 sm:pr-24 sm:text-base\"\n          />\n\n          <div className=\"absolute top-1/2 right-2 flex -translate-y-1/2 items-center gap-2 sm:right-3 sm:gap-3\">\n            <AnimatePresence mode=\"wait\">\n              {text.length > 0 ? (\n                <motion.button\n                  key=\"send-btn\"\n                  initial={{ opacity: 0, scale: 0.8 }}\n                  animate={{ opacity: 1, scale: 1 }}\n                  exit={{ opacity: 0, scale: 0.8 }}\n                  onClick={handleSend}\n                  className=\"bg-primary text-primary-foreground flex h-8 w-8 items-center justify-center rounded-lg shadow-md transition-all active:scale-90 sm:h-10 sm:w-10\"\n                >\n                  <ArrowUp size={18} strokeWidth={3} />\n                </motion.button>\n              ) : (\n                <motion.div\n                  key=\"placeholder-icons\"\n                  initial={{ opacity: 0 }}\n                  animate={{ opacity: 1 }}\n                  exit={{ opacity: 0 }}\n                  className=\"text-muted-foreground flex items-center gap-3 pr-1 sm:gap-4 sm:pr-2\"\n                >\n                  <ImageIcon\n                    size={20}\n                    className=\"hover:text-foreground cursor-pointer transition-colors\"\n                  />\n                  <Mic\n                    size={20}\n                    className=\"hover:text-foreground cursor-pointer transition-colors\"\n                  />\n                </motion.div>\n              )}\n            </AnimatePresence>\n          </div>\n        </div>\n      </div>\n    </div>\n  );\n};\n"
    }
  ]
}
