From f7b19c9d56a3a95d8e678ef093103c0127382fc8 Mon Sep 17 00:00:00 2001 From: iris Date: Fri, 31 Jul 2026 22:15:05 +0200 Subject: [PATCH] =?UTF-8?q?dashboard:=20convert=20the=20per-agent=20?= =?UTF-8?q?=E2=8B=AE=20context=20menu=20to=20a=20hive-agent-menu=20shadow-?= =?UTF-8?q?DOM=20component?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Moves buildAgentMenu's DOM-building body, the menuItem/menuSep/menuLink helpers, agentMenuPost, and the open-dropdown coordination logic out of swarm.js and into a new autonomous custom element (dashboard/src/agent-menu/), following the same shadow-DOM + one-dir-per- component shape as hive-dialog. swarm.js's buildAgentMenu is now a thin wrapper that constructs the element and sets ._opts before appending it, same convention hive-dialog uses since a custom element created via document.createElement can't take constructor args. The module-level "one dropdown open at a time" singleton (previously a single mutable variable in swarm.js) becomes a tracked Set of open instances inside the component module; each instance closes itself via its own close() method rather than another instance reaching into its shadow internals. The document-level outside-click and Escape listeners move into the component module too, keyed off e.composedPath() instead of e.target.closest() -- shadow-DOM event retargeting means a plain e.target check no longer reliably reaches into a specific instance's shadow tree. closeAllAgentMenus() is exported for swarm.js's buildAgentTree, which still needs to close any open menu before it replaces the container tree DOM. The hover-reveal opacity rule crosses the shadow boundary via a --menu-btn-opacity custom property (custom properties inherit through shadow boundaries): dashboard.css sets it on hover of the light-DOM hive-agent-menu element, and the component sets it directly from JS while its own dropdown is open, since that's component-internal state a CSS selector out in the light DOM can't see. The host element itself takes on the structural role (flex:none, position:relative, ...) the old light-DOM .agent-menu wrapper div played, since its shadow tree's absolute-positioned dropdown needs a positioned ancestor to anchor off of. Verified end to end with a standalone esbuild-bundled test harness run under headless chromium: row layout/flex sizing, hover-reveal opacity, and dropdown positioning all render correctly, and a scripted interaction pass (singleton exclusivity, outside-click close, Escape close, toggle behavior, menu-item click close, and the exported closeAllAgentMenus()) all pass. --- .../src/agent-menu/hive-agent-menu.css | 91 +++++++ .../src/agent-menu/hive-agent-menu.js | 244 ++++++++++++++++++ frontend/packages/dashboard/src/dashboard.css | 85 +----- frontend/packages/dashboard/src/swarm.js | 197 +------------- 4 files changed, 358 insertions(+), 259 deletions(-) create mode 100644 frontend/packages/dashboard/src/agent-menu/hive-agent-menu.css create mode 100644 frontend/packages/dashboard/src/agent-menu/hive-agent-menu.js diff --git a/frontend/packages/dashboard/src/agent-menu/hive-agent-menu.css b/frontend/packages/dashboard/src/agent-menu/hive-agent-menu.css new file mode 100644 index 00000000..9ef7cefa --- /dev/null +++ b/frontend/packages/dashboard/src/agent-menu/hive-agent-menu.css @@ -0,0 +1,91 @@ +/* hive-agent-menu.css — scoped stylesheet for the + shadow-DOM custom element (hive-agent-menu.js). Loaded as raw text at + build time (esbuild's `text` loader) and appended as a