2 unstable releases

0.1.0 Mar 8, 2026
0.0.1 Mar 8, 2026

#457 in Game dev

MIT license

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 exec bindings
  • 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:

  1. $AYATSURI_CONFIG environment variable
  2. $XDG_CONFIG_HOME/ayatsuri/ayatsuri.yaml (preferred YAML format)
  3. $XDG_CONFIG_HOME/ayatsuri/ayatsuri.toml
  4. $HOME/.ayatsuri.toml
  5. $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

Credits

Forked from Paneru by Karinushka. Window management inspired by Yabai, Niri, and PaperWM.spoon.

License

MIT

No runtime deps