From b442b8f5d668cd2779d16dd8400b92556124ea38 Mon Sep 17 00:00:00 2001 From: Erik Darling <2136037+erikdarlingdata@users.noreply.github.com> Date: Thu, 19 Mar 2026 21:21:12 -0400 Subject: [PATCH 01/31] Delete .coderabbit.yaml --- .coderabbit.yaml | 65 ------------------------------------------------ 1 file changed, 65 deletions(-) delete mode 100644 .coderabbit.yaml diff --git a/.coderabbit.yaml b/.coderabbit.yaml deleted file mode 100644 index 8eddc00..0000000 --- a/.coderabbit.yaml +++ /dev/null @@ -1,65 +0,0 @@ -# yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json - -language: "en-US" -early_access: false -enable_free_tier: true - -reviews: - profile: "chill" - high_level_summary: true - review_status: true - commit_status: true - collapse_walkthrough: true - sequence_diagrams: false - poem: false - - path_filters: - - "!**/*.Designer.cs" - - "!**/bin/**" - - "!**/obj/**" - - "!**/publish/**" - - "!**/*.user" - - "!**/*.suo" - - "!**/screenshots/**" - - path_instructions: - - path: "src/PlanViewer.App/**/*.cs" - instructions: > - Avalonia 11.3 desktop app using code-behind pattern (not MVVM). - Watch for: null reference risks, proper disposal of resources, - async/await patterns, and Avalonia-specific UI threading. - - path: "src/PlanViewer.Core/**/*.cs" - instructions: > - Core library with execution plan analysis (PlanAnalyzer), XML parsing, - and shared services. Watch for: XML parsing safety, null handling, - and performance with large execution plans. - - path: "src/PlanViewer.Mcp/**/*.cs" - instructions: > - MCP (Model Context Protocol) server integration for AI tools. - Watch for: input validation, proper error responses, and serialization safety. - - path: "tests/**/*.cs" - instructions: > - Unit and integration tests. Watch for: test isolation, - meaningful assertions, and proper test data setup. - - auto_review: - enabled: true - drafts: false - base_branches: - - "dev" - - "main" - - tools: - gitleaks: - enabled: true - github-checks: - enabled: true - -chat: - auto_reply: true - -knowledge_base: - learnings: - scope: "local" - pull_requests: - scope: "local" From 37cbe0288fda69e888d69e9aa7e54774a798f3d6 Mon Sep 17 00:00:00 2001 From: Erik Darling <2136037+erikdarlingdata@users.noreply.github.com> Date: Thu, 19 Mar 2026 21:24:37 -0400 Subject: [PATCH 02/31] Shorten update link text to 'click to install' --- src/PlanViewer.App/AboutWindow.axaml.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/PlanViewer.App/AboutWindow.axaml.cs b/src/PlanViewer.App/AboutWindow.axaml.cs index c66bdca..d5ce313 100644 --- a/src/PlanViewer.App/AboutWindow.axaml.cs +++ b/src/PlanViewer.App/AboutWindow.axaml.cs @@ -97,7 +97,7 @@ private async void CheckUpdate_Click(object? sender, RoutedEventArgs e) if (_velopackUpdate != null) { UpdateStatusText.Text = "Update available:"; - UpdateLink.Text = $"v{_velopackUpdate.TargetFullRelease.Version} — click to download and install"; + UpdateLink.Text = $"v{_velopackUpdate.TargetFullRelease.Version} — click to install"; UpdateLink.IsVisible = true; CheckUpdateButton.IsEnabled = true; return; From d16a861f1be064927a312281e2188d03269d6c79 Mon Sep 17 00:00:00 2001 From: Erik Darling <2136037+erikdarlingdata@users.noreply.github.com> Date: Fri, 20 Mar 2026 18:07:02 -0400 Subject: [PATCH 03/31] Fix Query Store time slicer metric label and chart data - Increase metric label font size from 9 to 12 for readability - Use ForegroundBrush instead of muted SlicerLabelBrush for metric label - Fix metric label showing "Total" for avg metrics (e.g., avg-cpu showed "Total CPU" instead of "Avg CPU") by splitting combined or-patterns - Add per-bucket average properties to QueryStoreTimeSlice so the slicer chart shape reflects the selected metric (total vs avg) instead of always showing totals Co-Authored-By: Claude Opus 4.6 (1M context) --- .../Controls/TimeRangeSlicerControl.axaml.cs | 40 ++++++++++++------- .../Models/QueryStoreTimeSlice.cs | 7 ++++ 2 files changed, 33 insertions(+), 14 deletions(-) diff --git a/src/PlanViewer.App/Controls/TimeRangeSlicerControl.axaml.cs b/src/PlanViewer.App/Controls/TimeRangeSlicerControl.axaml.cs index ca0d453..036aeaf 100644 --- a/src/PlanViewer.App/Controls/TimeRangeSlicerControl.axaml.cs +++ b/src/PlanViewer.App/Controls/TimeRangeSlicerControl.axaml.cs @@ -140,12 +140,18 @@ private double[] GetMetricValues() { return _metric switch { - "cpu" or "avg-cpu" => _data.Select(d => d.TotalCpu).ToArray(), - "duration" or "avg-duration" => _data.Select(d => d.TotalDuration).ToArray(), - "reads" or "avg-reads" => _data.Select(d => d.TotalReads).ToArray(), - "writes" or "avg-writes" => _data.Select(d => d.TotalWrites).ToArray(), - "physical-reads" or "avg-physical-reads" => _data.Select(d => d.TotalPhysicalReads).ToArray(), - "memory" or "avg-memory" => _data.Select(d => d.TotalMemory).ToArray(), + "cpu" => _data.Select(d => d.TotalCpu).ToArray(), + "avg-cpu" => _data.Select(d => d.AvgCpu).ToArray(), + "duration" => _data.Select(d => d.TotalDuration).ToArray(), + "avg-duration" => _data.Select(d => d.AvgDuration).ToArray(), + "reads" => _data.Select(d => d.TotalReads).ToArray(), + "avg-reads" => _data.Select(d => d.AvgReads).ToArray(), + "writes" => _data.Select(d => d.TotalWrites).ToArray(), + "avg-writes" => _data.Select(d => d.AvgWrites).ToArray(), + "physical-reads" => _data.Select(d => d.TotalPhysicalReads).ToArray(), + "avg-physical-reads" => _data.Select(d => d.AvgPhysicalReads).ToArray(), + "memory" => _data.Select(d => d.TotalMemory).ToArray(), + "avg-memory" => _data.Select(d => d.AvgMemory).ToArray(), "executions" => _data.Select(d => (double)d.TotalExecutions).ToArray(), _ => _data.Select(d => d.TotalCpu).ToArray(), }; @@ -155,12 +161,18 @@ private string GetMetricLabel() { return _metric switch { - "cpu" or "avg-cpu" => "Total CPU (ms)", - "duration" or "avg-duration" => "Total Duration (ms)", - "reads" or "avg-reads" => "Total Reads", - "writes" or "avg-writes" => "Total Writes", - "physical-reads" or "avg-physical-reads" => "Total Physical Reads", - "memory" or "avg-memory" => "Total Memory (MB)", + "cpu" => "Total CPU (ms)", + "avg-cpu" => "Avg CPU (ms)", + "duration" => "Total Duration (ms)", + "avg-duration" => "Avg Duration (ms)", + "reads" => "Total Reads", + "avg-reads" => "Avg Reads", + "writes" => "Total Writes", + "avg-writes" => "Avg Writes", + "physical-reads" => "Total Physical Reads", + "avg-physical-reads" => "Avg Physical Reads", + "memory" => "Total Memory (MB)", + "avg-memory" => "Avg Memory (MB)", "executions" => "Executions", _ => "Total CPU (ms)", }; @@ -256,8 +268,8 @@ public void Redraw() var metricTb = new TextBlock { Text = GetMetricLabel(), - FontSize = 9, - Foreground = labelBrush, + FontSize = 12, + Foreground = TryFindBrush("ForegroundBrush", new SolidColorBrush(Color.Parse("#E4E6EB"))), }; Canvas.SetRight(metricTb, 4); Canvas.SetTop(metricTb, 2); diff --git a/src/PlanViewer.Core/Models/QueryStoreTimeSlice.cs b/src/PlanViewer.Core/Models/QueryStoreTimeSlice.cs index b27ad4f..ba6c92a 100644 --- a/src/PlanViewer.Core/Models/QueryStoreTimeSlice.cs +++ b/src/PlanViewer.Core/Models/QueryStoreTimeSlice.cs @@ -15,4 +15,11 @@ public class QueryStoreTimeSlice public double TotalPhysicalReads { get; set; } public double TotalMemory { get; set; } public long TotalExecutions { get; set; } + + public double AvgCpu => TotalExecutions > 0 ? TotalCpu / TotalExecutions : 0; + public double AvgDuration => TotalExecutions > 0 ? TotalDuration / TotalExecutions : 0; + public double AvgReads => TotalExecutions > 0 ? TotalReads / TotalExecutions : 0; + public double AvgWrites => TotalExecutions > 0 ? TotalWrites / TotalExecutions : 0; + public double AvgPhysicalReads => TotalExecutions > 0 ? TotalPhysicalReads / TotalExecutions : 0; + public double AvgMemory => TotalExecutions > 0 ? TotalMemory / TotalExecutions : 0; } From 4cca2f718e65c0c37d334d1937a62b67c9cc23ab Mon Sep 17 00:00:00 2001 From: rferraton <16419423+rferraton@users.noreply.github.com> Date: Sun, 22 Mar 2026 21:33:58 +0100 Subject: [PATCH 04/31] Add Query Store wait stats: global bar/ribbon/table charts, per-row wait profile with v/% toggle, resizable separator with chevron, ribbon click/double-click interactions --- .../Controls/QueryStoreGridControl.axaml | 45 +++- .../Controls/QueryStoreGridControl.axaml.cs | 231 ++++++++++++++++-- .../Controls/WaitProfileBarControl.axaml | 9 + .../Controls/WaitProfileBarControl.axaml.cs | 145 +++++++++++ .../Controls/WaitStatsProfileControl.axaml | 48 ++++ .../Controls/WaitStatsProfileControl.axaml.cs | 132 ++++++++++ .../Controls/WaitStatsRibbonControl.axaml | 9 + .../Controls/WaitStatsRibbonControl.axaml.cs | 178 ++++++++++++++ src/PlanViewer.App/Themes/DarkTheme.axaml | 27 ++ src/PlanViewer.Core/Models/WaitStatsModels.cs | 47 ++++ .../Services/QueryStoreService.cs | 180 ++++++++++++++ 11 files changed, 1034 insertions(+), 17 deletions(-) create mode 100644 src/PlanViewer.App/Controls/WaitProfileBarControl.axaml create mode 100644 src/PlanViewer.App/Controls/WaitProfileBarControl.axaml.cs create mode 100644 src/PlanViewer.App/Controls/WaitStatsProfileControl.axaml create mode 100644 src/PlanViewer.App/Controls/WaitStatsProfileControl.axaml.cs create mode 100644 src/PlanViewer.App/Controls/WaitStatsRibbonControl.axaml create mode 100644 src/PlanViewer.App/Controls/WaitStatsRibbonControl.axaml.cs create mode 100644 src/PlanViewer.Core/Models/WaitStatsModels.cs diff --git a/src/PlanViewer.App/Controls/QueryStoreGridControl.axaml b/src/PlanViewer.App/Controls/QueryStoreGridControl.axaml index 697ad74..e3db6b7 100644 --- a/src/PlanViewer.App/Controls/QueryStoreGridControl.axaml +++ b/src/PlanViewer.App/Controls/QueryStoreGridControl.axaml @@ -4,8 +4,26 @@ x:Class="PlanViewer.App.Controls.QueryStoreGridControl" Background="{DynamicResource BackgroundBrush}"> - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + +