@sage-rsc/talking-head-react 1.0.64 → 1.0.66

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.64",
3
+ "version": "1.0.66",
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",
@@ -402,21 +402,38 @@ const CurriculumLearning = forwardRef(({
402
402
 
403
403
  const config = defaultAvatarConfigRef.current || { lipsyncLang: 'en' };
404
404
 
405
- // Speak the question text with proper introduction
405
+ // Check if this is the last question to adjust the introduction
406
+ const currentLesson = getCurrentLesson();
407
+ const totalQuestionsInLesson = currentLesson?.questions?.length || 0;
408
+ const isLastQuestion = stateRef.current.currentQuestionIndex >= totalQuestionsInLesson - 1;
409
+
410
+ // Speak the question text with proper introduction (no "next" if it's the last question)
406
411
  if (nextQuestionObj.type === 'code_test') {
407
- avatarRef.current.speakText(`Great! Now let's move on to your next coding challenge: ${nextQuestionObj.question}`, {
412
+ const intro = isLastQuestion
413
+ ? `Great! Here's your final coding challenge: ${nextQuestionObj.question}`
414
+ : `Great! Now let's move on to your next coding challenge: ${nextQuestionObj.question}`;
415
+ avatarRef.current.speakText(intro, {
408
416
  lipsyncLang: config.lipsyncLang
409
417
  });
410
418
  } else if (nextQuestionObj.type === 'multiple_choice') {
411
- avatarRef.current.speakText(`Alright! Here's your next question: ${nextQuestionObj.question}`, {
419
+ const intro = isLastQuestion
420
+ ? `Alright! Here's your final question: ${nextQuestionObj.question}`
421
+ : `Alright! Here's your next question: ${nextQuestionObj.question}`;
422
+ avatarRef.current.speakText(intro, {
412
423
  lipsyncLang: config.lipsyncLang
413
424
  });
414
425
  } else if (nextQuestionObj.type === 'true_false') {
415
- avatarRef.current.speakText(`Now let's try this one: ${nextQuestionObj.question}`, {
426
+ const intro = isLastQuestion
427
+ ? `Now let's try this final one: ${nextQuestionObj.question}`
428
+ : `Now let's try this one: ${nextQuestionObj.question}`;
429
+ avatarRef.current.speakText(intro, {
416
430
  lipsyncLang: config.lipsyncLang
417
431
  });
418
432
  } else {
419
- avatarRef.current.speakText(`Here's the next question: ${nextQuestionObj.question}`, {
433
+ const intro = isLastQuestion
434
+ ? `Here's your final question: ${nextQuestionObj.question}`
435
+ : `Here's the next question: ${nextQuestionObj.question}`;
436
+ avatarRef.current.speakText(intro, {
420
437
  lipsyncLang: config.lipsyncLang
421
438
  });
422
439
  }
@@ -649,6 +666,12 @@ const CurriculumLearning = forwardRef(({
649
666
  }
650
667
  }
651
668
  avatarRef.current.setBodyMovement("gesturing");
669
+
670
+ // Check if this is the last question
671
+ const currentLesson = getCurrentLesson();
672
+ const totalQuestionsInLesson = currentLesson?.questions?.length || 0;
673
+ const isLastQuestion = stateRef.current.currentQuestionIndex >= totalQuestionsInLesson - 1;
674
+
652
675
  const successMessage = currentQuestion.type === "code_test"
653
676
  ? `Great job! Your code passed all the tests! ${currentQuestion.explanation || ''}`
654
677
  : `Excellent! That's correct! ${currentQuestion.explanation || ''}`;
@@ -660,8 +683,6 @@ const CurriculumLearning = forwardRef(({
660
683
  lipsyncLang: config.lipsyncLang,
661
684
  onSpeechEnd: () => {
662
685
  // Notify parent that feedback is complete - parent decides next action
663
- const currentLesson = getCurrentLesson();
664
- const totalQuestionsInLesson = currentLesson?.questions?.length || 0;
665
686
  callbacksRef.current.onCustomAction({
666
687
  type: 'answerFeedbackComplete',
667
688
  moduleIndex: stateRef.current.currentModuleIndex,
@@ -684,9 +705,15 @@ const CurriculumLearning = forwardRef(({
684
705
  }
685
706
  }
686
707
  avatarRef.current.setBodyMovement("gesturing");
708
+
709
+ // Check if this is the last question
710
+ const currentLesson = getCurrentLesson();
711
+ const totalQuestionsInLesson = currentLesson?.questions?.length || 0;
712
+ const isLastQuestion = stateRef.current.currentQuestionIndex >= totalQuestionsInLesson - 1;
713
+
687
714
  const failureMessage = currentQuestion.type === "code_test"
688
715
  ? `Your code didn't pass all the tests. ${currentQuestion.explanation || 'Try again!'}`
689
- : `Not quite right, but don't worry! ${currentQuestion.explanation || ''} Let's move on to the next question.`;
716
+ : `Not quite right, but don't worry! ${currentQuestion.explanation || ''}${isLastQuestion ? '' : " Let's move on to the next question."}`;
690
717
 
691
718
  const config = defaultAvatarConfigRef.current || { lipsyncLang: 'en' };
692
719
 
@@ -695,8 +722,6 @@ const CurriculumLearning = forwardRef(({
695
722
  lipsyncLang: config.lipsyncLang,
696
723
  onSpeechEnd: () => {
697
724
  // Notify parent that feedback is complete - parent decides next action
698
- const currentLesson = getCurrentLesson();
699
- const totalQuestionsInLesson = currentLesson?.questions?.length || 0;
700
725
  callbacksRef.current.onCustomAction({
701
726
  type: 'answerFeedbackComplete',
702
727
  moduleIndex: stateRef.current.currentModuleIndex,
@@ -444,9 +444,17 @@ const TalkingHeadAvatar = forwardRef(({
444
444
  let remainingText = '';
445
445
  if (talkingHead.speechQueue && talkingHead.speechQueue.length > 0 && pausedSpeechRef.current) {
446
446
  // Extract text from remaining queue items
447
+ // Note: item.text is an array of objects like [{mark: 0, word: "Hello"}, {mark: 1, word: "world"}]
447
448
  const remainingParts = talkingHead.speechQueue
448
- .filter(item => item && item.text) // Only get items with text
449
- .map(item => item.text)
449
+ .filter(item => item && item.text && Array.isArray(item.text) && item.text.length > 0)
450
+ .map(item => {
451
+ // Extract words from the text array
452
+ return item.text
453
+ .map(wordObj => wordObj.word || '')
454
+ .filter(word => word.length > 0)
455
+ .join(' ');
456
+ })
457
+ .filter(text => text.length > 0)
450
458
  .join(' ');
451
459
 
452
460
  if (remainingParts && remainingParts.trim()) {