feat(FN-3811): add room mention popup to dashboard architecture docs and te
Added documentation for the room mention feature in the architecture docs and a changeset, alongside hardened tests for the `AgentMentionPopup` room variant. Fusion-Task-Id: FN-3811
This commit is contained in:
5
.changeset/FN-3811-room-mentions.md
Normal file
5
.changeset/FN-3811-room-mentions.md
Normal file
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"@runfusion/fusion": patch
|
||||
---
|
||||
|
||||
Update room chat mention UX so the mention popup prioritizes room members (with a member indicator) and rendered room-message mention chips visibly flag non-members, while preserving direct-chat behavior.
|
||||
@@ -275,6 +275,10 @@ Intentional exclusions from shared snapshots:
|
||||
- Room messages persist through `POST /api/chat/rooms/:id/messages`; the route persists the user message first, then calls `ChatManager.sendRoomMessage(...)` to orchestrate room-member responders and persist assistant room replies with `chatStore.addRoomMessage(...)`.
|
||||
- `sendRoomMessage(...)` uses existing room-member + mention resolution rules: mentioned members are direct responders, non-mentioned members are ambient responders (capped by `ROOM_AMBIENT_MAX_RESPONDERS`), and non-member mentions are handled explicitly by the manager instead of silently disappearing.
|
||||
- UI does not optimistically insert room messages; it renders persisted user + assistant room messages from `chat:room:message:*` SSE events.
|
||||
- Mention UI in rooms keeps direct-chat behavior unchanged while adding room affordances:
|
||||
- `AgentMentionPopup` receives room membership context and shows members first with a `status-dot` member indicator (`aria-label="Room member"`).
|
||||
- With an empty mention filter in room mode, only room members are listed; a hint row prompts the user to type to search non-members.
|
||||
- Mention chips rendered in room messages (`ChatView` and `QuickChatFAB`) mark non-members via `chat-mention-chip--non-member`, including `title`/`aria-label` text (`Not a member of {roomName}`) and muted warning-token styling.
|
||||
|
||||
### Agent Companies
|
||||
|
||||
|
||||
@@ -3,6 +3,16 @@ import { describe, expect, it, vi } from "vitest";
|
||||
import type { Agent } from "@fusion/core";
|
||||
import { AgentMentionPopup } from "../AgentMentionPopup";
|
||||
|
||||
vi.mock("lucide-react", async (importOriginal) => {
|
||||
const actual = await importOriginal<typeof import("lucide-react")>();
|
||||
return {
|
||||
...actual,
|
||||
Bot: ({ "data-testid": testId, ...props }: any) => (
|
||||
<svg data-testid={testId || "icon-bot"} {...props} />
|
||||
),
|
||||
};
|
||||
});
|
||||
|
||||
const agents: Agent[] = [
|
||||
{ id: "agent-001", name: "Alpha", role: "executor", state: "idle", createdAt: "2026-04-01T00:00:00.000Z", updatedAt: "2026-04-01T00:00:00.000Z", metadata: {} },
|
||||
{ id: "agent-002", name: "Alfred", role: "reviewer", state: "idle", createdAt: "2026-04-01T00:00:00.000Z", updatedAt: "2026-04-01T00:00:00.000Z", metadata: {} },
|
||||
@@ -59,6 +69,7 @@ describe("AgentMentionPopup room behavior", () => {
|
||||
);
|
||||
|
||||
expect(screen.getByTestId("agent-mention-item-agent-003")).toHaveClass("agent-mention-item--highlighted");
|
||||
expect(screen.getByTestId("agent-mention-item-agent-003")).toHaveAttribute("aria-selected", "true");
|
||||
|
||||
rerender(
|
||||
<AgentMentionPopup
|
||||
@@ -72,6 +83,7 @@ describe("AgentMentionPopup room behavior", () => {
|
||||
);
|
||||
|
||||
expect(screen.getByTestId("agent-mention-item-agent-001")).toHaveClass("agent-mention-item--highlighted");
|
||||
expect(screen.getByTestId("agent-mention-item-agent-001")).toHaveAttribute("aria-selected", "true");
|
||||
});
|
||||
|
||||
it("selects non-members and includes accessible member dot labels", () => {
|
||||
|
||||
Reference in New Issue
Block a user