From 50d0fd43e6dbb9cdd2d8f679d0306db81c0fc0d2 Mon Sep 17 00:00:00 2001 From: Roo Code Date: Fri, 25 Jul 2025 18:18:50 +0000 Subject: [PATCH 01/10] feat: Add experimental setting to prevent editor focus disruption - Added PREVENT_FOCUS_DISRUPTION experiment ID to experiments system - Updated experiment types and schemas in types package - Added localization entries for the new experimental feature - Modified DiffViewProvider to add saveDirectly method for direct file saving - Updated all file editing tools to respect the experimental setting - Added comprehensive tests for the new functionality Implements #4784 --- packages/types/src/experiment.ts | 3 +- src/core/tools/applyDiffTool.ts | 95 +++++++++++++------ src/core/tools/insertContentTool.ts | 51 ++++++---- src/core/tools/multiApplyDiffTool.ts | 46 ++++++--- src/core/tools/searchAndReplaceTool.ts | 49 ++++++---- src/core/tools/writeToFileTool.ts | 59 ++++++++---- src/integrations/editor/DiffViewProvider.ts | 95 ++++++++++++++++++- .../editor/__tests__/DiffViewProvider.spec.ts | 82 ++++++++++++++++ ...experiments-preventFocusDisruption.spec.ts | 31 ++++++ src/shared/experiments.ts | 2 + webview-ui/src/i18n/locales/en/settings.json | 4 + 11 files changed, 418 insertions(+), 99 deletions(-) create mode 100644 src/shared/__tests__/experiments-preventFocusDisruption.spec.ts 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/tools/applyDiffTool.ts b/src/core/tools/applyDiffTool.ts index f046ba67d2..4d34bbb4f5 100644 --- a/src/core/tools/applyDiffTool.ts +++ b/src/core/tools/applyDiffTool.ts @@ -12,6 +12,7 @@ import { formatResponse } from "../prompts/responses" import { fileExistsAtPath } from "../../utils/fs" import { RecordSource } from "../context-tracking/FileContextTrackerTypes" import { unescapeHtmlEntities } from "../../utils/text-normalization" +import { EXPERIMENT_IDS, experiments } from "../../shared/experiments" export async function applyDiffToolLegacy( cline: Task, @@ -87,7 +88,7 @@ export async function applyDiffToolLegacy( return } - let originalContent: string | null = await fs.readFile(absolutePath, "utf-8") + const originalContent: string = await fs.readFile(absolutePath, "utf-8") // Apply the diff to the original content const diffResult = (await cline.diffStrategy?.applyDiff( @@ -99,9 +100,6 @@ export async function applyDiffToolLegacy( error: "No diff strategy available", } - // Release the original content from memory as it's no longer needed - originalContent = null - if (!diffResult.success) { cline.consecutiveMistakeCount++ const currentCount = (cline.consecutiveMistakeCountForApplyDiff.get(relPath) || 0) + 1 @@ -142,40 +140,79 @@ export async function applyDiffToolLegacy( cline.consecutiveMistakeCount = 0 cline.consecutiveMistakeCountForApplyDiff.delete(relPath) - // Show diff view before asking for approval - cline.diffViewProvider.editType = "modify" - await cline.diffViewProvider.open(relPath) - await cline.diffViewProvider.update(diffResult.content, true) - cline.diffViewProvider.scrollToFirstDiff() + // Check if preventFocusDisruption experiment is enabled + const provider = cline.providerRef.deref() + const state = await provider?.getState() + const diagnosticsEnabled = state?.diagnosticsEnabled ?? true + const writeDelayMs = state?.writeDelayMs ?? DEFAULT_WRITE_DELAY_MS + const isPreventFocusDisruptionEnabled = experiments.isEnabled( + state?.experiments ?? {}, + EXPERIMENT_IDS.PREVENT_FOCUS_DISRUPTION, + ) // Check if file is write-protected const isWriteProtected = cline.rooProtectedController?.isWriteProtected(relPath) || false - const completeMessage = JSON.stringify({ - ...sharedMessageProps, - diff: diffContent, - isProtected: isWriteProtected, - } satisfies ClineSayTool) + if (isPreventFocusDisruptionEnabled) { + // Direct file write without diff view + const completeMessage = JSON.stringify({ + ...sharedMessageProps, + diff: diffContent, + isProtected: isWriteProtected, + } satisfies ClineSayTool) - let toolProgressStatus + let toolProgressStatus - if (cline.diffStrategy && cline.diffStrategy.getProgressStatus) { - toolProgressStatus = cline.diffStrategy.getProgressStatus(block, diffResult) - } + if (cline.diffStrategy && cline.diffStrategy.getProgressStatus) { + toolProgressStatus = cline.diffStrategy.getProgressStatus(block, diffResult) + } - const didApprove = await askApproval("tool", completeMessage, toolProgressStatus, isWriteProtected) + const didApprove = await askApproval("tool", completeMessage, toolProgressStatus, isWriteProtected) - if (!didApprove) { - await cline.diffViewProvider.revertChanges() // Cline likely handles closing the diff view - return - } + if (!didApprove) { + return + } - // Call saveChanges to update the DiffViewProvider properties - const provider = cline.providerRef.deref() - const state = await provider?.getState() - const diagnosticsEnabled = state?.diagnosticsEnabled ?? true - const writeDelayMs = state?.writeDelayMs ?? DEFAULT_WRITE_DELAY_MS - await cline.diffViewProvider.saveChanges(diagnosticsEnabled, writeDelayMs) + // Save directly without showing diff view + cline.diffViewProvider.editType = "modify" + cline.diffViewProvider.originalContent = originalContent + await cline.diffViewProvider.saveDirectly( + relPath, + diffResult.content, + true, + diagnosticsEnabled, + writeDelayMs, + ) + } else { + // Original behavior with diff view + // Show diff view before asking for approval + cline.diffViewProvider.editType = "modify" + await cline.diffViewProvider.open(relPath) + await cline.diffViewProvider.update(diffResult.content, true) + cline.diffViewProvider.scrollToFirstDiff() + + const completeMessage = JSON.stringify({ + ...sharedMessageProps, + diff: diffContent, + isProtected: isWriteProtected, + } satisfies ClineSayTool) + + let toolProgressStatus + + if (cline.diffStrategy && cline.diffStrategy.getProgressStatus) { + toolProgressStatus = cline.diffStrategy.getProgressStatus(block, diffResult) + } + + const didApprove = await askApproval("tool", completeMessage, toolProgressStatus, isWriteProtected) + + if (!didApprove) { + await cline.diffViewProvider.revertChanges() // Cline likely handles closing the diff view + return + } + + // Call saveChanges to update the DiffViewProvider properties + await cline.diffViewProvider.saveChanges(diagnosticsEnabled, writeDelayMs) + } // Track file edit operation if (relPath) { diff --git a/src/core/tools/insertContentTool.ts b/src/core/tools/insertContentTool.ts index 2b31224400..27b689062a 100644 --- a/src/core/tools/insertContentTool.ts +++ b/src/core/tools/insertContentTool.ts @@ -11,6 +11,7 @@ import { RecordSource } from "../context-tracking/FileContextTrackerTypes" import { fileExistsAtPath } from "../../utils/fs" import { insertGroups } from "../diff/insert-groups" import { DEFAULT_WRITE_DELAY_MS } from "@roo-code/types" +import { EXPERIMENT_IDS, experiments } from "../../shared/experiments" export async function insertContentTool( cline: Task, @@ -107,15 +108,15 @@ export async function insertContentTool( }, ]).join("\n") - // Show changes in diff view - if (!cline.diffViewProvider.isEditing) { - await cline.ask("tool", JSON.stringify(sharedMessageProps), true).catch(() => {}) - // First open with original content - await cline.diffViewProvider.open(relPath) - await cline.diffViewProvider.update(fileContent, false) - cline.diffViewProvider.scrollToFirstDiff() - await delay(200) - } + // Check if preventFocusDisruption experiment is enabled + const provider = cline.providerRef.deref() + const state = await provider?.getState() + const diagnosticsEnabled = state?.diagnosticsEnabled ?? true + const writeDelayMs = state?.writeDelayMs ?? DEFAULT_WRITE_DELAY_MS + const isPreventFocusDisruptionEnabled = experiments.isEnabled( + state?.experiments ?? {}, + EXPERIMENT_IDS.PREVENT_FOCUS_DISRUPTION, + ) // For consistency with writeToFileTool, handle new files differently let diff: string | undefined @@ -135,8 +136,6 @@ export async function insertContentTool( approvalContent = updatedContent } - await cline.diffViewProvider.update(updatedContent, true) - const completeMessage = JSON.stringify({ ...sharedMessageProps, diff, @@ -150,17 +149,33 @@ export async function insertContentTool( .then((response) => response.response === "yesButtonClicked") if (!didApprove) { - await cline.diffViewProvider.revertChanges() + if (!isPreventFocusDisruptionEnabled) { + await cline.diffViewProvider.revertChanges() + } pushToolResult("Changes were rejected by the user.") return } - // Call saveChanges to update the DiffViewProvider properties - const provider = cline.providerRef.deref() - const state = await provider?.getState() - const diagnosticsEnabled = state?.diagnosticsEnabled ?? true - const writeDelayMs = state?.writeDelayMs ?? DEFAULT_WRITE_DELAY_MS - await cline.diffViewProvider.saveChanges(diagnosticsEnabled, writeDelayMs) + if (isPreventFocusDisruptionEnabled) { + // Direct file write without diff view + await cline.diffViewProvider.saveDirectly(relPath, updatedContent, true, diagnosticsEnabled, writeDelayMs) + } else { + // Original behavior with diff view + // Show changes in diff view + if (!cline.diffViewProvider.isEditing) { + await cline.ask("tool", JSON.stringify(sharedMessageProps), true).catch(() => {}) + // First open with original content + await cline.diffViewProvider.open(relPath) + await cline.diffViewProvider.update(fileContent, false) + cline.diffViewProvider.scrollToFirstDiff() + await delay(200) + } + + await cline.diffViewProvider.update(updatedContent, true) + + // Call saveChanges to update the DiffViewProvider properties + await cline.diffViewProvider.saveChanges(diagnosticsEnabled, writeDelayMs) + } // Track file edit operation if (relPath) { diff --git a/src/core/tools/multiApplyDiffTool.ts b/src/core/tools/multiApplyDiffTool.ts index ec8c77a63b..0cbb7e689d 100644 --- a/src/core/tools/multiApplyDiffTool.ts +++ b/src/core/tools/multiApplyDiffTool.ts @@ -507,11 +507,15 @@ ${errorDetails ? `\nTechnical details:\n${errorDetails}\n` : ""} cline.consecutiveMistakeCount = 0 cline.consecutiveMistakeCountForApplyDiff.delete(relPath) - // Show diff view before asking for approval (only for single file or after batch approval) - cline.diffViewProvider.editType = "modify" - await cline.diffViewProvider.open(relPath) - await cline.diffViewProvider.update(originalContent!, true) - cline.diffViewProvider.scrollToFirstDiff() + // Check if preventFocusDisruption experiment is enabled + const provider = cline.providerRef.deref() + const state = await provider?.getState() + const diagnosticsEnabled = state?.diagnosticsEnabled ?? true + const writeDelayMs = state?.writeDelayMs ?? DEFAULT_WRITE_DELAY_MS + const isPreventFocusDisruptionEnabled = experiments.isEnabled( + state?.experiments ?? {}, + EXPERIMENT_IDS.PREVENT_FOCUS_DISRUPTION, + ) // For batch operations, we've already gotten approval const isWriteProtected = cline.rooProtectedController?.isWriteProtected(relPath) || false @@ -548,17 +552,35 @@ ${errorDetails ? `\nTechnical details:\n${errorDetails}\n` : ""} } if (!didApprove) { - await cline.diffViewProvider.revertChanges() + if (!isPreventFocusDisruptionEnabled) { + await cline.diffViewProvider.revertChanges() + } results.push(`Changes to ${relPath} were not approved by user`) continue } - // Call saveChanges to update the DiffViewProvider properties - const provider = cline.providerRef.deref() - const state = await provider?.getState() - const diagnosticsEnabled = state?.diagnosticsEnabled ?? true - const writeDelayMs = state?.writeDelayMs ?? DEFAULT_WRITE_DELAY_MS - await cline.diffViewProvider.saveChanges(diagnosticsEnabled, writeDelayMs) + if (isPreventFocusDisruptionEnabled) { + // Direct file write without diff view + cline.diffViewProvider.editType = "modify" + cline.diffViewProvider.originalContent = await fs.readFile(absolutePath, "utf-8") + await cline.diffViewProvider.saveDirectly( + relPath, + originalContent!, + true, + diagnosticsEnabled, + writeDelayMs, + ) + } else { + // Original behavior with diff view + // Show diff view before asking for approval (only for single file or after batch approval) + cline.diffViewProvider.editType = "modify" + await cline.diffViewProvider.open(relPath) + await cline.diffViewProvider.update(originalContent!, true) + cline.diffViewProvider.scrollToFirstDiff() + + // Call saveChanges to update the DiffViewProvider properties + await cline.diffViewProvider.saveChanges(diagnosticsEnabled, writeDelayMs) + } // Track file edit operation await cline.fileContextTracker.trackFileContext(relPath, "roo_edited" as RecordSource) diff --git a/src/core/tools/searchAndReplaceTool.ts b/src/core/tools/searchAndReplaceTool.ts index b6ec3ed39b..8a602f0d28 100644 --- a/src/core/tools/searchAndReplaceTool.ts +++ b/src/core/tools/searchAndReplaceTool.ts @@ -12,6 +12,7 @@ import { getReadablePath } from "../../utils/path" import { fileExistsAtPath } from "../../utils/fs" import { RecordSource } from "../context-tracking/FileContextTrackerTypes" import { DEFAULT_WRITE_DELAY_MS } from "@roo-code/types" +import { EXPERIMENT_IDS, experiments } from "../../shared/experiments" /** * Tool for performing search and replace operations on files @@ -199,16 +200,15 @@ export async function searchAndReplaceTool( return } - // Show changes in diff view - if (!cline.diffViewProvider.isEditing) { - await cline.ask("tool", JSON.stringify(sharedMessageProps), true).catch(() => {}) - await cline.diffViewProvider.open(validRelPath) - await cline.diffViewProvider.update(fileContent, false) - cline.diffViewProvider.scrollToFirstDiff() - await delay(200) - } - - await cline.diffViewProvider.update(newContent, true) + // Check if preventFocusDisruption experiment is enabled + const provider = cline.providerRef.deref() + const state = await provider?.getState() + const diagnosticsEnabled = state?.diagnosticsEnabled ?? true + const writeDelayMs = state?.writeDelayMs ?? DEFAULT_WRITE_DELAY_MS + const isPreventFocusDisruptionEnabled = experiments.isEnabled( + state?.experiments ?? {}, + EXPERIMENT_IDS.PREVENT_FOCUS_DISRUPTION, + ) // Request user approval for changes const completeMessage = JSON.stringify({ @@ -221,18 +221,33 @@ export async function searchAndReplaceTool( .then((response) => response.response === "yesButtonClicked") if (!didApprove) { - await cline.diffViewProvider.revertChanges() + if (!isPreventFocusDisruptionEnabled) { + await cline.diffViewProvider.revertChanges() + } pushToolResult("Changes were rejected by the user.") await cline.diffViewProvider.reset() return } - // Call saveChanges to update the DiffViewProvider properties - const provider = cline.providerRef.deref() - const state = await provider?.getState() - const diagnosticsEnabled = state?.diagnosticsEnabled ?? true - const writeDelayMs = state?.writeDelayMs ?? DEFAULT_WRITE_DELAY_MS - await cline.diffViewProvider.saveChanges(diagnosticsEnabled, writeDelayMs) + if (isPreventFocusDisruptionEnabled) { + // Direct file write without diff view + await cline.diffViewProvider.saveDirectly(validRelPath, newContent, true, diagnosticsEnabled, writeDelayMs) + } else { + // Original behavior with diff view + // Show changes in diff view + if (!cline.diffViewProvider.isEditing) { + await cline.ask("tool", JSON.stringify(sharedMessageProps), true).catch(() => {}) + await cline.diffViewProvider.open(validRelPath) + await cline.diffViewProvider.update(fileContent, false) + cline.diffViewProvider.scrollToFirstDiff() + await delay(200) + } + + await cline.diffViewProvider.update(newContent, true) + + // Call saveChanges to update the DiffViewProvider properties + await cline.diffViewProvider.saveChanges(diagnosticsEnabled, writeDelayMs) + } // Track file edit operation if (relPath) { diff --git a/src/core/tools/writeToFileTool.ts b/src/core/tools/writeToFileTool.ts index fd9d158f3f..39f59c7737 100644 --- a/src/core/tools/writeToFileTool.ts +++ b/src/core/tools/writeToFileTool.ts @@ -14,6 +14,7 @@ import { isPathOutsideWorkspace } from "../../utils/pathUtils" import { detectCodeOmission } from "../../integrations/editor/detect-omission" import { unescapeHtmlEntities } from "../../utils/text-normalization" import { DEFAULT_WRITE_DELAY_MS } from "@roo-code/types" +import { EXPERIMENT_IDS, experiments } from "../../shared/experiments" export async function writeToFileTool( cline: Task, @@ -198,27 +199,51 @@ export async function writeToFileTool( } } - const completeMessage = JSON.stringify({ - ...sharedMessageProps, - content: fileExists ? undefined : newContent, - diff: fileExists - ? formatResponse.createPrettyPatch(relPath, cline.diffViewProvider.originalContent, newContent) - : undefined, - } satisfies ClineSayTool) - - const didApprove = await askApproval("tool", completeMessage, undefined, isWriteProtected) - - if (!didApprove) { - await cline.diffViewProvider.revertChanges() - return - } - - // Call saveChanges to update the DiffViewProvider properties + // Check if preventFocusDisruption experiment is enabled const provider = cline.providerRef.deref() const state = await provider?.getState() const diagnosticsEnabled = state?.diagnosticsEnabled ?? true const writeDelayMs = state?.writeDelayMs ?? DEFAULT_WRITE_DELAY_MS - await cline.diffViewProvider.saveChanges(diagnosticsEnabled, writeDelayMs) + const isPreventFocusDisruptionEnabled = experiments.isEnabled( + state?.experiments ?? {}, + EXPERIMENT_IDS.PREVENT_FOCUS_DISRUPTION, + ) + + if (isPreventFocusDisruptionEnabled) { + // Direct file write without diff view + const completeMessage = JSON.stringify({ + ...sharedMessageProps, + content: newContent, + } satisfies ClineSayTool) + + const didApprove = await askApproval("tool", completeMessage, undefined, isWriteProtected) + + if (!didApprove) { + return + } + + // Save directly without showing diff view + await cline.diffViewProvider.saveDirectly(relPath, newContent, true, diagnosticsEnabled, writeDelayMs) + } else { + // Original behavior with diff view + const completeMessage = JSON.stringify({ + ...sharedMessageProps, + content: fileExists ? undefined : newContent, + diff: fileExists + ? formatResponse.createPrettyPatch(relPath, cline.diffViewProvider.originalContent, newContent) + : undefined, + } satisfies ClineSayTool) + + const didApprove = await askApproval("tool", completeMessage, undefined, isWriteProtected) + + if (!didApprove) { + await cline.diffViewProvider.revertChanges() + return + } + + // Call saveChanges to update the DiffViewProvider properties + await cline.diffViewProvider.saveChanges(diagnosticsEnabled, writeDelayMs) + } // Track file edit operation if (relPath) { diff --git a/src/integrations/editor/DiffViewProvider.ts b/src/integrations/editor/DiffViewProvider.ts index 64820beffb..e66ea3dd32 100644 --- a/src/integrations/editor/DiffViewProvider.ts +++ b/src/integrations/editor/DiffViewProvider.ts @@ -187,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 @@ -222,22 +225,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() // Get diagnostic settings from state @@ -625,4 +628,86 @@ export class DiffViewProvider { this.streamedLines = [] this.preDiagnostics = [] } + + /** + * Directly save content to a file without showing diff view + * Used when preventFocusDisruption experiment is enabled + * + * @param relPath - Relative path to the file + * @param content - Content to write to the file + * @param openWithoutFocus - Whether to open the file without stealing focus (for diagnostics) + * @returns Result of the save operation including any new problems detected + */ + async saveDirectly( + relPath: string, + content: string, + openWithoutFocus: boolean = true, + diagnosticsEnabled: boolean = true, + writeDelayMs: number = DEFAULT_WRITE_DELAY_MS, + ): Promise<{ + newProblemsMessage: string | undefined + userEdits: string | undefined + finalContent: string | undefined + }> { + const absolutePath = path.resolve(this.cwd, relPath) + + // Get diagnostics before editing the file + this.preDiagnostics = vscode.languages.getDiagnostics() + + // Write the content directly to the file + await createDirectoriesForFile(absolutePath) + await fs.writeFile(absolutePath, content, "utf-8") + + // Open the file without focus if requested (to capture diagnostics) + if (openWithoutFocus) { + await vscode.window.showTextDocument(vscode.Uri.file(absolutePath), { + preview: false, + preserveFocus: true, + }) + } + + let newProblemsMessage = "" + + if (diagnosticsEnabled) { + // Add configurable delay to allow linters time to process + const safeDelayMs = Math.max(0, writeDelayMs) + + try { + await delay(safeDelayMs) + } catch (error) { + console.warn(`Failed to apply write delay: ${error}`) + } + + const postDiagnostics = vscode.languages.getDiagnostics() + + // Get diagnostic settings from state + const task = this.taskRef.deref() + const state = await task?.providerRef.deref()?.getState() + const includeDiagnosticMessages = state?.includeDiagnosticMessages ?? true + const maxDiagnosticMessages = state?.maxDiagnosticMessages ?? 50 + + const newProblems = await diagnosticsToProblemsString( + getNewDiagnostics(this.preDiagnostics, postDiagnostics), + [vscode.DiagnosticSeverity.Error], + this.cwd, + includeDiagnosticMessages, + maxDiagnosticMessages, + ) + + newProblemsMessage = + newProblems.length > 0 ? `\n\nNew problems detected after saving the file:\n${newProblems}` : "" + } + + // Store the results for formatFileWriteResponse + this.newProblemsMessage = newProblemsMessage + this.userEdits = undefined + this.relPath = relPath + this.newContent = content + + return { + newProblemsMessage, + userEdits: undefined, + finalContent: content, + } + } } diff --git a/src/integrations/editor/__tests__/DiffViewProvider.spec.ts b/src/integrations/editor/__tests__/DiffViewProvider.spec.ts index 7159aca57a..13422ead0f 100644 --- a/src/integrations/editor/__tests__/DiffViewProvider.spec.ts +++ b/src/integrations/editor/__tests__/DiffViewProvider.spec.ts @@ -353,6 +353,88 @@ describe("DiffViewProvider", () => { }) }) + describe("saveDirectly method", () => { + beforeEach(() => { + // Mock vscode functions + vi.mocked(vscode.window.showTextDocument).mockResolvedValue({} as any) + vi.mocked(vscode.languages.getDiagnostics).mockReturnValue([]) + }) + + it("should write content directly to file without opening diff view", async () => { + const mockDelay = vi.mocked(delay) + mockDelay.mockClear() + + const result = await diffViewProvider.saveDirectly("test.ts", "new content", true, true, 2000) + + // Verify file was written + const fs = await import("fs/promises") + expect(fs.writeFile).toHaveBeenCalledWith(`${mockCwd}/test.ts`, "new content", "utf-8") + + // Verify file was opened without focus + expect(vscode.window.showTextDocument).toHaveBeenCalledWith( + expect.objectContaining({ fsPath: `${mockCwd}/test.ts` }), + { preview: false, preserveFocus: true }, + ) + + // Verify diagnostics were checked after delay + expect(mockDelay).toHaveBeenCalledWith(2000) + expect(vscode.languages.getDiagnostics).toHaveBeenCalled() + + // Verify result + expect(result.newProblemsMessage).toBe("") + expect(result.userEdits).toBeUndefined() + expect(result.finalContent).toBe("new content") + }) + + it("should not open file when openWithoutFocus is false", async () => { + await diffViewProvider.saveDirectly("test.ts", "new content", false, true, 1000) + + // Verify file was written + const fs = await import("fs/promises") + expect(fs.writeFile).toHaveBeenCalledWith(`${mockCwd}/test.ts`, "new content", "utf-8") + + // Verify file was NOT opened + expect(vscode.window.showTextDocument).not.toHaveBeenCalled() + }) + + it("should skip diagnostics when diagnosticsEnabled is false", async () => { + const mockDelay = vi.mocked(delay) + mockDelay.mockClear() + vi.mocked(vscode.languages.getDiagnostics).mockClear() + + await diffViewProvider.saveDirectly("test.ts", "new content", true, false, 1000) + + // Verify file was written + const fs = await import("fs/promises") + expect(fs.writeFile).toHaveBeenCalledWith(`${mockCwd}/test.ts`, "new content", "utf-8") + + // Verify delay was NOT called + expect(mockDelay).not.toHaveBeenCalled() + // getDiagnostics is called once for pre-diagnostics, but not for post-diagnostics + expect(vscode.languages.getDiagnostics).toHaveBeenCalledTimes(1) + }) + + it("should handle negative delay values", async () => { + const mockDelay = vi.mocked(delay) + mockDelay.mockClear() + + await diffViewProvider.saveDirectly("test.ts", "new content", true, true, -500) + + // Verify delay was called with 0 (safe minimum) + expect(mockDelay).toHaveBeenCalledWith(0) + }) + + it("should store results for formatFileWriteResponse", async () => { + await diffViewProvider.saveDirectly("test.ts", "new content", true, true, 1000) + + // Verify internal state was updated + expect((diffViewProvider as any).newProblemsMessage).toBe("") + expect((diffViewProvider as any).userEdits).toBeUndefined() + expect((diffViewProvider as any).relPath).toBe("test.ts") + expect((diffViewProvider as any).newContent).toBe("new content") + }) + }) + describe("saveChanges method with diagnostic settings", () => { beforeEach(() => { // Setup common mocks for saveChanges tests diff --git a/src/shared/__tests__/experiments-preventFocusDisruption.spec.ts b/src/shared/__tests__/experiments-preventFocusDisruption.spec.ts new file mode 100644 index 0000000000..7e5389c286 --- /dev/null +++ b/src/shared/__tests__/experiments-preventFocusDisruption.spec.ts @@ -0,0 +1,31 @@ +import { describe, it, expect } from "vitest" +import { EXPERIMENT_IDS, experimentConfigsMap, experimentDefault, experiments } from "../experiments" + +describe("PREVENT_FOCUS_DISRUPTION experiment", () => { + it("should include PREVENT_FOCUS_DISRUPTION in EXPERIMENT_IDS", () => { + expect(EXPERIMENT_IDS.PREVENT_FOCUS_DISRUPTION).toBe("preventFocusDisruption") + }) + + it("should have PREVENT_FOCUS_DISRUPTION in experimentConfigsMap", () => { + expect(experimentConfigsMap.PREVENT_FOCUS_DISRUPTION).toBeDefined() + expect(experimentConfigsMap.PREVENT_FOCUS_DISRUPTION.enabled).toBe(false) + }) + + it("should have PREVENT_FOCUS_DISRUPTION in experimentDefault", () => { + expect(experimentDefault.preventFocusDisruption).toBe(false) + }) + + it("should correctly check if PREVENT_FOCUS_DISRUPTION is enabled", () => { + // Test when experiment is disabled (default) + const disabledConfig = { preventFocusDisruption: false } + expect(experiments.isEnabled(disabledConfig, EXPERIMENT_IDS.PREVENT_FOCUS_DISRUPTION)).toBe(false) + + // Test when experiment is enabled + const enabledConfig = { preventFocusDisruption: true } + expect(experiments.isEnabled(enabledConfig, EXPERIMENT_IDS.PREVENT_FOCUS_DISRUPTION)).toBe(true) + + // Test when experiment is not in config (should use default) + const emptyConfig = {} + expect(experiments.isEnabled(emptyConfig, EXPERIMENT_IDS.PREVENT_FOCUS_DISRUPTION)).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/i18n/locales/en/settings.json b/webview-ui/src/i18n/locales/en/settings.json index cfd5b04286..106ea1310f 100644 --- a/webview-ui/src/i18n/locales/en/settings.json +++ b/webview-ui/src/i18n/locales/en/settings.json @@ -648,6 +648,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 editor focus disruption", + "description": "When enabled, file edits happen in the background without opening diff views or stealing focus. You can continue working uninterrupted while Roo makes changes. Files can be opened without focus to capture diagnostics or kept closed entirely." } }, "promptCaching": { From 012b76f4d43ad10214d94ae53b18ed18aaf98f03 Mon Sep 17 00:00:00 2001 From: Roo Code Date: Fri, 25 Jul 2025 18:20:06 +0000 Subject: [PATCH 02/10] fix: Add preventFocusDisruption to experiment test objects --- src/shared/__tests__/experiments.spec.ts | 3 +++ 1 file changed, 3 insertions(+) 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) }) From de376f5845b9a973405d8c6503468bebb3740a9c Mon Sep 17 00:00:00 2001 From: Roo Code Date: Fri, 25 Jul 2025 18:21:26 +0000 Subject: [PATCH 03/10] fix: Add preventFocusDisruption to ExtensionStateContext test --- webview-ui/src/context/__tests__/ExtensionStateContext.spec.tsx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/webview-ui/src/context/__tests__/ExtensionStateContext.spec.tsx b/webview-ui/src/context/__tests__/ExtensionStateContext.spec.tsx index 1e5867d3fc..54e46d2c7b 100644 --- a/webview-ui/src/context/__tests__/ExtensionStateContext.spec.tsx +++ b/webview-ui/src/context/__tests__/ExtensionStateContext.spec.tsx @@ -226,6 +226,7 @@ describe("mergeExtensionState", () => { disableCompletionCommand: false, concurrentFileReads: true, multiFileApplyDiff: true, + preventFocusDisruption: false, } as Record, } @@ -242,6 +243,7 @@ describe("mergeExtensionState", () => { disableCompletionCommand: false, concurrentFileReads: true, multiFileApplyDiff: true, + preventFocusDisruption: false, }) }) }) From 9980267bcfff9b4788724e239e8481a2e5f92a8b Mon Sep 17 00:00:00 2001 From: Roo Code Date: Fri, 25 Jul 2025 18:24:32 +0000 Subject: [PATCH 04/10] fix: Add PREVENT_FOCUS_DISRUPTION translations to all language files --- webview-ui/src/i18n/locales/ca/settings.json | 4 ++++ webview-ui/src/i18n/locales/de/settings.json | 4 ++++ webview-ui/src/i18n/locales/es/settings.json | 4 ++++ webview-ui/src/i18n/locales/fr/settings.json | 4 ++++ webview-ui/src/i18n/locales/hi/settings.json | 4 ++++ webview-ui/src/i18n/locales/id/settings.json | 4 ++++ webview-ui/src/i18n/locales/it/settings.json | 4 ++++ webview-ui/src/i18n/locales/ja/settings.json | 4 ++++ webview-ui/src/i18n/locales/ko/settings.json | 4 ++++ webview-ui/src/i18n/locales/nl/settings.json | 4 ++++ webview-ui/src/i18n/locales/pl/settings.json | 4 ++++ webview-ui/src/i18n/locales/pt-BR/settings.json | 4 ++++ webview-ui/src/i18n/locales/ru/settings.json | 4 ++++ webview-ui/src/i18n/locales/tr/settings.json | 4 ++++ webview-ui/src/i18n/locales/vi/settings.json | 4 ++++ webview-ui/src/i18n/locales/zh-CN/settings.json | 4 ++++ webview-ui/src/i18n/locales/zh-TW/settings.json | 4 ++++ 17 files changed, 68 insertions(+) diff --git a/webview-ui/src/i18n/locales/ca/settings.json b/webview-ui/src/i18n/locales/ca/settings.json index ab929d724d..f4c3806af0 100644 --- a/webview-ui/src/i18n/locales/ca/settings.json +++ b/webview-ui/src/i18n/locales/ca/settings.json @@ -648,6 +648,10 @@ "MULTI_FILE_APPLY_DIFF": { "name": "Habilita edicions de fitxers concurrents", "description": "Quan està activat, Roo pot editar múltiples fitxers en una sola petició. Quan està desactivat, Roo ha d'editar fitxers d'un en un. Desactivar això pot ajudar quan es treballa amb models menys capaços o quan vols més control sobre les modificacions de fitxers." + }, + "PREVENT_FOCUS_DISRUPTION": { + "name": "Prevent Editor Focus Disruption", + "description": "When enabled, file edits bypass the diff view entirely and are written directly to disk. This prevents the editor from stealing focus during file modifications." } }, "promptCaching": { diff --git a/webview-ui/src/i18n/locales/de/settings.json b/webview-ui/src/i18n/locales/de/settings.json index 766bc891e4..4ea7ed85aa 100644 --- a/webview-ui/src/i18n/locales/de/settings.json +++ b/webview-ui/src/i18n/locales/de/settings.json @@ -648,6 +648,10 @@ "MULTI_FILE_APPLY_DIFF": { "name": "Gleichzeitige Dateibearbeitungen aktivieren", "description": "Wenn aktiviert, kann Roo mehrere Dateien in einer einzigen Anfrage bearbeiten. Wenn deaktiviert, muss Roo Dateien einzeln bearbeiten. Das Deaktivieren kann hilfreich sein, wenn mit weniger fähigen Modellen gearbeitet wird oder wenn du mehr Kontrolle über Dateiänderungen haben möchtest." + }, + "PREVENT_FOCUS_DISRUPTION": { + "name": "Prevent Editor Focus Disruption", + "description": "When enabled, file edits bypass the diff view entirely and are written directly to disk. This prevents the editor from stealing focus during file modifications." } }, "promptCaching": { diff --git a/webview-ui/src/i18n/locales/es/settings.json b/webview-ui/src/i18n/locales/es/settings.json index f536863909..1b98bed34b 100644 --- a/webview-ui/src/i18n/locales/es/settings.json +++ b/webview-ui/src/i18n/locales/es/settings.json @@ -648,6 +648,10 @@ "MULTI_FILE_APPLY_DIFF": { "name": "Habilitar ediciones de archivos concurrentes", "description": "Cuando está habilitado, Roo puede editar múltiples archivos en una sola solicitud. Cuando está deshabilitado, Roo debe editar archivos de uno en uno. Deshabilitar esto puede ayudar cuando trabajas con modelos menos capaces o cuando quieres más control sobre las modificaciones de archivos." + }, + "PREVENT_FOCUS_DISRUPTION": { + "name": "Prevent Editor Focus Disruption", + "description": "When enabled, file edits bypass the diff view entirely and are written directly to disk. This prevents the editor from stealing focus during file modifications." } }, "promptCaching": { diff --git a/webview-ui/src/i18n/locales/fr/settings.json b/webview-ui/src/i18n/locales/fr/settings.json index 0e12c58d38..112b931280 100644 --- a/webview-ui/src/i18n/locales/fr/settings.json +++ b/webview-ui/src/i18n/locales/fr/settings.json @@ -648,6 +648,10 @@ "MULTI_FILE_APPLY_DIFF": { "name": "Activer les éditions de fichiers concurrentes", "description": "Lorsque cette option est activée, Roo peut éditer plusieurs fichiers en une seule requête. Lorsqu'elle est désactivée, Roo doit éditer les fichiers un par un. Désactiver cette option peut aider lorsque tu travailles avec des modèles moins capables ou lorsque tu veux plus de contrôle sur les modifications de fichiers." + }, + "PREVENT_FOCUS_DISRUPTION": { + "name": "Prevent Editor Focus Disruption", + "description": "When enabled, file edits bypass the diff view entirely and are written directly to disk. This prevents the editor from stealing focus during file modifications." } }, "promptCaching": { diff --git a/webview-ui/src/i18n/locales/hi/settings.json b/webview-ui/src/i18n/locales/hi/settings.json index d2cfa971ff..c00201b8a8 100644 --- a/webview-ui/src/i18n/locales/hi/settings.json +++ b/webview-ui/src/i18n/locales/hi/settings.json @@ -649,6 +649,10 @@ "MULTI_FILE_APPLY_DIFF": { "name": "समानांतर फ़ाइल संपादन सक्षम करें", "description": "जब सक्षम किया जाता है, तो Roo एक ही अनुरोध में कई फ़ाइलों को संपादित कर सकता है। जब अक्षम किया जाता है, तो Roo को एक समय में एक फ़ाइल संपादित करनी होगी। इसे अक्षम करना तब मदद कर सकता है जब आप कम सक्षम मॉडल के साथ काम कर रहे हों या जब आप फ़ाइल संशोधनों पर अधिक नियंत्रण चाहते हों।" + }, + "PREVENT_FOCUS_DISRUPTION": { + "name": "Prevent Editor Focus Disruption", + "description": "When enabled, file edits bypass the diff view entirely and are written directly to disk. This prevents the editor from stealing focus during file modifications." } }, "promptCaching": { diff --git a/webview-ui/src/i18n/locales/id/settings.json b/webview-ui/src/i18n/locales/id/settings.json index 3f362f650d..e51bf7cdd2 100644 --- a/webview-ui/src/i18n/locales/id/settings.json +++ b/webview-ui/src/i18n/locales/id/settings.json @@ -678,6 +678,10 @@ "MULTI_FILE_APPLY_DIFF": { "name": "Aktifkan edit file bersamaan", "description": "Ketika diaktifkan, Roo dapat mengedit beberapa file dalam satu permintaan. Ketika dinonaktifkan, Roo harus mengedit file satu per satu. Menonaktifkan ini dapat membantu saat bekerja dengan model yang kurang mampu atau ketika kamu ingin kontrol lebih terhadap modifikasi file." + }, + "PREVENT_FOCUS_DISRUPTION": { + "name": "Prevent Editor Focus Disruption", + "description": "When enabled, file edits bypass the diff view entirely and are written directly to disk. This prevents the editor from stealing focus during file modifications." } }, "promptCaching": { diff --git a/webview-ui/src/i18n/locales/it/settings.json b/webview-ui/src/i18n/locales/it/settings.json index 14deda20ea..a608455e81 100644 --- a/webview-ui/src/i18n/locales/it/settings.json +++ b/webview-ui/src/i18n/locales/it/settings.json @@ -649,6 +649,10 @@ "MULTI_FILE_APPLY_DIFF": { "name": "Abilita modifiche di file concorrenti", "description": "Quando abilitato, Roo può modificare più file in una singola richiesta. Quando disabilitato, Roo deve modificare i file uno alla volta. Disabilitare questa opzione può aiutare quando lavori con modelli meno capaci o quando vuoi più controllo sulle modifiche dei file." + }, + "PREVENT_FOCUS_DISRUPTION": { + "name": "Prevent Editor Focus Disruption", + "description": "When enabled, file edits bypass the diff view entirely and are written directly to disk. This prevents the editor from stealing focus during file modifications." } }, "promptCaching": { diff --git a/webview-ui/src/i18n/locales/ja/settings.json b/webview-ui/src/i18n/locales/ja/settings.json index d7864e40db..11c04a0b48 100644 --- a/webview-ui/src/i18n/locales/ja/settings.json +++ b/webview-ui/src/i18n/locales/ja/settings.json @@ -649,6 +649,10 @@ "MULTI_FILE_APPLY_DIFF": { "name": "同時ファイル編集を有効にする", "description": "有効にすると、Rooは単一のリクエストで複数のファイルを編集できます。無効にすると、Rooはファイルを一つずつ編集する必要があります。これを無効にすることで、能力の低いモデルで作業する場合や、ファイル変更をより細かく制御したい場合に役立ちます。" + }, + "PREVENT_FOCUS_DISRUPTION": { + "name": "Prevent Editor Focus Disruption", + "description": "When enabled, file edits bypass the diff view entirely and are written directly to disk. This prevents the editor from stealing focus during file modifications." } }, "promptCaching": { diff --git a/webview-ui/src/i18n/locales/ko/settings.json b/webview-ui/src/i18n/locales/ko/settings.json index adbe216463..3bc81cc777 100644 --- a/webview-ui/src/i18n/locales/ko/settings.json +++ b/webview-ui/src/i18n/locales/ko/settings.json @@ -649,6 +649,10 @@ "MULTI_FILE_APPLY_DIFF": { "name": "동시 파일 편집 활성화", "description": "활성화하면 Roo가 단일 요청으로 여러 파일을 편집할 수 있습니다. 비활성화하면 Roo는 파일을 하나씩 편집해야 합니다. 이 기능을 비활성화하면 덜 강력한 모델로 작업하거나 파일 수정에 대한 더 많은 제어가 필요할 때 도움이 됩니다." + }, + "PREVENT_FOCUS_DISRUPTION": { + "name": "Prevent Editor Focus Disruption", + "description": "When enabled, file edits bypass the diff view entirely and are written directly to disk. This prevents the editor from stealing focus during file modifications." } }, "promptCaching": { diff --git a/webview-ui/src/i18n/locales/nl/settings.json b/webview-ui/src/i18n/locales/nl/settings.json index 6085be8fb1..356d94d7f0 100644 --- a/webview-ui/src/i18n/locales/nl/settings.json +++ b/webview-ui/src/i18n/locales/nl/settings.json @@ -649,6 +649,10 @@ "MULTI_FILE_APPLY_DIFF": { "name": "Gelijktijdige bestandsbewerkingen inschakelen", "description": "Wanneer ingeschakeld, kan Roo meerdere bestanden in één verzoek bewerken. Wanneer uitgeschakeld, moet Roo bestanden één voor één bewerken. Het uitschakelen hiervan kan helpen wanneer je werkt met minder capabele modellen of wanneer je meer controle wilt over bestandswijzigingen." + }, + "PREVENT_FOCUS_DISRUPTION": { + "name": "Prevent Editor Focus Disruption", + "description": "When enabled, file edits bypass the diff view entirely and are written directly to disk. This prevents the editor from stealing focus during file modifications." } }, "promptCaching": { diff --git a/webview-ui/src/i18n/locales/pl/settings.json b/webview-ui/src/i18n/locales/pl/settings.json index 9107fd959e..112baf2280 100644 --- a/webview-ui/src/i18n/locales/pl/settings.json +++ b/webview-ui/src/i18n/locales/pl/settings.json @@ -649,6 +649,10 @@ "MULTI_FILE_APPLY_DIFF": { "name": "Włącz równoczesne edycje plików", "description": "Gdy włączone, Roo może edytować wiele plików w jednym żądaniu. Gdy wyłączone, Roo musi edytować pliki jeden po drugim. Wyłączenie tego może pomóc podczas pracy z mniej zdolnymi modelami lub gdy chcesz mieć większą kontrolę nad modyfikacjami plików." + }, + "PREVENT_FOCUS_DISRUPTION": { + "name": "Prevent Editor Focus Disruption", + "description": "When enabled, file edits bypass the diff view entirely and are written directly to disk. This prevents the editor from stealing focus during file modifications." } }, "promptCaching": { diff --git a/webview-ui/src/i18n/locales/pt-BR/settings.json b/webview-ui/src/i18n/locales/pt-BR/settings.json index 8137d917ae..21172f6c3e 100644 --- a/webview-ui/src/i18n/locales/pt-BR/settings.json +++ b/webview-ui/src/i18n/locales/pt-BR/settings.json @@ -649,6 +649,10 @@ "MULTI_FILE_APPLY_DIFF": { "name": "Habilitar edições de arquivos concorrentes", "description": "Quando habilitado, o Roo pode editar múltiplos arquivos em uma única solicitação. Quando desabilitado, o Roo deve editar arquivos um de cada vez. Desabilitar isso pode ajudar ao trabalhar com modelos menos capazes ou quando você quer mais controle sobre modificações de arquivos." + }, + "PREVENT_FOCUS_DISRUPTION": { + "name": "Prevent Editor Focus Disruption", + "description": "When enabled, file edits bypass the diff view entirely and are written directly to disk. This prevents the editor from stealing focus during file modifications." } }, "promptCaching": { diff --git a/webview-ui/src/i18n/locales/ru/settings.json b/webview-ui/src/i18n/locales/ru/settings.json index e93f24e9d7..ad797fba41 100644 --- a/webview-ui/src/i18n/locales/ru/settings.json +++ b/webview-ui/src/i18n/locales/ru/settings.json @@ -649,6 +649,10 @@ "MULTI_FILE_APPLY_DIFF": { "name": "Включить одновременное редактирование файлов", "description": "Когда включено, Roo может редактировать несколько файлов в одном запросе. Когда отключено, Roo должен редактировать файлы по одному. Отключение этой функции может помочь при работе с менее способными моделями или когда вы хотите больше контроля над изменениями файлов." + }, + "PREVENT_FOCUS_DISRUPTION": { + "name": "Prevent Editor Focus Disruption", + "description": "When enabled, file edits bypass the diff view entirely and are written directly to disk. This prevents the editor from stealing focus during file modifications." } }, "promptCaching": { diff --git a/webview-ui/src/i18n/locales/tr/settings.json b/webview-ui/src/i18n/locales/tr/settings.json index 450738183b..156c1820d0 100644 --- a/webview-ui/src/i18n/locales/tr/settings.json +++ b/webview-ui/src/i18n/locales/tr/settings.json @@ -649,6 +649,10 @@ "MULTI_FILE_APPLY_DIFF": { "name": "Eşzamanlı dosya düzenlemelerini etkinleştir", "description": "Etkinleştirildiğinde, Roo tek bir istekte birden fazla dosyayı düzenleyebilir. Devre dışı bırakıldığında, Roo dosyaları tek tek düzenlemek zorundadır. Bunu devre dışı bırakmak, daha az yetenekli modellerle çalışırken veya dosya değişiklikleri üzerinde daha fazla kontrol istediğinde yardımcı olabilir." + }, + "PREVENT_FOCUS_DISRUPTION": { + "name": "Prevent Editor Focus Disruption", + "description": "When enabled, file edits bypass the diff view entirely and are written directly to disk. This prevents the editor from stealing focus during file modifications." } }, "promptCaching": { diff --git a/webview-ui/src/i18n/locales/vi/settings.json b/webview-ui/src/i18n/locales/vi/settings.json index 87e99ce6f5..c57826fe0e 100644 --- a/webview-ui/src/i18n/locales/vi/settings.json +++ b/webview-ui/src/i18n/locales/vi/settings.json @@ -649,6 +649,10 @@ "MULTI_FILE_APPLY_DIFF": { "name": "Bật chỉnh sửa tệp đồng thời", "description": "Khi được bật, Roo có thể chỉnh sửa nhiều tệp trong một yêu cầu duy nhất. Khi bị tắt, Roo phải chỉnh sửa từng tệp một. Tắt tính năng này có thể hữu ích khi làm việc với các mô hình kém khả năng hơn hoặc khi bạn muốn kiểm soát nhiều hơn đối với các thay đổi tệp." + }, + "PREVENT_FOCUS_DISRUPTION": { + "name": "Prevent Editor Focus Disruption", + "description": "When enabled, file edits bypass the diff view entirely and are written directly to disk. This prevents the editor from stealing focus during file modifications." } }, "promptCaching": { diff --git a/webview-ui/src/i18n/locales/zh-CN/settings.json b/webview-ui/src/i18n/locales/zh-CN/settings.json index e94c857e65..991beb3259 100644 --- a/webview-ui/src/i18n/locales/zh-CN/settings.json +++ b/webview-ui/src/i18n/locales/zh-CN/settings.json @@ -649,6 +649,10 @@ "MULTI_FILE_APPLY_DIFF": { "name": "启用并发文件编辑", "description": "启用后 Roo 可在单个请求中编辑多个文件。禁用后 Roo 必须逐个编辑文件。禁用此功能有助于使用能力较弱的模型或需要更精确控制文件修改时。" + }, + "PREVENT_FOCUS_DISRUPTION": { + "name": "Prevent Editor Focus Disruption", + "description": "When enabled, file edits bypass the diff view entirely and are written directly to disk. This prevents the editor from stealing focus during file modifications." } }, "promptCaching": { diff --git a/webview-ui/src/i18n/locales/zh-TW/settings.json b/webview-ui/src/i18n/locales/zh-TW/settings.json index fa10cf3028..eaf8d151f7 100644 --- a/webview-ui/src/i18n/locales/zh-TW/settings.json +++ b/webview-ui/src/i18n/locales/zh-TW/settings.json @@ -649,6 +649,10 @@ "MULTI_FILE_APPLY_DIFF": { "name": "啟用並行檔案編輯", "description": "啟用後 Roo 可在單個請求中編輯多個檔案。停用後 Roo 必須逐個編輯檔案。停用此功能有助於使用能力較弱的模型或需要更精確控制檔案修改時。" + }, + "PREVENT_FOCUS_DISRUPTION": { + "name": "Prevent Editor Focus Disruption", + "description": "When enabled, file edits bypass the diff view entirely and are written directly to disk. This prevents the editor from stealing focus during file modifications." } }, "promptCaching": { From 13e163a0af32adba8c282841f50bf8f706a6da77 Mon Sep 17 00:00:00 2001 From: Daniel Riccio Date: Mon, 28 Jul 2025 18:07:32 -0500 Subject: [PATCH 05/10] fix: prevent focus stealing when PREVENT_FOCUS_DISRUPTION experiment is enabled - Modified saveDirectly method in DiffViewProvider to accept openFile parameter instead of openWithoutFocus - Updated all file editing tools to pass false for openFile when the experiment is enabled - This ensures files are not opened at all when the experiment is active, preventing any focus stealing Fixes #6214 --- src/core/tools/applyDiffTool.ts | 4 ++-- src/core/tools/insertContentTool.ts | 4 ++-- src/core/tools/multiApplyDiffTool.ts | 4 ++-- src/core/tools/searchAndReplaceTool.ts | 4 ++-- src/core/tools/writeToFileTool.ts | 4 ++-- src/integrations/editor/DiffViewProvider.ts | 7 ++++--- 6 files changed, 14 insertions(+), 13 deletions(-) diff --git a/src/core/tools/applyDiffTool.ts b/src/core/tools/applyDiffTool.ts index 4d34bbb4f5..903e3c846e 100644 --- a/src/core/tools/applyDiffTool.ts +++ b/src/core/tools/applyDiffTool.ts @@ -173,13 +173,13 @@ export async function applyDiffToolLegacy( return } - // Save directly without showing diff view + // Save directly without showing diff view or opening the file cline.diffViewProvider.editType = "modify" cline.diffViewProvider.originalContent = originalContent await cline.diffViewProvider.saveDirectly( relPath, diffResult.content, - true, + false, diagnosticsEnabled, writeDelayMs, ) diff --git a/src/core/tools/insertContentTool.ts b/src/core/tools/insertContentTool.ts index 27b689062a..a42eb6f6f9 100644 --- a/src/core/tools/insertContentTool.ts +++ b/src/core/tools/insertContentTool.ts @@ -157,8 +157,8 @@ export async function insertContentTool( } if (isPreventFocusDisruptionEnabled) { - // Direct file write without diff view - await cline.diffViewProvider.saveDirectly(relPath, updatedContent, true, diagnosticsEnabled, writeDelayMs) + // Direct file write without diff view or opening the file + await cline.diffViewProvider.saveDirectly(relPath, updatedContent, false, diagnosticsEnabled, writeDelayMs) } else { // Original behavior with diff view // Show changes in diff view diff --git a/src/core/tools/multiApplyDiffTool.ts b/src/core/tools/multiApplyDiffTool.ts index 0cbb7e689d..80f4c9a054 100644 --- a/src/core/tools/multiApplyDiffTool.ts +++ b/src/core/tools/multiApplyDiffTool.ts @@ -560,13 +560,13 @@ ${errorDetails ? `\nTechnical details:\n${errorDetails}\n` : ""} } if (isPreventFocusDisruptionEnabled) { - // Direct file write without diff view + // Direct file write without diff view or opening the file cline.diffViewProvider.editType = "modify" cline.diffViewProvider.originalContent = await fs.readFile(absolutePath, "utf-8") await cline.diffViewProvider.saveDirectly( relPath, originalContent!, - true, + false, diagnosticsEnabled, writeDelayMs, ) diff --git a/src/core/tools/searchAndReplaceTool.ts b/src/core/tools/searchAndReplaceTool.ts index 8a602f0d28..09b644f931 100644 --- a/src/core/tools/searchAndReplaceTool.ts +++ b/src/core/tools/searchAndReplaceTool.ts @@ -230,8 +230,8 @@ export async function searchAndReplaceTool( } if (isPreventFocusDisruptionEnabled) { - // Direct file write without diff view - await cline.diffViewProvider.saveDirectly(validRelPath, newContent, true, diagnosticsEnabled, writeDelayMs) + // Direct file write without diff view or opening the file + await cline.diffViewProvider.saveDirectly(validRelPath, newContent, false, diagnosticsEnabled, writeDelayMs) } else { // Original behavior with diff view // Show changes in diff view diff --git a/src/core/tools/writeToFileTool.ts b/src/core/tools/writeToFileTool.ts index 39f59c7737..9da295738f 100644 --- a/src/core/tools/writeToFileTool.ts +++ b/src/core/tools/writeToFileTool.ts @@ -222,8 +222,8 @@ export async function writeToFileTool( return } - // Save directly without showing diff view - await cline.diffViewProvider.saveDirectly(relPath, newContent, true, diagnosticsEnabled, writeDelayMs) + // Save directly without showing diff view or opening the file + await cline.diffViewProvider.saveDirectly(relPath, newContent, false, diagnosticsEnabled, writeDelayMs) } else { // Original behavior with diff view const completeMessage = JSON.stringify({ diff --git a/src/integrations/editor/DiffViewProvider.ts b/src/integrations/editor/DiffViewProvider.ts index e66ea3dd32..a0cd5a86f0 100644 --- a/src/integrations/editor/DiffViewProvider.ts +++ b/src/integrations/editor/DiffViewProvider.ts @@ -641,7 +641,7 @@ export class DiffViewProvider { async saveDirectly( relPath: string, content: string, - openWithoutFocus: boolean = true, + openFile: boolean = true, diagnosticsEnabled: boolean = true, writeDelayMs: number = DEFAULT_WRITE_DELAY_MS, ): Promise<{ @@ -658,8 +658,9 @@ export class DiffViewProvider { await createDirectoriesForFile(absolutePath) await fs.writeFile(absolutePath, content, "utf-8") - // Open the file without focus if requested (to capture diagnostics) - if (openWithoutFocus) { + // Only open the file if explicitly requested + // This prevents focus stealing when the experiment is enabled + if (openFile) { await vscode.window.showTextDocument(vscode.Uri.file(absolutePath), { preview: false, preserveFocus: true, From 7cd621f72f5ed7004bba16aeb656fe1f9a8be56c Mon Sep 17 00:00:00 2001 From: Daniel Riccio Date: Mon, 28 Jul 2025 18:33:42 -0500 Subject: [PATCH 06/10] fix: always open files with preserveFocus to ensure diagnostics load - Files are now always opened when using saveDirectly, but with preserveFocus: true - This ensures diagnostics are properly loaded without stealing focus - The openFile parameter is now deprecated but kept for backward compatibility --- src/integrations/editor/DiffViewProvider.ts | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/src/integrations/editor/DiffViewProvider.ts b/src/integrations/editor/DiffViewProvider.ts index a0cd5a86f0..fbb79c3d26 100644 --- a/src/integrations/editor/DiffViewProvider.ts +++ b/src/integrations/editor/DiffViewProvider.ts @@ -635,7 +635,7 @@ export class DiffViewProvider { * * @param relPath - Relative path to the file * @param content - Content to write to the file - * @param openWithoutFocus - Whether to open the file without stealing focus (for diagnostics) + * @param openFile - Whether to open the file (deprecated - file is always opened with preserveFocus) * @returns Result of the save operation including any new problems detected */ async saveDirectly( @@ -658,14 +658,12 @@ export class DiffViewProvider { await createDirectoriesForFile(absolutePath) await fs.writeFile(absolutePath, content, "utf-8") - // Only open the file if explicitly requested - // This prevents focus stealing when the experiment is enabled - if (openFile) { - await vscode.window.showTextDocument(vscode.Uri.file(absolutePath), { - preview: false, - preserveFocus: true, - }) - } + // Always open the file to ensure diagnostics are loaded + // When openFile is false (PREVENT_FOCUS_DISRUPTION enabled), we still open but preserve focus + await vscode.window.showTextDocument(vscode.Uri.file(absolutePath), { + preview: false, + preserveFocus: true, + }) let newProblemsMessage = "" From 2977019324730c9dda0e2ec2fdac4d5ca06af033 Mon Sep 17 00:00:00 2001 From: Daniel Riccio Date: Mon, 28 Jul 2025 19:38:20 -0500 Subject: [PATCH 07/10] fix: ensure document is saved after opening to trigger diagnostics - Add doc.save() call after opening document in memory when openFile is false - This ensures VSCode recognizes the file as saved and triggers language server diagnostics - Fixes issue where diagnostics were not loading for writeToFileTool with PREVENT_FOCUS_DISRUPTION enabled --- src/core/tools/writeToFileTool.ts | 175 +++++++++++++------- src/integrations/editor/DiffViewProvider.ts | 28 +++- 2 files changed, 133 insertions(+), 70 deletions(-) diff --git a/src/core/tools/writeToFileTool.ts b/src/core/tools/writeToFileTool.ts index 9da295738f..e82eab92bc 100644 --- a/src/core/tools/writeToFileTool.ts +++ b/src/core/tools/writeToFileTool.ts @@ -1,6 +1,7 @@ import path from "path" import delay from "delay" import * as vscode from "vscode" +import fs from "fs/promises" import { Task } from "../task/Task" import { ClineSayTool } from "../../shared/ExtensionMessage" @@ -100,22 +101,32 @@ export async function writeToFileTool( try { if (block.partial) { - // update gui message - const partialMessage = JSON.stringify(sharedMessageProps) - await cline.ask("tool", partialMessage, block.partial).catch(() => {}) - - // update editor - if (!cline.diffViewProvider.isEditing) { - // open the editor and prepare to stream content in - await cline.diffViewProvider.open(relPath) - } - - // editor is open, stream content in - await cline.diffViewProvider.update( - everyLineHasLineNumbers(newContent) ? stripLineNumbers(newContent) : newContent, - false, + // Check if preventFocusDisruption experiment is enabled + const provider = cline.providerRef.deref() + const state = await provider?.getState() + const isPreventFocusDisruptionEnabled = experiments.isEnabled( + state?.experiments ?? {}, + EXPERIMENT_IDS.PREVENT_FOCUS_DISRUPTION, ) + if (!isPreventFocusDisruptionEnabled) { + // update gui message + const partialMessage = JSON.stringify(sharedMessageProps) + await cline.ask("tool", partialMessage, block.partial).catch(() => {}) + + // update editor + if (!cline.diffViewProvider.isEditing) { + // open the editor and prepare to stream content in + await cline.diffViewProvider.open(relPath) + } + + // editor is open, stream content in + await cline.diffViewProvider.update( + everyLineHasLineNumbers(newContent) ? stripLineNumbers(newContent) : newContent, + false, + ) + } + return } else { if (predictedLineCount === undefined) { @@ -150,55 +161,6 @@ export async function writeToFileTool( cline.consecutiveMistakeCount = 0 - // if isEditingFile false, that means we have the full contents of the file already. - // it's important to note how cline function works, you can't make the assumption that the block.partial conditional will always be called since it may immediately get complete, non-partial data. So cline part of the logic will always be called. - // in other words, you must always repeat the block.partial logic here - if (!cline.diffViewProvider.isEditing) { - // show gui message before showing edit animation - const partialMessage = JSON.stringify(sharedMessageProps) - await cline.ask("tool", partialMessage, true).catch(() => {}) // sending true for partial even though it's not a partial, cline shows the edit row before the content is streamed into the editor - await cline.diffViewProvider.open(relPath) - } - - await cline.diffViewProvider.update( - everyLineHasLineNumbers(newContent) ? stripLineNumbers(newContent) : newContent, - true, - ) - - await delay(300) // wait for diff view to update - cline.diffViewProvider.scrollToFirstDiff() - - // Check for code omissions before proceeding - if (detectCodeOmission(cline.diffViewProvider.originalContent || "", newContent, predictedLineCount)) { - if (cline.diffStrategy) { - await cline.diffViewProvider.revertChanges() - - pushToolResult( - formatResponse.toolError( - `Content appears to be truncated (file has ${ - newContent.split("\n").length - } lines but was predicted to have ${predictedLineCount} lines), and found comments indicating omitted code (e.g., '// rest of code unchanged', '/* previous code */'). Please provide the complete file content without any omissions if possible, or otherwise use the 'apply_diff' tool to apply the diff to the original file.`, - ), - ) - return - } else { - vscode.window - .showWarningMessage( - "Potential code truncation detected. cline happens when the AI reaches its max output limit.", - "Follow cline guide to fix the issue", - ) - .then((selection) => { - if (selection === "Follow cline guide to fix the issue") { - vscode.env.openExternal( - vscode.Uri.parse( - "https://github.com/cline/cline/wiki/Troubleshooting-%E2%80%90-Cline-Deleting-Code-with-%22Rest-of-Code-Here%22-Comments", - ), - ) - } - }) - } - } - // Check if preventFocusDisruption experiment is enabled const provider = cline.providerRef.deref() const state = await provider?.getState() @@ -211,6 +173,35 @@ export async function writeToFileTool( if (isPreventFocusDisruptionEnabled) { // Direct file write without diff view + // Check for code omissions before proceeding + if (detectCodeOmission(cline.diffViewProvider.originalContent || "", newContent, predictedLineCount)) { + if (cline.diffStrategy) { + pushToolResult( + formatResponse.toolError( + `Content appears to be truncated (file has ${ + newContent.split("\n").length + } lines but was predicted to have ${predictedLineCount} lines), and found comments indicating omitted code (e.g., '// rest of code unchanged', '/* previous code */'). Please provide the complete file content without any omissions if possible, or otherwise use the 'apply_diff' tool to apply the diff to the original file.`, + ), + ) + return + } else { + vscode.window + .showWarningMessage( + "Potential code truncation detected. cline happens when the AI reaches its max output limit.", + "Follow cline guide to fix the issue", + ) + .then((selection) => { + if (selection === "Follow cline guide to fix the issue") { + vscode.env.openExternal( + vscode.Uri.parse( + "https://github.com/cline/cline/wiki/Troubleshooting-%E2%80%90-Cline-Deleting-Code-with-%22Rest-of-Code-Here%22-Comments", + ), + ) + } + }) + } + } + const completeMessage = JSON.stringify({ ...sharedMessageProps, content: newContent, @@ -222,10 +213,68 @@ export async function writeToFileTool( return } + // Set up diffViewProvider properties needed for saveDirectly + cline.diffViewProvider.editType = fileExists ? "modify" : "create" + if (fileExists) { + const absolutePath = path.resolve(cline.cwd, relPath) + cline.diffViewProvider.originalContent = await fs.readFile(absolutePath, "utf-8") + } else { + cline.diffViewProvider.originalContent = "" + } + // Save directly without showing diff view or opening the file await cline.diffViewProvider.saveDirectly(relPath, newContent, false, diagnosticsEnabled, writeDelayMs) } else { // Original behavior with diff view + // if isEditingFile false, that means we have the full contents of the file already. + // it's important to note how cline function works, you can't make the assumption that the block.partial conditional will always be called since it may immediately get complete, non-partial data. So cline part of the logic will always be called. + // in other words, you must always repeat the block.partial logic here + if (!cline.diffViewProvider.isEditing) { + // show gui message before showing edit animation + const partialMessage = JSON.stringify(sharedMessageProps) + await cline.ask("tool", partialMessage, true).catch(() => {}) // sending true for partial even though it's not a partial, cline shows the edit row before the content is streamed into the editor + await cline.diffViewProvider.open(relPath) + } + + await cline.diffViewProvider.update( + everyLineHasLineNumbers(newContent) ? stripLineNumbers(newContent) : newContent, + true, + ) + + await delay(300) // wait for diff view to update + cline.diffViewProvider.scrollToFirstDiff() + + // Check for code omissions before proceeding + if (detectCodeOmission(cline.diffViewProvider.originalContent || "", newContent, predictedLineCount)) { + if (cline.diffStrategy) { + await cline.diffViewProvider.revertChanges() + + pushToolResult( + formatResponse.toolError( + `Content appears to be truncated (file has ${ + newContent.split("\n").length + } lines but was predicted to have ${predictedLineCount} lines), and found comments indicating omitted code (e.g., '// rest of code unchanged', '/* previous code */'). Please provide the complete file content without any omissions if possible, or otherwise use the 'apply_diff' tool to apply the diff to the original file.`, + ), + ) + return + } else { + vscode.window + .showWarningMessage( + "Potential code truncation detected. cline happens when the AI reaches its max output limit.", + "Follow cline guide to fix the issue", + ) + .then((selection) => { + if (selection === "Follow cline guide to fix the issue") { + vscode.env.openExternal( + vscode.Uri.parse( + "https://github.com/cline/cline/wiki/Troubleshooting-%E2%80%90-Cline-Deleting-Code-with-%22Rest-of-Code-Here%22-Comments", + ), + ) + } + }) + } + } + const completeMessage = JSON.stringify({ ...sharedMessageProps, content: fileExists ? undefined : newContent, diff --git a/src/integrations/editor/DiffViewProvider.ts b/src/integrations/editor/DiffViewProvider.ts index fbb79c3d26..5acf09ea78 100644 --- a/src/integrations/editor/DiffViewProvider.ts +++ b/src/integrations/editor/DiffViewProvider.ts @@ -635,7 +635,7 @@ export class DiffViewProvider { * * @param relPath - Relative path to the file * @param content - Content to write to the file - * @param openFile - Whether to open the file (deprecated - file is always opened with preserveFocus) + * @param openFile - Whether to show the file in editor (false = open in memory only for diagnostics) * @returns Result of the save operation including any new problems detected */ async saveDirectly( @@ -658,12 +658,26 @@ export class DiffViewProvider { await createDirectoriesForFile(absolutePath) await fs.writeFile(absolutePath, content, "utf-8") - // Always open the file to ensure diagnostics are loaded - // When openFile is false (PREVENT_FOCUS_DISRUPTION enabled), we still open but preserve focus - await vscode.window.showTextDocument(vscode.Uri.file(absolutePath), { - preview: false, - preserveFocus: true, - }) + // Open the document to ensure diagnostics are loaded + // When openFile is false (PREVENT_FOCUS_DISRUPTION enabled), we only open in memory + if (openFile) { + // Show the document in the editor + await vscode.window.showTextDocument(vscode.Uri.file(absolutePath), { + preview: false, + preserveFocus: true, + }) + } else { + // Just open the document in memory to trigger diagnostics without showing it + const doc = await vscode.workspace.openTextDocument(vscode.Uri.file(absolutePath)) + + // Save the document to ensure VSCode recognizes it as saved and triggers diagnostics + if (doc.isDirty) { + await doc.save() + } + + // Force a small delay to ensure diagnostics are triggered + await new Promise((resolve) => setTimeout(resolve, 100)) + } let newProblemsMessage = "" From 74ed94b4b9bc9ff45d3707d6cfaa290945944085 Mon Sep 17 00:00:00 2001 From: Daniel Riccio Date: Mon, 28 Jul 2025 20:07:08 -0500 Subject: [PATCH 08/10] Update translations for Background editing experiment setting - Rename 'Prevent Editor Focus Disruption' to 'Background editing' across all locales - Move original name into description for better clarity - Update all 17 supported languages: ca, de, en, es, fr, hi, id, it, ja, ko, nl, pl, pt-BR, ru, tr, vi, zh-CN, zh-TW - Maintain consistent terminology and cultural adaptation for each language --- webview-ui/src/i18n/locales/ca/settings.json | 4 ++-- webview-ui/src/i18n/locales/de/settings.json | 4 ++-- webview-ui/src/i18n/locales/en/settings.json | 4 ++-- webview-ui/src/i18n/locales/es/settings.json | 4 ++-- webview-ui/src/i18n/locales/fr/settings.json | 4 ++-- webview-ui/src/i18n/locales/hi/settings.json | 4 ++-- webview-ui/src/i18n/locales/id/settings.json | 4 ++-- webview-ui/src/i18n/locales/it/settings.json | 4 ++-- webview-ui/src/i18n/locales/ja/settings.json | 4 ++-- webview-ui/src/i18n/locales/ko/settings.json | 4 ++-- webview-ui/src/i18n/locales/nl/settings.json | 4 ++-- webview-ui/src/i18n/locales/pl/settings.json | 4 ++-- webview-ui/src/i18n/locales/pt-BR/settings.json | 4 ++-- webview-ui/src/i18n/locales/ru/settings.json | 4 ++-- webview-ui/src/i18n/locales/tr/settings.json | 4 ++-- webview-ui/src/i18n/locales/vi/settings.json | 4 ++-- webview-ui/src/i18n/locales/zh-CN/settings.json | 4 ++-- webview-ui/src/i18n/locales/zh-TW/settings.json | 4 ++-- 18 files changed, 36 insertions(+), 36 deletions(-) diff --git a/webview-ui/src/i18n/locales/ca/settings.json b/webview-ui/src/i18n/locales/ca/settings.json index f4c3806af0..ae5a66d0d0 100644 --- a/webview-ui/src/i18n/locales/ca/settings.json +++ b/webview-ui/src/i18n/locales/ca/settings.json @@ -650,8 +650,8 @@ "description": "Quan està activat, Roo pot editar múltiples fitxers en una sola petició. Quan està desactivat, Roo ha d'editar fitxers d'un en un. Desactivar això pot ajudar quan es treballa amb models menys capaços o quan vols més control sobre les modificacions de fitxers." }, "PREVENT_FOCUS_DISRUPTION": { - "name": "Prevent Editor Focus Disruption", - "description": "When enabled, file edits bypass the diff view entirely and are written directly to disk. This prevents the editor from stealing focus during file modifications." + "name": "Edició en segon pla", + "description": "Quan s'activa, evita la interrupció del focus de l'editor. Les edicions de fitxers es produeixen en segon pla sense obrir la vista diff o robar el focus. Pots continuar treballant sense interrupcions mentre Roo fa canvis. Els fitxers poden obrir-se sense focus per capturar diagnòstics o romandre completament tancats." } }, "promptCaching": { diff --git a/webview-ui/src/i18n/locales/de/settings.json b/webview-ui/src/i18n/locales/de/settings.json index 4ea7ed85aa..2dc6c21eb4 100644 --- a/webview-ui/src/i18n/locales/de/settings.json +++ b/webview-ui/src/i18n/locales/de/settings.json @@ -650,8 +650,8 @@ "description": "Wenn aktiviert, kann Roo mehrere Dateien in einer einzigen Anfrage bearbeiten. Wenn deaktiviert, muss Roo Dateien einzeln bearbeiten. Das Deaktivieren kann hilfreich sein, wenn mit weniger fähigen Modellen gearbeitet wird oder wenn du mehr Kontrolle über Dateiänderungen haben möchtest." }, "PREVENT_FOCUS_DISRUPTION": { - "name": "Prevent Editor Focus Disruption", - "description": "When enabled, file edits bypass the diff view entirely and are written directly to disk. This prevents the editor from stealing focus during file modifications." + "name": "Hintergrundbearbeitung", + "description": "Verhindert Editor-Fokus-Störungen wenn aktiviert. Dateibearbeitungen erfolgen im Hintergrund ohne Öffnung von Diff-Ansichten oder Fokus-Diebstahl. Du kannst ungestört weiterarbeiten, während Roo Änderungen vornimmt. Dateien können ohne Fokus geöffnet werden, um Diagnosen zu erfassen oder vollständig geschlossen bleiben." } }, "promptCaching": { diff --git a/webview-ui/src/i18n/locales/en/settings.json b/webview-ui/src/i18n/locales/en/settings.json index 106ea1310f..acd07b0335 100644 --- a/webview-ui/src/i18n/locales/en/settings.json +++ b/webview-ui/src/i18n/locales/en/settings.json @@ -650,8 +650,8 @@ "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 editor focus disruption", - "description": "When enabled, file edits happen in the background without opening diff views or stealing focus. You can continue working uninterrupted while Roo makes changes. Files can be opened without focus to capture diagnostics or kept closed entirely." + "name": "Background editing", + "description": "Prevent editor focus disruption when enabled. File edits happen in the background without opening diff views or stealing focus. You can continue working uninterrupted while Roo makes changes. Files can be opened without focus to capture diagnostics or kept closed entirely." } }, "promptCaching": { diff --git a/webview-ui/src/i18n/locales/es/settings.json b/webview-ui/src/i18n/locales/es/settings.json index 1b98bed34b..7ad549b070 100644 --- a/webview-ui/src/i18n/locales/es/settings.json +++ b/webview-ui/src/i18n/locales/es/settings.json @@ -650,8 +650,8 @@ "description": "Cuando está habilitado, Roo puede editar múltiples archivos en una sola solicitud. Cuando está deshabilitado, Roo debe editar archivos de uno en uno. Deshabilitar esto puede ayudar cuando trabajas con modelos menos capaces o cuando quieres más control sobre las modificaciones de archivos." }, "PREVENT_FOCUS_DISRUPTION": { - "name": "Prevent Editor Focus Disruption", - "description": "When enabled, file edits bypass the diff view entirely and are written directly to disk. This prevents the editor from stealing focus during file modifications." + "name": "Edición en segundo plano", + "description": "Previene la interrupción del foco del editor cuando está habilitado. Las ediciones de archivos ocurren en segundo plano sin abrir vistas de diferencias o robar el foco. Puedes continuar trabajando sin interrupciones mientras Roo realiza cambios. Los archivos pueden abrirse sin foco para capturar diagnósticos o mantenerse completamente cerrados." } }, "promptCaching": { diff --git a/webview-ui/src/i18n/locales/fr/settings.json b/webview-ui/src/i18n/locales/fr/settings.json index 112b931280..57693015ad 100644 --- a/webview-ui/src/i18n/locales/fr/settings.json +++ b/webview-ui/src/i18n/locales/fr/settings.json @@ -650,8 +650,8 @@ "description": "Lorsque cette option est activée, Roo peut éditer plusieurs fichiers en une seule requête. Lorsqu'elle est désactivée, Roo doit éditer les fichiers un par un. Désactiver cette option peut aider lorsque tu travailles avec des modèles moins capables ou lorsque tu veux plus de contrôle sur les modifications de fichiers." }, "PREVENT_FOCUS_DISRUPTION": { - "name": "Prevent Editor Focus Disruption", - "description": "When enabled, file edits bypass the diff view entirely and are written directly to disk. This prevents the editor from stealing focus during file modifications." + "name": "Édition en arrière-plan", + "description": "Empêche la perturbation du focus de l'éditeur lorsqu'activé. Les modifications de fichiers se font en arrière-plan sans ouvrir de vues de différences ou voler le focus. Vous pouvez continuer à travailler sans interruption pendant que Roo effectue des changements. Les fichiers peuvent être ouverts sans focus pour capturer les diagnostics ou rester complètement fermés." } }, "promptCaching": { diff --git a/webview-ui/src/i18n/locales/hi/settings.json b/webview-ui/src/i18n/locales/hi/settings.json index c00201b8a8..8bc9116ab5 100644 --- a/webview-ui/src/i18n/locales/hi/settings.json +++ b/webview-ui/src/i18n/locales/hi/settings.json @@ -651,8 +651,8 @@ "description": "जब सक्षम किया जाता है, तो Roo एक ही अनुरोध में कई फ़ाइलों को संपादित कर सकता है। जब अक्षम किया जाता है, तो Roo को एक समय में एक फ़ाइल संपादित करनी होगी। इसे अक्षम करना तब मदद कर सकता है जब आप कम सक्षम मॉडल के साथ काम कर रहे हों या जब आप फ़ाइल संशोधनों पर अधिक नियंत्रण चाहते हों।" }, "PREVENT_FOCUS_DISRUPTION": { - "name": "Prevent Editor Focus Disruption", - "description": "When enabled, file edits bypass the diff view entirely and are written directly to disk. This prevents the editor from stealing focus during file modifications." + "name": "बैकग्राउंड संपादन", + "description": "सक्षम होने पर एडिटर फोकस व्यवधान को रोकता है। फ़ाइल संपादन diff व्यू खोले बिना या फोकस चुराए बिना बैकग्राउंड में होता है। आप Roo के बदलाव करते समय बिना किसी बाधा के काम जारी रख सकते हैं। फ़ाइलें डायग्नोस्टिक्स कैप्चर करने के लिए बिना फोकस के खुल सकती हैं या पूरी तरह बंद रह सकती हैं।" } }, "promptCaching": { diff --git a/webview-ui/src/i18n/locales/id/settings.json b/webview-ui/src/i18n/locales/id/settings.json index e51bf7cdd2..2568abf68f 100644 --- a/webview-ui/src/i18n/locales/id/settings.json +++ b/webview-ui/src/i18n/locales/id/settings.json @@ -680,8 +680,8 @@ "description": "Ketika diaktifkan, Roo dapat mengedit beberapa file dalam satu permintaan. Ketika dinonaktifkan, Roo harus mengedit file satu per satu. Menonaktifkan ini dapat membantu saat bekerja dengan model yang kurang mampu atau ketika kamu ingin kontrol lebih terhadap modifikasi file." }, "PREVENT_FOCUS_DISRUPTION": { - "name": "Prevent Editor Focus Disruption", - "description": "When enabled, file edits bypass the diff view entirely and are written directly to disk. This prevents the editor from stealing focus during file modifications." + "name": "Pengeditan Latar Belakang", + "description": "Ketika diaktifkan, mencegah gangguan fokus editor. Pengeditan file terjadi di latar belakang tanpa membuka tampilan diff atau mencuri fokus. Anda dapat terus bekerja tanpa gangguan saat Roo melakukan perubahan. File mungkin dibuka tanpa fokus untuk menangkap diagnostik atau tetap tertutup sepenuhnya." } }, "promptCaching": { diff --git a/webview-ui/src/i18n/locales/it/settings.json b/webview-ui/src/i18n/locales/it/settings.json index a608455e81..784fc6bc45 100644 --- a/webview-ui/src/i18n/locales/it/settings.json +++ b/webview-ui/src/i18n/locales/it/settings.json @@ -651,8 +651,8 @@ "description": "Quando abilitato, Roo può modificare più file in una singola richiesta. Quando disabilitato, Roo deve modificare i file uno alla volta. Disabilitare questa opzione può aiutare quando lavori con modelli meno capaci o quando vuoi più controllo sulle modifiche dei file." }, "PREVENT_FOCUS_DISRUPTION": { - "name": "Prevent Editor Focus Disruption", - "description": "When enabled, file edits bypass the diff view entirely and are written directly to disk. This prevents the editor from stealing focus during file modifications." + "name": "Modifica in background", + "description": "Previene l'interruzione del focus dell'editor quando abilitato. Le modifiche ai file avvengono in background senza aprire viste di differenze o rubare il focus. Puoi continuare a lavorare senza interruzioni mentre Roo effettua modifiche. I file possono essere aperti senza focus per catturare diagnostiche o rimanere completamente chiusi." } }, "promptCaching": { diff --git a/webview-ui/src/i18n/locales/ja/settings.json b/webview-ui/src/i18n/locales/ja/settings.json index 11c04a0b48..e2fc53360f 100644 --- a/webview-ui/src/i18n/locales/ja/settings.json +++ b/webview-ui/src/i18n/locales/ja/settings.json @@ -651,8 +651,8 @@ "description": "有効にすると、Rooは単一のリクエストで複数のファイルを編集できます。無効にすると、Rooはファイルを一つずつ編集する必要があります。これを無効にすることで、能力の低いモデルで作業する場合や、ファイル変更をより細かく制御したい場合に役立ちます。" }, "PREVENT_FOCUS_DISRUPTION": { - "name": "Prevent Editor Focus Disruption", - "description": "When enabled, file edits bypass the diff view entirely and are written directly to disk. This prevents the editor from stealing focus during file modifications." + "name": "バックグラウンド編集", + "description": "有効にするとエディターのフォーカス中断を防ぎます。ファイル編集は差分ビューを開いたりフォーカスを奪ったりすることなく、バックグラウンドで行われます。Rooが変更を行っている間も中断されることなく作業を続けることができます。ファイルは診断をキャプチャするためにフォーカスなしで開くか、完全に閉じたままにできます。" } }, "promptCaching": { diff --git a/webview-ui/src/i18n/locales/ko/settings.json b/webview-ui/src/i18n/locales/ko/settings.json index 3bc81cc777..11b5211cc9 100644 --- a/webview-ui/src/i18n/locales/ko/settings.json +++ b/webview-ui/src/i18n/locales/ko/settings.json @@ -651,8 +651,8 @@ "description": "활성화하면 Roo가 단일 요청으로 여러 파일을 편집할 수 있습니다. 비활성화하면 Roo는 파일을 하나씩 편집해야 합니다. 이 기능을 비활성화하면 덜 강력한 모델로 작업하거나 파일 수정에 대한 더 많은 제어가 필요할 때 도움이 됩니다." }, "PREVENT_FOCUS_DISRUPTION": { - "name": "Prevent Editor Focus Disruption", - "description": "When enabled, file edits bypass the diff view entirely and are written directly to disk. This prevents the editor from stealing focus during file modifications." + "name": "백그라운드 편집", + "description": "활성화하면 편집기 포커스 방해를 방지합니다. 파일 편집이 diff 뷰를 열거나 포커스를 빼앗지 않고 백그라운드에서 수행됩니다. Roo가 변경사항을 적용하는 동안 방해받지 않고 계속 작업할 수 있습니다. 파일은 진단을 캡처하기 위해 포커스 없이 열거나 완전히 닫힌 상태로 유지할 수 있습니다." } }, "promptCaching": { diff --git a/webview-ui/src/i18n/locales/nl/settings.json b/webview-ui/src/i18n/locales/nl/settings.json index 356d94d7f0..13eabe36ab 100644 --- a/webview-ui/src/i18n/locales/nl/settings.json +++ b/webview-ui/src/i18n/locales/nl/settings.json @@ -651,8 +651,8 @@ "description": "Wanneer ingeschakeld, kan Roo meerdere bestanden in één verzoek bewerken. Wanneer uitgeschakeld, moet Roo bestanden één voor één bewerken. Het uitschakelen hiervan kan helpen wanneer je werkt met minder capabele modellen of wanneer je meer controle wilt over bestandswijzigingen." }, "PREVENT_FOCUS_DISRUPTION": { - "name": "Prevent Editor Focus Disruption", - "description": "When enabled, file edits bypass the diff view entirely and are written directly to disk. This prevents the editor from stealing focus during file modifications." + "name": "Achtergrondbewerking", + "description": "Voorkomt editor focus verstoring wanneer ingeschakeld. Bestandsbewerkingen gebeuren op de achtergrond zonder diff-weergaven te openen of focus te stelen. Je kunt ononderbroken doorwerken terwijl Roo wijzigingen aanbrengt. Bestanden kunnen zonder focus worden geopend om diagnostiek vast te leggen of volledig gesloten blijven." } }, "promptCaching": { diff --git a/webview-ui/src/i18n/locales/pl/settings.json b/webview-ui/src/i18n/locales/pl/settings.json index 112baf2280..ef37b25546 100644 --- a/webview-ui/src/i18n/locales/pl/settings.json +++ b/webview-ui/src/i18n/locales/pl/settings.json @@ -651,8 +651,8 @@ "description": "Gdy włączone, Roo może edytować wiele plików w jednym żądaniu. Gdy wyłączone, Roo musi edytować pliki jeden po drugim. Wyłączenie tego może pomóc podczas pracy z mniej zdolnymi modelami lub gdy chcesz mieć większą kontrolę nad modyfikacjami plików." }, "PREVENT_FOCUS_DISRUPTION": { - "name": "Prevent Editor Focus Disruption", - "description": "When enabled, file edits bypass the diff view entirely and are written directly to disk. This prevents the editor from stealing focus during file modifications." + "name": "Edycja w tle", + "description": "Zapobiega zakłócaniu fokusa edytora gdy włączone. Edycje plików odbywają się w tle bez otwierania widoków różnic lub kradzieży fokusa. Możesz kontynuować pracę bez przeszkód podczas gdy Roo wprowadza zmiany. Pliki mogą być otwierane bez fokusa aby przechwycić diagnostykę lub pozostać całkowicie zamknięte." } }, "promptCaching": { diff --git a/webview-ui/src/i18n/locales/pt-BR/settings.json b/webview-ui/src/i18n/locales/pt-BR/settings.json index 21172f6c3e..c26c532844 100644 --- a/webview-ui/src/i18n/locales/pt-BR/settings.json +++ b/webview-ui/src/i18n/locales/pt-BR/settings.json @@ -651,8 +651,8 @@ "description": "Quando habilitado, o Roo pode editar múltiplos arquivos em uma única solicitação. Quando desabilitado, o Roo deve editar arquivos um de cada vez. Desabilitar isso pode ajudar ao trabalhar com modelos menos capazes ou quando você quer mais controle sobre modificações de arquivos." }, "PREVENT_FOCUS_DISRUPTION": { - "name": "Prevent Editor Focus Disruption", - "description": "When enabled, file edits bypass the diff view entirely and are written directly to disk. This prevents the editor from stealing focus during file modifications." + "name": "Edição em segundo plano", + "description": "Previne a interrupção do foco do editor quando habilitado. As edições de arquivos acontecem em segundo plano sem abrir visualizações de diferenças ou roubar o foco. Você pode continuar trabalhando sem interrupções enquanto o Roo faz alterações. Os arquivos podem ser abertos sem foco para capturar diagnósticos ou permanecer completamente fechados." } }, "promptCaching": { diff --git a/webview-ui/src/i18n/locales/ru/settings.json b/webview-ui/src/i18n/locales/ru/settings.json index ad797fba41..f5e9cd8ad3 100644 --- a/webview-ui/src/i18n/locales/ru/settings.json +++ b/webview-ui/src/i18n/locales/ru/settings.json @@ -651,8 +651,8 @@ "description": "Когда включено, Roo может редактировать несколько файлов в одном запросе. Когда отключено, Roo должен редактировать файлы по одному. Отключение этой функции может помочь при работе с менее способными моделями или когда вы хотите больше контроля над изменениями файлов." }, "PREVENT_FOCUS_DISRUPTION": { - "name": "Prevent Editor Focus Disruption", - "description": "When enabled, file edits bypass the diff view entirely and are written directly to disk. This prevents the editor from stealing focus during file modifications." + "name": "Фоновое редактирование", + "description": "Предотвращает нарушение фокуса редактора при включении. Редактирование файлов происходит в фоновом режиме без открытия представлений различий или кражи фокуса. Вы можете продолжать работать без перерывов, пока Roo вносит изменения. Файлы могут открываться без фокуса для захвата диагностики или оставаться полностью закрытыми." } }, "promptCaching": { diff --git a/webview-ui/src/i18n/locales/tr/settings.json b/webview-ui/src/i18n/locales/tr/settings.json index 156c1820d0..e7a4526672 100644 --- a/webview-ui/src/i18n/locales/tr/settings.json +++ b/webview-ui/src/i18n/locales/tr/settings.json @@ -651,8 +651,8 @@ "description": "Etkinleştirildiğinde, Roo tek bir istekte birden fazla dosyayı düzenleyebilir. Devre dışı bırakıldığında, Roo dosyaları tek tek düzenlemek zorundadır. Bunu devre dışı bırakmak, daha az yetenekli modellerle çalışırken veya dosya değişiklikleri üzerinde daha fazla kontrol istediğinde yardımcı olabilir." }, "PREVENT_FOCUS_DISRUPTION": { - "name": "Prevent Editor Focus Disruption", - "description": "When enabled, file edits bypass the diff view entirely and are written directly to disk. This prevents the editor from stealing focus during file modifications." + "name": "Arka plan düzenleme", + "description": "Etkinleştirildiğinde editör odak kesintisini önler. Dosya düzenlemeleri diff görünümlerini açmadan veya odağı çalmadan arka planda gerçekleşir. Roo değişiklikler yaparken kesintisiz çalışmaya devam edebilirsiniz. Dosyalar tanılamayı yakalamak için odaksız açılabilir veya tamamen kapalı kalabilir." } }, "promptCaching": { diff --git a/webview-ui/src/i18n/locales/vi/settings.json b/webview-ui/src/i18n/locales/vi/settings.json index c57826fe0e..d3235da82f 100644 --- a/webview-ui/src/i18n/locales/vi/settings.json +++ b/webview-ui/src/i18n/locales/vi/settings.json @@ -651,8 +651,8 @@ "description": "Khi được bật, Roo có thể chỉnh sửa nhiều tệp trong một yêu cầu duy nhất. Khi bị tắt, Roo phải chỉnh sửa từng tệp một. Tắt tính năng này có thể hữu ích khi làm việc với các mô hình kém khả năng hơn hoặc khi bạn muốn kiểm soát nhiều hơn đối với các thay đổi tệp." }, "PREVENT_FOCUS_DISRUPTION": { - "name": "Prevent Editor Focus Disruption", - "description": "When enabled, file edits bypass the diff view entirely and are written directly to disk. This prevents the editor from stealing focus during file modifications." + "name": "Chỉnh sửa nền", + "description": "Khi được bật, ngăn chặn gián đoạn tiêu điểm trình soạn thảo. Việc chỉnh sửa tệp diễn ra ở nền mà không mở chế độ xem diff hoặc chiếm tiêu điểm. Bạn có thể tiếp tục làm việc không bị gián đoạn trong khi Roo thực hiện thay đổi. Các tệp có thể được mở mà không có tiêu điểm để thu thập chẩn đoán hoặc giữ hoàn toàn đóng." } }, "promptCaching": { diff --git a/webview-ui/src/i18n/locales/zh-CN/settings.json b/webview-ui/src/i18n/locales/zh-CN/settings.json index 991beb3259..87990e6890 100644 --- a/webview-ui/src/i18n/locales/zh-CN/settings.json +++ b/webview-ui/src/i18n/locales/zh-CN/settings.json @@ -651,8 +651,8 @@ "description": "启用后 Roo 可在单个请求中编辑多个文件。禁用后 Roo 必须逐个编辑文件。禁用此功能有助于使用能力较弱的模型或需要更精确控制文件修改时。" }, "PREVENT_FOCUS_DISRUPTION": { - "name": "Prevent Editor Focus Disruption", - "description": "When enabled, file edits bypass the diff view entirely and are written directly to disk. This prevents the editor from stealing focus during file modifications." + "name": "后台编辑", + "description": "启用后防止编辑器焦点干扰。文件编辑在后台进行,不会打开差异视图或抢夺焦点。你可以在 Roo 进行更改时继续不受干扰地工作。文件可以在不获取焦点的情况下打开以捕获诊断信息,或保持完全关闭状态。" } }, "promptCaching": { diff --git a/webview-ui/src/i18n/locales/zh-TW/settings.json b/webview-ui/src/i18n/locales/zh-TW/settings.json index eaf8d151f7..14d4840b98 100644 --- a/webview-ui/src/i18n/locales/zh-TW/settings.json +++ b/webview-ui/src/i18n/locales/zh-TW/settings.json @@ -651,8 +651,8 @@ "description": "啟用後 Roo 可在單個請求中編輯多個檔案。停用後 Roo 必須逐個編輯檔案。停用此功能有助於使用能力較弱的模型或需要更精確控制檔案修改時。" }, "PREVENT_FOCUS_DISRUPTION": { - "name": "Prevent Editor Focus Disruption", - "description": "When enabled, file edits bypass the diff view entirely and are written directly to disk. This prevents the editor from stealing focus during file modifications." + "name": "背景編輯", + "description": "啟用後可防止編輯器焦點中斷。檔案編輯會在背景進行,不會開啟 diff 檢視或搶奪焦點。您可以在 Roo 進行變更時繼續不受干擾地工作。檔案可能會在不獲得焦點的情況下開啟以捕獲診斷,或保持完全關閉。" } }, "promptCaching": { From 98dc47b8afa718dafc67a5446808e0802299b197 Mon Sep 17 00:00:00 2001 From: Daniel Riccio Date: Mon, 28 Jul 2025 20:28:37 -0500 Subject: [PATCH 09/10] fix: add missing workspace.openTextDocument mock in DiffViewProvider tests --- src/integrations/editor/__tests__/DiffViewProvider.spec.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/integrations/editor/__tests__/DiffViewProvider.spec.ts b/src/integrations/editor/__tests__/DiffViewProvider.spec.ts index 13422ead0f..0737b143cd 100644 --- a/src/integrations/editor/__tests__/DiffViewProvider.spec.ts +++ b/src/integrations/editor/__tests__/DiffViewProvider.spec.ts @@ -30,6 +30,10 @@ vi.mock("vscode", () => ({ workspace: { applyEdit: vi.fn(), onDidOpenTextDocument: vi.fn(() => ({ dispose: vi.fn() })), + openTextDocument: vi.fn().mockResolvedValue({ + isDirty: false, + save: vi.fn().mockResolvedValue(undefined), + }), textDocuments: [], fs: { stat: vi.fn(), From ec93ce9638690543efec521149f16637b634c361 Mon Sep 17 00:00:00 2001 From: Daniel <57051444+daniel-lxs@users.noreply.github.com> Date: Mon, 28 Jul 2025 20:44:23 -0500 Subject: [PATCH 10/10] Update webview-ui/src/i18n/locales/ja/settings.json Co-authored-by: ellipsis-dev[bot] <65095814+ellipsis-dev[bot]@users.noreply.github.com> --- webview-ui/src/i18n/locales/ja/settings.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/webview-ui/src/i18n/locales/ja/settings.json b/webview-ui/src/i18n/locales/ja/settings.json index e2fc53360f..d5ba882ce9 100644 --- a/webview-ui/src/i18n/locales/ja/settings.json +++ b/webview-ui/src/i18n/locales/ja/settings.json @@ -652,7 +652,7 @@ }, "PREVENT_FOCUS_DISRUPTION": { "name": "バックグラウンド編集", - "description": "有効にするとエディターのフォーカス中断を防ぎます。ファイル編集は差分ビューを開いたりフォーカスを奪ったりすることなく、バックグラウンドで行われます。Rooが変更を行っている間も中断されることなく作業を続けることができます。ファイルは診断をキャプチャするためにフォーカスなしで開くか、完全に閉じたままにできます。" + "description": "有効にすると、エディターのフォーカス中断を防ぎます。ファイル編集は差分ビューを開いたりフォーカスを奪ったりすることなく、バックグラウンドで行われます。Rooが変更を行っている間も中断されることなく作業を続けることができます。ファイルは診断をキャプチャするためにフォーカスなしで開くか、完全に閉じたままにできます。" } }, "promptCaching": {