diff --git a/frontend/src/app/(dashboard)/dashboard/_components/dashboard-content.tsx b/frontend/src/app/(dashboard)/dashboard/_components/dashboard-content.tsx index f2549851..57e8433c 100644 --- a/frontend/src/app/(dashboard)/dashboard/_components/dashboard-content.tsx +++ b/frontend/src/app/(dashboard)/dashboard/_components/dashboard-content.tsx @@ -209,7 +209,7 @@ export function DashboardContent() {

Hey, I am

-

+

{displayName} {agentAvatar && ( diff --git a/frontend/src/components/thread/chat-input/chat-settings-dropdown.tsx b/frontend/src/components/thread/chat-input/chat-settings-dropdown.tsx index a0cfb896..65ccc29a 100644 --- a/frontend/src/components/thread/chat-input/chat-settings-dropdown.tsx +++ b/frontend/src/components/thread/chat-input/chat-settings-dropdown.tsx @@ -1,28 +1,25 @@ 'use client'; -import React, { useState } from 'react'; -import { Settings, ChevronRight, Bot, Presentation, Video, Code, FileSpreadsheet, Search, Plus, Star, User, Sparkles, Database, MessageSquare, Calculator, Palette, Zap } from 'lucide-react'; +import React, { useState, useRef, useEffect } from 'react'; +import { Settings, ChevronRight, Bot, Presentation, FileSpreadsheet, Search, Plus, User, Check, ChevronDown } from 'lucide-react'; import { Button } from '@/components/ui/button'; import { - Popover, - PopoverContent, - PopoverTrigger, -} from '@/components/ui/popover'; + DropdownMenu, + DropdownMenuContent, + DropdownMenuItem, + DropdownMenuTrigger, +} from '@/components/ui/dropdown-menu'; import { Badge } from '@/components/ui/badge'; -import { Separator } from '@/components/ui/separator'; import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger, } from '@/components/ui/tooltip'; -import { ChatSettingsDialog } from './chat-settings-dialog'; -import { SubscriptionStatus } from './_use-model-selection'; import { useAgents } from '@/hooks/react-query/agents/use-agents'; -import { useFeatureFlag } from '@/lib/feature-flags'; +import { ChatSettingsDialog } from './chat-settings-dialog'; import { useRouter } from 'next/navigation'; import { cn } from '@/lib/utils'; -import { Label } from '@/components/ui/label'; interface PredefinedAgent { id: string; @@ -35,16 +32,16 @@ interface PredefinedAgent { const PREDEFINED_AGENTS: PredefinedAgent[] = [ { id: 'slides', - name: 'Slides Pro', + name: 'Slides', description: 'Create stunning presentations and slide decks', - icon: , + icon: , category: 'productivity' }, { id: 'sheets', - name: 'Data Analyst', + name: 'Sheets', description: 'Spreadsheet and data analysis expert', - icon: , + icon: , category: 'productivity' } ]; @@ -55,14 +52,13 @@ interface ChatSettingsDropdownProps { selectedModel: string; onModelChange: (model: string) => void; modelOptions: any[]; - subscriptionStatus: SubscriptionStatus; + subscriptionStatus: any; canAccessModel: (modelId: string) => boolean; refreshCustomModels?: () => void; disabled?: boolean; - className?: string; } -export function ChatSettingsDropdown({ +export const ChatSettingsDropdown: React.FC = ({ selectedAgentId, onAgentSelect, selectedModel, @@ -72,227 +68,245 @@ export function ChatSettingsDropdown({ canAccessModel, refreshCustomModels, disabled = false, - className, -}: ChatSettingsDropdownProps) { - const [dropdownOpen, setDropdownOpen] = useState(false); +}) => { + const [isOpen, setIsOpen] = useState(false); + const [searchQuery, setSearchQuery] = useState(''); + const [highlightedIndex, setHighlightedIndex] = useState(-1); const [dialogOpen, setDialogOpen] = useState(false); + const searchInputRef = useRef(null); const router = useRouter(); - - // Check if custom agents feature is enabled - const { enabled: customAgentsEnabled, loading: flagsLoading } = useFeatureFlag('custom_agents'); - - // Fetch real agents from API only if feature is enabled - const { data: agentsResponse, isLoading: agentsLoading, refetch: loadAgents } = useAgents({ - limit: 100, - sort_by: 'name', - sort_order: 'asc' - }); - - const agents = (customAgentsEnabled && agentsResponse?.agents) || []; - const defaultAgent = agents.find(agent => agent.is_default); - - // Find selected agent - could be from real agents or predefined - const selectedRealAgent = agents.find(a => a.agent_id === selectedAgentId); - const selectedPredefinedAgent = PREDEFINED_AGENTS.find(a => a.id === selectedAgentId); - const selectedAgent = selectedRealAgent || selectedPredefinedAgent; - - const handleAgentSelect = (agentId: string | undefined) => { - onAgentSelect?.(agentId); - setDropdownOpen(false); - }; - const handleMoreOptions = () => { - setDropdownOpen(false); - setDialogOpen(true); - }; + const { data: agentsResponse, isLoading: agentsLoading } = useAgents(); + const agents = agentsResponse?.agents || []; - const handleExploreAll = () => { - setDropdownOpen(false); - router.push('/agents'); - }; + // Combine all agents + const allAgents = [ + { + id: undefined, + name: 'Suna', + description: 'Your personal AI assistant', + type: 'default' as const, + icon: + }, + ...PREDEFINED_AGENTS.map(agent => ({ + ...agent, + type: 'predefined' as const + })), + ...agents.map((agent: any) => ({ + ...agent, + id: agent.agent_id, + type: 'custom' as const, + icon: agent.avatar || + })) + ]; + + // Filter agents based on search query + const filteredAgents = allAgents.filter((agent) => + agent.name.toLowerCase().includes(searchQuery.toLowerCase()) || + agent.description?.toLowerCase().includes(searchQuery.toLowerCase()) + ); + + useEffect(() => { + if (isOpen && searchInputRef.current) { + setTimeout(() => { + searchInputRef.current?.focus(); + }, 50); + } else { + setSearchQuery(''); + setHighlightedIndex(-1); + } + }, [isOpen]); const getAgentDisplay = () => { - if (selectedRealAgent) { + const selectedAgent = allAgents.find(agent => agent.id === selectedAgentId); + if (selectedAgent) { return { - name: selectedRealAgent.name, - icon: , - avatar: selectedRealAgent.avatar - }; - } - if (selectedPredefinedAgent) { - return { - name: selectedPredefinedAgent.name, - icon: selectedPredefinedAgent.icon, - avatar: null + name: selectedAgent.name, + icon: selectedAgent.icon }; } return { name: 'Suna', - icon: , - avatar: null + icon: }; }; + const handleAgentSelect = (agentId: string | undefined) => { + onAgentSelect?.(agentId); + setIsOpen(false); + }; + + const handleSearchInputKeyDown = (e: React.KeyboardEvent) => { + e.stopPropagation(); + if (e.key === 'ArrowDown') { + e.preventDefault(); + setHighlightedIndex((prev) => + prev < filteredAgents.length - 1 ? prev + 1 : 0 + ); + } else if (e.key === 'ArrowUp') { + e.preventDefault(); + setHighlightedIndex((prev) => + prev > 0 ? prev - 1 : filteredAgents.length - 1 + ); + } else if (e.key === 'Enter' && highlightedIndex >= 0) { + e.preventDefault(); + const selectedAgent = filteredAgents[highlightedIndex]; + if (selectedAgent) { + handleAgentSelect(selectedAgent.id); + } + } + }; + + const handleExploreAll = () => { + setIsOpen(false); + router.push('/agents'); + }; + + const handleMoreOptions = () => { + setIsOpen(false); + setDialogOpen(true); + }; + const agentDisplay = getAgentDisplay(); - const AgentCard = ({ agent, isSelected, onClick, type }: { - agent: any; - isSelected: boolean; - onClick: () => void; - type: 'predefined' | 'custom' | 'default'; - }) => ( -
-
-
- {type === 'default' ? ( - - ) : type === 'custom' ? ( - agent.avatar - ) : ( - agent.icon - )} -
-
- {agent.name} -
-
-
- ); - return ( <> - + - + - + - -

- {agentDisplay.name} - {agentDisplay.avatar && ` ${agentDisplay.avatar}`} -

+ +

Select Agent

- - -
-
-

Choose Your Agent

-
-
-
-
- -
- handleAgentSelect(undefined)} - type="default" - /> -
- {PREDEFINED_AGENTS.map((agent) => ( - handleAgentSelect(agent.id)} - type="predefined" - /> - ))} -
-
-
-
- -
- {agentsLoading ? ( -
- Loading... -
- ) : agents.length > 0 ? ( -
- {agents.map((agent) => ( - handleAgentSelect(agent.agent_id)} - type="custom" - /> - ))} -
- ) : ( -
- -

No custom agents yet

- -
- )} -
+ +
+
+ + setSearchQuery(e.target.value)} + onKeyDown={handleSearchInputKeyDown} + className="w-full pl-8 pr-3 py-2 text-sm bg-transparent border border-input rounded-md focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2" + />
- -
-
- +
+
+ {agentsLoading ? ( +
+ Loading agents...
+ ) : filteredAgents.length === 0 ? ( +
+ No agents found +
+ ) : ( + filteredAgents.map((agent, index) => { + const isSelected = agent.id === selectedAgentId; + const isHighlighted = index === highlightedIndex; + + return ( + + + +
+ handleAgentSelect(agent.id)} + onMouseEnter={() => setHighlightedIndex(index)} + > +
+
+ {agent.icon} +
+
+
+ + {agent.name} + + {agent.type === 'predefined' && ( + + Pro + + )} + {agent.type === 'custom' && ( + + Custom + + )} +
+

+ {agent.description} +

+
+
+ {isSelected && ( + + )} +
+
+
+ +

{agent.description}

+
+
+
+ ); + }) + )} +
+
+
+
- - + + + ); -} \ No newline at end of file +}; \ No newline at end of file