Skip to content

Polish Lite chart axes and sub-tab styling#862

Merged
erikdarlingdata merged 1 commit intodevfrom
ui/lite-chart-and-tab-polish
Apr 18, 2026
Merged

Polish Lite chart axes and sub-tab styling#862
erikdarlingdata merged 1 commit intodevfrom
ui/lite-chart-and-tab-polish

Conversation

@erikdarlingdata
Copy link
Copy Markdown
Owner

Summary

Small set of Lite UI polish items reviewed in-session:

  • Chart X-axis date ticks — the date line now prints only on the first tick and on ticks where the date changes; all other ticks show time only. Format respects the current culture (en-GB shows dd/MM, de-DE shows dd.MM, 24-hour clocks, etc.). Implemented as a DateTimeTicksBottomDateChange() extension in Lite/Helpers/AxesExtensions.cs and applied at every DateTimeTicksBottom() call site in ServerTab and CorrelatedTimelineLanesControl.
  • Server name deduped in headerConnectionStatusText no longer prefixes the server name before Last refresh: … since ServerNameText already shows it. Initial value is Connecting... instead of the server name.
  • Chart tick font size bumped from 12 → 13 for readability, applied in both ApplyTheme and ReapplyAxisColors so it survives DateTimeTicksBottom resets.
  • SubTabItemStyle added to all three themes (Dark / Light / CoolBreeze) and wired up on the six sub-TabControls in ServerTab.xaml (Queries, Memory, File I/O, Blocking, plus two unnamed). Sub-tabs now use a thin accent underline with transparent background instead of the filled-cyan block, so sub-tab selection no longer looks identical to main-tab selection.

Test plan

  • Launch Lite in Dark / Light / CoolBreeze themes; verify sub-tabs render with accent underline, main tabs still filled
  • Connect to any server; watch the header — Connecting...Last refresh: HH:mm:ss (…) with no duplicate server name
  • View any trend chart that spans < 24 hours — date shows once on first tick, time-only on the rest
  • View any trend chart spanning a midnight boundary — date appears again at the rollover tick
  • Switch Windows display language (or use CultureInfo locally) to en-GB / de-DE and verify date format and 24-hour times
  • Verify Overview tab's File I/O lane (the only lane with visible bottom ticks) also follows the new formatter

🤖 Generated with Claude Code

- Chart X-axis prints the date line only on the first tick and on ticks
  where the date changes; all other ticks show time only. Format respects
  current culture (en-GB → dd/MM, de-DE → dd.MM, 24h clocks, etc.).
  Implemented as a DateTimeTicksBottomDateChange() extension in
  Lite/Helpers/AxesExtensions.cs and applied to every DateTimeTicksBottom
  call site in ServerTab and CorrelatedTimelineLanesControl.
- Server name no longer duplicated in the ServerTab header status line;
  ConnectionStatusText now shows just "Connecting..." / "Last refresh: ...".
- Chart tick label font bumped from 12 to 13 for readability.
- New SubTabItemStyle (thin accent underline, transparent background) in
  all three themes, applied to Queries / Memory / File I/O / Blocking /
  Perfmon / Running Jobs sub-TabControls so sub-tab selection no longer
  looks identical to main-tab selection.

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

@erikdarlingdata erikdarlingdata left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review summary

What this does
Lite UI polish: (1) new DateTimeTicksBottomDateChange() extension replaces DateTimeTicksBottom() at every call site in ServerTab.xaml.cs and CorrelatedTimelineLanesControl.xaml.cs, printing the date only when it changes; (2) ConnectionStatusText stops duplicating the server name already shown in ServerNameText; (3) chart tick font bumped 12 → 13 in both ApplyTheme and ReapplyAxisColors; (4) new SubTabItemStyle added to Dark/Light/CoolBreeze themes and applied to the six sub-TabControls in ServerTab.xaml.

Process checks

  • Base branch is dev
  • Lite-only — no Dashboard port needed, and Lite-first is the correct ordering ✓
  • No PlanAnalyzer.cs changes — sync check N/A
  • No SQL / install / upgrade changes — schema-path check N/A
  • No .github/workflows/build.yml changes — SignPath check N/A

Needs attention (details inline)

  1. Lite/Helpers/AxesExtensions.cs:31-42 — the lastDate closure persists across ScottPlot render passes, so after a zoom/pan the first visible tick of the new view may not show the date. This is the main correctness issue.
  2. Lite/Controls/ServerTab.xaml.cs:844 — header time string is still hardcoded HH:mm:ss while the chart-tick formatter this PR adds is culture-aware; minor inconsistency.
  3. Lite/Themes/{Dark,Light,CoolBreeze}Theme.xaml SubTabItemStyle — custom ControlTemplate has no keyboard-focus visual; minor a11y nit.

Other notes

  • BuildMonthDayPattern() at AxesExtensions.cs:12-19 — the regex-based stripping of the year component will produce unexpected output for cultures whose ShortDatePattern has the year in the middle (rare, but e.g. some locale customisations). The fallback to "M/d" only triggers if the result is whitespace, not if it's malformed. Low risk but worth a glance.
  • Test plan in the PR description is appropriate for UI polish; no unit tests warranted.

Generated by Claude Code

Comment on lines +31 to +42
DateTime? lastDate = null;
var culture = CultureInfo.CurrentCulture;
gen.LabelFormatter = dt =>
{
var time = dt.ToString("t", culture);
if (lastDate is null || dt.Date != lastDate.Value)
{
lastDate = dt.Date;
return $"{dt.ToString(MonthDayPattern, culture)}\n{time}";
}
return time;
};
Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Correctness concern: lastDate is captured in a closure and persists across ScottPlot render passes after the formatter is installed. The state is only reset when DateTimeTicksBottomDateChange() is called again (new closure), which happens on full chart refresh — but not on zoom/pan, where ScottPlot re-invokes the existing LabelFormatter for new visible ticks.

Concrete failure mode: chart shows Apr 15 → Apr 18. Last rendered tick is Apr 18, so lastDate ends at Apr 18. User zooms into a 1-hour window within Apr 18. All visible ticks have dt.Date == lastDate.Value, so the formatter returns time-only for every one of them — the date line never appears in the zoomed view, even though the contract is "first tick gets the date."

There's also an ordering assumption: the state machine only works if ScottPlot iterates ticks in chronological order. If it ever calls the formatter out of order (or twice for the same tick during layout), the output will flicker.

Safer approach: compute the "show date?" decision from the tick positions array after ticks are generated, rather than via stateful closure. ScottPlot's DateTimeAutomatic exposes Ticks after regeneration — you can iterate once and assign labels via a precomputed dictionary, or hook the regeneration event if one is available.


Generated by Claude Code


var tz = ServerTimeHelper.GetTimezoneLabel(ServerTimeHelper.CurrentDisplayMode);
ConnectionStatusText.Text = $"{_server.ServerNameDisplay} - Last refresh: {DateTime.Now:HH:mm:ss} ({tz})";
ConnectionStatusText.Text = $"Last refresh: {DateTime.Now:HH:mm:ss} ({tz})";
Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Minor inconsistency: the chart-tick formatter this PR introduces is culture-aware (uses "t" and culture-specific month-day pattern), but this status line is still hardcoded HH:mm:ss. For an en-US user the chart shows 3:45 PM while the header shows 15:45:03. Either pick one policy or use DateTime.Now.ToString("T", CultureInfo.CurrentCulture) for consistency.


Generated by Claude Code

Comment on lines +606 to +623
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="TabItem">
<Border x:Name="border"
Background="{TemplateBinding Background}"
BorderBrush="{StaticResource AccentBrush}"
BorderThickness="0"
Padding="{TemplateBinding Padding}">
<ContentPresenter ContentSource="Header"
HorizontalAlignment="Center"
VerticalAlignment="Center"/>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsSelected" Value="True">
<Setter TargetName="border" Property="BorderThickness" Value="0,0,0,2"/>
<Setter Property="Foreground" Value="{StaticResource ForegroundBrush}"/>
</Trigger>
<Trigger Property="IsMouseOver" Value="True">
Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The custom ControlTemplate replaces the default TabItem template without setting a FocusVisualStyle or any visual cue for IsKeyboardFocused/IsFocused. Users navigating by keyboard won't see which sub-tab is focused when tabbing through the UI. Consider adding a FocusVisualStyle setter or a trigger on IsKeyboardFocused that changes the border/background. Same applies to the identical style in LightTheme.xaml and CoolBreezeTheme.xaml.


Generated by Claude Code

@erikdarlingdata erikdarlingdata merged commit 333a8f7 into dev Apr 18, 2026
3 checks passed
@erikdarlingdata erikdarlingdata deleted the ui/lite-chart-and-tab-polish branch April 18, 2026 16:01
erikdarlingdata added a commit that referenced this pull request Apr 19, 2026
Dashboard polish (ports the same items merged to Lite in #862):
- New Dashboard/Helpers/AxesExtensions.cs with DateTimeTicksBottomDateChange(),
  culture-aware (dd/MM for en-GB, dd.MM for de-DE, 24h clocks, etc.). All 52
  call sites of DateTimeTicksBottom() across 10 files swapped to use it.
- TabHelpers.ApplyTheme + ReapplyAxisColors bump chart tick label font from
  12 to 13 so numbers read cleaner on wide charts.
- SubTabItemStyle added to Dark / Light / CoolBreeze themes: thin accent
  underline + transparent background instead of filled cyan, so sub-tabs
  don't look identical to main tabs when selected. Wired via
  ItemContainerStyle on 11 sub-TabControls (Overview's inner tabs,
  Collection Health's inner tabs, Locking, ConfigChanges, CurrentConfig,
  FinOps, Memory, ResourceMetrics ×2, SystemEvents, QueryPerformance).

LSP diagnostics cleanup (tracked work from chore/lsp-diagnostics-cleanup):
- Small nullability/warning fixes across Dashboard and Lite services,
  analysis helpers, and BenefitScorer / PlanAnalyzer.

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

1 participant