PROBLEMAS
SALIDA
TERMINAL
more_horiz
expand_less
close
Defecto
iPhone SE
iPhone XR
Samsung Galaxy S8+
Samsung Galaxy S20 Ultra
warning
0
error
0
Lín. 1, col. 1
UTF-8
PHP
notifications
⚠️
Warning: Variable no definida: 'proyecto'
Call Stack
0
handleError
/home/u266581275/domains/ikode.live/public_html/src/mopla/src/Parser.php::464
ErrorHandler
1
mensaje
/home/u266581275/domains/ikode.live/public_html/src/mopla/src/Parser.php::74
Parser
2
parse
/home/u266581275/domains/ikode.live/public_html/src/mopla/src/Mopla.php::58
Parser
3
render
/home/u266581275/domains/ikode.live/public_html/index.php::80
Mopla
res.json())
.then(json => {
if (json.error) {
const errorBox = document.getElementById('mensajeError');
if (errorBox) {
errorBox.textContent = json.error;
errorBox.style.display = 'block';
} else {
alert(json.error);
}
return;
}
fileTreeData = [json];
renderAll(fileTreeData);
});
}
function renderAll(data) {
const container = document.getElementById("fileTree");
container.innerHTML = '';
renderTree(data, container, "", 0);
}
function renderTree(tree, container, basePath, depth) {
tree.forEach(item => {
const itemEl = document.createElement('div');
itemEl.classList.add('folder-item');
const depthClasses = ['sub-item', 'sub-sub-item', 'sub-sub-sub-item', 'sub-sub-sub-sub-item', 'sub-sub-sub-sub-sub-item', 'sub-sub-sub-sub-sub-sub-item', 'sub-sub-sub-sub-sub-sub-sub-item','sub-sub-sub-sub-sub-sub-sub-sub-item'];
if (depth > 0 && depth <= 7) {
itemEl.classList.add(depthClasses[depth - 1]);
}
itemEl.dataset.name = item.name;
itemEl.dataset.type = item.type;
const fullPath = basePath ? `${basePath}/${item.name}` : item.name;
itemEl.title = fullPath;
const icon = document.createElement('span');
icon.className = `${item.type === 'file' ? obtenerClaseIcono(item.name) : 'ifr if-folder-closed'}`;//if-folder-open
const nameSpan = document.createElement('span');
nameSpan.classList.add('span-titulo');
nameSpan.textContent = item.name;
// CLICK: Abrir carpeta o imprimir archivo
itemEl.addEventListener('click', e => {
e.stopPropagation();
document.querySelectorAll('.folder-item.active').forEach(el => el.classList.remove('active'));
itemEl.classList.add('active');
if (item.type === 'folder') {
item.open = !item.open;
renderAll(fileTreeData);
} else {
if (item.type === 'file') {
console.log("Abrir archivo:", '/ronald/'+fullPath);
fetch('/src/api/files/open.php', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
ruta: '/ronald/'+fullPath // ← asegúrate que el JSON de archivos incluya el ID
})
})
.then(res => res.json())
.then(json => {
if (json.error) {
alert("Error al abrir archivo: " + json.error);
} else {
abrirArchivoEnEditor(item.name, fullPath ,json.contenido, item);
}
});
}else{console.log('no es un archivo');}
}
});
// DRAG & DROP
itemEl.setAttribute('draggable', true);
itemEl.addEventListener('dragstart', (e) => {
draggedItem = { item, from: tree };
const d = document.createElement('div');
d.className = 'drag-preview';
d.textContent = item.name;
// Insertar el div y posicionarlo fuera de la vista
document.body.appendChild(d);
// Ahora sí usarlo como imagen de drag
e.dataTransfer.setDragImage(d, -25,-7);
// No lo eliminamos de inmediato (para que no se corte), sino después
setTimeout(() => {
if (d.parentNode) d.remove();
}, 100);
});
itemEl.addEventListener('dragover', e => {
e.preventDefault();
itemEl.classList.add('drag-over');
});
itemEl.addEventListener('dragleave', () => {
itemEl.classList.remove('drag-over');
});
itemEl.addEventListener('drop', e => {
e.preventDefault();
itemEl.classList.remove('drag-over');
if (item.type === 'folder' && draggedItem.item !== item) {
if (!item.children) item.children = [];
item.open = true;
const idx = draggedItem.from.indexOf(draggedItem.item);
if (idx >= 0) draggedItem.from.splice(idx, 1);
item.children.push(draggedItem.item);
//lamar a mover.php para cambiar los archivos reales
fetch('/src/api/files/move.php', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
origen: draggedItem.item.path,
destino: item.path + '/' + draggedItem.item.name
})
})
.then(res => res.json())
.then(json => {
if (json.error) {
alert("Error al mover: " + json.error);
} else {
console.log("Movimiento confirmado");
}
});
renderAll(fileTreeData);
}
});
itemEl.appendChild(icon);
itemEl.appendChild(nameSpan);
container.appendChild(itemEl);
if (item.type === 'folder' && item.open && item.children) {
renderTree(item.children, container, fullPath, depth + 1);
}
});
}
function obtenerClaseIcono(nombre) {
const ext = nombre.split('.').pop().toLowerCase();
const iconos = {
js: 'ifb if-square-js',
php: 'ifb if-php',
html: 'ifb if-html5',
css: 'ifb if-css3-alt',
json: 'ifr if-file-code',
png: 'ifr if-file-png',
jpg: 'ifr if-file-jpg',
jpeg: 'ifr if-file-jpg',
gif: 'ifr if-gift',
mp4: 'ifr if-file-video',
mp3: 'ifr if-file-mp3',
txt: 'ifr if-file-text',
md: 'ifr if-file-circle-question',
pdf: 'ifr if-file-pdf',
zip: 'ifr if-file-zip',
rar: 'ifr if-file-zipper',
// Agrega más según tu librería de íconos
};
return iconos[ext] || 'ifr if-file'; // por defecto
}
fetchFileTree();
let contadorArchivos = 0;
const editores = {};
let archivoActivo = null;
require.config({ paths: { 'vs': 'assets/js/vs' }});
function abrirArchivoEnEditor(nombre, ruta, contenidoBase64) {
const id = 'archivo-' + (++contadorArchivos); // ID incremental
// Crear pestaña
const tab = document.createElement('div');
tab.className = 'tab';
tab.id = 'tab-' + id;
tab.innerHTML = `
${obtenerLenguajeDesdeExtension(nombre)}
${nombre}
close
`;
document.getElementById('tabs-content').appendChild(tab);
// Crear breadcrumb
const breadcrumb = document.createElement('div');
breadcrumb.className = 'breadcrumb-line';
breadcrumb.id = 'breadcrumb-' + id;
breadcrumb.innerHTML = `
${ruta.split('/')[0]}
chevron_right
${ruta}
`;
document.getElementById('breadcrumb-content').appendChild(breadcrumb);
// Crear contenedor de editor
const contenedorEditor = document.createElement('div');
contenedorEditor.className = 'editor-instance';
contenedorEditor.id = 'editor-' + id;
contenedorEditor.style.width = '100%';
contenedorEditor.style.height = '100%';
document.getElementById('editor').appendChild(contenedorEditor);
// Inicializar Monaco
require(['vs/editor/editor.main'], function () {
editores[id] = monaco.editor.create(contenedorEditor, {
value: decodeBase64UTF8(contenidoBase64),
language: obtenerLenguajeDesdeExtension(nombre),
theme: 'vs',
automaticLayout: true
});
activarArchivo(id); // Activar después de crear
});
}
function decodeBase64UTF8(base64) {
const binary = atob(base64);
const bytes = Uint8Array.from(binary, c => c.charCodeAt(0));
return new TextDecoder('utf-8').decode(bytes);
}
function activarArchivo(id) {
archivoActivo = id;
document.querySelectorAll('#tabs-content .tab').forEach(t => t.classList.remove('active'));
document.getElementById('tab-' + id).classList.add('active');
document.querySelectorAll('#breadcrumb-content .breadcrumb-line').forEach(b => b.classList.remove('active'));
document.getElementById('breadcrumb-' + id).classList.add('active');
document.querySelectorAll('#editor .editor-instance').forEach(e => e.style.display = 'none');
document.getElementById('editor-' + id).style.display = 'block';
}
// Cerrar pestaña
document.addEventListener('click', (e) => {
if (e.target.classList.contains('cerrar-tab')) {
const id = e.target.dataset.id;
document.getElementById('tab-' + id)?.remove();
document.getElementById('breadcrumb-' + id)?.remove();
document.getElementById('editor-' + id)?.remove();
editores[id]?.dispose();
delete editores[id];
if (archivoActivo === id) {
const restantes = Object.keys(editores);
if (restantes.length > 0) activarArchivo(restantes[0]);
else archivoActivo = null;
}
}
});
// Detectar clic en pestaña para activar
document.addEventListener('click', e => {
if (e.target.closest('.tab') && e.target.closest('.tab').id?.startsWith('tab-')) {
const id = e.target.closest('.tab').id.replace('tab-', '');
activarArchivo(id);
}
});
function obtenerLenguajeDesdeExtension(nombre) {
const ext = nombre.split('.').pop().toLowerCase();
return {
js: 'javascript',
php: 'php',
html: 'html',
css: 'css',
json: 'json',
md: 'markdown',
txt: 'plaintext'
}[ext] || 'plaintext';
}