@sage-rsc/talking-head-react 1.0.52 → 1.0.53

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.52",
3
+ "version": "1.0.53",
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",
@@ -303,7 +303,7 @@ const CurriculumLearning = forwardRef(({
303
303
  const speakFirstQuestion = () => {
304
304
  if (!avatarRef.current || !firstQuestion) return;
305
305
 
306
- avatarRef.current.setMood("curious");
306
+ avatarRef.current.setMood("happy");
307
307
 
308
308
  // Play custom animation if available
309
309
  if (animations.questionStart) {
@@ -404,7 +404,7 @@ const CurriculumLearning = forwardRef(({
404
404
  avatarRef.current.speakText(`Now let's try this one: ${nextQuestionObj.question}`, {
405
405
  lipsyncLang: config.lipsyncLang
406
406
  });
407
- } else {
407
+ } else {
408
408
  avatarRef.current.speakText(`Here's the next question: ${nextQuestionObj.question}`, {
409
409
  lipsyncLang: config.lipsyncLang
410
410
  });
@@ -475,7 +475,7 @@ const CurriculumLearning = forwardRef(({
475
475
 
476
476
  if (avatarRef.current) {
477
477
  avatarRef.current.setMood("happy");
478
- avatarRef.current.setBodyMovement("idle");
478
+ avatarRef.current.setBodyMovement("idle");
479
479
  }
480
480
  } else {
481
481
  // No more lessons in current module - check if there's a next module
@@ -647,7 +647,7 @@ const CurriculumLearning = forwardRef(({
647
647
  if (animations.incorrect) {
648
648
  try {
649
649
  avatarRef.current.playAnimation(animations.incorrect, true);
650
- } catch (error) {
650
+ } catch (error) {
651
651
  avatarRef.current.setBodyMovement("idle");
652
652
  }
653
653
  }
@@ -732,6 +732,136 @@ const CurriculumLearning = forwardRef(({
732
732
  }
733
733
  }, [getCurrentQuestion, checkAnswer]);
734
734
 
735
+ // Move to previous question
736
+ const previousQuestion = useCallback(() => {
737
+ if (stateRef.current.currentQuestionIndex > 0) {
738
+ stateRef.current.currentQuestionIndex -= 1;
739
+
740
+ // Trigger custom action for UI update
741
+ const prevQuestionObj = getCurrentQuestion();
742
+ if (prevQuestionObj) {
743
+ callbacksRef.current.onCustomAction({
744
+ type: 'questionStart',
745
+ moduleIndex: stateRef.current.currentModuleIndex,
746
+ lessonIndex: stateRef.current.currentLessonIndex,
747
+ questionIndex: stateRef.current.currentQuestionIndex,
748
+ totalQuestions: stateRef.current.totalQuestions,
749
+ question: prevQuestionObj
750
+ });
751
+ }
752
+
753
+ // Function to speak the previous question
754
+ const speakPrevQuestion = () => {
755
+ if (!avatarRef.current || !prevQuestionObj) return;
756
+
757
+ avatarRef.current.setMood("happy");
758
+ avatarRef.current.setBodyMovement("idle");
759
+
760
+ const config = defaultAvatarConfigRef.current || { lipsyncLang: 'en' };
761
+
762
+ // Speak the question text
763
+ if (prevQuestionObj.type === 'code_test') {
764
+ avatarRef.current.speakText(`Let's go back to this coding challenge: ${prevQuestionObj.question}`, {
765
+ lipsyncLang: config.lipsyncLang
766
+ });
767
+ } else {
768
+ avatarRef.current.speakText(`Going back to: ${prevQuestionObj.question}`, {
769
+ lipsyncLang: config.lipsyncLang
770
+ });
771
+ }
772
+ };
773
+
774
+ // Try to speak immediately if avatar is ready
775
+ if (avatarRef.current && avatarRef.current.isReady && prevQuestionObj) {
776
+ speakPrevQuestion();
777
+ } else if (prevQuestionObj) {
778
+ // Avatar not ready yet - wait for it to be ready, then speak
779
+ const checkReady = setInterval(() => {
780
+ if (avatarRef.current && avatarRef.current.isReady) {
781
+ clearInterval(checkReady);
782
+ speakPrevQuestion();
783
+ }
784
+ }, 100);
785
+
786
+ setTimeout(() => {
787
+ clearInterval(checkReady);
788
+ }, 5000);
789
+ }
790
+ }
791
+ }, [getCurrentQuestion]);
792
+
793
+ // Move to previous lesson
794
+ const previousLesson = useCallback(() => {
795
+ const curriculum = curriculumRef.current || { modules: [] };
796
+ const currentModule = curriculum.modules[stateRef.current.currentModuleIndex];
797
+
798
+ // Check if there's a previous lesson in the current module
799
+ const hasPrevLessonInModule = stateRef.current.currentLessonIndex > 0;
800
+
801
+ if (hasPrevLessonInModule) {
802
+ // Move to previous lesson in current module
803
+ stateRef.current.currentLessonIndex -= 1;
804
+ stateRef.current.currentQuestionIndex = 0;
805
+ stateRef.current.lessonCompleted = false;
806
+ stateRef.current.isQuestionMode = false;
807
+ stateRef.current.isTeaching = false;
808
+ stateRef.current.score = 0;
809
+ stateRef.current.totalQuestions = 0;
810
+
811
+ // Notify parent that lesson has changed
812
+ callbacksRef.current.onCustomAction({
813
+ type: 'lessonStart',
814
+ moduleIndex: stateRef.current.currentModuleIndex,
815
+ lessonIndex: stateRef.current.currentLessonIndex
816
+ });
817
+
818
+ callbacksRef.current.onLessonStart({
819
+ moduleIndex: stateRef.current.currentModuleIndex,
820
+ lessonIndex: stateRef.current.currentLessonIndex,
821
+ lesson: getCurrentLesson()
822
+ });
823
+
824
+ if (avatarRef.current) {
825
+ avatarRef.current.setMood("happy");
826
+ avatarRef.current.setBodyMovement("idle");
827
+ }
828
+ } else {
829
+ // No previous lesson in current module - check if there's a previous module
830
+ const hasPrevModule = stateRef.current.currentModuleIndex > 0;
831
+
832
+ if (hasPrevModule) {
833
+ // Move to last lesson of previous module
834
+ const prevModule = curriculum.modules[stateRef.current.currentModuleIndex - 1];
835
+ stateRef.current.currentModuleIndex -= 1;
836
+ stateRef.current.currentLessonIndex = (prevModule?.lessons?.length || 1) - 1;
837
+ stateRef.current.currentQuestionIndex = 0;
838
+ stateRef.current.lessonCompleted = false;
839
+ stateRef.current.isQuestionMode = false;
840
+ stateRef.current.isTeaching = false;
841
+ stateRef.current.score = 0;
842
+ stateRef.current.totalQuestions = 0;
843
+
844
+ // Notify parent that lesson has changed
845
+ callbacksRef.current.onCustomAction({
846
+ type: 'lessonStart',
847
+ moduleIndex: stateRef.current.currentModuleIndex,
848
+ lessonIndex: stateRef.current.currentLessonIndex
849
+ });
850
+
851
+ callbacksRef.current.onLessonStart({
852
+ moduleIndex: stateRef.current.currentModuleIndex,
853
+ lessonIndex: stateRef.current.currentLessonIndex,
854
+ lesson: getCurrentLesson()
855
+ });
856
+
857
+ if (avatarRef.current) {
858
+ avatarRef.current.setMood("happy");
859
+ avatarRef.current.setBodyMovement("idle");
860
+ }
861
+ }
862
+ }
863
+ }, [getCurrentLesson]);
864
+
735
865
  // Reset curriculum
736
866
  const resetCurriculum = useCallback(() => {
737
867
  stateRef.current.currentModuleIndex = 0;
@@ -781,7 +911,9 @@ const CurriculumLearning = forwardRef(({
781
911
  handleAnswerSelect,
782
912
  handleCodeTestResult,
783
913
  nextQuestion,
914
+ previousQuestion,
784
915
  nextLesson,
916
+ previousLesson,
785
917
  completeLesson,
786
918
  completeCurriculum,
787
919
  resetCurriculum,