@sage-rsc/talking-head-react 1.0.28 → 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-Dx7mDKJm.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-zMY6cyU2.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.28",
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,20 +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]);
206
+ }, [animations.lessonComplete, curriculum, defaultAvatarConfig]);
202
207
 
203
208
  // Complete entire curriculum
204
209
  const completeCurriculum = useCallback(() => {
@@ -208,8 +213,8 @@ const CurriculumLearning = forwardRef(({
208
213
  modules: curriculum.modules.length,
209
214
  totalLessons: curriculum.modules.reduce((sum, mod) => sum + mod.lessons.length, 0)
210
215
  });
211
-
212
- if (avatarRef.current) {
216
+
217
+ if (avatarRef.current) {
213
218
  avatarRef.current.setMood("celebrating");
214
219
  if (animations.curriculumComplete) {
215
220
  try {
@@ -262,7 +267,7 @@ const CurriculumLearning = forwardRef(({
262
267
  avatarRef.current.speakText(`Now let me ask you some questions. Here's the first one: ${firstQuestion.question}`, { lipsyncLang: defaultAvatarConfig.lipsyncLang });
263
268
  } else if (firstQuestion.type === 'true_false') {
264
269
  avatarRef.current.speakText(`Let's start with some true or false questions. First question: ${firstQuestion.question}`, { lipsyncLang: defaultAvatarConfig.lipsyncLang });
265
- } else {
270
+ } else {
266
271
  avatarRef.current.speakText(`Now let me ask you some questions. Here's the first one: ${firstQuestion.question}`, { lipsyncLang: defaultAvatarConfig.lipsyncLang });
267
272
  }
268
273
  } else if (avatarRef.current) {
@@ -291,14 +296,14 @@ const CurriculumLearning = forwardRef(({
291
296
  }
292
297
 
293
298
  if (avatarRef.current && nextQuestionObj) {
294
- avatarRef.current.setMood("happy");
299
+ avatarRef.current.setMood("happy");
295
300
  avatarRef.current.setBodyMovement("idle");
296
301
 
297
302
  // Play custom animation if available
298
303
  if (animations.nextQuestion) {
299
- try {
304
+ try {
300
305
  avatarRef.current.playAnimation(animations.nextQuestion, true);
301
- } catch (error) {
306
+ } catch (error) {
302
307
  console.warn('Failed to play nextQuestion animation:', error);
303
308
  }
304
309
  }
@@ -323,9 +328,12 @@ const CurriculumLearning = forwardRef(({
323
328
  }
324
329
  }
325
330
  } else {
326
- completeLesson();
331
+ // Use ref to avoid circular dependency
332
+ if (completeLessonRef.current) {
333
+ completeLessonRef.current();
334
+ }
327
335
  }
328
- }, [animations.nextQuestion, getCurrentLesson, completeLesson, getCurrentQuestion, defaultAvatarConfig]);
336
+ }, [animations.nextQuestion, getCurrentLesson, getCurrentQuestion, defaultAvatarConfig]);
329
337
 
330
338
  // Move to next lesson
331
339
  const nextLesson = useCallback(() => {
@@ -358,9 +366,11 @@ const CurriculumLearning = forwardRef(({
358
366
  }, 500);
359
367
  }
360
368
  } else {
361
- completeCurriculum();
369
+ if (completeCurriculumRef.current) {
370
+ completeCurriculumRef.current();
371
+ }
362
372
  }
363
- }, [curriculum, completeCurriculum]);
373
+ }, [curriculum]);
364
374
 
365
375
  // Start teaching the lesson
366
376
  const startTeaching = useCallback(() => {
@@ -406,7 +416,10 @@ const CurriculumLearning = forwardRef(({
406
416
  setTimeout(() => {
407
417
  stateRef.current.isTeaching = false;
408
418
  if (currentLesson.questions && currentLesson.questions.length > 0) {
409
- startQuestions();
419
+ // Use ref to avoid circular dependency
420
+ if (startQuestionsRef.current) {
421
+ startQuestionsRef.current();
422
+ }
410
423
  } else {
411
424
  // No questions, complete the lesson using ref to avoid circular dependency
412
425
  if (completeLessonRef.current) {
@@ -415,7 +428,7 @@ const CurriculumLearning = forwardRef(({
415
428
  }
416
429
  }, 8000);
417
430
  }
418
- }, [animations.teaching, getCurrentLesson, startQuestions, defaultAvatarConfig]);
431
+ }, [animations.teaching, getCurrentLesson, defaultAvatarConfig]);
419
432
 
420
433
  // Handle answer selection
421
434
  const handleAnswerSelect = useCallback((answer) => {
@@ -456,7 +469,9 @@ const CurriculumLearning = forwardRef(({
456
469
  onSpeechEnd: () => {
457
470
  // Add a small delay after speech ends for natural flow
458
471
  setTimeout(() => {
459
- nextQuestion();
472
+ if (nextQuestionRef.current) {
473
+ nextQuestionRef.current();
474
+ }
460
475
  }, 500);
461
476
  }
462
477
  });
@@ -480,16 +495,20 @@ const CurriculumLearning = forwardRef(({
480
495
  onSpeechEnd: () => {
481
496
  // Add a small delay after speech ends for natural flow
482
497
  setTimeout(() => {
483
- nextQuestion();
498
+ if (nextQuestionRef.current) {
499
+ nextQuestionRef.current();
500
+ }
484
501
  }, 500);
485
502
  }
486
503
  });
487
504
  }
488
505
  } else {
489
506
  // If avatar not ready, move to next question immediately
490
- nextQuestion();
507
+ if (nextQuestionRef.current) {
508
+ nextQuestionRef.current();
509
+ }
491
510
  }
492
- }, [animations.correct, animations.incorrect, getCurrentQuestion, checkAnswer, nextQuestion, defaultAvatarConfig]);
511
+ }, [animations.correct, animations.incorrect, getCurrentQuestion, checkAnswer, defaultAvatarConfig]);
493
512
 
494
513
  // Handle code test result submission
495
514
  const handleCodeTestResult = useCallback((testResult) => {
@@ -530,8 +549,10 @@ const CurriculumLearning = forwardRef(({
530
549
  });
531
550
 
532
551
  // Handle answer using the same logic as regular questions
533
- handleAnswerSelect(codeTestAnswer);
534
- }, [getCurrentQuestion, handleAnswerSelect]);
552
+ if (handleAnswerSelectRef.current) {
553
+ handleAnswerSelectRef.current(codeTestAnswer);
554
+ }
555
+ }, [getCurrentQuestion, checkAnswer, defaultAvatarConfig]);
535
556
 
536
557
  // Reset curriculum
537
558
  const resetCurriculum = useCallback(() => {
@@ -546,23 +567,30 @@ const CurriculumLearning = forwardRef(({
546
567
  stateRef.current.totalQuestions = 0;
547
568
  }, []);
548
569
 
549
- // Handle avatar ready callback
570
+ // Handle avatar ready callback - use ref to avoid circular dependency
550
571
  const handleAvatarReady = useCallback((talkingHead) => {
551
572
  console.log('Avatar is ready!', talkingHead);
552
573
  const currentLesson = getCurrentLesson();
553
574
  if (autoStart && currentLesson?.avatar_script) {
554
575
  setTimeout(() => {
555
- startTeaching();
576
+ if (startTeachingRef.current) {
577
+ startTeachingRef.current();
578
+ }
556
579
  }, 1000);
557
580
  }
558
- }, [autoStart, getCurrentLesson, startTeaching]);
581
+ }, [autoStart, getCurrentLesson]);
559
582
 
560
- // Update refs after all functions are defined to avoid circular dependency issues
561
- useEffect(() => {
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(() => {
562
586
  startTeachingRef.current = startTeaching;
563
587
  nextLessonRef.current = nextLesson;
564
588
  completeLessonRef.current = completeLesson;
565
- }, [startTeaching, nextLesson, completeLesson]);
589
+ nextQuestionRef.current = nextQuestion;
590
+ completeCurriculumRef.current = completeCurriculum;
591
+ startQuestionsRef.current = startQuestions;
592
+ handleAnswerSelectRef.current = handleAnswerSelect;
593
+ });
566
594
 
567
595
  // Expose methods via ref (for external control)
568
596
  useImperativeHandle(ref, () => ({
@@ -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));