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

@@ -317,7 +317,7 @@ export async function handleAddDevice(event) {
}
if (!name || (!isMockDevice(deviceType) && !url)) {
error.textContent = 'Please fill in all fields';
error.textContent = t('device_discovery.error.fill_all_fields');
error.style.display = 'block';
return;
}
@@ -351,7 +351,7 @@ export async function handleAddDevice(event) {
if (response.ok) {
const result = await response.json();
console.log('Device added successfully:', result);
showToast('Device added successfully', 'success');
showToast(t('device_discovery.added'), 'success');
addDeviceModal.forceClose();
// Use window.* to avoid circular imports
if (typeof window.loadDevices === 'function') await window.loadDevices();
@@ -365,12 +365,12 @@ export async function handleAddDevice(event) {
} else {
const errorData = await response.json();
console.error('Failed to add device:', errorData);
error.textContent = `Failed to add device: ${errorData.detail}`;
error.textContent = t('device_discovery.error.add_failed');
error.style.display = 'block';
}
} catch (err) {
if (err.isAuth) return;
console.error('Failed to add device:', err);
showToast('Failed to add device', 'error');
showToast(t('device_discovery.error.add_failed'), 'error');
}
}