Spaces:
Running
Running
<html><head><base href="https://voice-gemma-ai.example.com/"> | |
<meta charset="UTF-8"> | |
<meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
<title>AI Voice Assistant with Advanced Generation Capabilities</title> | |
<style> | |
body { | |
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; | |
margin: 0; | |
padding: 0; | |
display: flex; | |
flex-direction: column; | |
min-height: 100vh; | |
background-color: #1a1a1a; | |
color: #ffffff; | |
} | |
.assistant-container { | |
flex: 1; | |
width: 90%; | |
max-width: 800px; | |
margin: 20px auto; | |
background-color: #2a2a2a; | |
border-radius: 20px; | |
box-shadow: 0 0 20px rgba(0,0,0,0.3); | |
overflow: hidden; | |
display: flex; | |
flex-direction: column; | |
} | |
.assistant-header { | |
background-color: #3a3a3a; | |
color: #4a90e2; | |
padding: 20px; | |
text-align: center; | |
font-size: 28px; | |
font-weight: bold; | |
} | |
.visualization-area { | |
flex-grow: 1; | |
display: flex; | |
justify-content: center; | |
align-items: center; | |
background-color: #222222; | |
position: relative; | |
overflow: hidden; | |
height: 300px; | |
} | |
.animation-container { | |
width: 200px; | |
height: 200px; | |
position: relative; | |
} | |
.circle { | |
position: absolute; | |
border-radius: 50%; | |
transition: all 0.5s ease; | |
} | |
.idle .circle { | |
width: 20px; | |
height: 20px; | |
background-color: #4a90e2; | |
opacity: 0.7; | |
} | |
.idle .circle:nth-child(1) { top: 40%; left: 20%; animation: pulse 2s infinite; } | |
.idle .circle:nth-child(2) { top: 60%; left: 40%; animation: pulse 2s infinite 0.3s; } | |
.idle .circle:nth-child(3) { top: 40%; left: 60%; animation: pulse 2s infinite 0.6s; } | |
.idle .circle:nth-child(4) { top: 60%; left: 80%; animation: pulse 2s infinite 0.9s; } | |
@keyframes pulse { | |
0% { transform: scale(1); } | |
50% { transform: scale(1.2); } | |
100% { transform: scale(1); } | |
} | |
.listening .circle { | |
width: 30px; | |
height: 30px; | |
background-color: #4a90e2; | |
} | |
.listening .circle:nth-child(1) { top: 50%; left: 10%; animation: wave 1s infinite; } | |
.listening .circle:nth-child(2) { top: 50%; left: 30%; animation: wave 1s infinite 0.2s; } | |
.listening .circle:nth-child(3) { top: 50%; left: 50%; animation: wave 1s infinite 0.4s; } | |
.listening .circle:nth-child(4) { top: 50%; left: 70%; animation: wave 1s infinite 0.6s; } | |
@keyframes wave { | |
0%, 100% { transform: translateY(0); } | |
50% { transform: translateY(-20px); } | |
} | |
.generating .circle, .loading .circle { | |
width: 40px; | |
height: 40px; | |
background-color: #4a90e2; | |
} | |
.generating .circle:nth-child(1), .loading .circle:nth-child(1) { top: 50%; left: 20%; animation: rotate 2s infinite linear; } | |
.generating .circle:nth-child(2), .loading .circle:nth-child(2) { top: 30%; left: 50%; animation: rotate 2s infinite linear 0.5s; } | |
.generating .circle:nth-child(3), .loading .circle:nth-child(3) { top: 70%; left: 50%; animation: rotate 2s infinite linear 1s; } | |
.generating .circle:nth-child(4), .loading .circle:nth-child(4) { top: 50%; left: 80%; animation: rotate 2s infinite linear 1.5s; } | |
@keyframes rotate { | |
0% { transform: rotate(0deg); } | |
100% { transform: rotate(360deg); } | |
} | |
.speaking .circle { | |
width: 50px; | |
height: 50px; | |
background-color: #4a90e2; | |
} | |
.speaking .circle:nth-child(1) { top: 40%; left: 20%; animation: bounce 0.5s infinite alternate; } | |
.speaking .circle:nth-child(2) { top: 60%; left: 40%; animation: bounce 0.5s infinite alternate 0.1s; } | |
.speaking .circle:nth-child(3) { top: 40%; left: 60%; animation: bounce 0.5s infinite alternate 0.2s; } | |
.speaking .circle:nth-child(4) { top: 60%; left: 80%; animation: bounce 0.5s infinite alternate 0.3s; } | |
@keyframes bounce { | |
0% { transform: translateY(0); } | |
100% { transform: translateY(-20px); } | |
} | |
.controls { | |
display: flex; | |
justify-content: center; | |
padding: 20px; | |
background-color: #2a2a2a; | |
} | |
.control-button { | |
background-color: #4a90e2; | |
color: white; | |
border: none; | |
padding: 15px 30px; | |
margin: 0 10px; | |
border-radius: 50px; | |
cursor: pointer; | |
font-size: 18px; | |
transition: all 0.3s ease; | |
} | |
.control-button:hover { | |
background-color: #3a7bc8; | |
transform: scale(1.05); | |
} | |
.control-button:active { | |
transform: scale(0.95); | |
} | |
.control-button:disabled { | |
background-color: #999; | |
cursor: not-allowed; | |
} | |
.settings-button { | |
position: absolute; | |
top: 20px; | |
right: 20px; | |
background-color: transparent; | |
color: #4a90e2; | |
border: none; | |
font-size: 24px; | |
cursor: pointer; | |
} | |
.modal { | |
display: none; | |
position: fixed; | |
z-index: 1; | |
left: 0; | |
top: 0; | |
width: 100%; | |
height: 100%; | |
overflow: auto; | |
background-color: rgba(0,0,0,0.6); | |
} | |
.modal-content { | |
background-color: #2a2a2a; | |
margin: 15% auto; | |
padding: 20px; | |
border: 1px solid #3a3a3a; | |
width: 80%; | |
max-width: 500px; | |
border-radius: 10px; | |
color: #ffffff; | |
} | |
.close { | |
color: #aaa; | |
float: right; | |
font-size: 28px; | |
font-weight: bold; | |
} | |
.close:hover, | |
.close:focus { | |
color: #4a90e2; | |
text-decoration: none; | |
cursor: pointer; | |
} | |
#api-key, #language-select, #model-select, #tts-select, #system-prompt { | |
width: 100%; | |
padding: 10px; | |
margin: 10px 0; | |
border-radius: 5px; | |
border: 1px solid #3a3a3a; | |
background-color: #222222; | |
color: #ffffff; | |
} | |
#api-key { | |
-webkit-text-security: disc; | |
text-security: disc; | |
} | |
#save-settings, #reset-default { | |
background-color: #4a90e2; | |
color: white; | |
border: none; | |
padding: 10px 20px; | |
margin: 10px 5px 0 0; | |
border-radius: 5px; | |
cursor: pointer; | |
} | |
.chat-history { | |
background-color: #2a2a2a; | |
padding: 20px; | |
border-radius: 0 0 20px 20px; | |
max-height: 400px; | |
overflow-y: auto; | |
} | |
.chat-entry { | |
margin-bottom: 15px; | |
padding: 10px; | |
border-radius: 10px; | |
background-color: #3a3a3a; | |
} | |
.user-message { | |
color: #4a90e2; | |
font-weight: bold; | |
} | |
.ai-response { | |
color: #ffffff; | |
} | |
.status-message { | |
color: #ffa500; | |
font-style: italic; | |
} | |
#language-display, #model-display, #tts-display { | |
text-align: center; | |
padding: 5px; | |
font-size: 16px; | |
color: #4a90e2; | |
} | |
.generated-image { | |
max-width: 100%; | |
height: auto; | |
margin-top: 10px; | |
border-radius: 10px; | |
box-shadow: 0 0 10px rgba(0,0,0,0.3); | |
} | |
.music-player { | |
background-color: #3a3a3a; | |
border-radius: 10px; | |
padding: 15px; | |
margin-top: 15px; | |
} | |
.music-player audio { | |
width: 100%; | |
margin-top: 10px; | |
} | |
.music-controls { | |
display: flex; | |
justify-content: space-between; | |
margin-top: 10px; | |
} | |
.music-control-button { | |
background-color: #4a90e2; | |
color: white; | |
border: none; | |
padding: 5px 10px; | |
border-radius: 5px; | |
cursor: pointer; | |
} | |
.timer-container { | |
display: flex; | |
justify-content: center; | |
align-items: center; | |
margin-top: 10px; | |
} | |
.timer { | |
width: 60px; | |
height: 60px; | |
border-radius: 50%; | |
background: conic-gradient(#4a90e2 0deg, #2a2a2a 0deg); | |
display: flex; | |
justify-content: center; | |
align-items: center; | |
font-size: 20px; | |
font-weight: bold; | |
color: #ffffff; | |
} | |
#chat-log-checkbox, #auto-translate-checkbox { | |
margin-right: 5px; | |
} | |
#model-select option[disabled] { | |
font-weight: bold; | |
color: #4a90e2; | |
background-color: #3a3a3a; | |
} | |
</style> | |
</head> | |
<body> | |
<div class="assistant-container"> | |
<div class="assistant-header"> | |
AI Voice Assistant with Advanced Generation Capabilities | |
</div> | |
<div id="language-display">Current Language: Not set</div> | |
<div id="model-display">Current Model: Not set</div> | |
<div id="tts-display">Current TTS: Not set</div> | |
<div class="visualization-area"> | |
<div class="animation-container idle"> | |
<div class="circle"></div> | |
<div class="circle"></div> | |
<div class="circle"></div> | |
<div class="circle"></div> | |
</div> | |
</div> | |
<div class="timer-container"> | |
<div class="timer" id="cooldown-timer">20</div> | |
</div> | |
<div class="controls"> | |
<button id="start-button" class="control-button">Start Listening</button> | |
<button id="stop-button" class="control-button" style="display: none;">Stop Listening</button> | |
</div> | |
<div class="chat-history" id="chat-history"></div> | |
</div> | |
<button class="settings-button" id="settings-button">⚙️</button> | |
<div id="settings-modal" class="modal"> | |
<div class="modal-content"> | |
<span class="close">×</span> | |
<h2>Settings</h2> | |
<label for="api-key">API Key:</label> | |
<input type="password" id="api-key" placeholder="Enter your Hugging Face API key"> | |
<label for="language-select">Language:</label> | |
<select id="language-select"> | |
<option value="en-US">English 🇺🇸</option> | |
<option value="ru-RU">Russian 🇷🇺</option> | |
</select> | |
<label for="model-select">Model:</label> | |
<select id="model-select"> | |
<option disabled>Text Generation Models</option> | |
<option value="google/gemma-2b-it">Gemma 2B IT 🤖</option> | |
<option value="mistralai/Mixtral-8x7B-Instruct-v0.1">Mixtral 8x7B 🌪️</option> | |
<option value="HuggingFaceTB/SmolLM-1.7B-Instruct">SmolLM 1.7B 🐣</option> | |
<option value="mistralai/Mistral-Nemo-Instruct-2407">Mistral Nemo 🗣️</option> | |
<option value="01-ai/Yi-1.5-34B-Chat">Yi 1.5 34B 🧠</option> | |
<option value="NousResearch/Nous-Hermes-2-Mixtral-8x7B-DPO">Nous Hermes 2 Mixtral 🦉</option> | |
<option value="stabilityai/stablelm-2-zephyr-1_6b">StableLM Zephyr 1.6B 🌟</option> | |
<option value="microsoft/Phi-3-mini-4k-instruct">Phi-3 Mini 4K 🧠</option> | |
<option value="microsoft/DialoGPT-medium">DialoGPT Medium 💬</option> | |
<option value="google/gemma-1.1-7b-it">Gemma 1.1 7B IT 🧠</option> | |
<option value="google/gemma-2-27b-it">Gemma 2 27B IT 🧠</option> | |
<option value="meta-llama/Meta-Llama-3-8B-Instruct">Meta-Llama 3 8B Instruct 🦙</option> | |
<option value="codellama/CodeLlama-34b-Instruct-hf">CodeLlama 34B Instruct 💻</option> | |
<option disabled>Image Generation Models</option> | |
<option value="CompVis/stable-diffusion-v1-4">Stable Diffusion 🎨</option> | |
<option value="dreamlike-art/dreamlike-photoreal-2.0">Dreamlike Photoreal 📷</option> | |
<option value="openskyml/soviet-diffusion-xl">Soviet Diffusion XL 🎨</option> | |
<option value="black-forest-labs/FLUX.1-dev">FLUX.1 Dev 🖼️</option> | |
<option value="dataautogpt3/OpenDalleV1.1">OpenDalle V1.1 🎭</option> | |
<option value="black-forest-labs/FLUX.1-schnell">FLUX.1 Schnell 🌠</option> | |
<option disabled>Music Generation Models</option> | |
<option value="facebook/musicgen-small">MusicGen 🎵</option> | |
</select> | |
<label for="tts-select">Text-to-Speech:</label> | |
<select id="tts-select"> | |
<option value="standard">Standard 🔊</option> | |
<option value="espnet/kan-bayashi_ljspeech_vits">kan-bayashi 🎙️</option> | |
<option value="facebook/mms-tts-eng">mms-tts-eng 🗣️</option> | |
<option value="wasmdashai/vits-eng-us-ljs">VITS ENG US LJS 🎤</option> | |
</select> | |
<label for="system-prompt">System Prompt (optional):</label> | |
<textarea id="system-prompt" placeholder="Enter optional system prompt"></textarea> | |
<div> | |
<label for="auto-translate-checkbox"> | |
<input type="checkbox" id="auto-translate-checkbox"> Enable Auto-Translation (for image and music generation) | |
</label> | |
</div> | |
<div> | |
<label for="chat-log-checkbox"> | |
<input type="checkbox" id="chat-log-checkbox"> Enable Chat Logging | |
</label> | |
</div> | |
<button id="save-settings">Save</button> | |
<button id="reset-default">Reset to Default</button> | |
</div> | |
</div> | |
<script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script> | |
<script> | |
const startButton = document.getElementById('start-button'); | |
const stopButton = document.getElementById('stop-button'); | |
const settingsButton = document.getElementById('settings-button'); | |
const settingsModal = document.getElementById('settings-modal'); | |
const closeButton = document.getElementsByClassName('close')[0]; | |
const apiKeyInput = document.getElementById('api-key'); | |
const languageSelect = document.getElementById('language-select'); | |
const modelSelect = document.getElementById('model-select'); | |
const ttsSelect = document.getElementById('tts-select'); | |
const systemPromptInput = document.getElementById('system-prompt'); | |
const saveSettingsButton = document.getElementById('save-settings'); | |
const resetDefaultButton = document.getElementById('reset-default'); | |
const animationContainer = document.querySelector('.animation-container'); | |
const chatHistory = document.getElementById('chat-history'); | |
const languageDisplay = document.getElementById('language-display'); | |
const modelDisplay = document.getElementById('model-display'); | |
const ttsDisplay = document.getElementById('tts-display'); | |
const cooldownTimer = document.getElementById('cooldown-timer'); | |
const defaultApiKey = ''; | |
const defaultLanguage = 'en-US'; | |
const defaultModel = 'meta-llama/Meta-Llama-3-8B-Instruct'; | |
const defaultTts = 'standard'; | |
let apiKey = localStorage.getItem('huggingface_api_key') || defaultApiKey; | |
let selectedLanguage = localStorage.getItem('selected_language') || defaultLanguage; | |
let selectedModel = localStorage.getItem('selected_model') || defaultModel; | |
let selectedTts = localStorage.getItem('selected_tts') || defaultTts; | |
let systemPrompt = localStorage.getItem('system_prompt') || ''; | |
let chatLoggingEnabled = localStorage.getItem('chat_logging_enabled') === 'true'; | |
let autoTranslateEnabled = localStorage.getItem('auto_translate_enabled') === 'true'; | |
const translationModel = 'Helsinki-NLP/opus-mt-ru-en'; | |
async function translateText(text) { | |
try { | |
const response = await axios.post(`https://api-inference.huggingface.co/models/${translationModel}`, { | |
inputs: text | |
}, { | |
headers: { | |
'Authorization': `Bearer ${apiKey}`, | |
'Content-Type': 'application/json' | |
} | |
}); | |
return response.data[0].translation_text; | |
} catch (error) { | |
console.error('Translation error:', error); | |
return text; // Return original text if translation fails | |
} | |
} | |
let recognition; | |
let isListening = false; | |
let cooldownActive = false; | |
let cooldownSeconds = 20; | |
function setAnimationState(state) { | |
animationContainer.className = `animation-container ${state}`; | |
} | |
function addChatEntry(userMessage, aiResponse, isStatus = false, imageUrl = null, audioUrl = null) { | |
if (!chatLoggingEnabled) { | |
chatHistory.innerHTML = ''; // Clear all previous entries | |
} | |
const chatEntry = document.createElement('div'); | |
chatEntry.className = 'chat-entry'; | |
let content = ` | |
<div class="user-message">You: ${userMessage}</div> | |
<div class="${isStatus ? 'status-message' : 'ai-response'}">AI: ${aiResponse}</div> | |
`; | |
if (imageUrl) { | |
content += `<img src="${imageUrl}" alt="Generated Image" class="generated-image">`; | |
} | |
if (audioUrl) { | |
content += ` | |
<div class="music-player"> | |
<audio controls> | |
<source src="${audioUrl}" type="audio/wav"> | |
Your browser does not support the audio element. | |
</audio> | |
<div class="music-controls"> | |
<button class="music-control-button" onclick="document.querySelector('audio').play()">Play</button> | |
<button class="music-control-button" onclick="document.querySelector('audio').pause()">Pause</button> | |
<button class="music-control-button" onclick="document.querySelector('audio').currentTime = 0">Restart</button> | |
</div> | |
</div> | |
`; | |
} | |
chatEntry.innerHTML = content; | |
chatHistory.insertBefore(chatEntry, chatHistory.firstChild); | |
} | |
function initializeSpeechRecognition() { | |
if ('webkitSpeechRecognition' in window) { | |
recognition = new webkitSpeechRecognition(); | |
recognition.continuous = false; | |
recognition.interimResults = false; | |
recognition.lang = selectedLanguage; | |
recognition.onstart = function() { | |
isListening = true; | |
startButton.style.display = 'none'; | |
stopButton.style.display = 'inline-block'; | |
setAnimationState('listening'); | |
}; | |
recognition.onend = function() { | |
isListening = false; | |
startButton.style.display = 'inline-block'; | |
stopButton.style.display = 'none'; | |
setAnimationState('idle'); | |
startCooldown(); | |
}; | |
recognition.onresult = async function(event) { | |
const transcript = event.results[0][0].transcript; | |
console.log('You said: ' + transcript); | |
setAnimationState('generating'); | |
addChatEntry(transcript, "Processing your request...", true); | |
let processedText = transcript; | |
const isImageOrMusicModel = selectedModel.includes('stable-diffusion') || | |
selectedModel.includes('dreamlike-photoreal') || | |
selectedModel.includes('soviet-diffusion') || | |
selectedModel.includes('FLUX') || | |
selectedModel.includes('OpenDalle') || | |
selectedModel.includes('musicgen'); | |
if (autoTranslateEnabled && selectedLanguage === 'ru-RU' && isImageOrMusicModel) { | |
processedText = await translateText(transcript); | |
console.log('Translated text:', processedText); | |
} | |
try { | |
if (isImageOrMusicModel) { | |
if (selectedModel.includes('musicgen')) { | |
const audioUrl = await generateMusic(processedText); | |
addChatEntry(transcript, "Here's the generated music:", false, null, audioUrl); | |
} else { | |
const imageUrl = await generateImage(processedText); | |
addChatEntry(transcript, "Here's the generated image:", false, imageUrl); | |
} | |
} else { | |
const response = await makeApiRequest(processedText); | |
handleApiResponse(transcript, response); | |
} | |
} catch (error) { | |
console.error('Error:', error); | |
handleApiError(transcript, error); | |
} | |
}; | |
} else { | |
console.log('Web Speech API is not supported in this browser.'); | |
startButton.disabled = true; | |
startButton.textContent = 'Speech Recognition Not Supported'; | |
} | |
} | |
async function makeApiRequest(transcript) { | |
let userMessage = transcript; | |
if (systemPrompt) { | |
userMessage = `${systemPrompt}\n\n${transcript}`; | |
} | |
return axios.post(`https://api-inference.huggingface.co/models/${selectedModel}/v1/chat/completions`, { | |
model: selectedModel, | |
messages: [{"role": "user", "content": userMessage}], | |
max_tokens: 500, | |
stream: false | |
}, { | |
headers: { | |
'Authorization': `Bearer ${apiKey}`, | |
'Content-Type': 'application/json' | |
} | |
}); | |
} | |
async function generateImage(prompt) { | |
const response = await axios.post(`https://api-inference.huggingface.co/models/${selectedModel}`, { | |
inputs: prompt | |
}, { | |
headers: { | |
'Authorization': `Bearer ${apiKey}`, | |
'Content-Type': 'application/json' | |
}, | |
responseType: 'arraybuffer' | |
}); | |
const blob = new Blob([response.data], { type: 'image/jpeg' }); | |
return URL.createObjectURL(blob); | |
} | |
async function generateMusic(prompt) { | |
const response = await axios.post('https://api-inference.huggingface.co/models/facebook/musicgen-small', { | |
inputs: prompt | |
}, { | |
headers: { | |
'Authorization': `Bearer ${apiKey}`, | |
'Content-Type': 'application/json' | |
}, | |
responseType: 'arraybuffer' | |
}); | |
const blob = new Blob([response.data], { type: 'audio/wav' }); | |
return URL.createObjectURL(blob); | |
} | |
function handleApiResponse(transcript, response) { | |
if (response.status === 503 && response.data.error.includes("is currently loading")) { | |
setAnimationState('loading'); | |
addChatEntry(transcript, "Model is loading. Please wait...", true); | |
setTimeout(() => checkModelStatus(transcript), 5000); | |
} else { | |
let aiResponse; | |
if (response.data.choices && response.data.choices[0].message) { | |
aiResponse = response.data.choices[0].message.content; | |
} else if (response.data.generated_text) { | |
aiResponse = response.data.generated_text; | |
} else { | |
aiResponse = "Sorry, I couldn't generate a response."; | |
} | |
console.log('AI response:', aiResponse); | |
addChatEntry(transcript, aiResponse); | |
setAnimationState('speaking'); | |
speakResponse(aiResponse); | |
} | |
} | |
function handleApiError(transcript, error) { | |
let errorMessage = 'An error occurred while processing your request.'; | |
if (error.response) { | |
errorMessage = `Server Error: ${error.response.status} - ${error.response.data.error || error.response.statusText}`; | |
} else if (error.request) { | |
errorMessage = 'No response received from the server. Please check your internet connection.'; | |
} else { | |
errorMessage = `Error: ${error.message}`; | |
} | |
addChatEntry(transcript, errorMessage, true); | |
setAnimationState('idle'); | |
} | |
async function checkModelStatus(transcript) { | |
try { | |
const response = await makeApiRequest(transcript); | |
handleApiResponse(transcript, response); | |
} catch (error) { | |
if (error.response && error.response.status === 503) { | |
addChatEntry(transcript, "Model is still loading. Checking again...", true); | |
setTimeout(() => checkModelStatus(transcript), 5000); | |
} else { | |
handleApiError(transcript, error); | |
} | |
} | |
} | |
async function speakResponse(text) { | |
if (selectedTts === 'standard') { | |
const utterance = new SpeechSynthesisUtterance(text); | |
utterance.lang = selectedLanguage; | |
utterance.onend = function() { | |
setAnimationState('idle'); | |
}; | |
speechSynthesis.speak(utterance); | |
} else { | |
try { | |
const response = await axios.post(`https://api-inference.huggingface.co/models/${selectedTts}`, { | |
inputs: text | |
}, { | |
headers: { | |
'Authorization': `Bearer ${apiKey}`, | |
'Content-Type': 'application/json' | |
}, | |
responseType: 'arraybuffer' | |
}); | |
const audioContext = new (window.AudioContext || window.webkitAudioContext)(); | |
const audioBuffer = await audioContext.decodeAudioData(response.data); | |
const source = audioContext.createBufferSource(); | |
source.buffer = audioBuffer; | |
source.connect(audioContext.destination); | |
source.onended = function() { | |
setAnimationState('idle'); | |
}; | |
source.start(); | |
} catch (error) { | |
console.error('Error with AI TTS:', error); | |
const utterance = new SpeechSynthesisUtterance(text); | |
utterance.lang = selectedLanguage; | |
utterance.onend = function() { | |
setAnimationState('idle'); | |
}; | |
speechSynthesis.speak(utterance); | |
} | |
} | |
} | |
function startCooldown() { | |
cooldownActive = true; | |
startButton.disabled = true; | |
cooldownSeconds = 20; | |
updateCooldownTimer(); | |
const cooldownInterval = setInterval(() => { | |
cooldownSeconds--; | |
updateCooldownTimer(); | |
if (cooldownSeconds <= 0) { | |
clearInterval(cooldownInterval); | |
cooldownActive = false; | |
startButton.disabled = false; | |
cooldownTimer.style.background = 'conic-gradient(#4a90e2 360deg, #2a2a2a 360deg)'; | |
cooldownTimer.textContent = '20'; | |
} | |
}, 1000); | |
} | |
function updateCooldownTimer() { | |
const progress = (20 - cooldownSeconds) / 20 * 360; | |
cooldownTimer.style.background = `conic-gradient(#4a90e2 ${progress}deg, #2a2a2a ${progress}deg)`; | |
cooldownTimer.textContent = cooldownSeconds; | |
} | |
startButton.addEventListener('click', function() { | |
if (!cooldownActive && recognition) { | |
recognition.start(); | |
} | |
}); | |
stopButton.addEventListener('click', function() { | |
if (recognition) { | |
recognition.stop(); | |
} | |
}); | |
settingsButton.onclick = function() { | |
settingsModal.style.display = "block"; | |
apiKeyInput.value = apiKey; | |
languageSelect.value = selectedLanguage; | |
setInitialModelValue(); | |
ttsSelect.value = selectedTts; | |
systemPromptInput.value = systemPrompt; | |
document.getElementById('auto-translate-checkbox').checked = autoTranslateEnabled; | |
document.getElementById('chat-log-checkbox').checked = chatLoggingEnabled; | |
} | |
closeButton.onclick = function() { | |
settingsModal.style.display = "none"; | |
} | |
window.onclick = function(event) { | |
if (event.target == settingsModal) { | |
settingsModal.style.display = "none"; | |
} | |
} | |
saveSettingsButton.onclick = function() { | |
apiKey = apiKeyInput.value.trim(); | |
selectedLanguage = languageSelect.value; | |
selectedModel = modelSelect.value; | |
selectedTts = ttsSelect.value; | |
systemPrompt = systemPromptInput.value.trim(); | |
autoTranslateEnabled = document.getElementById('auto-translate-checkbox').checked; | |
chatLoggingEnabled = document.getElementById('chat-log-checkbox').checked; | |
localStorage.setItem('huggingface_api_key', apiKey); | |
localStorage.setItem('selected_language', selectedLanguage); | |
localStorage.setItem('selected_model', selectedModel); | |
localStorage.setItem('selected_tts', selectedTts); | |
localStorage.setItem('system_prompt', systemPrompt); | |
localStorage.setItem('auto_translate_enabled', autoTranslateEnabled); | |
localStorage.setItem('chat_logging_enabled', chatLoggingEnabled); | |
settingsModal.style.display = "none"; | |
updateLanguageDisplay(); | |
updateModelDisplay(); | |
updateTtsDisplay(); | |
initializeSpeechRecognition(); | |
addChatEntry("Update Settings", "Settings updated successfully!"); | |
if (!chatLoggingEnabled) { | |
clearChatHistoryExceptLast(); | |
} | |
} | |
function clearChatHistoryExceptLast() { | |
const chatEntries = chatHistory.getElementsByClassName('chat-entry'); | |
if (chatEntries.length > 1) { | |
for (let i = chatEntries.length - 1; i > 0; i--) { | |
chatHistory.removeChild(chatEntries[i]); | |
} | |
} | |
} | |
resetDefaultButton.onclick = function() { | |
apiKey = defaultApiKey; | |
selectedLanguage = defaultLanguage; | |
selectedModel = defaultModel; | |
selectedTts = defaultTts; | |
systemPrompt = ''; | |
autoTranslateEnabled = false; | |
chatLoggingEnabled = true; | |
apiKeyInput.value = apiKey; | |
languageSelect.value = selectedLanguage; | |
modelSelect.value = selectedModel; | |
ttsSelect.value = selectedTts; | |
systemPromptInput.value = systemPrompt; | |
document.getElementById('auto-translate-checkbox').checked = autoTranslateEnabled; | |
document.getElementById('chat-log-checkbox').checked = chatLoggingEnabled; | |
localStorage.setItem('huggingface_api_key', apiKey); | |
localStorage.setItem('selected_language', selectedLanguage); | |
localStorage.setItem('selected_model', selectedModel); | |
localStorage.setItem('selected_tts', selectedTts); | |
localStorage.setItem('system_prompt', systemPrompt); | |
localStorage.setItem('auto_translate_enabled', autoTranslateEnabled); | |
localStorage.setItem('chat_logging_enabled', chatLoggingEnabled); | |
updateLanguageDisplay(); | |
updateModelDisplay(); | |
updateTtsDisplay(); | |
initializeSpeechRecognition(); | |
addChatEntry("Reset Settings", "Settings reset to default."); | |
} | |
function updateLanguageDisplay() { | |
const languageName = languageSelect.options[languageSelect.selectedIndex].text; | |
languageDisplay.textContent = `Current Language: ${languageName}`; | |
} | |
function updateModelDisplay() { | |
const modelName = modelSelect.options[modelSelect.selectedIndex].text; | |
modelDisplay.textContent = `Current Model: ${modelName}`; | |
} | |
function updateTtsDisplay() { | |
const ttsName = ttsSelect.options[ttsSelect.selectedIndex].text; | |
ttsDisplay.textContent = `Current TTS: ${ttsName}`; | |
} | |
function setInitialModelValue() { | |
const storedModel = localStorage.getItem('selected_model'); | |
if (storedModel) { | |
const option = modelSelect.querySelector(`option[value="${storedModel}"]`); | |
if (option && !option.disabled) { | |
modelSelect.value = storedModel; | |
} else { | |
modelSelect.value = modelSelect.querySelector('option:not([disabled])').value; | |
} | |
} else { | |
modelSelect.value = modelSelect.querySelector('option:not([disabled])').value; | |
} | |
} | |
apiKeyInput.value = apiKey; | |
languageSelect.value = selectedLanguage; | |
setInitialModelValue(); | |
ttsSelect.value = selectedTts; | |
systemPromptInput.value = systemPrompt; | |
updateLanguageDisplay(); | |
updateModelDisplay(); | |
updateTtsDisplay(); | |
document.getElementById('auto-translate-checkbox').checked = autoTranslateEnabled; | |
document.getElementById('chat-log-checkbox').checked = chatLoggingEnabled; | |
setAnimationState('idle'); | |
initializeSpeechRecognition(); | |
</script> | |
</body></html> |