@agent-native/toolkit 0.19.4 → 0.19.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -109,7 +109,9 @@
109
109
  "sourceEntries": ["src/collab-ui"],
110
110
  "targetRoot": "app/components/agent-native/toolkit",
111
111
  "dependencies": [
112
+ "@radix-ui/react-dropdown-menu",
112
113
  "@radix-ui/react-tooltip",
114
+ "@tabler/icons-react",
113
115
  "clsx",
114
116
  "react",
115
117
  "tailwind-merge",
@@ -383,6 +385,7 @@
383
385
  }
384
386
  ],
385
387
  "dependencies": [
388
+ "@radix-ui/react-dropdown-menu",
386
389
  "@tabler/icons-react",
387
390
  "@radix-ui/react-popover",
388
391
  "@radix-ui/react-tooltip",
@@ -8,8 +8,10 @@ export interface PresenceBarProps {
8
8
  agentActive?: boolean;
9
9
  /** @deprecated Agent editing is represented by the AI presence circle and tooltip. */
10
10
  showAgentEditingDot?: boolean;
11
- /** Current user's email (to exclude from the list). */
11
+ /** Current user's email, excluded unless showCurrentUser is true. */
12
12
  currentUserEmail?: string;
13
+ /** Include the current user in the roster as a non-followable avatar. */
14
+ showCurrentUser?: boolean;
13
15
  /** Max visible avatars before "+N" overflow. Default: 5 */
14
16
  maxVisible?: number;
15
17
  /** Additional CSS classes. */
@@ -19,11 +21,13 @@ export interface PresenceBarProps {
19
21
  * (or null for the agent avatar). Use this to start/stop follow mode.
20
22
  */
21
23
  onAvatarClick?: (user: CollabUser | null) => void;
24
+ /** Keep the AI avatar display-only when no agent viewport can be followed. */
25
+ disableAgentClick?: boolean;
22
26
  /**
23
27
  * The email of the user currently being followed. Highlighted with a
24
28
  * blue ring to indicate active follow mode.
25
29
  */
26
30
  followingEmail?: string | null;
27
31
  }
28
- export declare function PresenceBar({ activeUsers, agentPresent, agentActive, currentUserEmail, maxVisible, className, onAvatarClick, followingEmail, }: PresenceBarProps): import("react").JSX.Element | null;
32
+ export declare function PresenceBar({ activeUsers, agentPresent, agentActive, currentUserEmail, showCurrentUser, maxVisible, className, onAvatarClick, disableAgentClick, followingEmail, }: PresenceBarProps): import("react").JSX.Element | null;
29
33
  //# sourceMappingURL=PresenceBar.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"PresenceBar.d.ts","sourceRoot":"","sources":["../../src/collab-ui/PresenceBar.tsx"],"names":[],"mappings":"AAQA,OAAO,EACL,KAAK,UAAU,EAIhB,MAAM,YAAY,CAAC;AAEpB,MAAM,WAAW,gBAAgB;IAC/B,6CAA6C;IAC7C,WAAW,EAAE,UAAU,EAAE,CAAC;IAC1B,sDAAsD;IACtD,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,4DAA4D;IAC5D,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,sFAAsF;IACtF,mBAAmB,CAAC,EAAE,OAAO,CAAC;IAC9B,uDAAuD;IACvD,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,2DAA2D;IAC3D,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,8BAA8B;IAC9B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB;;;OAGG;IACH,aAAa,CAAC,EAAE,CAAC,IAAI,EAAE,UAAU,GAAG,IAAI,KAAK,IAAI,CAAC;IAClD;;;OAGG;IACH,cAAc,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAChC;AA+MD,wBAAgB,WAAW,CAAC,EAC1B,WAAW,EACX,YAAY,EACZ,WAAW,EACX,gBAAgB,EAChB,UAAc,EACd,SAAS,EACT,aAAa,EACb,cAAc,GACf,EAAE,gBAAgB,sCA+DlB"}
1
+ {"version":3,"file":"PresenceBar.d.ts","sourceRoot":"","sources":["../../src/collab-ui/PresenceBar.tsx"],"names":[],"mappings":"AAeA,OAAO,EACL,KAAK,UAAU,EAIhB,MAAM,YAAY,CAAC;AAEpB,MAAM,WAAW,gBAAgB;IAC/B,6CAA6C;IAC7C,WAAW,EAAE,UAAU,EAAE,CAAC;IAC1B,sDAAsD;IACtD,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,4DAA4D;IAC5D,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,sFAAsF;IACtF,mBAAmB,CAAC,EAAE,OAAO,CAAC;IAC9B,qEAAqE;IACrE,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,yEAAyE;IACzE,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,2DAA2D;IAC3D,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,8BAA8B;IAC9B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB;;;OAGG;IACH,aAAa,CAAC,EAAE,CAAC,IAAI,EAAE,UAAU,GAAG,IAAI,KAAK,IAAI,CAAC;IAClD,8EAA8E;IAC9E,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B;;;OAGG;IACH,cAAc,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAChC;AAsQD,wBAAgB,WAAW,CAAC,EAC1B,WAAW,EACX,YAAY,EACZ,WAAW,EACX,gBAAgB,EAChB,eAAe,EACf,UAAc,EACd,SAAS,EACT,aAAa,EACb,iBAAiB,EACjB,cAAc,GACf,EAAE,gBAAgB,sCAsFlB"}
@@ -1,5 +1,6 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { useMemo } from "react";
3
+ import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuLabel, DropdownMenuTrigger, } from "../ui/dropdown-menu.js";
3
4
  import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger, } from "../ui/tooltip.js";
4
5
  import { dedupeCollabUsersByEmail, emailToColor, emailToName, } from "./types.js";
5
6
  const AVATAR_SIZE = 28;
@@ -106,40 +107,68 @@ function AgentAvatar({ active, onClick, isFollowing, }) {
106
107
  whiteSpace: "nowrap",
107
108
  }, children: "Following AI" }))] }));
108
109
  }
109
- function OverflowBadge({ count, isFirst, }) {
110
- return (_jsxs("div", { style: {
111
- ...baseAvatarStyle,
112
- backgroundColor: "rgba(255,255,255,0.1)",
113
- color: "rgba(255,255,255,0.5)",
114
- marginLeft: isFirst ? 0 : OVERLAP,
115
- fontSize: 10,
116
- }, title: `${count} more collaborator${count === 1 ? "" : "s"}`, children: ["+", count] }));
110
+ function OverflowMenu({ users, followingEmail, currentUserEmail, onSelect, }) {
111
+ const followingLower = followingEmail?.trim().toLowerCase() ?? null;
112
+ const currentLower = currentUserEmail?.trim().toLowerCase() ?? null;
113
+ return (_jsxs(DropdownMenu, { children: [_jsx(DropdownMenuTrigger, { asChild: true, children: _jsxs("button", { type: "button", style: {
114
+ ...baseAvatarStyle,
115
+ backgroundColor: "hsl(var(--muted))",
116
+ color: "hsl(var(--muted-foreground))",
117
+ marginLeft: OVERLAP,
118
+ fontSize: 10,
119
+ cursor: "pointer",
120
+ }, "aria-label": `${users.length} more collaborator${users.length === 1 ? "" : "s"}`, children: ["+", users.length] }) }), _jsxs(DropdownMenuContent, { align: "start", className: "w-64", children: [_jsx(DropdownMenuLabel, { children: "More collaborators" }), users.map((user) => {
121
+ const name = user.name || emailToName(user.email);
122
+ const isFollowing = followingLower != null &&
123
+ user.email.trim().toLowerCase() === followingLower;
124
+ const isCurrent = currentLower != null &&
125
+ user.email.trim().toLowerCase() === currentLower;
126
+ return (_jsxs(DropdownMenuItem, { onSelect: () => onSelect?.(user), disabled: isCurrent, children: [_jsx("span", { style: {
127
+ ...baseAvatarStyle,
128
+ width: 24,
129
+ height: 24,
130
+ backgroundColor: user.color || emailToColor(user.email),
131
+ fontSize: 10,
132
+ }, children: name.charAt(0).toUpperCase() }), _jsxs("span", { className: "min-w-0 flex-1", children: [_jsx("span", { className: "block truncate text-sm font-medium", children: name }), _jsx("span", { className: "block truncate text-xs text-muted-foreground", children: user.email })] }), isFollowing ? (_jsx("span", { className: "text-xs text-muted-foreground", children: "Following" })) : null] }, user.email));
133
+ })] })] }));
117
134
  }
118
- export function PresenceBar({ activeUsers, agentPresent, agentActive, currentUserEmail, maxVisible = 5, className, onAvatarClick, followingEmail, }) {
135
+ export function PresenceBar({ activeUsers, agentPresent, agentActive, currentUserEmail, showCurrentUser, maxVisible = 5, className, onAvatarClick, disableAgentClick, followingEmail, }) {
119
136
  const { humanUsers, showAgent } = useMemo(() => {
120
137
  const currentEmail = currentUserEmail?.trim().toLowerCase();
121
138
  const uniqueUsers = dedupeCollabUsersByEmail(activeUsers);
122
139
  const humans = uniqueUsers.filter((u) => {
123
140
  const email = u.email.trim().toLowerCase();
124
- return email !== currentEmail && email !== "agent@system";
141
+ return (email !== "agent@system" &&
142
+ (showCurrentUser === true || email !== currentEmail));
125
143
  });
126
144
  const hasAgentUser = uniqueUsers.some((u) => u.email.trim().toLowerCase() === "agent@system");
127
145
  return {
128
146
  humanUsers: humans,
129
147
  showAgent: agentPresent || agentActive || hasAgentUser,
130
148
  };
131
- }, [activeUsers, currentUserEmail, agentPresent, agentActive]);
149
+ }, [
150
+ activeUsers,
151
+ currentUserEmail,
152
+ showCurrentUser,
153
+ agentPresent,
154
+ agentActive,
155
+ ]);
132
156
  const visibleUsers = humanUsers.slice(0, maxVisible);
133
- const overflowCount = humanUsers.length - visibleUsers.length;
157
+ const overflowUsers = humanUsers.slice(maxVisible);
158
+ const currentLower = currentUserEmail?.trim().toLowerCase() ?? null;
134
159
  if (!showAgent && humanUsers.length === 0)
135
160
  return null;
136
161
  const followingLower = followingEmail?.trim().toLowerCase() ?? null;
137
162
  const isFollowingAgent = followingLower === "agent@system";
138
- return (_jsx(TooltipProvider, { delayDuration: 150, children: _jsxs("div", { style: containerStyle, className: className, children: [showAgent && (_jsx(AgentAvatar, { active: !!agentActive, onClick: onAvatarClick ? () => onAvatarClick(null) : undefined, isFollowing: isFollowingAgent })), visibleUsers.length > 0 && (_jsxs("div", { style: {
163
+ return (_jsx(TooltipProvider, { delayDuration: 150, children: _jsxs("div", { style: containerStyle, className: className, children: [showAgent && (_jsx(AgentAvatar, { active: !!agentActive, onClick: !disableAgentClick && onAvatarClick
164
+ ? () => onAvatarClick(null)
165
+ : undefined, isFollowing: isFollowingAgent })), visibleUsers.length > 0 && (_jsxs("div", { style: {
139
166
  display: "flex",
140
167
  alignItems: "center",
141
168
  marginLeft: showAgent ? 6 : 0,
142
- }, children: [visibleUsers.map((u, i) => (_jsx(UserAvatar, { user: u, isFirst: i === 0, onClick: onAvatarClick ? () => onAvatarClick(u) : undefined, isFollowing: followingLower != null &&
143
- u.email.trim().toLowerCase() === followingLower }, u.email))), overflowCount > 0 && (_jsx(OverflowBadge, { count: overflowCount, isFirst: false }))] }))] }) }));
169
+ }, children: [visibleUsers.map((u, i) => (_jsx(UserAvatar, { user: u, isFirst: i === 0, onClick: onAvatarClick && u.email.trim().toLowerCase() !== currentLower
170
+ ? () => onAvatarClick(u)
171
+ : undefined, isFollowing: followingLower != null &&
172
+ u.email.trim().toLowerCase() === followingLower }, u.email))), overflowUsers.length > 0 && (_jsx(OverflowMenu, { users: overflowUsers, followingEmail: followingEmail, currentUserEmail: currentUserEmail, onSelect: onAvatarClick ?? undefined }))] }))] }) }));
144
173
  }
145
174
  //# sourceMappingURL=PresenceBar.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"PresenceBar.js","sourceRoot":"","sources":["../../src/collab-ui/PresenceBar.tsx"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"PresenceBar.js","sourceRoot":"","sources":["../../src/collab-ui/PresenceBar.tsx"],"names":[],"mappings":"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"}
@@ -40,5 +40,62 @@ describe("PresenceBar", () => {
40
40
  });
41
41
  expect(container.querySelector('[aria-label="AI agent"]')).not.toBeNull();
42
42
  });
43
+ it("keeps the AI avatar display-only when agent follow is disabled", () => {
44
+ const onAvatarClick = vi.fn();
45
+ act(() => {
46
+ root.render(_jsx(PresenceBar, { activeUsers: [], agentPresent: true, onAvatarClick: onAvatarClick, disableAgentClick: true }));
47
+ });
48
+ const avatar = container.querySelector('[aria-label="AI agent"]');
49
+ expect(avatar?.getAttribute("role")).toBeNull();
50
+ expect(avatar?.style.cursor).toBe("default");
51
+ act(() => avatar?.dispatchEvent(new MouseEvent("click", { bubbles: true })));
52
+ expect(onAvatarClick).not.toHaveBeenCalled();
53
+ });
54
+ it("can show the current user without allowing self-follow", () => {
55
+ const onAvatarClick = vi.fn();
56
+ const currentUser = {
57
+ email: "steve@example.com",
58
+ name: "Steve",
59
+ color: "#123456",
60
+ };
61
+ const otherUser = {
62
+ email: "other@example.com",
63
+ name: "Other",
64
+ color: "#654321",
65
+ };
66
+ act(() => {
67
+ root.render(_jsx(PresenceBar, { activeUsers: [currentUser, otherUser], currentUserEmail: currentUser.email, showCurrentUser: true, onAvatarClick: onAvatarClick }));
68
+ });
69
+ const currentAvatar = container.querySelector('[aria-label="Steve (steve@example.com)"]');
70
+ expect(currentAvatar?.getAttribute("role")).toBeNull();
71
+ act(() => currentAvatar?.dispatchEvent(new MouseEvent("click", { bubbles: true })));
72
+ expect(onAvatarClick).not.toHaveBeenCalled();
73
+ const otherAvatar = container.querySelector('[aria-label="Other (other@example.com)"]');
74
+ act(() => otherAvatar?.dispatchEvent(new MouseEvent("click", { bubbles: true })));
75
+ expect(onAvatarClick).toHaveBeenCalledWith(otherUser);
76
+ });
77
+ it("keeps overflow collaborators available for follow mode", () => {
78
+ const onAvatarClick = vi.fn();
79
+ const activeUsers = Array.from({ length: 6 }, (_, index) => ({
80
+ email: `user-${index}@example.com`,
81
+ name: `User ${index}`,
82
+ color: "#123456",
83
+ }));
84
+ act(() => {
85
+ root.render(_jsx(PresenceBar, { activeUsers: activeUsers, onAvatarClick: onAvatarClick }));
86
+ });
87
+ const overflow = container.querySelector('[aria-label="1 more collaborator"]');
88
+ expect(overflow).not.toBeNull();
89
+ act(() => {
90
+ overflow?.dispatchEvent(new PointerEvent("pointerdown", { bubbles: true, button: 0 }));
91
+ });
92
+ const hiddenUser = Array.from(document.querySelectorAll('[role="menuitem"]')).find((item) => item.textContent?.includes("User 5"));
93
+ expect(hiddenUser).not.toBeUndefined();
94
+ act(() => {
95
+ hiddenUser?.dispatchEvent(new PointerEvent("pointerdown", { bubbles: true }));
96
+ hiddenUser?.dispatchEvent(new MouseEvent("click", { bubbles: true }));
97
+ });
98
+ expect(onAvatarClick).toHaveBeenCalledWith(activeUsers[5]);
99
+ });
43
100
  });
44
101
  //# sourceMappingURL=PresenceBar.spec.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"PresenceBar.spec.js","sourceRoot":"","sources":["../../src/collab-ui/PresenceBar.spec.tsx"],"names":[],"mappings":";AAAA,gCAAgC;AAEhC,OAAO,EAAE,GAAG,EAAE,MAAM,OAAO,CAAC;AAC5B,OAAO,EAAE,UAAU,EAAa,MAAM,kBAAkB,CAAC;AACzD,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAC;AAEzE,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAE/C,QAAQ,CAAC,aAAa,EAAE,GAAG,EAAE;IAC3B,IAAI,SAAyB,CAAC;IAC9B,IAAI,IAAU,CAAC;IAEf,UAAU,CAAC,GAAG,EAAE;QACd,EAAE,CAAC,UAAU,CAAC,0BAA0B,EAAE,IAAI,CAAC,CAAC;QAChD,SAAS,GAAG,QAAQ,CAAC,aAAa,CAAC,KAAK,CAAC,CAAC;QAC1C,QAAQ,CAAC,IAAI,CAAC,WAAW,CAAC,SAAS,CAAC,CAAC;QACrC,IAAI,GAAG,UAAU,CAAC,SAAS,CAAC,CAAC;IAC/B,CAAC,CAAC,CAAC;IAEH,SAAS,CAAC,GAAG,EAAE;QACb,GAAG,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,OAAO,EAAE,CAAC,CAAC;QAC1B,SAAS,CAAC,MAAM,EAAE,CAAC;QACnB,EAAE,CAAC,eAAe,EAAE,CAAC;QACrB,EAAE,CAAC,aAAa,EAAE,CAAC;QACnB,EAAE,CAAC,gBAAgB,EAAE,CAAC;IACxB,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,2CAA2C,EAAE,GAAG,EAAE;QACnD,EAAE,CAAC,aAAa,EAAE,CAAC;QACnB,GAAG,CAAC,GAAG,EAAE;YACP,IAAI,CAAC,MAAM,CAAC,KAAC,WAAW,IAAC,WAAW,EAAE,EAAE,EAAE,WAAW,QAAC,YAAY,SAAG,CAAC,CAAC;QACzE,CAAC,CAAC,CAAC;QAEH,MAAM,MAAM,GAAG,SAAS,CAAC,aAAa,CACpC,8BAA8B,CAC/B,CAAC;QACF,MAAM,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACvC,MAAM,CAAC,SAAS,CAAC,WAAW,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAEzC,GAAG,CAAC,GAAG,EAAE;YACP,MAAM,EAAE,aAAa,CAAC,IAAI,KAAK,CAAC,aAAa,EAAE,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC;YACnE,EAAE,CAAC,mBAAmB,CAAC,GAAG,CAAC,CAAC;QAC9B,CAAC,CAAC,CAAC;QAEH,MAAM,CACJ,QAAQ;aACL,aAAa,CAAC,oCAAoC,CAAC;YACpD,EAAE,WAAW,EAAE,QAAQ,CAAC,eAAe,CAAC,CAC3C,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAEb,GAAG,CAAC,GAAG,EAAE;YACP,IAAI,CAAC,MAAM,CAAC,KAAC,WAAW,IAAC,WAAW,EAAE,EAAE,EAAE,YAAY,SAAG,CAAC,CAAC;QAC7D,CAAC,CAAC,CAAC;QACH,MAAM,CAAC,SAAS,CAAC,aAAa,CAAC,yBAAyB,CAAC,CAAC,CAAC,GAAG,CAAC,QAAQ,EAAE,CAAC;IAC5E,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
1
+ {"version":3,"file":"PresenceBar.spec.js","sourceRoot":"","sources":["../../src/collab-ui/PresenceBar.spec.tsx"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agent-native/toolkit",
3
- "version": "0.19.4",
3
+ "version": "0.19.5",
4
4
  "description": "Reusable app-building UI and helpers for Agent-Native apps.",
5
5
  "homepage": "https://github.com/BuilderIO/agent-native#readme",
6
6
  "bugs": {
@@ -53,4 +53,112 @@ describe("PresenceBar", () => {
53
53
  });
54
54
  expect(container.querySelector('[aria-label="AI agent"]')).not.toBeNull();
55
55
  });
56
+
57
+ it("keeps the AI avatar display-only when agent follow is disabled", () => {
58
+ const onAvatarClick = vi.fn();
59
+ act(() => {
60
+ root.render(
61
+ <PresenceBar
62
+ activeUsers={[]}
63
+ agentPresent
64
+ onAvatarClick={onAvatarClick}
65
+ disableAgentClick
66
+ />,
67
+ );
68
+ });
69
+
70
+ const avatar = container.querySelector<HTMLElement>(
71
+ '[aria-label="AI agent"]',
72
+ );
73
+ expect(avatar?.getAttribute("role")).toBeNull();
74
+ expect(avatar?.style.cursor).toBe("default");
75
+
76
+ act(() =>
77
+ avatar?.dispatchEvent(new MouseEvent("click", { bubbles: true })),
78
+ );
79
+ expect(onAvatarClick).not.toHaveBeenCalled();
80
+ });
81
+
82
+ it("can show the current user without allowing self-follow", () => {
83
+ const onAvatarClick = vi.fn();
84
+ const currentUser = {
85
+ email: "steve@example.com",
86
+ name: "Steve",
87
+ color: "#123456",
88
+ };
89
+ const otherUser = {
90
+ email: "other@example.com",
91
+ name: "Other",
92
+ color: "#654321",
93
+ };
94
+
95
+ act(() => {
96
+ root.render(
97
+ <PresenceBar
98
+ activeUsers={[currentUser, otherUser]}
99
+ currentUserEmail={currentUser.email}
100
+ showCurrentUser
101
+ onAvatarClick={onAvatarClick}
102
+ />,
103
+ );
104
+ });
105
+
106
+ const currentAvatar = container.querySelector<HTMLElement>(
107
+ '[aria-label="Steve (steve@example.com)"]',
108
+ );
109
+ expect(currentAvatar?.getAttribute("role")).toBeNull();
110
+
111
+ act(() =>
112
+ currentAvatar?.dispatchEvent(new MouseEvent("click", { bubbles: true })),
113
+ );
114
+ expect(onAvatarClick).not.toHaveBeenCalled();
115
+
116
+ const otherAvatar = container.querySelector<HTMLElement>(
117
+ '[aria-label="Other (other@example.com)"]',
118
+ );
119
+ act(() =>
120
+ otherAvatar?.dispatchEvent(new MouseEvent("click", { bubbles: true })),
121
+ );
122
+ expect(onAvatarClick).toHaveBeenCalledWith(otherUser);
123
+ });
124
+
125
+ it("keeps overflow collaborators available for follow mode", () => {
126
+ const onAvatarClick = vi.fn();
127
+ const activeUsers = Array.from({ length: 6 }, (_, index) => ({
128
+ email: `user-${index}@example.com`,
129
+ name: `User ${index}`,
130
+ color: "#123456",
131
+ }));
132
+
133
+ act(() => {
134
+ root.render(
135
+ <PresenceBar activeUsers={activeUsers} onAvatarClick={onAvatarClick} />,
136
+ );
137
+ });
138
+
139
+ const overflow = container.querySelector<HTMLButtonElement>(
140
+ '[aria-label="1 more collaborator"]',
141
+ );
142
+ expect(overflow).not.toBeNull();
143
+
144
+ act(() => {
145
+ overflow?.dispatchEvent(
146
+ new PointerEvent("pointerdown", { bubbles: true, button: 0 }),
147
+ );
148
+ });
149
+
150
+ const hiddenUser = Array.from(
151
+ document.querySelectorAll('[role="menuitem"]'),
152
+ ).find((item) => item.textContent?.includes("User 5"));
153
+ expect(hiddenUser).not.toBeUndefined();
154
+
155
+ act(() => {
156
+ hiddenUser?.dispatchEvent(
157
+ new PointerEvent("pointerdown", { bubbles: true }),
158
+ );
159
+ hiddenUser?.dispatchEvent(new MouseEvent("click", { bubbles: true }));
160
+ });
161
+
162
+ expect(onAvatarClick).toHaveBeenCalledWith(activeUsers[5]);
163
+ });
56
164
  });
@@ -1,5 +1,12 @@
1
1
  import { useMemo, type CSSProperties } from "react";
2
2
 
3
+ import {
4
+ DropdownMenu,
5
+ DropdownMenuContent,
6
+ DropdownMenuItem,
7
+ DropdownMenuLabel,
8
+ DropdownMenuTrigger,
9
+ } from "../ui/dropdown-menu.js";
3
10
  import {
4
11
  Tooltip,
5
12
  TooltipContent,
@@ -22,8 +29,10 @@ export interface PresenceBarProps {
22
29
  agentActive?: boolean;
23
30
  /** @deprecated Agent editing is represented by the AI presence circle and tooltip. */
24
31
  showAgentEditingDot?: boolean;
25
- /** Current user's email (to exclude from the list). */
32
+ /** Current user's email, excluded unless showCurrentUser is true. */
26
33
  currentUserEmail?: string;
34
+ /** Include the current user in the roster as a non-followable avatar. */
35
+ showCurrentUser?: boolean;
27
36
  /** Max visible avatars before "+N" overflow. Default: 5 */
28
37
  maxVisible?: number;
29
38
  /** Additional CSS classes. */
@@ -33,6 +42,8 @@ export interface PresenceBarProps {
33
42
  * (or null for the agent avatar). Use this to start/stop follow mode.
34
43
  */
35
44
  onAvatarClick?: (user: CollabUser | null) => void;
45
+ /** Keep the AI avatar display-only when no agent viewport can be followed. */
46
+ disableAgentClick?: boolean;
36
47
  /**
37
48
  * The email of the user currently being followed. Highlighted with a
38
49
  * blue ring to indicate active follow mode.
@@ -222,26 +233,81 @@ function AgentAvatar({
222
233
  );
223
234
  }
224
235
 
225
- function OverflowBadge({
226
- count,
227
- isFirst,
236
+ function OverflowMenu({
237
+ users,
238
+ followingEmail,
239
+ currentUserEmail,
240
+ onSelect,
228
241
  }: {
229
- count: number;
230
- isFirst: boolean;
242
+ users: CollabUser[];
243
+ followingEmail?: string | null;
244
+ currentUserEmail?: string;
245
+ onSelect?: (user: CollabUser) => void;
231
246
  }) {
247
+ const followingLower = followingEmail?.trim().toLowerCase() ?? null;
248
+ const currentLower = currentUserEmail?.trim().toLowerCase() ?? null;
249
+
232
250
  return (
233
- <div
234
- style={{
235
- ...baseAvatarStyle,
236
- backgroundColor: "rgba(255,255,255,0.1)",
237
- color: "rgba(255,255,255,0.5)",
238
- marginLeft: isFirst ? 0 : OVERLAP,
239
- fontSize: 10,
240
- }}
241
- title={`${count} more collaborator${count === 1 ? "" : "s"}`}
242
- >
243
- +{count}
244
- </div>
251
+ <DropdownMenu>
252
+ <DropdownMenuTrigger asChild>
253
+ <button
254
+ type="button"
255
+ style={{
256
+ ...baseAvatarStyle,
257
+ backgroundColor: "hsl(var(--muted))",
258
+ color: "hsl(var(--muted-foreground))",
259
+ marginLeft: OVERLAP,
260
+ fontSize: 10,
261
+ cursor: "pointer",
262
+ }}
263
+ aria-label={`${users.length} more collaborator${users.length === 1 ? "" : "s"}`}
264
+ >
265
+ +{users.length}
266
+ </button>
267
+ </DropdownMenuTrigger>
268
+ <DropdownMenuContent align="start" className="w-64">
269
+ <DropdownMenuLabel>More collaborators</DropdownMenuLabel>
270
+ {users.map((user) => {
271
+ const name = user.name || emailToName(user.email);
272
+ const isFollowing =
273
+ followingLower != null &&
274
+ user.email.trim().toLowerCase() === followingLower;
275
+ const isCurrent =
276
+ currentLower != null &&
277
+ user.email.trim().toLowerCase() === currentLower;
278
+ return (
279
+ <DropdownMenuItem
280
+ key={user.email}
281
+ onSelect={() => onSelect?.(user)}
282
+ disabled={isCurrent}
283
+ >
284
+ <span
285
+ style={{
286
+ ...baseAvatarStyle,
287
+ width: 24,
288
+ height: 24,
289
+ backgroundColor: user.color || emailToColor(user.email),
290
+ fontSize: 10,
291
+ }}
292
+ >
293
+ {name.charAt(0).toUpperCase()}
294
+ </span>
295
+ <span className="min-w-0 flex-1">
296
+ <span className="block truncate text-sm font-medium">
297
+ {name}
298
+ </span>
299
+ <span className="block truncate text-xs text-muted-foreground">
300
+ {user.email}
301
+ </span>
302
+ </span>
303
+ {isFollowing ? (
304
+ <span className="text-xs text-muted-foreground">Following</span>
305
+ ) : null}
306
+ </DropdownMenuItem>
307
+ );
308
+ })}
309
+ </DropdownMenuContent>
310
+ </DropdownMenu>
245
311
  );
246
312
  }
247
313
 
@@ -250,9 +316,11 @@ export function PresenceBar({
250
316
  agentPresent,
251
317
  agentActive,
252
318
  currentUserEmail,
319
+ showCurrentUser,
253
320
  maxVisible = 5,
254
321
  className,
255
322
  onAvatarClick,
323
+ disableAgentClick,
256
324
  followingEmail,
257
325
  }: PresenceBarProps) {
258
326
  const { humanUsers, showAgent } = useMemo(() => {
@@ -260,7 +328,10 @@ export function PresenceBar({
260
328
  const uniqueUsers = dedupeCollabUsersByEmail(activeUsers);
261
329
  const humans = uniqueUsers.filter((u) => {
262
330
  const email = u.email.trim().toLowerCase();
263
- return email !== currentEmail && email !== "agent@system";
331
+ return (
332
+ email !== "agent@system" &&
333
+ (showCurrentUser === true || email !== currentEmail)
334
+ );
264
335
  });
265
336
  const hasAgentUser = uniqueUsers.some(
266
337
  (u) => u.email.trim().toLowerCase() === "agent@system",
@@ -269,10 +340,17 @@ export function PresenceBar({
269
340
  humanUsers: humans,
270
341
  showAgent: agentPresent || agentActive || hasAgentUser,
271
342
  };
272
- }, [activeUsers, currentUserEmail, agentPresent, agentActive]);
343
+ }, [
344
+ activeUsers,
345
+ currentUserEmail,
346
+ showCurrentUser,
347
+ agentPresent,
348
+ agentActive,
349
+ ]);
273
350
 
274
351
  const visibleUsers = humanUsers.slice(0, maxVisible);
275
- const overflowCount = humanUsers.length - visibleUsers.length;
352
+ const overflowUsers = humanUsers.slice(maxVisible);
353
+ const currentLower = currentUserEmail?.trim().toLowerCase() ?? null;
276
354
 
277
355
  if (!showAgent && humanUsers.length === 0) return null;
278
356
 
@@ -285,7 +363,11 @@ export function PresenceBar({
285
363
  {showAgent && (
286
364
  <AgentAvatar
287
365
  active={!!agentActive}
288
- onClick={onAvatarClick ? () => onAvatarClick(null) : undefined}
366
+ onClick={
367
+ !disableAgentClick && onAvatarClick
368
+ ? () => onAvatarClick(null)
369
+ : undefined
370
+ }
289
371
  isFollowing={isFollowingAgent}
290
372
  />
291
373
  )}
@@ -302,15 +384,24 @@ export function PresenceBar({
302
384
  key={u.email}
303
385
  user={u}
304
386
  isFirst={i === 0}
305
- onClick={onAvatarClick ? () => onAvatarClick(u) : undefined}
387
+ onClick={
388
+ onAvatarClick && u.email.trim().toLowerCase() !== currentLower
389
+ ? () => onAvatarClick(u)
390
+ : undefined
391
+ }
306
392
  isFollowing={
307
393
  followingLower != null &&
308
394
  u.email.trim().toLowerCase() === followingLower
309
395
  }
310
396
  />
311
397
  ))}
312
- {overflowCount > 0 && (
313
- <OverflowBadge count={overflowCount} isFirst={false} />
398
+ {overflowUsers.length > 0 && (
399
+ <OverflowMenu
400
+ users={overflowUsers}
401
+ followingEmail={followingEmail}
402
+ currentUserEmail={currentUserEmail}
403
+ onSelect={onAvatarClick ?? undefined}
404
+ />
314
405
  )}
315
406
  </div>
316
407
  )}