Skip to content

Release v1.11.0#341

Merged
erikdarlingdata merged 27 commits into
mainfrom
dev
May 19, 2026
Merged

Release v1.11.0#341
erikdarlingdata merged 27 commits into
mainfrom
dev

Conversation

@erikdarlingdata
Copy link
Copy Markdown
Owner

Summary

Cuts the v1.11.0 release. 26 commits since v1.10.0. Merging this PR triggers release.yml, which tags v1.11.0, builds all platforms, signs the Windows binary via SignPath (if configured), and publishes the GitHub release + Velopack channel.

Highlights

Features

Platform

Code health

CI

Test plan

  • check-version-bump sees 1.11.0 (dev) vs 1.10.0 (main) — passes
  • CI build + tests green on .NET 10
  • On merge: release.yml creates v1.11.0 tag and release with auto-generated notes
  • All four platform zips uploaded (win-x64, linux-x64, osx-x64, osx-arm64) + SHA256SUMS.txt
  • Velopack channel win updated with delta from v1.10.0
  • SignPath signs the Windows binary (or workflow logs ::warning:: if SIGNPATH_API_TOKEN is unset)
  • deploy-web.yml redeploys plans.erikdarling.com without the dropped sync step
  • First nightly build after merge picks up 1.11.0 as the base version

🤖 Generated with Claude Code

erikdarlingdata and others added 26 commits May 4, 2026 20:07
Avoids GitHub's June 2, 2026 forced migration off Node 20:
- actions/checkout v4 -> v5
- actions/setup-dotnet v4 -> v5
- actions/upload-artifact v4 -> v6 (first major to default to Node 24)
- signpath/github-action-submit-signing-request v1 -> v2

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* switch the default grid GroupBy from "None" to "Query Hash" and don't open the first line by default

* The Query Stores Overview feature has been implemented. Here's a summary of what was created:
New Files:
1. QueryStoreOverviewModels.cs — Models for:
• QueryStoreState enum (Off, ReadOnly, ReadWrite)
• DatabaseQueryStoreState — state per database
• DatabaseMetrics — aggregated metrics (total + avg) per database
• DatabaseTimeSlice — time slice data tagged by database
• DatabaseWaitCategoryTimeSlice — wait stats tagged by database
2. QueryStoreOverviewService.cs — Parallel data fetching with:
• SemaphoreSlim throttling (default DOP=8)
• ConcurrentBag<T> for thread-safe result collection
• Methods: FetchAllStatesAsync(string, int, CancellationToken), FetchAllMetricsAsync(string, List<string>, DateTime, DateTime, int, CancellationToken), FetchAllTimeSlicesAsync(string, List<string>, int, int, CancellationToken), FetchAllWaitStatsAsync(string, List<string>, DateTime, DateTime, int, CancellationToken)
3. QueryStoreOverviewControl.axaml — Layout with 3 rows:
• Row 1: Donut chart + consolidated time slicer + consolidated wait stats ribbon
• Row 2: 7 bar chart cards (Total metrics)
• Row 3: 7 bar chart cards (Avg metrics)
4. QueryStoreOverviewControl.axaml.cs — Code-behind with:
• Donut chart (RW=light blue, RO=dark blue, OFF=grey, center shows active/total)
• Consolidated time slicer (30-day, 24h default selection)
• Consolidated wait stats ribbon (sum across databases)
• Top-N bar cards with consistent database colors, adaptive font color, tooltips, and right-click "Drill Down to DB Query Store" context menu
Modified Files:
5. QuerySessionControl.axaml — Added "QS Overview" button
6. QuerySessionControl.axaml.cs — Added QueryStoreOverview_Click(object?, RoutedEventArgs) handler that opens the overview tab and wires drill-down to open single-DB Query Store tabs

* 1. Drill-down with time range: DrillDownRequested now passes a DrillDownEventArgs containing Database, StartUtc, and EndUtc. The session control calls grid.SetInitialTimeRange() before the grid auto-fetches, so the drilled-down Query Store tab starts with the same time range selected in the overview.
2.  Progress bar: Added an indeterminate ProgressBar at the top of the overview. It shows during LoadAsync() (all 3 phases) and during RefreshMetricsAndWaitStatsAsync(CancellationToken) (when the slicer range changes), and hides when complete via try/finally.

* improve the dashboard

* fix issue about waits stats on the QS overview

* fix null on WTR in sql

* add evg ref line in waitstats chart

* 1. Dead code removed — Deleted FetchDatabaseWaitStatsAsync and DatabaseWaitCategoryTimeSlice (no callers).
2. Bare catch blocks fixed — All 4 parallel fetch methods now use when (ex is not OperationCanceledException) or when (!ct.IsCancellationRequested) so cancellation propagates correctly.
3. Permission errors surfaced — Added QueryStoreState.Error enum value and ErrorMessage property to DatabaseQueryStoreState. The donut now shows a red "Error" segment, and clicking it lists databases with their error messages.
4. _cts leak fixed — Added _cts?.Dispose() before every reassignment in LoadAsync() and OnSlicerRangeChanged(object?, TimeRangeChangedEventArgs), plus a DetachedFromVisualTree handler that cancels and disposes on control teardown.
5. SizeChanged race fixed — DrawWaitStatsChart() now returns early if _dbColorMap is empty, preventing all bars from being bucketed into "Others" when a resize fires before DrawBarCards() has run.
6. Misnamed field renamed — WaitRatio → WaitAmountHours on DatabaseWaitAmountTimeSlice and all references in the service and control.

---------

Co-authored-by: Erik Darling <2136037+erikdarlingdata@users.noreply.github.com>
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>
All flagged controls (QuerySessionControl, QueryStoreGridControl,
QueryStoreOverviewControl, ConnectionDialog, QueryStoreHistoryWindow)
take typed constructor dependencies and are instantiated directly in
code-behind. They are never loaded via AvaloniaXamlLoader.Load() and
the Avalonia IDE previewer isn't used in this project, so the warning
flags scenarios that aren't load-bearing.

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- setup-dotnet@v5 with cache: true persists ~/.nuget/packages between
  runs, keyed on **/*.csproj. Cold restore (~90s) becomes a tar
  extract on cache hit.
- Replace five sequential dotnet restore + five sequential
  dotnet build invocations with one each against PlanViewer.sln.
  MSBuild builds the dependency graph in parallel internally, and
  we save four MSBuild startup cycles.
- Tests still build via the solution build, then run with --no-build.

PlanViewer.Ssms and PlanViewer.Ssms.Installer are intentionally not in
PlanViewer.sln, so they remain excluded from CI as before.

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Linux runners are ~2x faster than windows-latest on GitHub-hosted.
None of the projects in PlanViewer.sln are Windows-only:
- PlanViewer.Core, .App, .Cli are net8.0 cross-platform
- PlanViewer.Web is Blazor WASM, builds on Linux with wasm-tools
- PlanViewer.Core.Tests is xunit on net8.0
- PlanViewer.App already references SkiaSharp.NativeAssets.Linux

PlanViewer.Ssms (legacy net472 + VSSDK) is not in the solution and
is not built in CI; it stays Windows-only via its own workflow if
one exists.

If anything fails on Linux that's not flaggable as a small fix,
revert this PR and we keep the cache + solution-build wins from #317.

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
PR-only filter (push to main keeps building unconditionally). Skips
the workflow when every changed file matches one of:
- Documentation: **.md, LICENSE, CITATION.cff, llms.txt
- Repo config: .gitignore, .gitattributes, .github/ISSUE_TEMPLATE/**
- Non-CI artifacts: docs/, screenshots/, server/
- Projects intentionally excluded from PlanViewer.sln:
    src/PlanViewer.Ssms/**
    src/PlanViewer.Ssms.Installer/**

If any changed file falls outside the ignore list, the workflow runs
as before. Mirrors the path filter pattern already in deploy-web.yml.

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* Allow navigate between open tabs with keyboard #322

* Allow double-click to expand grouped rows #322

* Close tab when doing mouse wheel click #322

* improve implementation
* Allow filter by "Execution Type" #320

* Add the "Execution Type" to the grids. #320

* Make combobox wider #320

* Add the "Execution Type" text to the TextBlock #320

* Implements suggested fixes + "Failed" combo
Both methods had no callers — only FetchAggregateHistoryAsync is wired up
to QueryStoreHistoryWindow. Cleanup follows the execution_type_desc work
in #321.

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
sys.query_store_plan.query_plan is already nvarchar(max), so the
TRY_CONVERT was a no-op identity cast. It also broke the query on
databases at compatibility level <110, where the parser doesn't
recognize TRY_CONVERT and reports 'nvarchar' is not a recognized
built-in function name. Three call sites in QueryStoreService.cs:
FetchTopPlansAsync, FetchGroupedByQueryHashAsync, and
FetchGroupedByModuleAsync. Verified against a compat-100 database.

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds --execution-type to the CLI query-store command and execution_type
to the get_query_store_top MCP tool, accepting regular/aborted/exception/
failed (= aborted + exception). Mirrors the desktop UI filter added in
#321. Parsing logic is centralized in QueryStoreFilter.ParseExecutionType.

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* Add "Powered by Performance Studio" line on landing page

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* Add Darling Data favicon to web app

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* Add Open Graph and Twitter Card meta tags for social sharing

Uses the Darling Data barbell logo as hero image when shared on
social media. Also adds meta description for SEO.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* Clarify OG description: in-browser, nothing to install

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* Fix Rule 3 severity: CouldNotGenerateValidParallelPlan is actionable

This reason means something in the query blocks parallelism (scalar UDFs,
table variable inserts, etc.) — that's worth a Warning, not Info.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* Expand Rule 3 to cover all NonParallelPlanReason values

Adds human-readable messages for all 25 known reasons. Severity:
- Warning: actionable reasons (UDFs, cursors, table variables, remote
  queries, trace flags, hints, DML OUTPUT, writeback variables)
- Info: passive/environmental (cost below threshold, edition limits,
  memory-optimized tables, upgrade mode, index build edge cases)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* Split PlanViewerControl.axaml.cs into partial classes

Move-only refactor; no behavior changes. PlanViewerControl.axaml.cs
(4,497 lines) split into 7 partials:

  Rendering    (550) - RenderStatement/Nodes/Edges + edge tooltips
  Properties   (1860) - ShowPropertiesPanel + all property/runtime/wait panels
  Tooltips     (278) - BuildNodeTooltipContent + helpers
  Interaction  (327) - Node_Click, Select, zoom, pointer events, save
  Statements   (222) - statements grid panel
  Minimap      (502) - minimap render/drag/resize/navigation
  Schema       (347) - context-menu schema lookup + index/column formatting

Main file (PlanViewerControl.axaml.cs) now 504 lines — fields, brushes,
constructor, public API (LoadPlan/Clear/NavigateToNode), connection
state, and PlanConnect/PlanDatabase event handlers.

Build clean: 0 errors, 0 warnings on PlanViewer.sln.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Move-only refactor; no behavior changes. QuerySessionControl.axaml.cs
(2,251 lines) split into 8 partials:

  Editor       (286) - syntax highlighting, context menu, key/wheel, zoom
  Schema       (355) - ShowSchemaInfoAsync + Format{Indexes,Columns} + BuildWithOptions
  Connection   (120) - Connect_Click + Populate/Fetch* metadata
  Execution    (477) - Execute/EstimatedPlan/GetActualPlan + CaptureAndShowPlan
  Plans        (368) - plan tab add/close/rename/context-menu + Compare picker
  QueryStore   (267) - QueryStore_Click + Overview + OpenQueryStoreForDatabaseAsync
  Advice       ( 24) - Human/Robot advice click + ShowAdviceWindow
  Format       (124) - Format_Click + FormatOptions + CopyRepro

Main file now 177 lines — usings, ctor, fields, AXAML wiring.

Build clean: 0 errors, 0 warnings on PlanViewer.App.

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* Add "Powered by Performance Studio" line on landing page

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* Add Darling Data favicon to web app

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* Add Open Graph and Twitter Card meta tags for social sharing

Uses the Darling Data barbell logo as hero image when shared on
social media. Also adds meta description for SEO.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* Clarify OG description: in-browser, nothing to install

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* Fix Rule 3 severity: CouldNotGenerateValidParallelPlan is actionable

This reason means something in the query blocks parallelism (scalar UDFs,
table variable inserts, etc.) — that's worth a Warning, not Info.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* Expand Rule 3 to cover all NonParallelPlanReason values

Adds human-readable messages for all 25 known reasons. Severity:
- Warning: actionable reasons (UDFs, cursors, table variables, remote
  queries, trace flags, hints, DML OUTPUT, writeback variables)
- Info: passive/environmental (cost below threshold, edition limits,
  memory-optimized tables, upgrade mode, index build edge cases)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* Split PlanAnalyzer.cs into partial classes

Move-only refactor; no behavior changes. PlanAnalyzer.cs (2,238 lines)
split into 5 partials:

  Statement   (439) - AnalyzeStatement (statement-level rules)
  Node        (777) - AnalyzeNodeTree + AnalyzeNode (operator rules)
  Detection   (248) - predicate/pattern helpers (HasNotIn, IsAntiSemi,
                      IsRowstoreScan, IsProbeOnly, DetectNonSargable,
                      IsOrExpansionChain, HasAdaptiveJoin, ...)
  Timing      (345) - operator CPU/elapsed helpers + ScanImpact + memory
  Helpers     (275) - severity overrides, wait labels, format, truncate

Main file now 115 lines — class declaration, regex constants, static
dictionaries (RuleWarningTypes/WarningTypeToRule), static ctor, the
public Analyze() entry point, and the ScanImpact record.

PlanViewer.Web.csproj updated to link the new partial files.

Build clean: 0 errors, 0 warnings on PlanViewer.sln.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Move-only refactor; no behavior changes. QueryStoreGridControl.axaml.cs
(1,959 lines) split into 7 partials:

  Fetch       (452) - Fetch_Click + FetchPlans/WaitStats + grouping
  Filters     (256) - search type/value + column filter popup + ApplyFilters
  GroupBy     (161) - GroupBy + ReorderColumns + row expand/collapse
                      + ResultsGrid_DoubleTapped + ToggleRowExpansion
  Sort        (245) - sorting + UpdateBarRatios + GetSortKey + status
  Selection   (134) - select/load/ViewHistory/context menu + copy
  WaitStats   (182) - wait fetch + category click + mode toggle
  TimeRange   ( 33) - OnTimeRangeChanged

Main file now 465 lines — fields, ctor, public API
(SetInitialTimeRange), DB picker, and static accessor dictionaries.

The standalone QueryStoreRow class at the bottom of the file is
untouched.

Build clean: 0 errors, 0 warnings on PlanViewer.App.

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* Add "Powered by Performance Studio" line on landing page

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* Add Darling Data favicon to web app

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* Add Open Graph and Twitter Card meta tags for social sharing

Uses the Darling Data barbell logo as hero image when shared on
social media. Also adds meta description for SEO.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* Clarify OG description: in-browser, nothing to install

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* Fix Rule 3 severity: CouldNotGenerateValidParallelPlan is actionable

This reason means something in the query blocks parallelism (scalar UDFs,
table variable inserts, etc.) — that's worth a Warning, not Info.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* Expand Rule 3 to cover all NonParallelPlanReason values

Adds human-readable messages for all 25 known reasons. Severity:
- Warning: actionable reasons (UDFs, cursors, table variables, remote
  queries, trace flags, hints, DML OUTPUT, writeback variables)
- Info: passive/environmental (cost below threshold, edition limits,
  memory-optimized tables, upgrade mode, index build edge cases)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* Split ShowPlanParser.cs into partial classes

Move-only refactor; no behavior changes. ShowPlanParser.cs (1,844 lines)
split into 4 partials:

  RelOp      (789) - ParseRelOp (~760-line workhorse) +
                     GetOperatorElement + FindChildRelOps
  Warnings   (323) - ParseWarnings + ParseWarningsFromElement +
                     ParseMissingIndexes
  Costs      ( 25) - ComputeOperatorCosts + ComputeNodeCosts
  Helpers    ( 66) - CleanTempTableName, IsHexDigit, ScopedDescendants,
                     ParseColumnList, FormatColumnRef, ParseDouble/Long

Main file now 628 lines — class declaration, XML namespace, the public
Parse entry, ParseStatement{,AndChildren,Attributes},
ParseQueryPlanElements / ParseQueryPlanAsStatement.

PlanViewer.Web.csproj updated to link the new partial files.

Build clean: 0 errors, 0 warnings on PlanViewer.sln.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Move-only refactor; no behavior changes. QueryStoreService.cs (1,403
lines) split into 3 partials:

  Grouped    (584) - FetchGroupedByQueryHashAsync +
                     FetchGroupedByModuleAsync
  WaitStats  (232) - IsWaitStatsCaptureEnabledAsync +
                     FetchGlobalWaitStatsAsync +
                     FetchPlanWaitStatsAsync +
                     FetchGlobalWaitStatsRibbonAsync +
                     BuildWaitProfile

Main file now 584 lines — top-of-pipe fetches: CheckEnabledAsync,
FetchTopPlansAsync, FetchAggregateHistoryAsync, FetchTimeSliceDataAsync.

Class made `static partial`.

Build clean: 0 errors, 0 warnings on PlanViewer.Core.

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Move-only refactor; no behavior changes. MainWindow.axaml.cs (1,380
lines) split into 4 partials:

  FileOps     (260) - File open/paste/drag-drop, LoadSqlFile,
                      LoadPlanFile, ValidatePlanXml, Save/RestoreOpenPlans
  Tabs        (209) - CreateTab, Close/Context menu, StartRename,
                      GetTabFilePath/Label, GetQueryTextFromPlan
  PlanViewer  (468) - CreatePlanTabContent, ShowAdviceWindow,
                      ShowCompareDialog, GetActualPlanFromFile,
                      ExtractDatabaseFromPlanXml
  RecentPlans ( 78) - TrackRecentPlan, RebuildRecentPlansMenu,
                      RecentPlanItem_Click, ClearRecentPlans_Click

Main file now 342 lines — fields, ctor (drag-drop wiring, hotkeys,
command-line args, MCP start), StartPipeServer/StartMcpServer, OnClosed,
Exit_Click, About_Click, UpdateEmptyOverlay, ShowError,
CheckForUpdatesOnStartupAsync.

Build clean: 0 errors on PlanViewer.App (existing pre-existing CS0618
on DragEventArgs.Data is unchanged).

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* Add "Powered by Performance Studio" line on landing page

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* Add Darling Data favicon to web app

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* Add Open Graph and Twitter Card meta tags for social sharing

Uses the Darling Data barbell logo as hero image when shared on
social media. Also adds meta description for SEO.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* Clarify OG description: in-browser, nothing to install

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* Fix Rule 3 severity: CouldNotGenerateValidParallelPlan is actionable

This reason means something in the query blocks parallelism (scalar UDFs,
table variable inserts, etc.) — that's worth a Warning, not Info.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* Expand Rule 3 to cover all NonParallelPlanReason values

Adds human-readable messages for all 25 known reasons. Severity:
- Warning: actionable reasons (UDFs, cursors, table variables, remote
  queries, trace flags, hints, DML OUTPUT, writeback variables)
- Info: passive/environmental (cost below threshold, edition limits,
  memory-optimized tables, upgrade mode, index build edge cases)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* Split AdviceContentBuilder.cs into partial classes

Move-only refactor; no behavior changes. AdviceContentBuilder.cs (1,227
lines) split into 4 partials:

  NodeLinks  (243) - MakeNodeRefsClickable + ProcessInlines +
                     AddRunsWithNodeLinks + WireNodeClickHandler
  Operators  (251) - CreateOperatorLine/Group/TimingLine +
                     CreateWarningBlock
  WaitStats  (242) - CreateWaitStatLine + CreateMissingIndexImpactLine +
                     ParseWaitMs + GetWaitCategoryBrush +
                     CreateTriageSummaryCard
  Sql        ( 36) - BuildSqlHighlightedLine

Main file now 444 lines — brushes/fonts, PhysicalOperators &
SqlKeywords sets, regex constants, the 3 Build() overloads (entry
points), IsSubSectionLabel helper.

Class made `internal static partial`.

Build clean: 0 errors on PlanViewer.App.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Both are pre-existing bugs surfaced during smoke-testing of the
god-file split; neither was caused by the move-only refactor.

1. Run Repro from a file-loaded plan produced an actual-plan tab with
   schema lookups (Show Indexes / Show Table Definition) greyed out.
   The new PlanViewerControl wasn't given the connection used to run
   the repro. Now inherits ConnectionString + connection services +
   status, matching the query-editor Run Repro path.

2. QuerySessionControl toolbar overflowed off-screen on narrow windows
   — the Format / Format Options buttons were unreachable. Switched the
   toolbar from a single-row StackPanel to a WrapPanel so buttons wrap
   to a second row instead of being cut off.

Build clean: 0 errors on PlanViewer.sln.

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Corporate-proxy users were hitting "Check for Updates" failures because
neither Velopack's downloader nor the HttpClient fallback was sending
Windows credentials to the proxy. James (issue #314) confirmed his proxy
is auto-discovered for most apps but SSMS 25 needed manual address +
on-prem AD user/pass, so both paths are needed.

* ProxySettings — "System" (default) sends Windows credentials to the
  auto-discovered proxy; "Manual" lets the user enter address + optional
  username/password. Password is held in the existing ICredentialService
  (Windows Credential Manager / macOS Keychain).
* ProxyHttpHandlerFactory — single source of truth for proxy wiring on
  HttpClientHandler. Used by both UpdateChecker (GitHub API fallback)
  and ProxyAwareDownloader (Velopack's IFileDownloader override).
* ProxyAwareDownloader subclasses Velopack.Sources.HttpClientFileDownloader
  and snapshots ProxySettings at construction so retries/redirects don't
  re-read DPAPI.
* SettingsFile — merge-safe JSON read/update so the MCP and Proxy save
  paths don't stomp each other.
* AboutWindow — proxy settings UI (radio + collapsed manual grid). The
  password field is intentionally not round-tripped through the UI;
  watermark says "(saved — leave blank to keep)". Status text now wraps.
  When the update check fails, a "Open the releases page in your browser"
  link is shown as a manual-download fallback.
* If Velopack itself fails (the actual #314 symptom), its error is now
  surfaced alongside the API fallback error instead of being swallowed.

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- TargetFramework: net8.0 -> net10.0 across all SDK-style projects
  (App, Core, Cli, Web, PlanShare, Tests). SSMS extension and its
  installer stay on net472 (VS extension constraint).
- ModelContextProtocol/ModelContextProtocol.AspNetCore: 1.2.0 -> 1.3.0
- Microsoft.NET.Test.Sdk: 18.4.0 -> 18.5.1
- Microsoft.AspNetCore.Components.WebAssembly + DevServer: 8.0.12 -> 10.0.0
- CI workflows (ci, nightly, release, deploy-web): dotnet-version 8.0.x -> 10.0.x
- Doc/comment refs to .NET 8 updated to .NET 10
* Fix sort issues on the QueryHistory

* What changed
1. QueryStoreGridControl.Selection.cs — ViewHistory_Click now walks up the visual tree to find the parent QuerySessionControl and calls AddHistorySubTab(), placing the history as a sub-tab alongside "Query Store — DB" and "QS Overview" (instead of at the top-level MainTabControl).
2. QuerySessionControl.QueryStore.cs — Added two new methods:
• AddHistorySubTab() — creates a closeable sub-tab with the history control, including long-press (~500ms) to detach into a free-floating window
• DetachHistorySubTabToWindow() — pops the history content into a standalone non-modal window (movable to another screen). When that window is minimized or closed, the content automatically re-docks back as a sub-tab
3. MainWindow.Tabs.cs — Removed the now-unused AddHistoryTab() method since history tabs no longer live at the top level

User flow
• View History → opens as a sub-tab next to Query Store / QS Overview
• Long-press the history sub-tab → detaches into a free window (can move to another screen)
• Minimize or close the free window → content returns to the sub-tab

* What was added
1. QueryStoreService.FetchPlanByHashAsync() — New method that fetches a plan XML from sys.query_store_plan by query_plan_hash. The oldest parameter controls whether it returns the first (smallest plan_id) or last (largest plan_id) plan.
2. Context menu on QueryStoreHistoryControl — Right-clicking on the DataGrid or chart shows:
• "Load the First Plan" — fetches the oldest plan for the selected plan hash
• "Load the Last Plan" — fetches the most recent plan for the selected plan hash
The selected plan hash is determined from either the grid selection or chart selection.
3. PlanLoadRequested event — Raised when the user picks a plan from the context menu. The parent QuerySessionControl subscribes to this event and opens the plan XML as a new sub-tab (using the existing AddPlanTab mechanism).

User flow
• Select a row in the history grid (or a dot on the chart)
• Right-click → "Load the First Plan" or "Load the Last Plan"
• The plan opens as a new sub-tab (e.g., "QS 42 / 17")

* Code review :

Bugs Fixed
1. Shared ContextMenu — BuildContextMenu() now creates separate ContextMenu instances for DataGrid and Chart via CreatePlanContextMenu()
2. Silent catch — LoadPlanFromSelection now shows errors in StatusText with catch (Exception ex)
3. Wrong type check — Close_Click now checks is not PlanViewer.App.MainWindow instead of IClassicDesktopStyleApplicationLifetime
4. Event handler leak — AddHistorySubTab now unsubscribes before subscribing: -= OnHistoryPlanLoadRequested before +=
Improvements
5. Loading feedback — Shows "Loading plan…" / "Plan not found" / error in StatusText
6. Disable menu when no selection — Opening handler disables items when GetSelectedPlanHash() is null
7. IndexOf — Added clarifying comment (list is <500 items, no better API available)
8. ScrollIntoView — Moved after ItemsSource reset so the target row exists
9. Long-press duplication — Extracted TabHeaderLongPressBehavior helper, used in both MainWindow.Tabs.cs and QuerySessionControl.QueryStore.cs
10. HistoryPlanLoadEventArgs — Moved to its own file
Nits
11. Removed unnecessary ?. on non-nullable orderBy parameter
12. Changed bare catch to catch (Exception) in tooltip handler
13. Changed bare catch to catch (Exception ex) in LoadPlanFromSelection

* When the MainWindow closes, it now iterates all open windows via IClassicDesktopStyleApplicationLifetime.Windows and closes any that aren't the MainWindow itself. This ensures all detached free-floating windows (from tab detach, history detach, advice windows, etc.) are closed when the app's main window is closed.

* Must-fix fixed

* use pin/unpin button to fix ux-must-fix

* fix error truncation to 80 chars : truncation removed

* fix most remaining nice to have

* add spinner and cancel for history fetches
- Replace deprecated DataGridRow.GetIndex() with the Index property
- Remove dead _suppressGridSelectionEvent field; the suppression was
  only needed for the old ItemsSource-reset highlight path, which was
  refactored away

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
PR #315 moved <Version> from App.csproj into src/Directory.Build.props
but left four workflows still parsing the old path — every one of them
would have silently produced an empty VERSION on the next release:

- release.yml: would tag as "v" and break Velopack pack/upload
- check-version-bump.yml: empty == empty would block legit bumps
- nightly.yml: nightly tag becomes "-nightly.YYYYMMDD"
- deploy-web.yml: also tried to write <Version> back into Web csproj,
  which no longer has one — drop the obsolete sync step entirely
  (both csprojs now inherit from Directory.Build.props automatically)

Version bumps for 1.11.0:
- src/Directory.Build.props 1.10.0 -> 1.11.0
- PlanViewer.Ssms/Properties/AssemblyInfo.cs 1.10.0.0 -> 1.11.0.0
- PlanViewer.Ssms/source.extension.vsixmanifest 1.10.0 -> 1.11.0

Also ignore untracked local tools/ helper directory.

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…s missing (#342)

Bootstrap fix for the v1.10.0 -> v1.11.0 release PR (#341). PR #315 (the
version-unification refactor that introduced Directory.Build.props)
landed on dev AFTER v1.10.0 was tagged, so main has never seen
Directory.Build.props — the main-side checkout step fails on Get-Content
with "Cannot find path".

Test-Path now prefers Directory.Build.props and falls back to the legacy
App.csproj location. Becomes dead code after #341 lands on main, but
harmless and self-documenting.

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@erikdarlingdata erikdarlingdata merged commit d6eeec5 into main May 19, 2026
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants