// 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') ? '