@memori.ai/memori-react 7.19.1 → 7.21.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.
Files changed (145) hide show
  1. package/CHANGELOG.md +57 -0
  2. package/dist/components/Avatar/Avatar.js +3 -3
  3. package/dist/components/Avatar/Avatar.js.map +1 -1
  4. package/dist/components/Avatar/AvatarView/AvatarComponent/Shadow/DynamicShadow.d.ts +3 -2
  5. package/dist/components/Avatar/AvatarView/AvatarComponent/Shadow/DynamicShadow.js +13 -6
  6. package/dist/components/Avatar/AvatarView/AvatarComponent/Shadow/DynamicShadow.js.map +1 -1
  7. package/dist/components/Avatar/AvatarView/AvatarComponent/avatarComponent.d.ts +14 -18
  8. package/dist/components/Avatar/AvatarView/AvatarComponent/avatarComponent.js +19 -77
  9. package/dist/components/Avatar/AvatarView/AvatarComponent/avatarComponent.js.map +1 -1
  10. package/dist/components/Avatar/AvatarView/AvatarComponent/components/FullbodyAvatar/fullbodyAvatar.d.ts +17 -2
  11. package/dist/components/Avatar/AvatarView/AvatarComponent/components/FullbodyAvatar/fullbodyAvatar.js +95 -70
  12. package/dist/components/Avatar/AvatarView/AvatarComponent/components/FullbodyAvatar/fullbodyAvatar.js.map +1 -1
  13. package/dist/components/Avatar/AvatarView/AvatarComponent/components/controllers/AvatarAnimator.d.ts +65 -0
  14. package/dist/components/Avatar/AvatarView/AvatarComponent/components/controllers/AvatarAnimator.js +747 -0
  15. package/dist/components/Avatar/AvatarView/AvatarComponent/components/controllers/AvatarAnimator.js.map +1 -0
  16. package/dist/components/Avatar/AvatarView/AvatarComponent/components/controllers/MorphTargetController.d.ts +9 -2
  17. package/dist/components/Avatar/AvatarView/AvatarComponent/components/controllers/MorphTargetController.js +60 -2
  18. package/dist/components/Avatar/AvatarView/AvatarComponent/components/controllers/MorphTargetController.js.map +1 -1
  19. package/dist/components/Avatar/AvatarView/AvatarComponent/components/halfbodyAvatar.d.ts +3 -4
  20. package/dist/components/Avatar/AvatarView/AvatarComponent/components/halfbodyAvatar.js +5 -11
  21. package/dist/components/Avatar/AvatarView/AvatarComponent/components/halfbodyAvatar.js.map +1 -1
  22. package/dist/components/Avatar/AvatarView/AvatarComponent/constants.d.ts +13 -52
  23. package/dist/components/Avatar/AvatarView/AvatarComponent/constants.js +68 -70
  24. package/dist/components/Avatar/AvatarView/AvatarComponent/constants.js.map +1 -1
  25. package/dist/components/Avatar/AvatarView/index.d.ts +1 -1
  26. package/dist/components/Avatar/AvatarView/index.js +2 -2
  27. package/dist/components/Avatar/AvatarView/index.js.map +1 -1
  28. package/dist/components/Chat/Chat.js +2 -2
  29. package/dist/components/Chat/Chat.js.map +1 -1
  30. package/dist/components/ChatBubble/ChatBubble.js +12 -9
  31. package/dist/components/ChatBubble/ChatBubble.js.map +1 -1
  32. package/dist/components/ExpertsDrawer/ExpertsDrawer.js +1 -1
  33. package/dist/components/ExpertsDrawer/ExpertsDrawer.js.map +1 -1
  34. package/dist/components/LoginDrawer/LoginDrawer.js +6 -6
  35. package/dist/components/LoginDrawer/LoginDrawer.js.map +1 -1
  36. package/dist/components/MemoriWidget/MemoriWidget.js +143 -64
  37. package/dist/components/MemoriWidget/MemoriWidget.js.map +1 -1
  38. package/dist/components/SignupForm/SignupForm.js +4 -4
  39. package/dist/components/SignupForm/SignupForm.js.map +1 -1
  40. package/dist/components/StartPanel/StartPanel.js +5 -5
  41. package/dist/components/StartPanel/StartPanel.js.map +1 -1
  42. package/dist/components/UploadButton/UploadButton.js +2 -2
  43. package/dist/components/UploadButton/UploadButton.js.map +1 -1
  44. package/dist/components/WhyThisAnswer/WhyThisAnswer.css +43 -0
  45. package/dist/components/WhyThisAnswer/WhyThisAnswer.js +2 -1
  46. package/dist/components/WhyThisAnswer/WhyThisAnswer.js.map +1 -1
  47. package/dist/context/visemeContext.js +0 -39
  48. package/dist/context/visemeContext.js.map +1 -1
  49. package/dist/index.js +4 -3
  50. package/dist/index.js.map +1 -1
  51. package/dist/locales/de.json +1 -0
  52. package/dist/locales/en.json +1 -0
  53. package/dist/locales/es.json +1 -0
  54. package/dist/locales/fr.json +1 -0
  55. package/dist/locales/it.json +1 -0
  56. package/esm/components/Avatar/Avatar.js +3 -3
  57. package/esm/components/Avatar/Avatar.js.map +1 -1
  58. package/esm/components/Avatar/AvatarView/AvatarComponent/Shadow/DynamicShadow.d.ts +3 -2
  59. package/esm/components/Avatar/AvatarView/AvatarComponent/Shadow/DynamicShadow.js +13 -6
  60. package/esm/components/Avatar/AvatarView/AvatarComponent/Shadow/DynamicShadow.js.map +1 -1
  61. package/esm/components/Avatar/AvatarView/AvatarComponent/avatarComponent.d.ts +14 -18
  62. package/esm/components/Avatar/AvatarView/AvatarComponent/avatarComponent.js +20 -78
  63. package/esm/components/Avatar/AvatarView/AvatarComponent/avatarComponent.js.map +1 -1
  64. package/esm/components/Avatar/AvatarView/AvatarComponent/components/FullbodyAvatar/fullbodyAvatar.d.ts +17 -2
  65. package/esm/components/Avatar/AvatarView/AvatarComponent/components/FullbodyAvatar/fullbodyAvatar.js +99 -74
  66. package/esm/components/Avatar/AvatarView/AvatarComponent/components/FullbodyAvatar/fullbodyAvatar.js.map +1 -1
  67. package/esm/components/Avatar/AvatarView/AvatarComponent/components/controllers/AvatarAnimator.d.ts +65 -0
  68. package/esm/components/Avatar/AvatarView/AvatarComponent/components/controllers/AvatarAnimator.js +743 -0
  69. package/esm/components/Avatar/AvatarView/AvatarComponent/components/controllers/AvatarAnimator.js.map +1 -0
  70. package/esm/components/Avatar/AvatarView/AvatarComponent/components/controllers/MorphTargetController.d.ts +9 -2
  71. package/esm/components/Avatar/AvatarView/AvatarComponent/components/controllers/MorphTargetController.js +61 -3
  72. package/esm/components/Avatar/AvatarView/AvatarComponent/components/controllers/MorphTargetController.js.map +1 -1
  73. package/esm/components/Avatar/AvatarView/AvatarComponent/components/halfbodyAvatar.d.ts +3 -4
  74. package/esm/components/Avatar/AvatarView/AvatarComponent/components/halfbodyAvatar.js +5 -11
  75. package/esm/components/Avatar/AvatarView/AvatarComponent/components/halfbodyAvatar.js.map +1 -1
  76. package/esm/components/Avatar/AvatarView/AvatarComponent/constants.d.ts +13 -52
  77. package/esm/components/Avatar/AvatarView/AvatarComponent/constants.js +67 -69
  78. package/esm/components/Avatar/AvatarView/AvatarComponent/constants.js.map +1 -1
  79. package/esm/components/Avatar/AvatarView/index.d.ts +1 -1
  80. package/esm/components/Avatar/AvatarView/index.js +2 -2
  81. package/esm/components/Avatar/AvatarView/index.js.map +1 -1
  82. package/esm/components/Chat/Chat.js +2 -2
  83. package/esm/components/Chat/Chat.js.map +1 -1
  84. package/esm/components/ChatBubble/ChatBubble.js +12 -9
  85. package/esm/components/ChatBubble/ChatBubble.js.map +1 -1
  86. package/esm/components/ExpertsDrawer/ExpertsDrawer.js +1 -1
  87. package/esm/components/ExpertsDrawer/ExpertsDrawer.js.map +1 -1
  88. package/esm/components/LoginDrawer/LoginDrawer.js +6 -6
  89. package/esm/components/LoginDrawer/LoginDrawer.js.map +1 -1
  90. package/esm/components/MemoriWidget/MemoriWidget.js +143 -64
  91. package/esm/components/MemoriWidget/MemoriWidget.js.map +1 -1
  92. package/esm/components/SignupForm/SignupForm.js +4 -4
  93. package/esm/components/SignupForm/SignupForm.js.map +1 -1
  94. package/esm/components/StartPanel/StartPanel.js +5 -5
  95. package/esm/components/StartPanel/StartPanel.js.map +1 -1
  96. package/esm/components/UploadButton/UploadButton.js +2 -2
  97. package/esm/components/UploadButton/UploadButton.js.map +1 -1
  98. package/esm/components/WhyThisAnswer/WhyThisAnswer.css +43 -0
  99. package/esm/components/WhyThisAnswer/WhyThisAnswer.js +2 -1
  100. package/esm/components/WhyThisAnswer/WhyThisAnswer.js.map +1 -1
  101. package/esm/context/visemeContext.js +0 -39
  102. package/esm/context/visemeContext.js.map +1 -1
  103. package/esm/index.js +4 -3
  104. package/esm/index.js.map +1 -1
  105. package/esm/locales/de.json +1 -0
  106. package/esm/locales/en.json +1 -0
  107. package/esm/locales/es.json +1 -0
  108. package/esm/locales/fr.json +1 -0
  109. package/esm/locales/it.json +1 -0
  110. package/package.json +2 -2
  111. package/src/components/Avatar/Avatar.tsx +3 -3
  112. package/src/components/Avatar/AvatarView/AvatarComponent/Shadow/DynamicShadow.tsx +15 -8
  113. package/src/components/Avatar/AvatarView/AvatarComponent/avatarComponent.tsx +64 -219
  114. package/src/components/Avatar/AvatarView/AvatarComponent/components/FullbodyAvatar/fullbodyAvatar.tsx +221 -124
  115. package/src/components/Avatar/AvatarView/AvatarComponent/components/controllers/AvatarAnimator.ts +1250 -0
  116. package/src/components/Avatar/AvatarView/AvatarComponent/components/controllers/MorphTargetController.ts +164 -8
  117. package/src/components/Avatar/AvatarView/AvatarComponent/components/halfbodyAvatar.tsx +19 -17
  118. package/src/components/Avatar/AvatarView/AvatarComponent/constants.ts +80 -79
  119. package/src/components/Avatar/AvatarView/index.tsx +1 -7
  120. package/src/components/Chat/Chat.tsx +2 -2
  121. package/src/components/ChatBubble/ChatBubble.tsx +37 -26
  122. package/src/components/ExpertsDrawer/ExpertsDrawer.tsx +1 -1
  123. package/src/components/LoginDrawer/LoginDrawer.tsx +6 -6
  124. package/src/components/MemoriWidget/MemoriWidget.tsx +184 -78
  125. package/src/components/SignupForm/SignupForm.tsx +5 -5
  126. package/src/components/StartPanel/StartPanel.tsx +5 -5
  127. package/src/components/UploadButton/UploadButton.tsx +4 -4
  128. package/src/components/UploadButton/__snapshots__/UploadButton.test.tsx.snap +1 -1
  129. package/src/components/WhyThisAnswer/WhyThisAnswer.css +43 -0
  130. package/src/components/WhyThisAnswer/WhyThisAnswer.stories.tsx +44 -3
  131. package/src/components/WhyThisAnswer/WhyThisAnswer.test.tsx +128 -8
  132. package/src/components/WhyThisAnswer/WhyThisAnswer.tsx +28 -3
  133. package/src/components/WhyThisAnswer/__snapshots__/WhyThisAnswer.test.tsx.snap +15 -1
  134. package/src/components/layouts/layouts.stories.tsx +0 -8
  135. package/src/context/visemeContext.tsx +40 -41
  136. package/src/index.stories.tsx +63 -65
  137. package/src/index.tsx +5 -3
  138. package/src/locales/de.json +1 -0
  139. package/src/locales/en.json +1 -0
  140. package/src/locales/es.json +1 -0
  141. package/src/locales/fr.json +1 -0
  142. package/src/locales/it.json +1 -0
  143. package/src/mocks/data.ts +3 -9
  144. package/src/components/Avatar/AvatarView/AvatarComponent/components/controllers/AnimationController.ts +0 -308
  145. package/src/helpers/tenant.ts +0 -47
@@ -1,261 +1,106 @@
1
- // Import required dependencies
2
- import React, { useState, useEffect, useCallback } from 'react';
3
- import AnimationControlPanel from './components/controls';
1
+ import { useRef, useEffect, useCallback, memo } from 'react';
2
+ import { AvatarAnimator } from './components/controllers/AvatarAnimator';
4
3
  import { FullbodyAvatar } from './components/FullbodyAvatar/fullbodyAvatar';
5
4
  import HalfBodyAvatar from './components/halfbodyAvatar';
6
- import {
7
- BASE_ACTIONS,
8
- MAPPING_BLEND_SHAPE_TO_EMOTION_CUSTOM_GLB,
9
- MAPPING_BLEND_SHAPE_TO_EMOTION_RPM,
10
- } from './constants';
11
5
 
12
- // Props interface for AvatarView component
13
- interface Props {
14
- showControls: boolean;
15
- animation?: string;
16
- loading: boolean;
6
+ interface AvatarViewProps {
7
+ chatEmission: string | null | undefined;
17
8
  url: string;
18
9
  sex: 'MALE' | 'FEMALE';
19
10
  eyeBlink: boolean;
20
- headMovement: boolean;
21
- speaking: boolean;
22
- isZoomed: boolean;
23
- chatEmission: any;
24
- avatarHeight?: number;
25
- avatarDepth?: number;
26
- stopProcessing: () => void;
27
- resetVisemeQueue: () => void;
11
+ halfBody: boolean;
12
+ loading: boolean;
13
+ avatarHeight: number;
14
+ avatarDepth: number;
28
15
  updateCurrentViseme: (
29
16
  currentTime: number
30
17
  ) => { name: string; weight: number } | null;
31
- setCameraZ: (value: number) => void;
18
+ setCameraZ: (cameraZ: number) => void;
19
+ headMovement: boolean;
20
+ speaking: boolean;
21
+ showControls: boolean;
32
22
  }
33
23
 
34
- /**
35
- * AvatarView Component
36
- * Renders either a full body or half body 3D avatar with animations and morphing capabilities
37
- */
38
- export const AvatarView: React.FC<Props & { halfBody: boolean }> = ({
39
- stopProcessing,
24
+ // Memoized FullbodyAvatar component to prevent unnecessary re-renders
25
+ const MemoizedFullbodyAvatar = memo(FullbodyAvatar);
26
+
27
+ // Memoized HalfBodyAvatar component to prevent unnecessary re-renders
28
+ const MemoizedHalfBodyAvatar = memo(HalfBodyAvatar);
29
+
30
+ export const AvatarView = ({
40
31
  chatEmission,
41
- showControls,
42
- animation,
43
32
  url,
44
33
  sex,
45
34
  eyeBlink,
46
- headMovement,
47
35
  halfBody,
48
36
  loading,
49
37
  avatarHeight = 50,
50
38
  avatarDepth = -50,
51
39
  updateCurrentViseme,
52
- resetVisemeQueue,
53
40
  setCameraZ,
54
- }) => {
55
- // State management for avatar animations and morphing
56
- const [currentBaseAction, setCurrentBaseAction] = useState({
57
- action: animation || 'Idle1',
58
- weight: 1,
59
- });
60
- const [morphTargetInfluences, setMorphTargetInfluences] = useState<
61
- Record<string, number>
62
- >({});
63
- const [morphTargetDictionary, setMorphTargetDictionary] = useState<
64
- Record<string, number>
65
- >({});
66
- const [emotionMorphTargets, setEmotionMorphTargets] = useState<
67
- Record<string, number>
68
- >({});
69
- const [isRPM, setIsRPM] = useState(false);
70
- const [timeScale, setTimeScale] = useState(0.8);
71
-
72
- // Map of basic emotions with their corresponding morph values
73
- const emotionMap: Record<string, Record<string, number>> = {
74
- Joy: { Joy: 1 },
75
- Anger: { Anger: 1 },
76
- Surprise: { Surprise: 1 },
77
- Sadness: { Sadness: 1 },
78
- Fear: { Fear: 1 },
79
- };
80
-
81
- // Helper function to get default emotion state (all set to 0)
82
- const getDefaultEmotions = () =>
83
- Object.keys(emotionMap).reduce((acc, key) => ({ ...acc, [key]: 0 }), {});
84
-
85
- // Handlers for different blend shape types
86
- const handleRPMBlendShape = useCallback(
87
- (outputContent: string) =>
88
- MAPPING_BLEND_SHAPE_TO_EMOTION_RPM[
89
- outputContent as keyof typeof MAPPING_BLEND_SHAPE_TO_EMOTION_RPM
90
- ],
91
- []
92
- );
93
-
94
- const handleCustomGLBBlendShape = useCallback(
95
- (outputContent: string) =>
96
- MAPPING_BLEND_SHAPE_TO_EMOTION_CUSTOM_GLB[
97
- outputContent as keyof typeof MAPPING_BLEND_SHAPE_TO_EMOTION_CUSTOM_GLB
98
- ],
99
- []
100
- );
101
-
102
- // Handler for setting emotion morph target influences, used for RPM and GLB blend shapes
103
- const setEmotionMorphTargetInfluences = useCallback(
104
- (action: string, outputContent: string) => {
105
- if (action.startsWith('Loading')) return;
106
-
107
- const defaultEmotions = getDefaultEmotions();
108
-
109
- // If output content is default, set default emotions
110
- if (outputContent === 'default') {
111
- setEmotionMorphTargets(defaultEmotions);
112
- return;
113
- }
114
-
115
- // If RPM, convert emotion to blend shape
116
- /*from the chat output, we get the emotion and we convert it to the blend shapes
117
- * we map the emotion to the blend shape, example:
118
- * Anger -> {browDownLeft: 1, browDownRight: 0}
119
- * Joy -> {browUpLeft: 1, browUpRight: 0}
120
- * Surprise -> {browUpLeft: 1, browUpRight: 0}
121
- * Sadness -> {browDownLeft: 1, browDownRight: 0}
122
- * Fear -> {browDownLeft: 1, browDownRight: 0}
123
- */
124
- if (isRPM) {
125
- const emotion = handleRPMBlendShape(outputContent);
126
- setEmotionMorphTargets(_ => ({ ...defaultEmotions, ...emotion }));
127
- } else {
128
- // If GLB, convert italian emotions to english ones
129
- const emotion = handleCustomGLBBlendShape(outputContent);
130
- const emotionValues =
131
- emotion === 'default' ? defaultEmotions : emotionMap[emotion];
132
- setEmotionMorphTargets(_ => ({ ...defaultEmotions, ...emotionValues }));
133
- }
134
- },
135
- [isRPM, handleRPMBlendShape, handleCustomGLBBlendShape]
136
- );
137
-
138
- // Callback handlers for various avatar state changes
139
- const onBaseActionChange = useCallback(
140
- (action: string, outputContent: string) => {
141
- // Set emotion morph target influences
142
- setEmotionMorphTargetInfluences(action, outputContent);
143
-
144
- // Set current base action
145
- setCurrentBaseAction({ action, weight: 1 });
146
- },
147
- [setEmotionMorphTargetInfluences]
148
- );
149
-
150
- const onMorphTargetInfluencesChange = useCallback(
151
- (influences: Record<string, number>) => {
152
- // Set morph target influences
153
- setMorphTargetInfluences(prev => ({ ...prev, ...influences }));
154
- },
155
- []
156
- );
157
-
158
- const onMorphTargetDictionaryChange = useCallback(
159
- (dictionary: Record<string, number>) => {
160
- // Set morph target dictionary
161
- setMorphTargetDictionary(dictionary);
162
- },
163
- []
164
- );
165
-
166
- // Effect to handle animation changes based on loading state and chat emissions
167
- useEffect(() => {
168
- // If loading, set a random loading animation
169
- if (loading) {
170
- const randomNumber = Math.floor(Math.random() * 3) + 1;
171
- onBaseActionChange(`Loading${randomNumber}`, '');
172
- return;
41
+ headMovement,
42
+ // speaking,
43
+ showControls,
44
+ }: AvatarViewProps) => {
45
+ // Reference to the AvatarAnimator instance for animation control
46
+ const animatorRef = useRef<AvatarAnimator | null>(null);
47
+
48
+ // Memoize the setAnimatorRef callback to ensure it doesn't change on re-renders
49
+ const setAnimatorRef = useCallback((animator: AvatarAnimator | null) => {
50
+ if (animator !== animatorRef.current) {
51
+ console.log('[AvatarView] Animator reference updated');
52
+ animatorRef.current = animator;
173
53
  }
54
+ }, []); // Empty dependency array ensures this function never changes
174
55
 
175
- // Check if chat emission contains animation control
176
- const hasOutputTagEmotion = chatEmission?.includes(
177
- '<output class="memori-emotion">'
178
- );
179
- const outputContentEmotion = hasOutputTagEmotion
180
- ? chatEmission
181
- ?.split('<output class="memori-emotion">')[1]
182
- ?.split('</output>')[0]
183
- ?.trim()
184
- : null;
185
-
186
- // Check if chat emission contains animation sequence
187
- const hasOutputTagSequence = chatEmission?.includes(
188
- '<output class="animation-sequence">'
189
- );
190
- const outputContentSequence = hasOutputTagSequence
191
- ? chatEmission
192
- ?.split('<output class="animation-sequence">')[1]
193
- ?.split('</output>')[0]
194
- ?.trim()
195
- : null;
196
-
197
- if (outputContentSequence && outputContentSequence.includes('->')) {
198
- // It's a sequence
199
- onBaseActionChange(outputContentSequence, outputContentSequence);
200
- } else if (outputContentEmotion) {
201
-
202
- console.log('[AvatarView] outputContentEmotion:', outputContentEmotion);
203
- // It's an emotion
204
- const randomNumber = Math.floor(Math.random() * 3) + 1;
205
- onBaseActionChange(
206
- `${outputContentEmotion}${randomNumber}`,
207
- outputContentEmotion
208
- );
209
- } else {
210
- const randomNumber = Math.floor(Math.random() * 5) + 1;
211
- onBaseActionChange(`Idle${randomNumber === 3 ? 4 : randomNumber}`, '');
56
+ // React to changes in chat emission or loading state
57
+ // This effect doesn't re-create the animator, it just uses it
58
+ useEffect(() => {
59
+ if (!animatorRef.current) return;
60
+
61
+ try {
62
+ // Prevents any extra work if loading/emission hasn't changed
63
+ animatorRef.current.processChatEmission(chatEmission, loading);
64
+ } catch (error) {
65
+ console.error('Error processing chat emission:', error);
212
66
  }
213
- }, [chatEmission, loading, onBaseActionChange]);
67
+ }, [loading, chatEmission]); // Both dependencies are needed
214
68
 
215
- // Common props shared between full body and half body avatars
216
- const commonAvatarProps = {
69
+ // Memoize common props to prevent unnecessary object creation on re-renders
70
+ const commonAvatarProps = useCallback(() => ({
217
71
  url,
218
72
  onCameraZChange: setCameraZ,
219
- setMorphTargetInfluences,
220
- setMorphTargetDictionary,
221
73
  updateCurrentViseme,
222
74
  avatarHeight,
223
75
  avatarDepth,
224
- };
76
+ setAnimatorRef, // This is now stable across renders
77
+ }), [url, setCameraZ, updateCurrentViseme, avatarHeight, avatarDepth, setAnimatorRef]);
225
78
 
226
- // Render avatar with controls
79
+ // Render avatar based on halfBody flag
227
80
  return (
228
81
  <>
229
- {showControls && (
230
- <AnimationControlPanel
231
- timeScale={timeScale}
232
- morphTargetDictionary={morphTargetDictionary}
233
- onBaseActionChange={onBaseActionChange}
234
- onMorphTargetInfluencesChange={onMorphTargetInfluencesChange}
235
- onMorphTargetDictionaryChange={onMorphTargetDictionaryChange}
236
- baseActions={BASE_ACTIONS}
237
- currentBaseAction={currentBaseAction}
238
- modifyTimeScale={setTimeScale}
239
- />
240
- )}
241
-
242
82
  {halfBody ? (
243
- <HalfBodyAvatar {...commonAvatarProps} headMovement={headMovement} />
83
+ <MemoizedHalfBodyAvatar
84
+ {...commonAvatarProps()}
85
+ eyeBlink={eyeBlink}
86
+ headMovement={headMovement}
87
+ />
244
88
  ) : (
245
- <FullbodyAvatar
246
- {...commonAvatarProps}
89
+ <MemoizedFullbodyAvatar
90
+ {...commonAvatarProps()}
247
91
  sex={sex}
248
- setIsRpm={setIsRPM}
249
- resetVisemeQueue={resetVisemeQueue}
250
92
  eyeBlink={eyeBlink}
251
- currentBaseAction={currentBaseAction}
252
- timeScale={timeScale}
253
- morphTargetInfluences={morphTargetInfluences}
254
- stopProcessing={stopProcessing}
255
- emotionMorphTargets={emotionMorphTargets}
256
- halfBody={halfBody}
93
+ chatEmission={chatEmission}
94
+ loading={loading}
257
95
  />
258
96
  )}
97
+ {showControls && animatorRef.current && (
98
+ <div className="animation-controls">
99
+ {/* Optional animation control UI could be added here */}
100
+ </div>
101
+ )}
259
102
  </>
260
103
  );
261
104
  };
105
+
106
+ export default memo(AvatarView); // Memoize the entire component