Compare commits

...

5 Commits

Author SHA1 Message Date
Elephant Lumps 310a8b1290 only send event to matching webview type 2025-06-01 20:48:58 -07:00
Elephant Lumps 40311c5613 Merge branch 'main' into migrate-mcpbuttonclicked-protobus 2025-06-01 14:07:36 -07:00
Elephant Lumps 2c99a43759 merge conflicts 2025-06-01 10:45:59 -07:00
Elephant Lumps 036a43dd08 changeset 2025-06-01 10:44:43 -07:00
Elephant Lumps b5aed71de6 migrate mcpButtonClicked 2025-06-01 10:44:19 -07:00
7 changed files with 123 additions and 26 deletions
+5
View File
@@ -0,0 +1,5 @@
---
"claude-dev": minor
---
Migrate mcpButtonClicked to protobus
+15
View File
@@ -6,6 +6,18 @@ option java_multiple_files = true;
import "common.proto";
// Enum for webview provider types
enum WebviewProviderType {
SIDEBAR = 0;
TAB = 1;
}
// Define a new message type for webview provider info
message WebviewProviderTypeRequest {
Metadata metadata = 1;
WebviewProviderType providerType = 2;
}
// UiService provides methods for managing UI interactions
service UiService {
// Scrolls to a specific settings section in the settings view
@@ -16,4 +28,7 @@ service UiService {
// Subscribe to addToInput events (when user adds content via context menu)
rpc subscribeToAddToInput(EmptyRequest) returns (stream String);
// Subscribe to MCP button clicked events
rpc subscribeToMcpButtonClicked(WebviewProviderTypeRequest) returns (stream Empty);
}
@@ -0,0 +1,62 @@
import { Controller } from "../index"
import { Empty } from "@shared/proto/common"
import { WebviewProviderType, WebviewProviderTypeRequest } from "@shared/proto/ui"
import { StreamingResponseHandler, getRequestRegistry } from "../grpc-handler"
// Track subscriptions with their provider type
const mcpButtonClickedSubscriptions = new Map<StreamingResponseHandler, WebviewProviderType>()
/**
* Subscribe to mcpButtonClicked events
* @param controller The controller instance
* @param request The webview provider type request
* @param responseStream The streaming response handler
* @param requestId The ID of the request (passed by the gRPC handler)
*/
export async function subscribeToMcpButtonClicked(
controller: Controller,
request: WebviewProviderTypeRequest,
responseStream: StreamingResponseHandler,
requestId?: string,
): Promise<void> {
const providerType = request.providerType
console.log(`[DEBUG] set up mcpButtonClicked subscription for ${WebviewProviderType[providerType]} webview`)
// Store the subscription with its provider type
mcpButtonClickedSubscriptions.set(responseStream, providerType)
// Register cleanup when the connection is closed
const cleanup = () => {
mcpButtonClickedSubscriptions.delete(responseStream)
}
// Register the cleanup function with the request registry if we have a requestId
if (requestId) {
getRequestRegistry().registerRequest(requestId, cleanup, { type: "mcpButtonClicked_subscription" }, responseStream)
}
}
/**
* Send a mcpButtonClicked event to active subscribers based on webview type
* @param webviewType The type of webview that triggered the event (SIDEBAR or TAB)
*/
export async function sendMcpButtonClickedEvent(webviewType?: WebviewProviderType): Promise<void> {
const event: Empty = {}
// Process all subscriptions, filtering based on the source
const promises = Array.from(mcpButtonClickedSubscriptions.entries()).map(async ([responseStream, providerType]) => {
// Only send to subscribers of the same type as the event source
if (webviewType !== providerType) {
return // Skip subscribers of different types
}
try {
await responseStream(event, false)
} catch (error) {
console.error(`Error sending mcpButtonClicked event to ${WebviewProviderType[providerType]}:`, error)
mcpButtonClickedSubscriptions.delete(responseStream)
}
})
await Promise.all(promises)
}
+8 -10
View File
@@ -11,10 +11,12 @@ import assert from "node:assert"
import { posthogClientProvider } from "./services/posthog/PostHogClientProvider"
import { WebviewProvider } from "./core/webview"
import { Controller } from "./core/controller"
import { sendMcpButtonClickedEvent } from "./core/controller/ui/subscribeToMcpButtonClicked"
import { ErrorService } from "./services/error/ErrorService"
import { initializeTestMode, cleanupTestMode } from "./services/test/TestMode"
import { telemetryService } from "./services/posthog/telemetry/TelemetryService"
import { v4 as uuidv4 } from "uuid"
import { WebviewProviderType as WebviewProviderTypeEnum } from "@shared/proto/ui"
import { WebviewProviderType } from "./shared/webview/types"
/*
Built using https://github.com/microsoft/vscode-webview-ui-toolkit
@@ -107,17 +109,13 @@ export async function activate(context: vscode.ExtensionContext) {
context.subscriptions.push(
vscode.commands.registerCommand("cline.mcpButtonClicked", (webview: any) => {
const openMcp = (instance?: WebviewProvider) =>
instance?.controller.postMessageToWebview({
type: "action",
action: "mcpButtonClicked",
})
console.log("[DEBUG] mcpButtonClicked", webview)
// Pass the webview type to the event sender
const isSidebar = !webview
if (isSidebar) {
openMcp(WebviewProvider.getSidebarInstance())
} else {
WebviewProvider.getTabInstances().forEach(openMcp)
}
const webviewType = isSidebar ? WebviewProviderTypeEnum.SIDEBAR : WebviewProviderTypeEnum.TAB
// Will send to appropriate subscribers based on the source webview type
sendMcpButtonClickedEvent(webviewType)
}),
)
-1
View File
@@ -40,7 +40,6 @@ export interface ExtensionMessage {
text?: string
action?:
| "chatButtonClicked"
| "mcpButtonClicked"
| "settingsButtonClicked"
| "historyButtonClicked"
| "didBecomeVisible"
-5
View File
@@ -47,11 +47,6 @@ const AppContent = () => {
}
}, [shouldShowAnnouncement])
useEffect(() => {
const providerType = window.WEBVIEW_PROVIDER_TYPE || WebviewProviderType.TAB
console.log("[DEBUG] webviewProviderType", providerType)
}, [])
if (!didHydrateState) {
return null
}
@@ -1,12 +1,14 @@
import React, { createContext, useCallback, useContext, useEffect, useRef, useState } from "react"
import { useEvent } from "react-use"
import { StateServiceClient, UiServiceClient, ModelsServiceClient } from "../services/grpc-client"
import { EmptyRequest } from "@shared/proto/common"
import { WebviewProviderType as WebviewProviderTypeEnum, WebviewProviderTypeRequest } from "@shared/proto/ui"
import { DEFAULT_AUTO_APPROVAL_SETTINGS } from "@shared/AutoApprovalSettings"
import { DEFAULT_BROWSER_SETTINGS } from "@shared/BrowserSettings"
import { ChatSettings, DEFAULT_CHAT_SETTINGS } from "@shared/ChatSettings"
import { DEFAULT_PLATFORM, ExtensionMessage, ExtensionState } from "@shared/ExtensionMessage"
import { TelemetrySetting } from "@shared/TelemetrySetting"
import { findLastIndex } from "@shared/array"
import { EmptyRequest } from "@shared/proto/common"
import React, { createContext, useCallback, useContext, useEffect, useRef, useState } from "react"
import { useEvent } from "react-use"
import {
ApiConfiguration,
ModelInfo,
@@ -16,7 +18,6 @@ import {
requestyDefaultModelInfo,
} from "../../../src/shared/api"
import { McpMarketplaceCatalog, McpServer, McpViewTab } from "../../../src/shared/mcp"
import { ModelsServiceClient, StateServiceClient } from "../services/grpc-client"
import { convertTextMateToHljs } from "../utils/textMateToHljs"
import { vscode } from "../utils/vscode"
@@ -192,9 +193,6 @@ export const ExtensionStateContextProvider: React.FC<{
switch (message.type) {
case "action": {
switch (message.action!) {
case "mcpButtonClicked":
navigateToMcp(message.tab)
break
case "settingsButtonClicked":
navigateToSettings()
break
@@ -271,10 +269,11 @@ export const ExtensionStateContextProvider: React.FC<{
useEvent("message", handleMessage)
// Reference to store the state subscription cancellation function
// References to store subscription cancellation functions
const stateSubscriptionRef = useRef<(() => void) | null>(null)
const mcpButtonUnsubscribeRef = useRef<(() => void) | null>(null)
// Subscribe to state updates using the new gRPC streaming API
// Subscribe to state updates and UI events using the gRPC streaming API
useEffect(() => {
// Set up state subscription
stateSubscriptionRef.current = StateServiceClient.subscribeToState(EmptyRequest.create({}), {
@@ -346,15 +345,39 @@ export const ExtensionStateContextProvider: React.FC<{
},
})
// Subscribe to MCP button clicked events with webview type
mcpButtonUnsubscribeRef.current = UiServiceClient.subscribeToMcpButtonClicked(
WebviewProviderTypeRequest.create({
providerType:
window.WEBVIEW_PROVIDER_TYPE === "sidebar" ? WebviewProviderTypeEnum.SIDEBAR : WebviewProviderTypeEnum.TAB,
}),
{
onResponse: () => {
console.log("[DEBUG] Received mcpButtonClicked event from gRPC stream")
navigateToMcp()
},
onError: (error) => {
console.error("Error in mcpButtonClicked subscription:", error)
},
onComplete: () => {
console.log("mcpButtonClicked subscription completed")
},
},
)
// Still send the webviewDidLaunch message for other initialization
vscode.postMessage({ type: "webviewDidLaunch" })
// Clean up subscription when component unmounts
// Clean up subscriptions when component unmounts
return () => {
if (stateSubscriptionRef.current) {
stateSubscriptionRef.current()
stateSubscriptionRef.current = null
}
if (mcpButtonUnsubscribeRef.current) {
mcpButtonUnsubscribeRef.current()
mcpButtonUnsubscribeRef.current = null
}
}
}, [])