Spaces:
Running
Running
| <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>Gemini Buddy</title> | |
| <script src="https://cdn.tailwindcss.com"></script> | |
| <script src="https://unpkg.com/feather-icons"></script> | |
| <script src="https://cdn.jsdelivr.net/npm/feather-icons/dist/feather.min.js"></script> | |
| <script src="https://cdn.jsdelivr.net/npm/vanta@latest/dist/vanta.net.min.js"></script> | |
| <script> | |
| tailwind.config = { | |
| theme: { | |
| extend: { | |
| colors: { | |
| primary: { | |
| 500: '#4285F4', | |
| 600: '#3367D6' | |
| }, | |
| secondary: { | |
| 500: '#34A853', | |
| 600: '#2D9249' | |
| } | |
| } | |
| } | |
| } | |
| } | |
| </script> | |
| <style> | |
| .chat-message.user { | |
| background-color: #E8F0FE; | |
| border-radius: 18px 18px 4px 18px; | |
| } | |
| .chat-message.bot { | |
| background-color: #F1F3F4; | |
| border-radius: 18px 18px 18px 4px; | |
| } | |
| #vanta-bg { | |
| position: fixed; | |
| top: 0; | |
| left: 0; | |
| width: 100%; | |
| height: 100%; | |
| z-index: -1; | |
| opacity: 0.3; | |
| } | |
| .typing-indicator span { | |
| display: inline-block; | |
| width: 8px; | |
| height: 8px; | |
| background-color: #5F6368; | |
| border-radius: 50%; | |
| margin-right: 4px; | |
| animation: bounce 1.4s infinite ease-in-out both; | |
| } | |
| .typing-indicator span:nth-child(1) { | |
| animation-delay: 0s; | |
| } | |
| .typing-indicator span:nth-child(2) { | |
| animation-delay: 0.2s; | |
| } | |
| .typing-indicator span:nth-child(3) { | |
| animation-delay: 0.4s; | |
| margin-right: 0; | |
| } | |
| @keyframes bounce { | |
| 0%, 80%, 100% { transform: scale(0); } | |
| 40% { transform: scale(1); } | |
| } | |
| </style> | |
| </head> | |
| <body class="bg-gray-50 min-h-screen flex flex-col"> | |
| <div id="vanta-bg"></div> | |
| <header class="bg-white shadow-sm py-4 px-6 sticky top-0 z-10"> | |
| <div class="max-w-4xl mx-auto flex items-center justify-between"> | |
| <div class="flex items-center space-x-2"> | |
| <div class="w-10 h-10 rounded-full bg-gradient-to-r from-primary-500 to-secondary-500 flex items-center justify-center"> | |
| <i data-feather="message-square" class="text-white"></i> | |
| </div> | |
| <h1 class="text-xl font-bold text-gray-800">Gemini Buddy</h1> | |
| </div> | |
| <button id="clear-chat" class="text-gray-500 hover:text-primary-500 transition-colors"> | |
| <i data-feather="trash-2" class="w-5 h-5"></i> | |
| </button> | |
| </div> | |
| </header> | |
| <main class="flex-1 max-w-4xl w-full mx-auto px-4 py-6 overflow-y-auto"> | |
| <div id="chat-container" class="space-y-4"> | |
| <div class="chat-message bot max-w-[85%] p-4"> | |
| <div class="flex items-start space-x-2"> | |
| <div class="w-8 h-8 rounded-full bg-secondary-500 flex items-center justify-center flex-shrink-0"> | |
| <i data-feather="cpu" class="text-white w-4 h-4"></i> | |
| </div> | |
| <div class="flex-1"> | |
| <p class="text-gray-800">Hello! I'm Gemini, your AI assistant. How can I help you today?</p> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| </main> | |
| <footer class="bg-white border-t py-4 px-6 sticky bottom-0"> | |
| <div class="max-w-4xl mx-auto"> | |
| <form id="chat-form" class="flex space-x-2"> | |
| <input | |
| id="user-input" | |
| type="text" | |
| placeholder="Ask me anything..." | |
| class="flex-1 border border-gray-300 rounded-full px-4 py-2 focus:outline-none focus:ring-2 focus:ring-primary-500 focus:border-transparent" | |
| autocomplete="off" | |
| > | |
| <button | |
| type="submit" | |
| class="w-12 h-12 rounded-full bg-primary-500 text-white flex items-center justify-center hover:bg-primary-600 transition-colors" | |
| > | |
| <i data-feather="send" class="w-5 h-5"></i> | |
| </button> | |
| </form> | |
| </div> | |
| </footer> | |
| <script> | |
| const gemini = { | |
| getNewCookie: async function () { | |
| const r = await fetch("https://gemini.google.com/_/BardChatUi/data/batchexecute?rpcids=maGuAc&source-path=%2F&bl=boq_assistant-bard-web-server_20250814.06_p1&f.sid=-7816331052118000090&hl=en-US&_reqid=173780&rt=c", { | |
| "headers": { | |
| "content-type": "application/x-www-form-urlencoded;charset=UTF-8", | |
| }, | |
| "body": "f.req=%5B%5B%5B%22maGuAc%22%2C%22%5B0%5D%22%2Cnull%2C%22generic%22%5D%5D%5D&", | |
| "method": "POST" | |
| }); | |
| console.log('get new cookie') | |
| return r.headers.getSetCookie()[0].split("; ")[0] | |
| }, | |
| ask: async function (prompt, previousId = null) { | |
| try { | |
| if (typeof (prompt) !== "string" || !prompt?.trim()?.length) { | |
| throw new Error(`Prompt cannot be empty`); | |
| } | |
| const response = await fetch('/api/chat', { | |
| method: 'POST', | |
| headers: { | |
| 'Content-Type': 'application/json', | |
| }, | |
| body: JSON.stringify({ | |
| prompt: prompt, | |
| conversationId: previousId | |
| }) | |
| }); | |
| if (!response.ok) { | |
| const errorData = await response.json(); | |
| throw new Error(errorData.message || 'Failed to get response'); | |
| } | |
| const data = await response.json(); | |
| return { | |
| text: data.response, | |
| id: data.conversationId | |
| }; | |
| } catch (error) { | |
| console.error('Error in ask function:', error); | |
| throw error; | |
| } | |
| } | |
| } | |
| // Initialize Vanta.js background | |
| if (window.VANTA) { | |
| VANTA.NET({ | |
| el: "#vanta-bg", | |
| mouseControls: true, | |
| touchControls: true, | |
| gyroControls: false, | |
| minHeight: 200.00, | |
| minWidth: 200.00, | |
| scale: 1.00, | |
| scaleMobile: 1.00, | |
| color: 0x4285f4, | |
| backgroundColor: 0xf8fafc, | |
| points: 10.00, | |
| maxDistance: 20.00, | |
| spacing: 15.00 | |
| }); | |
| } else { | |
| console.warn('VANTA library not loaded'); | |
| document.getElementById('vanta-bg').style.backgroundColor = '#f8fafc'; | |
| } | |
| // Initialize chat functionality | |
| document.addEventListener('DOMContentLoaded', () => { | |
| feather.replace(); | |
| const chatContainer = document.getElementById('chat-container'); | |
| const chatForm = document.getElementById('chat-form'); | |
| const userInput = document.getElementById('user-input'); | |
| const clearChatBtn = document.getElementById('clear-chat'); | |
| let conversationId = null; | |
| // Add user message to chat | |
| function addUserMessage(message) { | |
| const messageDiv = document.createElement('div'); | |
| messageDiv.className = 'chat-message user max-w-[85%] ml-auto p-4'; | |
| messageDiv.innerHTML = ` | |
| <div class="flex items-start space-x-2 justify-end"> | |
| <div class="flex-1 text-right"> | |
| <p class="text-gray-800">${message}</p> | |
| </div> | |
| <div class="w-8 h-8 rounded-full bg-primary-500 flex items-center justify-center flex-shrink-0"> | |
| <i data-feather="user" class="text-white w-4 h-4"></i> | |
| </div> | |
| </div> | |
| `; | |
| chatContainer.appendChild(messageDiv); | |
| scrollToBottom(); | |
| } | |
| // Add bot message to chat | |
| function addBotMessage(message, isTyping = false) { | |
| const messageDiv = document.createElement('div'); | |
| messageDiv.className = 'chat-message bot max-w-[85%] p-4'; | |
| if (isTyping) { | |
| messageDiv.innerHTML = ` | |
| <div class="flex items-start space-x-2"> | |
| <div class="w-8 h-8 rounded-full bg-secondary-500 flex items-center justify-center flex-shrink-0"> | |
| <i data-feather="cpu" class="text-white w-4 h-4"></i> | |
| </div> | |
| <div class="typing-indicator flex space-x-1 items-center"> | |
| <span></span> | |
| <span></span> | |
| <span></span> | |
| </div> | |
| </div> | |
| `; | |
| } else { | |
| messageDiv.innerHTML = ` | |
| <div class="flex items-start space-x-2"> | |
| <div class="w-8 h-8 rounded-full bg-secondary-500 flex items-center justify-center flex-shrink-0"> | |
| <i data-feather="cpu" class="text-white w-4 h-4"></i> | |
| </div> | |
| <div class="flex-1"> | |
| <p class="text-gray-800">${message}</p> | |
| </div> | |
| </div> | |
| `; | |
| } | |
| chatContainer.appendChild(messageDiv); | |
| scrollToBottom(); | |
| feather.replace(); | |
| return messageDiv; | |
| } | |
| // Scroll chat to bottom | |
| function scrollToBottom() { | |
| chatContainer.scrollTop = chatContainer.scrollHeight; | |
| } | |
| // Handle form submission | |
| chatForm.addEventListener('submit', async (e) => { | |
| e.preventDefault(); | |
| const message = userInput.value.trim(); | |
| if (!message) { | |
| userInput.focus(); | |
| // Ensure form submission works on Enter key | |
| userInput.addEventListener('keydown', (e) => { | |
| if (e.key === 'Enter' && !e.shiftKey) { | |
| e.preventDefault(); | |
| chatForm.dispatchEvent(new Event('submit')); | |
| } | |
| }); | |
| return; | |
| } | |
| // Add user message | |
| addUserMessage(message); | |
| userInput.value = ''; | |
| // Add typing indicator | |
| const typingElement = addBotMessage('', true); | |
| try { | |
| // Get response from Gemini | |
| const response = await gemini.ask(message, conversationId); | |
| // Remove typing indicator | |
| chatContainer.removeChild(typingElement); | |
| // Add bot response with markdown support | |
| const formattedText = response.text | |
| .replace(/\*\*(.+?)\*\*/g, '<strong>$1</strong>') | |
| .replace(/\*(.+?)\*/g, '<em>$1</em>') | |
| .replace(/```([\s\S]+?)```/g, '<pre><code>$1</code></pre>'); | |
| addBotMessage(formattedText); | |
| // Update conversation ID for context | |
| conversationId = response.id; | |
| } catch (error) { | |
| // Remove typing indicator | |
| chatContainer.removeChild(typingElement); | |
| // Show user-friendly error message | |
| addBotMessage("I'm having trouble connecting right now. Please try again later."); | |
| console.error('Chat error:', error); | |
| } catch (error) { | |
| // Remove typing indicator | |
| chatContainer.removeChild(typingElement); | |
| // Show error message | |
| addBotMessage(`Sorry, I encountered an error: ${error.message}`); | |
| console.error(error); | |
| } | |
| }); | |
| // Clear chat history | |
| clearChatBtn.addEventListener('click', () => { | |
| // Keep only the initial bot message | |
| while (chatContainer.children.length > 1) { | |
| chatContainer.removeChild(chatContainer.lastChild); | |
| } | |
| conversationId = null; | |
| }); | |
| // Focus input on page load | |
| userInput.focus(); | |
| }); | |
| </script> | |
| </body> | |
| </html> | |