frontend: split hive-agent-menu's generic dropdown mechanics into a shared hive-menu component
<hive-agent-menu> bundled two concerns: the agent-specific trigger/item list, and generic "click a trigger, get a positioned dropdown" mechanics (shadow attach, open/close, singleton close-on-open coordination, outside-click/Escape handling). Pulled the latter out into a new @hive/shared/hive-menu.js (<hive-menu>), following the established per-component-directory + ._opts-before-append shadow-DOM pattern (<hive-dialog>). <hive-agent-menu> now just builds the "⋮" trigger and the action list and hands them to an internal <hive-menu> instance. <hive-menu> takes ownership of every <hive-menu> instance in the app for singleton coordination (closeAllMenus, renamed from closeAllAgentMenus) — a deliberate widening from the old per-agent-menu-only tracking, since the mechanism was never agent-specific to begin with. The one subtlety worth spelling out: <hive-menu> projects the caller's opaque trigger/content nodes via named <slot>s rather than moving them into its own shadow root. That's load-bearing, not cosmetic — if it re-parented them into its own shadow tree instead, <hive-agent-menu>'s own classes (.agent-menu-btn, .agent-menu-item, ...) would stop applying, since a <style> only styles elements within the same shadow tree/document it's part of, and only slotting (not re-parenting) keeps the caller's nodes in the caller's own tree for styling purposes. That in turn made <hive-agent-menu>'s own shadow root redundant once it wasn't the thing positioning or owning open/close state anymore, so it's dropped in favor of a plain light-DOM element styled by dashboard.css (already the one page it renders on) — hive-agent-menu.css is gone, its rules folded into dashboard.css's per-agent-menu section, minus the positioning rules that moved into hive-menu.css as the new generic `.menu-dropdown` wrapper. Verified with a standalone esbuild bundle + a cached nix chromium driven over raw CDP (no puppeteer/playwright/python3 available): hover-reveal opacity, dropdown open/close/positioning, outside-click/Escape dismissal, and cross-instance singleton coordination all behave identically to before the split.
This commit is contained in:
parent
f7b19c9d56
commit
395c9a6df2
7 changed files with 272 additions and 203 deletions
|
|
@ -279,17 +279,90 @@ body.dashboard-shell {
|
|||
}
|
||||
|
||||
/* ── per-agent three-dot context menu ─────────────────────────────────────
|
||||
Positioned after .card-body in the flex row. The menu itself is the
|
||||
<hive-agent-menu> shadow-DOM custom element (agent-menu/hive-agent-menu.js
|
||||
+ .css) — its own shadow-scoped stylesheet carries the button/dropdown/
|
||||
item/separator rules and the `:host` rules that play the structural
|
||||
role this light-DOM wrapper used to play. The one rule that still has
|
||||
to live out here is the hover reveal: a light-DOM descendant-combinator
|
||||
selector can't reach into the shadow tree, so it's relayed across the
|
||||
shadow boundary via the `--menu-btn-opacity` custom property (custom
|
||||
properties inherit through shadow boundaries) instead — the
|
||||
component's own JS forces the same variable to 1 while its dropdown is
|
||||
open (see hive-agent-menu.js's open()/close()). */
|
||||
Positioned after .card-body in the flex row. `<hive-agent-menu>`
|
||||
(agent-menu/hive-agent-menu.js) is a light-DOM element with no shadow
|
||||
root of its own — the generic dropdown mechanics (shadow attach,
|
||||
open/close, positioning) live in the shared `<hive-menu>` component
|
||||
instead (@hive/shared/hive-menu.js), which slots this element's
|
||||
trigger/content nodes into position rather than moving them into its
|
||||
own shadow root. That keeps these nodes in the ordinary light DOM the
|
||||
whole way down, so this ordinary global stylesheet reaches them
|
||||
directly — see hive-agent-menu.js's header for the full reasoning.
|
||||
`hive-agent-menu` itself just needs the flex/alignment role the old
|
||||
light-DOM `.agent-menu` wrapper div played in this row; the
|
||||
`position: relative` an absolutely-positioned dropdown needs now lives
|
||||
on `<hive-menu>`'s own `:host` instead, since that's the element whose
|
||||
shadow tree the dropdown is actually positioned within. */
|
||||
hive-agent-menu {
|
||||
flex: none;
|
||||
align-self: flex-start;
|
||||
margin-top: 0.3em;
|
||||
}
|
||||
.agent-menu-btn {
|
||||
display: block;
|
||||
background: none;
|
||||
border: none;
|
||||
color: var(--subtext0);
|
||||
font-size: 1.1em;
|
||||
line-height: 1;
|
||||
cursor: pointer;
|
||||
padding: 0.1em 0.4em;
|
||||
border-radius: 4px;
|
||||
opacity: var(--menu-btn-opacity, 0);
|
||||
transition: opacity 120ms, background 120ms, color 120ms;
|
||||
}
|
||||
.agent-menu-btn:hover,
|
||||
.agent-menu-btn:focus-visible {
|
||||
background: color-mix(in srgb, var(--purple) 10%, transparent);
|
||||
color: var(--purple);
|
||||
outline: none;
|
||||
}
|
||||
.agent-menu-btn:focus-visible {
|
||||
outline: 1px solid var(--purple);
|
||||
}
|
||||
/* Positioning (`position`/`top`/`right`/`z-index`) is generic and lives
|
||||
on `<hive-menu>`'s own `.menu-dropdown` wrapper instead — this is just
|
||||
the item list's visual chrome. */
|
||||
.agent-menu-dropdown {
|
||||
background: var(--bg-elev);
|
||||
border: 1px solid var(--purple-dim);
|
||||
border-radius: 6px;
|
||||
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.4);
|
||||
list-style: none;
|
||||
margin: 0;
|
||||
padding: 0.3em 0;
|
||||
min-width: 10em;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.agent-menu-item {
|
||||
display: block;
|
||||
width: 100%;
|
||||
background: none;
|
||||
border: none;
|
||||
color: var(--fg);
|
||||
font-family: inherit;
|
||||
font-size: 0.88em;
|
||||
letter-spacing: 0.04em;
|
||||
text-align: left;
|
||||
padding: 0.4em 0.9em;
|
||||
cursor: pointer;
|
||||
text-decoration: none;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
.agent-menu-item:hover {
|
||||
background: var(--border);
|
||||
color: var(--purple);
|
||||
}
|
||||
.agent-menu-sep {
|
||||
height: 1px;
|
||||
background: var(--purple-dim);
|
||||
margin: 0.3em 0;
|
||||
padding: 0;
|
||||
}
|
||||
/* Hover reveal: `--menu-btn-opacity` is forced to 1 while a hover
|
||||
selector (or the trigger's own open state, set by <hive-menu>) applies
|
||||
— inherits down through `hive-agent-menu` → `<hive-menu>` → the
|
||||
trigger button same as any custom property, shadow trees included. */
|
||||
.container-row:hover hive-agent-menu {
|
||||
--menu-btn-opacity: 1;
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue