diff --git a/server/src/wled_controller/static/js/features/audio-sources.ts b/server/src/wled_controller/static/js/features/audio-sources.ts index 733b045..a7f943b 100644 --- a/server/src/wled_controller/static/js/features/audio-sources.ts +++ b/server/src/wled_controller/static/js/features/audio-sources.ts @@ -58,7 +58,7 @@ export async function showAudioSourceModal(sourceType: any, editData?: any) { ? (editData.source_type === 'mono' ? 'audio_source.edit.mono' : 'audio_source.edit.multichannel') : (sourceType === 'mono' ? 'audio_source.add.mono' : 'audio_source.add.multichannel'); - document.getElementById('audio-source-modal-title').innerHTML = `${ICON_MUSIC} ${t(titleKey)}`; + document.getElementById('audio-source-modal-title')!.innerHTML = `${ICON_MUSIC} ${t(titleKey)}`; (document.getElementById('audio-source-id') as HTMLInputElement).value = isEdit ? editData.id : ''; (document.getElementById('audio-source-error') as HTMLElement).style.display = 'none'; @@ -253,12 +253,12 @@ function _filterDevicesBySelectedTemplate() { const template = templates.find(t => t.id === templateId); const engineType = template ? template.engine_type : null; - let devices = []; + let devices: any[] = []; if (engineType && _cachedDevicesByEngine[engineType]) { devices = _cachedDevicesByEngine[engineType]; } else { for (const devList of Object.values(_cachedDevicesByEngine)) { - devices = devices.concat(devList); + devices = devices.concat(devList as any[]); } } @@ -370,9 +370,9 @@ export function testAudioSource(sourceId: any) { _testAudioPeaks.fill(0); _testBeatFlash = 0; - document.getElementById('audio-test-rms').textContent = '---'; - document.getElementById('audio-test-peak').textContent = '---'; - document.getElementById('audio-test-beat-dot').classList.remove('active'); + document.getElementById('audio-test-rms')!.textContent = '---'; + document.getElementById('audio-test-peak')!.textContent = '---'; + document.getElementById('audio-test-beat-dot')!.classList.remove('active'); testAudioModal.open(); @@ -382,7 +382,7 @@ export function testAudioSource(sourceId: any) { // Connect WebSocket const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:'; - const wsUrl = `${protocol}//${window.location.host}${API_BASE}/audio-sources/${sourceId}/test/ws?token=${encodeURIComponent(apiKey)}`; + const wsUrl = `${protocol}//${window.location.host}${API_BASE}/audio-sources/${sourceId}/test/ws?token=${encodeURIComponent(apiKey ?? '')}`; try { _testAudioWs = new WebSocket(wsUrl); @@ -434,7 +434,7 @@ function _cleanupTest() { } function _sizeCanvas(canvas: HTMLCanvasElement) { - const rect = canvas.parentElement.getBoundingClientRect(); + const rect = canvas.parentElement!.getBoundingClientRect(); const dpr = window.devicePixelRatio || 1; canvas.width = rect.width * dpr; canvas.height = 200 * dpr; @@ -463,8 +463,7 @@ export function initAudioSourceDelegation(container: HTMLElement): void { if (!btn) return; const action = btn.dataset.action; - const id = btn.dataset.id; - if (!action || !id) return; + if (!action) return; // Only handle audio-source actions (prefixed with audio-) const handler = _audioSourceActions[action]; @@ -472,6 +471,9 @@ export function initAudioSourceDelegation(container: HTMLElement): void { // Verify we're inside an audio source section const section = btn.closest('[data-card-section="audio-multi"], [data-card-section="audio-mono"]'); if (!section) return; + const card = btn.closest('[data-id]'); + const id = card?.getAttribute('data-id'); + if (!id) return; e.stopPropagation(); handler(id); } @@ -530,12 +532,12 @@ function _renderAudioSpectrum() { } // Update stats - document.getElementById('audio-test-rms').textContent = (data.rms * 100).toFixed(1) + '%'; - document.getElementById('audio-test-peak').textContent = (data.peak * 100).toFixed(1) + '%'; + document.getElementById('audio-test-rms')!.textContent = (data.rms * 100).toFixed(1) + '%'; + document.getElementById('audio-test-peak')!.textContent = (data.peak * 100).toFixed(1) + '%'; const beatDot = document.getElementById('audio-test-beat-dot'); if (data.beat) { - beatDot.classList.add('active'); + beatDot!.classList.add('active'); } else { - beatDot.classList.remove('active'); + beatDot!.classList.remove('active'); } } diff --git a/server/src/wled_controller/static/js/features/automations.ts b/server/src/wled_controller/static/js/features/automations.ts index 4f92ba6..daf324e 100644 --- a/server/src/wled_controller/static/js/features/automations.ts +++ b/server/src/wled_controller/static/js/features/automations.ts @@ -191,11 +191,11 @@ function renderAutomations(automations: any, sceneMap: any) { { key: 'scenes', html: csScenes.render(sceneItems) }, ].map(p => `
${p.html}
`).join(''); - container.innerHTML = panels; + container!.innerHTML = panels; CardSection.bindAll([csAutomations, csScenes]); // Event delegation for scene preset card actions - initScenePresetDelegation(container); + initScenePresetDelegation(container!); _automationsTree.setExtraHtml(``); _automationsTree.update(treeItems, activeTab); @@ -313,7 +313,7 @@ export async function openAutomationEditor(automationId?: any, cloneData?: any) const errorEl = document.getElementById('automation-editor-error') as HTMLElement; errorEl.style.display = 'none'; - condList.innerHTML = ''; + condList!.innerHTML = ''; _ensureConditionLogicIconSelect(); _ensureDeactivationModeIconSelect(); @@ -331,7 +331,7 @@ export async function openAutomationEditor(automationId?: any, cloneData?: any) let _editorTags: any[] = []; if (automationId) { - titleEl.innerHTML = `${ICON_AUTOMATION} ${t('automations.edit')}`; + titleEl!.innerHTML = `${ICON_AUTOMATION} ${t('automations.edit')}`; try { const resp = await fetchWithAuth(`/automations/${automationId}`); if (!resp.ok) throw new Error('Failed to load automation'); @@ -363,7 +363,7 @@ export async function openAutomationEditor(automationId?: any, cloneData?: any) } } else if (cloneData) { // Clone mode — create with prefilled data - titleEl.innerHTML = `${ICON_AUTOMATION} ${t('automations.add')}`; + titleEl!.innerHTML = `${ICON_AUTOMATION} ${t('automations.add')}`; idInput.value = ''; nameInput.value = (cloneData.name || '') + ' (Copy)'; enabledInput.checked = cloneData.enabled !== false; @@ -386,7 +386,7 @@ export async function openAutomationEditor(automationId?: any, cloneData?: any) _initSceneSelector('automation-fallback-scene-id', cloneData.deactivation_scene_preset_id); _editorTags = cloneData.tags || []; } else { - titleEl.innerHTML = `${ICON_AUTOMATION} ${t('automations.add')}`; + titleEl!.innerHTML = `${ICON_AUTOMATION} ${t('automations.add')}`; idInput.value = ''; nameInput.value = ''; enabledInput.checked = true; @@ -400,11 +400,11 @@ export async function openAutomationEditor(automationId?: any, cloneData?: any) (document.getElementById('automation-deactivation-mode') as HTMLSelectElement).onchange = _onDeactivationModeChange; automationModal.open(); - modal.querySelectorAll('[data-i18n]').forEach(el => { - el.textContent = t(el.getAttribute('data-i18n')); + modal!.querySelectorAll('[data-i18n]').forEach(el => { + el.textContent = t(el.getAttribute('data-i18n')!); }); - modal.querySelectorAll('[data-i18n-placeholder]').forEach(el => { - (el as HTMLInputElement).placeholder = t(el.getAttribute('data-i18n-placeholder')); + modal!.querySelectorAll('[data-i18n-placeholder]').forEach(el => { + (el as HTMLInputElement).placeholder = t(el.getAttribute('data-i18n-placeholder')!); }); // Tags @@ -762,7 +762,7 @@ function addAutomationConditionRow(condition: any) { renderFields(typeSelect.value, {}); }); - list.appendChild(row); + list!.appendChild(row); } diff --git a/server/src/wled_controller/static/js/features/calibration.ts b/server/src/wled_controller/static/js/features/calibration.ts index 77090cd..92f8ec2 100644 --- a/server/src/wled_controller/static/js/features/calibration.ts +++ b/server/src/wled_controller/static/js/features/calibration.ts @@ -148,7 +148,7 @@ export async function showCalibration(deviceId: any) { const preview = document.querySelector('.calibration-preview') as HTMLElement; const displayIndex = device.settings?.display_index ?? 0; - const display = displays.find((d: any) => d.index === displayIndex); + const display = (displays ?? []).find((d: any) => d.index === displayIndex); if (display && display.width && display.height) { preview.style.aspectRatio = `${display.width} / ${display.height}`; } else { diff --git a/server/src/wled_controller/static/js/features/devices.ts b/server/src/wled_controller/static/js/features/devices.ts index e0ee62b..f6cd56e 100644 --- a/server/src/wled_controller/static/js/features/devices.ts +++ b/server/src/wled_controller/static/js/features/devices.ts @@ -38,7 +38,7 @@ function _ensureSettingsCsptSelect() { icon: ICON_TEMPLATE, desc: '', })), - placeholder: window.t ? t('palette.search') : 'Search...', + placeholder: t('palette.search'), allowNone: true, noneLabel: t('common.none_no_cspt'), } as any); @@ -443,7 +443,7 @@ export async function showSettings(deviceId: any) { // Tags if (_deviceTagsInput) _deviceTagsInput.destroy(); _deviceTagsInput = new TagInput(document.getElementById('device-tags-container'), { - placeholder: window.t ? t('tags.placeholder') : 'Add tag...' + placeholder: t('tags.placeholder'), }); _deviceTagsInput.setValue(device.tags || []); diff --git a/server/src/wled_controller/static/js/features/kc-targets.ts b/server/src/wled_controller/static/js/features/kc-targets.ts index 67f5ab4..e40f62f 100644 --- a/server/src/wled_controller/static/js/features/kc-targets.ts +++ b/server/src/wled_controller/static/js/features/kc-targets.ts @@ -195,9 +195,9 @@ export function createKCTargetCard(target: OutputTarget & { state?: any; metrics const brightness = kcSettings.brightness ?? 1.0; const brightnessInt = Math.round(brightness * 255); - const source = sourceMap[target.picture_source_id]; + const source = sourceMap[target.picture_source_id!]; const sourceName = source ? source.name : (target.picture_source_id || 'No source'); - const patTmpl = patternTemplateMap[kcSettings.pattern_template_id]; + const patTmpl = patternTemplateMap[kcSettings.pattern_template_id!]; const patternName = patTmpl ? patTmpl.name : 'No pattern'; const rectCount = patTmpl ? (patTmpl.rectangles || []).length : 0; @@ -226,7 +226,7 @@ export function createKCTargetCard(target: OutputTarget & { state?: any; metrics content: `
- ${escapeHtml(target.name)} + ${escapeHtml(target.name)}
@@ -617,7 +617,7 @@ export async function showKCEditor(targetId: any = null, cloneData: any = null) // Tags if (_kcTagsInput) _kcTagsInput.destroy(); _kcTagsInput = new TagInput(document.getElementById('kc-tags-container'), { - placeholder: window.t ? t('tags.placeholder') : 'Add tag...' + placeholder: t('tags.placeholder'), }); _kcTagsInput.setValue(_editorTags); diff --git a/server/src/wled_controller/static/js/features/scene-presets.ts b/server/src/wled_controller/static/js/features/scene-presets.ts index f44ab16..f2574cf 100644 --- a/server/src/wled_controller/static/js/features/scene-presets.ts +++ b/server/src/wled_controller/static/js/features/scene-presets.ts @@ -80,7 +80,7 @@ export function createSceneCard(preset: ScenePreset) {
-
${escapeHtml(preset.name)}
+
${escapeHtml(preset.name)}
${preset.description ? `
${escapeHtml(preset.description)}
` : ''}
@@ -221,7 +221,7 @@ export async function editScenePreset(presetId: string): Promise { export async function saveScenePreset(): Promise { const name = (document.getElementById('scene-preset-editor-name') as HTMLInputElement).value.trim(); const description = (document.getElementById('scene-preset-editor-description') as HTMLInputElement).value.trim(); - const errorEl = document.getElementById('scene-preset-editor-error'); + const errorEl = document.getElementById('scene-preset-editor-error')!; if (!name) { errorEl.textContent = t('scenes.error.name_required'); @@ -277,6 +277,7 @@ function _getAddedTargetIds(): Set { return new Set( [...document.querySelectorAll('#scene-target-list .scene-target-item')] .map(el => (el as HTMLElement).dataset.targetId) + .filter(Boolean) as string[] ); } @@ -482,7 +483,7 @@ export function initScenePresetDelegation(container: HTMLElement): void { if (action === 'navigate-scene') { // Only navigate if click wasn't on a child button if ((e.target as HTMLElement).closest('button')) return; - navigateToCard('automations', null, 'scenes', 'data-scene-id', id); + navigateToCard('automations', null, 'scenes', 'data-scene-id', id!); return; } diff --git a/server/src/wled_controller/static/js/features/settings.ts b/server/src/wled_controller/static/js/features/settings.ts index f262915..a6ee527 100644 --- a/server/src/wled_controller/static/js/features/settings.ts +++ b/server/src/wled_controller/static/js/features/settings.ts @@ -144,7 +144,7 @@ export function connectLogViewer(): void { } const proto = location.protocol === 'https:' ? 'wss:' : 'ws:'; - const url = `${proto}//${location.host}/api/v1/system/logs/ws?token=${encodeURIComponent(apiKey)}`; + const url = `${proto}//${location.host}/api/v1/system/logs/ws?token=${encodeURIComponent(apiKey ?? '')}`; _logWs = new WebSocket(url); @@ -321,7 +321,7 @@ export async function downloadBackup(): Promise { // ─── Restore ─────────────────────────────────────────────── export async function handleRestoreFileSelected(input: HTMLInputElement): Promise { - const file = input.files[0]; + const file = input.files![0]; input.value = ''; if (!file) return; @@ -438,7 +438,7 @@ export async function loadAutoBackupSettings(): Promise { (document.getElementById('auto-backup-interval') as HTMLInputElement).value = String(data.interval_hours); (document.getElementById('auto-backup-max') as HTMLInputElement).value = data.max_backups; - const statusEl = document.getElementById('auto-backup-status'); + const statusEl = document.getElementById('auto-backup-status')!; if (data.last_backup_time) { const d = new Date(data.last_backup_time); statusEl.textContent = t('settings.auto_backup.last_backup') + ': ' + d.toLocaleString(); @@ -476,7 +476,7 @@ export async function saveAutoBackupSettings(): Promise { // ─── Saved backup list ──────────────────────────────────── export async function loadBackupList(): Promise { - const container = document.getElementById('saved-backups-list'); + const container = document.getElementById('saved-backups-list')!; try { const resp = await fetchWithAuth('/system/backups'); if (!resp.ok) return; @@ -654,7 +654,7 @@ export async function downloadPartialExport(): Promise { } export async function handlePartialImportFileSelected(input: HTMLInputElement): Promise { - const file = input.files[0]; + const file = input.files![0]; input.value = ''; if (!file) return; diff --git a/server/src/wled_controller/static/js/features/streams-audio-templates.ts b/server/src/wled_controller/static/js/features/streams-audio-templates.ts index 2b19798..bd100b7 100644 --- a/server/src/wled_controller/static/js/features/streams-audio-templates.ts +++ b/server/src/wled_controller/static/js/features/streams-audio-templates.ts @@ -429,7 +429,7 @@ export function startAudioTemplateTest() { // Connect WebSocket const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:'; - const wsUrl = `${protocol}//${window.location.host}${API_BASE}/audio-templates/${_currentTestAudioTemplateId}/test/ws?token=${encodeURIComponent(apiKey)}&device_index=${devIdx}&is_loopback=${devLoop === '1' ? '1' : '0'}`; + const wsUrl = `${protocol}//${window.location.host}${API_BASE}/audio-templates/${_currentTestAudioTemplateId}/test/ws?token=${encodeURIComponent(apiKey ?? '')}&device_index=${devIdx}&is_loopback=${devLoop === '1' ? '1' : '0'}`; try { _tplTestWs = new WebSocket(wsUrl); diff --git a/server/src/wled_controller/static/js/features/sync-clocks.ts b/server/src/wled_controller/static/js/features/sync-clocks.ts index ab99334..d1b4985 100644 --- a/server/src/wled_controller/static/js/features/sync-clocks.ts +++ b/server/src/wled_controller/static/js/features/sync-clocks.ts @@ -41,19 +41,19 @@ const syncClockModal = new SyncClockModal(); export async function showSyncClockModal(editData: SyncClock | null): Promise { const isEdit = !!editData; const titleKey = isEdit ? 'sync_clock.edit' : 'sync_clock.add'; - document.getElementById('sync-clock-modal-title').innerHTML = `${ICON_CLOCK} ${t(titleKey)}`; - (document.getElementById('sync-clock-id') as HTMLInputElement).value = isEdit ? editData.id : ''; + document.getElementById('sync-clock-modal-title')!.innerHTML = `${ICON_CLOCK} ${t(titleKey)}`; + (document.getElementById('sync-clock-id') as HTMLInputElement).value = editData?.id || ''; (document.getElementById('sync-clock-error') as HTMLElement).style.display = 'none'; if (isEdit) { (document.getElementById('sync-clock-name') as HTMLInputElement).value = editData.name || ''; (document.getElementById('sync-clock-speed') as HTMLInputElement).value = String(editData.speed ?? 1.0); - document.getElementById('sync-clock-speed-display').textContent = String(editData.speed ?? 1.0); + document.getElementById('sync-clock-speed-display')!.textContent = String(editData.speed ?? 1.0); (document.getElementById('sync-clock-description') as HTMLInputElement).value = editData.description || ''; } else { (document.getElementById('sync-clock-name') as HTMLInputElement).value = ''; (document.getElementById('sync-clock-speed') as HTMLInputElement).value = String(1.0); - document.getElementById('sync-clock-speed-display').textContent = '1'; + document.getElementById('sync-clock-speed-display')!.textContent = '1'; (document.getElementById('sync-clock-description') as HTMLInputElement).value = ''; } @@ -230,10 +230,10 @@ export function createSyncClockCard(clock: SyncClock) { ${renderTagChips(clock.tags)} ${clock.description ? `
${escapeHtml(clock.description)}
` : ''}`, actions: ` - - - - `, + + + + `, }); } @@ -253,12 +253,13 @@ export function initSyncClockDelegation(container: HTMLElement): void { if (!btn) return; // Only handle actions within a sync-clock card (data-id on card root) - const card = btn.closest('[data-id]'); const section = btn.closest('[data-card-section="sync-clocks"]'); - if (!card || !section) return; + if (!section) return; + const card = btn.closest('[data-id]'); + if (!card) return; const action = btn.dataset.action; - const id = btn.dataset.id; + const id = card.getAttribute('data-id'); if (!action || !id) return; const handler = _syncClockActions[action]; diff --git a/server/src/wled_controller/static/js/features/tabs.ts b/server/src/wled_controller/static/js/features/tabs.ts index 5d3195e..2aeea4a 100644 --- a/server/src/wled_controller/static/js/features/tabs.ts +++ b/server/src/wled_controller/static/js/features/tabs.ts @@ -127,7 +127,7 @@ export function startAutoRefresh(): void { if (activeTab === 'targets') { // Skip refresh while user interacts with a picker or slider const panel = document.getElementById('targets-panel-content'); - if (panel && panel.contains(document.activeElement) && document.activeElement.matches('input')) return; + if (panel && panel.contains(document.activeElement) && document.activeElement!.matches('input')) return; if (typeof window.loadTargetsTab === 'function') window.loadTargetsTab(); } else if (activeTab === 'dashboard') { if (typeof window.loadDashboard === 'function') window.loadDashboard(); diff --git a/server/src/wled_controller/static/js/features/targets.ts b/server/src/wled_controller/static/js/features/targets.ts index 4372cb3..e42a356 100644 --- a/server/src/wled_controller/static/js/features/targets.ts +++ b/server/src/wled_controller/static/js/features/targets.ts @@ -461,7 +461,7 @@ export async function showTargetEditor(targetId: string | null = null, cloneData // Tags if (_targetTagsInput) _targetTagsInput.destroy(); _targetTagsInput = new TagInput(document.getElementById('target-tags-container'), { - placeholder: window.t ? t('tags.placeholder') : 'Add tag...' + placeholder: t('tags.placeholder'), }); _targetTagsInput.setValue(_editorTags); @@ -712,8 +712,8 @@ export async function loadTargetsTab() { const ledResult = csLedTargets.reconcile(ledTargetItems); const kcResult = csKCTargets.reconcile(kcTargetItems); csPatternTemplates.reconcile(patternItems); - changedTargetIds = new Set([...ledResult.added, ...ledResult.replaced, ...ledResult.removed, - ...kcResult.added, ...kcResult.replaced, ...kcResult.removed]); + changedTargetIds = new Set([...(ledResult.added as unknown as string[]), ...(ledResult.replaced as unknown as string[]), ...(ledResult.removed as unknown as string[]), + ...(kcResult.added as unknown as string[]), ...(kcResult.replaced as unknown as string[]), ...(kcResult.removed as unknown as string[])]); // Restore LED preview state on replaced cards (panel hidden by default in HTML) for (const id of Array.from(ledResult.replaced) as any[]) { @@ -976,7 +976,7 @@ export function createTargetCard(target: OutputTarget & { state?: any; metrics?: const isProcessing = state.processing || false; - const device = deviceMap[target.device_id]; + const device = deviceMap[target.device_id!]; const deviceName = device ? device.name : (target.device_id || 'No device'); const cssId = target.color_strip_source_id || ''; @@ -1008,7 +1008,7 @@ export function createTargetCard(target: OutputTarget & { state?: any; metrics?:
- ${escapeHtml(target.name)} + ${escapeHtml(target.name)} ${ICON_WARNING}
@@ -1018,7 +1018,7 @@ export function createTargetCard(target: OutputTarget & { state?: any; metrics?: ${_protocolBadge(device, target)} ${ICON_FILM} ${cssSummary} ${bvs ? `${getValueSourceIcon(bvs.source_type)} ${escapeHtml(bvs.name)}` : ''} - ${target.min_brightness_threshold > 0 ? `${ICON_SUN_DIM} <${target.min_brightness_threshold} → off` : ''} + ${(target.min_brightness_threshold ?? 0) > 0 ? `${ICON_SUN_DIM} <${target.min_brightness_threshold} → off` : ''}
${renderTagChips(target.tags)}
diff --git a/server/src/wled_controller/static/js/features/tutorials.ts b/server/src/wled_controller/static/js/features/tutorials.ts index e78e1b7..4374d9c 100644 --- a/server/src/wled_controller/static/js/features/tutorials.ts +++ b/server/src/wled_controller/static/js/features/tutorials.ts @@ -263,7 +263,7 @@ function _positionSpotlight(target: Element, overlay: HTMLElement, step: Tutoria w = targetRect.width + pad * 2; h = targetRect.height + pad * 2; } else { - const containerRect = activeTutorial.container.getBoundingClientRect(); + const containerRect = activeTutorial!.container!.getBoundingClientRect(); x = targetRect.left - containerRect.left - pad; y = targetRect.top - containerRect.top - pad; w = targetRect.width + pad * 2; @@ -292,12 +292,12 @@ function _positionSpotlight(target: Element, overlay: HTMLElement, step: Tutoria const textEl = overlay.querySelector('.tutorial-tooltip-text'); const counterEl = overlay.querySelector('.tutorial-step-counter'); if (textEl) textEl.textContent = t(step.textKey); - if (counterEl) counterEl.textContent = `${index + 1} / ${activeTutorial.steps.length}`; + if (counterEl) counterEl.textContent = `${index + 1} / ${activeTutorial!.steps.length}`; const prevBtn = overlay.querySelector('.tutorial-prev-btn') as HTMLButtonElement; const nextBtn = overlay.querySelector('.tutorial-next-btn'); if (prevBtn) prevBtn.disabled = (index === 0); - if (nextBtn) nextBtn.textContent = (index === activeTutorial.steps.length - 1) ? '\u2713' : '\u2192'; + if (nextBtn) nextBtn.textContent = (index === activeTutorial!.steps.length - 1) ? '\u2713' : '\u2192'; if (tooltip) { positionTutorialTooltip(tooltip, x, y, w, h, step.position, isFixed); @@ -381,8 +381,8 @@ function positionTutorialTooltip(tooltip: HTMLElement, sx: number, sy: number, s let pos = positions[preferred] || positions.bottom; - const cW = isFixed ? window.innerWidth : activeTutorial.container.clientWidth; - const cH = isFixed ? window.innerHeight : activeTutorial.container.clientHeight; + const cW = isFixed ? window.innerWidth : activeTutorial!.container!.clientWidth; + const cH = isFixed ? window.innerHeight : activeTutorial!.container!.clientHeight; if (pos.y + tooltipH > cH || pos.y < 0 || pos.x + tooltipW > cW || pos.x < 0) { const opposite = { top: 'bottom', bottom: 'top', left: 'right', right: 'left' }; diff --git a/server/src/wled_controller/static/js/features/value-sources.ts b/server/src/wled_controller/static/js/features/value-sources.ts index 19e3eb8..828cc7b 100644 --- a/server/src/wled_controller/static/js/features/value-sources.ts +++ b/server/src/wled_controller/static/js/features/value-sources.ts @@ -571,9 +571,12 @@ export function testValueSource(sourceId: any) { _testVsMinObserved = Infinity; _testVsMaxObserved = -Infinity; - document.getElementById('vs-test-current').textContent = '---'; - document.getElementById('vs-test-min').textContent = '---'; - document.getElementById('vs-test-max').textContent = '---'; + const currentEl = document.getElementById('vs-test-current'); + const minEl = document.getElementById('vs-test-min'); + const maxEl = document.getElementById('vs-test-max'); + if (currentEl) currentEl.textContent = '---'; + if (minEl) minEl.textContent = '---'; + if (maxEl) maxEl.textContent = '---'; testVsModal.open(); @@ -583,7 +586,7 @@ export function testValueSource(sourceId: any) { // Connect WebSocket const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:'; - const wsUrl = `${protocol}//${window.location.host}${API_BASE}/value-sources/${sourceId}/test/ws?token=${encodeURIComponent(apiKey)}`; + const wsUrl = `${protocol}//${window.location.host}${API_BASE}/value-sources/${sourceId}/test/ws?token=${encodeURIComponent(apiKey || '')}`; try { _testVsWs = new WebSocket(wsUrl); @@ -645,7 +648,7 @@ function _cleanupVsTest() { } function _sizeVsCanvas(canvas: HTMLCanvasElement) { - const rect = canvas.parentElement.getBoundingClientRect(); + const rect = canvas.parentElement!.getBoundingClientRect(); const dpr = window.devicePixelRatio || 1; canvas.width = rect.width * dpr; canvas.height = 200 * dpr; @@ -725,14 +728,17 @@ function _renderVsChart() { ctx.stroke(); // Update stats - if (_testVsLatest !== null) { - document.getElementById('vs-test-current').textContent = (_testVsLatest * 100).toFixed(1) + '%'; + const curEl = document.getElementById('vs-test-current'); + const mnEl = document.getElementById('vs-test-min'); + const mxEl = document.getElementById('vs-test-max'); + if (_testVsLatest !== null && curEl) { + curEl.textContent = (_testVsLatest * 100).toFixed(1) + '%'; } - if (_testVsMinObserved !== Infinity) { - document.getElementById('vs-test-min').textContent = (_testVsMinObserved * 100).toFixed(1) + '%'; + if (_testVsMinObserved !== Infinity && mnEl) { + mnEl.textContent = (_testVsMinObserved * 100).toFixed(1) + '%'; } - if (_testVsMaxObserved !== -Infinity) { - document.getElementById('vs-test-max').textContent = (_testVsMaxObserved * 100).toFixed(1) + '%'; + if (_testVsMaxObserved !== -Infinity && mxEl) { + mxEl.textContent = (_testVsMaxObserved * 100).toFixed(1) + '%'; } }