@sage-rsc/talking-head-react 1.0.21 → 1.0.22

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-BeFzGp0g.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-BrvgGvOZ.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-3_k5n4iL.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-B58d_xt0.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.21",
3
+ "version": "1.0.22",
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",
@@ -176,7 +176,17 @@ const CurriculumLearning = forwardRef(({
176
176
  stateRef.current.currentQuestionIndex = 0;
177
177
  stateRef.current.totalQuestions = currentLesson?.questions?.length || 0;
178
178
 
179
- if (avatarRef.current) {
179
+ // Trigger custom action immediately (before avatar speaks) - includes first question
180
+ const firstQuestion = getCurrentQuestion();
181
+ callbacksRef.current.onCustomAction({
182
+ type: 'questionStart',
183
+ moduleIndex: stateRef.current.currentModuleIndex,
184
+ lessonIndex: stateRef.current.currentLessonIndex,
185
+ totalQuestions: stateRef.current.totalQuestions,
186
+ question: firstQuestion // Include first question in event
187
+ });
188
+
189
+ if (avatarRef.current) {
180
190
  avatarRef.current.setMood("curious");
181
191
 
182
192
  // Play custom animation if available
@@ -188,17 +198,18 @@ const CurriculumLearning = forwardRef(({
188
198
  }
189
199
  }
190
200
 
191
- avatarRef.current.speakText("Now let me ask you some questions to test your understanding.");
192
-
193
- // Trigger custom action
194
- callbacksRef.current.onCustomAction({
195
- type: 'questionStart',
196
- moduleIndex: stateRef.current.currentModuleIndex,
197
- lessonIndex: stateRef.current.currentLessonIndex,
198
- totalQuestions: stateRef.current.totalQuestions
199
- });
201
+ if (firstQuestion) {
202
+ // If there's a question, introduce it directly
203
+ if (firstQuestion.type === 'code_test') {
204
+ avatarRef.current.speakText(`Let's test your coding skills! Here's your first challenge: ${firstQuestion.question}`);
205
+ } else {
206
+ avatarRef.current.speakText(`Now let me ask you some questions. Here's the first one: ${firstQuestion.question}`);
207
+ }
208
+ } else {
209
+ avatarRef.current.speakText("Now let me ask you some questions to test your understanding.");
210
+ }
200
211
  }
201
- }, [animations.questionStart, getCurrentLesson]);
212
+ }, [animations.questionStart, getCurrentLesson, getCurrentQuestion]);
202
213
 
203
214
  // Move to next question
204
215
  const nextQuestion = useCallback(() => {
@@ -206,33 +217,44 @@ const CurriculumLearning = forwardRef(({
206
217
  if (stateRef.current.currentQuestionIndex < (currentLesson?.questions?.length || 0) - 1) {
207
218
  stateRef.current.currentQuestionIndex += 1;
208
219
 
209
- if (avatarRef.current) {
210
- avatarRef.current.setMood("happy");
220
+ // Trigger custom action BEFORE speaking (so UI can update immediately)
221
+ const nextQuestionObj = getCurrentQuestion();
222
+ callbacksRef.current.onCustomAction({
223
+ type: 'nextQuestion',
224
+ moduleIndex: stateRef.current.currentModuleIndex,
225
+ lessonIndex: stateRef.current.currentLessonIndex,
226
+ questionIndex: stateRef.current.currentQuestionIndex,
227
+ question: nextQuestionObj
228
+ });
229
+
230
+ if (avatarRef.current) {
231
+ avatarRef.current.setMood("happy");
211
232
  avatarRef.current.setBodyMovement("idle");
212
233
 
213
234
  // Play custom animation if available
214
235
  if (animations.nextQuestion) {
215
- try {
236
+ try {
216
237
  avatarRef.current.playAnimation(animations.nextQuestion, true);
217
- } catch (error) {
238
+ } catch (error) {
218
239
  console.warn('Failed to play nextQuestion animation:', error);
219
240
  }
220
241
  }
221
242
 
222
- avatarRef.current.speakText("Here's the next question.");
223
-
224
- // Trigger custom action
225
- callbacksRef.current.onCustomAction({
226
- type: 'nextQuestion',
227
- moduleIndex: stateRef.current.currentModuleIndex,
228
- lessonIndex: stateRef.current.currentLessonIndex,
229
- questionIndex: stateRef.current.currentQuestionIndex
230
- });
243
+ if (nextQuestionObj) {
244
+ // Speak the question text directly if it's a code test, otherwise introduce it
245
+ if (nextQuestionObj.type === 'code_test') {
246
+ avatarRef.current.speakText(`Here's your next coding challenge: ${nextQuestionObj.question}`);
247
+ } else {
248
+ avatarRef.current.speakText(`Here's the next question: ${nextQuestionObj.question}`);
249
+ }
250
+ } else {
251
+ avatarRef.current.speakText("Here's the next question.");
252
+ }
231
253
  }
232
254
  } else {
233
255
  completeLesson();
234
256
  }
235
- }, [animations.nextQuestion, getCurrentLesson, completeLesson]);
257
+ }, [animations.nextQuestion, getCurrentLesson, completeLesson, getCurrentQuestion]);
236
258
 
237
259
  // Move to next lesson
238
260
  const nextLesson = useCallback(() => {
@@ -352,8 +374,26 @@ const CurriculumLearning = forwardRef(({
352
374
  : `Not quite right, but don't worry! ${currentQuestion.explanation || ''} Let's move on to the next question.`;
353
375
  avatarRef.current.speakText(failureMessage);
354
376
  }
377
+
378
+ // Automatically move to next question after avatar finishes speaking
379
+ // Estimate speaking time: ~150 characters per second, minimum 2 seconds
380
+ const messageLength = (isCorrect
381
+ ? (currentQuestion.type === "code_test"
382
+ ? `Great job! Your code passed all the tests! ${currentQuestion.explanation || ''}`
383
+ : `Excellent! That's correct! ${currentQuestion.explanation || ''}`)
384
+ : (currentQuestion.type === "code_test"
385
+ ? `Your code didn't pass all the tests. ${currentQuestion.explanation || 'Try again!'}`
386
+ : `Not quite right, but don't worry! ${currentQuestion.explanation || ''} Let's move on to the next question.`)).length;
387
+ const estimatedTime = Math.max(2000, (messageLength / 150) * 1000) + 500; // Add 500ms buffer
388
+
389
+ setTimeout(() => {
390
+ nextQuestion();
391
+ }, estimatedTime);
392
+ } else {
393
+ // If avatar not ready, move to next question immediately
394
+ nextQuestion();
355
395
  }
356
- }, [animations.correct, animations.incorrect, getCurrentQuestion, checkAnswer]);
396
+ }, [animations.correct, animations.incorrect, getCurrentQuestion, checkAnswer, nextQuestion]);
357
397
 
358
398
  // Handle code test result submission
359
399
  const handleCodeTestResult = useCallback((testResult) => {