Work in progress. Expect rough edges, breaking changes, and missing polish.
English | Français
Roadie is a small macOS tiling window manager written in Swift, built around one idea: automatic tiling and a Stage Manager-like workflow should be able to live together.
Current release: v0.3.0.
I never set out to write a window manager. For years, yabai has been the foundation of my macOS workstation: sharp, powerful, and deeply influential for anyone who cares about tiling on macOS. Roadie owes a lot to yabai, both functionally and culturally.
The trigger was personal: I never managed to make yabai coexist cleanly with the Stage Manager workflow I wanted. I wanted named, hideable, restorable groups of windows, while still keeping automatic tiling for the visible windows.
So Roadie focuses on that specific combination:
bsp,mutableBsp, andmasterStacktiling for the visible windows.- Roadie stages: named groups of windows that can be hidden, restored, reordered, and represented visually.
- Roadie virtual desktops managed without controlling native macOS Spaces.
- Multi-display support where each display keeps its own current desktop, active stage, and layout.
- Display parking: when a monitor disappears, Roadie parks its non-empty stages on a remaining display instead of merging them into the active stage.
- A modular runtime where tiling, rail, borders, focus-follow-mouse, target mode, title-bar actions, and restore safety can run as separate components.
Roadie is not trying to replace yabai. yabai is broader, older, and much more mature. Roadie is intentionally smaller and opinionated around my workflow.
The second major influence is AeroSpace.
Instead of trying to manipulate native macOS Spaces, Roadie follows the same broad direction: keep SIP on, avoid private write APIs, and manage virtual workspaces on Roadie's side. Switching a Roadie desktop means hiding windows from the outgoing desktop and restoring windows from the incoming one.
The result is a small hybrid:
- A tiling model inspired by yabai's practical macOS window-manager ergonomics.
- A virtual desktop model inspired by AeroSpace's refusal to fight native Spaces.
- A stage layer built for people who want a Stage Manager-like workflow on top of tiling.
If you need a mature general-purpose macOS WM, look at yabai or AeroSpace first. Roadie exists for the narrower case where tiling, virtual desktops, and stage groups need to be one workflow.
This is not a superiority table. It is only meant to make Roadie's scope clear.
Legend: ✅ supported, ◐ partial or narrower, ❌ not supported, 🧪 experimental, ⚙️ requires extra setup or an external companion tool.
| Feature | yabai | AeroSpace | Roadie |
|---|---|---|---|
| BSP tiling | ✅ | ✅ | ✅ |
| User-mutable tiling tree | ✅ | ✅ | ◐ |
| Master-stack layout | ◐ | ✅ | ✅ |
| Float mode | ✅ | ✅ | ✅ |
| Window grid placement | ✅ | ❌ | ❌ |
| Window insert point | ✅ | ❌ | ◐ |
| Stack/tab/accordion layout | ✅ stack | ✅ accordion | ◐ groups only |
| Scratchpad windows | ✅ | ❌ | ◐ |
| Power-user tree commands | ✅ | ✅ | ◐ |
| Feature | yabai | AeroSpace | Roadie |
|---|---|---|---|
| Single full window-manager daemon | ✅ | ✅ | ✅ |
| Modular components | ❌ | ❌ | ✅ |
| Run only overlays/stages beside another WM | ⚙️ | ⚙️ | 🧪 foundation |
| Component-level health checks | ◐ | ◐ | ✅ |
| Per-component logs | ◐ | ◐ | ✅ |
| Feature | yabai | AeroSpace | Roadie |
|---|---|---|---|
| Native macOS Spaces control | ✅ ⚙️ | ❌ | ❌ |
| Create/destroy/move native macOS Spaces | ✅ ⚙️ | ❌ | ❌ |
| Virtual desktops without native Spaces | ❌ | ✅ | ✅ |
| Named stages inside a desktop | ❌ | ❌ | ✅ |
| Stage rail with thumbnails | ❌ | ❌ | ✅ |
| Visible stage navigation and reorder | ❌ | ❌ | ✅ |
| Display parking and reconnect recovery | ❌ | ◐ | ✅ |
| Multi-display tiling | ✅ | ✅ | ✅ |
| Move workspace/stage to another display | ✅ | ✅ | ✅ |
| Mission Control awareness | ✅ | ◐ | ◐ |
| Feature | yabai | AeroSpace | Roadie |
|---|---|---|---|
| Window rules and automatic placement | ✅ | ✅ callbacks | ✅ |
| Rich rule effects | ✅ | ◐ | ◐ |
| Integrated hotkeys | ❌ ⚙️ skhd |
✅ | ❌ ⚙️ BTT/Karabiner |
| Binding modes | ❌ | ✅ | ❌ |
| Config callbacks | ✅ signals | ✅ | ◐ |
| Declarative config reload | ◐ shell | ✅ | ✅ |
| Stable JSON query API | ✅ | ✅ | ✅ |
| Automation event stream | ✅ signals | ✅ subscribe | ✅ JSONL |
| CLI-first operation | ✅ | ✅ | ✅ |
| Feature | yabai | AeroSpace | Roadie |
|---|---|---|---|
| Focus follows mouse | ✅ | ✅ | ✅ |
| Focus border overlay | ❌ | ❌ | ✅ |
| Window opacity/layers/PiP/shadow toggles | ✅ ⚙️ | ❌ | ❌ |
| Native fullscreen helpers | ✅ | ✅ | ◐ |
| Title-bar context actions | ❌ | ❌ | ✅ |
| Feature | yabai | AeroSpace | Roadie |
|---|---|---|---|
| Keyboard target labels | ❌ | ❌ | ✅ |
| App element target discovery | ❌ | ❌ | 🧪 |
| Recursive display grid | ❌ | ❌ | 🧪 |
| Recursive focused-window grid | ❌ | ❌ | 🧪 |
| Short macro record/replay | ❌ | ❌ | 🧪 |
◐ means Roadie has a narrower or less mature version of the feature, not full
parity with yabai or AeroSpace.
Roadie does not require disabling SIP. It uses Accessibility for window discovery and movement, and Screen Recording only for rail thumbnails.
- Tiles visible windows with
bsp,mutableBsp,masterStack, orfloatmodes. - Runs either as a monolithic daemon or as modular components with separate
tiler,rail,border,focus-mouse,titlebar-menu,target-mode, andrestore-safetyprocesses. - Keeps stage groups per display and Roadie desktop.
- Provides Roadie virtual desktops without controlling native macOS Spaces.
- Supports multiple displays independently.
- Parks stages from a disconnected display on a remaining display, then restores them when the same display comes back and can be recognized safely.
- Shows a native side rail with stage thumbnails.
- Lets you drag thumbnails between stages or into the active workspace.
- Lets you optionally right-click a managed window title bar to send that window to another stage, desktop, or display.
- Shows a focus border around the active window.
- Provides keyboard-friendly CLI commands for BetterTouchTool, Karabiner, shell scripts, or any launcher.
- Persists stage membership and layout state across daemon restarts.
- Exposes state, health, metrics, events, and audit commands for debugging.
- Publishes JSONL automation events and stable
roadie query ...JSON projections. - Supports TOML window rules with validation, explain, runtime
rule.*events, and automatic stage/display placement. - Supports power-user layout commands such as
focus back-and-forth,layout insert,layout flatten, andlayout zoom-parent. - Supports a persistent BSP tree and mutable BSP operations closer to yabai: move, warp, resize, insert, swap, and observed split-ratio preservation.
- Persists and exposes window groups for stack/tab-like workflows.
- Exposes Keyboard Targets for labels, search picking, JSON automation, target inspection, and experimental app-zone discovery.
- Provides two configurable spatial grids: Display Grid for full-screen recursive targeting, and Window Grid for recursive targeting inside the focused window.
- Records short mouse macros with target context and grid fallback.
- Provides safety tools: atomic config reload, manual restore snapshots, limited restore-on-exit/crash watcher, generated-file cleanup.
- Exposes read-only performance diagnostics without instrumenting focus/border hot paths.
Full documentation is available in English and French:
Main guides:
- macOS.
- Xcode Command Line Tools.
- Accessibility permission for
roadied. - Screen Recording permission if you want real window thumbnails in the rail.
Install Xcode Command Line Tools if needed:
xcode-select --installFrom the repository root:
make test
make startThe project scripts force the Xcode toolchain and avoid shell environments that may inject incompatible linker flags.
For a local manual install from source, use:
make installThis hides the usual macOS service setup:
- builds
roadieandroadiedin release mode; - installs them in
./binfor this repository; - also installs them in
~/.local/binfor shell usage; - ad-hoc signs both binaries;
- creates
~/Library/LaunchAgents/com.roadie.roadied.plist; - starts the LaunchAgent when it is not already loaded.
If Roadie is already running, make install does not stop it. Run this when you want the running daemon to reload the newly installed binaries:
make restartIf you only want to install files without starting the LaunchAgent:
ROADIE_INSTALL_NO_START=1 make installUseful commands:
make test
make build
make install
make start
make stop
make restart
make status
make logs
make doctor
make dmgEquivalent direct commands:
./scripts/test
./scripts/start
./scripts/stop
./scripts/status
./scripts/logs
./scripts/roadie daemon healthThe classic make start path runs Roadie as one daemon. The modular path starts
the same capabilities as separate processes:
./scripts/start-modular
./bin/roadie query components
./bin/roadie daemon healthThe standard modular script currently starts the full component set, including the tiler. Its architectural purpose is broader: it lets Roadie evolve from a single all-or-nothing window manager into a set of components that can also be used as a companion layer beside yabai, AeroSpace, or another window manager.
Today, that companion-mode usage is intentionally described as experimental: the runtime boundaries, component locks, health checks, and per-component logs are in place, but explicit "stage-only" or "overlay-only" startup profiles are still future work.
Roadie can be packaged as a classic macOS DMG:
make dmgThe output is:
dist/Roadie.dmg
The DMG contains Roadie.app and an /Applications shortcut, so installation is the usual drag-and-drop flow.
Important: this build is ad-hoc signed, not Developer ID signed, and not notarized. That means macOS Gatekeeper will not treat it like a fully trusted public app.
For users, the expected first-run flow is:
- Drag
Roadie.appto/Applications. - Open it once with right click > Open, then confirm. Roadie starts as a background menu-less app.
- If macOS still blocks it, run:
xattr -dr com.apple.quarantine /Applications/Roadie.app- Grant permissions to
/Applications/Roadie.appin System Settings > Privacy & Security:
- Accessibility.
- Screen Recording, if live nav rail thumbnails are wanted.
This limitation is normal until the app is signed with an Apple Developer ID certificate and notarized by Apple.
The packaged app currently starts Roadie for the current user session. A future signed installer can add a login item or LaunchAgent automatically; for now, startup-at-login is intentionally left explicit.
Roadie needs Accessibility permission to read and move windows.
After building and starting the daemon, add this binary in System Settings > Privacy & Security > Accessibility:
/Users/moi/Nextcloud/10.Scripts/39.roadie/bin/roadied
Then restart the daemon:
make restartScreen Recording is optional but recommended. Without it, the nav rail may show fallback app icons instead of live thumbnails.
The user configuration file is:
~/.config/roadies/roadies.toml
Validate it with:
./bin/roadie config validateInspect the loaded configuration:
./bin/roadie config showValidate and inspect rules:
./bin/roadie rules validate --config ~/.config/roadies/roadies.toml
./bin/roadie rules list --json
./bin/roadie rules explain --app Terminal --title roadie --role AXWindow --stage devStart or restart the daemon:
make restartCheck the runtime state:
./bin/roadie daemon health
./bin/roadie state audit
./bin/roadie metrics
./bin/roadie tree dumpList windows and displays:
./bin/roadie windows list
./bin/roadie display listSwitch layout mode for the current stage:
./bin/roadie mode bsp
./bin/roadie mode mutableBsp
./bin/roadie mode masterStack
./bin/roadie mode floatMove focus or windows:
./bin/roadie focus left
./bin/roadie focus right
./bin/roadie focus back-and-forth
./bin/roadie move left
./bin/roadie warp right
./bin/roadie resize left
./bin/roadie display focus rightMove the focused window to another display:
./bin/roadie window display 2Stages are groups of windows. Only the active stage is visible; inactive stages are hidden and represented in the nav rail.
Common commands:
./bin/roadie stage list
./bin/roadie stage create 4
./bin/roadie stage rename 4 Comms
./bin/roadie stage switch 2
./bin/roadie stage assign 2
./bin/roadie stage switch-position 2
./bin/roadie stage assign-position 2
./bin/roadie stage switch-visible next
./bin/roadie stage switch-visible prev
./bin/roadie stage assign-empty
./bin/roadie stage reorder 2 1
./bin/roadie stage delete 4
./bin/roadie stage prev
./bin/roadie stage nextstage switch and stage assign target stable stage IDs. stage switch-position
and stage assign-position target the visible order in the nav rail, so position
1 is the first visible stage even if its internal ID is different.
stage switch-visible prev|next cycles through non-empty stages only, matching
the nav rail order. stage assign-empty sends the active window to the next
unnamed empty stage, creating one if needed.
Bring an inactive-stage window back into the active stage:
./bin/roadie stage summon WINDOW_IDMove the active stage to another display:
./bin/roadie stage move-to-display 2
./bin/roadie stage move-to-display right
./bin/roadie stage move-to-display right --no-followWithout a flag, Roadie uses [focus].stage_move_follows_focus. --follow and
--no-follow override that preference for one command. The nav rail also exposes
the same operation from a stage card context menu.
Roadie desktops are virtual desktops managed by Roadie. They do not create, switch, or control native macOS Spaces.
./bin/roadie desktop list
./bin/roadie desktop current
./bin/roadie desktop focus 2
./bin/roadie desktop focus next
./bin/roadie desktop focus prev
./bin/roadie desktop focus back
./bin/roadie desktop back-and-forth
./bin/roadie desktop summon 3
./bin/roadie desktop label 2 DeepWork./bin/roadie layout split horizontal
./bin/roadie layout split vertical
./bin/roadie layout insert right
./bin/roadie layout join-with left
./bin/roadie layout flatten
./bin/roadie layout zoom-parentThese commands persist layout intent where applicable, so the maintainer does not immediately undo deliberate manual structure.
./bin/roadie group create terminals 12345 67890
./bin/roadie group add terminals 11111
./bin/roadie group focus terminals 67890
./bin/roadie group remove terminals 12345
./bin/roadie group dissolve terminals
./bin/roadie group listGroups are persisted in Roadie's stage state and exposed through roadie query groups.
Subscribe to live events:
./bin/roadie events subscribe --from-now --initial-stateRead stable JSON projections:
./bin/roadie query state
./bin/roadie query windows
./bin/roadie query displays
./bin/roadie query desktops
./bin/roadie query stages
./bin/roadie query groups
./bin/roadie query rules
./bin/roadie query health
./bin/roadie query eventsMove the focused window to another Roadie desktop:
./bin/roadie window desktop 2
./bin/roadie window desktop 2 --followThe nav rail is a native per-display side panel.
It shows non-empty stages, live thumbnails when available, fallback app icons when capture is unavailable, and a halo around the active stage.
Supported interactions:
- Click a stage thumbnail stack to switch stage.
- Click empty rail space to hide the active stage and switch to an empty stage. This can be disabled with
empty_click_hide_active = false. - Drag a thumbnail to another stage to move that window there.
- Drag a thumbnail into the active workspace to summon it.
- Drag a thumbnail to an empty rail area to place it in an empty or newly created stage.
- Drag an application window by its title bar onto a stage to move it there.
- Drag an application window by its title bar onto empty rail space to place it in an empty or newly created stage.
- Use the chevrons above and below a stage to reorder stages.
- Clicks in macOS-reserved areas such as the menu bar are ignored by rail empty-click handling.
Rail rendering is configured in ~/.config/roadies/roadies.toml.
Roadie exposes Keyboard Targets: short labels for windows, stages, displays, desktops, bookmarks, and optional Accessibility application zones. Targets can be searched, inspected, activated, and exported as JSON for scripts or agents.
Roadie also provides two spatial grid modes:
- Display Grid targets a whole display.
- Window Grid targets the currently focused window.
Both grids use the same spatial language: type a cell label to zoom into that
cell, use Space to validate, arrows to move, and Esc to cancel. Short macro
recording can store the best target context plus a grid fallback.
./bin/roadie targets interactive --source all --include-app-targets
./bin/roadie grid interactive --scope display
./bin/roadie grid interactive --scope focused_window
./bin/roadie record start demo --duration 10Risky surfaces are experimental and independently configurable through
[experimental.target_mode], [experimental.app_targets],
[experimental.target_grid], and [experimental.target_record].
Run the quick health checks:
./bin/roadie daemon health
./bin/roadie state audit
./bin/roadie self-testRepair conservative state issues:
./bin/roadie state heal
./bin/roadie daemon healInspect logs and events:
make logs
./bin/roadie events tail 50If windows stop moving after a rebuild, re-check Accessibility for bin/roadied, then restart:
make restartSources/RoadieAX Accessibility and system window snapshots
Sources/RoadieCore Shared types, geometry, config
Sources/RoadieTiler Pure layout strategies
Sources/RoadieStages Persistent Roadie desktop and stage state
Sources/RoadieDaemon Daemon services, rail, border, commands
Sources/roadie CLI
Sources/roadied Daemon entry point
Tests Unit tests
scripts Build and runtime helpers
Roadie is built for personal daily use first. Expect changes in command shape, configuration keys, and rail behavior while the project stabilizes.
