Auto-deploy: 2026-06-02 18:28:17
This commit is contained in:
@@ -44,15 +44,6 @@
|
|||||||
"post_feed.css"
|
"post_feed.css"
|
||||||
],
|
],
|
||||||
"run_at": "document_idle"
|
"run_at": "document_idle"
|
||||||
},
|
|
||||||
{
|
|
||||||
"matches": [
|
|
||||||
"https://claude.ai/*"
|
|
||||||
],
|
|
||||||
"js": [
|
|
||||||
"claude-arabic-voice/content.js"
|
|
||||||
],
|
|
||||||
"run_at": "document_idle"
|
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"action": {
|
"action": {
|
||||||
@@ -63,15 +54,6 @@
|
|||||||
"128": "icons/icon128.png"
|
"128": "icons/icon128.png"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"web_accessible_resources": [
|
|
||||||
{
|
|
||||||
"resources": [
|
|
||||||
"claude-arabic-voice/speech.html",
|
|
||||||
"claude-arabic-voice/speech.js"
|
|
||||||
],
|
|
||||||
"matches": ["https://claude.ai/*"]
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"background": {
|
"background": {
|
||||||
"service_worker": "background.js"
|
"service_worker": "background.js"
|
||||||
},
|
},
|
||||||
|
|||||||
17
popup.html
17
popup.html
@@ -414,6 +414,23 @@
|
|||||||
|
|
||||||
<div class="content">
|
<div class="content">
|
||||||
|
|
||||||
|
<!-- VOICE DICTATION -->
|
||||||
|
<div class="section" style="background: linear-gradient(135deg, rgba(108, 99, 255, 0.15) 0%, rgba(155, 93, 229, 0.15) 100%); border-color: var(--accent);">
|
||||||
|
<div class="section-header">
|
||||||
|
<span class="section-title">🎤 إملاء صوتي ذكي</span>
|
||||||
|
<button class="btn btn-sm" id="copy-dictation-btn" style="display:none; font-family: Tahoma, sans-serif;">📋 نسخ</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div style="text-align: center; margin-bottom: 12px;">
|
||||||
|
<button id="dictation-mic-btn" style="width: 60px; height: 60px; border-radius: 50%; border: none; background: linear-gradient(135deg, var(--accent), #9b5de5); color: white; font-size: 24px; cursor: pointer; transition: all 0.2s;">
|
||||||
|
🎤
|
||||||
|
</button>
|
||||||
|
<div id="dictation-status" style="font-size: 11px; color: var(--text-secondary); margin-top: 8px; font-family: Tahoma, sans-serif;">اضغط للتحدث (بالعربية)</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<textarea id="dictation-result" placeholder="النص سيظهر هنا..." style="min-height: 80px; direction: rtl; font-family: Tahoma, sans-serif;"></textarea>
|
||||||
|
</div>
|
||||||
|
|
||||||
<!-- QUICK ACTIONS -->
|
<!-- QUICK ACTIONS -->
|
||||||
<div class="section" style="background: linear-gradient(135deg, rgba(108, 99, 255, 0.15) 0%, rgba(155, 93, 229, 0.15) 100%); border-color: var(--accent);">
|
<div class="section" style="background: linear-gradient(135deg, rgba(108, 99, 255, 0.15) 0%, rgba(155, 93, 229, 0.15) 100%); border-color: var(--accent);">
|
||||||
<div class="section-header">
|
<div class="section-header">
|
||||||
|
|||||||
162
popup.js
162
popup.js
@@ -262,7 +262,169 @@ document.getElementById('autofill-btn').addEventListener('click', async () => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// ─── 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();
|
||||||
|
micBtn.style.background = 'linear-gradient(135deg, var(--accent), #9b5de5)';
|
||||||
|
micBtn.innerHTML = '🎤';
|
||||||
|
|
||||||
|
const textToProcess = resultArea.value.trim();
|
||||||
|
if (textToProcess && process) {
|
||||||
|
statusEl.textContent = '✨ جارٍ التحسين بواسطة الذكاء الاصطناعي...';
|
||||||
|
refineWithGemini(textToProcess);
|
||||||
|
} else {
|
||||||
|
statusEl.textContent = 'اضغط للتحدث (بالعربية)';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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 = '❌ يجب السماح باستخدام الميكروفون';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
copyBtn.addEventListener('click', () => {
|
||||||
|
navigator.clipboard.writeText(resultArea.value);
|
||||||
|
const originalText = copyBtn.textContent;
|
||||||
|
copyBtn.textContent = '✅ تم النسخ!';
|
||||||
|
setTimeout(() => { copyBtn.textContent = originalText; }, 2000);
|
||||||
|
});
|
||||||
|
|
||||||
|
async function refineWithGemini(text) {
|
||||||
|
const apiKey = document.getElementById('api-key-input').value.trim();
|
||||||
|
if (!apiKey) {
|
||||||
|
statusEl.textContent = '⚠️ الرجاء إدخال مفتاح Gemini لتحسين النص';
|
||||||
|
copyBtn.style.display = 'inline-flex';
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const prompt = `You are an expert Arabic text refinement assistant. Your task is to:
|
||||||
|
1. Correct any speech recognition errors in the following Arabic text
|
||||||
|
2. Fix punctuation, capitalization, and formatting
|
||||||
|
3. Keep the original meaning and content intact
|
||||||
|
4. Do NOT add any new information or commentary
|
||||||
|
5. Return ONLY the corrected text, nothing else
|
||||||
|
|
||||||
|
TEXT TO REFINE:
|
||||||
|
${text}
|
||||||
|
|
||||||
|
CORRECTED TEXT:`;
|
||||||
|
|
||||||
|
const response = await fetch('https://cv.intaleqapp.com/cv/server/generate_cv.php', {
|
||||||
|
method: 'POST',
|
||||||
|
headers: { 'Content-Type': 'application/json' },
|
||||||
|
body: JSON.stringify({
|
||||||
|
action: 'generateText',
|
||||||
|
apiKey: apiKey,
|
||||||
|
prompt: prompt
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
if (response.ok) {
|
||||||
|
const rawText = await response.text();
|
||||||
|
let data;
|
||||||
|
try { data = JSON.parse(rawText); } catch(e) { data = rawText; }
|
||||||
|
|
||||||
|
let processedText = text;
|
||||||
|
if (data && data.candidates && data.candidates[0]) {
|
||||||
|
processedText = data.candidates[0].content?.parts?.[0]?.text;
|
||||||
|
} else if (typeof data === 'string') {
|
||||||
|
processedText = data;
|
||||||
|
}
|
||||||
|
|
||||||
|
resultArea.value = processedText.trim();
|
||||||
|
statusEl.textContent = '✅ تم التحسين بنجاح! يمكنك النسخ الآن.';
|
||||||
|
copyBtn.style.display = 'inline-flex';
|
||||||
|
} else {
|
||||||
|
throw new Error('Server error');
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
statusEl.textContent = '⚠️ فشل التحسين الذكي، نعرض النص الأصلي.';
|
||||||
|
copyBtn.style.display = 'inline-flex';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// ─── Init ────────────────────────────────────────────────────────────────────
|
// ─── Init ────────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
loadSettings();
|
loadSettings();
|
||||||
|
initDictation();
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user