import { AppShell } from "../components/shell/AppShell"; import { Kicker } from "../components/ui"; import { AVATAR_EXPRESSION_LIBRARY, ClientAvatar, type AvatarExpression, type AvatarPersona, } from "../components/avatar/ClientAvatar"; import { PERSONA_LIVE2D_MODELS, live2dModel3Path, live2dMotionForExpression, type Live2DPersonaCode, type Live2DPersonaModel, } from "../components/avatar/live2dModel"; import "./avatar-expression-lab.css"; const PERSONA_CODES = ["P4", "P5", "P6", "P7"] as const satisfies readonly Live2DPersonaCode[]; function avatarFromModel(model: Live2DPersonaModel): AvatarPersona { return { code: model.personaCode, label: model.displayName, ageBand: "teen", skinTone: model.art.skinTone, hair: { style: model.art.hairStyle, color: model.art.hairColor }, outfitColor: model.art.outfitColor, eyeColor: "#2B2B2B", accentColor: model.art.accentColor, realism: 0.4, resistance: model.personaCode === "P7" ? 0.68 : model.personaCode === "P4" ? 0.3 : 0.45, expressionBias: model.defaultExpression, }; } function expressionTone(expression: AvatarExpression): string { return AVATAR_EXPRESSION_LIBRARY.find((item) => item.id === expression)?.group ?? "cognitive"; } export default function AvatarExpressionLab() { const models = PERSONA_CODES.map((code) => PERSONA_LIVE2D_MODELS[code]); const expressionCount = AVATAR_EXPRESSION_LIBRARY.length; const renderedAvatarCount = models.length * expressionCount; return (
{/* 상태가 아니라 단순 섹션 라벨이라 accent dot 을 뺀다(D1). */} Live2D persona rig

Persona expression board

P4-P7 first-party avatar models render every expression motion from the shared library.

{models.length} personas {expressionCount} expressions {renderedAvatarCount} previews
{models.map((model) => { const persona = avatarFromModel(model); const modelPath = live2dModel3Path(model); return (
{model.personaCode}

{model.displayName}

model
{model.modelId}
model3
{modelPath}
default
{model.defaultExpression}
{AVATAR_EXPRESSION_LIBRARY.map((expression) => { const motion = live2dMotionForExpression(model, expression.id); const tone = expressionTone(expression.id); return (
{expression.label} {expression.id} {motion.file}
); })}
); })}
); }