Skip to content

Copy AppHost path to clipboard when clicking the Path tree item (#18578)#18621

Draft
adamint wants to merge 8 commits into
microsoft:mainfrom
adamint:adamint-copy-apphost-path-18578
Draft

Copy AppHost path to clipboard when clicking the Path tree item (#18578)#18621
adamint wants to merge 8 commits into
microsoft:mainfrom
adamint:adamint-copy-apphost-path-18578

Conversation

@adamint

@adamint adamint commented Jul 2, 2026

Copy link
Copy Markdown
Member

Description

In the Aspire: AppHosts view, each idle AppHost has a Path child row that shows the AppHost path. Clicking it did nothing, which is surprising — the obvious expectation when you click a path is that it copies. The aspire-vscode.copyAppHostPath command already existed (it's the right-click "Copy path" action), so this just wires the row's command to that same handler.

Per @adamint's note on the issue, copying now also shows a confirmation notification ("AppHost path copied to clipboard."). Because the click and the existing right-click action route through the same handler, both paths get the notification.

Scoped to the Path item only — the running-AppHost tree (which swaps the Path row for the resources view) and the other copy handlers (endpoint URL, log file, resource name) are untouched.

Fixes #18578

User-facing usage

  1. Open the Aspire panel → AppHosts view.
  2. Expand an idle (not-yet-running) AppHost.
  3. Click the Path row.
  4. The AppHost path is copied to the clipboard and a notification confirms it.

The right-click Copy path context-menu action behaves the same and now also shows the confirmation.

Screenshots / Recordings

This PR includes UI changes. Please add screenshots or screen recordings so reviewers can evaluate the visual changes without running locally.

  • For before/after comparisons, place them side-by-side or label them clearly.
  • For interactive changes (animations, transitions, new flows), prefer a short screen recording (GIF or video).
  • If you cannot capture visuals now, note what scenario to test and mark this section as TODO.

Visuals weren't captured here — this was implemented in a headless automation environment without an interactive VS Code UI, so there's no good way to grab a real screenshot/recording of the notification. TODO before merge: capture a short clip of clicking the Path row and the "AppHost path copied to clipboard." notification appearing. Closest evidence in the meantime:

  • New E2E test clicking the Path tree item copies the AppHost path and shows a confirmation notification drives the actual tree: expands the idle AppHost, clicks the Path row, waits for the notification, and asserts the clipboard matches the AppHost path.
  • Unit tests assert the Path item's command is aspire-vscode.copyAppHostPath with the parent AppHost as its argument, and that the handler writes the clipboard + shows the confirmation (and shows a warning without copying when the path is missing).

Validation

  • extension/build.sh (CLI + extension webpack build): succeeded.
  • yarn run lint, yarn run compile-tests, yarn run compile-e2e: all clean.
  • Unit tests (appHostTreeView.test.ts): pass, including the updated command-wiring assertion and the two new copyAppHostPath tests.
  • E2E test: type-checks (compile-e2e) but was not executed — the full E2E harness is heavy and wasn't run in this environment. Needs a run before merge.

Checklist

  • Is this feature complete?
    • Yes. Ready to ship.
    • No. Follow-up changes expected.
  • Are you including unit tests for the changes and scenario tests if relevant?
    • Yes
    • No
  • Did you add public API?
    • Yes
      • If yes, did you have an API Review for it?
        • Yes
        • No
      • Did you add <remarks /> and <code /> elements on your triple slash comments?
        • Yes
        • No
    • No
  • Does the change make any security assumptions or guarantees?
    • Yes
      • If yes, have you done a threat model and had a security review?
        • Yes
        • No
    • No

Copilot AI review requested due to automatic review settings July 2, 2026 19:31
@github-actions

github-actions Bot commented Jul 2, 2026

Copy link
Copy Markdown
Contributor

🚀 Dogfood this PR with:

⚠️ WARNING: Do not do this without first carefully reviewing the code of this PR to satisfy yourself it is safe.

curl -fsSL https://raw.githubusercontent.com/microsoft/aspire/main/eng/scripts/get-aspire-cli-pr.sh | bash -s -- 18621

Or

  • Run remotely in PowerShell:
iex "& { $(irm https://raw.githubusercontent.com/microsoft/aspire/main/eng/scripts/get-aspire-cli-pr.ps1) } 18621"

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Wires the idle AppHost Path tree item in the Aspire VS Code extension to the existing aspire-vscode.copyAppHostPath command so clicking the row copies the AppHost path, and adds a confirmation notification after copying.

Changes:

  • Adds a command to WorkspaceAppHostPathItem so clicking Path triggers aspire-vscode.copyAppHostPath.
  • Updates copyAppHostPath to show an information notification after successfully writing to the clipboard.
  • Adds unit and E2E coverage for clicking the Path item and for the copy command behavior.
Show a summary per file
File Description
extension/src/views/AspireAppHostTreeProvider.ts Wires the Path tree row to the copy-path command and shows a confirmation toast on success.
extension/src/test/appHostTreeView.test.ts Updates tree-item command assertions and adds unit tests for copyAppHostPath clipboard + notification behavior.
extension/src/test-e2e/appHostTree.e2e.test.ts Adds E2E coverage for clicking the Path row and verifying notification + clipboard contents.
extension/src/loc/strings.ts Adds the new localized string for the copy confirmation message.

Review details

  • Files reviewed: 4/4 changed files
  • Comments generated: 2
  • Review effort level: Low

Comment on lines 121 to 125
export const appHostRunActionLabel = vscode.l10n.t('Run AppHost');
export const appHostDebugActionLabel = vscode.l10n.t('Debug AppHost');
export const appHostPathLabel = vscode.l10n.t('Path');
export const appHostPathCopiedToClipboard = vscode.l10n.t('AppHost path copied to clipboard.');
export const appHostStartingDescription = vscode.l10n.t('Starting...');
Comment on lines 1552 to +1559
async copyAppHostPath(element: AppHostItem | WorkspaceResourcesItem | WorkspaceAppHostItem): Promise<void> {
const appHostPath = element instanceof AppHostItem ? element.appHost.appHostPath : element.appHostPath;
if (!appHostPath) {
vscode.window.showWarningMessage(appHostSourceNotFound);
return;
}
await vscode.env.clipboard.writeText(appHostPath);
vscode.window.showInformationMessage(appHostPathCopiedToClipboard);
…osoft#18578)

The Path row in the Aspire: AppHosts view previously did nothing when
clicked. Wire its command to the existing aspire-vscode.copyAppHostPath
handler so a click copies the AppHost path, and show a confirmation
notification after copying (per the issue owner's request). The
notification also covers the existing right-click 'Copy path' action
since both route through the same handler.

Adds unit tests for the command wiring and the copy/notification
behavior, plus an E2E test that clicks the Path item and verifies the
clipboard contents and confirmation notification.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
@adamint adamint force-pushed the adamint-copy-apphost-path-18578 branch from 8e8f3a4 to 07b598b Compare July 2, 2026 19:41
@adamint

adamint commented Jul 2, 2026

Copy link
Copy Markdown
Member Author

One test cleanup thing before this is ready: the new unit/E2E tests use the real VS Code clipboard but don't restore the previous value afterward. Could we save the old clipboard value and restore it in teardown? That should keep local runs and later clipboard assertions from inheriting the AppHost path.

Save and restore the existing VS Code clipboard value around the AppHost path clipboard tests so they do not leak test state into later tests or the user environment. Add a minimal typed E2E writeClipboard bridge to pair with readClipboard for teardown restoration.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings July 2, 2026 21:11

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review details

  • Files reviewed: 7/7 changed files
  • Comments generated: 0 new
  • Review effort level: Medium

Comment thread extension/src/test-e2e/appHostTree.e2e.test.ts Outdated
Avoid sending clipboard snapshot contents through the VS Code extension E2E state/control JSON bridge. Snapshot and restore the clipboard in the extension host, and assert copied clipboard values in-host instead of returning raw clipboard contents.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Comment thread extension/src/testing/e2eStateFileBridge.ts
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings July 2, 2026 22:24

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review details

  • Files reviewed: 9/9 changed files
  • Comments generated: 2
  • Review effort level: Medium

export const appHostRunActionLabel = vscode.l10n.t('Run AppHost');
export const appHostDebugActionLabel = vscode.l10n.t('Debug AppHost');
export const appHostPathLabel = vscode.l10n.t('Path');
export const appHostPathCopiedToClipboard = vscode.l10n.t('AppHost path copied to clipboard.');
import * as assert from 'assert';
import { getCommandInvocationCount, getResources, getTerminalCommandCount, getTreeAppHostLabel, waitForCommandOutcome, waitForDashboardUrl, waitForNoDebugSessions, waitForNoRunningAppHost, waitForRepositoryIdle, waitForResource, waitForRunningAppHost, waitForTerminalCommand, waitForWorkspaceAppHost } from './helpers/assertions';
import { executeE2eControlCommand, restoreWorkspaceCliPath, runE2eTeardown, setCliUnavailableForE2E, setTerminalCommandExecutionSuppressedForE2E, stopAppHostIfRunning, stopPrimaryAppHostIfRunning } from './helpers/fixtures';
import { getCommandInvocationCount, getResources, getTerminalCommandCount, getTreeAppHostLabel, isSamePath, waitForCommandOutcome, waitForDashboardUrl, waitForNoDebugSessions, waitForNoRunningAppHost, waitForRepositoryIdle, waitForResource, waitForRunningAppHost, waitForTerminalCommand, waitForWorkspaceAppHost } from './helpers/assertions';
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Comment thread extension/src/test-e2e/treeActions.e2e.test.ts
Comment thread extension/src/test-e2e/helpers/fixtures.ts Outdated
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings July 2, 2026 22:59

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review details

  • Files reviewed: 11/11 changed files
  • Comments generated: 1
  • Review effort level: Medium

import * as assert from 'assert';
import { getCommandInvocationCount, getResources, getTerminalCommandCount, getTreeAppHostLabel, waitForCommandOutcome, waitForDashboardUrl, waitForNoDebugSessions, waitForNoRunningAppHost, waitForRepositoryIdle, waitForResource, waitForRunningAppHost, waitForTerminalCommand, waitForWorkspaceAppHost } from './helpers/assertions';
import { executeE2eControlCommand, restoreWorkspaceCliPath, runE2eTeardown, setCliUnavailableForE2E, setTerminalCommandExecutionSuppressedForE2E, stopAppHostIfRunning, stopPrimaryAppHostIfRunning } from './helpers/fixtures';
import { getCommandInvocationCount, getResources, getTerminalCommandCount, getTreeAppHostLabel, isSamePath, waitForCommandOutcome, waitForDashboardUrl, waitForNoDebugSessions, waitForNoRunningAppHost, waitForRepositoryIdle, waitForResource, waitForRunningAppHost, waitForTerminalCommand, waitForWorkspaceAppHost } from './helpers/assertions';
adamint and others added 2 commits July 2, 2026 17:12
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings July 2, 2026 23:25

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review details

  • Files reviewed: 11/11 changed files
  • Comments generated: 2
  • Review effort level: Medium

Comment on lines +1269 to +1274
command: Extract<AspireExtensionE2EControlCommand, { name: 'copyEndpointUrl' | 'openInIntegratedBrowser' | 'assertClipboardMatchesEndpointUrl' }>
): { element: unknown; url: string } {
const element = appHostTreeProvider.findEndpointElement({
appHostPath: command.appHostPath,
resourceName: command.resourceName,
url: command.url,
url: 'url' in command ? command.url : undefined,
Comment on lines +171 to +172
"aspire-vscode.strings.appHostPathCopiedToClipboard": "AppHost path copied to clipboard.",
"aspire-vscode.strings.appHostPathInvalid": "Could not determine the AppHost path to copy.",
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Clicking the "Path" item in the AppHosts view should copy the path to the clipboard

2 participants