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:
@@ -6,6 +6,7 @@ import {
|
||||
calibrationTestState, EDGE_TEST_COLORS,
|
||||
} from '../core/state.js';
|
||||
import { API_BASE, getHeaders, fetchWithAuth } from '../core/api.js';
|
||||
import { t } from '../core/i18n.js';
|
||||
import { showToast } from '../core/ui.js';
|
||||
import { Modal } from '../core/modal.js';
|
||||
import { closeTutorial, startCalibrationTutorial } from './tutorials.js';
|
||||
@@ -138,7 +139,7 @@ export async function showCalibration(deviceId) {
|
||||
fetchWithAuth('/config/displays'),
|
||||
]);
|
||||
|
||||
if (!response.ok) { showToast('Failed to load calibration', 'error'); return; }
|
||||
if (!response.ok) { showToast(t('calibration.error.load_failed'), 'error'); return; }
|
||||
|
||||
const device = await response.json();
|
||||
const calibration = device.calibration;
|
||||
@@ -215,7 +216,7 @@ export async function showCalibration(deviceId) {
|
||||
} catch (error) {
|
||||
if (error.isAuth) return;
|
||||
console.error('Failed to load calibration:', error);
|
||||
showToast('Failed to load calibration', 'error');
|
||||
showToast(t('calibration.error.load_failed'), 'error');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -240,7 +241,7 @@ export async function showCSSCalibration(cssId) {
|
||||
fetchWithAuth('/devices'),
|
||||
]);
|
||||
|
||||
if (!cssResp.ok) { showToast('Failed to load color strip source', 'error'); return; }
|
||||
if (!cssResp.ok) { showToast(t('calibration.error.css_load_failed'), 'error'); return; }
|
||||
const source = await cssResp.json();
|
||||
const calibration = source.calibration || {};
|
||||
|
||||
@@ -339,7 +340,7 @@ export async function showCSSCalibration(cssId) {
|
||||
} catch (error) {
|
||||
if (error.isAuth) return;
|
||||
console.error('Failed to load CSS calibration:', error);
|
||||
showToast('Failed to load calibration', 'error');
|
||||
showToast(t('calibration.error.load_failed'), 'error');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -841,13 +842,13 @@ export async function toggleTestEdge(edge) {
|
||||
});
|
||||
if (!response.ok) {
|
||||
const errorData = await response.json();
|
||||
error.textContent = `Test failed: ${errorData.detail}`;
|
||||
error.textContent = t('calibration.error.test_toggle_failed');
|
||||
error.style.display = 'block';
|
||||
}
|
||||
} catch (err) {
|
||||
if (err.isAuth) return;
|
||||
console.error('Failed to toggle CSS test edge:', err);
|
||||
error.textContent = 'Failed to toggle test edge';
|
||||
error.textContent = t('calibration.error.test_toggle_failed');
|
||||
error.style.display = 'block';
|
||||
}
|
||||
return;
|
||||
@@ -871,13 +872,13 @@ export async function toggleTestEdge(edge) {
|
||||
});
|
||||
if (!response.ok) {
|
||||
const errorData = await response.json();
|
||||
error.textContent = `Test failed: ${errorData.detail}`;
|
||||
error.textContent = t('calibration.error.test_toggle_failed');
|
||||
error.style.display = 'block';
|
||||
}
|
||||
} catch (err) {
|
||||
if (err.isAuth) return;
|
||||
console.error('Failed to toggle test edge:', err);
|
||||
error.textContent = 'Failed to toggle test edge';
|
||||
error.textContent = t('calibration.error.test_toggle_failed');
|
||||
error.style.display = 'block';
|
||||
}
|
||||
}
|
||||
@@ -920,13 +921,13 @@ export async function saveCalibration() {
|
||||
: parseInt(document.getElementById('cal-device-led-count-inline').textContent) || 0;
|
||||
if (!cssMode) {
|
||||
if (total !== declaredLedCount) {
|
||||
error.textContent = `Total LEDs (${total}) must equal device LED count (${declaredLedCount})`;
|
||||
error.textContent = t('calibration.error.led_count_mismatch');
|
||||
error.style.display = 'block';
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
if (declaredLedCount > 0 && total > declaredLedCount) {
|
||||
error.textContent = `Calibrated LEDs (${total}) exceed total LED count (${declaredLedCount})`;
|
||||
error.textContent = t('calibration.error.led_count_exceeded');
|
||||
error.style.display = 'block';
|
||||
return;
|
||||
}
|
||||
@@ -963,7 +964,7 @@ export async function saveCalibration() {
|
||||
});
|
||||
}
|
||||
if (response.ok) {
|
||||
showToast('Calibration saved', 'success');
|
||||
showToast(t('calibration.saved'), 'success');
|
||||
calibModal.forceClose();
|
||||
if (cssMode) {
|
||||
if (window.loadTargetsTab) window.loadTargetsTab();
|
||||
@@ -972,13 +973,13 @@ export async function saveCalibration() {
|
||||
}
|
||||
} else {
|
||||
const errorData = await response.json();
|
||||
error.textContent = `Failed to save: ${errorData.detail}`;
|
||||
error.textContent = t('calibration.error.save_failed');
|
||||
error.style.display = 'block';
|
||||
}
|
||||
} catch (err) {
|
||||
if (err.isAuth) return;
|
||||
console.error('Failed to save calibration:', err);
|
||||
error.textContent = 'Failed to save calibration';
|
||||
error.textContent = t('calibration.error.save_failed');
|
||||
error.style.display = 'block';
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user