diff options
Diffstat (limited to 'modules/core/module-manager.js')
| -rw-r--r-- | modules/core/module-manager.js | 138 |
1 files changed, 138 insertions, 0 deletions
diff --git a/modules/core/module-manager.js b/modules/core/module-manager.js new file mode 100644 index 0000000..8e9e911 --- /dev/null +++ b/modules/core/module-manager.js @@ -0,0 +1,138 @@ +// Extracted from newOS4 index.html — ModuleManager + class ModuleManager { + constructor(terminal) { + this.terminal = terminal; + this.modules = {}; + this.moduleRegistry = []; + this.loadFromStorage(); + } + loadFromStorage() { + try { + const saved = localStorage.getItem('webx11_modules'); + if (saved) { + this.moduleRegistry = JSON.parse(saved); + } + } catch (e) { console.error('Module load error:', e); } + } + saveToStorage() { + try { + localStorage.setItem('webx11_modules', JSON.stringify(this.moduleRegistry)); + } catch (e) { console.error('Module save error:', e); } + } + async loadModuleFromURL(url) { + try { + this.terminal.print(`Loading module from ${url}...`, 'info'); + const response = await fetch(url); + if (!response.ok) throw new Error(`HTTP ${response.status}`); + const code = await response.text(); + const ModuleClass = this.createModuleClass(code, url); + if (ModuleClass) { + const instance = new ModuleClass(); + this.registerModule(instance, url); + if (instance.init) { + await instance.init(); + } + this.terminal.print(`Module loaded successfully from ${url}`, 'success'); + return true; + } + } catch (error) { + this.terminal.print(`Failed to load module: ${error.message}`, 'error'); + return false; + } + } + createModuleClass(code, url) { + try { + const classMatch = code.match(/class\s+(\w+)\s+(extends\s+\S+)?\s*\{/); + if (classMatch) { + const className = classMatch[1]; + const mod = new Function('return ' + code + '\nreturn ' + className)(); + return mod; + } + throw new Error('No valid module class found'); + } catch (e) { + this.terminal.print(`Module parse error: ${e.message}`, 'error'); + return null; + } + } + registerModule(instance, url = null) { + const name = instance.constructor.name; + this.modules[name] = instance; + instance.terminal = this.terminal; + if (instance.registerCommands) { instance.registerCommands(); } + // Only persist modules loaded from absolute URLs (not boot-time ext modules) + if (url && url.startsWith('http') && !this.moduleRegistry.find(m => m.name === name)) { + this.moduleRegistry.push({ + name: name, url: url, + loaded: new Date().toISOString(), enabled: true + }); + this.saveToStorage(); + } + console.log(`Module registered: ${name}`); + } + unregisterModule(name) { + if (this.modules[name]) { + delete this.modules[name]; + this.moduleRegistry = this.moduleRegistry.filter(m => m.name !== name); + this.saveToStorage(); + return true; + } + return false; + } + getModuleList() { + const allNames = new Set([...this.moduleRegistry.map(m => m.name), ...Object.keys(this.modules)]); + return Array.from(allNames).map(name => ({ + name, + url: (this.moduleRegistry.find(m => m.name === name) || {}).url || null, + loaded: this.modules[name] !== undefined + })); + } + clearAll() { + this.modules = {}; + this.moduleRegistry = []; + this.saveToStorage(); + } + getModuleRequirements(name) { + const mod = this.modules[name]; + if (mod && typeof mod.requirements === 'function') { + return mod.requirements(); + } + return null; + } + checkAllRequirements() { + const results = {}; + for (const modName in this.modules) { + const mod = this.modules[modName]; + if (typeof mod.requirements === 'function') { + results[modName] = mod.requirements(); + } + } + return results; + } + getAllCommands() { + const commands = {}; + for (const modName in this.modules) { + const mod = this.modules[modName]; + if (mod.commands) { + for (const cmd in mod.commands) { + commands[cmd] = modName; + } + } + } + return commands; + } + getAllHelp() { + const help = {}; + for (const modName in this.modules) { + const mod = this.modules[modName]; + if (typeof mod.getHelpInfo === 'function') { + const modHelp = mod.getHelpInfo(); + if (modHelp) Object.assign(help, modHelp); + } else if (mod.commands) { + for (const cmd in mod.commands) { + if (!help[cmd]) help[cmd] = cmd + ' — (no description)'; + } + } + } + return help; + } + } |