@memori.ai/memori-react 7.5.1 → 7.6.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.
- package/CHANGELOG.md +44 -0
- package/README.md +10 -2
- package/dist/components/Avatar/Avatar.d.ts +2 -0
- package/dist/components/Avatar/Avatar.js +11 -6
- package/dist/components/Avatar/Avatar.js.map +1 -1
- package/dist/components/Avatar/AvatarView/AvatarComponent/avatarComponent.d.ts +20 -0
- package/dist/components/Avatar/AvatarView/AvatarComponent/avatarComponent.js +107 -0
- package/dist/components/Avatar/AvatarView/AvatarComponent/avatarComponent.js.map +1 -0
- package/dist/components/Avatar/AvatarView/AvatarComponent/components/controls.d.ts +26 -0
- package/dist/components/Avatar/AvatarView/AvatarComponent/components/controls.js +59 -0
- package/dist/components/Avatar/AvatarView/AvatarComponent/components/controls.js.map +1 -0
- package/dist/components/Avatar/AvatarView/AvatarComponent/components/fullbodyAvatar.d.ts +30 -0
- package/dist/components/Avatar/AvatarView/AvatarComponent/components/fullbodyAvatar.js +148 -0
- package/dist/components/Avatar/AvatarView/AvatarComponent/components/fullbodyAvatar.js.map +1 -0
- package/dist/components/Avatar/AvatarView/AvatarComponent/components/halfbodyAvatar.d.ts +15 -0
- package/dist/components/Avatar/AvatarView/AvatarComponent/components/halfbodyAvatar.js +77 -0
- package/dist/components/Avatar/AvatarView/AvatarComponent/components/halfbodyAvatar.js.map +1 -0
- package/dist/components/Avatar/AvatarView/AvatarComponent/components/loader.d.ts +5 -0
- package/dist/components/Avatar/AvatarView/AvatarComponent/components/loader.js +12 -0
- package/dist/components/Avatar/AvatarView/AvatarComponent/components/loader.js.map +1 -0
- package/dist/components/Avatar/AvatarView/components/fullbodyAvatar.d.ts +2 -1
- package/dist/components/Avatar/AvatarView/components/fullbodyAvatar.js +3 -2
- package/dist/components/Avatar/AvatarView/components/fullbodyAvatar.js.map +1 -1
- package/dist/components/Avatar/AvatarView/index.d.ts +6 -1
- package/dist/components/Avatar/AvatarView/index.js +14 -84
- package/dist/components/Avatar/AvatarView/index.js.map +1 -1
- package/dist/components/Avatar/AvatarView/utils/useEyeBlink.d.ts +16 -2
- package/dist/components/Avatar/AvatarView/utils/useEyeBlink.js +62 -38
- package/dist/components/Avatar/AvatarView/utils/useEyeBlink.js.map +1 -1
- package/dist/components/Avatar/AvatarView/utils/useMouthAnimation.d.ts +16 -0
- package/dist/components/Avatar/AvatarView/utils/useMouthAnimation.js +59 -0
- package/dist/components/Avatar/AvatarView/utils/useMouthAnimation.js.map +1 -0
- package/dist/components/Avatar/AvatarView/utils/useSmile.js +1 -1
- package/dist/components/Avatar/AvatarView/utils/useSmile.js.map +1 -1
- package/dist/components/ChatBubble/ChatBubble.js +2 -3
- package/dist/components/ChatBubble/ChatBubble.js.map +1 -1
- package/dist/components/CompletionProviderStatus/CompletionProviderStatus.d.ts +1 -1
- package/dist/components/CompletionProviderStatus/CompletionProviderStatus.js +24 -3
- package/dist/components/CompletionProviderStatus/CompletionProviderStatus.js.map +1 -1
- package/dist/components/MemoriWidget/MemoriWidget.d.ts +1 -1
- package/dist/components/MemoriWidget/MemoriWidget.js +25 -3
- package/dist/components/MemoriWidget/MemoriWidget.js.map +1 -1
- package/dist/components/StartPanel/StartPanel.js +1 -1
- package/dist/components/StartPanel/StartPanel.js.map +1 -1
- package/dist/components/layouts/HiddenChat.d.ts +4 -0
- package/dist/components/layouts/HiddenChat.js +51 -0
- package/dist/components/layouts/HiddenChat.js.map +1 -0
- package/dist/components/layouts/ZoomedFullBody.d.ts +4 -0
- package/dist/components/layouts/ZoomedFullBody.js +8 -0
- package/dist/components/layouts/ZoomedFullBody.js.map +1 -0
- package/dist/components/layouts/hidden-chat.css +184 -0
- package/dist/context/visemeContext.d.ts +27 -0
- package/dist/context/visemeContext.js +221 -0
- package/dist/context/visemeContext.js.map +1 -0
- package/dist/helpers/utils.d.ts +7 -0
- package/dist/helpers/utils.js +51 -1
- package/dist/helpers/utils.js.map +1 -1
- package/dist/index.js +20 -16
- package/dist/index.js.map +1 -1
- package/dist/styles.css +1 -0
- package/esm/components/Avatar/Avatar.d.ts +2 -0
- package/esm/components/Avatar/Avatar.js +11 -6
- package/esm/components/Avatar/Avatar.js.map +1 -1
- package/esm/components/Avatar/AvatarView/AvatarComponent/avatarComponent.d.ts +20 -0
- package/esm/components/Avatar/AvatarView/AvatarComponent/avatarComponent.js +102 -0
- package/esm/components/Avatar/AvatarView/AvatarComponent/avatarComponent.js.map +1 -0
- package/esm/components/Avatar/AvatarView/AvatarComponent/components/controls.d.ts +26 -0
- package/esm/components/Avatar/AvatarView/AvatarComponent/components/controls.js +56 -0
- package/esm/components/Avatar/AvatarView/AvatarComponent/components/controls.js.map +1 -0
- package/esm/components/Avatar/AvatarView/AvatarComponent/components/fullbodyAvatar.d.ts +30 -0
- package/esm/components/Avatar/AvatarView/AvatarComponent/components/fullbodyAvatar.js +145 -0
- package/esm/components/Avatar/AvatarView/AvatarComponent/components/fullbodyAvatar.js.map +1 -0
- package/esm/components/Avatar/AvatarView/AvatarComponent/components/halfbodyAvatar.d.ts +15 -0
- package/esm/components/Avatar/AvatarView/AvatarComponent/components/halfbodyAvatar.js +73 -0
- package/esm/components/Avatar/AvatarView/AvatarComponent/components/halfbodyAvatar.js.map +1 -0
- package/esm/components/Avatar/AvatarView/AvatarComponent/components/loader.d.ts +5 -0
- package/esm/components/Avatar/AvatarView/AvatarComponent/components/loader.js +9 -0
- package/esm/components/Avatar/AvatarView/AvatarComponent/components/loader.js.map +1 -0
- package/esm/components/Avatar/AvatarView/components/fullbodyAvatar.d.ts +2 -1
- package/esm/components/Avatar/AvatarView/components/fullbodyAvatar.js +3 -2
- package/esm/components/Avatar/AvatarView/components/fullbodyAvatar.js.map +1 -1
- package/esm/components/Avatar/AvatarView/index.d.ts +6 -1
- package/esm/components/Avatar/AvatarView/index.js +15 -85
- package/esm/components/Avatar/AvatarView/index.js.map +1 -1
- package/esm/components/Avatar/AvatarView/utils/useEyeBlink.d.ts +16 -2
- package/esm/components/Avatar/AvatarView/utils/useEyeBlink.js +61 -38
- package/esm/components/Avatar/AvatarView/utils/useEyeBlink.js.map +1 -1
- package/esm/components/Avatar/AvatarView/utils/useMouthAnimation.d.ts +16 -0
- package/esm/components/Avatar/AvatarView/utils/useMouthAnimation.js +55 -0
- package/esm/components/Avatar/AvatarView/utils/useMouthAnimation.js.map +1 -0
- package/esm/components/Avatar/AvatarView/utils/useSmile.js +1 -1
- package/esm/components/Avatar/AvatarView/utils/useSmile.js.map +1 -1
- package/esm/components/ChatBubble/ChatBubble.js +2 -3
- package/esm/components/ChatBubble/ChatBubble.js.map +1 -1
- package/esm/components/CompletionProviderStatus/CompletionProviderStatus.d.ts +1 -1
- package/esm/components/CompletionProviderStatus/CompletionProviderStatus.js +24 -3
- package/esm/components/CompletionProviderStatus/CompletionProviderStatus.js.map +1 -1
- package/esm/components/MemoriWidget/MemoriWidget.d.ts +1 -1
- package/esm/components/MemoriWidget/MemoriWidget.js +26 -4
- package/esm/components/MemoriWidget/MemoriWidget.js.map +1 -1
- package/esm/components/StartPanel/StartPanel.js +1 -1
- package/esm/components/StartPanel/StartPanel.js.map +1 -1
- package/esm/components/layouts/HiddenChat.d.ts +4 -0
- package/esm/components/layouts/HiddenChat.js +48 -0
- package/esm/components/layouts/HiddenChat.js.map +1 -0
- package/esm/components/layouts/ZoomedFullBody.d.ts +4 -0
- package/esm/components/layouts/ZoomedFullBody.js +5 -0
- package/esm/components/layouts/ZoomedFullBody.js.map +1 -0
- package/esm/components/layouts/hidden-chat.css +184 -0
- package/esm/context/visemeContext.d.ts +27 -0
- package/esm/context/visemeContext.js +216 -0
- package/esm/context/visemeContext.js.map +1 -0
- package/esm/helpers/utils.d.ts +7 -0
- package/esm/helpers/utils.js +45 -0
- package/esm/helpers/utils.js.map +1 -1
- package/esm/index.js +20 -16
- package/esm/index.js.map +1 -1
- package/esm/styles.css +1 -0
- package/package.json +2 -2
- package/src/components/Avatar/Avatar.test.tsx +28 -20
- package/src/components/Avatar/Avatar.tsx +19 -5
- package/src/components/Avatar/AvatarView/AvatarComponent/avatarComponent.tsx +222 -0
- package/src/components/Avatar/AvatarView/{components → AvatarComponent/components}/controls.tsx +16 -10
- package/src/components/Avatar/AvatarView/AvatarComponent/components/fullbodyAvatar.tsx +234 -0
- package/src/components/Avatar/AvatarView/AvatarComponent/components/halfbodyAvatar.tsx +123 -0
- package/src/components/Avatar/AvatarView/{components → AvatarComponent/components}/loader.tsx +1 -1
- package/src/components/Avatar/AvatarView/AvatarView.stories.tsx +47 -8
- package/src/components/Avatar/AvatarView/index.tsx +35 -167
- package/src/components/Avatar/AvatarView/utils/useEyeBlink.ts +89 -48
- package/src/components/Avatar/AvatarView/utils/useMouthAnimation.ts +93 -0
- package/src/components/Avatar/AvatarView/utils/useSmile.ts +1 -1
- package/src/components/ChatBubble/ChatBubble.tsx +3 -4
- package/src/components/CompletionProviderStatus/CompletionProviderStatus.tsx +33 -3
- package/src/components/CompletionProviderStatus/__snapshots__/CompletionProviderStatus.test.tsx.snap +18 -0
- package/src/components/MemoriWidget/MemoriWidget.tsx +60 -5
- package/src/components/StartPanel/StartPanel.tsx +1 -1
- package/src/components/layouts/Chat.test.tsx +7 -5
- package/src/components/layouts/FullPage.test.tsx +11 -8
- package/src/components/layouts/HiddenChat.test.tsx +37 -0
- package/src/components/layouts/HiddenChat.tsx +108 -0
- package/src/components/layouts/Totem.test.tsx +6 -4
- package/src/components/layouts/WebsiteAssistant.test.tsx +7 -5
- package/src/components/layouts/ZoomedFullBody.test.tsx +37 -0
- package/src/components/layouts/ZoomedFullBody.tsx +55 -0
- package/src/components/layouts/__snapshots__/HiddenChat.test.tsx.snap +210 -0
- package/src/components/layouts/__snapshots__/ZoomedFullBody.test.tsx.snap +444 -0
- package/src/components/layouts/hidden-chat.css +184 -0
- package/src/components/layouts/layouts.stories.tsx +135 -19
- package/src/context/visemeContext.tsx +328 -0
- package/src/helpers/utils.ts +73 -0
- package/src/index.stories.tsx +40 -17
- package/src/index.tsx +82 -78
- package/src/styles.css +1 -0
- package/src/components/Avatar/AvatarView/components/fullbodyAvatar.tsx +0 -120
- package/src/components/Avatar/AvatarView/components/halfbodyAvatar.tsx +0 -69
- package/src/components/Avatar/AvatarView/utils/useMouthSpeaking.ts +0 -87
|
@@ -1,23 +1,10 @@
|
|
|
1
1
|
import { CSSProperties } from 'react';
|
|
2
2
|
import React, { Suspense } from 'react';
|
|
3
|
-
import FullbodyAvatar from './components/fullbodyAvatar';
|
|
4
3
|
import { Canvas } from '@react-three/fiber';
|
|
5
4
|
import { OrbitControls, SpotLight, Environment } from '@react-three/drei';
|
|
6
5
|
import { isAndroid, isiOS } from '../../../helpers/utils';
|
|
7
|
-
import {
|
|
8
|
-
import
|
|
9
|
-
import HalfBodyAvatar from './components/halfbodyAvatar';
|
|
10
|
-
import Loader from './components/loader';
|
|
11
|
-
|
|
12
|
-
interface BaseAction {
|
|
13
|
-
weight: number;
|
|
14
|
-
action?: string;
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
interface AdditiveAction {
|
|
18
|
-
weight: number;
|
|
19
|
-
action?: string;
|
|
20
|
-
}
|
|
6
|
+
import {AvatarView} from './AvatarComponent/avatarComponent';
|
|
7
|
+
import Loader from './AvatarComponent/components/loader';
|
|
21
8
|
|
|
22
9
|
export interface Props {
|
|
23
10
|
url: string;
|
|
@@ -33,24 +20,18 @@ export interface Props {
|
|
|
33
20
|
loading?: boolean;
|
|
34
21
|
animation?: string;
|
|
35
22
|
showControls?: boolean;
|
|
23
|
+
isZoomed?: boolean;
|
|
24
|
+
chatEmission?: any;
|
|
25
|
+
setMeshRef?: any;
|
|
26
|
+
clearVisemes: () => void;
|
|
27
|
+
setEmotion: (emotion: string) => void;
|
|
36
28
|
}
|
|
37
29
|
|
|
38
|
-
const baseActions: Record<string, BaseAction> = {
|
|
39
|
-
Idle: { weight: 1 },
|
|
40
|
-
'Idle 1': { weight: 0 },
|
|
41
|
-
'Idle 2': { weight: 0 },
|
|
42
|
-
'Idle 3': { weight: 0 },
|
|
43
|
-
Loading: { weight: 0 },
|
|
44
|
-
Sad: { weight: 0 },
|
|
45
|
-
'Talk 1': { weight: 0 },
|
|
46
|
-
'Talk 2': { weight: 0 },
|
|
47
|
-
'Talk 3': { weight: 0 },
|
|
48
|
-
};
|
|
49
|
-
|
|
50
30
|
const defaultStyles = {
|
|
51
31
|
halfBody: {
|
|
52
|
-
width: '
|
|
53
|
-
height: '
|
|
32
|
+
width: '100%',
|
|
33
|
+
height: '100%',
|
|
34
|
+
minHeight: '500px', // Ensure minimum height
|
|
54
35
|
backgroundColor: 'white',
|
|
55
36
|
borderRadius: '100%',
|
|
56
37
|
},
|
|
@@ -62,12 +43,18 @@ const defaultStyles = {
|
|
|
62
43
|
};
|
|
63
44
|
|
|
64
45
|
/* Animation Control Panel */
|
|
65
|
-
const getCameraSettings = (halfBody: boolean) =>
|
|
46
|
+
const getCameraSettings = (halfBody: boolean, isZoomed?: boolean) =>
|
|
66
47
|
halfBody
|
|
67
48
|
? {
|
|
68
49
|
fov: 40,
|
|
69
50
|
position: [0, 0, 0.6],
|
|
70
51
|
}
|
|
52
|
+
: !halfBody && isZoomed
|
|
53
|
+
? {
|
|
54
|
+
// Zoomed in
|
|
55
|
+
fov: 44,
|
|
56
|
+
position: [0, 0, 1.25],
|
|
57
|
+
}
|
|
71
58
|
: { fov: 40, position: [0, 0.0000175, 3] };
|
|
72
59
|
|
|
73
60
|
const getLightingComponent = () =>
|
|
@@ -82,137 +69,8 @@ const getLightingComponent = () =>
|
|
|
82
69
|
) : (
|
|
83
70
|
<Environment files="https://raw.githack.com/pmndrs/drei-assets/456060a26bbeb8fdf79326f224b6d99b8bcce736/hdri/venice_sunset_1k.hdr" />
|
|
84
71
|
);
|
|
72
|
+
|
|
85
73
|
|
|
86
|
-
const AvatarComponent = ({
|
|
87
|
-
animation,
|
|
88
|
-
loading,
|
|
89
|
-
halfBody,
|
|
90
|
-
...props
|
|
91
|
-
}: Props & {
|
|
92
|
-
halfBody: boolean;
|
|
93
|
-
currentBaseAction: {
|
|
94
|
-
action: string;
|
|
95
|
-
weight: number;
|
|
96
|
-
};
|
|
97
|
-
baseActions: Record<string, BaseAction>;
|
|
98
|
-
additiveActions: Record<string, AdditiveAction>;
|
|
99
|
-
timeScale: number;
|
|
100
|
-
loading?: boolean;
|
|
101
|
-
animation?: string;
|
|
102
|
-
}) =>
|
|
103
|
-
halfBody ? <HalfBodyAvatar {...props} /> : <FullbodyAvatar {...props} />;
|
|
104
|
-
|
|
105
|
-
const AvatarView = ({
|
|
106
|
-
showControls,
|
|
107
|
-
animation,
|
|
108
|
-
loading,
|
|
109
|
-
url,
|
|
110
|
-
sex,
|
|
111
|
-
eyeBlink,
|
|
112
|
-
headMovement,
|
|
113
|
-
speaking,
|
|
114
|
-
halfBody,
|
|
115
|
-
}: Props & { halfBody: boolean }) => {
|
|
116
|
-
const [currentBaseAction, setCurrentBaseAction] = useState({
|
|
117
|
-
action: animation || 'Idle',
|
|
118
|
-
weight: 1,
|
|
119
|
-
});
|
|
120
|
-
|
|
121
|
-
const [additiveActions, setAdditiveActions] = useState({
|
|
122
|
-
smile: { weight: 0 },
|
|
123
|
-
blink: { weight: eyeBlink ? 1 : 0 },
|
|
124
|
-
speak: { weight: speaking ? 1 : 0 },
|
|
125
|
-
headMovement: { weight: headMovement ? 1 : 0 },
|
|
126
|
-
});
|
|
127
|
-
|
|
128
|
-
const [timeScale, setTimeScale] = useState(0.8);
|
|
129
|
-
|
|
130
|
-
function onBaseActionChange(action: string) {
|
|
131
|
-
setCurrentBaseAction({
|
|
132
|
-
action,
|
|
133
|
-
weight: 1,
|
|
134
|
-
});
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
function onAdditiveActionChange(action: string, weight: number) {
|
|
138
|
-
setAdditiveActions({
|
|
139
|
-
...additiveActions,
|
|
140
|
-
[action]: { weight },
|
|
141
|
-
});
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
function modifyTimeScale(value: number) {
|
|
145
|
-
setTimeScale(value);
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
useEffect(() => {
|
|
149
|
-
// If loading and not speaking, set to loading animation
|
|
150
|
-
if (loading && currentBaseAction.action !== 'Loading' && !speaking) {
|
|
151
|
-
setCurrentBaseAction({
|
|
152
|
-
action: 'Loading',
|
|
153
|
-
weight: 1,
|
|
154
|
-
});
|
|
155
|
-
return;
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
// Otherwise, if speaking, set to random talking animation
|
|
159
|
-
if (speaking) {
|
|
160
|
-
const talkingAnimations = ['Talk 1', 'Talk 2', 'Talk 3'];
|
|
161
|
-
const randomIndex = Math.floor(Math.random() * talkingAnimations.length);
|
|
162
|
-
const randomTalkingAnimation = talkingAnimations[randomIndex];
|
|
163
|
-
|
|
164
|
-
setAdditiveActions({
|
|
165
|
-
...additiveActions,
|
|
166
|
-
speak: { weight: 1 },
|
|
167
|
-
});
|
|
168
|
-
|
|
169
|
-
setCurrentBaseAction({
|
|
170
|
-
action: randomTalkingAnimation,
|
|
171
|
-
weight: 1,
|
|
172
|
-
});
|
|
173
|
-
} else if (!speaking && additiveActions.speak.weight !== 0) {
|
|
174
|
-
// Otherwise, if not speaking, set to idle
|
|
175
|
-
setAdditiveActions({
|
|
176
|
-
...additiveActions,
|
|
177
|
-
speak: { weight: 0 },
|
|
178
|
-
});
|
|
179
|
-
setCurrentBaseAction({
|
|
180
|
-
action: 'Idle',
|
|
181
|
-
weight: 1,
|
|
182
|
-
});
|
|
183
|
-
}
|
|
184
|
-
}, [speaking, loading]);
|
|
185
|
-
|
|
186
|
-
return (
|
|
187
|
-
<>
|
|
188
|
-
{showControls && (
|
|
189
|
-
<AnimationControlPanel
|
|
190
|
-
timeScale={timeScale}
|
|
191
|
-
onBaseActionChange={onBaseActionChange}
|
|
192
|
-
onAdditiveActionChange={onAdditiveActionChange}
|
|
193
|
-
baseActions={baseActions}
|
|
194
|
-
additiveActions={additiveActions}
|
|
195
|
-
currentBaseAction={currentBaseAction}
|
|
196
|
-
modifyTimeScale={modifyTimeScale}
|
|
197
|
-
/>
|
|
198
|
-
)}
|
|
199
|
-
<AvatarComponent
|
|
200
|
-
halfBody={halfBody}
|
|
201
|
-
url={url}
|
|
202
|
-
sex={sex}
|
|
203
|
-
eyeBlink={eyeBlink}
|
|
204
|
-
headMovement={headMovement}
|
|
205
|
-
speaking={speaking}
|
|
206
|
-
additiveActions={additiveActions}
|
|
207
|
-
baseActions={baseActions}
|
|
208
|
-
loading={loading || false}
|
|
209
|
-
currentBaseAction={currentBaseAction}
|
|
210
|
-
timeScale={timeScale}
|
|
211
|
-
animation={animation}
|
|
212
|
-
/>
|
|
213
|
-
</>
|
|
214
|
-
);
|
|
215
|
-
};
|
|
216
74
|
|
|
217
75
|
export default function ContainerAvatarView({
|
|
218
76
|
url,
|
|
@@ -228,27 +86,37 @@ export default function ContainerAvatarView({
|
|
|
228
86
|
loading,
|
|
229
87
|
animation,
|
|
230
88
|
showControls = false,
|
|
89
|
+
isZoomed,
|
|
90
|
+
chatEmission,
|
|
91
|
+
setMeshRef,
|
|
92
|
+
clearVisemes,
|
|
93
|
+
setEmotion,
|
|
231
94
|
}: Props) {
|
|
232
95
|
return (
|
|
233
96
|
<Canvas
|
|
234
97
|
style={
|
|
235
98
|
style || (halfBody ? defaultStyles.halfBody : defaultStyles.fullBody)
|
|
236
99
|
}
|
|
237
|
-
camera={getCameraSettings(halfBody) as any}
|
|
100
|
+
camera={getCameraSettings(halfBody, isZoomed) as any}
|
|
238
101
|
>
|
|
239
102
|
<Suspense fallback={fallback || <Loader fallbackImg={fallbackImg} />}>
|
|
240
103
|
{getLightingComponent()}
|
|
241
104
|
{rotateAvatar && <OrbitControls enablePan={false} enableZoom={false} />}
|
|
242
105
|
<AvatarView
|
|
243
|
-
halfBody={halfBody}
|
|
244
106
|
url={url}
|
|
245
107
|
sex={sex}
|
|
246
|
-
eyeBlink={eyeBlink}
|
|
247
|
-
headMovement={headMovement}
|
|
248
|
-
speaking={speaking}
|
|
249
|
-
loading={loading}
|
|
250
|
-
animation={animation}
|
|
251
108
|
showControls={showControls}
|
|
109
|
+
loading={loading || false}
|
|
110
|
+
animation={animation}
|
|
111
|
+
isZoomed={isZoomed || false}
|
|
112
|
+
eyeBlink={eyeBlink || false}
|
|
113
|
+
headMovement={headMovement || false}
|
|
114
|
+
speaking={speaking || false}
|
|
115
|
+
halfBody={halfBody || false}
|
|
116
|
+
chatEmission={chatEmission}
|
|
117
|
+
setMeshRef={setMeshRef}
|
|
118
|
+
clearVisemes={clearVisemes}
|
|
119
|
+
setEmotion={setEmotion}
|
|
252
120
|
/>
|
|
253
121
|
</Suspense>
|
|
254
122
|
</Canvas>
|
|
@@ -1,59 +1,100 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { Nodes } from './utils';
|
|
3
|
-
import { useEffect, useRef, useCallback } from 'react';
|
|
4
|
-
import { useFrame } from '@react-three/fiber';
|
|
1
|
+
import { useCallback, useEffect, useRef } from 'react';
|
|
5
2
|
|
|
6
|
-
interface
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
3
|
+
interface BlinkConfig {
|
|
4
|
+
minInterval: number;
|
|
5
|
+
maxInterval: number;
|
|
6
|
+
blinkDuration: number;
|
|
10
7
|
}
|
|
11
8
|
|
|
12
|
-
const
|
|
13
|
-
|
|
14
|
-
|
|
9
|
+
const DEFAULT_BLINK_CONFIG: BlinkConfig = {
|
|
10
|
+
minInterval: 1000, // Minimum time between blinks in milliseconds
|
|
11
|
+
maxInterval: 5000, // Maximum time between blinks in milliseconds
|
|
12
|
+
blinkDuration: 150, // Duration of a single blink in milliseconds
|
|
13
|
+
};
|
|
15
14
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
});
|
|
15
|
+
interface UseAvatarBlinkProps {
|
|
16
|
+
enabled: boolean;
|
|
17
|
+
setMorphTargetInfluences: (morphTargetInfluences: any) => void;
|
|
18
|
+
config?: Partial<BlinkConfig>;
|
|
19
|
+
}
|
|
22
20
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
21
|
+
export function useAvatarBlink({
|
|
22
|
+
enabled,
|
|
23
|
+
setMorphTargetInfluences,
|
|
24
|
+
config = {}
|
|
25
|
+
}: UseAvatarBlinkProps) {
|
|
26
|
+
const blinkTimeoutRef = useRef<NodeJS.Timeout>();
|
|
27
|
+
const isBlinkingRef = useRef(false);
|
|
28
|
+
const lastBlinkTime = useRef(0);
|
|
29
|
+
|
|
30
|
+
const blinkConfig = {
|
|
31
|
+
...DEFAULT_BLINK_CONFIG,
|
|
32
|
+
...config
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
const blink = useCallback(() => {
|
|
36
|
+
if (!enabled || isBlinkingRef.current) return;
|
|
37
|
+
|
|
38
|
+
isBlinkingRef.current = true;
|
|
39
|
+
// Close eyes
|
|
40
|
+
setMorphTargetInfluences((prev: any) => ({
|
|
41
|
+
...prev,
|
|
42
|
+
eyesClosed: 1
|
|
43
|
+
}));
|
|
44
|
+
|
|
45
|
+
// Open eyes after blinkDuration
|
|
46
|
+
setTimeout(() => {
|
|
47
|
+
setMorphTargetInfluences((prev: any) => ({
|
|
48
|
+
...prev,
|
|
49
|
+
eyesClosed: 0
|
|
50
|
+
}));
|
|
51
|
+
isBlinkingRef.current = false;
|
|
52
|
+
lastBlinkTime.current = Date.now();
|
|
53
|
+
|
|
54
|
+
// Schedule next blink
|
|
55
|
+
scheduleNextBlink();
|
|
56
|
+
}, blinkConfig.blinkDuration);
|
|
57
|
+
}, [enabled, blinkConfig.blinkDuration, setMorphTargetInfluences]);
|
|
58
|
+
|
|
59
|
+
const scheduleNextBlink = useCallback(() => {
|
|
60
|
+
if (blinkTimeoutRef.current) {
|
|
61
|
+
clearTimeout(blinkTimeoutRef.current);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// Randomize the next blink delay between min and max interval
|
|
65
|
+
const nextBlinkDelay = Math.random() *
|
|
66
|
+
(blinkConfig.maxInterval - blinkConfig.minInterval) +
|
|
67
|
+
blinkConfig.minInterval;
|
|
68
|
+
|
|
69
|
+
blinkTimeoutRef.current = setTimeout(blink, nextBlinkDelay);
|
|
70
|
+
}, [blink, blinkConfig.maxInterval, blinkConfig.minInterval]);
|
|
28
71
|
|
|
72
|
+
// Handle enabled state changes
|
|
29
73
|
useEffect(() => {
|
|
30
|
-
if (
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
74
|
+
if (enabled) {
|
|
75
|
+
scheduleNextBlink();
|
|
76
|
+
} else {
|
|
77
|
+
if (blinkTimeoutRef.current) {
|
|
78
|
+
clearTimeout(blinkTimeoutRef.current);
|
|
79
|
+
}
|
|
80
|
+
// Reset eyes to open
|
|
81
|
+
setMorphTargetInfluences((prevInfluences: any) => ({
|
|
82
|
+
...prevInfluences,
|
|
83
|
+
eyesClosed: 0
|
|
84
|
+
}));
|
|
37
85
|
}
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
86
|
+
|
|
87
|
+
// Cleanup
|
|
41
88
|
return () => {
|
|
42
|
-
|
|
89
|
+
if (blinkTimeoutRef.current) {
|
|
90
|
+
clearTimeout(blinkTimeoutRef.current);
|
|
91
|
+
}
|
|
43
92
|
};
|
|
44
|
-
}, [
|
|
45
|
-
|
|
46
|
-
useFrame((_, delta) => {
|
|
47
|
-
if (!enabled) return;
|
|
48
|
-
|
|
49
|
-
const { blinkTime, headMesh, morphIndex } = blinkStateRef.current;
|
|
93
|
+
}, [enabled, scheduleNextBlink, setMorphTargetInfluences]);
|
|
50
94
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
}
|
|
58
|
-
});
|
|
59
|
-
}
|
|
95
|
+
return {
|
|
96
|
+
isBlinking: isBlinkingRef.current,
|
|
97
|
+
lastBlinkTime: lastBlinkTime.current,
|
|
98
|
+
triggerBlink: blink
|
|
99
|
+
};
|
|
100
|
+
}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import React, { useRef } from 'react';
|
|
2
|
+
import { SkinnedMesh } from 'three';
|
|
3
|
+
|
|
4
|
+
const VISEME_SMOOTHING = 0.2;
|
|
5
|
+
const VISEME_SPEED_FACTOR = 0.44;
|
|
6
|
+
|
|
7
|
+
interface Viseme {
|
|
8
|
+
name: string;
|
|
9
|
+
duration: number;
|
|
10
|
+
weight: number;
|
|
11
|
+
startTime: number;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
interface UseMouthAnimationProps {
|
|
15
|
+
currentVisemes: Viseme[];
|
|
16
|
+
avatarMeshRef: React.RefObject<SkinnedMesh>;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const lerp = (start: number, end: number, alpha: number): number => {
|
|
20
|
+
return start * (1 - alpha) + end * alpha;
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
export function useMouthAnimation({ currentVisemes, avatarMeshRef }: UseMouthAnimationProps) {
|
|
24
|
+
const visemeStartTimeRef = useRef(0);
|
|
25
|
+
const currentVisemeWeightRef = useRef<{ [key: string]: number }>({});
|
|
26
|
+
|
|
27
|
+
// Helper function to get current viseme information
|
|
28
|
+
const getCurrentVisemeInfo = (elapsedTime: number) => {
|
|
29
|
+
let currentVisemeIndex = 0;
|
|
30
|
+
let accumulatedDuration = 0;
|
|
31
|
+
|
|
32
|
+
while (
|
|
33
|
+
currentVisemeIndex < currentVisemes.length &&
|
|
34
|
+
accumulatedDuration <= elapsedTime
|
|
35
|
+
) {
|
|
36
|
+
accumulatedDuration += currentVisemes[currentVisemeIndex].duration;
|
|
37
|
+
currentVisemeIndex++;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
return { currentVisemeIndex, accumulatedDuration };
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
// Helper function to apply the current viseme
|
|
44
|
+
const applyCurrentViseme = (index: number, elapsedTime: number, accumulatedDuration: number) => {
|
|
45
|
+
const currentViseme = currentVisemes[index - 1];
|
|
46
|
+
const visemeProgress = (elapsedTime - (accumulatedDuration - currentViseme.duration)) / currentViseme.duration;
|
|
47
|
+
const targetWeight = Math.sin(visemeProgress * Math.PI) * currentViseme.weight;
|
|
48
|
+
|
|
49
|
+
// Smooth the transition between visemes
|
|
50
|
+
if (!currentVisemeWeightRef.current[currentViseme.name]) {
|
|
51
|
+
currentVisemeWeightRef.current[currentViseme.name] = 0;
|
|
52
|
+
}
|
|
53
|
+
currentVisemeWeightRef.current[currentViseme.name] = lerp(
|
|
54
|
+
currentVisemeWeightRef.current[currentViseme.name],
|
|
55
|
+
targetWeight,
|
|
56
|
+
VISEME_SMOOTHING
|
|
57
|
+
);
|
|
58
|
+
|
|
59
|
+
const visemeIndex = avatarMeshRef.current?.morphTargetDictionary?.[currentViseme.name];
|
|
60
|
+
if (typeof visemeIndex === 'number' && avatarMeshRef.current?.morphTargetInfluences) {
|
|
61
|
+
avatarMeshRef.current.morphTargetInfluences[visemeIndex] = currentVisemeWeightRef.current[currentViseme.name];
|
|
62
|
+
}
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
// Helper function to reset viseme animation
|
|
66
|
+
const resetVisemeAnimation = (currentTime: number) => {
|
|
67
|
+
visemeStartTimeRef.current = currentTime;
|
|
68
|
+
currentVisemeWeightRef.current = {};
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
// Main function to handle viseme-based mouth movement
|
|
72
|
+
const handleMouthMovement = (elapsedTime: number) => {
|
|
73
|
+
if (currentVisemes.length === 0) return;
|
|
74
|
+
|
|
75
|
+
const currentTime = elapsedTime * VISEME_SPEED_FACTOR;
|
|
76
|
+
const visemeElapsedTime = currentTime - visemeStartTimeRef.current;
|
|
77
|
+
|
|
78
|
+
const { currentVisemeIndex, accumulatedDuration } = getCurrentVisemeInfo(visemeElapsedTime);
|
|
79
|
+
|
|
80
|
+
if (currentVisemeIndex > 0) {
|
|
81
|
+
applyCurrentViseme(currentVisemeIndex, visemeElapsedTime, accumulatedDuration);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// Reset viseme animation if we've reached the end
|
|
85
|
+
if (visemeElapsedTime > accumulatedDuration) {
|
|
86
|
+
resetVisemeAnimation(currentTime);
|
|
87
|
+
}
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
return {
|
|
91
|
+
handleMouthMovement,
|
|
92
|
+
};
|
|
93
|
+
}
|
|
@@ -17,7 +17,7 @@ export default function useSmile(smiling: boolean | undefined, nodes: Nodes) {
|
|
|
17
17
|
});
|
|
18
18
|
|
|
19
19
|
useEffect(() => {
|
|
20
|
-
const headMesh = (nodes.Wolf3D_Head || nodes.
|
|
20
|
+
const headMesh = (nodes.Wolf3D_Head || nodes.Wolf3D_Avatar020 || nodes.Wolf3D_Avatar001) as SkinnedMesh;
|
|
21
21
|
smileStateRef.current.headMesh = headMesh;
|
|
22
22
|
|
|
23
23
|
if (headMesh?.morphTargetDictionary && headMesh?.morphTargetInfluences) {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import React, { useLayoutEffect,
|
|
1
|
+
import React, { useLayoutEffect, useState } from 'react';
|
|
2
2
|
import cx from 'classnames';
|
|
3
3
|
import {
|
|
4
4
|
ExpertReference,
|
|
@@ -18,13 +18,12 @@ import FeedbackButtons from '../FeedbackButtons/FeedbackButtons';
|
|
|
18
18
|
import { useTranslation } from 'react-i18next';
|
|
19
19
|
import { marked } from 'marked';
|
|
20
20
|
import DOMPurify from 'dompurify';
|
|
21
|
-
import { cleanUrl } from '../../helpers/utils';
|
|
22
21
|
import Button from '../ui/Button';
|
|
23
22
|
import QuestionHelp from '../icons/QuestionHelp';
|
|
24
23
|
import Copy from '../icons/Copy';
|
|
25
24
|
import Code from '../icons/Code';
|
|
26
25
|
import WhyThisAnswer from '../WhyThisAnswer/WhyThisAnswer';
|
|
27
|
-
import {
|
|
26
|
+
import { cleanUrl, stripHTML, stripOutputTags } from '../../helpers/utils';
|
|
28
27
|
|
|
29
28
|
import markedLinkifyIt from 'marked-linkify-it';
|
|
30
29
|
import markedKatex from 'marked-katex-extension';
|
|
@@ -149,7 +148,7 @@ const ChatBubble: React.FC<Props> = ({
|
|
|
149
148
|
|
|
150
149
|
const plainText = message.fromUser
|
|
151
150
|
? text
|
|
152
|
-
:
|
|
151
|
+
: stripHTML(stripOutputTags(renderedText));
|
|
153
152
|
|
|
154
153
|
useLayoutEffect(() => {
|
|
155
154
|
if (typeof window !== 'undefined' && !message.fromUser) {
|
|
@@ -22,7 +22,6 @@ const initProviderStatus = (
|
|
|
22
22
|
statusPage: string;
|
|
23
23
|
} => {
|
|
24
24
|
switch (provider) {
|
|
25
|
-
case 'DEFAULT':
|
|
26
25
|
case 'OpenAI':
|
|
27
26
|
return {
|
|
28
27
|
getStatus: async () => {
|
|
@@ -37,6 +36,34 @@ const initProviderStatus = (
|
|
|
37
36
|
},
|
|
38
37
|
statusPage: 'https://status.openai.com/',
|
|
39
38
|
};
|
|
39
|
+
case 'Mistral':
|
|
40
|
+
return {
|
|
41
|
+
getStatus: async () => {
|
|
42
|
+
const res = await fetch(
|
|
43
|
+
'https://status.mistral-data.com/api/v2/summary.json'
|
|
44
|
+
);
|
|
45
|
+
const data = await res.json();
|
|
46
|
+
const status = data.components.find(
|
|
47
|
+
(component: { name: string }) => component.name === 'API'
|
|
48
|
+
)?.status as Status;
|
|
49
|
+
return status ?? 'operational';
|
|
50
|
+
},
|
|
51
|
+
statusPage: 'https://status.mistral-data.com/',
|
|
52
|
+
};
|
|
53
|
+
case 'Anthropic':
|
|
54
|
+
return {
|
|
55
|
+
getStatus: async () => {
|
|
56
|
+
const res = await fetch(
|
|
57
|
+
'https://status.anthropic.com/api/v2/summary.json'
|
|
58
|
+
);
|
|
59
|
+
const data = await res.json();
|
|
60
|
+
const status = data.components.find(
|
|
61
|
+
(component: { name: string }) => component.name === 'API'
|
|
62
|
+
)?.status as Status;
|
|
63
|
+
return status ?? 'operational';
|
|
64
|
+
},
|
|
65
|
+
statusPage: 'https://status.anthropic.com/',
|
|
66
|
+
};
|
|
40
67
|
default:
|
|
41
68
|
return {
|
|
42
69
|
getStatus: async () => 'operational',
|
|
@@ -45,7 +72,10 @@ const initProviderStatus = (
|
|
|
45
72
|
}
|
|
46
73
|
};
|
|
47
74
|
|
|
48
|
-
const CompletionProviderStatus = ({
|
|
75
|
+
const CompletionProviderStatus = ({
|
|
76
|
+
forceStatus,
|
|
77
|
+
provider = 'OpenAI',
|
|
78
|
+
}: Props) => {
|
|
49
79
|
const { t } = useTranslation();
|
|
50
80
|
const [status, setStatus] = useState<Status>(forceStatus ?? 'operational');
|
|
51
81
|
|
|
@@ -58,7 +88,7 @@ const CompletionProviderStatus = ({ forceStatus, provider }: Props) => {
|
|
|
58
88
|
.getStatus()
|
|
59
89
|
.then(status => setStatus(status))
|
|
60
90
|
.catch(console.log);
|
|
61
|
-
}, [forceStatus,
|
|
91
|
+
}, [forceStatus, providerStatus]);
|
|
62
92
|
|
|
63
93
|
return status !== 'operational' ? (
|
|
64
94
|
<Tooltip
|
package/src/components/CompletionProviderStatus/__snapshots__/CompletionProviderStatus.test.tsx.snap
CHANGED
|
@@ -12,6 +12,15 @@ exports[`renders CompletionProviderStatus errored unchanged 1`] = `
|
|
|
12
12
|
<p>
|
|
13
13
|
completionProviderDown
|
|
14
14
|
</p>
|
|
15
|
+
<p>
|
|
16
|
+
<a
|
|
17
|
+
href="https://status.openai.com/"
|
|
18
|
+
rel="noopener noreferrer"
|
|
19
|
+
target="_blank"
|
|
20
|
+
>
|
|
21
|
+
completionProviderCheckStatusPage
|
|
22
|
+
</a>
|
|
23
|
+
</p>
|
|
15
24
|
</div>
|
|
16
25
|
</div>
|
|
17
26
|
<div
|
|
@@ -89,6 +98,15 @@ exports[`renders CompletionProviderStatus unchanged 1`] = `
|
|
|
89
98
|
<p>
|
|
90
99
|
completionProviderDown
|
|
91
100
|
</p>
|
|
101
|
+
<p>
|
|
102
|
+
<a
|
|
103
|
+
href="https://status.openai.com/"
|
|
104
|
+
rel="noopener noreferrer"
|
|
105
|
+
target="_blank"
|
|
106
|
+
>
|
|
107
|
+
completionProviderCheckStatusPage
|
|
108
|
+
</a>
|
|
109
|
+
</p>
|
|
92
110
|
</div>
|
|
93
111
|
</div>
|
|
94
112
|
<div
|