CSS: add led_count field; calibration dialog improvements; color corrections collapsible section

- Add explicit led_count to PictureColorStripSource (0 = auto from calibration)
- Stream pads with black or truncates to match led_count exactly
- Calibration dialog: show led_count input above visual editor in CSS mode
- Calibration dialog: pre-populate led_count with effective count (cal sum) when stored value is 0
- Calibration dialog: sync preview label live as led_count input changes
- CSS editor: group brightness/saturation/gamma into collapsible "Color Corrections" section

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-02-20 16:42:32 +03:00
parent 7de3546b14
commit a3aeafef13
14 changed files with 173 additions and 47 deletions

View File

@@ -30,6 +30,7 @@ class CalibrationModal extends Modal {
skip_start: this.$('cal-skip-start').value,
skip_end: this.$('cal-skip-end').value,
border_width: this.$('cal-border-width').value,
led_count: this.$('cal-css-led-count').value,
};
}
@@ -112,6 +113,7 @@ export async function showCalibration(deviceId) {
document.getElementById('calibration-device-id').value = device.id;
document.getElementById('cal-device-led-count-inline').textContent = device.led_count;
document.getElementById('cal-css-led-count-group').style.display = 'none';
document.getElementById('cal-start-position').value = calibration.start_position;
document.getElementById('cal-layout').value = calibration.layout;
@@ -216,6 +218,11 @@ export async function showCSSCalibration(cssId) {
const preview = document.querySelector('.calibration-preview');
preview.style.aspectRatio = '';
document.getElementById('cal-device-led-count-inline').textContent = '—';
const ledCountGroup = document.getElementById('cal-css-led-count-group');
ledCountGroup.style.display = '';
const calLeds = (calibration.leds_top || 0) + (calibration.leds_right || 0) +
(calibration.leds_bottom || 0) + (calibration.leds_left || 0);
document.getElementById('cal-css-led-count').value = source.led_count || calLeds || 0;
document.getElementById('cal-start-position').value = calibration.start_position || 'bottom_left';
document.getElementById('cal-layout').value = calibration.layout || 'clockwise';
@@ -286,8 +293,17 @@ export function updateCalibrationPreview() {
parseInt(document.getElementById('cal-left-leds').value || 0);
const totalEl = document.querySelector('.preview-screen-total');
const inCSS = _isCSS();
const deviceCount = inCSS ? null : parseInt(document.getElementById('cal-device-led-count-inline').textContent || 0);
const mismatch = !inCSS && total !== deviceCount;
const declaredCount = inCSS
? parseInt(document.getElementById('cal-css-led-count').value || 0)
: parseInt(document.getElementById('cal-device-led-count-inline').textContent || 0);
if (inCSS) {
document.getElementById('cal-device-led-count-inline').textContent = declaredCount || '—';
}
// In device mode: calibration total must exactly equal device LED count
// In CSS mode: warn only if calibrated LEDs exceed the declared total (padding handles the rest)
const mismatch = inCSS
? (declaredCount > 0 && total > declaredCount)
: (total !== declaredCount);
document.getElementById('cal-total-leds-inline').textContent = (mismatch ? '\u26A0 ' : '') + total;
if (totalEl) totalEl.classList.toggle('mismatch', mismatch);
@@ -831,10 +847,18 @@ export async function saveCalibration() {
const leftLeds = parseInt(document.getElementById('cal-left-leds').value || 0);
const total = topLeds + rightLeds + bottomLeds + leftLeds;
const declaredLedCount = cssMode
? parseInt(document.getElementById('cal-css-led-count').value) || 0
: parseInt(document.getElementById('cal-device-led-count-inline').textContent) || 0;
if (!cssMode) {
const deviceLedCount = parseInt(document.getElementById('cal-device-led-count-inline').textContent);
if (total !== deviceLedCount) {
error.textContent = `Total LEDs (${total}) must equal device LED count (${deviceLedCount})`;
if (total !== declaredLedCount) {
error.textContent = `Total LEDs (${total}) must equal device LED count (${declaredLedCount})`;
error.style.display = 'block';
return;
}
} else {
if (declaredLedCount > 0 && total > declaredLedCount) {
error.textContent = `Calibrated LEDs (${total}) exceed total LED count (${declaredLedCount})`;
error.style.display = 'block';
return;
}
@@ -862,7 +886,7 @@ export async function saveCalibration() {
if (cssMode) {
response = await fetchWithAuth(`/color-strip-sources/${cssId}`, {
method: 'PUT',
body: JSON.stringify({ calibration }),
body: JSON.stringify({ calibration, led_count: declaredLedCount }),
});
} else {
response = await fetchWithAuth(`/devices/${deviceId}/calibration`, {