@sage-rsc/talking-head-react 1.2.2 → 1.2.3
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/dist/index.cjs +2 -2
- package/dist/index.js +725 -701
- package/package.json +1 -1
- package/src/components/SimpleTalkingAvatar.jsx +57 -2
package/package.json
CHANGED
|
@@ -27,7 +27,10 @@ import { getActiveTTSConfig, ELEVENLABS_CONFIG, DEEPGRAM_CONFIG } from '../confi
|
|
|
27
27
|
* @param {Function} props.onSpeechEnd - Callback when speech ends
|
|
28
28
|
* @param {string} props.className - Additional CSS classes
|
|
29
29
|
* @param {Object} props.style - Additional inline styles
|
|
30
|
-
* @param {Object} props.animations - Object mapping animation names to FBX file paths
|
|
30
|
+
* @param {Object} props.animations - Object mapping animation names to FBX file paths, or animation groups
|
|
31
|
+
* Can be: { "dance": "/animations/dance.fbx" } (single animation)
|
|
32
|
+
* Or: { "talking": ["/animations/talk1.fbx", "/animations/talk2.fbx"] } (group)
|
|
33
|
+
* @param {string} props.autoAnimationGroup - Animation group to automatically play when speaking (e.g., "talking")
|
|
31
34
|
* @param {boolean} props.autoSpeak - Whether to automatically speak the text prop when ready
|
|
32
35
|
* @param {Object} ref - Ref to access component methods
|
|
33
36
|
*/
|
|
@@ -51,6 +54,7 @@ const SimpleTalkingAvatar = forwardRef(({
|
|
|
51
54
|
className = "",
|
|
52
55
|
style = {},
|
|
53
56
|
animations = {},
|
|
57
|
+
autoAnimationGroup = null, // e.g., "talking" - will randomly select from this group when speaking
|
|
54
58
|
autoSpeak = false
|
|
55
59
|
}, ref) => {
|
|
56
60
|
const containerRef = useRef(null);
|
|
@@ -220,6 +224,44 @@ const SimpleTalkingAvatar = forwardRef(({
|
|
|
220
224
|
}
|
|
221
225
|
}, []);
|
|
222
226
|
|
|
227
|
+
// Helper function to get random animation from a group
|
|
228
|
+
const getRandomAnimation = useCallback((groupName) => {
|
|
229
|
+
if (!animations || !animations[groupName]) {
|
|
230
|
+
return null;
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
const group = animations[groupName];
|
|
234
|
+
|
|
235
|
+
// If it's an array, randomly select one
|
|
236
|
+
if (Array.isArray(group) && group.length > 0) {
|
|
237
|
+
const randomIndex = Math.floor(Math.random() * group.length);
|
|
238
|
+
return group[randomIndex];
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
// If it's a string, return it directly
|
|
242
|
+
if (typeof group === 'string') {
|
|
243
|
+
return group;
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
return null;
|
|
247
|
+
}, [animations]);
|
|
248
|
+
|
|
249
|
+
// Helper function to play random animation from a group
|
|
250
|
+
const playRandomAnimation = useCallback((groupName, disablePositionLock = false) => {
|
|
251
|
+
const animationPath = getRandomAnimation(groupName);
|
|
252
|
+
if (animationPath && talkingHeadRef.current) {
|
|
253
|
+
try {
|
|
254
|
+
talkingHeadRef.current.playAnimation(animationPath, null, 10, 0, 0.01, disablePositionLock);
|
|
255
|
+
console.log(`Playing random animation from "${groupName}" group:`, animationPath);
|
|
256
|
+
return animationPath;
|
|
257
|
+
} catch (error) {
|
|
258
|
+
console.warn(`Failed to play random animation from "${groupName}" group:`, error);
|
|
259
|
+
return null;
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
return null;
|
|
263
|
+
}, [getRandomAnimation]);
|
|
264
|
+
|
|
223
265
|
// Speak text with proper callback handling
|
|
224
266
|
const speakText = useCallback(async (textToSpeak, options = {}) => {
|
|
225
267
|
if (!talkingHeadRef.current || !isReady) {
|
|
@@ -235,6 +277,13 @@ const SimpleTalkingAvatar = forwardRef(({
|
|
|
235
277
|
// Always resume audio context first (required for user interaction)
|
|
236
278
|
await resumeAudioContext();
|
|
237
279
|
|
|
280
|
+
// Play random animation from autoAnimationGroup if specified
|
|
281
|
+
// Check both autoAnimationGroup prop and options.animationGroup
|
|
282
|
+
const animationGroup = options.animationGroup || autoAnimationGroup;
|
|
283
|
+
if (animationGroup && !options.skipAnimation) {
|
|
284
|
+
playRandomAnimation(animationGroup);
|
|
285
|
+
}
|
|
286
|
+
|
|
238
287
|
// Reset speech progress tracking
|
|
239
288
|
speechProgressRef.current = { remainingText: null, originalText: null, options: null };
|
|
240
289
|
originalSentencesRef.current = [];
|
|
@@ -279,7 +328,7 @@ const SimpleTalkingAvatar = forwardRef(({
|
|
|
279
328
|
console.error('Error speaking text:', err);
|
|
280
329
|
setError(err.message || 'Failed to speak text');
|
|
281
330
|
}
|
|
282
|
-
}, [isReady, onSpeechEnd, resumeAudioContext]);
|
|
331
|
+
}, [isReady, onSpeechEnd, resumeAudioContext, autoAnimationGroup, playRandomAnimation]);
|
|
283
332
|
|
|
284
333
|
// Auto-speak text when ready and autoSpeak is true
|
|
285
334
|
useEffect(() => {
|
|
@@ -394,6 +443,12 @@ const SimpleTalkingAvatar = forwardRef(({
|
|
|
394
443
|
talkingHeadRef.current.playAnimation(animationName, null, 10, 0, 0.01, disablePositionLock);
|
|
395
444
|
}
|
|
396
445
|
},
|
|
446
|
+
playRandomAnimation: (groupName, disablePositionLock = false) => {
|
|
447
|
+
return playRandomAnimation(groupName, disablePositionLock);
|
|
448
|
+
},
|
|
449
|
+
getRandomAnimation: (groupName) => {
|
|
450
|
+
return getRandomAnimation(groupName);
|
|
451
|
+
},
|
|
397
452
|
playReaction: (reactionType) => talkingHeadRef.current?.playReaction(reactionType),
|
|
398
453
|
playCelebration: () => talkingHeadRef.current?.playCelebration(),
|
|
399
454
|
setShowFullAvatar: (show) => {
|