import { IDENTITY_COLOR_NAMES, type IdentityColorName } from "@/styles/identity-colors"; import type { HostProfile } from "@/types/host-connection"; import { z } from "zod"; export type HostColor = "none" | IdentityColorName; export const HOST_COLORS: readonly HostColor[] = ["none", ...IDENTITY_COLOR_NAMES]; export type HostBadgeDisplay = "name" | "icon" | "hidden"; export const HOST_BADGE_DISPLAYS: readonly HostBadgeDisplay[] = ["name", "icon", "hidden"]; /** * Per-device host presentation. `badgeDisplay` is null while the user has not chosen, * because the default differs by host (local hides, remote shows) and local-ness is only * knowable from a desktop-only async query — never at parse time. */ export interface HostAppearance { color: HostColor; badgeDisplay: HostBadgeDisplay | null; } export const HostAppearanceSchema: z.ZodType = z.strictObject({ color: z.enum(["none", ...IDENTITY_COLOR_NAMES]), badgeDisplay: z.enum(["name", "icon", "hidden"]).nullable(), }); export function defaultHostAppearance(): HostAppearance { return { color: "none", badgeDisplay: null }; } export function normalizeStoredHostAppearance(value: unknown): HostAppearance { const result = HostAppearanceSchema.safeParse(value); return result.success ? result.data : defaultHostAppearance(); } export function resolveHostBadgeDisplay(input: { appearance: HostAppearance; isLocalHost: boolean; localHostResolutionPending?: boolean; }): HostBadgeDisplay | null { if (input.appearance.badgeDisplay) { return input.appearance.badgeDisplay; } if (input.localHostResolutionPending) { return null; } return input.isLocalHost ? "hidden" : "name"; } export interface HostBadgeModel { serverId: string; label: string; color: HostColor; showLabel: boolean; } export type HostAppearanceSource = Pick; /** * The sidebar's whole host-badge decision, resolved once per host list. Rows look their * badge up by serverId and render whatever they find; a host that should show no badge is * simply absent from the map. */ export function selectHostBadges(input: { hosts: readonly HostAppearanceSource[]; localServerId: string | null; localHostResolutionPending?: boolean; enabled: boolean; }): ReadonlyMap { const badges = new Map(); if (!input.enabled) { return badges; } for (const host of input.hosts) { const display = resolveHostBadgeDisplay({ appearance: host.appearance, isLocalHost: host.serverId === input.localServerId, localHostResolutionPending: input.localHostResolutionPending, }); if (display === null || display === "hidden") { continue; } badges.set(host.serverId, { serverId: host.serverId, label: host.label.trim() || host.serverId, color: host.appearance.color, showLabel: display === "name", }); } return badges; }