Centralize icon resolution into core/icons.js, fix auto-start row alignment

- Create core/icons.js with type-resolution getters and icon constants
- Replace inline emoji literals across 11 feature files with imports
- Remove duplicate icon maps (getEngineIcon, _vsTypeIcons, typeIcons, etc.)
- Fix dashboard auto-start row missing metrics placeholder div

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-02-25 15:28:01 +03:00
parent d05b4b78f4
commit b51839ef3c
11 changed files with 219 additions and 109 deletions

View File

@@ -8,6 +8,11 @@ import { t } from '../core/i18n.js';
import { showToast, formatUptime } from '../core/ui.js';
import { renderPerfSection, initPerfCharts, startPerfPolling, stopPerfPolling } from './perf-charts.js';
import { startAutoRefresh, updateTabBadge } from './tabs.js';
import {
getTargetTypeIcon,
ICON_TARGET, ICON_PROFILE, ICON_CLOCK, ICON_WARNING, ICON_OK,
ICON_STOP, ICON_STOP_PLAIN, ICON_AUTOSTART,
} from '../core/icons.js';
const DASHBOARD_COLLAPSED_KEY = 'dashboard_collapsed';
const MAX_FPS_SAMPLES = 120;
@@ -53,7 +58,7 @@ function _startUptimeTimer() {
if (!el) continue;
const seconds = _getInterpolatedUptime(id);
if (seconds != null) {
el.textContent = `🕐 ${formatUptime(seconds)}`;
el.textContent = `${ICON_CLOCK} ${formatUptime(seconds)}`;
}
}
}, 1000);
@@ -184,7 +189,7 @@ function _updateRunningMetrics(enrichedRunning) {
if (fpsEl) fpsEl.innerHTML = `${fpsActual}<span class="dashboard-fps-target">/${fpsTarget}</span>`;
const errorsEl = cached?.errors || document.querySelector(`[data-errors-text="${target.id}"]`);
if (errorsEl) errorsEl.textContent = `${errors > 0 ? '⚠️' : '✅'} ${errors}`;
if (errorsEl) errorsEl.textContent = `${errors > 0 ? ICON_WARNING : ICON_OK} ${errors}`;
// Update health dot
const isLed = target.target_type === 'led' || target.target_type === 'wled';
@@ -228,7 +233,7 @@ function _updateProfilesInPlace(profiles) {
if (btn) {
btn.className = `btn btn-icon ${p.enabled ? 'btn-warning' : 'btn-success'}`;
btn.setAttribute('onclick', `dashboardToggleProfile('${p.id}', ${!p.enabled})`);
btn.textContent = p.enabled ? '⏹' : '▶';
btn.textContent = p.enabled ? ICON_STOP_PLAIN : '▶';
}
}
}
@@ -369,21 +374,22 @@ export async function loadDashboard(forceFullRender = false) {
const isRunning = !!(target.state && target.state.processing);
const device = devicesMap[target.device_id];
const deviceName = device ? device.name : '';
const typeIcon = target.target_type === 'key_colors' ? '🎨' : '💡';
const typeIcon = getTargetTypeIcon(target.target_type);
const statusBadge = isRunning
? `<span class="dashboard-badge-active">${t('profiles.status.active')}</span>`
: `<span class="dashboard-badge-stopped">${t('profiles.status.inactive')}</span>`;
return `<div class="dashboard-target dashboard-autostart" data-target-id="${target.id}">
<div class="dashboard-target-info">
<span class="dashboard-target-icon"></span>
<span class="dashboard-target-icon">${ICON_AUTOSTART}</span>
<div>
<div class="dashboard-target-name">${escapeHtml(target.name)} ${statusBadge}</div>
${deviceName ? `<div class="dashboard-target-subtitle">${typeIcon} ${escapeHtml(deviceName)}</div>` : `<div class="dashboard-target-subtitle">${typeIcon}</div>`}
</div>
</div>
<div class="dashboard-target-metrics"></div>
<div class="dashboard-target-actions">
<button class="btn btn-icon ${isRunning ? 'btn-warning' : 'btn-success'}" onclick="${isRunning ? `dashboardStopTarget('${target.id}')` : `dashboardStartTarget('${target.id}')`}" title="${isRunning ? t('device.stop') : t('device.start')}">
${isRunning ? '⏹' : '▶'}
${isRunning ? ICON_STOP_PLAIN : '▶'}
</button>
</div>
</div>`;
@@ -412,7 +418,7 @@ export async function loadDashboard(forceFullRender = false) {
if (running.length > 0) {
runningIds = running.map(t => t.id);
const stopAllBtn = `<button class="btn btn-sm btn-danger dashboard-stop-all" onclick="event.stopPropagation(); dashboardStopAll()" title="${t('dashboard.stop_all')}">⏹️ ${t('dashboard.stop_all')}</button>`;
const stopAllBtn = `<button class="btn btn-sm btn-danger dashboard-stop-all" onclick="event.stopPropagation(); dashboardStopAll()" title="${t('dashboard.stop_all')}">${ICON_STOP} ${t('dashboard.stop_all')}</button>`;
const runningItems = running.map(target => renderDashboardTarget(target, true, devicesMap, cssSourceMap)).join('');
targetsInner += `<div class="dashboard-subsection">
@@ -472,7 +478,7 @@ function renderDashboardTarget(target, isRunning, devicesMap = {}, cssSourceMap
const state = target.state || {};
const metrics = target.metrics || {};
const isLed = target.target_type === 'led' || target.target_type === 'wled';
const icon = '⚡';
const icon = ICON_TARGET;
const typeLabel = isLed ? t('dashboard.type.led') : t('dashboard.type.kc');
let subtitleParts = [typeLabel];
@@ -530,14 +536,14 @@ function renderDashboardTarget(target, isRunning, devicesMap = {}, cssSourceMap
</div>
</div>
<div class="dashboard-metric" title="${t('dashboard.uptime')}">
<div class="dashboard-metric-value" data-uptime-text="${target.id}">🕐 ${uptime}</div>
<div class="dashboard-metric-value" data-uptime-text="${target.id}">${ICON_CLOCK} ${uptime}</div>
</div>
<div class="dashboard-metric" title="${t('dashboard.errors')}">
<div class="dashboard-metric-value" data-errors-text="${target.id}">${errors > 0 ? '⚠️' : '✅'} ${errors}</div>
<div class="dashboard-metric-value" data-errors-text="${target.id}">${errors > 0 ? ICON_WARNING : ICON_OK} ${errors}</div>
</div>
</div>
<div class="dashboard-target-actions">
<button class="btn btn-icon btn-warning" onclick="dashboardStopTarget('${target.id}')" title="${t('device.button.stop')}"></button>
<button class="btn btn-icon btn-warning" onclick="dashboardStopTarget('${target.id}')" title="${t('device.button.stop')}">${ICON_STOP_PLAIN}</button>
</div>
</div>`;
} else {
@@ -587,7 +593,7 @@ function renderDashboardProfile(profile) {
return `<div class="dashboard-target dashboard-profile" data-profile-id="${profile.id}">
<div class="dashboard-target-info">
<span class="dashboard-target-icon">📋</span>
<span class="dashboard-target-icon">${ICON_PROFILE}</span>
<div>
<div class="dashboard-target-name">${escapeHtml(profile.name)}</div>
${condSummary ? `<div class="dashboard-target-subtitle">${escapeHtml(condSummary)}</div>` : ''}
@@ -602,7 +608,7 @@ function renderDashboardProfile(profile) {
</div>
<div class="dashboard-target-actions">
<button class="btn btn-icon ${profile.enabled ? 'btn-warning' : 'btn-success'}" onclick="dashboardToggleProfile('${profile.id}', ${!profile.enabled})" title="${profile.enabled ? t('profiles.action.disable') : t('profiles.status.active')}">
${profile.enabled ? '⏹' : '▶'}
${profile.enabled ? ICON_STOP_PLAIN : '▶'}
</button>
</div>
</div>`;