diff options
Diffstat (limited to 'modules/core/terminal.js')
| -rw-r--r-- | modules/core/terminal.js | 58 |
1 files changed, 44 insertions, 14 deletions
diff --git a/modules/core/terminal.js b/modules/core/terminal.js index d53ab70..5008842 100644 --- a/modules/core/terminal.js +++ b/modules/core/terminal.js @@ -54,13 +54,12 @@ let curr = new Uint16Array(n + 1); for (let j = 0; j <= n; j++) prev[j] = j; for (let i = 1; i <= m; i++) { - let next = new Uint16Array(n + 1); - next[0] = i; + curr[0] = i; for (let j = 1; j <= n; j++) { const cost = a[i - 1] === b[j - 1] ? 0 : 1; - next[j] = Math.min(prev[j] + 1, curr[j - 1] + 1, prev[j - 1] + cost); + curr[j] = Math.min(prev[j] + 1, curr[j - 1] + 1, prev[j - 1] + cost); } - [prev, curr] = [curr, next]; + [prev, curr] = [curr, prev]; } return prev[n]; } @@ -98,8 +97,11 @@ if (['help','security','security-level'].includes(cmd)) { return this._dispatch(cmd, args); } - // Unknown command handling + // Unknown command handling — skip at level 0 (permissive) if (unknown && !this.whitelist.has(cmd)) { + if (this.level === 0) { + return this._dispatch(cmd, args); + } this.blockedCount++; const sug = this.suggest(cmd); if (sug) { @@ -107,7 +109,7 @@ } else { this.terminal.print(`Security: Unknown command "${cmd}" — blocked. Use 'security-level 0' to disable.`, 'warning'); } - // At levels 0-1: suggest/fix but still route to AI (terminal is also a chat interface) + // At level 1: suggest/fix but still route to AI (terminal is also a chat interface) // At level 2+: block entirely (paranoid) if (this.level >= 2) { this.terminal.print(`Security: Unknown command "${cmd}" blocked (level ${this.level}).`, 'error'); @@ -272,6 +274,9 @@ this.selectedSuggestion = (this.selectedSuggestion + 1) % this.suggestions.length; this.updateAutocompleteDisplay(); } + } else if (e.ctrlKey && (e.key === 'k' || e.key === 'K')) { + e.preventDefault(); + this.toggleCmdCards(); } else if (e.key === 'Escape') { if (this._aiBusy) { this.executeCommand('ai-stop'); @@ -984,7 +989,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', 'tutorial']; + const autoLoadKeys = filterKeys || ['calc', 'voice', 'cron', 'theme', 'wpm', 'mindmap', 'webdav', 'skill-pack-core', 'editor', 'diff', 'alias', 'finder', 'filemgr', 'cryptogzip', 'shell', 'tutorial', 'busybox']; const cacheVer = localStorage.getItem('webx11_cache_ver') || '2026-05-27-14'; try { const res = await fetch('packages.json?v=' + cacheVer); @@ -1098,7 +1103,7 @@ } // Enhanced: check fuzzy suggestions before showing "not found" const suggestion = this.security.suggest(cmd); - if (suggestion && suggestion.dist <= 2) { + if (suggestion && suggestion.dist <= 2 && suggestion.dist > 0) { this.print(`Command not found: ${cmd}`, 'error'); this.print(`Did you mean: ${suggestion.cmd}? (yes) (no)`, 'info'); // Render clickable yes/no @@ -1111,7 +1116,7 @@ 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 */ }; + noSpan.onclick = () => { this.print('Dismissed.', 'text-muted'); }; const line = out.lastElementChild; if (line) { line.appendChild(yesSpan); line.appendChild(noSpan); } } @@ -1450,8 +1455,7 @@ const endpoint = document.getElementById('ollama-endpoint').value.trim(); this.ollamaManager.setEndpoint(endpoint); const success = await this.ollamaManager.connect(); - this.updateOllamaStatus(); - if (success) { this.renderOllamaModels(); } + if (success) { this.renderOllamaModels(); this._populateValidatorModels(); this.updateOllamaStatus(); } } setOllamaModel() { const select = document.getElementById('ollama-current-model'); @@ -1589,6 +1593,8 @@ valSlider.style.borderColor = enabled ? 'var(--info-color)' : 'var(--border-color)'; } } + // Auto-populate validator model dropdown from connected models + this._populateValidatorModels(); // Initialize stream toggle from stored state const strCb = document.getElementById('ollama-stream-toggle'); const strLabel = document.getElementById('ollama-stream-label'); @@ -1617,12 +1623,27 @@ if (storedModel) { valSelect.innerHTML = '<option value="' + storedModel + '">' + storedModel + '</option><option value="">Disable (use main AI model)</option>'; valSelect.disabled = false; - } else { - valSelect.innerHTML = '<option value="">Connect to load models</option>'; - valSelect.disabled = true; } } } + _populateValidatorModels() { + const select = document.getElementById('validator-model-select'); + if (!select || !this.ollamaManager) return; + const models = this.ollamaManager.getModels(); + if (!models || models.length === 0) { + select.innerHTML = '<option value="">Connect to load models</option>'; + select.disabled = true; + return; + } + const stored = this.ollamaManager.validatorModel || ''; + let html = '<option value="">Disable (use main AI model)</option>'; + models.forEach(function(m) { + const sel = m.name === stored ? ' selected' : ''; + html += '<option value="' + m.name + '"' + sel + '>' + m.name + '</option>'; + }); + select.innerHTML = html; + select.disabled = false; + } openStartupModules() { document.getElementById('startup-modules').style.display = 'flex'; this.renderStartupModules(); @@ -1811,6 +1832,15 @@ // Also mark tutorial as skipped so it doesn't reappear if (this.tutorialModule) this.tutorialModule.skipTutorial(); } + toggleCmdCards() { + const panel = document.getElementById('cmd-cards'); + if (!panel) return; + panel.classList.toggle('visible'); + // If opening, focus the input field + if (panel.classList.contains('visible')) { + setTimeout(() => this.inputField.focus(), 50); + } + } log(level, source, message) { const d = new Date(); const ts = d.toISOString().split('T')[1].split('.')[0]; |