Add a per-output monitor schema (name / resolution / position / scale / transform / mirror / bitdepth / vrr / disable) that generates Hyprland `monitor` rules, keeping the ,preferred,auto,1 wildcard as the fallback. Hyprland applies the rules on hotplug, so a declared external/dock output arranges itself on connect (no kanshi -- it fights Hyprland's own output management). Default [] -> wildcard only, so no change for existing setups; set wayland.windowManager.hyprland.settings.monitor directly to override. nwg-displays ships behind nomarchy.displays.enable (default true) as an interactive arranger -- a helper to discover values; the declarative config stays the source of truth (its output file isn't sourced). Remaining (roadmap): docked/undocked profile switching, workspace-to-monitor binding. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
168 lines
7.3 KiB
Nix
168 lines
7.3 KiB
Nix
# 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` | `<w>x<h>@<hz>` | `disable`.";
|
||
};
|
||
position = lib.mkOption {
|
||
type = lib.types.str;
|
||
default = "auto";
|
||
description = "`auto` | `auto-right`/`auto-left`/… | `<x>x<y>`.";
|
||
};
|
||
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.";
|
||
};
|
||
|
||
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 (<slug>/waybar.css, <slug>/waybar.jsonc). Point it
|
||
at your own directory to ship custom layouts downstream.
|
||
'';
|
||
};
|
||
|
||
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.";
|
||
};
|
||
};
|
||
}
|