Bernardo Magri 20df0d8a60 docs: fold todo.md items into the roadmap
Theme-switch progress feedback; per-theme icon themes (unblocks rofi
show-icons); starship prompt + modern-CLI aliases (bat/eza/rg/fd/zoxide);
opt-out default app suite (nomarchy.apps.*); Plymouth logo contrast fix;
hibernate double-unlock (hyprlock redundant after LUKS resume); plus a
keybindings-cheatsheet menu module and the SUPER+Space / SUPER+M binds.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-13 15:48:50 +01:00

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
│   │   ├── plymouth.nix           #   boot splash, tinted from the JSON
│   │   └── file-manager.nix       #   Thunar GUI + gvfs/tumbler/udisks2
│   └── 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
│       ├── rofi.nix               #   launcher + nomarchy-menu (power, clip)
│       ├── swaync.nix             #   notifications, same JSON
│       ├── idle.nix               #   hyprlock + hypridle, same JSON
│       └── yazi.nix               #   flagship TUI file manager + plugins
├── 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)
├── docs/OVERRIDES.md              # how downstream users override defaults
└── 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:

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.

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:

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:

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 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:

sudo nixos-rebuild switch --flake .#default   # system: rare
home-manager switch --flake .#me              # desktop: every theme change, no sudo

Day-to-day you'll use the shipped shortcuts instead:

sys-update    # nix flake update + system rebuild (BTRFS snapshot first when available)
home-update   # home-manager switch (no flake update, no sudo)

Override anything via the nomarchy.* surface or plain NixOS/HM options: appearance (gaps/colors/fonts) changes through nomarchy-theme-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.

Option Default Purpose
nomarchy.stateFile — (required) Path to your theme-state.json
nomarchy.terminal "ghostty" Terminal for keybinds and $TERMINAL
nomarchy.keyboard.layout "us" XKB layout for the Hyprland session (installer writes it; pairs with xkb + console.useXkbConfig in system.nix)
nomarchy.keyboard.variant "" XKB variant for the session
nomarchy.hyprland.enable true Nomarchy's Hyprland config
nomarchy.waybar.enable true Nomarchy's Waybar
nomarchy.rofi.enable true Themed rofi launcher + nomarchy-menu dispatcher
nomarchy.swaync.enable true swaync notifications, themed
nomarchy.idle.enable true hyprlock + hypridle (idle lock 5 min, display off 10, suspend 30)
nomarchy.yazi.enable true yazi TUI file manager, themed + curated plugins
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.plymouth.enable true Branded boot splash, background from the theme JSON (recolors on system rebuilds)
nomarchy.system.fileManager.enable true Thunar GUI + gvfs/tumbler/udisks2 (the "open folder" handler)
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)
rofi.rasi whole-swap: replaces the generated launcher/menu theme entirely

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

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
sys-update                             # update inputs + rebuild the system (snapshots first)
home-update                            # rebuild just the desktop layer

Keybinds: SUPER+Return terminal · SUPER+D launcher · SUPER+T theme picker · SUPER+SHIFT+T next wallpaper · SUPER+X power menu · SUPER+E file manager (yazi) · SUPER+N notifications · SUPER+CTRL+V clipboard history · 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

  • Menu system (apps launcher + theme switching + system actions), built on rofi 2.0 (native Wayland on 26.05) — its .rasi theme is baked from theme-state.json like every other app, with rich per-element styling:
    • ✓ shipped: modules/home/rofi.nix (per-element theme generated from the palette, themes/<slug>/rofi.rasi whole-swap) and the nomarchy-menu dispatcher: root picker (no args) · power (lock/logout/suspend/hibernate/reboot/shutdown, SUPER+X) · theme (SUPER+T) · clipboard (cliphist, SUPER+CTRL+V) · calc (qalc, copy/chain) · files (fd → xdg-open) · web (DuckDuckGo). SUPER+D is rofi -show drun.
    • next modules: Network ▸ nmtui · Bluetooth ▸ blueman · Capture ▸ screenshot · keybindings cheatsheet (parse the bind list → rofi, so SUPER-? shows every shortcut) · ask Claude: free-text → $TERMINAL -e claude "<question>" — the claude CLI auths via OAuth against a Pro/Max subscription (no API key); REPL stays open for follow-ups (claude-code in nixpkgs is unfree — allowUnfree is on)
    • planned binds: SUPER+Spacerofi -dmenu/quick-launch and SUPER+Mnomarchy-menu (main menu); SUPER+D stays -show drun
    • launcher icons: show-icons is off until the icon-theme item below lands (rofi needs an installed icon theme to show app icons)
    • decision record: resolves the old Walker/Lua question — no GTK4 launcher, no second theming pipeline; the dispatcher owns the menu structure, so the renderer stays swappable (we moved fuzzel → rofi 2.0 once mainline gained native Wayland, for its richer theming)
  • Theme parity with legacy: summer-day/night now carry their legacy bar layouts as waybar.jsonc whole-swaps (adapted: dead legacy script modules dropped, Nerd-Fonts-v2 codepoints remapped to FontAwesome/v3, logo button opens nomarchy-menu); the other four identity themes are palette recolors and already match. Remaining: a visual pass over all six on the live ISO
  • Per-theme rofi identity: the themes/<slug>/rofi.rasi whole-swap ships, and summer-day/night carry their legacy designs (inverted window, green inputbar, yellow bottom-border). Remaining: author .rasi identities for the other four ported themes if/when they want one (the generated palette theme is the default and looks fine)
  • 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
  • Greeter (tuigreet/SDDM) theming from the same JSON (Plymouth ships since v1: nomarchy.system.plymouth.*, background tinted from the state file)
  • Installer round 2: multi-disk BTRFS RAID, impermanence, BIOS/legacy boot (v1 nomarchy-install is single-disk UEFI — see pkgs/nomarchy-install)
  • swayosd (volume/brightness OSD), launch-or-focus UX scripts
  • Distro branding, round 2: distroName = "Nomarchy" ships (os-release PRETTY_NAME, systemd-boot entries, ISO menu label). Remaining: isoImage.splashImage/grubTheme art for the ISO boot screen, tuigreet/MOTD text, and the distroId question (it changes DEFAULT_HOSTNAME and upstream isNixos checks — needs a test pass; nixos-* CLI names stay regardless)
  • Theme-switch feedback: a switch runs home-manager switch (seconds to minutes), and today the only signal is the notify-send toast ("Applying theme — rebuilding…"). Add a visible progress indicator so a slow switch never reads as a failed selection — e.g. a spinner/progress widget from nomarchy-theme-sync (it already brackets the rebuild) or a Waybar/swaync element while the switch runs.
  • Icon themes: add icon to theme-state.json, have Stylix/GTK and rofi (show-icons) consume it, and ensure the chosen icon theme is pulled into the closure per active theme (a dark/light pair, e.g. Papirus-Dark/Light, picked to match mode). Unblocks rofi launcher icons and tidies Thunar/GTK app icons.
  • Nicer shell out of the box: ship and default-configure a prompt (starship, themed from the JSON) plus modern-CLI ergonomics — bat→cat, eza→ls, ripgrep→grep, fd→find, zoxide→cd — wired as aliases/$PAGER so the terminal is pleasant by default (overridable).
  • Default application suite: install a complete-workstation set (vscode, libreoffice, gimp, inkscape, texlive-full, …) behind a nomarchy.apps.* option surface so each is individually opt-out for users who want a leaner machine. Watch closure size (texlive-full is multi-GB — likely default-off or a lighter scheme).
  • Plymouth logo contrast: the shipped splash logo is too dark against the dark base background; recolor/lighten it (or tint from the palette accent/text instead of shipping a fixed navy asset) so it reads on both light and dark themes.
  • Hibernate double-unlock: on resume from hibernate the LUKS passphrase already gates the machine, but hypridle's before_sleep_cmd also locks hyprlock, so the user types a password twice. Lock only on suspend (not hibernate), or skip the hyprlock prompt when resuming from a LUKS-encrypted hibernate — see modules/home/idle.nix.
Description
NixOS based distribution with Omarchy flavour
Readme MIT 308 MiB
Languages
Nix 71.4%
Python 11.9%
Shell 11.8%
CSS 4.9%