nexus/ui/src/components/ChatMentionPopover.tsx
Nexus Dev 6c550c8227 feat(22-04): ChatMentionPopover component
- Create ChatMentionPopover (200px, opens upward, agent icon + name + role label)
- Agents filtered by query, max 5 visible, No agents found empty state
- 3 skeleton rows loading state, onOpenAutoFocus prevented for textarea focus
- Include agent-role-colors dependency for worktree build
2026-04-02 15:08:50 +00:00

73 lines
2.5 KiB
TypeScript

import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover";
import { Skeleton } from "@/components/ui/skeleton";
import { AgentIcon } from "./AgentIconPicker";
import { agentRoleColors, agentRoleColorDefault } from "../lib/agent-role-colors";
import type { Agent, AgentRole } from "@paperclipai/shared";
interface ChatMentionPopoverProps {
open: boolean;
onOpenChange: (open: boolean) => void;
onSelect: (agentName: string) => void;
query: string;
agents: Agent[];
isLoading?: boolean;
children: React.ReactNode;
}
export function ChatMentionPopover({
open,
onOpenChange,
onSelect,
query,
agents,
isLoading,
children,
}: ChatMentionPopoverProps) {
const filtered = agents.filter((a) =>
a.name.toLowerCase().includes(query.toLowerCase()),
);
return (
<Popover open={open} onOpenChange={onOpenChange}>
<PopoverTrigger asChild>{children}</PopoverTrigger>
<PopoverContent
className="w-[200px] p-1"
align="start"
side="top"
onOpenAutoFocus={(e) => e.preventDefault()}
>
{isLoading ? (
<div className="space-y-1 p-1">
<Skeleton className="h-7 w-full" />
<Skeleton className="h-7 w-full" />
<Skeleton className="h-7 w-full" />
</div>
) : filtered.length === 0 ? (
<p className="text-sm text-muted-foreground text-center py-2">No agents found</p>
) : (
<div className="max-h-[180px] overflow-auto">
{filtered.slice(0, 5).map((agent) => {
const colorClass = agent.role
? (agentRoleColors[agent.role as AgentRole] ?? agentRoleColorDefault)
: agentRoleColorDefault;
return (
<button
key={agent.id}
className="flex items-center gap-2 w-full rounded-sm px-2 py-1.5 text-left text-sm hover:bg-accent hover:text-accent-foreground cursor-pointer"
onClick={() => {
onSelect(agent.name);
onOpenChange(false);
}}
>
<AgentIcon icon={agent.icon} className={`h-3.5 w-3.5 ${colorClass}`} />
<span className="truncate">{agent.name}</span>
<span className="ml-auto text-[11px] text-muted-foreground">{agent.role}</span>
</button>
);
})}
</div>
)}
</PopoverContent>
</Popover>
);
}