# User-level `nomarchy.*` options — the full surface downstream users # configure in their home.nix. Kept small on purpose. { lib, pkgs, ... }: let # One output's layout — turned into a Hyprland `monitor` rule in # hyprland.nix. Friendlier than raw positional CSV; unset optional fields # are omitted from the rule. monitorType = lib.types.submodule { options = { name = lib.mkOption { type = lib.types.str; example = "HDMI-A-1"; description = "Output name (see `hyprctl monitors`), or a `desc:<…>` match."; }; resolution = lib.mkOption { type = lib.types.str; default = "preferred"; description = "`preferred` | `highres` | `highrr` | `x@` | `disable`."; }; position = lib.mkOption { type = lib.types.str; default = "auto"; description = "`auto` | `auto-right`/`auto-left`/… | `x`."; }; scale = lib.mkOption { type = lib.types.oneOf [ lib.types.str lib.types.int lib.types.float ]; default = 1; description = "Scale factor (1, 1.5, …) or `auto`."; }; transform = lib.mkOption { type = lib.types.nullOr lib.types.int; default = null; description = "Rotation 0–7 (90°/180°/270° = 1/2/3)."; }; mirror = lib.mkOption { type = lib.types.nullOr lib.types.str; default = null; description = "Mirror another output by name."; }; bitdepth = lib.mkOption { type = lib.types.nullOr lib.types.int; default = null; description = "Colour bit depth (8 or 10)."; }; vrr = lib.mkOption { type = lib.types.nullOr lib.types.int; default = null; description = "Variable refresh: 0 off, 1 on, 2 fullscreen-only."; }; extra = lib.mkOption { type = lib.types.str; default = ""; description = "Extra comma-separated Hyprland monitor args, appended verbatim."; }; }; }; in { options.nomarchy = { # ── Required ─────────────────────────────────────────────────── stateFile = lib.mkOption { type = lib.types.path; example = lib.literalExpression "./theme-state.json"; description = '' Path to theme-state.json, the single source of truth for all UI configuration. Must live inside your flake (so evaluation stays pure) and be git-tracked. nomarchy-theme-sync writes to the on-disk copy; rebuilds bake it into the generation. ''; }; # ── Preferences ──────────────────────────────────────────────── terminal = lib.mkOption { type = lib.types.str; default = "ghostty"; description = "Terminal emulator command, used by keybinds and $TERMINAL."; }; keyboard.layout = lib.mkOption { type = lib.types.str; default = "us"; example = "de"; description = '' XKB layout for the Hyprland session. The console (and the LUKS passphrase prompt) take theirs from the system side — the installer writes services.xserver.xkb + console.useXkbConfig into system.nix with the same value. ''; }; keyboard.variant = lib.mkOption { type = lib.types.str; default = ""; example = "nodeadkeys"; description = "XKB variant for the Hyprland session."; }; keyboard.layouts = lib.mkOption { type = lib.types.listOf lib.types.str; default = [ ]; example = [ "de" "fr" ]; description = '' Extra candidate layouts offered by the interactive new-keyboard picker. When non-empty, a small watcher runs in the session: when a keyboard connects that isn't covered by nomarchy.keyboard.devices and hasn't been chosen before, it pops a rofi picker (these layouts plus the primary nomarchy.keyboard.layout), applies the choice to that keyboard only (a per-device kb_layout, so the built-in board is left alone), and remembers it per-device (~/.local/state) — re-applying automatically on later reconnects. The runtime-remember complement to the declarative keyboard.devices; a stateful runtime piece by design. ''; }; keyboard.devices = lib.mkOption { type = lib.types.attrsOf (lib.types.submodule { options = { layout = lib.mkOption { type = lib.types.str; description = "XKB layout for this specific keyboard."; }; variant = lib.mkOption { type = lib.types.str; default = ""; description = "XKB variant for this keyboard."; }; }; }); default = { }; example = lib.literalExpression ''{ "keychron-keychron-k2" = { layout = "de"; }; }''; description = '' Per-device keyboard layout. The key is the device name from `hyprctl devices` (lower-case, hyphenated). Each entry generates a Hyprland `device` block that overrides the session-wide nomarchy.keyboard.layout for that keyboard only — e.g. an external keyboard that's physically a different layout than the laptop's built-in one. Hyprland applies it automatically whenever that keyboard is connected. ''; }; package = lib.mkOption { type = lib.types.package; default = pkgs.nomarchy-theme-sync; defaultText = lib.literalExpression "pkgs.nomarchy-theme-sync"; description = "The nomarchy-theme-sync package (provided by overlays.default)."; }; themesDir = lib.mkOption { type = lib.types.path; default = ../../themes; defaultText = lib.literalExpression "\"\${nomarchy}/themes\""; description = '' Theme presets/assets directory probed at eval time for per-theme app overrides (/waybar.css, /waybar.jsonc). Point it at your own directory to ship custom layouts downstream. ''; }; nightlight = { enable = lib.mkEnableOption '' a scheduled blue-light filter (hyprsunset): warm at night, no shift by day. Opt-in; tune the temperature + sunrise/sunset below''; temperature = lib.mkOption { type = lib.types.int; default = 4000; example = 3500; description = "Warm colour temperature (K) applied at night — lower is warmer."; }; sunrise = lib.mkOption { type = lib.types.str; default = "07:00"; example = "06:30"; description = "Time (HH:MM) the filter turns OFF — daytime, no colour shift."; }; sunset = lib.mkOption { type = lib.types.str; default = "20:00"; example = "21:00"; description = "Time (HH:MM) the filter turns ON — warm."; }; }; monitors = lib.mkOption { type = lib.types.listOf monitorType; default = [ ]; example = lib.literalExpression '' [ { name = "eDP-1"; position = "0x0"; } { name = "HDMI-A-1"; position = "auto-right"; scale = 1; } ] ''; description = '' Declarative per-output monitor layout. Each entry becomes a Hyprland `monitor` rule; Hyprland applies them on hotplug, so a declared external/dock output arranges itself when connected. The built-in `,preferred,auto,1` wildcard stays as the fallback for any output you don't list. For raw control set `wayland.windowManager.hyprland.settings.monitor` directly instead (it replaces this). Run `nwg-displays` (nomarchy.displays.enable) to find the right values interactively. ''; }; # ── Component toggles ────────────────────────────────────────── hyprland.enable = lib.mkEnableOption "Nomarchy's Hyprland configuration" // { default = true; }; waybar.enable = lib.mkEnableOption "Nomarchy's Waybar configuration" // { default = true; }; rofi.enable = lib.mkEnableOption "Nomarchy's themed rofi launcher + the nomarchy-menu dispatcher" // { default = true; }; swaync.enable = lib.mkEnableOption "swaync notifications, themed from the state file" // { default = true; }; idle.enable = lib.mkEnableOption "hyprlock + hypridle (idle lock, display off, suspend)" // { default = true; }; yazi.enable = lib.mkEnableOption "the yazi TUI file manager, themed with a curated plugin set" // { default = true; }; osd.enable = lib.mkEnableOption "swayosd on-screen display for volume/brightness/mute" // { default = true; }; shell.enable = lib.mkEnableOption "the zsh shell experience (starship prompt, bat/eza/zoxide)" // { default = true; }; keys.enable = lib.mkEnableOption "the SSH + GPG agent (gpg-agent fronting SSH, pinentry-qt)" // { default = true; }; fastfetch.enable = lib.mkEnableOption "fastfetch system info fronted by the themed Nomarchy logo" // { default = true; }; ghostty.enable = lib.mkEnableOption "Nomarchy's Ghostty configuration" // { default = true; }; btop.enable = lib.mkEnableOption "btop with the per-theme nomarchy theme" // { default = true; }; stylix.enable = lib.mkEnableOption "Stylix theming for the long tail of apps (GTK, Qt, cursors)" // { default = true; }; displays.enable = lib.mkEnableOption "the nwg-displays interactive monitor arranger (a helper to find nomarchy.monitors values; the declarative config stays the source of truth)" // { default = true; }; # ── Computed (read-only) ─────────────────────────────────────── theme = lib.mkOption { type = lib.types.attrs; readOnly = true; description = "The parsed theme state (stateFile merged over defaults)."; }; lib = lib.mkOption { type = lib.types.attrs; readOnly = true; description = "Color-format helpers shared by the theme consumers."; }; }; }