utils_avatars.js

/**
 * 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;
}