diff --git a/apps/desktop/src/renderer/components/NewWorkspaceModal/NewWorkspaceModalDraftContext.tsx b/apps/desktop/src/renderer/components/NewWorkspaceModal/NewWorkspaceModalDraftContext.tsx index c72dc7d6d1..33d12cfb2d 100644 --- a/apps/desktop/src/renderer/components/NewWorkspaceModal/NewWorkspaceModalDraftContext.tsx +++ b/apps/desktop/src/renderer/components/NewWorkspaceModal/NewWorkspaceModalDraftContext.tsx @@ -7,6 +7,10 @@ import { useMemo, useState, } from "react"; +import { useCreateFromPr } from "renderer/react-query/workspaces/useCreateFromPr"; +import { useCreateWorkspace } from "renderer/react-query/workspaces/useCreateWorkspace"; +import { useOpenExternalWorktree } from "renderer/react-query/workspaces/useOpenExternalWorktree"; +import { useOpenTrackedWorktree } from "renderer/react-query/workspaces/useOpenTrackedWorktree"; export type NewWorkspaceModalTab = | "prompt" @@ -66,6 +70,10 @@ interface NewWorkspaceModalDraftContextValue { draftVersion: number; closeModal: () => void; closeAndResetDraft: () => void; + createWorkspace: ReturnType; + createFromPr: ReturnType; + openTrackedWorktree: ReturnType; + openExternalWorktree: ReturnType; runAsyncAction: ( promise: Promise, messages: NewWorkspaceModalActionMessages, @@ -84,6 +92,12 @@ export function NewWorkspaceModalDraftProvider({ }: PropsWithChildren<{ onClose: () => void }>) { const [state, setState] = useState(buildInitialDraftState); + // Owned here so onSuccess survives Dialog unmounting content on close. + const createWorkspace = useCreateWorkspace(); + const createFromPr = useCreateFromPr(); + const openTrackedWorktree = useOpenTrackedWorktree(); + const openExternalWorktree = useOpenExternalWorktree(); + const updateDraft = useCallback((patch: Partial) => { setState((state) => ({ ...state, @@ -153,6 +167,10 @@ export function NewWorkspaceModalDraftProvider({ draftVersion: state.draftVersion, closeModal: onClose, closeAndResetDraft, + createWorkspace, + createFromPr, + openTrackedWorktree, + openExternalWorktree, runAsyncAction, updateDraft, resetDraft, @@ -160,6 +178,10 @@ export function NewWorkspaceModalDraftProvider({ }), [ closeAndResetDraft, + createFromPr, + createWorkspace, + openExternalWorktree, + openTrackedWorktree, onClose, resetDraft, resetDraftIfVersion, diff --git a/apps/desktop/src/renderer/components/NewWorkspaceModal/components/BranchesGroup/BranchesGroup.tsx b/apps/desktop/src/renderer/components/NewWorkspaceModal/components/BranchesGroup/BranchesGroup.tsx index cddacfe871..5cc708b627 100644 --- a/apps/desktop/src/renderer/components/NewWorkspaceModal/components/BranchesGroup/BranchesGroup.tsx +++ b/apps/desktop/src/renderer/components/NewWorkspaceModal/components/BranchesGroup/BranchesGroup.tsx @@ -8,12 +8,7 @@ import { useCallback, useMemo, useState } from "react"; import { GoArrowUpRight, GoGitBranch, GoGlobe } from "react-icons/go"; import { useDebouncedValue } from "renderer/hooks/useDebouncedValue"; import { electronTrpc } from "renderer/lib/electron-trpc"; -import { - useCreateWorkspace, - useHandleOpenedWorktree, - useImportAllWorktrees, - useOpenExternalWorktree, -} from "renderer/react-query/workspaces"; +import { useImportAllWorktrees } from "renderer/react-query/workspaces"; import { navigateToWorkspace } from "renderer/routes/_authenticated/_dashboard/utils/workspace-navigation"; import { useHotkeysStore } from "renderer/stores/hotkeys/store"; import { useNewWorkspaceModalDraft } from "../../NewWorkspaceModalDraftContext"; @@ -30,12 +25,15 @@ export function BranchesGroup({ projectId }: BranchesGroupProps) { const platform = useHotkeysStore((state) => state.platform); const modKey = platform === "darwin" ? "⌘" : "Ctrl"; const navigate = useNavigate(); - const createWorkspace = useCreateWorkspace(); - const handleOpenedWorktree = useHandleOpenedWorktree(); const importAllWorktrees = useImportAllWorktrees(); - const openExternalWorktree = useOpenExternalWorktree(); - const { draft, closeAndResetDraft, runAsyncAction } = - useNewWorkspaceModalDraft(); + const { + createWorkspace, + openTrackedWorktree, + openExternalWorktree, + draft, + closeAndResetDraft, + runAsyncAction, + } = useNewWorkspaceModalDraft(); const [filterMode, setFilterMode] = useState("all"); // Fast query: local branches + cached remote refs (no network) @@ -232,12 +230,6 @@ export function BranchesGroup({ projectId }: BranchesGroupProps) { [closeAndResetDraft, navigate], ); - const openTrackedWorktree = electronTrpc.workspaces.openWorktree.useMutation({ - onSuccess: async (data) => { - await handleOpenedWorktree(data); - }, - }); - const handleOpenTrackedWorktree = useCallback( (worktreeId: string, branchName: string) => { void runAsyncAction(openTrackedWorktree.mutateAsync({ worktreeId }), { diff --git a/apps/desktop/src/renderer/components/NewWorkspaceModal/components/IssuesGroup/IssuesGroup.tsx b/apps/desktop/src/renderer/components/NewWorkspaceModal/components/IssuesGroup/IssuesGroup.tsx index a3430eb6b0..adccda0db8 100644 --- a/apps/desktop/src/renderer/components/NewWorkspaceModal/components/IssuesGroup/IssuesGroup.tsx +++ b/apps/desktop/src/renderer/components/NewWorkspaceModal/components/IssuesGroup/IssuesGroup.tsx @@ -13,7 +13,6 @@ import { GATED_FEATURES, usePaywall } from "renderer/components/Paywall"; import { useDebouncedValue } from "renderer/hooks/useDebouncedValue"; import { electronTrpc } from "renderer/lib/electron-trpc"; import { getSlugColumnWidth } from "renderer/lib/slug-width"; -import { useCreateWorkspace } from "renderer/react-query/workspaces"; import { StatusIcon, type StatusType, @@ -32,8 +31,7 @@ export function IssuesGroup({ projectId }: IssuesGroupProps) { const collections = useCollections(); const navigate = useNavigate(); const { gateFeature } = usePaywall(); - const createWorkspace = useCreateWorkspace(); - const { draft, closeAndResetDraft, runAsyncAction } = + const { createWorkspace, draft, closeAndResetDraft, runAsyncAction } = useNewWorkspaceModalDraft(); const { data: integrations } = useLiveQuery( diff --git a/apps/desktop/src/renderer/components/NewWorkspaceModal/components/PromptGroup/PromptGroup.tsx b/apps/desktop/src/renderer/components/NewWorkspaceModal/components/PromptGroup/PromptGroup.tsx index 9ff2beb87d..907d284921 100644 --- a/apps/desktop/src/renderer/components/NewWorkspaceModal/components/PromptGroup/PromptGroup.tsx +++ b/apps/desktop/src/renderer/components/NewWorkspaceModal/components/PromptGroup/PromptGroup.tsx @@ -27,7 +27,6 @@ import { } from "renderer/assets/app-icons/preset-icons"; import { electronTrpc } from "renderer/lib/electron-trpc"; import { resolveEffectiveWorkspaceBaseBranch } from "renderer/lib/workspaceBaseBranch"; -import { useCreateWorkspace } from "renderer/react-query/workspaces"; import { useHotkeysStore } from "renderer/stores/hotkeys/store"; import { resolveBranchPrefix, @@ -50,7 +49,7 @@ export function PromptGroup({ projectId }: PromptGroupProps) { const modKey = platform === "darwin" ? "⌘" : "Ctrl"; const isDark = useIsDarkTheme(); const textareaRef = useRef(null); - const { closeModal, draft, runAsyncAction, updateDraft } = + const { closeModal, createWorkspace, draft, runAsyncAction, updateDraft } = useNewWorkspaceModalDraft(); const [baseBranchOpen, setBaseBranchOpen] = useState(false); const { @@ -64,10 +63,6 @@ export function PromptGroup({ projectId }: PromptGroupProps) { } = draft; const runSetupScriptRef = useRef(runSetupScript); runSetupScriptRef.current = runSetupScript; - const createWorkspace = useCreateWorkspace({ - resolveInitialCommands: (commands) => - runSetupScriptRef.current ? commands : null, - }); const [selectedAgent, setSelectedAgent] = useState( () => { if (typeof window === "undefined") return "none"; @@ -222,7 +217,11 @@ export function PromptGroup({ projectId }: PromptGroupProps) { baseBranch: baseBranch || undefined, applyPrefix, }, - launchRequest ? { agentLaunchRequest: launchRequest } : undefined, + { + agentLaunchRequest: launchRequest ?? undefined, + resolveInitialCommands: (commands) => + runSetupScriptRef.current ? commands : null, + }, ), { loading: "Creating workspace...", diff --git a/apps/desktop/src/renderer/components/NewWorkspaceModal/components/PullRequestsGroup/PullRequestsGroup.tsx b/apps/desktop/src/renderer/components/NewWorkspaceModal/components/PullRequestsGroup/PullRequestsGroup.tsx index 8ebe67f6b2..9e6dc165b4 100644 --- a/apps/desktop/src/renderer/components/NewWorkspaceModal/components/PullRequestsGroup/PullRequestsGroup.tsx +++ b/apps/desktop/src/renderer/components/NewWorkspaceModal/components/PullRequestsGroup/PullRequestsGroup.tsx @@ -15,7 +15,6 @@ import { SiGithub } from "react-icons/si"; import { GATED_FEATURES, usePaywall } from "renderer/components/Paywall"; import { useDebouncedValue } from "renderer/hooks/useDebouncedValue"; import { electronTrpc } from "renderer/lib/electron-trpc"; -import { useCreateFromPr } from "renderer/react-query/workspaces/useCreateFromPr"; import { navigateToWorkspace } from "renderer/routes/_authenticated/_dashboard/utils/workspace-navigation"; import { useCollections } from "renderer/routes/_authenticated/providers/CollectionsProvider"; import { useNewWorkspaceModalDraft } from "../../NewWorkspaceModalDraftContext"; @@ -34,8 +33,7 @@ export function PullRequestsGroup({ const collections = useCollections(); const navigate = useNavigate(); const { gateFeature } = usePaywall(); - const createFromPr = useCreateFromPr(); - const { draft, closeAndResetDraft, runAsyncAction } = + const { createFromPr, draft, closeAndResetDraft, runAsyncAction } = useNewWorkspaceModalDraft(); // Match GitHub repository by owner + name from the local project diff --git a/apps/desktop/src/renderer/react-query/workspaces/index.ts b/apps/desktop/src/renderer/react-query/workspaces/index.ts index dc49fc3fe1..0e8928ea7d 100644 --- a/apps/desktop/src/renderer/react-query/workspaces/index.ts +++ b/apps/desktop/src/renderer/react-query/workspaces/index.ts @@ -10,6 +10,7 @@ export { useMoveWorkspacesToSection } from "./useMoveWorkspacesToSection"; export { useMoveWorkspaceToSection } from "./useMoveWorkspaceToSection"; export { useOpenExternalWorktree } from "./useOpenExternalWorktree"; export { useOpenMainRepoWorkspace } from "./useOpenMainRepoWorkspace"; +export { useOpenTrackedWorktree } from "./useOpenTrackedWorktree"; export { useReorderProjectChildren } from "./useReorderProjectChildren"; export { useReorderSections } from "./useReorderSections"; export { useReorderWorkspaces } from "./useReorderWorkspaces"; diff --git a/apps/desktop/src/renderer/react-query/workspaces/useCreateWorkspace.ts b/apps/desktop/src/renderer/react-query/workspaces/useCreateWorkspace.ts index d86c3901f3..9675040263 100644 --- a/apps/desktop/src/renderer/react-query/workspaces/useCreateWorkspace.ts +++ b/apps/desktop/src/renderer/react-query/workspaces/useCreateWorkspace.ts @@ -19,7 +19,9 @@ interface UseCreateWorkspaceOptions extends NonNullable { type PendingSetupOverrides = Pick< PendingTerminalSetup, "defaultPresets" | "agentCommand" | "agentLaunchRequest" ->; +> & { + resolveInitialCommands?: (serverCommands: string[] | null) => string[] | null; +}; export function useCreateWorkspace(options?: UseCreateWorkspaceOptions) { const navigate = useNavigate(); @@ -82,11 +84,14 @@ export function useCreateWorkspace(options?: UseCreateWorkspaceOptions) { workspaceId: data.workspace.id, } : undefined; + const resolveCommands = + pendingSetupOverrides?.resolveInitialCommands ?? + options?.resolveInitialCommands; addPendingTerminalSetup({ workspaceId: data.workspace.id, projectId: data.projectId, - initialCommands: options?.resolveInitialCommands - ? options.resolveInitialCommands(data.initialCommands) + initialCommands: resolveCommands + ? resolveCommands(data.initialCommands) : data.initialCommands, defaultPresets: pendingSetupOverrides?.defaultPresets, agentCommand: pendingSetupOverrides?.agentCommand, diff --git a/apps/desktop/src/renderer/react-query/workspaces/useOpenTrackedWorktree.ts b/apps/desktop/src/renderer/react-query/workspaces/useOpenTrackedWorktree.ts new file mode 100644 index 0000000000..11e603431f --- /dev/null +++ b/apps/desktop/src/renderer/react-query/workspaces/useOpenTrackedWorktree.ts @@ -0,0 +1,12 @@ +import { electronTrpc } from "renderer/lib/electron-trpc"; +import { useHandleOpenedWorktree } from "./useHandleOpenedWorktree"; + +export function useOpenTrackedWorktree() { + const handleOpenedWorktree = useHandleOpenedWorktree(); + + return electronTrpc.workspaces.openWorktree.useMutation({ + onSuccess: async (data) => { + await handleOpenedWorktree(data); + }, + }); +}