1529 lines
59 KiB
JavaScript
1529 lines
59 KiB
JavaScript
// launcher.js - Funcionalidad del Launcher GUI
|
|
|
|
class LauncherManager {
|
|
constructor() {
|
|
this.currentGroup = null;
|
|
this.groups = [];
|
|
this.scripts = [];
|
|
this.favorites = new Set();
|
|
this.history = [];
|
|
this.categories = {};
|
|
this.currentFilter = 'all';
|
|
this.currentEditingGroup = null;
|
|
this.pythonEnvs = [];
|
|
this.markdownFiles = [];
|
|
}
|
|
|
|
async init() {
|
|
console.log('Inicializando Launcher GUI...');
|
|
await this.loadCategories();
|
|
await this.loadPythonEnvironments();
|
|
await this.loadGroups();
|
|
await this.loadFavorites();
|
|
await this.loadHistory();
|
|
this.setupEventListeners();
|
|
this.renderInterface();
|
|
}
|
|
|
|
async loadCategories() {
|
|
try {
|
|
const response = await fetch('/api/launcher-categories');
|
|
this.categories = await response.json();
|
|
} catch (error) {
|
|
console.error('Error loading launcher categories:', error);
|
|
}
|
|
}
|
|
|
|
async loadPythonEnvironments() {
|
|
try {
|
|
const response = await fetch('/api/python-environments');
|
|
this.pythonEnvs = await response.json();
|
|
this.renderPythonEnvSelector();
|
|
} catch (error) {
|
|
console.error('Error loading Python environments:', error);
|
|
}
|
|
}
|
|
|
|
renderPythonEnvSelector() {
|
|
const selector = document.getElementById('group-python-env');
|
|
if (!selector) return;
|
|
|
|
selector.innerHTML = '';
|
|
this.pythonEnvs.forEach(env => {
|
|
const option = document.createElement('option');
|
|
option.value = env.name;
|
|
option.textContent = env.display_name;
|
|
selector.appendChild(option);
|
|
});
|
|
}
|
|
|
|
async loadGroups() {
|
|
try {
|
|
const response = await fetch('/api/launcher-groups');
|
|
this.groups = await response.json();
|
|
this.renderGroupSelector();
|
|
} catch (error) {
|
|
console.error('Error loading launcher groups:', error);
|
|
}
|
|
}
|
|
|
|
async loadFavorites() {
|
|
try {
|
|
const response = await fetch('/api/launcher-favorites');
|
|
const data = await response.json();
|
|
this.favorites = new Set(data.favorites.map(f => `${f.group_id}_${f.script_name}`));
|
|
await this.renderFavorites(data.favorites);
|
|
} catch (error) {
|
|
console.error('Error loading favorites:', error);
|
|
}
|
|
}
|
|
|
|
async loadHistory() {
|
|
try {
|
|
const response = await fetch('/api/launcher-history');
|
|
const data = await response.json();
|
|
this.history = data.history || [];
|
|
this.renderHistory();
|
|
} catch (error) {
|
|
console.error('Error loading history:', error);
|
|
}
|
|
}
|
|
|
|
setupEventListeners() {
|
|
// Event listener para el formulario de grupos
|
|
const groupForm = document.getElementById('group-form');
|
|
if (groupForm) {
|
|
groupForm.addEventListener('submit', (e) => {
|
|
e.preventDefault();
|
|
this.saveGroup();
|
|
});
|
|
}
|
|
|
|
// Event listener para el formulario de metadatos de scripts
|
|
const scriptMetadataForm = document.getElementById('script-metadata-form');
|
|
if (scriptMetadataForm) {
|
|
scriptMetadataForm.addEventListener('submit', (e) => {
|
|
e.preventDefault();
|
|
this.saveScriptMetadata();
|
|
});
|
|
}
|
|
}
|
|
|
|
renderInterface() {
|
|
this.renderGroupSelector();
|
|
this.renderCategoryFilter();
|
|
this.updateFavoritesCount();
|
|
}
|
|
|
|
renderGroupSelector() {
|
|
const selector = document.getElementById('launcher-group-select');
|
|
if (!selector) return;
|
|
|
|
// Guardar la selección actual
|
|
const currentSelection = this.getCurrentGroupSelection();
|
|
|
|
selector.innerHTML = '<option value="">-- Seleccionar Grupo --</option>';
|
|
|
|
// Ordenar grupos alfabéticamente por nombre
|
|
const sortedGroups = [...this.groups].sort((a, b) => a.name.localeCompare(b.name));
|
|
|
|
sortedGroups.forEach(group => {
|
|
const option = document.createElement('option');
|
|
option.value = group.id;
|
|
option.textContent = group.name;
|
|
option.dataset.category = group.category;
|
|
option.dataset.description = group.description;
|
|
selector.appendChild(option);
|
|
});
|
|
|
|
// Restaurar la selección guardada
|
|
this.restoreGroupSelection(currentSelection);
|
|
}
|
|
|
|
// Nuevo método para obtener la selección actual del localStorage
|
|
getCurrentGroupSelection() {
|
|
const currentValue = document.getElementById('launcher-group-select')?.value;
|
|
return localStorage.getItem('launcher-selected-group') || currentValue || '';
|
|
}
|
|
|
|
// Nuevo método para restaurar la selección desde localStorage
|
|
restoreGroupSelection(groupId) {
|
|
const selector = document.getElementById('launcher-group-select');
|
|
if (!selector || !groupId) return;
|
|
|
|
// Verificar que el grupo aún existe
|
|
const groupExists = this.groups.some(g => g.id === groupId);
|
|
if (groupExists) {
|
|
selector.value = groupId;
|
|
// Cargar scripts del grupo restaurado automáticamente
|
|
this.loadLauncherScripts();
|
|
} else {
|
|
// Si el grupo ya no existe, limpiar localStorage
|
|
localStorage.removeItem('launcher-selected-group');
|
|
}
|
|
}
|
|
|
|
renderCategoryFilter() {
|
|
const filterContainer = document.querySelector('.category-filter .flex');
|
|
if (!filterContainer) return;
|
|
|
|
// Limpiar botones existentes excepto "Todas"
|
|
const buttons = filterContainer.querySelectorAll('.category-btn:not([data-category="all"])');
|
|
buttons.forEach(btn => btn.remove());
|
|
|
|
// Agregar botones por categoría
|
|
Object.keys(this.categories).forEach(category => {
|
|
const categoryData = this.categories[category];
|
|
const button = document.createElement('button');
|
|
button.className = 'category-btn px-3 py-1 rounded-full text-sm border';
|
|
button.dataset.category = category;
|
|
button.innerHTML = `${categoryData.icon} ${category}`;
|
|
button.onclick = () => this.filterByCategory(category);
|
|
filterContainer.appendChild(button);
|
|
});
|
|
}
|
|
|
|
async loadLauncherScripts() {
|
|
const groupId = document.getElementById('launcher-group-select').value;
|
|
|
|
// Guardar la selección en localStorage
|
|
if (groupId) {
|
|
localStorage.setItem('launcher-selected-group', groupId);
|
|
} else {
|
|
localStorage.removeItem('launcher-selected-group');
|
|
}
|
|
|
|
if (!groupId) {
|
|
this.scripts = [];
|
|
this.markdownFiles = [];
|
|
this.renderScripts();
|
|
this.renderMarkdownFiles();
|
|
this.updateManageScriptsButton(false);
|
|
this.updateEditorButtons(false);
|
|
return;
|
|
}
|
|
|
|
// Cargar scripts (parte crítica)
|
|
try {
|
|
const response = await fetch(`/api/launcher-scripts/${groupId}`);
|
|
if (response.ok) {
|
|
this.scripts = await response.json();
|
|
} else {
|
|
console.error('Error loading scripts:', response.status, response.statusText);
|
|
this.scripts = [];
|
|
}
|
|
} catch (error) {
|
|
console.error('Error loading launcher scripts:', error);
|
|
this.scripts = [];
|
|
}
|
|
|
|
// Cargar archivos markdown (parte opcional)
|
|
try {
|
|
const markdownResponse = await fetch(`/api/launcher-markdown/${groupId}`);
|
|
if (markdownResponse.ok) {
|
|
const markdownData = await markdownResponse.json();
|
|
this.markdownFiles = markdownData.files || [];
|
|
} else {
|
|
console.warn('No markdown files available for group:', groupId);
|
|
this.markdownFiles = [];
|
|
}
|
|
} catch (error) {
|
|
console.warn('Error loading markdown files (non-critical):', error);
|
|
this.markdownFiles = [];
|
|
}
|
|
|
|
// Actualizar interfaz
|
|
this.currentGroup = this.groups.find(g => g.id === groupId);
|
|
this.updateGroupIcon();
|
|
this.renderScripts();
|
|
this.renderMarkdownFiles();
|
|
this.updateManageScriptsButton(this.scripts.length > 0);
|
|
this.updateEditorButtons(true); // Usar la nueva función
|
|
}
|
|
|
|
updateManageScriptsButton(show) {
|
|
const button = document.getElementById('manage-scripts-btn');
|
|
if (button) {
|
|
button.style.display = show ? 'block' : 'none';
|
|
}
|
|
}
|
|
|
|
// Función actualizada para mostrar/ocultar todos los botones
|
|
updateEditorButtons(show) {
|
|
const vscodeButton = document.getElementById('vscode-launcher-btn');
|
|
const cursorButton = document.getElementById('cursor-launcher-btn');
|
|
const folderButton = document.getElementById('folder-launcher-btn');
|
|
const copyPathButton = document.getElementById('copy-path-launcher-btn');
|
|
|
|
if (vscodeButton) {
|
|
vscodeButton.style.display = show ? 'block' : 'none';
|
|
}
|
|
if (cursorButton) {
|
|
cursorButton.style.display = show ? 'block' : 'none';
|
|
}
|
|
if (folderButton) {
|
|
folderButton.style.display = show ? 'block' : 'none';
|
|
}
|
|
if (copyPathButton) {
|
|
copyPathButton.style.display = show ? 'block' : 'none';
|
|
}
|
|
}
|
|
|
|
updateGroupIcon() {
|
|
const iconElement = document.getElementById('selected-group-icon');
|
|
if (!iconElement || !this.currentGroup) return;
|
|
|
|
// Intentar cargar icono personalizado
|
|
const img = document.createElement('img');
|
|
img.src = `/api/group-icon/launcher/${this.currentGroup.id}`;
|
|
img.className = 'w-6 h-6 rounded';
|
|
img.onerror = () => {
|
|
// Fallback a icono por defecto
|
|
iconElement.innerHTML = this.getDefaultIconForCategory(this.currentGroup.category);
|
|
};
|
|
img.onload = () => {
|
|
iconElement.innerHTML = '';
|
|
iconElement.appendChild(img);
|
|
};
|
|
}
|
|
|
|
getDefaultIconForCategory(category) {
|
|
const icons = {
|
|
'Herramientas': '🔧',
|
|
'Análisis': '📊',
|
|
'Utilidades': '⚙️',
|
|
'Desarrollo': '💻',
|
|
'Visualización': '📈',
|
|
'Otros': '📁'
|
|
};
|
|
return icons[category] || '📁';
|
|
}
|
|
|
|
renderScripts() {
|
|
const grid = document.getElementById('launcher-scripts-grid');
|
|
if (!grid) return;
|
|
|
|
if (this.scripts.length === 0) {
|
|
grid.innerHTML = `
|
|
<div class="col-span-full empty-state">
|
|
<svg fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z"></path>
|
|
</svg>
|
|
<p class="text-lg font-medium">No hay scripts disponibles</p>
|
|
<p class="text-sm">Selecciona un grupo o verifica que el directorio contenga archivos .py</p>
|
|
</div>
|
|
`;
|
|
return;
|
|
}
|
|
|
|
let filteredScripts = this.scripts;
|
|
if (this.currentFilter !== 'all' && this.currentGroup) {
|
|
// Filtrar solo si la categoría del grupo no coincide con el filtro
|
|
const groupCategory = this.currentGroup.category;
|
|
if (groupCategory !== this.currentFilter) {
|
|
filteredScripts = [];
|
|
}
|
|
}
|
|
|
|
grid.innerHTML = '';
|
|
filteredScripts.forEach(script => {
|
|
const favoriteId = `${this.currentGroup.id}_${script.name}`;
|
|
const isFavorite = this.favorites.has(favoriteId);
|
|
|
|
const card = document.createElement('div');
|
|
card.className = `script-card ${isFavorite ? 'favorited' : ''}`;
|
|
card.innerHTML = `
|
|
<div class="flex justify-between items-start mb-2">
|
|
<h4 class="font-medium text-gray-900">${script.display_name}</h4>
|
|
<button class="favorite-star ${isFavorite ? 'active' : ''}"
|
|
onclick="launcherManager.toggleFavorite('${this.currentGroup.id}', '${script.name}')">
|
|
⭐
|
|
</button>
|
|
</div>
|
|
<p class="text-sm text-gray-600 mb-3 line-clamp-2">${script.description || 'Script: ' + script.name}</p>
|
|
<div class="flex justify-between items-center">
|
|
<span class="category-badge">${this.currentGroup.category}</span>
|
|
<div class="flex flex-col gap-1 text-xs">
|
|
<div class="flex gap-1">
|
|
${script.long_description && script.long_description.trim() ? `
|
|
<button class="text-green-600 hover:underline"
|
|
onclick="launcherManager.showDescriptionModal('${script.name}', '${this.escapeHtml(script.display_name)}')">
|
|
Descripción
|
|
</button>` : ''}
|
|
<button class="text-blue-500 hover:underline"
|
|
onclick="launcherManager.showArgsModal('${script.name}', '${this.escapeHtml(script.display_name)}')">
|
|
Con Argumentos
|
|
</button>
|
|
</div>
|
|
<div class="flex gap-1">
|
|
<button class="bg-blue-500 text-white px-2 py-1 rounded hover:bg-blue-600 text-xs"
|
|
onclick="launcherManager.executeScript('${script.name}', [], null, false)"
|
|
title="Ejecutar con python.exe (muestra logs)">
|
|
🖥️ Con Log
|
|
</button>
|
|
<button class="bg-green-500 text-white px-2 py-1 rounded hover:bg-green-600 text-xs"
|
|
onclick="launcherManager.executeScript('${script.name}', [], null, true)"
|
|
title="Ejecutar con pythonw.exe (sin ventana de consola)">
|
|
🚀 Sin Log
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
`;
|
|
grid.appendChild(card);
|
|
});
|
|
}
|
|
|
|
renderMarkdownFiles() {
|
|
const container = document.getElementById('markdown-files-section');
|
|
if (!container) return;
|
|
|
|
if (!this.currentGroup || this.markdownFiles.length === 0) {
|
|
container.style.display = 'none';
|
|
return;
|
|
}
|
|
|
|
container.style.display = 'block';
|
|
const grid = document.getElementById('markdown-files-grid');
|
|
if (!grid) return;
|
|
|
|
grid.innerHTML = '';
|
|
this.markdownFiles.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 = `
|
|
<div class="flex items-start justify-between">
|
|
<div class="flex-1">
|
|
<h4 class="font-medium text-gray-900 text-sm">${file.display_name}</h4>
|
|
<p class="text-xs text-gray-500 mt-1">
|
|
📄 ${(file.size / 1024).toFixed(1)} KB
|
|
${file.level === 1 ? ' • Subdirectorio' : ''}
|
|
</p>
|
|
</div>
|
|
<span class="text-xs text-gray-400">${this.getTimeAgo(file.modified)}</span>
|
|
</div>
|
|
`;
|
|
card.onclick = () => this.openMarkdownViewer(file.relative_path, file.display_name);
|
|
grid.appendChild(card);
|
|
});
|
|
}
|
|
|
|
async openMarkdownViewer(relativePath, displayName) {
|
|
if (!this.currentGroup) return;
|
|
|
|
try {
|
|
const response = await fetch(`/api/launcher-markdown-content/${this.currentGroup.id}/${encodeURIComponent(relativePath)}`);
|
|
const result = await response.json();
|
|
|
|
if (result.status === 'success') {
|
|
const modal = document.getElementById('markdown-viewer-modal');
|
|
const titleElement = document.getElementById('markdown-viewer-title');
|
|
const pathElement = document.getElementById('markdown-viewer-path');
|
|
const contentElement = document.getElementById('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.message}`);
|
|
}
|
|
} catch (error) {
|
|
console.error('Error loading markdown file:', error);
|
|
alert('Error cargando archivo Markdown');
|
|
}
|
|
}
|
|
|
|
escapeHtml(text) {
|
|
const div = document.createElement('div');
|
|
div.textContent = text;
|
|
return div.innerHTML;
|
|
}
|
|
|
|
async showDescriptionModal(scriptName, displayName) {
|
|
if (!this.currentGroup) return;
|
|
|
|
try {
|
|
// Cargar metadatos del script para obtener la descripción larga
|
|
const response = await fetch(`/api/launcher-script-metadata/${this.currentGroup.id}/${scriptName}`);
|
|
const metadata = await response.json();
|
|
|
|
const modal = document.getElementById('script-description-modal');
|
|
const scriptNameElement = document.getElementById('desc-modal-script-name');
|
|
const scriptFileElement = document.getElementById('desc-modal-script-file');
|
|
const contentElement = document.getElementById('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 = '<p class="text-gray-500 italic">No hay descripción disponible para este script.</p>';
|
|
}
|
|
|
|
modal.classList.remove('hidden');
|
|
}
|
|
} catch (error) {
|
|
console.error('Error loading script description:', error);
|
|
alert('Error cargando la descripción del script');
|
|
}
|
|
}
|
|
|
|
// === GESTIÓN DE SCRIPTS INDIVIDUALES ===
|
|
|
|
async openScriptManager() {
|
|
if (!this.currentGroup) {
|
|
alert('Selecciona un grupo primero');
|
|
return;
|
|
}
|
|
|
|
const modal = document.getElementById('script-manager-modal');
|
|
const groupInfo = document.getElementById('script-manager-group-info');
|
|
|
|
if (modal && groupInfo) {
|
|
groupInfo.textContent = `Grupo: ${this.currentGroup.name} (${this.currentGroup.category})`;
|
|
await this.loadAllScriptsForManagement();
|
|
modal.classList.remove('hidden');
|
|
}
|
|
}
|
|
|
|
closeScriptManager() {
|
|
const modal = document.getElementById('script-manager-modal');
|
|
if (modal) {
|
|
modal.classList.add('hidden');
|
|
}
|
|
}
|
|
|
|
async loadAllScriptsForManagement() {
|
|
if (!this.currentGroup) return;
|
|
|
|
try {
|
|
const response = await fetch(`/api/launcher-scripts-all/${this.currentGroup.id}`);
|
|
const allScripts = await response.json();
|
|
this.renderScriptManagerList(allScripts);
|
|
} catch (error) {
|
|
console.error('Error loading all scripts for management:', error);
|
|
}
|
|
}
|
|
|
|
renderScriptManagerList(scripts) {
|
|
const list = document.getElementById('script-manager-list');
|
|
if (!list) return;
|
|
|
|
if (scripts.length === 0) {
|
|
list.innerHTML = `
|
|
<div class="text-center text-gray-500 py-8">
|
|
<p>No hay scripts Python en este directorio</p>
|
|
</div>
|
|
`;
|
|
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 = `
|
|
<div class="flex justify-between items-start">
|
|
<div class="flex-1">
|
|
<div class="flex items-center gap-2 mb-1">
|
|
<h4 class="font-medium ${script.hidden ? 'text-gray-500' : 'text-gray-900'}">${script.display_name}</h4>
|
|
${script.hidden ? '<span class="text-xs bg-gray-200 text-gray-600 px-2 py-1 rounded">Oculto</span>' : ''}
|
|
</div>
|
|
<p class="text-sm text-gray-600 mb-2">${script.description || 'Sin descripción'}</p>
|
|
<p class="text-xs text-gray-500">Archivo: ${script.name}</p>
|
|
</div>
|
|
<div class="flex gap-2">
|
|
<button onclick="launcherManager.editScriptMetadata('${script.name}')"
|
|
class="text-blue-500 hover:underline text-sm">
|
|
Editar
|
|
</button>
|
|
</div>
|
|
</div>
|
|
`;
|
|
list.appendChild(item);
|
|
});
|
|
}
|
|
|
|
async editScriptMetadata(scriptName) {
|
|
if (!this.currentGroup) return;
|
|
|
|
try {
|
|
const response = await fetch(`/api/launcher-script-metadata/${this.currentGroup.id}/${scriptName}`);
|
|
const metadata = await response.json();
|
|
|
|
// Poblar el formulario
|
|
document.getElementById('edit-meta-group-id').value = this.currentGroup.id;
|
|
document.getElementById('edit-meta-script-name').value = scriptName;
|
|
document.getElementById('edit-meta-filename-display').textContent = scriptName;
|
|
document.getElementById('edit-meta-display-name').value = metadata.display_name || scriptName.replace('.py', '');
|
|
document.getElementById('edit-meta-description').value = metadata.description || '';
|
|
document.getElementById('edit-meta-long-description').value = metadata.long_description || '';
|
|
document.getElementById('edit-meta-hidden').checked = metadata.hidden || false;
|
|
|
|
// Mostrar modal
|
|
document.getElementById('script-metadata-editor-modal').classList.remove('hidden');
|
|
} catch (error) {
|
|
console.error('Error loading script metadata:', error);
|
|
alert('Error cargando metadatos del script');
|
|
}
|
|
}
|
|
|
|
closeScriptMetadataEditor() {
|
|
document.getElementById('script-metadata-editor-modal').classList.add('hidden');
|
|
}
|
|
|
|
async saveScriptMetadata() {
|
|
const groupId = document.getElementById('edit-meta-group-id').value;
|
|
const scriptName = document.getElementById('edit-meta-script-name').value;
|
|
const metadata = {
|
|
display_name: document.getElementById('edit-meta-display-name').value,
|
|
description: document.getElementById('edit-meta-description').value,
|
|
long_description: document.getElementById('edit-meta-long-description').value,
|
|
hidden: document.getElementById('edit-meta-hidden').checked
|
|
};
|
|
|
|
try {
|
|
const response = await fetch(`/api/launcher-script-metadata/${groupId}/${scriptName}`, {
|
|
method: 'POST',
|
|
headers: {
|
|
'Content-Type': 'application/json',
|
|
},
|
|
body: JSON.stringify(metadata)
|
|
});
|
|
|
|
const result = await response.json();
|
|
if (result.status === 'success') {
|
|
this.closeScriptMetadataEditor();
|
|
// Recargar datos
|
|
await this.loadAllScriptsForManagement();
|
|
await this.loadLauncherScripts(); // Actualizar la vista principal también
|
|
} else {
|
|
alert(`Error: ${result.message}`);
|
|
}
|
|
} catch (error) {
|
|
console.error('Error saving script metadata:', error);
|
|
alert('Error guardando metadatos del script');
|
|
}
|
|
}
|
|
|
|
// === GESTIÓN DE GRUPOS (actualizada) ===
|
|
|
|
populateGroupForm(group) {
|
|
document.getElementById('group-name').value = group.name;
|
|
document.getElementById('group-description').value = group.description || '';
|
|
document.getElementById('group-category').value = group.category;
|
|
document.getElementById('group-version').value = group.version || '1.0';
|
|
document.getElementById('group-python-env').value = group.python_env || 'base';
|
|
document.getElementById('group-directory').value = group.directory;
|
|
}
|
|
|
|
clearGroupForm() {
|
|
document.getElementById('group-name').value = '';
|
|
document.getElementById('group-description').value = '';
|
|
document.getElementById('group-category').value = 'Otros';
|
|
document.getElementById('group-version').value = '1.0';
|
|
document.getElementById('group-python-env').value = 'base';
|
|
document.getElementById('group-directory').value = '';
|
|
document.getElementById('delete-group-btn').style.display = 'none';
|
|
}
|
|
|
|
async saveGroup() {
|
|
const formData = {
|
|
name: document.getElementById('group-name').value,
|
|
description: document.getElementById('group-description').value,
|
|
category: document.getElementById('group-category').value,
|
|
version: document.getElementById('group-version').value,
|
|
python_env: document.getElementById('group-python-env').value,
|
|
directory: document.getElementById('group-directory').value
|
|
};
|
|
|
|
try {
|
|
let response;
|
|
if (this.currentEditingGroup) {
|
|
// Actualizar grupo existente
|
|
response = await fetch(`/api/launcher-groups/${this.currentEditingGroup.id}`, {
|
|
method: 'PUT',
|
|
headers: {
|
|
'Content-Type': 'application/json',
|
|
},
|
|
body: JSON.stringify(formData)
|
|
});
|
|
} else {
|
|
// Crear nuevo grupo
|
|
response = await fetch('/api/launcher-groups', {
|
|
method: 'POST',
|
|
headers: {
|
|
'Content-Type': 'application/json',
|
|
},
|
|
body: JSON.stringify(formData)
|
|
});
|
|
}
|
|
|
|
const result = await response.json();
|
|
if (result.status === 'success') {
|
|
await this.loadGroups();
|
|
this.closeGroupEditor();
|
|
this.renderInterface();
|
|
} else {
|
|
alert(`Error: ${result.message}`);
|
|
}
|
|
} catch (error) {
|
|
console.error('Error saving group:', error);
|
|
alert('Error al guardar el grupo');
|
|
}
|
|
}
|
|
|
|
renderExistingGroups() {
|
|
const container = document.getElementById('existing-groups-list');
|
|
if (!container) return;
|
|
|
|
container.innerHTML = '';
|
|
this.groups.forEach(group => {
|
|
const envInfo = this.pythonEnvs.find(env => env.name === group.python_env);
|
|
const envDisplay = envInfo ? envInfo.display_name : group.python_env;
|
|
|
|
const item = document.createElement('div');
|
|
item.className = 'group-list-item';
|
|
item.innerHTML = `
|
|
<div class="group-icon-small default">
|
|
${this.getDefaultIconForCategory(group.category)}
|
|
</div>
|
|
<div class="group-info">
|
|
<div class="group-name">${group.name}</div>
|
|
<div class="group-category">${group.category} • ${envDisplay}</div>
|
|
</div>
|
|
<button class="text-blue-500 hover:underline text-sm" onclick="launcherManager.editGroup('${group.id}')">
|
|
Editar
|
|
</button>
|
|
`;
|
|
container.appendChild(item);
|
|
});
|
|
}
|
|
|
|
renderHistory() {
|
|
const historyList = document.getElementById('history-list');
|
|
if (!historyList) return;
|
|
|
|
if (this.history.length === 0) {
|
|
historyList.innerHTML = `
|
|
<div class="text-center text-gray-500 py-4">
|
|
<p>No hay ejecuciones recientes</p>
|
|
</div>
|
|
`;
|
|
return;
|
|
}
|
|
|
|
historyList.innerHTML = '';
|
|
this.history.slice(0, 10).forEach(entry => {
|
|
const group = this.groups.find(g => g.id === entry.group_id);
|
|
const groupName = group ? group.name : 'Grupo desconocido';
|
|
|
|
const timeAgo = this.getTimeAgo(entry.executed_date);
|
|
const statusClass = entry.status === 'success' ? 'success' :
|
|
entry.status === 'error' ? 'error' : 'running';
|
|
|
|
// Iconos y mensajes más descriptivos por status
|
|
let statusIcon, statusText;
|
|
switch (entry.status) {
|
|
case 'success':
|
|
statusIcon = '✅';
|
|
statusText = 'Completado';
|
|
break;
|
|
case 'error':
|
|
statusIcon = '❌';
|
|
statusText = 'Error';
|
|
break;
|
|
case 'running':
|
|
statusIcon = '🔄';
|
|
statusText = 'En ejecución (GUI activa)';
|
|
break;
|
|
default:
|
|
statusIcon = '❓';
|
|
statusText = entry.status;
|
|
}
|
|
|
|
// Información del entorno Python y ejecutable
|
|
const envInfo = entry.python_env ? ` • ${entry.python_env}` : '';
|
|
const executableType = entry.executable_type || 'python.exe';
|
|
const executableIcon = executableType.includes('pythonw') ? '🚀' : '🖥️';
|
|
|
|
// Botones para procesos en ejecución
|
|
const processButtons = entry.status === 'running' && entry.pid ? `
|
|
<div class="flex gap-1 mt-2">
|
|
<button onclick="launcherManager.focusProcess(${entry.pid})"
|
|
class="text-blue-500 hover:underline text-xs">
|
|
📍 Activar
|
|
</button>
|
|
<button onclick="launcherManager.terminateProcess(${entry.pid})"
|
|
class="text-red-500 hover:underline text-xs">
|
|
❌ Cerrar
|
|
</button>
|
|
</div>
|
|
` : '';
|
|
|
|
const item = document.createElement('div');
|
|
item.className = `history-item ${statusClass}`;
|
|
item.innerHTML = `
|
|
<div class="flex justify-between items-start">
|
|
<div>
|
|
<span class="font-medium">${entry.script_name.replace('.py', '')}</span>
|
|
<span class="text-sm text-gray-500 ml-2">${groupName}${envInfo}</span>
|
|
</div>
|
|
<span class="text-xs text-gray-400">${timeAgo}</span>
|
|
</div>
|
|
<div class="text-sm text-gray-600 mt-1">
|
|
${statusIcon} ${statusText}
|
|
${entry.execution_time ? ` • ${entry.execution_time.toFixed(1)}s` : ''}
|
|
${entry.arguments && entry.arguments.length > 0 ? ` • Con argumentos` : ''}
|
|
</div>
|
|
<div class="text-xs text-gray-500 mt-1">
|
|
${executableIcon} ${executableType}${entry.working_directory ? ` • ${entry.working_directory}` : ''}
|
|
</div>
|
|
${processButtons}
|
|
`;
|
|
historyList.appendChild(item);
|
|
});
|
|
}
|
|
|
|
// ... métodos existentes sin cambios ...
|
|
|
|
// El resto de métodos permanecen igual
|
|
filterByCategory(category) {
|
|
this.currentFilter = category;
|
|
|
|
// Actualizar botones activos
|
|
document.querySelectorAll('.category-btn').forEach(btn => {
|
|
btn.classList.remove('active');
|
|
});
|
|
document.querySelector(`[data-category="${category}"]`).classList.add('active');
|
|
|
|
// Filtrar grupos en el selector
|
|
const selector = document.getElementById('launcher-group-select');
|
|
Array.from(selector.options).forEach(option => {
|
|
if (option.value === '') return;
|
|
option.style.display = (category === 'all' || option.dataset.category === category) ? '' : 'none';
|
|
});
|
|
|
|
// Re-renderizar scripts si hay grupo seleccionado
|
|
if (this.currentGroup) {
|
|
this.renderScripts();
|
|
}
|
|
}
|
|
|
|
async toggleFavorite(groupId, scriptName) {
|
|
try {
|
|
const response = await fetch('/api/launcher-favorites', {
|
|
method: 'POST',
|
|
headers: {
|
|
'Content-Type': 'application/json',
|
|
},
|
|
body: JSON.stringify({
|
|
group_id: groupId,
|
|
script_name: scriptName
|
|
})
|
|
});
|
|
|
|
const result = await response.json();
|
|
if (result.status === 'success') {
|
|
const favoriteId = `${groupId}_${scriptName}`;
|
|
if (result.action === 'added') {
|
|
this.favorites.add(favoriteId);
|
|
} else {
|
|
this.favorites.delete(favoriteId);
|
|
}
|
|
|
|
// Recargar datos y re-renderizar
|
|
await this.loadFavorites();
|
|
this.renderScripts();
|
|
this.updateFavoritesCount();
|
|
}
|
|
} catch (error) {
|
|
console.error('Error toggling favorite:', error);
|
|
}
|
|
}
|
|
|
|
showArgsModal(scriptName, displayName) {
|
|
const modal = document.getElementById('script-args-modal');
|
|
const scriptDisplayElement = document.getElementById('script-display-name');
|
|
const argsInput = document.getElementById('script-args-input');
|
|
const workingDirInput = document.getElementById('script-working-dir');
|
|
|
|
if (modal && scriptDisplayElement && argsInput) {
|
|
scriptDisplayElement.textContent = displayName;
|
|
argsInput.value = '';
|
|
workingDirInput.value = ''; // Limpiar directorio de trabajo
|
|
modal.classList.remove('hidden');
|
|
|
|
// Guardar datos para uso posterior
|
|
modal.dataset.scriptName = scriptName;
|
|
modal.dataset.groupId = this.currentGroup.id;
|
|
}
|
|
}
|
|
|
|
async executeScript(scriptName, args = [], workingDir = null, usePythonw = false) {
|
|
if (!this.currentGroup) return;
|
|
|
|
try {
|
|
const requestData = {
|
|
group_id: this.currentGroup.id,
|
|
script_name: scriptName,
|
|
args: args,
|
|
use_pythonw: usePythonw
|
|
};
|
|
|
|
// Agregar directorio de trabajo si se especifica
|
|
if (workingDir && workingDir.trim()) {
|
|
requestData.working_dir = workingDir.trim();
|
|
}
|
|
|
|
const response = await fetch('/api/execute-gui-script', {
|
|
method: 'POST',
|
|
headers: {
|
|
'Content-Type': 'application/json',
|
|
},
|
|
body: JSON.stringify(requestData)
|
|
});
|
|
|
|
const result = await response.json();
|
|
if (result.status === 'success') {
|
|
// Recargar historial
|
|
await this.loadHistory();
|
|
}
|
|
} catch (error) {
|
|
console.error('Error executing script:', error);
|
|
}
|
|
}
|
|
|
|
// Función para buscar directorio de trabajo
|
|
browseWorkingDirectory() {
|
|
fetch('/api/browse-directories')
|
|
.then(response => response.json())
|
|
.then(data => {
|
|
if (data.status === 'success') {
|
|
document.getElementById('script-working-dir').value = data.path;
|
|
}
|
|
})
|
|
.catch(error => {
|
|
console.error('Error browsing directory:', error);
|
|
});
|
|
}
|
|
|
|
async renderFavorites(favorites) {
|
|
const favoritesList = document.getElementById('favorites-list');
|
|
const favoritesPanel = document.getElementById('favorites-panel');
|
|
|
|
if (!favoritesList || !favoritesPanel) return;
|
|
|
|
if (favorites.length === 0) {
|
|
favoritesPanel.classList.add('empty');
|
|
return;
|
|
}
|
|
|
|
favoritesPanel.classList.remove('empty');
|
|
favoritesList.innerHTML = '';
|
|
|
|
// Cambiar a diseño de grid para cards más compactas
|
|
favoritesList.className = 'grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-3';
|
|
|
|
// Obtener metadatos de todos los scripts favoritos
|
|
for (const fav of favorites.slice(0, 6)) { // Mostrar hasta 6 favoritos en grid
|
|
const group = this.groups.find(g => g.id === fav.group_id);
|
|
if (!group) continue;
|
|
|
|
try {
|
|
// Obtener metadatos del script para mostrar el display_name
|
|
const response = await fetch(`/api/launcher-script-metadata/${fav.group_id}/${fav.script_name}`);
|
|
const metadata = await response.json();
|
|
const displayName = metadata.display_name || fav.script_name.replace('.py', '');
|
|
|
|
const card = document.createElement('div');
|
|
card.className = 'bg-white border border-yellow-300 rounded-lg p-3 hover:shadow-md transition-all duration-200 hover:border-yellow-400';
|
|
|
|
// Crear contenedor para el icono que se actualizará dinámicamente
|
|
const iconId = `fav-icon-${fav.group_id}`;
|
|
|
|
card.innerHTML = `
|
|
<div class="flex items-start justify-between mb-2">
|
|
<div class="flex items-center min-w-0 flex-1">
|
|
<div id="${iconId}" class="group-icon-small default mr-2 flex-shrink-0">
|
|
${this.getDefaultIconForCategory(group.category)}
|
|
</div>
|
|
<div class="min-w-0 flex-1">
|
|
<div class="font-medium text-sm text-gray-900 truncate" title="${displayName}">
|
|
${displayName}
|
|
</div>
|
|
<div class="text-xs text-gray-500 truncate" title="${group.name}">
|
|
${group.name}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<button class="favorite-star active text-yellow-500 ml-2 flex-shrink-0"
|
|
onclick="launcherManager.toggleFavorite('${fav.group_id}', '${fav.script_name}')"
|
|
title="Quitar de favoritos">
|
|
⭐
|
|
</button>
|
|
</div>
|
|
<div class="flex gap-1">
|
|
<button class="bg-blue-500 text-white px-2 py-1 rounded text-xs hover:bg-blue-600 flex-1"
|
|
onclick="launcherManager.executeFavoriteScript('${fav.group_id}', '${fav.script_name}')"
|
|
title="Ejecutar script">
|
|
🖥️ Ejecutar
|
|
</button>
|
|
<button class="bg-green-500 text-white px-2 py-1 rounded text-xs hover:bg-green-600 flex-1"
|
|
onclick="launcherManager.executeFavoriteScriptSilent('${fav.group_id}', '${fav.script_name}')"
|
|
title="Ejecutar sin ventana de consola">
|
|
🔇 Silencioso
|
|
</button>
|
|
</div>
|
|
`;
|
|
favoritesList.appendChild(card);
|
|
|
|
// Intentar cargar el icono personalizado del grupo después de agregar la card
|
|
this.loadGroupIconForFavorite(iconId, fav.group_id, group.category);
|
|
|
|
} catch (error) {
|
|
console.error('Error loading script metadata for favorite:', error);
|
|
// Fallback al nombre del archivo
|
|
const card = document.createElement('div');
|
|
card.className = 'bg-white border border-yellow-300 rounded-lg p-3 hover:shadow-md transition-all duration-200 hover:border-yellow-400';
|
|
|
|
const iconId = `fav-icon-${fav.group_id}`;
|
|
|
|
card.innerHTML = `
|
|
<div class="flex items-start justify-between mb-2">
|
|
<div class="flex items-center min-w-0 flex-1">
|
|
<div id="${iconId}" class="group-icon-small default mr-2 flex-shrink-0">
|
|
${this.getDefaultIconForCategory(group.category)}
|
|
</div>
|
|
<div class="min-w-0 flex-1">
|
|
<div class="font-medium text-sm text-gray-900 truncate" title="${fav.script_name.replace('.py', '')}">
|
|
${fav.script_name.replace('.py', '')}
|
|
</div>
|
|
<div class="text-xs text-gray-500 truncate" title="${group.name}">
|
|
${group.name}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<button class="favorite-star active text-yellow-500 ml-2 flex-shrink-0"
|
|
onclick="launcherManager.toggleFavorite('${fav.group_id}', '${fav.script_name}')"
|
|
title="Quitar de favoritos">
|
|
⭐
|
|
</button>
|
|
</div>
|
|
<div class="flex gap-1">
|
|
<button class="bg-blue-500 text-white px-2 py-1 rounded text-xs hover:bg-blue-600 flex-1"
|
|
onclick="launcherManager.executeFavoriteScript('${fav.group_id}', '${fav.script_name}')"
|
|
title="Ejecutar script">
|
|
🖥️ Ejecutar
|
|
</button>
|
|
<button class="bg-green-500 text-white px-2 py-1 rounded text-xs hover:bg-green-600 flex-1"
|
|
onclick="launcherManager.executeFavoriteScriptSilent('${fav.group_id}', '${fav.script_name}')"
|
|
title="Ejecutar sin ventana de consola">
|
|
🔇 Silencioso
|
|
</button>
|
|
</div>
|
|
`;
|
|
favoritesList.appendChild(card);
|
|
|
|
// Intentar cargar el icono personalizado del grupo
|
|
this.loadGroupIconForFavorite(iconId, fav.group_id, group.category);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Nuevo método para cargar iconos específicos de grupo en favoritos
|
|
loadGroupIconForFavorite(iconElementId, groupId, fallbackCategory) {
|
|
const iconElement = document.getElementById(iconElementId);
|
|
if (!iconElement) return;
|
|
|
|
// Intentar cargar icono personalizado
|
|
const img = document.createElement('img');
|
|
img.src = `/api/group-icon/launcher/${groupId}`;
|
|
img.className = 'w-5 h-5 rounded object-cover';
|
|
img.onerror = () => {
|
|
// Fallback a icono por defecto basado en categoría
|
|
iconElement.innerHTML = this.getDefaultIconForCategory(fallbackCategory);
|
|
iconElement.className = 'group-icon-small default mr-2 flex-shrink-0';
|
|
};
|
|
img.onload = () => {
|
|
// Reemplazar con imagen personalizada
|
|
iconElement.innerHTML = '';
|
|
iconElement.className = 'mr-2 flex-shrink-0 flex items-center justify-center';
|
|
iconElement.appendChild(img);
|
|
};
|
|
}
|
|
|
|
// Nuevo método para ejecutar favoritos en modo silencioso
|
|
async executeFavoriteScriptSilent(groupId, scriptName) {
|
|
// Cambiar al grupo correcto si no está seleccionado
|
|
if (!this.currentGroup || this.currentGroup.id !== groupId) {
|
|
document.getElementById('launcher-group-select').value = groupId;
|
|
localStorage.setItem('launcher-selected-group', groupId);
|
|
await this.loadLauncherScripts();
|
|
}
|
|
|
|
this.executeScript(scriptName, [], null, true); // true para usar pythonw
|
|
}
|
|
|
|
async executeFavoriteScript(groupId, scriptName) {
|
|
// Cambiar al grupo correcto si no está seleccionado
|
|
if (!this.currentGroup || this.currentGroup.id !== groupId) {
|
|
document.getElementById('launcher-group-select').value = groupId;
|
|
localStorage.setItem('launcher-selected-group', groupId);
|
|
await this.loadLauncherScripts();
|
|
}
|
|
|
|
this.executeScript(scriptName);
|
|
}
|
|
|
|
getTimeAgo(dateString) {
|
|
const date = new Date(dateString);
|
|
const now = new Date();
|
|
const diffMs = now - date;
|
|
const diffMinutes = Math.floor(diffMs / 60000);
|
|
|
|
if (diffMinutes < 1) return 'ahora';
|
|
if (diffMinutes < 60) return `hace ${diffMinutes}m`;
|
|
if (diffMinutes < 1440) return `hace ${Math.floor(diffMinutes / 60)}h`;
|
|
return `hace ${Math.floor(diffMinutes / 1440)}d`;
|
|
}
|
|
|
|
updateFavoritesCount() {
|
|
const countElement = document.getElementById('favorites-count');
|
|
if (countElement) {
|
|
countElement.textContent = `${this.favorites.size} favoritos`;
|
|
}
|
|
}
|
|
|
|
async clearLauncherHistory() {
|
|
// Eliminada la confirmación - directamente procede a limpiar
|
|
try {
|
|
const response = await fetch('/api/launcher-history', {
|
|
method: 'DELETE'
|
|
});
|
|
|
|
const result = await response.json();
|
|
if (result.status === 'success') {
|
|
this.history = [];
|
|
this.renderHistory();
|
|
console.log('Historial del launcher limpiado');
|
|
}
|
|
} catch (error) {
|
|
console.error('Error clearing history:', error);
|
|
}
|
|
}
|
|
|
|
// === GESTIÓN DE GRUPOS ===
|
|
|
|
openGroupEditor() {
|
|
const modal = document.getElementById('group-editor-modal');
|
|
if (modal) {
|
|
this.currentEditingGroup = null;
|
|
this.clearGroupForm();
|
|
this.renderExistingGroups();
|
|
modal.classList.remove('hidden');
|
|
}
|
|
}
|
|
|
|
closeGroupEditor() {
|
|
const modal = document.getElementById('group-editor-modal');
|
|
if (modal) {
|
|
modal.classList.add('hidden');
|
|
this.currentEditingGroup = null;
|
|
}
|
|
}
|
|
|
|
editGroup(groupId) {
|
|
const group = this.groups.find(g => g.id === groupId);
|
|
if (!group) return;
|
|
|
|
this.currentEditingGroup = group;
|
|
this.populateGroupForm(group);
|
|
document.getElementById('delete-group-btn').style.display = 'block';
|
|
}
|
|
|
|
async deleteGroup() {
|
|
if (!this.currentEditingGroup) return;
|
|
|
|
if (!confirm(`¿Estás seguro de que quieres eliminar el grupo "${this.currentEditingGroup.name}"?`)) return;
|
|
|
|
try {
|
|
const response = await fetch(`/api/launcher-groups/${this.currentEditingGroup.id}`, {
|
|
method: 'DELETE'
|
|
});
|
|
|
|
const result = await response.json();
|
|
if (result.status === 'success') {
|
|
await this.loadGroups();
|
|
this.closeGroupEditor();
|
|
this.renderInterface();
|
|
|
|
// Limpiar selección si era el grupo actual
|
|
if (this.currentGroup && this.currentGroup.id === this.currentEditingGroup.id) {
|
|
document.getElementById('launcher-group-select').value = '';
|
|
this.currentGroup = null;
|
|
this.scripts = [];
|
|
this.renderScripts();
|
|
this.updateManageScriptsButton(false);
|
|
this.updateEditorButtons(false); // Ocultar botones de editor
|
|
}
|
|
} else {
|
|
alert(`Error: ${result.message}`);
|
|
}
|
|
} catch (error) {
|
|
console.error('Error deleting group:', error);
|
|
alert('Error al eliminar el grupo');
|
|
}
|
|
}
|
|
|
|
browseGroupDirectory() {
|
|
// Similar a la función existente pero para el formulario de grupos
|
|
fetch('/api/browse-directories')
|
|
.then(response => response.json())
|
|
.then(data => {
|
|
if (data.status === 'success') {
|
|
document.getElementById('group-directory').value = data.path;
|
|
}
|
|
})
|
|
.catch(error => {
|
|
console.error('Error browsing directory:', error);
|
|
});
|
|
}
|
|
|
|
// Nueva función genérica para abrir en editor
|
|
async openGroupInEditor(editorCode, groupSystem, groupId) {
|
|
if (!groupId) {
|
|
alert('Selecciona un grupo primero');
|
|
return;
|
|
}
|
|
|
|
const editorName = editorCode.toUpperCase();
|
|
|
|
try {
|
|
const response = await fetch(`/api/open-editor/${editorCode}/${groupSystem}/${groupId}`, {
|
|
method: 'POST'
|
|
});
|
|
|
|
if (!response.ok) {
|
|
// If response is not OK, it might not be JSON (e.g., Flask error page)
|
|
const errorText = await response.text();
|
|
console.error(`Error al abrir ${editorName}: HTTP ${response.status}`, errorText);
|
|
alert(`Error al abrir ${editorName}: ${response.status} ${response.statusText}\n${errorText.substring(0, 200)}...`); // Show limited error text
|
|
} else {
|
|
const result = await response.json(); // Now it's safer to parse JSON
|
|
if (result.status === 'success') {
|
|
console.log(result.message);
|
|
// Opcional: mostrar un toast/notificación de éxito
|
|
// showToast(result.message, 'success');
|
|
} else {
|
|
console.error(`Error al abrir ${editorName}:`, result.message);
|
|
alert(`Error: ${result.message || 'Error desconocido.'}`);
|
|
}
|
|
}
|
|
} catch (error) {
|
|
console.error(`Error en la llamada fetch para open-editor (${editorName}):`, error);
|
|
alert(`Error de red o del cliente al intentar abrir ${editorName}.`);
|
|
}
|
|
}
|
|
|
|
|
|
async focusProcess(pid) {
|
|
try {
|
|
const response = await fetch(`/api/launcher-process-focus/${pid}`, {
|
|
method: 'POST'
|
|
});
|
|
const result = await response.json();
|
|
|
|
if (result.status === 'success') {
|
|
console.log(result.message);
|
|
} else {
|
|
alert(`Error: ${result.message}`);
|
|
}
|
|
} catch (error) {
|
|
console.error('Error focusing process:', error);
|
|
alert('Error activando proceso');
|
|
}
|
|
}
|
|
|
|
async terminateProcess(pid) {
|
|
// Eliminada la confirmación - directamente procede a cerrar
|
|
try {
|
|
const response = await fetch(`/api/launcher-process-terminate/${pid}`, {
|
|
method: 'POST'
|
|
});
|
|
const result = await response.json();
|
|
|
|
if (result.status === 'success') {
|
|
console.log(result.message);
|
|
// Recargar historial para actualizar estado
|
|
await this.loadHistory();
|
|
} else {
|
|
alert(`Error: ${result.message}`);
|
|
}
|
|
} catch (error) {
|
|
console.error('Error terminating process:', error);
|
|
alert('Error cerrando proceso');
|
|
}
|
|
}
|
|
|
|
// Nueva función para abrir carpeta del grupo launcher
|
|
async openGroupFolder() {
|
|
if (!this.currentGroup) {
|
|
alert('Selecciona un grupo primero');
|
|
return;
|
|
}
|
|
|
|
try {
|
|
const response = await fetch(`/api/open-group-folder/launcher/${this.currentGroup.id}`, {
|
|
method: 'POST'
|
|
});
|
|
|
|
const result = await response.json();
|
|
if (result.status === 'success') {
|
|
console.log(`Carpeta abierta: ${result.path}`);
|
|
} else {
|
|
alert(`Error al abrir carpeta: ${result.message}`);
|
|
}
|
|
} catch (error) {
|
|
console.error('Error opening folder:', error);
|
|
alert('Error al comunicarse con el servidor');
|
|
}
|
|
}
|
|
|
|
// Nueva función para copiar path del grupo launcher
|
|
async copyGroupPath() {
|
|
if (!this.currentGroup) {
|
|
alert('Selecciona un grupo primero');
|
|
return;
|
|
}
|
|
|
|
try {
|
|
const response = await fetch(`/api/get-group-path/launcher/${this.currentGroup.id}`);
|
|
const result = await response.json();
|
|
|
|
if (result.status === 'success') {
|
|
// Copiar al portapapeles usando la API moderna
|
|
if (navigator.clipboard && navigator.clipboard.writeText) {
|
|
await navigator.clipboard.writeText(result.path);
|
|
console.log('Path copiado al portapapeles');
|
|
} else {
|
|
// Fallback para navegadores más antiguos
|
|
const textArea = document.createElement('textarea');
|
|
textArea.value = result.path;
|
|
document.body.appendChild(textArea);
|
|
textArea.focus();
|
|
textArea.select();
|
|
|
|
try {
|
|
document.execCommand('copy');
|
|
console.log('Path copiado al portapapeles');
|
|
} catch (err) {
|
|
console.error('Error copying to clipboard:', err);
|
|
alert(`Error al copiar. Path: ${result.path}`);
|
|
}
|
|
|
|
document.body.removeChild(textArea);
|
|
}
|
|
} else {
|
|
alert(`Error al obtener path: ${result.message}`);
|
|
}
|
|
} catch (error) {
|
|
console.error('Error getting path:', error);
|
|
alert('Error al comunicarse con el servidor');
|
|
}
|
|
}
|
|
}
|
|
|
|
// === FUNCIONES GLOBALES ===
|
|
|
|
// Función para cambiar entre tabs
|
|
function switchTab(tabName) {
|
|
// Cambiar tabs activos
|
|
document.querySelectorAll('.tab-button').forEach(btn => {
|
|
btn.classList.remove('active');
|
|
});
|
|
document.getElementById(`${tabName}-tab`).classList.add('active');
|
|
|
|
// Cambiar contenido
|
|
document.querySelectorAll('.tab-content').forEach(content => {
|
|
content.classList.add('hidden');
|
|
});
|
|
document.getElementById(`${tabName}-content`).classList.remove('hidden');
|
|
|
|
// Inicializar launcher si es la primera vez
|
|
if (tabName === 'launcher' && !window.launcherManager) {
|
|
window.launcherManager = new LauncherManager();
|
|
window.launcherManager.init();
|
|
}
|
|
|
|
// Inicializar C# launcher si es la primera vez
|
|
if (tabName === 'csharp') {
|
|
if (!window.csharpLauncherManager) {
|
|
console.error('csharpLauncherManager not found! Make sure csharp_launcher.js is loaded.');
|
|
return;
|
|
}
|
|
if (!window.csharpLauncherManager.initialized) {
|
|
window.csharpLauncherManager.init();
|
|
}
|
|
}
|
|
|
|
// Inicializar Python launcher si es la primera vez
|
|
if (tabName === 'python') {
|
|
if (typeof initPythonLauncher === 'function') {
|
|
initPythonLauncher();
|
|
} else {
|
|
console.error('initPythonLauncher function not found! Make sure python_launcher.js is loaded.');
|
|
}
|
|
}
|
|
}
|
|
|
|
// Funciones para modales
|
|
function openGroupEditor() {
|
|
if (window.launcherManager) {
|
|
window.launcherManager.openGroupEditor();
|
|
}
|
|
}
|
|
|
|
function closeGroupEditor() {
|
|
if (window.launcherManager) {
|
|
window.launcherManager.closeGroupEditor();
|
|
}
|
|
}
|
|
|
|
function deleteGroup() {
|
|
if (window.launcherManager) {
|
|
window.launcherManager.deleteGroup();
|
|
}
|
|
}
|
|
|
|
function browseGroupDirectory() {
|
|
if (window.launcherManager) {
|
|
window.launcherManager.browseGroupDirectory();
|
|
}
|
|
}
|
|
|
|
function filterByCategory(category) {
|
|
if (window.launcherManager) {
|
|
window.launcherManager.filterByCategory(category);
|
|
}
|
|
}
|
|
|
|
function loadLauncherScripts() {
|
|
if (window.launcherManager) {
|
|
window.launcherManager.loadLauncherScripts();
|
|
}
|
|
}
|
|
|
|
function clearLauncherHistory() {
|
|
if (window.launcherManager) {
|
|
window.launcherManager.clearLauncherHistory();
|
|
}
|
|
}
|
|
|
|
// Funciones para gestión de scripts
|
|
function openScriptManager() {
|
|
if (window.launcherManager) {
|
|
window.launcherManager.openScriptManager();
|
|
}
|
|
}
|
|
|
|
function closeScriptManager() {
|
|
if (window.launcherManager) {
|
|
window.launcherManager.closeScriptManager();
|
|
}
|
|
}
|
|
|
|
function closeScriptMetadataEditor() {
|
|
if (window.launcherManager) {
|
|
window.launcherManager.closeScriptMetadataEditor();
|
|
}
|
|
}
|
|
|
|
// Funciones para modal de argumentos
|
|
function closeArgsModal() {
|
|
const modal = document.getElementById('script-args-modal');
|
|
if (modal) {
|
|
modal.classList.add('hidden');
|
|
}
|
|
}
|
|
|
|
function executeWithArgs() {
|
|
const modal = document.getElementById('script-args-modal');
|
|
const argsInput = document.getElementById('script-args-input');
|
|
const workingDirInput = document.getElementById('script-working-dir');
|
|
const executionTypeInputs = document.getElementsByName('execution-type');
|
|
|
|
if (modal && argsInput && window.launcherManager) {
|
|
const scriptName = modal.dataset.scriptName;
|
|
const args = argsInput.value.trim().split(/\s+/).filter(arg => arg.length > 0);
|
|
const workingDir = workingDirInput.value.trim();
|
|
|
|
// Leer el tipo de ejecución seleccionado
|
|
let usePythonw = false;
|
|
for (const input of executionTypeInputs) {
|
|
if (input.checked) {
|
|
usePythonw = input.value === 'true';
|
|
break;
|
|
}
|
|
}
|
|
|
|
window.launcherManager.executeScript(scriptName, args, workingDir, usePythonw);
|
|
closeArgsModal();
|
|
}
|
|
}
|
|
|
|
function browseWorkingDirectory() {
|
|
if (window.launcherManager) {
|
|
window.launcherManager.browseWorkingDirectory();
|
|
}
|
|
}
|
|
|
|
// Funciones para modal de descripción
|
|
function closeDescriptionModal() {
|
|
const modal = document.getElementById('script-description-modal');
|
|
if (modal) {
|
|
modal.classList.add('hidden');
|
|
}
|
|
}
|
|
|
|
function closeMarkdownViewer() {
|
|
const modal = document.getElementById('markdown-viewer-modal');
|
|
if (modal) {
|
|
modal.classList.add('hidden');
|
|
}
|
|
}
|
|
|
|
// Inicialización cuando se carga la página
|
|
document.addEventListener('DOMContentLoaded', function () {
|
|
console.log('Launcher JS loaded');
|
|
});
|
|
|
|
// Función para colapsar/expandir el historial
|
|
function toggleHistoryPanel() {
|
|
const historyList = document.getElementById('history-list');
|
|
const toggleIcon = document.getElementById('history-toggle-icon');
|
|
|
|
if (historyList && toggleIcon) {
|
|
if (historyList.classList.contains('hidden')) {
|
|
// Expandir
|
|
historyList.classList.remove('hidden');
|
|
toggleIcon.textContent = '▼';
|
|
} else {
|
|
// Colapsar
|
|
historyList.classList.add('hidden');
|
|
toggleIcon.textContent = '▶';
|
|
}
|
|
}
|
|
}
|