diff --git a/packages/ui/src/lib/api/types.ts b/packages/ui/src/lib/api/types.ts index 8cc7278d..2d8b217b 100644 --- a/packages/ui/src/lib/api/types.ts +++ b/packages/ui/src/lib/api/types.ts @@ -619,6 +619,7 @@ export interface EditorAPI { export interface VSCodeAPI { executeCommand(command: string, ...args: unknown[]): Promise; openAgentManager(): Promise; + openExternalUrl(url: string): Promise; } export interface PushSubscribePayload { diff --git a/packages/ui/src/lib/url.ts b/packages/ui/src/lib/url.ts index 46ce97cd..b50774de 100644 --- a/packages/ui/src/lib/url.ts +++ b/packages/ui/src/lib/url.ts @@ -1,3 +1,5 @@ +import { getRegisteredRuntimeAPIs } from '@/contexts/runtimeAPIRegistry'; + /** * Utility for opening external URLs with Tauri shell support. * In desktop runtime, uses tauri.shell.open() for proper system browser handling. @@ -55,6 +57,16 @@ export const openExternalUrl = async (url: string): Promise => { const normalizedTarget = parsed.toString(); + const runtimeApis = getRegisteredRuntimeAPIs(); + if (runtimeApis?.runtime?.isVSCode && runtimeApis.vscode?.openExternalUrl) { + try { + await runtimeApis.vscode.openExternalUrl(normalizedTarget); + return true; + } catch { + return false; + } + } + const tauri = (window as unknown as { __TAURI__?: TauriShell }).__TAURI__; if (tauri?.shell?.open) { try { diff --git a/packages/vscode/src/bridge.ts b/packages/vscode/src/bridge.ts index 9dc59f8d..67798010 100644 --- a/packages/vscode/src/bridge.ts +++ b/packages/vscode/src/bridge.ts @@ -3139,6 +3139,21 @@ export async function handleBridgeMessage(message: BridgeRequest, ctx?: BridgeCo } } + case 'vscode:openExternalUrl': { + const { url } = (payload || {}) as { url?: string }; + const target = typeof url === 'string' ? url.trim() : ''; + if (!target) { + return { id, type, success: false, error: 'URL is required' }; + } + try { + await vscode.env.openExternal(vscode.Uri.parse(target)); + return { id, type, success: true, data: { opened: true } }; + } catch (error) { + const errorMessage = error instanceof Error ? error.message : String(error); + return { id, type, success: false, error: errorMessage }; + } + } + case 'notifications:can-notify': { return { id, type, success: true, data: true }; } diff --git a/packages/vscode/webview/api/bridge.ts b/packages/vscode/webview/api/bridge.ts index b4f39136..59375dec 100644 --- a/packages/vscode/webview/api/bridge.ts +++ b/packages/vscode/webview/api/bridge.ts @@ -138,6 +138,10 @@ export async function executeVSCodeCommand(command: string, args?: unknown[]): P return sendBridgeMessage<{ result?: unknown }>('vscode:command', { command, args }); } +export async function openVSCodeExternalUrl(url: string): Promise { + await sendBridgeMessage('vscode:openExternalUrl', { url }); +} + type CommandHandler = (payload: unknown) => void; const commandHandlers = new Map(); diff --git a/packages/vscode/webview/api/vscode.ts b/packages/vscode/webview/api/vscode.ts index 5e3232d0..1bcea35e 100644 --- a/packages/vscode/webview/api/vscode.ts +++ b/packages/vscode/webview/api/vscode.ts @@ -1,5 +1,5 @@ import type { VSCodeAPI } from '@openchamber/ui/lib/api/types'; -import { executeVSCodeCommand } from './bridge'; +import { executeVSCodeCommand, openVSCodeExternalUrl } from './bridge'; export const createVSCodeActionsAPI = (): VSCodeAPI => ({ async executeCommand(command: string, ...args: unknown[]): Promise { @@ -10,4 +10,8 @@ export const createVSCodeActionsAPI = (): VSCodeAPI => ({ async openAgentManager(): Promise { await executeVSCodeCommand('openchamber.openAgentManager'); }, + + async openExternalUrl(url: string): Promise { + await openVSCodeExternalUrl(url); + }, });