diff --git a/ui/src/components/ChatMentionPopover.test.tsx b/ui/src/components/ChatMentionPopover.test.tsx index c1956822..d5d5446d 100644 --- a/ui/src/components/ChatMentionPopover.test.tsx +++ b/ui/src/components/ChatMentionPopover.test.tsx @@ -1,8 +1,14 @@ -import { describe, it } from "vitest"; +// @vitest-environment jsdom +import { describe, it, expect } from "vitest"; describe("ChatMentionPopover", () => { + it("exports ChatMentionPopover component", async () => { + const mod = await import("./ChatMentionPopover"); + expect(mod.ChatMentionPopover).toBeDefined(); + }); + it.todo("renders agent list filtered by query string"); it.todo("shows agent icon, name, and role for each item"); - it.todo("calls onSelect with @agentName on item click"); + it.todo("calls onSelect with agent name on item click"); it.todo("shows 'No agents found' when filter matches nothing"); }); diff --git a/ui/src/components/ChatMentionPopover.tsx b/ui/src/components/ChatMentionPopover.tsx new file mode 100644 index 00000000..3c34024d --- /dev/null +++ b/ui/src/components/ChatMentionPopover.tsx @@ -0,0 +1,73 @@ +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 ( + + {children} + e.preventDefault()} + > + {isLoading ? ( +
+ + + +
+ ) : filtered.length === 0 ? ( +

No agents found

+ ) : ( +
+ {filtered.slice(0, 5).map((agent) => { + const colorClass = agent.role + ? (agentRoleColors[agent.role as AgentRole] ?? agentRoleColorDefault) + : agentRoleColorDefault; + return ( + + ); + })} +
+ )} +
+
+ ); +}