Add LED axis ticks and calibration labels to color strip test preview
- Add horizontal axis with tick marks and LED index labels below strip and composite preview canvases (first/last labels edge-aligned) - Show actual/calibration LED count label on picture-based composite layers (e.g. "25/934") - Display warning icon in orange when LED counts don't match - Send is_picture and calibration_led_count in composite layer_infos Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -132,6 +132,12 @@
|
||||
image-rendering: pixelated;
|
||||
}
|
||||
|
||||
.css-test-strip-axis {
|
||||
display: block;
|
||||
width: 100%;
|
||||
height: 20px;
|
||||
}
|
||||
|
||||
.css-test-fire-btn {
|
||||
position: absolute;
|
||||
right: 6px;
|
||||
@@ -306,6 +312,27 @@
|
||||
}
|
||||
.css-test-layer-brightness svg { width: 12px; height: 12px; }
|
||||
|
||||
.css-test-layer-cal {
|
||||
position: absolute;
|
||||
right: 6px;
|
||||
bottom: 4px;
|
||||
font-size: 0.6rem;
|
||||
font-family: var(--font-mono, monospace);
|
||||
color: #fff;
|
||||
text-shadow: 0 0 3px rgba(0,0,0,0.9), 0 0 6px rgba(0,0,0,0.6);
|
||||
pointer-events: none;
|
||||
white-space: nowrap;
|
||||
opacity: 0.7;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 2px;
|
||||
}
|
||||
.css-test-layer-cal svg { width: 12px; height: 12px; }
|
||||
.css-test-layer-cal-warn {
|
||||
color: var(--warning-color, #ff9800);
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
/* LED count control */
|
||||
.css-test-led-control {
|
||||
display: flex;
|
||||
|
||||
@@ -13,7 +13,7 @@ import {
|
||||
ICON_LED, ICON_PALETTE, ICON_FPS, ICON_MAP_PIN, ICON_MUSIC,
|
||||
ICON_AUDIO_LOOPBACK, ICON_TIMER, ICON_LINK_SOURCE, ICON_FILM,
|
||||
ICON_LINK, ICON_SPARKLES, ICON_ACTIVITY, ICON_CLOCK, ICON_BELL, ICON_EYE,
|
||||
ICON_SUN_DIM,
|
||||
ICON_SUN_DIM, ICON_WARNING,
|
||||
} from '../core/icons.js';
|
||||
import * as P from '../core/icon-paths.js';
|
||||
import { wrapCard } from '../core/card-colors.js';
|
||||
@@ -2024,6 +2024,12 @@ function _cssTestConnect(sourceId, ledCount, fps) {
|
||||
// Build composite layer canvases
|
||||
if (_cssTestIsComposite) {
|
||||
_cssTestBuildLayers(_cssTestMeta.layers, _cssTestMeta.source_type, _cssTestMeta.layer_infos);
|
||||
requestAnimationFrame(() => _cssTestRenderStripAxis('css-test-layers-axis', _cssTestMeta.led_count));
|
||||
}
|
||||
|
||||
// Render strip axis for non-picture, non-composite views
|
||||
if (!isPicture && !_cssTestIsComposite) {
|
||||
requestAnimationFrame(() => _cssTestRenderStripAxis('css-test-strip-axis', _cssTestMeta.led_count));
|
||||
}
|
||||
} else {
|
||||
const raw = new Uint8Array(event.data);
|
||||
@@ -2110,9 +2116,15 @@ function _cssTestBuildLayers(layerNames, sourceType, layerInfos) {
|
||||
const briLabel = hasBri
|
||||
? `<span class="css-test-layer-brightness" data-layer-idx="${i}" style="display:none"></span>`
|
||||
: '';
|
||||
let calLabel = '';
|
||||
if (info && info.is_picture && info.calibration_led_count) {
|
||||
const mismatch = _cssTestMeta.led_count !== info.calibration_led_count;
|
||||
calLabel = `<span class="css-test-layer-cal${mismatch ? ' css-test-layer-cal-warn' : ''}" data-layer-idx="${i}">${mismatch ? ICON_WARNING + ' ' : ''}${_cssTestMeta.led_count}/${info.calibration_led_count}</span>`;
|
||||
}
|
||||
html += `<div class="css-test-layer css-test-strip-wrap">` +
|
||||
`<canvas class="css-test-layer-canvas" data-layer-idx="${i}"></canvas>` +
|
||||
`<span class="css-test-layer-label">${escapeHtml(layerNames[i])}</span>` +
|
||||
calLabel +
|
||||
briLabel +
|
||||
fireBtn +
|
||||
`</div>`;
|
||||
@@ -2376,6 +2388,73 @@ function _cssTestRenderTicks(edges) {
|
||||
}
|
||||
}
|
||||
|
||||
function _cssTestRenderStripAxis(canvasId, ledCount) {
|
||||
const canvas = document.getElementById(canvasId);
|
||||
if (!canvas || ledCount <= 0) return;
|
||||
|
||||
const dpr = window.devicePixelRatio || 1;
|
||||
const w = canvas.clientWidth;
|
||||
const h = canvas.clientHeight;
|
||||
canvas.width = w * dpr;
|
||||
canvas.height = h * dpr;
|
||||
const ctx = canvas.getContext('2d');
|
||||
ctx.scale(dpr, dpr);
|
||||
ctx.clearRect(0, 0, w, h);
|
||||
|
||||
const isDark = document.documentElement.getAttribute('data-theme') !== 'light';
|
||||
const tickStroke = isDark ? 'rgba(255, 255, 255, 0.5)' : 'rgba(0, 0, 0, 0.35)';
|
||||
const tickFill = isDark ? 'rgba(255, 255, 255, 0.75)' : 'rgba(0, 0, 0, 0.65)';
|
||||
ctx.strokeStyle = tickStroke;
|
||||
ctx.fillStyle = tickFill;
|
||||
ctx.lineWidth = 1;
|
||||
ctx.font = '10px -apple-system, BlinkMacSystemFont, sans-serif';
|
||||
ctx.textBaseline = 'top';
|
||||
|
||||
const tickLen = 5;
|
||||
|
||||
// Determine which ticks to label
|
||||
const labelsToShow = new Set([0]);
|
||||
if (ledCount > 1) labelsToShow.add(ledCount - 1);
|
||||
|
||||
if (ledCount > 2) {
|
||||
const maxDigits = String(ledCount - 1).length;
|
||||
const minSpacing = maxDigits * 7 + 8;
|
||||
const niceSteps = [5, 10, 25, 50, 100, 250, 500];
|
||||
let step = niceSteps[niceSteps.length - 1];
|
||||
for (const s of niceSteps) {
|
||||
if (Math.floor(ledCount / s) <= Math.floor(w / minSpacing)) { step = s; break; }
|
||||
}
|
||||
|
||||
const tickPx = i => (i / (ledCount - 1)) * w;
|
||||
const placed = [];
|
||||
labelsToShow.forEach(i => placed.push(tickPx(i)));
|
||||
|
||||
for (let i = 1; i < ledCount - 1; i++) {
|
||||
if (i % step === 0) {
|
||||
const px = tickPx(i);
|
||||
if (!placed.some(p => Math.abs(px - p) < minSpacing)) {
|
||||
labelsToShow.add(i);
|
||||
placed.push(px);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
labelsToShow.forEach(idx => {
|
||||
const fraction = ledCount > 1 ? idx / (ledCount - 1) : 0.5;
|
||||
const tx = fraction * w;
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(tx, 0);
|
||||
ctx.lineTo(tx, tickLen);
|
||||
ctx.stroke();
|
||||
// Align first tick left, last tick right, others center
|
||||
if (idx === 0) ctx.textAlign = 'left';
|
||||
else if (idx === ledCount - 1) ctx.textAlign = 'right';
|
||||
else ctx.textAlign = 'center';
|
||||
ctx.fillText(String(idx), tx, tickLen + 1);
|
||||
});
|
||||
}
|
||||
|
||||
export function fireCssTestNotification() {
|
||||
for (const id of _cssTestNotificationIds) {
|
||||
testNotification(id);
|
||||
|
||||
Reference in New Issue
Block a user