import type { AgentAvatarProfile } from "./profile";
const AVATAR_BG = "#070b16";
const EYE_COLOR = "#111827";
const HEADSET_BAND = "#94a3b8";
const HEADSET_PAD = "#475569";
const MOUTH_COLOR = "#9c4a4a";
const escapeXml = (value: string) =>
value
.replaceAll("&", "&")
.replaceAll("<", "<")
.replaceAll(">", ">")
.replaceAll('"', """)
.replaceAll("'", "'");
const normalizeHex = (value: string): string | null => {
const trimmed = value.trim();
if (/^#[0-9a-f]{6}$/i.test(trimmed)) return trimmed.toLowerCase();
if (/^#[0-9a-f]{3}$/i.test(trimmed)) {
const [, r, g, b] = trimmed;
return `#${r}${r}${g}${g}${b}${b}`.toLowerCase();
}
return null;
};
const blendHex = (source: string, target: string, weight: number): string => {
const sourceHex = normalizeHex(source);
const targetHex = normalizeHex(target);
if (!sourceHex || !targetHex) return source;
const ratio = Math.max(0, Math.min(1, weight));
const sourceChannels = [
Number.parseInt(sourceHex.slice(1, 3), 16),
Number.parseInt(sourceHex.slice(3, 5), 16),
Number.parseInt(sourceHex.slice(5, 7), 16),
];
const targetChannels = [
Number.parseInt(targetHex.slice(1, 3), 16),
Number.parseInt(targetHex.slice(3, 5), 16),
Number.parseInt(targetHex.slice(5, 7), 16),
];
const mixed = sourceChannels.map((channel, index) =>
Math.round(channel * (1 - ratio) + targetChannels[index] * ratio)
);
return `#${mixed.map((channel) => channel.toString(16).padStart(2, "0")).join("")}`;
};
const buildHairSvg = (profile: AgentAvatarProfile, hairColor: string) => {
if (profile.accessories.hatStyle !== "none") return "";
switch (profile.hair.style) {
case "short":
return ``;
case "parted":
return [
``,
``,
].join("");
case "spiky":
return [
``,
``,
``,
``,
].join("");
case "bun":
return [
``,
``,
].join("");
default:
return "";
}
};
const buildHatSvg = (profile: AgentAvatarProfile, accessoryColor: string) => {
switch (profile.accessories.hatStyle) {
case "cap":
return [
``,
``,
].join("");
case "beanie":
return ``;
default:
return "";
}
};
const buildHeadsetSvg = (enabled: boolean) => {
if (!enabled) return "";
return [
``,
``,
``,
].join("");
};
const buildGlassesSvg = (enabled: boolean) => {
if (!enabled) return "";
return [
``,
``,
``,
].join("");
};
export const buildAgentAvatarPortraitSvg = (profile: AgentAvatarProfile): string => {
const skinTone = profile.body.skinTone;
const hairColor = profile.hair.color;
const topColor = profile.clothing.topColor;
const accessoryColor = blendHex(topColor, "#ffffff", 0.08);
const shirtShadow = blendHex(topColor, "#000000", 0.18);
const faceShadow = blendHex(skinTone, "#000000", 0.12);
const faceHighlight = blendHex(skinTone, "#ffffff", 0.16);
return [
``,
].join("");
};
export const buildAgentAvatarPortraitDataUrl = (profile: AgentAvatarProfile): string =>
`data:image/svg+xml;utf8,${encodeURIComponent(buildAgentAvatarPortraitSvg(profile))}`;