aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAgando <roduccomm@gmail.com>2026-06-01 06:00:39 +0200
committerAgando <roduccomm@gmail.com>2026-06-01 06:00:39 +0200
commit6aed9c9e0252b0c8f681a358e5a8707271454137 (patch)
tree332a29a515c87c2295355cf81f18b818cfbaa846
parent7cc82d2d32e44ab1c5b7d042d5522d036d9aa0be (diff)
auto: 2026-06-01_06:00 changes
-rw-r--r--cheatsheet.html166
-rw-r--r--help.html254
-rw-r--r--main.html73
-rw-r--r--modules/core/module-aiassist.js10
-rw-r--r--modules/core/module-loader.js2
-rw-r--r--modules/core/ollama-manager.js72
-rw-r--r--modules/core/terminal.js58
-rw-r--r--modules/ext/busybox.js8
8 files changed, 589 insertions, 54 deletions
diff --git a/cheatsheet.html b/cheatsheet.html
new file mode 100644
index 0000000..4cb4a1f
--- /dev/null
+++ b/cheatsheet.html
@@ -0,0 +1,166 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+<meta charset="UTF-8">
+<meta name="viewport" content="width=device-width, initial-scale=1">
+<title>newOS4 — Quick Reference</title>
+<style>
+*,*::before,*::after{box-sizing:border-box;margin:0;padding:0}
+:root{
+ --bg:#0a0a14;--bg2:#111122;--bg3:#16162a;--text:#d0d0e8;--text2:#8888aa;
+ --accent:#7c5cfc;--accent2:#a78bfa;--green:#34d399;--cyan:#22d3ee;--amber:#fbbf24;
+ --rose:#fb7185;--border:#2a2a4a;--radius:8px;
+ --font-mono:'JetBrains Mono','Fira Code','Courier New',monospace;
+ --font-sans:-apple-system,BlinkMacSystemFont,'Segoe UI','Inter',sans-serif
+}
+body{background:var(--bg);color:var(--text);font-family:var(--font-sans);font-size:14px;line-height:1.5;padding:20px;-webkit-font-smoothing:antialiased}
+h1{font-size:1.4em;color:#fff;margin-bottom:4px;letter-spacing:-.02em;display:flex;align-items:center;gap:8px}
+.sub{color:var(--text2);font-size:.82em;margin-bottom:20px;border-bottom:1px solid var(--border);padding-bottom:12px}
+.sub a{color:var(--accent2);text-decoration:none}
+.sub a:hover{color:#fff}
+h2{font-size:.95em;color:var(--accent2);margin:16px 0 8px;letter-spacing:.03em;text-transform:uppercase;display:flex;align-items:center;gap:6px}
+.grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(180px,1fr));gap:4px 12px;margin-bottom:4px}
+.cmd{font-family:var(--font-mono);font-size:.85em;color:var(--accent2);font-weight:600}
+.desc{color:var(--text2);font-size:.78em}
+.item{padding:4px 0;border-bottom:1px solid rgba(42,42,74,.3);display:flex;gap:8px;align-items:baseline}
+.item .cmd{min-width:80px;flex-shrink:0}
+.cols{display:grid;grid-template-columns:1fr 1fr;gap:8px 24px}
+@media(max-width:600px){.cols{grid-template-columns:1fr}}
+pre{background:var(--bg2);border:1px solid var(--border);border-radius:var(--radius);padding:10px 14px;font-family:var(--font-mono);font-size:.82em;overflow-x:auto;margin:8px 0;line-height:1.6;color:var(--text)}
+@media print{body{background:#fff;color:#222;padding:10mm;font-size:10px}:root{--bg:#fff;--bg2:#f5f5f5;--text:#222;--text2:#666;--accent2:#6366f1;--accent:#6366f1;--border:#ddd;--green:#16a34a;--rose:#dc2626}.sub{border-color:#ddd}.item{border-color:#eee}.cmd{color:#4f46e5}pre{background:#f5f5f5;border-color:#ddd;color:#333}.no-print{display:none!important}}
+.footer{text-align:center;margin-top:24px;padding-top:12px;border-top:1px solid var(--border);color:var(--text2);font-size:.78em}
+</style>
+</head>
+<body>
+
+<h1>⚡ newOS4 Quick Reference</h1>
+<div class="sub">WebX11 v4.2 · <a href="help.html">Full docs →</a> · <span class="no-print">Ctrl+P to print</span></div>
+
+<div class="cols">
+
+<div>
+<h2>🟢 Basics</h2>
+<div class="grid">
+<div class="item"><span class="cmd">help</span><span class="desc">Show all commands</span></div>
+<div class="item"><span class="cmd">help &lt;cmd&gt;</span><span class="desc">Details for a command</span></div>
+<div class="item"><span class="cmd">ls</span><span class="desc">List files</span></div>
+<div class="item"><span class="cmd">cd &lt;dir&gt;</span><span class="desc">Change directory</span></div>
+<div class="item"><span class="cmd">pwd</span><span class="desc">Print working dir</span></div>
+<div class="item"><span class="cmd">clear / cls</span><span class="desc">Clear terminal</span></div>
+<div class="item"><span class="cmd">cat &lt;file&gt;</span><span class="desc">View file</span></div>
+<div class="item"><span class="cmd">touch &lt;file&gt;</span><span class="desc">Create empty file</span></div>
+<div class="item"><span class="cmd">mkdir &lt;dir&gt;</span><span class="desc">Create directory</span></div>
+<div class="item"><span class="cmd">rm &lt;file&gt;</span><span class="desc">Remove file</span></div>
+<div class="item"><span class="cmd">cp &lt;src&gt; &lt;dst&gt;</span><span class="desc">Copy file</span></div>
+<div class="item"><span class="cmd">mv &lt;old&gt; &lt;new&gt;</span><span class="desc">Move/rename</span></div>
+<div class="item"><span class="cmd">edit &lt;file&gt;</span><span class="desc">Open code editor</span></div>
+<div class="item"><span class="cmd">find &lt;name&gt;</span><span class="desc">Search files</span></div>
+<div class="item"><span class="cmd">grep &lt;str&gt;</span><span class="desc">Search inside files</span></div>
+<div class="item"><span class="cmd">tree</span><span class="desc">Dir tree view</span></div>
+</div>
+
+<h2>🤖 AI</h2>
+<div class="grid">
+<div class="item"><span class="cmd">ai "..."</span><span class="desc">Ask AI assistant</span></div>
+<div class="item"><span class="cmd">ask "..."</span><span class="desc">Alias for ai</span></div>
+<div class="item"><span class="cmd">ai-model</span><span class="desc">List/switch model</span></div>
+<div class="item"><span class="cmd">ai-clear</span><span class="desc">Reset conversation</span></div>
+<div class="item"><span class="cmd">ai-system</span><span class="desc">Show/edit system prompt</span></div>
+<div class="item"><span class="cmd">ai-context</span><span class="desc">View AI context</span></div>
+<div class="item"><span class="cmd">ai-stop</span><span class="desc">Cancel AI</span></div>
+</div>
+
+<h2>🧩 Modules</h2>
+<div class="grid">
+<div class="item"><span class="cmd">modules / listmods</span><span class="desc">List loaded modules</span></div>
+<div class="item"><span class="cmd">loadmod &lt;url&gt;</span><span class="desc">Load module from URL</span></div>
+<div class="item"><span class="cmd">startup-modules</span><span class="desc">Configure boot modules</span></div>
+<div class="item"><span class="cmd">tutorial</span><span class="desc">Interactive tour</span></div>
+</div>
+
+<h2>🦙 Ollama</h2>
+<div class="grid">
+<div class="item"><span class="cmd">ollama</span><span class="desc">Open Ollama panel</span></div>
+<div class="item"><span class="cmd">models</span><span class="desc">List available models</span></div>
+<div class="item"><span class="cmd">model-pull &lt;name&gt;</span><span class="desc">Download a model</span></div>
+</div>
+
+<h2>📡 Network</h2>
+<div class="grid">
+<div class="item"><span class="cmd">search &lt;q&gt;</span><span class="desc">Web search</span></div>
+<div class="item"><span class="cmd">fetch &lt;url&gt;</span><span class="desc">Fetch web page</span></div>
+<div class="item"><span class="cmd">ddg &lt;q&gt;</span><span class="desc">DuckDuckGo search</span></div>
+<div class="item"><span class="cmd">server</span><span class="desc">Start/status server</span></div>
+</div>
+
+</div>
+
+<div>
+
+<h2>🔧 Tools</h2>
+<div class="grid">
+<div class="item"><span class="cmd">calc &lt;expr&gt;</span><span class="desc">Calculator</span></div>
+<div class="item"><span class="cmd">cron-every &lt;s&gt; &lt;cmd&gt;</span><span class="desc">Schedule repeating</span></div>
+<div class="item"><span class="cmd">cron-list / cron-stop</span><span class="desc">Manage jobs</span></div>
+<div class="item"><span class="cmd">wpm</span><span class="desc">Package manager</span></div>
+<div class="item"><span class="cmd">mindmap</span><span class="desc">Brainstorming tool</span></div>
+<div class="item"><span class="cmd">webdav-status</span><span class="desc">Cloud sync status</span></div>
+<div class="item"><span class="cmd">webdav-push / pull</span><span class="desc">Sync files</span></div>
+<div class="item"><span class="cmd">theme &lt;name&gt;</span><span class="desc">Change theme</span></div>
+<div class="item"><span class="cmd">voice</span><span class="desc">Speech-to-text</span></div>
+<div class="item"><span class="cmd">chat</span><span class="desc">Chat overlay</span></div>
+<div class="item"><span class="cmd">gzip / gunzip</span><span class="desc">Compress files</span></div>
+<div class="item"><span class="cmd">encrypt / decrypt</span><span class="desc">AES-256 encrypt</span></div>
+</div>
+
+<h2>🐚 Shell</h2>
+<div class="grid">
+<div class="item"><span class="cmd">export &lt;K&gt;=&lt;V&gt;</span><span class="desc">Set variable</span></div>
+<div class="item"><span class="cmd">script &lt;file.sh&gt;</span><span class="desc">Run shell script</span></div>
+<div class="item"><span class="cmd">set</span><span class="desc">List variables</span></div>
+<div class="item"><span class="cmd">cmd1 ; cmd2</span><span class="desc">Sequence</span></div>
+<div class="item"><span class="cmd">cmd1 && cmd2</span><span class="desc">Chain (AND)</span></div>
+<div class="item"><span class="cmd">cmd1 || cmd2</span><span class="desc">Chain (OR)</span></div>
+<div class="item"><span class="cmd">cmd1 | cmd2</span><span class="desc">Pipe</span></div>
+<div class="item"><span class="cmd">cmd &gt; file</span><span class="desc">Redirect output</span></div>
+</div>
+
+<h2>🖥️ System</h2>
+<div class="grid">
+<div class="item"><span class="cmd">status</span><span class="desc">System health</span></div>
+<div class="item"><span class="cmd">mem</span><span class="desc">Memory usage</span></div>
+<div class="item"><span class="cmd">echo &lt;text&gt;</span><span class="desc">Print text</span></div>
+<div class="item"><span class="cmd">date / cal</span><span class="desc">Date & calendar</span></div>
+<div class="item"><span class="cmd">whoami / hostname</span><span class="desc">User & machine</span></div>
+<div class="item"><span class="cmd">logs &lt;n&gt;</span><span class="desc">View recent logs</span></div>
+<div class="item"><span class="cmd">logrotate</span><span class="desc">Rotate log files</span></div>
+<div class="item"><span class="cmd">config</span><span class="desc">Config manager UI</span></div>
+</div>
+
+<h2>🔒 Security</h2>
+<div class="grid">
+<div class="item"><span class="cmd">security</span><span class="desc">Show current level</span></div>
+<div class="item"><span class="cmd">security-level 0-3</span><span class="desc">Set level</span></div>
+</div>
+<div style="margin-top:8px">
+<p style="font-size:.78em;color:var(--text2)">Level 0 = permissive · 1 = standard (default) · 2 = paranoid · 3 = audit</p>
+</div>
+
+<h2>⌨️ Shortcuts</h2>
+<div class="grid">
+<div class="item"><span class="cmd">↑ / ↓</span><span class="desc">Command history</span></div>
+<div class="item"><span class="cmd">Tab</span><span class="desc">Autocomplete</span></div>
+<div class="item"><span class="cmd">Ctrl+K</span><span class="desc">Quick Commands panel</span></div>
+<div class="item"><span class="cmd">Esc</span><span class="desc">Cancel AI / close</span></div>
+<div class="item"><span class="cmd">Ctrl+L</span><span class="desc">Clear terminal</span></div>
+</div>
+
+</div>
+</div>
+
+<div class="footer">
+ WebX11 v4.2 · <a href="help.html">help.html</a> · newOS4 on <a href="main.html">Launch →</a>
+</div>
+
+</body>
+</html>
diff --git a/help.html b/help.html
index 78cf3f0..484c20d 100644
--- a/help.html
+++ b/help.html
@@ -10,26 +10,26 @@
*,*::before,*::after{box-sizing:border-box;margin:0;padding:0;-webkit-tap-highlight-color:transparent}
:root{
--bg:#0a0a14;
- --bg2:#111122;
- --bg3:#16162a;
- --bg4:#1c1c36;
- --text:#d0d0e8;
- --text2:#8888aa;
- --accent:#7c5cfc;
+ --bg2:#141428;
+ --bg3:#1c1c36;
+ --bg4:#24244a;
+ --text:#e8e8ff;
+ --text2:#b0b0cc;
+ --accent:#818cf8;
--accent2:#a78bfa;
--green:#34d399;
--cyan:#22d3ee;
--amber:#fbbf24;
--rose:#fb7185;
--pink:#e879f9;
- --border:#2a2a4a;
+ --border:#3a3a5a;
--radius:12px;
--font-mono:'JetBrains Mono','Fira Code','Cascadia Code','Courier New',monospace;
--font-sans:-apple-system,BlinkMacSystemFont,'Segoe UI','Inter','SF Pro',Roboto,sans-serif
}
html{scroll-behavior:smooth}
-body{background:var(--bg);color:var(--text);font-family:var(--font-sans);line-height:1.7;font-size:15px;-webkit-font-smoothing:antialiased}
-@media(max-width:600px){body{font-size:13.5px}}
+body{background:var(--bg);color:var(--text);font-family:var(--font-sans);line-height:1.8;font-size:17px;-webkit-font-smoothing:antialiased}
+@media(max-width:600px){body{font-size:15px}}
/* ── Header ── */
header{
@@ -206,6 +206,20 @@ footer .tagline{color:var(--accent2);margin-top:10px;font-weight:500}
.quick-ref-grid{grid-template-columns:1fr}
.badge-new{display:none}
}
+
+/* ── Doc Tabs ── */
+.doc-tabs{display:flex;gap:0;margin-bottom:32px;border-bottom:1px solid var(--border);flex-wrap:wrap}
+.doc-tabs input{display:none}
+.doc-tabs label{
+ flex-shrink:0;padding:12px 20px;cursor:pointer;font-size:.82em;font-weight:600;
+ color:var(--text2);border-bottom:2px solid transparent;transition:all .15s;
+ font-family:var(--font-sans);user-select:none;-webkit-user-select:none
+}
+.doc-tabs label:hover{color:var(--text)}
+.doc-tabs input:checked+label{color:var(--accent2);border-color:var(--accent2)}
+.doc-tab{display:none}
+.doc-tab.active{display:block}
+
</style>
</head>
<body>
@@ -250,19 +264,160 @@ footer .tagline{color:var(--accent2);margin-top:10px;font-weight:500}
</nav>
<main>
+<div class="doc-tabs">
+ <input type="radio" name="doc-layer" id="tab-basics" checked>
+ <label for="tab-basics">🌱 Start in 5 Min</label>
+ <input type="radio" name="doc-layer" id="tab-guide">
+ <label for="tab-guide">📘 User Guide</label>
+ <input type="radio" name="doc-layer" id="tab-dev">
+ <label for="tab-dev">🔧 Dev Reference</label>
+</div>
+<div class="doc-tab active" id="content-basics">
+ <section id="basics-overview">
+ <h2>🚀 Welcome to newOS4</h2>
+ <div class="callout callout-success">
+ <span class="callout-title">🎮 New here?</span>
+ Type <code>tutorial</code> in the terminal to start a 2-minute interactive tour!
+ </div>
+ <div class="feature-grid">
+ <div class="feature-card"><h4>🖥️ Terminal OS</h4><p>Full virtual filesystem with ls, cd, cat, touch, edit, find, grep and 50+ Unix-like commands.</p></div>
+ <div class="feature-card"><h4>🤖 AI Assistant</h4><p>Built-in AI agent with tool calling, skills, memory, and 28 tools. Just type <code>ai "your question"</code>.</p></div>
+ <div class="feature-card"><h4>🧩 Module System</h4><p>40+ modules including cron, mindmap, calculator, voice, WebDAV, themes, and shell scripting.</p></div>
+ <div class="feature-card"><h4>📚 Skills</h4><p>Self-improving AI remembers workflows and teaches you new capabilities — type <code>skill-list</code> to see what's installed.</p></div>
+ </div>
+ </section>
+
+ <section id="basics-first">
+ <h3>🔤 First 6 Commands</h3>
+ <div class="quick-ref">
+ <div class="quick-ref-grid">
+ <div class="quick-ref-item"><span class="cmd">help</span><span class="desc">Show all commands</span></div>
+ <div class="quick-ref-item"><span class="cmd">ls</span><span class="desc">List files</span></div>
+ <div class="quick-ref-item"><span class="cmd">cat</span><span class="desc">View file content</span></div>
+ <div class="quick-ref-item"><span class="cmd">clear</span><span class="desc">Clear terminal</span></div>
+ <div class="quick-ref-item"><span class="cmd">touch</span><span class="desc">Create a file</span></div>
+ <div class="quick-ref-item"><span class="cmd">ai</span><span class="desc">Ask the AI assistant</span></div>
+ </div>
+ </div>
+ <h3>📋 Workflow</h3>
+ <ol style="margin-bottom:14px;padding-left:22px;color:var(--text)">
+ <li>Type <code>help</code> — see every available command</li>
+ <li>Type <code>ls</code> — browse files in your virtual drive</li>
+ <li>Type <code>touch hello.txt</code> — create a file</li>
+ <li>Type <code>edit hello.txt</code> — write something and save</li>
+ <li>Type <code>ai "summarize this"</code> — let AI analyze your files</li>
+ <li>Type <code>status</code> — check system health</li>
+ </ol>
+ </section>
+
+ <section>
+ <h3>🔧 Quick Start</h3>
+ <p>Type <code>help</code> to see all 130+ commands grouped by category. Use <code>help &lt;command&gt;</code> for details on a specific one.</p>
+ <p>The <strong>📋 Quick Commands</strong> panel (Ctrl+K) has clickable cards for common tasks.</p>
+ <p>For the full reference, switch to the <strong>📘 User Guide</strong> tab — it covers every module, filesystem, AI, skills, configuration, and all extended modules in detail.</p>
+ </section>
+</div>
<div class="search-box">
<input type="text" id="search-input" placeholder="Search docs…" spellcheck="false">
<button onclick="searchDocs()">Search</button>
</div>
<div class="search-result-msg" id="search-result-msg"></div>
+ <div class="doc-tab" id="content-dev">
+ <section id="dev-api">
+ <h2>🔧 Module Development API</h2>
+ <h3>Ext Module Structure</h3>
+ <p>Modules are ES6 classes with 3 required and 1 optional method:</p>
+ <pre><code>class MyModule {
+ constructor() { this.commands = {}; this.terminal = null; }
+ requirements() { return { met: true, missing: [] }; }
+ getHelpInfo() { return { 'mycmd': 'mycmd — What it does' }; }
+ registerCommands() { this.commands['mycmd'] = (args) => { /* ... */ }; }
+}
+// Auto-register:
+if (typeof window.webx11 !== 'undefined' && window.webx11) {
+ webx11.moduleManager.registerModule(new MyModule(), 'url');
+}</code></pre>
+
+ <h3>VFS (Virtual FileSystem) API</h3>
+ <p>Access via <code>this.terminal.fs</code>:</p>
+ <div class="table-wrap"><table>
+ <tr><th>Method</th><th>Returns</th><th>Description</th></tr>
+ <tr><td><code>readFile(path)</code></td><td>string or null</td><td>Read file content</td></tr>
+ <tr><td><code>writeFile(path, content)</code></td><td>boolean</td><td>Write string to file</td></tr>
+ <tr><td><code>ls(path)</code></td><td>array</td><td>List directory entries</td></tr>
+ <tr><td><code>mkdir(path)</code></td><td>boolean</td><td>Create directory</td></tr>
+ <tr><td><code>rm(path)</code></td><td>boolean</td><td>Remove file</td></tr>
+ <tr><td><code>rmdir(path)</code></td><td>boolean</td><td>Remove empty directory</td></tr>
+ <tr><td><code>touch(path)</code></td><td>boolean</td><td>Create empty file</td></tr>
+ <tr><td><code>cd(path)</code></td><td>boolean</td><td>Change current directory</td></tr>
+ <tr><td><code>pwd()</code></td><td>string</td><td>Current directory path</td></tr>
+ <tr><td><code>mv(old, new)</code></td><td>boolean</td><td>Move/rename file</td></tr>
+ <tr><td><code>cp(src, dst)</code></td><td>boolean</td><td>Copy file</td></tr>
+ </table></div>
+
+ <h3>AI Tool Definition Format</h3>
+ <p>Add tools to <code>module-aiassist.js</code> in <code>getToolDefinitions()</code> (around line 300):</p>
+ <pre><code>{
+ type: 'function',
+ function: {
+ name: 'my_tool',
+ description: 'What it does — AI reads this to decide when to call it.',
+ parameters: { type: 'object', properties: {
+ paramName: { type: 'string', description: 'What this param does' }
+ }, required: ['paramName'] }
+ }
+}</code></pre>
+ <p>Then add a <code>case 'my_tool':</code> handler in <code>runTool()</code> (around line 1000). Return <code>JSON.stringify(...)</code>.</p>
+ </section>
+
+ <section id="dev-skills">
+ <h3>Skill Format</h3>
+ <p>Skills are markdown files with YAML frontmatter, stored in <code>/skills/</code> in VFS:</p>
+ <pre><code>---
+name: my-skill
+description: Brief description
+tags: [tag1, tag2]
+---
+# Skill Body
+
+Instructions, examples, and workflows here.</code></pre>
+ </section>
+
+ <section id="dev-ollama">
+ <h3>OllamaManager API</h3>
+ <div class="table-wrap"><table>
+ <tr><th>Property/Method</th><th>Description</th></tr>
+ <tr><td><code>.endpoint</code></td><td>Current Ollama API URL</td></tr>
+ <tr><td><code>.currentModel</code></td><td>Active model name</td></tr>
+ <tr><td><code>.models</code></td><td>Array of available models</td></tr>
+ <tr><td><code>.connected</code></td><td>Boolean connection state</td></tr>
+ <tr><td><code>.connect()</code></td><td>Connect to Ollama, load models</td></tr>
+ <tr><td><code>.chatWithTools()</code></td><td>AI chat with tool calling support</td></tr>
+ </table></div>
+ </section>
+
+ <section id="dev-global">
+ <h3>window.webx11 Global</h3>
+ <p>The terminal instance is exposed globally as <code>window.webx11</code> (aliased as <code>webx11</code>). This gives access to everything:</p>
+ <pre><code>webx11.fs // VFS instance
+webx11.moduleManager // Module registry
+webx11.ollamaManager // Ollama connection
+webx11.security // Security manager
+webx11.executeCommand(cmd) // Run a terminal command programmatically
+webx11.print(text, type) // Print to terminal output
+webx11.tutorialModule // Tutorial progress (if active)
+webx11.inputField // DOM input element</code></pre>
+ </section>
+</div>
+<div class="doc-tab" id="content-guide">
<!-- ── Overview ── -->
<section id="overview">
<h2>⛅ Overview</h2>
<p>WebX11 v4.2 is a browser-based Unix-like terminal — dynamic modules, AI assistant (Ollama), persistent virtual filesystem, web search, voice input, code editor, and a full skill system. Everything runs client-side in your browser.</p>
<div class="feature-grid">
<div class="feature-card"><h4>🧩 26 Core + 14 Ext</h4><p>Modules loaded at startup. Shell with pipes, redirects, vars, and scripts.</p></div>
- <div class="feature-card"><h4>🤖 Agent with Tools</h4><p>15+ tools — file ops, web search, skills, modules. Destructive action confirmations with inline bar + countdown.</p></div>
+ <div class="feature-card"><h4>🤖 Agent with Tools</h4><p>28 tools — file ops, web search, skills, modules. Destructive action confirmations with inline bar + countdown.</p></div>
<div class="feature-card"><h4>📚 Skills System</h4><p>Persistent procedural memory. Create, patch, search, export skills for repeatable workflows.</p></div>
<div class="feature-card"><h4>💾 VFS + Sessions</h4><p>Virtual filesystem persists in localStorage. Save/load/export sessions.</p></div>
<div class="feature-card"><h4>🎤 Voice Input</h4><p>Speech recognition in the terminal. Works in Chrome, Edge, Safari.</p></div>
@@ -441,14 +596,14 @@ diff a.txt b.txt</pre>
<h2>🤖 AI Assistant</h2>
<div class="callout callout-success">
<span class="callout-title">Tool-Calling Agent</span>
- The AI runs a full agent loop with 15+ tools: filesystem, web search, URL fetch, skills, WPM, session memory, user profile, module introspection.
+ The AI runs a full agent loop with 28 tools: filesystem, web search, URL fetch, skills, WPM, session memory, user profile, module introspection.
</div>
<h3>How It Works</h3>
<ol>
<li>Type <code>ai &lt;request&gt;</code></li>
<li>AI loads relevant skills + recent session memory</li>
- <li>Decides which tools to call (up to 5 turns)</li>
+ <li>Decides which tools to call (up to 8 turns)</li>
<li><strong>Destructive tools</strong> trigger an inline confirm bar — no dark overlay</li>
<li>120s countdown: yellow → bold red at 30s → pulsing at 10s</li>
<li>Type <code>ai-allow</code> / <code>ai-deny</code> or tap the buttons</li>
@@ -578,6 +733,7 @@ ollamaconnect</pre>
<tr><td><code>ddg</code> / <code>duck</code></td><td>DDG</td><td>DuckDuckGo fallback</td><td><code>ddg news today</code></td></tr>
<tr><td><code>fetch</code> / <code>web</code></td><td>Fetcher</td><td>Fetch URL content</td><td><code>fetch https://example.com</code></td></tr>
<tr><td><code>extract</code></td><td>Fetcher</td><td>Clean readable text</td><td><code>extract https://news.article</code></td></tr>
+ <tr><td><code>images</code></td><td>SearXNG</td><td>Image search</td><td><code>images cats</code></td></tr>
</table></div>
<p>The AI agent also has <code>web_search</code> and <code>web_fetch</code> tools using the same APIs.</p>
</section>
@@ -931,6 +1087,10 @@ listmods</pre>
</div>
</section>
+ <div style="text-align:center;margin-bottom:32px;">
+ <p>📄 One-page <a href="cheatsheet.html" style="color:var(--accent2);font-weight:600;">Quick Reference cheat sheet</a> — printable, dark mode, all commands.</p>
+ </div>
+
<!-- ── Changelog ── -->
<section id="history">
<h2>📋 Changelog</h2>
@@ -955,7 +1115,7 @@ listmods</pre>
<li><strong>Diff</strong> — <code>diff a b</code> LCS-based comparison + <code>diff --module</code></li>
<li><strong>Aliases</strong> — <code>ll</code>, <code>h</code>, <code>cls</code> defaults + custom alias definition</li>
<li><strong>File Finder</strong> — <code>find &lt;query&gt;</code> recursive VFS search</li>
- <li>Agent loop: capped at 5 turns (was 1), <code>edit_module</code> destructive confirmation, model auto-validation</li>
+ <li>Agent loop: capped at 8 turns (was 1), <code>edit_module</code> destructive confirmation, model auto-validation</li>
<li><code>ai-stop</code> now cancels chat + stream, not just tool calls</li>
<li>Unknown commands awaited, "Command not found" suppressed on AI route</li>
<li><code>loopResult</code> scope fix — no more undefined error on AI fail</li>
@@ -967,7 +1127,7 @@ listmods</pre>
<div class="version-item">
<div class="v-header"><span class="v-num">v4.1</span><span class="v-date">2026-05-23</span></div>
<ul>
- <li>AI Agent with full tool-calling loop (15+ tools)</li>
+ <li>AI Agent with full tool-calling loop (28 tools)</li>
<li>Inline destructive confirmation bar with 120s countdown timer</li>
<li>Skills system (8 CLI commands + UI modal)</li>
<li>Voice input (🎤 button, SpeechRecognition, show/hide toggle)</li>
@@ -997,6 +1157,7 @@ listmods</pre>
</ul>
</div>
</section>
+</div>
</main>
<footer>
@@ -1020,15 +1181,29 @@ function searchDocs(){
const q=document.getElementById('search-input').value.toLowerCase().trim();
const msg=document.getElementById('search-result-msg');
if(!q){msg.style.display='none';return}
- const sections=document.querySelectorAll('section');
- let found=false;
- sections.forEach(s=>{
- if(s.textContent.toLowerCase().includes(q)){
- s.scrollIntoView({behavior:'smooth'});
- s.style.border='2px solid var(--accent)';s.style.borderRadius='12px';
- s.style.padding='0 20px';s.style.transition='border .3s';
- setTimeout(()=>{s.style.border='none';s.style.padding='0'},2000);
- found=true;
+ // Search in ALL tabs, switch to the one with results
+ const tabs = document.querySelectorAll('.doc-tab');
+ let found = false;
+ tabs.forEach(tab => {
+ if (tab.textContent.toLowerCase().includes(q)) {
+ // Found a match in this tab — switch to it
+ const tabId = tab.id.replace('content-', 'tab-');
+ const radio = document.getElementById(tabId);
+ if (radio) {
+ radio.checked = true;
+ radio.dispatchEvent(new Event('change'));
+ }
+ // Highlight the matching element
+ const sections = tab.querySelectorAll('section, .quick-ref-item, .feature-card, .callout');
+ sections.forEach(s => {
+ if (s.textContent.toLowerCase().includes(q)) {
+ s.scrollIntoView({behavior:'smooth', block:'center'});
+ s.style.border='2px solid var(--accent)'; s.style.borderRadius='12px';
+ s.style.padding='0 20px'; s.style.transition='border .3s';
+ setTimeout(() => { s.style.border='none'; s.style.padding='0'; }, 2000);
+ found = true;
+ }
+ });
}
});
if(!found){
@@ -1039,14 +1214,41 @@ function searchDocs(){
}
document.getElementById('search-input').addEventListener('keypress',e=>{if(e.key==='Enter')searchDocs()});
-/* ── Smooth scroll nav ── */
+/* ── Smooth scroll nav — switch to User Guide tab first ── */
document.querySelectorAll('.nav-link').forEach(l=>{
l.addEventListener('click',e=>{
e.preventDefault();
- const t=document.querySelector(l.getAttribute('href'));
- if(t)t.scrollIntoView({behavior:'smooth'});
+ const href = l.getAttribute('href');
+ // Switch to User Guide tab first
+ const guideTab = document.getElementById('tab-guide');
+ if (guideTab) {
+ guideTab.checked = true;
+ guideTab.dispatchEvent(new Event('change'));
+ }
+ // Then scroll after a short delay so tab content becomes visible
+ setTimeout(() => {
+ const t = document.querySelector(href);
+ if (t) t.scrollIntoView({behavior:'smooth', block:'start'});
+ }, 100);
+ });
+});
+/* ── Doc Tabs ── */
+document.querySelectorAll('.doc-tabs input').forEach(radio=>{
+ radio.addEventListener('change',function(){
+ document.querySelectorAll('.doc-tab').forEach(t=>t.classList.remove('active'));
+ const tab=document.getElementById('content-'+this.id.replace('tab-',''));
+ if(tab)tab.classList.add('active');
+ localStorage.setItem('webx11_help_tab',this.id);
});
});
+// Restore last tab
+(function(){
+ const saved = localStorage.getItem('webx11_help_tab');
+ if(saved){
+ const el = document.getElementById(saved);
+ if(el){ el.checked = true; el.dispatchEvent(new Event('change')); }
+ }
+})();
console.log('WebX11 v4.2 Docs — Modern');
</script>
diff --git a/main.html b/main.html
index f6a020d..d5bd57d 100644
--- a/main.html
+++ b/main.html
@@ -567,6 +567,35 @@
.mod-card { padding: 10px 6px; }
.mod-card .mod-name { font-size: 0.82em; }
}
+ /* ── Command Cards Panel ── */
+ #cmd-cards {
+ position: fixed; right: 0; top: 48px; bottom: 0; width: 280px;
+ background: var(--bg-secondary); border-left: 1px solid var(--border-color);
+ z-index: 999; display: none; flex-direction: column;
+ backdrop-filter: blur(8px); -webkit-backdrop-filter: blur(8px);
+ overflow-y: auto;
+ }
+ #cmd-cards.visible { display: flex; }
+ #cmd-cards-header {
+ display: flex; justify-content: space-between; align-items: center;
+ padding: 12px 14px; border-bottom: 1px solid var(--border-color);
+ font-weight: bold; font-size: 0.9em;
+ position: sticky; top: 0; background: var(--bg-secondary);
+ }
+ .cmd-card-section { padding: 10px 14px; }
+ .cmd-card-section + .cmd-card-section { border-top: 1px solid var(--border-color); }
+ .cmd-card-section-title { font-size: 0.75em; color: var(--text-muted); margin-bottom: 8px; text-transform: uppercase; letter-spacing: 1px; }
+ .cmd-card {
+ display: flex; align-items: center; gap: 10px;
+ padding: 10px 12px; margin-bottom: 4px;
+ background: var(--bg-tertiary); border: 1px solid var(--border-color);
+ border-radius: 6px; cursor: pointer; transition: border-color .15s;
+ min-height: 44px; touch-action: manipulation;
+ }
+ .cmd-card:hover { border-color: var(--accent-color); }
+ .cmd-card .cmd { font-family: var(--font-primary); font-weight: bold; font-size: 0.85em; color: var(--accent-color); flex-shrink: 0; min-width: 60px; }
+ .cmd-card .desc { font-size: 0.78em; color: var(--text-muted); }
+ #cmd-cards-toggle { cursor: pointer; font-size: 1.1em; line-height: 1; }
</style>
</head>
<body>
@@ -576,10 +605,43 @@
<span class="header-version">v4.2</span>
</div>
<div id="header-status" class="status-pill">Mem: --</div>
+ <span id="cmd-cards-toggle" onclick="if(webx11)webx11.toggleCmdCards()" title="Quick Commands (Ctrl+K)">📋</span>
</header>
+ <!-- Command Cards Panel -->
+ <div id="cmd-cards">
+ <div id="cmd-cards-header">
+ <span>📋 Quick Commands</span>
+ <span onclick="if(webx11)webx11.toggleCmdCards()" style="cursor:pointer;font-size:1.2em;">✕</span>
+ </div>
+ <div class="cmd-card-section">
+ <div class="cmd-card-section-title">🟢 Beginners</div>
+ <div class="cmd-card" onclick="webx11?.executeCommand('help')"><span class="cmd">help</span><span class="desc">Show all commands</span></div>
+ <div class="cmd-card" onclick="webx11?.executeCommand('ls')"><span class="cmd">ls</span><span class="desc">List files</span></div>
+ <div class="cmd-card" onclick="webx11?.executeCommand('clear')"><span class="cmd">clear</span><span class="desc">Clear terminal</span></div>
+ <div class="cmd-card" onclick="webx11?.executeCommand('status')"><span class="cmd">status</span><span class="desc">System health</span></div>
+ </div>
+ <div class="cmd-card-section">
+ <div class="cmd-card-section-title">🤖 AI</div>
+ <div class="cmd-card" onclick="webx11?.executeCommand('ai \"what can you do?\"')"><span class="cmd">ai "..."</span><span class="desc">Ask AI assistant</span></div>
+ <div class="cmd-card" onclick="webx11?.executeCommand('ai-model')"><span class="cmd">ai-model</span><span class="desc">Set AI model</span></div>
+ </div>
+ <div class="cmd-card-section">
+ <div class="cmd-card-section-title">🔍 Explore</div>
+ <div class="cmd-card" onclick="webx11?.executeCommand('modules')"><span class="cmd">modules</span><span class="desc">List loaded modules</span></div>
+ <div class="cmd-card" onclick="webx11?.executeCommand('tutorial')"><span class="cmd">tutorial</span><span class="desc">Interactive tour</span></div>
+ </div>
+ <div class="cmd-card-section">
+ <div class="cmd-card-section-title">🚀 Advanced</div>
+ <div class="cmd-card" onclick="webx11?.executeCommand('cron-every 60 echo hello')"><span class="cmd">cron</span><span class="desc">Schedule tasks</span></div>
+ <div class="cmd-card" onclick="webx11?.executeCommand('webdav-status')"><span class="cmd">webdav</span><span class="desc">Cloud sync</span></div>
+ <div class="cmd-card" onclick="webx11?.executeCommand('help.html')"><span class="cmd">help</span><span class="desc">Full reference</span></div>
+ </div>
+ </div>
+
+ <!-- Terminal Container -->
<div id="terminal-container">
-<div class="output-line info">WebX11 Module System v4.2 - Unix-like Terminal</div>
+ <div class="output-line info">WebX11 Module System v4.2 - Unix-like Terminal</div>
<div class="output-line info">Commands: ls, cd, pwd, cat, echo, rm, mkdir, mv, cp, touch, tree, find, grep</div>
<div class="output-line info">Type 'help' for all commands</div>
<div class="output-line"></div>
@@ -708,10 +770,11 @@
<span style="color:var(--warning-color);">Disable on slow connections or when browser becomes unresponsive during long outputs.</span>
</div>
<hr style="border-color:var(--border-color); margin: 15px 0;">
- <h4 style="color: var(--info-color); margin-bottom: 10px;">🔍 Validator Agent (Future)</h4>
+ <h4 style="color: var(--info-color); margin-bottom: 10px;">🔍 Validator Agent</h4>
<div style="font-size:0.85em; color:var(--text-muted); margin-bottom: 10px;">
A separate lightweight AI that validates the main agent's tool results. Has its own
- endpoint and model — independent from your active AI model.
+ endpoint and model — independent from your active AI model.<br>
+ <span style="color:var(--success-color);">⚡ Best validator model: <strong>qwen2.5:0.5b</strong> (1.94s, 352MB)</span>
</div>
<div style="display:flex; align-items:center; gap:10px; margin-bottom: 10px;">
<span style="color:var(--text-muted); font-size:0.85em;">Enable Validator:</span>
@@ -862,6 +925,10 @@
Skip — I'll explore
</button>
</div>
+ <label style="display:flex;align-items:center;justify-content:center;gap:6px;margin-top:14px;font-size:0.78em;color:var(--text-muted);cursor:pointer;">
+ <input type="checkbox" id="welcome-dont-show" onchange="if(this.checked)localStorage.setItem('webx11_welcome_dismissed','1');else localStorage.removeItem('webx11_welcome_dismissed')">
+ Don't show this again
+ </label>
<p style="font-size:0.78em;color:var(--text-muted);margin-top:16px;">
Your progress is saved. Type <code>tutorial</code> anytime to resume.
</p>
diff --git a/modules/core/module-aiassist.js b/modules/core/module-aiassist.js
index 8f8a60c..f77ec61 100644
--- a/modules/core/module-aiassist.js
+++ b/modules/core/module-aiassist.js
@@ -665,17 +665,19 @@ RULES:
}
// Validator: check safe tool results for consistency
if (safeResults.length > 0 && pendingText) {
- const om = this.terminal.ollamaManager;
+ const om = this.terminal.ollamaManager;
if (om && om.validatorEnabled && om.validatorModel) {
- let anyInvalid = false;
+ let invalidTools = [];
for (const { fn, toolResult } of safeResults) {
const valid = await om.validateToolResult(fn.name, fn.arguments, toolResult, pendingText || '');
if (!valid && !this._cancelled) {
- anyInvalid = true;
+ invalidTools.push(fn.name);
this.terminal.print('🔍 Validator: ' + fn.name + ' result — AI note may not match (logged)', 'info');
- // Just log it — don't force retry (cloud models get 400 on retry)
}
}
+ if (invalidTools.length > 0) {
+ pendingText += '\n\n⚠️ **Validator flagged** ' + invalidTools.join(', ') + ' — the AI analysis may not match what was returned. Use `ai-ignore` to dismiss and proceed.';
+ }
}
}
if (pendingText) {
diff --git a/modules/core/module-loader.js b/modules/core/module-loader.js
index cc3a79c..667a396 100644
--- a/modules/core/module-loader.js
+++ b/modules/core/module-loader.js
@@ -156,7 +156,7 @@
// 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', 'cryptogzip', 'tutorial'];
+ const extKeys = ['calc', 'voice', 'cron', 'theme', 'wpm', 'mindmap', 'webdav', 'skill-pack-core', 'editor', 'diff', 'alias', 'finder', 'filemgr', 'cryptogzip', 'tutorial', 'shell', 'busybox'];
const extSelected = selected
? extKeys.filter(k => isSelected(k + '.js', selected))
: extKeys;
diff --git a/modules/core/ollama-manager.js b/modules/core/ollama-manager.js
index ab3ec2c..9999184 100644
--- a/modules/core/ollama-manager.js
+++ b/modules/core/ollama-manager.js
@@ -15,9 +15,9 @@ class OllamaManager {
this.optimizationEnabled = localStorage.getItem('webx11_optimization_enabled') !== 'false';
// --- Streaming Toggle ---
this.streamEnabled = localStorage.getItem('webx11_stream_enabled') !== 'false';
- // --- Validator Agent Config (future) ---
- this.validatorEnabled = localStorage.getItem('webx11_validator_enabled') === 'true';
- this.validatorModel = localStorage.getItem('webx11_validator_model') || '';
+ // --- Validator Agent Config ---
+ this.validatorEnabled = localStorage.getItem('webx11_validator_enabled') !== 'false';
+ this.validatorModel = localStorage.getItem('webx11_validator_model') || 'ministral-3:3b-cloud';
this.validatorEndpoint = localStorage.getItem('webx11_validator_endpoint') || '';
// Build fallback endpoints
this.fallbackEndpoints = [];
@@ -865,6 +865,70 @@ class OllamaManager {
}
}
+ // MiniCPM5 XML tool calling mode — sends tools in message context instead of API parameter
+ // Model natively outputs <function name="x"><param name="y">val</param> XML
+ if (useModel && useModel.includes('minicpm5') && tools && tools.length > 0) {
+ this.terminal.print(`🧩 Using XML tool mode for ${useModel}...`, 'info');
+ try {
+ // Inject tool defs into a system message (the orchestrator model knows XML format)
+ const toolDesc = tools.map(t => JSON.stringify({type:'function',function:t.function})).join('\n');
+ const toolContext = { role: 'system', content:
+`You are a tool orchestrator. When you need to call a tool, output XML like:
+<function name="function-name"><param name="param-name">param-value</param>
+
+Available tools:
+<tools>
+${toolDesc}
+</tools>
+
+Only output XML function calls for tool tasks. If no tool is needed, answer normally.` };
+ const msgsWithTools = [toolContext, ...messages];
+ const body = { model: useModel, messages: msgsWithTools, stream: false, options: { temperature: 0.1 } };
+
+ const response = await fetch(`${this.endpoint}/api/chat`, {
+ method: 'POST', headers: { 'Content-Type': 'application/json' },
+ body: JSON.stringify(body), signal
+ });
+ if (!response.ok) throw new Error(`HTTP ${response.status}`);
+ const data = await response.json();
+ const text = (data.message?.content || '').trim();
+
+ // Parse XML function calls from the response
+ const toolCalls = [];
+ const funcRegex = /<function\s+name="([^"]+)"(.*?)<\/function>/gs;
+ let match;
+ while ((match = funcRegex.exec(text)) !== null) {
+ const funcName = match[1];
+ const args = {};
+ const paramRegex = /<param\s+name="([^"]+)"(.*?)>(.*?)<\/param>/gs;
+ let pmatch;
+ while ((pmatch = paramRegex.exec(match[0])) !== null) {
+ args[pmatch[1]] = pmatch[3]
+ .replace('<![CDATA[', '').replace(']]>', '')
+ .replace(/^\s+|\s+$/g, '');
+ }
+ if (funcName && Object.keys(args).length > 0) {
+ toolCalls.push({
+ id: 'call_' + Math.random().toString(36).slice(2, 10),
+ type: 'function',
+ function: { name: funcName, arguments: JSON.stringify(args) }
+ });
+ }
+ }
+ if (toolCalls.length > 0) {
+ this.terminal.print(`✅ XML tool call(s) parsed from ${useModel}`, 'success');
+ return { message: { role: 'assistant', content: '' }, tool_calls: toolCalls, error: null };
+ }
+
+ return { message: { role: 'assistant', content: text || '(no response)' }, tool_calls: null, error: null };
+ } catch (e) {
+ if (e.name === 'AbortError') {
+ return { message: null, tool_calls: null, error: 'Cancelled' };
+ }
+ return { message: null, tool_calls: null, error: e.message };
+ }
+ }
+
// Original Ollama fallback
this.terminal.print(`Model doesn't support tool calling. Retrying /api/chat without tools...`, 'warning');
try {
@@ -981,7 +1045,7 @@ class OllamaManager {
});
const data = await res.json();
const answer = (data.message?.content || '').trim().toUpperCase();
- return answer === 'YES';
+ return answer.startsWith('YES');
} catch (e) {
if (this.terminal) this.terminal.print('⚠️ Validator error: ' + e.message, 'warning');
return true;
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];
diff --git a/modules/ext/busybox.js b/modules/ext/busybox.js
index 709324f..34221f8 100644
--- a/modules/ext/busybox.js
+++ b/modules/ext/busybox.js
@@ -57,8 +57,12 @@ class BusyBoxModule {
const keys = Object.keys(all);
if (all[query]) {
- const mod = this.terminal.moduleManager._findModuleForCommand(query);
- const src = mod ? ` (from ${mod.constructor.name})` : '';
+ let foundMod = null;
+ for (const modName in this.terminal.moduleManager.modules) {
+ const mod = this.terminal.moduleManager.modules[modName];
+ if (mod.commands && mod.commands[query]) { foundMod = mod; break; }
+ }
+ const src = foundMod ? ` (from ${foundMod.constructor.name})` : '';
return this.terminal.print(`${query}${src}`, 'info');
}