Roadmap items 1+2 plus the issues found testing on a Latitude 5410: - Ship the 10 most popular Nerd Fonts by default (iosevka swapped for mononoki: 1.1 GB vs 27 MB) and warn from nomarchy-theme-sync when a configured fonts.mono/fonts.ui family isn't installed (fc-match) — fontconfig substitutes silently otherwise. - hardware.enableRedistributableFirmware: installed systems shipped NO firmware blobs (wifi/SOF audio/BT) — nixos-generate-config only emits microcode lines referencing this flag, it never sets it. - Live ISO wifi: networking.wireless.enable = mkForce false killed NetworkManager's supplicant — since 26.05 the NM module drives its wifi backend THROUGH networking.wireless (dbusControlled). Devices vanished from nmtui with iwlwifi loaded and no rfkill block. - Hyprland 0.55: launch sessions via start-hyprland (watchdog; bare binary warns), add the new gesture keyword (workspace_swipe is gone — touchpads had no gestures at all), media keys via wpctl/bindel/bindl plus the missing mic/play/next/prev binds. - Offline theme switching: pin mustache-go + stdenv + the repo's own standalone HM generation (incl. home-files inputDerivation) into the ISO — stylix re-renders base16 templates per switch and an offline `apply` cascaded into building stdenv from source (1107 drvs). Verified with tools/vm/gap-analysis.py: 17 config drvs, zero fetches. - Stylix: track release-26.05 (kills the HM version-skew warning and the stale home-manager follows), lock updated. - Roadmap: swaync (no notification daemon ships today). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
243 lines
13 KiB
Markdown
243 lines
13 KiB
Markdown
# Nomarchy
|
|
|
|
**The rock-solid reproducibility of NixOS 26.05. The out-of-the-box polish of
|
|
Omarchy/Omakub.** One JSON file rules the look of the entire desktop; every
|
|
theme change is a Home Manager generation — atomic, rollbackable, never
|
|
partial.
|
|
|
|
```
|
|
┌──────────────────────────────────────────────────────────────────────────┐
|
|
│ theme-state.json (single source of truth) │
|
|
│ lives INSIDE your flake checkout, git-tracked │
|
|
└───────────────────────────────────┬──────────────────────────────────────┘
|
|
│
|
|
nomarchy-theme-sync apply gruvbox
|
|
1. merges the preset into the JSON (atomic write)
|
|
2. runs `home-manager switch` (no sudo, no system rebuild)
|
|
│
|
|
▼ pure read (nomarchy.stateFile)
|
|
┌──────────────────────────────────────────────────────────────────────────┐
|
|
│ Home Manager bakes EVERYTHING into one read-only generation: │
|
|
│ Hyprland (colors/gaps/borders) Waybar (palette or whole-swap) │
|
|
│ Ghostty (full ANSI palette) btop (asset or generated) │
|
|
│ Stylix → GTK, Qt, cursors, fonts │
|
|
└───────────────────────────────────┬──────────────────────────────────────┘
|
|
▼
|
|
wallpaper via awww (the one runtime piece:
|
|
applied post-switch + at session start, `bg next` cycles)
|
|
```
|
|
|
|
## 1. Layout
|
|
|
|
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!)
|
|
├── themes/ # 21 presets: <slug>.json + optional <slug>/ assets
|
|
│ ├── nord.json # palette (required, works alone)
|
|
│ └── nord/ # assets (optional, fixed filenames)
|
|
│ ├── backgrounds/ # wallpapers (auto-picked, SUPER+SHIFT+T cycles)
|
|
│ ├── btop.theme # hand-made config drop (else generated)
|
|
│ └── waybar.css # whole-swap: replaces the generated bar style
|
|
├── modules/
|
|
│ ├── nixos/ # the distro, system side
|
|
│ │ ├── default.nix # Hyprland session, Pipewire, greetd, fonts
|
|
│ │ └── options.nix # nomarchy.system.* toggles
|
|
│ └── home/ # the distro, user side
|
|
│ ├── default.nix # entry point
|
|
│ ├── options.nix # nomarchy.* option surface
|
|
│ ├── theme.nix # JSON ingestion + wallpaper hook
|
|
│ ├── stylix.nix # GTK/Qt/cursors/fonts from the same JSON
|
|
│ ├── hyprland.nix # all JSON-driven
|
|
│ ├── waybar.nix
|
|
│ ├── ghostty.nix
|
|
│ └── btop.nix
|
|
├── hosts/
|
|
│ ├── 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-install/ # live-ISO installer (gum + disko + mkFlake)
|
|
├── templates/downstream/ # `nix flake init -t` starter for users
|
|
├── docs/TESTING.md # how to verify changes (incl. AI-agent rules)
|
|
└── tools/ # maintainer-only
|
|
├── import-palettes.py # converts old-distro themes → JSON + assets
|
|
├── test-live-iso.sh # build the ISO + boot it in QEMU
|
|
├── test-install.sh # full offline-install regression in QEMU
|
|
└── vm/ # headless-VM helpers (QMP keys, VNC shots,
|
|
# offline-pin gap analysis)
|
|
```
|
|
|
|
**Rule of thumb:** `modules/` is the distro (reusable, no machine specifics),
|
|
`hosts/` is a machine, `themes/` is data, `pkgs/` is code, `tools/` is
|
|
maintainer-only. If a new file doesn't obviously belong to one of those, it
|
|
probably shouldn't exist.
|
|
|
|
## 2. Try it first (live ISO)
|
|
|
|
Boot the full desktop from a USB stick or VM without installing anything:
|
|
|
|
```sh
|
|
nix build .#nixosConfigurations.nomarchy-live.config.system.build.isoImage
|
|
# → result/iso/*.iso — dd to a stick, or boot it in QEMU:
|
|
tools/test-live-iso.sh
|
|
```
|
|
|
|
The live session auto-logs-in, seeds the flake at `~/.nomarchy`, and pins
|
|
the locked inputs into the ISO store — so theme switching (including the
|
|
`home-manager switch` it triggers) works **offline**, exactly like on an
|
|
installed system. Verification checklist: [docs/TESTING.md](docs/TESTING.md).
|
|
|
|
Like what you see? **`nomarchy-install`** (in a terminal) walks you through
|
|
installing to disk: pick a disk, LUKS2 full-disk encryption **by default**
|
|
(in exchange the desktop logs in passwordless — the passphrase already
|
|
gates the machine), user + hostname + timezone, hardware autodetection
|
|
(DMI → nixos-hardware profile), a hibernation-ready swapfile sized to RAM,
|
|
then disko partitions (GPT + ESP + BTRFS subvolumes incl. `@snapshots` —
|
|
snapper timeline snapshots are on) and `nixos-install` runs — **without a
|
|
network** when the ISO was built from a clean tree (the target's
|
|
`flake.lock` is composed from the rev the ISO carries). The installed
|
|
machine gets the standard downstream layout: the flake at `~/.nomarchy`
|
|
(`/etc/nixos` symlinks to it), one `mkFlake` call, your
|
|
`system.nix`/`home.nix`. First boot lands in the fully themed desktop —
|
|
the installer pre-activates the Home Manager generation. UEFI only for now.
|
|
|
|
## 3. Using Nomarchy on your machine (downstream)
|
|
|
|
Nomarchy is consumed as a flake input — you never fork or edit this repo:
|
|
|
|
```sh
|
|
mkdir my-machine && cd my-machine
|
|
nix flake init -t "git+https://git.bemagri.xyz/bernardo/nomarchy.git?ref=v1"
|
|
```
|
|
|
|
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 —
|
|
later by the installer — and never hand-edited; it's a single call:
|
|
|
|
```nix
|
|
outputs = { nomarchy, ... }:
|
|
nomarchy.lib.mkFlake {
|
|
src = ./.;
|
|
username = "me";
|
|
hardwareProfile = "framework-13-7040-amd"; # optional, nixos-hardware name
|
|
};
|
|
```
|
|
|
|
| `mkFlake` arg | Default | Purpose |
|
|
|---|---|---|
|
|
| `src` | — (required) | Your flake directory (`./.`) |
|
|
| `username` | — (required) | Login name; flows into `system.nix` and names the HM config |
|
|
| `hardwareProfile` | `null` | One [nixos-hardware](https://github.com/NixOS/nixos-hardware) module name, or a list of them (pinned + tested by Nomarchy; unknown names fail with suggestions) |
|
|
| `system` | `"x86_64-linux"` | Platform |
|
|
|
|
(Power users can skip `mkFlake` and wire `nixosModules.nomarchy` /
|
|
`homeModules.nomarchy` / `overlays.default` by hand — the wrapper is sugar.)
|
|
|
|
Two deliberately separate rebuild paths:
|
|
|
|
```sh
|
|
sudo nixos-rebuild switch --flake .#default # system: rare
|
|
home-manager switch --flake .#me # desktop: every theme change, no sudo
|
|
```
|
|
|
|
Override anything with plain NixOS/HM options (the distro uses `mkDefault`
|
|
throughout) or the `nomarchy.*` surface:
|
|
|
|
| Option | Default | Purpose |
|
|
|---|---|---|
|
|
| `nomarchy.stateFile` | — (required) | Path to your theme-state.json |
|
|
| `nomarchy.terminal` | `"ghostty"` | Terminal for keybinds and `$TERMINAL` |
|
|
| `nomarchy.hyprland.enable` | `true` | Nomarchy's Hyprland config |
|
|
| `nomarchy.waybar.enable` | `true` | Nomarchy's Waybar |
|
|
| `nomarchy.ghostty.enable` | `true` | Nomarchy's Ghostty |
|
|
| `nomarchy.btop.enable` | `true` | btop with per-theme colors |
|
|
| `nomarchy.stylix.enable` | `true` | GTK/Qt/cursor theming |
|
|
| `nomarchy.themesDir` | Nomarchy's `themes/` | Where per-theme app overrides are probed |
|
|
| `nomarchy.system.greeter.enable` | `true` | greetd/tuigreet |
|
|
| `nomarchy.system.audio.enable` | `true` | Pipewire stack |
|
|
| `nomarchy.system.bluetooth.enable` | `true` | Bluetooth + blueman |
|
|
|
|
## 4. How theming works
|
|
|
|
### Pure JSON ingestion
|
|
|
|
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
|
|
`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
|
|
`home-manager switch` (override the command with `$NOMARCHY_REBUILD`, or pass
|
|
`--no-switch` to only write). Everything is baked: Hyprland, Waybar, Ghostty,
|
|
btop, and — via Stylix, mapped onto base16 roles — GTK, Qt, cursors and
|
|
fonts. No runtime patching means no partial states, and `home-manager
|
|
generations` is also your theme history. Waybar even restyles in place: it
|
|
re-reads `style.css` when the symlink flips.
|
|
|
|
The **wallpaper** is the one runtime piece (awww — nixpkgs' swww — is
|
|
imperative; nothing in Nix consumes the path): applied at session start and
|
|
after every switch via a tiny activation hook, cycled instantly with
|
|
`bg next`.
|
|
|
|
### Per-theme app assets (`themes/<slug>/`)
|
|
|
|
Recoloring covers 95% of theming; the rest is one optional assets directory
|
|
per theme — a single place to look, unlike the old distro's split:
|
|
|
|
| Asset | Mechanism |
|
|
|---|---|
|
|
| `backgrounds/` | wallpapers; empty `wallpaper` in the state means "first one"; `bg next` cycles |
|
|
| `btop.theme` | baked into the generation (generated from the palette when absent) |
|
|
| `waybar.css` | **whole-swap**: replaces the generated bar style entirely (probed at eval time, self-contained) |
|
|
| `waybar.jsonc` | whole-swap for the bar *layout* (must be plain JSON) |
|
|
|
|
Six ported themes ship a `waybar.css` identity (catppuccin, lumon, nord,
|
|
retro-82, summer-day, summer-night). Custom user themes can live in
|
|
`$NOMARCHY_PATH/themes/` (preset lookup) and `nomarchy.themesDir` (eval-time
|
|
asset probe).
|
|
|
|
## 5. Day-to-day
|
|
|
|
```sh
|
|
nomarchy-theme-sync list # 21 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
|
|
```
|
|
|
|
Keybinds: `SUPER+Return` terminal · `SUPER+D` launcher · `SUPER+T` theme
|
|
picker · `SUPER+SHIFT+T` next wallpaper · `SUPER+Q` close · `SUPER+1..9`
|
|
workspaces · `Print` region screenshot.
|
|
|
|
## 6. Extending
|
|
|
|
- **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
|
|
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/`.
|
|
|
|
## Roadmap
|
|
- We need to start creating a menu system (for apps launcher and for theme switching)
|
|
- We need to add networkManager applet to waybar so user can easily connect to internet
|
|
- **Faster switches:** move `backgrounds/` out of the flake source (the 86 MB
|
|
re-copy on every state write is the main eval tax), then pre-built theme
|
|
variants if still needed
|
|
- Plymouth + SDDM/greeter theming from the same JSON
|
|
- Installer round 2: multi-disk BTRFS RAID, impermanence, BIOS/legacy
|
|
boot (v1 `nomarchy-install` is single-disk UEFI — see `pkgs/nomarchy-install`)
|
|
- `hyprlock`/`hypridle`, swayosd, launch-or-focus UX scripts
|
|
- swaync notification center (notify-send currently renders bare)
|