Spacetile is source-available: anyone can read its code. This page is for developers who want to build it, investigate a problem, or understand how it works. To use Spacetile, download it instead.
Spacetile’s Source licence lets you build it with --dev to audit it or investigate a problem. Any other use, including everyday use of a copy you built, needs a Licence.
Build and run
You need macOS 26 or later, Xcode 26 or later, and ripgrep (brew install ripgrep).
- Clone Spacetile/Spacetile.app and open its folder in Terminal.
- Build, test and bundle a development copy:
swift build
swift test
scripts/bundle.sh --dev
open build/Spacetile.app
Spacetile starts and its Welcome window asks for Accessibility. Settings → Licence says “Development build: tiling and profiles work without a Licence.”
scripts/bundle.sh:
- Builds the release binary.
- Wraps it in
build/Spacetile.app, versioned as the latest release plus-local(for example0.2.0-local), with a build number from the UTC date and time. - Embeds Sparkle, the updater. A copy you built only checks for updates when you choose Check for Updates…, since installing an update would replace it with a download that needs Accessibility granted again.
- Compiles the app icon,
Resources/AppIcon.icon(an Icon Composer file), withactool. - Generates the App Intents metadata that the Focus filter needs. Xcode normally does this; the script runs Apple’s processor on the Swift Package Manager’s output.
- Signs Sparkle’s helpers, then the app, with the first “Apple Development” identity in your keychain.
That signature ties the Accessibility permission to your certificate, so it survives rebuilds. Check it with codesign -d -r- build/Spacetile.app: it should show a certificate requirement, not a cdhash. If signing fails with “unable to build chain”, add Apple’s WWDR G3 intermediate certificate to your login keychain.
With no Apple Development identity, the script signs the app ad hoc and warns you. You then have to grant Accessibility again after every build.
Add --install (as in scripts/bundle.sh --dev --install) to quit Spacetile, replace /Applications/Spacetile.app with your build and open it. Install Command… needs the app in Applications. This replaces a downloaded copy too; reinstall the DMG to go back.
A build without --dev has the same 14-day Trial and Licence check as a download. --dev compiles the check out and adds the development commands below.
Developer commands
scripts/spacetile-ctl sends any command from Commands to the running app, as the spacetile command does. It also has one of its own:
| Command | What it does |
|---|---|
frames |
Prints every on-screen window on each display’s current Space, one per line: window id, app, x, y, width and height. It runs in the script itself, so Spacetile needn’t be running |
scripts/fullscreen-probe prints what the window server reports about full-screen Spaces and Split View, the windows on them, and the macOS tiling settings. --menu <App> also lists that app’s Window menu items. It’s for checking how a new macOS release reports these.
These only work in a build made with scripts/bundle.sh --dev:
| Command | What it does |
|---|---|
settings-shot <pane> <path> |
Saves a Settings pane as a PNG. Panes are General, Keys, Rules, Spaces, MenuBar, Licence, Setup (Permissions) and Profiles. <pane>@<height> captures a taller window |
minimap-shot <path> |
Saves the mini-map as a PNG |
min-size <window id> |
Asks a window for 300 × 300 pt, logs the size it actually takes after 0.1 s and 0.5 s, then puts it back in its tile |
minimap |
Opens or closes the mini-map, as clicking the menu-bar item does |
welcome-shot <page> <path> |
Saves a Welcome page (permission, macOS, presets or keys) as a PNG |
menubar-shot <path> |
Saves the menu-bar item’s image |
overlay-preview <path> |
Saves every drop-overlay style side by side |
hover <x> <y> |
Runs focus follows mouse for that point |
show <window id> |
Does what clicking a window in the mini-map does: switches to its Space, on whichever display, and focuses it. Window ids come from frames |
wake |
Runs what Spacetile does after the Mac wakes: wait for macOS to settle, check displays, lay out again |
fullscreen <app> |
Toggles full screen on that app’s focused window |
spaces-override <n> |
Makes Settings act as if there were n Spaces (0 clears it) |
login on |
Registers Spacetile to open at login |
Logs and performance
Spacetile logs to the unified log under the com.kodehort.spacetile subsystem:
log show --last 5m --predicate 'subsystem == "com.kodehort.spacetile"' --style compact
It also marks layout passes, Space changes, Space switches, profile loads and window animations as signpost intervals in the performance category. Record Spacetile with Instruments’ os_signpost (or Logging) template to see how long each takes.
How it works without SIP
yabai needs System Integrity Protection (SIP) partly turned off to switch Spaces and move windows between them. Spacetile uses these window-server calls instead, which work with SIP on. They were checked on macOS 27.0:
| Job | How |
|---|---|
| Moving a window to another Space | SLSBridgedMoveWindowsToManagedSpaceOperation, falling back to SLSMoveWindowsToManagedSpace |
| Instant Space switching | Synthetic dock-swipe events carrying an IOHID payload |
| Focusing a window, with or without raising | _SLPSSetFrontProcessWithOptions and key-window event records, as yabai does |
| Reading Spaces and which windows are on them | SLSCopyManagedDisplaySpaces and SLSCopySpacesForWindows |
| Active border and dimming | Spacetile’s own click-through windows, ordered just above and just below the focused window with the public NSWindow.order(_:relativeTo:). No other app’s window is changed |
| Full screen and Split View | The public AXFullScreen attribute (or pressing the green button), the app’s own Window → Full-Screen Tile menu items through Accessibility, and a synthetic click in the Split View picker. TileLayoutManager in SLSCopyManagedDisplaySpaces says which windows belong to a full-screen Space |
A macOS update could break any of these. Each is isolated, so Spacetile loses that one ability rather than failing outright.
Limits
- Spacetile can’t create, delete or reorder Spaces.
- It can’t change other apps’ window opacity or layer, or make windows appear on every Space; those need SIP off. Dimming the other windows works by placing a shade behind the focused one instead.
- Clicking a tile, or focusing it with the keyboard, can bring it above a floating window from another app.
- The private window-server calls rule out the Mac App Store, so Spacetile is distributed directly.
See also
- Commands: every command
spacetile-ctlcan send. - Troubleshooting: common problems and collecting logs.
- Report a bug.