mirror of
https://github.com/DreamMaoMao/maomaowm.git
synced 2026-03-23 05:35:53 -04:00
refactor(nix): add structured config support to home-manager module
Convert settings from raw text to structured Nix attrs, following Hyprland's module pattern. Implementation based 1:1 on Hyprland's design - all credit to the Hyprland project. - Add nix/lib.nix with toMango conversion function - Support nested attrs, lists for duplicate keys - Add extraConfig, topPrefixes, bottomPrefixes options - Auto-add exec-once for autostart.sh Adapted for mangowc syntax (underscore separators vs colons).
This commit is contained in:
parent
62ab00a7a3
commit
9773b43592
2 changed files with 393 additions and 25 deletions
|
|
@ -1,18 +1,22 @@
|
||||||
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;
|
||||||
variables = lib.concatStringsSep " " cfg.systemd.variables;
|
variables = lib.concatStringsSep " " cfg.systemd.variables;
|
||||||
extraCommands = lib.concatStringsSep " && " cfg.systemd.extraCommands;
|
extraCommands = lib.concatStringsSep " && " cfg.systemd.extraCommands;
|
||||||
systemdActivation = ''${pkgs.dbus}/bin/dbus-update-activation-environment --systemd ${variables}; ${extraCommands}'';
|
systemdActivation = "${pkgs.dbus}/bin/dbus-update-activation-environment --systemd ${variables}; ${extraCommands}";
|
||||||
autostart_sh = pkgs.writeShellScript "autostart.sh" ''
|
autostart_sh = pkgs.writeShellScript "autostart.sh" ''
|
||||||
${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 {
|
||||||
|
|
@ -54,7 +58,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.
|
||||||
'';
|
'';
|
||||||
|
|
@ -75,32 +79,140 @@ in {
|
||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
settings = mkOption {
|
settings = mkOption {
|
||||||
description = "mango config content";
|
type =
|
||||||
type = types.lines;
|
with lib.types;
|
||||||
default = "";
|
let
|
||||||
example = ''
|
valueType =
|
||||||
# menu and terminal
|
nullOr (oneOf [
|
||||||
bind=Alt,space,spawn,rofi -show drun
|
bool
|
||||||
bind=Alt,Return,spawn,foot
|
int
|
||||||
|
float
|
||||||
|
str
|
||||||
|
path
|
||||||
|
(attrsOf valueType)
|
||||||
|
(listOf valueType)
|
||||||
|
])
|
||||||
|
// {
|
||||||
|
description = "Mango configuration value";
|
||||||
|
};
|
||||||
|
in
|
||||||
|
valueType;
|
||||||
|
default = { };
|
||||||
|
description = ''
|
||||||
|
Mango configuration written in Nix. Entries with the same key
|
||||||
|
should be written as lists. Variables and colors names should be
|
||||||
|
quoted. See <https://mangowc.vercel.app/docs> for more examples.
|
||||||
|
|
||||||
|
::: {.note}
|
||||||
|
This option uses a structured format that is converted to Mango's
|
||||||
|
configuration syntax. Nested attributes are flattened with underscore separators.
|
||||||
|
For example: `animation.duration_open = 400` becomes `animation_duration_open = 400`
|
||||||
|
:::
|
||||||
|
'';
|
||||||
|
example = lib.literalExpression ''
|
||||||
|
{
|
||||||
|
# Window effects
|
||||||
|
blur = 1;
|
||||||
|
blur_optimized = 1;
|
||||||
|
blur_params = {
|
||||||
|
radius = 5;
|
||||||
|
num_passes = 2;
|
||||||
|
};
|
||||||
|
border_radius = 6;
|
||||||
|
focused_opacity = 1.0;
|
||||||
|
|
||||||
|
# Animations - use underscores for multi-part keys
|
||||||
|
animations = 1;
|
||||||
|
animation_type_open = "slide";
|
||||||
|
animation_type_close = "slide";
|
||||||
|
animation_duration_open = 400;
|
||||||
|
animation_duration_close = 800;
|
||||||
|
|
||||||
|
# Or use nested attrs (will be flattened with underscores)
|
||||||
|
animation_curve = {
|
||||||
|
open = "0.46,1.0,0.29,1";
|
||||||
|
close = "0.08,0.92,0,1";
|
||||||
|
};
|
||||||
|
|
||||||
|
# Use lists for duplicate keys like bind and tagrule
|
||||||
|
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"
|
||||||
|
];
|
||||||
|
}
|
||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
|
extraConfig = mkOption {
|
||||||
|
type = types.lines;
|
||||||
|
default = "";
|
||||||
|
description = ''
|
||||||
|
Extra configuration lines to add to `~/.config/mango/config.conf`.
|
||||||
|
This is useful for advanced configurations that don't fit the structured
|
||||||
|
settings format, or for options that aren't yet supported by the module.
|
||||||
|
'';
|
||||||
|
example = ''
|
||||||
|
# Advanced config that doesn't fit structured format
|
||||||
|
special_option = 1
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
topPrefixes = mkOption {
|
||||||
|
type = with lib.types; listOf str;
|
||||||
|
default = [ ];
|
||||||
|
description = ''
|
||||||
|
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.
|
||||||
|
'';
|
||||||
|
example = [ "source" ];
|
||||||
|
};
|
||||||
|
bottomPrefixes = mkOption {
|
||||||
|
type = with lib.types; listOf str;
|
||||||
|
default = [ ];
|
||||||
|
description = ''
|
||||||
|
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.
|
||||||
|
'';
|
||||||
|
example = [ "source" ];
|
||||||
|
};
|
||||||
autostart_sh = mkOption {
|
autostart_sh = mkOption {
|
||||||
description = "WARRNING: This is a shell script, but no need to add shebang";
|
description = ''
|
||||||
|
Shell script to run on mango startup. No shebang needed.
|
||||||
|
|
||||||
|
When this option is set, the script will be written to
|
||||||
|
`~/.config/mango/autostart.sh` and an `exec-once` line
|
||||||
|
will be automatically added to the config to execute it.
|
||||||
|
'';
|
||||||
type = types.lines;
|
type = types.lines;
|
||||||
default = "";
|
default = "";
|
||||||
example = ''
|
example = ''
|
||||||
waybar &
|
waybar &
|
||||||
|
dunst &
|
||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
config = lib.mkIf cfg.enable {
|
config = lib.mkIf cfg.enable {
|
||||||
home.packages = [cfg.package];
|
home.packages = [ cfg.package ];
|
||||||
xdg.configFile = {
|
xdg.configFile = {
|
||||||
"mango/config.conf" = lib.mkIf (cfg.settings != "") {
|
"mango/config.conf" =
|
||||||
text = cfg.settings;
|
lib.mkIf (cfg.settings != { } || cfg.extraConfig != "" || cfg.autostart_sh != "")
|
||||||
};
|
{
|
||||||
|
text =
|
||||||
|
lib.optionalString (cfg.settings != { }) (
|
||||||
|
selflib.toMango {
|
||||||
|
topCommandsPrefixes = cfg.topPrefixes;
|
||||||
|
bottomCommandsPrefixes = cfg.bottomPrefixes;
|
||||||
|
} cfg.settings
|
||||||
|
)
|
||||||
|
+ lib.optionalString (cfg.extraConfig != "") cfg.extraConfig
|
||||||
|
+ lib.optionalString (cfg.autostart_sh != "") "\nexec-once=~/.config/mango/autostart.sh\n";
|
||||||
|
};
|
||||||
"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;
|
||||||
|
|
@ -109,14 +221,13 @@ 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";
|
||||||
++ lib.optional cfg.systemd.xdgAutostart "xdg-desktop-autostart.target";
|
After = [ "graphical-session-pre.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";
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
|
||||||
257
nix/lib.nix
Normal file
257
nix/lib.nix
Normal file
|
|
@ -0,0 +1,257 @@
|
||||||
|
lib:
|
||||||
|
let
|
||||||
|
inherit (lib)
|
||||||
|
attrNames
|
||||||
|
filterAttrs
|
||||||
|
foldl
|
||||||
|
generators
|
||||||
|
partition
|
||||||
|
;
|
||||||
|
|
||||||
|
inherit (lib.strings)
|
||||||
|
concatMapStrings
|
||||||
|
hasPrefix
|
||||||
|
;
|
||||||
|
|
||||||
|
/**
|
||||||
|
Convert a structured Nix attribute set into Mango's configuration format.
|
||||||
|
|
||||||
|
This function takes a nested attribute set and converts it into Mango-compatible
|
||||||
|
configuration syntax, supporting top, bottom, and regular command sections.
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
Configuration:
|
||||||
|
|
||||||
|
* `topCommandsPrefixes` - A list of prefixes to define **top** commands (default: `[]`).
|
||||||
|
* `bottomCommandsPrefixes` - A list of prefixes to define **bottom** commands (default: `[]`).
|
||||||
|
|
||||||
|
Attention:
|
||||||
|
|
||||||
|
- 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.
|
||||||
|
- Lists are converted into multiple entries, ensuring compatibility with Mango.
|
||||||
|
|
||||||
|
# Inputs
|
||||||
|
|
||||||
|
Structured function argument:
|
||||||
|
|
||||||
|
: topCommandsPrefixes (optional, default: `[]`)
|
||||||
|
: 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.
|
||||||
|
: bottomCommandsPrefixes (optional, default: `[]`)
|
||||||
|
: 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.
|
||||||
|
|
||||||
|
Value:
|
||||||
|
|
||||||
|
: The attribute set to be converted to Hyprland configuration format.
|
||||||
|
|
||||||
|
# Type
|
||||||
|
|
||||||
|
```
|
||||||
|
toMango :: AttrSet -> AttrSet -> String
|
||||||
|
```
|
||||||
|
|
||||||
|
# Examples
|
||||||
|
:::{.example}
|
||||||
|
|
||||||
|
## Basic mangowc configuration
|
||||||
|
|
||||||
|
```nix
|
||||||
|
let
|
||||||
|
config = {
|
||||||
|
blur = 1;
|
||||||
|
blur_params_radius = 5;
|
||||||
|
border_radius = 6;
|
||||||
|
animations = 1;
|
||||||
|
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;
|
||||||
|
};
|
||||||
|
animation_curve = {
|
||||||
|
open = "0.46,1.0,0.29,1";
|
||||||
|
close = "0.08,0.92,0,1";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
in lib.toMango {} config
|
||||||
|
```
|
||||||
|
|
||||||
|
**Output:**
|
||||||
|
```
|
||||||
|
animation_curve_close = 0.08,0.92,0,1
|
||||||
|
animation_curve_open = 0.46,1.0,0.29,1
|
||||||
|
blur = 1
|
||||||
|
blur_params_noise = 0.02
|
||||||
|
blur_params_num_passes = 2
|
||||||
|
blur_params_radius = 5
|
||||||
|
```
|
||||||
|
|
||||||
|
## Using lists for duplicate keys
|
||||||
|
|
||||||
|
```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
|
||||||
|
```
|
||||||
|
|
||||||
|
:::
|
||||||
|
*/
|
||||||
|
toMango =
|
||||||
|
{
|
||||||
|
topCommandsPrefixes ? [ ],
|
||||||
|
bottomCommandsPrefixes ? [ ],
|
||||||
|
}:
|
||||||
|
attrs:
|
||||||
|
let
|
||||||
|
toMango' =
|
||||||
|
attrs:
|
||||||
|
let
|
||||||
|
# Specially configured `toKeyValue` generator with support for duplicate keys
|
||||||
|
# and a legible key-value separator.
|
||||||
|
mkCommands = generators.toKeyValue {
|
||||||
|
mkKeyValue = generators.mkKeyValueDefault { } " = ";
|
||||||
|
listsAsDuplicateKeys = true;
|
||||||
|
indent = ""; # No indent, since we don't have nesting
|
||||||
|
};
|
||||||
|
|
||||||
|
# Flatten the attrset, combining keys in a "path" like `"a_b_c" = "x"`.
|
||||||
|
# Uses `flattenAttrs` with an underscore separator.
|
||||||
|
commands = flattenAttrs (p: k: "${p}_${k}") attrs;
|
||||||
|
|
||||||
|
# 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;
|
||||||
|
|
||||||
|
# Partition keys into top commands and the rest
|
||||||
|
result = partition (filterCommands topCommandsPrefixes) (attrNames commands);
|
||||||
|
topCommands = filterAttrs (n: _: builtins.elem n result.right) commands;
|
||||||
|
remainingCommands = removeAttrs commands result.right;
|
||||||
|
|
||||||
|
# Partition remaining commands into bottom commands and regular commands
|
||||||
|
result2 = partition (filterCommands bottomCommandsPrefixes) result.wrong;
|
||||||
|
bottomCommands = filterAttrs (n: _: builtins.elem n result2.right) remainingCommands;
|
||||||
|
regularCommands = removeAttrs remainingCommands result2.right;
|
||||||
|
in
|
||||||
|
# Concatenate strings from mapping `mkCommands` over top, regular, and bottom commands.
|
||||||
|
concatMapStrings mkCommands [
|
||||||
|
topCommands
|
||||||
|
regularCommands
|
||||||
|
bottomCommands
|
||||||
|
];
|
||||||
|
in
|
||||||
|
toMango' attrs;
|
||||||
|
|
||||||
|
/**
|
||||||
|
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
|
||||||
|
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.
|
||||||
|
|
||||||
|
Configuration:
|
||||||
|
|
||||||
|
* `pred` - A function `(string -> string -> string)` defining how keys should be concatenated.
|
||||||
|
|
||||||
|
# Inputs
|
||||||
|
|
||||||
|
Structured function argument:
|
||||||
|
|
||||||
|
: pred (required)
|
||||||
|
: 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.
|
||||||
|
|
||||||
|
Value:
|
||||||
|
|
||||||
|
: The nested attribute set to be flattened.
|
||||||
|
|
||||||
|
# Type
|
||||||
|
|
||||||
|
```
|
||||||
|
flattenAttrs :: (String -> String -> String) -> AttrSet -> AttrSet
|
||||||
|
```
|
||||||
|
|
||||||
|
# Examples
|
||||||
|
:::{.example}
|
||||||
|
|
||||||
|
```nix
|
||||||
|
let
|
||||||
|
nested = {
|
||||||
|
a = "3";
|
||||||
|
b = { c = "4"; d = "5"; };
|
||||||
|
};
|
||||||
|
|
||||||
|
separator = (prefix: key: "${prefix}.${key}"); # Use dot notation
|
||||||
|
in lib.flattenAttrs separator nested
|
||||||
|
```
|
||||||
|
|
||||||
|
**Output:**
|
||||||
|
```nix
|
||||||
|
{
|
||||||
|
"a" = "3";
|
||||||
|
"b.c" = "4";
|
||||||
|
"b.d" = "5";
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
:::
|
||||||
|
*/
|
||||||
|
flattenAttrs =
|
||||||
|
pred: attrs:
|
||||||
|
let
|
||||||
|
flattenAttrs' =
|
||||||
|
prefix: attrs:
|
||||||
|
builtins.foldl' (
|
||||||
|
acc: key:
|
||||||
|
let
|
||||||
|
value = attrs.${key};
|
||||||
|
newKey = if prefix == "" then key else pred prefix key;
|
||||||
|
in
|
||||||
|
acc // (if builtins.isAttrs value then flattenAttrs' newKey value else { "${newKey}" = value; })
|
||||||
|
) { } (builtins.attrNames attrs);
|
||||||
|
in
|
||||||
|
flattenAttrs' "" attrs;
|
||||||
|
in
|
||||||
|
{
|
||||||
|
inherit flattenAttrs toMango;
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue