diff options
| author | Agando <roduccomm@gmail.com> | 2026-05-25 18:02:02 +0200 |
|---|---|---|
| committer | Agando <roduccomm@gmail.com> | 2026-05-25 18:02:02 +0200 |
| commit | 4baa484b84c5184286cac2743575251454a5ee2b (patch) | |
| tree | cb9002f93644f363d0f35831dc97e6492bbb0ed6 /modules/core/module-loader.js | |
auto: 2026-05-25_18:02 changes
Diffstat (limited to 'modules/core/module-loader.js')
| -rw-r--r-- | modules/core/module-loader.js | 178 |
1 files changed, 178 insertions, 0 deletions
diff --git a/modules/core/module-loader.js b/modules/core/module-loader.js new file mode 100644 index 0000000..850d627 --- /dev/null +++ b/modules/core/module-loader.js @@ -0,0 +1,178 @@ +(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=18'; + + 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']; + 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(); + } +})(); |