Lightweight macOS window layout manager. Define layouts in TOML, switch with global hotkeys.
No tiling tree, no automatic rearrangement — just precise window positioning with percentages.
- TOML config — define layouts with window positions as percentages
- Global hotkeys — switch layouts with
alt+1,alt+2, etc. - Auto variants — one hotkey can apply different window layouts for exact display setups
- Menu bar icon — click to apply layouts, edit config, or quit
- Window matching — match by app name + optional title filter (supports Chrome profiles)
- Spacing — configurable gaps between windows (% or px)
- Hide others — automatically hide apps not in the active layout
- Multi-screen — assign windows to specific screens
- Launch at login — toggle from the menu bar
brew tap 0xtlt/tap
brew install vitrailGrab the latest DMG from Releases — signed and notarized.
git clone https://github.com/0xtlt/vitrail.git
cd vitrail
swift build -c release
cp .build/release/Vitrail /usr/local/bin/vitrailCreate your config at ~/.config/vitrail/config.toml (or click Edit Config... in the menu bar):
spacing = "1%"
hide_others = true
[[layout]]
name = "dev"
hotkey = "alt+1"
[[layout.variant]]
name = "MacBook"
display_setup = "name=Built-in Retina Display;kind=builtin;primary=1;scale=2;frame=0,0,1512,982"
[[layout.variant.window]]
app = "Terminal"
x = 0
y = 0
width = 40
height = 100
[[layout.variant.window]]
app = "Google Chrome"
x = 40
y = 0
width = 60
height = 100
[[layout.variant]]
name = "Desk"
display_setup = "name=Studio Display XDR;kind=external;primary=1;scale=2;frame=-3008,0,3008,1692|name=Built-in Retina Display;kind=builtin;primary=0;scale=2;frame=0,0,1512,982"
[[layout.variant.window]]
app = "Terminal"
screen = 1
x = 0
y = 0
width = 50
height = 100
[[layout.variant.window]]
app = "Google Chrome"
screen = 2
x = 0
y = 0
width = 100
height = 100
[[layout]]
name = "work"
hotkey = "alt+2"
[[layout.variant]]
name = "MacBook"
display_setup = "name=Built-in Retina Display;kind=builtin;primary=1;scale=2;frame=0,0,1512,982"
[[layout.variant.window]]
app = "Slack"
x = 0
y = 0
width = 40
height = 100
[[layout.variant.window]]
app = "Google Chrome"
title = "Work"
x = 40
y = 0
width = 60
height = 100Older flat configs using [[layout.window]] still load. The configurator assigns them to the current display setup in memory and writes the variant schema on save.
| Key | Default | Description |
|---|---|---|
spacing |
"1%" |
Gap between windows. "1%", "10px", or "10" |
hide_others |
true |
Hide apps not in the active layout |
| Key | Description |
|---|---|
name |
Hotkey group name (shown in menu bar) |
hotkey |
Global shortcut. Modifiers: alt, cmd, ctrl, shift. Example: "alt+1", "ctrl+shift+a" |
| Key | Description |
|---|---|
name |
Display setup variant name, for example "MacBook" or "Desk" |
display_setup |
Exact display setup signature generated by the configurator |
| Key | Required | Description |
|---|---|---|
app |
yes | App name (as shown in Activity Monitor) |
title |
no | Window title filter (substring match). Useful for Chrome profiles |
screen |
no | Screen index inside the variant display setup. Default: 1 |
x |
yes | X position as % of screen (0-100) |
y |
yes | Y position as % of screen (0-100) |
width |
yes | Width as % of screen (0-100) |
height |
yes | Height as % of screen (0-100) |
# Run with default config (~/.config/vitrail/config.toml)
vitrail
# Run with custom config
vitrail /path/to/config.tomlVitrail needs Accessibility permission to move and resize windows.
On first launch, macOS will prompt you to grant access in System Settings > Privacy & Security > Accessibility.
- macOS 13+
- Swift 5.9+
MIT