Spaces:
Running
Running
| <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>AI Chat Assistant</title> | |
| <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css"> | |
| <script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"></script> | |
| <style> | |
| :root { | |
| --primary-color: #5438dc; | |
| --secondary-color: #6c5ce7; | |
| --text-color: #2d3436; | |
| --light-color: #f5f6fa; | |
| --dark-color: #2d3436; | |
| --success-color: #00b894; | |
| --warning-color: #fdcb6e; | |
| --error-color: #d63031; | |
| } | |
| * { | |
| margin: 0; | |
| padding: 0; | |
| box-sizing: border-box; | |
| font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; | |
| } | |
| body { | |
| background-color: #f9f9f9; | |
| color: var(--text-color); | |
| line-height: 1.6; | |
| display: flex; | |
| flex-direction: column; | |
| min-height: 100vh; | |
| } | |
| .container { | |
| max-width: 1200px; | |
| margin: 0 auto; | |
| padding: 20px; | |
| display: flex; | |
| flex-direction: column; | |
| flex-grow: 1; | |
| } | |
| header { | |
| padding: 20px 0; | |
| text-align: center; | |
| border-bottom: 1px solid #eee; | |
| margin-bottom: 20px; | |
| } | |
| h1 { | |
| color: var(--primary-color); | |
| font-size: 2.5rem; | |
| margin-bottom: 10px; | |
| } | |
| .subtitle { | |
| color: var(--dark-color); | |
| opacity: 0.8; | |
| font-size: 1.1rem; | |
| } | |
| .chat-container { | |
| border-radius: 12px; | |
| box-shadow: 0 10px 30px rgba(0, 0, 0, 0.1); | |
| background-color: white; | |
| display: flex; | |
| flex-direction: column; | |
| flex-grow: 1; | |
| overflow: hidden; | |
| position: relative; | |
| } | |
| .chat-header { | |
| background: linear-gradient(135deg, var(--primary-color), var(--secondary-color)); | |
| color: white; | |
| padding: 15px 20px; | |
| display: flex; | |
| align-items: center; | |
| gap: 10px; | |
| } | |
| .chat-header i { | |
| font-size: 1.5rem; | |
| } | |
| .chat-header h2 { | |
| font-size: 1.3rem; | |
| font-weight: 500; | |
| } | |
| .chat-messages { | |
| padding: 20px; | |
| overflow-y: auto; | |
| flex-grow: 1; | |
| max-height: 70vh; | |
| display: flex; | |
| flex-direction: column; | |
| gap: 20px; | |
| } | |
| .message { | |
| max-width: 80%; | |
| padding: 12px 16px; | |
| border-radius: 12px; | |
| line-height: 1.5; | |
| position: relative; | |
| animation: fadeIn 0.3s ease-out; | |
| } | |
| @keyframes fadeIn { | |
| from { opacity: 0; transform: translateY(10px); } | |
| to { opacity: 1; transform: translateY(0); } | |
| } | |
| .user-message { | |
| background-color: var(--primary-color); | |
| color: white; | |
| align-self: flex-end; | |
| border-bottom-right-radius: 4px; | |
| } | |
| .ai-message { | |
| background-color: #f1f3fe; | |
| color: var(--text-color); | |
| align-self: flex-start; | |
| border-bottom-left-radius: 4px; | |
| } | |
| .message-info { | |
| display: flex; | |
| align-items: center; | |
| gap: 8px; | |
| margin-bottom: 5px; | |
| font-size: 0.85rem; | |
| opacity: 0.7; | |
| } | |
| .user-message .message-info { | |
| color: rgba(255, 255, 255, 0.8); | |
| justify-content: flex-end; | |
| } | |
| .ai-message .message-info { | |
| color: rgba(45, 52, 54, 0.7); | |
| } | |
| .typing-indicator { | |
| display: flex; | |
| padding: 12px 16px; | |
| background-color: #f1f3fe; | |
| border-radius: 12px; | |
| align-self: flex-start; | |
| margin-bottom: 20px; | |
| border-bottom-left-radius: 4px; | |
| } | |
| .typing-dot { | |
| width: 8px; | |
| height: 8px; | |
| background-color: var(--primary-color); | |
| border-radius: 50%; | |
| margin: 0 2px; | |
| animation: typingAnimation 1.4s infinite ease-in-out; | |
| } | |
| .typing-dot:nth-child(1) { | |
| animation-delay: 0s; | |
| } | |
| .typing-dot:nth-child(2) { | |
| animation-delay: 0.2s; | |
| } | |
| .typing-dot:nth-child(3) { | |
| animation-delay: 0.4s; | |
| } | |
| @keyframes typingAnimation { | |
| 0%, 60%, 100% { | |
| transform: translateY(0); | |
| } | |
| 30% { | |
| transform: translateY(-5px); | |
| } | |
| } | |
| .chat-input-container { | |
| padding: 15px; | |
| background-color: white; | |
| border-top: 1px solid #eee; | |
| display: flex; | |
| gap: 10px; | |
| position: relative; | |
| } | |
| .chat-textarea { | |
| flex-grow: 1; | |
| border: 1px solid #ddd; | |
| border-radius: 24px; | |
| padding: 12px 20px; | |
| resize: none; | |
| font-size: 1rem; | |
| outline: none; | |
| transition: border 0.3s; | |
| max-height: 150px; | |
| min-height: 50px; | |
| } | |
| .chat-textarea:focus { | |
| border-color: var(--primary-color); | |
| box-shadow: 0 0 0 2px rgba(84, 56, 220, 0.2); | |
| } | |
| .send-button { | |
| background-color: var(--primary-color); | |
| color: white; | |
| border: none; | |
| border-radius: 50%; | |
| width: 50px; | |
| height: 50px; | |
| display: flex; | |
| align-items: center; | |
| justify-content: center; | |
| cursor: pointer; | |
| transition: background-color 0.3s, transform 0.2s; | |
| } | |
| .send-button:hover { | |
| background-color: var(--secondary-color); | |
| } | |
| .send-button:active { | |
| transform: scale(0.95); | |
| } | |
| .send-button i { | |
| font-size: 1.2rem; | |
| } | |
| footer { | |
| text-align: center; | |
| padding: 20px; | |
| color: var(--dark-color); | |
| opacity: 0.7; | |
| font-size: 0.9rem; | |
| } | |
| .api-key-container { | |
| margin-bottom: 20px; | |
| padding: 15px; | |
| background-color: white; | |
| border-radius: 8px; | |
| box-shadow: 0 4px 12px rgba(0, 0, 0, 0.05); | |
| display: flex; | |
| gap: 10px; | |
| } | |
| .api-key-input { | |
| flex-grow: 1; | |
| border: 1px solid #ddd; | |
| border-radius: 6px; | |
| padding: 10px 15px; | |
| font-size: 1rem; | |
| outline: none; | |
| } | |
| .api-key-input:focus { | |
| border-color: var(--primary-color); | |
| } | |
| .save-key-button { | |
| background-color: var(--primary-color); | |
| color: white; | |
| border: none; | |
| border-radius: 6px; | |
| padding: 10px 20px; | |
| cursor: pointer; | |
| transition: background-color 0.3s; | |
| } | |
| .save-key-button:hover { | |
| background-color: var(--secondary-color); | |
| } | |
| .model-selector { | |
| margin-bottom: 20px; | |
| padding: 10px 15px; | |
| border-radius: 6px; | |
| background-color: white; | |
| border: 1px solid #ddd; | |
| outline: none; | |
| width: 100%; | |
| max-width: 300px; | |
| font-size: 1rem; | |
| } | |
| .model-selector:focus { | |
| border-color: var(--primary-color); | |
| } | |
| .settings-panel { | |
| display: flex; | |
| flex-wrap: wrap; | |
| gap: 15px; | |
| margin-bottom: 20px; | |
| } | |
| .setting-group { | |
| background-color: white; | |
| padding: 15px; | |
| border-radius: 8px; | |
| box-shadow: 0 4px 12px rgba(0, 0, 0, 0.05); | |
| flex-grow: 1; | |
| min-width: 200px; | |
| } | |
| .setting-group h3 { | |
| margin-bottom: 10px; | |
| color: var(--primary-color); | |
| font-size: 1rem; | |
| } | |
| .slider-container { | |
| width: 100%; | |
| } | |
| .slider-container label { | |
| display: block; | |
| margin-bottom: 5px; | |
| font-size: 0.9rem; | |
| color: var(--text-color); | |
| } | |
| .slider-container input[type="range"] { | |
| width: 100%; | |
| -webkit-appearance: none; | |
| height: 6px; | |
| border-radius: 3px; | |
| background: #ddd; | |
| outline: none; | |
| } | |
| .slider-container input[type="range"]::-webkit-slider-thumb { | |
| -webkit-appearance: none; | |
| appearance: none; | |
| width: 18px; | |
| height: 18px; | |
| border-radius: 50%; | |
| background: var(--primary-color); | |
| cursor: pointer; | |
| transition: background .15s ease-in-out; | |
| } | |
| .slider-value { | |
| text-align: right; | |
| font-size: 0.85rem; | |
| color: var(--dark-color); | |
| opacity: 0.7; | |
| } | |
| .clear-chat-button { | |
| background-color: #f5f6fa; | |
| color: var(--error-color); | |
| border: 1px solid var(--error-color); | |
| border-radius: 6px; | |
| padding: 10px 20px; | |
| cursor: pointer; | |
| transition: all 0.3s; | |
| margin-left: auto; | |
| display: block; | |
| } | |
| .clear-chat-button:hover { | |
| background-color: var(--error-color); | |
| color: white; | |
| } | |
| /* Markdown styling */ | |
| .message-content { | |
| overflow-x: auto; | |
| } | |
| .message-content h1, | |
| .message-content h2, | |
| .message-content h3, | |
| .message-content h4, | |
| .message-content h5, | |
| .message-content h6 { | |
| margin-top: 1em; | |
| margin-bottom: 0.5em; | |
| font-weight: bold; | |
| } | |
| .message-content h1 { | |
| font-size: 1.5em; | |
| border-bottom: 1px solid rgba(0, 0, 0, 0.1); | |
| padding-bottom: 0.3em; | |
| } | |
| .message-content h2 { | |
| font-size: 1.3em; | |
| border-bottom: 1px solid rgba(0, 0, 0, 0.1); | |
| padding-bottom: 0.2em; | |
| } | |
| .message-content h3 { | |
| font-size: 1.1em; | |
| } | |
| .message-content p { | |
| margin-bottom: 1em; | |
| line-height: 1.6; | |
| } | |
| .message-content ul, | |
| .message-content ol { | |
| margin-bottom: 1em; | |
| padding-left: 2em; | |
| } | |
| .message-content ul { | |
| list-style-type: disc; | |
| } | |
| .message-content ol { | |
| list-style-type: decimal; | |
| } | |
| .message-content li { | |
| margin-bottom: 0.5em; | |
| } | |
| .message-content blockquote { | |
| margin: 1em 0; | |
| padding: 0 1em; | |
| border-left: 0.25em solid #dfe2e5; | |
| color: #6a737d; | |
| } | |
| .message-content pre { | |
| background-color: #f6f8fa; | |
| border-radius: 6px; | |
| padding: 1em; | |
| overflow: auto; | |
| margin-bottom: 1em; | |
| } | |
| .message-content code { | |
| font-family: 'SFMono-Regular', Consolas, 'Liberation Mono', Menlo, monospace; | |
| background-color: rgba(27, 31, 35, 0.05); | |
| border-radius: 3px; | |
| padding: 0.2em 0.4em; | |
| font-size: 0.9em; | |
| } | |
| .message-content pre code { | |
| background-color: transparent; | |
| padding: 0; | |
| border-radius: 0; | |
| } | |
| .message-content table { | |
| border-collapse: collapse; | |
| width: 100%; | |
| margin-bottom: 1em; | |
| } | |
| .message-content th, | |
| .message-content td { | |
| border: 1px solid #dfe2e5; | |
| padding: 6px 13px; | |
| } | |
| .message-content th { | |
| background-color: #f6f8fa; | |
| font-weight: bold; | |
| text-align: left; | |
| } | |
| .message-content tr:nth-child(even) { | |
| background-color: #f6f8fa; | |
| } | |
| .message-content a { | |
| color: var(--primary-color); | |
| text-decoration: none; | |
| } | |
| .message-content a:hover { | |
| text-decoration: underline; | |
| } | |
| .message-content strong { | |
| font-weight: bold; | |
| } | |
| .message-content em { | |
| font-style: italic; | |
| } | |
| .user-message .message-content a { | |
| color: #b7c2ff; | |
| } | |
| .user-message .message-content a:hover { | |
| color: #fff; | |
| } | |
| @media (max-width: 768px) { | |
| .chat-messages { | |
| padding: 15px; | |
| } | |
| .message { | |
| max-width: 90%; | |
| } | |
| .api-key-container { | |
| flex-direction: column; | |
| } | |
| .settings-panel { | |
| flex-direction: column; | |
| } | |
| } | |
| </style> | |
| </head> | |
| <body> | |
| <div class="container"> | |
| <header> | |
| <h1>AI Chat Assistant</h1> | |
| <p class="subtitle">Powered by OpenAI's advanced language models</p> | |
| </header> | |
| <select class="model-selector" id="modelSelector"> | |
| <option value="gpt-4o-mini">GPT-4o-mini</option> | |
| <option value="gpt-4o">GPT-4o</option> | |
| </select> | |
| <div class="settings-panel"> | |
| <div class="setting-group"> | |
| <h3>Model Settings</h3> | |
| <div class="slider-container"> | |
| <label for="temperatureSlider">Temperature (Creativity)</label> | |
| <input type="range" id="temperatureSlider" min="0" max="1" step="0.1" value="0.7"> | |
| <div class="slider-value" id="temperatureValue">0.7</div> | |
| </div> | |
| <div class="slider-container"> | |
| <label for="maxTokensSlider">Max Tokens (Response Length)</label> | |
| <input type="range" id="maxTokensSlider" min="50" max="2000" step="50" value="500"> | |
| <div class="slider-value" id="maxTokensValue">500</div> | |
| </div> | |
| </div> | |
| </div> | |
| <div class="api-key-container"> | |
| <input type="password" class="api-key-input" id="apiKeyInput" placeholder="Enter your OpenAI API key"> | |
| <button class="save-key-button" id="saveKeyButton"> | |
| <i class="fas fa-save"></i> Save | |
| </button> | |
| </div> | |
| <button class="clear-chat-button" id="clearChatButton"> | |
| <i class="fas fa-trash-alt"></i> Clear Chat | |
| </button> | |
| <div class="chat-container"> | |
| <div class="chat-header"> | |
| <i class="fas fa-robot"></i> | |
| <h2>AI Assistant</h2> | |
| </div> | |
| <div class="chat-messages" id="chatMessages"> | |
| <div class="message ai-message"> | |
| <div class="message-info"> | |
| <span><i class="fas fa-robot"></i> AI Assistant</span> | |
| </div> | |
| <div class="message-content"> | |
| Hello! I'm your AI assistant. How can I help you today? | |
| </div> | |
| </div> | |
| </div> | |
| <div class="chat-input-container"> | |
| <textarea class="chat-textarea" id="chatInput" placeholder="Type your message here..." rows="1"></textarea> | |
| <button class="send-button" id="sendButton"> | |
| <i class="fas fa-paper-plane"></i> | |
| </button> | |
| </div> | |
| </div> | |
| </div> | |
| <footer> | |
| <p>AI Chat Assistant © 2023 | Uses OpenAI API to generate responses</p> | |
| </footer> | |
| <script> | |
| // Configure marked.js | |
| marked.setOptions({ | |
| breaks: true, | |
| gfm: true, | |
| highlight: function(code, lang) { | |
| return code; // No syntax highlighting but keeps the code block | |
| } | |
| }); | |
| // DOM Elements | |
| const apiKeyInput = document.getElementById('apiKeyInput'); | |
| const saveKeyButton = document.getElementById('saveKeyButton'); | |
| const chatInput = document.getElementById('chatInput'); | |
| const sendButton = document.getElementById('sendButton'); | |
| const chatMessages = document.getElementById('chatMessages'); | |
| const modelSelector = document.getElementById('modelSelector'); | |
| const temperatureSlider = document.getElementById('temperatureSlider'); | |
| const temperatureValue = document.getElementById('temperatureValue'); | |
| const maxTokensSlider = document.getElementById('maxTokensSlider'); | |
| const maxTokensValue = document.getElementById('maxTokensValue'); | |
| const clearChatButton = document.getElementById('clearChatButton'); | |
| // Chat history | |
| let conversationHistory = [ | |
| { | |
| role: "assistant", | |
| content: "Hello! I'm your AI assistant. How can I help you today?" | |
| } | |
| ]; | |
| // Load API key from localStorage | |
| if (localStorage.getItem('openai_api_key')) { | |
| apiKeyInput.value = localStorage.getItem('openai_api_key'); | |
| } | |
| // Load settings from localStorage | |
| if (localStorage.getItem('model')) { | |
| modelSelector.value = localStorage.getItem('model'); | |
| } | |
| if (localStorage.getItem('temperature')) { | |
| temperatureSlider.value = localStorage.getItem('temperature'); | |
| temperatureValue.textContent = localStorage.getItem('temperature'); | |
| } | |
| if (localStorage.getItem('max_tokens')) { | |
| maxTokensSlider.value = localStorage.getItem('max_tokens'); | |
| maxTokensValue.textContent = localStorage.getItem('max_tokens'); | |
| } | |
| // Auto-expand textarea as user types | |
| chatInput.addEventListener('input', () => { | |
| chatInput.style.height = 'auto'; | |
| chatInput.style.height = (chatInput.scrollHeight) + 'px'; | |
| }); | |
| // Save API key | |
| saveKeyButton.addEventListener('click', () => { | |
| const apiKey = apiKeyInput.value.trim(); | |
| if (!apiKey) { | |
| alert('Please enter your OpenAI API key'); | |
| return; | |
| } | |
| localStorage.setItem('openai_api_key', apiKey); | |
| showToast('API key saved successfully!', 'success'); | |
| }); | |
| // Update slider values in real time | |
| temperatureSlider.addEventListener('input', () => { | |
| temperatureValue.textContent = temperatureSlider.value; | |
| localStorage.setItem('temperature', temperatureSlider.value); | |
| }); | |
| maxTokensSlider.addEventListener('input', () => { | |
| maxTokensValue.textContent = maxTokensSlider.value; | |
| localStorage.setItem('max_tokens', maxTokensSlider.value); | |
| }); | |
| modelSelector.addEventListener('change', () => { | |
| localStorage.setItem('model', modelSelector.value); | |
| }); | |
| // Clear chat history | |
| clearChatButton.addEventListener('click', () => { | |
| if (confirm('Are you sure you want to clear the chat history?')) { | |
| conversationHistory = [ | |
| { | |
| role: "assistant", | |
| content: "Hello! I'm your AI assistant. How can I help you today?" | |
| } | |
| ]; | |
| chatMessages.innerHTML = ` | |
| <div class="message ai-message"> | |
| <div class="message-info"> | |
| <span><i class="fas fa-robot"></i> AI Assistant</span> | |
| </div> | |
| <div class="message-content"> | |
| Hello! I'm your AI assistant. How can I help you today? | |
| </div> | |
| </div> | |
| `; | |
| } | |
| }); | |
| // Send message function | |
| async function sendMessage() { | |
| const userMessage = chatInput.value.trim(); | |
| if (!userMessage) return; | |
| // Check if API key exists | |
| if (!localStorage.getItem('openai_api_key')) { | |
| showToast('Please enter your OpenAI API key first', 'error'); | |
| return; | |
| } | |
| // Add user message to chat | |
| appendMessage('user', userMessage); | |
| conversationHistory.push({ role: "user", content: userMessage }); | |
| // Clear input | |
| chatInput.value = ''; | |
| chatInput.style.height = 'auto'; | |
| // Show typing indicator | |
| const typingIndicator = document.createElement('div'); | |
| typingIndicator.className = 'typing-indicator'; | |
| typingIndicator.innerHTML = ` | |
| <div class="typing-dot"></div> | |
| <div class="typing-dot"></div> | |
| <div class="typing-dot"></div> | |
| `; | |
| chatMessages.appendChild(typingIndicator); | |
| chatMessages.scrollTop = chatMessages.scrollHeight; | |
| try { | |
| // Call OpenAI API | |
| const response = await fetch('https://api.openai.com/v1/chat/completions', { | |
| method: 'POST', | |
| headers: { | |
| 'Content-Type': 'application/json', | |
| 'Authorization': `Bearer ${localStorage.getItem('openai_api_key')}` | |
| }, | |
| body: JSON.stringify({ | |
| model: modelSelector.value, | |
| messages: conversationHistory, | |
| temperature: parseFloat(temperatureSlider.value), | |
| max_tokens: parseInt(maxTokensSlider.value) | |
| }) | |
| }); | |
| // Remove typing indicator | |
| chatMessages.removeChild(typingIndicator); | |
| if (!response.ok) { | |
| const errorData = await response.json(); | |
| throw new Error(errorData.error?.message || 'Failed to get response from OpenAI'); | |
| } | |
| const data = await response.json(); | |
| const aiResponse = data.choices[0].message.content; | |
| // Add AI response to chat (rendered as Markdown) | |
| appendMessage('assistant', aiResponse); | |
| conversationHistory.push({ role: "assistant", content: aiResponse }); | |
| } catch (error) { | |
| chatMessages.removeChild(typingIndicator); | |
| console.error('Error:', error); | |
| showToast(error.message, 'error'); | |
| appendMessage('assistant', `Sorry, I'm having trouble responding. ${error.message}`); | |
| } | |
| } | |
| // Append message to chat with Markdown rendering | |
| function appendMessage(role, content) { | |
| const messageDiv = document.createElement('div'); | |
| messageDiv.className = `message ${role === 'user' ? 'user-message' : 'ai-message'}`; | |
| const icon = role === 'user' ? 'fa-user' : 'fa-robot'; | |
| const name = role === 'user' ? 'You' : 'AI Assistant'; | |
| messageDiv.innerHTML = ` | |
| <div class="message-info"> | |
| <span><i class="fas ${icon}"></i> ${name}</span> | |
| </div> | |
| <div class="message-content"> | |
| ${role === 'assistant' ? marked.parse(content) : escapeHtml(content)} | |
| </div> | |
| `; | |
| chatMessages.appendChild(messageDiv); | |
| chatMessages.scrollTop = chatMessages.scrollHeight; | |
| } | |
| // Simple HTML escape function for user messages | |
| function escapeHtml(unsafe) { | |
| return unsafe | |
| .replace(/&/g, "&") | |
| .replace(/</g, "<") | |
| .replace(/>/g, ">") | |
| .replace(/"/g, """) | |
| .replace(/'/g, "'"); | |
| } | |
| // Show toast notification | |
| function showToast(message, type) { | |
| const toast = document.createElement('div'); | |
| toast.className = `toast ${type}`; | |
| toast.textContent = message; | |
| document.body.appendChild(toast); | |
| setTimeout(() => { | |
| toast.classList.add('show'); | |
| }, 10); | |
| setTimeout(() => { | |
| toast.classList.remove('show'); | |
| setTimeout(() => { | |
| document.body.removeChild(toast); | |
| }, 300); | |
| }, 3000); | |
| } | |
| // Event listeners for sending messages | |
| sendButton.addEventListener('click', sendMessage); | |
| chatInput.addEventListener('keydown', (e) => { | |
| if (e.key === 'Enter' && !e.shiftKey) { | |
| e.preventDefault(); | |
| sendMessage(); | |
| } | |
| }); | |
| // Add toast styles | |
| const toastStyles = document.createElement('style'); | |
| toastStyles.textContent = ` | |
| .toast { | |
| position: fixed; | |
| bottom: 30px; | |
| left: 50%; | |
| transform: translateX(-50%); | |
| padding: 12px 24px; | |
| border-radius: 6px; | |
| color: white; | |
| font-size: 0.9rem; | |
| opacity: 0; | |
| transition: opacity 0.3s ease; | |
| z-index: 1000; | |
| } | |
| .toast.show { | |
| opacity: 1; | |
| } | |
| .toast.success { | |
| background-color: var(--success-color); | |
| } | |
| .toast.error { | |
| background-color: var(--error-color); | |
| } | |
| .toast.warning { | |
| background-color: var(--warning-color); | |
| color: var(--dark-color); | |
| } | |
| `; | |
| document.head.appendChild(toastStyles); | |
| </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 <a href="https://enzostvs-deepsite.hf.space" style="color: #fff;" target="_blank" >DeepSite</a> <img src="https://enzostvs-deepsite.hf.space/logo.svg" alt="DeepSite Logo" style="width: 16px; height: 16px; vertical-align: middle;"></p></body> | |
| </html> |