live preview updates to formatting

This commit is contained in:
Chuck
2025-08-10 12:31:12 -05:00
parent d95e6539e3
commit 95e3e4dda4
2 changed files with 161 additions and 18 deletions

View File

@@ -92,7 +92,7 @@
.main-grid { .main-grid {
display: grid; display: grid;
grid-template-columns: 1fr 450px; grid-template-columns: 1fr;
gap: 20px; gap: 20px;
margin-bottom: 20px; margin-bottom: 20px;
} }
@@ -216,6 +216,8 @@
border-radius: var(--border-radius); border-radius: var(--border-radius);
padding: 20px; padding: 20px;
box-shadow: var(--shadow); box-shadow: var(--shadow);
max-width: 1200px;
margin: 0 auto;
} }
.tabs { .tabs {
@@ -699,7 +701,7 @@
<!-- Main Grid --> <!-- Main Grid -->
<div class="main-grid"> <div class="main-grid">
<!-- Display Panel --> <!-- Display Panel -->
<div class="display-panel"> <div class="display-panel" style="max-width: 1200px; margin: 0 auto;">
<h2><i class="fas fa-desktop"></i> Live Display Preview</h2> <h2><i class="fas fa-desktop"></i> Live Display Preview</h2>
<div class="display-preview" id="displayPreview"> <div class="display-preview" id="displayPreview">
<div id="previewStage" class="preview-stage" style="display:none;"> <div id="previewStage" class="preview-stage" style="display:none;">
@@ -740,7 +742,7 @@
</div> </div>
</div> </div>
<!-- Control Panel --> <!-- Control Panel under the preview, wider -->
<div class="control-panel"> <div class="control-panel">
<div class="tabs"> <div class="tabs">
<button class="tab-btn active" onclick="showTab('overview')"> <button class="tab-btn active" onclick="showTab('overview')">
@@ -1085,10 +1087,13 @@
<h3>Sports Configuration</h3> <h3>Sports Configuration</h3>
<p>Configure which sports leagues to display and their settings.</p> <p>Configure which sports leagues to display and their settings.</p>
<!-- Sports configuration will be populated by JavaScript --> <!-- Sports configuration will be populated by JavaScript -->
<div id="sports-config"> <div id="sports-config">
Loading sports configuration... Loading sports configuration...
</div> </div>
<button type="button" class="btn btn-success" onclick="saveSportsConfig()">Save Sports Settings</button> <div style="margin-top:10px; display:flex; gap:10px;">
<button type="button" class="btn btn-primary" onclick="refreshSportsConfig()">Refresh</button>
<button type="button" class="btn btn-success" onclick="saveSportsConfig()">Save Sports Settings</button>
</div>
</div> </div>
</div> </div>
@@ -1950,6 +1955,8 @@
// Load specific data when tabs are opened // Load specific data when tabs are opened
if (tabName === 'news') { if (tabName === 'news') {
loadNewsManagerData(); loadNewsManagerData();
} else if (tabName === 'sports') {
refreshSportsConfig();
} else if (tabName === 'logs') { } else if (tabName === 'logs') {
fetchLogs(); fetchLogs();
} else if (tabName === 'raw-json') { } else if (tabName === 'raw-json') {
@@ -2215,6 +2222,24 @@
} }
} }
async function loadLayout(){
try {
const res = await fetch('/api/editor/layouts');
const data = await res.json();
if (data.status === 'success' && data.data && data.data.elements) {
currentElements = data.data.elements;
selectedElement = null;
updatePreview();
updatePropertiesPanel();
showNotification('Layout loaded', 'success');
} else {
showNotification('No saved layout found', 'warning');
}
} catch (err) {
showNotification('Error loading layout: ' + err, 'error');
}
}
// Utility functions // Utility functions
function showNotification(message, type) { function showNotification(message, type) {
const notification = document.getElementById('notification'); const notification = document.getElementById('notification');
@@ -2843,9 +2868,88 @@
showNotification('News status refreshed', 'success'); showNotification('News status refreshed', 'success');
} }
// Sports configuration placeholder // Sports configuration
function saveSportsConfig() { async function refreshSportsConfig(){
showNotification('Sports configuration saved (placeholder)', 'success'); try {
const res = await fetch('/api/system/status');
const stats = await res.json();
// Build a minimal sports UI off current config
const cfg = currentConfig;
const leagues = [
{ key: 'nfl_scoreboard', label: 'NFL' },
{ key: 'mlb', label: 'MLB' },
{ key: 'milb', label: 'MiLB' },
{ key: 'nhl_scoreboard', label: 'NHL' },
{ key: 'nba_scoreboard', label: 'NBA' },
{ key: 'ncaa_fb_scoreboard', label: 'NCAA FB' },
{ key: 'ncaa_baseball_scoreboard', label: 'NCAA Baseball' },
{ key: 'ncaam_basketball_scoreboard', label: 'NCAAM Basketball' },
{ key: 'soccer_scoreboard', label: 'Soccer' }
];
const container = document.getElementById('sports-config');
const html = leagues.map(l => {
const sec = cfg[l.key] || {};
const fav = (sec.favorite_teams || []).join(', ');
return `
<div style="border:1px solid #ddd; border-radius:6px; padding:12px; margin:10px 0;">
<label style="display:flex; align-items:center; gap:8px;">
<input type="checkbox" data-league="${l.key}" class="sp-enabled" ${sec.enabled ? 'checked' : ''}>
<strong>${l.label}</strong>
</label>
<div class="form-row" style="margin-top:10px;">
<div class="form-group">
<label>Live Priority</label>
<input type="checkbox" data-league="${l.key}" class="sp-live-priority" ${sec.live_priority ? 'checked' : ''}>
</div>
<div class="form-group">
<label>Show Odds</label>
<input type="checkbox" data-league="${l.key}" class="sp-show-odds" ${sec.show_odds ? 'checked' : ''}>
</div>
<div class="form-group">
<label>Favorites Only</label>
<input type="checkbox" data-league="${l.key}" class="sp-favorites-only" ${sec.show_favorite_teams_only ? 'checked' : ''}>
</div>
<div class="form-group">
<label>Favorite Teams</label>
<input type="text" data-league="${l.key}" class="form-control sp-favorites" value="${fav}">
<div class="description">Comma-separated abbreviations</div>
</div>
</div>
</div>
`;
}).join('');
container.innerHTML = html || 'No sports configuration found.';
} catch (err) {
document.getElementById('sports-config').textContent = 'Failed to load sports configuration';
}
}
async function saveSportsConfig(){
try {
const leagues = document.querySelectorAll('.sp-enabled');
const fragment = {};
leagues.forEach(chk => {
const key = chk.getAttribute('data-league');
const enabled = chk.checked;
const livePriority = document.querySelector(`.sp-live-priority[data-league="${key}"]`)?.checked || false;
const showOdds = document.querySelector(`.sp-show-odds[data-league="${key}"]`)?.checked || false;
const favoritesOnly = document.querySelector(`.sp-favorites-only[data-league="${key}"]`)?.checked || false;
const favs = document.querySelector(`.sp-favorites[data-league="${key}"]`)?.value || '';
const favorite_teams = favs.split(',').map(s => s.trim()).filter(Boolean);
fragment[key] = {
enabled,
live_priority: livePriority,
show_odds: showOdds,
show_favorite_teams_only: favoritesOnly,
favorite_teams
};
});
await saveConfigJson(fragment);
} catch (err) {
showNotification('Error saving sports configuration: ' + err, 'error');
return;
}
showNotification('Sports configuration saved', 'success');
} }
</script> </script>
</body> </body>

View File

@@ -201,6 +201,23 @@ def start_display():
logger.info("Using fallback DisplayManager for web simulation") logger.info("Using fallback DisplayManager for web simulation")
display_monitor.start() display_monitor.start()
# Immediately publish a snapshot for the client
try:
img_buffer = io.BytesIO()
display_manager.image.save(img_buffer, format='PNG')
img_str = base64.b64encode(img_buffer.getvalue()).decode()
snapshot = {
'image': img_str,
'width': display_manager.width,
'height': display_manager.height,
'timestamp': time.time()
}
# Update global and notify clients
global current_display_data
current_display_data = snapshot
socketio.emit('display_update', snapshot)
except Exception as snap_err:
logger.error(f"Failed to publish initial snapshot: {snap_err}")
display_running = True display_running = True
@@ -398,10 +415,15 @@ def system_action():
result = subprocess.run(['sudo', 'reboot'], result = subprocess.run(['sudo', 'reboot'],
capture_output=True, text=True) capture_output=True, text=True)
elif action == 'git_pull': elif action == 'git_pull':
home_dir = str(Path.home()) # Run git pull from the repository directory where this file lives
project_dir = os.path.join(home_dir, 'LEDMatrix') repo_dir = Path(__file__).resolve().parent
result = subprocess.run(['git', 'pull'], if not (repo_dir / '.git').exists():
capture_output=True, text=True, cwd=project_dir, check=False) return jsonify({
'status': 'error',
'message': f'Not a git repository: {repo_dir}'
}), 400
result = subprocess.run(['git', 'pull'],
capture_output=True, text=True, cwd=str(repo_dir), check=False)
else: else:
return jsonify({ return jsonify({
'status': 'error', 'status': 'error',
@@ -562,10 +584,14 @@ def run_action_route():
result = subprocess.run(['sudo', 'reboot'], result = subprocess.run(['sudo', 'reboot'],
capture_output=True, text=True) capture_output=True, text=True)
elif action == 'git_pull': elif action == 'git_pull':
home_dir = str(Path.home()) repo_dir = Path(__file__).resolve().parent
project_dir = os.path.join(home_dir, 'LEDMatrix') if not (repo_dir / '.git').exists():
result = subprocess.run(['git', 'pull'], return jsonify({
capture_output=True, text=True, cwd=project_dir, check=True) 'status': 'error',
'message': f'Not a git repository: {repo_dir}'
}), 400
result = subprocess.run(['git', 'pull'],
capture_output=True, text=True, cwd=str(repo_dir), check=False)
else: else:
return jsonify({ return jsonify({
'status': 'error', 'status': 'error',
@@ -847,6 +873,19 @@ def get_current_display():
"""Get current display image as base64.""" """Get current display image as base64."""
return jsonify(current_display_data) return jsonify(current_display_data)
@app.route('/api/editor/layouts', methods=['GET'])
def get_custom_layouts():
"""Return saved custom layouts for the editor if available."""
try:
layouts_path = Path('config') / 'custom_layouts.json'
if not layouts_path.exists():
return jsonify({'status': 'success', 'data': {'elements': []}})
with open(layouts_path, 'r') as f:
data = json.load(f)
return jsonify({'status': 'success', 'data': data})
except Exception as e:
return jsonify({'status': 'error', 'message': str(e)}), 500
@socketio.on('connect') @socketio.on('connect')
def handle_connect(): def handle_connect():
"""Handle client connection.""" """Handle client connection."""