-
Notifications
You must be signed in to change notification settings - Fork 926
Add dashboard browser setting for VS Code extension #14684
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 7 commits
1076d66
e857065
7fa2922
817e561
547ded9
40a9d76
41df6e7
e259c63
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -6,15 +6,18 @@ import { AspireResourceExtendedDebugConfiguration, AspireResourceDebugSession, E | |
| import { extensionLogOutputChannel } from "../utils/logging"; | ||
| import AspireDcpServer, { generateDcpIdPrefix } from "../dcp/AspireDcpServer"; | ||
| import { spawnCliProcess } from "./languages/cli"; | ||
| import { disconnectingFromSession, launchingWithAppHost, launchingWithDirectory, processExceptionOccurred, processExitedWithCode } from "../loc/strings"; | ||
| import { disconnectingFromSession, launchingWithAppHost, launchingWithDirectory, processExceptionOccurred, processExitedWithCode, aspireDashboard } from "../loc/strings"; | ||
| import { projectDebuggerExtension } from "./languages/dotnet"; | ||
| import AspireRpcServer from "../server/AspireRpcServer"; | ||
| import { createDebugSessionConfiguration } from "./debuggerExtensions"; | ||
| import { AspireTerminalProvider } from "../utils/AspireTerminalProvider"; | ||
| import { ICliRpcClient } from "../server/rpcClient"; | ||
| import path from "path"; | ||
| import os from "os"; | ||
| import { EnvironmentVariables } from "../utils/environment"; | ||
|
|
||
| export type DashboardBrowserType = 'openExternalBrowser' | 'debugChrome' | 'debugEdge' | 'debugFirefox'; | ||
|
|
||
| export class AspireDebugSession implements vscode.DebugAdapter { | ||
| private readonly _onDidSendMessage = new EventEmitter<any>(); | ||
| private _messageSeq = 1; | ||
|
|
@@ -28,6 +31,7 @@ export class AspireDebugSession implements vscode.DebugAdapter { | |
| private _resourceDebugSessions: AspireResourceDebugSession[] = []; | ||
| private _trackedDebugAdapters: string[] = []; | ||
| private _rpcClient?: ICliRpcClient; | ||
| private _dashboardDebugSession: vscode.DebugSession | null = null; | ||
| private readonly _disposables: vscode.Disposable[] = []; | ||
|
|
||
| public readonly onDidSendMessage = this._onDidSendMessage.event; | ||
|
|
@@ -280,6 +284,79 @@ export class AspireDebugSession implements vscode.DebugAdapter { | |
| }); | ||
| } | ||
|
|
||
| /** | ||
| * Opens the dashboard URL in the specified browser. | ||
| * For debugChrome/debugEdge/debugFirefox, launches as a child debug session that auto-closes with the Aspire debug session. | ||
| */ | ||
| async openDashboard(url: string, browserType: DashboardBrowserType): Promise<void> { | ||
| extensionLogOutputChannel.info(`Opening dashboard in browser: ${browserType}, URL: ${url}`); | ||
|
|
||
| switch (browserType) { | ||
| case 'debugChrome': | ||
| await this.launchDebugBrowser(url, 'pwa-chrome'); | ||
| break; | ||
|
|
||
| case 'debugEdge': | ||
| await this.launchDebugBrowser(url, 'pwa-msedge'); | ||
| break; | ||
|
|
||
| case 'debugFirefox': | ||
| await this.launchDebugBrowser(url, 'firefox'); | ||
| break; | ||
|
|
||
| case 'openExternalBrowser': | ||
| default: | ||
| // Use VS Code's default external browser handling | ||
| await vscode.env.openExternal(vscode.Uri.parse(url)); | ||
| break; | ||
| } | ||
| } | ||
|
|
||
| /** | ||
| * Launches a browser as a child debug session. | ||
| * The browser will automatically close when the parent Aspire debug session ends. | ||
| */ | ||
| private async launchDebugBrowser(url: string, debugType: 'pwa-chrome' | 'pwa-msedge' | 'firefox'): Promise<void> { | ||
| const debugConfig: vscode.DebugConfiguration = { | ||
| type: debugType, | ||
| name: aspireDashboard, | ||
| request: 'launch', | ||
| url: url, | ||
| }; | ||
|
|
||
| // Add type-specific options | ||
| if (debugType === 'pwa-chrome' || debugType === 'pwa-msedge') { | ||
| // Don't pause on entry for Chrome/Edge | ||
| debugConfig.pauseForSourceMap = false; | ||
| } | ||
| else if (debugType === 'firefox') { | ||
| // Firefox debugger requires webRoot; resolve to actual workspace path | ||
| debugConfig.webRoot = vscode.workspace.workspaceFolders?.[0]?.uri.fsPath ?? os.tmpdir(); | ||
| debugConfig.pathMappings = []; | ||
| } | ||
|
|
||
| // Register listener before starting so we don't miss the event | ||
| const disposable = vscode.debug.onDidStartDebugSession((session) => { | ||
| if (session.configuration.name === aspireDashboard && session.type === debugType) { | ||
| this._dashboardDebugSession = session; | ||
| disposable.dispose(); | ||
| } | ||
| }); | ||
|
|
||
| // Start as a child debug session - it will close when parent closes | ||
| const didStart = await vscode.debug.startDebugging( | ||
| undefined, | ||
| debugConfig, | ||
| this._session | ||
| ); | ||
|
|
||
| if (!didStart) { | ||
| disposable.dispose(); | ||
| extensionLogOutputChannel.warn(`Failed to start debug browser (${debugType}), falling back to default browser`); | ||
| await vscode.env.openExternal(vscode.Uri.parse(url)); | ||
| } | ||
|
adamint marked this conversation as resolved.
|
||
| } | ||
|
|
||
| dispose(): void { | ||
| extensionLogOutputChannel.info('Stopping the Aspire debug session'); | ||
| vscode.debug.stopDebugging(this._session); | ||
|
|
@@ -288,6 +365,36 @@ export class AspireDebugSession implements vscode.DebugAdapter { | |
| this._rpcClient?.stopCli(); | ||
| } | ||
|
|
||
| /** | ||
| * Closes the dashboard browser if closeDashboardOnDebugEnd is enabled. | ||
| * Handles closing debug browser sessions. | ||
| */ | ||
| private closeDashboard(): void { | ||
| const aspireConfig = vscode.workspace.getConfiguration('aspire'); | ||
| const shouldClose = aspireConfig.get<boolean>('closeDashboardOnDebugEnd', true); | ||
|
Comment on lines
+387
to
+388
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. In VS Code extensions I've written, I always end up creating a config class that reads the config and exposes property values the class as a service. Then you simply import the config class (and it implicitly reads from keys) - eliminating duplicated hardcoded strings throughout the code. This is an abstraction I'd strongly recommend.
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. That's a good idea! I will follow this up with a clean up |
||
|
|
||
| if (!shouldClose) { | ||
| this._dashboardDebugSession = null; | ||
| return; | ||
| } | ||
|
|
||
| extensionLogOutputChannel.info('Closing dashboard browser...'); | ||
|
|
||
| // For debug browsers, stop the debug session | ||
| if (this._dashboardDebugSession) { | ||
| vscode.debug.stopDebugging(this._dashboardDebugSession).then( | ||
| () => extensionLogOutputChannel.info('Dashboard debug session stopped.'), | ||
| (err) => extensionLogOutputChannel.warn(`Failed to stop dashboard debug session: ${err}`) | ||
| ); | ||
| this._dashboardDebugSession = null; | ||
| return; | ||
| } | ||
| // At this point there is no tracked dashboard debug session to stop. | ||
| // Any debug browser child sessions (debugChrome, debugEdge, debugFirefox) will | ||
| // automatically close when the parent Aspire session is stopped, so no further | ||
| // cleanup is required here. | ||
| } | ||
|
|
||
| private sendResponse(request: any, body: any = {}) { | ||
| this._onDidSendMessage.fire({ | ||
| type: 'response', | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is probably a dumb question, but why would a user want to debug the dashboard?
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It ties the lifetime of the dashboard window to the Aspire debug session by opening a controlled browser window. It's what VS does with the aspire dashboard
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does the user have to configure something themselves to get this to work? In VS it just happens AFAICT
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Currently yes because the default is to open the external browser. I could change the default to
debugChrome? But we don't know that Chrome is installed. What do you thinkThere was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What does VS do?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actually, dev kit has the same behavior we do (launching the external browser). I think we should keep that approach