diff --git a/CHANGELOG.md b/CHANGELOG.md index 6dd7e1e7..d5b10fcc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,19 @@ That paragraph is not decoration: the release workflow copies each entry verbati the GitHub release body and the announcement discussion, so it is the first thing a prospective user reads. CI fails a pull request whose newest entry is missing it. +## [1.76.15] - 2026-09-04 + +On any of the six light colour themes, the green tick on the little "finished" pop-up was almost invisible +against its own background. It was a fixed green picked for the dark themes and never changed for the others. + +### Fixed +- **The "finished" pop-up's tick is readable on the light themes.** Its background follows whichever theme you + picked, but the tick, its circle and the border were fixed to one green chosen against a dark background. On + the light themes that left the tick at between 1.65 and 2.08 times the contrast of its background, where 4.5 + is the readable threshold — so the pop-up said something had finished and you could barely see the mark + confirming it. All three now come from the theme, which already keeps a lighter green for dark themes and a + darker one for light. A check now fails the build if any screen fixes a colour the theme cannot change. + ## [1.76.14] - 2026-09-04 Double-clicking a cell in the App Updates or Windows Update list used to put a cursor in it, as if you could diff --git a/SysManager/SysManager.Tests/ArchitectureTests.cs b/SysManager/SysManager.Tests/ArchitectureTests.cs index bc014ed7..4a32e716 100644 --- a/SysManager/SysManager.Tests/ArchitectureTests.cs +++ b/SysManager/SysManager.Tests/ArchitectureTests.cs @@ -5496,6 +5496,80 @@ public void TheTimerResolutionQuery_BindsItsOutParametersCoarsestFinestCurrent() private static partial Regex TimerQueryCall(); + /// + /// A view may not paint itself with a literal colour; the theme decides colours. + /// + /// + /// The completion toast fixed three greens at #22C55E — the DARK-mode value — while its container + /// followed the theme through Surface2. Measured against each preset's real Surface2, the + /// tick came out at 1.65:1 on soft-blossom and never better than 2.08:1 on any of the six light presets, + /// against 7.66:1 on midnight-indigo where the value was chosen. WCAG asks 4.5:1 for text and 3:1 for a + /// meaningful graphic; it cleared neither. + /// NoThemedFill_CarriesAHardcodedWhiteForeground pins the same class and could not see this + /// one: it reads App.xaml styles whose Background binds Accent or Danger, and looks for the + /// literal White. Different file, different fill, different literal. This is the general rule, and + /// it is cheap because the view layer is nearly clean already — seven literals in total before the toast + /// was fixed, four of which are legitimate. + /// + [Fact] + public void NoViewPaintsItselfWithALiteralColour() + { + // file -> the reason its literals are colours being SHOWN rather than colours being applied. + var swatches = new Dictionary(StringComparer.Ordinal) + { + ["ThemePopup.xaml"] = "the custom-theme editor's 24x24 preview squares: each is an x:Name'd Border " + + "whose fill the code-behind replaces with the value being edited, so the " + + "literal is a designer-time default and the swatch's job is to BE a colour", + }; + + // Literals that must NOT follow the theme, allowed by exact value and only where they appear. The + // FocusRing is two stacked strokes precisely because no single themed colour survives every surface + // it lands on — PrimaryButton's accent fill, DangerButton's red, a raised grey and a card — where the + // accent itself falls to 1.00:1 against one of them. Theme-independence is the feature. + var themeIndependent = new Dictionary(StringComparer.Ordinal) + { + ["App.xaml"] = ["Stroke=\"#111111\"", "Stroke=\"#FFFFFF\""], + }; + + var appDir = FindAppProjectDir(); + var files = Directory.GetFiles(appDir, "*.xaml") + .Concat(Directory.GetFiles(Path.Combine(appDir, "Views"), "*.xaml")) + .Where(f => !f.Contains($"{Path.DirectorySeparatorChar}obj{Path.DirectorySeparatorChar}", + StringComparison.Ordinal)) + .ToList(); + + // Vacuity floor: the view layer is 30-odd files. Zero would mean the enumeration broke. + Assert.True(files.Count >= 25, + $"only {files.Count} view files were enumerated — this guard is reading the wrong folder."); + + var offenders = new List(); + foreach (var file in files) + { + var name = Path.GetFileName(file); + if (swatches.ContainsKey(name)) continue; + + var text = File.ReadAllText(file); + var allowed = themeIndependent.TryGetValue(name, out var values) ? values : []; + foreach (var m in LiteralColourAttribute().Matches(text).Cast()) + { + if (allowed.Contains(m.Value, StringComparer.Ordinal)) continue; + var line = text[..m.Index].Count(c => c == '\n') + 1; + offenders.Add($"{name}:{line} {m.Value}"); + } + } + + Assert.True(offenders.Count == 0, + "these views set a colour the theme cannot change, so whichever preset they were eyeballed against " + + "is the only one they are correct on. Bind a themed brush — Success/SuccessText/SuccessBorder, " + + "Danger…, TextPrimary, Surface… — or, if the element's purpose is to display a colour rather " + + "than be styled by one, name the file in the exception list in this test WITH that reason:\n " + + string.Join("\n ", offenders)); + } + + /// A colour-bearing attribute given a literal hex value. + [GeneratedRegex(@"(?:Foreground|Background|Fill|Stroke|BorderBrush)=""#[0-9A-Fa-f]{6,8}""")] + private static partial Regex LiteralColourAttribute(); + /// /// A text column in a report grid must not accept typing. /// diff --git a/SysManager/SysManager/MainWindow.xaml b/SysManager/SysManager/MainWindow.xaml index b81d7d7c..c9a9273b 100644 --- a/SysManager/SysManager/MainWindow.xaml +++ b/SysManager/SysManager/MainWindow.xaml @@ -498,8 +498,14 @@ HorizontalAlignment="Right" VerticalAlignment="Bottom" AutomationProperties.LiveSetting="Assertive" Margin="0,0,20,20" Visibility="Collapsed"> + @@ -511,9 +517,9 @@ + Background="{DynamicResource SuccessBgSubtle}" Margin="0,0,10,0"> diff --git a/SysManager/SysManager/SysManager.csproj b/SysManager/SysManager/SysManager.csproj index 2b605436..27fb2c4d 100644 --- a/SysManager/SysManager/SysManager.csproj +++ b/SysManager/SysManager/SysManager.csproj @@ -10,9 +10,9 @@ SysManager true NU1603;NU1701 - 1.76.14 - 1.76.14.0 - 1.76.14.0 + 1.76.15 + 1.76.15.0 + 1.76.15.0 SysManager SysManager — Windows system monitoring toolkit by laurentiu021. Network, updates, health, logs, safe deep cleanup. https://github.com/laurentiu021/SystemManager