mirror of
https://github.com/ChuckBuilds/LEDMatrix.git
synced 2026-04-10 21:03:01 +00:00
Feature/static image manager (#95)
* feat(static-image): Add static image manager with web interface - Create StaticImageManager class with image scaling and transparency support - Add configuration options for display duration, zoom scale, and background color - Integrate with display controller and web interface - Add image upload functionality to web interface - Support for various image formats with proper scaling - Efficient image processing with aspect ratio preservation - Ready for future scrolling feature implementation * fix(static-image): Move display duration to main display_durations block - Remove display_duration from static_image config section - Update StaticImageManager to read duration from display.display_durations.static_image - Remove display duration field from web interface form - Update web interface JavaScript to not include display_duration in payload - Follows same pattern as all other managers in the project * feat(static-image): Add fit to display option - Add fit_to_display checkbox to automatically scale images to fit display - When enabled, images are scaled to fit display dimensions while preserving aspect ratio - When disabled, manual zoom_scale control is available - Update web interface with smart form controls (zoom scale disabled when fit to display is on) - Prevents stretching or cropping - images are always properly fitted - Default to fit_to_display=true for better user experience * refactor(static-image): Remove zoom_scale and simplify to fit_to_display only - Remove zoom_scale option entirely as it was confusing and redundant - Simplify image processing to always fit to display dimensions - Remove zoom_scale field from web interface - Clean up JavaScript to remove zoom scale logic - Images are now always properly fitted without stretching or cropping - Much simpler and more intuitive user experience
This commit is contained in:
@@ -875,6 +875,9 @@
|
||||
<button class="tab-btn" onclick="showTab('text')">
|
||||
<i class="fas fa-font"></i> Text
|
||||
</button>
|
||||
<button class="tab-btn" onclick="showTab('static_image')">
|
||||
<i class="fas fa-image"></i> Static Image
|
||||
</button>
|
||||
<button class="tab-btn" onclick="showTab('features')">
|
||||
<i class="fas fa-star"></i> Features
|
||||
</button>
|
||||
@@ -1637,6 +1640,37 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Static Image Tab -->
|
||||
<div id="static_image" class="tab-content">
|
||||
<div class="config-section">
|
||||
<div style="display:flex; justify-content: space-between; align-items:center;">
|
||||
<h3>Static Image Display</h3>
|
||||
<div style="display:flex; gap:8px;">
|
||||
<button type="button" class="btn btn-info" onclick="startOnDemand('static_image')"><i class="fas fa-bolt"></i> On-Demand</button>
|
||||
</div>
|
||||
</div>
|
||||
<form id="static_image-form">
|
||||
<div class="form-group"><label><input type="checkbox" id="static_image_enabled" {% if safe_config_get(main_config, 'static_image', 'enabled', default=False) %}checked{% endif %}> Enable</label></div>
|
||||
<div class="form-group">
|
||||
<label for="static_image_path">Image Path</label>
|
||||
<div style="display:flex; gap:8px;">
|
||||
<input type="text" id="static_image_path" class="form-control" value="{{ safe_config_get(main_config, 'static_image', 'image_path', default='assets/static_images/default.png') }}">
|
||||
<button type="button" class="btn btn-secondary" onclick="document.getElementById('image_upload').click()"><i class="fas fa-upload"></i> Upload</button>
|
||||
</div>
|
||||
<input type="file" id="image_upload" accept="image/*" style="display:none" onchange="handleImageUpload(this)">
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<div class="form-group"><label><input type="checkbox" id="static_image_fit_to_display" {% if safe_config_get(main_config, 'static_image', 'fit_to_display', default=True) %}checked{% endif %}> Fit to Display</label></div>
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<div class="form-group"><label><input type="checkbox" id="static_image_preserve_aspect_ratio" {% if safe_config_get(main_config, 'static_image', 'preserve_aspect_ratio', default=True) %}checked{% endif %}> Preserve Aspect Ratio</label></div>
|
||||
<div class="form-group"><label for="static_image_background_color">Background Color</label><input type="color" id="static_image_background_color" class="form-control" data-rgb='{{ safe_config_get(main_config, 'static_image', 'background_color', default=[0, 0, 0]) | tojson }}'></div>
|
||||
</div>
|
||||
<button type="submit" class="btn btn-success">Save Static Image Settings</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Features Tab -->
|
||||
<div id="features" class="tab-content">
|
||||
<div class="config-section">
|
||||
@@ -3114,6 +3148,55 @@
|
||||
});
|
||||
})();
|
||||
|
||||
// Static Image form submit
|
||||
(function augmentStaticImageForm(){
|
||||
const form = document.getElementById('static_image-form');
|
||||
const initColor = (input) => {
|
||||
try { const rgb = JSON.parse(input.dataset.rgb || '[255,255,255]'); input.value = rgbToHex(rgb); } catch {}
|
||||
};
|
||||
initColor(document.getElementById('static_image_background_color'));
|
||||
|
||||
form.addEventListener('submit', async function(e){
|
||||
e.preventDefault();
|
||||
const payload = {
|
||||
static_image: {
|
||||
enabled: document.getElementById('static_image_enabled').checked,
|
||||
image_path: document.getElementById('static_image_path').value,
|
||||
fit_to_display: document.getElementById('static_image_fit_to_display').checked,
|
||||
preserve_aspect_ratio: document.getElementById('static_image_preserve_aspect_ratio').checked,
|
||||
background_color: hexToRgbArray(document.getElementById('static_image_background_color').value)
|
||||
}
|
||||
};
|
||||
await saveConfigJson(payload);
|
||||
});
|
||||
})();
|
||||
|
||||
// Image upload handler
|
||||
function handleImageUpload(input) {
|
||||
const file = input.files[0];
|
||||
if (file) {
|
||||
const formData = new FormData();
|
||||
formData.append('image', file);
|
||||
|
||||
fetch('/upload_image', {
|
||||
method: 'POST',
|
||||
body: formData
|
||||
})
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
if (data.success) {
|
||||
document.getElementById('static_image_path').value = data.path;
|
||||
showNotification('Image uploaded successfully!', 'success');
|
||||
} else {
|
||||
showNotification('Failed to upload image: ' + data.error, 'error');
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
showNotification('Error uploading image: ' + error, 'error');
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// YouTube form submit
|
||||
(function augmentYouTubeForm(){
|
||||
const form = document.getElementById('youtube-form');
|
||||
|
||||
Reference in New Issue
Block a user