docs: add comprehensive documentation for AI agents, chezmoi workflow, shell setup, desktop configuration, terminal multiplexers, and developer tools

This commit is contained in:
Prad Nukala committed 2026-09-28 11:08:17 -04:00
1 parent 41437d4abd
commit 1da988c876
47 files changed
+5485

No files matched your search

+101
View File
@@ -0,0 +1,101 @@
---
title: Fonts and default apps
description: Fontconfig monospace override to Victor Mono Nerd Font, the Nerd Font packages, and the XDG default-application table.
---
Two small files set system-wide defaults: `fonts.conf` picks the monospace font, and `mimeapps.list` picks which application opens each file type and URL scheme.
:::note
Linux `desktop` and `laptop` roles only. `.chezmoiignore.tmpl` skips `~/.config/fontconfig/**` and `~/.config/mimeapps.list` on other roles.
:::
## Fontconfig
| Source (repo) | Target |
| --- | --- |
| `dot_config/fontconfig/fonts.conf` | `~/.config/fontconfig/fonts.conf` |
```xml ~/.config/fontconfig/fonts.conf
<fontconfig>
<match target="pattern">
<test name="family" qual="any">
<string>monospace</string>
</test>
<edit name="family" mode="prepend_first" binding="strong">
<string>VictorMono Nerd Font</string>
</edit>
</match>
</fontconfig>
```
Any font request that includes the generic `monospace` family gets `VictorMono Nerd Font` added to the front of its family list (`prepend_first`) with strong binding. Apps that ask for "monospace" therefore render in Victor Mono with Nerd Font glyphs, and fall back through fontconfig's normal monospace list if a glyph is missing. The file does not configure sans/serif families, hinting, antialiasing, or subpixel rendering. Those stay at system defaults.
Some tools set their own fonts. Waybar uses `CaskaydiaCove Nerd Font Propo` (see [Waybar](/desktop/waybar)), and the terminals set fonts in their own configs (see [kitty](/terminal/kitty)).
### Font packages
Victor Mono Nerd Font is installed on every role by `run_onchange_before_10-packages.sh.tmpl` from `.chezmoidata/packages.yaml`, not only on desktop roles:
| Platform | Package | Source |
| --- | --- | --- |
| Arch Linux | `ttf-victor-mono-nerd` | pacman (`packages.arch.pacman`) |
| macOS | `font-victor-mono-nerd-font` | Homebrew cask (`packages.darwin.cask`) |
`fonts.conf` is Linux desktop only, but the font package is also installed on `server` and `mac`.
## Default applications
| Source (repo) | Target |
| --- | --- |
| `dot_config/mimeapps.list` | `~/.config/mimeapps.list` |
`mimeapps.list` is the XDG file that `xdg-open`, GTK/Qt file dialogs, and browsers use to choose a handler. `[Default Applications]` sets the preferred handler. `[Added Associations]` adds entries to "Open with" lists.
### Default applications
| Group | MIME types / schemes | Handler (`.desktop`) |
| --- | --- | --- |
| Text and scripts | `text/plain`, `text/x-shellscript`, `text/x-python`, `text/x-script.python` | `vim.desktop` |
| Web pages | `text/html`, `application/xhtml+xml`, `application/x-extension-htm`, `-html`, `-shtml`, `-xhtml`, `-xht` | `brave-origin-nightly.desktop` |
| Web links | `x-scheme-handler/http`, `https`, `chrome` | `brave-origin-nightly.desktop` |
| Browser internals | `x-scheme-handler/about`, `unknown` | `chromium.desktop` |
| PDF | `application/pdf` | `com.brave.Origin.nightly.desktop` |
| Directories | `inode/directory` | `com.thisisgm.flea.desktop` (flea) |
| Mail | `x-scheme-handler/mailto` | `omamail.desktop` |
| Windows executables | `application/vnd.microsoft.portable-executable` | `wine.desktop` |
| AI tools | `x-scheme-handler/claude` | `com.anthropic.Claude.desktop` |
| | `x-scheme-handler/claude-cli` | `claude-code-url-handler.desktop` |
| | `x-scheme-handler/opencode` | `ai.opencode.desktop.desktop` |
| | `x-scheme-handler/antigravity` | `antigravity.desktop` |
| | `x-scheme-handler/piebald` | `piebald-handler.desktop` |
| | `x-scheme-handler/openhuman` | `OpenHuman-handler.desktop` |
| Productivity | `x-scheme-handler/todoist`, `com.todoist` | `todoist.desktop` |
| | `x-scheme-handler/notion` | `notion.desktop` |
| | `x-scheme-handler/anytype` | `anytype.desktop` |
| | `x-scheme-handler/tolaria` | `tolaria-handler.desktop` |
| | `x-scheme-handler/tldraw-offline` | `tldraw-offline.desktop` |
| | `x-scheme-handler/figma` | `figma-linux.desktop` |
| Chat | `x-scheme-handler/slack` | `slack.desktop` |
| | `x-scheme-handler/discord-647066573147996160` | `discord-647066573147996161.desktop` |
| Feeds | `x-scheme-handler/follow`, `folo` | `folo.desktop` |
### Added associations
| MIME types / schemes | Also offered |
| --- | --- |
| `http`, `https`, `chrome` schemes; `text/html`, `application/xhtml+xml`, and the `x-extension-*` web types | `brave-origin-nightly.desktop`, `userapp-Floorp-8MTFP3.desktop` |
| `x-scheme-handler/mailto` | `userapp-Evolution-PCEYQ4.desktop` |
| `application/vnd.microsoft.portable-executable` | `wine.desktop` |
| `application/pdf` | `com.brave.Origin.nightly.desktop` |
| `x-scheme-handler/opencode` | `ai.opencode.desktop.desktop` |
The added mail association is Evolution, but the default mail handler is `omamail.desktop`.
:::tip
In symlink mode, `~/.config/mimeapps.list` is a symlink into the repo. When `xdg-mime default` or a "set as default" dialog updates it, the tool either writes through the symlink and edits the repo file directly, or replaces the symlink with a regular file. Check `git status` or `chezmoi status` after changing defaults.
:::
## Related
- [niri](/desktop/niri) and [Hyprland](/desktop/hyprland): the compositors these defaults serve.
- [Installation](/installation): role selection that decides whether this layer applies.
+356
View File
@@ -0,0 +1,356 @@
---
title: Hyprland
description: Hyprland Lua config layered on Omarchy — bindings ported from niri, master layout, terminal-only blur, monitors, and the omarchy-* helper scripts.
---
[Hyprland](https://hypr.land) is a dynamic tiling Wayland compositor. This config uses Hyprland's Lua configuration and sits on top of [Omarchy](https://omarchy.org), which provides the defaults, theme, menus, and `omarchy-*` commands. The files here only override. Most bindings are ported from the [niri config](/desktop/niri) so the same keys do the same jobs.
:::note
`~/.config/hypr` and the `omarchy-*` scripts are **not** listed in `.chezmoiignore.tmpl`. chezmoi applies them on every role and OS, including `mac` and `server`. They only take effect where Hyprland and Omarchy are installed.
:::
## Files
| Source (repo) | Target | Purpose |
| --- | --- | --- |
| `dot_config/hypr/hyprland.lua` | `~/.config/hypr/hyprland.lua` | Entry point, workspace and window rules |
| `dot_config/hypr/autostart.lua` | `~/.config/hypr/autostart.lua` | Extra autostart (empty) |
| `dot_config/hypr/bindings.lua` | `~/.config/hypr/bindings.lua` | Keybindings |
| `dot_config/hypr/input.lua` | `~/.config/hypr/input.lua` | Key repeat, tablet/touch mapping |
| `dot_config/hypr/looknfeel.lua` | `~/.config/hypr/looknfeel.lua` | Layout, decoration, blur |
| `dot_config/hypr/monitors.lua` | `~/.config/hypr/monitors.lua` | Monitor modes and positions |
| `dot_config/hypr/hyprsunset.conf` | `~/.config/hypr/hyprsunset.conf` | Night-light profile |
| `dot_config/hypr/xdph.conf` | `~/.config/hypr/xdph.conf` | xdg-desktop-portal-hyprland |
| `dot_config/hypr/dot_luarc.json` | `~/.config/hypr/.luarc.json` | Lua language server settings |
| `dot_local/bin/executable_omarchy-*` | `~/.local/bin/omarchy-*` | Helper scripts (see [below](#omarchy-scripts)) |
None are templates. Hyprland and Omarchy are not in `.chezmoidata/packages.yaml`. They come from the Omarchy install.
## Entry point
`hyprland.lua` loads Omarchy, then the personal files, then Omarchy's toggles:
```lua ~/.config/hypr/hyprland.lua
dofile((os.getenv("OMARCHY_PATH") or "/usr/share/omarchy") .. "/default/hypr/bootstrap.lua")
require("default.hypr.omarchy")
require("hypr.monitors")
require("hypr.input")
require("hypr.bindings")
require("hypr.looknfeel")
require("hypr.autostart")
require("default.hypr.toggles")
```
Omarchy's default bindings stay enabled. `omarchy_default_bindings` and `omarchy_preinstalled_bindings` are left commented. `bindings.lua` unbinds each default it replaces.
### Workspace rules
| Workspace | Monitor |
| --- | --- |
| 3 | `desc:Apple Computer Inc StudioDisplay 0x984262A7` (EDID match, survives connector changes) |
| 4 | `eDP-1` |
| 5 | `eDP-1` |
### Window rules
| Class | Rule |
| --- | --- |
| `^brave-www\.tldraw\.com__-Default$` | Workspace 3 |
| `^tldraw-offline$` | Workspace 5, fullscreen |
| `^com\.anthropic\.Claude$`, `^kitty-herdr$` | `special:scratchpad silent` |
| `^com\.fastmail\.Fastmail$` | `special:fastmail silent` |
| `^Beeper$` | `special:beeper silent` |
| `brave-(www\.)?youtube\.com__.*`, title not matching `.*Picture.?in.?[Pp]icture.*` | Float, pin, size 30% × 30% of the monitor, move to `10, monitor_h*0.7-10` (bottom-left corner) |
Scratch residents are placed `silent` so that [`omarchy-toggle-scratch`](#omarchy-toggle-scratch) decides when to reveal them. The YouTube rule matches both `youtube.com` and `www.youtube.com` because Chromium's class changes once a video plays. It excludes the picture-in-picture overlay so Omarchy's own PiP rule still applies. Its size and inset must stay in sync with [`omarchy-pip`](#omarchy-pip).
## Input
`input.lua`:
- `repeat_delay = 250` (Omarchy's value), `repeat_rate = 50` (raised from 40). The file's comments record that 200/50 felt too fast. The typing lag it was meant to fix came from the keyboard's firmware tap-hold handling and was fixed on the keyboard itself.
- Tablet and touch devices are pinned to one output each:
| Device | Output | Extra |
| --- | --- | --- |
| `elan9008:00-04f3:43c7-stylus` (Flow Z13 pen) | `eDP-1` | |
| `elan9008:00-04f3:43c7` (Flow Z13 touchscreen) | `eDP-1` | Taps land on the panel instead of the focused monitor |
| `wacom-intuos-bt-s-pen` | `desc:Apple Computer Inc StudioDisplay 0x984262A7` | `active_area_size = { 152, 85.5 }`, `active_area_position = { 0, 0 }` — a 16:9 band cropped from the 16:10 tablet |
The layout, touchpad, and gesture examples in the file are commented out. Palm rejection is done by a libinput quirks file in `/etc`, which chezmoi does not manage.
## Look and feel
`looknfeel.lua` loads after the Omarchy theme's `hyprland.lua`, so its values override the theme's. The theme sets colors. This file sets everything else. The approach is flat windows by default, with blur only behind terminals.
| Setting | Value |
| --- | --- |
| `general.layout` | `master` (instead of Omarchy's dwindle; per-workspace layouts saved by `omarchy-hyprland-workspace-layout-toggle` still win) |
| `decoration.rounding` | 0 |
| `decoration.blur` | enabled; `size 5`, `passes 2`, `brightness 0.8`, `contrast 1.1`, `vibrancy 0.15`, `noise 0.01`, `new_optimizations`, `popups = false` |
| `decoration.shadow` | disabled |
| `group.groupbar.gradient_rounding` | 0 |
| Workspace animation | disabled (`hl.animation({ leaf = "workspaces", enabled = false })`) |
Blur must be enabled globally before any window can use it, so every window tagged `default-opacity` gets `no_blur = true`, and terminals get it back:
```lua ~/.config/hypr/looknfeel.lua
o.window({ tag = "default-opacity" }, { no_blur = true })
o.window(
"^(kitty(-.*)?|Alacritty|com\\.mitchellh\\.ghostty|foot|org\\.codeberg\\.dnkl\\.foot|wezterm)$",
{ no_blur = false }
)
```
Omarchy's stock fade (0.985 active / 0.96 inactive) is kept. Terminals get their transparency from kitty's `background_opacity 0.9` (see [kitty](/terminal/kitty)). The comments give the reason for limiting blur: the cyberdream and Spectra themes enabled rounding, blur, and shadow on every window, and that costs frame time on an iGPU driving the laptop panel plus a 5K display.
## Monitors
`monitors.lua`:
| Output | Mode | Position | Scale |
| --- | --- | --- | --- |
| any (fallback) | `preferred` | `auto` | 2 |
| `DP-4` (Apple Studio Display) | `5120x2880@60` | `0x0` | 2 |
| `eDP-1` (laptop) | `2560x1600@180` | `2560x0` | 2 |
`GDK_SCALE=2` is exported. The Studio Display sits left of the laptop panel. At scale 2 its logical width is 2560, so `eDP-1` starts at x = 2560.
## hyprsunset and portal
```ini ~/.config/hypr/hyprsunset.conf
profile {
time = 07:00
identity = true
}
```
The identity profile stops hyprsunset from tinting the screen. A commented 20:00 / 4000 K night profile and an `o.launch_on_start("hyprsunset")` hint remain in the file.
```ini ~/.config/hypr/xdph.conf
screencopy {
allow_token_by_default = true
custom_picker_binary = hyprland-preview-share-picker
}
```
Screen-share requests get a restore token by default and use `hyprland-preview-share-picker` as the source picker.
## .luarc.json
Configures lua-language-server for the Lua config: the library path is `/usr/share/hypr/stubs`, `checkThirdParty` is off, and `hl` and `o` are declared as globals.
`autostart.lua` contains only a commented `o.launch_on_start` example.
## Keybindings
Every binding defined in `bindings.lua`. Bindings not listed here are Omarchy defaults. The *Replaces* column gives the Omarchy default that was unbound first.
### Windows
| Keys | Action | Replaces |
| --- | --- | --- |
| `SUPER + Q` | Close window | same (made explicit) |
| `SUPER + W` | `omarchy-toggle-layout` — flip workspace layout | close window |
| `SUPER + BACKSPACE` | Focus last window | toggle transparency |
| `SUPER + TAB` | Next window in the active workspace | next workspace |
| `SUPER + SHIFT + TAB` | Previous window in the active workspace | previous workspace |
| `SUPER + H` / `J` / `K` / `L` | Focus left / down / up / right | `J`: toggle split, `K`: keybindings menu, `L`: toggle layout |
| `SUPER + D` | Toggle split (dwindle `togglesplit`) | — |
| `SUPER + SHIFT + H` | `omarchy-smart-move l` — move/merge left; floating: bottom-left corner | — |
| `SUPER + SHIFT + J` | `omarchy-smart-move d` — move/merge down; floating: top-left | — |
| `SUPER + SHIFT + K` | `omarchy-smart-move u` — move/merge up; floating: top-right | — |
| `SUPER + SHIFT + L` | `omarchy-smart-move r` — move/merge right; floating: bottom-right | — |
| `SUPER + SHIFT + T` | `omarchy-pip` — toggle picture-in-picture | — |
`SUPER + TAB` walks a ring built only from the active workspace's mapped windows, ordered by `stable_id`. Grouped windows stay in the ring and hidden ones are skipped. It raises the focused window afterwards. It avoids `cyclenext`, which would pull in a hidden scratchpad. `binds.movefocus_cycles_groupfirst = true` makes `hjkl` focus step through a group's tabs before leaving the group.
### Workspaces and monitors
| Keys | Action | Replaces |
| --- | --- | --- |
| `SUPER + CTRL + J` | Next workspace (`e+1`) | — |
| `SUPER + CTRL + K` | Previous workspace (`e-1`) | Herdr keybindings |
| `ALT + Q` / `W` / `E` / `R` / `T` | Switch to workspace 1 / 2 / 3 / 4 / 5 | — |
| `ALT + SHIFT + Q` / `W` / `E` / `R` / `T` | Move window to workspace 1 / 2 / 3 / 4 / 5 | — |
| `SUPER + CTRL + S` | `omarchy-toggle-scratch scratchpad` | share menu |
| `SUPER + A` | `omarchy-toggle-scratch scratchpad` | — |
| `SUPER + O` | Focus next monitor | pop window out |
| `SUPER + SHIFT + O` | Move window to next monitor | Obsidian |
| `SUPER + CTRL + SHIFT + J` | Move window to next monitor | — |
| `SUPER + CTRL + SHIFT + K` | Move window to previous monitor | — |
| `SUPER + SHIFT + CTRL + ALT + W` | Workspace `name:w` | — |
| `SUPER + SHIFT + CTRL + ALT + H` | Workspace `name:h` | — |
| `SUPER + CTRL + ALT + X` | Workspace `name:x` | — |
Named workspaces replace niri-dynamic-workspaces. `SUPER + ALT + S` (send to scratchpad) keeps its Omarchy default.
### Sizing
| Keys | Action |
| --- | --- |
| `SUPER + R` | `omarchy-cycle-window-size` — next preset size |
| `SUPER + SHIFT + R` | `omarchy-cycle-window-size prev` |
| `ALT + H` | Width −100 px |
| `ALT + L` | Width +100 px |
| `ALT + K` | Height −100 px |
| `ALT + J` | Height +100 px |
| `SUPER + ALT + C` | Fit active column (scrolling layout only) |
| `SUPER + SHIFT + ALT + C` | Fit visible columns (scrolling layout only) |
### Applications
| Keys | Action | Replaces |
| --- | --- | --- |
| `SUPER + CTRL + A` | Claude: `omarchy-launch-or-focus claude "gtk-launch com.anthropic.Claude"` | audio panel |
| `SUPER + CTRL + G` | Gemini: `omarchy-launch-or-focus gemini "gtk-launch gemini-desktop"` | — |
| `SUPER + CTRL + ALT + H` | `omarchy-cycle ai` | — |
| `SUPER + CTRL + ALT + J` | `omarchy-cycle-terminal` | — |
| `SUPER + CTRL + ALT + K` | `omarchy-cycle-browser` | — |
| `SUPER + CTRL + ALT + G` | `omarchy-cycle ghostty` | — |
| `SUPER + CTRL + ALT + O` | `omarchy-cycle obsidian` | — |
| `SUPER + CTRL + ALT + F` | Nautilus: `omarchy-launch-or-focus nautilus "gtk-launch org.gnome.Nautilus"` | — |
| `SUPER + CTRL + ALT + E` | `omarchy-toggle-scratch fastmail` | — |
| `SUPER + CTRL + ALT + M` | `omarchy-toggle-scratch beeper` | — |
| `SUPER + CTRL + ALT + Y` | `omarchy-summon youtube` | web apps cycle |
| `SUPER + SHIFT + Y` | `omarchy-summon youtube` | YouTube desktop entry |
| `SUPER + CTRL + ALT + T` | tldraw offline: `omarchy-launch-or-focus tldraw-offline "gtk-launch tldraw-offline"` | show time |
| `SUPER + CTRL + ALT + W` | tldraw: `omarchy-launch-or-focus tldraw "gtk-launch TLDraw"` | toggle weather |
| `SUPER + CTRL + ALT + SPACE` | Sticky Notes Canvas via `gtk-launch io.github.faridjaff.StickyNotesCanvas` | — |
| `SUPER + SHIFT + G` | Goliath: `kitty --class=kitty-goliath --title=goliath --config ~/.config/kitty/goliath.conf` | Signal |
| `SUPER + SHIFT + F` | `flea --gui` | unbound first (default unnamed) |
| `SUPER + ALT + SHIFT + F` | `flea --gui "$(omarchy-cmd-terminal-cwd)"` — file manager in the terminal's cwd | unbound first (default unnamed) |
The two `flea` bindings and a rule tagging `com.thisisgm.flea.picker` as `+floating-window` sit between `flea --default` / `flea --picker` marker comments. The `flea` tool writes and removes those blocks itself.
### Screenshots, session, menus
| Keys | Action | Replaces |
| --- | --- | --- |
| `SUPER + S` | `omarchy-capture-screenshot region` | toggle scratchpad |
| `SUPER + SHIFT + S` | `omarchy-capture-screenshot windows` | Google Maps |
| `SUPER + SHIFT + ALT + S` | `omarchy-capture-screenshot fullscreen` | — |
| `SUPER + SHIFT + E` | `omarchy-menu toggle system` | Email |
| `SUPER + SHIFT + Q` | `omarchy-menu toggle system` | — |
| `SUPER + F24` | `voxtype record toggle --profile hermes` — dictation | — |
| `SUPER + SHIFT + SPACE` | Emoji picker (`omarchy-shell shell toggle omarchy.emojis`) | toggle top bar |
| `SUPER + P` | Clipboard manager (`omarchy-shell shell toggle omarchy.clipboard`) | pseudo window |
| `ALT + TAB` | ScrollOverview `toggle all` (only when the plugin is loaded) | Omarchy's two `ALT + TAB` binds |
`SUPER + SPACE` (Omarchy menu), `SUPER + ALT + SPACE` (apps menu), and `SUPER + SHIFT + RETURN` (browser) keep their Omarchy defaults.
### ScrollOverview plugin
When `hl.plugin.scrolloverview` is present, `bindings.lua` configures it:
| Option | Value |
| --- | --- |
| `gesture_distance` | 300 |
| `scale` | 0.5 |
| `workspace_gap` | 100 |
| `layout` | `vertical` |
| `wallpaper` | 2 (global and per-workspace) |
| `blur` | true |
| `shadow` | enabled, range 50 |
## omarchy scripts
The scripts live in `dot_local/bin/` as `executable_omarchy-*` and deploy to `~/.local/bin/`. Each one calls the Lua dispatcher first (`hyprctl dispatch "hl.dsp…"`) and falls back to the classic dispatcher name on older Hyprland.
### omarchy-cycle
```sh
omarchy-cycle <app|browser|brave|terminal|ai|ghostty|kitty|kitty-CLASS|webapp|webapp-NAME>
```
Focuses an app's window, cycles to the next one on repeated presses, or launches the app when none is open. Windows are matched on `class` or `initialClass` (case-insensitive) and sorted by workspace, then address. If the focused window is not in the set, focus goes to the first one. Launches use `setsid uwsm-app --`.
| Argument | Matches | Launches when none |
| --- | --- | --- |
| `webapp` | `^(webapp-\|chrome-)` | re-runs as `webapp-youtube` |
| `webapp-NAME` | `webapp-NAME` or `^chrome-.*NAME` | `gtk-launch` on the first `WebApp-NAME*.desktop` or `*NAME*.desktop` in `~/.local/share/applications` |
| `kitty` | `^kitty` | `kitty` |
| `kitty-CLASS` | `^kitty-CLASS$` | `kitty --class kitty-CLASS` (command from the empty `KITTY_CMDS` map, if set) |
| `ghostty` | `ghostty` | `ghostty` |
| `browser` | zen, brave, chromium, google-chrome, firefox, librewolf, vivaldi, microsoft-edge, helium — excluding `chrome-`/`webapp-` web apps | `omarchy-launch-browser` |
| `brave` | `^brave` (Brave Origin Nightly and its web apps) | `brave-origin-nightly` |
| `terminal` | kitty, alacritty, ghostty, foot, wezterm — excluding `^kitty-herdr$` (the scratchpad terminal) | `omarchy-launch-terminal` |
| `ai` | `claude\|hermes` | `hermes desktop` |
| anything else | `^APP$` | the argument as a command |
Bound to `SUPER + CTRL + ALT + H` (`ai`), `G` (`ghostty`), and `O` (`obsidian`).
### omarchy-cycle-browser / omarchy-cycle-terminal
One-line wrappers: `omarchy-cycle brave` and `omarchy-cycle terminal`. Bound to `SUPER + CTRL + ALT + K` and `SUPER + CTRL + ALT + J`.
### omarchy-cycle-window-size
```sh
omarchy-cycle-window-size [next|prev]
```
Steps the focused window through preset widths `0.333, 0.5, 0.667` of the monitor.
- **Scrolling layout (tiled):** sends `colresize +conf` / `-conf`, so the layout's own preset list is used.
- **Floating:** resizes to the fraction × monitor width and 0.8 × monitor height, then centers. The index is stored per window.
- **Dwindle/master (tiled):** sets `splitratio` to `2 × fraction`, mirrored when the window is on the right or bottom half, clamped to 0.1–1.9. The index is stored per workspace.
Indices are stored in `$XDG_STATE_HOME/omarchy/window-sizes/`. Bound to `SUPER + R` and `SUPER + SHIFT + R`.
### omarchy-pip
```sh
omarchy-pip [toggle|on] [address:0x...]
```
Picture-in-picture for any window. Floats and pins it, resizes it to 30% of the focused monitor on both axes, and places it in the bottom-left corner with a 10 px inset (matching `general:gaps_out`). `toggle` (the default) unpins and re-tiles a window that is already floating and pinned. `on` always applies the fit. Defaults to the active window. The dispatcher is called with `on`/`off` because `set` toggles on Hyprland 0.56. Bound to `SUPER + SHIFT + T`. `omarchy-summon` also calls it.
### omarchy-smart-move
```sh
omarchy-smart-move <l|r|u|d>
```
- **Tiled:** finds the nearest tiled, visible window in that direction that overlaps on the other axis. If that neighbor, or the focused window, is in a group, it runs a group-aware move (`movewindoworgroup`), which merges into or leaves a group. Otherwise it swaps with the neighbor.
- **Floating:** moves the window to a corner of its monitor without resizing, inside the reserved area (the bar) and inset 10 px. Corners go clockwise from bottom-left: `l` bottom-left, `d` top-left, `u` top-right, `r` bottom-right.
Bound to `SUPER + SHIFT + H/J/K/L`.
### omarchy-summon
```sh
omarchy-summon [youtube]
```
The only profile is `youtube`, which matches class `^brave-(www\.)?youtube\.com__.*$` and skips Picture-in-Picture titles. With no window open, it runs `gtk-launch YouTube`, and the window rule in `hyprland.lua` floats and positions it. With a window open, it moves the window to the focused monitor, runs `omarchy-pip on` to refit it (the two panels differ in logical size), and focuses it. Bound to `SUPER + CTRL + ALT + Y` and `SUPER + SHIFT + Y`.
### omarchy-toggle-layout
```sh
omarchy-toggle-layout [width]
```
Runs `omarchy-hyprland-workspace-layout-toggle`, then polls the workspace layout (6 × 50 ms). If it became `scrolling`, it runs `colresize all <width>` (default `0.75`). Dwindle needs no widths. Bound to `SUPER + W`.
### omarchy-toggle-scratch
```sh
omarchy-toggle-scratch [scratchpad|fastmail|beeper]
```
| Profile | Special workspace | Home monitor | Residents (class → launch) |
| --- | --- | --- | --- |
| `scratchpad` | `special:scratchpad` | Apple Studio Display | `com.anthropic.Claude` → `claude-desktop`; `kitty-herdr` → `kitty --class=kitty-herdr --config ~/.config/kitty/herdr.conf` |
| `fastmail` | `special:fastmail` | focused | `com.fastmail.Fastmail` → `gtk-launch fastmail` |
| `beeper` | `special:beeper` | focused | `Beeper` → `gtk-launch beeper` |
If the workspace is visible, the script hides it. If not, it launches any resident with no window there, waits up to 8 s for the first new window, and reveals the workspace on the home monitor (or the focused one if the home monitor is absent). Launching only missing residents avoids duplicate web-app windows. Bound to `SUPER + CTRL + S`, `SUPER + A`, `SUPER + CTRL + ALT + E`, and `SUPER + CTRL + ALT + M`.
## Related
- [niri](/desktop/niri): the original config these bindings mirror.
- [Waybar](/desktop/waybar): the bar used with this setup.
- [kitty](/terminal/kitty): the `herdr` and `goliath` kitty configs launched here.
+32
View File
@@ -0,0 +1,32 @@
---
title: Desktop
description: The Linux desktop layer — niri and Hyprland compositors, the Waybar bar, fontconfig, and default applications.
sidebar:
label: Overview
---
:::note[Role gating]
`.chezmoiignore.tmpl` skips the niri layer unless the machine runs Linux **and** its role is `desktop` or `laptop`. The gated targets are `~/.config/niri`, `~/.config/noctalia`, `~/.config/niri-dynamic-workspaces`, `~/.config/fontconfig`, `~/.config/mimeapps.list`, `~/.config/systemd`, `~/.local/bin/niri-launch`, `~/.local/bin/cp-appid`, `~/.config/fish/auto-Niri.fish`, and `~/.config/tmux/niri-title.sh`. On `mac` and `server` roles these are not applied.
`~/.config/hypr`, `~/.config/waybar`, the `omarchy-*` scripts, and the `niri-launch-ai` / `-browser` / `-terminal` / `-webapp` wrappers are **not** listed there, so chezmoi applies them on every role and OS. Only the exact path `.local/bin/niri-launch` is ignored, not the wrappers that call it.
:::
Two Wayland compositors are configured side by side. niri is the original setup, paired with the Noctalia shell. Hyprland runs on top of [Omarchy](https://omarchy.org), and its bindings are ported from the niri config: the same keys launch the same apps where both compositors support it. Waybar is the Hyprland bar. Noctalia replaces it under niri.
| Page | Covers |
| --- | --- |
| [niri](/desktop/niri) | `~/.config/niri` (base config, Noctalia colors, the `config.d/` fragments, per-display profiles), niri helper scripts, the `niri-launch*` launchers, `cp-appid`, the `nirinit` user service |
| [Hyprland](/desktop/hyprland) | `~/.config/hypr` Lua config on Omarchy: bindings, input, look and feel, monitors, hyprsunset, the portal; every `omarchy-*` script in `~/.local/bin` |
| [Waybar](/desktop/waybar) | `~/.config/waybar`: module layout, stylesheet, the `window_pill.py` title module |
| [Fonts and default apps](/desktop/fonts-and-apps) | `~/.config/fontconfig/fonts.conf`, `~/.config/mimeapps.list`, the Victor Mono Nerd Font packages |
## Packages
`.chezmoidata/packages.yaml` declares the desktop packages under `packages.arch.desktop`. `.chezmoiscripts/run_onchange_before_10-packages.sh.tmpl` installs them only when the role is `desktop` or `laptop`:
| Source | Packages |
| --- | --- |
| pacman | `niri`, `xdg-user-dirs` |
| AUR (via `paru`) | `noctalia-shell` |
The script skips AUR packages with a warning if `paru` is not installed. Hyprland, Waybar, and the `omarchy-*` commands that the scripts call (`omarchy-menu`, `omarchy-launch-or-focus`, and others) come from an Omarchy install. This repo does not install them. See [chezmoi scripts](/chezmoi/scripts) for the full install flow.
+7
View File
@@ -0,0 +1,7 @@
import { defineMeta } from "blume";
export default defineMeta({
title: "Desktop",
icon: "monitor",
pages: ["index", "niri", "hyprland", "waybar", "fonts-and-apps"],
});
+458
View File
@@ -0,0 +1,458 @@
---
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-nightly`, `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 Nightly |
| `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-nightly` (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.
Binary file not shown.

After

Width:  |  Height:  |  Size: 366 KiB

+143
View File
@@ -0,0 +1,143 @@
---
title: Waybar
description: Bottom Waybar for Hyprland/Omarchy — module layout, a drawer of system indicators, the window-title pill, and theme-driven styling.
---
[Waybar](https://github.com/Alexays/Waybar) is a status bar for wlroots-based and Hyprland compositors. This config replaces Omarchy's default bar with a bottom-docked layout. It uses Hyprland and Omarchy modules and commands, so it pairs with the [Hyprland](/desktop/hyprland) setup. Under [niri](/desktop/niri), Noctalia provides the bar.
:::note
`~/.config/waybar` is not listed in `.chezmoiignore.tmpl`, so chezmoi applies it on every role. Waybar itself comes from Omarchy and is not in `.chezmoidata/packages.yaml`.
:::
## Files
| Source (repo) | Target |
| --- | --- |
| `dot_config/waybar/config.jsonc` | `~/.config/waybar/config.jsonc` |
| `dot_config/waybar/style.css` | `~/.config/waybar/style.css` |
| `dot_config/waybar/window_pill.py` | `~/.config/waybar/window_pill.py` |
| `dot_config/waybar/V6.cb.png` | `~/.config/waybar/V6.cb.png` |
:::warning
`window_pill.py` has no `executable_` prefix, and the repo file is mode `0644`. In symlink mode, chezmoi links `~/.config/waybar/window_pill.py` to that non-executable file. The module's `exec` calls the path directly, so it only runs if the executable bit is set by hand (`chmod +x` on the source) or the file is renamed to `executable_window_pill.py`.
:::
`V6.cb.png` is a 1920×921 reference screenshot of this bar, taken with a floating btop window over a plain background. No config file references it.
![Waybar reference screenshot (V6.cb.png)](./waybar-preview.png)
## Bar settings
| Option | Value |
| --- | --- |
| `position` | `bottom` |
| `exclusive` | true (reserves space) |
| `fixed-center` | true |
| `spacing` | 2 |
| `margin-top` | 1 |
| `reload_style_on_change` | true |
## Layout
<Columns cols={3}>
<Column>
**Left**
- separator
- `custom/omarchy`
- separator
- `hyprland/workspaces`
- separator
- `tray`
- separator
- `custom/voxtype`
- `custom/update`
- `custom/screenrecording-indicator`
- `custom/updatespacman`
- separator
- `idle_inhibitor`
</Column>
<Column>
**Center**
- `custom/windowpill`
</Column>
<Column>
**Right**
- `group/right-group2` → `group/right2` → `group/tray-expander`
- `group/right-group1` → `group/right1` → `clock`
</Column>
</Columns>
The right side nests groups in vertical wrappers so the drawer and the two-line clock align. `group/tray-expander` is a drawer (600 ms transition, children class `tray-group-item`). It shows only `custom/expand-icon` until hovered, then reveals: `backlight`, `bluetooth`, `pulseaudio#output`, `pulseaudio#input`, `power-profiles-daemon`, `network`, `memory`, `cpu`, `battery`.
## Modules
| Module | Configuration |
| --- | --- |
| `custom/separator` (also `separator2`, `separator3`, defined but unused) | Literal `\|`, no tooltip |
| `custom/omarchy` | Omarchy logo glyph (`omarchy` font). Click: `omarchy-menu`. Right-click: `xdg-terminal-exec`. Tooltip "Omarchy Menu — Super + Alt + Space" |
| `hyprland/workspaces` | `format "{icon}"`, click to activate, pointer cursor. Persistent workspaces 1, 2, 3, 4, 5, 7, 8, 9 |
| `tray` | Icon size 12, spacing 5, shows passive items |
| `custom/voxtype` | `omarchy-voxtype-status` (JSON). Icons for `idle` (none), `recording`, `transcribing`. Click: `omarchy-voxtype-model`. Right-click: `omarchy-voxtype-config` |
| `custom/update` | `omarchy-update-available` every 6 h (`interval 21600`, `signal 7`). Click runs `omarchy-update` in a floating terminal |
| `custom/screenrecording-indicator` | `$OMARCHY_PATH/default/waybar/indicators/screen-recording.sh` (JSON, `signal 8`). Click: `omarchy-capture-screenrecording` |
| `custom/updatespacman` | `waybar-module-pacman-updates --no-zero-output`, only if that binary exists. Shows an icon when updates are pending |
| `idle_inhibitor` | Activated/deactivated icons |
| `custom/windowpill` | `~/.config/waybar/window_pill.py` (JSON, `restart-interval 1`), centered |
| `clock` | `{:%H:%M\n%d/%m/%Y}` (two lines); alt `{:%H:%M - %d/%m/%Y}`. Month calendar tooltip (3 columns; month `#ffead3`, weekdays `#ffcc66`, today `#ff6699`). Right-click: `omarchy-tz-select` in a floating terminal |
| `backlight` | Device `intel_backlight`, 15-step icon ramp, tooltip shows percent |
| `bluetooth` | Off/on/connected icons, tooltip shows connection count. Click: `omarchy-launch-bluetooth` |
| `pulseaudio#output` | Icon only (headphone or 3-step speaker, muted icon). Max 100%, scroll step 2. Click: `omarchy-launch-audio`. Right-click: `pamixer -t` (mute) |
| `pulseaudio#input` | Mic / muted-mic icon. Max 100%, scroll step 1 via `pactl set-source-volume`. Click: `omarchy-launch-audio`. Right-click: `pamixer -t` |
| `power-profiles-daemon` | Icons for performance / balanced / power-saver. Tooltip shows profile and driver |
| `network` | 5-step Wi-Fi signal icons; ethernet and disconnected icons. Tooltips show ESSID, frequency, and bandwidth. Polls every 3 s. Click: `omarchy-launch-wifi` |
| `memory` | `{used:0.1f}G` every 2 s. Click: `omarchy-launch-or-focus-tui btop` |
| `cpu` | `{usage}%` every 10 s. Click: btop |
| `battery` | `{capacity}% {icon}` with 10-step charging and discharging ramps. Warning at 20%, critical at 10%, polled every 5 s. Click: `omarchy-menu power`. Right-click: `notify-send` of `omarchy-battery-status` |
Icons are Nerd Font glyphs.
## window_pill.py
A Python script that feeds `custom/windowpill` a two-line label for the focused window:
- **Top line:** the window class, small, `#a6adc8`, lowered with `rise='-2000'`.
- **Bottom line:** the title, bold, size 11000, `#ffffff`, truncated to 80 characters (`MAX_TITLE_LEN`). For Discord/Vesktop it removes the unread counter prefix such as `(209) ` and a leading `Discord | `.
- **No focused window:** shows `Workspace <id>` over a non-breaking space.
Text is HTML-escaped for Pango and printed as JSON (`class: custom-window`, tooltip `class: title`). After the first print it connects to Hyprland's event socket `/tmp/hypr/$HYPRLAND_INSTANCE_SIGNATURE/.socket2.sock` and reprints on `activewindow>>` and `workspace>>` events. It exits if `HYPRLAND_INSTANCE_SIGNATURE` is unset, which means the module does nothing outside Hyprland.
## Styling
`style.css` imports the active Omarchy theme's palette, so colors follow whatever theme Omarchy has selected (for example cyberdream):
```css ~/.config/waybar/style.css
@import "/home/prad/.local/state/omarchy/current/theme/waybar.css";
```
The import path is hard-coded to `/home/prad`. On another account the import fails and the `@background` / `@foreground` colors are undefined.
| Element | Style |
| --- | --- |
| All | `font-family: 'CaskaydiaCove Nerd Font Propo'`, no borders or text shadow, square corners |
| Bar box (`#waybar > box`) | `@background` fill, `@foreground` text, 2px/6px margins, 4px radius, `box-shadow 0 1px 2px` black, weight 700, size 12px, min height 30px, 0.5 s background transition |
| Window background | `@transparent` |
| Workspace buttons | Opacity 0.55. Active 1.0. Empty 0.2. Empty and active 1.0. Bold 12px |
| Modules | 6px horizontal padding, 1px margins, min width 12px |
| Separators | Opacity 0.25 |
| `custom/updatespacman` | Opacity 0.55 |
| `custom/expand-icon` | Opacity 0.5 |
| `custom/update` | 10px font |
| Tray | `@background`. Passive items dimmed, attention items highlighted |
| Tooltips | `@background`, 12px, 8px radius, `1.5px solid rgba(255,255,255,0.25)` border |
| Screen recording active | `#eb7087`, blinks to `@background` every 0.5 s |
| Voxtype recording | `#a55555` |
| Battery warning | `#c95d0f` |
| Battery critical (not charging) | `#e6d8ba` on transparent, blinking to `#f53c3c` (0.5 s, 12 steps) |
## Related
- [Hyprland](/desktop/hyprland): the compositor this bar is built for.
- [Fonts and default apps](/desktop/fonts-and-apps): Nerd Font packages.