const botCoderEditor = { instances: {}, currentFile: null, tabs: [], models: {}, editor: null, init: function() { if (!window.require) { console.error('Monaco loader not found.'); return; } require.config({ paths: { vs: '/suite/js/vendor/vs' } }); require(['vs/editor/editor.main'], () => { this.editor = monaco.editor.create(document.getElementById('monacoEditorContainer'), { value: '// Welcome to BotCoder Multi-Agent IDE\n// Select a file from the explorer to begin viewing or editing.', language: 'javascript', theme: 'vs-dark', automaticLayout: true, minimap: { enabled: true }, fontSize: 14, fontFamily: 'Consolas, "Courier New", monospace' }); this.setupKeyboardShortcuts(); this.refreshFiles(); // Auto-save debouncer could be added here on content change this.editor.onDidChangeModelContent(() => { const statusSpan = document.getElementById('botcoderFileStatus'); if (statusSpan && this.currentFile) { statusSpan.textContent = '• Unsaved'; this.markTabDirty(this.currentFile, true); } }); }); }, setupKeyboardShortcuts: function() { // Ctrl+S to save this.editor.addCommand(monaco.KeyMod.CtrlCmd | monaco.KeyCode.KeyS, () => { this.saveCurrentFile(); }); document.addEventListener('keydown', (e) => { if (e.ctrlKey && e.key === 's') { e.preventDefault(); this.saveCurrentFile(); } }); }, getLanguageFromExt: function(fileName) { if (fileName.endsWith('.js')) return 'javascript'; if (fileName.endsWith('.html')) return 'html'; if (fileName.endsWith('.css')) return 'css'; if (fileName.endsWith('.json')) return 'json'; if (fileName.endsWith('.bas')) return 'basic'; // Custom type maybe if (fileName.endsWith('.rs')) return 'rust'; return 'plaintext'; }, openFile: async function(filePath) { try { document.getElementById('botcoderCurrentFile').textContent = 'Loading...'; // Check if already open if (this.models[filePath]) { this.switchToTab(filePath); return; } const response = await fetch(`/api/editor/file/${encodeURIComponent(filePath)}`); if (!response.ok) throw new Error('File not found'); const result = await response.json(); const content = result.content || ''; const language = this.getLanguageFromExt(filePath); const model = monaco.editor.createModel(content, language, monaco.Uri.file(filePath)); this.models[filePath] = { model, dirty: false }; this.addTab(filePath); this.switchToTab(filePath); } catch (err) { console.error('Error opening file:', err); alert('Failed to open file: ' + err.message); } }, addTab: function(filePath) { if (!this.tabs.includes(filePath)) { this.tabs.push(filePath); this.renderTabs(); } }, switchToTab: function(filePath) { if (!this.models[filePath]) return; this.currentFile = filePath; this.editor.setModel(this.models[filePath].model); const fileName = filePath.split('/').pop() || filePath; document.getElementById('botcoderCurrentFile').textContent = fileName; const isDirty = this.models[filePath].dirty; document.getElementById('botcoderFileStatus').textContent = isDirty ? '• Unsaved' : ''; this.renderTabs(); }, closeTab: function(filePath, e) { if (e) e.stopPropagation(); if (this.models[filePath] && this.models[filePath].dirty) { if (!confirm(`Save changes to ${filePath} before closing?`)) { return; } } if (this.models[filePath]) { this.models[filePath].model.dispose(); delete this.models[filePath]; } this.tabs = this.tabs.filter(t => t !== filePath); if (this.currentFile === filePath) { this.currentFile = this.tabs.length > 0 ? this.tabs[this.tabs.length - 1] : null; if (this.currentFile) { this.switchToTab(this.currentFile); } else { this.editor.setValue('// Select a file to edit'); document.getElementById('botcoderCurrentFile').textContent = 'No file open'; document.getElementById('botcoderFileStatus').textContent = ''; } } this.renderTabs(); }, renderTabs: function() { const tabsContainer = document.getElementById('botcoderTabs'); if (!tabsContainer) return; tabsContainer.innerHTML = this.tabs.map(tab => { const fileName = tab.split('/').pop(); const isActive = tab === this.currentFile; const isDirty = this.models[tab]?.dirty; return `