mirror of
https://github.com/block/buzz.git
synced 2026-11-06 01:11:33 +00:00
feat: render agent avatars as squircles (#7106)
## Summary - render every agent/AI identity as a 30% squircle across desktop and mobile while keeping human avatars circular - propagate agent identity through message, thread, profile, reaction, member, DM, search, workflow, project, huddle, forum, pulse, and agent-management surfaces - preserve squircle geometry for fallbacks, focus/status treatments, add-agent controls, and overlapping avatar outlines (`calc(30% + 2px)` for the outer background) ### Related issue None found. This change was requested and visually reviewed in the originating Buzz thread. ### Testing - `just desktop-test` — 5,799 passed - `just mobile-test` — 2,008 passed - pre-push gates passed at `0d59d77b120dcb90aac2f918e422c11c9fa5353b`: desktop check, TypeScript typecheck, desktop full test suite, mobile format/analyze and full test suite, Rust tests, Tauri checks, and differential file-size gate - deterministic desktop visual sweep covered channel messages/thread summaries; thread, subthread, and sub-subthread depths; reactions and reactor popovers; hover/full profiles; added-to-channel activity; channel members/settings; agent library/team overlaps; agent creation; mention autocomplete; and DM header/sidebar/settings ### UI evidence The complete labeled visual matrix is available in the originating Buzz review thread. GitHub-hosted copies will be added in a follow-up PR comment using the repository screenshot script. --------- Signed-off-by: Fizz <dae5f6af70b8695a8b83c8deae555f63be41630ec2b8cd493e41a439c9527dd8@buzz.block.builderlab.xyz> Co-authored-by: Fizz <dae5f6af70b8695a8b83c8deae555f63be41630ec2b8cd493e41a439c9527dd8@buzz.block.builderlab.xyz>
This commit is contained in:
@@ -37,6 +37,7 @@ type UserAvatarProps = {
|
||||
displayName: string;
|
||||
size?: UserAvatarSize;
|
||||
accent?: boolean;
|
||||
shape?: "circle" | "squircle";
|
||||
className?: string;
|
||||
fallbackDelayMs?: number;
|
||||
testId?: string;
|
||||
@@ -47,6 +48,7 @@ export function UserAvatar({
|
||||
displayName,
|
||||
size = "md",
|
||||
accent = false,
|
||||
shape,
|
||||
className,
|
||||
fallbackDelayMs = 200,
|
||||
testId,
|
||||
@@ -61,12 +63,20 @@ export function UserAvatar({
|
||||
: avatarUrl
|
||||
? rewriteRelayUrl(avatarUrl)
|
||||
: null;
|
||||
const resolvedShape = shape ?? "circle";
|
||||
const radiusClass =
|
||||
resolvedShape === "squircle" ? "rounded-[30%]" : "rounded-full";
|
||||
|
||||
return (
|
||||
<Avatar
|
||||
// Animated avatars carry their own backdrop disc and transparent
|
||||
// surroundings — any container fill would flatten the pop-out.
|
||||
className={cn(sizeClasses[size], !animated && "shadow-xs", className)}
|
||||
className={cn(
|
||||
sizeClasses[size],
|
||||
radiusClass,
|
||||
!animated && "shadow-xs",
|
||||
className,
|
||||
)}
|
||||
data-testid={testId}
|
||||
onMouseEnter={animated ? () => setIsHovered(true) : undefined}
|
||||
onMouseLeave={animated ? () => setIsHovered(false) : undefined}
|
||||
|
||||
@@ -43,7 +43,6 @@ import {
|
||||
saveReviewPlaybackPosition,
|
||||
setVideoReviewOpen,
|
||||
} from "./videoPlayerState";
|
||||
|
||||
type VideoReviewReaction = {
|
||||
emoji: string;
|
||||
emojiUrl?: string;
|
||||
@@ -55,11 +54,11 @@ type VideoReviewReaction = {
|
||||
avatarUrl: string | null;
|
||||
}>;
|
||||
};
|
||||
|
||||
export type VideoReviewComment = {
|
||||
id: string;
|
||||
author: string;
|
||||
avatarUrl?: string | null;
|
||||
isAgent?: boolean;
|
||||
body: string;
|
||||
createdAt: number;
|
||||
time: string;
|
||||
@@ -67,7 +66,6 @@ export type VideoReviewComment = {
|
||||
parentId?: string | null;
|
||||
reactions?: VideoReviewReaction[];
|
||||
};
|
||||
|
||||
export type VideoReviewContext = {
|
||||
channelId?: string | null;
|
||||
channelName?: string;
|
||||
@@ -91,7 +89,6 @@ export type VideoReviewContext = {
|
||||
rootEventId?: string;
|
||||
title?: string;
|
||||
};
|
||||
|
||||
type VideoPlayerProps = {
|
||||
src: string;
|
||||
poster?: string;
|
||||
@@ -108,14 +105,12 @@ type VideoPlayerProps = {
|
||||
/** imeta `filename`, used as the save-dialog name. */
|
||||
filename?: string;
|
||||
};
|
||||
|
||||
type TimecodedComment = {
|
||||
comment: VideoReviewComment;
|
||||
seconds: number | null;
|
||||
timecode: string | null;
|
||||
text: string;
|
||||
};
|
||||
|
||||
const QUICK_REACTIONS = ["😂", "😍", "😮", "🙌", "👍", "👎"];
|
||||
const DEFAULT_PLAYBACK_SPEED = 1;
|
||||
const INLINE_SPEED_CONTROL_MIN_WIDTH = 220;
|
||||
@@ -1813,6 +1808,9 @@ function VideoReviewDialog({
|
||||
avatarUrl={item.comment.avatarUrl ?? null}
|
||||
className="h-4 w-4 shadow-none"
|
||||
displayName={item.comment.author}
|
||||
shape={
|
||||
item.comment.isAgent ? "squircle" : "circle"
|
||||
}
|
||||
size="xs"
|
||||
/>
|
||||
</button>
|
||||
@@ -2143,6 +2141,7 @@ function VideoReviewCommentBody({
|
||||
avatarUrl={item.comment.avatarUrl ?? null}
|
||||
className="h-6 w-6 shadow-none"
|
||||
displayName={item.comment.author}
|
||||
shape={item.comment.isAgent ? "squircle" : "circle"}
|
||||
size="xs"
|
||||
/>
|
||||
<p className="truncate text-sm font-semibold text-foreground">
|
||||
|
||||
Reference in New Issue
Block a user