lahjabot / index.html
wasmdashai's picture
Update index.html
3fae9cb verified
<!DOCTYPE html>
<html lang="ar" dir="rtl">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>مساعد صوتي متحرك - وضع ليلي/نهاري</title>
<style>
* { margin:0; padding:0; box-sizing:border-box; font-family:'Segoe UI','Noto Sans Arabic',sans-serif; }
body {
background:#fff;
display:flex;
justify-content:center;
align-items:center;
min-height:100vh;
flex-direction:column;
color: rgb(11, 186, 131);
transition: all 0.3s ease;
overflow: hidden;
position: relative;
}
/* تنسيقات الوضع الليلي */
body.night-mode {
background: #1a202c;
color: rgb(11, 186, 131);
}
/* تأثير خلفية ديناميكي */
.background-effect {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
z-index: -1;
opacity: 0.1;
background: radial-gradient(circle at 20% 50%, rgba(11, 186, 131, 0.4) 0%, transparent 50%),
radial-gradient(circle at 80% 80%, rgba(11, 186, 131, 0.3) 0%, transparent 40%);
animation: backgroundMove 20s infinite alternate ease-in-out;
}
@keyframes backgroundMove {
0% { transform: scale(1) rotate(0deg); }
100% { transform: scale(1.2) rotate(5deg); }
}
.night-mode .background-effect {
background: radial-gradient(circle at 20% 50%, rgba(11, 186, 131, 0.2) 0%, transparent 50%),
radial-gradient(circle at 80% 80%, rgba(11, 186, 131, 0.15) 0%, transparent 40%);
}
.circle-outer {
width:220px; height:220px;
border-radius:50%;
background: rgba(11, 186, 131, 0.15);
display:flex; justify-content:center; align-items:center;
cursor:pointer; transition: all 0.3s ease;
box-shadow: 0 15px 30px rgba(0,0,0,0.1);
position: relative;
overflow:hidden;
animation: subtlePulse 3s infinite ease-in-out;
z-index: 10;
}
@keyframes subtlePulse {
0% { transform: scale(1); box-shadow: 0 15px 30px rgba(0,0,0,0.1); }
50% { transform: scale(1.02); box-shadow: 0 20px 40px rgba(0,0,0,0.15); }
100% { transform: scale(1); box-shadow: 0 15px 30px rgba(0,0,0,0.1); }
}
.night-mode .circle-outer {
background: rgba(11, 186, 131, 0.15);
box-shadow: 0 15px 30px rgba(0,0,0,0.3);
animation: subtlePulseNight 3s infinite ease-in-out;
}
@keyframes subtlePulseNight {
0% { transform: scale(1); box-shadow: 0 15px 30px rgba(0,0,0,0.3); }
50% { transform: scale(1.02); box-shadow: 0 20px 40px rgba(0,0,0,0.4); }
100% { transform: scale(1); box-shadow: 0 15px 30px rgba(0,0,0,0.3); }
}
.circle-middle {
width:180px; height:180px;
border-radius:50%;
background: rgba(11, 186, 131, 0.25);
display:flex; justify-content:center; align-items:center;
transition: all 0.3s ease;
animation: middlePulse 4s infinite ease-in-out;
}
@keyframes middlePulse {
0% { transform: scale(1); background: rgba(11, 186, 131, 0.25); }
50% { transform: scale(1.03); background: rgba(11, 186, 131, 0.3); }
100% { transform: scale(1); background: rgba(11, 186, 131, 0.25); }
}
.night-mode .circle-middle {
background: rgba(11, 186, 131, 0.25);
animation: middlePulseNight 4s infinite ease-in-out;
}
@keyframes middlePulseNight {
0% { transform: scale(1); background: rgba(11, 186, 131, 0.25); }
50% { transform: scale(1.03); background: rgba(11, 186, 131, 0.3); }
100% { transform: scale(1); background: rgba(11, 186, 131, 0.25); }
}
.circle-inner {
width:140px; height:140px;
border-radius:50%;
background:white;
display:flex; justify-content:center; align-items:center;
box-shadow: 0 5px 15px rgba(0,0,0,0.1);
transition: all 0.3s ease;
animation: innerGlow 5s infinite alternate;
}
@keyframes innerGlow {
0% { box-shadow: 0 5px 15px rgba(0,0,0,0.1); }
100% { box-shadow: 0 5px 25px rgba(11, 186, 131, 0.3); }
}
.night-mode .circle-inner {
background: #2d3748;
box-shadow: 0 5px 15px rgba(0,0,0,0.3);
animation: innerGlowNight 5s infinite alternate;
}
@keyframes innerGlowNight {
0% { box-shadow: 0 5px 15px rgba(0,0,0,0.3); }
100% { box-shadow: 0 5px 25px rgba(11, 186, 131, 0.4); }
}
.mic-svg {
width:60px; height:60px;
fill: rgb(11, 186, 131);
transition: all 0.3s ease;
animation: iconFloat 6s infinite ease-in-out;
}
@keyframes iconFloat {
0% { transform: translateY(0) scale(1); }
50% { transform: translateY(-5px) scale(1.05); }
100% { transform: translateY(0) scale(1); }
}
.night-mode .mic-svg {
fill: rgb(11, 186, 131);
}
/* أثناء الاستماع */
.circle-outer.listening {
animation: rotatePulse 1.5s infinite linear, colorShift 3s infinite alternate;
}
@keyframes rotatePulse {
0% { transform: rotate(0deg) scale(1); }
25% { transform: rotate(5deg) scale(1.05); }
50% { transform: rotate(0deg) scale(1.1); }
75% { transform: rotate(-5deg) scale(1.05); }
100% { transform: rotate(0deg) scale(1); }
}
@keyframes colorShift {
0% { background: rgba(11, 186, 131, 0.15); }
25% { background: rgba(11, 186, 131, 0.25); }
50% { background: rgba(11, 186, 131, 0.35); }
75% { background: rgba(11, 186, 131, 0.25); }
100% { background: rgba(11, 186, 131, 0.15); }
}
.night-mode .circle-outer.listening {
animation: rotatePulse 1.5s infinite linear, colorShiftNight 3s infinite alternate;
}
@keyframes colorShiftNight {
0% { background: rgba(11, 186, 131, 0.15); }
25% { background: rgba(11, 186, 131, 0.25); }
50% { background: rgba(11, 186, 131, 0.35); }
75% { background: rgba(11, 186, 131, 0.25); }
100% { background: rgba(11, 186, 131, 0.15); }
}
/* حركة الميكروفون أثناء الرد */
.circle-outer.speaking .mic-svg {
animation: speakIcon 0.8s infinite alternate, glowMic 1.2s infinite alternate;
}
@keyframes speakIcon {
0% { transform: translateY(0) scale(1); }
25% { transform: translateY(-8px) scale(1.1); }
50% { transform: translateY(0) scale(1); }
75% { transform: translateY(8px) scale(1.1); }
100% { transform: translateY(0) scale(1); }
}
@keyframes glowMic {
0% { filter: drop-shadow(0 0 0 rgba(11, 186, 131, 0.4)); }
50% { filter: drop-shadow(0 0 15px rgba(11, 186, 131, 0.8)); }
100% { filter: drop-shadow(0 0 0 rgba(11, 186, 131, 0.4)); }
}
.night-mode .circle-outer.speaking .mic-svg {
animation: speakIcon 0.8s infinite alternate, glowMicNight 1.2s infinite alternate;
}
@keyframes glowMicNight {
0% { filter: drop-shadow(0 0 0 rgba(11, 186, 131, 0.4)); }
50% { filter: drop-shadow(0 0 15px rgba(11, 186, 131, 0.8)); }
100% { filter: drop-shadow(0 0 0 rgba(11, 186, 131, 0.4)); }
}
/* موجات نابضة حول الدائرة - دائمة الآن */
.pulse-wave {
position:absolute;
border:2px solid rgba(11, 186, 131, 0.5);
border-radius:50%;
width:220px;
height:220px;
top:0; left:0;
animation: pulse 3s infinite ease-in-out;
opacity: 0.7;
}
.pulse-wave:nth-child(2) {
animation-delay: 1s;
}
.pulse-wave:nth-child(3) {
animation-delay: 2s;
}
@keyframes pulse {
0% { transform: scale(1); opacity:0.5; }
50% { transform: scale(1.3); opacity:0.2; }
100% { transform: scale(1.6); opacity:0; }
}
.night-mode .pulse-wave {
border:2px solid rgba(11, 186, 131, 0.5);
}
/* أشرطة الصوت */
.wave { display:flex; justify-content:center; align-items:flex-end; height:50px; margin-top:15px; }
.wave span {
display:inline-block;
width:5px; height:25px;
background: rgb(11, 186, 131);
margin:0 4px; border-radius:3px;
animation: wave 1.2s infinite ease-in-out;
}
.wave span:nth-child(2){animation-delay:0.1s;}
.wave span:nth-child(3){animation-delay:0.2s;}
.wave span:nth-child(4){animation-delay:0.3s;}
.wave span:nth-child(5){animation-delay:0.4s;}
@keyframes wave {
0%,40%,100% { transform: scaleY(0.6); }
20% { transform: scaleY(1.5); }
}
.night-mode .wave span {
background: rgb(11, 186, 131);
}
.hidden { display:none; }
.status {
text-align:center;
margin-top:20px;
font-size:18px;
color:#4a5568;
transition: all 0.3s ease;
min-height: 30px;
padding: 0 10px;
}
.night-mode .status {
color: #a0aec0;
}
/* زر تبديل الوضع */
.theme-toggle {
position: absolute;
top: 20px;
right: 20px;
background: rgba(11, 186, 131, 0.1);
border: none;
border-radius: 50%;
width: 50px;
height: 50px;
cursor: pointer;
font-size: 24px;
display: flex;
justify-content: center;
align-items: center;
transition: all 0.3s ease;
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
animation: buttonPulse 4s infinite ease-in-out;
z-index: 100;
}
@keyframes buttonPulse {
0% { transform: scale(1); box-shadow: 0 2px 10px rgba(0,0,0,0.1); }
50% { transform: scale(1.05); box-shadow: 0 4px 15px rgba(0,0,0,0.2); }
100% { transform: scale(1); box-shadow: 0 2px 10px rgba(0,0,0,0.1); }
}
.night-mode .theme-toggle {
background: rgba(11, 186, 131, 0.1);
box-shadow: 0 2px 10px rgba(0,0,0,0.3);
}
.theme-toggle:hover {
transform: scale(1.1);
animation: none;
}
/* رسالة حالة المتصفح */
.browser-warning {
position: fixed;
bottom: 20px;
left: 50%;
transform: translateX(-50%);
background: #ff4757;
color: white;
padding: 10px 20px;
border-radius: 5px;
font-size: 14px;
display: none;
box-shadow: 0 4px 12px rgba(0,0,0,0.1);
z-index: 1000;
text-align: center;
max-width: 90%;
}
/* تأثيرات النص المتحركة */
@keyframes textPulse {
0% { opacity: 0.8; }
50% { opacity: 1; }
100% { opacity: 0.8; }
}
.listening-text {
animation: textPulse 1.5s infinite;
font-weight: bold;
}
/* تخصيص شريط التمرير */
::-webkit-scrollbar {
width: 8px;
}
::-webkit-scrollbar-track {
background: rgba(11, 186, 131, 0.1);
border-radius: 10px;
}
::-webkit-scrollbar-thumb {
background: rgba(11, 186, 131, 0.3);
border-radius: 10px;
}
::-webkit-scrollbar-thumb:hover {
background: rgba(11, 186, 131, 0.5);
}
.night-mode ::-webkit-scrollbar-track {
background: rgba(11, 186, 131, 0.05);
}
.night-mode ::-webkit-scrollbar-thumb {
background: rgba(11, 186, 131, 0.2);
}
.night-mode ::-webkit-scrollbar-thumb:hover {
background: rgba(11, 186, 131, 0.4);
}
/* لوحة الإعدادات */
.settings-panel {
position: absolute;
top: 80px;
right: 20px;
background: white;
border-radius: 10px;
padding: 15px;
box-shadow: 0 5px 20px rgba(0,0,0,0.1);
display: none;
z-index: 100;
min-width: 200px;
}
.night-mode .settings-panel {
background: #2d3748;
box-shadow: 0 5px 20px rgba(0,0,0,0.3);
}
.settings-panel.show {
display: block;
}
.setting-item {
margin: 10px 0;
text-align: right;
}
.setting-item label {
display: block;
margin-bottom: 5px;
color: #4a5568;
font-size: 14px;
}
.night-mode .setting-item label {
color: #a0aec0;
}
.setting-item select, .setting-item input[type=range] {
width: 100%;
padding: 5px;
border-radius: 5px;
border: 1px solid #e2e8f0;
}
.night-mode .setting-item select,
.night-mode .setting-item input[type=range] {
background: #4a5568;
border-color: #718096;
color: white;
}
.speed-value {
display: inline-block;
margin-right: 10px;
font-size: 14px;
}
</style>
</head>
<body>
<!-- تأثير الخلفية الديناميكي -->
<div class="background-effect"></div>
<!-- زر تبديل الوضع -->
<button class="theme-toggle" id="themeToggle">🌙</button>
<!-- لوحة الإعدادات -->
<div class="settings-panel" id="settingsPanel">
<div class="setting-item">
<label for="voiceSelect">نوع الصوت:</label>
<select id="voiceSelect">
<option value="alloy">alloy</option>
</select>
</div>
<div class="setting-item">
<label for="speedRange">سرعة الكلام:</label>
<input type="range" id="speedRange" min="0.5" max="2.0" step="0.1" value="1">
<span class="speed-value" id="speedValue">1.0</span>
</div>
</div>
<div class="circle-outer" id="micCircle">
<div class="pulse-wave"></div>
<div class="pulse-wave"></div>
<div class="pulse-wave"></div>
<div class="circle-middle">
<div class="circle-inner">
<svg class="mic-svg" focusable="false" viewBox="0 0 24 24" aria-hidden="true" role="img">
<title>API</title>
<path d="M0 0h24v24H0z" fill="none"></path>
<path d="M6 13c-.55 0-1 .45-1 1s.45 1 1 1 1-.45 1-1-.45-1-1-1zm0 4c-.55 0-1 .45-1 1s.45 1 1 1 1-.45 1-1-.45-1-1-1zm0-8c-.55 0-1 .45-1 1s.45 1 1 1 1-.45 1-1-.45-1-1-1zm-3 .5c-.28 0-.5.22-.5.5s.22.5.5.5.5-.22.5-.5-.22-.5-.5-.5zM6 5c-.55 0-1 .45-1 1s.45 1 1 1 1-.45 1-1-.45-1-1-1zm15 5.5c.28 0 .5-.22.5-.5s-.22-.5-.5-.5-.5.22-.5.5.22.5.5.5zM14 7c.55 0 1-.45 1-1s-.45-1-1-1-1 .45-1 1 .45 1 1 1zm0-3.5c.28 0 .5-.22.5-.5s-.22-.5-.5-.5-.5.22-.5.5.22.5.5.5zm-11 10c-.28 0-.5.22-.5.5s.22.5.5.5.5-.22.5-.5-.22-.5-.5-.5zm7 7c-.28 0-.5.22-.5.5s.22.5.5.5.5-.22.5-.5-.22-.5-.5-.5zm0-17c.28 0 .5-.22.5-.5s-.22-.5-.5-.5-.5.22-.5.5.22.5.5.5zM10 7c.55 0 1-.45 1-1s-.45-1-1-1-1 .45-1 1 .45 1 1 1zm0 5.5c-.83 0-1.5.67-1.5 1.5s.67 1.5 1.5 1.5 1.5-.67 1.5-1.5-.67-1.5-1.5-1.5zm8 .5c-.55 0-1 .45-1 1s.45 1 1 1 1-.45 1-1-.45-1-1-1zm0 4c-.55 0-1 .45-1 1s.45 1 1 1 1-.45 1-1-.45-1-1-1zm0-8c-.55 0-1 .45-1 1s.45 1 1 1 1-.45 1-1-.45-1-1-1zm0-4c-.55 0-1 .45-1 1s.45 1 1 1 1-.45 1-1-.45-1-1-1zm3 8.5c-.28 0-.5.22-.5.5s.22.5.5.5.5-.22.5-.5-.22-.5-.5-.5zM14 17c-.55 0-1 .45-1 1s.45 1 1 1 1-.45 1-1-.45-1-1-1zm0 3.5c-.28 0-.5.22-.5.5s.22.5.5.5.5-.22.5-.5-.22-.5-.5-.5zm-4-12c-.83 0-1.5.67-1.5 1.5s.67 1.5 1.5 1.5 1.5-.67 1.5-1.5-.67-1.5-1.5-1.5zm0 8.5c-.55 0-1 .45-1 1s.45 1 1 1 1-.45 1-1-.45-1-1-1zm4-4.5c-.83 0-1.5.67-1.5 1.5s.67 1.5 1.5 1.5 1.5-.67 1.5-1.5-.67-1.5-1.5-1.5zm0-4c-.83 0-1.5.67-1.5 1.5s.67 1.5 1.5 1.5 1.5-.67 1.5-1.5-.67-1.5-1.5-1.5z"></path>
</svg>
</div>
</div>
</div>
<div class="status" id="status">انقر على الدائرة للتحدث</div>
<div class="wave hidden" id="wave">
<span></span><span></span><span></span><span></span><span></span>
</div>
<div class="browser-warning" id="browserWarning">
عذرًا، متصفحك الحالي لا يدعم خاصية التعرف على الصوت. يرجى استخدام Chrome أو Edge للحصول على أفضل تجربة.
</div>
<script>
const micCircle = document.getElementById('micCircle');
const status = document.getElementById('status');
const wave = document.getElementById('wave');
const themeToggle = document.getElementById('themeToggle');
const browserWarning = document.getElementById('browserWarning');
const settingsPanel = document.getElementById('settingsPanel');
const speedRange = document.getElementById('speedRange');
const speedValue = document.getElementById('speedValue');
// تحديث عرض سرعة الصوت
speedRange.addEventListener('input', () => {
speedValue.textContent = speedRange.value;
});
// التحقق من دعم المتصفح للوضع الليلي
let isNightMode = localStorage.getItem('nightMode') === 'true';
// تطبيق الوضع الليلي عند التحميل
if (isNightMode) {
document.body.classList.add('night-mode');
themeToggle.textContent = '☀️';
}
// تبديل الوضع الليلي/النهاري
themeToggle.addEventListener('click', function () {
isNightMode = !isNightMode;
document.body.classList.toggle('night-mode');
if (isNightMode) {
themeToggle.textContent = '☀️';
localStorage.setItem('nightMode', 'true');
} else {
themeToggle.textContent = '🌙';
localStorage.setItem('nightMode', 'false');
}
});
// إظهار/إخفاء الإعدادات
themeToggle.addEventListener('contextmenu', function (e) {
e.preventDefault();
settingsPanel.classList.toggle('show');
});
// التحقق من دعم التعرف على الصوت
if (!('webkitSpeechRecognition' in window || 'SpeechRecognition' in window)) {
status.textContent = "متصفحك لا يدعم التعرف على الصوت";
browserWarning.style.display = 'block';
} else {
const SpeechRecognition = window.SpeechRecognition || window.webkitSpeechRecognition;
const recognition = new SpeechRecognition();
recognition.lang = 'ar-SA';
recognition.continuous = true; // <--- خليناها true عشان يستمر بالاستماع
recognition.interimResults = false;
let isListening = false;
let isSpeaking = false; // حالة جديدة لتتبع إذا كان المساعد يرد
let recognitionTimeout; // لتخزين معرف الـ timeout
function startRecognition() {
if (!isListening && !isSpeaking) { // لا تبدأ إذا كان يستمع أو يرد بالفعل
try {
recognition.start();
status.textContent = "أستمع إليك...";
status.classList.add('listening-text');
micCircle.classList.add('listening');
wave.classList.remove('hidden');
isListening = true;
console.log("Recognition started.");
} catch (e) {
console.warn("Recognition start failed, might be already active or an error occurred:", e);
// ممكن يكون شغال بالفعل، ما نسوي شي
}
}
}
// أول ما تفتح الصفحة، يبدأ الاستماع
startRecognition();
micCircle.addEventListener('click', async function () {
// إذا كان يضغط وهو يتكلم، نوقف المساعد الصوتي
if (isSpeaking) {
// هنا ممكن نضيف لوجيك لإيقاف الصوت إذا كان يتكلم
console.log("User clicked while speaking, stopping current speech.");
// على سبيل المثال، إيقاف أي صوت شغال
if (window.currentAudioElement) {
window.currentAudioElement.pause();
window.currentAudioElement.currentTime = 0;
}
// ونرجع المايك لحالة الاستعداد
isSpeaking = false;
clearInterval(window.speakingAnim);
micCircle.classList.remove('speaking');
micCircle.style.transform = "scale(1)";
wave.classList.add('hidden');
status.textContent = "تم إيقاف الرد. انقر للتحدث مرة أخرى.";
// ثم نحاول نبدأ الاستماع مرة أخرى
startRecognition();
} else if (isListening) {
// إذا كان يستمع وضغط، يوقف الاستماع
recognition.stop();
console.log("Recognition stopped by user click.");
} else {
// إذا ما كان يستمع ولا يتكلم، نبدأ الاستماع
startRecognition();
}
});
recognition.onresult = async function (event) {
// هنا إذا كان الـ recognition continuous، ممكن يعطي نتائج فارغة أو نتائج جزئية
// نتأكد إن فيه كلام مفيد قبل ما نعالجه
if (event.results.length === 0 || event.results[0].isFinal === false) {
return; // تجاهل النتائج الجزئية أو الفارغة
}
const transcript = event.results[0][0].transcript;
console.log(`Transcript: "${transcript}"`);
// إذا كان الكلام قصير جداً أو مجرد ضوضاء، ممكن نتجاهله
if (transcript.trim().length < 2) { // على سبيل المثال، أقل من حرفين
status.textContent = "لم أسمع شيئاً واضحاً. حاول مرة أخرى.";
micCircle.classList.remove('listening');
status.classList.remove('listening-text');
wave.classList.add('hidden');
isListening = false; // نعتبره خلص عشان يعيد الاستماع
startRecognition(); // نعيد تشغيل الاستماع تلقائيًا
return;
}
status.textContent = `تم الاستماع إلى: "${transcript}"`;
status.classList.remove('listening-text');
// إيقاف الاستماع مؤقتاً أثناء الرد
recognition.stop();
isListening = false;
micCircle.classList.remove('listening');
wave.classList.add('hidden');
console.log("Recognition temporarily stopped for processing/speaking.");
// إضافة حركة الرد
micCircle.classList.add('speaking');
isSpeaking = true;
// تحريك الدائرة ديناميكيًا أثناء الكلام
let scale = 1;
let direction = 1;
window.speakingAnim = setInterval(() => { // استخدمنا window عشان نقدر نوقفها من أي مكان
scale += 0.01 * direction;
if (scale > 1.1) direction = -1;
if (scale < 0.9) direction = 1;
micCircle.style.transform = `scale(${scale})`;
}, 16);
try {
const reply = await getGPTResponse(transcript);
await speakResponse(reply);
} catch (error) {
console.error('Error in processing or speaking:', error);
status.textContent = "حدث خطأ في المعالجة";
clearInterval(window.speakingAnim);
micCircle.classList.remove('speaking');
micCircle.style.transform = "scale(1)";
isSpeaking = false;
startRecognition(); // إعادة تشغيل الاستماع
}
}
recognition.onend = function () {
console.log("Recognition onend triggered.");
if (!isSpeaking) { // إذا ما كان المساعد يرد حالياً
// إذا انتهى الاستماع، ممكن يكون عشان خلص الكلام أو صار timeout
// نعيد تشغيل الاستماع إذا ما كنا في حالة رد
micCircle.classList.remove('listening');
status.classList.remove('listening-text');
wave.classList.add('hidden');
isListening = false;
status.textContent = "انقر على الدائرة للتحدث"; // نص افتراضي
startRecognition(); // <--- إعادة تشغيل الاستماع بعد الانتهاء
}
}
recognition.onerror = function (event) {
console.error('خطأ في التعرف على الصوت:', event.error);
status.textContent = "حدث خطأ: " + event.error + ". حاول مرة أخرى.";
status.classList.remove('listening-text');
micCircle.classList.remove('listening');
wave.classList.add('hidden');
isListening = false;
isSpeaking = false;
clearInterval(window.speakingAnim); // تأكد من إيقاف أي أنيميشن
micCircle.classList.remove('speaking');
micCircle.style.transform = "scale(1)";
startRecognition(); // <--- إعادة تشغيل الاستماع حتى لو كان فيه خطأ
}
async function speakResponse(text) {
try {
const voice = document.getElementById("voiceSelect").value;
const speed = parseFloat(document.getElementById("speedRange").value);
const response = await fetch("https://lahja-dev-resource.cognitiveservices.azure.com/openai/deployments/LAHJA-V1/audio/speech?api-version=2025-03-01-preview", {
method: "POST",
headers: {
"Content-Type": "application/json",
"Authorization": "Bearer 4AwsIf87cyBIgaJVsy0phWUQdZFcbrJxpQBDQNzL4xjcP2MFzrrYJQQJ99BIACHYHv6XJ3w3AAAAACOGYrzM"
},
body: JSON.stringify({
model: "LAHJA-V1",
input: text,
voice: voice,
speed: 0.8
})
});
if (!response.ok) {
throw new Error(`TTS error! status: ${response.status}`);
}
const audioBlob = await response.blob();
const audioUrl = URL.createObjectURL(audioBlob);
const audioElem = new Audio(audioUrl);
window.currentAudioElement = audioElem; // لتتبع العنصر الصوتي الحالي
status.textContent = "جاري الرد...";
audioElem.play().then(() => {
audioElem.onended = () => {
console.log("Speech finished.");
clearInterval(window.speakingAnim);
micCircle.classList.remove('speaking');
micCircle.style.transform = "scale(1)";
wave.classList.add('hidden');
isSpeaking = false; // انتهى الرد
status.textContent = "انقر على الدائرة للتحدث مرة أخرى"; // نص مؤقت قبل إعادة الاستماع
startRecognition(); // <--- إعادة تشغيل الاستماع تلقائيًا بعد انتهاء الرد
};
}).catch(e => {
console.error('Play error:', e);
status.textContent = "خطأ في تشغيل الصوت. اضغط تشغيل لسماع الرد";
clearInterval(window.speakingAnim);
micCircle.classList.remove('speaking');
micCircle.style.transform = "scale(1)";
wave.classList.add('hidden');
isSpeaking = false;
startRecognition(); // إعادة تشغيل الاستماع
});
} catch (error) {
console.error('Error in speakResponse:', error);
status.textContent = "خطأ في تحويل النص إلى صوت";
clearInterval(window.speakingAnim);
micCircle.classList.remove('speaking');
micCircle.style.transform = "scale(1)";
wave.classList.add('hidden');
isSpeaking = false;
startRecognition(); // إعادة تشغيل الاستماع
}
}
async function getGPTResponse(text) {
try {
status.textContent = 'جاري التواصل مع المساعد...';
const response = await fetch("https://lahja-dev-resource.cognitiveservices.azure.com/openai/deployments/gpt-4o/chat/completions?api-version=2025-01-01-preview", {
method: "POST",
headers: {
"Content-Type": "application/json",
"Authorization": "Bearer 4AwsIf87cyBIgaJVsy0phWUQdZFcbrJxpQBDQNzL4xjcP2MFzrrYJQQJ99BIACHYHv6XJ3w3AAAAACOGYrzM"
},
body: JSON.stringify({
messages: [
{
"role": "system",
"content": "انت نموذج اسمه (لهجة) مطور من قبل شركة أسس الذكاء الرقمي. رد باللهجة النجدية بطريقة ودية وطبيعية في جميع المحادثات."
},
{
"role": "user",
"content":"دائما الاجابات بصوره مختصر لا تتجاوز سطر ودائما باللهجة النجدية "
}
,
{
"role": "user",
"content":+ text
}
],
max_tokens: 4096,
temperature: 0.8,
top_p: 1,
model: "gpt-4o"
})
});
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const data = await response.json();
return data.choices[0].message.content;
} catch (error) {
console.error('Error getting GPT response:', error);
// رد افتراضي في حالة الخطأ
return "الله يوفقك يا طيب! والله ما قدرت أفهم كلامك تمام، حاول مرة ثانية وخذ راحتك بالكلام.";
}
}
}
// إغلاق الإعدادات عند النقر خارجها
document.addEventListener('click', function (e) {
if (!settingsPanel.contains(e.target) && e.target !== themeToggle) {
settingsPanel.classList.remove('show');
}
});
</script>
</body>
</html>