A free, open-source macOS window manager with a spatial 3×3 keyboard grid and multi-tap cycles. Snap, resize, and tile windows on your Mac with global keyboard shortcuts — a modern Spectacle, Rectangle, Magnet, and Moom alternative, built natively for Apple Silicon and licensed under Apache 2.0.
WindowKit is a lightweight menubar app for macOS that snaps the focused window to halves, quadrants, thirds, sixths, a 3×3 spatial grid, or fullscreen — all with keyboard shortcuts. If you've used Spectacle, Rectangle, Magnet, BetterSnapTool, or Moom, WindowKit will feel familiar — with a spatial-grid twist that is unique to it.
- Free & open source — Apache 2.0 licensed, no paywall, no telemetry, no account required.
- 3×3 spatial grid —
U / I / O,J / K / L,M / , / .map to the 9 screen positions. The keys' physical layout is the layout of the snap targets. - Spatial-key cycles — repeat a grid shortcut to cycle anchored layouts. For example,
⌘⌥Icycles top half → top third → top two-thirds → top-center 1/9. - Apple Silicon native — arm64 build for M1 / M2 / M3 / M4 Macs on macOS 14 Sonoma or later.
- Works with Electron apps — Chrome, Discord, Cursor, VSCode, Slack. A one-time
AXEnhancedUserInterfacenudge per app per launch handles Chromium's usual Accessibility API quirks. - Dock-aware placement — windows never cover a pinned Dock. Auto-hide Dock is handled the way macOS expects.
- Geometry-driven cycles — repeat a spatial shortcut whenever you like; the current window layout determines the next step.
| WindowKit | Spectacle | Rectangle | Magnet | Moom | |
|---|---|---|---|---|---|
| Free | ✅ | ✅ | ✅ | ❌ ($4.99) | ❌ ($10) |
| Open source | ✅ Apache 2.0 | ✅ (archived 2017) | ✅ MIT | ❌ | ❌ |
| Halves / quadrants via keyboard | ✅ | ✅ | ✅ | ✅ | ✅ |
| Thirds / sixths / 2/3 bands | ✅ | ➖ limited | ✅ | ✅ | ✅ |
| Spatial 3×3 grid (U/I/O, J/K/L, M/,/.) | ✅ | ❌ | ❌ | ❌ | ➖ custom grid |
| Multi-tap cycles | ✅ | ❌ | ➖ repeat-resize | ❌ | ❌ |
| Drag-to-snap | ❌ | ❌ | ✅ | ✅ | ✅ |
| App-specific rules | ❌ | ❌ | ➖ | ❌ | ✅ |
| Native Apple Silicon | ✅ | ❌ (unmaintained) | ✅ | ✅ | ✅ |
| Menubar-only, no Dock icon | ✅ | ✅ | ✅ | ✅ | ✅ |
Use Rectangle if you love drag-to-snap. Use Magnet or Moom if you want paid-app polish and mouse gestures. Use WindowKit if you live on the keyboard and want the spatial-grid mental model.
License: Apache 2.0 — see LICENSE. Provided "as is", without warranty of any kind, express or implied. The authors and dotfun are not liable for any damages arising from use of this software.
- Grab the latest WindowKit-x.y.z.zip from Releases.
- Unzip, drag WindowKit.app into /Applications.
- Launch it. The menubar icon (orange 3×3 grid on dotfun orange) appears.
- On first launch Gatekeeper will complain — the build is unsigned. Right-click the app → Open → Open Anyway, or run:
xattr -dr com.apple.quarantine /Applications/WindowKit.app
- Grant Accessibility when prompted (System Settings → Privacy & Security → Accessibility → toggle WindowKit on).
- Preferences… — configure, rebind, or clear every window action shortcut
- About WindowKit — logo + version
- Launch at Login — toggle auto-start with macOS
- Debug → Copy Focused Window Info — copies bundle ID / AX role / settable flags to the clipboard for bug reports
- Quit WindowKit — ⌘Q
- macOS 14+
- Swift 5.9+ / Xcode 15+
- Accessibility permission (for moving windows of other apps)
swift build -c releaseOr open Package.swift in Xcode and build the WindowKit scheme.
scripts/build-app.shProduces build/WindowKit.app. Double-click to launch; a menu-bar icon appears (the app is LSUIElement, no Dock presence).
swift run WindowKitswift testLegend: ⌃ = Control, ⌥ = Option, ⌘ = Command, ⇧ = Shift. The tables list every action available in Preferences → Keyboard Shortcuts. — means the action is unassigned by default; assign any shortcut or clear an existing one there. WindowKit keeps one action per shortcut.
| Action | Default shortcut |
|---|---|
| Left half | ⌃⌥ ← |
| Right half | ⌃⌥ → |
| Top half | ⌃⌥ ↑ |
| Bottom half | ⌃⌥ ↓ |
| Top-left quadrant | ⌃⌥ 1 |
| Top-right quadrant | ⌃⌥ 2 |
| Bottom-left quadrant | ⌃⌥ 3 |
| Bottom-right quadrant | ⌃⌥ 4 |
| Action | Default shortcut |
|---|---|
| First third (left) | ⌃⌥⌘ ← |
| Center third | ⌃⌥⌘ ↑ |
| Last third (right) | ⌃⌥⌘ → |
| First two-thirds (left) | — |
| Last two-thirds (right) | — |
| Top third | — |
| Bottom third | — |
| Top two-thirds | — |
| Bottom two-thirds | — |
| Top-left sixth | — |
| Top-center sixth | — |
| Top-right sixth | — |
| Bottom-left sixth | — |
| Bottom-center sixth | — |
| Bottom-right sixth | — |
All grid shortcuts use ⌘⌥. The letter layout mirrors the target position:
U I O
J K L
M , .
| Action | Default shortcut | Cycle from a non-matching layout |
|---|---|---|
| Top-left | ⌘⌥ U | top-left 1/4 → top-left 1/9 → top-left 2/3 × 2/3 |
| Top-center | ⌘⌥ I | top 1/2 → top 1/3 → top 2/3 → top-center 1/9 |
| Top-right | ⌘⌥ O | top-right 1/4 → top-right 1/9 → top-right 2/3 × 2/3 |
| Middle-left | ⌘⌥ J | left 1/2 → left 1/3 → left 2/3 → middle-left 1/9 |
| Middle-center | ⌘⌥ K | fullscreen → center 1/9 → center 1/3 column |
| Middle-right | ⌘⌥ L | right 1/2 → right 1/3 → right 2/3 → middle-right 1/9 |
| Bottom-left | ⌘⌥ M | bottom-left 1/4 → bottom-left 1/9 → bottom-left 2/3 × 2/3 |
| Bottom-center | ⌘⌥ , | bottom 1/2 → bottom 1/3 → bottom 2/3 → bottom-center 1/9 |
| Bottom-right | ⌘⌥ . | bottom-right 1/4 → bottom-right 1/9 → bottom-right 2/3 × 2/3 |
Cycles are based on the focused window's current geometry, not press timing. Repeating a grid shortcut advances from its current cycle step; the final step wraps to the first. Moving or resizing the window outside a cycle layout restarts at the first step.
| Action | Default shortcut | Behavior |
|---|---|---|
| Fullscreen | ⌃⌥ F | Fill the visible screen area |
| Almost maximize | ⌃⌥ = | Center at 90% of the visible screen area |
| Center on screen | ⌃⌥ C | Keep the current size and center it |
| Make larger | — | Grow by 5% of the visible screen in each dimension |
| Make smaller | — | Shrink by 5% of the visible screen in each dimension, down to 100 px per edge |
| Move to next display | ⌃⌥⌘ ] | Preserve relative position and size on the next display |
| Move to previous display | ⌃⌥⌘ [ | Preserve relative position and size on the previous display |
| Undo last move | ⌃⌥ Z | Restore the prior window frame |
| Redo last move | ⌃⌥⇧ Z | Reapply the last undone window frame |
- Non-resizable apps (e.g. System Settings panels) will be moved to the target location but keep their intrinsic size. WindowKit does not beep or error on move-only outcomes.
- The Dock is respected — snapped windows will not overlap a pinned Dock. The target frame is computed against
NSScreen.visibleFrame, which excludes the Dock and menu bar. - Auto-hide Dock lets windows use the full screen; the Dock overlays on hover (standard macOS behavior).
WindowKit requires Accessibility access to query and set frames of other apps' windows via AXUIElement. Without it, no hotkeys will take effect. On first launch the app shows an onboarding window with a button that opens System Settings → Privacy & Security → Accessibility.
Because this build is unsigned (and ad-hoc signatures change on every rebuild), macOS treats each new build of WindowKit.app as a distinct identity for Accessibility purposes. After every rebuild you must:
- Open System Settings → Privacy & Security → Accessibility.
- Remove any stale
WindowKitentry. - Add the freshly built
WindowKit.appand enable its toggle.
The onboarding window reappears whenever trust is revoked or invalidated, and hotkeys automatically disarm until trust is restored.
- Fully supported: all native Cocoa apps (Finder, Safari, iTerm, Xcode, Terminal, Preview, Mail, Messages, etc.).
- Electron / Chromium apps (Chrome, Discord, Cursor, VSCode, Slack, etc.): supported via a multi-tier window resolver and a one-time
AXEnhancedUserInterfacenudge per app per launch. The first hotkey press on such an app may feel ~30 ms slower while Chromium rebuilds its AX tree; subsequent presses are instant. - Fixed-size apps (EMEET Studio, System Settings panels, 1Password mini, Spotify mini): these will be moved to the target cell's origin but keep their intrinsic size — by design. No beep, no error.
- Apps running as root or with full-screen space privileges (most games, some installers): unreachable via the Accessibility API. This is a macOS limitation, not a WindowKit bug.
If a specific app doesn't respond, open the menubar menu → Debug → Copy Focused Window Info and share the clipboard contents — it contains the bundle ID, AX role/subrole, and which attributes are settable, which is enough to triage.
Yes. Spectacle stopped receiving updates in 2017. WindowKit keeps the same spirit — a keyboard-first, menubar-only Mac window manager — and adds a spatial 3×3 grid with multi-tap cycles to snap windows on Mac in one or more presses. Apache 2.0, no paywall, no telemetry.
Rectangle is the most popular modern Spectacle fork and is excellent if you love drag-to-snap. WindowKit has a smaller surface area — no mouse snapping, no app-specific rules — but it ships the spatial 3×3 grid (U/I/O, J/K/L, M/,/.) and multi-tap cycles that Rectangle doesn't. Both are free and open source. Pick whichever matches your mental model; they don't strictly conflict (though you may want to disable overlapping hotkeys in one of them).
Yes. The release binary is arm64-native, compiled against the macOS 14 SDK.
macOS 14 Sonoma and later (Sonoma, Sequoia, and newer). Earlier versions are not supported — SwiftUI's MenuBarExtra requires macOS 13+ and some Accessibility APIs we rely on need Sonoma.
Yes. Chromium/Electron apps get a one-time AXEnhancedUserInterface nudge from WindowKit the first time you press a hotkey on them per launch — after that, frame changes work as expected. The first press on a Chromium app is ~30 ms slower while it rebuilds its Accessibility tree; subsequent presses are instant.
macOS requires Accessibility access for any app that reads or moves other apps' windows. This is the same permission Spectacle, Rectangle, Magnet, BetterSnapTool, and Moom all require. WindowKit only uses it to set window position and size — no keystroke logging, no screen recording, no network calls.
Yes, functionally. Magnet and Moom are polished paid apps (roughly $5 – $10 on the Mac App Store); WindowKit is free and open source. If you want drag-to-snap edges or app-specific autolayout rules, Magnet or Moom are still better choices. If you want keyboard-first minimalism, WindowKit is the lightest option.
Most window managers think in halves and quadrants. WindowKit adds a third mental tier: nine positional cells whose keyboard shortcuts are arranged spatially — the letters' physical position on your keyboard matches the cell's position on screen. Repeating a grid shortcut cycles its matching layouts with no timing requirement: ⌘⌥M goes bottom-left 1/4 → bottom-left 1/9 → bottom-left 2/3 × 2/3, while the edge keys I, J, L, and , also reach their respective 1/9 cells.
All of WindowKit is in this repository under Apache 2.0. Issues and PRs welcome. See Project Layout for the module map.
App/— SwiftUI@mainapp (MenuBarExtra),ActionRunner,Info.plist, assetsSources/WindowEngine/— pureGeometry,AXWindowwrapper,ScreenResolver, coordinate converterSources/HotkeyManager/— Carbon global hotkeysSources/PreferencesStore/—Shortcutmodel,UserDefaultspersistence, default bindingsSources/PreferencesUI/— Preferences window, shortcut recorder, onboardingSources/PermissionsCoordinator/— Accessibility trust checks + publisherSources/UndoStack/— window frame historyTests/WindowEngineTests/— geometry unit testsTests/PreferencesStoreTests/— store/shortcut testsscripts/build-app.sh— release bundle assembly
Unsigned local build. Not notarized. See unsigned-build caveat above.
- v0.2.8 — Completed the 3×3 edge-grid cycles:
⌘⌥I,⌘⌥J,⌘⌥L, and⌘⌥,now end at their top-center, middle-left, middle-right, and bottom-center 1/9 cells. - v0.2.7 — Reliable Discord/Electron window management: replaced the permanent
AXEnhancedUserInterfacenudge (which actually breaks positioning — it's VoiceOver's attribute) with Rectangle's disable-around-the-write technique, and wakes Chromium's a11y tree via the side-effect-freeAXManualAccessibility. Also fixed a false "Accessibility grant is out of date" warning that flapped (and disarmed hotkeys) whenever a busy app like Discord-in-a-call was focused — the trust canary no longer treatskAXErrorCannotCompleteas a dead grant. - v0.2.6 — Simplified edge tap cycles.
- v0.2.5 — Big→small tap ordering; GitHub update checker.
- v0.2.4 — Corner keys (U, O, M, .) gain a 3rd tap: 2/3 × 2/3 in that corner.
- v0.2.3 —
⌘⌥Kcycle adds a 1/3-wide center column between the 1/9 cell and fullscreen. - v0.2.2 — Works with Chromium/Electron apps (Chrome, Discord, Cursor, VSCode, Slack) via a one-time
AXEnhancedUserInterfacenudge. Tap window default raised to 700 ms; slider ceiling raised to 1 s. - v0.2.1 — dotfun branding, stale-grant detection, Launch at Login via
SMAppService, Apache 2.0 LICENSE.
Full list on the Releases page.


