refactor(#107): theme-state.json → state.json, theme-sync → state-sync
Some checks failed
Check / eval (push) Has been cancelled

The machine flake's git-tracked settings file is system state, not
"theme" only — rename it to state.json. CLI becomes nomarchy-state-sync
with a nomarchy-theme-sync symlink for scripts and muscle memory.

Eval (mkFlake, doctor, lifecycle) still accepts theme-state.json; the
next write migrates to state.json and removes the legacy file.
Documented in MIGRATION.md; drop the CLI alias after release notes.
This commit is contained in:
2026-07-15 11:26:59 +01:00
parent 013403deb4
commit d8e1a13d50
62 changed files with 463 additions and 369 deletions

View File

@@ -7,11 +7,11 @@ partial.
```
┌──────────────────────────────────────────────────────────────────────────┐
theme-state.json (single source of truth) │
│ state.json (single source of truth) │
│ lives INSIDE your flake checkout, git-tracked │
└───────────────────────────────────┬──────────────────────────────────────┘
nomarchy-theme-sync apply gruvbox
nomarchy-state-sync apply gruvbox
1. merges the preset into the JSON (atomic write)
2. runs `home-manager switch` (no sudo, no system rebuild)
@@ -35,7 +35,7 @@ Flat on purpose. Two module trees, one options file each, no hidden layers.
.
├── flake.nix # inputs + the downstream API (exports below)
├── lib.nix # nomarchy.lib.mkFlake — one-call downstream wrapper
├── theme-state.json # ★ THE single source of truth (git-tracked!)
├── state.json # ★ THE single source of truth (git-tracked!)
├── themes/ # 24 presets: <slug>.json + optional <slug>/ assets
│ ├── nord.json # palette (required, works alone)
│ └── nord/ # assets (optional, fixed filenames)
@@ -72,10 +72,10 @@ Flat on purpose. Two module trees, one options file each, no hidden layers.
│ ├── default/ # reference machine (thin: boot, user, hostname)
│ └── live.nix # bootable live ISO (try the distro, no install)
├── pkgs/
│ ├── nomarchy-theme-sync/ # state writer + rebuild dispatcher (Python)
│ ├── nomarchy-state-sync/ # state writer + rebuild dispatcher (Python)
│ ├── nomarchy-install/ # live-ISO installer (gum + disko + mkFlake)
│ ├── nomarchy-doctor/ # read-only health sheet, one command per failure
│ ├── nomarchy-control-center/ # TUI settings front-end over nomarchy-theme-sync (gum)
│ ├── nomarchy-control-center/ # TUI settings front-end over nomarchy-state-sync (gum)
│ └── nomarchy-battery-notify/ # low-battery toast watcher backing battery-notify.nix
├── templates/downstream/ # machine flake SoT (`flake init` + installer copy/patch)
├── docs/ # human docs — map: docs/README.md
@@ -161,7 +161,7 @@ Nomarchy in place, reusing your existing `hardware-configuration.nix`:
**[docs/MIGRATION.md](docs/MIGRATION.md)**.
You own two files day-to-day: `system.nix` and `home.nix` (plus
`theme-state.json`, written by the CLI). Your `flake.nix` is set up once —
`state.json`, written by the CLI). Your `flake.nix` is set up once —
later by the installer — and never hand-edited; it's a single call:
```nix
@@ -229,7 +229,7 @@ Legacy aliases still work: `sys-update` → pull+rebuild, `sys-rebuild` →
`nomarchy-rebuild`, `home-update``nomarchy-home`.
Override anything via the `nomarchy.*` surface or plain NixOS/HM options:
appearance (gaps/colors/fonts) changes through `nomarchy-theme-sync`,
appearance (gaps/colors/fonts) changes through `nomarchy-state-sync`,
behaviour (input/misc/monitor/chrome) is `mkDefault` so a plain `home.nix`
assignment wins, and bind/exec-once lists concatenate. Full guide with
examples: **[docs/OVERRIDES.md](docs/OVERRIDES.md)**.
@@ -243,7 +243,7 @@ two tables below are split along exactly that line.
| Option | Default | Purpose |
|---|---|---|
| `nomarchy.stateFile` | — (required) | Path to your theme-state.json |
| `nomarchy.stateFile` | — (required) | Path to your state.json |
| `nomarchy.terminal` | `"kitty"` | Terminal for keybinds and `$TERMINAL` (Kitty is the only shipped/themed emulator) |
| `nomarchy.kitty.enable` | `true` | Nomarchy's Kitty config (palette/font/opacity from theme-state) |
| `nomarchy.keyboard.layout` | `"us"` | XKB layout for the Hyprland session (installer writes the matching `services.xserver.xkb` in system.nix; the console + LUKS prompt follow via the distro default) |
@@ -275,7 +275,7 @@ two tables below are split along exactly that line.
| `nomarchy.displayProfiles` | `{}` | Named layouts for the same outputs (docked/undocked/…), switched from System Display Profiles: instant via hyprctl, persisted in-flake (`settings.displayProfile`), baked over `nomarchy.monitors` at the next rebuild. Profiles that disable the laptop panel use the same safe ordering as Dock mode (external on → workspace handoff → internal off). The menu's Auto-switch row (`settings.displayProfileAuto`) applies the matching profile from Hyprland hotplug events. Workspace pins (`workspaces = { "1" = "DP-3"; }`) are moved instantly and baked as rules |
| `nomarchy.launchOrFocus` | `[]` | Launch-or-focus binds: `SUPER+<key>` focuses the app's window (case-insensitive class match) or launches it; entries land in the SUPER+? cheatsheet, and a bind whose app was removed notifies instead of failing silently |
| `nomarchy.themesDir` | Nomarchy's `themes/` | Where per-theme app overrides are probed |
| `nomarchy.package` | overlay's `nomarchy-theme-sync` | The theme/state tool package, overridable if you fork it |
| `nomarchy.package` | overlay's `nomarchy-state-sync` | The theme/state tool package, overridable if you fork it |
**Always-on, no toggle by design:** `services.cliphist`, `services.udiskie`
(automount + safe-removal toasts) and `services.easyeffects` (mic noise
@@ -290,7 +290,7 @@ option, e.g. `services.easyeffects.enable = lib.mkForce false;` — see
| Option | Default | Purpose |
|---|---|---|
| `nomarchy.system.plymouth.enable` | `true` | Branded boot splash, background from the theme JSON (recolors on system rebuilds) |
| `nomarchy.system.stateFile` | `null` | theme-state.json for the system-side consumers (the Plymouth tint); `lib.mkFlake` wires it for you — set it only when composing the modules by hand |
| `nomarchy.system.stateFile` | `null` | state.json for the system-side consumers (the Plymouth tint); `lib.mkFlake` wires it for you — set it only when composing the modules by hand |
| `nomarchy.system.fileManager.enable` | `true` | Thunar GUI + gvfs/tumbler/udisks2 (the "open folder" handler) |
| `nomarchy.system.greeter.enable` | `true` | greetd/tuigreet |
| `nomarchy.system.greeter.autoLogin` | state | Auto-login this user into Hyprland. Owned by System Auto-login (`settings.greeter.autoLogin`; installer seeds it on LUKS machines) — set it here only to pin it against the menu |
@@ -367,13 +367,13 @@ The trap with "read a mutable file from Nix" is pure evaluation: flakes
cannot read arbitrary `$HOME` paths without `--impure` (the old prototype
required it — never again). Nomarchy's convention: **the state file lives
inside the consuming flake** and is wired via
`nomarchy.stateFile = ./theme-state.json;`. Reading it is pure — it's flake
source. It must be git-tracked (`nomarchy-theme-sync` runs
`nomarchy.stateFile = ./state.json;`. Reading it is pure — it's flake
source. It must be git-tracked (`nomarchy-state-sync` runs
`git add --intent-to-add` after every write as a safety net).
### One change = one generation
`nomarchy-theme-sync apply <theme>` merges the preset into the JSON and runs
`nomarchy-state-sync apply <theme>` merges the preset into the JSON and runs
`home-manager switch` (override the command with `$NOMARCHY_REBUILD`, or pass
`--no-switch` to only write). Everything is baked: Hyprland, Waybar, Kitty,
btop, and — via Stylix, mapped onto base16 roles — GTK, Qt, cursors and
@@ -404,7 +404,7 @@ gates the unit, the first enable rebuilds) and `settings.nightlight.on` (the
instant on/off). Expect more `nomarchy.*` toggles to migrate to this pattern.
**Auto-commit (opt-in):** System Auto-commit makes every `apply`/`set`
mutation also `git commit` theme-state.json in your flake — *only* that
mutation also `git commit` state.json in your flake — *only* that
file, so unrelated dirty work is never swept up — turning your settings
history into `git log`. Off by default; the toggle is instant (nothing in
Nix consumes the flag), the off-write is itself committed so history stays
@@ -432,12 +432,12 @@ asset probe).
## 5. Day-to-day
```sh
nomarchy-theme-sync list # 24 presets (nord, gruvbox, rose-pine, …)
nomarchy-theme-sync apply kanagawa # whole desktop, one generation (~a switch)
nomarchy-theme-sync set ui.gapsOut 16 # tweak one knob (also a switch)
nomarchy-theme-sync bg next # cycle wallpapers — instant, no rebuild
nomarchy-theme-sync bg auto # back to the theme's default wallpaper
nomarchy-theme-sync get colors.accent
nomarchy-state-sync list # 24 presets (nord, gruvbox, rose-pine, …)
nomarchy-state-sync apply kanagawa # whole desktop, one generation (~a switch)
nomarchy-state-sync set ui.gapsOut 16 # tweak one knob (also a switch)
nomarchy-state-sync bg next # cycle wallpapers — instant, no rebuild
nomarchy-state-sync bg auto # back to the theme's default wallpaper
nomarchy-state-sync get colors.accent
nomarchy-pull # flake input update (no rebuild)
nomarchy-rebuild # rebuild the system, current lock
nomarchy-home # rebuild just the desktop layer
@@ -484,7 +484,7 @@ reload # exec zsh (reload the shell)
- **New theme:** drop a JSON into `themes/` (schema = any existing preset),
plus an optional `themes/<slug>/` assets directory.
- **New themed value:** add the key to `theme-state.json` and consume it in
- **New themed value:** add the key to `state.json` and consume it in
the Nix modules. One place — there is no second renderer to keep in sync.
- **Importing more old-distro palettes:**
`tools/import-palettes.py <palettes-dir> themes/` (roles are first-class: