From 560e24cbc283d0ab87a0d59d952a7626ebd0001f Mon Sep 17 00:00:00 2001 From: Tom Rochette Date: Sat, 13 Jun 2026 01:55:17 -0400 Subject: [PATCH] fix: prevent worktree modal from disappearing after opening (#1585) The worktree dialog state was local React state in SessionSidebar, which gets destroyed when the component unmounts (mobile drawer close, VSCode view switch). This caused the modal to briefly appear then disappear. Three changes: - Move newWorktreeDialogOpen from local state to useUIStore so it survives component unmount - Guard useProjectSessionSelection layout effect to skip when the worktree dialog is open (prevents auto-session-selection from closing the sidebar) - Remove setSessionSwitcherOpen(false) from worktree button handler on mobile (prevents drawer close that unmounts the sidebar) Fixes #1414 --- packages/ui/src/components/session/SessionSidebar.tsx | 5 +++-- .../src/components/session/sidebar/SidebarProjectsList.tsx | 1 - .../session/sidebar/hooks/useProjectSessionSelection.ts | 5 +++++ packages/ui/src/stores/useUIStore.ts | 7 +++++++ 4 files changed, 15 insertions(+), 3 deletions(-) diff --git a/packages/ui/src/components/session/SessionSidebar.tsx b/packages/ui/src/components/session/SessionSidebar.tsx index 6c385d50..24a44243 100644 --- a/packages/ui/src/components/session/SessionSidebar.tsx +++ b/packages/ui/src/components/session/SessionSidebar.tsx @@ -189,7 +189,8 @@ export const SessionSidebar: React.FC = ({ const [projectRepoStatus, setProjectRepoStatus] = React.useState>(new Map()); const [visibleSessionCountByGroup, setVisibleSessionCountByGroup] = React.useState>(new Map()); - const [newWorktreeDialogOpen, setNewWorktreeDialogOpen] = React.useState(false); + const newWorktreeDialogOpen = useUIStore((state) => state.isNewWorktreeDialogOpen); + const setNewWorktreeDialogOpen = useUIStore((state) => state.setNewWorktreeDialogOpen); const [updateDialogOpen, setUpdateDialogOpen] = React.useState(false); const [openSidebarMenuKey, setOpenSidebarMenuKey] = React.useState(null); const [renamingFolderId, setRenamingFolderId] = React.useState(null); @@ -545,7 +546,7 @@ export const SessionSidebar: React.FC = ({ const openNewWorktreeDialog = React.useCallback(() => { setNewWorktreeDialogOpen(true); - }, []); + }, [setNewWorktreeDialogOpen]); const handleOpenUpdateDialog = React.useCallback(() => { const current = useUpdateStore.getState(); diff --git a/packages/ui/src/components/session/sidebar/SidebarProjectsList.tsx b/packages/ui/src/components/session/sidebar/SidebarProjectsList.tsx index 1b0d9e67..12f175d4 100644 --- a/packages/ui/src/components/session/sidebar/SidebarProjectsList.tsx +++ b/packages/ui/src/components/session/sidebar/SidebarProjectsList.tsx @@ -192,7 +192,6 @@ export function SidebarProjectsList(props: Props): React.ReactNode { onNewWorktreeSession={() => { if (projectKey !== props.activeProjectId) props.setActiveProjectIdOnly(projectKey); props.setActiveMainTab('chat'); - if (props.mobileVariant) props.setSessionSwitcherOpen(false); props.openNewWorktreeDialog(); }} onRenameStart={() => props.openProjectEditDialog(projectKey)} diff --git a/packages/ui/src/components/session/sidebar/hooks/useProjectSessionSelection.ts b/packages/ui/src/components/session/sidebar/hooks/useProjectSessionSelection.ts index b4d4e46b..9771f18c 100644 --- a/packages/ui/src/components/session/sidebar/hooks/useProjectSessionSelection.ts +++ b/packages/ui/src/components/session/sidebar/hooks/useProjectSessionSelection.ts @@ -3,6 +3,7 @@ import type { Session } from '@opencode-ai/sdk/v2'; import type { SessionGroup, SessionNode } from '../types'; import { normalizePath } from '../utils'; import type { MainTab } from '@/stores/useUIStore'; +import { useUIStore } from '@/stores/useUIStore'; type ProjectSection = { project: { id: string; normalizedPath: string }; @@ -93,6 +94,10 @@ export const useProjectSessionSelection = (args: Args): { currentSessionDirector return; } + if (useUIStore.getState().isNewWorktreeDialogOpen) { + return; + } + if (previousActiveProjectRef.current === activeProjectId) { return; } diff --git a/packages/ui/src/stores/useUIStore.ts b/packages/ui/src/stores/useUIStore.ts index aa6570e5..d58537f8 100644 --- a/packages/ui/src/stores/useUIStore.ts +++ b/packages/ui/src/stores/useUIStore.ts @@ -536,6 +536,7 @@ interface UIStore { isSessionCreateDialogOpen: boolean; isScheduledTasksDialogOpen: boolean; isSettingsDialogOpen: boolean; + isNewWorktreeDialogOpen: boolean; isModelSelectorOpen: boolean; sidebarSection: SidebarSection; @@ -684,6 +685,7 @@ interface UIStore { setSessionCreateDialogOpen: (open: boolean) => void; setScheduledTasksDialogOpen: (open: boolean) => void; setSettingsDialogOpen: (open: boolean) => void; + setNewWorktreeDialogOpen: (open: boolean) => void; setModelSelectorOpen: (open: boolean) => void; applyTheme: () => void; setSidebarSection: (section: SidebarSection) => void; @@ -824,6 +826,7 @@ export const useUIStore = create()( isSessionCreateDialogOpen: false, isScheduledTasksDialogOpen: false, isSettingsDialogOpen: false, + isNewWorktreeDialogOpen: false, isModelSelectorOpen: false, sidebarSection: 'sessions', settingsPage: 'home', @@ -1495,6 +1498,10 @@ export const useUIStore = create()( }); }, + setNewWorktreeDialogOpen: (open) => { + set({ isNewWorktreeDialogOpen: open }); + }, + setModelSelectorOpen: (open) => { set({ isModelSelectorOpen: open }); },