diff --git a/backend/app.py b/backend/app.py index e7e59c5..c295683 100644 --- a/backend/app.py +++ b/backend/app.py @@ -1,21 +1,35 @@ # backend/app.py +import os +import sys +from pathlib import Path + +# Add the backend directory to Python path +backend_dir = Path(__file__).parent +if str(backend_dir) not in sys.path: + sys.path.append(str(backend_dir)) + from flask import Flask, render_template, jsonify, request, send_from_directory from core.directory_handler import select_directory -from pathlib import Path +from core.script_manager import ScriptManager from core.profile_manager import ProfileManager app = Flask(__name__, template_folder='../frontend/templates', static_folder='../frontend/static') -# Initialize profile manager -profile_manager = ProfileManager(Path('../data')) +# Initialize managers +data_dir = Path(__file__).parent.parent / 'data' +script_groups_dir = Path(__file__).parent / 'script_groups' + +profile_manager = ProfileManager(data_dir) +script_manager = ScriptManager(script_groups_dir) @app.route('/') def index(): """Render main page""" return render_template('index.html') +# Profile endpoints @app.route('/api/profiles', methods=['GET']) def get_profiles(): """Get all profiles""" @@ -58,6 +72,7 @@ def delete_profile(profile_id): except Exception as e: return jsonify({"error": str(e)}), 400 +# Directory handling endpoints @app.route('/api/select-directory', methods=['GET']) def handle_select_directory(): """Handle directory selection""" @@ -66,5 +81,61 @@ def handle_select_directory(): return jsonify(result), 400 return jsonify(result) +# Script management endpoints +@app.route('/api/scripts', methods=['GET']) +def get_scripts(): + """Get all available script groups""" + try: + groups = script_manager.discover_groups() + return jsonify(groups) + except Exception as e: + return jsonify({"error": str(e)}), 500 + +@app.route('/api/scripts///run', methods=['POST']) +def run_script(group_id, script_id): + """Execute a specific script""" + data = request.json + work_dir = data.get('work_dir') + profile = data.get('profile') + + if not work_dir: + return jsonify({"error": "Work directory not specified"}), 400 + if not profile: + return jsonify({"error": "Profile not specified"}), 400 + + try: + result = script_manager.execute_script(group_id, script_id, work_dir, profile) + return jsonify(result) + except Exception as e: + return jsonify({"error": str(e)}), 500 + +# Work directory configuration endpoints +@app.route('/api/workdir-config/', methods=['GET']) +def get_workdir_config(work_dir): + """Get work directory configuration""" + from core.workdir_config import WorkDirConfigManager + config_manager = WorkDirConfigManager(work_dir) + return jsonify(config_manager.get_config()) + +@app.route('/api/workdir-config//group/', methods=['GET']) +def get_group_config(work_dir, group_id): + """Get group configuration from work directory""" + from core.workdir_config import WorkDirConfigManager + config_manager = WorkDirConfigManager(work_dir) + return jsonify(config_manager.get_group_config(group_id)) + +@app.route('/api/workdir-config//group/', methods=['PUT']) +def update_group_config(work_dir, group_id): + """Update group configuration in work directory""" + from core.workdir_config import WorkDirConfigManager + config_manager = WorkDirConfigManager(work_dir) + + try: + settings = request.json + config_manager.update_group_config(group_id, settings) + return jsonify({"status": "success"}) + except Exception as e: + return jsonify({"error": str(e)}), 400 + if __name__ == '__main__': - app.run(debug=True, port=5000) \ No newline at end of file + app.run(debug=True, port=5000) diff --git a/backend/core/__init__.py b/backend/core/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/backend/core/__pycache__/__init__.cpython-310.pyc b/backend/core/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..9000c4e Binary files /dev/null and b/backend/core/__pycache__/__init__.cpython-310.pyc differ diff --git a/backend/core/__pycache__/directory_handler.cpython-310.pyc b/backend/core/__pycache__/directory_handler.cpython-310.pyc new file mode 100644 index 0000000..1bd90e3 Binary files /dev/null and b/backend/core/__pycache__/directory_handler.cpython-310.pyc differ diff --git a/backend/core/__pycache__/profile_manager.cpython-310.pyc b/backend/core/__pycache__/profile_manager.cpython-310.pyc new file mode 100644 index 0000000..a11e478 Binary files /dev/null and b/backend/core/__pycache__/profile_manager.cpython-310.pyc differ diff --git a/backend/core/__pycache__/script_manager.cpython-310.pyc b/backend/core/__pycache__/script_manager.cpython-310.pyc new file mode 100644 index 0000000..3962c1f Binary files /dev/null and b/backend/core/__pycache__/script_manager.cpython-310.pyc differ diff --git a/backend/core/__pycache__/workdir_config.cpython-310.pyc b/backend/core/__pycache__/workdir_config.cpython-310.pyc new file mode 100644 index 0000000..450317b Binary files /dev/null and b/backend/core/__pycache__/workdir_config.cpython-310.pyc differ diff --git a/backend/core/script_manager.py b/backend/core/script_manager.py new file mode 100644 index 0000000..cf6f0fa --- /dev/null +++ b/backend/core/script_manager.py @@ -0,0 +1,112 @@ +# backend/core/script_manager.py +from pathlib import Path +import importlib.util +import inspect +from typing import Dict, List, Any, Optional +import json + +class ScriptManager: + """Manages script discovery and execution""" + + def __init__(self, script_groups_dir: Path): + self.script_groups_dir = script_groups_dir + + def discover_groups(self) -> List[Dict[str, Any]]: + """Discover all script groups""" + groups = [] + + for group_dir in self.script_groups_dir.iterdir(): + if group_dir.is_dir() and not group_dir.name.startswith('_'): + group_info = self._analyze_group(group_dir) + if group_info: + groups.append(group_info) + + return groups + + def _analyze_group(self, group_dir: Path) -> Optional[Dict[str, Any]]: + """Analyze a script group directory""" + scripts = [] + + for script_file in group_dir.glob('x[0-9].py'): + try: + script_info = self._analyze_script(script_file) + if script_info: + scripts.append(script_info) + except Exception as e: + print(f"Error analyzing script {script_file}: {e}") + + if scripts: + return { + "id": group_dir.name, + "name": group_dir.name.replace('_', ' ').title(), + "scripts": sorted(scripts, key=lambda x: x['id']) + } + return None + + def _analyze_script(self, script_file: Path) -> Optional[Dict[str, Any]]: + """Analyze a single script file""" + try: + # Import script module + spec = importlib.util.spec_from_file_location( + script_file.stem, + script_file + ) + module = importlib.util.module_from_spec(spec) + spec.loader.exec_module(module) + + # Find script class + script_class = None + for name, obj in inspect.getmembers(module): + if (inspect.isclass(obj) and + obj.__module__ == module.__name__ and + hasattr(obj, 'run')): + script_class = obj + break + + if script_class: + return { + "id": script_file.stem, + "name": script_class.__doc__.split('\n')[0].strip() if script_class.__doc__ else script_file.stem, + "description": inspect.getdoc(script_class), + "file": str(script_file.relative_to(self.script_groups_dir)) + } + + except Exception as e: + print(f"Error loading script {script_file}: {e}") + + return None + + def execute_script(self, group_id: str, script_id: str, work_dir: str, + profile: Dict[str, Any]) -> Dict[str, Any]: + """Execute a specific script""" + script_file = self.script_groups_dir / group_id / f"{script_id}.py" + + if not script_file.exists(): + raise ValueError(f"Script {script_id} not found in group {group_id}") + + try: + # Import script module + spec = importlib.util.spec_from_file_location(script_id, script_file) + module = importlib.util.module_from_spec(spec) + spec.loader.exec_module(module) + + # Find and instantiate script class + script_class = None + for name, obj in inspect.getmembers(module): + if (inspect.isclass(obj) and + obj.__module__ == module.__name__ and + hasattr(obj, 'run')): + script_class = obj + break + + if not script_class: + raise ValueError(f"No valid script class found in {script_id}") + + script = script_class() + return script.run(work_dir, profile) + + except Exception as e: + return { + "status": "error", + "error": str(e) + } diff --git a/backend/script_groups/__init__.py b/backend/script_groups/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/backend/script_groups/base_script.py b/backend/script_groups/base_script.py new file mode 100644 index 0000000..2a34b9a --- /dev/null +++ b/backend/script_groups/base_script.py @@ -0,0 +1,61 @@ +# backend/script_groups/base_script.py +from typing import Dict, Any +from pathlib import Path +import json + +class BaseScript: + """Base class for all scripts""" + + def run(self, work_dir: str, profile: Dict[str, Any]) -> Dict[str, Any]: + """ + Execute the script + + Args: + work_dir (str): Working directory path + profile (Dict[str, Any]): Current profile configuration + + Returns: + Dict[str, Any]: Execution results + """ + raise NotImplementedError("Script must implement run method") + + def get_config(self, work_dir: str, group_id: str) -> Dict[str, Any]: + """Get group configuration from work directory""" + config_file = Path(work_dir) / "script_config.json" + + if config_file.exists(): + try: + with open(config_file, 'r', encoding='utf-8') as f: + config = json.load(f) + return config.get("group_settings", {}).get(group_id, {}) + except Exception as e: + print(f"Error loading config: {e}") + + return {} + + def save_config(self, work_dir: str, group_id: str, settings: Dict[str, Any]): + """Save group configuration to work directory""" + config_file = Path(work_dir) / "script_config.json" + + try: + # Load existing config or create new + if config_file.exists(): + with open(config_file, 'r', encoding='utf-8') as f: + config = json.load(f) + else: + config = { + "version": "1.0", + "group_settings": {} + } + + # Update settings + if "group_settings" not in config: + config["group_settings"] = {} + config["group_settings"][group_id] = settings + + # Save config + with open(config_file, 'w', encoding='utf-8') as f: + json.dump(config, f, indent=4) + + except Exception as e: + print(f"Error saving config: {e}") diff --git a/backend/script_groups/example_group/__init__.py b/backend/script_groups/example_group/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/backend/script_groups/example_group/__pycache__/x1.cpython-310.pyc b/backend/script_groups/example_group/__pycache__/x1.cpython-310.pyc new file mode 100644 index 0000000..c1e701b Binary files /dev/null and b/backend/script_groups/example_group/__pycache__/x1.cpython-310.pyc differ diff --git a/backend/script_groups/example_group/__pycache__/x2.cpython-310.pyc b/backend/script_groups/example_group/__pycache__/x2.cpython-310.pyc new file mode 100644 index 0000000..8a20ed3 Binary files /dev/null and b/backend/script_groups/example_group/__pycache__/x2.cpython-310.pyc differ diff --git a/backend/script_groups/example_group/x1.py b/backend/script_groups/example_group/x1.py new file mode 100644 index 0000000..3dc0f84 --- /dev/null +++ b/backend/script_groups/example_group/x1.py @@ -0,0 +1,49 @@ +# backend/script_groups/example_group/x1.py +from ..base_script import BaseScript +import os +from pathlib import Path + +class FileCounter(BaseScript): + """ + Count Files in Directory + Lists and counts files in the working directory by extension + """ + + def run(self, work_dir: str, profile: dict) -> dict: + try: + # Get configuration if any + config = self.get_config(work_dir, "example_group") + exclude_dirs = config.get("exclude_dirs", []) + + # Initialize counters + extension_counts = {} + total_files = 0 + + # Walk through directory + for root, dirs, files in os.walk(work_dir): + # Skip excluded directories + dirs[:] = [d for d in dirs if d not in exclude_dirs] + + for file in files: + total_files += 1 + ext = Path(file).suffix.lower() or 'no extension' + extension_counts[ext] = extension_counts.get(ext, 0) + 1 + + return { + "status": "success", + "data": { + "total_files": total_files, + "extension_counts": extension_counts + }, + "output": f"Found {total_files} files\n" + "\n".join( + f"{ext}: {count} files" + for ext, count in sorted(extension_counts.items()) + ) + } + + except Exception as e: + return { + "status": "error", + "error": str(e) + } + diff --git a/backend/script_groups/example_group/x2.py b/backend/script_groups/example_group/x2.py new file mode 100644 index 0000000..f2dfbef --- /dev/null +++ b/backend/script_groups/example_group/x2.py @@ -0,0 +1,57 @@ +# backend/script_groups/example_group/x2.py +from ..base_script import BaseScript +import psutil +import json +from datetime import datetime + +class SystemInfo(BaseScript): + """ + System Information + Collects and displays basic system information + """ + + def run(self, work_dir: str, profile: dict) -> dict: + try: + # Collect system information + info = { + "cpu": { + "cores": psutil.cpu_count(), + "usage": psutil.cpu_percent(interval=1), + }, + "memory": { + "total": psutil.virtual_memory().total, + "available": psutil.virtual_memory().available, + "percent": psutil.virtual_memory().percent, + }, + "disk": { + "total": psutil.disk_usage(work_dir).total, + "free": psutil.disk_usage(work_dir).free, + "percent": psutil.disk_usage(work_dir).percent, + }, + "timestamp": datetime.now().isoformat() + } + + # Save to work directory if configured + config = self.get_config(work_dir, "example_group") + if config.get("save_system_info", False): + output_file = Path(work_dir) / "system_info.json" + with open(output_file, 'w') as f: + json.dump(info, f, indent=2) + + # Format output + output = f"""System Information: +CPU: {info['cpu']['cores']} cores ({info['cpu']['usage']}% usage) +Memory: {info['memory']['percent']}% used +Disk: {info['disk']['percent']}% used""" + + return { + "status": "success", + "data": info, + "output": output + } + + except Exception as e: + return { + "status": "error", + "error": str(e) + } diff --git a/data/profiles.json b/data/profiles.json new file mode 100644 index 0000000..ea2b923 --- /dev/null +++ b/data/profiles.json @@ -0,0 +1,26 @@ +{ + "default": { + "id": "default", + "name": "Default Profile", + "work_dir": "", + "llm_settings": { + "model": "gpt-4", + "temperature": 0.7, + "api_key": "" + }, + "created_at": "2025-02-07T12:47:49.766608", + "updated_at": "2025-02-07T12:47:49.766608" + }, + "1": { + "id": "1", + "name": "Base", + "work_dir": "D:/Proyectos/AutoCAD", + "llm_settings": { + "api_key": "333333333333", + "model": "gpt-4", + "temperature": 0.7 + }, + "created_at": "2025-02-07T13:00:43.541932", + "updated_at": "2025-02-07T13:01:40.473406" + } +} \ No newline at end of file diff --git a/files.txt b/files.txt new file mode 100644 index 0000000..dd78c0b Binary files /dev/null and b/files.txt differ diff --git a/frontend/static/css/style.css b/frontend/static/css/style.css index 02df161..bc6f798 100644 --- a/frontend/static/css/style.css +++ b/frontend/static/css/style.css @@ -1,113 +1,163 @@ -# frontend/static/css/style.css -:root { - --primary-color: #2c3e50; - --secondary-color: #34495e; - --accent-color: #3498db; - --text-color: #333; - --bg-color: #f5f6fa; - --border-color: #dcdde1; -} +/* frontend/static/css/style.css - Add these styles */ -* { - margin: 0; - padding: 0; - box-sizing: border-box; -} - -body { - font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; - line-height: 1.6; - color: var(--text-color); - background-color: var(--bg-color); -} - -header { - background-color: var(--primary-color); - color: white; +.script-group { + background: white; + border-radius: 8px; padding: 1rem; + box-shadow: 0 2px 4px rgba(0,0,0,0.1); } -nav { +.script-group-header { display: flex; justify-content: space-between; align-items: center; - max-width: 1200px; - margin: 0 auto; + margin-bottom: 1rem; + padding-bottom: 0.5rem; + border-bottom: 1px solid var(--border-color); } -.nav-brand { - font-size: 1.5rem; - font-weight: bold; +.script-group-header h3 { + margin: 0; + color: var(--primary-color); } -.profile-selector { +.script-list { display: flex; + flex-direction: column; gap: 1rem; - align-items: center; } -.container { - max-width: 1200px; - margin: 2rem auto; - padding: 0 1rem; -} - -select, input, button { - padding: 0.5rem 1rem; - border: 1px solid var(--border-color); - border-radius: 4px; - font-size: 1rem; -} - -button { - background-color: var(--accent-color); - color: white; - border: none; - cursor: pointer; - transition: background-color 0.3s; -} - -button:hover { - background-color: #2980b9; -} - -.work-dir-section { - background: white; +.script-item { + display: flex; + justify-content: space-between; + align-items: start; padding: 1rem; - border-radius: 8px; - margin-bottom: 2rem; - box-shadow: 0 2px 4px rgba(0,0,0,0.1); + background: var(--bg-color); + border-radius: 4px; } -.work-dir-controls { - display: flex; - gap: 1rem; - margin-top: 1rem; -} - -.work-dir-controls input { +.script-info { flex: 1; } -.scripts-section { - background: white; - padding: 1rem; - border-radius: 8px; - margin-bottom: 2rem; - box-shadow: 0 2px 4px rgba(0,0,0,0.1); +.script-info h4 { + margin: 0 0 0.5rem 0; + color: var(--secondary-color); } -.script-groups { - display: grid; - grid-template-columns: repeat(auto-fill, minmax(300px, 1fr)); - gap: 1rem; - margin-top: 1rem; +.script-info p { + margin: 0; + font-size: 0.9rem; + color: #666; } -.script-group { - background: var(--bg-color); - padding: 1rem; +.script-actions { + display: flex; + gap: 0.5rem; +} + +.config-btn { + background-color: var(--secondary-color); +} + +.run-btn { + background-color: #27ae60; +} + +.run-btn:hover { + background-color: #219a52; +} + +.config-editor { + width: 100%; + font-family: monospace; + padding: 0.5rem; + border: 1px solid var(--border-color); border-radius: 4px; } -.script \ No newline at end of file +.output-area { + background: white; + padding: 1rem; + border-radius: 4px; + border: 1px solid var(--border-color); + height: 200px; + overflow-y: auto; + font-family: monospace; + white-space: pre-wrap; +} + +.no-scripts { + text-align: center; + color: #666; + padding: 2rem; +} + +/* Add to your existing style.css */ + +.modal { + position: fixed; + top: 0; + left: 0; + width: 100%; + height: 100%; + background: rgba(0, 0, 0, 0.5); + display: flex; + justify-content: center; + align-items: center; + z-index: 1000; +} + +.modal-content { + background: white; + padding: 2rem; + border-radius: 8px; + max-width: 600px; + width: 90%; + max-height: 90vh; + overflow-y: auto; +} + +.form-group { + margin-bottom: 1rem; +} + +.form-group label { + display: block; + margin-bottom: 0.5rem; + font-weight: 500; +} + +.form-group input, +.form-group select, +.form-group textarea { + width: 100%; +} + +.button-group { + display: flex; + justify-content: flex-end; + gap: 1rem; + margin-top: 2rem; +} + +.config-info { + margin-bottom: 1.5rem; +} + +.group-configs { + margin-top: 1.5rem; +} + +.group-config { + margin-top: 1rem; + padding: 1rem; + background: var(--bg-color); + border-radius: 4px; +} + +.group-config pre { + margin-top: 0.5rem; + white-space: pre-wrap; + font-size: 0.9rem; +} \ No newline at end of file diff --git a/frontend/static/js/main.js b/frontend/static/js/main.js index 1b8339b..1b2c923 100644 --- a/frontend/static/js/main.js +++ b/frontend/static/js/main.js @@ -1,11 +1,18 @@ -# frontend/static/js/main.js +// frontend/static/js/main.js + // Global state let currentProfile = null; // Initialize when page loads document.addEventListener('DOMContentLoaded', async () => { - await loadProfiles(); - updateWorkDirDisplay(); + try { + await loadProfiles(); + await loadScriptGroups(); + updateWorkDirDisplay(); + } catch (error) { + console.error('Initialization error:', error); + showError('Failed to initialize application'); + } }); // API functions @@ -70,13 +77,18 @@ async function selectProfile(profileId) { async function changeProfile() { const select = document.getElementById('profileSelect'); - await selectProfile(select.value); + if (select.value) { + await selectProfile(select.value); + await loadScriptGroups(); // Reload scripts when profile changes + } } // Work directory functions function updateWorkDirDisplay() { const input = document.getElementById('workDirPath'); - input.value = currentProfile?.work_dir || ''; + if (input && currentProfile) { + input.value = currentProfile.work_dir || ''; + } } async function selectWorkDir() { @@ -101,17 +113,39 @@ async function selectWorkDir() { // Output functions function showError(message) { const output = document.getElementById('outputArea'); - output.innerHTML += `\nError: ${message}`; + const timestamp = new Date().toLocaleTimeString(); + output.innerHTML += `\n[${timestamp}] ERROR: ${message}`; output.scrollTop = output.scrollHeight; } function showSuccess(message) { const output = document.getElementById('outputArea'); - output.innerHTML += `\nSuccess: ${message}`; + const timestamp = new Date().toLocaleTimeString(); + output.innerHTML += `\n[${timestamp}] SUCCESS: ${message}`; output.scrollTop = output.scrollHeight; } function clearOutput() { const output = document.getElementById('outputArea'); output.innerHTML = ''; -} \ No newline at end of file +} + +// Modal helper functions +function closeModal(button) { + const modal = button.closest('.modal'); + if (modal) { + modal.remove(); + } +} + +// Global error handler +window.addEventListener('unhandledrejection', function(event) { + console.error('Unhandled promise rejection:', event.reason); + showError('An unexpected error occurred'); +}); + +// Export functions for use in other modules +window.showError = showError; +window.showSuccess = showSuccess; +window.closeModal = closeModal; +window.currentProfile = currentProfile; \ No newline at end of file diff --git a/frontend/static/js/profile.js b/frontend/static/js/profile.js index 3893e11..07e4417 100644 --- a/frontend/static/js/profile.js +++ b/frontend/static/js/profile.js @@ -1,4 +1,65 @@ -# frontend/static/js/profile.js +// frontend/static/js/profile.js + +// Profile functions +async function loadProfiles() { + try { + const profiles = await apiRequest('/profiles'); + updateProfileSelector(profiles); + + // Select first profile if none selected + if (!currentProfile) { + const defaultProfile = profiles.find(p => p.id === 'default') || profiles[0]; + if (defaultProfile) { + await selectProfile(defaultProfile.id); + } + } + } catch (error) { + showError('Failed to load profiles'); + } +} + +function updateProfileSelector(profiles) { + const select = document.getElementById('profileSelect'); + select.innerHTML = profiles.map(profile => ` + + `).join(''); +} + +async function selectProfile(profileId) { + try { + currentProfile = await apiRequest(`/profiles/${profileId}`); + updateWorkDirDisplay(); + } catch (error) { + showError('Failed to load profile'); + } +} + +async function changeProfile() { + const select = document.getElementById('profileSelect'); + await selectProfile(select.value); +} + +async function selectWorkDir() { + try { + const response = await apiRequest('/select-directory'); + if (response.path) { + await apiRequest(`/profiles/${currentProfile.id}`, { + method: 'PUT', + body: JSON.stringify({ + ...currentProfile, + work_dir: response.path + }) + }); + await selectProfile(currentProfile.id); + showSuccess('Work directory updated successfully'); + } + } catch (error) { + showError('Failed to update work directory'); + } +} + // Profile editor modal let editingProfile = null; @@ -27,7 +88,7 @@ function showProfileEditor(profile = null) {
+ value="${profile?.work_dir || ''}" readonly>
@@ -48,7 +109,7 @@ function showProfileEditor(profile = null) { min="0" max="2" step="0.1">
- +
@@ -58,14 +119,6 @@ function showProfileEditor(profile = null) { document.body.appendChild(modal); } -function closeProfileEditor() { - const modal = document.querySelector('.modal'); - if (modal) { - modal.remove(); - } - editingProfile = null; -} - async function saveProfile(event) { event.preventDefault(); const form = event.target; @@ -96,7 +149,7 @@ async function saveProfile(event) { } await loadProfiles(); - closeProfileEditor(); + closeModal(event.target); showSuccess(`Profile ${editingProfile ? 'updated' : 'created'} successfully`); } catch (error) { showError(`Failed to ${editingProfile ? 'update' : 'create'} profile`); diff --git a/frontend/static/js/scripts.js b/frontend/static/js/scripts.js new file mode 100644 index 0000000..1b61ff0 --- /dev/null +++ b/frontend/static/js/scripts.js @@ -0,0 +1,145 @@ +// frontend/static/js/scripts.js + +// Script groups state +let scriptGroups = []; + +// Load script groups when page loads +document.addEventListener('DOMContentLoaded', async () => { + await loadScriptGroups(); +}); + +// Load script groups from API +async function loadScriptGroups() { + try { + scriptGroups = await apiRequest('/scripts'); + updateScriptGroupsDisplay(); + } catch (error) { + showError('Failed to load script groups'); + } +} + +// Update script groups display +function updateScriptGroupsDisplay() { + const container = document.getElementById('scriptGroups'); + + if (!scriptGroups.length) { + container.innerHTML = '

No script groups available

'; + return; + } + + container.innerHTML = scriptGroups.map(group => ` +
+
+

${group.name}

+ +
+
+ ${group.scripts.map(script => ` +
+
+

${script.name}

+

${script.description || 'No description available'}

+
+
+ +
+
+ `).join('')} +
+
+ `).join(''); +} + +// Run a script +async function runScript(groupId, scriptId) { + if (!currentProfile?.work_dir) { + showError('Please select a work directory first'); + return; + } + + try { + const result = await apiRequest(`/scripts/${groupId}/${scriptId}/run`, { + method: 'POST', + body: JSON.stringify({ + work_dir: currentProfile.work_dir, + profile: currentProfile + }) + }); + + if (result.status === 'error') { + showError(result.error); + } else { + showSuccess(`Script ${scriptId} executed successfully`); + if (result.output) { + const output = document.getElementById('outputArea'); + output.innerHTML += `\n[${new Date().toLocaleTimeString()}] ${result.output}`; + output.scrollTop = output.scrollHeight; + } + } + } catch (error) { + showError(`Failed to run script: ${error.message}`); + } +} + +// Configure script group +async function configureGroup(groupId) { + if (!currentProfile?.work_dir) { + showError('Please select a work directory first'); + return; + } + + try { + const config = await getGroupConfig(groupId); + showGroupConfigEditor(groupId, config); + } catch (error) { + showError('Failed to load group configuration'); + } +} + +// Show group configuration editor +function showGroupConfigEditor(groupId, config) { + const group = scriptGroups.find(g => g.id === groupId); + if (!group) return; + + const modal = document.createElement('div'); + modal.className = 'modal active'; + + modal.innerHTML = ` + + `; + + document.body.appendChild(modal); +} + +// Save group configuration +async function saveGroupConfig(event, groupId) { + event.preventDefault(); + + try { + const configText = document.getElementById('configData').value; + const config = JSON.parse(configText); + + await updateGroupConfig(groupId, config); + closeModal(event.target); + showSuccess('Group configuration updated successfully'); + } catch (error) { + showError(`Failed to save configuration: ${error.message}`); + } +} \ No newline at end of file diff --git a/frontend/static/js/workdir_config.js b/frontend/static/js/workdir_config.js index e496ad3..6a8bb6c 100644 --- a/frontend/static/js/workdir_config.js +++ b/frontend/static/js/workdir_config.js @@ -1,4 +1,5 @@ -# frontend/static/js/workdir_config.js +// frontend/static/js/workdir_config.js + async function getWorkDirConfig() { if (!currentProfile?.work_dir) { showError('No work directory selected'); @@ -51,44 +52,43 @@ async function updateGroupConfig(groupId, settings) { } } -function showWorkDirConfig() { +async function showWorkDirConfig() { if (!currentProfile?.work_dir) { showError('No work directory selected'); return; } - getWorkDirConfig().then(config => { - if (config) { - const modal = document.createElement('div'); - modal.className = 'modal active'; - - modal.innerHTML = ` - -{% endblock %} - -{% block extra_js %} - -{% endblock %} \ No newline at end of file + + + + + + + \ No newline at end of file