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 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 that are substituted from the same palette at build time, so changing an accent
means editing one file. 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, lib,
config, config,
theme, theme,
my,
... ...
}: }:
let 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 in
{ {
# ------------------------------------------------------------------------- # -------------------------------------------------------------------------
@@ -14,8 +50,8 @@ in
# #
# Hosts declare their displays once, in hosts/<host>/default.nix, via: # Hosts declare their displays once, in hosts/<host>/default.nix, via:
# home-manager.users.<my.username>.hypr.monitors = [ { ... } ]; # home-manager.users.<my.username>.hypr.monitors = [ { ... } ];
# and the setting flows into Hyprland here. The default (empty output + # The list is rendered to ~/.config/hypr/monitors.lua, which the
# preferred mode) works on any machine with any display. # hand-maintained hyprland.lua pulls in with require("monitors").
# ------------------------------------------------------------------------- # -------------------------------------------------------------------------
options.hypr.monitors = lib.mkOption { options.hypr.monitors = lib.mkOption {
description = "Hyprland monitor rules for this host"; description = "Hyprland monitor rules for this host";
@@ -59,563 +95,13 @@ in
}; };
config = { config = {
wayland.windowManager.hyprland.settings.monitor = map ( # Live, hand-maintained config: symlinked straight to the repo so edits
m: # apply on `hyprctl reload` without a rebuild.
{ xdg.configFile."hypr/hyprland.lua".source =
inherit (m) config.lib.file.mkOutOfStoreSymlink "${my.flakeDir}/home/hypr/hyprland.lua";
output
mode
position
scale
;
}
// (lib.optionalAttrs (m.vrr != null) { inherit (m) vrr; })
) config.hypr.monitors;
wayland.windowManager.hyprland = { # Generated fragments required by the live config.
enable = true; xdg.configFile."hypr/theme.lua".text = themeLua;
configType = "lua"; xdg.configFile."hypr/monitors.lua".text = monitorsLua;
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;
}
];
};
};
}; };
} }

View File

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

View File

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