Skip to content

Commit eb1f4d5

Browse files
QS Overview review followups, brand sweep, version unification (#315)
QueryStoreOverviewControl fixes: - Replace #888888 with theme ForegroundMutedBrush (dim-text rule) - Convert wait-stats error modal to inline warning badge with tooltip - Drop dispose-while-in-flight on the OnSlicerRangeChanged CTS race - Replace blanket catch{} with an inline RefreshErrorBadge so failed refreshes surface their SqlException instead of silently blanking Brand sweep ("SQL Performance Studio" -> "Performance Studio"): User-visible surfaces only — issue templates, .csproj <Product>, VSIX manifest, vsct ButtonText, VSPackage.resx, AssemblyInfo, all SSMS dialog titles + error messages, install.cmd, installer console banner. Internal IDs (pipe name, registry key) and Program-Files paths kept; legacy spaced "SQL Performance Studio" Program-Files paths kept as additional fallbacks so existing installs still launch. Version unification: - New src/Directory.Build.props centralizes Version + identity for all SDK-style projects under src/ - Removed duplicated <Version>/<Authors>/<Company>/<Product>/ <Copyright> from App, Core, Cli, Web, Installer csprojs - PlanViewer.Ssms is legacy non-SDK and bumped manually: AssemblyInfo.cs 1.0.0.0 -> 1.10.0.0, vsixmanifest 1.0.0 -> 1.10.0 - Tests/server projects are outside src/ and unaffected Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent c90bff7 commit eb1f4d5

18 files changed

Lines changed: 116 additions & 90 deletions

.github/ISSUE_TEMPLATE/bug_report.yml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
name: Bug Report
2-
description: Report a problem with SQL Performance Studio
2+
description: Report a problem with Performance Studio
33
title: "[BUG] "
44
labels: ["bug"]
55

@@ -8,7 +8,7 @@ body:
88
id: component
99
attributes:
1010
label: Component
11-
description: Which part of SQL Performance Studio is affected?
11+
description: Which part of Performance Studio is affected?
1212
options:
1313
- Desktop App (Windows)
1414
- Desktop App (macOS)
@@ -21,7 +21,7 @@ body:
2121
- type: input
2222
id: version
2323
attributes:
24-
label: SQL Performance Studio Version
24+
label: Performance Studio Version
2525
description: Check the About dialog or the release you downloaded.
2626
placeholder: "e.g., 0.7.0"
2727
validations:

.github/ISSUE_TEMPLATE/config.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,4 @@ blank_issues_enabled: false
22
contact_links:
33
- name: Questions & Discussion
44
url: https://github.com/erikdarlingdata/PerformanceStudio/discussions
5-
about: Ask questions and discuss SQL Performance Studio with the community
5+
about: Ask questions and discuss Performance Studio with the community

src/Directory.Build.props

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
<Project>
2+
<!--
3+
Single source of truth for product version + identity across src/ projects.
4+
5+
Picked up automatically by SDK-style projects in this folder and below:
6+
PlanViewer.App, PlanViewer.Core, PlanViewer.Cli, PlanViewer.Web,
7+
PlanViewer.Ssms.Installer.
8+
9+
NOT picked up by PlanViewer.Ssms — it's a legacy non-SDK project.
10+
Its version lives in:
11+
Properties/AssemblyInfo.cs (AssemblyVersion + AssemblyFileVersion)
12+
source.extension.vsixmanifest (Identity Version="...")
13+
Bump those manually when bumping the version here.
14+
15+
Tests and server/ projects are outside src/ and are unaffected.
16+
-->
17+
<PropertyGroup>
18+
<Version>1.10.0</Version>
19+
<Authors>Erik Darling</Authors>
20+
<Company>Darling Data LLC</Company>
21+
<Product>Performance Studio</Product>
22+
<Copyright>Copyright (c) 2026 Erik Darling, Darling Data LLC</Copyright>
23+
</PropertyGroup>
24+
</Project>

src/PlanViewer.App/Controls/QueryStoreOverviewControl.axaml

Lines changed: 21 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,18 @@
1010
<RowDefinition Height="2*"/>
1111
</Grid.RowDefinitions>
1212

13-
<!-- Row 0: Progress bar (always visible to reserve space, toggle IsIndeterminate) -->
14-
<ProgressBar x:Name="LoadingBar" Grid.Row="0" IsIndeterminate="False"
15-
Height="3" Margin="0"
16-
Foreground="{DynamicResource AccentBrush}"/>
13+
<!-- Row 0: Progress bar + refresh-error badge -->
14+
<Grid Grid.Row="0">
15+
<ProgressBar x:Name="LoadingBar" IsIndeterminate="False"
16+
Height="3" Margin="0"
17+
VerticalAlignment="Top"
18+
Foreground="{DynamicResource AccentBrush}"/>
19+
<TextBlock x:Name="RefreshErrorBadge" Text="⚠ refresh failed"
20+
FontSize="10" FontWeight="SemiBold"
21+
Foreground="#F2C94C"
22+
HorizontalAlignment="Right" VerticalAlignment="Top"
23+
Margin="0,2,10,0" IsVisible="False" Cursor="Hand"/>
24+
</Grid>
1725

1826
<!-- Row 1: Donut + TimeSlicer + WaitStats -->
1927
<Grid Grid.Row="1" Margin="10">
@@ -43,9 +51,15 @@
4351
<Border Grid.Column="2" Background="{DynamicResource BackgroundLightBrush}"
4452
CornerRadius="4" Margin="5,0,0,0" ClipToBounds="True">
4553
<Grid RowDefinitions="Auto,*">
46-
<TextBlock Grid.Row="0" Text="Wait Stats by Database" FontSize="11" FontWeight="SemiBold"
47-
Foreground="{DynamicResource ForegroundBrush}"
48-
HorizontalAlignment="Center" Margin="0,4,0,0"/>
54+
<Grid Grid.Row="0">
55+
<TextBlock Text="Wait Stats by Database" FontSize="11" FontWeight="SemiBold"
56+
Foreground="{DynamicResource ForegroundBrush}"
57+
HorizontalAlignment="Center" Margin="0,4,0,0"/>
58+
<TextBlock x:Name="WaitStatsWarning" Text="" FontSize="12"
59+
Foreground="#F2C94C"
60+
HorizontalAlignment="Right" VerticalAlignment="Center"
61+
Margin="0,4,8,0" IsVisible="False" Cursor="Hand"/>
62+
</Grid>
4963
<Border x:Name="WaitStatsBorder" Grid.Row="1" ClipToBounds="True">
5064
<Canvas x:Name="WaitStatsCanvas" Background="Transparent"/>
5165
</Border>

src/PlanViewer.App/Controls/QueryStoreOverviewControl.axaml.cs

Lines changed: 43 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -170,15 +170,13 @@ private async Task RefreshMetricsAndWaitStatsAsync(CancellationToken ct)
170170
_masterConnectionString, _activeDbs, _slicerStartUtc, _slicerEndUtc, _maxDop, ct);
171171
_waitSlices = slices;
172172

173-
if (errors.Count > 0)
174-
{
175-
await Dispatcher.UIThread.InvokeAsync(() =>
176-
ShowWaitStatsErrors(errors));
177-
}
173+
await Dispatcher.UIThread.InvokeAsync(() => UpdateWaitStatsWarning(errors));
178174
}
179175
else
180176
{
181177
_waitSlices.Clear();
178+
await Dispatcher.UIThread.InvokeAsync(() =>
179+
UpdateWaitStatsWarning(new List<(string Database, string Error)>()));
182180
}
183181

184182
await Dispatcher.UIThread.InvokeAsync(() =>
@@ -193,31 +191,23 @@ await Dispatcher.UIThread.InvokeAsync(() =>
193191
}
194192
}
195193

196-
private void ShowWaitStatsErrors(List<(string Database, string Error)> errors)
194+
private void UpdateWaitStatsWarning(List<(string Database, string Error)> errors)
197195
{
198-
var msg = string.Join("\n", errors.Select(e => $"[{e.Database}] {e.Error}"));
199-
var window = new Avalonia.Controls.Window
196+
if (errors.Count == 0)
200197
{
201-
Title = "Wait Stats Errors",
202-
Width = 600,
203-
Height = 300,
204-
Content = new ScrollViewer
205-
{
206-
Content = new TextBlock
207-
{
208-
Text = msg,
209-
TextWrapping = Avalonia.Media.TextWrapping.Wrap,
210-
Margin = new Thickness(10),
211-
Foreground = new SolidColorBrush(Color.Parse("#E4E6EB")),
212-
FontSize = 12,
213-
}
214-
}
215-
};
216-
var topLevel = TopLevel.GetTopLevel(this);
217-
if (topLevel is Avalonia.Controls.Window owner)
218-
window.ShowDialog(owner);
219-
else
220-
window.Show();
198+
WaitStatsWarning.IsVisible = false;
199+
ToolTip.SetTip(WaitStatsWarning, null);
200+
return;
201+
}
202+
203+
var header = errors.Count == 1
204+
? "Wait stats incomplete (1 error):"
205+
: $"Wait stats incomplete ({errors.Count} errors):";
206+
var msg = string.Join("\n", errors.Select(e => $"[{e.Database}] {e.Error}"));
207+
208+
ToolTip.SetTip(WaitStatsWarning, $"{header}\n{msg}");
209+
ToolTip.SetShowDelay(WaitStatsWarning, 200);
210+
WaitStatsWarning.IsVisible = true;
221211
}
222212

223213
// ── Donut Chart ──────────────────────────────────────────────────────────
@@ -438,15 +428,35 @@ private async void OnSlicerRangeChanged(object? sender, TimeRangeChangedEventArg
438428
_slicerStartUtc = e.StartUtc;
439429
_slicerEndUtc = e.EndUtc;
440430

431+
// Don't dispose the previous CTS — the in-flight refresh still holds its token.
432+
// Cancel signals the previous run; GC reclaims the source after both runs unwind.
441433
_cts?.Cancel();
442-
_cts?.Dispose();
443-
_cts = new CancellationTokenSource();
434+
var newCts = new CancellationTokenSource();
435+
_cts = newCts;
436+
437+
ClearRefreshError();
444438
try
445439
{
446-
await RefreshMetricsAndWaitStatsAsync(_cts.Token);
440+
await RefreshMetricsAndWaitStatsAsync(newCts.Token);
447441
}
448442
catch (OperationCanceledException) { }
449-
catch { /* swallow errors from refresh */ }
443+
catch (Exception ex)
444+
{
445+
ShowRefreshError(ex);
446+
}
447+
}
448+
449+
private void ShowRefreshError(Exception ex)
450+
{
451+
ToolTip.SetTip(RefreshErrorBadge, $"Last refresh failed:\n{ex.Message}");
452+
ToolTip.SetShowDelay(RefreshErrorBadge, 200);
453+
RefreshErrorBadge.IsVisible = true;
454+
}
455+
456+
private void ClearRefreshError()
457+
{
458+
RefreshErrorBadge.IsVisible = false;
459+
ToolTip.SetTip(RefreshErrorBadge, null);
450460
}
451461

452462
// ── Wait Stats Chart (stacked by database) ──────────────────────────────
@@ -460,7 +470,7 @@ private void DrawWaitStatsChart()
460470
{
461471
Text = _supportsWaitStats ? "No wait stats data" : "Wait stats not supported (SQL 2017+ required)",
462472
FontSize = 10,
463-
Foreground = new SolidColorBrush(Color.Parse("#888888")),
473+
Foreground = this.FindResource("ForegroundMutedBrush") as IBrush ?? new SolidColorBrush(Color.Parse("#B0B6C0")),
464474
TextWrapping = Avalonia.Media.TextWrapping.Wrap,
465475
};
466476
Canvas.SetLeft(msg, 10);

src/PlanViewer.App/PlanViewer.App.csproj

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,6 @@
66
<ApplicationManifest>app.manifest</ApplicationManifest>
77
<ApplicationIcon>EDD.ico</ApplicationIcon>
88
<AvaloniaUseCompiledBindingsByDefault>true</AvaloniaUseCompiledBindingsByDefault>
9-
<Version>1.10.0</Version>
10-
<Authors>Erik Darling</Authors>
11-
<Company>Darling Data LLC</Company>
12-
<Product>Performance Studio</Product>
13-
<Copyright>Copyright (c) 2026 Erik Darling, Darling Data LLC</Copyright>
149
</PropertyGroup>
1510

1611
<ItemGroup>

src/PlanViewer.Cli/PlanViewer.Cli.csproj

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,6 @@
1111
<Nullable>enable</Nullable>
1212
<RootNamespace>PlanViewer.Cli</RootNamespace>
1313
<AssemblyName>planview</AssemblyName>
14-
<Version>1.3.0</Version>
15-
<Authors>Erik Darling</Authors>
16-
<Company>Darling Data LLC</Company>
17-
<Product>Performance Studio</Product>
18-
<Copyright>Copyright (c) 2026 Erik Darling, Darling Data LLC</Copyright>
1914
</PropertyGroup>
2015

2116
<ItemGroup>

src/PlanViewer.Core/PlanViewer.Core.csproj

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,6 @@
55
<ImplicitUsings>enable</ImplicitUsings>
66
<Nullable>enable</Nullable>
77
<RootNamespace>PlanViewer.Core</RootNamespace>
8-
<Version>1.4.0</Version>
9-
<Authors>Erik Darling</Authors>
10-
<Company>Darling Data LLC</Company>
11-
<Product>SQL Performance Studio</Product>
12-
<Copyright>Copyright (c) 2026 Erik Darling, Darling Data LLC</Copyright>
138
</PropertyGroup>
149

1510
<ItemGroup>

src/PlanViewer.Ssms.Installer/PlanViewer.Ssms.Installer.csproj

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,6 @@
66
<AssemblyName>InstallSsmsExtension</AssemblyName>
77
<RootNamespace>PlanViewer.Ssms.Installer</RootNamespace>
88
<ApplicationManifest>app.manifest</ApplicationManifest>
9-
<Version>1.3.0</Version>
10-
<Authors>Erik Darling</Authors>
11-
<Company>Darling Data LLC</Company>
12-
<Product>SQL Performance Studio</Product>
13-
<Copyright>Copyright (c) 2026 Erik Darling, Darling Data LLC</Copyright>
149
</PropertyGroup>
1510

1611
</Project>

src/PlanViewer.Ssms.Installer/Program.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ static readonly (string Label, string VsixInstallerPath)[] SsmsVersions =
1616
static int Main(string[] args)
1717
{
1818
Console.WriteLine("===========================================");
19-
Console.WriteLine(" SQL Performance Studio — SSMS Extension");
19+
Console.WriteLine(" Performance Studio — SSMS Extension");
2020
Console.WriteLine("===========================================");
2121
Console.WriteLine();
2222

0 commit comments

Comments
 (0)