// 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 }; } }