From c1833d77de4ab32895f7b0a40862e27d97506066 Mon Sep 17 00:00:00 2001 From: Roo Code Date: Mon, 21 Jul 2025 13:10:26 +0000 Subject: [PATCH 1/2] feat: add diffViewAutoFocus setting to control diff editor focus behavior - Added diffViewAutoFocus boolean setting to global settings schema - Added UI checkbox in AutoApproveSettings component - Updated ExtensionStateContext to manage the setting state - Modified DiffViewProvider to use the setting for preserveFocus parameter - Updated Task class to pass the setting to DiffViewProvider - Added comprehensive tests for the new functionality - Added i18n translations for the setting Fixes #6010 --- packages/types/src/global-settings.ts | 2 + src/core/task/Task.ts | 17 +- src/core/webview/webviewMessageHandler.ts | 5 + src/integrations/editor/DiffViewProvider.ts | 40 +++-- .../editor/__tests__/DiffViewProvider.spec.ts | 147 +++++++++++++++++- src/shared/WebviewMessage.ts | 1 + .../settings/AutoApproveSettings.tsx | 22 +++ .../settings/FileEditingSettings.tsx | 42 +++++ .../src/components/settings/SettingsView.tsx | 3 + .../src/context/ExtensionStateContext.tsx | 3 + webview-ui/src/i18n/locales/en/settings.json | 7 + 11 files changed, 276 insertions(+), 13 deletions(-) create mode 100644 webview-ui/src/components/settings/FileEditingSettings.tsx diff --git a/packages/types/src/global-settings.ts b/packages/types/src/global-settings.ts index a30550dce1..3dae25e83f 100644 --- a/packages/types/src/global-settings.ts +++ b/packages/types/src/global-settings.ts @@ -107,6 +107,7 @@ export const globalSettingsSchema = z.object({ rateLimitSeconds: z.number().optional(), diffEnabled: z.boolean().optional(), + diffViewAutoFocus: z.boolean().optional(), fuzzyMatchThreshold: z.number().optional(), experiments: experimentsSchema.optional(), @@ -250,6 +251,7 @@ export const EVALS_SETTINGS: RooCodeSettings = { diagnosticsEnabled: true, diffEnabled: true, + diffViewAutoFocus: false, fuzzyMatchThreshold: 1, enableCheckpoints: false, diff --git a/src/core/task/Task.ts b/src/core/task/Task.ts index 53b8ef5b87..78f63f3278 100644 --- a/src/core/task/Task.ts +++ b/src/core/task/Task.ts @@ -260,7 +260,22 @@ export class Task extends EventEmitter { this.consecutiveMistakeLimit = consecutiveMistakeLimit ?? DEFAULT_CONSECUTIVE_MISTAKE_LIMIT this.providerRef = new WeakRef(provider) this.globalStoragePath = provider.context.globalStorageUri.fsPath - this.diffViewProvider = new DiffViewProvider(this.cwd) + + // Get diffViewAutoFocus setting from provider state + provider + .getState() + .then((state) => { + const diffViewAutoFocus = (state as any)?.diffViewAutoFocus ?? false + this.diffViewProvider = new DiffViewProvider(this.cwd, diffViewAutoFocus) + }) + .catch(() => { + // Fallback if state retrieval fails + this.diffViewProvider = new DiffViewProvider(this.cwd, false) + }) + + // Create with default for immediate use + this.diffViewProvider = new DiffViewProvider(this.cwd, false) + this.enableCheckpoints = enableCheckpoints this.rootTask = rootTask diff --git a/src/core/webview/webviewMessageHandler.ts b/src/core/webview/webviewMessageHandler.ts index 780d40df89..0f32b3867a 100644 --- a/src/core/webview/webviewMessageHandler.ts +++ b/src/core/webview/webviewMessageHandler.ts @@ -932,6 +932,11 @@ export const webviewMessageHandler = async ( await updateGlobalState("diffEnabled", diffEnabled) await provider.postStateToWebview() break + case "diffViewAutoFocus": + const diffViewAutoFocus = message.bool ?? false + await updateGlobalState("diffViewAutoFocus", diffViewAutoFocus) + await provider.postStateToWebview() + break case "enableCheckpoints": const enableCheckpoints = message.bool ?? true await updateGlobalState("enableCheckpoints", enableCheckpoints) diff --git a/src/integrations/editor/DiffViewProvider.ts b/src/integrations/editor/DiffViewProvider.ts index f4133029c9..36f842c461 100644 --- a/src/integrations/editor/DiffViewProvider.ts +++ b/src/integrations/editor/DiffViewProvider.ts @@ -36,8 +36,14 @@ export class DiffViewProvider { private activeLineController?: DecorationController private streamedLines: string[] = [] private preDiagnostics: [vscode.Uri, vscode.Diagnostic[]][] = [] + private diffViewAutoFocus: boolean - constructor(private cwd: string) {} + constructor( + private cwd: string, + diffViewAutoFocus: boolean = false, + ) { + this.diffViewAutoFocus = diffViewAutoFocus + } async open(relPath: string): Promise { this.relPath = relPath @@ -181,7 +187,10 @@ export class DiffViewProvider { } } - async saveChanges(diagnosticsEnabled: boolean = true, writeDelayMs: number = DEFAULT_WRITE_DELAY_MS): Promise<{ + async saveChanges( + diagnosticsEnabled: boolean = true, + writeDelayMs: number = DEFAULT_WRITE_DELAY_MS, + ): Promise<{ newProblemsMessage: string | undefined userEdits: string | undefined finalContent: string | undefined @@ -198,7 +207,10 @@ export class DiffViewProvider { await updatedDocument.save() } - await vscode.window.showTextDocument(vscode.Uri.file(absolutePath), { preview: false, preserveFocus: true }) + await vscode.window.showTextDocument(vscode.Uri.file(absolutePath), { + preview: false, + preserveFocus: !this.diffViewAutoFocus, + }) await this.closeAllDiffViews() // Getting diagnostics before and after the file edit is a better approach than @@ -216,22 +228,22 @@ export class DiffViewProvider { // and can address them accordingly. If problems don't change immediately after // applying a fix, won't be notified, which is generally fine since the // initial fix is usually correct and it may just take time for linters to catch up. - + let newProblemsMessage = "" - + if (diagnosticsEnabled) { // Add configurable delay to allow linters time to process and clean up issues // like unused imports (especially important for Go and other languages) // Ensure delay is non-negative const safeDelayMs = Math.max(0, writeDelayMs) - + try { await delay(safeDelayMs) } catch (error) { // Log error but continue - delay failure shouldn't break the save operation console.warn(`Failed to apply write delay: ${error}`) } - + const postDiagnostics = vscode.languages.getDiagnostics() const newProblems = await diagnosticsToProblemsString( @@ -390,7 +402,7 @@ export class DiffViewProvider { if (this.documentWasOpen) { await vscode.window.showTextDocument(vscode.Uri.file(absolutePath), { preview: false, - preserveFocus: true, + preserveFocus: !this.diffViewAutoFocus, }) } @@ -457,7 +469,9 @@ export class DiffViewProvider { ) if (diffTab && diffTab.input instanceof vscode.TabInputTextDiff) { - const editor = await vscode.window.showTextDocument(diffTab.input.modified, { preserveFocus: true }) + const editor = await vscode.window.showTextDocument(diffTab.input.modified, { + preserveFocus: !this.diffViewAutoFocus, + }) return editor } @@ -523,7 +537,11 @@ export class DiffViewProvider { // Pre-open the file as a text document to ensure it doesn't open in preview mode // This fixes issues with files that have custom editor associations (like markdown preview) vscode.window - .showTextDocument(uri, { preview: false, viewColumn: vscode.ViewColumn.Active, preserveFocus: true }) + .showTextDocument(uri, { + preview: false, + viewColumn: vscode.ViewColumn.Active, + preserveFocus: !this.diffViewAutoFocus, + }) .then(() => { // Execute the diff command after ensuring the file is open as text return vscode.commands.executeCommand( @@ -533,7 +551,7 @@ export class DiffViewProvider { }), uri, `${fileName}: ${fileExists ? `${DIFF_VIEW_LABEL_CHANGES}` : "New File"} (Editable)`, - { preserveFocus: true }, + { preserveFocus: !this.diffViewAutoFocus }, ) }) .then( diff --git a/src/integrations/editor/__tests__/DiffViewProvider.spec.ts b/src/integrations/editor/__tests__/DiffViewProvider.spec.ts index a4aded95bb..7a653f351e 100644 --- a/src/integrations/editor/__tests__/DiffViewProvider.spec.ts +++ b/src/integrations/editor/__tests__/DiffViewProvider.spec.ts @@ -12,6 +12,7 @@ vi.mock("delay", () => ({ vi.mock("fs/promises", () => ({ readFile: vi.fn().mockResolvedValue("file content"), writeFile: vi.fn().mockResolvedValue(undefined), + unlink: vi.fn().mockResolvedValue(undefined), })) // Mock utils @@ -110,7 +111,7 @@ describe("DiffViewProvider", () => { } vi.mocked(vscode.WorkspaceEdit).mockImplementation(() => mockWorkspaceEdit as any) - diffViewProvider = new DiffViewProvider(mockCwd) + diffViewProvider = new DiffViewProvider(mockCwd, false) // Mock the necessary properties and methods ;(diffViewProvider as any).relPath = "test.txt" ;(diffViewProvider as any).activeDiffEditor = { @@ -236,6 +237,63 @@ describe("DiffViewProvider", () => { ) }) + it("should respect diffViewAutoFocus setting when opening diff", async () => { + // Create a new instance with diffViewAutoFocus enabled + const focusEnabledProvider = new DiffViewProvider(mockCwd, true) + + // Setup + const mockEditor = { + document: { + uri: { fsPath: `${mockCwd}/test.md` }, + getText: vi.fn().mockReturnValue(""), + lineCount: 0, + }, + selection: { + active: { line: 0, character: 0 }, + anchor: { line: 0, character: 0 }, + }, + edit: vi.fn().mockResolvedValue(true), + revealRange: vi.fn(), + } + + // Mock showTextDocument + vi.mocked(vscode.window.showTextDocument).mockResolvedValue(mockEditor as any) + + // Mock executeCommand + vi.mocked(vscode.commands.executeCommand).mockResolvedValue(undefined) + + // Mock workspace.onDidOpenTextDocument to trigger immediately + vi.mocked(vscode.workspace.onDidOpenTextDocument).mockImplementation((callback) => { + setTimeout(() => { + callback({ uri: { fsPath: `${mockCwd}/test.md` } } as any) + }, 0) + return { dispose: vi.fn() } + }) + + // Mock window.visibleTextEditors + vi.mocked(vscode.window).visibleTextEditors = [mockEditor as any] + + // Set up for file + ;(focusEnabledProvider as any).editType = "modify" + + // Execute open + await focusEnabledProvider.open("test.md") + + // Verify that preserveFocus is false when diffViewAutoFocus is true + expect(vscode.window.showTextDocument).toHaveBeenCalledWith( + expect.objectContaining({ fsPath: `${mockCwd}/test.md` }), + { preview: false, viewColumn: vscode.ViewColumn.Active, preserveFocus: false }, + ) + + expect(vscode.commands.executeCommand).toHaveBeenCalledWith( + "vscode.diff", + expect.any(Object), + expect.any(Object), + `test.md: ${DIFF_VIEW_LABEL_CHANGES} (Editable)`, + { preserveFocus: false }, + ) + }) + it("should handle showTextDocument failure", async () => { // Mock showTextDocument to fail vi.mocked(vscode.window.showTextDocument).mockRejectedValue(new Error("Cannot open file")) @@ -418,4 +476,91 @@ describe("DiffViewProvider", () => { expect(vscode.languages.getDiagnostics).toHaveBeenCalled() }) }) + + describe("diffViewAutoFocus behavior", () => { + it("should use preserveFocus=true when diffViewAutoFocus is false", async () => { + // Default provider has diffViewAutoFocus=false + const mockEditor = { + document: { + uri: { fsPath: `${mockCwd}/test.txt` }, + getText: vi.fn().mockReturnValue("content"), + save: vi.fn().mockResolvedValue(undefined), + isDirty: false, + }, + } + + vi.mocked(vscode.window.showTextDocument).mockResolvedValue(mockEditor as any) + ;(diffViewProvider as any).activeDiffEditor = mockEditor + ;(diffViewProvider as any).relPath = "test.txt" + ;(diffViewProvider as any).newContent = "new content" + ;(diffViewProvider as any).preDiagnostics = [] + ;(diffViewProvider as any).closeAllDiffViews = vi.fn().mockResolvedValue(undefined) + + await diffViewProvider.saveChanges(false) + + expect(vscode.window.showTextDocument).toHaveBeenCalledWith(expect.any(Object), { + preview: false, + preserveFocus: true, + }) + }) + + it("should use preserveFocus=false when diffViewAutoFocus is true", async () => { + // Create provider with diffViewAutoFocus=true + const focusProvider = new DiffViewProvider(mockCwd, true) + const mockEditor = { + document: { + uri: { fsPath: `${mockCwd}/test.txt` }, + getText: vi.fn().mockReturnValue("content"), + save: vi.fn().mockResolvedValue(undefined), + isDirty: false, + }, + } + + vi.mocked(vscode.window.showTextDocument).mockResolvedValue(mockEditor as any) + ;(focusProvider as any).activeDiffEditor = mockEditor + ;(focusProvider as any).relPath = "test.txt" + ;(focusProvider as any).newContent = "new content" + ;(focusProvider as any).preDiagnostics = [] + ;(focusProvider as any).closeAllDiffViews = vi.fn().mockResolvedValue(undefined) + + await focusProvider.saveChanges(false) + + expect(vscode.window.showTextDocument).toHaveBeenCalledWith(expect.any(Object), { + preview: false, + preserveFocus: false, + }) + }) + + it("should use preserveFocus=false for revertChanges when diffViewAutoFocus is true", async () => { + // Create provider with diffViewAutoFocus=true + const focusProvider = new DiffViewProvider(mockCwd, true) + const mockEditor = { + document: { + uri: { fsPath: `${mockCwd}/test.txt` }, + getText: vi.fn().mockReturnValue("content"), + save: vi.fn().mockResolvedValue(undefined), + isDirty: false, + positionAt: vi.fn().mockReturnValue({ line: 0, character: 0 }), + }, + edit: vi.fn().mockResolvedValue(true), + } + + vi.mocked(vscode.window.showTextDocument).mockResolvedValue(mockEditor as any) + vi.mocked(vscode.workspace.applyEdit).mockResolvedValue(true) + ;(focusProvider as any).activeDiffEditor = mockEditor + ;(focusProvider as any).relPath = "test.txt" + ;(focusProvider as any).originalContent = "original content" + ;(focusProvider as any).preDiagnostics = [] + ;(focusProvider as any).editType = "modify" // Set to modify so it goes through the revert path + ;(focusProvider as any).documentWasOpen = true // This needs to be true for showTextDocument to be called + ;(focusProvider as any).closeAllDiffViews = vi.fn().mockResolvedValue(undefined) + + await focusProvider.revertChanges() + + expect(vscode.window.showTextDocument).toHaveBeenCalledWith(expect.any(Object), { + preview: false, + preserveFocus: false, + }) + }) + }) }) diff --git a/src/shared/WebviewMessage.ts b/src/shared/WebviewMessage.ts index 1f56829f7b..d334bf6b46 100644 --- a/src/shared/WebviewMessage.ts +++ b/src/shared/WebviewMessage.ts @@ -93,6 +93,7 @@ export interface WebviewMessage { | "ttsSpeed" | "soundVolume" | "diffEnabled" + | "diffViewAutoFocus" | "enableCheckpoints" | "browserViewportSize" | "screenshotQuality" diff --git a/webview-ui/src/components/settings/AutoApproveSettings.tsx b/webview-ui/src/components/settings/AutoApproveSettings.tsx index e1d3c52cb9..d6df303582 100644 --- a/webview-ui/src/components/settings/AutoApproveSettings.tsx +++ b/webview-ui/src/components/settings/AutoApproveSettings.tsx @@ -33,6 +33,7 @@ type AutoApproveSettingsProps = HTMLAttributes & { followupAutoApproveTimeoutMs?: number allowedCommands?: string[] deniedCommands?: string[] + diffViewAutoFocus?: boolean setCachedStateField: SetCachedStateField< | "alwaysAllowReadOnly" | "alwaysAllowReadOnlyOutsideWorkspace" @@ -52,6 +53,7 @@ type AutoApproveSettingsProps = HTMLAttributes & { | "allowedCommands" | "deniedCommands" | "alwaysAllowUpdateTodoList" + | "diffViewAutoFocus" > } @@ -74,6 +76,7 @@ export const AutoApproveSettings = ({ alwaysAllowUpdateTodoList, allowedCommands, deniedCommands, + diffViewAutoFocus, setCachedStateField, ...props }: AutoApproveSettingsProps) => { @@ -393,6 +396,25 @@ export const AutoApproveSettings = ({ )} + + {/* Diff View Settings */} +
+
+ +
{t("settings:autoApprove.diffView.label")}
+
+
+ setCachedStateField("diffViewAutoFocus", e.target.checked)} + data-testid="diff-view-auto-focus-checkbox"> + {t("settings:autoApprove.diffView.autoFocus.label")} + +
+ {t("settings:autoApprove.diffView.autoFocus.description")} +
+
+
) diff --git a/webview-ui/src/components/settings/FileEditingSettings.tsx b/webview-ui/src/components/settings/FileEditingSettings.tsx new file mode 100644 index 0000000000..c89c8918f5 --- /dev/null +++ b/webview-ui/src/components/settings/FileEditingSettings.tsx @@ -0,0 +1,42 @@ +import { HTMLAttributes } from "react" +import { useAppTranslation } from "@/i18n/TranslationContext" +import { VSCodeCheckbox } from "@vscode/webview-ui-toolkit/react" +import { FileEdit } from "lucide-react" + +import { SetCachedStateField } from "./types" +import { SectionHeader } from "./SectionHeader" +import { Section } from "./Section" + +type FileEditingSettingsProps = HTMLAttributes & { + diffViewAutoFocus?: boolean + setCachedStateField: SetCachedStateField<"diffViewAutoFocus"> +} + +export const FileEditingSettings = ({ diffViewAutoFocus, setCachedStateField, ...props }: FileEditingSettingsProps) => { + const { t } = useAppTranslation() + return ( +
+ +
+ +
{t("settings:sections.fileEditing")}
+
+
+ +
+
+ { + setCachedStateField("diffViewAutoFocus", e.target.checked) + }}> + {t("settings:fileEditing.diffViewAutoFocus.label")} + +
+ {t("settings:fileEditing.diffViewAutoFocus.description")} +
+
+
+
+ ) +} diff --git a/webview-ui/src/components/settings/SettingsView.tsx b/webview-ui/src/components/settings/SettingsView.tsx index 517c1c159d..99df83c8c3 100644 --- a/webview-ui/src/components/settings/SettingsView.tsx +++ b/webview-ui/src/components/settings/SettingsView.tsx @@ -141,6 +141,7 @@ const SettingsView = forwardRef(({ onDone, t browserViewportSize, enableCheckpoints, diffEnabled, + diffViewAutoFocus, experiments, fuzzyMatchThreshold, maxOpenTabsContext, @@ -294,6 +295,7 @@ const SettingsView = forwardRef(({ onDone, t vscode.postMessage({ type: "ttsSpeed", value: ttsSpeed }) vscode.postMessage({ type: "soundVolume", value: soundVolume }) vscode.postMessage({ type: "diffEnabled", bool: diffEnabled }) + vscode.postMessage({ type: "diffViewAutoFocus", bool: diffViewAutoFocus }) vscode.postMessage({ type: "enableCheckpoints", bool: enableCheckpoints }) vscode.postMessage({ type: "browserViewportSize", text: browserViewportSize }) vscode.postMessage({ type: "remoteBrowserHost", text: remoteBrowserHost }) @@ -619,6 +621,7 @@ const SettingsView = forwardRef(({ onDone, t followupAutoApproveTimeoutMs={followupAutoApproveTimeoutMs} allowedCommands={allowedCommands} deniedCommands={deniedCommands} + diffViewAutoFocus={diffViewAutoFocus} setCachedStateField={setCachedStateField} /> )} diff --git a/webview-ui/src/context/ExtensionStateContext.tsx b/webview-ui/src/context/ExtensionStateContext.tsx index c970733fba..1e2604f013 100644 --- a/webview-ui/src/context/ExtensionStateContext.tsx +++ b/webview-ui/src/context/ExtensionStateContext.tsx @@ -25,6 +25,7 @@ import { convertTextMateToHljs } from "@src/utils/textMateToHljs" export interface ExtensionStateContextType extends ExtensionState { historyPreviewCollapsed?: boolean // Add the new state property + diffViewAutoFocus?: boolean didHydrateState: boolean showWelcome: boolean theme: any @@ -80,6 +81,7 @@ export interface ExtensionStateContextType extends ExtensionState { setTtsEnabled: (value: boolean) => void setTtsSpeed: (value: number) => void setDiffEnabled: (value: boolean) => void + setDiffViewAutoFocus: (value: boolean) => void setEnableCheckpoints: (value: boolean) => void setBrowserViewportSize: (value: string) => void setFuzzyMatchThreshold: (value: number) => void @@ -405,6 +407,7 @@ export const ExtensionStateContextProvider: React.FC<{ children: React.ReactNode setTtsEnabled: (value) => setState((prevState) => ({ ...prevState, ttsEnabled: value })), setTtsSpeed: (value) => setState((prevState) => ({ ...prevState, ttsSpeed: value })), setDiffEnabled: (value) => setState((prevState) => ({ ...prevState, diffEnabled: value })), + setDiffViewAutoFocus: (value) => setState((prevState) => ({ ...prevState, diffViewAutoFocus: value })), setEnableCheckpoints: (value) => setState((prevState) => ({ ...prevState, enableCheckpoints: value })), setBrowserViewportSize: (value: string) => setState((prevState) => ({ ...prevState, browserViewportSize: value })), diff --git a/webview-ui/src/i18n/locales/en/settings.json b/webview-ui/src/i18n/locales/en/settings.json index 7e3c2e3fcc..2ea6bce413 100644 --- a/webview-ui/src/i18n/locales/en/settings.json +++ b/webview-ui/src/i18n/locales/en/settings.json @@ -190,6 +190,13 @@ "label": "Todo", "description": "Automatically update the to-do list without requiring approval" }, + "diffView": { + "label": "Diff View", + "autoFocus": { + "label": "Auto-focus diff editors", + "description": "When enabled, diff editors opened by Roo will automatically gain focus. When disabled, diff editors will open in the background without taking focus from your current editor." + } + }, "apiRequestLimit": { "title": "Max Requests", "description": "Automatically make this many API requests before asking for approval to continue with the task.", From 816c6585b851af675bdd259b9fd201729a6a84bf Mon Sep 17 00:00:00 2001 From: Roo Code Date: Mon, 21 Jul 2025 13:35:53 +0000 Subject: [PATCH 2/2] fix: resolve CI failures for diffViewAutoFocus setting - Add missing translations for diffViewAutoFocus setting to all 17 locale files - Remove unused FileEditingSettings.tsx file to fix knip issues - Fix unit test failures by updating vscode mock with missing exports (Uri, RelativePattern, onDidChangeWorkspaceFolders) - Ensure mockProvider.getState returns valid response in Task.spec.ts --- src/core/task/__tests__/Task.spec.ts | 9 ++++ .../settings/FileEditingSettings.tsx | 42 ------------------- webview-ui/src/i18n/locales/ca/settings.json | 9 +++- webview-ui/src/i18n/locales/de/settings.json | 9 +++- webview-ui/src/i18n/locales/es/settings.json | 9 +++- webview-ui/src/i18n/locales/fr/settings.json | 7 ++++ webview-ui/src/i18n/locales/hi/settings.json | 9 +++- webview-ui/src/i18n/locales/id/settings.json | 9 +++- webview-ui/src/i18n/locales/it/settings.json | 9 +++- webview-ui/src/i18n/locales/ja/settings.json | 9 +++- webview-ui/src/i18n/locales/ko/settings.json | 9 +++- webview-ui/src/i18n/locales/nl/settings.json | 9 +++- webview-ui/src/i18n/locales/pl/settings.json | 9 +++- .../src/i18n/locales/pt-BR/settings.json | 9 +++- webview-ui/src/i18n/locales/ru/settings.json | 9 +++- webview-ui/src/i18n/locales/tr/settings.json | 9 +++- webview-ui/src/i18n/locales/vi/settings.json | 9 +++- .../src/i18n/locales/zh-CN/settings.json | 9 +++- .../src/i18n/locales/zh-TW/settings.json | 9 +++- 19 files changed, 144 insertions(+), 58 deletions(-) delete mode 100644 webview-ui/src/components/settings/FileEditingSettings.tsx diff --git a/src/core/task/__tests__/Task.spec.ts b/src/core/task/__tests__/Task.spec.ts index 9aa5a8d7a8..8dde6d8d82 100644 --- a/src/core/task/__tests__/Task.spec.ts +++ b/src/core/task/__tests__/Task.spec.ts @@ -116,6 +116,7 @@ vi.mock("vscode", () => { stat: vi.fn().mockResolvedValue({ type: 1 }), // FileType.File = 1 }, onDidSaveTextDocument: vi.fn(() => mockDisposable), + onDidChangeWorkspaceFolders: vi.fn(() => mockDisposable), getConfiguration: vi.fn(() => ({ get: (key: string, defaultValue: any) => defaultValue })), }, env: { @@ -127,6 +128,11 @@ vi.mock("vscode", () => { from: vi.fn(), }, TabInputText: vi.fn(), + Uri: { + file: vi.fn((path: string) => ({ fsPath: path, scheme: "file", path })), + parse: vi.fn((str: string) => ({ fsPath: str, scheme: "file", path: str })), + }, + RelativePattern: vi.fn().mockImplementation((base, pattern) => ({ base, pattern })), } }) @@ -1386,6 +1392,9 @@ describe("Cline", () => { }) it("should not create diff strategy when enableDiff is false", async () => { + // Ensure getState returns a valid response + mockProvider.getState.mockResolvedValue({}) + const task = new Task({ provider: mockProvider, apiConfiguration: mockApiConfig, diff --git a/webview-ui/src/components/settings/FileEditingSettings.tsx b/webview-ui/src/components/settings/FileEditingSettings.tsx deleted file mode 100644 index c89c8918f5..0000000000 --- a/webview-ui/src/components/settings/FileEditingSettings.tsx +++ /dev/null @@ -1,42 +0,0 @@ -import { HTMLAttributes } from "react" -import { useAppTranslation } from "@/i18n/TranslationContext" -import { VSCodeCheckbox } from "@vscode/webview-ui-toolkit/react" -import { FileEdit } from "lucide-react" - -import { SetCachedStateField } from "./types" -import { SectionHeader } from "./SectionHeader" -import { Section } from "./Section" - -type FileEditingSettingsProps = HTMLAttributes & { - diffViewAutoFocus?: boolean - setCachedStateField: SetCachedStateField<"diffViewAutoFocus"> -} - -export const FileEditingSettings = ({ diffViewAutoFocus, setCachedStateField, ...props }: FileEditingSettingsProps) => { - const { t } = useAppTranslation() - return ( -
- -
- -
{t("settings:sections.fileEditing")}
-
-
- -
-
- { - setCachedStateField("diffViewAutoFocus", e.target.checked) - }}> - {t("settings:fileEditing.diffViewAutoFocus.label")} - -
- {t("settings:fileEditing.diffViewAutoFocus.description")} -
-
-
-
- ) -} diff --git a/webview-ui/src/i18n/locales/ca/settings.json b/webview-ui/src/i18n/locales/ca/settings.json index eaa83b1b0d..9aa4f6de7b 100644 --- a/webview-ui/src/i18n/locales/ca/settings.json +++ b/webview-ui/src/i18n/locales/ca/settings.json @@ -197,7 +197,14 @@ "description": "Fes aquesta quantitat de sol·licituds API automàticament abans de demanar aprovació per continuar amb la tasca.", "unlimited": "Il·limitat" }, - "selectOptionsFirst": "Seleccioneu almenys una opció a continuació per activar l'aprovació automàtica" + "selectOptionsFirst": "Seleccioneu almenys una opció a continuació per activar l'aprovació automàtica", + "diffView": { + "label": "Vista de diferències", + "autoFocus": { + "label": "Enfocar automàticament els editors de diferències", + "description": "Quan està activat, els editors de diferències oberts per Roo guanyaran automàticament el focus. Quan està desactivat, els editors de diferències s'obriran en segon pla sense prendre el focus del vostre editor actual." + } + } }, "providers": { "providerDocumentation": "Documentació de {{provider}}", diff --git a/webview-ui/src/i18n/locales/de/settings.json b/webview-ui/src/i18n/locales/de/settings.json index 7f7c2c22b7..a6ff07e9e3 100644 --- a/webview-ui/src/i18n/locales/de/settings.json +++ b/webview-ui/src/i18n/locales/de/settings.json @@ -197,7 +197,14 @@ "description": "Automatisch so viele API-Anfragen stellen, bevor du um die Erlaubnis gebeten wirst, mit der Aufgabe fortzufahren.", "unlimited": "Unbegrenzt" }, - "selectOptionsFirst": "Wähle mindestens eine Option unten aus, um die automatische Genehmigung zu aktivieren" + "selectOptionsFirst": "Wähle mindestens eine Option unten aus, um die automatische Genehmigung zu aktivieren", + "diffView": { + "label": "Diff-Ansicht", + "autoFocus": { + "label": "Diff-Editoren automatisch fokussieren", + "description": "Wenn aktiviert, erhalten von Roo geöffnete Diff-Editoren automatisch den Fokus. Wenn deaktiviert, werden Diff-Editoren im Hintergrund geöffnet, ohne den Fokus von Ihrem aktuellen Editor zu nehmen." + } + } }, "providers": { "providerDocumentation": "{{provider}}-Dokumentation", diff --git a/webview-ui/src/i18n/locales/es/settings.json b/webview-ui/src/i18n/locales/es/settings.json index f00c2c9b42..73483e1d5f 100644 --- a/webview-ui/src/i18n/locales/es/settings.json +++ b/webview-ui/src/i18n/locales/es/settings.json @@ -197,7 +197,14 @@ "description": "Realizar automáticamente esta cantidad de solicitudes a la API antes de pedir aprobación para continuar con la tarea.", "unlimited": "Ilimitado" }, - "selectOptionsFirst": "Selecciona al menos una opción a continuación para habilitar la aprobación automática" + "selectOptionsFirst": "Selecciona al menos una opción a continuación para habilitar la aprobación automática", + "diffView": { + "label": "Vista de diferencias", + "autoFocus": { + "label": "Enfocar automáticamente los editores de diferencias", + "description": "Cuando está habilitado, los editores de diferencias abiertos por Roo obtendrán automáticamente el foco. Cuando está deshabilitado, los editores de diferencias se abrirán en segundo plano sin tomar el foco de su editor actual." + } + } }, "providers": { "providerDocumentation": "Documentación de {{provider}}", diff --git a/webview-ui/src/i18n/locales/fr/settings.json b/webview-ui/src/i18n/locales/fr/settings.json index 02dfac3552..4869bbfe15 100644 --- a/webview-ui/src/i18n/locales/fr/settings.json +++ b/webview-ui/src/i18n/locales/fr/settings.json @@ -197,6 +197,13 @@ "title": "Requêtes maximales", "description": "Effectuer automatiquement ce nombre de requêtes API avant de demander l'approbation pour continuer la tâche.", "unlimited": "Illimité" + }, + "diffView": { + "label": "Vue des différences", + "autoFocus": { + "label": "Focus automatique sur les éditeurs de différences", + "description": "Lorsqu'activé, les éditeurs de différences ouverts par Roo obtiendront automatiquement le focus. Lorsque désactivé, les éditeurs de différences s'ouvriront en arrière-plan sans prendre le focus de votre éditeur actuel." + } } }, "providers": { diff --git a/webview-ui/src/i18n/locales/hi/settings.json b/webview-ui/src/i18n/locales/hi/settings.json index 2b7fd03d56..d952b510d4 100644 --- a/webview-ui/src/i18n/locales/hi/settings.json +++ b/webview-ui/src/i18n/locales/hi/settings.json @@ -197,7 +197,14 @@ "description": "कार्य जारी रखने के लिए अनुमति मांगने से पहले स्वचालित रूप से इतने API अनुरोध करें।", "unlimited": "असीमित" }, - "selectOptionsFirst": "स्वतः-अनुमोदन सक्षम करने के लिए नीचे से कम से कम एक विकल्प चुनें" + "selectOptionsFirst": "स्वतः-अनुमोदन सक्षम करने के लिए नीचे से कम से कम एक विकल्प चुनें", + "diffView": { + "label": "अंतर दृश्य", + "autoFocus": { + "label": "अंतर संपादकों को स्वचालित रूप से फोकस करें", + "description": "जब सक्षम किया जाता है, तो Roo द्वारा खोले गए अंतर संपादक स्वचालित रूप से फोकस प्राप्त करेंगे। जब अक्षम किया जाता है, तो अंतर संपादक आपके वर्तमान संपादक से फोकस लिए बिना पृष्ठभूमि में खुलेंगे।" + } + } }, "providers": { "providerDocumentation": "{{provider}} दस्तावेज़ीकरण", diff --git a/webview-ui/src/i18n/locales/id/settings.json b/webview-ui/src/i18n/locales/id/settings.json index c6ba2728a7..85d56b362f 100644 --- a/webview-ui/src/i18n/locales/id/settings.json +++ b/webview-ui/src/i18n/locales/id/settings.json @@ -201,7 +201,14 @@ "description": "Secara otomatis membuat sejumlah permintaan API ini sebelum meminta persetujuan untuk melanjutkan tugas.", "unlimited": "Tidak terbatas" }, - "selectOptionsFirst": "Pilih setidaknya satu opsi di bawah ini untuk mengaktifkan persetujuan otomatis" + "selectOptionsFirst": "Pilih setidaknya satu opsi di bawah ini untuk mengaktifkan persetujuan otomatis", + "diffView": { + "label": "Tampilan Diff", + "autoFocus": { + "label": "Fokus otomatis editor diff", + "description": "Ketika diaktifkan, editor diff yang dibuka oleh Roo akan secara otomatis mendapatkan fokus. Ketika dinonaktifkan, editor diff akan terbuka di latar belakang tanpa mengambil fokus dari editor Anda saat ini." + } + } }, "providers": { "providerDocumentation": "Dokumentasi {{provider}}", diff --git a/webview-ui/src/i18n/locales/it/settings.json b/webview-ui/src/i18n/locales/it/settings.json index 26c776d60d..754a395691 100644 --- a/webview-ui/src/i18n/locales/it/settings.json +++ b/webview-ui/src/i18n/locales/it/settings.json @@ -197,7 +197,14 @@ "description": "Esegui automaticamente questo numero di richieste API prima di chiedere l'approvazione per continuare con l'attività.", "unlimited": "Illimitato" }, - "selectOptionsFirst": "Seleziona almeno un'opzione qui sotto per abilitare l'approvazione automatica" + "selectOptionsFirst": "Seleziona almeno un'opzione qui sotto per abilitare l'approvazione automatica", + "diffView": { + "label": "Vista differenze", + "autoFocus": { + "label": "Focus automatico sugli editor delle differenze", + "description": "Quando abilitato, gli editor delle differenze aperti da Roo otterranno automaticamente il focus. Quando disabilitato, gli editor delle differenze si apriranno in background senza prendere il focus dal tuo editor corrente." + } + } }, "providers": { "providerDocumentation": "Documentazione {{provider}}", diff --git a/webview-ui/src/i18n/locales/ja/settings.json b/webview-ui/src/i18n/locales/ja/settings.json index 9eb4328c12..cc7a001604 100644 --- a/webview-ui/src/i18n/locales/ja/settings.json +++ b/webview-ui/src/i18n/locales/ja/settings.json @@ -197,7 +197,14 @@ "description": "タスクを続行するための承認を求める前に、自動的にこの数のAPIリクエストを行います。", "unlimited": "無制限" }, - "selectOptionsFirst": "自動承認を有効にするには、以下のオプションを少なくとも1つ選択してください" + "selectOptionsFirst": "自動承認を有効にするには、以下のオプションを少なくとも1つ選択してください", + "diffView": { + "label": "差分ビュー", + "autoFocus": { + "label": "差分エディタを自動的にフォーカス", + "description": "有効にすると、Rooによって開かれた差分エディタは自動的にフォーカスを取得します。無効にすると、差分エディタは現在のエディタからフォーカスを奪うことなくバックグラウンドで開きます。" + } + } }, "providers": { "providerDocumentation": "{{provider}}のドキュメント", diff --git a/webview-ui/src/i18n/locales/ko/settings.json b/webview-ui/src/i18n/locales/ko/settings.json index 18b054bbb8..87d19daa02 100644 --- a/webview-ui/src/i18n/locales/ko/settings.json +++ b/webview-ui/src/i18n/locales/ko/settings.json @@ -197,7 +197,14 @@ "description": "작업을 계속하기 위한 승인을 요청하기 전에 자동으로 이 수의 API 요청을 수행합니다.", "unlimited": "무제한" }, - "selectOptionsFirst": "자동 승인을 활성화하려면 아래에서 하나 이상의 옵션을 선택하세요" + "selectOptionsFirst": "자동 승인을 활성화하려면 아래에서 하나 이상의 옵션을 선택하세요", + "diffView": { + "label": "차이점 보기", + "autoFocus": { + "label": "차이점 편집기 자동 포커스", + "description": "활성화되면 Roo가 연 차이점 편집기가 자동으로 포커스를 받습니다. 비활성화되면 차이점 편집기가 현재 편집기에서 포커스를 가져가지 않고 백그라운드에서 열립니다." + } + } }, "providers": { "providerDocumentation": "{{provider}} 문서", diff --git a/webview-ui/src/i18n/locales/nl/settings.json b/webview-ui/src/i18n/locales/nl/settings.json index 73ecf87d34..bb2c6ca76d 100644 --- a/webview-ui/src/i18n/locales/nl/settings.json +++ b/webview-ui/src/i18n/locales/nl/settings.json @@ -197,7 +197,14 @@ "description": "Voer automatisch dit aantal API-verzoeken uit voordat om goedkeuring wordt gevraagd om door te gaan met de taak.", "unlimited": "Onbeperkt" }, - "selectOptionsFirst": "Selecteer ten minste één optie hieronder om automatische goedkeuring in te schakelen" + "selectOptionsFirst": "Selecteer ten minste één optie hieronder om automatische goedkeuring in te schakelen", + "diffView": { + "label": "Diff-weergave", + "autoFocus": { + "label": "Diff-editors automatisch focussen", + "description": "Wanneer ingeschakeld, krijgen diff-editors geopend door Roo automatisch focus. Wanneer uitgeschakeld, worden diff-editors op de achtergrond geopend zonder focus van uw huidige editor te nemen." + } + } }, "providers": { "providerDocumentation": "{{provider}} documentatie", diff --git a/webview-ui/src/i18n/locales/pl/settings.json b/webview-ui/src/i18n/locales/pl/settings.json index b0fb8efac3..200c10c6ec 100644 --- a/webview-ui/src/i18n/locales/pl/settings.json +++ b/webview-ui/src/i18n/locales/pl/settings.json @@ -197,7 +197,14 @@ "description": "Automatycznie wykonaj tyle żądań API przed poproszeniem o zgodę na kontynuowanie zadania.", "unlimited": "Bez limitu" }, - "selectOptionsFirst": "Wybierz co najmniej jedną opcję poniżej, aby włączyć automatyczne zatwierdzanie" + "selectOptionsFirst": "Wybierz co najmniej jedną opcję poniżej, aby włączyć automatyczne zatwierdzanie", + "diffView": { + "label": "Widok różnic", + "autoFocus": { + "label": "Automatyczne skupienie edytorów różnic", + "description": "Gdy włączone, edytory różnic otwarte przez Roo automatycznie uzyskają fokus. Gdy wyłączone, edytory różnic otworzą się w tle bez zabierania fokusu z bieżącego edytora." + } + } }, "providers": { "providerDocumentation": "Dokumentacja {{provider}}", diff --git a/webview-ui/src/i18n/locales/pt-BR/settings.json b/webview-ui/src/i18n/locales/pt-BR/settings.json index 4167ade974..be10b1cc67 100644 --- a/webview-ui/src/i18n/locales/pt-BR/settings.json +++ b/webview-ui/src/i18n/locales/pt-BR/settings.json @@ -197,7 +197,14 @@ "description": "Fazer automaticamente este número de requisições à API antes de pedir aprovação para continuar com a tarefa.", "unlimited": "Ilimitado" }, - "selectOptionsFirst": "Selecione pelo menos uma opção abaixo para habilitar a aprovação automática" + "selectOptionsFirst": "Selecione pelo menos uma opção abaixo para habilitar a aprovação automática", + "diffView": { + "label": "Visualização de diferenças", + "autoFocus": { + "label": "Focar automaticamente editores de diferenças", + "description": "Quando ativado, os editores de diferenças abertos pelo Roo ganharão foco automaticamente. Quando desativado, os editores de diferenças serão abertos em segundo plano sem tirar o foco do seu editor atual." + } + } }, "providers": { "providerDocumentation": "Documentação do {{provider}}", diff --git a/webview-ui/src/i18n/locales/ru/settings.json b/webview-ui/src/i18n/locales/ru/settings.json index c07bc1d98a..d4fca89ca8 100644 --- a/webview-ui/src/i18n/locales/ru/settings.json +++ b/webview-ui/src/i18n/locales/ru/settings.json @@ -197,7 +197,14 @@ "description": "Автоматически выполнять это количество API-запросов перед запросом разрешения на продолжение задачи.", "unlimited": "Без ограничений" }, - "selectOptionsFirst": "Выберите хотя бы один вариант ниже, чтобы включить автоодобрение" + "selectOptionsFirst": "Выберите хотя бы один вариант ниже, чтобы включить автоодобрение", + "diffView": { + "label": "Просмотр различий", + "autoFocus": { + "label": "Автофокус на редакторах различий", + "description": "Когда включено, редакторы различий, открытые Roo, автоматически получат фокус. Когда отключено, редакторы различий будут открываться в фоновом режиме, не забирая фокус у вашего текущего редактора." + } + } }, "providers": { "providerDocumentation": "Документация {{provider}}", diff --git a/webview-ui/src/i18n/locales/tr/settings.json b/webview-ui/src/i18n/locales/tr/settings.json index ae6fad364d..ab81ac5d58 100644 --- a/webview-ui/src/i18n/locales/tr/settings.json +++ b/webview-ui/src/i18n/locales/tr/settings.json @@ -197,7 +197,14 @@ "description": "Göreve devam etmek için onay istemeden önce bu sayıda API isteği otomatik olarak yap.", "unlimited": "Sınırsız" }, - "selectOptionsFirst": "Otomatik onayı etkinleştirmek için aşağıdan en az bir seçenek seçin" + "selectOptionsFirst": "Otomatik onayı etkinleştirmek için aşağıdan en az bir seçenek seçin", + "diffView": { + "label": "Fark Görünümü", + "autoFocus": { + "label": "Fark düzenleyicilerini otomatik odakla", + "description": "Etkinleştirildiğinde, Roo tarafından açılan fark düzenleyicileri otomatik olarak odak kazanır. Devre dışı bırakıldığında, fark düzenleyicileri mevcut düzenleyicinizden odağı almadan arka planda açılır." + } + } }, "providers": { "providerDocumentation": "{{provider}} Dokümantasyonu", diff --git a/webview-ui/src/i18n/locales/vi/settings.json b/webview-ui/src/i18n/locales/vi/settings.json index 6505d7df0e..b1e028188f 100644 --- a/webview-ui/src/i18n/locales/vi/settings.json +++ b/webview-ui/src/i18n/locales/vi/settings.json @@ -197,7 +197,14 @@ "description": "Tự động thực hiện số lượng API request này trước khi yêu cầu phê duyệt để tiếp tục với nhiệm vụ.", "unlimited": "Không giới hạn" }, - "selectOptionsFirst": "Chọn ít nhất một tùy chọn bên dưới để bật tự động phê duyệt" + "selectOptionsFirst": "Chọn ít nhất một tùy chọn bên dưới để bật tự động phê duyệt", + "diffView": { + "label": "Xem khác biệt", + "autoFocus": { + "label": "Tự động lấy nét trình chỉnh sửa khác biệt", + "description": "Khi được bật, các trình chỉnh sửa khác biệt được mở bởi Roo sẽ tự động nhận được tiêu điểm. Khi bị tắt, các trình chỉnh sửa khác biệt sẽ mở trong nền mà không lấy tiêu điểm từ trình chỉnh sửa hiện tại của bạn." + } + } }, "providers": { "providerDocumentation": "Tài liệu {{provider}}", diff --git a/webview-ui/src/i18n/locales/zh-CN/settings.json b/webview-ui/src/i18n/locales/zh-CN/settings.json index bf3eea0294..dea088531b 100644 --- a/webview-ui/src/i18n/locales/zh-CN/settings.json +++ b/webview-ui/src/i18n/locales/zh-CN/settings.json @@ -197,7 +197,14 @@ "description": "在请求批准以继续执行任务之前,自动发出此数量的 API 请求。", "unlimited": "无限制" }, - "selectOptionsFirst": "请至少选择以下一个选项以启用自动批准" + "selectOptionsFirst": "请至少选择以下一个选项以启用自动批准", + "diffView": { + "label": "差异视图", + "autoFocus": { + "label": "自动聚焦差异编辑器", + "description": "启用后,Roo 打开的差异编辑器将自动获得焦点。禁用后,差异编辑器将在后台打开,而不会从当前编辑器中夺取焦点。" + } + } }, "providers": { "providerDocumentation": "{{provider}} 文档", diff --git a/webview-ui/src/i18n/locales/zh-TW/settings.json b/webview-ui/src/i18n/locales/zh-TW/settings.json index e2a896dce4..102e4020f0 100644 --- a/webview-ui/src/i18n/locales/zh-TW/settings.json +++ b/webview-ui/src/i18n/locales/zh-TW/settings.json @@ -197,7 +197,14 @@ "description": "在請求批准以繼續執行工作之前,自動發出此數量的 API 請求。", "unlimited": "無限制" }, - "selectOptionsFirst": "請至少選擇以下一個選項以啟用自動核准" + "selectOptionsFirst": "請至少選擇以下一個選項以啟用自動核准", + "diffView": { + "label": "差異檢視", + "autoFocus": { + "label": "自動聚焦差異編輯器", + "description": "啟用後,Roo 開啟的差異編輯器將自動獲得焦點。停用後,差異編輯器將在背景開啟,而不會從目前的編輯器中奪取焦點。" + } + } }, "providers": { "providerDocumentation": "{{provider}} 文件",