@sage-rsc/talking-head-react 1.0.57 → 1.0.59

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sage-rsc/talking-head-react",
3
- "version": "1.0.57",
3
+ "version": "1.0.59",
4
4
  "description": "A reusable React component for 3D talking avatars with lip-sync and text-to-speech",
5
5
  "main": "./dist/index.cjs",
6
6
  "module": "./dist/index.js",
@@ -185,7 +185,11 @@ const CurriculumLearning = forwardRef(({
185
185
  : 100;
186
186
  let feedbackMessage = `Congratulations! You've completed this lesson`;
187
187
  if (stateRef.current.totalQuestions > 0) {
188
- feedbackMessage += ` with a score of ${stateRef.current.score} out of ${stateRef.current.totalQuestions} (${percentage}%). `;
188
+ // Rephrase for clearer TTS: "You got X correct out of Y questions, which is Z percent"
189
+ const scoreText = stateRef.current.score === 1 ? 'one' : stateRef.current.score;
190
+ const totalText = stateRef.current.totalQuestions === 1 ? 'one' : stateRef.current.totalQuestions;
191
+ const percentText = percentage === 50 ? 'fifty' : `${percentage}`;
192
+ feedbackMessage += ` You got ${scoreText} correct out of ${totalText} question${stateRef.current.totalQuestions === 1 ? '' : 's'}, which is ${percentText} percent. `;
189
193
  } else {
190
194
  feedbackMessage += `! `;
191
195
  }
@@ -994,7 +998,12 @@ const CurriculumLearning = forwardRef(({
994
998
  stopSpeaking: () => avatarRef.current?.stopSpeaking(),
995
999
  pauseSpeaking: () => avatarRef.current?.pauseSpeaking(),
996
1000
  resumeSpeaking: async () => await avatarRef.current?.resumeSpeaking(),
997
- isPaused: () => avatarRef.current?.isPaused || false,
1001
+ isPaused: () => {
1002
+ if (avatarRef.current && typeof avatarRef.current.isPaused !== 'undefined') {
1003
+ return avatarRef.current.isPaused;
1004
+ }
1005
+ return false;
1006
+ },
998
1007
  setMood: (mood) => avatarRef.current?.setMood(mood),
999
1008
  playAnimation: (animationName, disablePositionLock) => avatarRef.current?.playAnimation(animationName, disablePositionLock),
1000
1009
  setBodyMovement: (movement) => avatarRef.current?.setBodyMovement(movement),
@@ -399,8 +399,19 @@ const TalkingHeadAvatar = forwardRef(({
399
399
 
400
400
  const pauseSpeaking = useCallback(() => {
401
401
  if (talkingHeadRef.current && talkingHeadRef.current.pauseSpeaking) {
402
- // Only pause if we have paused speech stored (meaning something is currently speaking)
403
- if (pausedSpeechRef.current && pausedSpeechRef.current.text) {
402
+ // Check if avatar is currently speaking
403
+ const talkingHead = talkingHeadRef.current;
404
+ const isCurrentlySpeaking = talkingHead.isSpeaking ||
405
+ (talkingHead.audioPlaylist && talkingHead.audioPlaylist.length > 0) ||
406
+ (talkingHead.speechQueue && talkingHead.speechQueue.length > 0);
407
+
408
+ // Only pause if avatar is actually speaking
409
+ if (isCurrentlySpeaking && pausedSpeechRef.current && pausedSpeechRef.current.text) {
410
+ talkingHeadRef.current.pauseSpeaking();
411
+ setIsPaused(true);
412
+ } else if (isCurrentlySpeaking) {
413
+ // If speaking but no paused speech stored, try to pause anyway
414
+ // This handles cases where speech started before we could track it
404
415
  talkingHeadRef.current.pauseSpeaking();
405
416
  setIsPaused(true);
406
417
  }
@@ -408,16 +419,17 @@ const TalkingHeadAvatar = forwardRef(({
408
419
  }, []);
409
420
 
410
421
  const resumeSpeaking = useCallback(async () => {
411
- if (talkingHeadRef.current && pausedSpeechRef.current) {
422
+ if (talkingHeadRef.current && isPaused) {
412
423
  setIsPaused(false);
413
- const pausedSpeech = pausedSpeechRef.current;
414
- pausedSpeechRef.current = null;
415
424
 
416
425
  // Resume audio context
417
426
  await resumeAudioContext();
418
427
 
419
- // Re-speak the paused text
420
- if (pausedSpeech.text) {
428
+ // Re-speak the paused text if we have it stored
429
+ if (pausedSpeechRef.current && pausedSpeechRef.current.text) {
430
+ const pausedSpeech = pausedSpeechRef.current;
431
+ pausedSpeechRef.current = null; // Clear after getting the text
432
+
421
433
  const speakOptions = {
422
434
  ...pausedSpeech.options,
423
435
  lipsyncLang: pausedSpeech.options.lipsyncLang || defaultAvatarConfig.lipsyncLang || 'en'
@@ -429,9 +441,12 @@ const TalkingHeadAvatar = forwardRef(({
429
441
  }
430
442
  talkingHeadRef.current.speakText(pausedSpeech.text, speakOptions);
431
443
  }
444
+ } else {
445
+ // If no paused speech stored, just clear the pause state
446
+ pausedSpeechRef.current = null;
432
447
  }
433
448
  }
434
- }, [resumeAudioContext]);
449
+ }, [resumeAudioContext, isPaused]);
435
450
 
436
451
  const setMood = useCallback((mood) => {
437
452
  if (talkingHeadRef.current) {
@@ -517,7 +532,7 @@ const TalkingHeadAvatar = forwardRef(({
517
532
  setTimingAdjustment,
518
533
  playAnimation,
519
534
  isReady,
520
- isPaused,
535
+ isPaused: isPaused,
521
536
  talkingHead: talkingHeadRef.current,
522
537
  handleResize,
523
538
  setBodyMovement: (movement) => {