Skip to content

Tags: iliyami/MacSai

Tags

v1.11.4

Toggle v1.11.4's commit message

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature.
Bump version to 1.11.4 for release (#75)

v1.9.0

Toggle v1.9.0's commit message

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature.
CI: notarize the DMG only + cache SwiftPM .build (faster, robust rele…

…ases) (#63)

v1.8.4

Toggle v1.8.4's commit message

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature.
Fix menu-bar launch crash on macOS 26 — hop to main actor via async o…

…pen (1.8.4) (#60)

NSWorkspace.openApplication's completion handler is delivered on
LaunchServices' background queue (com.apple.launchservices.open-queue).
Because MenuBarLauncher is @mainactor, the trailing closure was inferred
main-actor-isolated; on the macOS 26 runtime its main-actor executor
assertion traps (SIGTRAP) the instant it fires off-main — crashing the
app when the menu-bar widget launches the helper. Older runtimes
tolerated the violation, so it only surfaced on macOS 26. Fixes #58.

Switch to the async openApplication overload inside a @mainactor Task:
the continuation resumes on the main actor, so the lastError write is
safe and no main-actor closure is ever handed to LaunchServices.

Adds a regression test (openHelper against a non-launchable URL) that, on
macOS 26, aborts the test process if this path ever touches @mainactor
state off-main again. Test count 484 -> 485.

v1.8.3

Toggle v1.8.3's commit message

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature.
Docs: use star-history /chart embed with light+dark legend (#59)

v1.8.2

Toggle v1.8.2's commit message

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature.
Mac Clean 1.8.2 — community feedback #21: file-list UX, uninstaller, …

…scan persistence, settings & sidebar folding

Lands the stacked work from #44#48 in one release (1.8.2). Closes #21.

- File lists: collapsible categories, whole-row select, Reveal in Finder (#44)
- Uninstaller: confirm dialog, real app icons, removable non-critical Apple apps (#45)
- Scans preserved across navigation; views kept alive (no switch lag, scans continue) (#46, #48)
- Settings: dynamic installed-language discovery + search, English always kept; sidebar group folding; removed dead sidebar toggle (#47, #48)
- Updater: real app icons; Smart Scan real cleanup; SpaceLens off-main + zoom-out (earlier in stack)

v1.7.0

Toggle v1.7.0's commit message

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature.
Smart Scan real cleanup + SpaceLens un-freeze & zoom-out (1.7.0) (#43)

Closes #31, #37, #38.

v1.6.1

Toggle v1.6.1's commit message

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature.
Fix Trash Bins: permanently delete instead of re-trashing (no-op) (1.…

…6.1) (#30)

Emptying the Trash reported success but never deleted anything — a
rescan re-found every item and ~/.Trash stayed full.

Root cause: CleanActions.executeUserClean routed every non-
universalBinaries category, including .trashBins, through the engine's
.trash mode (FileManager.trashItem). Trash Bins items already live in
~/.Trash, and trashItem on an already-trashed file is a silent no-op:
it returns success (so removedCount incremented and the UI showed
"cleaned N items, freed X bytes") while leaving the file exactly where
it was.

Fix: partition selected items by category and send .trashBins through
.permanent mode (FileManager.removeItem), which actually empties the
Trash. Ancestor/descendant dedup is applied to the permanent path too.

Because emptying the Trash is now irreversible, the Trash Bins Clean
button shows an "Empty the Trash? … cannot be undone" confirmation on
every empty (ModuleContainerView.confirmEmptyTrash), regardless of
selection size. Other modules still move to Trash (recoverable) and
keep the existing large-selection confirm.

Adds a red→green regression test that places a real file in ~/.Trash
and asserts executeUserClean removes it.

v1.6.0

Toggle v1.6.0's commit message

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature.
README: add Mole, surface recently-shipped features in comparison + s…

…afety model (#26)

* README: add Mole to comparison, surface recently-shipped features

Comparison table:
  - Add Mole column (tw93/Mole — 54.2k stars, CLI-first MIT cleaner)
  - New rows for what the codebase actually does now and what
    differentiates Mac Clean from the rest:
      * Native GUI app                (CLI-only callout for Mole)
      * Universal Binary thinning     (lipo-based fat-binary slimming)
      * In-app activity log viewer    (post-clean sheet with copy-all)
  - Test count badge 275 → 388

Features section:
  - System Junk row calls out Universal Binary thinning with a one-
    sentence mechanic (lipo + native-arch detection)
  - Maintenance row mentions severity tagging, 'Run All' confirmation,
    mid-flight cancellation

Safety Model:
  - Adds firmlink canonicalization (var↔private/var etc.)
  - Adds pre-scan cleanability filter (drops items access(W_OK) denies
    so the UI never offers root-owned or UF_DATAVAULT items)
  - Replaces stale '10,000 file cap' line with the real story —
    chunked cleanup, 50k confirmation, recursive byte accounting
  - Adds in-app log viewer + 30-day prune callout
  - Adds kernel-enforced XPC code-signing requirement

Acknowledgments:
  - Fix dead links: Mole (nicehash/Mole → tw93/Mole) and
    Tencent Lemon Cleaner (nicehash/Lemon → Tencent/lemon-cleaner).

* README: document the menu bar widget with screenshot

Adds assets/menu_bar.png and rewrites the "Menu Bar Monitor" section
into "Menu Bar Widget" — the shipped glassmorphism widget: 2×2 stat
ring grid (CPU/Memory/Disk/Battery), network/uptime/swap, actionable
Recommendations, Protection status, Connected devices, and throttled
health alerts. Bumps the test badge 388 → 403 to match main.

Merged main into this branch first so it carries the merged menu-bar
and scan-correctness work. Verified the full local gate
(check-version-sync + swift build + swift test, 403 passing) before
pushing.

v1.5.3

Toggle v1.5.3's commit message

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature.
Fix three scan-correctness bugs: Trash FDA, duplicate rows, inflated …

…estimate (#29)

Root cause shared by two of these: TargetedScanner swallowed permission
denials and never de-duplicated overlapping targets.

1. "Trash is empty" on a full Trash. ~/.Trash is TCC-protected; without
   Full Disk Access the recursive enumerator silently yields nothing and
   fileExists() still returns true, so the scan looked empty. The scanner
   now probes each target root with open(O_RDONLY|O_DIRECTORY) and reports
   EPERM/EACCES via the new ScanOutcome.permissionDenied. TrashBinsView
   surfaces a "Full Disk Access needed" empty-state (Open Settings /
   Rescan) instead of a false "Trash is empty."

2. Large & Old Files listed the same file twice. That module scans ~
   recursively AND ~/Downloads recursively, so Downloads files were
   enumerated by both targets. Scan results are now de-duplicated by URL.

3. Cleanup freed less than the estimate (~2 GB estimated, 934 MB freed).
   Same duplicates: the pre-clean estimate summed each duplicate FileItem,
   while Clean trashes each URL once (the second copy hits a benign
   "already gone" skip). selectedSize() now counts each URL once, so the
   "X will be freed" preview matches reality even when a file appears in
   multiple categories.

Bump 1.5.2 -> 1.5.3.

v1.5.2

Toggle v1.5.2's commit message

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature.
Filter un-cleanable items at module boundary, not just ScanCoordinato…

…r (1.5.2) (#25)

* Add Array<ScanResult>.filteringUncleanable() — the contract carrier

Modules will call this on their results before returning so every
consumer (ScanCoordinator AND per-module ViewModels/Views that call
`module.scan()` directly) sees a filtered set. The single 1.5.1 wire
in ScanCoordinator was a partial fix — it only covered Smart Scan;
direct module.scan() calls from SystemJunkViewModel, MailAttachmentsView,
TrashBinsView, MalwareView, PrivacyView, LargeOldFilesView, and
DuplicatesView still leaked uncleanable items to the UI.

Adds one Array-extension test asserting category + autoSelect are
preserved across the filter.

* Apply .filteringUncleanable() inside every producing module's scan()

Each ScanModule now returns only items the current process could
trash. Callers don't need to know about the filter — the contract
travels with the result. This is what 1.5.1 was supposed to be: the
fix has to live at the module boundary, not at the ScanCoordinator,
because seven views/ViewModels call `module.scan()` directly:

  - SystemJunkViewModel       — the path the user reported on
  - MailAttachmentsView
  - TrashBinsView
  - MalwareView
  - PrivacyView
  - LargeOldFilesView
  - DuplicatesView

ScanCoordinator (Smart Scan) was the only path that went through
the 1.5.1 inline filter, so the per-module Clean flow leaked the
exact same 10 un-cleanable items the filter was designed to drop:
/Library/Caches/com.apple.*, /private/var/log/com.apple.xpc.launchd/*,
/Library/Logs/PaloAltoNetworks/*, and the data-vaulted
~/Library/Caches/com.apple.* set.

The three modules that don't produce file results (Optimization,
Maintenance, Updater, Uninstaller, SpaceLens, Shredder all return
[] from scan()) are untouched.

* Bump 1.5.1 → 1.5.2: filter at module boundary, not just coordinator

Patch bump. Same UX outcome as 1.5.1 intended — un-cleanable items
disappear from the post-clean error count — but the fix is applied
inside each producing module's scan() instead of solely at
ScanCoordinator, so it covers every per-module Clean flow.