feat(gui)!: replace the terminal interface with an egui desktop window - #48
Merged
Merged
Conversation
β¦ OS command execution
β¦m specs panel width
β¦and extend VSS timeouts
Implements whea_logger diagnostic module to query Windows Hardware Error Architecture events (Events 19, 18, 17, 47, 1), extract APIC ID/Core and PCIe BDF fault locations, provide ASPM power management and mdsched fixes, and integrate into engine and UI.
β¦ext scan Three separate scans re-reported findings that a repair had already handled, or that no repair could ever clear: - Scheduled Tasks ignored a task's state. Disabling is the only thing the repair does, and Windows neither resets LastTaskResult nor restores a deleted program, so every task WinMedic switched off came straight back on the following scan. Disabled tasks are now skipped in both checks, and the disable reads the state back afterwards so a TrustedInstaller-owned task that accepts the command without applying it fails honestly instead of counting as repaired. - System Cleaner raised a finding on any non-zero byte. Every directory it sweeps is one the system refills by itself - a service's next log line, Explorer's 129-byte $Recycle.Bin\desktop.ini shell stub, the next favicon a browser caches - so the findings reappeared seconds after a successful repair. Cleanup targets now need 10 MB (50 MB for browser caches) before they are worth reporting, and the Recycle Bin's shell stub is neither counted nor deleted. - The DNS check ran `nslookup <name> 8.8.8.8`, pinning a public resolver and bypassing the machine's own. Networks that block outbound port 53 produced a permanent critical finding that ipconfig /flushdns cannot possibly fix. It now queries the configured resolver for two independent names, parses the answer record rather than substring matching a header that is present on failure too, and re-queries after the repair so an unfixed resolver is reported as a failed repair. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The README claimed the cache cleaner covered Brave and Opera; the discovery only ever walked Chrome, Edge and Firefox, so both browsers' caches were left untouched on every run. Brave is Chromium on the same `User Data\<profile>\<cache>` layout as Chrome and Edge, so all three now go through one helper instead of two copies of the same loop. Opera does not use a `User Data` root: each installed flavour (Opera Stable, Opera GX Stable, Opera One, ...) gets its own directory under `Opera Software` and that directory *is* the profile, so the flavours are enumerated rather than named and a flavour Opera ships next needs no code change. Both the LocalAppData and the Roaming root are checked, since the caches moved between releases and an upgraded machine still carries the old one. Only `Default` and `Profile N` directories are collected, so Chromium's shared state next to them - `Local State`, the user's dictionary, safe-browsing lists - is never swept. The progress line, issue title and fix steps name the browsers actually covered, so the finding in the TUI matches what the sweep does. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Parses kernel minidumps directly (PAGEDU64/PAGEDUMP headers) without external tools like WinDbg or BlueScreenView: stop codes, bugcheck parameters and faulting drivers (e.g. nvlddmkm.sys) are extracted from C:\Windows\Minidump and correlated with BugCheck Event 1001 and Kernel-Power Event 41 from the System log. Findings: crash_driver_fault (driver attribution with rollback plan), crash_video_tdr (0x116/0x117), crash_memory_bugcheck (0x1A/0x50/...), crash_bugcheck_history, crash_unexpected_shutdown (Event 41 without bugcheck) and crash_stale_dumps (cleanup). Fixes open Device Manager for driver issues, schedule mdsched.exe for memory-class stop codes and remove analysed dumps via PowerShell. Registered as the 11th module; module-count assertions and integration tests updated, 20 new unit tests added.
β¦1.98 Clippy 1.98 promotes chunks_exact_to_as_chunks to -D warnings, but the as_chunks return order differs between toolchain versions, so plain index pairs keep the UTF-16LE scan stable on every toolchain.
`src/ui/` is gone and `src/gui/` takes its place. Almost nothing else had to move: `App` was already pure state with no ratatui in it, the engine already reported over channels any front end can drain, and exactly one line outside the render layer named a UI library β the `crossterm::event::KeyCode` import in `app::input`. That import is now a toolkit-neutral `Key` enum which the front end translates egui's events into, so the whole layered dispatch table and its tests survived the move unchanged. `src/app/`, `src/engine/`, `src/modules/`, `src/safety/` and the headless CLI are untouched apart from comments that had gone stale. The binary stays in the *console* subsystem, deliberately. `#![windows_subsystem = "windows"]` would buy a GUI that never flashes a console and pay for it by breaking every headless guarantee the CLI help makes: a Windows-subsystem process does not hold the shell, so `winmedic --json > out.json` returns before the file is finished, the documented exit codes arrive after whatever ran next, and `release.yml` can no longer capture `winmedic.exe --version` into the variable it compares against the tag. `utils::console` instead hands the console back on the way up, and only when `GetConsoleProcessList` says this process is alone in it β freeing a console shared with `cmd.exe` would detach WinMedic from the terminal it was typed into. The cost is a console window visible until `main` is reached. `main` is no longer `#[tokio::main]`. `run_gui` needs a runtime it can hold open for the life of the window and building one from inside another panics, so each branch now owns its own; the GUI ends with `shutdown_background()` so closing the window never blocks on a DISM call that has not come back yet. The MSRV moves 1.88 -> 1.95: the old floor came from `ratatui -> instability -> darling`, which left with ratatui, and egui 0.36 sets the new one. Three pieces of state stopped being reachable once the drawing changed, and are handled rather than left dangling. The log scroll keys and their offsets are gone because the consoles are scroll areas the wheel and scrollbars drive. `/` no longer switches on a character-capture mode β a window has a real text field bound to the same string, so the flag is now `focus_search`, a one-shot request the front end honours and clears. The settings pane draws which row is selected and which of its two lists owns the arrow keys, both of which the keyboard moves and neither of which was visible. The nine ratatui render tests become nine egui_kittest tests asserting against the accessibility tree, which is the more honest target: a label missing from it is missing for a screen reader, not merely for a test. One of them is new. `every_character_the_window_draws_has_a_glyph` exists because the settings pane first shipped its focus hint as the arrow glyphs the terminal version used, and all three reached the screen as empty boxes β a terminal inherits the system's font coverage, a window ships its own, and egui's is a Latin text face. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Follows the front end from the terminal to a window. The badge, the title, the overview sentence and the MSRV all moved; so did the reason CONTRIBUTING gives for the MSRV, which named the `ratatui -> instability -> darling` chain that no longer exists. Two shortcut rows are deleted rather than reworded. `[PgUp]`/`[PgDn]` and `[Home]`/`[End]` scrolled the live log consoles, which are scroll areas now that the wheel and their own scrollbars drive; there is nothing left for those keys to move. `[Tab]` becomes `[Ctrl+Tab]` because bare Tab belongs to the window, where it walks the focus between controls. The headless section now says out loud what the console subsystem is there to guarantee β that exit codes, pipes and redirects behave the way a script expects β since that is the whole reason the GUI is not a Windows-subsystem binary. The prerequisites gain the MSVC build tools: `build.rs` needs the Windows SDK's `rc.exe` to embed the icon and version resources, and a machine with only rustup installed gets a link error rather than an explanation. Not covered here: `assets/banner.jpg` still shows the terminal interface. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The comment above `cargo audit` enumerated three informational warnings β RUSTSEC-2024-0436 `paste`, RUSTSEC-2026-0002 and RUSTSEC-2026-0253 `lru` β as things arriving through ratatui that could not be resolved here. All three left with ratatui, and `cargo audit` now reports nothing at all against the lock file. The policy the comment defends is unchanged and still worth stating, so only the list is replaced: a gate that goes red for reasons nobody can act on is a gate people learn to ignore. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The banner is the one place the old front end survived as pixels: the tagline read "ADVANCED WINDOWS DIAGNOSTICS & REPAIR TUI" and the fourth feature box read "Blazing Fast Rust TUI". The previous commit noted both as uncovered. Only the leading letter differs between TUI and GUI, and the banner already contains a capital G in DIAGNOSTICS β same line, same face, same weight, same drop shadow β so the replacement is lifted from the image rather than typeset into it, which is what keeps it from reading as a patch. The feature box sets its text at 0.84 the size and sits on a card with a vertical gradient, so that copy is scaled and composited as a difference against the flat background it came from, which carries the ink and the shadow onto a differently coloured surface. Re-encoded at quality 100: at 95 the file dropped from 520 KB to 195 KB, which for a picture that is mostly soft glow around a shield is a lot of gradient to throw away for two letters. At 100 it lands on 523 KB, so the rest of the artwork is preserved. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
v0.4.0's diagnostics arrive unchanged. The WHEA hardware error logger, the crash dump analyser, the wider cleaner sweep, the scheduled task resolution, the repaired-findings fix and scan state persistence all merged without a conflict, because none of them ever reached the front end β the same seam that made the move to egui cheap makes this merge cheap. What did conflict was the terminal itself, in five files, and the window wins each time: the ratatui views v0.4.0 had edited are deleted, `ensure_terminal_size` and `run_tui` go with them, and the log scroll offsets they carried are egui's own scroll areas now. Three things had to be carried across rather than dropped. Page Up, Page Down, Home and End move the triage selection again. They are new variants on the toolkit-neutral `Key`, translated in `gui::keys` the way every other key is, and they page by a fixed ten rather than by the height of the visible list: a terminal's list had one height, a window's changes as the user drags the frame, and a key that travels a different distance each time is worse than one that travels a predictable one. The dispatch test v0.4.0 wrote for these keys came with them, on `Key` instead of `KeyCode`. The reboot-pending badge is drawn again. `has_pending_reboot` and the state behind it merged cleanly, but the two places that showed it β the header and the issue list β were terminal widgets, so nothing in the window said a repair was waiting on a restart. The header carries the badge and a pending row shows `reboot` where an open one shows its checkbox, which is also what `toggle_select_all_issues` skips over. `App::new` now restores the last scan from `%APPDATA%`, which makes every test that builds an `App` depend on whatever the machine it runs on has scanned. CI never sees this and a developer's machine always does. The GUI harness starts by clearing that restored scan, next to the elevation prompt it already clears for the same reason: these tests describe the findings they mean to draw. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What this changes
WinMedic's interactive front end moves from a ratatui terminal interface to an
egui/eframe desktop window.
src/ui/is deleted,src/gui/takes its place, andthe TUI is not kept behind a flag.
Almost nothing else had to move.
Appwas already pure state with no ratatui init, the engine already reported over channels any front end can drain, and exactly
one line outside the render layer named a UI library β the
crossterm::event::KeyCodeimport inapp::input. That is now a toolkit-neutralKeyenum which the front end translates egui's events into, so the whole layereddispatch table and its tests survived unchanged.
src/app/,src/engine/,src/modules/,src/safety/and the headless CLI are untouched apart from stalecomments.
Three decisions worth reviewing on their merits:
#![windows_subsystem = "windows"]would buy a GUI that never flashes a console and pay for it by breaking every
headless guarantee: a Windows-subsystem process does not hold the shell, so
winmedic --json > out.jsonreturns before the file is finished, the documentedexit codes arrive after whatever ran next, and
release.ymlcan no longercapture
winmedic.exe --versioninto the variable it compares against the tag.utils::consolehands the console back at GUI start instead, and only whenGetConsoleProcessListsays this process is alone in it. The cost is a consolewindow visible until
mainis reached.alternative and was rejected because
swap_in_placeinutils::self_updateisbuilt around updating exactly one file, with one checksum and one signature.
ratatui β instability β darlingand left with ratatui; egui 0.36 sets the new one. Verified against a real 1.95
toolchain, not assumed.
Three pieces of state stopped being reachable once the drawing changed, and are
handled rather than left dangling: the log scroll keys and their offsets are gone
because the consoles are scroll areas now;
/no longer switches on acharacter-capture mode, since a window has a real text field bound to the same
string; and the settings pane now draws which row is selected and which of its two
lists owns the arrow keys, both of which the keyboard moves and neither of which
was visible.
Dropping ratatui also clears the three RUSTSEC advisories the audit job's comment
documented as unfixable (
paste, 2Γlru).cargo auditnow reports nothing.Related issue
None.
Does it change what WinMedic does to a system?
No diagnostic or repair logic changed. Every module, every fix path, the audit
log, the registry backups and the restore-point handling are byte-identical; what
changed is which pixels the same state is drawn as.
Checks
cargo fmt -- --checkcargo clippy --locked --all-targets -- -D warningscargo test --lockedβ 549 testsMockCommandRunner, not real commandsThe nine ratatui render tests become nine
egui_kittesttests asserting againstthe accessibility tree, which is the more honest target: a label missing from it is
missing for a screen reader, not merely for a test.
What was verified, and how
Windows 11 26200, Rust 1.98.1, MSVC toolchain.
cargo fmt -- --check,cargo clippy --locked --all-targets -- -D warningsclean; 549 tests pass.
cargo auditclean against the new lock file.cargo +1.95 check --locked --all-targetspasses, so the declared MSRV is real.cargo build --locked --release(LTO,panic = "abort") links, and the releasebinary opens its window and closes cleanly.
winmedic.exe --versionis capturable into a shell variable (this is the exactstep
release.ymlperforms), and--json --scandelivers 135 KB of valid JSONthrough a pipe with exit code 2.
Four defects were found by that verification rather than by the compiler, and are
fixed here: the settings pane's focus hint used the arrow glyphs the terminal
version had and all three rendered as empty boxes, because egui bundles a Latin
text face; the confirmation dialog's primary button drew dark text on a dark
surface; the selected setting row and the arrow-key focus were invisible; and
/was handling keystrokes twice.
every_character_the_window_draws_has_a_glyphguards the first of those, and was itself checked by reintroducing the defect and
confirming it goes red β the first version of that test passed while the bug was
still on screen, because it read AccessKit's
labeland static text lives invalue.Honest gaps. No full scan has been driven through the window end to end; the
Health Scan and Repair Center tabs are covered by tests with realistic run state
(a failed module, a partial repair pass, console output), but nobody has watched a
real scan progress in the GUI. The release binary grew to 7.9 MB from the bundled
fonts and the OpenGL renderer.
π€ Generated with Claude Code