Skip to content

Commit 78a3370

Browse files
Stop the wait stats rows clipping their trailing benefit text
Round-1 finding V4: wait rows clipped mid-word, so "up to 27%" arrived as "up to", and the panel grew a horizontal scrollbar to show the rest. The row grid put the bar in the flexible column and left the wait type, duration and benefit on Auto, so when the strip narrowed there was nothing left to give and the whole row overflowed to the right. The flexible column is now the duration, which is the one piece the bar beside it already conveys. The wait type is capped at 150px and ellipsizes with the full type in its tooltip, the bar is fixed, and the trailing "up to N%" sits in an Auto column so it is never what gets cut. The panel's ScrollViewer no longer scrolls horizontally at all. Every row also carries a tooltip now: the wait type says which category it fell into, the duration spells itself out, and the benefit says what the percentage actually means. Colours move onto the token set. The category palette maps I/O, Lock and Network onto WarningBrush, ErrorBrush and SuccessBrush, which already mean the same thing elsewhere; CPU takes the panel's own InsightWaitsBrush; Memory borrows InsightServerBrush as the one remaining token that stays distinct; and "Other", the unclassified bucket, takes the neutral muted foreground it always meant. The bars drop the hand-rolled alpha blend and use the category brush at 0.38 opacity, so a token change carries into them for free. The header also loses the two leading spaces it had when populated but not when empty, which made it jump sideways as plans loaded. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017DEJ8FiSKJg44Ew2GG2nQ7
1 parent 87bad14 commit 78a3370

1 file changed

Lines changed: 45 additions & 21 deletions

File tree

‎src/PlanViewer.App/Controls/PlanViewerControl.WaitStats.cs‎

Lines changed: 45 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -27,10 +27,12 @@ private void ShowWaitStats(List<WaitStatInfo> waits, List<WaitBenefit> benefits,
2727
? "No wait stats recorded"
2828
: "No wait stats (estimated plan)";
2929
WaitStatsEmpty.IsVisible = true;
30+
SetInsightQuiet(WaitStatsHeader, WaitStatsAccent, true);
3031
return;
3132
}
3233

3334
WaitStatsEmpty.IsVisible = false;
35+
SetInsightQuiet(WaitStatsHeader, WaitStatsAccent, false);
3436

3537
// Build benefit lookup
3638
var benefitLookup = new Dictionary<string, double>(StringComparer.OrdinalIgnoreCase);
@@ -41,44 +43,53 @@ private void ShowWaitStats(List<WaitStatInfo> waits, List<WaitBenefit> benefits,
4143
var maxWait = sorted[0].WaitTimeMs;
4244
var totalWait = sorted.Sum(w => w.WaitTimeMs);
4345

44-
// Update expander header with total
45-
WaitStatsHeader.Text = $" Wait Stats \u2014 {totalWait:N0}ms total";
46+
WaitStatsHeader.Text = $"Wait Stats \u2014 {totalWait:N0}ms total";
4647

47-
// Build a single Grid for all rows so columns align
48-
// Name, bar, duration, and benefit columns
48+
/* One Grid for all rows so the columns align. Only the duration column flexes (round-1
49+
finding V4): the wait type is capped and ellipsized, the bar is fixed, and the trailing
50+
"up to N%" sits in an Auto column so it is never the thing that gets clipped. The panel's
51+
horizontal scrolling is off, so squeezing the strip narrows the duration rather than
52+
growing a sideways scrollbar. */
4953
var grid = new Grid
5054
{
51-
ColumnDefinitions = new ColumnDefinitions("Auto,*,Auto,Auto")
55+
ColumnDefinitions = new ColumnDefinitions("Auto,Auto,*,Auto")
5256
};
5357
for (int i = 0; i < sorted.Count; i++)
5458
grid.RowDefinitions.Add(new RowDefinition(GridLength.Auto));
5559

60+
var durationBrush = FindBrushResource("ForegroundBrush");
61+
var benefitBrush = FindBrushResource("ForegroundMutedBrush");
62+
5663
for (int i = 0; i < sorted.Count; i++)
5764
{
5865
var w = sorted[i];
5966
var barFraction = maxWait > 0 ? (double)w.WaitTimeMs / maxWait : 0;
60-
var color = GetWaitCategoryColor(GetWaitCategory(w.WaitType));
67+
var category = GetWaitCategory(w.WaitType);
68+
var categoryBrush = FindBrushResource(GetWaitCategoryBrushKey(category));
6169

62-
// Wait type name — colored by category
70+
// Wait type name, colored by category. Capped so one long type cannot widen every row.
6371
var nameText = new TextBlock
6472
{
6573
Text = w.WaitType,
6674
FontSize = 12,
67-
Foreground = new SolidColorBrush(Color.Parse(color)),
75+
Foreground = categoryBrush,
76+
MaxWidth = 150,
77+
TextTrimming = TextTrimming.CharacterEllipsis,
6878
VerticalAlignment = VerticalAlignment.Center,
6979
Margin = new Thickness(0, 2, 10, 2)
7080
};
81+
ToolTip.SetTip(nameText, $"{w.WaitType} \u2014 {category} wait");
7182
Grid.SetRow(nameText, i);
7283
Grid.SetColumn(nameText, 0);
7384
grid.Children.Add(nameText);
7485

75-
// Bar — semi-transparent category color, compact proportional indicator
76-
var barColor = Color.Parse(color);
86+
// Bar: the category color at low opacity, a compact proportional indicator
7787
var colorBar = new Border
7888
{
7989
Width = Math.Max(4, barFraction * 60),
8090
Height = 14,
81-
Background = new SolidColorBrush(Color.FromArgb(0x60, barColor.R, barColor.G, barColor.B)),
91+
Background = categoryBrush,
92+
Opacity = 0.38,
8293
CornerRadius = new CornerRadius(2),
8394
HorizontalAlignment = HorizontalAlignment.Left,
8495
VerticalAlignment = VerticalAlignment.Center,
@@ -88,15 +99,17 @@ private void ShowWaitStats(List<WaitStatInfo> waits, List<WaitBenefit> benefits,
8899
Grid.SetColumn(colorBar, 1);
89100
grid.Children.Add(colorBar);
90101

91-
// Duration text
102+
// Duration text: the flexible column, so this is what gives when the strip is narrow
92103
var durationText = new TextBlock
93104
{
94105
Text = $"{w.WaitTimeMs:N0}ms ({w.WaitCount:N0} waits)",
95106
FontSize = 12,
96-
Foreground = new SolidColorBrush(Color.Parse("#E4E6EB")),
107+
Foreground = durationBrush,
108+
TextTrimming = TextTrimming.CharacterEllipsis,
97109
VerticalAlignment = VerticalAlignment.Center,
98110
Margin = new Thickness(0, 2, 8, 2)
99111
};
112+
ToolTip.SetTip(durationText, $"{w.WaitTimeMs:N0} ms across {w.WaitCount:N0} waits");
100113
Grid.SetRow(durationText, i);
101114
Grid.SetColumn(durationText, 2);
102115
grid.Children.Add(durationText);
@@ -108,10 +121,12 @@ private void ShowWaitStats(List<WaitStatInfo> waits, List<WaitBenefit> benefits,
108121
{
109122
Text = $"up to {benefitPct:N0}%",
110123
FontSize = 11,
111-
Foreground = new SolidColorBrush(Color.Parse("#8b949e")),
124+
Foreground = benefitBrush,
112125
VerticalAlignment = VerticalAlignment.Center,
113126
Margin = new Thickness(0, 2, 0, 2)
114127
};
128+
ToolTip.SetTip(benefitText,
129+
$"Up to {benefitPct:N0}% of this statement's runtime could be recovered by removing {w.WaitType} waits");
115130
Grid.SetRow(benefitText, i);
116131
Grid.SetColumn(benefitText, 3);
117132
grid.Children.Add(benefitText);
@@ -149,16 +164,25 @@ private static string GetWaitCategory(string waitType)
149164
return "Other";
150165
}
151166

152-
private static string GetWaitCategoryColor(string category)
167+
/// <summary>
168+
/// The theme token each wait category is drawn in.
169+
///
170+
/// <para>I/O, Lock and Network map onto the semantic tokens that already mean the same thing
171+
/// elsewhere (warning, error, ok). CPU takes the Wait Stats panel's own accent. Memory has no
172+
/// semantic token of its own, so it borrows the one token left that stays distinct from the
173+
/// other five. "Other" is the unclassified bucket and gets the neutral muted foreground, which
174+
/// is what it always meant.</para>
175+
/// </summary>
176+
private static string GetWaitCategoryBrushKey(string category)
153177
{
154178
return category switch
155179
{
156-
"CPU" => "#4FA3FF",
157-
"I/O" => "#FFB347",
158-
"Lock" => "#E57373",
159-
"Memory" => "#9B59B6",
160-
"Network" => "#2ECC71",
161-
_ => "#6BB5FF"
180+
"CPU" => "InsightWaitsBrush",
181+
"I/O" => "WarningBrush",
182+
"Lock" => "ErrorBrush",
183+
"Memory" => "InsightServerBrush",
184+
"Network" => "SuccessBrush",
185+
_ => "ForegroundMutedBrush"
162186
};
163187
}
164188
}

0 commit comments

Comments
 (0)