/** * Deterministic avatar background color per user/topic ID. Algorithm (Java-style String#hashCode, * rolling `hash = hash*31 + charCode`) ported from the old app's `letterTileColorId` -- but the old * app's palette had 16 colors (`% 16`) where this design's token palette (index.css's --color-avatar-1 * through -11) has 11, so the modulus is adjusted to match; the hash function itself is unchanged. */ function stringToColorHash(value: string): number { let hash = 0; for (let i = 0; i < value.length; i++) { hash = (hash << 5) - hash + value.charCodeAt(i); hash = hash & hash; } return hash; } const AVATAR_COLOR_COUNT = 11; /** 1-based index matching the --color-avatar-1..11 tokens in index.css. */ export function avatarColorIndex(id: string): number { return (Math.abs(stringToColorHash(id)) % AVATAR_COLOR_COUNT) + 1; } /** * Written out as literal strings, NOT template-generated (`bg-avatar-${i}`) -- Tailwind's build * finds utility classes by statically scanning source text for complete class-name strings, so a * dynamically-assembled name is invisible to it and never gets its CSS generated at all. That exact * bug shipped here once already: every avatar rendered with a fully transparent background instead * of its color, discovered by checking computed style, not by reading the class name off the DOM * (which looked correct). */ const AVATAR_BG_CLASSES = [ 'bg-avatar-1', 'bg-avatar-2', 'bg-avatar-3', 'bg-avatar-4', 'bg-avatar-5', 'bg-avatar-6', 'bg-avatar-7', 'bg-avatar-8', 'bg-avatar-9', 'bg-avatar-10', 'bg-avatar-11', ]; export function avatarBgClass(id: string): string { return AVATAR_BG_CLASSES[avatarColorIndex(id) - 1]; } /** First character of a display name, matching the old app's letter-tile convention. */ export function avatarLetter(name: string | undefined): string { return (name || '').trim().slice(0, 1) || '?'; }