| | |
| | 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(); |