From 9c740c0dda31744bf061745bd69cf10c7d229f05 Mon Sep 17 00:00:00 2001 From: klopez4212 Date: Mon, 22 Jun 2026 16:01:50 +0100 Subject: [PATCH] Fix agent avatars in cards and teams --- .../features/agents/ui/TeamIdentityCard.tsx | 98 ++++++++++++++----- .../agents/ui/UnifiedAgentsSection.tsx | 18 +++- .../features/profile/ui/UserProfilePanel.tsx | 12 +-- .../profile/ui/UserProfilePanelUtils.ts | 23 +++++ 4 files changed, 116 insertions(+), 35 deletions(-) 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,