Files
ledgrab/server/src/wled_controller/templates/modals/calibration.html
T
alexei.dolgolyov 384362ccf1
Lint & Test / test (push) Successful in 1m27s
feat: new value source types (HA entity, gradient map, strip extract) + UI fixes
New value source types:
- ha_entity: reads numeric values from HA entity state/attribute, normalizes
  via min/max range, applies EMA smoothing. EntitySelect for HA connection
  and entity selection with live entity list fetching.
- gradient_map: maps a float value source (0-1) through a gradient entity.
  EntitySelect for both input source and gradient with inline previews.
- css_extract: extracts single color by averaging LED range from a color
  strip source. EntitySelect for source selection.

Value source type picker:
- Filter tabs (All / Numeric / Color) above the icon grid
- showTypePicker extended with filterTabs + onFilterChange support

Palette selectors converted to EntitySelect:
- Effect palette, gradient preset, and audio palette selectors now use
  command-palette style EntitySelect with gradient strip previews

Tab indicator fixes:
- Icon now updates on tab switch (was passing no args to updateTabIndicator)
- Visible with any background effect active, not just Noise Field
- Noise Field is the default background effect for new users

Dashboard section collapse fix:
- Split header into clickable toggle (chevron+label) and non-clickable
  actions area — buttons no longer trigger collapse/expand

Discriminated union fix (422 errors):
- source_type/target_type now always included in update payloads for:
  CSS editor, LED target, HA light target, simple calibration,
  advanced calibration
2026-03-29 20:38:22 +03:00

169 lines
13 KiB
HTML

<!-- Calibration Modal -->
<div id="calibration-modal" class="modal" role="dialog" aria-modal="true" aria-labelledby="calibration-modal-title">
<div class="modal-content" style="max-width: 700px;">
<div class="modal-header">
<h2 id="calibration-modal-title"><svg class="icon" viewBox="0 0 24 24"><path d="M21.3 15.3a2.4 2.4 0 0 1 0 3.4l-2.6 2.6a2.4 2.4 0 0 1-3.4 0L2.7 8.7a2.41 2.41 0 0 1 0-3.4l2.6-2.6a2.41 2.41 0 0 1 3.4 0Z"/><path d="m14.5 12.5 2-2"/><path d="m11.5 9.5 2-2"/><path d="m8.5 6.5 2-2"/><path d="m17.5 15.5 2-2"/></svg> <span data-i18n="calibration.title">LED Calibration</span></h2>
<button id="calibration-tutorial-btn" class="tutorial-trigger-btn" onclick="startCalibrationTutorial()" data-i18n-title="calibration.tutorial.start" title="Start tutorial">?</button>
<button class="modal-close-btn" onclick="closeCalibrationModal()" title="Close" data-i18n-aria-label="aria.close">&#x2715;</button>
</div>
<div class="modal-body">
<input type="hidden" id="calibration-device-id">
<input type="hidden" id="calibration-css-id">
<input type="hidden" id="calibration-css-source-type">
<!-- Device picker shown in CSS calibration mode for edge testing -->
<div id="calibration-css-test-group" class="form-group" style="display:none; margin-bottom: 12px; padding: 0 4px;">
<div class="label-row">
<label for="calibration-test-device" data-i18n="color_strip.test_device">Test on Device:</label>
<button type="button" class="hint-toggle" onclick="toggleHint(this)" title="?" data-i18n-aria-label="aria.hint">?</button>
</div>
<small class="input-hint" style="display:none" data-i18n="color_strip.test_device.hint">Select a device to send test pixels to when clicking edge toggles</small>
<select id="calibration-test-device"></select>
</div>
<!-- LED count input (CSS calibration mode only) -->
<div id="cal-css-led-count-group" class="form-group" style="display:none; margin-bottom: 12px; padding: 0 4px;">
<div class="label-row">
<label for="cal-css-led-count" data-i18n="color_strip.led_count">LED Count:</label>
<button type="button" class="hint-toggle" onclick="toggleHint(this)" title="?" data-i18n-aria-label="aria.hint">?</button>
</div>
<small class="input-hint" style="display:none" data-i18n="color_strip.led_count.hint">Total number of LEDs on the physical strip. Set to 0 to use the sum from calibration. If your strip has LEDs behind the TV that are not mapped to screen edges, set the exact count here and they will be filled with black.</small>
<input type="number" id="cal-css-led-count" min="0" max="1500" step="1" value="0" oninput="updateCalibrationPreview()">
</div>
<!-- Interactive Preview with integrated LED inputs and test toggles -->
<div style="margin-bottom: 12px; padding: 0 24px;">
<div class="calibration-preview">
<!-- Screen with direction toggle, total LEDs, and offset -->
<div class="preview-screen">
<button type="button" class="direction-toggle" onclick="toggleDirection()" title="Toggle direction">
<span id="direction-icon"><svg class="icon" viewBox="0 0 24 24"><path d="M21 12a9 9 0 1 1-9-9c2.52 0 4.93 1 6.74 2.74L21 8"/><path d="M21 3v5h-5"/></svg></span> <span id="direction-label">CW</span>
</button>
<div class="preview-screen-total" onclick="toggleEdgeInputs()" title="Toggle edge LED inputs"><span id="cal-total-leds-inline">0</span> / <span id="cal-device-led-count-inline">0</span></div>
<div class="preview-screen-border-width">
<label for="cal-border-width" data-i18n="calibration.border_width">Border (px):</label>
<input type="number" id="cal-border-width" min="1" max="100" value="10">
</div>
<button id="calibration-overlay-btn" class="calibration-overlay-toggle" onclick="toggleCalibrationOverlay()" data-i18n-title="overlay.button.show" title="Show overlay visualization" style="display:none">
<svg class="icon" viewBox="0 0 24 24"><path d="M15 14c.2-1 .7-1.7 1.5-2.5 1-.9 1.5-2.2 1.5-3.5A6 6 0 0 0 6 8c0 1 .2 2.2 1.5 3.5.7.7 1.3 1.5 1.5 2.5"/><path d="M9 18h6"/><path d="M10 22h4"/></svg> <span data-i18n="calibration.overlay_toggle">Overlay</span>
</button>
</div>
<!-- Edge bars with span controls and LED count inputs -->
<div class="preview-edge edge-top">
<div class="edge-span-bar" data-edge="top">
<div class="edge-span-handle edge-span-handle-start" data-edge="top" data-handle="start"></div>
<div class="edge-span-handle edge-span-handle-end" data-edge="top" data-handle="end"></div>
</div>
<input type="number" id="cal-top-leds" class="edge-led-input" min="0" value="0"
oninput="updateCalibrationPreview()">
</div>
<div class="preview-edge edge-right">
<div class="edge-span-bar" data-edge="right">
<div class="edge-span-handle edge-span-handle-start" data-edge="right" data-handle="start"></div>
<div class="edge-span-handle edge-span-handle-end" data-edge="right" data-handle="end"></div>
</div>
<input type="number" id="cal-right-leds" class="edge-led-input" min="0" value="0"
oninput="updateCalibrationPreview()">
</div>
<div class="preview-edge edge-bottom">
<div class="edge-span-bar" data-edge="bottom">
<div class="edge-span-handle edge-span-handle-start" data-edge="bottom" data-handle="start"></div>
<div class="edge-span-handle edge-span-handle-end" data-edge="bottom" data-handle="end"></div>
</div>
<input type="number" id="cal-bottom-leds" class="edge-led-input" min="0" value="0"
oninput="updateCalibrationPreview()">
</div>
<div class="preview-edge edge-left">
<div class="edge-span-bar" data-edge="left">
<div class="edge-span-handle edge-span-handle-start" data-edge="left" data-handle="start"></div>
<div class="edge-span-handle edge-span-handle-end" data-edge="left" data-handle="end"></div>
</div>
<input type="number" id="cal-left-leds" class="edge-led-input" min="0" value="0"
oninput="updateCalibrationPreview()">
</div>
<!-- Edge test toggle zones (outside container border, in tick area) -->
<button type="button" class="edge-toggle toggle-top" onclick="toggleTestEdge('top')"></button>
<button type="button" class="edge-toggle toggle-right" onclick="toggleTestEdge('right')"></button>
<button type="button" class="edge-toggle toggle-bottom" onclick="toggleTestEdge('bottom')"></button>
<button type="button" class="edge-toggle toggle-left" onclick="toggleTestEdge('left')"></button>
<!-- Corner start position buttons -->
<button type="button" class="preview-corner corner-top-left" onclick="setStartPosition('top_left')"></button>
<button type="button" class="preview-corner corner-top-right" onclick="setStartPosition('top_right')"></button>
<button type="button" class="preview-corner corner-bottom-left" onclick="setStartPosition('bottom_left')"></button>
<button type="button" class="preview-corner corner-bottom-right" onclick="setStartPosition('bottom_right')"></button>
<!-- Canvas overlay for ticks, arrows, start label -->
<canvas id="calibration-preview-canvas"></canvas>
</div>
</div>
<!-- Hidden selects (used by saveCalibration) -->
<div style="display: none;">
<select id="cal-start-position">
<option value="bottom_left">Bottom Left</option>
<option value="bottom_right">Bottom Right</option>
<option value="top_left">Top Left</option>
<option value="top_right">Top Right</option>
</select>
<select id="cal-layout">
<option value="clockwise">Clockwise</option>
<option value="counterclockwise">Counterclockwise</option>
</select>
</div>
<!-- Offset & Skip LEDs -->
<div style="display: grid; grid-template-columns: 1fr 1fr 1fr; gap: 16px; padding: 0 24px;">
<div class="form-group">
<div class="label-row">
<label for="cal-offset" data-i18n="calibration.offset">LED Offset:</label>
<button type="button" class="hint-toggle" onclick="toggleHint(this)" title="?">?</button>
</div>
<small class="input-hint" style="display:none" data-i18n="calibration.offset.hint">Distance from physical LED 0 to the start corner (along strip direction)</small>
<input type="number" id="cal-offset" min="0" value="0" oninput="updateOffsetSkipLock(); updateCalibrationPreview()">
</div>
<div class="form-group">
<div class="label-row">
<label for="cal-skip-start" data-i18n="calibration.skip_start">Skip LEDs (Start):</label>
<button type="button" class="hint-toggle" onclick="toggleHint(this)" title="?">?</button>
</div>
<small class="input-hint" style="display:none" data-i18n="calibration.skip_start.hint">Number of LEDs to turn off at the beginning of the strip (0 = none)</small>
<input type="number" id="cal-skip-start" min="0" value="0" oninput="updateOffsetSkipLock(); updateCalibrationPreview()">
</div>
<div class="form-group">
<div class="label-row">
<label for="cal-skip-end" data-i18n="calibration.skip_end">Skip LEDs (End):</label>
<button type="button" class="hint-toggle" onclick="toggleHint(this)" title="?">?</button>
</div>
<small class="input-hint" style="display:none" data-i18n="calibration.skip_end.hint">Number of LEDs to turn off at the end of the strip (0 = none)</small>
<input type="number" id="cal-skip-end" min="0" value="0" oninput="updateOffsetSkipLock(); updateCalibrationPreview()">
</div>
</div>
<!-- Tutorial Overlay -->
<div id="calibration-tutorial-overlay" class="tutorial-overlay">
<div class="tutorial-backdrop"></div>
<div class="tutorial-ring"></div>
<div class="tutorial-tooltip">
<div class="tutorial-tooltip-header">
<span class="tutorial-step-counter"></span>
<button class="tutorial-close-btn" onclick="closeTutorial()">&times;</button>
</div>
<p class="tutorial-tooltip-text"></p>
<div class="tutorial-tooltip-nav">
<button class="tutorial-prev-btn" onclick="tutorialPrev()">&#8592;</button>
<button class="tutorial-next-btn" onclick="tutorialNext()">&#8594;</button>
</div>
</div>
</div>
<div id="calibration-error" class="error-message" style="display: none;"></div>
</div>
<div class="modal-footer">
<button class="btn btn-icon btn-secondary" onclick="closeCalibrationModal()" title="Cancel" data-i18n-aria-label="aria.cancel">&#x2715;</button>
<button class="btn btn-icon btn-primary" onclick="saveCalibration()" title="Save" data-i18n-aria-label="aria.save">&#x2713;</button>
</div>
</div>
</div>