Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 8 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,11 @@ Side-by-side comparison of two plans showing cost, runtime, I/O, memory, and wai
### Query Store Integration
Fetch top queries by CPU, duration, logical reads, physical reads, writes, memory, or executions from Query Store and load their plans directly into the analyzer.

![Query Store Integration](screenshots/Query%20Store%20Integration.png)
![Query Store Integration](screenshots/performance_studio_querystore_analysis_top_cpu_by_query_hash.png)

### Minimap and colored links by accuracy ratio divergence
The minimap provides a high-level overview of the entire plan, allowing you to quickly navigate to areas of interest. Colored links between operators indicate accuracy ratio divergence, helping you identify where estimates are most off from actuals.
![Minimap and Colored Links](screenshots/minimap_and_planviewer_colored_actual_plan.png)

### MCP Integration
Ask Claude Code to analyze loaded plans, identify warnings, suggest indexes, and compare plans — all through the built-in MCP server.
Expand Down Expand Up @@ -299,6 +303,8 @@ Features:
- Warning badge on root node showing total warning count
- Plan Insights panel — three-column view with runtime summary, missing indexes, and wait stats visualization
- Zoom and pan (mouse wheel + middle-click drag)
- Minimap for quick navigation of large plans
- Color-coded links between operators based on accuracy ratio divergence (estimates vs actuals)
- Click any operator to see full properties (30 sections)
- Statement grid with sortable columns (cost, rows, DOP, warnings)
- Tooltips on hover with key operator metrics
Expand All @@ -308,6 +314,7 @@ Features:
- **Copy Repro Script** — extracts parameters, SET options, and query text into a runnable `sp_executesql` script
- **Get Actual Plan** — connect to a server and re-execute the query to capture runtime stats
- **Query Store Analysis** — connect to a server and analyze top queries by CPU, duration, or reads
- **Query History** — view a history of executed queries with their plans along the timeline and metrics from query store
- **MCP Server** — built-in Model Context Protocol server for AI-assisted plan analysis (opt-in)
- Dark theme

Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
88 changes: 72 additions & 16 deletions src/PlanViewer.App/Controls/PlanViewerControl.axaml
Original file line number Diff line number Diff line change
Expand Up @@ -190,7 +190,7 @@
</Border>

<!-- Statements Panel + Plan Canvas + Properties Panel -->
<Grid Grid.Row="2">
<Grid x:Name="PlanGrid" Grid.Row="2">
<Grid.ColumnDefinitions>
<!-- Col 0: Statements Panel (hidden by default) -->
<ColumnDefinition Width="0"/>
Expand Down Expand Up @@ -255,21 +255,77 @@
Background="{DynamicResource BorderBrush}"
IsVisible="False"/>

<!-- Plan Canvas -->
<ScrollViewer Grid.Column="2" x:Name="PlanScrollViewer"
HorizontalScrollBarVisibility="Auto"
VerticalScrollBarVisibility="Auto"
HorizontalContentAlignment="Left"
VerticalContentAlignment="Top"
Background="{DynamicResource BackgroundBrush}">
<LayoutTransformControl x:Name="PlanLayoutTransform"
HorizontalAlignment="Left" VerticalAlignment="Top">
<LayoutTransformControl.LayoutTransform>
<ScaleTransform ScaleX="1" ScaleY="1"/>
</LayoutTransformControl.LayoutTransform>
<Canvas x:Name="PlanCanvas" ClipToBounds="False"/>
</LayoutTransformControl>
</ScrollViewer>
<!-- Plan Canvas + Minimap overlay -->
<Grid Grid.Column="2">
<ScrollViewer x:Name="PlanScrollViewer"
HorizontalScrollBarVisibility="Auto"
VerticalScrollBarVisibility="Auto"
HorizontalContentAlignment="Left"
VerticalContentAlignment="Top"
Background="{DynamicResource BackgroundBrush}">
<LayoutTransformControl x:Name="PlanLayoutTransform"
HorizontalAlignment="Left" VerticalAlignment="Top">
<LayoutTransformControl.LayoutTransform>
<ScaleTransform ScaleX="1" ScaleY="1"/>
</LayoutTransformControl.LayoutTransform>
<Canvas x:Name="PlanCanvas" ClipToBounds="False"/>
</LayoutTransformControl>
</ScrollViewer>

<!-- Minimap toggle button (top-left, always visible) -->
<Button x:Name="MinimapToggleButton" Content="minimap"
Click="MinimapToggle_Click"
HorizontalAlignment="Left" VerticalAlignment="Top"
Margin="4,4,0,0" Padding="4,1" FontSize="9"
Height="18" MinWidth="0" MinHeight="0"
Opacity="0.8" ZIndex="10"
Theme="{StaticResource AppButton}"
ToolTip.Tip="Show/hide minimap"/>

<!-- Minimap panel -->
<Border x:Name="MinimapPanel" IsVisible="False"
HorizontalAlignment="Left" VerticalAlignment="Top"
Margin="4,26,0,0" ZIndex="10"
Width="400" Height="400"
Background="{DynamicResource BackgroundBrush}"
BorderBrush="{DynamicResource BorderBrush}" BorderThickness="5"
CornerRadius="4" ClipToBounds="True">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<!-- Minimap header with close button -->
<DockPanel Grid.Row="0" Background="{DynamicResource BackgroundBrush}">
<Button DockPanel.Dock="Right" Content="&#x2715;" Click="MinimapClose_Click"
Width="18" Height="18" Padding="0" FontSize="10"
VerticalAlignment="Center" Margin="0,0,2,0"
Theme="{StaticResource AppButton}" ToolTip.Tip="Close minimap"/>
<TextBlock Text="Minimap" FontSize="10" VerticalAlignment="Center"
Margin="4,1,0,1"
Foreground="{DynamicResource ForegroundBrush}"/>
</DockPanel>
<!-- Minimap canvas -->
<Canvas x:Name="MinimapCanvas" Grid.Row="1" ClipToBounds="True"
Background="{DynamicResource BackgroundBrush}"/>
<!-- Resize grip (bottom-right corner) -->
<Border x:Name="MinimapResizeGrip" Grid.Row="1"
Width="14" Height="14"
HorizontalAlignment="Right" VerticalAlignment="Bottom"
Background="Transparent"
Cursor="BottomRightCorner">
<Canvas Width="10" Height="10" Margin="1,1,3,3">
<Line StartPoint="6,10" EndPoint="10,6"
Stroke="{DynamicResource ForegroundBrush}" StrokeThickness="1" Opacity="0.5"/>
<Line StartPoint="3,10" EndPoint="10,3"
Stroke="{DynamicResource ForegroundBrush}" StrokeThickness="1" Opacity="0.5"/>
<Line StartPoint="0,10" EndPoint="10,0"
Stroke="{DynamicResource ForegroundBrush}" StrokeThickness="1" Opacity="0.5"/>
</Canvas>
</Border>
</Grid>
</Border>
</Grid>

<!-- Empty State -->
<StackPanel x:Name="EmptyState" Grid.Column="2" VerticalAlignment="Center" HorizontalAlignment="Center">
Expand Down
Loading
Loading