diff --git a/frontend/packages/dashboard/src/dashboard.css b/frontend/packages/dashboard/src/dashboard.css index fd4aac40..29f59830 100644 --- a/frontend/packages/dashboard/src/dashboard.css +++ b/frontend/packages/dashboard/src/dashboard.css @@ -384,13 +384,9 @@ a:hover { padding: 0.4em 0.9em; cursor: pointer; } -.agent-menu-item:not(:disabled):hover { +.agent-menu-item:hover { background: var(--surface1); } -.agent-menu-item:disabled { - opacity: 0.35; - cursor: default; -} .agent-menu-sep { height: 1px; background: var(--surface2); diff --git a/frontend/packages/dashboard/src/tabs.js b/frontend/packages/dashboard/src/tabs.js index 62946eb9..2a6229b7 100644 --- a/frontend/packages/dashboard/src/tabs.js +++ b/frontend/packages/dashboard/src/tabs.js @@ -328,8 +328,8 @@ window.marked = marked; // ─── per-agent context menu ────────────────────────────────────────── // Three-dot (⋮) button on each agent card for quick single-agent // lifecycle actions without needing to select first. State-aware: - // restart/stop disabled when agent is stopped, start disabled when - // running, destroy/purge hidden for the manager. + // restart/stop only shown when running, start only shown when stopped, + // destroy/purge hidden for the manager. // The button is CSS-invisible until the row is hovered (or menu is // open) so it doesn't clutter quiet rows. @@ -351,12 +351,12 @@ window.marked = marked; document.addEventListener('click', (e) => { if (!e.target.closest('.agent-menu')) closeAllAgentMenus(); }, true); - // Close on Escape (shares the keydown listener below with selection-clear; - // close happens first since no selection change is needed here). + // Close on Escape. stopImmediatePropagation so the selection-clear + // handler on the same element doesn't also fire when a menu is open. document.addEventListener('keydown', (e) => { if (e.key === 'Escape' && openAgentMenu) { closeAllAgentMenus(); - e.stopPropagation(); + e.stopImmediatePropagation(); } }, true); @@ -401,16 +401,11 @@ window.marked = marked; class: 'agent-menu-item', role: 'menuitem', }, label); - if (opts.disabled) { - item.disabled = true; - if (opts.title) item.title = opts.title; - } else { - item.addEventListener('click', async () => { - closeAllAgentMenus(); - if (opts.confirm && !confirm(opts.confirm)) return; - await agentMenuPost(opts.action, c.name, opts.body || null); - }); - } + item.addEventListener('click', async () => { + closeAllAgentMenus(); + if (opts.confirm && !confirm(opts.confirm)) return; + await agentMenuPost(opts.action, c.name, opts.body || null); + }); li.append(item); return li; } @@ -419,29 +414,19 @@ window.marked = marked; return el('li', { class: 'agent-menu-sep', role: 'separator' }); } + // Show only actions that are applicable in the current state. + if (c.running) { + dropdown.append( + menuItem('↺ R3ST4RT', { action: '/restart/', confirm: `restart ${c.name}?` }), + menuItem('■ ST0P', { action: '/kill/', confirm: `stop ${c.name}?` }), + ); + } else { + dropdown.append( + menuItem('▶ ST4RT', { action: '/start/', confirm: `start ${c.name}?` }), + ); + } dropdown.append( - menuItem('↺ R3ST4RT', { - action: '/restart/', - disabled: !c.running, - title: 'agent is stopped', - confirm: `restart ${c.name}?`, - }), - menuItem('■ ST0P', { - action: '/kill/', - disabled: !c.running, - title: 'agent is already stopped', - confirm: `stop ${c.name}?`, - }), - menuItem('▶ ST4RT', { - action: '/start/', - disabled: c.running, - title: 'agent is already running', - confirm: `start ${c.name}?`, - }), - menuItem('↻ R3BU1LD', { - action: '/rebuild/', - confirm: `rebuild ${c.name}? hot-reloads the container.`, - }), + menuItem('↻ R3BU1LD', { action: '/rebuild/', confirm: `rebuild ${c.name}? hot-reloads the container.` }), ); if (!c.is_manager) {