// Local user preferences — title, signature, default agent, plus a cache // mirror of the resolved avatar URL. Persisted in localStorage; reactive // across tabs. Identity (name, email) is owned by the arcadia session // (~/lib/session.ts); the public profile (bio, phone, location, timezone) // is server-backed via /api/v1/profile (~/lib/arcadia/profiles.ts). import { useEffect, useSyncExternalStore } from "react" export type Profile = { title: string signature: string avatarUrl: string defaultAgentId: string } export const DEFAULT_PROFILE: Profile = { title: "", signature: "", avatarUrl: "", defaultAgentId: "", } const STORAGE_KEY = "crema.profile" const CHANGE_EVENT = "crema:profile-change" function readFromStorage(): Profile { if (typeof window === "undefined") return DEFAULT_PROFILE try { const raw = localStorage.getItem(STORAGE_KEY) if (!raw) return DEFAULT_PROFILE const parsed = JSON.parse(raw) as Partial return { title: typeof parsed.title === "string" ? parsed.title : DEFAULT_PROFILE.title, signature: typeof parsed.signature === "string" ? parsed.signature : DEFAULT_PROFILE.signature, avatarUrl: typeof parsed.avatarUrl === "string" ? parsed.avatarUrl : DEFAULT_PROFILE.avatarUrl, defaultAgentId: typeof parsed.defaultAgentId === "string" ? parsed.defaultAgentId : DEFAULT_PROFILE.defaultAgentId, } } catch { return DEFAULT_PROFILE } } export function loadProfile(): Profile { return readFromStorage() } export function saveProfile(next: Profile) { if (typeof window === "undefined") return localStorage.setItem(STORAGE_KEY, JSON.stringify(next)) window.dispatchEvent(new CustomEvent(CHANGE_EVENT)) } export function resetProfile() { saveProfile(DEFAULT_PROFILE) } export function profileInitials(name: string): string { const words = name.trim().split(/\s+/).filter(Boolean) if (words.length === 0) return "?" if (words.length === 1) return words[0].slice(0, 2).toUpperCase() return (words[0][0] + words[words.length - 1][0]).toUpperCase() } let cached: Profile | null = null function subscribe(cb: () => void): () => void { const onChange = () => { cached = null cb() } window.addEventListener(CHANGE_EVENT, onChange) window.addEventListener("storage", (e) => { if (e.key === STORAGE_KEY) onChange() }) return () => { window.removeEventListener(CHANGE_EVENT, onChange) } } function getSnapshot(): Profile { if (!cached) cached = readFromStorage() return cached } function getServerSnapshot(): Profile { return DEFAULT_PROFILE } export function useProfile(): Profile { const value = useSyncExternalStore(subscribe, getSnapshot, getServerSnapshot) useEffect(() => { cached = null }, []) return value }