niri: move config to nix

Signed-off-by: faukah <fau@faukah.com>
Change-Id: I6a6a6964cffaba7ce75488a48739398758d49363
This commit is contained in:
fau 2025-07-27 18:06:22 +02:00 committed by faukah
commit c5915e1d24
4 changed files with 690 additions and 434 deletions

View file

@ -1,433 +0,0 @@
input {
keyboard {
xkb {
layout "us,ru"
variant ",phonetic_winkeys"
options "grp:rctrl_rshift_toggle, compose:102"
}
repeat-rate 60
repeat-delay 200
numlock
}
touchpad {
tap
}
// focus-follows-mouse
focus-follows-mouse max-scroll-amount="0%"
}
// Laptop
output "eDP-1" {
mode "1920x1080@59.999"
scale 1
position x=0 y=0
}
// PC
output "DP-2" {
mode "1920x1080@59.999"
scale 1
position x=0 y=0
}
output "HDMI-A-1" {
mode "1920x1080@59.999"
scale 1
position x=1920 y=0
}
layout {
gaps 0
center-focused-column "on-overflow"
focus-ring {
width 2
active-color "#f9e2af"
inactive-color "transparent"
active-gradient from="#E5989B" to="#FFB4A2" angle=45 relative-to="workspace-view" in="oklch longer hue"
}
border {
off
}
tab-indicator {
width 2
gap 4
gaps-between-tabs 2
}
shadow {
on
}
insert-hint {
color "#ffc87f80"
gradient from="#f38ba880" to="#f9e2af80" angle=45 relative-to="workspace-view"
}
preset-column-widths {
proportion 0.33333
proportion 0.5
proportion 0.66667
}
default-column-width {
proportion 0.5
}
background-color "transparent"
}
spawn-at-startup "waybar"
spawn-at-startup "nu" "-c" "quickshell --path ~/repos/projects/nichts/main/modules/style/quickshell/shell"
environment {
DISPLAY ":0"
}
layer-rule {
match namespace="overview$"
place-within-backdrop true
}
// prefer no client side decorations
prefer-no-csd
switch-events {
lid-close {
spawn "swaylock"
}
}
overview {
zoom 0.6
backdrop-color "#777777"
}
animations {
window-close {
duration-ms 250
curve "linear"
custom-shader "\n vec4 fall_and_rotate(vec3 coords_geo, vec3 size_geo) {\n\n float progress = niri_clamped_progress * niri_clamped_progress;\n vec2 coords = (coords_geo.xy - vec2(0.5, 1.0)) * size_geo.xy;\n coords.y -= progress * 1440.0;\n float random = (niri_random_seed - 0.5) / 2.0;\n random = sign(random) - random;\n float max_angle = 0.5 * random;\n float angle = progress * max_angle;\n mat2 rotate = mat2(cos(angle), -sin(angle), sin(angle), cos(angle));\n coords = rotate * coords;\n coords_geo = vec3(coords / size_geo.xy + vec2(0.5, 1.0), 1.0);\n vec3 coords_tex = niri_geo_to_tex * coords_geo;\n vec4 color = texture2D(niri_tex, coords_tex.st);\n\n return color;\n }\n\n vec4 close_color(vec3 coords_geo, vec3 size_geo) {\n return fall_and_rotate(coords_geo, size_geo);\n }\n "
}
}
window-rule {
match app-id="unset"
open-floating true
}
window-rule {
match app-id="org.gnome.Nautilus"
open-floating true
}
window-rule {
match app-id="org.freedesktop.impl.portal.desktop.kde"
open-floating true
}
window-rule {
draw-border-with-background false
}
// Fix discord screencasting
debug {
wait-for-frame-completion-in-pipewire
}
hotkey-overlay {
skip-at-startup
}
cursor {
xcursor-theme "BreezeX-RosePine-Linux"
xcursor-size 32
hide-when-typing
}
binds {
Mod+Shift+Slash {
show-hotkey-overlay
}
Mod+Return {
spawn "ghostty"
}
Mod+Shift+Return {
spawn "sh" "-c" "niri msg action spawn -- ghostty; sleep 0.3; niri msg action consume-or-expel-window-left"
}
Mod+B {
spawn "brave"
}
// Mod+D {
// spawn "nu" "-c" "quickshell --path ~/repos/projects/nichts/main/modules/style/quickshell/shell msg launcher open"
// }
Mod+D {
spawn "fuzzel"
}
Mod+Alt+L {
spawn "swaylock"
}
// Audio
XF86AudioRaiseVolume allow-when-locked=true {
spawn "volumectl" "-u" "up"
}
XF86AudioLowerVolume allow-when-locked=true {
spawn "volumectl" "-u" "down"
}
XF86AudioMute allow-when-locked=true {
spawn "volumectl" "toggle-mute"
}
XF86AudioMicMute allow-when-locked=true {
spawn "volumectl" "-m" "toggle-mute"
}
XF86AudioPlay allow-when-locked=true {
spawn "playerctl" "play-pause"
}
XF86AudioNext allow-when-locked=true {
spawn "playerctl" "next"
}
XF86AudioPrev allow-when-locked=true {
spawn "playerctl" "previous"
}
// Monitor brightness
XF86MonBrightnessUp allow-when-locked=true {
spawn "lightctl" "up"
}
XF86MonBrightnessDown allow-when-locked=true {
spawn "lightctl" "down"
}
Mod+Q {
close-window
}
Mod+H {
focus-column-or-monitor-left
}
Mod+J {
focus-window-or-workspace-down
}
Mod+K {
focus-window-or-workspace-up
}
Mod+L {
focus-column-or-monitor-right
}
Mod+Shift+H {
move-column-left-or-to-monitor-left
}
Mod+Shift+J {
move-window-down-or-to-workspace-down
}
Mod+Shift+K {
move-window-up-or-to-workspace-up
}
Mod+Shift+L {
move-column-right-or-to-monitor-right
}
Mod+Home {
focus-column-first
}
Mod+End {
focus-column-last
}
Mod+Ctrl+Home {
move-column-to-first
}
Mod+Ctrl+End {
move-column-to-last
}
Mod+Ctrl+H {
focus-monitor-left
}
Mod+Ctrl+J {
focus-monitor-down
}
Mod+Ctrl+K {
focus-monitor-up
}
Mod+Ctrl+L {
focus-monitor-right
}
Mod+Shift+Ctrl+Left {
move-column-to-monitor-left
}
Mod+Shift+Ctrl+Down {
move-column-to-monitor-down
}
Mod+Shift+Ctrl+Up {
move-column-to-monitor-up
}
Mod+Shift+Ctrl+Right {
move-column-to-monitor-right
}
Mod+Shift+Ctrl+H {
move-column-to-monitor-left
}
Mod+Shift+Ctrl+J {
move-column-to-monitor-down
}
Mod+Shift+Ctrl+K {
move-column-to-monitor-up
}
Mod+Shift+Ctrl+L {
move-column-to-monitor-right
}
Mod+Page_Down {
focus-workspace-down
}
Mod+Page_Up {
focus-workspace-up
}
Mod+Shift+Page_Down {
move-column-to-workspace-down
}
Mod+Shift+Page_Up {
move-column-to-workspace-up
}
Mod+U {
focus-workspace-down
}
Mod+I {
focus-workspace-up
}
Mod+Shift+U {
move-column-to-workspace-down
}
Mod+Shift+I {
move-column-to-workspace-up
}
Mod+WheelScrollDown cooldown-ms=150 {
focus-workspace-down
}
Mod+WheelScrollUp cooldown-ms=150 {
focus-workspace-up
}
Mod+Shift+WheelScrollDown cooldown-ms=150 {
move-column-to-workspace-down
}
Mod+Shift+WheelScrollUp cooldown-ms=150 {
move-column-to-workspace-up
}
Mod+WheelScrollRight {
focus-column-right
}
Mod+WheelScrollLeft {
focus-column-left
}
Mod+Shift+WheelScrollRight {
move-column-right
}
Mod+Shift+WheelScrollLeft {
move-column-left
}
Mod+1 {
focus-workspace 1
}
Mod+2 {
focus-workspace 2
}
Mod+3 {
focus-workspace 3
}
Mod+4 {
focus-workspace 4
}
Mod+5 {
focus-workspace 5
}
Mod+6 {
focus-workspace 6
}
Mod+7 {
focus-workspace 7
}
Mod+8 {
focus-workspace 8
}
Mod+9 {
focus-workspace 9
}
Mod+Shift+1 {
move-column-to-workspace 1
}
Mod+Shift+2 {
move-column-to-workspace 2
}
Mod+Shift+3 {
move-column-to-workspace 3
}
Mod+Shift+4 {
move-column-to-workspace 4
}
Mod+Shift+5 {
move-column-to-workspace 5
}
Mod+Shift+6 {
move-column-to-workspace 6
}
Mod+Shift+7 {
move-column-to-workspace 7
}
Mod+Shift+8 {
move-column-to-workspace 8
}
Mod+Shift+9 {
move-column-to-workspace 9
}
Mod+BracketLeft {
consume-or-expel-window-left
}
Mod+BracketRight {
consume-or-expel-window-right
}
Mod+Comma {
consume-window-into-column
}
Mod+Period {
expel-window-from-column
}
Mod+R {
switch-preset-column-width
}
Mod+Shift+R {
switch-preset-window-height
}
Mod+Ctrl+R {
reset-window-height
}
Mod+F {
maximize-column
}
Mod+Shift+F {
fullscreen-window
}
Mod+Ctrl+F {
expand-column-to-available-width
}
Mod+C {
center-column
}
Mod+Minus {
set-column-width "-10%"
}
Mod+Equal {
set-column-width "+10%"
}
// Finer height adjustments when in column with other windows.
Mod+Shift+Minus {
set-window-height "-10%"
}
Mod+Shift+Equal {
set-window-height "+10%"
}
// Move the focused window between the floating and the tiling layout.
Mod+V {
toggle-window-floating
}
Mod+Shift+V {
switch-focus-between-floating-and-tiling
}
Mod+W {
toggle-column-tabbed-display
}
Mod+O {
toggle-overview
}
Print {
screenshot
}
Ctrl+Print {
screenshot-screen
}
// Alt+Print {
// spawn "sh" "-c" ""
// }
Mod+T {
screenshot
}
// "$mainMod, T, exec, ${getExe pkgs.grimblast} save area - | ${getExe pkgs.tesseract} - - | ${getExe' pkgs.wl-clipboard "wl-copy"}"
Mod+Escape allow-inhibiting=false {
toggle-keyboard-shortcuts-inhibit
}
Mod+Shift+E {
quit
}
Mod+Shift+P {
power-off-monitors
}
}

324
modules/wms/niri/config.nix Normal file
View file

@ -0,0 +1,324 @@
{
lib,
pkgs,
node,
plain,
leaf,
flag,
}:
let
inherit (lib.meta) getExe;
inherit (builtins) readFile;
in
[
(plain "input" [
(plain "keyboard" [
(plain "xkb" [
(leaf "layout" "us,ru")
(leaf "variant" ",phonetic_winkeys")
(leaf "options" "grp:rctrl_rshift_toggle, compose:102")
])
(leaf "repeat-rate" 60)
(leaf "repeat-delay" 200)
(flag "numlock")
])
(plain "touchpad" [ (flag "tap") ])
(leaf "focus-follows-mouse" { max-scroll-amount = "0%"; })
(plain "tablet" [
(leaf "map-to-output" "DP-2")
])
])
# Laptop monitor
(node "output" "eDP-1" [
(leaf "mode" "1920x1080@59.999")
(leaf "scale" 1)
(leaf "position" {
x = 0;
y = 0;
})
])
# PC monitors
(node "output" "DP-2" [
(leaf "mode" "1920x1080@59.999")
(leaf "scale" 1)
(leaf "position" {
x = 0;
y = 0;
})
])
(node "output" "HDMI-A-1" [
(leaf "mode" "1920x1080@59.999")
(leaf "scale" 1)
(leaf "position" {
x = 1920;
y = 0;
})
])
(plain "layout" [
(leaf "gaps" 0)
(leaf "center-focused-column" "on-overflow")
(plain "focus-ring" [
(leaf "width" 2)
(leaf "active-color" "#f9e2af")
(leaf "inactive-color" "transparent")
(leaf "active-gradient" {
from = "#E5989B";
to = "#FFB4A2";
angle = 45;
relative-to = "workspace-view";
"in" = "oklch longer hue";
})
])
(plain "border" [
(flag "off")
])
(plain "shadow" [
# (flag "on")
])
(plain "tab-indicator" [
(leaf "width" 2)
(leaf "gap" 4)
(leaf "gaps-between-tabs" 2)
])
(plain "insert-hint" [
(leaf "color" "#ffc87f80")
(leaf "gradient" {
from = "#f38ba880";
to = "#f9e2af80";
angle = 45;
relative-to = "workspace-view";
"in" = "oklch longer hue";
})
])
(plain "preset-column-widths" (
map (width: (leaf "proportion" width)) [
0.3333
0.5
0.66666
]
))
(plain "default-column-width" [
(leaf "proportion" 0.5)
])
(leaf "background-color" "transparent")
])
(leaf "spawn-at-startup" "waybar")
(plain "environment" [
(leaf "DISPLAY" ":0")
])
(plain "layer-rule" [
(leaf "match" { namespace = "overview$"; })
(leaf "place-within-backdrop" true)
])
(flag "prefer-no-csd")
(plain "switch-events" [
(plain "lid-close" [
(leaf "spawn" <| getExe pkgs.swaylock)
])
])
(plain "overview" [
(leaf "zoom" 0.6)
(leaf "backdrop-color" "#777777")
])
(plain "animations" [
(plain "window-close" [
(leaf "duration-ms" 250)
(leaf "curve" "linear")
(leaf "custom-shader" <| readFile ./window-close.glsl)
])
])
(plain "window-rule" [
(leaf "draw-border-with-background" false)
])
(plain "debug" [
(flag "wait-for-frame-completion-in-pipewire")
])
(plain "hotkey-overlay" [
(flag "skip-at-startup")
])
(plain "cursor" [
(leaf "xcursor-theme" "BreezeX-RosePine-Linux")
(leaf "xcursor-size" 32)
(flag "hide-when-typing")
])
(plain "binds" [
(plain "XF86AudioRaiseVolume" [
(leaf "spawn" [
"volumectl"
"-u"
"up"
])
])
(plain "XF86AudioLowerVolume" [
(leaf "spawn" [
"volumectl"
"-u"
"down"
])
])
(plain "XF86AudioMute" [
(leaf "spawn" [
"volumectl"
"toggle-mute"
])
])
(plain "XF86AudioMicMute" [
(leaf "spawn" [
"volumectl"
"-m"
"toggle-mute"
])
])
(plain "XF86AudioPlay" [
(leaf "spawn" [
"playerctl"
"play-pause"
])
])
(plain "XF86AudioNext" [
(leaf "spawn" [
"playerctl"
"next"
])
])
(plain "XF86AudioPrev" [
(leaf "spawn" [
"playerctl"
"previous"
])
])
(plain "XF86MonBrightnessUp" [
(leaf "spawn" [
"lightctl"
"up"
])
])
(plain "XF86MonBrightnessDown" [
(leaf "spawn" [
"lightctl"
"down"
])
])
(plain "Mod+Shift+Slash" [ (flag "show-hotkey-overlay") ])
(plain "Mod+Return" [ (leaf "spawn" <| getExe pkgs.ghostty) ])
(plain "Mod+Shift+Return" [
(leaf "spawn" [
(getExe pkgs.bash)
"-c"
"${getExe pkgs.niri} msg action spawn -- ${getExe pkgs.ghostty}; sleep 0.2; ${getExe pkgs.niri} msg action consume-or-expel-window-left"
])
])
(plain "Ctrl+Print" [ (flag "screenshot-screen") ])
(plain "Mod+0" [ (leaf "focus-workspace" 0) ])
(plain "Mod+1" [ (leaf "focus-workspace" 1) ])
(plain "Mod+2" [ (leaf "focus-workspace" 2) ])
(plain "Mod+3" [ (leaf "focus-workspace" 3) ])
(plain "Mod+4" [ (leaf "focus-workspace" 4) ])
(plain "Mod+5" [ (leaf "focus-workspace" 5) ])
(plain "Mod+6" [ (leaf "focus-workspace" 6) ])
(plain "Mod+7" [ (leaf "focus-workspace" 7) ])
(plain "Mod+8" [ (leaf "focus-workspace" 8) ])
(plain "Mod+9" [ (leaf "focus-workspace" 9) ])
(plain "Mod+Alt+L" [ (leaf "spawn" <| getExe pkgs.swaylock) ])
(plain "Mod+B" [ (leaf "spawn" <| getExe pkgs.brave) ])
(plain "Mod+BracketLeft" [ (flag "consume-or-expel-window-left") ])
(plain "Mod+BracketRight" [ (flag "consume-or-expel-window-right") ])
(plain "Mod+C" [ (flag "center-column") ])
(plain "Mod+Comma" [ (flag "consume-window-into-column") ])
(plain "Mod+Ctrl+End" [ (flag "move-column-to-last") ])
(plain "Mod+Ctrl+F" [ (flag "expand-column-to-available-width") ])
(plain "Mod+Ctrl+H" [ (flag "focus-monitor-left") ])
(plain "Mod+Ctrl+Home" [ (flag "move-column-to-first") ])
(plain "Mod+Ctrl+J" [ (flag "focus-monitor-down") ])
(plain "Mod+Ctrl+K" [ (flag "focus-monitor-up") ])
(plain "Mod+Ctrl+L" [ (flag "focus-monitor-right") ])
(plain "Mod+Ctrl+R" [ (flag "reset-window-height") ])
(plain "Mod+D" [ (leaf "spawn" <| getExe pkgs.fuzzel) ])
(plain "Mod+End" [ (flag "focus-column-last") ])
(plain "Mod+Equal" [ (leaf "set-column-width" [ "+10%" ]) ])
(plain "Mod+F" [ (flag "maximize-column") ])
(plain "Mod+H" [ (flag "focus-column-or-monitor-left") ])
(plain "Mod+Home" [ (flag "focus-column-first") ])
(plain "Mod+I" [ (flag "focus-workspace-up") ])
(plain "Mod+J" [ (flag "focus-window-or-workspace-down") ])
(plain "Mod+K" [ (flag "focus-window-or-workspace-up") ])
(plain "Mod+L" [ (flag "focus-column-or-monitor-right") ])
(plain "Mod+Minus" [ (leaf "set-column-width" [ "-10%" ]) ])
(plain "Mod+O" [ (flag "toggle-overview") ])
(plain "Mod+Page_Down" [ (flag "focus-workspace-down") ])
(plain "Mod+Page_Up" [ (flag "focus-workspace-up") ])
(plain "Mod+Period" [ (flag "expel-window-from-column") ])
(plain "Mod+Q" [ (flag "close-window") ])
(plain "Mod+R" [ (flag "switch-preset-column-width") ])
(plain "Mod+Shift+0" [ (leaf "move-column-to-workspace" 0) ])
(plain "Mod+Shift+1" [ (leaf "move-column-to-workspace" 1) ])
(plain "Mod+Shift+2" [ (leaf "move-column-to-workspace" 2) ])
(plain "Mod+Shift+3" [ (leaf "move-column-to-workspace" 3) ])
(plain "Mod+Shift+4" [ (leaf "move-column-to-workspace" 4) ])
(plain "Mod+Shift+5" [ (leaf "move-column-to-workspace" 5) ])
(plain "Mod+Shift+6" [ (leaf "move-column-to-workspace" 6) ])
(plain "Mod+Shift+7" [ (leaf "move-column-to-workspace" 7) ])
(plain "Mod+Shift+8" [ (leaf "move-column-to-workspace" 8) ])
(plain "Mod+Shift+9" [ (leaf "move-column-to-workspace" 9) ])
(plain "Mod+Shift+Ctrl+Down" [ (flag "move-column-to-monitor-down") ])
(plain "Mod+Shift+Ctrl+H" [ (flag "move-column-to-monitor-left") ])
(plain "Mod+Shift+Ctrl+J" [ (flag "move-column-to-monitor-down") ])
(plain "Mod+Shift+Ctrl+K" [ (flag "move-column-to-monitor-up") ])
(plain "Mod+Shift+Ctrl+Left" [ (flag "move-column-to-monitor-left") ])
(plain "Mod+Shift+Ctrl+L" [ (flag "move-column-to-monitor-right") ])
(plain "Mod+Shift+Ctrl+Right" [ (flag "move-column-to-monitor-right") ])
(plain "Mod+Shift+Ctrl+Up" [ (flag "move-column-to-monitor-up") ])
(plain "Mod+Shift+E" [ (flag "quit") ])
(plain "Mod+Shift+Equal" [ (leaf "set-window-height" [ "+10%" ]) ])
(plain "Mod+Shift+F" [ (flag "fullscreen-window") ])
(plain "Mod+Shift+H" [ (flag "move-column-left-or-to-monitor-left") ])
(plain "Mod+Shift+I" [ (flag "move-column-to-workspace-up") ])
(plain "Mod+Shift+J" [ (flag "move-window-down-or-to-workspace-down") ])
(plain "Mod+Shift+K" [ (flag "move-window-up-or-to-workspace-up") ])
(plain "Mod+Shift+L" [ (flag "move-column-right-or-to-monitor-right") ])
(plain "Mod+Shift+Minus" [ (leaf "set-window-height" [ "-10%" ]) ])
(plain "Mod+Shift+Page_Down" [ (flag "move-column-to-workspace-down") ])
(plain "Mod+Shift+Page_Up" [ (flag "move-column-to-workspace-up") ])
(plain "Mod+Shift+P" [ (flag "power-off-monitors") ])
(plain "Mod+Shift+R" [ (flag "switch-preset-window-height") ])
(plain "Mod+Shift+U" [ (flag "move-column-to-workspace-down") ])
(plain "Mod+Shift+V" [ (flag "switch-focus-between-floating-and-tiling") ])
(plain "Mod+Shift+WheelScrollDown" [ (flag "move-column-to-workspace-down") ])
(plain "Mod+Shift+WheelScrollLeft" [ (flag "move-column-left") ])
(plain "Mod+Shift+WheelScrollRight" [ (flag "move-column-right") ])
(plain "Mod+Shift+WheelScrollUp" [ (flag "move-column-to-workspace-up") ])
(plain "Mod+T" [ (flag "screenshot") ])
(plain "Mod+U" [ (flag "focus-workspace-down") ])
(plain "Mod+V" [ (flag "toggle-window-floating") ])
(plain "Mod+W" [ (flag "toggle-column-tabbed-display") ])
(plain "Mod+WheelScrollDown" [ (flag "focus-workspace-down") ])
(plain "Mod+WheelScrollLeft" [ (flag "focus-column-left") ])
(plain "Mod+WheelScrollRight" [ (flag "focus-column-right") ])
(plain "Mod+WheelScrollUp" [ (flag "focus-workspace-up") ])
(plain "Print" [ (flag "screenshot") ])
])
]
++ (map
(
app-id:
(plain "window-rule" [
(leaf "match" { inherit app-id; })
(leaf "open-floating" true)
])
)
[
"unset"
"org.gnome.Nautilus"
"org.freedesktop.impl.portal.desktop.kde"
]
)

323
modules/wms/niri/kdl.nix Normal file
View file

@ -0,0 +1,323 @@
# https://github.com/NixOS/nixpkgs/pull/426828
# The KDL document language (https://kdl.dev/)
/*
Original file: formats.nix
Copyright (c) 2003-2025 Eelco Dolstra and the Nixpkgs/NixOS contributors
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
/*
* Modificatied by faukah (2025)
*
* This modified version is part of the Nichts project and is licensed under
* the GNU General Public License v3.0.
*
* See <https://www.gnu.org/licenses/> for details.
*/
{
lib,
pkgs,
...
}:
let
inherit (lib.types)
listOf
attrsOf
str
bool
number
oneOf
nullOr
submodule
coercedTo
path
;
inherit (lib.lists) isList;
inherit (lib.attrsets) isAttrs;
inherit (lib.trivial) isBool;
in
{
type = (
let
mergeUniq =
mergeOne:
lib.mergeUniqueOption {
message = "";
merge =
loc: defs:
let
inherit (lib.head defs) file value;
in
mergeOne file loc value;
};
mergeFlat =
elemType: loc: file: value:
if value ? _type then
throw "${lib.showOption loc} has wrong type: expected '${elemType.description}', got `${value._type}`"
else
elemType.merge loc [ { inherit file value; } ];
uniqFlatListOf =
elemType:
lib.mkOptionType {
name = "uniqFlatListOf";
inherit (listOf elemType) description descriptionClass;
check = isList;
merge = mergeUniq (
file: loc: lib.imap1 (i: mergeFlat elemType (loc ++ [ "[entry ${toString i}]" ]) file)
);
};
uniqFlatAttrsOf =
elemType:
lib.mkOptionType {
name = "uniqFlatAttrsOf";
inherit (attrsOf elemType) description descriptionClass;
check = isAttrs;
merge = mergeUniq (file: loc: lib.mapAttrs (name: mergeFlat elemType (loc ++ [ name ]) file));
};
kdlUntypedValue = lib.mkOptionType {
name = "kdlUntypedValue";
description = "KDL value without type annotation";
descriptionClass = "noun";
inherit
(nullOr (oneOf [
str
bool
number
path
]))
check
merge
;
};
kdlTypedValue = lib.mkOptionType {
name = "kdlTypedValue";
description = "KDL value with type annotation";
descriptionClass = "noun";
check = isAttrs;
merge =
(submodule {
options = {
type = lib.mkOption {
type = nullOr str;
default = null;
description = ''
[Type annotation](https://kdl.dev/spec/#name-type-annotation) of a [KDL value](https://kdl.dev/spec/#name-value).
'';
};
value = lib.mkOption {
type = kdlUntypedValue;
description = ''
Scalar part of a [KDL value](https://kdl.dev/spec/#name-value)
'';
};
};
}).merge;
};
# https://kdl.dev/spec/#name-value
kdlValue = lib.mkOptionType {
name = "kdlValue";
description = "KDL value";
descriptionClass = "noun";
inherit (coercedTo kdlUntypedValue (value: { inherit value; }) kdlTypedValue) check merge;
nestedTypes = {
type = nullOr str;
scalar = kdlUntypedValue;
};
};
# https://kdl.dev/spec/#name-node
kdlNode = lib.mkOptionType {
name = "kdlNode";
description = "KDL node";
descriptionClass = "noun";
check = isAttrs;
merge =
(submodule {
options = {
type = lib.mkOption {
type = nullOr str;
default = null;
description = ''
[Type annotation](https://kdl.dev/spec/#name-type-annotation) of a KDL node.
'';
};
name = lib.mkOption {
type = str;
description = ''
Name of a [KDL node](https://kdl.dev/spec/#name-node).
'';
};
arguments = lib.mkOption {
type = uniqFlatListOf kdlValue;
default = [ ];
description = ''
[Arguments](https://kdl.dev/spec/#name-argument) of a KDL node.
'';
};
properties = lib.mkOption {
type = uniqFlatAttrsOf kdlValue;
default = { };
description = ''
[Properties](https://kdl.dev/spec/#name-property) of a KDL node.
'';
};
children = lib.mkOption {
type = kdlDocument;
default = [ ];
description = ''
[Children](https://kdl.dev/spec/#children-block) of a KDL node.
'';
};
};
}).merge;
nestedTypes = {
name = str;
type = nullOr str;
arguments = uniqFlatListOf kdlValue;
properties = uniqFlatAttrsOf kdlValue;
children = kdlDocument;
};
};
kdlDocument = lib.mkOptionType {
name = "kdlDocument";
description = "KDL document";
descriptionClass = "noun";
check = isList;
merge = mergeUniq (
file:
let
mergeDocument =
loc: toplevel:
builtins.concatLists (
lib.imap1 (i: mergeDocumentEntry (loc ++ [ "[entry ${toString i}]" ])) toplevel
);
mergeDocumentEntry =
loc: value:
let
inherit (lib.options) showDefs;
defs = [ { inherit file value; } ];
in
if isList value then
mergeDocument loc value
else if value ? _type then
if value._type == "if" then
if isBool value.condition then
if value.condition then mergeDocumentEntry loc value.content else [ ]
else
throw "`mkIf` called with non-Boolean condition at ${lib.showOption loc}. Definition value:${showDefs defs}"
else if value._type == "merge" then
throw ''
${lib.showOption loc} has wrong type: expected a KDL node or document, got 'merge'.
note: `mkMerge` is potentially ambiguous in a KDL document, as "merging" is application-specific. if you intended to "splat" all the nodes in a KDL document, you can just insert the list of nodes directly. you can arbitrarily nest KDL documents, and they will be concatenated.
''
else
throw "${lib.showOption loc} has wrong type: expected a KDL node or document, got '${value._type}'. Definition value:${showDefs defs}"
else if kdlNode.check value then
[ (kdlNode.merge loc [ { inherit file value; } ]) ]
else
throw "${lib.showOption loc} has wrong type: expected a KDL node or document. Definition value:${showDefs defs}";
in
mergeDocument
);
nestedTypes.node = kdlNode;
};
in
kdlDocument
);
lib = {
/**
Helper function for generating attrsets expected by pkgs.formats.kdl
# Example
```nix
let
settingsFormat = pkgs.formats.kdl { };
inherit (settingsFormat.lib) node;
in
settingsFormat.generate "sample.kdl" [
(node "foo" null [ ] { } [
(node "bar" null [ "baz" ] { a = 1; } [ ])
])
]
```
# Arguments
name
: The name of the node, represented by a string
type
: The type annotation of the node, represented by a string, or null to avoid generating a type annotation
arguments
: The arguments of the node, represented as a list of KDL values
properties
: The properties of the node, represented as an attrset of KDL values
children
: The children of the node, represented as a list of nodes
*/
node = name: type: arguments: properties: children: {
inherit
name
type
arguments
properties
children
;
};
/**
Helper function for generating the format of a typed value as expected by pkgs.formats.kdl
type
: The type of the value, represented by a string
value
: The value itself
*/
typed = type: value: { inherit type value; };
};
generate =
name: value:
pkgs.callPackage (
{ runCommand, jsonkdl }:
runCommand name
{
nativeBuildInputs = [ jsonkdl ];
value = builtins.toJSON value;
passAsFile = [ "value" ];
}
''
jsonkdl --kdl-v1 -- "$valuePath" "$out"
''
) { };
}

View file

@ -102,6 +102,48 @@ let
} }
) { }; ) { };
kdl = pkgs.callPackage ./kdl.nix { };
node = name: args: children: {
inherit name;
inherit
(lib.foldl
(
self: arg:
if lib.isAttrs arg then
self // { properties = self.properties // arg; }
else
self // { arguments = self.arguments ++ [ arg ]; }
)
{
arguments = [ ];
properties = { };
}
(lib.toList args)
)
arguments
properties
;
inherit children;
};
plain = name: node name [ ];
leaf = name: args: node name args [ ];
flag = name: node name [ ] [ ];
niri-config = kdl.generate "niri-config.kdl" (
import ./config.nix {
inherit
node
plain
leaf
flag
lib
pkgs
;
}
);
in in
{ {
options.modules.desktops.niri.enable = mkEnableOption "Niri, a scolling tiling wayland compositor"; options.modules.desktops.niri.enable = mkEnableOption "Niri, a scolling tiling wayland compositor";
@ -115,7 +157,7 @@ in
# which is something I direly want to avoid. # which is something I direly want to avoid.
services.gnome.gnome-keyring.enable = mkForce false; services.gnome.gnome-keyring.enable = mkForce false;
hjem.users.${username}.files.".config/niri/config.kdl".source = ./config.kdl; hjem.users.${username}.files.".config/niri/config.kdl".source = niri-config;
environment.systemPackages = builtins.attrValues { environment.systemPackages = builtins.attrValues {
inherit (pkgs) inherit (pkgs)