Compare commits

...
Author SHA1 Message Date
celestial-vault b793d262ed you know what im talking about 2025-06-19 22:00:01 -07:00
5 changed files with 70 additions and 39 deletions
+3 -1
View File
@@ -51,7 +51,7 @@ https://github.com/KumarVariable/vscode-extension-sidebar-html/blob/master/src/c
*/
export class Controller {
readonly id: string = uuidv4()
readonly id: string
private postMessage: (message: ExtensionMessage) => Thenable<boolean> | undefined
private disposables: vscode.Disposable[] = []
@@ -65,7 +65,9 @@ export class Controller {
readonly context: vscode.ExtensionContext,
private readonly outputChannel: vscode.OutputChannel,
postMessage: (message: ExtensionMessage) => Thenable<boolean> | undefined,
id: string,
) {
this.id = id
this.outputChannel.appendLine("ClineProvider instantiated")
this.postMessage = postMessage
@@ -1,33 +1,33 @@
import { Controller } from "../index"
import { Empty } from "@shared/proto/common"
import { WebviewProviderType, WebviewProviderTypeRequest } from "@shared/proto/ui"
import { EmptyRequest } from "@shared/proto/common"
import { StreamingResponseHandler, getRequestRegistry } from "../grpc-handler"
// Track subscriptions with their provider type
const mcpButtonClickedSubscriptions = new Map<StreamingResponseHandler, WebviewProviderType>()
// Keep track of active mcpButtonClicked subscriptions by controller ID
const activeMcpButtonClickedSubscriptions = new Map<string, StreamingResponseHandler>()
/**
* Subscribe to mcpButtonClicked events
* @param controller The controller instance
* @param request The webview provider type request
* @param request The empty 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,
controller: Controller,
_request: EmptyRequest,
responseStream: StreamingResponseHandler,
requestId?: string,
): Promise<void> {
const providerType = request.providerType
console.log(`[DEBUG] set up mcpButtonClicked subscription for ${WebviewProviderType[providerType]} webview`)
const controllerId = controller.id
console.log(`[DEBUG] set up mcpButtonClicked subscription for controller ${controllerId}`)
// Store the subscription with its provider type
mcpButtonClickedSubscriptions.set(responseStream, providerType)
// Add this subscription to the active subscriptions with the controller ID
activeMcpButtonClickedSubscriptions.set(controllerId, responseStream)
// Register cleanup when the connection is closed
const cleanup = () => {
mcpButtonClickedSubscriptions.delete(responseStream)
activeMcpButtonClickedSubscriptions.delete(controllerId)
}
// Register the cleanup function with the request registry if we have a requestId
@@ -37,26 +37,27 @@ export async function subscribeToMcpButtonClicked(
}
/**
* Send a mcpButtonClicked event to active subscribers based on webview type
* @param webviewType The type of webview that triggered the event (SIDEBAR or TAB)
* Send a mcpButtonClicked event to a specific controller's subscription
* @param controllerId The ID of the controller to send the event to
*/
export async function sendMcpButtonClickedEvent(webviewType?: WebviewProviderType): Promise<void> {
const event = Empty.create({})
export async function sendMcpButtonClickedEvent(controllerId: string): Promise<void> {
// Get the subscription for this specific controller
const responseStream = activeMcpButtonClickedSubscriptions.get(controllerId)
// 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
}
if (!responseStream) {
console.error(`[DEBUG] No active subscription for controller ${controllerId}`)
return
}
try {
await responseStream(event, false)
} catch (error) {
console.error(`Error sending mcpButtonClicked event to ${WebviewProviderType[providerType]}:`, error)
mcpButtonClickedSubscriptions.delete(responseStream)
}
})
await Promise.all(promises)
try {
const event = Empty.create({})
await responseStream(
event,
false, // Not the last message
)
} catch (error) {
console.error(`Error sending mcpButtonClicked event to controller ${controllerId}:`, error)
// Remove the subscription if there was an error
activeMcpButtonClickedSubscriptions.delete(controllerId)
}
}
+14 -1
View File
@@ -28,7 +28,7 @@ export abstract class WebviewProvider {
WebviewProvider.activeInstances.add(this)
this.clientId = uuidv4()
WebviewProvider.clientIdMap.set(this, this.clientId)
this.controller = new Controller(context, outputChannel, (message) => this.postMessageToWebview(message))
this.controller = new Controller(context, outputChannel, (message) => this.postMessageToWebview(message), this.clientId)
}
// Add a method to get the client ID
@@ -58,6 +58,19 @@ export abstract class WebviewProvider {
return findLast(Array.from(this.activeInstances), (instance) => instance.isVisible() === true)
}
public static getActiveInstance(): WebviewProvider | undefined {
return Array.from(this.activeInstances).find((instance) => {
if (
instance.getWebview() &&
instance.getWebview().viewType === "claude-dev.TabPanelProvider" &&
"active" in instance.getWebview()
) {
return instance.getWebview().active === true
}
return false
})
}
public static getAllInstances(): WebviewProvider[] {
return Array.from(this.activeInstances)
}
+20 -6
View File
@@ -141,12 +141,26 @@ export async function activate(context: vscode.ExtensionContext) {
context.subscriptions.push(
vscode.commands.registerCommand("cline.mcpButtonClicked", (webview: any) => {
console.log("[DEBUG] mcpButtonClicked", webview)
// Pass the webview type to the event sender
const isSidebar = !webview
const webviewType = isSidebar ? WebviewProviderTypeEnum.SIDEBAR : WebviewProviderTypeEnum.TAB
// Will send to appropriate subscribers based on the source webview type
sendMcpButtonClickedEvent(webviewType)
const activeInstance = WebviewProvider.getActiveInstance()
const isSidebar = !webview
if (isSidebar) {
const sidebarInstance = WebviewProvider.getSidebarInstance()
const sidebarInstanceId = sidebarInstance?.getClientId()
if (sidebarInstanceId) {
sendMcpButtonClickedEvent(sidebarInstanceId)
} else {
console.error("[DEBUG] No sidebar instance found, cannot send MCP button event")
}
} else {
const activeInstanceId = activeInstance?.getClientId()
if (activeInstanceId) {
sendMcpButtonClickedEvent(activeInstanceId)
} else {
console.error("[DEBUG] No active instance found, cannot send MCP button event")
}
}
}),
)
@@ -628,7 +642,7 @@ export async function activate(context: vscode.ExtensionContext) {
} else {
// Create a temporary controller just for this operation
const outputChannel = vscode.window.createOutputChannel("Cline Commit Generator")
const tempController = new Controller(context, outputChannel, () => Promise.resolve(true))
const tempController = new Controller(context, outputChannel, () => Promise.resolve(true), uuidv4())
await tempController.generateGitCommitMessage()
outputChannel.dispose()
+2 -1
View File
@@ -11,13 +11,14 @@ import { addProtobusServices } from "@generated/standalone/server-setup"
import { StreamingResponseHandler } from "@/core/controller/grpc-handler"
import { ExternalHostBridgeClientManager } from "./host-bridge-client-manager"
import { ExternalWebviewProvider } from "./ExternalWebviewProvider"
import { v4 as uuidv4 } from "uuid"
async function main() {
log("Starting standalone service...")
hostProviders.initializeHostProviders(ExternalWebviewProvider.create, new ExternalHostBridgeClientManager())
activate(extensionContext)
const controller = new Controller(extensionContext, outputChannel, postMessage)
const controller = new Controller(extensionContext, outputChannel, postMessage, uuidv4())
const server = new grpc.Server()
// Set up health check.