Spaces:
Runtime error
Runtime error
<script> | |
let isRecording = false; | |
let recognition; | |
let responsesHistory = {}; // Holds all responses per prompt | |
let currentIndex = {}; // Track current response index | |
function initSpeechRecognition() { | |
window.SpeechRecognition = window.SpeechRecognition || window.webkitSpeechRecognition; | |
recognition = new SpeechRecognition(); | |
recognition.lang = 'en-US'; | |
recognition.interimResults = false; | |
recognition.maxAlternatives = 1; | |
recognition.onresult = function(event) { | |
const transcript = event.results[0][0].transcript; | |
sendMessageFromVoice(transcript); | |
}; | |
recognition.onerror = function(event) { | |
console.log("Speech recognition error:", event.error); | |
}; | |
} | |
const micIcon = document.getElementById('mic-icon'); | |
micIcon.addEventListener('mousedown', function() { | |
isRecording = true; | |
micIcon.textContent = 'mic_off'; | |
recognition.start(); | |
}); | |
micIcon.addEventListener('mouseup', function() { | |
isRecording = false; | |
micIcon.textContent = 'mic'; | |
recognition.stop(); | |
}); | |
function appendMessage(text, className) { | |
let chatbox = document.getElementById('chatbox'); | |
let messageDiv = document.createElement('div'); | |
messageDiv.className = 'message ' + className; | |
messageDiv.innerHTML = text; | |
chatbox.appendChild(messageDiv); | |
chatbox.scrollTop = chatbox.scrollHeight; | |
} | |
function sendMessageFromVoice(message) { | |
appendMessage(message, 'user'); | |
fetchMessageFromAI(message); | |
} | |
function sendMessage() { | |
let userInput = document.getElementById('user-input'); | |
let message = userInput.value.trim(); | |
if (message === '') return; | |
appendMessage(message, 'user'); | |
userInput.value = ''; | |
fetchMessageFromAI(message); | |
} | |
function fetchMessageFromAI(message) { | |
document.getElementById('typing-indicator').style.display = 'flex'; | |
fetch('/message', { | |
method: 'POST', | |
headers: { | |
'Content-Type': 'application/json', | |
}, | |
body: JSON.stringify({ text: message }) | |
}) | |
.then(response => { | |
if (!response.ok) { | |
throw new Error(`Server error: ${response.statusText}`); | |
} | |
return response.json(); | |
}) | |
.then(data => { | |
document.getElementById('typing-indicator').style.display = 'none'; | |
if (data.response) { | |
addAIResponse(data.response, message); | |
} else { | |
console.error("No response from the server."); | |
} | |
}) | |
.catch(error => { | |
document.getElementById('typing-indicator').style.display = 'none'; | |
console.error('Error:', error); | |
appendMessage("An error occurred. Please try again later.", 'ai'); | |
}); | |
} | |
function addAIResponse(responseText, userPrompt) { | |
const responseId = Date.now(); | |
responsesHistory[responseId] = [responseText]; | |
currentIndex[responseId] = 0; | |
renderAIResponse(responseText, responseId, userPrompt); | |
} | |
function renderAIResponse(responseText, responseId, userPrompt) { | |
let chatbox = document.getElementById('chatbox'); | |
let messageDiv = document.createElement('div'); | |
messageDiv.className = 'message ai'; | |
messageDiv.dataset.responseId = responseId; | |
messageDiv.dataset.userPrompt = userPrompt; // Store the prompt | |
let responseTextDiv = document.createElement('div'); | |
responseTextDiv.className = 'response-text'; | |
responseTextDiv.innerHTML = responseText; | |
messageDiv.appendChild(responseTextDiv); | |
let iconsDiv = document.createElement('div'); | |
iconsDiv.className = 'icons'; | |
let speakerIcon = document.createElement('span'); | |
speakerIcon.className = 'material-icons'; | |
speakerIcon.innerText = 'volume_up'; | |
speakerIcon.onclick = () => speakText(responseId); | |
let copyIcon = document.createElement('span'); | |
copyIcon.className = 'material-icons'; | |
copyIcon.innerText = 'content_copy'; | |
copyIcon.onclick = () => copyResponse(responseId); | |
let regenerateIcon = document.createElement('span'); | |
regenerateIcon.className = 'material-icons'; | |
regenerateIcon.innerText = 'replay'; | |
regenerateIcon.onclick = () => regenerateResponse(responseId, responseTextDiv, iconsDiv); | |
iconsDiv.appendChild(speakerIcon); | |
iconsDiv.appendChild(copyIcon); | |
iconsDiv.appendChild(regenerateIcon); | |
messageDiv.appendChild(iconsDiv); | |
chatbox.appendChild(messageDiv); | |
chatbox.scrollTop = chatbox.scrollHeight; | |
} | |
function regenerateResponse(responseId, responseTextDiv, iconsDiv) { | |
responseTextDiv.innerHTML = 'Generating new response...'; | |
// Get the original prompt from the dataset | |
const messageDiv = document.querySelector(`[data-response-id="${responseId}"]`); | |
const originalPrompt = messageDiv.dataset.userPrompt; | |
fetch('/message', { | |
method: 'POST', | |
headers: { | |
'Content-Type': 'application/json', | |
}, | |
body: JSON.stringify({ text: originalPrompt }) // Use the original prompt | |
}) | |
.then(response => response.json()) | |
.then(data => { | |
if (data.response) { | |
responsesHistory[responseId].push(data.response); | |
currentIndex[responseId] = responsesHistory[responseId].length - 1; | |
displayUpdatedResponse(responseId, data.response, iconsDiv); | |
} else { | |
responseTextDiv.innerHTML = 'Error generating response'; | |
} | |
}) | |
.catch(error => { | |
console.error('Error:', error); | |
responseTextDiv.innerHTML = 'Error generating response'; | |
}); | |
} | |
function displayUpdatedResponse(responseId, newResponse, iconsDiv) { | |
let messageDiv = document.querySelector(`[data-response-id="${responseId}"]`); | |
let responseTextDiv = messageDiv.querySelector('.response-text'); | |
responseTextDiv.innerHTML = newResponse; | |
// Remove existing navigation if present | |
const existingNav = messageDiv.querySelector('.navigation'); | |
if (existingNav) { | |
existingNav.remove(); | |
} | |
let navigationDiv = document.createElement('div'); | |
navigationDiv.className = 'navigation'; | |
iconsDiv.appendChild(navigationDiv); | |
let backIcon = document.createElement('span'); | |
backIcon.className = 'material-icons'; | |
backIcon.innerText = 'arrow_back'; | |
backIcon.onclick = () => navigateResponse(responseId, -1, responseTextDiv, navigationDiv); | |
let nextIcon = document.createElement('span'); | |
nextIcon.className = 'material-icons'; | |
nextIcon.innerText = 'arrow_forward'; | |
nextIcon.onclick = () => navigateResponse(responseId, 1, responseTextDiv, navigationDiv); | |
let responseIndexText = document.createElement('span'); | |
responseIndexText.className = 'response-index'; | |
navigationDiv.appendChild(backIcon); | |
navigationDiv.appendChild(responseIndexText); | |
navigationDiv.appendChild(nextIcon); | |
updateResponseIndex(responseId, responseIndexText); | |
} | |
function navigateResponse(responseId, direction, responseTextDiv, navigationDiv) { | |
currentIndex[responseId] += direction; | |
currentIndex[responseId] = Math.max(0, Math.min(currentIndex[responseId], responsesHistory[responseId].length - 1)); | |
responseTextDiv.innerHTML = responsesHistory[responseId][currentIndex[responseId]]; | |
updateResponseIndex(responseId, navigationDiv.querySelector('.response-index')); | |
} | |
function updateResponseIndex(responseId, responseIndexText) { | |
responseIndexText.innerText = `${currentIndex[responseId] + 1}/${responsesHistory[responseId].length}`; | |
} | |
// Updated function to speak the current response | |
function speakText(responseId) { | |
const text = responsesHistory[responseId][currentIndex[responseId]]; | |
const utterance = new SpeechSynthesisUtterance(text); | |
speechSynthesis.speak(utterance); | |
} | |
// Updated function to copy the current response | |
function copyResponse(responseId) { | |
const text = responsesHistory[responseId][currentIndex[responseId]]; | |
navigator.clipboard.writeText(text); | |
alert("Copied: " + text); | |
} | |
document.addEventListener('DOMContentLoaded', initSpeechRecognition); |