@sage-rsc/talking-head-react 1.0.54 → 1.0.55

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.js CHANGED
@@ -2629,7 +2629,7 @@ const $e = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
2629
2629
  fr: qe,
2630
2630
  fi: Ke,
2631
2631
  lt: $e
2632
- }, U = new f.Quaternion(), E = new f.Euler(), te = new f.Vector3(), ie = new f.Vector3(), Ae = new f.Box3();
2632
+ }, W = new f.Quaternion(), E = new f.Euler(), te = new f.Vector3(), ie = new f.Vector3(), Ae = new f.Box3();
2633
2633
  new f.Matrix4();
2634
2634
  new f.Matrix4();
2635
2635
  new f.Vector3();
@@ -2712,8 +2712,8 @@ class we {
2712
2712
  ttsVoice: "fi-FI-Standard-A",
2713
2713
  ttsRate: 1,
2714
2714
  ttsPitch: 0,
2715
- ttsVolume: 0,
2716
- mixerGainSpeech: null,
2715
+ ttsVolume: 0.3,
2716
+ mixerGainSpeech: 1.2,
2717
2717
  mixerGainBackground: null,
2718
2718
  lipsyncLang: "fi",
2719
2719
  lipsyncModules: ["fi", "en", "lt"],
@@ -4376,7 +4376,7 @@ class we {
4376
4376
  if (e.x === 0 && e.y === 0 && e.z === 0) continue;
4377
4377
  E.set(e.x, e.y, e.z);
4378
4378
  const i = this.poseAvatar.props[t];
4379
- i.isQuaternion ? (U.setFromEuler(E), i.multiply(U)) : i.isVector3 && i.add(E);
4379
+ i.isQuaternion ? (W.setFromEuler(E), i.multiply(W)) : i.isVector3 && i.add(E);
4380
4380
  }
4381
4381
  }
4382
4382
  /**
@@ -5180,7 +5180,7 @@ class we {
5180
5180
  eyeLookOutRight: [null, 0],
5181
5181
  eyeContact: [0]
5182
5182
  }
5183
- })))), e > 2 * this.animFrameDur && (e = 2 * this.animFrameDur), (this.viewName !== "full" || this.isAvatarOnly) && (i = this.mtRandomized[Math.floor(Math.random() * this.mtRandomized.length)], n = this.mtAvatar[i], n.needsUpdate || Object.assign(n, { base: (this.mood.baseline[i] || 0) + (1 + l / 255) * Math.random() / 5, needsUpdate: !0 })), this.updatePoseBase(this.animClock), this.mixer && this.mixer.update(e / 1e3 * this.mixer.timeScale), this.updatePoseDelta(), (this.isSpeaking || this.isListening) && u ? l > this.volumeMax ? (this.volumeHeadBase = 0.05, Math.random() > 0.6 && (this.volumeHeadTarget = -0.05 - Math.random() / 15), this.volumeMax = l) : (this.volumeMax *= 0.92, this.volumeHeadTarget = this.volumeHeadBase - 0.9 * (this.volumeHeadBase - this.volumeHeadTarget)) : (this.volumeHeadTarget = 0, this.volumeMax = 0), i = this.volumeHeadTarget - this.volumeHeadCurrent, n = Math.abs(i), n > 1e-4 && (o = n * (this.volumeHeadEasing(Math.min(1, this.volumeHeadVelocity * e / 1e3 / n) / 2 + 0.5) - 0.5), this.volumeHeadCurrent += Math.sign(i) * Math.min(n, o)), Math.abs(this.volumeHeadCurrent) > 1e-4 && (U.setFromAxisAngle(tt, this.volumeHeadCurrent), this.objectNeck.quaternion.multiply(U)), Ae.setFromObject(this.armature), this.objectLeftToeBase.getWorldPosition(te), te.sub(this.armature.position), this.objectRightToeBase.getWorldPosition(ie), ie.sub(this.armature.position), this.objectHips.position.y -= Ae.min.y / 2, this.objectHips.position.x -= (te.x + ie.x) / 4, this.objectHips.position.z -= (te.z + ie.z) / 2, this.dynamicbones.update(e), this.fbxAnimationLoader && this.fbxAnimationLoader.update(), this.opt.update && this.opt.update(e), this.updateMorphTargets(e), this.isAvatarOnly)
5183
+ })))), e > 2 * this.animFrameDur && (e = 2 * this.animFrameDur), (this.viewName !== "full" || this.isAvatarOnly) && (i = this.mtRandomized[Math.floor(Math.random() * this.mtRandomized.length)], n = this.mtAvatar[i], n.needsUpdate || Object.assign(n, { base: (this.mood.baseline[i] || 0) + (1 + l / 255) * Math.random() / 5, needsUpdate: !0 })), this.updatePoseBase(this.animClock), this.mixer && this.mixer.update(e / 1e3 * this.mixer.timeScale), this.updatePoseDelta(), (this.isSpeaking || this.isListening) && u ? l > this.volumeMax ? (this.volumeHeadBase = 0.05, Math.random() > 0.6 && (this.volumeHeadTarget = -0.05 - Math.random() / 15), this.volumeMax = l) : (this.volumeMax *= 0.92, this.volumeHeadTarget = this.volumeHeadBase - 0.9 * (this.volumeHeadBase - this.volumeHeadTarget)) : (this.volumeHeadTarget = 0, this.volumeMax = 0), i = this.volumeHeadTarget - this.volumeHeadCurrent, n = Math.abs(i), n > 1e-4 && (o = n * (this.volumeHeadEasing(Math.min(1, this.volumeHeadVelocity * e / 1e3 / n) / 2 + 0.5) - 0.5), this.volumeHeadCurrent += Math.sign(i) * Math.min(n, o)), Math.abs(this.volumeHeadCurrent) > 1e-4 && (W.setFromAxisAngle(tt, this.volumeHeadCurrent), this.objectNeck.quaternion.multiply(W)), Ae.setFromObject(this.armature), this.objectLeftToeBase.getWorldPosition(te), te.sub(this.armature.position), this.objectRightToeBase.getWorldPosition(ie), ie.sub(this.armature.position), this.objectHips.position.y -= Ae.min.y / 2, this.objectHips.position.x -= (te.x + ie.x) / 4, this.objectHips.position.z -= (te.z + ie.z) / 2, this.dynamicbones.update(e), this.fbxAnimationLoader && this.fbxAnimationLoader.update(), this.opt.update && this.opt.update(e), this.updateMorphTargets(e), this.isAvatarOnly)
5184
5184
  this.stats && this.stats.end();
5185
5185
  else {
5186
5186
  if (this.cameraClock !== null && this.cameraClock < 1e3) {
@@ -6146,10 +6146,10 @@ class we {
6146
6146
  this.lookAt(null, null, t);
6147
6147
  return;
6148
6148
  }
6149
- this.objectLeftEye.updateMatrixWorld(!0), this.objectRightEye.updateMatrixWorld(!0), te.setFromMatrixPosition(this.objectLeftEye.matrixWorld), ie.setFromMatrixPosition(this.objectRightEye.matrixWorld), te.add(ie).divideScalar(2), U.copy(this.armature.quaternion), U.multiply(this.poseTarget.props["Hips.quaternion"]), U.multiply(this.poseTarget.props["Spine.quaternion"]), U.multiply(this.poseTarget.props["Spine1.quaternion"]), U.multiply(this.poseTarget.props["Spine2.quaternion"]), U.multiply(this.poseTarget.props["Neck.quaternion"]), U.multiply(this.poseTarget.props["Head.quaternion"]);
6149
+ this.objectLeftEye.updateMatrixWorld(!0), this.objectRightEye.updateMatrixWorld(!0), te.setFromMatrixPosition(this.objectLeftEye.matrixWorld), ie.setFromMatrixPosition(this.objectRightEye.matrixWorld), te.add(ie).divideScalar(2), W.copy(this.armature.quaternion), W.multiply(this.poseTarget.props["Hips.quaternion"]), W.multiply(this.poseTarget.props["Spine.quaternion"]), W.multiply(this.poseTarget.props["Spine1.quaternion"]), W.multiply(this.poseTarget.props["Spine2.quaternion"]), W.multiply(this.poseTarget.props["Neck.quaternion"]), W.multiply(this.poseTarget.props["Head.quaternion"]);
6150
6150
  const i = new f.Vector3().subVectors(e, te).normalize(), n = Math.atan2(i.x, i.z), s = Math.asin(-i.y);
6151
6151
  E.set(s, n, 0, "YXZ");
6152
- const l = new f.Quaternion().setFromEuler(E), u = new f.Quaternion().copy(l).multiply(U.clone().invert());
6152
+ const l = new f.Quaternion().setFromEuler(E), u = new f.Quaternion().copy(l).multiply(W.clone().invert());
6153
6153
  E.setFromQuaternion(u, "YXZ");
6154
6154
  let a = E.x / (40 / 24) + 0.2, h = E.y / (9 / 4), r = Math.min(0.6, Math.max(-0.3, a)), d = Math.min(0.8, Math.max(-0.8, h)), c = (Math.random() - 0.5) / 4, g = (Math.random() - 0.5) / 4;
6155
6155
  if (t) {
@@ -6186,7 +6186,7 @@ class we {
6186
6186
  const s = new f.Vector3().setFromMatrixPosition(this.objectLeftEye.matrixWorld), o = new f.Vector3().setFromMatrixPosition(this.objectRightEye.matrixWorld), l = new f.Vector3().addVectors(s, o).divideScalar(2);
6187
6187
  l.project(this.camera);
6188
6188
  let u = (l.x + 1) / 2 * n.width + n.left, a = -(l.y - 1) / 2 * n.height + n.top;
6189
- t === null && (t = u), e === null && (e = a), U.copy(this.armature.quaternion), U.multiply(this.poseTarget.props["Hips.quaternion"]), U.multiply(this.poseTarget.props["Spine.quaternion"]), U.multiply(this.poseTarget.props["Spine1.quaternion"]), U.multiply(this.poseTarget.props["Spine2.quaternion"]), U.multiply(this.poseTarget.props["Neck.quaternion"]), U.multiply(this.poseTarget.props["Head.quaternion"]), E.setFromQuaternion(U);
6189
+ t === null && (t = u), e === null && (e = a), W.copy(this.armature.quaternion), W.multiply(this.poseTarget.props["Hips.quaternion"]), W.multiply(this.poseTarget.props["Spine.quaternion"]), W.multiply(this.poseTarget.props["Spine1.quaternion"]), W.multiply(this.poseTarget.props["Spine2.quaternion"]), W.multiply(this.poseTarget.props["Neck.quaternion"]), W.multiply(this.poseTarget.props["Head.quaternion"]), E.setFromQuaternion(W);
6190
6190
  let h = E.x / (40 / 24), r = E.y / (9 / 4), d = Math.min(0.4, Math.max(-0.4, this.camera.rotation.x)), c = Math.min(0.4, Math.max(-0.4, this.camera.rotation.y)), g = Math.max(window.innerWidth - u, u), y = Math.max(window.innerHeight - a, a), x = this.convertRange(e, [a - y, a + y], [-0.3, 0.6]) - h + d, L = this.convertRange(t, [u - g, u + g], [-0.8, 0.8]) - r + c;
6191
6191
  x = Math.min(0.6, Math.max(-0.3, x)), L = Math.min(0.8, Math.max(-0.8, L));
6192
6192
  let F = (Math.random() - 0.5) / 4, p = (Math.random() - 0.5) / 4;
@@ -6582,7 +6582,7 @@ class we {
6582
6582
  const T = y[p].bone;
6583
6583
  T.matrixWorld.decompose(u, a, h), a.invert(), o.setFromMatrixPosition(g.matrixWorld), l.subVectors(o, u), l.applyQuaternion(a), l.normalize(), s.subVectors(e, u), s.applyQuaternion(a), s.normalize();
6584
6584
  let I = s.dot(l);
6585
- I > 1 ? I = 1 : I < -1 && (I = -1), I = Math.acos(I), !(I < 1e-5) && (y[p].minAngle !== void 0 && I < y[p].minAngle && (I = y[p].minAngle), y[p].maxAngle !== void 0 && I > y[p].maxAngle && (I = y[p].maxAngle), r.crossVectors(l, s), r.normalize(), U.setFromAxisAngle(r, I), T.quaternion.multiply(U), T.rotation.setFromVector3(d.setFromEuler(T.rotation).clamp(new f.Vector3(
6585
+ I > 1 ? I = 1 : I < -1 && (I = -1), I = Math.acos(I), !(I < 1e-5) && (y[p].minAngle !== void 0 && I < y[p].minAngle && (I = y[p].minAngle), y[p].maxAngle !== void 0 && I > y[p].maxAngle && (I = y[p].maxAngle), r.crossVectors(l, s), r.normalize(), W.setFromAxisAngle(r, I), T.quaternion.multiply(W), T.rotation.setFromVector3(d.setFromEuler(T.rotation).clamp(new f.Vector3(
6586
6586
  y[p].minx !== void 0 ? y[p].minx : -1 / 0,
6587
6587
  y[p].miny !== void 0 ? y[p].miny : -1 / 0,
6588
6588
  y[p].minz !== void 0 ? y[p].minz : -1 / 0
@@ -6713,7 +6713,7 @@ const ze = fe(({
6713
6713
  // Override API key if provided via props
6714
6714
  apiKey: o !== null ? o : S.apiKey
6715
6715
  };
6716
- const W = {
6716
+ const V = {
6717
6717
  url: N,
6718
6718
  body: t,
6719
6719
  avatarMood: e,
@@ -6732,7 +6732,7 @@ const ze = fe(({
6732
6732
  }, K = M(async () => {
6733
6733
  if (!(!F.current || p.current))
6734
6734
  try {
6735
- if (I(!0), D(null), p.current = new we(F.current, _), p.current.controls && (p.current.controls.enableRotate = !1, p.current.controls.enableZoom = !1, p.current.controls.enablePan = !1, p.current.controls.enableDamping = !1), x && Object.keys(x).length > 0 && (p.current.customAnimations = x), await p.current.showAvatar(W, (z) => {
6735
+ if (I(!0), D(null), p.current = new we(F.current, _), p.current.controls && (p.current.controls.enableRotate = !1, p.current.controls.enableZoom = !1, p.current.controls.enablePan = !1, p.current.controls.enableDamping = !1), x && Object.keys(x).length > 0 && (p.current.customAnimations = x), await p.current.showAvatar(V, (z) => {
6736
6736
  if (z.lengthComputable) {
6737
6737
  const C = Math.min(100, Math.round(z.loaded / z.total * 100));
6738
6738
  d(C);
@@ -6788,16 +6788,16 @@ const ze = fe(({
6788
6788
  await se();
6789
6789
  const z = {
6790
6790
  ...k,
6791
- lipsyncLang: k.lipsyncLang || W.lipsyncLang || "en"
6791
+ lipsyncLang: k.lipsyncLang || V.lipsyncLang || "en"
6792
6792
  };
6793
6793
  if (k.onSpeechEnd && p.current) {
6794
6794
  const C = p.current;
6795
- let V = null, J = 0;
6795
+ let U = null, J = 0;
6796
6796
  const ye = 1200;
6797
6797
  let he = !1;
6798
- V = setInterval(() => {
6798
+ U = setInterval(() => {
6799
6799
  if (J++, J > ye) {
6800
- if (V && (clearInterval(V), V = null), !he) {
6800
+ if (U && (clearInterval(U), U = null), !he) {
6801
6801
  he = !0;
6802
6802
  try {
6803
6803
  k.onSpeechEnd();
@@ -6810,7 +6810,7 @@ const ze = fe(({
6810
6810
  const He = !C.speechQueue || C.speechQueue.length === 0, Te = !C.audioPlaylist || C.audioPlaylist.length === 0;
6811
6811
  C && C.isSpeaking === !1 && He && Te && C.isAudioPlaying === !1 && !he && setTimeout(() => {
6812
6812
  if (C && C.isSpeaking === !1 && (!C.speechQueue || C.speechQueue.length === 0) && (!C.audioPlaylist || C.audioPlaylist.length === 0) && C.isAudioPlaying === !1 && !he) {
6813
- he = !0, V && (clearInterval(V), V = null);
6813
+ he = !0, U && (clearInterval(U), U = null);
6814
6814
  try {
6815
6815
  k.onSpeechEnd();
6816
6816
  } catch (Me) {
@@ -6826,7 +6826,7 @@ const ze = fe(({
6826
6826
  } catch (z) {
6827
6827
  console.error("Error speaking text:", z), D(z.message || "Failed to speak text");
6828
6828
  }
6829
- }, [Y, se, W.lipsyncLang]), ue = M(() => {
6829
+ }, [Y, se, V.lipsyncLang]), ue = M(() => {
6830
6830
  p.current && (p.current.stopSpeaking(), p.current.setSlowdownRate && p.current.setSlowdownRate(1));
6831
6831
  }, []), ge = M((v) => {
6832
6832
  p.current && p.current.setMood(v);
@@ -6847,20 +6847,20 @@ const ze = fe(({
6847
6847
  console.warn(`Failed to play ${v}:`, C);
6848
6848
  try {
6849
6849
  p.current.setBodyMovement("idle");
6850
- } catch (V) {
6851
- console.warn("Fallback animation also failed:", V);
6850
+ } catch (U) {
6851
+ console.warn("Fallback animation also failed:", U);
6852
6852
  }
6853
6853
  }
6854
6854
  else {
6855
6855
  const C = [".fbx", ".glb", ".gltf"];
6856
- let V = !1;
6856
+ let U = !1;
6857
6857
  for (const J of C)
6858
6858
  try {
6859
- p.current.playAnimation(v + J, null, 10, 0, 0.01, k), V = !0;
6859
+ p.current.playAnimation(v + J, null, 10, 0, 0.01, k), U = !0;
6860
6860
  break;
6861
6861
  } catch {
6862
6862
  }
6863
- if (!V) {
6863
+ if (!U) {
6864
6864
  console.warn("Animation not found:", v);
6865
6865
  try {
6866
6866
  p.current.setBodyMovement("idle");
@@ -7036,20 +7036,20 @@ const it = fe(({
7036
7036
  try {
7037
7037
  if (r(!0), c(null), a.current = new we(u.current, H), await a.current.showAvatar(p, (O) => {
7038
7038
  if (O.lengthComputable) {
7039
- const W = Math.min(100, Math.round(O.loaded / O.total * 100));
7040
- t(W);
7039
+ const V = Math.min(100, Math.round(O.loaded / O.total * 100));
7040
+ t(V);
7041
7041
  }
7042
7042
  }), a.current.morphs && a.current.morphs.length > 0) {
7043
7043
  const O = a.current.morphs[0].morphTargetDictionary;
7044
7044
  console.log("Available morph targets:", Object.keys(O));
7045
- const W = Object.keys(O).filter((_) => _.startsWith("viseme_"));
7046
- console.log("Viseme morph targets found:", W), W.length === 0 && (console.warn("No viseme morph targets found! Lip-sync will not work properly."), console.log("Expected viseme targets: viseme_aa, viseme_E, viseme_I, viseme_O, viseme_U, viseme_PP, viseme_SS, viseme_TH, viseme_DD, viseme_FF, viseme_kk, viseme_nn, viseme_RR, viseme_CH, viseme_sil"));
7045
+ const V = Object.keys(O).filter((_) => _.startsWith("viseme_"));
7046
+ console.log("Viseme morph targets found:", V), V.length === 0 && (console.warn("No viseme morph targets found! Lip-sync will not work properly."), console.log("Expected viseme targets: viseme_aa, viseme_E, viseme_I, viseme_O, viseme_U, viseme_PP, viseme_SS, viseme_TH, viseme_DD, viseme_FF, viseme_kk, viseme_nn, viseme_RR, viseme_CH, viseme_sil"));
7047
7047
  }
7048
7048
  if (await new Promise((O) => {
7049
- const W = () => {
7050
- a.current.lipsync && Object.keys(a.current.lipsync).length > 0 ? (console.log("Lip-sync modules loaded:", Object.keys(a.current.lipsync)), O()) : (console.log("Waiting for lip-sync modules to load..."), setTimeout(W, 100));
7049
+ const V = () => {
7050
+ a.current.lipsync && Object.keys(a.current.lipsync).length > 0 ? (console.log("Lip-sync modules loaded:", Object.keys(a.current.lipsync)), O()) : (console.log("Waiting for lip-sync modules to load..."), setTimeout(V, 100));
7051
7051
  };
7052
- W();
7052
+ V();
7053
7053
  }), a.current && a.current.setShowFullAvatar)
7054
7054
  try {
7055
7055
  a.current.setShowFullAvatar(!0), console.log("Avatar initialized in full body mode");
@@ -7092,14 +7092,14 @@ const it = fe(({
7092
7092
  if (a.current.setShowFullAvatar)
7093
7093
  try {
7094
7094
  a.current.setShowFullAvatar(!0);
7095
- } catch (W) {
7096
- console.warn("Error setting full body mode:", W);
7095
+ } catch (V) {
7096
+ console.warn("Error setting full body mode:", V);
7097
7097
  }
7098
7098
  if (S.includes("."))
7099
7099
  try {
7100
7100
  a.current.playAnimation(S, null, 10, 0, 0.01, P), console.log("Playing animation:", S);
7101
- } catch (W) {
7102
- console.log(`Failed to play ${S}:`, W);
7101
+ } catch (V) {
7102
+ console.log(`Failed to play ${S}:`, V);
7103
7103
  try {
7104
7104
  a.current.setBodyMovement("idle"), console.log("Fallback to idle animation");
7105
7105
  } catch (_) {
@@ -7107,9 +7107,9 @@ const it = fe(({
7107
7107
  }
7108
7108
  }
7109
7109
  else {
7110
- const W = [".fbx", ".glb", ".gltf"];
7110
+ const V = [".fbx", ".glb", ".gltf"];
7111
7111
  let _ = !1;
7112
- for (const K of W)
7112
+ for (const K of V)
7113
7113
  try {
7114
7114
  a.current.playAnimation(S + K, null, 10, 0, 0.01, P), console.log("Playing animation:", S + K), _ = !0;
7115
7115
  break;
@@ -7334,7 +7334,7 @@ const nt = fe(({
7334
7334
  } catch {
7335
7335
  h.current.playCelebration();
7336
7336
  }
7337
- const v = H.current || { modules: [] }, k = v.modules[r.current.currentModuleIndex], z = r.current.currentLessonIndex < (k?.lessons?.length || 0) - 1, C = r.current.currentModuleIndex < (v.modules?.length || 0) - 1, V = z || C, J = T.current || { lipsyncLang: "en" };
7337
+ const v = H.current || { modules: [] }, k = v.modules[r.current.currentModuleIndex], z = r.current.currentLessonIndex < (k?.lessons?.length || 0) - 1, C = r.current.currentModuleIndex < (v.modules?.length || 0) - 1, U = z || C, J = T.current || { lipsyncLang: "en" };
7338
7338
  h.current.speakText(R, {
7339
7339
  lipsyncLang: J.lipsyncLang,
7340
7340
  onSpeechEnd: () => {
@@ -7345,7 +7345,7 @@ const nt = fe(({
7345
7345
  score: r.current.score,
7346
7346
  totalQuestions: r.current.totalQuestions,
7347
7347
  percentage: b,
7348
- hasNextLesson: V
7348
+ hasNextLesson: U
7349
7349
  });
7350
7350
  }
7351
7351
  });
@@ -7368,7 +7368,7 @@ const nt = fe(({
7368
7368
  }
7369
7369
  }, [e.curriculumComplete]), S = M(() => {
7370
7370
  const b = I();
7371
- r.current.isQuestionMode = !0, r.current.currentQuestionIndex = 0, r.current.totalQuestions = b?.questions?.length || 0;
7371
+ r.current.isQuestionMode = !0, r.current.currentQuestionIndex = 0, r.current.totalQuestions = b?.questions?.length || 0, r.current.score = 0;
7372
7372
  const R = B();
7373
7373
  R && d.current.onCustomAction({
7374
7374
  type: "questionStart",
@@ -7376,7 +7376,8 @@ const nt = fe(({
7376
7376
  lessonIndex: r.current.currentLessonIndex,
7377
7377
  questionIndex: r.current.currentQuestionIndex,
7378
7378
  totalQuestions: r.current.totalQuestions,
7379
- question: R
7379
+ question: R,
7380
+ score: r.current.score
7380
7381
  });
7381
7382
  const v = () => {
7382
7383
  if (!h.current || !R) return;
@@ -7413,7 +7414,8 @@ const nt = fe(({
7413
7414
  lessonIndex: r.current.currentLessonIndex,
7414
7415
  questionIndex: r.current.currentQuestionIndex,
7415
7416
  totalQuestions: r.current.totalQuestions,
7416
- question: R
7417
+ question: R,
7418
+ score: r.current.score
7417
7419
  });
7418
7420
  const v = () => {
7419
7421
  if (!h.current || !R) return;
@@ -7456,12 +7458,12 @@ const nt = fe(({
7456
7458
  const b = H.current || { modules: [] }, R = b.modules[r.current.currentModuleIndex];
7457
7459
  if (r.current.currentLessonIndex < (R?.lessons?.length || 0) - 1) {
7458
7460
  r.current.currentLessonIndex += 1, r.current.currentQuestionIndex = 0, r.current.lessonCompleted = !1, r.current.isQuestionMode = !1, r.current.isTeaching = !1, r.current.score = 0, r.current.totalQuestions = 0;
7459
- const k = b.modules[r.current.currentModuleIndex], z = r.current.currentLessonIndex < (k?.lessons?.length || 0) - 1, C = r.current.currentModuleIndex < (b.modules?.length || 0) - 1, V = z || C;
7461
+ const k = b.modules[r.current.currentModuleIndex], z = r.current.currentLessonIndex < (k?.lessons?.length || 0) - 1, C = r.current.currentModuleIndex < (b.modules?.length || 0) - 1, U = z || C;
7460
7462
  d.current.onCustomAction({
7461
7463
  type: "lessonStart",
7462
7464
  moduleIndex: r.current.currentModuleIndex,
7463
7465
  lessonIndex: r.current.currentLessonIndex,
7464
- hasNextLesson: V
7466
+ hasNextLesson: U
7465
7467
  }), d.current.onLessonStart({
7466
7468
  moduleIndex: r.current.currentModuleIndex,
7467
7469
  lessonIndex: r.current.currentLessonIndex,
@@ -7469,7 +7471,7 @@ const nt = fe(({
7469
7471
  }), h.current && (h.current.setMood("happy"), h.current.setBodyMovement("idle"));
7470
7472
  } else if (r.current.currentModuleIndex < (b.modules?.length || 0) - 1) {
7471
7473
  r.current.currentModuleIndex += 1, r.current.currentLessonIndex = 0, r.current.currentQuestionIndex = 0, r.current.lessonCompleted = !1, r.current.isQuestionMode = !1, r.current.isTeaching = !1, r.current.score = 0, r.current.totalQuestions = 0;
7472
- const z = b.modules[r.current.currentModuleIndex], C = r.current.currentLessonIndex < (z?.lessons?.length || 0) - 1, V = r.current.currentModuleIndex < (b.modules?.length || 0) - 1, J = C || V;
7474
+ const z = b.modules[r.current.currentModuleIndex], C = r.current.currentLessonIndex < (z?.lessons?.length || 0) - 1, U = r.current.currentModuleIndex < (b.modules?.length || 0) - 1, J = C || U;
7473
7475
  d.current.onCustomAction({
7474
7476
  type: "lessonStart",
7475
7477
  moduleIndex: r.current.currentModuleIndex,
@@ -7482,7 +7484,7 @@ const nt = fe(({
7482
7484
  }), h.current && (h.current.setMood("happy"), h.current.setBodyMovement("idle"));
7483
7485
  } else
7484
7486
  L.current && L.current();
7485
- }, []), W = M(() => {
7487
+ }, []), V = M(() => {
7486
7488
  const b = I();
7487
7489
  let R = null;
7488
7490
  if (b?.avatar_script && b?.body) {
@@ -7491,7 +7493,7 @@ const nt = fe(({
7491
7493
  } else
7492
7494
  R = b?.avatar_script || b?.body || null;
7493
7495
  if (h.current && h.current.isReady && R) {
7494
- r.current.isTeaching = !0, r.current.isQuestionMode = !1, h.current.setMood("happy");
7496
+ r.current.isTeaching = !0, r.current.isQuestionMode = !1, r.current.score = 0, r.current.totalQuestions = 0, h.current.setMood("happy");
7495
7497
  let v = !1;
7496
7498
  if (e.teaching)
7497
7499
  try {
@@ -7545,13 +7547,16 @@ const nt = fe(({
7545
7547
  h.current.speakText(k, {
7546
7548
  lipsyncLang: z.lipsyncLang,
7547
7549
  onSpeechEnd: () => {
7550
+ const U = I()?.questions?.length || 0;
7548
7551
  d.current.onCustomAction({
7549
7552
  type: "answerFeedbackComplete",
7550
7553
  moduleIndex: r.current.currentModuleIndex,
7551
7554
  lessonIndex: r.current.currentLessonIndex,
7552
7555
  questionIndex: r.current.currentQuestionIndex,
7553
7556
  isCorrect: !0,
7554
- hasNextQuestion: r.current.currentQuestionIndex < (I()?.questions?.length || 0) - 1
7557
+ hasNextQuestion: r.current.currentQuestionIndex < U - 1,
7558
+ score: r.current.score,
7559
+ totalQuestions: r.current.totalQuestions
7555
7560
  });
7556
7561
  }
7557
7562
  });
@@ -7567,27 +7572,34 @@ const nt = fe(({
7567
7572
  h.current.speakText(k, {
7568
7573
  lipsyncLang: z.lipsyncLang,
7569
7574
  onSpeechEnd: () => {
7575
+ const U = I()?.questions?.length || 0;
7570
7576
  d.current.onCustomAction({
7571
7577
  type: "answerFeedbackComplete",
7572
7578
  moduleIndex: r.current.currentModuleIndex,
7573
7579
  lessonIndex: r.current.currentLessonIndex,
7574
7580
  questionIndex: r.current.currentQuestionIndex,
7575
7581
  isCorrect: !1,
7576
- hasNextQuestion: r.current.currentQuestionIndex < (I()?.questions?.length || 0) - 1
7582
+ hasNextQuestion: r.current.currentQuestionIndex < U - 1,
7583
+ score: r.current.score,
7584
+ totalQuestions: r.current.totalQuestions
7577
7585
  });
7578
7586
  }
7579
7587
  });
7580
7588
  }
7581
- else
7589
+ else {
7590
+ const z = I()?.questions?.length || 0;
7582
7591
  d.current.onCustomAction({
7583
7592
  type: "answerFeedbackComplete",
7584
7593
  moduleIndex: r.current.currentModuleIndex,
7585
7594
  lessonIndex: r.current.currentLessonIndex,
7586
7595
  questionIndex: r.current.currentQuestionIndex,
7587
7596
  isCorrect: v,
7588
- hasNextQuestion: r.current.currentQuestionIndex < (I()?.questions?.length || 0) - 1,
7597
+ hasNextQuestion: r.current.currentQuestionIndex < z - 1,
7598
+ score: r.current.score,
7599
+ totalQuestions: r.current.totalQuestions,
7589
7600
  avatarNotReady: !0
7590
7601
  });
7602
+ }
7591
7603
  }, [e.correct, e.incorrect, B, I, D]), K = M((b) => {
7592
7604
  const R = B();
7593
7605
  if (!b || typeof b != "object") {
@@ -7626,7 +7638,8 @@ const nt = fe(({
7626
7638
  lessonIndex: r.current.currentLessonIndex,
7627
7639
  questionIndex: r.current.currentQuestionIndex,
7628
7640
  totalQuestions: r.current.totalQuestions,
7629
- question: b
7641
+ question: b,
7642
+ score: r.current.score
7630
7643
  });
7631
7644
  const R = () => {
7632
7645
  if (!h.current || !b) return;
@@ -7683,10 +7696,10 @@ const nt = fe(({
7683
7696
  }, 10);
7684
7697
  }, [u, I]);
7685
7698
  Ee(() => {
7686
- c.current = W, g.current = O, y.current = Y, x.current = P, L.current = oe, F.current = S, p.current = _;
7699
+ c.current = V, g.current = O, y.current = Y, x.current = P, L.current = oe, F.current = S, p.current = _;
7687
7700
  }), xe(a, () => ({
7688
7701
  // Curriculum control methods
7689
- startTeaching: W,
7702
+ startTeaching: V,
7690
7703
  startQuestions: S,
7691
7704
  handleAnswerSelect: _,
7692
7705
  handleCodeTestResult: K,
@@ -7747,7 +7760,7 @@ const nt = fe(({
7747
7760
  handleResize: () => h.current?.handleResize(),
7748
7761
  // Avatar readiness check (always returns current value)
7749
7762
  isAvatarReady: () => h.current?.isReady || !1
7750
- }), [W, S, _, K, P, O, Y, oe, ue, B, I]);
7763
+ }), [V, S, _, K, P, O, Y, oe, ue, B, I]);
7751
7764
  const Q = T.current || {
7752
7765
  avatarUrl: "/avatars/brunette.glb",
7753
7766
  avatarBody: "F",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sage-rsc/talking-head-react",
3
- "version": "1.0.54",
3
+ "version": "1.0.55",
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",
@@ -284,6 +284,8 @@ const CurriculumLearning = forwardRef(({
284
284
  stateRef.current.isQuestionMode = true;
285
285
  stateRef.current.currentQuestionIndex = 0;
286
286
  stateRef.current.totalQuestions = currentLesson?.questions?.length || 0;
287
+ // Reset score when starting questions for a lesson
288
+ stateRef.current.score = 0;
287
289
 
288
290
  // Trigger custom action IMMEDIATELY for UI to show the first question
289
291
  const firstQuestion = getCurrentQuestion();
@@ -293,10 +295,11 @@ const CurriculumLearning = forwardRef(({
293
295
  type: 'questionStart',
294
296
  moduleIndex: stateRef.current.currentModuleIndex,
295
297
  lessonIndex: stateRef.current.currentLessonIndex,
296
- questionIndex: stateRef.current.currentQuestionIndex,
297
- totalQuestions: stateRef.current.totalQuestions,
298
- question: firstQuestion
299
- });
298
+ questionIndex: stateRef.current.currentQuestionIndex,
299
+ totalQuestions: stateRef.current.totalQuestions,
300
+ question: firstQuestion,
301
+ score: stateRef.current.score
302
+ });
300
303
  }
301
304
 
302
305
  // Function to speak the first question
@@ -369,7 +372,8 @@ const CurriculumLearning = forwardRef(({
369
372
  lessonIndex: stateRef.current.currentLessonIndex,
370
373
  questionIndex: stateRef.current.currentQuestionIndex,
371
374
  totalQuestions: stateRef.current.totalQuestions,
372
- question: nextQuestionObj
375
+ question: nextQuestionObj,
376
+ score: stateRef.current.score
373
377
  });
374
378
  }
375
379
 
@@ -554,6 +558,9 @@ const CurriculumLearning = forwardRef(({
554
558
  if (avatarRef.current && avatarRef.current.isReady && teachingText) {
555
559
  stateRef.current.isTeaching = true;
556
560
  stateRef.current.isQuestionMode = false;
561
+ // Reset score and totalQuestions when starting a new lesson teaching
562
+ stateRef.current.score = 0;
563
+ stateRef.current.totalQuestions = 0;
557
564
 
558
565
  avatarRef.current.setMood("happy");
559
566
 
@@ -646,13 +653,17 @@ const CurriculumLearning = forwardRef(({
646
653
  lipsyncLang: config.lipsyncLang,
647
654
  onSpeechEnd: () => {
648
655
  // Notify parent that feedback is complete - parent decides next action
656
+ const currentLesson = getCurrentLesson();
657
+ const totalQuestionsInLesson = currentLesson?.questions?.length || 0;
649
658
  callbacksRef.current.onCustomAction({
650
659
  type: 'answerFeedbackComplete',
651
660
  moduleIndex: stateRef.current.currentModuleIndex,
652
661
  lessonIndex: stateRef.current.currentLessonIndex,
653
662
  questionIndex: stateRef.current.currentQuestionIndex,
654
663
  isCorrect: true,
655
- hasNextQuestion: stateRef.current.currentQuestionIndex < (getCurrentLesson()?.questions?.length || 0) - 1
664
+ hasNextQuestion: stateRef.current.currentQuestionIndex < totalQuestionsInLesson - 1,
665
+ score: stateRef.current.score,
666
+ totalQuestions: stateRef.current.totalQuestions
656
667
  });
657
668
  }
658
669
  });
@@ -677,26 +688,34 @@ const CurriculumLearning = forwardRef(({
677
688
  lipsyncLang: config.lipsyncLang,
678
689
  onSpeechEnd: () => {
679
690
  // Notify parent that feedback is complete - parent decides next action
691
+ const currentLesson = getCurrentLesson();
692
+ const totalQuestionsInLesson = currentLesson?.questions?.length || 0;
680
693
  callbacksRef.current.onCustomAction({
681
694
  type: 'answerFeedbackComplete',
682
695
  moduleIndex: stateRef.current.currentModuleIndex,
683
696
  lessonIndex: stateRef.current.currentLessonIndex,
684
697
  questionIndex: stateRef.current.currentQuestionIndex,
685
698
  isCorrect: false,
686
- hasNextQuestion: stateRef.current.currentQuestionIndex < (getCurrentLesson()?.questions?.length || 0) - 1
699
+ hasNextQuestion: stateRef.current.currentQuestionIndex < totalQuestionsInLesson - 1,
700
+ score: stateRef.current.score,
701
+ totalQuestions: stateRef.current.totalQuestions
687
702
  });
688
703
  }
689
704
  });
690
705
  }
691
706
  } else {
692
707
  // Avatar not ready - notify parent
708
+ const currentLesson = getCurrentLesson();
709
+ const totalQuestionsInLesson = currentLesson?.questions?.length || 0;
693
710
  callbacksRef.current.onCustomAction({
694
711
  type: 'answerFeedbackComplete',
695
712
  moduleIndex: stateRef.current.currentModuleIndex,
696
713
  lessonIndex: stateRef.current.currentLessonIndex,
697
714
  questionIndex: stateRef.current.currentQuestionIndex,
698
715
  isCorrect: isCorrect,
699
- hasNextQuestion: stateRef.current.currentQuestionIndex < (getCurrentLesson()?.questions?.length || 0) - 1,
716
+ hasNextQuestion: stateRef.current.currentQuestionIndex < totalQuestionsInLesson - 1,
717
+ score: stateRef.current.score,
718
+ totalQuestions: stateRef.current.totalQuestions,
700
719
  avatarNotReady: true
701
720
  });
702
721
  }
@@ -760,7 +779,8 @@ const CurriculumLearning = forwardRef(({
760
779
  lessonIndex: stateRef.current.currentLessonIndex,
761
780
  questionIndex: stateRef.current.currentQuestionIndex,
762
781
  totalQuestions: stateRef.current.totalQuestions,
763
- question: prevQuestionObj
782
+ question: prevQuestionObj,
783
+ score: stateRef.current.score
764
784
  });
765
785
  }
766
786
 
@@ -146,8 +146,8 @@ class TalkingHead {
146
146
  ttsVoice: "fi-FI-Standard-A",
147
147
  ttsRate: 1,
148
148
  ttsPitch: 0,
149
- ttsVolume: 0,
150
- mixerGainSpeech: null,
149
+ ttsVolume: 0.3,
150
+ mixerGainSpeech: 1.2,
151
151
  mixerGainBackground: null,
152
152
  lipsyncLang: 'fi',
153
153
  lipsyncModules: ['fi','en','lt'],