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
37 changes: 22 additions & 15 deletions ARCHITECTURE.md
Original file line number Diff line number Diff line change
Expand Up @@ -136,24 +136,25 @@ QA-verified is marked with `IsInDevelopment` (surfaced as a PREVIEW badge) inste
- `AudioMixerViewModel` — per-app volume mixer (Volume Control tab): lists apps playing on the default render device with a volume slider, mute toggle, and a live peak meter. Two loops drive it, both idle while the tab is hidden (`IsActive`) and both sampling off the UI thread: membership reconciles on a ~1 s cadence, and the meters refresh every 50 ms via one batched `GetPeaks` call per tick. The peak loop *parks* on an activation gate while hidden rather than ticking and skipping — at 50 ms a skip-check still queues 20 Dispatcher continuations a second. (Per-row `GetPeak` on the UI thread was the 1.65.11 stutter fix.) Rows reconcile in place by session id (a wholesale replace would drop a slider mid-drag). Adds per-app output-device routing (via the guarded `AudioPolicyConfigFactory`; falls back to guiding the user to Windows sound settings when the OS lacks the interface) and named volume presets (persisted by `VolumePresetService`, keyed by exe name so they re-apply across restarts). Row VMs (`AudioSessionRowViewModel`) propagate volume/mute/route to the service, with a re-entrancy guard so an external change surfaced by a refresh is not echoed back.
- `StandbyMemoryViewModel` — live memory stats (2s poll) with on-demand and threshold-based auto-purge of the Windows standby list; purge needs admin.
- `GamingProfileViewModel` — one-click game mode (Gaming Profile tab, Preview): gathers the desired reversible optimizations plus an optional running-game target and delegates to `IGamingProfileService` to apply/revert them as a unit. Reports the batch outcome honestly (applied / needs-admin / failed), seeds its toggles from the last-used config, and offers to restore a leftover session on startup (crash recovery). Fully reversible; killing background apps and named per-game profiles are intentionally out of scope for the preview.
- `ProfileViewModel` — export/import SysManager's own config (theme, speed-test history) as a portable JSON profile with selective sections and version checking.
- `ProfileViewModel` — export/import SysManager's own config as a portable JSON profile with selective sections and version checking. The sections are whatever `ProfileService.Catalog` lists (nine today: theme, speed-test history, update-check preference, dark-mode schedule, gaming profiles, volume presets, close-button behaviour, standby-memory preference, app-icon fetching), and a section is skipped on export when its file does not exist yet.
- `DebloaterViewModel` — list and remove preinstalled Store apps with a curated bloat preset; system-critical packages are denylisted; removal is per-user and reversible via the Store. Takes the shared `ISessionRestorePoint` snapshot before the first removal, and words it honestly: System Restore does not bring Appx packages back, so the Store reinstall leads and the point is described as covering the rest of the system.
- `BrowserCleanerViewModel` — scan per-browser cache/history/cookies/sessions with sizes and clean the selected categories; cookies/sessions default unticked.
- `EdgeOneDriveViewModel` — reversibly de-integrate Edge and OneDrive (Edge/OneDrive Remover tab): OneDrive is fully removed per-user (no admin) with restore; Edge is only disabled & de-integrated (background/startup-boost policy + auto-update tasks, admin-gated) with restore — never uninstalled; guides the user to Windows settings to change the default browser. Every action confirms first and reports its honest outcome (success / needs-admin / not-applicable).
- `PrivacyMonitorViewModel` — read-only camera/mic/location access history from the consent store; hands off to Windows settings to change permissions.
- `BandwidthMonitorViewModel` — live total download/upload speed with a rolling throughput chart and a per-app usage list (Bandwidth Monitor tab). Polls the active `IBandwidthMonitorService` on a ~1 s loop, paused while the tab is hidden (`IsActive`) and wrapping every sample in one `Task.Run` so no source runs its work on the render thread, reconciling rows in place by PID so icons/order don't flicker. Defaults to the no-admin connection source; when elevated and opted in, switches to the ETW source for precise per-app rates and falls back automatically if ETW can't start. Threshold-alert derivation and rate formatting come from `BandwidthFormat`/`FormatHelper`. Read-only.
- `ConsoleViewModel` — shared, per-tab scrollable console (each tab gets its own
instance; lines capped at 5000 to bound memory) backing the in-app Console mirror
used by Cleanup, Windows Update, System Health, App Updates, and Uninstaller.
used by Cleanup, Windows Update, System Health, App Updates, and System Fixes —
the views whose XAML references it.

## Services

Thin wrappers around the underlying platform. Each service is designed to be
unit-testable. Services that a view-model needs to substitute in tests sit behind
an interface seam. Thirteen are registered against their implementation in `ServiceRegistration.cs` and
an interface seam. Fourteen are registered against their implementation in `ServiceRegistration.cs` and
constructor-injected: `IPowerShellRunner` (PowerShellRunner), `IWingetService` (WingetService),
`IAppBlockerService` (AppBlockerService), `ICpuAffinityService`, `IFileLockService`,
`INotificationBlockerService`, `ISettingsWatchdogService`, `ITimerResolutionService`,
`IAppBlockerService` (AppBlockerService), `ICleanupPreScanService`, `ICpuAffinityService`,
`IFileLockService`, `INotificationBlockerService`, `ISettingsWatchdogService`, `ITimerResolutionService`,
`ITweaksHubService`, `IWindowsThemeService`, `IAudioMixerService`, `IGamingProfileService`, and
`ISessionRestorePoint` (the last two via a factory).

Expand All @@ -169,7 +170,9 @@ finds nothing:

Key services:
- `PingMonitorService` / `TracerouteService` / `TracerouteMonitorService` —
network probes on `System.Net.NetworkInformation.Ping` and `tracert`.
network probes on `System.Net.NetworkInformation.Ping`. Traceroute walks the TTL itself
(`PingOptions(ttl, true)`, reading `TtlExpired` replies) rather than shelling out to a
command-line tool, which is why it needs no admin rights.
- `SpeedTestService` — HTTP speed test against Cloudflare plus the Ookla CLI,
auto-downloaded on first use.
- `PowerShellRunner` — wraps `System.Management.Automation` to run scripts
Expand Down Expand Up @@ -624,15 +627,19 @@ transient registrations at the top of `ServiceRegistration.cs`).

`MainWindowViewModel` resolves child VMs from the container **lazily**: each tab's
`NavItem` holds a `ContentFactory` and builds its view-model from DI only when the tab
is first opened (`NavItem.Content`). This avoids constructing all ~50 tab VMs at startup
— most kick off a background scan/timer in their constructor, so eager construction ran
that work up front for tabs the user might never open. A small set stays eager because
its constructor drives always-on, app-wide behavior independent of its tab: `Dashboard`
(the initially-selected tab), `DarkModeViewModel` (owns the theme-schedule poll), and
`AboutViewModel` (its startup update-check feeds the app-shell version label and update
banner). The four Network tabs also stay eager because they share one
`NetworkSharedState` and their constructors do no work. In tests/designer (no DI
container) every VM is built eagerly via a manual dependency graph, exactly as before.
is first opened (`NavItem.Content`). This avoids constructing all 55 lazily-registered tab
VMs at startup — most kick off a background scan/timer in their constructor, so eager
construction ran that work up front for tabs the user might never open.

**Exactly three** stay eager, each because its constructor drives always-on, app-wide
behavior independent of its tab: `Dashboard` (the initially-selected tab), `DarkModeViewModel`
(owns the theme-schedule poll), and `AboutViewModel` (its startup update-check feeds the
app-shell version label and update banner). That list is not maintained by hand here —
`ArchitectureTests.OnlyTheJustifiedTabs_AreBuiltAtStartup` fails the build on a fourth, and
carries the same three names with the same reasons. The Network tabs were the last exception
to go: they share one `NetworkSharedState`, which turned out not to require eager construction.

In tests/designer (no DI container) every VM is built eagerly via a manual dependency graph.

## Admin elevation

Expand Down
2 changes: 1 addition & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -210,7 +210,7 @@ text with a blank margin beside it.
- **The sidebar icons appear.** Each entry in the navigation list had a slot for an icon and a property to
hold it, and nothing ever put a character in that property — so the slot rendered as whitespace on all
fifty-eight entries. Every one of the twelve groups now has its own symbol, all distinct: a house for
Dashboard, a hard drive for Storage, a trash can for Cleanup, a shield for Privacy & Security, and so
Dashboard, a hard drive for Storage, a trash can for Cleanup, a padlock for Privacy & Security, and so
on. The individual pages inside a group are deliberately left without one. Fifty-eight icons in a rail
this narrow is noise, and the group heading is what you actually scan; the reclaimed space goes to the
page names, which now have room to be read in full.
Expand Down
13 changes: 10 additions & 3 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -70,8 +70,15 @@ dotnet test SysManager/SysManager.Tests/SysManager.Tests.csproj -c Release
difference. A clean build does not catch it, so run it before you push:

```powershell
dotnet format SysManager/SysManager/SysManager.csproj --verify-no-changes
# drop --verify-no-changes to let it fix the file in place
# All four, because CI checks all four — a single-project run passes while the PR fails.
foreach ($p in @(
'SysManager/SysManager/SysManager.csproj',
'SysManager/SysManager.Tests/SysManager.Tests.csproj',
'SysManager/SysManager.IntegrationTests/SysManager.IntegrationTests.csproj',
'SysManager/SysManager.UITests/SysManager.UITests.csproj')) {
dotnet format $p --verify-no-changes
}
# drop --verify-no-changes to let it fix the files in place
```

## Project layout
Expand Down Expand Up @@ -127,7 +134,7 @@ guide. That said, a few explicit rules:

### Author headers

Every source file opens with a three-line attribution block. All 688 of them
Every source file opens with a three-line attribution block. All 704 of them
carry it, and a test fails the build if a new file doesn't — so copy the shape
exactly. In `.cs` files (the `— summary` after the class name is optional):

Expand Down
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -147,8 +147,8 @@ row and every group header is also keyboard-operable with a visible focus cue. A
> 🔬 = Preview — fully implemented and usable, marked in-app while it settles in.

Groups expand and collapse with a click. Collapsed groups show a child count
badge, a subtitle with abbreviated child labels, and a tooltip with the full
list. Dashboard renders as a flat top-level entry without an expander arrow.
badge, a written one-line summary of what the group covers, and a tooltip with the
full list. Dashboard renders as a flat top-level entry without an expander arrow.
Each tab shows a slim progress bar under its name when performing a
long-running operation, so you always know which tab is working.

Expand Down Expand Up @@ -961,7 +961,7 @@ offers, "rate us" prompts:
load and refreshes with "Scan system".
- **System Tray** — background health monitoring (60s polling), CPU/RAM tooltip,
Windows notifications when RAM > 90%, uptime > 14 days, or disk health degrades.
Context menu: Show / Exit.
Context menu: Show SysManager / Volume mixer / Exit.
- **Closing is your choice** — the first time you close the window, SysManager asks
whether it should keep running in the notification area or close completely, and
remembers the answer. If you pick the notification area, it tells you where the
Expand Down
3 changes: 2 additions & 1 deletion SysManager/SysManager/ViewModels/NavGroup.cs
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,8 @@ public sealed partial class NavGroup : ObservableObject

/// <summary>
/// Short subtitle shown under the group label when collapsed.
/// Abbreviated child labels joined with " · ".
/// Written copy describing what the group covers, set per group at the call site in
/// <c>MainWindowViewModel.BuildNavGroups</c> -- not derived from the child labels.
/// </summary>
public string Subtitle { get; set; } = "";

Expand Down
2 changes: 1 addition & 1 deletion TESTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ happily as a readable one and Codecov never reports the rejection back.
| Package | Purpose |
|---|---|
| xUnit 2.9 | Test framework |
| NSubstitute 6.1 | Mocking/substitution for interface-based testing |
| NSubstitute 6.2 | Mocking/substitution for interface-based testing |
| NetArchTest.Rules 1.3 | Architecture fitness functions — MVVM dependency direction, and guards that pin recurring defect classes |
| coverlet | Code coverage collection |
| JunitXml.TestLogger | JUnit XML test report — the only format Codecov's test analytics parses |
Expand Down