hypr,nvim,quickshell: make configs live-editable

Symlink three configs straight from the repo with mkOutOfStoreSymlink so
edits apply without a rebuild:

- home/hypr/hyprland.lua  -> reload with `hyprctl reload`
- home/hypr/quickshell/** -> restart quickshell
- home/nvim/config/**     -> next nvim start (lazy-lock.json now writable)

Hyprland's config was generated Lua; extract it to a hand-maintained file
and keep the palette and per-host monitors as generated fragments
(theme.lua / monitors.lua) loaded via dofile. The file also restores the
startup hook that imports the Wayland env into systemd and starts
graphical-session.target, which the removed home-manager hyprland module
used to provide.

Verified: lua syntax + execution against a stub hl, and flake check.
This commit is contained in:
2026-09-19 01:14:33 -07:00
parent 8010cea0bc
commit d1e243dd12
5 changed files with 374 additions and 575 deletions

View File

@@ -2,11 +2,47 @@
lib,
config,
theme,
my,
...
}:
let
mkLua = lib.generators.mkLuaInline;
# Minimal Lua value renderer for the generated fragments.
toLua =
v:
if builtins.isString v then
''"${v}"''
else if builtins.isBool v then
(if v then "true" else "false")
else if builtins.isInt v || builtins.isFloat v then
toString v
else if v == null then
"nil"
else if builtins.isList v then
"{ " + lib.concatMapStringsSep ", " toLua v + " }"
else if builtins.isAttrs v then
"{ " + lib.concatStringsSep ", " (lib.mapAttrsToList (k: val: "${k} = ${toLua val}") v) + " }"
else
throw "toLua: unsupported value type";
themeLua = "return " + toLua { inherit (theme) colors bare; };
monitorsLua =
"return "
+ toLua (
map (
m:
{
inherit (m)
output
mode
position
scale
;
}
// lib.optionalAttrs (m.vrr != null) { inherit (m) vrr; }
) config.hypr.monitors
);
in
{
# -------------------------------------------------------------------------
@@ -14,8 +50,8 @@ in
#
# Hosts declare their displays once, in hosts/<host>/default.nix, via:
# home-manager.users.<my.username>.hypr.monitors = [ { ... } ];
# and the setting flows into Hyprland here. The default (empty output +
# preferred mode) works on any machine with any display.
# The list is rendered to ~/.config/hypr/monitors.lua, which the
# hand-maintained hyprland.lua pulls in with require("monitors").
# -------------------------------------------------------------------------
options.hypr.monitors = lib.mkOption {
description = "Hyprland monitor rules for this host";
@@ -59,563 +95,13 @@ in
};
config = {
wayland.windowManager.hyprland.settings.monitor = map (
m:
{
inherit (m)
output
mode
position
scale
;
}
// (lib.optionalAttrs (m.vrr != null) { inherit (m) vrr; })
) config.hypr.monitors;
# Live, hand-maintained config: symlinked straight to the repo so edits
# apply on `hyprctl reload` without a rebuild.
xdg.configFile."hypr/hyprland.lua".source =
config.lib.file.mkOutOfStoreSymlink "${my.flakeDir}/home/hypr/hyprland.lua";
wayland.windowManager.hyprland = {
enable = true;
configType = "lua";
package = null; # Hyprland package is managed system-wide via NixOS programs.hyprland
settings = {
# -----------------------------------------------------------------------
# 1. Variables & Modifiers (Rendered as Lua local variables)
# -----------------------------------------------------------------------
mainMod = {
_var = "SUPER";
};
terminal = {
_var = "foot";
};
browser = {
_var = "brave";
};
# -----------------------------------------------------------------------
# 2. Monitors (hl.monitor) - declared per-host via hypr.monitors
# (see hosts/<host>/default.nix); defaults to any display @ preferred.
# -----------------------------------------------------------------------
# -----------------------------------------------------------------------
# 3. Environment Variables (hl.env)
# -----------------------------------------------------------------------
env = [
{
_args = [
"XCURSOR_SIZE"
"24"
];
}
{
_args = [
"XDG_SESSION_TYPE"
"wayland"
];
}
];
# -----------------------------------------------------------------------
# 4. Main Configuration (hl.config) - Strictly validates against Hyprland 0.55+ schema
# -----------------------------------------------------------------------
config = {
input = {
kb_layout = "us";
repeat_delay = 300;
repeat_rate = 50;
follow_mouse = 1;
sensitivity = 0;
touchpad = {
natural_scroll = false;
};
};
cursor = {
inactive_timeout = 3;
# Hardware cursors ON (explicit). Note: this replaces the old
# WLR_NO_HARDWARE_CURSORS=1, which forced software rendering.
no_hardware_cursors = false;
};
general = {
gaps_in = 5;
gaps_out = 10;
border_size = 2;
col = {
# Silicon Volt: electric lime gradient on focus, glass gray at rest
active_border = {
colors = [
"rgba(${theme.bare.accentVolt}ee)"
"rgba(${theme.bare.accentVoltLight}ee)"
];
angle = 45;
};
inactive_border = "rgba(${theme.bare.textPrimary}14)";
};
layout = "dwindle";
};
group = {
groupbar = {
font_size = 14;
font_family = "JetBrainsMono Nerd Font";
col = {
active = "rgba(${theme.bare.accentVolt}22)";
inactive = "rgba(${theme.bare.textPrimary}08)";
};
};
col = {
border_active = {
colors = [
"rgba(${theme.bare.accentVolt}ee)"
"rgba(${theme.bare.accentVoltLight}ee)"
];
angle = 45;
};
border_inactive = "rgba(${theme.bare.textPrimary}14)";
};
};
misc = {
disable_hyprland_logo = true;
disable_splash_rendering = true;
};
decoration = {
rounding = 4;
# Subtle glass blur to match the brutalist-glass aesthetic
blur = {
enabled = true;
size = 4;
passes = 2;
new_optimizations = true;
ignore_opacity = true;
contrast = 1.1;
brightness = 0.9;
vibrancy = 0.1;
};
shadow = {
enabled = true;
range = 12;
render_power = 3;
color = "rgba(00000066)";
};
};
animations = {
enabled = true;
};
dwindle = {
preserve_split = true;
};
master = {
new_status = "master";
};
};
# -----------------------------------------------------------------------
# 5. Curves & Animations (hl.curve & hl.animation)
# -----------------------------------------------------------------------
curve = [
{
_args = [
"myBezier"
{
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";
}
];
# -----------------------------------------------------------------------
# 6. Autostart Daemons & Apps
# -----------------------------------------------------------------------
# Desktop daemons (quickshell, hyprpaper, mako, cliphist and the
# polkit agent) are declared as systemd user services in
# home/hypr/services.nix and home/hypr/quickshell.nix, tied to the
# Wayland session target instead of exec-once.
# -----------------------------------------------------------------------
# 7. Keybindings (hl.bind)
# -----------------------------------------------------------------------
bind = [
# Launchers & Window Controls
{
_args = [
(mkLua "mainMod .. \" + Q\"")
(mkLua "hl.dsp.exec_cmd(terminal)")
];
}
{
_args = [
(mkLua "mainMod .. \" + C\"")
(mkLua "hl.dsp.window.close()")
];
}
{
_args = [
(mkLua "mainMod .. \" + SPACE\"")
(mkLua "hl.dsp.exec_cmd(\"rofi -show combi -show-icons\")")
];
}
{
_args = [
(mkLua "mainMod .. \" + N\"")
(mkLua "hl.dsp.exec_cmd(\"swaync-client -t -sw\")")
];
}
{
_args = [
(mkLua "mainMod .. \" + TAB\"")
(mkLua "hl.dsp.exec_cmd(\"qs ipc call overview toggle\")")
];
}
{
_args = [
(mkLua "mainMod .. \" + V\"")
(mkLua "hl.dsp.window.float({ action = \"toggle\" })")
];
}
{
_args = [
(mkLua "mainMod .. \" + R\"")
(mkLua "hl.dsp.exec_cmd(\"rofi -show combi\")")
];
}
{
_args = [
(mkLua "mainMod .. \" + G\"")
(mkLua "hl.dsp.group.toggle()")
];
}
{
_args = [
(mkLua "mainMod .. \" + F\"")
(mkLua "hl.dsp.window.fullscreen({ fullscreen = 0 })")
];
}
# Move focus with mainMod + vim
{
_args = [
(mkLua "mainMod .. \" + h\"")
(mkLua "hl.dsp.focus({ direction = \"left\" })")
];
}
{
_args = [
(mkLua "mainMod .. \" + l\"")
(mkLua "hl.dsp.focus({ direction = \"right\" })")
];
}
{
_args = [
(mkLua "mainMod .. \" + k\"")
(mkLua "hl.dsp.focus({ direction = \"up\" })")
];
}
{
_args = [
(mkLua "mainMod .. \" + j\"")
(mkLua "hl.dsp.focus({ direction = \"down\" })")
];
}
# Move focus within a grouped page
{
_args = [
(mkLua "mainMod .. \" + e\"")
(mkLua "hl.dsp.group.next()")
];
}
{
_args = [
(mkLua "mainMod .. \" + d\"")
(mkLua "hl.dsp.group.prev()")
];
}
# Move window or group with vim keys
{
_args = [
(mkLua "mainMod .. \" + SHIFT + h\"")
(mkLua "hl.dsp.window.move({ direction = \"left\" })")
];
}
{
_args = [
(mkLua "mainMod .. \" + SHIFT + l\"")
(mkLua "hl.dsp.window.move({ direction = \"right\" })")
];
}
{
_args = [
(mkLua "mainMod .. \" + SHIFT + k\"")
(mkLua "hl.dsp.window.move({ direction = \"up\" })")
];
}
{
_args = [
(mkLua "mainMod .. \" + SHIFT + j\"")
(mkLua "hl.dsp.window.move({ direction = \"down\" })")
];
}
# Scroll through existing workspaces with mainMod + scroll
{
_args = [
(mkLua "mainMod .. \" + mouse_down\"")
(mkLua "hl.dsp.focus({ workspace = \"e+1\" })")
];
}
{
_args = [
(mkLua "mainMod .. \" + mouse_up\"")
(mkLua "hl.dsp.focus({ workspace = \"e-1\" })")
];
}
# Lock shortcut
{
_args = [
(mkLua "mainMod .. \" + S\"")
(mkLua "hl.dsp.exec_cmd(\"hyprlock\")")
];
}
# Quick access applications
{
_args = [
(mkLua "mainMod .. \" + b\"")
(mkLua "hl.dsp.exec_cmd(browser)")
];
}
# Scratch workspace
{
_args = [
(mkLua "mainMod .. \" + RETURN\"")
(mkLua "hl.dsp.workspace.toggle_special(\"scratch\")")
];
}
{
_args = [
(mkLua "mainMod .. \" + SHIFT + RETURN\"")
(mkLua "hl.dsp.window.move({ workspace = \"special:scratch\" })")
];
}
# Messaging workspace
{
_args = [
(mkLua "mainMod .. \" + M\"")
(mkLua "hl.dsp.workspace.toggle_special(\"messaging\")")
];
}
# Move/resize windows with mouse and dragging
{
_args = [
(mkLua "mainMod .. \" + mouse:272\"")
(mkLua "hl.dsp.window.drag()")
{ mouse = true; }
];
}
{
_args = [
(mkLua "mainMod .. \" + mouse:273\"")
(mkLua "hl.dsp.window.resize()")
{ mouse = true; }
];
}
# Function Row Keys
{
_args = [
"XF86AudioRaiseVolume"
(mkLua "hl.dsp.exec_cmd(\"pactl -- set-sink-volume @DEFAULT_SINK@ +5%\")")
{ repeating = true; }
];
}
{
_args = [
"XF86AudioLowerVolume"
(mkLua "hl.dsp.exec_cmd(\"pactl -- set-sink-volume @DEFAULT_SINK@ -5%\")")
{
repeating = true;
locked = true;
}
];
}
{
_args = [
"XF86AudioMute"
(mkLua "hl.dsp.exec_cmd(\"pactl -- set-sink-mute @DEFAULT_SINK@ toggle\")")
{
repeating = true;
locked = true;
}
];
}
{
_args = [
"XF86MonBrightnessUp"
(mkLua "hl.dsp.exec_cmd(\"brightnessctl set 5%+\")")
{
repeating = true;
locked = true;
}
];
}
{
_args = [
"XF86MonBrightnessDown"
(mkLua "hl.dsp.exec_cmd(\"brightnessctl set 5%-\")")
{
repeating = true;
locked = true;
}
];
}
# Lid switch - generically detects the embedded (eDP*) panel on any laptop
{
_args = [
"switch:off:Lid Switch"
(mkLua "hl.dsp.exec_cmd(\"m=$(hyprctl monitors | awk '$1==\\\"Monitor\\\"{print $2; exit}'); [ -n \\\"$m\\\" ] && hyprctl keyword monitor \\\"$m, preferred, auto, auto\\\"\")")
{ locked = true; }
];
}
{
_args = [
"switch:on:Lid Switch"
(mkLua "hl.dsp.exec_cmd(\"m=$(hyprctl monitors | awk '$1==\\\"Monitor\\\"{print $2; exit}'); [ -n \\\"$m\\\" ] && hyprctl keyword monitor \\\"$m, disable\\\"\")")
{ locked = true; }
];
}
]
# Workspace 1-9 switching and moving
++ (lib.concatMap (i: [
{
_args = [
(mkLua "mainMod .. \" + ${toString i}\"")
(mkLua "hl.dsp.focus({ workspace = ${toString i} })")
];
}
{
_args = [
(mkLua "mainMod .. \" + SHIFT + ${toString i}\"")
(mkLua "hl.dsp.window.move({ workspace = ${toString i} })")
];
}
]) (lib.range 1 9))
# Workspace 10 switching and moving (mapped to key 0)
++ [
{
_args = [
(mkLua "mainMod .. \" + 0\"")
(mkLua "hl.dsp.focus({ workspace = 10 })")
];
}
{
_args = [
(mkLua "mainMod .. \" + SHIFT + 0\"")
(mkLua "hl.dsp.window.move({ workspace = 10 })")
];
}
];
# -----------------------------------------------------------------------
# 8. Workspace Rules (hl.workspace_rule)
# -----------------------------------------------------------------------
workspace_rule = [
{
workspace = "name:messaging";
on_created_empty = "[] togglegroup";
}
];
# -----------------------------------------------------------------------
# 9. Window Rules (hl.window_rule)
# -----------------------------------------------------------------------
window_rule = [
{
name = "signal-messaging";
match = {
class = "Signal";
};
workspace = "special:messaging";
}
{
name = "vesktop-messaging";
match = {
class = "vesktop";
};
workspace = "special:messaging";
}
{
name = "ghidra-tile";
match = {
class = "ghidra-Ghidra";
};
float = false;
}
];
};
};
# Generated fragments required by the live config.
xdg.configFile."hypr/theme.lua".text = themeLua;
xdg.configFile."hypr/monitors.lua".text = monitorsLua;
};
}