@contentgrowth/llm-service 0.9.91 → 0.9.93

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.
@@ -203,76 +203,32 @@ var useSpeechRecognition = (onResult, onEnd, language = "en-US") => {
203
203
  const recognitionRef = (0, import_react2.useRef)(null);
204
204
  const isSimulatingRef = (0, import_react2.useRef)(false);
205
205
  const simulationTimeoutRef = (0, import_react2.useRef)(null);
206
+ const languageRef = (0, import_react2.useRef)(language);
207
+ const instanceIdRef = (0, import_react2.useRef)(Math.random().toString(36).slice(2));
208
+ const lastStartAtRef = (0, import_react2.useRef)(null);
209
+ const lastStopAtRef = (0, import_react2.useRef)(null);
206
210
  const onResultRef = (0, import_react2.useRef)(onResult);
207
211
  const onEndRef = (0, import_react2.useRef)(onEnd);
208
212
  (0, import_react2.useEffect)(() => {
209
213
  onResultRef.current = onResult;
210
214
  onEndRef.current = onEnd;
211
215
  }, [onResult, onEnd]);
216
+ (0, import_react2.useEffect)(() => {
217
+ languageRef.current = language;
218
+ if (recognitionRef.current) {
219
+ console.log("[useSpeechRecognition] Updating language to:", language);
220
+ recognitionRef.current.lang = language;
221
+ }
222
+ }, [language]);
212
223
  const isStartingRef = (0, import_react2.useRef)(false);
213
224
  (0, import_react2.useEffect)(() => {
225
+ var _a;
214
226
  if (typeof window !== "undefined") {
215
227
  const SpeechRecognition = window.SpeechRecognition || window.webkitSpeechRecognition;
216
- if (SpeechRecognition) {
217
- setIsSupported(true);
218
- const recognition = new SpeechRecognition();
219
- recognition.continuous = true;
220
- recognition.interimResults = true;
221
- recognition.onstart = () => {
222
- console.log("[useSpeechRecognition] Native onstart event fired");
223
- isStartingRef.current = false;
224
- setIsListening(true);
225
- setError(null);
226
- };
227
- recognition.onend = () => {
228
- console.log("[useSpeechRecognition] Native onend event fired");
229
- isStartingRef.current = false;
230
- if (isSimulatingRef.current) {
231
- return;
232
- }
233
- setIsListening(false);
234
- if (onEndRef.current) onEndRef.current();
235
- };
236
- recognition.onresult = (event) => {
237
- let interimTranscript = "";
238
- let finalTranscript = "";
239
- for (let i = event.results.length - 1; i < event.results.length; ++i) {
240
- const result = event.results[i];
241
- if (result.isFinal) {
242
- finalTranscript += result[0].transcript;
243
- if (onResultRef.current) onResultRef.current(finalTranscript, true);
244
- } else {
245
- interimTranscript += result[0].transcript;
246
- if (onResultRef.current) onResultRef.current(interimTranscript, false);
247
- }
248
- }
249
- setTranscript((prev) => prev + finalTranscript);
250
- };
251
- recognition.onerror = (event) => {
252
- console.error("[useSpeechRecognition] Native onerror event:", event.error);
253
- isStartingRef.current = false;
254
- if (event.error === "not-allowed" && process.env.NODE_ENV === "development") {
255
- console.warn("Speech recognition blocked. Simulating input for development...");
256
- isSimulatingRef.current = true;
257
- setError(null);
258
- setIsListening(true);
259
- simulationTimeoutRef.current = setTimeout(() => {
260
- const mockText = "This is a simulated voice input for testing.";
261
- setTranscript((prev) => prev + (prev ? " " : "") + mockText);
262
- if (onResultRef.current) onResultRef.current(mockText, true);
263
- isSimulatingRef.current = false;
264
- setIsListening(false);
265
- if (onEndRef.current) onEndRef.current();
266
- simulationTimeoutRef.current = null;
267
- }, 3e3);
268
- return;
269
- }
270
- console.error("Speech recognition error", event.error);
271
- setError(event.error);
272
- setIsListening(false);
273
- };
274
- recognitionRef.current = recognition;
275
- }
228
+ console.log("[useSpeechRecognition] Env - isSecureContext:", window.isSecureContext, "protocol:", (_a = window.location) == null ? void 0 : _a.protocol);
229
+ const isMobile = /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent) || "ontouchstart" in window || navigator.maxTouchPoints > 0;
230
+ console.log("[useSpeechRecognition] Init check - SpeechRecognition available:", !!SpeechRecognition, "isMobile:", isMobile, "instanceId:", instanceIdRef.current);
231
+ setIsSupported(!!SpeechRecognition);
276
232
  }
277
233
  return () => {
278
234
  console.log("[useSpeechRecognition] Effect cleanup - stopping recognition");
@@ -281,49 +237,204 @@ var useSpeechRecognition = (onResult, onEnd, language = "en-US") => {
281
237
  simulationTimeoutRef.current = null;
282
238
  }
283
239
  if (recognitionRef.current) {
284
- recognitionRef.current.stop();
240
+ try {
241
+ recognitionRef.current.stop();
242
+ } catch (e) {
243
+ }
244
+ recognitionRef.current = null;
245
+ }
246
+ if (typeof window !== "undefined") {
247
+ const w = window;
248
+ if (w.__llmSpeechRecognitionActiveInstanceId === instanceIdRef.current) {
249
+ console.log("[useSpeechRecognition] Cleanup clearing global active instance lock. instanceId:", instanceIdRef.current);
250
+ w.__llmSpeechRecognitionActiveInstanceId = null;
251
+ }
285
252
  }
286
253
  };
287
254
  }, []);
288
- (0, import_react2.useEffect)(() => {
289
- if (recognitionRef.current) {
290
- console.log("[useSpeechRecognition] Updating language to:", language);
291
- recognitionRef.current.lang = language;
255
+ const createRecognitionInstance = (0, import_react2.useCallback)(() => {
256
+ const SpeechRecognition = window.SpeechRecognition || window.webkitSpeechRecognition;
257
+ if (!SpeechRecognition) {
258
+ console.error("[useSpeechRecognition] SpeechRecognition not available");
259
+ return null;
292
260
  }
293
- }, [language]);
261
+ console.log("[useSpeechRecognition] Creating NEW recognition instance within user gesture context. Timestamp:", Date.now());
262
+ const recognition = new SpeechRecognition();
263
+ const isMobile = /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent) || "ontouchstart" in window || navigator.maxTouchPoints > 0;
264
+ recognition.continuous = !isMobile;
265
+ recognition.interimResults = true;
266
+ recognition.lang = languageRef.current;
267
+ console.log("[useSpeechRecognition] Instance created. continuous:", recognition.continuous, "interimResults:", recognition.interimResults, "lang:", recognition.lang, "isMobile:", isMobile, "instanceId:", instanceIdRef.current);
268
+ recognition.onaudiostart = () => {
269
+ console.log("[useSpeechRecognition] Native onaudiostart. Timestamp:", Date.now(), "instanceId:", instanceIdRef.current);
270
+ };
271
+ recognition.onaudioend = () => {
272
+ console.log("[useSpeechRecognition] Native onaudioend. Timestamp:", Date.now(), "instanceId:", instanceIdRef.current);
273
+ };
274
+ recognition.onsoundstart = () => {
275
+ console.log("[useSpeechRecognition] Native onsoundstart. Timestamp:", Date.now(), "instanceId:", instanceIdRef.current);
276
+ };
277
+ recognition.onsoundend = () => {
278
+ console.log("[useSpeechRecognition] Native onsoundend. Timestamp:", Date.now(), "instanceId:", instanceIdRef.current);
279
+ };
280
+ recognition.onspeechstart = () => {
281
+ console.log("[useSpeechRecognition] Native onspeechstart. Timestamp:", Date.now(), "instanceId:", instanceIdRef.current);
282
+ };
283
+ recognition.onspeechend = () => {
284
+ console.log("[useSpeechRecognition] Native onspeechend. Timestamp:", Date.now(), "instanceId:", instanceIdRef.current);
285
+ };
286
+ recognition.onnomatch = () => {
287
+ console.log("[useSpeechRecognition] Native onnomatch. Timestamp:", Date.now(), "instanceId:", instanceIdRef.current);
288
+ };
289
+ recognition.onstart = () => {
290
+ console.log("[useSpeechRecognition] Native onstart event fired. Timestamp:", Date.now());
291
+ isStartingRef.current = false;
292
+ setIsListening(true);
293
+ setError(null);
294
+ if (typeof window !== "undefined") {
295
+ const w = window;
296
+ w.__llmSpeechRecognitionActiveInstanceId = instanceIdRef.current;
297
+ console.log("[useSpeechRecognition] Set global active instance lock. instanceId:", instanceIdRef.current);
298
+ }
299
+ };
300
+ recognition.onend = () => {
301
+ console.log("[useSpeechRecognition] Native onend event fired. Timestamp:", Date.now());
302
+ isStartingRef.current = false;
303
+ if (isSimulatingRef.current) {
304
+ console.log("[useSpeechRecognition] onend ignored - simulating");
305
+ return;
306
+ }
307
+ setIsListening(false);
308
+ if (onEndRef.current) onEndRef.current();
309
+ if (typeof window !== "undefined") {
310
+ const w = window;
311
+ if (w.__llmSpeechRecognitionActiveInstanceId === instanceIdRef.current) {
312
+ w.__llmSpeechRecognitionActiveInstanceId = null;
313
+ console.log("[useSpeechRecognition] Cleared global active instance lock. instanceId:", instanceIdRef.current);
314
+ }
315
+ }
316
+ };
317
+ recognition.onresult = (event) => {
318
+ console.log("[useSpeechRecognition] onresult event. results count:", event.results.length);
319
+ let interimTranscript = "";
320
+ let finalTranscript = "";
321
+ for (let i = event.results.length - 1; i < event.results.length; ++i) {
322
+ const result = event.results[i];
323
+ if (result.isFinal) {
324
+ finalTranscript += result[0].transcript;
325
+ console.log("[useSpeechRecognition] Final transcript:", finalTranscript);
326
+ if (onResultRef.current) onResultRef.current(finalTranscript, true);
327
+ } else {
328
+ interimTranscript += result[0].transcript;
329
+ console.log("[useSpeechRecognition] Interim transcript:", interimTranscript);
330
+ if (onResultRef.current) onResultRef.current(interimTranscript, false);
331
+ }
332
+ }
333
+ setTranscript((prev) => prev + finalTranscript);
334
+ };
335
+ recognition.onerror = (event) => {
336
+ console.error("[useSpeechRecognition] Native onerror event:", event.error, "Timestamp:", Date.now());
337
+ console.error("[useSpeechRecognition] Error context - lastStartAt:", lastStartAtRef.current, "lastStopAt:", lastStopAtRef.current, "instanceId:", instanceIdRef.current);
338
+ console.error("[useSpeechRecognition] Error details - This could be caused by:");
339
+ if (event.error === "aborted") {
340
+ console.error("[useSpeechRecognition] - aborted: Recognition was aborted. Common causes: keyboard appeared, focus changed, another recognition started, or page navigation");
341
+ } else if (event.error === "not-allowed") {
342
+ console.error("[useSpeechRecognition] - not-allowed: Microphone permission denied");
343
+ } else if (event.error === "no-speech") {
344
+ console.error("[useSpeechRecognition] - no-speech: No speech detected");
345
+ } else if (event.error === "network") {
346
+ console.error("[useSpeechRecognition] - network: Network error during recognition");
347
+ }
348
+ isStartingRef.current = false;
349
+ if (event.error === "not-allowed" && process.env.NODE_ENV === "development") {
350
+ console.warn("Speech recognition blocked. Simulating input for development...");
351
+ isSimulatingRef.current = true;
352
+ setError(null);
353
+ setIsListening(true);
354
+ simulationTimeoutRef.current = setTimeout(() => {
355
+ const mockText = "This is a simulated voice input for testing.";
356
+ setTranscript((prev) => prev + (prev ? " " : "") + mockText);
357
+ if (onResultRef.current) onResultRef.current(mockText, true);
358
+ isSimulatingRef.current = false;
359
+ setIsListening(false);
360
+ if (onEndRef.current) onEndRef.current();
361
+ simulationTimeoutRef.current = null;
362
+ }, 3e3);
363
+ return;
364
+ }
365
+ console.error("Speech recognition error", event.error);
366
+ setError(event.error);
367
+ setIsListening(false);
368
+ if (typeof window !== "undefined") {
369
+ const w = window;
370
+ if (w.__llmSpeechRecognitionActiveInstanceId === instanceIdRef.current) {
371
+ w.__llmSpeechRecognitionActiveInstanceId = null;
372
+ console.log("[useSpeechRecognition] Cleared global active instance lock after error. instanceId:", instanceIdRef.current);
373
+ }
374
+ }
375
+ };
376
+ return recognition;
377
+ }, []);
294
378
  const start = (0, import_react2.useCallback)(() => {
295
- console.log("[useSpeechRecognition] start() called. isListening:", isListening, "isStarting:", isStartingRef.current, "hasInstance:", !!recognitionRef.current);
379
+ var _a;
380
+ const startTimestamp = Date.now();
381
+ console.log("[useSpeechRecognition] start() called. Timestamp:", startTimestamp);
382
+ console.log("[useSpeechRecognition] State check - isListening:", isListening, "isStarting:", isStartingRef.current, "hasExistingInstance:", !!recognitionRef.current);
383
+ if (typeof document !== "undefined") {
384
+ console.log("[useSpeechRecognition] Document hasFocus:", document.hasFocus(), "activeElement:", (_a = document.activeElement) == null ? void 0 : _a.tagName);
385
+ }
296
386
  if (isSimulatingRef.current) {
297
387
  console.log("[useSpeechRecognition] isSimulating, ignoring start");
298
388
  return;
299
389
  }
300
- if (!recognitionRef.current) {
301
- console.error("[useSpeechRecognition] Recognition instance missing");
302
- return;
303
- }
304
390
  if (isStartingRef.current) {
305
391
  console.warn("[useSpeechRecognition] Already starting - ignoring duplicate call");
306
392
  return;
307
393
  }
308
- if (recognitionRef.current.isListening) {
309
- console.warn("[useSpeechRecognition] Already listening (native prop) - ignoring");
310
- }
311
394
  if (isListening) {
312
395
  console.warn("[useSpeechRecognition] App state says already listening - ignoring");
313
396
  return;
314
397
  }
398
+ if (typeof window !== "undefined") {
399
+ const w = window;
400
+ if (w.__llmSpeechRecognitionActiveInstanceId && w.__llmSpeechRecognitionActiveInstanceId !== instanceIdRef.current) {
401
+ console.error("[useSpeechRecognition] Another recognition instance appears active. activeInstanceId:", w.__llmSpeechRecognitionActiveInstanceId, "thisInstanceId:", instanceIdRef.current);
402
+ }
403
+ }
315
404
  try {
405
+ if (recognitionRef.current) {
406
+ console.log("[useSpeechRecognition] Stopping existing instance before creating new one");
407
+ try {
408
+ recognitionRef.current.stop();
409
+ } catch (e) {
410
+ }
411
+ recognitionRef.current = null;
412
+ }
413
+ const recognition = createRecognitionInstance();
414
+ if (!recognition) {
415
+ console.error("[useSpeechRecognition] Failed to create recognition instance");
416
+ setError("Speech recognition not available");
417
+ return;
418
+ }
419
+ recognitionRef.current = recognition;
316
420
  setTranscript("");
317
421
  isStartingRef.current = true;
422
+ lastStartAtRef.current = Date.now();
423
+ console.log("[useSpeechRecognition] About to call recognition.start(). Timestamp:", Date.now());
318
424
  recognitionRef.current.start();
319
- console.log("[useSpeechRecognition] recognition.start() executed successfully");
425
+ console.log("[useSpeechRecognition] recognition.start() executed successfully. Timestamp:", Date.now());
320
426
  } catch (error2) {
321
427
  isStartingRef.current = false;
322
- console.error("[useSpeechRecognition] Failed to start recognition:", error2);
428
+ console.error("[useSpeechRecognition] Failed to start recognition:", (error2 == null ? void 0 : error2.message) || error2);
429
+ if ((error2 == null ? void 0 : error2.name) === "InvalidStateError") {
430
+ console.error("[useSpeechRecognition] InvalidStateError - recognition may already be running");
431
+ }
432
+ setError((error2 == null ? void 0 : error2.message) || "Failed to start speech recognition");
323
433
  }
324
- }, [isListening]);
434
+ }, [isListening, createRecognitionInstance]);
325
435
  const stop = (0, import_react2.useCallback)(() => {
326
436
  console.log("[useSpeechRecognition] stop() called");
437
+ lastStopAtRef.current = Date.now();
327
438
  if (isSimulatingRef.current) {
328
439
  if (simulationTimeoutRef.current) {
329
440
  clearTimeout(simulationTimeoutRef.current);
@@ -643,26 +754,47 @@ var ChatInputArea = (0, import_react5.forwardRef)(({
643
754
  handleSubmit();
644
755
  }
645
756
  };
757
+ const isMobile = (0, import_react5.useCallback)(() => {
758
+ if (typeof window === "undefined") return false;
759
+ return /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent) || "ontouchstart" in window || navigator.maxTouchPoints > 0;
760
+ }, []);
646
761
  const startRecording = async (trigger) => {
647
762
  var _a2;
648
- if (voiceTrigger || isTranscribing) return;
763
+ console.log("[ChatInputArea] startRecording called. trigger:", trigger, "isMobile:", isMobile());
764
+ console.log("[ChatInputArea] Current state - voiceTrigger:", voiceTrigger, "isTranscribing:", isTranscribing);
765
+ if (voiceTrigger || isTranscribing) {
766
+ console.log("[ChatInputArea] startRecording ignored - already active");
767
+ return;
768
+ }
649
769
  setVoiceTrigger(trigger);
650
770
  setVoiceError(null);
771
+ console.log("[ChatInputArea] Calling voiceConfig.onVoiceStart if exists...");
651
772
  (_a2 = voiceConfig == null ? void 0 : voiceConfig.onVoiceStart) == null ? void 0 : _a2.call(voiceConfig);
652
773
  if ((voiceConfig == null ? void 0 : voiceConfig.mode) === "native") {
774
+ console.log("[ChatInputArea] Using native speech recognition");
653
775
  if (!nativeSpeech.isSupported) {
776
+ console.error("[ChatInputArea] Native speech not supported");
654
777
  alert("Speech recognition is not supported in this browser.");
655
778
  setVoiceTrigger(null);
656
779
  return;
657
780
  }
781
+ console.log("[ChatInputArea] Calling nativeSpeech.start()...");
658
782
  nativeSpeech.start();
783
+ console.log("[ChatInputArea] nativeSpeech.start() called");
659
784
  } else {
785
+ console.log("[ChatInputArea] Using custom recorder");
660
786
  await customRecorder.start();
787
+ console.log("[ChatInputArea] Custom recorder started");
788
+ }
789
+ if (!isMobile()) {
790
+ console.log("[ChatInputArea] Re-focusing textarea (desktop only)");
791
+ setTimeout(() => {
792
+ var _a3;
793
+ return (_a3 = textareaRef.current) == null ? void 0 : _a3.focus();
794
+ }, 0);
795
+ } else {
796
+ console.log("[ChatInputArea] SKIPPING textarea focus on mobile to prevent keyboard conflict");
661
797
  }
662
- setTimeout(() => {
663
- var _a3;
664
- return (_a3 = textareaRef.current) == null ? void 0 : _a3.focus();
665
- }, 0);
666
798
  };
667
799
  const stopRecording = () => {
668
800
  if (!voiceTrigger) return;
@@ -951,18 +1083,22 @@ var TapToTalk = ({
951
1083
  const isListening = !!voiceTrigger || nativeSpeech.isListening || customRecorder.isRecording;
952
1084
  const isActive = isListening || isTranscribing;
953
1085
  const processingRef = (0, import_react6.useRef)(false);
1086
+ const isMobile = (0, import_react6.useCallback)(() => {
1087
+ if (typeof window === "undefined") return false;
1088
+ return /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent) || "ontouchstart" in window || navigator.maxTouchPoints > 0;
1089
+ }, []);
954
1090
  const toggleVoice = async (e) => {
955
1091
  if (e) {
956
1092
  e.preventDefault();
957
1093
  e.stopPropagation();
958
1094
  }
959
- console.trace("[TapToTalk] toggleVoice called trace");
1095
+ console.log("[TapToTalk] toggleVoice called. isMobile:", isMobile());
960
1096
  if (processingRef.current) {
961
1097
  console.log("[TapToTalk] toggleVoice ignored - processing");
962
1098
  return;
963
1099
  }
964
1100
  processingRef.current = true;
965
- console.log("[TapToTalk] toggleVoice called. isActive:", isActive);
1101
+ console.log("[TapToTalk] toggleVoice called. isActive:", isActive, "isListening:", isListening, "isTranscribing:", isTranscribing);
966
1102
  try {
967
1103
  const now = Date.now();
968
1104
  if (now - tapCountRef.current.lastTap < 500) {
@@ -995,29 +1131,39 @@ var TapToTalk = ({
995
1131
  }
996
1132
  setVoiceTrigger(null);
997
1133
  } else {
998
- console.log("[TapToTalk] Starting voice...");
1134
+ console.log("[TapToTalk] Starting voice... mode:", voiceConfig == null ? void 0 : voiceConfig.mode);
999
1135
  setErrorMsg(null);
1000
- if (onFocusTarget) {
1001
- console.log("[TapToTalk] calling onFocusTarget() - this might trigger keyboard");
1136
+ if (onFocusTarget && !isMobile()) {
1137
+ console.log("[TapToTalk] calling onFocusTarget() (desktop only)");
1002
1138
  onFocusTarget();
1139
+ } else if (onFocusTarget) {
1140
+ console.log("[TapToTalk] SKIPPING onFocusTarget on mobile to prevent keyboard conflict");
1003
1141
  } else {
1004
1142
  console.log("[TapToTalk] onFocusTarget is undefined");
1005
1143
  }
1006
1144
  setVoiceTrigger("click");
1145
+ console.log("[TapToTalk] voiceTrigger set to click");
1007
1146
  if ((voiceConfig == null ? void 0 : voiceConfig.mode) === "custom") {
1147
+ console.log("[TapToTalk] Starting custom recorder...");
1008
1148
  try {
1009
1149
  await customRecorder.start();
1150
+ console.log("[TapToTalk] Custom recorder started successfully");
1010
1151
  } catch (e2) {
1152
+ console.error("[TapToTalk] Custom recorder failed:", e2);
1011
1153
  setErrorMsg("Mic access denied");
1012
1154
  setVoiceTrigger(null);
1013
1155
  }
1014
1156
  } else {
1157
+ console.log("[TapToTalk] Starting native speech recognition...");
1015
1158
  if (!nativeSpeech.isSupported) {
1159
+ console.error("[TapToTalk] Native speech not supported");
1016
1160
  setErrorMsg("Speech not supported");
1017
1161
  setVoiceTrigger(null);
1018
1162
  return;
1019
1163
  }
1164
+ console.log("[TapToTalk] Calling nativeSpeech.start()...");
1020
1165
  nativeSpeech.start();
1166
+ console.log("[TapToTalk] nativeSpeech.start() called");
1021
1167
  }
1022
1168
  }
1023
1169
  } finally {