CodeStudio
Asistente IA
`); w.document.close(); } // AI Chat const chatMessagesEl = document.getElementById('chatMessages'); const chatInputEl = document.getElementById('chatInput'); const btnSend = document.getElementById('btnSend'); const btnInsert = document.getElementById('btnInsert'); const btnReplace = document.getElementById('btnReplace'); const btnCreateFile = document.getElementById('btnCreateFile'); const btnClearChat = document.getElementById('btnClearChat'); function addMessage(role, content) { state.chat.push({ role, content, ts: Date.now() }); const el = document.createElement('div'); el.className = `flex ${role === 'user' ? 'justify-end' : 'justify-start'} animate-slideIn`; el.innerHTML = `
${escapeHTML(content)}
`; chatMessagesEl.appendChild(el); chatMessagesEl.scrollTop = chatMessagesEl.scrollHeight; } function escapeHTML(str) { return String(str).replace(/[&<>"']/g, s => ({'&':'&','<':'<','>':'>','"':'"',"'":'''}[s])); } function simulateAIResponse(prompt) { // Simple contextual responses const hasCreate = /create\s+new\s+file|crear\s+archivo/i.test(prompt); const isExplain = /explain|explica|qué hace|how does/i.test(prompt); const hasFix = /fix|bug|arregla|error/i.test(prompt); const hasFormat = /format|formattear/i.test(prompt); if (hasCreate) { return `Sure! I can help you create a new file. For example: // nueva Funcionalidad export function hello() { console.log("Hello from your AI!"); } Use "Create file" to insert it directly into the virtual FS.`; } if (isExplain) { const active = state.activePath ? `(${state.activePath})` : ''; return `Here’s a quick explanation${active}: - This editor simulates a virtual FS loaded with fetchFileSystem(). - Use Ctrl+S to save to memory, Ctrl+B to toggle the sidebar, Ctrl+J to toggle the console, and Ctrl+Enter to send a prompt. Let me know what you want to explore next.`; } if (hasFix) { return `I can suggest a fix. Please share the snippet or error message you’re seeing. Meanwhile, try: 1) Checking the path is correct. 2) Ensure the file exists in the virtual FS. 3) Press Ctrl+S to persist changes to memory.`; } if (hasFormat) { return `Formatting is handled by Monaco automatically (editor.action.formatDocument). You can also click the "Format" button in the top bar.`; } // Generic helpful answer return `I’m your coding assistant. I can: - Explain files and code - Generate or refactor snippets - Help create and update files in the virtual FS - Insert code at cursor or replace selection Ask me to "create a new file utils.js with helper functions" or to "explain install.php".`; } function sendPrompt() { const text = chatInputEl.value.trim(); if (!text) return; chatInputEl.value = ''; addMessage('user', text); const response = simulateAIResponse(text); // streaming effect let i = 0; const step = () => { if (i >= response.length) return; const chunk = response.slice(i, i += Math.max(1, Math.round(Math.random() * 8))); addMessage('assistant', chunk); setTimeout(step, 20 + Math.random() * 20); }; setTimeout(step, 150); } function insertAtCursor(text) { if (!editor) return; const sel = editor.getSelection(); const id = { major: 1, minor: 1 }; const op = { identifier: id, range: sel, text, forceMoveMarkers: true }; editor.executeEdits('ai-insert', [op]); } function replaceSelection(text) { if (!editor) return; const sel = editor.getSelection(); const op = { identifier: { major: 1, minor: 2 }, range: sel, text, forceMoveMarkers: true }; editor.executeEdits('ai-replace', [op]); } function createFileFromAssistant() { const last = [...state.chat].reverse().find(m => m.role === 'assistant'); if (!last) return; const suggested = prompt('Nombre/ubicación del archivo (ej: utils/helper.js):', 'utils/helper.js'); if (!suggested) return; const fullPath = suggested.replace(/^\/+/, ''); try { createFile(fullPath, last.content); refreshExplorer(document.getElementById('fileSearch').value); addMessage('assistant', `Created ${fullPath} ✅`); if (!state.openTabs.includes(fullPath)) openFile(fullPath); } catch (e) { alert('Error al crear archivo: ' + e.message); } } // Hotkeys window.addEventListener('keydown', (e) => { if (e.ctrlKey || e.metaKey) { if (e.key.toLowerCase() === 'b') { e.preventDefault(); if (sidebarEl.classList.contains('-translate-x-full')) openSidebar(); else closeSidebar(); } if (e.key.toLowerCase() === 'j') { e.preventDefault(); toggleConsole(); } if (e.key.toLowerCase() === 's') { e.preventDefault(); saveActiveFile(); } if (e.key === 'Enter') { e.preventDefault(); sendPrompt(); } } }); // Wire header buttons document.getElementById('btnToggleSidebar').addEventListener('click', () => { if (sidebarEl.classList.contains('-translate-x-full')) openSidebar(); else closeSidebar(); }); sidebarOverlay.addEventListener('click', closeSidebar); document.getElementById('btnToggleConsole').addEventListener('click', toggleConsole); document.getElementById('btnToggleTheme').addEventListener('click', () => { const mode = document.documentElement.classList.contains('dark') ? 'light' : 'dark'; setTheme(mode); }); document.getElementById('btnFormat').addEventListener('click', async () => { if (!editor) return; editor.getAction('editor.action.formatDocument').run(); flashHeader('Documento formateado'); }); document.getElementById('btnPreview').addEventListener('click', openPreview); document.getElementById('btnNewFile').addEventListener('click', () => { const name = prompt('Nombre del archivo (puede incluir carpetas, ej: components/Hello.vue):', 'utils/newfile.php'); if (!name) return; const path = name.replace(/^\/+/, ''); try { createFile(path, ''); refreshExplorer(document.getElementById('fileSearch').value); openFile(path); } catch (e) { alert('Error: ' + e.message); } }); document.getElementById('btnRefreshFS').addEventListener('click', async () => { document.getElementById('fileSearch').value = ''; state.fs = await fetchFileSystem(); refreshExplorer(''); }); document.getElementById('fileSearch').addEventListener('input', (e) => refreshExplorer(e.target.value)); // Chat actions btnSend.addEventListener('click', sendPrompt); chatInputEl.addEventListener('keydown', (e) => { if ((e.ctrlKey || e.metaKey) && e.key === 'Enter') { e.preventDefault