From be7b2d046406fff35f9e631c09f42f0aba2234e9 Mon Sep 17 00:00:00 2001 From: James Newton-King Date: Tue, 19 May 2026 23:50:37 +0800 Subject: [PATCH 1/4] Fix stack overflow in notification center dialog focus trapping Disable TrapFocus on the notifications center dialog because it launches child dialogs. When multiple dialogs trap focus simultaneously, a recursive focus loop occurs causing 'Maximum call stack size exceeded' errors. Fixes #16593 --- .../Components/Dialogs/NotificationEntryComponent.razor.cs | 3 +++ src/Aspire.Dashboard/Components/Layout/MainLayout.razor.cs | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/src/Aspire.Dashboard/Components/Dialogs/NotificationEntryComponent.razor.cs b/src/Aspire.Dashboard/Components/Dialogs/NotificationEntryComponent.razor.cs index 3ee4f90d268..87858489d3c 100644 --- a/src/Aspire.Dashboard/Components/Dialogs/NotificationEntryComponent.razor.cs +++ b/src/Aspire.Dashboard/Components/Dialogs/NotificationEntryComponent.razor.cs @@ -16,6 +16,9 @@ public partial class NotificationEntryComponent : ComponentBase [Parameter] public EventCallback OnDismiss { get; set; } + [CascadingParameter] + public FluentDialog Dialog { get; set; } = default!; + private string IntentClass => Entry.Intent switch { MessageIntent.Success => "intent-success", diff --git a/src/Aspire.Dashboard/Components/Layout/MainLayout.razor.cs b/src/Aspire.Dashboard/Components/Layout/MainLayout.razor.cs index bf194da1f92..8a19215b72c 100644 --- a/src/Aspire.Dashboard/Components/Layout/MainLayout.razor.cs +++ b/src/Aspire.Dashboard/Components/Layout/MainLayout.razor.cs @@ -324,7 +324,7 @@ public async Task LaunchNotificationsAsync() Title = Loc[nameof(Resources.Layout.MainLayoutNotificationCenterTitle)], PrimaryAction = Loc[nameof(Resources.Layout.MainLayoutSettingsDialogClose)].Value, SecondaryAction = null, - TrapFocus = true, + TrapFocus = false, // This dialog launches child dialogs, and multiple dialogs can't trap focus correctly Modal = true, Alignment = HorizontalAlignment.Right, Width = "350px", From a6af01c80adfce1b2f2bab22668e1fb7fcd59345 Mon Sep 17 00:00:00 2001 From: James Newton-King Date: Wed, 20 May 2026 00:00:17 +0800 Subject: [PATCH 2/4] Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- .../Components/Dialogs/NotificationEntryComponent.razor.cs | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/Aspire.Dashboard/Components/Dialogs/NotificationEntryComponent.razor.cs b/src/Aspire.Dashboard/Components/Dialogs/NotificationEntryComponent.razor.cs index 87858489d3c..3ee4f90d268 100644 --- a/src/Aspire.Dashboard/Components/Dialogs/NotificationEntryComponent.razor.cs +++ b/src/Aspire.Dashboard/Components/Dialogs/NotificationEntryComponent.razor.cs @@ -16,9 +16,6 @@ public partial class NotificationEntryComponent : ComponentBase [Parameter] public EventCallback OnDismiss { get; set; } - [CascadingParameter] - public FluentDialog Dialog { get; set; } = default!; - private string IntentClass => Entry.Intent switch { MessageIntent.Success => "intent-success", From 5ce9505d371f233d86578bd4ed061c37a32139dd Mon Sep 17 00:00:00 2001 From: James Newton-King Date: Wed, 20 May 2026 00:22:16 +0800 Subject: [PATCH 3/4] Update --- .../Dialogs/NotificationEntryComponent.razor.cs | 10 +++++++++- .../Components/Dialogs/TextVisualizerDialog.razor.cs | 4 ++-- .../Components/Layout/MainLayout.razor.cs | 2 +- src/Aspire.Dashboard/Model/DashboardCommandExecutor.cs | 7 +++++-- 4 files changed, 17 insertions(+), 6 deletions(-) diff --git a/src/Aspire.Dashboard/Components/Dialogs/NotificationEntryComponent.razor.cs b/src/Aspire.Dashboard/Components/Dialogs/NotificationEntryComponent.razor.cs index 3ee4f90d268..ad603d414f5 100644 --- a/src/Aspire.Dashboard/Components/Dialogs/NotificationEntryComponent.razor.cs +++ b/src/Aspire.Dashboard/Components/Dialogs/NotificationEntryComponent.razor.cs @@ -49,7 +49,15 @@ private async Task HandlePrimaryAction() { if (Entry.PrimaryAction is { } primaryAction) { - await primaryAction.OnClick(); + try + { + Dialog.Hide(); + await primaryAction.OnClick(); + } + finally + { + Dialog.Show(); + } } } } diff --git a/src/Aspire.Dashboard/Components/Dialogs/TextVisualizerDialog.razor.cs b/src/Aspire.Dashboard/Components/Dialogs/TextVisualizerDialog.razor.cs index b4a563cc018..0dfc6a479df 100644 --- a/src/Aspire.Dashboard/Components/Dialogs/TextVisualizerDialog.razor.cs +++ b/src/Aspire.Dashboard/Components/Dialogs/TextVisualizerDialog.razor.cs @@ -114,7 +114,7 @@ internal MarkdownProcessor GetMarkdownProcessor() return _markdownProcessor ??= new MarkdownProcessor(ControlsStringsLoc, safeUrlSchemes: MarkdownHelpers.SafeUrlSchemes, extensions: []); } - public static async Task OpenDialogAsync(OpenTextVisualizerDialogOptions options) + public static async Task OpenDialogAsync(OpenTextVisualizerDialogOptions options) { var width = options.DialogService.IsDesktop ? "75vw" : "100vw"; var parameters = new DialogParameters @@ -126,7 +126,7 @@ public static async Task OpenDialogAsync(OpenTextVisualizerDialogOptions options PreventScroll = true, }; - await options.DialogService.ShowDialogAsync( + return await options.DialogService.ShowDialogAsync( new TextVisualizerDialogViewModel(options.Value, options.ValueDescription, options.ContainsSecret, options.DownloadFileName, options.FixedFormat), parameters); } diff --git a/src/Aspire.Dashboard/Components/Layout/MainLayout.razor.cs b/src/Aspire.Dashboard/Components/Layout/MainLayout.razor.cs index 8a19215b72c..bf194da1f92 100644 --- a/src/Aspire.Dashboard/Components/Layout/MainLayout.razor.cs +++ b/src/Aspire.Dashboard/Components/Layout/MainLayout.razor.cs @@ -324,7 +324,7 @@ public async Task LaunchNotificationsAsync() Title = Loc[nameof(Resources.Layout.MainLayoutNotificationCenterTitle)], PrimaryAction = Loc[nameof(Resources.Layout.MainLayoutSettingsDialogClose)].Value, SecondaryAction = null, - TrapFocus = false, // This dialog launches child dialogs, and multiple dialogs can't trap focus correctly + TrapFocus = true, Modal = true, Alignment = HorizontalAlignment.Right, Width = "350px", diff --git a/src/Aspire.Dashboard/Model/DashboardCommandExecutor.cs b/src/Aspire.Dashboard/Model/DashboardCommandExecutor.cs index 66021ac1013..8780a7ced8d 100644 --- a/src/Aspire.Dashboard/Model/DashboardCommandExecutor.cs +++ b/src/Aspire.Dashboard/Model/DashboardCommandExecutor.cs @@ -292,12 +292,15 @@ private async Task OpenViewResponseDialogAsync(CommandViewModel command, Resourc _ => null }; - await TextVisualizerDialog.OpenDialogAsync(new OpenTextVisualizerDialogOptions + var reference = await TextVisualizerDialog.OpenDialogAsync(new OpenTextVisualizerDialogOptions { DialogService = dialogService, ValueDescription = command.GetDisplayName(), Value = response.Result.Value, FixedFormat = fixedFormat - }).ConfigureAwait(false); + }).ConfigureAwait(true); + + // Await the result to wait here until the dialog is closed. + await reference.Result.ConfigureAwait(true); } } From 31d58efb63e79ce429322f9dbdd31c4c52a42207 Mon Sep 17 00:00:00 2001 From: James Newton-King Date: Wed, 20 May 2026 00:43:02 +0800 Subject: [PATCH 4/4] Fix --- .../Components/Dialogs/NotificationEntryComponent.razor.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/Aspire.Dashboard/Components/Dialogs/NotificationEntryComponent.razor.cs b/src/Aspire.Dashboard/Components/Dialogs/NotificationEntryComponent.razor.cs index ad603d414f5..a5774f0946f 100644 --- a/src/Aspire.Dashboard/Components/Dialogs/NotificationEntryComponent.razor.cs +++ b/src/Aspire.Dashboard/Components/Dialogs/NotificationEntryComponent.razor.cs @@ -16,6 +16,9 @@ public partial class NotificationEntryComponent : ComponentBase [Parameter] public EventCallback OnDismiss { get; set; } + [CascadingParameter] + public FluentDialog Dialog { get; set; } = default!; + private string IntentClass => Entry.Intent switch { MessageIntent.Success => "intent-success",