diff --git a/apps/desktop/src/renderer/routes/_authenticated/components/DashboardNewWorkspaceModal/components/DashboardNewWorkspaceForm/PromptGroup/PromptGroup.tsx b/apps/desktop/src/renderer/routes/_authenticated/components/DashboardNewWorkspaceModal/components/DashboardNewWorkspaceForm/PromptGroup/PromptGroup.tsx
index 3763b5d5e2a..c3f703a3abb 100644
--- a/apps/desktop/src/renderer/routes/_authenticated/components/DashboardNewWorkspaceModal/components/DashboardNewWorkspaceForm/PromptGroup/PromptGroup.tsx
+++ b/apps/desktop/src/renderer/routes/_authenticated/components/DashboardNewWorkspaceModal/components/DashboardNewWorkspaceForm/PromptGroup/PromptGroup.tsx
@@ -29,6 +29,7 @@ import { useAgentLaunchPreferences } from "renderer/hooks/useAgentLaunchPreferen
import { useEnabledAgents } from "renderer/hooks/useEnabledAgents";
import { PLATFORM } from "renderer/hotkeys";
import { useNewWorkspaceModalOpen } from "renderer/stores/new-workspace-modal";
+import { useV2WorkspaceCreateDefaultsStore } from "renderer/stores/v2-workspace-create-defaults";
import { useDashboardNewWorkspaceDraft } from "../../../DashboardNewWorkspaceDraftContext";
import { DevicePicker } from "../components/DevicePicker";
import { AttachmentButtons } from "./components/AttachmentButtons";
@@ -70,6 +71,19 @@ export function PromptGroup({
const navigate = useNavigate();
const attachments = useProviderAttachments();
const needsSetup = selectedProject?.needsSetup === true;
+ const persistedBaseBranchDefault = useV2WorkspaceCreateDefaultsStore(
+ (state) =>
+ projectId ? (state.baseBranchesByProjectId[projectId] ?? null) : null,
+ );
+ const setBaseBranchDefault = useV2WorkspaceCreateDefaultsStore(
+ (state) => state.setBaseBranchDefault,
+ );
+ const clearBaseBranchDefault = useV2WorkspaceCreateDefaultsStore(
+ (state) => state.clearBaseBranchDefault,
+ );
+ const setLastHostTarget = useV2WorkspaceCreateDefaultsStore(
+ (state) => state.setLastHostTarget,
+ );
const handleGoToSetup = useCallback(() => {
if (!selectedProject?.id) return;
const targetProjectId = selectedProject.id;
@@ -111,7 +125,8 @@ export function PromptGroup({
? sanitizeUserBranchName(branchName)
: friendlyFallback;
- // Reset baseBranch on project or host change.
+ // Reset baseBranch on project or host change, defaulting to the user's
+ // last selected branch for that project when one exists.
const previousProjectIdRef = useRef(projectId);
const previousHostRef = useRef(JSON.stringify(hostTarget));
useEffect(() => {
@@ -122,9 +137,12 @@ export function PromptGroup({
) {
previousProjectIdRef.current = projectId;
previousHostRef.current = nextHost;
- updateDraft({ baseBranch: null, baseBranchSource: null });
+ updateDraft({
+ baseBranch: persistedBaseBranchDefault?.branchName ?? null,
+ baseBranchSource: persistedBaseBranchDefault?.source ?? null,
+ });
}
- }, [projectId, hostTarget, updateDraft]);
+ }, [projectId, hostTarget, persistedBaseBranchDefault, updateDraft]);
// ── Branch picker controller ─────────────────────────────────────
const { pickerProps } = useBranchPickerController({
@@ -133,8 +151,16 @@ export function PromptGroup({
baseBranch,
runSetupScript: draft.runSetupScript,
typedWorkspaceName: workspaceName,
- onBaseBranchChange: (branch, source) =>
- updateDraft({ baseBranch: branch, baseBranchSource: source }),
+ onBaseBranchChange: (branch, source) => {
+ if (projectId) {
+ if (branch && source) {
+ setBaseBranchDefault(projectId, branch, source);
+ } else {
+ clearBaseBranchDefault(projectId);
+ }
+ }
+ updateDraft({ baseBranch: branch, baseBranchSource: source });
+ },
closeModal,
});
@@ -389,7 +415,10 @@ export function PromptGroup({
updateDraft({ hostTarget: t })}
+ onSelectHostTarget={(t) => {
+ setLastHostTarget(t);
+ updateDraft({ hostTarget: t });
+ }}
/>
state.setLastProjectId,
+ );
const collections = useCollections();
const { data: session } = authClient.useSession();
const activeOrganizationId = env.SKIP_ENV_VALIDATION
@@ -76,12 +80,29 @@ export function DashboardNewWorkspaceModalContent({
const areProjectsReady = v2Projects !== undefined;
const appliedPreSelectionRef = useRef(null);
+ const appliedHostTargetRef = useRef(false);
useEffect(() => {
if (!isOpen) {
appliedPreSelectionRef.current = null;
+ appliedHostTargetRef.current = false;
+ return;
+ }
+ if (appliedHostTargetRef.current) return;
+ appliedHostTargetRef.current = true;
+ const persistedHostTarget =
+ useV2WorkspaceCreateDefaultsStore.getState().lastHostTarget;
+ const validHostTarget =
+ persistedHostTarget?.kind === "local"
+ ? persistedHostTarget
+ : persistedHostTarget?.kind === "host" &&
+ typeof persistedHostTarget.hostId === "string"
+ ? persistedHostTarget
+ : null;
+ if (validHostTarget) {
+ updateDraft({ hostTarget: validHostTarget });
}
- }, [isOpen]);
+ }, [isOpen, updateDraft]);
useEffect(() => {
if (!isOpen) return;
@@ -109,7 +130,15 @@ export function DashboardNewWorkspaceModalContent({
(project) => project.id === draft.selectedProjectId,
);
if (!hasSelectedProject) {
- updateDraft({ selectedProjectId: recentProjects[0]?.id ?? null });
+ const { lastProjectId } = useV2WorkspaceCreateDefaultsStore.getState();
+ const persistedProjectId =
+ lastProjectId &&
+ recentProjects.some((project) => project.id === lastProjectId)
+ ? lastProjectId
+ : null;
+ updateDraft({
+ selectedProjectId: persistedProjectId ?? recentProjects[0]?.id ?? null,
+ });
}
}, [
draft.selectedProjectId,
@@ -130,9 +159,10 @@ export function DashboardNewWorkspaceModalContent({
projectId={draft.selectedProjectId}
selectedProject={selectedProject}
recentProjects={recentProjects.filter((project) => Boolean(project.id))}
- onSelectProject={(selectedProjectId) =>
- updateDraft({ selectedProjectId })
- }
+ onSelectProject={(selectedProjectId) => {
+ setLastProjectId(selectedProjectId);
+ updateDraft({ selectedProjectId });
+ }}
/>
);
diff --git a/apps/desktop/src/renderer/stores/v2-workspace-create-defaults.ts b/apps/desktop/src/renderer/stores/v2-workspace-create-defaults.ts
new file mode 100644
index 00000000000..25f40660eb3
--- /dev/null
+++ b/apps/desktop/src/renderer/stores/v2-workspace-create-defaults.ts
@@ -0,0 +1,69 @@
+import { create } from "zustand";
+import { devtools, persist } from "zustand/middleware";
+
+export type V2WorkspaceCreateBaseBranchSource = "local" | "remote-tracking";
+
+export interface V2WorkspaceCreateBaseBranchDefault {
+ branchName: string;
+ source: V2WorkspaceCreateBaseBranchSource;
+}
+
+export type V2WorkspaceCreateHostTarget =
+ | { kind: "local" }
+ | { kind: "host"; hostId: string };
+
+interface V2WorkspaceCreateDefaultsState {
+ lastProjectId: string | null;
+ baseBranchesByProjectId: Record;
+ lastHostTarget: V2WorkspaceCreateHostTarget | null;
+
+ setLastProjectId: (projectId: string | null) => void;
+ setBaseBranchDefault: (
+ projectId: string,
+ branchName: string,
+ source: V2WorkspaceCreateBaseBranchSource,
+ ) => void;
+ clearBaseBranchDefault: (projectId: string) => void;
+ setLastHostTarget: (target: V2WorkspaceCreateHostTarget) => void;
+}
+
+export const useV2WorkspaceCreateDefaultsStore =
+ create()(
+ devtools(
+ persist(
+ (set) => ({
+ lastProjectId: null,
+ baseBranchesByProjectId: {},
+ lastHostTarget: null,
+
+ setLastProjectId: (projectId) => set({ lastProjectId: projectId }),
+
+ setBaseBranchDefault: (projectId, branchName, source) => {
+ const trimmed = branchName.trim();
+ if (!trimmed) return;
+ set((state) => ({
+ baseBranchesByProjectId: {
+ ...state.baseBranchesByProjectId,
+ [projectId]: { branchName: trimmed, source },
+ },
+ }));
+ },
+
+ clearBaseBranchDefault: (projectId) =>
+ set((state) => {
+ if (!(projectId in state.baseBranchesByProjectId)) return state;
+ const next = { ...state.baseBranchesByProjectId };
+ delete next[projectId];
+ return { baseBranchesByProjectId: next };
+ }),
+
+ setLastHostTarget: (target) => set({ lastHostTarget: target }),
+ }),
+ {
+ name: "v2-workspace-create-defaults",
+ version: 1,
+ },
+ ),
+ { name: "V2WorkspaceCreateDefaultsStore" },
+ ),
+ );