From 73ff6e43dd7c6331ce550d9c3acbe7104862bead Mon Sep 17 00:00:00 2001 From: Neil <4138956+nwparker@users.noreply.github.com> Date: Tue, 19 May 2026 14:00:37 -0700 Subject: [PATCH] fix: close workspace hovercard on context menu (#2347) --- .../sidebar/WorkspaceKanbanCard.tsx | 48 +++++++++++++++++-- .../sidebar/WorktreeContextMenu.tsx | 31 ++++++++---- .../workspace-kanban-details-hover.test.ts | 28 +++++++++++ .../sidebar/workspace-kanban-details-hover.ts | 9 ++++ 4 files changed, 102 insertions(+), 14 deletions(-) create mode 100644 src/renderer/src/components/sidebar/workspace-kanban-details-hover.test.ts create mode 100644 src/renderer/src/components/sidebar/workspace-kanban-details-hover.ts diff --git a/src/renderer/src/components/sidebar/WorkspaceKanbanCard.tsx b/src/renderer/src/components/sidebar/WorkspaceKanbanCard.tsx index 63997f67d..6588adc45 100644 --- a/src/renderer/src/components/sidebar/WorkspaceKanbanCard.tsx +++ b/src/renderer/src/components/sidebar/WorkspaceKanbanCard.tsx @@ -1,4 +1,4 @@ -import React, { useCallback, useMemo } from 'react' +import React, { useCallback, useMemo, useRef, useState } from 'react' import { Pin } from 'lucide-react' import { useAppStore } from '@/store' import { Badge } from '@/components/ui/badge' @@ -10,6 +10,7 @@ import type { Repo, Worktree } from '../../../../shared/types' import WorktreeCard from './WorktreeCard' import { WorktreeActivityStatusIndicator } from './WorktreeActivityStatusIndicator' import WorktreeContextMenu from './WorktreeContextMenu' +import { getWorkspaceKanbanDetailsHoverOpenState } from './workspace-kanban-details-hover' import { writeWorkspaceDragData } from './workspace-status' type WorkspaceKanbanCardProps = { @@ -106,6 +107,8 @@ function WorkspaceKanbanCompactCard({ }: Omit): React.JSX.Element { const deleteState = useAppStore((s) => s.deleteStateByWorktreeId[worktree.id]) const isDeleting = deleteState?.isDeleting ?? false + const [detailsOpen, setDetailsOpen] = useState(false) + const contextMenuOpenRef = useRef(false) const contextWorktrees = useMemo( () => isSelected && selectedWorktrees && selectedWorktrees.length > 0 @@ -150,13 +153,45 @@ function WorkspaceKanbanCompactCard({ [contextWorktrees, isDeleting, isSelected, worktree.id] ) + const handleDetailsOpenChange = useCallback((requestedOpen: boolean) => { + setDetailsOpen( + getWorkspaceKanbanDetailsHoverOpenState({ + contextMenuOpen: contextMenuOpenRef.current, + requestedOpen + }) + ) + }, []) + + const handleContextMenuOpenChange = useCallback((open: boolean) => { + contextMenuOpenRef.current = open + if (open) { + // Why: the preview sits beside the compact card, so it should disappear + // as soon as the card's context menu becomes the active surface. + setDetailsOpen(false) + } + }, []) + + const handleContextMenuSelect = useCallback( + (event: React.MouseEvent) => { + setDetailsOpen(false) + return onContextMenuSelect(event, worktree) + }, + [onContextMenuSelect, worktree] + ) + return ( onContextMenuSelect(event, worktree)} + onContextMenuSelect={handleContextMenuSelect} + onOpenChange={handleContextMenuOpenChange} > - +