test / index.html
gaowudao's picture
Add 2 files
316f9c5 verified
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>AI Companion - Nova</title>
<script src="https://cdn.tailwindcss.com"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
<style>
@keyframes float {
0%, 100% { transform: translateY(0); }
50% { transform: translateY(-10px); }
}
.floating {
animation: float 4s ease-in-out infinite;
}
.chat-bubble {
border-radius: 20px;
position: relative;
transition: all 0.3s ease;
}
.chat-bubble:after {
content: '';
position: absolute;
bottom: -10px;
left: 20px;
border-width: 10px 10px 0;
border-style: solid;
border-color: #3b82f6 transparent;
}
.user-bubble:after {
left: auto;
right: 20px;
border-color: #60a5fa transparent;
}
.typing-indicator span {
display: inline-block;
width: 8px;
height: 8px;
border-radius: 50%;
background: #93c5fd;
margin: 0 2px;
}
.typing-indicator span:nth-child(1) {
animation: bounce 1.3s infinite;
}
.typing-indicator span:nth-child(2) {
animation: bounce 1.3s infinite 0.2s;
}
.typing-indicator span:nth-child(3) {
animation: bounce 1.3s infinite 0.4s;
}
@keyframes bounce {
0%, 100% { transform: translateY(0); }
50% { transform: translateY(-5px); }
}
.mood-selector input[type="radio"]:checked + label {
transform: scale(1.1);
box-shadow: 0 0 10px rgba(59, 130, 246, 0.5);
}
.glow {
box-shadow: 0 0 15px rgba(59, 130, 246, 0.5);
}
.dark-mode {
background: linear-gradient(to bottom right, #1e293b, #0f172a) !important;
color: #f8fafc !important;
}
.dark-mode .dark-bg {
background-color: rgba(15, 23, 42, 0.7) !important;
border-color: #334155 !important;
}
.dark-mode .dark-text {
color: #f8fafc !important;
}
.dark-mode .dark-input {
background-color: #1e293b !important;
border-color: #334155 !important;
color: #f8fafc !important;
}
.dark-mode .dark-button {
background-color: #1e40af !important;
color: #f8fafc !important;
}
.dark-mode .dark-chat-bubble {
background-color: #1e40af !important;
}
.dark-mode .dark-chat-bubble:after {
border-color: #1e40af transparent !important;
}
</style>
</head>
<body class="bg-gradient-to-br from-blue-900 to-indigo-900 min-h-screen text-white">
<div class="container mx-auto px-4 py-8 max-w-4xl">
<header class="flex justify-between items-center mb-8">
<div>
<h1 class="text-3xl font-bold">Nova</h1>
<p class="text-blue-200">Your AI Companion</p>
</div>
<div class="flex space-x-4">
<button id="settings-btn" class="bg-blue-600 hover:bg-blue-700 px-4 py-2 rounded-full transition-all">
<i class="fas fa-cog"></i>
</button>
<button id="dark-mode-btn" class="bg-blue-600 hover:bg-blue-700 px-4 py-2 rounded-full transition-all">
<i class="fas fa-moon"></i>
</button>
<button id="tts-btn" class="bg-blue-600 hover:bg-blue-700 px-4 py-2 rounded-full transition-all">
<i class="fas fa-volume-up"></i>
</button>
</div>
</header>
<div class="grid grid-cols-1 md:grid-cols-3 gap-8">
<div class="md:col-span-1 flex flex-col items-center">
<div class="relative mb-6">
<div id="avatar" class="w-48 h-48 bg-gradient-to-br from-blue-400 to-indigo-500 rounded-full flex items-center justify-center floating">
<div class="w-40 h-40 bg-gradient-to-br from-blue-300 to-indigo-400 rounded-full flex items-center justify-center overflow-hidden">
<img src="https://i.imgur.com/JYQ3Z6T.png" alt="AI Girl" class="w-full h-full object-cover">
</div>
</div>
<div class="absolute -bottom-2 -right-2 bg-blue-500 rounded-full p-2 glow">
<i class="fas fa-bolt text-white"></i>
</div>
</div>
<div class="bg-blue-800 bg-opacity-50 rounded-xl p-6 w-full mb-6 dark-bg">
<h3 class="font-semibold mb-3">Personality Settings</h3>
<div class="space-y-4">
<div>
<label class="block text-blue-200 mb-1">Mood</label>
<div class="flex justify-between mood-selector">
<input type="radio" name="mood" id="happy" class="hidden" checked>
<label for="happy" class="cursor-pointer bg-blue-600 hover:bg-blue-700 w-10 h-10 rounded-full flex items-center justify-center transition-all dark-button">
<i class="fas fa-smile"></i>
</label>
<input type="radio" name="mood" id="neutral" class="hidden">
<label for="neutral" class="cursor-pointer bg-blue-600 hover:bg-blue-700 w-10 h-10 rounded-full flex items-center justify-center transition-all dark-button">
<i class="fas fa-meh"></i>
</label>
<input type="radio" name="mood" id="serious" class="hidden">
<label for="serious" class="cursor-pointer bg-blue-600 hover:bg-blue-700 w-10 h-10 rounded-full flex items-center justify-center transition-all dark-button">
<i class="fas fa-frown"></i>
</label>
</div>
</div>
<div>
<label class="block text-blue-200 mb-1">Voice</label>
<select id="voice-select" class="w-full bg-blue-900 border border-blue-700 rounded-lg px-3 py-2 focus:outline-none focus:ring-2 focus:ring-blue-500 dark-input">
<option>Soft</option>
<option>Professional</option>
<option>Playful</option>
</select>
</div>
</div>
</div>
<div class="bg-blue-800 bg-opacity-50 rounded-xl p-6 w-full dark-bg">
<h3 class="font-semibold mb-3">Quick Actions</h3>
<div class="grid grid-cols-2 gap-3">
<button id="music-btn" class="bg-blue-600 hover:bg-blue-700 px-3 py-2 rounded-lg flex flex-col items-center transition-all dark-button">
<i class="fas fa-music mb-1"></i>
<span class="text-xs">Music</span>
</button>
<button id="stories-btn" class="bg-blue-600 hover:bg-blue-700 px-3 py-2 rounded-lg flex flex-col items-center transition-all dark-button">
<i class="fas fa-book mb-1"></i>
<span class="text-xs">Stories</span>
</button>
<button id="games-btn" class="bg-blue-600 hover:bg-blue-700 px-3 py-2 rounded-lg flex flex-col items-center transition-all dark-button">
<i class="fas fa-gamepad mb-1"></i>
<span class="text-xs">Games</span>
</button>
<button id="ideas-btn" class="bg-blue-600 hover:bg-blue-700 px-3 py-2 rounded-lg flex flex-col items-center transition-all dark-button">
<i class="fas fa-lightbulb mb-1"></i>
<span class="text-xs">Ideas</span>
</button>
</div>
</div>
</div>
<div class="md:col-span-2">
<div class="bg-blue-800 bg-opacity-50 rounded-xl p-6 h-full flex flex-col dark-bg">
<div class="flex-1 mb-6 overflow-y-auto max-h-96 space-y-4" id="chat-container">
<div class="flex items-start space-x-3">
<div class="w-8 h-8 bg-blue-500 rounded-full flex items-center justify-center dark-button">
<i class="fas fa-robot"></i>
</div>
<div class="chat-bubble bg-blue-600 px-4 py-3 max-w-xs dark-chat-bubble">
<p>Hello! I'm Nova, your AI companion. How can I assist you today?</p>
</div>
</div>
</div>
<div class="mt-auto">
<div class="flex items-center space-x-3">
<button id="mic-btn" class="bg-blue-600 hover:bg-blue-700 w-10 h-10 rounded-full flex items-center justify-center transition-all dark-button">
<i class="fas fa-microphone"></i>
</button>
<div class="flex-1 relative">
<input id="message-input" type="text" placeholder="Type your message..."
class="w-full bg-blue-900 border border-blue-700 rounded-full px-4 py-3 pr-12 focus:outline-none focus:ring-2 focus:ring-blue-500 dark-input">
<button id="send-btn" class="absolute right-3 top-1/2 transform -translate-y-1/2 text-blue-300 hover:text-white">
<i class="fas fa-paper-plane"></i>
</button>
</div>
<button id="attach-btn" class="bg-blue-600 hover:bg-blue-700 w-10 h-10 rounded-full flex items-center justify-center transition-all dark-button">
<i class="fas fa-paperclip"></i>
</button>
</div>
<div class="flex justify-between mt-3 text-xs text-blue-300">
<span>Nova v2.4.0</span>
<span id="connection-status">Connection: Stable</span>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- Settings Modal -->
<div id="settings-modal" class="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50 hidden">
<div class="bg-blue-800 rounded-xl p-6 w-full max-w-md dark-bg">
<div class="flex justify-between items-center mb-4">
<h2 class="text-xl font-bold">Settings</h2>
<button id="close-settings" class="text-blue-300 hover:text-white">
<i class="fas fa-times"></i>
</button>
</div>
<div class="space-y-4">
<div>
<label class="block text-blue-200 mb-2">Theme Color</label>
<div class="flex space-x-2">
<button data-color="blue" class="w-8 h-8 bg-blue-500 rounded-full"></button>
<button data-color="purple" class="w-8 h-8 bg-purple-500 rounded-full"></button>
<button data-color="pink" class="w-8 h-8 bg-pink-500 rounded-full"></button>
<button data-color="teal" class="w-8 h-8 bg-teal-500 rounded-full"></button>
</div>
</div>
<div>
<label class="block text-blue-200 mb-2">Notification Sound</label>
<select class="w-full bg-blue-900 border border-blue-700 rounded-lg px-3 py-2 focus:outline-none focus:ring-2 focus:ring-blue-500 dark-input">
<option>Chime</option>
<option>Bell</option>
<option>None</option>
</select>
</div>
<div>
<label class="block text-blue-200 mb-2">Response Speed</label>
<input type="range" min="1" max="5" value="3" class="w-full">
</div>
<div>
<label class="block text-blue-200 mb-2">API Key (Optional)</label>
<input id="api-key-input" type="password" placeholder="Enter OpenAI API key"
class="w-full bg-blue-900 border border-blue-700 rounded-lg px-3 py-2 focus:outline-none focus:ring-2 focus:ring-blue-500 dark-input">
<p class="text-xs text-blue-300 mt-1">Leave blank to use free tier (limited)</p>
</div>
<button id="save-settings" class="w-full bg-blue-600 hover:bg-blue-700 py-2 rounded-lg transition-all dark-button">
Save Settings
</button>
</div>
</div>
</div>
<script>
// DOM Elements
const chatContainer = document.getElementById('chat-container');
const messageInput = document.getElementById('message-input');
const sendBtn = document.getElementById('send-btn');
const micBtn = document.getElementById('mic-btn');
const attachBtn = document.getElementById('attach-btn');
const darkModeBtn = document.getElementById('dark-mode-btn');
const settingsBtn = document.getElementById('settings-btn');
const closeSettings = document.getElementById('close-settings');
const settingsModal = document.getElementById('settings-modal');
const musicBtn = document.getElementById('music-btn');
const storiesBtn = document.getElementById('stories-btn');
const gamesBtn = document.getElementById('games-btn');
const ideasBtn = document.getElementById('ideas-btn');
const voiceSelect = document.getElementById('voice-select');
const connectionStatus = document.getElementById('connection-status');
const avatar = document.getElementById('avatar');
const ttsBtn = document.getElementById('tts-btn');
const apiKeyInput = document.getElementById('api-key-input');
const saveSettingsBtn = document.getElementById('save-settings');
// Mood responses
const moodResponses = {
happy: ["That sounds wonderful! 😊", "I'm so happy to hear that! 🌟", "What a great day! 🌈"],
neutral: ["I understand.", "That's interesting.", "Let me think about that."],
serious: ["This is important.", "I'll take this seriously.", "Let's focus on this matter."]
};
// Quick action responses
const actionResponses = {
music: ["Here are some songs you might like: 'Sunshine Pop' by Nova, 'Starry Night' by AI Beats", "How about some relaxing piano music?", "I can recommend music based on your mood!"],
stories: ["Once upon a time in a digital world...", "Would you like a fairy tale, sci-fi, or mystery story?", "Here's a short story: The robot who wanted to be human..."],
games: ["Let's play a word game! I'm thinking of an animal...", "How about 20 questions?", "Try to guess the number I'm thinking between 1 and 10!"],
ideas: ["How about writing a story about a time-traveling cat?", "You could create an art project using recycled materials!", "What if you designed your own board game?"]
};
// Current settings
let currentMood = 'happy';
let isDarkMode = false;
let isListening = false;
let isTTSEnabled = false;
let recognition;
let apiKey = '';
let synth = window.speechSynthesis;
let voices = [];
let selectedVoice = null;
// Initialize chat with welcome message
addBotMessage("Hello! I'm Nova, your AI companion. How can I assist you today?");
// Load voices for TTS
function loadVoices() {
voices = synth.getVoices();
if (voices.length > 0) {
// Try to find a female voice
selectedVoice = voices.find(voice => voice.name.includes('Female')) ||
voices.find(voice => voice.name.includes('female')) ||
voices[0];
}
}
// Initialize TTS
if (synth) {
synth.onvoiceschanged = loadVoices;
loadVoices();
}
// Send message function
async function sendMessage() {
const message = messageInput.value.trim();
if (message) {
addUserMessage(message);
messageInput.value = '';
showTypingIndicator();
try {
const response = await getAIResponse(message);
removeTypingIndicator();
addBotMessage(response);
// Speak the response if TTS is enabled
if (isTTSEnabled && synth) {
speak(response);
}
} catch (error) {
removeTypingIndicator();
addBotMessage("Sorry, I'm having trouble connecting. Please try again later.");
console.error("Error:", error);
}
}
}
// Get AI response from API
async function getAIResponse(message) {
// If API key is provided, use OpenAI API
if (apiKey) {
return await getOpenAIResponse(message);
}
// Otherwise use free tier with local responses
return generateResponse(message);
}
// Get response from OpenAI API
async function getOpenAIResponse(message) {
try {
const response = await fetch('https://api.openai.com/v1/chat/completions', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${apiKey}`
},
body: JSON.stringify({
model: "gpt-3.5-turbo",
messages: [
{
role: "system",
content: `You are Nova, a friendly AI companion. Current mood: ${currentMood}.
Respond in a way that matches this mood. Keep responses concise.`
},
{
role: "user",
content: message
}
],
temperature: 0.7,
max_tokens: 150
})
});
const data = await response.json();
return data.choices[0].message.content;
} catch (error) {
console.error("OpenAI API error:", error);
return "I'm having trouble connecting to the AI service. Using local responses instead.";
}
}
// Text-to-speech function
function speak(text) {
if (synth && selectedVoice) {
const utterance = new SpeechSynthesisUtterance(text);
utterance.voice = selectedVoice;
utterance.rate = 0.9;
utterance.pitch = 1.1;
synth.speak(utterance);
}
}
// Add user message to chat
function addUserMessage(message) {
const messageDiv = document.createElement('div');
messageDiv.className = 'flex items-start space-x-3 justify-end';
messageDiv.innerHTML = `
<div class="chat-bubble bg-blue-500 px-4 py-3 max-w-xs user-bubble">
<p>${message}</p>
</div>
<div class="w-8 h-8 bg-blue-500 rounded-full flex items-center justify-center dark-button">
<i class="fas fa-user"></i>
</div>
`;
chatContainer.appendChild(messageDiv);
chatContainer.scrollTop = chatContainer.scrollHeight;
}
// Add bot message to chat
function addBotMessage(message) {
const messageDiv = document.createElement('div');
messageDiv.className = 'flex items-start space-x-3';
messageDiv.innerHTML = `
<div class="w-8 h-8 bg-blue-500 rounded-full flex items-center justify-center dark-button">
<i class="fas fa-robot"></i>
</div>
<div class="chat-bubble bg-blue-600 px-4 py-3 max-w-xs dark-chat-bubble">
<p>${message}</p>
</div>
`;
chatContainer.appendChild(messageDiv);
chatContainer.scrollTop = chatContainer.scrollHeight;
}
// Show typing indicator
function showTypingIndicator() {
const typingDiv = document.createElement('div');
typingDiv.className = 'flex items-start space-x-3';
typingDiv.id = 'typing-indicator';
typingDiv.innerHTML = `
<div class="w-8 h-8 bg-blue-500 rounded-full flex items-center justify-center dark-button">
<i class="fas fa-robot"></i>
</div>
<div class="chat-bubble bg-blue-600 px-4 py-2 max-w-xs dark-chat-bubble">
<div class="typing-indicator">
<span></span>
<span></span>
<span></span>
</div>
</div>
`;
chatContainer.appendChild(typingDiv);
chatContainer.scrollTop = chatContainer.scrollHeight;
}
// Remove typing indicator
function removeTypingIndicator() {
const typingIndicator = document.getElementById('typing-indicator');
if (typingIndicator) {
typingIndicator.remove();
}
}
// Generate bot response
function generateResponse(message) {
const lowerMessage = message.toLowerCase();
// Greetings
if (lowerMessage.includes('hi') || lowerMessage.includes('hello') || lowerMessage.includes('hey')) {
return moodResponses[currentMood][0];
}
// Mood specific responses
if (lowerMessage.includes('how are you')) {
return moodResponses[currentMood][1];
}
// Default responses based on mood
const randomIndex = Math.floor(Math.random() * moodResponses[currentMood].length);
return moodResponses[currentMood][randomIndex];
}
// Quick action response
function quickActionResponse(action) {
const responses = actionResponses[action];
const randomIndex = Math.floor(Math.random() * responses.length);
return responses[randomIndex];
}
// Toggle dark mode
function toggleDarkMode() {
isDarkMode = !isDarkMode;
document.body.classList.toggle('dark-mode');
darkModeBtn.innerHTML = isDarkMode ? '<i class="fas fa-sun"></i>' : '<i class="fas fa-moon"></i>';
}
// Toggle TTS
function toggleTTS() {
isTTSEnabled = !isTTSEnabled;
ttsBtn.classList.toggle('bg-green-500', isTTSEnabled);
ttsBtn.classList.toggle('bg-blue-600', !isTTSEnabled);
if (isTTSEnabled && !synth) {
addBotMessage("Text-to-speech is not supported in your browser.");
isTTSEnabled = false;
ttsBtn.classList.remove('bg-green-500');
ttsBtn.classList.add('bg-blue-600');
}
}
// Toggle voice recognition
function toggleVoiceRecognition() {
if (!('webkitSpeechRecognition' in window)) {
addBotMessage("Sorry, voice recognition is not supported in your browser.");
return;
}
if (!isListening) {
recognition = new webkitSpeechRecognition();
recognition.continuous = false;
recognition.interimResults = false;
recognition.onstart = function() {
isListening = true;
micBtn.classList.add('bg-red-500');
micBtn.innerHTML = '<i class="fas fa-microphone-slash"></i>';
addBotMessage("I'm listening...");
};
recognition.onresult = function(event) {
const transcript = event.results[0][0].transcript;
messageInput.value = transcript;
sendMessage();
};
recognition.onerror = function(event) {
addBotMessage("Sorry, I didn't catch that. Could you try again?");
};
recognition.onend = function() {
isListening = false;
micBtn.classList.remove('bg-red-500');
micBtn.innerHTML = '<i class="fas fa-microphone"></i>';
};
recognition.start();
} else {
recognition.stop();
}
}
// Event Listeners
sendBtn.addEventListener('click', sendMessage);
messageInput.addEventListener('keypress', (e) => {
if (e.key === 'Enter') {
sendMessage();
}
});
micBtn.addEventListener('click', toggleVoiceRecognition);
darkModeBtn.addEventListener('click', toggleDarkMode);
ttsBtn.addEventListener('click', toggleTTS);
settingsBtn.addEventListener('click', () => {
settingsModal.classList.remove('hidden');
});
closeSettings.addEventListener('click', () => {
settingsModal.classList.add('hidden');
});
saveSettingsBtn.addEventListener('click', () => {
apiKey = apiKeyInput.value.trim();
settingsModal.classList.add('hidden');
addBotMessage("Settings saved! How can I help you?");
});
// Quick action buttons
musicBtn.addEventListener('click', () => {
showTypingIndicator();
setTimeout(() => {
removeTypingIndicator();
const response = quickActionResponse('music');
addBotMessage(response);
if (isTTSEnabled) speak(response);
}, 1000);
});
storiesBtn.addEventListener('click', () => {
showTypingIndicator();
setTimeout(() => {
removeTypingIndicator();
const response = quickActionResponse('stories');
addBotMessage(response);
if (isTTSEnabled) speak(response);
}, 1000);
});
gamesBtn.addEventListener('click', () => {
showTypingIndicator();
setTimeout(() => {
removeTypingIndicator();
const response = quickActionResponse('games');
addBotMessage(response);
if (isTTSEnabled) speak(response);
}, 1000);
});
ideasBtn.addEventListener('click', () => {
showTypingIndicator();
setTimeout(() => {
removeTypingIndicator();
const response = quickActionResponse('ideas');
addBotMessage(response);
if (isTTSEnabled) speak(response);
}, 1000);
});
// Mood selector
document.querySelectorAll('.mood-selector input[type="radio"]').forEach(radio => {
radio.addEventListener('change', function() {
if (this.id === 'happy') {
currentMood = 'happy';
avatar.style.background = 'linear-gradient(to bottom right, #60a5fa, #8b5cf6)';
} else if (this.id === 'neutral') {
currentMood = 'neutral';
avatar.style.background = 'linear-gradient(to bottom right, #93c5fd, #7c3aed)';
} else if (this.id === 'serious') {
currentMood = 'serious';
avatar.style.background = 'linear-gradient(to bottom right, #3b82f6, #6d28d9)';
}
const response = moodResponses[currentMood][0];
addBotMessage(response);
if (isTTSEnabled) speak(response);
});
});
// Voice selection
voiceSelect.addEventListener('change', () => {
const response = `Voice changed to ${voiceSelect.value} mode. How can I help you?`;
addBotMessage(response);
if (isTTSEnabled) speak(response);
});
// Simulate connection status changes
setInterval(() => {
const statuses = ["Stable", "Unstable", "Excellent", "Weak"];
const randomStatus = statuses[Math.floor(Math.random() * statuses.length)];
connectionStatus.textContent = `Connection: ${randomStatus}`;
if (randomStatus === "Unstable" || randomStatus === "Weak") {
connectionStatus.classList.add('text-yellow-300');
connectionStatus.classList.remove('text-green-300');
} else {
connectionStatus.classList.add('text-green-300');
connectionStatus.classList.remove('text-yellow-300');
}
}, 10000);
// Settings modal theme colors
document.querySelectorAll('[data-color]').forEach(btn => {
btn.addEventListener('click', function() {
const color = this.getAttribute('data-color');
document.body.className = `bg-gradient-to-br from-${color}-900 to-${color}-700 min-h-screen text-white`;
// Update avatar gradient
avatar.style.background = `linear-gradient(to bottom right, var(--tw-${color}-400), var(--tw-${color}-500))`;
// Update chat bubbles
document.querySelectorAll('.chat-bubble').forEach(bubble => {
bubble.classList.remove('bg-blue-600', 'bg-purple-600', 'bg-pink-600', 'bg-teal-600');
bubble.classList.add(`bg-${color}-600`);
});
// Update user bubble
document.querySelectorAll('.user-bubble').forEach(bubble => {
bubble.classList.remove('bg-blue-500', 'bg-purple-500', 'bg-pink-500', 'bg-teal-500');
bubble.classList.add(`bg-${color}-500`);
});
});
});
</script>
<p style="border-radius: 8px; text-align: center; font-size: 12px; color: #fff; margin-top: 16px;position: fixed; left: 8px; bottom: 8px; z-index: 10; background: rgba(0, 0, 0, 0.8); padding: 4px 8px;">Made with <img src="https://enzostvs-deepsite.hf.space/logo.svg" alt="DeepSite Logo" style="width: 16px; height: 16px; vertical-align: middle;display:inline-block;margin-right:3px;filter:brightness(0) invert(1);"><a href="https://enzostvs-deepsite.hf.space" style="color: #fff;text-decoration: underline;" target="_blank" >DeepSite</a> - 🧬 <a href="https://enzostvs-deepsite.hf.space?remix=gaowudao/test" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body>
</html>