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