macOS tiling window manager written in Rust.
- Tag-based workspaces - Bitmask tags (like awesome/river) allow windows to belong to multiple tags and view any combination
- External layout engines - Stdin/stdout JSON protocol lets you write custom layouts in any language
- Multi-monitor support - Each display has independent tags
- Window rules - Automatically configure windows by app name, bundle identifier, or title
- Cursor warp - Mouse follows focus (configurable: disabled, on-output-change, on-focus-change)
- State streaming - Real-time events for status bars and external tools
- No SIP disable required - Uses only public Accessibility API
- Shell script configuration - Config is just a shell script (
~/.config/yashiki/init)
Early development stage. API and configuration format may change.
- macOS 12.0+
- Accessibility permission (System Settings → Privacy & Security → Accessibility)
brew tap typester/yashiki
brew install --cask yashikiThe cask installs:
Yashiki.appto/Applications- CLI tools:
yashiki,yashiki-layout-tatami,yashiki-layout-byobu
Note: Yashiki.app is not signed. On first launch, allow it in System Settings → Privacy & Security. Or install with --no-quarantine:
brew install --cask --no-quarantine yashiki# Core daemon and CLI
cargo install yashiki
# Install layout engines you want to use
cargo install yashiki-layout-tatami # Master-stack layout
cargo install yashiki-layout-byobu # Accordion layout- Open System Settings → Privacy & Security → Accessibility
- Add
Yashiki.app(if installed via Homebrew or as app bundle) - Or add your terminal app if running
yashiki startdirectly (Not recommended)
For a detailed walkthrough, see the Quick Start Guide.
-
Launch Yashiki.app:
- If installed via Homebrew: Open
/Applications/Yashiki.app - The app will request Accessibility permission on first launch
Note: Running
yashiki startfrom terminal is not recommended as it requires granting Accessibility permission to your terminal app. - If installed via Homebrew: Open
-
Create config file
~/.config/yashiki/init:#!/bin/sh # Add Homebrew to exec path (if needed for custom layout engines) # yashiki add-exec-path /opt/homebrew/bin # Layout configuration yashiki layout-set-default tatami yashiki set-outer-gap 10 # Gap between windows and screen edges (global) yashiki layout-cmd --layout tatami set-inner-gap 10 # Gap between windows (layout-specific) # Cursor warp (mouse follows focus) yashiki set-cursor-warp on-focus-change # Tag bindings (tag N = bitmask $((1<<(N-1)))) for i in 1 2 3 4 5 6 7 8 9; do yashiki bind "alt-$i" tag-view "$((1<<(i-1)))" yashiki bind "alt-shift-$i" window-move-to-tag "$((1<<(i-1)))" done # Window focus yashiki bind alt-j window-focus next yashiki bind alt-k window-focus prev yashiki bind alt-h layout-cmd dec-main-ratio yashiki bind alt-l layout-cmd inc-main-ratio # Multi-monitor yashiki bind alt-o output-focus next yashiki bind alt-shift-o output-send next
-
Make it executable:
chmod +x ~/.config/yashiki/init -
Restart yashiki to apply config:
- Quit with
yashiki quit - Relaunch Yashiki.app
- Quit with
Yashiki uses a shell script for configuration. The init script is executed when the daemon starts.
Format: <modifiers>-<key>
Modifiers:
alt(Option key)ctrl(Control key)shiftcmd(Command key)
Examples: alt-1, alt-shift-j, ctrl-alt-return
Tags use bitmask format:
- Tag 1 =
1(binary: 001) - Tag 2 =
2(binary: 010) - Tag 3 =
4(binary: 100) - Tags 1+2 =
3(binary: 011)
In shell scripts: $((1<<0)) = 1, $((1<<1)) = 2, $((1<<2)) = 4
yashiki start # Start daemon
yashiki quit # Stop daemon
yashiki version # Show versionyashiki bind alt-1 tag-view 1 # Bind hotkey
yashiki unbind alt-1 # Unbind hotkey
yashiki list-bindings # List all bindingsyashiki tag-view 1 # Switch to tag 1
yashiki tag-view 3 # View tags 1+2 (bitmask 3)
yashiki tag-toggle 2 # Toggle tag 2 visibility
yashiki tag-view-last # Switch to previous tags
yashiki window-move-to-tag 1 # Move focused window to tag 1
yashiki window-toggle-tag 2 # Toggle tag 2 on focused windowyashiki window-focus next # Focus next window
yashiki window-focus prev # Focus previous window
yashiki window-focus left # Focus window to the left
yashiki window-focus right # Focus window to the right
yashiki window-focus up # Focus window above
yashiki window-focus down # Focus window below
yashiki window-swap next # Swap with next window
yashiki window-swap prev # Swap with previous window
yashiki window-swap left # Swap with window to the left
yashiki window-swap right # Swap with window to the right
yashiki window-swap up # Swap with window above
yashiki window-swap down # Swap with window below
yashiki window-toggle-fullscreen # Toggle fullscreen (AeroSpace-style)
yashiki window-toggle-float # Toggle floating state
yashiki window-close # Close focused windowyashiki output-focus next # Focus next display
yashiki output-focus prev # Focus previous display
yashiki output-send next # Move window to next display
yashiki output-send prev # Move window to previous display
yashiki tag-view --output 2 1 # Switch tag on display 2
yashiki tag-view --output "DELL" 1 # Target display by nameyashiki retile # Apply layout
yashiki layout-set-default tatami # Set default layout
yashiki layout-set byobu # Set layout for current tag
yashiki layout-set --tags 4 byobu # Set layout for tag 3
yashiki layout-get # Get current layout
yashiki layout-cmd set-main-ratio 0.6 # Send command to layout
yashiki layout-cmd --layout tatami set-inner-gap 10 # Configure specific layoutyashiki list-windows # List managed windows
yashiki list-windows --all # Include ignored windows (popups, tooltips)
yashiki list-windows --debug # Show debug info (ax_id, subrole, window_level, buttons)
yashiki list-outputs # List all displays
yashiki get-state # Get current state
yashiki exec "open -a Safari" # Execute command
yashiki exec --track "borders" # Execute and terminate on yashiki quit
yashiki exec-or-focus --app-name Safari "open -a Safari" # Focus or launchThe --track option is useful for launching companion tools like JankyBorders that should run alongside yashiki:
# In ~/.config/yashiki/init
yashiki exec --track "borders active_color=0xffe1e3e4"Control whether mouse cursor follows window focus.
yashiki set-cursor-warp disabled # Don't move cursor (default)
yashiki set-cursor-warp on-output-change # Move cursor when switching displays
yashiki set-cursor-warp on-focus-change # Always move cursor to focused window
yashiki get-cursor-warp # Get current modeControl the gap between windows and screen edges. Applied globally to all layouts and fullscreen windows.
yashiki set-outer-gap 10 # Set all sides to 10px
yashiki set-outer-gap 10 20 # Set vertical=10px, horizontal=20px
yashiki set-outer-gap 10 20 15 25 # Set top=10, right=20, bottom=15, left=25 (CSS-style)
yashiki get-outer-gap # Get current outer gapSubscribe to real-time state change events (useful for status bars like engawa):
yashiki subscribe # Subscribe to all events
yashiki subscribe --snapshot # Get initial snapshot on connect
yashiki subscribe --filter focus,tags # Filter specific eventsEvent types: window, focus, display, tags, layout
Events are streamed as JSON lines to stdout.
The exec path is used for exec commands and custom layout engine discovery.
yashiki exec-path # Get current exec path
yashiki set-exec-path "/path1:/path2" # Set exec path
yashiki add-exec-path /opt/homebrew/bin # Add to start (high priority)
yashiki add-exec-path --append /usr/local/bin # Add to end (low priority)Default exec path: <yashiki_executable_dir>:<system_PATH>
Automatically configure window properties based on app name, bundle identifier, title, AXIdentifier, AXSubrole, window level, or button states.
By default, new windows inherit the display's current visible tags. Use the tags action to override this.
# Match by app name
yashiki rule-add --app-name Finder float
yashiki rule-add --app-name Safari tags 2
# Match by bundle identifier (app-id)
yashiki rule-add --app-id com.apple.finder float
yashiki rule-add --app-id "com.google.*" output 2 # Glob pattern
# Match by window title
yashiki rule-add --title "*Preferences*" float
# Match by AXIdentifier (useful for special windows like Ghostty Quick Terminal)
yashiki rule-add --ax-id "com.mitchellh.ghostty.quickTerminal" float
# Match by AXSubrole (AX prefix optional: "Dialog" matches "AXDialog")
yashiki rule-add --subrole Dialog float
yashiki rule-add --subrole FloatingWindow float
# Match by window level (normal, floating, modal, utility, popup, other, or numeric)
yashiki rule-add --window-level other ignore # Ignore non-normal windows (palettes, etc.)
yashiki rule-add --window-level floating float # Float utility panels
# Match by button states (exists, none, enabled, disabled)
yashiki rule-add --fullscreen-button none float # Float windows without fullscreen button
yashiki rule-add --close-button none ignore # Ignore windows without close button (popups)
yashiki rule-add --app-id com.mitchellh.ghostty --fullscreen-button disabled ignore # Ghostty Quick Terminal
# Ignore windows completely (never manage - useful for popups/dropdowns)
yashiki rule-add --subrole AXUnknown ignore # Ignore all popup windows
yashiki rule-add --app-id org.mozilla.firefox --subrole AXUnknown ignore # Firefox popups only
yashiki rule-add --app-id com.microsoft.Outlook --ax-id none --subrole none ignore # Outlook invisible windows
# Combined matching (more specific)
yashiki rule-add --app-name Safari --title "*Preferences*" float
yashiki rule-add --app-id com.mitchellh.ghostty --subrole FloatingWindow float
# Other actions
yashiki rule-add --app-name Preview dimensions 800 600
yashiki rule-add --app-name Preview position 100 100
# Remove rule
yashiki rule-del --app-name Finder float
# List all rules
yashiki list-rulesAvailable actions:
| Action | Example | Description |
|---|---|---|
ignore |
ignore |
Never manage (skip completely) |
float |
float |
Window floats (excluded from tiling) |
no-float |
no-float |
Override float rule |
tags |
tags 2 |
Set window tags |
output |
output 2 |
Move to display |
position |
position 100 200 |
Set position |
dimensions |
dimensions 800 600 |
Set size |
Rules are sorted by specificity - more specific rules take priority.
For detailed window rules configuration including how to find AX attributes (--ax-id, --subrole), see docs/window-rules.md.
For app-specific workarounds (Firefox flickering, etc.), see docs/workarounds.md.
Classic tiling layout with main area and stack.
Commands:
| Command | Description |
|---|---|
set-main-ratio <0.1-0.9> |
Set main area ratio |
inc-main-ratio |
Increase main ratio |
dec-main-ratio |
Decrease main ratio |
inc-main-count |
Add window to main area |
dec-main-count |
Remove window from main area |
zoom [window_id] |
Move window to main area |
set-inner-gap <px> |
Gap between windows |
AeroSpace-style stacked windows with focused window at front.
Commands:
| Command | Description |
|---|---|
set-padding <px> |
Stagger offset between windows |
set-orientation <h|v> |
Horizontal or vertical stacking |
toggle-orientation |
Toggle orientation |
Yashiki supports external layout engines via stdin/stdout JSON protocol.
See docs/layout-engine.md for the specification.
# Run daemon with debug logging
RUST_LOG=info cargo run -p yashiki -- start
# Run CLI commands
cargo run -p yashiki -- list-windows
# Run tests
cargo test --all
# Format code
cargo fmt --allyashiki/ # WM core daemon + CLI
yashiki-ipc/ # Shared protocol definitions
yashiki-layout-tatami/ # Master-stack layout engine
yashiki-layout-byobu/ # Accordion layout engine
Inspired by:
- river - External layout protocol, multi-monitor model
- AeroSpace - Virtual workspaces approach, accordion layout
- dwm / awesomewm - Tag-based workspaces
MIT License - see LICENSE for details.