@memori.ai/memori-react 7.11.4 → 7.12.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 (152) hide show
  1. package/CHANGELOG.md +41 -0
  2. package/README.md +2 -1
  3. package/dist/components/Avatar/AvatarView/AvatarComponent/avatarComponent.js +52 -75
  4. package/dist/components/Avatar/AvatarView/AvatarComponent/avatarComponent.js.map +1 -1
  5. package/dist/components/Avatar/AvatarView/AvatarComponent/components/FullbodyAvatar/AnimationController.js +2 -2
  6. package/dist/components/Avatar/AvatarView/AvatarComponent/components/FullbodyAvatar/AnimationController.js.map +1 -1
  7. package/dist/components/Avatar/AvatarView/AvatarComponent/components/FullbodyAvatar/fullbodyAvatar.d.ts +1 -1
  8. package/dist/components/Avatar/AvatarView/AvatarComponent/components/FullbodyAvatar/fullbodyAvatar.js +8 -2
  9. package/dist/components/Avatar/AvatarView/AvatarComponent/components/FullbodyAvatar/fullbodyAvatar.js.map +1 -1
  10. package/dist/components/Avatar/AvatarView/AvatarComponent/components/FullbodyAvatar/types.d.ts +1 -0
  11. package/dist/components/Avatar/AvatarView/AvatarComponent/components/MorphTargetController.js +6 -3
  12. package/dist/components/Avatar/AvatarView/AvatarComponent/components/MorphTargetController.js.map +1 -1
  13. package/dist/components/Avatar/AvatarView/AvatarComponent/components/PositionController.js +1 -1
  14. package/dist/components/Avatar/AvatarView/AvatarComponent/components/PositionController.js.map +1 -1
  15. package/dist/components/Avatar/AvatarView/AvatarComponent/components/controls.d.ts +1 -1
  16. package/dist/components/Avatar/AvatarView/AvatarComponent/components/controls.js +1 -1
  17. package/dist/components/Avatar/AvatarView/AvatarComponent/components/controls.js.map +1 -1
  18. package/dist/components/Avatar/AvatarView/AvatarComponent/components/halfbodyAvatar.js +1 -1
  19. package/dist/components/Avatar/AvatarView/AvatarComponent/components/halfbodyAvatar.js.map +1 -1
  20. package/dist/components/Avatar/AvatarView/AvatarComponent/constants.d.ts +74 -0
  21. package/dist/components/Avatar/AvatarView/AvatarComponent/constants.js +101 -0
  22. package/dist/components/Avatar/AvatarView/AvatarComponent/constants.js.map +1 -0
  23. package/dist/components/Chat/Chat.d.ts +10 -1
  24. package/dist/components/Chat/Chat.js +2 -10
  25. package/dist/components/Chat/Chat.js.map +1 -1
  26. package/dist/components/ChatBubble/ChatBubble.js +14 -4
  27. package/dist/components/ChatBubble/ChatBubble.js.map +1 -1
  28. package/dist/components/ChatInputs/ChatInputs.d.ts +11 -2
  29. package/dist/components/ChatInputs/ChatInputs.js +44 -6
  30. package/dist/components/ChatInputs/ChatInputs.js.map +1 -1
  31. package/dist/components/CompletionProviderStatus/CompletionProviderStatus.css +8 -0
  32. package/dist/components/CompletionProviderStatus/CompletionProviderStatus.js +75 -51
  33. package/dist/components/CompletionProviderStatus/CompletionProviderStatus.js.map +1 -1
  34. package/dist/components/ExpertsDrawer/ExpertsDrawer.js +1 -1
  35. package/dist/components/ExpertsDrawer/ExpertsDrawer.js.map +1 -1
  36. package/dist/components/FilePreview/FilePreview.css +155 -0
  37. package/dist/components/FilePreview/FilePreview.d.ts +12 -0
  38. package/dist/components/FilePreview/FilePreview.js +21 -0
  39. package/dist/components/FilePreview/FilePreview.js.map +1 -0
  40. package/dist/components/MemoriWidget/MemoriWidget.d.ts +3 -1
  41. package/dist/components/MemoriWidget/MemoriWidget.js +26 -11
  42. package/dist/components/MemoriWidget/MemoriWidget.js.map +1 -1
  43. package/dist/components/UploadButton/UploadButton.css +38 -0
  44. package/dist/components/UploadButton/UploadButton.d.ts +8 -0
  45. package/dist/components/UploadButton/UploadButton.js +79 -0
  46. package/dist/components/UploadButton/UploadButton.js.map +1 -0
  47. package/dist/components/icons/Preview.d.ts +5 -0
  48. package/dist/components/icons/Preview.js +6 -0
  49. package/dist/components/icons/Preview.js.map +1 -0
  50. package/dist/components/icons/Upload.d.ts +5 -0
  51. package/dist/components/icons/Upload.js +6 -0
  52. package/dist/components/icons/Upload.js.map +1 -0
  53. package/dist/index.d.ts +1 -0
  54. package/dist/index.js +3 -2
  55. package/dist/index.js.map +1 -1
  56. package/dist/styles.css +2 -0
  57. package/esm/components/Avatar/AvatarView/AvatarComponent/avatarComponent.js +52 -75
  58. package/esm/components/Avatar/AvatarView/AvatarComponent/avatarComponent.js.map +1 -1
  59. package/esm/components/Avatar/AvatarView/AvatarComponent/components/FullbodyAvatar/AnimationController.js +2 -2
  60. package/esm/components/Avatar/AvatarView/AvatarComponent/components/FullbodyAvatar/AnimationController.js.map +1 -1
  61. package/esm/components/Avatar/AvatarView/AvatarComponent/components/FullbodyAvatar/fullbodyAvatar.d.ts +1 -1
  62. package/esm/components/Avatar/AvatarView/AvatarComponent/components/FullbodyAvatar/fullbodyAvatar.js +8 -2
  63. package/esm/components/Avatar/AvatarView/AvatarComponent/components/FullbodyAvatar/fullbodyAvatar.js.map +1 -1
  64. package/esm/components/Avatar/AvatarView/AvatarComponent/components/FullbodyAvatar/types.d.ts +1 -0
  65. package/esm/components/Avatar/AvatarView/AvatarComponent/components/MorphTargetController.js +6 -3
  66. package/esm/components/Avatar/AvatarView/AvatarComponent/components/MorphTargetController.js.map +1 -1
  67. package/esm/components/Avatar/AvatarView/AvatarComponent/components/PositionController.js +1 -1
  68. package/esm/components/Avatar/AvatarView/AvatarComponent/components/PositionController.js.map +1 -1
  69. package/esm/components/Avatar/AvatarView/AvatarComponent/components/controls.d.ts +1 -1
  70. package/esm/components/Avatar/AvatarView/AvatarComponent/components/controls.js +1 -1
  71. package/esm/components/Avatar/AvatarView/AvatarComponent/components/controls.js.map +1 -1
  72. package/esm/components/Avatar/AvatarView/AvatarComponent/components/halfbodyAvatar.js +1 -1
  73. package/esm/components/Avatar/AvatarView/AvatarComponent/components/halfbodyAvatar.js.map +1 -1
  74. package/esm/components/Avatar/AvatarView/AvatarComponent/constants.d.ts +74 -0
  75. package/esm/components/Avatar/AvatarView/AvatarComponent/constants.js +98 -0
  76. package/esm/components/Avatar/AvatarView/AvatarComponent/constants.js.map +1 -0
  77. package/esm/components/Chat/Chat.d.ts +10 -1
  78. package/esm/components/Chat/Chat.js +2 -10
  79. package/esm/components/Chat/Chat.js.map +1 -1
  80. package/esm/components/ChatBubble/ChatBubble.js +14 -4
  81. package/esm/components/ChatBubble/ChatBubble.js.map +1 -1
  82. package/esm/components/ChatInputs/ChatInputs.d.ts +11 -2
  83. package/esm/components/ChatInputs/ChatInputs.js +45 -7
  84. package/esm/components/ChatInputs/ChatInputs.js.map +1 -1
  85. package/esm/components/CompletionProviderStatus/CompletionProviderStatus.css +8 -0
  86. package/esm/components/CompletionProviderStatus/CompletionProviderStatus.js +76 -52
  87. package/esm/components/CompletionProviderStatus/CompletionProviderStatus.js.map +1 -1
  88. package/esm/components/ExpertsDrawer/ExpertsDrawer.js +1 -1
  89. package/esm/components/ExpertsDrawer/ExpertsDrawer.js.map +1 -1
  90. package/esm/components/FilePreview/FilePreview.css +155 -0
  91. package/esm/components/FilePreview/FilePreview.d.ts +12 -0
  92. package/esm/components/FilePreview/FilePreview.js +18 -0
  93. package/esm/components/FilePreview/FilePreview.js.map +1 -0
  94. package/esm/components/MemoriWidget/MemoriWidget.d.ts +3 -1
  95. package/esm/components/MemoriWidget/MemoriWidget.js +26 -11
  96. package/esm/components/MemoriWidget/MemoriWidget.js.map +1 -1
  97. package/esm/components/UploadButton/UploadButton.css +38 -0
  98. package/esm/components/UploadButton/UploadButton.d.ts +8 -0
  99. package/esm/components/UploadButton/UploadButton.js +76 -0
  100. package/esm/components/UploadButton/UploadButton.js.map +1 -0
  101. package/esm/components/icons/Preview.d.ts +5 -0
  102. package/esm/components/icons/Preview.js +4 -0
  103. package/esm/components/icons/Preview.js.map +1 -0
  104. package/esm/components/icons/Upload.d.ts +5 -0
  105. package/esm/components/icons/Upload.js +4 -0
  106. package/esm/components/icons/Upload.js.map +1 -0
  107. package/esm/index.d.ts +1 -0
  108. package/esm/index.js +3 -2
  109. package/esm/index.js.map +1 -1
  110. package/esm/styles.css +2 -0
  111. package/package.json +2 -1
  112. package/src/components/Avatar/AvatarView/AvatarComponent/avatarComponent.tsx +120 -153
  113. package/src/components/Avatar/AvatarView/AvatarComponent/components/FullbodyAvatar/AnimationController.ts +3 -6
  114. package/src/components/Avatar/AvatarView/AvatarComponent/components/FullbodyAvatar/fullbodyAvatar.tsx +11 -1
  115. package/src/components/Avatar/AvatarView/AvatarComponent/components/FullbodyAvatar/types.ts +1 -0
  116. package/src/components/Avatar/AvatarView/AvatarComponent/components/MorphTargetController.ts +33 -10
  117. package/src/components/Avatar/AvatarView/AvatarComponent/components/PositionController.ts +1 -1
  118. package/src/components/Avatar/AvatarView/AvatarComponent/components/controls.tsx +2 -2
  119. package/src/components/Avatar/AvatarView/AvatarComponent/components/halfbodyAvatar.tsx +1 -1
  120. package/src/components/Avatar/AvatarView/AvatarComponent/constants.ts +127 -0
  121. package/src/components/Chat/Chat.tsx +16 -10
  122. package/src/components/ChatBubble/ChatBubble.tsx +23 -2
  123. package/src/components/ChatInputs/ChatInputs.stories.tsx +5 -0
  124. package/src/components/ChatInputs/ChatInputs.test.tsx +6 -6
  125. package/src/components/ChatInputs/ChatInputs.tsx +91 -23
  126. package/src/components/CompletionProviderStatus/CompletionProviderStatus.css +8 -0
  127. package/src/components/CompletionProviderStatus/CompletionProviderStatus.stories.tsx +1 -1
  128. package/src/components/CompletionProviderStatus/CompletionProviderStatus.tsx +95 -66
  129. package/src/components/ExpertsDrawer/ExpertsDrawer.tsx +3 -1
  130. package/src/components/FilePreview/FilePreview.css +155 -0
  131. package/src/components/FilePreview/FilePreview.stories.tsx +66 -0
  132. package/src/components/FilePreview/FilePreview.test.tsx +26 -0
  133. package/src/components/FilePreview/FilePreview.tsx +85 -0
  134. package/src/components/FilePreview/__snapshots__/FilePreview.test.tsx.snap +149 -0
  135. package/src/components/MemoriWidget/MemoriWidget.tsx +68 -3
  136. package/src/components/StartPanel/__snapshots__/StartPanel.test.tsx.snap +240 -0
  137. package/src/components/UploadButton/UploadButton.css +38 -0
  138. package/src/components/UploadButton/UploadButton.stories.tsx +62 -0
  139. package/src/components/UploadButton/UploadButton.test.tsx +11 -0
  140. package/src/components/UploadButton/UploadButton.tsx +126 -0
  141. package/src/components/UploadButton/__snapshots__/UploadButton.test.tsx.snap +31 -0
  142. package/src/components/icons/Preview.tsx +23 -0
  143. package/src/components/icons/Upload.tsx +23 -0
  144. package/src/components/layouts/__snapshots__/Chat.test.tsx.snap +24 -0
  145. package/src/components/layouts/__snapshots__/FullPage.test.tsx.snap +24 -0
  146. package/src/components/layouts/__snapshots__/Totem.test.tsx.snap +24 -0
  147. package/src/components/layouts/__snapshots__/ZoomedFullBody.test.tsx.snap +24 -0
  148. package/src/components/layouts/layouts.stories.tsx +30 -46
  149. package/src/index.stories.tsx +68 -2
  150. package/src/index.tsx +4 -0
  151. package/src/styles.css +2 -0
  152. package/src/components/Avatar/AvatarView/AvatarComponent/components/constants.ts +0 -29
@@ -1,7 +1,10 @@
1
1
  import { SkinnedMesh } from 'three';
2
2
  import { MathUtils } from 'three';
3
- import { EMOTION_SMOOTHING, VISEME_SMOOTHING, BLINK_CONFIG } from './constants';
3
+ import { EMOTION_SMOOTHING, VISEME_SMOOTHING, BLINK_CONFIG } from '../constants';
4
4
 
5
+ /**
6
+ * Controller class for handling morph target animations including emotions, visemes and blinking
7
+ */
5
8
  export class MorphTargetController {
6
9
  private headMesh: SkinnedMesh;
7
10
  private currentEmotionValues: Record<string, number> = {};
@@ -10,7 +13,9 @@ export class MorphTargetController {
10
13
  constructor(headMesh: SkinnedMesh) {
11
14
  this.headMesh = headMesh;
12
15
  }
13
-
16
+ /**
17
+ * Updates the morph target influences for emotions, visemes and blinking
18
+ */
14
19
  updateMorphTargets(
15
20
  currentTime: number,
16
21
  emotionMorphTargets: Record<string, number>,
@@ -23,57 +28,64 @@ export class MorphTargetController {
23
28
  blinkStartTime: number;
24
29
  }
25
30
  ) {
31
+ // Validate required mesh properties exist
26
32
  if (
27
33
  !this.headMesh.morphTargetDictionary ||
28
34
  !this.headMesh.morphTargetInfluences
29
35
  ) {
36
+ console.error('[MorphTargetController] Missing morphTargetDictionary or morphTargetInfluences');
30
37
  return;
31
38
  }
32
39
 
40
+ // Calculate blink value for this frame
33
41
  const blinkValue = this.calculateBlinkValue(
34
42
  currentTime,
35
43
  blinkState,
36
44
  eyeBlink
37
45
  );
46
+
38
47
  const currentEmotionKeys = new Set(Object.keys(emotionMorphTargets));
39
48
 
49
+ // Process each morph target
40
50
  Object.entries(this.headMesh.morphTargetDictionary).forEach(
41
51
  ([key, index]) => {
42
52
  if (typeof index !== 'number') return;
43
53
 
44
54
  let targetValue = 0;
45
55
 
46
- // Handle emotion morphs
56
+ // Handle emotion morphs with smoothing
47
57
  if (currentEmotionKeys.has(key)) {
48
58
  const targetEmotionValue = emotionMorphTargets[key];
49
59
  const currentEmotionValue = this.currentEmotionValues[key] || 0;
50
60
  const newEmotionValue = MathUtils.lerp(
51
61
  currentEmotionValue,
52
- targetEmotionValue * 2.5,
62
+ targetEmotionValue * 3, // Amplify emotion by 3x
53
63
  EMOTION_SMOOTHING
54
64
  );
65
+ console.log(`[MorphTargetController] Emotion ${key}: current=${currentEmotionValue}, target=${targetEmotionValue}, new=${newEmotionValue}`);
55
66
  this.currentEmotionValues[key] = newEmotionValue;
56
67
  targetValue += newEmotionValue;
57
68
  }
58
69
 
59
- // Handle viseme
70
+ // Add viseme influence if active
60
71
  if (currentViseme && key === currentViseme.name) {
61
72
  targetValue += currentViseme.weight;
62
73
  }
63
74
 
64
- // Handle blinking
75
+ // Add blink influence if active
65
76
  if (key === 'eyesClosed' && eyeBlink) {
66
77
  targetValue += blinkValue;
67
78
  }
68
79
 
69
- // Apply final value
80
+ // Clamp and smooth final value
70
81
  targetValue = MathUtils.clamp(targetValue, 0, 1);
71
82
  if (this.headMesh.morphTargetInfluences) {
72
- this.headMesh.morphTargetInfluences[index] = MathUtils.lerp(
83
+ const finalValue = MathUtils.lerp(
73
84
  this.headMesh.morphTargetInfluences[index] || 0,
74
85
  targetValue,
75
86
  VISEME_SMOOTHING
76
87
  );
88
+ this.headMesh.morphTargetInfluences[index] = finalValue;
77
89
  }
78
90
  }
79
91
  );
@@ -81,6 +93,9 @@ export class MorphTargetController {
81
93
  this.previousEmotionKeys = currentEmotionKeys;
82
94
  }
83
95
 
96
+ /**
97
+ * Calculates the blink value based on timing and state
98
+ */
84
99
  private calculateBlinkValue(
85
100
  currentTime: number,
86
101
  blinkState: {
@@ -95,6 +110,7 @@ export class MorphTargetController {
95
110
 
96
111
  let blinkValue = 0;
97
112
 
113
+ // Start new blink if it's time
98
114
  if (currentTime >= blinkState.nextBlinkTime && !blinkState.isBlinking) {
99
115
  blinkState.isBlinking = true;
100
116
  blinkState.blinkStartTime = currentTime;
@@ -105,14 +121,21 @@ export class MorphTargetController {
105
121
  BLINK_CONFIG.minInterval;
106
122
  }
107
123
 
124
+ // Calculate blink animation progress
108
125
  if (blinkState.isBlinking) {
109
126
  const blinkProgress =
110
127
  (currentTime - blinkState.blinkStartTime) / BLINK_CONFIG.blinkDuration;
128
+
129
+ // First half of blink - closing eyes
111
130
  if (blinkProgress <= 0.5) {
112
131
  blinkValue = blinkProgress * 2;
113
- } else if (blinkProgress <= 1) {
132
+ }
133
+ // Second half of blink - opening eyes
134
+ else if (blinkProgress <= 1) {
114
135
  blinkValue = 2 - blinkProgress * 2;
115
- } else {
136
+ }
137
+ // Blink complete
138
+ else {
116
139
  blinkState.isBlinking = false;
117
140
  blinkValue = 0;
118
141
  }
@@ -1,5 +1,5 @@
1
1
  import { Vector3, MathUtils } from 'three';
2
- import { AVATAR_POSITION, AVATAR_POSITION_ZOOMED } from './constants';
2
+ import { AVATAR_POSITION, AVATAR_POSITION_ZOOMED } from '../constants';
3
3
 
4
4
  export class AvatarPositionController {
5
5
  private currentScale: Vector3;
@@ -13,7 +13,7 @@ interface AdditiveAction {
13
13
 
14
14
  export interface AnimationControlPanelProps {
15
15
  baseActions: Record<string, BaseAction>;
16
- onBaseActionChange: (action: string) => void;
16
+ onBaseActionChange: (action: string, outputContent: string) => void;
17
17
  currentBaseAction: {
18
18
  action: string;
19
19
  weight: number;
@@ -54,7 +54,7 @@ const AnimationControlPanel: React.FC<AnimationControlPanelProps> = ({
54
54
  baseNames.forEach(name => {
55
55
  const settings = baseActions[name];
56
56
  panelSettingsRef.current[name] = () => {
57
- onBaseActionChange(name);
57
+ onBaseActionChange(name, '');
58
58
  };
59
59
 
60
60
  const control = folder1.add(panelSettingsRef.current, name);
@@ -9,7 +9,7 @@ import {
9
9
  AVATAR_POSITION,
10
10
  SCALE_LERP_FACTOR,
11
11
  AVATAR_POSITION_ZOOMED,
12
- } from './constants';
12
+ } from '../constants';
13
13
  import { hideHands } from '../../utils/utils';
14
14
  import useHeadMovement from '../../utils/useHeadMovement';
15
15
 
@@ -0,0 +1,127 @@
1
+ import { Vector3, Euler } from 'three';
2
+ import { AnimationConfig } from './components/FullbodyAvatar/types';
3
+
4
+ // Position and rotation of the avatar
5
+ export const AVATAR_POSITION = new Vector3(0, -1, 0);
6
+ export const AVATAR_ROTATION = new Euler(0.175, 0, 0);
7
+ export const AVATAR_POSITION_ZOOMED = new Vector3(0, -1.45, 0);
8
+
9
+ // Factor for lerping the scale of the avatar
10
+ export const SCALE_LERP_FACTOR = 0.1;
11
+
12
+ // Maximum number of idle loops before forcing change
13
+ export const MAX_IDLE_LOOPS_DEFAULT = 5;
14
+
15
+
16
+ interface BaseAction {
17
+ weight: number;
18
+ action?: string;
19
+ }
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
+ };
56
+
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,
65
+ },
66
+ Timore: {
67
+ 'browOuterUpLeft': -0.5,
68
+ 'browOuterUpRight': -0.5,
69
+ 'eyeWideLeft': -0.5,
70
+ 'eyeWideRight': -0.5,
71
+ },
72
+ Tristezza: {
73
+ 'browDownLeft': -0.5,
74
+ 'browDownRight': -0.5,
75
+ 'eyeSquintLeft': 0.5,
76
+ 'eyeSquintRight': 0.5,
77
+ 'mouthSmile': -0.6,
78
+ },
79
+ Sorpresa: {
80
+ 'browInnerUp': 0.5,
81
+ 'browOuterUpLeft': 0.5,
82
+ 'browOuterUpRight': 0.5,
83
+ 'eyeWideLeft': 0.5,
84
+ 'eyeWideRight': 0.5,
85
+ },
86
+ Gioia: {
87
+ 'browDownLeft': 0.5,
88
+ 'browDownRight': 0.5,
89
+ 'browInnerUp': 0.5,
90
+ 'mouthSmile': 0.8,
91
+ },
92
+ };
93
+
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
+ };
102
+
103
+ // URL for the male avatar
104
+ export const ANIMATION_URLS = {
105
+ MALE: 'https://assets.memori.ai/api/v2/asset/2c5e88a4-cf62-408b-9ef0-518b099dfcb2.glb',
106
+ FEMALE:
107
+ 'https://assets.memori.ai/api/v2/asset/2adc934b-24b2-45bd-94ad-ffec58d3cb32.glb',
108
+ };
109
+
110
+ // Blink configuration
111
+ export const BLINK_CONFIG = {
112
+ minInterval: 1000,
113
+ maxInterval: 5000,
114
+ blinkDuration: 150,
115
+ };
116
+
117
+ // Default animation configuration
118
+ export const DEFAULT_CONFIG: AnimationConfig = {
119
+ fadeInDuration: 0.8,
120
+ fadeOutDuration: 0.8,
121
+ idleCount: 5,
122
+ timeScale: 1.0,
123
+ };
124
+
125
+ // Smoothing factors for emotion and viseme morphs
126
+ export const EMOTION_SMOOTHING = 0.3;
127
+ export const VISEME_SMOOTHING = 0.5;
@@ -38,6 +38,7 @@ export interface Props {
38
38
  pushMessage: (message: Message) => void;
39
39
  simulateUserPrompt: (text: string, translatedText?: string) => void;
40
40
  showDates?: boolean;
41
+ showUpload?: boolean;
41
42
  showContextPerLine?: boolean;
42
43
  showAIicon?: boolean;
43
44
  showTranslationOriginal?: boolean;
@@ -56,7 +57,16 @@ export interface Props {
56
57
  showMicrophone?: boolean;
57
58
  userMessage?: string;
58
59
  onChangeUserMessage: (userMessage: string) => void;
59
- sendMessage: (msg: string) => void;
60
+ sendMessage: (
61
+ msg: string,
62
+ media?: {
63
+ mediumID: string;
64
+ mimeType: string;
65
+ content: string;
66
+ title?: string;
67
+ properties?: { [key: string]: any };
68
+ }
69
+ ) => void;
60
70
  listening?: boolean;
61
71
  setEnableFocusChatInput: (enableFocusChatInput: boolean) => void;
62
72
  isPlayingAudio?: boolean;
@@ -115,6 +125,7 @@ const Chat: React.FC<Props> = ({
115
125
  customMediaRenderer,
116
126
  user,
117
127
  userAvatar,
128
+ showUpload = false,
118
129
  experts,
119
130
  useMathFormatting = false,
120
131
  }) => {
@@ -162,14 +173,6 @@ const Chat: React.FC<Props> = ({
162
173
  scrollToBottom();
163
174
  }
164
175
  };
165
- const onTextareaPressEnter = () => {
166
- if (sendOnEnter === 'keypress' && userMessage?.length > 0) {
167
- stopListening();
168
- sendMessage(userMessage);
169
- onChangeUserMessage('');
170
- resetTranscript();
171
- }
172
- };
173
176
 
174
177
  return (
175
178
  <div
@@ -233,6 +236,7 @@ const Chat: React.FC<Props> = ({
233
236
  showCopyButton={showCopyButton}
234
237
  useMathFormatting={useMathFormatting}
235
238
  />
239
+
236
240
  {showDates && !!message.timestamp && (
237
241
  <small
238
242
  className={`memori-chat--timestamp ${
@@ -252,6 +256,7 @@ const Chat: React.FC<Props> = ({
252
256
  )}
253
257
  </small>
254
258
  )}
259
+
255
260
  {showContextPerLine &&
256
261
  !!Object.keys(message.contextVars ?? {}).length && (
257
262
  <div className="memori-chat--context-vars">
@@ -363,6 +368,7 @@ const Chat: React.FC<Props> = ({
363
368
 
364
369
  {showInputs && (
365
370
  <ChatInputs
371
+ resetTranscript={resetTranscript}
366
372
  userMessage={userMessage}
367
373
  onChangeUserMessage={onChangeUserMessage}
368
374
  dialogState={dialogState}
@@ -372,9 +378,9 @@ const Chat: React.FC<Props> = ({
372
378
  microphoneMode={microphoneMode}
373
379
  sendOnEnter={sendOnEnter}
374
380
  setSendOnEnter={setSendOnEnter}
381
+ showUpload={showUpload}
375
382
  attachmentsMenuOpen={attachmentsMenuOpen}
376
383
  setAttachmentsMenuOpen={setAttachmentsMenuOpen}
377
- onTextareaPressEnter={onTextareaPressEnter}
378
384
  onTextareaFocus={onTextareaFocus}
379
385
  onTextareaBlur={onTextareaBlur}
380
386
  startListening={startListening}
@@ -24,12 +24,12 @@ import Copy from '../icons/Copy';
24
24
  import Code from '../icons/Code';
25
25
  import WhyThisAnswer from '../WhyThisAnswer/WhyThisAnswer';
26
26
  import { cleanUrl, stripHTML, stripOutputTags } from '../../helpers/utils';
27
+ import FilePreview from '../FilePreview/FilePreview';
27
28
 
28
29
  import markedLinkifyIt from 'marked-linkify-it';
29
30
  import markedKatex from 'marked-katex-extension';
30
31
  import markedExtendedTables from '../../helpers/markedExtendedTables';
31
32
 
32
-
33
33
  marked.use({
34
34
  async: false,
35
35
  gfm: true,
@@ -245,7 +245,9 @@ const ChatBubble: React.FC<Props> = ({
245
245
  !!message.emitter?.length &&
246
246
  !!memori.enableBoardOfExperts &&
247
247
  experts?.find(e => e.name === message.emitter)
248
- ? `${apiUrl}/api/v1/memoriai/memori/avatar/${
248
+ ? `${
249
+ new URL(apiUrl ?? '/').origin
250
+ }/api/v1/memoriai/memori/avatar/${
249
251
  experts.find(e => e.name === message.emitter)
250
252
  ?.expertMemoriID
251
253
  }`
@@ -265,6 +267,7 @@ const ChatBubble: React.FC<Props> = ({
265
267
  })
266
268
  }
267
269
  onError={e => {
270
+ // Fallback image handling if primary source fails
268
271
  e.currentTarget.src =
269
272
  memori.avatarURL && memori.avatarURL.length > 0
270
273
  ? getResourceUrl({
@@ -284,6 +287,7 @@ const ChatBubble: React.FC<Props> = ({
284
287
  />
285
288
  </Transition.Child>
286
289
  )}
290
+
287
291
  <Transition.Child
288
292
  as="div"
289
293
  className={cx('memori-chat--bubble', {
@@ -311,6 +315,7 @@ const ChatBubble: React.FC<Props> = ({
311
315
  className="memori-chat--bubble-content"
312
316
  dangerouslySetInnerHTML={{ __html: renderedText }}
313
317
  />
318
+
314
319
  {((!message.fromUser && showCopyButton) ||
315
320
  (message.generatedByAI && showAIicon) ||
316
321
  (showFeedback && simulateUserPrompt)) && (
@@ -325,6 +330,7 @@ const ChatBubble: React.FC<Props> = ({
325
330
  onClick={() => navigator.clipboard.writeText(plainText)}
326
331
  />
327
332
  )}
333
+
328
334
  {!message.fromUser &&
329
335
  showCopyButton &&
330
336
  plainText !== message.text && (
@@ -401,6 +407,21 @@ const ChatBubble: React.FC<Props> = ({
401
407
  )}
402
408
  </div>
403
409
  )}
410
+
411
+ {message.fromUser &&
412
+ message.media?.length &&
413
+ message.media[0].properties?.isAttachedFile && (
414
+ <FilePreview
415
+ previewFiles={message.media.map(m => ({
416
+ name: m.title ?? '',
417
+ id: m.mediumID,
418
+ content: m.content ?? '',
419
+ }))}
420
+ removeFile={() => {}}
421
+ allowRemove={false}
422
+ isMessagePreview={true}
423
+ />
424
+ )}
404
425
  </Transition.Child>
405
426
 
406
427
  {message.fromUser && (
@@ -227,6 +227,11 @@ ContinuousSpeechListening.args = {
227
227
  microphoneMode: 'CONTINUOUS',
228
228
  };
229
229
 
230
+ export const WithUploadButton = Template.bind({});
231
+ WithUploadButton.args = {
232
+ showUpload: true,
233
+ };
234
+
230
235
  export const WithoutMicrophone = Template.bind({});
231
236
  WithoutMicrophone.args = {
232
237
  dialogState,
@@ -10,7 +10,6 @@ it('renders ChatInputs unchanged', () => {
10
10
  userMessage=""
11
11
  onChangeUserMessage={jest.fn()}
12
12
  sendMessage={jest.fn()}
13
- onTextareaPressEnter={jest.fn()}
14
13
  onTextareaFocus={jest.fn()}
15
14
  onTextareaBlur={jest.fn()}
16
15
  setAttachmentsMenuOpen={jest.fn()}
@@ -20,6 +19,7 @@ it('renders ChatInputs unchanged', () => {
20
19
  stopAudio={jest.fn()}
21
20
  startListening={jest.fn()}
22
21
  stopListening={jest.fn()}
22
+ resetTranscript={jest.fn()}
23
23
  showMicrophone={true}
24
24
  />
25
25
  );
@@ -33,7 +33,6 @@ it('renders ChatInputs with user message unchanged', () => {
33
33
  onChangeUserMessage={jest.fn()}
34
34
  dialogState={dialogState}
35
35
  sendMessage={jest.fn()}
36
- onTextareaPressEnter={jest.fn()}
37
36
  onTextareaFocus={jest.fn()}
38
37
  onTextareaBlur={jest.fn()}
39
38
  setAttachmentsMenuOpen={jest.fn()}
@@ -43,6 +42,7 @@ it('renders ChatInputs with user message unchanged', () => {
43
42
  stopAudio={jest.fn()}
44
43
  startListening={jest.fn()}
45
44
  stopListening={jest.fn()}
45
+ resetTranscript={jest.fn()}
46
46
  showMicrophone={true}
47
47
  />
48
48
  );
@@ -59,7 +59,6 @@ it('renders ChatInputs on instruct unchanged', () => {
59
59
  acceptsMedia: true,
60
60
  }}
61
61
  sendMessage={jest.fn()}
62
- onTextareaPressEnter={jest.fn()}
63
62
  onTextareaFocus={jest.fn()}
64
63
  onTextareaBlur={jest.fn()}
65
64
  setAttachmentsMenuOpen={jest.fn()}
@@ -70,6 +69,7 @@ it('renders ChatInputs on instruct unchanged', () => {
70
69
  stopAudio={jest.fn()}
71
70
  startListening={jest.fn()}
72
71
  stopListening={jest.fn()}
72
+ resetTranscript={jest.fn()}
73
73
  showMicrophone={true}
74
74
  />
75
75
  );
@@ -83,7 +83,6 @@ it('renders ChatInputs listening unchanged', () => {
83
83
  onChangeUserMessage={jest.fn()}
84
84
  dialogState={dialogState}
85
85
  sendMessage={jest.fn()}
86
- onTextareaPressEnter={jest.fn()}
87
86
  onTextareaFocus={jest.fn()}
88
87
  onTextareaBlur={jest.fn()}
89
88
  setAttachmentsMenuOpen={jest.fn()}
@@ -93,6 +92,7 @@ it('renders ChatInputs listening unchanged', () => {
93
92
  stopAudio={jest.fn()}
94
93
  startListening={jest.fn()}
95
94
  stopListening={jest.fn()}
95
+ resetTranscript={jest.fn()}
96
96
  showMicrophone={true}
97
97
  />
98
98
  );
@@ -106,7 +106,6 @@ it('renders ChatInputs without microphone button unchanged', () => {
106
106
  onChangeUserMessage={jest.fn()}
107
107
  dialogState={dialogState}
108
108
  sendMessage={jest.fn()}
109
- onTextareaPressEnter={jest.fn()}
110
109
  onTextareaFocus={jest.fn()}
111
110
  onTextareaBlur={jest.fn()}
112
111
  setAttachmentsMenuOpen={jest.fn()}
@@ -116,6 +115,7 @@ it('renders ChatInputs without microphone button unchanged', () => {
116
115
  stopAudio={jest.fn()}
117
116
  startListening={jest.fn()}
118
117
  stopListening={jest.fn()}
118
+ resetTranscript={jest.fn()}
119
119
  showMicrophone={false}
120
120
  />
121
121
  );
@@ -132,7 +132,6 @@ it('renders ChatInputs disabled unchanged', () => {
132
132
  state: 'X3',
133
133
  }}
134
134
  sendMessage={jest.fn()}
135
- onTextareaPressEnter={jest.fn()}
136
135
  onTextareaFocus={jest.fn()}
137
136
  onTextareaBlur={jest.fn()}
138
137
  setAttachmentsMenuOpen={jest.fn()}
@@ -142,6 +141,7 @@ it('renders ChatInputs disabled unchanged', () => {
142
141
  stopAudio={jest.fn()}
143
142
  startListening={jest.fn()}
144
143
  stopListening={jest.fn()}
144
+ resetTranscript={jest.fn()}
145
145
  showMicrophone={true}
146
146
  />
147
147
  );