An open API service indexing awesome lists of open source software.

https://github.com/asthestarsfalll/piri

πŸš€ Piri is a high-performance Niri extension tool built with Rust. It leverages efficient Niri IPC interaction and a unified event distribution mechanism to provide a robust, state-driven plugin system.
https://github.com/asthestarsfalll/piri

desktop extension multiscreen niri niri-ipc plugin productivity quality-of-life scratchpad scriptable

Last synced: 12 days ago
JSON representation

πŸš€ Piri is a high-performance Niri extension tool built with Rust. It leverages efficient Niri IPC interaction and a unified event distribution mechanism to provide a robust, state-driven plugin system.

Awesome Lists containing this project

README

          

# Piri

**English** | [δΈ­ζ–‡](README.md)

---

Piri is a high-performance [Niri](https://github.com/YaLTeR/niri) extension tool built with Rust. It leverages efficient Niri IPC interaction and a unified event distribution mechanism to provide a robust, state-driven plugin system.

## Core Plugins

- πŸ“¦ **Scratchpads**: Intelligent hide/show windows. Supports auto-capturing existing windows or launching on-demand, following you seamlessly across workspaces and monitors (see [Scratchpads Docs](docs/en/plugins/scratchpads.md))
- πŸ”Œ **Empty**: Automation for empty workspaces. Automatically triggers preset commands when switching to an empty workspace to get you into the flow faster (see [Empty Docs](docs/en/plugins/empty.md))
- 🎯 **Window Rule**: Powerful rule engine. Automatically places windows based on regex matching and provides focus-triggered command execution with a built-in de-duplication mechanism (see [Window Rule Docs](docs/en/plugins/window_rule.md))
- πŸ“ **Workspace Rule**: Workspace window layout management. Provides automatic width adjustment, automatic tiling, automatic alignment, and automatic maximization. Integrates original Autofill functionality (see [Workspace Rule Docs](docs/en/plugins/workspace_rule.md))
- πŸ”’ **Singleton**: Single-instance assurance. Ensures specific applications remain globally unique, supporting quick focus or automatic process launching (see [Singleton Docs](docs/en/plugins/singleton.md))
- πŸ“Œ **Mark**: Named window marks for quick focus. Bind the focused window to a name and jump back later; bindings are in-memory only (see [Mark Docs](docs/en/plugins/mark.md))
- πŸ“ **Sticky**: Floating window follower. Pin one floating window to follow the focused workspace, with optional cross-monitor behavior via `--cross` (see [Sticky Docs](docs/en/plugins/sticky.md))
- πŸ“‹ **Window Order**: Intelligent reordering. Automatically reorders tiled windows based on configured weights, preserving relative positions for identical weights to minimize movement (see [Window Order Docs](docs/en/plugins/window_order.md))
- 🍽️ **Swallow**: Window swallowing mechanism. Automatically hides parent windows when child windows are opened, allowing child windows to replace parent windows in the layout (see [Swallow Docs](docs/en/plugins/swallow.md))

## Window Matching

Piri uses a unified window matching mechanism: regex on `app_id` and/or `title`. Plugins such as `window_rule`, `singleton`, and `scratchpads` use it to find windows.

**Supported matching**:
- Full regular expression syntax
- Match `app_id` and/or `title`
- If both are set, **either** match can satisfy the rule (OR)

> **Note**: The Window Rule plugin also supports list matching for `app_id` and `title`; see [Window Rule Docs](docs/en/plugins/window_rule.md).

**Details**: [Window matching](docs/en/window_matching.md)

## Quick Start

### Installation

#### Using Install Script (Recommended)

The easiest way is to use the provided install script:

```bash
# Run the install script
./install.sh
```

The install script will automatically:
- Build the release version
- Install to `~/.local/bin/piri` (regular user) or `/usr/local/bin/piri` (root)
- Copy configuration file to `~/.config/niri/piri.toml`

If `~/.local/bin` is not in your PATH, the script will prompt you to add it.

#### Using Cargo

```bash
# Install to user directory (recommended, no root required)
cargo install --path .

# Or install to system directory (requires root)
sudo cargo install --path . --root /usr/local
```

After installation, if installed to user directory, make sure `~/.cargo/bin` is in your `PATH`:

```bash
export PATH="$PATH:$HOME/.cargo/bin"
```

You can add this command to your shell configuration file (e.g., `~/.bashrc` or `~/.zshrc`).

### Configuration

Copy the example configuration file to the config directory:

```bash
mkdir -p ~/.config/niri
cp config.example.toml ~/.config/niri/piri.toml
```

Then edit `~/.config/niri/piri.toml` to configure your features.

## Usage

### Starting the Daemon

#### Manual Start

```bash
# Start daemon (runs in foreground)
piri daemon
```

```bash
# More debug logs
piri --debug daemon
```

#### Auto-start (Recommended)

Add the following configuration to your niri config file to automatically start piri daemon when niri starts:

Edit `~/.config/niri/config.kdl`, add to the `spawn-at-startup` section:

```kdl
spawn-at-startup "bash" "-c" "/path/to/piri daemon > /dev/null 2>&1 &"
```

### Shell Completion

Generate shell completion scripts:

```bash
# Bash
piri completion bash > ~/.bash_completion.d/piri

# Zsh
piri completion zsh > ~/.zsh_completion.d/_piri

# Fish
piri completion fish > ~/.config/fish/completions/piri.fish
```

## Plugins

### Scratchpads

![Scratchpads](./assets/scratchpads.mp4)

Quickly show and hide windows of frequently used applications. Supports cross-workspace and cross-monitor, so you can quickly access your scratchpad windows regardless of which workspace or monitor you're on. Features **dynamic window addition**, **automatic retention of manual size and margin adjustments**, **automatic moving to a specific workspace when hidden**, and **swallowing window into the currently focused window** (`swallow_to_focus` option).

**Configuration Example**:
```toml
[piri.plugins]
scratchpads = true

[piri.scratchpad]
default_size = "40% 60%"
default_margin = 50
move_to_workspace = "tmp" # Automatically move to workspace tmp when hidden

[scratchpads.term]
direction = "fromRight"
command = "GTK_IM_MODULE=wayland ghostty --class=float.dropterm"
app_id = "float.dropterm"
size = "40% 60%"
margin = 50

[scratchpads.preview]
direction = "fromRight"
command = "imv"
app_id = "imv"
size = "60% 80%"
margin = 50
swallow_to_focus = true # Automatically swallow into focused window when shown
```

**Quick Usage**:
```bash
# Toggle scratchpad show/hide
piri scratchpads {name} toggle

# Dynamically add current window as scratchpad
piri scratchpads {name} add {direction}
```

> **Tip**: Dynamically added windows only use default size and margin during initial registration. After that, you can manually resize or move the window, and the plugin will automatically maintain these adjustments.

For detailed documentation, please refer to [Scratchpads documentation](docs/en/plugins/scratchpads.md).

### Empty

Automatically execute commands when switching to empty workspaces, useful for automating workflows.

> **Reference**: This functionality is similar to [Hyprland's `on-created-empty` workspace rule](https://wiki.hypr.land/Configuring/Workspace-Rules/#rules).

**Configuration Example**:
```toml
[piri.plugins]
empty = true

# Execute command when switching to workspace 1 if it's empty
[empty.1]
command = "alacritty"

# Use workspace name
[empty.main]
command = "firefox"
```

**Workspace Identifiers**: Supports matching by workspace name (e.g., `"main"`) or index (e.g., `"1"`).

For detailed documentation, please refer to [Plugin System documentation](docs/en/plugins/empty.md).

### Window Rule

Automatically move windows to specified workspaces based on their `app_id` or `title` using regular expression matching. This is very useful for automating window management, such as automatically assigning specific applications to specific workspaces.

> **Reference**: This functionality is similar to [Hyprland's window rules](https://wiki.hypr.land/Configuring/Window-Rules/).

**Configuration Example**:
```toml
[piri.plugins]
window_rule = true

# Match by app_id
[[window_rule]]
app_id = "ghostty"
open_on_workspace = "1"

# Match by title
[[window_rule]]
title = ".*Chrome.*"
open_on_workspace = "browser"

# Specify both app_id and title (either match works)
[[window_rule]]
app_id = "code"
title = ".*VS Code.*"
open_on_workspace = "dev"

# Only focus_command, don't move window
[[window_rule]]
title = ".*Chrome.*"
focus_command = "notify-send 'Chrome focused'"

# Execute focus_command only once per rule (rule-level, not window-level)
[[window_rule]]
app_id = "firefox"
focus_command = "notify-send 'Firefox focused'"
focus_command_once = true

# app_id as a list (any one matches)
[[window_rule]]
app_id = ["code", "code-oss", "codium"]
open_on_workspace = "dev"

# title as a list (any one matches)
[[window_rule]]
title = [".*Chrome.*", ".*Chromium.*", ".*Google Chrome.*"]
open_on_workspace = "browser"
```

**Features**:
- Regular expression pattern matching support
- Match by `app_id` or `title`, or both combined (OR logic)
- Support for lists of patterns: `app_id` and `title` can be lists, any one match triggers the rule
- Support workspace name or index matching
- Focus-triggered command execution with built-in de-duplication mechanism
- `focus_command_once` option: execute `focus_command` only once per rule globally (see [issue #1](https://github.com/Asthestarsfalll/piri/issues/1))
- Pure event-driven, real-time response to window creation

For detailed documentation, please refer to the [Window Rule documentation](docs/en/plugins/window_rule.md).

### Workspace Rule

![Workspace Rule](./assets/autofill.mp4)

Workspace window layout management plugin that provides automatic width adjustment, automatic tiling, automatic alignment, and automatic maximization. Integrates the original Autofill plugin functionality, providing comprehensive workspace window management capabilities.

**Configuration Example**:
```toml
[piri.plugins]
workspace_rule = true

# Default configuration (applies to all workspaces)
[piri.workspace_rule]
auto_width = ["100%", "50%", "33.33%", "25%", "20%"]
auto_fill = true # Automatic alignment (original Autofill functionality)
auto_maximize = true # Automatic maximization

# Workspace-specific configuration
[workspace_rule.main]
auto_maximize = true

[workspace_rule.dev]
auto_width = ["100%", ["45%", "55%"], ["30%", "35%", "35%"]]
auto_tile = true # Automatic tiling
```

**Features**:
- Automatic width adjustment: Automatically adjust window widths based on window count
- Automatic tiling: Automatically merge new windows into existing columns
- Automatic alignment: Automatically align to rightmost position after closing windows (original Autofill functionality)
- Automatic maximization: Automatically maximize when only one window, unmaximize when multiple windows
- Workspace-aware: Each workspace can be configured independently
- Flexible configuration: Supports default and workspace-specific configuration

**Migration from Autofill**:
```toml
# Old configuration
[piri.plugins]
autofill = true

# New configuration
[piri.plugins]
workspace_rule = true

[piri.workspace_rule]
auto_fill = true # Enable original Autofill functionality
```

For detailed documentation, please refer to the [Workspace Rule documentation](docs/en/plugins/workspace_rule.md).

### Singleton

Manages singleton windows - windows that should only have one instance. When you toggle a singleton, if the window already exists, it will focus it; otherwise, it will launch the application. This is useful for applications like browsers, terminals, or other tools where you typically only want one instance running.

**Configuration Example**:
```toml
[piri.plugins]
singleton = true

[singleton.browser]
command = "google-chrome-stable"

[singleton.term]
command = "GTK_IM_MODULE=wayland ghostty --class=singleton.term"
app_id = "singleton.term"

[singleton.editor]
command = "code"
app_id = "code"
on_created_command = "notify-send 'Editor opened'"
```

**Quick Usage**:
```bash
# Toggle singleton (focus if exists, launch if not)
piri singleton {name} toggle
```

**Features**:
- Smart window detection, automatically detects existing windows
- Automatic App ID extraction, no manual specification needed
- Window registry for fast lookup of existing windows
- Automatically focuses existing windows, prevents duplicate instances
- Supports executing custom commands after window creation (`on_created_command`)

For detailed documentation, please refer to the [Singleton documentation](docs/en/plugins/singleton.md).

### Mark

Assign **named marks** (e.g. letters `a`, `b`) to windows for quick focus. Marks are kept in the daemon’s memory and are **cleared when the daemon restarts**. You only enable the plugin in `piri.toml` and bind `spawn` commands in Niri for marks you use often.

**Configuration example**:

```toml
[piri.plugins]
mark = true
```

**Quick usage**:

```bash
# No valid binding: bind focused window to name; binding exists and window lives: focus it
piri mark {name} toggle

# Force-bind focused window to name (overwrites previous binding)
piri mark {name} add

# Remove this mark
piri mark {name} delete
```

**Note**: Piri cannot capture the β€œnext key” globally. To save shortcut slots, you can use a launcher (e.g. `fuzzel`) to pick a letter, then run the commands above. If Niri adds multi-key sequences or binding modes, you can group `piri mark …` calls under one prefix.

For detailed documentation, see the [Mark documentation](docs/en/plugins/mark.md).

### Sticky

Set the currently focused **floating window** as a follower so it moves with your focused workspace. Useful for persistent utility windows like dictionary, translator, logs, or media control.

**Configuration example**:

```toml
[piri.plugins]
sticky = true
```

**Quick usage**:

```bash
# Set sticky (same-monitor follow only)
piri sticky add

# Set sticky (allow cross-monitor follow)
piri sticky add --cross

# Remove sticky binding
piri sticky delete
```

For details, see the [Sticky documentation](docs/en/plugins/sticky.md).

### Window Order

![Window Order - Manual Trigger](./assets/window_order.mp4)

![Window Order - Event-Driven Automatic Trigger](./assets/window_order_envent.mp4)

Automatically reorder windows in workspace based on configured priority weights. Larger weight values position windows further to the left.

**Configuration Example**:
```toml
[piri.plugins]
window_order = true

[piri.window_order]
enable_event_listener = true # Enable event listening for automatic reordering
default_weight = 0 # Default weight for unconfigured windows
# workspaces = ["1", "2", "dev"] # Optional: only apply to specific workspaces (empty = all)

[window_order]
google-chrome = 100
code = 80
ghostty = 70
```

**Quick Usage**:
```bash
# Manually trigger window reordering (works in any workspace)
piri window_order toggle
```

**Features**:
- Intelligent sorting algorithm that minimizes window moves
- Supports manual trigger and event-driven automatic trigger
- Supports workspace filtering (only for automatic trigger)
- Preserves relative order for windows with same weight
- Supports partial matching of `app_id`

For detailed documentation, please refer to the [Window Order documentation](docs/en/plugins/window_order.md).

### Swallow

![Swallow](./assets/autofill_1.mp4)

Automatically hides parent windows when child windows are opened, allowing child windows to replace parent windows in the layout. This is useful for scenarios like terminals spawning image viewers or media players.

**Configuration Example**:
```toml
[piri.plugins]
swallow = true

[piri.swallow]
use_pid_matching = true # Enable PID-based parent-child process matching (default: true)

# Global exclude rule (optional)
[piri.swallow.exclude]
app_id = [".*dialog.*"]

# Rules list
[[swallow]]
parent_app_id = [".*terminal.*", ".*alacritty.*", ".*foot.*", ".*ghostty.*"]
child_app_id = [".*mpv.*", ".*imv.*", ".*feh.*"]
exclude_child_app_id = [".*dialog.*", ".*error.*"]

[[swallow]]
parent_app_id = ["code", "nvim-qt"]
child_app_id = [".*preview.*", ".*markdown.*"]
```

**Features**:
- Supports PID-based parent-child process matching (enabled by default)
- Supports rule-based matching (via `app_id`, `title`, or `pid` patterns)
- Supports global and rule-level exclude rules
- Intelligent focus window queue for automatic parent window discovery
- Automatically handles workspace movement and floating window conversion
- Smart floating window handling: Floating windows are not swallowed, but re-attempts swallowing when converting from floating to tiled

For detailed documentation, please refer to the [Swallow documentation](docs/en/plugins/swallow.md).

## Documentation

- [Architecture](docs/en/architecture.md) - Project architecture and how it works
- [Plugin System](docs/en/plugins/plugins.md) - Detailed plugin system documentation
- [Development Guide](docs/en/development.md) - Development, extension, and contribution guide

## License

MIT License

## References

This project is inspired by [Pyprland](https://github.com/hyprland-community/pyprland). Pyprland is an excellent project that provides extension capabilities for the Hyprland compositor, offering a plethora of plugins to enhance user experience.