Files
command-center/agent/managed-skills/niri-config-edit-validate/SKILL.md
T

7.2 KiB
Raw Blame History

name, description
name description
niri-config-edit-validate Locate, edit, validate, and commit niri compositor config on Frank's workstations: desk/display profiles, EDID-keyed outputs, layout, keybind grepping, dotfiles mirror, noctalia.kdl last-include color override, forced hot-reload.

Editing niri config on Dank workstations

Layout of the config

  • Live config: ~/.config/niri/config.kdl is a thin file of include "./cfg/*.kdl" lines. Never conclude "not bound" from grepping only config.kdl — keybinds live in cfg/keybinds.kdl, rules in cfg/rules.kdl, layout in cfg/layout.kdl, etc. Grep ~/.config/niri/cfg/ recursively.
  • Include order wins: config.kdl ends with include "noctalia.kdl" — a hand-written accent overlay at ~/.config/niri/noctalia.kdl that re-declares layout { focus-ring/border/tab-indicator/insert-hint } + recent-windows.highlight colors and BEATS every cfg/layout.kdl color edit (observed 2026-10-18: focus-ring color edits to layout.kdl appeared to "not reload" while this file silently repainted them red). Before changing any accent/outline color, grep the WHOLE ~/.config/niri/ tree (not just cfg/) for the property and fix the LAST includer. Tracked in repo at dotfiles/zz-blankpad/niri/.config/niri/noctalia.kdl; live file is NOT a symlink — sync by cp and diff like the rest.
  • niri notation: Mod = Super/Win key. Chords like Mod+R { switch-preset-column-width; }.
  • ~/.config/niri is a REAL directory, not a symlink; the version-controlled copies are a partial mirror under ~/.omp/dotfiles/zz-blankpad/niri/.config/niri/. Trees can drift: diff them before/after edits. (Hardlink wiring is FRAGILE — editor/agent atomic-rename writes silently break hardlinks; verify live↔mirror by CONTENT diff, never by link trust.) On blankpad the cfg/*.kdl entries are symlinks into the repo (dotfiles/niri/... base + zz-blankpad overrides) — edit through them freely; noctalia.kdl and config.kdl are the plain-file exceptions.

Desk/display profiles (monitor arrangement)

  • Profiles: cfg/desk-<n>-<name>.kdl (e.g. desk-1-home.kdl, desk-2-portable.kdl); ACTIVE profile is symlinked as cfg/display.kdl — readlink it to know which. scripts/desk.sh manages switching; desk.sh capture prints connected outputs' EDID long names.
  • Panels are keyed by EDID long name (output "Lenovo Group Limited 0x40A9 Unknown" = the laptop eDP-1 panel), not connector — connector names reshuffle between desks. Long-name blocks BEAT connector-keyed blocks when both match one output (legacy connector blocks like output "eDP-1" linger as duplicates; niri applies the EDID block's position).
  • Position math: x,y = top-left of each output in the logical layout; right edge = x + mode width. To place panel A left of panel B: A.x = B.x - A.width; bottom-align: A.y = B.y + (B.height - A.height).
  • Verify: niri validate → hot-reload → niri msg outputs | grep -iE 'Output |Position' (prints Logical position: x, y).

Schema gotchas (niri 26.04)

  • preset-column-widths AND preset-window-heights are children of layout { } as SIBLING blocks — wiki wording misleads; nesting or window-rule placement fails niri validate with expected one of proportion, fixed / unexpected node.
  • Preset list order = cycle order for Mod+R (switch-preset-column-width); Mod+Shift+R cycles backwards. WITH custom preset lists the cycle contains ONLY the configured presets — no implicit full/maximize tail (verified: [0.25, 0.5] cycles period 2). Maximize = separate Mod+F maximize-column. Built-in defaults without the node: 1/3, 1/2, 2/3.
  • WITHOUT preset-window-heights, switch-preset-window-height FALLS BACK to the width presets — user sees width-fractions as heights; define the node explicitly to stop it.
  • Ground truth for node placement: the binary's embedded template — grep -a -B4 -A8 "preset-column-widths {" $(which niri).
  • Window outlines: base geometry (width, off) lives in cfg/layout.kdl focus-ring/border; COLORS effectively live in noctalia.kdl (see include-order law). border { off } + focus-ring active = the visible outline.

Workflow

  1. Edit the live file under ~/.config/niri/cfg/ (symlinked = repo file; except noctalia.kdl, edit live then cp to mirror).
  2. niri validate (exit 0 + "config is valid") — mandatory before done.
  3. Hot-reload is NOT reliable after harness edits (atomic-rename writes are missed by niri's watcher; user then tests against the OLD config and reports "no change"). ALWAYS follow with niri msg action load-config-file (NOT reload-config).
  4. Sync changed file into the mirror: cp ~/.config/niri/cfg/<f>.kdl ~/.omp/dotfiles/zz-blankpad/niri/.config/niri/cfg/<f>.kdl (repo root is ~/.omp, branch main, remote = dankclaw Gitea dank/command-center; push there is pre-authorized). Verify by diff, not by cp success.
  5. Untracked mirror files: config.kdl, noctalia.kdl, animation.kdl, input.kdl, display.kdl (symlink target) — flag drift, don't silently add; when a live-only file turns out to be load-bearing (like noctalia.kdl was), commit the first durable copy into dotfiles/zz-blankpad/niri/.config/niri/.
  6. Mirror files often carry pre-existing uncommitted drift (desk.sh captures); a commit of your edit folds it in — say so in the report/commit body.

Live-behavior verification without disturbing Frank's windows

  • niri msg --json <subcmd> — the --json flag goes BEFORE the subcmd.
  • Targeting differs per action: WINDOW-scoped actions take --id <window-id> (NOT --window) — e.g. switch-preset-window-height --id N. COLUMN-scoped actions (switch-preset-column-width, set-column-width, maximize-column) have NO id — they hit the FOCUSED column; only set-column-width <px|±N%> positional CHANGE, no flags.
  • Spawned test apps do NOT reliably take focus, so blind spawn + action cycles resize whatever Frank is looking at. For column actions you must focus-window --id N first — and that STEALS focus; if Frank is actively testing chords, your probes and his keystrokes interleave and corrupt both. STOP probing, deliver config + validate, and have him press the chord himself.
  • niri msg --json focused-window can report a different window than assumed (focus moves); for per-window measurement read niri msg --json windows and filter by id. Present height of proportion p with gaps g over work-area H renders ≈ p*(H+g) − g. NOTE (measured 2026-10-18): this build's windows/focused-window JSON returns x/y/width/height = null — pixel-sampling the outline color from screenshots is not viable; ask Frank to eyeball after load-config-file instead of burning turns on capture forensics.
  • If Frank reports "still seeing old values" after a config edit: run load-config-file first, then verify empirically before believing anything else — and check noctalia.kdl include-order override BEFORE suspecting the reload.

"What does chord X do?" triage

For "mod+" questions, check in order: ~/.config/niri/cfg/keybinds.kdl (compositor wins first), terminal configs (~/.config/ghostty, foot, kitty), tmux, then omp TUI chords (omp://keybindings.md; modifiers are ctrl/shift/alt/super only, no Mod; Ctrl+R=history search, Alt+R=app.retry).