aboutsummaryrefslogtreecommitdiff
path: root/modules/core/terminal.js
diff options
context:
space:
mode:
authorAgando <roduccomm@gmail.com>2026-05-30 06:00:47 +0200
committerAgando <roduccomm@gmail.com>2026-05-30 06:00:47 +0200
commite3fb2d434c4f7317944b7c77781599528d766a39 (patch)
tree8f9f9c94b19d46f42f8086adb6b68ee07dfce5c6 /modules/core/terminal.js
parentd570f86177c514ae888fb63fc0bb0da7dd4d5097 (diff)
auto: 2026-05-30_06:00 changes
Diffstat (limited to 'modules/core/terminal.js')
-rw-r--r--modules/core/terminal.js65
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];