Files
dotfiles/docs/desktop/niri.mdx
T

459 lines
22 KiB
Plaintext
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
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.
<FileTree>
- ~/.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
</FileTree>
## 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.
<Tabs>
<Tab title="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`
</Tab>
<Tab title="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"` |
</Tab>
<Tab title="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"` |
</Tab>
<Tab title="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"` |
</Tab>
<Tab title="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"` |
</Tab>
</Tabs>
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.