5bd7647f48
- Rewrite hyprland.nix to use configType = "lua" - Use Hyprland 0.55 Lua API: hl.config(), hl.bind(), hl.curve(), hl.animation(), hl.window_rule(), hl.workspace_rule(), hl.dsp.* - Consolidate window rules where possible - Update flake.lock: home-manager -> 74f170c6 (2026-05-18) - hypridle and hyprlock keep their own config format
320 lines
12 KiB
Nix
320 lines
12 KiB
Nix
# Hyprland window manager — Lua config for Hyprland 0.55+
|
|
# Home-Manager configType = "lua" generates hyprland.lua using hl.* API.
|
|
# API reference: https://wiki.hypr.land/Configuring/Start/
|
|
{
|
|
config,
|
|
lib,
|
|
...
|
|
}:
|
|
with lib; let
|
|
cfg = config.desktop.wm.hyprland;
|
|
p = config.colorScheme.palette;
|
|
in {
|
|
options.desktop.wm.hyprland.enable = mkEnableOption "Hyprland window manager";
|
|
|
|
config = mkIf cfg.enable {
|
|
wayland.windowManager.hyprland = {
|
|
configType = "lua";
|
|
|
|
settings = {
|
|
# ── Lua local variables (rendered as: local mainMod = "SUPER") ──
|
|
mainMod = {_var = "SUPER";};
|
|
terminal = {_var = "ghostty";};
|
|
|
|
# ── Config sections (rendered as: hl.config({...})) ──
|
|
config = {
|
|
xwayland = {force_zero_scaling = true;};
|
|
|
|
input = {
|
|
kb_layout = "de,us";
|
|
kb_variant = "";
|
|
kb_model = "";
|
|
kb_rules = "";
|
|
kb_options = "ctrl:nocaps";
|
|
follow_mouse = 1;
|
|
};
|
|
|
|
general = {
|
|
gaps_in = 5;
|
|
gaps_out = 5;
|
|
border_size = 1;
|
|
col = {
|
|
active_border = {colors = ["rgba(9742b5ee)" "rgba(9742b5ee)"]; angle = 45;};
|
|
inactive_border = "rgba(${p.base03}aa)";
|
|
};
|
|
layout = "dwindle";
|
|
};
|
|
|
|
decoration = {
|
|
shadow = {
|
|
enabled = true;
|
|
range = 60;
|
|
render_power = 3;
|
|
color = "rgba(${p.base00}66)";
|
|
scale = 0.97;
|
|
};
|
|
rounding = 8;
|
|
blur = {
|
|
enabled = true;
|
|
size = 3;
|
|
passes = 3;
|
|
};
|
|
active_opacity = 0.9;
|
|
inactive_opacity = 0.5;
|
|
};
|
|
|
|
animations = {enabled = true;};
|
|
|
|
dwindle = {
|
|
pseudotile = true;
|
|
preserve_split = true;
|
|
};
|
|
|
|
master = {new_status = "master";};
|
|
};
|
|
|
|
# ── Environment variables (rendered as: hl.env("KEY", "VALUE")) ──
|
|
env = [
|
|
{ _args = ["XCURSOR_SIZE" "32"]; }
|
|
{ _args = ["HYPRCURSOR_THEME" "Bibata-Modern-Ice"]; }
|
|
{ _args = ["WLR_NO_HARDWARE_CURSORS" "1"]; }
|
|
{ _args = ["GTK_THEME" "Dracula"]; }
|
|
{ _args = ["XDG_CURRENT_DESKTOP" "Hyprland"]; }
|
|
{ _args = ["XDG_SESSION_TYPE" "wayland"]; }
|
|
{ _args = ["XDG_SESSION_DESKTOP" "Hyprland"]; }
|
|
{ _args = ["XKB_DEFAULT_LAYOUT" "de"]; }
|
|
{ _args = ["NIXOS_OZONE_WL" "1"]; }
|
|
];
|
|
|
|
# ── Per-device config (rendered as: hl.device({...})) ──
|
|
device = [
|
|
{name = "epic-mouse-v1"; sensitivity = -0.5;}
|
|
{name = "zsa-technology-labs-moonlander-mark-i"; kb_layout = "us";}
|
|
{name = "keychron-keychron-k7"; kb_layout = "us";}
|
|
];
|
|
};
|
|
|
|
# ── Complex Lua: curves, animations, binds, window rules, startup ──
|
|
extraConfig = ''
|
|
-- Bezier curves
|
|
hl.curve("myBezier", { type = "bezier", points = { {0.05, 0.9}, {0.1, 1.05} } })
|
|
|
|
-- Animations
|
|
hl.animation({ leaf = "windows", enabled = true, speed = 7, bezier = "myBezier" })
|
|
hl.animation({ leaf = "windowsOut", enabled = true, speed = 7, bezier = "default", style = "popin 80%" })
|
|
hl.animation({ leaf = "border", enabled = true, speed = 10, bezier = "default" })
|
|
hl.animation({ leaf = "borderangle", enabled = true, speed = 8, bezier = "default" })
|
|
hl.animation({ leaf = "fade", enabled = true, speed = 7, bezier = "default" })
|
|
hl.animation({ leaf = "workspaces", enabled = true, speed = 6, bezier = "default" })
|
|
|
|
-- Keybinds: terminals & editors
|
|
hl.bind(mainMod .. " + Return", hl.dsp.exec_cmd(terminal .. " nu -c zellij-ps"))
|
|
hl.bind(mainMod .. " + T", hl.dsp.exec_cmd(terminal .. " -e nu -c 'nitch; exec nu'"))
|
|
hl.bind(mainMod .. " + Shift + T", hl.dsp.exec_cmd("launch-timer"))
|
|
hl.bind(mainMod .. " + N", hl.dsp.exec_cmd(terminal .. " -e nvim"))
|
|
hl.bind(mainMod .. " + Z", hl.dsp.exec_cmd("uwsm app -- zeditor"))
|
|
|
|
-- Keybinds: misc
|
|
hl.bind(mainMod .. " + O", hl.dsp.exec_cmd("hyprctl dispatch setprop activewindow opaque toggle"))
|
|
hl.bind(mainMod .. " + B", hl.dsp.exec_cmd("uwsm app -- thunar"))
|
|
hl.bind(mainMod .. " + Shift + B", hl.dsp.exec_cmd("uwsm app -- vivaldi"))
|
|
hl.bind(mainMod .. " + Escape", hl.dsp.exec_cmd("uwsm app -- wlogout -p layer-shell"))
|
|
|
|
-- Keybinds: window management
|
|
hl.bind(mainMod .. " + Space", hl.dsp.window.float({ action = "toggle" }))
|
|
hl.bind(mainMod .. " + Q", hl.dsp.window.close())
|
|
hl.bind(mainMod .. " + M", hl.dsp.exit())
|
|
hl.bind(mainMod .. " + F", hl.dsp.window.fullscreen())
|
|
hl.bind(mainMod .. " + Shift + V", hl.dsp.window.float({ action = "toggle" }))
|
|
|
|
-- Keybinds: launchers
|
|
hl.bind(mainMod .. " + D", hl.dsp.exec_cmd('uwsm app -- rofi -show drun -run-command "uwsm app -- {cmd}"'))
|
|
hl.bind(mainMod .. " + V", hl.dsp.exec_cmd("uwsm app -- cliphist list | rofi -dmenu | cliphist decode | wl-copy"))
|
|
hl.bind(mainMod .. " + C", hl.dsp.exec_cmd("bash -c 'FILE=/tmp/screenshot_$(date +%s).png; grim -g \"$(slurp)\" \"$FILE\" && ksnip \"$FILE\"'"))
|
|
hl.bind(mainMod .. " + Shift + S", hl.dsp.exec_cmd("uwsm app -- rofi -show emoji"))
|
|
hl.bind(mainMod .. " + P", hl.dsp.exec_cmd("uwsm app -- rofi-pass"))
|
|
hl.bind(mainMod .. " + Shift + P", hl.dsp.window.pseudo())
|
|
|
|
-- Keybinds: STT push-to-talk
|
|
hl.bind(mainMod .. " + R", hl.dsp.exec_cmd("stt-ptt start"))
|
|
hl.bind(mainMod .. " + R", hl.dsp.exec_cmd("stt-ptt stop"), { release = true })
|
|
hl.bind(mainMod .. " + S", hl.dsp.exec_cmd("stt-ptt start"))
|
|
hl.bind(mainMod .. " + S", hl.dsp.exec_cmd("stt-ptt format-stop"), { release = true })
|
|
|
|
-- Keybinds: focus
|
|
hl.bind(mainMod .. " + H", hl.dsp.focus({ direction = "left" }))
|
|
hl.bind(mainMod .. " + L", hl.dsp.focus({ direction = "right" }))
|
|
hl.bind(mainMod .. " + K", hl.dsp.focus({ direction = "up" }))
|
|
hl.bind(mainMod .. " + J", hl.dsp.focus({ direction = "down" }))
|
|
|
|
-- Keybinds: workspaces (1-10)
|
|
for i = 1, 10 do
|
|
local key = i % 10
|
|
hl.bind(mainMod .. " + " .. key, hl.dsp.focus({ workspace = i }))
|
|
hl.bind(mainMod .. " + Shift + " .. key, hl.dsp.window.move({ workspace = i }))
|
|
end
|
|
hl.bind(mainMod .. " + mouse_down", hl.dsp.focus({ workspace = "e+1" }))
|
|
hl.bind(mainMod .. " + mouse_up", hl.dsp.focus({ workspace = "e-1" }))
|
|
|
|
-- Keybinds: mouse (drag & resize)
|
|
hl.bind(mainMod .. " + mouse:272", hl.dsp.window.drag(), { mouse = true })
|
|
hl.bind(mainMod .. " + mouse:273", hl.dsp.window.resize(), { mouse = true })
|
|
|
|
-- Window rules: floating dialogs
|
|
for _, cls in ipairs({
|
|
"file_progress", "confirm", "dialog", "download",
|
|
"notification", "error", "splash", "confirmreset",
|
|
}) do
|
|
hl.window_rule({ match = { class = cls }, float = true })
|
|
end
|
|
|
|
-- Window rules: title-based floating
|
|
hl.window_rule({ match = { title = "Open File" }, float = true })
|
|
hl.window_rule({ match = { title = "branchdialog" }, float = true })
|
|
|
|
-- Window rules: audio/video
|
|
hl.window_rule({ match = { class = "pavucontrol-qt" }, float = true })
|
|
hl.window_rule({ match = { class = "pavucontrol" }, float = true })
|
|
hl.window_rule({ match = { class = "^espanso$" }, float = true })
|
|
|
|
-- Window rules: wlogout
|
|
hl.window_rule({ match = { class = "wlogout" }, fullscreen = true })
|
|
hl.window_rule({ match = { title = "wlogout" }, float = true, fullscreen = true })
|
|
|
|
-- Window rules: mpv
|
|
hl.window_rule({ match = { class = "mpv" }, float = true, idle_inhibit = "focus", opacity = 1.0 })
|
|
|
|
-- Window rules: misc floating
|
|
hl.window_rule({ match = { title = "^Media viewer$" }, float = true })
|
|
hl.window_rule({ match = { title = "^Volume Control$" }, float = true })
|
|
hl.window_rule({ match = { title = "^Picture-in-Picture$" }, float = true })
|
|
|
|
-- Window rules: pomodoro (consolidated)
|
|
hl.window_rule({
|
|
match = { title = "^floating-pomodoro$" },
|
|
float = true, size = { 250, 50 }, move = { 12, "monitor_h-150" }, pin = true,
|
|
})
|
|
|
|
-- Window rules: streamlabs (consolidated)
|
|
hl.window_rule({
|
|
match = { initial_title = ".*streamlabs.com.*" },
|
|
float = true, pin = true, size = { 800, 400 }, opacity = 0.5,
|
|
idle_inhibit = "focus", no_anim = true, decorate = false, no_shadow = true, no_blur = true,
|
|
})
|
|
hl.window_rule({ match = { initial_title = ".*alert-box.*" }, move = { "100%-820", 102 } })
|
|
hl.window_rule({ match = { initial_title = ".*chat-box.*" }, move = { "100%-820", 512 } })
|
|
|
|
-- Window rules: vibe-typer
|
|
hl.window_rule({ match = { class = "^vibe-typer$", title = "^Recording Indicator$" }, no_blur = true })
|
|
|
|
-- Window rules: xwayland border
|
|
hl.window_rule({ match = { xwayland = true }, border_color = "rgb(ffffff)" })
|
|
|
|
-- Startup commands
|
|
hl.on("hyprland.start", function()
|
|
hl.exec_cmd("hyprpanel")
|
|
hl.exec_cmd("while ! hyprpaper-random; do sleep 0.5; done")
|
|
hl.exec_cmd("wl-paste --type text --watch cliphist store")
|
|
hl.exec_cmd("wl-paste --type image --watch cliphist store")
|
|
hl.exec_cmd("vibetyper")
|
|
end)
|
|
'';
|
|
};
|
|
|
|
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";
|
|
}
|
|
];
|
|
};
|
|
};
|
|
|
|
services.hyprpaper.enable = true;
|
|
|
|
programs.hyprlock = {
|
|
enable = true;
|
|
settings = {
|
|
"$font" = "JetBrainsMono Nerd Font";
|
|
"$base" = "rgb(${p.base00})";
|
|
"$text" = "rgb(${p.base05})";
|
|
"$textAlpha" = "${p.base05}";
|
|
"$accentAlpha" = "${p.base0D}";
|
|
"$red" = "rgb(${p.base08})";
|
|
"$yellow" = "rgb(${p.base0A})";
|
|
|
|
general = {
|
|
hide_cursor = true;
|
|
};
|
|
|
|
background = {
|
|
monitor = "";
|
|
path = "${config.home.homeDirectory}/.config/hypr/wallpapers/wallhaven-lmmo8r.jpg";
|
|
blur_passes = 0;
|
|
color = "rgb(${p.base00})";
|
|
};
|
|
|
|
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";
|
|
}
|
|
];
|
|
|
|
input-field = [
|
|
{
|
|
monitor = "";
|
|
size = "300, 60";
|
|
outline_thickness = 4;
|
|
dots_size = 0.2;
|
|
dots_spacing = 0.2;
|
|
dots_center = true;
|
|
outer_color = "rgb(${p.base0D})";
|
|
inner_color = "rgb(${p.base00})";
|
|
font_color = "rgb(${p.base05})";
|
|
fade_on_empty = false;
|
|
placeholder_text = ''<span foreground="##${p.base05}"> Logged in as <span foreground="##${p.base0D}">$USER</span></span>'';
|
|
hide_input = false;
|
|
check_color = "rgb(${p.base0D})";
|
|
fail_color = "rgb(${p.base08})";
|
|
fail_text = ''<i>$FAIL <b>($ATTEMPTS)</b></i>'';
|
|
capslock_color = "rgb(${p.base0A})";
|
|
position = "0, -35";
|
|
halign = "center";
|
|
valign = "center";
|
|
}
|
|
];
|
|
};
|
|
};
|
|
};
|
|
}
|