Spaces:
Running
Running
<!DOCTYPE html><html lang="en"> <head> <meta charset="UTF-8"/> <meta name="viewport" content="width=device-width, initial-scale=1.0"/> <title>Thunder AI</title> <script src="https://cdn.tailwindcss.com"></script> <link href="https://fonts.googleapis.com/css2?family=Google+Sans:wght@400;500;700&display=swap" rel="stylesheet"> <link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet"> <script src="https://js.puter.com/v2/"></script> <style> body { font-family: 'Google Sans', Arial, sans-serif; background-color: #121212; color: #ffffff; height: 100vh; margin: 0; display: flex; flex-direction: column; } .chat-container { height: calc(100vh - 110px); overflow-y: auto; } .message-user { background-color: #1e1e1e; border-radius: 18px 18px 0 18px; } .message-ai { background-color: #1f1f1f; border-radius: 18px 18px 18px 0; position: relative; } .input-container { box-shadow: 0 -2px 5px rgba(255, 255, 255, 0.1); } .speak-btn { cursor: pointer; margin-left: 10px; font-size: 18px; background: none; border: none; color: #4285f4; } .voice-button { position: absolute; bottom: 8px; right: -40px; background: #4285f4; color: white; border-radius: 50%; width: 28px; height: 28px; font-size: 18px; display: flex; align-items: center; justify-content: center; } #menuModal { display: none; position: absolute; top: 60px; right: 20px; background: #1e1e1e; box-shadow: 0 2px 10px rgba(255, 255, 255, 0.2); border-radius: 10px; z-index: 100; } #menuModal p { padding: 10px 15px; cursor: pointer; font-size: 14px; color: #4285f4; } #menuModal p:hover { background: #333333; } </style> </head> <body> <header class="bg-gray-800 shadow-sm py-3 px-4 flex items-center justify-between relative"> <div class="flex items-center"> <div class="w-10 h-10 rounded-full bg-gradient-to-r from-blue-500 to-purple-500 flex items-center justify-center text-white font-bold mr-3">T</div> <h1 class="text-xl font-medium">Thunder AI</h1> </div> <div class="flex items-center space-x-2"> <button id="restart-btn" class="text-gray-400 hover:bg-gray-700 p-2 rounded-full"> <span class="material-icons">refresh</span> </button> <button id="menu-btn" class="text-gray-400 hover:bg-gray-700 p-2 rounded-full"> <span class="material-icons">menu</span> </button> </div> <div id="menuModal"> <p onclick="alert('Chat saved!')">Save Chat</p> <p onclick="alert('For any query: moodthunder00@gmail.com')">Help</p> </div> </header> <div id="chatBox" class="chat-container flex-1 overflow-y-auto p-4 space-y-4"> <div class="message-ai p-4 shadow-sm max-w-[80%]"> <p class="text-gray-200">Hello! How can I help you today?</p> </div> </div> <div class="input-container bg-gray-800 p-4"> <div class="max-w-3xl mx-auto flex items-end space-x-2"> <div class="flex-1 bg-gray-700 rounded-full px-4 py-3 flex items-center"> <textarea id="userInput" class="flex-1 bg-transparent outline-none resize-none max-h-32 overflow-y-auto" placeholder="Message Anything...." rows="1"></textarea> <button class="material-icons speak-btn" onclick="readLastMessage()">volume_up</button> <button class="material-icons speak-btn" onclick="startVoiceInput()">mic</button> </div> <button onclick="sendMessage()" class="bg-blue-500 hover:bg-blue-600 text-white rounded-full p-3"> <span class="material-icons">send</span> </button> </div> <div class="text-xs text-gray-400 text-center mt-2 flex justify-center space-x-4"> <a href="https://t.me/Thunderownerx" target="_blank" class="hover:text-blue-600"><span class="material-icons">telegram</span></a> <a href="https://instagram.com/Thunderownerx" target="_blank" class="hover:text-pink-600"><span class="material-icons">photo_camera</span></a> <span>Contact Us <strong>moodthunder00@gmail.com</strong></span> </div> </div> <script> const tools = [ { type: "function", function: { name: "get_weather", description: "Get current weather for a given location", parameters: { type: "object", properties: { location: { type: "string", description: "City name e.g. Paris, London" } }, required: ["location"] }, strict: true } } ]; function getWeather(location) { const data = { 'Paris': '22°C, Partly Cloudy', 'London': '18°C, Rainy', 'Tokyo': '28°C, Clear', 'New York': '25°C, Sunny' }; return data[location] || '20°C, Unknown'; } let lastAIMessage = ""; document.getElementById("restart-btn").addEventListener("click", () => { document.getElementById("chatBox").innerHTML = ""; }); document.getElementById("menu-btn").addEventListener("click", () => { const modal = document.getElementById("menuModal"); modal.style.display = modal.style.display === "block" ? "none" : "block"; }); async function sendMessage(text = null) { const input = document.getElementById("userInput"); const message = text || input.value.trim(); if (!message) return; appendMessage(message, 'user'); input.value = ''; try { const completion = await puter.ai.chat(message, { tools }); let finalResponse; if (completion.message.tool_calls && completion.message.tool_calls.length > 0) { const toolCall = completion.message.tool_calls[0]; if (toolCall.function.name === 'get_weather') { const args = JSON.parse(toolCall.function.arguments); const result = getWeather(args.location); finalResponse = await puter.ai.chat([ { role: "user", content: message }, completion.message, { role: "tool", tool_call_id: toolCall.id, content: result } ]); } } else { finalResponse = completion; } const reply = finalResponse.message?.content || "Sorry, no reply."; appendMessage(reply, 'ai'); lastAIMessage = reply; } catch (err) { appendMessage("API Error: " + err.message, 'ai'); } } function appendMessage(text, sender) { const chatBox = document.getElementById("chatBox"); const msgDiv = document.createElement("div"); msgDiv.className = `flex items-start space-x-3 max-w-3xl mx-auto ${sender === 'user' ? 'justify-end' : ''}`; if (sender === 'user') { msgDiv.innerHTML = ` <div class='message-user p-4 shadow-sm max-w-[80%]'><p class='text-gray-200'>${text}</p></div> <div class='w-10 h-10 rounded-full bg-blue-100 flex items-center justify-center text-blue-500'><span class='material-icons'>person</span></div>`; } else { const safeText = text.replace(/[`\\]/g, '\\$&'); // 🔥 Yeh line important hai! msgDiv.innerHTML = ` <div class='w-10 h-10 rounded-full bg-gray-200 flex items-center justify-center text-gray-600'><span class='material-icons'>smart_toy</span></div> <div class='message-ai p-4 shadow-sm max-w-[80%] relative'> <p class='text-gray-200'>${text}</p> <div class="voice-button" onclick="playVoice(\`${safeText}\`)">🔊</div> </div>`; } chatBox.appendChild(msgDiv); chatBox.scrollTop = chatBox.scrollHeight; } function readLastMessage() { if (lastAIMessage) playVoice(lastAIMessage); else alert("No AI message to read."); } function playVoice(text) { const utter = new SpeechSynthesisUtterance(text); utter.lang = "en-US"; utter.rate = 1; speechSynthesis.speak(utter); } function startVoiceInput() { try { const recognition = new (window.SpeechRecognition || window.webkitSpeechRecognition)(); recognition.lang = "en-US"; recognition.start(); recognition.onresult = (event) => { const transcript = event.results[0][0].transcript; sendMessage(transcript); }; recognition.onerror = (event) => { alert("Voice input error: " + event.error); }; } catch (err) { alert("Your browser doesn't support voice input."); } } </script></body> </html> isme gmail se our phone number se id login hone ka system dalo signing our login ka our chat history save ho same like chat gpt - Initial Deployment
35e7740 verified | <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8"/> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"/> | |
| <title>Thunder AI</title> | |
| <script src="https://cdn.tailwindcss.com"></script> | |
| <link href="https://fonts.googleapis.com/css2?family=Google+Sans:wght@400;500;700&display=swap" rel="stylesheet"> | |
| <link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet"> | |
| <script src="https://js.puter.com/v2/"></script> | |
| <style> | |
| body { | |
| font-family: 'Google Sans', Arial, sans-serif; | |
| background-color: #121212; | |
| color: #ffffff; | |
| height: 100vh; | |
| margin: 0; | |
| display: flex; | |
| flex-direction: column; | |
| } | |
| .chat-container { height: calc(100vh - 110px); overflow-y: auto; } | |
| .message-user { background-color: #1e1e1e; border-radius: 18px 18px 0 18px; } | |
| .message-ai { background-color: #1f1f1f; border-radius: 18px 18px 18px 0; position: relative; } | |
| .input-container { box-shadow: 0 -2px 5px rgba(255, 255, 255, 0.1); } | |
| .speak-btn { | |
| cursor: pointer; | |
| margin-left: 10px; | |
| font-size: 18px; | |
| background: none; | |
| border: none; | |
| color: #4285f4; | |
| } | |
| .voice-button { | |
| position: absolute; | |
| bottom: 8px; | |
| right: -40px; | |
| background: #4285f4; | |
| color: white; | |
| border-radius: 50%; | |
| width: 28px; | |
| height: 28px; | |
| font-size: 18px; | |
| display: flex; | |
| align-items: center; | |
| justify-content: center; | |
| } | |
| #menuModal { | |
| display: none; | |
| position: absolute; | |
| top: 60px; | |
| right: 20px; | |
| background: #1e1e1e; | |
| box-shadow: 0 2px 10px rgba(255, 255, 255, 0.2); | |
| border-radius: 10px; | |
| z-index: 100; | |
| } | |
| #menuModal p { | |
| padding: 10px 15px; | |
| cursor: pointer; | |
| font-size: 14px; | |
| color: #4285f4; | |
| } | |
| #menuModal p:hover { | |
| background: #333333; | |
| } | |
| #authModal { | |
| display: none; | |
| position: fixed; | |
| top: 0; | |
| left: 0; | |
| width: 100%; | |
| height: 100%; | |
| background: rgba(0, 0, 0, 0.8); | |
| z-index: 1000; | |
| justify-content: center; | |
| align-items: center; | |
| } | |
| .auth-container { | |
| background: #1e1e1e; | |
| border-radius: 12px; | |
| width: 90%; | |
| max-width: 400px; | |
| padding: 30px; | |
| box-shadow: 0 5px 15px rgba(0, 0, 0, 0.3); | |
| } | |
| .auth-tabs { | |
| display: flex; | |
| margin-bottom: 20px; | |
| border-bottom: 1px solid #333; | |
| } | |
| .auth-tab { | |
| padding: 10px 20px; | |
| cursor: pointer; | |
| color: #777; | |
| font-weight: 500; | |
| } | |
| .auth-tab.active { | |
| color: #4285f4; | |
| border-bottom: 2px solid #4285f4; | |
| } | |
| .auth-content { | |
| display: none; | |
| } | |
| .auth-content.active { | |
| display: block; | |
| } | |
| .otp-input { | |
| display: flex; | |
| justify-content: space-between; | |
| margin: 20px 0; | |
| } | |
| .otp-input input { | |
| width: 40px; | |
| height: 50px; | |
| text-align: center; | |
| font-size: 18px; | |
| background: #333; | |
| border: none; | |
| border-radius: 8px; | |
| color: white; | |
| } | |
| #chatHistoryModal { | |
| display: none; | |
| position: fixed; | |
| top: 0; | |
| left: 0; | |
| width: 100%; | |
| height: 100%; | |
| background: rgba(0, 0, 0, 0.8); | |
| z-index: 1000; | |
| justify-content: center; | |
| align-items: center; | |
| } | |
| .chat-history-container { | |
| background: #1e1e1e; | |
| border-radius: 12px; | |
| width: 90%; | |
| max-width: 600px; | |
| height: 70%; | |
| padding: 20px; | |
| box-shadow: 0 5px 15px rgba(0, 0, 0, 0.3); | |
| overflow-y: auto; | |
| } | |
| .chat-history-item { | |
| padding: 15px; | |
| margin-bottom: 10px; | |
| background: #252525; | |
| border-radius: 8px; | |
| cursor: pointer; | |
| transition: all 0.2s; | |
| } | |
| .chat-history-item:hover { | |
| background: #333; | |
| } | |
| .chat-history-item h3 { | |
| margin: 0; | |
| font-size: 16px; | |
| color: #fff; | |
| } | |
| .chat-history-item p { | |
| margin: 5px 0 0; | |
| font-size: 14px; | |
| color: #aaa; | |
| white-space: nowrap; | |
| overflow: hidden; | |
| text-overflow: ellipsis; | |
| } | |
| </style> | |
| </head> | |
| <body> | |
| <div id="authModal"> | |
| <div class="auth-container"> | |
| <div class="auth-tabs"> | |
| <div class="auth-tab active" onclick="switchAuthTab('login')">Login</div> | |
| <div class="auth-tab" onclick="switchAuthTab('signup')">Sign Up</div> | |
| </div> | |
| <div id="loginContent" class="auth-content active"> | |
| <h2 class="text-xl font-medium mb-6 text-center">Login to Thunder AI</h2> | |
| <div class="mb-4"> | |
| <label class="block text-gray-400 mb-2">Email or Phone</label> | |
| <input type="text" id="loginEmail" class="w-full bg-gray-700 rounded-lg px-4 py-3 text-white outline-none"> | |
| </div> | |
| <div class="mb-6"> | |
| <label class="block text-gray-400 mb-2">Password</label> | |
| <input type="password" id="loginPassword" class="w-full bg-gray-700 rounded-lg px-4 py-3 text-white outline-none"> | |
| </div> | |
| <button onclick="login()" class="w-full bg-blue-500 hover:bg-blue-600 text-white rounded-lg py-3 font-medium">Login</button> | |
| <p class="text-center mt-4 text-gray-400">Forgot password? <span class="text-blue-400 cursor-pointer" onclick="switchAuthTab('forgot')">Reset</span></p> | |
| </div> | |
| <div id="signupContent" class="auth-content"> | |
| <h2 class="text-xl font-medium mb-6 text-center">Create Account</h2> | |
| <div class="mb-4"> | |
| <label class="block text-gray-400 mb-2">Email or Phone</label> | |
| <input type="text" id="signupEmail" class="w-full bg-gray-700 rounded-lg px-4 py-3 text-white outline-none"> | |
| </div> | |
| <div class="mb-4"> | |
| <label class="block text-gray-400 mb-2">Password</label> | |
| <input type="password" id="signupPassword" class="w-full bg-gray-700 rounded-lg px-4 py-3 text-white outline-none"> | |
| </div> | |
| <div class="mb-6"> | |
| <label class="block text-gray-400 mb-2">Confirm Password</label> | |
| <input type="password" id="signupConfirmPassword" class="w-full bg-gray-700 rounded-lg px-4 py-3 text-white outline-none"> | |
| </div> | |
| <button onclick="signup()" class="w-full bg-blue-500 hover:bg-blue-600 text-white rounded-lg py-3 font-medium">Sign Up</button> | |
| </div> | |
| <div id="forgotContent" class="auth-content"> | |
| <h2 class="text-xl font-medium mb-6 text-center">Reset Password</h2> | |
| <div class="mb-4"> | |
| <label class="block text-gray-400 mb-2">Email or Phone</label> | |
| <input type="text" id="forgotEmail" class="w-full bg-gray-700 rounded-lg px-4 py-3 text-white outline-none"> | |
| </div> | |
| <button onclick="sendResetCode()" class="w-full bg-blue-500 hover:bg-blue-600 text-white rounded-lg py-3 font-medium">Send Reset Code</button> | |
| <p class="text-center mt-4 text-gray-400">Remember password? <span class="text-blue-400 cursor-pointer" onclick="switchAuthTab('login')">Login</span></p> | |
| </div> | |
| <div id="otpContent" class="auth-content"> | |
| <h2 class="text-xl font-medium mb-6 text-center">Verify OTP</h2> | |
| <p class="text-gray-400 mb-4 text-center">We've sent a 6-digit code to your email/phone</p> | |
| <div class="otp-input"> | |
| <input type="text" maxlength="1" oninput="moveToNext(this, 1)"> | |
| <input type="text" maxlength="1" oninput="moveToNext(this, 2)"> | |
| <input type="text" maxlength="1" oninput="moveToNext(this, 3)"> | |
| <input type="text" maxlength="1" oninput="moveToNext(this, 4)"> | |
| <input type="text" maxlength="1" oninput="moveToNext(this, 5)"> | |
| <input type="text" maxlength="1" oninput="moveToNext(this, 6)"> | |
| </div> | |
| <button onclick="verifyOTP()" class="w-full bg-blue-500 hover:bg-blue-600 text-white rounded-lg py-3 font-medium">Verify</button> | |
| <p class="text-center mt-4 text-gray-400">Didn't receive code? <span class="text-blue-400 cursor-pointer" onclick="resendOTP()">Resend</span></p> | |
| </div> | |
| </div> | |
| </div> | |
| <div id="chatHistoryModal"> | |
| <div class="chat-history-container"> | |
| <div class="flex justify-between items-center mb-6"> | |
| <h2 class="text-xl font-medium">Chat History</h2> | |
| <span class="material-icons cursor-pointer" onclick="closeChatHistory()">close</span> | |
| </div> | |
| <div id="chatHistoryList"> | |
| <!-- Chat history items will be added here --> | |
| </div> | |
| </div> | |
| </div> | |
| <header class="bg-gray-800 shadow-sm py-3 px-4 flex items-center justify-between relative"> | |
| <div class="flex items-center"> | |
| <div class="w-10 h-10 rounded-full bg-gradient-to-r from-blue-500 to-purple-500 flex items-center justify-center text-white font-bold mr-3">T</div> | |
| <h1 class="text-xl font-medium">Thunder AI</h1> | |
| </div> | |
| <div class="flex items-center space-x-2"> | |
| <button id="history-btn" class="text-gray-400 hover:bg-gray-700 p-2 rounded-full"> | |
| <span class="material-icons">history</span> | |
| </button> | |
| <button id="restart-btn" class="text-gray-400 hover:bg-gray-700 p-2 rounded-full"> | |
| <span class="material-icons">refresh</span> | |
| </button> | |
| <button id="menu-btn" class="text-gray-400 hover:bg-gray-700 p-2 rounded-full"> | |
| <span class="material-icons">menu</span> | |
| </button> | |
| <button id="user-btn" class="text-gray-400 hover:bg-gray-700 p-2 rounded-full"> | |
| <span class="material-icons">account_circle</span> | |
| </button> | |
| </div> | |
| <div id="menuModal"> | |
| <p onclick="saveCurrentChat()">Save Chat</p> | |
| <p onclick="showChatHistory()">Chat History</p> | |
| <p onclick="alert('For any query: moodthunder00@gmail.com')">Help</p> | |
| <p onclick="logout()">Logout</p> | |
| </div> | |
| </header> | |
| <div id="chatBox" class="chat-container flex-1 overflow-y-auto p-4 space-y-4"> | |
| <div class="message-ai p-4 shadow-sm max-w-[80%]"> | |
| <p class="text-gray-200">Hello! Please login to start chatting.</p> | |
| </div> | |
| </div> | |
| <div class="input-container bg-gray-800 p-4"> | |
| <div class="max-w-3xl mx-auto flex items-end space-x-2"> | |
| <div class="flex-1 bg-gray-700 rounded-full px-4 py-3 flex items-center"> | |
| <textarea id="userInput" class="flex-1 bg-transparent outline-none resize-none max-h-32 overflow-y-auto" placeholder="Please login to chat..." rows="1" disabled></textarea> | |
| <button class="material-icons speak-btn" onclick="readLastMessage()">volume_up</button> | |
| <button class="material-icons speak-btn" onclick="startVoiceInput()">mic</button> | |
| </div> | |
| <button onclick="sendMessage()" class="bg-blue-500 hover:bg-blue-600 text-white rounded-full p-3"> | |
| <span class="material-icons">send</span> | |
| </button> | |
| </div> | |
| <div class="text-xs text-gray-400 text-center mt-2 flex justify-center space-x-4"> | |
| <a href="https://t.me/Thunderownerx" target="_blank" class="hover:text-blue-600"><span class="material-icons">telegram</span></a> | |
| <a href="https://instagram.com/Thunderownerx" target="_blank" class="hover:text-pink-600"><span class="material-icons">photo_camera</span></a> | |
| <span>Contact Us <strong>moodthunder00@gmail.com</strong></span> | |
| </div> | |
| </div> | |
| <script> | |
| // Authentication state | |
| let currentUser = null; | |
| let isLoggedIn = false; | |
| let currentChatId = null; | |
| let chatHistory = {}; | |
| // Initialize with demo data if localStorage is empty | |
| if (!localStorage.getItem('thunderAIUsers')) { | |
| localStorage.setItem('thunderAIUsers', JSON.stringify({ | |
| 'user@example.com': { | |
| password: 'password123', | |
| phone: '+1234567890', | |
| chats: {} | |
| } | |
| })); | |
| } | |
| // Check if user is already logged in | |
| if (localStorage.getItem('thunderAICurrentUser')) { | |
| currentUser = localStorage.getItem('thunderAICurrentUser'); | |
| isLoggedIn = true; | |
| loadUserChats(); | |
| document.getElementById('userInput').placeholder = "Message Anything...."; | |
| document.getElementById('userInput').disabled = false; | |
| } else { | |
| document.getElementById('authModal').style.display = 'flex'; | |
| } | |
| const tools = [ | |
| { | |
| type: "function", | |
| function: { | |
| name: "get_weather", | |
| description: "Get current weather for a given location", | |
| parameters: { | |
| type: "object", | |
| properties: { | |
| location: { | |
| type: "string", | |
| description: "City name e.g. Paris, London" | |
| } | |
| }, | |
| required: ["location"] | |
| }, | |
| strict: true | |
| } | |
| } | |
| ]; | |
| function getWeather(location) { | |
| const data = { | |
| 'Paris': '22°C, Partly Cloudy', | |
| 'London': '18°C, Rainy', | |
| 'Tokyo': '28°C, Clear', | |
| 'New York': '25°C, Sunny' | |
| }; | |
| return data[location] || '20°C, Unknown'; | |
| } | |
| let lastAIMessage = ""; | |
| document.getElementById("restart-btn").addEventListener("click", () => { | |
| if (!isLoggedIn) { | |
| alert("Please login to start a new chat"); | |
| return; | |
| } | |
| document.getElementById("chatBox").innerHTML = ` | |
| <div class="message-ai p-4 shadow-sm max-w-[80%]"> | |
| <p class="text-gray-200">Hello! How can I help you today?</p> | |
| </div> | |
| `; | |
| currentChatId = 'chat_' + Date.now(); | |
| saveCurrentChat(); | |
| }); | |
| document.getElementById("menu-btn").addEventListener("click", () => { | |
| const modal = document.getElementById("menuModal"); | |
| modal.style.display = modal.style.display === "block" ? "none" : "block"; | |
| }); | |
| document.getElementById("history-btn").addEventListener("click", () => { | |
| if (!isLoggedIn) { | |
| alert("Please login to view chat history"); | |
| return; | |
| } | |
| showChatHistory(); | |
| }); | |
| document.getElementById("user-btn").addEventListener("click", () => { | |
| if (isLoggedIn) { | |
| const modal = document.getElementById("menuModal"); | |
| modal.style.display = modal.style.display === "block" ? "none" : "block"; | |
| } else { | |
| document.getElementById("authModal").style.display = 'flex'; | |
| } | |
| }); | |
| async function sendMessage(text = null) { | |
| if (!isLoggedIn) { | |
| alert("Please login to chat"); | |
| return; | |
| } | |
| const input = document.getElementById("userInput"); | |
| const message = text || input.value.trim(); | |
| if (!message) return; | |
| appendMessage(message, 'user'); | |
| input.value = ''; | |
| // Show loading indicator | |
| const loadingDiv = document.createElement("div"); | |
| loadingDiv.className = "flex items-start space-x-3 max-w-3xl mx-auto"; | |
| loadingDiv.innerHTML = ` | |
| <div class='w-10 h-10 rounded-full bg-gray-200 flex items-center justify-center text-gray-600'><span class='material-icons'>smart_toy</span></div> | |
| <div class='message-ai p-4 shadow-sm max-w-[80%] relative'> | |
| <div class="flex space-x-2"> | |
| <div class="w-2 h-2 rounded-full bg-gray-400 animate-bounce"></div> | |
| <div class="w-2 h-2 rounded-full bg-gray-400 animate-bounce" style="animation-delay: 0.2s"></div> | |
| <div class="w-2 h-2 rounded-full bg-gray-400 animate-bounce" style="animation-delay: 0.4s"></div> | |
| </div> | |
| </div> | |
| `; | |
| document.getElementById("chatBox").appendChild(loadingDiv); | |
| document.getElementById("chatBox").scrollTop = document.getElementById("chatBox").scrollHeight; | |
| try { | |
| const completion = await puter.ai.chat(message, { tools }); | |
| let finalResponse; | |
| if (completion.message.tool_calls && completion.message.tool_calls.length > 0) { | |
| const toolCall = completion.message.tool_calls[0]; | |
| if (toolCall.function.name === 'get_weather') { | |
| const args = JSON.parse(toolCall.function.arguments); | |
| const result = getWeather(args.location); | |
| finalResponse = await puter.ai.chat([ | |
| { role: "user", content: message }, | |
| completion.message, | |
| { role: "tool", tool_call_id: toolCall.id, content: result } | |
| ]); | |
| } | |
| } else { | |
| finalResponse = completion; | |
| } | |
| // Remove loading indicator | |
| document.getElementById("chatBox").removeChild(loadingDiv); | |
| const reply = finalResponse.message?.content || "Sorry, no reply."; | |
| appendMessage(reply, 'ai'); | |
| lastAIMessage = reply; | |
| // Save the chat after each message | |
| saveCurrentChat(); | |
| } catch (err) { | |
| // Remove loading indicator | |
| document.getElementById("chatBox").removeChild(loadingDiv); | |
| appendMessage("API Error: " + err.message, 'ai'); | |
| } | |
| } | |
| function appendMessage(text, sender) { | |
| const chatBox = document.getElementById("chatBox"); | |
| const msgDiv = document.createElement("div"); | |
| msgDiv.className = `flex items-start space-x-3 max-w-3xl mx-auto ${sender === 'user' ? 'justify-end' : ''}`; | |
| if (sender === 'user') { | |
| msgDiv.innerHTML = ` | |
| <div class='message-user p-4 shadow-sm max-w-[80%]'><p class='text-gray-200'>${text}</p></div> | |
| <div class='w-10 h-10 rounded-full bg-blue-100 flex items-center justify-center text-blue-500'><span class='material-icons'>person</span></div>`; | |
| } else { | |
| const safeText = text.replace(/[`\\]/g, '\\$&'); | |
| msgDiv.innerHTML = ` | |
| <div class='w-10 h-10 rounded-full bg-gray-200 flex items-center justify-center text-gray-600'><span class='material-icons'>smart_toy</span></div> | |
| <div class='message-ai p-4 shadow-sm max-w-[80%] relative'> | |
| <p class='text-gray-200'>${text}</p> | |
| <div class="voice-button" onclick="playVoice(\`${safeText}\`)">🔊</div> | |
| </div>`; | |
| } | |
| chatBox.appendChild(msgDiv); | |
| chatBox.scrollTop = chatBox.scrollHeight; | |
| } | |
| function readLastMessage() { | |
| if (lastAIMessage) playVoice(lastAIMessage); | |
| else alert("No AI message to read."); | |
| } | |
| function playVoice(text) { | |
| const utter = new SpeechSynthesisUtterance(text); | |
| utter.lang = "en-US"; | |
| utter.rate = 1; | |
| speechSynthesis.speak(utter); | |
| } | |
| function startVoiceInput() { | |
| if (!isLoggedIn) { | |
| alert("Please login to use voice input"); | |
| return; | |
| } | |
| try { | |
| const recognition = new (window.SpeechRecognition || window.webkitSpeechRecognition)(); | |
| recognition.lang = "en-US"; | |
| recognition.start(); | |
| recognition.onresult = (event) => { | |
| const transcript = event.results[0][0].transcript; | |
| sendMessage(transcript); | |
| }; | |
| recognition.onerror = (event) => { | |
| alert("Voice input error: " + event.error); | |
| }; | |
| } catch (err) { | |
| alert("Your browser doesn't support voice input."); | |
| } | |
| } | |
| // Authentication functions | |
| function switchAuthTab(tab) { | |
| document.querySelectorAll('.auth-tab').forEach(t => t.classList.remove('active')); | |
| document.querySelectorAll('.auth-content').forEach(c => c.classList.remove('active')); | |
| if (tab === 'login') { | |
| document.querySelector('.auth-tab:nth-child(1)').classList.add('active'); | |
| document.getElementById('loginContent').classList.add('active'); | |
| } else if (tab === 'signup') { | |
| document.querySelector('.auth-tab:nth-child(2)').classList.add('active'); | |
| document.getElementById('signupContent').classList.add('active'); | |
| } else if (tab === 'forgot') { | |
| document.getElementById('forgotContent').classList.add('active'); | |
| } else if (tab === 'otp') { | |
| document.getElementById('otpContent').classList.add('active'); | |
| } | |
| } | |
| function login() { | |
| const email = document.getElementById('loginEmail').value.trim(); | |
| const password = document.getElementById('loginPassword').value; | |
| if (!email || !password) { | |
| alert('Please enter both email/phone and password'); | |
| return; | |
| } | |
| const users = JSON.parse(localStorage.getItem('thunderAIUsers')); | |
| let userFound = false; | |
| // Check email or phone login | |
| for (const key in users) { | |
| if (key === email || users[key].phone === email) { | |
| if (users[key].password === password) { | |
| userFound = true; | |
| currentUser = key; | |
| break; | |
| } | |
| } | |
| } | |
| if (userFound) { | |
| isLoggedIn = true; | |
| localStorage.setItem('thunderAICurrentUser', currentUser); | |
| document.getElementById('authModal').style.display = 'none'; | |
| document.getElementById('userInput').placeholder = "Message Anything...."; | |
| document.getElementById('userInput').disabled = false; | |
| // Start a new chat session | |
| document.getElementById("chatBox").innerHTML = ` | |
| <div class="message-ai p-4 shadow-sm max-w-[80%]"> | |
| <p class="text-gray-200">Hello! How can I help you today?</p> | |
| </div> | |
| `; | |
| currentChatId = 'chat_' + Date.now(); | |
| loadUserChats(); | |
| } else { | |
| alert('Invalid email/phone or password'); | |
| } | |
| } | |
| function signup() { | |
| const email = document.getElementById('signupEmail').value.trim(); | |
| const password = document.getElementById('signupPassword').value; | |
| const confirmPassword = document.getElementById('signupConfirmPassword').value; | |
| if (!email) { | |
| alert('Please enter your email or phone number'); | |
| return; | |
| } | |
| if (!password || password.length < 6) { | |
| alert('Password must be at least 6 characters'); | |
| return; | |
| } | |
| if (password !== confirmPassword) { | |
| alert('Passwords do not match'); | |
| return; | |
| } | |
| const users = JSON.parse(localStorage.getItem('thunderAIUsers')); | |
| // Check if email is already registered | |
| if (users[email]) { | |
| alert('This email/phone is already registered'); | |
| return; | |
| } | |
| // Add new user | |
| users[email] = { | |
| password: password, | |
| phone: email.includes('@') ? '' : email, // Store phone if it's not email | |
| chats: {} | |
| }; | |
| localStorage.setItem('thunderAIUsers', JSON.stringify(users)); | |
| alert('Account created successfully! Please login.'); | |
| switchAuthTab('login'); | |
| document.getElementById('loginEmail').value = email; | |
| } | |
| function sendResetCode() { | |
| const email = document.getElementById('forgotEmail').value.trim(); | |
| if (!email) { | |
| alert('Please enter your email or phone number'); | |
| return; | |
| } | |
| // In a real app, you would send an OTP to the user's email/phone | |
| // Here we'll just simulate it | |
| alert(`OTP sent to ${email} (simulated)`); | |
| switchAuthTab('otp'); | |
| } | |
| function resendOTP() { | |
| alert('New OTP sent (simulated)'); | |
| } | |
| function verifyOTP() { | |
| // In a real app, you would verify the OTP | |
| // Here we'll just simulate successful verification | |
| alert('Password reset successful! Please login with your new password.'); | |
| switchAuthTab('login'); | |
| } | |
| function moveToNext(input, next) { | |
| if (input.value.length === 1) { | |
| if (next <= 6) { | |
| const nextInput = input.parentElement.querySelector(`input:nth-child(${next})`); | |
| if (nextInput) nextInput.focus(); | |
| } | |
| } | |
| } | |
| function logout() { | |
| isLoggedIn = false; | |
| currentUser = null; | |
| localStorage.removeItem('thunderAICurrentUser'); | |
| document.getElementById('menuModal').style.display = 'none'; | |
| document.getElementById('authModal').style.display = 'flex'; | |
| document.getElementById('userInput').placeholder = "Please login to chat..."; | |
| document.getElementById('userInput').disabled = true; | |
| document.getElementById("chatBox").innerHTML = ` | |
| <div class="message-ai p-4 shadow-sm max-w-[80%]"> | |
| <p class="text-gray-200">Hello! Please login to start chatting.</p> | |
| </div> | |
| `; | |
| } | |
| // Chat history functions | |
| function saveCurrentChat() { | |
| if (!isLoggedIn || !currentChatId) return; | |
| const chatBox = document.getElementById('chatBox'); | |
| const messages = []; | |
| // Get all messages from chat | |
| chatBox.querySelectorAll('.flex.items-start').forEach(msgDiv => { | |
| const isUser = msgDiv.classList.contains('justify-end'); | |
| const messageText = msgDiv.querySelector('p')?.textContent || ''; | |
| if (messageText) { | |
| messages.push({ | |
| sender: isUser ? 'user' : 'ai', | |
| text: messageText | |
| }); | |
| } | |
| }); | |
| // Save to user's chat history | |
| const users = JSON.parse(localStorage.getItem('thunderAIUsers')); | |
| if (!users[currentUser].chats) users[currentUser].chats = {}; | |
| users[currentUser].chats[currentChatId] = { | |
| id: currentChatId, | |
| title: messages.length > 0 ? messages[0].text.substring(0, 30) : 'New Chat', | |
| lastMessage: messages.length > 0 ? messages[messages.length-1].text.substring(0, 50) : '', | |
| timestamp: Date.now(), | |
| messages: messages | |
| }; | |
| localStorage.setItem('thunderAIUsers', JSON.stringify(users)); | |
| loadUserChats(); | |
| } | |
| function loadUserChats() { | |
| if (!isLoggedIn) return; | |
| const users = JSON.parse(localStorage.getItem('thunderAIUsers')); | |
| chatHistory = users[currentUser].chats || {}; | |
| // If no current chat, create a new one | |
| if (!currentChatId || !chatHistory[currentChatId]) { | |
| currentChatId = 'chat_' + Date.now(); | |
| chatHistory[currentChatId] = { | |
| id: currentChatId, | |
| title: 'New Chat', | |
| lastMessage: '', | |
| timestamp: Date.now(), | |
| messages: [] | |
| }; | |
| saveCurrentChat(); | |
| } | |
| } | |
| function showChatHistory() { | |
| if (!isLoggedIn) return; | |
| const chatHistoryList = document.getElementById('chatHistoryList'); | |
| chatHistoryList.innerHTML = ''; | |
| const users = JSON.parse(localStorage.getItem('thunderAIUsers')); | |
| const chats = users[currentUser].chats; | |
| // Sort chats by timestamp (newest first) | |
| const sortedChats = Object.values(chats).sort((a, b) => b.timestamp - a.timestamp); | |
| sortedChats.forEach(chat => { | |
| const chatItem = document.createElement('div'); | |
| chatItem.className = 'chat-history-item'; | |
| chatItem.innerHTML = ` | |
| <h3>${chat.title}</h3> | |
| <p>${chat.lastMessage || 'No messages yet'}</p> | |
| `; | |
| chatItem.onclick = () => loadChat(chat.id); | |
| chatHistoryList.appendChild(chatItem); | |
| }); | |
| document.getElementById('chatHistoryModal').style.display = 'flex'; | |
| } | |
| function closeChatHistory() { | |
| document.getElementById('chatHistoryModal').style.display = 'none'; | |
| } | |
| function loadChat(chatId) { | |
| if (!isLoggedIn) return; | |
| const users = JSON.parse(localStorage.getItem('thunderAIUsers')); | |
| const chat = users[currentUser].chats[chatId]; | |
| if (!chat) return; | |
| currentChatId = chatId; | |
| // Clear current chat | |
| const chatBox = document.getElementById('chatBox'); | |
| chatBox.innerHTML = ''; | |
| // Load messages from chat history | |
| chat.messages.forEach(msg => { | |
| appendMessage(msg.text, msg.sender); | |
| }); | |
| closeChatHistory(); | |
| } | |
| // Initialize a new chat if none exists | |
| if (isLoggedIn && (!currentChatId || !chatHistory[currentChatId])) { | |
| currentChatId = 'chat_' + Date.now(); | |
| saveCurrentChat(); | |
| } | |
| </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=ThunderOwnerx/thunderai" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body> | |
| </html> |