@memori.ai/memori-react 7.8.8 → 7.9.1

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 (190) hide show
  1. package/CHANGELOG.md +61 -0
  2. package/dist/components/AccountForm/AccountForm.d.ts +3 -2
  3. package/dist/components/AccountForm/AccountForm.js +2 -4
  4. package/dist/components/AccountForm/AccountForm.js.map +1 -1
  5. package/dist/components/Avatar/Avatar.d.ts +4 -0
  6. package/dist/components/Avatar/Avatar.js +6 -6
  7. package/dist/components/Avatar/Avatar.js.map +1 -1
  8. package/dist/components/Avatar/AvatarView/AvatarComponent/avatarComponent.d.ts +3 -0
  9. package/dist/components/Avatar/AvatarView/AvatarComponent/avatarComponent.js +3 -3
  10. package/dist/components/Avatar/AvatarView/AvatarComponent/avatarComponent.js.map +1 -1
  11. package/dist/components/Avatar/AvatarView/AvatarComponent/components/FullbodyAvatar/AnimationController.js +1 -1
  12. package/dist/components/Avatar/AvatarView/AvatarComponent/components/FullbodyAvatar/AnimationController.js.map +1 -1
  13. package/dist/components/Avatar/AvatarView/AvatarComponent/components/FullbodyAvatar/fullbodyAvatar.d.ts +1 -1
  14. package/dist/components/Avatar/AvatarView/AvatarComponent/components/FullbodyAvatar/fullbodyAvatar.js +48 -10
  15. package/dist/components/Avatar/AvatarView/AvatarComponent/components/FullbodyAvatar/fullbodyAvatar.js.map +1 -1
  16. package/dist/components/Avatar/AvatarView/AvatarComponent/components/FullbodyAvatar/types.d.ts +4 -1
  17. package/dist/components/Avatar/AvatarView/AvatarComponent/components/MorphTargetController.d.ts +17 -0
  18. package/dist/components/Avatar/AvatarView/AvatarComponent/components/MorphTargetController.js +73 -0
  19. package/dist/components/Avatar/AvatarView/AvatarComponent/components/MorphTargetController.js.map +1 -0
  20. package/dist/components/Avatar/AvatarView/AvatarComponent/components/PositionController.d.ts +19 -0
  21. package/dist/components/Avatar/AvatarView/AvatarComponent/components/PositionController.js +60 -0
  22. package/dist/components/Avatar/AvatarView/AvatarComponent/components/PositionController.js.map +1 -0
  23. package/dist/components/Avatar/AvatarView/AvatarComponent/components/constants.d.ts +18 -0
  24. package/dist/components/Avatar/AvatarView/AvatarComponent/components/constants.js +26 -0
  25. package/dist/components/Avatar/AvatarView/AvatarComponent/components/constants.js.map +1 -0
  26. package/dist/components/Avatar/AvatarView/AvatarComponent/components/halfbodyAvatar.d.ts +8 -7
  27. package/dist/components/Avatar/AvatarView/AvatarComponent/components/halfbodyAvatar.js +65 -78
  28. package/dist/components/Avatar/AvatarView/AvatarComponent/components/halfbodyAvatar.js.map +1 -1
  29. package/dist/components/Avatar/AvatarView/AvatarComponent/positionControls/positionControls.css +99 -0
  30. package/dist/components/Avatar/AvatarView/AvatarComponent/positionControls/positionControls.d.ts +12 -0
  31. package/dist/components/Avatar/AvatarView/AvatarComponent/positionControls/positionControls.js +98 -0
  32. package/dist/components/Avatar/AvatarView/AvatarComponent/positionControls/positionControls.js.map +1 -0
  33. package/dist/components/Avatar/AvatarView/index.d.ts +4 -1
  34. package/dist/components/Avatar/AvatarView/index.js +36 -13
  35. package/dist/components/Avatar/AvatarView/index.js.map +1 -1
  36. package/dist/components/Avatar/AvatarView/utils/hideHands.d.ts +2 -0
  37. package/dist/components/Avatar/AvatarView/utils/hideHands.js +14 -0
  38. package/dist/components/Avatar/AvatarView/utils/hideHands.js.map +1 -0
  39. package/dist/components/Chat/Chat.d.ts +1 -0
  40. package/dist/components/Chat/Chat.js +5 -2
  41. package/dist/components/Chat/Chat.js.map +1 -1
  42. package/dist/components/KnownFacts/KnownFacts.d.ts +3 -2
  43. package/dist/components/KnownFacts/KnownFacts.js +2 -4
  44. package/dist/components/KnownFacts/KnownFacts.js.map +1 -1
  45. package/dist/components/LoginDrawer/LoginDrawer.d.ts +3 -2
  46. package/dist/components/LoginDrawer/LoginDrawer.js +4 -6
  47. package/dist/components/LoginDrawer/LoginDrawer.js.map +1 -1
  48. package/dist/components/MemoriWidget/MemoriWidget.d.ts +3 -2
  49. package/dist/components/MemoriWidget/MemoriWidget.js +36 -10
  50. package/dist/components/MemoriWidget/MemoriWidget.js.map +1 -1
  51. package/dist/components/SettingsDrawer/SettingsDrawer.css +4 -2
  52. package/dist/components/SettingsDrawer/SettingsDrawer.d.ts +6 -1
  53. package/dist/components/SettingsDrawer/SettingsDrawer.js +10 -4
  54. package/dist/components/SettingsDrawer/SettingsDrawer.js.map +1 -1
  55. package/dist/components/SignupForm/SignupForm.d.ts +3 -2
  56. package/dist/components/SignupForm/SignupForm.js +2 -4
  57. package/dist/components/SignupForm/SignupForm.js.map +1 -1
  58. package/dist/components/layouts/Totem.js +1 -1
  59. package/dist/components/layouts/Totem.js.map +1 -1
  60. package/dist/components/layouts/totem.css +9 -9
  61. package/dist/components/ui/Button.css +4 -0
  62. package/dist/components/ui/Button.d.ts +1 -0
  63. package/dist/components/ui/Button.js +2 -1
  64. package/dist/components/ui/Button.js.map +1 -1
  65. package/dist/components/ui/Slider.css +177 -0
  66. package/dist/components/ui/Slider.d.ts +12 -0
  67. package/dist/components/ui/Slider.js +78 -0
  68. package/dist/components/ui/Slider.js.map +1 -0
  69. package/dist/index.js +4 -1
  70. package/dist/index.js.map +1 -1
  71. package/dist/locales/en.json +11 -0
  72. package/dist/locales/it.json +11 -0
  73. package/dist/styles.css +1 -0
  74. package/esm/components/AccountForm/AccountForm.d.ts +3 -2
  75. package/esm/components/AccountForm/AccountForm.js +2 -4
  76. package/esm/components/AccountForm/AccountForm.js.map +1 -1
  77. package/esm/components/Avatar/Avatar.d.ts +4 -0
  78. package/esm/components/Avatar/Avatar.js +6 -6
  79. package/esm/components/Avatar/Avatar.js.map +1 -1
  80. package/esm/components/Avatar/AvatarView/AvatarComponent/avatarComponent.d.ts +3 -0
  81. package/esm/components/Avatar/AvatarView/AvatarComponent/avatarComponent.js +3 -3
  82. package/esm/components/Avatar/AvatarView/AvatarComponent/avatarComponent.js.map +1 -1
  83. package/esm/components/Avatar/AvatarView/AvatarComponent/components/FullbodyAvatar/AnimationController.js +1 -1
  84. package/esm/components/Avatar/AvatarView/AvatarComponent/components/FullbodyAvatar/AnimationController.js.map +1 -1
  85. package/esm/components/Avatar/AvatarView/AvatarComponent/components/FullbodyAvatar/fullbodyAvatar.d.ts +1 -1
  86. package/esm/components/Avatar/AvatarView/AvatarComponent/components/FullbodyAvatar/fullbodyAvatar.js +45 -8
  87. package/esm/components/Avatar/AvatarView/AvatarComponent/components/FullbodyAvatar/fullbodyAvatar.js.map +1 -1
  88. package/esm/components/Avatar/AvatarView/AvatarComponent/components/FullbodyAvatar/types.d.ts +4 -1
  89. package/esm/components/Avatar/AvatarView/AvatarComponent/components/MorphTargetController.d.ts +17 -0
  90. package/esm/components/Avatar/AvatarView/AvatarComponent/components/MorphTargetController.js +69 -0
  91. package/esm/components/Avatar/AvatarView/AvatarComponent/components/MorphTargetController.js.map +1 -0
  92. package/esm/components/Avatar/AvatarView/AvatarComponent/components/PositionController.d.ts +19 -0
  93. package/esm/components/Avatar/AvatarView/AvatarComponent/components/PositionController.js +56 -0
  94. package/esm/components/Avatar/AvatarView/AvatarComponent/components/PositionController.js.map +1 -0
  95. package/esm/components/Avatar/AvatarView/AvatarComponent/components/constants.d.ts +18 -0
  96. package/esm/components/Avatar/AvatarView/AvatarComponent/components/constants.js +23 -0
  97. package/esm/components/Avatar/AvatarView/AvatarComponent/components/constants.js.map +1 -0
  98. package/esm/components/Avatar/AvatarView/AvatarComponent/components/halfbodyAvatar.d.ts +8 -7
  99. package/esm/components/Avatar/AvatarView/AvatarComponent/components/halfbodyAvatar.js +66 -80
  100. package/esm/components/Avatar/AvatarView/AvatarComponent/components/halfbodyAvatar.js.map +1 -1
  101. package/esm/components/Avatar/AvatarView/AvatarComponent/positionControls/positionControls.css +99 -0
  102. package/esm/components/Avatar/AvatarView/AvatarComponent/positionControls/positionControls.d.ts +12 -0
  103. package/esm/components/Avatar/AvatarView/AvatarComponent/positionControls/positionControls.js +95 -0
  104. package/esm/components/Avatar/AvatarView/AvatarComponent/positionControls/positionControls.js.map +1 -0
  105. package/esm/components/Avatar/AvatarView/index.d.ts +4 -1
  106. package/esm/components/Avatar/AvatarView/index.js +38 -15
  107. package/esm/components/Avatar/AvatarView/index.js.map +1 -1
  108. package/esm/components/Avatar/AvatarView/utils/hideHands.d.ts +2 -0
  109. package/esm/components/Avatar/AvatarView/utils/hideHands.js +10 -0
  110. package/esm/components/Avatar/AvatarView/utils/hideHands.js.map +1 -0
  111. package/esm/components/Chat/Chat.d.ts +1 -0
  112. package/esm/components/Chat/Chat.js +5 -2
  113. package/esm/components/Chat/Chat.js.map +1 -1
  114. package/esm/components/KnownFacts/KnownFacts.d.ts +3 -2
  115. package/esm/components/KnownFacts/KnownFacts.js +2 -4
  116. package/esm/components/KnownFacts/KnownFacts.js.map +1 -1
  117. package/esm/components/LoginDrawer/LoginDrawer.d.ts +3 -2
  118. package/esm/components/LoginDrawer/LoginDrawer.js +4 -6
  119. package/esm/components/LoginDrawer/LoginDrawer.js.map +1 -1
  120. package/esm/components/MemoriWidget/MemoriWidget.d.ts +3 -2
  121. package/esm/components/MemoriWidget/MemoriWidget.js +36 -10
  122. package/esm/components/MemoriWidget/MemoriWidget.js.map +1 -1
  123. package/esm/components/SettingsDrawer/SettingsDrawer.css +4 -2
  124. package/esm/components/SettingsDrawer/SettingsDrawer.d.ts +6 -1
  125. package/esm/components/SettingsDrawer/SettingsDrawer.js +10 -4
  126. package/esm/components/SettingsDrawer/SettingsDrawer.js.map +1 -1
  127. package/esm/components/SignupForm/SignupForm.d.ts +3 -2
  128. package/esm/components/SignupForm/SignupForm.js +2 -4
  129. package/esm/components/SignupForm/SignupForm.js.map +1 -1
  130. package/esm/components/layouts/Totem.js +1 -1
  131. package/esm/components/layouts/Totem.js.map +1 -1
  132. package/esm/components/layouts/totem.css +9 -9
  133. package/esm/components/ui/Button.css +4 -0
  134. package/esm/components/ui/Button.d.ts +1 -0
  135. package/esm/components/ui/Button.js +2 -1
  136. package/esm/components/ui/Button.js.map +1 -1
  137. package/esm/components/ui/Slider.css +177 -0
  138. package/esm/components/ui/Slider.d.ts +12 -0
  139. package/esm/components/ui/Slider.js +75 -0
  140. package/esm/components/ui/Slider.js.map +1 -0
  141. package/esm/index.js +4 -1
  142. package/esm/index.js.map +1 -1
  143. package/esm/locales/en.json +11 -0
  144. package/esm/locales/it.json +11 -0
  145. package/esm/styles.css +1 -0
  146. package/package.json +3 -2
  147. package/src/components/AccountForm/AccountForm.test.tsx +2 -1
  148. package/src/components/AccountForm/AccountForm.tsx +3 -5
  149. package/src/components/Avatar/Avatar.test.tsx +8 -0
  150. package/src/components/Avatar/Avatar.tsx +19 -6
  151. package/src/components/Avatar/AvatarView/AvatarComponent/avatarComponent.tsx +17 -8
  152. package/src/components/Avatar/AvatarView/AvatarComponent/components/FullbodyAvatar/AnimationController.ts +1 -1
  153. package/src/components/Avatar/AvatarView/AvatarComponent/components/FullbodyAvatar/fullbodyAvatar.tsx +69 -18
  154. package/src/components/Avatar/AvatarView/AvatarComponent/components/FullbodyAvatar/types.ts +4 -1
  155. package/src/components/Avatar/AvatarView/AvatarComponent/components/PositionController.ts +83 -0
  156. package/src/components/Avatar/AvatarView/AvatarComponent/components/{FullbodyAvatar/constants.ts → constants.ts} +2 -1
  157. package/src/components/Avatar/AvatarView/AvatarComponent/components/halfbodyAvatar.tsx +106 -124
  158. package/src/components/Avatar/AvatarView/AvatarComponent/positionControls/positionControls.css +99 -0
  159. package/src/components/Avatar/AvatarView/AvatarComponent/positionControls/positionControls.tsx +187 -0
  160. package/src/components/Avatar/AvatarView/index.tsx +115 -43
  161. package/src/components/Avatar/AvatarView/utils/hideHands.ts +11 -0
  162. package/src/components/Avatar/__snapshots__/Avatar.test.tsx.snap +32 -93
  163. package/src/components/Chat/Chat.test.tsx +11 -0
  164. package/src/components/Chat/Chat.tsx +8 -1
  165. package/src/components/KnownFacts/KnownFacts.stories.tsx +5 -4
  166. package/src/components/KnownFacts/KnownFacts.test.tsx +6 -3
  167. package/src/components/KnownFacts/KnownFacts.tsx +3 -4
  168. package/src/components/LoginDrawer/LoginDrawer.stories.tsx +2 -1
  169. package/src/components/LoginDrawer/LoginDrawer.test.tsx +9 -8
  170. package/src/components/LoginDrawer/LoginDrawer.tsx +5 -6
  171. package/src/components/MemoriWidget/MemoriWidget.tsx +56 -14
  172. package/src/components/SettingsDrawer/SettingsDrawer.css +4 -2
  173. package/src/components/SettingsDrawer/SettingsDrawer.test.tsx +24 -0
  174. package/src/components/SettingsDrawer/SettingsDrawer.tsx +76 -4
  175. package/src/components/SignupForm/SignupForm.test.tsx +3 -2
  176. package/src/components/SignupForm/SignupForm.tsx +3 -4
  177. package/src/components/layouts/Totem.tsx +1 -1
  178. package/src/components/layouts/layouts.stories.tsx +111 -3
  179. package/src/components/layouts/totem.css +9 -9
  180. package/src/components/ui/Button.css +4 -0
  181. package/src/components/ui/Button.tsx +3 -0
  182. package/src/components/ui/Slider.css +177 -0
  183. package/src/components/ui/Slider.stories.tsx +63 -0
  184. package/src/components/ui/Slider.tsx +142 -0
  185. package/src/index.stories.tsx +1 -1
  186. package/src/index.tsx +5 -1
  187. package/src/locales/en.json +11 -0
  188. package/src/locales/it.json +11 -0
  189. package/src/styles.css +1 -0
  190. /package/src/components/Avatar/AvatarView/AvatarComponent/components/{FullbodyAvatar/MorhTargetController.ts → MorphTargetController.ts} +0 -0
@@ -1,168 +1,150 @@
1
- import React, { useEffect, useMemo, useRef } from 'react';
2
- import { Object3D, SkinnedMesh, Vector3 } from 'three';
1
+ import { useEffect, useMemo, useRef } from 'react';
2
+ import { Object3D, SkinnedMesh } from 'three';
3
3
  import { useGLTF } from '@react-three/drei';
4
+ import { useGraph, useFrame, useThree } from '@react-three/fiber';
4
5
  import { correctMaterials, isSkinnedMesh } from '../../../../../helpers/utils';
5
- import { useGraph, dispose, useFrame } from '@react-three/fiber';
6
- import { useAvatarBlink } from '../../utils/useEyeBlink';
7
- import useHeadMovement from '../../utils/useHeadMovement';
6
+ import { MorphTargetController } from './MorphTargetController';
7
+ import { AvatarPositionController } from './PositionController';
8
+ import {
9
+ AVATAR_POSITION,
10
+ SCALE_LERP_FACTOR,
11
+ AVATAR_POSITION_ZOOMED,
12
+ } from './constants';
8
13
  import { hideHands } from '../../utils/utils';
9
- import { AnimationMixer, MathUtils } from 'three';
14
+ import useHeadMovement from '../../utils/useHeadMovement';
10
15
 
11
16
  interface HalfBodyAvatarProps {
12
17
  url: string;
13
18
  setMorphTargetInfluences: (morphTargetInfluences: any) => void;
14
- headMovement?: boolean;
15
- speaking?: boolean;
16
- onLoaded?: () => void;
17
19
  setMorphTargetDictionary: (morphTargetDictionary: any) => void;
18
- eyeBlink?: boolean;
19
- morphTargetInfluences: any;
20
20
  updateCurrentViseme: (currentTime: number) => any;
21
- morphTargetSmoothing?: number;
21
+ eyeBlink?: boolean;
22
+ heightValue?: number; // 0-100 slider value
23
+ avatarHeight: number;
24
+ avatarDepth: number;
25
+ onLoaded?: () => void;
26
+ onCameraZChange: (value: number) => void;
27
+ headMovement?: boolean;
22
28
  }
23
29
 
24
- const AVATAR_POSITION = new Vector3(0, -0.6, 0);
25
- // Blink configuration
26
- const BLINK_CONFIG = {
27
- minInterval: 1000,
28
- maxInterval: 5000,
29
- blinkDuration: 150,
30
- };
31
-
32
30
  export default function HalfBodyAvatar({
33
31
  url,
34
32
  setMorphTargetInfluences,
35
33
  setMorphTargetDictionary,
36
- eyeBlink,
37
- onLoaded,
38
- morphTargetSmoothing = 0.5,
39
34
  updateCurrentViseme,
35
+ eyeBlink = false,
36
+ avatarHeight = 50,
37
+ avatarDepth = 0,
38
+ headMovement = false,
39
+ onLoaded,
40
+ onCameraZChange,
40
41
  }: HalfBodyAvatarProps) {
41
42
  const { scene } = useGLTF(url);
42
43
  const { nodes, materials } = useGraph(scene);
43
- const mixer = useRef(new AnimationMixer(scene));
44
- const avatarMeshRef = useRef<SkinnedMesh | null>(null);
44
+ const { camera } = useThree();
45
45
 
46
- // Blink state
47
- const lastBlinkTime = useRef(0);
48
- const nextBlinkTime = useRef(0);
49
- const isBlinking = useRef(false);
50
- const blinkStartTime = useRef(0);
46
+ const morphTargetControllerRef = useRef<MorphTargetController>();
47
+ const positionControllerRef = useRef<AvatarPositionController>();
48
+ const targetCameraZRef = useRef(camera.position.z);
51
49
 
52
- const headMeshRef = useRef<SkinnedMesh>();
53
50
 
54
- useEffect(() => {
55
- correctMaterials(materials);
51
+ useHeadMovement(headMovement, nodes);
52
+
53
+ const blinkStateRef = useRef({
54
+ isBlinking: false,
55
+ lastBlinkTime: 0,
56
+ nextBlinkTime: 0,
57
+ blinkStartTime: 0,
58
+ });
56
59
 
57
- scene.traverse((object: Object3D) => {
58
- if (object instanceof SkinnedMesh) {
59
- if (object.name === 'GBNL__Head' || object.name === 'Wolf3D_Avatar') {
60
- headMeshRef.current = object;
61
- if (object.morphTargetDictionary && object.morphTargetInfluences) {
62
- setMorphTargetDictionary(object.morphTargetDictionary);
63
-
64
- const initialInfluences = Object.keys(
65
- object.morphTargetDictionary
66
- ).reduce((acc, key) => ({ ...acc, [key]: 0 }), {});
67
- setMorphTargetInfluences(initialInfluences);
68
- }
69
- }
60
+ // Find head mesh
61
+ const headMesh = useMemo(() => {
62
+ let foundMesh: SkinnedMesh | undefined;
63
+ scene?.traverse((object: Object3D) => {
64
+ if (
65
+ object instanceof SkinnedMesh &&
66
+ (object.name === 'GBNL__Head' || object.name === 'Wolf3D_Avatar')
67
+ ) {
68
+ foundMesh = object;
70
69
  }
71
70
  });
71
+ return foundMesh;
72
+ }, [scene]);
72
73
 
73
- onLoaded?.();
74
+ // Initialize controllers
75
+ useEffect(() => {
76
+ if (!positionControllerRef.current) {
77
+ positionControllerRef.current = new AvatarPositionController(
78
+ AVATAR_POSITION,
79
+ AVATAR_POSITION_ZOOMED,
80
+ );
81
+ }
74
82
 
83
+ if (headMesh) {
84
+ 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
+ }
93
+
94
+ correctMaterials(materials);
95
+ onLoaded?.();
96
+ hideHands(nodes);
97
+
75
98
  return () => {
76
99
  Object.values(materials).forEach(material => material.dispose());
77
100
  Object.values(nodes)
78
101
  .filter(isSkinnedMesh)
79
102
  .forEach(mesh => mesh.geometry.dispose());
80
103
  };
81
- }, [materials, nodes, url, onLoaded, scene]);
82
- useFrame(state => {
83
-
84
- if (
85
- headMeshRef.current &&
86
- headMeshRef.current.morphTargetDictionary &&
87
- headMeshRef.current.morphTargetInfluences
88
- ) {
89
- const currentTime = state.clock.getElapsedTime() * 1000; // Convert to milliseconds
90
-
91
- // Handle blinking
92
- let blinkValue = 0;
93
- if (eyeBlink) {
94
- if (currentTime >= nextBlinkTime.current && !isBlinking.current) {
95
- isBlinking.current = true;
96
- blinkStartTime.current = currentTime;
97
- lastBlinkTime.current = currentTime;
98
- nextBlinkTime.current =
99
- currentTime +
100
- Math.random() *
101
- (BLINK_CONFIG.maxInterval - BLINK_CONFIG.minInterval) +
102
- BLINK_CONFIG.minInterval;
103
- }
104
-
105
- if (isBlinking.current) {
106
- const blinkProgress =
107
- (currentTime - blinkStartTime.current) / BLINK_CONFIG.blinkDuration;
108
- if (blinkProgress <= 0.5) {
109
- // Eyes closing
110
- blinkValue = blinkProgress * 2;
111
- } else if (blinkProgress <= 1) {
112
- // Eyes opening
113
- blinkValue = 2 - blinkProgress * 2;
114
- } else {
115
- // Blink finished
116
- isBlinking.current = false;
117
- blinkValue = 0;
118
- }
119
- }
120
- }
104
+ }, [materials, nodes, url, onLoaded, scene, headMesh]);
121
105
 
122
- const currentViseme = updateCurrentViseme(currentTime / 1000);
106
+ useEffect(() => {
107
+ if (positionControllerRef.current) {
108
+ positionControllerRef.current.updateHeight(avatarHeight, true);
109
+ }
110
+ }, [avatarHeight]);
111
+
112
+ useEffect(() => {
113
+ if (positionControllerRef.current && onCameraZChange) {
114
+ const newCameraZ = positionControllerRef.current.updateDepth(avatarDepth, true);
115
+ onCameraZChange(newCameraZ);
116
+ }
117
+ }, [avatarDepth, onCameraZChange]);
118
+
119
+ // Animation and morphing update loop
120
+ useFrame((state) => {
121
+ const currentTime = state.clock.elapsedTime * 1000;
123
122
 
124
- // Update morph targets
125
- Object.entries(headMeshRef.current.morphTargetDictionary).forEach(
126
- ([key, index]) => {
127
- if (typeof index === 'number') {
128
- let targetValue = 0;
129
-
130
- // Handle visemes (additive layer)
131
- if (currentViseme && key === currentViseme.name) {
132
- targetValue += currentViseme.weight * 1.3; // Amplify the effect
133
- }
134
-
135
- // Handle blinking (additive layer, only for 'eyesClosed')
136
- if (key === 'eyesClosed' && eyeBlink) {
137
- targetValue += blinkValue;
138
- }
139
-
140
- // Clamp the final value between 0 and 1
141
- targetValue = MathUtils.clamp(targetValue, 0, 1);
142
-
143
- // Apply smoothing
144
- if (
145
- headMeshRef.current &&
146
- headMeshRef.current.morphTargetInfluences
147
- ) {
148
- headMeshRef.current.morphTargetInfluences[index] = MathUtils.lerp(
149
- headMeshRef.current.morphTargetInfluences[index],
150
- targetValue,
151
- morphTargetSmoothing
152
- );
153
- }
154
- }
155
- }
123
+ // Update morph targets
124
+ if (morphTargetControllerRef.current) {
125
+ const currentViseme = updateCurrentViseme(currentTime / 1000);
126
+ morphTargetControllerRef.current.updateMorphTargets(
127
+ currentTime,
128
+ {},
129
+ currentViseme,
130
+ eyeBlink,
131
+ blinkStateRef.current,
156
132
  );
133
+ }
157
134
 
158
- // Update the animation mixer
159
- mixer.current.update(0.01); // Fixed delta time for consistent animation speed
135
+ // Update scale with smooth transition
136
+ if (scene && positionControllerRef.current) {
137
+ const newScale = positionControllerRef.current.updateScale(SCALE_LERP_FACTOR);
138
+ scene.scale.copy(newScale);
160
139
  }
161
140
  });
162
141
 
142
+ // Get current position from controller
143
+ const position = positionControllerRef.current?.getPosition() || AVATAR_POSITION;
144
+
163
145
  return (
164
- <group position={AVATAR_POSITION}>
146
+ <group position={position}>
165
147
  <primitive object={scene} />
166
148
  </group>
167
149
  );
168
- }
150
+ }
@@ -0,0 +1,99 @@
1
+ .memori--position-controls {
2
+ position: fixed;
3
+ z-index: 1000;
4
+ top: auto; /* Remove top positioning */
5
+ top: 65px; /* Position from bottom to avoid overlapping with buttons */
6
+ left: 15px; /* Remove left positioning */
7
+ display: flex;
8
+ width: 350px; /* Fixed width */
9
+ max-width: 90%; /* Maximum width relative to screen */
10
+ height: 300px;
11
+ flex-direction: column;
12
+ padding: 16px;
13
+ border-radius: 16px;
14
+ -webkit-backdrop-filter: blur(10px);
15
+ backdrop-filter: blur(10px);
16
+ background-color: var(--memori-inner-bg, #fff);
17
+ box-shadow: 0 4px 20px rgba(0, 0, 0, 0.15);
18
+ gap: 0.5rem;
19
+ text-align: left;
20
+ transform: none; /* Remove transform */
21
+ }
22
+
23
+ /* Slider adjustments */
24
+ .memori--slider-container {
25
+ display: flex;
26
+ flex-direction: column;
27
+ padding: 8px 0;
28
+ gap: 0.75rem;
29
+ }
30
+
31
+ .memori--slider-label {
32
+ margin-bottom: 4px;
33
+ font-size: 1rem;
34
+ }
35
+
36
+ /* Button adjustments */
37
+ .memori--preset-buttons {
38
+ display: flex;
39
+ flex-direction: row;
40
+ justify-content: space-between;
41
+ margin-top: 8px;
42
+ gap: 8px;
43
+ }
44
+
45
+ .memori--preset-buttons button {
46
+ min-height: 36px;
47
+ flex: 1;
48
+ padding: 8px 4px;
49
+ font-size: 0.9rem;
50
+ }
51
+
52
+ /* Close button */
53
+ .memori--position-controls-close {
54
+ position: absolute;
55
+ top: -25px;
56
+ right: -30px;
57
+ border-radius: 50%;
58
+ background-color: var(--memori-inner-bg, #fff);
59
+ }
60
+
61
+ .memori--position-controls-close-button {
62
+ min-width: 36px;
63
+ min-height: 36px;
64
+ padding: 8px;
65
+ }
66
+
67
+ /* Totem specific adjustments */
68
+ @media screen and (max-width: 480px) {
69
+ .memori--position-controls {
70
+ right: 10px;
71
+ bottom: 80px;
72
+ width: 280px;
73
+ }
74
+
75
+ .memori--preset-buttons button {
76
+ padding: 6px 4px;
77
+ font-size: 0.85rem;
78
+ }
79
+
80
+ .memori--slider-container {
81
+ padding: 6px 0;
82
+ }
83
+ }
84
+
85
+ /* Portrait/vertical orientation */
86
+ @media screen and (min-height: 800px) {
87
+ .memori--position-controls {
88
+ bottom: 120px;
89
+ }
90
+
91
+ .memori--slider-label {
92
+ font-size: 1.1rem;
93
+ }
94
+
95
+ .memori--preset-buttons button {
96
+ padding: 10px 6px;
97
+ font-size: 1rem;
98
+ }
99
+ }
@@ -0,0 +1,187 @@
1
+ import { useEffect, useRef } from 'react';
2
+ import { setLocalConfig } from '../../../../../helpers/configuration';
3
+ import { useTranslation } from 'react-i18next';
4
+ import Slider from '../../../../../components/ui/Slider';
5
+ import './positionControls.css';
6
+ import Button from '../../../../ui/Button';
7
+ import Close from '../../../../icons/Close';
8
+
9
+ interface PositionControlsProps {
10
+ avatarHeight: number;
11
+ avatarDepth: number;
12
+ halfBody: boolean;
13
+ setAvatarHeight: (value: number) => void;
14
+ setAvatarDepth: (value: number) => void;
15
+ setEnablePositionControls: (value: boolean) => void;
16
+ }
17
+
18
+ // eslint-disable-next-line no-undef
19
+ const PositionControls: React.FC<PositionControlsProps> = ({
20
+ avatarHeight,
21
+ avatarDepth,
22
+ setAvatarHeight,
23
+ setAvatarDepth,
24
+ halfBody,
25
+ setEnablePositionControls,
26
+ }: PositionControlsProps) => {
27
+ const normalPosition = halfBody ? { height: 80, depth: 50 } : { height: 25, depth: 25 };
28
+ const zoomedPosition = halfBody ? { height: 55, depth: 10 } : { height: 15, depth: 5 };
29
+ const farPosition = halfBody ? { height: 100, depth: 80 } : { height: 65, depth: 100 };
30
+ const settingsRef = useRef<Record<string, any>>({
31
+ height: avatarHeight,
32
+ depth: avatarDepth,
33
+ zoomed: false,
34
+ normal: false,
35
+ far: false,
36
+ });
37
+ const { t } = useTranslation();
38
+
39
+ // Update settings when values change externally
40
+ useEffect(() => {
41
+ settingsRef.current.height = avatarHeight;
42
+ settingsRef.current.depth = avatarDepth;
43
+ }, [avatarHeight, avatarDepth]);
44
+
45
+ // Keyboard controls for depth
46
+ useEffect(() => {
47
+ const handleKeyDown = (event: KeyboardEvent) => {
48
+ if (event.key === '-' || event.key === '_' && settingsRef.current.depth < 100) {
49
+ const newValue = Math.min(settingsRef.current.depth + 10, 100);
50
+ setAvatarDepth(newValue);
51
+ setLocalConfig('avatarDepth', newValue);
52
+ } else if (
53
+ (event.key === '+' || event.key === '=') &&
54
+ settingsRef.current.depth > -100
55
+ ) {
56
+ const newValue = Math.max(settingsRef.current.depth - 10, -100);
57
+ setAvatarDepth(newValue);
58
+ setLocalConfig('avatarDepth', newValue);
59
+ }
60
+ };
61
+
62
+ //add event listeners for plus and minus
63
+ window.addEventListener('keydown', handleKeyDown);
64
+ return () => {
65
+ window.removeEventListener('keydown', handleKeyDown);
66
+ };
67
+ }, [setAvatarDepth]);
68
+
69
+ useEffect(() => {
70
+ const handleArrowUp = (event: KeyboardEvent) => {
71
+ if (event.key === 'ArrowUp' && settingsRef.current.height < 100) {
72
+ const newValue = settingsRef.current.height + 5;
73
+ setAvatarHeight(newValue);
74
+ setLocalConfig('avatarHeight', newValue);
75
+ }
76
+ };
77
+
78
+ const handleArrowDown = (event: KeyboardEvent) => {
79
+ if (event.key === 'ArrowDown' && settingsRef.current.height > 0) {
80
+ const newValue = settingsRef.current.height - 5;
81
+ setAvatarHeight(newValue);
82
+ setLocalConfig('avatarHeight', newValue);
83
+ }
84
+ };
85
+
86
+ window.addEventListener('keydown', handleArrowUp);
87
+ window.addEventListener('keydown', handleArrowDown);
88
+
89
+ return () => {
90
+ window.removeEventListener('keydown', handleArrowUp);
91
+ window.removeEventListener('keydown', handleArrowDown);
92
+ };
93
+ }, [setAvatarHeight]);
94
+
95
+ return (
96
+ <div className="memori--position-controls">
97
+ <div className="memori--position-controls-close">
98
+ <Button
99
+ ghost
100
+ icon={<Close />}
101
+ outlined
102
+ danger
103
+ shape="circle"
104
+ className="memori--position-controls-close-button"
105
+ onClick={() => {
106
+ setEnablePositionControls(false);
107
+ }}
108
+ />
109
+ </div>
110
+ <div className="memori--slider-container">
111
+ <Slider
112
+ defaultValue={settingsRef.current.height}
113
+ min={0.5}
114
+ max={100}
115
+ label={<label className="memori--slider-label">{t('write_and_speak.height')}</label>}
116
+ step={1}
117
+ onChange={(value: number) => {
118
+ setAvatarHeight(value);
119
+ setLocalConfig('avatarHeight', value);
120
+ }}
121
+ />
122
+ </div>
123
+ <div className="memori--slider-container">
124
+ <Slider
125
+ defaultValue={settingsRef.current.depth}
126
+ min={0.5}
127
+ max={100}
128
+ step={5}
129
+ label={<label className="memori--slider-label">{t('write_and_speak.depth')}</label>}
130
+ onChange={(value: number) => {
131
+ setAvatarDepth(value);
132
+ setLocalConfig('avatarDepth', value);
133
+ }}
134
+ />
135
+ </div>
136
+ <div className="memori--preset-buttons">
137
+ <Button
138
+ outlined
139
+ isActive={
140
+ avatarHeight === (halfBody ? zoomedPosition.height : normalPosition.height) &&
141
+ avatarDepth === (halfBody ? zoomedPosition.depth : normalPosition.depth)
142
+ }
143
+ onClick={() => {
144
+ setAvatarHeight(zoomedPosition.height);
145
+ setAvatarDepth(zoomedPosition.depth);
146
+ setLocalConfig('avatarHeight', zoomedPosition.height);
147
+ setLocalConfig('avatarDepth', zoomedPosition.depth);
148
+ }}
149
+ >
150
+ {t('write_and_speak.zoomed')}
151
+ </Button>
152
+ <Button
153
+ outlined
154
+ isActive={
155
+ avatarHeight === (halfBody ? normalPosition.height : zoomedPosition.height) &&
156
+ avatarDepth === (halfBody ? normalPosition.depth : zoomedPosition.depth)
157
+ }
158
+ onClick={() => {
159
+ setAvatarHeight(normalPosition.height);
160
+ setAvatarDepth(normalPosition.depth);
161
+ setLocalConfig('avatarHeight', normalPosition.height);
162
+ setLocalConfig('avatarDepth', normalPosition.depth);
163
+ }}
164
+ >
165
+ {t('write_and_speak.normal')}
166
+ </Button>
167
+ <Button
168
+ outlined
169
+ isActive={
170
+ avatarHeight === (halfBody ? farPosition.height : normalPosition.height) &&
171
+ avatarDepth === (halfBody ? farPosition.depth : normalPosition.depth)
172
+ }
173
+ onClick={() => {
174
+ setAvatarHeight(farPosition.height);
175
+ setAvatarDepth(farPosition.depth);
176
+ setLocalConfig('avatarHeight', farPosition.height);
177
+ setLocalConfig('avatarDepth', farPosition.depth);
178
+ }}
179
+ >
180
+ {t('write_and_speak.far')}
181
+ </Button>
182
+ </div>
183
+ </div>
184
+ );
185
+ };
186
+
187
+ export default PositionControls;