@contentgrowth/llm-service 0.9.92 → 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.
|
@@ -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
|
-
|
|
183
|
+
var _a;
|
|
173
184
|
if (typeof window !== "undefined") {
|
|
174
185
|
const SpeechRecognition = window.SpeechRecognition || window.webkitSpeechRecognition;
|
|
175
|
-
console.log("[useSpeechRecognition]
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
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
|
-
|
|
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
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
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
|
-
|
|
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, "
|
|
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);
|