Funcionando

This commit is contained in:
Miguel 2025-02-07 23:08:39 +01:00
parent 3353b45424
commit dbf4d9d685
23 changed files with 855 additions and 173 deletions

View File

@ -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/<group_id>/<script_id>/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/<path:work_dir>', 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/<path:work_dir>/group/<group_id>', 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/<path:work_dir>/group/<group_id>', 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)
app.run(debug=True, port=5000)

0
backend/core/__init__.py Normal file
View File

Binary file not shown.

View File

@ -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)
}

View File

View File

@ -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}")

View File

@ -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)
}

View File

@ -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)
}

26
data/profiles.json Normal file
View File

@ -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"
}
}

BIN
files.txt Normal file

Binary file not shown.

View File

@ -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
.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;
}

View File

@ -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 = '';
}
}
// 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;

View File

@ -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 => `
<option value="${profile.id}" ${profile.id === currentProfile?.id ? 'selected' : ''}>
${profile.name}
</option>
`).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) {
<div class="form-group">
<label for="workDir">Work Directory</label>
<input type="text" id="workDir" name="work_dir"
value="${profile?.work_dir || ''}" required>
value="${profile?.work_dir || ''}" readonly>
</div>
<div class="form-group">
<label for="llmModel">LLM Model</label>
@ -48,7 +109,7 @@ function showProfileEditor(profile = null) {
min="0" max="2" step="0.1">
</div>
<div class="button-group">
<button type="button" onclick="closeProfileEditor()">Cancel</button>
<button type="button" onclick="closeModal(this)">Cancel</button>
<button type="submit">Save</button>
</div>
</form>
@ -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`);

View File

@ -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 = '<p class="no-scripts">No script groups available</p>';
return;
}
container.innerHTML = scriptGroups.map(group => `
<div class="script-group" data-group-id="${group.id}">
<div class="script-group-header">
<h3>${group.name}</h3>
<button onclick="configureGroup('${group.id}')" class="config-btn">
Configure
</button>
</div>
<div class="script-list">
${group.scripts.map(script => `
<div class="script-item">
<div class="script-info">
<h4>${script.name}</h4>
<p>${script.description || 'No description available'}</p>
</div>
<div class="script-actions">
<button onclick="runScript('${group.id}', '${script.id}')" class="run-btn">
Run
</button>
</div>
</div>
`).join('')}
</div>
</div>
`).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 = `
<div class="modal-content">
<h2>${group.name} Configuration</h2>
<form id="groupConfigForm" onsubmit="saveGroupConfig(event, '${groupId}')">
<div class="form-group">
<label for="configData">Configuration (JSON)</label>
<textarea id="configData" name="config" rows="10"
class="config-editor">${JSON.stringify(config || {}, null, 2)}</textarea>
</div>
<div class="button-group">
<button type="button" onclick="closeModal(this)">Cancel</button>
<button type="submit">Save</button>
</div>
</form>
</div>
`;
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}`);
}
}

View File

@ -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 = `
<div class="modal-content">
<h2>Work Directory Configuration</h2>
<div class="config-info">
<p><strong>Directory:</strong> ${currentProfile.work_dir}</p>
<p><strong>Version:</strong> ${config.version}</p>
<p><strong>Created:</strong> ${new Date(config.created_at).toLocaleString()}</p>
<p><strong>Updated:</strong> ${new Date(config.updated_at).toLocaleString()}</p>
</div>
<div class="group-configs">
<h3>Group Configurations</h3>
${Object.entries(config.group_settings || {}).map(([groupId, settings]) => `
<div class="group-config">
<h4>${groupId}</h4>
<pre>${JSON.stringify(settings, null, 2)}</pre>
</div>
`).join('')}
</div>
<div class="button-group">
<button onclick="closeModal(this)">Close</button>
</div>
const config = await getWorkDirConfig();
if (config) {
const modal = document.createElement('div');
modal.className = 'modal active';
modal.innerHTML = `
<div class="modal-content">
<h2>Work Directory Configuration</h2>
<div class="config-info">
<p><strong>Directory:</strong> ${currentProfile.work_dir}</p>
<p><strong>Version:</strong> ${config.version}</p>
<p><strong>Created:</strong> ${new Date(config.created_at).toLocaleString()}</p>
<p><strong>Updated:</strong> ${new Date(config.updated_at).toLocaleString()}</p>
</div>
`;
document.body.appendChild(modal);
}
});
<div class="group-configs">
<h3>Group Configurations</h3>
${Object.entries(config.group_settings || {}).map(([groupId, settings]) => `
<div class="group-config">
<h4>${groupId}</h4>
<pre>${JSON.stringify(settings, null, 2)}</pre>
</div>
`).join('')}
</div>
<div class="button-group">
<button onclick="closeModal(this)">Close</button>
</div>
</div>
`;
document.body.appendChild(modal);
}
}
function closeModal(button) {

View File

@ -1,35 +1,59 @@
# frontend/templates/index.html
{% extends "base.html" %}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Local Scripts Web - Home</title>
<link rel="stylesheet" href="{{ url_for('static', filename='css/style.css') }}">
</head>
<body>
<header>
<nav>
<div class="nav-brand">Local Scripts Web</div>
<div class="profile-selector">
<select id="profileSelect" onchange="changeProfile()">
<option value="">Loading profiles...</option>
</select>
<button onclick="editProfile()">Edit Profile</button>
<button onclick="newProfile()">New Profile</button>
</div>
</nav>
</header>
{% block title %}Local Scripts Web - Home{% endblock %}
<main>
<div class="container">
<div class="work-dir-section">
<h2>Work Directory</h2>
<div class="work-dir-controls">
<input type="text" id="workDirPath" readonly>
<button onclick="selectWorkDir()">Browse</button>
<button onclick="showWorkDirConfig()">Config</button>
</div>
</div>
{% block content %}
<div class="container">
<div class="work-dir-section">
<h2>Work Directory</h2>
<div class="work-dir-controls">
<input type="text" id="workDirPath" readonly>
<button onclick="selectWorkDir()">Browse</button>
<button onclick="editWorkDirConfig()">Config</button>
<div class="scripts-section">
<h2>Available Scripts</h2>
<div id="scriptGroups" class="script-groups">
<!-- Script groups will be loaded here -->
</div>
</div>
<div class="output-section">
<h2>Output</h2>
<div class="output-controls">
<button onclick="clearOutput()">Clear Output</button>
</div>
<div id="outputArea" class="output-area">
<!-- Script output will appear here -->
</div>
</div>
</div>
</div>
</main>
<div class="scripts-section">
<h2>Available Scripts</h2>
<div id="scriptGroups" class="script-groups">
<!-- Script groups will be loaded here -->
</div>
</div>
<div class="output-section">
<h2>Output</h2>
<div id="outputArea" class="output-area">
<!-- Script output will appear here -->
</div>
</div>
</div>
{% endblock %}
{% block extra_js %}
<script src="{{ url_for('static', filename='js/profile.js') }}"></script>
{% endblock %}
<!-- Scripts -->
<script src="{{ url_for('static', filename='js/main.js') }}"></script>
<script src="{{ url_for('static', filename='js/workdir_config.js') }}"></script>
<script src="{{ url_for('static', filename='js/profile.js') }}"></script>
<script src="{{ url_for('static', filename='js/scripts.js') }}"></script>
</body>
</html>