Migrate frontend from JavaScript to TypeScript
- Rename all 54 .js files to .ts, update esbuild entry point - Add tsconfig.json, TypeScript devDependency, typecheck script - Create types.ts with 25+ interfaces matching backend Pydantic schemas (Device, OutputTarget, ColorStripSource, PatternTemplate, ValueSource, AudioSource, PictureSource, ScenePreset, SyncClock, Automation, etc.) - Make DataCache generic (DataCache<T>) with typed state instances - Type all state variables in state.ts with proper entity types - Type all create*Card functions with proper entity interfaces - Type all function parameters and return types across all 54 files - Type core component constructors (CardSection, IconSelect, EntitySelect, FilterList, TagInput, TreeNav, Modal) with exported option interfaces - Add comprehensive global.d.ts for window function declarations - Type fetchWithAuth with FetchAuthOpts interface - Remove all (window as any) casts in favor of global.d.ts declarations - Zero tsc errors, esbuild bundle unchanged Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
682
server/src/wled_controller/static/js/app.ts
Normal file
682
server/src/wled_controller/static/js/app.ts
Normal file
@@ -0,0 +1,682 @@
|
||||
/**
|
||||
* Entry point — imports all modules, registers globals, initializes app.
|
||||
*/
|
||||
|
||||
// Layer 0: state
|
||||
import { apiKey, setApiKey, refreshInterval } from './core/state.ts';
|
||||
import { Modal } from './core/modal.ts';
|
||||
|
||||
// Layer 1: api, i18n
|
||||
import { loadServerInfo, loadDisplays, configureApiKey, startConnectionMonitor, stopConnectionMonitor } from './core/api.ts';
|
||||
import { t, initLocale, changeLocale } from './core/i18n.ts';
|
||||
|
||||
// Layer 1.5: visual effects
|
||||
import { initCardGlare } from './core/card-glare.ts';
|
||||
import { initBgAnim, updateBgAnimAccent, updateBgAnimTheme } from './core/bg-anim.ts';
|
||||
import { initTabIndicator, updateTabIndicator } from './core/tab-indicator.ts';
|
||||
|
||||
// Layer 2: ui
|
||||
import {
|
||||
toggleHint, lockBody, unlockBody, closeLightbox,
|
||||
showToast, showConfirm, closeConfirmModal,
|
||||
openFullImageLightbox, showOverlaySpinner, hideOverlaySpinner,
|
||||
} from './core/ui.ts';
|
||||
|
||||
// Layer 3: displays, tutorials
|
||||
import {
|
||||
openDisplayPicker, closeDisplayPicker, selectDisplay, formatDisplayLabel,
|
||||
} from './features/displays.ts';
|
||||
import {
|
||||
startCalibrationTutorial, startDeviceTutorial, startGettingStartedTutorial,
|
||||
startDashboardTutorial, startTargetsTutorial, startSourcesTutorial, startAutomationsTutorial,
|
||||
closeTutorial, tutorialNext, tutorialPrev,
|
||||
} from './features/tutorials.ts';
|
||||
|
||||
// Layer 4: devices, dashboard, streams, kc-targets, pattern-templates, automations
|
||||
import {
|
||||
showSettings, closeDeviceSettingsModal, forceCloseDeviceSettingsModal,
|
||||
saveDeviceSettings, updateBrightnessLabel, saveCardBrightness,
|
||||
turnOffDevice, pingDevice, removeDevice, loadDevices,
|
||||
updateSettingsBaudFpsHint, copyWsUrl,
|
||||
} from './features/devices.ts';
|
||||
import {
|
||||
loadDashboard, stopUptimeTimer,
|
||||
dashboardToggleAutomation, dashboardStartTarget, dashboardStopTarget, dashboardStopAll,
|
||||
dashboardPauseClock, dashboardResumeClock, dashboardResetClock,
|
||||
toggleDashboardSection, changeDashboardPollInterval,
|
||||
} from './features/dashboard.ts';
|
||||
import { startEventsWS, stopEventsWS } from './core/events-ws.ts';
|
||||
import { startEntityEventListeners } from './core/entity-events.ts';
|
||||
import {
|
||||
startPerfPolling, stopPerfPolling,
|
||||
} from './features/perf-charts.ts';
|
||||
import {
|
||||
loadPictureSources, switchStreamTab,
|
||||
showAddTemplateModal, editTemplate, closeTemplateModal, saveTemplate, deleteTemplate,
|
||||
showTestTemplateModal, closeTestTemplateModal, onEngineChange, runTemplateTest,
|
||||
showAddAudioTemplateModal, editAudioTemplate, closeAudioTemplateModal, saveAudioTemplate, deleteAudioTemplate,
|
||||
cloneAudioTemplate, onAudioEngineChange,
|
||||
showTestAudioTemplateModal, closeTestAudioTemplateModal, startAudioTemplateTest,
|
||||
showAddStreamModal, editStream, closeStreamModal, saveStream, deleteStream,
|
||||
onStreamTypeChange, onStreamDisplaySelected, onTestDisplaySelected,
|
||||
showTestStreamModal, closeTestStreamModal, updateStreamTestDuration, runStreamTest,
|
||||
showTestPPTemplateModal, closeTestPPTemplateModal, updatePPTestDuration, runPPTemplateTest,
|
||||
showAddPPTemplateModal, editPPTemplate, closePPTemplateModal, savePPTemplate, deletePPTemplate,
|
||||
addFilterFromSelect, toggleFilterExpand, removeFilter, moveFilter, updateFilterOption,
|
||||
renderModalFilterList, updateCaptureDuration,
|
||||
cloneStream, cloneCaptureTemplate, clonePPTemplate,
|
||||
showAddCSPTModal, editCSPT, closeCSPTModal, saveCSPT, deleteCSPT, cloneCSPT,
|
||||
csptAddFilterFromSelect, csptToggleFilterExpand, csptRemoveFilter, csptUpdateFilterOption,
|
||||
renderCSPTModalFilterList,
|
||||
} from './features/streams.ts';
|
||||
import {
|
||||
createKCTargetCard, testKCTarget,
|
||||
showKCEditor, closeKCEditorModal, forceCloseKCEditorModal, saveKCEditor,
|
||||
deleteKCTarget, disconnectAllKCWebSockets,
|
||||
updateKCBrightnessLabel, saveKCBrightness,
|
||||
cloneKCTarget,
|
||||
} from './features/kc-targets.ts';
|
||||
import {
|
||||
createPatternTemplateCard,
|
||||
showPatternTemplateEditor, closePatternTemplateModal, forceClosePatternTemplateModal,
|
||||
savePatternTemplate, deletePatternTemplate,
|
||||
renderPatternRectList, selectPatternRect, updatePatternRect,
|
||||
addPatternRect, deleteSelectedPatternRect, removePatternRect,
|
||||
capturePatternBackground,
|
||||
clonePatternTemplate,
|
||||
} from './features/pattern-templates.ts';
|
||||
import {
|
||||
loadAutomations, openAutomationEditor, closeAutomationEditorModal,
|
||||
saveAutomationEditor, addAutomationCondition,
|
||||
toggleAutomationEnabled, cloneAutomation, deleteAutomation, copyWebhookUrl,
|
||||
} from './features/automations.ts';
|
||||
import {
|
||||
openScenePresetCapture, editScenePreset, saveScenePreset, closeScenePresetEditor,
|
||||
activateScenePreset, recaptureScenePreset, cloneScenePreset, deleteScenePreset,
|
||||
addSceneTarget, removeSceneTarget,
|
||||
} from './features/scene-presets.ts';
|
||||
|
||||
// Layer 5: device-discovery, targets
|
||||
import {
|
||||
onDeviceTypeChanged, updateBaudFpsHint, onSerialPortFocus,
|
||||
showAddDevice, closeAddDeviceModal, scanForDevices, handleAddDevice,
|
||||
cloneDevice,
|
||||
} from './features/device-discovery.ts';
|
||||
import {
|
||||
loadTargetsTab, switchTargetSubTab,
|
||||
showTargetEditor, closeTargetEditorModal, forceCloseTargetEditorModal, saveTargetEditor,
|
||||
startTargetProcessing, stopTargetProcessing,
|
||||
stopAllLedTargets, stopAllKCTargets,
|
||||
startTargetOverlay, stopTargetOverlay, deleteTarget,
|
||||
cloneTarget, toggleLedPreview,
|
||||
disconnectAllLedPreviewWS,
|
||||
} from './features/targets.ts';
|
||||
|
||||
// Layer 5: color-strip sources
|
||||
import {
|
||||
showCSSEditor, closeCSSEditorModal, forceCSSEditorClose, saveCSSEditor, deleteColorStrip,
|
||||
onCSSTypeChange, onEffectTypeChange, onAnimationTypeChange, onCSSClockChange, onDaylightRealTimeChange,
|
||||
colorCycleAddColor, colorCycleRemoveColor,
|
||||
compositeAddLayer, compositeRemoveLayer,
|
||||
mappedAddZone, mappedRemoveZone,
|
||||
onAudioVizChange,
|
||||
applyGradientPreset,
|
||||
onGradientPresetChange,
|
||||
promptAndSaveGradientPreset,
|
||||
applyCustomGradientPreset,
|
||||
deleteAndRefreshGradientPreset,
|
||||
cloneColorStrip,
|
||||
toggleCSSOverlay,
|
||||
previewCSSFromEditor,
|
||||
copyEndpointUrl,
|
||||
onNotificationFilterModeChange,
|
||||
notificationAddAppColor, notificationRemoveAppColor,
|
||||
testNotification,
|
||||
showNotificationHistory, closeNotificationHistory, refreshNotificationHistory,
|
||||
testColorStrip, testCSPT, closeTestCssSourceModal, applyCssTestSettings, fireCssTestNotification, fireCssTestNotificationLayer,
|
||||
} from './features/color-strips.ts';
|
||||
|
||||
// Layer 5: audio sources
|
||||
import {
|
||||
showAudioSourceModal, closeAudioSourceModal, saveAudioSource,
|
||||
editAudioSource, cloneAudioSource, deleteAudioSource,
|
||||
testAudioSource, closeTestAudioSourceModal,
|
||||
refreshAudioDevices,
|
||||
} from './features/audio-sources.ts';
|
||||
|
||||
// Layer 5: value sources
|
||||
import {
|
||||
showValueSourceModal, closeValueSourceModal, saveValueSource,
|
||||
editValueSource, cloneValueSource, deleteValueSource, onValueSourceTypeChange,
|
||||
onDaylightVSRealTimeChange,
|
||||
addSchedulePoint,
|
||||
testValueSource, closeTestValueSourceModal,
|
||||
} from './features/value-sources.ts';
|
||||
|
||||
// Layer 5: calibration
|
||||
import {
|
||||
showCalibration, closeCalibrationModal, forceCloseCalibrationModal, saveCalibration,
|
||||
updateOffsetSkipLock, updateCalibrationPreview,
|
||||
setStartPosition, toggleEdgeInputs, toggleDirection, toggleTestEdge,
|
||||
showCSSCalibration, toggleCalibrationOverlay,
|
||||
} from './features/calibration.ts';
|
||||
import {
|
||||
showAdvancedCalibration, closeAdvancedCalibration, saveAdvancedCalibration,
|
||||
addCalibrationLine, removeCalibrationLine, selectCalibrationLine, moveCalibrationLine,
|
||||
updateCalibrationLine, resetCalibrationView,
|
||||
} from './features/advanced-calibration.ts';
|
||||
|
||||
// Layer 5.5: graph editor
|
||||
import {
|
||||
loadGraphEditor,
|
||||
toggleGraphLegend, toggleGraphMinimap, toggleGraphFilter, toggleGraphFilterTypes, toggleGraphHelp, graphUndo, graphRedo,
|
||||
graphFitAll, graphZoomIn, graphZoomOut, graphRelayout,
|
||||
graphToggleFullscreen, graphAddEntity,
|
||||
} from './features/graph-editor.ts';
|
||||
|
||||
// Layer 6: tabs, navigation, command palette, settings
|
||||
import { switchTab, initTabs, startAutoRefresh, handlePopState } from './features/tabs.ts';
|
||||
import { navigateToCard } from './core/navigation.ts';
|
||||
import { openCommandPalette, closeCommandPalette, initCommandPalette } from './core/command-palette.ts';
|
||||
import {
|
||||
openSettingsModal, closeSettingsModal, switchSettingsTab,
|
||||
downloadBackup, handleRestoreFileSelected,
|
||||
saveAutoBackupSettings, restoreSavedBackup, downloadSavedBackup, deleteSavedBackup,
|
||||
restartServer, saveMqttSettings,
|
||||
loadApiKeysList,
|
||||
downloadPartialExport, handlePartialImportFileSelected,
|
||||
connectLogViewer, disconnectLogViewer, clearLogViewer, applyLogFilter,
|
||||
openLogOverlay, closeLogOverlay,
|
||||
loadLogLevel, setLogLevel,
|
||||
saveExternalUrl, getBaseOrigin, loadExternalUrl,
|
||||
} from './features/settings.ts';
|
||||
|
||||
// ─── Register all HTML onclick / onchange / onfocus globals ───
|
||||
|
||||
Object.assign(window, {
|
||||
// core / state (for inline script)
|
||||
setApiKey,
|
||||
|
||||
// visual effects (called from inline <script>)
|
||||
_updateBgAnimAccent: updateBgAnimAccent,
|
||||
_updateBgAnimTheme: updateBgAnimTheme,
|
||||
_updateTabIndicator: updateTabIndicator,
|
||||
|
||||
// core / ui
|
||||
toggleHint,
|
||||
lockBody,
|
||||
unlockBody,
|
||||
closeLightbox,
|
||||
showToast,
|
||||
showConfirm,
|
||||
closeConfirmModal,
|
||||
openFullImageLightbox,
|
||||
showOverlaySpinner,
|
||||
hideOverlaySpinner,
|
||||
|
||||
// core / api + i18n
|
||||
t,
|
||||
configureApiKey,
|
||||
loadServerInfo,
|
||||
loadDisplays,
|
||||
changeLocale,
|
||||
|
||||
// displays
|
||||
openDisplayPicker,
|
||||
closeDisplayPicker,
|
||||
selectDisplay,
|
||||
formatDisplayLabel,
|
||||
|
||||
// tutorials
|
||||
startCalibrationTutorial,
|
||||
startDeviceTutorial,
|
||||
startGettingStartedTutorial,
|
||||
startDashboardTutorial,
|
||||
startTargetsTutorial,
|
||||
startSourcesTutorial,
|
||||
startAutomationsTutorial,
|
||||
closeTutorial,
|
||||
tutorialNext,
|
||||
tutorialPrev,
|
||||
|
||||
// devices
|
||||
showSettings,
|
||||
closeDeviceSettingsModal,
|
||||
forceCloseDeviceSettingsModal,
|
||||
saveDeviceSettings,
|
||||
updateBrightnessLabel,
|
||||
saveCardBrightness,
|
||||
turnOffDevice,
|
||||
pingDevice,
|
||||
removeDevice,
|
||||
loadDevices,
|
||||
updateSettingsBaudFpsHint,
|
||||
copyWsUrl,
|
||||
cloneDevice,
|
||||
|
||||
// dashboard
|
||||
loadDashboard,
|
||||
dashboardToggleAutomation,
|
||||
dashboardStartTarget,
|
||||
dashboardStopTarget,
|
||||
dashboardStopAll,
|
||||
dashboardPauseClock,
|
||||
dashboardResumeClock,
|
||||
dashboardResetClock,
|
||||
toggleDashboardSection,
|
||||
changeDashboardPollInterval,
|
||||
stopUptimeTimer,
|
||||
startPerfPolling,
|
||||
stopPerfPolling,
|
||||
|
||||
// streams / capture templates / PP templates
|
||||
loadPictureSources,
|
||||
switchStreamTab,
|
||||
showAddTemplateModal,
|
||||
editTemplate,
|
||||
closeTemplateModal,
|
||||
saveTemplate,
|
||||
deleteTemplate,
|
||||
showTestTemplateModal,
|
||||
closeTestTemplateModal,
|
||||
onEngineChange,
|
||||
runTemplateTest,
|
||||
updateCaptureDuration,
|
||||
showAddStreamModal,
|
||||
editStream,
|
||||
closeStreamModal,
|
||||
saveStream,
|
||||
deleteStream,
|
||||
onStreamTypeChange,
|
||||
onStreamDisplaySelected,
|
||||
onTestDisplaySelected,
|
||||
showTestStreamModal,
|
||||
closeTestStreamModal,
|
||||
updateStreamTestDuration,
|
||||
runStreamTest,
|
||||
showTestPPTemplateModal,
|
||||
closeTestPPTemplateModal,
|
||||
updatePPTestDuration,
|
||||
runPPTemplateTest,
|
||||
showAddPPTemplateModal,
|
||||
editPPTemplate,
|
||||
closePPTemplateModal,
|
||||
savePPTemplate,
|
||||
deletePPTemplate,
|
||||
addFilterFromSelect,
|
||||
toggleFilterExpand,
|
||||
removeFilter,
|
||||
moveFilter,
|
||||
updateFilterOption,
|
||||
renderModalFilterList,
|
||||
cloneStream,
|
||||
cloneCaptureTemplate,
|
||||
clonePPTemplate,
|
||||
showAddCSPTModal,
|
||||
editCSPT,
|
||||
closeCSPTModal,
|
||||
saveCSPT,
|
||||
deleteCSPT,
|
||||
cloneCSPT,
|
||||
csptAddFilterFromSelect,
|
||||
csptToggleFilterExpand,
|
||||
csptRemoveFilter,
|
||||
csptUpdateFilterOption,
|
||||
renderCSPTModalFilterList,
|
||||
showAddAudioTemplateModal,
|
||||
editAudioTemplate,
|
||||
closeAudioTemplateModal,
|
||||
saveAudioTemplate,
|
||||
deleteAudioTemplate,
|
||||
cloneAudioTemplate,
|
||||
onAudioEngineChange,
|
||||
showTestAudioTemplateModal,
|
||||
closeTestAudioTemplateModal,
|
||||
startAudioTemplateTest,
|
||||
|
||||
// kc-targets
|
||||
createKCTargetCard,
|
||||
testKCTarget,
|
||||
showKCEditor,
|
||||
closeKCEditorModal,
|
||||
forceCloseKCEditorModal,
|
||||
saveKCEditor,
|
||||
deleteKCTarget,
|
||||
disconnectAllKCWebSockets,
|
||||
updateKCBrightnessLabel,
|
||||
saveKCBrightness,
|
||||
cloneKCTarget,
|
||||
|
||||
// pattern-templates
|
||||
createPatternTemplateCard,
|
||||
showPatternTemplateEditor,
|
||||
closePatternTemplateModal,
|
||||
forceClosePatternTemplateModal,
|
||||
savePatternTemplate,
|
||||
deletePatternTemplate,
|
||||
renderPatternRectList,
|
||||
selectPatternRect,
|
||||
updatePatternRect,
|
||||
addPatternRect,
|
||||
deleteSelectedPatternRect,
|
||||
removePatternRect,
|
||||
capturePatternBackground,
|
||||
clonePatternTemplate,
|
||||
|
||||
// automations
|
||||
loadAutomations,
|
||||
openAutomationEditor,
|
||||
closeAutomationEditorModal,
|
||||
saveAutomationEditor,
|
||||
addAutomationCondition,
|
||||
toggleAutomationEnabled,
|
||||
cloneAutomation,
|
||||
deleteAutomation,
|
||||
copyWebhookUrl,
|
||||
|
||||
// scene presets
|
||||
openScenePresetCapture,
|
||||
editScenePreset,
|
||||
saveScenePreset,
|
||||
closeScenePresetEditor,
|
||||
activateScenePreset,
|
||||
recaptureScenePreset,
|
||||
cloneScenePreset,
|
||||
deleteScenePreset,
|
||||
addSceneTarget,
|
||||
removeSceneTarget,
|
||||
|
||||
// device-discovery
|
||||
onDeviceTypeChanged,
|
||||
updateBaudFpsHint,
|
||||
onSerialPortFocus,
|
||||
showAddDevice,
|
||||
closeAddDeviceModal,
|
||||
scanForDevices,
|
||||
handleAddDevice,
|
||||
|
||||
// targets
|
||||
loadTargetsTab,
|
||||
switchTargetSubTab,
|
||||
showTargetEditor,
|
||||
closeTargetEditorModal,
|
||||
forceCloseTargetEditorModal,
|
||||
saveTargetEditor,
|
||||
startTargetProcessing,
|
||||
stopTargetProcessing,
|
||||
stopAllLedTargets,
|
||||
stopAllKCTargets,
|
||||
startTargetOverlay,
|
||||
stopTargetOverlay,
|
||||
deleteTarget,
|
||||
cloneTarget,
|
||||
toggleLedPreview,
|
||||
disconnectAllLedPreviewWS,
|
||||
|
||||
// color-strip sources
|
||||
showCSSEditor,
|
||||
closeCSSEditorModal,
|
||||
forceCSSEditorClose,
|
||||
saveCSSEditor,
|
||||
deleteColorStrip,
|
||||
onCSSTypeChange,
|
||||
onEffectTypeChange,
|
||||
onCSSClockChange,
|
||||
onAnimationTypeChange,
|
||||
onDaylightRealTimeChange,
|
||||
colorCycleAddColor,
|
||||
colorCycleRemoveColor,
|
||||
compositeAddLayer,
|
||||
compositeRemoveLayer,
|
||||
mappedAddZone,
|
||||
mappedRemoveZone,
|
||||
onAudioVizChange,
|
||||
applyGradientPreset,
|
||||
onGradientPresetChange,
|
||||
promptAndSaveGradientPreset,
|
||||
applyCustomGradientPreset,
|
||||
deleteAndRefreshGradientPreset,
|
||||
cloneColorStrip,
|
||||
toggleCSSOverlay,
|
||||
previewCSSFromEditor,
|
||||
copyEndpointUrl,
|
||||
onNotificationFilterModeChange,
|
||||
notificationAddAppColor, notificationRemoveAppColor,
|
||||
testNotification,
|
||||
showNotificationHistory, closeNotificationHistory, refreshNotificationHistory,
|
||||
testColorStrip, testCSPT, closeTestCssSourceModal, applyCssTestSettings, fireCssTestNotification, fireCssTestNotificationLayer,
|
||||
|
||||
// audio sources
|
||||
showAudioSourceModal,
|
||||
closeAudioSourceModal,
|
||||
saveAudioSource,
|
||||
editAudioSource,
|
||||
cloneAudioSource,
|
||||
deleteAudioSource,
|
||||
testAudioSource,
|
||||
closeTestAudioSourceModal,
|
||||
refreshAudioDevices,
|
||||
|
||||
// value sources
|
||||
showValueSourceModal,
|
||||
closeValueSourceModal,
|
||||
saveValueSource,
|
||||
editValueSource,
|
||||
cloneValueSource,
|
||||
deleteValueSource,
|
||||
onValueSourceTypeChange,
|
||||
onDaylightVSRealTimeChange,
|
||||
addSchedulePoint,
|
||||
testValueSource,
|
||||
closeTestValueSourceModal,
|
||||
|
||||
// calibration
|
||||
showCalibration,
|
||||
closeCalibrationModal,
|
||||
forceCloseCalibrationModal,
|
||||
saveCalibration,
|
||||
updateOffsetSkipLock,
|
||||
updateCalibrationPreview,
|
||||
setStartPosition,
|
||||
toggleEdgeInputs,
|
||||
toggleDirection,
|
||||
toggleTestEdge,
|
||||
showCSSCalibration,
|
||||
toggleCalibrationOverlay,
|
||||
|
||||
// advanced calibration
|
||||
showAdvancedCalibration,
|
||||
closeAdvancedCalibration,
|
||||
saveAdvancedCalibration,
|
||||
addCalibrationLine,
|
||||
removeCalibrationLine,
|
||||
selectCalibrationLine,
|
||||
moveCalibrationLine,
|
||||
updateCalibrationLine,
|
||||
resetCalibrationView,
|
||||
|
||||
// graph editor
|
||||
loadGraphEditor,
|
||||
toggleGraphLegend,
|
||||
toggleGraphMinimap,
|
||||
toggleGraphFilter,
|
||||
toggleGraphFilterTypes,
|
||||
toggleGraphHelp,
|
||||
graphUndo,
|
||||
graphRedo,
|
||||
graphFitAll,
|
||||
graphZoomIn,
|
||||
graphZoomOut,
|
||||
graphRelayout,
|
||||
graphToggleFullscreen,
|
||||
graphAddEntity,
|
||||
|
||||
// tabs / navigation / command palette
|
||||
switchTab,
|
||||
startAutoRefresh,
|
||||
navigateToCard,
|
||||
openCommandPalette,
|
||||
closeCommandPalette,
|
||||
|
||||
// settings (tabs / backup / restore / auto-backup / MQTT / partial export-import / api keys / log level)
|
||||
openSettingsModal,
|
||||
closeSettingsModal,
|
||||
switchSettingsTab,
|
||||
downloadBackup,
|
||||
handleRestoreFileSelected,
|
||||
saveAutoBackupSettings,
|
||||
restoreSavedBackup,
|
||||
downloadSavedBackup,
|
||||
deleteSavedBackup,
|
||||
restartServer,
|
||||
saveMqttSettings,
|
||||
loadApiKeysList,
|
||||
downloadPartialExport,
|
||||
handlePartialImportFileSelected,
|
||||
connectLogViewer,
|
||||
disconnectLogViewer,
|
||||
clearLogViewer,
|
||||
applyLogFilter,
|
||||
openLogOverlay,
|
||||
closeLogOverlay,
|
||||
loadLogLevel,
|
||||
setLogLevel,
|
||||
saveExternalUrl,
|
||||
getBaseOrigin,
|
||||
});
|
||||
|
||||
// ─── Global keyboard shortcuts ───
|
||||
|
||||
document.addEventListener('keydown', (e) => {
|
||||
const tag = document.activeElement?.tagName;
|
||||
const inInput = tag === 'INPUT' || tag === 'TEXTAREA' || tag === 'SELECT';
|
||||
|
||||
// Command palette: Ctrl+K / Cmd+K (works even in inputs)
|
||||
if ((e.ctrlKey || e.metaKey) && e.key === 'k' && !e.altKey && !e.shiftKey) {
|
||||
e.preventDefault();
|
||||
openCommandPalette();
|
||||
return;
|
||||
}
|
||||
|
||||
// Tab shortcuts: Ctrl+1..4 (skip when typing in inputs)
|
||||
if (!inInput && e.ctrlKey && !e.altKey && !e.shiftKey && !e.metaKey) {
|
||||
const tabMap = { '1': 'dashboard', '2': 'automations', '3': 'targets', '4': 'streams', '5': 'graph' };
|
||||
const tab = tabMap[e.key];
|
||||
if (tab) {
|
||||
e.preventDefault();
|
||||
switchTab(tab);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (e.key === 'Escape') {
|
||||
// Close in order: log overlay > overlay lightboxes > modals via stack
|
||||
const logOverlay = document.getElementById('log-overlay');
|
||||
if (logOverlay && logOverlay.style.display !== 'none') {
|
||||
closeLogOverlay();
|
||||
} else if (document.getElementById('display-picker-lightbox').classList.contains('active')) {
|
||||
closeDisplayPicker(null as any);
|
||||
} else if (document.getElementById('image-lightbox').classList.contains('active')) {
|
||||
closeLightbox(null as any);
|
||||
} else {
|
||||
Modal.closeTopmost();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// ─── Browser back/forward via hash routing ───
|
||||
|
||||
window.addEventListener('popstate', handlePopState);
|
||||
|
||||
// ─── Cleanup on page unload ───
|
||||
|
||||
window.addEventListener('beforeunload', () => {
|
||||
if (refreshInterval) {
|
||||
clearInterval(refreshInterval);
|
||||
}
|
||||
stopConnectionMonitor();
|
||||
stopEventsWS();
|
||||
disconnectAllKCWebSockets();
|
||||
disconnectAllLedPreviewWS();
|
||||
});
|
||||
|
||||
// ─── Initialization ───
|
||||
|
||||
document.addEventListener('DOMContentLoaded', async () => {
|
||||
// Load API key from localStorage before anything that triggers API calls
|
||||
setApiKey(localStorage.getItem('wled_api_key'));
|
||||
|
||||
// Initialize locale (dispatches languageChanged which may trigger API calls)
|
||||
await initLocale();
|
||||
|
||||
// Load external URL setting early so getBaseOrigin() is available for card rendering
|
||||
loadExternalUrl();
|
||||
|
||||
// Restore active tab before showing content to avoid visible jump
|
||||
initTabs();
|
||||
|
||||
// Show content now that translations are loaded and tabs are set
|
||||
document.body.style.visibility = 'visible';
|
||||
|
||||
// Initialize visual effects
|
||||
initCardGlare();
|
||||
initBgAnim();
|
||||
initTabIndicator();
|
||||
updateBgAnimTheme(document.documentElement.getAttribute('data-theme') !== 'light');
|
||||
const accent = localStorage.getItem('accentColor') || '#4CAF50';
|
||||
updateBgAnimAccent(accent);
|
||||
|
||||
// Set CSS variable for sticky header height (header now includes tab bar)
|
||||
const headerEl = document.querySelector('header');
|
||||
if (headerEl) {
|
||||
const updateHeaderHeight = () => {
|
||||
const hh = headerEl.offsetHeight;
|
||||
document.documentElement.style.setProperty('--header-height', hh + 'px');
|
||||
document.documentElement.style.setProperty('--sticky-top', hh + 'px');
|
||||
};
|
||||
updateHeaderHeight();
|
||||
window.addEventListener('resize', updateHeaderHeight);
|
||||
}
|
||||
|
||||
// Scroll-to-top button visibility
|
||||
const scrollBtn = document.getElementById('scroll-to-top');
|
||||
if (scrollBtn) {
|
||||
window.addEventListener('scroll', () => {
|
||||
scrollBtn.classList.toggle('visible', window.scrollY > 300);
|
||||
}, { passive: true });
|
||||
}
|
||||
|
||||
// Initialize command palette
|
||||
initCommandPalette();
|
||||
|
||||
// Setup form handler
|
||||
document.getElementById('add-device-form').addEventListener('submit', handleAddDevice);
|
||||
|
||||
// Always monitor server connection (even before login)
|
||||
loadServerInfo();
|
||||
startConnectionMonitor();
|
||||
|
||||
// Show modal if no API key is stored
|
||||
if (!apiKey) {
|
||||
setTimeout(() => {
|
||||
if (typeof window.showApiKeyModal === 'function') {
|
||||
window.showApiKeyModal(null, true);
|
||||
}
|
||||
}, 100);
|
||||
return;
|
||||
}
|
||||
|
||||
// User is logged in, load data
|
||||
loadDisplays();
|
||||
loadTargetsTab();
|
||||
|
||||
// Start global events WebSocket and auto-refresh
|
||||
startEventsWS();
|
||||
startEntityEventListeners();
|
||||
startAutoRefresh();
|
||||
|
||||
// Show getting-started tutorial on first visit
|
||||
if (!localStorage.getItem('tour_completed')) {
|
||||
setTimeout(() => startGettingStartedTutorial(), 600);
|
||||
}
|
||||
});
|
||||
Reference in New Issue
Block a user