/** * Collab user identity: shared types, color hashing and the localStorage * fallback used when the viewer is not authenticated yet. */ export interface CollabUser { name: string; color: string; avatarUrl?: string; } const COLORS = [ "#958DF1", "#F98181", "#FBBC88", "#FAF594", "#70CFF8", "#94FADB", "#B9F18D", "#C4B5FD", ]; /** Deterministic color from a name, used for cursors and author chips. */ export function colorFromName(name: string): string { let hash = 0; for (let i = 0; i < name.length; i++) hash = (hash * 31 + name.charCodeAt(i)) | 0; return COLORS[Math.abs(hash) % COLORS.length]; } const STORAGE_KEY = "collab-editor:fallback-user"; const FALLBACK_NAMES = ["Alice", "Bob", "Carol", "Dave", "Eve", "Frank", "Grace", "Heidi"]; /** * Pick (and persist) a random pseudonymous user when the session is anonymous. * Stored in localStorage so a refresh keeps the same name/color. */ export function stableFallbackUser(): CollabUser { const stored = localStorage.getItem(STORAGE_KEY); if (stored) { try { return JSON.parse(stored); } catch { /* fall through */ } } const name = FALLBACK_NAMES[Math.floor(Math.random() * FALLBACK_NAMES.length)]; const user = { name, color: colorFromName(name) }; localStorage.setItem(STORAGE_KEY, JSON.stringify(user)); return user; }