diff options
| author | Agando <roduccomm@gmail.com> | 2026-05-30 06:00:47 +0200 |
|---|---|---|
| committer | Agando <roduccomm@gmail.com> | 2026-05-30 06:00:47 +0200 |
| commit | e3fb2d434c4f7317944b7c77781599528d766a39 (patch) | |
| tree | 8f9f9c94b19d46f42f8086adb6b68ee07dfce5c6 /modules/core/terminal.js | |
| parent | d570f86177c514ae888fb63fc0bb0da7dd4d5097 (diff) | |
auto: 2026-05-30_06:00 changes
Diffstat (limited to 'modules/core/terminal.js')
| -rw-r--r-- | modules/core/terminal.js | 65 |
1 files changed, 62 insertions, 3 deletions
diff --git a/modules/core/terminal.js b/modules/core/terminal.js index 5e6db6a..de2f4a7 100644 --- a/modules/core/terminal.js +++ b/modules/core/terminal.js @@ -211,6 +211,7 @@ this.suggestions = []; this._isCdAutocomplete = false; this._autocompletePrefix = ''; + this._welcomeShown = false; this.initEventListeners(); this.updateNetworkStatus(); this.loadBuiltInModules(); @@ -226,6 +227,8 @@ soundBtn.textContent = localStorage.getItem('webx11_config_ai_sound') === 'off' ? '🔇 Muted' : '🔊 Sound'; } setTimeout(() => this.inputField.focus(), 100); + // First-run welcome check — fires after ext modules are loaded + setTimeout(() => this._checkFirstRun(), 2000); } initEventListeners() { this.inputField.addEventListener('keydown', (e) => { @@ -943,7 +946,7 @@ catch (e) { console.warn(`[SafeRegister] Skipped ${label}: ${e.message}`); } } async _loadExtModules(filterKeys) { - const autoLoadKeys = filterKeys || ['calc', 'voice', 'cron', 'theme', 'wpm', 'mindmap', 'webdav', 'skill-pack-core', 'editor', 'diff', 'alias', 'finder', 'filemgr', 'cryptogzip', 'shell']; + const autoLoadKeys = filterKeys || ['calc', 'voice', 'cron', 'theme', 'wpm', 'mindmap', 'webdav', 'skill-pack-core', 'editor', 'diff', 'alias', 'finder', 'filemgr', 'cryptogzip', 'shell', 'tutorial']; const cacheVer = localStorage.getItem('webx11_cache_ver') || '2026-05-27-14'; try { const res = await fetch('packages.json?v=' + cacheVer); @@ -1048,11 +1051,34 @@ for (const modName in this.moduleManager.modules) { const mod = this.moduleManager.modules[modName]; if (mod.commands && mod.commands[cmd]) { - try { await mod.commands[cmd](args); return; } + try { await mod.commands[cmd](args); + // Notify tutorial module for quest tracking + if (this.tutorialModule && cmd !== 'tutorial') this.tutorialModule.onCommandExecuted(cmd, args); + return; } catch (err) { this.print(`Error in ${modName}.${cmd}: ${err.message}`, 'error'); return; } } } - // No AI routing here — security manager handles that via checkAndRun + // Enhanced: check fuzzy suggestions before showing "not found" + const suggestion = this.security.suggest(cmd); + if (suggestion && suggestion.dist <= 2) { + this.print(`Command not found: ${cmd}`, 'error'); + this.print(`Did you mean: ${suggestion.cmd}? (yes) (no)`, 'info'); + // Render clickable yes/no + const out = this.outputContainer; + if (out) { + const yesSpan = document.createElement('span'); + yesSpan.textContent = ' [yes] '; + yesSpan.style.cssText = 'color:var(--success-color);cursor:pointer;text-decoration:underline;margin-right:12px'; + yesSpan.onclick = () => this.executeCommand(suggestion.cmd + (args.length ? ' ' + args.join(' ') : '')); + const noSpan = document.createElement('span'); + noSpan.textContent = ' [no] '; + noSpan.style.cssText = 'color:var(--error-color);cursor:pointer;text-decoration:underline'; + noSpan.onclick = () => { /* do nothing — user declined */ }; + const line = out.lastElementChild; + if (line) { line.appendChild(yesSpan); line.appendChild(noSpan); } + } + return; + } this.print(`Command not found: ${cmd}`, 'error'); } openFileBrowser() { @@ -1675,6 +1701,39 @@ } } catch (e) {} } + // ── First-run welcome ── + _checkFirstRun() { + if (localStorage.getItem('webx11_tutorial_complete') || localStorage.getItem('webx11_tutorial_skipped')) return; + // Check if tutorial module is loaded — retry if ext modules not ready yet + const tutMod = this.moduleManager.modules['TutorialModule']; + if (!tutMod) { + // Ext modules may still be loading; retry in 2s + if (!this._welcomeRetry) { + this._welcomeRetry = true; + setTimeout(() => this._checkFirstRun(), 2000); + } + return; + } + this.tutorialModule = tutMod; + // Only show welcome if user hasn't explicitly dismissed it + if (!localStorage.getItem('webx11_welcome_dismissed')) { + this.showWelcomeModal(); + } + } + showWelcomeModal() { + const modal = document.getElementById('welcome-modal'); + if (modal) { + modal.style.display = 'flex'; + this._welcomeShown = true; + } + } + dismissWelcome() { + const modal = document.getElementById('welcome-modal'); + if (modal) modal.style.display = 'none'; + localStorage.setItem('webx11_welcome_dismissed', '1'); + // Also mark tutorial as skipped so it doesn't reappear + if (this.tutorialModule) this.tutorialModule.skipTutorial(); + } log(level, source, message) { const d = new Date(); const ts = d.toISOString().split('T')[1].split('.')[0]; |