---
title: niri
description: Scrollable-tiling Wayland compositor config — input, layout, window rules, per-display profiles, keybindings, and launcher scripts.
---

[niri](https://github.com/YaLTeR/niri) is a scrollable-tiling Wayland compositor. Here it runs with the Noctalia shell (bar, panels, OSD, notifications), vicinae as the launcher, and `niri-dynamic-workspaces` for named workspaces.

:::note
Linux `desktop` and `laptop` roles only. `.chezmoiignore.tmpl` skips `~/.config/niri/**`, `~/.local/bin/niri-launch`, and `~/.local/bin/cp-appid` on other roles. The `niri-launch-*` wrappers are not in the ignore list and are applied everywhere.
:::

## Files

| Source (repo) | Target |
| --- | --- |
| `dot_config/niri/config.kdl` | `~/.config/niri/config.kdl` |
| `dot_config/niri/noctalia.kdl` | `~/.config/niri/noctalia.kdl` |
| `dot_config/niri/exact_config.d/*.kdl` | `~/.config/niri/config.d/*.kdl` |
| `dot_config/niri/scripts/executable_*` | `~/.config/niri/scripts/*` (executable) |
| `dot_local/bin/executable_niri-launch`, `executable_niri-launch-{ai,browser,terminal,webapp}` | `~/.local/bin/niri-launch*` |
| `dot_local/bin/executable_cp-appid` | `~/.local/bin/cp-appid` |

`exact_config.d` is a chezmoi **exact** directory. On apply, chezmoi deletes any file in `~/.config/niri/config.d/` that the repo does not manage, so stale fragments cannot linger. None of these files are templates.

- ~/.config/niri/
  - config.kdl
  - noctalia.kdl
  - config.d/
    - 10-input-and-cursor.kdl
    - 20-layout-and-overview.kdl
    - 30-window-rules.kdl
    - 40-environment.kdl
    - 50-startup.kdl
    - 60-animations.kdl
    - 70-binds.kdl
    - 80-layer-rules.kdl
    - 90-outputs.kdl
    - 91-internal-display.kdl
    - 92-ultrawide.kdl
    - 93-apple-display.kdl
    - 94-portable-hdmi.kdl
    - 95-portable-usbc.kdl
  - scripts/
    - kill-phantom-display.fish
    - ndw-route-workspace.sh
    - niri-floating-sidebar.sh
    - niri-focus.sh
    - niri-move.sh

## Installation

- `niri` and `xdg-user-dirs` from pacman, `noctalia-shell` from the AUR. Declared under `packages.arch.desktop` in `.chezmoidata/packages.yaml` and installed by `run_onchange_before_10-packages.sh.tmpl` for `desktop`/`laptop` roles. See [chezmoi scripts](/chezmoi/scripts).
- `.chezmoiscripts/run_once_after_30-user-services.sh.tmpl` runs once on Linux. If `~/.config/systemd/user/nirinit.service` exists, it runs `systemctl --user daemon-reload` and `systemctl --user enable nirinit.service`. The script's comment says other personal units (brain-lens, omp-loop, rclone, hermes, voxtype) ship as files only and are enabled by hand. The repo does not currently contain `dot_config/systemd`, so the unit must come from elsewhere for the enable step to run.
- Tools the config calls but does not install: `vicinae`, `cliphist`/`wl-paste`, `polkit-gnome`, `kbuildsycoca6`, `voxtype`, `niri-dynamic-workspaces`, `dot-screenshot`, `niri-smart-close`, `niri-smart-move`, and the `capitaine-cursors-light` cursor theme. `niri-smart-close`, `niri-smart-move`, and `dot-screenshot` are called by path or name but are not in this repo.

## Load order

`config.kdl` sets two top-level options and then includes the fragments in numeric order:

```kdl ~/.config/niri/config.kdl
hotkey-overlay {
    skip-at-startup
}

screenshot-path "~/Pictures/Screenshots/Screenshot from %Y-%m-%d %H-%M-%S.png"

include "config.d/10-input-and-cursor.kdl"
// … 20 through 90 …
include "config.d/90-outputs.kdl"
```

After the includes, `config.kdl` repeats inline `input`, `layout`, `gestures`, `cursor`, `overview`, `window-rule`, `animations`, and `environment` blocks, a leftover of the iNiR (illogical-impulse) base it started from. It then sets `debug { honor-xdg-activation-with-invalid-serial }`, which lets Noctalia v5 activate windows from notification actions. It includes `./noctalia.kdl` and ends with `prefer-no-csd`. niri merges repeated sections and later values win, so the inline blocks override the fragments where they differ. Those differences are listed in each section below.

## Input and cursor

`config.d/10-input-and-cursor.kdl`:

| Setting | Value |
| --- | --- |
| Keyboard layout | `us` |
| Repeat delay / rate | 400 ms / 50 Hz |
| Touchpad | `natural-scroll`, `tap`, `tap-button-map "left-right-middle"`, `accel-speed 1.0` |
| Tablet and touch | `map-to-output "eDP-1"` (pen and touchscreen stay on the laptop panel) |
| Mod key | `Super`; `Alt` when nested inside another compositor |
| Cursor | `capitaine-cursors-light`, size 24, `hide-when-typing` |

The inline `input` block in `config.kdl` repeats the same keyboard and touchpad values without `accel-speed` or the tablet/touch mappings, so those two settings from the fragment still apply.

## Layout and overview

`config.d/20-layout-and-overview.kdl`:

| Setting | Value |
| --- | --- |
| `center-focused-column` | `never` |
| `background-color` | `transparent` (the Noctalia wallpaper shows through as backdrop) |
| `default-column-width` | proportion 0.5 |
| `preset-column-widths` | 0.33333, 0.5, 0.66667 |
| Focus ring | width 1.5 |
| Border | `off` (width 1.5 if enabled) |
| Shadow | `off` (softness 30, spread 5, offset y=5 if enabled) |
| Tab indicator | `place-within-column`, `hide-when-single-tab`, position `left` |
| Overview | `zoom 0.4`, workspace shadow off |

The `gestures { hot-corners { } }` block is empty in the fragment. The inline block in `config.kdl` sets `hot-corners { off }`, so hot corners are disabled.

### Colors (Noctalia)

`noctalia.kdl` is included last, so its colors override the fragment's placeholder colors (`#7fc8ff` ring, `#ffc87f` border):

| Element | Active | Inactive | Urgent |
| --- | --- | --- | --- |
| Focus ring | `#a398b3` | `#1e1c21` | `#fd4663` |
| Border | `#a398b3` | `#1e1c21` | `#fd4663` |
| Tab indicator | `#a398b3` | `#4a4158` | `#fd4663` |
| Recent-windows highlight | `#a398b3` | — | `#fd4663` |

Shadow color is `#1e1c2170` and the insert hint is `#a398b380`. The palette follows the Noctalia shell's color scheme, not cyberdream.

## Window rules

`config.d/30-window-rules.kdl`:

| Match | Rule |
| --- | --- |
| All windows | `geometry-corner-radius 10`, `clip-to-geometry true`; background blur on, xray off |
| `dev.noctalia.Noctalia` (Noctalia v5 settings) | Floating, 1080 × 920 |
| `Folo`, `Spotify`, `zen`, `brave-origin`, `gemini-desktop`, `claude-desktop` | Opacity 0.9, tiled, blur, no border background, open focused |
| `kitty`, `zed`, `obsidian`, `Beeper`, `chromium`, `com.mitchellh.ghostty` | Opacity 0.9, tiled, blur, no border background, open focused |
| `org.gnome.Nautilus`, `com.gabm.satty` | Floating, focused, width proportion 0.5 |
| Title `Picture-in-Picture` (app `zen`) or `Picture in picture` | Floating, not focused, opacity 1.0, 24 px from the top-right corner, on `eDP-1` |
| `zed`, `obsidian`, `sticky-notest-canvas`, title `Sticky Notes`, `vacuumtube`, `Fladder`, `kitty`, `kitty-herdr`, `kitty-tmux` | Open on the Apple Studio Display |

Global blur settings: `passes 4`, `offset 5.0`, `noise 0.02`, `saturation 2.0`. The inline `window-rule` in `config.kdl` sets `geometry-corner-radius 8` after the fragment, so the effective radius is 8.

## Environment

`config.d/40-environment.kdl` (repeated verbatim inline in `config.kdl`):

| Variable | Value | Purpose |
| --- | --- | --- |
| `XDG_CURRENT_DESKTOP` | `niri` | |
| `XDG_MENU_PREFIX` | `plasma-` | Dolphin/KDE file associations |
| `QT_QPA_PLATFORM` | `wayland` | |
| `ELECTRON_OZONE_PLATFORM_HINT` | `auto` | Native Wayland for Electron |
| `QT_LOGGING_RULES` | `quickshell.dbus.properties=false` | Silence quickshell D-Bus logs |
| `QT_QPA_PLATFORMTHEME` | `kde` | Qt colors from `kdeglobals` (needs `plasma-integration`) |
| `QT_STYLE_OVERRIDE` | `Darkly` | Qt widget style |
| `INIR_VENV`, `ILLOGICAL_IMPULSE_VIRTUAL_ENV` | `$HOME/.local/state/quickshell/.venv` | Quickshell Python venv |

## Startup

`config.d/50-startup.kdl` spawns at login:

| Command | Purpose |
| --- | --- |
| `systemctl --user import-environment XDG_MENU_PREFIX && kbuildsycoca6` | Export the menu prefix to systemd, rebuild the KDE service cache |
| `/usr/lib/polkit-gnome/polkit-gnome-authentication-agent-1` | Polkit agent |
| `noctalia` | Shell: bar, panels, notifications, OSD, wallpaper |
| `niri-dynamic-workspaces daemon` | Named dynamic workspaces |
| `vicinae server` | Launcher backend |
| `wl-paste --watch cliphist store` | Clipboard history |

## Animations

`config.d/60-animations.kdl` makes every animation a critically damped spring (`damping-ratio=1.0`, `epsilon=0.0001`) with high stiffness: workspace-switch 4000, window-open 5000, window-close 6000, horizontal-view-movement 4500, window-movement 5000, window-resize 5500, config-notification-open-close 5000, screenshot-ui-open 5000.

The inline `animations` block in `config.kdl` comes later and overrides these with softer springs tuned to match quickshell's Material motion:

| Animation | Damping ratio | Stiffness |
| --- | --- | --- |
| workspace-switch | 0.78 | 1350 |
| window-open | 0.82 | 1125 |
| window-close | 0.88 | 2025 |
| horizontal-view-movement | 0.80 | 1238 |
| window-movement | 0.85 | 1463 |
| window-resize | 0.88 | 1575 |
| config-notification-open-close | 0.90 | 1800 |
| screenshot-ui-open | 0.85 | 1688 |

## Keybindings

All bindings from `config.d/70-binds.kdl`. `Mod` is `Super` (`Alt` when niri runs nested). `Super+…` and `Mod+…` are the same modifier. The table groups bindings by the file's comment headers.

### System and launcher

| Keys | Action |
| --- | --- |
| `Mod+Tab` | Toggle overview (no repeat) |
| `Mod+Shift+E` | Quit niri |
| `Mod+Escape` | Toggle keyboard-shortcuts inhibit (cannot itself be inhibited) |
| `Mod+Space` | `vicinae toggle` — launcher |
| `Mod+Shift+Space` | vicinae emoji search |
| `Mod+Shift+Return` | vicinae window switcher |
| `Super+Alt+Ctrl+Return` | vicinae window switcher |
| `Mod+BackSpace` | Focus previous window |
| `Mod+P` | vicinae clipboard history |
| `Mod+Ctrl+R` | Toggle the Noctalia `prdlk/ostt:recorder` plugin |
| `Mod+Period` | Noctalia control center panel |
| `Mod+Comma` | Noctalia settings |
| `Mod+Shift+Q` | Noctalia session menu (hotkey-overlay title "Session Menu: noctalia session") |
| `Mod+F24` | `voxtype record toggle --profile hermes` — dictation |

### Screenshots

| Keys | Action |
| --- | --- |
| `Mod+S` | `dot-screenshot region` |
| `Mod+Shift+S` | `dot-screenshot window` |
| `Mod+Ctrl+S` | `dot-screenshot monitor-focused` |
| `Mod+Ctrl+Shift+S` | `dot-screenshot monitor-all` |
| `Print` | niri screenshot UI |
| `Ctrl+Print` | Screenshot the screen |
| `Alt+Print` | Screenshot the window |

### Applications

| Keys | Action |
| --- | --- |
| `Mod+Shift+G` | Goliath: `kitty --class=kitty-goliath --title=goliath --config ~/.config/kitty/goliath.conf` (mosh + tmux; see [kitty](/terminal/kitty)) |
| `Super+Ctrl+A` | Claude desktop (via vicinae) |
| `Super+Ctrl+G` | Gemini desktop (via vicinae) |
| `Super+Alt+Ctrl+H` | `niri-launch-ai` — cycle Claude/Hermes windows |
| `Super+Alt+Ctrl+J` | `niri-launch-terminal` — cycle terminals |
| `Super+Alt+Ctrl+K` | `niri-launch-browser` — cycle Brave Origin |
| `Super+Alt+Ctrl+O` | `niri-launch obsidian` |
| `Super+Alt+Ctrl+Y` | `niri-launch-webapp` — cycle web apps |
| `Super+Alt+Ctrl+W` | tldraw offline (via vicinae) |
| `Super+Alt+Ctrl+E` | Fastmail (via vicinae) |
| `Super+Alt+Ctrl+B` | Beeper (via vicinae) |
| `Super+Alt+Ctrl+M` | Beeper (via vicinae) |
| `Super+Alt+Ctrl+G` | Ghostty (via vicinae) |
| `Super+Alt+Ctrl+L` | Linear web app (via vicinae) |
| `Super+Alt+Ctrl+T` | Todoist (via vicinae) |
| `Super+Alt+Ctrl+N` | Open the Noctalia `noctalia/notes` panel |
| `Super+Alt+Ctrl+F` | Nautilus (via vicinae) |
| `Super+Alt+Ctrl+Space` | Sticky Notes Canvas (via vicinae) |

### Dynamic workspaces

| Keys | Action |
| --- | --- |
| `Super+Shift+Alt+Ctrl+W` | `niri-dynamic-workspaces switch w` |
| `Super+Shift+Alt+Ctrl+H` | `niri-dynamic-workspaces switch h` |
| `Super+Alt+Ctrl+X` | `niri-dynamic-workspaces switch x` |

### Focus and movement

| Keys | Action |
| --- | --- |
| `Mod+H` | Focus column left (wraps to last) |
| `Mod+L` | Focus column right (wraps to first) |
| `Mod+J` | Focus window below, or workspace below |
| `Mod+K` | Focus window above, or workspace above |
| `Mod+Shift+H` | `niri-smart-move left` |
| `Mod+Shift+L` | `niri-smart-move right` |
| `Mod+Shift+J` | Move column to workspace below |
| `Mod+Shift+K` | Move column to workspace above |
| `Super+Ctrl+H` | Focus first column |
| `Super+Ctrl+L` | Focus last column |
| `Super+Ctrl+Shift+H` | Move column to first |
| `Super+Ctrl+Shift+L` | Move column to last |
| `Mod+Q` | `niri-smart-close` (no repeat) |

### Monitors

| Keys | Action |
| --- | --- |
| `Mod+Left` | Focus monitor left |
| `Mod+Up` | Focus monitor up |
| `Mod+Right` | Focus monitor right |
| `Mod+Shift+Down` | Move column to monitor right |
| `Mod+Shift+Up` | Move column to monitor left |
| `Super+O` | Focus next monitor |
| `Super+Ctrl+J` | Focus next monitor |
| `Super+Ctrl+K` | Focus previous monitor |
| `Super+Shift+O` | Move column to next monitor |
| `Mod+Ctrl+Shift+J` | Move column to next monitor |
| `Mod+Ctrl+Shift+K` | Move column to previous monitor |

### Layout and sizing

| Keys | Action |
| --- | --- |
| `Mod+R` | Cycle preset column widths |
| `Mod+Shift+R` | Cycle preset window heights |
| `Mod+F` | Expand column to available width |
| `Mod+Shift+F` | Maximize column |
| `Mod+Ctrl+F` | Fullscreen window |
| `Super+C` | Center column |
| `Mod+Ctrl+C` | Center visible columns |
| `Mod+Ctrl+O` | Toggle window floating |
| `Mod+W` | Toggle tabbed column display |
| `Alt+Shift+H` | Column width −10% |
| `Alt+Shift+L` | Column width +10% |
| `Alt+Shift+J` | Window height −10% |
| `Alt+Shift+K` | Window height +10% |

### Hardware keys

All of these work while the screen is locked (`allow-when-locked=true`) and go through Noctalia IPC, so the Noctalia OSD shows feedback. The commented `v4:` lines in the file are the old `qs -c noctalia-shell ipc call …` forms.

| Keys | Action |
| --- | --- |
| `XF86AudioRaiseVolume` | `noctalia msg volume-up` |
| `XF86AudioLowerVolume` | `noctalia msg volume-down` |
| `XF86AudioMute` | `noctalia msg volume-mute` |
| `XF86AudioMicMute` | `noctalia msg mic-mute` |
| `XF86AudioNext` | `noctalia msg media next` |
| `XF86AudioPrev` | `noctalia msg media previous` |
| `XF86AudioPlay` / `XF86AudioPause` | `noctalia msg media toggle` |
| `XF86MonBrightnessUp` | `noctalia msg brightness-up` |
| `XF86MonBrightnessDown` | `noctalia msg brightness-down` |

## Layer rules

`config.d/80-layer-rules.kdl`:

- Noctalia surfaces matching `^noctalia-(bar-…|notification|dock|panel|attached-panel|osd)$` get `background-effect { xray false }`, so their blur samples what is actually behind them. Noctalia publishes blur regions itself when `ext-background-effects` is available.
- `^noctalia-wallpaper` is placed within the backdrop (`place-within-backdrop true`). The wallpaper stays still while workspaces move and shows in the overview.
- `layout { background-color "transparent" }` and overview `workspace-shadow { off }` are restated here so the backdrop is always visible.

## Outputs and display profiles

`config.d/90-outputs.kdl` turns `DP-5` off and includes the per-display profiles. An older Apple Display block and a `DP-4 off` block remain as comments.

```kdl ~/.config/niri/config.d/90-outputs.kdl
include "91-internal-display.kdl"
// include "92-ultrawide.kdl"
include "93-apple-display.kdl"
include "94-portable-hdmi.kdl"
// include "95-portable-usbc.kdl"
```

The Apple Studio Display also exposes a phantom connector (DP-4/5/6, varies per boot) whose only mode is 640×480. A commented `spawn-at-startup` for `scripts/kill-phantom-display.fish` handles it by mode signature, since connector name and EDID both shift.

**Internal (91)**

`Tianma Microelectronics Ltd. TL134ADXP03 Unknown`, the laptop panel. **Active.**

| Setting | Value |
| --- | --- |
| Position / scale | `0,0` / 1.5 |
| VRR | `on-demand=true` |
| Gaps | 8 |
| Default column width | 1.0 |
| Preset widths | 0.5, 1.0 |
| Centering | `always-center-single-column`, `center-focused-column "never"` |

Named workspaces `1`, `2`, `3` open on this panel. Three window rules open these apps maximized and focused on workspace `1`, without border backgrounds (all three target `"1"` as written):

- `chrome-youtube.com__-Default`, `chromium`
- `org.gnome.Calendar`, `discord`, `slack`, `com-fastmail-fastmail`, `gemini-desktop`, `Beeper`, `com.anthropic.Claude`, `Hermes`, `todoist`
- `WebApp-CloudflarePersonal7594`, `WebApp-CloudflareWalletbase8881`

**Ultrawide (92)**

`Samsung Electric Company S34CG50 HNTY501103`. **Included in the repo but commented out.**

| Setting | Value |
| --- | --- |
| Mode | `3440x1440@100.00` |
| Position / scale | `0,0` / 1 |
| VRR | `on-demand=true` |
| Gaps | 16 |
| Default column width | 0.3333 |
| Preset widths | 0.3333, 0.5, 0.6667 |
| Centering | `always-center-single-column`, `center-focused-column "never"` |

**Apple Display (93)**

`Apple Computer Inc StudioDisplay 0x984262A7`. **Active.**

| Setting | Value |
| --- | --- |
| Scale | 1.75 |
| Gaps | 8 |
| Default column width | 0.5 |
| Preset widths | 0.25, 0.3333, 0.5, 0.6667 |
| Centering | `always-center-single-column`, `center-focused-column "never"` |

**Portable HDMI (94)**

`PNP(AOP) 16PM1QB 14490001D3X00`. **Active.**

| Setting | Value |
| --- | --- |
| Transform | `90` (portrait) |
| Gaps | 2 |
| Default column width | 1.0 |
| Preset widths | 0.5, 1.0 |
| Centering | `always-center-single-column`, `center-focused-column "never"` |

**Portable USB-C (95)**

`ASUSTek COMPUTER INC ASUS MB16AC M8LMTF195749`. **Included in the repo but commented out.**

| Setting | Value |
| --- | --- |
| Position | `0,0` |
| Gaps | 2 |
| Default column width | 1.0 |
| Preset widths | 0.5, 1.0 |
| Centering | `always-center-single-column`, `center-focused-column "never"` |

To switch a profile, uncomment or comment its `include` line in `90-outputs.kdl`. niri reloads config on save.

## Scripts

Deployed to `~/.config/niri/scripts/`. `70-binds.kdl` binds none of them, so they run by hand or from other tools.

| Script | Usage | Behavior |
| --- | --- | --- |
| `kill-phantom-display.fish` | no args | Polls `niri msg -j outputs` up to 10 times, 1 s apart. Any output whose only mode is 640×480 gets `niri msg output <name> off`. Does not handle mid-session hotplug. |
| `ndw-route-workspace.sh` | `$NDW_WORKSPACE_KEY` or `$1` | niri-dynamic-workspaces `on_create` hook. Keys `y g m x z p l a` move the workspace to `HDMI-A-1`. Keys `b f s w c d o` move it to the Apple Studio Display (resolved by make/model through `niri msg --json outputs`), or to `HDMI-A-1` if that display is absent. Other keys exit silently. |
| `niri-floating-sidebar.sh` | `toggle` \| `hide` \| `flip` \| `reorder` \| `help` | Per-workspace floating sidebar on the right edge. Windows are 550 px wide and stack into 4 slots (more as needed) with 15 px gaps and 30/50/20 px top/bottom/side margins. `toggle` floats the focused window into the sidebar or restores it to tiling at its original size. `hide` slides the sidebar off-screen, leaving a 70 px sliver. `flip` switches bottom-up and top-down stacking. State is kept in `/tmp/niri.toggle.<workspace>.*`. |
| `niri-focus.sh` | `up` \| `down` | Cascading focus: next window in the column, then the next *occupied* workspace (skipping the trailing empty one), then the adjacent monitor. Wraps to the far-end monitor at the edge. |
| `niri-move.sh` | `up` \| `down` | Cascading move: reorder within the column, then move to the next workspace (including the empty trailing one), then the adjacent monitor. Wraps at the edge. |

## Launchers

### niri-launch

`~/.local/bin/niri-launch <app>` focuses an app's window, cycles through its windows on repeated presses (sorted by window id), or launches it when none is open.

| Argument | Windows matched (app-id) | Launched when none |
| --- | --- | --- |
| `webapp` | `^(webapp-\|chrome-)` (Firefox and Chromium/Brave site-specific apps) | Re-runs as `webapp-youtube` |
| `webapp-NAME` | `WebApp-Name` (case-insensitive) | `gtk-launch` on `~/.local/share/applications/WebApp-Name*.desktop` |
| `kitty` | any app-id starting with `kitty` | `kitty` |
| `kitty-CLASS` | exactly that class | `kitty --class kitty-CLASS` (optional command from the empty `KITTY_CMDS` map) |
| `ghostty` | `ghostty` | nothing, prints a message |
| `browser` | `^(zen\|brave\|chromium\|firefox)` | `zen` |
| `terminal` | `kitty\|alacritty\|ghostty` | `kitty` |
| `ai` | `claude\|hermes` | `hermes desktop` |
| anything else | exact app-id | the argument as a command |

### Wrappers

Each wrapper resolves its own symlink and execs `niri-launch` from the same directory:

| Script | Runs | Bound to |
| --- | --- | --- |
| `niri-launch-ai` | `niri-launch ai` | `Super+Alt+Ctrl+H` |
| `niri-launch-terminal` | `niri-launch terminal` | `Super+Alt+Ctrl+J` |
| `niri-launch-browser` | `niri-launch brave-origin` (exact app-id, not the `browser` mode) | `Super+Alt+Ctrl+K` |
| `niri-launch-webapp` | `niri-launch webapp` | `Super+Alt+Ctrl+Y` |

### cp-appid

`cp-appid` lists every `.desktop` file (via `fd`) in `/usr/share/applications`, `/usr/local/share/applications`, `~/.local/share/applications`, the system and user Flatpak export dirs, and `/var/lib/snapd/desktop/applications`. It shows each entry's `Name=` and ID in `fzf`, then copies the chosen ID to the clipboard with `wl-copy`. Use it to find app-ids for window rules and `niri-launch`.

## Related

- [Hyprland](/desktop/hyprland): the Omarchy setup with the same key layout.
- [kitty](/terminal/kitty): `kitty-goliath`, `kitty-herdr`, and `kitty-tmux` classes referenced by the rules.
