diff --git a/packages/types/src/experiment.ts b/packages/types/src/experiment.ts index 10384db8ed..5424121d67 100644 --- a/packages/types/src/experiment.ts +++ b/packages/types/src/experiment.ts @@ -6,7 +6,7 @@ import type { Keys, Equals, AssertEqual } from "./type-fu.js" * ExperimentId */ -export const experimentIds = ["powerSteering", "multiFileApplyDiff"] as const +export const experimentIds = ["powerSteering", "multiFileApplyDiff", "preventFocusDisruption"] as const export const experimentIdsSchema = z.enum(experimentIds) @@ -19,6 +19,7 @@ export type ExperimentId = z.infer export const experimentsSchema = z.object({ powerSteering: z.boolean().optional(), multiFileApplyDiff: z.boolean().optional(), + preventFocusDisruption: z.boolean().optional(), }) export type Experiments = z.infer diff --git a/src/core/task/Task.ts b/src/core/task/Task.ts index ccd24b7d71..63e945b754 100644 --- a/src/core/task/Task.ts +++ b/src/core/task/Task.ts @@ -288,6 +288,9 @@ export class Task extends EventEmitter { if (isMultiFileApplyDiffEnabled) { this.diffStrategy = new MultiFileSearchReplaceDiffStrategy(this.fuzzyMatchThreshold) } + + // Update DiffViewProvider with experiments + this.diffViewProvider.setExperiments(state.experiments ?? {}) }) } diff --git a/src/integrations/editor/DiffViewProvider.ts b/src/integrations/editor/DiffViewProvider.ts index dbef4539bd..093839d5d8 100644 --- a/src/integrations/editor/DiffViewProvider.ts +++ b/src/integrations/editor/DiffViewProvider.ts @@ -14,6 +14,8 @@ import { ClineSayTool } from "../../shared/ExtensionMessage" import { Task } from "../../core/task/Task" import { DEFAULT_WRITE_DELAY_MS } from "@roo-code/types" import { Package } from "../../shared/package" +import { EXPERIMENT_IDS, experiments as Experiments } from "../../shared/experiments" +import type { Experiments as ExperimentsType } from "@roo-code/types" import { DecorationController } from "./DecorationController" @@ -37,8 +39,18 @@ export class DiffViewProvider { private activeLineController?: DecorationController private streamedLines: string[] = [] private preDiagnostics: [vscode.Uri, vscode.Diagnostic[]][] = [] + private experiments?: ExperimentsType - constructor(private cwd: string) {} + constructor( + private cwd: string, + experiments?: ExperimentsType, + ) { + this.experiments = experiments + } + + public setExperiments(experiments: ExperimentsType) { + this.experiments = experiments + } async open(relPath: string): Promise { this.relPath = relPath @@ -203,9 +215,9 @@ export class DiffViewProvider { } // Check if the experimental setting is enabled - const preventFocusDisruption = vscode.workspace - .getConfiguration(Package.name) - .get("experimentalPreventFocusDisruption", false) + const preventFocusDisruption = this.experiments + ? Experiments.isEnabled(this.experiments, EXPERIMENT_IDS.PREVENT_FOCUS_DISRUPTION) + : false await vscode.window.showTextDocument(vscode.Uri.file(absolutePath), { preview: false, @@ -401,9 +413,9 @@ export class DiffViewProvider { if (this.documentWasOpen) { // Check if the experimental setting is enabled - const preventFocusDisruption = vscode.workspace - .getConfiguration(Package.name) - .get("experimentalPreventFocusDisruption", false) + const preventFocusDisruption = this.experiments + ? Experiments.isEnabled(this.experiments, EXPERIMENT_IDS.PREVENT_FOCUS_DISRUPTION) + : false await vscode.window.showTextDocument(vscode.Uri.file(absolutePath), { preview: false, @@ -462,9 +474,9 @@ export class DiffViewProvider { const uri = vscode.Uri.file(path.resolve(this.cwd, this.relPath)) // Check if the experimental setting is enabled - const preventFocusDisruption = vscode.workspace - .getConfiguration(Package.name) - .get("experimentalPreventFocusDisruption", false) + const preventFocusDisruption = this.experiments + ? Experiments.isEnabled(this.experiments, EXPERIMENT_IDS.PREVENT_FOCUS_DISRUPTION) + : false // If this diff editor is already open (ie if a previous write file was // interrupted) then we should activate that instead of opening a new diff --git a/src/integrations/editor/__tests__/DiffViewProvider.spec.ts b/src/integrations/editor/__tests__/DiffViewProvider.spec.ts index 1e35c64b95..42e9dff0c7 100644 --- a/src/integrations/editor/__tests__/DiffViewProvider.spec.ts +++ b/src/integrations/editor/__tests__/DiffViewProvider.spec.ts @@ -2,6 +2,7 @@ import { DiffViewProvider, DIFF_VIEW_URI_SCHEME, DIFF_VIEW_LABEL_CHANGES } from import * as vscode from "vscode" import * as path from "path" import delay from "delay" +import { EXPERIMENT_IDS } from "../../../shared/experiments" // Mock delay vi.mock("delay", () => ({ @@ -425,10 +426,11 @@ describe("DiffViewProvider", () => { describe("experimentalPreventFocusDisruption setting", () => { it("should preserve focus when experimentalPreventFocusDisruption is enabled", async () => { - // Mock the configuration to return true for experimentalPreventFocusDisruption - vi.mocked(vscode.workspace.getConfiguration).mockReturnValue({ - get: vi.fn().mockReturnValue(true), - } as any) + // Create a new DiffViewProvider with experiments enabled + const experiments = { + [EXPERIMENT_IDS.PREVENT_FOCUS_DISRUPTION]: true, + } + diffViewProvider = new DiffViewProvider(mockCwd, experiments) // Setup mock editor const mockEditor = { @@ -482,10 +484,11 @@ describe("DiffViewProvider", () => { }) it("should not preserve focus when experimentalPreventFocusDisruption is disabled", async () => { - // Mock the configuration to return false for experimentalPreventFocusDisruption - vi.mocked(vscode.workspace.getConfiguration).mockReturnValue({ - get: vi.fn().mockReturnValue(false), - } as any) + // Create a new DiffViewProvider with experiments disabled + const experiments = { + [EXPERIMENT_IDS.PREVENT_FOCUS_DISRUPTION]: false, + } + diffViewProvider = new DiffViewProvider(mockCwd, experiments) // Setup mock editor const mockEditor = { @@ -539,10 +542,11 @@ describe("DiffViewProvider", () => { }) it("should preserve focus in saveChanges when experimentalPreventFocusDisruption is enabled", async () => { - // Mock the configuration to return true - vi.mocked(vscode.workspace.getConfiguration).mockReturnValue({ - get: vi.fn().mockReturnValue(true), - } as any) + // Create a new DiffViewProvider with experiments enabled + const experiments = { + [EXPERIMENT_IDS.PREVENT_FOCUS_DISRUPTION]: true, + } + diffViewProvider = new DiffViewProvider(mockCwd, experiments) // Setup for saveChanges ;(diffViewProvider as any).relPath = "test.ts" @@ -571,10 +575,11 @@ describe("DiffViewProvider", () => { }) it("should preserve focus in revertChanges when experimentalPreventFocusDisruption is enabled", async () => { - // Mock the configuration to return true - vi.mocked(vscode.workspace.getConfiguration).mockReturnValue({ - get: vi.fn().mockReturnValue(true), - } as any) + // Create a new DiffViewProvider with experiments enabled + const experiments = { + [EXPERIMENT_IDS.PREVENT_FOCUS_DISRUPTION]: true, + } + diffViewProvider = new DiffViewProvider(mockCwd, experiments) // Setup for revertChanges ;(diffViewProvider as any).relPath = "test.ts" diff --git a/src/package.json b/src/package.json index 5f9dc21f45..cfdec57bde 100644 --- a/src/package.json +++ b/src/package.json @@ -386,11 +386,6 @@ "type": "string", "default": "", "description": "%settings.autoImportSettingsPath.description%" - }, - "roo-cline.experimentalPreventFocusDisruption": { - "type": "boolean", - "default": false, - "description": "%settings.experimentalPreventFocusDisruption.description%" } } } diff --git a/src/package.nls.json b/src/package.nls.json index 825e3a91f0..1eb294ca44 100644 --- a/src/package.nls.json +++ b/src/package.nls.json @@ -36,6 +36,5 @@ "settings.vsCodeLmModelSelector.family.description": "The family of the language model (e.g. gpt-4)", "settings.customStoragePath.description": "Custom storage path. Leave empty to use the default location. Supports absolute paths (e.g. 'D:\\RooCodeStorage')", "settings.enableCodeActions.description": "Enable Roo Code quick fixes", - "settings.autoImportSettingsPath.description": "Path to a RooCode configuration file to automatically import on extension startup. Supports absolute paths and paths relative to the home directory (e.g. '~/Documents/roo-code-settings.json'). Leave empty to disable auto-import.", - "settings.experimentalPreventFocusDisruption.description": "(Experimental) Prevent file edits from stealing focus. When enabled, diff views and file edits will not disrupt your current work. Files will update in the background without forcing you to switch context." + "settings.autoImportSettingsPath.description": "Path to a RooCode configuration file to automatically import on extension startup. Supports absolute paths and paths relative to the home directory (e.g. '~/Documents/roo-code-settings.json'). Leave empty to disable auto-import." } diff --git a/src/shared/__tests__/experiments.spec.ts b/src/shared/__tests__/experiments.spec.ts index 4a8f06d62a..607c1e0b04 100644 --- a/src/shared/__tests__/experiments.spec.ts +++ b/src/shared/__tests__/experiments.spec.ts @@ -28,6 +28,7 @@ describe("experiments", () => { const experiments: Record = { powerSteering: false, multiFileApplyDiff: false, + preventFocusDisruption: false, } expect(Experiments.isEnabled(experiments, EXPERIMENT_IDS.POWER_STEERING)).toBe(false) }) @@ -36,6 +37,7 @@ describe("experiments", () => { const experiments: Record = { powerSteering: true, multiFileApplyDiff: false, + preventFocusDisruption: false, } expect(Experiments.isEnabled(experiments, EXPERIMENT_IDS.POWER_STEERING)).toBe(true) }) @@ -44,6 +46,7 @@ describe("experiments", () => { const experiments: Record = { powerSteering: false, multiFileApplyDiff: false, + preventFocusDisruption: false, } expect(Experiments.isEnabled(experiments, EXPERIMENT_IDS.POWER_STEERING)).toBe(false) }) diff --git a/src/shared/experiments.ts b/src/shared/experiments.ts index 1edadf654f..548b55f68c 100644 --- a/src/shared/experiments.ts +++ b/src/shared/experiments.ts @@ -3,6 +3,7 @@ import type { AssertEqual, Equals, Keys, Values, ExperimentId, Experiments } fro export const EXPERIMENT_IDS = { MULTI_FILE_APPLY_DIFF: "multiFileApplyDiff", POWER_STEERING: "powerSteering", + PREVENT_FOCUS_DISRUPTION: "preventFocusDisruption", } as const satisfies Record type _AssertExperimentIds = AssertEqual>> @@ -16,6 +17,7 @@ interface ExperimentConfig { export const experimentConfigsMap: Record = { MULTI_FILE_APPLY_DIFF: { enabled: false }, POWER_STEERING: { enabled: false }, + PREVENT_FOCUS_DISRUPTION: { enabled: false }, } export const experimentDefault = Object.fromEntries( diff --git a/webview-ui/src/context/__tests__/ExtensionStateContext.spec.tsx b/webview-ui/src/context/__tests__/ExtensionStateContext.spec.tsx index 1e5867d3fc..c29dfe4192 100644 --- a/webview-ui/src/context/__tests__/ExtensionStateContext.spec.tsx +++ b/webview-ui/src/context/__tests__/ExtensionStateContext.spec.tsx @@ -222,10 +222,8 @@ describe("mergeExtensionState", () => { apiConfiguration: { modelMaxThinkingTokens: 456, modelTemperature: 0.3 }, experiments: { powerSteering: true, - marketplace: false, - disableCompletionCommand: false, - concurrentFileReads: true, multiFileApplyDiff: true, + preventFocusDisruption: false, } as Record, } @@ -238,10 +236,8 @@ describe("mergeExtensionState", () => { expect(result.experiments).toEqual({ powerSteering: true, - marketplace: false, - disableCompletionCommand: false, - concurrentFileReads: true, multiFileApplyDiff: true, + preventFocusDisruption: false, }) }) }) diff --git a/webview-ui/src/i18n/locales/en/settings.json b/webview-ui/src/i18n/locales/en/settings.json index 4a826bddab..2d8e452d7b 100644 --- a/webview-ui/src/i18n/locales/en/settings.json +++ b/webview-ui/src/i18n/locales/en/settings.json @@ -618,6 +618,10 @@ "MULTI_FILE_APPLY_DIFF": { "name": "Enable concurrent file edits", "description": "When enabled, Roo can edit multiple files in a single request. When disabled, Roo must edit files one at a time. Disabling this can help when working with less capable models or when you want more control over file modifications." + }, + "PREVENT_FOCUS_DISRUPTION": { + "name": "Prevent focus disruption on file edits", + "description": "When enabled, file edits will not automatically switch focus to the edited file. This helps maintain your current workflow when Roo makes changes to multiple files." } }, "promptCaching": {