// Python Launcher - Gestión de proyectos Python sin GUI (MCP servers, Flask apps, etc.) // Variables globales para Python launcher let pythonProjects = []; let pythonScripts = []; let pythonFavorites = []; let pythonRunningProcesses = []; let pythonHistory = []; let selectedPythonProject = null; let pythonCategories = {}; let currentPythonFilter = 'all'; let pythonMarkdownFiles = []; let currentEditingPythonProject = null; let pythonEnvs = []; // === INICIALIZACIÓN === document.addEventListener('DOMContentLoaded', function () { console.log('Python Launcher: DOM loaded'); }); // Función llamada cuando se activa el tab Python function initPythonLauncher() { console.log('Python Launcher: Initializing...'); loadPythonEnvironments(); loadPythonProjects(); loadPythonCategories(); loadPythonFavorites(); loadPythonHistory(); refreshPythonProcesses(); // Actualizar procesos cada 30 segundos setInterval(refreshPythonProcesses, 30000); } // === GESTIÓN DE ENTORNOS PYTHON === async function loadPythonEnvironments() { try { const response = await fetch('/api/python-environments'); if (response.ok) { pythonEnvs = await response.json(); renderPythonEnvSelector(); console.log('Python environments loaded:', pythonEnvs.length); } else { console.error('Error loading Python environments:', response.statusText); } } catch (error) { console.error('Error loading Python environments:', error); } } function renderPythonEnvSelector() { const selector = document.getElementById('python-project-python-env'); if (!selector) return; selector.innerHTML = ''; pythonEnvs.forEach(env => { const option = document.createElement('option'); option.value = env.name; option.textContent = env.display_name; selector.appendChild(option); }); } // === GESTIÓN DE PROYECTOS === async function loadPythonProjects() { try { const response = await fetch('/api/python-projects'); if (response.ok) { pythonProjects = await response.json(); updatePythonProjectSelector(); console.log('Python projects loaded:', pythonProjects.length); } else { console.error('Error loading Python projects:', response.statusText); } } catch (error) { console.error('Error loading Python projects:', error); } } function updatePythonProjectSelector() { const selector = document.getElementById('python-project-select'); if (!selector) return; // Guardar la selección actual const currentSelection = getCurrentPythonProjectSelection(); // Limpiar opciones existentes selector.innerHTML = ''; // Ordenar proyectos alfabéticamente por nombre const sortedProjects = [...pythonProjects].sort((a, b) => a.name.localeCompare(b.name)); // Agregar proyectos sortedProjects.forEach(project => { const option = document.createElement('option'); option.value = project.id; option.textContent = project.name; option.dataset.description = project.description || ''; option.dataset.category = project.category || 'Otros'; option.dataset.pythonEnv = project.python_env || 'base'; selector.appendChild(option); }); // Restaurar la selección guardada restorePythonProjectSelection(currentSelection); } // Función para obtener la selección actual del localStorage function getCurrentPythonProjectSelection() { const currentValue = document.getElementById('python-project-select')?.value; return localStorage.getItem('python-selected-project') || currentValue || ''; } // Función para restaurar la selección desde localStorage function restorePythonProjectSelection(projectId) { const selector = document.getElementById('python-project-select'); if (!selector || !projectId) return; // Verificar que el proyecto aún existe const projectExists = pythonProjects.some(p => p.id === projectId); if (projectExists) { selector.value = projectId; // Cargar scripts del proyecto restaurado automáticamente loadPythonScripts(); } else { // Si el proyecto ya no existe, limpiar localStorage localStorage.removeItem('python-selected-project'); } } async function loadPythonScripts() { const projectId = document.getElementById('python-project-select').value; // Guardar la selección en localStorage if (projectId) { localStorage.setItem('python-selected-project', projectId); } else { localStorage.removeItem('python-selected-project'); } if (!projectId) { selectedPythonProject = null; pythonScripts = []; pythonMarkdownFiles = []; updatePythonScriptsGrid(); updatePythonProjectButtons(); renderPythonMarkdownFiles(); return; } try { // Cargar scripts y archivos markdown en paralelo const [scriptsResponse, markdownResponse] = await Promise.all([ fetch(`/api/python-scripts/${projectId}`), fetch(`/api/python-markdown/${projectId}`) ]); if (scriptsResponse.ok) { pythonScripts = await scriptsResponse.json(); selectedPythonProject = pythonProjects.find(p => p.id === projectId); updatePythonScriptsGrid(); updatePythonProjectButtons(); console.log('Python scripts loaded:', pythonScripts.length); } else { console.error('Error loading Python scripts:', scriptsResponse.statusText); pythonScripts = []; updatePythonScriptsGrid(); } if (markdownResponse.ok) { const markdownData = await markdownResponse.json(); pythonMarkdownFiles = markdownData.files || []; renderPythonMarkdownFiles(); console.log('Python markdown files loaded:', pythonMarkdownFiles.length); } else { pythonMarkdownFiles = []; renderPythonMarkdownFiles(); } } catch (error) { console.error('Error loading Python project content:', error); pythonScripts = []; pythonMarkdownFiles = []; updatePythonScriptsGrid(); renderPythonMarkdownFiles(); } } function updatePythonProjectButtons() { const buttonsToShow = ['vscode-python-btn', 'cursor-python-btn', 'folder-python-btn', 'copy-path-python-btn', 'manage-python-scripts-btn']; const hasProject = selectedPythonProject !== null; buttonsToShow.forEach(buttonId => { const button = document.getElementById(buttonId); if (button) { button.style.display = hasProject ? 'block' : 'none'; } }); // Actualizar icono del proyecto const projectIcon = document.getElementById('selected-python-project-icon'); if (projectIcon && selectedPythonProject) { const categoryIcon = pythonCategories[selectedPythonProject.category]?.icon || '🐍'; projectIcon.textContent = categoryIcon; } } // === GESTIÓN DE SCRIPTS === function updatePythonScriptsGrid() { const grid = document.getElementById('python-scripts-grid'); if (!grid) return; grid.innerHTML = ''; if (!selectedPythonProject || pythonScripts.length === 0) { grid.innerHTML = `
${!selectedPythonProject ? 'Selecciona un proyecto para ver sus scripts' : 'No hay scripts disponibles en este proyecto' }
`; return; } // Filtrar scripts por categoría const filteredScripts = currentPythonFilter === 'all' ? pythonScripts : pythonScripts.filter(script => { const scriptCategory = getScriptCategory(script); return scriptCategory === currentPythonFilter; }); filteredScripts.forEach(script => { const scriptCard = createPythonScriptCard(script); grid.appendChild(scriptCard); }); if (filteredScripts.length === 0) { grid.innerHTML = `
No hay scripts que coincidan con el filtro seleccionado
`; } } function createPythonScriptCard(script) { const card = document.createElement('div'); card.className = 'bg-white border rounded-lg p-4 hover:shadow-md transition-shadow'; const isFavorite = pythonFavorites.some(fav => fav.project_id === selectedPythonProject.id && fav.script_name === script.filename ); const scriptCategory = getScriptCategory(script); const categoryInfo = pythonCategories[scriptCategory] || pythonCategories['Otros']; // Determinar si es un servidor o script de background const isServerScript = script.is_server || script.requires_background; const serverInfo = script.server_port ? ` (Puerto: ${script.server_port})` : ''; card.innerHTML = `

${script.display_name}

${script.tags.map(tag => `${tag}`).join('')}
${categoryInfo.icon} ${scriptCategory} ${isServerScript ? `SERVER${serverInfo}` : ''}

${script.description || 'Sin descripción'}

📄 ${script.filename}
${isServerScript ? ` ` : ''}
`; return card; } function getScriptCategory(script) { // Categorización automática basada en tags o nombres const filename = script.filename.toLowerCase(); const tags = script.tags.map(tag => tag.toLowerCase()); if (tags.includes('mcp') || filename.includes('mcp') || script.is_server) { return 'MCP Servers'; } else if (tags.includes('flask') || filename.includes('flask') || filename.includes('api')) { return 'Flask Apps'; } else if (tags.includes('bot') || filename.includes('bot')) { return 'Bots'; } else if (tags.includes('data') || filename.includes('data') || tags.includes('analysis')) { return 'Data Processing'; } else if (script.requires_background) { return 'Scripts'; } return 'Scripts'; } // === EJECUCIÓN DE SCRIPTS === async function executePythonScript(scriptName, args = [], runInBackground = false) { if (!selectedPythonProject) { alert('Selecciona un proyecto primero'); return; } try { const payload = { project_id: selectedPythonProject.id, script_name: scriptName, args: args, working_dir: selectedPythonProject.directory, run_in_background: runInBackground }; const response = await fetch('/api/execute-python-script', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(payload) }); const result = await response.json(); if (result.error) { alert(`Error: ${result.error}`); } else { console.log('Script execution result:', result); // Actualizar historial y procesos setTimeout(() => { loadPythonHistory(); refreshPythonProcesses(); }, 1000); } } catch (error) { console.error('Error executing Python script:', error); alert(`Error ejecutando script: ${error.message}`); } } function showPythonScriptOptions(scriptName) { const script = pythonScripts.find(s => s.filename === scriptName); if (!script) return; const modal = document.getElementById('python-script-options-modal'); const scriptDisplayName = document.getElementById('python-script-display-name'); const scriptDescription = document.getElementById('python-script-description'); if (modal && scriptDisplayName && scriptDescription) { scriptDisplayName.textContent = script.display_name; scriptDescription.textContent = script.description || 'Script Python'; // Limpiar campos anteriores document.getElementById('python-script-args-input').value = ''; // Configurar tipo de ejecución por defecto basado en el script const normalRadio = document.querySelector('input[name="python-execution-type"][value="false"]'); const backgroundRadio = document.querySelector('input[name="python-execution-type"][value="true"]'); if (script.requires_background || script.is_server) { backgroundRadio.checked = true; } else { normalRadio.checked = true; } modal.dataset.scriptName = scriptName; modal.classList.remove('hidden'); } } function closePythonScriptOptions() { const modal = document.getElementById('python-script-options-modal'); if (modal) { modal.classList.add('hidden'); } } function executePythonScriptWithOptions() { const modal = document.getElementById('python-script-options-modal'); const argsInput = document.getElementById('python-script-args-input'); const executionTypeInputs = document.getElementsByName('python-execution-type'); if (modal && argsInput) { const scriptName = modal.dataset.scriptName; const args = argsInput.value.trim().split(/\s+/).filter(arg => arg.length > 0); // Leer el tipo de ejecución seleccionado let runInBackground = false; for (const input of executionTypeInputs) { if (input.checked) { runInBackground = input.value === 'true'; break; } } executePythonScript(scriptName, args, runInBackground); closePythonScriptOptions(); } } // === FAVORITOS === async function loadPythonFavorites() { try { const response = await fetch('/api/python-favorites'); if (response.ok) { const data = await response.json(); pythonFavorites = data.favorites || []; updatePythonFavoritesPanel(); console.log('Python favorites loaded:', pythonFavorites.length); } } catch (error) { console.error('Error loading Python favorites:', error); } } async function togglePythonFavorite(projectId, scriptName) { try { const response = await fetch('/api/python-favorites', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ project_id: projectId, script_name: scriptName }) }); const result = await response.json(); if (result.error) { alert(`Error: ${result.error}`); } else { console.log('Favorite toggled:', result.message); loadPythonFavorites(); updatePythonScriptsGrid(); // Actualizar para mostrar cambio de estrella } } catch (error) { console.error('Error toggling Python favorite:', error); } } function updatePythonFavoritesPanel() { const panel = document.getElementById('python-favorites-list'); const counter = document.getElementById('python-favorites-count'); if (!panel || !counter) return; counter.textContent = `${pythonFavorites.length} favoritos`; if (pythonFavorites.length === 0) { panel.innerHTML = '

No tienes scripts favoritos

'; return; } panel.innerHTML = ''; pythonFavorites.forEach(favorite => { const favoriteItem = document.createElement('div'); favoriteItem.className = 'flex items-center justify-between bg-white p-3 rounded border'; favoriteItem.innerHTML = `
${favorite.display_name}
${favorite.project_name} • ${favorite.script_name}
${favorite.description || 'Sin descripción'}
`; panel.appendChild(favoriteItem); }); } async function executePythonFavorite(projectId, scriptName) { // Cambiar al proyecto correcto si es necesario const currentProject = document.getElementById('python-project-select').value; if (currentProject !== projectId) { document.getElementById('python-project-select').value = projectId; await loadPythonScripts(); } // Ejecutar el script executePythonScript(scriptName); } // === CATEGORÍAS Y FILTROS === async function loadPythonCategories() { try { const response = await fetch('/api/python-categories'); if (response.ok) { pythonCategories = await response.json(); console.log('Python categories loaded:', Object.keys(pythonCategories).length); } } catch (error) { console.error('Error loading Python categories:', error); } } function filterPythonByCategory(category) { currentPythonFilter = category; // Actualizar botones de filtro document.querySelectorAll('.python-category-btn').forEach(btn => { btn.classList.remove('active'); if (btn.dataset.category === category) { btn.classList.add('active'); } }); updatePythonScriptsGrid(); } // === PROCESOS EN EJECUCIÓN === async function refreshPythonProcesses() { try { const response = await fetch('/api/python-running-processes'); if (response.ok) { const data = await response.json(); pythonRunningProcesses = data.processes || []; updatePythonProcessesPanel(); } } catch (error) { console.error('Error loading Python processes:', error); } } function updatePythonProcessesPanel() { const panel = document.getElementById('python-running-processes'); if (!panel) return; if (pythonRunningProcesses.length === 0) { panel.innerHTML = '

No hay procesos en ejecución

'; return; } panel.innerHTML = ''; pythonRunningProcesses.forEach(process => { const processItem = document.createElement('div'); processItem.className = 'flex items-center justify-between bg-gray-50 p-3 rounded border'; const startTime = new Date(process.start_time).toLocaleString(); const isBackground = process.is_background; processItem.innerHTML = `
${process.script_name} ${isBackground ? 'BACKGROUND' : ''}
${process.project_name} • PID: ${process.pid}
Iniciado: ${startTime}
`; panel.appendChild(processItem); }); } async function terminatePythonProcess(pid) { if (!confirm(`¿Estás seguro de que quieres terminar el proceso ${pid}?`)) return; try { const response = await fetch(`/api/python-process-terminate/${pid}`, { method: 'POST' }); const result = await response.json(); if (result.error) { alert(`Error: ${result.error}`); } else { console.log('Process terminated:', result.message); refreshPythonProcesses(); } } catch (error) { console.error('Error terminating Python process:', error); } } async function focusPythonProcess(pid) { try { const response = await fetch(`/api/python-process-focus/${pid}`, { method: 'POST' }); const result = await response.json(); console.log('Focus result:', result.message); } catch (error) { console.error('Error focusing Python process:', error); } } // === HISTORIAL === async function loadPythonHistory() { try { const response = await fetch('/api/python-history'); if (response.ok) { const data = await response.json(); pythonHistory = data.history || []; updatePythonHistoryPanel(); } } catch (error) { console.error('Error loading Python history:', error); } } function updatePythonHistoryPanel() { const panel = document.getElementById('python-history-list'); if (!panel) return; if (pythonHistory.length === 0) { panel.innerHTML = '

No hay historial de ejecuciones

'; return; } panel.innerHTML = ''; pythonHistory.slice(0, 10).forEach(entry => { const historyItem = document.createElement('div'); historyItem.className = 'flex items-center justify-between bg-gray-50 p-3 rounded border'; const timestamp = new Date(entry.timestamp).toLocaleString(); const statusIcon = entry.status === 'completed' ? '✅' : entry.status === 'error' ? '❌' : '🔄'; const executionTime = entry.execution_time ? ` (${entry.execution_time.toFixed(2)}s)` : ''; historyItem.innerHTML = `
${statusIcon} ${entry.script_name}${executionTime}
${entry.arguments.length > 0 ? `Args: ${entry.arguments.join(' ')}` : 'Sin argumentos'}
${timestamp}
${entry.python_env || 'base'}
`; panel.appendChild(historyItem); }); } async function clearPythonHistory() { if (!confirm('¿Estás seguro de que quieres limpiar el historial?')) return; try { const response = await fetch('/api/python-history', { method: 'DELETE' }); const result = await response.json(); if (result.error) { alert(`Error: ${result.error}`); } else { console.log('History cleared:', result.message); loadPythonHistory(); } } catch (error) { console.error('Error clearing Python history:', error); } } // === FUNCIONES DE INTEGRACIÓN CON EDITORES === function openPythonProjectInEditor(editor) { if (!selectedPythonProject) { alert('Selecciona un proyecto primero'); return; } openGroupInEditor(editor, 'python', selectedPythonProject.id); } function openPythonProjectFolder() { if (!selectedPythonProject) { alert('Selecciona un proyecto primero'); return; } openGroupFolder('python', selectedPythonProject.id); } async function copyPythonProjectPath() { if (!selectedPythonProject) { alert('Selecciona un proyecto primero'); return; } try { await navigator.clipboard.writeText(selectedPythonProject.directory); // Mostrar mensaje temporal de éxito const button = document.getElementById('copy-path-python-btn'); const originalText = button.textContent; button.textContent = '✓'; setTimeout(() => button.textContent = originalText, 1000); } catch (error) { console.error('Error copying path:', error); alert('Error al copiar el path al portapapeles'); } } // === GESTIÓN DE ARCHIVOS MARKDOWN === function renderPythonMarkdownFiles() { const container = document.getElementById('python-markdown-files-section'); if (!container) return; if (!selectedPythonProject || pythonMarkdownFiles.length === 0) { container.style.display = 'none'; return; } container.style.display = 'block'; const grid = document.getElementById('python-markdown-files-grid'); if (!grid) return; grid.innerHTML = ''; pythonMarkdownFiles.forEach(file => { const card = document.createElement('div'); card.className = 'markdown-file-card bg-white border rounded-lg p-3 hover:shadow-md transition-shadow cursor-pointer'; card.innerHTML = `

${file.title || file.filename}

📄 ${(file.size / 1024).toFixed(1)} KB ${file.relative_path.includes('/') ? ' • Subdirectorio' : ''}

${getTimeAgo(file.modified)}
`; card.onclick = () => openPythonMarkdownViewer(file.relative_path, file.title || file.filename); grid.appendChild(card); }); } async function openPythonMarkdownViewer(relativePath, displayName) { if (!selectedPythonProject) return; try { const response = await fetch(`/api/python-markdown-content/${selectedPythonProject.id}/${encodeURIComponent(relativePath)}`); const result = await response.json(); if (result.content) { const modal = document.getElementById('python-markdown-viewer-modal'); const titleElement = document.getElementById('python-markdown-viewer-title'); const pathElement = document.getElementById('python-markdown-viewer-path'); const contentElement = document.getElementById('python-markdown-viewer-content'); if (modal && titleElement && contentElement) { titleElement.textContent = displayName; pathElement.textContent = `Archivo: ${relativePath}`; // Renderizar markdown const md = window.markdownit(); contentElement.innerHTML = md.render(result.content); modal.classList.remove('hidden'); } } else { alert(`Error: ${result.error || 'Error cargando archivo'}`); } } catch (error) { console.error('Error loading Python markdown file:', error); alert('Error cargando archivo Markdown'); } } function closePythonMarkdownViewer() { const modal = document.getElementById('python-markdown-viewer-modal'); if (modal) { modal.classList.add('hidden'); } } // === GESTIÓN DE DESCRIPCIONES DE SCRIPTS === async function showPythonScriptDescription(scriptName, displayName) { if (!selectedPythonProject) return; try { // Cargar metadatos del script para obtener la descripción larga const response = await fetch(`/api/python-script-metadata/${selectedPythonProject.id}/${scriptName}`); const metadata = await response.json(); const modal = document.getElementById('python-script-description-modal'); const scriptNameElement = document.getElementById('python-desc-modal-script-name'); const scriptFileElement = document.getElementById('python-desc-modal-script-file'); const contentElement = document.getElementById('python-script-description-content'); if (modal && scriptNameElement && contentElement) { scriptNameElement.textContent = displayName; scriptFileElement.textContent = `Archivo: ${scriptName}`; // Renderizar markdown if (metadata.long_description && metadata.long_description.trim()) { const md = window.markdownit(); contentElement.innerHTML = md.render(metadata.long_description); } else { contentElement.innerHTML = '

No hay descripción disponible para este script.

'; } modal.classList.remove('hidden'); } } catch (error) { console.error('Error loading Python script description:', error); alert('Error cargando la descripción del script'); } } function closePythonScriptDescription() { const modal = document.getElementById('python-script-description-modal'); if (modal) { modal.classList.add('hidden'); } } // === GESTIÓN DE PROYECTOS === function openPythonProjectEditor() { const modal = document.getElementById('python-project-editor-modal'); if (modal) { currentEditingPythonProject = null; clearPythonProjectForm(); renderExistingPythonProjects(); modal.classList.remove('hidden'); } } function closePythonProjectEditor() { const modal = document.getElementById('python-project-editor-modal'); if (modal) { modal.classList.add('hidden'); currentEditingPythonProject = null; } } function clearPythonProjectForm() { document.getElementById('python-project-name').value = ''; document.getElementById('python-project-description').value = ''; document.getElementById('python-project-category').value = 'MCP Servers'; document.getElementById('python-project-version').value = '1.0'; document.getElementById('python-project-python-env').value = 'base'; document.getElementById('python-project-directory').value = ''; document.getElementById('delete-python-project-btn').style.display = 'none'; } function populatePythonProjectForm(project) { document.getElementById('python-project-name').value = project.name; document.getElementById('python-project-description').value = project.description || ''; document.getElementById('python-project-category').value = project.category; document.getElementById('python-project-version').value = project.version || '1.0'; document.getElementById('python-project-python-env').value = project.python_env || 'base'; document.getElementById('python-project-directory').value = project.directory; } function renderExistingPythonProjects() { const list = document.getElementById('existing-python-projects-list'); if (!list) return; if (pythonProjects.length === 0) { list.innerHTML = '

No hay proyectos Python creados aún

'; return; } list.innerHTML = ''; pythonProjects.forEach(project => { const item = document.createElement('div'); item.className = 'flex justify-between items-center p-2 border rounded hover:bg-gray-50 cursor-pointer'; item.innerHTML = `
${project.name} (${project.category})
`; list.appendChild(item); }); } function editPythonProject(projectId) { const project = pythonProjects.find(p => p.id === projectId); if (!project) return; currentEditingPythonProject = project; populatePythonProjectForm(project); document.getElementById('delete-python-project-btn').style.display = 'block'; } async function savePythonProject() { const formData = { name: document.getElementById('python-project-name').value.trim(), description: document.getElementById('python-project-description').value.trim(), category: document.getElementById('python-project-category').value, version: document.getElementById('python-project-version').value.trim(), python_env: document.getElementById('python-project-python-env').value, directory: document.getElementById('python-project-directory').value.trim() }; if (!formData.name || !formData.directory) { alert('Por favor, completa todos los campos obligatorios (Nombre, Directorio)'); return; } try { const isEdit = currentEditingPythonProject !== null; const url = isEdit ? `/api/python-projects/${currentEditingPythonProject.id}` : '/api/python-projects'; const method = isEdit ? 'PUT' : 'POST'; const response = await fetch(url, { method: method, headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(formData) }); const result = await response.json(); if (result.status === 'success') { await loadPythonProjects(); closePythonProjectEditor(); alert(isEdit ? 'Proyecto actualizado correctamente' : 'Proyecto creado correctamente'); } else { alert(`Error: ${result.message}`); } } catch (error) { console.error('Error saving Python project:', error); alert('Error al guardar el proyecto'); } } async function deletePythonProject() { if (!currentEditingPythonProject) return; if (!confirm(`¿Estás seguro de que quieres eliminar el proyecto "${currentEditingPythonProject.name}"?`)) return; try { const response = await fetch(`/api/python-projects/${currentEditingPythonProject.id}`, { method: 'DELETE' }); const result = await response.json(); if (result.status === 'success') { await loadPythonProjects(); closePythonProjectEditor(); // Limpiar selección si era el proyecto actual if (selectedPythonProject && selectedPythonProject.id === currentEditingPythonProject.id) { document.getElementById('python-project-select').value = ''; selectedPythonProject = null; pythonScripts = []; pythonMarkdownFiles = []; updatePythonScriptsGrid(); updatePythonProjectButtons(); renderPythonMarkdownFiles(); localStorage.removeItem('python-selected-project'); } alert('Proyecto eliminado correctamente'); } else { alert(`Error: ${result.message}`); } } catch (error) { console.error('Error deleting Python project:', error); alert('Error al eliminar el proyecto'); } } function browsePythonProjectDirectory() { // Similar a la función existente pero para el formulario de proyectos Python fetch('/api/browse-directories') .then(response => response.json()) .then(data => { if (data.status === 'success') { document.getElementById('python-project-directory').value = data.path; } }) .catch(error => { console.error('Error browsing directory:', error); }); } // === GESTIÓN DE SCRIPTS INDIVIDUALES === function openPythonScriptManager() { if (!selectedPythonProject) { alert('Selecciona un proyecto primero'); return; } const modal = document.getElementById('python-script-manager-modal'); const projectInfo = document.getElementById('python-script-manager-project-info'); if (modal && projectInfo) { projectInfo.textContent = `Proyecto: ${selectedPythonProject.name} (${selectedPythonProject.category})`; loadAllPythonScriptsForManagement(); modal.classList.remove('hidden'); } } function closePythonScriptManager() { const modal = document.getElementById('python-script-manager-modal'); if (modal) { modal.classList.add('hidden'); } } async function loadAllPythonScriptsForManagement() { if (!selectedPythonProject) return; try { const response = await fetch(`/api/python-scripts-all/${selectedPythonProject.id}`); const allScripts = await response.json(); renderPythonScriptManagerList(allScripts); } catch (error) { console.error('Error loading all Python scripts for management:', error); } } function renderPythonScriptManagerList(scripts) { const list = document.getElementById('python-script-manager-list'); if (!list) return; if (scripts.length === 0) { list.innerHTML = `

No hay scripts Python en este directorio

`; return; } list.innerHTML = ''; scripts.forEach(script => { const item = document.createElement('div'); item.className = `border rounded-lg p-4 ${script.hidden ? 'bg-gray-50 border-gray-300' : 'bg-white border-gray-200'}`; item.innerHTML = `

${script.display_name}

${script.hidden ? 'Oculto' : ''}

${script.description || 'Sin descripción'}

Archivo: ${script.name}

`; list.appendChild(item); }); } async function editPythonScriptMetadata(scriptName) { if (!selectedPythonProject) return; try { const response = await fetch(`/api/python-script-metadata/${selectedPythonProject.id}/${scriptName}`); const metadata = await response.json(); // Poblar el formulario document.getElementById('edit-python-meta-project-id').value = selectedPythonProject.id; document.getElementById('edit-python-meta-script-name').value = scriptName; document.getElementById('edit-python-meta-filename-display').textContent = scriptName; document.getElementById('edit-python-meta-display-name').value = metadata.display_name || scriptName.replace('.py', ''); document.getElementById('edit-python-meta-description').value = metadata.description || ''; document.getElementById('edit-python-meta-long-description').value = metadata.long_description || ''; document.getElementById('edit-python-meta-hidden').checked = metadata.hidden || false; // Mostrar modal document.getElementById('python-script-metadata-editor-modal').classList.remove('hidden'); } catch (error) { console.error('Error loading Python script metadata:', error); alert('Error cargando metadatos del script'); } } function closePythonScriptMetadataEditor() { const modal = document.getElementById('python-script-metadata-editor-modal'); if (modal) { modal.classList.add('hidden'); } } async function savePythonScriptMetadata() { const formData = { display_name: document.getElementById('edit-python-meta-display-name').value.trim(), description: document.getElementById('edit-python-meta-description').value.trim(), long_description: document.getElementById('edit-python-meta-long-description').value.trim(), hidden: document.getElementById('edit-python-meta-hidden').checked }; const projectId = document.getElementById('edit-python-meta-project-id').value; const scriptName = document.getElementById('edit-python-meta-script-name').value; if (!formData.display_name) { alert('El nombre a mostrar es obligatorio'); return; } try { const response = await fetch(`/api/python-script-metadata/${projectId}/${scriptName}`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(formData) }); const result = await response.json(); if (result.status === 'success') { await loadAllPythonScriptsForManagement(); await loadPythonScripts(); // Recargar scripts visibles también closePythonScriptMetadataEditor(); alert('Metadatos guardados correctamente'); } else { alert(`Error: ${result.message}`); } } catch (error) { console.error('Error saving Python script metadata:', error); alert('Error al guardar metadatos'); } } // === UTILIDADES === function getTimeAgo(dateString) { const now = new Date(); const date = new Date(dateString); const diffMs = now - date; const diffMins = Math.floor(diffMs / (1000 * 60)); const diffHours = Math.floor(diffMs / (1000 * 60 * 60)); const diffDays = Math.floor(diffMs / (1000 * 60 * 60 * 24)); if (diffMins < 60) { return `${diffMins}min`; } else if (diffHours < 24) { return `${diffHours}h`; } else { return `${diffDays}d`; } } // === FUNCIONES GLOBALES REQUERIDAS === // Estas funciones son llamadas desde el HTML pero están implementadas en scripts.js // Solo las declaramos aquí para evitar errores if (typeof openGroupInEditor === 'undefined') { window.openGroupInEditor = function (editor, groupSystem, groupId) { console.log(`Opening ${editor} for ${groupSystem} group ${groupId}`); // Implementación en scripts.js }; } if (typeof openGroupFolder === 'undefined') { window.openGroupFolder = function (groupSystem, groupId) { console.log(`Opening folder for ${groupSystem} group ${groupId}`); // Implementación en scripts.js }; } console.log('Python Launcher JavaScript loaded');