Spaces:
Running
Running
| // script.js - Voice queries now return text responses only | |
| // DOM Elements | |
| const chatMessages = document.getElementById('chatMessages'); | |
| const userInput = document.getElementById('userInput'); | |
| const sendButton = document.getElementById('sendButton'); | |
| const voiceButton = document.getElementById('voiceButton'); | |
| const typingIndicator = document.getElementById('typingIndicator'); | |
| const messageCount = document.getElementById('messageCount'); | |
| const activeLanguage = document.getElementById('activeLanguage'); | |
| const currentLanguageDisplay = document.getElementById('currentLanguageDisplay'); | |
| const welcomeTime = document.getElementById('welcomeTime'); | |
| const englishQuestions = document.getElementById('english-questions'); | |
| const urduQuestions = document.getElementById('urdu-questions'); | |
| // State Management | |
| let currentLanguage = 'english'; | |
| let messageCounter = 0; | |
| let mediaRecorder; | |
| let audioChunks = []; | |
| let isRecording = false; | |
| // Initialize | |
| document.addEventListener('DOMContentLoaded', function() { | |
| initializeApp(); | |
| setupEventListeners(); | |
| setWelcomeTime(); | |
| initializeWelcomeMessage(); | |
| }); | |
| async function initializeApp() { | |
| updateLanguageDisplay(); | |
| updateInputPlaceholder(); | |
| await loadPredefinedQuestions(); | |
| updateQuestionsDisplay(); | |
| } | |
| function setupEventListeners() { | |
| // Send message on button click | |
| sendButton.addEventListener('click', sendMessage); | |
| // Send message on Enter key | |
| userInput.addEventListener('keypress', function(e) { | |
| if (e.key === 'Enter') { | |
| sendMessage(); | |
| } | |
| }); | |
| // Tab switching | |
| document.querySelectorAll('.tab-btn').forEach(btn => { | |
| btn.addEventListener('click', function() { | |
| const tabId = this.getAttribute('data-tab'); | |
| switchTab(tabId); | |
| }); | |
| }); | |
| // Voice button listener | |
| if (voiceButton) { | |
| voiceButton.addEventListener('click', async () => { | |
| if (!isRecording) { | |
| startRecording(); | |
| } else { | |
| stopRecording(); | |
| } | |
| }); | |
| } | |
| userInput.focus(); | |
| } | |
| function initializeWelcomeMessage() { | |
| // Ensure the welcome message has proper styling and is visible | |
| const welcomeMessage = document.querySelector('.system-message'); | |
| if (welcomeMessage) { | |
| welcomeMessage.style.opacity = '1'; | |
| welcomeMessage.style.transform = 'translateY(0) scale(1)'; | |
| } | |
| } | |
| function setWelcomeTime() { | |
| const now = new Date(); | |
| welcomeTime.textContent = now.toLocaleTimeString([], { | |
| hour: '2-digit', | |
| minute: '2-digit' | |
| }); | |
| } | |
| function switchTab(tabId) { | |
| document.querySelectorAll('.tab-btn').forEach(btn => btn.classList.remove('active')); | |
| document.querySelector(`[data-tab="${tabId}"]`).classList.add('active'); | |
| document.querySelectorAll('.tab-content').forEach(content => content.classList.remove('active')); | |
| document.getElementById(`${tabId}-tab`).classList.add('active'); | |
| const newLanguage = tabId === 'urdu' ? 'urdu' : 'english'; | |
| if (currentLanguage !== newLanguage) { | |
| currentLanguage = newLanguage; | |
| updateLanguageDisplay(); | |
| updateInputPlaceholder(); | |
| } | |
| } | |
| function updateLanguageDisplay() { | |
| activeLanguage.textContent = currentLanguage === 'urdu' ? 'اردو' : 'English'; | |
| currentLanguageDisplay.textContent = currentLanguage === 'urdu' ? 'Urdu' : 'English'; | |
| } | |
| function updateInputPlaceholder() { | |
| if (currentLanguage === 'urdu') { | |
| userInput.placeholder = 'بریسٹ کینسر کے بارے میں پوچھیں...'; | |
| userInput.style.direction = 'rtl'; | |
| userInput.style.textAlign = 'right'; | |
| } else { | |
| userInput.placeholder = 'Ask about breast cancer support, treatment options, or recovery...'; | |
| userInput.style.direction = 'ltr'; | |
| userInput.style.textAlign = 'left'; | |
| } | |
| } | |
| async function loadPredefinedQuestions() { | |
| try { | |
| const englishResponse = await fetch('/predefined-questions?language=english'); | |
| const englishData = await englishResponse.json(); | |
| if (englishData.status === 'success') { | |
| window.predefinedEnglishQuestions = englishData.questions; | |
| } | |
| const urduResponse = await fetch('/predefined-questions?language=urdu'); | |
| const urduData = await urduResponse.json(); | |
| if (urduData.status === 'success') { | |
| window.predefinedUrduQuestions = urduData.questions; | |
| } | |
| } catch (error) { | |
| console.error('Error loading predefined questions:', error); | |
| } | |
| } | |
| function updateQuestionsDisplay() { | |
| updateQuestionList(englishQuestions, 'english'); | |
| updateQuestionList(urduQuestions, 'urdu'); | |
| } | |
| function updateQuestionList(container, language) { | |
| container.innerHTML = ''; | |
| const predefinedQuestions = language === 'urdu' ? | |
| window.predefinedUrduQuestions : | |
| window.predefinedEnglishQuestions; | |
| if (predefinedQuestions && predefinedQuestions.length > 0) { | |
| predefinedQuestions.forEach((questionData) => { | |
| const questionCard = createQuestionCard(questionData, language); | |
| container.appendChild(questionCard); | |
| }); | |
| } else { | |
| const emptyState = document.createElement('div'); | |
| emptyState.className = 'empty-state'; | |
| emptyState.innerHTML = ` | |
| <i class="fas fa-comments"></i> | |
| <p>${language === 'urdu' ? 'ابھی تک کوئی اردو سوالات نہیں ہیں۔' : 'No questions available yet.'}</p> | |
| `; | |
| container.appendChild(emptyState); | |
| } | |
| } | |
| function createQuestionCard(questionData, language) { | |
| const questionCard = document.createElement('button'); | |
| questionCard.className = `question-card ${language === 'urdu' ? 'urdu-text' : ''} predefined-card`; | |
| questionCard.setAttribute('data-question', questionData.question); | |
| const icon = questionData.icon || 'fas fa-question-circle'; | |
| questionCard.innerHTML = ` | |
| <div class="card-icon ${questionData.category || 'general'}"> | |
| <i class="${icon}"></i> | |
| </div> | |
| <div class="card-content"> | |
| <h3>${questionData.question}</h3> | |
| </div> | |
| <div class="card-arrow"> | |
| <i class="fas fa-chevron-right"></i> | |
| </div> | |
| `; | |
| questionCard.addEventListener('click', function() { | |
| userInput.value = questionData.question; | |
| sendMessage(); | |
| }); | |
| return questionCard; | |
| } | |
| async function sendMessage() { | |
| const message = userInput.value.trim(); | |
| if (!message) return; | |
| addMessageToChat(message, 'user', currentLanguage); | |
| userInput.value = ''; | |
| userInput.disabled = true; | |
| sendButton.disabled = true; | |
| showTypingIndicator(); | |
| try { | |
| const response = await fetch('/ask-query', { | |
| method: 'POST', | |
| headers: { 'Content-Type': 'application/json' }, | |
| body: JSON.stringify({ query: message, language: currentLanguage, response_type: 'text' }) | |
| }); | |
| const data = await response.json(); | |
| hideTypingIndicator(); | |
| if (data.status === 'success') { | |
| addMessageToChat(data.answer, 'system', data.language); | |
| updateMessageCount(); | |
| } else { | |
| addMessageToChat("I'm having trouble processing your request. Please try again.", 'system', 'english'); | |
| } | |
| } catch (error) { | |
| console.error('Error:', error); | |
| hideTypingIndicator(); | |
| addMessageToChat("Connection issue. Please try again.", 'system', 'english'); | |
| } finally { | |
| userInput.disabled = false; | |
| sendButton.disabled = false; | |
| userInput.focus(); | |
| } | |
| } | |
| function cleanUrduText(text) { | |
| const urduFixes = { | |
| // Character fixes | |
| 'ہےہ': 'ہے', | |
| 'مہےں': 'میں', | |
| 'ہےں': 'ہیں', | |
| 'ھے': 'ہے', | |
| 'ھوں': 'ہوں', | |
| 'ھیں': 'ہیں', | |
| 'ےے': 'ے', | |
| 'ںں': 'ں', | |
| 'ہہ': 'ہ', | |
| 'یی': 'ی', | |
| // Word fixes | |
| 'مجہے': 'مجھے', | |
| 'پروگرہوں': 'پروگرام', | |
| 'کہےنسر': 'کینسر', | |
| 'ڈڈاکٹر': 'ڈاکٹر', | |
| 'کا ے لہےے': 'کے لیے', | |
| 'جسے سے': 'جس سے', | |
| 'اکٹر': 'ڈاکٹر', | |
| 'اکیل': 'اکیلے', | |
| 'میش': 'میں', | |
| 'وتی': 'ہوتی', | |
| 'لکی': 'ہلکی', | |
| 'بتر': 'بہتر', | |
| // Grammar fixes | |
| 'ک دوران': 'کے دوران', | |
| 'ک بار': 'کے بارے', | |
| 'ک بعد': 'کے بعد', | |
| 'ک لی': 'کے لیے', | |
| 'ک ساتھ': 'کے ساتھ', | |
| 'ک طور': 'کے طور', | |
| 'ک ذریع': 'کے ذریعے', | |
| 'ک مطابق': 'کے مطابق' | |
| }; | |
| let cleanedText = text; | |
| // Apply all fixes | |
| Object.keys(urduFixes).forEach(wrong => { | |
| const regex = new RegExp(escapeRegExp(wrong), 'g'); | |
| cleanedText = cleanedText.replace(regex, urduFixes[wrong]); | |
| }); | |
| // Fix spacing issues | |
| cleanedText = cleanedText.replace(/\s+/g, ' '); | |
| cleanedText = cleanedText.replace(/ \./g, '.'); | |
| cleanedText = cleanedText.replace(/ ،/g, '،'); | |
| cleanedText = cleanedText.replace(/ /g, ' '); | |
| cleanedText = cleanedText.replace(/۔۔/g, '۔'); | |
| return cleanedText.trim(); | |
| } | |
| function escapeRegExp(string) { | |
| return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); | |
| } | |
| function addMessageToChat(message, sender, language = 'english') { | |
| // Clean Urdu text before displaying | |
| if (language === 'urdu') { | |
| message = cleanUrduText(message); | |
| } | |
| const messageDiv = document.createElement('div'); | |
| messageDiv.className = `message ${sender}-message`; | |
| if (language === 'urdu') { | |
| messageDiv.classList.add('urdu-text'); | |
| } | |
| const timestamp = new Date().toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' }); | |
| const avatarIcon = sender === 'user' ? 'fas fa-user' : 'fas fa-robot'; | |
| messageDiv.innerHTML = ` | |
| <div class="message-avatar"> | |
| <i class="${avatarIcon}"></i> | |
| </div> | |
| <div class="message-content ${language === 'urdu' ? 'urdu-text' : ''}"> | |
| <p>${formatMessage(message)}</p> | |
| <span class="message-time">${timestamp}</span> | |
| ${language === 'urdu' ? '<div class="language-badge">اردو</div>' : ''} | |
| </div> | |
| `; | |
| chatMessages.appendChild(messageDiv); | |
| scrollToBottom(); | |
| // Apply animation to new messages only | |
| setTimeout(() => { | |
| messageDiv.style.opacity = '1'; | |
| messageDiv.style.transform = 'translateY(0) scale(1)'; | |
| }, 10); | |
| } | |
| function formatMessage(message) { | |
| return message.replace(/\n/g, '<br>'); | |
| } | |
| function showTypingIndicator() { | |
| typingIndicator.style.display = 'flex'; | |
| scrollToBottom(); | |
| } | |
| function hideTypingIndicator() { | |
| typingIndicator.style.display = 'none'; | |
| } | |
| function updateMessageCount() { | |
| messageCounter++; | |
| messageCount.textContent = messageCounter; | |
| } | |
| function scrollToBottom() { | |
| setTimeout(() => { | |
| chatMessages.scrollTo({ top: chatMessages.scrollHeight, behavior: 'smooth' }); | |
| }, 100); | |
| } | |
| // Voice Recording Feature - Now returns text responses only | |
| async function startRecording() { | |
| try { | |
| const stream = await navigator.mediaDevices.getUserMedia({ audio: true }); | |
| mediaRecorder = new MediaRecorder(stream); | |
| audioChunks = []; | |
| mediaRecorder.ondataavailable = e => audioChunks.push(e.data); | |
| mediaRecorder.onstop = async () => { | |
| const audioBlob = new Blob(audioChunks, { type: 'audio/webm' }); | |
| const formData = new FormData(); | |
| formData.append('file', audioBlob, 'voiceNote.webm'); | |
| formData.append('language', currentLanguage); // Pass current tab language | |
| // Add user voice message to chat | |
| addUserVoiceMessageToChat(audioBlob); | |
| showTypingIndicator(); | |
| try { | |
| const response = await fetch('/voice-query', { | |
| method: 'POST', | |
| body: formData | |
| }); | |
| const data = await response.json(); | |
| hideTypingIndicator(); | |
| if (data.status === 'success') { | |
| // ✅ ALWAYS show text response for voice queries | |
| if (data.text && data.text.trim() !== '') { | |
| addMessageToChat(data.text, 'system', data.language); | |
| } else { | |
| // Fallback message | |
| const fallbackMessage = data.language === 'urdu' | |
| ? "میں آپ کی آواز کا پیغام سمجھ گئی ہوں۔ آپ کیسے مدد کر سکتی ہوں؟" | |
| : "I've processed your voice message. How can I help you further?"; | |
| addMessageToChat(fallbackMessage, 'system', data.language); | |
| } | |
| updateMessageCount(); | |
| } else { | |
| const errorMessage = currentLanguage === 'urdu' | |
| ? "معذرت، آپ کی آواز کا پیغام پروسیس نہیں کر سکی۔" | |
| : "Sorry, couldn't process your voice message."; | |
| addMessageToChat(errorMessage, 'system', currentLanguage); | |
| } | |
| } catch (err) { | |
| console.error('Voice query error:', err); | |
| hideTypingIndicator(); | |
| const errorMessage = currentLanguage === 'urdu' | |
| ? "آواز کے پروسیس میں خرابی۔" | |
| : "Error processing voice input."; | |
| addMessageToChat(errorMessage, 'system', currentLanguage); | |
| } | |
| }; | |
| mediaRecorder.start(); | |
| isRecording = true; | |
| voiceButton.classList.add('recording'); | |
| voiceButton.innerHTML = '<i class="fas fa-stop"></i>'; | |
| } catch (err) { | |
| console.error('Microphone access error:', err); | |
| const errorMessage = currentLanguage === 'urdu' | |
| ? 'براہ کرم آواز ریکارڈ کرنے کے لیے مائیکروفون کی رسائی کی اجازت دیں۔' | |
| : 'Please allow microphone access to record voice messages.'; | |
| alert(errorMessage); | |
| } | |
| } | |
| function addUserVoiceMessageToChat(audioBlob) { | |
| const messageDiv = document.createElement('div'); | |
| messageDiv.className = `message user-message audio-message`; | |
| const timestamp = new Date().toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' }); | |
| const audioUrl = URL.createObjectURL(audioBlob); | |
| messageDiv.innerHTML = ` | |
| <div class="message-avatar"> | |
| <i class="fas fa-user"></i> | |
| </div> | |
| <div class="message-content"> | |
| <div class="audio-message-container user-audio"> | |
| <div class="audio-player-wrapper"> | |
| <audio controls class="voice-note-player"> | |
| <source src="${audioUrl}" type="audio/webm"> | |
| Your browser does not support the audio element. | |
| </audio> | |
| </div> | |
| <div class="audio-duration">Your voice message</div> | |
| </div> | |
| <span class="message-time">${timestamp}</span> | |
| </div> | |
| `; | |
| chatMessages.appendChild(messageDiv); | |
| scrollToBottom(); | |
| setTimeout(() => { | |
| messageDiv.style.opacity = '1'; | |
| messageDiv.style.transform = 'translateY(0) scale(1)'; | |
| }, 10); | |
| } | |
| function stopRecording() { | |
| if (mediaRecorder && mediaRecorder.state !== 'inactive') { | |
| mediaRecorder.stop(); | |
| } | |
| isRecording = false; | |
| voiceButton.classList.remove('recording'); | |
| voiceButton.innerHTML = '<i class="fas fa-microphone"></i>'; | |
| } | |
| // Logo animation | |
| const logo = document.querySelector('.logo'); | |
| if (logo) { | |
| setInterval(() => { | |
| logo.style.transform = 'rotate(5deg)'; | |
| setTimeout(() => { logo.style.transform = 'rotate(-5deg)'; }, 1000); | |
| setTimeout(() => { logo.style.transform = 'rotate(0deg)'; }, 2000); | |
| }, 5000); | |
| } |