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

5.5 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, 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.
  • 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.)

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).

Workflow

  1. Edit the live file under ~/.config/niri/cfg/.
  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, display.kdl, input.kdl — flag drift, don't silently add.
  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 subcommand.
  • 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.
  • If Frank reports "still seeing old values" after a config edit: run load-config-file first, then verify empirically before believing anything else.

"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).