Fix critical frontend issues: race conditions, memory leaks, silent failures

- Add loading guard to loadPictureSources to prevent concurrent fetches
- Pause perf chart polling and uptime timer when browser tab is hidden
- Disconnect KC and LED preview WebSockets when leaving targets tab
- Add error toasts to loadCaptureTemplates and saveKCBrightness
- Skip auto-refresh polling when document is hidden
- Widen auto-start dashboard cards for better text display

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-02-25 17:29:47 +03:00
parent b51839ef3c
commit 82e12ffaac
9 changed files with 86 additions and 17 deletions

View File

@@ -20,6 +20,7 @@ import {
_lastValidatedImageSource, set_lastValidatedImageSource,
_cachedAudioSources, set_cachedAudioSources,
_cachedValueSources, set_cachedValueSources,
_sourcesLoading, set_sourcesLoading,
apiKey,
} from '../core/state.js';
import { API_BASE, getHeaders, fetchWithAuth, escapeHtml } from '../core/api.js';
@@ -129,7 +130,9 @@ async function loadCaptureTemplates() {
set_cachedCaptureTemplates(data.templates || []);
renderPictureSourcesList(_cachedStreams);
} catch (error) {
if (error.isAuth) return;
console.error('Error loading capture templates:', error);
showToast(t('streams.error.load'), 'error');
}
}
@@ -511,6 +514,8 @@ export async function deleteTemplate(templateId) {
// ===== Picture Sources =====
export async function loadPictureSources() {
if (_sourcesLoading) return;
set_sourcesLoading(true);
try {
const [filtersResp, ppResp, captResp, streamsResp, audioResp, valueResp] = await Promise.all([
_availableFilters.length === 0 ? fetchWithAuth('/filters') : Promise.resolve(null),
@@ -546,10 +551,13 @@ export async function loadPictureSources() {
set_cachedStreams(data.streams || []);
renderPictureSourcesList(_cachedStreams);
} catch (error) {
if (error.isAuth) return;
console.error('Error loading picture sources:', error);
document.getElementById('streams-list').innerHTML = `
<div class="error-message">${t('streams.error.load')}: ${error.message}</div>
`;
} finally {
set_sourcesLoading(false);
}
}