Skip to content

Commit c6e7b52

Browse files
celestial-vaultElephant Lumps
andauthored
Migrate settingsButtonClicked protobus (RooCodeInc#3976)
* migrate settingsButtonClicked * changeset * add provider type filtering --------- Co-authored-by: Elephant Lumps <[email protected]>
1 parent 9dcb82f commit c6e7b52

File tree

6 files changed

+103
-24
lines changed

6 files changed

+103
-24
lines changed

.changeset/gorgeous-pugs-cover.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"claude-dev": minor
3+
---
4+
5+
Migrate settingsButtonClicked to protobus

proto/ui.proto

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,4 +40,7 @@ service UiService {
4040

4141
// Subscribe to account button click events
4242
rpc subscribeToAccountButtonClicked(EmptyRequest) returns (stream Empty);
43+
44+
// Subscribe to settings button clicked events
45+
rpc subscribeToSettingsButtonClicked(WebviewProviderTypeRequest) returns (stream Empty);
4346
}
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
import { Empty } from "@shared/proto/common"
2+
import { WebviewProviderType, WebviewProviderTypeRequest } from "@shared/proto/ui"
3+
import { StreamingResponseHandler, getRequestRegistry } from "../grpc-handler"
4+
import type { Controller } from "../index"
5+
6+
// Track subscriptions with their provider type
7+
const subscriptions = new Map<StreamingResponseHandler, WebviewProviderType>()
8+
9+
/**
10+
* Subscribe to settings button clicked events
11+
* @param controller The controller instance
12+
* @param request The request with provider type
13+
* @param responseStream The streaming response handler
14+
* @param requestId The ID of the request (passed by the gRPC handler)
15+
*/
16+
export async function subscribeToSettingsButtonClicked(
17+
controller: Controller,
18+
request: WebviewProviderTypeRequest,
19+
responseStream: StreamingResponseHandler,
20+
requestId?: string,
21+
): Promise<void> {
22+
const providerType = request.providerType
23+
console.log(`[DEBUG] set up settings button subscription for ${WebviewProviderType[providerType]} webview`)
24+
25+
// Store the subscription with its provider type
26+
subscriptions.set(responseStream, providerType)
27+
28+
// Register cleanup when the connection is closed
29+
const cleanup = () => {
30+
subscriptions.delete(responseStream)
31+
}
32+
33+
// Register the cleanup function with the request registry if we have a requestId
34+
if (requestId) {
35+
getRequestRegistry().registerRequest(requestId, cleanup, { type: "settings_button_clicked_subscription" }, responseStream)
36+
}
37+
}
38+
39+
/**
40+
* Send a settings button clicked event to active subscribers of matching provider type
41+
* @param webviewType The type of webview that triggered the event
42+
*/
43+
export async function sendSettingsButtonClickedEvent(webviewType?: WebviewProviderType): Promise<void> {
44+
// Process all subscriptions, filtering based on the source
45+
const promises = Array.from(subscriptions.entries()).map(async ([responseStream, providerType]) => {
46+
// If webviewType is provided, only send to subscribers of the same type
47+
if (webviewType !== undefined && webviewType !== providerType) {
48+
return // Skip subscribers of different types
49+
}
50+
51+
try {
52+
const event = Empty.create({})
53+
await responseStream(event, false) // Not the last message
54+
} catch (error) {
55+
console.error(`Error sending settings button clicked event to ${WebviewProviderType[providerType]}:`, error)
56+
subscriptions.delete(responseStream)
57+
}
58+
})
59+
60+
await Promise.all(promises)
61+
}

src/extension.ts

Lines changed: 5 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import { sendChatButtonClickedEvent } from "./core/controller/ui/subscribeToChat
1616
import { ErrorService } from "./services/error/ErrorService"
1717
import { initializeTestMode, cleanupTestMode } from "./services/test/TestMode"
1818
import { telemetryService } from "./services/posthog/telemetry/TelemetryService"
19+
import { sendSettingsButtonClickedEvent } from "./core/controller/ui/subscribeToSettingsButtonClicked"
1920
import { v4 as uuidv4 } from "uuid"
2021
import { WebviewProviderType as WebviewProviderTypeEnum } from "@shared/proto/ui"
2122
import { WebviewProviderType } from "./shared/webview/types"
@@ -169,20 +170,10 @@ export async function activate(context: vscode.ExtensionContext) {
169170

170171
context.subscriptions.push(
171172
vscode.commands.registerCommand("cline.settingsButtonClicked", (webview: any) => {
172-
WebviewProvider.getAllInstances().forEach((instance) => {
173-
const openSettings = async (instance?: WebviewProvider) => {
174-
instance?.controller.postMessageToWebview({
175-
type: "action",
176-
action: "settingsButtonClicked",
177-
})
178-
}
179-
const isSidebar = !webview
180-
if (isSidebar) {
181-
openSettings(WebviewProvider.getSidebarInstance())
182-
} else {
183-
WebviewProvider.getTabInstances().forEach(openSettings)
184-
}
185-
})
173+
const isSidebar = !webview
174+
const webviewType = isSidebar ? WebviewProviderTypeEnum.SIDEBAR : WebviewProviderTypeEnum.TAB
175+
176+
sendSettingsButtonClickedEvent(webviewType)
186177
}),
187178
)
188179

src/shared/ExtensionMessage.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ export interface ExtensionMessage {
3838
| "fileSearchResults"
3939
| "grpc_response" // New type for gRPC responses
4040
text?: string
41-
action?: "settingsButtonClicked" | "didBecomeVisible" | "accountLogoutClicked" | "focusChatInput"
41+
action?: "didBecomeVisible" | "accountLogoutClicked" | "focusChatInput"
4242
state?: ExtensionState
4343
images?: string[]
4444
files?: string[]

webview-ui/src/context/ExtensionStateContext.tsx

Lines changed: 28 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
import React, { createContext, useCallback, useContext, useEffect, useRef, useState } from "react"
22
import { useEvent } from "react-use"
3-
import { StateServiceClient, ModelsServiceClient, UiServiceClient } from "../services/grpc-client"
43
import { EmptyRequest } from "@shared/proto/common"
54
import { WebviewProviderType as WebviewProviderTypeEnum, WebviewProviderTypeRequest } from "@shared/proto/ui"
65
import { DEFAULT_AUTO_APPROVAL_SETTINGS } from "@shared/AutoApprovalSettings"
@@ -18,6 +17,7 @@ import {
1817
requestyDefaultModelInfo,
1918
} from "../../../src/shared/api"
2019
import { McpMarketplaceCatalog, McpServer, McpViewTab } from "../../../src/shared/mcp"
20+
import { ModelsServiceClient, StateServiceClient, UiServiceClient } from "../services/grpc-client"
2121
import { convertTextMateToHljs } from "../utils/textMateToHljs"
2222
import { vscode } from "../utils/vscode"
2323

@@ -90,6 +90,9 @@ const ExtensionStateContext = createContext<ExtensionStateContextType | undefine
9090
export const ExtensionStateContextProvider: React.FC<{
9191
children: React.ReactNode
9292
}> = ({ children }) => {
93+
// Get the current webview provider type
94+
const currentProviderType =
95+
window.WEBVIEW_PROVIDER_TYPE === "sidebar" ? WebviewProviderTypeEnum.SIDEBAR : WebviewProviderTypeEnum.TAB
9396
// UI view state
9497
const [showMcp, setShowMcp] = useState(false)
9598
const [mcpTab, setMcpTab] = useState<McpViewTab | undefined>(undefined)
@@ -193,14 +196,6 @@ export const ExtensionStateContextProvider: React.FC<{
193196
const handleMessage = useCallback((event: MessageEvent) => {
194197
const message: ExtensionMessage = event.data
195198
switch (message.type) {
196-
case "action": {
197-
switch (message.action!) {
198-
case "settingsButtonClicked":
199-
navigateToSettings()
200-
break
201-
}
202-
break
203-
}
204199
case "theme": {
205200
if (message.text) {
206201
setTheme(convertTextMateToHljs(JSON.parse(message.text)))
@@ -268,6 +263,7 @@ export const ExtensionStateContextProvider: React.FC<{
268263
const historyButtonClickedSubscriptionRef = useRef<(() => void) | null>(null)
269264
const chatButtonUnsubscribeRef = useRef<(() => void) | null>(null)
270265
const accountButtonClickedSubscriptionRef = useRef<(() => void) | null>(null)
266+
const settingsButtonClickedSubscriptionRef = useRef<(() => void) | null>(null)
271267

272268
// Subscribe to state updates and UI events using the gRPC streaming API
273269
useEffect(() => {
@@ -397,6 +393,25 @@ export const ExtensionStateContextProvider: React.FC<{
397393
onComplete: () => {},
398394
})
399395

396+
// Set up settings button clicked subscription
397+
settingsButtonClickedSubscriptionRef.current = UiServiceClient.subscribeToSettingsButtonClicked(
398+
WebviewProviderTypeRequest.create({
399+
providerType: currentProviderType,
400+
}),
401+
{
402+
onResponse: () => {
403+
// When settings button is clicked, navigate to settings
404+
navigateToSettings()
405+
},
406+
onError: (error) => {
407+
console.error("Error in settings button clicked subscription:", error)
408+
},
409+
onComplete: () => {
410+
console.log("Settings button clicked subscription completed")
411+
},
412+
},
413+
)
414+
400415
// Still send the webviewDidLaunch message for other initialization
401416
vscode.postMessage({ type: "webviewDidLaunch" })
402417

@@ -437,6 +452,10 @@ export const ExtensionStateContextProvider: React.FC<{
437452
accountButtonClickedSubscriptionRef.current()
438453
accountButtonClickedSubscriptionRef.current = null
439454
}
455+
if (settingsButtonClickedSubscriptionRef.current) {
456+
settingsButtonClickedSubscriptionRef.current()
457+
settingsButtonClickedSubscriptionRef.current = null
458+
}
440459
}
441460
}, [])
442461

0 commit comments

Comments
 (0)