From 1a2778fa6e849f5ac2c3a5977d4010e6b398a0b9 Mon Sep 17 00:00:00 2001 From: Roo Code Date: Thu, 7 May 2026 19:54:20 +0000 Subject: [PATCH] fix: prevent save button from becoming inactive in Modes settings (#12283) - Pass checkUnsaveChanges from SettingsView to ModesView so API config changes in the Modes tab prompt for unsaved changes before applying - Remove auto-save of current config when switching to a mode without a saved config, preventing config bleed across modes - Update tests to match new behavior --- src/core/webview/ClineProvider.ts | 14 ++++--------- .../webview/__tests__/ClineProvider.spec.ts | 12 +++++------ webview-ui/src/components/modes/ModesView.tsx | 20 ++++++++++++++----- .../src/components/settings/SettingsView.tsx | 2 +- 4 files changed, 26 insertions(+), 22 deletions(-) diff --git a/src/core/webview/ClineProvider.ts b/src/core/webview/ClineProvider.ts index 1106d340050..cbb435e8aff 100644 --- a/src/core/webview/ClineProvider.ts +++ b/src/core/webview/ClineProvider.ts @@ -1463,16 +1463,10 @@ export class ClineProvider // The task will continue with the current/default configuration. } } else { - // If no saved config for this mode, save current config as default. - const currentApiConfigNameAfter = this.getGlobalState("currentApiConfigName") - - if (currentApiConfigNameAfter) { - const config = listApiConfig.find((c) => c.name === currentApiConfigNameAfter) - - if (config?.id) { - await this.providerSettingsManager.setModeConfig(newMode, config.id) - } - } + // No saved config for this mode — leave the current config active + // without persisting it as the mode's default. This prevents config + // "bleed" where switching modes silently inherits and saves the + // previous mode's API configuration. } await this.postStateToWebview() diff --git a/src/core/webview/__tests__/ClineProvider.spec.ts b/src/core/webview/__tests__/ClineProvider.spec.ts index da0fb2003fb..60178656c5c 100644 --- a/src/core/webview/__tests__/ClineProvider.spec.ts +++ b/src/core/webview/__tests__/ClineProvider.spec.ts @@ -890,7 +890,7 @@ describe("ClineProvider", () => { expect(mockContext.globalState.update).toHaveBeenCalledWith("currentApiConfigName", "test-config") }) - it("saves current config when switching to mode without config", async () => { + it("does not auto-save current config when switching to mode without config", async () => { await provider.resolveWebviewView(mockWebviewView) const messageHandler = (mockWebviewView.webview.onDidReceiveMessage as any).mock.calls[0][0] @@ -907,8 +907,8 @@ describe("ClineProvider", () => { // Switch to architect mode await messageHandler({ type: "mode", text: "architect" }) - // Should save current config as default for architect mode - expect(provider.providerSettingsManager.setModeConfig).toHaveBeenCalledWith("architect", "current-id") + // Should NOT auto-save current config as default for the new mode + expect(provider.providerSettingsManager.setModeConfig).not.toHaveBeenCalled() }) it("saves config as default for current mode when loading config", async () => { @@ -1483,7 +1483,7 @@ describe("ClineProvider", () => { expect(mockPostMessage).toHaveBeenCalledWith(expect.objectContaining({ type: "state" })) }) - test("saves current config when switching to mode without config", async () => { + test("does not auto-save current config when switching to mode without config", async () => { ;(provider as any).providerSettingsManager = { getModeConfigId: vi.fn().mockResolvedValue(undefined), listConfig: vi @@ -1506,8 +1506,8 @@ describe("ClineProvider", () => { // Verify mode was updated expect(mockContext.globalState.update).toHaveBeenCalledWith("mode", "architect") - // Verify current config was saved as default for new mode - expect(provider.providerSettingsManager.setModeConfig).toHaveBeenCalledWith("architect", "current-id") + // Should NOT auto-save current config as default for the new mode + expect(provider.providerSettingsManager.setModeConfig).not.toHaveBeenCalled() // Verify state was posted to webview expect(mockPostMessage).toHaveBeenCalledWith(expect.objectContaining({ type: "state" })) diff --git a/webview-ui/src/components/modes/ModesView.tsx b/webview-ui/src/components/modes/ModesView.tsx index eeeaf026cc2..7b9f623e125 100644 --- a/webview-ui/src/components/modes/ModesView.tsx +++ b/webview-ui/src/components/modes/ModesView.tsx @@ -63,7 +63,11 @@ function getGroupName(group: GroupEntry): ToolGroup { return Array.isArray(group) ? group[0] : group } -const ModesView = () => { +interface ModesViewProps { + checkUnsaveChanges?: (then: () => void) => void +} + +const ModesView = ({ checkUnsaveChanges }: ModesViewProps) => { const { t } = useAppTranslation() const { @@ -913,10 +917,16 @@ const ModesView = () => {