import { AgentAvatar, type AvatarAgent } from "@/components/AgentAvatar"; import { useEffect, useMemo, useState, type ComponentProps, type ReactNode } from "react"; import { ChevronRight } from "lucide-react"; import { Button } from "@/components/ui/button"; import { Checkbox } from "@/components/ui/checkbox"; import { Input } from "@/components/ui/input"; import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover"; import { Skeleton } from "@/components/ui/skeleton"; import { cn } from "@/lib/utils"; export interface AgentMultiSelectOption extends AvatarAgent { id: string; name: string; title?: string | null; icon?: string | null; } export function AgentSelect({ agents, value, onChange, placeholder = "Select agent…", emptyMessage = "No agents yet.", disabled = false, triggerClassName, id, }: { agents: AgentMultiSelectOption[]; value: string; onChange: (agentId: string) => void; placeholder?: string; emptyMessage?: string; disabled?: boolean; triggerClassName?: string; id?: string; }) { const [open, setOpen] = useState(false); const [filter, setFilter] = useState(""); const selectedAgent = agents.find((agent) => agent.id === value); const normalizedFilter = filter.trim().toLowerCase(); const filteredAgents = useMemo( () => agents .filter((agent) => `${agent.name} ${agent.title ?? ""}`.toLowerCase().includes(normalizedFilter)) .sort((a, b) => a.name.localeCompare(b.name)), [agents, normalizedFilter], ); return ( { setOpen(nextOpen); if (!nextOpen) setFilter(""); }} >
setFilter(event.target.value)} placeholder="Filter agents" className="h-8" autoFocus />
{agents.length === 0 ? (
{emptyMessage}
) : (
{filteredAgents.map((agent) => ( ))} {filteredAgents.length === 0 ? (
No matches.
) : null}
)}
); } export function AgentMultiSelect({ agents, selectedAgentIds, onChange, onSave, loading = false, disabled = false, pending = false, getDescription, isAgentDisabled, renderNameSuffix, triggerLabel, triggerIcon, triggerVariant = "outline", triggerSize = "default", triggerFullWidth = true, triggerClassName, contentAlign = "start", headerContent, emptyMessage = "No agents yet.", showSelectionPreview = true, onOpenChange, }: { agents: AgentMultiSelectOption[]; selectedAgentIds: Set; onChange?: (next: Set) => void; onSave?: (next: Set) => void; loading?: boolean; disabled?: boolean; pending?: boolean; getDescription?: (agent: AgentMultiSelectOption) => string | null | undefined; isAgentDisabled?: (agent: AgentMultiSelectOption) => boolean; renderNameSuffix?: (agent: AgentMultiSelectOption) => ReactNode; triggerLabel?: string; triggerIcon?: ReactNode; triggerVariant?: ComponentProps["variant"]; triggerSize?: ComponentProps["size"]; triggerFullWidth?: boolean; triggerClassName?: string; contentAlign?: ComponentProps["align"]; headerContent?: ReactNode; emptyMessage?: string; showSelectionPreview?: boolean; onOpenChange?: (open: boolean) => void; }) { const [open, setOpen] = useState(false); const [filter, setFilter] = useState(""); const [draftAgentIds, setDraftAgentIds] = useState>(new Set(selectedAgentIds)); const staged = Boolean(onSave); const workingAgentIds = staged ? draftAgentIds : selectedAgentIds; useEffect(() => { if (open && staged) setDraftAgentIds(new Set(selectedAgentIds)); }, [open, selectedAgentIds, staged]); const normalizedFilter = filter.trim().toLowerCase(); const filteredAgents = useMemo( () => agents .filter((agent) => { const description = getDescription?.(agent) ?? agent.title ?? ""; return `${agent.name} ${description}`.toLowerCase().includes(normalizedFilter); }) .sort((a, b) => { const aSelected = workingAgentIds.has(a.id); const bSelected = workingAgentIds.has(b.id); if (aSelected !== bSelected) return aSelected ? -1 : 1; return a.name.localeCompare(b.name); }), [agents, getDescription, normalizedFilter, workingAgentIds], ); const selectedCount = selectedAgentIds.size; const selectedAgents = agents.filter((agent) => selectedAgentIds.has(agent.id)); function setSelection(next: Set) { if (staged) setDraftAgentIds(next); else onChange?.(next); } return (
{ setOpen(nextOpen); onOpenChange?.(nextOpen); if (!nextOpen) setFilter(""); }} >
setFilter(event.target.value)} placeholder="Filter agents" className="h-8" autoFocus /> {headerContent}
{loading ? (
) : agents.length === 0 ? (
{emptyMessage}
) : (
{filteredAgents.map((agent) => { const description = getDescription?.(agent) ?? agent.title; const optionDisabled = isAgentDisabled?.(agent) ?? false; return ( ); })} {filteredAgents.length === 0 ? (
No matches.
) : null}
)}
{workingAgentIds.size === 0 ? "No agents selected" : `${workingAgentIds.size} selected`}
{staged ? ( ) : null}
{showSelectionPreview && selectedAgents.length > 0 ? (
{selectedAgents.slice(0, 3).map((agent) => (
{agent.name}
))} {selectedAgents.length > 3 ? (

and {selectedAgents.length - 3} more

) : null}
) : null}
); }