Files
cv/speech.js
2026-06-02 18:48:35 +03:00

121 lines
3.7 KiB
JavaScript

// speech.js - Handles webkitSpeechRecognition inside the injected iframe
let recognition = null;
let isRecording = false;
let localStream = null;
function stopMediaTracks() {
if (localStream) {
localStream.getTracks().forEach(track => track.stop());
localStream = null;
}
}
function initRecognition(language) {
if (recognition) return recognition;
const SpeechRecognition = window.SpeechRecognition || window.webkitSpeechRecognition;
if (!SpeechRecognition) {
throw new Error('Speech recognition not supported');
}
recognition = new SpeechRecognition();
recognition.continuous = true;
recognition.interimResults = true;
recognition.lang = language || 'ar-SA';
recognition.maxAlternatives = 1;
recognition.onresult = (event) => {
let interimText = '';
let finalText = '';
for (let i = 0; i < event.results.length; i++) {
const result = event.results[i];
if (result.isFinal) {
finalText += result[0].transcript + ' ';
} else {
interimText += result[0].transcript;
}
}
window.parent.postMessage({
type: 'SPEECH_RESULT',
payload: { interimText, finalText }
}, '*');
};
recognition.onerror = (event) => {
console.error('[Speech Iframe] Recognition error:', event.error);
if (event.error !== 'no-speech') {
isRecording = false;
stopMediaTracks();
}
window.parent.postMessage({
type: 'SPEECH_ERROR',
payload: { error: event.error }
}, '*');
};
recognition.onend = () => {
if (isRecording) {
try {
recognition.start();
} catch (e) {
console.warn('[Speech Iframe] Restart failed:', e);
}
} else {
stopMediaTracks();
window.parent.postMessage({ type: 'SPEECH_END' }, '*');
}
};
return recognition;
}
// Listen for messages broadcasted across the extension
window.addEventListener('message', (event) => {
const message = event.data;
if (!message || !message.type) return;
if (message.type === 'START_RECORDING_FROM_POPUP') {
const lang = message.payload?.language || 'ar-SA';
navigator.mediaDevices.getUserMedia({ audio: true })
.then((stream) => {
localStream = stream;
try {
if (!recognition || recognition.lang !== lang) {
recognition = initRecognition(lang);
}
if (!isRecording) {
recognition.start();
isRecording = true;
// Tell popup it started successfully
window.parent.postMessage({ type: 'SPEECH_START_SUCCESS' }, '*');
}
} catch (e) {
console.error('[Speech Iframe] Failed to start:', e);
window.parent.postMessage({ type: 'SPEECH_ERROR', payload: { error: e.message } }, '*');
}
})
.catch((err) => {
console.error('[Speech Iframe] getUserMedia failed:', err);
window.parent.postMessage({ type: 'SPEECH_ERROR', payload: { error: 'not-allowed' } }, '*');
});
}
if (message.type === 'STOP_RECORDING_FROM_POPUP') {
if (isRecording && recognition) {
isRecording = false;
try {
recognition.stop();
} catch (e) {
console.warn('[Speech Iframe] Stop failed:', e);
}
}
stopMediaTracks();
}
});