@bod.ee/ai-avatar 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,75 @@
1
+ /**
2
+ * SpeechController - Drives avatar mouth animation from speech data
3
+ *
4
+ * Dual-mode controller:
5
+ * 1. Viseme mode: word timestamps → mouth shapes with timing
6
+ * 2. Amplitude mode: audio analysis → mouth openness
7
+ *
8
+ * Both modes can run simultaneously: visemes choose shape, amplitude drives openness.
9
+ *
10
+ * @example
11
+ * ```typescript
12
+ * const speech = new SpeechController(avatarController);
13
+ * speech.connectElement(audioElement);
14
+ * speech.feedTimestamps([{ word: 'hello', start: 0, end: 0.5 }]);
15
+ * speech.start();
16
+ * ```
17
+ */
18
+ import { AvatarController } from './AvatarController';
19
+ import type { MouthShape } from './types';
20
+ export interface WordTimestamp {
21
+ word: string;
22
+ start: number;
23
+ end: number;
24
+ }
25
+ export interface SpeechOptions {
26
+ sensitivity?: number;
27
+ smoothing?: number;
28
+ idleExpression?: string;
29
+ }
30
+ /** Map a word's first character(s) to a mouth shape viseme */
31
+ export declare function wordToViseme(word: string): MouthShape;
32
+ export declare class SpeechController {
33
+ private controller;
34
+ private options;
35
+ private _isSpeaking;
36
+ private timingOrigin;
37
+ private scheduledTimeouts;
38
+ private audioContext;
39
+ private analyser;
40
+ private sourceNode;
41
+ private amplitudeFrame;
42
+ private amplitudeData;
43
+ private ownsAudioContext;
44
+ private connectedElement;
45
+ constructor(controller: AvatarController, options?: SpeechOptions);
46
+ get isSpeaking(): boolean;
47
+ /**
48
+ * Feed word-level timestamps from TTS (e.g. Cartesia).
49
+ * Schedules mouth shape changes relative to the timing origin set by start().
50
+ */
51
+ feedTimestamps(timestamps: WordTimestamp[]): void;
52
+ /**
53
+ * Connect to an HTMLAudioElement or HTMLMediaElement for amplitude analysis.
54
+ */
55
+ connectElement(el: HTMLAudioElement | HTMLMediaElement): void;
56
+ /**
57
+ * Connect to an existing AudioNode for amplitude analysis.
58
+ */
59
+ connectSource(node: AudioNode, ctx: AudioContext): void;
60
+ /**
61
+ * Start speech animation. Sets the timing origin for viseme scheduling.
62
+ */
63
+ start(): void;
64
+ /**
65
+ * Stop speech animation and reset mouth to closed.
66
+ */
67
+ stop(): void;
68
+ /**
69
+ * Clean up all resources. Call when done with the controller.
70
+ */
71
+ destroy(): void;
72
+ private setupAnalyser;
73
+ private startAmplitudeLoop;
74
+ private disconnectAudio;
75
+ }
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Emotion-to-action mapping for avatar expressions and sequences.
3
+ * Maps emotion names to their corresponding expression or sequence IDs.
4
+ */
5
+ export interface EmotionAction {
6
+ type: 'expression' | 'sequence';
7
+ id: string;
8
+ }
9
+ export declare const EMOTION_ACTIONS: Record<string, EmotionAction>;
10
+ export declare const FALLBACK_MAP: Record<string, string>;
11
+ export declare function resolveEmotionKey(id: string): string | null;
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Expression Registry
3
+ * Predefined avatar expressions with configurable states
4
+ */
5
+ import { ExpressionConfig } from "./types";
6
+ export declare const expressions: Record<string, ExpressionConfig>;
7
+ export declare const expressionList: ExpressionConfig[];
8
+ export declare const getExpression: (id: string) => ExpressionConfig | undefined;
9
+ export declare const getDefaultState: () => import("./types").AvatarState;
@@ -0,0 +1,35 @@
1
+ /**
2
+ * Avatar API - Main Export
3
+ * Production-ready avatar animation system
4
+ *
5
+ * @example
6
+ * ```tsx
7
+ * import { Avatar, useAvatarController, expressions, sequences } from '@/lib/avatar';
8
+ *
9
+ * function App() {
10
+ * const controller = useAvatarController({ autoAnimate: true });
11
+ *
12
+ * return (
13
+ * <div>
14
+ * <Avatar controller={controller} />
15
+ * <button onClick={() => controller.setExpression('happy')}>Smile</button>
16
+ * <button onClick={() => controller.playSequence('demoLoop')}>Demo</button>
17
+ * </div>
18
+ * );
19
+ * }
20
+ * ```
21
+ */
22
+ export type { EyeState, EyePosition, MouthState, MouthShape, AvatarState, ExpressionConfig, AnimationFrame, AnimationSequence, AvatarControllerOptions, AvatarController, IAvatarController, AvatarProps, } from "./types";
23
+ export { AvatarController as AvatarControllerClass } from "./AvatarController";
24
+ export { useAvatarController, default as useAvatarControllerDefault } from "./useAvatarController";
25
+ export { expressions, expressionList, getExpression, getDefaultState, } from "./expressions";
26
+ export { sequences, sequenceList, getSequence, } from "./sequences";
27
+ export { EMOTION_ACTIONS, FALLBACK_MAP, resolveEmotionKey, } from "./emotions";
28
+ export type { EmotionAction } from "./emotions";
29
+ export { SpeechController, wordToViseme } from "./SpeechController";
30
+ export type { WordTimestamp, SpeechOptions } from "./SpeechController";
31
+ export { SpeechBubbleGrouper } from "./SpeechBubbleGrouper";
32
+ export type { BubbleGroup, SpeechBubbleGrouperOptions } from "./SpeechBubbleGrouper";
33
+ export { PlaybackSync } from "./PlaybackSync";
34
+ export type { ExpressionMark, PlaybackSyncCallbacks, PlaybackSyncOptions } from "./PlaybackSync";
35
+ export type { IdleFidgetOptions } from "./AvatarController";
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Animation Sequences
3
+ * Predefined animation sequences including the signature demo loop
4
+ */
5
+ import { AnimationSequence } from "./types";
6
+ export declare const sequences: Record<string, AnimationSequence>;
7
+ export declare const sequenceList: AnimationSequence[];
8
+ export declare const getSequence: (id: string) => AnimationSequence | undefined;
@@ -0,0 +1,101 @@
1
+ /**
2
+ * Avatar API Types
3
+ * Production-ready type definitions for the avatar animation system
4
+ */
5
+ export type Mood = 'normal' | 'energized' | 'tired';
6
+ export declare const MOOD_SPEEDS: Record<Mood, number>;
7
+ export interface EyePosition {
8
+ x: number;
9
+ y: number;
10
+ }
11
+ export interface EyeState {
12
+ scaleY: number;
13
+ scaleX?: number;
14
+ position?: EyePosition;
15
+ shake?: number;
16
+ leftScaleY?: number;
17
+ rightScaleY?: number;
18
+ }
19
+ export type MouthShape = 'closed' | 'neutral' | 'smile' | 'open' | 'A' | 'E' | 'I' | 'O' | 'U' | 'M' | 'F' | 'W';
20
+ export interface MouthState {
21
+ shape?: MouthShape;
22
+ openness?: number;
23
+ width?: number;
24
+ visible?: boolean;
25
+ scale?: number;
26
+ }
27
+ export interface FacePosition {
28
+ x: number;
29
+ y: number;
30
+ tilt?: number;
31
+ }
32
+ export interface AvatarState {
33
+ eyes: EyeState;
34
+ mouth: MouthState;
35
+ face?: FacePosition;
36
+ }
37
+ export interface ExpressionConfig {
38
+ id: string;
39
+ name: string;
40
+ state: AvatarState;
41
+ transitionDuration?: number;
42
+ easing?: string;
43
+ }
44
+ export interface AnimationFrame {
45
+ expression?: string;
46
+ state?: Partial<AvatarState>;
47
+ duration: number;
48
+ transitionDuration?: number;
49
+ easing?: string;
50
+ }
51
+ export interface AnimationSequence {
52
+ id: string;
53
+ name: string;
54
+ frames: AnimationFrame[];
55
+ loop?: boolean;
56
+ }
57
+ export interface AvatarControllerOptions {
58
+ initialExpression?: string;
59
+ autoAnimate?: boolean;
60
+ initialMood?: Mood;
61
+ idleMode?: boolean;
62
+ onStateChange?: (state: AvatarState) => void;
63
+ onExpressionChange?: (expression: string) => void;
64
+ onSequenceStart?: (sequence: string) => void;
65
+ onSequenceEnd?: (sequence: string) => void;
66
+ }
67
+ export interface IAvatarController {
68
+ state: AvatarState;
69
+ currentExpression: string | null;
70
+ isPlaying: boolean;
71
+ isAutoAnimating: boolean;
72
+ isIdleMode: boolean;
73
+ mood: Mood;
74
+ setExpression: (expressionId: string, transitionMs?: number) => void;
75
+ setState: (state: Partial<AvatarState>, transitionMs?: number) => void;
76
+ blink: (durationMs?: number) => Promise<void>;
77
+ playSequence: (sequenceId: string) => Promise<void>;
78
+ stopSequence: () => void;
79
+ setAutoAnimate: (enabled: boolean) => void;
80
+ setIdleMode: (enabled: boolean) => void;
81
+ setMood: (mood: Mood) => void;
82
+ getExpressions: () => ExpressionConfig[];
83
+ getSequences: () => AnimationSequence[];
84
+ }
85
+ export type AvatarController = IAvatarController;
86
+ export interface AvatarProps {
87
+ controller?: IAvatarController;
88
+ state?: AvatarState;
89
+ expression?: string;
90
+ autoAnimate?: boolean;
91
+ className?: string;
92
+ eyeSize?: number;
93
+ eyeGap?: number;
94
+ mouthSize?: number;
95
+ glowColor?: string;
96
+ glowIntensity?: number;
97
+ /** Enable cursor tracking for eye movement */
98
+ cursorTracking?: boolean;
99
+ /** Enable glitch effects on expression changes */
100
+ enableGlitch?: boolean;
101
+ }
@@ -0,0 +1,28 @@
1
+ /**
2
+ * Avatar Controller Hook
3
+ * React wrapper for the framework-agnostic AvatarController class
4
+ */
5
+ import { AvatarControllerOptions, IAvatarController } from "./types";
6
+ /**
7
+ * React hook that wraps the AvatarController class.
8
+ * Maintains backward compatibility with existing React components.
9
+ *
10
+ * @param options - Controller configuration options
11
+ * @returns Controller interface with reactive state
12
+ *
13
+ * @example
14
+ * ```tsx
15
+ * const controller = useAvatarController({ autoAnimate: true });
16
+ *
17
+ * return (
18
+ * <div>
19
+ * <Avatar controller={controller} />
20
+ * <button onClick={() => controller.setExpression('happy')}>
21
+ * Make Happy
22
+ * </button>
23
+ * </div>
24
+ * );
25
+ * ```
26
+ */
27
+ export declare const useAvatarController: (options?: AvatarControllerOptions) => IAvatarController;
28
+ export default useAvatarController;
@@ -0,0 +1,13 @@
1
+ /**
2
+ * Simple EventEmitter implementation for browser compatibility
3
+ * Replaces Node.js EventEmitter for Vite build compatibility
4
+ */
5
+ type EventHandler = (...args: any[]) => void;
6
+ export declare class EventEmitter {
7
+ private events;
8
+ on(event: string, handler: EventHandler): this;
9
+ off(event: string, handler: EventHandler): this;
10
+ emit(event: string, ...args: any[]): boolean;
11
+ removeAllListeners(event?: string): this;
12
+ }
13
+ export {};
@@ -0,0 +1 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" width="1200" height="1200" fill="none"><rect width="1200" height="1200" fill="#EAEAEA" rx="3"/><g opacity=".5"><g opacity=".5"><path fill="#FAFAFA" d="M600.709 736.5c-75.454 0-136.621-61.167-136.621-136.62 0-75.454 61.167-136.621 136.621-136.621 75.453 0 136.62 61.167 136.62 136.621 0 75.453-61.167 136.62-136.62 136.62Z"/><path stroke="#C9C9C9" stroke-width="2.418" d="M600.709 736.5c-75.454 0-136.621-61.167-136.621-136.62 0-75.454 61.167-136.621 136.621-136.621 75.453 0 136.62 61.167 136.62 136.621 0 75.453-61.167 136.62-136.62 136.62Z"/></g><path stroke="url(#a)" stroke-width="2.418" d="M0-1.209h553.581" transform="scale(1 -1) rotate(45 1163.11 91.165)"/><path stroke="url(#b)" stroke-width="2.418" d="M404.846 598.671h391.726"/><path stroke="url(#c)" stroke-width="2.418" d="M599.5 795.742V404.017"/><path stroke="url(#d)" stroke-width="2.418" d="m795.717 796.597-391.441-391.44"/><path fill="#fff" d="M600.709 656.704c-31.384 0-56.825-25.441-56.825-56.824 0-31.384 25.441-56.825 56.825-56.825 31.383 0 56.824 25.441 56.824 56.825 0 31.383-25.441 56.824-56.824 56.824Z"/><g clip-path="url(#e)"><path fill="#666" fill-rule="evenodd" d="M616.426 586.58h-31.434v16.176l3.553-3.554.531-.531h9.068l.074-.074 8.463-8.463h2.565l7.18 7.181V586.58Zm-15.715 14.654 3.698 3.699 1.283 1.282-2.565 2.565-1.282-1.283-5.2-5.199h-6.066l-5.514 5.514-.073.073v2.876a2.418 2.418 0 0 0 2.418 2.418h26.598a2.418 2.418 0 0 0 2.418-2.418v-8.317l-8.463-8.463-7.181 7.181-.071.072Zm-19.347 5.442v4.085a6.045 6.045 0 0 0 6.046 6.045h26.598a6.044 6.044 0 0 0 6.045-6.045v-7.108l1.356-1.355-1.282-1.283-.074-.073v-17.989h-38.689v23.43l-.146.146.146.147Z" clip-rule="evenodd"/></g><path stroke="#C9C9C9" stroke-width="2.418" d="M600.709 656.704c-31.384 0-56.825-25.441-56.825-56.824 0-31.384 25.441-56.825 56.825-56.825 31.383 0 56.824 25.441 56.824 56.825 0 31.383-25.441 56.824-56.824 56.824Z"/></g><defs><linearGradient id="a" x1="554.061" x2="-.48" y1=".083" y2=".087" gradientUnits="userSpaceOnUse"><stop stop-color="#C9C9C9" stop-opacity="0"/><stop offset=".208" stop-color="#C9C9C9"/><stop offset=".792" stop-color="#C9C9C9"/><stop offset="1" stop-color="#C9C9C9" stop-opacity="0"/></linearGradient><linearGradient id="b" x1="796.912" x2="404.507" y1="599.963" y2="599.965" gradientUnits="userSpaceOnUse"><stop stop-color="#C9C9C9" stop-opacity="0"/><stop offset=".208" stop-color="#C9C9C9"/><stop offset=".792" stop-color="#C9C9C9"/><stop offset="1" stop-color="#C9C9C9" stop-opacity="0"/></linearGradient><linearGradient id="c" x1="600.792" x2="600.794" y1="403.677" y2="796.082" gradientUnits="userSpaceOnUse"><stop stop-color="#C9C9C9" stop-opacity="0"/><stop offset=".208" stop-color="#C9C9C9"/><stop offset=".792" stop-color="#C9C9C9"/><stop offset="1" stop-color="#C9C9C9" stop-opacity="0"/></linearGradient><linearGradient id="d" x1="404.85" x2="796.972" y1="403.903" y2="796.02" gradientUnits="userSpaceOnUse"><stop stop-color="#C9C9C9" stop-opacity="0"/><stop offset=".208" stop-color="#C9C9C9"/><stop offset=".792" stop-color="#C9C9C9"/><stop offset="1" stop-color="#C9C9C9" stop-opacity="0"/></linearGradient><clipPath id="e"><path fill="#fff" d="M581.364 580.535h38.689v38.689h-38.689z"/></clipPath></defs></svg>
package/dist/react.cjs ADDED
@@ -0,0 +1 @@
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const c=require("./PlaybackSync-DwFiX-W4.cjs"),x=require("react/jsx-runtime"),t=require("react"),N=(e={})=>{const{enabled:u=!0,maxOffset:i=.35,smoothing:S=.15,containerRef:f,deadZone:b=.1}=e,[F,P]=t.useState({x:0,y:0}),l=t.useRef({x:0,y:0}),a=t.useRef(),s=t.useRef({x:0,y:0}),h=t.useCallback(()=>{const n=l.current.x-s.current.x,v=l.current.y-s.current.y;s.current.x+=n*S,s.current.y+=v*S,(Math.abs(n)>.001||Math.abs(v)>.001)&&P({x:s.current.x,y:s.current.y}),a.current=requestAnimationFrame(h)},[S]),y=t.useCallback(n=>{if(!u)return;let v,$,r,q;if(f!=null&&f.current){const o=f.current.getBoundingClientRect();v=o.left+o.width/2,$=o.top+o.height/2,r=o.width/2,q=o.height/2}else v=window.innerWidth/2,$=window.innerHeight/2,r=window.innerWidth/2,q=window.innerHeight/2;let k=(n.clientX-v)/r,m=(n.clientY-$)/q;Math.abs(k)<b?k=0:k=(k-Math.sign(k)*b)/(1-b),Math.abs(m)<b?m=0:m=(m-Math.sign(m)*b)/(1-b),l.current={x:Math.max(-1,Math.min(1,k))*i,y:Math.max(-1,Math.min(1,m))*i*.6}},[u,f,i,b]),g=t.useCallback(()=>{l.current={x:0,y:0}},[]);t.useEffect(()=>{if(!u){l.current={x:0,y:0};return}return a.current=requestAnimationFrame(h),window.addEventListener("mousemove",y),document.addEventListener("mouseleave",g),()=>{a.current&&cancelAnimationFrame(a.current),window.removeEventListener("mousemove",y),document.removeEventListener("mouseleave",g)}},[u,h,y,g]);const M=t.useCallback(()=>{l.current={x:0,y:0},s.current={x:0,y:0},P({x:0,y:0})},[]),G=t.useCallback(n=>{l.current=n},[]);return{eyePosition:F,resetPosition:M,setPosition:G,isTracking:u}},X={shakeX:0,shakeY:0,scaleDistort:1,isGlitching:!1,opacity:1},_=()=>{const[e,u]=t.useState(X),i=t.useRef([]),S=t.useCallback(()=>{i.current.forEach(clearTimeout),i.current=[]},[]),f=t.useCallback((a={})=>{const{duration:s=300,intensity:h=.8,frames:y=8}=a;return new Promise(g=>{S(),u(n=>({...n,isGlitching:!0}));const M=s/y;for(let n=0;n<y;n++){const v=setTimeout(()=>{const $=n/y,r=h*(1-$*.5);u({shakeX:(Math.random()-.5)*20*r,shakeY:(Math.random()-.5)*10*r,scaleDistort:1+(Math.random()-.5)*.1*r,isGlitching:!0,opacity:Math.random()>.3?1:.7+Math.random()*.3})},n*M);i.current.push(v)}const G=setTimeout(()=>{u(X),g()},s);i.current.push(G)})},[S]),b=t.useCallback(()=>f({duration:100,intensity:.3,frames:3}),[f]),F=t.useCallback(()=>f({duration:500,intensity:1,frames:12}),[f]),P=t.useCallback(()=>new Promise(a=>{S(),u(g=>({...g,isGlitching:!0}));const s=[50,30,80,20,60,40];let h=0;s.forEach((g,M)=>{const G=setTimeout(()=>{u({shakeX:M%2===0?(Math.random()-.5)*15:0,shakeY:M%2===0?(Math.random()-.5)*8:0,scaleDistort:M%2===0?.95+Math.random()*.1:1,isGlitching:!0,opacity:M%2===0?.8:1})},h);i.current.push(G),h+=g});const y=setTimeout(()=>{u(X),a()},h);i.current.push(y)}),[S]),l=t.useCallback((a="")=>{if(!e.isGlitching)return a;const s=`translate(${e.shakeX}px, ${e.shakeY}px) scale(${e.scaleDistort})`;return a?`${a} ${s}`:s},[e]);return{glitchState:e,triggerGlitch:f,microGlitch:b,heavyGlitch:F,stutterGlitch:P,getGlitchTransform:l,isGlitching:e.isGlitching}},I={closed:1,M:1,neutral:1.2,O:1.2,open:1.5,A:1.5,F:1.5,E:2,I:2,smile:2.5,W:.8,U:.8},J={closed:0,M:.05,F:.15,E:.35,I:.3,neutral:.4,smile:.55,W:.65,U:.65,open:.8,A:.85,O:.95};function Q(e){if(e.shape){const u=e.openness??J[e.shape],i=e.width??I[e.shape];return{shape:e.shape,openness:u,widthMultiplier:i}}return{shape:e.visible?"open":"closed",openness:e.scale??1,widthMultiplier:1}}const ee=({controller:e,state:u,expression:i,autoAnimate:S=!1,className:f="",eyeSize:b,eyeGap:F,mouthSize:P,glowColor:l,glowIntensity:a=.4,cursorTracking:s=!0,enableGlitch:h=!0,speechText:y,playIntro:g,onIntroComplete:M})=>{const G=t.useRef(null),[n,v]=t.useState(g===void 0?"done":"pixel"),$=t.useRef(M);$.current=M,t.useEffect(()=>{if(g===void 0){v("done");return}if(!g)return;v("pixel");const d=[setTimeout(()=>v("grow"),900),setTimeout(()=>v("split"),1400),setTimeout(()=>{var E;v("done"),(E=$.current)==null||E.call($)},1900)];return()=>d.forEach(clearTimeout)},[g]);const r=b??90,q=F??120,k=P??40,{eyePosition:m}=N({enabled:s&&!(e!=null&&e.isPlaying),containerRef:G,maxOffset:.4,smoothing:.12,deadZone:.08}),{glitchState:o,getGlitchTransform:A}=_(),p=t.useMemo(()=>{var d;return e?e.state:u||(i?((d=c.getExpression(i))==null?void 0:d.state)??c.getDefaultState():c.getDefaultState())},[e,u,i]),O=t.useRef({x:0,y:0,tilt:0}),z=t.useRef(p.face);z.current!==p.face&&(z.current=p.face,O.current={x:(Math.random()-.5)*.08,y:(Math.random()-.5)*.08,tilt:(Math.random()-.5)*3});const L=t.useMemo(()=>{const d=p.face??{x:0,y:0,tilt:0},E=s&&!(e!=null&&e.isPlaying)?.15:0,R=O.current;return{x:d.x+m.x*E+R.x,y:d.y+m.y*E+R.y,tilt:(d.tilt??0)+R.tilt}},[p.face,m,s,e==null?void 0:e.isPlaying]),Y=t.useMemo(()=>{const d=p.eyes.position??{x:0,y:0};return e!=null&&e.isPlaying?d:{x:d.x+m.x,y:d.y+m.y}},[p.eyes.position,m,e==null?void 0:e.isPlaying]),j=t.useMemo(()=>({x:Y.x*(r*.4),y:Y.y*(r*.4)}),[Y,r]),w=t.useMemo(()=>{if(!s||e!=null&&e.isPlaying)return{squintFactor:1,crossEyeFactor:0};const d=Math.sqrt(m.x**2+m.y**2);if(d<.15){const E=1-d/.15;return{squintFactor:1-E*.6,crossEyeFactor:E*.35}}return{squintFactor:1,crossEyeFactor:0}},[m,s,e==null?void 0:e.isPlaying]),C=t.useMemo(()=>({width:`min(${r}px, 12vmin)`,height:`min(${r}px, 12vmin)`,transition:o.isGlitching?"none":"transform 0.15s cubic-bezier(0.34, 1.56, 0.64, 1)",boxShadow:l?`0 0 ${25*a}px ${4*a}px ${l}`:`0 0 ${25*a}px ${4*a}px hsl(var(--avatar-glow) / ${a})`,opacity:o.opacity}),[r,l,a,o]),B=t.useMemo(()=>({...C,transform:A(`scaleY(${(p.eyes.leftScaleY??p.eyes.scaleY)*w.squintFactor}) scaleX(${p.eyes.scaleX??1})`)}),[C,p.eyes,A,w.squintFactor]),W=t.useMemo(()=>({...C,transform:A(`scaleY(${(p.eyes.rightScaleY??p.eyes.scaleY)*w.squintFactor}) scaleX(${p.eyes.scaleX??1})`)}),[C,p.eyes,A,w.squintFactor]),H=t.useMemo(()=>({transform:`translate(${j.x+w.crossEyeFactor*r*.4}px, ${j.y}px)`,transition:o.isGlitching?"none":"transform 0.08s ease-out"}),[j,w.crossEyeFactor,r,o.isGlitching]),K=t.useMemo(()=>({transform:`translate(${j.x-w.crossEyeFactor*r*.4}px, ${j.y}px)`,transition:o.isGlitching?"none":"transform 0.08s ease-out"}),[j,w.crossEyeFactor,r,o.isGlitching]),T=t.useMemo(()=>Q(p.mouth),[p.mouth]),U=t.useMemo(()=>{const d=T.openness,E=T.widthMultiplier*(.6+d*.4),R=k*E,D=d*.9+.1,Z=k*D;return{width:`min(${R}px, ${4*E}vmin)`,height:`min(${Z}px, ${4*D}vmin)`,marginTop:`min(${r*.6}px, 7.2vmin)`,opacity:T.shape==="closed"?0:o.opacity,transform:A(`scale(${T.shape==="closed"?0:1})`),transition:o.isGlitching?"none":"opacity 0.05s, transform 0.08s cubic-bezier(0.68, -0.55, 0.265, 1.55), width 0.1s ease-out, height 0.1s ease-out",boxShadow:l?`0 0 ${20*a}px ${3*a}px ${l}`:`0 0 ${20*a}px ${3*a}px hsl(var(--avatar-glow) / ${a})`}},[k,r,T.shape,T.openness,T.widthMultiplier,l,a,A,o]),V={"--avatar-bg":"var(--background, #000)","--avatar-fg":"var(--foreground, #fff)","--avatar-glow-color":l||"var(--avatar-glow, 210 100% 60%)"};return x.jsxs("div",{ref:G,className:f||void 0,style:{...V,position:"relative",width:"100%",height:"100%",backgroundColor:"hsl(var(--avatar-bg))",overflow:"hidden"},children:[x.jsx("div",{style:{position:"absolute",inset:0,pointerEvents:"none",background:"radial-gradient(ellipse at center, transparent 35%, hsl(var(--avatar-bg) / 0.3) 70%, hsl(var(--avatar-bg) / 0.6) 100%)"}}),x.jsx("div",{style:{position:"absolute",inset:0,pointerEvents:"none",opacity:.03,backgroundImage:"repeating-linear-gradient(0deg, transparent, transparent 2px, hsl(var(--avatar-fg)) 2px, hsl(var(--avatar-fg)) 4px)"}}),y&&x.jsx("div",{style:{position:"absolute",bottom:32,left:"50%",transform:"translateX(-50%)",zIndex:10},children:x.jsxs("div",{style:{position:"relative",backgroundColor:"hsl(var(--avatar-bg))",border:"2px solid hsl(var(--avatar-fg))",borderRadius:16,padding:"12px 24px",boxShadow:"0 -10px 15px -3px rgba(0,0,0,0.1)",maxWidth:448},children:[x.jsx("div",{style:{position:"absolute",top:-12,left:"50%",transform:"translateX(-50%)",width:0,height:0,borderLeft:"12px solid transparent",borderRight:"12px solid transparent",borderBottom:"12px solid hsl(var(--avatar-fg))"}}),x.jsx("div",{style:{position:"absolute",top:-10,left:"50%",transform:"translateX(-50%)",width:0,height:0,borderLeft:"10px solid transparent",borderRight:"10px solid transparent",borderBottom:"10px solid hsl(var(--avatar-bg))"}}),x.jsx("p",{style:{fontSize:14,fontWeight:500,color:"hsl(var(--avatar-fg))",margin:0},children:y})]})}),x.jsx("div",{style:{position:"absolute",inset:0,display:"flex",alignItems:"center",justifyContent:"center"},children:x.jsxs("div",{style:{position:"relative",display:"flex",flexDirection:"column",alignItems:"center",marginTop:"-5%",transform:n!=="done"?"none":o.isGlitching?`translate(${o.shakeX*.5}px, ${o.shakeY*.5}px)`:`translate(calc(${L.x*60}px + ${L.x*8}vmin), calc(${L.y*60}px + ${L.y*8}vmin)) rotate(${L.tilt}deg)`,transition:o.isGlitching?"none":"transform 0.6s cubic-bezier(0.34, 1.56, 0.64, 1)"},children:[x.jsxs("div",{style:{display:"flex",alignItems:"center",justifyContent:"center",gap:n==="split"||n==="done"?`min(${q}px, 16vmin)`:"0px",transition:"gap 0.5s cubic-bezier(0.34, 1.56, 0.64, 1)"},children:[x.jsx("div",{style:{position:"relative",...n==="done"?B:{...C,width:n==="pixel"?"20px":C.width,height:n==="pixel"?"20px":C.height,transform:"scaleY(1)",transition:"all 0.5s cubic-bezier(0.34, 1.56, 0.64, 1)"}},children:x.jsx("div",{style:{position:"absolute",inset:0,backgroundColor:"hsl(var(--avatar-fg))",...n==="done"?H:{}}})}),x.jsx("div",{style:{position:"relative",...n==="done"?W:{...C,width:n==="pixel"?"20px":C.width,height:n==="pixel"?"20px":C.height,transform:"scaleY(1)",transition:"all 0.5s cubic-bezier(0.34, 1.56, 0.64, 1)",marginLeft:n==="pixel"?"-20px":n==="grow"?`calc(-1 * min(${r}px, 12vmin))`:void 0}},children:x.jsx("div",{style:{position:"absolute",inset:0,backgroundColor:"hsl(var(--avatar-fg))",...n==="done"?K:{}}})})]}),x.jsx("div",{style:{backgroundColor:"hsl(var(--avatar-fg))",...U,...n!=="done"?{opacity:0,transform:"scale(0)"}:{}}})]})})]})};function te(e,u){const i=t.useRef(null),[S,f]=t.useState(!1);t.useEffect(()=>(i.current=new c.SpeechController(e,u),()=>{var s;(s=i.current)==null||s.destroy(),i.current=null}),[e]);const b=t.useCallback(s=>{var h;(h=i.current)==null||h.feedTimestamps(s)},[]),F=t.useCallback(s=>{var h;(h=i.current)==null||h.connectElement(s)},[]),P=t.useCallback((s,h)=>{var y;(y=i.current)==null||y.connectSource(s,h)},[]),l=t.useCallback(()=>{var s;(s=i.current)==null||s.start(),f(!0)},[]),a=t.useCallback(()=>{var s;(s=i.current)==null||s.stop(),f(!1)},[]);return{feedTimestamps:b,connectElement:F,connectSource:P,start:l,stop:a,isSpeaking:S}}exports.AvatarControllerClass=c.AvatarController;exports.EMOTION_ACTIONS=c.EMOTION_ACTIONS;exports.FALLBACK_MAP=c.FALLBACK_MAP;exports.PlaybackSync=c.PlaybackSync;exports.SpeechBubbleGrouper=c.SpeechBubbleGrouper;exports.SpeechController=c.SpeechController;exports.expressionList=c.expressionList;exports.expressions=c.expressions;exports.getDefaultState=c.getDefaultState;exports.getExpression=c.getExpression;exports.getSequence=c.getSequence;exports.resolveEmotionKey=c.resolveEmotionKey;exports.sequenceList=c.sequenceList;exports.sequences=c.sequences;exports.useAvatarController=c.useAvatarController;exports.useAvatarControllerDefault=c.useAvatarController;exports.wordToViseme=c.wordToViseme;exports.Avatar=ee;exports.useCursorTracking=N;exports.useGlitchEffect=_;exports.useSpeech=te;