@sage-rsc/talking-head-react 1.0.26 → 1.0.28

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-BAyfuKmk.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-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;
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-R68Q6EcC.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-zMY6cyU2.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.26",
3
+ "version": "1.0.28",
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",
@@ -51,6 +51,11 @@ const CurriculumLearning = forwardRef(({
51
51
  onCustomAction
52
52
  });
53
53
 
54
+ // Refs for functions to avoid circular dependencies
55
+ const startTeachingRef = useRef(null);
56
+ const nextLessonRef = useRef(null);
57
+ const completeLessonRef = useRef(null);
58
+
54
59
  // Update callbacks ref when they change
55
60
  useEffect(() => {
56
61
  callbacksRef.current = {
@@ -111,13 +116,20 @@ const CurriculumLearning = forwardRef(({
111
116
  return false;
112
117
  }, []);
113
118
 
114
- // Complete current lesson
119
+ // Complete current lesson - defined before it's used to avoid circular dependency
115
120
  const completeLesson = useCallback(() => {
116
121
  stateRef.current.lessonCompleted = true;
117
122
  stateRef.current.isQuestionMode = false;
118
123
 
119
- const percentage = Math.round((stateRef.current.score / stateRef.current.totalQuestions) * 100);
120
- let feedbackMessage = `Congratulations! You've completed this lesson with a score of ${stateRef.current.score} out of ${stateRef.current.totalQuestions} (${percentage}%). `;
124
+ const percentage = stateRef.current.totalQuestions > 0
125
+ ? Math.round((stateRef.current.score / stateRef.current.totalQuestions) * 100)
126
+ : 100;
127
+ let feedbackMessage = `Congratulations! You've completed this lesson`;
128
+ if (stateRef.current.totalQuestions > 0) {
129
+ feedbackMessage += ` with a score of ${stateRef.current.score} out of ${stateRef.current.totalQuestions} (${percentage}%). `;
130
+ } else {
131
+ feedbackMessage += `! `;
132
+ }
121
133
 
122
134
  if (percentage >= 80) {
123
135
  feedbackMessage += "Excellent work! You have a great understanding of this topic.";
@@ -135,6 +147,16 @@ const CurriculumLearning = forwardRef(({
135
147
  percentage: percentage
136
148
  });
137
149
 
150
+ // Trigger custom action for lesson completion
151
+ callbacksRef.current.onCustomAction({
152
+ type: 'lessonComplete',
153
+ moduleIndex: stateRef.current.currentModuleIndex,
154
+ lessonIndex: stateRef.current.currentLessonIndex,
155
+ score: stateRef.current.score,
156
+ totalQuestions: stateRef.current.totalQuestions,
157
+ percentage: percentage
158
+ });
159
+
138
160
  if (avatarRef.current) {
139
161
  avatarRef.current.setMood("happy");
140
162
  if (animations.lessonComplete) {
@@ -144,9 +166,39 @@ const CurriculumLearning = forwardRef(({
144
166
  avatarRef.current.playCelebration();
145
167
  }
146
168
  }
147
- avatarRef.current.speakText(feedbackMessage, { lipsyncLang: defaultAvatarConfig.lipsyncLang });
169
+
170
+ // Check if there's a next lesson available
171
+ const currentModule = curriculum.modules[stateRef.current.currentModuleIndex];
172
+ const hasNextLesson = stateRef.current.currentLessonIndex < (currentModule?.lessons?.length || 0) - 1;
173
+
174
+ if (hasNextLesson) {
175
+ // Wait for speech to finish, then automatically move to next lesson
176
+ avatarRef.current.speakText(feedbackMessage, {
177
+ lipsyncLang: defaultAvatarConfig.lipsyncLang,
178
+ onSpeechEnd: () => {
179
+ // Add a small delay after speech ends for natural flow
180
+ setTimeout(() => {
181
+ // Use ref to avoid circular dependency
182
+ if (nextLessonRef.current) {
183
+ nextLessonRef.current();
184
+ }
185
+ }, 1000);
186
+ }
187
+ });
188
+ } else {
189
+ // This is the last lesson, complete curriculum instead
190
+ avatarRef.current.speakText(feedbackMessage, {
191
+ lipsyncLang: defaultAvatarConfig.lipsyncLang,
192
+ onSpeechEnd: () => {
193
+ // Add a small delay after speech ends for natural flow
194
+ setTimeout(() => {
195
+ completeCurriculum();
196
+ }, 1000);
197
+ }
198
+ });
199
+ }
148
200
  }
149
- }, [animations.lessonComplete, defaultAvatarConfig]);
201
+ }, [animations.lessonComplete, curriculum, completeCurriculum, defaultAvatarConfig]);
150
202
 
151
203
  // Complete entire curriculum
152
204
  const completeCurriculum = useCallback(() => {
@@ -246,7 +298,7 @@ const CurriculumLearning = forwardRef(({
246
298
  if (animations.nextQuestion) {
247
299
  try {
248
300
  avatarRef.current.playAnimation(animations.nextQuestion, true);
249
- } catch (error) {
301
+ } catch (error) {
250
302
  console.warn('Failed to play nextQuestion animation:', error);
251
303
  }
252
304
  }
@@ -282,16 +334,33 @@ const CurriculumLearning = forwardRef(({
282
334
  stateRef.current.currentLessonIndex += 1;
283
335
  stateRef.current.currentQuestionIndex = 0;
284
336
  stateRef.current.lessonCompleted = false;
337
+ stateRef.current.isQuestionMode = false;
338
+ stateRef.current.isTeaching = false;
285
339
  stateRef.current.score = 0;
286
340
  stateRef.current.totalQuestions = 0;
287
341
 
342
+ // Clear current question in UI
343
+ callbacksRef.current.onCustomAction({
344
+ type: 'lessonStart',
345
+ moduleIndex: stateRef.current.currentModuleIndex,
346
+ lessonIndex: stateRef.current.currentLessonIndex
347
+ });
348
+
288
349
  if (avatarRef.current) {
289
- avatarRef.current.speakText("Let's move on to the next lesson!", { lipsyncLang: defaultAvatarConfig.lipsyncLang });
350
+ avatarRef.current.setMood("happy");
351
+ avatarRef.current.setBodyMovement("idle");
352
+
353
+ // Automatically start teaching the next lesson after a brief pause
354
+ setTimeout(() => {
355
+ if (startTeachingRef.current) {
356
+ startTeachingRef.current();
357
+ }
358
+ }, 500);
290
359
  }
291
360
  } else {
292
361
  completeCurriculum();
293
362
  }
294
- }, [curriculum, completeCurriculum, defaultAvatarConfig]);
363
+ }, [curriculum, completeCurriculum]);
295
364
 
296
365
  // Start teaching the lesson
297
366
  const startTeaching = useCallback(() => {
@@ -339,11 +408,14 @@ const CurriculumLearning = forwardRef(({
339
408
  if (currentLesson.questions && currentLesson.questions.length > 0) {
340
409
  startQuestions();
341
410
  } else {
342
- completeLesson();
411
+ // No questions, complete the lesson using ref to avoid circular dependency
412
+ if (completeLessonRef.current) {
413
+ completeLessonRef.current();
414
+ }
343
415
  }
344
416
  }, 8000);
345
417
  }
346
- }, [animations.teaching, getCurrentLesson, startQuestions, completeLesson, defaultAvatarConfig]);
418
+ }, [animations.teaching, getCurrentLesson, startQuestions, defaultAvatarConfig]);
347
419
 
348
420
  // Handle answer selection
349
421
  const handleAnswerSelect = useCallback((answer) => {
@@ -365,9 +437,9 @@ const CurriculumLearning = forwardRef(({
365
437
 
366
438
  if (avatarRef.current) {
367
439
  if (isCorrect) {
368
- avatarRef.current.setMood("happy");
440
+ avatarRef.current.setMood("happy");
369
441
  if (animations.correct) {
370
- try {
442
+ try {
371
443
  avatarRef.current.playReaction("happy");
372
444
  } catch (error) {
373
445
  avatarRef.current.setBodyMovement("happy");
@@ -388,7 +460,7 @@ const CurriculumLearning = forwardRef(({
388
460
  }, 500);
389
461
  }
390
462
  });
391
- } else {
463
+ } else {
392
464
  avatarRef.current.setMood("sad");
393
465
  if (animations.incorrect) {
394
466
  try {
@@ -485,6 +557,13 @@ const CurriculumLearning = forwardRef(({
485
557
  }
486
558
  }, [autoStart, getCurrentLesson, startTeaching]);
487
559
 
560
+ // Update refs after all functions are defined to avoid circular dependency issues
561
+ useEffect(() => {
562
+ startTeachingRef.current = startTeaching;
563
+ nextLessonRef.current = nextLesson;
564
+ completeLessonRef.current = completeLesson;
565
+ }, [startTeaching, nextLesson, completeLesson]);
566
+
488
567
  // Expose methods via ref (for external control)
489
568
  useImperativeHandle(ref, () => ({
490
569
  // Curriculum control methods