diff options
| author | Agando <roduccomm@gmail.com> | 2026-10-17 20:17:28 +0200 |
|---|---|---|
| committer | Agando <roduccomm@gmail.com> | 2026-10-17 20:17:28 +0200 |
| commit | 526c420f4591f0a58f805b473fa76d3da2981eae (patch) | |
| tree | 9c8f3bb3bf5f329b81647f064d6c87554850d196 | |
| parent | c3f89abb448021ecf894b27692dba0a7dc2ff8e3 (diff) | |
newos4: floating-ui-designer v3 — repair 12 escape-mangled JS sites, rebuild 344-cmd registry (124->344), quoting/import/preset UX fixes; add Node harness + registry generator; floating-ui skill JSON de-piped
| -rw-r--r-- | downloads/floatimg-buttons.html | 290 | ||||
| -rw-r--r-- | downloads/floating-ui-designer.html | 932 | ||||
| -rw-r--r-- | scripts/gen-floating-designer-registry.py | 117 | ||||
| -rw-r--r-- | scripts/test-floating-designer.mjs | 144 | ||||
| -rw-r--r-- | skills/floating-ui.json | 8 |
5 files changed, 1487 insertions, 4 deletions
diff --git a/downloads/floatimg-buttons.html b/downloads/floatimg-buttons.html new file mode 100644 index 0000000..317dd42 --- /dev/null +++ b/downloads/floatimg-buttons.html @@ -0,0 +1,290 @@ +<!DOCTYPE html> +<html lang="en"> +<head> + <meta charset="UTF-8"> + <meta name="viewport" content="width=device-width, initial-scale=1.0"> + <title>Floating Buttons Setup</title> + <style> + * { box-sizing: border-box; margin: 0; padding: 0; } + body { + font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; + background: linear-gradient(135deg, #1a1a2e 0%, #16213e 100%); + min-height: 100vh; + padding: 40px 20px; + color: #fff; + } + .container { max-width: 640px; margin: 0 auto; } + h1 { text-align: center; margin-bottom: 10px; font-size: 1.8em; } + h2 { margin: 35px 0 15px; font-size: 1.2em; color: #90EE90; } + .subtitle { text-align: center; color: #888; margin-bottom: 30px; } + + .card { + background: rgba(255,255,255,0.1); + border: 1px solid transparent; + border-radius: 12px; + padding: 18px 20px; + margin-bottom: 12px; + cursor: pointer; + transition: background 0.2s; + display: flex; + align-items: center; + gap: 15px; + user-select: none; + } + .card:hover { background: rgba(255,255,255,0.18); } + .card:active { transform: scale(0.98); } + .card.copied { background: rgba(76,175,80,0.35); border-color: #4CAF50; } + + .emoji { font-size: 1.8em; width: 50px; text-align: center; } + .info { flex: 1; min-width: 0; } + .label { font-size: 1.1em; font-weight: bold; margin-bottom: 5px; } + .cmd { + font-family: monospace; + background: rgba(0,0,0,0.35); + padding: 8px 12px; + border-radius: 6px; + font-size: 0.82em; + color: #90EE90; + word-break: break-all; + white-space: pre-wrap; + } + .copy-icon { font-size: 1.3em; opacity: 0.4; } + .card:hover .copy-icon { opacity: 1; } + + .btn-wide { + background: #4CAF50; + color: white; + border: none; + padding: 14px 30px; + border-radius: 8px; + font-size: 1.05em; + cursor: pointer; + width: 100%; + margin-top: 15px; + transition: background 0.2s; + } + .btn-wide:hover { background: #45a049; } + .btn-wide:active { transform: scale(0.98); } + + .note { + background: rgba(255,193,7,0.12); + border-left: 4px solid #FFC107; + padding: 14px 18px; + border-radius: 0 8px 8px 0; + margin: 20px 0; + font-size: 0.92em; + color: #ffe082; + } + + .preview { + background: rgba(0,0,0,0.25); + border-radius: 12px; + padding: 20px; + margin-bottom: 25px; + text-align: center; + } + .preview-label { color: #888; margin-bottom: 14px; font-size: 0.9em; } + .preview-btns { display: flex; gap: 10px; justify-content: center; flex-wrap: wrap; } + .preview-btn { + background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); + border: none; + padding: 10px 16px; + border-radius: 8px; + font-size: 1.25em; + } + + .toast { + position: fixed; + bottom: 30px; + left: 50%; + transform: translateX(-50%) translateY(120px); + background: #4CAF50; + color: white; + padding: 12px 24px; + border-radius: 8px; + font-weight: bold; + transition: transform 0.3s; + z-index: 1000; + } + .toast.show { transform: translateX(-50%) translateY(0); } + </style> +</head> +<body> + <div class="container"> + <h1>👆 Floating Buttons Setup</h1> + <p class="subtitle">Click a card to copy the command · paste it in the WebX11 terminal</p> + + <div class="preview"> + <div class="preview-label">Preview</div> + <div class="preview-btns" id="previewBtns"></div> + </div> + + <div class="note"> + ⚠️ <b>Note:</b> <code>clipboard-history</code> is <b>not</b> a real command in this system — + that's why the 📋 button did nothing. It was replaced below with real, verified commands. + </div> + + <h2>✅ Your Buttons (all verified working)</h2> + <div id="cardsMain"></div> + <button class="btn-wide" id="btnCopyMain">📋 Copy All Main Commands</button> + + <h2>💡 More Examples (all verified working)</h2> + <div id="cardsMore"></div> + <button class="btn-wide" id="btnCopyMore">📋 Copy All Example Commands</button> + </div> + + <div class="toast" id="toast">Copied!</div> + + <script> + // ── All commands below were verified against the system command list ── + + var MAIN_BUTTONS = [ + { emoji: '🆕', label: 'New Session', + cmd: 'floating-add btn new-session "🆕" new-session' }, + { emoji: '✅', label: 'AI Allow', + cmd: 'floating-add btn ai-allow "✅" ai-allow' }, + { emoji: '⚙️', label: 'AI Config (confirm-timeout 0)', + cmd: 'floating-add btn config-timeout "⚙️" ai-config confirm-timeout 0' }, + { emoji: '🌐', label: 'WebDAV Browser', + cmd: 'floating-add btn webdav-btn "🌐" webdav-browser' }, + { emoji: '📊', label: 'System Status', + cmd: 'floating-add btn status-btn "📊" status' } + ]; + + var MORE_BUTTONS = [ + { emoji: '🩺', label: 'Health Check', + cmd: 'floating-add btn health-btn "🩺" health-check' }, + { emoji: '🧹', label: 'Clear AI Context', + cmd: 'floating-add btn ai-clear-btn "🧹" ai-clear' }, + { emoji: '⏹️', label: 'Stop AI Task', + cmd: 'floating-add btn ai-stop-btn "⏹️" ai-stop' }, + { emoji: '🧠', label: 'Memory Usage', + cmd: 'floating-add btn mem-btn "🧠" mem' }, + { emoji: '📦', label: 'List Modules', + cmd: 'floating-add btn modules-btn "📦" modules' }, + { emoji: '🎨', label: 'Change Theme', + cmd: 'floating-add btn theme-btn "🎨" theme' }, + { emoji: '💬', label: 'Chat Mode', + cmd: 'floating-add btn chat-btn "💬" chat' }, + { emoji: '👥', label: 'List Agents', + cmd: 'floating-add btn agents-btn "👥" agent-list' }, + { emoji: '🗂️', label: 'Kanban Board', + cmd: 'floating-add btn kanban-btn "🗂️" kanban' }, + { emoji: '🔐', label: 'WebDAV Status', + cmd: 'floating-add btn webdav-status-btn "🔐" webdav-status' }, + { emoji: '🧪', label: 'Run System Doctor', + cmd: 'floating-add btn doctor-btn "🧪" doctor' }, + { emoji: '⏰', label: 'System Uptime', + cmd: 'floating-add btn uptime-btn "⏰" uptime' } + ]; + + // ── Copy helpers (robust: clipboard API first, textarea fallback second) ── + + function fallbackCopy(text) { + var ta = document.createElement('textarea'); + ta.value = text; + ta.setAttribute('readonly', ''); + ta.style.position = 'fixed'; + ta.style.left = '-9999px'; + ta.style.top = '0'; + document.body.appendChild(ta); + ta.focus(); + ta.select(); + var ok = false; + try { ok = document.execCommand('copy'); } catch (e) { ok = false; } + document.body.removeChild(ta); + return ok; + } + + function copyText(text) { + return new Promise(function (resolve) { + if (navigator.clipboard && window.isSecureContext) { + navigator.clipboard.writeText(text).then( + function () { resolve(true); }, + function () { resolve(fallbackCopy(text)); } + ); + } else { + resolve(fallbackCopy(text)); + } + }); + } + + function showToast(msg) { + var toast = document.getElementById('toast'); + toast.textContent = msg; + toast.classList.add('show'); + setTimeout(function () { toast.classList.remove('show'); }, 2000); + } + + function copyCard(el, cmd) { + copyText(cmd).then(function (ok) { + if (ok) { + el.classList.add('copied'); + showToast('Copied! Paste in terminal'); + setTimeout(function () { el.classList.remove('copied'); }, 1500); + } else { + showToast('Copy failed — select the command text manually'); + } + }); + } + + // ── Render cards from data (no inline onclick — avoids quote-escaping bugs) ── + + function renderCards(containerId, list) { + var host = document.getElementById(containerId); + list.forEach(function (b) { + var card = document.createElement('div'); + card.className = 'card'; + + var emoji = document.createElement('div'); + emoji.className = 'emoji'; + emoji.textContent = b.emoji; + + var info = document.createElement('div'); + info.className = 'info'; + var label = document.createElement('div'); + label.className = 'label'; + label.textContent = b.label; + var cmd = document.createElement('div'); + cmd.className = 'cmd'; + cmd.textContent = b.cmd; // textContent → no HTML escaping issues + info.appendChild(label); + info.appendChild(cmd); + + var icon = document.createElement('div'); + icon.className = 'copy-icon'; + icon.textContent = '📋'; + + card.appendChild(emoji); + card.appendChild(info); + card.appendChild(icon); + + card.addEventListener('click', function () { copyCard(card, b.cmd); }); + host.appendChild(card); + }); + } + + renderCards('cardsMain', MAIN_BUTTONS); + renderCards('cardsMore', MORE_BUTTONS); + + // Preview strip + var preview = document.getElementById('previewBtns'); + MAIN_BUTTONS.forEach(function (b) { + var pb = document.createElement('div'); + pb.className = 'preview-btn'; + pb.textContent = b.emoji; + preview.appendChild(pb); + }); + + // Copy-all buttons + document.getElementById('btnCopyMain').addEventListener('click', function () { + copyText(MAIN_BUTTONS.map(function (b) { return b.cmd; }).join('\n')) + .then(function () { showToast('All main commands copied!'); }); + }); + document.getElementById('btnCopyMore').addEventListener('click', function () { + copyText(MORE_BUTTONS.map(function (b) { return b.cmd; }).join('\n')) + .then(function () { showToast('All example commands copied!'); }); + }); + </script> +</body> +</html>
\ No newline at end of file diff --git a/downloads/floating-ui-designer.html b/downloads/floating-ui-designer.html new file mode 100644 index 0000000..495bdc4 --- /dev/null +++ b/downloads/floating-ui-designer.html @@ -0,0 +1,932 @@ +<!DOCTYPE html> +<html lang="en"><head> +<meta charset="UTF-8"> +<meta name="viewport" content="width=device-width, initial-scale=1.0"> +<meta name="description" content="Design floating buttons, menus and panels for the WebX11 newOS4 terminal — generate verified floating-add commands."> +<title>🎨 Floating UI Designer v3 — WebX11 newOS4</title> +<style> +:root{--bg:#0d1117;--panel:#161b22;--panel2:#1c2230;--border:#30363d;--text:#e6edf3;--muted:#8b949e;--accent:#58a6ff;--green:#3fb950;--yellow:#d29922;--red:#f85149;--purple:#bc8cff} +*{box-sizing:border-box;margin:0;padding:0} +body{background:var(--bg);color:var(--text);font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',sans-serif;padding:20px;max-width:1500px;margin:0 auto} +h1{font-size:22px;margin-bottom:4px} +h2{font-size:15px;margin-bottom:10px;color:var(--accent)} +.sub{color:var(--muted);font-size:13px;margin-bottom:20px} +.grid{display:grid;grid-template-columns:1fr 1fr;gap:20px;align-items:start} +@media(max-width:1000px){.grid{grid-template-columns:1fr}} +.card{background:var(--panel);border:1px solid var(--border);border-radius:10px;padding:16px;margin-bottom:20px} +.tabs{display:flex;gap:6px;margin-bottom:16px} +.tab{flex:1;padding:9px;border-radius:8px;border:1px solid var(--border);background:var(--panel2);color:var(--muted);cursor:pointer;font-size:14px} +.tab.active{background:var(--accent);color:#fff;border-color:var(--accent);font-weight:600} +label{display:block;font-size:12px;color:var(--muted);margin:10px 0 4px;text-transform:uppercase;letter-spacing:.5px} +input[type=text],textarea,select{width:100%;background:var(--panel2);border:1px solid var(--border);border-radius:6px;color:var(--text);padding:9px;font-size:14px;font-family:inherit} +input:focus,textarea:focus,select:focus{outline:none;border-color:var(--accent)} +.row{display:flex;gap:8px;align-items:center} +.row>*{flex:1} +.emoji-chips{display:flex;flex-wrap:wrap;gap:4px;margin-top:6px} +.emoji-chips button{background:var(--panel2);border:1px solid var(--border);border-radius:6px;padding:4px 7px;font-size:17px;cursor:pointer} +.emoji-chips button:hover{border-color:var(--accent);background:#1f2a3a} +.btn{padding:9px 16px;border-radius:8px;border:none;cursor:pointer;font-size:14px;font-weight:600} +.btn-primary{background:var(--accent);color:#fff} +.btn-green{background:var(--green);color:#fff} +.btn-ghost{background:var(--panel2);color:var(--text);border:1px solid var(--border)} +.btn-red{background:transparent;color:var(--red);border:1px solid var(--red);padding:4px 10px;font-size:12px} +.btn-sm{padding:5px 10px;font-size:12px} +.out{background:#0a0e14;border:1px solid var(--border);border-radius:8px;padding:12px;margin-top:12px;position:relative;word-break:break-all} +.out code{font-family:'SF Mono',Consolas,monospace;font-size:13px;color:#7ee787;display:block;line-height:1.5} +.out .copy-btn{position:absolute;top:8px;right:8px} +.val{font-size:12px;margin-top:6px;min-height:16px} +.val.ok{color:var(--green)} +.val.warn{color:var(--yellow)} +.val.err{color:var(--red)} +/* preview */ +.term{background:#090c12;border:1px solid var(--border);border-radius:10px;overflow:hidden;height:460px;position:relative;display:flex;flex-direction:column} +.term-bar{background:var(--panel2);padding:9px 14px;font-size:12px;color:var(--muted);display:flex;align-items:center;gap:8px;border-bottom:1px solid var(--border)} +.term-bar .dots{display:flex;gap:5px} +.term-bar .dots span{width:11px;height:11px;border-radius:50%;display:block} +.term-body{flex:1;padding:14px;font-family:'SF Mono',Consolas,monospace;font-size:12.5px;color:#9aa7b8;overflow:hidden;line-height:1.7} +.term-body .p{color:#3fb950} +.term-body .c{color:#58a6ff} +.float-layer{position:absolute;right:14px;bottom:14px;display:flex;flex-direction:column-reverse;align-items:flex-end;gap:10px} +/* mock floating elements — styled like the real floating UI */ +.fbtn{width:46px;height:46px;border-radius:50%;background:linear-gradient(135deg,#58a6ff,#bc8cff);color:#fff;border:none;font-size:12px;cursor:pointer;display:flex;align-items:center;justify-content:center;box-shadow:0 4px 14px rgba(0,0,0,.5);transition:transform .15s;padding:4px;text-align:center;line-height:1.1;overflow:hidden} +.fbtn:hover{transform:scale(1.1)} +.fbtn.draft{outline:2px dashed var(--yellow);outline-offset:3px} +.fmenu{display:flex;flex-direction:column;align-items:flex-end} +.fmenu-head{width:46px;height:46px;border-radius:50%;background:linear-gradient(135deg,#3fb950,#58a6ff);color:#fff;border:none;font-size:12px;cursor:pointer;display:flex;align-items:center;justify-content:center;box-shadow:0 4px 14px rgba(0,0,0,.5);padding:4px;text-align:center;line-height:1.1;overflow:hidden} +.fmenu-head:hover{transform:scale(1.1)} +.fmenu-items{display:none;flex-direction:column;align-items:flex-end;gap:8px;margin-bottom:8px} +.fmenu.open .fmenu-items{display:flex} +.fsub{padding:8px 14px;border-radius:20px;background:#21262e;color:var(--text);border:1px solid var(--border);font-size:13px;cursor:pointer;white-space:nowrap} +.fsub:hover{background:#2d333b;border-color:var(--accent)} +.fpanel{background:#1c2230;border:1px solid var(--border);border-radius:10px;padding:12px 14px;min-width:180px;max-width:260px;box-shadow:0 4px 14px rgba(0,0,0,.5)} +.fpanel b{display:block;font-size:13px;margin-bottom:6px;color:#e6edf3} +.fpanel span{font-size:12px;color:var(--muted)} +/* collection */ +.item{display:flex;gap:10px;align-items:center;padding:10px;border:1px solid var(--border);border-radius:8px;margin-bottom:8px;background:var(--panel2)} +.item code{font-family:'SF Mono',Consolas,monospace;font-size:12.5px;color:#7ee787;flex:1;word-break:break-all} +.item .tag{font-size:11px;padding:2px 8px;border-radius:10px;background:#2d333b;color:var(--accent);white-space:nowrap} +/* palette */ +.pal-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(190px,1fr));gap:6px;max-height:320px;overflow-y:auto;padding:4px} +.pal-cmd{background:var(--panel2);border:1px solid var(--border);border-radius:6px;padding:7px 10px;cursor:pointer;font-size:12.5px} +.pal-cmd:hover{border-color:var(--accent);background:#1f2a3a} +.pal-cmd code{font-family:'SF Mono',Consolas,monospace;color:#79c0ff} +.pal-cmd small{display:block;color:var(--muted);font-size:11px;margin-top:2px} +.pal-cmd .cat{float:right;font-size:10px;color:var(--purple);text-transform:uppercase;letter-spacing:.5px} +.note{background:#2d2500;border:1px solid var(--yellow);border-radius:8px;padding:10px 14px;font-size:13px;margin-bottom:16px;line-height:1.5} +.note b{color:var(--yellow)} +.note-ok{background:#04260f;border:1px solid var(--green)} +.note-ok b{color:var(--green)} +#toast{position:fixed;bottom:20px;left:50%;transform:translateX(-50%) translateY(80px);background:var(--green);color:#fff;padding:10px 22px;border-radius:8px;font-size:14px;font-weight:600;transition:transform .25s;z-index:99} +#toast.show{transform:translateX(-50%) translateY(0)} +.menu-item-row{display:grid;grid-template-columns:1fr 1.4fr auto;gap:8px;margin-bottom:8px;align-items:center} +.hint{font-size:11.5px;color:var(--muted);margin-top:6px;line-height:1.5} +.grammar{background:#0a0e14;border:1px solid var(--border);border-radius:8px;padding:12px;font-family:'SF Mono',Consolas,monospace;font-size:12px;line-height:1.8;margin-bottom:14px} +.grammar b{color:var(--accent);font-weight:600} +.grammar em{color:var(--muted);font-style:normal} +.preset{display:block;width:100%;text-align:left;background:var(--panel2);border:1px solid var(--border);border-radius:8px;padding:9px 12px;margin-bottom:6px;cursor:pointer;color:var(--text)} +.preset:hover{border-color:var(--accent)} +.preset b{font-size:13px} +.preset small{display:block;color:var(--muted);font-size:11.5px;margin-top:2px;word-break:break-all} +</style> +</head> +<body> + +<h1>🎨 Floating UI Designer <span style="color:var(--green);font-size:14px">v3 — quoting, import & registry fixed</span></h1> +<div class="sub">Design floating buttons, menus & panels → live preview → copy validated <code style="color:#79c0ff">floating-add</code> commands for the WebX11 newOS4 terminal</div> + +<div class="note note-ok"> + ✅ <b>Syntax verified against FloatingModule source code (2026-10-17):</b> menu items are <b>space-separated</b> and use <b>two colons</b> — <code>btn:Label:command</code>.<br> + Old pipe syntax (<code>btn:Label|cmd</code>) was <b>wrong</b> and silently produced broken menus. Multi-word labels/commands are auto-quoted here (<code>btn:"My Label":"cmd with args"</code>) — tested live in the real terminal and confirmed working.<br> + <b>v3 (2026-10-17):</b> auto-quoting, import/preset parsing, ID slugs and the full 344-command registry verified against the live terminal (v4.6.26). +</div> + +<div class="grid"> + <!-- ============ LEFT: BUILDER ============ --> + <div> + <div class="card"> + <h2>1 · Real grammar (from the module's parser)</h2> + <div class="grammar"> + <b>button:</b> floating-add btn <id> "<em>label</em>" <command> <em>[args...]</em><br> + <b>menu:</b> floating-add menu <id> "<em>label</em>" btn:Label:cmd btn:Label2:cmd2 ...<br> + <b>panel:</b> floating-add panel <id> "<em>title</em>" "<em>content</em>"<br> + <span style="color:var(--red)">⚠</span> an item with ONE colon (btn:Files) is dropped → "No valid sub-buttons"<br> + <span style="color:var(--red)">⚠</span> menu items may NOT contain bare spaces — quote them: btn:"A B":ls + </div> + + <h2>2 · Design your element</h2> + <div class="tabs" id="type-tabs"> + <button class="tab active" data-type="btn">🔘 Button</button> + <button class="tab" data-type="menu">📋 Menu</button> + <button class="tab" data-type="panel">📄 Panel</button> + </div> + + <!-- BUTTON FORM --> + <div id="form-btn" style=""> + <label for="btn-label">Label (pick an emoji or type)</label> + <input type="text" id="btn-label" placeholder="🆕 or 🆕 New Session" value="🆕"> + <div class="emoji-chips" id="emoji-chips"><button>🆕</button><button>✅</button><button>⚙️</button><button>🌐</button><button>📋</button><button>🗂️</button><button>📁</button><button>🧠</button><button>🖥️</button><button>📊</button><button>🔍</button><button>🔧</button><button>🚀</button><button>💾</button><button>🔐</button><button>⚡</button><button>🎨</button><button>🧹</button><button>⏹️</button><button>❤️</button><button>🤖</button><button>💬</button><button>🧪</button><button>☁️</button></div> + <label for="btn-id">ID <span style="text-transform:none">(single word, no spaces)</span></label> + <input type="text" id="btn-id" placeholder="my-button" value="my-button"> + <label for="btn-cmd">Command <span style="text-transform:none">(click a command in the palette below to insert)</span></label> + <input type="text" id="btn-cmd" placeholder="status (args allowed: ai-config confirm-timeout 0)" value="status"> + </div> + + <!-- MENU FORM --> + <div id="form-menu" style="display: none;"> + <label for="menu-label">Menu label (header)</label> + <input type="text" id="menu-label" placeholder="🗂️ Files"> + <div class="emoji-chips" id="emoji-chips2"><button>🆕</button><button>✅</button><button>⚙️</button><button>🌐</button><button>📋</button><button>🗂️</button><button>📁</button><button>🧠</button><button>🖥️</button><button>📊</button><button>🔍</button><button>🔧</button><button>🚀</button><button>💾</button><button>🔐</button><button>⚡</button><button>🎨</button><button>🧹</button><button>⏹️</button><button>❤️</button><button>🤖</button><button>💬</button><button>🧪</button><button>☁️</button></div> + <label for="menu-id">Menu ID</label> + <input type="text" id="menu-id" placeholder="files-menu"> + <label>Items — each needs Label AND Command</label> + <div id="menu-items"><div class="menu-item-row"><input type="text" placeholder="Label"><input type="text" placeholder="Command (e.g. ls)"><button class="btn btn-red btn-sm">✕</button></div><div class="menu-item-row"><input type="text" placeholder="Label"><input type="text" placeholder="Command (e.g. ls)"><button class="btn btn-red btn-sm">✕</button></div></div> + <button class="btn btn-ghost btn-sm" id="menu-add-item">+ Add item</button> + <div class="hint">Item labels/commands with spaces are auto-quoted → btn:"My Label":"cmd with args" (verified working). Items with only one colon will be rejected here before you ever install them.</div> + </div> + + <!-- PANEL FORM --> + <div id="form-panel" style="display: none;"> + <label for="panel-label">Panel title</label> + <input type="text" id="panel-label" placeholder="📊 Status"> + <label for="panel-id">Panel ID</label> + <input type="text" id="panel-id" placeholder="status-p"> + <label for="panel-content">Content (markdown-lite: **bold** *italic* `code` [links](https://…)) — line breaks collapse to spaces</label> + <textarea id="panel-content" rows="3" placeholder="CPU: 2% | RAM: 45%"></textarea> + </div> + </div> + + <div class="card"> + <h2>3 · Presets (all commands verified real)</h2> + <div id="presets"><button class="preset"><b>🗂️ Files menu</b><small>list · trash · concept tree (this one is live in your UI)</small></button><button class="preset"><b>🧠 AI menu</b><small>ask · clear · context · debug</small></button><button class="preset"><b>🖥️ System menu</b><small>status · mem · logs · help</small></button><button class="preset"><b>🌐 Web menu</b><small>search · images · fetch · extract</small></button><button class="preset"><b>📊 Status panel</b><small>markdown-lite panel</small></button><button class="preset"><b>⚙️ Config button</b><small>button with arguments (user-verified)</small></button><button class="preset"><b>🧪 Quoted items demo</b><small>multi-word label + multi-word command</small></button></div> + <button class="btn btn-ghost btn-sm" id="load-current">📥 Import your current live setup (6 elements)</button> + </div> + + <div class="card"> + <h2>4 · Command palette</h2> + <input type="text" id="pal-search" placeholder="Search commands… (e.g. file, ai, web)"> + <div class="pal-grid" id="pal-grid"><div class="pal-cmd"><span class="cat">ai</span>✓ <code>ai</code><small>Ask the AI assistant</small></div><div class="pal-cmd"><span class="cat">ai</span>✓ <code>ask</code><small>Ask the AI assistant</small></div><div class="pal-cmd"><span class="cat">ai</span>✓ <code>ai-clear</code><small>Clear AI context</small></div><div class="pal-cmd"><span class="cat">ai</span>✓ <code>ai-system</code><small>AI system prompt</small></div><div class="pal-cmd"><span class="cat">ai</span>✓ <code>ai-context</code><small>AI context info</small></div><div class="pal-cmd"><span class="cat">ai</span>✓ <code>ai-allow</code><small>Allow AI command</small></div><div class="pal-cmd"><span class="cat">ai</span>✓ <code>ai-deny</code><small>Deny AI command</small></div><div class="pal-cmd"><span class="cat">ai</span>✓ <code>ai-ignore</code><small>Ignore AI command</small></div><div class="pal-cmd"><span class="cat">ai</span>✓ <code>ai-config</code><small>AI configuration (user-verified)</small></div><div class="pal-cmd"><span class="cat">ai</span>✓ <code>chat</code><small>Open chat studio</small></div><div class="pal-cmd"><span class="cat">ai</span>✓ <code>debug</code><small>Open debug panel</small></div><div class="pal-cmd"><span class="cat">ai</span>✓ <code>debug-ai</code><small>AI debug panel</small></div><div class="pal-cmd"><span class="cat">agents</span>✓ <code>agent-list</code><small>List registered agents</small></div><div class="pal-cmd"><span class="cat">agents</span>✓ <code>agent-status</code><small>Agent status</small></div><div class="pal-cmd"><span class="cat">agents</span>✓ <code>agent-register</code><small>Register agent</small></div><div class="pal-cmd"><span class="cat">agents</span>✓ <code>agent-unregister</code><small>Unregister agent</small></div><div class="pal-cmd"><span class="cat">agents</span>✓ <code>contract-list</code><small>List A2A contracts</small></div><div class="pal-cmd"><span class="cat">agents</span>✓ <code>orchestrate</code><small>Run orchestrator</small></div><div class="pal-cmd"><span class="cat">agents</span>✓ <code>orchestrator-status</code><small>Orchestrator status</small></div><div class="pal-cmd"><span class="cat">agents</span>✓ <code>orchestrator-spawn</code><small>Spawn orchestrator</small></div><div class="pal-cmd"><span class="cat">agents</span>✓ <code>orchestrator-kill</code><small>Kill orchestrator</small></div><div class="pal-cmd"><span class="cat">ai</span>✓ <code>webxai</code><small>WebX AI alias</small></div><div class="pal-cmd"><span class="cat">ai</span>✓ <code>wx</code><small>WebX AI alias</small></div><div class="pal-cmd"><span class="cat">files</span>✓ <code>ls</code><small>List current directory</small></div><div class="pal-cmd"><span class="cat">files</span>✓ <code>cat</code><small>Read a file</small></div><div class="pal-cmd"><span class="cat">files</span>✓ <code>put</code><small>Write a file</small></div><div class="pal-cmd"><span class="cat">files</span>✓ <code>rm</code><small>Delete a file</small></div><div class="pal-cmd"><span class="cat">files</span>✓ <code>trash</code><small>Trash a file</small></div><div class="pal-cmd"><span class="cat">files</span>✓ <code>trash-dir</code><small>Trash a directory</small></div><div class="pal-cmd"><span class="cat">files</span>✓ <code>ls-trash</code><small>List trash</small></div><div class="pal-cmd"><span class="cat">files</span>✓ <code>empty-trash</code><small>Empty trash</small></div><div class="pal-cmd"><span class="cat">files</span>✓ <code>nvfs-index</code><small>Build neural VFS index</small></div><div class="pal-cmd"><span class="cat">files</span>✓ <code>nvfs-status</code><small>Neural VFS status</small></div><div class="pal-cmd"><span class="cat">files</span>✓ <code>nvfs-resolve</code><small>Semantic file search</small></div><div class="pal-cmd"><span class="cat">files</span>✓ <code>nvfs-tree</code><small>Concept tree</small></div><div class="pal-cmd"><span class="cat">files</span>✓ <code>save</code><small>Save VFS snapshot</small></div><div class="pal-cmd"><span class="cat">files</span>✓ <code>load</code><small>Load VFS snapshot</small></div><div class="pal-cmd"><span class="cat">files</span>✓ <code>import</code><small>Import VFS</small></div><div class="pal-cmd"><span class="cat">system</span>✓ <code>help</code><small>Command reference</small></div><div class="pal-cmd"><span class="cat">system</span>✓ <code>echo</code><small>Print text</small></div><div class="pal-cmd"><span class="cat">system</span>✓ <code>mem</code><small>Memory usage</small></div><div class="pal-cmd"><span class="cat">system</span>✓ <code>status</code><small>System status</small></div><div class="pal-cmd"><span class="cat">system</span>✓ <code>preview</code><small>Preview a file</small></div><div class="pal-cmd"><span class="cat">system</span>✓ <code>logs</code><small>System logs</small></div><div class="pal-cmd"><span class="cat">system</span>✓ <code>clear</code><small>Clear terminal</small></div><div class="pal-cmd"><span class="cat">system</span>✓ <code>cd</code><small>Change directory</small></div><div class="pal-cmd"><span class="cat">system</span>✓ <code>modules</code><small>List modules</small></div><div class="pal-cmd"><span class="cat">system</span>✓ <code>listmods</code><small>List modules</small></div><div class="pal-cmd"><span class="cat">system</span>✓ <code>loadmod</code><small>Load a module</small></div><div class="pal-cmd"><span class="cat">system</span>✓ <code>removemod</code><small>Remove a module</small></div><div class="pal-cmd"><span class="cat">system</span>✓ <code>startup-modules</code><small>Manage startup modules</small></div><div class="pal-cmd"><span class="cat">system</span>✓ <code>config</code><small>Config manager</small></div><div class="pal-cmd"><span class="cat">system</span>✓ <code>config-list</code><small>List config</small></div><div class="pal-cmd"><span class="cat">system</span>✓ <code>config-set</code><small>Set config key</small></div><div class="pal-cmd"><span class="cat">system</span>✓ <code>config-export</code><small>Export config</small></div><div class="pal-cmd"><span class="cat">system</span>✓ <code>factory-reset</code><small>Full reset (destructive)</small></div><div class="pal-cmd"><span class="cat">tools</span>✓ <code>js</code><small>Run JavaScript</small></div><div class="pal-cmd"><span class="cat">tools</span>✓ <code>js-run</code><small>Run JavaScript</small></div><div class="pal-cmd"><span class="cat">tools</span>✓ <code>node</code><small>Run JavaScript</small></div><div class="pal-cmd"><span class="cat">tools</span>✓ <code>gui</code><small>Simple GUI</small></div><div class="pal-cmd"><span class="cat">tools</span>✓ <code>gui-button</code><small>GUI button</small></div><div class="pal-cmd"><span class="cat">tools</span>✓ <code>gui-input</code><small>GUI input</small></div><div class="pal-cmd"><span class="cat">tools</span>✓ <code>gui-text</code><small>GUI text</small></div><div class="pal-cmd"><span class="cat">tools</span>✓ <code>gui-clear</code><small>Clear GUI</small></div><div class="pal-cmd"><span class="cat">tools</span>✓ <code>gui-run</code><small>Run GUI actions</small></div><div class="pal-cmd"><span class="cat">tools</span>✓ <code>ollama</code><small>Ollama manager</small></div><div class="pal-cmd"><span class="cat">tools</span>✓ <code>ollamaconnect</code><small>Connect Ollama</small></div><div class="pal-cmd"><span class="cat">tools</span>✓ <code>ollamamodels</code><small>List Ollama models</small></div><div class="pal-cmd"><span class="cat">tools</span>✓ <code>ollamasetmodel</code><small>Set Ollama model</small></div><div class="pal-cmd"><span class="cat">tools</span>✓ <code>models</code><small>List LLM models</small></div><div class="pal-cmd"><span class="cat">tools</span>✓ <code>model-info</code><small>Model info</small></div><div class="pal-cmd"><span class="cat">tools</span>✓ <code>analyze</code><small>Media analyze</small></div><div class="pal-cmd"><span class="cat">agents</span>✓ <code>lease-list</code><small>List leases</small></div><div class="pal-cmd"><span class="cat">network</span>✓ <code>fetch</code><small>Fetch a URL</small></div><div class="pal-cmd"><span class="cat">network</span>✓ <code>web</code><small>Fetch a URL</small></div><div class="pal-cmd"><span class="cat">network</span>✓ <code>extract</code><small>Extract URL content</small></div><div class="pal-cmd"><span class="cat">network</span>✓ <code>search</code><small>Web search</small></div><div class="pal-cmd"><span class="cat">network</span>✓ <code>s</code><small>Web search (short)</small></div><div class="pal-cmd"><span class="cat">network</span>✓ <code>images</code><small>Image search</small></div><div class="pal-cmd"><span class="cat">network</span>✓ <code>videos</code><small>Video search</small></div><div class="pal-cmd"><span class="cat">network</span>✓ <code>server</code><small>Server monitor</small></div><div class="pal-cmd"><span class="cat">network</span>✓ <code>servers</code><small>List servers</small></div><div class="pal-cmd"><span class="cat">network</span>✓ <code>server-check</code><small>Check server</small></div><div class="pal-cmd"><span class="cat">network</span>✓ <code>monitor</code><small>Monitor system</small></div><div class="pal-cmd"><span class="cat">network</span>✓ <code>system-health</code><small>System health</small></div><div class="pal-cmd"><span class="cat">security</span>✓ <code>secret-list</code><small>List secrets</small></div><div class="pal-cmd"><span class="cat">security</span>✓ <code>secret-status</code><small>Secret vault status</small></div><div class="pal-cmd"><span class="cat">security</span>✓ <code>secret-set</code><small>Store a secret</small></div><div class="pal-cmd"><span class="cat">security</span>✓ <code>secret-get</code><small>Read a secret</small></div><div class="pal-cmd"><span class="cat">security</span>✓ <code>secret-lock</code><small>Lock vault</small></div><div class="pal-cmd"><span class="cat">security</span>✓ <code>secret-unlock</code><small>Unlock vault</small></div><div class="pal-cmd"><span class="cat">security</span>✓ <code>security</code><small>Security info</small></div><div class="pal-cmd"><span class="cat">security</span>✓ <code>security-level</code><small>Security level</small></div><div class="pal-cmd"><span class="cat">security</span>✓ <code>cmd-allow</code><small>Allow a command</small></div><div class="pal-cmd"><span class="cat">security</span>✓ <code>cmd-deny</code><small>Deny a command</small></div><div class="pal-cmd"><span class="cat">ui</span>✓ <code>floating-add</code><small>Add floating element</small></div><div class="pal-cmd"><span class="cat">ui</span>✓ <code>floating-remove</code><small>Remove element</small></div><div class="pal-cmd"><span class="cat">ui</span>✓ <code>floating-list</code><small>List elements</small></div><div class="pal-cmd"><span class="cat">ui</span>✓ <code>floating-pos</code><small>Position element</small></div><div class="pal-cmd"><span class="cat">ui</span>✓ <code>floating-toggle</code><small>Show/hide element</small></div><div class="pal-cmd"><span class="cat">ui</span>✓ <code>floating-clear</code><small>Remove ALL elements</small></div><div class="pal-cmd"><span class="cat">files</span>⚠ <code>files</code><small>File manager UI</small></div><div class="pal-cmd"><span class="cat">files</span>⚠ <code>edit</code><small>Open file editor</small></div><div class="pal-cmd"><span class="cat">files</span>⚠ <code>find</code><small>Find files</small></div><div class="pal-cmd"><span class="cat">ui</span>⚠ <code>theme</code><small>Change theme</small></div><div class="pal-cmd"><span class="cat">ui</span>⚠ <code>mindmap</code><small>Create mindmap</small></div><div class="pal-cmd"><span class="cat">ui</span>⚠ <code>svg</code><small>Render SVG</small></div><div class="pal-cmd"><span class="cat">tools</span>⚠ <code>calc</code><small>Calculator</small></div><div class="pal-cmd"><span class="cat">tools</span>⚠ <code>date</code><small>Show date</small></div><div class="pal-cmd"><span class="cat">tools</span>⚠ <code>env</code><small>Environment vars</small></div><div class="pal-cmd"><span class="cat">tools</span>⚠ <code>which</code><small>Locate command</small></div><div class="pal-cmd"><span class="cat">tools</span>⚠ <code>diff</code><small>Diff files</small></div><div class="pal-cmd"><span class="cat">tools</span>⚠ <code>gzip</code><small>Gzip file</small></div><div class="pal-cmd"><span class="cat">tools</span>⚠ <code>gunzip</code><small>Ungzip file</small></div><div class="pal-cmd"><span class="cat">tools</span>⚠ <code>encrypt</code><small>Encrypt file</small></div><div class="pal-cmd"><span class="cat">tools</span>⚠ <code>decrypt</code><small>Decrypt file</small></div><div class="pal-cmd"><span class="cat">network</span>⚠ <code>webdav-status</code><small>WebDAV status</small></div><div class="pal-cmd"><span class="cat">network</span>⚠ <code>webdav-ls</code><small>WebDAV listing</small></div><div class="pal-cmd"><span class="cat">network</span>⚠ <code>webdav-push</code><small>Push to WebDAV</small></div><div class="pal-cmd"><span class="cat">network</span>⚠ <code>webdav-pull</code><small>Pull from WebDAV</small></div><div class="pal-cmd"><span class="cat">system</span>⚠ <code>cron-list</code><small>List cron jobs</small></div><div class="pal-cmd"><span class="cat">system</span>⚠ <code>wpm</code><small>Package manager</small></div><div class="pal-cmd"><span class="cat">tools</span>⚠ <code>voice</code><small>Voice control</small></div><div class="pal-cmd"><span class="cat">system</span>⚠ <code>alias</code><small>Manage aliases</small></div></div> + <div class="hint">✓ = real terminal command (all default modules auto-load) · ⚠ = unknown, routes to the AI · click to insert into the form · search is case-insensitive.</div> + </div> + + <div class="card"> + <h2>5 · Import existing command</h2> + <textarea id="import-area" rows="3" placeholder="Paste floating-add lines here, e.g. floating-add menu files-menu "🗂️ Files" btn:List:ls"></textarea> + <div style="margin-top:8px"><button class="btn btn-primary btn-sm" id="import-btn">Parse & load into builder</button></div> + </div> + </div> + + <!-- ============ RIGHT: PREVIEW + OUTPUT ============ --> + <div> + <div class="card"> + <h2>Live preview</h2> + <div class="term"> + <div class="term-bar"><div class="dots"><span style="background:#f85149"></span><span style="background:#d29922"></span><span style="background:#3fb950"></span></div>newOS4 — mock terminal</div> + <div class="term-body"> + <span class="p">webx11@newos4</span>:<span class="c">~</span>$ floating-list<br> + <span style="color:var(--muted)">… your draft renders bottom-right (dashed = draft) …</span> + </div> + <div class="float-layer" id="float-layer"><button class="fbtn draft">🗂️New</button></div> + </div> + <div class="hint">Menus expand on click in the preview. The real UI also lets you drag elements by their header/button; position is saved automatically.</div> + </div> + + <div class="card"> + <h2>Generated command + validation</h2> + <div class="out"> + <code id="out-code">floating-add btn my-button "🆕" status</code> + <button class="btn btn-green btn-sm copy-btn" id="copy-main">📋 Copy</button> + </div> + <div class="val ok" id="out-val" aria-live="polite">✓ "status" is a real terminal command</div> + <div style="margin-top:10px" class="row"> + <button class="btn btn-primary" id="add-coll">➕ Add to collection</button> + <button class="btn btn-ghost" id="install-now">🚀 Copy & run in terminal</button> + <a class="btn btn-ghost" href="https://webx11.duckdns.org/spaces/newos4/main.html" target="_blank" rel="noopener" style="text-align:center;text-decoration:none">🖥 Open newOS4 terminal ↗</a> + </div> + </div> + + <div class="card"> + <h2>Collection <span style="font-size:12px;color:var(--muted)" id="coll-count">(0)</span></h2> + <div id="coll-list"></div> + <div class="row" style="margin-top:10px"> + <button class="btn btn-green" id="copy-all">📋 Copy all</button> + <button class="btn btn-ghost" id="coll-clear">🗑 Clear</button> + </div> + <div class="hint">Paste the copied block into the newOS4 terminal to install everything at once.</div> + </div> + </div> +</div> + +<div id="toast" class=""></div> + +<script> +(function(){ +'use strict'; +var $ = function(id){ return document.getElementById(id); }; + +/* ───────── Command registry — built from live list_available_modules (2026-10-17) ───────── + [command, description, category, auto] — auto=1 available immediately, auto=0 needs loadmod */ +var CMDS = [ +['a2a-ack','Acknowledge a message with result','agents',1], +['a2a-list','Show pending A2A messages across all agents','agents',1], +['a2a-poll','Poll for messages addressed to an agent (default: current or "user")','agents',1], +['a2a-post','Post an A2A message','agents',1], +['a2a-prune','Clean up expired and old completed messages','agents',1], +['a2a-reject','Reject a message with error','agents',1], +['a2a-send','Send an A2A message to an agent\u2019s inbox','agents',1], +['a2a-status','Show scratchpad stats per agent','agents',1], +['agent-list','Terminal command','agents',1], +['agent-register','Terminal command','agents',1], +['agent-status','Terminal command','agents',1], +['agent-unregister','Terminal command','agents',1], +['consensus','Start a consensus round: spawn 3 agents, debate the question, resolve by majority','agents',1], +['consensus-history','Show past consensus rounds and their resolutions','agents',1], +['consensus-status','Show current/active consensus round status','agents',1], +['contract-list','Terminal command','agents',1], +['contract-register','Terminal command','agents',1], +['lease-create','Terminal command','agents',1], +['lease-list','Terminal command','agents',1], +['lease-renew','Terminal command','agents',1], +['lease-revoke','Terminal command','agents',1], +['memgraph-agent','Show namespaced nodes for an agent','agents',1], +['memgraph-decay-config','Configure decay policy (default: 30d, 3 uses)','agents',1], +['memgraph-export','Export graph as JSON','agents',1], +['memgraph-learn','Add a fact to the memory graph','agents',1], +['memgraph-prune','Remove stale low-weight edges (>30d, <3 accesses)','agents',1], +['memgraph-search','Find nodes by label or type','agents',1], +['memgraph-session','Show graph nodes for a session (default: today)','agents',1], +['memgraph-stats','Most connected nodes, strongest edges, cluster info','agents',1], +['memgraph-status','Show node/edge counts, last access','agents',1], +['orchestrate','Delegate a task to an available agent (auto-spawns if needed)','agents',1], +['orchestrate-decompose','Preview how a task would be decomposed into sub-tasks','agents',1], +['orchestrate-parallel','Decompose and delegate sub-tasks in parallel (auto-splits on "and"/"then")','agents',1], +['orchestrate-result','Show the last parallel batch results (stored in VFS)','agents',1], +['orchestrator-config','Terminal command','agents',1], +['orchestrator-kill','Terminate an agent and revoke leases','agents',1], +['orchestrator-list','List all spawned agents with full details','agents',1], +['orchestrator-purge','Remove dead agents (>60s since heartbeat)','agents',1], +['orchestrator-restart-policy','Set per-agent restart policy','agents',1], +['orchestrator-schedule','Schedule a recurring task delegation','agents',1], +['orchestrator-schedules','List all active recurring schedules','agents',1], +['orchestrator-sleep','Suspend agent to VFS','agents',1], +['orchestrator-spawn','Manually spawn a new agent','agents',1], +['orchestrator-status','Show agent pool status and config','agents',1], +['orchestrator-unschedule','Remove a recurring schedule','agents',1], +['orchestrator-wake','Restore agent from VFS sleep','agents',1], +['pipeline','Autonomous CI/CD: test agents/queue/modules, deploy (test→auto-fix→verify), history (sh...','agents',1], +['world-model','Show full world model as structured output','agents',1], +['world-model-ask','Query the model (e.g. "what changed since yesterday")','agents',1], +['world-model-config','Tune: refresh-interval, max-projects','agents',1], +['world-model-refresh','Force re-synthesis from all subsystems','agents',1], +['world-model-summary','One-line system self-awareness statement','agents',1], +['agent-loops','Manage agent loop profiles','ai',1], +['ai','Ask the AI assistant anything','ai',1], +['ai-allow','Confirm pending AI action','ai',1], +['ai-clear','Clear conversation context','ai',1], +['ai-compress','Terminal command','ai',1], +['ai-config','View or set AI loop config (max-turns, max-context, confirm-timeout(0=disabled))','ai',1], +['ai-context','Show current conversation context','ai',1], +['ai-deny','Deny pending AI action','ai',1], +['ai-ignore','Suppress validator warnings for this session','ai',1], +['ai-model','Show or set AI model','ai',1], +['ai-stop','Cancel current AI operation','ai',1], +['ai-system','Set system prompt','ai',1], +['archive-list','Terminal command','ai',1], +['archive-read','Terminal command','ai',1], +['ask','Alias for ai command','ai',1], +['bench','Run benchmark suite on a profile','ai',1], +['bench-compare','Compare benchmark results of two profiles','ai',1], +['chat','default','ai',1], +['commit-approve','Terminal command','ai',1], +['commit-deny','Terminal command','ai',1], +['commit-status','Terminal command','ai',1], +['context','Set or view current context profile','ai',1], +['context-auto','Toggle auto-routing (detects intent from user input)','ai',1], +['context-delete','Delete a context profile','ai',1], +['context-new','Create a new context profile','ai',1], +['contexts','List all available context profiles','ai',1], +['debug','Toggle system debug console (logs, fetch, errors)','ai',1], +['debug-ai','Toggle AI tool-call debug panel','ai',1], +['debug-ai-last','Terminal command','ai',1], +['debug-ai-log','Terminal command','ai',1], +['debug-ai-send','Terminal command','ai',1], +['guardrail','Terminal command','ai',1], +['guardrail-add','Terminal command','ai',1], +['guardrail-remove','Terminal command','ai',1], +['mem-debug','Show memory breakdown by component','ai',1], +['model-delete','Delete a model from local storage','ai',1], +['model-info','Show details about a specific model','ai',1], +['model-pull','Pull a model from Ollama registry','ai',1], +['model-run','Run inference with a model','ai',1], +['models','List all available Ollama models (table format)','ai',1], +['ollama','Open the Ollama model manager UI','ai',1], +['ollamaconnect','Connect to the Ollama server','ai',1], +['ollamadisconnect','Disconnect from the Ollama server','ai',1], +['ollamamodels','List all available models from Ollama','ai',1], +['ollamasetmodel','Set the active Ollama model','ai',1], +['ollamasetproxy','View or change the Ollama endpoint URL','ai',1], +['reset-mem','Reset AI memory (logs, context, profile, guardrails) without losing VFS or config','ai',1], +['shadow','Terminal command','ai',1], +['shadow-commit','Terminal command','ai',1], +['shadow-rollback','Terminal command','ai',1], +['smart-ask','Ask a question using the active context','ai',1], +['vaultmind-connect','Connect VaultMind wallet using hyperliquid_key from SecretVault','ai',1], +['vaultmind-query','Query VaultMind API','ai',1], +['vaultmind-status','Check VaultMind session status','ai',1], +['wallet-lookup','Look up Hyperliquid wallet summary','ai',1], +['webx','Alias for webxai','ai',1], +['webxai','Send a question to the AI assistant','ai',1], +['wx','Alias for webxai','ai',1], +['base64','Base64 encode/decode','files',1], +['basename','Strip directory from path','files',1], +['cal','Display an ASCII calendar','files',1], +['cat','Display file contents','files',1], +['cd','Change current directory; cd / to go to root','files',1], +['cp','Copy file or directory','files',1], +['cut','Extract column from file','files',1], +['date','Show current date/time (ISO, local, UTC, timestamp)','files',1], +['decrypt','Decrypt a .enc file','files',1], +['diff','Show differences between two VFS files or compare module source (usage: diff <file1> <f...','files',1], +['dirname','Strip filename from path','files',1], +['download','Download a file to your device','files',1], +['du','Estimate file/directory size','files',1], +['echo','Print text to the terminal','files',1], +['edit','Open the code editor for a VFS file (usage: edit <path>)','files',1], +['empty-trash','Permanently delete all trash items','files',1], +['encrypt','AES-256-GCM encrypt a file','files',1], +['env','List environment variables (webx11_ keys + user profile)','files',1], +['expand','Toggle full-screen terminal output','files',1], +['export','Set a shell variable','files',1], +['files','Open the file manager overlay with VFS directory listing, preview, encrypt, zip','files',1], +['find','Search files by name pattern','files',1], +['grep','Search file content for pattern','files',1], +['gunzip','Decompress a .gz file (binary-safe)','files',1], +['gzip','Compress a VFS file to .gz','files',1], +['head','Show first N lines (default: 10)','files',1], +['hostname','Show host/user-agent info','files',1], +['ls','List files and directories','files',1], +['ls-trash','List items in trash','files',1], +['mkdir','Create a directory; -p creates parent paths','files',1], +['mv','Move file or directory','files',1], +['nvfs-alias','Manage concept aliases','files',1], +['nvfs-cancel','Cancel running index operation','files',1], +['nvfs-config','View or set Neural VFS config (fuzzy-limit, tree-files, tree-concepts, resolve-files, d...','files',1], +['nvfs-index','Scan VFS and build semantic concept index','files',1], +['nvfs-index-graph','Terminal command','files',1], +['nvfs-resolve','Find files related to a concept','files',1], +['nvfs-save','Force-save current in-memory index to VFS (survives refresh)','files',1], +['nvfs-status','Show semantic index stats and last scan time','files',1], +['nvfs-sync','Incremental background sync (auto-sync dirty files)','files',1], +['nvfs-tree','Show concept-to-file mapping as a tree','files',1], +['preview','Render an HTML file from the VFS in a preview pane','files',1], +['printf','Formatted print (%s, %d, %f, \u005Cn, \u005Ct)','files',1], +['put','Create or overwrite a file','files',1], +['pwd','Print current working directory','files',1], +['rm','Remove a file (moved to trash); use -r/-rf for directories','files',1], +['rmdir','Remove a directory','files',1], +['script','Run a shell script from VFS','files',1], +['sed','Stream editor; s/pat/rep/[g][i][p], /pat/d, Nd, $d; -i write in place; -n quiet mode','files',1], +['seq','Generate sequence of numbers','files',1], +['session-delete','Delete a saved session','files',1], +['session-export','Terminal command','files',1], +['session-import','Terminal command','files',1], +['session-list','List all saved sessions','files',1], +['session-load','Load a saved session','files',1], +['session-new','Start a fresh session (auto-saves current, clears AI context)','files',1], +['session-save','Save current session (path + terminal history)','files',1], +['set','List all shell variables','files',1], +['sh','script','files',1], +['sleep','Delay execution with countdown','files',1], +['sort','Sort lines of a file alphabetically','files',1], +['tail','Show last N lines; -f follows live changes (Ctrl+C/Esc to stop)','files',1], +['tar','Bundle a VFS directory into a tar archive (gzip if .gz)','files',1], +['touch','Create empty file or update timestamp','files',1], +['trash','Move a file to trash','files',1], +['trash-dir','Move a directory to trash','files',1], +['tree','Display directory structure as a tree','files',1], +['uniq','Filter adjacent duplicate lines from a file','files',1], +['unset','Remove a shell variable','files',1], +['untar','Extract a tar archive into VFS (auto-detects gzip)','files',1], +['upload','Upload a file from your device','files',1], +['vfs-export','Export the entire virtual filesystem as JSON','files',1], +['vfs-import','Import a previously exported VFS JSON file','files',1], +['vfs-size','Terminal command','files',1], +['wc','Count lines, words, and bytes','files',1], +['wget','Download a URL and save to VFS','files',1], +['which','Locate a command in the module registry','files',1], +['whoami','Show current user name','files',1], +['yes','no','files',1], +['extract','Extract readable article content (title + body text) from a URL','network',1], +['fetch','Fetch URL (raw HTML/text); --post enables POST with body, --type sets Content-Type, --r...','network',1], +['images','Search for images','network',1], +['s','No special action needed','network',1], +['search','Terminal command','network',1], +['videos','Search for videos','network',1], +['webdav-backup','Backup entire VFS to WebDAV as a dated JSON snapshot','network',1], +['webdav-backup-list','List available backup files on WebDAV','network',1], +['webdav-browser','Modern WebDAV file-tree browser with bulk operations','network',1], +['webdav-config','Configure WebDAV. No args = auto-detect from current page URL','network',1], +['webdav-login','Open the login panel (Telegram or password) in THIS browser and configure WebDAV','network',1], +['webdav-ls','List files/directories on the WebDAV remote','network',1], +['webdav-mkdir','Create a remote directory','network',1], +['webdav-move','Alias for webdav-rename','network',1], +['webdav-pull','Download a WebDAV file to VFS','network',1], +['webdav-push','Upload a VFS file to WebDAV','network',1], +['webdav-rename','Rename or move a remote file/dir','network',1], +['webdav-restore','Restore VFS from a WebDAV backup file','network',1], +['webdav-rm','Delete a remote file or directory','network',1], +['webdav-status','Show current WebDAV configuration status','network',1], +['alias','List, set, or show aliases (usage: alias [name=value])','system',1], +['bug-report','Generate a markdown bug report to /tmp/ for debugging','system',1], +['checkpoint','Create a VFS snapshot (name defaults to ISO timestamp)','system',1], +['checkpoint-del','Delete a VFS checkpoint','system',1], +['checkpoint-list','List all VFS checkpoints','system',1], +['clear','Terminal command','system',1], +['cmd-allow','Terminal command','system',1], +['cmd-deny','Terminal command','system',1], +['config','Show all config or a specific key','system',1], +['config-delete','Delete a configuration key','system',1], +['config-export','Export all config as JSON','system',1], +['config-list','List all configuration keys','system',1], +['config-reset','Reset all configuration to defaults','system',1], +['config-set','Set a configuration value','system',1], +['cron-clear','Stop all cron jobs','system',1], +['cron-every','Schedule a command to run every N seconds','system',1], +['cron-list','List all active cron jobs','system',1], +['cron-stop','Stop a specific cron job','system',1], +['dashboard','Terminal command','system',1], +['debug-analyze','Deep analysis of a specific error','system',1], +['debug-fix','Suggest and optionally apply fix for an error','system',1], +['debug-health','Check supervisor module health status','system',1], +['debug-history','Show last N errors (default: 20)','system',1], +['debug-stats','Show error summary (total, unique, clusters, top issues)','system',1], +['debug-sync','Sync errors to backend API (or set sync URL)','system',1], +['debug-sync-url','View or set the backend sync endpoint','system',1], +['debug-trends','Error frequency over time (default: 7)','system',1], +['doctor','Run all health checks, rank issues, show top 3 warnings','system',1], +['errors','Show last n ERROR log entries (default: 20)','system',1], +['factory-reset','Alias for reset all','system',1], +['factory-reset-old','Terminal command','system',1], +['font-size','Set terminal font size (persists across sessions)','system',1], +['gui','Show the GUI builder overview','system',1], +['gui-button','Add a clickable button','system',1], +['gui-clear','Clear all GUI elements','system',1], +['gui-input','Add a text input field','system',1], +['gui-run','Run a terminal command from the GUI','system',1], +['gui-text','Add a text element','system',1], +['health-check','Audit VFS size, modules, logs, trash, skills, tool failures','system',1], +['help','Show all commands or usage for a specific command','system',1], +['history','Terminal command','system',1], +['learn-skill','Reflect on topic, draft a skill from experience','system',1], +['listmods','List all loaded modules with status','system',1], +['load-local','Terminal command','system',1], +['loadlocal','Terminal command','system',1], +['loadmod','Load a module from a URL','system',1], +['logrotate','Rotate current.log to a dated archive (auto-cleans old)','system',1], +['logs','Show last n log entries (optionally filtered by level: INFO/WARN/ERROR)','system',1], +['mem','Show memory usage (heap + localStorage + VFS)','system',1], +['memory-map-export','Export memory index to /memory/reference.md (overwrites file)','system',1], +['module-disable','Disable a module from auto-loading on refresh (persists)','system',1], +['module-enable','Re-enable a previously disabled module','system',1], +['module-info','Show module details: path, status, size, type, source, commands list','system',1], +['module-list','List loaded modules grouped by category. Flags: --flat, --core, --ext, --foundation, --...','system',1], +['module-load','Mark a lazy module as loaded','system',1], +['module-reload','Unload then reload a module (shows diff)','system',1], +['module-reset','Reset a module to a clean state (clears its state, keeps commands)','system',1], +['module-stats','Show aggregate module statistics (loaded/unloaded/lazy/failed/disabled + total size)','system',1], +['module-unload','Mark a module as unloaded','system',1], +['modules','Open the module manager GUI','system',1], +['modules-list','List all available modules from the module index','system',1], +['monitor','Toggle auto-monitoring (checks every 60s)','system',1], +['predict','Predict task duration, tool turns, and confidence','system',1], +['predictor-config','Tune: confidence-threshold, min-data-points','system',1], +['predictor-status','Model accuracy, data points, prediction count','system',1], +['removemod','Remove a loaded module by name','system',1], +['reset','Global reset command (default: mem)','system',1], +['reset-vfs','Alias for reset vfs','system',1], +['rollback','Restore VFS from a checkpoint','system',1], +['sandbox-config','Tune: max-workers, min-workers, pool-timeout','system',1], +['sandbox-run','Run a task in the sandbox (automatic routing)','system',1], +['sandbox-status','Show sandbox pool health and task metrics','system',1], +['secret-del','Remove a stored secret','system',1], +['secret-get','Decrypt and display a secret value','system',1], +['secret-list','List all stored secret names','system',1], +['secret-lock','Lock the vault (clear derived key from memory)','system',1], +['secret-set','Encrypt and store a secret','system',1], +['secret-setpin','Set or change the vault PIN code','system',1], +['secret-status','Show vault status (locked/unlocked/pin set/open mode)','system',1], +['secret-unlock','Unlock the vault with a PIN code','system',1], +['security','safe','system',1], +['security-destructive','Terminal command','system',1], +['security-flag','Terminal command','system',1], +['security-level','Terminal command','system',1], +['security-system','Terminal command','system',1], +['security-whitelist','Terminal command','system',1], +['server','Add a server to monitor','system',1], +['server-check','Check a server status','system',1], +['server-remove','Remove a server','system',1], +['servers','List all monitored servers','system',1], +['setfont','Alias for font-size','system',1], +['skill-audit','Audit skills for stale, duplicate, and broken references','system',1], +['skill-create','Create a new skill','system',1], +['skill-delete','Delete a skill','system',1], +['skill-export','Export a skill as JSON','system',1], +['skill-import','Import a skill from JSON string','system',1], +['skill-list','List all skills, optionally filtered by category','system',1], +['skill-pack-core','Seed and manage built-in core skills (navigation, memory, search, modules, backup, agen...','system',1], +['skill-patch','Replace a skill\u2019s full content','system',1], +['skill-restore','Restore an archived skill','system',1], +['skill-sync','Rebuild skills index from /skills/ directory','system',1], +['skill-validate','Validate skill structure and get confidence score','system',1], +['skill-view','View a skill\u2019s full content','system',1], +['skill-webdav-sync','Push all skills to WebDAV as JSON files','system',1], +['smart-prompt','Toggle context-aware prompt (shows name, skills, sessions, memory, edits)','system',1], +['startup-modules','Terminal command','system',1], +['status','Display system status (network, files, modules, Ollama)','system',1], +['sysmsg','Toggle system/warning/error/info message visibility','system',1], +['system-health','Show tool latency, module load times, VFS size, and error rates','system',1], +['test-net','Check proxy API, Ollama, and internet connectivity','system',1], +['unalias','Remove a user-defined alias (usage: unalias <name>)','system',1], +['uptime','Show time since page load + last save time','system',1], +['usage','Terminal command','system',1], +['version','Show version, build date, and cache key','system',1], +['vfs','Terminal command','system',1], +['vfs-clean','Terminal command','system',1], +['analyze','Analyze image/audio/video. Flags: --frames N, --spectrogram, --beats, --scene','tools',1], +['calc','Evaluate expressions or convert between units','tools',1], +['js','Evaluate JavaScript in page context','tools',1], +['js-run','Load and execute a JS file from VFS','tools',1], +['mindmap','Create, edit, visualize mindmaps. v4.5.44: multi-mindmap, find, move, undo, import, mer...','tools',1], +['node','Alias for js-run (executes a JS file from VFS)','tools',1], +['speak','Text-to-speech. No args = toggle auto-speak for AI responses','tools',1], +['three','3D scene builder (Three.js). Run `three help` for full list.','tools',1], +['tutorial','Interactive guided tour of newOS4 (6 quests)','tools',1], +['voice','Start voice recording (Whisper STT), or type "voice stop" to cancel','tools',1], +['wpm','Web Package Manager for installing and managing modules','tools',1], +['floating-add','Add a floating UI element','ui',1], +['floating-clear','Remove all floating elements','ui',1], +['floating-list','Show all floating elements','ui',1], +['floating-pos','Reposition a floating element','ui',1], +['floating-remove','Remove a floating element','ui',1], +['floating-toggle','Show/hide a floating element','ui',1], +['kanban','Manage tasks on a kanban board (todo, doing, done columns)','ui',1], +['theme','Manage and preview visual themes','ui',1], +]; +var KNOWN = {}, AUTO = {}; +CMDS.forEach(function(c){ KNOWN[c[0]] = 1; AUTO[c[0]] = c[3]; }); + +/* ───────── state ───────── */ +var state = { type:'btn', items:[{label:'List',command:'ls'},{label:'Trash',command:'ls-trash'}], collection:[] }; + +/* ───────── helpers ───────── */ +function q(s){ return /\s/.test(s) ? '"'+s+'"' : s; } +function slug(s){ + var t = (s||'').toLowerCase().replace(/[^\w\s-]/g,'').trim().replace(/\s+/g,'-'); + return t || ''; +} +function typo(s){ return (s||'').replace(/"/g,'”').replace(/'/g,'’'); } +function toast(msg){ + var t = $('toast'); t.textContent = msg; t.classList.add('show'); + clearTimeout(t._h); t._h = setTimeout(function(){ t.classList.remove('show'); }, 1800); +} +function copyText(text, msg){ + var done = function(){ toast(msg || 'Copied!'); }; + if (navigator.clipboard && navigator.clipboard.writeText) { + navigator.clipboard.writeText(text).then(done, function(){ fallback(); }); + } else { fallback(); } + function fallback(){ + var ta = document.createElement('textarea'); + ta.value = text; ta.style.position='fixed'; ta.style.opacity='0'; + document.body.appendChild(ta); ta.select(); + try { document.execCommand('copy'); done(); } catch(e){ toast('Copy failed — select manually'); } + document.body.removeChild(ta); + } +} +function val(cls, msg){ + var v = $('out-val'); v.className = 'val ' + cls; v.textContent = msg; +} +function fail(msg){ + val('err', msg); $('out-code').textContent = '—'; state.lastCmd = ''; +} + +/* ───────── builder ───────── */ +var EMOJIS = ['🆕','✅','⚙️','🌐','📋','🗂️','📁','🧠','🖥️','📊','🔍','🔧','🚀','💾','🔐','⚡','🎨','🧹','⏹️','❤️','🤖','💬','🧪','☁️']; + +function buildEmojiChips(){ + ['emoji-chips','emoji-chips2'].forEach(function(cid){ + var box = $(cid); box.innerHTML = ''; + EMOJIS.forEach(function(e){ + var b = document.createElement('button'); b.textContent = e; + b.onclick = function(){ + var input = cid === 'emoji-chips' ? $('btn-label') : $('menu-label'); + var v = input.value.trim(); + var rest = v; + if (v && v.charCodeAt(0) >= 0x2190) rest = v.replace(/^[^\x00-\x7F]+[ \t]*/, ''); + input.value = e + (rest ? ' ' + rest : ''); + syncId(); generate(); + }; + box.appendChild(b); + }); + }); +} + +function currentForm(){ + if (state.type === 'btn') return { label:$('btn-label').value.trim(), id:$('btn-id').value.trim() }; + if (state.type === 'menu') return { label:$('menu-label').value.trim(), id:$('menu-id').value.trim() }; + return { label:$('panel-label').value.trim(), id:$('panel-id').value.trim() }; +} +function syncId(){ + var f = currentForm(); + var idIn = state.type==='btn' ? $('btn-id') : state.type==='menu' ? $('menu-id') : $('panel-id'); + var s = slug(f.label); + if (!idIn._dirty || !idIn.value) idIn.value = s ? s + (state.type==='menu' ? '-menu' : state.type==='panel' ? '-panel' : '') : ''; +} +function markDirty(ev){ ev.target._dirty = true; } + +function itemRow(it, idx){ + var row = document.createElement('div'); row.className = 'menu-item-row'; + var l = document.createElement('input'); l.type='text'; l.placeholder='Label'; l.value = it.label; + var c = document.createElement('input'); c.type='text'; c.placeholder='Command (e.g. ls)'; c.value = it.command; + var x = document.createElement('button'); x.className='btn btn-red btn-sm'; x.textContent='✕'; + l.oninput = function(){ it.label = l.value; generate(); }; + c.oninput = function(){ it.command = c.value; generate(); }; + l.onfocus = c.onfocus = function(){ state.focusItem = idx; }; + x.onclick = function(){ + state.items = state.items.filter(function(r){ return r !== it; }); + renderItems(); generate(); + }; + row.appendChild(l); row.appendChild(c); row.appendChild(x); + return row; +} +function renderItems(){ + var box = $('menu-items'); box.innerHTML = ''; + state.items.forEach(function(it, i){ box.appendChild(itemRow(it, i)); }); +} +$('menu-add-item').onclick = function(){ + state.items.push({label:'', command:''}); + state.focusItem = state.items.length - 1; + renderItems(); generate(); + var rows = $('menu-items').querySelectorAll('.menu-item-row input'); + if (rows.length) rows[rows.length-2].focus(); +}; + +/* tabs */ +$('type-tabs').querySelectorAll('.tab').forEach(function(t){ + t.onclick = function(){ + $('type-tabs').querySelectorAll('.tab').forEach(function(x){ x.classList.remove('active'); }); + t.classList.add('active'); + state.type = t.dataset.type; + $('form-btn').style.display = state.type==='btn' ? '' : 'none'; + $('form-menu').style.display = state.type==='menu' ? '' : 'none'; + $('form-panel').style.display = state.type==='panel' ? '' : 'none'; + syncId(); generate(); + if (state.type === 'btn') $('btn-label').focus(); + else if (state.type === 'menu') $('menu-label').focus(); + else $('panel-label').focus(); + }; +}); + +/* ───────── generator + validation (mirrors FloatingModule._cmdAdd) ───────── */ +function validateCmd(cmd){ + var w = (cmd || '').split(/\s+/)[0]; + if (!w) return { level:'err', msg:'Command is empty' }; + if (!KNOWN[w]) return { level:'warn', msg:'⚠ "'+w+'" is NOT a known terminal command — clicking will route it to the AI' }; + return { level:'ok', msg:'✓ "'+w+'" is a real terminal command' }; +} +function generate(){ + var f = currentForm(); + var cmd = '', notes = []; + if (!f.id || /[\s"']/.test(f.id)) { fail('✗ ID is required and must be a single word — no spaces or quote characters'); return; } + if (!f.label) notes.push('label is empty'); + + if (state.type === 'btn') { + if (/["']/.test($('btn-cmd').value.trim())) { fail('✗ Commands cannot contain quote characters (they break terminal tokenizing) — rephrase the command'); return; } + cmd = 'floating-add btn '+f.id+' "'+typo(f.label)+'" '+$('btn-cmd').value.trim(); + var v = validateCmd($('btn-cmd').value.trim()); + if (v.level==='err'){ fail('✗ '+v.msg); return; } + if (v.level==='warn') notes.push(v.msg); + if (notes.length===0) val('ok', v.msg+' — ready to install'); + else val('warn', notes.join(' · ')); + } + else if (state.type === 'menu') { + if (state.items.length === 0){ fail('✗ Menu needs at least one item'); return; } + var parts = []; + for (var i=0;i<state.items.length;i++){ + var it = state.items[i]; + if (!it.label || !it.command){ fail('✗ Item '+(i+1)+': BOTH label and command are required (one-colon items get dropped by the parser)'); return; } + if (it.label.indexOf(':') !== -1){ fail('✗ Item '+(i+1)+': label must not contain ":"'); return; } + if (/["']/.test(it.command)){ fail('✗ Item '+(i+1)+': commands cannot contain quote characters — rephrase it'); return; } + parts.push('btn:'+q(typo(it.label))+':'+q(it.command)); + var vc = validateCmd(it.command); + if (vc.level !== 'ok') notes.push('item "'+it.label+'": '+vc.msg); + } + cmd = 'floating-add menu '+f.id+' "'+typo(f.label)+'" '+parts.join(' '); + if (notes.length) val('warn', notes.join(' · ')); + else val('ok','✓ all ' + state.items.length + ' item commands are real, auto-loaded commands — ready to install'); + } + else { + cmd = 'floating-add panel '+f.id+' "'+typo(f.label)+'" "'+typo($('panel-content').value.trim().replace(/\s*\n\s*/g,' '))+'"'; + if (notes.length) val('warn', notes.join(' · ')); else val('ok','✓ panel ready (markdown-lite supported)'); + } + $('out-code').textContent = cmd; + state.lastCmd = cmd; + renderPreview(); +} + +/* ───────── preview ───────── */ +function renderPreview(){ + var fl = $('float-layer'); fl.innerHTML = ''; + var f = currentForm(); + if (state.type === 'btn') { + var b = document.createElement('button'); b.className='fbtn draft'; b.textContent = f.label || '?'; + b.onclick = function(){ toast('→ would run: '+$('btn-cmd').value.trim()); }; + fl.appendChild(b); + } else if (state.type === 'menu') { + var m = document.createElement('div'); m.className = 'fmenu open'; + var it = document.createElement('div'); it.className='fmenu-items'; + state.items.forEach(function(s){ + var sb = document.createElement('button'); sb.className='fsub'; sb.textContent = s.label || '?'; + sb.onclick = function(ev){ ev.stopPropagation(); toast('→ would run: '+s.command); }; + it.appendChild(sb); + }); + var h = document.createElement('button'); h.className='fmenu-head draft'; h.textContent = f.label || '?'; + h.onclick = function(){ m.classList.toggle('open'); }; + m.appendChild(it); m.appendChild(h); fl.appendChild(m); + } else { + var p = document.createElement('div'); p.className='fpanel'; + var t = document.createElement('b'); t.textContent = f.label || '?'; + var c = document.createElement('span'); c.textContent = $('panel-content').value || '…'; + p.appendChild(t); p.appendChild(c); fl.appendChild(p); + } +} + +/* ───────── palette ───────── */ +function renderPalette(filter){ + var g = $('pal-grid'); g.innerHTML = ''; + CMDS.forEach(function(c){ + if (filter && (c[0]+' '+c[1]+' '+c[2]).toLowerCase().indexOf(filter) === -1) return; + var d = document.createElement('div'); d.className='pal-cmd'; + var auto = c[3] ? '✓' : '⚠'; + d.innerHTML = '<span class="cat">'+c[2]+'</span>'+auto+' <code>'+c[0]+'</code><small>'+c[1]+'</small>'; + d.onclick = function(){ + if (state.type === 'btn') { $('btn-cmd').value = c[0]; } + else if (state.type === 'menu' && state.items.length) { + var idx = (typeof state.focusItem === 'number' && state.focusItem < state.items.length) ? state.focusItem : state.items.length - 1; + state.items[idx].command = c[0]; renderItems(); + } + else if (state.type === 'panel') { toast('Panels show text, not commands'); return; } + generate(); + }; + g.appendChild(d); + }); + if (!g.children.length) g.innerHTML = '<div style="color:var(--muted);font-size:12px">No matching commands</div>'; +} +$('pal-search').oninput = function(){ renderPalette($('pal-search').value.trim().toLowerCase()); }; + +/* ───────── presets ───────── */ +var PRESETS = [ + ['🗂️ Files menu','list · trash · concept tree (this one is live in your UI)','floating-add menu files-menu "🗂️ Files" btn:List:ls btn:Trash:ls-trash btn:Tree:nvfs-tree'], + ['🧠 AI menu','ask · clear · context · debug','floating-add menu ai-menu "🧠 AI" btn:Ask:ai btn:Clear:ai-clear btn:Context:ai-context btn:Debug:debug'], + ['🖥️ System menu','status · mem · logs · help','floating-add menu sys-menu "🖥️ System" btn:Status:status btn:Memory:mem btn:Logs:logs btn:Help:help'], + ['🌐 Web menu','search · images · fetch · extract','floating-add menu web-menu "🌐 Web" btn:Search:search btn:Images:images btn:Fetch:fetch btn:Extract:extract'], + ['📊 Status panel','markdown-lite panel','floating-add panel status-p "📊 Status" "newOS4 · **bold**, *italic* and `code` supported"'], + ['⚙️ Config button','button with arguments (user-verified)','floating-add btn cfg-timeout "⚙️ Timeout" ai-config confirm-timeout 0'], + ['🧪 Quoted items demo','multi-word label + multi-word command','floating-add menu demo-q "🧪 Demo" btn:"My Files":ls btn:Cfg:"ai-config confirm-timeout 0"'] +]; +var CURRENT_SETUP = [ + 'floating-add btn config-timeout "⚙️timeout" ai-config confirm-timeout 0', + 'floating-add btn ai-allow "✅Allow" ai-allow', + 'floating-add btn new-session "🆕New" new-session', + 'floating-add btn webdav-btn "🌐" webdav-browser', + 'floating-add btn clipboard "📋" clipboard-history', + 'floating-add menu files-menu "🗂️ Files" btn:List:ls btn:Trash:ls-trash btn:Tree:nvfs-tree' +]; +function renderPresets(){ + var box = $('presets'); box.innerHTML = ''; + PRESETS.forEach(function(p){ + var b = document.createElement('button'); b.className = 'preset'; + b.innerHTML = '<b>'+p[0]+'</b><small>'+p[1]+'</small>'; + b.title = p[2]; + b.onclick = function(){ $('import-area').value = p[2]; parseImport(); }; + box.appendChild(b); + }); +} +$('load-current').onclick = function(){ + $('import-area').value = CURRENT_SETUP.join('\n'); + parseImport(); + toast('Note: "new-session" and "clipboard-history" are not real commands — they route to the AI; consider "session-new" or "floating-list"'); +}; + +/* ───────── import (mirrors the real parser) ───────── */ +function tokenize(line){ + var toks=[], cur='', qq=null; + for (var i=0;i<line.length;i++){ + var ch = line[i]; + if (qq){ if (ch===qq){ qq=null; } else { cur += ch; } } + else if (ch==='"'||ch==="'"){ qq = ch; } + else if (/\s/.test(ch)){ if (cur){ toks.push(cur); cur=''; } } + else { cur += ch; } + } + if (cur) toks.push(cur); + return toks; +} +function parseFA(line){ + var t = tokenize(line); + if (t.length < 4 || t[0] !== 'floating-add') return null; + var ty = t[1]; + if (ty === 'btn' || ty === 'button') return { type:'btn', id:t[2], label:t[3], cmd:t.slice(4).join(' ') }; + if (ty === 'menu'){ + var items=[], buf=''; + for (var i=4;i<t.length;i++){ + buf = buf ? buf+' '+t[i] : t[i]; + var f1 = buf.indexOf(':'); + if (f1 === -1){ buf=''; continue; } + var f2 = buf.indexOf(':', f1+1); + if (f2 === -1) continue; // wait for command (exactly like the module) + if (buf.slice(0,f1) !== 'btn'){ buf=''; continue; } + var l = buf.slice(f1+1,f2), c = buf.slice(f2+1); + if (l && c) items.push({label:l, command:c}); + buf = ''; + } + return { type:'menu', id:t[2], label:t[3], items:items }; + } + if (ty === 'panel') return { type:'panel', id:t[2], label:t[3], content:t.slice(4).join(' ') }; + return null; +} +function parseImport(){ + var lines = $('import-area').value.split('\n').filter(function(l){ return l.trim(); }); + if (!lines.length){ toast('Nothing to parse'); return; } + var first = parseFA(lines[0].replace(/^[>$]\s+/, '')); + if (!first){ val('err','✗ Not a valid floating-add line'); toast('Parse failed'); return; } + loadIntoBuilder(first); + // remaining lines go straight to collection + var added = 0; + for (var i=1;i<lines.length;i++){ + var ln = lines[i].replace(/^[>$]\s+/, ''); + var fa = parseFA(ln); + if (fa) { addToCollection(ln.trim(), false); added++; } + } + generate(); toast('Loaded into builder — ' + (1 + added) + ' of ' + lines.length + ' line(s)'); +} +function loadIntoBuilder(fa){ + var tab = document.querySelector('#type-tabs .tab[data-type="'+(fa.type==='btn'?'btn':fa.type==='menu'?'menu':'panel')+'"]'); + if (tab) tab.click(); + if (fa.type === 'btn'){ $('btn-label').value = fa.label; $('btn-id').value = fa.id; $('btn-id')._dirty = true; $('btn-cmd').value = fa.cmd; } + else if (fa.type === 'menu'){ $('menu-label').value = fa.label; $('menu-id').value = fa.id; $('menu-id')._dirty = true; state.items = fa.items.length ? fa.items : [{label:'',command:''}]; renderItems(); } + else { $('panel-label').value = fa.label; $('panel-id').value = fa.id; $('panel-id')._dirty = true; $('panel-content').value = fa.content; } +} +$('import-btn').onclick = parseImport; + +/* ───────── collection ───────── */ +function addToCollection(cmd, announce){ + if (state.collection.indexOf(cmd) !== -1){ if (announce !== false) toast('Already in collection'); return; } + state.collection.push(cmd); + renderCollection(); + if (announce !== false) toast('Added to collection ('+state.collection.length+')'); +} +function renderCollection(){ + var box = $('coll-list'); box.innerHTML = ''; + $('coll-count').textContent = '(' + state.collection.length + ')'; + if (!state.collection.length){ var e = document.createElement('div'); e.style.cssText = 'color:var(--muted);font-size:12px;padding:6px 2px'; e.textContent = 'Collection is empty — add commands from the form above'; box.appendChild(e); } + state.collection.forEach(function(cmd, idx){ + var row = document.createElement('div'); row.className='item'; + var c = document.createElement('code'); c.textContent = cmd; + var tag = document.createElement('span'); tag.className='tag'; var kind = (cmd.split(' ')[1]||''); tag.textContent = (kind==='menu'||kind==='panel')?kind:'btn'; + var cp = document.createElement('button'); cp.className='btn btn-ghost btn-sm'; cp.textContent='📋'; + var rm = document.createElement('button'); rm.className='btn btn-red btn-sm'; rm.textContent='✕'; + cp.setAttribute('aria-label', 'Copy command'); rm.setAttribute('aria-label', 'Remove command'); + cp.onclick = function(){ copyText(cmd, 'Copied command'); }; + rm.onclick = function(){ state.collection.splice(idx,1); renderCollection(); }; + row.appendChild(c); row.appendChild(tag); row.appendChild(cp); row.appendChild(rm); + var mm = cmd.match(/^floating-add\s+\S+\s+\S+\s+"[^"]*"\s*([^\s]*)/); + if (mm && mm[1] && !KNOWN[mm[1]]) { var wn = document.createElement('span'); wn.textContent = '⚠'; wn.title = 'not a known terminal command — will route to the AI'; wn.style.color = 'var(--yellow)'; row.appendChild(wn); } + box.appendChild(row); + }); +} +$('add-coll').onclick = function(){ if (state.lastCmd) addToCollection(state.lastCmd); else toast('Nothing to add yet'); }; +$('copy-all').onclick = function(){ + if (!state.collection.length){ toast('Collection is empty'); return; } + copyText(state.collection.join('\n'), 'Copied ' + state.collection.length + ' commands'); +}; +$('coll-clear').onclick = function(){ state.collection = []; renderCollection(); }; +$('copy-main').onclick = function(){ var t = state.lastCmd; if (!t){ toast('Nothing to copy yet — complete the form'); return; } copyText(t, 'Command copied'); }; +$('install-now').onclick = function(){ var t = state.lastCmd; if (!t){ toast('Nothing to copy yet — complete the form'); return; } copyText(t, 'Copied — now paste it in the newOS4 terminal and press Enter'); }; + +/* wire inputs */ +[['btn-label','btn-id'],['menu-label','menu-id'],['panel-label','panel-id']].forEach(function(pair){ + $(pair[0]).oninput = function(){ syncId(); generate(); }; + $(pair[1]).oninput = markDirty; +}); +$('btn-cmd').oninput = generate; +$('panel-content').oninput = generate; + +/* init */ +buildEmojiChips(); +renderItems(); +renderPresets(); +renderPalette(''); +renderCollection(); +generate(); +})(); +</script> + +</body></html>
\ No newline at end of file diff --git a/scripts/gen-floating-designer-registry.py b/scripts/gen-floating-designer-registry.py new file mode 100644 index 0000000..1b150d6 --- /dev/null +++ b/scripts/gen-floating-designer-registry.py @@ -0,0 +1,117 @@ +#!/usr/bin/env python3 +"""gen-floating-designer-registry.py — Task 4 of the v2->v3 repair plan. (v2) + +Rebuilds the floating-ui-designer CMDS registry from ground truth: + * union of `commands['X']` registrations in modules/core + modules/ext + (bundle/worker files skipped), and + * terminal.js dispatch builtins (`cmd === 'x'`). + +Emits /tmp/fui_cmds_snippet.txt with one JS line per command: + ['name','desc','cat',1], +Descriptions are re-escaped safely: apostrophes -> \u2019, backslashes -> \u005C +(no raw backslash pairs ever enter the page). Prints acceptance diagnostics. +""" +import os, re +from collections import Counter + +BASE = '/var/www/webx11.duckdns.org/spaces/newos4' +OUT = '/tmp/fui_cmds_snippet.txt' + +files = [] +for d in ('modules/core', 'modules/ext'): + dd = os.path.join(BASE, d) + for f in sorted(os.listdir(dd)): + if not f.endswith('.js'): + continue + if 'bundle' in f or 'worker' in f: + continue + files.append(os.path.join(d, f)) + +texts = {} +for rel in files: + try: + texts[rel] = open(os.path.join(BASE, rel), encoding='utf-8', errors='replace').read() + except Exception as e: + print('skip', rel, e) + +cmd_re = re.compile(r"commands\['([^']+)'\]\s*=") +src_of = {} +for rel, txt in texts.items(): + for name in cmd_re.findall(txt): + src_of.setdefault(name, set()).add(rel) + +disp = set() +t = texts.get('modules/core/terminal.js', '') +for m in re.finditer(r"cmd === '([a-z0-9-]+)'", t): + disp.add(m.group(1)) +for n in disp: + src_of.setdefault(n, set()).add('modules/core/terminal.js') + +ALL = sorted(src_of.keys()) + +def cat_for(rels): + joined = ' '.join(rels) + rules = [ + (('aiassist', 'aiasm', 'smartctx', 'chatstudio', 'ollama'), 'ai'), + (('agent-registry', 'orchestrator', 'leases', 'memory-graph', 'world-model'), 'agents'), + (('module-fs', 'busybox', 'shell', 'filemgr', 'editor', 'finder', 'diff', 'neural-vfs', 'cryptogzip'), 'files'), + (('searxng', 'fetcher', 'webdav'), 'network'), + (('calc', 'mindmap', 'three', 'media', 'voice', 'wpm', 'tutorial', 'console'), 'tools'), + (('floating', 'theme', 'kanban'), 'ui'), + (('system', 'config', 'modmgr', 'debug-supervisor', 'predictor', 'servermon', 'sandbox', 'secret-vault', 'terminal'), 'system'), + ] + for keys, cat in rules: + if any(k in joined for k in keys): + return cat + return 'system' + +DESC_FALLBACK = 'Terminal command' + +def desc_for(name): + pat = re.compile(r"'" + re.escape(name) + r"'\s*:\s*'((?:[^'\\]|\\.){1,200})'") + for rel in sorted(texts.keys()): + m = pat.search(texts[rel]) + if not m: + continue + raw = m.group(1) + # unescape source-level escaping: \\ -> placeholder, \' and \" -> quote + raw = raw.replace('\\\\', '\x01') + raw = raw.replace("\\'", "'").replace('\\"', '"') + raw = raw.replace('\x01', '\\') + # strip control chars + raw = ''.join(' ' if ord(c) < 32 else c for c in raw) + if '\u2014' in raw: + raw = raw.split('\u2014', 1)[1].strip() + if not raw or raw.lower() == name: + continue + if len(raw) > 90: + raw = raw[:87].rstrip() + '...' + return raw + return DESC_FALLBACK + +entries = [] +for n in ALL: + d = desc_for(n) + d = d.replace('\\', '\\u005C').replace("'", '\\u2019') + entries.append((cat_for(src_of[n]), n, d)) + +entries.sort(key=lambda e: (e[0], e[1])) + +cnt = Counter(c for c, _, _ in entries) +print('total=%d' % len(entries)) +for k in sorted(cnt): + print(' %-8s %d' % (k, cnt[k])) + +want = ['session-new', 'health-check', 'doctor', 'uptime', 'ai-stop', 'kanban', 'webdav-browser', 'vfs-export', 'fetch'] +bad = ['new-session', 'clipboard-history', 'web', 'save', 'load', 'import', 'svg'] +have = set(e[1] for e in entries) +print('missing:', [w for w in want if w not in have]) +print('excluded-present:', [b for b in bad if b in have]) +print('no-desc fallback count:', sum(1 for _, _, d in entries if d == DESC_FALLBACK)) + +assert len(entries) >= 330, 'TOO FEW COMMANDS: %d' % len(entries) + +with open(OUT, 'w', encoding='utf-8') as f: + for cat, n, d in entries: + f.write("['%s','%s','%s',1],\n" % (n, d, cat)) +print('WROTE', OUT) diff --git a/scripts/test-floating-designer.mjs b/scripts/test-floating-designer.mjs new file mode 100644 index 0000000..3647683 --- /dev/null +++ b/scripts/test-floating-designer.mjs @@ -0,0 +1,144 @@ +#!/usr/bin/env node +/* + * test-floating-designer.mjs — behavioral + byte-level regression tests for + * downloads/floating-ui-designer.html (newOS4 Floating UI Designer). + * + * Usage: node scripts/test-floating-designer.mjs + * Exit code 0 = all pass; 1 = failures (details printed). + * + * Created 2026-10-17 during the v2 -> v3 repair + * (see .hermes/plans/2026-10-17_195836-floating-ui-designer-review-fixes.md). + */ +import { readFileSync } from 'node:fs'; +import { fileURLToPath } from 'node:url'; +import { dirname, join as pjoin } from 'node:path'; + +const here = dirname(fileURLToPath(import.meta.url)); +const PAGE = pjoin(here, '..', 'downloads', 'floating-ui-designer.html'); +const src = readFileSync(PAGE, 'utf8'); +const raw = readFileSync(PAGE); // Buffer for byte-level checks +const BS = String.fromCharCode(92); // backslash + +/* ---------- tiny test runner ---------- */ +const results = []; +function check(name, fn) { + try { fn(); results.push(['ok', name]); } + catch (e) { results.push(['FAIL', name + ' -- ' + e.message]); } +} +function assert(cond, msg) { if (!cond) throw new Error(msg || 'assertion failed'); } +function eq(a, b, msg) { + if (a !== b) throw new Error((msg || '') + ' | expected ' + JSON.stringify(b) + ' got ' + JSON.stringify(a)); +} +function deepEq(a, b, msg) { + const A = JSON.stringify(a), B = JSON.stringify(b); + if (A !== B) throw new Error((msg || '') + ' | expected ' + B + ' got ' + A); +} + +/* ---------- extract JS pieces from the page ---------- */ +function sliceBetween(source, from, to) { + const i = source.indexOf(from); + if (i === -1) throw new Error('marker not found: ' + from); + const j = source.indexOf(to, i + from.length); + if (j === -1) throw new Error('marker not found: ' + to); + return source.slice(i, j); +} +function extractFn(source, name) { + const start = source.indexOf('function ' + name + '('); + if (start === -1) throw new Error('function not found: ' + name); + let depth = 0, i = source.indexOf('{', start); + for (; i < source.length; i++) { + if (source[i] === '{') depth++; + else if (source[i] === '}') { depth--; if (depth === 0) { i++; break; } } + } + return source.slice(start, i); +} + +const cmdsBlock = sliceBetween(src, 'var CMDS = [', 'var KNOWN'); +const knownBlock = sliceBetween(src, 'var KNOWN', 'var state = {'); +const code = + cmdsBlock + + knownBlock + + ['q', 'slug', 'validateCmd', 'tokenize', 'parseFA'].map(n => extractFn(src, n)).join('\n') + + '\nreturn { CMDS, KNOWN, q, slug, validateCmd, tokenize, parseFA };'; +const api = new Function(code)(); + +/* ---------- behavioral checks ---------- */ +const LINE = 'floating-add menu files-menu "🗂️ Files" btn:List:ls btn:Cfg:"ai-config confirm-timeout 0"'; + +check('q() quotes multi-word labels', () => eq(api.q('My Label'), '"My Label"')); +check('q() quotes multi-word commands', () => eq(api.q('cmd with args'), '"cmd with args"')); +check('q() leaves single words bare', () => eq(api.q('ls'), 'ls')); + +check('slug() normalizes spaced label', () => eq(api.slug('New Session'), 'new-session')); +check('slug() drops emoji + space', () => eq(api.slug('🗂️ Files'), 'files')); + +check('tokenize() 6 tokens, quotes stripped, inner spaces kept', () => { + const t = api.tokenize(LINE); + eq(t.length, 6, 'token count'); + eq(t[3], '🗂️ Files', 'label token'); + eq(t[5], 'btn:Cfg:ai-config confirm-timeout 0', 'quoted command token'); +}); + +check('parseFA() parses quoted menu items', () => { + const fa = api.parseFA(LINE); + assert(fa, 'parseFA returned null'); + eq(fa.type, 'menu'); + eq(fa.id, 'files-menu'); + deepEq(fa.items, [ + { label: 'List', command: 'ls' }, + { label: 'Cfg', command: 'ai-config confirm-timeout 0' }, + ]); +}); + +check('validateCmd(): real cmd -> ok', () => eq(api.validateCmd('ai-config confirm-timeout 0').level, 'ok')); +check('validateCmd(): unknown -> warn', () => eq(api.validateCmd('nope-cmd').level, 'warn')); +check('validateCmd(): empty -> err', () => eq(api.validateCmd('').level, 'err')); + +/* ---------- byte-level checks ---------- */ +check('bytes: zero double-backslash pairs in file', () => { + let n = 0; + for (let i = 0; i < raw.length - 1; i++) if (raw[i] === 92 && raw[i + 1] === 92) n++; + eq(n, 0, 'double-backslash byte pairs'); +}); + +const latin = raw.toString('latin1'); +const JOIN_N = "join('" + BS + "n')"; +const SPLIT_N = "split('" + BS + "n')"; +function occurrences(hay, needle) { + let n = 0, i = 0; + while ((i = hay.indexOf(needle, i)) !== -1) { n++; i += needle.length; } + return n; +} +check('bytes: join newline sites present (>=2)', () => { + const n = occurrences(latin, JOIN_N); + assert(n >= 2, 'join(' + BS + 'n) count = ' + n); +}); +check('bytes: split newline site present (>=1)', () => { + const n = occurrences(latin, SPLIT_N); + assert(n >= 1, 'split(' + BS + 'n) count = ' + n); +}); + +/* ---------- registry checks ---------- */ +check('registry: >= 330 commands', () => assert(api.CMDS.length >= 330, 'CMDS length = ' + api.CMDS.length)); +check('registry: contains new commands', () => { + const have = new Set(api.CMDS.map(c => c[0])); + const want = ['session-new', 'health-check', 'doctor', 'uptime', 'ai-stop', 'kanban', 'webdav-browser', 'vfs-export', 'fetch']; + const missing = want.filter(w => !have.has(w)); + eq(missing.length, 0, 'missing: ' + missing.join(', ')); +}); +check('registry: no fake entries', () => { + const have = new Set(api.CMDS.map(c => c[0])); + const bad = ['new-session', 'clipboard-history', 'web', 'save', 'load', 'import', 'svg']; + const present = bad.filter(b => have.has(b)); + eq(present.length, 0, 'should not contain: ' + present.join(', ')); +}); + +/* ---------- report ---------- */ +let failed = 0; +for (const [st, name] of results) { + console.log(st + ' ' + name); + if (st !== 'ok') failed++; +} +console.log('----'); +console.log(failed === 0 ? 'ALL PASS (' + results.length + ' checks)' : failed + ' FAILED of ' + results.length + ' checks'); +process.exit(failed ? 1 : 0); diff --git a/skills/floating-ui.json b/skills/floating-ui.json index 7cc0e69..602f688 100644 --- a/skills/floating-ui.json +++ b/skills/floating-ui.json @@ -9,7 +9,7 @@ "gui" ], "category": "ui", - "created": "2026-07-06T00:13:29.404Z", - "updated": "2026-07-06T00:13:29.404Z", - "content": "---\nname: floating-ui\ndescription: How to create, manage, and remove floating UI elements — buttons, collapsible menus, and status panels that persist across page reloads\ntags: [floating, ui, buttons, menu, gui]\ncategory: ui\n---\n## Commands\n\n1. **Create a floating button:**\n - floating-add btn <id> <label> <command>\n - Example: floating-add btn files \"📁 Files\" ls\n - Button appears at bottom-right, persists across reloads\n\n2. **Create a collapsible menu:**\n - floating-add menu <id> <label> btn:SubLabel|command btn:Sub2|cmd2 ...\n - Example: floating-add menu tools \"🔧 Tools\" btn:Help|help btn:Clear|clear\n - Click header to expand/collapse. State persists.\n\n3. **Create a text panel:**\n - floating-add panel <id> <title> <content>\n - Example: floating-add panel status \"📊 Status\" \"CPU: 2%\"\n\n4. **Manage elements:**\n - floating-list — show all elements with IDs and positions\n - floating-remove <id> — delete by ID\n - floating-toggle <id> — show/hide without deleting\n - floating-pos <id> x|auto y|auto — reposition or reset to auto\n - floating-clear — remove everything\n\n5. **Persistence:**\n - All elements auto-save to localStorage (webx11_floating_elements)\n - Survive page reload, browser restart\n - Run floating-clear to start fresh\n\n## When to use\n- User wants quick-access buttons instead of typing commands\n- User says \"add a button for X\", \"make a shortcut\", \"floating menu\"\n- Creating a dashboard with status panels and action buttons" -}
\ No newline at end of file + "created": "2026-08-19T07:27:29.655Z", + "updated": "2026-10-17T18:13:15.959Z", + "content": "---\nname: floating-ui\ndescription: How to create, manage, and remove floating UI elements — buttons, collapsible menus, and status panels that persist across page reloads\ntags: [floating, ui, buttons, menu, gui]\ncategory: ui\n---\n## Commands\n\n1. **Create a floating button:**\n - floating-add btn <id> \"<label>\" <command>\n - Example: floating-add btn files \"📁 Files\" ls\n - Button appears at bottom-right, persists across reloads\n\n2. **Create a collapsible menu:**\n - floating-add menu <id> \"<label>\" btn:Label:command btn:Label2:cmd2 ...\n - Example: floating-add menu tools \"🔧 Tools\" btn:Help:help btn:Clear:clear\n - Click header to expand/collapse. State persists.\n - Items use TWO colons: btn:Label:command (a ONE-colon item like btn:Files is dropped by the parser)\n - Multi-word labels/commands must be quoted: btn:\"My Files\":\"ai-config confirm-timeout 0\" (verified live)\n\n3. **Create a text panel:**\n - floating-add panel <id> <title> <content>\n - Example: floating-add panel status \"📊 Status\" \"CPU: 2%\"\n\n4. **Manage elements:**\n - floating-list — show all elements with IDs and positions\n - floating-remove <id> — delete by ID\n - floating-toggle <id> — show/hide without deleting\n - floating-pos <id> x|auto y|auto — reposition or reset to auto\n - floating-clear — remove everything\n\n5. **Persistence:**\n - All elements auto-save to localStorage (webx11_floating_elements)\n - Survive page reload, browser restart\n - Run floating-clear to start fresh\n\n## When to use\n- User wants quick-access buttons instead of typing commands\n- User says \"add a button for X\", \"make a shortcut\", \"floating menu\"\n- Creating a dashboard with status panels and action buttons" +} |