diff --git a/CHANGELOG.md b/CHANGELOG.md index 7d8c2195..642e3de1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,10 +6,16 @@ adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). ## [Unreleased] -### Fixed -- **Startup Manager columns** — reduced fixed widths to prevent last column overflow. -- **Startup Manager icons** — use extracted executable path for more accurate icon resolution. -- **Windows Update live output** — increased MinHeight/MaxHeight for better visibility. +## [1.10.0] - 2026-05-26 + +### Added +- **Safety ratings on Services** — each service shows Safe/Caution/Critical badge with + description tooltip. Filter chips in toolbar to show only safe-to-disable services. +- **Safety ratings on Windows Features** — same badge system as Services. +- **Curated safety database** — 50+ services and 20+ features with researched safety + levels and human-readable explanations. +- **Startup Manager hide system** — toggle to filter out Windows/Microsoft system entries. +- **Filter chip styles** — reusable green/amber/red radio pill components. ## [1.9.0] - 2026-05-26 diff --git a/SysManager/SysManager.Tests/ServicesViewModelTests.cs b/SysManager/SysManager.Tests/ServicesViewModelTests.cs index 04f8340e..83111cea 100644 --- a/SysManager/SysManager.Tests/ServicesViewModelTests.cs +++ b/SysManager/SysManager.Tests/ServicesViewModelTests.cs @@ -17,11 +17,11 @@ public class ServicesViewModelTests { private static readonly List TestServices = new() { - new() { Name = "wuauserv", DisplayName = "Windows Update", Description = "Manages Windows updates", Status = "Running", StartType = "Automatic", Recommendation = "keep-enabled" }, - new() { Name = "Spooler", DisplayName = "Print Spooler", Description = "Manages print jobs", Status = "Running", StartType = "Automatic", Recommendation = "safe-to-disable" }, - new() { Name = "XboxGipSvc", DisplayName = "Xbox Accessory Management", Description = "Manages Xbox accessories", Status = "Stopped", StartType = "Manual", Recommendation = "safe-to-disable" }, - new() { Name = "WSearch", DisplayName = "Windows Search", Description = "Provides content indexing", Status = "Running", StartType = "Automatic", Recommendation = "advanced" }, - new() { Name = "BITS", DisplayName = "Background Intelligent Transfer", Description = "Transfers files in background", Status = "Stopped", StartType = "Manual", Recommendation = "keep-enabled" }, + new() { Name = "wuauserv", DisplayName = "Windows Update", Description = "Manages Windows updates", Status = "Running", StartType = "Automatic", Recommendation = "keep-enabled", SafetyLevel = Models.SafetyLevel.Caution }, + new() { Name = "Spooler", DisplayName = "Print Spooler", Description = "Manages print jobs", Status = "Running", StartType = "Automatic", Recommendation = "safe-to-disable", SafetyLevel = Models.SafetyLevel.Caution }, + new() { Name = "XboxGipSvc", DisplayName = "Xbox Accessory Management", Description = "Manages Xbox accessories", Status = "Stopped", StartType = "Manual", Recommendation = "safe-to-disable", SafetyLevel = Models.SafetyLevel.Safe }, + new() { Name = "WSearch", DisplayName = "Windows Search", Description = "Provides content indexing", Status = "Running", StartType = "Automatic", Recommendation = "advanced", SafetyLevel = Models.SafetyLevel.Caution }, + new() { Name = "BITS", DisplayName = "Background Intelligent Transfer", Description = "Transfers files in background", Status = "Stopped", StartType = "Manual", Recommendation = "keep-enabled", SafetyLevel = Models.SafetyLevel.Critical }, }; private static ServicesViewModel CreateWithData(List? services = null) @@ -52,8 +52,9 @@ public void Constructor_FilterOptions_ContainsExpected() Assert.Contains("All", vm.FilterOptions); Assert.Contains("Running", vm.FilterOptions); Assert.Contains("Stopped", vm.FilterOptions); - Assert.Contains("Safe to disable", vm.FilterOptions); - Assert.Contains("Advanced", vm.FilterOptions); + Assert.Contains("Safe", vm.FilterOptions); + Assert.Contains("Caution", vm.FilterOptions); + Assert.Contains("Critical", vm.FilterOptions); } [Fact] @@ -111,21 +112,20 @@ public void ApplyFilter_Stopped_ShowsOnlyStopped() } [Fact] - public void ApplyFilter_SafeToDisable_ShowsOnlySafe() + public void ApplyFilter_SafeLevel_ShowsOnlySafe() { var vm = CreateWithData(); - vm.SelectedFilter = "Safe to disable"; - Assert.All(vm.Services, s => Assert.Equal("safe-to-disable", s.Recommendation)); - Assert.Equal(2, vm.Services.Count); + vm.SelectedFilter = "Safe"; + Assert.All(vm.Services, s => Assert.Equal(Models.SafetyLevel.Safe, s.SafetyLevel)); + Assert.Single(vm.Services); } [Fact] - public void ApplyFilter_Advanced_ShowsOnlyAdvanced() + public void ApplyFilter_Safe_ShowsOnlySafe() { var vm = CreateWithData(); - vm.SelectedFilter = "Advanced"; - Assert.All(vm.Services, s => Assert.Equal("advanced", s.Recommendation)); - Assert.Single(vm.Services); + vm.SelectedFilter = "Safe"; + Assert.All(vm.Services, s => Assert.Equal(Models.SafetyLevel.Safe, s.SafetyLevel)); } // ── ApplyFilter: text filter ── diff --git a/SysManager/SysManager/App.xaml b/SysManager/SysManager/App.xaml index 944affd1..48c8cf1d 100644 --- a/SysManager/SysManager/App.xaml +++ b/SysManager/SysManager/App.xaml @@ -21,6 +21,9 @@ + + + @@ -273,6 +276,92 @@ + + + + + + + + + - + - - - - - + + + + + + diff --git a/SysManager/SysManager/Views/StartupView.xaml b/SysManager/SysManager/Views/StartupView.xaml index 890774a6..205f42ab 100644 --- a/SysManager/SysManager/Views/StartupView.xaml +++ b/SysManager/SysManager/Views/StartupView.xaml @@ -32,6 +32,11 @@ Style="{StaticResource SecondaryButton}" Margin="0,0,8,0"/>