@sage-rsc/talking-head-react 1.0.29 → 1.0.30

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 CHANGED
@@ -1 +1 @@
1
- "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const i=require("./index-C3A1_CcE.cjs");exports.CurriculumLearning=i.CurriculumLearning;exports.TalkingHeadAvatar=i.TalkingHeadAvatar;exports.TalkingHeadComponent=i.TalkingHeadComponent;exports.animations=i.animations;exports.getActiveTTSConfig=i.getActiveTTSConfig;exports.getAnimation=i.getAnimation;exports.getVoiceOptions=i.getVoiceOptions;exports.hasAnimation=i.hasAnimation;
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const i=require("./index-BFQNNYmP.cjs");exports.CurriculumLearning=i.CurriculumLearning;exports.TalkingHeadAvatar=i.TalkingHeadAvatar;exports.TalkingHeadComponent=i.TalkingHeadComponent;exports.animations=i.animations;exports.getActiveTTSConfig=i.getActiveTTSConfig;exports.getAnimation=i.getAnimation;exports.getVoiceOptions=i.getVoiceOptions;exports.hasAnimation=i.hasAnimation;
package/dist/index.js CHANGED
@@ -1,4 +1,4 @@
1
- import { C as n, T as s, a as t, d as e, b as o, g, c as m, h as r } from "./index-BvDk1TRw.js";
1
+ import { C as n, T as s, a as t, d as e, b as o, g, c as m, h as r } from "./index-V_d1NQ-5.js";
2
2
  export {
3
3
  n as CurriculumLearning,
4
4
  s as TalkingHeadAvatar,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sage-rsc/talking-head-react",
3
- "version": "1.0.29",
3
+ "version": "1.0.30",
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",
@@ -1,4 +1,4 @@
1
- import React, { useRef, useEffect, forwardRef, useImperativeHandle, useCallback } from 'react';
1
+ import React, { useRef, useEffect, useLayoutEffect, forwardRef, useImperativeHandle, useCallback } from 'react';
2
2
  import TalkingHeadAvatar from './TalkingHeadAvatar';
3
3
 
4
4
  /**
@@ -55,6 +55,9 @@ const CurriculumLearning = forwardRef(({
55
55
  const startTeachingRef = useRef(null);
56
56
  const nextLessonRef = useRef(null);
57
57
  const completeLessonRef = useRef(null);
58
+ const nextQuestionRef = useRef(null);
59
+ const completeCurriculumRef = useRef(null);
60
+ const startQuestionsRef = useRef(null);
58
61
 
59
62
  // Update callbacks ref when they change
60
63
  useEffect(() => {
@@ -127,7 +130,7 @@ const CurriculumLearning = forwardRef(({
127
130
  let feedbackMessage = `Congratulations! You've completed this lesson`;
128
131
  if (stateRef.current.totalQuestions > 0) {
129
132
  feedbackMessage += ` with a score of ${stateRef.current.score} out of ${stateRef.current.totalQuestions} (${percentage}%). `;
130
- } else {
133
+ } else {
131
134
  feedbackMessage += `! `;
132
135
  }
133
136
 
@@ -185,23 +188,22 @@ const CurriculumLearning = forwardRef(({
185
188
  }, 1000);
186
189
  }
187
190
  });
188
- } else {
191
+ } else {
189
192
  // This is the last lesson, complete curriculum instead
190
193
  avatarRef.current.speakText(feedbackMessage, {
191
194
  lipsyncLang: defaultAvatarConfig.lipsyncLang,
192
195
  onSpeechEnd: () => {
193
196
  // Add a small delay after speech ends for natural flow
194
197
  setTimeout(() => {
195
- completeCurriculum();
198
+ if (completeCurriculumRef.current) {
199
+ completeCurriculumRef.current();
200
+ }
196
201
  }, 1000);
197
202
  }
198
203
  });
199
204
  }
200
205
  }
201
- }, [animations.lessonComplete, curriculum, completeCurriculum, defaultAvatarConfig]);
202
-
203
- // Assign ref immediately after function is defined
204
- completeLessonRef.current = completeLesson;
206
+ }, [animations.lessonComplete, curriculum, defaultAvatarConfig]);
205
207
 
206
208
  // Complete entire curriculum
207
209
  const completeCurriculum = useCallback(() => {
@@ -211,8 +213,8 @@ const CurriculumLearning = forwardRef(({
211
213
  modules: curriculum.modules.length,
212
214
  totalLessons: curriculum.modules.reduce((sum, mod) => sum + mod.lessons.length, 0)
213
215
  });
214
-
215
- if (avatarRef.current) {
216
+
217
+ if (avatarRef.current) {
216
218
  avatarRef.current.setMood("celebrating");
217
219
  if (animations.curriculumComplete) {
218
220
  try {
@@ -265,7 +267,7 @@ const CurriculumLearning = forwardRef(({
265
267
  avatarRef.current.speakText(`Now let me ask you some questions. Here's the first one: ${firstQuestion.question}`, { lipsyncLang: defaultAvatarConfig.lipsyncLang });
266
268
  } else if (firstQuestion.type === 'true_false') {
267
269
  avatarRef.current.speakText(`Let's start with some true or false questions. First question: ${firstQuestion.question}`, { lipsyncLang: defaultAvatarConfig.lipsyncLang });
268
- } else {
270
+ } else {
269
271
  avatarRef.current.speakText(`Now let me ask you some questions. Here's the first one: ${firstQuestion.question}`, { lipsyncLang: defaultAvatarConfig.lipsyncLang });
270
272
  }
271
273
  } else if (avatarRef.current) {
@@ -294,14 +296,14 @@ const CurriculumLearning = forwardRef(({
294
296
  }
295
297
 
296
298
  if (avatarRef.current && nextQuestionObj) {
297
- avatarRef.current.setMood("happy");
299
+ avatarRef.current.setMood("happy");
298
300
  avatarRef.current.setBodyMovement("idle");
299
301
 
300
302
  // Play custom animation if available
301
303
  if (animations.nextQuestion) {
302
- try {
304
+ try {
303
305
  avatarRef.current.playAnimation(animations.nextQuestion, true);
304
- } catch (error) {
306
+ } catch (error) {
305
307
  console.warn('Failed to play nextQuestion animation:', error);
306
308
  }
307
309
  }
@@ -364,12 +366,11 @@ const CurriculumLearning = forwardRef(({
364
366
  }, 500);
365
367
  }
366
368
  } else {
367
- completeCurriculum();
369
+ if (completeCurriculumRef.current) {
370
+ completeCurriculumRef.current();
371
+ }
368
372
  }
369
- }, [curriculum, completeCurriculum]);
370
-
371
- // Assign ref immediately after function is defined
372
- nextLessonRef.current = nextLesson;
373
+ }, [curriculum]);
373
374
 
374
375
  // Start teaching the lesson
375
376
  const startTeaching = useCallback(() => {
@@ -415,7 +416,10 @@ const CurriculumLearning = forwardRef(({
415
416
  setTimeout(() => {
416
417
  stateRef.current.isTeaching = false;
417
418
  if (currentLesson.questions && currentLesson.questions.length > 0) {
418
- startQuestions();
419
+ // Use ref to avoid circular dependency
420
+ if (startQuestionsRef.current) {
421
+ startQuestionsRef.current();
422
+ }
419
423
  } else {
420
424
  // No questions, complete the lesson using ref to avoid circular dependency
421
425
  if (completeLessonRef.current) {
@@ -424,10 +428,7 @@ const CurriculumLearning = forwardRef(({
424
428
  }
425
429
  }, 8000);
426
430
  }
427
- }, [animations.teaching, getCurrentLesson, startQuestions, defaultAvatarConfig]);
428
-
429
- // Assign ref immediately after function is defined
430
- startTeachingRef.current = startTeaching;
431
+ }, [animations.teaching, getCurrentLesson, defaultAvatarConfig]);
431
432
 
432
433
  // Handle answer selection
433
434
  const handleAnswerSelect = useCallback((answer) => {
@@ -468,7 +469,9 @@ const CurriculumLearning = forwardRef(({
468
469
  onSpeechEnd: () => {
469
470
  // Add a small delay after speech ends for natural flow
470
471
  setTimeout(() => {
471
- nextQuestion();
472
+ if (nextQuestionRef.current) {
473
+ nextQuestionRef.current();
474
+ }
472
475
  }, 500);
473
476
  }
474
477
  });
@@ -492,16 +495,20 @@ const CurriculumLearning = forwardRef(({
492
495
  onSpeechEnd: () => {
493
496
  // Add a small delay after speech ends for natural flow
494
497
  setTimeout(() => {
495
- nextQuestion();
498
+ if (nextQuestionRef.current) {
499
+ nextQuestionRef.current();
500
+ }
496
501
  }, 500);
497
502
  }
498
503
  });
499
504
  }
500
505
  } else {
501
506
  // If avatar not ready, move to next question immediately
502
- nextQuestion();
507
+ if (nextQuestionRef.current) {
508
+ nextQuestionRef.current();
509
+ }
503
510
  }
504
- }, [animations.correct, animations.incorrect, getCurrentQuestion, checkAnswer, nextQuestion, defaultAvatarConfig]);
511
+ }, [animations.correct, animations.incorrect, getCurrentQuestion, checkAnswer, defaultAvatarConfig]);
505
512
 
506
513
  // Handle code test result submission
507
514
  const handleCodeTestResult = useCallback((testResult) => {
@@ -542,8 +549,10 @@ const CurriculumLearning = forwardRef(({
542
549
  });
543
550
 
544
551
  // Handle answer using the same logic as regular questions
545
- handleAnswerSelect(codeTestAnswer);
546
- }, [getCurrentQuestion, handleAnswerSelect]);
552
+ if (handleAnswerSelectRef.current) {
553
+ handleAnswerSelectRef.current(codeTestAnswer);
554
+ }
555
+ }, [getCurrentQuestion, checkAnswer, defaultAvatarConfig]);
547
556
 
548
557
  // Reset curriculum
549
558
  const resetCurriculum = useCallback(() => {
@@ -571,6 +580,18 @@ const CurriculumLearning = forwardRef(({
571
580
  }
572
581
  }, [autoStart, getCurrentLesson]);
573
582
 
583
+ // Set refs after all functions are defined - use useLayoutEffect for synchronous execution
584
+ // This ensures refs are set before React commits the render, avoiding initialization errors
585
+ useLayoutEffect(() => {
586
+ startTeachingRef.current = startTeaching;
587
+ nextLessonRef.current = nextLesson;
588
+ completeLessonRef.current = completeLesson;
589
+ nextQuestionRef.current = nextQuestion;
590
+ completeCurriculumRef.current = completeCurriculum;
591
+ startQuestionsRef.current = startQuestions;
592
+ handleAnswerSelectRef.current = handleAnswerSelect;
593
+ });
594
+
574
595
  // Expose methods via ref (for external control)
575
596
  useImperativeHandle(ref, () => ({
576
597
  // Curriculum control methods
@@ -3248,7 +3248,7 @@ class TalkingHead {
3248
3248
  const module = LIPSYNC_MODULES[langLower];
3249
3249
 
3250
3250
  if (module && module[className]) {
3251
- this.lipsync[lang] = new module[className];
3251
+ this.lipsync[lang] = new module[className];
3252
3252
  console.log(`Loaded lip-sync module for ${lang}`);
3253
3253
  } else {
3254
3254
  console.warn(`Lip-sync module for ${lang} not found. Available modules:`, Object.keys(LIPSYNC_MODULES));