From 2bb66d180becea8635bf04315d850101454a2a4c Mon Sep 17 00:00:00 2001 From: Egor Kraev Date: Wed, 21 Oct 2026 18:11:44 +0200 Subject: [PATCH 1/2] Close the Hubs menu on a press outside it; no worktree menu on the main checkout The Hubs menu now closes on a pointer press outside it and the Hubs button, like the worktree menu and preset picker already do. Right-clicking the main worktree opened a menu whose only item, Delete worktree, was always disabled; it now opens none and closes any open one. --- src/web/ui/app.tsx | 13 ++++++++++++- src/web/ui/view.ts | 4 +++- test/e2e/contract.ts | 4 ++-- test/e2e/hubs.spec.ts | 15 +++++++++++++++ test/e2e/sidebar.spec.ts | 20 +++++++++++++++----- 5 files changed, 47 insertions(+), 9 deletions(-) diff --git a/src/web/ui/app.tsx b/src/web/ui/app.tsx index 2dfad91..453c6d4 100644 --- a/src/web/ui/app.tsx +++ b/src/web/ui/app.tsx @@ -116,6 +116,17 @@ const Hubs = ({ client }: { client: HubClient }) => { const [open, setOpen] = useState(false); const stateRef = useRef(state); const menuButton = useRef(null); + const menu = useRef(null); + useEffect(() => { + if (!open) return undefined; + const outside = (event: PointerEvent): void => { + if (event.target instanceof Node && menu.current?.contains(event.target) !== true) setOpen(false); + }; + document.addEventListener('pointerdown', outside, true); + return () => { + document.removeEventListener('pointerdown', outside, true); + }; + }, [open]); const hosts = client.store.hosts.value; const dispatch = (action: HubAction): void => { const { state: next, effect } = hubStep(stateRef.current, action); @@ -174,7 +185,7 @@ const Hubs = ({ client }: { client: HubClient }) => { /> )} {entries !== null && ( - +