@sage-rsc/talking-head-react 1.0.54 → 1.0.56
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 +2 -2
- package/dist/index.js +67 -54
- package/package.json +1 -1
- package/src/components/CurriculumLearning.jsx +34 -9
- package/src/lib/talkinghead.mjs +2 -2
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
|
-
},
|
|
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:
|
|
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 ? (
|
|
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 && (
|
|
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),
|
|
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(
|
|
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),
|
|
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(),
|
|
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
|
|
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(
|
|
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 ||
|
|
6791
|
+
lipsyncLang: k.lipsyncLang || V.lipsyncLang || "en"
|
|
6792
6792
|
};
|
|
6793
6793
|
if (k.onSpeechEnd && p.current) {
|
|
6794
6794
|
const C = p.current;
|
|
6795
|
-
let
|
|
6795
|
+
let U = null, J = 0;
|
|
6796
6796
|
const ye = 1200;
|
|
6797
6797
|
let he = !1;
|
|
6798
|
-
|
|
6798
|
+
U = setInterval(() => {
|
|
6799
6799
|
if (J++, J > ye) {
|
|
6800
|
-
if (
|
|
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,
|
|
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,
|
|
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 (
|
|
6851
|
-
console.warn("Fallback animation also failed:",
|
|
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
|
|
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),
|
|
6859
|
+
p.current.playAnimation(v + J, null, 10, 0, 0.01, k), U = !0;
|
|
6860
6860
|
break;
|
|
6861
6861
|
} catch {
|
|
6862
6862
|
}
|
|
6863
|
-
if (!
|
|
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
|
|
7040
|
-
t(
|
|
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
|
|
7046
|
-
console.log("Viseme morph targets found:",
|
|
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
|
|
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(
|
|
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
|
-
|
|
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 (
|
|
7096
|
-
console.warn("Error setting full body mode:",
|
|
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 (
|
|
7102
|
-
console.log(`Failed to play ${S}:`,
|
|
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
|
|
7110
|
+
const V = [".fbx", ".glb", ".gltf"];
|
|
7111
7111
|
let _ = !1;
|
|
7112
|
-
for (const K of
|
|
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,
|
|
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:
|
|
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;
|
|
@@ -7405,7 +7406,7 @@ const nt = fe(({
|
|
|
7405
7406
|
}, [e.questionStart, I, B]), P = M(() => {
|
|
7406
7407
|
const b = I();
|
|
7407
7408
|
if (r.current.currentQuestionIndex < (b?.questions?.length || 0) - 1) {
|
|
7408
|
-
r.current.currentQuestionIndex += 1;
|
|
7409
|
+
h.current && h.current.stopSpeaking && h.current.stopSpeaking(), r.current.currentQuestionIndex += 1;
|
|
7409
7410
|
const R = B();
|
|
7410
7411
|
R && d.current.onCustomAction({
|
|
7411
7412
|
type: "nextQuestion",
|
|
@@ -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,
|
|
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:
|
|
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,
|
|
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
|
-
}, []),
|
|
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 <
|
|
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 <
|
|
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 <
|
|
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 =
|
|
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:
|
|
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
|
-
}), [
|
|
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
|
@@ -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
|
-
|
|
297
|
-
|
|
298
|
-
|
|
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
|
|
@@ -357,6 +360,11 @@ const CurriculumLearning = forwardRef(({
|
|
|
357
360
|
const nextQuestion = useCallback(() => {
|
|
358
361
|
const currentLesson = getCurrentLesson();
|
|
359
362
|
if (stateRef.current.currentQuestionIndex < (currentLesson?.questions?.length || 0) - 1) {
|
|
363
|
+
// Stop any current speech/feedback before skipping to next question
|
|
364
|
+
if (avatarRef.current && avatarRef.current.stopSpeaking) {
|
|
365
|
+
avatarRef.current.stopSpeaking();
|
|
366
|
+
}
|
|
367
|
+
|
|
360
368
|
stateRef.current.currentQuestionIndex += 1;
|
|
361
369
|
|
|
362
370
|
// Trigger custom action IMMEDIATELY for UI update (before avatar speaks)
|
|
@@ -369,7 +377,8 @@ const CurriculumLearning = forwardRef(({
|
|
|
369
377
|
lessonIndex: stateRef.current.currentLessonIndex,
|
|
370
378
|
questionIndex: stateRef.current.currentQuestionIndex,
|
|
371
379
|
totalQuestions: stateRef.current.totalQuestions,
|
|
372
|
-
question: nextQuestionObj
|
|
380
|
+
question: nextQuestionObj,
|
|
381
|
+
score: stateRef.current.score
|
|
373
382
|
});
|
|
374
383
|
}
|
|
375
384
|
|
|
@@ -554,6 +563,9 @@ const CurriculumLearning = forwardRef(({
|
|
|
554
563
|
if (avatarRef.current && avatarRef.current.isReady && teachingText) {
|
|
555
564
|
stateRef.current.isTeaching = true;
|
|
556
565
|
stateRef.current.isQuestionMode = false;
|
|
566
|
+
// Reset score and totalQuestions when starting a new lesson teaching
|
|
567
|
+
stateRef.current.score = 0;
|
|
568
|
+
stateRef.current.totalQuestions = 0;
|
|
557
569
|
|
|
558
570
|
avatarRef.current.setMood("happy");
|
|
559
571
|
|
|
@@ -646,13 +658,17 @@ const CurriculumLearning = forwardRef(({
|
|
|
646
658
|
lipsyncLang: config.lipsyncLang,
|
|
647
659
|
onSpeechEnd: () => {
|
|
648
660
|
// Notify parent that feedback is complete - parent decides next action
|
|
661
|
+
const currentLesson = getCurrentLesson();
|
|
662
|
+
const totalQuestionsInLesson = currentLesson?.questions?.length || 0;
|
|
649
663
|
callbacksRef.current.onCustomAction({
|
|
650
664
|
type: 'answerFeedbackComplete',
|
|
651
665
|
moduleIndex: stateRef.current.currentModuleIndex,
|
|
652
666
|
lessonIndex: stateRef.current.currentLessonIndex,
|
|
653
667
|
questionIndex: stateRef.current.currentQuestionIndex,
|
|
654
668
|
isCorrect: true,
|
|
655
|
-
hasNextQuestion: stateRef.current.currentQuestionIndex <
|
|
669
|
+
hasNextQuestion: stateRef.current.currentQuestionIndex < totalQuestionsInLesson - 1,
|
|
670
|
+
score: stateRef.current.score,
|
|
671
|
+
totalQuestions: stateRef.current.totalQuestions
|
|
656
672
|
});
|
|
657
673
|
}
|
|
658
674
|
});
|
|
@@ -677,26 +693,34 @@ const CurriculumLearning = forwardRef(({
|
|
|
677
693
|
lipsyncLang: config.lipsyncLang,
|
|
678
694
|
onSpeechEnd: () => {
|
|
679
695
|
// Notify parent that feedback is complete - parent decides next action
|
|
696
|
+
const currentLesson = getCurrentLesson();
|
|
697
|
+
const totalQuestionsInLesson = currentLesson?.questions?.length || 0;
|
|
680
698
|
callbacksRef.current.onCustomAction({
|
|
681
699
|
type: 'answerFeedbackComplete',
|
|
682
700
|
moduleIndex: stateRef.current.currentModuleIndex,
|
|
683
701
|
lessonIndex: stateRef.current.currentLessonIndex,
|
|
684
702
|
questionIndex: stateRef.current.currentQuestionIndex,
|
|
685
703
|
isCorrect: false,
|
|
686
|
-
hasNextQuestion: stateRef.current.currentQuestionIndex <
|
|
704
|
+
hasNextQuestion: stateRef.current.currentQuestionIndex < totalQuestionsInLesson - 1,
|
|
705
|
+
score: stateRef.current.score,
|
|
706
|
+
totalQuestions: stateRef.current.totalQuestions
|
|
687
707
|
});
|
|
688
708
|
}
|
|
689
709
|
});
|
|
690
710
|
}
|
|
691
711
|
} else {
|
|
692
712
|
// Avatar not ready - notify parent
|
|
713
|
+
const currentLesson = getCurrentLesson();
|
|
714
|
+
const totalQuestionsInLesson = currentLesson?.questions?.length || 0;
|
|
693
715
|
callbacksRef.current.onCustomAction({
|
|
694
716
|
type: 'answerFeedbackComplete',
|
|
695
717
|
moduleIndex: stateRef.current.currentModuleIndex,
|
|
696
718
|
lessonIndex: stateRef.current.currentLessonIndex,
|
|
697
719
|
questionIndex: stateRef.current.currentQuestionIndex,
|
|
698
720
|
isCorrect: isCorrect,
|
|
699
|
-
hasNextQuestion: stateRef.current.currentQuestionIndex <
|
|
721
|
+
hasNextQuestion: stateRef.current.currentQuestionIndex < totalQuestionsInLesson - 1,
|
|
722
|
+
score: stateRef.current.score,
|
|
723
|
+
totalQuestions: stateRef.current.totalQuestions,
|
|
700
724
|
avatarNotReady: true
|
|
701
725
|
});
|
|
702
726
|
}
|
|
@@ -760,7 +784,8 @@ const CurriculumLearning = forwardRef(({
|
|
|
760
784
|
lessonIndex: stateRef.current.currentLessonIndex,
|
|
761
785
|
questionIndex: stateRef.current.currentQuestionIndex,
|
|
762
786
|
totalQuestions: stateRef.current.totalQuestions,
|
|
763
|
-
question: prevQuestionObj
|
|
787
|
+
question: prevQuestionObj,
|
|
788
|
+
score: stateRef.current.score
|
|
764
789
|
});
|
|
765
790
|
}
|
|
766
791
|
|
package/src/lib/talkinghead.mjs
CHANGED
|
@@ -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:
|
|
149
|
+
ttsVolume: 0.3,
|
|
150
|
+
mixerGainSpeech: 1.2,
|
|
151
151
|
mixerGainBackground: null,
|
|
152
152
|
lipsyncLang: 'fi',
|
|
153
153
|
lipsyncModules: ['fi','en','lt'],
|