@sage-rsc/talking-head-react 1.0.35 → 1.0.36

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
@@ -1,5 +1,5 @@
1
1
  import { jsxs as Ae, jsx as se } from "react/jsx-runtime";
2
- import { forwardRef as ye, useRef as X, useState as re, useCallback as T, useEffect as he, useImperativeHandle as fe, useLayoutEffect as Ce } from "react";
2
+ import { forwardRef as ye, useRef as X, useState as re, useCallback as M, useEffect as he, useImperativeHandle as fe, useLayoutEffect as Ce } from "react";
3
3
  import * as y from "three";
4
4
  import { OrbitControls as He } from "three/addons/controls/OrbitControls.js";
5
5
  import { GLTFLoader as Te } from "three/addons/loaders/GLTFLoader.js";
@@ -2629,7 +2629,7 @@ const qe = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
2629
2629
  fr: Xe,
2630
2630
  fi: je,
2631
2631
  lt: qe
2632
- }, N = new y.Quaternion(), H = new y.Euler(), ie = new y.Vector3(), oe = new y.Vector3(), Ie = new y.Box3();
2632
+ }, N = new y.Quaternion(), T = new y.Euler(), ie = new y.Vector3(), oe = new y.Vector3(), Ie = new y.Box3();
2633
2633
  new y.Matrix4();
2634
2634
  new y.Matrix4();
2635
2635
  new y.Vector3();
@@ -4374,9 +4374,9 @@ class Le {
4374
4374
  updatePoseDelta() {
4375
4375
  for (const [t, e] of Object.entries(this.poseDelta.props)) {
4376
4376
  if (e.x === 0 && e.y === 0 && e.z === 0) continue;
4377
- H.set(e.x, e.y, e.z);
4377
+ T.set(e.x, e.y, e.z);
4378
4378
  const i = this.poseAvatar.props[t];
4379
- i.isQuaternion ? (N.setFromEuler(H), i.multiply(N)) : i.isVector3 && i.add(H);
4379
+ i.isQuaternion ? (N.setFromEuler(T), i.multiply(N)) : i.isVector3 && i.add(T);
4380
4380
  }
4381
4381
  }
4382
4382
  /**
@@ -5159,7 +5159,7 @@ class Le {
5159
5159
  }, n.x ? new y.Vector3(n.x, n.y, n.z) : null, !0, n.d);
5160
5160
  break;
5161
5161
  }
5162
- if ((h || a) && (H.setFromQuaternion(this.poseAvatar.props["Head.quaternion"]), H.x = Math.max(-0.9, Math.min(0.9, 2 * H.x - 0.5)), H.y = Math.max(-0.9, Math.min(0.9, -2.5 * H.y)), h ? (Object.assign(this.mtAvatar.eyesLookDown, { system: H.x < 0 ? -H.x : 0, needsUpdate: !0 }), Object.assign(this.mtAvatar.eyesLookUp, { system: H.x < 0 ? 0 : H.x, needsUpdate: !0 }), Object.assign(this.mtAvatar.eyeLookInLeft, { system: H.y < 0 ? -H.y : 0, needsUpdate: !0 }), Object.assign(this.mtAvatar.eyeLookOutLeft, { system: H.y < 0 ? 0 : H.y, needsUpdate: !0 }), Object.assign(this.mtAvatar.eyeLookInRight, { system: H.y < 0 ? 0 : H.y, needsUpdate: !0 }), Object.assign(this.mtAvatar.eyeLookOutRight, { system: H.y < 0 ? -H.y : 0, needsUpdate: !0 }), a && (i = -this.mtAvatar.bodyRotateY.value, n = this.gaussianRandom(-0.2, 0.2), this.animQueue.push(this.animFactory({
5162
+ if ((h || a) && (T.setFromQuaternion(this.poseAvatar.props["Head.quaternion"]), T.x = Math.max(-0.9, Math.min(0.9, 2 * T.x - 0.5)), T.y = Math.max(-0.9, Math.min(0.9, -2.5 * T.y)), h ? (Object.assign(this.mtAvatar.eyesLookDown, { system: T.x < 0 ? -T.x : 0, needsUpdate: !0 }), Object.assign(this.mtAvatar.eyesLookUp, { system: T.x < 0 ? 0 : T.x, needsUpdate: !0 }), Object.assign(this.mtAvatar.eyeLookInLeft, { system: T.y < 0 ? -T.y : 0, needsUpdate: !0 }), Object.assign(this.mtAvatar.eyeLookOutLeft, { system: T.y < 0 ? 0 : T.y, needsUpdate: !0 }), Object.assign(this.mtAvatar.eyeLookInRight, { system: T.y < 0 ? 0 : T.y, needsUpdate: !0 }), Object.assign(this.mtAvatar.eyeLookOutRight, { system: T.y < 0 ? -T.y : 0, needsUpdate: !0 }), a && (i = -this.mtAvatar.bodyRotateY.value, n = this.gaussianRandom(-0.2, 0.2), this.animQueue.push(this.animFactory({
5163
5163
  name: "headmove",
5164
5164
  dt: [[1e3, 2e3], [1e3, 2e3, 1, 2], [1e3, 2e3], [1e3, 2e3, 1, 2]],
5165
5165
  vs: {
@@ -5251,10 +5251,10 @@ class Le {
5251
5251
  let u = "", l = "", c = 0, d = [], g = [];
5252
5252
  const f = Array.from(this.segmenter.segment(t), (x) => x.segment);
5253
5253
  for (let x = 0; x < f.length; x++) {
5254
- const I = x === f.length - 1, C = f[x].match(r);
5254
+ const I = x === f.length - 1, H = f[x].match(r);
5255
5255
  let p = f[x].match(s);
5256
- const M = f[x].match(h), z = f[x].match(o);
5257
- if (p && !I && !M && f[x + 1].match(s) && (p = !1), i && (u += f[x]), C && (!n || n.every((v) => x < v[0] || x > v[1])) && (l += f[x]), (z || p || I) && (l.length && (l = this.lipsyncPreProcessText(l, a), l.length && d.push({
5256
+ const E = f[x].match(h), z = f[x].match(o);
5257
+ if (p && !I && !E && f[x + 1].match(s) && (p = !1), i && (u += f[x]), H && (!n || n.every((v) => x < v[0] || x > v[1])) && (l += f[x]), (z || p || I) && (l.length && (l = this.lipsyncPreProcessText(l, a), l.length && d.push({
5258
5258
  mark: c,
5259
5259
  word: l
5260
5260
  })), u.length && (g.push({
@@ -5287,7 +5287,7 @@ class Le {
5287
5287
  };
5288
5288
  i && (v.onSubtitles = i), d.length && !e.avatarMute && (v.text = d, e.avatarMood && (v.mood = e.avatarMood), e.ttsLang && (v.lang = e.ttsLang), e.ttsVoice && (v.voice = e.ttsVoice), e.ttsRate && (v.rate = e.ttsRate), e.ttsVoice && (v.pitch = e.ttsPitch), e.ttsVolume && (v.volume = e.ttsVolume)), this.speechQueue.push(v), d = [], l = "", c = 0, g = [];
5289
5289
  }
5290
- if (M) {
5290
+ if (E) {
5291
5291
  let v = this.animEmojis[f[x]];
5292
5292
  v && v.link && (v = this.animEmojis[v.link]), v && this.speechQueue.push({ emoji: v });
5293
5293
  }
@@ -5385,10 +5385,10 @@ class Le {
5385
5385
  let f = 0.6 + this.convertRange(g, [0, u], [0, 0.4]);
5386
5386
  if (u = Math.min(u, c.visemes.length * 200), d > 0)
5387
5387
  for (let x = 0; x < c.visemes.length; x++) {
5388
- const I = a + c.times[x] / d * u, C = c.durations[x] / d * u;
5388
+ const I = a + c.times[x] / d * u, H = c.durations[x] / d * u;
5389
5389
  o.push({
5390
5390
  template: { name: "viseme" },
5391
- ts: [I - Math.min(60, 2 * C / 3), I + Math.min(25, C / 2), I + C + Math.min(60, C / 2)],
5391
+ ts: [I - Math.min(60, 2 * H / 3), I + Math.min(25, H / 2), I + H + Math.min(60, H / 2)],
5392
5392
  vs: {
5393
5393
  ["viseme_" + c.visemes[x]]: [null, c.visemes[x] === "PP" || c.visemes[x] === "FF" ? 0.9 : f, 0]
5394
5394
  }
@@ -5470,7 +5470,7 @@ class Le {
5470
5470
  s.lang = o, s.rate = Math.max(0.1, Math.min(10, r)), s.pitch = Math.max(0, Math.min(2, h)), s.volume = Math.max(0, Math.min(1, a));
5471
5471
  const u = speechSynthesis.getVoices(), l = t.voice || this.avatar.ttsVoice || this.opt.ttsVoice;
5472
5472
  if (l && u.length > 0) {
5473
- const p = u.find((M) => M.name.includes(l) || M.lang === o);
5473
+ const p = u.find((E) => E.name.includes(l) || E.lang === o);
5474
5474
  p && (s.voice = p);
5475
5475
  }
5476
5476
  const c = n.length * 100 / s.rate, d = this.audioCtx.createBuffer(1, this.audioCtx.sampleRate * (c / 1e3), this.audioCtx.sampleRate), g = this.avatar.lipsyncLang || this.opt.lipsyncLang || "en", f = this.lipsyncPreProcessText(n, g), x = this.lipsyncWordsToVisemes(f, g);
@@ -5485,8 +5485,8 @@ class Le {
5485
5485
  const I = [];
5486
5486
  if (x && x.visemes && x.visemes.length > 0) {
5487
5487
  const p = x.times[x.visemes.length - 1] + x.durations[x.visemes.length - 1];
5488
- for (let M = 0; M < x.visemes.length; M++) {
5489
- const z = x.visemes[M], v = x.times[M] / p, O = x.durations[M] / p, P = v * c, K = O * c;
5488
+ for (let E = 0; E < x.visemes.length; E++) {
5489
+ const z = x.visemes[E], v = x.times[E] / p, O = x.durations[E] / p, P = v * c, K = O * c;
5490
5490
  I.push({
5491
5491
  template: { name: "viseme" },
5492
5492
  ts: [P - Math.min(60, 2 * K / 3), P + Math.min(25, K / 2), P + K + Math.min(60, K / 2)],
@@ -5496,8 +5496,8 @@ class Le {
5496
5496
  });
5497
5497
  }
5498
5498
  }
5499
- const C = [...t.anim, ...I];
5500
- this.audioPlaylist.push({ anim: C, audio: d }), this.onSubtitles = t.onSubtitles || null, this.resetLips(), t.mood && this.setMood(t.mood), this.playAudio(), s.onend = () => {
5499
+ const H = [...t.anim, ...I];
5500
+ this.audioPlaylist.push({ anim: H, audio: d }), this.onSubtitles = t.onSubtitles || null, this.resetLips(), t.mood && this.setMood(t.mood), this.playAudio(), s.onend = () => {
5501
5501
  e();
5502
5502
  }, s.onerror = (p) => {
5503
5503
  console.error("Speech synthesis error:", p.error), i(p.error);
@@ -6050,10 +6050,10 @@ class Le {
6050
6050
  }
6051
6051
  this.objectLeftEye.updateMatrixWorld(!0), this.objectRightEye.updateMatrixWorld(!0), ie.setFromMatrixPosition(this.objectLeftEye.matrixWorld), oe.setFromMatrixPosition(this.objectRightEye.matrixWorld), ie.add(oe).divideScalar(2), N.copy(this.armature.quaternion), N.multiply(this.poseTarget.props["Hips.quaternion"]), N.multiply(this.poseTarget.props["Spine.quaternion"]), N.multiply(this.poseTarget.props["Spine1.quaternion"]), N.multiply(this.poseTarget.props["Spine2.quaternion"]), N.multiply(this.poseTarget.props["Neck.quaternion"]), N.multiply(this.poseTarget.props["Head.quaternion"]);
6052
6052
  const i = new y.Vector3().subVectors(e, ie).normalize(), n = Math.atan2(i.x, i.z), s = Math.asin(-i.y);
6053
- H.set(s, n, 0, "YXZ");
6054
- const r = new y.Quaternion().setFromEuler(H), h = new y.Quaternion().copy(r).multiply(N.clone().invert());
6055
- H.setFromQuaternion(h, "YXZ");
6056
- let a = H.x / (40 / 24) + 0.2, u = H.y / (9 / 4), l = Math.min(0.6, Math.max(-0.3, a)), c = Math.min(0.8, Math.max(-0.8, u)), d = (Math.random() - 0.5) / 4, g = (Math.random() - 0.5) / 4;
6053
+ T.set(s, n, 0, "YXZ");
6054
+ const r = new y.Quaternion().setFromEuler(T), h = new y.Quaternion().copy(r).multiply(N.clone().invert());
6055
+ T.setFromQuaternion(h, "YXZ");
6056
+ let a = T.x / (40 / 24) + 0.2, u = T.y / (9 / 4), l = Math.min(0.6, Math.max(-0.3, a)), c = Math.min(0.8, Math.max(-0.8, u)), d = (Math.random() - 0.5) / 4, g = (Math.random() - 0.5) / 4;
6057
6057
  if (t) {
6058
6058
  let f = this.animQueue.findIndex((I) => I.template.name === "lookat");
6059
6059
  f !== -1 && this.animQueue.splice(f, 1);
@@ -6088,20 +6088,20 @@ class Le {
6088
6088
  const s = new y.Vector3().setFromMatrixPosition(this.objectLeftEye.matrixWorld), o = new y.Vector3().setFromMatrixPosition(this.objectRightEye.matrixWorld), r = new y.Vector3().addVectors(s, o).divideScalar(2);
6089
6089
  r.project(this.camera);
6090
6090
  let h = (r.x + 1) / 2 * n.width + n.left, a = -(r.y - 1) / 2 * n.height + n.top;
6091
- t === null && (t = h), e === null && (e = a), N.copy(this.armature.quaternion), N.multiply(this.poseTarget.props["Hips.quaternion"]), N.multiply(this.poseTarget.props["Spine.quaternion"]), N.multiply(this.poseTarget.props["Spine1.quaternion"]), N.multiply(this.poseTarget.props["Spine2.quaternion"]), N.multiply(this.poseTarget.props["Neck.quaternion"]), N.multiply(this.poseTarget.props["Head.quaternion"]), H.setFromQuaternion(N);
6092
- let u = H.x / (40 / 24), l = H.y / (9 / 4), c = Math.min(0.4, Math.max(-0.4, this.camera.rotation.x)), d = Math.min(0.4, Math.max(-0.4, this.camera.rotation.y)), g = Math.max(window.innerWidth - h, h), f = Math.max(window.innerHeight - a, a), x = this.convertRange(e, [a - f, a + f], [-0.3, 0.6]) - u + c, I = this.convertRange(t, [h - g, h + g], [-0.8, 0.8]) - l + d;
6091
+ t === null && (t = h), e === null && (e = a), N.copy(this.armature.quaternion), N.multiply(this.poseTarget.props["Hips.quaternion"]), N.multiply(this.poseTarget.props["Spine.quaternion"]), N.multiply(this.poseTarget.props["Spine1.quaternion"]), N.multiply(this.poseTarget.props["Spine2.quaternion"]), N.multiply(this.poseTarget.props["Neck.quaternion"]), N.multiply(this.poseTarget.props["Head.quaternion"]), T.setFromQuaternion(N);
6092
+ let u = T.x / (40 / 24), l = T.y / (9 / 4), c = Math.min(0.4, Math.max(-0.4, this.camera.rotation.x)), d = Math.min(0.4, Math.max(-0.4, this.camera.rotation.y)), g = Math.max(window.innerWidth - h, h), f = Math.max(window.innerHeight - a, a), x = this.convertRange(e, [a - f, a + f], [-0.3, 0.6]) - u + c, I = this.convertRange(t, [h - g, h + g], [-0.8, 0.8]) - l + d;
6093
6093
  x = Math.min(0.6, Math.max(-0.3, x)), I = Math.min(0.8, Math.max(-0.8, I));
6094
- let C = (Math.random() - 0.5) / 4, p = (Math.random() - 0.5) / 4;
6094
+ let H = (Math.random() - 0.5) / 4, p = (Math.random() - 0.5) / 4;
6095
6095
  if (i) {
6096
- let M = this.animQueue.findIndex((v) => v.template.name === "lookat");
6097
- M !== -1 && this.animQueue.splice(M, 1);
6096
+ let E = this.animQueue.findIndex((v) => v.template.name === "lookat");
6097
+ E !== -1 && this.animQueue.splice(E, 1);
6098
6098
  const z = {
6099
6099
  name: "lookat",
6100
6100
  dt: [750, i],
6101
6101
  vs: {
6102
- bodyRotateX: [x + C],
6102
+ bodyRotateX: [x + H],
6103
6103
  bodyRotateY: [I + p],
6104
- eyesRotateX: [-3 * C + 0.1],
6104
+ eyesRotateX: [-3 * H + 0.1],
6105
6105
  eyesRotateY: [-5 * p],
6106
6106
  browInnerUp: [[0, 0.7]],
6107
6107
  mouthLeft: [[0, 0.7]],
@@ -6479,8 +6479,8 @@ class Le {
6479
6479
  const x = t.iterations || 10;
6480
6480
  if (e)
6481
6481
  for (let I = 0; I < x; I++) {
6482
- let C = !1;
6483
- for (let p = 0, M = f.length; p < M; p++) {
6482
+ let H = !1;
6483
+ for (let p = 0, E = f.length; p < E; p++) {
6484
6484
  const z = f[p].bone;
6485
6485
  z.matrixWorld.decompose(h, a, u), a.invert(), o.setFromMatrixPosition(g.matrixWorld), r.subVectors(o, h), r.applyQuaternion(a), r.normalize(), s.subVectors(e, h), s.applyQuaternion(a), s.normalize();
6486
6486
  let v = s.dot(r);
@@ -6492,9 +6492,9 @@ class Le {
6492
6492
  f[p].maxx !== void 0 ? f[p].maxx : 1 / 0,
6493
6493
  f[p].maxy !== void 0 ? f[p].maxy : 1 / 0,
6494
6494
  f[p].maxz !== void 0 ? f[p].maxz : 1 / 0
6495
- ))), z.updateMatrixWorld(!0), C = !0);
6495
+ ))), z.updateMatrixWorld(!0), H = !0);
6496
6496
  }
6497
- if (!C) break;
6497
+ if (!H) break;
6498
6498
  }
6499
6499
  n && f.forEach((I) => {
6500
6500
  this.poseTarget.props[I.link + ".quaternion"].copy(I.bone.quaternion), this.poseTarget.props[I.link + ".quaternion"].t = this.animClock, this.poseTarget.props[I.link + ".quaternion"].d = n;
@@ -6568,7 +6568,7 @@ const Se = ye(({
6568
6568
  style: f = {},
6569
6569
  animations: x = {}
6570
6570
  }, I) => {
6571
- const C = X(null), p = X(null), [M, z] = re(!0), [v, O] = re(null), [P, K] = re(!1), J = xe(), S = n || J.service;
6571
+ const H = X(null), p = X(null), [E, z] = re(!0), [v, O] = re(null), [P, K] = re(!1), J = xe(), S = n || J.service;
6572
6572
  let F;
6573
6573
  S === "browser" ? F = {
6574
6574
  service: "browser",
@@ -6602,10 +6602,10 @@ const Se = ye(({
6602
6602
  ttsService: S,
6603
6603
  lipsyncModules: ["en"],
6604
6604
  cameraView: u
6605
- }, j = T(async () => {
6606
- if (!(!C.current || p.current))
6605
+ }, j = M(async () => {
6606
+ if (!(!H.current || p.current))
6607
6607
  try {
6608
- if (z(!0), O(null), p.current = new Le(C.current, V), 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, (U) => {
6608
+ if (z(!0), O(null), p.current = new Le(H.current, V), 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, (U) => {
6609
6609
  if (U.lengthComputable) {
6610
6610
  const D = Math.min(100, Math.round(U.loaded / U.total * 100));
6611
6611
  c(D);
@@ -6622,11 +6622,11 @@ const Se = ye(({
6622
6622
  console.warn("Error setting full body mode on initialization:", U);
6623
6623
  }
6624
6624
  p.current && p.current.controls && (p.current.controls.enableRotate = !1, p.current.controls.enableZoom = !1, p.current.controls.enablePan = !1, p.current.controls.enableDamping = !1, p.current.controls.update()), z(!1), K(!0), l(p.current);
6625
- const E = () => {
6625
+ const C = () => {
6626
6626
  document.visibilityState === "visible" ? p.current?.start() : p.current?.stop();
6627
6627
  };
6628
- return document.addEventListener("visibilitychange", E), () => {
6629
- document.removeEventListener("visibilitychange", E);
6628
+ return document.addEventListener("visibilitychange", C), () => {
6629
+ document.removeEventListener("visibilitychange", C);
6630
6630
  };
6631
6631
  } catch (L) {
6632
6632
  console.error("Error initializing TalkingHead:", L), O(L.message || "Failed to initialize avatar"), z(!1), d(L);
@@ -6635,42 +6635,42 @@ const Se = ye(({
6635
6635
  he(() => (j(), () => {
6636
6636
  p.current && (p.current.stop(), p.current.dispose(), p.current = null);
6637
6637
  }), [j]), he(() => {
6638
- if (!C.current || !p.current) return;
6638
+ if (!H.current || !p.current) return;
6639
6639
  const L = new ResizeObserver((U) => {
6640
6640
  for (const D of U)
6641
6641
  p.current && p.current.onResize && p.current.onResize();
6642
6642
  });
6643
- L.observe(C.current);
6644
- const E = () => {
6643
+ L.observe(H.current);
6644
+ const C = () => {
6645
6645
  p.current && p.current.onResize && p.current.onResize();
6646
6646
  };
6647
- return window.addEventListener("resize", E), () => {
6648
- L.disconnect(), window.removeEventListener("resize", E);
6647
+ return window.addEventListener("resize", C), () => {
6648
+ L.disconnect(), window.removeEventListener("resize", C);
6649
6649
  };
6650
6650
  }, [P]);
6651
- const Q = T(async () => {
6651
+ const Q = M(async () => {
6652
6652
  if (p.current && p.current.audioCtx)
6653
6653
  try {
6654
6654
  (p.current.audioCtx.state === "suspended" || p.current.audioCtx.state === "interrupted") && (await p.current.audioCtx.resume(), console.log("Audio context resumed"));
6655
6655
  } catch (L) {
6656
6656
  console.warn("Failed to resume audio context:", L);
6657
6657
  }
6658
- }, []), le = T(async (L, E = {}) => {
6658
+ }, []), le = M(async (L, C = {}) => {
6659
6659
  if (p.current && P)
6660
6660
  try {
6661
6661
  await Q();
6662
6662
  const U = {
6663
- ...E,
6664
- lipsyncLang: E.lipsyncLang || W.lipsyncLang || "en"
6663
+ ...C,
6664
+ lipsyncLang: C.lipsyncLang || W.lipsyncLang || "en"
6665
6665
  };
6666
- if (E.onSpeechEnd && p.current) {
6666
+ if (C.onSpeechEnd && p.current) {
6667
6667
  const D = p.current, ne = D.onAudioEnd;
6668
6668
  let $ = null, pe = 0;
6669
6669
  const we = 600, ze = () => {
6670
6670
  if (pe++, pe > we) {
6671
6671
  $ && (clearInterval($), $ = null);
6672
6672
  try {
6673
- E.onSpeechEnd();
6673
+ C.onSpeechEnd();
6674
6674
  } catch (ge) {
6675
6675
  console.error("Error in onSpeechEnd callback:", ge);
6676
6676
  }
@@ -6678,7 +6678,7 @@ const Se = ye(({
6678
6678
  }
6679
6679
  D && (!D.isSpeaking || D.isSpeaking === !1) && (!D.audioPlaylist || D.audioPlaylist.length === 0) && (!D.isAudioPlaying || D.isAudioPlaying === !1) && ($ && (clearInterval($), $ = null), setTimeout(() => {
6680
6680
  try {
6681
- E.onSpeechEnd();
6681
+ C.onSpeechEnd();
6682
6682
  } catch (ge) {
6683
6683
  console.error("Error in onSpeechEnd callback:", ge);
6684
6684
  }
@@ -6694,13 +6694,13 @@ const Se = ye(({
6694
6694
  } catch (U) {
6695
6695
  console.error("Error speaking text:", U), O(U.message || "Failed to speak text");
6696
6696
  }
6697
- }, [P, Q, W.lipsyncLang]), me = T(() => {
6697
+ }, [P, Q, W.lipsyncLang]), me = M(() => {
6698
6698
  p.current && (p.current.stopSpeaking(), p.current.setSlowdownRate && p.current.setSlowdownRate(1));
6699
- }, []), q = T((L) => {
6699
+ }, []), q = M((L) => {
6700
6700
  p.current && p.current.setMood(L);
6701
- }, []), R = T((L) => {
6701
+ }, []), b = M((L) => {
6702
6702
  p.current && p.current.setSlowdownRate && p.current.setSlowdownRate(L);
6703
- }, []), b = T((L, E = !1) => {
6703
+ }, []), R = M((L, C = !1) => {
6704
6704
  if (p.current && p.current.playAnimation) {
6705
6705
  if (x && x[L] && (L = x[L]), p.current.setShowFullAvatar)
6706
6706
  try {
@@ -6710,7 +6710,7 @@ const Se = ye(({
6710
6710
  }
6711
6711
  if (L.includes("."))
6712
6712
  try {
6713
- p.current.playAnimation(L, null, 10, 0, 0.01, E);
6713
+ p.current.playAnimation(L, null, 10, 0, 0.01, C);
6714
6714
  } catch (D) {
6715
6715
  console.warn(`Failed to play ${L}:`, D);
6716
6716
  try {
@@ -6724,7 +6724,7 @@ const Se = ye(({
6724
6724
  let ne = !1;
6725
6725
  for (const $ of D)
6726
6726
  try {
6727
- p.current.playAnimation(L + $, null, 10, 0, 0.01, E), ne = !0;
6727
+ p.current.playAnimation(L + $, null, 10, 0, 0.01, C), ne = !0;
6728
6728
  break;
6729
6729
  } catch {
6730
6730
  }
@@ -6738,7 +6738,7 @@ const Se = ye(({
6738
6738
  }
6739
6739
  }
6740
6740
  }
6741
- }, [x]), w = T(() => {
6741
+ }, [x]), w = M(() => {
6742
6742
  p.current && p.current.onResize && p.current.onResize();
6743
6743
  }, []);
6744
6744
  return fe(I, () => ({
@@ -6746,8 +6746,8 @@ const Se = ye(({
6746
6746
  stopSpeaking: me,
6747
6747
  resumeAudioContext: Q,
6748
6748
  setMood: q,
6749
- setTimingAdjustment: R,
6750
- playAnimation: b,
6749
+ setTimingAdjustment: b,
6750
+ playAnimation: R,
6751
6751
  isReady: P,
6752
6752
  talkingHead: p.current,
6753
6753
  handleResize: w,
@@ -6755,8 +6755,8 @@ const Se = ye(({
6755
6755
  if (p.current && p.current.setShowFullAvatar && p.current.setBodyMovement)
6756
6756
  try {
6757
6757
  p.current.setShowFullAvatar(!0), p.current.setBodyMovement(L);
6758
- } catch (E) {
6759
- console.warn("Error setting body movement:", E);
6758
+ } catch (C) {
6759
+ console.warn("Error setting body movement:", C);
6760
6760
  }
6761
6761
  },
6762
6762
  setMovementIntensity: (L) => p.current?.setMovementIntensity(L),
@@ -6772,8 +6772,8 @@ const Se = ye(({
6772
6772
  if (p.current && p.current.setShowFullAvatar && p.current.playReaction)
6773
6773
  try {
6774
6774
  p.current.setShowFullAvatar(!0), p.current.playReaction(L);
6775
- } catch (E) {
6776
- console.warn("Error playing reaction:", E);
6775
+ } catch (C) {
6776
+ console.warn("Error playing reaction:", C);
6777
6777
  }
6778
6778
  },
6779
6779
  playCelebration: () => {
@@ -6788,8 +6788,8 @@ const Se = ye(({
6788
6788
  if (p.current && p.current.setShowFullAvatar)
6789
6789
  try {
6790
6790
  p.current.setShowFullAvatar(L);
6791
- } catch (E) {
6792
- console.warn("Error setting showFullAvatar:", E);
6791
+ } catch (C) {
6792
+ console.warn("Error setting showFullAvatar:", C);
6793
6793
  }
6794
6794
  },
6795
6795
  lockAvatarPosition: () => {
@@ -6822,7 +6822,7 @@ const Se = ye(({
6822
6822
  /* @__PURE__ */ se(
6823
6823
  "div",
6824
6824
  {
6825
- ref: C,
6825
+ ref: H,
6826
6826
  className: "talking-head-viewer",
6827
6827
  style: {
6828
6828
  width: "100%",
@@ -6831,7 +6831,7 @@ const Se = ye(({
6831
6831
  }
6832
6832
  }
6833
6833
  ),
6834
- M && /* @__PURE__ */ se("div", { className: "loading-overlay", style: {
6834
+ E && /* @__PURE__ */ se("div", { className: "loading-overlay", style: {
6835
6835
  position: "absolute",
6836
6836
  top: "50%",
6837
6837
  left: "50%",
@@ -6869,7 +6869,7 @@ const Je = ye(({
6869
6869
  style: s = {},
6870
6870
  avatarConfig: o = {}
6871
6871
  }, r) => {
6872
- const h = X(null), a = X(null), [u, l] = re(!0), [c, d] = re(null), [g, f] = re(!1), x = xe(), I = o.ttsService || x.service, C = I === "browser" ? {
6872
+ const h = X(null), a = X(null), [u, l] = re(!0), [c, d] = re(null), [g, f] = re(!1), x = xe(), I = o.ttsService || x.service, H = I === "browser" ? {
6873
6873
  endpoint: "",
6874
6874
  apiKey: null,
6875
6875
  defaultVoice: "Google US English"
@@ -6885,7 +6885,7 @@ const Je = ye(({
6885
6885
  body: "F",
6886
6886
  avatarMood: "neutral",
6887
6887
  ttsLang: I === "browser" ? "en-US" : "en",
6888
- ttsVoice: o.ttsVoice || C.defaultVoice,
6888
+ ttsVoice: o.ttsVoice || H.defaultVoice,
6889
6889
  lipsyncLang: "en",
6890
6890
  // English lip-sync
6891
6891
  showFullAvatar: !0,
@@ -6893,16 +6893,16 @@ const Je = ye(({
6893
6893
  bodyMovement: "idle",
6894
6894
  movementIntensity: 0.5,
6895
6895
  ...o
6896
- }, M = {
6897
- ttsEndpoint: C.endpoint,
6898
- ttsApikey: C.apiKey,
6896
+ }, E = {
6897
+ ttsEndpoint: H.endpoint,
6898
+ ttsApikey: H.apiKey,
6899
6899
  ttsService: I,
6900
6900
  lipsyncModules: ["en"],
6901
6901
  cameraView: "upper"
6902
- }, z = T(async () => {
6902
+ }, z = M(async () => {
6903
6903
  if (!(!h.current || a.current))
6904
6904
  try {
6905
- if (l(!0), d(null), a.current = new Le(h.current, M), await a.current.showAvatar(p, (W) => {
6905
+ if (l(!0), d(null), a.current = new Le(h.current, E), await a.current.showAvatar(p, (W) => {
6906
6906
  if (W.lengthComputable) {
6907
6907
  const V = Math.min(100, Math.round(W.loaded / W.total * 100));
6908
6908
  t(V);
@@ -6938,7 +6938,7 @@ const Je = ye(({
6938
6938
  he(() => (z(), () => {
6939
6939
  a.current && (a.current.stop(), a.current.dispose(), a.current = null);
6940
6940
  }), [z]);
6941
- const v = T((S) => {
6941
+ const v = M((S) => {
6942
6942
  if (a.current && g)
6943
6943
  try {
6944
6944
  console.log("Speaking text:", S), console.log("Avatar config:", p), console.log("TalkingHead instance:", a.current), a.current.lipsync && Object.keys(a.current.lipsync).length > 0 ? (console.log("Lip-sync modules loaded:", Object.keys(a.current.lipsync)), a.current.setSlowdownRate && (a.current.setSlowdownRate(1.05), console.log("Applied timing adjustment for better lip-sync")), a.current.speakText(S)) : (console.warn("Lip-sync modules not ready, waiting..."), setTimeout(() => {
@@ -6949,13 +6949,13 @@ const Je = ye(({
6949
6949
  }
6950
6950
  else
6951
6951
  console.warn("Avatar not ready for speaking. isReady:", g, "talkingHeadRef:", !!a.current);
6952
- }, [g, p]), O = T(() => {
6952
+ }, [g, p]), O = M(() => {
6953
6953
  a.current && (a.current.stopSpeaking(), a.current.setSlowdownRate && (a.current.setSlowdownRate(1), console.log("Reset timing to normal")));
6954
- }, []), P = T((S) => {
6954
+ }, []), P = M((S) => {
6955
6955
  a.current && a.current.setMood(S);
6956
- }, []), K = T((S) => {
6956
+ }, []), K = M((S) => {
6957
6957
  a.current && a.current.setSlowdownRate && (a.current.setSlowdownRate(S), console.log("Timing adjustment set to:", S));
6958
- }, []), J = T((S, F = !1) => {
6958
+ }, []), J = M((S, F = !1) => {
6959
6959
  if (a.current && a.current.playAnimation) {
6960
6960
  if (a.current.setShowFullAvatar)
6961
6961
  try {
@@ -7130,7 +7130,7 @@ const $e = ye(({
7130
7130
  onQuestionAnswer: s,
7131
7131
  onCurriculumComplete: o,
7132
7132
  onCustomAction: r
7133
- }), d = X(null), g = X(null), f = X(null), x = X(null), I = X(null), C = X(null), p = X(null), M = X(B?.curriculum || {
7133
+ }), d = X(null), g = X(null), f = X(null), x = X(null), I = X(null), H = X(null), p = X(null), E = X(B?.curriculum || {
7134
7134
  title: "Default Curriculum",
7135
7135
  description: "No curriculum data provided",
7136
7136
  language: "en",
@@ -7158,7 +7158,7 @@ const $e = ye(({
7158
7158
  onCustomAction: r
7159
7159
  };
7160
7160
  }, [i, n, s, o, r]), he(() => {
7161
- M.current = B?.curriculum || {
7161
+ E.current = B?.curriculum || {
7162
7162
  title: "Default Curriculum",
7163
7163
  description: "No curriculum data provided",
7164
7164
  language: "en",
@@ -7178,23 +7178,23 @@ const $e = ye(({
7178
7178
  lipsyncLang: "en"
7179
7179
  };
7180
7180
  }, [B, t, e]);
7181
- const v = T(() => (M.current || { modules: [] }).modules[l.current.currentModuleIndex]?.lessons[l.current.currentLessonIndex], []), O = T(() => v()?.questions[l.current.currentQuestionIndex], [v]), P = T((R, b) => b.type === "multiple_choice" || b.type === "true_false" ? R === b.answer : b.type === "code_test" && typeof R == "object" && R !== null ? R.passed === !0 : !1, []), K = T(() => {
7181
+ const v = M(() => (E.current || { modules: [] }).modules[l.current.currentModuleIndex]?.lessons[l.current.currentLessonIndex], []), O = M(() => v()?.questions[l.current.currentQuestionIndex], [v]), P = M((b, R) => R.type === "multiple_choice" || R.type === "true_false" ? b === R.answer : R.type === "code_test" && typeof b == "object" && b !== null ? b.passed === !0 : !1, []), K = M(() => {
7182
7182
  l.current.lessonCompleted = !0, l.current.isQuestionMode = !1;
7183
- const R = l.current.totalQuestions > 0 ? Math.round(l.current.score / l.current.totalQuestions * 100) : 100;
7184
- let b = "Congratulations! You've completed this lesson";
7185
- if (l.current.totalQuestions > 0 ? b += ` with a score of ${l.current.score} out of ${l.current.totalQuestions} (${R}%). ` : b += "! ", R >= 80 ? b += "Excellent work! You have a great understanding of this topic." : R >= 60 ? b += "Good job! You understand most of the concepts." : b += "Keep practicing! You're making progress.", c.current.onLessonComplete({
7183
+ const b = l.current.totalQuestions > 0 ? Math.round(l.current.score / l.current.totalQuestions * 100) : 100;
7184
+ let R = "Congratulations! You've completed this lesson";
7185
+ if (l.current.totalQuestions > 0 ? R += ` with a score of ${l.current.score} out of ${l.current.totalQuestions} (${b}%). ` : R += "! ", b >= 80 ? R += "Excellent work! You have a great understanding of this topic." : b >= 60 ? R += "Good job! You understand most of the concepts." : R += "Keep practicing! You're making progress.", c.current.onLessonComplete({
7186
7186
  moduleIndex: l.current.currentModuleIndex,
7187
7187
  lessonIndex: l.current.currentLessonIndex,
7188
7188
  score: l.current.score,
7189
7189
  totalQuestions: l.current.totalQuestions,
7190
- percentage: R
7190
+ percentage: b
7191
7191
  }), c.current.onCustomAction({
7192
7192
  type: "lessonComplete",
7193
7193
  moduleIndex: l.current.currentModuleIndex,
7194
7194
  lessonIndex: l.current.currentLessonIndex,
7195
7195
  score: l.current.score,
7196
7196
  totalQuestions: l.current.totalQuestions,
7197
- percentage: R
7197
+ percentage: b
7198
7198
  }), u.current) {
7199
7199
  if (u.current.setMood("happy"), e.lessonComplete)
7200
7200
  try {
@@ -7202,15 +7202,15 @@ const $e = ye(({
7202
7202
  } catch {
7203
7203
  u.current.playCelebration();
7204
7204
  }
7205
- const w = M.current || { modules: [] }, L = w.modules[l.current.currentModuleIndex], E = l.current.currentLessonIndex < (L?.lessons?.length || 0) - 1, U = l.current.currentModuleIndex < (w.modules?.length || 0) - 1, D = E || U, ne = z.current || { lipsyncLang: "en" };
7206
- D ? u.current.speakText(b, {
7205
+ const w = E.current || { modules: [] }, L = w.modules[l.current.currentModuleIndex], C = l.current.currentLessonIndex < (L?.lessons?.length || 0) - 1, U = l.current.currentModuleIndex < (w.modules?.length || 0) - 1, D = C || U, ne = z.current || { lipsyncLang: "en" };
7206
+ D ? u.current.speakText(R, {
7207
7207
  lipsyncLang: ne.lipsyncLang,
7208
7208
  onSpeechEnd: () => {
7209
7209
  setTimeout(() => {
7210
7210
  g.current && g.current();
7211
7211
  }, 1e3);
7212
7212
  }
7213
- }) : u.current.speakText(b, {
7213
+ }) : u.current.speakText(R, {
7214
7214
  lipsyncLang: ne.lipsyncLang,
7215
7215
  onSpeechEnd: () => {
7216
7216
  setTimeout(() => {
@@ -7219,12 +7219,12 @@ const $e = ye(({
7219
7219
  }
7220
7220
  });
7221
7221
  }
7222
- }, [e.lessonComplete]), J = T(() => {
7222
+ }, [e.lessonComplete]), J = M(() => {
7223
7223
  l.current.curriculumCompleted = !0;
7224
- const R = M.current || { modules: [] };
7224
+ const b = E.current || { modules: [] };
7225
7225
  if (c.current.onCurriculumComplete({
7226
- modules: R.modules.length,
7227
- totalLessons: R.modules.reduce((b, w) => b + w.lessons.length, 0)
7226
+ modules: b.modules.length,
7227
+ totalLessons: b.modules.reduce((R, w) => R + w.lessons.length, 0)
7228
7228
  }), u.current) {
7229
7229
  if (u.current.setMood("celebrating"), e.curriculumComplete)
7230
7230
  try {
@@ -7232,21 +7232,21 @@ const $e = ye(({
7232
7232
  } catch {
7233
7233
  u.current.playCelebration();
7234
7234
  }
7235
- const b = z.current || { lipsyncLang: "en" };
7236
- u.current.speakText("Amazing! You've completed the entire curriculum! You're now ready to move on to more advanced topics. Well done!", { lipsyncLang: b.lipsyncLang });
7235
+ const R = z.current || { lipsyncLang: "en" };
7236
+ u.current.speakText("Amazing! You've completed the entire curriculum! You're now ready to move on to more advanced topics. Well done!", { lipsyncLang: R.lipsyncLang });
7237
7237
  }
7238
- }, [e.curriculumComplete]), S = T(() => {
7239
- const R = v();
7240
- l.current.isQuestionMode = !0, l.current.currentQuestionIndex = 0, l.current.totalQuestions = R?.questions?.length || 0;
7241
- const b = O();
7242
- if (b && c.current.onCustomAction({
7238
+ }, [e.curriculumComplete]), S = M(() => {
7239
+ const b = v();
7240
+ l.current.isQuestionMode = !0, l.current.currentQuestionIndex = 0, l.current.totalQuestions = b?.questions?.length || 0;
7241
+ const R = O();
7242
+ if (R && c.current.onCustomAction({
7243
7243
  type: "questionStart",
7244
7244
  moduleIndex: l.current.currentModuleIndex,
7245
7245
  lessonIndex: l.current.currentLessonIndex,
7246
7246
  questionIndex: l.current.currentQuestionIndex,
7247
7247
  totalQuestions: l.current.totalQuestions,
7248
- question: b
7249
- }), u.current && b) {
7248
+ question: R
7249
+ }), u.current && R) {
7250
7250
  if (u.current.setMood("curious"), e.questionStart)
7251
7251
  try {
7252
7252
  u.current.playAnimation(e.questionStart, !0);
@@ -7254,24 +7254,24 @@ const $e = ye(({
7254
7254
  console.warn("Failed to play questionStart animation:", L);
7255
7255
  }
7256
7256
  const w = z.current || { lipsyncLang: "en" };
7257
- b.type === "code_test" ? u.current.speakText(`Let's test your coding skills! Here's your first challenge: ${b.question}`, { lipsyncLang: w.lipsyncLang }) : b.type === "multiple_choice" ? u.current.speakText(`Now let me ask you some questions. Here's the first one: ${b.question}`, { lipsyncLang: w.lipsyncLang }) : b.type === "true_false" ? u.current.speakText(`Let's start with some true or false questions. First question: ${b.question}`, { lipsyncLang: w.lipsyncLang }) : u.current.speakText(`Now let me ask you some questions. Here's the first one: ${b.question}`, { lipsyncLang: w.lipsyncLang });
7257
+ R.type === "code_test" ? u.current.speakText(`Let's test your coding skills! Here's your first challenge: ${R.question}`, { lipsyncLang: w.lipsyncLang }) : R.type === "multiple_choice" ? u.current.speakText(`Now let me ask you some questions. Here's the first one: ${R.question}`, { lipsyncLang: w.lipsyncLang }) : R.type === "true_false" ? u.current.speakText(`Let's start with some true or false questions. First question: ${R.question}`, { lipsyncLang: w.lipsyncLang }) : u.current.speakText(`Now let me ask you some questions. Here's the first one: ${R.question}`, { lipsyncLang: w.lipsyncLang });
7258
7258
  } else if (u.current) {
7259
7259
  const w = z.current || { lipsyncLang: "en" };
7260
7260
  u.current.speakText("Now let me ask you some questions to test your understanding.", { lipsyncLang: w.lipsyncLang });
7261
7261
  }
7262
- }, [e.questionStart, v, O]), F = T(() => {
7263
- const R = v();
7264
- if (l.current.currentQuestionIndex < (R?.questions?.length || 0) - 1) {
7262
+ }, [e.questionStart, v, O]), F = M(() => {
7263
+ const b = v();
7264
+ if (l.current.currentQuestionIndex < (b?.questions?.length || 0) - 1) {
7265
7265
  l.current.currentQuestionIndex += 1;
7266
- const b = O();
7267
- if (b && c.current.onCustomAction({
7266
+ const R = O();
7267
+ if (R && c.current.onCustomAction({
7268
7268
  type: "nextQuestion",
7269
7269
  moduleIndex: l.current.currentModuleIndex,
7270
7270
  lessonIndex: l.current.currentLessonIndex,
7271
7271
  questionIndex: l.current.currentQuestionIndex,
7272
7272
  totalQuestions: l.current.totalQuestions,
7273
- question: b
7274
- }), u.current && b) {
7273
+ question: R
7274
+ }), u.current && R) {
7275
7275
  if (u.current.setMood("happy"), u.current.setBodyMovement("idle"), e.nextQuestion)
7276
7276
  try {
7277
7277
  u.current.playAnimation(e.nextQuestion, !0);
@@ -7279,73 +7279,79 @@ const $e = ye(({
7279
7279
  console.warn("Failed to play nextQuestion animation:", L);
7280
7280
  }
7281
7281
  const w = z.current || { lipsyncLang: "en" };
7282
- b.type === "code_test" ? u.current.speakText(`Great! Now let's move on to your next coding challenge: ${b.question}`, {
7282
+ R.type === "code_test" ? u.current.speakText(`Great! Now let's move on to your next coding challenge: ${R.question}`, {
7283
7283
  lipsyncLang: w.lipsyncLang
7284
- }) : b.type === "multiple_choice" ? u.current.speakText(`Alright! Here's your next question: ${b.question}`, {
7284
+ }) : R.type === "multiple_choice" ? u.current.speakText(`Alright! Here's your next question: ${R.question}`, {
7285
7285
  lipsyncLang: w.lipsyncLang
7286
- }) : b.type === "true_false" ? u.current.speakText(`Now let's try this one: ${b.question}`, {
7286
+ }) : R.type === "true_false" ? u.current.speakText(`Now let's try this one: ${R.question}`, {
7287
7287
  lipsyncLang: w.lipsyncLang
7288
- }) : u.current.speakText(`Here's the next question: ${b.question}`, {
7288
+ }) : u.current.speakText(`Here's the next question: ${R.question}`, {
7289
7289
  lipsyncLang: w.lipsyncLang
7290
7290
  });
7291
7291
  }
7292
7292
  } else
7293
7293
  f.current && f.current();
7294
- }, [e.nextQuestion, v, O]), W = T(() => {
7295
- const R = M.current || { modules: [] }, b = R.modules[l.current.currentModuleIndex];
7296
- l.current.currentLessonIndex < (b?.lessons?.length || 0) - 1 ? (l.current.currentLessonIndex += 1, l.current.currentQuestionIndex = 0, l.current.lessonCompleted = !1, l.current.isQuestionMode = !1, l.current.isTeaching = !1, l.current.score = 0, l.current.totalQuestions = 0, c.current.onCustomAction({
7294
+ }, [e.nextQuestion, v, O]), W = M(() => {
7295
+ const b = E.current || { modules: [] }, R = b.modules[l.current.currentModuleIndex];
7296
+ l.current.currentLessonIndex < (R?.lessons?.length || 0) - 1 ? (l.current.currentLessonIndex += 1, l.current.currentQuestionIndex = 0, l.current.lessonCompleted = !1, l.current.isQuestionMode = !1, l.current.isTeaching = !1, l.current.score = 0, l.current.totalQuestions = 0, c.current.onCustomAction({
7297
7297
  type: "lessonStart",
7298
7298
  moduleIndex: l.current.currentModuleIndex,
7299
7299
  lessonIndex: l.current.currentLessonIndex
7300
7300
  }), u.current && (u.current.setMood("happy"), u.current.setBodyMovement("idle"), setTimeout(() => {
7301
7301
  d.current && d.current();
7302
- }, 500))) : l.current.currentModuleIndex < (R.modules?.length || 0) - 1 ? (l.current.currentModuleIndex += 1, l.current.currentLessonIndex = 0, l.current.currentQuestionIndex = 0, l.current.lessonCompleted = !1, l.current.isQuestionMode = !1, l.current.isTeaching = !1, l.current.score = 0, l.current.totalQuestions = 0, c.current.onCustomAction({
7302
+ }, 500))) : l.current.currentModuleIndex < (b.modules?.length || 0) - 1 ? (l.current.currentModuleIndex += 1, l.current.currentLessonIndex = 0, l.current.currentQuestionIndex = 0, l.current.lessonCompleted = !1, l.current.isQuestionMode = !1, l.current.isTeaching = !1, l.current.score = 0, l.current.totalQuestions = 0, c.current.onCustomAction({
7303
7303
  type: "lessonStart",
7304
7304
  moduleIndex: l.current.currentModuleIndex,
7305
7305
  lessonIndex: l.current.currentLessonIndex
7306
7306
  }), u.current && (u.current.setMood("happy"), u.current.setBodyMovement("idle"), setTimeout(() => {
7307
7307
  d.current && d.current();
7308
7308
  }, 500))) : I.current && I.current();
7309
- }, []), V = T(() => {
7310
- const R = v();
7311
- if (u.current && u.current.isReady && R?.avatar_script) {
7309
+ }, []), V = M(() => {
7310
+ const b = v();
7311
+ let R = null;
7312
+ if (b?.avatar_script && b?.body) {
7313
+ const w = b.avatar_script.trim(), L = b.body.trim(), C = w.match(/[.!?]$/) ? " " : ". ";
7314
+ R = `${w}${C}${L}`;
7315
+ } else
7316
+ R = b?.avatar_script || b?.body || null;
7317
+ if (u.current && u.current.isReady && R) {
7312
7318
  l.current.isTeaching = !0, l.current.isQuestionMode = !1, u.current.setMood("happy");
7313
- let b = !1;
7319
+ let w = !1;
7314
7320
  if (e.teaching)
7315
7321
  try {
7316
- u.current.playAnimation(e.teaching, !0), b = !0;
7317
- } catch (L) {
7318
- console.warn("Failed to play teaching animation:", L);
7322
+ u.current.playAnimation(e.teaching, !0), w = !0;
7323
+ } catch (C) {
7324
+ console.warn("Failed to play teaching animation:", C);
7319
7325
  }
7320
- b || u.current.setBodyMovement("gesturing");
7321
- const w = z.current || { lipsyncLang: "en" };
7326
+ w || u.current.setBodyMovement("gesturing");
7327
+ const L = z.current || { lipsyncLang: "en" };
7322
7328
  c.current.onLessonStart({
7323
7329
  moduleIndex: l.current.currentModuleIndex,
7324
7330
  lessonIndex: l.current.currentLessonIndex,
7325
- lesson: R
7331
+ lesson: b
7326
7332
  }), c.current.onCustomAction({
7327
7333
  type: "teachingStart",
7328
7334
  moduleIndex: l.current.currentModuleIndex,
7329
7335
  lessonIndex: l.current.currentLessonIndex,
7330
- lesson: R
7331
- }), u.current.speakText(R.avatar_script, {
7332
- lipsyncLang: w.lipsyncLang,
7336
+ lesson: b
7337
+ }), u.current.speakText(R, {
7338
+ lipsyncLang: L.lipsyncLang,
7333
7339
  onSpeechEnd: () => {
7334
7340
  l.current.isTeaching = !1, setTimeout(() => {
7335
- R.questions && R.questions.length > 0 ? C.current && C.current() : f.current && f.current();
7341
+ b.questions && b.questions.length > 0 ? H.current && H.current() : f.current && f.current();
7336
7342
  }, 500);
7337
7343
  }
7338
7344
  });
7339
7345
  }
7340
- }, [e.teaching, v]), j = T((R) => {
7341
- const b = O(), w = P(R, b);
7346
+ }, [e.teaching, v]), j = M((b) => {
7347
+ const R = O(), w = P(b, R);
7342
7348
  if (w && (l.current.score += 1), c.current.onQuestionAnswer({
7343
7349
  moduleIndex: l.current.currentModuleIndex,
7344
7350
  lessonIndex: l.current.currentLessonIndex,
7345
7351
  questionIndex: l.current.currentQuestionIndex,
7346
- answer: R,
7352
+ answer: b,
7347
7353
  isCorrect: w,
7348
- question: b
7354
+ question: R
7349
7355
  }), u.current)
7350
7356
  if (w) {
7351
7357
  if (u.current.setMood("happy"), e.correct)
@@ -7355,9 +7361,9 @@ const $e = ye(({
7355
7361
  u.current.setBodyMovement("happy");
7356
7362
  }
7357
7363
  u.current.setBodyMovement("gesturing");
7358
- const L = b.type === "code_test" ? `Great job! Your code passed all the tests! ${b.explanation || ""}` : `Excellent! That's correct! ${b.explanation || ""}`, E = z.current || { lipsyncLang: "en" };
7364
+ const L = R.type === "code_test" ? `Great job! Your code passed all the tests! ${R.explanation || ""}` : `Excellent! That's correct! ${R.explanation || ""}`, C = z.current || { lipsyncLang: "en" };
7359
7365
  u.current.speakText(L, {
7360
- lipsyncLang: E.lipsyncLang,
7366
+ lipsyncLang: C.lipsyncLang,
7361
7367
  onSpeechEnd: () => {
7362
7368
  setTimeout(() => {
7363
7369
  x.current && x.current();
@@ -7372,9 +7378,9 @@ const $e = ye(({
7372
7378
  u.current.setBodyMovement("idle");
7373
7379
  }
7374
7380
  u.current.setBodyMovement("gesturing");
7375
- const L = b.type === "code_test" ? `Your code didn't pass all the tests. ${b.explanation || "Try again!"}` : `Not quite right, but don't worry! ${b.explanation || ""} Let's move on to the next question.`, E = z.current || { lipsyncLang: "en" };
7381
+ const L = R.type === "code_test" ? `Your code didn't pass all the tests. ${R.explanation || "Try again!"}` : `Not quite right, but don't worry! ${R.explanation || ""} Let's move on to the next question.`, C = z.current || { lipsyncLang: "en" };
7376
7382
  u.current.speakText(L, {
7377
- lipsyncLang: E.lipsyncLang,
7383
+ lipsyncLang: C.lipsyncLang,
7378
7384
  onSpeechEnd: () => {
7379
7385
  setTimeout(() => {
7380
7386
  x.current && x.current();
@@ -7384,25 +7390,25 @@ const $e = ye(({
7384
7390
  }
7385
7391
  else
7386
7392
  x.current && x.current();
7387
- }, [e.correct, e.incorrect, O, P]), Q = T((R) => {
7388
- const b = O();
7389
- if (!R || typeof R != "object") {
7393
+ }, [e.correct, e.incorrect, O, P]), Q = M((b) => {
7394
+ const R = O();
7395
+ if (!b || typeof b != "object") {
7390
7396
  console.error("Invalid code test result format. Expected object with {passed: boolean, ...}");
7391
7397
  return;
7392
7398
  }
7393
- if (b?.type !== "code_test") {
7399
+ if (R?.type !== "code_test") {
7394
7400
  console.warn("Current question is not a code test. Use handleAnswerSelect for other question types.");
7395
7401
  return;
7396
7402
  }
7397
7403
  const w = {
7398
- passed: R.passed === !0,
7399
- results: R.results || [],
7400
- output: R.output || "",
7401
- error: R.error || null,
7402
- executionTime: R.executionTime || null,
7403
- testCount: R.testCount || 0,
7404
- passedCount: R.passedCount || 0,
7405
- failedCount: R.failedCount || 0
7404
+ passed: b.passed === !0,
7405
+ results: b.results || [],
7406
+ output: b.output || "",
7407
+ error: b.error || null,
7408
+ executionTime: b.executionTime || null,
7409
+ testCount: b.testCount || 0,
7410
+ passedCount: b.passedCount || 0,
7411
+ failedCount: b.failedCount || 0
7406
7412
  };
7407
7413
  c.current.onCustomAction({
7408
7414
  type: "codeTestSubmitted",
@@ -7410,19 +7416,19 @@ const $e = ye(({
7410
7416
  lessonIndex: l.current.currentLessonIndex,
7411
7417
  questionIndex: l.current.currentQuestionIndex,
7412
7418
  testResult: w,
7413
- question: b
7419
+ question: R
7414
7420
  }), p.current && p.current(w);
7415
- }, [O, P]), le = T(() => {
7421
+ }, [O, P]), le = M(() => {
7416
7422
  l.current.currentModuleIndex = 0, l.current.currentLessonIndex = 0, l.current.currentQuestionIndex = 0, l.current.isTeaching = !1, l.current.isQuestionMode = !1, l.current.lessonCompleted = !1, l.current.curriculumCompleted = !1, l.current.score = 0, l.current.totalQuestions = 0;
7417
- }, []), me = T((R) => {
7418
- console.log("Avatar is ready!", R);
7419
- const b = v();
7420
- h && b?.avatar_script && setTimeout(() => {
7423
+ }, []), me = M((b) => {
7424
+ console.log("Avatar is ready!", b);
7425
+ const R = v(), w = R?.avatar_script || R?.body;
7426
+ h && w && setTimeout(() => {
7421
7427
  d.current && d.current();
7422
7428
  }, 1e3);
7423
7429
  }, [h, v]);
7424
7430
  Ce(() => {
7425
- d.current = V, g.current = W, f.current = K, x.current = F, I.current = J, C.current = S, p.current = j;
7431
+ d.current = V, g.current = W, f.current = K, x.current = F, I.current = J, H.current = S, p.current = j;
7426
7432
  }), fe(a, () => ({
7427
7433
  // Curriculum control methods
7428
7434
  startTeaching: V,
@@ -7440,20 +7446,20 @@ const $e = ye(({
7440
7446
  // Direct access to avatar ref (always returns current value)
7441
7447
  getAvatarRef: () => u.current,
7442
7448
  // Convenience methods that delegate to avatar (always check current ref)
7443
- speakText: async (R, b = {}) => {
7449
+ speakText: async (b, R = {}) => {
7444
7450
  await u.current?.resumeAudioContext?.();
7445
7451
  const w = z.current || { lipsyncLang: "en" };
7446
- u.current?.speakText(R, { ...b, lipsyncLang: b.lipsyncLang || w.lipsyncLang });
7452
+ u.current?.speakText(b, { ...R, lipsyncLang: R.lipsyncLang || w.lipsyncLang });
7447
7453
  },
7448
7454
  resumeAudioContext: async () => {
7449
7455
  if (u.current?.resumeAudioContext)
7450
7456
  return await u.current.resumeAudioContext();
7451
- const R = u.current?.talkingHead;
7452
- if (R?.audioCtx) {
7453
- const b = R.audioCtx;
7454
- if (b.state === "suspended" || b.state === "interrupted")
7457
+ const b = u.current?.talkingHead;
7458
+ if (b?.audioCtx) {
7459
+ const R = b.audioCtx;
7460
+ if (R.state === "suspended" || R.state === "interrupted")
7455
7461
  try {
7456
- await b.resume(), console.log("Audio context resumed via talkingHead");
7462
+ await R.resume(), console.log("Audio context resumed via talkingHead");
7457
7463
  } catch (w) {
7458
7464
  console.warn("Failed to resume audio context:", w);
7459
7465
  }
@@ -7461,22 +7467,22 @@ const $e = ye(({
7461
7467
  console.warn("Audio context not available yet");
7462
7468
  },
7463
7469
  stopSpeaking: () => u.current?.stopSpeaking(),
7464
- setMood: (R) => u.current?.setMood(R),
7465
- playAnimation: (R, b) => u.current?.playAnimation(R, b),
7466
- setBodyMovement: (R) => u.current?.setBodyMovement(R),
7467
- setMovementIntensity: (R) => u.current?.setMovementIntensity(R),
7470
+ setMood: (b) => u.current?.setMood(b),
7471
+ playAnimation: (b, R) => u.current?.playAnimation(b, R),
7472
+ setBodyMovement: (b) => u.current?.setBodyMovement(b),
7473
+ setMovementIntensity: (b) => u.current?.setMovementIntensity(b),
7468
7474
  playRandomDance: () => u.current?.playRandomDance(),
7469
- playReaction: (R) => u.current?.playReaction(R),
7475
+ playReaction: (b) => u.current?.playReaction(b),
7470
7476
  playCelebration: () => u.current?.playCelebration(),
7471
- setShowFullAvatar: (R) => u.current?.setShowFullAvatar(R),
7472
- setTimingAdjustment: (R) => u.current?.setTimingAdjustment(R),
7477
+ setShowFullAvatar: (b) => u.current?.setShowFullAvatar(b),
7478
+ setTimingAdjustment: (b) => u.current?.setTimingAdjustment(b),
7473
7479
  lockAvatarPosition: () => u.current?.lockAvatarPosition(),
7474
7480
  unlockAvatarPosition: () => u.current?.unlockAvatarPosition(),
7475
7481
  // Custom action trigger
7476
- triggerCustomAction: (R, b) => {
7482
+ triggerCustomAction: (b, R) => {
7477
7483
  c.current.onCustomAction({
7478
- type: R,
7479
- ...b,
7484
+ type: b,
7485
+ ...R,
7480
7486
  state: { ...l.current }
7481
7487
  });
7482
7488
  },
@@ -7517,8 +7523,8 @@ const $e = ye(({
7517
7523
  onReady: me,
7518
7524
  onLoading: () => {
7519
7525
  },
7520
- onError: (R) => {
7521
- console.error("Avatar error:", R);
7526
+ onError: (b) => {
7527
+ console.error("Avatar error:", b);
7522
7528
  }
7523
7529
  }
7524
7530
  ) });