fix: prevent sidebar opening in multi-window VS Code setups (#5485)

- Add logic to detect multi-window scenarios in focusPanel utility
- Prevent automatic sidebar activation when user is working in different window
- Add comprehensive tests for focusPanel functionality
- Maintain backward compatibility with existing behavior

Fixes #5485
This commit is contained in:
Roo Code 2025-07-09 00:55:51 +00:00
parent cb4652e45e
commit a591dcb77f
2 changed files with 171 additions and 2 deletions

View file

@ -0,0 +1,127 @@
import { describe, it, expect, vi, beforeEach } from "vitest"
import * as vscode from "vscode"
import { focusPanel } from "../focusPanel"
import { ClineProvider } from "../../core/webview/ClineProvider"
// Mock vscode module
vi.mock("vscode", () => ({
commands: {
executeCommand: vi.fn(),
},
window: {
activeTextEditor: undefined,
visibleTextEditors: [],
},
ViewColumn: {
Active: 1,
},
}))
// Mock ClineProvider
vi.mock("../../core/webview/ClineProvider", () => ({
ClineProvider: {
sideBarId: "roo-code.SidebarProvider",
getVisibleInstance: vi.fn(),
},
}))
// Mock Package
vi.mock("../../shared/package", () => ({
Package: {
name: "roo-code",
},
}))
describe("focusPanel", () => {
const mockExecuteCommand = vi.mocked(vscode.commands.executeCommand)
const mockGetVisibleInstance = vi.mocked(ClineProvider.getVisibleInstance)
beforeEach(() => {
vi.clearAllMocks()
// Reset window state
;(vscode.window as any).activeTextEditor = undefined
;(vscode.window as any).visibleTextEditors = []
})
describe("when panels exist", () => {
it("should reveal tab panel when it exists but is not active", async () => {
const mockTabPanel = {
active: false,
reveal: vi.fn(),
} as any
await focusPanel(mockTabPanel, undefined)
expect(mockTabPanel.reveal).toHaveBeenCalledWith(1, false)
expect(mockExecuteCommand).not.toHaveBeenCalled()
})
it("should focus sidebar panel when it exists", async () => {
const mockSidebarPanel = {} as any
await focusPanel(undefined, mockSidebarPanel)
expect(mockExecuteCommand).toHaveBeenCalledWith("roo-code.SidebarProvider.focus")
})
it("should prefer tab panel over sidebar panel when both exist", async () => {
const mockTabPanel = {
active: false,
reveal: vi.fn(),
} as any
const mockSidebarPanel = {} as any
await focusPanel(mockTabPanel, mockSidebarPanel)
expect(mockTabPanel.reveal).toHaveBeenCalledWith(1, false)
expect(mockExecuteCommand).not.toHaveBeenCalled()
})
})
describe("when no panels exist", () => {
it("should open sidebar when there is a visible Roo Code instance", async () => {
mockGetVisibleInstance.mockReturnValue({} as any)
await focusPanel(undefined, undefined)
expect(mockExecuteCommand).toHaveBeenCalledWith("workbench.view.extension.roo-code-ActivityBar")
})
it("should open sidebar when there is an active editor (user is working in this window)", async () => {
mockGetVisibleInstance.mockReturnValue(undefined)
;(vscode.window as any).activeTextEditor = { document: { fileName: "test.ts" } }
await focusPanel(undefined, undefined)
expect(mockExecuteCommand).toHaveBeenCalledWith("workbench.view.extension.roo-code-ActivityBar")
})
it("should open sidebar when there are visible editors (user is working in this window)", async () => {
mockGetVisibleInstance.mockReturnValue(undefined)
;(vscode.window as any).visibleTextEditors = [{ document: { fileName: "test.ts" } }]
await focusPanel(undefined, undefined)
expect(mockExecuteCommand).toHaveBeenCalledWith("workbench.view.extension.roo-code-ActivityBar")
})
it("should NOT open sidebar when no visible instance and no editors (multi-window scenario)", async () => {
mockGetVisibleInstance.mockReturnValue(undefined)
// No active editor and no visible editors (default state)
await focusPanel(undefined, undefined)
expect(mockExecuteCommand).not.toHaveBeenCalled()
})
it("should open sidebar when detection fails (fallback to existing behavior)", async () => {
mockGetVisibleInstance.mockImplementation(() => {
throw new Error("Test error")
})
await focusPanel(undefined, undefined)
expect(mockExecuteCommand).toHaveBeenCalledWith("workbench.view.extension.roo-code-ActivityBar")
})
})
})

View file

@ -15,8 +15,14 @@ export async function focusPanel(
const panel = tabPanel || sidebarPanel
if (!panel) {
// If no panel is open, open the sidebar
await vscode.commands.executeCommand(`workbench.view.extension.${Package.name}-ActivityBar`)
// Check if we should open the sidebar - avoid opening in multi-window scenarios
// where the user might be working in a different VS Code window
const shouldOpenSidebar = await shouldAllowSidebarActivation()
if (shouldOpenSidebar) {
// If no panel is open, open the sidebar
await vscode.commands.executeCommand(`workbench.view.extension.${Package.name}-ActivityBar`)
}
} else if (panel === tabPanel && !panel.active) {
// For tab panels, use reveal to focus
panel.reveal(vscode.ViewColumn.Active, false)
@ -25,3 +31,39 @@ export async function focusPanel(
await vscode.commands.executeCommand(`${ClineProvider.sideBarId}.focus`)
}
}
/**
* Determines if we should allow automatic sidebar activation
* This helps prevent unwanted sidebar opening in multi-window VS Code setups
* @returns Promise<boolean> - true if sidebar activation is allowed
*/
async function shouldAllowSidebarActivation(): Promise<boolean> {
try {
// Check if there's a visible Roo Code instance already
const visibleProvider = ClineProvider.getVisibleInstance()
if (visibleProvider) {
// If there's already a visible provider, it's safe to open the sidebar
return true
}
// Check if the current window has focus and is the active window
// This helps prevent opening sidebar when user is working in another window
const activeEditor = vscode.window.activeTextEditor
const visibleEditors = vscode.window.visibleTextEditors
// If there are active editors in this window, it's likely the user's current working window
if (activeEditor || visibleEditors.length > 0) {
return true
}
// If no editors are visible and no Roo Code instance is visible,
// be conservative and don't auto-open the sidebar to avoid disrupting
// the user's workflow in other windows
return false
} catch (error) {
// If there's any error in detection, err on the side of caution
// and allow sidebar activation to maintain existing functionality
console.warn("Error in shouldAllowSidebarActivation:", error)
return true
}
}