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

This commit is contained in:
Hamza-Ayed
2026-06-02 18:34:02 +03:00
parent 6601d7314f
commit 8ebbedad83
2 changed files with 90 additions and 68 deletions

View File

@@ -4,7 +4,25 @@
const GEMINI_MODEL = 'gemini-flash-lite-latest';
const GEMINI_URL = `https://generativelanguage.googleapis.com/v1beta/models/${GEMINI_MODEL}:generateContent`;
// (Offscreen document logic removed in favor of iframe approach)
// ─── Offscreen Document Management ───────────────────────────────────────────
async function setupOffscreenDocument(path) {
const offscreenUrl = chrome.runtime.getURL(path);
const existingContexts = await chrome.runtime.getContexts({
contextTypes: ['OFFSCREEN_DOCUMENT'],
documentUrls: [offscreenUrl]
});
if (existingContexts.length > 0) {
return;
}
await chrome.offscreen.createDocument({
url: path,
reasons: ['USER_MEDIA'],
justification: 'Recording microphone input for speech recognition',
});
}
chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
if (message.type === 'GEMINI_REQUEST') {
@@ -22,7 +40,32 @@ chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
}
if (message.type === 'OPEN_PERMISSION_PAGE') {
chrome.tabs.create({ url: chrome.runtime.getURL('claude-arabic-voice/permission.html') });
chrome.tabs.create({ url: chrome.runtime.getURL('permission.html') });
return true;
}
// --- Offscreen Document Relays for Popup ---
if (message.type === 'START_RECORDING_FROM_POPUP') {
setupOffscreenDocument('claude-arabic-voice/offscreen.html')
.then(() => {
chrome.runtime.sendMessage({
type: 'START_RECORDING',
payload: message.payload
}, (response) => {
sendResponse(response || { success: true });
});
})
.catch(err => {
console.error('Failed to setup offscreen doc', err);
sendResponse({ success: false, error: err.message });
});
return true;
}
if (message.type === 'STOP_RECORDING_FROM_POPUP') {
chrome.runtime.sendMessage({ type: 'STOP_RECORDING' }, (response) => {
sendResponse(response || { success: true });
});
return true;
}
});

107
popup.js
View File

@@ -264,76 +264,22 @@ document.getElementById('autofill-btn').addEventListener('click', async () => {
// ─── Voice Dictation ───────────────────────────────────────────────────────────
// ─── Voice Dictation ───────────────────────────────────────────────────────────
function initDictation() {
const SpeechRecognition = window.SpeechRecognition || window.webkitSpeechRecognition;
const statusEl = document.getElementById('dictation-status');
const micBtn = document.getElementById('dictation-mic-btn');
const resultArea = document.getElementById('dictation-result');
const copyBtn = document.getElementById('copy-dictation-btn');
if (!SpeechRecognition) {
statusEl.textContent = '❌ المتصفح لا يدعم التعرف على الصوت';
micBtn.disabled = true;
micBtn.style.opacity = '0.5';
return;
}
let recognition = new SpeechRecognition();
recognition.continuous = true;
recognition.interimResults = true;
recognition.lang = 'ar-SA';
let isRecording = false;
let finalTranscript = '';
recognition.onstart = () => {
isRecording = true;
micBtn.style.background = 'linear-gradient(135deg, #ff4d6d, #c9184a)';
micBtn.innerHTML = '🔴';
micBtn.style.animation = 'claude-voice-pulse 1.5s infinite'; // We can add this via inline or just rely on color
statusEl.textContent = 'جارٍ الاستماع... اضغط للإيقاف';
finalTranscript = '';
resultArea.value = '';
copyBtn.style.display = 'none';
};
recognition.onresult = (event) => {
let interimTranscript = '';
let currentFinal = '';
for (let i = event.resultIndex; i < event.results.length; i++) {
const transcript = event.results[i][0].transcript;
if (event.results[i].isFinal) {
currentFinal += transcript + ' ';
} else {
interimTranscript += transcript;
}
}
finalTranscript += currentFinal;
resultArea.value = finalTranscript + interimTranscript;
};
recognition.onerror = (event) => {
console.error('Speech recognition error', event.error);
if (event.error === 'not-allowed') {
statusEl.textContent = '❌ يرجى السماح للميكروفون من إعدادات المتصفح';
} else if (event.error !== 'no-speech') {
statusEl.textContent = '❌ خطأ: ' + event.error;
}
stopRecording(false);
};
recognition.onend = () => {
if (isRecording) {
stopRecording(true);
}
};
function stopRecording(process = true) {
if (!isRecording) return;
isRecording = false;
recognition.stop();
chrome.runtime.sendMessage({ type: 'STOP_RECORDING_FROM_POPUP' });
micBtn.style.background = 'linear-gradient(135deg, var(--accent), #9b5de5)';
micBtn.innerHTML = '🎤';
@@ -346,18 +292,51 @@ function initDictation() {
}
}
// Listen to messages from the offscreen document via the background
chrome.runtime.onMessage.addListener((message) => {
if (message.type === 'OFFSCREEN_RECORDING_RESULT') {
interimTranscript = message.payload.interimText || '';
finalTranscript = message.payload.finalText || '';
resultArea.value = (finalTranscript + interimTranscript).trim();
} else if (message.type === 'OFFSCREEN_RECORDING_ERROR') {
console.error('Speech recognition error', message.payload.error);
if (message.payload.error === 'not-allowed') {
statusEl.textContent = '❌ يرجى السماح للميكروفون من إعدادات المتصفح';
chrome.tabs.create({ url: chrome.runtime.getURL('permission.html') });
} else if (message.payload.error !== 'no-speech') {
statusEl.textContent = '❌ خطأ: ' + message.payload.error;
}
stopRecording(false);
} else if (message.type === 'OFFSCREEN_RECORDING_END') {
if (isRecording) {
stopRecording(true);
}
}
});
micBtn.addEventListener('click', async () => {
if (isRecording) {
stopRecording(true);
} else {
try {
const stream = await navigator.mediaDevices.getUserMedia({ audio: true });
stream.getTracks().forEach(t => t.stop());
recognition.start();
} catch (err) {
statusEl.textContent = '❌ يجب السماح باستخدام الميكروفون';
chrome.tabs.create({ url: chrome.runtime.getURL('permission.html') });
isRecording = true;
micBtn.style.background = 'linear-gradient(135deg, #ff4d6d, #c9184a)';
micBtn.innerHTML = '🔴';
statusEl.textContent = 'جارٍ الاستماع... اضغط للإيقاف';
finalTranscript = '';
interimTranscript = '';
resultArea.value = '';
copyBtn.style.display = 'none';
chrome.runtime.sendMessage({
type: 'START_RECORDING_FROM_POPUP',
payload: { language: 'ar-SA' }
}, (response) => {
if (!response || !response.success) {
console.error('Failed to start recording', response);
statusEl.textContent = '❌ فشل بدء التسجيل';
stopRecording(false);
}
});
}
});