From cb234f357b1555f1a5d1730bdba038ece21cf5ed Mon Sep 17 00:00:00 2001 From: Erik Darling <2136037+erikdarlingdata@users.noreply.github.com> Date: Thu, 5 Mar 2026 13:24:51 -0500 Subject: [PATCH] Show loading placeholder tab for Run Repro Script (#24) Opens an "Actual Plan" tab immediately with an indeterminate progress bar and status text while the query executes. Replaces with the real plan on success, or shows the error message in-place on failure. Co-Authored-By: Claude Opus 4.6 --- src/PlanViewer.App/MainWindow.axaml.cs | 53 ++++++++++++++++++++++---- 1 file changed, 45 insertions(+), 8 deletions(-) diff --git a/src/PlanViewer.App/MainWindow.axaml.cs b/src/PlanViewer.App/MainWindow.axaml.cs index 2c39362..15cad87 100644 --- a/src/PlanViewer.App/MainWindow.axaml.cs +++ b/src/PlanViewer.App/MainWindow.axaml.cs @@ -1022,6 +1022,43 @@ private async Task GetActualPlanFromFile(PlanViewerControl viewer) dialog.ResultConnection.ServerName.Contains(".database.azure.com", StringComparison.OrdinalIgnoreCase); + // Create a loading placeholder tab immediately + var loadingPanel = new StackPanel + { + VerticalAlignment = VerticalAlignment.Center, + HorizontalAlignment = HorizontalAlignment.Center, + Width = 300 + }; + + var progressBar = new ProgressBar + { + IsIndeterminate = true, + Height = 4, + Margin = new Avalonia.Thickness(0, 0, 0, 12) + }; + + var statusText = new TextBlock + { + Text = "Executing query...", + FontSize = 14, + Foreground = new SolidColorBrush(Color.Parse("#B0B6C0")), + HorizontalAlignment = HorizontalAlignment.Center + }; + + loadingPanel.Children.Add(progressBar); + loadingPanel.Children.Add(statusText); + + var loadingContainer = new Grid + { + Background = new SolidColorBrush(Color.Parse("#1A1D23")), + Children = { loadingPanel } + }; + + var tab = CreateTab("Actual Plan", loadingContainer); + MainTabControl.Items.Add(tab); + MainTabControl.SelectedItem = tab; + UpdateEmptyOverlay(); + try { // Fetch server metadata for advice and Plan Insights @@ -1035,6 +1072,8 @@ private async Task GetActualPlanFromFile(PlanViewerControl viewer) } catch { /* Non-fatal — advice will just lack server context */ } + statusText.Text = "Capturing actual plan..."; + var cts = new System.Threading.CancellationTokenSource(); var sw = System.Diagnostics.Stopwatch.StartNew(); @@ -1047,24 +1086,22 @@ private async Task GetActualPlanFromFile(PlanViewerControl viewer) if (string.IsNullOrEmpty(actualPlanXml)) { - ShowError($"No actual plan returned ({sw.Elapsed.TotalSeconds:F1}s)."); + statusText.Text = $"No actual plan returned ({sw.Elapsed.TotalSeconds:F1}s)."; + progressBar.IsVisible = false; return; } - // Add a new tab with the actual plan + // Replace loading content with the actual plan var actualViewer = new PlanViewerControl(); actualViewer.Metadata = metadata; actualViewer.LoadPlan(actualPlanXml, "Actual Plan", queryText); - var content = CreatePlanTabContent(actualViewer); - var tab = CreateTab("Actual Plan", content); - MainTabControl.Items.Add(tab); - MainTabControl.SelectedItem = tab; - UpdateEmptyOverlay(); + tab.Content = CreatePlanTabContent(actualViewer); } catch (Exception ex) { - ShowError($"Error capturing actual plan:\n\n{ex.Message}"); + statusText.Text = $"Error: {ex.Message}"; + progressBar.IsVisible = false; } }