{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "morph-search",
  "type": "registry:ui",
  "title": "Morph Search",
  "description": "Adaptive morphing search bar with cycling placeholder, spring physics, and intent card morphs.",
  "dependencies": [
    "clsx",
    "tailwind-merge",
    "motion"
  ],
  "registryDependencies": [
    "utils"
  ],
  "files": [
    {
      "path": "src/registry/ui/morph-search.tsx",
      "content": "\"use client\";\n\nimport React, {\n  useCallback,\n  useEffect,\n  useId,\n  useMemo,\n  useRef,\n  useState,\n} from \"react\";\nimport {\n  AnimatePresence,\n  motion,\n  useReducedMotion,\n  type Transition,\n} from \"motion/react\";\nimport { cn } from \"@/lib/utils\";\n\nexport interface MorphSearchProps {\n  value?: string;\n  defaultValue?: string;\n  onChange?: (value: string) => void;\n  onSubmit?: (value: string, intent?: string) => void;\n  onClear?: () => void;\n  placeholder?: string;\n  className?: string;\n  autoFocus?: boolean;\n}\n\ntype IntentType = \"poll\" | \"event\" | \"todo\" | \"timer\" | \"split\" | \"search\";\n\ninterface PollData {\n  title: string;\n  options: string[];\n}\n\ninterface EventData {\n  title: string;\n  time: string;\n  attendee: string;\n}\n\ninterface TodoData {\n  items: string[];\n}\n\ninterface TimerData {\n  seconds: number;\n  label: string;\n}\n\ninterface SplitData {\n  total: number;\n  people: number;\n  perPerson: number;\n}\n\ninterface DetectedIntent {\n  type: IntentType;\n  poll?: PollData;\n  event?: EventData;\n  todo?: TodoData;\n  timer?: TimerData;\n  split?: SplitData;\n}\n\ninterface SavedEntry {\n  id: number;\n  intent: IntentType;\n  text: string;\n  summary: string;\n  timestamp: number;\n}\n\nconst PLACEHOLDERS = [\n  \"pizza or burgers for friday?\",\n  \"dinner with priya friday 8pm\",\n  \"buy milk, eggs, bread and coffee\",\n  \"25 min focus\",\n  \"split 2400 between 3\",\n  \"explore components and hooks\",\n];\n\nconst springMorph: Transition = {\n  type: \"spring\",\n  stiffness: 440,\n  damping: 38,\n  mass: 0.8,\n};\n\nconst fadeTween: Transition = {\n  duration: 0.14,\n  ease: [0.23, 1, 0.32, 1],\n};\n\nfunction parseQuery(text: string): DetectedIntent {\n  const clean = text.trim();\n  const lower = clean.toLowerCase();\n\n  if (!clean) {\n    return { type: \"search\" };\n  }\n\n  if (\n    lower.includes(\" or \") ||\n    lower.includes(\" vs \") ||\n    (lower.endsWith(\"?\") &&\n      (lower.includes(\"pizza\") || lower.includes(\"burger\")))\n  ) {\n    const rawNoQ = clean.replace(/\\?+$/, \"\");\n    const parts = rawNoQ.split(/\\s+(?:or|vs\\.?)\\s+/i).map((s) => s.trim());\n    if (parts.length >= 2) {\n      const opts = parts.map((p) => {\n        const words = p.split(/\\s+/);\n        return words.length > 3 ? words.slice(-2).join(\" \") : p;\n      });\n      return {\n        type: \"poll\",\n        poll: {\n          title: clean.endsWith(\"?\") ? clean : `${clean}?`,\n          options: opts.slice(0, 4),\n        },\n      };\n    }\n  }\n\n  if (\n    /^(?:split|divide|share)\\b/i.test(lower) ||\n    (/\\bbetween\\s+\\d+/i.test(lower) && /\\d+/.test(lower))\n  ) {\n    const numbers = lower.match(/\\d+(?:\\.\\d+)?/g);\n    if (numbers && numbers.length >= 2) {\n      const total = parseFloat(numbers[0]);\n      const people = parseInt(numbers[1], 10) || 1;\n      const perPerson = Math.round((total / Math.max(1, people)) * 100) / 100;\n      return {\n        type: \"split\",\n        split: { total, people, perPerson },\n      };\n    }\n  }\n\n  if (\n    /\\b(?:timer|focus|break|pomodoro)\\b/i.test(lower) ||\n    /^\\d+\\s*(?:m|min|mins|minutes|s|sec|seconds|h|hr|hrs|hours)\\b/i.test(lower)\n  ) {\n    let secs = 25 * 60;\n    const match = lower.match(\n      /(\\d+)\\s*(m|min|mins|minutes|s|sec|seconds|h|hr|hrs|hours)?/i,\n    );\n    if (match) {\n      const num = parseInt(match[1], 10);\n      const unit = match[2]?.charAt(0) || \"m\";\n      if (unit === \"h\") secs = num * 3600;\n      else if (unit === \"s\") secs = num;\n      else secs = num * 60;\n    }\n    const label = lower\n      .replace(\n        /\\d+\\s*(?:m|min|mins|minutes|s|sec|seconds|h|hr|hrs|hours)?/i,\n        \"\",\n      )\n      .replace(/timer|for/gi, \"\")\n      .trim();\n    return {\n      type: \"timer\",\n      timer: {\n        seconds: Math.max(10, secs),\n        label: label\n          ? label.charAt(0).toUpperCase() + label.slice(1)\n          : \"Focus Session\",\n      },\n    };\n  }\n\n  if (\n    /^(?:buy|get|todo|tasks|groceries|shopping list)\\b/i.test(lower) ||\n    (clean.includes(\",\") && (clean.includes(\"and\") || clean.includes(\"&\")))\n  ) {\n    const sanitized = clean\n      .replace(/^(?:buy|get|todo|tasks|groceries|shopping list):?\\s*/i, \"\")\n      .replace(/\\band\\b/gi, \",\");\n    const rawItems = sanitized\n      .split(/[,;\\n]/)\n      .map((i) => i.trim())\n      .filter((i) => i.length > 0);\n    if (rawItems.length >= 2) {\n      return {\n        type: \"todo\",\n        todo: {\n          items: rawItems.map(\n            (item) => item.charAt(0).toUpperCase() + item.slice(1),\n          ),\n        },\n      };\n    }\n  }\n\n  if (\n    /\\b(?:dinner|lunch|breakfast|coffee|meeting|sync|call|party|interview|session)\\b/i.test(\n      lower,\n    ) ||\n    /\\b(?:today|tomorrow|tonight|monday|tuesday|wednesday|thursday|friday|saturday|sunday)\\b/i.test(\n      lower,\n    )\n  ) {\n    const withMatch = clean.match(/\\bwith\\s+([A-Za-z]+)/i);\n    const timeMatch = clean.match(\n      /\\b(?:at\\s+)?(\\d{1,2}(?::\\d{2})?\\s*(?:am|pm)?|\\d{1,2}\\s*(?:am|pm)|noon|midnight)\\b/i,\n    );\n    const dayMatch = clean.match(\n      /\\b(today|tomorrow|tonight|monday|tuesday|wednesday|thursday|friday|saturday|sunday)\\b/i,\n    );\n    const timeStr = [\n      dayMatch\n        ? dayMatch[1].charAt(0).toUpperCase() + dayMatch[1].slice(1)\n        : \"Upcoming\",\n      timeMatch ? timeMatch[1].toUpperCase() : \"8:00 PM\",\n    ].join(\", \");\n\n    return {\n      type: \"event\",\n      event: {\n        title: clean.charAt(0).toUpperCase() + clean.slice(1),\n        time: timeStr,\n        attendee: withMatch\n          ? withMatch[1].charAt(0).toUpperCase() + withMatch[1].slice(1)\n          : \"Team\",\n      },\n    };\n  }\n\n  return { type: \"search\" };\n}\n\nfunction formatTimer(secs: number): string {\n  const m = Math.floor(secs / 60);\n  const s = secs % 60;\n  return `${String(m).padStart(2, \"0\")}:${String(s).padStart(2, \"0\")}`;\n}\n\nexport function MorphSearch({\n  value: controlledValue,\n  defaultValue = \"\",\n  onChange,\n  onSubmit,\n  onClear,\n  placeholder,\n  className,\n  autoFocus = false,\n}: MorphSearchProps) {\n  const isControlled = controlledValue !== undefined;\n  const [internalValue, setInternalValue] = useState(defaultValue);\n  const query = isControlled ? controlledValue : internalValue;\n\n  const [isOpen, setIsOpen] = useState(false);\n  const [placeholderIndex, setPlaceholderIndex] = useState(0);\n  const [activeCategory, setActiveCategory] = useState<string>(\"all\");\n  const [savedItems, setSavedItems] = useState<SavedEntry[]>([]);\n\n  const [pollVotes, setPollVotes] = useState<Record<number, number>>({});\n  const [completedTodos, setCompletedTodos] = useState<Record<number, boolean>>(\n    {},\n  );\n  const [timerRunning, setTimerRunning] = useState(false);\n  const [timerTimeLeft, setTimerTimeLeft] = useState<number | null>(null);\n\n  const containerRef = useRef<HTMLDivElement>(null);\n  const inputRef = useRef<HTMLInputElement>(null);\n  const triggerRef = useRef<HTMLButtonElement>(null);\n  const reduceMotion = useReducedMotion();\n  const inputId = useId();\n\n  const detected = useMemo(() => parseQuery(query), [query]);\n\n  useEffect(() => {\n    if (reduceMotion) return;\n    const interval = setInterval(() => {\n      setPlaceholderIndex((prev) => (prev + 1) % PLACEHOLDERS.length);\n    }, 2800);\n    return () => clearInterval(interval);\n  }, [reduceMotion]);\n\n  const [prevQuery, setPrevQuery] = useState(query);\n  if (query !== prevQuery) {\n    setPrevQuery(query);\n    setPollVotes({});\n    setCompletedTodos({});\n    setTimerRunning(false);\n    setTimerTimeLeft(\n      detected.type === \"timer\" && detected.timer\n        ? detected.timer.seconds\n        : null,\n    );\n  }\n\n  useEffect(() => {\n    if (!timerRunning || timerTimeLeft === null || timerTimeLeft <= 0) return;\n    const interval = setInterval(() => {\n      setTimerTimeLeft((prev) => {\n        if (prev === null || prev <= 1) {\n          setTimerRunning(false);\n          return 0;\n        }\n        return prev - 1;\n      });\n    }, 1000);\n    return () => clearInterval(interval);\n  }, [timerRunning, timerTimeLeft]);\n\n  const updateQuery = useCallback(\n    (newVal: string) => {\n      if (!isControlled) {\n        setInternalValue(newVal);\n      }\n      onChange?.(newVal);\n    },\n    [isControlled, onChange],\n  );\n\n  const handleOpen = useCallback(() => {\n    setIsOpen(true);\n    requestAnimationFrame(() => {\n      inputRef.current?.focus();\n    });\n  }, []);\n\n  const handleClose = useCallback(() => {\n    setIsOpen(false);\n    inputRef.current?.blur();\n  }, []);\n\n  const closeToTrigger = useCallback(() => {\n    handleClose();\n    triggerRef.current?.focus();\n  }, [handleClose]);\n\n  const handleClear = useCallback(() => {\n    updateQuery(\"\");\n    onClear?.();\n    inputRef.current?.focus();\n  }, [updateQuery, onClear]);\n\n  const saveCurrentItem = useCallback(() => {\n    const trimmed = query.trim();\n    if (!trimmed) return;\n\n    let summary = trimmed;\n    if (detected.type === \"poll\" && detected.poll) {\n      summary = detected.poll.title;\n    } else if (detected.type === \"event\" && detected.event) {\n      summary = `${detected.event.title} · ${detected.event.time}`;\n    } else if (detected.type === \"todo\" && detected.todo) {\n      summary = detected.todo.items.join(\", \");\n    } else if (detected.type === \"timer\" && detected.timer) {\n      summary = `${detected.timer.label} · ${formatTimer(detected.timer.seconds)}`;\n    } else if (detected.type === \"split\" && detected.split) {\n      summary = `₹${detected.split.total} among ${detected.split.people} (₹${detected.split.perPerson}/person)`;\n    }\n\n    const newItem: SavedEntry = {\n      id: Date.now() + Math.random(),\n      intent: detected.type,\n      text: trimmed,\n      summary,\n      timestamp: Date.now(),\n    };\n\n    setSavedItems((prev) => [newItem, ...prev.slice(0, 8)]);\n    onSubmit?.(trimmed, detected.type);\n    updateQuery(\"\");\n    setIsOpen(false);\n  }, [query, detected, onSubmit, updateQuery]);\n\n  const reopenItem = (item: SavedEntry) => {\n    updateQuery(item.text);\n    setIsOpen(true);\n    requestAnimationFrame(() => {\n      inputRef.current?.focus();\n    });\n  };\n\n  const removeItem = (id: number) => {\n    setSavedItems((prev) => prev.filter((item) => item.id !== id));\n  };\n\n  const handleKeyDown = (e: React.KeyboardEvent<HTMLInputElement>) => {\n    if (e.key === \"Escape\") {\n      e.preventDefault();\n      if (query) {\n        handleClear();\n      } else {\n        closeToTrigger();\n      }\n    } else if (e.key === \"Enter\" && !e.nativeEvent.isComposing) {\n      e.preventDefault();\n      saveCurrentItem();\n    }\n  };\n\n  useEffect(() => {\n    const handleGlobalKeyDown = (e: KeyboardEvent) => {\n      const activeEl = document.activeElement;\n      const isInput =\n        activeEl instanceof HTMLInputElement ||\n        activeEl instanceof HTMLTextAreaElement ||\n        activeEl?.getAttribute(\"contenteditable\") === \"true\";\n\n      if (\n        (e.key === \"/\" || (e.key === \"k\" && (e.metaKey || e.ctrlKey))) &&\n        !isInput\n      ) {\n        e.preventDefault();\n        handleOpen();\n      }\n    };\n    window.addEventListener(\"keydown\", handleGlobalKeyDown);\n    return () => window.removeEventListener(\"keydown\", handleGlobalKeyDown);\n  }, [handleOpen]);\n\n  useEffect(() => {\n    const handleClickOutside = (e: MouseEvent) => {\n      if (\n        containerRef.current &&\n        !containerRef.current.contains(e.target as Node)\n      ) {\n        handleClose();\n      }\n    };\n    if (isOpen) {\n      document.addEventListener(\"mousedown\", handleClickOutside);\n    }\n    return () => document.removeEventListener(\"mousedown\", handleClickOutside);\n  }, [isOpen, handleClose]);\n\n  const voteOnPoll = (index: number) => {\n    setPollVotes((prev) => ({\n      ...prev,\n      [index]: (prev[index] || 0) + 1,\n    }));\n  };\n\n  const toggleTodo = (index: number) => {\n    setCompletedTodos((prev) => ({\n      ...prev,\n      [index]: !prev[index],\n    }));\n  };\n\n  const sampleChips = [\n    { label: \"All\", id: \"all\" },\n    { label: \"Poll\", id: \"poll\", sample: \"pizza or burgers for friday?\" },\n    { label: \"Event\", id: \"event\", sample: \"dinner with priya friday 8pm\" },\n    { label: \"Todo\", id: \"todo\", sample: \"buy milk, eggs, bread and coffee\" },\n    { label: \"Timer\", id: \"timer\", sample: \"25 min focus\" },\n    { label: \"Split\", id: \"split\", sample: \"split 2400 between 3\" },\n  ];\n\n  const searchResults = useMemo(() => {\n    if (detected.type !== \"search\" || !query.trim()) return [];\n    const pool = [\n      {\n        title: \"Navigation Components\",\n        category: \"Components\",\n        desc: \"Gooey navbar and fluid floating tab selectors\",\n      },\n      {\n        title: \"Action Controls\",\n        category: \"Inputs\",\n        desc: \"Tactile buttons, liquid toggles, and switches\",\n      },\n      {\n        title: \"Micro Physics Engines\",\n        category: \"Motion\",\n        desc: \"Spring solvers, collision dynamics, and kinetic easing\",\n      },\n      {\n        title: \"Theming & Palette Matrix\",\n        category: \"Tokens\",\n        desc: \"Adaptive dark mode palettes and high contrast curves\",\n      },\n    ];\n    return pool.filter(\n      (item) =>\n        item.title.toLowerCase().includes(query.toLowerCase()) ||\n        item.desc.toLowerCase().includes(query.toLowerCase()) ||\n        item.category.toLowerCase().includes(query.toLowerCase()),\n    );\n  }, [detected.type, query]);\n\n  return (\n    <div\n      ref={containerRef}\n      className={cn(\"relative w-full max-w-140 mx-auto select-none\", className)}\n    >\n      <motion.div\n        layout\n        transition={reduceMotion ? fadeTween : springMorph}\n        style={{ borderRadius: 28 }}\n        className={cn(\n          \"relative overflow-hidden border border-zinc-200/80 bg-white/95 backdrop-blur-2xl dark:border-white/10 dark:bg-[#0d0d12]/95\",\n          isOpen\n            ? \"border-zinc-300 shadow-[0_24px_64px_rgba(0,0,0,0.08),0_0_0_1px_rgba(0,0,0,0.04)] dark:border-white/20 dark:shadow-[0_24px_64px_rgba(0,0,0,0.7),0_0_0_1px_rgba(255,255,255,0.06)]\"\n            : \"hover:border-zinc-300 shadow-[0_8px_32px_rgba(0,0,0,0.06),0_1px_1px_rgba(0,0,0,0.02)] dark:hover:border-white/15 dark:shadow-[0_8px_32px_rgba(0,0,0,0.36),0_1px_1px_rgba(255,255,255,0.04)]\",\n        )}\n      >\n        <motion.div\n          layout=\"position\"\n          transition={reduceMotion ? fadeTween : springMorph}\n          className=\"relative flex h-14.5 items-center px-6 gap-3\"\n        >\n          <div className=\"relative flex-1 flex items-center h-full min-w-0\">\n            <input\n              id={inputId}\n              ref={inputRef}\n              autoFocus={autoFocus}\n              autoComplete=\"off\"\n              spellCheck={false}\n              value={query}\n              onFocus={handleOpen}\n              onChange={(e) => updateQuery(e.target.value)}\n              onKeyDown={handleKeyDown}\n              className=\"w-full bg-transparent text-[17px] font-normal tracking-[-0.01em] text-zinc-900 caret-zinc-900 dark:text-white dark:caret-white outline-none placeholder:text-transparent\"\n              aria-label=\"Search or enter prompt\"\n            />\n\n            {!query && (\n              <span className=\"pointer-events-none absolute inset-0 flex items-center overflow-hidden\">\n                <AnimatePresence mode=\"popLayout\" initial={false}>\n                  <motion.span\n                    key={placeholderIndex}\n                    initial={\n                      reduceMotion\n                        ? { opacity: 0 }\n                        : { opacity: 0, y: 8, filter: \"blur(4px)\" }\n                    }\n                    animate={{ opacity: 1, y: 0, filter: \"blur(0px)\" }}\n                    exit={\n                      reduceMotion\n                        ? { opacity: 0 }\n                        : { opacity: 0, y: -8, filter: \"blur(4px)\" }\n                    }\n                    transition={fadeTween}\n                    className=\"truncate text-[17px] font-normal tracking-[-0.01em] text-zinc-400 dark:text-zinc-400\"\n                  >\n                    {placeholder ?? PLACEHOLDERS[placeholderIndex]}\n                  </motion.span>\n                </AnimatePresence>\n              </span>\n            )}\n          </div>\n\n          <div className=\"flex items-center gap-2 shrink-0\">\n            {query && (\n              <button\n                type=\"button\"\n                onClick={handleClear}\n                className=\"text-xs text-zinc-500 hover:text-zinc-900 dark:text-zinc-400 dark:hover:text-white px-1.5 py-0.5 rounded transition-colors cursor-pointer\"\n                aria-label=\"Clear input\"\n              >\n                Clear\n              </button>\n            )}\n\n            <button\n              ref={triggerRef}\n              type=\"button\"\n              onClick={isOpen ? closeToTrigger : handleOpen}\n              aria-label={isOpen ? \"Close search\" : \"Open search\"}\n              aria-expanded={isOpen}\n              className=\"relative flex items-center rounded-full border border-zinc-200 bg-zinc-100/80 px-2 py-0.5 text-[11px] font-mono text-zinc-500 hover:text-zinc-900 hover:border-zinc-300 dark:border-white/10 dark:bg-white/5 dark:text-zinc-400 dark:hover:text-white dark:hover:border-white/20 transition-colors cursor-pointer after:absolute after:-inset-x-2.5 after:-inset-y-3 outline-none focus-visible:ring-2 focus-visible:ring-zinc-400 dark:focus-visible:ring-white/40\"\n            >\n              <span>{isOpen ? \"ESC\" : \"/\"}</span>\n            </button>\n          </div>\n        </motion.div>\n\n        <AnimatePresence mode=\"popLayout\" initial={false}>\n          {isOpen && (\n            <motion.div\n              key=\"content-body\"\n              initial={\n                reduceMotion\n                  ? { opacity: 0 }\n                  : { opacity: 0, filter: \"blur(4px)\" }\n              }\n              animate={{ opacity: 1, filter: \"blur(0px)\" }}\n              exit={{\n                opacity: 0,\n                filter: \"blur(4px)\",\n                transition: { duration: 0.12, ease: [0.23, 1, 0.32, 1] },\n              }}\n              transition={\n                reduceMotion ? fadeTween : { ...springMorph, delay: 0.02 }\n              }\n              className=\"w-full overflow-hidden border-t border-zinc-200/80 dark:border-white/8\"\n            >\n              <div className=\"p-5 flex flex-col gap-4\">\n                {detected.type === \"poll\" && detected.poll && (\n                  <div className=\"flex flex-col gap-3 rounded-xl border border-zinc-200/80 bg-zinc-50/60 p-4 dark:border-white/8 dark:bg-white/2\">\n                    <div className=\"flex items-center justify-between text-xs text-zinc-400\">\n                      <span className=\"font-medium text-zinc-700 dark:text-zinc-300\">\n                        Poll\n                      </span>\n                      <span>Select an option to vote</span>\n                    </div>\n\n                    <h3 className=\"text-[15px] font-medium text-zinc-900 dark:text-white\">\n                      {detected.poll.title}\n                    </h3>\n\n                    <div className=\"flex flex-col gap-2\">\n                      {detected.poll.options.map((opt, idx) => {\n                        const totalVotes = Object.values(pollVotes).reduce(\n                          (a, b) => a + b,\n                          0,\n                        );\n                        const votes = pollVotes[idx] || 0;\n                        const pct =\n                          totalVotes > 0\n                            ? Math.round((votes / totalVotes) * 100)\n                            : 0;\n\n                        return (\n                          <button\n                            key={idx}\n                            type=\"button\"\n                            onClick={() => voteOnPoll(idx)}\n                            className=\"group relative flex items-center justify-between overflow-hidden rounded-lg border border-zinc-200/80 bg-zinc-100/60 px-3.5 py-2.5 text-left text-xs transition-colors hover:border-zinc-300 hover:bg-zinc-100 dark:border-white/8 dark:bg-white/4 dark:hover:border-white/15 dark:hover:bg-white/7 cursor-pointer\"\n                          >\n                            <motion.div\n                              className=\"absolute inset-y-0 left-0 bg-zinc-900/10 dark:bg-white/10\"\n                              initial={{ width: \"0%\" }}\n                              animate={{ width: `${pct}%` }}\n                              transition={springMorph}\n                            />\n                            <span className=\"relative z-10 font-medium text-zinc-800 group-hover:text-zinc-950 dark:text-zinc-200 dark:group-hover:text-white\">\n                              {opt}\n                            </span>\n                            <span className=\"relative z-10 font-mono text-zinc-500 dark:text-zinc-400 tabular-nums\">\n                              {pct}%\n                            </span>\n                          </button>\n                        );\n                      })}\n                    </div>\n\n                    <div className=\"flex items-center justify-between pt-1\">\n                      <span className=\"text-[11px] text-zinc-500 dark:text-zinc-400\">\n                        {Object.values(pollVotes).reduce((a, b) => a + b, 0)}{\" \"}\n                        total votes\n                      </span>\n                      <button\n                        type=\"button\"\n                        onClick={saveCurrentItem}\n                        className=\"flex items-center rounded-full bg-zinc-900 px-3 py-1 text-xs font-medium text-white hover:bg-zinc-800 dark:bg-white dark:text-zinc-950 dark:hover:bg-zinc-200 transition-colors cursor-pointer\"\n                      >\n                        <span>Save Poll</span>\n                      </button>\n                    </div>\n                  </div>\n                )}\n\n                {detected.type === \"event\" && detected.event && (\n                  <div className=\"flex flex-col gap-3 rounded-xl border border-zinc-200/80 bg-zinc-50/60 p-4 dark:border-white/8 dark:bg-white/2\">\n                    <div className=\"flex items-center justify-between text-xs text-zinc-400\">\n                      <span className=\"font-medium text-zinc-700 dark:text-zinc-300\">\n                        Event\n                      </span>\n                      <span>Scheduled Meeting</span>\n                    </div>\n\n                    <h3 className=\"text-[15px] font-medium text-zinc-900 dark:text-white\">\n                      {detected.event.title}\n                    </h3>\n\n                    <div className=\"flex flex-wrap items-center gap-2\">\n                      <div className=\"rounded-md border border-zinc-200 bg-zinc-100 px-2.5 py-1 text-xs text-zinc-700 dark:border-white/8 dark:bg-white/4 dark:text-zinc-300\">\n                        <span>{detected.event.time}</span>\n                      </div>\n                      <div className=\"rounded-md border border-zinc-200 bg-zinc-100 px-2.5 py-1 text-xs text-zinc-700 dark:border-white/8 dark:bg-white/4 dark:text-zinc-300\">\n                        <span>{detected.event.attendee}</span>\n                      </div>\n                    </div>\n\n                    <div className=\"flex justify-end pt-1\">\n                      <button\n                        type=\"button\"\n                        onClick={saveCurrentItem}\n                        className=\"flex items-center rounded-full bg-zinc-900 px-3 py-1 text-xs font-medium text-white hover:bg-zinc-800 dark:bg-white dark:text-zinc-950 dark:hover:bg-zinc-200 transition-colors cursor-pointer\"\n                      >\n                        <span>Save Event</span>\n                      </button>\n                    </div>\n                  </div>\n                )}\n\n                {detected.type === \"todo\" && detected.todo && (\n                  <div className=\"flex flex-col gap-3 rounded-xl border border-zinc-200/80 bg-zinc-50/60 p-4 dark:border-white/8 dark:bg-white/2\">\n                    <div className=\"flex items-center justify-between text-xs text-zinc-400\">\n                      <span className=\"font-medium text-zinc-700 dark:text-zinc-300\">\n                        Checklist\n                      </span>\n                      <span>\n                        {Object.values(completedTodos).filter(Boolean).length}/\n                        {detected.todo.items.length} completed\n                      </span>\n                    </div>\n\n                    <div className=\"flex flex-col gap-1.5\">\n                      {detected.todo.items.map((item, idx) => {\n                        const done = Boolean(completedTodos[idx]);\n                        return (\n                          <button\n                            key={idx}\n                            type=\"button\"\n                            onClick={() => toggleTodo(idx)}\n                            className=\"flex items-center gap-2.5 rounded-lg border border-zinc-200/60 bg-zinc-100/40 px-3 py-2 text-left text-xs transition-colors hover:bg-zinc-100/80 dark:border-white/6 dark:bg-white/3 dark:hover:bg-white/6 cursor-pointer\"\n                          >\n                            <span\n                              className={cn(\n                                \"flex size-3.5 shrink-0 rounded-sm border transition-colors\",\n                                done\n                                  ? \"border-zinc-900 bg-zinc-900 dark:border-white dark:bg-white\"\n                                  : \"border-zinc-300 dark:border-white/30\",\n                              )}\n                            />\n                            <span\n                              className={cn(\n                                \"font-normal transition-all\",\n                                done\n                                  ? \"text-zinc-400 line-through dark:text-zinc-500\"\n                                  : \"text-zinc-800 dark:text-zinc-200\",\n                              )}\n                            >\n                              {item}\n                            </span>\n                          </button>\n                        );\n                      })}\n                    </div>\n\n                    <div className=\"flex justify-end pt-1\">\n                      <button\n                        type=\"button\"\n                        onClick={saveCurrentItem}\n                        className=\"flex items-center rounded-full bg-zinc-900 px-3 py-1 text-xs font-medium text-white hover:bg-zinc-800 dark:bg-white dark:text-zinc-950 dark:hover:bg-zinc-200 transition-colors cursor-pointer\"\n                      >\n                        <span>Save Tasks</span>\n                      </button>\n                    </div>\n                  </div>\n                )}\n\n                {detected.type === \"timer\" && detected.timer && (\n                  <div className=\"flex flex-col gap-3 rounded-xl border border-zinc-200/80 bg-zinc-50/60 p-4 dark:border-white/8 dark:bg-white/2\">\n                    <div className=\"flex items-center justify-between text-xs text-zinc-400\">\n                      <span className=\"font-medium text-zinc-700 dark:text-zinc-300\">\n                        Timer\n                      </span>\n                      <span>{detected.timer.label}</span>\n                    </div>\n\n                    <div className=\"flex items-center justify-between py-2\">\n                      <span className=\"font-mono text-3xl font-medium tracking-tight text-zinc-900 dark:text-white\">\n                        {formatTimer(timerTimeLeft ?? detected.timer.seconds)}\n                      </span>\n\n                      <div className=\"flex items-center gap-2\">\n                        <button\n                          type=\"button\"\n                          onClick={() => setTimerRunning((p) => !p)}\n                          className=\"flex items-center rounded-full bg-zinc-900 px-3 py-1 text-xs font-medium text-white hover:bg-zinc-800 dark:bg-white dark:text-zinc-950 dark:hover:bg-zinc-200 transition-colors cursor-pointer\"\n                        >\n                          {timerRunning ? \"Pause\" : \"Start\"}\n                        </button>\n                        <button\n                          type=\"button\"\n                          onClick={() => {\n                            setTimerRunning(false);\n                            setTimerTimeLeft(detected.timer?.seconds ?? 1500);\n                          }}\n                          className=\"flex items-center rounded-full border border-zinc-200 bg-zinc-100 px-3 py-1 text-xs text-zinc-600 hover:text-zinc-900 dark:border-white/10 dark:bg-white/5 dark:text-zinc-400 dark:hover:text-white transition-colors cursor-pointer\"\n                        >\n                          Reset\n                        </button>\n                        <button\n                          type=\"button\"\n                          onClick={saveCurrentItem}\n                          className=\"flex items-center rounded-full border border-zinc-200 bg-zinc-100 px-3 py-1 text-xs text-zinc-700 hover:text-zinc-900 dark:border-white/10 dark:bg-white/5 dark:text-zinc-300 dark:hover:text-white transition-colors cursor-pointer\"\n                        >\n                          Save\n                        </button>\n                      </div>\n                    </div>\n                  </div>\n                )}\n\n                {detected.type === \"split\" && detected.split && (\n                  <div className=\"flex flex-col gap-3 rounded-xl border border-zinc-200/80 bg-zinc-50/60 p-4 dark:border-white/8 dark:bg-white/2\">\n                    <div className=\"flex items-center justify-between text-xs text-zinc-400\">\n                      <span className=\"font-medium text-zinc-700 dark:text-zinc-300\">\n                        Split Bill\n                      </span>\n                      <span>\n                        {detected.split.total} among {detected.split.people}\n                      </span>\n                    </div>\n\n                    <div className=\"flex items-baseline justify-between py-1\">\n                      <span className=\"text-xs text-zinc-500 dark:text-zinc-400\">\n                        Each pays\n                      </span>\n                      <span className=\"font-mono text-2xl font-semibold text-zinc-900 dark:text-white\">\n                        ₹{detected.split.perPerson}\n                      </span>\n                    </div>\n\n                    <div className=\"grid grid-cols-3 gap-1.5 pt-1\">\n                      {(() => {\n                        const splitData = detected.split;\n                        return Array.from({\n                          length: Math.min(6, splitData.people),\n                        }).map((_, i) => (\n                          <div\n                            key={i}\n                            className=\"flex flex-col items-center justify-center rounded-lg border border-zinc-200/60 bg-zinc-100/40 py-2 text-center dark:border-white/6 dark:bg-white/3\"\n                          >\n                            <span className=\"text-[10px] text-zinc-500 dark:text-zinc-500\">\n                              Person {i + 1}\n                            </span>\n                            <span className=\"font-mono text-xs font-medium text-zinc-800 dark:text-zinc-200\">\n                              {splitData.perPerson}\n                            </span>\n                          </div>\n                        ));\n                      })()}\n                    </div>\n\n                    <div className=\"flex justify-end pt-1\">\n                      <button\n                        type=\"button\"\n                        onClick={saveCurrentItem}\n                        className=\"flex items-center rounded-full bg-zinc-900 px-3 py-1 text-xs font-medium text-white hover:bg-zinc-800 dark:bg-white dark:text-zinc-950 dark:hover:bg-zinc-200 transition-colors cursor-pointer\"\n                      >\n                        <span>Save Split</span>\n                      </button>\n                    </div>\n                  </div>\n                )}\n\n                {detected.type === \"search\" && query.trim() && (\n                  <div className=\"flex flex-col gap-1.5\">\n                    <div className=\"px-1 text-[11px] font-medium text-zinc-500 dark:text-zinc-400 uppercase tracking-wider\">\n                      Search Results\n                    </div>\n                    {searchResults.length > 0 ? (\n                      searchResults.map((item, idx) => (\n                        <button\n                          key={idx}\n                          type=\"button\"\n                          onClick={() => {\n                            updateQuery(item.title);\n                            saveCurrentItem();\n                          }}\n                          className=\"group flex items-center justify-between rounded-lg border border-transparent px-3 py-2 text-left text-xs transition-colors hover:border-zinc-200 hover:bg-zinc-100/70 dark:hover:border-white/8 dark:hover:bg-white/5 cursor-pointer\"\n                        >\n                          <div className=\"flex flex-col\">\n                            <span className=\"font-medium text-zinc-800 group-hover:text-zinc-950 dark:text-zinc-200 dark:group-hover:text-white\">\n                              {item.title}\n                            </span>\n                            <span className=\"text-[11px] text-zinc-500 dark:text-zinc-500\">\n                              {item.desc}\n                            </span>\n                          </div>\n                          <span className=\"rounded-full border border-zinc-200 bg-zinc-100 px-2 py-0.5 text-[10px] text-zinc-600 dark:border-white/8 dark:bg-white/4 dark:text-zinc-400\">\n                            {item.category}\n                          </span>\n                        </button>\n                      ))\n                    ) : (\n                      <div className=\"py-6 text-center text-xs text-zinc-500 dark:text-zinc-500\">\n                        No matches found for &quot;{query}&quot;\n                      </div>\n                    )}\n                  </div>\n                )}\n\n                {!query.trim() && (\n                  <div className=\"flex flex-col gap-3\">\n                    <div className=\"flex flex-wrap items-center gap-1.5\">\n                      {sampleChips.map((chip) => {\n                        const isSelected = activeCategory === chip.id;\n                        return (\n                          <button\n                            key={chip.id}\n                            type=\"button\"\n                            onClick={() => {\n                              setActiveCategory(chip.id);\n                              if (chip.sample) {\n                                updateQuery(chip.sample);\n                              }\n                            }}\n                            className={cn(\n                              \"flex items-center rounded-full border px-3 py-1 text-xs transition-colors cursor-pointer\",\n                              isSelected\n                                ? \"border-zinc-900 bg-zinc-900 text-white dark:border-white/25 dark:bg-white/10 dark:text-white\"\n                                : \"border-zinc-200 bg-zinc-100 text-zinc-600 hover:border-zinc-300 hover:text-zinc-900 dark:border-white/8 dark:bg-white/4 dark:text-zinc-400 dark:hover:border-white/15 dark:hover:text-zinc-200\",\n                            )}\n                          >\n                            <span>{chip.label}</span>\n                          </button>\n                        );\n                      })}\n                    </div>\n\n                    <div className=\"flex flex-col gap-1 pt-1\">\n                      <span className=\"px-1 text-[11px] font-medium text-zinc-500 dark:text-zinc-500 uppercase tracking-wider\">\n                        Suggested Prompts\n                      </span>\n                      {PLACEHOLDERS.slice(0, 4).map((p, i) => (\n                        <button\n                          key={i}\n                          type=\"button\"\n                          onClick={() => updateQuery(p)}\n                          className=\"flex items-center justify-between rounded-lg px-2.5 py-1.5 text-left text-xs text-zinc-700 hover:bg-zinc-100 hover:text-zinc-950 dark:text-zinc-300 dark:hover:bg-white/5 dark:hover:text-white transition-colors cursor-pointer\"\n                        >\n                          <span className=\"truncate\">{p}</span>\n                        </button>\n                      ))}\n                    </div>\n                  </div>\n                )}\n              </div>\n            </motion.div>\n          )}\n        </AnimatePresence>\n      </motion.div>\n\n      <AnimatePresence initial={false}>\n        {savedItems.length > 0 && (\n          <motion.div\n            layout\n            initial={{ opacity: 0, y: 6 }}\n            animate={{ opacity: 1, y: 0 }}\n            exit={{ opacity: 0, y: 6 }}\n            transition={reduceMotion ? fadeTween : springMorph}\n            className=\"mt-3 flex flex-col gap-1 rounded-[22px] border border-zinc-200/80 bg-white/95 backdrop-blur-2xl p-2 shadow-[0_8px_32px_rgba(0,0,0,0.06)] dark:border-white/10 dark:bg-[#0d0d12]/95 dark:shadow-[0_8px_32px_rgba(0,0,0,0.36)]\"\n          >\n            <AnimatePresence initial={false}>\n              {savedItems.map((item) => (\n                <motion.div\n                  key={item.id}\n                  layout\n                  initial={{ opacity: 0, y: -4 }}\n                  animate={{ opacity: 1, y: 0 }}\n                  exit={{ opacity: 0, scale: 0.96 }}\n                  transition={reduceMotion ? fadeTween : springMorph}\n                  className=\"group relative flex items-center justify-between rounded-xl px-3 py-2.5 transition-colors hover:bg-zinc-100/70 dark:hover:bg-white/5 cursor-pointer\"\n                  onClick={() => reopenItem(item)}\n                >\n                  <div className=\"flex items-center gap-2.5 min-w-0 flex-1 pr-3\">\n                    <span className=\"shrink-0 rounded-md border border-zinc-200 bg-zinc-100 px-2 py-0.5 text-[11px] font-medium uppercase tracking-wider text-zinc-600 dark:border-white/10 dark:bg-white/5 dark:text-zinc-400\">\n                      {item.intent}\n                    </span>\n                    <span className=\"truncate text-[14px] text-zinc-800 group-hover:text-zinc-950 dark:text-zinc-200 dark:group-hover:text-white\">\n                      {item.summary}\n                    </span>\n                  </div>\n                  <button\n                    type=\"button\"\n                    onClick={(e) => {\n                      e.stopPropagation();\n                      removeItem(item.id);\n                    }}\n                    className=\"shrink-0 rounded px-1.5 py-0.5 text-xs text-zinc-400 hover:text-zinc-800 dark:text-zinc-500 dark:hover:text-zinc-200 transition-colors cursor-pointer\"\n                  >\n                    Delete\n                  </button>\n                </motion.div>\n              ))}\n            </AnimatePresence>\n          </motion.div>\n        )}\n      </AnimatePresence>\n    </div>\n  );\n}\n",
      "type": "registry:ui",
      "target": "@ui/morph-search.tsx"
    }
  ]
}
