Releases: CaseReed/wincleaner
Release list
WinCleaner v0.10.1
Fixed
- Force-closing a browser now only touches that browser, in this session.
The second pass, the one that catches whatever outlived the main process,
used to terminate any pid from the list that was still a live process —
and Windows hands pid numbers straight back out, with three seconds of
waiting for it to happen. It now re-lists the browser by name and keeps the
intersection, so a number that came back as something else is left alone.
The list of processes is narrowed to the current logon session as well: the
same account can be signed in twice, on the console and over RDP, and the
other session's browser has windows nobody here can see. And Firefox is
finally read correctly — its children are marked-contentproc, not
--type=, so everyfirefox.exeused to pass for a main process and be
terminated one by one. - The command line refuses an ambiguous line instead of measuring the wrong
thing.--rules a,ameasured that rule twice and counted its files twice
in the totals; a repeated id is now kept once. A second--rulessilently
replaced the first: it is a usage error, exit 2. And the arguments are read
asargs_os, so a line carrying bytes that are not valid text is refused
with the usage rather than panicking — in a windowed binary, that panic had
no console to print to. - The release notes no longer imply the portable executable is signed.
Only the installers go through SignPath; the note the workflow writes once
they are signed now says so, and the README anddocs/releasing.mdsay it
too. - The quit confirmation is announced, and the focus goes somewhere. The
confirmation next to a background browser is a live region now, so its
sentence is read out as it appears. Once the quit goes through, the banner
and the button that raised it disappear together and the focus used to fall
to the top of the page: it lands on Analyze instead, and back on the Quit
button when the browser refused to die.
Installers are unsigned until SignPath signing is configured in the repository secrets — see docs/code-signing.md.
Full changelog: https://github.com/CaseReed/wincleaner/blob/main/CHANGELOG.md
WinCleaner v0.10.0
Added
-
A portable build. Drop a file named
portable.txtnext to
wincleaner.exeand the application stops writing to%APPDATA%: the
exclusions list and the Recycle Bin measurement move to
<exe dir>\WinCleaner\, so a copy on a USB stick leaves nothing behind on
the machine it is run from. The content of the marker is never read — it is
a marker, not a configuration file, and a user creating it by hand cannot
get it wrong. One resolver decides for both stores, so a store added later
cannot quietly stay in the roaming profile, and a sandbox still keeps
precedence over both layouts. Settings → About says "Portable" when the mode
is active, which is the only way to tell the two apart from inside the
window: no path crosses the IPC boundary here either. Theme, language and
the automatic-update consent stay in WebView2's own storage and do not
move. Releases now carryWinCleaner_<version>_x64-portable.exenext to the
MSI and the NSIS installer; WebView2 is still required, and it ships with
Windows 11. -
A read-only command line.
wincleaner --analyzemeasures every rule and
prints a table;--jsonprints one stable object instead, for a script or a
support ticket, and--rules id,idnarrows the run. It opens no window: the
arguments are answered before Tauri starts, and the release binary attaches
the calling terminal's console to print at all. The measurement is the
application's own — same catalogue, same detected Winapp2 entries, same
stored exclusions with the same fail-closed rule, same concurrent scan — so a
figure printed here is the figure the window would show. There is no
--cleanand there never will be: the confirmation before a deletion is an
invariant, and an unattended flag would be the way around it. No option
takes a path either —--rulesaccepts rule ids and refuses anything the
catalogue does not know. Exit codes are 0 for a run, 1 for a scan error and
2 for a bad argument, which is what a script actually branches on.
Installers are unsigned until SignPath signing is configured in the repository secrets — see docs/code-signing.md.
Full changelog: https://github.com/CaseReed/wincleaner/blob/main/CHANGELOG.md
WinCleaner v0.9.2
Added
- Quitting a browser that only runs in the background. Chrome and Edge
keep a handful of processes alive after the last window is closed, and one
extension is enough to hold them there for the rest of the session: on the
machine this was written for, ninechrome.exeprocesses with no window
between them, and a Clean that reported the cache files "in use" every time.
The banner that already told those two cases apart now does something about
the second one: a Quit {browser} button next to the warning, a
confirmation that says what it costs — the browser is force-closed, and it
may offer to restore its session next time — and a count of the processes
stopped. Chrome has no external "quit" command, so the newquit_browser
terminates its main process (the one with no--type=on its command line)
and gives the children three seconds to follow it out before terminating
whatever is left. It takes an executable name and matches it against the
three browsers the detection already knows, never a path, and it re-checks
at the moment of the click that no window has appeared since the banner was
painted — a browser you can see is one you close yourself. The banner also
names the permanent fix now: the "Continue running background apps" setting
in the browser itself.
Installers are unsigned until SignPath signing is configured in the repository secrets — see docs/code-signing.md.
Full changelog: https://github.com/CaseReed/wincleaner/blob/main/CHANGELOG.md
WinCleaner v0.9.1
Fixed
-
The Recycle Bin was the whole Analyze. Measured on a loaded profile,
SHQueryRecycleBinWtook 243 s cold for 110,553 items and 19 GB, while
every other rule of the same run finished in seconds: the Analyze simply
waited on that one call. The last measurement is now remembered in
%APPDATA%\WinCleaner\recycle-bin.toml, keyed on a cheap fingerprint of the
bin — theLastWriteTimeof<drive>\$Recycle.Bin\<your SID>on each fixed
drive, plus the drive list. Windows restamps that directory whenever
anything is filed into the bin or taken out of it, so emptying the bin, or
dropping one more file into it, invalidates the cache on its own; the drive
list covers the one case timestamps alone would miss, a bin on a disk
plugged in between two runs. A rule answered from the cache says so on its
row. Unlike the exclusions store this one deliberately fails open: a
missing, unreadable or corrupt file is ignored and rewritten, because the
worst a lost cache can cost is one slow Analyze — nothing is ever deleted on
the strength of a cached figure. -
The progress bar named the wrong rule.
scan-progresscarried only the
rule that had just finished, so while the Recycle Bin took its four
minutes the hero read "84 / 85 · AMD" — a rule that was long done. The event
now also carries the rules still in flight, and the counter says
"Analyzing 84 / 85 · still measuring: Recycle Bin" while any of them is,
falling back to the finished rule on the last event. -
Excluding a folder hid only the row that was clicked. The stored pattern
is%VAR%\folder\**, so the whole folder is out of the rule — but its
siblings stayed on screen, each still offering an Exclude button for a file
already excluded. Every listed path under that folder now goes at once
(matched case-insensitively, as everywhere else a Windows path is compared),
and the file count drops by the number of rows actually hidden. The byte
total is still left as measured, with the same "Analyze again to refresh the
figures" note: a scan result carries no per-file size, and inventing one
would be worse than admitting the figure is stale. -
The cleanup report quoted raw shell errors. A locked file read
Error during a `trash` operation: Unknown { description: "Some operations were aborted" }, and a file the system refuses read "(os error 1920)".
Every skipped item now also carries a stable code —in-use,
access-denied,not-found,other, derived once in Rust from the
sharing- and lock-violation classes, os errors 5, 32, 33 and 1920, and the
shell's own "aborted" — which the window turns into its own sentence ("File
in use or locked"). The raw message stays one hover away, and stays verbatim
in the JSON report, where a script or a bug report needs it. -
The deletion-mode help always described Auto. The footer rendered
mode.autoHelpwhatever was selected, so picking Permanent still read
"recycle bin for the rest". It now shows the help for the mode in the
picker, in both languages: the Recycle Bin keeps everything recoverable, and
Permanent recovers nothing. -
Switching screens threw the analysis away. The scan results, the last
cleanup report and the ticked boxes lived inside the Cleanup panel, which
the screen switch unmounts: a glance at Space or Settings cost a full
re-Analyze — minutes, on the profile that needs it most. They now live in
App, so they survive the trip. Entering or leaving the sandbox still
clears them, because the catalogue and the profile both change with it. -
The Space screen showed "0 B" before measuring anything. Nothing had
been measured, and a zero is a figure. It now shows the em dash the Cleanup
hero already shows in the same situation.
Installers are unsigned until SignPath signing is configured in the repository secrets — see docs/code-signing.md.
Full changelog: https://github.com/CaseReed/wincleaner/blob/main/CHANGELOG.md
WinCleaner v0.9.0
Added
- A Space screen: where your own files sit, without a delete button. A
fourth entry in the sidebar measures the six known folders of your profile —
Downloads, Desktop, Documents, Pictures, Videos and Music, resolved through
SHGetKnownFolderPathso a folder redirected into OneDrive is the one that
gets measured — and shows the total, a bar per folder, the 100 largest files
and the 20 largest folders, each with Reveal in Explorer. The walk goes
through the same two guards as Analyze: a known folder that resolves outside
the profile, or that is a reparse point, is refused and named instead of
followed, and a junction below one is never crossed. Nothing on this screen
deletes anything and the back end offers it no way to: this is your own
data, and the gesture that removes a file stays with you, in Explorer. The
reveal takes the row's index, never its path, exactly ascleanand
add_exclusiondo. Unavailable while a sandbox is active, because the
folders it measures are the real ones.
Installers are unsigned until SignPath signing is configured in the repository secrets — see docs/code-signing.md.
Full changelog: https://github.com/CaseReed/wincleaner/blob/main/CHANGELOG.md
WinCleaner v0.8.1
Fixed
- Three spots in the French interface still read in English. The
copyable cleanup report's per-rule lines now go throughruleLabel, so a
French report says "Fichiers temporaires" instead of "Temporary files" (the
JSON report is unaffected: itslabelstays English and language-neutral,
as documented). The Exclusions section in Settings now loads the rule
catalogue alongside the exclusion list and shows each entry's French label
when the interface is French, falling back to the back end's own label for
an id no longer in the catalogue, and formats the "Added on" date for the
locale instead of printing the rawYYYY-MM-DDstring (parsed as a local
date, so the day never shifts). The rule row's sr-only file count is now a
proper.one/.otherpair, so a screen reader hears "1 fichier" rather
than "1 fichiers".
Installers are unsigned until SignPath signing is configured in the repository secrets — see docs/code-signing.md.
Full changelog: https://github.com/CaseReed/wincleaner/blob/main/CHANGELOG.md
WinCleaner v0.8.0
Added
-
A file or a folder can be kept out of a rule for good, picked from the
paths that rule actually found. "Show the paths" gains two actions on every
line — Exclude this file, Exclude its folder — and Settings gains an
Exclusions section listing what was kept out, with the rule it belongs to,
the date, and a way to drop it again. The back end never receives a path: the
command takes an index into the last analysis of that rule, the same reason
scanandcleantake only rule ids, and the same reason there is no
free-form glob editor. What gets stored is a pattern written with the rule
variables —%TEMP%\a\b.logfor a file,%TEMP%\a\**for a folder — so the
file carries no account name and survives a profile that moves; a path under
none of the four allowed variables is refused. The list lives in
%APPDATA%\WinCleaner\exclusions.toml, written through a temporary file and
a rename so an interrupted write cannot leave a half-file behind, and under
the sandbox root instead while a sandbox is active, so the sandbox stays
isolated. The patterns are merged into the rule's ownexcludelist before
the scan and again before the clean, and go through the validation any
rules.tomlexclude goes through — a stored pattern that is not a valid glob
is refused exactly like a bad rule. Becausecleanre-walks each rule rather
than trusting the paths the window is holding, a file excluded after the last
Analyze is already spared by the clean that follows. A store that exists but
cannot be read or parsed fails the scan and the clean outright rather than
proceeding as though nothing were excluded. -
The ten native rules now have French labels.
rules.tomlgains optional
label_fr,description_frandcategory_frfields (Rule/RuleSummary
in Rust,src/lib/rule-i18n.ts::ruleLabel/ruleDescription/ruleCategory
on the front end, falling back to the English field when the French one is
absent). A native rule is grouped, searched and announced under its French
name when the interface is French — search still matches the English name
too — including the "Analyze 3 / 85 · …" progress line, which now looks the
label up by rule id from the loaded summaries instead of the (deliberately
unlocalised) event Rust emits. Winapp2 (community) rules stay English, as
documented in Settings. -
The last cleanup report can be copied to the clipboard. "Copy report"
puts a readable summary on the clipboard — app version, date, deletion mode,
a total line of what Clean actually freed, the skipped paths if any, and
per rule how many files and bytes the last Analyze measured for it plus
its skipped count — in the current interface language.CleanReportonly
ever returns aggregate totals, never a per-rule breakdown, so the per-rule
figures are explicitly labelled as measured rather than freed: a rule with
a skipped file frees less than it measured, and the report says so instead
of implying otherwise. "Copy as JSON" puts the same data on the clipboard as
a stable, language-neutral object (version,generated_at,mode, a
notespelling out the same distinction,rules[]withfiles_measured/
bytes_measured,totalswith the realfiles_deleted/bytes_freed, and
skipped[]). Both reuse the clipboard mechanism already behind "Copy link"
in Settings (navigator.clipboard.writeText, a success or failure toast)
and build off a pure, unit-testedsrc/lib/report.ts. No file is written.
Installers are unsigned until SignPath signing is configured in the repository secrets — see docs/code-signing.md.
Full changelog: https://github.com/CaseReed/wincleaner/blob/main/CHANGELOG.md
WinCleaner v0.7.0
Added
-
Added a native rule for per-user Windows Error Reporting reports.
windows.wer-reportsclears%LOCALAPPDATA%\Microsoft\Windows\WER\ReportQueue
andReportArchive— the crash reports Windows queues and archives for this
user, regenerated on the next crash and not user data. The machine-wide
%ProgramData%\Microsoft\Windows\WER\*store is out of scope: no allowed
variable resolves there.risk = medium,default_checked = false, same
category and voice aswindows.crash-dumps. -
The interface speaks English and French. Every string the application
writes itself now goes through a small typed dictionary (src/i18n/en.ts,
src/i18n/fr.ts, keys typed from the English one, so a missing translation
failstsc): labels, buttons, toasts, the confirmation, the progress lines,
the sandbox banner and every accessibility string. Settings gains a Language
selector — System, English, Français — that followsnavigator.language
until it is touched, applies without a reload, is remembered in
localStoragebeside the theme and the update-check consent, and sets
document.documentElement.lang. Sizes and counters are formatted for the
locale (1.5 KB/1,5 Ko). What stays English whatever the setting: the
rule labels and descriptions, which come fromrules.tomland from Winapp2,
the "What's new" body extracted fromCHANGELOG.md, the release notes
GitHub returns, and this changelog.
Changed
-
Analyze scans its rules concurrently instead of one after another.
commands::scan_rules_withused to walk the 84 rules of a full catalogue in
a straight loop, so the wall time was their sum — dominated by a couple of
slow ones (the Winapp2 catch-all, and the Recycle Bin's own OS call). It now
runs them on a small worker pool (std::thread::scope, sized to
available_parallelismcapped at 8), each rule scanned by the exact same
scan::scan_rule/scan_rule_with_apifunction as before.scan_bench
(cargo run --release --example scan_benchinsrc-tauri), on this machine:
26s cold / 17s warm before, 11.2s cold / 7.3s warm after — the warm run now
lands almost exactly on the slowest single rule (7.3s total against a 7.3s
Recycle Bin query) instead of their sum. Thescan-progressevent still
fires once per rule with a running byte total, but the order it arrives in
now follows completion rather than catalogue order; the result list handed
back to the front end is unaffected, since it is reassembled by index after
every rule finishes. -
Measured the Recycle Bin batching gain: about 2.6x. The new opt-in
benchmark (cargo run --release --example trash_benchinsrc-tauri)
creates its own fixture files and times both strategies on this machine: one
trash::deleteper file ran at 77-80 files/s, matching the ~75 files/s seen
on the real 77,000-file run that motivated the batching change; batches of
500 throughtrash::delete_allran at about 205 files/s, a 2.5x-2.7x
speed-up depending on fixture size (2,000 and 5,000 files tested).
Installers are unsigned until SignPath signing is configured in the repository secrets — see docs/code-signing.md.
Full changelog: https://github.com/CaseReed/wincleaner/blob/main/CHANGELOG.md
WinCleaner v0.6.0
Fixed
- The browser warning now tells the truth about background processes. Closing every
Chrome window is not enough when "Continue running background apps" is on; the
banner used to say the browser was open. It now names the browser, counts its
processes, distinguishes a visible window from background-only processes, and
is refreshed every time Analyze is clicked. Closes #5.
Added
- The Clean has a progress bar of its own. Cleaning used to be a disabled
button reading "Cleaning…" for as long as the work took — thirteen minutes on
a measured run.cleannow emitsclean-progressas each rule closes and
every 500 files inside a rule, and the hero draws the same determinate bar as
Analyze: "Cleaning 3 / 9 · Temporary files", with the freed bytes and the
file count ticking up in the big-number position. The live region follows the
rules, not the files, so a rule deleting 59,000 of them is not read out a
hundred times over.
Changed
-
Recycle Bin mode is far faster: files go to the bin 500 at a time. Each
trash::deleteis oneIFileOperation, and its fixed cost — COM plumbing,
the shell's own progress reporting, one undo record — is what dominates a bin
full of small files: a real run measured about 75 files/s, so 59,000 files
took thirteen minutes. Onetrash::delete_allper batch of 500 divides that
by the batch. Thedeletable_pathguard still runs per file, immediately
before the file joins its batch, and a batch the shell refuses is retried one
file at a time soskippedstill names exactly what is still on disk.
Permanent mode is unchanged: oneremove_fileper file. -
Keyboard and screen-reader pass over the whole interface. The sidebar is
a namedMainlandmark with a roving tabindex — the arrow keys,Homeand
Endmove between the three screens, and it costs one Tab press to get past
instead of three; the theme toggle states which theme is on, not only what
the next press does. On Cleanup, each category header names the rows it
folds, each "Show the paths" names its rule and opens a named region, and the
confirmation takes the focus when it appears, cancels onEscapeand hands
the focus back to the Clean button. The scan announces itself every ten rules
and on completion — not on every rule, which would be hundreds of
interruptions — and the cleanup report, the sandbox verdict and the sandbox
banner announce themselves too. The reclaim gauge states its total and its
three largest rules in words. The startup table is named and every row toggle
reports its outcome. Nothing moved on screen: no layout, no wording, no test
id changed. -
Two measured contrast failures fixed. The focus ring on the controls that
carry no border — sidebar entries, category headers, paths triggers, the
search field, the mode selector — was drawn at half opacity (1.66:1 against
the card in the light theme) and is now solid (4.87:1). The Clean and Confirm
buttons wrote--destructiveon a wash of itself: 4.11:1 light, 3.75:1 dark,
2.87:1 hovered, all under the 4.5:1 body text asks for. A new
--destructive-foregroundtoken takes the worst case to 4.71:1 with the
fills untouched. Everything else measured clean,--muted-foreground
included (5.11:1 to 6.42:1 on every surface, both themes), and
src/theme-contrast.test.tsrecomputes all of it from the stylesheet so the
next nudge to a colour fails in CI. -
prefers-reduced-motionis now honoured app-wide, the shadcn transitions
and the loading spinners included, by one rule in the base layer rather than
a class to remember on each element.
Installers are unsigned until SignPath signing is configured in the repository secrets — see docs/code-signing.md.
Full changelog: https://github.com/CaseReed/wincleaner/blob/main/CHANGELOG.md
WinCleaner v0.5.1
Fixed
- Two Winapp2 entries deleted application content, not cache.
[Vortex *]
swept%LocalAppData%\Vortex-Updater, where Vortex stages the executables of
its own pending update, and[Discord *]deleted the Squirrel.nupkg
packages the installed tree is unpacked from and the next delta is computed
against. The converter now carries an explicit app-content deny-list
(winapp2.rs::APP_CONTENT_DENY): a denied directory prefix sinks the whole
FileKey, a denied file spec removes that spec. Refused keys are counted by
app_content_keysand an entry left with noFileKeyby
dropped_app_content; on the embedded file this refuses 3 keys and drops no
entry — both applications keep their real cache keys. Closes #2. - Sandbox folders are no longer left behind in
%TEMP%. Leaving the
sandbox was the only thing that removed its directory, so a crash, a kill or
a window closed with a sandbox still open left
%TEMP%\wincleaner-sandbox-…and its few hundred files there for good.
WinCleaner now sweeps those leftovers at every start — only ever a directory
whose owning process is gone, never one a running WinCleaner is using — and
Settings → Sandbox shows a line naming how many are left and how much they
take, with a Remove button, so no restart is needed. Closing the window with
a sandbox active also attempts the removal on the way out, within a short
budget so the window never hangs on it. The sweep unlinks junctions before
removing a tree, exactly as leaving the sandbox does, and never counts nor
follows what is on the other side. Closes #3.
Installers are unsigned until SignPath signing is configured in the repository secrets — see docs/code-signing.md.
Full changelog: https://github.com/CaseReed/wincleaner/blob/main/CHANGELOG.md