refactor: format nix code with alejandra & rename nix files for clarity

This commit is contained in:
pengo 2026-06-08 17:38:35 -06:00
parent bb86ea12ca
commit df492dab6b
6 changed files with 298 additions and 311 deletions

View file

@ -40,25 +40,25 @@
system, system,
}: let }: let
inherit (pkgs) callPackage; inherit (pkgs) callPackage;
mango = callPackage ./nix { mango = callPackage ./nix/package.nix {
inherit (scenefx.packages.${system}) scenefx; inherit (scenefx.packages.${system}) scenefx;
}; };
in { in {
inherit mango; inherit mango;
default = mango; default = mango;
hm-options-json = callPackage (import ./nix/generate-options.nix self) { hm-options-json = callPackage (import ./nix/generate-options.nix self) {
module = ./nix/hm-modules.nix; module = ./nix/hm-module.nix;
optionPrefix = "wayland.windowManager.mango."; optionPrefix = "wayland.windowManager.mango.";
}; };
nixos-options-json = callPackage (import ./nix/generate-options.nix self) { nixos-options-json = callPackage (import ./nix/generate-options.nix self) {
module = ./nix/nixos-modules.nix; module = ./nix/nixos-module.nix;
optionPrefix = "programs.mango."; optionPrefix = "programs.mango.";
}; };
} }
); );
nixosModules.mango = import ./nix/nixos-modules.nix self; nixosModules.mango = import ./nix/nixos-module.nix self;
hmModules.mango = import ./nix/hm-modules.nix self; hmModules.mango = import ./nix/hm-module.nix self;
devShells = forEachSystem ( devShells = forEachSystem (
{system, ...}: { {system, ...}: {

View file

@ -1,20 +1,18 @@
self: self: {
{
pkgs, pkgs,
lib ? pkgs.lib, lib ? pkgs.lib,
module, module,
optionPrefix, optionPrefix,
}: }: let
let
# Absolute store path of the flake root, used to compute relative subpaths # Absolute store path of the flake root, used to compute relative subpaths
repoPath = toString self; repoPath = toString self;
eval = lib.evalModules { eval = lib.evalModules {
modules = [ modules = [
(import module self) (import module self)
{ _module.check = false; } {_module.check = false;}
]; ];
specialArgs = { inherit pkgs; }; specialArgs = {inherit pkgs;};
}; };
# Relative path of the module file within the repo (e.g. "nix/hm-modules.nix") # Relative path of the module file within the repo (e.g. "nix/hm-modules.nix")
@ -28,15 +26,14 @@ let
optionsDoc = pkgs.nixosOptionsDoc { optionsDoc = pkgs.nixosOptionsDoc {
options = eval.options; options = eval.options;
transformOptions = transformOptions = opt:
opt:
opt opt
// { // {
visible = opt.visible && !opt.internal; visible = opt.visible && !opt.internal;
# Strip the option prefix so docs show "enable" instead of "programs.mango.enable" # Strip the option prefix so docs show "enable" instead of "programs.mango.enable"
name = lib.removePrefix optionPrefix opt.name; name = lib.removePrefix optionPrefix opt.name;
declarations = [ moduleDeclaration ]; declarations = [moduleDeclaration];
}; };
}; };
in in
optionsDoc.optionsJSON optionsDoc.optionsJSON

View file

@ -1,11 +1,9 @@
self: self: {
{
lib, lib,
config, config,
pkgs, pkgs,
... ...
}: }: let
let
cfg = config.wayland.windowManager.mango; cfg = config.wayland.windowManager.mango;
selflib = import ./lib.nix lib; selflib = import ./lib.nix lib;
variables = lib.concatStringsSep " " cfg.systemd.variables; variables = lib.concatStringsSep " " cfg.systemd.variables;
@ -15,8 +13,7 @@ let
${lib.optionalString cfg.systemd.enable systemdActivation} ${lib.optionalString cfg.systemd.enable systemdActivation}
${cfg.autostart_sh} ${cfg.autostart_sh}
''; '';
in in {
{
options = { options = {
wayland.windowManager.mango = with lib; { wayland.windowManager.mango = with lib; {
enable = mkOption { enable = mkOption {
@ -59,7 +56,7 @@ in
"XCURSOR_THEME" "XCURSOR_THEME"
"XCURSOR_SIZE" "XCURSOR_SIZE"
]; ];
example = [ "--all" ]; example = ["--all"];
description = '' description = ''
Environment variables imported into the systemd and D-Bus user environment. Environment variables imported into the systemd and D-Bus user environment.
''; '';
@ -80,25 +77,23 @@ in
''; '';
}; };
settings = mkOption { settings = mkOption {
type = type = with lib.types; let
with lib.types; valueType =
let nullOr (oneOf [
valueType = bool
nullOr (oneOf [ int
bool float
int str
float path
str (attrsOf valueType)
path (listOf valueType)
(attrsOf valueType) ])
(listOf valueType) // {
]) description = "Mango configuration value";
// { };
description = "Mango configuration value"; in
};
in
valueType; valueType;
default = { }; default = {};
description = '' description = ''
Mango configuration written in Nix. Entries with the same key Mango configuration written in Nix. Entries with the same key
should be written as lists. Variables and colors names should be should be written as lists. Variables and colors names should be
@ -178,21 +173,21 @@ in
}; };
topPrefixes = mkOption { topPrefixes = mkOption {
type = with lib.types; listOf str; type = with lib.types; listOf str;
default = [ ]; default = [];
description = '' description = ''
List of prefixes for attributes that should appear at the top of the config file. List of prefixes for attributes that should appear at the top of the config file.
Attributes starting with these prefixes will be sorted to the beginning. Attributes starting with these prefixes will be sorted to the beginning.
''; '';
example = [ "source" ]; example = ["source"];
}; };
bottomPrefixes = mkOption { bottomPrefixes = mkOption {
type = with lib.types; listOf str; type = with lib.types; listOf str;
default = [ ]; default = [];
description = '' description = ''
List of prefixes for attributes that should appear at the bottom of the config file. List of prefixes for attributes that should appear at the bottom of the config file.
Attributes starting with these prefixes will be sorted to the end. Attributes starting with these prefixes will be sorted to the end.
''; '';
example = [ "source" ]; example = ["source"];
}; };
autostart_sh = mkOption { autostart_sh = mkOption {
description = '' description = ''
@ -217,25 +212,25 @@ in
finalConfigText = finalConfigText =
# Support old string-based config during transition period # Support old string-based config during transition period
( (
if builtins.isString cfg.settings then if builtins.isString cfg.settings
cfg.settings then cfg.settings
else else
lib.optionalString (cfg.settings != { }) ( lib.optionalString (cfg.settings != {}) (
selflib.toMango { selflib.toMango {
topCommandsPrefixes = cfg.topPrefixes; topCommandsPrefixes = cfg.topPrefixes;
bottomCommandsPrefixes = cfg.bottomPrefixes; bottomCommandsPrefixes = cfg.bottomPrefixes;
} cfg.settings }
cfg.settings
) )
) )
+ lib.optionalString (cfg.extraConfig != "") cfg.extraConfig + lib.optionalString (cfg.extraConfig != "") cfg.extraConfig
+ lib.optionalString (cfg.autostart_sh != "") "\nexec-once=~/.config/mango/autostart.sh\n"; + lib.optionalString (cfg.autostart_sh != "") "\nexec-once=~/.config/mango/autostart.sh\n";
validatedConfig = pkgs.runCommand "mango-config.conf" { } '' validatedConfig = pkgs.runCommand "mango-config.conf" {} ''
cp ${pkgs.writeText "mango-config.conf" finalConfigText} "$out" cp ${pkgs.writeText "mango-config.conf" finalConfigText} "$out"
${cfg.package}/bin/mango -c "$out" -p || exit 1 ${cfg.package}/bin/mango -c "$out" -p || exit 1
''; '';
in in {
{
# Backwards compatibility warning for old string-based config # Backwards compatibility warning for old string-based config
warnings = lib.optional (builtins.isString cfg.settings) '' warnings = lib.optional (builtins.isString cfg.settings) ''
wayland.windowManager.mango.settings: Using a string for settings is deprecated. wayland.windowManager.mango.settings: Using a string for settings is deprecated.
@ -244,13 +239,13 @@ in
The old string format will be removed in a future release. The old string format will be removed in a future release.
''; '';
home.packages = [ cfg.package ]; home.packages = [cfg.package];
xdg.configFile = { xdg.configFile = {
"mango/config.conf" = "mango/config.conf" =
lib.mkIf (cfg.settings != { } || cfg.extraConfig != "" || cfg.autostart_sh != "") lib.mkIf (cfg.settings != {} || cfg.extraConfig != "" || cfg.autostart_sh != "")
{ {
source = validatedConfig; source = validatedConfig;
}; };
"mango/autostart.sh" = lib.mkIf (cfg.autostart_sh != "") { "mango/autostart.sh" = lib.mkIf (cfg.autostart_sh != "") {
source = autostart_sh; source = autostart_sh;
executable = true; executable = true;
@ -259,13 +254,14 @@ in
systemd.user.targets.mango-session = lib.mkIf cfg.systemd.enable { systemd.user.targets.mango-session = lib.mkIf cfg.systemd.enable {
Unit = { Unit = {
Description = "mango compositor session"; Description = "mango compositor session";
Documentation = [ "man:systemd.special(7)" ]; Documentation = ["man:systemd.special(7)"];
BindsTo = [ "graphical-session.target" ]; BindsTo = ["graphical-session.target"];
Wants = [ Wants =
"graphical-session-pre.target" [
] "graphical-session-pre.target"
++ lib.optional cfg.systemd.xdgAutostart "xdg-desktop-autostart.target"; ]
After = [ "graphical-session-pre.target" ]; ++ lib.optional cfg.systemd.xdgAutostart "xdg-desktop-autostart.target";
After = ["graphical-session-pre.target"];
Before = lib.optional cfg.systemd.xdgAutostart "xdg-desktop-autostart.target"; Before = lib.optional cfg.systemd.xdgAutostart "xdg-desktop-autostart.target";
}; };
}; };

View file

@ -1,6 +1,6 @@
lib: lib: let
let inherit
inherit (lib) (lib)
attrNames attrNames
filterAttrs filterAttrs
foldl foldl
@ -9,304 +9,299 @@ let
removeAttrs removeAttrs
; ;
inherit (lib.strings) inherit
(lib.strings)
concatMapStrings concatMapStrings
hasPrefix hasPrefix
; ;
/** /**
Convert a structured Nix attribute set into Mango's configuration format. Convert a structured Nix attribute set into Mango's configuration format.
This function takes a nested attribute set and converts it into Mango-compatible This function takes a nested attribute set and converts it into Mango-compatible
configuration syntax, supporting top, bottom, and regular command sections. configuration syntax, supporting top, bottom, and regular command sections.
Commands are flattened using the `flattenAttrs` function, and attributes are formatted as Commands are flattened using the `flattenAttrs` function, and attributes are formatted as
`key = value` pairs. Lists are expanded as duplicate keys to match Mango's expected format. `key = value` pairs. Lists are expanded as duplicate keys to match Mango's expected format.
Configuration: Configuration:
* `topCommandsPrefixes` - A list of prefixes to define **top** commands (default: `[]`). * `topCommandsPrefixes` - A list of prefixes to define **top** commands (default: `[]`).
* `bottomCommandsPrefixes` - A list of prefixes to define **bottom** commands (default: `[]`). * `bottomCommandsPrefixes` - A list of prefixes to define **bottom** commands (default: `[]`).
Attention: Attention:
- The function ensures top commands appear **first** and bottom commands **last**. - The function ensures top commands appear **first** and bottom commands **last**.
- The generated configuration is a **single string**, suitable for writing to a config file. - The generated configuration is a **single string**, suitable for writing to a config file.
- Lists are converted into multiple entries, ensuring compatibility with Mango. - Lists are converted into multiple entries, ensuring compatibility with Mango.
# Inputs # Inputs
Structured function argument: Structured function argument:
: topCommandsPrefixes (optional, default: `[]`) : topCommandsPrefixes (optional, default: `[]`)
: A list of prefixes that define **top** commands. Any key starting with one of these : A list of prefixes that define **top** commands. Any key starting with one of these
prefixes will be placed at the beginning of the configuration. prefixes will be placed at the beginning of the configuration.
: bottomCommandsPrefixes (optional, default: `[]`) : bottomCommandsPrefixes (optional, default: `[]`)
: A list of prefixes that define **bottom** commands. Any key starting with one of these : A list of prefixes that define **bottom** commands. Any key starting with one of these
prefixes will be placed at the end of the configuration. prefixes will be placed at the end of the configuration.
Value: Value:
: The attribute set to be converted to Hyprland configuration format. : The attribute set to be converted to Hyprland configuration format.
# Type # Type
``` ```
toMango :: AttrSet -> AttrSet -> String toMango :: AttrSet -> AttrSet -> String
``` ```
# Examples # Examples
:::{.example} :::{.example}
## Basic mangowc configuration ## Basic mangowc configuration
```nix ```nix
let let
config = { config = {
blur = 1; blur = 1;
blur_params_radius = 5; blur_params_radius = 5;
border_radius = 6; border_radius = 6;
animations = 1; animations = 1;
animation_duration_open = 400; animation_duration_open = 400;
};
in lib.toMango {} config
```
**Output:**
```
animations = 1
animation_duration_open = 400
blur = 1
blur_params_radius = 5
border_radius = 6
```
## Using nested attributes
```nix
let
config = {
blur = 1;
blur_params = {
radius = 5;
num_passes = 2;
noise = 0.02;
}; };
in lib.toMango {} config animation_curve = {
``` open = "0.46,1.0,0.29,1";
close = "0.08,0.92,0,1";
};
};
in lib.toMango {} config
```
**Output:** **Output:**
``` ```
animations = 1 animation_curve_close = 0.08,0.92,0,1
animation_duration_open = 400 animation_curve_open = 0.46,1.0,0.29,1
blur = 1 blur = 1
blur_params_radius = 5 blur_params_noise = 0.02
border_radius = 6 blur_params_num_passes = 2
``` blur_params_radius = 5
```
## Using nested attributes ## Using lists for duplicate keys
```nix ```nix
let let
config = { config = {
blur = 1; bind = [
blur_params = { "SUPER,r,reload_config"
radius = 5; "Alt,space,spawn,rofi -show drun"
num_passes = 2; "Alt,Return,spawn,foot"
noise = 0.02; ];
}; tagrule = [
animation_curve = { "id:1,layout_name:tile"
open = "0.46,1.0,0.29,1"; "id:2,layout_name:scroller"
close = "0.08,0.92,0,1"; ];
};
in lib.toMango {} config
```
**Output:**
```
bind = SUPER,r,reload_config
bind = Alt,space,spawn,rofi -show drun
bind = Alt,Return,spawn,foot
tagrule = id:1,layout_name:tile
tagrule = id:2,layout_name:scroller
```
## Using keymodes (submaps)
```nix
let
config = {
bind = [
"SUPER,Q,killclient"
"ALT,R,setkeymode,resize"
];
keymode = {
resize = {
bind = [
"NONE,Left,resizewin,-10,0"
"NONE,Right,resizewin,10,0"
"NONE,Escape,setkeymode,default"
];
}; };
}; };
in lib.toMango {} config };
``` in lib.toMango {} config
```
**Output:** **Output:**
``` ```
animation_curve_close = 0.08,0.92,0,1 bind = SUPER,Q,killclient
animation_curve_open = 0.46,1.0,0.29,1 bind = ALT,R,setkeymode,resize
blur = 1
blur_params_noise = 0.02
blur_params_num_passes = 2
blur_params_radius = 5
```
## Using lists for duplicate keys keymode = resize
bind = NONE,Left,resizewin,-10,0
bind = NONE,Right,resizewin,10,0
bind = NONE,Escape,setkeymode,default
```
```nix :::
let
config = {
bind = [
"SUPER,r,reload_config"
"Alt,space,spawn,rofi -show drun"
"Alt,Return,spawn,foot"
];
tagrule = [
"id:1,layout_name:tile"
"id:2,layout_name:scroller"
];
};
in lib.toMango {} config
```
**Output:**
```
bind = SUPER,r,reload_config
bind = Alt,space,spawn,rofi -show drun
bind = Alt,Return,spawn,foot
tagrule = id:1,layout_name:tile
tagrule = id:2,layout_name:scroller
```
## Using keymodes (submaps)
```nix
let
config = {
bind = [
"SUPER,Q,killclient"
"ALT,R,setkeymode,resize"
];
keymode = {
resize = {
bind = [
"NONE,Left,resizewin,-10,0"
"NONE,Right,resizewin,10,0"
"NONE,Escape,setkeymode,default"
];
};
};
};
in lib.toMango {} config
```
**Output:**
```
bind = SUPER,Q,killclient
bind = ALT,R,setkeymode,resize
keymode = resize
bind = NONE,Left,resizewin,-10,0
bind = NONE,Right,resizewin,10,0
bind = NONE,Escape,setkeymode,default
```
:::
*/ */
toMango = toMango = {
{ topCommandsPrefixes ? [],
topCommandsPrefixes ? [ ], bottomCommandsPrefixes ? [],
bottomCommandsPrefixes ? [ ], }: attrs: let
}: toMango' = attrs: let
attrs: # Specially configured `toKeyValue` generator with support for duplicate keys
let # and a legible key-value separator.
toMango' = mkCommands = generators.toKeyValue {
attrs: mkKeyValue = generators.mkKeyValueDefault {} " = ";
let listsAsDuplicateKeys = true;
# Specially configured `toKeyValue` generator with support for duplicate keys indent = ""; # No indent, since we don't have nesting
# and a legible key-value separator. };
mkCommands = generators.toKeyValue {
mkKeyValue = generators.mkKeyValueDefault { } " = ";
listsAsDuplicateKeys = true;
indent = ""; # No indent, since we don't have nesting
};
# Extract keymode definitions if they exist # Extract keymode definitions if they exist
keymodes = attrs.keymode or { }; keymodes = attrs.keymode or {};
attrsWithoutKeymodes = removeAttrs attrs [ "keymode" ]; attrsWithoutKeymodes = removeAttrs attrs ["keymode"];
# Generate keymode blocks # Generate keymode blocks
# Format: keymode=name\nbind=...\nbind=...\n # Format: keymode=name\nbind=...\nbind=...\n
mkKeymodeBlock = mkKeymodeBlock = name: modeAttrs: let
name: modeAttrs: modeCommands = flattenAttrs (p: k: "${p}_${k}") modeAttrs;
let in "keymode = ${name}\n${mkCommands modeCommands}";
modeCommands = flattenAttrs (p: k: "${p}_${k}") modeAttrs;
in
"keymode = ${name}\n${mkCommands modeCommands}";
keymodeBlocks = keymodeBlocks =
if keymodes == { } then if keymodes == {}
"" then ""
else else "\n" + concatMapStrings (name: mkKeymodeBlock name keymodes.${name} + "\n") (attrNames keymodes);
"\n" + concatMapStrings (name: mkKeymodeBlock name keymodes.${name} + "\n") (attrNames keymodes);
# Flatten the attrset, combining keys in a "path" like `"a_b_c" = "x"`. # Flatten the attrset, combining keys in a "path" like `"a_b_c" = "x"`.
# Uses `flattenAttrs` with an underscore separator. # Uses `flattenAttrs` with an underscore separator.
commands = flattenAttrs (p: k: "${p}_${k}") attrsWithoutKeymodes; commands = flattenAttrs (p: k: "${p}_${k}") attrsWithoutKeymodes;
# General filtering function to check if a key starts with any prefix in a given list. # General filtering function to check if a key starts with any prefix in a given list.
filterCommands = list: n: foldl (acc: prefix: acc || hasPrefix prefix n) false list; filterCommands = list: n: foldl (acc: prefix: acc || hasPrefix prefix n) false list;
# Partition keys into top commands and the rest # Partition keys into top commands and the rest
result = partition (filterCommands topCommandsPrefixes) (attrNames commands); result = partition (filterCommands topCommandsPrefixes) (attrNames commands);
topCommands = filterAttrs (n: _: builtins.elem n result.right) commands; topCommands = filterAttrs (n: _: builtins.elem n result.right) commands;
remainingCommands = removeAttrs commands result.right; remainingCommands = removeAttrs commands result.right;
# Partition remaining commands into bottom commands and regular commands # Partition remaining commands into bottom commands and regular commands
result2 = partition (filterCommands bottomCommandsPrefixes) result.wrong; result2 = partition (filterCommands bottomCommandsPrefixes) result.wrong;
bottomCommands = filterAttrs (n: _: builtins.elem n result2.right) remainingCommands; bottomCommands = filterAttrs (n: _: builtins.elem n result2.right) remainingCommands;
regularCommands = removeAttrs remainingCommands result2.right; regularCommands = removeAttrs remainingCommands result2.right;
in
# Concatenate strings from mapping `mkCommands` over top, regular, and bottom commands.
# Keymodes are appended at the end.
concatMapStrings mkCommands [
topCommands
regularCommands
bottomCommands
]
+ keymodeBlocks;
in in
# Concatenate strings from mapping `mkCommands` over top, regular, and bottom commands.
# Keymodes are appended at the end.
concatMapStrings mkCommands [
topCommands
regularCommands
bottomCommands
]
+ keymodeBlocks;
in
toMango' attrs; toMango' attrs;
/** /**
Flatten a nested attribute set into a flat attribute set, using a custom key separator function. Flatten a nested attribute set into a flat attribute set, using a custom key separator function.
This function recursively traverses a nested attribute set and produces a flat attribute set This function recursively traverses a nested attribute set and produces a flat attribute set
where keys are joined using a user-defined function (`pred`). It allows transforming deeply where keys are joined using a user-defined function (`pred`). It allows transforming deeply
nested structures into a single-level attribute set while preserving key-value relationships. nested structures into a single-level attribute set while preserving key-value relationships.
Configuration: Configuration:
* `pred` - A function `(string -> string -> string)` defining how keys should be concatenated. * `pred` - A function `(string -> string -> string)` defining how keys should be concatenated.
# Inputs # Inputs
Structured function argument: Structured function argument:
: pred (required) : pred (required)
: A function that determines how parent and child keys should be combined into a single key. : A function that determines how parent and child keys should be combined into a single key.
It takes a `prefix` (parent key) and `key` (current key) and returns the joined key. It takes a `prefix` (parent key) and `key` (current key) and returns the joined key.
Value: Value:
: The nested attribute set to be flattened. : The nested attribute set to be flattened.
# Type # Type
``` ```
flattenAttrs :: (String -> String -> String) -> AttrSet -> AttrSet flattenAttrs :: (String -> String -> String) -> AttrSet -> AttrSet
``` ```
# Examples # Examples
:::{.example} :::{.example}
```nix ```nix
let let
nested = { nested = {
a = "3"; a = "3";
b = { c = "4"; d = "5"; }; b = { c = "4"; d = "5"; };
}; };
separator = (prefix: key: "${prefix}.${key}"); # Use dot notation separator = (prefix: key: "${prefix}.${key}"); # Use dot notation
in lib.flattenAttrs separator nested in lib.flattenAttrs separator nested
``` ```
**Output:** **Output:**
```nix ```nix
{ {
"a" = "3"; "a" = "3";
"b.c" = "4"; "b.c" = "4";
"b.d" = "5"; "b.d" = "5";
} }
``` ```
::: :::
*/ */
flattenAttrs = flattenAttrs = pred: attrs: let
pred: attrs: flattenAttrs' = prefix: attrs:
let builtins.foldl' (
flattenAttrs' = acc: key: let
prefix: attrs: value = attrs.${key};
builtins.foldl' ( newKey =
acc: key: if prefix == ""
let then key
value = attrs.${key}; else pred prefix key;
newKey = if prefix == "" then key else pred prefix key; in
in acc
acc // (if builtins.isAttrs value then flattenAttrs' newKey value else { "${newKey}" = value; }) // (
) { } (builtins.attrNames attrs); if builtins.isAttrs value
in then flattenAttrs' newKey value
else {"${newKey}" = value;}
)
) {} (builtins.attrNames attrs);
in
flattenAttrs' "" attrs; flattenAttrs' "" attrs;
in in {
{
inherit flattenAttrs toMango; inherit flattenAttrs toMango;
} }

View file

@ -23,10 +23,9 @@ in {
}; };
config = lib.mkIf cfg.enable { config = lib.mkIf cfg.enable {
environment.systemPackages = environment.systemPackages = [
[ cfg.package
cfg.package ];
];
xdg.portal = { xdg.portal = {
enable = lib.mkDefault true; enable = lib.mkDefault true;
@ -60,7 +59,7 @@ in {
programs.xwayland.enable = lib.mkDefault true; programs.xwayland.enable = lib.mkDefault true;
services = { services = {
displayManager.sessionPackages = lib.mkIf cfg.addLoginEntry [ cfg.package ]; displayManager.sessionPackages = lib.mkIf cfg.addLoginEntry [cfg.package];
graphical-desktop.enable = lib.mkDefault true; graphical-desktop.enable = lib.mkDefault true;
}; };