// static/js/editor.js v8 var editor; function initEditor() { var el = document.getElementById('ace-editor'); if (!el) return; // Already initialized — just resize and focus if (editor) { try { editor.resize(); editor.focus(); } catch(e) {} return; } try { editor = ace.edit('ace-editor'); } catch(e) { console.error('Ace editor init failed:', e); return; } editor.setTheme('ace/theme/tomorrow_night_eighties'); editor.session.setMode('ace/mode/python'); editor.setOptions({ fontSize: '14px', fontFamily: "'JetBrains Mono', monospace", showPrintMargin: false, enableBasicAutocompletion: true, }); var fnameInput = document.getElementById('current-filename'); if (fnameInput) fnameInput.addEventListener('input', checkRunBtn); // Force focus on click anywhere in editor area var editorContainer = document.getElementById('editor-main-container'); if (editorContainer) { editorContainer.addEventListener('click', function() { if (editor) editor.focus(); }); } // Resize + focus when editor view becomes active var obs = new MutationObserver(function() { var ev = document.getElementById('editor-view'); if (ev && ev.classList.contains('active')) { setTimeout(function() { if (editor) { try { editor.resize(); editor.focus(); } catch(e) {} } }, 150); } }); var ev = document.getElementById('editor-view'); if (ev) obs.observe(ev, { attributes: true, attributeFilter: ['class'] }); // Initial focus setTimeout(function() { if (editor) { try { editor.focus(); } catch(e) {} } }, 200); loadFiles(); } function fileIcon(f) { var m = { '.py': 'fa-brands fa-python', '.js': 'fa-brands fa-js', '.html': 'fa-brands fa-html5', '.css': 'fa-brands fa-css3-alt', '.json': 'fa-solid fa-code', '.md': 'fa-solid fa-file-lines' }; var c = { '.py': '#4B8BBE', '.js': '#F7DF1E', '.html': '#E34F26', '.css': '#1572B6', '.json': '#8BC34A', '.md': 'var(--text-2)' }; var ext = f.split('.').pop() ? '.' + f.split('.').pop() : ''; return ''; } async function loadFiles() { if (!currentToken) return; try { var res = await fetch('/api/files', { method: 'POST', headers: {'Content-Type': 'application/json'}, body: JSON.stringify({token: currentToken}) }); var data = await res.json(); var list = document.getElementById('file-list'); if (!list) return; list.innerHTML = ''; if (data.files && data.files.length) { data.files.forEach(function(f) { var div = document.createElement('div'); div.className = 'file-item'; div.innerHTML = fileIcon(f) + '' + f + ''; div.onclick = function() { openFile(f); }; list.appendChild(div); }); } else { list.innerHTML = '
No files. Tap + to create.
'; } } catch (e) { console.error(e); } } function newFile() { document.getElementById('current-filename').value = 'untitled.html'; checkRunBtn(); if (editor) { editor.setValue('\n\n\n New\n\n\n

Hello

\n\n', -1); editor.session.setMode('ace/mode/html'); editor.focus(); } } async function openFile(name) { document.getElementById('current-filename').value = name; checkRunBtn(); document.querySelectorAll('.file-item').forEach(function(el) { el.classList.toggle('active', el.textContent.trim() === name); }); try { var res = await fetch('/api/file/read', { method: 'POST', headers: {'Content-Type': 'application/json'}, body: JSON.stringify({token: currentToken, filename: name}) }); var data = await res.json(); if (!data.error && editor) { editor.setValue(data.content, -1); var modes = { '.py': 'python', '.js': 'javascript', '.html': 'html', '.css': 'css', '.json': 'json' }; var ext = '.' + name.split('.').pop(); editor.session.setMode('ace/mode/' + (modes[ext] || 'text')); editor.focus(); } } catch (e) { showToast('Error reading file', 'error'); } } async function saveFile() { var name = document.getElementById('current-filename').value; var content = editor ? editor.getValue() : ''; if (!name) { showToast('Enter a filename', 'warning'); return; } var btn = document.querySelector('.ed-btn.primary'); if (btn) btn.innerHTML = ' Saving...'; try { var res = await fetch('/api/file/save', { method: 'POST', headers: {'Content-Type': 'application/json'}, body: JSON.stringify({token: currentToken, filename: name, content: content}) }); var data = await res.json(); if (data.success) { showToast('Saved ' + name, 'success'); loadFiles(); if (document.getElementById('preview-container').classList.contains('visible')) runCode(); } else showToast('Error: ' + data.error, 'error'); } catch (e) { showToast('Network error', 'error'); } if (btn) btn.innerHTML = ' Save'; } async function renameFile() { var newName = prompt('New filename:'); if (!newName) return; var old = document.getElementById('current-filename').value; try { var res = await fetch('/api/file/rename', { method: 'POST', headers: {'Content-Type': 'application/json'}, body: JSON.stringify({token: currentToken, filename: old, new_name: newName}) }); var data = await res.json(); if (data.success) { document.getElementById('current-filename').value = newName; checkRunBtn(); loadFiles(); showToast('Renamed', 'success'); } else showToast(data.error || 'Failed', 'error'); } catch (e) { showToast('Error', 'error'); } } async function aiEdit() { var p = prompt('What should AI do?'); if (!p || !editor) return; var orig = editor.getValue(); editor.setValue('AI processing...', -1); try { var res = await fetch('/api/ai_edit', { method: 'POST', headers: {'Content-Type': 'application/json'}, body: JSON.stringify({prompt: p, content: orig}) }); var data = await res.json(); if (data.code && !data.code.includes('NETWORK_ERROR')) { editor.setValue(data.code, -1); showToast('AI edit applied', 'success'); } else { editor.setValue(orig, -1); showToast('AI edit failed', 'error'); } } catch (e) { editor.setValue(orig, -1); showToast('Network error', 'error'); } } function checkRunBtn() { var f = document.getElementById('current-filename').value; var btn = document.getElementById('run-btn'); var show = f.endsWith('.html') || f.endsWith('.js') || f.endsWith('.css'); btn.style.display = show ? 'inline-flex' : 'none'; if (!show) closePreview(); } async function runCode() { var name = document.getElementById('current-filename').value; await saveFile(); var pc = document.getElementById('preview-container'); var iframe = document.getElementById('live-preview-frame'); pc.classList.add('visible'); if (name.endsWith('.html')) { iframe.src = '/preview/' + currentToken + '/' + name; } else { var content = editor.getValue(); var html = name.endsWith('.js') ? '