import { z } from "zod"; export const BEAM_AVATAR_RENDERER_VERSION = 1 as const; export const beamAvatarPalettes = [ { name: "星砂", colors: ["#17324D", "#2D6073", "#D5A45D", "#F2E6CF", "#B9684A"] }, { name: "靛夜", colors: ["#202842", "#495B8C", "#93A3D1", "#E4D9C7", "#C58D68"] }, { name: "莲火", colors: ["#553640", "#A35D63", "#D99B7A", "#F2D8BA", "#5E7161"] }, { name: "松雾", colors: ["#1F3D3A", "#4D746C", "#A7B59A", "#E8DEC8", "#C77B56"] }, { name: "晨曦", colors: ["#34405F", "#7484A6", "#E2AF72", "#F4E5C9", "#B75D54"] }, { name: "紫藤", colors: ["#3E3555", "#77638F", "#B9A3CA", "#E9DCCB", "#C47B6C"] }, { name: "海盐", colors: ["#1D4554", "#4F8090", "#A8C0BD", "#EFE2C8", "#D18A5D"] }, { name: "桂影", colors: ["#443B2D", "#7B6848", "#C6A25E", "#EFE0BE", "#8C5B4C"] }, ] as const; export const beamAvatarPaletteSchema = z.number().int().min(0).max(beamAvatarPalettes.length - 1); export const beamAvatarSchema = z.object({ seed: z.string().uuid(), palette: beamAvatarPaletteSchema, rendererVersion: z.literal(BEAM_AVATAR_RENDERER_VERSION), }).strict(); export const beamAvatarPatchSchema = z.discriminatedUnion("action", [ z.object({ action: z.literal("set_palette"), palette: beamAvatarPaletteSchema, }).strict(), z.object({ action: z.literal("randomize"), palette: beamAvatarPaletteSchema.optional(), }).strict(), ]); export type BeamAvatar = z.infer; export type BeamAvatarPatch = z.infer; export function createBeamAvatarProfilePatch( patch: BeamAvatarPatch, createSeed: () => string = () => globalThis.crypto.randomUUID(), ) { return { ...(patch.palette !== undefined ? { avatar_palette: patch.palette } : {}), avatar_renderer_version: BEAM_AVATAR_RENDERER_VERSION, ...(patch.action === "randomize" ? { avatar_seed: createSeed() } : {}), }; } export function beamAvatarFromProfile(profile: Record): BeamAvatar { return beamAvatarSchema.parse({ seed: profile.avatar_seed, palette: profile.avatar_palette, rendererVersion: profile.avatar_renderer_version, }); } export function optionalBeamAvatarFromProfile( profile: Record, ): BeamAvatar | null { const parsed = beamAvatarSchema.safeParse({ seed: profile.avatar_seed, palette: profile.avatar_palette, rendererVersion: profile.avatar_renderer_version, }); return parsed.success ? parsed.data : null; }