feat: add api_input LED interpolation; fix LED preview, FPS charts, dashboard layout
Lint & Test / test (push) Successful in 1m26s

API Input:
- Add interpolation mode (linear/nearest/none) for LED count mismatch
  between incoming data and device LED count
- New IconSelect in editor, i18n for en/ru/zh
- Mark crossfade as won't-do (client owns temporal transitions)
- Mark last-write-wins as already implemented

LED Preview:
- Fix zone-mode preview parsing composite wire format (0xFE header
  bytes were rendered as color data, garbling multi-zone previews)
- Fix _restoreLedPreviewState to handle zone-mode panels

FPS Charts:
- Seed target card charts from server metrics-history on first load
- Add fetchMetricsHistory() with 5s TTL cache shared across
  dashboard, targets, perf-charts, and graph-editor
- Fix chart padding: pass maxSamples per caller (120 for dashboard,
  30 for target cards) instead of hardcoded 120
- Fix dashboard chart empty on tab switch (always fetch server history)
- Left-pad with nulls for consistent chart width across targets

Dashboard:
- Fix metrics row alignment (grid layout with fixed column widths)
- Fix FPS label overflow into uptime column
This commit is contained in:
2026-03-26 02:06:49 +03:00
parent be4c98b543
commit 3e0bf8538c
17 changed files with 248 additions and 67 deletions
@@ -13,7 +13,7 @@ import {
outputTargetsCache, patternTemplatesCache, scenePresetsCache,
automationsCacheObj, csptCache,
} from '../core/state.ts';
import { fetchWithAuth } from '../core/api.ts';
import { fetchWithAuth, fetchMetricsHistory } from '../core/api.ts';
import { showToast, showConfirm, formatUptime, formatCompact } from '../core/ui.ts';
import { createFpsSparkline } from '../core/chart-utils.ts';
import { t } from '../core/i18n.ts';
@@ -2436,10 +2436,9 @@ async function _showNodeTooltip(nodeId: string, nodeEl: Element, container: HTML
// Seed from server-side metrics history (non-blocking)
try {
const histResp = await fetchWithAuth('/system/metrics-history');
const hist = await fetchMetricsHistory();
if (_hoverNodeId !== nodeId) return; // user moved away during fetch
if (histResp.ok) {
const hist = await histResp.json();
if (hist) {
const samples = hist.targets?.[nodeId] || [];
for (const s of samples) {
if (s.fps != null) _hoverFpsHistory.push(s.fps);