Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
6860b65
docs(ui): record UI overhaul decisions and the cut status surfaces
carterscode Aug 7, 2026
2fb86b8
feat(settings): map every setting id to one of nine UI sections
carterscode Aug 7, 2026
c4c6693
feat(monitor): publish the current drift set for the UI to count
carterscode Aug 7, 2026
0c347dd
feat(beta): add a BETA build flavor that runs isolated from a stable …
carterscode Aug 7, 2026
58e74ea
ci(beta): add a manual-only beta build workflow
carterscode Aug 7, 2026
6703aa3
feat(beta): seed beta config from the stable install, and pin unknown…
carterscode Aug 7, 2026
f1da998
fix(beta): genuinely remove the update path, and make the CI guard ab…
carterscode Aug 7, 2026
a6b27c0
refactor(config): remove the dead ConsolidateNotifications setting
carterscode Aug 7, 2026
58e1cc1
fix(ci): scan both UTF-16 byte alignments, or the guard misses odd-al…
carterscode Aug 7, 2026
3f6fa4d
docs: pre-extraction feature inventory of all 10 tabs and 5 windows
carterscode Aug 7, 2026
fbead66
feat(ui): replace the TabControl with a grouped NavigationView and ex…
carterscode Aug 7, 2026
93324ef
feat(beta): add the beta installer, and make the test suite flavor-aware
carterscode Aug 7, 2026
5e4def4
fix(ui): make the navigation pane actually clickable
carterscode Aug 7, 2026
4c002fa
fix(ui): repair em-dashes mangled during the view extraction
carterscode Aug 7, 2026
de75a90
feat(ui): add a "This PC" dashboard grid to the Status page
carterscode Aug 7, 2026
28b6f27
fix(ui): make the This PC cards uniform, legible and lighter, and fil…
carterscode Aug 8, 2026
cd48a5e
feat(ui): recolour the app with GitHub's dark (Primer) palette
carterscode Aug 8, 2026
dcc9969
fix(ui): the view headings rendered black on the dark canvas
carterscode Aug 8, 2026
d06b67e
docs: release notes for 0.1.65
carterscode Aug 8, 2026
2ed2924
Merge remote-tracking branch 'origin/main' into feature/ui-overhaul
carterscode Aug 8, 2026
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
124 changes: 124 additions & 0 deletions .github/workflows/beta.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
name: beta

# Manual only. There is deliberately no push trigger and no tag trigger, so this
# cannot fire as a side effect of committing, merging, or tagging anything.
on:
workflow_dispatch:

# Read-only token. This workflow cannot create a release, push a tag, or write to
# the repository even if a step tried to: the GITHUB_TOKEN it is handed has no
# write scope. The beta EXE leaves as a workflow artifact and nothing else.
permissions:
contents: read

concurrency:
group: beta-${{ github.ref }}
cancel-in-progress: true

jobs:
beta:
runs-on: windows-latest
# No permissions block here either -- the job inherits the read-only set above.
# release.yml needs contents/id-token/attestations write to publish; this one
# intentionally has none of them.
steps:
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
fetch-depth: 0
fetch-tags: true

- name: Resolve beta version
id: ver
shell: pwsh
run: |
# Same shape as dev-build.yml: a numeric base for AssemblyVersion and
# FileVersion (which must stay a pure a.b.c.d), and a descriptive
# InformationalVersion carrying the -beta.<sha> suffix. AppIdentity parses
# the sha back out of it for the in-app BETA marker.
# Stable tags only -- 'v*.*.*' also matches prerelease tags, and [int] on
# a "65-beta" patch component throws.
$latest = (git tag --list 'v*.*.*' --sort=-v:refname | Where-Object { $_ -notmatch '-' } | Select-Object -First 1)
if ([string]::IsNullOrWhiteSpace($latest)) { $latest = 'v0.0.0' }
$parts = ($latest -replace '^v','').Split('.')
$patch = [int]$parts[2] + 1
$base = "$($parts[0]).$($parts[1]).$patch"
$sha = $env:GITHUB_SHA.Substring(0,7)
$full = "$base-beta.$sha"
"base=$base" | Out-File -FilePath $env:GITHUB_OUTPUT -Append
"version=$full" | Out-File -FilePath $env:GITHUB_OUTPUT -Append
"sha=$sha" | Out-File -FilePath $env:GITHUB_OUTPUT -Append
Write-Host "Building beta $full (numeric base $base) from $env:GITHUB_REF_NAME"

- name: Setup .NET 8
uses: actions/setup-dotnet@9a946fdbd5fb07b82b2f5a4466058b876ab72bb2 # v5.3.0
with:
dotnet-version: '8.0.x'

- name: Restore
run: dotnet restore GamerGuardian.sln

# A beta goes to real testers, so it does not ship without the suite passing.
# This builds the default (non-BETA) flavor, which is also the check that the
# non-beta path still compiles warning-free.
- name: Test
run: dotnet test GamerGuardian.sln --nologo

- name: Publish beta (self-contained, single-file, win-x64)
# Identical publish flags to release.yml, plus -p:Beta=true to define the
# BETA constant and an explicit InformationalVersion for the sha marker.
run: >
dotnet publish src/GamerGuardian/GamerGuardian.csproj
-c Release
-r win-x64
--self-contained true
-p:PublishSingleFile=true
-p:IncludeNativeLibrariesForSelfExtract=true
-p:EnableCompressionInSingleFile=true
-p:Beta=true
-p:Version=${{ steps.ver.outputs.base }}
-p:AssemblyVersion=${{ steps.ver.outputs.base }}.0
-p:FileVersion=${{ steps.ver.outputs.base }}.0
-p:InformationalVersion=${{ steps.ver.outputs.version }}
-o publish

# Fails the run if the update path somehow survived into a beta build.
#
# Scans the managed assembly, NOT publish\GamerGuardian.exe. The shipped EXE is
# a single-file bundle published with EnableCompressionInSingleFile, so its
# assemblies are compressed: scanning the EXE's bytes finds nothing even in a
# stable build. An earlier version of this step did exactly that and passed on
# both flavors, which made it false assurance rather than a check. The DLL
# below is the same assembly that gets bundled into that EXE.
#
# build.yml proves this check can actually fail, by asserting the differential
# against a stable build. Here we only assert the absolute property.
- name: Verify the update path is absent from the beta assembly
shell: pwsh
run: |
$dll = Get-ChildItem -Recurse -Path "src/GamerGuardian/bin/Release" -Filter "GamerGuardian.dll" |
Select-Object -First 1
if (-not $dll) { throw "Could not find the built GamerGuardian.dll to scan." }
$bytes = [System.IO.File]::ReadAllBytes($dll.FullName)
# .NET stores string literals as UTF-16 in the #US heap, but at arbitrary
# byte offsets. Decoding from offset 0 only sees even-aligned literals; an
# odd-aligned one decodes to garbage and is missed. Check both alignments.
$even = [System.Text.Encoding]::Unicode.GetString($bytes, 0, $bytes.Length - ($bytes.Length % 2))
$odd = [System.Text.Encoding]::Unicode.GetString($bytes, 1, $bytes.Length - 1 - (($bytes.Length - 1) % 2))
$pattern = 'api\.github\.com/repos/carterscode/GamerGuardian/releases'
if ([regex]::IsMatch($even, $pattern) -or [regex]::IsMatch($odd, $pattern)) {
throw "BETA build still contains the update-feed URL -- the update path was not compiled out."
}
Write-Host "OK: no update-feed URL in $($dll.FullName)"

- name: List artifacts
shell: pwsh
run: Get-ChildItem -Recurse publish | Select-Object FullName, Length

# The only output. No installer is built, no release is created, no tag is
# pushed, and nothing is published to the update feed.
- name: Upload beta EXE artifact
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: GamerGuardian-beta-r${{ github.run_number }}-${{ steps.ver.outputs.sha }}
path: publish/GamerGuardian.exe
if-no-files-found: error
57 changes: 57 additions & 0 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -26,3 +26,60 @@ jobs:

- name: Test
run: dotnet test GamerGuardian.sln -c Release --no-build --verbosity normal

# beta.yml is workflow_dispatch-only by design, so without this nothing routinely
# proves the BETA flavor still compiles. TreatWarningsAsErrors is on and
# #if-excluded code is invisible to the other compile, so the beta path can rot
# silently between manual dispatches.
beta-compile:
runs-on: windows-latest
steps:
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0

- name: Setup .NET 8
uses: actions/setup-dotnet@9a946fdbd5fb07b82b2f5a4466058b876ab72bb2 # v5.3.0
with:
dotnet-version: '8.0.x'

- name: Restore
run: dotnet restore GamerGuardian.sln

- name: Build the BETA flavor
run: dotnet build src/GamerGuardian/GamerGuardian.csproj -c Release -p:Beta=true --no-restore -o out-beta

# Differential proof that the update path really is absent from a beta build --
# and, just as importantly, that the check is capable of failing. Asserting only
# "absent in beta" would pass just as happily against a scan that can never
# match anything, which is exactly the trap the previous byte-scan of the
# compressed single-file EXE fell into.
- name: Build the stable flavor for comparison
run: dotnet build src/GamerGuardian/GamerGuardian.csproj -c Release --no-restore -o out-stable

- name: Assert the update path is present in stable and absent in beta
shell: pwsh
run: |
function Test-UpdateUrl($path) {
$bytes = [System.IO.File]::ReadAllBytes($path)
# .NET stores string literals as UTF-16 in the #US heap, but at arbitrary
# byte offsets. Decoding the file as UTF-16 from offset 0 only sees
# literals that happen to start on an even boundary -- an odd-aligned one
# decodes to garbage and is missed. Measured: the BETA marker literal in
# this very assembly is odd-aligned while the update URL is even-aligned,
# so a single-alignment scan is a coin flip. Check both.
$even = [System.Text.Encoding]::Unicode.GetString($bytes, 0, $bytes.Length - ($bytes.Length % 2))
$odd = [System.Text.Encoding]::Unicode.GetString($bytes, 1, $bytes.Length - 1 - (($bytes.Length - 1) % 2))
$pattern = 'api\.github\.com/repos/carterscode/GamerGuardian/releases'
return [regex]::IsMatch($even, $pattern) -or [regex]::IsMatch($odd, $pattern)
}
$stable = Test-UpdateUrl "out-stable/GamerGuardian.dll"
$beta = Test-UpdateUrl "out-beta/GamerGuardian.dll"
Write-Host "stable contains update URL : $stable (expected True)"
Write-Host "beta contains update URL : $beta (expected False)"

if (-not $stable) {
throw "The scan found no update URL in the STABLE build. The check is broken and would pass against anything -- fix the scan, do not trust the beta result."
}
if ($beta) {
throw "The BETA build still contains the update-feed URL. The update path was not compiled out."
}
Write-Host "OK: update path present in stable, absent in beta."
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ bin/
obj/
out/
publish/
publish-beta/
.tmp_*/
*.user
*.suo
Expand Down
29 changes: 29 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,36 @@ Versions before 1.0.0 are pre-release: features and defaults may still change.

## [Unreleased]

## [0.1.65] - 2026-08-07

The biggest visual change since the app was first released: the window has been
rebuilt around a grouped sidebar, and it now has a home screen.

### Added
- **A Status home screen.** Opening GamerGuardian now lands you on Status
instead of the first settings tab. It leads with a single number — how many of
the settings you monitor have drifted from what you asked for — and breaks
that down by section, so you can see at a glance whether anything needs you.
- **A "This PC" summary.** Status shows six cards describing the machine:
processor, graphics, memory, Windows version, displays, and the active power
plan. Each one is context for settings GamerGuardian actually manages — the
CPU behind the power-plan recommendation, the GPU behind hardware-accelerated
scheduling, the Windows build that decides which policies apply.
- **Real memory detail.** The memory card reads your firmware directly and
reports the installed total with its type, plus the module layout and speed —
for example "31.2 GB DDR5" and "2 × 16 GB @ 5600 MT/s". Mismatched sticks are
listed rather than averaged away, because a mismatched pair is worth noticing.
- **Pause monitoring from Status.** Stopping and resuming background checks no
longer means hunting through the settings.

### Changed
- **New navigation.** The row of tabs is gone. Settings are now grouped in a
sidebar under Performance, Privacy, Cleanup and Reference, so related settings
sit together and the window no longer runs out of horizontal room. Everything
that was in the old tabs is still there — nothing was dropped in the move.
- **A GitHub-dark colour scheme.** The app is recoloured to match GitHub's dark
theme: a deep navy-black canvas, cards that actually read as cards, and
GitHub's green, amber and red for status. Light theme is unchanged.
- **Clearer upgrade notes.** The "what's new" text shown when a new version is
offered is now plain-English highlights only — no more raw list of internal
pull-request titles — and Markdown formatting is cleaned up so it reads
Expand Down
Loading