WS now sends color RGB data for color-type streams. Test modal renders a color history swatch strip below the chart, colors the chart line and fill area with the current output color, and shows rgb() in the stats. Works for static_color, animated_color, and adaptive_time_color sources.
This commit is contained in:
@@ -82,6 +82,17 @@
|
||||
border-radius: 6px;
|
||||
}
|
||||
|
||||
.vs-test-color-swatch {
|
||||
margin-top: 8px;
|
||||
}
|
||||
|
||||
.vs-test-color-canvas {
|
||||
display: block;
|
||||
width: 100%;
|
||||
height: 32px;
|
||||
border-radius: 6px;
|
||||
}
|
||||
|
||||
.vs-test-stats {
|
||||
display: flex;
|
||||
gap: 20px;
|
||||
|
||||
@@ -760,6 +760,8 @@ let _testVsMinObserved = Infinity;
|
||||
let _testVsMaxObserved = -Infinity;
|
||||
let _testVsRawLatest: number | null = null;
|
||||
let _testVsRawRange: [number, number] | null = null;
|
||||
let _testVsColorLatest: [number, number, number] | null = null;
|
||||
let _testVsColorHistory: [number, number, number][] = [];
|
||||
|
||||
const testVsModal = new Modal('test-value-source-modal', { backdrop: true, lock: true });
|
||||
|
||||
@@ -777,11 +779,17 @@ export function testValueSource(sourceId: any) {
|
||||
_testVsMaxObserved = -Infinity;
|
||||
_testVsRawLatest = null;
|
||||
_testVsRawRange = null;
|
||||
_testVsColorLatest = null;
|
||||
_testVsColorHistory = [];
|
||||
|
||||
// Hide color swatch until color data arrives
|
||||
const swatchEl = document.getElementById('vs-test-color-swatch');
|
||||
if (swatchEl) swatchEl.style.display = 'none';
|
||||
|
||||
const currentEl = document.getElementById('vs-test-current');
|
||||
const minEl = document.getElementById('vs-test-min');
|
||||
const maxEl = document.getElementById('vs-test-max');
|
||||
if (currentEl) currentEl.textContent = '---';
|
||||
if (currentEl) { currentEl.textContent = '---'; currentEl.style.color = ''; }
|
||||
if (minEl) minEl.textContent = '---';
|
||||
if (maxEl) maxEl.textContent = '---';
|
||||
|
||||
@@ -816,6 +824,13 @@ export function testValueSource(sourceId: any) {
|
||||
_testVsRawLatest = data.raw_value;
|
||||
if (data.raw_range) _testVsRawRange = data.raw_range;
|
||||
}
|
||||
if (data.color) {
|
||||
_testVsColorLatest = data.color;
|
||||
_testVsColorHistory.push(data.color);
|
||||
if (_testVsColorHistory.length > VS_HISTORY_SIZE) {
|
||||
_testVsColorHistory.shift();
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
console.error('Value source test WS parse error:', e);
|
||||
return;
|
||||
@@ -869,6 +884,7 @@ function _sizeVsCanvas(canvas: HTMLCanvasElement) {
|
||||
|
||||
function _renderVsTestLoop() {
|
||||
_renderVsChart();
|
||||
_renderVsColorSwatch();
|
||||
if (testVsModal.isOpen) {
|
||||
_testVsAnimFrame = requestAnimationFrame(_renderVsTestLoop);
|
||||
}
|
||||
@@ -923,7 +939,9 @@ function _renderVsChart() {
|
||||
}
|
||||
ctx.lineTo((startOffset + history.length - 1) * stepX, h);
|
||||
ctx.closePath();
|
||||
ctx.fillStyle = 'rgba(76, 175, 80, 0.15)';
|
||||
ctx.fillStyle = _testVsColorLatest
|
||||
? `rgba(${_testVsColorLatest[0]},${_testVsColorLatest[1]},${_testVsColorLatest[2]},0.15)`
|
||||
: 'rgba(76, 175, 80, 0.15)';
|
||||
ctx.fill();
|
||||
|
||||
// Draw the line
|
||||
@@ -934,7 +952,10 @@ function _renderVsChart() {
|
||||
if (i === 0) ctx.moveTo(x, y);
|
||||
else ctx.lineTo(x, y);
|
||||
}
|
||||
ctx.strokeStyle = '#4caf50';
|
||||
const lineColor = _testVsColorLatest
|
||||
? `rgb(${_testVsColorLatest[0]},${_testVsColorLatest[1]},${_testVsColorLatest[2]})`
|
||||
: '#4caf50';
|
||||
ctx.strokeStyle = lineColor;
|
||||
ctx.lineWidth = 2;
|
||||
ctx.stroke();
|
||||
|
||||
@@ -993,6 +1014,51 @@ function _fmtRaw(v: number): string {
|
||||
return Number.isInteger(v) ? String(v) : v.toFixed(1);
|
||||
}
|
||||
|
||||
function _renderVsColorSwatch() {
|
||||
const swatchEl = document.getElementById('vs-test-color-swatch');
|
||||
const canvas = document.getElementById('vs-test-color-canvas') as HTMLCanvasElement | null;
|
||||
if (!canvas || !swatchEl) return;
|
||||
|
||||
if (_testVsColorHistory.length === 0) {
|
||||
swatchEl.style.display = 'none';
|
||||
return;
|
||||
}
|
||||
|
||||
swatchEl.style.display = '';
|
||||
|
||||
const dpr = window.devicePixelRatio || 1;
|
||||
const rect = canvas.parentElement!.getBoundingClientRect();
|
||||
const w = rect.width;
|
||||
const h = 32;
|
||||
canvas.width = w * dpr;
|
||||
canvas.height = h * dpr;
|
||||
canvas.style.height = h + 'px';
|
||||
|
||||
const ctx = canvas.getContext('2d')!;
|
||||
ctx.setTransform(dpr, 0, 0, dpr, 0, 0);
|
||||
|
||||
const colors = _testVsColorHistory;
|
||||
const stepX = w / VS_HISTORY_SIZE;
|
||||
const barW = Math.max(stepX, 1);
|
||||
const startOffset = VS_HISTORY_SIZE - colors.length;
|
||||
|
||||
for (let i = 0; i < colors.length; i++) {
|
||||
const [r, g, b] = colors[i];
|
||||
ctx.fillStyle = `rgb(${r},${g},${b})`;
|
||||
ctx.fillRect((startOffset + i) * stepX, 0, barW + 0.5, h);
|
||||
}
|
||||
|
||||
// Update current color stat display
|
||||
if (_testVsColorLatest) {
|
||||
const [r, g, b] = _testVsColorLatest;
|
||||
const curEl = document.getElementById('vs-test-current');
|
||||
if (curEl) {
|
||||
curEl.textContent = `rgb(${r}, ${g}, ${b})`;
|
||||
curEl.style.color = `rgb(${r},${g},${b})`;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ── Card rendering (used by streams.js) ───────────────────────
|
||||
|
||||
export function createValueSourceCard(src: ValueSource) {
|
||||
|
||||
Reference in New Issue
Block a user