Ultra Lays Tiles Right Away - a keyboard-driven window manager for macOS, built for ultrawide monitors.
A tiling window manager featuring:
- Adaptive layouts - Fixed-pixel zones for ultrawides, proportional for standard displays
- Workspaces - Multi-app presets (coding, comms, web) with a single keystroke
- Smart organize - Cycle layouts based on window count
- App launchers - One key to launch, focus, or minimize any app
- Environment-aware - Different app sets for work vs personal
demo.mov
macOS window management requires too much mousing. I'd been duct-taping scripts together since switching to an ultrawide. Hammerspoon let me consolidate everything into one system.
Now I get:
- Keyboard-first window positioning
- Ultrawide-specific layouts (860 + 1720 + 860 center-focused)
- Workspace switching without manual arranging
- Single-key app toggle (launch → focus → hide)
Built with Hammerspoon (Lua-based macOS automation). Hyper key (Caps Lock → Shift+Cmd+Ctrl+Opt) via Karabiner-Elements.
┌──────────┬──────────────────────┬──────────┐
│ Left │ Center │ Right │
│ 860px │ 1720px │ 860px │
└──────────┴──────────────────────┴──────────┘
Two-thirds splits:
┌─────────────────────────┬──────────┐
│ Left 2/3 │ Right │
│ 2580px │ 860px │
└─────────────────────────┴──────────┘
2 Windows - cycles 3 configs:
| [1] | [2] | [3] |
|---|---|---|
| ██░░ | ██░░ | ░░██ |
| Focused 2/3 | Equal 50/50 | Focused 2/3 right |
3 Windows - cycles 4 configs:
| [1] | [2] | [3] | [4] |
|---|---|---|---|
| ██░░██ | ██░░░░ | ██░░██ | ░░░░██ |
| Center + sides | Left 2/3 + stack | Equal thirds | Right 2/3 + stack |
4+ Windows - cycles 4 configs (focused + stacks)
| Key | Layout | Position | Ultrawide | Standard |
|---|---|---|---|---|
y |
█░░░ | Left 1/3 | 860px | 1/3 |
h |
██░░ | Left 1/2 | 1720px | 1/2 |
j |
███░ | Left 2/3 | 2580px | 2/3 |
u |
░█░ | Center focus | 1200px | 1/3 |
i |
░██░ | Center | 1720px | 1/3 |
o |
████ | Full | 3440px | Full |
k |
░███ | Right 2/3 | 2580px | 2/3 |
l |
░░██ | Right 1/2 | 1720px | 1/2 |
p |
░░░█ | Right 1/3 | 860px | 1/3 |
| Key | Action |
|---|---|
\ |
Smart organize (cycle layouts) |
1 |
Mission Control (all windows) |
2 |
App Exposé (current app) |
3 |
Minimize all (show desktop) |
f |
Focus mode (center + minimize) |
[/] |
Move to left/right display |
4 |
Cycle input source (keyboard) |
r |
Reload config |
| Key | App |
|---|---|
F1 |
Play/Pause |
F2 |
Ghostty |
F3 |
Cursor |
F4 |
Spotify |
F8 |
Slack |
F9 |
Android Studio |
F10 |
Obsidian |
F11 |
Browser (env-aware) |
F12 |
|
G |
Google Meet |
; |
Msty |
5 |
scrcpy |
| Key | Workspace | Apps | Layout |
|---|---|---|---|
N |
Comms ↔ Web | Slack/WhatsApp + Meet + Browser | 3-way |
M |
Coding ↔ Android | Cursor/Android Studio + Ghostty + Browser | 3-way / 50-50 |
Workspaces auto-minimize non-workspace windows and position apps.
Hostname-based detection:
- Browser: Chrome (work) / Zen (personal)
- Communication: Slack (work) / WhatsApp (personal)
When Obsidian is frontmost:
Ctrl+hjkl→ Arrow keys (vim nav)- `Cmd+`` → Forward delete
- Hammerspoon
- Karabiner-Elements (for Hyper key)
# Clone to ~/.config/ultra
git clone https://github.com/iurysza/ultra ~/.config/ultra
# Run install script (creates bootstrap file)
~/.config/ultra/scripts/install.sh
# Launch Hammerspoon
open -a Hammerspoon
# Grant accessibility permissions when promptedThe install script creates a minimal ~/.hammerspoon/init.lua bootstrap that loads from ~/.config/ultra/.
~/.config/ultra/scripts/uninstall.shRemoves bootstrap and CLI symlink. Optionally deletes config directory.
Copy default config and customize:
cp ~/.config/ultra/config.default.json ~/.config/ultra/config.jsonEdit config.json to customize:
{
"launchers": [
{ "key": "F2", "app": "com.mitchellh.ghostty", "name": "Ghostty" },
{ "key": "F11", "appRef": "browser" }
],
"apps": {
"browser": { "work": "com.google.Chrome", "personal": "app.zen-browser.zen" }
},
"workspaces": {
"comms": { "apps": ["slack", "meet", "browser"], "layouts": ["left", "center", "right"] }
},
"environment": {
"personalHostnamePattern": "your-hostname"
}
}Config changes auto-reload (no restart needed).
Add to Karabiner config:
{
"description": "Caps Lock → Hyper Key",
"manipulators": [
{
"from": { "key_code": "caps_lock" },
"to": [
{
"key_code": "left_shift",
"modifiers": ["left_command", "left_control", "left_option"]
}
],
"type": "basic"
}
]
}tmux-aware macOS notifications with session focusing.
# CLI usage
notify-claude task-complete "Build done"
notify-claude permission "Approve commit?"
notify-claude error "Tests failed"
# Click notification → focuses exact tmux session:window.pane// ~/.claude/settings.json
{
"hooks": {
"Stop": [{ "type": "command", "command": "notify-claude task-complete" }],
"Notification": [{ "type": "command", "command": "notify-claude permission" }]
}
}# Lint
./scripts/lint.sh
# Format
./scripts/format.sh
# Debug logs
tail -f ~/.config/ultra/debug.log
# Reload
Hyper+RLuaLS type definitions included in types/hs.lua. Works with Neovim/LazyVim out of the box - provides autocomplete and type checking for Hammerspoon APIs.
~/.config/ultra/
├── init.lua # Entry point
├── config.default.json # Default configuration
├── config.json # User configuration (gitignored)
├── src/
│ ├── config.lua # Config loader with hot-reload
│ ├── keybindings.lua # Hotkey definitions
│ ├── workspaces.lua # Workspace presets
│ ├── layouts.lua # Window layouts
│ ├── app-launcher.lua # App toggle logic
│ └── ...
└── types/
└── hs.lua # LuaLS type stubs
Shortcuts not working:
- Check Hammerspoon accessibility permissions
- Verify Karabiner Hyper key mapping
- Check logs:
tail -f ~/.config/ultra/debug.log
Ultrawide detection:
- Uses aspect ratio ≥ 2.3 (21:9+), not hardcoded resolution
Config not loading:
- Verify bootstrap:
cat ~/.hammerspoon/init.lua - Check JSON syntax:
jq . ~/.config/ultra/config.json - Check logs for errors
- Reload: Hyper+R
MIT
