Codebase review: stability, performance, usability, and i18n fixes

Stability:
- Fix race condition: set _is_running before create_task in target processors
- Await probe task after cancel in wled_target_processor
- Replace raw fetch() with fetchWithAuth() across devices, kc-targets, pattern-templates
- Add try/catch to showTestTemplateModal in streams.js
- Wrap blocking I/O in asyncio.to_thread (picture_targets, system restore)
- Fix dashboardStopAll to filter only running targets with ok guard

Performance:
- Vectorize fire effect spark loop with numpy in effect_stream
- Vectorize FFT band binning with cumulative sum in analysis.py
- Rewrite pixel_processor with vectorized numpy (accept ndarray or list)
- Add httpx.AsyncClient connection pooling with lock in wled_provider
- Optimize _send_pixels_http to avoid np.hstack allocation in wled_client
- Mutate chart arrays in-place in dashboard, perf-charts, targets
- Merge dashboard 2-batch fetch into single Promise.all
- Hoist frame_time outside loop in mapped_stream

Usability:
- Fix health check interval load/save in device settings
- Swap confirm modal button classes (No=secondary, Yes=danger)
- Add aria-modal to audio/value source editors, fix close button aria-labels
- Add modal footer close button to settings modal
- Add dedicated calibration LED count validation error keys

i18n:
- Replace ~50 hardcoded English strings with t() calls across 12 JS files
- Add 50 new keys to en.json, ru.json, zh.json
- Localize inline toasts in index.html with window.t fallback
- Add data-i18n to command palette footer
- Add localization policy to CLAUDE.md

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-02-28 12:12:37 +03:00
parent c95c6e9a44
commit bd8d7a019f
31 changed files with 460 additions and 233 deletions

View File

@@ -170,7 +170,7 @@
<div class="cp-dialog">
<input id="cp-input" class="cp-input" placeholder="Search..." autocomplete="off">
<div id="cp-results" class="cp-results"></div>
<div class="cp-footer">↑↓ navigate · Enter select · Esc close</div>
<div class="cp-footer" data-i18n="search.footer">↑↓ navigate · Enter select · Esc close</div>
</div>
</div>
@@ -198,7 +198,7 @@
// Re-derive accent text variant for the new theme
const accent = localStorage.getItem('accentColor');
if (accent) applyAccentColor(accent, true);
showToast(`Switched to ${newTheme} theme`, 'info');
showToast(window.t ? t(newTheme === 'dark' ? 'theme.switched.dark' : 'theme.switched.light') : `Switched to ${newTheme} theme`, 'info');
}
// Initialize accent color
@@ -247,7 +247,7 @@
if (native) native.value = hex;
localStorage.setItem('accentColor', hex);
document.dispatchEvent(new CustomEvent('accentColorChanged', { detail: { color: hex } }));
if (!silent) showToast('Accent color updated', 'info');
if (!silent) showToast(window.t ? t('accent.color.updated') : 'Accent color updated', 'info');
}
// Bootstrap _cpToggle/_cpPick globals before the color-picker module loads

View File

@@ -1,9 +1,9 @@
<!-- Audio Source Editor Modal -->
<div id="audio-source-modal" class="modal" role="dialog" aria-labelledby="audio-source-modal-title">
<div id="audio-source-modal" class="modal" role="dialog" aria-modal="true" aria-labelledby="audio-source-modal-title">
<div class="modal-content">
<div class="modal-header">
<h2 id="audio-source-modal-title" data-i18n="audio_source.add">Add Audio Source</h2>
<button class="modal-close-btn" onclick="closeAudioSourceModal()" aria-label="Close">&times;</button>
<button class="modal-close-btn" onclick="closeAudioSourceModal()" data-i18n-aria-label="aria.close">&#x2715;</button>
</div>
<div class="modal-body">
<form id="audio-source-form" onsubmit="return false;">

View File

@@ -9,8 +9,8 @@
<p id="confirm-message" class="modal-description"></p>
</div>
<div class="modal-footer">
<button class="btn btn-danger" id="confirm-no-btn" onclick="closeConfirmModal(false)">No</button>
<button class="btn btn-secondary" id="confirm-yes-btn" onclick="closeConfirmModal(true)">Yes</button>
<button class="btn btn-secondary" id="confirm-no-btn" onclick="closeConfirmModal(false)">No</button>
<button class="btn btn-danger" id="confirm-yes-btn" onclick="closeConfirmModal(true)">Yes</button>
</div>
</div>
</div>

View File

@@ -75,5 +75,8 @@
<div id="settings-error" class="error-message" style="display:none;"></div>
</div>
<div class="modal-footer">
<button class="btn btn-icon btn-secondary" onclick="closeSettingsModal()" title="Close" data-i18n-title="settings.button.cancel" data-i18n-aria-label="aria.close">&#x2715;</button>
</div>
</div>
</div>

View File

@@ -1,9 +1,9 @@
<!-- Value Source Editor Modal -->
<div id="value-source-modal" class="modal" role="dialog" aria-labelledby="value-source-modal-title">
<div id="value-source-modal" class="modal" role="dialog" aria-modal="true" aria-labelledby="value-source-modal-title">
<div class="modal-content">
<div class="modal-header">
<h2 id="value-source-modal-title" data-i18n="value_source.add">Add Value Source</h2>
<button class="modal-close-btn" onclick="closeValueSourceModal()" aria-label="Close">&times;</button>
<button class="modal-close-btn" onclick="closeValueSourceModal()" data-i18n-aria-label="aria.close">&#x2715;</button>
</div>
<div class="modal-body">
<form id="value-source-form" onsubmit="return false;">