77e14968ee
Two desktop-config fixes found together:
1. Keybindings: the previous Mod+Space launcher bind wrapped the set in
`lib.mkMerge [ { ... } (mkOptionDefault ...) ]`. A normal-priority
definition there wins over (and discards) the home-manager module's
default keybindings, which are at mkOptionDefault priority -- so every
default bind (terminal Mod+Return, movement, workspaces, kill, ...) and
even the custom swaylock/volume binds silently vanished; only Mod+Space
survived. That is why Super+Enter opened nothing. Restore the single
mkOptionDefault wrapper (so it merges with the module defaults) and
override just Mod+Space via lib.mkForce to beat the module's default
focus-mode_toggle without a same-priority conflict. Generated config
goes from 1 bindsym back to 57.
2. foot 1.27: the bare [colors] section is deprecated in favour of
[colors-dark], and `[cursor] color` is rejected ("not a valid option:
color"). Move the palette to colors-dark and set the cursor colour via
its `cursor` key ("<text> <cursor>"). `foot --check-config` now passes.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
335 lines
11 KiB
Nix
335 lines
11 KiB
Nix
# Declarative Sway window manager, status bar, lock, idle and notifications.
|
|
# Imported via ./desktop.nix, so only graphical hosts get it.
|
|
#
|
|
# The compositor binary, PAM and polkit integration come from the system-level
|
|
# programs.sway (see ../swaywm.nix); package = null below reuses it instead of
|
|
# pulling a second Sway. home-manager owns the user config (~/.config/sway) and
|
|
# wires the systemd user session (sway-session.target), which is what lets the
|
|
# swayidle/dunst user services start with the desktop.
|
|
{
|
|
pkgs,
|
|
lib,
|
|
# Threaded from mkHost (flake.nix). Desktop hosts set this false to drop
|
|
# mobile components (battery block, screen-brightness keys).
|
|
portable ? true,
|
|
...
|
|
}:
|
|
let
|
|
# Catppuccin Mocha (shared with the ReGreet greeter). Raw hex; prefix "#"
|
|
# where a consumer needs it -- Sway/i3status/dunst want "#", foot/swaylock do
|
|
# not.
|
|
ctp = import ../catppuccin-mocha.nix;
|
|
in
|
|
{
|
|
wayland.windowManager.sway = {
|
|
enable = true;
|
|
package = null;
|
|
# `sway --validate` needs a real package; skip it since package = null.
|
|
checkConfig = false;
|
|
config = rec {
|
|
modifier = "Mod4";
|
|
terminal = "${pkgs.foot}/bin/foot";
|
|
# Launcher: sway-launcher-desktop running inside a floating foot window.
|
|
menu = "${pkgs.foot}/bin/foot --app-id=launcher ${pkgs.sway-launcher-desktop}/bin/sway-launcher-desktop";
|
|
|
|
# Dvorak is a variant of the "us" layout, not a standalone layout --
|
|
# `xkb_layout = "dvorak"` fails to compile (no symbols/dvorak) and wlroots
|
|
# silently falls back to QWERTY. Use the variant.
|
|
input."type:keyboard" = {
|
|
xkb_layout = "us";
|
|
xkb_variant = "dvorak";
|
|
};
|
|
|
|
# Window borders -- Catppuccin Mocha (blue accent on the focused window).
|
|
colors = {
|
|
focused = {
|
|
border = "#${ctp.blue}";
|
|
background = "#${ctp.base}";
|
|
text = "#${ctp.text}";
|
|
indicator = "#${ctp.blue}";
|
|
childBorder = "#${ctp.blue}";
|
|
};
|
|
focusedInactive = {
|
|
border = "#${ctp.surface0}";
|
|
background = "#${ctp.base}";
|
|
text = "#${ctp.subtext0}";
|
|
indicator = "#${ctp.surface0}";
|
|
childBorder = "#${ctp.surface0}";
|
|
};
|
|
unfocused = {
|
|
border = "#${ctp.surface0}";
|
|
background = "#${ctp.base}";
|
|
text = "#${ctp.subtext0}";
|
|
indicator = "#${ctp.surface0}";
|
|
childBorder = "#${ctp.surface0}";
|
|
};
|
|
urgent = {
|
|
border = "#${ctp.red}";
|
|
background = "#${ctp.base}";
|
|
text = "#${ctp.text}";
|
|
indicator = "#${ctp.red}";
|
|
childBorder = "#${ctp.red}";
|
|
};
|
|
};
|
|
|
|
window.commands = [
|
|
{
|
|
criteria.app_id = "launcher";
|
|
command = "floating enable, resize set 800 500";
|
|
}
|
|
];
|
|
|
|
bars = [
|
|
{
|
|
position = "top";
|
|
statusCommand = "${pkgs.i3status-rust}/bin/i3status-rs ~/.config/i3status-rust/config-default.toml";
|
|
fonts = {
|
|
names = [
|
|
"Noto Sans"
|
|
"Font Awesome 6 Free"
|
|
];
|
|
size = 11.0;
|
|
};
|
|
# Bar background + workspace buttons -- Catppuccin Mocha. The mantle
|
|
# background matches i3status-rust's idle_bg below for a uniform strip.
|
|
colors = {
|
|
background = "#${ctp.mantle}";
|
|
statusline = "#${ctp.text}";
|
|
separator = "#${ctp.surface0}";
|
|
focusedWorkspace = {
|
|
border = "#${ctp.blue}";
|
|
background = "#${ctp.blue}";
|
|
text = "#${ctp.base}";
|
|
};
|
|
activeWorkspace = {
|
|
border = "#${ctp.surface0}";
|
|
background = "#${ctp.surface0}";
|
|
text = "#${ctp.text}";
|
|
};
|
|
inactiveWorkspace = {
|
|
border = "#${ctp.mantle}";
|
|
background = "#${ctp.mantle}";
|
|
text = "#${ctp.subtext0}";
|
|
};
|
|
urgentWorkspace = {
|
|
border = "#${ctp.red}";
|
|
background = "#${ctp.red}";
|
|
text = "#${ctp.base}";
|
|
};
|
|
};
|
|
}
|
|
];
|
|
|
|
# NB: this whole set is wrapped in mkOptionDefault so it MERGES with the
|
|
# home-manager module's default keybindings (same priority) rather than
|
|
# replacing them. Do not wrap it in mkMerge with a normal-priority attr --
|
|
# that makes the normal-priority def win and silently drops every default
|
|
# bind (terminal, movement, workspaces, ...).
|
|
keybindings = lib.mkOptionDefault (
|
|
{
|
|
# Launcher on Mod+Space. mkForce overrides the module's own default
|
|
# Mod+Space (focus mode_toggle); a plain value would conflict with it
|
|
# at equal priority. Mod+d also still runs the launcher (module default).
|
|
"${modifier}+space" = lib.mkForce "exec ${menu}";
|
|
"${modifier}+l" = "exec ${pkgs.swaylock}/bin/swaylock -f";
|
|
"Print" = "exec ${pkgs.grim}/bin/grim ~/screenshot-$(date +%F-%H%M%S).png";
|
|
"XF86AudioRaiseVolume" = "exec ${pkgs.wireplumber}/bin/wpctl set-volume @DEFAULT_AUDIO_SINK@ 5%+";
|
|
"XF86AudioLowerVolume" = "exec ${pkgs.wireplumber}/bin/wpctl set-volume @DEFAULT_AUDIO_SINK@ 5%-";
|
|
"XF86AudioMute" = "exec ${pkgs.wireplumber}/bin/wpctl set-mute @DEFAULT_AUDIO_SINK@ toggle";
|
|
}
|
|
# Screen backlight: laptops only (no internal backlight on a desktop).
|
|
// lib.optionalAttrs portable {
|
|
"XF86MonBrightnessUp" = "exec ${pkgs.brightnessctl}/bin/brightnessctl set 5%+";
|
|
"XF86MonBrightnessDown" = "exec ${pkgs.brightnessctl}/bin/brightnessctl set 5%-";
|
|
}
|
|
);
|
|
};
|
|
};
|
|
|
|
# Terminal: Catppuccin Mocha. foot reads ~/.config/foot/foot.ini; the Sway
|
|
# `terminal` above still launches the same binary, now themed.
|
|
programs.foot = {
|
|
enable = true;
|
|
# foot 1.27: the bare [colors] section is deprecated in favour of
|
|
# [colors-dark] (the default theme), and the cursor colour moved out of
|
|
# [cursor] (where `color` is now rejected) into a `cursor` key here, written
|
|
# "<text> <cursor>" (man foot.ini(5): "ff0000 00ff00" => green cursor, red
|
|
# text). Only colors-dark is needed; we never set initial-color-theme=light.
|
|
settings = {
|
|
"colors-dark" = {
|
|
background = ctp.base;
|
|
foreground = ctp.text;
|
|
regular0 = ctp.surface1;
|
|
regular1 = ctp.red;
|
|
regular2 = ctp.green;
|
|
regular3 = ctp.yellow;
|
|
regular4 = ctp.blue;
|
|
regular5 = ctp.pink;
|
|
regular6 = ctp.teal;
|
|
regular7 = ctp.subtext1;
|
|
bright0 = ctp.surface2;
|
|
bright1 = ctp.red;
|
|
bright2 = ctp.green;
|
|
bright3 = ctp.yellow;
|
|
bright4 = ctp.blue;
|
|
bright5 = ctp.pink;
|
|
bright6 = ctp.teal;
|
|
bright7 = ctp.subtext0;
|
|
"selection-foreground" = ctp.base;
|
|
"selection-background" = ctp.rosewater;
|
|
cursor = "${ctp.base} ${ctp.rosewater}";
|
|
};
|
|
};
|
|
};
|
|
|
|
programs.swaylock = {
|
|
enable = true;
|
|
# Catppuccin Mocha (swaylock colours are hex without "#").
|
|
settings = {
|
|
color = ctp.base;
|
|
indicator-radius = 100;
|
|
indicator-thickness = 7;
|
|
show-failed-attempts = true;
|
|
font = "Noto Sans";
|
|
|
|
inside-color = ctp.base;
|
|
inside-clear-color = ctp.base;
|
|
inside-ver-color = ctp.base;
|
|
inside-wrong-color = ctp.base;
|
|
|
|
ring-color = ctp.surface1;
|
|
ring-clear-color = ctp.yellow;
|
|
ring-ver-color = ctp.blue;
|
|
ring-wrong-color = ctp.red;
|
|
|
|
key-hl-color = ctp.blue;
|
|
bs-hl-color = ctp.red;
|
|
|
|
line-color = ctp.base;
|
|
line-clear-color = ctp.base;
|
|
line-ver-color = ctp.base;
|
|
line-wrong-color = ctp.base;
|
|
separator-color = "00000000";
|
|
|
|
text-color = ctp.text;
|
|
text-clear-color = ctp.text;
|
|
text-ver-color = ctp.text;
|
|
text-wrong-color = ctp.text;
|
|
};
|
|
};
|
|
|
|
# Lock on idle, turn screens off shortly after, and lock before sleep.
|
|
services.swayidle = {
|
|
enable = true;
|
|
timeouts = [
|
|
{
|
|
timeout = 300;
|
|
command = "${pkgs.swaylock}/bin/swaylock -f";
|
|
}
|
|
{
|
|
timeout = 600;
|
|
command = "${pkgs.sway}/bin/swaymsg 'output * power off'";
|
|
resumeCommand = "${pkgs.sway}/bin/swaymsg 'output * power on'";
|
|
}
|
|
];
|
|
events = {
|
|
before-sleep = "${pkgs.swaylock}/bin/swaylock -f";
|
|
lock = "${pkgs.swaylock}/bin/swaylock -f";
|
|
};
|
|
};
|
|
|
|
services.dunst = {
|
|
enable = true;
|
|
# Catppuccin Mocha notifications (dunst colours need a leading "#").
|
|
settings = {
|
|
global = {
|
|
font = "Noto Sans 11";
|
|
frame_color = "#${ctp.blue}";
|
|
frame_width = 2;
|
|
separator_color = "frame";
|
|
offset = "10x10";
|
|
corner_radius = 5;
|
|
};
|
|
urgency_low = {
|
|
background = "#${ctp.base}";
|
|
foreground = "#${ctp.text}";
|
|
frame_color = "#${ctp.surface1}";
|
|
};
|
|
urgency_normal = {
|
|
background = "#${ctp.base}";
|
|
foreground = "#${ctp.text}";
|
|
frame_color = "#${ctp.blue}";
|
|
};
|
|
urgency_critical = {
|
|
background = "#${ctp.base}";
|
|
foreground = "#${ctp.text}";
|
|
frame_color = "#${ctp.peach}";
|
|
timeout = 0;
|
|
};
|
|
};
|
|
};
|
|
|
|
programs.i3status-rust = {
|
|
enable = true;
|
|
bars.default = {
|
|
# Catppuccin Mocha: a flat "plain" base recoloured via overrides. Idle
|
|
# blocks sit on mantle (matching the Sway bar background) with light text;
|
|
# only warning/critical states get a loud tinted background. The `theme`
|
|
# bar option is shallow-merged away by `settings.theme`, so set the base
|
|
# theme and its overrides together here.
|
|
icons = "awesome6";
|
|
settings.theme = {
|
|
theme = "plain";
|
|
overrides = {
|
|
idle_bg = "#${ctp.mantle}";
|
|
idle_fg = "#${ctp.text}";
|
|
info_bg = "#${ctp.mantle}";
|
|
info_fg = "#${ctp.blue}";
|
|
good_bg = "#${ctp.mantle}";
|
|
good_fg = "#${ctp.green}";
|
|
warning_bg = "#${ctp.peach}";
|
|
warning_fg = "#${ctp.base}";
|
|
critical_bg = "#${ctp.red}";
|
|
critical_fg = "#${ctp.base}";
|
|
separator_bg = "#${ctp.mantle}";
|
|
separator_fg = "#${ctp.surface1}";
|
|
};
|
|
};
|
|
blocks = [
|
|
{
|
|
block = "disk_space";
|
|
path = "/";
|
|
format = " $icon $available ";
|
|
}
|
|
{
|
|
block = "memory";
|
|
format = " $icon $mem_used_percents ";
|
|
}
|
|
{
|
|
block = "cpu";
|
|
interval = 2;
|
|
}
|
|
]
|
|
# Desktop-only: CPU temperature and wired network throughput, in place
|
|
# of the laptop's battery readout.
|
|
++ lib.optionals (!portable) [
|
|
{
|
|
block = "temperature";
|
|
interval = 5;
|
|
format = " $icon $average avg, $max max ";
|
|
}
|
|
{ block = "net"; }
|
|
]
|
|
++ [ { block = "sound"; } ]
|
|
++ lib.optional portable { block = "battery"; }
|
|
++ [
|
|
{
|
|
block = "time";
|
|
interval = 5;
|
|
format = " $timestamp.datetime(f:'%a %d/%m %R') ";
|
|
}
|
|
];
|
|
};
|
|
};
|
|
}
|