Add overlay toggle to calibration dialog, fix serial reconnect on edge test

Add a 💡 button in the calibration modal header (CSS mode only) that
toggles the LED overlay visualization. Auto-stops overlay on modal close
if started from the dialog. Checks and reflects current overlay status
on modal open.

Fix serial devices creating a new connection on every edge test toggle,
which triggered Arduino bootloader resets. Now reuses the cached idle
client for all device types.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-02-24 17:22:15 +03:00
parent 67a15776b2
commit a6253e8d96
4 changed files with 70 additions and 24 deletions

View File

@@ -120,7 +120,7 @@ import {
showCalibration, closeCalibrationModal, forceCloseCalibrationModal, saveCalibration,
updateOffsetSkipLock, updateCalibrationPreview,
setStartPosition, toggleEdgeInputs, toggleDirection, toggleTestEdge,
showCSSCalibration,
showCSSCalibration, toggleCalibrationOverlay,
} from './features/calibration.js';
// Layer 6: tabs
@@ -345,6 +345,7 @@ Object.assign(window, {
toggleDirection,
toggleTestEdge,
showCSSCalibration,
toggleCalibrationOverlay,
// tabs
switchTab,

View File

@@ -9,6 +9,7 @@ import { API_BASE, getHeaders, fetchWithAuth } from '../core/api.js';
import { showToast } from '../core/ui.js';
import { Modal } from '../core/modal.js';
import { closeTutorial, startCalibrationTutorial } from './tutorials.js';
import { startCSSOverlay, stopCSSOverlay } from './color-strips.js';
/* ── CalibrationModal subclass ────────────────────────────────── */
@@ -37,6 +38,11 @@ class CalibrationModal extends Modal {
onForceClose() {
closeTutorial();
if (_isCSS()) {
const cssId = document.getElementById('calibration-css-id')?.value;
if (_overlayStartedHere && cssId) {
stopCSSOverlay(cssId);
_overlayStartedHere = false;
}
_clearCSSTestMode();
document.getElementById('calibration-css-id').value = '';
const testGroup = document.getElementById('calibration-css-test-group');
@@ -55,6 +61,7 @@ const calibModal = new CalibrationModal();
let _dragRaf = null;
let _previewRaf = null;
let _overlayStartedHere = false;
/* ── Helpers ──────────────────────────────────────────────────── */
@@ -83,6 +90,50 @@ async function _clearCSSTestMode() {
}
}
function _setOverlayBtnActive(active) {
const btn = document.getElementById('calibration-overlay-btn');
if (!btn) return;
if (active) {
btn.style.background = 'var(--primary-color)';
btn.style.color = 'white';
} else {
btn.style.background = '';
btn.style.color = '';
}
}
async function _checkOverlayStatus(cssId) {
try {
const resp = await fetchWithAuth(`/color-strip-sources/${cssId}/overlay/status`);
if (resp.ok) {
const data = await resp.json();
_setOverlayBtnActive(data.active);
}
} catch { /* ignore */ }
}
export async function toggleCalibrationOverlay() {
const cssId = document.getElementById('calibration-css-id')?.value;
if (!cssId) return;
try {
const resp = await fetchWithAuth(`/color-strip-sources/${cssId}/overlay/status`);
if (!resp.ok) return;
const { active } = await resp.json();
if (active) {
await stopCSSOverlay(cssId);
_setOverlayBtnActive(false);
_overlayStartedHere = false;
} else {
await startCSSOverlay(cssId);
_setOverlayBtnActive(true);
_overlayStartedHere = true;
}
} catch (err) {
if (err.isAuth) return;
console.error('Failed to toggle calibration overlay:', err);
}
}
/* ── Public API (exported names unchanged) ────────────────────── */
export async function showCalibration(deviceId) {
@@ -114,6 +165,8 @@ 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('calibration-overlay-btn').style.display = 'none';
document.getElementById('calibration-tutorial-btn').style.marginLeft = '';
document.getElementById('cal-start-position').value = calibration.start_position;
document.getElementById('cal-layout').value = calibration.layout;
@@ -267,6 +320,14 @@ export async function showCSSCalibration(cssId) {
calibModal.snapshot();
calibModal.open();
// Show overlay toggle and check current status
_overlayStartedHere = false;
const overlayBtn = document.getElementById('calibration-overlay-btn');
overlayBtn.style.display = '';
document.getElementById('calibration-tutorial-btn').style.marginLeft = '0';
_setOverlayBtnActive(false);
_checkOverlayStatus(cssId);
initSpanDrag();
requestAnimationFrame(() => renderCalibrationCanvas());