@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,6 +1,14 @@
1
1
  import { SkinnedMesh } from 'three';
2
2
  import { MathUtils } from 'three';
3
- import { EMOTION_SMOOTHING, VISEME_SMOOTHING, BLINK_CONFIG } from '../../constants';
3
+ import {
4
+ EMOTION_SMOOTHING,
5
+ VISEME_SMOOTHING,
6
+ BLINK_CONFIG,
7
+ MAPPING_EMOTIONS_ITALIAN_TO_ENGLISH,
8
+ MAPPING_BLEND_SHAPE_TO_EMOTION_RPM,
9
+ EmotionMapping,
10
+ BlendShapeMap,
11
+ } from '../../constants';
4
12
 
5
13
  /**
6
14
  * Controller class for handling morph target animations including emotions, visemes and blinking
@@ -9,16 +17,155 @@ export class MorphTargetController {
9
17
  private headMesh: SkinnedMesh;
10
18
  private currentEmotionValues: Record<string, number> = {};
11
19
  private previousEmotionKeys: Set<string> = new Set();
20
+ private isRPM: boolean = false;
12
21
 
13
- constructor(headMesh: SkinnedMesh) {
22
+ // Default RPM blend shape mappings
23
+ private rpmBlendShapes: BlendShapeMap =
24
+ MAPPING_BLEND_SHAPE_TO_EMOTION_RPM as BlendShapeMap;
25
+
26
+ // Default custom GLB emotion mappings
27
+ private customGlbMapping: EmotionMapping =
28
+ MAPPING_EMOTIONS_ITALIAN_TO_ENGLISH as EmotionMapping;
29
+
30
+ /**
31
+ * Constructor for MorphTargetController
32
+ * @param headMesh SkinnedMesh representing the head
33
+ * @param rpmBlendShapes Optional custom RPM blend shapes
34
+ * @param customGlbMapping Optional custom GLB emotion mapping
35
+ */
36
+ constructor(
37
+ headMesh: SkinnedMesh,
38
+ rpmBlendShapes?: BlendShapeMap,
39
+ customGlbMapping?: EmotionMapping
40
+ ) {
14
41
  this.headMesh = headMesh;
42
+
43
+ // Detect if this is an RPM avatar based on mesh name
44
+ this.isRPM = headMesh.name !== 'GBNL__Head';
45
+
46
+ // Override default maps if provided
47
+ if (rpmBlendShapes) {
48
+ this.rpmBlendShapes = rpmBlendShapes;
49
+ }
50
+
51
+ if (customGlbMapping) {
52
+ this.customGlbMapping = customGlbMapping;
53
+ }
54
+ }
55
+
56
+ /**
57
+ * Process chat emission to extract emotion data
58
+ * @param chatEmission Chat emission text
59
+ * @param isLoading Loading state
60
+ * @returns Object with emotion morph targets
61
+ */
62
+ processChatEmission(
63
+ chatEmission: any,
64
+ isLoading: boolean
65
+ ): Record<string, number> {
66
+ // Default empty emotion targets
67
+ const defaultEmotions = this.getDefaultEmotionMorphTargets();
68
+
69
+ // If loading or no chat emission, return default emotions
70
+ if (isLoading || !chatEmission) {
71
+ return defaultEmotions;
72
+ }
73
+
74
+ // Check if chat emission contains emotion tag
75
+ const hasOutputTagEmotion = chatEmission?.includes(
76
+ '<output class="memori-emotion">'
77
+ );
78
+
79
+ if (!hasOutputTagEmotion) {
80
+ return defaultEmotions;
81
+ }
82
+
83
+ // Extract emotion name
84
+ const outputContentEmotion = chatEmission
85
+ ?.split('<output class="memori-emotion">')[1]
86
+ ?.split('</output>')[0]
87
+ ?.trim();
88
+
89
+ if (!outputContentEmotion) {
90
+ return defaultEmotions;
91
+ }
92
+
93
+ // Process emotion based on avatar type
94
+ return this.processEmotion(outputContentEmotion);
95
+ }
96
+
97
+ /**
98
+ * Process an emotion name into morph target values
99
+ * @param emotionName Emotion name (in Italian or English)
100
+ * @returns Object with morph target values
101
+ */
102
+ private processEmotion(emotionName: string): Record<string, number> {
103
+ // Get default empty emotion targets
104
+ const defaultEmotions = this.getDefaultEmotionMorphTargets();
105
+
106
+ // First, try to find the emotion regardless of language
107
+ if (this.isRPM) {
108
+ // For RPM avatars, find the matching emotion in the rpmBlendShapes array
109
+ const foundEmotion = this.rpmBlendShapes.find(
110
+ item =>
111
+ item.emotion.italian.toLowerCase() === emotionName.toLowerCase() ||
112
+ item.emotion.english.toLowerCase() === emotionName.toLowerCase()
113
+ );
114
+
115
+ if (foundEmotion) {
116
+ return { ...defaultEmotions, ...foundEmotion.blendShapes };
117
+ }
118
+ } else {
119
+ // For custom GLB, find the matching emotion in the customGlbMapping array
120
+ const foundEmotion = this.customGlbMapping.find(
121
+ item =>
122
+ item.italian.toLowerCase() === emotionName.toLowerCase() ||
123
+ item.english.toLowerCase() === emotionName.toLowerCase()
124
+ );
125
+
126
+ if (foundEmotion) {
127
+ return { ...defaultEmotions, [foundEmotion.english]: 1 };
128
+ }
129
+ }
130
+
131
+ console.log('[MorphTargetController] No emotion found:', emotionName);
132
+ return defaultEmotions;
133
+ }
134
+
135
+ /**
136
+ * Get default emotion morph targets (all set to 0)
137
+ */
138
+ private getDefaultEmotionMorphTargets(): Record<string, number> {
139
+ // For RPM, collect all blend shape keys
140
+ if (this.isRPM) {
141
+ const allBlendShapeKeys = new Set<string>();
142
+
143
+ this.rpmBlendShapes.forEach(item => {
144
+ Object.keys(item.blendShapes).forEach(key =>
145
+ allBlendShapeKeys.add(key)
146
+ );
147
+ });
148
+
149
+ return Array.from(allBlendShapeKeys).reduce(
150
+ (acc, key) => ({ ...acc, [key]: 0 }),
151
+ {}
152
+ );
153
+ }
154
+ // For custom GLB, use English emotion names
155
+ else {
156
+ return this.customGlbMapping.reduce(
157
+ (acc, emotion) => ({ ...acc, [emotion.english]: 0 }),
158
+ {}
159
+ );
160
+ }
15
161
  }
16
162
  /**
17
163
  * Updates the morph target influences for emotions, visemes and blinking
18
164
  */
19
165
  updateMorphTargets(
20
166
  currentTime: number,
21
- emotionMorphTargets: Record<string, number>,
167
+ chatEmission: any,
168
+ isLoading: boolean,
22
169
  currentViseme: { name: string; weight: number } | null,
23
170
  eyeBlink: boolean,
24
171
  blinkState: {
@@ -33,17 +180,25 @@ export class MorphTargetController {
33
180
  !this.headMesh.morphTargetDictionary ||
34
181
  !this.headMesh.morphTargetInfluences
35
182
  ) {
36
- console.error('[MorphTargetController] Missing morphTargetDictionary or morphTargetInfluences');
183
+ console.error(
184
+ '[MorphTargetController] Missing morphTargetDictionary or morphTargetInfluences'
185
+ );
37
186
  return;
38
187
  }
39
188
 
189
+ // Process chat emission to get emotion morph targets
190
+ const emotionMorphTargets = this.processChatEmission(
191
+ chatEmission,
192
+ isLoading
193
+ );
194
+
40
195
  // Calculate blink value for this frame
41
196
  const blinkValue = this.calculateBlinkValue(
42
197
  currentTime,
43
198
  blinkState,
44
199
  eyeBlink
45
200
  );
46
-
201
+
47
202
  const currentEmotionKeys = new Set(Object.keys(emotionMorphTargets));
48
203
 
49
204
  // Process each morph target
@@ -55,6 +210,7 @@ export class MorphTargetController {
55
210
 
56
211
  // Handle emotion morphs with smoothing
57
212
  if (currentEmotionKeys.has(key)) {
213
+ // console.log('[MorphTargetController] Processing morph target:', key);
58
214
  const targetEmotionValue = emotionMorphTargets[key];
59
215
  const currentEmotionValue = this.currentEmotionValues[key] || 0;
60
216
  const newEmotionValue = MathUtils.lerp(
@@ -124,15 +280,15 @@ export class MorphTargetController {
124
280
  if (blinkState.isBlinking) {
125
281
  const blinkProgress =
126
282
  (currentTime - blinkState.blinkStartTime) / BLINK_CONFIG.blinkDuration;
127
-
283
+
128
284
  // First half of blink - closing eyes
129
285
  if (blinkProgress <= 0.5) {
130
286
  blinkValue = blinkProgress * 2;
131
- }
287
+ }
132
288
  // Second half of blink - opening eyes
133
289
  else if (blinkProgress <= 1) {
134
290
  blinkValue = 2 - blinkProgress * 2;
135
- }
291
+ }
136
292
  // Blink complete
137
293
  else {
138
294
  blinkState.isBlinking = false;
@@ -15,22 +15,19 @@ import useHeadMovement from '../../utils/useHeadMovement';
15
15
 
16
16
  interface HalfBodyAvatarProps {
17
17
  url: string;
18
- setMorphTargetInfluences: (morphTargetInfluences: any) => void;
19
- setMorphTargetDictionary: (morphTargetDictionary: any) => void;
20
18
  updateCurrentViseme: (currentTime: number) => any;
21
19
  eyeBlink?: boolean;
22
- heightValue?: number; // 0-100 slider value
23
20
  avatarHeight: number;
24
21
  avatarDepth: number;
25
22
  onLoaded?: () => void;
26
23
  onCameraZChange: (value: number) => void;
27
24
  headMovement?: boolean;
25
+ chatEmission?: any;
26
+ loading?: boolean;
28
27
  }
29
28
 
30
29
  export default function HalfBodyAvatar({
31
30
  url,
32
- setMorphTargetInfluences,
33
- setMorphTargetDictionary,
34
31
  updateCurrentViseme,
35
32
  eyeBlink = false,
36
33
  avatarHeight = 50,
@@ -38,18 +35,22 @@ export default function HalfBodyAvatar({
38
35
  headMovement = false,
39
36
  onLoaded,
40
37
  onCameraZChange,
38
+ chatEmission,
39
+ loading = false,
41
40
  }: HalfBodyAvatarProps) {
42
41
  const { scene } = useGLTF(url);
43
42
  const { nodes, materials } = useGraph(scene);
44
43
  const { camera } = useThree();
45
44
 
46
- const morphTargetControllerRef = useRef<MorphTargetController>();
47
- const positionControllerRef = useRef<AvatarPositionController>();
45
+ const morphTargetControllerRef = useRef<MorphTargetController | null>(null);
46
+ const positionControllerRef = useRef<AvatarPositionController | null>(null);
48
47
  const targetCameraZRef = useRef(camera.position.z);
49
48
 
50
-
49
+ // Apply head movement if enabled
51
50
  useHeadMovement(headMovement, nodes);
52
51
 
52
+
53
+ // Eye blinking state
53
54
  const blinkStateRef = useRef({
54
55
  isBlinking: false,
55
56
  lastBlinkTime: 0,
@@ -63,7 +64,7 @@ export default function HalfBodyAvatar({
63
64
  scene?.traverse((object: Object3D) => {
64
65
  if (
65
66
  object instanceof SkinnedMesh &&
66
- (object.name === 'GBNL__Head' || object.name === 'Wolf3D_Avatar')
67
+ (object.name === 'GBNL__Head' || object.name === 'Wolf3D_Avatar' || object.name === 'Wolf3D_Avatar006_1')
67
68
  ) {
68
69
  foundMesh = object;
69
70
  }
@@ -80,21 +81,17 @@ export default function HalfBodyAvatar({
80
81
  );
81
82
  }
82
83
 
84
+ // Initialize MorphTargetController if head mesh exists
83
85
  if (headMesh) {
84
86
  morphTargetControllerRef.current = new MorphTargetController(headMesh);
85
-
86
- if (headMesh.morphTargetDictionary && headMesh.morphTargetInfluences) {
87
- setMorphTargetDictionary(headMesh.morphTargetDictionary);
88
- const initialInfluences = Object.keys(headMesh.morphTargetDictionary)
89
- .reduce((acc, key) => ({ ...acc, [key]: 0 }), {});
90
- setMorphTargetInfluences(initialInfluences);
91
- }
92
87
  }
93
88
 
89
+ // Correct materials and perform other initialization tasks
94
90
  correctMaterials(materials);
95
91
  onLoaded?.();
96
92
  hideHands(nodes);
97
93
 
94
+ // Cleanup on unmount
98
95
  return () => {
99
96
  Object.values(materials).forEach(material => material.dispose());
100
97
  Object.values(nodes)
@@ -103,12 +100,14 @@ export default function HalfBodyAvatar({
103
100
  };
104
101
  }, [materials, nodes, url, onLoaded, scene, headMesh]);
105
102
 
103
+ // Handle avatar height changes
106
104
  useEffect(() => {
107
105
  if (positionControllerRef.current) {
108
106
  positionControllerRef.current.updateHeight(avatarHeight, true);
109
107
  }
110
108
  }, [avatarHeight]);
111
109
 
110
+ // Handle avatar depth changes
112
111
  useEffect(() => {
113
112
  if (positionControllerRef.current && onCameraZChange) {
114
113
  const newCameraZ = positionControllerRef.current.updateDepth(avatarDepth, true);
@@ -123,9 +122,12 @@ export default function HalfBodyAvatar({
123
122
  // Update morph targets
124
123
  if (morphTargetControllerRef.current) {
125
124
  const currentViseme = updateCurrentViseme(currentTime / 1000);
125
+
126
+ // Use the updated MorphTargetController that handles chat emission directly
126
127
  morphTargetControllerRef.current.updateMorphTargets(
127
128
  currentTime,
128
- {},
129
+ chatEmission,
130
+ loading,
129
131
  currentViseme,
130
132
  eyeBlink,
131
133
  blinkStateRef.current,
@@ -11,94 +11,95 @@ export const SCALE_LERP_FACTOR = 0.1;
11
11
 
12
12
  // Maximum number of idle loops before forcing change
13
13
  export const MAX_IDLE_LOOPS_DEFAULT = 5;
14
-
15
-
16
- interface BaseAction {
17
- weight: number;
18
- action?: string;
14
+ // Type definitions for emotion handling
15
+ interface EmotionMappingItem {
16
+ italian: string;
17
+ english: string;
19
18
  }
19
+ export type EmotionMapping = EmotionMappingItem[];
20
20
 
21
- // Base animations for the avatar
22
- export const BASE_ACTIONS: Record<string, BaseAction> = {
23
- Gioia1: { weight: 0 },
24
- Gioia2: { weight: 0 },
25
- Gioia3: { weight: 0 },
26
- Idle1: { weight: 1 },
27
- Idle2: { weight: 0 },
28
- Idle3: { weight: 0 },
29
- Idle4: { weight: 0 },
30
- Idle5: { weight: 0 },
31
- Rabbia1: { weight: 0 },
32
- Rabbia2: { weight: 0 },
33
- Rabbia3: { weight: 0 },
34
- Sorpresa1: { weight: 0 },
35
- Sorpresa2: { weight: 0 },
36
- Sorpresa3: { weight: 0 },
37
- Timore1: { weight: 0 },
38
- Timore2: { weight: 0 },
39
- Timore3: { weight: 0 },
40
- Tristezza1: { weight: 0 },
41
- Tristezza2: { weight: 0 },
42
- Tristezza3: { weight: 0 },
43
- Loading1: { weight: 0 },
44
- Loading2: { weight: 0 },
45
- Loading3: { weight: 0 },
46
- };
47
-
48
- // Mapping of emotions from Italian to English
49
- export const MAPPING_EMOTIONS_ITALIAN_TO_ENGLISH = {
50
- Gioia: 'Joy',
51
- Rabbia: 'Anger',
52
- Sorpresa: 'Surprise',
53
- Tristezza: 'Sadness',
54
- Timore: 'Fear',
55
- };
21
+ interface BlendShapeMapItem {
22
+ emotion: { italian: string; english: string };
23
+ blendShapes: Record<string, number>;
24
+ }
25
+ export type BlendShapeMap = BlendShapeMapItem[];
56
26
 
57
- // Mapping of blend shapes to emotions
58
- export const MAPPING_BLEND_SHAPE_TO_EMOTION_RPM = {
59
- Rabbia: {
60
- 'browDownLeft': 0.5,
61
- 'browDownRight': 0.5,
62
- 'browOuterUpLeft': 0.5,
63
- 'browOuterUpRight': 0.5,
64
- 'mouthSmile': -0.2,
27
+ // Italian to English emotion mapping
28
+ // Italian to English emotion mapping
29
+ export const MAPPING_EMOTIONS_ITALIAN_TO_ENGLISH: EmotionMapping = [
30
+ {
31
+ italian: 'Gioia',
32
+ english: 'Joy',
65
33
  },
66
- Timore: {
67
- 'browOuterUpLeft': -0.5,
68
- 'browOuterUpRight': -0.5,
69
- 'eyeWideLeft': -0.5,
70
- 'eyeWideRight': -0.5,
34
+ {
35
+ italian: 'Rabbia',
36
+ english: 'Anger',
71
37
  },
72
- Tristezza: {
73
- 'browDownLeft': -0.5,
74
- 'browDownRight': -0.5,
75
- 'eyeSquintLeft': 0.5,
76
- 'eyeSquintRight': 0.5,
77
- 'mouthSmile': -0.6,
38
+ {
39
+ italian: 'Sorpresa',
40
+ english: 'Surprise',
78
41
  },
79
- Sorpresa: {
80
- 'browInnerUp': 0.5,
81
- 'browOuterUpLeft': 0.5,
82
- 'browOuterUpRight': 0.5,
83
- 'eyeWideLeft': 0.5,
84
- 'eyeWideRight': 0.5,
42
+ {
43
+ italian: 'Tristezza',
44
+ english: 'Sadness',
85
45
  },
86
- Gioia: {
87
- 'browDownLeft': 0.5,
88
- 'browDownRight': 0.5,
89
- 'browInnerUp': 0.5,
90
- 'mouthSmile': 0.8,
46
+ {
47
+ italian: 'Timore',
48
+ english: 'Fear',
91
49
  },
92
- };
50
+ ];
93
51
 
94
- // Mapping of blend shapes to emotions for custom GLB
95
- export const MAPPING_BLEND_SHAPE_TO_EMOTION_CUSTOM_GLB = {
96
- Gioia: 'Joy',
97
- Rabbia: 'Anger',
98
- Sorpresa: 'Surprise',
99
- Tristezza: 'Sadness',
100
- Timore: 'Fear',
101
- };
52
+ // Mapping of blend shapes to emotions for RPM avatars
53
+ export const MAPPING_BLEND_SHAPE_TO_EMOTION_RPM: BlendShapeMap = [
54
+ {
55
+ emotion: { italian: 'Rabbia', english: 'Anger' },
56
+ blendShapes: {
57
+ 'browDownLeft': 0.5,
58
+ 'browDownRight': 0.5,
59
+ 'browOuterUpLeft': 0.5,
60
+ 'browOuterUpRight': 0.5,
61
+ 'mouthSmile': -0.2,
62
+ },
63
+ },
64
+ {
65
+ emotion: { italian: 'Timore', english: 'Fear' },
66
+ blendShapes: {
67
+ 'browOuterUpLeft': -0.5,
68
+ 'browOuterUpRight': -0.5,
69
+ 'eyeWideLeft': -0.5,
70
+ 'eyeWideRight': -0.5,
71
+ },
72
+ },
73
+ {
74
+ emotion: { italian: 'Tristezza', english: 'Sadness' },
75
+ blendShapes: {
76
+ 'browDownLeft': -0.5,
77
+ 'browDownRight': -0.5,
78
+ 'eyeSquintLeft': 0.5,
79
+ 'eyeSquintRight': 0.5,
80
+ 'mouthSmile': -0.6,
81
+ },
82
+ },
83
+ {
84
+ emotion: { italian: 'Sorpresa', english: 'Surprise' },
85
+ blendShapes: {
86
+ 'browInnerUp': 0.5,
87
+ 'browOuterUpLeft': 0.5,
88
+ 'browOuterUpRight': 0.5,
89
+ 'eyeWideLeft': 0.5,
90
+ 'eyeWideRight': 0.5,
91
+ },
92
+ },
93
+ {
94
+ emotion: { italian: 'Gioia', english: 'Joy' },
95
+ blendShapes: {
96
+ 'browDownLeft': 0.5,
97
+ 'browDownRight': 0.5,
98
+ 'browInnerUp': 0.5,
99
+ 'mouthSmile': 0.8,
100
+ },
101
+ },
102
+ ];
102
103
 
103
104
  // URL for the male avatar
104
105
  export const ANIMATION_URLS = {
@@ -79,12 +79,10 @@ export default function ContainerAvatarView({
79
79
  fallbackImg,
80
80
  halfBody = true,
81
81
  loading,
82
- animation,
82
+ // animation,
83
83
  showControls = false,
84
84
  isZoomed,
85
85
  chatEmission,
86
- stopProcessing,
87
- resetVisemeQueue,
88
86
  updateCurrentViseme,
89
87
  enablePositionControls,
90
88
  setEnablePositionControls,
@@ -179,16 +177,12 @@ export default function ContainerAvatarView({
179
177
  sex={sex}
180
178
  showControls={showControls}
181
179
  loading={loading || false}
182
- animation={animation}
183
- isZoomed={isZoomed || false}
184
180
  eyeBlink={eyeBlink || false}
185
181
  headMovement={headMovement || false}
186
182
  speaking={speaking || false}
187
183
  halfBody={halfBody}
188
184
  chatEmission={chatEmission}
189
185
  updateCurrentViseme={updateCurrentViseme}
190
- stopProcessing={stopProcessing}
191
- resetVisemeQueue={resetVisemeQueue}
192
186
  setCameraZ={setCameraZ}
193
187
  avatarHeight={avatarHeight}
194
188
  avatarDepth={avatarDepth}
@@ -193,13 +193,13 @@ const Chat: React.FC<Props> = ({
193
193
  style={{
194
194
  backgroundImage: `url("${getResourceUrl({
195
195
  type: 'cover',
196
- tenantID: tenant?.id,
196
+ tenantID: tenant?.name,
197
197
  resourceURI: memori.coverURL,
198
198
  baseURL: baseUrl,
199
199
  apiURL: apiUrl,
200
200
  })}"), url("${getResourceUrl({
201
201
  type: 'cover',
202
- tenantID: tenant?.id,
202
+ tenantID: tenant?.name,
203
203
  baseURL: baseUrl || 'https://www.aisuru.com',
204
204
  apiURL: apiUrl,
205
205
  })}")`,
@@ -97,33 +97,32 @@ const renderMsg = (text: string, useMathFormatting = false): string => {
97
97
  if (useMathFormatting) {
98
98
  // Normalizza tutti i delimitatori LaTeX per equazioni su linea separata
99
99
  // Da \\[ ... \\] o \\[ ... ] a $$ ... $$
100
- preprocessedText = preprocessedText.replace(
101
- /\\+\[(.*?)\\*\]/gs,
102
- (_, content) => {
103
- return `$$${content}$$`;
104
- }
105
- );
100
+ preprocessedText = preprocessedText.replace(
101
+ /\\+\[(.*?)\\*\]/gs,
102
+ (_, content) => {
103
+ return `$$${content}$$`;
104
+ }
105
+ );
106
106
 
107
107
  // Gestione dei delimitatori [ ... ] che dovrebbero essere equazioni
108
- preprocessedText = preprocessedText.replace(
109
- /\[([^[\]]+?)\]/g,
110
- (match, content) => {
108
+ preprocessedText = preprocessedText.replace(
109
+ /\[([^[\]]+?)\]/g,
110
+ (match, content) => {
111
111
  // Verifica se sembra una formula matematica
112
- if (/[\\+a-z0-9_{}^=\-\+\*\/]+/i.test(content) &&
113
- !match.startsWith('[http') &&
114
- !match.includes('](')) {
115
- return `$$${content}$$`;
116
- }
112
+ if (
113
+ /[\\+a-z0-9_{}^=\-\+\*\/]+/i.test(content) &&
114
+ !match.startsWith('[http') &&
115
+ !match.includes('](')
116
+ ) {
117
+ return `$$${content}$$`;
118
+ }
117
119
  return match; // Mantieni invariati i link e altre strutture
118
- }
119
- );
120
+ }
121
+ );
120
122
  }
121
123
 
122
124
  // Ora procedi con il parsing markdown
123
- let parsedText = marked
124
- .parse(preprocessedText)
125
- .toString()
126
- .trim();
125
+ let parsedText = marked.parse(preprocessedText).toString().trim();
127
126
 
128
127
  // Sanitize HTML
129
128
  parsedText = DOMPurify.sanitize(parsedText, {
@@ -291,13 +290,13 @@ const ChatBubble: React.FC<Props> = ({
291
290
  : memori.avatarURL && memori.avatarURL.length > 0
292
291
  ? getResourceUrl({
293
292
  type: 'avatar',
294
- tenantID: tenant?.id,
293
+ tenantID: tenant?.name,
295
294
  resourceURI: memori.avatarURL,
296
295
  baseURL: baseUrl,
297
296
  apiURL: apiUrl,
298
297
  })
299
298
  : getResourceUrl({
300
- tenantID: tenant?.id,
299
+ tenantID: tenant?.name,
301
300
  type: 'avatar',
302
301
  baseURL: baseUrl || 'https://www.aisuru.com',
303
302
  apiURL: apiUrl,
@@ -309,12 +308,12 @@ const ChatBubble: React.FC<Props> = ({
309
308
  memori.avatarURL && memori.avatarURL.length > 0
310
309
  ? getResourceUrl({
311
310
  type: 'avatar',
312
- tenantID: tenant?.id,
311
+ tenantID: tenant?.name,
313
312
  resourceURI: memori.avatarURL,
314
313
  baseURL: baseUrl,
315
314
  })
316
315
  : getResourceUrl({
317
- tenantID: tenant?.id,
316
+ tenantID: tenant?.name,
318
317
  type: 'avatar',
319
318
  baseURL: baseUrl,
320
319
  });
@@ -397,11 +396,23 @@ const ChatBubble: React.FC<Props> = ({
397
396
  {message.generatedByAI && showAIicon && (
398
397
  <Tooltip
399
398
  align="left"
400
- content={t('generatedByAI')}
399
+ content={
400
+ t('generatedByAI') ||
401
+ (lang === 'it'
402
+ ? 'Risposta generata da IA, può talvolta generare informazioni non corrette'
403
+ : 'Answer generated by AI, may occasionally generate incorrect informations')
404
+ }
401
405
  className="memori-chat--bubble-action-icon memori-chat--bubble-action-icon--ai"
402
406
  >
403
407
  <span>
404
- <AI title={t('generatedByAI') || undefined} />
408
+ <AI
409
+ title={
410
+ t('generatedByAI') ||
411
+ (lang === 'it'
412
+ ? 'Risposta generata da IA, può talvolta generare informazioni non corrette'
413
+ : 'Answer generated by AI, may occasionally generate incorrect informations')
414
+ }
415
+ />
405
416
  </span>
406
417
  </Tooltip>
407
418
  )}
@@ -47,7 +47,7 @@ const ExpertsDrawer = ({
47
47
  alt={expert.name}
48
48
  onError={e => {
49
49
  e.currentTarget.src = getResourceUrl({
50
- tenantID: tenant?.id,
50
+ tenantID: tenant?.name,
51
51
  type: 'avatar',
52
52
  baseURL: baseUrl,
53
53
  });