Repository navigation
Releases: amitayks/ThreeFingerSwitcher
Release list
v2.1.1
Full Changelog: v2.1.0...v2.1.1
v2.1.1-rc1
v2.1.0
v2.0.2
v2.0.1
v2.0.0 — refocused, and built to stay fast
ThreeFingerSwitcher 2.0 — refocused, and built to stay fast
2.0 is a breaking release. The on-device AI stack (Gemma/MLX, the AI command band + canvas, background agents, the notch timeline, voice/computer-use), the Files band, and the iPhone↔Mac device link (QR pairing, the local-network clipboard/file bridge, the Hub Devices page) are removed. The app is refocused on what it does best: the three-finger window switcher, the four-finger launcher (with context bands and clipboard history), Dock window previews, and per-app / per-site keyboard language. The full-featured 1.x app stays available on the v1.0.0 release and the v1 branch.
The app no longer gets slower the longer it runs
A long-standing complaint — CPU creeping up over hours, switcher previews refreshing slower, the gesture trigger developing a "needs to wake up" lag — was traced to a family of independent accumulation bugs plus a scheduling posture that let a busy Mac starve the app. All fixed:
- One trackpad listener, ever. The vendored multitouch framework's own sleep/wake handler double-started trackpad devices, orphaning one running listener per sleep/wake cycle — after N wakes every touch frame was processed N+1 times. The framework's handlers are now neutralized; the app owns sleep/wake.
- No more stacked preview sweeps, leaked observers, or hidden animation clocks (closing the welcome wizard used to leave a 20 Hz animation ticking forever). Thumbnail caches are LRU and pruned to live windows.
- A busy Mac can't starve it anymore. The process opts out of App Nap (system sleep unaffected), bounds every Accessibility round-trip to 0.5 s (the OS default is 6 s per call, answered by the other app's main thread), and re-arms its event taps out-of-band if macOS disables them under load.
- Nothing forks, hits disk, or walks a whole payload on the gesture path. With Space-row switching on, every switcher open used to spawn a
/usr/bin/defaultssubprocess on the main thread. Clipboard capture no longer hashes the whole payload or fully decodes every image. Launcher icons, blob-backed clipboard previews, Stage Manager state, and the browser's address field are memoized.
Idle CPU should read ~0% indefinitely. If it doesn't, that's a bug — please report it.
Crash and stuck-state fixes
- Opening the Hub once and closing it could crash every subsequent switcher commit (
CGWindowIDconversion on the closed window's number). - A misbehaving app's Accessibility server could crash the switcher (unguarded type casts on cross-process data).
- Sleeping the Mac with three fingers on the trackpad could leave the app swallowing every scroll event in every app until quit.
- Closing Mission Control any way but the app's own gesture left it posting a stray Escape into your app on every later commit.
- Double-firing a launcher item could open two windows; rapid Space switches could focus the wrong Space's window; a de-minimize could steal focus back from a newer selection.
- Taps now re-arm automatically when Accessibility is granted mid-session.
Install
Download the DMG below, drag ThreeFingerSwitcher to Applications, launch it, and follow the First Touch wizard. The DMG is Developer-ID-signed and notarized. Upgrading from 1.x in place keeps your permissions and settings.
For developers
The investigation, rejected alternatives, and deferred follow-ups are documented in openspec/changes/fix-progressive-cpu-degradation/; the load-bearing-but-odd-looking pieces are listed in CLAUDE.md → "Progressive-degradation guardrails". 783 unit tests.