Auto-deploy: 2026-06-02 18:02:41

This commit is contained in:
Hamza-Ayed
2026-06-02 18:02:41 +03:00
parent d5919fbf01
commit 36e4dd42e4
5 changed files with 212 additions and 97 deletions

View File

@@ -49,76 +49,35 @@
});
}
// ─── Speech Recognition Setup ────────────────────────────────────────────
function initSpeechRecognition() {
const SpeechRecognition = window.SpeechRecognition || window.webkitSpeechRecognition;
if (!SpeechRecognition) {
console.warn('[ClaudeVoice] Speech recognition not supported in this browser');
return null;
}
const recog = new SpeechRecognition();
recog.continuous = true;
recog.interimResults = true;
recog.lang = settings.language;
recog.maxAlternatives = 1;
recog.onresult = (event) => {
// ─── Offscreen Document Integration ──────────────────────────────────────
chrome.runtime.onMessage.addListener((message) => {
if (message.type === 'OFFSCREEN_RECORDING_RESULT') {
lastSpeechTime = Date.now();
interimText = '';
finalText = '';
for (let i = event.resultIndex; i < event.results.length; i++) {
const result = event.results[i];
if (result.isFinal) {
finalText += result[0].transcript + ' ';
} else {
interimText += result[0].transcript;
}
}
interimText = message.payload.interimText || '';
finalText = message.payload.finalText || '';
updateInputField();
// Reset silence timer on new speech
clearTimeout(silenceTimer);
silenceTimer = setTimeout(() => {
if (isListening && Date.now() - lastSpeechTime >= SILENCE_TIMEOUT) {
stopListening();
}
}, SILENCE_TIMEOUT);
};
recog.onerror = (event) => {
console.error('[ClaudeVoice] Recognition error:', event.error);
if (event.error === 'no-speech') {
// No speech detected, restart if still listening
if (isListening) {
try { recog.stop(); } catch (e) { }
setTimeout(() => {
if (isListening) {
try { recog.start(); } catch (e) { }
}
}, 300);
}
return;
} else if (message.type === 'OFFSCREEN_RECORDING_ERROR') {
console.error('[ClaudeVoice] Recognition error:', message.payload.error);
if (message.payload.error === 'no-speech') {
return; // offscreen script restarts it automatically
}
stopListening();
showStatus('error', '❌ خطأ: ' + getArabicError(event.error));
};
recog.onend = () => {
// If we're still supposed to be listening, restart
showStatus('error', '❌ خطأ: ' + getArabicError(message.payload.error));
} else if (message.type === 'OFFSCREEN_RECORDING_END') {
if (isListening) {
try {
recog.start();
} catch (e) {
console.warn('[ClaudeVoice] Restart failed:', e);
}
// Unexpected end from offscreen while we still consider ourselves listening
stopListening();
}
};
return recog;
}
}
});
function getArabicError(error) {
const errors = {
@@ -137,59 +96,51 @@
async function startListening() {
await loadSettings();
if (!recognition) {
recognition = initSpeechRecognition();
}
if (!recognition) {
showStatus('error', '❌ المتصفح لا يدعم التعرف على الصوت');
return;
}
// Update language in case it changed
recognition.lang = settings.language;
try {
recognition.start();
isListening = true;
lastSpeechTime = Date.now();
updateMicButton(true);
showStatus('listening', '🎤 جارٍ الاستماع...');
chrome.runtime.sendMessage({
type: 'START_RECORDING_FROM_CONTENT',
payload: { language: settings.language }
}, (response) => {
if (response && response.success) {
isListening = true;
lastSpeechTime = Date.now();
interimText = '';
finalText = '';
updateMicButton(true);
showStatus('listening', '🎤 جارٍ الاستماع...');
// Auto-stop after max time
clearTimeout(autoSendTimer);
autoSendTimer = setTimeout(() => {
if (isListening) stopListening();
}, MAX_RECORDING_TIME);
clearTimeout(autoSendTimer);
autoSendTimer = setTimeout(() => {
if (isListening) stopListening();
}, MAX_RECORDING_TIME);
// Silence detection
clearTimeout(silenceTimer);
silenceTimer = setTimeout(() => {
if (isListening && Date.now() - lastSpeechTime >= SILENCE_TIMEOUT) {
stopListening();
clearTimeout(silenceTimer);
silenceTimer = setTimeout(() => {
if (isListening && Date.now() - lastSpeechTime >= SILENCE_TIMEOUT) {
stopListening();
}
}, SILENCE_TIMEOUT);
} else {
console.error('[ClaudeVoice] Start failed:', response?.error);
showStatus('error', '❌ فشل بدء التسجيل');
}
}, SILENCE_TIMEOUT);
});
} catch (e) {
console.error('[ClaudeVoice] Start failed:', e);
showStatus('error', '❌ فشل بدء التسجيل');
console.error('[ClaudeVoice] Start message failed:', e);
showStatus('error', '❌ فشل الاتصال بالإضافة');
}
}
async function stopListening() {
if (!recognition) return;
if (!isListening) return;
isListening = false;
clearTimeout(silenceTimer);
clearTimeout(autoSendTimer);
try {
recognition.stop();
} catch (e) { /* ignore */ }
chrome.runtime.sendMessage({ type: 'STOP_RECORDING_FROM_CONTENT' });
updateMicButton(false);
// If we have final text, process it
const text = finalText.trim() || interimText.trim();
if (text) {
showStatus('processing', '⏳ جارٍ المعالجة...');
@@ -197,7 +148,6 @@
if (settings.useGemini && settings.geminiApiKey) {
await processWithGemini(text);
} else {
// Just insert the text directly
insertTextIntoClaude(text);
showStatus('done', '✅ تم الإدراج');
setTimeout(() => hideStatus(), 2000);