diff options
Diffstat (limited to 'modules/ext/ui-chat.js')
| -rw-r--r-- | modules/ext/ui-chat.js | 99 |
1 files changed, 99 insertions, 0 deletions
diff --git a/modules/ext/ui-chat.js b/modules/ext/ui-chat.js new file mode 100644 index 0000000..32c67e7 --- /dev/null +++ b/modules/ext/ui-chat.js @@ -0,0 +1,99 @@ +// UI mode: chat (level 2). Single column, sticky composer, slash chips, raw-terminal drawer. +class UIChatMode { + constructor() { this.commands = {}; this.terminal = null; } + requirements() { return { met: true, missing: [] }; } + getHelpInfo() { return {}; } + registerCommands() {} + init(terminal) { + this.terminal = terminal; + if (!terminal.uiShell) return; + terminal.uiShell.registerMode({ + id: 'chat', label: 'Chat', level: 2, icon: '💬', + tagline: 'Conversation first.', + mount: (api) => this.mount(api) + }); + } + mount(api) { + api.injectCSS('chat', ` +body[data-ui-mode="chat"] #ui-root { display: flex; flex-direction: column; } +#chat-head { flex: 0 0 auto; display: flex; align-items: center; gap: 8px; padding: 10px 14px; + border-bottom: 1px solid var(--border-color); background: var(--bg-secondary); } +#chat-head .title { font-weight: 600; flex: 1 1 auto; } +#chat-head button { min-height: 44px; } +#chat-stream { flex: 1 1 auto; min-height: 0; overflow-y: auto; padding: 12px 14px; + display: flex; flex-direction: column; gap: 6px; max-width: 860px; width: 100%; margin: 0 auto; } +#chat-stream .output-line { font-family: inherit; font-size: .95em; line-height: 1.5; + white-space: pre-wrap; word-break: break-word; padding: 10px 12px; border-radius: 14px; + background: var(--bg-secondary); border: 1px solid var(--border-color); max-width: 88%; } +#chat-stream .output-line.info { background: var(--ai-bg-color); border-color: var(--border-color); color: var(--ai-text-color); } +#chat-stream .output-line.error { border-color: var(--error-color); } +#chat-chips { flex: 0 0 auto; padding: 8px 14px 0; overflow-x: auto; white-space: nowrap; + max-width: 860px; margin: 0 auto; width: 100%; } +#chat-composer { flex: 0 0 auto; padding: 8px 14px 14px; max-width: 860px; margin: 0 auto; width: 100%; } +#chat-composer #input-area { display: flex; gap: 8px; align-items: flex-end; background: var(--bg-secondary); + border: 1px solid var(--border-color); border-radius: 16px; padding: 8px; } +#chat-composer .prompt-label { display: none; } +#chat-composer #command-input { width: 100%; font-family: inherit; font-size: 1em; min-height: 44px; background: transparent; border: 0; } +#chat-composer #autocomplete-dropdown { display: none; } +body[data-ui-mode="chat"] #raw-drawer { position: absolute; inset: 0 0 34px 0; z-index: 60; + background: var(--bg-color); display: none; flex-direction: column; padding: 10px; } +body[data-ui-mode="chat"] #raw-drawer.open { display: flex; } +body[data-ui-mode="chat"] #raw-drawer #terminal-container { flex: 1 1 auto; min-height: 0; overflow: auto; + font-family: var(--font-primary); font-size: .9em; } +`); + + const root = api.root; + root.innerHTML = ''; + const head = api.el('div', { id: 'chat-head' }); + head.appendChild(api.el('span', { class: 'title', text: 'newOS4' })); + const rawBtn = api.el('button', { class: 'wx11-btn', text: '⌨ Plain terminal' }); + const uiBtn = api.el('button', { class: 'wx11-btn', text: '⇄ Style' }); + uiBtn.addEventListener('click', () => api.exec('ui picker')); + head.appendChild(uiBtn); + head.appendChild(rawBtn); + + const stream = api.el('div', { id: 'chat-stream' }); + const chips = api.el('div', { id: 'chat-chips' }); + const composer = api.el('div', { id: 'chat-composer' }); + const drawer = api.el('div', { id: 'raw-drawer' }); + + const chipMap = { '/help': 'help', '/files': 'ls', '/modules': 'modules', '/sys': 'cat /sys/info' }; + ['/help', '/files', '/modules', '/sys', '/style'].forEach(label => { + const chip = api.el('span', { class: 'wx11-chip', text: label }); + chip.addEventListener('click', () => { + if (label === '/style') return void api.exec('ui picker'); + api.exec(chipMap[label]); + }); + chips.appendChild(chip); + }); + + root.appendChild(head); + root.appendChild(stream); + root.appendChild(chips); + root.appendChild(composer); + root.appendChild(drawer); + + api.attach(stream, api.stock.output); // visible stream first + api.attach(composer, api.stock.input); + // Do NOT attach the output to the drawer here: it is one node, the last attach wins, and + // the drawer starts hidden — the stream would render empty. The toggle moves it. + + const field = document.getElementById('command-input'); + if (field) field.placeholder = 'Message newOS4…'; + + let open = false; + rawBtn.addEventListener('click', () => { + open = !open; + if (open) { api.attach(drawer, api.stock.output); drawer.classList.add('open'); } + else { api.attach(stream, api.stock.output); drawer.classList.remove('open'); } + rawBtn.textContent = open ? '💬 Chat view' : '⌨ Plain terminal'; + }); + + return () => { + open = false; + drawer.classList.remove('open'); + const f = document.getElementById('command-input'); + if (f) f.placeholder = 'Type a command...'; // leave the next mode a clean prompt + }; + } +} |