| <!DOCTYPE html> |
| <html lang="en"> |
| <head> |
| <meta charset="UTF-8"> |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> |
| <title>Professional AI Chat Interface</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> |
| .message-fade { |
| animation: fadeIn 0.3s ease-in-out; |
| } |
| @keyframes fadeIn { |
| from { opacity: 0; transform: translateY(10px); } |
| to { opacity: 1; transform: translateY(0); } |
| } |
| .typing-indicator::after { |
| content: "..."; |
| animation: typing 1.5s infinite; |
| } |
| @keyframes typing { |
| 0% { content: "."; } |
| 33% { content: ".."; } |
| 66% { content: "..."; } |
| } |
| .code-block { |
| position: relative; |
| } |
| .code-toolbar { |
| position: absolute; |
| top: 8px; |
| right: 8px; |
| opacity: 0; |
| transition: opacity 0.2s; |
| } |
| .code-block:hover .code-toolbar { |
| opacity: 1; |
| } |
| .tooltip { |
| position: relative; |
| } |
| .tooltip:hover::after { |
| content: attr(data-tooltip); |
| position: absolute; |
| bottom: 100%; |
| left: 50%; |
| transform: translateX(-50%); |
| background: #333; |
| color: white; |
| padding: 4px 8px; |
| border-radius: 4px; |
| font-size: 12px; |
| white-space: nowrap; |
| z-index: 10; |
| } |
| .quick-command { |
| transition: all 0.2s; |
| } |
| .quick-command:hover { |
| transform: translateY(-1px); |
| } |
| </style> |
| </head> |
| <body class="bg-gray-100 font-sans"> |
| <div class="flex flex-col h-screen"> |
| |
| <header class="bg-indigo-700 text-white p-4 shadow-md"> |
| <div class="container mx-auto flex justify-between items-center"> |
| <div class="flex items-center space-x-4"> |
| <h1 class="text-2xl font-bold">AI Chat Professional</h1> |
| <select id="api-provider" class="text-sm border border-gray-300 rounded px-2 py-1 bg-white"> |
| <option value="openai">OpenAI</option> |
| <option value="anthropic">Anthropic</option> |
| <option value="aliyun">Aliyun</option> |
| </select> |
| <select id="chat-mode" class="text-sm border border-gray-300 rounded px-2 py-1 bg-white"> |
| <option value="general">General</option> |
| <option value="technical">Technical</option> |
| <option value="creative">Creative</option> |
| <option value="academic">Academic</option> |
| <option value="business">Business</option> |
| </select> |
| </div> |
| <div class="flex items-center space-x-4"> |
| <button id="settings-btn" class="p-2 rounded-full hover:bg-indigo-600 transition"> |
| <i class="fas fa-cog"></i> |
| </button> |
| <button id="history-btn" class="p-2 rounded-full hover:bg-indigo-600 transition"> |
| <i class="fas fa-history"></i> |
| </button> |
| </div> |
| </div> |
| </header> |
|
|
| |
| <main class="flex-1 flex flex-col md:flex-row container mx-auto p-4 gap-4 overflow-hidden"> |
| |
| <div id="settings-sidebar" class="hidden md:block w-full md:w-80 bg-white rounded-lg shadow-md p-4 overflow-y-auto"> |
| <h2 class="text-xl font-semibold mb-4 text-gray-800">Model Configuration</h2> |
| |
| <div class="mb-6"> |
| <label class="block text-gray-700 mb-2">API Key</label> |
| <input type="password" id="api-key" value="sk-5c6d9121897544829cd0213db3d6749c" |
| class="w-full p-2 border border-gray-300 rounded focus:ring-2 focus:ring-indigo-500 focus:border-indigo-500"> |
| </div> |
| |
| <div class="mb-6"> |
| <label class="block text-gray-700 mb-2">Model Behavior</label> |
| <textarea id="system-prompt" rows="4" |
| class="w-full p-2 border border-gray-300 rounded focus:ring-2 focus:ring-indigo-500 focus:border-indigo-500" |
| placeholder="You are a helpful AI assistant...">You are an expert AI assistant specialized in programming and technical topics. Provide clear, concise answers with code examples when appropriate. Format code blocks properly and explain complex concepts in simple terms.</textarea> |
| </div> |
| |
| <div class="mb-6"> |
| <label class="block text-gray-700 mb-2">Temperature</label> |
| <input type="range" id="temperature" min="0" max="1" step="0.1" value="0.7" |
| class="w-full h-2 bg-gray-200 rounded-lg appearance-none cursor-pointer"> |
| <div class="flex justify-between text-xs text-gray-500 mt-1"> |
| <span>Precise</span> |
| <span>Balanced</span> |
| <span>Creative</span> |
| </div> |
| </div> |
| |
| <div class="mb-6"> |
| <label class="block text-gray-700 mb-2">Max Tokens</label> |
| <input type="number" id="max-tokens" min="100" max="4000" value="2000" |
| class="w-full p-2 border border-gray-300 rounded focus:ring-2 focus:ring-indigo-500 focus:border-indigo-500"> |
| </div> |
|
|
| <div class="mb-6"> |
| <label class="block text-gray-700 mb-2">Response Style</label> |
| <select id="response-style" class="w-full p-2 border border-gray-300 rounded focus:ring-2 focus:ring-indigo-500 focus:border-indigo-500"> |
| <option value="professional">Professional</option> |
| <option value="friendly">Friendly</option> |
| <option value="concise">Concise</option> |
| <option value="detailed">Detailed</option> |
| <option value="creative">Creative</option> |
| </select> |
| </div> |
|
|
| <div class="mb-6"> |
| <label class="flex items-center space-x-2 cursor-pointer"> |
| <input type="checkbox" id="auto-continue" class="rounded text-indigo-600 focus:ring-indigo-500"> |
| <span class="text-gray-700">Auto-continue long responses</span> |
| </label> |
| </div> |
|
|
| <div class="mb-6"> |
| <label class="flex items-center space-x-2 cursor-pointer"> |
| <input type="checkbox" id="web-search" class="rounded text-indigo-600 focus:ring-indigo-500"> |
| <span class="text-gray-700">Enable web search for answers</span> |
| </label> |
| </div> |
| |
| <div class="mb-6"> |
| <h3 class="text-lg font-semibold mb-2 text-gray-800">Quick Commands</h3> |
| <div class="grid grid-cols-2 gap-2"> |
| <button class="quick-command bg-gray-200 hover:bg-gray-300 text-gray-800 py-1 px-2 rounded text-sm" data-command="/summarize"> |
| Summarize |
| </button> |
| <button class="quick-command bg-gray-200 hover:bg-gray-300 text-gray-800 py-1 px-2 rounded text-sm" data-command="/translate"> |
| Translate |
| </button> |
| <button class="quick-command bg-gray-200 hover:bg-gray-300 text-gray-800 py-1 px-2 rounded text-sm" data-command="/explain"> |
| Explain |
| </button> |
| <button class="quick-command bg-gray-200 hover:bg-gray-300 text-gray-800 py-1 px-2 rounded text-sm" data-command="/code"> |
| Generate Code |
| </button> |
| </div> |
| </div> |
|
|
| <button id="save-settings" class="w-full bg-indigo-600 text-white py-2 px-4 rounded hover:bg-indigo-700 transition"> |
| Save Configuration |
| </button> |
| </div> |
| |
| |
| <div class="flex-1 flex flex-col bg-white rounded-lg shadow-md overflow-hidden"> |
| |
| <div id="messages-container" class="flex-1 overflow-y-auto p-4 space-y-4"> |
| <div class="text-center text-gray-500 py-8"> |
| <i class="fas fa-robot text-4xl mb-2 text-indigo-500"></i> |
| <p>How can I help you today?</p> |
| </div> |
| </div> |
| |
| |
| <div class="border-t border-gray-200 p-4 bg-gray-50"> |
| <div class="flex items-end space-x-2 mb-2"> |
| <div class="flex space-x-1"> |
| <button id="bold-btn" class="p-2 text-gray-500 hover:text-indigo-600 tooltip" data-tooltip="Bold"> |
| <i class="fas fa-bold"></i> |
| </button> |
| <button id="italic-btn" class="p-2 text-gray-500 hover:text-indigo-600 tooltip" data-tooltip="Italic"> |
| <i class="fas fa-italic"></i> |
| </button> |
| <button id="list-btn" class="p-2 text-gray-500 hover:text-indigo-600 tooltip" data-tooltip="List"> |
| <i class="fas fa-list-ul"></i> |
| </button> |
| <button id="code-btn" class="p-2 text-gray-500 hover:text-indigo-600 tooltip" data-tooltip="Code Block"> |
| <i class="fas fa-code"></i> |
| </button> |
| <button id="attach-btn" class="p-2 text-gray-500 hover:text-indigo-600 tooltip" data-tooltip="Attach File"> |
| <i class="fas fa-paperclip"></i> |
| </button> |
| <button id="image-btn" class="p-2 text-gray-500 hover:text-indigo-600 tooltip" data-tooltip="Insert Image"> |
| <i class="fas fa-image"></i> |
| </button> |
| </div> |
| </div> |
| <div class="flex space-x-2"> |
| <textarea id="message-input" rows="2" |
| class="flex-1 p-3 border border-gray-300 rounded-lg focus:ring-2 focus:ring-indigo-500 focus:border-indigo-500 resize-none" |
| placeholder="Type your message here..."></textarea> |
| <button id="send-btn" class="bg-indigo-600 text-white p-3 rounded-lg hover:bg-indigo-700 transition self-end"> |
| <i class="fas fa-paper-plane"></i> |
| </button> |
| </div> |
| <div class="flex justify-between items-center mt-2 text-xs text-gray-500"> |
| <div id="typing-indicator" class="typing-indicator hidden">AI is typing</div> |
| <div> |
| <span id="char-count">0</span>/1000 |
| </div> |
| </div> |
| </div> |
| </div> |
| </main> |
| </div> |
|
|
| |
| |
| <div id="history-modal" class="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50 hidden"> |
| <div class="bg-white rounded-lg shadow-xl w-full max-w-2xl max-h-[80vh] overflow-hidden"> |
| <div class="flex justify-between items-center p-4 border-b"> |
| <h3 class="text-lg font-semibold">Chat History</h3> |
| <button id="close-history" class="text-gray-500 hover:text-gray-700"> |
| <i class="fas fa-times"></i> |
| </button> |
| </div> |
| <div id="history-list" class="overflow-y-auto p-4 space-y-2"> |
| |
| </div> |
| <div class="p-4 border-t flex justify-end"> |
| <button id="clear-history" class="text-red-500 hover:text-red-700"> |
| <i class="fas fa-trash mr-1"></i> Clear History |
| </button> |
| </div> |
| </div> |
| </div> |
|
|
| |
| <div id="code-save-modal" class="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50 hidden"> |
| <div class="bg-white rounded-lg shadow-xl w-full max-w-md p-6"> |
| <div class="flex justify-between items-center mb-4"> |
| <h3 class="text-lg font-semibold">Save Code Snippet</h3> |
| <button id="close-code-save" class="text-gray-500 hover:text-gray-700"> |
| <i class="fas fa-times"></i> |
| </button> |
| </div> |
| <div class="mb-4"> |
| <label class="block text-gray-700 mb-2">Title</label> |
| <input type="text" id="code-title" class="w-full p-2 border border-gray-300 rounded"> |
| </div> |
| <div class="mb-4"> |
| <label class="block text-gray-700 mb-2">Language</label> |
| <select id="code-language" class="w-full p-2 border border-gray-300 rounded"> |
| <option value="javascript">JavaScript</option> |
| <option value="html">HTML</option> |
| <option value="css">CSS</option> |
| <option value="python">Python</option> |
| <option value="java">Java</option> |
| <option value="php">PHP</option> |
| <option value="csharp">C#</option> |
| <option value="cpp">C++</option> |
| </select> |
| </div> |
| <div class="flex justify-end space-x-2"> |
| <button id="cancel-code-save" class="px-4 py-2 border border-gray-300 rounded hover:bg-gray-100"> |
| Cancel |
| </button> |
| <button id="confirm-code-save" class="px-4 py-2 bg-indigo-600 text-white rounded hover:bg-indigo-700"> |
| Save |
| </button> |
| </div> |
| </div> |
| </div> |
|
|
| |
| <div id="saved-codes-modal" class="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50 hidden"> |
| <div class="bg-white rounded-lg shadow-xl w-full max-w-4xl max-h-[80vh] overflow-hidden"> |
| <div class="flex justify-between items-center p-4 border-b"> |
| <h3 class="text-lg font-semibold">Saved Code Snippets</h3> |
| <button id="close-saved-codes" class="text-gray-500 hover:text-gray-700"> |
| <i class="fas fa-times"></i> |
| </button> |
| </div> |
| <div id="saved-codes-list" class="overflow-y-auto p-4 grid grid-cols-1 md:grid-cols-2 gap-4"> |
| |
| </div> |
| </div> |
| </div> |
|
|
| <script> |
| document.addEventListener('DOMContentLoaded', function() { |
| |
| const messagesContainer = document.getElementById('messages-container'); |
| const messageInput = document.getElementById('message-input'); |
| const sendBtn = document.getElementById('send-btn'); |
| const apiKeyInput = document.getElementById('api-key'); |
| const systemPrompt = document.getElementById('system-prompt'); |
| const temperature = document.getElementById('temperature'); |
| const maxTokens = document.getElementById('max-tokens'); |
| const saveSettingsBtn = document.getElementById('save-settings'); |
| const settingsSidebar = document.getElementById('settings-sidebar'); |
| const settingsBtn = document.getElementById('settings-btn'); |
| const historyBtn = document.getElementById('history-btn'); |
| const historyModal = document.getElementById('history-modal'); |
| const closeHistory = document.getElementById('close-history'); |
| const historyList = document.getElementById('history-list'); |
| const clearHistory = document.getElementById('clear-history'); |
| const typingIndicator = document.getElementById('typing-indicator'); |
| const charCount = document.getElementById('char-count'); |
| const codeBtn = document.getElementById('code-btn'); |
| const codeSaveModal = document.getElementById('code-save-modal'); |
| const closeCodeSave = document.getElementById('close-code-save'); |
| const cancelCodeSave = document.getElementById('cancel-code-save'); |
| const confirmCodeSave = document.getElementById('confirm-code-save'); |
| const savedCodesModal = document.getElementById('saved-codes-modal'); |
| const closeSavedCodes = document.getElementById('close-saved-codes'); |
| const savedCodesList = document.getElementById('saved-codes-list'); |
| |
| |
| let chatHistory = JSON.parse(localStorage.getItem('chatHistory')) || []; |
| let savedCodes = JSON.parse(localStorage.getItem('savedCodes')) || []; |
| let currentCodeToSave = null; |
| let isWaitingForResponse = false; |
| |
| |
| updateCharCount(); |
| renderSavedCodes(); |
| |
| |
| messageInput.addEventListener('input', updateCharCount); |
| messageInput.addEventListener('keydown', function(e) { |
| if (e.key === 'Enter' && !e.shiftKey) { |
| e.preventDefault(); |
| sendMessage(); |
| } |
| }); |
| |
| sendBtn.addEventListener('click', sendMessage); |
| saveSettingsBtn.addEventListener('click', saveSettings); |
| settingsBtn.addEventListener('click', toggleSettings); |
| historyBtn.addEventListener('click', showHistoryModal); |
| closeHistory.addEventListener('click', hideHistoryModal); |
| clearHistory.addEventListener('click', clearChatHistory); |
| codeBtn.addEventListener('click', showSavedCodesModal); |
| closeCodeSave.addEventListener('click', hideCodeSaveModal); |
| cancelCodeSave.addEventListener('click', hideCodeSaveModal); |
| confirmCodeSave.addEventListener('click', saveCurrentCode); |
| closeSavedCodes.addEventListener('click', hideSavedCodesModal); |
| |
| |
| function updateCharCount() { |
| const count = messageInput.value.length; |
| charCount.textContent = count; |
| if (count > 1000) { |
| charCount.classList.add('text-red-500'); |
| } else { |
| charCount.classList.remove('text-red-500'); |
| } |
| } |
| |
| function toggleSettings() { |
| settingsSidebar.classList.toggle('hidden'); |
| } |
| |
| function saveSettings() { |
| localStorage.setItem('apiKey', apiKeyInput.value); |
| localStorage.setItem('systemPrompt', systemPrompt.value); |
| localStorage.setItem('temperature', temperature.value); |
| localStorage.setItem('maxTokens', maxTokens.value); |
| |
| |
| const confirmation = document.createElement('div'); |
| confirmation.className = 'fixed bottom-4 right-4 bg-green-500 text-white px-4 py-2 rounded shadow-lg'; |
| confirmation.textContent = 'Settings saved successfully!'; |
| document.body.appendChild(confirmation); |
| |
| setTimeout(() => { |
| confirmation.remove(); |
| }, 3000); |
| } |
| |
| async function getApiEndpoint(modelType) { |
| const modelMap = { |
| 'general': 'gpt-3.5-turbo', |
| 'technical': 'gpt-4', |
| 'creative': 'gpt-4-turbo', |
| 'academic': 'claude-3', |
| 'business': 'gpt-4', |
| 'vision': 'gpt-4-vision' |
| }; |
| |
| const baseUrls = { |
| 'openai': 'https://api.openai.com/v1/chat/completions', |
| 'anthropic': 'https://api.anthropic.com/v1/messages', |
| 'aliyun': 'https://dashscope-intl.aliyuncs.com/compatible-mode/v1/chat/completions' |
| }; |
| |
| const model = modelMap[modelType] || 'gpt-3.5-turbo'; |
| |
| if (model.includes('gpt')) return { url: baseUrls.openai, model }; |
| if (model.includes('claude')) return { url: baseUrls.anthropic, model }; |
| return { url: baseUrls.aliyun, model }; |
| } |
| |
| async function sendMessage() { |
| const message = messageInput.value.trim(); |
| if (!message || isWaitingForResponse) return; |
| |
| |
| addMessage('user', message); |
| messageInput.value = ''; |
| updateCharCount(); |
| |
| |
| isWaitingForResponse = true; |
| typingIndicator.classList.remove('hidden'); |
| |
| try { |
| const chatMode = document.getElementById('chat-mode').value; |
| const { url, model } = await getApiEndpoint(chatMode); |
| |
| const response = await fetch(url, { |
| method: 'POST', |
| headers: { |
| 'Content-Type': 'application/json', |
| 'Authorization': `Bearer ${apiKeyInput.value || 'sk-5c6d9121897544829cd0213db3d6749c'}` |
| }, |
| body: JSON.stringify({ |
| model: model, |
| messages: [ |
| { |
| role: "system", |
| content: systemPrompt.value || "You are a helpful AI assistant." |
| }, |
| ...chatHistory.slice(-6).map(msg => ({ |
| role: msg.role, |
| content: msg.content |
| })), |
| { |
| role: "user", |
| content: message |
| } |
| ], |
| temperature: parseFloat(temperature.value) || 0.7, |
| max_tokens: parseInt(maxTokens.value) || 2000, |
| stream: true |
| }), |
| headers: { |
| 'Content-Type': 'application/json', |
| 'Authorization': `Bearer ${apiKeyInput.value || 'sk-5c6d9121897544829cd0213db3d6749c'}`, |
| 'Accept': 'application/json' |
| } |
| }); |
| |
| if (!response.ok) { |
| throw new Error(`API request failed with status ${response.status}`); |
| } |
| |
| if (!response.ok) { |
| const errorData = await response.json(); |
| throw new Error(errorData.error?.message || `API request failed with status ${response.status}`); |
| } |
| |
| |
| const reader = response.body.getReader(); |
| const decoder = new TextDecoder(); |
| let aiMessage = ''; |
| let messageDiv = document.createElement('div'); |
| messageDiv.className = 'message-fade flex justify-start'; |
| let bubble = document.createElement('div'); |
| bubble.className = 'max-w-[80%] rounded-lg p-4 bg-gray-200 text-gray-800'; |
| messageDiv.appendChild(bubble); |
| messagesContainer.appendChild(messageDiv); |
| |
| while (true) { |
| const { done, value } = await reader.read(); |
| if (done) break; |
| |
| const chunk = decoder.decode(value); |
| const lines = chunk.split('\n').filter(line => line.trim() !== ''); |
| |
| for (const line of lines) { |
| if (line.startsWith('data:') && !line.includes('[DONE]')) { |
| try { |
| const data = JSON.parse(line.substring(5)); |
| const content = data.choices[0]?.delta?.content || ''; |
| aiMessage += content; |
| bubble.innerHTML = formatMessageContent(aiMessage); |
| messagesContainer.scrollTop = messagesContainer.scrollHeight; |
| } catch (e) { |
| console.error('Error parsing stream chunk:', e); |
| } |
| } |
| } |
| } |
| |
| |
| chatHistory.push( |
| { role: 'user', content: message }, |
| { role: 'assistant', content: aiMessage } |
| ); |
| |
| |
| localStorage.setItem('chatHistory', JSON.stringify(chatHistory)); |
| |
| } catch (error) { |
| console.error('Error:', error); |
| addMessage('assistant', `Sorry, I encountered an error: ${error.message}`); |
| } finally { |
| |
| typingIndicator.classList.add('hidden'); |
| isWaitingForResponse = false; |
| } |
| } |
| |
| function formatMessageContent(content) { |
| |
| let formattedContent = content.replace(/```(\w*)\n([\s\S]*?)\n```/g, |
| (match, lang, code) => { |
| return `<div class="code-block bg-gray-800 text-gray-100 p-4 rounded my-2 overflow-x-auto relative"> |
| <pre><code class="language-${lang || 'text'}">${code.trim()}</code></pre> |
| <div class="code-toolbar flex space-x-2"> |
| <button class="copy-code bg-gray-700 hover:bg-gray-600 text-white p-1 rounded" title="Copy"> |
| <i class="fas fa-copy text-xs"></i> |
| </button> |
| <button class="save-code bg-gray-700 hover:bg-gray-600 text-white p-1 rounded" title="Save"> |
| <i class="fas fa-save text-xs"></i> |
| </button> |
| </div> |
| </div>`; |
| }); |
| |
| |
| formattedContent = formattedContent.replace(/`([^`]+)`/g, '<code class="bg-gray-200 text-gray-800 px-1 rounded">$1</code>'); |
| |
| |
| formattedContent = formattedContent.replace(/(https?:\/\/[^\s]+)/g, '<a href="$1" target="_blank" class="text-indigo-600 hover:underline">$1</a>'); |
| |
| return formattedContent; |
| } |
| |
| function addMessage(role, content) { |
| const messageDiv = document.createElement('div'); |
| messageDiv.className = `message-fade flex ${role === 'user' ? 'justify-end' : 'justify-start'}`; |
| |
| const bubble = document.createElement('div'); |
| bubble.className = `max-w-[80%] rounded-lg p-4 ${role === 'user' ? 'bg-indigo-600 text-white' : 'bg-gray-200 text-gray-800'}`; |
| |
| |
| let formattedContent = content; |
| |
| |
| formattedContent = formattedContent.replace(/```(\w*)\n([\s\S]*?)\n```/g, |
| (match, lang, code) => { |
| return `<div class="code-block bg-gray-800 text-gray-100 p-4 rounded my-2 overflow-x-auto relative"> |
| <pre><code class="language-${lang || 'text'}">${code.trim()}</code></pre> |
| <div class="code-toolbar flex space-x-2"> |
| <button class="copy-code bg-gray-700 hover:bg-gray-600 text-white p-1 rounded" title="Copy"> |
| <i class="fas fa-copy text-xs"></i> |
| </button> |
| <button class="save-code bg-gray-700 hover:bg-gray-600 text-white p-1 rounded" title="Save"> |
| <i class="fas fa-save text-xs"></i> |
| </button> |
| </div> |
| </div>`; |
| }); |
| |
| |
| formattedContent = formattedContent.replace(/`([^`]+)`/g, '<code class="bg-gray-200 text-gray-800 px-1 rounded">$1</code>'); |
| |
| bubble.innerHTML = formattedContent; |
| messageDiv.appendChild(bubble); |
| |
| |
| messagesContainer.appendChild(messageDiv); |
| |
| |
| messagesContainer.scrollTop = messagesContainer.scrollHeight; |
| |
| |
| setTimeout(() => { |
| document.querySelectorAll('.copy-code').forEach(btn => { |
| btn.addEventListener('click', function() { |
| const codeBlock = this.closest('.code-block').querySelector('code'); |
| navigator.clipboard.writeText(codeBlock.textContent); |
| |
| |
| const notification = document.createElement('div'); |
| notification.className = 'absolute top-2 right-2 bg-green-500 text-white px-2 py-1 rounded text-xs'; |
| notification.textContent = 'Copied!'; |
| this.parentNode.appendChild(notification); |
| |
| setTimeout(() => { |
| notification.remove(); |
| }, 2000); |
| }); |
| }); |
| |
| document.querySelectorAll('.save-code').forEach(btn => { |
| btn.addEventListener('click', function() { |
| const codeBlock = this.closest('.code-block'); |
| const code = codeBlock.querySelector('code').textContent; |
| const lang = codeBlock.querySelector('code').className.replace('language-', '') || 'text'; |
| |
| currentCodeToSave = { code, lang }; |
| showCodeSaveModal(); |
| }); |
| }); |
| }, 100); |
| } |
| |
| function showHistoryModal() { |
| historyList.innerHTML = ''; |
| |
| if (chatHistory.length === 0) { |
| historyList.innerHTML = '<p class="text-gray-500 text-center py-4">No chat history available</p>'; |
| } else { |
| chatHistory.forEach((msg, index) => { |
| if (msg.role === 'user') { |
| const historyItem = document.createElement('div'); |
| historyItem.className = 'p-3 bg-gray-100 rounded mb-2 cursor-pointer hover:bg-gray-200'; |
| historyItem.textContent = msg.content; |
| historyItem.addEventListener('click', () => { |
| |
| |
| }); |
| historyList.appendChild(historyItem); |
| } |
| }); |
| } |
| |
| historyModal.classList.remove('hidden'); |
| } |
| |
| function hideHistoryModal() { |
| historyModal.classList.add('hidden'); |
| } |
| |
| function clearChatHistory() { |
| chatHistory = []; |
| localStorage.removeItem('chatHistory'); |
| hideHistoryModal(); |
| |
| |
| messagesContainer.innerHTML = ` |
| <div class="text-center text-gray-500 py-8"> |
| <i class="fas fa-robot text-4xl mb-2 text-indigo-500"></i> |
| <p>How can I help you today?</p> |
| </div> |
| `; |
| } |
| |
| function showCodeSaveModal() { |
| if (!currentCodeToSave) return; |
| codeSaveModal.classList.remove('hidden'); |
| } |
| |
| function hideCodeSaveModal() { |
| codeSaveModal.classList.add('hidden'); |
| } |
| |
| function saveCurrentCode() { |
| if (!currentCodeToSave) return; |
| |
| const title = document.getElementById('code-title').value.trim() || 'Untitled Code'; |
| const language = document.getElementById('code-language').value; |
| |
| savedCodes.push({ |
| title, |
| language, |
| code: currentCodeToSave.code, |
| timestamp: new Date().toISOString() |
| }); |
| |
| localStorage.setItem('savedCodes', JSON.stringify(savedCodes)); |
| renderSavedCodes(); |
| |
| |
| document.getElementById('code-title').value = ''; |
| document.getElementById('code-language').value = 'javascript'; |
| currentCodeToSave = null; |
| |
| hideCodeSaveModal(); |
| |
| |
| const confirmation = document.createElement('div'); |
| confirmation.className = 'fixed bottom-4 right-4 bg-green-500 text-white px-4 py-2 rounded shadow-lg'; |
| confirmation.textContent = 'Code saved successfully!'; |
| document.body.appendChild(confirmation); |
| |
| setTimeout(() => { |
| confirmation.remove(); |
| }, 3000); |
| } |
| |
| function showSavedCodesModal() { |
| renderSavedCodes(); |
| savedCodesModal.classList.remove('hidden'); |
| } |
| |
| function hideSavedCodesModal() { |
| savedCodesModal.classList.add('hidden'); |
| } |
| |
| function renderSavedCodes() { |
| savedCodesList.innerHTML = ''; |
| |
| if (savedCodes.length === 0) { |
| savedCodesList.innerHTML = '<p class="text-gray-500 text-center col-span-2 py-8">No saved code snippets</p>'; |
| } else { |
| savedCodes.forEach((snippet, index) => { |
| const snippetDiv = document.createElement('div'); |
| snippetDiv.className = 'bg-gray-100 rounded-lg p-4 border border-gray-200'; |
| |
| snippetDiv.innerHTML = ` |
| <div class="flex justify-between items-start mb-2"> |
| <h4 class="font-medium text-gray-800">${snippet.title}</h4> |
| <span class="text-xs bg-indigo-100 text-indigo-800 px-2 py-1 rounded">${snippet.language}</span> |
| </div> |
| <pre class="bg-gray-800 text-gray-100 text-xs p-3 rounded overflow-x-auto max-h-40">${snippet.code}</pre> |
| <div class="flex justify-between items-center mt-2 text-xs text-gray-500"> |
| <span>${new Date(snippet.timestamp).toLocaleString()}</span> |
| <div class="space-x-2"> |
| <button class="copy-snippet text-gray-500 hover:text-indigo-600" data-index="${index}"> |
| <i class="fas fa-copy"></i> |
| </button> |
| <button class="delete-snippet text-gray-500 hover:text-red-600" data-index="${index}"> |
| <i class="fas fa-trash"></i> |
| </button> |
| </div> |
| </div> |
| `; |
| |
| savedCodesList.appendChild(snippetDiv); |
| }); |
| |
| |
| document.querySelectorAll('.copy-snippet').forEach(btn => { |
| btn.addEventListener('click', function() { |
| const index = parseInt(this.getAttribute('data-index')); |
| navigator.clipboard.writeText(savedCodes[index].code); |
| |
| |
| const notification = document.createElement('div'); |
| notification.className = 'fixed bottom-4 right-4 bg-green-500 text-white px-4 py-2 rounded shadow-lg'; |
| notification.textContent = 'Code copied to clipboard!'; |
| document.body.appendChild(notification); |
| |
| setTimeout(() => { |
| notification.remove(); |
| }, 3000); |
| }); |
| }); |
| |
| document.querySelectorAll('.delete-snippet').forEach(btn => { |
| btn.addEventListener('click', function() { |
| const index = parseInt(this.getAttribute('data-index')); |
| if (confirm('Are you sure you want to delete this code snippet?')) { |
| savedCodes.splice(index, 1); |
| localStorage.setItem('savedCodes', JSON.stringify(savedCodes)); |
| renderSavedCodes(); |
| } |
| }); |
| }); |
| } |
| } |
| }); |
| </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=fahad509/f" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body> |
| </html> |