feat(web): add persistent beam avatars
This commit is contained in:
@@ -0,0 +1,66 @@
|
||||
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<typeof beamAvatarSchema>;
|
||||
export type BeamAvatarPatch = z.infer<typeof beamAvatarPatchSchema>;
|
||||
|
||||
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<string, unknown>): BeamAvatar {
|
||||
return beamAvatarSchema.parse({
|
||||
seed: profile.avatar_seed,
|
||||
palette: profile.avatar_palette,
|
||||
rendererVersion: profile.avatar_renderer_version,
|
||||
});
|
||||
}
|
||||
|
||||
export function optionalBeamAvatarFromProfile(
|
||||
profile: Record<string, unknown>,
|
||||
): 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;
|
||||
}
|
||||
Reference in New Issue
Block a user