dot
vizie's NixOS desktop: Hyprland, CachyOS kernel and gaming tweaks
6bafe8f
30 files changed +1091 −960
modifiedREADME.md
@@ -24,10 +24,12 @@ | ||
| 24 | 24 | apps.nix desktop apps, Claude Cowork, OpenDeck udev, Flatpak |
| 25 | 25 | vm.nix the test VM |
| 26 | 26 | |
| 27 | - | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 28 | 30 | |
| 29 | 31 | |
| 30 | - | |
| 32 | + | |
| 31 | 33 | |
| 32 | 34 | |
| 33 | 35 | |
@@ -44,10 +46,18 @@ | ||
| 44 | 46 | |
| 45 | 47 | |
| 46 | 48 | |
| 47 | - | |
| 48 | - | |
| 49 | - | |
| 50 | - | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 51 | 61 | |
| 52 | 62 | |
| 53 | 63 | |
@@ -78,8 +88,8 @@ | ||
| 78 | 88 | |
| 79 | 89 | |
| 80 | 90 | |
| 81 | - | |
| 82 | - | |
| 91 | + | |
| 92 | + | |
| 83 | 93 | |
| 84 | 94 | |
| 85 | 95 | |
deletedconfig/dunst/dunstrc
@@ -1,49 +1,0 @@ | ||
| 1 | -[global] | |
| 2 | -monitor = 0 | |
| 3 | -follow = mouse | |
| 4 | -alignment = center | |
| 5 | -width = 360 | |
| 6 | -offset = 15 | |
| 7 | -origin = top-right | |
| 8 | -padding = 12 | |
| 9 | -horizontal_padding = 12 | |
| 10 | -frame_width = 0 | |
| 11 | -markup = full | |
| 12 | -icon_position = off | |
| 13 | -font = "DejaVu Sans Mono 10" | |
| 14 | -stack_duplicates = true | |
| 15 | -hide_duplicate_count = true | |
| 16 | -separator_height = 0 | |
| 17 | -fullscreen = pushback | |
| 18 | -corner_radius = 15 | |
| 19 | -# Left click does what the notification is for (e.g. opens the chat), then closes | |
| 20 | -# it. Middle click only dismisses it, right click dismisses all. | |
| 21 | -mouse_left_click = do_action, close_current | |
| 22 | -mouse_middle_click = close_current | |
| 23 | -mouse_right_click = close_all | |
| 24 | -progress_bar = true | |
| 25 | - | |
| 26 | - | |
| 27 | -[urgency_low] | |
| 28 | -timeout = 4 | |
| 29 | -background = "#25252501" | |
| 30 | -foreground = "#ffffff" | |
| 31 | -frame_color = "#25252501" | |
| 32 | - | |
| 33 | -[urgency_normal] | |
| 34 | -timeout = 8 | |
| 35 | -background = "#25252501" | |
| 36 | -foreground = "#ffffff" | |
| 37 | -frame_color = "#25252501" | |
| 38 | - | |
| 39 | -[urgency_critical] | |
| 40 | -timeout = 16 | |
| 41 | -background = "#25252501" | |
| 42 | -foreground = "#ffffff" | |
| 43 | -frame_color = "#25252501" | |
| 44 | - | |
| 45 | -[status] | |
| 46 | -category = status | |
| 47 | -set_stack_tag = status | |
| 48 | -history_ignore = yes | |
| 49 | -timeout = 2 |
deletedconfig/firefox/user.js
@@ -1,28 +1,0 @@ | ||
| 1 | -// Restore previous session | |
| 2 | -; | |
| 3 | - | |
| 4 | -// Middle mouse scroll | |
| 5 | -; | |
| 6 | - | |
| 7 | -// Enable browser toolbox | |
| 8 | -; | |
| 9 | -; | |
| 10 | - | |
| 11 | -; | |
| 12 | -; | |
| 13 | -; | |
| 14 | -; | |
| 15 | -// user_pref("image.jxl.enabled", true); | |
| 16 | -; | |
| 17 | -; | |
| 18 | -; | |
| 19 | -; | |
| 20 | - | |
| 21 | -; | |
| 22 | - | |
| 23 | -// Hardware video decoding on NVIDIA via VA-API (libva-nvidia-driver). | |
| 24 | -// Firefox blocks NVIDIA for this by default; needs NVD_BACKEND and | |
| 25 | -// MOZ_DISABLE_RDD_SANDBOX from hyprland.lua as well. | |
| 26 | -; | |
| 27 | -; | |
| 28 | -; |
deletedconfig/fuzzel/fuzzel.ini
@@ -1,37 +1,0 @@ | ||
| 1 | -[main] | |
| 2 | -font=NotoSansM Nerd Font Propo:size=12 | |
| 3 | -# Size fonts like GTK apps do (96 DPI) rather than by the monitors' real DPI, | |
| 4 | -# which made everything bigger. | |
| 5 | -dpi-aware=no | |
| 6 | -terminal=kitty | |
| 7 | -# Same icon theme as Qt/KDE (qt6ct, kdeglobals). Without it fuzzel mixes each | |
| 8 | -# app's own icon, in different styles and sizes. | |
| 9 | -icon-theme=Papirus-Dark | |
| 10 | -list-executables-in-path=yes | |
| 11 | -placeholder=Search... | |
| 12 | -line-height=36 | |
| 13 | -lines=5 | |
| 14 | -width=45 | |
| 15 | -horizontal-pad=25 | |
| 16 | -vertical-pad=25 | |
| 17 | -inner-pad=10 | |
| 18 | - | |
| 19 | -# RRGGBBAA. Same near-transparent black as Waybar and the control centre, over | |
| 20 | -# Hyprland's blur (the "launcher" layer rule). | |
| 21 | -[colors] | |
| 22 | -background=0000001a | |
| 23 | -text=f2f2f2cc | |
| 24 | -prompt=f2f2f2cc | |
| 25 | -input=f2f2f2ff | |
| 26 | -placeholder=f2f2f2cc | |
| 27 | -match=f4d69fff | |
| 28 | -selection=0000001a | |
| 29 | -selection-text=f2f2f2ff | |
| 30 | -selection-match=f4d69fff | |
| 31 | -counter=f2f2f2cc | |
| 32 | - | |
| 33 | -# Panel 20, rows inside it 10, like the control centre. | |
| 34 | -[border] | |
| 35 | -width=0 | |
| 36 | -radius=20 | |
| 37 | -selection-radius=10 |
modifiedconfig/fuzzel/launch
@@ -1,6 +1,6 @@ | ||
| 1 | 1 | #!/usr/bin/env bash |
| 2 | 2 | # Opens fuzzel with the apps in ./favorites at the top of its list. Hyprland's |
| 3 | -# mod + Return runs this (hyprland.lua). | |
| 3 | +# mod + Return runs this (modules/home/desktop.nix). | |
| 4 | 4 | # |
| 5 | 5 | # Fuzzel can't pin apps; it sorts by how often each one was launched, counted in |
| 6 | 6 | # its cache ($XDG_CACHE_HOME/fuzzel, lines of "name.desktop|count"). So before |
modifiedconfig/greetd/style.css
@@ -1,4 +1,4 @@ | ||
| 1 | -/* greetd login screen (gtkgreet -l), styled like hypr/hyprlock.conf in the dotfiles: | |
| 1 | +/* greetd login screen (gtkgreet -l), styled like hyprlock (modules/home/desktop.nix): | |
| 2 | 2 | full-screen layer, Hyprland blurs the wallpaper behind it (layer rule in |
| 3 | 3 | hyprland.lua), translucent black boxes, #eee text, rounding 10. */ |
| 4 | 4 | |
deletedconfig/hypr/hypridle.conf
@@ -1,24 +1,0 @@ | ||
| 1 | -general { | |
| 2 | - lock_cmd=pidof hyprlock || hyprlock | |
| 3 | - before_sleep_cmd=loginctl lock-session | |
| 4 | - after_sleep_cmd=hyprctl dispatch 'hl.dsp.dpms({ action = "on" })' | |
| 5 | -} | |
| 6 | - | |
| 7 | -# Lock after 10 minutes idle (respects inhibitors, e.g. fullscreen video). | |
| 8 | -listener { | |
| 9 | - timeout=600 | |
| 10 | - on-timeout=loginctl lock-session | |
| 11 | -} | |
| 12 | - | |
| 13 | -listener { | |
| 14 | - timeout=1 | |
| 15 | - on-resume=hyprctl dispatch 'hl.dsp.dpms({ action = "on" })' | |
| 16 | - on-timeout=true | |
| 17 | - ignore_inhibit=true | |
| 18 | -} | |
| 19 | - | |
| 20 | -listener { | |
| 21 | - timeout=300 | |
| 22 | - on-timeout=hyprctl dispatch 'hl.dsp.dpms({ action = "off" })' | |
| 23 | - ignore_inhibit=true | |
| 24 | -} |
deletedconfig/hypr/hyprland.lua
@@ -1,343 +1,0 @@ | ||
| 1 | --- Generated by hyprlang2lua. Review TODOs before reloading Hyprland. | |
| 2 | - | |
| 3 | -local mod = "SUPER" | |
| 4 | - | |
| 5 | ---GENERAL# | |
| 6 | - | |
| 7 | ---Monitor settings | |
| 8 | -hl.monitor({ | |
| 9 | - output = "HDMI-A-2", | |
| 10 | - mode = "2560x1440@280", | |
| 11 | - position = "0x0", | |
| 12 | - scale = 1, | |
| 13 | - vrr = 0, | |
| 14 | -}) | |
| 15 | - | |
| 16 | -hl.monitor({ | |
| 17 | - output = "DP-1", | |
| 18 | - mode = "2560x1440@144", | |
| 19 | - position = "2560x-900", | |
| 20 | - scale = 1, | |
| 21 | - transform = 1, | |
| 22 | - vrr = 0, | |
| 23 | -}) | |
| 24 | - | |
| 25 | ---monitor=DP-1,disable | |
| 26 | - | |
| 27 | --- WS on HDMI-A-2 | |
| 28 | -hl.workspace_rule({ | |
| 29 | - workspace = "1", | |
| 30 | - monitor = "HDMI-A-2", | |
| 31 | - persistent = true, | |
| 32 | -}) | |
| 33 | - | |
| 34 | -hl.workspace_rule({ | |
| 35 | - workspace = "2", | |
| 36 | - monitor = "HDMI-A-2", | |
| 37 | - persistent = true, | |
| 38 | -}) | |
| 39 | - | |
| 40 | --- WS on DP-1 | |
| 41 | -hl.workspace_rule({ | |
| 42 | - workspace = "3", | |
| 43 | - monitor = "DP-1", | |
| 44 | - persistent = true, | |
| 45 | -}) | |
| 46 | - | |
| 47 | -hl.workspace_rule({ | |
| 48 | - workspace = "4", | |
| 49 | - monitor = "DP-1", | |
| 50 | - persistent = true, | |
| 51 | -}) | |
| 52 | - | |
| 53 | -hl.workspace_rule({ | |
| 54 | - workspace = "5", | |
| 55 | - monitor = "DP-1", | |
| 56 | - persistent = true, | |
| 57 | -}) | |
| 58 | - | |
| 59 | -hl.window_rule({ | |
| 60 | - name = "windowrule-1", | |
| 61 | - match = { | |
| 62 | - title = "^RuneLite.*", | |
| 63 | - }, | |
| 64 | - float = true, | |
| 65 | -}) | |
| 66 | - | |
| 67 | -hl.window_rule({ | |
| 68 | - name = "windowrule-2", | |
| 69 | - match = { | |
| 70 | - class = "net-runelite-client-RuneLite", | |
| 71 | - title = "win.*", | |
| 72 | - }, | |
| 73 | - no_focus = true, | |
| 74 | -}) | |
| 75 | - | |
| 76 | -hl.window_rule({ | |
| 77 | - name = "windowrule-3", | |
| 78 | - match = { | |
| 79 | - class = ".*", | |
| 80 | - }, | |
| 81 | - immediate = true, | |
| 82 | -}) | |
| 83 | - | |
| 84 | --- Battle.net (Proton via Faugus). The class is shared by every umu game without | |
| 85 | --- its own ID, so match on the title too. | |
| 86 | -hl.window_rule({ | |
| 87 | - name = "battlenet-float", | |
| 88 | - match = { | |
| 89 | - class = "^steam_app_default$", | |
| 90 | - title = "^Battle\\.net.*", | |
| 91 | - }, | |
| 92 | - float = true, | |
| 93 | -}) | |
| 94 | - | |
| 95 | --- xembedsniproxy (pkgs/xembedsniproxy) puts Wine/Proton tray icons such as | |
| 96 | --- Battle.net into Waybar's tray. It keeps a small helper window mapped for the | |
| 97 | --- icons it hosts, which Hyprland would otherwise show as a floating window. | |
| 98 | -hl.window_rule({ | |
| 99 | - name = "xembedsniproxy", | |
| 100 | - match = { | |
| 101 | - class = "^xembedsniproxy$", | |
| 102 | - }, | |
| 103 | - opacity = "0.0 override 0.0 override", | |
| 104 | - border_size = 0, | |
| 105 | - no_blur = true, | |
| 106 | - no_shadow = true, | |
| 107 | - no_anim = true, | |
| 108 | - no_focus = true, | |
| 109 | - no_initial_focus = true, | |
| 110 | -}) | |
| 111 | - | |
| 112 | --- Dolphin transparency now comes from Kvantum's own translucent_windows | |
| 113 | --- (see kvantum/RiceDark.kvconfig) rather than a forced window opacity here: | |
| 114 | --- Kvantum punches real per-pixel alpha into the background only, so text and | |
| 115 | --- icons stay fully opaque and legible, same mechanism as kitty (which also | |
| 116 | --- has no windowrule here) -- decoration.blur is already global+enabled above | |
| 117 | --- with ignore_opacity defaulting true, so it blurs through automatically. | |
| 118 | --- If Dolphin still looks flat/opaque after applying the Kvantum theme, that | |
| 119 | --- rule can come back as a fallback: | |
| 120 | --- hl.window_rule({ | |
| 121 | --- name = "windowrule-4", | |
| 122 | --- match = { class = "^(org.kde.dolphin)$" }, | |
| 123 | --- opacity = "0.90 0.80", | |
| 124 | --- }) | |
| 125 | - | |
| 126 | --- Claude's desktop app creates its window opaque (transparent: false in the | |
| 127 | --- Electron bundle) and has no user stylesheet, so unlike kitty and Dolphin it | |
| 128 | --- can't put alpha in the background only. A forced window opacity is the | |
| 129 | --- closest it gets: decoration.blur frosts what shows through, at the cost of | |
| 130 | --- the text being translucent too. | |
| 131 | -hl.window_rule({ | |
| 132 | - name = "claude-frosted", | |
| 133 | - match = { class = "^com\\.anthropic\\.Claude$" }, | |
| 134 | - opacity = "0.80 0.80", | |
| 135 | -}) | |
| 136 | - | |
| 137 | ---windowrulev2 = maxsize 1803 1153,title:^(RuneLite)$ | |
| 138 | ---windowrulev2 = maxsize 1803 1153,title:^(RuneLite)$ | |
| 139 | - | |
| 140 | -hl.layer_rule({ | |
| 141 | - name = "layerrule-1", | |
| 142 | - match = { | |
| 143 | - namespace = "waybar", | |
| 144 | - }, | |
| 145 | - blur = true, | |
| 146 | - ignore_alpha = 0, | |
| 147 | - blur_popups = true, | |
| 148 | -}) | |
| 149 | - | |
| 150 | -hl.layer_rule({ | |
| 151 | - name = "layerrule-2", | |
| 152 | - match = { | |
| 153 | - namespace = "launcher", -- fuzzel | |
| 154 | - }, | |
| 155 | - ignore_alpha = 0, | |
| 156 | - blur = true, | |
| 157 | -}) | |
| 158 | - | |
| 159 | -hl.layer_rule({ | |
| 160 | - name = "layerrule-3", | |
| 161 | - match = { | |
| 162 | - namespace = "notifications", | |
| 163 | - }, | |
| 164 | - ignore_alpha = 0, | |
| 165 | - blur = true, | |
| 166 | -}) | |
| 167 | - | |
| 168 | -hl.layer_rule({ | |
| 169 | - name = "layerrule-4", | |
| 170 | - match = { | |
| 171 | - namespace = "eww-control-center", | |
| 172 | - }, | |
| 173 | - ignore_alpha = 0, | |
| 174 | - blur = true, | |
| 175 | -}) | |
| 176 | - | |
| 177 | -hl.bind(mod .. " + SHIFT + S", hl.dsp.exec_cmd("imsh-shot area -Cufa@~/.config/keys/scrnis -s1")) | |
| 178 | -hl.bind(mod .. " + SHIFT + F", hl.dsp.exec_cmd("imsh-shot screen -Cua@~/.config/keys/scrnis")) | |
| 179 | -hl.bind(mod .. " + SHIFT + R", hl.dsp.exec_cmd("imsh-cast -Cua@~/.config/keys/scrnis")) | |
| 180 | -hl.bind(mod .. " + Return", hl.dsp.exec_cmd("~/.config/fuzzel/launch")) | |
| 181 | -hl.bind(mod .. " + T", hl.dsp.exec_cmd("kitty")) | |
| 182 | -hl.bind(mod .. " + W", hl.dsp.exec_cmd("firefox")) | |
| 183 | -hl.bind(mod .. " + Q", hl.dsp.window.close()) | |
| 184 | -hl.bind(mod .. " + F", hl.dsp.window.float({ action = "toggle" })) | |
| 185 | -hl.bind(mod .. " + G", hl.dsp.window.fullscreen({ mode = "fullscreen", action = "toggle" })) | |
| 186 | -hl.bind(mod .. " + mouse_down", hl.dsp.focus({ workspace = "m+1" })) | |
| 187 | -hl.bind(mod .. " + mouse_up", hl.dsp.focus({ workspace = "m-1" })) | |
| 188 | -hl.bind(mod .. " + mouse:272", hl.dsp.window.drag()) | |
| 189 | -hl.bind(mod .. " + mouse:273", hl.dsp.window.resize()) | |
| 190 | -hl.bind(mod .. " + Delete", hl.dsp.exit()) | |
| 191 | - | |
| 192 | -hl.env("LIBVA_DRIVER_NAME", "nvidia") | |
| 193 | -hl.env("NVD_BACKEND", "direct") -- libva-nvidia-driver backend (Firefox hardware video decoding) | |
| 194 | -hl.env("MOZ_DISABLE_RDD_SANDBOX", "1") -- let Firefox's video decoder process reach the NVIDIA driver | |
| 195 | -hl.env("XDG_SESSION_TYPE", "wayland") | |
| 196 | -hl.env("GBM_BACKEND", "nvidia-drm") | |
| 197 | -hl.env("__GLX_VENDOR_LIBRARY_NAME", "nvidia") | |
| 198 | -hl.env("XCURSOR_THEME", "BreezeX-Black") | |
| 199 | -hl.env("XCURSOR_SIZE", "28") | |
| 200 | -hl.env("QT_QPA_PLATFORMTHEME", "qt6ct") | |
| 201 | - | |
| 202 | -hl.config({ | |
| 203 | - general = { | |
| 204 | - border_size = 1, | |
| 205 | - gaps_in = 5, | |
| 206 | - gaps_out = 17, | |
| 207 | - layout = "dwindle", | |
| 208 | - col = { | |
| 209 | - active_border = "rgba(00000000)", | |
| 210 | - inactive_border = "rgba(00000000)", | |
| 211 | - }, | |
| 212 | - allow_tearing = true, | |
| 213 | - }, | |
| 214 | - decoration = { | |
| 215 | - rounding = 10, | |
| 216 | - -- No visible borders; the focused window is the one that isn't dimmed. | |
| 217 | - dim_inactive = true, | |
| 218 | - dim_strength = 0.15, | |
| 219 | - blur = { | |
| 220 | - enabled = true, | |
| 221 | - size = 9, | |
| 222 | - passes = 3, | |
| 223 | - new_optimizations = true, | |
| 224 | - popups = true, -- blur tooltips/context menus (Kvantum's popup_blurring is on for this) | |
| 225 | - }, | |
| 226 | - shadow = { | |
| 227 | - enabled = false, | |
| 228 | - render_power = 3, | |
| 229 | - range = 4, | |
| 230 | - color = "rgba(1a1a1aee)", | |
| 231 | - }, | |
| 232 | - }, | |
| 233 | - input = { | |
| 234 | - kb_layout = "no", | |
| 235 | - follow_mouse = 1, | |
| 236 | - sensitivity = 0, | |
| 237 | - accel_profile = "flat", | |
| 238 | - }, | |
| 239 | - --MISC# | |
| 240 | - misc = { | |
| 241 | - disable_hyprland_logo = true, | |
| 242 | - disable_splash_rendering = true, | |
| 243 | - -- Wallpaper's average colour instead of black, so the moment between the | |
| 244 | - -- login screen and hyprpaper drawing the wallpaper doesn't flash black. | |
| 245 | - background_color = "rgb(121d1a)", | |
| 246 | - }, | |
| 247 | - ecosystem = { | |
| 248 | - no_donation_nag = true, | |
| 249 | - }, | |
| 250 | - --BINDS# | |
| 251 | - binds = { | |
| 252 | - scroll_event_delay = 10, | |
| 253 | - }, | |
| 254 | - --EXEC# | |
| 255 | - --ENVIROMENT# | |
| 256 | - --CURSOR# | |
| 257 | - cursor = { | |
| 258 | - -- Software cursor, for tearing. Hyprland (0.56) refuses to tear while a | |
| 259 | - -- hardware cursor is visible, because the kernel can't update the cursor | |
| 260 | - -- plane during a tearing flip. So with a hardware cursor a game only tears | |
| 261 | - -- while it hides the pointer (e.g. mouselook) and is vsynced otherwise, and | |
| 262 | - -- switching back and forth left WoW's cursor shape stale. With a software | |
| 263 | - -- cursor the game tears all the time; the price is that frames are | |
| 264 | - -- composited (one extra copy) while the pointer is visible. | |
| 265 | - no_hardware_cursors = true, | |
| 266 | - }, | |
| 267 | - render = { | |
| 268 | - -- Fullscreen games (e.g. WoW) scan out straight to the monitor instead of | |
| 269 | - -- being copied by the compositor. With the software cursor above this only | |
| 270 | - -- applies while the game hides the pointer. | |
| 271 | - -- 1 = always; XWayland games don't set a content type, so 2 (auto) won't trigger. | |
| 272 | - direct_scanout = 1, | |
| 273 | - }, | |
| 274 | -}) | |
| 275 | - | |
| 276 | -hl.on("hyprland.start", function() | |
| 277 | - -- Wallpaper, bar, notifications, the control centre, tray helpers, the | |
| 278 | - -- clipboard bridge and the polkit agent are systemd user services now | |
| 279 | - -- (modules/home/services.nix in the dot repo): uwsm starts them with | |
| 280 | - -- graphical-session.target, restarts them if they crash, and | |
| 281 | - -- `systemctl --user status <name>` / `journalctl --user -u <name>` show them. | |
| 282 | - | |
| 283 | - -- Apps opened at login, in their usual places (scripts/autostart-apps starts | |
| 284 | - -- them, from autostart-apps.service). These placement rules are switched off | |
| 285 | - -- again after two minutes, so opening the same apps by hand later behaves like | |
| 286 | - -- any other window. | |
| 287 | - local startup_rules = { | |
| 288 | - hl.window_rule({ | |
| 289 | - name = "startup-signal", | |
| 290 | - match = { class = "^signal$" }, | |
| 291 | - workspace = "3 silent", | |
| 292 | - }), | |
| 293 | - hl.window_rule({ | |
| 294 | - name = "startup-discord", | |
| 295 | - match = { class = "^discord$" }, | |
| 296 | - workspace = "3 silent", | |
| 297 | - }), | |
| 298 | - hl.window_rule({ | |
| 299 | - name = "startup-faugus", | |
| 300 | - match = { class = "^io\\.github\\.Faugus\\.faugus-launcher$" }, | |
| 301 | - workspace = "1 silent", | |
| 302 | - float = true, | |
| 303 | - size = { 430, 610 }, | |
| 304 | - move = { 1901, 333 }, | |
| 305 | - }), | |
| 306 | - } | |
| 307 | - hl.timer(function() | |
| 308 | - for _, rule in ipairs(startup_rules) do | |
| 309 | - rule:set_enabled(false) | |
| 310 | - end | |
| 311 | - end, { timeout = 120000, type = "oneshot" }) | |
| 312 | - | |
| 313 | - -- WowUp can't start hidden, so the window it opens at login is sent to a hidden | |
| 314 | - -- workspace and closed again, which leaves it in the tray. That rule must not | |
| 315 | - -- outlive the one window: it is dropped as soon as the window has opened (or | |
| 316 | - -- after a minute if none came), so opening WowUp yourself right after login | |
| 317 | - -- lands on your workspace like any other window. | |
| 318 | - local wowup_rule = hl.window_rule({ | |
| 319 | - name = "startup-wowup", | |
| 320 | - match = { class = "^WowUpCf$" }, | |
| 321 | - workspace = "special:autostart silent", | |
| 322 | - }) | |
| 323 | - local wowup_pending = true | |
| 324 | - local | |
| 325 | - wowup_pending = false | |
| 326 | - wowup_rule:set_enabled(false) | |
| 327 | - end | |
| 328 | - hl.on("window.open", function(window) | |
| 329 | - if wowup_pending and window.class == "WowUpCf" then | |
| 330 | - wowup_done() | |
| 331 | - -- closing it from inside the open event is too early and gets ignored | |
| 332 | - local address = window.address | |
| 333 | - hl.timer(function() | |
| 334 | - hl.dispatch(hl.dsp.window.close({ window = "address:" .. address })) | |
| 335 | - end, { timeout = 300, type = "oneshot" }) | |
| 336 | - end | |
| 337 | - end) | |
| 338 | - hl.timer(wowup_done, { timeout = 60000, type = "oneshot" }) | |
| 339 | - | |
| 340 | - -- hl.exec_cmd("gsettings set org.gnome.desktop.interface color-scheme prefer-dark") | |
| 341 | - -- hl.exec_cmd("gsettings set org.gnome.desktop.interface gtk-theme Adwaita-dark") | |
| 342 | -end) | |
| 343 | - |
deletedconfig/hypr/hyprlock.conf
@@ -1,89 +1,0 @@ | ||
| 1 | -# Lock screen, styled to match the greetd login screen (/etc/greetd/style.css): | |
| 2 | -# same bamboo wallpaper, translucent black boxes, #eee text, rounding 10. | |
| 3 | - | |
| 4 | -$font = NotoSans Nerd Font | |
| 5 | -$text = rgb(238, 238, 238) | |
| 6 | -$box = rgba(0, 0, 0, 0.2) | |
| 7 | - | |
| 8 | -general { | |
| 9 | - hide_cursor = false | |
| 10 | - ignore_empty_input = true | |
| 11 | -} | |
| 12 | - | |
| 13 | -animations { | |
| 14 | - enabled = true | |
| 15 | - bezier = ease, 0.25, 0.1, 0.25, 1 | |
| 16 | - animation = fadeIn, 1, 4, ease | |
| 17 | - animation = fadeOut, 1, 4, ease | |
| 18 | - animation = inputFieldDots, 1, 2, ease | |
| 19 | -} | |
| 20 | - | |
| 21 | -background { | |
| 22 | - monitor = | |
| 23 | - path = ~/.config/hypr/wallpaper.jpg | |
| 24 | - # Light blur + dim so the text stays readable over the leaves, | |
| 25 | - # like the blurred panel behind the login box. | |
| 26 | - blur_passes = 2 | |
| 27 | - blur_size = 5 | |
| 28 | - brightness = 0.75 | |
| 29 | -} | |
| 30 | - | |
| 31 | -# Clock | |
| 32 | -label { | |
| 33 | - monitor = HDMI-A-2 | |
| 34 | - text = <span font_weight="light">$TIME</span> | |
| 35 | - color = $text | |
| 36 | - font_family = $font | |
| 37 | - font_size = 110 | |
| 38 | - position = 0, 170 | |
| 39 | - halign = center | |
| 40 | - valign = center | |
| 41 | -} | |
| 42 | - | |
| 43 | -# Date | |
| 44 | -label { | |
| 45 | - monitor = HDMI-A-2 | |
| 46 | - text = cmd[update:60000] date +"%A %-d %B" | |
| 47 | - color = rgba(238, 238, 238, 0.75) | |
| 48 | - font_family = $font | |
| 49 | - font_size = 18 | |
| 50 | - position = 0, 70 | |
| 51 | - halign = center | |
| 52 | - valign = center | |
| 53 | -} | |
| 54 | - | |
| 55 | -# Password box | |
| 56 | -input-field { | |
| 57 | - monitor = HDMI-A-2 | |
| 58 | - size = 320, 50 | |
| 59 | - rounding = 10 | |
| 60 | - outline_thickness = 0 | |
| 61 | - inner_color = $box | |
| 62 | - outer_color = rgba(0, 0, 0, 0) | |
| 63 | - check_color = rgba(170, 255, 255, 0.5) | |
| 64 | - fail_color = rgba(255, 107, 107, 0.6) | |
| 65 | - capslock_color = rgba(244, 214, 159, 0.5) | |
| 66 | - font_color = $text | |
| 67 | - font_family = $font | |
| 68 | - fade_on_empty = false | |
| 69 | - placeholder_text = <span foreground="##eeeeeeaa">Password</span> | |
| 70 | - fail_text = <span foreground="##eeeeee">$FAIL ($ATTEMPTS)</span> | |
| 71 | - dots_size = 0.22 | |
| 72 | - dots_spacing = 0.4 | |
| 73 | - dots_center = true | |
| 74 | - position = 0, -60 | |
| 75 | - halign = center | |
| 76 | - valign = center | |
| 77 | -} | |
| 78 | - | |
| 79 | -# User | |
| 80 | -label { | |
| 81 | - monitor = HDMI-A-2 | |
| 82 | - text = $USER | |
| 83 | - color = $text | |
| 84 | - font_family = $font | |
| 85 | - font_size = 15 | |
| 86 | - position = 0, 0 | |
| 87 | - halign = center | |
| 88 | - valign = center | |
| 89 | -} |
deletedconfig/hypr/hyprpaper.conf
@@ -1,7 +1,0 @@ | ||
| 1 | -wallpaper { | |
| 2 | - monitor = | |
| 3 | - path = /home/vizie/Pictures/Wallpapers | |
| 4 | - timeout = 30 | |
| 5 | -} | |
| 6 | - | |
| 7 | -splash = false |
@@ -7,7 +7,7 @@ | ||
| 7 | 7 | # Signal, Discord workspace 3, Signal on top and Discord below it |
| 8 | 8 | # |
| 9 | 9 | # Which workspace a window lands on (and Faugus' position) is decided by the |
| 10 | -# temporary "startup-*" window rules in hyprland.lua, which also closes the window | |
| 10 | +# temporary "startup-*" window rules in startup.lua, which also closes the window | |
| 11 | 11 | # WowUp opens. This script starts the apps and then stacks Signal |
| 12 | 12 | # above Discord. Apps that are already running are left alone, so it is safe to run |
| 13 | 13 | # again by hand. |
@@ -29,7 +29,7 @@ | ||
| 29 | 29 | done |
| 30 | 30 | |
| 31 | 31 | # OpenDeck starts hidden with its flag. WowUp has one too but shows its window |
| 32 | -# anyway; hyprland.lua closes that window (see "startup-wowup" there). | |
| 32 | +# anyway; startup.lua closes that window (see "startup-wowup" there). | |
| 33 | 33 | running '(^|/)\.?opendeck(-wrapped)? ' || launch opendeck --hide |
| 34 | 34 | running 'wowup-cf' || launch wowup-cf --hidden |
| 35 | 35 | running 'faugus\.(launcher|tray_only)' || launch faugus-launcher |
@@ -1,0 +1,71 @@ | ||
| 1 | +-- Run once when Hyprland starts. Loaded from the hyprland.lua that Home Manager | |
| 2 | +-- writes (modules/home/desktop.nix has the rest of the Hyprland config). | |
| 3 | + | |
| 4 | +hl.on("hyprland.start", function() | |
| 5 | + -- Wallpaper, bar, notifications, the control centre, tray helpers, the | |
| 6 | + -- clipboard bridge and the polkit agent are systemd user services now | |
| 7 | + -- (modules/home/services.nix in the dot repo): uwsm starts them with | |
| 8 | + -- graphical-session.target, restarts them if they crash, and | |
| 9 | + -- `systemctl --user status <name>` / `journalctl --user -u <name>` show them. | |
| 10 | + | |
| 11 | + -- Apps opened at login, in their usual places (scripts/autostart-apps starts | |
| 12 | + -- them, from autostart-apps.service). These placement rules are switched off | |
| 13 | + -- again after two minutes, so opening the same apps by hand later behaves like | |
| 14 | + -- any other window. | |
| 15 | + local startup_rules = { | |
| 16 | + hl.window_rule({ | |
| 17 | + name = "startup-signal", | |
| 18 | + match = { class = "^signal$" }, | |
| 19 | + workspace = "3 silent", | |
| 20 | + }), | |
| 21 | + hl.window_rule({ | |
| 22 | + name = "startup-discord", | |
| 23 | + match = { class = "^discord$" }, | |
| 24 | + workspace = "3 silent", | |
| 25 | + }), | |
| 26 | + hl.window_rule({ | |
| 27 | + name = "startup-faugus", | |
| 28 | + match = { class = "^io\\.github\\.Faugus\\.faugus-launcher$" }, | |
| 29 | + workspace = "1 silent", | |
| 30 | + float = true, | |
| 31 | + size = { 430, 610 }, | |
| 32 | + move = { 1901, 333 }, | |
| 33 | + }), | |
| 34 | + } | |
| 35 | + hl.timer(function() | |
| 36 | + for _, rule in ipairs(startup_rules) do | |
| 37 | + rule:set_enabled(false) | |
| 38 | + end | |
| 39 | + end, { timeout = 120000, type = "oneshot" }) | |
| 40 | + | |
| 41 | + -- WowUp can't start hidden, so the window it opens at login is sent to a hidden | |
| 42 | + -- workspace and closed again, which leaves it in the tray. That rule must not | |
| 43 | + -- outlive the one window: it is dropped as soon as the window has opened (or | |
| 44 | + -- after a minute if none came), so opening WowUp yourself right after login | |
| 45 | + -- lands on your workspace like any other window. | |
| 46 | + local wowup_rule = hl.window_rule({ | |
| 47 | + name = "startup-wowup", | |
| 48 | + match = { class = "^WowUpCf$" }, | |
| 49 | + workspace = "special:autostart silent", | |
| 50 | + }) | |
| 51 | + local wowup_pending = true | |
| 52 | + local | |
| 53 | + wowup_pending = false | |
| 54 | + wowup_rule:set_enabled(false) | |
| 55 | + end | |
| 56 | + hl.on("window.open", function(window) | |
| 57 | + if wowup_pending and window.class == "WowUpCf" then | |
| 58 | + wowup_done() | |
| 59 | + -- closing it from inside the open event is too early and gets ignored | |
| 60 | + local address = window.address | |
| 61 | + hl.timer(function() | |
| 62 | + hl.dispatch(hl.dsp.window.close({ window = "address:" .. address })) | |
| 63 | + end, { timeout = 300, type = "oneshot" }) | |
| 64 | + end | |
| 65 | + end) | |
| 66 | + hl.timer(wowup_done, { timeout = 60000, type = "oneshot" }) | |
| 67 | + | |
| 68 | + -- hl.exec_cmd("gsettings set org.gnome.desktop.interface color-scheme prefer-dark") | |
| 69 | + -- hl.exec_cmd("gsettings set org.gnome.desktop.interface gtk-theme Adwaita-dark") | |
| 70 | +end) | |
| 71 | + |
deletedconfig/kde/kdeglobals
@@ -1,8 +1,0 @@ | ||
| 1 | -[General] | |
| 2 | -widgetStyle=kvantum | |
| 3 | - | |
| 4 | -[Icons] | |
| 5 | -Theme=Papirus-Dark | |
| 6 | - | |
| 7 | -[KDE] | |
| 8 | -widgetStyle=kvantum |
deletedconfig/kitty/kitty.conf
@@ -1,29 +1,0 @@ | ||
| 1 | -background_opacity 0 | |
| 2 | -font_size 12 | |
| 3 | -font_family JetBrainsMono Nerd Font | |
| 4 | -bold_font JetBrains Mono Bold | |
| 5 | -italic_font JetBrains Mono Italic | |
| 6 | -bold_italic_font JetBrains Mono Bold Italic | |
| 7 | -disable_ligatures never | |
| 8 | -color0 #292d3e | |
| 9 | -color1 #ff8288 | |
| 10 | -color2 #b4e88d | |
| 11 | -color3 #f4d69f | |
| 12 | -color4 #82aaff | |
| 13 | -color5 #e9c1ff | |
| 14 | -color6 #89ebff | |
| 15 | -color7 #d0d0d0 | |
| 16 | -color8 #434758 | |
| 17 | -color9 #ff8b92 | |
| 18 | -color10 #ddffa7 | |
| 19 | -color11 #ffe585 | |
| 20 | -color12 #9cc4ff | |
| 21 | -color13 #ddb0f6 | |
| 22 | -color14 #a3f7ff | |
| 23 | -color15 #ffffff | |
| 24 | -window_padding_width 10 | |
| 25 | -term xterm-256color | |
| 26 | -# Do not restore the last window size or maximized state on startup; the tiling | |
| 27 | -# window manager decides the size. Otherwise one kitty closed while maximized makes | |
| 28 | -# every new one open maximized. | |
| 29 | -remember_window_size no |
modifiedconfig/kvantum/RiceDark.kvconfig
@@ -1,5 +1,5 @@ | ||
| 1 | 1 | # RiceDark — Kvantum's bundled KvArcDark theme, unmodified except for the |
| 2 | -# accent color, changed to match this rig's cyan (see hypr/hyprland.lua's | |
| 2 | +# accent color, changed to match this rig's cyan (see modules/home/desktop.nix's | |
| 3 | 3 | # active_border rgb(aaffff)). Everything else is copied verbatim from |
| 4 | 4 | # KvArcDark since that's the theme actually confirmed to render correctly |
| 5 | 5 | # in Dolphin -- minimal diff from a known-good file, on purpose. |
deletedconfig/kvantum/kvantum.kvconfig
@@ -1,2 +1,0 @@ | ||
| 1 | -[General] | |
| 2 | -theme=KvArcDark# |
modifiedconfig/philly/theme.css
@@ -2,7 +2,7 @@ | ||
| 2 | 2 | |
| 3 | 3 | |
| 4 | 4 | |
| 5 | -/ | |
| 5 | +//) | |
| 6 | 6 | |
| 7 | 7 | |
| 8 | 8 | |
deletedconfig/qt6ct/qt6ct.conf
@@ -1,27 +1,0 @@ | ||
| 1 | -[Appearance] | |
| 2 | -custom_palette=false | |
| 3 | -icon_theme=Papirus-Dark | |
| 4 | -standard_dialogs=default | |
| 5 | -style=kvantum | |
| 6 | - | |
| 7 | -[Fonts] | |
| 8 | -fixed="JetBrainsMono Nerd Font,10,-1,5,50,0,0,0,0,0" | |
| 9 | -general="Noto Sans,10,-1,5,50,0,0,0,0,0" | |
| 10 | - | |
| 11 | -[Interface] | |
| 12 | -activate_item_on_single_click=1 | |
| 13 | -buttonbox_layout=0 | |
| 14 | -cursor_flash_time=1000 | |
| 15 | -dialog_buttons_have_icons=1 | |
| 16 | -double_click_interval=400 | |
| 17 | -gui_effects=@Invalid() | |
| 18 | -keyboard_scheme=2 | |
| 19 | -menus_have_icons=true | |
| 20 | -show_shortcuts_in_context_menus=true | |
| 21 | -stylesheets=@Invalid() | |
| 22 | -toolbutton_style=4 | |
| 23 | -underline_shortcut=1 | |
| 24 | -wheel_scroll_lines=3 | |
| 25 | - | |
| 26 | -[SettingsWindow] | |
| 27 | -geometry=@ByteArray(\x1\xd9\xd0\xcb\0\x3\0\0\0\0\0\0\0\0\0\0\0\0\x4_\0\0\x5\x46\0\0\n\0\xff\xff\xfc|\0\0\vU\xff\xff\xfe\t\0\0\0\x1\x2\0\0\0\n\0\0\0\0\0\0\0\0\0\0\0\x4_\0\0\x5\x46) |
modifiedconfig/signal/theme.css
@@ -4,7 +4,7 @@ | ||
| 4 | 4 | |
| 5 | 5 | / |
| 6 | 6 | / |
| 7 | -/ | |
| 7 | +//) | |
| 8 | 8 | |
| 9 | 9 | |
| 10 | 10 | |
deletedconfig/waybar/config
@@ -1,94 +1,0 @@ | ||
| 1 | -[ | |
| 2 | - { | |
| 3 | - "clock": { | |
| 4 | - "format": "{:%H:%M}", | |
| 5 | - "format-alt": "{:%A, %B %d, %Y (%R)}", | |
| 6 | - "tooltip-format": "<tt><big>{calendar}</big></tt>", | |
| 7 | - "calendar": { | |
| 8 | - "mode" : "month", | |
| 9 | - "iso8601" : true, | |
| 10 | - "mode-mon-col" : 3, | |
| 11 | - "weeks-pos" : "right", | |
| 12 | - "on-scroll" : 1, | |
| 13 | - "format": { | |
| 14 | - "months": "<span color='#ffffff'><b>{}</b></span>", | |
| 15 | - "days": "<span color='#ffffff'>{}</span>", | |
| 16 | - "weeks": "<span color='#8a8a8a'>W{:%V}</span>", | |
| 17 | - "weekdays": "<span color='#b0b0b0'><b>{}</b></span>", | |
| 18 | - "today": "<span color='#aaffff'><b><u>{}</u></b></span>" | |
| 19 | - } | |
| 20 | - }, | |
| 21 | - "actions": { | |
| 22 | - "on-click-right": "mode", | |
| 23 | - "on-scroll-up": "shift_up", | |
| 24 | - "on-scroll-down": "shift_down" | |
| 25 | - } | |
| 26 | - }, | |
| 27 | - "custom/notifications": { | |
| 28 | - "format": "<span size='12288'></span>", | |
| 29 | - "tooltip": false, | |
| 30 | - "on-click": "bash $HOME/.config/waybar/eww/scripts/toggle.sh notifications" | |
| 31 | - }, | |
| 32 | - "custom/notifications-count": { | |
| 33 | - "exec": "python3 $HOME/.config/waybar/scripts/notification-count.py", | |
| 34 | - "return-type": "json", | |
| 35 | - "hide-empty-text": true, | |
| 36 | - "tooltip": false, | |
| 37 | - "on-click": "bash $HOME/.config/waybar/eww/scripts/toggle.sh notifications" | |
| 38 | - }, | |
| 39 | - "custom/control-center": { | |
| 40 | - "format": " ", | |
| 41 | - "tooltip": false, | |
| 42 | - "on-click": "bash $HOME/.config/waybar/eww/scripts/toggle.sh" | |
| 43 | - }, | |
| 44 | - "tray": { | |
| 45 | - "icon-size": 16, | |
| 46 | - "spacing": 10, | |
| 47 | - "order": ["discord_status_icon_1"], | |
| 48 | - "icons": { | |
| 49 | - "Signal_status_icon_1": "/home/vizie/.config/waybar/icons/signal.svg", | |
| 50 | - "Bitwarden_status_icon_1": "/home/vizie/.config/waybar/icons/bitwarden.svg", | |
| 51 | - "WowUpCf_status_icon_1": "/home/vizie/.config/waybar/icons/wowup.svg", | |
| 52 | - "Claude_status_icon_1": "/home/vizie/.config/waybar/icons/claude.svg", | |
| 53 | - "steam_app_default": "/home/vizie/.config/waybar/icons/battlenet.svg", | |
| 54 | - "faugus-launcher": "/home/vizie/.config/waybar/icons/faugus.svg", | |
| 55 | - "steam": "/home/vizie/.config/waybar/icons/steam.svg", | |
| 56 | - "tray-icon tray app opendeck": "/home/vizie/.config/waybar/icons/opendeck.svg" | |
| 57 | - } | |
| 58 | - }, | |
| 59 | - "custom/imsh-cast-monitor": { | |
| 60 | - "format": "{text}", | |
| 61 | - "exec": "imsh-cast-monitor", | |
| 62 | - "return-type": "json", | |
| 63 | - "hide-empty-text": true | |
| 64 | - }, | |
| 65 | - "height": 36, | |
| 66 | - "hyprland/window": { | |
| 67 | - "max-length": 60 | |
| 68 | - }, | |
| 69 | - "hyprland/workspaces": { | |
| 70 | - "format": "{name}", | |
| 71 | - "on-click": "activate", | |
| 72 | - "sort-by": "number" | |
| 73 | - }, | |
| 74 | - "layer": "top", | |
| 75 | - "modules-center": [ | |
| 76 | - "custom/imsh-cast-monitor", | |
| 77 | - "clock" | |
| 78 | - ], | |
| 79 | - "modules-left": [ | |
| 80 | - "hyprland/workspaces", | |
| 81 | - "hyprland/window" | |
| 82 | - ], | |
| 83 | - "modules-right": [ | |
| 84 | - "tray", | |
| 85 | - "custom/notifications", | |
| 86 | - "custom/notifications-count", | |
| 87 | - "custom/control-center" | |
| 88 | - ], | |
| 89 | - "position": "top", | |
| 90 | - "margin-left": 15, | |
| 91 | - "margin-right": 15, | |
| 92 | - "margin-top": 15 | |
| 93 | - } | |
| 94 | -] |
modifiedconfig/waybar/eww/eww.scss
@@ -22,7 +22,7 @@ | ||
| 22 | 22 | |
| 23 | 23 | .cc { |
| 24 | 24 | // Same near-transparent black as #waybar / fuzzel's background; the |
| 25 | - // compositor's own blur (layerrule-4 in hyprland.lua) does the rest, no | |
| 25 | + // compositor's own blur (layerrule-4 in modules/home/desktop.nix) does the rest, no | |
| 26 | 26 | // border, same as the rest of the rice. |
| 27 | 27 | background-color: rgba(0, 0, 0, 0.1); |
| 28 | 28 | border-radius: 20px; |
deletedconfig/waybar/style.css
@@ -1,114 +1,0 @@ | ||
| 1 | - | |
| 2 | - | |
| 3 | - | |
| 4 | - | |
| 5 | - | |
| 6 | - | |
| 7 | - | |
| 8 | - | |
| 9 | - | |
| 10 | -} | |
| 11 | - | |
| 12 | - | |
| 13 | - | |
| 14 | -} | |
| 15 | - | |
| 16 | - | |
| 17 | - | |
| 18 | -} | |
| 19 | - | |
| 20 | - | |
| 21 | - | |
| 22 | -} | |
| 23 | - | |
| 24 | - | |
| 25 | - | |
| 26 | -} | |
| 27 | - | |
| 28 | - | |
| 29 | - | |
| 30 | -} | |
| 31 | - | |
| 32 | -/* Workspace buttons are pills inset 4px from the bar's edge, so their 13px radius | |
| 33 | - is concentric with the bar's 17px one. Everything GTK's theme draws on a button | |
| 34 | - (border, shadows, hover gradient) is reset here, as it otherwise shows up as a | |
| 35 | - square box on hover. */ | |
| 36 | - | |
| 37 | - | |
| 38 | - | |
| 39 | - | |
| 40 | - | |
| 41 | - | |
| 42 | - | |
| 43 | - | |
| 44 | - | |
| 45 | - | |
| 46 | - | |
| 47 | - | |
| 48 | - | |
| 49 | - | |
| 50 | - | |
| 51 | - | |
| 52 | -} | |
| 53 | - | |
| 54 | - | |
| 55 | - | |
| 56 | -} | |
| 57 | - | |
| 58 | - | |
| 59 | - | |
| 60 | - | |
| 61 | -} | |
| 62 | - | |
| 63 | - | |
| 64 | - | |
| 65 | -} | |
| 66 | - | |
| 67 | -/* Workspaces with no windows are dimmed, unless it's the one you're on. The colour | |
| 68 | - goes on the label because the `*` rule above gives every label its own white. */ | |
| 69 | - | |
| 70 | - | |
| 71 | -} | |
| 72 | - | |
| 73 | - | |
| 74 | - | |
| 75 | -} | |
| 76 | - | |
| 77 | - | |
| 78 | - | |
| 79 | -} | |
| 80 | - | |
| 81 | -/* Notification count: a small white disc overlapping the bell's top right corner. | |
| 82 | - The margins shrink it from the bar's full height to 12px; the negative left margin | |
| 83 | - pulls it over the bell. Hidden by Waybar when there are no notifications. */ | |
| 84 | - | |
| 85 | - | |
| 86 | - | |
| 87 | - | |
| 88 | - | |
| 89 | - | |
| 90 | - | |
| 91 | - | |
| 92 | -} | |
| 93 | - | |
| 94 | - | |
| 95 | - | |
| 96 | -} | |
| 97 | - | |
| 98 | - | |
| 99 | - | |
| 100 | -} | |
| 101 | - | |
| 102 | -/* Control-center button: your logo as a pre-rendered 18px image. | |
| 103 | - GTK stretches SVG backgrounds as pixels (which makes them blurry), so logo-18.png is | |
| 104 | - rendered at exactly 18px, and logo-36.png is used automatically on 2x-scaled monitors. */ | |
| 105 | - | |
| 106 | - | |
| 107 | - | |
| 108 | - | |
| 109 | - | |
| 110 | - | |
| 111 | - | |
| 112 | - | |
| 113 | - | |
| 114 | -} |
@@ -1,0 +1,151 @@ | ||
| 1 | +# Settings for the apps you use directly: terminal, launcher, editor, browser. | |
| 2 | +{ | |
| 3 | + config, | |
| 4 | + lib, | |
| 5 | + ... | |
| 6 | +}: | |
| 7 | +let | |
| 8 | + inherit (config.lib.dot) link; | |
| 9 | +in | |
| 10 | +{ | |
| 11 | + programs.kitty = { | |
| 12 | + enable = true; | |
| 13 | + settings = { | |
| 14 | + background_opacity = 0; | |
| 15 | + font_size = 12; | |
| 16 | + # JetBrains Mono isn't installed (see fonts.packages in | |
| 17 | + # modules/nixos/desktop.nix), so kitty falls back to Noto Sans Mono. | |
| 18 | + font_family = "JetBrainsMono Nerd Font"; | |
| 19 | + bold_font = "JetBrains Mono Bold"; | |
| 20 | + italic_font = "JetBrains Mono Italic"; | |
| 21 | + bold_italic_font = "JetBrains Mono Bold Italic"; | |
| 22 | + disable_ligatures = "never"; | |
| 23 | + window_padding_width = 10; | |
| 24 | + term = "xterm-256color"; | |
| 25 | + # Do not restore the last window size or maximized state on startup; the | |
| 26 | + # tiling window manager decides the size. Otherwise one kitty closed while | |
| 27 | + # maximized makes every new one open maximized. | |
| 28 | + remember_window_size = false; | |
| 29 | + | |
| 30 | + color0 = "#292d3e"; | |
| 31 | + color1 = "#ff8288"; | |
| 32 | + color2 = "#b4e88d"; | |
| 33 | + color3 = "#f4d69f"; | |
| 34 | + color4 = "#82aaff"; | |
| 35 | + color5 = "#e9c1ff"; | |
| 36 | + color6 = "#89ebff"; | |
| 37 | + color7 = "#d0d0d0"; | |
| 38 | + color8 = "#434758"; | |
| 39 | + color9 = "#ff8b92"; | |
| 40 | + color10 = "#ddffa7"; | |
| 41 | + color11 = "#ffe585"; | |
| 42 | + color12 = "#9cc4ff"; | |
| 43 | + color13 = "#ddb0f6"; | |
| 44 | + color14 = "#a3f7ff"; | |
| 45 | + color15 = "#ffffff"; | |
| 46 | + }; | |
| 47 | + }; | |
| 48 | + | |
| 49 | + # Hyprland's mod + Return runs config/fuzzel/launch (linked in files.nix), | |
| 50 | + # which puts the apps in config/fuzzel/favorites at the top. | |
| 51 | + programs.fuzzel = { | |
| 52 | + enable = true; | |
| 53 | + settings = { | |
| 54 | + main = { | |
| 55 | + font = "NotoSansM Nerd Font Propo:size=12"; | |
| 56 | + # Size fonts like GTK apps do (96 DPI) rather than by the monitors' real | |
| 57 | + # DPI, which made everything bigger. | |
| 58 | + dpi-aware = "no"; | |
| 59 | + terminal = lib.getExe config.programs.kitty.package; | |
| 60 | + # Same icon theme as Qt/KDE (theming.nix). Without it fuzzel mixes each | |
| 61 | + # app's own icon, in different styles and sizes. | |
| 62 | + icon-theme = config.qt.qt6ctSettings.Appearance.icon_theme; | |
| 63 | + list-executables-in-path = "yes"; | |
| 64 | + placeholder = "Search..."; | |
| 65 | + line-height = 36; | |
| 66 | + lines = 5; | |
| 67 | + width = 45; | |
| 68 | + horizontal-pad = 25; | |
| 69 | + vertical-pad = 25; | |
| 70 | + inner-pad = 10; | |
| 71 | + }; | |
| 72 | + | |
| 73 | + # RRGGBBAA. Same near-transparent black as Waybar and the control centre, | |
| 74 | + # over Hyprland's blur (the "launcher" layer rule). | |
| 75 | + colors = { | |
| 76 | + background = "0000001a"; | |
| 77 | + text = "f2f2f2cc"; | |
| 78 | + prompt = "f2f2f2cc"; | |
| 79 | + input = "f2f2f2ff"; | |
| 80 | + placeholder = "f2f2f2cc"; | |
| 81 | + match = "f4d69fff"; | |
| 82 | + selection = "0000001a"; | |
| 83 | + selection-text = "f2f2f2ff"; | |
| 84 | + selection-match = "f4d69fff"; | |
| 85 | + counter = "f2f2f2cc"; | |
| 86 | + }; | |
| 87 | + | |
| 88 | + # Panel 20, rows inside it 10, like the control centre. | |
| 89 | + border = { | |
| 90 | + width = 0; | |
| 91 | + radius = 20; | |
| 92 | + selection-radius = 10; | |
| 93 | + }; | |
| 94 | + }; | |
| 95 | + }; | |
| 96 | + | |
| 97 | + # config/nvim/init.lua. With liveConfig it is loaded from the checkout, so an | |
| 98 | + # edit applies the next time nvim starts. | |
| 99 | + programs.neovim = { | |
| 100 | + enable = true; | |
| 101 | + withRuby = false; | |
| 102 | + withPython3 = false; | |
| 103 | + initLua = | |
| 104 | + if config.dot.liveConfig then | |
| 105 | + ''dofile("/config/nvim/init.lua")'' | |
| 106 | + else | |
| 107 | + builtins.readFile ../../config/nvim/init.lua; | |
| 108 | + }; | |
| 109 | + | |
| 110 | + # Firefox: the profile restored from CachyOS, kept by its existing folder and | |
| 111 | + # StoreID (its Profile Groups database). Nixpkgs' Firefox runs with | |
| 112 | + # MOZ_LEGACY_PROFILES, so it opens the profile marked default here. Firefox | |
| 113 | + # itself comes from the system (programs.firefox in modules/nixos/apps.nix). | |
| 114 | + programs.firefox = { | |
| 115 | + enable = true; | |
| 116 | + package = null; | |
| 117 | + profiles.default-release = { | |
| 118 | + path = "3b2nokq1.default-release"; | |
| 119 | + storeId = "be5ac646"; | |
| 120 | + # Written to user.js, which Firefox reads at startup. Setting userChrome | |
| 121 | + # also turns on toolkit.legacyUserProfileCustomizations.stylesheets. | |
| 122 | + settings = { | |
| 123 | + # Restore the previous session. | |
| 124 | + "browser.startup.page" = 3; | |
| 125 | + "browser.sessionstore.resume_from_crash" = true; | |
| 126 | + # Middle mouse scroll. | |
| 127 | + "general.autoScroll" = true; | |
| 128 | + "middlemouse.paste" = false; | |
| 129 | + "general.aboutConfig.enable" = true; | |
| 130 | + "sidebar.animation.enabled" = false; | |
| 131 | + # "image.jxl.enabled" = true; | |
| 132 | + | |
| 133 | + # Browser toolbox and devtools. | |
| 134 | + "devtools.chrome.enabled" = true; | |
| 135 | + "devtools.debugger.remote-enabled" = true; | |
| 136 | + "devtools.cache.disabled" = true; | |
| 137 | + "devtools.netmonitor.persistlog" = true; | |
| 138 | + "devtools.webconsole.persistlog" = true; | |
| 139 | + "devtools.webconsole.timestampMessages" = true; | |
| 140 | + | |
| 141 | + # Hardware video decoding on NVIDIA via VA-API (libva-nvidia-driver). | |
| 142 | + # Firefox blocks NVIDIA for this by default; needs NVD_BACKEND and | |
| 143 | + # MOZ_DISABLE_RDD_SANDBOX from Hyprland's env (desktop.nix) as well. | |
| 144 | + "media.hardware-video-decoding.force-enabled" = true; | |
| 145 | + "media.ffmpeg.vaapi.enabled" = true; | |
| 146 | + "widget.dmabuf.force-enabled" = true; | |
| 147 | + }; | |
| 148 | + userChrome = link "firefox/userChrome.css"; | |
| 149 | + }; | |
| 150 | + }; | |
| 151 | +} |
modifiedmodules/home/default.nix
@@ -7,6 +7,8 @@ | ||
| 7 | 7 | { |
| 8 | 8 | imports = [ |
| 9 | 9 | ./files.nix |
| 10 | + ./desktop.nix | |
| 11 | + ./apps.nix | |
| 10 | 12 | ./services.nix |
| 11 | 13 | ./shell.nix |
| 12 | 14 | ./theming.nix |
@@ -24,16 +26,26 @@ | ||
| 24 | 26 | type = lib.types.bool; |
| 25 | 27 | default = true; |
| 26 | 28 | description = '' |
| 27 | - Link the hand-edited configs (hypr, waybar, kitty, ...) straight to the | |
| 28 | - checkout at repoPath, as the old symlink.sh did: an edit takes effect | |
| 29 | - the next time the program reads it, with no rebuild. Off, they are | |
| 30 | - copied into the Nix store and change only on a rebuild (the test VM | |
| 31 | - does this, since it has no checkout). | |
| 29 | + Link the hand-edited configs that no module writes (scripts, Eww, | |
| 30 | + startup.lua, init.lua, stylesheets, themes) straight to the checkout at | |
| 31 | + repoPath, as the old symlink.sh did: an edit takes effect the next time | |
| 32 | + the program reads it, with no rebuild. Off, they are copied into the | |
| 33 | + Nix store and change only on a rebuild (the test VM does this, since it | |
| 34 | + has no checkout). | |
| 32 | 35 | ''; |
| 33 | 36 | }; |
| 34 | 37 | }; |
| 35 | 38 | |
| 36 | 39 | config = { |
| 40 | + # A file or directory of config/: linked to the checkout when liveConfig is | |
| 41 | + # on (edits apply at once), copied into the store otherwise. | |
| 42 | + lib.dot.link = | |
| 43 | + rel: | |
| 44 | + if config.dot.liveConfig then | |
| 45 | + config.lib.file.mkOutOfStoreSymlink "/config/" | |
| 46 | + else | |
| 47 | + ../../config + "/"; | |
| 48 | + | |
| 37 | 49 | home = { |
| 38 | 50 | username = "vizie"; |
| 39 | 51 | homeDirectory = "/home/vizie"; |
@@ -1,0 +1,704 @@ | ||
| 1 | +# The Hyprland desktop: compositor, lock screen, idle and wallpaper daemons, the | |
| 2 | +# bar and notifications. Hyprland and hyprlock themselves come from the NixOS | |
| 3 | +# modules (modules/nixos/desktop.nix); these write their configs. | |
| 4 | +# | |
| 5 | +# The services (hypridle, hyprpaper, waybar, dunst) run with | |
| 6 | +# graphical-session.target, which uwsm starts; services.nix puts them in uwsm's | |
| 7 | +# session slice. | |
| 8 | +{ | |
| 9 | + config, | |
| 10 | + lib, | |
| 11 | + pkgs, | |
| 12 | + ... | |
| 13 | +}: | |
| 14 | +let | |
| 15 | + inherit (config.lib.dot) link; | |
| 16 | + | |
| 17 | + lua = lib.generators.mkLuaInline; | |
| 18 | + toLua = lib.generators.toLua { }; | |
| 19 | + | |
| 20 | + configHome = config.xdg.configHome; | |
| 21 | + wallpaper = ../../config/hypr/wallpaper.jpg; | |
| 22 | + | |
| 23 | + # --- Hyprland ----------------------------------------------------------------- | |
| 24 | + | |
| 25 | + mod = "SUPER"; | |
| 26 | + bind = keys: action: { | |
| 27 | + _args = [ | |
| 28 | + " + " | |
| 29 | + (lua action) | |
| 30 | + ]; | |
| 31 | + }; | |
| 32 | + exec = command: "hl.dsp.exec_cmd()"; | |
| 33 | + screenshotKeys = "/keys/scrnis"; | |
| 34 | + | |
| 35 | + # hl.dsp.dpms for hypridle, which runs it through hyprctl. | |
| 36 | + dpms = action: "hyprctl dispatch 'hl.dsp.dpms({ action = \"\" })'"; | |
| 37 | + | |
| 38 | + # --- Waybar ------------------------------------------------------------------- | |
| 39 | + | |
| 40 | + toggleFlyout = "/bin/bash /waybar/eww/scripts/toggle.sh"; | |
| 41 | + trayIcons = ../../config/waybar/icons; | |
| 42 | +in | |
| 43 | +{ | |
| 44 | + wayland.windowManager.hyprland = { | |
| 45 | + enable = true; | |
| 46 | + # From the NixOS module (programs.hyprland), which uwsm starts. uwsm also | |
| 47 | + # brings up graphical-session.target, so Home Manager's own session target | |
| 48 | + # isn't needed. | |
| 49 | + package = null; | |
| 50 | + portalPackage = null; | |
| 51 | + systemd.enable = false; | |
| 52 | + configType = "lua"; | |
| 53 | + | |
| 54 | + settings = { | |
| 55 | + monitor = [ | |
| 56 | + { | |
| 57 | + output = "HDMI-A-2"; | |
| 58 | + mode = "2560x1440@280"; | |
| 59 | + position = "0x0"; | |
| 60 | + scale = 1; | |
| 61 | + vrr = 0; | |
| 62 | + } | |
| 63 | + { | |
| 64 | + output = "DP-1"; | |
| 65 | + mode = "2560x1440@144"; | |
| 66 | + position = "2560x-900"; | |
| 67 | + scale = 1; | |
| 68 | + transform = 1; | |
| 69 | + vrr = 0; | |
| 70 | + } | |
| 71 | + ]; | |
| 72 | + | |
| 73 | + # Workspaces 1-2 on HDMI-A-2, 3-5 on DP-1. | |
| 74 | + workspace_rule = map (workspace: { | |
| 75 | + workspace = toString workspace; | |
| 76 | + monitor = if workspace <= 2 then "HDMI-A-2" else "DP-1"; | |
| 77 | + persistent = true; | |
| 78 | + }) (lib.range 1 5); | |
| 79 | + | |
| 80 | + window_rule = [ | |
| 81 | + { | |
| 82 | + name = "windowrule-1"; | |
| 83 | + match.title = "^RuneLite.*"; | |
| 84 | + float = true; | |
| 85 | + } | |
| 86 | + { | |
| 87 | + name = "windowrule-2"; | |
| 88 | + match = { | |
| 89 | + class = "net-runelite-client-RuneLite"; | |
| 90 | + title = "win.*"; | |
| 91 | + }; | |
| 92 | + no_focus = true; | |
| 93 | + } | |
| 94 | + { | |
| 95 | + name = "windowrule-3"; | |
| 96 | + match.class = ".*"; | |
| 97 | + immediate = true; | |
| 98 | + } | |
| 99 | + # Battle.net (Proton via Faugus). The class is shared by every umu game | |
| 100 | + # without its own ID, so match on the title too. | |
| 101 | + { | |
| 102 | + name = "battlenet-float"; | |
| 103 | + match = { | |
| 104 | + class = "^steam_app_default$"; | |
| 105 | + title = "^Battle\\.net.*"; | |
| 106 | + }; | |
| 107 | + float = true; | |
| 108 | + } | |
| 109 | + # xembedsniproxy (pkgs/xembedsniproxy) puts Wine/Proton tray icons such | |
| 110 | + # as Battle.net into Waybar's tray. It keeps a small helper window mapped | |
| 111 | + # for the icons it hosts, which Hyprland would otherwise show as a | |
| 112 | + # floating window. | |
| 113 | + { | |
| 114 | + name = "xembedsniproxy"; | |
| 115 | + match.class = "^xembedsniproxy$"; | |
| 116 | + opacity = "0.0 override 0.0 override"; | |
| 117 | + border_size = 0; | |
| 118 | + no_blur = true; | |
| 119 | + no_shadow = true; | |
| 120 | + no_anim = true; | |
| 121 | + no_focus = true; | |
| 122 | + no_initial_focus = true; | |
| 123 | + } | |
| 124 | + # Dolphin's transparency comes from Kvantum's translucent_windows (see | |
| 125 | + # config/kvantum/RiceDark.kvconfig), not a forced window opacity: Kvantum | |
| 126 | + # puts alpha in the background only, so text and icons stay opaque, as | |
| 127 | + # in kitty. decoration.blur blurs through it. If Dolphin looks opaque | |
| 128 | + # after applying the Kvantum theme, this rule can come back: | |
| 129 | + # { name = "windowrule-4"; match.class = "^(org.kde.dolphin)$"; opacity = "0.90 0.80"; } | |
| 130 | + | |
| 131 | + # Claude's desktop app creates its window opaque (transparent: false in | |
| 132 | + # the Electron bundle) and has no user stylesheet, so unlike kitty and | |
| 133 | + # Dolphin it can't put alpha in the background only. A forced window | |
| 134 | + # opacity is the closest it gets: decoration.blur frosts what shows | |
| 135 | + # through, at the cost of the text being translucent too. | |
| 136 | + { | |
| 137 | + name = "claude-frosted"; | |
| 138 | + match.class = "^com\\.anthropic\\.Claude$"; | |
| 139 | + opacity = "0.80 0.80"; | |
| 140 | + } | |
| 141 | + ]; | |
| 142 | + | |
| 143 | + # Blur behind the translucent layers: Waybar, fuzzel ("launcher"), dunst | |
| 144 | + # and the control centre. | |
| 145 | + layer_rule = [ | |
| 146 | + { | |
| 147 | + name = "layerrule-1"; | |
| 148 | + match.namespace = "waybar"; | |
| 149 | + blur = true; | |
| 150 | + ignore_alpha = 0; | |
| 151 | + blur_popups = true; | |
| 152 | + } | |
| 153 | + { | |
| 154 | + name = "layerrule-2"; | |
| 155 | + match.namespace = "launcher"; | |
| 156 | + ignore_alpha = 0; | |
| 157 | + blur = true; | |
| 158 | + } | |
| 159 | + { | |
| 160 | + name = "layerrule-3"; | |
| 161 | + match.namespace = "notifications"; | |
| 162 | + ignore_alpha = 0; | |
| 163 | + blur = true; | |
| 164 | + } | |
| 165 | + { | |
| 166 | + name = "layerrule-4"; | |
| 167 | + match.namespace = "eww-control-center"; | |
| 168 | + ignore_alpha = 0; | |
| 169 | + blur = true; | |
| 170 | + } | |
| 171 | + ]; | |
| 172 | + | |
| 173 | + bind = [ | |
| 174 | + (bind "SHIFT + S" (exec "imsh-shot area -Cufa@ -s1")) | |
| 175 | + (bind "SHIFT + F" (exec "imsh-shot screen -Cua@")) | |
| 176 | + (bind "SHIFT + R" (exec "imsh-cast -Cua@")) | |
| 177 | + (bind "Return" (exec "/fuzzel/launch")) | |
| 178 | + (bind "T" (exec (lib.getExe config.programs.kitty.package))) | |
| 179 | + (bind "W" (exec "firefox")) | |
| 180 | + (bind "Q" "hl.dsp.window.close()") | |
| 181 | + (bind "F" ''hl.dsp.window.float({ action = "toggle" })'') | |
| 182 | + (bind "G" ''hl.dsp.window.fullscreen({ mode = "fullscreen", action = "toggle" })'') | |
| 183 | + (bind "mouse_down" ''hl.dsp.focus({ workspace = "m+1" })'') | |
| 184 | + (bind "mouse_up" ''hl.dsp.focus({ workspace = "m-1" })'') | |
| 185 | + (bind "mouse:272" "hl.dsp.window.drag()") | |
| 186 | + (bind "mouse:273" "hl.dsp.window.resize()") | |
| 187 | + (bind "Delete" "hl.dsp.exit()") | |
| 188 | + ]; | |
| 189 | + | |
| 190 | + env = | |
| 191 | + lib.mapAttrsToList | |
| 192 | + (name: value: { | |
| 193 | + _args = [ | |
| 194 | + name | |
| 195 | + value | |
| 196 | + ]; | |
| 197 | + }) | |
| 198 | + { | |
| 199 | + LIBVA_DRIVER_NAME = "nvidia"; | |
| 200 | + # libva-nvidia-driver's backend, for Firefox's hardware video decoding. | |
| 201 | + NVD_BACKEND = "direct"; | |
| 202 | + # Lets Firefox's video decoder process reach the NVIDIA driver. | |
| 203 | + MOZ_DISABLE_RDD_SANDBOX = "1"; | |
| 204 | + XDG_SESSION_TYPE = "wayland"; | |
| 205 | + GBM_BACKEND = "nvidia-drm"; | |
| 206 | + __GLX_VENDOR_LIBRARY_NAME = "nvidia"; | |
| 207 | + XCURSOR_THEME = config.home.pointerCursor.name; | |
| 208 | + XCURSOR_SIZE = toString config.home.pointerCursor.size; | |
| 209 | + QT_QPA_PLATFORMTHEME = config.qt.platformTheme.name; | |
| 210 | + }; | |
| 211 | + | |
| 212 | + config = { | |
| 213 | + general = { | |
| 214 | + border_size = 1; | |
| 215 | + gaps_in = 5; | |
| 216 | + gaps_out = 17; | |
| 217 | + layout = "dwindle"; | |
| 218 | + col = { | |
| 219 | + active_border = "rgba(00000000)"; | |
| 220 | + inactive_border = "rgba(00000000)"; | |
| 221 | + }; | |
| 222 | + allow_tearing = true; | |
| 223 | + }; | |
| 224 | + decoration = { | |
| 225 | + rounding = 10; | |
| 226 | + # No visible borders; the focused window is the one that isn't dimmed. | |
| 227 | + dim_inactive = true; | |
| 228 | + dim_strength = 0.15; | |
| 229 | + blur = { | |
| 230 | + enabled = true; | |
| 231 | + size = 9; | |
| 232 | + passes = 3; | |
| 233 | + new_optimizations = true; | |
| 234 | + # Blur tooltips and context menus (Kvantum's popup_blurring is on for this). | |
| 235 | + popups = true; | |
| 236 | + }; | |
| 237 | + shadow = { | |
| 238 | + enabled = false; | |
| 239 | + render_power = 3; | |
| 240 | + range = 4; | |
| 241 | + color = "rgba(1a1a1aee)"; | |
| 242 | + }; | |
| 243 | + }; | |
| 244 | + input = { | |
| 245 | + kb_layout = "no"; | |
| 246 | + follow_mouse = 1; | |
| 247 | + sensitivity = 0; | |
| 248 | + accel_profile = "flat"; | |
| 249 | + }; | |
| 250 | + misc = { | |
| 251 | + disable_hyprland_logo = true; | |
| 252 | + disable_splash_rendering = true; | |
| 253 | + # The wallpaper's average colour instead of black, so the moment | |
| 254 | + # between the login screen and hyprpaper drawing the wallpaper doesn't | |
| 255 | + # flash black. | |
| 256 | + background_color = "rgb(121d1a)"; | |
| 257 | + }; | |
| 258 | + ecosystem.no_donation_nag = true; | |
| 259 | + binds.scroll_event_delay = 10; | |
| 260 | + # Software cursor, for tearing. Hyprland (0.56) refuses to tear while a | |
| 261 | + # hardware cursor is visible, because the kernel can't update the cursor | |
| 262 | + # plane during a tearing flip. So with a hardware cursor a game only | |
| 263 | + # tears while it hides the pointer (e.g. mouselook) and is vsynced | |
| 264 | + # otherwise, and switching back and forth left WoW's cursor shape stale. | |
| 265 | + # With a software cursor the game tears all the time; the price is that | |
| 266 | + # frames are composited (one extra copy) while the pointer is visible. | |
| 267 | + cursor.no_hardware_cursors = true; | |
| 268 | + # Fullscreen games (e.g. WoW) scan out straight to the monitor instead | |
| 269 | + # of being copied by the compositor. With the software cursor above this | |
| 270 | + # only applies while the game hides the pointer. 1 = always; XWayland | |
| 271 | + # games don't set a content type, so 2 (auto) won't trigger. | |
| 272 | + render.direct_scanout = 1; | |
| 273 | + }; | |
| 274 | + }; | |
| 275 | + | |
| 276 | + # What happens at login (the apps' placement rules), in Lua: linked to the | |
| 277 | + # checkout like the other hand-edited configs. | |
| 278 | + extraLuaFiles.startup = link "hypr/startup.lua"; | |
| 279 | + }; | |
| 280 | + | |
| 281 | + # --- Lock screen, idle, wallpaper ----------------------------------------------- | |
| 282 | + | |
| 283 | + # Styled to match the greetd login screen (config/greetd/style.css): same | |
| 284 | + # bamboo wallpaper, translucent black boxes, #eee text, rounding 10. | |
| 285 | + programs.hyprlock = { | |
| 286 | + enable = true; | |
| 287 | + # From the NixOS module (programs.hyprlock), which also sets up its PAM. | |
| 288 | + package = null; | |
| 289 | + settings = | |
| 290 | + let | |
| 291 | + centered = { | |
| 292 | + monitor = "HDMI-A-2"; | |
| 293 | + halign = "center"; | |
| 294 | + valign = "center"; | |
| 295 | + }; | |
| 296 | + in | |
| 297 | + { | |
| 298 | + "$font" = "NotoSans Nerd Font"; | |
| 299 | + "$text" = "rgb(238, 238, 238)"; | |
| 300 | + "$box" = "rgba(0, 0, 0, 0.2)"; | |
| 301 | + | |
| 302 | + general = { | |
| 303 | + hide_cursor = false; | |
| 304 | + ignore_empty_input = true; | |
| 305 | + }; | |
| 306 | + | |
| 307 | + animations = { | |
| 308 | + enabled = true; | |
| 309 | + bezier = "ease, 0.25, 0.1, 0.25, 1"; | |
| 310 | + animation = [ | |
| 311 | + "fadeIn, 1, 4, ease" | |
| 312 | + "fadeOut, 1, 4, ease" | |
| 313 | + "inputFieldDots, 1, 2, ease" | |
| 314 | + ]; | |
| 315 | + }; | |
| 316 | + | |
| 317 | + # Light blur and dim so the text stays readable over the leaves, like | |
| 318 | + # the blurred panel behind the login box. | |
| 319 | + background = { | |
| 320 | + monitor = ""; | |
| 321 | + path = ""; | |
| 322 | + blur_passes = 2; | |
| 323 | + blur_size = 5; | |
| 324 | + brightness = 0.75; | |
| 325 | + }; | |
| 326 | + | |
| 327 | + label = [ | |
| 328 | + # Clock | |
| 329 | + ( | |
| 330 | + centered | |
| 331 | + // { | |
| 332 | + text = ''<span font_weight="light">$TIME</span>''; | |
| 333 | + color = "$text"; | |
| 334 | + font_family = "$font"; | |
| 335 | + font_size = 110; | |
| 336 | + position = "0, 170"; | |
| 337 | + } | |
| 338 | + ) | |
| 339 | + # Date | |
| 340 | + ( | |
| 341 | + centered | |
| 342 | + // { | |
| 343 | + text = ''cmd[update:60000] date +"%A %-d %B"''; | |
| 344 | + color = "rgba(238, 238, 238, 0.75)"; | |
| 345 | + font_family = "$font"; | |
| 346 | + font_size = 18; | |
| 347 | + position = "0, 70"; | |
| 348 | + } | |
| 349 | + ) | |
| 350 | + # User | |
| 351 | + ( | |
| 352 | + centered | |
| 353 | + // { | |
| 354 | + text = "$USER"; | |
| 355 | + color = "$text"; | |
| 356 | + font_family = "$font"; | |
| 357 | + font_size = 15; | |
| 358 | + position = "0, 0"; | |
| 359 | + } | |
| 360 | + ) | |
| 361 | + ]; | |
| 362 | + | |
| 363 | + input-field = centered // { | |
| 364 | + size = "320, 50"; | |
| 365 | + rounding = 10; | |
| 366 | + outline_thickness = 0; | |
| 367 | + inner_color = "$box"; | |
| 368 | + outer_color = "rgba(0, 0, 0, 0)"; | |
| 369 | + check_color = "rgba(170, 255, 255, 0.5)"; | |
| 370 | + fail_color = "rgba(255, 107, 107, 0.6)"; | |
| 371 | + capslock_color = "rgba(244, 214, 159, 0.5)"; | |
| 372 | + font_color = "$text"; | |
| 373 | + font_family = "$font"; | |
| 374 | + fade_on_empty = false; | |
| 375 | + placeholder_text = ''<span foreground="##eeeeeeaa">Password</span>''; | |
| 376 | + fail_text = ''<span foreground="##eeeeee">$FAIL ($ATTEMPTS)</span>''; | |
| 377 | + dots_size = 0.22; | |
| 378 | + dots_spacing = 0.4; | |
| 379 | + dots_center = true; | |
| 380 | + position = "0, -60"; | |
| 381 | + }; | |
| 382 | + }; | |
| 383 | + }; | |
| 384 | + | |
| 385 | + services.hypridle = { | |
| 386 | + enable = true; | |
| 387 | + settings = { | |
| 388 | + general = { | |
| 389 | + lock_cmd = "pidof hyprlock || hyprlock"; | |
| 390 | + before_sleep_cmd = "loginctl lock-session"; | |
| 391 | + after_sleep_cmd = dpms "on"; | |
| 392 | + }; | |
| 393 | + listener = [ | |
| 394 | + # Lock after 10 minutes idle (respects inhibitors, e.g. fullscreen video). | |
| 395 | + { | |
| 396 | + timeout = 600; | |
| 397 | + on-timeout = "loginctl lock-session"; | |
| 398 | + } | |
| 399 | + { | |
| 400 | + timeout = 1; | |
| 401 | + on-resume = dpms "on"; | |
| 402 | + on-timeout = "true"; | |
| 403 | + ignore_inhibit = true; | |
| 404 | + } | |
| 405 | + { | |
| 406 | + timeout = 300; | |
| 407 | + on-timeout = dpms "off"; | |
| 408 | + ignore_inhibit = true; | |
| 409 | + } | |
| 410 | + ]; | |
| 411 | + }; | |
| 412 | + }; | |
| 413 | + | |
| 414 | + # A new picture from the folder every 30 seconds, on every monitor. | |
| 415 | + services.hyprpaper = { | |
| 416 | + enable = true; | |
| 417 | + settings = { | |
| 418 | + wallpaper = { | |
| 419 | + monitor = ""; | |
| 420 | + path = "/Wallpapers"; | |
| 421 | + timeout = 30; | |
| 422 | + }; | |
| 423 | + splash = false; | |
| 424 | + }; | |
| 425 | + }; | |
| 426 | + | |
| 427 | + # --- Waybar --------------------------------------------------------------------- | |
| 428 | + | |
| 429 | + # The flyouts it opens (control centre, notifications) are Eww windows, in | |
| 430 | + # config/waybar/eww; its scripts are in config/waybar/scripts. Both stay linked | |
| 431 | + # to the checkout (files.nix). | |
| 432 | + programs.waybar = { | |
| 433 | + enable = true; | |
| 434 | + systemd.enable = true; | |
| 435 | + | |
| 436 | + settings.main = { | |
| 437 | + layer = "top"; | |
| 438 | + position = "top"; | |
| 439 | + height = 36; | |
| 440 | + margin-left = 15; | |
| 441 | + margin-right = 15; | |
| 442 | + margin-top = 15; | |
| 443 | + | |
| 444 | + modules-left = [ | |
| 445 | + "hyprland/workspaces" | |
| 446 | + "hyprland/window" | |
| 447 | + ]; | |
| 448 | + modules-center = [ | |
| 449 | + "custom/imsh-cast-monitor" | |
| 450 | + "clock" | |
| 451 | + ]; | |
| 452 | + modules-right = [ | |
| 453 | + "tray" | |
| 454 | + "custom/notifications" | |
| 455 | + "custom/notifications-count" | |
| 456 | + "custom/control-center" | |
| 457 | + ]; | |
| 458 | + | |
| 459 | + "hyprland/workspaces" = { | |
| 460 | + format = "{name}"; | |
| 461 | + on-click = "activate"; | |
| 462 | + sort-by = "number"; | |
| 463 | + }; | |
| 464 | + "hyprland/window".max-length = 60; | |
| 465 | + | |
| 466 | + "custom/imsh-cast-monitor" = { | |
| 467 | + format = "{text}"; | |
| 468 | + exec = "imsh-cast-monitor"; | |
| 469 | + return-type = "json"; | |
| 470 | + hide-empty-text = true; | |
| 471 | + }; | |
| 472 | + | |
| 473 | + clock = { | |
| 474 | + format = "{:%H:%M}"; | |
| 475 | + format-alt = "{:%A, %B %d, %Y (%R)}"; | |
| 476 | + tooltip-format = "<tt><big>{calendar}</big></tt>"; | |
| 477 | + calendar = { | |
| 478 | + mode = "month"; | |
| 479 | + iso8601 = true; | |
| 480 | + mode-mon-col = 3; | |
| 481 | + weeks-pos = "right"; | |
| 482 | + on-scroll = 1; | |
| 483 | + format = { | |
| 484 | + months = "<span color='#ffffff'><b>{}</b></span>"; | |
| 485 | + days = "<span color='#ffffff'>{}</span>"; | |
| 486 | + weeks = "<span color='#8a8a8a'>W{:%V}</span>"; | |
| 487 | + weekdays = "<span color='#b0b0b0'><b>{}</b></span>"; | |
| 488 | + today = "<span color='#aaffff'><b><u>{}</u></b></span>"; | |
| 489 | + }; | |
| 490 | + }; | |
| 491 | + actions = { | |
| 492 | + on-click-right = "mode"; | |
| 493 | + on-scroll-up = "shift_up"; | |
| 494 | + on-scroll-down = "shift_down"; | |
| 495 | + }; | |
| 496 | + }; | |
| 497 | + | |
| 498 | + # Own icons for the tray apps, by their StatusNotifierItem ID. | |
| 499 | + tray = { | |
| 500 | + icon-size = 16; | |
| 501 | + spacing = 10; | |
| 502 | + order = [ "discord_status_icon_1" ]; | |
| 503 | + icons = { | |
| 504 | + Signal_status_icon_1 = "/signal.svg"; | |
| 505 | + Bitwarden_status_icon_1 = "/bitwarden.svg"; | |
| 506 | + WowUpCf_status_icon_1 = "/wowup.svg"; | |
| 507 | + Claude_status_icon_1 = "/claude.svg"; | |
| 508 | + steam_app_default = "/battlenet.svg"; | |
| 509 | + faugus-launcher = "/faugus.svg"; | |
| 510 | + steam = "/steam.svg"; | |
| 511 | + "tray-icon tray app opendeck" = "/opendeck.svg"; | |
| 512 | + }; | |
| 513 | + }; | |
| 514 | + | |
| 515 | + "custom/notifications" = { | |
| 516 | + format = "<span size='12288'></span>"; | |
| 517 | + tooltip = false; | |
| 518 | + on-click = " notifications"; | |
| 519 | + }; | |
| 520 | + "custom/notifications-count" = { | |
| 521 | + exec = "/bin/python3 /waybar/scripts/notification-count.py"; | |
| 522 | + return-type = "json"; | |
| 523 | + hide-empty-text = true; | |
| 524 | + tooltip = false; | |
| 525 | + on-click = " notifications"; | |
| 526 | + }; | |
| 527 | + "custom/control-center" = { | |
| 528 | + format = " "; | |
| 529 | + tooltip = false; | |
| 530 | + on-click = toggleFlyout; | |
| 531 | + }; | |
| 532 | + }; | |
| 533 | + | |
| 534 | + style = '' | |
| 535 | + * { | |
| 536 | + border: none; | |
| 537 | + border-radius: 17px; | |
| 538 | + font-family: NotoSans Nerd Font Regular; | |
| 539 | + font-size: 12px; | |
| 540 | + font-weight: bolder; | |
| 541 | + background: transparent; | |
| 542 | + color: white; | |
| 543 | + min-height: 0; | |
| 544 | + } | |
| 545 | + | |
| 546 | + window#waybar { | |
| 547 | + background: rgba(0, 0, 0, 0.1); | |
| 548 | + } | |
| 549 | + | |
| 550 | + window#waybar #tray menu { | |
| 551 | + background: rgba(0, 0, 0, 0.1); | |
| 552 | + } | |
| 553 | + | |
| 554 | + #tray menu :hover { | |
| 555 | + background: rgba(0, 0, 0, 0.1); | |
| 556 | + } | |
| 557 | + | |
| 558 | + #tray menu :hover label { | |
| 559 | + background: rgba(0, 0, 0, 0.1); | |
| 560 | + } | |
| 561 | + | |
| 562 | + tooltip { | |
| 563 | + background: rgba(0, 0, 0, 0.1); | |
| 564 | + } | |
| 565 | + | |
| 566 | + /* Workspace buttons are pills inset 4px from the bar's edge, so their 13px radius | |
| 567 | + is concentric with the bar's 17px one. Everything GTK's theme draws on a button | |
| 568 | + (border, shadows, hover gradient) is reset here, as it otherwise shows up as a | |
| 569 | + square box on hover. */ | |
| 570 | + #workspaces button { | |
| 571 | + margin: 4px 0 4px 4px; | |
| 572 | + padding: 0 6px; | |
| 573 | + min-width: 16px; | |
| 574 | + border: none; | |
| 575 | + border-radius: 13px; | |
| 576 | + outline: none; | |
| 577 | + box-shadow: none; | |
| 578 | + text-shadow: none; | |
| 579 | + background-color: transparent; | |
| 580 | + /* Active-workspace underline: 2px line that grows from the centre (see .active). */ | |
| 581 | + background-image: linear-gradient(white, white); | |
| 582 | + background-repeat: no-repeat; | |
| 583 | + background-position: 50% 92%; | |
| 584 | + background-size: 0 2px; | |
| 585 | + transition: background-size 200ms ease-out, background-color 150ms ease-out; | |
| 586 | + } | |
| 587 | + | |
| 588 | + #workspaces button:hover { | |
| 589 | + background-color: rgba(255, 255, 255, 0.12); | |
| 590 | + } | |
| 591 | + | |
| 592 | + #tray window decoration { | |
| 593 | + border-style: none; | |
| 594 | + box-shadow: none; | |
| 595 | + } | |
| 596 | + | |
| 597 | + #workspaces button.active { | |
| 598 | + background-size: 10px 2px; | |
| 599 | + } | |
| 600 | + | |
| 601 | + /* Workspaces with no windows are dimmed, unless it's the one you're on. The colour | |
| 602 | + goes on the label because the `*` rule above gives every label its own white. */ | |
| 603 | + #workspaces button label { | |
| 604 | + transition: color 150ms ease-out; | |
| 605 | + } | |
| 606 | + | |
| 607 | + #workspaces button.empty:not(.active) label { | |
| 608 | + color: rgba(255, 255, 255, 0.4); | |
| 609 | + } | |
| 610 | + | |
| 611 | + #clock, #tray, #custom-notifications, #custom-control-center { | |
| 612 | + padding: 0 7px; | |
| 613 | + } | |
| 614 | + | |
| 615 | + /* Notification count: a small white disc overlapping the bell's top right corner. | |
| 616 | + The margins shrink it from the bar's full height to 12px; the negative left margin | |
| 617 | + pulls it over the bell. Hidden by Waybar when there are no notifications. */ | |
| 618 | + #custom-notifications-count { | |
| 619 | + min-width: 6px; | |
| 620 | + padding: 0 3px; | |
| 621 | + margin: 7px 3px 17px -11px; | |
| 622 | + border-radius: 6px; | |
| 623 | + background: white; | |
| 624 | + color: #18181c; | |
| 625 | + font-size: 8px; | |
| 626 | + } | |
| 627 | + | |
| 628 | + .imsh-cast-monitor.recording { | |
| 629 | + color: #f00; | |
| 630 | + } | |
| 631 | + | |
| 632 | + .imsh-cast-monitor.error { | |
| 633 | + color: #ff0; | |
| 634 | + } | |
| 635 | + | |
| 636 | + /* Control-center button: your logo as a pre-rendered 18px image. | |
| 637 | + GTK stretches SVG backgrounds as pixels (which makes them blurry), so logo-18.png is | |
| 638 | + rendered at exactly 18px, and logo-36.png is used automatically on 2x-scaled monitors. */ | |
| 639 | + #custom-control-center { | |
| 640 | + min-width: 18px; | |
| 641 | + min-height: 18px; | |
| 642 | + background-image: -gtk-scaled(url("file://"), | |
| 643 | + url("file://")); | |
| 644 | + background-repeat: no-repeat; | |
| 645 | + background-position: center; | |
| 646 | + background-size: 18px 18px; | |
| 647 | + margin-right: 6px; /* gap between the logo and the end of the bar */ | |
| 648 | + } | |
| 649 | + ''; | |
| 650 | + }; | |
| 651 | + | |
| 652 | + # --- Notifications -------------------------------------------------------------- | |
| 653 | + | |
| 654 | + services.dunst = { | |
| 655 | + enable = true; | |
| 656 | + settings = | |
| 657 | + let | |
| 658 | + # Near-transparent, over Hyprland's blur (the "notifications" layer rule). | |
| 659 | + urgency = timeout: { | |
| 660 | + inherit timeout; | |
| 661 | + background = "#25252501"; | |
| 662 | + foreground = "#ffffff"; | |
| 663 | + frame_color = "#25252501"; | |
| 664 | + }; | |
| 665 | + in | |
| 666 | + { | |
| 667 | + global = { | |
| 668 | + monitor = 0; | |
| 669 | + follow = "mouse"; | |
| 670 | + alignment = "center"; | |
| 671 | + width = 360; | |
| 672 | + offset = 15; | |
| 673 | + origin = "top-right"; | |
| 674 | + padding = 12; | |
| 675 | + horizontal_padding = 12; | |
| 676 | + frame_width = 0; | |
| 677 | + markup = "full"; | |
| 678 | + icon_position = "off"; | |
| 679 | + font = "DejaVu Sans Mono 10"; | |
| 680 | + stack_duplicates = true; | |
| 681 | + hide_duplicate_count = true; | |
| 682 | + separator_height = 0; | |
| 683 | + fullscreen = "pushback"; | |
| 684 | + corner_radius = 15; | |
| 685 | + # Left click does what the notification is for (e.g. opens the chat), | |
| 686 | + # then closes it. Middle click only dismisses it, right click | |
| 687 | + # dismisses all. | |
| 688 | + mouse_left_click = "do_action, close_current"; | |
| 689 | + mouse_middle_click = "close_current"; | |
| 690 | + mouse_right_click = "close_all"; | |
| 691 | + progress_bar = true; | |
| 692 | + }; | |
| 693 | + urgency_low = urgency 4; | |
| 694 | + urgency_normal = urgency 8; | |
| 695 | + urgency_critical = urgency 16; | |
| 696 | + status = { | |
| 697 | + category = "status"; | |
| 698 | + set_stack_tag = "status"; | |
| 699 | + history_ignore = true; | |
| 700 | + timeout = 2; | |
| 701 | + }; | |
| 702 | + }; | |
| 703 | + }; | |
| 704 | +} |
modifiedmodules/home/files.nix
@@ -1,22 +1,14 @@ | ||
| 1 | -# The config files in ../../config, put where the programs read them. This | |
| 2 | -# replaces symlink.sh from the old dotfiles repo. | |
| 1 | +# The config files in ../../config that no Home Manager module writes, put where | |
| 2 | +# the programs read them: scripts, stylesheets and themes edited by hand, and | |
| 3 | +# the configs apps save themselves. The rest are set through the modules in | |
| 4 | +# desktop.nix, apps.nix, theming.nix and shell.nix. | |
| 3 | 5 | { |
| 4 | 6 | config, |
| 5 | 7 | lib, |
| 6 | - pkgs, | |
| 7 | 8 | ... |
| 8 | 9 | }: |
| 9 | 10 | let |
| 10 | - cfg = config.dot; | |
| 11 | - | |
| 12 | - # A file or directory of config/: linked to the checkout when liveConfig is | |
| 13 | - # on (edits apply at once), copied into the store otherwise. | |
| 14 | - link = | |
| 15 | - rel: | |
| 16 | - if cfg.liveConfig then | |
| 17 | - config.lib.file.mkOutOfStoreSymlink "/config/" | |
| 18 | - else | |
| 19 | - ../../config + "/"; | |
| 11 | + inherit (config.lib.dot) link; | |
| 20 | 12 | |
| 21 | 13 | # Configs the programs write themselves (MangoHud, btop, micro, apps that |
| 22 | 14 | # register URL handlers): copied in only when missing, and never overwritten, |
@@ -30,28 +22,31 @@ | ||
| 30 | 22 | ".config/micro/bindings.json" = "micro/bindings.json"; |
| 31 | 23 | ".config/mimeapps.list" = "mimeapps.list"; |
| 32 | 24 | }; |
| 25 | + | |
| 26 | + # Linked whole to the checkout before Home Manager modules wrote files in them | |
| 27 | + # (and kdeglobals, now set with kwriteconfig6). | |
| 28 | + oldLinks = [ | |
| 29 | + "hypr" | |
| 30 | + "waybar" | |
| 31 | + "fuzzel" | |
| 32 | + "kitty" | |
| 33 | + "dunst" | |
| 34 | + "nvim" | |
| 35 | + "qt6ct" | |
| 36 | + "kdeglobals" | |
| 37 | + ]; | |
| 33 | 38 | in |
| 34 | 39 | { |
| 35 | 40 | xdg.configFile = { |
| 36 | - "hypr".source = link "hypr"; | |
| 37 | - "waybar".source = link "waybar"; | |
| 38 | - "dunst".source = link "dunst"; | |
| 39 | - "kitty".source = link "kitty"; | |
| 40 | - "fuzzel".source = link "fuzzel"; | |
| 41 | - "nvim".source = link "nvim"; | |
| 42 | - "qt6ct".source = link "qt6ct"; | |
| 43 | - "kdeglobals".source = link "kde/kdeglobals"; | |
| 41 | + # Started by services.nix: the apps opened at login, the clipboard bridge. | |
| 42 | + "hypr/scripts".source = link "hypr/scripts"; | |
| 43 | + # Waybar's flyouts (Eww) and the scripts its modules and the flyouts run. | |
| 44 | + "waybar/eww".source = link "waybar/eww"; | |
| 45 | + "waybar/scripts".source = link "waybar/scripts"; | |
| 46 | + # Hyprland's mod + Return; reads favorites next to itself. | |
| 47 | + "fuzzel/launch".source = link "fuzzel/launch"; | |
| 48 | + "fuzzel/favorites".source = link "fuzzel/favorites"; | |
| 44 | 49 | "philly/theme.css".source = link "philly/theme.css"; |
| 45 | - | |
| 46 | - # Kvantum: the active theme is KvArcDark# (your customised copy of KvArcDark, | |
| 47 | - # made with Kvantum Manager; it uses the stock theme's artwork). RiceDark is | |
| 48 | - # the tweaked theme from the old repo, with the stock KvArcDark artwork | |
| 49 | - # copied in under its name, as kvantum/install.sh did. | |
| 50 | - "Kvantum/kvantum.kvconfig".source = link "kvantum/kvantum.kvconfig"; | |
| 51 | - "Kvantum/KvArcDark#/KvArcDark#.kvconfig".source = link "kvantum/KvArcDark#.kvconfig"; | |
| 52 | - "Kvantum/RiceDark/RiceDark.kvconfig".source = link "kvantum/RiceDark.kvconfig"; | |
| 53 | - "Kvantum/RiceDark/RiceDark.svg".source = | |
| 54 | - "/share/Kvantum/KvArcDark/KvArcDark.svg"; | |
| 55 | 50 | |
| 56 | 51 | "micro/colorschemes".source = ../../config/micro/colorschemes; |
| 57 | 52 | }; |
@@ -59,6 +54,19 @@ | ||
| 59 | 54 | home.file.".p10k.zsh".source = link "zsh/p10k.zsh"; |
| 60 | 55 | |
| 61 | 56 | home.activation = { |
| 57 | + # The folders above were links into the checkout. Home Manager checks for | |
| 58 | + # files in its way before it removes old links, so through such a link it | |
| 59 | + # would find the checkout's own files (waybar/eww, hypr/scripts) and move | |
| 60 | + # them aside in the repo. Remove the old links first; afterwards these are | |
| 61 | + # real folders and this does nothing. | |
| 62 | + dropOldLinks = lib.hm.dag.entryBefore [ "checkLinkTargets" ] ( | |
| 63 | + lib.concatMapStrings (name: '' | |
| 64 | + if [ -L "/" ]; then | |
| 65 | + run rm "/" | |
| 66 | + fi | |
| 67 | + '') oldLinks | |
| 68 | + ); | |
| 69 | + | |
| 62 | 70 | seedConfigs = lib.hm.dag.entryAfter [ "writeBoundary" ] ( |
| 63 | 71 | lib.concatStrings ( |
| 64 | 72 | lib.mapAttrsToList (target: rel: '' |
@@ -69,27 +77,5 @@ | ||
| 69 | 77 | '') seeds |
| 70 | 78 | ) |
| 71 | 79 | ); |
| 72 | - | |
| 73 | - # Firefox: user.js and userChrome.css in the default profile, which Firefox | |
| 74 | - # creates itself (Home Manager's own Firefox module would make a new profile | |
| 75 | - # and take over profiles.ini). Same as symlink.sh did. | |
| 76 | - firefoxProfile = lib.hm.dag.entryAfter [ "writeBoundary" ] '' | |
| 77 | - ini="$HOME/.config/mozilla/firefox/profiles.ini" | |
| 78 | - if [ -f "$ini" ]; then | |
| 79 | - profile=$(/bin/awk -F= ' | |
| 80 | - /^\[Install/ { install = 1; next } | |
| 81 | - /^\[/ { install = 0 } | |
| 82 | - install && $1 == "Default" { print $2; exit } | |
| 83 | - ' "$ini") | |
| 84 | - if [ -n "$profile" ]; then | |
| 85 | - dir="$HOME/.config/mozilla/firefox/$profile" | |
| 86 | - run mkdir -p "$dir/chrome" | |
| 87 | - run ln -sfn "$dir/user.js" | |
| 88 | - run ln -sfn "$dir/chrome/userChrome.css" | |
| 89 | - fi | |
| 90 | - else | |
| 91 | - warnEcho "Firefox has no profile yet: start Firefox once, then run home-manager switch (or nixos-rebuild switch) again." | |
| 92 | - fi | |
| 93 | - ''; | |
| 94 | 80 | }; |
| 95 | 81 | } |
modifiedmodules/home/services.nix
@@ -49,34 +49,16 @@ | ||
| 49 | 49 | in |
| 50 | 50 | { |
| 51 | 51 | systemd.user.services = { |
| 52 | - waybar = graphical { | |
| 53 | - description = "Waybar"; | |
| 54 | - exec = lib.getExe pkgs.waybar; | |
| 55 | - slice = "session-graphical.slice"; | |
| 56 | - service.ExecReload = "/bin/kill -SIGUSR2 $MAINPID"; | |
| 57 | - }; | |
| 58 | - | |
| 59 | - hypridle = graphical { | |
| 60 | - description = "Hyprland's idle daemon"; | |
| 61 | - exec = lib.getExe pkgs.hypridle; | |
| 62 | - slice = "session-graphical.slice"; | |
| 63 | - unit.ConditionEnvironment = "WAYLAND_DISPLAY"; | |
| 64 | - }; | |
| 65 | - | |
| 66 | - hyprpaper = graphical { | |
| 67 | - description = "Hyprland wallpaper daemon"; | |
| 68 | - exec = lib.getExe pkgs.hyprpaper; | |
| 69 | - slice = "session-graphical.slice"; | |
| 70 | - }; | |
| 71 | - | |
| 72 | - dunst = graphical { | |
| 73 | - description = "Dunst notification daemon"; | |
| 74 | - exec = lib.getExe pkgs.dunst; | |
| 75 | - slice = "session-graphical.slice"; | |
| 76 | - service = { | |
| 77 | - Type = "dbus"; | |
| 78 | - BusName = "org.freedesktop.Notifications"; | |
| 79 | - }; | |
| 52 | + # Waybar, hypridle, hyprpaper and dunst come from their Home Manager modules | |
| 53 | + # (desktop.nix); these put them with the rest of the shell. | |
| 54 | + waybar.Service.Slice = "session-graphical.slice"; | |
| 55 | + hypridle.Service.Slice = "session-graphical.slice"; | |
| 56 | + hyprpaper.Service.Slice = "session-graphical.slice"; | |
| 57 | + dunst = { | |
| 58 | + Service.Slice = "session-graphical.slice"; | |
| 59 | + # The module only starts dunst on the first notification (D-Bus | |
| 60 | + # activation); start it with the session, as before. | |
| 61 | + Install.WantedBy = [ "graphical-session.target" ]; | |
| 80 | 62 | }; |
| 81 | 63 | |
| 82 | 64 | # The Waybar flyouts (control centre, notifications). Started with the |
@@ -113,7 +95,7 @@ | ||
| 113 | 95 | |
| 114 | 96 | # The apps opened at login (Signal, Discord, Faugus, OpenDeck, WowUp); see |
| 115 | 97 | # config/hypr/scripts/autostart-apps. It starts each one as its own unit and |
| 116 | - # exits; the window placement rules for them are in hyprland.lua. | |
| 98 | + # exits; the window placement rules for them are in config/hypr/startup.lua. | |
| 117 | 99 | autostart-apps = graphical { |
| 118 | 100 | description = "Open the apps that start at login"; |
| 119 | 101 | exec = "/hypr/scripts/autostart-apps"; |
modifiedmodules/home/theming.nix
@@ -1,9 +1,13 @@ | ||
| 1 | 1 | # GTK and cursor theme, as nwg-look had set them on CachyOS (settings.ini, |
| 2 | -# .gtkrc-2.0, GSettings). Qt is themed by config/qt6ct and config/kvantum. | |
| 2 | +# .gtkrc-2.0, GSettings), and Qt's: qt6ct with the Kvantum style. | |
| 3 | 3 | # |
| 4 | 4 | # These files are now written by Nix, so change the theme here rather than in |
| 5 | -# nwg-look (it can still preview themes, but can't save over these files). | |
| 6 | -{ pkgs, ... }: | |
| 5 | +# nwg-look or qt6ct (they can still preview themes, but can't save over these | |
| 6 | +# files). | |
| 7 | +{ config, pkgs, ... }: | |
| 8 | +let | |
| 9 | + inherit (config.lib.dot) link; | |
| 10 | +in | |
| 7 | 11 | { |
| 8 | 12 | home.pointerCursor = { |
| 9 | 13 | enable = true; |
@@ -65,4 +69,66 @@ | ||
| 65 | 69 | cursor-size = 28; |
| 66 | 70 | font-name = "Adwaita Sans 11"; |
| 67 | 71 | }; |
| 72 | + | |
| 73 | + # QT_QPA_PLATFORMTHEME=qt6ct and QT_STYLE_OVERRIDE=kvantum for the session | |
| 74 | + # (Hyprland's env also passes the first on, desktop.nix). qt6ct and Kvantum | |
| 75 | + # themselves are installed system-wide (modules/nixos/desktop.nix). | |
| 76 | + qt = { | |
| 77 | + enable = true; | |
| 78 | + platformTheme.name = "qt6ct"; | |
| 79 | + style.name = "kvantum"; | |
| 80 | + | |
| 81 | + qt6ctSettings = { | |
| 82 | + Appearance = { | |
| 83 | + custom_palette = false; | |
| 84 | + icon_theme = "Papirus-Dark"; | |
| 85 | + standard_dialogs = "default"; | |
| 86 | + style = "kvantum"; | |
| 87 | + }; | |
| 88 | + Fonts = { | |
| 89 | + fixed = ''"JetBrainsMono Nerd Font,10,-1,5,50,0,0,0,0,0"''; | |
| 90 | + general = ''"Noto Sans,10,-1,5,50,0,0,0,0,0"''; | |
| 91 | + }; | |
| 92 | + Interface = { | |
| 93 | + activate_item_on_single_click = 1; | |
| 94 | + buttonbox_layout = 0; | |
| 95 | + cursor_flash_time = 1000; | |
| 96 | + dialog_buttons_have_icons = 1; | |
| 97 | + double_click_interval = 400; | |
| 98 | + gui_effects = "@Invalid()"; | |
| 99 | + keyboard_scheme = 2; | |
| 100 | + menus_have_icons = true; | |
| 101 | + show_shortcuts_in_context_menus = true; | |
| 102 | + stylesheets = "@Invalid()"; | |
| 103 | + toolbutton_style = 4; | |
| 104 | + underline_shortcut = 1; | |
| 105 | + wheel_scroll_lines = 3; | |
| 106 | + }; | |
| 107 | + }; | |
| 108 | + | |
| 109 | + # The active theme is KvArcDark# (your customised copy of KvArcDark, made | |
| 110 | + # with Kvantum Manager; it uses the stock theme's artwork). RiceDark is the | |
| 111 | + # tweaked theme from the old repo, with the stock KvArcDark artwork copied in | |
| 112 | + # under its name, as kvantum/install.sh did. Both stay linked to the | |
| 113 | + # checkout. | |
| 114 | + kvantum = { | |
| 115 | + enable = true; | |
| 116 | + settings.General.theme = "KvArcDark#"; | |
| 117 | + }; | |
| 118 | + | |
| 119 | + # KDE apps (Dolphin, Ark) read the style and icons from kdeglobals. Set with | |
| 120 | + # kwriteconfig6, so the apps can still save their own settings there. | |
| 121 | + kde.settings.kdeglobals = { | |
| 122 | + General.widgetStyle = "kvantum"; | |
| 123 | + Icons.Theme = config.qt.qt6ctSettings.Appearance.icon_theme; | |
| 124 | + KDE.widgetStyle = "kvantum"; | |
| 125 | + }; | |
| 126 | + }; | |
| 127 | + | |
| 128 | + xdg.configFile = { | |
| 129 | + "Kvantum/KvArcDark#/KvArcDark#.kvconfig".source = link "kvantum/KvArcDark#.kvconfig"; | |
| 130 | + "Kvantum/RiceDark/RiceDark.kvconfig".source = link "kvantum/RiceDark.kvconfig"; | |
| 131 | + "Kvantum/RiceDark/RiceDark.svg".source = | |
| 132 | + "/share/Kvantum/KvArcDark/KvArcDark.svg"; | |
| 133 | + }; | |
| 68 | 134 | } |
modifiedmodules/nixos/desktop.nix
@@ -95,7 +95,7 @@ | ||
| 95 | 95 | |
| 96 | 96 | # --- Fonts ------------------------------------------------------------------- |
| 97 | 97 | |
| 98 | - # The fonts installed on CachyOS. kitty.conf asks for JetBrains Mono, which | |
| 98 | + # The fonts installed on CachyOS. kitty's config asks for JetBrains Mono, which | |
| 99 | 99 | # was never installed there, so kitty has been using Noto Sans Mono; add |
| 100 | 100 | # nerd-fonts.jetbrains-mono here to get the font the config names. |
| 101 | 101 | fonts.packages = with pkgs; [ |
@@ -117,7 +117,7 @@ | ||
| 117 | 117 | |
| 118 | 118 | # --- Theming (system side) ------------------------------------------------------ |
| 119 | 119 | |
| 120 | - # Qt apps use qt6ct + Kvantum (QT_QPA_PLATFORMTHEME is set in hyprland.lua). | |
| 120 | + # Qt apps use qt6ct + Kvantum (set up by Home Manager, modules/home/theming.nix). | |
| 121 | 121 | qt = { |
| 122 | 122 | enable = true; |
| 123 | 123 | platformTheme = "qt5ct"; |
modifiedmodules/nixos/nvidia.nix
@@ -55,7 +55,7 @@ | ||
| 55 | 55 | }; |
| 56 | 56 | |
| 57 | 57 | # libva-nvidia-driver: Firefox hardware video decoding (see the NVD_BACKEND |
| 58 | - # and LIBVA_DRIVER_NAME lines in hyprland.lua). | |
| 58 | + # and LIBVA_DRIVER_NAME lines in modules/home/desktop.nix). | |
| 59 | 59 | hardware.graphics.extraPackages = [ pkgs.nvidia-vaapi-driver ]; |
| 60 | 60 | }) |
| 61 | 61 | ]; |