2 unstable releases
| 0.1.0 | Mar 8, 2026 |
|---|---|
| 0.0.1 | Mar 8, 2026 |
#457 in Game dev
2KB
Ayatsuri
A programmable macOS window manager and status bar built on the Bevy ECS game engine. Ayatsuri arranges windows on a per-monitor infinite horizontal strip with support for vertical stacking, animated transitions, touchpad gestures, and full Rhai scripting. It includes a built-in status bar with customizable widgets, event-driven updates, and popup menus — replacing tools like SketchyBar with a unified Rust-native solution. An MCP server provides live state inspection and command dispatch from Claude Code.
Forked from Paneru, Ayatsuri retains the infinite-strip layout model while adding a scripting layer, a plugin architecture, an MCP integration, and a GPU-ready status bar.
Architecture
Ayatsuri is structured as three decoupled layers connected by the Bevy ECS scheduler:
┌──────────────────────────────────────────────────────────────┐
│ Bevy ECS Scheduler │
│ │
│ PreUpdate Event Ingestion (pump_events, triggers) │
│ Update State Transitions (window lifecycle, swipe) │
│ PostUpdate Layout → Animation → Rendering │
└──────────┬─────────────────┬─────────────────┬───────────────┘
│ │ │
┌──────▼──────┐ ┌─────▼──────┐ ┌──────▼──────┐
│ Platform │ │ Manager │ │ Plugins │
│ (macOS) │ │ (Traits) │ │ (Bevy) │
│ │ │ │ │ │
│ objc2 │ │ Window │ │ Scripting │
│ CoreGraphx │ │ Display │ │ Hotkey │
│ A11y API │ │ Layout │ │ Clipboard │
│ Gestures │ │ Process │ │ Overlay │
└─────────────┘ └────────────┘ │ MenuBar │
│ Snapshot │
│ Notify │
└─────────────┘
Every frame follows a strict five-stage pipeline. No system in a later stage may send events consumed by an earlier stage within the same frame. Layout computation is a pure function: given a window list, display bounds, config, and viewport offset, it emits reposition/resize markers without side effects.
An InteractionMode FSM (Idle, Dragging, Swiping, MissionControl) gates system execution via Bevy States run conditions.
Features
Window Management
- Sliding tiling layout -- windows arranged on an infinite horizontal strip per monitor
- Vertical stacking -- stack multiple windows in a single column with equal-height distribution
- Focus follows mouse -- optional mouse-driven focus tracking
- Touchpad gestures -- four-finger swipe to scroll the window strip
- Spring-physics animation -- critically-damped harmonic oscillator with mid-flight retargeting and instant-snap fallback
- Window border overlays -- colored borders and dim-inactive overlays via native Cocoa windows
- Edge-snap drag preview -- visual snap zones during window dragging
- Multi-display -- independent window strips per monitor with directional focus/swap across displays
- Fullscreen integration -- navigate into and out of native macOS fullscreen windows
Status Bar
- Built-in status bar -- customizable macOS menu bar replacement rendered with CoreGraphics
- Item types -- standard items, workspace indicators, graphs, sliders, brackets, native aliases
- Five positions -- left, center, right, left-of-notch, right-of-notch
- Event-driven -- items subscribe to system events (volume, brightness, power, wifi, media, space change)
- Script plugins -- shell scripts with environment variables ($NAME, $SENDER, $INFO, $BUTTON)
- Popup menus -- child items anchored below parent, click-to-toggle
- Background blur -- NSVisualEffectView vibrancy (same look as native macOS bar)
- Mouse interaction -- per-item click, scroll, hover detection
- Animated transitions -- spring physics for item position/color/opacity changes
Platform
- Rhai scripting -- programmable hotkeys, event callbacks, and automation via
~/.config/ayatsuri/scripts/*.rhai - Bevy plugin architecture -- modular plugins for clipboard, notifications, status bar, overlays, and snapshots
- MCP server -- live ECS state inspection and command dispatch for Claude Code (
ayatsuri mcp) - Hot-reload -- configuration and scripts reload automatically without restart
- Shell command execution -- bind arbitrary shell commands to hotkeys via
execbindings - Wallpaper control -- set desktop wallpaper from config or scripts
Installation
Prerequisites
- macOS (Apple Silicon or Intel)
- Accessibility permissions (System Settings > Privacy & Security > Accessibility)
- "Displays have separate spaces" enabled in System Settings > Desktop & Dock
Multiple displays: Arrange additional displays above or below (not left/right) to prevent macOS from relocating off-screen windows.
Installing with Nix
# flake.nix
inputs.ayatsuri = {
url = "github:pleme-io/ayatsuri";
inputs.nixpkgs.follows = "nixpkgs";
};
Home Manager Module
{ inputs, ... }:
{
imports = [ inputs.ayatsuri.homeManagerModules.default ];
services.ayatsuri = {
enable = true;
settings = {
options = {
preset_column_widths = [ 0.25 0.33 0.5 0.66 0.75 ];
swipe_gesture_fingers = 4;
swipe_gesture_direction = "Natural";
animation_speed = 4000;
};
bindings = {
window_focus_west = "cmd - h";
window_focus_east = "cmd - l";
window_focus_north = "cmd - k";
window_focus_south = "cmd - j";
window_swap_west = "alt - h";
window_swap_east = "alt - l";
window_center = "alt - c";
window_resize = "alt - r";
window_fullwidth = "alt - f";
window_manage = "ctrl + alt - t";
window_stack = "alt - ]";
window_unstack = "alt + shift - ]";
quit = "ctrl + alt - q";
};
};
};
}
Installing from Source
git clone https://github.com/pleme-io/ayatsuri.git
cd ayatsuri
cargo build --release
cargo install --path .
Usage
Running as a Service
ayatsuri install # Install launchd service
ayatsuri start # Start the service
ayatsuri stop # Stop the service
ayatsuri restart # Restart the service
ayatsuri uninstall # Remove the service
Running in the Foreground
ayatsuri # Launch directly (default subcommand)
Sending Commands
Control the running daemon via Unix socket (/tmp/ayatsuri.socket):
ayatsuri send-cmd <command> [args...]
| Command | Description |
|---|---|
window focus <direction> |
Move focus in the given direction |
window swap <direction> |
Swap the focused window with a neighbour |
window center |
Center the focused window on screen |
window resize |
Cycle through preset_column_widths |
window fullwidth |
Toggle full-width mode |
window manage |
Toggle managed/floating state |
window equalize |
Distribute equal heights in a stack |
window stack |
Stack onto the left neighbour |
window unstack |
Unstack into its own column |
window nextdisplay |
Move window to the next display |
mouse nextdisplay |
Warp mouse to the next display |
printstate |
Print ECS state to the debug log |
quit |
Quit Ayatsuri |
Directions: west, east, north, south, first, last.
MCP Server (Claude Code Integration)
ayatsuri mcp # Start MCP server on stdio
Exposes five tools: get_state, get_focused, get_displays, get_config, and send_command.
Configuration
Ayatsuri searches for configuration in order:
$AYATSURI_CONFIGenvironment variable$XDG_CONFIG_HOME/ayatsuri/ayatsuri.yaml(preferred YAML format)$XDG_CONFIG_HOME/ayatsuri/ayatsuri.toml$HOME/.ayatsuri.toml$HOME/.ayatsuri
Configuration changes are automatically hot-reloaded.
Window Management Options
| Option | Type | Description |
|---|---|---|
preset_column_widths |
[f64] |
Width ratios to cycle through on resize |
animation_speed |
u32 |
Animation duration in milliseconds |
swipe_gesture_fingers |
u8 |
Number of fingers for swipe gestures |
swipe_gesture_direction |
string |
"Natural" or "Inverted" |
focus_follows_mouse |
bool |
Enable mouse-driven focus |
mouse_follows_focus |
bool |
Warp mouse to focused window |
auto_center |
bool |
Auto-center focused window |
edge_padding |
[i32; 4] |
Top, right, bottom, left padding |
wallpaper |
string |
Path to desktop wallpaper image |
Status Bar Options
status_bar:
enabled: true
position: top
height: 28
blur_radius: 20
color: "0xCC1e1e2e"
font: "Hack Nerd Font:Regular:14.0"
hide_macos_menubar: true
items:
- id: spaces
type: space
position: left
spaces: [1, 2, 3, 4, 5]
- id: front_app
type: item
position: left
subscribe: [front_app_switched]
- id: clock
type: item
position: right
update_freq: 1
script: "date '+%H:%M:%S'"
- id: battery
type: item
position: right
subscribe: [power_source_change]
- id: cpu
type: graph
position: right
width: 60
update_freq: 2
- id: volume
type: slider
position: right
subscribe: [volume_change]
See CLAUDE.md for full architecture documentation.
Development
cargo build # Compile (zero warnings required)
cargo test # Run 175 unit tests (platform-independent, deterministic)
cargo run # Launch (requires macOS Accessibility permissions)
Tests are platform-independent: pump_events is a no-op in test mode and events are injected via world.write_message::<Event>(). A static TEST_MUTEX serializes integration tests to prevent SIGABRT from parallel Bevy App initialization. Production code has zero unwrap()/expect() calls — all error paths use Result propagation, let-else guards, or safe casts.
Project Structure
| Path | Purpose |
|---|---|
src/main.rs |
CLI entry point (clap), dispatches subcommands |
src/logic/ |
Pure testable logic (snap, navigation, swipe, drag, spring, layout) — 106 tests |
src/ecs/state.rs |
Bevy States enums, context resources, guards |
src/ecs/systems/ |
Frame-driven systems (mod.rs + animation.rs + overlay.rs) |
src/ecs/triggers.rs |
Observer-driven triggers (focus, workspace, config, drag) |
src/ecs/params.rs |
Custom SystemParams (Windows, ActiveDisplay, Configuration) |
src/ecs.rs |
Entity helpers, component/marker definitions, app setup |
src/commands.rs |
Command enum and all command handler systems |
src/config.rs |
Config parsing (YAML preferred, TOML supported), keybinding resolution |
src/mcp.rs |
MCP server (stdio transport) for Claude Code |
src/plugins/window.rs |
WindowPlugin -- system registration and ordering |
src/plugins/scripting/ |
Rhai scripting engine, API registration, script loader |
src/plugins/clipboard.rs |
Clipboard monitoring and history |
src/plugins/notification.rs |
macOS notification dispatch |
src/plugins/status_bar/ |
Status bar rendering, layout, items, events |
src/plugins/hotkey.rs |
Global hotkey registration |
src/plugins/snapshot.rs |
State snapshot for MCP queries |
src/overlay.rs |
Window border and dim-inactive overlay rendering |
src/manager/ |
Window, Display, LayoutStrip, Process abstractions |
src/platform/ |
macOS platform layer (Accessibility API, gestures, service) |
module/ |
Nix home-manager module |
Related Projects
- blackmatter-ayatsuri -- Nix home-manager module and MCP server config
- substrate -- Nix build patterns (provides
hm-service-helpers) - blackmatter -- Home-manager module aggregator
- Paneru -- Original upstream project
Credits
Forked from Paneru by Karinushka. Window management inspired by Yabai, Niri, and PaperWM.spoon.