diff --git a/server/src/wled_controller/static/css/calibration.css b/server/src/wled_controller/static/css/calibration.css index 6a55be1..60805d1 100644 --- a/server/src/wled_controller/static/css/calibration.css +++ b/server/src/wled_controller/static/css/calibration.css @@ -416,6 +416,34 @@ font-size: 14px; } +/* Overlay toggle inside the preview screen */ +.calibration-overlay-toggle { + display: flex; + align-items: center; + gap: 4px; + height: 26px; + padding: 0 10px; + background: rgba(255, 255, 255, 0.15); + border: 1px solid rgba(255, 255, 255, 0.3); + border-radius: 12px; + color: white; + font-family: inherit; + font-size: 12px; + box-sizing: border-box; + cursor: pointer; + transition: background 0.2s, border-color 0.2s; + user-select: none; +} + +.calibration-overlay-toggle:hover { + background: rgba(255, 255, 255, 0.25); +} + +.calibration-overlay-toggle.active { + background: rgba(76, 175, 80, 0.35); + border-color: rgba(76, 175, 80, 0.7); +} + .preview-hint { text-align: center; font-size: 0.8rem; diff --git a/server/src/wled_controller/static/js/features/calibration.js b/server/src/wled_controller/static/js/features/calibration.js index c48ca17..d2880f2 100644 --- a/server/src/wled_controller/static/js/features/calibration.js +++ b/server/src/wled_controller/static/js/features/calibration.js @@ -93,13 +93,7 @@ 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 = ''; - } + btn.classList.toggle('active', active); } async function _checkOverlayStatus(cssId) { @@ -166,7 +160,6 @@ export async function showCalibration(deviceId) { 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; @@ -324,7 +317,6 @@ export async function showCSSCalibration(cssId) { _overlayStartedHere = false; const overlayBtn = document.getElementById('calibration-overlay-btn'); overlayBtn.style.display = ''; - document.getElementById('calibration-tutorial-btn').style.marginLeft = '0'; _setOverlayBtnActive(false); _checkOverlayStatus(cssId); diff --git a/server/src/wled_controller/static/js/features/tutorials.js b/server/src/wled_controller/static/js/features/tutorials.js index 3ebbb38..a43299f 100644 --- a/server/src/wled_controller/static/js/features/tutorials.js +++ b/server/src/wled_controller/static/js/features/tutorials.js @@ -11,6 +11,7 @@ const calibrationTutorialSteps = [ { selector: '.direction-toggle', textKey: 'calibration.tip.direction', position: 'bottom' }, { selector: '.edge-span-bar[data-edge="top"]', textKey: 'calibration.tip.span', position: 'bottom' }, { selector: '.toggle-top', textKey: 'calibration.tip.test', position: 'top' }, + { selector: '#calibration-overlay-btn', textKey: 'calibration.tip.overlay', position: 'bottom' }, { selector: '.preview-screen-total', textKey: 'calibration.tip.toggle_inputs', position: 'top' }, { selector: '.preview-screen-border-width', textKey: 'calibration.tip.border_width', position: 'bottom' }, { selector: '#cal-offset', textKey: 'calibration.tip.offset', position: 'top' }, @@ -55,7 +56,13 @@ export function startCalibrationTutorial() { overlayId: 'tutorial-overlay', mode: 'absolute', container: container, - resolveTarget: (step) => document.querySelector(step.selector) + resolveTarget: (step) => { + const el = document.querySelector(step.selector); + if (!el) return null; + // Skip elements hidden via display:none (e.g. overlay btn in device mode) + if (el.style.display === 'none' || getComputedStyle(el).display === 'none') return null; + return el; + } }); } @@ -102,11 +109,11 @@ export function tutorialNext() { export function tutorialPrev() { if (!activeTutorial) return; if (activeTutorial.step > 0) { - showTutorialStep(activeTutorial.step - 1); + showTutorialStep(activeTutorial.step - 1, -1); } } -function showTutorialStep(index) { +function showTutorialStep(index, direction = 1) { if (!activeTutorial) return; activeTutorial.step = index; const step = activeTutorial.steps[index]; @@ -119,7 +126,13 @@ function showTutorialStep(index) { }); const target = activeTutorial.resolveTarget(step); - if (!target) return; + if (!target) { + // Auto-skip hidden/missing targets in the current direction + const next = index + direction; + if (next >= 0 && next < activeTutorial.steps.length) showTutorialStep(next, direction); + else closeTutorial(); + return; + } target.classList.add('tutorial-target'); if (isFixed) target.style.zIndex = '10001'; diff --git a/server/src/wled_controller/static/locales/en.json b/server/src/wled_controller/static/locales/en.json index cd71fe0..2bd7758 100644 --- a/server/src/wled_controller/static/locales/en.json +++ b/server/src/wled_controller/static/locales/en.json @@ -208,11 +208,13 @@ "calibration.tip.offset": "Set LED offset — distance from LED 0 to the start corner", "calibration.tip.span": "Drag green bars to adjust coverage span", "calibration.tip.test": "Click an edge to toggle test LEDs", + "calibration.tip.overlay": "Toggle screen overlay to see LED positions and numbering on your monitor", "calibration.tip.toggle_inputs": "Click total LED count to toggle edge inputs", "calibration.tip.border_width": "How many pixels from the screen edge to sample for LED colors", "calibration.tip.skip_leds_start": "Skip LEDs at the start of the strip — skipped LEDs stay off", "calibration.tip.skip_leds_end": "Skip LEDs at the end of the strip — skipped LEDs stay off", "calibration.tutorial.start": "Start tutorial", + "calibration.overlay_toggle": "Overlay", "calibration.start_position": "Starting Position:", "calibration.position.bottom_left": "Bottom Left", "calibration.position.bottom_right": "Bottom Right", diff --git a/server/src/wled_controller/static/locales/ru.json b/server/src/wled_controller/static/locales/ru.json index 17428a2..e70ea20 100644 --- a/server/src/wled_controller/static/locales/ru.json +++ b/server/src/wled_controller/static/locales/ru.json @@ -208,11 +208,13 @@ "calibration.tip.offset": "Смещение LED — расстояние от LED 0 до стартового угла", "calibration.tip.span": "Перетащите зелёные полосы для настройки зоны покрытия", "calibration.tip.test": "Нажмите на край для теста LED", + "calibration.tip.overlay": "Включите оверлей для отображения позиций и нумерации LED на мониторе", "calibration.tip.toggle_inputs": "Нажмите на общее количество LED для скрытия боковых полей", "calibration.tip.border_width": "Сколько пикселей от края экрана использовать для цветов LED", "calibration.tip.skip_leds_start": "Пропуск LED в начале ленты — пропущенные LED остаются выключенными", "calibration.tip.skip_leds_end": "Пропуск LED в конце ленты — пропущенные LED остаются выключенными", "calibration.tutorial.start": "Начать обучение", + "calibration.overlay_toggle": "Оверлей", "calibration.start_position": "Начальная Позиция:", "calibration.position.bottom_left": "Нижний Левый", "calibration.position.bottom_right": "Нижний Правый", diff --git a/server/src/wled_controller/templates/modals/calibration.html b/server/src/wled_controller/templates/modals/calibration.html index c8932f6..e64f523 100644 --- a/server/src/wled_controller/templates/modals/calibration.html +++ b/server/src/wled_controller/templates/modals/calibration.html @@ -3,7 +3,6 @@ +