{"id":39720482,"url":"https://github.com/asthestarsfalll/piri","last_synced_at":"2026-04-13T14:01:30.924Z","repository":{"id":331262076,"uuid":"1125215891","full_name":"Asthestarsfalll/piri","owner":"Asthestarsfalll","description":"🚀 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.","archived":false,"fork":false,"pushed_at":"2026-02-02T10:32:35.000Z","size":36195,"stargazers_count":23,"open_issues_count":1,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2026-02-02T23:38:09.531Z","etag":null,"topics":["desktop","extension","multiscreen","niri","niri-ipc","plugin","productivity","quality-of-life","scratchpad","scriptable"],"latest_commit_sha":null,"homepage":"","language":"Rust","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Asthestarsfalll.png","metadata":{"files":{"readme":"README.en.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2025-12-30T10:38:06.000Z","updated_at":"2026-02-02T13:43:20.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/Asthestarsfalll/piri","commit_stats":null,"previous_names":["asthestarsfalll/piri"],"tags_count":5,"template":false,"template_full_name":null,"purl":"pkg:github/Asthestarsfalll/piri","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Asthestarsfalll%2Fpiri","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Asthestarsfalll%2Fpiri/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Asthestarsfalll%2Fpiri/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Asthestarsfalll%2Fpiri/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Asthestarsfalll","download_url":"https://codeload.github.com/Asthestarsfalll/piri/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Asthestarsfalll%2Fpiri/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":31755536,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-13T13:27:56.013Z","status":"ssl_error","status_checked_at":"2026-04-13T13:21:23.512Z","response_time":93,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["desktop","extension","multiscreen","niri","niri-ipc","plugin","productivity","quality-of-life","scratchpad","scriptable"],"created_at":"2026-01-18T10:46:40.000Z","updated_at":"2026-04-13T14:01:30.903Z","avatar_url":"https://github.com/Asthestarsfalll.png","language":"Rust","readme":"# Piri\n\n**English** | [中文](README.md)\n\n---\n\nPiri 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.\n\n## Core Plugins\n\n- 📦 **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))\n- 🔌 **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))\n- 🎯 **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))\n- 📐 **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))\n- 🔒 **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))\n- 📌 **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))\n- 📍 **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))\n- 📋 **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))\n- 🍽️ **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))\n\n## Window Matching\n\nPiri 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.\n\n**Supported matching**:\n- Full regular expression syntax\n- Match `app_id` and/or `title`\n- If both are set, **either** match can satisfy the rule (OR)\n\n\u003e **Note**: The Window Rule plugin also supports list matching for `app_id` and `title`; see [Window Rule Docs](docs/en/plugins/window_rule.md).\n\n**Details**: [Window matching](docs/en/window_matching.md)\n\n## Quick Start\n\n### Installation\n\n#### Using Install Script (Recommended)\n\nThe easiest way is to use the provided install script:\n\n```bash\n# Run the install script\n./install.sh\n```\n\nThe install script will automatically:\n- Build the release version\n- Install to `~/.local/bin/piri` (regular user) or `/usr/local/bin/piri` (root)\n- Copy configuration file to `~/.config/niri/piri.toml`\n\nIf `~/.local/bin` is not in your PATH, the script will prompt you to add it.\n\n#### Using Cargo\n\n```bash\n# Install to user directory (recommended, no root required)\ncargo install --path .\n\n# Or install to system directory (requires root)\nsudo cargo install --path . --root /usr/local\n```\n\nAfter installation, if installed to user directory, make sure `~/.cargo/bin` is in your `PATH`:\n\n```bash\nexport PATH=\"$PATH:$HOME/.cargo/bin\"\n```\n\nYou can add this command to your shell configuration file (e.g., `~/.bashrc` or `~/.zshrc`).\n\n### Configuration\n\nCopy the example configuration file to the config directory:\n\n```bash\nmkdir -p ~/.config/niri\ncp config.example.toml ~/.config/niri/piri.toml\n```\n\nThen edit `~/.config/niri/piri.toml` to configure your features.\n\n## Usage\n\n### Starting the Daemon\n\n#### Manual Start\n\n```bash\n# Start daemon (runs in foreground)\npiri daemon\n```\n\n```bash\n# More debug logs\npiri --debug daemon\n```\n\n#### Auto-start (Recommended)\n\nAdd the following configuration to your niri config file to automatically start piri daemon when niri starts:\n\nEdit `~/.config/niri/config.kdl`, add to the `spawn-at-startup` section:\n\n```kdl\nspawn-at-startup \"bash\" \"-c\" \"/path/to/piri daemon \u003e /dev/null 2\u003e\u00261 \u0026\"\n```\n\n### Shell Completion\n\nGenerate shell completion scripts:\n\n```bash\n# Bash\npiri completion bash \u003e ~/.bash_completion.d/piri\n\n# Zsh\npiri completion zsh \u003e ~/.zsh_completion.d/_piri\n\n# Fish\npiri completion fish \u003e ~/.config/fish/completions/piri.fish\n```\n\n## Plugins\n\n### Scratchpads\n\n![Scratchpads](./assets/scratchpads.mp4)\n\nQuickly 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).\n\n**Configuration Example**:\n```toml\n[piri.plugins]\nscratchpads = true\n\n[piri.scratchpad]\ndefault_size = \"40% 60%\"\ndefault_margin = 50\nmove_to_workspace = \"tmp\" # Automatically move to workspace tmp when hidden\n\n[scratchpads.term]\ndirection = \"fromRight\"\ncommand = \"GTK_IM_MODULE=wayland ghostty --class=float.dropterm\"\napp_id = \"float.dropterm\"\nsize = \"40% 60%\"\nmargin = 50\n\n[scratchpads.preview]\ndirection = \"fromRight\"\ncommand = \"imv\"\napp_id = \"imv\"\nsize = \"60% 80%\"\nmargin = 50\nswallow_to_focus = true  # Automatically swallow into focused window when shown\n```\n\n**Quick Usage**:\n```bash\n# Toggle scratchpad show/hide\npiri scratchpads {name} toggle\n\n# Dynamically add current window as scratchpad\npiri scratchpads {name} add {direction}\n```\n\n\u003e **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.\n\nFor detailed documentation, please refer to [Scratchpads documentation](docs/en/plugins/scratchpads.md).\n\n### Empty\n\nAutomatically execute commands when switching to empty workspaces, useful for automating workflows.\n\n\u003e **Reference**: This functionality is similar to [Hyprland's `on-created-empty` workspace rule](https://wiki.hypr.land/Configuring/Workspace-Rules/#rules).\n\n**Configuration Example**:\n```toml\n[piri.plugins]\nempty = true\n\n# Execute command when switching to workspace 1 if it's empty\n[empty.1]\ncommand = \"alacritty\"\n\n# Use workspace name\n[empty.main]\ncommand = \"firefox\"\n```\n\n**Workspace Identifiers**: Supports matching by workspace name (e.g., `\"main\"`) or index (e.g., `\"1\"`).\n\nFor detailed documentation, please refer to [Plugin System documentation](docs/en/plugins/empty.md).\n\n### Window Rule\n\nAutomatically 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.\n\n\u003e **Reference**: This functionality is similar to [Hyprland's window rules](https://wiki.hypr.land/Configuring/Window-Rules/).\n\n**Configuration Example**:\n```toml\n[piri.plugins]\nwindow_rule = true\n\n# Match by app_id\n[[window_rule]]\napp_id = \"ghostty\"\nopen_on_workspace = \"1\"\n\n# Match by title\n[[window_rule]]\ntitle = \".*Chrome.*\"\nopen_on_workspace = \"browser\"\n\n# Specify both app_id and title (either match works)\n[[window_rule]]\napp_id = \"code\"\ntitle = \".*VS Code.*\"\nopen_on_workspace = \"dev\"\n\n# Only focus_command, don't move window\n[[window_rule]]\ntitle = \".*Chrome.*\"\nfocus_command = \"notify-send 'Chrome focused'\"\n\n# Execute focus_command only once per rule (rule-level, not window-level)\n[[window_rule]]\napp_id = \"firefox\"\nfocus_command = \"notify-send 'Firefox focused'\"\nfocus_command_once = true\n\n# app_id as a list (any one matches)\n[[window_rule]]\napp_id = [\"code\", \"code-oss\", \"codium\"]\nopen_on_workspace = \"dev\"\n\n# title as a list (any one matches)\n[[window_rule]]\ntitle = [\".*Chrome.*\", \".*Chromium.*\", \".*Google Chrome.*\"]\nopen_on_workspace = \"browser\"\n```\n\n**Features**:\n- Regular expression pattern matching support\n- Match by `app_id` or `title`, or both combined (OR logic)\n- Support for lists of patterns: `app_id` and `title` can be lists, any one match triggers the rule\n- Support workspace name or index matching\n- Focus-triggered command execution with built-in de-duplication mechanism\n- `focus_command_once` option: execute `focus_command` only once per rule globally (see [issue #1](https://github.com/Asthestarsfalll/piri/issues/1))\n- Pure event-driven, real-time response to window creation\n\nFor detailed documentation, please refer to the [Window Rule documentation](docs/en/plugins/window_rule.md).\n\n### Workspace Rule\n\n![Workspace Rule](./assets/autofill.mp4)\n\nWorkspace 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.\n\n**Configuration Example**:\n```toml\n[piri.plugins]\nworkspace_rule = true\n\n# Default configuration (applies to all workspaces)\n[piri.workspace_rule]\nauto_width = [\"100%\", \"50%\", \"33.33%\", \"25%\", \"20%\"]\nauto_fill = true  # Automatic alignment (original Autofill functionality)\nauto_maximize = true  # Automatic maximization\n\n# Workspace-specific configuration\n[workspace_rule.main]\nauto_maximize = true\n\n[workspace_rule.dev]\nauto_width = [\"100%\", [\"45%\", \"55%\"], [\"30%\", \"35%\", \"35%\"]]\nauto_tile = true  # Automatic tiling\n```\n\n**Features**:\n- Automatic width adjustment: Automatically adjust window widths based on window count\n- Automatic tiling: Automatically merge new windows into existing columns\n- Automatic alignment: Automatically align to rightmost position after closing windows (original Autofill functionality)\n- Automatic maximization: Automatically maximize when only one window, unmaximize when multiple windows\n- Workspace-aware: Each workspace can be configured independently\n- Flexible configuration: Supports default and workspace-specific configuration\n\n**Migration from Autofill**:\n```toml\n# Old configuration\n[piri.plugins]\nautofill = true\n\n# New configuration\n[piri.plugins]\nworkspace_rule = true\n\n[piri.workspace_rule]\nauto_fill = true  # Enable original Autofill functionality\n```\n\nFor detailed documentation, please refer to the [Workspace Rule documentation](docs/en/plugins/workspace_rule.md).\n\n### Singleton\n\nManages 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.\n\n**Configuration Example**:\n```toml\n[piri.plugins]\nsingleton = true\n\n[singleton.browser]\ncommand = \"google-chrome-stable\"\n\n[singleton.term]\ncommand = \"GTK_IM_MODULE=wayland ghostty --class=singleton.term\"\napp_id = \"singleton.term\"\n\n[singleton.editor]\ncommand = \"code\"\napp_id = \"code\"\non_created_command = \"notify-send 'Editor opened'\"\n```\n\n**Quick Usage**:\n```bash\n# Toggle singleton (focus if exists, launch if not)\npiri singleton {name} toggle\n```\n\n**Features**:\n- Smart window detection, automatically detects existing windows\n- Automatic App ID extraction, no manual specification needed\n- Window registry for fast lookup of existing windows\n- Automatically focuses existing windows, prevents duplicate instances\n- Supports executing custom commands after window creation (`on_created_command`)\n\nFor detailed documentation, please refer to the [Singleton documentation](docs/en/plugins/singleton.md).\n\n### Mark\n\nAssign **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.\n\n**Configuration example**:\n\n```toml\n[piri.plugins]\nmark = true\n```\n\n**Quick usage**:\n\n```bash\n# No valid binding: bind focused window to name; binding exists and window lives: focus it\npiri mark {name} toggle\n\n# Force-bind focused window to name (overwrites previous binding)\npiri mark {name} add\n\n# Remove this mark\npiri mark {name} delete\n```\n\n**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.\n\nFor detailed documentation, see the [Mark documentation](docs/en/plugins/mark.md).\n\n### Sticky\n\nSet 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.\n\n**Configuration example**:\n\n```toml\n[piri.plugins]\nsticky = true\n```\n\n**Quick usage**:\n\n```bash\n# Set sticky (same-monitor follow only)\npiri sticky add\n\n# Set sticky (allow cross-monitor follow)\npiri sticky add --cross\n\n# Remove sticky binding\npiri sticky delete\n```\n\nFor details, see the [Sticky documentation](docs/en/plugins/sticky.md).\n\n### Window Order\n\n![Window Order - Manual Trigger](./assets/window_order.mp4)\n\n![Window Order - Event-Driven Automatic Trigger](./assets/window_order_envent.mp4)\n\nAutomatically reorder windows in workspace based on configured priority weights. Larger weight values position windows further to the left.\n\n**Configuration Example**:\n```toml\n[piri.plugins]\nwindow_order = true\n\n[piri.window_order]\nenable_event_listener = true  # Enable event listening for automatic reordering\ndefault_weight = 0            # Default weight for unconfigured windows\n# workspaces = [\"1\", \"2\", \"dev\"]  # Optional: only apply to specific workspaces (empty = all)\n\n[window_order]\ngoogle-chrome = 100\ncode = 80\nghostty = 70\n```\n\n**Quick Usage**:\n```bash\n# Manually trigger window reordering (works in any workspace)\npiri window_order toggle\n```\n\n**Features**:\n- Intelligent sorting algorithm that minimizes window moves\n- Supports manual trigger and event-driven automatic trigger\n- Supports workspace filtering (only for automatic trigger)\n- Preserves relative order for windows with same weight\n- Supports partial matching of `app_id`\n\nFor detailed documentation, please refer to the [Window Order documentation](docs/en/plugins/window_order.md).\n\n### Swallow\n\n![Swallow](./assets/autofill_1.mp4)\n\nAutomatically 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.\n\n**Configuration Example**:\n```toml\n[piri.plugins]\nswallow = true\n\n[piri.swallow]\nuse_pid_matching = true  # Enable PID-based parent-child process matching (default: true)\n\n# Global exclude rule (optional)\n[piri.swallow.exclude]\napp_id = [\".*dialog.*\"]\n\n# Rules list\n[[swallow]]\nparent_app_id = [\".*terminal.*\", \".*alacritty.*\", \".*foot.*\", \".*ghostty.*\"]\nchild_app_id = [\".*mpv.*\", \".*imv.*\", \".*feh.*\"]\nexclude_child_app_id = [\".*dialog.*\", \".*error.*\"]\n\n[[swallow]]\nparent_app_id = [\"code\", \"nvim-qt\"]\nchild_app_id = [\".*preview.*\", \".*markdown.*\"]\n```\n\n**Features**:\n- Supports PID-based parent-child process matching (enabled by default)\n- Supports rule-based matching (via `app_id`, `title`, or `pid` patterns)\n- Supports global and rule-level exclude rules\n- Intelligent focus window queue for automatic parent window discovery\n- Automatically handles workspace movement and floating window conversion\n- Smart floating window handling: Floating windows are not swallowed, but re-attempts swallowing when converting from floating to tiled\n\nFor detailed documentation, please refer to the [Swallow documentation](docs/en/plugins/swallow.md).\n\n## Documentation\n\n- [Architecture](docs/en/architecture.md) - Project architecture and how it works\n- [Plugin System](docs/en/plugins/plugins.md) - Detailed plugin system documentation\n- [Development Guide](docs/en/development.md) - Development, extension, and contribution guide\n\n## License\n\nMIT License\n\n## References\n\nThis 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.\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fasthestarsfalll%2Fpiri","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fasthestarsfalll%2Fpiri","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fasthestarsfalll%2Fpiri/lists"}