diff --git a/desktop/src/features/agents/ui/TeamIdentityCard.tsx b/desktop/src/features/agents/ui/TeamIdentityCard.tsx
index 3109cf600..8716a8ee6 100644
--- a/desktop/src/features/agents/ui/TeamIdentityCard.tsx
+++ b/desktop/src/features/agents/ui/TeamIdentityCard.tsx
@@ -1,6 +1,7 @@
import type { ReactNode } from "react";
import { Link, Users } from "lucide-react";
+import { ProfileAvatar } from "@/features/profile/ui/ProfileAvatar";
import type { AgentPersona } from "@/shared/api/types";
import { Card } from "@/shared/ui/card";
import { Tooltip, TooltipContent, TooltipTrigger } from "@/shared/ui/tooltip";
@@ -127,40 +128,83 @@ function TeamAvatarCluster({
role="img"
>
{items.map((item, index) => (
-
0 ? -overlap : 0,
- width: size,
- zIndex: index + 1,
- ...(index < items.length - 1
- ? {
- WebkitMask: `radial-gradient(circle ${maskRadius}px at calc(100% + ${maskOffset}px) 50%, transparent 99%, #fff 100%)`,
- mask: `radial-gradient(circle ${maskRadius}px at calc(100% + ${maskOffset}px) 50%, transparent 99%, #fff 100%)`,
- }
- : null),
- }}
- >
- {item.kind === "persona" ? (
-
- ) : (
-
- +{item.count}
-
- )}
-
+ maskOffset={maskOffset}
+ maskRadius={maskRadius}
+ overlap={overlap}
+ size={size}
+ />
))}
);
}
+function TeamClusterItem({
+ index,
+ isMasked,
+ item,
+ maskOffset,
+ maskRadius,
+ overlap,
+ size,
+}: {
+ index: number;
+ isMasked: boolean;
+ item: ClusterItem;
+ maskOffset: number;
+ maskRadius: number;
+ overlap: number;
+ size: number;
+}) {
+ const avatarUrl =
+ item.kind === "persona" ? (item.persona.avatarUrl?.trim() ?? null) : null;
+
+ return (
+ 0 ? -overlap : 0,
+ width: size,
+ zIndex: index + 1,
+ ...(isMasked
+ ? {
+ WebkitMask: `radial-gradient(circle ${maskRadius}px at calc(100% + ${maskOffset}px) 50%, transparent 99%, #fff 100%)`,
+ mask: `radial-gradient(circle ${maskRadius}px at calc(100% + ${maskOffset}px) 50%, transparent 99%, #fff 100%)`,
+ }
+ : null),
+ }}
+ >
+ {item.kind === "persona" ? (
+ avatarUrl ? (
+
+ ) : (
+
+ )
+ ) : (
+
+ +{item.count}
+
+ )}
+
+ );
+}
+
function buildClusterItems(
personas: AgentPersona[],
memberCount: number,
diff --git a/desktop/src/features/agents/ui/UnifiedAgentsSection.tsx b/desktop/src/features/agents/ui/UnifiedAgentsSection.tsx
index 70a807407..61bd70351 100644
--- a/desktop/src/features/agents/ui/UnifiedAgentsSection.tsx
+++ b/desktop/src/features/agents/ui/UnifiedAgentsSection.tsx
@@ -252,7 +252,11 @@ function AgentPersonaCard({
const modelLabel = formatAgentModelLabel(agent?.model ?? persona.model);
const profileQuery = useUserProfileQuery(agent?.pubkey);
const avatarUrl = agent
- ? (profileQuery.data?.avatarUrl ?? agent.avatarUrl ?? persona.avatarUrl)
+ ? firstAvatarUrl(
+ profileQuery.data?.avatarUrl,
+ agent.avatarUrl,
+ persona.avatarUrl,
+ )
: persona.avatarUrl;
return (
@@ -286,7 +290,7 @@ function StandaloneAgentCard({
return (
0 ? trimmed : "Auto";
}
+function firstAvatarUrl(
+ ...candidates: Array
+): string | null {
+ for (const candidate of candidates) {
+ const trimmed = candidate?.trim();
+ if (trimmed) return trimmed;
+ }
+ return null;
+}
+
function SectionHeader({
fileInputRef,
handleFileChange,
diff --git a/desktop/src/features/profile/ui/UserProfilePanel.tsx b/desktop/src/features/profile/ui/UserProfilePanel.tsx
index c9eae5da8..ab25be954 100644
--- a/desktop/src/features/profile/ui/UserProfilePanel.tsx
+++ b/desktop/src/features/profile/ui/UserProfilePanel.tsx
@@ -77,6 +77,7 @@ import {
resolveProfileDisplayName,
type UserProfilePanelProps,
useRetainedPersona,
+ withProfileAvatarFallback,
} from "@/features/profile/ui/UserProfilePanelUtils";
import { useUserStatusQuery } from "@/features/user-status/hooks";
import { useAgentSession } from "@/shared/context/AgentSessionContext";
@@ -224,16 +225,15 @@ export function UserProfilePanel({
const unfollowMutation = useUnfollowMutation(currentPubkey);
const { onOpenAgentSession } = useAgentSession();
const { goChannel } = useAppNavigation();
-
const profile = React.useMemo(() => {
const baseProfile =
profileQuery.data ??
(resolvedPersona ? buildPersonaDraftProfile(resolvedPersona) : undefined);
- const fallbackAvatarUrl =
- managedAgent?.avatarUrl ?? resolvedPersona?.avatarUrl ?? null;
- return baseProfile && !baseProfile.avatarUrl && fallbackAvatarUrl
- ? { ...baseProfile, avatarUrl: fallbackAvatarUrl }
- : baseProfile;
+ return withProfileAvatarFallback(
+ baseProfile,
+ managedAgent?.avatarUrl,
+ resolvedPersona?.avatarUrl,
+ );
}, [managedAgent?.avatarUrl, profileQuery.data, resolvedPersona]);
const presenceStatus = pubkeyLower
? presenceQuery.data?.[pubkeyLower]
diff --git a/desktop/src/features/profile/ui/UserProfilePanelUtils.ts b/desktop/src/features/profile/ui/UserProfilePanelUtils.ts
index b5e02a598..26cf4d3c4 100644
--- a/desktop/src/features/profile/ui/UserProfilePanelUtils.ts
+++ b/desktop/src/features/profile/ui/UserProfilePanelUtils.ts
@@ -135,6 +135,29 @@ export function buildPersonaDraftProfile(persona: AgentPersona): Profile {
};
}
+export function resolveProfileAvatarUrl(
+ ...candidates: Array
+): string | null {
+ for (const candidate of candidates) {
+ const trimmed = candidate?.trim();
+ if (trimmed) return trimmed;
+ }
+ return null;
+}
+
+export function withProfileAvatarFallback(
+ profile: Profile | undefined,
+ ...fallbackAvatarUrls: Array
+): Profile | undefined {
+ const avatarUrl = resolveProfileAvatarUrl(
+ profile?.avatarUrl,
+ ...fallbackAvatarUrls,
+ );
+ return profile && avatarUrl !== profile.avatarUrl
+ ? { ...profile, avatarUrl }
+ : profile;
+}
+
export function resolveProfileDisplayName({
persona,
profile,