<!DOCTYPE html> <html dir="rtl" lang="ar"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>محادثة صوتية</title> <style> /* Previous styles remain the same */ body { font-family: Arial, sans-serif; margin: 0; padding: 0; background: #ffffff; min-height: 100vh; display: flex; flex-direction: column; align-items: center; } .container { width: 100%; min-height: 100vh; display: flex; flex-direction: column; align-items: center; position: relative; } .record-button-container { width: 100%; height: 100vh; display: flex; flex-direction: column; justify-content: center; align-items: center; background: #ffffff; } .record-button { width: 60vmin; height: 60vmin; border-radius: 50%; background: rgba(40, 167, 69, 0.1); border: none; cursor: pointer; display: flex; align-items: center; justify-content: center; position: relative; transition: all 0.3s ease; box-shadow: 0 10px 30px rgba(40, 167, 69, 0.2); } .record-button:hover { transform: scale(1.02); box-shadow: 0 15px 40px rgba(40, 167, 69, 0.3); } .record-button.recording { background: rgba(40, 167, 69, 0.2); animation: pulse 2s infinite; } /* Modified inner shape styles */ .inner-shape { width: 40%; height: 40%; background: #28a745; transition: all 0.3s ease; box-shadow: 0 5px 15px rgba(40, 167, 69, 0.2); } /* Circle state (default) */ .inner-shape { border-radius: 50%; } /* Square state (recording) */ .record-button.recording .inner-shape { border-radius: 15%; transform: scale(0.8); } .wave-container { margin-top: 30px; display: flex; justify-content: center; align-items: center; gap: 5px; height: 50px; } .wave { width: 4px; height: 20px; background: rgba(40, 167, 69, 0.6); border-radius: 2px; animation: waveAnimation 1s ease-in-out infinite; } .wave:nth-child(2) { animation-delay: 0.1s; } .wave:nth-child(3) { animation-delay: 0.2s; } .wave:nth-child(4) { animation-delay: 0.3s; } .wave:nth-child(5) { animation-delay: 0.4s; } .wave:nth-child(6) { animation-delay: 0.5s; } .wave:nth-child(7) { animation-delay: 0.6s; } .wave:nth-child(8) { animation-delay: 0.7s; } @keyframes waveAnimation { 0%, 100% { transform: scaleY(0.5); } 50% { transform: scaleY(2); } } .status-text { text-align: center; color: #28a745; font-size: 1.5rem; font-weight: bold; margin-top: 20px; } .toggle-code-button { position: fixed; top: 20px; right: 20px; padding: 10px 20px; background: #28a745; border: none; border-radius: 25px; color: white; cursor: pointer; transition: all 0.3s ease; z-index: 1000; } .toggle-code-button:hover { background: #218838; } .code-section { display: none; position: fixed; top: 0; left: 0; width: 100%; height: 100%; background: rgba(255, 255, 255, 0.95); z-index: 999; overflow-y: auto; padding: 20px; box-sizing: border-box; } .code-section.visible { display: block; } .message-container { background: rgba(40, 167, 69, 0.1); padding: 15px; border-radius: 15px; margin: 10px; color: #333; width: 90%; max-width: 600px; } .message-label { font-weight: bold; margin-bottom: 8px; color: #28a745; } .message-content { padding: 10px; background: rgba(40, 167, 69, 0.05); border-radius: 8px; min-height: 40px; } .voice-settings { position: fixed; bottom: 20px; width: 90%; max-width: 600px; padding: 15px; background: rgba(40, 167, 69, 0.1); border-radius: 15px; } select { width: 100%; padding: 12px; border-radius: 10px; border: 1px solid #28a745; background: white; color: #28a745; font-size: 16px; } @keyframes pulse { 0% { transform: scale(1); } 50% { transform: scale(1.05); } 100% { transform: scale(1); } } </style> </head> <body> <div class="container"> <button class="toggle-code-button" id="toggleCode">عرض النص</button> <div class="record-button-container"> <button class="record-button" id="recordButton"> <div class="inner-shape"></div> </button> <div class="wave-container"> <div class="wave"></div> <div class="wave"></div> <div class="wave"></div> <div class="wave"></div> <div class="wave"></div> <div class="wave"></div> <div class="wave"></div> <div class="wave"></div> </div> </div> <div class="status-text" id="statusText">انقر للبدء في التسجيل</div> <div class="code-section" id="codeSection"> <div class="message-container"> <div class="message-label">ما قلته:</div> <div class="message-content" id="spokenText"></div> </div> <div class="message-container"> <div class="message-label">رد النموذج:</div> <div class="message-content" id="modelResponse"></div> </div> <div class="voice-settings"> <select id="voiceSelect"> <option value="">اختر صوت رجل عربي فصحى</option> </select> </div> </div> </div> <script> const API_URL = 'https://g2mgow5tgbxsjy-7777.proxy.runpod.net/proxy/8000/chat'; const recordButton = document.getElementById('recordButton'); const statusText = document.getElementById('statusText'); const spokenText = document.getElementById('spokenText'); const modelResponse = document.getElementById('modelResponse'); const voiceSelect = document.getElementById('voiceSelect'); const toggleCodeButton = document.getElementById('toggleCode'); const codeSection = document.getElementById('codeSection'); const waveContainer = document.querySelector('.wave-container'); let isRecording = false; toggleCodeButton.onclick = () => { codeSection.classList.toggle('visible'); toggleCodeButton.textContent = codeSection.classList.contains('visible') ? 'إخفاء النص' : 'عرض النص'; }; const recognition = new (window.SpeechRecognition || window.webkitSpeechRecognition)(); recognition.lang = 'ar-SA'; recognition.continuous = false; recognition.interimResults = false; function loadVoices() { const voices = speechSynthesis.getVoices(); voiceSelect.innerHTML = '<option value="">اختر صوت رجل عربي فصحى</option>'; voices.forEach((voice, index) => { if (voice.lang.includes('ar') && (voice.name.includes('Male') || voice.name.includes('رجل'))) { const option = document.createElement('option'); option.value = index; option.textContent = voice.name; if (voice.default) { option.selected = true; } voiceSelect.appendChild(option); } }); if (voiceSelect.options.length === 1) { voices.forEach((voice, index) => { if (voice.name.includes('Male')) { const option = document.createElement('option'); option.value = index; option.textContent = voice.name; voiceSelect.appendChild(option); } }); } } speechSynthesis.onvoiceschanged = loadVoices; loadVoices(); recognition.onresult = async (event) => { const text = event.results[0][0].transcript; spokenText.textContent = text; try { const response = await fetch(API_URL, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ message: text }) }); const data = await response.json(); const botReply = data.response; modelResponse.textContent = botReply; const utterance = new SpeechSynthesisUtterance(botReply); const voices = speechSynthesis.getVoices(); const selectedVoice = voices[voiceSelect.value || 0]; utterance.voice = selectedVoice; utterance.lang = 'ar-SA'; utterance.rate = 1; utterance.pitch = 1; speechSynthesis.speak(utterance); } catch (error) { modelResponse.textContent = 'حدث خطأ في الاتصال بالنموذج'; statusText.textContent = 'حدث خطأ في الاتصال'; } }; recognition.onerror = (event) => { console.error('خطأ:', event.error); statusText.textContent = 'حدث خطأ في التعرف على الكلام'; isRecording = false; recordButton.classList.remove('recording'); }; recognition.onend = () => { isRecording = false; recordButton.classList.remove('recording'); statusText.textContent = 'انقر للبدء في التسجيل'; }; recordButton.onclick = () => { if (!isRecording) { recognition.start(); isRecording = true; recordButton.classList.add('recording'); statusText.textContent = 'جارٍ التسجيل... انقر للإيقاف'; spokenText.textContent = ''; modelResponse.textContent = ''; } else { recognition.stop(); isRecording = false; recordButton.classList.remove('recording'); statusText.textContent = 'جارٍ معالجة الكلام...'; } }; </script> </body> </html>