feat: add api_input LED interpolation; fix LED preview, FPS charts, dashboard layout
Lint & Test / test (push) Successful in 1m26s
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:
@@ -3,7 +3,7 @@
|
||||
*/
|
||||
|
||||
import { apiKey, _dashboardLoading, set_dashboardLoading, dashboardPollInterval, setDashboardPollInterval, colorStripSourcesCache, devicesCache, outputTargetsCache } from '../core/state.ts';
|
||||
import { API_BASE, getHeaders, fetchWithAuth, escapeHtml } from '../core/api.ts';
|
||||
import { API_BASE, getHeaders, fetchWithAuth, escapeHtml, fetchMetricsHistory } from '../core/api.ts';
|
||||
import { t } from '../core/i18n.ts';
|
||||
import { showToast, showConfirm, formatUptime, formatCompact, setTabRefreshing } from '../core/ui.ts';
|
||||
import { renderPerfSection, initPerfCharts, startPerfPolling, stopPerfPolling } from './perf-charts.ts';
|
||||
@@ -99,21 +99,16 @@ function _createFpsChart(canvasId: string, actualHistory: number[], currentHisto
|
||||
async function _initFpsCharts(runningTargetIds: string[]): Promise<void> {
|
||||
_destroyFpsCharts();
|
||||
|
||||
// Seed FPS history from server ring buffer on first load
|
||||
if (Object.keys(_fpsHistory).length === 0 && runningTargetIds.length > 0) {
|
||||
try {
|
||||
const resp = await fetch(`${API_BASE}/system/metrics-history`, { headers: getHeaders() });
|
||||
if (resp.ok) {
|
||||
const data = await resp.json();
|
||||
const serverTargets = data.targets || {};
|
||||
for (const id of runningTargetIds) {
|
||||
const samples = serverTargets[id] || [];
|
||||
_fpsHistory[id] = samples.map(s => s.fps).filter(v => v != null);
|
||||
_fpsCurrentHistory[id] = samples.map(s => s.fps_current).filter(v => v != null);
|
||||
}
|
||||
// Seed FPS history from server ring buffer (on first load and tab switches)
|
||||
if (runningTargetIds.length > 0) {
|
||||
const data = await fetchMetricsHistory();
|
||||
if (data) {
|
||||
const serverTargets = data.targets || {};
|
||||
for (const id of runningTargetIds) {
|
||||
const samples = serverTargets[id] || [];
|
||||
_fpsHistory[id] = samples.map(s => s.fps).filter(v => v != null);
|
||||
_fpsCurrentHistory[id] = samples.map(s => s.fps_current).filter(v => v != null);
|
||||
}
|
||||
} catch {
|
||||
// Silently ignore — charts will fill from polling
|
||||
}
|
||||
}
|
||||
|
||||
@@ -162,15 +157,18 @@ function _updateRunningMetrics(enrichedRunning: any[]): void {
|
||||
if (chart) {
|
||||
const actualH = _fpsHistory[target.id] || [];
|
||||
const currentH = _fpsCurrentHistory[target.id] || [];
|
||||
// Mutate in-place to avoid array copies
|
||||
// Left-pad with nulls so all charts span full width
|
||||
const pad0 = MAX_FPS_SAMPLES - actualH.length;
|
||||
const pad1 = MAX_FPS_SAMPLES - currentH.length;
|
||||
const ds0 = chart.data.datasets[0].data;
|
||||
ds0.length = 0;
|
||||
if (pad0 > 0) for (let i = 0; i < pad0; i++) ds0.push(null);
|
||||
ds0.push(...actualH);
|
||||
const ds1 = chart.data.datasets[1].data;
|
||||
ds1.length = 0;
|
||||
if (pad1 > 0) for (let i = 0; i < pad1; i++) ds1.push(null);
|
||||
ds1.push(...currentH);
|
||||
while (chart.data.labels.length < ds0.length) chart.data.labels.push('');
|
||||
chart.data.labels.length = ds0.length;
|
||||
chart.data.labels.length = MAX_FPS_SAMPLES;
|
||||
chart.update('none');
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user