Live KC test WS, sync clock fix, device card perf, camera icons, tab indicator

Key Colors test:
- New WS endpoint for live KC target test streaming (replaces REST polling)
- Auto-connect on lightbox open, auto-disconnect on close
- Uses same FPS/preview_width as CSS source test (no separate controls)
- Removed FPS selector, start/stop toggle, and updateAutoRefreshButton

Device cards:
- Fix full re-render on every poll caused by relative "Last seen" time in HTML
- Last seen label now patched in-place via data attribute (like FPS metrics)
- Remove overlay visualization button from LED target cards

Sync clocks:
- Fix card not updating start/stop icon: invalidate cache before reload

Other:
- Tab indicator respects bg-anim toggle (hidden when dynamic background off)
- Camera backend icon grid uses SVG icons instead of emoji
- Frontend context rule: no emoji in IconSelect items

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-03-17 02:03:07 +03:00
parent bcba5f33fc
commit 00c9ad3a86
16 changed files with 430 additions and 85 deletions

View File

@@ -157,6 +157,7 @@ export async function pauseSyncClock(clockId) {
const resp = await fetchWithAuth(`/sync-clocks/${clockId}/pause`, { method: 'POST' });
if (!resp.ok) throw new Error(`HTTP ${resp.status}`);
showToast(t('sync_clock.paused'), 'success');
syncClocksCache.invalidate();
await loadPictureSources();
} catch (e) {
if (e.isAuth) return;
@@ -169,6 +170,7 @@ export async function resumeSyncClock(clockId) {
const resp = await fetchWithAuth(`/sync-clocks/${clockId}/resume`, { method: 'POST' });
if (!resp.ok) throw new Error(`HTTP ${resp.status}`);
showToast(t('sync_clock.resumed'), 'success');
syncClocksCache.invalidate();
await loadPictureSources();
} catch (e) {
if (e.isAuth) return;
@@ -181,6 +183,7 @@ export async function resetSyncClock(clockId) {
const resp = await fetchWithAuth(`/sync-clocks/${clockId}/reset`, { method: 'POST' });
if (!resp.ok) throw new Error(`HTTP ${resp.status}`);
showToast(t('sync_clock.reset_done'), 'success');
syncClocksCache.invalidate();
await loadPictureSources();
} catch (e) {
if (e.isAuth) return;