feat(keyboard): per-device layout memory in-flake (no ~/.local/state)

Phase 2 of the in-flake-state philosophy, mirroring night-light: the
nomarchy-keyboard-watch daemon now remembers per-device layouts in the
git-tracked state file (settings.keyboard.devices) instead of
~/.local/state/nomarchy/keyboard-layouts.

- Reads the LIVE working-tree map (theme-sync get → jq) so a pick this
  session is honoured at once; writes instantly with --no-switch (no
  rebuild). Writes the whole map back at the dot-free parent path
  (read-modify-write) so a device name with a dot can't corrupt the
  tool's dotted set-path (Hyprland only sanitises spaces→-).
- Graduation: nomarchy.keyboard.devices now mkDefault-merges entries
  from settings.keyboard.devices, so each remembered device becomes a
  generated Hyprland device{} block on the next rebuild (reproducible,
  hotplug-applied). A hand-written keyboard.devices.<name> still wins;
  once baked, the watcher's is_declared check makes it step back.
- theme.nix seeds settings.keyboard.devices = {} (sparse state still
  evaluates); options/template descriptions updated; ROADMAP marked done.

Eval-verified (graduation + mkDefault precedence) and the set/get
writer round-trips. Still needs the on-hardware hotplug re-verify (the
picker path isn't testable in CI).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Bernardo Magri
2026-06-23 09:18:33 +01:00
parent 685126ab47
commit aed41793f8
5 changed files with 83 additions and 28 deletions

View File

@@ -55,11 +55,18 @@ let
# switch always replaces it (deep_merge would otherwise leave it stuck).
border = { active = "accent"; inactive = "overlay"; };
# Non-appearance feature settings the menu writes into this same in-flake
# state. nomarchy.nightlight: `installed` (sticky — gates the unit, so the
# first enable rebuilds) and `on` (instant runtime on/off). Defaulted so a
# sparse/older state file still evaluates; nomarchy.settings exposes them.
settings.nightlight = { installed = false; on = true; };
# Non-appearance feature settings the menu/watchers write into this same
# in-flake state. nomarchy.nightlight: `installed` (sticky — gates the unit,
# so the first enable rebuilds) and `on` (instant runtime on/off).
# settings.keyboard.devices: per-device layouts the new-keyboard watcher
# remembers (device-name -> XKB layout), git-tracked instead of
# ~/.local/state; they graduate into nomarchy.keyboard.devices on the next
# rebuild. Defaulted so a sparse/older state file still evaluates;
# nomarchy.settings exposes them.
settings = {
nightlight = { installed = false; on = true; };
keyboard.devices = { };
};
};
parsed = lib.recursiveUpdate defaults themeState;