test11 / index.html
joermd's picture
Update index.html
ef9d07b verified
<!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>