/**
* Avatar stack utilities for rendering initials-based user avatars.
*
* Users have display_name and email but no avatar_url (yet).
* Colors are deterministically generated from user ID.
*/
/**
* @param {string} name - display_name or email
* @returns {string} 1-2 letter initials
*/
export function getInitials(name) {
if (!name) return '?';
const parts = name.trim().split(/\s+/);
if (parts.length === 1) return parts[0][0].toUpperCase();
return (parts[0][0] + parts[parts.length - 1][0]).toUpperCase();
}
/**
* @param {string} id - user UUID
* @returns {string} CSS hsl() color string
*/
export function hashColor(id) {
let hash = 0;
for (let i = 0; i < (id || '').length; i++) hash = (hash * 31 + id.charCodeAt(i)) | 0;
const h = Math.abs(hash) % 360;
return `hsl(${h}, 55%, 45%)`;
}
/**
* Build a DOM element showing a stack of avatar initials circles.
*
* @param {Array<{id: string, display_name: (string|undefined), email: (string|undefined)}>} users - list of user objects to render avatars for
* @param {number} [max=3] - max avatars before showing +N overflow badge
* @returns {HTMLElement}
*/
export function renderAvatarStack(users, max = 3) {
const wrap = document.createElement('div');
wrap.className = 'avatar-stack';
const visible = users.slice(0, max);
const overflow = users.length - visible.length;
visible.forEach(u => {
const el = document.createElement('div');
el.className = 'avatar-stack-item';
el.textContent = getInitials(u.display_name || u.email);
el.style.setProperty('--avatar-bg', hashColor(u.id));
el.title = u.display_name || u.email || '';
wrap.appendChild(el);
});
if (overflow > 0) {
const more = document.createElement('div');
more.className = 'avatar-stack-item avatar-stack-overflow';
more.textContent = `+${overflow}`;
wrap.appendChild(more);
}
return wrap;
}