// App Manager - Dynamic app loading with beautiful styling class AppManager { constructor() { this.cache = new Map(); } getRandomLoadingMessage() { const messages = [ "Preparing your application settings...", "Gathering application information...", "Loading application configuration...", "Setting up your app management panel...", "Loading the perfect app settings...", "Crafting your application experience...", "Preparing your app control panel...", "Loading application details...", "Setting up your app workspace...", "Configuring your application environment..." ]; return messages[Math.floor(Math.random() * messages.length)]; } async loadApp(appName) { //console.log(`AppManager: Loading ${appName} app...`); // Check cache first if (this.cache.has(appName)) { //console.log(`AppManager: Using cached ${appName} app`); return this.cache.get(appName); } try { // Load app data from apps.json const response = await fetch('/data/apps/generated/apps.json', { cache: 'no-store' }); if (!response.ok) { throw new Error(`Failed to load apps.json: ${response.status}`); } const appsData = await response.json(); // Try multiple ways to find the app let app = appsData.apps.find(app => app.name.toLowerCase().includes(appName.toLowerCase()) || app.command.toLowerCase().includes(appName.toLowerCase()) || app.name === appName || app.name.toLowerCase() === appName.toLowerCase() ); if (!app) { // Try case-insensitive exact match app = appsData.apps.find(app => app.name.toLowerCase() === appName.toLowerCase() || app.command.toLowerCase().includes(appName.toLowerCase()) ); } if (!app) { //console.log(`Available apps:`, appsData.apps.map(a => ({ name: a.name, command: a.command }))); throw new Error(`App ${appName} not found`); } //console.log(`AppManager: Loaded ${appName} app:`, app); // Cache the result this.cache.set(appName, app); return app; } catch (error) { console.error(`AppManager: Error loading ${appName} app:`, error); return null; } } async renderApp(appName) { //console.log(`AppManager: Rendering ${appName} app...`); const configSection = document.getElementById('config-section'); if (!configSection) { console.error('AppManager: config-section element not found'); return; } // Show loading with enhanced visual configSection.innerHTML = `
Loading application...
${this.getRandomLoadingMessage()}
`; // Update loading bar if available if (typeof router !== 'undefined' && router.updateProgress) { router.updateProgress(60); } try { // Load app data const app = await this.loadApp(appName); // Update loading bar if (typeof router !== 'undefined' && router.updateProgress) { router.updateProgress(70); } if (!app) { configSection.innerHTML = '
Application not found
'; return; } // App config comes from apps.json (window.apps), not a separate // per-app JSON. Pass null — the renderer's config section is gated // on appConfig?.config keys so it just skips that section. if (typeof router !== 'undefined' && router.updateProgress) { router.updateProgress(80); } await this.renderWithOriginalStyling(appName, app, null); // Final progress update if (typeof router !== 'undefined' && router.updateProgress) { router.updateProgress(80); } //console.log(`AppManager: Successfully rendered ${appName} app`); } catch (error) { console.error(`AppManager: Error rendering ${appName} app:`, error); configSection.innerHTML = `
Failed to load ${appName} application: ${error.message}
`; } } async renderWithOriginalStyling(appName, app, appConfig) { const configSection = document.getElementById('config-section'); // Render using the original app-config system let formHTML = `

${app.displayName || app.name} Application

${app.description || 'Manage settings and configuration for ' + (app.displayName || app.name)}

`; // App information section formHTML += `

Application Information

Basic information about this application

`; // Configuration section if available if (appConfig && appConfig.config && Object.keys(appConfig.config).length > 0) { // Use ConfigShared if available for beautiful rendering if (typeof ConfigShared !== 'undefined') { const groupedConfigs = ConfigShared.groupConfigKeys(appConfig.config); const categoryOrder = ConfigShared.extractCategoryOrder(appConfig.config); for (const category of categoryOrder) { const keys = groupedConfigs[category]; if (keys && keys.length > 0 && category !== 'Hidden/Unused Options') { const displayCategory = ConfigShared.formatCategoryName(category); const categoryDescription = await ConfigShared.getCategoryDescription(category); formHTML += `

${displayCategory}

${categoryDescription}

${ConfigShared.generateFieldsForCategory(keys, category, appConfig.config, (fieldId, key, value, title, description, options, config) => ConfigShared.generateField(fieldId, key, value, title, description, options, config))}
`; } } } else { // Fallback simple rendering formHTML += `

Configuration

Application-specific settings

`; } } else { // No configuration available formHTML += `

Configuration

No specific configuration available for this application

`; } formHTML += `
`; configSection.innerHTML = formHTML; } static async saveAppConfig(appName) { //console.log(`AppManager: Saving ${appName} config...`); const form = document.getElementById(`app-form-${appName}`); if (!form) { console.error('AppManager: Form not found'); return; } // Show success message if (typeof ConfigShared !== 'undefined' && ConfigShared.showNotification) { ConfigShared.showNotification('Application configuration saved successfully!', 'success'); } else { // Fallback message const message = document.createElement('div'); message.className = 'config-message success'; message.textContent = 'Application configuration saved successfully!'; const actionsDiv = form.parentElement.querySelector('.config-actions'); actionsDiv.insertBefore(message, actionsDiv.firstChild); // Remove message after 3 seconds setTimeout(() => { if (message.parentNode) { message.parentNode.removeChild(message); } }, 3000); } } static async resetAppConfig(appName) { //console.log(`AppManager: Resetting ${appName} config...`); if (confirm('Are you sure you want to reset all settings to their default values?')) { // Reload the page to reset window.location.reload(); } } async loadScript(src) { // Check if script is already loaded const scriptId = src.replace(/[^a-zA-Z0-9]/g, '_'); if (document.getElementById(scriptId)) { //console.log(`Script ${src} already loaded, skipping`); return; } //console.log(`Loading script: ${src}`); return new Promise((resolve, reject) => { const script = document.createElement('script'); script.src = src; script.id = scriptId; script.onload = () => { //console.log(`Script loaded successfully: ${src}`); resolve(); }; script.onerror = (error) => { console.error(`Script failed to load: ${src}`, error); reject(new Error(`Failed to load script: ${src}`)); }; document.head.appendChild(script); }); } } // Global instance window.appManager = new AppManager();