@sage-rsc/talking-head-react 1.0.70 → 1.0.71

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sage-rsc/talking-head-react",
3
- "version": "1.0.70",
3
+ "version": "1.0.71",
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",
@@ -178,7 +178,7 @@ const SimpleTalkingAvatar = forwardRef(({
178
178
  setIsLoading(false);
179
179
  onError(err);
180
180
  }
181
- }, [avatarUrl, avatarBody, mood, ttsLang, effectiveTtsService, ttsVoice, ttsApiKey, bodyMovement, movementIntensity, showFullAvatar, cameraView, onLoading, onReady, onError]);
181
+ }, []); // Empty deps - only initialize once
182
182
 
183
183
  useEffect(() => {
184
184
  initializeTalkingHead();
@@ -192,15 +192,31 @@ const SimpleTalkingAvatar = forwardRef(({
192
192
  };
193
193
  }, [initializeTalkingHead]);
194
194
 
195
+ // Resume audio context helper
196
+ const resumeAudioContext = useCallback(async () => {
197
+ if (talkingHeadRef.current) {
198
+ try {
199
+ // Try to access audio context through talkingHead instance
200
+ const audioCtx = talkingHeadRef.current.audioCtx || talkingHeadRef.current.audioContext;
201
+ if (audioCtx && (audioCtx.state === 'suspended' || audioCtx.state === 'interrupted')) {
202
+ await audioCtx.resume();
203
+ console.log('Audio context resumed');
204
+ }
205
+ } catch (err) {
206
+ console.warn('Failed to resume audio context:', err);
207
+ }
208
+ }
209
+ }, []);
210
+
195
211
  // Auto-speak text when ready and autoSpeak is true
196
212
  useEffect(() => {
197
213
  if (isReady && text && autoSpeak && talkingHeadRef.current) {
198
214
  speakText(text);
199
215
  }
200
- }, [isReady, text, autoSpeak]);
216
+ }, [isReady, text, autoSpeak, speakText]);
201
217
 
202
218
  // Speak text with proper callback handling
203
- const speakText = useCallback((textToSpeak, options = {}) => {
219
+ const speakText = useCallback(async (textToSpeak, options = {}) => {
204
220
  if (!talkingHeadRef.current || !isReady) {
205
221
  console.warn('Avatar not ready for speaking');
206
222
  return;
@@ -211,6 +227,9 @@ const SimpleTalkingAvatar = forwardRef(({
211
227
  return;
212
228
  }
213
229
 
230
+ // Always resume audio context first (required for user interaction)
231
+ await resumeAudioContext();
232
+
214
233
  // Reset speech progress tracking
215
234
  speechProgressRef.current = { remainingText: null, originalText: null, options: null };
216
235
  originalSentencesRef.current = [];
@@ -255,7 +274,7 @@ const SimpleTalkingAvatar = forwardRef(({
255
274
  console.error('Error speaking text:', err);
256
275
  setError(err.message || 'Failed to speak text');
257
276
  }
258
- }, [isReady, onSpeechEnd]);
277
+ }, [isReady, onSpeechEnd, resumeAudioContext]);
259
278
 
260
279
  // Pause speaking
261
280
  const pauseSpeaking = useCallback(() => {
@@ -308,10 +327,8 @@ const SimpleTalkingAvatar = forwardRef(({
308
327
  if (!talkingHeadRef.current || !isPaused) return;
309
328
 
310
329
  try {
311
- // Resume audio context if needed
312
- if (talkingHeadRef.current.audioContext && talkingHeadRef.current.audioContext.state === 'suspended') {
313
- await talkingHeadRef.current.audioContext.resume();
314
- }
330
+ // Resume audio context first
331
+ await resumeAudioContext();
315
332
 
316
333
  setIsPaused(false);
317
334
  isPausedRef.current = false;
@@ -331,7 +348,7 @@ const SimpleTalkingAvatar = forwardRef(({
331
348
  setIsPaused(false);
332
349
  isPausedRef.current = false;
333
350
  }
334
- }, [isPaused, speakText]);
351
+ }, [isPaused, speakText, resumeAudioContext]);
335
352
 
336
353
  // Stop speaking
337
354
  const stopSpeaking = useCallback(() => {
@@ -352,6 +369,7 @@ const SimpleTalkingAvatar = forwardRef(({
352
369
  pauseSpeaking,
353
370
  resumeSpeaking,
354
371
  stopSpeaking,
372
+ resumeAudioContext,
355
373
  isPaused: () => isPaused,
356
374
  setMood: (mood) => talkingHeadRef.current?.setMood(mood),
357
375
  setBodyMovement: (movement) => {