@sage-rsc/talking-head-react 1.0.31 → 1.0.33

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-WXvj5jje.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-CdCA-KAp.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-iUfJQ70v.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-Dd7BPF7g.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.31",
3
+ "version": "1.0.33",
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",
@@ -55,15 +55,15 @@
55
55
  },
56
56
  "devDependencies": {
57
57
  "@eslint/js": "^9.36.0",
58
- "@types/react": "^19.1.16",
59
- "@types/react-dom": "^19.1.9",
58
+ "@types/react": "^18.3.0",
59
+ "@types/react-dom": "^18.3.0",
60
60
  "@vitejs/plugin-react": "^5.0.4",
61
61
  "eslint": "^9.36.0",
62
62
  "eslint-plugin-react-hooks": "^5.2.0",
63
63
  "eslint-plugin-react-refresh": "^0.4.22",
64
64
  "globals": "^16.4.0",
65
- "react": "^19.1.1",
66
- "react-dom": "^19.1.1",
65
+ "react": "^18.3.0",
66
+ "react-dom": "^18.3.0",
67
67
  "vite": "^7.1.7"
68
68
  }
69
69
  }
@@ -194,15 +194,17 @@ const CurriculumLearning = forwardRef(({
194
194
  if (animations.lessonComplete) {
195
195
  try {
196
196
  avatarRef.current.playAnimation(animations.lessonComplete, true);
197
- } catch (error) {
197
+ } catch (error) {
198
198
  avatarRef.current.playCelebration();
199
199
  }
200
200
  }
201
201
 
202
- // Check if there's a next lesson available
202
+ // Check if there's a next lesson available (either in current module or next module)
203
203
  const curriculum = curriculumRef.current || { modules: [] };
204
204
  const currentModule = curriculum.modules[stateRef.current.currentModuleIndex];
205
- const hasNextLesson = stateRef.current.currentLessonIndex < (currentModule?.lessons?.length || 0) - 1;
205
+ const hasNextLessonInModule = stateRef.current.currentLessonIndex < (currentModule?.lessons?.length || 0) - 1;
206
+ const hasNextModule = stateRef.current.currentModuleIndex < (curriculum.modules?.length || 0) - 1;
207
+ const hasNextLesson = hasNextLessonInModule || hasNextModule;
206
208
 
207
209
  const config = defaultAvatarConfigRef.current || { lipsyncLang: 'en' };
208
210
 
@@ -212,7 +214,7 @@ const CurriculumLearning = forwardRef(({
212
214
  lipsyncLang: config.lipsyncLang,
213
215
  onSpeechEnd: () => {
214
216
  // Add a small delay after speech ends for natural flow
215
- setTimeout(() => {
217
+ setTimeout(() => {
216
218
  // Use ref to avoid circular dependency
217
219
  if (nextLessonRef.current) {
218
220
  nextLessonRef.current();
@@ -220,8 +222,8 @@ const CurriculumLearning = forwardRef(({
220
222
  }, 1000);
221
223
  }
222
224
  });
223
- } else {
224
- // This is the last lesson, complete curriculum instead
225
+ } else {
226
+ // This is the last lesson in the last module, complete curriculum instead
225
227
  avatarRef.current.speakText(feedbackMessage, {
226
228
  lipsyncLang: config.lipsyncLang,
227
229
  onSpeechEnd: () => {
@@ -340,7 +342,7 @@ const CurriculumLearning = forwardRef(({
340
342
  if (animations.nextQuestion) {
341
343
  try {
342
344
  avatarRef.current.playAnimation(animations.nextQuestion, true);
343
- } catch (error) {
345
+ } catch (error) {
344
346
  console.warn('Failed to play nextQuestion animation:', error);
345
347
  }
346
348
  }
@@ -378,7 +380,12 @@ const CurriculumLearning = forwardRef(({
378
380
  const nextLesson = useCallback(() => {
379
381
  const curriculum = curriculumRef.current || { modules: [] };
380
382
  const currentModule = curriculum.modules[stateRef.current.currentModuleIndex];
381
- if (stateRef.current.currentLessonIndex < (currentModule?.lessons?.length || 0) - 1) {
383
+
384
+ // Check if there's a next lesson in the current module
385
+ const hasNextLessonInModule = stateRef.current.currentLessonIndex < (currentModule?.lessons?.length || 0) - 1;
386
+
387
+ if (hasNextLessonInModule) {
388
+ // Move to next lesson in current module
382
389
  stateRef.current.currentLessonIndex += 1;
383
390
  stateRef.current.currentQuestionIndex = 0;
384
391
  stateRef.current.lessonCompleted = false;
@@ -395,7 +402,7 @@ const CurriculumLearning = forwardRef(({
395
402
  });
396
403
 
397
404
  if (avatarRef.current) {
398
- avatarRef.current.setMood("happy");
405
+ avatarRef.current.setMood("happy");
399
406
  avatarRef.current.setBodyMovement("idle");
400
407
 
401
408
  // Automatically start teaching the next lesson after a brief pause
@@ -406,8 +413,43 @@ const CurriculumLearning = forwardRef(({
406
413
  }, 500);
407
414
  }
408
415
  } else {
409
- if (completeCurriculumRef.current) {
410
- completeCurriculumRef.current();
416
+ // No more lessons in current module - check if there's a next module
417
+ const hasNextModule = stateRef.current.currentModuleIndex < (curriculum.modules?.length || 0) - 1;
418
+
419
+ if (hasNextModule) {
420
+ // Move to first lesson of next module
421
+ stateRef.current.currentModuleIndex += 1;
422
+ stateRef.current.currentLessonIndex = 0;
423
+ stateRef.current.currentQuestionIndex = 0;
424
+ stateRef.current.lessonCompleted = false;
425
+ stateRef.current.isQuestionMode = false;
426
+ stateRef.current.isTeaching = false;
427
+ stateRef.current.score = 0;
428
+ stateRef.current.totalQuestions = 0;
429
+
430
+ // Clear current question in UI
431
+ callbacksRef.current.onCustomAction({
432
+ type: 'lessonStart',
433
+ moduleIndex: stateRef.current.currentModuleIndex,
434
+ lessonIndex: stateRef.current.currentLessonIndex
435
+ });
436
+
437
+ if (avatarRef.current) {
438
+ avatarRef.current.setMood("happy");
439
+ avatarRef.current.setBodyMovement("idle");
440
+
441
+ // Automatically start teaching the next lesson after a brief pause
442
+ setTimeout(() => {
443
+ if (startTeachingRef.current) {
444
+ startTeachingRef.current();
445
+ }
446
+ }, 500);
447
+ }
448
+ } else {
449
+ // No more modules or lessons - complete curriculum
450
+ if (completeCurriculumRef.current) {
451
+ completeCurriculumRef.current();
452
+ }
411
453
  }
412
454
  }
413
455
  }, []);
@@ -424,10 +466,10 @@ const CurriculumLearning = forwardRef(({
424
466
  // Play animation if available (can be overridden via custom action)
425
467
  let animationPlayed = false;
426
468
  if (animations.teaching) {
427
- try {
469
+ try {
428
470
  avatarRef.current.playAnimation(animations.teaching, true);
429
471
  animationPlayed = true;
430
- } catch (error) {
472
+ } catch (error) {
431
473
  console.warn('Failed to play teaching animation:', error);
432
474
  }
433
475
  }
@@ -437,7 +479,6 @@ const CurriculumLearning = forwardRef(({
437
479
  }
438
480
 
439
481
  const config = defaultAvatarConfigRef.current || { lipsyncLang: 'en' };
440
- avatarRef.current.speakText(currentLesson.avatar_script, { lipsyncLang: config.lipsyncLang });
441
482
 
442
483
  callbacksRef.current.onLessonStart({
443
484
  moduleIndex: stateRef.current.currentModuleIndex,
@@ -453,21 +494,27 @@ const CurriculumLearning = forwardRef(({
453
494
  lesson: currentLesson
454
495
  });
455
496
 
456
- // After speaking, move to question mode
457
- setTimeout(() => {
458
- stateRef.current.isTeaching = false;
459
- if (currentLesson.questions && currentLesson.questions.length > 0) {
460
- // Use ref to avoid circular dependency
461
- if (startQuestionsRef.current) {
462
- startQuestionsRef.current();
463
- }
464
- } else {
465
- // No questions, complete the lesson using ref to avoid circular dependency
466
- if (completeLessonRef.current) {
467
- completeLessonRef.current();
468
- }
497
+ // Wait for avatar to finish speaking before moving to questions
498
+ avatarRef.current.speakText(currentLesson.avatar_script, {
499
+ lipsyncLang: config.lipsyncLang,
500
+ onSpeechEnd: () => {
501
+ stateRef.current.isTeaching = false;
502
+ // Add a small delay after speech ends for natural flow
503
+ setTimeout(() => {
504
+ if (currentLesson.questions && currentLesson.questions.length > 0) {
505
+ // Use ref to avoid circular dependency
506
+ if (startQuestionsRef.current) {
507
+ startQuestionsRef.current();
508
+ }
509
+ } else {
510
+ // No questions, complete the lesson using ref to avoid circular dependency
511
+ if (completeLessonRef.current) {
512
+ completeLessonRef.current();
513
+ }
514
+ }
515
+ }, 500);
469
516
  }
470
- }, 8000);
517
+ });
471
518
  }
472
519
  }, [animations.teaching, getCurrentLesson]);
473
520
 
@@ -495,7 +542,7 @@ const CurriculumLearning = forwardRef(({
495
542
  if (animations.correct) {
496
543
  try {
497
544
  avatarRef.current.playReaction("happy");
498
- } catch (error) {
545
+ } catch (error) {
499
546
  avatarRef.current.setBodyMovement("happy");
500
547
  }
501
548
  }