diff --git a/packages/ui/src/features/settings/settingsStore.test.ts b/packages/ui/src/features/settings/settingsStore.test.ts index a220df98cd..b645566426 100644 --- a/packages/ui/src/features/settings/settingsStore.test.ts +++ b/packages/ui/src/features/settings/settingsStore.test.ts @@ -75,6 +75,33 @@ describe("feature settingsStore cloud selections", () => { expect(useSettingsStore.getState().allowBypassPermissions).toBe(true); }); + + it.each([ + ["lastUsedWorkspaceMode", "local", "cloud"], + ["debugLogsCloudRuns", false, true], + ] as const)("rehydrates %s", async (field, initial, persisted) => { + getItem.mockResolvedValue( + JSON.stringify({ state: { [field]: persisted }, version: 0 }), + ); + + useSettingsStore.setState({ [field]: initial } as Parameters< + typeof useSettingsStore.setState + >[0]); + + await useSettingsStore.persist.rehydrate(); + + expect(useSettingsStore.getState()[field]).toBe(persisted); + }); + + it("flips _hasHydrated once the persisted snapshot lands", async () => { + getItem.mockResolvedValue(JSON.stringify({ state: {}, version: 0 })); + + useSettingsStore.setState({ _hasHydrated: false }); + + await useSettingsStore.persist.rehydrate(); + + expect(useSettingsStore.getState()._hasHydrated).toBe(true); + }); }); describe("feature settingsStore terminal font", () => { diff --git a/packages/ui/src/features/settings/settingsStore.ts b/packages/ui/src/features/settings/settingsStore.ts index ddf2c314a5..8baf4134cf 100644 --- a/packages/ui/src/features/settings/settingsStore.ts +++ b/packages/ui/src/features/settings/settingsStore.ts @@ -130,6 +130,9 @@ interface SettingsStore { shouldShowHint: (key: string, max?: number) => boolean; recordHintShown: (key: string) => void; markHintLearned: (key: string) => void; + + _hasHydrated: boolean; + setHasHydrated: (hydrated: boolean) => void; } // ---------- Store ---------- @@ -259,6 +262,9 @@ export const useSettingsStore = create()( }, }; }), + + _hasHydrated: false, + setHasHydrated: (hydrated) => set({ _hasHydrated: hydrated }), }), { name: "settings-storage", @@ -309,6 +315,9 @@ export const useSettingsStore = create()( // Onboarding hints hints: state.hints, }), + onRehydrateStorage: () => (state) => { + state?.setHasHydrated(true); + }, merge: (persisted, current) => { const merged = { ...current, diff --git a/packages/ui/src/features/task-detail/components/TaskInput.tsx b/packages/ui/src/features/task-detail/components/TaskInput.tsx index 8086c49656..b228986a11 100644 --- a/packages/ui/src/features/task-detail/components/TaskInput.tsx +++ b/packages/ui/src/features/task-detail/components/TaskInput.tsx @@ -117,6 +117,7 @@ export function TaskInput({ lastUsedInitialTaskMode, setLastUsedReasoningEffort, setLastUsedModel, + _hasHydrated: settingsHydrated, } = useSettingsStore(); const editorRef = useRef(null); @@ -211,7 +212,17 @@ export function TaskInput({ return lastUsedWorkspaceMode || "local"; }); + const didResolveWorkspaceModeRef = useRef(false); + useEffect(() => { + if (didResolveWorkspaceModeRef.current) return; + if (!settingsHydrated) return; + didResolveWorkspaceModeRef.current = true; + if (initialCloudRepository) return; + setWorkspaceModeState(lastUsedWorkspaceMode || "local"); + }, [settingsHydrated, lastUsedWorkspaceMode, initialCloudRepository]); + const setWorkspaceMode = (mode: WorkspaceMode) => { + didResolveWorkspaceModeRef.current = true; setWorkspaceModeState(mode); setLastUsedWorkspaceMode(mode); if (mode !== "cloud") {