Skip to content

Add notification center, command response message, and markdown support to Dashboard#15906

Merged
JamesNK merged 16 commits intomainfrom
jamesnk/notifications-dialog
Apr 8, 2026
Merged

Add notification center, command response message, and markdown support to Dashboard#15906
JamesNK merged 16 commits intomainfrom
jamesnk/notifications-dialog

Conversation

@JamesNK
Copy link
Copy Markdown
Member

@JamesNK JamesNK commented Apr 6, 2026

Description

Add a notification center to the Aspire Dashboard, enhance resource command responses with a Message property throughout the stack, and add markdown rendering support to the text visualizer.

Notification Center

  • Add a notifications dialog accessible via a bell icon button in the header (desktop) and mobile nav menu.
  • Notifications are rendered using a custom NotificationEntryComponent in a scrollable panel with a "Dismiss all" action.
  • INotificationService thread-safe singleton tracks notifications with an unread count badge on the bell icon, reset when the dialog is opened.
  • Command execution results (starting, success, failure) are shown as notifications with timestamps and intent-based styling (info/success/error).
  • Success and error notifications include a "View response" action button to open TextVisualizerDialog when a command result is present.
  • Notifications are capped at 100 entries (oldest removed when exceeded).

Command Response Message

  • Add optional string message = 3 field to proto ResourceCommandResponse, deprecating error_message (field 2).
  • Add Message property to ExecuteCommandResult, marking ErrorMessage as [Obsolete].
  • Add Message property to ExecuteResourceCommandResponse (backchannel), marking ErrorMessage as [Obsolete].
  • Restructure command result data into CommandResultData / ResourceCommandResult types with Value, Format, and DisplayImmediately properties.
  • Add CommandResultFormat.Markdown enum value.
  • Add localized success messages for all built-in resource commands: Start, Stop, Restart, Rebuild, Set Parameter, Delete Parameter.
  • Toast titles show the command display name (e.g. "Start" succeeded); the body/details carry the full message from the server.
  • Dashboard and CLI consumers read Message with fallback to ErrorMessage for backward compatibility.

Markdown Support

  • Add CommandResultFormat.Markdown to hosting and dashboard.
  • TextVisualizerDialog renders markdown content using MarkdownRenderer when format is markdown.
  • CLI DisplayMarkdown now accepts an optional ConsoleOutput parameter for output routing.
  • Playground stress app includes a "Migrate Database" sample command returning markdown-formatted results.

Code Generation

  • Updated verify snapshots for Go, Java, Python, Rust, and TypeScript code generators to reflect new CommandResultData type.

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
  • Does the change require an update in our Aspire docs?

Copilot AI review requested due to automatic review settings April 6, 2026 08:07
@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Apr 6, 2026

🚀 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 -- 15906

Or

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

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR adds a notification center to the Aspire Dashboard UI and introduces a new Message field/property for resource command responses across the hosting service, backchannel, Dashboard client, and CLI, keeping compatibility with the deprecated ErrorMessage.

Changes:

  • Added a notifications panel (bell icon + dialog) and wiring to surface resource command progress/results via FluentMessageBarProvider.
  • Extended command result plumbing end-to-end with Message (proto + hosting DTOs + Dashboard/CLI consumers), deprecating ErrorMessage.
  • Updated built-in command implementations and tests to emit/expect the new message behavior and added localized success strings.

Reviewed changes

Copilot reviewed 88 out of 92 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
tests/Aspire.Hosting.Tests/WithHttpCommandTests.cs Updates test assertions to validate ExecuteCommandResult.Message.
tests/Aspire.Hosting.Tests/ResourceCommandServiceTests.cs Updates tests for Message and new CommandResults.Success(message, result, format) overload.
tests/Aspire.Dashboard.Components.Tests/Shared/FluentUISetupHelpers.cs Registers INotificationService for dashboard component tests.
src/Aspire.Hosting/Resources/xlf/CommandStrings.zh-Hant.xlf Adds new localized command success string entries.
src/Aspire.Hosting/Resources/xlf/CommandStrings.zh-Hans.xlf Adds new localized command success string entries.
src/Aspire.Hosting/Resources/xlf/CommandStrings.tr.xlf Adds new localized command success string entries.
src/Aspire.Hosting/Resources/xlf/CommandStrings.ru.xlf Adds new localized command success string entries.
src/Aspire.Hosting/Resources/xlf/CommandStrings.pt-BR.xlf Adds new localized command success string entries.
src/Aspire.Hosting/Resources/xlf/CommandStrings.pl.xlf Adds new localized command success string entries.
src/Aspire.Hosting/Resources/xlf/CommandStrings.ko.xlf Adds new localized command success string entries.
src/Aspire.Hosting/Resources/xlf/CommandStrings.ja.xlf Adds new localized command success string entries.
src/Aspire.Hosting/Resources/xlf/CommandStrings.it.xlf Adds new localized command success string entries.
src/Aspire.Hosting/Resources/xlf/CommandStrings.fr.xlf Adds new localized command success string entries.
src/Aspire.Hosting/Resources/xlf/CommandStrings.es.xlf Adds new localized command success string entries.
src/Aspire.Hosting/Resources/xlf/CommandStrings.de.xlf Adds new localized command success string entries.
src/Aspire.Hosting/Resources/xlf/CommandStrings.cs.xlf Adds new localized command success string entries.
src/Aspire.Hosting/Resources/CommandStrings.resx Adds new command success message resources.
src/Aspire.Hosting/Resources/CommandStrings.Designer.cs Regenerates strongly-typed accessors for added resources.
src/Aspire.Hosting/ResourceBuilderExtensions.cs Updates HTTP command failure result to use Message.
src/Aspire.Hosting/Orchestrator/ParameterProcessor.cs Returns success messages for set/delete parameter commands.
src/Aspire.Hosting/Dashboard/proto/dashboard_service.proto Adds message field and deprecates error_message.
src/Aspire.Hosting/Dashboard/DashboardServiceData.cs Switches to returning resolved message with ErrorMessage fallback.
src/Aspire.Hosting/Dashboard/DashboardService.cs Populates proto Message (and deprecated ErrorMessage) on responses.
src/Aspire.Hosting/Backchannel/BackchannelDataTypes.cs Adds Message to backchannel response DTO and obsoletes ErrorMessage.
src/Aspire.Hosting/Backchannel/AuxiliaryBackchannelRpcTarget.cs Maps command execution results into backchannel response (incl. Message).
src/Aspire.Hosting/ApplicationModel/ResourceCommandService.cs Uses Message for failures/logging and command-not-found errors.
src/Aspire.Hosting/ApplicationModel/ResourceCommandAnnotation.cs Adds Message to ExecuteCommandResult and updates CommandResults factories.
src/Aspire.Hosting/ApplicationModel/CommandsConfigurationExtensions.cs Adds localized success messages for lifecycle/rebuild commands and updates failure text to Message.
src/Aspire.Dashboard/Utils/DashboardUIHelpers.cs Introduces a dedicated message bar section for notifications.
src/Aspire.Dashboard/ServiceClient/Partials.cs Maps deprecated error_message to message for backward compatibility.
src/Aspire.Dashboard/ServiceClient/DashboardClient.cs Ensures Message is set in local failure responses.
src/Aspire.Dashboard/Resources/xlf/Resources.zh-Hant.xlf Updates toast title string formats and adds “View response”.
src/Aspire.Dashboard/Resources/xlf/Resources.zh-Hans.xlf Updates toast title string formats and adds “View response”.
src/Aspire.Dashboard/Resources/xlf/Resources.tr.xlf Updates toast title string formats and adds “View response”.
src/Aspire.Dashboard/Resources/xlf/Resources.ru.xlf Updates toast title string formats and adds “View response”.
src/Aspire.Dashboard/Resources/xlf/Resources.pt-BR.xlf Updates toast title string formats and adds “View response”.
src/Aspire.Dashboard/Resources/xlf/Resources.pl.xlf Updates toast title string formats and adds “View response”.
src/Aspire.Dashboard/Resources/xlf/Resources.ko.xlf Updates toast title string formats and adds “View response”.
src/Aspire.Dashboard/Resources/xlf/Resources.ja.xlf Updates toast title string formats and adds “View response”.
src/Aspire.Dashboard/Resources/xlf/Resources.it.xlf Updates toast title string formats and adds “View response”.
src/Aspire.Dashboard/Resources/xlf/Resources.fr.xlf Updates toast title string formats and adds “View response”.
src/Aspire.Dashboard/Resources/xlf/Resources.es.xlf Updates toast title string formats and adds “View response”.
src/Aspire.Dashboard/Resources/xlf/Resources.de.xlf Updates toast title string formats and adds “View response”.
src/Aspire.Dashboard/Resources/xlf/Resources.cs.xlf Updates toast title string formats and adds “View response”.
src/Aspire.Dashboard/Resources/xlf/Layout.zh-Hant.xlf Adds strings for notification center entry points and dialog title.
src/Aspire.Dashboard/Resources/xlf/Layout.zh-Hans.xlf Adds strings for notification center entry points and dialog title.
src/Aspire.Dashboard/Resources/xlf/Layout.tr.xlf Adds strings for notification center entry points and dialog title.
src/Aspire.Dashboard/Resources/xlf/Layout.ru.xlf Adds strings for notification center entry points and dialog title.
src/Aspire.Dashboard/Resources/xlf/Layout.pt-BR.xlf Adds strings for notification center entry points and dialog title.
src/Aspire.Dashboard/Resources/xlf/Layout.pl.xlf Adds strings for notification center entry points and dialog title.
src/Aspire.Dashboard/Resources/xlf/Layout.ko.xlf Adds strings for notification center entry points and dialog title.
src/Aspire.Dashboard/Resources/xlf/Layout.ja.xlf Adds strings for notification center entry points and dialog title.
src/Aspire.Dashboard/Resources/xlf/Layout.it.xlf Adds strings for notification center entry points and dialog title.
src/Aspire.Dashboard/Resources/xlf/Layout.fr.xlf Adds strings for notification center entry points and dialog title.
src/Aspire.Dashboard/Resources/xlf/Layout.es.xlf Adds strings for notification center entry points and dialog title.
src/Aspire.Dashboard/Resources/xlf/Layout.de.xlf Adds strings for notification center entry points and dialog title.
src/Aspire.Dashboard/Resources/xlf/Layout.cs.xlf Adds strings for notification center entry points and dialog title.
src/Aspire.Dashboard/Resources/xlf/Dialogs.zh-Hant.xlf Adds notification center dialog strings (“Dismiss all”, “No notifications”).
src/Aspire.Dashboard/Resources/xlf/Dialogs.zh-Hans.xlf Adds notification center dialog strings (“Dismiss all”, “No notifications”).
src/Aspire.Dashboard/Resources/xlf/Dialogs.tr.xlf Adds notification center dialog strings (“Dismiss all”, “No notifications”).
src/Aspire.Dashboard/Resources/xlf/Dialogs.ru.xlf Adds notification center dialog strings (“Dismiss all”, “No notifications”).
src/Aspire.Dashboard/Resources/xlf/Dialogs.pt-BR.xlf Adds notification center dialog strings (“Dismiss all”, “No notifications”).
src/Aspire.Dashboard/Resources/xlf/Dialogs.pl.xlf Adds notification center dialog strings (“Dismiss all”, “No notifications”).
src/Aspire.Dashboard/Resources/xlf/Dialogs.ko.xlf Adds notification center dialog strings (“Dismiss all”, “No notifications”).
src/Aspire.Dashboard/Resources/xlf/Dialogs.ja.xlf Adds notification center dialog strings (“Dismiss all”, “No notifications”).
src/Aspire.Dashboard/Resources/xlf/Dialogs.it.xlf Adds notification center dialog strings (“Dismiss all”, “No notifications”).
src/Aspire.Dashboard/Resources/xlf/Dialogs.fr.xlf Adds notification center dialog strings (“Dismiss all”, “No notifications”).
src/Aspire.Dashboard/Resources/xlf/Dialogs.es.xlf Adds notification center dialog strings (“Dismiss all”, “No notifications”).
src/Aspire.Dashboard/Resources/xlf/Dialogs.de.xlf Adds notification center dialog strings (“Dismiss all”, “No notifications”).
src/Aspire.Dashboard/Resources/xlf/Dialogs.cs.xlf Adds notification center dialog strings (“Dismiss all”, “No notifications”).
src/Aspire.Dashboard/Resources/Resources.resx Updates toast title formats and adds “View response” resource.
src/Aspire.Dashboard/Resources/Resources.Designer.cs Regenerates strongly-typed accessors for updated resources.
src/Aspire.Dashboard/Resources/Layout.resx Adds notification center strings.
src/Aspire.Dashboard/Resources/Layout.Designer.cs Regenerates strongly-typed accessors for added layout strings.
src/Aspire.Dashboard/Resources/Dialogs.resx Adds notification center dialog strings.
src/Aspire.Dashboard/Resources/Dialogs.Designer.cs Regenerates strongly-typed accessors for added dialog strings.
src/Aspire.Dashboard/Model/ResourceCommandResponseViewModel.cs Adds Message property for command response view model.
src/Aspire.Dashboard/Model/NotificationService.cs Adds singleton implementation tracking unread notification count.
src/Aspire.Dashboard/Model/INotificationService.cs Adds public interface for unread notification count tracking.
src/Aspire.Dashboard/Model/DashboardCommandExecutor.cs Emits notifications and adds “View response” actions for command results.
src/Aspire.Dashboard/DashboardWebApplication.cs Registers INotificationService in dashboard DI.
src/Aspire.Dashboard/Components/Layout/NotificationsHeaderButton.razor.cs Adds bell icon component logic + unread badge binding.
src/Aspire.Dashboard/Components/Layout/NotificationsHeaderButton.razor Adds bell icon button markup + counter badge.
src/Aspire.Dashboard/Components/Layout/MobileNavMenu.razor.cs Adds “Notifications” entry to the mobile nav menu.
src/Aspire.Dashboard/Components/Layout/MobileNavMenu.razor Adds required LaunchNotificationsAsync parameter.
src/Aspire.Dashboard/Components/Layout/MainLayout.razor.cs Adds notification center dialog launch logic.
src/Aspire.Dashboard/Components/Layout/MainLayout.razor Wires header bell button and mobile nav notifications entry.
src/Aspire.Dashboard/Components/Dialogs/NotificationsDialog.razor.css Adds styling for the notifications dialog layout.
src/Aspire.Dashboard/Components/Dialogs/NotificationsDialog.razor.cs Tracks presence of notifications and implements “Dismiss all”.
src/Aspire.Dashboard/Components/Dialogs/NotificationsDialog.razor Adds dialog UI and message bar provider for notifications section.
src/Aspire.Cli/Mcp/Tools/ExecuteResourceCommandTool.cs Prefers Message with ErrorMessage fallback when reporting failures.
src/Aspire.Cli/Commands/ResourceCommandHelper.cs Prefers Message with ErrorMessage fallback for CLI error output.
Files not reviewed (4)
  • src/Aspire.Dashboard/Resources/Dialogs.Designer.cs: Language not supported
  • src/Aspire.Dashboard/Resources/Layout.Designer.cs: Language not supported
  • src/Aspire.Dashboard/Resources/Resources.Designer.cs: Language not supported
  • src/Aspire.Hosting/Resources/CommandStrings.Designer.cs: Language not supported
Comments suppressed due to low confidence (1)

src/Aspire.Dashboard/Model/DashboardCommandExecutor.cs:156

  • If dashboardClient.ExecuteResourceCommandAsync(...) throws (network failure, server error, etc.), the in-progress notification message bar (progressMessage) is never closed and the unread count was already incremented. Wrap the remote call in a try/catch/finally that closes the progress message (and ideally disposes the CancellationTokenSource) so the notification center doesn't accumulate stuck "starting" entries on failures.
        var messageBarStartingTitle = string.Format(CultureInfo.InvariantCulture, loc[nameof(Dashboard.Resources.Resources.ResourceCommandStarting)], command.GetDisplayName());
        var toastStartingTitle = $"{getResourceName(resource)} {messageBarStartingTitle}";

        // Add a message bar to the notification center section for rendering via FluentMessageBarProvider.
        var progressMessage = await messageService.ShowMessageBarAsync(options =>
        {
            options.Title = messageBarStartingTitle;
            options.Intent = MessageIntent.Info;
            options.Section = DashboardUIHelpers.NotificationSection;
            options.AllowDismiss = true;
            options.Timestamp = DateTime.Now;
        }).ConfigureAwait(false);

        notificationService.IncrementUnreadCount();

        // When a resource command starts a toast is immediately shown.
        // The toast is open for a certain amount of time and then automatically closed.
        // When the resource command is finished the status is displayed in a toast.
        // Either the open toast is updated and its time is exteneded, or the a new toast is shown with the finished status.
        // Because of this logic we need to manage opening and closing the toasts manually.
        var toastParameters = new ToastParameters<CommunicationToastContent>()
        {
            Id = Guid.NewGuid().ToString(),
            Intent = ToastIntent.Progress,
            Title = toastStartingTitle,
            Content = new CommunicationToastContent(),
            Timeout = 0 // App logic will handle closing the toast
        };

        // Track whether toast is closed by timeout or user action.
        var toastClosed = false;
        Action<string?> closeCallback = (id) =>
        {
            if (id == toastParameters.Id)
            {
                toastClosed = true;
            }
        };

        ResourceCommandResponseViewModel response;
        CancellationTokenSource closeToastCts;
        try
        {
            toastService.OnClose += closeCallback;
            // Show a toast immediately to indicate the command is starting.
            toastService.ShowCommunicationToast(toastParameters);

            closeToastCts = new CancellationTokenSource();
            closeToastCts.Token.Register(() =>
            {
                toastService.CloseToast(toastParameters.Id);
            });
            closeToastCts.CancelAfter(DashboardUIHelpers.ToastTimeout);

            response = await dashboardClient.ExecuteResourceCommandAsync(resource.Name, resource.ResourceType, command, CancellationToken.None).ConfigureAwait(false);
        }
        finally

@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Apr 6, 2026

Re-running the failed jobs in the CI workflow for this pull request because 1 job was identified as retry-safe transient failures in the CI run attempt.
GitHub was asked to rerun all failed jobs for that attempt, and the rerun is being tracked in the rerun attempt.
The job links below point to the failed attempt jobs that matched the retry-safe transient failure rules.

Copy link
Copy Markdown
Member

@joperezr joperezr left a comment

Choose a reason for hiding this comment

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

Nice feature addition! Left a few comments — the proto wire compatibility and the removed API overload (minor release) are the most important ones to address. The rest are smaller improvements around accessibility, theming, and resource cleanup.

@joperezr
Copy link
Copy Markdown
Member

joperezr commented Apr 6, 2026

@JamesNK assuming copilot filled in the pr template, but of course this will need to be documented in our docs so please log an issue in aspire.dev to track documenting this

@JamesNK JamesNK changed the title Add notification center and command response message to Dashboard Add notification center, command response message, and markdown support to Dashboard Apr 7, 2026
@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Apr 7, 2026

Re-running the failed jobs in the CI workflow for this pull request because 1 job was identified as retry-safe transient failures in the CI run attempt.
GitHub was asked to rerun all failed jobs for that attempt, and the rerun is being tracked in the rerun attempt.
The job links below point to the failed attempt jobs that matched the retry-safe transient failure rules.

@joperezr joperezr added the blog-candidate PR that should be included in the release blog label Apr 7, 2026
Copy link
Copy Markdown
Member

@joperezr joperezr left a comment

Choose a reason for hiding this comment

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

All previous feedback has been addressed. LGTM!


@* Timestamp *@
<div class="notification-entry-time">
@((TimeProvider.GetUtcNow() - Entry.Timestamp).ToTimeAgo())
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

nit: The relative timestamp (ToTimeAgo()) is computed at render time and won't auto-refresh while the dialog stays open — so "5 minutes ago" will remain frozen until something triggers a re-render. I think this is fine as-is (notification centers are typically opened briefly, and it refreshes whenever new notifications arrive), but wanted to flag it in case others feel differently.

serviceBuilder.WithHttpCommand("/nested-trace-spans", "Out of order nested spans", commandOptions: new() { Method = HttpMethod.Get, IconName = "ContentViewGalleryLightning" });
serviceBuilder.WithHttpCommand("/exemplars-no-span", "Examplars with no span", commandOptions: new() { Method = HttpMethod.Get, IconName = "ContentViewGalleryLightning" });
serviceBuilder.WithHttpCommand("/genai-trace", "Gen AI trace", commandOptions: new() { Method = HttpMethod.Get, IconName = "ContentViewGalleryLightning" });
serviceBuilder.WithHttpCommand("/genai-langchain-trace", "Gen AI LangChain trace", commandOptions: new() { Method = HttpMethod.Get, IconName = "ContentViewGalleryLightning" });
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I'm not sure what to do about it, but I I don't like that every time I execute a value-less command there's a persistent notification. I don't want to have to click into the notification center to remove them each time.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

It's just a number displayed in the header. You don't have to dismiss it.

For an example of prior art, every time you start/restart/stop an AppService in Azure Portal, a notification is added for the operation.

{
var connectionString = $"Server=localhost,1433;Database=StressDb;User Id=sa;Password={Guid.NewGuid():N};TrustServerCertificate=true";
return Task.FromResult(CommandResults.Success(connectionString, CommandResultFormat.Text));
return Task.FromResult(CommandResults.Success("Retrieved connection string.", new CommandResultData { Value = connectionString, DisplayImmediately = true }));
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Same thing with this command, if the pop-up shows the command result, why do I also need a notification if the command succeeded?

Copy link
Copy Markdown
Member Author

@JamesNK JamesNK Apr 8, 2026

Choose a reason for hiding this comment

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

It's a history of what you've done. I think it would be unexpected if they weren't consistently added.

.WithCommand(
name: "migrate-database",
displayName: "Migrate Database",
executeCommand: (c) =>
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Why is the experience not consistent with no pop-ups showing up for Migrate Database when there is one for Get Connection String? It feels a little incongruous

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

They're test commands. It's for testing different scenarios.


private static void DisplayCommandResult(IInteractionService interactionService, ExecuteResourceCommandResult result)
{
if (string.Equals(result.Format, "markdown", StringComparison.OrdinalIgnoreCase))
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

const?

{
<div class="notifications-dismiss">
<FluentButton Appearance="Appearance.Lightweight" OnClick="@DismissAll">
@Loc[nameof(Dialogs.NotificationCenterDismissAll)]
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

a la vs code, could we use an icon button? and can we move to be in-line with the notification header

Image

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

The UI follows what Azure Portal does for notifications and has the dismiss all button below the title and above the notifications.

We don't care as much about UI real estate compared to VS Code.

TrapFocus = true,
Modal = true,
Alignment = HorizontalAlignment.Right,
Width = "350px",
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

should we make this controllable?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Pardon? This is the same width as the settings dialog. Everything wraps so it should be fine.

However, I'll add a test with a very long message to double check it displays ok. Probably should truncate it and then display a tooltip.

/// <summary>
/// Gets the number of notifications added since the dialog was last opened.
/// </summary>
int UnreadCount { get; }
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

why is this a property instead of a method? just curious, since it has a setter function

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

It doesn't have a setter function?

It's a property because accessing the value is fast and has no side effects. i.e. it behaves like a readonly field. A property is better than a method in this situation.

JamesNK added 12 commits April 8, 2026 11:50
…mponent

- Make INotificationService a singleton that stores notification data
  (AddNotification, ReplaceNotification, RemoveNotification, ClearAll)
- Remove dependency on IMessageService for notification center
- Create NotificationEntryComponent styled like FluentMessageBar notification
- Update NotificationsDialog to iterate over stored notifications
- Update DashboardCommandExecutor to use notification service directly
- Fix CancellationTokenSource disposal in toast auto-close logic
@JamesNK JamesNK force-pushed the jamesnk/notifications-dialog branch from 0566d92 to 0a23e40 Compare April 8, 2026 03:51
@JamesNK JamesNK enabled auto-merge (squash) April 8, 2026 04:21
@JamesNK JamesNK merged commit c0c721a into main Apr 8, 2026
264 checks passed
@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Apr 8, 2026

🎬 CLI E2E Test Recordings — 56 recordings uploaded (commit 3416da5)

View recordings
Test Recording
AddPackageInteractiveWhileAppHostRunningDetached ▶️ View Recording
AddPackageWhileAppHostRunningDetached ▶️ View Recording
AgentCommands_AllHelpOutputs_AreCorrect ▶️ View Recording
AgentInitCommand_DefaultSelection_InstallsSkillOnly ▶️ View Recording
AgentInitCommand_MigratesDeprecatedConfig ▶️ View Recording
AllPublishMethodsBuildDockerImages ▶️ View Recording
AspireAddPackageVersionToDirectoryPackagesProps ▶️ View Recording
AspireUpdateRemovesAppHostPackageVersionFromDirectoryPackagesProps ▶️ View Recording
Banner_DisplayedOnFirstRun ▶️ View Recording
Banner_DisplayedWithExplicitFlag ▶️ View Recording
Banner_NotDisplayedWithNoLogoFlag ▶️ View Recording
CertificatesClean_RemovesCertificates ▶️ View Recording
CertificatesTrust_WithNoCert_CreatesAndTrustsCertificate ▶️ View Recording
CertificatesTrust_WithUntrustedCert_TrustsCertificate ▶️ View Recording
ConfigSetGet_CreatesNestedJsonFormat ▶️ View Recording
CreateAndRunAspireStarterProject ▶️ View Recording
CreateAndRunAspireStarterProjectWithBundle ▶️ View Recording
CreateAndRunEmptyAppHostProject ▶️ View Recording
CreateAndRunJavaEmptyAppHostProject ▶️ View Recording
CreateAndRunJsReactProject ▶️ View Recording
CreateAndRunPythonReactProject ▶️ View Recording
CreateAndRunTypeScriptEmptyAppHostProject ▶️ View Recording
CreateAndRunTypeScriptStarterProject ▶️ View Recording
CreateJavaAppHostWithViteApp ▶️ View Recording
CreateStartAndStopAspireProject ▶️ View Recording
CreateTypeScriptAppHostWithViteApp ▶️ View Recording
DashboardRunWithOtelTracesReturnsNoTraces ▶️ View Recording
DescribeCommandResolvesReplicaNames ▶️ View Recording
DescribeCommandShowsRunningResources ▶️ View Recording
DetachFormatJsonProducesValidJson ▶️ View Recording
DoctorCommand_DetectsDeprecatedAgentConfig ▶️ View Recording
DoctorCommand_WithSslCertDir_ShowsTrusted ▶️ View Recording
DoctorCommand_WithoutSslCertDir_ShowsPartiallyTrusted ▶️ View Recording
GlobalMigration_HandlesCommentsAndTrailingCommas ▶️ View Recording
GlobalMigration_HandlesMalformedLegacyJson ▶️ View Recording
GlobalMigration_PreservesAllValueTypes ▶️ View Recording
GlobalMigration_SkipsWhenNewConfigExists ▶️ View Recording
GlobalSettings_MigratedFromLegacyFormat ▶️ View Recording
InvalidAppHostPathWithComments_IsHealedOnRun ▶️ View Recording
LegacySettingsMigration_AdjustsRelativeAppHostPath ▶️ View Recording
LogsCommandShowsResourceLogs ▶️ View Recording
PsCommandListsRunningAppHost ▶️ View Recording
PsFormatJsonOutputsOnlyJsonToStdout ▶️ View Recording
PublishWithDockerComposeServiceCallbackSucceeds ▶️ View Recording
RestoreGeneratesSdkFiles ▶️ View Recording
RestoreSupportsConfigOnlyHelperPackageAndCrossPackageTypes ▶️ View Recording
RunFromParentDirectory_UsesExistingConfigNearAppHost ▶️ View Recording
RunWithMissingAwaitShowsHelpfulError ▶️ View Recording
SecretCrudOnDotNetAppHost ▶️ View Recording
SecretCrudOnTypeScriptAppHost ▶️ View Recording
StagingChannel_ConfigureAndVerifySettings_ThenSwitchChannels ▶️ View Recording
StopAllAppHostsFromAppHostDirectory ▶️ View Recording
StopAllAppHostsFromUnrelatedDirectory ▶️ View Recording
StopNonInteractiveMultipleAppHostsShowsError ▶️ View Recording
StopNonInteractiveSingleAppHost ▶️ View Recording
StopWithNoRunningAppHostExitsSuccessfully ▶️ View Recording

📹 Recordings uploaded automatically from CI run #24117741289

@aspire-repo-bot
Copy link
Copy Markdown

📝 Documentation updates drafted

Documentation updates for the changes in this PR have been prepared for microsoft/aspire.dev. The docs workflow couldn't create the draft PR automatically (the aspire.dev checkout wasn't available in the workflow environment), so the changes are described below for a human to apply.


Files to update

src/frontend/src/content/docs/dashboard/explore.mdx

1. Update text visualizer description (line ~224) — change:

Some values are formatted as JSON or XML. In these cases, the text visualizer enables the **Select format** dropdown to switch between the different formats.

to:

Some values are formatted as JSON, XML, or Markdown. In these cases, the text visualizer enables the **Select format** dropdown to switch between the different formats. Commands that return a result with `CommandResultFormat.Markdown` have their content rendered as formatted Markdown text in the visualizer.

2. Add a "Notification center" section between the ## Settings dialog and ## Dashboard shortcuts sections:

## Notification center

The dashboard includes a notification center accessible via the bell icon in the top-right corner of the header. When there are unread notifications, a badge with the unread count appears on the icon.

Selecting the bell icon opens the **Notifications** dialog, which lists all notifications. Notifications are generated when:

- A resource command completes and returns a `Message` in the `ExecuteCommandResult`. Both success and failure messages appear as notifications.
- System events occur that require user attention.

Each notification shows its title, message body, and a timestamp. The unread count resets when you open the notification center. To clear all notifications, select the **Clear all** button in the dialog.

For information on returning messages from custom commands, see [Custom resource commands](/fundamentals/custom-resource-commands/).

src/frontend/src/content/docs/fundamentals/custom-resource-commands.mdx

1. Fix deprecated ErrorMessage reference in the "Execute a command by resource ID" code example — change:

logger.LogError("Command failed: {ErrorMessage}", result.ErrorMessage);

to:

logger.LogError("Command failed: {Message}", result.Message);

2. Replace the "Handle command execution results" section with:

### Handle command execution results

The `ExecuteCommandResult` class provides information about the command execution:

- **`Success`**: A boolean indicating whether the command was successful.
- **`Message`**: An optional string with a message displayed in the dashboard after the command completes. Shown for both successful and failed commands.
- **`Data`**: An optional `CommandResultData` instance with structured result data to display in the dashboard's text visualizer.
- **`Canceled`**: A boolean indicating whether the command was canceled by the user.

The `CommandResults` helper class provides factory methods to create result instances:

```csharp
// Simple success
return CommandResults.Success();

// Success with a dashboard notification message
return CommandResults.Success("Cache cleared successfully.");

// Success with a message and structured result data shown in the text visualizer
return CommandResults.Success("Migration report", migrationReport, CommandResultFormat.Markdown);

// Failure with a message
return CommandResults.Failure("An error occurred during execution.");

// Canceled by user
return CommandResults.Canceled();
```

#### Return structured result data

Commands can return a `CommandResultData` value to display result content in the dashboard's text visualizer. The `CommandResultData` class has these properties:

- **`Value`**: The string content to display.
- **`Format`**: A `CommandResultFormat` value controlling how the content is rendered:
  - `Text`: Plain text content (default).
  - `Json`: JSON-formatted content with syntax highlighting.
  - `Markdown`: Markdown content rendered as formatted text.
- **`DisplayImmediately`**: When `true`, the text visualizer opens automatically after the command completes.

The following example returns a Markdown-formatted migration report:

```csharp
private static async Task<ExecuteCommandResult> OnRunMigrateCommandAsync(
    ExecuteCommandContext context)
{
    // Run migrations and collect results...
    var report = "## Migration Report\n\n- ✅ AppliedMigration1\n- ✅ AppliedMigration2";

    return CommandResults.Success(
        message: "Migrations applied successfully.",
        result: report,
        resultFormat: CommandResultFormat.Markdown);
}
```

The dashboard displays a notification with the message and, when the user opens the result, renders the Markdown content in the text visualizer.

Note: Screenshots for the notification center UI would be a welcome addition to the explore.mdx changes above.

Generated by PR Documentation Check for issue #15906 · ● 2.8M ·

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

blog-candidate PR that should be included in the release blog

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants