From 432cdcd064361c6f8c83c8065fce532483df2296 Mon Sep 17 00:00:00 2001 From: Sawyer Date: Wed, 30 Sep 2026 14:14:43 -0700 Subject: [PATCH] fix(web): worker avatars are hue-tinted rounded squares like the mockup, with the 8px status dot (CL-9663) --- apps/web/src/bench/bench-pill.tsx | 7 +------ apps/web/src/chat/avatar.css | 31 +++++++++++++++++++++++++++++++ apps/web/src/chat/avatar.tsx | 16 +++++++++++++++- 3 files changed, 47 insertions(+), 7 deletions(-) create mode 100644 apps/web/src/chat/avatar.css diff --git a/apps/web/src/bench/bench-pill.tsx b/apps/web/src/bench/bench-pill.tsx index fe309c7aa..def8001f0 100644 --- a/apps/web/src/bench/bench-pill.tsx +++ b/apps/web/src/bench/bench-pill.tsx @@ -42,12 +42,7 @@ export function BenchPill({ ) ) : ( - + )} {benchName} {statusLine(status, worker?.name)} diff --git a/apps/web/src/chat/avatar.css b/apps/web/src/chat/avatar.css new file mode 100644 index 000000000..fc992bd59 --- /dev/null +++ b/apps/web/src/chat/avatar.css @@ -0,0 +1,31 @@ +/* Workers are hue-tinted rounded squares, people are ink circles. Unlayered, + so it wins over react-ui's utility-layer tone fill. */ +.wb-av--orange { + background: var(--hue-orange-bg); + color: var(--hue-ink); +} +.wb-av--blue { + background: var(--hue-blue-bg); + color: var(--hue-ink); +} +.wb-av--green { + background: var(--hue-green-bg); + color: var(--hue-ink); +} +.wb-av--person { + background: var(--ink); + color: var(--canvas); +} + +/* The status dot is the only round non-initials child. */ +.wb-av > span.rounded-full { + width: 8px; + height: 8px; + right: -2px; + bottom: -2px; + background: var(--success); + --tw-ring-color: var(--surface); +} +.wb-av > span.rounded-full.bg-primary-emphasis { + background: var(--action); +} diff --git a/apps/web/src/chat/avatar.tsx b/apps/web/src/chat/avatar.tsx index 83a78bfde..637ef7978 100644 --- a/apps/web/src/chat/avatar.tsx +++ b/apps/web/src/chat/avatar.tsx @@ -8,6 +8,7 @@ import { } from "@corbits/react-ui"; import { agentInitials } from "@/chat/threads-api"; +import "./avatar.css"; // Token references, not hex, so no product code hardcodes a color value. export const AVATAR_COLORS = ["--avatar-1", "--avatar-2", "--avatar-3", "--avatar-4"] as const; @@ -32,6 +33,14 @@ export const avatarColorClass: Record = { "--avatar-4": "bg-(--avatar-4) text-black", }; +const WORKER_HUES = ["orange", "blue", "green"] as const; + +// Hashed off the name: each bench deploys its own copy of a worker with a new +// principal id, so the id would recolor one worker per bench. +function workerHueClass(name: string): string { + return `wb-av--${WORKER_HUES[hashPrincipal(name) % WORKER_HUES.length]}`; +} + export function hashPrincipal(principalId: string): number { let hash = 0; for (let index = 0; index < principalId.length; index += 1) { @@ -98,7 +107,12 @@ export function WorkbenchAvatar({ shape={kind === "person" ? "circle" : "square"} orbit={kind === "worker" && status === "working"} {...(status === undefined ? {} : { status })} - {...(className === undefined ? {} : { className })} + className={[ + kind === "person" ? "wb-av wb-av--person" : `wb-av ${workerHueClass(name)}`, + className, + ] + .filter((c) => c !== undefined) + .join(" ")} /> ); }