(function() { 'use strict'; const BASE = 'modules/core/'; const FOUNDATION = [ 'virtual-fs.js', 'module-manager.js', 'ollama-manager.js', 'terminal.js' ]; const BUILTIN = [ 'module-system.js', 'module-fs.js', 'module-modmgr.js', 'module-ollama.js', 'module-my.js', 'module-searxng.js', 'module-servermon.js', 'module-ollamamodels.js', 'module-config.js', 'module-simplegui.js', 'module-io.js', 'module-dynamicprompts.js', 'module-aiassist.js', 'module-aiasm.js', 'module-smartctx.js', 'module-duckduckgo.js', 'module-fetcher.js', 'module-skills.js', 'secret-vault.js', 'module-chatstudio.js' ]; function getSelectedModules() { try { const saved = localStorage.getItem('webx11_startup_modules'); if (!saved) return null; const parsed = JSON.parse(saved); if (Array.isArray(parsed)) return parsed; } catch (e) {} return null; } function isSelected(name, selected) { if (!selected) return true; return selected.includes(name); } function loadScript(url, filename) { return new Promise((resolve) => { const existing = document.querySelector(`script[src="${url}"]`); if (existing) { resolve(); return; } const el = document.createElement('script'); el.src = url + (url.includes('?') ? '&t=' : '?t=') + Date.now(); el.onload = () => resolve(); el.onerror = () => { console.warn(`[ModuleLoader] Failed to load: ${filename || url} — continuing`); // Log to terminal if available if (typeof window !== 'undefined' && window.webx11 && window.webx11.log) { window.webx11.log('WARN', 'Loader', 'Failed to load: ' + (filename || url)); } resolve(); }; document.body.appendChild(el); }); } function setSplash(phase, current, total, filename) { const fill = document.getElementById('splash-progress-fill'); const phaseEl = document.getElementById('splash-phase'); const fileEl = document.getElementById('splash-filename'); if (fill && total > 0) { fill.style.width = Math.round((current / total) * 100) + '%'; } if (phaseEl) phaseEl.textContent = phase; if (fileEl) fileEl.textContent = filename || ''; } function hideSplash() { const overlay = document.getElementById('splash-overlay'); if (overlay) { overlay.classList.add('hidden'); setTimeout(() => { overlay.style.display = 'none'; }, 350); } } function showSplashError(msg) { const el = document.getElementById('splash-error'); const fill = document.getElementById('splash-progress-fill'); if (el) { el.textContent = msg; el.classList.add('show'); } if (fill) fill.style.background = 'var(--error-color)'; } function reloadWithSelection() { location.reload(); } window.__moduleLoaderReload = reloadWithSelection; const CACHE = '?v=' + (localStorage.getItem('webx11_cache_ver') || '2026-05-27-14'); if (!localStorage.getItem('webx11_cache_ver')) { localStorage.setItem('webx11_cache_ver', '2026-05-27-14'); } async function init() { try { const selected = getSelectedModules(); const total = FOUNDATION.length + BUILTIN.length; let count = 0; // Phase 1: Foundation (always load) setSplash('Loading foundation layer (1/3)', 0, total, ''); for (const mod of FOUNDATION) { count++; setSplash('Loading foundation layer (1/3)', count, total, mod); await loadScript(BASE + mod + CACHE, mod); } // Phase 2: Built-in modules (filtered) setSplash('Loading built-in modules (2/3)', count, total, ''); for (const mod of BUILTIN) { count++; if (!isSelected(mod, selected)) { console.log(`[ModuleLoader] Skipped: ${mod} (not in startup selection)`); setSplash('Loading built-in modules (2/3)', count, total, '⏭ ' + mod); await new Promise(r => setTimeout(r, 20)); continue; } setSplash('Loading built-in modules (2/3)', count, total, mod); await loadScript(BASE + mod + CACHE, mod); } // Phase 3: Initialize terminal setSplash('Initializing terminal (3/3)', total, total, ''); const webx11 = new WebX11Terminal(); window.webx11 = webx11; // Auto-connect to Ollama after init setTimeout(async () => { try { if (webx11.ollamaManager && !webx11.ollamaManager.connected) { webx11.log('INFO', 'System', 'WebX11 v4.2 started'); webx11.log('INFO', 'System', 'Default model: ' + webx11.ollamaManager.currentModel); await webx11.ollamaManager.connect(); if (typeof webx11.updateOllamaStatus === 'function') { webx11.updateOllamaStatus(); } webx11.print('🤖 Default model: ' + webx11.ollamaManager.currentModel, 'info'); } } catch (e) { // Silently ignore — user can manually connect } }, 500); // Phase 4: Ext modules (filtered) if (typeof webx11._loadExtModules === 'function') { const extKeys = ['calc', 'voice', 'cron', 'theme', 'wpm', 'mindmap', 'webdav', 'skill-pack-core', 'editor', 'diff', 'alias', 'finder', 'filemgr', 'cryptogzip']; const extSelected = selected ? extKeys.filter(k => isSelected(k + '.js', selected)) : extKeys; if (extSelected.length > 0) { webx11._loadExtModules(extSelected); } } hideSplash(); console.log('[ModuleLoader] All modules loaded successfully'); } catch (err) { console.error('[ModuleLoader] Fatal error:', err); showSplashError('Loading failed — ' + err.message); } } if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', init); } else { init(); } })();