"use client"; import React, { useState, useEffect, forwardRef, useImperativeHandle, } from "react"; import { Input } from "@/app/_components/ui/input"; import { motion, AnimatePresence } from "framer-motion"; import { Search, Send, BarChart2, Globe, Video, PlaneTakeoff, AudioLines, } from "lucide-react"; import useDebounce from "@/app/_hooks/use-debounce"; interface Action { id: string; label: string; icon: React.ReactNode; description?: string; short?: string; end?: string; } interface SearchResult { actions: Action[]; } const allActions = [ { id: "1", label: "Book tickets", icon: , description: "Operator", short: "⌘K", end: "Agent", }, { id: "2", label: "Summarize", icon: , description: "gpt-4o", short: "⌘cmd+p", end: "Command", }, { id: "3", label: "Screen Studio", icon: , description: "gpt-4o", short: "", end: "Application", }, { id: "4", label: "Talk to Jarvis", icon: , description: "gpt-4o voice", short: "", end: "Active", }, { id: "5", label: "Translate", icon: , description: "gpt-4o", short: "", end: "Command", }, ]; interface ActionSearchBarProps { actions?: Action[]; autoFocus?: boolean; isFloating?: boolean; } const ActionSearchBar = forwardRef( ({ actions = allActions, autoFocus = false, isFloating = false }, ref) => { const [query, setQuery] = useState(""); const [result, setResult] = useState(null); const [isFocused, setIsFocused] = useState(autoFocus); const [selectedAction, setSelectedAction] = useState(null); const debouncedQuery = useDebounce(query, 200); const inputRef = React.useRef(null); useImperativeHandle(ref, () => inputRef.current as HTMLInputElement); useEffect(() => { if (autoFocus && inputRef.current) { inputRef.current.focus(); } }, [autoFocus]); useEffect(() => { if (!debouncedQuery) { setResult({ actions: allActions }); return; } const normalizedQuery = debouncedQuery.toLowerCase().trim(); const filteredActions = allActions.filter((action) => { const searchableText = action.label.toLowerCase(); return searchableText.includes(normalizedQuery); }); setResult({ actions: filteredActions }); }, [debouncedQuery]); const handleInputChange = (e: React.ChangeEvent) => { setQuery(e.target.value); }; const container = { hidden: { opacity: 0, height: 0 }, show: { opacity: 1, height: "auto", transition: { height: { duration: 0.4, }, staggerChildren: 0.1, }, }, exit: { opacity: 0, height: 0, transition: { height: { duration: 0.3, }, opacity: { duration: 0.2, }, }, }, }; const item = { hidden: { opacity: 0, y: 20 }, show: { opacity: 1, y: 0, transition: { duration: 0.3, }, }, exit: { opacity: 0, y: -10, transition: { duration: 0.2, }, }, }; return ( setIsFocused(true)} onBlur={() => !isFloating && setTimeout(() => setIsFocused(false), 200) } className="pl-3 pr-9 py-1.5 h-9 text-sm rounded-lg focus-visible:ring-offset-0" /> {query.length > 0 ? ( ) : ( )} {(isFocused || isFloating) && result && !selectedAction && ( {result.actions.map((action) => ( setSelectedAction(action)} > {action.icon} {action.label} {action.description} {action.short} {action.end} ))} Press ⌘K to open commands ESC to cancel )} ); } ); ActionSearchBar.displayName = "ActionSearchBar"; export default ActionSearchBar;