| |
| let chatHistory = []; |
| let conversationId = 1; |
| let currentFile = null; |
| let files = { |
| 'main.py': '# Main application file\nprint("Hello, World!")', |
| 'app.py': '# Flask application\nfrom flask import Flask\n\napp = Flask(__name__)\n\n@app.route("/")\ndef index():\n return "Hello from Flask!"\n\nif __name__ == "__main__":\n app.run(debug=True)', |
| 'style.css': '/* Main styles */\nbody {\n font-family: Arial, sans-serif;\n background-color: #f0f0f0;\n}', |
| 'script.js': '// Main script\nconsole.log("Hello from JavaScript!");', |
| 'header.py': '# Header component\n\ndef render_header():\n return "<header>Header Content</header>"', |
| 'footer.py': '# Footer component\n\ndef render_footer():\n return "<footer>Footer Content</footer>"', |
| 'README.md': '# My Project\nThis is a sample project.', |
| 'requirements.txt': 'flask==2.3.3\nopenai==1.10.0' |
| }; |
|
|
| |
| const chatContainer = document.getElementById('chat-container'); |
| const userInput = document.getElementById('user-input'); |
| const sendButton = document.getElementById('send-button'); |
| const welcomeScreen = document.getElementById('welcome-screen'); |
| const hamburgerBtn = document.getElementById('hamburger-btn'); |
| const closeSidebarBtn = document.getElementById('close-sidebar-btn'); |
| const sidebar = document.getElementById('sidebar'); |
| const newChatBtn = document.getElementById('new-chat-btn'); |
| const floatingNewChatBtn = document.getElementById('floating-new-chat'); |
| const suggestionCards = document.querySelectorAll('.suggestion-card'); |
| const modeSelector = document.getElementById('mode-selector'); |
| const modelSelector = document.getElementById('model-selector'); |
| const tabs = document.querySelectorAll('.tab'); |
| const tabContents = document.querySelectorAll('.tab-content'); |
| const fileTree = document.getElementById('file-tree'); |
| const terminalOutput = document.getElementById('terminal-output'); |
| const terminalInput = document.getElementById('terminal-input'); |
| const clearTerminalBtn = document.getElementById('clear-terminal-btn'); |
| const runCodeBtn = document.getElementById('run-code-btn'); |
| const previewFrame = document.getElementById('preview-frame'); |
| const refreshPreviewBtn = document.getElementById('refresh-preview-btn'); |
| const openPreviewBtn = document.getElementById('open-preview-btn'); |
| const newFileBtn = document.getElementById('new-file-btn'); |
| const newFolderBtn = document.getElementById('new-folder-btn'); |
| const sidebarBackdrop = document.querySelector('.sidebar-backdrop'); |
|
|
| |
| function addMessageToUI(role, content) { |
| |
| if (chatContainer.children.length === 1) { |
| welcomeScreen.style.display = 'none'; |
| } |
|
|
| const messageDiv = document.createElement('div'); |
| messageDiv.classList.add('message'); |
| messageDiv.classList.add(role === 'user' ? 'user-message' : 'assistant-message'); |
|
|
| const timeString = new Date().toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' }); |
| const avatarClass = role === 'user' ? 'user-avatar' : 'assistant-avatar'; |
| const avatarText = role === 'user' ? 'U' : 'AI'; |
| const sender = role === 'user' ? 'You' : 'VicoXDarkGPT'; |
|
|
| messageDiv.innerHTML = ` |
| <div class="message-header"> |
| <div class="avatar ${avatarClass}">${avatarText}</div> |
| <div>${sender} • ${timeString}</div> |
| </div> |
| <div class="message-content">${formatMessageContent(content)}</div> |
| `; |
|
|
| chatContainer.appendChild(messageDiv); |
| chatContainer.scrollTop = chatContainer.scrollHeight; |
| } |
|
|
| |
| function formatMessageContent(content) { |
| let formattedContent = content; |
| |
| formattedContent = formattedContent.replace(/```([\s\S]*?)```/g, '<div class="code-container"><pre><code>$1</code></pre></div>'); |
| |
| formattedContent = formattedContent.replace(/(🎯|📋|🛠️|📁|💡|🏗️|📝|🔍)/g, '<strong>$1</strong>'); |
| formattedContent = formattedContent.replace(/(Step \d+:?|Step \d\.)/gi, '<div class="plan-step">$1</div>'); |
| formattedContent = formattedContent.replace(/(ANALYZE:|PLAN:|IMPLEMENT:|EXPLAIN:)/gi, '<strong>$1</strong>'); |
| return formattedContent; |
| } |
|
|
| |
| function showTypingIndicator() { |
| const typingDiv = document.createElement('div'); |
| typingDiv.id = 'typing-indicator'; |
| typingDiv.classList.add('message', 'assistant-message'); |
| typingDiv.innerHTML = ` |
| <div class="message-header"> |
| <div class="avatar assistant-avatar">AI</div> |
| <div>VicoXDarkGPT • typing...</div> |
| </div> |
| <div class="message-content"> |
| <div class="typing-indicator"> |
| <span class="typing-dot"></span> |
| <span class="typing-dot"></span> |
| <span class="typing-dot"></span> |
| </div> |
| </div> |
| `; |
| chatContainer.appendChild(typingDiv); |
| chatContainer.scrollTop = chatContainer.scrollHeight; |
| return typingDiv; |
| } |
|
|
| |
| function removeTypingIndicator(typingElement) { |
| if (typingElement && typingElement.parentNode) { |
| typingElement.parentNode.removeChild(typingElement); |
| } |
| } |
|
|
| |
| async function sendUserMessage() { |
| const message = userInput.value.trim(); |
| if (!message) return; |
|
|
| |
| addMessageToUI('user', message); |
| chatHistory.push({ role: 'user', content: message }); |
| userInput.value = ''; |
| userInput.style.height = 'auto'; |
|
|
| |
| const typingElement = showTypingIndicator(); |
|
|
| try { |
| |
| const selectedModel = modelSelector.value; |
| const selectedMode = modeSelector.value; |
|
|
| |
| const response = await getAIResponse(message, selectedModel, selectedMode); |
|
|
| |
| removeTypingIndicator(typingElement); |
|
|
| |
| addMessageToUI('assistant', response); |
| chatHistory.push({ role: 'assistant', content: response }); |
| } catch (error) { |
| |
| removeTypingIndicator(typingElement); |
| |
| addMessageToUI('assistant', `Error: ${error.message || 'Gagal menghubungi AI'}`); |
| console.error('Error getting AI response:', error); |
| } |
| } |
|
|
| |
| function startNewChat() { |
| chatHistory = []; |
| conversationId++; |
| chatContainer.innerHTML = ''; |
| welcomeScreen.style.display = 'block'; |
| chatContainer.appendChild(welcomeScreen); |
| chatContainer.scrollTop = 0; |
| } |
|
|
| |
| function handleSidebarItemClick(itemId) { |
| console.log('Sidebar item clicked:', itemId); |
| |
| switch(itemId) { |
| case 'new-chat': |
| startNewChat(); |
| break; |
| case 'project-planning': |
| userInput.value = 'Help me plan a new project. What should be the first steps?'; |
| userInput.focus(); |
| switchTab('chat'); |
| break; |
| case 'code-review': |
| userInput.value = 'Please review this code snippet and suggest improvements:'; |
| userInput.focus(); |
| switchTab('chat'); |
| break; |
| case 'web-app-builder': |
| userInput.value = 'Help me build a simple web application. What technologies should I use?'; |
| userInput.focus(); |
| switchTab('chat'); |
| break; |
| case 'file-explorer': |
| switchTab('files'); |
| break; |
| case 'checkpoints': |
| alert('Checkpoints clicked. This would show saved checkpoints.'); |
| |
| break; |
| case 'terminal': |
| switchTab('terminal'); |
| break; |
| case 'create-application': |
| userInput.value = 'Create a new application for me. What kind of application would you like?'; |
| userInput.focus(); |
| switchTab('chat'); |
| break; |
| case 'auto-debug': |
| userInput.value = 'I have an error in my code. Can you help me debug it? Here is the error: '; |
| userInput.focus(); |
| switchTab('chat'); |
| break; |
| case 'code-suggestions': |
| userInput.value = 'Can you suggest code improvements for best practices?'; |
| userInput.focus(); |
| switchTab('chat'); |
| break; |
| case 'performance-analysis': |
| userInput.value = 'Analyze the performance of this code:'; |
| userInput.focus(); |
| switchTab('chat'); |
| break; |
| default: |
| console.log('Unknown sidebar item:', itemId); |
| } |
| |
| sidebar.classList.remove('open'); |
| sidebarBackdrop.classList.remove('active'); |
| } |
|
|
| |
| function switchTab(tabName) { |
| |
| tabs.forEach(tab => tab.classList.remove('active')); |
| tabContents.forEach(content => content.classList.remove('active')); |
| |
| |
| document.querySelector(`.tab[data-tab="${tabName}"]`).classList.add('active'); |
| document.getElementById(`${tabName}-tab`).classList.add('active'); |
| } |
|
|
| |
| function updateFileTree() { |
| fileTree.innerHTML = ''; |
| |
| |
| const srcFolder = document.createElement('div'); |
| srcFolder.classList.add('folder-item'); |
| srcFolder.innerHTML = ` |
| <div class="folder-header"> |
| <i class="fas fa-folder folder-icon"></i> |
| <span>src</span> |
| </div> |
| <div class="folder-content"> |
| <div class="file-item" data-file="main.py"> |
| <i class="fas fa-file-code"></i> main.py |
| </div> |
| <div class="file-item" data-file="app.py"> |
| <i class="fas fa-file-code"></i> app.py |
| </div> |
| </div> |
| `; |
| fileTree.appendChild(srcFolder); |
| |
| const staticFolder = document.createElement('div'); |
| staticFolder.classList.add('folder-item'); |
| staticFolder.innerHTML = ` |
| <div class="folder-header"> |
| <i class="fas fa-folder folder-icon"></i> |
| <span>static</span> |
| </div> |
| <div class="folder-content"> |
| <div class="folder-item"> |
| <div class="folder-header"> |
| <i class="fas fa-folder folder-icon"></i> |
| <span>css</span> |
| </div> |
| <div class="folder-content"> |
| <div class="file-item" data-file="style.css"> |
| <i class="fas fa-file-code"></i> style.css |
| </div> |
| </div> |
| </div> |
| <div class="folder-item"> |
| <div class="folder-header"> |
| <i class="fas fa-folder folder-icon"></i> |
| <span>js</span> |
| </div> |
| <div class="folder-content"> |
| <div class="file-item" data-file="script.js"> |
| <i class="fas fa-file-code"></i> script.js |
| </div> |
| </div> |
| </div> |
| </div> |
| `; |
| fileTree.appendChild(staticFolder); |
| |
| const readmeFile = document.createElement('div'); |
| readmeFile.classList.add('file-item'); |
| readmeFile.setAttribute('data-file', 'README.md'); |
| readmeFile.innerHTML = `<i class="fas fa-file-alt"></i> README.md`; |
| fileTree.appendChild(readmeFile); |
| |
| const reqFile = document.createElement('div'); |
| reqFile.classList.add('file-item'); |
| reqFile.setAttribute('data-file', 'requirements.txt'); |
| reqFile.innerHTML = `<i class="fas fa-file-alt"></i> requirements.txt`; |
| fileTree.appendChild(reqFile); |
| |
| |
| document.querySelectorAll('.file-item').forEach(item => { |
| item.addEventListener('click', () => { |
| const filename = item.getAttribute('data-file'); |
| openFile(filename); |
| }); |
| }); |
| |
| |
| document.querySelectorAll('.folder-header').forEach(header => { |
| header.addEventListener('click', () => { |
| const folderItem = header.parentElement; |
| folderItem.classList.toggle('expanded'); |
| }); |
| }); |
| } |
|
|
| |
| function openFile(filename) { |
| currentFile = filename; |
| alert(`File opened: ${filename}\nIn a real app, this would open the editor.`); |
| |
| } |
|
|
| |
| function addTerminalOutput(output, isError = false) { |
| const line = document.createElement('div'); |
| line.classList.add('terminal-line'); |
| if (isError) { |
| line.classList.add('terminal-error'); |
| line.textContent = `Error: ${output}`; |
| } else { |
| line.textContent = output; |
| } |
| terminalOutput.appendChild(line); |
| terminalOutput.scrollTop = terminalOutput.scrollHeight; |
| } |
|
|
| |
| function runTerminalCommand(command) { |
| addTerminalOutput(`[user@ai-assistant ~]$ ${command}`); |
| |
| if (command === 'help') { |
| addTerminalOutput('Available commands: help, ls, clear, run [file], pwd, cd'); |
| } else if (command === 'ls') { |
| addTerminalOutput('Files in directory:'); |
| for (const filename of Object.keys(files)) { |
| addTerminalOutput(` ${filename}`); |
| } |
| } else if (command === 'pwd') { |
| addTerminalOutput('/home/user/project'); |
| } else if (command.startsWith('cd ')) { |
| const dir = command.split(' ')[1]; |
| addTerminalOutput(`Changed directory to ${dir}`); |
| } else if (command.startsWith('run ')) { |
| const filename = command.split(' ')[1]; |
| if (filename && files[filename]) { |
| addTerminalOutput(`Running ${filename}...`); |
| |
| setTimeout(() => { |
| addTerminalOutput(`Output of ${filename}:`); |
| addTerminalOutput(files[filename]); |
| }, 500); |
| } else { |
| addTerminalOutput(`File not found: ${filename}`, true); |
| } |
| } else { |
| addTerminalOutput(`Command not found: ${command}`, true); |
| } |
| } |
|
|
| |
| sendButton.addEventListener('click', sendUserMessage); |
|
|
| userInput.addEventListener('input', function() { |
| this.style.height = 'auto'; |
| this.style.height = (this.scrollHeight) + 'px'; |
| }); |
|
|
| userInput.addEventListener('keydown', (e) => { |
| if (e.key === 'Enter' && !e.shiftKey) { |
| e.preventDefault(); |
| sendUserMessage(); |
| } |
| }); |
|
|
| hamburgerBtn.addEventListener('click', () => { |
| sidebar.classList.add('open'); |
| sidebarBackdrop.classList.add('active'); |
| }); |
|
|
| closeSidebarBtn.addEventListener('click', () => { |
| sidebar.classList.remove('open'); |
| sidebarBackdrop.classList.remove('active'); |
| }); |
|
|
| sidebarBackdrop.addEventListener('click', () => { |
| sidebar.classList.remove('open'); |
| sidebarBackdrop.classList.remove('active'); |
| }); |
|
|
| newChatBtn.addEventListener('click', startNewChat); |
| floatingNewChatBtn.addEventListener('click', startNewChat); |
|
|
| |
| suggestionCards.forEach(card => { |
| card.addEventListener('click', () => { |
| const prompt = card.getAttribute('data-prompt'); |
| userInput.value = prompt; |
| userInput.focus(); |
| switchTab('chat'); |
| }); |
| }); |
|
|
| |
| document.querySelectorAll('.sidebar-item').forEach(item => { |
| |
| const itemId = item.getAttribute('data-id'); |
| if (itemId) { |
| item.addEventListener('click', () => handleSidebarItemClick(itemId)); |
| } |
| }); |
|
|
| |
| tabs.forEach(tab => { |
| tab.addEventListener('click', () => { |
| const tabName = tab.getAttribute('data-tab'); |
| switchTab(tabName); |
| }); |
| }); |
|
|
| |
| newFileBtn.addEventListener('click', () => { |
| const filename = prompt('Enter new file name (e.g., myfile.py):'); |
| if (filename) { |
| files[filename] = ''; |
| updateFileTree(); |
| } |
| }); |
|
|
| newFolderBtn.addEventListener('click', () => { |
| alert('Folder creation is not implemented in this demo.'); |
| }); |
|
|
| |
| terminalInput.addEventListener('keydown', (e) => { |
| if (e.key === 'Enter') { |
| const command = terminalInput.value.trim(); |
| if (command) { |
| runTerminalCommand(command); |
| terminalInput.value = ''; |
| } |
| } |
| }); |
|
|
| runCodeBtn.addEventListener('click', () => { |
| const filename = prompt('Enter file name to run:'); |
| if (filename && files[filename]) { |
| runTerminalCommand(`run ${filename}`); |
| } else if (filename) { |
| addTerminalOutput(`File not found: ${filename}`, true); |
| } |
| }); |
|
|
| clearTerminalBtn.addEventListener('click', () => { |
| terminalOutput.innerHTML = ''; |
| addTerminalOutput('[user@ai-assistant ~]$ Welcome to VicoXDarkGPT Terminal'); |
| addTerminalOutput('[user@ai-assistant ~]$ Type \'help\' for available commands'); |
| }); |
|
|
| |
| refreshPreviewBtn.addEventListener('click', () => { |
| alert('Preview refresh is not implemented in this demo.'); |
| }); |
|
|
| openPreviewBtn.addEventListener('click', () => { |
| alert('Opening preview in new tab is not implemented in this demo.'); |
| }); |
|
|
| |
| updateFileTree(); |