Implementación de mejoras en el Launcher C# y gestión de proyectos
- Se completó la implementación del editor de proyectos C#, permitiendo agregar, editar y eliminar proyectos con un formulario avanzado. - Se mejoró la gestión de ejecutables C#, incluyendo la capacidad de ejecutar, obtener metadatos y argumentos predefinidos. - Se añadieron nuevas rutas API para gestionar proyectos y ejecutables C#, mejorando la funcionalidad y la experiencia del usuario. - Se actualizaron los logs para reflejar la ejecución de procesos y se implementaron notificaciones para el manejo de errores. - Se mejoró la interfaz de usuario con nuevos modales para la gestión de proyectos y ejecutables, facilitando la interacción.
This commit is contained in:
parent
7ab11a94ce
commit
5be80138c5
|
@ -239,23 +239,27 @@ GET /api/group-icon/<launcher_type>/<group_id>
|
|||
- ✅ Soporte básico para argumentos
|
||||
- ✅ Integración con sistema de tabs existente
|
||||
|
||||
### 🚧 IMPLEMENTACIÓN BÁSICA (Funcional pero Pendiente de Mejoras)
|
||||
- 🚧 **Editor de proyectos C#**: Modal básico (alert temporal)
|
||||
- 🚧 **Gestor de metadatos**: Funcional pero sin UI avanzada
|
||||
- 🚧 **Integración VS2022**: API preparada, implementación pendiente
|
||||
- 🚧 **Integración Cursor**: API preparada, implementación pendiente
|
||||
- 🚧 **Detección .sln**: No implementada aún
|
||||
### ✅ IMPLEMENTACIÓN AVANZADA (Funcional y Completa)
|
||||
- ✅ **Editor de proyectos C#**: Modal completo con formulario avanzado
|
||||
- ✅ **Gestor de metadatos**: UI completa con validaciones y notificaciones
|
||||
- ✅ **Integración VS2022**: Completamente implementada con detección automática
|
||||
- ✅ **Integración Cursor**: Completamente implementada con detección automática
|
||||
- ✅ **Integración Explorer**: Apertura de carpetas implementada
|
||||
- 🚧 **Detección .sln**: No implementada aún (opcional)
|
||||
|
||||
### 🎯 PRÓXIMOS PASOS OPCIONALES
|
||||
1. **Modal de Gestión de Proyectos**
|
||||
- Formulario completo para agregar/editar proyectos
|
||||
- Navegador de carpetas integrado
|
||||
- Validación de campos en tiempo real
|
||||
1. **Mejoras de Editor de Proyectos** (ya implementado básicamente)
|
||||
- ✅ Formulario completo para agregar/editar proyectos
|
||||
- ✅ Navegador de carpetas integrado
|
||||
- ✅ Validación de campos en tiempo real
|
||||
- 🔧 Drag & drop para agregar proyectos (opcional)
|
||||
|
||||
2. **Integración Completa de Editores**
|
||||
- Detección automática de Visual Studio 2022
|
||||
- Apertura directa de archivos .sln
|
||||
- Configuración de rutas de editores
|
||||
2. **Mejoras Opcionales de Editores** (ya implementado básicamente)
|
||||
- ✅ Detección automática de Visual Studio 2022
|
||||
- ✅ Detección automática de Cursor
|
||||
- ✅ Apertura de directorios de proyecto
|
||||
- 🔧 Apertura directa de archivos .sln (opcional)
|
||||
- 🔧 Configuración personalizada de rutas de editores
|
||||
|
||||
3. **Mejoras de UX**
|
||||
- Drag & drop para agregar proyectos
|
||||
|
@ -349,3 +353,59 @@ El proyecto está **listo para uso** con todas las funcionalidades core implemen
|
|||
|
||||
**¡Implementación exitosa y funcional!** ✅
|
||||
|
||||
---
|
||||
|
||||
## 🎉 Últimas Mejoras Implementadas
|
||||
|
||||
### Integración Completa de Editores ✅
|
||||
- **Cursor**: Detección automática en múltiples ubicaciones comunes
|
||||
- **Visual Studio 2022**: Soporte para Community, Professional y Enterprise
|
||||
- **Explorer**: Apertura directa de carpetas de proyecto
|
||||
|
||||
### APIs Extendidas ✅
|
||||
```python
|
||||
# Editores - Ahora soporta C# con vs2022
|
||||
POST /api/open-editor/<editor>/csharp/<project_id>
|
||||
# Soporta: editor='cursor'|'vs2022'|'vscode'
|
||||
|
||||
# Gestión de carpetas - Extiende soporte a C#
|
||||
POST /api/open-group-folder/csharp/<project_id>
|
||||
GET /api/get-group-path/csharp/<project_id>
|
||||
```
|
||||
|
||||
### Frontend Mejorado ✅
|
||||
- **Funciones Async**: Todas las funciones de editor ahora son asíncronas
|
||||
- **Manejo de Errores**: Notificaciones elegantes para éxitos y errores
|
||||
- **Fallbacks**: Alertas de respaldo si el sistema de notificaciones no está disponible
|
||||
- **Clipboard API**: Copia de paths con fallback para navegadores antiguos
|
||||
|
||||
### Rutas de Detección Automática ✅
|
||||
```javascript
|
||||
// Visual Studio 2022 - Busca en orden de preferencia:
|
||||
- Community: C:\Program Files\Microsoft Visual Studio\2022\Community\Common7\IDE\devenv.exe
|
||||
- Professional: C:\Program Files\Microsoft Visual Studio\2022\Professional\Common7\IDE\devenv.exe
|
||||
- Enterprise: C:\Program Files\Microsoft Visual Studio\2022\Enterprise\Common7\IDE\devenv.exe
|
||||
- x86 fallback: C:\Program Files (x86)\Microsoft Visual Studio\2022\Community\Common7\IDE\devenv.exe
|
||||
|
||||
// Cursor - Busca en ubicaciones comunes:
|
||||
- Local: C:\Users\[user]\AppData\Local\Programs\cursor\Cursor.exe
|
||||
- Program Files: C:\Program Files\Cursor\Cursor.exe
|
||||
- Program Files x86: C:\Program Files (x86)\Cursor\Cursor.exe
|
||||
- PATH: cursor (comando global)
|
||||
```
|
||||
|
||||
### Experiencia de Usuario ✅
|
||||
1. **Seleccionar Proyecto C#** → Botones de editores se activan automáticamente
|
||||
2. **Click en "Cursor"** → Abre el proyecto directamente en Cursor
|
||||
3. **Click en "Visual Studio 2022"** → Abre el proyecto en VS2022
|
||||
4. **Click en "📁 Explorador"** → Abre la carpeta del proyecto
|
||||
5. **Notificaciones** → Confirmación visual de éxito o error
|
||||
|
||||
### Compatibilidad ✅
|
||||
- **Windows**: Totalmente funcional con rutas nativas
|
||||
- **Cross-platform**: APIs preparadas para macOS y Linux
|
||||
- **Fallbacks**: Alertas tradicionales si fallan las notificaciones modernas
|
||||
- **Navegadores**: Clipboard API con fallback a execCommand
|
||||
|
||||
**🚀 El Launcher C# ahora tiene funcionalidad completa de editores igual que el launcher Python!** ✅
|
||||
|
||||
|
|
243
app.py
243
app.py
|
@ -817,7 +817,179 @@ def handle_unhandled_exception(e):
|
|||
return "<h1>Internal Server Error</h1><p>An unhandled error occurred.</p>", 500
|
||||
|
||||
|
||||
# === FIN LAUNCHER GUI APIs ===
|
||||
# === C# LAUNCHER APIs ===
|
||||
|
||||
@app.route("/api/csharp-projects", methods=["GET", "POST"])
|
||||
def handle_csharp_projects():
|
||||
"""Gestionar proyectos C# (GET: obtener, POST: crear)"""
|
||||
if request.method == "GET":
|
||||
try:
|
||||
projects = csharp_launcher_manager.get_csharp_projects()
|
||||
return jsonify(projects)
|
||||
except Exception as e:
|
||||
return jsonify({"error": str(e)}), 500
|
||||
else: # POST
|
||||
try:
|
||||
data = request.json
|
||||
result = csharp_launcher_manager.add_csharp_project(data)
|
||||
return jsonify(result)
|
||||
except Exception as e:
|
||||
return jsonify({"error": str(e)}), 500
|
||||
|
||||
@app.route("/api/csharp-projects/<project_id>", methods=["GET", "PUT", "DELETE"])
|
||||
def handle_csharp_project(project_id):
|
||||
"""Gestionar proyecto C# específico (GET: obtener, PUT: actualizar, DELETE: eliminar)"""
|
||||
if request.method == "GET":
|
||||
try:
|
||||
project = csharp_launcher_manager.get_csharp_project(project_id)
|
||||
if not project:
|
||||
return jsonify({"error": "Project not found"}), 404
|
||||
return jsonify(project)
|
||||
except Exception as e:
|
||||
return jsonify({"error": str(e)}), 500
|
||||
elif request.method == "PUT":
|
||||
try:
|
||||
data = request.json
|
||||
result = csharp_launcher_manager.update_csharp_project(project_id, data)
|
||||
return jsonify(result)
|
||||
except Exception as e:
|
||||
return jsonify({"error": str(e)}), 500
|
||||
else: # DELETE
|
||||
try:
|
||||
result = csharp_launcher_manager.delete_csharp_project(project_id)
|
||||
return jsonify(result)
|
||||
except Exception as e:
|
||||
return jsonify({"error": str(e)}), 500
|
||||
|
||||
@app.route("/api/csharp-executables/<project_id>")
|
||||
def get_csharp_executables(project_id):
|
||||
"""Obtener ejecutables de un proyecto C#"""
|
||||
try:
|
||||
executables = csharp_launcher_manager.get_project_executables(project_id)
|
||||
return jsonify(executables)
|
||||
except Exception as e:
|
||||
return jsonify({"error": str(e)}), 500
|
||||
|
||||
@app.route("/api/execute-csharp-executable", methods=["POST"])
|
||||
def execute_csharp_executable():
|
||||
"""Ejecutar ejecutable C# con argumentos opcionales"""
|
||||
try:
|
||||
data = request.json
|
||||
project_id = data["project_id"]
|
||||
exe_name = data["exe_name"]
|
||||
exe_args = data.get("args", [])
|
||||
working_dir = data.get("working_dir", None)
|
||||
|
||||
result = csharp_launcher_manager.execute_csharp_executable(
|
||||
project_id, exe_name, exe_args, broadcast_message, working_dir
|
||||
)
|
||||
return jsonify(result)
|
||||
except Exception as e:
|
||||
error_msg = f"Error ejecutando ejecutable C#: {str(e)}"
|
||||
broadcast_message(error_msg)
|
||||
return jsonify({"error": error_msg}), 500
|
||||
|
||||
@app.route("/api/csharp-favorites", methods=["GET", "POST"])
|
||||
def handle_csharp_favorites():
|
||||
"""Gestionar favoritos del launcher C#"""
|
||||
if request.method == "GET":
|
||||
try:
|
||||
favorites = csharp_launcher_manager.get_favorites()
|
||||
return jsonify({"favorites": favorites})
|
||||
except Exception as e:
|
||||
return jsonify({"error": str(e)}), 500
|
||||
else: # POST
|
||||
try:
|
||||
data = request.json
|
||||
project_id = data["project_id"]
|
||||
exe_name = data["exe_name"]
|
||||
result = csharp_launcher_manager.toggle_favorite(project_id, exe_name)
|
||||
return jsonify(result)
|
||||
except Exception as e:
|
||||
return jsonify({"error": str(e)}), 500
|
||||
|
||||
@app.route("/api/csharp-categories")
|
||||
def get_csharp_categories():
|
||||
"""Obtener categorías disponibles del launcher C#"""
|
||||
try:
|
||||
categories = csharp_launcher_manager.get_categories()
|
||||
return jsonify(categories)
|
||||
except Exception as e:
|
||||
return jsonify({"error": str(e)}), 500
|
||||
|
||||
@app.route("/api/csharp-running-processes")
|
||||
def get_csharp_running_processes():
|
||||
"""Obtener procesos C# en ejecución"""
|
||||
try:
|
||||
processes = csharp_launcher_manager.get_running_processes()
|
||||
return jsonify({"processes": processes})
|
||||
except Exception as e:
|
||||
return jsonify({"error": str(e)}), 500
|
||||
|
||||
@app.route("/api/csharp-process-terminate/<int:pid>", methods=["POST"])
|
||||
def terminate_csharp_process(pid):
|
||||
"""Cerrar un proceso C#"""
|
||||
try:
|
||||
result = csharp_launcher_manager.terminate_process(pid)
|
||||
return jsonify(result)
|
||||
except Exception as e:
|
||||
return jsonify({"error": str(e)}), 500
|
||||
|
||||
@app.route("/api/csharp-process-focus/<int:pid>", methods=["POST"])
|
||||
def focus_csharp_process(pid):
|
||||
"""Activar foco de un proceso C#"""
|
||||
try:
|
||||
result = csharp_launcher_manager.focus_process(pid)
|
||||
return jsonify(result)
|
||||
except Exception as e:
|
||||
return jsonify({"error": str(e)}), 500
|
||||
|
||||
@app.route("/api/csharp-executable-metadata/<project_id>/<exe_name>", methods=["GET", "PUT"])
|
||||
def handle_csharp_executable_metadata(project_id, exe_name):
|
||||
"""Gestionar metadatos de ejecutables C# (GET: obtener, PUT: actualizar)"""
|
||||
if request.method == "GET":
|
||||
try:
|
||||
metadata = csharp_launcher_manager.get_executable_metadata(project_id, exe_name)
|
||||
return jsonify(metadata)
|
||||
except Exception as e:
|
||||
return jsonify({"error": str(e)}), 500
|
||||
else: # PUT
|
||||
try:
|
||||
data = request.json
|
||||
result = csharp_launcher_manager.update_executable_metadata(project_id, exe_name, data)
|
||||
return jsonify(result)
|
||||
except Exception as e:
|
||||
return jsonify({"error": str(e)}), 500
|
||||
|
||||
@app.route("/api/csharp-executable-arguments/<project_id>/<exe_name>", methods=["GET", "PUT"])
|
||||
def handle_csharp_executable_arguments(project_id, exe_name):
|
||||
"""Gestionar argumentos predefinidos de ejecutables C#"""
|
||||
if request.method == "GET":
|
||||
try:
|
||||
arguments = csharp_launcher_manager.get_executable_arguments(project_id, exe_name)
|
||||
return jsonify({"arguments": arguments})
|
||||
except Exception as e:
|
||||
return jsonify({"error": str(e)}), 500
|
||||
else: # PUT
|
||||
try:
|
||||
data = request.json
|
||||
result = csharp_launcher_manager.update_executable_arguments(
|
||||
project_id, exe_name, data.get("arguments", [])
|
||||
)
|
||||
return jsonify(result)
|
||||
except Exception as e:
|
||||
return jsonify({"error": str(e)}), 500
|
||||
|
||||
@app.route("/api/csharp-all-executables/<project_id>")
|
||||
def get_all_csharp_executables(project_id):
|
||||
"""Obtener todos los ejecutables de un proyecto C# (incluyendo ocultos)"""
|
||||
try:
|
||||
executables = csharp_launcher_manager.get_all_project_executables(project_id)
|
||||
return jsonify(executables)
|
||||
except Exception as e:
|
||||
return jsonify({"error": str(e)}), 500
|
||||
|
||||
# === FIN C# LAUNCHER APIs ===
|
||||
|
||||
# --- Helper function to find VS Code ---
|
||||
def find_vscode_executable():
|
||||
|
@ -846,10 +1018,10 @@ def open_group_in_editor(editor, group_system, group_id):
|
|||
"""Ruta unificada para abrir grupos en diferentes editores"""
|
||||
try:
|
||||
# Validar editor
|
||||
if editor not in ['vscode', 'cursor']:
|
||||
if editor not in ['vscode', 'cursor', 'vs2022']:
|
||||
return jsonify({
|
||||
"status": "error",
|
||||
"message": f"Editor '{editor}' no soportado. Usar 'vscode' o 'cursor'"
|
||||
"message": f"Editor '{editor}' no soportado. Usar 'vscode', 'cursor' o 'vs2022'"
|
||||
}), 400
|
||||
|
||||
# Determinar directorio según el sistema
|
||||
|
@ -873,10 +1045,23 @@ def open_group_in_editor(editor, group_system, group_id):
|
|||
"status": "error",
|
||||
"message": f"Directorio del grupo launcher '{group['name']}' no encontrado"
|
||||
}), 404
|
||||
elif group_system == 'csharp':
|
||||
project = csharp_launcher_manager.get_csharp_project(group_id)
|
||||
if not project:
|
||||
return jsonify({
|
||||
"status": "error",
|
||||
"message": f"Proyecto C# '{group_id}' no encontrado"
|
||||
}), 404
|
||||
script_group_path = project["directory"]
|
||||
if not os.path.isdir(script_group_path):
|
||||
return jsonify({
|
||||
"status": "error",
|
||||
"message": f"Directorio del proyecto C# '{project['name']}' no encontrado"
|
||||
}), 404
|
||||
else:
|
||||
return jsonify({
|
||||
"status": "error",
|
||||
"message": f"Sistema de grupo '{group_system}' no válido. Usar 'config' o 'launcher'"
|
||||
"message": f"Sistema de grupo '{group_system}' no válido. Usar 'config', 'launcher' o 'csharp'"
|
||||
}), 400
|
||||
|
||||
# Definir rutas de ejecutables
|
||||
|
@ -906,6 +1091,26 @@ def open_group_in_editor(editor, group_system, group_id):
|
|||
"message": f"Cursor no encontrado. Intenté en: {', '.join(possible_cursor_paths)}"
|
||||
}), 404
|
||||
editor_name = "Cursor"
|
||||
elif editor == 'vs2022':
|
||||
# Rutas comunes para Visual Studio 2022
|
||||
possible_vs_paths = [
|
||||
r"C:\Program Files\Microsoft Visual Studio\2022\Community\Common7\IDE\devenv.exe",
|
||||
r"C:\Program Files\Microsoft Visual Studio\2022\Professional\Common7\IDE\devenv.exe",
|
||||
r"C:\Program Files\Microsoft Visual Studio\2022\Enterprise\Common7\IDE\devenv.exe",
|
||||
r"C:\Program Files (x86)\Microsoft Visual Studio\2022\Community\Common7\IDE\devenv.exe"
|
||||
]
|
||||
editor_path = None
|
||||
for path in possible_vs_paths:
|
||||
if os.path.isfile(path):
|
||||
editor_path = path
|
||||
break
|
||||
|
||||
if not editor_path:
|
||||
return jsonify({
|
||||
"status": "error",
|
||||
"message": f"Visual Studio 2022 no encontrado. Intenté en: {', '.join(possible_vs_paths)}"
|
||||
}), 404
|
||||
editor_name = "Visual Studio 2022"
|
||||
|
||||
# Verificar que el ejecutable existe
|
||||
if not os.path.isfile(editor_path):
|
||||
|
@ -958,10 +1163,23 @@ def open_group_folder(group_system, group_id):
|
|||
"status": "error",
|
||||
"message": f"Directorio del grupo launcher '{group['name']}' no encontrado"
|
||||
}), 404
|
||||
elif group_system == 'csharp':
|
||||
project = csharp_launcher_manager.get_csharp_project(group_id)
|
||||
if not project:
|
||||
return jsonify({
|
||||
"status": "error",
|
||||
"message": f"Proyecto C# '{group_id}' no encontrado"
|
||||
}), 404
|
||||
script_group_path = project["directory"]
|
||||
if not os.path.isdir(script_group_path):
|
||||
return jsonify({
|
||||
"status": "error",
|
||||
"message": f"Directorio del proyecto C# '{project['name']}' no encontrado"
|
||||
}), 404
|
||||
else:
|
||||
return jsonify({
|
||||
"status": "error",
|
||||
"message": f"Sistema de grupo '{group_system}' no válido. Usar 'config' o 'launcher'"
|
||||
"message": f"Sistema de grupo '{group_system}' no válido. Usar 'config', 'launcher' o 'csharp'"
|
||||
}), 400
|
||||
|
||||
# Abrir en el explorador según el sistema operativo
|
||||
|
@ -1016,10 +1234,23 @@ def get_group_path(group_system, group_id):
|
|||
"status": "error",
|
||||
"message": f"Directorio del grupo launcher '{group['name']}' no encontrado"
|
||||
}), 404
|
||||
elif group_system == 'csharp':
|
||||
project = csharp_launcher_manager.get_csharp_project(group_id)
|
||||
if not project:
|
||||
return jsonify({
|
||||
"status": "error",
|
||||
"message": f"Proyecto C# '{group_id}' no encontrado"
|
||||
}), 404
|
||||
script_group_path = project["directory"]
|
||||
if not os.path.isdir(script_group_path):
|
||||
return jsonify({
|
||||
"status": "error",
|
||||
"message": f"Directorio del proyecto C# '{project['name']}' no encontrado"
|
||||
}), 404
|
||||
else:
|
||||
return jsonify({
|
||||
"status": "error",
|
||||
"message": f"Sistema de grupo '{group_system}' no válido. Usar 'config' o 'launcher'"
|
||||
"message": f"Sistema de grupo '{group_system}' no válido. Usar 'config', 'launcher' o 'csharp'"
|
||||
}), 400
|
||||
|
||||
return jsonify({
|
||||
|
|
34
data/log.txt
34
data/log.txt
|
@ -0,0 +1,34 @@
|
|||
[18:25:29] 🚀 Ejecutando: CtrEditor
|
||||
[18:25:29] 📁 Directorio: D:/Proyectos/VisualStudio/CtrEditor\bin\Release\net8.0-windows8.0
|
||||
[18:25:29] ⚡ Comando: D:/Proyectos/VisualStudio/CtrEditor\bin\Release\net8.0-windows8.0\CtrEditor.exe
|
||||
[18:25:29] ==================================================
|
||||
[18:25:29] ✅ Proceso iniciado con PID: 30240
|
||||
[18:25:29] Historial de undo limpiado
|
||||
[18:25:29] Historial de undo limpiado
|
||||
[18:25:29] Historial de undo limpiado
|
||||
[18:25:30] Historial de undo limpiado
|
||||
[18:25:30] Historial de undo limpiado
|
||||
[18:25:33] ✅ Proceso completado exitosamente (PID: 30240)
|
||||
[18:25:33] ⏱️ Tiempo de ejecución: 4.19 segundos
|
||||
[18:25:33] ==================================================
|
||||
[01:53:47] 🚀 Ejecutando: CtrEditor
|
||||
[01:53:47] 📁 Directorio: D:/Proyectos/VisualStudio/CtrEditor\bin\Release\net8.0-windows8.0
|
||||
[01:53:47] ⚡ Comando: D:/Proyectos/VisualStudio/CtrEditor\bin\Release\net8.0-windows8.0\CtrEditor.exe
|
||||
[01:53:47] ==================================================
|
||||
[01:53:47] ✅ Proceso iniciado con PID: 40560
|
||||
[01:53:48] Historial de undo limpiado
|
||||
[01:53:48] Historial de undo limpiado
|
||||
[01:53:48] Historial de undo limpiado
|
||||
[01:53:48] Historial de undo limpiado
|
||||
[01:53:48] Historial de undo limpiado
|
||||
[01:53:51] ✅ Proceso completado exitosamente (PID: 40560)
|
||||
[01:53:51] ⏱️ Tiempo de ejecución: 3.78 segundos
|
||||
[01:53:51] ==================================================
|
||||
[01:54:52] 🚀 Ejecutando: GTPCorrgir
|
||||
[01:54:52] 📁 Directorio: D:/Proyectos/VisualStudio/GTPCorrgir\bin\Release\net8.0-windows
|
||||
[01:54:52] ⚡ Comando: D:/Proyectos/VisualStudio/GTPCorrgir\bin\Release\net8.0-windows\GTPCorrgir.exe
|
||||
[01:54:52] ==================================================
|
||||
[01:54:52] ✅ Proceso iniciado con PID: 44892
|
||||
[01:54:56] ✅ Proceso completado exitosamente (PID: 44892)
|
||||
[01:54:56] ⏱️ Tiempo de ejecución: 3.87 segundos
|
||||
[01:54:56] ==================================================
|
|
@ -199,12 +199,210 @@ class CSharpLauncherManager:
|
|||
# Limpiar metadatos y favoritos relacionados
|
||||
self._cleanup_metadata_for_project(project_id)
|
||||
self._cleanup_favorites_for_project(project_id)
|
||||
self._cleanup_favorites_for_project(project_id)
|
||||
|
||||
return {"status": "success", "message": "Proyecto eliminado exitosamente"}
|
||||
|
||||
except Exception as e:
|
||||
return {"status": "error", "message": f"Error eliminando proyecto: {str(e)}"}
|
||||
|
||||
def execute_csharp_executable(self, project_id: str, exe_name: str, exe_args: List[str],
|
||||
broadcast_func, working_dir: str = None) -> Dict[str, Any]:
|
||||
"""Ejecutar un ejecutable C#"""
|
||||
try:
|
||||
project = self.get_csharp_project(project_id)
|
||||
if not project:
|
||||
return {"status": "error", "message": "Proyecto no encontrado"}
|
||||
|
||||
# Buscar el ejecutable
|
||||
executables = self.get_all_project_executables(project_id)
|
||||
exe_info = None
|
||||
for exe in executables:
|
||||
if exe["filename"] == exe_name:
|
||||
exe_info = exe
|
||||
break
|
||||
|
||||
if not exe_info:
|
||||
return {"status": "error", "message": f"Ejecutable '{exe_name}' no encontrado"}
|
||||
|
||||
exe_path = exe_info["full_path"]
|
||||
|
||||
# Determinar directorio de trabajo
|
||||
if working_dir and os.path.isdir(working_dir):
|
||||
work_dir = working_dir
|
||||
else:
|
||||
work_dir = os.path.dirname(exe_path)
|
||||
|
||||
# Preparar comando
|
||||
cmd = [exe_path] + exe_args
|
||||
|
||||
execution_id = str(uuid.uuid4())[:8]
|
||||
|
||||
broadcast_func(f"🚀 Ejecutando: {exe_info['display_name']}")
|
||||
broadcast_func(f"📁 Directorio: {work_dir}")
|
||||
broadcast_func(f"⚡ Comando: {' '.join(cmd)}")
|
||||
broadcast_func("=" * 50)
|
||||
|
||||
try:
|
||||
# Ejecutar el proceso
|
||||
process = subprocess.Popen(
|
||||
cmd,
|
||||
cwd=work_dir,
|
||||
creationflags=subprocess.CREATE_NEW_CONSOLE if sys.platform == "win32" else 0
|
||||
)
|
||||
|
||||
# Almacenar información del proceso
|
||||
with self.process_lock:
|
||||
self.running_processes[process.pid] = {
|
||||
"project_id": project_id,
|
||||
"exe_name": exe_name,
|
||||
"display_name": exe_info['display_name'],
|
||||
"start_time": datetime.now(),
|
||||
"process": process
|
||||
}
|
||||
|
||||
broadcast_func(f"✅ Proceso iniciado con PID: {process.pid}")
|
||||
|
||||
return {
|
||||
"status": "success",
|
||||
"message": f"Ejecutable '{exe_info['display_name']}' iniciado",
|
||||
"pid": process.pid,
|
||||
"execution_id": execution_id
|
||||
}
|
||||
|
||||
except subprocess.SubprocessError as e:
|
||||
return {"status": "error", "message": f"Error ejecutando el proceso: {str(e)}"}
|
||||
except Exception as e:
|
||||
return {"status": "error", "message": f"Error inesperado: {str(e)}"}
|
||||
|
||||
except Exception as e:
|
||||
return {"status": "error", "message": f"Error ejecutando ejecutable: {str(e)}"}
|
||||
|
||||
def get_favorites(self) -> List[Dict[str, Any]]:
|
||||
"""Obtener lista de favoritos"""
|
||||
try:
|
||||
with open(self.favorites_path, 'r', encoding='utf-8') as f:
|
||||
data = json.load(f)
|
||||
return data.get("favorites", [])
|
||||
except Exception as e:
|
||||
print(f"Error loading C# favorites: {e}")
|
||||
return []
|
||||
|
||||
def toggle_favorite(self, project_id: str, exe_name: str) -> Dict[str, str]:
|
||||
"""Agregar o quitar de favoritos"""
|
||||
try:
|
||||
with open(self.favorites_path, 'r', encoding='utf-8') as f:
|
||||
data = json.load(f)
|
||||
|
||||
favorites = data.get("favorites", [])
|
||||
favorite_key = f"{project_id}_{exe_name}"
|
||||
|
||||
# Buscar si ya existe
|
||||
existing_favorite = None
|
||||
for fav in favorites:
|
||||
if fav.get("project_id") == project_id and fav.get("exe_name") == exe_name:
|
||||
existing_favorite = fav
|
||||
break
|
||||
|
||||
if existing_favorite:
|
||||
# Quitar de favoritos
|
||||
favorites.remove(existing_favorite)
|
||||
message = "Removido de favoritos"
|
||||
is_favorite = False
|
||||
else:
|
||||
# Agregar a favoritos
|
||||
favorites.append({
|
||||
"id": favorite_key,
|
||||
"project_id": project_id,
|
||||
"exe_name": exe_name,
|
||||
"added_date": datetime.now().isoformat() + "Z"
|
||||
})
|
||||
message = "Agregado a favoritos"
|
||||
is_favorite = True
|
||||
|
||||
data["favorites"] = favorites
|
||||
|
||||
with open(self.favorites_path, 'w', encoding='utf-8') as f:
|
||||
json.dump(data, f, indent=2, ensure_ascii=False)
|
||||
|
||||
return {"status": "success", "message": message, "is_favorite": is_favorite}
|
||||
|
||||
except Exception as e:
|
||||
return {"status": "error", "message": f"Error toggle favorite: {str(e)}"}
|
||||
|
||||
def get_categories(self) -> Dict[str, Any]:
|
||||
"""Obtener categorías disponibles"""
|
||||
try:
|
||||
with open(self.launcher_config_path, 'r', encoding='utf-8') as f:
|
||||
config = json.load(f)
|
||||
return config.get("categories", {})
|
||||
except Exception as e:
|
||||
print(f"Error loading C# categories: {e}")
|
||||
return {}
|
||||
|
||||
def get_running_processes(self) -> List[Dict[str, Any]]:
|
||||
"""Obtener procesos C# en ejecución"""
|
||||
processes = []
|
||||
|
||||
with self.process_lock:
|
||||
for pid, info in list(self.running_processes.items()):
|
||||
try:
|
||||
# Verificar si el proceso sigue activo
|
||||
process = info["process"]
|
||||
if process.poll() is None:
|
||||
processes.append({
|
||||
"pid": pid,
|
||||
"project_id": info["project_id"],
|
||||
"exe_name": info["exe_name"],
|
||||
"display_name": info["display_name"],
|
||||
"start_time": info["start_time"].isoformat() + "Z"
|
||||
})
|
||||
else:
|
||||
# Proceso terminado, remover de la lista
|
||||
del self.running_processes[pid]
|
||||
except:
|
||||
# Error verificando proceso, remover
|
||||
del self.running_processes[pid]
|
||||
|
||||
return processes
|
||||
|
||||
def terminate_process(self, pid: int) -> Dict[str, str]:
|
||||
"""Cerrar un proceso C#"""
|
||||
try:
|
||||
with self.process_lock:
|
||||
if pid in self.running_processes:
|
||||
process_info = self.running_processes[pid]
|
||||
process = process_info["process"]
|
||||
|
||||
try:
|
||||
process.terminate()
|
||||
process.wait(timeout=5)
|
||||
del self.running_processes[pid]
|
||||
return {
|
||||
"status": "success",
|
||||
"message": f"Proceso {process_info['display_name']} (PID: {pid}) terminado"
|
||||
}
|
||||
except subprocess.TimeoutExpired:
|
||||
process.kill()
|
||||
del self.running_processes[pid]
|
||||
return {
|
||||
"status": "success",
|
||||
"message": f"Proceso {process_info['display_name']} (PID: {pid}) forzado a cerrar"
|
||||
}
|
||||
else:
|
||||
return {"status": "error", "message": "Proceso no encontrado en ejecución"}
|
||||
|
||||
except Exception as e:
|
||||
return {"status": "error", "message": f"Error terminando proceso: {str(e)}"}
|
||||
|
||||
def _load_executable_metadata(self) -> Dict[str, Any]:
|
||||
"""Cargar metadatos de ejecutables desde archivo"""
|
||||
try:
|
||||
with open(self.script_metadata_path, 'r', encoding='utf-8') as f:
|
||||
return json.load(f)
|
||||
except (FileNotFoundError, json.JSONDecodeError):
|
||||
return {"version": "1.0", "executable_metadata": {}}
|
||||
|
||||
def get_project_executables(self, project_id: str) -> List[Dict[str, Any]]:
|
||||
"""Obtener ejecutables de un proyecto específico (solo visibles)"""
|
||||
project = self.get_csharp_project(project_id)
|
||||
|
@ -620,6 +818,48 @@ class CSharpLauncherManager:
|
|||
except Exception as e:
|
||||
print(f"Error cleaning favorites for project {project_id}: {e}")
|
||||
|
||||
def get_executable_arguments(self, project_id: str, exe_name: str) -> List[Dict[str, str]]:
|
||||
"""Obtener argumentos predefinidos de un ejecutable"""
|
||||
try:
|
||||
metadata = self._load_executable_metadata()
|
||||
exe_key = f"{project_id}_{exe_name}"
|
||||
|
||||
if "executable_metadata" in metadata and exe_key in metadata["executable_metadata"]:
|
||||
return metadata["executable_metadata"][exe_key].get("arguments", [])
|
||||
|
||||
return []
|
||||
|
||||
except Exception as e:
|
||||
print(f"Error loading executable arguments: {e}")
|
||||
return []
|
||||
|
||||
def update_executable_arguments(self, project_id: str, exe_name: str, arguments: List[Dict[str, str]]) -> Dict[str, str]:
|
||||
"""Actualizar argumentos predefinidos de un ejecutable"""
|
||||
try:
|
||||
metadata = self._load_executable_metadata()
|
||||
exe_key = f"{project_id}_{exe_name}"
|
||||
|
||||
# Crear estructura si no existe
|
||||
if "executable_metadata" not in metadata:
|
||||
metadata["executable_metadata"] = {}
|
||||
if exe_key not in metadata["executable_metadata"]:
|
||||
metadata["executable_metadata"][exe_key] = {}
|
||||
|
||||
# Validar formato de argumentos
|
||||
for arg in arguments:
|
||||
if not isinstance(arg, dict) or "description" not in arg or "arguments" not in arg:
|
||||
return {"status": "error", "message": "Formato de argumentos inválido. Debe ser [{'description': '...', 'arguments': '...'}]"}
|
||||
|
||||
# Actualizar argumentos
|
||||
metadata["executable_metadata"][exe_key]["arguments"] = arguments
|
||||
metadata["executable_metadata"][exe_key]["updated_date"] = datetime.now().isoformat() + "Z"
|
||||
|
||||
self._save_executable_metadata(metadata)
|
||||
return {"status": "success", "message": "Argumentos actualizados exitosamente"}
|
||||
|
||||
except Exception as e:
|
||||
return {"status": "error", "message": f"Error actualizando argumentos: {str(e)}"}
|
||||
|
||||
def get_all_project_executables(self, project_id: str) -> List[Dict[str, Any]]:
|
||||
"""Obtener TODOS los ejecutables de un proyecto (incluyendo ocultos) para gestión"""
|
||||
project = self.get_csharp_project(project_id)
|
||||
|
@ -737,4 +977,4 @@ class CSharpLauncherManager:
|
|||
with open(self.favorites_path, 'w', encoding='utf-8') as f:
|
||||
json.dump(data, f, indent=2, ensure_ascii=False)
|
||||
except Exception as e:
|
||||
print(f"Error cleaning favorites for project {project_id}: {e}")
|
||||
print(f"Error cleaning favorites for project {project_id}: {e}")
|
|
@ -105,7 +105,7 @@ class CSharpLauncherManager {
|
|||
}
|
||||
|
||||
showCSharpProjectButtons() {
|
||||
const buttons = ['cursor-csharp-btn', 'vs2022-csharp-btn', 'folder-csharp-btn', 'copy-path-csharp-btn'];
|
||||
const buttons = ['cursor-csharp-btn', 'vs2022-csharp-btn', 'folder-csharp-btn', 'copy-path-csharp-btn', 'manage-csharp-executables-btn'];
|
||||
buttons.forEach(id => {
|
||||
const btn = document.getElementById(id);
|
||||
if (btn) btn.style.display = 'block';
|
||||
|
@ -113,7 +113,7 @@ class CSharpLauncherManager {
|
|||
}
|
||||
|
||||
hideCSharpProjectButtons() {
|
||||
const buttons = ['cursor-csharp-btn', 'vs2022-csharp-btn', 'folder-csharp-btn', 'copy-path-csharp-btn'];
|
||||
const buttons = ['cursor-csharp-btn', 'vs2022-csharp-btn', 'folder-csharp-btn', 'copy-path-csharp-btn', 'manage-csharp-executables-btn'];
|
||||
buttons.forEach(id => {
|
||||
const btn = document.getElementById(id);
|
||||
if (btn) btn.style.display = 'none';
|
||||
|
@ -385,6 +385,276 @@ class CSharpLauncherManager {
|
|||
grid.innerHTML = '<div class="col-span-full text-center py-8 text-gray-500">Selecciona un proyecto para ver los ejecutables</div>';
|
||||
}
|
||||
}
|
||||
|
||||
// === GESTIÓN DE PROYECTOS ===
|
||||
|
||||
openProjectEditor() {
|
||||
// Cargar proyectos existentes y mostrar modal
|
||||
this.renderExistingProjects();
|
||||
this.clearProjectForm();
|
||||
const modal = document.getElementById('csharp-project-editor-modal');
|
||||
if (modal) {
|
||||
modal.classList.remove('hidden');
|
||||
}
|
||||
}
|
||||
|
||||
closeProjectEditor() {
|
||||
const modal = document.getElementById('csharp-project-editor-modal');
|
||||
if (modal) {
|
||||
modal.classList.add('hidden');
|
||||
}
|
||||
}
|
||||
|
||||
clearProjectForm() {
|
||||
const form = document.getElementById('csharp-project-form');
|
||||
if (form) {
|
||||
form.reset();
|
||||
document.getElementById('csharp-project-id').value = '';
|
||||
}
|
||||
|
||||
// Ocultar botón de eliminar cuando se crea nuevo proyecto
|
||||
const deleteBtn = document.getElementById('delete-csharp-project-btn');
|
||||
if (deleteBtn) {
|
||||
deleteBtn.style.display = 'none';
|
||||
}
|
||||
}
|
||||
|
||||
populateProjectForm(project) {
|
||||
document.getElementById('csharp-project-id').value = project.id || '';
|
||||
document.getElementById('csharp-project-name').value = project.name || '';
|
||||
document.getElementById('csharp-project-description').value = project.description || '';
|
||||
document.getElementById('csharp-project-directory').value = project.directory || '';
|
||||
document.getElementById('csharp-project-category').value = project.category || 'Otros';
|
||||
document.getElementById('csharp-project-version').value = project.version || '1.0';
|
||||
document.getElementById('csharp-project-author').value = project.author || '';
|
||||
document.getElementById('csharp-project-dotnet-version').value = project.dotnet_version || '';
|
||||
|
||||
// Tags (convertir array a string)
|
||||
const tagsInput = document.getElementById('csharp-project-tags');
|
||||
if (tagsInput && project.tags) {
|
||||
tagsInput.value = Array.isArray(project.tags) ? project.tags.join(', ') : project.tags;
|
||||
}
|
||||
|
||||
// Mostrar botón de eliminar cuando se edita un proyecto existente
|
||||
const deleteBtn = document.getElementById('delete-csharp-project-btn');
|
||||
if (deleteBtn) {
|
||||
deleteBtn.style.display = 'block';
|
||||
}
|
||||
}
|
||||
|
||||
async saveProject() {
|
||||
const projectData = {
|
||||
name: document.getElementById('csharp-project-name').value.trim(),
|
||||
description: document.getElementById('csharp-project-description').value.trim(),
|
||||
directory: document.getElementById('csharp-project-directory').value.trim(),
|
||||
category: document.getElementById('csharp-project-category').value || 'Otros',
|
||||
version: document.getElementById('csharp-project-version').value.trim() || '1.0',
|
||||
author: document.getElementById('csharp-project-author').value.trim(),
|
||||
dotnet_version: document.getElementById('csharp-project-dotnet-version').value.trim(),
|
||||
tags: document.getElementById('csharp-project-tags').value.split(',').map(tag => tag.trim()).filter(tag => tag)
|
||||
};
|
||||
|
||||
// Validación básica
|
||||
if (!projectData.name) {
|
||||
this.showNotification('El nombre del proyecto es requerido', 'error');
|
||||
document.getElementById('csharp-project-name').focus();
|
||||
return;
|
||||
}
|
||||
if (!projectData.directory) {
|
||||
this.showNotification('El directorio del proyecto es requerido', 'error');
|
||||
document.getElementById('csharp-project-directory').focus();
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const projectId = document.getElementById('csharp-project-id').value;
|
||||
const isEdit = projectId && projectId.trim() !== '';
|
||||
|
||||
let response;
|
||||
if (isEdit) {
|
||||
// Actualizar proyecto existente
|
||||
response = await fetch(`/api/csharp-projects/${projectId}`, {
|
||||
method: 'PUT',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify(projectData)
|
||||
});
|
||||
} else {
|
||||
// Crear nuevo proyecto
|
||||
response = await fetch('/api/csharp-projects', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify(projectData)
|
||||
});
|
||||
}
|
||||
|
||||
const result = await response.json();
|
||||
|
||||
if (response.ok && result.status === 'success') {
|
||||
// Recargar proyectos y cerrar modal
|
||||
await this.loadProjects();
|
||||
this.closeProjectEditor();
|
||||
this.showNotification(`Proyecto ${isEdit ? 'actualizado' : 'creado'} exitosamente`, 'success');
|
||||
} else {
|
||||
this.showNotification(`Error: ${result.message || 'Error desconocido'}`, 'error');
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error saving project:', error);
|
||||
this.showNotification('Error al comunicarse con el servidor', 'error');
|
||||
}
|
||||
}
|
||||
|
||||
editProject(projectId) {
|
||||
const project = this.projects.find(p => p.id === projectId);
|
||||
if (project) {
|
||||
this.populateProjectForm(project);
|
||||
// No necesitamos abrir el modal aquí, ya debería estar abierto
|
||||
}
|
||||
}
|
||||
|
||||
async deleteProject() {
|
||||
const projectId = document.getElementById('csharp-project-id').value;
|
||||
if (!projectId) {
|
||||
this.showNotification('No hay proyecto seleccionado para eliminar', 'error');
|
||||
return;
|
||||
}
|
||||
|
||||
const project = this.projects.find(p => p.id === projectId);
|
||||
const projectName = project ? project.name : 'este proyecto';
|
||||
|
||||
if (!confirm(`¿Estás seguro de que quieres eliminar ${projectName}?`)) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await fetch(`/api/csharp-projects/${projectId}`, {
|
||||
method: 'DELETE'
|
||||
});
|
||||
|
||||
const result = await response.json();
|
||||
|
||||
if (response.ok && result.status === 'success') {
|
||||
// Recargar proyectos y limpiar formulario
|
||||
await this.loadProjects();
|
||||
this.clearProjectForm();
|
||||
this.showNotification('Proyecto eliminado exitosamente', 'success');
|
||||
} else {
|
||||
this.showNotification(`Error: ${result.message || 'Error desconocido'}`, 'error');
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error deleting project:', error);
|
||||
this.showNotification('Error al comunicarse con el servidor', 'error');
|
||||
}
|
||||
}
|
||||
|
||||
renderExistingProjects() {
|
||||
const container = document.getElementById('csharp-existing-projects');
|
||||
if (!container) return;
|
||||
|
||||
if (this.projects.length === 0) {
|
||||
container.innerHTML = '<div class="text-center text-gray-500 py-4">No hay proyectos C# configurados</div>';
|
||||
return;
|
||||
}
|
||||
|
||||
const projectItems = this.projects.map(project => {
|
||||
const categoryInfo = this.getCategoryInfo(project.category);
|
||||
return `
|
||||
<div class="project-item border rounded-lg p-4 hover:bg-gray-50">
|
||||
<div class="flex justify-between items-start">
|
||||
<div class="flex-1">
|
||||
<div class="flex items-center gap-2 mb-2">
|
||||
<span class="text-lg">${categoryInfo.icon}</span>
|
||||
<h4 class="font-bold">${project.name}</h4>
|
||||
<span class="text-xs px-2 py-1 rounded-full" style="background-color: ${categoryInfo.color}20; color: ${categoryInfo.color}">
|
||||
${project.category}
|
||||
</span>
|
||||
</div>
|
||||
<p class="text-sm text-gray-600 mb-1">${project.description || 'Sin descripción'}</p>
|
||||
<p class="text-xs text-gray-500">${project.directory}</p>
|
||||
<div class="flex items-center gap-4 mt-2 text-xs text-gray-500">
|
||||
<span>v${project.version}</span>
|
||||
${project.dotnet_version ? `<span>.NET ${project.dotnet_version}</span>` : ''}
|
||||
${project.author ? `<span>por ${project.author}</span>` : ''}
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex gap-2 ml-4">
|
||||
<button class="text-blue-500 hover:text-blue-700 text-sm"
|
||||
onclick="csharpLauncherManager.editProject('${project.id}')">
|
||||
Editar
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
}).join('');
|
||||
|
||||
container.innerHTML = projectItems;
|
||||
}
|
||||
|
||||
getCategoryInfo(category) {
|
||||
const categories = {
|
||||
'Aplicaciones': { icon: '🖥️', color: '#3B82F6' },
|
||||
'Herramientas': { icon: '🔧', color: '#10B981' },
|
||||
'Análisis': { icon: '📊', color: '#8B5CF6' },
|
||||
'Desarrollo': { icon: '💻', color: '#F59E0B' },
|
||||
'APIs': { icon: '🌐', color: '#EF4444' },
|
||||
'Otros': { icon: '📁', color: '#6B7280' }
|
||||
};
|
||||
return categories[category] || categories['Otros'];
|
||||
}
|
||||
|
||||
showNotification(message, type = 'info') {
|
||||
// Usar la función showToast global si está disponible, sino crear una simple
|
||||
if (typeof showToast === 'function') {
|
||||
showToast(message, type);
|
||||
} else {
|
||||
// Fallback simple
|
||||
const bgColor = type === 'success' ? 'bg-green-500' :
|
||||
type === 'error' ? 'bg-red-500' : 'bg-blue-500';
|
||||
|
||||
const notification = document.createElement('div');
|
||||
notification.className = `fixed top-4 right-4 ${bgColor} text-white px-4 py-2 rounded-lg shadow-lg z-50`;
|
||||
notification.textContent = message;
|
||||
|
||||
document.body.appendChild(notification);
|
||||
|
||||
setTimeout(() => {
|
||||
notification.remove();
|
||||
}, 3000);
|
||||
}
|
||||
}
|
||||
|
||||
async browseProjectDirectory() {
|
||||
try {
|
||||
const currentPath = document.getElementById('csharp-project-directory').value;
|
||||
const response = await fetch('/api/browse-directories', {
|
||||
method: 'GET',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
}
|
||||
});
|
||||
|
||||
if (response.ok) {
|
||||
const result = await response.json();
|
||||
if (result.status === 'success' && result.path) {
|
||||
document.getElementById('csharp-project-directory').value = result.path;
|
||||
}
|
||||
} else {
|
||||
// Fallback a prompt si falla el navegador
|
||||
const newPath = prompt('Ingresa el directorio del proyecto C#:', currentPath);
|
||||
if (newPath !== null) {
|
||||
document.getElementById('csharp-project-directory').value = newPath.trim();
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error browsing directory:', error);
|
||||
// Fallback a prompt si hay error
|
||||
const currentPath = document.getElementById('csharp-project-directory').value;
|
||||
const newPath = prompt('Ingresa el directorio del proyecto C#:', currentPath);
|
||||
if (newPath !== null) {
|
||||
document.getElementById('csharp-project-directory').value = newPath.trim();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Funciones globales para el HTML
|
||||
|
@ -408,11 +678,12 @@ function refreshCSharpProcesses() {
|
|||
}
|
||||
|
||||
function openCSharpProjectEditor() {
|
||||
// TODO: Implementar editor de proyectos C#
|
||||
alert('Editor de proyectos C# - Por implementar');
|
||||
if (window.csharpLauncherManager) {
|
||||
window.csharpLauncherManager.openProjectEditor();
|
||||
}
|
||||
}
|
||||
|
||||
function openCSharpProjectInEditor(editor) {
|
||||
async function openCSharpProjectInEditor(editor) {
|
||||
if (!window.csharpLauncherManager?.currentProject) {
|
||||
alert('Selecciona un proyecto primero');
|
||||
return;
|
||||
|
@ -420,33 +691,117 @@ function openCSharpProjectInEditor(editor) {
|
|||
|
||||
const projectId = window.csharpLauncherManager.currentProject.id;
|
||||
|
||||
if (editor === 'cursor') {
|
||||
// Implementar apertura en Cursor
|
||||
alert(`Abriendo proyecto ${projectId} en Cursor - Por implementar`);
|
||||
} else if (editor === 'vs2022') {
|
||||
// Implementar apertura en Visual Studio 2022
|
||||
alert(`Abriendo proyecto ${projectId} en Visual Studio 2022 - Por implementar`);
|
||||
try {
|
||||
const response = await fetch(`/api/open-editor/${editor}/csharp/${projectId}`, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' }
|
||||
});
|
||||
|
||||
const result = await response.json();
|
||||
if (response.ok && result.status === 'success') {
|
||||
const editorName = editor === 'cursor' ? 'Cursor' : 'Visual Studio 2022';
|
||||
if (window.csharpLauncherManager && window.csharpLauncherManager.showNotification) {
|
||||
window.csharpLauncherManager.showNotification(`${editorName} abierto exitosamente`, 'success');
|
||||
} else {
|
||||
alert(`${editorName} abierto exitosamente`);
|
||||
}
|
||||
} else {
|
||||
const errorMsg = `Error: ${result.message}`;
|
||||
if (window.csharpLauncherManager && window.csharpLauncherManager.showNotification) {
|
||||
window.csharpLauncherManager.showNotification(errorMsg, 'error');
|
||||
} else {
|
||||
alert(errorMsg);
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(`Error opening ${editor}:`, error);
|
||||
const errorMsg = 'Error al comunicarse con el servidor';
|
||||
if (window.csharpLauncherManager && window.csharpLauncherManager.showNotification) {
|
||||
window.csharpLauncherManager.showNotification(errorMsg, 'error');
|
||||
} else {
|
||||
alert(errorMsg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function openCSharpProjectFolder() {
|
||||
async function openCSharpProjectFolder() {
|
||||
if (!window.csharpLauncherManager?.currentProject) {
|
||||
alert('Selecciona un proyecto primero');
|
||||
return;
|
||||
}
|
||||
|
||||
// Implementar apertura de carpeta
|
||||
alert('Abriendo carpeta del proyecto - Por implementar');
|
||||
const projectId = window.csharpLauncherManager.currentProject.id;
|
||||
|
||||
try {
|
||||
const response = await fetch(`/api/open-group-folder/csharp/${projectId}`, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' }
|
||||
});
|
||||
|
||||
const result = await response.json();
|
||||
if (response.ok && result.status === 'success') {
|
||||
if (window.csharpLauncherManager && window.csharpLauncherManager.showNotification) {
|
||||
window.csharpLauncherManager.showNotification('Explorador abierto exitosamente', 'success');
|
||||
} else {
|
||||
alert('Explorador abierto exitosamente');
|
||||
}
|
||||
} else {
|
||||
const errorMsg = `Error: ${result.message}`;
|
||||
if (window.csharpLauncherManager && window.csharpLauncherManager.showNotification) {
|
||||
window.csharpLauncherManager.showNotification(errorMsg, 'error');
|
||||
} else {
|
||||
alert(errorMsg);
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error opening folder:', error);
|
||||
const errorMsg = 'Error al comunicarse con el servidor';
|
||||
if (window.csharpLauncherManager && window.csharpLauncherManager.showNotification) {
|
||||
window.csharpLauncherManager.showNotification(errorMsg, 'error');
|
||||
} else {
|
||||
alert(errorMsg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function copyCSharpProjectPath() {
|
||||
async function copyCSharpProjectPath() {
|
||||
if (!window.csharpLauncherManager?.currentProject) {
|
||||
alert('Selecciona un proyecto primero');
|
||||
return;
|
||||
}
|
||||
|
||||
// Implementar copia de path
|
||||
navigator.clipboard.writeText(window.csharpLauncherManager.currentProject.directory);
|
||||
const projectPath = window.csharpLauncherManager.currentProject.directory;
|
||||
|
||||
try {
|
||||
await navigator.clipboard.writeText(projectPath);
|
||||
if (window.csharpLauncherManager && window.csharpLauncherManager.showNotification) {
|
||||
window.csharpLauncherManager.showNotification('Path copiado al portapapeles', 'success');
|
||||
} else {
|
||||
alert('Path copiado al portapapeles');
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error copying path:', error);
|
||||
// Fallback para navegadores que no soportan clipboard API
|
||||
const textArea = document.createElement('textarea');
|
||||
textArea.value = projectPath;
|
||||
document.body.appendChild(textArea);
|
||||
textArea.select();
|
||||
try {
|
||||
document.execCommand('copy');
|
||||
if (window.csharpLauncherManager && window.csharpLauncherManager.showNotification) {
|
||||
window.csharpLauncherManager.showNotification('Path copiado al portapapeles', 'success');
|
||||
} else {
|
||||
alert('Path copiado al portapapeles');
|
||||
}
|
||||
} catch (fallbackError) {
|
||||
if (window.csharpLauncherManager && window.csharpLauncherManager.showNotification) {
|
||||
window.csharpLauncherManager.showNotification('Error al copiar path', 'error');
|
||||
} else {
|
||||
alert('Error al copiar path');
|
||||
}
|
||||
}
|
||||
document.body.removeChild(textArea);
|
||||
}
|
||||
}
|
||||
|
||||
function openCSharpExecutableManager() {
|
||||
|
@ -454,6 +809,442 @@ function openCSharpExecutableManager() {
|
|||
alert('Gestor de ejecutables C# - Por implementar');
|
||||
}
|
||||
|
||||
// === FUNCIONES GLOBALES PARA GESTIÓN DE PROYECTOS ===
|
||||
|
||||
function closeCSharpProjectEditor() {
|
||||
if (window.csharpLauncherManager) {
|
||||
window.csharpLauncherManager.closeProjectEditor();
|
||||
}
|
||||
}
|
||||
|
||||
function saveCSharpProject() {
|
||||
if (window.csharpLauncherManager) {
|
||||
window.csharpLauncherManager.saveProject();
|
||||
}
|
||||
}
|
||||
|
||||
function deleteCSharpProject() {
|
||||
if (window.csharpLauncherManager) {
|
||||
window.csharpLauncherManager.deleteProject();
|
||||
}
|
||||
}
|
||||
|
||||
function browseCSharpProjectDirectory() {
|
||||
if (window.csharpLauncherManager) {
|
||||
window.csharpLauncherManager.browseProjectDirectory();
|
||||
}
|
||||
}
|
||||
|
||||
function openCSharpExecutableManager() {
|
||||
if (!csharpLauncherManager.currentProject) {
|
||||
alert('Selecciona un proyecto primero');
|
||||
return;
|
||||
}
|
||||
|
||||
const modal = document.getElementById('csharp-executable-manager');
|
||||
if (modal) {
|
||||
modal.style.display = 'block';
|
||||
loadCSharpExecutableManager();
|
||||
}
|
||||
}
|
||||
|
||||
async function loadCSharpExecutableManager() {
|
||||
if (!csharpLauncherManager.currentProject) return;
|
||||
|
||||
try {
|
||||
const response = await fetch(`/api/csharp-all-executables/${csharpLauncherManager.currentProject.id}`);
|
||||
if (response.ok) {
|
||||
const executables = await response.json();
|
||||
renderCSharpExecutableManager(executables);
|
||||
} else {
|
||||
console.error('Error loading all executables:', await response.text());
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error loading all executables:', error);
|
||||
}
|
||||
}
|
||||
|
||||
function renderCSharpExecutableManager(executables) {
|
||||
const container = document.getElementById('csharp-executable-list');
|
||||
if (!container) return;
|
||||
|
||||
if (executables.length === 0) {
|
||||
container.innerHTML = `
|
||||
<div class="text-center text-gray-500 py-8">
|
||||
<div class="text-4xl mb-2">📝</div>
|
||||
<div>No se encontraron ejecutables en este proyecto</div>
|
||||
</div>
|
||||
`;
|
||||
return;
|
||||
}
|
||||
|
||||
container.innerHTML = executables.map(exe => `
|
||||
<div class="executable-item bg-white border rounded-lg p-4 mb-3">
|
||||
<div class="flex justify-between items-start">
|
||||
<div class="flex-1">
|
||||
<div class="flex items-center gap-2 mb-2">
|
||||
<h4 class="font-medium">${exe.display_name}</h4>
|
||||
<span class="text-xs px-2 py-1 rounded ${exe.build_type === 'Release' ? 'bg-green-100 text-green-800' : 'bg-yellow-100 text-yellow-800'}">${exe.build_type}</span>
|
||||
<span class="text-xs px-2 py-1 rounded ${exe.hidden ? 'bg-red-100 text-red-800' : 'bg-green-100 text-green-800'}">${exe.hidden ? 'Oculto' : 'Visible'}</span>
|
||||
</div>
|
||||
<p class="text-sm text-gray-600 mb-1">${exe.short_description}</p>
|
||||
<p class="text-xs text-gray-500">${exe.filename}</p>
|
||||
</div>
|
||||
<div class="flex gap-2">
|
||||
<button class="text-blue-500 hover:underline text-sm"
|
||||
onclick="editCSharpExecutableMetadata('${csharpLauncherManager.currentProject.id}', '${exe.filename}')">
|
||||
Editar
|
||||
</button>
|
||||
<button class="text-green-500 hover:underline text-sm"
|
||||
onclick="editCSharpExecutableArguments('${csharpLauncherManager.currentProject.id}', '${exe.filename}')">
|
||||
Argumentos
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`).join('');
|
||||
}
|
||||
|
||||
async function editCSharpExecutableMetadata(projectId, exeName) {
|
||||
try {
|
||||
const response = await fetch(`/api/csharp-executable-metadata/${projectId}/${exeName}`);
|
||||
const metadata = response.ok ? await response.json() : {};
|
||||
|
||||
showCSharpExecutableMetadataEditor(projectId, exeName, metadata);
|
||||
} catch (error) {
|
||||
console.error('Error loading executable metadata:', error);
|
||||
}
|
||||
}
|
||||
|
||||
function showCSharpExecutableMetadataEditor(projectId, exeName, metadata) {
|
||||
const modalHtml = `
|
||||
<div id="csharp-metadata-modal" class="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50">
|
||||
<div class="bg-white rounded-lg p-6 w-full max-w-2xl max-h-[90vh] overflow-y-auto">
|
||||
<div class="flex justify-between items-center mb-4">
|
||||
<h3 class="text-lg font-semibold">Editar Ejecutable: ${exeName}</h3>
|
||||
<button onclick="closeCSharpMetadataEditor()" class="text-gray-500 hover:text-gray-700">×</button>
|
||||
</div>
|
||||
|
||||
<form id="csharp-metadata-form" class="space-y-4">
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-gray-700 mb-1">Nombre para mostrar</label>
|
||||
<input type="text" id="metadata-display-name"
|
||||
value="${metadata.display_name || exeName.replace('.exe', '')}"
|
||||
class="w-full border border-gray-300 rounded-md px-3 py-2">
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-gray-700 mb-1">Descripción corta</label>
|
||||
<input type="text" id="metadata-short-description"
|
||||
value="${metadata.short_description || 'Aplicación C#'}"
|
||||
class="w-full border border-gray-300 rounded-md px-3 py-2">
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-gray-700 mb-1">Descripción larga</label>
|
||||
<textarea id="metadata-long-description" rows="3"
|
||||
class="w-full border border-gray-300 rounded-md px-3 py-2">${metadata.long_description || ''}</textarea>
|
||||
</div>
|
||||
|
||||
<div class="flex items-center">
|
||||
<input type="checkbox" id="metadata-hidden" ${metadata.hidden ? 'checked' : ''}
|
||||
class="h-4 w-4 text-blue-600 border-gray-300 rounded">
|
||||
<label for="metadata-hidden" class="ml-2 text-sm text-gray-700">Ocultar ejecutable</label>
|
||||
</div>
|
||||
|
||||
<div class="flex justify-end space-x-3 pt-4">
|
||||
<button type="button" onclick="closeCSharpMetadataEditor()"
|
||||
class="px-4 py-2 text-gray-600 border border-gray-300 rounded-md hover:bg-gray-50">
|
||||
Cancelar
|
||||
</button>
|
||||
<button type="submit"
|
||||
class="px-4 py-2 bg-blue-500 text-white rounded-md hover:bg-blue-600">
|
||||
Guardar
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
document.body.insertAdjacentHTML('beforeend', modalHtml);
|
||||
|
||||
document.getElementById('csharp-metadata-form').onsubmit = async (e) => {
|
||||
e.preventDefault();
|
||||
await saveCSharpExecutableMetadata(projectId, exeName);
|
||||
};
|
||||
}
|
||||
|
||||
async function saveCSharpExecutableMetadata(projectId, exeName) {
|
||||
const metadata = {
|
||||
display_name: document.getElementById('metadata-display-name').value,
|
||||
short_description: document.getElementById('metadata-short-description').value,
|
||||
long_description: document.getElementById('metadata-long-description').value,
|
||||
hidden: document.getElementById('metadata-hidden').checked
|
||||
};
|
||||
|
||||
try {
|
||||
const response = await fetch(`/api/csharp-executable-metadata/${projectId}/${exeName}`, {
|
||||
method: 'PUT',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify(metadata)
|
||||
});
|
||||
|
||||
const result = await response.json();
|
||||
|
||||
if (result.status === 'success') {
|
||||
showNotification(result.message, 'success');
|
||||
closeCSharpMetadataEditor();
|
||||
await loadCSharpExecutableManager();
|
||||
await csharpLauncherManager.loadProjectExecutables(projectId);
|
||||
} else {
|
||||
showNotification(result.message, 'error');
|
||||
}
|
||||
} catch (error) {
|
||||
showNotification('Error guardando metadatos', 'error');
|
||||
console.error('Error saving metadata:', error);
|
||||
}
|
||||
}
|
||||
|
||||
function closeCSharpMetadataEditor() {
|
||||
const modal = document.getElementById('csharp-metadata-modal');
|
||||
if (modal) modal.remove();
|
||||
}
|
||||
|
||||
async function editCSharpExecutableArguments(projectId, exeName) {
|
||||
try {
|
||||
const response = await fetch(`/api/csharp-executable-arguments/${projectId}/${exeName}`);
|
||||
const data = response.ok ? await response.json() : { arguments: [] };
|
||||
|
||||
showCSharpArgumentsEditor(projectId, exeName, data.arguments);
|
||||
} catch (error) {
|
||||
console.error('Error loading executable arguments:', error);
|
||||
}
|
||||
}
|
||||
|
||||
function showCSharpArgumentsEditor(projectId, exeName, arguments) {
|
||||
const modalHtml = `
|
||||
<div id="csharp-arguments-modal" class="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50">
|
||||
<div class="bg-white rounded-lg p-6 w-full max-w-4xl max-h-[90vh] overflow-y-auto">
|
||||
<div class="flex justify-between items-center mb-4">
|
||||
<h3 class="text-lg font-semibold">Argumentos de: ${exeName}</h3>
|
||||
<button onclick="closeCSharpArgumentsEditor()" class="text-gray-500 hover:text-gray-700">×</button>
|
||||
</div>
|
||||
|
||||
<div class="mb-4">
|
||||
<button onclick="addCSharpArgument()"
|
||||
class="px-4 py-2 bg-green-500 text-white rounded-md hover:bg-green-600">
|
||||
+ Agregar Argumento
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div id="csharp-arguments-list" class="space-y-3 mb-4">
|
||||
<!-- Arguments will be rendered here -->
|
||||
</div>
|
||||
|
||||
<div class="flex justify-end space-x-3 pt-4 border-t">
|
||||
<button onclick="closeCSharpArgumentsEditor()"
|
||||
class="px-4 py-2 text-gray-600 border border-gray-300 rounded-md hover:bg-gray-50">
|
||||
Cancelar
|
||||
</button>
|
||||
<button onclick="saveCSharpExecutableArguments('${projectId}', '${exeName}')"
|
||||
class="px-4 py-2 bg-blue-500 text-white rounded-md hover:bg-blue-600">
|
||||
Guardar
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
document.body.insertAdjacentHTML('beforeend', modalHtml);
|
||||
renderCSharpArguments(arguments);
|
||||
}
|
||||
|
||||
function renderCSharpArguments(arguments) {
|
||||
const container = document.getElementById('csharp-arguments-list');
|
||||
if (!container) return;
|
||||
|
||||
container.innerHTML = arguments.map((arg, index) => `
|
||||
<div class="argument-item bg-gray-50 p-4 rounded-lg" data-index="${index}">
|
||||
<div class="grid grid-cols-12 gap-3 items-center">
|
||||
<div class="col-span-4">
|
||||
<input type="text" placeholder="Descripción (ej: Modo debug)"
|
||||
value="${arg.description || ''}"
|
||||
class="w-full border border-gray-300 rounded-md px-3 py-2 text-sm arg-description">
|
||||
</div>
|
||||
<div class="col-span-7">
|
||||
<input type="text" placeholder="Argumentos (ej: --debug --verbose)"
|
||||
value="${arg.arguments || ''}"
|
||||
class="w-full border border-gray-300 rounded-md px-3 py-2 text-sm arg-value">
|
||||
</div>
|
||||
<div class="col-span-1">
|
||||
<button onclick="removeCSharpArgument(${index})"
|
||||
class="text-red-500 hover:text-red-700 p-1">
|
||||
🗑️
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`).join('');
|
||||
|
||||
if (arguments.length === 0) {
|
||||
container.innerHTML = `
|
||||
<div class="text-center text-gray-500 py-8">
|
||||
<div class="text-2xl mb-2">⚙️</div>
|
||||
<div>No hay argumentos configurados</div>
|
||||
<div class="text-sm">Usa el botón "Agregar Argumento" para crear opciones predefinidas</div>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
}
|
||||
|
||||
function addCSharpArgument() {
|
||||
const container = document.getElementById('csharp-arguments-list');
|
||||
if (!container) return;
|
||||
|
||||
const currentArguments = collectCSharpArguments();
|
||||
currentArguments.push({ description: '', arguments: '' });
|
||||
renderCSharpArguments(currentArguments);
|
||||
}
|
||||
|
||||
function removeCSharpArgument(index) {
|
||||
const currentArguments = collectCSharpArguments();
|
||||
currentArguments.splice(index, 1);
|
||||
renderCSharpArguments(currentArguments);
|
||||
}
|
||||
|
||||
function collectCSharpArguments() {
|
||||
const argumentItems = document.querySelectorAll('.argument-item');
|
||||
const arguments = [];
|
||||
|
||||
argumentItems.forEach(item => {
|
||||
const description = item.querySelector('.arg-description').value.trim();
|
||||
const argumentValue = item.querySelector('.arg-value').value.trim();
|
||||
|
||||
if (description && argumentValue) {
|
||||
arguments.push({ description, arguments: argumentValue });
|
||||
}
|
||||
});
|
||||
|
||||
return arguments;
|
||||
}
|
||||
|
||||
async function saveCSharpExecutableArguments(projectId, exeName) {
|
||||
const arguments = collectCSharpArguments();
|
||||
|
||||
try {
|
||||
const response = await fetch(`/api/csharp-executable-arguments/${projectId}/${exeName}`, {
|
||||
method: 'PUT',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ arguments })
|
||||
});
|
||||
|
||||
const result = await response.json();
|
||||
|
||||
if (result.status === 'success') {
|
||||
showNotification(result.message, 'success');
|
||||
closeCSharpArgumentsEditor();
|
||||
} else {
|
||||
showNotification(result.message, 'error');
|
||||
}
|
||||
} catch (error) {
|
||||
showNotification('Error guardando argumentos', 'error');
|
||||
console.error('Error saving arguments:', error);
|
||||
}
|
||||
}
|
||||
|
||||
function closeCSharpArgumentsEditor() {
|
||||
const modal = document.getElementById('csharp-arguments-modal');
|
||||
if (modal) modal.remove();
|
||||
}
|
||||
|
||||
async function showCSharpExecutableArgs(projectId, exeName, displayName) {
|
||||
try {
|
||||
// Cargar argumentos predefinidos
|
||||
const response = await fetch(`/api/csharp-executable-arguments/${projectId}/${exeName}`);
|
||||
const data = response.ok ? await response.json() : { arguments: [] };
|
||||
const predefinedArgs = data.arguments || [];
|
||||
|
||||
showCSharpExecutableArgsModal(projectId, exeName, displayName, predefinedArgs);
|
||||
} catch (error) {
|
||||
console.error('Error loading predefined arguments:', error);
|
||||
showCSharpExecutableArgsModal(projectId, exeName, displayName, []);
|
||||
}
|
||||
}
|
||||
|
||||
function showCSharpExecutableArgsModal(projectId, exeName, displayName, predefinedArgs) {
|
||||
const predefinedSection = predefinedArgs.length > 0 ? `
|
||||
<div class="mb-4">
|
||||
<label class="block text-sm font-medium text-gray-700 mb-2">Argumentos predefinidos:</label>
|
||||
<div class="space-y-2">
|
||||
${predefinedArgs.map((arg, index) => `
|
||||
<button type="button"
|
||||
onclick="selectPredefinedCSharpArguments('${arg.arguments.replace(/'/g, "\\'")}', '${arg.description}')"
|
||||
class="w-full text-left p-3 border border-gray-300 rounded-lg hover:bg-gray-50">
|
||||
<div class="font-medium text-sm">${arg.description}</div>
|
||||
<div class="text-xs text-gray-600 mt-1">${arg.arguments}</div>
|
||||
</button>
|
||||
`).join('')}
|
||||
</div>
|
||||
</div>
|
||||
` : '';
|
||||
|
||||
const modalHtml = `
|
||||
<div id="csharp-args-modal" class="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50">
|
||||
<div class="bg-white rounded-lg p-6 w-full max-w-md">
|
||||
<h3 class="text-lg font-semibold mb-4">Ejecutar: ${displayName}</h3>
|
||||
|
||||
${predefinedSection}
|
||||
|
||||
<div class="mb-4">
|
||||
<label class="block text-sm font-medium text-gray-700 mb-2">Argumentos personalizados:</label>
|
||||
<input type="text" id="csharp-custom-args" placeholder="--debug --verbose"
|
||||
class="w-full border border-gray-300 rounded-md px-3 py-2">
|
||||
</div>
|
||||
|
||||
<div class="flex justify-end space-x-3">
|
||||
<button onclick="closeCSharpArgsModal()"
|
||||
class="px-4 py-2 text-gray-600 border border-gray-300 rounded-md hover:bg-gray-50">
|
||||
Cancelar
|
||||
</button>
|
||||
<button onclick="executeCSharpWithArgs('${projectId}', '${exeName}')"
|
||||
class="px-4 py-2 bg-blue-500 text-white rounded-md hover:bg-blue-600">
|
||||
Ejecutar
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
document.body.insertAdjacentHTML('beforeend', modalHtml);
|
||||
}
|
||||
|
||||
function selectPredefinedCSharpArguments(args, description) {
|
||||
const input = document.getElementById('csharp-custom-args');
|
||||
if (input) {
|
||||
input.value = args;
|
||||
}
|
||||
}
|
||||
|
||||
async function executeCSharpWithArgs(projectId, exeName) {
|
||||
const argsInput = document.getElementById('csharp-custom-args');
|
||||
const argsString = argsInput ? argsInput.value.trim() : '';
|
||||
const args = argsString ? argsString.split(' ').filter(arg => arg.length > 0) : [];
|
||||
|
||||
closeCSharpArgsModal();
|
||||
await csharpLauncherManager.executeExecutable(projectId, exeName, args);
|
||||
}
|
||||
|
||||
function closeCSharpArgsModal() {
|
||||
const modal = document.getElementById('csharp-args-modal');
|
||||
if (modal) modal.remove();
|
||||
}
|
||||
|
||||
function closeCSharpExecutableManager() {
|
||||
const modal = document.getElementById('csharp-executable-manager');
|
||||
if (modal) modal.style.display = 'none';
|
||||
}
|
||||
|
||||
function showCSharpExecutableArgs(projectId, exeName, displayName) {
|
||||
// TODO: Implementar modal de argumentos para C#
|
||||
const args = prompt(`Argumentos para ${displayName}:`, '');
|
||||
|
|
|
@ -497,6 +497,128 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Modal: Editor de Proyectos C# -->
|
||||
<div id="csharp-project-editor-modal"
|
||||
class="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50 hidden">
|
||||
<div class="bg-white rounded-lg shadow-xl w-full max-w-6xl mx-4 max-h-[90vh] overflow-hidden">
|
||||
<div class="flex justify-between items-center p-6 border-b">
|
||||
<h2 class="text-xl font-bold">Gestión de Proyectos C#</h2>
|
||||
<button onclick="closeCSharpProjectEditor()" class="text-gray-500 hover:text-gray-700">
|
||||
<svg class="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
|
||||
d="M6 18L18 6M6 6l12 12"></path>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="flex h-[70vh]">
|
||||
<!-- Lista de Proyectos Existentes -->
|
||||
<div class="w-1/2 p-6 border-r overflow-y-auto">
|
||||
<h3 class="text-lg font-semibold mb-4">Proyectos Existentes</h3>
|
||||
<div id="csharp-existing-projects" class="space-y-3">
|
||||
<!-- Lista dinámica de proyectos -->
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Formulario de Proyecto -->
|
||||
<div class="w-1/2 p-6 overflow-y-auto">
|
||||
<h3 class="text-lg font-semibold mb-4">Agregar/Editar Proyecto</h3>
|
||||
<form id="csharp-project-form" class="space-y-4">
|
||||
<input type="hidden" id="csharp-project-id">
|
||||
|
||||
<!-- Nombre del Proyecto -->
|
||||
<div>
|
||||
<label class="block text-sm font-medium mb-1">Nombre del Proyecto *</label>
|
||||
<input type="text" id="csharp-project-name" class="w-full p-2 border rounded-lg"
|
||||
placeholder="Mi Aplicación C#" required>
|
||||
</div>
|
||||
|
||||
<!-- Descripción -->
|
||||
<div>
|
||||
<label class="block text-sm font-medium mb-1">Descripción</label>
|
||||
<textarea id="csharp-project-description" class="w-full p-2 border rounded-lg h-20"
|
||||
placeholder="Descripción del proyecto"></textarea>
|
||||
</div>
|
||||
|
||||
<!-- Directorio -->
|
||||
<div>
|
||||
<label class="block text-sm font-medium mb-1">Directorio del Proyecto *</label>
|
||||
<div class="flex gap-2">
|
||||
<input type="text" id="csharp-project-directory"
|
||||
class="flex-1 p-2 border rounded-lg" placeholder="C:\Proyectos\MiApp" required>
|
||||
<button type="button" onclick="browseCSharpProjectDirectory()"
|
||||
class="bg-gray-500 text-white px-3 py-2 rounded-lg hover:bg-gray-600">
|
||||
📁
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Categoría -->
|
||||
<div>
|
||||
<label class="block text-sm font-medium mb-1">Categoría</label>
|
||||
<select id="csharp-project-category" class="w-full p-2 border rounded-lg">
|
||||
<option value="Aplicaciones">🖥️ Aplicaciones</option>
|
||||
<option value="Herramientas">🔧 Herramientas</option>
|
||||
<option value="Análisis">📊 Análisis</option>
|
||||
<option value="Desarrollo">💻 Desarrollo</option>
|
||||
<option value="APIs">🌐 APIs</option>
|
||||
<option value="Otros">📁 Otros</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<!-- Versión -->
|
||||
<div>
|
||||
<label class="block text-sm font-medium mb-1">Versión</label>
|
||||
<input type="text" id="csharp-project-version" class="w-full p-2 border rounded-lg"
|
||||
placeholder="1.0" value="1.0">
|
||||
</div>
|
||||
|
||||
<!-- Autor -->
|
||||
<div>
|
||||
<label class="block text-sm font-medium mb-1">Autor</label>
|
||||
<input type="text" id="csharp-project-author" class="w-full p-2 border rounded-lg"
|
||||
placeholder="Nombre del desarrollador">
|
||||
</div>
|
||||
|
||||
<!-- Versión .NET -->
|
||||
<div>
|
||||
<label class="block text-sm font-medium mb-1">Versión .NET</label>
|
||||
<input type="text" id="csharp-project-dotnet-version"
|
||||
class="w-full p-2 border rounded-lg"
|
||||
placeholder="6.0, Framework 4.8, Core 3.1, etc.">
|
||||
</div>
|
||||
|
||||
<!-- Tags -->
|
||||
<div>
|
||||
<label class="block text-sm font-medium mb-1">Tags</label>
|
||||
<input type="text" id="csharp-project-tags" class="w-full p-2 border rounded-lg"
|
||||
placeholder="winforms, wpf, console, api (separados por comas)">
|
||||
</div>
|
||||
|
||||
<!-- Botones -->
|
||||
<div class="flex justify-between pt-4">
|
||||
<button type="button" onclick="deleteCSharpProject()"
|
||||
class="bg-red-500 text-white px-4 py-2 rounded-lg hover:bg-red-600"
|
||||
id="delete-csharp-project-btn">
|
||||
Eliminar Proyecto
|
||||
</button>
|
||||
<div class="space-x-2">
|
||||
<button type="button" onclick="closeCSharpProjectEditor()"
|
||||
class="bg-gray-500 text-white px-4 py-2 rounded-lg hover:bg-gray-600">
|
||||
Cancelar
|
||||
</button>
|
||||
<button type="button" onclick="saveCSharpProject()"
|
||||
class="bg-blue-500 text-white px-4 py-2 rounded-lg hover:bg-blue-600">
|
||||
Guardar Proyecto
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Logs (común para ambos sistemas) -->
|
||||
<div class="bg-white p-6 rounded-lg shadow">
|
||||
<div class="flex justify-between items-center mb-4">
|
||||
|
@ -910,6 +1032,36 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<!-- C# Executable Manager Modal -->
|
||||
<div id="csharp-executable-manager" class="hidden fixed inset-0 bg-gray-600 bg-opacity-50 z-50">
|
||||
<div class="flex items-center justify-center min-h-screen p-4">
|
||||
<div class="bg-white rounded-lg shadow-xl max-w-4xl w-full max-h-[90vh] overflow-hidden">
|
||||
<div class="p-6 border-b">
|
||||
<div class="flex justify-between items-center">
|
||||
<h3 class="text-lg font-semibold">Gestionar Ejecutables C#</h3>
|
||||
<button onclick="closeCSharpExecutableManager()"
|
||||
class="text-gray-500 hover:text-gray-700 text-2xl">×</button>
|
||||
</div>
|
||||
<p class="text-sm text-gray-600 mt-1">Edita la visibilidad, descripciones y argumentos de los
|
||||
ejecutables</p>
|
||||
</div>
|
||||
|
||||
<div class="p-6 overflow-y-auto max-h-[75vh]">
|
||||
<div id="csharp-executable-list">
|
||||
<!-- Lista dinámica de ejecutables -->
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="p-4 border-t bg-gray-50 flex justify-end">
|
||||
<button onclick="closeCSharpExecutableManager()"
|
||||
class="px-4 py-2 bg-gray-500 text-white rounded hover:bg-gray-600">
|
||||
Cerrar
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="https://unpkg.com/markdown-it@14.1.0/dist/markdown-it.min.js"></script>
|
||||
<script src="{{ url_for('static', filename='js/scripts.js') }}" defer></script>
|
||||
<script src="{{ url_for('static', filename='js/launcher.js') }}" defer></script>
|
||||
|
|
Loading…
Reference in New Issue