Split monolithic app.js into native ES modules
Replace the single 7034-line app.js with 17 ES module files organized into core/ (state, api, i18n, ui) and features/ (calibration, dashboard, device-discovery, devices, displays, kc-targets, pattern-templates, profiles, streams, tabs, targets, tutorials) with an app.js entry point that registers ~90 onclick globals on window. No bundler needed — FastAPI serves modules directly via <script type="module">. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
347
server/src/wled_controller/static/js/app.js
Normal file
347
server/src/wled_controller/static/js/app.js
Normal file
@@ -0,0 +1,347 @@
|
||||
/**
|
||||
* Entry point — imports all modules, registers globals, initializes app.
|
||||
*/
|
||||
|
||||
// Layer 0: state
|
||||
import { apiKey, setApiKey, refreshInterval } from './core/state.js';
|
||||
|
||||
// Layer 1: api, i18n
|
||||
import { loadServerInfo, loadDisplays, configureApiKey } from './core/api.js';
|
||||
import { t, initLocale, changeLocale } from './core/i18n.js';
|
||||
|
||||
// Layer 2: ui
|
||||
import {
|
||||
toggleHint, lockBody, unlockBody, closeLightbox,
|
||||
showToast, showConfirm, closeConfirmModal,
|
||||
openFullImageLightbox, showOverlaySpinner, hideOverlaySpinner,
|
||||
} from './core/ui.js';
|
||||
|
||||
// Layer 3: displays, tutorials
|
||||
import {
|
||||
openDisplayPicker, closeDisplayPicker, selectDisplay, formatDisplayLabel,
|
||||
} from './features/displays.js';
|
||||
import {
|
||||
startCalibrationTutorial, startDeviceTutorial,
|
||||
closeTutorial, tutorialNext, tutorialPrev,
|
||||
} from './features/tutorials.js';
|
||||
|
||||
// Layer 4: devices, dashboard, streams, kc-targets, pattern-templates, profiles
|
||||
import {
|
||||
showSettings, closeDeviceSettingsModal, forceCloseDeviceSettingsModal,
|
||||
saveDeviceSettings, updateBrightnessLabel, saveCardBrightness,
|
||||
saveDeviceStaticColor, clearDeviceStaticColor,
|
||||
toggleDevicePower, removeDevice, loadDevices,
|
||||
updateSettingsBaudFpsHint,
|
||||
} from './features/devices.js';
|
||||
import {
|
||||
loadDashboard, startDashboardWS, stopDashboardWS,
|
||||
dashboardToggleProfile, dashboardStartTarget, dashboardStopTarget, dashboardStopAll,
|
||||
} from './features/dashboard.js';
|
||||
import {
|
||||
loadPictureSources, switchStreamTab,
|
||||
showAddTemplateModal, editTemplate, closeTemplateModal, saveTemplate, deleteTemplate,
|
||||
showTestTemplateModal, closeTestTemplateModal, onEngineChange, runTemplateTest,
|
||||
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,
|
||||
} from './features/streams.js';
|
||||
import {
|
||||
createKCTargetCard, testKCTarget, toggleKCTestAutoRefresh,
|
||||
showKCEditor, closeKCEditorModal, forceCloseKCEditorModal, saveKCEditor,
|
||||
deleteKCTarget, disconnectAllKCWebSockets,
|
||||
} from './features/kc-targets.js';
|
||||
import {
|
||||
createPatternTemplateCard,
|
||||
showPatternTemplateEditor, closePatternTemplateModal, forceClosePatternTemplateModal,
|
||||
savePatternTemplate, deletePatternTemplate,
|
||||
renderPatternRectList, selectPatternRect, updatePatternRect,
|
||||
addPatternRect, deleteSelectedPatternRect, removePatternRect,
|
||||
capturePatternBackground,
|
||||
} from './features/pattern-templates.js';
|
||||
import {
|
||||
loadProfiles, openProfileEditor, closeProfileEditorModal,
|
||||
saveProfileEditor, addProfileCondition,
|
||||
toggleProfileEnabled, deleteProfile,
|
||||
} from './features/profiles.js';
|
||||
|
||||
// Layer 5: device-discovery, targets
|
||||
import {
|
||||
onDeviceTypeChanged, updateBaudFpsHint, onSerialPortFocus,
|
||||
showAddDevice, closeAddDeviceModal, scanForDevices, handleAddDevice,
|
||||
} from './features/device-discovery.js';
|
||||
import {
|
||||
loadTargetsTab, loadTargets, switchTargetSubTab,
|
||||
showTargetEditor, closeTargetEditorModal, forceCloseTargetEditorModal, saveTargetEditor,
|
||||
startTargetProcessing, stopTargetProcessing,
|
||||
startTargetOverlay, stopTargetOverlay, deleteTarget,
|
||||
} from './features/targets.js';
|
||||
|
||||
// Layer 5: calibration
|
||||
import {
|
||||
showCalibration, closeCalibrationModal, forceCloseCalibrationModal, saveCalibration,
|
||||
updateOffsetSkipLock, updateCalibrationPreview,
|
||||
setStartPosition, toggleEdgeInputs, toggleDirection, toggleTestEdge,
|
||||
} from './features/calibration.js';
|
||||
|
||||
// Layer 6: tabs
|
||||
import { switchTab, initTabs, startAutoRefresh } from './features/tabs.js';
|
||||
|
||||
// ─── Register all HTML onclick / onchange / onfocus globals ───
|
||||
|
||||
Object.assign(window, {
|
||||
// core / state (for inline script)
|
||||
setApiKey,
|
||||
|
||||
// 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,
|
||||
closeTutorial,
|
||||
tutorialNext,
|
||||
tutorialPrev,
|
||||
|
||||
// devices
|
||||
showSettings,
|
||||
closeDeviceSettingsModal,
|
||||
forceCloseDeviceSettingsModal,
|
||||
saveDeviceSettings,
|
||||
updateBrightnessLabel,
|
||||
saveCardBrightness,
|
||||
saveDeviceStaticColor,
|
||||
clearDeviceStaticColor,
|
||||
toggleDevicePower,
|
||||
removeDevice,
|
||||
loadDevices,
|
||||
updateSettingsBaudFpsHint,
|
||||
|
||||
// dashboard
|
||||
loadDashboard,
|
||||
startDashboardWS,
|
||||
stopDashboardWS,
|
||||
dashboardToggleProfile,
|
||||
dashboardStartTarget,
|
||||
dashboardStopTarget,
|
||||
dashboardStopAll,
|
||||
|
||||
// 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,
|
||||
|
||||
// kc-targets
|
||||
createKCTargetCard,
|
||||
testKCTarget,
|
||||
toggleKCTestAutoRefresh,
|
||||
showKCEditor,
|
||||
closeKCEditorModal,
|
||||
forceCloseKCEditorModal,
|
||||
saveKCEditor,
|
||||
deleteKCTarget,
|
||||
disconnectAllKCWebSockets,
|
||||
|
||||
// pattern-templates
|
||||
createPatternTemplateCard,
|
||||
showPatternTemplateEditor,
|
||||
closePatternTemplateModal,
|
||||
forceClosePatternTemplateModal,
|
||||
savePatternTemplate,
|
||||
deletePatternTemplate,
|
||||
renderPatternRectList,
|
||||
selectPatternRect,
|
||||
updatePatternRect,
|
||||
addPatternRect,
|
||||
deleteSelectedPatternRect,
|
||||
removePatternRect,
|
||||
capturePatternBackground,
|
||||
|
||||
// profiles
|
||||
loadProfiles,
|
||||
openProfileEditor,
|
||||
closeProfileEditorModal,
|
||||
saveProfileEditor,
|
||||
addProfileCondition,
|
||||
toggleProfileEnabled,
|
||||
deleteProfile,
|
||||
|
||||
// device-discovery
|
||||
onDeviceTypeChanged,
|
||||
updateBaudFpsHint,
|
||||
onSerialPortFocus,
|
||||
showAddDevice,
|
||||
closeAddDeviceModal,
|
||||
scanForDevices,
|
||||
handleAddDevice,
|
||||
|
||||
// targets
|
||||
loadTargetsTab,
|
||||
loadTargets,
|
||||
switchTargetSubTab,
|
||||
showTargetEditor,
|
||||
closeTargetEditorModal,
|
||||
forceCloseTargetEditorModal,
|
||||
saveTargetEditor,
|
||||
startTargetProcessing,
|
||||
stopTargetProcessing,
|
||||
startTargetOverlay,
|
||||
stopTargetOverlay,
|
||||
deleteTarget,
|
||||
|
||||
// calibration
|
||||
showCalibration,
|
||||
closeCalibrationModal,
|
||||
forceCloseCalibrationModal,
|
||||
saveCalibration,
|
||||
updateOffsetSkipLock,
|
||||
updateCalibrationPreview,
|
||||
setStartPosition,
|
||||
toggleEdgeInputs,
|
||||
toggleDirection,
|
||||
toggleTestEdge,
|
||||
|
||||
// tabs
|
||||
switchTab,
|
||||
startAutoRefresh,
|
||||
});
|
||||
|
||||
// ─── Global Escape key handler ───
|
||||
|
||||
document.addEventListener('keydown', (e) => {
|
||||
if (e.key === 'Escape') {
|
||||
// Close in order: overlay lightboxes first, then modals
|
||||
if (document.getElementById('display-picker-lightbox').classList.contains('active')) {
|
||||
closeDisplayPicker();
|
||||
} else if (document.getElementById('image-lightbox').classList.contains('active')) {
|
||||
closeLightbox();
|
||||
} else {
|
||||
const modals = [
|
||||
{ id: 'test-pp-template-modal', close: closeTestPPTemplateModal },
|
||||
{ id: 'test-stream-modal', close: closeTestStreamModal },
|
||||
{ id: 'test-template-modal', close: closeTestTemplateModal },
|
||||
{ id: 'stream-modal', close: closeStreamModal },
|
||||
{ id: 'pp-template-modal', close: closePPTemplateModal },
|
||||
{ id: 'template-modal', close: closeTemplateModal },
|
||||
{ id: 'device-settings-modal', close: forceCloseDeviceSettingsModal },
|
||||
{ id: 'calibration-modal', close: forceCloseCalibrationModal },
|
||||
{ id: 'target-editor-modal', close: forceCloseTargetEditorModal },
|
||||
{ id: 'add-device-modal', close: closeAddDeviceModal },
|
||||
];
|
||||
for (const m of modals) {
|
||||
const el = document.getElementById(m.id);
|
||||
if (el && el.style.display === 'flex') {
|
||||
m.close();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// ─── Cleanup on page unload ───
|
||||
|
||||
window.addEventListener('beforeunload', () => {
|
||||
if (refreshInterval) {
|
||||
clearInterval(refreshInterval);
|
||||
}
|
||||
disconnectAllKCWebSockets();
|
||||
});
|
||||
|
||||
// ─── Initialization ───
|
||||
|
||||
document.addEventListener('DOMContentLoaded', async () => {
|
||||
// Initialize locale first
|
||||
await initLocale();
|
||||
|
||||
// Load API key from localStorage
|
||||
setApiKey(localStorage.getItem('wled_api_key'));
|
||||
|
||||
// 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';
|
||||
|
||||
// Setup form handler
|
||||
document.getElementById('add-device-form').addEventListener('submit', handleAddDevice);
|
||||
|
||||
// Show modal if no API key is stored
|
||||
if (!apiKey) {
|
||||
setTimeout(() => {
|
||||
if (typeof window.showApiKeyModal === 'function') {
|
||||
window.showApiKeyModal('Welcome! Please login with your API key to get started.', true);
|
||||
}
|
||||
}, 100);
|
||||
return;
|
||||
}
|
||||
|
||||
// User is logged in, load data
|
||||
loadServerInfo();
|
||||
loadDisplays();
|
||||
loadTargetsTab();
|
||||
|
||||
// Start auto-refresh
|
||||
startAutoRefresh();
|
||||
});
|
||||
Reference in New Issue
Block a user