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

@@ -83,3 +83,29 @@ every home-manager module through `extraSpecialArgs`. Tools that only accept a
raw config file (starship, rofi, the herdr picker) carry `@color@` placeholders
that are substituted from the same palette at build time, so changing an accent
means editing one file.
## Editing configs: live vs generated
Most configs are generated from Nix and symlinked read-only from the store, so
they need a `rebuild switch` to change:
- `foot.ini`, `hyprlock.conf`, `hypridle.conf`, `mako/config`, `starship.toml`,
`rofi/config.rasi`, `.zshenv`/`.zshrc` (Nix attrs, themed)
Three configs are instead symlinked **straight from the repo** with
`mkOutOfStoreSymlink`, so edits apply without a rebuild:
| Live file | Applies on |
| --- | --- |
| `home/hypr/hyprland.lua` | `hyprctl reload` |
| `home/hypr/quickshell/**` | quickshell restart |
| `home/nvim/config/**` (incl. `lazy-lock.json`) | next nvim start; `:Lazy update` writes here |
Because `hyprland.lua` is live, the palette and per-host monitors are emitted as
generated fragments, `~/.config/hypr/theme.lua` and
`~/.config/hypr/monitors.lua`, which the live file loads with `dofile`. Edit the
Lua directly for anything else.
The tradeoff: live files are not part of the Nix closure, so the repo must exist
at `my.flakeDir` for the symlinks to resolve, and uncommitted edits can drift
from git. Keep them committed like any other tracked file.

282
home/hypr/hyprland.lua Normal file
View File

@@ -0,0 +1,282 @@
-- Silicon Volt Hyprland configuration (hand-maintained).
--
-- home-manager symlinks this file straight from the repo, so edits apply
-- on `hyprctl reload` without a rebuild. The generated fragments
-- `theme.lua` (from home/theme.nix) and `monitors.lua` (from hypr.monitors)
-- live next to this file and are loaded below.
local configDir = (os.getenv("XDG_CONFIG_HOME") or (os.getenv("HOME") .. "/.config")) .. "/hypr"
local theme = dofile(configDir .. "/theme.lua")
local monitors = dofile(configDir .. "/monitors.lua")
-- settings.locals
local browser = "brave"
local mainMod = "SUPER"
local terminal = "foot"
-- settings.curve
hl.curve("myBezier", {
["points"] = {
{
0.05,
0.9
},
{
0.1,
1.05
}
},
["type"] = "bezier"
})
-- settings.animation
hl.animation({
["bezier"] = "myBezier",
["enabled"] = true,
["leaf"] = "windows",
["speed"] = 7
})
hl.animation({
["bezier"] = "default",
["enabled"] = true,
["leaf"] = "windowsOut",
["speed"] = 7,
["style"] = "popin 80%"
})
hl.animation({
["bezier"] = "default",
["enabled"] = true,
["leaf"] = "border",
["speed"] = 10
})
hl.animation({
["bezier"] = "default",
["enabled"] = true,
["leaf"] = "borderangle",
["speed"] = 8
})
hl.animation({
["bezier"] = "default",
["enabled"] = true,
["leaf"] = "fade",
["speed"] = 7
})
hl.animation({
["bezier"] = "default",
["enabled"] = true,
["leaf"] = "workspaces",
["speed"] = 6
})
-- settings.bind
hl.bind((mainMod .. " + Q"), (hl.dsp.exec_cmd(terminal)))
hl.bind((mainMod .. " + C"), (hl.dsp.window.close()))
hl.bind((mainMod .. " + SPACE"), (hl.dsp.exec_cmd("rofi -show combi -show-icons")))
hl.bind((mainMod .. " + N"), (hl.dsp.exec_cmd("swaync-client -t -sw")))
hl.bind((mainMod .. " + TAB"), (hl.dsp.exec_cmd("qs ipc call overview toggle")))
hl.bind((mainMod .. " + V"), (hl.dsp.window.float({ action = "toggle" })))
hl.bind((mainMod .. " + R"), (hl.dsp.exec_cmd("rofi -show combi")))
hl.bind((mainMod .. " + G"), (hl.dsp.group.toggle()))
hl.bind((mainMod .. " + F"), (hl.dsp.window.fullscreen({ fullscreen = 0 })))
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" })))
hl.bind((mainMod .. " + e"), (hl.dsp.group.next()))
hl.bind((mainMod .. " + d"), (hl.dsp.group.prev()))
hl.bind((mainMod .. " + SHIFT + h"), (hl.dsp.window.move({ direction = "left" })))
hl.bind((mainMod .. " + SHIFT + l"), (hl.dsp.window.move({ direction = "right" })))
hl.bind((mainMod .. " + SHIFT + k"), (hl.dsp.window.move({ direction = "up" })))
hl.bind((mainMod .. " + SHIFT + j"), (hl.dsp.window.move({ direction = "down" })))
hl.bind((mainMod .. " + mouse_down"), (hl.dsp.focus({ workspace = "e+1" })))
hl.bind((mainMod .. " + mouse_up"), (hl.dsp.focus({ workspace = "e-1" })))
hl.bind((mainMod .. " + S"), (hl.dsp.exec_cmd("hyprlock")))
hl.bind((mainMod .. " + b"), (hl.dsp.exec_cmd(browser)))
hl.bind((mainMod .. " + RETURN"), (hl.dsp.workspace.toggle_special("scratch")))
hl.bind((mainMod .. " + SHIFT + RETURN"), (hl.dsp.window.move({ workspace = "special:scratch" })))
hl.bind((mainMod .. " + M"), (hl.dsp.workspace.toggle_special("messaging")))
hl.bind((mainMod .. " + mouse:272"), (hl.dsp.window.drag()), {
["mouse"] = true
})
hl.bind((mainMod .. " + mouse:273"), (hl.dsp.window.resize()), {
["mouse"] = true
})
hl.bind("XF86AudioRaiseVolume", (hl.dsp.exec_cmd("pactl -- set-sink-volume @DEFAULT_SINK@ +5%")), {
["repeating"] = true
})
hl.bind("XF86AudioLowerVolume", (hl.dsp.exec_cmd("pactl -- set-sink-volume @DEFAULT_SINK@ -5%")), {
["locked"] = true,
["repeating"] = true
})
hl.bind("XF86AudioMute", (hl.dsp.exec_cmd("pactl -- set-sink-mute @DEFAULT_SINK@ toggle")), {
["locked"] = true,
["repeating"] = true
})
hl.bind("XF86MonBrightnessUp", (hl.dsp.exec_cmd("brightnessctl set 5%+")), {
["locked"] = true,
["repeating"] = true
})
hl.bind("XF86MonBrightnessDown", (hl.dsp.exec_cmd("brightnessctl set 5%-")), {
["locked"] = true,
["repeating"] = true
})
hl.bind("switch:off:Lid Switch", (hl.dsp.exec_cmd("m=$(hyprctl monitors | awk '$1==\"Monitor\"{print $2; exit}'); [ -n \"$m\" ] && hyprctl keyword monitor \"$m, preferred, auto, auto\"")), {
["locked"] = true
})
hl.bind("switch:on:Lid Switch", (hl.dsp.exec_cmd("m=$(hyprctl monitors | awk '$1==\"Monitor\"{print $2; exit}'); [ -n \"$m\" ] && hyprctl keyword monitor \"$m, disable\"")), {
["locked"] = true
})
hl.bind((mainMod .. " + 1"), (hl.dsp.focus({ workspace = 1 })))
hl.bind((mainMod .. " + SHIFT + 1"), (hl.dsp.window.move({ workspace = 1 })))
hl.bind((mainMod .. " + 2"), (hl.dsp.focus({ workspace = 2 })))
hl.bind((mainMod .. " + SHIFT + 2"), (hl.dsp.window.move({ workspace = 2 })))
hl.bind((mainMod .. " + 3"), (hl.dsp.focus({ workspace = 3 })))
hl.bind((mainMod .. " + SHIFT + 3"), (hl.dsp.window.move({ workspace = 3 })))
hl.bind((mainMod .. " + 4"), (hl.dsp.focus({ workspace = 4 })))
hl.bind((mainMod .. " + SHIFT + 4"), (hl.dsp.window.move({ workspace = 4 })))
hl.bind((mainMod .. " + 5"), (hl.dsp.focus({ workspace = 5 })))
hl.bind((mainMod .. " + SHIFT + 5"), (hl.dsp.window.move({ workspace = 5 })))
hl.bind((mainMod .. " + 6"), (hl.dsp.focus({ workspace = 6 })))
hl.bind((mainMod .. " + SHIFT + 6"), (hl.dsp.window.move({ workspace = 6 })))
hl.bind((mainMod .. " + 7"), (hl.dsp.focus({ workspace = 7 })))
hl.bind((mainMod .. " + SHIFT + 7"), (hl.dsp.window.move({ workspace = 7 })))
hl.bind((mainMod .. " + 8"), (hl.dsp.focus({ workspace = 8 })))
hl.bind((mainMod .. " + SHIFT + 8"), (hl.dsp.window.move({ workspace = 8 })))
hl.bind((mainMod .. " + 9"), (hl.dsp.focus({ workspace = 9 })))
hl.bind((mainMod .. " + SHIFT + 9"), (hl.dsp.window.move({ workspace = 9 })))
hl.bind((mainMod .. " + 0"), (hl.dsp.focus({ workspace = 10 })))
hl.bind((mainMod .. " + SHIFT + 0"), (hl.dsp.window.move({ workspace = 10 })))
-- settings.config
hl.config({
["animations"] = {
["enabled"] = true
},
["cursor"] = {
["inactive_timeout"] = 3,
["no_hardware_cursors"] = false
},
["decoration"] = {
["blur"] = {
["brightness"] = 0.9,
["contrast"] = 1.1,
["enabled"] = true,
["ignore_opacity"] = true,
["new_optimizations"] = true,
["passes"] = 2,
["size"] = 4,
["vibrancy"] = 0.1
},
["rounding"] = 4,
["shadow"] = {
["color"] = "rgba(00000066)",
["enabled"] = true,
["range"] = 12,
["render_power"] = 3
}
},
["dwindle"] = {
["preserve_split"] = true
},
["general"] = {
["border_size"] = 2,
["col"] = {
["active_border"] = {
["angle"] = 45,
["colors"] = {
"rgba(" .. theme.bare.accentVolt .. "ee)",
"rgba(" .. theme.bare.accentVoltLight .. "ee)"
}
},
["inactive_border"] = "rgba(" .. theme.bare.textPrimary .. "14)"
},
["gaps_in"] = 5,
["gaps_out"] = 10,
["layout"] = "dwindle"
},
["group"] = {
["col"] = {
["border_active"] = {
["angle"] = 45,
["colors"] = {
"rgba(" .. theme.bare.accentVolt .. "ee)",
"rgba(" .. theme.bare.accentVoltLight .. "ee)"
}
},
["border_inactive"] = "rgba(" .. theme.bare.textPrimary .. "14)"
},
["groupbar"] = {
["col"] = {
["active"] = "rgba(" .. theme.bare.accentVolt .. "22)",
["inactive"] = "rgba(" .. theme.bare.textPrimary .. "08)"
},
["font_family"] = "JetBrainsMono Nerd Font",
["font_size"] = 14
}
},
["input"] = {
["follow_mouse"] = 1,
["kb_layout"] = "us",
["repeat_delay"] = 300,
["repeat_rate"] = 50,
["sensitivity"] = 0,
["touchpad"] = {
["natural_scroll"] = false
}
},
["master"] = {
["new_status"] = "master"
},
["misc"] = {
["disable_hyprland_logo"] = true,
["disable_splash_rendering"] = true
}
})
-- settings.env
hl.env("XCURSOR_SIZE", "24")
hl.env("XDG_SESSION_TYPE", "wayland")
-- monitors (generated from hypr.monitors)
for _, m in ipairs(monitors) do
hl.monitor(m)
end
-- settings.window_rule
hl.window_rule({
["match"] = {
["class"] = "Signal"
},
["name"] = "signal-messaging",
["workspace"] = "special:messaging"
})
hl.window_rule({
["match"] = {
["class"] = "vesktop"
},
["name"] = "vesktop-messaging",
["workspace"] = "special:messaging"
})
hl.window_rule({
["float"] = false,
["match"] = {
["class"] = "ghidra-Ghidra"
},
["name"] = "ghidra-tile"
})
-- settings.workspace_rule
hl.workspace_rule({
["on_created_empty"] = "[] togglegroup",
["workspace"] = "name:messaging"
})
-- Start the systemd user session target after importing the Wayland
-- environment, so the desktop services declared in home/hypr/services.nix
-- and home/hypr/quickshell.nix (hypridle, hyprpaper, mako, cliphist,
-- quickshell, polkit agent) are pulled in. This replaces the hook that the
-- home-manager hyprland module used to generate.
hl.on("hyprland.start", function()
hl.exec_cmd("dbus-update-activation-environment --systemd DISPLAY HYPRLAND_INSTANCE_SIGNATURE WAYLAND_DISPLAY XDG_CURRENT_DESKTOP XDG_SESSION_TYPE && systemctl --user start graphical-session.target")
end)

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;
};
}

View File

@@ -1,4 +1,10 @@
{ pkgs, inputs, ... }:
{
pkgs,
inputs,
config,
my,
...
}:
let
quickshellPkg =
@@ -16,11 +22,10 @@ in
{
# Quickshell overview module for Hyprland (Super+Tab workspace overview).
# Vendored from: https://github.com/Shanu-Kumawat/quickshell-overview
# Placed at the quickshell config root so a bare `qs` finds shell.qml.
xdg.configFile."quickshell" = {
source = ./quickshell;
recursive = true;
};
# Symlinked straight from the repo so edits are live; placed at the
# quickshell config root so a bare `qs` finds shell.qml.
xdg.configFile."quickshell".source =
config.lib.file.mkOutOfStoreSymlink "${my.flakeDir}/home/hypr/quickshell";
# Run quickshell as a systemd user service tied to the Wayland session.
programs.quickshell = {

View File

@@ -2,6 +2,7 @@
config,
pkgs,
lib,
my,
...
}:
@@ -72,15 +73,14 @@ in
];
};
# Symlink LazyVim configuration to ~/.config/nvim recursively. This includes
# lazy-lock.json, which pins every plugin to a specific commit for
# reproducible builds. To update plugins, run `:Lazy update` in a writable
# copy and commit the resulting lazy-lock.json back to this repo, since the
# linked file is read-only.
xdg.configFile."nvim" = {
source = ./config;
recursive = true;
};
# Live LazyVim config symlinked straight from the repo: edits (and
# `:Lazy update` writes to lazy-lock.json) apply without a rebuild.
xdg.configFile."nvim/init.lua".source =
config.lib.file.mkOutOfStoreSymlink "${my.flakeDir}/home/nvim/config/init.lua";
xdg.configFile."nvim/lua".source =
config.lib.file.mkOutOfStoreSymlink "${my.flakeDir}/home/nvim/config/lua";
xdg.configFile."nvim/lazy-lock.json".source =
config.lib.file.mkOutOfStoreSymlink "${my.flakeDir}/home/nvim/config/lazy-lock.json";
# Symlink precompiled Treesitter parsers directly into ~/.config/nvim/parser
xdg.configFile."nvim/parser".source = "${treesitterParsers}/parser";