From 526c420f4591f0a58f805b473fa76d3da2981eae Mon Sep 17 00:00:00 2001 From: Agando Date: Sat, 17 Oct 2026 20:17:28 +0200 Subject: 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 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- downloads/floatimg-buttons.html | 290 ++++++++++ downloads/floating-ui-designer.html | 932 ++++++++++++++++++++++++++++++ scripts/gen-floating-designer-registry.py | 117 ++++ scripts/test-floating-designer.mjs | 144 +++++ skills/floating-ui.json | 8 +- 5 files changed, 1487 insertions(+), 4 deletions(-) create mode 100644 downloads/floatimg-buttons.html create mode 100644 downloads/floating-ui-designer.html create mode 100644 scripts/gen-floating-designer-registry.py create mode 100644 scripts/test-floating-designer.mjs 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 @@ + + + + + + Floating Buttons Setup + + + +
+

πŸ‘† Floating Buttons Setup

+

Click a card to copy the command  Β·  paste it in the WebX11 terminal

+ +
+
Preview
+
+
+ +
+ ⚠️ Note: clipboard-history is not a real command in this system β€” + that's why the πŸ“‹ button did nothing. It was replaced below with real, verified commands. +
+ +

βœ… Your Buttons (all verified working)

+
+ + +

πŸ’‘ More Examples (all verified working)

+
+ +
+ +
Copied!
+ + + + \ 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 @@ + + + + + +🎨 Floating UI Designer v3 β€” WebX11 newOS4 + + + + +

🎨 Floating UI Designer v3 β€” quoting, import & registry fixed

+
Design floating buttons, menus & panels β†’ live preview β†’ copy validated floating-add commands for the WebX11 newOS4 terminal
+ +
+ βœ… Syntax verified against FloatingModule source code (2026-10-17): menu items are space-separated and use two colons β€” btn:Label:command.
+ Old pipe syntax (btn:Label|cmd) was wrong and silently produced broken menus. Multi-word labels/commands are auto-quoted here (btn:"My Label":"cmd with args") β€” tested live in the real terminal and confirmed working.
+ v3 (2026-10-17): auto-quoting, import/preset parsing, ID slugs and the full 344-command registry verified against the live terminal (v4.6.26). +
+ +
+ +
+
+

1 Β· Real grammar (from the module's parser)

+
+ button: floating-add btn <id> "label" <command> [args...]
+ menu:   floating-add menu <id> "label" btn:Label:cmd btn:Label2:cmd2 ...
+ panel:   floating-add panel <id> "title" "content"
+ ⚠ an item with ONE colon (btn:Files) is dropped β†’ "No valid sub-buttons"
+ ⚠ menu items may NOT contain bare spaces β€” quote them: btn:"A B":ls +
+ +

2 Β· Design your element

+
+ + + +
+ + +
+ + +
+ + + + +
+ + + + + + +
+ +
+

3 Β· Presets (all commands verified real)

+
+ +
+ +
+

4 Β· Command palette

+ +
aiβœ“ aiAsk the AI assistant
aiβœ“ askAsk the AI assistant
aiβœ“ ai-clearClear AI context
aiβœ“ ai-systemAI system prompt
aiβœ“ ai-contextAI context info
aiβœ“ ai-allowAllow AI command
aiβœ“ ai-denyDeny AI command
aiβœ“ ai-ignoreIgnore AI command
aiβœ“ ai-configAI configuration (user-verified)
aiβœ“ chatOpen chat studio
aiβœ“ debugOpen debug panel
aiβœ“ debug-aiAI debug panel
agentsβœ“ agent-listList registered agents
agentsβœ“ agent-statusAgent status
agentsβœ“ agent-registerRegister agent
agentsβœ“ agent-unregisterUnregister agent
agentsβœ“ contract-listList A2A contracts
agentsβœ“ orchestrateRun orchestrator
agentsβœ“ orchestrator-statusOrchestrator status
agentsβœ“ orchestrator-spawnSpawn orchestrator
agentsβœ“ orchestrator-killKill orchestrator
aiβœ“ webxaiWebX AI alias
aiβœ“ wxWebX AI alias
filesβœ“ lsList current directory
filesβœ“ catRead a file
filesβœ“ putWrite a file
filesβœ“ rmDelete a file
filesβœ“ trashTrash a file
filesβœ“ trash-dirTrash a directory
filesβœ“ ls-trashList trash
filesβœ“ empty-trashEmpty trash
filesβœ“ nvfs-indexBuild neural VFS index
filesβœ“ nvfs-statusNeural VFS status
filesβœ“ nvfs-resolveSemantic file search
filesβœ“ nvfs-treeConcept tree
filesβœ“ saveSave VFS snapshot
filesβœ“ loadLoad VFS snapshot
filesβœ“ importImport VFS
systemβœ“ helpCommand reference
systemβœ“ echoPrint text
systemβœ“ memMemory usage
systemβœ“ statusSystem status
systemβœ“ previewPreview a file
systemβœ“ logsSystem logs
systemβœ“ clearClear terminal
systemβœ“ cdChange directory
systemβœ“ modulesList modules
systemβœ“ listmodsList modules
systemβœ“ loadmodLoad a module
systemβœ“ removemodRemove a module
systemβœ“ startup-modulesManage startup modules
systemβœ“ configConfig manager
systemβœ“ config-listList config
systemβœ“ config-setSet config key
systemβœ“ config-exportExport config
systemβœ“ factory-resetFull reset (destructive)
toolsβœ“ jsRun JavaScript
toolsβœ“ js-runRun JavaScript
toolsβœ“ nodeRun JavaScript
toolsβœ“ guiSimple GUI
toolsβœ“ gui-buttonGUI button
toolsβœ“ gui-inputGUI input
toolsβœ“ gui-textGUI text
toolsβœ“ gui-clearClear GUI
toolsβœ“ gui-runRun GUI actions
toolsβœ“ ollamaOllama manager
toolsβœ“ ollamaconnectConnect Ollama
toolsβœ“ ollamamodelsList Ollama models
toolsβœ“ ollamasetmodelSet Ollama model
toolsβœ“ modelsList LLM models
toolsβœ“ model-infoModel info
toolsβœ“ analyzeMedia analyze
agentsβœ“ lease-listList leases
networkβœ“ fetchFetch a URL
networkβœ“ webFetch a URL
networkβœ“ extractExtract URL content
networkβœ“ searchWeb search
networkβœ“ sWeb search (short)
networkβœ“ imagesImage search
networkβœ“ videosVideo search
networkβœ“ serverServer monitor
networkβœ“ serversList servers
networkβœ“ server-checkCheck server
networkβœ“ monitorMonitor system
networkβœ“ system-healthSystem health
securityβœ“ secret-listList secrets
securityβœ“ secret-statusSecret vault status
securityβœ“ secret-setStore a secret
securityβœ“ secret-getRead a secret
securityβœ“ secret-lockLock vault
securityβœ“ secret-unlockUnlock vault
securityβœ“ securitySecurity info
securityβœ“ security-levelSecurity level
securityβœ“ cmd-allowAllow a command
securityβœ“ cmd-denyDeny a command
uiβœ“ floating-addAdd floating element
uiβœ“ floating-removeRemove element
uiβœ“ floating-listList elements
uiβœ“ floating-posPosition element
uiβœ“ floating-toggleShow/hide element
uiβœ“ floating-clearRemove ALL elements
files⚠ filesFile manager UI
files⚠ editOpen file editor
files⚠ findFind files
ui⚠ themeChange theme
ui⚠ mindmapCreate mindmap
ui⚠ svgRender SVG
tools⚠ calcCalculator
tools⚠ dateShow date
tools⚠ envEnvironment vars
tools⚠ whichLocate command
tools⚠ diffDiff files
tools⚠ gzipGzip file
tools⚠ gunzipUngzip file
tools⚠ encryptEncrypt file
tools⚠ decryptDecrypt file
network⚠ webdav-statusWebDAV status
network⚠ webdav-lsWebDAV listing
network⚠ webdav-pushPush to WebDAV
network⚠ webdav-pullPull from WebDAV
system⚠ cron-listList cron jobs
system⚠ wpmPackage manager
tools⚠ voiceVoice control
system⚠ aliasManage aliases
+
βœ“ = real terminal command (all default modules auto-load) Β· ⚠ = unknown, routes to the AI Β· click to insert into the form Β· search is case-insensitive.
+
+ +
+

5 Β· Import existing command

+ +
+
+
+ + +
+
+

Live preview

+
+
newOS4 β€” mock terminal
+
+ webx11@newos4:~$ floating-list
+ … your draft renders bottom-right (dashed = draft) … +
+
+
+
Menus expand on click in the preview. The real UI also lets you drag elements by their header/button; position is saved automatically.
+
+ +
+

Generated command + validation

+
+ floating-add btn my-button "πŸ†•" status + +
+
βœ“ "status" is a real terminal command
+
+ + + πŸ–₯ Open newOS4 terminal β†— +
+
+ +
+

Collection (0)

+
+
+ + +
+
Paste the copied block into the newOS4 terminal to install everything at once.
+
+
+
+ +
+ + + + \ 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