nsticky is a window management helper tool built on top of niri. It manages sticky windows — windows fixed across all workspaces — and staged windows — windows temporarily moved to a dedicated workspace. Niri has no native global sticky windows; nsticky lets you pin windows to every workspace and park them in a staging area without losing track of them.
Make sure you have Rust installed along with the required niri tool.
git clone https://github.com/lonerOrz/nsticky.git
cd nsticky
cargo build --release{
inputs = {
nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable";
flake-utils.url = "github:numtide/flake-utils";
nsticky.url = "github:lonerOrz/nsticky";
};
outputs =
inputs@{
self,
flake-utils,
nixpkgs,
...
}:
flake-utils.lib.eachDefaultSystem (
system:
let
pkgs = import nixpkgs {
inherit system;
};
in
{
devShells.default = pkgs.mkShell {
packages = [ inputs.nsticky.packages.${system}.nsticky ];
};
}
);
}Create ~/.config/nsticky/config.toml to auto-sticky windows matching rules:
menu = "pantry -m"
[sticky.firefox]
app-id = "firefox"
[sticky.kitty]
app-id = "kitty"
title = ".*server.*"
[sticky.gmail]
title = ".*Gmail.*"Matching rules:
app_idandtitleare AND logic (both must match)- Use regex patterns
- If only one field is specified, it matches any value
You can also configure nsticky from the home-manager module:
{ inputs, ... }:
{
imports = [
inputs.nsticky.homeModules.default
];
programs.nsticky = {
enable = true;
menu = "pantry -m"; # optional: selector for `stage restore`
settings = {
sticky = {
firefox.app-id = "firefox";
kitty = {
app-id = "kitty";
title = ".*server.*";
};
gmail.title = ".*Gmail.*";
};
};
};
}Configure niri to auto-start the nsticky daemon:
spawn-at-startup "nsticky"Control nsticky from the terminal using grouped CLI commands:
nsticky sticky add <window_id> # Add a window to the sticky list
nsticky sticky remove <window_id> # Remove a window from the sticky list
nsticky sticky list # List all sticky windows
nsticky sticky toggle-active # Toggle sticky state of the active window
nsticky sticky toggle-appid <appid> # Toggle sticky state of window by application ID
nsticky sticky toggle-title <title> # Toggle sticky state of window by titleNote: The stage commands require a "stage" workspace to be defined in your Niri config:
workspace "stage" { open-on-output = "eDP-1" # optional: specify which output }
nsticky stage list # List all currently staged windows
nsticky stage add <window_id> # Move a sticky window to the "stage" workspace
nsticky stage remove <window_id> # Move a staged window back to the current workspace
nsticky stage toggle-active # Toggle stage state of the active window (if in sticky, moves to stage; if in stage, moves back)
nsticky stage toggle-appid <appid> # Move window with app ID to stage (if sticky) or back to current workspace (if staged)
nsticky stage toggle-title <title> # Move window with title to stage (if sticky) or back to current workspace (if staged)
nsticky stage add-all # Move all sticky windows to the "stage" workspace
nsticky stage remove-all # Move all staged windows back to the current workspace
nsticky stage restore # Pick staged window(s) to restore; pipes list to $NSTICKY_MENU (or config `menu`), else terminal promptstage restore is menu-agnostic and supports multi-select. It pipes the staged window list to an external selector, or falls back to a built-in terminal prompt.
- Set the selector in
config.toml:menu = "pantry"— or any dmenu-compatible command. - The
NSTICKY_MENUenvironment variable overrides the config value. - The stdin list is
id\tapp_id — title; the selected lines' ids are restored. - Recommended selector: pantry.
You can set up shortcuts in niri:
Mod+Ctrl+Space { spawn "nsticky" "sticky" "toggle-active"; }
Mod+Shift+Space { spawn "nsticky" "stage" "toggle-active"; }
Mod+Shift+R { spawn "nsticky" "stage" "restore"; } # pick staged window(s) to restorenstickyrelies on theniriwindow manager.- The daemon requires the
NIRI_SOCKETenvironment variable to connect to Niri. - The staging feature moves windows to a workspace named "stage". Ensure this workspace exists in your Niri configuration.
- Window IDs can be obtained using
niri msg --json windows
Contributions and feedback are welcome!
Please format code with cargo fmt and check with cargo clippy.
This project is licensed under the BSD 3-Clause License.
If you find
nstickyuseful, please give it a ⭐ and share! 🎉
