diff --git a/docs/web-ui/agent.md b/docs/web-ui/agent.md index c8271f67..b9bbd8b7 100644 --- a/docs/web-ui/agent.md +++ b/docs/web-ui/agent.md @@ -63,28 +63,18 @@ through. Three flex columns: click opens the loose-ends flyout. - **Overflow button** (`⋯`): always visible. Opens a frosted popover (`#overflow-menu`, positioned outside the header to escape any - stacking context) with four management rows followed by a model - quick-picker section: `↑ dashboard` (link), `↻ rebuild container` - (POST confirm, same action as the dashboard R3BU1LD button), - `↻ new claude session` (POST confirm → `POST /api/new-session`; - next turn drops `--continue`), `🔓 logout` (POST confirm → `POST - /api/logout`; SIGINTs any in-flight turn, wipes OAuth credential - files, flips the agent to `needs_login` — session history - preserved). All destructive actions require one extra click to - acknowledge — rare ops shouldn't live in the primary state strip. - Below a separator, a **model quick-picker** section labelled - `model` renders three one-click shortcuts: `haiku (fast)`, - `sonnet (balanced)`, `opus (powerful)`. Clicking a button POSTs - `/api/model` with the alias (same path as the `/model ` - slash command). The button for the currently-active model is - highlighted via the `active` class; `renderModelChip` keeps the - picker state in sync with live `model_changed` events so it stays - accurate when the model is changed from another session. Clicking - the already-active model closes the menu without an extra POST. - The popover's display rules are scoped to `:not([hidden])` so the - `[hidden]` HTML attribute's UA `display: none` isn't overridden by - the author CSS's `display: flex` — the popover stays hidden until - JS removes the attribute. + stacking context) with four rows: `↑ dashboard` (link), `↻ rebuild + container` (POST confirm, same action as the dashboard R3BU1LD + button), `↻ new claude session` (POST confirm → `POST + /api/new-session`; next turn drops `--continue`), `🔓 logout` + (POST confirm → `POST /api/logout`; SIGINTs any in-flight turn, + wipes OAuth credential files, flips the agent to `needs_login` + — session history preserved). All destructive actions require + one extra click to acknowledge — rare ops shouldn't live in the + primary state strip. The popover's display rules are scoped to + `:not([hidden])` so the `[hidden]` HTML attribute's UA `display: + none` isn't overridden by the author CSS's `display: flex` — + the popover stays hidden until JS removes the attribute. `/api/state` is fetched once on cold load (+ while `status === 'needs_login_in_progress'`); all other updates arrive via diff --git a/frontend/packages/agent/src/agent.css b/frontend/packages/agent/src/agent.css index 2636b4ba..544cfc27 100644 --- a/frontend/packages/agent/src/agent.css +++ b/frontend/packages/agent/src/agent.css @@ -263,32 +263,6 @@ h2, h3 { opacity: 0.4; cursor: progress; } -.overflow-sep { - height: 1px; - background: var(--border); - margin: 0.3em 0.4em; -} -.overflow-section-label { - font-size: 0.72em; - letter-spacing: 0.1em; - color: var(--muted); - padding: 0 0.7em 0.1em; - text-transform: uppercase; -} -.overflow-item-model { - color: var(--muted); - font-variant-numeric: tabular-nums; -} -.overflow-item-model:hover { - color: var(--fg); - background: rgba(203, 166, 247, 0.08); - border-color: var(--purple-dim); -} -.overflow-item-model.active { - color: var(--purple); - border-color: var(--purple-dim); - background: rgba(203, 166, 247, 0.06); -} /* Header pill — inbox / loose-ends triggers. Compact, count-prominent. */ .header-pill { diff --git a/frontend/packages/agent/src/app.js b/frontend/packages/agent/src/app.js index c47683df..b9320108 100644 --- a/frontend/packages/agent/src/app.js +++ b/frontend/packages/agent/src/app.js @@ -272,39 +272,6 @@ window.marked = marked; }); menu.append(logoutBtn); - // ─── model quick-picker ──────────────────────────────────────── - // Three one-click shortcuts for the most common model aliases. - // The active model is highlighted via the `active` class; see - // `renderModelChip` which updates `modelPickerBtns` live. - const modelSep = el('div', { class: 'overflow-sep', 'aria-hidden': 'true' }); - const modelLabel = el('div', { class: 'overflow-section-label' }, 'model'); - menu.append(modelSep, modelLabel); - const MODEL_ALIASES = [ - { name: 'haiku', label: 'haiku (fast)' }, - { name: 'sonnet', label: 'sonnet (balanced)' }, - { name: 'opus', label: 'opus (powerful)' }, - ]; - modelPickerBtns = []; - for (const m of MODEL_ALIASES) { - const btn = el('button', { - type: 'button', - class: 'overflow-item overflow-item-model', - role: 'menuitem', - title: `/model ${m.name}`, - 'data-model': m.name, - }, - el('span', { class: 'overflow-item-icon', 'aria-hidden': 'true' }, '⊞'), - m.label, - ); - btn.addEventListener('click', () => { - if (currentModel === m.name) { closeOverflowMenu(); return; } - closeOverflowMenu(); - postModel(m.name); - }); - modelPickerBtns.push(btn); - menu.append(btn); - } - overflowMenuPopulated = true; } @@ -464,13 +431,6 @@ window.marked = marked; // bus-driven `status_changed` handler so it can re-enable the // composer without waiting for the next snapshot fetch. let currentLabel = ''; - // Tracked so the overflow-menu model picker can highlight the active - // model and avoid a redundant `/api/model` POST on same-model click. - let currentModel = null; - // References to the model picker buttons populated in the overflow - // menu so renderModelChip can update their `active` state without - // rebuilding the whole menu. - let modelPickerBtns = []; const SLASH_COMMANDS = [ { name: '/help', desc: 'list slash commands' }, @@ -1009,23 +969,12 @@ window.marked = marked; } function renderModelChip(model) { - currentModel = model || null; const el_ = $('model-chip'); if (!el_) return; - if (!model) { el_.hidden = true; } else { - el_.hidden = false; - el_.textContent = 'model · ' + model; - el_.title = `claude --model ${model}\nset via the operator's /model command; persists across turns until changed`; - } - // Sync model picker buttons: highlight the active alias. A short alias - // (haiku/sonnet/opus) matches if `model` ends with that string; full - // API names like `claude-3-5-haiku-20241022` match on the suffix too. - for (const btn of modelPickerBtns) { - const alias = btn.dataset.model || ''; - const isActive = !!model && (model === alias || model.endsWith(alias)); - btn.classList.toggle('active', isActive); - btn.setAttribute('aria-pressed', String(isActive)); - } + if (!model) { el_.hidden = true; return; } + el_.hidden = false; + el_.textContent = 'model · ' + model; + el_.title = `claude --model ${model}\nset via the operator's /model command; persists across turns until changed`; } // Token badges — two separate chips: // ctx · N last inference's prompt size = current context window