Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,14 @@ adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
Windows toast notifications when RAM > 90%, uptime > 14 days, or disk
health degrades. Right-click context menu with Show / Exit. Uses
H.NotifyIcon.Wpf 2.2.1. Closes #262.
- **About — Changelog link** — new "View Changelog" button opens the
GitHub CHANGELOG.md in the browser. Closes #232.
- **Drivers — Hide system drivers** — toggle to filter out Microsoft /
Windows drivers from the list, showing only third-party drivers.
Closes #234.
- **Startup Manager — Hide Windows entries** — toggle to filter out
Microsoft / Windows startup items that should not be disabled.
Closes #238.
- **IntGreaterThanZeroConverter** — value converter for conditional
visibility when an integer is greater than zero.
- **IDialogService** — abstraction for user confirmation dialogs, replacing
Expand Down
3 changes: 3 additions & 0 deletions SysManager/SysManager/ViewModels/AboutViewModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -204,6 +204,9 @@ private void OpenManualDownload()
[RelayCommand]
private void OpenRepo() => OpenUrl($"https://github.com/{UpdateService.Owner}/{UpdateService.Repo}");

[RelayCommand]
private void OpenChangelog() => OpenUrl($"https://github.com/{UpdateService.Owner}/{UpdateService.Repo}/blob/main/CHANGELOG.md");

[RelayCommand]
private void OpenLicense() => OpenUrl($"https://github.com/{UpdateService.Owner}/{UpdateService.Repo}/blob/main/LICENSE");

Expand Down
34 changes: 31 additions & 3 deletions SysManager/SysManager/ViewModels/DriversViewModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,24 +17,29 @@ public partial class DriversViewModel : ViewModelBase
{
private readonly PowerShellRunner _runner;
private CancellationTokenSource? _cts;
private readonly List<DriverEntry> _allDrivers = new();

public ObservableCollection<DriverEntry> Drivers { get; } = new();

[ObservableProperty] private int _driverCount;
[ObservableProperty] private string _summary = "Click List drivers to scan installed drivers.";
[ObservableProperty] private bool _hideSystemDrivers;

public DriversViewModel(PowerShellRunner runner)
{
_runner = runner;
}

partial void OnHideSystemDriversChanged(bool value) => ApplyFilter();

[RelayCommand]
private async Task ListDriversAsync()
{
IsBusy = true;
IsProgressIndeterminate = true;
StatusMessage = "Scanning installed drivers…";
Drivers.Clear();
_allDrivers.Clear();
_cts?.Dispose();
_cts = new CancellationTokenSource();

Expand All @@ -60,8 +65,8 @@ await _runner.RunScriptViaPwshAsync(@"
finally { _runner.LineReceived -= Capture; }

ParseDriverJson(json.ToString());
DriverCount = Drivers.Count;
Summary = $"{DriverCount} drivers found.";
Summary = $"{_allDrivers.Count} drivers found" +
(HideSystemDrivers ? $" ({DriverCount} shown, system drivers hidden)." : ".");
StatusMessage = "Done";
}
catch (OperationCanceledException) { StatusMessage = "Cancelled."; }
Expand Down Expand Up @@ -103,16 +108,39 @@ private void ParseDriverJson(string raw)
DriverDate = ParseCimDate(el.TryGetProperty("DriverDate", out var dd) ? dd : default),
}))
{
Drivers.Add(entry);
_allDrivers.Add(entry);
}
}
catch (JsonException ex)
{
Log.Warning("Failed to parse driver JSON: {Error}", ex.Message);
StatusMessage = "Parse error — some drivers may not be shown.";
}

ApplyFilter();
}

private void ApplyFilter()
{
Drivers.Clear();
var filtered = HideSystemDrivers
? _allDrivers.Where(d => !IsSystemDriver(d))
: _allDrivers;

foreach (var d in filtered)
Drivers.Add(d);

DriverCount = Drivers.Count;
if (_allDrivers.Count > 0)
Summary = HideSystemDrivers
? $"{_allDrivers.Count} drivers found ({DriverCount} shown, system drivers hidden)."
: $"{_allDrivers.Count} drivers found.";
}

private static bool IsSystemDriver(DriverEntry d)
=> d.Manufacturer.Contains("Microsoft", StringComparison.OrdinalIgnoreCase) ||
d.Manufacturer.Contains("Windows", StringComparison.OrdinalIgnoreCase);

/// <summary>
/// CIM dates come as "/Date(ticks)/" strings in JSON.
/// </summary>
Expand Down
29 changes: 26 additions & 3 deletions SysManager/SysManager/ViewModels/StartupViewModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,19 +18,23 @@ namespace SysManager.ViewModels;
public partial class StartupViewModel : ViewModelBase
{
private readonly StartupService _service = new();
private readonly List<StartupEntry> _allEntries = new();

public ObservableCollection<StartupEntry> Entries { get; } = new();

[ObservableProperty] private int _enabledCount;
[ObservableProperty] private int _disabledCount;
[ObservableProperty] private int _totalCount;
[ObservableProperty] private string _scanSummary = "Click Scan to discover startup items.";
[ObservableProperty] private bool _hideWindowsEntries;

public StartupViewModel()
{
_ = InitAsync();
}

partial void OnHideWindowsEntriesChanged(bool value) => ApplyFilter();

private async Task InitAsync()
{
try { await ScanAsync(); }
Expand All @@ -46,15 +50,16 @@ private async Task ScanAsync()
try
{
var items = await _service.ScanAsync();
_allEntries.Clear();
Entries.Clear();
foreach (var item in items.OrderBy(e => e.Name, StringComparer.OrdinalIgnoreCase))
{
item.Icon = IconExtractorService.GetIcon(item.Command);
Entries.Add(item);
_allEntries.Add(item);
}

UpdateCounts();
StatusMessage = $"Found {TotalCount} startup items.";
ApplyFilter();
StatusMessage = $"Found {_allEntries.Count} startup items.";
}
catch (InvalidOperationException ex)
{
Expand Down Expand Up @@ -132,4 +137,22 @@ private void UpdateCounts()
TotalCount = Entries.Count;
ScanSummary = $"{EnabledCount} enabled · {DisabledCount} disabled · {TotalCount} total";
}

private void ApplyFilter()
{
Entries.Clear();
var filtered = HideWindowsEntries
? _allEntries.Where(e => !IsWindowsEntry(e))
: _allEntries;

foreach (var item in filtered)
Entries.Add(item);

UpdateCounts();
}

private static bool IsWindowsEntry(StartupEntry entry)
=> entry.Publisher.Contains("Microsoft", StringComparison.OrdinalIgnoreCase) ||
entry.Command.Contains(@"\Windows\", StringComparison.OrdinalIgnoreCase) ||
entry.Command.Contains(@"\Microsoft\", StringComparison.OrdinalIgnoreCase);
}
Loading