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:
@@ -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`, {
|
||||
|
||||
@@ -22,6 +22,7 @@ class CSSEditorModal extends Modal {
|
||||
brightness: document.getElementById('css-editor-brightness').value,
|
||||
saturation: document.getElementById('css-editor-saturation').value,
|
||||
gamma: document.getElementById('css-editor-gamma').value,
|
||||
led_count: document.getElementById('css-editor-led-count').value,
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -35,7 +36,8 @@ export function createColorStripCard(source, pictureSourceMap) {
|
||||
? pictureSourceMap[source.picture_source_id].name
|
||||
: source.picture_source_id || '—';
|
||||
const cal = source.calibration || {};
|
||||
const ledCount = (cal.leds_top || 0) + (cal.leds_right || 0) + (cal.leds_bottom || 0) + (cal.leds_left || 0);
|
||||
const calLeds = (cal.leds_top || 0) + (cal.leds_right || 0) + (cal.leds_bottom || 0) + (cal.leds_left || 0);
|
||||
const ledCount = (source.led_count > 0) ? source.led_count : calLeds;
|
||||
|
||||
return `
|
||||
<div class="card" data-css-id="${source.id}">
|
||||
@@ -107,6 +109,8 @@ export async function showCSSEditor(cssId = null) {
|
||||
document.getElementById('css-editor-gamma').value = gamma;
|
||||
document.getElementById('css-editor-gamma-value').textContent = parseFloat(gamma).toFixed(2);
|
||||
|
||||
document.getElementById('css-editor-led-count').value = css.led_count ?? 0;
|
||||
|
||||
document.getElementById('css-editor-title').textContent = t('color_strip.edit');
|
||||
} else {
|
||||
document.getElementById('css-editor-id').value = '';
|
||||
@@ -122,6 +126,7 @@ export async function showCSSEditor(cssId = null) {
|
||||
document.getElementById('css-editor-saturation-value').textContent = '1.00';
|
||||
document.getElementById('css-editor-gamma').value = 1.0;
|
||||
document.getElementById('css-editor-gamma-value').textContent = '1.00';
|
||||
document.getElementById('css-editor-led-count').value = 0;
|
||||
document.getElementById('css-editor-title').textContent = t('color_strip.add');
|
||||
}
|
||||
|
||||
@@ -160,6 +165,7 @@ export async function saveCSSEditor() {
|
||||
brightness: parseFloat(document.getElementById('css-editor-brightness').value),
|
||||
saturation: parseFloat(document.getElementById('css-editor-saturation').value),
|
||||
gamma: parseFloat(document.getElementById('css-editor-gamma').value),
|
||||
led_count: parseInt(document.getElementById('css-editor-led-count').value) || 0,
|
||||
};
|
||||
|
||||
try {
|
||||
|
||||
@@ -100,9 +100,7 @@ export function createDeviceCard(device) {
|
||||
<button class="btn btn-icon btn-secondary" onclick="showSettings('${device.id}')" title="${t('device.button.settings')}">
|
||||
⚙️
|
||||
</button>
|
||||
<button class="btn btn-icon btn-secondary" onclick="showCalibration('${device.id}')" title="${t('device.button.calibrate')}">
|
||||
📐
|
||||
</button>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
Reference in New Issue
Block a user