fix(expressions): use full expression names with .exp3.json suffix

Epsilon model registers expressions as 'Smile.exp3.json' not 'Smile'.
Added EXPR_MAP to map friendly names to full registered names.
Fixes expression buttons and idle cycling.
This commit is contained in:
2026-06-05 14:19:59 -04:00
parent 73fe77f9aa
commit 37c06db6be
+12 -2
View File
@@ -18,6 +18,16 @@ type ExpressionName = 'Normal' | 'Smile' | 'Sad' | 'Angry' | 'Surprised' | 'Blus
const EXPRESSIONS: ExpressionName[] = ['Normal', 'Smile', 'Sad', 'Angry', 'Surprised', 'Blushing'];
const IDLE_EXPRESSIONS: ExpressionName[] = ['Normal', 'Smile', 'Blushing'];
// The Epsilon model registers expressions with the .exp3.json suffix
const EXPR_MAP: Record<ExpressionName, string> = {
Normal: 'Normal.exp3.json',
Smile: 'Smile.exp3.json',
Sad: 'Sad.exp3.json',
Angry: 'Angry.exp3.json',
Surprised: 'Surprised.exp3.json',
Blushing: 'Blushing.exp3.json',
};
export default function KiraAvatar(props: Props) {
const lipSyncRef = useRef<number>(0);
const idleExprRef = useRef<ReturnType<typeof setInterval> | null>(null);
@@ -29,7 +39,7 @@ export default function KiraAvatar(props: Props) {
idleExprRef.current = setInterval(() => {
if (props.isSpeaking) return;
const expr = IDLE_EXPRESSIONS[Math.floor(Math.random() * IDLE_EXPRESSIONS.length)];
try { props.kiraModel.expression(expr); } catch {}
try { props.kiraModel.expression(EXPR_MAP[expr]); } catch {}
setCurrentExpression(expr);
}, 8000 + Math.random() * 7000);
return () => clearInterval(idleExprRef.current ?? undefined);
@@ -106,7 +116,7 @@ export default function KiraAvatar(props: Props) {
<button
key={expr}
onClick={() => {
try { props.kiraModel?.expression(expr); setCurrentExpression(expr); props.onExpressionChange?.(expr); } catch {}
try { props.kiraModel?.expression(EXPR_MAP[expr]); setCurrentExpression(expr); props.onExpressionChange?.(expr); } catch {}
}}
className={`text-[9px] px-2 py-0.5 rounded-full font-medium transition-all ${
currentExpression === expr