diff options
Diffstat (limited to 'modules/core')
| -rw-r--r-- | modules/core/core-bundle.js | 28 | ||||
| -rw-r--r-- | modules/core/module-loader.js | 2 | ||||
| -rw-r--r-- | modules/core/module-uishell.js | 28 |
3 files changed, 41 insertions, 17 deletions
diff --git a/modules/core/core-bundle.js b/modules/core/core-bundle.js index 623bce5..c2b52d0 100644 --- a/modules/core/core-bundle.js +++ b/modules/core/core-bundle.js @@ -23326,10 +23326,13 @@ class UIShellModule { // scrollIntoView() on new output scrolled #ui-root by 464px — the title, the action cards // and the composer all left the screen (measured 2026-11-07). `clip` cannot be scrolled. '#ui-root{position:fixed;inset:0;display:none;overflow:hidden;overflow:clip;z-index:40;background:var(--bg-color);color:var(--text-color)}', - // flex column, not block: every mode is a vertical stack (chrome / content / composer / - // footer) and a block container makes each child's `flex:1 1 auto` inert — the terminal - // collapsed to 0px and the composer sat at the very bottom of a 4 600px flow. - 'body[data-ui-mode]:not([data-ui-mode="classic"]) #ui-root{display:flex;flex-direction:column;height:100%}', + // flex column, not block: a block container makes each child's `flex:1 1 auto` inert and the + // terminal collapsed to 0px. Specificity is deliberately (1,0,0) so a mode that wants a GRID + // wins — with the old `body[data-ui-mode]:not(...) #ui-root` (1,2,1) every mode's + // `display:grid` was beaten and commander/studio/cockpit laid out as a plain stack + // (commander on a phone: two rails hidden, 253px of content in an 844px black page). + '#ui-root{display:flex;flex-direction:column;height:100%}', + 'body:not([data-ui-mode]) #ui-root,body[data-ui-mode="classic"] #ui-root{display:none!important}', '#ui-root > *{min-width:0;min-height:0}', // A non-classic mode owns the viewport: the page itself must not scroll, or terminal // output drags the action cards off-screen and the composer never comes back. @@ -23412,6 +23415,14 @@ class UIShellModule { .map((a) => a.replace(/^["']/, '').replace(/["']$/, '')); const cmd = parts[0]; if (!cmd) return Promise.resolve(''); + // Commands with UI side effects are not capturable: dispatchCommand() runs them for + // real, so capturing `modules` re-opened the module-manager panel on every refresh + // tick ("the modules tab keeps opening", reported 2026-11-07). + const UI_ONLY = ['modules', 'config', 'edit', 'course', 'dashboard', 'ui', 'clear', + 'expand', 'sysmsg', 'font-size', 'setfont', 'startup-modules', 'load-local']; + if (UI_ONLY.indexOf(cmd) !== -1) { + return Promise.resolve('(' + cmd + ' opens a panel — not capturable)'); + } return term.capturePipeOutput(cmd, parts.slice(1), raw); }, fs: term.fs, @@ -23482,16 +23493,17 @@ class UIShellModule { const firstCard = wrap.querySelector('#wx11-ui-picker-list button'); if (firstCard && firstCard.focus) firstCard.focus(); const list = wrap.querySelector('#wx11-ui-picker-list'); - // Fewer than 4 registered means the ext modules are still in flight — use the static list so - // the user never sees a one-card chooser (the card handler re-checks and warns if missing). - const modes = this.list().length >= 4 ? this.list() : [ + // ALWAYS the six, in level order. `this.list()` only holds modes that have already been + // fetched, so switching on it showed 4 cards once the user had visited 4 modes and silently + // dropped commander/cockpit (reported 2026-11-07). Registered specs win for label/tagline. + const modes = [ { id: 'easy', label: 'Easy', level: 1, icon: '🌱', tagline: 'Just ask. No commands.' }, { id: 'chat', label: 'Chat', level: 2, icon: '💬', tagline: 'Conversation first.' }, { id: 'studio', label: 'Studio', level: 3, icon: '🎨', tagline: 'Chat plus a place for your files.' }, { id: 'workspace', label: 'Workspace', level: 4, icon: '🗂', tagline: 'Everything at a glance.' }, { id: 'commander', label: 'Commander', level: 5, icon: '⌨️', tagline: 'Terminal with rails.' }, { id: 'cockpit', label: 'Cockpit', level: 6, icon: '🛰', tagline: 'Live system telemetry.' } - ]; + ].map((m) => this.modes[m.id] || m); modes.forEach(m => { const b = document.createElement('button'); b.className = 'wx11-btn'; diff --git a/modules/core/module-loader.js b/modules/core/module-loader.js index 7f9ffe8..67be01b 100644 --- a/modules/core/module-loader.js +++ b/modules/core/module-loader.js @@ -4,7 +4,7 @@ // Core module bundle loaded synchronously in main.html. // This loader only handles version cache-busting, splash, terminal init, and optional ext modules. - const LATEST_VER = 'v4.6.155'; + const LATEST_VER = 'v4.6.156'; const CACHE = '?v=' + LATEST_VER; // Exposed so ext modules can register their own URL with the same cache-busting version // instead of an unversioned path the browser may already have cached (shell.js did). diff --git a/modules/core/module-uishell.js b/modules/core/module-uishell.js index 11bad38..94e9bd1 100644 --- a/modules/core/module-uishell.js +++ b/modules/core/module-uishell.js @@ -365,10 +365,13 @@ class UIShellModule { // scrollIntoView() on new output scrolled #ui-root by 464px — the title, the action cards // and the composer all left the screen (measured 2026-11-07). `clip` cannot be scrolled. '#ui-root{position:fixed;inset:0;display:none;overflow:hidden;overflow:clip;z-index:40;background:var(--bg-color);color:var(--text-color)}', - // flex column, not block: every mode is a vertical stack (chrome / content / composer / - // footer) and a block container makes each child's `flex:1 1 auto` inert — the terminal - // collapsed to 0px and the composer sat at the very bottom of a 4 600px flow. - 'body[data-ui-mode]:not([data-ui-mode="classic"]) #ui-root{display:flex;flex-direction:column;height:100%}', + // flex column, not block: a block container makes each child's `flex:1 1 auto` inert and the + // terminal collapsed to 0px. Specificity is deliberately (1,0,0) so a mode that wants a GRID + // wins — with the old `body[data-ui-mode]:not(...) #ui-root` (1,2,1) every mode's + // `display:grid` was beaten and commander/studio/cockpit laid out as a plain stack + // (commander on a phone: two rails hidden, 253px of content in an 844px black page). + '#ui-root{display:flex;flex-direction:column;height:100%}', + 'body:not([data-ui-mode]) #ui-root,body[data-ui-mode="classic"] #ui-root{display:none!important}', '#ui-root > *{min-width:0;min-height:0}', // A non-classic mode owns the viewport: the page itself must not scroll, or terminal // output drags the action cards off-screen and the composer never comes back. @@ -451,6 +454,14 @@ class UIShellModule { .map((a) => a.replace(/^["']/, '').replace(/["']$/, '')); const cmd = parts[0]; if (!cmd) return Promise.resolve(''); + // Commands with UI side effects are not capturable: dispatchCommand() runs them for + // real, so capturing `modules` re-opened the module-manager panel on every refresh + // tick ("the modules tab keeps opening", reported 2026-11-07). + const UI_ONLY = ['modules', 'config', 'edit', 'course', 'dashboard', 'ui', 'clear', + 'expand', 'sysmsg', 'font-size', 'setfont', 'startup-modules', 'load-local']; + if (UI_ONLY.indexOf(cmd) !== -1) { + return Promise.resolve('(' + cmd + ' opens a panel — not capturable)'); + } return term.capturePipeOutput(cmd, parts.slice(1), raw); }, fs: term.fs, @@ -521,16 +532,17 @@ class UIShellModule { const firstCard = wrap.querySelector('#wx11-ui-picker-list button'); if (firstCard && firstCard.focus) firstCard.focus(); const list = wrap.querySelector('#wx11-ui-picker-list'); - // Fewer than 4 registered means the ext modules are still in flight — use the static list so - // the user never sees a one-card chooser (the card handler re-checks and warns if missing). - const modes = this.list().length >= 4 ? this.list() : [ + // ALWAYS the six, in level order. `this.list()` only holds modes that have already been + // fetched, so switching on it showed 4 cards once the user had visited 4 modes and silently + // dropped commander/cockpit (reported 2026-11-07). Registered specs win for label/tagline. + const modes = [ { id: 'easy', label: 'Easy', level: 1, icon: '🌱', tagline: 'Just ask. No commands.' }, { id: 'chat', label: 'Chat', level: 2, icon: '💬', tagline: 'Conversation first.' }, { id: 'studio', label: 'Studio', level: 3, icon: '🎨', tagline: 'Chat plus a place for your files.' }, { id: 'workspace', label: 'Workspace', level: 4, icon: '🗂', tagline: 'Everything at a glance.' }, { id: 'commander', label: 'Commander', level: 5, icon: '⌨️', tagline: 'Terminal with rails.' }, { id: 'cockpit', label: 'Cockpit', level: 6, icon: '🛰', tagline: 'Live system telemetry.' } - ]; + ].map((m) => this.modes[m.id] || m); modes.forEach(m => { const b = document.createElement('button'); b.className = 'wx11-btn'; |