2026-05-02 09:08:40 +02:00
# Hyprland window manager with keybindings, window rules, idle/lock, and hyprpaper.
{
config ,
lib ,
...
}:
with lib ; let
cfg = config . desktop . wm . hyprland ;
2026-08-01 10:53:09 +02:00
lua = lib . generators . mkLuaInline ;
luaString = builtins . toJSON ;
mkEnvVar = name : value : {
_args = [
name
value
];
};
modKey = key : lua ''mainMod .. " + ${ key } "'' ;
mkDispatcher = expression : lua expression ;
execCmd = command : "hl.dsp.exec_cmd( ${ luaString command } )" ;
execExpr = expression : "hl.dsp.exec_cmd( ${ expression } )" ;
mkBinding = keys : dispatcher : {
_args = [
keys
( mkDispatcher dispatcher )
];
};
mkBindingWithOptions = keys : dispatcher : options : {
_args = [
keys
( mkDispatcher dispatcher )
options
];
};
startupCommand = command : {
_args = [
"hyprland.start"
( lua ''
function()
hl.exec_cmd( ${ luaString command } )
end
'' )
];
};
focusWorkspace = workspace : "hl.dsp.focus({ workspace = ${ luaString workspace } })" ;
moveToWorkspace = workspace : "hl.dsp.window.move({ workspace = ${ luaString workspace } })" ;
2026-05-02 09:08:40 +02:00
in {
options . desktop . wm . hyprland . enable = mkEnableOption "Hyprland window manager" ;
config = mkIf cfg . enable {
wayland . windowManager . hyprland = {
2026-08-01 10:53:09 +02:00
configType = "lua" ;
extraLuaFiles . "00-dms" = {
autoLoad = true ;
content = ''
-- DMS setup/runtime owns these modules. Load only the Lua equivalents
-- of the pre-migration active sources, before local Home Manager
-- settings, preserving the old source-first order without enabling new
-- DMS default binds or rules.
--
-- Keep fresh Home Manager generations usable before DMS has written
-- ~/.config/hypr/dms/*.lua.
local function require_if_present(module)
if package.searchpath(module, package.path) ~= nil then
require(module)
end
end
require_if_present("dms.colors")
require_if_present("dms.layout")
require_if_present("dms.outputs")
'' ;
};
2026-05-02 09:08:40 +02:00
settings = {
2026-08-01 10:53:09 +02:00
mainMod = {
_var = "SUPER" ;
2026-05-02 09:08:40 +02:00
};
2026-08-01 10:53:09 +02:00
terminal = {
_var = "ghostty" ;
2026-05-02 09:08:40 +02:00
};
2026-08-01 10:53:09 +02:00
config = {
xwayland = {
force_zero_scaling = true ;
2026-05-02 09:08:40 +02:00
};
2026-08-01 10:53:09 +02:00
input = {
kb_layout = "us" ;
kb_variant = "" ;
kb_model = "" ;
kb_rules = "" ;
kb_options = "ctrl:nocaps" ;
follow_mouse = 1 ;
};
general = {
gaps_in = 5 ;
gaps_out = 5 ;
border_size = 1 ;
layout = "dwindle" ;
};
decoration = {
shadow = {
enabled = true ;
range = 60 ;
render_power = 3 ;
# color = "rgba(${config.colorScheme.palette.base00}66)";
offset = "1 2" ;
scale = 0 .97 ;
};
rounding = 8 ;
blur = {
enabled = true ;
size = 3 ;
passes = 3 ;
};
active_opacity = 0 .9 ;
inactive_opacity = 0 .5 ;
};
animations = {
2026-05-02 09:08:40 +02:00
enabled = true ;
2026-08-01 10:53:09 +02:00
};
dwindle = {
# pseudotile = true; # TODO
preserve_split = true ;
};
master = {
new_status = "master" ;
2026-05-02 09:08:40 +02:00
};
};
2026-08-01 10:53:09 +02:00
on = [
( startupCommand "vibetyper" )
];
2026-05-02 09:08:40 +02:00
2026-08-01 10:53:09 +02:00
"env" = [
( mkEnvVar "XCURSOR_SIZE" "32" )
( mkEnvVar "HYPRCURSOR_THEME" "Bibata-Modern-Ice" )
( mkEnvVar "WLR_NO_HARDWARE_CURSORS" "1" )
( mkEnvVar "XDG_CURRENT_DESKTOP" "Hyprland" )
( mkEnvVar "XDG_SESSION_TYPE" "wayland" )
( mkEnvVar "XDG_SESSION_DESKTOP" "Hyprland" )
( mkEnvVar "XKB_DEFAULT_LAYOUT" "de" )
( mkEnvVar "NIXOS_OZONE_WL" "1" )
( mkEnvVar "QT_QPA_PLATFORM" "wayland;xcb" )
( mkEnvVar "QT_QPA_PLATFORMTHEME" "gtk3" )
( mkEnvVar "QT_QPA_PLATFORMTHEME_QT6" "gtk3" )
];
2026-05-02 09:08:40 +02:00
device = [
2026-06-19 11:29:02 +02:00
# Alle normalen Keyboards explizit auf de setzen:
2026-05-02 09:08:40 +02:00
{
2026-06-19 11:29:02 +02:00
name = "video-bus" ;
kb_layout = "de,us" ;
2026-06-19 06:56:48 +02:00
kb_options = "ctrl:nocaps" ;
}
{
2026-06-19 11:29:02 +02:00
name = "razer-razer-pro-click-v2-vertical-edition-keyboard" ;
kb_layout = "de,us" ;
2026-06-19 06:56:48 +02:00
kb_options = "ctrl:nocaps" ;
}
{
2026-06-19 11:29:02 +02:00
name = "razer-razer-pro-click-v2-vertical-edition-1" ;
kb_layout = "de,us" ;
2026-06-19 06:56:48 +02:00
kb_options = "ctrl:nocaps" ;
2026-05-02 09:08:40 +02:00
}
2026-06-11 07:14:21 +02:00
{
2026-06-11 07:14:35 +02:00
name = "vibe-typer-virtual-keyboard" ;
2026-06-13 08:17:16 +02:00
kb_layout = "de" ;
2026-06-19 06:56:48 +02:00
kb_options = "ctrl:nocaps" ;
2026-06-11 07:14:21 +02:00
}
2026-05-02 09:08:40 +02:00
{
name = "keychron-keychron-k7" ;
kb_layout = "us" ;
2026-06-19 06:56:48 +02:00
kb_options = "ctrl:nocaps" ;
2026-05-02 09:08:40 +02:00
}
2026-06-19 11:29:02 +02:00
# Moonlander Sub-Devices bleiben us (erben global)
{
name = "zsa-technology-labs-moonlander-mark-i-consumer-control" ;
kb_layout = "us" ;
kb_options = "ctrl:nocaps" ;
}
{
name = "zsa-technology-labs-moonlander-mark-i-keyboard" ;
kb_layout = "us" ;
kb_options = "ctrl:nocaps" ;
}
{
name = "zsa-technology-labs-moonlander-mark-i-system-control" ;
kb_layout = "us" ;
kb_options = "ctrl:nocaps" ;
}
2026-05-02 09:08:40 +02:00
];
2026-08-01 10:53:09 +02:00
curve = {
_args = [
"myBezier"
( lua ''{ type = "bezier", points = { { 0.05, 0.9 }, { 0.1, 1.05 } } }'' )
];
};
animation = [
{
leaf = "windows" ;
enabled = true ;
speed = 7 ;
bezier = "myBezier" ;
}
{
leaf = "windowsOut" ;
enabled = true ;
speed = 7 ;
bezier = "default" ;
style = "popin 80%" ;
}
{
leaf = "border" ;
enabled = true ;
speed = 10 ;
bezier = "default" ;
}
{
leaf = "borderangle" ;
enabled = true ;
speed = 8 ;
bezier = "default" ;
}
{
leaf = "fade" ;
enabled = true ;
speed = 7 ;
bezier = "default" ;
}
{
leaf = "workspaces" ;
enabled = true ;
speed = 6 ;
bezier = "default" ;
}
2026-05-02 09:08:40 +02:00
];
2026-08-01 10:53:09 +02:00
window_rule = [
{
match . class = "file_progress" ;
float = true ;
}
{
match . class = "confirm" ;
float = true ;
}
{
match . class = "dialog" ;
float = true ;
}
{
match . class = "download" ;
float = true ;
}
{
match . class = "notification" ;
float = true ;
}
{
match . class = "error" ;
float = true ;
}
{
match . class = "splash" ;
float = true ;
}
{
match . class = "confirmreset" ;
float = true ;
}
{
match . title = "Open File" ;
float = true ;
}
{
match . title = "branchdialog" ;
float = true ;
}
{
match . class = "pavucontrol-qt" ;
float = true ;
}
{
match . class = "pavucontrol" ;
float = true ;
}
{
match . class = "class:^(espanso)$" ;
float = true ;
}
{
match . class = "wlogout" ;
fullscreen = true ;
}
{
match . title = "wlogout" ;
float = true ;
}
{
match . title = "wlogout" ;
fullscreen = true ;
}
{
match . class = "mpv" ;
float = true ;
}
{
match . class = "mpv" ;
idle_inhibit = "focus" ;
}
{
match . class = "mpv" ;
opacity = "1.0 override" ;
}
{
match . title = "^(Media viewer)$" ;
float = true ;
}
{
match . title = "^(Volume Control)$" ;
float = true ;
}
{
match . title = "^(Picture-in-Picture)$" ;
float = true ;
}
{
match . title = "^(floating-pomodoro)$" ;
float = true ;
}
{
match . title = "^(floating-pomodoro)$" ;
size = "250 50" ;
}
{
match . title = "^(floating-pomodoro)$" ;
move = "12 (monitor_h-150)" ;
}
{
match . title = "^(floating-pomodoro)$" ;
pin = true ;
}
{
match . initial_title = ".*streamlabs.com.*" ;
float = true ;
}
{
match . initial_title = ".*streamlabs.com.*" ;
pin = true ;
}
{
match . initial_title = ".*streamlabs.com.*" ;
size = "800 400" ;
}
{
match . initial_title = ".*alert-box.*" ;
move = "100%-820 102" ;
}
{
match . initial_title = ".*chat-box.*" ;
move = "100%-820 512" ;
}
{
match . initial_title = ".*streamlabs.com.*" ;
opacity = "0.5 override" ;
}
{
match . initial_title = ".*streamlabs.com.*" ;
idle_inhibit = "focus" ;
}
{
match . initial_title = ".*streamlabs.com.*" ;
no_anim = true ;
}
{
match . initial_title = ".*streamlabs.com.*" ;
decorate = false ;
}
{
match . initial_title = ".*streamlabs.com.*" ;
no_shadow = true ;
}
{
match . initial_title = ".*streamlabs.com.*" ;
no_blur = true ;
}
{
match = {
class = "^vibe-typer$" ;
title = "^Recording Indicator$" ;
};
no_blur = true ;
}
{
match = {
class = "^vibe-typer$" ;
title = "^Recording Indicator$" ;
};
no_shadow = true ;
}
{
match = {
class = "^vibe-typer$" ;
title = "^Recording Indicator$" ;
};
no_anim = true ;
}
{
match = {
class = "^vibe-typer$" ;
title = "^Recording Indicator$" ;
};
border_size = 0 ;
}
{
match = {
class = "^vibe-typer$" ;
title = "^Recording Indicator$" ;
};
no_focus = true ;
}
{
match . xwayland = true ;
border_color = "rgb(ffffff)" ;
}
2026-05-02 09:08:40 +02:00
];
2026-08-01 10:53:09 +02:00
"bind" = [
# mainMod + return intentionally remains disabled from the previous config.
( mkBinding ( modKey "t" ) ( execExpr ''terminal .. " -e nu -c 'nitch; exec nu'"'' ))
( mkBinding ( modKey "SHIFT + t" ) ( execCmd "launch-timer" ))
( mkBinding ( modKey "n" ) ( execExpr ''terminal .. " -e nvim"'' ))
( mkBinding ( modKey "z" ) ( execCmd "uwsm app -- zeditor" ))
( mkBinding ( modKey "o" ) ''hl.dsp.window.set_prop({ prop = "opaque", value = "toggle" })'' )
( mkBinding ( modKey "r" ) (
execCmd ''hyprctl dispatch focuswindow "initialtitle:.*alert-box.*" && hyprctl dispatch moveactive exact 4300 102 && hyprctl dispatch focuswindow "initialtitle:.*chat-box.*" && hyprctl dispatch moveactive exact 4300 512''
))
( mkBinding ( modKey "b" ) ( execCmd "uwsm app -- thunar" ))
( mkBinding ( modKey "SHIFT + B" ) ( execCmd "uwsm app -- vivaldi" ))
( mkBinding ( modKey "Escape" ) ( execCmd "uwsm app -- dms ipc call powermenu toggle" ))
( mkBinding ( modKey "q" ) "hl.dsp.window.close()" )
( mkBinding ( modKey "M" ) "hl.dsp.exit()" )
( mkBinding ( modKey "F" ) "hl.dsp.window.fullscreen({})" )
( mkBinding ( modKey "SHIFT + V" ) ''hl.dsp.window.float({ action = "toggle" })'' )
( mkBinding ( modKey "D" ) ( execCmd "uwsm app -- dms ipc call spotlight toggle" ))
( mkBinding ( modKey "V" ) ( execCmd "uwsm app -- dms ipc call clipboard toggle" ))
( mkBinding ( modKey "C" ) (
execCmd "bash -c 'FILE=/tmp/screenshot_$(date +%s).png; grim -g \" $(slurp) \" \" $FILE \" && ksnip \" $FILE \" '"
))
( mkBinding ( modKey "SHIFT + S" ) ( execCmd "uwsm app -- rofi -show emoji" ))
( mkBinding ( modKey "P" ) ( execCmd "uwsm app -- rofi-pass" ))
( mkBinding ( modKey "SHIFT + P" ) "hl.dsp.window.pseudo({})" )
( mkBinding ( modKey "R" ) ( execCmd "stt-ptt start" ))
( mkBinding ( modKey "S" ) ( execCmd "stt-ptt start" ))
( mkBinding ( modKey "h" ) ''hl.dsp.focus({ direction = "l" })'' )
( mkBinding ( modKey "l" ) ''hl.dsp.focus({ direction = "r" })'' )
( mkBinding ( modKey "k" ) ''hl.dsp.focus({ direction = "u" })'' )
( mkBinding ( modKey "j" ) ''hl.dsp.focus({ direction = "d" })'' )
( mkBinding ( modKey "1" ) ( focusWorkspace "1" ))
( mkBinding ( modKey "2" ) ( focusWorkspace "2" ))
( mkBinding ( modKey "3" ) ( focusWorkspace "3" ))
( mkBinding ( modKey "4" ) ( focusWorkspace "4" ))
( mkBinding ( modKey "5" ) ( focusWorkspace "5" ))
( mkBinding ( modKey "6" ) ( focusWorkspace "6" ))
( mkBinding ( modKey "7" ) ( focusWorkspace "7" ))
( mkBinding ( modKey "8" ) ( focusWorkspace "8" ))
( mkBinding ( modKey "9" ) ( focusWorkspace "9" ))
( mkBinding ( modKey "0" ) ( focusWorkspace "10" ))
( mkBinding ( modKey "SHIFT + 1" ) ( moveToWorkspace "1" ))
( mkBinding ( modKey "SHIFT + 2" ) ( moveToWorkspace "2" ))
( mkBinding ( modKey "SHIFT + 3" ) ( moveToWorkspace "3" ))
( mkBinding ( modKey "SHIFT + 4" ) ( moveToWorkspace "4" ))
( mkBinding ( modKey "SHIFT + 5" ) ( moveToWorkspace "5" ))
( mkBinding ( modKey "SHIFT + 6" ) ( moveToWorkspace "6" ))
( mkBinding ( modKey "SHIFT + 7" ) ( moveToWorkspace "7" ))
( mkBinding ( modKey "SHIFT + 8" ) ( moveToWorkspace "8" ))
( mkBinding ( modKey "SHIFT + 9" ) ( moveToWorkspace "9" ))
( mkBinding ( modKey "SHIFT + 0" ) ( moveToWorkspace "10" ))
( mkBinding ( modKey "mouse_down" ) ( focusWorkspace "e+1" ))
( mkBinding ( modKey "mouse_up" ) ( focusWorkspace "e-1" ))
( mkBindingWithOptions ( modKey "mouse:272" ) "hl.dsp.window.drag()" { mouse = true ;})
( mkBindingWithOptions ( modKey "mouse:273" ) "hl.dsp.window.resize()" { mouse = true ;})
2026-05-02 09:08:40 +02:00
];
};
};
2026-06-01 19:34:00 +02:00
# services.hypridle = {
# enable = true;
# settings = {
# general = {
# before_sleep_cmd = "hyprlock";
# after_sleep_cmd = "hyprctl dispatch dpms on";
# inhibit_sleep = 3;
# };
# listener = [
# {
# timeout = 300;
# on-timeout = "hyprlock";
# }
# {
# timeout = 420;
# on-timeout = "hyprctl dispatch dpms off";
# on-resume = "hyprctl dispatch dpms on";
# }
# ];
# };
# };
2026-05-02 09:08:40 +02:00
2026-06-01 19:34:00 +02:00
# services.hyprpaper.enable = true;
2026-05-02 09:08:40 +02:00
2026-06-01 19:34:00 +02:00
# programs.hyprlock = {
# enable = true;
# settings = {
# "$font" = "JetBrainsMono Nerd Font";
# "$base" = "rgb(${config.colorScheme.palette.base00})";
# "$text" = "rgb(${config.colorScheme.palette.base05})";
# "$textAlpha" = "${config.colorScheme.palette.base05}";
# "$accentAlpha" = "${config.colorScheme.palette.base0D}";
# "$red" = "rgb(${config.colorScheme.palette.base08})";
# "$yellow" = "rgb(${config.colorScheme.palette.base0A})";
2026-05-02 09:08:40 +02:00
2026-06-01 19:34:00 +02:00
# general = {
# hide_cursor = true;
# };
2026-05-02 09:08:40 +02:00
2026-06-01 19:34:00 +02:00
# background = {
# monitor = "";
# path = "${config.home.homeDirectory}/.config/hypr/wallpapers/wallhaven-lmmo8r.jpg";
# blur_passes = 0;
# color = "rgb(${config.colorScheme.palette.base00})";
# };
2026-05-02 09:08:40 +02:00
2026-06-01 19:34:00 +02:00
# label = [
# {
# monitor = "";
# text = "$TIME";
# color = "$text";
# font_size = 90;
# font_family = "$font";
# position = "30, 0";
# halign = "left";
# valign = "top";
# }
# {
# monitor = "";
# text = ''cmd[update:43200000] echo "$(date +"%A, %d %B %Y")"'';
# color = "$text";
# font_size = 25;
# font_family = "$font";
# position = "30, -150";
# halign = "left";
# valign = "top";
# }
# ];
2026-05-02 09:08:40 +02:00
2026-06-01 19:34:00 +02:00
# input-field = [
# {
# monitor = "";
# size = "300, 60";
# outline_thickness = 4;
# dots_size = 0.2;
# dots_spacing = 0.2;
# dots_center = true;
# outer_color = "rgb(${config.colorScheme.palette.base0D})";
# inner_color = "rgb(${config.colorScheme.palette.base00})";
# font_color = "rgb(${config.colorScheme.palette.base05})";
# fade_on_empty = false;
# placeholder_text = ''<span foreground="##${config.colorScheme.palette.base05}"> Logged in as <span foreground="##${config.colorScheme.palette.base0D}">$USER</span></span>'';
# hide_input = false;
# check_color = "rgb(${config.colorScheme.palette.base0D})";
# fail_color = "rgb(${config.colorScheme.palette.base08})";
# fail_text = ''<i>$FAIL <b>($ATTEMPTS)</b></i>'';
# capslock_color = "rgb(${config.colorScheme.palette.base0A})";
# position = "0, -35";
# halign = "center";
# valign = "center";
# }
# ];
# };
# };
2026-05-02 09:08:40 +02:00
};
}