From 0a7dfc03ee1dbc29d65605e8ca37ed9d137bd2ec Mon Sep 17 00:00:00 2001 From: Brendan Allan Date: Mon, 23 Mar 2026 16:58:20 +0800 Subject: [PATCH] fix(app): lift up project hover state to layout (#18732) --- packages/app/src/pages/layout.tsx | 4 +++ .../app/src/pages/layout/sidebar-project.tsx | 29 +++++-------------- 2 files changed, 12 insertions(+), 21 deletions(-) diff --git a/packages/app/src/pages/layout.tsx b/packages/app/src/pages/layout.tsx index 88572503f..d8b073258 100644 --- a/packages/app/src/pages/layout.tsx +++ b/packages/app/src/pages/layout.tsx @@ -1989,6 +1989,10 @@ export default function Layout(props: ParentProps) { onProjectMouseEnter: (worktree, event) => aim.enter(worktree, event), onProjectMouseLeave: (worktree) => aim.leave(worktree), onProjectFocus: (worktree) => aim.activate(worktree), + onHoverOpenChanged: (worktree, hoverOpen) => { + if (!hoverOpen && state.hoverProject && state.hoverProject !== worktree) return + setState("hoverProject", hoverOpen ? worktree : undefined) + }, navigateToProject, openSidebar: () => layout.sidebar.open(), closeProject, diff --git a/packages/app/src/pages/layout/sidebar-project.tsx b/packages/app/src/pages/layout/sidebar-project.tsx index 99f1edb74..aff0645dd 100644 --- a/packages/app/src/pages/layout/sidebar-project.tsx +++ b/packages/app/src/pages/layout/sidebar-project.tsx @@ -23,6 +23,7 @@ export type ProjectSidebarContext = { onProjectMouseEnter: (worktree: string, event: MouseEvent) => void onProjectMouseLeave: (worktree: string) => void onProjectFocus: (worktree: string) => void + onHoverOpenChanged: (worktree: string, hovered: boolean) => void navigateToProject: (directory: string) => void openSidebar: () => void closeProject: (directory: string) => void @@ -197,7 +198,6 @@ const ProjectPreviewPanel = (props: { projectChildren: Accessor> workspaceSessions: (directory: string) => ReturnType workspaceChildren: (directory: string) => Map - setOpen: (value: boolean) => void ctx: ProjectSidebarContext language: ReturnType }): JSX.Element => ( @@ -264,7 +264,7 @@ const ProjectPreviewPanel = (props: { class="flex w-full text-left justify-start text-text-base px-2 hover:bg-transparent active:bg-transparent" onClick={() => { props.ctx.openSidebar() - props.setOpen(false) + props.ctx.onHoverOpenChanged(props.project.worktree, false) if (props.selected()) return props.ctx.navigateToProject(props.project.worktree) }} @@ -289,28 +289,16 @@ export const SortableProject = (props: { const workspaceEnabled = createMemo(() => props.ctx.workspacesEnabled(props.project)) const dirs = createMemo(() => props.ctx.workspaceIds(props.project)) const [state, setState] = createStore({ - open: false, menu: false, suppressHover: false, }) + const isHoverProject = () => props.ctx.hoverProject() === props.project.worktree const preview = createMemo(() => !props.mobile && props.ctx.sidebarOpened()) const overlay = createMemo(() => !props.mobile && !props.ctx.sidebarOpened()) - const active = createMemo( - () => state.menu || (preview() ? state.open : overlay() && props.ctx.hoverProject() === props.project.worktree), - ) + const active = createMemo(() => state.menu || (preview() ? isHoverProject() : overlay() && isHoverProject())) - createEffect(() => { - if (preview()) return - if (!state.open) return - setState("open", false) - }) - - createEffect(() => { - if (!selected()) return - if (!state.open) return - setState("open", false) - }) + const hoverOpen = () => isHoverProject() && preview() && !selected() && !state.menu const label = (directory: string) => { const [data] = globalSync.child(directory, { bootstrap: false }) @@ -351,7 +339,7 @@ export const SortableProject = (props: { workspacesEnabled={props.ctx.workspacesEnabled} closeProject={props.ctx.closeProject} setMenu={(value) => setState("menu", value)} - setOpen={(value) => setState("open", value)} + setOpen={(value) => props.ctx.onHoverOpenChanged(props.project.worktree, value)} setSuppressHover={(value) => setState("suppressHover", value)} language={language} /> @@ -362,7 +350,7 @@ export const SortableProject = (props: {
{ if (state.menu) return if (value && state.suppressHover) return - setState("open", value) + props.ctx.onHoverOpenChanged(props.project.worktree, value) if (value) props.ctx.setHoverSession(undefined) }} > @@ -386,7 +374,6 @@ export const SortableProject = (props: { projectChildren={projectChildren} workspaceSessions={workspaceSessions} workspaceChildren={workspaceChildren} - setOpen={(value) => setState("open", value)} ctx={props.ctx} language={language} />