From ea63a7a38813af3abfbeabdd18672d338b018088 Mon Sep 17 00:00:00 2001 From: ciregenz Date: Mon, 27 Jul 2026 12:08:30 -0700 Subject: [PATCH] [eric] cards: traffic-light chrome inert until card hover + tile menu lazy-mounts --- .../pages/Dashboard/cards/WindowControls.tsx | 20 +++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/frontend/src/app/pages/Dashboard/cards/WindowControls.tsx b/frontend/src/app/pages/Dashboard/cards/WindowControls.tsx index 634e381e..515b4d1b 100644 --- a/frontend/src/app/pages/Dashboard/cards/WindowControls.tsx +++ b/frontend/src/app/pages/Dashboard/cards/WindowControls.tsx @@ -59,8 +59,15 @@ export const ARC_CHIP_SX: Record = { function WindowControls({ onClose, onMinimize, onTile, tiled, fullscreen, noTileMenu }: WindowControlsProps): React.ReactElement { const [menuOpen, setMenuOpen] = useState(false); + // Menu DOM (12 tiles + labels, ~30 nodes) mounts on first green-dot hover, not per card at boot. + const [menuHot, setMenuHot] = useState(false); const closeTimer = useRef(null); - const openMenu = (): void => { if (noTileMenu) return; if (closeTimer.current) window.clearTimeout(closeTimer.current); setMenuOpen(true); }; + const openMenu = (): void => { + if (noTileMenu) return; + if (closeTimer.current) window.clearTimeout(closeTimer.current); + if (!menuHot) { setMenuHot(true); requestAnimationFrame(() => setMenuOpen(true)); return; } + setMenuOpen(true); + }; const scheduleClose = (): void => { closeTimer.current = window.setTimeout(() => setMenuOpen(false), 180); }; const stop = (e: React.PointerEvent | React.MouseEvent): void => { e.stopPropagation(); }; @@ -99,7 +106,12 @@ function WindowControls({ onClose, onMinimize, onTile, tiled, fullscreen, noTile return ( + sx={{ + display: 'flex', gap: '8px', alignItems: 'center', flex: 'none', '&:hover span': { opacity: 1 }, + // Inert until the card is hovered: crossing a card can't hit-test or fire React enter/leave + // through the dots, and you can't aim at a dot without hovering its card first anyway. + pointerEvents: 'none', '.osw-card:hover &': { pointerEvents: 'auto' }, + }}> {btn(RED, '×', onClose, 'Close')} {btn(YELLOW, '–', onMinimize, 'Minimize')} {tiled ? '–' : '+'} - ))} - + } );