diff --git a/.changeset/five-sheep-count.md b/.changeset/five-sheep-count.md new file mode 100644 index 0000000000..3ad45bf471 --- /dev/null +++ b/.changeset/five-sheep-count.md @@ -0,0 +1,5 @@ +--- +"claude-dev": patch +--- + +openInBrowser protobus migration diff --git a/proto/web.proto b/proto/web.proto index 3debacbc30..0df3b6e8e5 100644 --- a/proto/web.proto +++ b/proto/web.proto @@ -9,6 +9,7 @@ import "common.proto"; service WebService { rpc checkIsImageUrl(StringRequest) returns (IsImageUrl); rpc fetchOpenGraphData(StringRequest) returns (OpenGraphData); + rpc openInBrowser(StringRequest) returns (Empty); } message IsImageUrl { diff --git a/src/core/controller/index.ts b/src/core/controller/index.ts index 2b318fbcc3..9cf8b45e9d 100644 --- a/src/core/controller/index.ts +++ b/src/core/controller/index.ts @@ -303,11 +303,6 @@ export class Controller { text: message.text, }) break - case "openInBrowser": - if (message.url) { - vscode.env.openExternal(vscode.Uri.parse(message.url)) - } - break case "fetchUserCreditsData": { await this.fetchUserCreditsData() break diff --git a/src/core/controller/web/openInBrowser.ts b/src/core/controller/web/openInBrowser.ts new file mode 100644 index 0000000000..e31d86d602 --- /dev/null +++ b/src/core/controller/web/openInBrowser.ts @@ -0,0 +1,21 @@ +import * as vscode from "vscode" +import { Controller } from ".." +import { Empty, StringRequest } from "../../../shared/proto/common" + +/** + * Opens a URL in the user's default browser + * @param controller The controller instance + * @param request The URL to open + * @returns Empty response since the client doesn't need a return value + */ +export async function openInBrowser(controller: Controller, request: StringRequest): Promise { + try { + if (request.value) { + await vscode.env.openExternal(vscode.Uri.parse(request.value)) + } + return Empty.create() + } catch (error) { + console.error("Error opening URL in browser:", error) + return Empty.create() + } +} diff --git a/src/shared/WebviewMessage.ts b/src/shared/WebviewMessage.ts index de0adb7c36..4ea2a4dc7b 100644 --- a/src/shared/WebviewMessage.ts +++ b/src/shared/WebviewMessage.ts @@ -13,7 +13,6 @@ export interface WebviewMessage { | "newTask" | "condense" | "reportBug" - | "openInBrowser" | "showChatView" | "requestVsCodeLmModels" | "authStateChanged" @@ -54,8 +53,6 @@ export interface WebviewMessage { // For auth user?: UserInfo | null customToken?: string - // For openInBrowser - url?: string planActSeparateModelsSetting?: boolean enableCheckpointsSetting?: boolean mcpMarketplaceEnabled?: boolean diff --git a/webview-ui/src/components/mcp/chat-display/ImagePreview.tsx b/webview-ui/src/components/mcp/chat-display/ImagePreview.tsx index bbe7ae605f..5cf70cbf79 100644 --- a/webview-ui/src/components/mcp/chat-display/ImagePreview.tsx +++ b/webview-ui/src/components/mcp/chat-display/ImagePreview.tsx @@ -1,5 +1,6 @@ import React from "react" import { vscode } from "@/utils/vscode" +import { WebServiceClient } from "@/services/grpc-client" import DOMPurify from "dompurify" import { getSafeHostname, formatUrlForOpening, checkIfImageUrl } from "./utils/mcpRichUtil" import ChatErrorBoundary from "@/components/chat/ChatErrorBoundary" @@ -232,11 +233,14 @@ class ImagePreview extends React.Component< borderRadius: "4px", color: "var(--vscode-errorForeground)", }} - onClick={() => { - vscode.postMessage({ - type: "openInBrowser", - url: DOMPurify.sanitize(url), - }) + onClick={async () => { + try { + await WebServiceClient.openInBrowser({ + value: DOMPurify.sanitize(url), + }) + } catch (err) { + console.error("Error opening URL in browser:", err) + } }}>
Failed to load image
{getSafeHostname(url)}
@@ -256,11 +260,14 @@ class ImagePreview extends React.Component< maxWidth: "100%", cursor: "pointer", }} - onClick={() => { - vscode.postMessage({ - type: "openInBrowser", - url: DOMPurify.sanitize(formatUrlForOpening(url)), - }) + onClick={async () => { + try { + await WebServiceClient.openInBrowser({ + value: DOMPurify.sanitize(formatUrlForOpening(url)), + }) + } catch (err) { + console.error("Error opening URL in browser:", err) + } }}> {/\.svg(\?.*)?$/i.test(url) ? ( // Special handling for SVG images diff --git a/webview-ui/src/components/mcp/chat-display/LinkPreview.tsx b/webview-ui/src/components/mcp/chat-display/LinkPreview.tsx index 0e408590b5..232a7898e4 100644 --- a/webview-ui/src/components/mcp/chat-display/LinkPreview.tsx +++ b/webview-ui/src/components/mcp/chat-display/LinkPreview.tsx @@ -238,11 +238,14 @@ class LinkPreview extends React.Component { maxWidth: "512px", overflow: "auto", }} - onClick={() => { - vscode.postMessage({ - type: "openInBrowser", - url: DOMPurify.sanitize(url), - }) + onClick={async () => { + try { + await WebServiceClient.openInBrowser({ + value: DOMPurify.sanitize(url), + }) + } catch (err) { + console.error("Error opening URL in browser:", err) + } }}>
{errorDisplay}
{getSafeHostname(url)}
@@ -275,11 +278,14 @@ class LinkPreview extends React.Component { height: "128px", maxWidth: "512px", }} - onClick={() => { - vscode.postMessage({ - type: "openInBrowser", - url: DOMPurify.sanitize(url), - }) + onClick={async () => { + try { + await WebServiceClient.openInBrowser({ + value: DOMPurify.sanitize(url), + }) + } catch (err) { + console.error("Error opening URL in browser:", err) + } }}> {data.image && (