@contentgrowth/llm-service 0.9.92 → 0.9.94

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.
@@ -161,93 +161,32 @@ var useSpeechRecognition = (onResult, onEnd, language = "en-US") => {
161
161
  const recognitionRef = useRef(null);
162
162
  const isSimulatingRef = useRef(false);
163
163
  const simulationTimeoutRef = useRef(null);
164
+ const languageRef = useRef(language);
165
+ const instanceIdRef = useRef(Math.random().toString(36).slice(2));
166
+ const lastStartAtRef = useRef(null);
167
+ const lastStopAtRef = useRef(null);
164
168
  const onResultRef = useRef(onResult);
165
169
  const onEndRef = useRef(onEnd);
166
170
  useEffect(() => {
167
171
  onResultRef.current = onResult;
168
172
  onEndRef.current = onEnd;
169
173
  }, [onResult, onEnd]);
174
+ useEffect(() => {
175
+ languageRef.current = language;
176
+ if (recognitionRef.current) {
177
+ console.log("[useSpeechRecognition] Updating language to:", language);
178
+ recognitionRef.current.lang = language;
179
+ }
180
+ }, [language]);
170
181
  const isStartingRef = useRef(false);
171
182
  useEffect(() => {
172
- const isMobile = typeof window !== "undefined" && (/Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent) || "ontouchstart" in window || navigator.maxTouchPoints > 0);
183
+ var _a;
173
184
  if (typeof window !== "undefined") {
174
185
  const SpeechRecognition = window.SpeechRecognition || window.webkitSpeechRecognition;
175
- console.log("[useSpeechRecognition] Init - SpeechRecognition available:", !!SpeechRecognition, "isMobile:", isMobile);
176
- if (SpeechRecognition) {
177
- setIsSupported(true);
178
- const recognition = new SpeechRecognition();
179
- recognition.continuous = true;
180
- recognition.interimResults = true;
181
- console.log("[useSpeechRecognition] Created recognition instance. continuous:", recognition.continuous, "interimResults:", recognition.interimResults);
182
- recognition.onstart = () => {
183
- console.log("[useSpeechRecognition] Native onstart event fired. Timestamp:", Date.now());
184
- isStartingRef.current = false;
185
- setIsListening(true);
186
- setError(null);
187
- };
188
- recognition.onend = () => {
189
- console.log("[useSpeechRecognition] Native onend event fired. Timestamp:", Date.now());
190
- isStartingRef.current = false;
191
- if (isSimulatingRef.current) {
192
- console.log("[useSpeechRecognition] onend ignored - simulating");
193
- return;
194
- }
195
- setIsListening(false);
196
- if (onEndRef.current) onEndRef.current();
197
- };
198
- recognition.onresult = (event) => {
199
- console.log("[useSpeechRecognition] onresult event. results count:", event.results.length);
200
- let interimTranscript = "";
201
- let finalTranscript = "";
202
- for (let i = event.results.length - 1; i < event.results.length; ++i) {
203
- const result = event.results[i];
204
- if (result.isFinal) {
205
- finalTranscript += result[0].transcript;
206
- console.log("[useSpeechRecognition] Final transcript:", finalTranscript);
207
- if (onResultRef.current) onResultRef.current(finalTranscript, true);
208
- } else {
209
- interimTranscript += result[0].transcript;
210
- console.log("[useSpeechRecognition] Interim transcript:", interimTranscript);
211
- if (onResultRef.current) onResultRef.current(interimTranscript, false);
212
- }
213
- }
214
- setTranscript((prev) => prev + finalTranscript);
215
- };
216
- recognition.onerror = (event) => {
217
- console.error("[useSpeechRecognition] Native onerror event:", event.error, "Timestamp:", Date.now());
218
- console.error("[useSpeechRecognition] Error details - This could be caused by:");
219
- if (event.error === "aborted") {
220
- console.error("[useSpeechRecognition] - aborted: Recognition was aborted. Common causes: keyboard appeared, focus changed, another recognition started, or page navigation");
221
- } else if (event.error === "not-allowed") {
222
- console.error("[useSpeechRecognition] - not-allowed: Microphone permission denied");
223
- } else if (event.error === "no-speech") {
224
- console.error("[useSpeechRecognition] - no-speech: No speech detected");
225
- } else if (event.error === "network") {
226
- console.error("[useSpeechRecognition] - network: Network error during recognition");
227
- }
228
- isStartingRef.current = false;
229
- if (event.error === "not-allowed" && process.env.NODE_ENV === "development") {
230
- console.warn("Speech recognition blocked. Simulating input for development...");
231
- isSimulatingRef.current = true;
232
- setError(null);
233
- setIsListening(true);
234
- simulationTimeoutRef.current = setTimeout(() => {
235
- const mockText = "This is a simulated voice input for testing.";
236
- setTranscript((prev) => prev + (prev ? " " : "") + mockText);
237
- if (onResultRef.current) onResultRef.current(mockText, true);
238
- isSimulatingRef.current = false;
239
- setIsListening(false);
240
- if (onEndRef.current) onEndRef.current();
241
- simulationTimeoutRef.current = null;
242
- }, 3e3);
243
- return;
244
- }
245
- console.error("Speech recognition error", event.error);
246
- setError(event.error);
247
- setIsListening(false);
248
- };
249
- recognitionRef.current = recognition;
250
- }
186
+ console.log("[useSpeechRecognition] Env - isSecureContext:", window.isSecureContext, "protocol:", (_a = window.location) == null ? void 0 : _a.protocol);
187
+ const isMobile = /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent) || "ontouchstart" in window || navigator.maxTouchPoints > 0;
188
+ console.log("[useSpeechRecognition] Init check - SpeechRecognition available:", !!SpeechRecognition, "isMobile:", isMobile, "instanceId:", instanceIdRef.current);
189
+ setIsSupported(!!SpeechRecognition);
251
190
  }
252
191
  return () => {
253
192
  console.log("[useSpeechRecognition] Effect cleanup - stopping recognition");
@@ -256,21 +195,149 @@ var useSpeechRecognition = (onResult, onEnd, language = "en-US") => {
256
195
  simulationTimeoutRef.current = null;
257
196
  }
258
197
  if (recognitionRef.current) {
259
- recognitionRef.current.stop();
198
+ try {
199
+ recognitionRef.current.stop();
200
+ } catch (e) {
201
+ }
202
+ recognitionRef.current = null;
203
+ }
204
+ if (typeof window !== "undefined") {
205
+ const w = window;
206
+ if (w.__llmSpeechRecognitionActiveInstanceId === instanceIdRef.current) {
207
+ console.log("[useSpeechRecognition] Cleanup clearing global active instance lock. instanceId:", instanceIdRef.current);
208
+ w.__llmSpeechRecognitionActiveInstanceId = null;
209
+ }
260
210
  }
261
211
  };
262
212
  }, []);
263
- useEffect(() => {
264
- if (recognitionRef.current) {
265
- console.log("[useSpeechRecognition] Updating language to:", language);
266
- recognitionRef.current.lang = language;
213
+ const createRecognitionInstance = useCallback(() => {
214
+ const SpeechRecognition = window.SpeechRecognition || window.webkitSpeechRecognition;
215
+ if (!SpeechRecognition) {
216
+ console.error("[useSpeechRecognition] SpeechRecognition not available");
217
+ return null;
267
218
  }
268
- }, [language]);
219
+ console.log("[useSpeechRecognition] Creating NEW recognition instance within user gesture context. Timestamp:", Date.now());
220
+ const recognition = new SpeechRecognition();
221
+ const isMobile = /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent) || "ontouchstart" in window || navigator.maxTouchPoints > 0;
222
+ recognition.continuous = !isMobile;
223
+ recognition.interimResults = true;
224
+ recognition.lang = languageRef.current;
225
+ console.log("[useSpeechRecognition] Instance created. continuous:", recognition.continuous, "interimResults:", recognition.interimResults, "lang:", recognition.lang, "isMobile:", isMobile, "instanceId:", instanceIdRef.current);
226
+ recognition.onaudiostart = () => {
227
+ console.log("[useSpeechRecognition] Native onaudiostart. Timestamp:", Date.now(), "instanceId:", instanceIdRef.current);
228
+ };
229
+ recognition.onaudioend = () => {
230
+ console.log("[useSpeechRecognition] Native onaudioend. Timestamp:", Date.now(), "instanceId:", instanceIdRef.current);
231
+ };
232
+ recognition.onsoundstart = () => {
233
+ console.log("[useSpeechRecognition] Native onsoundstart. Timestamp:", Date.now(), "instanceId:", instanceIdRef.current);
234
+ };
235
+ recognition.onsoundend = () => {
236
+ console.log("[useSpeechRecognition] Native onsoundend. Timestamp:", Date.now(), "instanceId:", instanceIdRef.current);
237
+ };
238
+ recognition.onspeechstart = () => {
239
+ console.log("[useSpeechRecognition] Native onspeechstart. Timestamp:", Date.now(), "instanceId:", instanceIdRef.current);
240
+ };
241
+ recognition.onspeechend = () => {
242
+ console.log("[useSpeechRecognition] Native onspeechend. Timestamp:", Date.now(), "instanceId:", instanceIdRef.current);
243
+ };
244
+ recognition.onnomatch = () => {
245
+ console.log("[useSpeechRecognition] Native onnomatch. Timestamp:", Date.now(), "instanceId:", instanceIdRef.current);
246
+ };
247
+ recognition.onstart = () => {
248
+ console.log("[useSpeechRecognition] Native onstart event fired. Timestamp:", Date.now());
249
+ isStartingRef.current = false;
250
+ setIsListening(true);
251
+ setError(null);
252
+ if (typeof window !== "undefined") {
253
+ const w = window;
254
+ w.__llmSpeechRecognitionActiveInstanceId = instanceIdRef.current;
255
+ console.log("[useSpeechRecognition] Set global active instance lock. instanceId:", instanceIdRef.current);
256
+ }
257
+ };
258
+ recognition.onend = () => {
259
+ console.log("[useSpeechRecognition] Native onend event fired. Timestamp:", Date.now());
260
+ isStartingRef.current = false;
261
+ if (isSimulatingRef.current) {
262
+ console.log("[useSpeechRecognition] onend ignored - simulating");
263
+ return;
264
+ }
265
+ setIsListening(false);
266
+ if (onEndRef.current) onEndRef.current();
267
+ if (typeof window !== "undefined") {
268
+ const w = window;
269
+ if (w.__llmSpeechRecognitionActiveInstanceId === instanceIdRef.current) {
270
+ w.__llmSpeechRecognitionActiveInstanceId = null;
271
+ console.log("[useSpeechRecognition] Cleared global active instance lock. instanceId:", instanceIdRef.current);
272
+ }
273
+ }
274
+ };
275
+ recognition.onresult = (event) => {
276
+ console.log("[useSpeechRecognition] onresult event. results count:", event.results.length);
277
+ let interimTranscript = "";
278
+ let finalTranscript = "";
279
+ for (let i = event.results.length - 1; i < event.results.length; ++i) {
280
+ const result = event.results[i];
281
+ if (result.isFinal) {
282
+ finalTranscript += result[0].transcript;
283
+ console.log("[useSpeechRecognition] Final transcript:", finalTranscript);
284
+ if (onResultRef.current) onResultRef.current(finalTranscript, true);
285
+ } else {
286
+ interimTranscript += result[0].transcript;
287
+ console.log("[useSpeechRecognition] Interim transcript:", interimTranscript);
288
+ if (onResultRef.current) onResultRef.current(interimTranscript, false);
289
+ }
290
+ }
291
+ setTranscript((prev) => prev + finalTranscript);
292
+ };
293
+ recognition.onerror = (event) => {
294
+ console.error("[useSpeechRecognition] Native onerror event:", event.error, "Timestamp:", Date.now());
295
+ console.error("[useSpeechRecognition] Error context - lastStartAt:", lastStartAtRef.current, "lastStopAt:", lastStopAtRef.current, "instanceId:", instanceIdRef.current);
296
+ console.error("[useSpeechRecognition] Error details - This could be caused by:");
297
+ if (event.error === "aborted") {
298
+ console.error("[useSpeechRecognition] - aborted: Recognition was aborted. Common causes: keyboard appeared, focus changed, another recognition started, or page navigation");
299
+ } else if (event.error === "not-allowed") {
300
+ console.error("[useSpeechRecognition] - not-allowed: Microphone permission denied");
301
+ } else if (event.error === "no-speech") {
302
+ console.error("[useSpeechRecognition] - no-speech: No speech detected");
303
+ } else if (event.error === "network") {
304
+ console.error("[useSpeechRecognition] - network: Network error during recognition");
305
+ }
306
+ isStartingRef.current = false;
307
+ if (event.error === "not-allowed" && process.env.NODE_ENV === "development") {
308
+ console.warn("Speech recognition blocked. Simulating input for development...");
309
+ isSimulatingRef.current = true;
310
+ setError(null);
311
+ setIsListening(true);
312
+ simulationTimeoutRef.current = setTimeout(() => {
313
+ const mockText = "This is a simulated voice input for testing.";
314
+ setTranscript((prev) => prev + (prev ? " " : "") + mockText);
315
+ if (onResultRef.current) onResultRef.current(mockText, true);
316
+ isSimulatingRef.current = false;
317
+ setIsListening(false);
318
+ if (onEndRef.current) onEndRef.current();
319
+ simulationTimeoutRef.current = null;
320
+ }, 3e3);
321
+ return;
322
+ }
323
+ console.error("Speech recognition error", event.error);
324
+ setError(event.error);
325
+ setIsListening(false);
326
+ if (typeof window !== "undefined") {
327
+ const w = window;
328
+ if (w.__llmSpeechRecognitionActiveInstanceId === instanceIdRef.current) {
329
+ w.__llmSpeechRecognitionActiveInstanceId = null;
330
+ console.log("[useSpeechRecognition] Cleared global active instance lock after error. instanceId:", instanceIdRef.current);
331
+ }
332
+ }
333
+ };
334
+ return recognition;
335
+ }, []);
269
336
  const start = useCallback(() => {
270
337
  var _a;
271
338
  const startTimestamp = Date.now();
272
339
  console.log("[useSpeechRecognition] start() called. Timestamp:", startTimestamp);
273
- console.log("[useSpeechRecognition] State check - isListening:", isListening, "isStarting:", isStartingRef.current, "hasInstance:", !!recognitionRef.current);
340
+ console.log("[useSpeechRecognition] State check - isListening:", isListening, "isStarting:", isStartingRef.current, "hasExistingInstance:", !!recognitionRef.current);
274
341
  if (typeof document !== "undefined") {
275
342
  console.log("[useSpeechRecognition] Document hasFocus:", document.hasFocus(), "activeElement:", (_a = document.activeElement) == null ? void 0 : _a.tagName);
276
343
  }
@@ -278,24 +345,39 @@ var useSpeechRecognition = (onResult, onEnd, language = "en-US") => {
278
345
  console.log("[useSpeechRecognition] isSimulating, ignoring start");
279
346
  return;
280
347
  }
281
- if (!recognitionRef.current) {
282
- console.error("[useSpeechRecognition] Recognition instance missing");
283
- return;
284
- }
285
348
  if (isStartingRef.current) {
286
349
  console.warn("[useSpeechRecognition] Already starting - ignoring duplicate call");
287
350
  return;
288
351
  }
289
- if (recognitionRef.current.isListening) {
290
- console.warn("[useSpeechRecognition] Already listening (native prop) - ignoring");
291
- }
292
352
  if (isListening) {
293
353
  console.warn("[useSpeechRecognition] App state says already listening - ignoring");
294
354
  return;
295
355
  }
356
+ if (typeof window !== "undefined") {
357
+ const w = window;
358
+ if (w.__llmSpeechRecognitionActiveInstanceId && w.__llmSpeechRecognitionActiveInstanceId !== instanceIdRef.current) {
359
+ console.error("[useSpeechRecognition] Another recognition instance appears active. activeInstanceId:", w.__llmSpeechRecognitionActiveInstanceId, "thisInstanceId:", instanceIdRef.current);
360
+ }
361
+ }
296
362
  try {
363
+ if (recognitionRef.current) {
364
+ console.log("[useSpeechRecognition] Stopping existing instance before creating new one");
365
+ try {
366
+ recognitionRef.current.stop();
367
+ } catch (e) {
368
+ }
369
+ recognitionRef.current = null;
370
+ }
371
+ const recognition = createRecognitionInstance();
372
+ if (!recognition) {
373
+ console.error("[useSpeechRecognition] Failed to create recognition instance");
374
+ setError("Speech recognition not available");
375
+ return;
376
+ }
377
+ recognitionRef.current = recognition;
297
378
  setTranscript("");
298
379
  isStartingRef.current = true;
380
+ lastStartAtRef.current = Date.now();
299
381
  console.log("[useSpeechRecognition] About to call recognition.start(). Timestamp:", Date.now());
300
382
  recognitionRef.current.start();
301
383
  console.log("[useSpeechRecognition] recognition.start() executed successfully. Timestamp:", Date.now());
@@ -305,10 +387,12 @@ var useSpeechRecognition = (onResult, onEnd, language = "en-US") => {
305
387
  if ((error2 == null ? void 0 : error2.name) === "InvalidStateError") {
306
388
  console.error("[useSpeechRecognition] InvalidStateError - recognition may already be running");
307
389
  }
390
+ setError((error2 == null ? void 0 : error2.message) || "Failed to start speech recognition");
308
391
  }
309
- }, [isListening]);
392
+ }, [isListening, createRecognitionInstance]);
310
393
  const stop = useCallback(() => {
311
394
  console.log("[useSpeechRecognition] stop() called");
395
+ lastStopAtRef.current = Date.now();
312
396
  if (isSimulatingRef.current) {
313
397
  if (simulationTimeoutRef.current) {
314
398
  clearTimeout(simulationTimeoutRef.current);
@@ -633,7 +717,7 @@ var ChatInputArea = forwardRef(({
633
717
  return /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent) || "ontouchstart" in window || navigator.maxTouchPoints > 0;
634
718
  }, []);
635
719
  const startRecording = async (trigger) => {
636
- var _a2;
720
+ var _a2, _b2;
637
721
  console.log("[ChatInputArea] startRecording called. trigger:", trigger, "isMobile:", isMobile());
638
722
  console.log("[ChatInputArea] Current state - voiceTrigger:", voiceTrigger, "isTranscribing:", isTranscribing);
639
723
  if (voiceTrigger || isTranscribing) {
@@ -642,8 +726,6 @@ var ChatInputArea = forwardRef(({
642
726
  }
643
727
  setVoiceTrigger(trigger);
644
728
  setVoiceError(null);
645
- console.log("[ChatInputArea] Calling voiceConfig.onVoiceStart if exists...");
646
- (_a2 = voiceConfig == null ? void 0 : voiceConfig.onVoiceStart) == null ? void 0 : _a2.call(voiceConfig);
647
729
  if ((voiceConfig == null ? void 0 : voiceConfig.mode) === "native") {
648
730
  console.log("[ChatInputArea] Using native speech recognition");
649
731
  if (!nativeSpeech.isSupported) {
@@ -655,8 +737,22 @@ var ChatInputArea = forwardRef(({
655
737
  console.log("[ChatInputArea] Calling nativeSpeech.start()...");
656
738
  nativeSpeech.start();
657
739
  console.log("[ChatInputArea] nativeSpeech.start() called");
740
+ console.log("[ChatInputArea] Calling voiceConfig.onVoiceStart if exists (after nativeSpeech.start)...");
741
+ try {
742
+ (_a2 = voiceConfig == null ? void 0 : voiceConfig.onVoiceStart) == null ? void 0 : _a2.call(voiceConfig);
743
+ console.log("[ChatInputArea] voiceConfig.onVoiceStart completed");
744
+ } catch (e) {
745
+ console.error("[ChatInputArea] voiceConfig.onVoiceStart threw error", e);
746
+ }
658
747
  } else {
659
748
  console.log("[ChatInputArea] Using custom recorder");
749
+ console.log("[ChatInputArea] Calling voiceConfig.onVoiceStart if exists (custom mode)...");
750
+ try {
751
+ (_b2 = voiceConfig == null ? void 0 : voiceConfig.onVoiceStart) == null ? void 0 : _b2.call(voiceConfig);
752
+ console.log("[ChatInputArea] voiceConfig.onVoiceStart completed");
753
+ } catch (e) {
754
+ console.error("[ChatInputArea] voiceConfig.onVoiceStart threw error", e);
755
+ }
660
756
  await customRecorder.start();
661
757
  console.log("[ChatInputArea] Custom recorder started");
662
758
  }
@@ -668,6 +764,10 @@ var ChatInputArea = forwardRef(({
668
764
  }, 0);
669
765
  } else {
670
766
  console.log("[ChatInputArea] SKIPPING textarea focus on mobile to prevent keyboard conflict");
767
+ if (document.activeElement instanceof HTMLElement) {
768
+ console.log("[ChatInputArea] Blur active element on mobile");
769
+ document.activeElement.blur();
770
+ }
671
771
  }
672
772
  };
673
773
  const stopRecording = () => {
@@ -1010,10 +1110,14 @@ var TapToTalk = ({
1010
1110
  if (onFocusTarget && !isMobile()) {
1011
1111
  console.log("[TapToTalk] calling onFocusTarget() (desktop only)");
1012
1112
  onFocusTarget();
1013
- } else if (onFocusTarget) {
1014
- console.log("[TapToTalk] SKIPPING onFocusTarget on mobile to prevent keyboard conflict");
1015
1113
  } else {
1016
- console.log("[TapToTalk] onFocusTarget is undefined");
1114
+ if (onFocusTarget) {
1115
+ console.log("[TapToTalk] SKIPPING onFocusTarget on mobile to prevent keyboard conflict");
1116
+ }
1117
+ if (isMobile() && document.activeElement instanceof HTMLElement) {
1118
+ console.log("[TapToTalk] Blurring active element on mobile");
1119
+ document.activeElement.blur();
1120
+ }
1017
1121
  }
1018
1122
  setVoiceTrigger("click");
1019
1123
  console.log("[TapToTalk] voiceTrigger set to click");