Skip to content

Theme the editor with ktsu.ThemeProvider, and stop clipping tree labels - #142

Merged
matt-edmondson merged 2 commits into
mainfrom
claude/project-continuation-qgaxsq
Sep 5, 2026
Merged

Theme the editor with ktsu.ThemeProvider, and stop clipping tree labels#142
matt-edmondson merged 2 commits into
mainfrom
claude/project-continuation-qgaxsq

Conversation

@matt-edmondson

@matt-edmondsonmatt-edmondson commented Sep 4, 2026

Copy link
Copy Markdown
Contributor

Two things visible in a screenshot of the running editor.

1. Everything was blue

OnRender wrapped every frame in Theme.FromColor(Palette.Semantic.Primary). FromColor is documented as a scoped colour for one widget — it is what marks an element carrying an error or a warning — so applying it to the whole frame tinted the entire interface with the primary colour and left an ordinary button looking exactly like an emphasised one. The application-wide look is what ktsu.ThemeProvider is for, and the editor had no theme at all.

So the editor now applies a real theme: read from settings on start, defaulting to VSCode Dark, with a Theme menu that opens the registry's browser (38 themes — Catppuccin, Dracula, Everforest, Gruvbox, Kanagawa, Monokai, Nord, One Dark, Tokyo Night, VSCode…) and remembers the choice. The saved name is resolved against the registry rather than trusted, so a theme renamed or dropped upstream falls back instead of leaving the editor unstyled.

Verified by rendering, not by assuming

SchemaEditor.Test renders through a software rasterizer with no display, so the change could be looked at rather than assumed. Capturing the same scene before and after:

  • Before — the primary colour on every widget: the tree rows, the name field, the description box, the section headers and the member rows are all the same blue, and the red "Save Now" banner competes with them rather than standing out.
  • After — themed neutrals with readable text, and the error banner is the only thing shouting.

App.Capture().SavePng(path) reproduces both; that is now documented in the development guide as the way to check a visual change headlessly.

Where it lives

EditorTheme is a new type rather than more surface on SchemaEditor, because the registry's types push that class past CA1506's coupling limit — the same budget Program.cs already warns about in its own doc comment.

It also became the one place that says how a validation issue is coloured. That severity ternary was written out four times (ButtonTree, the diagnostics list, the summary, the member row) and can no longer drift apart:

internalstaticScopedThemeColorSeverity(SchemaValidationSeverityseverity)=>severity==SchemaValidationSeverity.Error?Error():Warning();

2. "Code Generators (" lost its count

Tree rows were drawn at a fixed width — 15% of the display width — and ImGui clips a button's label to its frame. "Code Generators (0)" is the longest label in the tree, and at the window size this was reported from it did not fit. The width is a minimum now: short labels still line up as a column, a long one grows to fit.

The test starts the harness at 700px wide, because at the default 1280 every heading fits and there is nothing to prove — which is exactly why my first version of that test passed against the unfixed code. Reverting RowWidth to the fixed width fails both width tests; I checked.

Tests

13 new tests, 395 total, 0 failures.

  • Theme resolution — settings naming no theme get the default; a named theme is applied; a theme no longer in the registry falls back; the saved theme is applied on start; and the editor never draws a frame with no theme applied, which is the property that stops the blanket-tint approach returning as "no theme at all".
  • Choosing a theme — the browser marks each of its cards, so three tests open it, click one, and check the theme is applied, reaches the settings, and comes back on the next start. The cards named are ones near the top of the grid: it scrolls, and a card below the fold is recorded at a position that has been clipped away, so clicking it hits the modal behind. Nord is the thirtieth card and that is exactly what happened to it first time.
  • The error colour — measured on screen, because that is what this change was for. Point a member at a class that is not there and the member's row gains red pixels; add a class with an empty name and the menu bar's summary does. They count clearly-red pixels rather than an exact value, so they do not depend on the shade VSCode Dark picks.
  • Row width — a longer heading and a longer class name are each drawn wider than the column, and short labels still share one width.

EditorHarness.Start(HarnessOptions) is new, so a test can choose the display size — which matters because the editor derives its column widths from it.

Coverage

New-code coverage is 96.3%. The one line not covered is the severity colour inside ShowDiagnostic, which sits behind the Diagnostics tab; the tab bar comes from a widget library that does not record its tabs, so there is no name for a test to click — the same limitation that sends the class-graph tests through WidgetHarness.

The second commit exists because the first missed that: EditorTheme also had a third fallback (if neither the saved nor the default theme resolved, take the first dark theme in the registry) that no test could reach, since it needs ktsu.ThemeProvider to drop VSCode Dark. Removed rather than worked around — neither resolving now leaves ImGui's own styling, which the doc comment already called a readable neutral rather than a failure.

🤖 Generated with Claude Code

https://claude.ai/code/session_01Jc8o5zF3cmfCGjzWvQdGDE

Two things visible in a screenshot of the running editor.
## Everything was blue
OnRender wrapped every frame in Theme.FromColor(Palette.Semantic.Primary).
FromColor is a scoped colour for one widget - it is what marks an element
carrying an error or a warning - so applying it to the whole frame tinted the
entire interface with the primary colour, and left an ordinary button looking
exactly like an emphasised one. The application-wide look is what
ktsu.ThemeProvider is for, and the editor had no theme at all.
So: a real theme, applied on start from the name in settings and defaulting to
VSCode Dark, with a Theme menu that opens the registry's browser (38 themes -
Catppuccin, Gruvbox, Nord, Tokyo Night and the rest) and remembers the choice.
The name is resolved against the registry rather than trusted, so a theme
renamed or dropped upstream falls back instead of leaving the editor unstyled.
That lives in a new EditorTheme rather than on SchemaEditor, because the
registry's types push that class past the analyzer's coupling limit - the same
budget Program.cs already warns about. EditorTheme also became the one place
that says how a validation issue is coloured, replacing the same severity
ternary written out in ButtonTree, the diagnostics list, the summary and the
member row.
Verified by rendering, not by assuming: the harness captured the same scene
before and after, and the second image is a properly themed editor with the red
"Save Now" standing out instead of competing with everything else.
## "Code Generators (" lost its count
Tree rows were drawn at a fixed width of 15% of the display width, and ImGui
clips a button's label to its frame. "Code Generators (0)" is the longest label
in the tree, and at the window size this was reported from it did not fit. The
width is a minimum now: short labels still line up as a column, a long one
grows.
The test for it starts the harness at 700px wide, because at the default 1280
every heading fits and there is nothing to prove - which is also why the first
version of the test passed against the bug.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Jc8o5zF3cmfCGjzWvQdGDE
…allback
SonarCloud's gate failed at 64.3% coverage on new code. Three gaps, all real
rather than measurement noise.
EditorTheme had a third fallback - if neither the saved theme nor the default
resolved, pick the first dark theme in the registry. That can only happen if
ktsu.ThemeProvider drops VSCode Dark, so no test could reach it, and the pick
was arbitrary anyway. Removed: neither resolving now leaves ImGui's own styling,
which the doc comment already called a readable neutral rather than a failure.
Choosing a theme was uncovered end to end. The browser turns out to be fully
drivable - it marks each card - so three tests open it, click one, and check the
theme is applied, reaches the settings, and comes back on the next start, which
is the round trip the setting exists for. The cards named are ones near the top
of the grid: it scrolls, and a card below the fold is recorded at a position
that has been clipped away, so clicking it hits the modal behind. Nord is the
thirtieth card and that is exactly what happened to it.
The severity colour was uncovered because nothing rendered an element that had
a validation issue - which is the thing this whole change was for. Two tests now
measure it on screen: point a member at a class that is not there and the
member's row gains red pixels; add a class with an empty name and the menu bar's
summary does. They count clearly-red pixels rather than an exact value, so they
do not depend on the shade VSCode Dark picks.
New-code coverage measured locally at 97.1%. The one line still uncovered is the
severity colour inside ShowDiagnostic, which is behind the Diagnostics tab, and
the tab bar comes from a widget library that does not record its tabs.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Jc8o5zF3cmfCGjzWvQdGDE
@sonarqubecloud

Copy link
Copy Markdown

@matt-edmondson
matt-edmondson merged commit 10e47b2 into mainSep 5, 2026
12 checks passed
@matt-edmondson
matt-edmondson deleted the claude/project-continuation-qgaxsq branch September 5, 2026 02:16
Sign up for freeto 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.

2 participants

@matt-edmondson@claude