Compare commits
19 Commits
250b0b71f9
...
fix/waybar
| Author | SHA1 | Date | |
|---|---|---|---|
| c053846adb | |||
| 2edad6c5df | |||
| 1d5acc09c6 | |||
| ee29e53f7c | |||
| 3c9d0ec886 | |||
| f0c5544b10 | |||
| 4dd0a5a4f5 | |||
| a4a795f7ee | |||
| ecdf877308 | |||
| d1face10ec | |||
| 337d9a2014 | |||
| f059068873 | |||
| 7662c04cda | |||
| 33128fc3c7 | |||
| 68415ad47e | |||
| 6b7201e6fa | |||
| 4360f4863b | |||
| 370cab8f83 | |||
| e74a08b853 |
@@ -2,10 +2,10 @@
|
|||||||
|
|
||||||
## Desktop
|
## Desktop
|
||||||
|
|
||||||
- TODO: Verify this path after the NixOS configuration is migrated.
|
- When adding a graphical application, register its workspace icon in `registry.window-icon`.
|
||||||
When adding a graphical application, add a `window-rewrite` icon mapping in `modules/desktop/session/waybar.nix`.
|
|
||||||
The workspace indicator uses this mapping to show an application icon instead of the generic default glyph.
|
The workspace indicator uses this mapping to show an application icon instead of the generic default glyph.
|
||||||
Match the mapping on the window class.
|
Match the mapping on the window class.
|
||||||
|
Store the icon as the rendered string.
|
||||||
Run `hyprctl clients -j | jq -r '.[].class' | sort -u` to list classes in the running session.
|
Run `hyprctl clients -j | jq -r '.[].class' | sort -u` to list classes in the running session.
|
||||||
- TODO: Verify this path after the NixOS configuration is migrated.
|
- TODO: Verify this path after the NixOS configuration is migrated.
|
||||||
Set Hyprland session environment variables in `modules/desktop/session/hyprland/default.nix` with `env = KEY,VALUE`.
|
Set Hyprland session environment variables in `modules/desktop/session/hyprland/default.nix` with `env = KEY,VALUE`.
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
{
|
{
|
||||||
inputs,
|
inputs,
|
||||||
|
util,
|
||||||
...
|
...
|
||||||
}:
|
}:
|
||||||
# neogaia — Dell XPS 13 9380 laptop.
|
# neogaia — Dell XPS 13 9380 laptop.
|
||||||
@@ -14,8 +15,7 @@
|
|||||||
|
|
||||||
modules.host.cachyos.enable = true;
|
modules.host.cachyos.enable = true;
|
||||||
|
|
||||||
# So wifi can be joined from the console.
|
modules.network.enable = true;
|
||||||
networking.networkmanager.enable = true;
|
|
||||||
|
|
||||||
registry.address.neogaia.self = "10.23.50.146";
|
registry.address.neogaia.self = "10.23.50.146";
|
||||||
|
|
||||||
@@ -40,4 +40,23 @@
|
|||||||
modules.desktop.apps.obsidian.enable = true;
|
modules.desktop.apps.obsidian.enable = true;
|
||||||
modules.desktop.apps.steam.enable = true;
|
modules.desktop.apps.steam.enable = true;
|
||||||
|
|
||||||
|
registry.waybar.neogaia.battery = {
|
||||||
|
order = 70;
|
||||||
|
settings = {
|
||||||
|
format = "{icon} {capacity}%";
|
||||||
|
format-charging = "${util.glyph "f0e7"} {capacity}%";
|
||||||
|
format-icons = [
|
||||||
|
(util.glyph "f244")
|
||||||
|
(util.glyph "f243")
|
||||||
|
(util.glyph "f242")
|
||||||
|
(util.glyph "f241")
|
||||||
|
(util.glyph "f240")
|
||||||
|
];
|
||||||
|
states = {
|
||||||
|
warning = 20;
|
||||||
|
critical = 10;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,7 +21,7 @@
|
|||||||
enable32Bit = true;
|
enable32Bit = true;
|
||||||
};
|
};
|
||||||
|
|
||||||
networking.networkmanager.enable = true;
|
modules.network.enable = true;
|
||||||
|
|
||||||
services.logind.settings.Login = {
|
services.logind.settings.Login = {
|
||||||
HandlePowerKey = "ignore";
|
HandlePowerKey = "ignore";
|
||||||
@@ -52,14 +52,14 @@
|
|||||||
{
|
{
|
||||||
output = "HDMI-A-1";
|
output = "HDMI-A-1";
|
||||||
mode = "1920x1080@120";
|
mode = "1920x1080@120";
|
||||||
position = "0x0";
|
position = "-1080x0";
|
||||||
scale = 1;
|
scale = 1;
|
||||||
transform = 3;
|
transform = 3;
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
output = "DP-3";
|
output = "DP-3";
|
||||||
mode = "2560x1440@239.96";
|
mode = "2560x1440@239.96";
|
||||||
position = "1080x0";
|
position = "0x0";
|
||||||
scale = 1;
|
scale = 1;
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
inputs,
|
inputs,
|
||||||
lib,
|
lib,
|
||||||
pkgs,
|
pkgs,
|
||||||
|
util,
|
||||||
...
|
...
|
||||||
}:
|
}:
|
||||||
# Configures Firefox as the desktop browser.
|
# Configures Firefox as the desktop browser.
|
||||||
@@ -26,6 +27,11 @@ in
|
|||||||
};
|
};
|
||||||
|
|
||||||
config = lib.mkIf cfg.enable {
|
config = lib.mkIf cfg.enable {
|
||||||
|
registry.window-icon.${config.networking.hostName}.firefox = {
|
||||||
|
class = ".*[Ff]irefox.*";
|
||||||
|
icon = util.glyph "f269";
|
||||||
|
};
|
||||||
|
|
||||||
home-manager.users.${config.user.name} = hm: {
|
home-manager.users.${config.user.name} = hm: {
|
||||||
programs.firefox = {
|
programs.firefox = {
|
||||||
enable = true;
|
enable = true;
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
config,
|
config,
|
||||||
lib,
|
lib,
|
||||||
pkgs,
|
pkgs,
|
||||||
|
util,
|
||||||
...
|
...
|
||||||
}:
|
}:
|
||||||
# Ghostty as the desktop terminal.
|
# Ghostty as the desktop terminal.
|
||||||
@@ -16,10 +17,15 @@ in
|
|||||||
keys = "SUPER + Return";
|
keys = "SUPER + Return";
|
||||||
action = {
|
action = {
|
||||||
kind = "exec";
|
kind = "exec";
|
||||||
command = "${pkgs.ghostty}/bin/ghostty";
|
command = lib.getExe pkgs.ghostty;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
registry.window-icon.${config.networking.hostName}.ghostty = {
|
||||||
|
class = "com.mitchellh.ghostty";
|
||||||
|
icon = util.glyph "f120";
|
||||||
|
};
|
||||||
|
|
||||||
home-manager.users.${user}.home.packages = [ pkgs.ghostty ];
|
home-manager.users.${user}.home.packages = [ pkgs.ghostty ];
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
config,
|
config,
|
||||||
lib,
|
lib,
|
||||||
pkgs,
|
pkgs,
|
||||||
|
util,
|
||||||
...
|
...
|
||||||
}:
|
}:
|
||||||
# Obsidian as the desktop note-taking vault.
|
# Obsidian as the desktop note-taking vault.
|
||||||
@@ -14,5 +15,10 @@ in
|
|||||||
|
|
||||||
config = lib.mkIf config.modules.desktop.apps.obsidian.enable {
|
config = lib.mkIf config.modules.desktop.apps.obsidian.enable {
|
||||||
home-manager.users.${user}.home.packages = [ pkgs.obsidian ];
|
home-manager.users.${user}.home.packages = [ pkgs.obsidian ];
|
||||||
|
|
||||||
|
registry.window-icon.${config.networking.hostName}.obsidian = {
|
||||||
|
class = "obsidian";
|
||||||
|
icon = util.glyph "f02d";
|
||||||
|
};
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
config,
|
config,
|
||||||
lib,
|
lib,
|
||||||
pkgs,
|
pkgs,
|
||||||
|
util,
|
||||||
...
|
...
|
||||||
}:
|
}:
|
||||||
# Signal desktop messenger.
|
# Signal desktop messenger.
|
||||||
@@ -13,5 +14,10 @@ in
|
|||||||
|
|
||||||
config = lib.mkIf config.modules.desktop.apps.signal.enable {
|
config = lib.mkIf config.modules.desktop.apps.signal.enable {
|
||||||
home-manager.users.${user}.home.packages = [ pkgs.signal-desktop ];
|
home-manager.users.${user}.home.packages = [ pkgs.signal-desktop ];
|
||||||
|
|
||||||
|
registry.window-icon.${config.networking.hostName}.signal = {
|
||||||
|
class = "[Ss]ignal";
|
||||||
|
icon = util.glyph "f4ad";
|
||||||
|
};
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
config,
|
config,
|
||||||
lib,
|
lib,
|
||||||
pkgs,
|
pkgs,
|
||||||
|
util,
|
||||||
...
|
...
|
||||||
}:
|
}:
|
||||||
# Steam game launcher and runtime integration.
|
# Steam game launcher and runtime integration.
|
||||||
@@ -23,5 +24,10 @@
|
|||||||
environment.systemPackages = [
|
environment.systemPackages = [
|
||||||
pkgs.gamescope
|
pkgs.gamescope
|
||||||
];
|
];
|
||||||
|
|
||||||
|
registry.window-icon.${config.networking.hostName}.steam = {
|
||||||
|
class = "[Ss]team";
|
||||||
|
icon = util.glyph "f1b6";
|
||||||
|
};
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
config,
|
config,
|
||||||
lib,
|
lib,
|
||||||
pkgs,
|
pkgs,
|
||||||
|
util,
|
||||||
...
|
...
|
||||||
}:
|
}:
|
||||||
# Vesktop, a Discord client with Vencord built in.
|
# Vesktop, a Discord client with Vencord built in.
|
||||||
@@ -14,5 +15,10 @@ in
|
|||||||
|
|
||||||
config = lib.mkIf config.modules.desktop.apps.vencord.enable {
|
config = lib.mkIf config.modules.desktop.apps.vencord.enable {
|
||||||
home-manager.users.${user}.home.packages = [ pkgs.vesktop ];
|
home-manager.users.${user}.home.packages = [ pkgs.vesktop ];
|
||||||
|
|
||||||
|
registry.window-icon.${config.networking.hostName}.vesktop = {
|
||||||
|
class = "[Vv]esktop";
|
||||||
|
icon = util.glyph "f1ff";
|
||||||
|
};
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,5 +23,6 @@
|
|||||||
modules.desktop.utilities.screenshot.enable = lib.mkDefault true;
|
modules.desktop.utilities.screenshot.enable = lib.mkDefault true;
|
||||||
modules.desktop.utilities.theming.enable = lib.mkDefault true;
|
modules.desktop.utilities.theming.enable = lib.mkDefault true;
|
||||||
modules.desktop.utilities.userdirs.enable = lib.mkDefault true;
|
modules.desktop.utilities.userdirs.enable = lib.mkDefault true;
|
||||||
|
registry.env.${config.networking.hostName}.NIXOS_OZONE_WL = "1";
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
{
|
{
|
||||||
config,
|
config,
|
||||||
lib,
|
lib,
|
||||||
|
util,
|
||||||
...
|
...
|
||||||
}:
|
}:
|
||||||
# The Hyprland compositor.
|
# The Hyprland compositor.
|
||||||
@@ -63,6 +64,13 @@
|
|||||||
dwindle.preserve_split = true;
|
dwindle.preserve_split = true;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
env = lib.mapAttrsToList (name: value: {
|
||||||
|
_args = [
|
||||||
|
name
|
||||||
|
value
|
||||||
|
];
|
||||||
|
}) (lib.attrByPath [ config.networking.hostName ] { } config.registry.env);
|
||||||
|
|
||||||
bind = lib.mapAttrsToList (_name: keybind: {
|
bind = lib.mapAttrsToList (_name: keybind: {
|
||||||
_args = [
|
_args = [
|
||||||
keybind.keys
|
keybind.keys
|
||||||
@@ -111,6 +119,9 @@
|
|||||||
]
|
]
|
||||||
++ lib.optional keybind.repeating {
|
++ lib.optional keybind.repeating {
|
||||||
repeating = true;
|
repeating = true;
|
||||||
|
}
|
||||||
|
++ lib.optional keybind.locked {
|
||||||
|
locked = true;
|
||||||
};
|
};
|
||||||
}) (lib.attrByPath [ config.networking.hostName ] { } config.registry.keybind);
|
}) (lib.attrByPath [ config.networking.hostName ] { } config.registry.keybind);
|
||||||
};
|
};
|
||||||
@@ -217,5 +228,18 @@
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
|
registry.waybar.${config.networking.hostName}."hyprland/workspaces" = {
|
||||||
|
position = "left";
|
||||||
|
order = 10;
|
||||||
|
settings = {
|
||||||
|
format = "{id} {windows}";
|
||||||
|
format-window-separator = " ";
|
||||||
|
window-rewrite-default = util.glyph "f2d0";
|
||||||
|
window-rewrite = lib.mapAttrs' (
|
||||||
|
_name: windowIcon: lib.nameValuePair "class<${windowIcon.class}>" windowIcon.icon
|
||||||
|
) (lib.attrByPath [ config.networking.hostName ] { } config.registry.window-icon);
|
||||||
|
};
|
||||||
|
};
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,50 +5,32 @@
|
|||||||
...
|
...
|
||||||
}:
|
}:
|
||||||
# hypridle: idle-triggered locking and display power management.
|
# hypridle: idle-triggered locking and display power management.
|
||||||
let
|
|
||||||
cfg = config.modules.desktop.session.hyprland.hypridle; # NOLINT(let_order)
|
|
||||||
|
|
||||||
# The guard drops the call when a locker is already up, so no idle trigger
|
|
||||||
# stacks a second hyprlock over the first.
|
|
||||||
lockCmd = "${pkgs.procps}/bin/pidof hyprlock || ${hyprlock}";
|
|
||||||
user = config.user.name;
|
|
||||||
|
|
||||||
hyprctl = "${config.programs.hyprland.package}/bin/hyprctl";
|
|
||||||
hyprlock = "${config.home-manager.users.${user}.programs.hyprlock.package}/bin/hyprlock";
|
|
||||||
|
|
||||||
in
|
|
||||||
{
|
{
|
||||||
options.modules.desktop.session.hyprland.hypridle = {
|
options.modules.desktop.session.hyprland.hypridle.enable =
|
||||||
enable = lib.mkEnableOption "hypridle idle management";
|
lib.mkEnableOption "hypridle idle management";
|
||||||
|
|
||||||
lockTimeout = lib.mkOption {
|
config = lib.mkIf config.modules.desktop.session.hyprland.hypridle.enable {
|
||||||
type = lib.types.ints.positive;
|
|
||||||
default = 300;
|
|
||||||
description = "Seconds of inactivity before the screen locks.";
|
|
||||||
};
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
config = lib.mkIf cfg.enable {
|
|
||||||
# Closing the lid suspends, and every suspend locks first through the
|
# Closing the lid suspends, and every suspend locks first through the
|
||||||
# before_sleep_cmd below, so the lid always lands at a locked screen.
|
# before_sleep_cmd below, so the lid always lands at a locked screen.
|
||||||
services.logind.settings.Login.HandleLidSwitch = "suspend";
|
services.logind.settings.Login.HandleLidSwitch = "suspend";
|
||||||
|
|
||||||
home-manager.users.${user}.services.hypridle = {
|
home-manager.users.${config.user.name}.services.hypridle = {
|
||||||
enable = true;
|
enable = true;
|
||||||
|
|
||||||
settings = {
|
settings = {
|
||||||
general = {
|
general = {
|
||||||
lock_cmd = lockCmd;
|
# The guard drops the call when a locker is already up, so no idle
|
||||||
|
# trigger stacks a second hyprlock over the first.
|
||||||
|
lock_cmd = "${lib.getExe' pkgs.procps "pidof"} hyprlock || ${lib.getExe pkgs.hyprlock}";
|
||||||
before_sleep_cmd = "loginctl lock-session";
|
before_sleep_cmd = "loginctl lock-session";
|
||||||
# Waking pokes the compositor in case firmware leaves displays dark.
|
# Waking pokes the compositor in case firmware leaves displays dark.
|
||||||
after_sleep_cmd = "${hyprctl} dispatch dpms on";
|
after_sleep_cmd = "${lib.getExe' config.programs.hyprland.package "hyprctl"} dispatch 'hl.dsp.dpms({ action = \"enable\" })'";
|
||||||
};
|
};
|
||||||
|
|
||||||
listener = [
|
listener = [
|
||||||
# Lock on idle.
|
# Lock on idle.
|
||||||
{
|
{
|
||||||
timeout = cfg.lockTimeout;
|
timeout = 300;
|
||||||
on-timeout = "loginctl lock-session";
|
on-timeout = "loginctl lock-session";
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|||||||
@@ -2,44 +2,54 @@
|
|||||||
config,
|
config,
|
||||||
lib,
|
lib,
|
||||||
pkgs,
|
pkgs,
|
||||||
|
util,
|
||||||
...
|
...
|
||||||
}:
|
}:
|
||||||
# The lock screen: a hyprlock session-lock surface the compositor owns.
|
# The lock screen: a hyprlock session-lock surface the compositor owns.
|
||||||
let
|
let
|
||||||
user = config.user.name;
|
sleepIcon = util.glyph [
|
||||||
|
"db81"
|
||||||
# The hyprlock this module installs, used by the lock keybind below.
|
"dcb2"
|
||||||
hyprlock = "${config.home-manager.users.${user}.programs.hyprlock.package}/bin/hyprlock";
|
];
|
||||||
|
sleepOffIcon = util.glyph [
|
||||||
sleepIcon = builtins.fromJSON ''"\udb81\udcb2"'';
|
"db81"
|
||||||
sleepOffIcon = builtins.fromJSON ''"\udb81\udcb3"'';
|
"dcb3"
|
||||||
|
];
|
||||||
|
|
||||||
idleLockToggle = pkgs.writeShellScript "lockscreen-idle-inhibit-toggle" ''
|
idleLockToggle = pkgs.writeShellScript "lockscreen-idle-inhibit-toggle" ''
|
||||||
unit=lockscreen-idle-inhibit.service
|
unit=lockscreen-idle-inhibit.service
|
||||||
|
|
||||||
refresh_waybar() {
|
refresh_waybar() {
|
||||||
${pkgs.procps}/bin/pkill -RTMIN+9 waybar >/dev/null 2>&1 || true
|
${lib.getExe' pkgs.procps "pkill"} -RTMIN+9 waybar >/dev/null 2>&1 || true
|
||||||
}
|
}
|
||||||
|
|
||||||
if ${pkgs.systemd}/bin/systemctl --user is-active --quiet "$unit"; then
|
if ${lib.getExe' pkgs.systemd "systemctl"} --user is-active --quiet "$unit"; then
|
||||||
${pkgs.systemd}/bin/systemctl --user stop "$unit"
|
${lib.getExe' pkgs.systemd "systemctl"} --user stop "$unit"
|
||||||
refresh_waybar
|
refresh_waybar
|
||||||
${pkgs.swayosd}/bin/swayosd-client --custom-message "${sleepIcon}"
|
${lib.getExe' pkgs.swayosd "swayosd-client"} --custom-message "${sleepIcon}"
|
||||||
exit 0
|
exit 0
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if ${pkgs.systemd}/bin/systemd-run \
|
if ${lib.getExe' pkgs.systemd "systemd-run"} \
|
||||||
--user \
|
--user \
|
||||||
--collect \
|
--collect \
|
||||||
--unit=lockscreen-idle-inhibit \
|
--unit=lockscreen-idle-inhibit \
|
||||||
--description="Disable idle-triggered screen locking" \
|
--description="Disable idle-triggered screen locking" \
|
||||||
${pkgs.systemd}/bin/systemd-inhibit \
|
${lib.getExe' pkgs.systemd "systemd-inhibit"} \
|
||||||
--what=idle \
|
--what=idle \
|
||||||
--who=lockscreen-hotkey \
|
--who=lockscreen-hotkey \
|
||||||
--why="lock screen disabled by hotkey" \
|
--why="lock screen disabled by hotkey" \
|
||||||
${pkgs.coreutils}/bin/sleep infinity >/dev/null; then
|
${lib.getExe' pkgs.coreutils "sleep"} infinity >/dev/null; then
|
||||||
refresh_waybar
|
refresh_waybar
|
||||||
${pkgs.swayosd}/bin/swayosd-client --custom-message "${sleepOffIcon}"
|
${lib.getExe' pkgs.swayosd "swayosd-client"} --custom-message "${sleepOffIcon}"
|
||||||
|
fi
|
||||||
|
'';
|
||||||
|
|
||||||
|
idleLockStatus = pkgs.writeShellScript "waybar-idle-lock-status" ''
|
||||||
|
if ${lib.getExe' pkgs.systemd "systemctl"} --user is-active --quiet lockscreen-idle-inhibit.service; then
|
||||||
|
printf '{"text":"${sleepOffIcon}","tooltip":"Idle locking inhibited","class":"inhibited"}\n'
|
||||||
|
else
|
||||||
|
printf '{"text":"","class":"idle"}\n'
|
||||||
fi
|
fi
|
||||||
'';
|
'';
|
||||||
in
|
in
|
||||||
@@ -48,7 +58,7 @@ in
|
|||||||
lib.mkEnableOption "the hyprlock lock screen";
|
lib.mkEnableOption "the hyprlock lock screen";
|
||||||
|
|
||||||
config = lib.mkIf config.modules.desktop.session.hyprland.hyprlock.enable {
|
config = lib.mkIf config.modules.desktop.session.hyprland.hyprlock.enable {
|
||||||
home-manager.users.${user} = {
|
home-manager.users.${config.user.name} = {
|
||||||
# Colors and the lock-screen background come from Stylix's hyprlock target,
|
# Colors and the lock-screen background come from Stylix's hyprlock target,
|
||||||
# so only geometry and behaviour are set here.
|
# so only geometry and behaviour are set here.
|
||||||
programs.hyprlock = {
|
programs.hyprlock = {
|
||||||
@@ -81,16 +91,36 @@ in
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
};
|
||||||
|
|
||||||
# Lock on Super+X.
|
registry.keybind.${config.networking.hostName} = {
|
||||||
# The guard drops the keypress when a locker is already up, so a second
|
hyprlock-lock = {
|
||||||
# hyprlock never stacks over the first.
|
keys = "SUPER + X";
|
||||||
/*
|
action = {
|
||||||
wayland.windowManager.hyprland.settings.bind = [
|
kind = "exec";
|
||||||
"$mod, X, exec, ${pkgs.procps}/bin/pidof hyprlock || ${hyprlock}"
|
# The guard drops the call when a locker is already up, so no manual
|
||||||
"$mod CTRL, X, exec, ${idleLockToggle}"
|
# trigger stacks a second hyprlock over the first.
|
||||||
];
|
command = "${lib.getExe' pkgs.procps "pidof"} hyprlock || ${lib.getExe pkgs.hyprlock}";
|
||||||
*/
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
hyprlock-toggle-idle-inhibit = {
|
||||||
|
keys = "SUPER + CTRL + X";
|
||||||
|
action = {
|
||||||
|
kind = "exec";
|
||||||
|
command = "${idleLockToggle}";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
registry.waybar.${config.networking.hostName}."custom/idle-lock" = {
|
||||||
|
order = 20;
|
||||||
|
settings = {
|
||||||
|
return-type = "json";
|
||||||
|
exec = "${idleLockStatus}";
|
||||||
|
interval = "once";
|
||||||
|
signal = 9;
|
||||||
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,9 +13,13 @@
|
|||||||
config = lib.mkIf config.modules.desktop.session.login.enable {
|
config = lib.mkIf config.modules.desktop.session.login.enable {
|
||||||
services.greetd = {
|
services.greetd = {
|
||||||
enable = true;
|
enable = true;
|
||||||
settings.default_session.command =
|
settings.default_session.command = lib.escapeShellArgs [
|
||||||
"${lib.getExe pkgs.tuigreet} --time --remember "
|
(lib.getExe pkgs.tuigreet)
|
||||||
+ "--cmd 'uwsm start -e -D Hyprland hyprland.desktop'";
|
"--time"
|
||||||
|
"--remember"
|
||||||
|
"--cmd"
|
||||||
|
"${lib.getExe config.programs.uwsm.package} start -e -D Hyprland hyprland.desktop"
|
||||||
|
];
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,19 +2,29 @@
|
|||||||
config,
|
config,
|
||||||
lib,
|
lib,
|
||||||
pkgs,
|
pkgs,
|
||||||
|
util,
|
||||||
...
|
...
|
||||||
}:
|
}:
|
||||||
# mako as the notification daemon: toasts, a do-not-disturb mode, and history recall.
|
# mako as the notification daemon: toasts, a do-not-disturb mode, and history recall.
|
||||||
let
|
let
|
||||||
user = config.user.name;
|
dndStatus = pkgs.writeShellScript "waybar-dnd-status" ''
|
||||||
|
if ${lib.getExe' pkgs.mako "makoctl"} mode 2>/dev/null | ${lib.getExe pkgs.gnugrep} -qx dnd; then
|
||||||
|
printf '{"text":"${util.glyph "f1f6"}","tooltip":"Do not disturb: on","class":"dnd"}\n'
|
||||||
|
else
|
||||||
|
printf '{"text":"${util.glyph "f0f3"}","tooltip":"Notifications on","class":"active"}\n'
|
||||||
|
fi
|
||||||
|
'';
|
||||||
|
|
||||||
makoctl = "${pkgs.mako}/bin/makoctl";
|
dndToggle = pkgs.writeShellScript "waybar-dnd-toggle" ''
|
||||||
|
${lib.getExe' pkgs.mako "makoctl"} mode -t dnd >/dev/null 2>&1
|
||||||
|
${lib.getExe' pkgs.procps "pkill"} -RTMIN+8 waybar
|
||||||
|
'';
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
options.modules.desktop.session.mako.enable = lib.mkEnableOption "the mako notification daemon";
|
options.modules.desktop.session.mako.enable = lib.mkEnableOption "the mako notification daemon";
|
||||||
|
|
||||||
config = lib.mkIf config.modules.desktop.session.mako.enable {
|
config = lib.mkIf config.modules.desktop.session.mako.enable {
|
||||||
home-manager.users.${user} = {
|
home-manager.users.${config.user.name} = {
|
||||||
# Colors and the popup font come from Stylix's mako target, so only
|
# Colors and the popup font come from Stylix's mako target, so only
|
||||||
# behaviour is set here.
|
# behaviour is set here.
|
||||||
services.mako = {
|
services.mako = {
|
||||||
@@ -31,15 +41,25 @@ in
|
|||||||
"mode=dnd".invisible = true;
|
"mode=dnd".invisible = true;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
};
|
||||||
|
|
||||||
# Recall the last notification from history, keyboard-only like the rest
|
registry.keybind.${config.networking.hostName}.mako-history = {
|
||||||
# of the session.
|
keys = "SUPER + N";
|
||||||
# $mod is defined by the compositor config these binds share.
|
action = {
|
||||||
/*
|
kind = "exec";
|
||||||
wayland.windowManager.hyprland.settings.bind = [
|
command = "${lib.getExe' pkgs.mako "makoctl"} restore";
|
||||||
"$mod, N, exec, ${makoctl} restore"
|
};
|
||||||
];
|
};
|
||||||
*/
|
|
||||||
|
registry.waybar.${config.networking.hostName}."custom/dnd" = {
|
||||||
|
order = 80;
|
||||||
|
settings = {
|
||||||
|
return-type = "json";
|
||||||
|
exec = "${dndStatus}";
|
||||||
|
on-click = "${dndToggle}";
|
||||||
|
interval = "once";
|
||||||
|
signal = 8;
|
||||||
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,19 +6,17 @@
|
|||||||
}:
|
}:
|
||||||
# rofi as the desktop launcher and dmenu-style menu frontend.
|
# rofi as the desktop launcher and dmenu-style menu frontend.
|
||||||
let
|
let
|
||||||
user = config.user.name;
|
|
||||||
|
|
||||||
# A session menu built on the same themed rofi, so the power actions look
|
# A session menu built on the same themed rofi, so the power actions look
|
||||||
# like every other menu the launcher drives.
|
# like every other menu the launcher drives.
|
||||||
powerMenu = pkgs.writeShellScript "rofi-power-menu" ''
|
powerMenu = pkgs.writeShellScript "rofi-power-menu" ''
|
||||||
chosen=$(printf '%s\n' Lock Suspend 'Log out' Reboot 'Shut down' \
|
chosen=$(printf '%s\n' Lock Suspend 'Log out' Reboot 'Shut down' \
|
||||||
| ${pkgs.rofi}/bin/rofi -dmenu -i -p Power)
|
| ${lib.getExe pkgs.rofi} -dmenu -i -p Power)
|
||||||
case "$chosen" in
|
case "$chosen" in
|
||||||
Lock) ${pkgs.systemd}/bin/loginctl lock-session ;;
|
Lock) ${lib.getExe' pkgs.systemd "loginctl"} lock-session ;;
|
||||||
Suspend) ${pkgs.systemd}/bin/systemctl suspend ;;
|
Suspend) ${lib.getExe' pkgs.systemd "systemctl"} suspend ;;
|
||||||
'Log out') ${pkgs.systemd}/bin/loginctl terminate-session "$XDG_SESSION_ID" ;;
|
'Log out') ${lib.getExe' pkgs.systemd "loginctl"} terminate-session "$XDG_SESSION_ID" ;;
|
||||||
Reboot) ${pkgs.systemd}/bin/systemctl reboot ;;
|
Reboot) ${lib.getExe' pkgs.systemd "systemctl"} reboot ;;
|
||||||
'Shut down') ${pkgs.systemd}/bin/systemctl poweroff ;;
|
'Shut down') ${lib.getExe' pkgs.systemd "systemctl"} poweroff ;;
|
||||||
esac
|
esac
|
||||||
'';
|
'';
|
||||||
in
|
in
|
||||||
@@ -26,10 +24,11 @@ in
|
|||||||
options.modules.desktop.session.rofi.enable = lib.mkEnableOption "the rofi launcher";
|
options.modules.desktop.session.rofi.enable = lib.mkEnableOption "the rofi launcher";
|
||||||
|
|
||||||
config = lib.mkIf config.modules.desktop.session.rofi.enable {
|
config = lib.mkIf config.modules.desktop.session.rofi.enable {
|
||||||
home-manager.users.${user} = {
|
home-manager.users.${config.user.name} = hm: {
|
||||||
# No theme is set here: Stylix's rofi target supplies the Nord one.
|
# Stylix supplies the Nord theme.
|
||||||
programs.rofi = {
|
programs.rofi = {
|
||||||
enable = true;
|
enable = true;
|
||||||
|
font = lib.mkForce "JetBrains Mono 12";
|
||||||
|
|
||||||
# Application launch is the only mode, kept minimal so the prompt opens
|
# Application launch is the only mode, kept minimal so the prompt opens
|
||||||
# as fast as rofi can read the desktop entries.
|
# as fast as rofi can read the desktop entries.
|
||||||
@@ -38,16 +37,31 @@ in
|
|||||||
modi = "drun";
|
modi = "drun";
|
||||||
show-icons = false;
|
show-icons = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
theme.window = {
|
||||||
|
border = hm.config.lib.formats.rasi.mkLiteral "2px";
|
||||||
|
border-color = hm.config.lib.formats.rasi.mkLiteral "#FFFFFF";
|
||||||
|
border-radius = hm.config.lib.formats.rasi.mkLiteral "8px";
|
||||||
|
};
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
# The launcher and the session menu, kept next to the tool they drive.
|
registry.keybind.${config.networking.hostName} = {
|
||||||
# $mod is defined by the compositor config these binds share.
|
rofi-launcher = {
|
||||||
/*
|
keys = "SUPER + R";
|
||||||
wayland.windowManager.hyprland.settings.bind = [
|
action = {
|
||||||
"$mod, R, exec, ${pkgs.rofi}/bin/rofi -show drun"
|
kind = "exec";
|
||||||
"$mod SHIFT, X, exec, ${powerMenu}"
|
command = "${lib.getExe pkgs.rofi} -show drun";
|
||||||
];
|
};
|
||||||
*/
|
};
|
||||||
|
|
||||||
|
rofi-power-menu = {
|
||||||
|
keys = "SUPER + SHIFT + X";
|
||||||
|
action = {
|
||||||
|
kind = "exec";
|
||||||
|
command = "${powerMenu}";
|
||||||
|
};
|
||||||
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,97 +6,19 @@
|
|||||||
}:
|
}:
|
||||||
# Waybar: a top status bar reading system state at a glance.
|
# Waybar: a top status bar reading system state at a glance.
|
||||||
let
|
let
|
||||||
vpnClient = config.modules.network.vpn-client;
|
moduleNames =
|
||||||
|
position:
|
||||||
# A Nerd Font Private-Use-Area codepoint as a literal character, decoded
|
map (entry: entry.name) (
|
||||||
# through JSON so the glyph survives as bytes rather than an editor paste.
|
lib.sort (left: right: left.module.order < right.module.order) (
|
||||||
g = code: builtins.fromJSON ''"\u${code}"'';
|
lib.filter (entry: entry.module.position == position) moduleEntries
|
||||||
user = config.user.name;
|
|
||||||
|
|
||||||
sleepOffIcon = builtins.fromJSON ''"\udb81\udcb3"'';
|
|
||||||
|
|
||||||
# Renders and toggles mako's do-not-disturb mode.
|
|
||||||
# With no mako daemon answering, the status reads "notifications on", keeping
|
|
||||||
# the widget inert rather than broken.
|
|
||||||
dndStatus = pkgs.writeShellScript "waybar-dnd-status" ''
|
|
||||||
if ${pkgs.mako}/bin/makoctl mode 2>/dev/null | ${pkgs.gnugrep}/bin/grep -qx dnd; then
|
|
||||||
printf '{"text":"${g "f1f6"}","tooltip":"Do not disturb: on","class":"dnd"}\n'
|
|
||||||
else
|
|
||||||
printf '{"text":"${g "f0f3"}","tooltip":"Notifications on","class":"active"}\n'
|
|
||||||
fi
|
|
||||||
'';
|
|
||||||
|
|
||||||
dndToggle = pkgs.writeShellScript "waybar-dnd-toggle" ''
|
|
||||||
${pkgs.mako}/bin/makoctl mode -t dnd >/dev/null 2>&1
|
|
||||||
${pkgs.procps}/bin/pkill -RTMIN+8 waybar
|
|
||||||
'';
|
|
||||||
|
|
||||||
# Lit while a wf-recorder capture is running.
|
|
||||||
# Empty text collapses the widget when idle, so it shows nothing until then.
|
|
||||||
recStatus = pkgs.writeShellScript "waybar-recording-status" ''
|
|
||||||
if ${pkgs.procps}/bin/pgrep -x wf-recorder >/dev/null; then
|
|
||||||
printf '{"text":"${g "f03d"}","tooltip":"Recording","class":"recording"}\n'
|
|
||||||
else
|
|
||||||
printf '{"text":"","class":"idle"}\n'
|
|
||||||
fi
|
|
||||||
'';
|
|
||||||
|
|
||||||
idleLockStatus = pkgs.writeShellScript "waybar-idle-lock-status" ''
|
|
||||||
if ${pkgs.systemd}/bin/systemctl --user is-active --quiet lockscreen-idle-inhibit.service; then
|
|
||||||
printf '{"text":"${sleepOffIcon}","tooltip":"Idle locking inhibited","class":"inhibited"}\n'
|
|
||||||
else
|
|
||||||
printf '{"text":"","class":"idle"}\n'
|
|
||||||
fi
|
|
||||||
'';
|
|
||||||
|
|
||||||
vpnStatus = pkgs.writeShellScript "waybar-vpn-status" ''
|
|
||||||
interface=${lib.escapeShellArg vpnClient.interface}
|
|
||||||
|
|
||||||
if ! ${pkgs.iproute2}/bin/ip link show dev "$interface" >/dev/null 2>&1; then
|
|
||||||
printf '{"text":"","class":"idle"}\n'
|
|
||||||
exit 0
|
|
||||||
fi
|
|
||||||
|
|
||||||
ipaddr=$(${pkgs.iproute2}/bin/ip -4 -o addr show dev "$interface" | ${pkgs.gawk}/bin/awk '{ print $4; exit }')
|
|
||||||
|
|
||||||
${pkgs.jq}/bin/jq -cn \
|
|
||||||
--arg text "" \
|
|
||||||
--arg tooltip "VPN $interface: ''${ipaddr:-no IPv4}" \
|
|
||||||
--arg class "active" \
|
|
||||||
'{ text: $text, tooltip: $tooltip, class: $class }'
|
|
||||||
'';
|
|
||||||
|
|
||||||
networkStatus = pkgs.writeShellScript "waybar-network-status" ''
|
|
||||||
active=$(
|
|
||||||
${pkgs.networkmanager}/bin/nmcli -t -f DEVICE,TYPE,STATE,CONNECTION device status \
|
|
||||||
| ${pkgs.gawk}/bin/awk -F: '$3 == "connected" && $2 != "loopback" { print; exit }'
|
|
||||||
)
|
)
|
||||||
|
);
|
||||||
|
|
||||||
if [ -z "$active" ]; then
|
moduleEntries = lib.mapAttrsToList (name: module: {
|
||||||
${pkgs.jq}/bin/jq -cn \
|
inherit name module;
|
||||||
--arg text "${g "f127"} off" \
|
}) modules;
|
||||||
--arg tooltip "Network disconnected" \
|
|
||||||
--arg class "disconnected" \
|
|
||||||
'{ text: $text, tooltip: $tooltip, class: $class }'
|
|
||||||
exit 0
|
|
||||||
fi
|
|
||||||
|
|
||||||
IFS=: read -r device type _state connection <<< "$active"
|
modules = lib.attrByPath [ config.networking.hostName ] { } config.registry.waybar;
|
||||||
|
|
||||||
case "$type" in
|
|
||||||
wifi) icon="${g "f1eb"}" ;;
|
|
||||||
ethernet) icon="LAN" ;;
|
|
||||||
*) icon="NET" ;;
|
|
||||||
esac
|
|
||||||
|
|
||||||
ipaddr=$(${pkgs.iproute2}/bin/ip -4 -o addr show dev "$device" | ${pkgs.gawk}/bin/awk '{ print $4; exit }')
|
|
||||||
|
|
||||||
${pkgs.jq}/bin/jq -cn \
|
|
||||||
--arg text "$icon $connection" \
|
|
||||||
--arg tooltip "$device: ''${ipaddr:-no IPv4}" \
|
|
||||||
--arg class "$type" \
|
|
||||||
'{ text: $text, tooltip: $tooltip, class: $class }'
|
|
||||||
'';
|
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
options.modules.desktop.session.waybar.enable = lib.mkEnableOption "the Waybar status bar";
|
options.modules.desktop.session.waybar.enable = lib.mkEnableOption "the Waybar status bar";
|
||||||
@@ -106,64 +28,34 @@ in
|
|||||||
# codepoints Stylix's monospace font does not carry.
|
# codepoints Stylix's monospace font does not carry.
|
||||||
fonts.packages = [ pkgs.nerd-fonts.symbols-only ];
|
fonts.packages = [ pkgs.nerd-fonts.symbols-only ];
|
||||||
|
|
||||||
home-manager.users.${user}.programs.waybar = {
|
home-manager.users.${config.user.name}.programs.waybar = {
|
||||||
enable = true;
|
enable = true;
|
||||||
|
|
||||||
# A user service bound to graphical-session.target, which uwsm activates,
|
# A user service bound to graphical-session.target, which uwsm activates,
|
||||||
# so the bar comes up with the session and no compositor exec-once.
|
# so the bar comes up with the session and no compositor exec-once.
|
||||||
systemd.enable = true;
|
systemd.enable = true;
|
||||||
|
|
||||||
style = ''
|
settings.mainBar = lib.mkMerge [
|
||||||
.modules-right {
|
{
|
||||||
margin-right: 8px;
|
|
||||||
}
|
|
||||||
|
|
||||||
#custom-dnd {
|
|
||||||
margin-right: 8px;
|
|
||||||
}
|
|
||||||
'';
|
|
||||||
|
|
||||||
settings.mainBar = {
|
|
||||||
layer = "top";
|
layer = "top";
|
||||||
position = "top";
|
position = "top";
|
||||||
height = 30;
|
height = 30;
|
||||||
spacing = 8;
|
spacing = 8;
|
||||||
|
margin-right = 8;
|
||||||
|
|
||||||
modules-left = [ "hyprland/workspaces" ];
|
modules-left = moduleNames "left";
|
||||||
modules-center = [ "clock" ];
|
modules-center = moduleNames "center";
|
||||||
modules-right = [
|
modules-right = moduleNames "right";
|
||||||
"custom/recording"
|
}
|
||||||
"custom/idle-lock"
|
(lib.mapAttrs (_name: module: module.settings) modules)
|
||||||
"mpris"
|
|
||||||
"wireplumber"
|
|
||||||
]
|
|
||||||
++ lib.optional vpnClient.enable "custom/vpn"
|
|
||||||
++ [
|
|
||||||
"custom/network"
|
|
||||||
"battery"
|
|
||||||
"custom/dnd"
|
|
||||||
];
|
];
|
||||||
|
|
||||||
# Per-application icons for the windows on each workspace, mapped from
|
|
||||||
# window class with a neutral glyph for anything unlisted.
|
|
||||||
"hyprland/workspaces" = {
|
|
||||||
on-click = "activate";
|
|
||||||
format = "{id} {windows}";
|
|
||||||
format-window-separator = " ";
|
|
||||||
window-rewrite-default = g "f2d0";
|
|
||||||
window-rewrite = {
|
|
||||||
"class<com.mitchellh.ghostty>" = g "f120";
|
|
||||||
"class<.*[Ff]irefox.*>" = g "f269";
|
|
||||||
"class<chromium.*>" = g "f268";
|
|
||||||
"class<[Cc]ode>" = g "f121";
|
|
||||||
"class<obsidian>" = g "f02d";
|
|
||||||
"class<[Ss]ignal>" = g "f4ad";
|
|
||||||
"class<[Ss]team>" = g "f1b6";
|
|
||||||
"class<[Vv]esktop>" = g "f1ff";
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
registry.waybar.${config.networking.hostName} = {
|
||||||
clock = {
|
clock = {
|
||||||
|
position = "center";
|
||||||
|
order = 10;
|
||||||
|
settings = {
|
||||||
format = "{:%a %d %b %H:%M}";
|
format = "{:%a %d %b %H:%M}";
|
||||||
tooltip-format = "<tt>{calendar}</tt>";
|
tooltip-format = "<tt>{calendar}</tt>";
|
||||||
calendar = {
|
calendar = {
|
||||||
@@ -172,77 +64,6 @@ in
|
|||||||
format.today = "<b><u>{}</u></b>";
|
format.today = "<b><u>{}</u></b>";
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
mpris = {
|
|
||||||
format = "${g "f001"} {title}";
|
|
||||||
format-paused = "${g "f04c"} {title}";
|
|
||||||
max-length = 40;
|
|
||||||
on-click = "play-pause";
|
|
||||||
on-scroll-up = "next";
|
|
||||||
on-scroll-down = "previous";
|
|
||||||
};
|
|
||||||
|
|
||||||
wireplumber = {
|
|
||||||
format = "{icon} {volume}%";
|
|
||||||
format-muted = "${g "f026"} muted";
|
|
||||||
format-icons = [
|
|
||||||
(g "f026")
|
|
||||||
(g "f027")
|
|
||||||
(g "f028")
|
|
||||||
];
|
|
||||||
on-click = "${pkgs.wireplumber}/bin/wpctl set-mute @DEFAULT_AUDIO_SINK@ toggle";
|
|
||||||
};
|
|
||||||
|
|
||||||
"custom/vpn" = {
|
|
||||||
return-type = "json";
|
|
||||||
exec = "${vpnStatus}";
|
|
||||||
interval = 5;
|
|
||||||
};
|
|
||||||
|
|
||||||
"custom/network" = {
|
|
||||||
return-type = "json";
|
|
||||||
exec = "${networkStatus}";
|
|
||||||
interval = 5;
|
|
||||||
};
|
|
||||||
|
|
||||||
battery = {
|
|
||||||
format = "{icon} {capacity}%";
|
|
||||||
format-charging = "${g "f0e7"} {capacity}%";
|
|
||||||
format-icons = [
|
|
||||||
(g "f244")
|
|
||||||
(g "f243")
|
|
||||||
(g "f242")
|
|
||||||
(g "f241")
|
|
||||||
(g "f240")
|
|
||||||
];
|
|
||||||
states = {
|
|
||||||
warning = 20;
|
|
||||||
critical = 10;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
"custom/dnd" = {
|
|
||||||
return-type = "json";
|
|
||||||
exec = "${dndStatus}";
|
|
||||||
on-click = "${dndToggle}";
|
|
||||||
interval = "once";
|
|
||||||
signal = 8;
|
|
||||||
};
|
|
||||||
|
|
||||||
# Recording state has no event to hook, so the widget samples the
|
|
||||||
# wf-recorder process once a second.
|
|
||||||
"custom/recording" = {
|
|
||||||
return-type = "json";
|
|
||||||
exec = "${recStatus}";
|
|
||||||
interval = 1;
|
|
||||||
};
|
|
||||||
|
|
||||||
"custom/idle-lock" = {
|
|
||||||
return-type = "json";
|
|
||||||
exec = "${idleLockStatus}";
|
|
||||||
interval = "once";
|
|
||||||
signal = 9;
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,5 +1,21 @@
|
|||||||
{ config, lib, ... }:
|
{
|
||||||
|
config,
|
||||||
|
lib,
|
||||||
|
pkgs,
|
||||||
|
util,
|
||||||
|
...
|
||||||
|
}:
|
||||||
# PipeWire as the desktop audio server.
|
# PipeWire as the desktop audio server.
|
||||||
|
let
|
||||||
|
mediaKey = keys: command: {
|
||||||
|
inherit keys;
|
||||||
|
locked = true;
|
||||||
|
action = {
|
||||||
|
kind = "exec";
|
||||||
|
command = "${lib.getExe pkgs.playerctl} ${command}";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
in
|
||||||
{
|
{
|
||||||
options.modules.desktop.utilities.audio.enable = lib.mkEnableOption "the PipeWire audio server";
|
options.modules.desktop.utilities.audio.enable = lib.mkEnableOption "the PipeWire audio server";
|
||||||
|
|
||||||
@@ -12,5 +28,39 @@
|
|||||||
alsa.enable = true;
|
alsa.enable = true;
|
||||||
pulse.enable = true;
|
pulse.enable = true;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
registry.keybind.${config.networking.hostName} = {
|
||||||
|
media-play = mediaKey "XF86AudioPlay" "play-pause";
|
||||||
|
media-next = mediaKey "XF86AudioNext" "next";
|
||||||
|
media-previous = mediaKey "XF86AudioPrev" "previous";
|
||||||
|
};
|
||||||
|
|
||||||
|
registry.waybar.${config.networking.hostName} = {
|
||||||
|
mpris = {
|
||||||
|
order = 30;
|
||||||
|
settings = {
|
||||||
|
format = "${util.glyph "f001"} {title}";
|
||||||
|
format-paused = "${util.glyph "f04c"} {title}";
|
||||||
|
max-length = 40;
|
||||||
|
on-click = "play-pause";
|
||||||
|
on-scroll-up = "next";
|
||||||
|
on-scroll-down = "previous";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
wireplumber = {
|
||||||
|
order = 40;
|
||||||
|
settings = {
|
||||||
|
format = "{icon} {volume}%";
|
||||||
|
format-muted = "${util.glyph "f026"} muted";
|
||||||
|
format-icons = [
|
||||||
|
(util.glyph "f026")
|
||||||
|
(util.glyph "f027")
|
||||||
|
(util.glyph "f028")
|
||||||
|
];
|
||||||
|
on-click = "${lib.getExe' pkgs.wireplumber "wpctl"} set-mute @DEFAULT_AUDIO_SINK@ toggle";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,21 +6,15 @@
|
|||||||
}:
|
}:
|
||||||
# Clipboard history: cliphist records every copy, picked back through rofi.
|
# Clipboard history: cliphist records every copy, picked back through rofi.
|
||||||
let
|
let
|
||||||
user = config.user.name;
|
|
||||||
|
|
||||||
cliphist = "${pkgs.cliphist}/bin/cliphist";
|
|
||||||
rofi = "${pkgs.rofi}/bin/rofi";
|
|
||||||
wl-copy = "${pkgs.wl-clipboard}/bin/wl-copy";
|
|
||||||
|
|
||||||
# The picker reuses the themed rofi, so history looks like every other menu
|
# The picker reuses the themed rofi, so history looks like every other menu
|
||||||
# the launcher drives.
|
# the launcher drives.
|
||||||
# decode is needed because list emits id-prefixed lines rather than the copied
|
# decode is needed because list emits id-prefixed lines rather than the copied
|
||||||
# bytes, so the chosen id has to be resolved back before it can be re-copied.
|
# bytes, so the chosen id has to be resolved back before it can be re-copied.
|
||||||
picker = pkgs.writeShellScript "clipboard-picker" ''
|
picker = pkgs.writeShellScript "clipboard-picker" ''
|
||||||
${cliphist} list \
|
${lib.getExe pkgs.cliphist} list \
|
||||||
| ${rofi} -dmenu -i -p Clipboard \
|
| ${lib.getExe pkgs.rofi} -dmenu -i -p Clipboard \
|
||||||
| ${cliphist} decode \
|
| ${lib.getExe pkgs.cliphist} decode \
|
||||||
| ${wl-copy}
|
| ${lib.getExe' pkgs.wl-clipboard "wl-copy"}
|
||||||
'';
|
'';
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
@@ -28,7 +22,7 @@ in
|
|||||||
lib.mkEnableOption "cliphist clipboard history";
|
lib.mkEnableOption "cliphist clipboard history";
|
||||||
|
|
||||||
config = lib.mkIf config.modules.desktop.utilities.clipboard.enable {
|
config = lib.mkIf config.modules.desktop.utilities.clipboard.enable {
|
||||||
home-manager.users.${user} = {
|
home-manager.users.${config.user.name} = {
|
||||||
# wl-copy and wl-paste on PATH, so the shell can pipe into and out of the
|
# wl-copy and wl-paste on PATH, so the shell can pipe into and out of the
|
||||||
# clipboard.
|
# clipboard.
|
||||||
# The watchers and picker above reach wl-clipboard by store path, so this
|
# The watchers and picker above reach wl-clipboard by store path, so this
|
||||||
@@ -38,13 +32,14 @@ in
|
|||||||
# Two watchers record text and images to history, bound to the graphical
|
# Two watchers record text and images to history, bound to the graphical
|
||||||
# session so uwsm starts and stops them with it.
|
# session so uwsm starts and stops them with it.
|
||||||
services.cliphist.enable = true;
|
services.cliphist.enable = true;
|
||||||
|
};
|
||||||
|
|
||||||
# $mod is defined by the compositor config these binds share.
|
registry.keybind.${config.networking.hostName}.clipboard-history = {
|
||||||
/*
|
keys = "SUPER + SHIFT + V";
|
||||||
wayland.windowManager.hyprland.settings.bind = [
|
action = {
|
||||||
"$mod SHIFT, V, exec, ${picker}"
|
kind = "exec";
|
||||||
];
|
command = "${picker}";
|
||||||
*/
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,6 +13,53 @@ in
|
|||||||
lib.mkEnableOption "the SwayOSD volume and brightness popup";
|
lib.mkEnableOption "the SwayOSD volume and brightness popup";
|
||||||
|
|
||||||
config = lib.mkIf config.modules.desktop.utilities.osd.enable {
|
config = lib.mkIf config.modules.desktop.utilities.osd.enable {
|
||||||
|
registry.keybind.${config.networking.hostName} = {
|
||||||
|
volume-up = {
|
||||||
|
keys = "XF86AudioRaiseVolume";
|
||||||
|
repeating = true;
|
||||||
|
action = {
|
||||||
|
kind = "exec";
|
||||||
|
command = "${pkgs.swayosd}/bin/swayosd-client --output-volume raise --max-volume 100";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
volume-down = {
|
||||||
|
keys = "XF86AudioLowerVolume";
|
||||||
|
repeating = true;
|
||||||
|
action = {
|
||||||
|
kind = "exec";
|
||||||
|
command = "${pkgs.swayosd}/bin/swayosd-client --output-volume lower";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
brightness-up = {
|
||||||
|
keys = "XF86MonBrightnessUp";
|
||||||
|
repeating = true;
|
||||||
|
action = {
|
||||||
|
kind = "exec";
|
||||||
|
command = "${pkgs.swayosd}/bin/swayosd-client --brightness raise";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
brightness-down = {
|
||||||
|
keys = "XF86MonBrightnessDown";
|
||||||
|
repeating = true;
|
||||||
|
action = {
|
||||||
|
kind = "exec";
|
||||||
|
command = "${pkgs.swayosd}/bin/swayosd-client --brightness lower";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
mute = {
|
||||||
|
keys = "XF86AudioMute";
|
||||||
|
locked = true;
|
||||||
|
action = {
|
||||||
|
kind = "exec";
|
||||||
|
command = "${pkgs.swayosd}/bin/swayosd-client --output-volume mute-toggle";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
environment.systemPackages = [ pkgs.swayosd ];
|
environment.systemPackages = [ pkgs.swayosd ];
|
||||||
|
|
||||||
# The udev rule chgrps each backlight's brightness node to `video` and adds
|
# The udev rule chgrps each backlight's brightness node to `video` and adds
|
||||||
|
|||||||
@@ -2,21 +2,11 @@
|
|||||||
config,
|
config,
|
||||||
lib,
|
lib,
|
||||||
pkgs,
|
pkgs,
|
||||||
|
util,
|
||||||
...
|
...
|
||||||
}:
|
}:
|
||||||
# Keybound screen recording: pick a region, then toggle a video-only capture.
|
# Keybound screen recording: pick a region, then toggle a video-only capture.
|
||||||
let
|
let
|
||||||
user = config.user.name;
|
|
||||||
|
|
||||||
slurp = "${pkgs.slurp}/bin/slurp";
|
|
||||||
wf-recorder = "${pkgs.wf-recorder}/bin/wf-recorder";
|
|
||||||
notify-send = "${pkgs.libnotify}/bin/notify-send";
|
|
||||||
pgrep = "${pkgs.procps}/bin/pgrep";
|
|
||||||
pkill = "${pkgs.procps}/bin/pkill";
|
|
||||||
date = "${pkgs.coreutils}/bin/date";
|
|
||||||
mkdir = "${pkgs.coreutils}/bin/mkdir";
|
|
||||||
xdgUserDir = "${pkgs.xdg-user-dirs}/bin/xdg-user-dir";
|
|
||||||
|
|
||||||
# A single key both starts and stops.
|
# A single key both starts and stops.
|
||||||
# A running wf-recorder is stopped with SIGINT so it finalises the file.
|
# A running wf-recorder is stopped with SIGINT so it finalises the file.
|
||||||
# Otherwise slurp picks a region and wf-recorder runs in the foreground until
|
# Otherwise slurp picks a region and wf-recorder runs in the foreground until
|
||||||
@@ -24,19 +14,29 @@ let
|
|||||||
# reports it saved.
|
# reports it saved.
|
||||||
# No -a means no audio is captured.
|
# No -a means no audio is captured.
|
||||||
toggle = pkgs.writeShellScript "screen-record-toggle" ''
|
toggle = pkgs.writeShellScript "screen-record-toggle" ''
|
||||||
if ${pgrep} -x wf-recorder >/dev/null; then
|
if ${lib.getExe' pkgs.procps "pgrep"} -x wf-recorder >/dev/null; then
|
||||||
${pkill} -INT -x wf-recorder
|
${lib.getExe' pkgs.procps "pkill"} -INT -x wf-recorder
|
||||||
exit 0
|
exit 0
|
||||||
fi
|
fi
|
||||||
|
|
||||||
region=$(${slurp}) || exit 0
|
region=$(${lib.getExe pkgs.slurp}) || exit 0
|
||||||
dir="$(${xdgUserDir} VIDEOS)/Recordings"
|
dir="$(${lib.getExe' pkgs.xdg-user-dirs "xdg-user-dir"} VIDEOS)/Recordings"
|
||||||
${mkdir} -p "$dir"
|
${lib.getExe' pkgs.coreutils "mkdir"} -p "$dir"
|
||||||
file="$dir/recording-$(${date} +%Y%m%d-%H%M%S).mp4"
|
file="$dir/recording-$(${lib.getExe' pkgs.coreutils "date"} +%Y%m%d-%H%M%S).mp4"
|
||||||
|
|
||||||
${notify-send} -a "Screen recording" "Recording started" "Region capture, no audio."
|
${lib.getExe' pkgs.libnotify "notify-send"} -a "Screen recording" "Recording started" "Region capture, no audio."
|
||||||
${wf-recorder} -g "$region" -f "$file"
|
${lib.getExe pkgs.wf-recorder} -g "$region" -f "$file"
|
||||||
${notify-send} -a "Screen recording" "Recording saved" "$file"
|
${lib.getExe' pkgs.libnotify "notify-send"} -a "Screen recording" "Recording saved" "$file"
|
||||||
|
'';
|
||||||
|
|
||||||
|
# Lit while a wf-recorder capture is running.
|
||||||
|
# Empty text collapses the widget when idle, so it shows nothing until then.
|
||||||
|
recordingStatus = pkgs.writeShellScript "waybar-recording-status" ''
|
||||||
|
if ${lib.getExe' pkgs.procps "pgrep"} -x wf-recorder >/dev/null; then
|
||||||
|
printf '{"text":"${util.glyph "f03d"}","tooltip":"Recording","class":"recording"}\n'
|
||||||
|
else
|
||||||
|
printf '{"text":"","class":"idle"}\n'
|
||||||
|
fi
|
||||||
'';
|
'';
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
@@ -44,13 +44,21 @@ in
|
|||||||
lib.mkEnableOption "wf-recorder screen recording";
|
lib.mkEnableOption "wf-recorder screen recording";
|
||||||
|
|
||||||
config = lib.mkIf config.modules.desktop.utilities.recording.enable {
|
config = lib.mkIf config.modules.desktop.utilities.recording.enable {
|
||||||
home-manager.users.${user} = {
|
registry.keybind.${config.networking.hostName}.recording-toggle = {
|
||||||
# $mod is defined by the compositor config these binds share.
|
keys = "SUPER + SHIFT + R";
|
||||||
/*
|
action = {
|
||||||
wayland.windowManager.hyprland.settings.bind = [
|
kind = "exec";
|
||||||
"$mod SHIFT, R, exec, ${toggle}"
|
command = "${toggle}";
|
||||||
];
|
};
|
||||||
*/
|
};
|
||||||
|
|
||||||
|
registry.waybar.${config.networking.hostName}."custom/recording" = {
|
||||||
|
order = 10;
|
||||||
|
settings = {
|
||||||
|
return-type = "json";
|
||||||
|
exec = "${recordingStatus}";
|
||||||
|
interval = 1;
|
||||||
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -37,27 +37,42 @@ in
|
|||||||
lib.mkEnableOption "screenshots via grimblast and satty";
|
lib.mkEnableOption "screenshots via grimblast and satty";
|
||||||
|
|
||||||
config = lib.mkIf config.modules.desktop.utilities.screenshot.enable {
|
config = lib.mkIf config.modules.desktop.utilities.screenshot.enable {
|
||||||
|
registry.keybind.${config.networking.hostName} = {
|
||||||
|
screenshot-area = {
|
||||||
|
keys = "Print";
|
||||||
|
action = {
|
||||||
|
kind = "exec";
|
||||||
|
command = "${capture "area"}";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
screenshot-active = {
|
||||||
|
keys = "SHIFT + Print";
|
||||||
|
action = {
|
||||||
|
kind = "exec";
|
||||||
|
command = "${capture "active"}";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
screenshot-screen = {
|
||||||
|
keys = "CTRL + Print";
|
||||||
|
action = {
|
||||||
|
kind = "exec";
|
||||||
|
command = "${capture "screen"}";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
home-manager.users.${user} = {
|
home-manager.users.${user} = {
|
||||||
# satty is a one-shot annotation surface, so float it rather than letting
|
# satty is a one-shot annotation surface, so float it rather than letting
|
||||||
# it claim a tile in the layout.
|
# it claim a tile in the layout.
|
||||||
# windowrule v3 syntax: space-separated field/value tokens, comma
|
wayland.windowManager.hyprland.settings.window_rule = [
|
||||||
# separated, with matchers under a match: prefix and effects bare.
|
{
|
||||||
/*
|
match.class = "^(com\\.gabm\\.satty)$";
|
||||||
wayland.windowManager.hyprland.settings.windowrule = [
|
float = true;
|
||||||
"float 1, match:class ^(com\\.gabm\\.satty)$"
|
}
|
||||||
];
|
];
|
||||||
*/
|
|
||||||
|
|
||||||
# Print with plain/Shift/Ctrl for region/window/full.
|
|
||||||
# Super+L is already the hjkl focus and movement bind, so screenshots take
|
|
||||||
# the Print key instead.
|
|
||||||
/*
|
|
||||||
wayland.windowManager.hyprland.settings.bind = [
|
|
||||||
", Print, exec, ${capture "area"}"
|
|
||||||
"SHIFT, Print, exec, ${capture "active"}"
|
|
||||||
"CTRL, Print, exec, ${capture "screen"}"
|
|
||||||
];
|
|
||||||
*/
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -58,6 +58,10 @@ in
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
registry.env.${config.networking.hostName} = {
|
||||||
|
HYPRCURSOR_THEME = config.stylix.cursor.name;
|
||||||
|
HYPRCURSOR_SIZE = toString config.stylix.cursor.size;
|
||||||
|
};
|
||||||
|
|
||||||
home-manager.users.${user} = {
|
home-manager.users.${user} = {
|
||||||
# Stylix drives the cursor through home-manager's pointer-cursor config,
|
# Stylix drives the cursor through home-manager's pointer-cursor config,
|
||||||
|
|||||||
@@ -53,6 +53,7 @@ in
|
|||||||
home.username = config.user.name;
|
home.username = config.user.name;
|
||||||
home.homeDirectory = "/home/${config.user.name}";
|
home.homeDirectory = "/home/${config.user.name}";
|
||||||
home.stateVersion = config.system.stateVersion;
|
home.stateVersion = config.system.stateVersion;
|
||||||
|
home.sessionVariables = lib.attrByPath [ config.networking.hostName ] { } config.registry.env;
|
||||||
home.packages = with pkgs; [
|
home.packages = with pkgs; [
|
||||||
eza
|
eza
|
||||||
unzip
|
unzip
|
||||||
|
|||||||
57
configs/nixos/modules/network/default.nix
Normal file
57
configs/nixos/modules/network/default.nix
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
{
|
||||||
|
config,
|
||||||
|
lib,
|
||||||
|
pkgs,
|
||||||
|
util,
|
||||||
|
...
|
||||||
|
}:
|
||||||
|
# NetworkManager-managed networking.
|
||||||
|
let
|
||||||
|
networkStatus = pkgs.writeShellScript "waybar-network-status" ''
|
||||||
|
active=$(
|
||||||
|
${lib.getExe' pkgs.networkmanager "nmcli"} -t -f DEVICE,TYPE,STATE,CONNECTION device status \
|
||||||
|
| ${lib.getExe' pkgs.gawk "awk"} -F: '$3 == "connected" && $2 != "loopback" { print; exit }'
|
||||||
|
)
|
||||||
|
|
||||||
|
if [ -z "$active" ]; then
|
||||||
|
${lib.getExe pkgs.jq} -cn \
|
||||||
|
--arg text "${util.glyph "f127"} off" \
|
||||||
|
--arg tooltip "Network disconnected" \
|
||||||
|
--arg class "disconnected" \
|
||||||
|
'{ text: $text, tooltip: $tooltip, class: $class }'
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
IFS=: read -r device type _state connection <<< "$active"
|
||||||
|
|
||||||
|
case "$type" in
|
||||||
|
wifi) icon="${util.glyph "f1eb"}" ;;
|
||||||
|
ethernet) icon="LAN" ;;
|
||||||
|
*) icon="NET" ;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
ipaddr=$(${lib.getExe' pkgs.iproute2 "ip"} -4 -o addr show dev "$device" | ${lib.getExe' pkgs.gawk "awk"} '{ print $4; exit }')
|
||||||
|
|
||||||
|
${lib.getExe pkgs.jq} -cn \
|
||||||
|
--arg text "$icon $connection" \
|
||||||
|
--arg tooltip "$device: ''${ipaddr:-no IPv4}" \
|
||||||
|
--arg class "$type" \
|
||||||
|
'{ text: $text, tooltip: $tooltip, class: $class }'
|
||||||
|
'';
|
||||||
|
in
|
||||||
|
{
|
||||||
|
options.modules.network.enable = lib.mkEnableOption "NetworkManager-managed networking";
|
||||||
|
|
||||||
|
config = lib.mkIf config.modules.network.enable {
|
||||||
|
networking.networkmanager.enable = true;
|
||||||
|
|
||||||
|
registry.waybar.${config.networking.hostName}."custom/network" = {
|
||||||
|
order = 60;
|
||||||
|
settings = {
|
||||||
|
return-type = "json";
|
||||||
|
exec = "${networkStatus}";
|
||||||
|
interval = 5;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -1,10 +1,34 @@
|
|||||||
{
|
{
|
||||||
config,
|
config,
|
||||||
lib,
|
lib,
|
||||||
|
pkgs,
|
||||||
|
util,
|
||||||
...
|
...
|
||||||
}:
|
}:
|
||||||
let
|
let
|
||||||
cfg = config.modules.network.vpn-client; # NOLINT(let_order)
|
cfg = config.modules.network.vpn-client; # NOLINT(let_order)
|
||||||
|
|
||||||
|
vpnIcon = util.glyph [
|
||||||
|
"db83"
|
||||||
|
"dc9d"
|
||||||
|
];
|
||||||
|
|
||||||
|
vpnStatus = pkgs.writeShellScript "waybar-vpn-status" ''
|
||||||
|
interface=${lib.escapeShellArg cfg.interface}
|
||||||
|
|
||||||
|
if ! ${lib.getExe' pkgs.iproute2 "ip"} link show dev "$interface" >/dev/null 2>&1; then
|
||||||
|
printf '{"text":"","class":"idle"}\n'
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
ipaddr=$(${lib.getExe' pkgs.iproute2 "ip"} -4 -o addr show dev "$interface" | ${lib.getExe' pkgs.gawk "awk"} '{ print $4; exit }')
|
||||||
|
|
||||||
|
${lib.getExe pkgs.jq} -cn \
|
||||||
|
--arg text "${vpnIcon}" \
|
||||||
|
--arg tooltip "VPN $interface: ''${ipaddr:-no IPv4}" \
|
||||||
|
--arg class "active" \
|
||||||
|
'{ text: $text, tooltip: $tooltip, class: $class }'
|
||||||
|
'';
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
options.modules.network.vpn-client = {
|
options.modules.network.vpn-client = {
|
||||||
@@ -57,6 +81,15 @@ in
|
|||||||
vpn-off = "sudo systemctl stop 'wireguard-${cfg.interface}*.service' wireguard-${cfg.interface}.target";
|
vpn-off = "sudo systemctl stop 'wireguard-${cfg.interface}*.service' wireguard-${cfg.interface}.target";
|
||||||
};
|
};
|
||||||
|
|
||||||
|
registry.waybar.${config.networking.hostName}."custom/vpn" = {
|
||||||
|
order = 50;
|
||||||
|
settings = {
|
||||||
|
return-type = "json";
|
||||||
|
exec = "${vpnStatus}";
|
||||||
|
interval = 5;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
systemd.targets."wireguard-${cfg.interface}".wantedBy = lib.mkForce [ ];
|
systemd.targets."wireguard-${cfg.interface}".wantedBy = lib.mkForce [ ];
|
||||||
|
|
||||||
networking.wireguard.interfaces.${cfg.interface} = {
|
networking.wireguard.interfaces.${cfg.interface} = {
|
||||||
|
|||||||
@@ -13,9 +13,8 @@ in
|
|||||||
lib.mkEnableOption "Python tools for the primary user's shell environment";
|
lib.mkEnableOption "Python tools for the primary user's shell environment";
|
||||||
|
|
||||||
config = lib.mkIf config.modules.shell.python.enable {
|
config = lib.mkIf config.modules.shell.python.enable {
|
||||||
home-manager.users.${user}.home = {
|
registry.env.${config.networking.hostName}.PYTHON_TOOLCHAIN_ROOT = "${pkgs.python3}";
|
||||||
sessionVariables.PYTHON_TOOLCHAIN_ROOT = "${pkgs.python3}";
|
|
||||||
packages = [ pkgs.python3 ];
|
home-manager.users.${user}.home.packages = [ pkgs.python3 ];
|
||||||
};
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,9 +23,9 @@ in
|
|||||||
lib.mkEnableOption "Rust tools for the primary user's shell environment";
|
lib.mkEnableOption "Rust tools for the primary user's shell environment";
|
||||||
|
|
||||||
config = lib.mkIf config.modules.shell.rust.enable {
|
config = lib.mkIf config.modules.shell.rust.enable {
|
||||||
home-manager.users.${user} = {
|
registry.env.${config.networking.hostName}.RUST_TOOLCHAIN_ROOT = "${toolchain}";
|
||||||
home.sessionVariables.RUST_TOOLCHAIN_ROOT = "${toolchain}";
|
|
||||||
|
|
||||||
|
home-manager.users.${user} = {
|
||||||
home.packages = [
|
home.packages = [
|
||||||
toolchain
|
toolchain
|
||||||
pkgs.rust-analyzer
|
pkgs.rust-analyzer
|
||||||
|
|||||||
4
configs/nixos/registry/env.nix
Normal file
4
configs/nixos/registry/env.nix
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
{ lib, ... }:
|
||||||
|
{
|
||||||
|
type = lib.types.str;
|
||||||
|
}
|
||||||
@@ -53,6 +53,13 @@
|
|||||||
example = true;
|
example = true;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
locked = lib.mkOption {
|
||||||
|
description = "Allows the keybind action while the session is locked.";
|
||||||
|
type = lib.types.bool;
|
||||||
|
default = false;
|
||||||
|
example = true;
|
||||||
|
};
|
||||||
|
|
||||||
action = lib.mkOption {
|
action = lib.mkOption {
|
||||||
description = "Declares the action this keybind runs.";
|
description = "Declares the action this keybind runs.";
|
||||||
type = lib.types.submodule {
|
type = lib.types.submodule {
|
||||||
|
|||||||
29
configs/nixos/registry/waybar.nix
Normal file
29
configs/nixos/registry/waybar.nix
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
{ lib, ... }:
|
||||||
|
{
|
||||||
|
type = lib.types.submodule {
|
||||||
|
options = {
|
||||||
|
position = lib.mkOption {
|
||||||
|
type = lib.types.enum [
|
||||||
|
"left"
|
||||||
|
"center"
|
||||||
|
"right"
|
||||||
|
];
|
||||||
|
default = "right";
|
||||||
|
description = "Selects the Waybar section that shows the module.";
|
||||||
|
};
|
||||||
|
|
||||||
|
order = lib.mkOption {
|
||||||
|
type = lib.types.int;
|
||||||
|
default = 100;
|
||||||
|
example = 50;
|
||||||
|
description = "Sorts the module within its Waybar section.";
|
||||||
|
};
|
||||||
|
|
||||||
|
settings = lib.mkOption {
|
||||||
|
type = lib.types.attrsOf lib.types.anything;
|
||||||
|
default = { };
|
||||||
|
description = "Sets the Waybar module configuration.";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
19
configs/nixos/registry/window-icon.nix
Normal file
19
configs/nixos/registry/window-icon.nix
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
{ lib, ... }:
|
||||||
|
{
|
||||||
|
|
||||||
|
type = lib.types.submodule {
|
||||||
|
options = {
|
||||||
|
class = lib.mkOption {
|
||||||
|
description = "Matches the window class reported by the compositor.";
|
||||||
|
type = lib.types.str;
|
||||||
|
example = ".*[Ff]irefox.*";
|
||||||
|
};
|
||||||
|
|
||||||
|
icon = lib.mkOption {
|
||||||
|
description = "Sets the icon shown for matching windows.";
|
||||||
|
type = lib.types.str;
|
||||||
|
example = "";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -25,6 +25,14 @@ in
|
|||||||
|
|
||||||
removeNixSuffix = path: lib.removeSuffix ".nix" (builtins.baseNameOf path);
|
removeNixSuffix = path: lib.removeSuffix ".nix" (builtins.baseNameOf path);
|
||||||
|
|
||||||
|
glyph =
|
||||||
|
code:
|
||||||
|
builtins.fromJSON (
|
||||||
|
"\""
|
||||||
|
+ (if builtins.isList code then lib.concatMapStrings (part: "\\u${part}") code else "\\u${code}")
|
||||||
|
+ "\""
|
||||||
|
);
|
||||||
|
|
||||||
registry.predicates.uniqueValue = uniqueValue;
|
registry.predicates.uniqueValue = uniqueValue;
|
||||||
|
|
||||||
registry.predicates.uniquePerHost =
|
registry.predicates.uniquePerHost =
|
||||||
|
|||||||
@@ -21,3 +21,32 @@ Do not call `lib.removeSuffix ".nix"` outside the utility definition.
|
|||||||
### Rationale
|
### Rationale
|
||||||
|
|
||||||
The utility centralizes Nix file-name normalization.
|
The utility centralizes Nix file-name normalization.
|
||||||
|
|
||||||
|
## Use executable helpers for package commands
|
||||||
|
|
||||||
|
### Rule
|
||||||
|
|
||||||
|
Use `lib.getExe` for a package's main executable.
|
||||||
|
Use `lib.getExe'` for a package's non-main executable.
|
||||||
|
Do not build executable paths with `"${pkg}/bin/name"` when an executable helper names the target.
|
||||||
|
Use `lib.getExe' config.programs.<name>.package "tool"` when a command follows a configured package option.
|
||||||
|
|
||||||
|
### Rationale
|
||||||
|
|
||||||
|
Executable helpers select the package output that contains binaries.
|
||||||
|
They make package ownership explicit.
|
||||||
|
`lib.getExe'` prevents calls to a package's main executable when code needs another tool.
|
||||||
|
|
||||||
|
## Prefer NixOS package ownership for commands
|
||||||
|
|
||||||
|
### Rule
|
||||||
|
|
||||||
|
Use `pkgs.<name>` for Home Manager tools.
|
||||||
|
Use `config.programs.<name>.package` when a NixOS module owns the package selection.
|
||||||
|
Do not read `config.home-manager.users.<user>.programs.<name>.package` for command paths.
|
||||||
|
|
||||||
|
### Rationale
|
||||||
|
|
||||||
|
Home Manager uses the NixOS package set in this repository.
|
||||||
|
`pkgs.<name>` avoids dependency paths through the user tree under Home Manager.
|
||||||
|
Configured NixOS package options preserve package selection when a module owns the tool.
|
||||||
|
|||||||
Reference in New Issue
Block a user