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:
Fusion
2026-05-10 10:54:46 -07:00
committed by gsxdsm
parent ff4e7cad78
commit 6b55b26195
3 changed files with 21 additions and 0 deletions

View 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.

View File

@@ -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(...)`. - 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. - `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. - 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 ### Agent Companies

View File

@@ -3,6 +3,16 @@ import { describe, expect, it, vi } from "vitest";
import type { Agent } from "@fusion/core"; import type { Agent } from "@fusion/core";
import { AgentMentionPopup } from "../AgentMentionPopup"; 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[] = [ 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-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: {} }, { 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")).toHaveClass("agent-mention-item--highlighted");
expect(screen.getByTestId("agent-mention-item-agent-003")).toHaveAttribute("aria-selected", "true");
rerender( rerender(
<AgentMentionPopup <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")).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", () => { it("selects non-members and includes accessible member dot labels", () => {