@contentgrowth/llm-service 0.9.8 → 0.9.91

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.
@@ -209,6 +209,7 @@ var useSpeechRecognition = (onResult, onEnd, language = "en-US") => {
209
209
  onResultRef.current = onResult;
210
210
  onEndRef.current = onEnd;
211
211
  }, [onResult, onEnd]);
212
+ const isStartingRef = (0, import_react2.useRef)(false);
212
213
  (0, import_react2.useEffect)(() => {
213
214
  if (typeof window !== "undefined") {
214
215
  const SpeechRecognition = window.SpeechRecognition || window.webkitSpeechRecognition;
@@ -219,11 +220,13 @@ var useSpeechRecognition = (onResult, onEnd, language = "en-US") => {
219
220
  recognition.interimResults = true;
220
221
  recognition.onstart = () => {
221
222
  console.log("[useSpeechRecognition] Native onstart event fired");
223
+ isStartingRef.current = false;
222
224
  setIsListening(true);
223
225
  setError(null);
224
226
  };
225
227
  recognition.onend = () => {
226
228
  console.log("[useSpeechRecognition] Native onend event fired");
229
+ isStartingRef.current = false;
227
230
  if (isSimulatingRef.current) {
228
231
  return;
229
232
  }
@@ -247,6 +250,7 @@ var useSpeechRecognition = (onResult, onEnd, language = "en-US") => {
247
250
  };
248
251
  recognition.onerror = (event) => {
249
252
  console.error("[useSpeechRecognition] Native onerror event:", event.error);
253
+ isStartingRef.current = false;
250
254
  if (event.error === "not-allowed" && process.env.NODE_ENV === "development") {
251
255
  console.warn("Speech recognition blocked. Simulating input for development...");
252
256
  isSimulatingRef.current = true;
@@ -288,20 +292,36 @@ var useSpeechRecognition = (onResult, onEnd, language = "en-US") => {
288
292
  }
289
293
  }, [language]);
290
294
  const start = (0, import_react2.useCallback)(() => {
291
- console.log("[useSpeechRecognition] start() called");
292
- if (isSimulatingRef.current) return;
295
+ console.log("[useSpeechRecognition] start() called. isListening:", isListening, "isStarting:", isStartingRef.current, "hasInstance:", !!recognitionRef.current);
296
+ if (isSimulatingRef.current) {
297
+ console.log("[useSpeechRecognition] isSimulating, ignoring start");
298
+ return;
299
+ }
293
300
  if (!recognitionRef.current) {
294
301
  console.error("[useSpeechRecognition] Recognition instance missing");
295
302
  return;
296
303
  }
304
+ if (isStartingRef.current) {
305
+ console.warn("[useSpeechRecognition] Already starting - ignoring duplicate call");
306
+ return;
307
+ }
308
+ if (recognitionRef.current.isListening) {
309
+ console.warn("[useSpeechRecognition] Already listening (native prop) - ignoring");
310
+ }
311
+ if (isListening) {
312
+ console.warn("[useSpeechRecognition] App state says already listening - ignoring");
313
+ return;
314
+ }
297
315
  try {
298
316
  setTranscript("");
317
+ isStartingRef.current = true;
299
318
  recognitionRef.current.start();
300
- console.log("[useSpeechRecognition] recognition.start() executed");
319
+ console.log("[useSpeechRecognition] recognition.start() executed successfully");
301
320
  } catch (error2) {
321
+ isStartingRef.current = false;
302
322
  console.error("[useSpeechRecognition] Failed to start recognition:", error2);
303
323
  }
304
- }, []);
324
+ }, [isListening]);
305
325
  const stop = (0, import_react2.useCallback)(() => {
306
326
  console.log("[useSpeechRecognition] stop() called");
307
327
  if (isSimulatingRef.current) {
@@ -930,58 +950,80 @@ var TapToTalk = ({
930
950
  });
931
951
  const isListening = !!voiceTrigger || nativeSpeech.isListening || customRecorder.isRecording;
932
952
  const isActive = isListening || isTranscribing;
933
- const toggleVoice = async () => {
934
- console.log("[TapToTalk] toggleVoice called. isActive:", isActive);
935
- const now = Date.now();
936
- if (now - tapCountRef.current.lastTap < 500) {
937
- tapCountRef.current.count++;
938
- } else {
939
- tapCountRef.current.count = 1;
953
+ const processingRef = (0, import_react6.useRef)(false);
954
+ const toggleVoice = async (e) => {
955
+ if (e) {
956
+ e.preventDefault();
957
+ e.stopPropagation();
940
958
  }
941
- tapCountRef.current.lastTap = now;
942
- if (tapCountRef.current.count >= 5) {
943
- setShowDebug((prev) => !prev);
944
- tapCountRef.current.count = 0;
945
- if (isActive) {
946
- console.log("[TapToTalk] Debug trigger force-stop");
947
- if ((voiceConfig == null ? void 0 : voiceConfig.mode) === "native") nativeSpeech.stop();
948
- else customRecorder.stop();
949
- setVoiceTrigger(null);
950
- }
959
+ console.trace("[TapToTalk] toggleVoice called trace");
960
+ if (processingRef.current) {
961
+ console.log("[TapToTalk] toggleVoice ignored - processing");
951
962
  return;
952
963
  }
953
- if (isActive) {
954
- if (isTranscribing && !isListening) {
955
- console.log("[TapToTalk] Ignoring click during transcription");
956
- return;
957
- }
958
- console.log("[TapToTalk] Stopping voice...");
959
- if ((voiceConfig == null ? void 0 : voiceConfig.mode) === "native") {
960
- nativeSpeech.stop();
964
+ processingRef.current = true;
965
+ console.log("[TapToTalk] toggleVoice called. isActive:", isActive);
966
+ try {
967
+ const now = Date.now();
968
+ if (now - tapCountRef.current.lastTap < 500) {
969
+ tapCountRef.current.count++;
961
970
  } else {
962
- customRecorder.stop();
971
+ tapCountRef.current.count = 1;
963
972
  }
964
- setVoiceTrigger(null);
965
- } else {
966
- console.log("[TapToTalk] Starting voice...");
967
- setErrorMsg(null);
968
- onFocusTarget == null ? void 0 : onFocusTarget();
969
- setVoiceTrigger("click");
970
- if ((voiceConfig == null ? void 0 : voiceConfig.mode) === "custom") {
971
- try {
972
- await customRecorder.start();
973
- } catch (e) {
974
- setErrorMsg("Mic access denied");
973
+ tapCountRef.current.lastTap = now;
974
+ if (tapCountRef.current.count >= 5) {
975
+ setShowDebug((prev) => !prev);
976
+ tapCountRef.current.count = 0;
977
+ if (isActive) {
978
+ console.log("[TapToTalk] Debug trigger force-stop");
979
+ if ((voiceConfig == null ? void 0 : voiceConfig.mode) === "native") nativeSpeech.stop();
980
+ else customRecorder.stop();
975
981
  setVoiceTrigger(null);
976
982
  }
977
- } else {
978
- if (!nativeSpeech.isSupported) {
979
- setErrorMsg("Speech not supported");
980
- setVoiceTrigger(null);
983
+ return;
984
+ }
985
+ if (isActive) {
986
+ if (isTranscribing && !isListening) {
987
+ console.log("[TapToTalk] Ignoring click during transcription");
981
988
  return;
982
989
  }
983
- nativeSpeech.start();
990
+ console.log("[TapToTalk] Stopping voice...");
991
+ if ((voiceConfig == null ? void 0 : voiceConfig.mode) === "native") {
992
+ nativeSpeech.stop();
993
+ } else {
994
+ customRecorder.stop();
995
+ }
996
+ setVoiceTrigger(null);
997
+ } else {
998
+ console.log("[TapToTalk] Starting voice...");
999
+ setErrorMsg(null);
1000
+ if (onFocusTarget) {
1001
+ console.log("[TapToTalk] calling onFocusTarget() - this might trigger keyboard");
1002
+ onFocusTarget();
1003
+ } else {
1004
+ console.log("[TapToTalk] onFocusTarget is undefined");
1005
+ }
1006
+ setVoiceTrigger("click");
1007
+ if ((voiceConfig == null ? void 0 : voiceConfig.mode) === "custom") {
1008
+ try {
1009
+ await customRecorder.start();
1010
+ } catch (e2) {
1011
+ setErrorMsg("Mic access denied");
1012
+ setVoiceTrigger(null);
1013
+ }
1014
+ } else {
1015
+ if (!nativeSpeech.isSupported) {
1016
+ setErrorMsg("Speech not supported");
1017
+ setVoiceTrigger(null);
1018
+ return;
1019
+ }
1020
+ nativeSpeech.start();
1021
+ }
984
1022
  }
1023
+ } finally {
1024
+ setTimeout(() => {
1025
+ processingRef.current = false;
1026
+ }, 300);
985
1027
  }
986
1028
  };
987
1029
  let bgColor = accentColor;