diff --git a/package.json b/package.json index 393fefe9e82..010b8463038 100644 --- a/package.json +++ b/package.json @@ -265,6 +265,43 @@ "group": "navigation@7", "when": "view == roo-cline.SidebarProvider" } + ], + "editor/title": [ + { + "command": "roo-cline.plusButtonClicked", + "group": "navigation@1", + "when": "activeWebviewPanelId == roo-cline.TabPanelProvider" + }, + { + "command": "roo-cline.promptsButtonClicked", + "group": "navigation@2", + "when": "activeWebviewPanelId == roo-cline.TabPanelProvider" + }, + { + "command": "roo-cline.mcpButtonClicked", + "group": "navigation@3", + "when": "activeWebviewPanelId == roo-cline.TabPanelProvider" + }, + { + "command": "roo-cline.historyButtonClicked", + "group": "navigation@4", + "when": "activeWebviewPanelId == roo-cline.TabPanelProvider" + }, + { + "command": "roo-cline.popoutButtonClicked", + "group": "navigation@5", + "when": "activeWebviewPanelId == roo-cline.TabPanelProvider" + }, + { + "command": "roo-cline.settingsButtonClicked", + "group": "navigation@6", + "when": "activeWebviewPanelId == roo-cline.TabPanelProvider" + }, + { + "command": "roo-cline.helpButtonClicked", + "group": "navigation@7", + "when": "activeWebviewPanelId == roo-cline.TabPanelProvider" + } ] }, "configuration": { diff --git a/src/activate/__tests__/registerCommands.test.ts b/src/activate/__tests__/registerCommands.test.ts new file mode 100644 index 00000000000..4dfa9a82c03 --- /dev/null +++ b/src/activate/__tests__/registerCommands.test.ts @@ -0,0 +1,54 @@ +jest.mock("vscode", () => ({ + CodeActionKind: { + QuickFix: { value: "quickfix" }, + RefactorRewrite: { value: "refactor.rewrite" }, + }, + window: { + createTextEditorDecorationType: jest.fn().mockReturnValue({ dispose: jest.fn() }), + }, +})) + +import * as vscode from "vscode" +import { ClineProvider } from "../../core/webview/ClineProvider" + +// Import the helper function from the actual file +import { getVisibleProviderOrLog } from "../registerCommands" + +jest.mock("../../core/webview/ClineProvider") + +describe("getVisibleProviderOrLog", () => { + let mockOutputChannel: vscode.OutputChannel + + beforeEach(() => { + mockOutputChannel = { + appendLine: jest.fn(), + append: jest.fn(), + clear: jest.fn(), + hide: jest.fn(), + name: "mock", + replace: jest.fn(), + show: jest.fn(), + dispose: jest.fn(), + } + jest.clearAllMocks() + }) + + it("returns the visible provider if found", () => { + const mockProvider = {} as ClineProvider + ;(ClineProvider.getVisibleInstance as jest.Mock).mockReturnValue(mockProvider) + + const result = getVisibleProviderOrLog(mockOutputChannel) + + expect(result).toBe(mockProvider) + expect(mockOutputChannel.appendLine).not.toHaveBeenCalled() + }) + + it("logs and returns undefined if no provider found", () => { + ;(ClineProvider.getVisibleInstance as jest.Mock).mockReturnValue(undefined) + + const result = getVisibleProviderOrLog(mockOutputChannel) + + expect(result).toBeUndefined() + expect(mockOutputChannel.appendLine).toHaveBeenCalledWith("Cannot find any visible Cline instances.") + }) +}) diff --git a/src/activate/registerCommands.ts b/src/activate/registerCommands.ts index 9559bbc596f..4af6b81c544 100644 --- a/src/activate/registerCommands.ts +++ b/src/activate/registerCommands.ts @@ -3,6 +3,18 @@ import delay from "delay" import { ClineProvider } from "../core/webview/ClineProvider" +/** + * Helper to get the visible ClineProvider instance or log if not found. + */ +export function getVisibleProviderOrLog(outputChannel: vscode.OutputChannel): ClineProvider | undefined { + const visibleProvider = ClineProvider.getVisibleInstance() + if (!visibleProvider) { + outputChannel.appendLine("Cannot find any visible Cline instances.") + return undefined + } + return visibleProvider +} + import { registerHumanRelayCallback, unregisterHumanRelayCallback, handleHumanRelayResponse } from "./humanRelay" import { handleNewTask } from "./handleTask" @@ -52,23 +64,33 @@ const getCommandsMap = ({ context, outputChannel, provider }: RegisterCommandOpt return { "roo-cline.activationCompleted": () => {}, "roo-cline.plusButtonClicked": async () => { - await provider.removeClineFromStack() - await provider.postStateToWebview() - await provider.postMessageToWebview({ type: "action", action: "chatButtonClicked" }) + const visibleProvider = getVisibleProviderOrLog(outputChannel) + if (!visibleProvider) return + await visibleProvider.removeClineFromStack() + await visibleProvider.postStateToWebview() + await visibleProvider.postMessageToWebview({ type: "action", action: "chatButtonClicked" }) }, "roo-cline.mcpButtonClicked": () => { - provider.postMessageToWebview({ type: "action", action: "mcpButtonClicked" }) + const visibleProvider = getVisibleProviderOrLog(outputChannel) + if (!visibleProvider) return + visibleProvider.postMessageToWebview({ type: "action", action: "mcpButtonClicked" }) }, "roo-cline.promptsButtonClicked": () => { - provider.postMessageToWebview({ type: "action", action: "promptsButtonClicked" }) + const visibleProvider = getVisibleProviderOrLog(outputChannel) + if (!visibleProvider) return + visibleProvider.postMessageToWebview({ type: "action", action: "promptsButtonClicked" }) }, "roo-cline.popoutButtonClicked": () => openClineInNewTab({ context, outputChannel }), "roo-cline.openInNewTab": () => openClineInNewTab({ context, outputChannel }), "roo-cline.settingsButtonClicked": () => { - provider.postMessageToWebview({ type: "action", action: "settingsButtonClicked" }) + const visibleProvider = getVisibleProviderOrLog(outputChannel) + if (!visibleProvider) return + visibleProvider.postMessageToWebview({ type: "action", action: "settingsButtonClicked" }) }, "roo-cline.historyButtonClicked": () => { - provider.postMessageToWebview({ type: "action", action: "historyButtonClicked" }) + const visibleProvider = getVisibleProviderOrLog(outputChannel) + if (!visibleProvider) return + visibleProvider.postMessageToWebview({ type: "action", action: "historyButtonClicked" }) }, "roo-cline.helpButtonClicked": () => { vscode.env.openExternal(vscode.Uri.parse("https://docs.roocode.com"))