diff options
291 files changed, 16682 insertions, 6065 deletions
| @@ -1,7 +1,5 @@ | |||
| 1 | **/result | 1 | **/result |
| 2 | **/result-* | 2 | **/result-* |
| 3 | **/#*# | ||
| 4 | **/.#* | ||
| 5 | **/.gup | 3 | **/.gup |
| 6 | .direnv | 4 | .direnv |
| 7 | 5 | ||
| @@ -8,6 +8,12 @@ creation_rules: | |||
| 8 | - path_regex: ^hosts/surtr/email/ca | 8 | - path_regex: ^hosts/surtr/email/ca |
| 9 | key_groups: | 9 | key_groups: |
| 10 | - age: [ *admin_gkleen ] | 10 | - age: [ *admin_gkleen ] |
| 11 | - path_regex: ^home-modules/lmu-hausschrift/ | ||
| 12 | key_groups: | ||
| 13 | - age: [ *admin_gkleen ] | ||
| 14 | - path_regex: ^accounts/gkleen@sif/ | ||
| 15 | key_groups: | ||
| 16 | - age: [ *admin_gkleen ] | ||
| 11 | - path_regex: surtr\/?[^\/]*$ | 17 | - path_regex: surtr\/?[^\/]*$ |
| 12 | key_groups: | 18 | key_groups: |
| 13 | - age: [ *admin_gkleen, *machine_surtr ] | 19 | - age: [ *admin_gkleen, *machine_surtr ] |
| @@ -26,3 +32,6 @@ creation_rules: | |||
| 26 | - path_regex: ^hosts/sif/ | 32 | - path_regex: ^hosts/sif/ |
| 27 | key_groups: | 33 | key_groups: |
| 28 | - age: [ *admin_gkleen, *machine_sif ] | 34 | - age: [ *admin_gkleen, *machine_sif ] |
| 35 | - path_regex: ^modules/nix-access-tokens/ | ||
| 36 | key_groups: | ||
| 37 | - age: [ *admin_gkleen, *machine_sif, *machine_surtr, *machine_vidhar ] | ||
diff --git a/_sources/generated.json b/_sources/generated.json index 06346969..dd73e455 100644 --- a/_sources/generated.json +++ b/_sources/generated.json | |||
| @@ -20,23 +20,9 @@ | |||
| 20 | }, | 20 | }, |
| 21 | "version": "8ef9a5b73e5d1063cf912c70027c655fb19d1109" | 21 | "version": "8ef9a5b73e5d1063cf912c70027c655fb19d1109" |
| 22 | }, | 22 | }, |
| 23 | "batman-adv": { | ||
| 24 | "cargoLocks": null, | ||
| 25 | "date": null, | ||
| 26 | "extract": null, | ||
| 27 | "name": "batman-adv", | ||
| 28 | "passthru": null, | ||
| 29 | "pinned": false, | ||
| 30 | "src": { | ||
| 31 | "sha256": "sha256-VYyIkH5IFfKN6EOHZxSx6AaepD3a22/hhmLhqkle5Z0=", | ||
| 32 | "type": "tarball", | ||
| 33 | "url": "https://downloads.open-mesh.org/batman/stable/sources/batman-adv/batman-adv-2024.4.tar.gz" | ||
| 34 | }, | ||
| 35 | "version": "2024.4" | ||
| 36 | }, | ||
| 37 | "bpf-examples": { | 23 | "bpf-examples": { |
| 38 | "cargoLocks": null, | 24 | "cargoLocks": null, |
| 39 | "date": "2024-01-31", | 25 | "date": "2025-09-19", |
| 40 | "extract": null, | 26 | "extract": null, |
| 41 | "name": "bpf-examples", | 27 | "name": "bpf-examples", |
| 42 | "passthru": null, | 28 | "passthru": null, |
| @@ -48,12 +34,12 @@ | |||
| 48 | "name": null, | 34 | "name": null, |
| 49 | "owner": "xdp-project", | 35 | "owner": "xdp-project", |
| 50 | "repo": "bpf-examples", | 36 | "repo": "bpf-examples", |
| 51 | "rev": "5343ed3377471c7b7ef2237526c8bdc0f00a0cef", | 37 | "rev": "d621b4fb25c4877415a563887606ab0fe47ad59a", |
| 52 | "sha256": "sha256-vKVI8pQ17BNWLKm8wwpyNkLslnB9E2CAZTS6EP5lDT0=", | 38 | "sha256": "sha256-IQBTYtqHsghbb/Mpx29Hjr9AsLVG6w3BqfJYSKoMotU=", |
| 53 | "sparseCheckout": [], | 39 | "sparseCheckout": [], |
| 54 | "type": "github" | 40 | "type": "github" |
| 55 | }, | 41 | }, |
| 56 | "version": "5343ed3377471c7b7ef2237526c8bdc0f00a0cef" | 42 | "version": "d621b4fb25c4877415a563887606ab0fe47ad59a" |
| 57 | }, | 43 | }, |
| 58 | "emacs-scratch_el": { | 44 | "emacs-scratch_el": { |
| 59 | "cargoLocks": null, | 45 | "cargoLocks": null, |
| @@ -90,12 +76,12 @@ | |||
| 90 | "name": null, | 76 | "name": null, |
| 91 | "owner": "Mange", | 77 | "owner": "Mange", |
| 92 | "repo": "emoji-data", | 78 | "repo": "emoji-data", |
| 93 | "rev": "v2.6", | 79 | "rev": "v2.7", |
| 94 | "sha256": "sha256-6nBiT9q139P1pXLqkV1JejE0s2rZn1gUbNsejXJR6RU=", | 80 | "sha256": "sha256-bUFh0Q7xcnKTBgVBUJU8BH6zzq1Y3krLfJJAgx5TqKs=", |
| 95 | "sparseCheckout": [], | 81 | "sparseCheckout": [], |
| 96 | "type": "github" | 82 | "type": "github" |
| 97 | }, | 83 | }, |
| 98 | "version": "v2.6" | 84 | "version": "v2.7" |
| 99 | }, | 85 | }, |
| 100 | "lesspipe": { | 86 | "lesspipe": { |
| 101 | "cargoLocks": null, | 87 | "cargoLocks": null, |
| @@ -105,11 +91,31 @@ | |||
| 105 | "passthru": null, | 91 | "passthru": null, |
| 106 | "pinned": false, | 92 | "pinned": false, |
| 107 | "src": { | 93 | "src": { |
| 108 | "sha256": "sha256-s6oV77sOPYAd8CA51KZK6nydWIh8oK2+SUpPfm7yehg=", | 94 | "sha256": "sha256-yb3IzdaMiv1PwqHOfSyHvmWXyStvK/XXC49saXVAJFU=", |
| 109 | "type": "tarball", | 95 | "type": "tarball", |
| 110 | "url": "https://github.com/wofr06/lesspipe/archive/refs/tags/v2.16.tar.gz" | 96 | "url": "https://github.com/wofr06/lesspipe/archive/refs/tags/v2.20.tar.gz" |
| 97 | }, | ||
| 98 | "version": "2.20" | ||
| 99 | }, | ||
| 100 | "mako": { | ||
| 101 | "cargoLocks": null, | ||
| 102 | "date": "2025-09-11", | ||
| 103 | "extract": null, | ||
| 104 | "name": "mako", | ||
| 105 | "passthru": null, | ||
| 106 | "pinned": false, | ||
| 107 | "src": { | ||
| 108 | "deepClone": false, | ||
| 109 | "fetchSubmodules": false, | ||
| 110 | "leaveDotGit": false, | ||
| 111 | "name": null, | ||
| 112 | "rev": "8318972590420c042c0177af16e26a1768550fab", | ||
| 113 | "sha256": "sha256-Y/exF/Pv60E31Zl+M1zboWkmkZgOUCA3l93OKbtvZ+g=", | ||
| 114 | "sparseCheckout": [], | ||
| 115 | "type": "git", | ||
| 116 | "url": "https://github.com/emersion/mako" | ||
| 111 | }, | 117 | }, |
| 112 | "version": "2.16" | 118 | "version": "8318972590420c042c0177af16e26a1768550fab" |
| 113 | }, | 119 | }, |
| 114 | "mpv-autosave": { | 120 | "mpv-autosave": { |
| 115 | "cargoLocks": null, | 121 | "cargoLocks": null, |
| @@ -196,7 +202,7 @@ | |||
| 196 | }, | 202 | }, |
| 197 | "mpv-reload": { | 203 | "mpv-reload": { |
| 198 | "cargoLocks": null, | 204 | "cargoLocks": null, |
| 199 | "date": "2024-03-22", | 205 | "date": "2025-02-07", |
| 200 | "extract": null, | 206 | "extract": null, |
| 201 | "name": "mpv-reload", | 207 | "name": "mpv-reload", |
| 202 | "passthru": null, | 208 | "passthru": null, |
| @@ -208,16 +214,16 @@ | |||
| 208 | "name": null, | 214 | "name": null, |
| 209 | "owner": "4e6", | 215 | "owner": "4e6", |
| 210 | "repo": "mpv-reload", | 216 | "repo": "mpv-reload", |
| 211 | "rev": "1a6a9383ba1774708fddbd976e7a9b72c3eec938", | 217 | "rev": "60e6fb1c578aa9af80d725857dac8e439095b033", |
| 212 | "sha256": "sha256-BshxCjec/UNGyiC0/g1Rai2NvG2qOIHXDDEUYwwdij0=", | 218 | "sha256": "sha256-elA9bi5ov5MbehLD1kyS4Z8zKgTc+8dcOPq32muRGcE=", |
| 213 | "sparseCheckout": [], | 219 | "sparseCheckout": [], |
| 214 | "type": "github" | 220 | "type": "github" |
| 215 | }, | 221 | }, |
| 216 | "version": "1a6a9383ba1774708fddbd976e7a9b72c3eec938" | 222 | "version": "60e6fb1c578aa9af80d725857dac8e439095b033" |
| 217 | }, | 223 | }, |
| 218 | "mpv-subselect": { | 224 | "mpv-subselect": { |
| 219 | "cargoLocks": null, | 225 | "cargoLocks": null, |
| 220 | "date": "2024-08-31", | 226 | "date": "2025-04-04", |
| 221 | "extract": null, | 227 | "extract": null, |
| 222 | "name": "mpv-subselect", | 228 | "name": "mpv-subselect", |
| 223 | "passthru": null, | 229 | "passthru": null, |
| @@ -227,13 +233,13 @@ | |||
| 227 | "fetchSubmodules": false, | 233 | "fetchSubmodules": false, |
| 228 | "leaveDotGit": false, | 234 | "leaveDotGit": false, |
| 229 | "name": null, | 235 | "name": null, |
| 230 | "rev": "9b0043ba6042ba01fdd2533281313b70cbc98be4", | 236 | "rev": "26d24a0fd1d69988eaedda6056a2c87d0a55b6cb", |
| 231 | "sha256": "sha256-ZLWwkwrFaOg7PnuC23VaZ0P3zMhm1JmVf0eH9lqO0BY=", | 237 | "sha256": "sha256-+eVga4b7KIBnfrtmlgq/0HNjQVS3SK6YWVXCPvOeOOc=", |
| 232 | "sparseCheckout": [], | 238 | "sparseCheckout": [], |
| 233 | "type": "git", | 239 | "type": "git", |
| 234 | "url": "https://github.com/CogentRedTester/mpv-sub-select" | 240 | "url": "https://github.com/CogentRedTester/mpv-sub-select" |
| 235 | }, | 241 | }, |
| 236 | "version": "9b0043ba6042ba01fdd2533281313b70cbc98be4" | 242 | "version": "26d24a0fd1d69988eaedda6056a2c87d0a55b6cb" |
| 237 | }, | 243 | }, |
| 238 | "mpv-youtube-quality": { | 244 | "mpv-youtube-quality": { |
| 239 | "cargoLocks": null, | 245 | "cargoLocks": null, |
| @@ -255,6 +261,36 @@ | |||
| 255 | }, | 261 | }, |
| 256 | "version": "1f8c31457459ffc28cd1c3f3c2235a53efad7148" | 262 | "version": "1f8c31457459ffc28cd1c3f3c2235a53efad7148" |
| 257 | }, | 263 | }, |
| 264 | "netbootxyz-efi": { | ||
| 265 | "cargoLocks": null, | ||
| 266 | "date": null, | ||
| 267 | "extract": null, | ||
| 268 | "name": "netbootxyz-efi", | ||
| 269 | "passthru": null, | ||
| 270 | "pinned": false, | ||
| 271 | "src": { | ||
| 272 | "name": null, | ||
| 273 | "sha256": "sha256-ipbZJ0mPCuwzb/TDtXXUBTuWOcSsKGAJ1GEGIgB2G7E=", | ||
| 274 | "type": "url", | ||
| 275 | "url": "https://github.com/netbootxyz/netboot.xyz/releases/download/2.0.88/netboot.xyz.efi" | ||
| 276 | }, | ||
| 277 | "version": "2.0.88" | ||
| 278 | }, | ||
| 279 | "netbootxyz-lkrn": { | ||
| 280 | "cargoLocks": null, | ||
| 281 | "date": null, | ||
| 282 | "extract": null, | ||
| 283 | "name": "netbootxyz-lkrn", | ||
| 284 | "passthru": null, | ||
| 285 | "pinned": false, | ||
| 286 | "src": { | ||
| 287 | "name": null, | ||
| 288 | "sha256": "sha256-igy3O30noS25dU7ZnHuKrWqLLkjjd/L46IdCTd038dI=", | ||
| 289 | "type": "url", | ||
| 290 | "url": "https://github.com/netbootxyz/netboot.xyz/releases/download/2.0.88/netboot.xyz.lkrn" | ||
| 291 | }, | ||
| 292 | "version": "2.0.88" | ||
| 293 | }, | ||
| 258 | "postfix-mta-sts-resolver": { | 294 | "postfix-mta-sts-resolver": { |
| 259 | "cargoLocks": null, | 295 | "cargoLocks": null, |
| 260 | "date": null, | 296 | "date": null, |
| @@ -263,11 +299,11 @@ | |||
| 263 | "passthru": null, | 299 | "passthru": null, |
| 264 | "pinned": false, | 300 | "pinned": false, |
| 265 | "src": { | 301 | "src": { |
| 266 | "sha256": "sha256-wpBbT/KXd2iU6Jn6Y/6i5C+Wv3OsUTjFcJDhUm6w46I=", | 302 | "sha256": "sha256-DrPWxAlzdtb5K0Z+yVi+rL1h7CyLj0/Fiio8B2H/Ssg=", |
| 267 | "type": "tarball", | 303 | "type": "tarball", |
| 268 | "url": "https://github.com/Snawoot/postfix-mta-sts-resolver/archive/refs/tags/v1.4.0.tar.gz" | 304 | "url": "https://github.com/Snawoot/postfix-mta-sts-resolver/archive/refs/tags/v1.5.0.tar.gz" |
| 269 | }, | 305 | }, |
| 270 | "version": "1.4.0" | 306 | "version": "1.5.0" |
| 271 | }, | 307 | }, |
| 272 | "postfwd": { | 308 | "postfwd": { |
| 273 | "cargoLocks": null, | 309 | "cargoLocks": null, |
| @@ -291,11 +327,11 @@ | |||
| 291 | "passthru": null, | 327 | "passthru": null, |
| 292 | "pinned": false, | 328 | "pinned": false, |
| 293 | "src": { | 329 | "src": { |
| 294 | "sha256": "sha256-mA84Bnq5JF0BGfqHhcCzTef5nDotLgQuiyg3/zOPqTE=", | 330 | "sha256": "sha256-mg4iyp/heYzSoK+pGSMYfZb5UauoBMrEL1QPH6EoJ8o=", |
| 295 | "type": "tarball", | 331 | "type": "tarball", |
| 296 | "url": "https://github.com/hansmi/prometheus-lvm-exporter/archive/refs/tags/v0.3.3.tar.gz" | 332 | "url": "https://github.com/hansmi/prometheus-lvm-exporter/archive/refs/tags/v0.6.1.tar.gz" |
| 297 | }, | 333 | }, |
| 298 | "version": "0.3.3" | 334 | "version": "0.6.1" |
| 299 | }, | 335 | }, |
| 300 | "psql-versioning": { | 336 | "psql-versioning": { |
| 301 | "cargoLocks": null, | 337 | "cargoLocks": null, |
| @@ -317,6 +353,26 @@ | |||
| 317 | }, | 353 | }, |
| 318 | "version": "330cb9da36651b701085ad53ae75ff296d02202a" | 354 | "version": "330cb9da36651b701085ad53ae75ff296d02202a" |
| 319 | }, | 355 | }, |
| 356 | "quickshell": { | ||
| 357 | "cargoLocks": null, | ||
| 358 | "date": "2025-09-19", | ||
| 359 | "extract": null, | ||
| 360 | "name": "quickshell", | ||
| 361 | "passthru": null, | ||
| 362 | "pinned": false, | ||
| 363 | "src": { | ||
| 364 | "deepClone": false, | ||
| 365 | "fetchSubmodules": false, | ||
| 366 | "leaveDotGit": false, | ||
| 367 | "name": null, | ||
| 368 | "rev": "e9a574d919a89602d2868621576b2ccae54a5cb0", | ||
| 369 | "sha256": "sha256-wOv1guIi9THD1NjOtBU2Xh/Avg9xv7nIjsfFSkr1NeQ=", | ||
| 370 | "sparseCheckout": [], | ||
| 371 | "type": "git", | ||
| 372 | "url": "https://git.outfoxxed.me/quickshell/quickshell.git" | ||
| 373 | }, | ||
| 374 | "version": "e9a574d919a89602d2868621576b2ccae54a5cb0" | ||
| 375 | }, | ||
| 320 | "scutiger": { | 376 | "scutiger": { |
| 321 | "cargoLocks": null, | 377 | "cargoLocks": null, |
| 322 | "date": null, | 378 | "date": null, |
| @@ -359,6 +415,26 @@ | |||
| 359 | }, | 415 | }, |
| 360 | "version": "0.2.1" | 416 | "version": "0.2.1" |
| 361 | }, | 417 | }, |
| 418 | "swayosd": { | ||
| 419 | "cargoLocks": null, | ||
| 420 | "date": "2025-07-07", | ||
| 421 | "extract": null, | ||
| 422 | "name": "swayosd", | ||
| 423 | "passthru": null, | ||
| 424 | "pinned": false, | ||
| 425 | "src": { | ||
| 426 | "deepClone": false, | ||
| 427 | "fetchSubmodules": false, | ||
| 428 | "leaveDotGit": false, | ||
| 429 | "name": null, | ||
| 430 | "rev": "73aed75146b81aaf67c4301353790ff5a17aed1f", | ||
| 431 | "sha256": "sha256-p31HNelptAw7Sk0NmYP4FkoUCdA5uAsrXC20JJp24Vw=", | ||
| 432 | "sparseCheckout": [], | ||
| 433 | "type": "git", | ||
| 434 | "url": "https://github.com/ErikReider/SwayOSD" | ||
| 435 | }, | ||
| 436 | "version": "73aed75146b81aaf67c4301353790ff5a17aed1f" | ||
| 437 | }, | ||
| 362 | "tomorrow-night-paradise-theme": { | 438 | "tomorrow-night-paradise-theme": { |
| 363 | "cargoLocks": null, | 439 | "cargoLocks": null, |
| 364 | "date": "2012-06-04", | 440 | "date": "2012-06-04", |
| @@ -381,7 +457,7 @@ | |||
| 381 | }, | 457 | }, |
| 382 | "v4l2loopback": { | 458 | "v4l2loopback": { |
| 383 | "cargoLocks": null, | 459 | "cargoLocks": null, |
| 384 | "date": "2024-11-26", | 460 | "date": "2025-08-18", |
| 385 | "extract": null, | 461 | "extract": null, |
| 386 | "name": "v4l2loopback", | 462 | "name": "v4l2loopback", |
| 387 | "passthru": null, | 463 | "passthru": null, |
| @@ -393,16 +469,16 @@ | |||
| 393 | "name": null, | 469 | "name": null, |
| 394 | "owner": "umlaeute", | 470 | "owner": "umlaeute", |
| 395 | "repo": "v4l2loopback", | 471 | "repo": "v4l2loopback", |
| 396 | "rev": "e750af9eb17d729b8c5257a4bcd2faba2b28029c", | 472 | "rev": "5eaa59e7c41d0e6f35a6c14c3b756d94d25f58ed", |
| 397 | "sha256": "sha256-ePA1LcxQInrLLpbZ7Wljv75lWl6V6s9KkdMp0tF1vhk=", | 473 | "sha256": "sha256-YcSpNfItvUdPVirlDyGdYuCnVvxHhh780x+OI5VNZmE=", |
| 398 | "sparseCheckout": [], | 474 | "sparseCheckout": [], |
| 399 | "type": "github" | 475 | "type": "github" |
| 400 | }, | 476 | }, |
| 401 | "version": "e750af9eb17d729b8c5257a4bcd2faba2b28029c" | 477 | "version": "5eaa59e7c41d0e6f35a6c14c3b756d94d25f58ed" |
| 402 | }, | 478 | }, |
| 403 | "xcompose": { | 479 | "xcompose": { |
| 404 | "cargoLocks": null, | 480 | "cargoLocks": null, |
| 405 | "date": "2022-09-14", | 481 | "date": "2025-06-05", |
| 406 | "extract": null, | 482 | "extract": null, |
| 407 | "name": "xcompose", | 483 | "name": "xcompose", |
| 408 | "passthru": null, | 484 | "passthru": null, |
| @@ -414,12 +490,12 @@ | |||
| 414 | "name": null, | 490 | "name": null, |
| 415 | "owner": "kragen", | 491 | "owner": "kragen", |
| 416 | "repo": "xcompose", | 492 | "repo": "xcompose", |
| 417 | "rev": "cd8d3e622f547ec9f83d7f64f51d4a27ee812681", | 493 | "rev": "4d8eab4d05a19537ce79294ae0459fdae78ffb20", |
| 418 | "sha256": "sha256-fkl2lDv/DdrqPjVsEUKSRD3BNGwTjTsA0ovI8akFI6U=", | 494 | "sha256": "sha256-vKY4u5Z2IL111orLLkkF4AoVzqluKG/VQhNUUCqO/k8=", |
| 419 | "sparseCheckout": [], | 495 | "sparseCheckout": [], |
| 420 | "type": "github" | 496 | "type": "github" |
| 421 | }, | 497 | }, |
| 422 | "version": "cd8d3e622f547ec9f83d7f64f51d4a27ee812681" | 498 | "version": "4d8eab4d05a19537ce79294ae0459fdae78ffb20" |
| 423 | }, | 499 | }, |
| 424 | "yt-dlp": { | 500 | "yt-dlp": { |
| 425 | "cargoLocks": null, | 501 | "cargoLocks": null, |
| @@ -430,10 +506,10 @@ | |||
| 430 | "pinned": false, | 506 | "pinned": false, |
| 431 | "src": { | 507 | "src": { |
| 432 | "name": null, | 508 | "name": null, |
| 433 | "sha256": "sha256-dD2+CB6ocb4/X/CD4s2V2oZt6nc/xwrmsQmDjPv3KsQ=", | 509 | "sha256": "sha256-koKtHerbTJCy5tO8+fNgq/iMXy5LqDba17UTh7CG11c=", |
| 434 | "type": "url", | 510 | "type": "url", |
| 435 | "url": "https://pypi.org/packages/source/y/yt_dlp/yt_dlp-2024.12.6.tar.gz" | 511 | "url": "https://pypi.org/packages/source/y/yt_dlp/yt_dlp-2025.9.23.tar.gz" |
| 436 | }, | 512 | }, |
| 437 | "version": "2024.12.6" | 513 | "version": "2025.9.23" |
| 438 | } | 514 | } |
| 439 | } \ No newline at end of file | 515 | } \ No newline at end of file |
diff --git a/_sources/generated.nix b/_sources/generated.nix index b07979a7..8eac064b 100644 --- a/_sources/generated.nix +++ b/_sources/generated.nix | |||
| @@ -16,25 +16,17 @@ | |||
| 16 | }; | 16 | }; |
| 17 | date = "2021-05-30"; | 17 | date = "2021-05-30"; |
| 18 | }; | 18 | }; |
| 19 | batman-adv = { | ||
| 20 | pname = "batman-adv"; | ||
| 21 | version = "2024.4"; | ||
| 22 | src = fetchTarball { | ||
| 23 | url = "https://downloads.open-mesh.org/batman/stable/sources/batman-adv/batman-adv-2024.4.tar.gz"; | ||
| 24 | sha256 = "sha256-VYyIkH5IFfKN6EOHZxSx6AaepD3a22/hhmLhqkle5Z0="; | ||
| 25 | }; | ||
| 26 | }; | ||
| 27 | bpf-examples = { | 19 | bpf-examples = { |
| 28 | pname = "bpf-examples"; | 20 | pname = "bpf-examples"; |
| 29 | version = "5343ed3377471c7b7ef2237526c8bdc0f00a0cef"; | 21 | version = "d621b4fb25c4877415a563887606ab0fe47ad59a"; |
| 30 | src = fetchFromGitHub { | 22 | src = fetchFromGitHub { |
| 31 | owner = "xdp-project"; | 23 | owner = "xdp-project"; |
| 32 | repo = "bpf-examples"; | 24 | repo = "bpf-examples"; |
| 33 | rev = "5343ed3377471c7b7ef2237526c8bdc0f00a0cef"; | 25 | rev = "d621b4fb25c4877415a563887606ab0fe47ad59a"; |
| 34 | fetchSubmodules = true; | 26 | fetchSubmodules = true; |
| 35 | sha256 = "sha256-vKVI8pQ17BNWLKm8wwpyNkLslnB9E2CAZTS6EP5lDT0="; | 27 | sha256 = "sha256-IQBTYtqHsghbb/Mpx29Hjr9AsLVG6w3BqfJYSKoMotU="; |
| 36 | }; | 28 | }; |
| 37 | date = "2024-01-31"; | 29 | date = "2025-09-19"; |
| 38 | }; | 30 | }; |
| 39 | emacs-scratch_el = { | 31 | emacs-scratch_el = { |
| 40 | pname = "emacs-scratch_el"; | 32 | pname = "emacs-scratch_el"; |
| @@ -50,23 +42,37 @@ | |||
| 50 | }; | 42 | }; |
| 51 | emoji-data = { | 43 | emoji-data = { |
| 52 | pname = "emoji-data"; | 44 | pname = "emoji-data"; |
| 53 | version = "v2.6"; | 45 | version = "v2.7"; |
| 54 | src = fetchFromGitHub { | 46 | src = fetchFromGitHub { |
| 55 | owner = "Mange"; | 47 | owner = "Mange"; |
| 56 | repo = "emoji-data"; | 48 | repo = "emoji-data"; |
| 57 | rev = "v2.6"; | 49 | rev = "v2.7"; |
| 58 | fetchSubmodules = true; | 50 | fetchSubmodules = true; |
| 59 | sha256 = "sha256-6nBiT9q139P1pXLqkV1JejE0s2rZn1gUbNsejXJR6RU="; | 51 | sha256 = "sha256-bUFh0Q7xcnKTBgVBUJU8BH6zzq1Y3krLfJJAgx5TqKs="; |
| 60 | }; | 52 | }; |
| 61 | }; | 53 | }; |
| 62 | lesspipe = { | 54 | lesspipe = { |
| 63 | pname = "lesspipe"; | 55 | pname = "lesspipe"; |
| 64 | version = "2.16"; | 56 | version = "2.20"; |
| 65 | src = fetchTarball { | 57 | src = fetchTarball { |
| 66 | url = "https://github.com/wofr06/lesspipe/archive/refs/tags/v2.16.tar.gz"; | 58 | url = "https://github.com/wofr06/lesspipe/archive/refs/tags/v2.20.tar.gz"; |
| 67 | sha256 = "sha256-s6oV77sOPYAd8CA51KZK6nydWIh8oK2+SUpPfm7yehg="; | 59 | sha256 = "sha256-yb3IzdaMiv1PwqHOfSyHvmWXyStvK/XXC49saXVAJFU="; |
| 68 | }; | 60 | }; |
| 69 | }; | 61 | }; |
| 62 | mako = { | ||
| 63 | pname = "mako"; | ||
| 64 | version = "8318972590420c042c0177af16e26a1768550fab"; | ||
| 65 | src = fetchgit { | ||
| 66 | url = "https://github.com/emersion/mako"; | ||
| 67 | rev = "8318972590420c042c0177af16e26a1768550fab"; | ||
| 68 | fetchSubmodules = false; | ||
| 69 | deepClone = false; | ||
| 70 | leaveDotGit = false; | ||
| 71 | sparseCheckout = [ ]; | ||
| 72 | sha256 = "sha256-Y/exF/Pv60E31Zl+M1zboWkmkZgOUCA3l93OKbtvZ+g="; | ||
| 73 | }; | ||
| 74 | date = "2025-09-11"; | ||
| 75 | }; | ||
| 70 | mpv-autosave = { | 76 | mpv-autosave = { |
| 71 | pname = "mpv-autosave"; | 77 | pname = "mpv-autosave"; |
| 72 | version = "744c3ee61d2f0a8e9bb4e308dec6897215ae4704"; | 78 | version = "744c3ee61d2f0a8e9bb4e308dec6897215ae4704"; |
| @@ -118,29 +124,29 @@ | |||
| 118 | }; | 124 | }; |
| 119 | mpv-reload = { | 125 | mpv-reload = { |
| 120 | pname = "mpv-reload"; | 126 | pname = "mpv-reload"; |
| 121 | version = "1a6a9383ba1774708fddbd976e7a9b72c3eec938"; | 127 | version = "60e6fb1c578aa9af80d725857dac8e439095b033"; |
| 122 | src = fetchFromGitHub { | 128 | src = fetchFromGitHub { |
| 123 | owner = "4e6"; | 129 | owner = "4e6"; |
| 124 | repo = "mpv-reload"; | 130 | repo = "mpv-reload"; |
| 125 | rev = "1a6a9383ba1774708fddbd976e7a9b72c3eec938"; | 131 | rev = "60e6fb1c578aa9af80d725857dac8e439095b033"; |
| 126 | fetchSubmodules = false; | 132 | fetchSubmodules = false; |
| 127 | sha256 = "sha256-BshxCjec/UNGyiC0/g1Rai2NvG2qOIHXDDEUYwwdij0="; | 133 | sha256 = "sha256-elA9bi5ov5MbehLD1kyS4Z8zKgTc+8dcOPq32muRGcE="; |
| 128 | }; | 134 | }; |
| 129 | date = "2024-03-22"; | 135 | date = "2025-02-07"; |
| 130 | }; | 136 | }; |
| 131 | mpv-subselect = { | 137 | mpv-subselect = { |
| 132 | pname = "mpv-subselect"; | 138 | pname = "mpv-subselect"; |
| 133 | version = "9b0043ba6042ba01fdd2533281313b70cbc98be4"; | 139 | version = "26d24a0fd1d69988eaedda6056a2c87d0a55b6cb"; |
| 134 | src = fetchgit { | 140 | src = fetchgit { |
| 135 | url = "https://github.com/CogentRedTester/mpv-sub-select"; | 141 | url = "https://github.com/CogentRedTester/mpv-sub-select"; |
| 136 | rev = "9b0043ba6042ba01fdd2533281313b70cbc98be4"; | 142 | rev = "26d24a0fd1d69988eaedda6056a2c87d0a55b6cb"; |
| 137 | fetchSubmodules = false; | 143 | fetchSubmodules = false; |
| 138 | deepClone = false; | 144 | deepClone = false; |
| 139 | leaveDotGit = false; | 145 | leaveDotGit = false; |
| 140 | sparseCheckout = [ ]; | 146 | sparseCheckout = [ ]; |
| 141 | sha256 = "sha256-ZLWwkwrFaOg7PnuC23VaZ0P3zMhm1JmVf0eH9lqO0BY="; | 147 | sha256 = "sha256-+eVga4b7KIBnfrtmlgq/0HNjQVS3SK6YWVXCPvOeOOc="; |
| 142 | }; | 148 | }; |
| 143 | date = "2024-08-31"; | 149 | date = "2025-04-04"; |
| 144 | }; | 150 | }; |
| 145 | mpv-youtube-quality = { | 151 | mpv-youtube-quality = { |
| 146 | pname = "mpv-youtube-quality"; | 152 | pname = "mpv-youtube-quality"; |
| @@ -156,12 +162,28 @@ | |||
| 156 | }; | 162 | }; |
| 157 | date = "2020-02-10"; | 163 | date = "2020-02-10"; |
| 158 | }; | 164 | }; |
| 165 | netbootxyz-efi = { | ||
| 166 | pname = "netbootxyz-efi"; | ||
| 167 | version = "2.0.88"; | ||
| 168 | src = fetchurl { | ||
| 169 | url = "https://github.com/netbootxyz/netboot.xyz/releases/download/2.0.88/netboot.xyz.efi"; | ||
| 170 | sha256 = "sha256-ipbZJ0mPCuwzb/TDtXXUBTuWOcSsKGAJ1GEGIgB2G7E="; | ||
| 171 | }; | ||
| 172 | }; | ||
| 173 | netbootxyz-lkrn = { | ||
| 174 | pname = "netbootxyz-lkrn"; | ||
| 175 | version = "2.0.88"; | ||
| 176 | src = fetchurl { | ||
| 177 | url = "https://github.com/netbootxyz/netboot.xyz/releases/download/2.0.88/netboot.xyz.lkrn"; | ||
| 178 | sha256 = "sha256-igy3O30noS25dU7ZnHuKrWqLLkjjd/L46IdCTd038dI="; | ||
| 179 | }; | ||
| 180 | }; | ||
| 159 | postfix-mta-sts-resolver = { | 181 | postfix-mta-sts-resolver = { |
| 160 | pname = "postfix-mta-sts-resolver"; | 182 | pname = "postfix-mta-sts-resolver"; |
| 161 | version = "1.4.0"; | 183 | version = "1.5.0"; |
| 162 | src = fetchTarball { | 184 | src = fetchTarball { |
| 163 | url = "https://github.com/Snawoot/postfix-mta-sts-resolver/archive/refs/tags/v1.4.0.tar.gz"; | 185 | url = "https://github.com/Snawoot/postfix-mta-sts-resolver/archive/refs/tags/v1.5.0.tar.gz"; |
| 164 | sha256 = "sha256-wpBbT/KXd2iU6Jn6Y/6i5C+Wv3OsUTjFcJDhUm6w46I="; | 186 | sha256 = "sha256-DrPWxAlzdtb5K0Z+yVi+rL1h7CyLj0/Fiio8B2H/Ssg="; |
| 165 | }; | 187 | }; |
| 166 | }; | 188 | }; |
| 167 | postfwd = { | 189 | postfwd = { |
| @@ -174,10 +196,10 @@ | |||
| 174 | }; | 196 | }; |
| 175 | prometheus-lvm-exporter = { | 197 | prometheus-lvm-exporter = { |
| 176 | pname = "prometheus-lvm-exporter"; | 198 | pname = "prometheus-lvm-exporter"; |
| 177 | version = "0.3.3"; | 199 | version = "0.6.1"; |
| 178 | src = fetchTarball { | 200 | src = fetchTarball { |
| 179 | url = "https://github.com/hansmi/prometheus-lvm-exporter/archive/refs/tags/v0.3.3.tar.gz"; | 201 | url = "https://github.com/hansmi/prometheus-lvm-exporter/archive/refs/tags/v0.6.1.tar.gz"; |
| 180 | sha256 = "sha256-mA84Bnq5JF0BGfqHhcCzTef5nDotLgQuiyg3/zOPqTE="; | 202 | sha256 = "sha256-mg4iyp/heYzSoK+pGSMYfZb5UauoBMrEL1QPH6EoJ8o="; |
| 181 | }; | 203 | }; |
| 182 | }; | 204 | }; |
| 183 | psql-versioning = { | 205 | psql-versioning = { |
| @@ -194,6 +216,20 @@ | |||
| 194 | }; | 216 | }; |
| 195 | date = "2023-11-23"; | 217 | date = "2023-11-23"; |
| 196 | }; | 218 | }; |
| 219 | quickshell = { | ||
| 220 | pname = "quickshell"; | ||
| 221 | version = "e9a574d919a89602d2868621576b2ccae54a5cb0"; | ||
| 222 | src = fetchgit { | ||
| 223 | url = "https://git.outfoxxed.me/quickshell/quickshell.git"; | ||
| 224 | rev = "e9a574d919a89602d2868621576b2ccae54a5cb0"; | ||
| 225 | fetchSubmodules = false; | ||
| 226 | deepClone = false; | ||
| 227 | leaveDotGit = false; | ||
| 228 | sparseCheckout = [ ]; | ||
| 229 | sha256 = "sha256-wOv1guIi9THD1NjOtBU2Xh/Avg9xv7nIjsfFSkr1NeQ="; | ||
| 230 | }; | ||
| 231 | date = "2025-09-19"; | ||
| 232 | }; | ||
| 197 | scutiger = { | 233 | scutiger = { |
| 198 | pname = "scutiger"; | 234 | pname = "scutiger"; |
| 199 | version = "0.2.0"; | 235 | version = "0.2.0"; |
| @@ -218,6 +254,20 @@ | |||
| 218 | sha256 = "sha256-7d/0fepOvdswuBGJCCMULB2kXOFBLP78yqX4NmByCF8="; | 254 | sha256 = "sha256-7d/0fepOvdswuBGJCCMULB2kXOFBLP78yqX4NmByCF8="; |
| 219 | }; | 255 | }; |
| 220 | }; | 256 | }; |
| 257 | swayosd = { | ||
| 258 | pname = "swayosd"; | ||
| 259 | version = "73aed75146b81aaf67c4301353790ff5a17aed1f"; | ||
| 260 | src = fetchgit { | ||
| 261 | url = "https://github.com/ErikReider/SwayOSD"; | ||
| 262 | rev = "73aed75146b81aaf67c4301353790ff5a17aed1f"; | ||
| 263 | fetchSubmodules = false; | ||
| 264 | deepClone = false; | ||
| 265 | leaveDotGit = false; | ||
| 266 | sparseCheckout = [ ]; | ||
| 267 | sha256 = "sha256-p31HNelptAw7Sk0NmYP4FkoUCdA5uAsrXC20JJp24Vw="; | ||
| 268 | }; | ||
| 269 | date = "2025-07-07"; | ||
| 270 | }; | ||
| 221 | tomorrow-night-paradise-theme = { | 271 | tomorrow-night-paradise-theme = { |
| 222 | pname = "tomorrow-night-paradise-theme"; | 272 | pname = "tomorrow-night-paradise-theme"; |
| 223 | version = "70225a5bf90d495e13a9260bfdc268632ece0801"; | 273 | version = "70225a5bf90d495e13a9260bfdc268632ece0801"; |
| @@ -234,34 +284,34 @@ | |||
| 234 | }; | 284 | }; |
| 235 | v4l2loopback = { | 285 | v4l2loopback = { |
| 236 | pname = "v4l2loopback"; | 286 | pname = "v4l2loopback"; |
| 237 | version = "e750af9eb17d729b8c5257a4bcd2faba2b28029c"; | 287 | version = "5eaa59e7c41d0e6f35a6c14c3b756d94d25f58ed"; |
| 238 | src = fetchFromGitHub { | 288 | src = fetchFromGitHub { |
| 239 | owner = "umlaeute"; | 289 | owner = "umlaeute"; |
| 240 | repo = "v4l2loopback"; | 290 | repo = "v4l2loopback"; |
| 241 | rev = "e750af9eb17d729b8c5257a4bcd2faba2b28029c"; | 291 | rev = "5eaa59e7c41d0e6f35a6c14c3b756d94d25f58ed"; |
| 242 | fetchSubmodules = true; | 292 | fetchSubmodules = true; |
| 243 | sha256 = "sha256-ePA1LcxQInrLLpbZ7Wljv75lWl6V6s9KkdMp0tF1vhk="; | 293 | sha256 = "sha256-YcSpNfItvUdPVirlDyGdYuCnVvxHhh780x+OI5VNZmE="; |
| 244 | }; | 294 | }; |
| 245 | date = "2024-11-26"; | 295 | date = "2025-08-18"; |
| 246 | }; | 296 | }; |
| 247 | xcompose = { | 297 | xcompose = { |
| 248 | pname = "xcompose"; | 298 | pname = "xcompose"; |
| 249 | version = "cd8d3e622f547ec9f83d7f64f51d4a27ee812681"; | 299 | version = "4d8eab4d05a19537ce79294ae0459fdae78ffb20"; |
| 250 | src = fetchFromGitHub { | 300 | src = fetchFromGitHub { |
| 251 | owner = "kragen"; | 301 | owner = "kragen"; |
| 252 | repo = "xcompose"; | 302 | repo = "xcompose"; |
| 253 | rev = "cd8d3e622f547ec9f83d7f64f51d4a27ee812681"; | 303 | rev = "4d8eab4d05a19537ce79294ae0459fdae78ffb20"; |
| 254 | fetchSubmodules = false; | 304 | fetchSubmodules = false; |
| 255 | sha256 = "sha256-fkl2lDv/DdrqPjVsEUKSRD3BNGwTjTsA0ovI8akFI6U="; | 305 | sha256 = "sha256-vKY4u5Z2IL111orLLkkF4AoVzqluKG/VQhNUUCqO/k8="; |
| 256 | }; | 306 | }; |
| 257 | date = "2022-09-14"; | 307 | date = "2025-06-05"; |
| 258 | }; | 308 | }; |
| 259 | yt-dlp = { | 309 | yt-dlp = { |
| 260 | pname = "yt-dlp"; | 310 | pname = "yt-dlp"; |
| 261 | version = "2024.12.6"; | 311 | version = "2025.9.23"; |
| 262 | src = fetchurl { | 312 | src = fetchurl { |
| 263 | url = "https://pypi.org/packages/source/y/yt_dlp/yt_dlp-2024.12.6.tar.gz"; | 313 | url = "https://pypi.org/packages/source/y/yt_dlp/yt_dlp-2025.9.23.tar.gz"; |
| 264 | sha256 = "sha256-dD2+CB6ocb4/X/CD4s2V2oZt6nc/xwrmsQmDjPv3KsQ="; | 314 | sha256 = "sha256-koKtHerbTJCy5tO8+fNgq/iMXy5LqDba17UTh7CG11c="; |
| 265 | }; | 315 | }; |
| 266 | }; | 316 | }; |
| 267 | } | 317 | } |
diff --git a/accounts/gkleen@eostre.nix b/accounts/gkleen@eostre.nix index 72818d44..28daf3fd 100644 --- a/accounts/gkleen@eostre.nix +++ b/accounts/gkleen@eostre.nix | |||
| @@ -1,16 +1,16 @@ | |||
| 1 | { flake, userName, pkgs, ... }: | 1 | { flake, userName, pkgs, ... }: |
| 2 | { | 2 | { |
| 3 | imports = with flake.nixosModules.userProfiles.${userName}; [ | 3 | imports = with flake.nixosModules.userProfiles.${userName}; [ |
| 4 | zsh utils tmux | 4 | utils |
| 5 | ]; | 5 | ]; |
| 6 | 6 | ||
| 7 | config = { | 7 | config = { |
| 8 | home-manager.users.${userName} = { | 8 | home-manager.users.${userName} = { |
| 9 | home.stateVersion = "20.09"; | 9 | home.stateVersion = "20.09"; |
| 10 | 10 | ||
| 11 | nixpkgs.config = { | 11 | # nixpkgs.config = { |
| 12 | allowUnfree = true; | 12 | # allowUnfree = true; |
| 13 | }; | 13 | # }; |
| 14 | 14 | ||
| 15 | home.packages = with pkgs; [ | 15 | home.packages = with pkgs; [ |
| 16 | thunderbird libreoffice element-desktop keepassxc vlc | 16 | thunderbird libreoffice element-desktop keepassxc vlc |
diff --git a/accounts/gkleen@installer.nix b/accounts/gkleen@installer.nix index c7a418f8..5fe1db38 100644 --- a/accounts/gkleen@installer.nix +++ b/accounts/gkleen@installer.nix | |||
| @@ -1,7 +1,11 @@ | |||
| 1 | { userName, ... }: | 1 | { flake, userName, ... }: |
| 2 | 2 | ||
| 3 | { | 3 | { |
| 4 | home-manager.users.${userName} = { config, ... } : { | 4 | imports = with flake.nixosModules.userProfiles.${userName}; [ |
| 5 | zsh tmux | ||
| 6 | ]; | ||
| 7 | |||
| 8 | config.home-manager.users.${userName} = { config, ... } : { | ||
| 5 | home.stateVersion = config.home.version.release; | 9 | home.stateVersion = config.home.version.release; |
| 6 | }; | 10 | }; |
| 7 | } | 11 | } |
diff --git a/accounts/gkleen@sif/default.nix b/accounts/gkleen@sif/default.nix index 00707e87..36b722e4 100644 --- a/accounts/gkleen@sif/default.nix +++ b/accounts/gkleen@sif/default.nix | |||
| @@ -4,33 +4,6 @@ with lib; | |||
| 4 | 4 | ||
| 5 | let | 5 | let |
| 6 | cfg = config.home-manager.users.${userName}; | 6 | cfg = config.home-manager.users.${userName}; |
| 7 | emacsScratch = pkgs.stdenv.mkDerivation (sources.emacs-scratch_el // rec { | ||
| 8 | phases = [ "installPhase" ]; | ||
| 9 | |||
| 10 | installPhase = '' | ||
| 11 | mkdir -p $out/share/emacs/site-lisp | ||
| 12 | cp $src/scratch.el $out/share/emacs/site-lisp/default.el | ||
| 13 | ''; | ||
| 14 | }); | ||
| 15 | muteScript = pkgs.stdenv.mkDerivation { | ||
| 16 | name = "mute"; | ||
| 17 | src = ./scripts/mute.zsh; | ||
| 18 | |||
| 19 | buildInputs = with pkgs; [ makeWrapper ]; | ||
| 20 | |||
| 21 | phases = [ "installPhase" ]; | ||
| 22 | |||
| 23 | installPhase = '' | ||
| 24 | mkdir -p $out/bin | ||
| 25 | install -m 0755 $src $out/bin/mute | ||
| 26 | wrapProgram $out/bin/mute \ | ||
| 27 | --prefix PATH : ${pkgs.zsh}/bin \ | ||
| 28 | --prefix PATH : ${pkgs.findutils}/bin \ | ||
| 29 | --prefix PATH : ${pkgs.util-linux}/bin \ | ||
| 30 | --prefix PATH : ${pkgs.coreutils}/bin \ | ||
| 31 | --prefix PATH : ${pkgs.pulseaudio}/bin | ||
| 32 | ''; | ||
| 33 | }; | ||
| 34 | wrapElectron = { package, bin ? package.meta.mainProgram or package.pname or (pkgs.lib.strings.nameFromURL package.name "-"), outBin ? bin, sandbox ? true }: pkgs.symlinkJoin { | 7 | wrapElectron = { package, bin ? package.meta.mainProgram or package.pname or (pkgs.lib.strings.nameFromURL package.name "-"), outBin ? bin, sandbox ? true }: pkgs.symlinkJoin { |
| 35 | name = "${package.name}-wrapped"; | 8 | name = "${package.name}-wrapped"; |
| 36 | buildInputs = with pkgs; [ makeWrapper ]; | 9 | buildInputs = with pkgs; [ makeWrapper ]; |
| @@ -47,10 +20,6 @@ let | |||
| 47 | ''; | 20 | ''; |
| 48 | }; | 21 | }; |
| 49 | 22 | ||
| 50 | wrappedChrome = wrapElectron { package = pkgs.google-chrome; outBin = "google-chrome"; }; | ||
| 51 | wrappedZulip = wrapElectron { package = pkgs.zulip; bin = "zulip"; outBin = "zulip"; }; | ||
| 52 | wrappedElementDesktop = wrapElectron { package = pkgs.element-desktop; bin = "element-desktop"; }; | ||
| 53 | wrappedRocketChatDesktop = wrapElectron { package = pkgs.rocketchat-desktop; bin = "rocketchat-desktop"; outBin = "rocketchat"; }; | ||
| 54 | wrappedYTMDesktop = wrapElectron { package = pkgs.ytmdesktop; sandbox = false; }; | 23 | wrappedYTMDesktop = wrapElectron { package = pkgs.ytmdesktop; sandbox = false; }; |
| 55 | 24 | ||
| 56 | wrappedKeepassxc = pkgs.symlinkJoin { | 25 | wrappedKeepassxc = pkgs.symlinkJoin { |
| @@ -63,7 +32,7 @@ let | |||
| 63 | text = '' | 32 | text = '' |
| 64 | [D-BUS Service] | 33 | [D-BUS Service] |
| 65 | Name=org.keepassxc.KeePassXC.MainWindow | 34 | Name=org.keepassxc.KeePassXC.MainWindow |
| 66 | Exec=${pkgs.coreutils}/bin/false | 35 | Exec=${lib.getExe' pkgs.coreutils "false"} |
| 67 | SystemdService=keepassxc.service | 36 | SystemdService=keepassxc.service |
| 68 | ''; | 37 | ''; |
| 69 | }) | 38 | }) |
| @@ -73,35 +42,48 @@ let | |||
| 73 | text = '' | 42 | text = '' |
| 74 | [D-BUS Service] | 43 | [D-BUS Service] |
| 75 | Name=org.freedesktop.secrets | 44 | Name=org.freedesktop.secrets |
| 76 | Exec=${pkgs.coreutils}/bin/false | 45 | Exec=${lib.getExe' pkgs.coreutils "false"} |
| 77 | SystemdService=keepassxc.service | 46 | SystemdService=keepassxc.service |
| 78 | ''; | 47 | ''; |
| 79 | }) | 48 | }) |
| 80 | ]; | 49 | ]; |
| 81 | }; | 50 | }; |
| 82 | 51 | ||
| 83 | lockCommand = "${config.systemd.package}/bin/systemctl --user start gtklock.service"; | 52 | # lockCommand = "${lib.getExe' config.systemd.package "systemctl"} --user start gtklock.service"; |
| 53 | lockCommand = "${lib.getExe' cfg.programs.quickshell.package "qs"} ipc call Lockscreen setLocked true"; | ||
| 54 | |||
| 55 | editor = pkgs.symlinkJoin { | ||
| 56 | inherit (cfg.services.emacs.package) name; | ||
| 57 | buildInputs = with pkgs; [ makeWrapper ]; | ||
| 58 | paths = [ cfg.services.emacs.package ]; | ||
| 59 | postBuild = '' | ||
| 60 | wrapProgram $out/bin/emacsclient \ | ||
| 61 | --inherit-argv0 \ | ||
| 62 | --add-flags ${lib.escapeShellArg (lib.escapeShellArgs cfg.services.emacs.client.arguments)} | ||
| 63 | ''; | ||
| 64 | }; | ||
| 84 | in { | 65 | in { |
| 85 | imports = with flake.nixosModules.userProfiles.${userName}; [ | 66 | imports = with flake.nixosModules.userProfiles.${userName}; [ |
| 86 | mpv yt-dlp (args: import ./xcompose.nix (inputs // args)) | 67 | zsh tmux mpv yt-dlp (args: import ./xcompose.nix (inputs // args)) |
| 87 | ]; | 68 | ]; |
| 88 | 69 | ||
| 89 | config = { | 70 | config = { |
| 90 | services.displayManager.defaultSession = "Hyprland"; # "none+xmonad"; | ||
| 91 | |||
| 92 | home-manager.users.${userName} = { | 71 | home-manager.users.${userName} = { |
| 93 | imports = [ | 72 | imports = [ |
| 94 | ./libvirt | 73 | ./libvirt |
| 95 | flakeInputs.nix-index-database.hmModules.nix-index | 74 | ./niri |
| 75 | ./shell | ||
| 76 | ./synadm | ||
| 77 | flakeInputs.nix-index-database.homeModules.nix-index | ||
| 96 | flakeInputs.impermanence.nixosModules.home-manager.impermanence | 78 | flakeInputs.impermanence.nixosModules.home-manager.impermanence |
| 97 | ]; | 79 | ]; |
| 98 | 80 | ||
| 99 | home.stateVersion = "20.09"; | 81 | home.stateVersion = "20.09"; |
| 100 | 82 | ||
| 101 | nixpkgs.config = { | 83 | # nixpkgs.config = { |
| 102 | allowUnfree = true; | 84 | # allowUnfree = true; |
| 103 | zathura.useMupdf = false; | 85 | # zathura.useMupdf = false; |
| 104 | }; | 86 | # }; |
| 105 | 87 | ||
| 106 | nix.registry = { | 88 | nix.registry = { |
| 107 | "flk" = { | 89 | "flk" = { |
| @@ -111,14 +93,14 @@ in { | |||
| 111 | }; | 93 | }; |
| 112 | to = { | 94 | to = { |
| 113 | type = "git"; | 95 | type = "git"; |
| 114 | url = "file:///home/gkleen/config/nixos-flakes"; | 96 | url = "file:///home/gkleen/projects/machines"; |
| 115 | }; | 97 | }; |
| 116 | }; | 98 | }; |
| 117 | }; | 99 | }; |
| 118 | 100 | ||
| 119 | programs = { | 101 | programs = { |
| 120 | ssh = { | 102 | ssh = { |
| 121 | matchBlocks = import ./ssh-hosts.nix { inherit pkgs; }; # customUtils.nixImport { dir = ./ssh-hosts; }; | 103 | matchBlocks = import ./ssh-hosts.nix inputs; # customUtils.nixImport { dir = ./ssh-hosts; }; |
| 122 | extraConfig = '' | 104 | extraConfig = '' |
| 123 | Match host uniworx3.ifi.lmu.de,uniworx4.ifi.lmu.de,uniworx5.ifi.lmu.de,uni2workgw.ifi.lmu.de,blackbeard.tcs.ifi.lmu.de,gitlab2.rz.ifi.lmu.de,oregon.tcs.ifi.lmu.de !exec "nc -z -w 1 %h %p &>/dev/null" | 105 | Match host uniworx3.ifi.lmu.de,uniworx4.ifi.lmu.de,uniworx5.ifi.lmu.de,uni2workgw.ifi.lmu.de,blackbeard.tcs.ifi.lmu.de,gitlab2.rz.ifi.lmu.de,oregon.tcs.ifi.lmu.de !exec "nc -z -w 1 %h %p &>/dev/null" |
| 124 | ProxyJump remote.cip.ifi.lmu.de | 106 | ProxyJump remote.cip.ifi.lmu.de |
| @@ -136,8 +118,8 @@ in { | |||
| 136 | ''} | 118 | ''} |
| 137 | 119 | ||
| 138 | Match host *.mathinst.loc,*.math.lmu.de !host ssh.math.lmu.de !exec "nc -z -w 1 %h %p &>/dev/null" | 120 | Match host *.mathinst.loc,*.math.lmu.de !host ssh.math.lmu.de !exec "nc -z -w 1 %h %p &>/dev/null" |
| 139 | # ProxyCommand ${pkgs.socat}/bin/socat - SOCKS4A:127.0.0.1:%h:%p,socksport=8118 | 121 | ProxyCommand ${lib.getExe pkgs.socat} - SOCKS4A:127.0.0.1:%h:%p,socksport=8118 |
| 140 | ProxyJump ssh.math.lmu.de | 122 | # ProxyJump ssh.math.lmu.de |
| 141 | 123 | ||
| 142 | Match host *.cipmath.loc !host cip04.cipmath.loc,mgmt-cls01.cipmath.loc !exec "nc -z -w 1 %h %p &>/dev/null" | 124 | Match host *.cipmath.loc !host cip04.cipmath.loc,mgmt-cls01.cipmath.loc !exec "nc -z -w 1 %h %p &>/dev/null" |
| 143 | ProxyJump cip04 | 125 | ProxyJump cip04 |
| @@ -154,22 +136,31 @@ in { | |||
| 154 | 136 | ||
| 155 | emacs = { | 137 | emacs = { |
| 156 | enable = true; | 138 | enable = true; |
| 157 | package = pkgs.emacs29-pgtk; | 139 | package = pkgs.emacs-pgtk; |
| 158 | extraPackages = epkgs: with epkgs; [ | 140 | extraPackages = epkgs: with epkgs; [ |
| 159 | evil evil-dvorak undo-tree magit haskell-tng-mode nix-mode | 141 | evil evil-dvorak undo-tree magit haskell-tng-mode nix-mode |
| 160 | yaml-mode json-mode shakespeare-mode smart-mode-line | 142 | yaml-mode json-mode shakespeare-mode smart-mode-line |
| 161 | highlight-parentheses highlight-symbol ag sass-mode lua-mode | 143 | highlight-parentheses highlight-symbol ag sass-mode |
| 162 | fira-code-mode use-package wanderlust # notmuch | 144 | lua-mode fira-code-mode use-package wanderlust # notmuch |
| 163 | use-package-ensure-system-package git-gutter emacsScratch | 145 | git-gutter scratch edit-server mediawiki editorconfig |
| 164 | edit-server mediawiki editorconfig typescript-mode | 146 | typescript-mode markdown-mode nftables-mode rustic |
| 165 | markdown-mode nftables-mode rustic lsp-mode lsp-ui | 147 | lsp-mode lsp-ui direnv company projectile |
| 166 | direnv company projectile tomorrow-night-paradise-theme | 148 | tomorrow-night-paradise-theme |
| 167 | treesit-grammars.with-all-grammars magit-delta scad-mode | 149 | treesit-grammars.with-all-grammars magit-delta scad-mode |
| 168 | ]; | 150 | ]; |
| 169 | overrides = self: super: { | 151 | overrides = self: super: { |
| 170 | tomorrow-night-paradise-theme = super.trivialBuild { | 152 | tomorrow-night-paradise-theme = super.trivialBuild { |
| 171 | inherit (sources.tomorrow-night-paradise-theme) pname version src; | 153 | inherit (sources.tomorrow-night-paradise-theme) pname version src; |
| 172 | }; | 154 | }; |
| 155 | scratch = pkgs.stdenv.mkDerivation { | ||
| 156 | inherit (sources.emacs-scratch_el) pname version src; | ||
| 157 | |||
| 158 | phases = [ "unpackPhase" "installPhase" ]; | ||
| 159 | |||
| 160 | installPhase = '' | ||
| 161 | install -Dt $out/share/emacs/site-lisp scratch.el | ||
| 162 | ''; | ||
| 163 | }; | ||
| 173 | }; | 164 | }; |
| 174 | }; | 165 | }; |
| 175 | firefox = { | 166 | firefox = { |
| @@ -183,8 +174,14 @@ in { | |||
| 183 | }; | 174 | }; |
| 184 | }; | 175 | }; |
| 185 | }; | 176 | }; |
| 177 | chromium.enable = true; | ||
| 186 | 178 | ||
| 187 | zathura.enable = true; | 179 | zathura = { |
| 180 | enable = true; | ||
| 181 | options = { | ||
| 182 | scroll-page-aware = true; | ||
| 183 | }; | ||
| 184 | }; | ||
| 188 | imv.enable = true; | 185 | imv.enable = true; |
| 189 | 186 | ||
| 190 | mpv.config = { | 187 | mpv.config = { |
| @@ -193,13 +190,93 @@ in { | |||
| 193 | gpu-api = "vulkan"; | 190 | gpu-api = "vulkan"; |
| 194 | }; | 191 | }; |
| 195 | 192 | ||
| 196 | zsh.initExtra = '' | 193 | zsh.initContent = let |
| 197 | source ${./zshrc} | 194 | zshrc = pkgs.resholve.mkDerivation { |
| 195 | pname = "zshrc"; | ||
| 196 | version = "0.0.0"; | ||
| 197 | |||
| 198 | src = ./zshrc; | ||
| 199 | |||
| 200 | dontUnpack = true; | ||
| 201 | dontConfigure = true; | ||
| 202 | dontBuild = true; | ||
| 203 | |||
| 204 | installPhase = '' | ||
| 205 | mkdir -p $out/share | ||
| 206 | install "$src" $out/share/zshrc | ||
| 207 | ''; | ||
| 208 | |||
| 209 | solutions = { | ||
| 210 | default = { | ||
| 211 | scripts = [ "share/zshrc" ]; | ||
| 212 | interpreter = "none"; | ||
| 213 | inputs = with pkgs; [ | ||
| 214 | coreutils | ||
| 215 | rpm | ||
| 216 | binutils | ||
| 217 | squashfsTools | ||
| 218 | unzip | ||
| 219 | cfg.programs.git.package | ||
| 220 | magickWrapped | ||
| 221 | curl | ||
| 222 | file | ||
| 223 | gnutar | ||
| 224 | cpio | ||
| 225 | magic-wormhole | ||
| 226 | cfg.programs.zsh.package | ||
| 227 | fuse | ||
| 228 | util-linux | ||
| 229 | findutils | ||
| 230 | qrencode | ||
| 231 | tty-clock | ||
| 232 | cfg.programs.jq.package | ||
| 233 | eza | ||
| 234 | less | ||
| 235 | config.systemd.package | ||
| 236 | config.programs.ssh.package | ||
| 237 | gnused | ||
| 238 | miniserve | ||
| 239 | p7zip | ||
| 240 | ]; | ||
| 241 | execer = with pkgs; [ | ||
| 242 | "cannot:${lib.getExe' rpm "rpm2cpio"}" | ||
| 243 | "cannot:${lib.getExe' squashfsTools "unsquashfs"}" | ||
| 244 | "cannot:${lib.getExe' unzip "unzip"}" | ||
| 245 | "cannot:${lib.getExe cfg.programs.git.package}" | ||
| 246 | "cannot:${lib.getExe cpio}" | ||
| 247 | "cannot:${lib.getExe' magic-wormhole "wormhole"}" | ||
| 248 | "cannot:${lib.getExe' fuse "fusermount"}" | ||
| 249 | "cannot:${lib.getExe less}" | ||
| 250 | "cannot:${lib.getExe' config.systemd.package "systemctl"}" | ||
| 251 | "cannot:${lib.getExe config.programs.ssh.package}" | ||
| 252 | "cannot:${lib.getExe' p7zip "7z"}" | ||
| 253 | ]; | ||
| 254 | wrapper = with pkgs; [ | ||
| 255 | "${lib.getExe' magickWrapped "magick"}:${lib.getExe' imagemagick "magick"}" | ||
| 256 | ]; | ||
| 257 | fake = { | ||
| 258 | builtin = ["print"]; | ||
| 259 | external = ["sudo" "umount"]; | ||
| 260 | }; | ||
| 261 | }; | ||
| 262 | }; | ||
| 263 | }; | ||
| 264 | magickWrapped = pkgs.symlinkJoin { | ||
| 265 | inherit (pkgs.imagemagick) name; | ||
| 266 | paths = [ pkgs.imagemagick ]; | ||
| 267 | |||
| 268 | buildInputs = with pkgs; [ makeWrapper ]; | ||
| 269 | postBuild = '' | ||
| 270 | wrapProgram $out/bin/magick \ | ||
| 271 | --prefix PATH : ${lib.makeBinPath (with pkgs; [ ghostscript ])} | ||
| 272 | ''; | ||
| 273 | }; | ||
| 274 | in '' | ||
| 275 | source ${zshrc}/share/zshrc | ||
| 198 | ''; | 276 | ''; |
| 199 | zsh.dirHashes = let | 277 | zsh.dirHashes = let |
| 200 | flakeHashes = mapAttrs' (n: v: nameValuePair (inputNames.${n} or n) (toString v)) flakeInputs; | 278 | flakeHashes = mapAttrs' (n: v: nameValuePair (inputNames.${n} or n) (toString v)) flakeInputs; |
| 201 | inputNames = { | 279 | inputNames = { |
| 202 | "nixpkgs" = "nixos"; | ||
| 203 | }; | 280 | }; |
| 204 | in flakeHashes // { | 281 | in flakeHashes // { |
| 205 | u2w = "$HOME/projects/uni2work"; | 282 | u2w = "$HOME/projects/uni2work"; |
| @@ -211,6 +288,16 @@ in { | |||
| 211 | pro = "$HOME/projects/pro"; | 288 | pro = "$HOME/projects/pro"; |
| 212 | media = "$HOME/media"; | 289 | media = "$HOME/media"; |
| 213 | }; | 290 | }; |
| 291 | jq.colors = { | ||
| 292 | arrays = "1;37"; | ||
| 293 | "false" = "0;37"; | ||
| 294 | "null" = "2;37"; | ||
| 295 | numbers = "0;37"; | ||
| 296 | objectKeys = "1;34"; | ||
| 297 | objects = "1;37"; | ||
| 298 | strings = "0;32"; | ||
| 299 | "true" = "0;37"; | ||
| 300 | }; | ||
| 214 | 301 | ||
| 215 | obs-studio = { | 302 | obs-studio = { |
| 216 | enable = true; | 303 | enable = true; |
| @@ -220,7 +307,7 @@ in { | |||
| 220 | gh = { | 307 | gh = { |
| 221 | enable = true; | 308 | enable = true; |
| 222 | settings = { | 309 | settings = { |
| 223 | editor = "${config.home-manager.users.${userName}.programs.emacs.package}/bin/emacsclient"; | 310 | editor = lib.getExe' editor "emacsclient"; |
| 224 | gitProtocol = "ssh"; | 311 | gitProtocol = "ssh"; |
| 225 | }; | 312 | }; |
| 226 | }; | 313 | }; |
| @@ -246,309 +333,23 @@ in { | |||
| 246 | # notify_on_cmd_finish = "invisible 120"; | 333 | # notify_on_cmd_finish = "invisible 120"; |
| 247 | }; | 334 | }; |
| 248 | keybindings = { | 335 | keybindings = { |
| 249 | "kitty_mod+n" = "detach_window"; | 336 | "kitty_mod+n" = "new_os_window_with_cwd"; |
| 250 | "kitty_mod+m" = "detach_window ask"; | 337 | "kitty_mod+m" = "detach_window ask"; |
| 251 | }; | 338 | "kitty_mod+enter" = "new_window_with_cwd"; |
| 252 | }; | 339 | "kitty_mod+t" = "new_tab_with_cwd"; |
| 253 | waybar = { | ||
| 254 | enable = true; | ||
| 255 | systemd = { | ||
| 256 | enable = true; | ||
| 257 | target = "hyprland-session.target"; | ||
| 258 | }; | ||
| 259 | settings = let | ||
| 260 | windowRewrites = { | ||
| 261 | "(.*) — Mozilla Firefox" = "$1"; | ||
| 262 | "(.*) - Mozilla Thunderbird" = "$1"; | ||
| 263 | "(.*) - mpv" = "$1"; | ||
| 264 | }; | ||
| 265 | iconSize = 11; | ||
| 266 | in [ | ||
| 267 | { | ||
| 268 | layer = "top"; | ||
| 269 | position = "top"; | ||
| 270 | height = 14; | ||
| 271 | output = [ "eDP-1" "DP-2" "DP-3" ]; | ||
| 272 | modules-left = [ "hyprland/workspaces" ]; | ||
| 273 | modules-center = [ "hyprland/window" ]; | ||
| 274 | modules-right = [ "custom/worktime" "custom/worktime-today" "custom/weather" "custom/keymap" "privacy" "tray" "wireplumber" "backlight" "battery" "idle_inhibitor" "clock" ]; | ||
| 275 | |||
| 276 | "custom/weather" = { | ||
| 277 | format = "{}"; | ||
| 278 | tooltip = true; | ||
| 279 | interval = 3600; | ||
| 280 | exec = "${lib.getExe pkgs.wttrbar} --hide-conditions --custom-indicator \"<span font=\\\"Symbols Nerd Font Mono\\\" size=\\\"120%\\\">{ICON}</span> {FeelsLikeC}°\""; | ||
| 281 | return-type = "json"; | ||
| 282 | }; | ||
| 283 | "custom/keymap" = { | ||
| 284 | format = "{}"; | ||
| 285 | tooltip = true; | ||
| 286 | return-type = "json"; | ||
| 287 | exec = pkgs.writers.writePython3 "keymap" {} '' | ||
| 288 | import os | ||
| 289 | import socket | ||
| 290 | import re | ||
| 291 | import subprocess | ||
| 292 | import json | ||
| 293 | |||
| 294 | |||
| 295 | def output(keymap): | ||
| 296 | short = keymap | ||
| 297 | if keymap == "English (programmer Dvorak)": | ||
| 298 | short = "dvp" | ||
| 299 | elif keymap == "English (US)": | ||
| 300 | short = "<span color=\"#ffffff\">us</span>" | ||
| 301 | print(json.dumps({'text': short, 'tooltip': keymap}, separators=(',', ':')), flush=True) # noqa: E501 | ||
| 302 | |||
| 303 | |||
| 304 | r = subprocess.run(["hyprctl", "devices", "-j"], check=True, stdout=subprocess.PIPE, text=True) # noqa: E501 | ||
| 305 | for keyboard in json.loads(r.stdout)['keyboards']: | ||
| 306 | if keyboard['name'] != "at-translated-set-2-keyboard": | ||
| 307 | continue | ||
| 308 | output(keyboard['active_keymap']) | ||
| 309 | |||
| 310 | sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) | ||
| 311 | sock.connect(os.environ["XDG_RUNTIME_DIR"] + "/hypr/" + os.environ["HYPRLAND_INSTANCE_SIGNATURE"] + "/.socket2.sock") # noqa: E501 | ||
| 312 | expected = re.compile(r'^activelayout>>at-translated-set-2-keyboard,(?P<keymap>.+)$') # noqa: E501 | ||
| 313 | for line in sock.makefile(buffering=1, encoding='utf-8'): | ||
| 314 | if match := expected.match(line): | ||
| 315 | output(match.group("keymap")) | ||
| 316 | ''; | ||
| 317 | on-click = "hyprctl switchxkblayout at-translated-set-2-keyboard next"; | ||
| 318 | }; | ||
| 319 | "custom/worktime" = { | ||
| 320 | interval = 60; | ||
| 321 | exec = getExe pkgs.worktime; | ||
| 322 | tooltip = false; | ||
| 323 | }; | ||
| 324 | "custom/worktime-today" = { | ||
| 325 | interval = 60; | ||
| 326 | exec = "${getExe pkgs.worktime} today"; | ||
| 327 | tooltip = false; | ||
| 328 | }; | ||
| 329 | "hyprland/workspaces" = { | ||
| 330 | all-outputs = true; | ||
| 331 | }; | ||
| 332 | "hyprland/window" = { | ||
| 333 | separate-outputs = true; | ||
| 334 | icon = true; | ||
| 335 | icon-size = 14; | ||
| 336 | rewrite = windowRewrites; | ||
| 337 | }; | ||
| 338 | clock = { | ||
| 339 | interval = 1; | ||
| 340 | # timezone = "Europe/Berlin"; | ||
| 341 | format = "W{:%V-%u %F %H:%M:%S%Ez}"; | ||
| 342 | tooltip-format = "<tt><small>{calendar}</small></tt>"; | ||
| 343 | calendar = { | ||
| 344 | mode = "year"; | ||
| 345 | mode-mon-col = 3; | ||
| 346 | weeks-pos = "left"; | ||
| 347 | on-scroll = 1; | ||
| 348 | format = { | ||
| 349 | months = "<span color='#ffead3'><b>{}</b></span>"; | ||
| 350 | days = "{}"; | ||
| 351 | weeks = "<span color='#99ffdd'><b>{}</b></span>"; | ||
| 352 | weekdays = "<span color='#ffcc66'><b>{}</b></span>"; | ||
| 353 | today = "<span color='#ff6699'><b>{}</b></span>"; | ||
| 354 | }; | ||
| 355 | }; | ||
| 356 | }; | ||
| 357 | battery = { | ||
| 358 | format = "<span font=\"Symbols Nerd Font Mono\" size=\"90%\">{icon}</span>"; | ||
| 359 | icon-size = iconSize - 2; | ||
| 360 | states = { warning = 30; critical = 15; }; | ||
| 361 | format-icons = ["󰂎" "󰁺" "󰁻" "󰁼" "󰁽" "󰁾" "󰁿" "󰂀" "󰂁" "󰂂" "󰁹" ]; | ||
| 362 | format-charging = "󰂄"; | ||
| 363 | format-plugged = "󰚥"; | ||
| 364 | tooltip-format = "{capacity}% {timeTo}"; | ||
| 365 | interval = 20; | ||
| 366 | }; | ||
| 367 | tray = { | ||
| 368 | icon-size = 16; | ||
| 369 | # show-passive-items = true; | ||
| 370 | spacing = 1; | ||
| 371 | }; | ||
| 372 | privacy = { | ||
| 373 | icon-spacing = 7; | ||
| 374 | icon-size = iconSize; | ||
| 375 | modules = [ | ||
| 376 | { type = "screenshare"; } | ||
| 377 | { type = "audio-in"; } | ||
| 378 | ]; | ||
| 379 | }; | ||
| 380 | idle_inhibitor = { | ||
| 381 | format = "<span font=\"Symbols Nerd Font Mono\" size=\"90%\">{icon}</span>"; | ||
| 382 | icon-size = iconSize; | ||
| 383 | format-icons = { activated = "󰈈"; deactivated = "󰈉"; }; | ||
| 384 | timeout = 120; | ||
| 385 | }; | ||
| 386 | backlight = { | ||
| 387 | format = "<span font=\"Symbols Nerd Font Mono\" size=\"90%\">{icon}</span>"; | ||
| 388 | icon-size = iconSize; | ||
| 389 | tooltip-format = "{percent}%"; | ||
| 390 | format-icons = ["󰃚" "󰃛" "󰃜" "󰃝" "󰃞" "󰃟" "󰃠"]; | ||
| 391 | on-scroll-up = "lightctl -d -e4 -n1 up"; | ||
| 392 | on-scroll-down = "lightctl -d -e4 -n1 down"; | ||
| 393 | }; | ||
| 394 | wireplumber = { | ||
| 395 | format = "<span font=\"Symbols Nerd Font Mono\" size=\"90%\">{icon}</span>"; | ||
| 396 | icon-size = iconSize; | ||
| 397 | tooltip-format = "{volume}% {node_name}"; | ||
| 398 | format-icons = ["󰕿" "󰖀" "󰕾"]; | ||
| 399 | format-muted = "<span font=\"Symbols Nerd Font Mono\" size=\"90%\">󰝟</span>"; | ||
| 400 | # ignored-sinks = ["Easy Effects Sink"]; | ||
| 401 | on-scroll-up = "volumectl -d -u up"; | ||
| 402 | on-scroll-down = "volumectl -d -u down"; | ||
| 403 | on-click = "volumectl -d toggle-mute"; | ||
| 404 | }; | ||
| 405 | } | ||
| 406 | { | ||
| 407 | layer = "top"; | ||
| 408 | position = "top"; | ||
| 409 | height = 14; | ||
| 410 | output = [ "!eDP-1" "!DP-2" "!DP-3" ]; | ||
| 411 | modules-left = [ "hyprland/workspaces" ]; | ||
| 412 | modules-center = [ "hyprland/window" ]; | ||
| 413 | modules-right = [ "clock" ]; | ||
| 414 | |||
| 415 | "hyprland/workspaces" = { | ||
| 416 | all-outputs = false; | ||
| 417 | }; | ||
| 418 | "hyprland/window" = { | ||
| 419 | separate-outputs = true; | ||
| 420 | icon = true; | ||
| 421 | icon-size = 14; | ||
| 422 | rewrite = windowRewrites; | ||
| 423 | }; | ||
| 424 | clock = { | ||
| 425 | interval = 1; | ||
| 426 | # timezone = "Europe/Berlin"; | ||
| 427 | format = "{:%H:%M}"; | ||
| 428 | tooltip-format = "W{:%V-%u %F %H:%M:%S%Ez}"; | ||
| 429 | }; | ||
| 430 | } | ||
| 431 | ]; | ||
| 432 | style = '' | ||
| 433 | @define-color white #ffffff; | ||
| 434 | @define-color grey #555555; | ||
| 435 | @define-color blue #1a8fff; | ||
| 436 | @define-color green #23fd00; | ||
| 437 | @define-color orange #f28a21; | ||
| 438 | @define-color red #f2201f; | ||
| 439 | |||
| 440 | * { | ||
| 441 | border: none; | ||
| 442 | font-family: "Fira Sans Nerd Font"; | ||
| 443 | font-size: 10pt; | ||
| 444 | min-height: 0; | ||
| 445 | } | ||
| 446 | |||
| 447 | window#waybar { | ||
| 448 | background-color: rgba(0, 0, 0, 0.66); | ||
| 449 | color: @white; | ||
| 450 | } | ||
| 451 | |||
| 452 | .modules-left { | ||
| 453 | margin-left: 9px; | ||
| 454 | } | ||
| 455 | .modules-right { | ||
| 456 | margin-right: 9px; | ||
| 457 | } | ||
| 458 | |||
| 459 | .module { | ||
| 460 | margin: 0 5px; | ||
| 461 | } | ||
| 462 | |||
| 463 | #workspaces button { | ||
| 464 | color: @grey; | ||
| 465 | } | ||
| 466 | #workspaces button.hosting-monitor { | ||
| 467 | color: @white; | ||
| 468 | } | ||
| 469 | #workspaces button.visible { | ||
| 470 | color: @blue; | ||
| 471 | } | ||
| 472 | #workspaces button.active { | ||
| 473 | color: @green; | ||
| 474 | } | ||
| 475 | #workspaces button.urgent { | ||
| 476 | color: @red; | ||
| 477 | } | ||
| 478 | |||
| 479 | #custom-weather, #custom-keymap, #custom-worktime, #custom-worktime-today { | ||
| 480 | color: @grey; | ||
| 481 | margin: 0 5px; | ||
| 482 | } | ||
| 483 | #custom-weather, #custom-worktime-today { | ||
| 484 | margin-right: 3px; | ||
| 485 | } | ||
| 486 | #custom-keymap, #custom-weather { | ||
| 487 | margin-left: 3px; | ||
| 488 | } | ||
| 489 | |||
| 490 | #tray { | ||
| 491 | margin: 0; | ||
| 492 | } | ||
| 493 | #battery, #idle_inhibitor, #backlight, #wireplumber { | ||
| 494 | color: @grey; | ||
| 495 | margin: 0 5px 0 2px; | ||
| 496 | } | ||
| 497 | #idle_inhibitor { | ||
| 498 | margin-right: 2px; | ||
| 499 | margin-left: 3px; | ||
| 500 | } | ||
| 501 | #battery { | ||
| 502 | margin-right: 3px; | ||
| 503 | } | ||
| 504 | #battery.discharging { | ||
| 505 | color: @white; | ||
| 506 | } | ||
| 507 | #battery.warning { | ||
| 508 | color: @orange; | ||
| 509 | } | ||
| 510 | #battery.critical { | ||
| 511 | color: @red; | ||
| 512 | } | ||
| 513 | #battery.charging { | ||
| 514 | color: @white; | ||
| 515 | } | ||
| 516 | #idle_inhibitor.activated { | ||
| 517 | color: @white; | ||
| 518 | } | ||
| 519 | |||
| 520 | #idle_inhibitor { | ||
| 521 | padding-top: 1px; | ||
| 522 | } | ||
| 523 | |||
| 524 | #privacy { | ||
| 525 | color: @red; | ||
| 526 | margin: -1px 2px 0px 5px; | ||
| 527 | } | ||
| 528 | #clock { | ||
| 529 | /* margin-right: 5px; */ | ||
| 530 | } | ||
| 531 | ''; | ||
| 532 | }; | ||
| 533 | wpaperd = { | ||
| 534 | enable = true; | ||
| 535 | settings.default = { | ||
| 536 | path = "~/.wallpapers"; | ||
| 537 | duration = "8h"; | ||
| 538 | mode = "center"; | ||
| 539 | }; | 340 | }; |
| 540 | }; | 341 | }; |
| 541 | fuzzel = { | 342 | fuzzel = { |
| 542 | enable = true; | 343 | enable = true; |
| 543 | settings = { | 344 | settings = { |
| 544 | main = { | 345 | main = { |
| 545 | terminal = lib.getExe pkgs.kitty; | 346 | terminal = lib.getExe cfg.programs.kitty.package; |
| 546 | layer = "overlay"; | 347 | layer = "overlay"; |
| 547 | icon-theme = "Paper"; | 348 | icon-theme = "Paper"; |
| 548 | font = "Fira Sans"; | 349 | font = "Fira Sans"; |
| 549 | }; | 350 | }; |
| 550 | colors = { | 351 | colors = { |
| 551 | background = "000000aa"; | 352 | background = "000000cc"; |
| 552 | text = "cdd6f4ff"; | 353 | text = "cdd6f4ff"; |
| 553 | match = "94e2d5ff"; | 354 | match = "94e2d5ff"; |
| 554 | selection = "585b70ff"; | 355 | selection = "585b70ff"; |
| @@ -561,34 +362,46 @@ in { | |||
| 561 | }; | 362 | }; |
| 562 | }; | 363 | }; |
| 563 | }; | 364 | }; |
| 365 | pandoc = { | ||
| 366 | enable = true; | ||
| 367 | extraAbbreviations = ["i.A." "d.h." "D.h." "gdw."]; | ||
| 368 | }; | ||
| 369 | nushell = { | ||
| 370 | enable = true; | ||
| 371 | settings.show_banner = false; | ||
| 372 | }; | ||
| 373 | fd.enable = true; | ||
| 564 | }; | 374 | }; |
| 565 | 375 | ||
| 566 | services = { | 376 | services = { |
| 567 | dunst = { | 377 | wpaperd = { |
| 568 | settings = import ./dunst-settings.nix inputs; | 378 | enable = false; |
| 569 | iconTheme = { | 379 | settings.default = { |
| 570 | package = pkgs.paper-icon-theme; | 380 | path = "~/.wallpapers"; |
| 571 | name = "Paper"; | 381 | duration = "15m"; |
| 382 | mode = "center"; | ||
| 572 | }; | 383 | }; |
| 573 | enable = true; | ||
| 574 | }; | 384 | }; |
| 575 | emacs = { | 385 | emacs = { |
| 576 | enable = true; | 386 | enable = true; |
| 577 | socketActivation.enable = true; | 387 | socketActivation.enable = true; |
| 578 | client = { | 388 | client = { |
| 579 | enable = true; | 389 | enable = true; |
| 580 | arguments = mkForce ["--reuse-frame" "--alternate-editor" "\"\""]; | 390 | arguments = mkForce ["--create-frame" "--alternate-editor" (lib.getExe cfg.services.emacs.package)]; |
| 581 | }; | 391 | }; |
| 582 | }; | 392 | }; |
| 583 | gpg-agent = { | 393 | gpg-agent = { |
| 584 | enable = true; | 394 | enable = true; |
| 585 | enableSshSupport = true; | 395 | enableSshSupport = true; |
| 586 | extraConfig = '' | 396 | extraConfig = '' |
| 587 | pinentry-program ${pkgs.pinentry-gtk2}/bin/pinentry | 397 | pinentry-program ${lib.getExe' pkgs.pinentry-gtk2 "pinentry"} |
| 588 | grab | 398 | grab |
| 589 | ''; | 399 | ''; |
| 590 | }; | 400 | }; |
| 591 | xembed-sni-proxy.enable = true; | 401 | xembed-sni-proxy = { |
| 402 | enable = true; | ||
| 403 | package = pkgs.kdePackages.plasma-workspace; | ||
| 404 | }; | ||
| 592 | udiskie = { | 405 | udiskie = { |
| 593 | enable = true; | 406 | enable = true; |
| 594 | automount = false; | 407 | automount = false; |
| @@ -599,6 +412,12 @@ in { | |||
| 599 | notification_actions = { | 412 | notification_actions = { |
| 600 | device_mounted = []; | 413 | device_mounted = []; |
| 601 | }; | 414 | }; |
| 415 | device_config = [ | ||
| 416 | { loop_file = "/nix/store/*-etc-metadata.erofs"; is_mounted = false; ignore = true; } | ||
| 417 | { mount_path = "/run/nixos-etc-metadata"; ignore = true; } | ||
| 418 | { mount_path = "/run/nixos-etc-metadata.*"; ignore = true; } | ||
| 419 | ]; | ||
| 420 | icon_names.media = ["drive-removable-media-symbolic"]; | ||
| 602 | }; | 421 | }; |
| 603 | }; | 422 | }; |
| 604 | network-manager-applet.enable = true; | 423 | network-manager-applet.enable = true; |
| @@ -615,7 +434,7 @@ in { | |||
| 615 | batch = "true"; | 434 | batch = "true"; |
| 616 | log = "false"; | 435 | log = "false"; |
| 617 | repeat = "watch"; | 436 | repeat = "watch"; |
| 618 | sshcmd = "${pkgs.openssh}/bin/ssh"; | 437 | sshcmd = lib.getExe' pkgs.openssh "ssh"; |
| 619 | ui = "text"; | 438 | ui = "text"; |
| 620 | }; | 439 | }; |
| 621 | }; | 440 | }; |
| @@ -631,35 +450,7 @@ in { | |||
| 631 | serverUrl = "https://etesync.yggdrasil.li"; | 450 | serverUrl = "https://etesync.yggdrasil.li"; |
| 632 | }; | 451 | }; |
| 633 | 452 | ||
| 634 | swayidle = { | ||
| 635 | enable = true; | ||
| 636 | events = [ | ||
| 637 | { event = "before-sleep"; command = lockCommand; } | ||
| 638 | { event = "after-resume"; command = "${cfg.wayland.windowManager.hyprland.package}/bin/hyprctl dispatch dpms on"; } | ||
| 639 | { event = "lock"; command = lockCommand; } | ||
| 640 | ]; | ||
| 641 | timeouts = [ | ||
| 642 | { timeout = 300; | ||
| 643 | command = "${cfg.wayland.windowManager.hyprland.package}/bin/hyprctl dispatch dpms off"; | ||
| 644 | } | ||
| 645 | { timeout = 330; command = lockCommand; } | ||
| 646 | ]; | ||
| 647 | extraArgs = [ | ||
| 648 | "idlehint" "30" | ||
| 649 | ]; | ||
| 650 | }; | ||
| 651 | poweralertd.enable = true; | 453 | poweralertd.enable = true; |
| 652 | avizo = { | ||
| 653 | enable = true; | ||
| 654 | settings.default = { | ||
| 655 | time = "1.0"; | ||
| 656 | background = "rgba(0, 0, 0, 0.8)"; | ||
| 657 | border-color = "rgba(0, 0, 0, 1)"; | ||
| 658 | bar-fg-color = "rgba(160, 160, 160, 1)"; | ||
| 659 | bar-bg-color = "rgba(32, 32, 32, 0.96)"; | ||
| 660 | # y-offset = "0.25"; | ||
| 661 | }; | ||
| 662 | }; | ||
| 663 | }; | 454 | }; |
| 664 | 455 | ||
| 665 | home.pointerCursor = { | 456 | home.pointerCursor = { |
| @@ -690,6 +481,15 @@ in { | |||
| 690 | name = "Paper-Mono-Dark"; | 481 | name = "Paper-Mono-Dark"; |
| 691 | }; | 482 | }; |
| 692 | }; | 483 | }; |
| 484 | qt.enable = true; | ||
| 485 | qt.platformTheme.name = "gtk"; | ||
| 486 | |||
| 487 | qt.kde.settings = { | ||
| 488 | kwalletrc = { | ||
| 489 | KSecretD.Enabled = false; | ||
| 490 | Wallet."Default Wallet" = "store"; | ||
| 491 | }; | ||
| 492 | }; | ||
| 693 | 493 | ||
| 694 | xsession.preferStatusNotifierItems = true; | 494 | xsession.preferStatusNotifierItems = true; |
| 695 | 495 | ||
| @@ -699,18 +499,19 @@ in { | |||
| 699 | packages = with pkgs; [ | 499 | packages = with pkgs; [ |
| 700 | fira fira-code pwvucontrol wrappedKeepassxc wl-clipboard-rs | 500 | fira fira-code pwvucontrol wrappedKeepassxc wl-clipboard-rs |
| 701 | mumble pulseaudio-ctl pamixer libnotify screen-message | 501 | mumble pulseaudio-ctl pamixer libnotify screen-message |
| 702 | wrappedYTMDesktop libsForQt5.qt5ct playerctl evince | 502 | wrappedYTMDesktop libsForQt5.qt5ct playerctl evince papers |
| 703 | thunderbird zoom-us steam steam-run wireshark virt-manager | 503 | thunderbird zoom-us xdg-desktop-portal steam steam-run |
| 704 | rclone cached-nix-shell worktime fira-code-symbols | 504 | wireshark virt-manager rclone cached-nix-shell worktime |
| 705 | libreoffice xournalpp google-chrome nixos-shell virt-viewer | 505 | fira-code-symbols libreoffice xournalpp |
| 706 | freerdp gnome-icon-theme paper-icon-theme sshpassSecret | 506 | nixos-shell virt-viewer freerdp gnome-icon-theme |
| 707 | weechat element-desktop matrix-synapse-tools.synadm | 507 | paper-icon-theme sshpassSecret weechat element-desktop |
| 708 | flakeInputs.deploy-rs.packages.${config.nixpkgs.system}.deploy-rs | 508 | sieve-connect gimp3 inkscape udiskie glab nitrokey-app |
| 709 | sieve-connect gimp inkscape udiskie glab nitrokey-app | ||
| 710 | pynitrokey gtklock wlrctl remmina openscad spice-record | 509 | pynitrokey gtklock wlrctl remmina openscad spice-record |
| 711 | libguestfs-with-appliance nerd-fonts.fira-mono | 510 | nerd-fonts.fira-mono |
| 712 | nerd-fonts.symbols-only nerd-fonts.fira-code powerline-fonts | 511 | nerd-fonts.symbols-only nerd-fonts.fira-code powerline-fonts |
| 713 | ]; | 512 | swtpm (hunspell.withDicts (dicts: with dicts; [en_GB-large de_DE])) |
| 513 | libation libqalculate | ||
| 514 | ] ++ mapAttrsToList (_name: pkg: pkgs.callPackage pkg {}) (customUtils.nixImport { dir = ./utils; }); | ||
| 714 | 515 | ||
| 715 | file = { | 516 | file = { |
| 716 | ".backup-munin".source = ./backup-patterns; | 517 | ".backup-munin".source = ./backup-patterns; |
| @@ -727,15 +528,12 @@ in { | |||
| 727 | sessionVariables = { | 528 | sessionVariables = { |
| 728 | # GDK_SCALE = 96.0 / 282.0; | 529 | # GDK_SCALE = 96.0 / 282.0; |
| 729 | # QT_AUTO_SCREEN_SCALE_FACTOR = 1; | 530 | # QT_AUTO_SCREEN_SCALE_FACTOR = 1; |
| 730 | QT_QPA_PLATFORMTHEME = "qt5ct"; | 531 | QT_QPA_PLATFORMTHEME = lib.mkForce "gtk3"; |
| 731 | LIBVIRT_DEFAULT_URI = "qemu:///system"; | 532 | LIBVIRT_DEFAULT_URI = "qemu:///system"; |
| 732 | STACK_XDG = 1; | 533 | STACK_XDG = 1; |
| 733 | EDITOR = pkgs.writeShellScript "editor" '' | 534 | EDITOR = lib.getExe' editor "emacsclient"; |
| 734 | args=("--reuse-frame" "--alternate-editor" "") | 535 | RCLONE_PASSWORD_COMMAND = "${lib.getExe' pkgs.libsecret "secret-tool"} lookup service rclone"; |
| 735 | args+=("$@") | 536 | SYSTEMD_TINT_BACKGROUND = "false"; |
| 736 | exec -a emacsclient ${cfg.services.emacs.package}/bin/emacsclient "''${args[@]}" | ||
| 737 | ''; | ||
| 738 | RCLONE_PASSWORD_COMMAND = "${pkgs.libsecret}/bin/secret-tool lookup service rclone"; | ||
| 739 | }; | 537 | }; |
| 740 | 538 | ||
| 741 | extraProfileCommands = '' | 539 | extraProfileCommands = '' |
| @@ -744,18 +542,11 @@ in { | |||
| 744 | }; | 542 | }; |
| 745 | 543 | ||
| 746 | xdg.configFile = { | 544 | xdg.configFile = { |
| 747 | "dunst/dunstrc.d" = { | ||
| 748 | source = ./dunstrc.d; | ||
| 749 | recursive = true; | ||
| 750 | onChange = '' | ||
| 751 | ${pkgs.systemd}/bin/systemctl --user try-restart dunst | ||
| 752 | ''; | ||
| 753 | }; | ||
| 754 | "wireplumber" = { | 545 | "wireplumber" = { |
| 755 | source = ./wireplumber; | 546 | source = ./wireplumber; |
| 756 | recursive = true; | 547 | recursive = true; |
| 757 | onChange = '' | 548 | onChange = '' |
| 758 | ${pkgs.systemd}/bin/systemctl --user try-restart wireplumber | 549 | ${lib.getExe' config.systemd.package "systemctl"} --user try-restart wireplumber |
| 759 | ''; | 550 | ''; |
| 760 | }; | 551 | }; |
| 761 | "stack/config.yaml" = { | 552 | "stack/config.yaml" = { |
| @@ -779,37 +570,36 @@ in { | |||
| 779 | General = { | 570 | General = { |
| 780 | dot_as_separator = 0; | 571 | dot_as_separator = 0; |
| 781 | }; | 572 | }; |
| 573 | Mode = { | ||
| 574 | calculate_as_you_type = 1; | ||
| 575 | }; | ||
| 782 | }; | 576 | }; |
| 783 | }; | 577 | }; |
| 784 | "emacs/init.el".source = ./emacs.el; | 578 | "emacs/init.el".source = pkgs.substitute { |
| 579 | src = ./emacs.el; | ||
| 580 | substitutions = [ | ||
| 581 | "--subst-var-by" "ksshaskpass" (lib.getExe pkgs.kdePackages.ksshaskpass) | ||
| 582 | ]; | ||
| 583 | }; | ||
| 584 | # "systemd/user/xdg-desktop-portal.service.d/after-graphical-session.conf".text = '' | ||
| 585 | # [Unit] | ||
| 586 | # After=graphical-session.target | ||
| 587 | # ''; | ||
| 588 | "systemd/user/home-manager.service.d/before-graphical-session.conf".text = '' | ||
| 589 | [Unit] | ||
| 590 | Before=graphical-session-pre.target | ||
| 591 | ''; | ||
| 592 | "pdfpc/pdfpcrc".text = '' | ||
| 593 | mouse 8 prev | ||
| 594 | mouse 9 next | ||
| 595 | ''; | ||
| 785 | }; | 596 | }; |
| 786 | 597 | ||
| 787 | xdg.dataFile = { | 598 | xdg.dataFile = { |
| 788 | "pandoc/abbreviations" = { | ||
| 789 | source = pkgs.runCommand "pandoc-abbreviations" { | ||
| 790 | buildInputs = [ pkgs.pandoc pkgs.coreutils ]; | ||
| 791 | } (let | ||
| 792 | germanAbbrevs = pkgs.fetchFromGitHub { | ||
| 793 | owner = "jfilter"; | ||
| 794 | repo = "german-abbreviations"; | ||
| 795 | rev = "8eb9dae93b6f05d7c53374cd217ab2dc89558e0c"; | ||
| 796 | sha256 = "SaD3tSqzen6Y3SPICe6/9vhe4iMHlArZ3kFQaEk7Hps="; | ||
| 797 | }; | ||
| 798 | in '' | ||
| 799 | cat \ | ||
| 800 | <(pandoc --print-default-data-file=abbreviations) \ | ||
| 801 | <(grep -E '^[^ ]+\.$' ${germanAbbrevs}/german_abbreviations.txt) \ | ||
| 802 | ${pkgs.writeText "abbrevs.txt" '' | ||
| 803 | i.A. | ||
| 804 | d.h. | ||
| 805 | D.h. | ||
| 806 | gdw. | ||
| 807 | ''} \ | ||
| 808 | | sort | uniq >$out | ||
| 809 | ''); | ||
| 810 | }; | ||
| 811 | "dbus-1/services/org.keepassxc.KeePassXC.service".source = "${wrappedKeepassxc}/share/dbus-1/services/org.keepassxc.KeePassXC.service"; | 599 | "dbus-1/services/org.keepassxc.KeePassXC.service".source = "${wrappedKeepassxc}/share/dbus-1/services/org.keepassxc.KeePassXC.service"; |
| 812 | "dbus-1/services/org.freedesktop.secrets.service.service".source = "${wrappedKeepassxc}/share/dbus-1/services/org.freedesktop.secrets.service.service"; | 600 | "dbus-1/services/org.freedesktop.secrets.service.service".source = "${wrappedKeepassxc}/share/dbus-1/services/org.freedesktop.secrets.service.service"; |
| 601 | "dbus-1/services/org.kde.kwalletd6.service".source = "${pkgs.kdePackages.kwallet}/share/dbus-1/services/org.kde.kwalletd6.service"; | ||
| 602 | "dbus-1/services/org.kde.kwalletd5.service".source = "${pkgs.kdePackages.kwallet}/share/dbus-1/services/org.kde.kwalletd5.service"; | ||
| 813 | "emoji-data/list.txt".source = pkgs.stdenv.mkDerivation { | 603 | "emoji-data/list.txt".source = pkgs.stdenv.mkDerivation { |
| 814 | inherit (sources.emoji-data) pname src; | 604 | inherit (sources.emoji-data) pname src; |
| 815 | version = lib.removePrefix "v" sources.emoji-data.version; | 605 | version = lib.removePrefix "v" sources.emoji-data.version; |
| @@ -895,19 +685,68 @@ in { | |||
| 895 | name = "Rainbow"; | 685 | name = "Rainbow"; |
| 896 | exec = toString (pkgs.writeShellScript "rainbow" '' | 686 | exec = toString (pkgs.writeShellScript "rainbow" '' |
| 897 | exec -- \ | 687 | exec -- \ |
| 898 | ${config.systemd.package}/bin/systemd-run --wait --user --slice-inherit \ | 688 | ${lib.getExe' config.systemd.package "systemd-run"} --wait --user --slice-inherit \ |
| 899 | --property 'CPUAccounting=yes' --property 'CPUQuotaPeriodSec=50ms' \ | 689 | --property 'CPUAccounting=yes' --property 'CPUQuotaPeriodSec=50ms' \ |
| 900 | --property 'Environment=DSCP=46' \ | 690 | -E DSCP=46 -E NIXOS_OZONE_WL \ |
| 901 | -- ${pkgs.dscp}/bin/dscp ${pkgs.google-chrome}/bin/google-chrome-stable \ | 691 | -- ${lib.getExe pkgs.dscp} ${lib.getExe cfg.programs.chromium.package} \ |
| 902 | --force-device-scale-factor=1.5 \ | ||
| 903 | --class=Rainbow \ | 692 | --class=Rainbow \ |
| 904 | --kiosk "https://web.openrainbow.com" \ | 693 | --app="https://web.openrainbow.com" \ |
| 905 | --user-data-dir=''${HOME}/.config/google-chrome-rainbow | 694 | --user-data-dir=''${HOME}/.config/chromium-rainbow |
| 906 | ''); | 695 | ''); |
| 907 | icon = pkgs.fetchurl { | 696 | icon = pkgs.fetchurl { |
| 908 | url = "https://web.openrainbow.com/rb/2.139.17/assets/skins/rainbow/images/homepage/logo__rainbow.svg"; | 697 | url = "https://web.openrainbow.com/rb/2.139.17/assets/skins/rainbow/images/homepage/logo__rainbow.svg"; |
| 909 | hash = "sha256-5fmo8rDqVDpzkGaPjk4Y+SsSZpAsY7VUQSFW6WdHwuU="; | 698 | hash = "sha256-5fmo8rDqVDpzkGaPjk4Y+SsSZpAsY7VUQSFW6WdHwuU="; |
| 910 | }; | 699 | }; |
| 700 | settings = { | ||
| 701 | StartupWMClass = "Rainbow"; | ||
| 702 | }; | ||
| 703 | }; | ||
| 704 | kimai = { | ||
| 705 | name = "Kimai"; | ||
| 706 | exec = toString (pkgs.writeShellScript "kimai" '' | ||
| 707 | exec -- \ | ||
| 708 | ${lib.getExe cfg.programs.chromium.package} \ | ||
| 709 | --class=Kimai \ | ||
| 710 | --app="https://kimai.yggdrasil.li" \ | ||
| 711 | --user-data-dir=''${HOME}/.config/chromium-kimai | ||
| 712 | ''); | ||
| 713 | icon = pkgs.fetchurl { | ||
| 714 | url = "https://www.kimai.org/images/kimai_logo.png"; | ||
| 715 | hash = "sha256-lnlOttzR2SwXA70R+egJUkeKr4U5V0avqTk8uX4bqfs="; | ||
| 716 | }; | ||
| 717 | settings = { | ||
| 718 | StartupWMClass = "Kimai"; | ||
| 719 | StartupNotify = "true"; | ||
| 720 | }; | ||
| 721 | }; | ||
| 722 | audiobookshelf = { | ||
| 723 | name = "Audiobookshelf"; | ||
| 724 | exec = toString (pkgs.writeShellScript "audiobookshelf" '' | ||
| 725 | exec -- \ | ||
| 726 | ${lib.getExe cfg.programs.chromium.package} \ | ||
| 727 | --class=Audiobookshelf \ | ||
| 728 | --app="https://audiobookshelf.yggdrasil.li" \ | ||
| 729 | --user-data-dir=''${HOME}/.config/chromium-audiobookshelf | ||
| 730 | ''); | ||
| 731 | icon = pkgs.fetchurl { | ||
| 732 | url = "https://www.audiobookshelf.org/Logo.png"; | ||
| 733 | hash = "sha256-JGPk+WNT1C4DC4lSMb0K0YmAMT5LvmSOeO0QRzkc7Lk="; | ||
| 734 | }; | ||
| 735 | settings = { | ||
| 736 | StartupWMClass = "Audiobookshelf"; | ||
| 737 | StartupNotify = "true"; | ||
| 738 | }; | ||
| 739 | }; | ||
| 740 | thunderbird-lmu = { | ||
| 741 | name = "Thunderbird (LMU)"; | ||
| 742 | exec = "thunderbird --name thunderbird -P lmu %U"; | ||
| 743 | icon = "thunderbird"; | ||
| 744 | genericName = "Email Client"; | ||
| 745 | categories = [ "Network" "Chat" "Email" "Feed" "GTK" "News" ]; | ||
| 746 | settings = { | ||
| 747 | StartupWMClass = "thunderbird"; | ||
| 748 | StartupNotify = "true"; | ||
| 749 | }; | ||
| 911 | }; | 750 | }; |
| 912 | }; | 751 | }; |
| 913 | 752 | ||
| @@ -923,23 +762,6 @@ in { | |||
| 923 | color-scheme = "prefer-dark"; | 762 | color-scheme = "prefer-dark"; |
| 924 | }; | 763 | }; |
| 925 | }; | 764 | }; |
| 926 | |||
| 927 | wayland.windowManager.hyprland = { | ||
| 928 | enable = true; | ||
| 929 | settings = import ./hyprland.nix inputs; | ||
| 930 | }; | ||
| 931 | |||
| 932 | xdg.portal = { | ||
| 933 | enable = true; | ||
| 934 | xdgOpenUsePortal = true; | ||
| 935 | config = { | ||
| 936 | common.default = [ "gtk" ]; | ||
| 937 | hyprland.default = [ "gtk" "kde" "hyprland" ]; | ||
| 938 | }; | ||
| 939 | extraPortals = with pkgs; [ | ||
| 940 | xdg-desktop-portal-kde xdg-desktop-portal-gtk xdg-desktop-portal-wlr xdg-desktop-portal-hyprland | ||
| 941 | ]; | ||
| 942 | }; | ||
| 943 | }; | 765 | }; |
| 944 | }; | 766 | }; |
| 945 | } | 767 | } |
diff --git a/accounts/gkleen@sif/dunst-settings.nix b/accounts/gkleen@sif/dunst-settings.nix deleted file mode 100644 index 72687aea..00000000 --- a/accounts/gkleen@sif/dunst-settings.nix +++ /dev/null | |||
| @@ -1,45 +0,0 @@ | |||
| 1 | { pkgs, ... }: | ||
| 2 | { | ||
| 3 | global = { | ||
| 4 | font = "Fira Sans 12"; | ||
| 5 | markup = "full"; | ||
| 6 | format = "<i>%s</i> %p\\n%b"; | ||
| 7 | alignment = "left"; | ||
| 8 | # geometry = "1216x10-32+64"; | ||
| 9 | width = 500; | ||
| 10 | height = 100; | ||
| 11 | offset = "4x4"; | ||
| 12 | origin = "top-right"; | ||
| 13 | shrink = true; | ||
| 14 | monitor = 0; | ||
| 15 | follow = "none"; | ||
| 16 | padding = 6; | ||
| 17 | horizontal_padding = 6; | ||
| 18 | separator_height = 1; | ||
| 19 | separator_color = "frame"; | ||
| 20 | idle_threshold = 0; | ||
| 21 | |||
| 22 | transparency = 10; | ||
| 23 | |||
| 24 | frame_width = 1; | ||
| 25 | frame_color = "#999999"; | ||
| 26 | |||
| 27 | word_wrap = true; | ||
| 28 | show_age_threshold = 15; | ||
| 29 | show_indicators = false; | ||
| 30 | icon_position = "right"; | ||
| 31 | min_icon_size = 25; | ||
| 32 | max_icon_size = 25; | ||
| 33 | sort = false; | ||
| 34 | sticky_history = false; | ||
| 35 | |||
| 36 | dmenu = "fuzzel --dmenu"; | ||
| 37 | browser = "${pkgs.xdg-utils}/bin/xdg-open"; | ||
| 38 | }; | ||
| 39 | # shortcuts = { | ||
| 40 | # close = "ctrl+space"; | ||
| 41 | # close_all = "ctrl+shift+space"; | ||
| 42 | # history = "ctrl+comma"; | ||
| 43 | # context = "ctrl+period"; | ||
| 44 | # }; | ||
| 45 | } | ||
diff --git a/accounts/gkleen@sif/dunstrc.d/00-urgency_low.conf b/accounts/gkleen@sif/dunstrc.d/00-urgency_low.conf deleted file mode 100644 index 98c94b64..00000000 --- a/accounts/gkleen@sif/dunstrc.d/00-urgency_low.conf +++ /dev/null | |||
| @@ -1,4 +0,0 @@ | |||
| 1 | [urgency_low] | ||
| 2 | background="#000000aa" | ||
| 3 | foreground="#999999" | ||
| 4 | timeout=5 \ No newline at end of file | ||
diff --git a/accounts/gkleen@sif/dunstrc.d/01-urgency_normal.conf b/accounts/gkleen@sif/dunstrc.d/01-urgency_normal.conf deleted file mode 100644 index f8fa8e2d..00000000 --- a/accounts/gkleen@sif/dunstrc.d/01-urgency_normal.conf +++ /dev/null | |||
| @@ -1,4 +0,0 @@ | |||
| 1 | [urgency_normal] | ||
| 2 | background="#000000aa" | ||
| 3 | foreground="#ffffff" | ||
| 4 | timeout=15 \ No newline at end of file | ||
diff --git a/accounts/gkleen@sif/dunstrc.d/02-urgency_critical.conf b/accounts/gkleen@sif/dunstrc.d/02-urgency_critical.conf deleted file mode 100644 index a08bf4b1..00000000 --- a/accounts/gkleen@sif/dunstrc.d/02-urgency_critical.conf +++ /dev/null | |||
| @@ -1,4 +0,0 @@ | |||
| 1 | [urgency_critical] | ||
| 2 | background="#900000aa" | ||
| 3 | foreground="#ffffff" | ||
| 4 | timeout=0 | ||
diff --git a/accounts/gkleen@sif/dunstrc.d/10-brightness.conf b/accounts/gkleen@sif/dunstrc.d/10-brightness.conf deleted file mode 100644 index c54595ab..00000000 --- a/accounts/gkleen@sif/dunstrc.d/10-brightness.conf +++ /dev/null | |||
| @@ -1,5 +0,0 @@ | |||
| 1 | [brightness] | ||
| 2 | appname="brightness" | ||
| 3 | set_stack_tag="brightness" | ||
| 4 | set_transient=yes | ||
| 5 | history_ignore=yes | ||
diff --git a/accounts/gkleen@sif/dunstrc.d/10-pulseaudio-ctl.conf b/accounts/gkleen@sif/dunstrc.d/10-pulseaudio-ctl.conf deleted file mode 100644 index 074f4535..00000000 --- a/accounts/gkleen@sif/dunstrc.d/10-pulseaudio-ctl.conf +++ /dev/null | |||
| @@ -1,5 +0,0 @@ | |||
| 1 | [pulseaudio-ctl] | ||
| 2 | body="Current is *" | ||
| 3 | history_ignore=yes | ||
| 4 | set_stack_tag="volume" | ||
| 5 | summary="Volume *" | ||
diff --git a/accounts/gkleen@sif/dunstrc.d/20-element.conf b/accounts/gkleen@sif/dunstrc.d/20-element.conf deleted file mode 100644 index 5ff6031e..00000000 --- a/accounts/gkleen@sif/dunstrc.d/20-element.conf +++ /dev/null | |||
| @@ -1,3 +0,0 @@ | |||
| 1 | [element-im] | ||
| 2 | appname=Element | ||
| 3 | timeout=0 \ No newline at end of file | ||
diff --git a/accounts/gkleen@sif/dunstrc.d/20-kitty.conf b/accounts/gkleen@sif/dunstrc.d/20-kitty.conf deleted file mode 100644 index b27ee27e..00000000 --- a/accounts/gkleen@sif/dunstrc.d/20-kitty.conf +++ /dev/null | |||
| @@ -1,3 +0,0 @@ | |||
| 1 | [kitty] | ||
| 2 | appname=kitty | ||
| 3 | urgency=low | ||
diff --git a/accounts/gkleen@sif/dunstrc.d/20-mail.conf b/accounts/gkleen@sif/dunstrc.d/20-mail.conf deleted file mode 100644 index cb568e01..00000000 --- a/accounts/gkleen@sif/dunstrc.d/20-mail.conf +++ /dev/null | |||
| @@ -1,3 +0,0 @@ | |||
| 1 | [element] | ||
| 2 | appname="notmuch" | ||
| 3 | timeout=0 \ No newline at end of file | ||
diff --git a/accounts/gkleen@sif/dunstrc.d/20-zulip.conf b/accounts/gkleen@sif/dunstrc.d/20-zulip.conf deleted file mode 100644 index d7fbd32c..00000000 --- a/accounts/gkleen@sif/dunstrc.d/20-zulip.conf +++ /dev/null | |||
| @@ -1,3 +0,0 @@ | |||
| 1 | [zulip] | ||
| 2 | appname="Zulip" | ||
| 3 | timeout=0 \ No newline at end of file | ||
diff --git a/accounts/gkleen@sif/emacs.el b/accounts/gkleen@sif/emacs.el index b1b1b198..3beefba6 100644 --- a/accounts/gkleen@sif/emacs.el +++ b/accounts/gkleen@sif/emacs.el | |||
| @@ -14,7 +14,7 @@ | |||
| 14 | (setq package-archives nil) | 14 | (setq package-archives nil) |
| 15 | (package-initialize) | 15 | (package-initialize) |
| 16 | (require 'use-package) | 16 | (require 'use-package) |
| 17 | (use-package use-package-ensure-system-package :ensure t) | 17 | ;; (use-package use-package-ensure-system-package :ensure t) |
| 18 | 18 | ||
| 19 | (require 'evil) | 19 | (require 'evil) |
| 20 | (evil-mode 1) | 20 | (evil-mode 1) |
| @@ -51,7 +51,7 @@ | |||
| 51 | 51 | ||
| 52 | ;; (require 'scratch) | 52 | ;; (require 'scratch) |
| 53 | (global-set-key (kbd "C-x B") 'scratch-create) | 53 | (global-set-key (kbd "C-x B") 'scratch-create) |
| 54 | (setq initial-major-mode 'scratch-mode) | 54 | ;; (setq initial-major-mode 'scratch-mode) |
| 55 | (setq initial-scratch-message "") | 55 | (setq initial-scratch-message "") |
| 56 | 56 | ||
| 57 | (global-set-key (kbd "C-x K") 'kill-current-buffer) | 57 | (global-set-key (kbd "C-x K") 'kill-current-buffer) |
| @@ -228,6 +228,7 @@ necessarily running." | |||
| 228 | (global-set-key (kbd "C-x k") 'kill-buffer-with-special-emacsclient-handling)) | 228 | (global-set-key (kbd "C-x k") 'kill-buffer-with-special-emacsclient-handling)) |
| 229 | 229 | ||
| 230 | (add-hook 'server-switch-hook 'install-emacsclient-wrapped-kill-buffer) | 230 | (add-hook 'server-switch-hook 'install-emacsclient-wrapped-kill-buffer) |
| 231 | (add-hook 'server-switch-hook #'raise-frame) | ||
| 231 | 232 | ||
| 232 | (defun move-file (new-location) | 233 | (defun move-file (new-location) |
| 233 | "Write this file to NEW-LOCATION, and delete the old one." | 234 | "Write this file to NEW-LOCATION, and delete the old one." |
| @@ -253,3 +254,5 @@ necessarily running." | |||
| 253 | (bind-key "C-x C-m" #'move-file) | 254 | (bind-key "C-x C-m" #'move-file) |
| 254 | 255 | ||
| 255 | (let ((ssh_auth_sock (string-chop-newline (shell-command-to-string "gpgconf --list-dirs agent-ssh-socket")))) (setenv "SSH_AUTH_SOCK" ssh_auth_sock)) | 256 | (let ((ssh_auth_sock (string-chop-newline (shell-command-to-string "gpgconf --list-dirs agent-ssh-socket")))) (setenv "SSH_AUTH_SOCK" ssh_auth_sock)) |
| 257 | (setenv "SSH_ASKPASS_REQUIRE" "prefer") | ||
| 258 | (setenv "SSH_ASKPASS" "@ksshaskpass@") | ||
diff --git a/accounts/gkleen@sif/firefox-chrome.css b/accounts/gkleen@sif/firefox-chrome.css index 8900e2b9..726f1e4b 100644 --- a/accounts/gkleen@sif/firefox-chrome.css +++ b/accounts/gkleen@sif/firefox-chrome.css | |||
| @@ -4,6 +4,21 @@ | |||
| 4 | font-size:12px; | 4 | font-size:12px; |
| 5 | } | 5 | } |
| 6 | 6 | ||
| 7 | #sidebar-main:has([expanded]) { | ||
| 8 | min-width:20em !important; | ||
| 9 | max-width:20em !important; | ||
| 10 | } | ||
| 11 | |||
| 12 | #sidebar, #sidebar-box { | ||
| 13 | min-width:35em !important; | ||
| 14 | max-width:35em !important; | ||
| 15 | } | ||
| 16 | |||
| 17 | #sidebar-box { | ||
| 18 | margin-right: var(--space-small); | ||
| 19 | } | ||
| 20 | |||
| 21 | /* | ||
| 7 | #sidebar { | 22 | #sidebar { |
| 8 | min-width:20em !important; | 23 | min-width:20em !important; |
| 9 | max-width:20em !important; | 24 | max-width:20em !important; |
| @@ -19,8 +34,21 @@ | |||
| 19 | } | 34 | } |
| 20 | 35 | ||
| 21 | #toolbar-menubar[inactive="true"] + #TabsToolbar { | 36 | #toolbar-menubar[inactive="true"] + #TabsToolbar { |
| 22 | visibility: collapse !important; | 37 | visibility: collapse !important; |
| 23 | } | 38 | } |
| 24 | 39 | ||
| 25 | #sidebar-box[sidebarcommand="tabcenter-reborn_ariasuni-sidebar-action"] #sidebar-header { visibility: collapse !important; } | 40 | #sidebar-box[sidebarcommand="tabcenter-reborn_ariasuni-sidebar-action"] #sidebar-header { visibility: collapse !important; } |
| 26 | #sidebar-box[sidebarcommand="_3c078156-979c-498b-8990-85f7987dd929_-sidebar-action"] #sidebar-header { visibility: collapse !important; } | 41 | #sidebar-box[sidebarcommand="_3c078156-979c-498b-8990-85f7987dd929_-sidebar-action"] #sidebar-header { visibility: collapse !important; } |
| 42 | */ | ||
| 43 | |||
| 44 | .titlebar-buttonbox-container{ display: none; } | ||
| 45 | #vertical-spacer { display: none; } | ||
| 46 | #tabbrowser-tabs[orient="vertical"] .tab-background { | ||
| 47 | border-radius: var(--border-radius-small) !important; | ||
| 48 | } | ||
| 49 | hbox:has(> #tabs-newtab-button) { | ||
| 50 | display: none; | ||
| 51 | } | ||
| 52 | #sidebar-main .tools-and-extensions { | ||
| 53 | justify-content: space-around !important; | ||
| 54 | } | ||
diff --git a/accounts/gkleen@sif/hyprland.nix b/accounts/gkleen@sif/hyprland.nix deleted file mode 100644 index d3061c61..00000000 --- a/accounts/gkleen@sif/hyprland.nix +++ /dev/null | |||
| @@ -1,424 +0,0 @@ | |||
| 1 | { pkgs, lib, config, userName, ... }: | ||
| 2 | let | ||
| 3 | cfg = config.home-manager.users.${userName}; | ||
| 4 | in { | ||
| 5 | monitor = [ | ||
| 6 | ",preferred,auto,auto,bitdepth,8" | ||
| 7 | "eDP-1,3840x2160@60,auto,1.5,bitdepth,8" | ||
| 8 | ]; | ||
| 9 | |||
| 10 | "$terminal" = "kitty"; | ||
| 11 | "$menu" = "fuzzel"; | ||
| 12 | |||
| 13 | exec-once = [ | ||
| 14 | "wpaperd" | ||
| 15 | ]; | ||
| 16 | |||
| 17 | env = [ | ||
| 18 | "NIXOS_OZONE_WL,1" | ||
| 19 | "QT_QPA_PLATFORM,wayland" | ||
| 20 | "QT_WAYLAND_DISABLE_WINDOWDECORATION,1" | ||
| 21 | "GDK_BACKEND,wayland" | ||
| 22 | "GDK_SCALE,0.66" | ||
| 23 | "QT_AUTO_SCREEN_SCALE_FACTOR,1" | ||
| 24 | "SDL_VIDEODRIVER,wayland" | ||
| 25 | # "AQ_DRM_DEVICES,/dev/dri/by-path/pci-0000:01:00.0-card" | ||
| 26 | "__NV_PRIME_RENDER_OFFLOAD,1" | ||
| 27 | "__NV_PRIME_RENDER_OFFLOAD_PROVIDER,NVIDIA-G0" | ||
| 28 | "__GLX_VENDOR_LIBRARY_NAME,nvidia" | ||
| 29 | "__VK_LAYER_NV_optimus,NVIDIA_only" | ||
| 30 | ]; | ||
| 31 | |||
| 32 | xwayland.force_zero_scaling = true; | ||
| 33 | |||
| 34 | general = { | ||
| 35 | gaps_in = 3; | ||
| 36 | gaps_out = 9; | ||
| 37 | "col.active_border" = "rgba(33ccffee) rgba(00ff95ee) 45deg"; | ||
| 38 | "col.inactive_border" = "rgba(595959aa)"; | ||
| 39 | |||
| 40 | resize_on_border = false; | ||
| 41 | |||
| 42 | allow_tearing = false; | ||
| 43 | |||
| 44 | layout = "dwindle"; | ||
| 45 | }; | ||
| 46 | |||
| 47 | decoration = { | ||
| 48 | rounding = 5; | ||
| 49 | dim_special = 0.0; | ||
| 50 | }; | ||
| 51 | |||
| 52 | animations = { | ||
| 53 | enabled = true; | ||
| 54 | bezier = "myBezier, 0.05, 0.9, 0.1, 1.05"; | ||
| 55 | animation = [ | ||
| 56 | "windows, 1, 1, default, popin 80%" | ||
| 57 | "windowsMove, 0" | ||
| 58 | # "windows, 1, 7, myBezier" | ||
| 59 | # "windowsOut, 1, 7, myBezier, popin 80%" | ||
| 60 | "border, 1, 10, default" | ||
| 61 | "borderangle, 1, 8, default" | ||
| 62 | "fade, 1, 1, default" | ||
| 63 | "workspaces, 1, 1, default, fade" | ||
| 64 | # "workspaces, 1, 6, default" | ||
| 65 | ]; | ||
| 66 | }; | ||
| 67 | |||
| 68 | dwindle = { | ||
| 69 | pseudotile = false; | ||
| 70 | preserve_split = true; | ||
| 71 | }; | ||
| 72 | |||
| 73 | master = { | ||
| 74 | new_status = "master"; | ||
| 75 | }; | ||
| 76 | |||
| 77 | misc = { | ||
| 78 | disable_hyprland_logo = true; | ||
| 79 | disable_splash_rendering = true; | ||
| 80 | # focus_on_activate = true; | ||
| 81 | mouse_move_enables_dpms = true; | ||
| 82 | key_press_enables_dpms = true; | ||
| 83 | new_window_takes_over_fullscreen = 1; | ||
| 84 | exit_window_retains_fullscreen = true; | ||
| 85 | }; | ||
| 86 | |||
| 87 | cursor = { | ||
| 88 | hide_on_key_press = true; | ||
| 89 | }; | ||
| 90 | |||
| 91 | input = { | ||
| 92 | kb_layout = "us,us"; | ||
| 93 | kb_variant = "dvp,"; | ||
| 94 | kb_model = ""; | ||
| 95 | kb_options = "compose:caps,grp:win_space_toggle"; | ||
| 96 | kb_rules = ""; | ||
| 97 | |||
| 98 | follow_mouse = 1; | ||
| 99 | |||
| 100 | sensitivity = 0; | ||
| 101 | |||
| 102 | touchpad = { | ||
| 103 | natural_scroll = false; | ||
| 104 | }; | ||
| 105 | }; | ||
| 106 | |||
| 107 | device = [ | ||
| 108 | { name = "synaptics-tm3512-010"; | ||
| 109 | sensitivity = 0.4; | ||
| 110 | } | ||
| 111 | { name = "tpps/2-elan-trackpoint"; | ||
| 112 | sensitivity = 0.2; | ||
| 113 | } | ||
| 114 | { name = "logitech-ergo-m575"; | ||
| 115 | sensitivity = 1.333; | ||
| 116 | } | ||
| 117 | ]; | ||
| 118 | |||
| 119 | gestures = { | ||
| 120 | workspace_swipe = false; | ||
| 121 | }; | ||
| 122 | |||
| 123 | dwindle = { | ||
| 124 | # no_gaps_when_only = 1; | ||
| 125 | }; | ||
| 126 | |||
| 127 | "$mainMod" = "SUPER"; | ||
| 128 | |||
| 129 | bind = [ | ||
| 130 | "$mainMod, return, exec, $terminal" | ||
| 131 | "$mainMod, Q, killactive" | ||
| 132 | "$mainMod SHIFT, Q, exec, hyprctl kill" | ||
| 133 | "$mainMod, V, togglefloating" | ||
| 134 | "$mainMod, D, exec, $menu" | ||
| 135 | "$mainMod SHIFT, D, exec, $menu --list-executables-in-path" | ||
| 136 | # "$mainMod, J, togglesplit," | ||
| 137 | |||
| 138 | "$mainMod SHIFT, L, exec, loginctl lock-session" | ||
| 139 | "$mainMod SHIFT, E, exit" | ||
| 140 | |||
| 141 | "$mainMod, left, movefocus, l" | ||
| 142 | "$mainMod, right, movefocus, r" | ||
| 143 | "$mainMod, up, movefocus, u" | ||
| 144 | "$mainMod, down, movefocus, d" | ||
| 145 | "$mainMod SHIFT, left, swapwindow, l" | ||
| 146 | "$mainMod SHIFT, right, swapwindow, r" | ||
| 147 | "$mainMod SHIFT, up, swapwindow, u" | ||
| 148 | "$mainMod SHIFT, down, swapwindow, d" | ||
| 149 | |||
| 150 | "$mainMod, T, cyclenext" | ||
| 151 | |||
| 152 | "$mainMod, G, focusmonitor, 0" | ||
| 153 | "$mainMod, C, focusmonitor, 1" | ||
| 154 | "$mainMod, R, focusmonitor, 2" | ||
| 155 | "$mainMod, L, focusmonitor, 3" | ||
| 156 | |||
| 157 | "$mainMod CTRL, G, movecurrentworkspacetomonitor, 0" | ||
| 158 | "$mainMod CTRL, C, movecurrentworkspacetomonitor, 1" | ||
| 159 | "$mainMod CTRL, R, movecurrentworkspacetomonitor, 2" | ||
| 160 | "$mainMod CTRL, L, movecurrentworkspacetomonitor, 3" | ||
| 161 | |||
| 162 | "$mainMod, F, fullscreen, 1" | ||
| 163 | "$mainMod SHIFT, F, fullscreen, 0" | ||
| 164 | "$mainMod CTRL SHIFT, F, fullscreenstate, 1, 2" | ||
| 165 | |||
| 166 | "$mainMod, code:14, workspace, 1" | ||
| 167 | "$mainMod, code:17, workspace, 2" | ||
| 168 | "$mainMod, code:13, workspace, 3" | ||
| 169 | "$mainMod, code:18, workspace, 4" | ||
| 170 | "$mainMod, code:12, workspace, 5" | ||
| 171 | "$mainMod, code:19, workspace, 6" | ||
| 172 | "$mainMod, code:11, workspace, 7" | ||
| 173 | "$mainMod, code:20, workspace, 8" | ||
| 174 | "$mainMod, code:15, workspace, 9" | ||
| 175 | "$mainMod, code:16, workspace, 10" | ||
| 176 | |||
| 177 | "$mainMod SHIFT, code:14, movetoworkspacesilent, 1" | ||
| 178 | "$mainMod SHIFT, code:17, movetoworkspacesilent, 2" | ||
| 179 | "$mainMod SHIFT, code:13, movetoworkspacesilent, 3" | ||
| 180 | "$mainMod SHIFT, code:18, movetoworkspacesilent, 4" | ||
| 181 | "$mainMod SHIFT, code:12, movetoworkspacesilent, 5" | ||
| 182 | "$mainMod SHIFT, code:19, movetoworkspacesilent, 6" | ||
| 183 | "$mainMod SHIFT, code:11, movetoworkspacesilent, 7" | ||
| 184 | "$mainMod SHIFT, code:20, movetoworkspacesilent, 8" | ||
| 185 | "$mainMod SHIFT, code:15, movetoworkspacesilent, 9" | ||
| 186 | "$mainMod SHIFT, code:16, movetoworkspacesilent, 10" | ||
| 187 | |||
| 188 | "$mainMod, semicolon, exec, dunstctl close" | ||
| 189 | "$mainMod SHIFT, semicolon, exec, dunstctl close-all" | ||
| 190 | "$mainMod, period, exec, dunstctl context" | ||
| 191 | "$mainMod, comma, exec, dunstctl history-pop" | ||
| 192 | |||
| 193 | "$mainMod ALT, E, exec, emacsclient -c" | ||
| 194 | "$mainMod ALT, Y, exec, ${pkgs.writeShellScript "yt-dlp" '' | ||
| 195 | export PATH="${lib.makeBinPath (with pkgs; [ wl-clipboard-rs socat ])}:$PATH" | ||
| 196 | socat STDIO UNIX-CONNECT:"$XDG_RUNTIME_DIR"/yt-dlp.sock <<<$'{ "urls": ["'"$(wl-paste)"$'"] }' | ||
| 197 | ''}" | ||
| 198 | "$mainMod ALT, L, exec, ${pkgs.writeShellScript "mpv" '' | ||
| 199 | export PATH="${lib.makeBinPath (with pkgs; [ wl-clipboard-rs ])}:$PATH" | ||
| 200 | exec mpv "$(wl-paste)" | ||
| 201 | ''}" | ||
| 202 | |||
| 203 | ", Print, exec, ${pkgs.writeShellScript "screenshot" '' | ||
| 204 | export PATH="${lib.makeBinPath (with pkgs; [ grim slurp wl-clipboard-rs coreutils ])}:$PATH" | ||
| 205 | |||
| 206 | outFile="$HOME/screenshots/$(date +"%Y-%m-%dT%H:%M:%S").png" | ||
| 207 | grim -g "$(slurp -b 00000080 -c FFFFFFFF -s 00000000 -w 1)" "$outFile" | ||
| 208 | wl-copy --type image/png <"$outFile" | ||
| 209 | ''}" | ||
| 210 | "SHIFT, Print, exec, ${pkgs.writeShellScript "screenshot" '' | ||
| 211 | export PATH="${lib.makeBinPath (with pkgs; [ grim jq wl-clipboard-rs coreutils ])}:$PATH" | ||
| 212 | |||
| 213 | outFile="$HOME/screenshots/$(date +"%Y-%m-%dT%H:%M:%S").png" | ||
| 214 | grim -o "$(hyprctl monitors -j | jq -r '.[] | select(.focused) | .name')" "$outFile" | ||
| 215 | wl-copy --type image/png <"$outFile" | ||
| 216 | ''}" | ||
| 217 | "CTRL SHIFT, Print, exec, ${pkgs.runCommand "picker" { | ||
| 218 | buildInputs = [ pkgs.makeWrapper ]; | ||
| 219 | } '' | ||
| 220 | makeWrapper ${lib.getExe pkgs.hyprpicker} $out \ | ||
| 221 | --prefix PATH : ${lib.makeBinPath [pkgs.wl-clipboard-rs]} | ||
| 222 | ''} -a" | ||
| 223 | "$mainMod, M, exec, ${pkgs.writeShellScript "qalc-fuzzel" '' | ||
| 224 | export PATH="${lib.makeBinPath (with pkgs; [ wl-clipboard-rs libqalculate cfg.programs.fuzzel.package coreutils findutils libnotify gnugrep ])}:$PATH" | ||
| 225 | |||
| 226 | RESULTS_DIR="$HOME/.cache/qalc-fuzzel" | ||
| 227 | prev() { | ||
| 228 | FOUND=false | ||
| 229 | while IFS= read -r line; do | ||
| 230 | [[ -n "$line" ]] || continue | ||
| 231 | FOUND=true | ||
| 232 | echo $line | ||
| 233 | done < <(export LC_ALL=C.UTF-8; echo; find "$RESULTS_DIR" -type f -printf $'%T@ %p\n' | sort -n | cut -d' ' -f2- | xargs -r cat) | ||
| 234 | $FOUND || echo | ||
| 235 | } | ||
| 236 | FUZZEL_RES=$(prev | fuzzel --dmenu --prompt "qalc> ") || exit $? | ||
| 237 | if [[ "$FUZZEL_RES" =~ .*\ =\ .* ]]; then | ||
| 238 | QALC_RES="$FUZZEL_RES" | ||
| 239 | QALC_RET=0 | ||
| 240 | else | ||
| 241 | QALC_RES=$(qalc "$FUZZEL_RES" 2>&1) | ||
| 242 | QALC_RET=$? | ||
| 243 | fi | ||
| 244 | [[ -n "$QALC_RES" ]] || exit 1 | ||
| 245 | EXISTING=false | ||
| 246 | fgrep -xrl "$QALC_RES" "$RESULTS_DIR" | xargs -r touch | ||
| 247 | [[ ''${PIPESTATUS[0]} -eq 0 ]] && EXISTING=true | ||
| 248 | if [[ $QALC_RET -eq 0 ]] && ! $EXISTING; then | ||
| 249 | RES_FILE="$RESULTS_DIR"/$(date -uIs).$(tr -Cd 'a-zA-Z0-9' </dev/random | head -c 10) | ||
| 250 | cat >"$RES_FILE" <<<"$QALC_RES" | ||
| 251 | fi | ||
| 252 | [[ "$QALC_RES" =~ .*\ =\ (.*) ]] && QALC_RES="''${BASH_REMATCH[1]}" | ||
| 253 | [[ $QALC_RET -eq 0 ]] && wl-copy "$QALC_RES" | ||
| 254 | notify-send "$QALC_RES" | ||
| 255 | ''}" | ||
| 256 | "$mainMod, E, exec, ${pkgs.writeShellScript "emoji-fuzzel" '' | ||
| 257 | export PATH="${lib.makeBinPath (with pkgs; [ cfg.programs.fuzzel.package wtype wl-clipboard-rs ])}:$PATH" | ||
| 258 | |||
| 259 | FUZZEL_RES=$(fuzzel --dmenu --prompt "emoji> " <$HOME/.local/share/emoji-data/list.txt) || exit $? | ||
| 260 | [[ -n "$FUZZEL_RES" ]] || exit 1 | ||
| 261 | wl-copy "$(cut -d ':' -f 1 <<<"$FUZZEL_RES" | tr -d '\n')" && wtype -k XF86Paste | ||
| 262 | ''}" | ||
| 263 | "$mainMod, B, exec, ${pkgs.writeShellScript "bring" '' | ||
| 264 | export PATH="${lib.makeBinPath (with pkgs; [ cfg.programs.fuzzel.package gawk gojq cfg.wayland.windowManager.hyprland.package ])}:$PATH" | ||
| 265 | |||
| 266 | state="$(hyprctl -j clients)" | ||
| 267 | active_window="$(hyprctl -j activewindow)" | ||
| 268 | active_workspace="$(hyprctl -j activeworkspace | gojq -r '.id')" | ||
| 269 | |||
| 270 | current_addr="$(echo "$active_window" | gojq -r '.address')" | ||
| 271 | |||
| 272 | window="$(echo "$state" | | ||
| 273 | gojq -r '.[] | select(.workspace.id == '"$active_workspace"') | select(.monitor != -1 ) | "\(.title)\t\(.address)"' | | ||
| 274 | fuzzel --log-level=warning --dmenu)" | ||
| 275 | |||
| 276 | addr="$(echo "$window" | awk -F $'\t' '{print $2}')" | ||
| 277 | |||
| 278 | if [[ "$addr" = "$current_addr" ]]; then | ||
| 279 | exit 0 | ||
| 280 | fi | ||
| 281 | |||
| 282 | fullscreen_on_same_ws="$(echo "$state" | gojq -r '.[] | select(.fullscreen == true) | select(.workspace.id == '"$active_workspace"') | .address')" | ||
| 283 | |||
| 284 | if [[ "$window" != "" ]]; then | ||
| 285 | if [[ "$fullscreen_on_same_ws" == "" ]]; then | ||
| 286 | hyprctl dispatch focuswindow address:"''${addr}" | ||
| 287 | else | ||
| 288 | # If we want to focus app_A and app_B is fullscreen on the same workspace, | ||
| 289 | # app_A will get focus, but app_B will remain on top. | ||
| 290 | # This monstrosity is to make sure app_A will end up on top instead. | ||
| 291 | # XXX: doesn't handle fullscreen 0, but I don't care. | ||
| 292 | hyprctl --batch "dispatch focuswindow address:''${fullscreen_on_same_ws}; dispatch fullscreen 1; dispatch focuswindow address:''${addr}; dispatch fullscreen 1" | ||
| 293 | fi | ||
| 294 | fi | ||
| 295 | ''}" | ||
| 296 | "$mainMod SHIFT, B, exec, ${pkgs.writeShellScript "bring" '' | ||
| 297 | export PATH="${lib.makeBinPath (with pkgs; [ cfg.programs.fuzzel.package gawk gojq cfg.wayland.windowManager.hyprland.package ])}:$PATH" | ||
| 298 | |||
| 299 | state="$(hyprctl -j clients)" | ||
| 300 | active_window="$(hyprctl -j activewindow)" | ||
| 301 | |||
| 302 | current_addr="$(echo "$active_window" | gojq -r '.address')" | ||
| 303 | |||
| 304 | window="$(echo "$state" | | ||
| 305 | gojq -r '.[] | select(.monitor != -1 ) | "\(.title)\t\(.workspace.name)\t\(.address)"' | | ||
| 306 | fuzzel --log-level=warning --dmenu)" | ||
| 307 | |||
| 308 | addr="$(echo "$window" | awk -F $'\t' '{print $3}')" | ||
| 309 | ws="$(echo "$window" | awk -F $'\t' '{print $2}')" | ||
| 310 | |||
| 311 | if [[ "$addr" = "$current_addr" ]]; then | ||
| 312 | exit 0 | ||
| 313 | fi | ||
| 314 | |||
| 315 | fullscreen_on_same_ws="$(echo "$state" | gojq -r ".[] | select(.fullscreen == true) | select(.workspace.name == \"$ws\") | .address")" | ||
| 316 | |||
| 317 | if [[ "$window" != "" ]]; then | ||
| 318 | if [[ "$fullscreen_on_same_ws" == "" ]]; then | ||
| 319 | hyprctl dispatch focuswindow address:"''${addr}" | ||
| 320 | else | ||
| 321 | # If we want to focus app_A and app_B is fullscreen on the same workspace, | ||
| 322 | # app_A will get focus, but app_B will remain on top. | ||
| 323 | # This monstrosity is to make sure app_A will end up on top instead. | ||
| 324 | # XXX: doesn't handle fullscreen 0, but I don't care. | ||
| 325 | hyprctl --batch "dispatch focuswindow address:''${fullscreen_on_same_ws}; dispatch fullscreen 1; dispatch focuswindow address:''${addr}; dispatch fullscreen 1" | ||
| 326 | fi | ||
| 327 | fi | ||
| 328 | ''}" | ||
| 329 | |||
| 330 | "$mainMod CTRL, return, togglespecialworkspace, term" | ||
| 331 | "$mainMod CTRL, e, togglespecialworkspace, edit" | ||
| 332 | "$mainMod CTRL, a, togglespecialworkspace, pwvucontrol" | ||
| 333 | "$mainMod CTRL, o, togglespecialworkspace, easyeffects" | ||
| 334 | "$mainMod CTRL, b, togglespecialworkspace, blueman" | ||
| 335 | "$mainMod CTRL, p, togglespecialworkspace, keepass" | ||
| 336 | ]; | ||
| 337 | bindm = [ | ||
| 338 | "$mainMod, mouse:272, movewindow" | ||
| 339 | "$mainMod, mouse:273, resizewindow" | ||
| 340 | ]; | ||
| 341 | bindel = [ | ||
| 342 | ", XF86MonBrightnessUp, exec, lightctl -d -e4 -n1 up" | ||
| 343 | ", XF86MonBrightnessDown, exec, lightctl -d -e4 -n1 down" | ||
| 344 | ", XF86AudioRaiseVolume, exec, volumectl -d -u up" | ||
| 345 | ", XF86AudioLowerVolume, exec, volumectl -d -u down" | ||
| 346 | ]; | ||
| 347 | bindl = [ | ||
| 348 | ", XF86AudioMute, exec, volumectl -d toggle-mute" | ||
| 349 | ", XF86AudioMicMute, exec, volumectl -d -m toggle-mute" | ||
| 350 | "$mainMod SHIFT, S, exec, systemctl suspend" | ||
| 351 | |||
| 352 | ", switch:off:Lid Switch,exec,hyprctl dispatch dpms on eDP-1" | ||
| 353 | ", switch:on:Lid Switch,exec,hyprctl dispatch dpms off eDP-1" | ||
| 354 | |||
| 355 | ", switch:off:Lid Switch,exec,${pkgs.writeShellScript "clamshell-off" '' | ||
| 356 | export PATH="${lib.makeBinPath (with pkgs; [ jq ])}:$PATH" | ||
| 357 | [[ $(hyprctl monitors -j | jq '.[] | select(.name == "eDP-1") | .disabled') = "true" ]] || exit 0 | ||
| 358 | |||
| 359 | hyprctl keyword monitor "eDP-1,3840x2160@60,auto,1.5" | ||
| 360 | ''}" | ||
| 361 | ", switch:on:Lid Switch,exec,${pkgs.writeShellScript "clamshell-on" '' | ||
| 362 | export PATH="${lib.makeBinPath (with pkgs; [ jq ])}:$PATH" | ||
| 363 | |||
| 364 | [[ $(hyprctl monitors -j | jq 'reduce (.[] | select(.disabled == false)) as $_ (0; .+1)') -gt 1 ]] || exit 0 | ||
| 365 | |||
| 366 | hyprctl keyword monitor "eDP-1,disable" | ||
| 367 | ''}" | ||
| 368 | ]; | ||
| 369 | |||
| 370 | windowrulev2 = [ | ||
| 371 | "suppressevent maximize fullscreen, class:.*" | ||
| 372 | |||
| 373 | # "maximize, class:^(Element|thunderbird)$" | ||
| 374 | "workspace special:pwvucontrol, class:^com\.saivert\.pwvucontrol$" | ||
| 375 | "workspace special:easyeffects, class:^com\.github\.wwmm\.easyeffects$" | ||
| 376 | "workspace special:blueman, class:^\.blueman-manager-wrapped$" | ||
| 377 | "workspace special:keepass silent, class:^org\.keepassxc\.KeePassXC$, title:^(?!Unlock Database.*)(?!.*(Access Request|Passkey credentials))" | ||
| 378 | # "group set always lock invade, class:^Element$" | ||
| 379 | "workspace 2, class:^firefox$" | ||
| 380 | "workspace 4, class:^evince$" | ||
| 381 | "workspace 4, class:^imv$" | ||
| 382 | "workspace 4, class:^org\.pwmt\.zathura$" | ||
| 383 | "workspace 10, class:^mpv$" | ||
| 384 | "workspace 1, class:^Element$" | ||
| 385 | "workspace 1, class:^thunderbird$" | ||
| 386 | "workspace 5, class:^virt-manager$" | ||
| 387 | "workspace 5, class:^qemu$" | ||
| 388 | "float, class:^org\.keepassxc\.KeePassXC$, title:Access Request$" | ||
| 389 | "center, class:^org\.keepassxc\.KeePassXC$, title:Access Request$" | ||
| 390 | "float, class:^org\.keepassxc\.KeePassXC$, title:Passkey credentials$" | ||
| 391 | "center, class:^org\.keepassxc\.KeePassXC$, title:Passkey credentials$" | ||
| 392 | "float, class:^org\.keepassxc\.KeePassXC$, title:^Unlock Database" | ||
| 393 | "center, class:^org\.keepassxc\.KeePassXC$, title:^Unlock Database" | ||
| 394 | "float, class:^xdg-desktop-portal-gtk$" | ||
| 395 | "center, class:^xdg-desktop-portal-gtk$" | ||
| 396 | |||
| 397 | "bordercolor rgba(ffaa33ee) rgba(bfff00ee) 45deg, fullscreen:1" | ||
| 398 | "bordercolor rgba(3366ffee) rgba(6a00ffee) 45deg, xwayland:1" | ||
| 399 | "bordercolor rgba(6633ffee) rgba(ea00ffee) 45deg, xwayland:1, fullscreen:1" | ||
| 400 | ]; | ||
| 401 | |||
| 402 | workspace = [ | ||
| 403 | "s[true], gapsout:100" | ||
| 404 | |||
| 405 | "special:term, on-created-empty:kitty" | ||
| 406 | "special:edit, on-created-empty:emacsclient -c" | ||
| 407 | "special:pwvucontrol, on-created-empty:pwvucontrol" | ||
| 408 | "special:easyeffects, on-created-empty:easyeffects" | ||
| 409 | "special:blueman, on-created-empty:blueman-manager" | ||
| 410 | "special:keepass, on-created-empty:keepassxc" | ||
| 411 | |||
| 412 | "1, defaultName:comm" | ||
| 413 | "2, defaultName:web" | ||
| 414 | "3, defaultName:work" | ||
| 415 | "4, defaultName:read" | ||
| 416 | ]; | ||
| 417 | |||
| 418 | layerrule = [ | ||
| 419 | "blur, waybar" | ||
| 420 | "blur, launcher" | ||
| 421 | "noanim, notifications" | ||
| 422 | "blur, notifications" | ||
| 423 | ]; | ||
| 424 | } | ||
diff --git a/accounts/gkleen@sif/libvirt/default.nix b/accounts/gkleen@sif/libvirt/default.nix index f86a68a2..4e5a9b90 100644 --- a/accounts/gkleen@sif/libvirt/default.nix +++ b/accounts/gkleen@sif/libvirt/default.nix | |||
| @@ -7,6 +7,7 @@ with flakeInputs.nixVirt.lib; | |||
| 7 | config = { | 7 | config = { |
| 8 | virtualisation.libvirt = { | 8 | virtualisation.libvirt = { |
| 9 | enable = true; | 9 | enable = true; |
| 10 | swtpm.enable = true; | ||
| 10 | connections."qemu:///session" = { | 11 | connections."qemu:///session" = { |
| 11 | domains = [ | 12 | domains = [ |
| 12 | { definition = domain.writeXML (updateManyAttrsByPath [ | 13 | { definition = domain.writeXML (updateManyAttrsByPath [ |
| @@ -16,8 +17,8 @@ with flakeInputs.nixVirt.lib; | |||
| 16 | memory = { count = 16; unit = "GiB"; }; | 17 | memory = { count = 16; unit = "GiB"; }; |
| 17 | storage_vol = "/home/gkleen/.local/share/libvirt/images/lmmirzm-vmrz01.qcow2"; | 18 | storage_vol = "/home/gkleen/.local/share/libvirt/images/lmmirzm-vmrz01.qcow2"; |
| 18 | nvram_path = "/home/gkleen/.local/share/libvirt/lmmirzm-vmrz01.nvram"; | 19 | nvram_path = "/home/gkleen/.local/share/libvirt/lmmirzm-vmrz01.nvram"; |
| 19 | virtio_drive = false; | 20 | virtio_drive = true; |
| 20 | virtio_video = false; | 21 | virtio_video = true; |
| 21 | install_virtio = false; | 22 | install_virtio = false; |
| 22 | }) { | 23 | }) { |
| 23 | qemu-commandline.env = [ | 24 | qemu-commandline.env = [ |
| @@ -33,11 +34,12 @@ with flakeInputs.nixVirt.lib; | |||
| 33 | os.bootmenu.enable = true; | 34 | os.bootmenu.enable = true; |
| 34 | devices.graphics = { | 35 | devices.graphics = { |
| 35 | listen.type = "address"; | 36 | listen.type = "address"; |
| 36 | # gl.enable = true; | 37 | gl.enable = false; |
| 37 | }; | 38 | }; |
| 39 | devices.video.model.acceleration.accel3d = false; | ||
| 38 | devices.interface = { | 40 | devices.interface = { |
| 39 | # model.type = "virtio"; | 41 | model.type = "virtio"; |
| 40 | model.type = "e1000e"; | 42 | # model.type = "e1000e"; |
| 41 | type = "bridge"; | 43 | type = "bridge"; |
| 42 | mac.address = "52:54:00:b9:f3:ed"; | 44 | mac.address = "52:54:00:b9:f3:ed"; |
| 43 | source.bridge = "rz-0971"; | 45 | source.bridge = "rz-0971"; |
| @@ -47,6 +49,15 @@ with flakeInputs.nixVirt.lib; | |||
| 47 | type = "unix"; | 49 | type = "unix"; |
| 48 | target = { type = "virtio"; name = "org.qemu.guest_agent.0"; }; | 50 | target = { type = "virtio"; name = "org.qemu.guest_agent.0"; }; |
| 49 | } | 51 | } |
| 52 | { | ||
| 53 | type = "spicevmc"; | ||
| 54 | target = { type = "virtio"; name = "com.redhat.spice.0"; }; | ||
| 55 | } | ||
| 56 | { | ||
| 57 | type = "spiceport"; | ||
| 58 | target = { type = "virtio"; name = "org.spice-space.webdav.0"; }; | ||
| 59 | source.channel = "org.spice-space.webdav.0"; | ||
| 60 | } | ||
| 50 | ]; | 61 | ]; |
| 51 | devices.tpm.model = "tpm-tis"; | 62 | devices.tpm.model = "tpm-tis"; |
| 52 | })); | 63 | })); |
diff --git a/accounts/gkleen@sif/niri/default.nix b/accounts/gkleen@sif/niri/default.nix new file mode 100644 index 00000000..5ae372c1 --- /dev/null +++ b/accounts/gkleen@sif/niri/default.nix | |||
| @@ -0,0 +1,978 @@ | |||
| 1 | { config, hostConfig, pkgs, lib, flakeInputs, ... }: | ||
| 2 | let | ||
| 3 | cfg = config.programs.niri; | ||
| 4 | |||
| 5 | kdl = flakeInputs.niri-flake.lib.kdl; | ||
| 6 | sleaf = name: arg: kdl.node name [arg] []; | ||
| 7 | |||
| 8 | niri = cfg.package; | ||
| 9 | terminal = lib.getExe config.programs.kitty.package; | ||
| 10 | |||
| 11 | focus_or_spawn = pkgs.writeShellApplication { | ||
| 12 | name = "focus-or-spawn"; | ||
| 13 | runtimeInputs = [ niri pkgs.gojq pkgs.gnugrep pkgs.socat ]; | ||
| 14 | text = '' | ||
| 15 | window_select="$1" | ||
| 16 | shift | ||
| 17 | workspace_name="$1" | ||
| 18 | shift | ||
| 19 | |||
| 20 | workspaces_json="$(niri msg -j workspaces)" | ||
| 21 | workspace_output="$(jq -r --arg workspace_name "$workspace_name" '.[] | select(.name == $workspace_name) | .output' <<<"$workspaces_json")" | ||
| 22 | # active_workspace="$(jq -r --arg workspace_output "$workspace_output" '.[] | select(.output == $workspace_output and .is_active) | .id' <<<"$workspaces_json")" | ||
| 23 | active_output="$(jq -r '.[] | select(.is_focused) | .output' <<<"$workspaces_json")" | ||
| 24 | if [[ $workspace_output != "$active_output" ]]; then | ||
| 25 | niri msg action move-workspace-to-monitor --reference "$workspace_name" "$active_output" | ||
| 26 | # socat STDIO "$NIRI_SOCKET" <<<'{"Action":{"FocusWorkspace":{"reference":{"Id":'"''${active_workspace}"'}}}}' | ||
| 27 | # niri msg action move-workspace-to-index --reference "$workspace_name" 1 | ||
| 28 | fi | ||
| 29 | |||
| 30 | while IFS=$'\n' read -r window_json; do | ||
| 31 | if [[ -n $(jq -c "$window_select" <<<"$window_json") ]]; then | ||
| 32 | if jq -e '.is_focused' <<<"$window_json" >/dev/null; then | ||
| 33 | niri msg action focus-workspace-previous | ||
| 34 | else | ||
| 35 | if [[ $(jq -r --arg workspace_name "$workspace_name" 'map(select(.name == $workspace_name)) | .[0].is_focused' <<<"$workspaces_json") != "true" ]] && [[ $(jq -r --arg workspace_name "$workspace_name" 'map(select(.name == $workspace_name)) | .[0].id' <<<"$workspaces_json") = $(jq -r '.workspace_id' <<<"$window_json") ]]; then | ||
| 36 | niri msg action focus-workspace "$workspace_name" | ||
| 37 | else | ||
| 38 | niri msg action focus-window --id "$(jq -r '.id' <<<"$window_json")" | ||
| 39 | fi | ||
| 40 | fi | ||
| 41 | exit 0 | ||
| 42 | fi | ||
| 43 | done < <(niri msg -j windows | jq -c '.[]') | ||
| 44 | |||
| 45 | exec "$@" | ||
| 46 | ''; | ||
| 47 | }; | ||
| 48 | focus-or-spawn-action = config.lib.niri.actions.spawn (lib.getExe focus_or_spawn); | ||
| 49 | |||
| 50 | with_adjacent_workspace = pkgs.writeShellApplication { | ||
| 51 | name = "with-adjacent-workspace"; | ||
| 52 | runtimeInputs = [ niri pkgs.gojq pkgs.socat ]; | ||
| 53 | text = '' | ||
| 54 | blacklist="$1" | ||
| 55 | shift | ||
| 56 | direction="$1" | ||
| 57 | shift | ||
| 58 | action="$1" | ||
| 59 | shift | ||
| 60 | |||
| 61 | workspaces_json="$(niri msg -j workspaces)" | ||
| 62 | active_workspace="$(jq -r '.[] | select(.is_focused) | .id' <<<"$workspaces_json")" | ||
| 63 | workspace_output="$(jq -r --arg active_workspace "$active_workspace" '.[] | select(.id == ($active_workspace | tonumber)) | .output' <<<"$workspaces_json")" | ||
| 64 | workspace_idx="$(jq -r '.[] | select(.is_focused) | .idx' <<<"$workspaces_json")" | ||
| 65 | |||
| 66 | jq_script='map(select(' | ||
| 67 | case "$direction" in | ||
| 68 | down) | ||
| 69 | # shellcheck disable=SC2016 | ||
| 70 | jq_script=''${jq_script}'.idx > ($workspace_idx | tonumber)';; | ||
| 71 | up) | ||
| 72 | # shellcheck disable=SC2016 | ||
| 73 | jq_script=''${jq_script}'.idx < ($workspace_idx | tonumber)';; | ||
| 74 | esac | ||
| 75 | # shellcheck disable=SC2016 | ||
| 76 | jq_script=''${jq_script}' and .output == $workspace_output and ((.name == null) or (.name | test($blacklist) | not)))) | sort_by(.idx)' | ||
| 77 | [[ $direction == "up" ]] && jq_script=''${jq_script}' | reverse' | ||
| 78 | jq_script=''${jq_script}' | .[0]' | ||
| 79 | |||
| 80 | workspace_json=$(jq -c --arg blacklist "$blacklist" --arg workspace_output "$workspace_output" --arg workspace_idx "$workspace_idx" "$jq_script" <<<"$workspaces_json") | ||
| 81 | [[ -n $workspace_json && $workspace_json != null ]] || exit 0 | ||
| 82 | jq --arg active_workspace "$active_workspace" -c "$action" <<<"$workspace_json" | tee /dev/stderr | socat STDIO "$NIRI_SOCKET" | ||
| 83 | ''; | ||
| 84 | }; | ||
| 85 | with-adjacent-workspace-action = config.lib.niri.actions.spawn (lib.getExe with_adjacent_workspace) "^${lib.concatMapStringsSep "|" ({ name, ...}: name) cfg.scratchspaces}$"; | ||
| 86 | focus-adjacent-workspace = direction: with-adjacent-workspace-action direction ''{"Action":{"FocusWorkspace":{"reference":{"Id": .id}}}}''; | ||
| 87 | move-column-to-adjacent-workspace = direction: with-adjacent-workspace-action direction ''{"Action":{"MoveColumnToWorkspace":{"reference":{"Id": .id}, "focus": true}}}''; | ||
| 88 | |||
| 89 | with_unnamed_workspace = pkgs.writeShellApplication { | ||
| 90 | name = "with-unnamed-workspace"; | ||
| 91 | runtimeInputs = [ niri pkgs.gojq pkgs.socat ]; | ||
| 92 | text = '' | ||
| 93 | action="$1" | ||
| 94 | shift | ||
| 95 | |||
| 96 | workspaces_json="$(niri msg -j workspaces)" | ||
| 97 | active_output="$(jq -r '.[] | select(.is_focused) | .output' <<<"$workspaces_json")" | ||
| 98 | active_workspace="$(jq -r '.[] | select(.is_focused) | .id' <<<"$workspaces_json")" | ||
| 99 | |||
| 100 | history_json="$(socat STDIO UNIX-CONNECT:"$XDG_RUNTIME_DIR"/niri-workspace-history.sock)" | ||
| 101 | workspace_json="$(jq -c --arg active_output "$active_output" --argjson history "$history_json" 'map(select(.output == $active_output and .name == null)) | map({"value": ., "history_idx": ((. as $workspace | ($history[$active_output] | index($workspace | .id))) as $active_idx | if $active_idx then $active_idx else ($history[$active_output] | length) + 1 end)}) | sort_by(.history_idx, .value.idx) | map(.value) | .[0]' <<<"$workspaces_json")" | ||
| 102 | [[ -n $workspace_json && $workspace_json != null ]] || exit 0 | ||
| 103 | jq --arg active_workspace "$active_workspace" -c "$action" <<<"$workspace_json" | tee /dev/stderr | socat STDIO "$NIRI_SOCKET" | ||
| 104 | ''; | ||
| 105 | }; | ||
| 106 | with-unnamed-workspace-action = config.lib.niri.actions.spawn (lib.getExe with_unnamed_workspace); | ||
| 107 | |||
| 108 | with_empty_unnamed_workspace = pkgs.writeShellApplication { | ||
| 109 | name = "with-empty-unnamed-workspace"; | ||
| 110 | runtimeInputs = [ niri pkgs.gojq pkgs.socat ]; | ||
| 111 | text = '' | ||
| 112 | action="$1" | ||
| 113 | shift | ||
| 114 | |||
| 115 | workspaces_json="$(niri msg -j workspaces)" | ||
| 116 | active_output="$(jq '.[] | select(.is_focused) | .output' <<<"$workspaces_json")" | ||
| 117 | target_workspace_id="$(jq --argjson active_output "$active_output" 'map(select(.active_window_id == null and .name == null and .output == $active_output)) | sort_by(.idx) | .[0].id' <<<"$workspaces_json")" | ||
| 118 | jq --argjson workspace_id "$target_workspace_id" -nc "$action" | tee /dev/stderr | socat STDIO "$NIRI_SOCKET" | ||
| 119 | ''; | ||
| 120 | }; | ||
| 121 | with-empty-unnamed-workspace-action = config.lib.niri.actions.spawn (lib.getExe with_empty_unnamed_workspace); | ||
| 122 | |||
| 123 | with_select_window = pkgs.writeShellApplication { | ||
| 124 | name = "with-select-window"; | ||
| 125 | runtimeInputs = [ niri pkgs.gojq pkgs.socat config.programs.fuzzel.package pkgs.gawk ]; | ||
| 126 | text = '' | ||
| 127 | window_select="$1" | ||
| 128 | shift | ||
| 129 | action="$1" | ||
| 130 | shift | ||
| 131 | |||
| 132 | windows_json="$(niri msg -j windows)" | ||
| 133 | active_workspace="$(jq -r '.[] | select(.is_focused) | .workspace_id' <<<"$windows_json")" | ||
| 134 | window_ix="$(gojq -r --arg active_workspace "$active_workspace" '.[] | select('"$window_select"') | "\(.title)\u0000icon\u001f\(.app_id)"' <<<"$windows_json" | fuzzel --width=60 --log-level=warning --dmenu --index)" | ||
| 135 | # shellcheck disable=SC2016 | ||
| 136 | window_json="$(gojq -rc --arg active_workspace "$active_workspace" --arg window_ix "$window_ix" 'map(select('"$window_select"')) | .[($window_ix | tonumber)]' <<<"$windows_json")" | ||
| 137 | |||
| 138 | [[ -z "$window_json" ]] && exit 1 | ||
| 139 | |||
| 140 | jq -c "$action" <<<"$window_json" | socat STDIO "$NIRI_SOCKET" | ||
| 141 | ''; | ||
| 142 | }; | ||
| 143 | with-select-window-action = config.lib.niri.actions.spawn (lib.getExe with_select_window); | ||
| 144 | |||
| 145 | with_predicate_window = pred: pkgs.writeShellApplication { | ||
| 146 | name = "with-predicate-window"; | ||
| 147 | runtimeInputs = [ niri pkgs.gojq pkgs.socat ]; | ||
| 148 | text = '' | ||
| 149 | action="$1" | ||
| 150 | shift | ||
| 151 | |||
| 152 | windows_json="$(niri msg -j windows)" | ||
| 153 | window_json="$(gojq -rc 'map(select(${pred})) | .[0]' <<<"$windows_json")" | ||
| 154 | |||
| 155 | [[ -z "$window_json" || $window_json = "null" ]] && exit 1 | ||
| 156 | |||
| 157 | jq -c "$action" <<<"$window_json" | socat STDIO "$NIRI_SOCKET" | ||
| 158 | ''; | ||
| 159 | }; | ||
| 160 | |||
| 161 | with-urgent-window-action = config.lib.niri.actions.spawn (lib.getExe (with_predicate_window ".is_urgent")); | ||
| 162 | with-focused-window-action = config.lib.niri.actions.spawn (lib.getExe (with_predicate_window ".is_focused")); | ||
| 163 | in { | ||
| 164 | options = { | ||
| 165 | programs.niri.scratchspaces = lib.mkOption { | ||
| 166 | type = lib.types.listOf (lib.types.submodule ({ config, ... }: { | ||
| 167 | options = { | ||
| 168 | name = lib.mkOption { | ||
| 169 | type = lib.types.str; | ||
| 170 | }; | ||
| 171 | match = lib.mkOption { | ||
| 172 | type = lib.types.listOf (lib.types.attrsOf kdl.types.kdl-args); | ||
| 173 | default = []; | ||
| 174 | }; | ||
| 175 | exclude = lib.mkOption { | ||
| 176 | type = lib.types.listOf (lib.types.attrsOf kdl.types.kdl-args); | ||
| 177 | default = []; | ||
| 178 | }; | ||
| 179 | windowRuleExtra = lib.mkOption { | ||
| 180 | type = kdl.types.kdl-nodes; | ||
| 181 | default = []; | ||
| 182 | }; | ||
| 183 | key = lib.mkOption { | ||
| 184 | type = lib.types.nullOr lib.types.str; | ||
| 185 | default = null; | ||
| 186 | }; | ||
| 187 | moveKey = lib.mkOption { | ||
| 188 | type = lib.types.nullOr lib.types.str; | ||
| 189 | default = let | ||
| 190 | keys = lib.splitString "+" config.key; | ||
| 191 | defMoveKey = lib.concatStringsSep "+" (lib.flatten [ | ||
| 192 | (lib.take (lib.length keys - 1) keys) | ||
| 193 | ["Shift"] | ||
| 194 | (lib.takeEnd 1 keys) | ||
| 195 | ]); | ||
| 196 | in if config.key == null then null else defMoveKey; | ||
| 197 | }; | ||
| 198 | spawn = lib.mkOption { | ||
| 199 | type = lib.types.nullOr (lib.types.listOf lib.types.str); | ||
| 200 | default = null; | ||
| 201 | }; | ||
| 202 | app-id = lib.mkOption { | ||
| 203 | type = lib.types.nullOr lib.types.str; | ||
| 204 | default = null; | ||
| 205 | }; | ||
| 206 | selector = lib.mkOption { | ||
| 207 | type = lib.types.nullOr lib.types.str; | ||
| 208 | default = null; | ||
| 209 | }; | ||
| 210 | }; | ||
| 211 | |||
| 212 | config = lib.mkMerge [ | ||
| 213 | (lib.mkIf (config.app-id != null) { | ||
| 214 | match = lib.mkDefault [ { app-id = "^${lib.escapeRegex config.app-id}$"; } ]; | ||
| 215 | selector = lib.mkDefault "select(.app_id == \"${config.app-id}\")"; | ||
| 216 | }) | ||
| 217 | ]; | ||
| 218 | })); | ||
| 219 | default = []; | ||
| 220 | }; | ||
| 221 | }; | ||
| 222 | |||
| 223 | config = { | ||
| 224 | home.packages = [ pkgs.xwayland-satellite-unstable ]; | ||
| 225 | |||
| 226 | systemd.user.sockets.niri-workspace-history = { | ||
| 227 | Socket = { | ||
| 228 | ListenStream = "%t/niri-workspace-history.sock"; | ||
| 229 | SocketMode = "0600"; | ||
| 230 | }; | ||
| 231 | }; | ||
| 232 | systemd.user.services.niri-workspace-history = { | ||
| 233 | Unit = { | ||
| 234 | BindsTo = [ "niri.service" ]; | ||
| 235 | After = [ "niri.service" ]; | ||
| 236 | }; | ||
| 237 | Install = { | ||
| 238 | WantedBy = [ "niri.service" ]; | ||
| 239 | }; | ||
| 240 | Service = { | ||
| 241 | Type = "simple"; | ||
| 242 | Sockets = [ "niri-workspace-history.socket" ]; | ||
| 243 | ExecStart = pkgs.writers.writePython3 "niri-workspace-history" { flakeIgnore = ["E501"]; } '' | ||
| 244 | import os | ||
| 245 | import socket | ||
| 246 | import json | ||
| 247 | # import sys | ||
| 248 | from collections import defaultdict | ||
| 249 | from threading import Thread, Lock | ||
| 250 | from socketserver import StreamRequestHandler, ThreadingTCPServer | ||
| 251 | from contextlib import contextmanager | ||
| 252 | from io import TextIOWrapper | ||
| 253 | |||
| 254 | |||
| 255 | @contextmanager | ||
| 256 | def detaching(thing): | ||
| 257 | try: | ||
| 258 | yield thing | ||
| 259 | finally: | ||
| 260 | thing.detach() | ||
| 261 | |||
| 262 | |||
| 263 | workspace_history = defaultdict(list) | ||
| 264 | history_lock = Lock() | ||
| 265 | |||
| 266 | |||
| 267 | def monitor_niri(): | ||
| 268 | workspaces = list() | ||
| 269 | |||
| 270 | def focus_workspace(output, workspace): | ||
| 271 | with history_lock: | ||
| 272 | workspace_history[output] = [workspace] + [ws for ws in workspace_history[output] if ws != workspace] | ||
| 273 | # print(json.dumps(workspace_history), file=sys.stderr) | ||
| 274 | |||
| 275 | sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) | ||
| 276 | sock.connect(os.environ["NIRI_SOCKET"]) | ||
| 277 | sock.send(b"\"EventStream\"\n") | ||
| 278 | for line in sock.makefile(buffering=1, encoding='utf-8'): | ||
| 279 | if line_json := json.loads(line): | ||
| 280 | if "WorkspacesChanged" in line_json: | ||
| 281 | workspaces = line_json["WorkspacesChanged"]["workspaces"] | ||
| 282 | for ws in workspaces: | ||
| 283 | if ws["is_focused"]: | ||
| 284 | focus_workspace(ws["output"], ws["id"]) | ||
| 285 | if "WorkspaceActivated" in line_json: | ||
| 286 | for ws in workspaces: | ||
| 287 | if ws["id"] != line_json["WorkspaceActivated"]["id"]: | ||
| 288 | continue | ||
| 289 | focus_workspace(ws["output"], ws["id"]) | ||
| 290 | break | ||
| 291 | |||
| 292 | |||
| 293 | class RequestHandler(StreamRequestHandler): | ||
| 294 | def handle(self): | ||
| 295 | with detaching(TextIOWrapper(self.wfile, encoding='utf-8', write_through=True)) as out: | ||
| 296 | with history_lock: | ||
| 297 | json.dump(workspace_history, out) | ||
| 298 | |||
| 299 | |||
| 300 | class Server(ThreadingTCPServer): | ||
| 301 | def __init__(self): | ||
| 302 | ThreadingTCPServer.__init__(self, ("", 8000), RequestHandler, bind_and_activate=False) | ||
| 303 | self.socket = socket.fromfd(3, self.address_family, self.socket_type) | ||
| 304 | |||
| 305 | |||
| 306 | def run_server(): | ||
| 307 | with Server() as server: | ||
| 308 | server.serve_forever() | ||
| 309 | |||
| 310 | |||
| 311 | niri = Thread(target=monitor_niri) | ||
| 312 | niri.daemon = True | ||
| 313 | niri.start() | ||
| 314 | |||
| 315 | server_thread = Thread(target=run_server) | ||
| 316 | server_thread.daemon = True | ||
| 317 | server_thread.start() | ||
| 318 | |||
| 319 | while True: | ||
| 320 | server_thread.join(timeout=0.5) | ||
| 321 | niri.join(timeout=0.5) | ||
| 322 | |||
| 323 | if not (niri.is_alive() and server_thread.is_alive()): | ||
| 324 | break | ||
| 325 | ''; | ||
| 326 | }; | ||
| 327 | }; | ||
| 328 | systemd.user.services.niri-workspace-sort = { | ||
| 329 | Unit = { | ||
| 330 | BindsTo = [ "niri.service" ]; | ||
| 331 | After = [ "niri.service" ]; | ||
| 332 | }; | ||
| 333 | Install = { | ||
| 334 | WantedBy = [ "niri.service" ]; | ||
| 335 | }; | ||
| 336 | Service = { | ||
| 337 | Type = "simple"; | ||
| 338 | ExecStart = pkgs.writers.writePython3 "niri-workspace-sort" { flakeIgnore = ["E501"]; } '' | ||
| 339 | import os | ||
| 340 | import sys | ||
| 341 | import socket | ||
| 342 | import json | ||
| 343 | |||
| 344 | outputs = None | ||
| 345 | only = {'HDMI-A-1': {'bmr'}, 'eDP-1': {'vid'}} | ||
| 346 | |||
| 347 | |||
| 348 | class Niri(socket.socket): | ||
| 349 | def __init__(self): | ||
| 350 | super().__init__(socket.AF_UNIX, socket.SOCK_STREAM) | ||
| 351 | super().connect(os.environ["NIRI_SOCKET"]) | ||
| 352 | self.fh = super().makefile(mode='rw', buffering=1, encoding='utf-8') | ||
| 353 | |||
| 354 | def cmd(self, obj): | ||
| 355 | print(json.dumps(obj, separators=(',', ':')), flush=True, file=self.fh) | ||
| 356 | |||
| 357 | def event_stream(self): | ||
| 358 | self.cmd("EventStream") | ||
| 359 | return self.fh | ||
| 360 | |||
| 361 | |||
| 362 | with Niri() as niri, Niri().event_stream() as niri_stream: | ||
| 363 | for line in niri_stream: | ||
| 364 | workspaces = None | ||
| 365 | if line_json := json.loads(line): | ||
| 366 | if "WorkspacesChanged" in line_json: | ||
| 367 | workspaces = line_json["WorkspacesChanged"]["workspaces"] | ||
| 368 | |||
| 369 | if workspaces is None: | ||
| 370 | continue | ||
| 371 | |||
| 372 | old_outputs = outputs | ||
| 373 | outputs = {ws["output"] for ws in workspaces} | ||
| 374 | if old_outputs is None: | ||
| 375 | print("Initial outputs: {}".format(outputs), file=sys.stderr) | ||
| 376 | continue | ||
| 377 | |||
| 378 | new_outputs = outputs - old_outputs | ||
| 379 | if not new_outputs: | ||
| 380 | continue | ||
| 381 | print("New outputs: {}".format(new_outputs), file=sys.stderr) | ||
| 382 | |||
| 383 | relevant_workspaces = list(filter(lambda ws: (ws["name"] is not None) or (ws["active_window_id"] is not None), workspaces)) | ||
| 384 | target_output = next(iter(outputs - set(only.keys()))) | ||
| 385 | if not target_output: | ||
| 386 | continue | ||
| 387 | for ws in relevant_workspaces: | ||
| 388 | ws_ident = ws["name"] if ws["name"] is not None else (ws["output"], ws["idx"]) | ||
| 389 | if ws["output"] not in set(only.keys()): | ||
| 390 | continue | ||
| 391 | if ws_ident in only[ws["output"]]: | ||
| 392 | continue | ||
| 393 | |||
| 394 | print("{} -> {}".format(ws_ident, target_output), file=sys.stderr) | ||
| 395 | niri.cmd({"Action": {"MoveWorkspaceToMonitor": {"reference": {"Id": ws["id"]}, "output": target_output}}}) | ||
| 396 | ''; | ||
| 397 | Restart = "on-failure"; | ||
| 398 | RestartSec = 10; | ||
| 399 | }; | ||
| 400 | }; | ||
| 401 | |||
| 402 | programs.niri.scratchspaces = [ | ||
| 403 | { name = "pwctl"; | ||
| 404 | key = "Mod+Control+A"; | ||
| 405 | spawn = ["pwvucontrol"]; | ||
| 406 | app-id = "com.saivert.pwvucontrol"; | ||
| 407 | } | ||
| 408 | { name = "kpxc"; | ||
| 409 | exclude = [ | ||
| 410 | { title = "^Unlock Database.*"; } | ||
| 411 | { title = "^Access Request.*"; } | ||
| 412 | { title = ".*Passkey credentials$"; } | ||
| 413 | ]; | ||
| 414 | windowRuleExtra = with kdl; [ | ||
| 415 | (sleaf "open-focused" false) | ||
| 416 | ]; | ||
| 417 | key = "Mod+Control+P"; | ||
| 418 | app-id = "org.keepassxc.KeePassXC"; | ||
| 419 | spawn = [ "keepassxc" ]; | ||
| 420 | } | ||
| 421 | { name = "bmgr"; | ||
| 422 | key = "Mod+Control+B"; | ||
| 423 | app-id = ".blueman-manager-wrapped"; | ||
| 424 | spawn = [ "blueman-manager" ]; | ||
| 425 | } | ||
| 426 | { name = "term"; | ||
| 427 | key = "Mod+Control+Return"; | ||
| 428 | app-id = "kitty-scratch"; | ||
| 429 | spawn = [ "kitty" "--app-id" "kitty-scratch" ]; | ||
| 430 | } | ||
| 431 | { name = "edit"; | ||
| 432 | match = [ { title = "^scratch$"; app-id = "^emacs$"; } ]; | ||
| 433 | key = "Mod+Control+E"; | ||
| 434 | selector = "select(.app_id == \"emacs\" and .title == \"scratch\")"; | ||
| 435 | spawn = [ "emacsclient" "-c" "--frame-parameters=(quote (name . \"scratch\"))" ]; | ||
| 436 | } | ||
| 437 | { name = "eff"; | ||
| 438 | key = "Mod+Control+O"; | ||
| 439 | app-id = "com.github.wwmm.easyeffects"; | ||
| 440 | spawn = [ "easyeffects" ]; | ||
| 441 | } | ||
| 442 | { name = "time"; | ||
| 443 | key = "Mod+Control+K"; | ||
| 444 | app-id = "chrome-kimai.yggdrasil.li__-Default"; | ||
| 445 | spawn = [ (toString (pkgs.resholve.writeScript "kimai" { | ||
| 446 | interpreter = pkgs.runtimeShell; | ||
| 447 | inputs = [ pkgs.dex ]; | ||
| 448 | execer = [ "cannot:${lib.getExe pkgs.dex}" ]; | ||
| 449 | } '' | ||
| 450 | exec dex $HOME/.local/state/nix/profile/share/applications/kimai.desktop | ||
| 451 | '')) ]; | ||
| 452 | windowRuleExtra = with kdl; [ | ||
| 453 | (sleaf "block-out-from" "screencast") | ||
| 454 | ]; | ||
| 455 | } | ||
| 456 | ]; | ||
| 457 | programs.niri.config = | ||
| 458 | let | ||
| 459 | inherit (kdl) node plain leaf flag; | ||
| 460 | optional-node = cond: v: | ||
| 461 | if cond | ||
| 462 | then v | ||
| 463 | else null; | ||
| 464 | opt-props = lib.filterAttrs (lib.const (value: value != null)); | ||
| 465 | normalize-nodes = nodes: lib.remove null (lib.flatten nodes); | ||
| 466 | in | ||
| 467 | normalize-nodes [ | ||
| 468 | (flag "prefer-no-csd") | ||
| 469 | |||
| 470 | (sleaf "screenshot-path" "~/screenshots/%Y-%m-%dT%H:%M:%S.png") | ||
| 471 | |||
| 472 | (plain "hotkey-overlay" [ | ||
| 473 | (flag "skip-at-startup") | ||
| 474 | ]) | ||
| 475 | |||
| 476 | (plain "input" [ | ||
| 477 | (plain "keyboard" [ | ||
| 478 | (sleaf "repeat-delay" 300) | ||
| 479 | (sleaf "repeat-rate" 50) | ||
| 480 | |||
| 481 | (plain "xkb" [ | ||
| 482 | (sleaf "layout" "us,us") | ||
| 483 | (sleaf "variant" "dvp,") | ||
| 484 | (sleaf "options" "compose:caps,grp:win_space_toggle") | ||
| 485 | ]) | ||
| 486 | ]) | ||
| 487 | |||
| 488 | (flag "workspace-auto-back-and-forth") | ||
| 489 | # (sleaf "focus-follows-mouse" {}) | ||
| 490 | # (flag "warp-mouse-to-focus") | ||
| 491 | |||
| 492 | # (plain "touchpad" [ (flag "off") ]) | ||
| 493 | (plain "trackball" [ | ||
| 494 | (sleaf "scroll-method" "on-button-down") | ||
| 495 | (sleaf "scroll-button" 278) | ||
| 496 | ]) | ||
| 497 | (plain "touch" [ | ||
| 498 | (sleaf "map-to-output" "eDP-1") | ||
| 499 | ]) | ||
| 500 | ]) | ||
| 501 | |||
| 502 | (plain "gestures" [ | ||
| 503 | (plain "hot-corners" [(flag "off")]) | ||
| 504 | ]) | ||
| 505 | |||
| 506 | (plain "environment" (lib.mapAttrsToList sleaf { | ||
| 507 | NIXOS_OZONE_WL = "1"; | ||
| 508 | QT_QPA_PLATFORM = "wayland"; | ||
| 509 | QT_WAYLAND_DISABLE_WINDOWDECORATION = "1"; | ||
| 510 | GDK_BACKEND = "wayland"; | ||
| 511 | SDL_VIDEODRIVER = "wayland"; | ||
| 512 | DISPLAY = ":0"; | ||
| 513 | ELECTRON_OZONE_PLATFORM_HINT = "auto"; | ||
| 514 | SSH_ASKPASS_REQUIRE = "prefer"; | ||
| 515 | SSH_ASKPASS = lib.getExe pkgs.kdePackages.ksshaskpass; | ||
| 516 | SUDO_ASKPASS = lib.getExe pkgs.kdePackages.ksshaskpass; | ||
| 517 | })) | ||
| 518 | |||
| 519 | (node "output" ["eDP-1"] [ | ||
| 520 | (sleaf "scale" 1.5) | ||
| 521 | (sleaf "position" { x = 0; y = 0; }) | ||
| 522 | ]) | ||
| 523 | (node "output" ["Ancor Communications Inc ASUS PB287Q 0x0000DD9B"] [ | ||
| 524 | (sleaf "scale" 1.5) | ||
| 525 | (sleaf "position" { x = 2560; y = 0; }) | ||
| 526 | ]) | ||
| 527 | (node "output" ["HP Inc. HP 727pu CN4417143K"] [ | ||
| 528 | (sleaf "mode" "2560x1440@119.998") | ||
| 529 | (sleaf "scale" 1) | ||
| 530 | (sleaf "position" { x = 2560; y = 0; }) | ||
| 531 | (flag "variable-refresh-rate") | ||
| 532 | ]) | ||
| 533 | |||
| 534 | (plain "debug" [ | ||
| 535 | (sleaf "render-drm-device" "/dev/dri/by-path/pci-0000:00:02.0-render") | ||
| 536 | ]) | ||
| 537 | |||
| 538 | (plain "animations" [ | ||
| 539 | (sleaf "slowdown" 0.5) | ||
| 540 | (plain "workspace-switch" [(flag "off")]) | ||
| 541 | ]) | ||
| 542 | |||
| 543 | (plain "layout" [ | ||
| 544 | (sleaf "gaps" 8) | ||
| 545 | (plain "struts" [ | ||
| 546 | (sleaf "left" 26) | ||
| 547 | (sleaf "right" 26) | ||
| 548 | (sleaf "top" 0) | ||
| 549 | (sleaf "bottom" 0) | ||
| 550 | ]) | ||
| 551 | (plain "border" [ | ||
| 552 | (sleaf "width" 2) | ||
| 553 | (sleaf "active-gradient" { | ||
| 554 | from = "hsla(195 100% 45% 1)"; | ||
| 555 | to = "hsla(155 100% 37.5% 1)"; | ||
| 556 | angle = 29; | ||
| 557 | relative-to = "workspace-view"; | ||
| 558 | }) | ||
| 559 | (sleaf "inactive-gradient" { | ||
| 560 | from = "hsla(0 0% 27.7% 1)"; | ||
| 561 | to = "hsla(0 0% 23% 1)"; | ||
| 562 | angle = 29; | ||
| 563 | relative-to = "workspace-view"; | ||
| 564 | }) | ||
| 565 | ]) | ||
| 566 | (plain "focus-ring" [ | ||
| 567 | (flag "off") | ||
| 568 | ]) | ||
| 569 | |||
| 570 | (plain "preset-column-widths" (map (prop: sleaf "proportion" prop) [ | ||
| 571 | (1. / 4.) (1. / 3.) (1. / 2.) (2. / 3.) (3. / 4.) (1.) | ||
| 572 | ])) | ||
| 573 | (plain "default-column-width" [ (sleaf "proportion" (1. / 2.)) ]) | ||
| 574 | (plain "preset-window-heights" (map (prop: sleaf "proportion" prop) [ | ||
| 575 | (1. / 3.) (1. / 2.) (2. / 3.) (1.) | ||
| 576 | ])) | ||
| 577 | |||
| 578 | (flag "always-center-single-column") | ||
| 579 | |||
| 580 | (plain "tab-indicator" [ | ||
| 581 | (sleaf "gap" 4) | ||
| 582 | (sleaf "width" 8) | ||
| 583 | (sleaf "gaps-between-tabs" 4) | ||
| 584 | (flag "place-within-column") | ||
| 585 | (sleaf "length" { total-proportion = 1.; }) | ||
| 586 | (sleaf "active-gradient" { | ||
| 587 | from = "hsla(195 100% 60% 0.75)"; | ||
| 588 | to = "hsla(155 100% 50% 0.75)"; | ||
| 589 | angle = 29; | ||
| 590 | relative-to = "workspace-view"; | ||
| 591 | }) | ||
| 592 | (sleaf "inactive-gradient" { | ||
| 593 | from = "hsla(0 0% 42% 0.66)"; | ||
| 594 | to = "hsla(0 0% 35% 0.66)"; | ||
| 595 | angle = 29; | ||
| 596 | relative-to = "workspace-view"; | ||
| 597 | }) | ||
| 598 | ]) | ||
| 599 | ]) | ||
| 600 | |||
| 601 | (plain "cursor" [ | ||
| 602 | (flag "hide-when-typing") | ||
| 603 | ]) | ||
| 604 | |||
| 605 | (map (name: | ||
| 606 | (node "workspace" [name] [ | ||
| 607 | (sleaf "open-on-output" "eDP-1") | ||
| 608 | ]) | ||
| 609 | ) (map ({name, ...}: name) cfg.scratchspaces)) | ||
| 610 | (map (name: | ||
| 611 | (sleaf "workspace" name) | ||
| 612 | ) ["comm" "web" "vid" "bmr"]) | ||
| 613 | |||
| 614 | (plain "window-rule" [ | ||
| 615 | (sleaf "clip-to-geometry" true) | ||
| 616 | ]) | ||
| 617 | |||
| 618 | (plain "window-rule" [ | ||
| 619 | (sleaf "match" { is-floating = true; }) | ||
| 620 | (sleaf "geometry-corner-radius" 8) | ||
| 621 | (plain "shadow" [ (flag "on") ]) | ||
| 622 | ]) | ||
| 623 | |||
| 624 | (plain "window-rule" [ | ||
| 625 | (sleaf "match" { app-id = "^org\\.keepassxc\\.KeePassXC$"; }) | ||
| 626 | (sleaf "block-out-from" "screencast") | ||
| 627 | ]) | ||
| 628 | (plain "window-rule" (normalize-nodes [ | ||
| 629 | (map (title: | ||
| 630 | (sleaf "match" { app-id = "^org\\.keepassxc\\.KeePassXC$"; inherit title; }) | ||
| 631 | ) ["^Unlock Database.*" "^Access Request.*" ".*Passkey credentials$" "Browser Access Request$"]) | ||
| 632 | (sleaf "open-focused" true) | ||
| 633 | (sleaf "open-floating" true) | ||
| 634 | ])) | ||
| 635 | |||
| 636 | (map ({ name, match, exclude, windowRuleExtra, ... }: | ||
| 637 | (optional-node (match != []) (plain "window-rule" (normalize-nodes [ | ||
| 638 | (map (sleaf "match") match) | ||
| 639 | (map (sleaf "exclude") exclude) | ||
| 640 | (sleaf "open-on-workspace" name) | ||
| 641 | (sleaf "open-maximized" true) | ||
| 642 | windowRuleExtra | ||
| 643 | ]))) | ||
| 644 | ) cfg.scratchspaces) | ||
| 645 | |||
| 646 | (plain "window-rule" [ | ||
| 647 | (sleaf "match" { app-id = "^emacs$"; }) | ||
| 648 | (sleaf "match" { app-id = "^firefox$"; }) | ||
| 649 | (plain "default-column-width" [(sleaf "proportion" (2. / 3.))]) | ||
| 650 | ]) | ||
| 651 | (plain "window-rule" [ | ||
| 652 | (sleaf "match" { app-id = "^kitty$"; }) | ||
| 653 | (sleaf "match" { app-id = "^kitty-play$"; }) | ||
| 654 | (plain "default-column-width" [(sleaf "proportion" (1. / 3.))]) | ||
| 655 | ]) | ||
| 656 | |||
| 657 | (plain "window-rule" [ | ||
| 658 | (sleaf "match" { app-id = "^thunderbird$"; }) | ||
| 659 | (sleaf "match" { app-id = "^Element$"; }) | ||
| 660 | (sleaf "match" { app-id = "^chrome-web\.openrainbow\.com__-Default$"; }) | ||
| 661 | (sleaf "open-on-workspace" "comm") | ||
| 662 | ]) | ||
| 663 | (plain "window-rule" [ | ||
| 664 | (sleaf "match" { app-id = "^firefox$"; }) | ||
| 665 | (sleaf "open-on-workspace" "web") | ||
| 666 | (sleaf "open-maximized" true) | ||
| 667 | ]) | ||
| 668 | (plain "window-rule" [ | ||
| 669 | (sleaf "match" { app-id = "^mpv$"; }) | ||
| 670 | (sleaf "open-on-workspace" "vid") | ||
| 671 | (plain "default-column-width" [(sleaf "proportion" 1.)]) | ||
| 672 | ]) | ||
| 673 | (plain "window-rule" [ | ||
| 674 | (sleaf "match" { app-id = "^kitty-play$"; }) | ||
| 675 | (sleaf "open-on-workspace" "vid") | ||
| 676 | (sleaf "open-focused" false) | ||
| 677 | ]) | ||
| 678 | (plain "window-rule" [ | ||
| 679 | (sleaf "match" { app-id = "^chrome-audiobookshelf\.yggdrasil\.li__-Default$"; }) | ||
| 680 | (sleaf "match" { app-id = "^YouTube Music Desktop App$"; }) | ||
| 681 | (sleaf "open-on-workspace" "vid") | ||
| 682 | ]) | ||
| 683 | (plain "window-rule" [ | ||
| 684 | (sleaf "match" { app-id = "^pdfpc$"; }) | ||
| 685 | (plain "default-column-width" [(sleaf "proportion" 1.)]) | ||
| 686 | ]) | ||
| 687 | (plain "window-rule" [ | ||
| 688 | (sleaf "match" { app-id = "^pdfpc$"; title = "^.*presentation.*$"; }) | ||
| 689 | (plain "default-column-width" [(sleaf "proportion" 1.)]) | ||
| 690 | (sleaf "open-fullscreen" true) | ||
| 691 | (sleaf "open-on-workspace" "bmr") | ||
| 692 | (sleaf "open-focused" false) | ||
| 693 | ]) | ||
| 694 | (plain "window-rule" (normalize-nodes [ | ||
| 695 | (map (sleaf "match") [ | ||
| 696 | { app-id = "^Gimp-"; title = "^Quit GIMP$"; } | ||
| 697 | { app-id = "^org\\.kde\\.polkit-kde-authentication-agent-1$"; } | ||
| 698 | { app-id = "^xdg-desktop-portal-gtk$"; } | ||
| 699 | ]) | ||
| 700 | (sleaf "open-floating" true) | ||
| 701 | ])) | ||
| 702 | (plain "window-rule" [ | ||
| 703 | (sleaf "match" { app-id = "^org\\.pwmt\\.zathura$"; }) | ||
| 704 | (sleaf "match" { app-id = "^evince$"; }) | ||
| 705 | (sleaf "match" { app-id = "^org\\.gnome\\.Papers$"; }) | ||
| 706 | (sleaf "default-column-display" "tabbed") | ||
| 707 | ]) | ||
| 708 | |||
| 709 | (plain "layer-rule" [ | ||
| 710 | (sleaf "match" { namespace = "^notifications$"; }) | ||
| 711 | (sleaf "match" { namespace = "^bar$"; }) | ||
| 712 | (sleaf "match" { namespace = "^launcher$"; }) | ||
| 713 | (sleaf "block-out-from" "screencast") | ||
| 714 | ]) | ||
| 715 | |||
| 716 | (plain "binds" | ||
| 717 | (let | ||
| 718 | bind = name: cfg: node name [(lib.removeAttrs cfg ["action"])] (lib.mapAttrsToList leaf (lib.removeAttrs cfg.action ["__functor"])); | ||
| 719 | in | ||
| 720 | normalize-nodes [ | ||
| 721 | (lib.mapAttrsToList bind (with config.lib.niri.actions; { | ||
| 722 | "Mod+Slash".action = show-hotkey-overlay; | ||
| 723 | |||
| 724 | "Mod+Return".action = spawn terminal; | ||
| 725 | "Mod+Shift+Return".action = | ||
| 726 | let | ||
| 727 | nushellKitty = pkgs.symlinkJoin { | ||
| 728 | name = "nushell-kitty"; | ||
| 729 | paths = [ config.programs.kitty.package ]; | ||
| 730 | buildInputs = [ pkgs.makeWrapper ]; | ||
| 731 | postBuild = '' | ||
| 732 | wrapProgram $out/bin/kitty \ | ||
| 733 | --add-flags "--config ${pkgs.writeText "kitty.conf" '' | ||
| 734 | include $HOME/${config.xdg.configFile."kitty/kitty.conf".target} | ||
| 735 | shell ${lib.getExe config.programs.nushell.package} | ||
| 736 | ''}" | ||
| 737 | ''; | ||
| 738 | }; | ||
| 739 | in spawn (lib.getExe' nushellKitty "kitty"); | ||
| 740 | "Mod+Q".action = close-window; | ||
| 741 | "Mod+O".action = spawn (lib.getExe config.programs.fuzzel.package); | ||
| 742 | "Mod+Shift+O".action = spawn (lib.getExe config.programs.fuzzel.package) "--list-executables-in-path"; | ||
| 743 | |||
| 744 | "Mod+Alt+E".action = spawn (lib.getExe' config.services.emacs.package "emacsclient") "-c"; | ||
| 745 | "Mod+Alt+Y".action = spawn (lib.getExe (pkgs.writeShellApplication { | ||
| 746 | name = "queue-yt-dlp"; | ||
| 747 | runtimeInputs = with pkgs; [ wl-clipboard-rs socat ]; | ||
| 748 | text = '' | ||
| 749 | socat STDIO UNIX-CONNECT:"$XDG_RUNTIME_DIR"/yt-dlp.sock <<<$'{ "urls": ["'"$(wl-paste)"$'"] }' | ||
| 750 | ''; | ||
| 751 | })); | ||
| 752 | "Mod+Alt+L".action = spawn (lib.getExe (pkgs.writeShellApplication { | ||
| 753 | name = "queue-yt-dlp"; | ||
| 754 | runtimeInputs = with pkgs; [ wl-clipboard-rs config.programs.kitty.package ]; | ||
| 755 | text = '' | ||
| 756 | exec -- kitty --app-id kitty-play --directory "$HOME"/media mpv "$(wl-paste)" | ||
| 757 | ''; | ||
| 758 | })); | ||
| 759 | "Mod+Alt+M".action = spawn (lib.getExe' pkgs.screen-message "sm") "-n" "Fira Mono" "-a" "1" "-f" "#fff" "-b" "#000"; | ||
| 760 | |||
| 761 | "Mod+U".action = spawn (lib.getExe (pkgs.writeShellApplication { | ||
| 762 | name = "qalc-fuzzel"; | ||
| 763 | runtimeInputs = with pkgs; [ wl-clipboard-rs libqalculate config.programs.fuzzel.package coreutils findutils libnotify gnugrep ]; | ||
| 764 | text = '' | ||
| 765 | RESULTS_DIR="$HOME/.cache/qalc-fuzzel" | ||
| 766 | prev() { | ||
| 767 | FOUND=false | ||
| 768 | while IFS= read -r line; do | ||
| 769 | [[ -n "$line" ]] || continue | ||
| 770 | FOUND=true | ||
| 771 | echo "$line" | ||
| 772 | done < <(export LC_ALL=C.UTF-8; echo; find "$RESULTS_DIR" -type f -printf $'%T@ %p\n' | sort -n | cut -d' ' -f2- | xargs -r cat) | ||
| 773 | $FOUND || echo | ||
| 774 | } | ||
| 775 | FUZZEL_RES=$(prev | fuzzel --dmenu --prompt "qalc> " --width=60) || exit $? | ||
| 776 | if [[ "$FUZZEL_RES" =~ .*\ =\ .* ]]; then | ||
| 777 | QALC_RES="$FUZZEL_RES" | ||
| 778 | QALC_RET=0 | ||
| 779 | else | ||
| 780 | QALC_RES=$(qalc -set "autocalc off" "$FUZZEL_RES" 2>&1) | ||
| 781 | QALC_RET=$? | ||
| 782 | fi | ||
| 783 | [[ -n "$QALC_RES" ]] || exit 1 | ||
| 784 | EXISTING=false | ||
| 785 | set +o pipefail | ||
| 786 | grep -Fxrl "$QALC_RES" "$RESULTS_DIR" | xargs -r touch | ||
| 787 | [[ ''${PIPESTATUS[0]} -eq 0 ]] && EXISTING=true | ||
| 788 | set -o pipefail | ||
| 789 | if [[ $QALC_RET -eq 0 ]] && ! $EXISTING; then | ||
| 790 | set +o pipefail | ||
| 791 | RES_FILE="$RESULTS_DIR"/$(date -uIs).$(tr -Cd 'a-zA-Z0-9' </dev/random | head -c 10) | ||
| 792 | set -o pipefail | ||
| 793 | cat >"$RES_FILE" <<<"$QALC_RES" | ||
| 794 | fi | ||
| 795 | [[ "$QALC_RES" =~ .*\ =\ (.*) ]] && QALC_RES="''${BASH_REMATCH[1]}" | ||
| 796 | [[ $QALC_RET -eq 0 ]] && wl-copy "$QALC_RES" | ||
| 797 | notify-send "$QALC_RES" | ||
| 798 | ''; | ||
| 799 | })); | ||
| 800 | "Mod+Shift+U".action = | ||
| 801 | let | ||
| 802 | qalcKitty = pkgs.symlinkJoin { | ||
| 803 | name = "qalc-kitty"; | ||
| 804 | paths = [ config.programs.kitty.package ]; | ||
| 805 | buildInputs = [ pkgs.makeWrapper ]; | ||
| 806 | postBuild = '' | ||
| 807 | wrapProgram $out/bin/kitty \ | ||
| 808 | --add-flags "--config ${pkgs.writeText "kitty.conf" '' | ||
| 809 | include $HOME/${config.xdg.configFile."kitty/kitty.conf".target} | ||
| 810 | shell ${lib.getExe pkgs.libqalculate} | ||
| 811 | ''}" | ||
| 812 | ''; | ||
| 813 | }; | ||
| 814 | in spawn (lib.getExe' qalcKitty "kitty"); | ||
| 815 | "Mod+E".action = spawn (lib.getExe (pkgs.writeShellApplication { | ||
| 816 | name = "emoji-fuzzel"; | ||
| 817 | runtimeInputs = with pkgs; [ config.programs.fuzzel.package wtype wl-clipboard-rs ]; | ||
| 818 | text = '' | ||
| 819 | FUZZEL_RES=$(fuzzel --dmenu --prompt "emoji> " --cache "$HOME"/.cache/fuzzel-emoji --width=60 <"$HOME"/.local/share/emoji-data/list.txt) || exit $? | ||
| 820 | [[ -n "$FUZZEL_RES" ]] || exit 1 | ||
| 821 | wl-copy "$(cut -d ':' -f 1 <<<"$FUZZEL_RES" | tr -d '\n')" && wtype -k XF86Paste | ||
| 822 | ''; | ||
| 823 | })); | ||
| 824 | "Print".action = screenshot; | ||
| 825 | "Control+Print".action = screenshot-window; | ||
| 826 | "Shift+Print".action = kdl.magic-leaf "screenshot-screen"; | ||
| 827 | "Mod+B".action = with-select-window-action ".workspace_id == ($active_workspace | tonumber)" "{\"Action\":{\"FocusWindow\":{\"id\": .id}}}"; | ||
| 828 | "Mod+Shift+B".action = with-select-window-action "true" "{\"Action\":{\"FocusWindow\":{\"id\": .id}}}"; | ||
| 829 | |||
| 830 | "Mod+Escape" = { | ||
| 831 | allow-inhibiting = false; | ||
| 832 | action = toggle-keyboard-shortcuts-inhibit; | ||
| 833 | }; | ||
| 834 | |||
| 835 | "Mod+H".action = focus-column-left; | ||
| 836 | "Mod+T".action = focus-window-down; | ||
| 837 | "Mod+N".action = focus-window-up; | ||
| 838 | "Mod+S".action = focus-column-right; | ||
| 839 | |||
| 840 | "Mod+Shift+H".action = move-column-left; | ||
| 841 | "Mod+Shift+T".action = move-window-down; | ||
| 842 | "Mod+Shift+N".action = move-window-up; | ||
| 843 | "Mod+Shift+S".action = move-column-right; | ||
| 844 | |||
| 845 | "Mod+Control+H".action = focus-monitor-left; | ||
| 846 | "Mod+Control+T".action = focus-monitor-down; | ||
| 847 | "Mod+Control+N".action = focus-monitor-up; | ||
| 848 | "Mod+Control+S".action = focus-monitor-right; | ||
| 849 | |||
| 850 | "Mod+Shift+Control+H".action = move-workspace-to-monitor-left; | ||
| 851 | "Mod+Shift+Control+T".action = move-workspace-to-monitor-down; | ||
| 852 | "Mod+Shift+Control+N".action = move-workspace-to-monitor-up; | ||
| 853 | "Mod+Shift+Control+S".action = move-workspace-to-monitor-right; | ||
| 854 | |||
| 855 | "Mod+G".action = focus-adjacent-workspace "down"; | ||
| 856 | "Mod+C".action = focus-adjacent-workspace "up"; | ||
| 857 | |||
| 858 | "Mod+Shift+G".action = move-column-to-adjacent-workspace "down"; | ||
| 859 | "Mod+Shift+C".action = move-column-to-adjacent-workspace "up"; | ||
| 860 | |||
| 861 | "Mod+Shift+Control+G".action = move-workspace-down; | ||
| 862 | "Mod+Shift+Control+C".action = move-workspace-up; | ||
| 863 | |||
| 864 | "Mod+ParenLeft".action = focus-workspace "comm"; | ||
| 865 | "Mod+Shift+ParenLeft".action = kdl.magic-leaf "move-column-to-workspace" "comm"; | ||
| 866 | |||
| 867 | "Mod+ParenRight".action = focus-workspace "web"; | ||
| 868 | "Mod+Shift+ParenRight".action = kdl.magic-leaf "move-column-to-workspace" "web"; | ||
| 869 | |||
| 870 | "Mod+BraceRight".action = focus-workspace "read"; | ||
| 871 | "Mod+Shift+BraceRight".action = kdl.magic-leaf "move-column-to-workspace" "read"; | ||
| 872 | |||
| 873 | "Mod+BraceLeft".action = focus-workspace "mon"; | ||
| 874 | "Mod+Shift+BraceLeft".action = kdl.magic-leaf "move-column-to-workspace" "mon"; | ||
| 875 | |||
| 876 | "Mod+Asterisk".action = focus-workspace "vid"; | ||
| 877 | "Mod+Shift+Asterisk".action = kdl.magic-leaf "move-column-to-workspace" "vid"; | ||
| 878 | |||
| 879 | "Mod+Plus".action = with-unnamed-workspace-action ''{"Action":{"FocusWorkspace":{"reference":{"Id": .id}}}}''; | ||
| 880 | "Mod+Shift+Plus".action = with-unnamed-workspace-action ''{"Action":{"MoveColumnToWorkspace":{"reference":{"Id": .id}, "focus": true}}}''; | ||
| 881 | |||
| 882 | "Mod+M".action = consume-or-expel-window-left; | ||
| 883 | "Mod+W".action = consume-or-expel-window-right; | ||
| 884 | |||
| 885 | "Mod+Shift+M".action = toggle-column-tabbed-display; | ||
| 886 | |||
| 887 | "Mod+R".action = switch-preset-column-width; | ||
| 888 | "Mod+Shift+R".action = maximize-column; | ||
| 889 | "Mod+Shift+Ctrl+R".action = switch-preset-window-height; | ||
| 890 | "Mod+F".action = center-column; | ||
| 891 | "Mod+Shift+F".action = toggle-windowed-fullscreen; | ||
| 892 | "Mod+Ctrl+Shift+F".action = fullscreen-window; | ||
| 893 | |||
| 894 | "Mod+V".action = switch-focus-between-floating-and-tiling; | ||
| 895 | "Mod+Shift+V".action = toggle-window-floating; | ||
| 896 | |||
| 897 | "Mod+Left".action = set-column-width "-10%"; | ||
| 898 | "Mod+Down".action = set-window-height "-10%"; | ||
| 899 | "Mod+Up".action = set-window-height "+10%"; | ||
| 900 | "Mod+Right".action = set-column-width "+10%"; | ||
| 901 | |||
| 902 | "Mod+Shift+Z" = { | ||
| 903 | action = power-off-monitors; | ||
| 904 | allow-when-locked = true; | ||
| 905 | }; | ||
| 906 | "Mod+Shift+E".action = quit; | ||
| 907 | |||
| 908 | # "Mod+Semicolon".action = spawn makoctl "dismiss" "--group"; | ||
| 909 | # "Mod+Shift+Semicolon".action = spawn makoctl "dismiss" "--all"; | ||
| 910 | # "Mod+Period".action = spawn makoctl "menu" "--" (lib.getExe config.programs.fuzzel.package) "--dmenu"; | ||
| 911 | # "Mod+Comma".action = spawn makoctl "restore"; | ||
| 912 | |||
| 913 | "Mod+Control+W".action = with-empty-unnamed-workspace-action "{\"Action\":{\"FocusWorkspace\":{\"reference\":{\"Id\": $workspace_id}}}}"; | ||
| 914 | "Mod+Control+Shift+W".action = with-empty-unnamed-workspace-action "{\"Action\":{\"MoveColumnToWorkspace\":{\"reference\":{\"Id\": $workspace_id}, \"focus\": true}}}"; | ||
| 915 | |||
| 916 | "Mod+X".action = set-dynamic-cast-window; | ||
| 917 | "Mod+Shift+X".action = set-dynamic-cast-monitor; | ||
| 918 | "Mod+Control+Shift+X".action = clear-dynamic-cast-target; | ||
| 919 | |||
| 920 | "Mod+D".action = with-urgent-window-action "{\"Action\":{\"FocusWindow\":{\"id\": .id}}}"; | ||
| 921 | "Mod+Shift+D".action = with-focused-window-action "{\"Action\":{\"UnsetUrgent\":{\"id\": .id}}}"; | ||
| 922 | |||
| 923 | "Mod+K".action = spawn (lib.getExe' pkgs.worktime "worktime-ui"); | ||
| 924 | "Mod+Shift+K".action = spawn (lib.getExe' pkgs.worktime "worktime-stop"); | ||
| 925 | })) | ||
| 926 | (lib.mapAttrsToList (name: cfg: node name [(lib.removeAttrs cfg ["action"])] [cfg.action]) (let | ||
| 927 | shell = obj: leaf "send-unix" [ | ||
| 928 | { path = ''''${XDG_RUNTIME_DIR}/shell.sock''; } | ||
| 929 | (builtins.toJSON obj + "\n") | ||
| 930 | ]; | ||
| 931 | in { | ||
| 932 | "XF86AudioRaiseVolume" = { | ||
| 933 | allow-when-locked = true; | ||
| 934 | action = shell { Volume.volume = "up"; }; | ||
| 935 | }; | ||
| 936 | "XF86AudioLowerVolume" = { | ||
| 937 | allow-when-locked = true; | ||
| 938 | action = shell { Volume.volume = "down"; }; | ||
| 939 | }; | ||
| 940 | "XF86AudioMute" = { | ||
| 941 | allow-when-locked = true; | ||
| 942 | action = shell { Volume.muted = "toggle"; }; | ||
| 943 | }; | ||
| 944 | "XF86AudioMicMute" = { | ||
| 945 | allow-when-locked = true; | ||
| 946 | action = shell { Volume."mic-muted" = "toggle"; }; | ||
| 947 | }; | ||
| 948 | "XF86MonBrightnessUp" = { | ||
| 949 | action = shell { Brightness = "up"; }; | ||
| 950 | allow-when-locked = true; | ||
| 951 | }; | ||
| 952 | "XF86MonBrightnessDown" = { | ||
| 953 | action = shell { Brightness = "down"; }; | ||
| 954 | allow-when-locked = true; | ||
| 955 | }; | ||
| 956 | "Mod+Shift+L".action = shell { LockSession = {}; }; | ||
| 957 | "Mod+Shift+Minus" = { | ||
| 958 | action = shell { Suspend = {}; }; | ||
| 959 | allow-when-locked = true; | ||
| 960 | }; | ||
| 961 | "Mod+Shift+Control+Minus" = { | ||
| 962 | action = shell { Hibernate = {}; }; | ||
| 963 | allow-when-locked = true; | ||
| 964 | }; | ||
| 965 | "Mod+Shift+P" = { | ||
| 966 | action = shell { Mpris = { PauseAll = {}; }; }; | ||
| 967 | allow-when-locked = true; | ||
| 968 | }; | ||
| 969 | "Mod+Semicolon".action = shell { Notifications = { DismissGroup = {}; }; }; | ||
| 970 | "Mod+Shift+Semicolon".action = shell { Notifications = { DismissAll = {}; }; }; | ||
| 971 | })) | ||
| 972 | (map ({ name, selector, spawn, key, ...}: if key != null && selector != null && spawn != null then bind key { action = focus-or-spawn-action selector name spawn; } else null) cfg.scratchspaces) | ||
| 973 | (map ({ name, moveKey, ...}: if moveKey != null then bind moveKey { action = kdl.magic-leaf "move-column-to-workspace" name; } else null) cfg.scratchspaces) | ||
| 974 | ] | ||
| 975 | )) | ||
| 976 | ]; | ||
| 977 | }; | ||
| 978 | } | ||
diff --git a/accounts/gkleen@sif/niri/mako.nix b/accounts/gkleen@sif/niri/mako.nix new file mode 100644 index 00000000..3d246d96 --- /dev/null +++ b/accounts/gkleen@sif/niri/mako.nix | |||
| @@ -0,0 +1,112 @@ | |||
| 1 | { config, lib, pkgs, ... }: | ||
| 2 | { | ||
| 3 | config = lib.mkIf false { | ||
| 4 | services.mako = { | ||
| 5 | enable = true; | ||
| 6 | settings = { | ||
| 7 | font = "Fira Sans 10"; | ||
| 8 | format = "<i>%s</i>\\n%b"; | ||
| 9 | margin = "2"; | ||
| 10 | max-visible = -1; | ||
| 11 | background-color = "#000000dd"; | ||
| 12 | progress-color = "source #223544ff"; | ||
| 13 | width = 384; | ||
| 14 | outer-margin = 1; | ||
| 15 | max-history = 100; | ||
| 16 | max-icon-size = 48; | ||
| 17 | |||
| 18 | grouped.format = "<b>(%g)</b> <i>%s</i>\\n%b"; | ||
| 19 | "urgency=low".text-color = "#999999ff"; | ||
| 20 | "urgency=critical".background-color = "#900000dd"; | ||
| 21 | "app-name=Element".group-by = "summary"; | ||
| 22 | "app-name=poweralertd" = { | ||
| 23 | history = false; | ||
| 24 | ignore-timeout = true; | ||
| 25 | default-timeout = 2000; | ||
| 26 | }; | ||
| 27 | "app-name=worktime".history = false; | ||
| 28 | "mode=silent".invisible = true; | ||
| 29 | }; | ||
| 30 | package = pkgs.symlinkJoin { | ||
| 31 | name = "${pkgs.mako.name}-wrapped"; | ||
| 32 | paths = with pkgs; [ mako ]; | ||
| 33 | inherit (pkgs.mako) meta; | ||
| 34 | postBuild = '' | ||
| 35 | rm -r $out/share/dbus-1 | ||
| 36 | ''; | ||
| 37 | }; | ||
| 38 | }; | ||
| 39 | systemd.user.services.mako = { | ||
| 40 | Unit = { | ||
| 41 | Description = "Mako notification daemon"; | ||
| 42 | PartOf = [ "graphical-session.target" ]; | ||
| 43 | }; | ||
| 44 | Install = { | ||
| 45 | WantedBy = [ "graphical-session.target" ]; | ||
| 46 | }; | ||
| 47 | Service = { | ||
| 48 | Type = "dbus"; | ||
| 49 | BusName = "org.freedesktop.Notifications"; | ||
| 50 | ExecStart = lib.getExe config.services.mako.package; | ||
| 51 | RestartSec = 5; | ||
| 52 | Restart = "always"; | ||
| 53 | }; | ||
| 54 | }; | ||
| 55 | |||
| 56 | systemd.user.services.mako-follows-focus = { | ||
| 57 | Unit = { | ||
| 58 | BindsTo = [ "niri.service" "mako.service" ]; | ||
| 59 | After = [ "niri.service" "mako.service" ]; | ||
| 60 | }; | ||
| 61 | Service = { | ||
| 62 | Type = "simple"; | ||
| 63 | Restart = "always"; | ||
| 64 | ExecStart = pkgs.writers.writePython3 "mako-follows-focus" { | ||
| 65 | libraries = with pkgs.python3Packages; []; | ||
| 66 | } '' | ||
| 67 | import os | ||
| 68 | import socket | ||
| 69 | import json | ||
| 70 | import subprocess | ||
| 71 | |||
| 72 | |||
| 73 | current_output = None | ||
| 74 | workspaces = [] | ||
| 75 | |||
| 76 | |||
| 77 | def output_changed(new_output): | ||
| 78 | global current_output | ||
| 79 | |||
| 80 | if current_output == new_output: | ||
| 81 | return | ||
| 82 | |||
| 83 | current_output = new_output | ||
| 84 | subprocess.run(["makoctl", "reload"]) | ||
| 85 | |||
| 86 | |||
| 87 | sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) | ||
| 88 | sock.connect(os.environ["NIRI_SOCKET"]) | ||
| 89 | sock.send(b"\"EventStream\"\n") | ||
| 90 | for line in sock.makefile(buffering=1, encoding='utf-8'): | ||
| 91 | if line_json := json.loads(line): | ||
| 92 | if "WorkspacesChanged" in line_json: | ||
| 93 | workspaces = line_json["WorkspacesChanged"]["workspaces"] | ||
| 94 | for workspace in workspaces: | ||
| 95 | if not workspace["is_focused"]: | ||
| 96 | continue | ||
| 97 | output_changed(workspace["output"]) | ||
| 98 | break | ||
| 99 | if "WorkspaceActivated" in line_json and line_json["WorkspaceActivated"]["focused"]: # noqa: E501 | ||
| 100 | for workspace in workspaces: | ||
| 101 | if not workspace["id"] == line_json["WorkspaceActivated"]["id"]: # noqa: E501 | ||
| 102 | continue | ||
| 103 | output_changed(workspace["output"]) | ||
| 104 | break | ||
| 105 | ''; | ||
| 106 | }; | ||
| 107 | Install = { | ||
| 108 | WantedBy = [ "mako.service" ]; | ||
| 109 | }; | ||
| 110 | }; | ||
| 111 | }; | ||
| 112 | } | ||
diff --git a/accounts/gkleen@sif/shell/default.nix b/accounts/gkleen@sif/shell/default.nix new file mode 100644 index 00000000..44462865 --- /dev/null +++ b/accounts/gkleen@sif/shell/default.nix | |||
| @@ -0,0 +1,115 @@ | |||
| 1 | { config, pkgs, lib, ... }: | ||
| 2 | |||
| 3 | { | ||
| 4 | config = { | ||
| 5 | programs.quickshell = { | ||
| 6 | enable = true; | ||
| 7 | package = pkgs.symlinkJoin { | ||
| 8 | pname = pkgs.quickshell.pname + "-wrapped"; | ||
| 9 | inherit (pkgs.quickshell) version meta; | ||
| 10 | paths = [ pkgs.quickshell ]; | ||
| 11 | buildInputs = [ pkgs.makeWrapper ]; | ||
| 12 | postBuild = '' | ||
| 13 | for binary in quickshell qs; do | ||
| 14 | wrapProgram $out/bin/$binary \ | ||
| 15 | --prefix QML_IMPORT_PATH : ${pkgs.qt6Packages.callPackage ./quickshell-plugins {}}/${pkgs.qt6.qtbase.qtQmlPrefix} | ||
| 16 | done | ||
| 17 | ''; | ||
| 18 | }; | ||
| 19 | config = { | ||
| 20 | src = ./quickshell; | ||
| 21 | replacements = { | ||
| 22 | ignore_workspaces = builtins.toJSON (map ({ name, ... }: name) config.programs.niri.scratchspaces); | ||
| 23 | wallpapers = builtins.toJSON (pkgs.stdenvNoCC.mkDerivation { | ||
| 24 | name = "wallpapers"; | ||
| 25 | srcs = [ | ||
| 26 | (pkgs.fetchurl { | ||
| 27 | url = "https://esawebb.org/media/archives/images/publicationtiff10k/carinanebula3.tif"; | ||
| 28 | hash = "sha256-YxZEweDKJfvfrdxb/QFmgJhcZDEJYxotoHrG+RRn1tw="; | ||
| 29 | }) | ||
| 30 | (pkgs.fetchurl { | ||
| 31 | url = "https://esawebb.org/media/archives/images/original/pillarsofcreation_composite.tif"; | ||
| 32 | hash = "sha256-qRiODxR0lZWdxgYXna0fNRFFDErpBJDwOJuQl6sNjRc="; | ||
| 33 | }) | ||
| 34 | (pkgs.fetchurl { | ||
| 35 | url = "https://esawebb.org/media/archives/images/publicationtiff10k/weic2212a.tif"; | ||
| 36 | hash = "sha256-l2fqE/z//C1a0xkvZwsnwPbTSb+WuA11h+SUl3E1dhw="; | ||
| 37 | }) | ||
| 38 | (pkgs.fetchurl { | ||
| 39 | url = "https://esawebb.org/media/archives/images/publicationtiff10k/weic2415a.tif"; | ||
| 40 | hash = "sha256-onBy7cPoUpDuzQStbY2E+qmlGgSLXPwFCLX53ukAb4c="; | ||
| 41 | }) | ||
| 42 | (pkgs.fetchurl { | ||
| 43 | url = "https://esawebb.org/media/archives/images/publicationtiff10k/weic2330a.tif"; | ||
| 44 | hash = "sha256-nn0ZtjZIrPcpj3YcLTsrL7XiXvyh3QYgCSmdDMD+3OM="; | ||
| 45 | }) | ||
| 46 | (pkgs.fetchurl { | ||
| 47 | url = "https://esawebb.org/media/archives/images/original/weic2426a.tif"; | ||
| 48 | hash = "sha256-EDnfPn3GE9jt6XPqiGInP7E2F3Az7d25NqATSWltDv0="; | ||
| 49 | }) | ||
| 50 | (pkgs.fetchurl { | ||
| 51 | url = "https://esawebb.org/media/archives/images/original/weic2503a.tif"; | ||
| 52 | hash = "sha256-3/RX6RQp8naELcgReHPd5/zhJkoCjnA10w5BEnNo+qI="; | ||
| 53 | }) | ||
| 54 | (pkgs.fetchurl { | ||
| 55 | url = "https://esawebb.org/media/archives/images/original/weic2506a.tif"; | ||
| 56 | hash = "sha256-aDld0aoY1owRxDVf7Jcyw71TH42M1foYotxn2thyFYw="; | ||
| 57 | }) | ||
| 58 | (pkgs.fetchurl { | ||
| 59 | url = "https://esawebb.org/media/archives/images/original/weic2514a.tif"; | ||
| 60 | hash = "sha256-jTi1G1Ofo5xsF6ggrbtYJHxqLaCQ7edM5B3uORiVQtg="; | ||
| 61 | }) | ||
| 62 | (pkgs.fetchurl { | ||
| 63 | url = "https://esawebb.org/media/archives/images/original/weic2425c.tif"; | ||
| 64 | hash = "sha256-oaEOexSJHEGj090dJF3ct5HAoR+Y5gRiPrUlxdvnTRo="; | ||
| 65 | }) | ||
| 66 | ]; | ||
| 67 | |||
| 68 | dontUnpack = true; | ||
| 69 | |||
| 70 | buildInputs = [ pkgs.imagemagick ]; | ||
| 71 | buildPhase = '' | ||
| 72 | runHook preBuild | ||
| 73 | |||
| 74 | typeset sources=($srcs) | ||
| 75 | |||
| 76 | mkdir -p $out | ||
| 77 | magick ''${sources[0]} -crop 10000x5625+0+79 +repage -define jpeg:extent=10MB $out/carinanebula3.jpeg | ||
| 78 | magick ''${sources[1]} -crop 6716x3778+329+80 +repage -define jpeg:extent=10MB $out/pillarsofcreation_composite.jpeg | ||
| 79 | magick ''${sources[2]} -crop 10000x5625+0+79 +repage -define jpeg:extent=10MB $out/weic2212a.jpeg | ||
| 80 | magick ''${sources[3]} -crop 7650x4302+1166+389 +repage -define jpeg:extent=10MB $out/weic2415a.jpeg | ||
| 81 | magick ''${sources[4]} -crop 8732x4912+0+434 +repage -define jpeg:extent=10MB $out/weic2330a.jpeg | ||
| 82 | magick ''${sources[5]} -crop 5302x2982+636+0 +repage -define jpeg:extent=10MB $out/weic2426a.jpeg | ||
| 83 | magick ''${sources[6]} -crop 4328x2434+0+906 +repage -define jpeg:extent=10MB $out/weic2503a.jpeg | ||
| 84 | magick ''${sources[7]} -crop 4152x2335+0+666 +repage -define jpeg:extent=10MB $out/weic2506a.jpeg | ||
| 85 | magick ''${sources[8]} -crop 4320x2430+0+0 +repage -define jpeg:extent=10MB $out/weic2514a.jpeg | ||
| 86 | magick ''${sources[9]} -crop 5863x3298+0+477 +repage -define jpeg:extent=10MB $out/weic2425c.jpeg | ||
| 87 | |||
| 88 | runHook postBuild | ||
| 89 | ''; | ||
| 90 | }); | ||
| 91 | niri_session = builtins.toJSON [ | ||
| 92 | (pkgs.writeShellScript "niri-session" '' | ||
| 93 | exec ${lib.getExe pkgs.dex} -w ${config.programs.niri.package}/share/wayland-sessions/niri.desktop &>/tmp/niri-session-$$.log | ||
| 94 | '') | ||
| 95 | # (lib.getExe pkgs.dex) | ||
| 96 | # "${config.programs.niri.package}/share/wayland-sessions/niri.desktop" | ||
| 97 | ]; | ||
| 98 | username = builtins.toJSON config.home.username; | ||
| 99 | mdi = builtins.toJSON (pkgs.fetchFromGitHub { | ||
| 100 | owner = "Templarian"; | ||
| 101 | repo = "MaterialDesign"; | ||
| 102 | rev = "2424e748e0cc63ab7b9c095a099b9fe239b737c0"; | ||
| 103 | hash = "sha256-QMGl7soAhErrrnY3aKOZpt49yebkSNzy10p/v5OaqQ0="; | ||
| 104 | }); | ||
| 105 | worktime = builtins.toJSON (lib.getExe pkgs.worktime); | ||
| 106 | }; | ||
| 107 | }; | ||
| 108 | }; | ||
| 109 | systemd.user.services.quickshell = { | ||
| 110 | Service = { | ||
| 111 | RuntimeDirectory = "quickshell"; | ||
| 112 | }; | ||
| 113 | }; | ||
| 114 | }; | ||
| 115 | } | ||
diff --git a/accounts/gkleen@sif/shell/quickshell-plugins/CMakeLists.txt b/accounts/gkleen@sif/shell/quickshell-plugins/CMakeLists.txt new file mode 100644 index 00000000..020c0515 --- /dev/null +++ b/accounts/gkleen@sif/shell/quickshell-plugins/CMakeLists.txt | |||
| @@ -0,0 +1,168 @@ | |||
| 1 | set(INSTALL_QMLDIR "" CACHE STRING "QML install dir") | ||
| 2 | set(INSTALL_QML_PREFIX "" CACHE STRING "QML install prefix") | ||
| 3 | |||
| 4 | # There doesn't seem to be a standard cross-distro qml install path. | ||
| 5 | if ("${INSTALL_QMLDIR}" STREQUAL "" AND "${INSTALL_QML_PREFIX}" STREQUAL "") | ||
| 6 | message(WARNING "Neither INSTALL_QMLDIR nor INSTALL_QML_PREFIX is set. QML modules will not be installed.") | ||
| 7 | else() | ||
| 8 | if ("${INSTALL_QMLDIR}" STREQUAL "") | ||
| 9 | set(QML_FULL_INSTALLDIR "${CMAKE_INSTALL_PREFIX}/${INSTALL_QML_PREFIX}") | ||
| 10 | else() | ||
| 11 | set(QML_FULL_INSTALLDIR "${INSTALL_QMLDIR}") | ||
| 12 | endif() | ||
| 13 | |||
| 14 | message(STATUS "QML install dir: ${QML_FULL_INSTALLDIR}") | ||
| 15 | endif() | ||
| 16 | |||
| 17 | # Install a given target as a QML module. This is mostly pulled from ECM, as there does not seem | ||
| 18 | # to be an official way to do it. | ||
| 19 | # see https://github.com/KDE/extra-cmake-modules/blob/fe0f606bf7f222e36f7560fd7a2c33ef993e23bb/modules/ECMQmlModule6.cmake#L160 | ||
| 20 | function(install_qml_module arg_TARGET) | ||
| 21 | if (NOT DEFINED QML_FULL_INSTALLDIR) | ||
| 22 | return() | ||
| 23 | endif() | ||
| 24 | |||
| 25 | qt_query_qml_module(${arg_TARGET} | ||
| 26 | URI module_uri | ||
| 27 | VERSION module_version | ||
| 28 | PLUGIN_TARGET module_plugin_target | ||
| 29 | TARGET_PATH module_target_path | ||
| 30 | QMLDIR module_qmldir | ||
| 31 | TYPEINFO module_typeinfo | ||
| 32 | QML_FILES module_qml_files | ||
| 33 | RESOURCES module_resources | ||
| 34 | ) | ||
| 35 | |||
| 36 | set(module_dir "${QML_FULL_INSTALLDIR}/${module_target_path}") | ||
| 37 | |||
| 38 | if (NOT TARGET "${module_plugin_target}") | ||
| 39 | message(FATAL_ERROR "install_qml_modules called for a target without a plugin") | ||
| 40 | endif() | ||
| 41 | |||
| 42 | get_target_property(target_type "${arg_TARGET}" TYPE) | ||
| 43 | if (NOT "${target_type}" STREQUAL "STATIC_LIBRARY") | ||
| 44 | install( | ||
| 45 | TARGETS "${arg_TARGET}" | ||
| 46 | LIBRARY DESTINATION "${module_dir}" | ||
| 47 | RUNTIME DESTINATION "${module_dir}" | ||
| 48 | ) | ||
| 49 | |||
| 50 | install( | ||
| 51 | TARGETS "${module_plugin_target}" | ||
| 52 | LIBRARY DESTINATION "${module_dir}" | ||
| 53 | RUNTIME DESTINATION "${module_dir}" | ||
| 54 | ) | ||
| 55 | endif() | ||
| 56 | |||
| 57 | install(FILES "${module_qmldir}" DESTINATION "${module_dir}") | ||
| 58 | install(FILES "${module_typeinfo}" DESTINATION "${module_dir}") | ||
| 59 | |||
| 60 | # Install QML files | ||
| 61 | list(LENGTH module_qml_files num_files) | ||
| 62 | if (NOT "${module_qml_files}" MATCHES "NOTFOUND" AND ${num_files} GREATER 0) | ||
| 63 | qt_query_qml_module(${arg_TARGET} QML_FILES_DEPLOY_PATHS qml_files_deploy_paths) | ||
| 64 | |||
| 65 | math(EXPR last_index "${num_files} - 1") | ||
| 66 | foreach(i RANGE 0 ${last_index}) | ||
| 67 | list(GET module_qml_files ${i} src_file) | ||
| 68 | list(GET qml_files_deploy_paths ${i} deploy_path) | ||
| 69 | get_filename_component(dst_name "${deploy_path}" NAME) | ||
| 70 | get_filename_component(dest_dir "${deploy_path}" DIRECTORY) | ||
| 71 | install(FILES "${src_file}" DESTINATION "${module_dir}/${dest_dir}" RENAME "${dst_name}") | ||
| 72 | endforeach() | ||
| 73 | endif() | ||
| 74 | |||
| 75 | # Install resources | ||
| 76 | list(LENGTH module_resources num_files) | ||
| 77 | if (NOT "${module_resources}" MATCHES "NOTFOUND" AND ${num_files} GREATER 0) | ||
| 78 | qt_query_qml_module(${arg_TARGET} RESOURCES_DEPLOY_PATHS resources_deploy_paths) | ||
| 79 | |||
| 80 | math(EXPR last_index "${num_files} - 1") | ||
| 81 | foreach(i RANGE 0 ${last_index}) | ||
| 82 | list(GET module_resources ${i} src_file) | ||
| 83 | list(GET resources_deploy_paths ${i} deploy_path) | ||
| 84 | get_filename_component(dst_name "${deploy_path}" NAME) | ||
| 85 | get_filename_component(dest_dir "${deploy_path}" DIRECTORY) | ||
| 86 | install(FILES "${src_file}" DESTINATION "${module_dir}/${dest_dir}" RENAME "${dst_name}") | ||
| 87 | endforeach() | ||
| 88 | endif() | ||
| 89 | endfunction() | ||
| 90 | |||
| 91 | |||
| 92 | cmake_minimum_required(VERSION 3.20) | ||
| 93 | project(custom LANGUAGES CXX) | ||
| 94 | |||
| 95 | find_package(Qt6 REQUIRED COMPONENTS Core Qml DBus) | ||
| 96 | |||
| 97 | qt_standard_project_setup(REQUIRES 6.6) | ||
| 98 | |||
| 99 | qt6_policy(SET QTP0001 NEW) | ||
| 100 | qt6_add_qml_module(customplugin | ||
| 101 | URI "Custom" | ||
| 102 | PLUGIN_TARGET customplugin | ||
| 103 | ) | ||
| 104 | |||
| 105 | set_source_files_properties(org.keepassxc.KeePassXC.MainWindow.xml PROPERTIES | ||
| 106 | CLASSNAME DBusKeePassXC | ||
| 107 | NO_NAMESPACE TRUE | ||
| 108 | ) | ||
| 109 | qt_add_dbus_interface(DBUS_INTERFACES | ||
| 110 | org.keepassxc.KeePassXC.MainWindow.xml | ||
| 111 | dbus_keepassxc | ||
| 112 | ) | ||
| 113 | |||
| 114 | set_source_files_properties(org.freedesktop.systemd1.Manager.xml PROPERTIES | ||
| 115 | CLASSNAME DBusSystemdManager | ||
| 116 | NO_NAMESPACE TRUE | ||
| 117 | ) | ||
| 118 | qt_add_dbus_interface(DBUS_INTERFACES | ||
| 119 | org.freedesktop.systemd1.Manager.xml | ||
| 120 | dbus_systemd_manager | ||
| 121 | ) | ||
| 122 | |||
| 123 | set_source_files_properties(org.freedesktop.login1.Manager.xml PROPERTIES | ||
| 124 | CLASSNAME DBusLogindManager | ||
| 125 | NO_NAMESPACE TRUE | ||
| 126 | ) | ||
| 127 | qt_add_dbus_interface(DBUS_INTERFACES | ||
| 128 | org.freedesktop.login1.Manager.xml | ||
| 129 | dbus_logind_manager | ||
| 130 | ) | ||
| 131 | |||
| 132 | set_source_files_properties(org.freedesktop.login1.Session.xml PROPERTIES | ||
| 133 | CLASSNAME DBusLogindSession | ||
| 134 | NO_NAMESPACE TRUE | ||
| 135 | ) | ||
| 136 | qt_add_dbus_interface(DBUS_INTERFACES | ||
| 137 | org.freedesktop.login1.Session.xml | ||
| 138 | dbus_logind_session | ||
| 139 | ) | ||
| 140 | |||
| 141 | set_source_files_properties(org.freedesktop.DBus.Properties.xml PROPERTIES | ||
| 142 | CLASSNAME DBusProperties | ||
| 143 | NO_NAMESPACE TRUE | ||
| 144 | ) | ||
| 145 | qt_add_dbus_interface(DBUS_INTERFACES | ||
| 146 | org.freedesktop.DBus.Properties.xml | ||
| 147 | dbus_properties | ||
| 148 | ) | ||
| 149 | |||
| 150 | include_directories(${CMAKE_SOURCE_DIR}/build) | ||
| 151 | |||
| 152 | target_compile_features(customplugin PUBLIC cxx_std_26) | ||
| 153 | |||
| 154 | target_link_libraries(customplugin PRIVATE | ||
| 155 | Qt6::Core | ||
| 156 | Qt6::Qml | ||
| 157 | Qt6::DBus | ||
| 158 | ) | ||
| 159 | |||
| 160 | target_sources(customplugin PRIVATE | ||
| 161 | Chrono.cpp Chrono.hpp | ||
| 162 | FileSelector.cpp FileSelector.hpp | ||
| 163 | KeePassXC.cpp KeePassXC.hpp | ||
| 164 | Systemd.cpp Systemd.hpp | ||
| 165 | ${DBUS_INTERFACES} | ||
| 166 | ) | ||
| 167 | |||
| 168 | install_qml_module(customplugin) | ||
diff --git a/accounts/gkleen@sif/shell/quickshell-plugins/Chrono.cpp b/accounts/gkleen@sif/shell/quickshell-plugins/Chrono.cpp new file mode 100644 index 00000000..22b3469b --- /dev/null +++ b/accounts/gkleen@sif/shell/quickshell-plugins/Chrono.cpp | |||
| @@ -0,0 +1,83 @@ | |||
| 1 | #include "Chrono.hpp" | ||
| 2 | |||
| 3 | #include <format> | ||
| 4 | #include <iostream> | ||
| 5 | #include <cmath> | ||
| 6 | |||
| 7 | Chrono::Chrono(QObject* parent): QObject(parent) { | ||
| 8 | QObject::connect(&this->timer, &QTimer::timeout, this, &Chrono::onTimeout); | ||
| 9 | this->update(); | ||
| 10 | } | ||
| 11 | |||
| 12 | bool Chrono::enabled() const { return this->mEnabled; } | ||
| 13 | |||
| 14 | void Chrono::setEnabled(bool enabled) { | ||
| 15 | if (enabled == this->mEnabled) return; | ||
| 16 | this->mEnabled = enabled; | ||
| 17 | emit this->enabledChanged(); | ||
| 18 | this->update(); | ||
| 19 | } | ||
| 20 | |||
| 21 | Chrono::Precision Chrono::precision() const { return this->mPrecision; } | ||
| 22 | |||
| 23 | void Chrono::setPrecision(Chrono::Precision precision) { | ||
| 24 | if (precision == this->mPrecision) return; | ||
| 25 | this->mPrecision = precision; | ||
| 26 | emit this->precisionChanged(); | ||
| 27 | this->update(); | ||
| 28 | } | ||
| 29 | |||
| 30 | void Chrono::onTimeout() { | ||
| 31 | this->setTime(this->targetTime); | ||
| 32 | this->schedule(this->targetTime); | ||
| 33 | } | ||
| 34 | |||
| 35 | void Chrono::update() { | ||
| 36 | if (this->mEnabled) { | ||
| 37 | this->setTime(std::chrono::time_point<std::chrono::system_clock>()); | ||
| 38 | this->schedule(std::chrono::time_point<std::chrono::system_clock>()); | ||
| 39 | } else { | ||
| 40 | this->timer.stop(); | ||
| 41 | } | ||
| 42 | } | ||
| 43 | |||
| 44 | void Chrono::setTime(const std::chrono::time_point<std::chrono::system_clock>& targetTime) { | ||
| 45 | using namespace std::chrono_literals; | ||
| 46 | |||
| 47 | auto currentTime = std::chrono::system_clock::now(); | ||
| 48 | auto offset = std::chrono::duration_cast<std::chrono::milliseconds>(targetTime - currentTime); | ||
| 49 | this->currentTime = abs(offset) < 500ms ? targetTime : currentTime; | ||
| 50 | |||
| 51 | switch (this->mPrecision) { | ||
| 52 | case Chrono::Hours: this->currentTime = std::chrono::time_point_cast<std::chrono::hours>(this->currentTime); | ||
| 53 | case Chrono::Minutes: this->currentTime = std::chrono::time_point_cast<std::chrono::minutes>(this->currentTime); | ||
| 54 | case Chrono::Seconds: this->currentTime = std::chrono::time_point_cast<std::chrono::seconds>(this->currentTime); | ||
| 55 | } | ||
| 56 | |||
| 57 | emit this->dateChanged(); | ||
| 58 | } | ||
| 59 | |||
| 60 | void Chrono::schedule(const std::chrono::time_point<std::chrono::system_clock>& targetTime) { | ||
| 61 | using namespace std::chrono_literals; | ||
| 62 | |||
| 63 | auto currentTime = std::chrono::system_clock::now(); | ||
| 64 | auto offset = std::chrono::duration_cast<std::chrono::milliseconds>(targetTime - currentTime); | ||
| 65 | auto nextTime = abs(offset) < 500ms ? targetTime : currentTime; | ||
| 66 | |||
| 67 | switch (this->mPrecision) { | ||
| 68 | case Chrono::Hours: nextTime = std::chrono::time_point_cast<std::chrono::hours>(nextTime) + 1h; | ||
| 69 | case Chrono::Minutes: nextTime = std::chrono::time_point_cast<std::chrono::minutes>(nextTime) + 1min; | ||
| 70 | case Chrono::Seconds: nextTime = std::chrono::time_point_cast<std::chrono::seconds>(nextTime) + 1s; | ||
| 71 | } | ||
| 72 | |||
| 73 | this->targetTime = nextTime; | ||
| 74 | this->timer.start(std::chrono::duration_cast<std::chrono::milliseconds>(nextTime - currentTime)); | ||
| 75 | } | ||
| 76 | |||
| 77 | QString Chrono::format(const QString& fmt) const { | ||
| 78 | return QString::fromStdString(std::format(std::runtime_format(fmt.toStdString()), std::chrono::zoned_time(std::chrono::current_zone(), std::chrono::time_point_cast<std::chrono::seconds>(this->currentTime)))); | ||
| 79 | } | ||
| 80 | |||
| 81 | QDateTime Chrono::date() const { | ||
| 82 | return QDateTime::fromStdTimePoint(std::chrono::time_point_cast<std::chrono::milliseconds>(this->currentTime)); | ||
| 83 | } | ||
diff --git a/accounts/gkleen@sif/shell/quickshell-plugins/Chrono.hpp b/accounts/gkleen@sif/shell/quickshell-plugins/Chrono.hpp new file mode 100644 index 00000000..04080187 --- /dev/null +++ b/accounts/gkleen@sif/shell/quickshell-plugins/Chrono.hpp | |||
| @@ -0,0 +1,55 @@ | |||
| 1 | #pragma once | ||
| 2 | |||
| 3 | #include <chrono> | ||
| 4 | |||
| 5 | #include <QDateTime> | ||
| 6 | #include <QObject> | ||
| 7 | #include <QTimer> | ||
| 8 | |||
| 9 | #include <qqmlintegration.h> | ||
| 10 | |||
| 11 | class Chrono : public QObject { | ||
| 12 | Q_OBJECT; | ||
| 13 | Q_PROPERTY(bool enabled READ enabled WRITE setEnabled NOTIFY enabledChanged); | ||
| 14 | Q_PROPERTY(Chrono::Precision precision READ precision WRITE setPrecision NOTIFY precisionChanged); | ||
| 15 | Q_PROPERTY(QDateTime date READ date NOTIFY dateChanged) | ||
| 16 | QML_ELEMENT; | ||
| 17 | |||
| 18 | public: | ||
| 19 | enum Precision : quint8 { | ||
| 20 | Hours = 1, | ||
| 21 | Minutes = 2, | ||
| 22 | Seconds = 3, | ||
| 23 | }; | ||
| 24 | Q_ENUM(Precision); | ||
| 25 | |||
| 26 | explicit Chrono(QObject* parent = nullptr); | ||
| 27 | |||
| 28 | bool enabled() const; | ||
| 29 | void setEnabled(bool enabled); | ||
| 30 | |||
| 31 | Chrono::Precision precision() const; | ||
| 32 | void setPrecision(Chrono::Precision precision); | ||
| 33 | |||
| 34 | Q_INVOKABLE QString format(const QString& fmt) const; | ||
| 35 | |||
| 36 | QDateTime date() const; | ||
| 37 | |||
| 38 | signals: | ||
| 39 | void enabledChanged(); | ||
| 40 | void precisionChanged(); | ||
| 41 | void dateChanged(); | ||
| 42 | |||
| 43 | private slots: | ||
| 44 | void onTimeout(); | ||
| 45 | |||
| 46 | private: | ||
| 47 | bool mEnabled = true; | ||
| 48 | Chrono::Precision mPrecision = Chrono::Seconds; | ||
| 49 | QTimer timer; | ||
| 50 | std::chrono::time_point<std::chrono::system_clock> currentTime, targetTime; | ||
| 51 | |||
| 52 | void update(); | ||
| 53 | void setTime(const std::chrono::time_point<std::chrono::system_clock>& targetTime); | ||
| 54 | void schedule(const std::chrono::time_point<std::chrono::system_clock>& targetTime); | ||
| 55 | }; | ||
diff --git a/accounts/gkleen@sif/shell/quickshell-plugins/FileSelector.cpp b/accounts/gkleen@sif/shell/quickshell-plugins/FileSelector.cpp new file mode 100644 index 00000000..d7051d2a --- /dev/null +++ b/accounts/gkleen@sif/shell/quickshell-plugins/FileSelector.cpp | |||
| @@ -0,0 +1,102 @@ | |||
| 1 | #include "FileSelector.hpp" | ||
| 2 | |||
| 3 | #include <sstream> | ||
| 4 | #include <vector> | ||
| 5 | #include <random> | ||
| 6 | #include <algorithm> | ||
| 7 | |||
| 8 | #include <iostream> | ||
| 9 | #include <format> | ||
| 10 | |||
| 11 | namespace fs = std::filesystem; | ||
| 12 | |||
| 13 | FileSelector::FileSelector(QObject* parent): QObject(parent) { | ||
| 14 | QObject::connect(&this->timer, &QTimer::timeout, this, &FileSelector::onTimeout); | ||
| 15 | this->timer.setTimerType(Qt::PreciseTimer); | ||
| 16 | } | ||
| 17 | |||
| 18 | QString FileSelector::directory() const { | ||
| 19 | return QString::fromStdString(this->mDirectory->string()); | ||
| 20 | } | ||
| 21 | void FileSelector::setDirectory(QString directory) { | ||
| 22 | this->mDirectory = directory.toStdString(); | ||
| 23 | if (this->mDirectory && this->mEpoch) | ||
| 24 | this->update(); | ||
| 25 | emit this->directoryChanged(); | ||
| 26 | } | ||
| 27 | |||
| 28 | unsigned int FileSelector::epoch() const { | ||
| 29 | return std::chrono::duration_cast<std::chrono::milliseconds>(*this->mEpoch).count(); | ||
| 30 | } | ||
| 31 | void FileSelector::setEpoch(unsigned int epoch) { | ||
| 32 | this->mEpoch = std::chrono::milliseconds{epoch}; | ||
| 33 | if (this->mDirectory && this->mEpoch) | ||
| 34 | this->update(); | ||
| 35 | emit this->epochChanged(); | ||
| 36 | } | ||
| 37 | |||
| 38 | QString FileSelector::seed() const { | ||
| 39 | return this->mSeed; | ||
| 40 | } | ||
| 41 | void FileSelector::setSeed(QString seed) { | ||
| 42 | this->mSeed = seed; | ||
| 43 | emit this->seedChanged(); | ||
| 44 | if (this->mDirectory && this->mEpoch) | ||
| 45 | emit this->selectedChanged(); | ||
| 46 | } | ||
| 47 | |||
| 48 | QString FileSelector::selected() const { | ||
| 49 | if (!this->mDirectory || !this->mEpoch) | ||
| 50 | return QString(); | ||
| 51 | |||
| 52 | std::vector<fs::path> shuffled(this->mFiles.begin(), this->mFiles.end()); | ||
| 53 | std::sort(shuffled.begin(), shuffled.end()); | ||
| 54 | |||
| 55 | auto currentTime = std::chrono::system_clock::now(); | ||
| 56 | uint64_t currentEpoch = currentTime.time_since_epoch() / *this->mEpoch; | ||
| 57 | std::chrono::milliseconds timeInEpoch = std::chrono::duration_cast<std::chrono::milliseconds>(currentTime.time_since_epoch()) % *this->mEpoch; | ||
| 58 | |||
| 59 | std::ostringstream seed; | ||
| 60 | seed << this->mSeed.size() << ";" << this->mSeed.toStdString() << ";"; | ||
| 61 | seed << *this->mEpoch << ";"; | ||
| 62 | seed << currentEpoch << ";"; | ||
| 63 | seed << this->mDirectory->string().size() << ";" << *this->mDirectory << ";"; | ||
| 64 | seed << this->mFiles.size() << ";"; | ||
| 65 | for (const fs::path& p: this->mFiles) | ||
| 66 | seed << p.string().size() << ";" << p << ";"; | ||
| 67 | |||
| 68 | std::vector<std::seed_seq::result_type> v; | ||
| 69 | v.reserve(seed.str().size()); | ||
| 70 | for (const char& c: seed.str()) | ||
| 71 | v.push_back(c); | ||
| 72 | |||
| 73 | std::seed_seq engine_seed(v.begin(), v.end()); | ||
| 74 | std::mt19937 g(engine_seed); | ||
| 75 | std::shuffle(shuffled.begin(), shuffled.end(), g); | ||
| 76 | |||
| 77 | std::vector<fs::path>::size_type ix = shuffled.size() * timeInEpoch / *this->mEpoch; | ||
| 78 | return QString::fromStdString((*this->mDirectory / shuffled[ix]).string()); | ||
| 79 | } | ||
| 80 | |||
| 81 | void FileSelector::onTimeout() { | ||
| 82 | if (!this->mFiles.size()) | ||
| 83 | return; | ||
| 84 | |||
| 85 | auto currentTime = std::chrono::system_clock::now(); | ||
| 86 | uint64_t currentMinorEpoch = currentTime.time_since_epoch() / (*this->mEpoch / this->mFiles.size()); | ||
| 87 | auto nextTime = std::chrono::time_point<std::chrono::system_clock>((currentMinorEpoch + 1) * (*this->mEpoch / this->mFiles.size())); | ||
| 88 | this->timer.start(std::chrono::duration_cast<std::chrono::milliseconds>(nextTime - currentTime)); | ||
| 89 | |||
| 90 | emit this->selectedChanged(); | ||
| 91 | } | ||
| 92 | |||
| 93 | void FileSelector::update() { | ||
| 94 | this->mFiles = std::set<fs::path>{}; | ||
| 95 | for (const fs::directory_entry& entry: | ||
| 96 | fs::recursive_directory_iterator(*this->mDirectory, fs::directory_options::follow_directory_symlink)) | ||
| 97 | { | ||
| 98 | this->mFiles.insert(fs::relative(entry, *this->mDirectory)); | ||
| 99 | } | ||
| 100 | |||
| 101 | this->onTimeout(); | ||
| 102 | } | ||
diff --git a/accounts/gkleen@sif/shell/quickshell-plugins/FileSelector.hpp b/accounts/gkleen@sif/shell/quickshell-plugins/FileSelector.hpp new file mode 100644 index 00000000..72c4f2a7 --- /dev/null +++ b/accounts/gkleen@sif/shell/quickshell-plugins/FileSelector.hpp | |||
| @@ -0,0 +1,52 @@ | |||
| 1 | #pragma once | ||
| 2 | |||
| 3 | #include <filesystem> | ||
| 4 | #include <chrono> | ||
| 5 | #include <set> | ||
| 6 | #include <optional> | ||
| 7 | |||
| 8 | #include <QObject> | ||
| 9 | #include <QTimer> | ||
| 10 | |||
| 11 | #include <qqmlintegration.h> | ||
| 12 | |||
| 13 | class FileSelector : public QObject { | ||
| 14 | Q_OBJECT; | ||
| 15 | Q_PROPERTY(QString directory READ directory WRITE setDirectory NOTIFY directoryChanged REQUIRED); | ||
| 16 | Q_PROPERTY(unsigned int epoch READ epoch WRITE setEpoch NOTIFY epochChanged REQUIRED); | ||
| 17 | Q_PROPERTY(QString seed READ seed WRITE setSeed NOTIFY seedChanged); | ||
| 18 | Q_PROPERTY(QString selected READ selected NOTIFY selectedChanged); | ||
| 19 | QML_ELEMENT; | ||
| 20 | |||
| 21 | public: | ||
| 22 | explicit FileSelector(QObject* parent = nullptr); | ||
| 23 | |||
| 24 | QString directory() const; | ||
| 25 | void setDirectory(QString directory); | ||
| 26 | |||
| 27 | unsigned int epoch() const; | ||
| 28 | void setEpoch(unsigned int epoch); | ||
| 29 | |||
| 30 | QString seed() const; | ||
| 31 | void setSeed(QString seed); | ||
| 32 | |||
| 33 | QString selected() const; | ||
| 34 | |||
| 35 | signals: | ||
| 36 | void directoryChanged(); | ||
| 37 | void epochChanged(); | ||
| 38 | void seedChanged(); | ||
| 39 | void selectedChanged(); | ||
| 40 | |||
| 41 | private slots: | ||
| 42 | void onTimeout(); | ||
| 43 | |||
| 44 | private: | ||
| 45 | std::optional<std::filesystem::path> mDirectory; | ||
| 46 | std::optional<std::chrono::milliseconds> mEpoch; | ||
| 47 | std::set<std::filesystem::path> mFiles; | ||
| 48 | QString mSeed; | ||
| 49 | QTimer timer; | ||
| 50 | |||
| 51 | void update(); | ||
| 52 | }; | ||
diff --git a/accounts/gkleen@sif/shell/quickshell-plugins/KeePassXC.cpp b/accounts/gkleen@sif/shell/quickshell-plugins/KeePassXC.cpp new file mode 100644 index 00000000..f6e4dd6e --- /dev/null +++ b/accounts/gkleen@sif/shell/quickshell-plugins/KeePassXC.cpp | |||
| @@ -0,0 +1,18 @@ | |||
| 1 | #include "KeePassXC.hpp" | ||
| 2 | |||
| 3 | #include <QDBusConnection> | ||
| 4 | |||
| 5 | KeePassXC::KeePassXC() { | ||
| 6 | this->service = new DBusKeePassXC(DBusKeePassXC::staticInterfaceName(), "/keepassxc", QDBusConnection::sessionBus(), this); | ||
| 7 | } | ||
| 8 | KeePassXC::~KeePassXC() { | ||
| 9 | if (this->service) | ||
| 10 | delete this->service; | ||
| 11 | } | ||
| 12 | |||
| 13 | void KeePassXC::lockAllDatabases() { | ||
| 14 | if (!this->service) | ||
| 15 | return; | ||
| 16 | |||
| 17 | this->service->lockAllDatabases(); | ||
| 18 | } | ||
diff --git a/accounts/gkleen@sif/shell/quickshell-plugins/KeePassXC.hpp b/accounts/gkleen@sif/shell/quickshell-plugins/KeePassXC.hpp new file mode 100644 index 00000000..c4cd71e0 --- /dev/null +++ b/accounts/gkleen@sif/shell/quickshell-plugins/KeePassXC.hpp | |||
| @@ -0,0 +1,21 @@ | |||
| 1 | #pragma once | ||
| 2 | |||
| 3 | #include "dbus_keepassxc.h" | ||
| 4 | |||
| 5 | #include <QObject> | ||
| 6 | #include <QtQmlIntegration/qqmlintegration.h> | ||
| 7 | |||
| 8 | class KeePassXC : public QObject { | ||
| 9 | Q_OBJECT; | ||
| 10 | QML_SINGLETON; | ||
| 11 | QML_ELEMENT; | ||
| 12 | |||
| 13 | public: | ||
| 14 | explicit KeePassXC(); | ||
| 15 | ~KeePassXC(); | ||
| 16 | |||
| 17 | Q_INVOKABLE void lockAllDatabases(); | ||
| 18 | |||
| 19 | private: | ||
| 20 | DBusKeePassXC* service = nullptr; | ||
| 21 | }; | ||
diff --git a/accounts/gkleen@sif/shell/quickshell-plugins/Systemd.cpp b/accounts/gkleen@sif/shell/quickshell-plugins/Systemd.cpp new file mode 100644 index 00000000..308659e9 --- /dev/null +++ b/accounts/gkleen@sif/shell/quickshell-plugins/Systemd.cpp | |||
| @@ -0,0 +1,198 @@ | |||
| 1 | #include "Systemd.hpp" | ||
| 2 | |||
| 3 | #include <sstream> | ||
| 4 | |||
| 5 | #include <QDBusConnection> | ||
| 6 | #include <QDBusReply> | ||
| 7 | #include <QDebug> | ||
| 8 | #include <QDBusUnixFileDescriptor> | ||
| 9 | #include <QDBusObjectPath> | ||
| 10 | |||
| 11 | Systemd::Systemd(QObject* parent) : QObject(parent) { | ||
| 12 | this->systemdManager = new DBusSystemdManager( | ||
| 13 | "org.freedesktop.systemd1", | ||
| 14 | "/org/freedesktop/systemd1", | ||
| 15 | QDBusConnection::sessionBus(), | ||
| 16 | this | ||
| 17 | ); | ||
| 18 | this->logindManager = new DBusLogindManager( | ||
| 19 | "org.freedesktop.login1", | ||
| 20 | "/org/freedesktop/login1", | ||
| 21 | QDBusConnection::systemBus(), | ||
| 22 | this | ||
| 23 | ); | ||
| 24 | |||
| 25 | QDBusReply<QDBusObjectPath> sessionPath = this->logindManager->GetSession("auto"); | ||
| 26 | qDebug() << sessionPath; | ||
| 27 | this->logindSession = new DBusLogindSession( | ||
| 28 | "org.freedesktop.login1", | ||
| 29 | sessionPath.value().path(), | ||
| 30 | QDBusConnection::systemBus(), | ||
| 31 | this | ||
| 32 | ); | ||
| 33 | this->logindSessionProperties = new DBusProperties( | ||
| 34 | "org.freedesktop.login1", | ||
| 35 | sessionPath.value().path(), | ||
| 36 | QDBusConnection::systemBus(), | ||
| 37 | this | ||
| 38 | ); | ||
| 39 | |||
| 40 | QObject::connect(this->logindManager, &DBusLogindManager::PrepareForShutdown, this, &Systemd::shutdown); | ||
| 41 | QObject::connect(this->logindManager, &DBusLogindManager::PrepareForSleep, this, &Systemd::sleep); | ||
| 42 | QObject::connect(this->logindSession, &DBusLogindSession::Lock, this, &Systemd::lock); | ||
| 43 | QObject::connect(this->logindSession, &DBusLogindSession::Unlock, this, &Systemd::unlock); | ||
| 44 | QObject::connect(this->logindSessionProperties, &DBusProperties::PropertiesChanged, this, &Systemd::onLogindSessionPropertiesChanged); | ||
| 45 | } | ||
| 46 | |||
| 47 | void Systemd::onLogindSessionPropertiesChanged(const QString& interface_name, const QVariantMap& changed_properties, const QStringList& invalidated_properties) { | ||
| 48 | if (changed_properties.contains("IdleHint") || invalidated_properties.contains("IdleHint")) | ||
| 49 | emit this->idleHintChanged(); | ||
| 50 | if (changed_properties.contains("LockedHint") || invalidated_properties.contains("LockedHint")) | ||
| 51 | emit this->lockedHintChanged(); | ||
| 52 | } | ||
| 53 | |||
| 54 | void Systemd::stopUserUnit(const QString& unit, const QString& mode) { this->systemdManager->StopUnit(unit, mode); } | ||
| 55 | |||
| 56 | void Systemd::setBrightness(const QString& subsystem, const QString& name, quint32 brightness) { this->logindSession->SetBrightness(subsystem, name, brightness); } | ||
| 57 | |||
| 58 | bool Systemd::idleHint() { return this->logindSession->idleHint(); } | ||
| 59 | void Systemd::setIdleHint(bool idle) { this->logindSession->SetIdleHint(idle); } | ||
| 60 | bool Systemd::lockedHint() { return this->logindSession->lockedHint(); } | ||
| 61 | void Systemd::setLockedHint(bool locked) { this->logindSession->SetLockedHint(locked); } | ||
| 62 | void Systemd::lockSession() { this->logindSession->call("Lock"); } | ||
| 63 | void Systemd::suspend() { this->logindManager->Suspend(true); } | ||
| 64 | void Systemd::hibernate() { this->logindManager->Hibernate(true); } | ||
| 65 | |||
| 66 | std::string SystemdInhibitorParams::toString(SystemdInhibitorParams::WhatItem what) { | ||
| 67 | if (what == SystemdInhibitorParams::Shutdown) | ||
| 68 | return "shutdown"; | ||
| 69 | else if (what == SystemdInhibitorParams::Sleep) | ||
| 70 | return "sleep"; | ||
| 71 | else if (what == SystemdInhibitorParams::Idle) | ||
| 72 | return "idle"; | ||
| 73 | else if (what == SystemdInhibitorParams::HandlePowerKey) | ||
| 74 | return "handle-power-key"; | ||
| 75 | else if (what == SystemdInhibitorParams::HandleSuspendKey) | ||
| 76 | return "handle-suspend-key"; | ||
| 77 | else if (what == SystemdInhibitorParams::HandleHibernateKey) | ||
| 78 | return "handle-hibernate-key"; | ||
| 79 | else if (what == SystemdInhibitorParams::HandleLidSwitch) | ||
| 80 | return "handle-lid-switch"; | ||
| 81 | return ""; | ||
| 82 | } | ||
| 83 | |||
| 84 | std::string SystemdInhibitorParams::toString(SystemdInhibitorParams::What what) { | ||
| 85 | std::ostringstream res; | ||
| 86 | bool first = true; | ||
| 87 | for (const WhatItem& item: SystemdInhibitorParams::allWhatItems) { | ||
| 88 | if (!(what & item)) | ||
| 89 | continue; | ||
| 90 | |||
| 91 | if (!first) | ||
| 92 | res << ":"; | ||
| 93 | else | ||
| 94 | first = false; | ||
| 95 | res << SystemdInhibitorParams::toString(item); | ||
| 96 | } | ||
| 97 | return res.str(); | ||
| 98 | } | ||
| 99 | |||
| 100 | std::string SystemdInhibitorParams::toString(SystemdInhibitorParams::Mode mode) { | ||
| 101 | if (mode == SystemdInhibitorParams::Block) | ||
| 102 | return "block"; | ||
| 103 | else if (mode == SystemdInhibitorParams::BlockWeak) | ||
| 104 | return "block-weak"; | ||
| 105 | else if (mode == SystemdInhibitorParams::Delay) | ||
| 106 | return "delay"; | ||
| 107 | return ""; | ||
| 108 | } | ||
| 109 | |||
| 110 | bool SystemdInhibitor::enabled() const { return static_cast<bool>(this->activeInhibitor); } | ||
| 111 | void SystemdInhibitor::setEnabled(bool enabled) { | ||
| 112 | this->mEnabled = enabled; | ||
| 113 | |||
| 114 | if (enabled) | ||
| 115 | this->update(); | ||
| 116 | else | ||
| 117 | this->release(); | ||
| 118 | } | ||
| 119 | |||
| 120 | SystemdInhibitorParams::What SystemdInhibitor::what() const { return this->mWhat; } | ||
| 121 | void SystemdInhibitor::setWhat(SystemdInhibitorParams::What what) { | ||
| 122 | this->mWhat = what; | ||
| 123 | this->update(); | ||
| 124 | } | ||
| 125 | |||
| 126 | QString SystemdInhibitor::who() const { return this->mWho; } | ||
| 127 | void SystemdInhibitor::setWho(QString who) { | ||
| 128 | this->mWho = who; | ||
| 129 | this->update(); | ||
| 130 | } | ||
| 131 | |||
| 132 | QString SystemdInhibitor::why() const { return this->mWhy; } | ||
| 133 | void SystemdInhibitor::setWhy(QString why) { | ||
| 134 | this->mWhy = why; | ||
| 135 | this->update(); | ||
| 136 | } | ||
| 137 | |||
| 138 | SystemdInhibitorParams::Mode SystemdInhibitor::mode() const { return this->mMode; } | ||
| 139 | void SystemdInhibitor::setMode(SystemdInhibitorParams::Mode mode) { | ||
| 140 | this->mMode = mode; | ||
| 141 | this->update(); | ||
| 142 | } | ||
| 143 | |||
| 144 | SystemdInhibitor::ActiveSystemdInhibitor::ActiveSystemdInhibitor(SystemdInhibitorParams::What what_, QString who_, QString why_, SystemdInhibitorParams::Mode mode_): what(what_), who(who_), why(why_), mode(mode_) { | ||
| 145 | DBusLogindManager logindManager( | ||
| 146 | "org.freedesktop.login1", | ||
| 147 | "/org/freedesktop/login1", | ||
| 148 | QDBusConnection::systemBus() | ||
| 149 | ); | ||
| 150 | QDBusReply<QDBusUnixFileDescriptor> fd_ = logindManager.Inhibit(QString::fromStdString(SystemdInhibitorParams::toString(what)), who, why, QString::fromStdString(SystemdInhibitorParams::toString(mode))); | ||
| 151 | if (fd_.error().isValid()) | ||
| 152 | throw fd_.error(); | ||
| 153 | this->fd = ::dup(fd_.value().fileDescriptor()); | ||
| 154 | } | ||
| 155 | SystemdInhibitor::ActiveSystemdInhibitor::~ActiveSystemdInhibitor() { | ||
| 156 | if (this->fd != -1) | ||
| 157 | ::close(this->fd); | ||
| 158 | } | ||
| 159 | |||
| 160 | void SystemdInhibitor::release() { | ||
| 161 | if (!this->activeInhibitor) | ||
| 162 | return; | ||
| 163 | |||
| 164 | this->activeInhibitor.reset(); | ||
| 165 | emit this->enabledChanged(); | ||
| 166 | } | ||
| 167 | |||
| 168 | void SystemdInhibitor::update() { | ||
| 169 | if (!this->mWhat || this->mWho.isEmpty() || this->mWhy.isEmpty() || !this->mMode || !this->mEnabled) | ||
| 170 | if (this->activeInhibitor) | ||
| 171 | this->release(); | ||
| 172 | else | ||
| 173 | return; | ||
| 174 | |||
| 175 | if (this->activeInhibitor && this->mWhat == this->activeInhibitor->what && this->mWho == this->activeInhibitor->who && this->mWhy == this->activeInhibitor->why && this->mMode == this->activeInhibitor->mode) | ||
| 176 | return; | ||
| 177 | |||
| 178 | std::unique_ptr<ActiveSystemdInhibitor> otherInhibitor; | ||
| 179 | try { | ||
| 180 | otherInhibitor.reset(new SystemdInhibitor::ActiveSystemdInhibitor(this->mWhat, this->mWho, this->mWhy, this->mMode)); | ||
| 181 | } catch (const QDBusError& err) { | ||
| 182 | qCritical().noquote() | ||
| 183 | << err.name().toStdString() << " " << err.message().toStdString(); | ||
| 184 | return; | ||
| 185 | } | ||
| 186 | this->activeInhibitor.swap(otherInhibitor); | ||
| 187 | |||
| 188 | if (!otherInhibitor && this->activeInhibitor) | ||
| 189 | emit this->enabledChanged(); | ||
| 190 | if (otherInhibitor && otherInhibitor->what != this->activeInhibitor->what) | ||
| 191 | emit this->whatChanged(); | ||
| 192 | if (otherInhibitor && otherInhibitor->who != this->activeInhibitor->who) | ||
| 193 | emit this->whoChanged(); | ||
| 194 | if (otherInhibitor && otherInhibitor->why != this->activeInhibitor->why) | ||
| 195 | emit this->whyChanged(); | ||
| 196 | if (otherInhibitor && otherInhibitor->mode != this->activeInhibitor->mode) | ||
| 197 | emit this->modeChanged(); | ||
| 198 | } | ||
diff --git a/accounts/gkleen@sif/shell/quickshell-plugins/Systemd.hpp b/accounts/gkleen@sif/shell/quickshell-plugins/Systemd.hpp new file mode 100644 index 00000000..615024d2 --- /dev/null +++ b/accounts/gkleen@sif/shell/quickshell-plugins/Systemd.hpp | |||
| @@ -0,0 +1,147 @@ | |||
| 1 | #pragma once | ||
| 2 | |||
| 3 | #include <cstdint> | ||
| 4 | #include <memory> | ||
| 5 | #include <string> | ||
| 6 | |||
| 7 | #include <QObject> | ||
| 8 | #include <QDBusInterface> | ||
| 9 | #include <QtQmlIntegration/qqmlintegration.h> | ||
| 10 | |||
| 11 | #include "dbus_systemd_manager.h" | ||
| 12 | #include "dbus_logind_manager.h" | ||
| 13 | #include "dbus_logind_session.h" | ||
| 14 | #include "dbus_properties.h" | ||
| 15 | |||
| 16 | class Systemd : public QObject { | ||
| 17 | Q_OBJECT; | ||
| 18 | QML_SINGLETON; | ||
| 19 | QML_ELEMENT; | ||
| 20 | |||
| 21 | public: | ||
| 22 | explicit Systemd(QObject* parent = nullptr); | ||
| 23 | |||
| 24 | Q_PROPERTY(bool idleHint READ idleHint WRITE setIdleHint NOTIFY idleHintChanged) | ||
| 25 | Q_PROPERTY(bool lockedHint READ lockedHint WRITE setLockedHint NOTIFY lockedHintChanged) | ||
| 26 | |||
| 27 | Q_INVOKABLE void stopUserUnit(const QString& unit, const QString& mode); | ||
| 28 | Q_INVOKABLE void setBrightness(const QString& subsystem, const QString& name, quint32 brightness); | ||
| 29 | Q_INVOKABLE void setIdleHint(bool idle); | ||
| 30 | Q_INVOKABLE void setLockedHint(bool locked); | ||
| 31 | Q_INVOKABLE void lockSession(); | ||
| 32 | Q_INVOKABLE void suspend(); | ||
| 33 | Q_INVOKABLE void hibernate(); | ||
| 34 | |||
| 35 | bool idleHint(); | ||
| 36 | bool lockedHint(); | ||
| 37 | |||
| 38 | signals: | ||
| 39 | void shutdown(bool before); | ||
| 40 | void sleep(bool before); | ||
| 41 | void lock(); | ||
| 42 | void unlock(); | ||
| 43 | void idleHintChanged(); | ||
| 44 | void lockedHintChanged(); | ||
| 45 | |||
| 46 | private slots: | ||
| 47 | void onLogindSessionPropertiesChanged(const QString& interface_name, const QVariantMap& changed_properties, const QStringList& invalidated_properties); | ||
| 48 | |||
| 49 | private: | ||
| 50 | DBusSystemdManager* systemdManager; | ||
| 51 | DBusLogindManager* logindManager; | ||
| 52 | DBusLogindSession* logindSession; | ||
| 53 | DBusProperties* logindSessionProperties; | ||
| 54 | }; | ||
| 55 | |||
| 56 | class SystemdInhibitorParams : public QObject { | ||
| 57 | Q_OBJECT; | ||
| 58 | QML_ELEMENT; | ||
| 59 | QML_SINGLETON; | ||
| 60 | |||
| 61 | public: | ||
| 62 | enum WhatItem : uint8_t { | ||
| 63 | Shutdown = 0b1, | ||
| 64 | Sleep = 0b10, | ||
| 65 | Idle = 0b100, | ||
| 66 | HandlePowerKey = 0b1000, | ||
| 67 | HandleSuspendKey = 0b10000, | ||
| 68 | HandleHibernateKey = 0b100000, | ||
| 69 | HandleLidSwitch = 0b1000000, | ||
| 70 | }; | ||
| 71 | Q_ENUM(WhatItem); | ||
| 72 | Q_DECLARE_FLAGS(What, WhatItem); | ||
| 73 | |||
| 74 | enum Mode : uint8_t { | ||
| 75 | Block = 1, | ||
| 76 | BlockWeak = 2, | ||
| 77 | Delay = 3, | ||
| 78 | }; | ||
| 79 | Q_ENUM(Mode); | ||
| 80 | |||
| 81 | Q_INVOKABLE static std::string toString(WhatItem what); | ||
| 82 | Q_INVOKABLE static std::string toString(What what); | ||
| 83 | Q_INVOKABLE static std::string toString(Mode mode); | ||
| 84 | |||
| 85 | static constexpr WhatItem allWhatItems[] = { Shutdown, Sleep, Idle, HandlePowerKey, HandleSuspendKey, HandleHibernateKey, HandleLidSwitch }; | ||
| 86 | }; | ||
| 87 | Q_DECLARE_OPERATORS_FOR_FLAGS(SystemdInhibitorParams::What) | ||
| 88 | |||
| 89 | class SystemdInhibitor : public QObject { | ||
| 90 | Q_OBJECT; | ||
| 91 | Q_PROPERTY(bool enabled READ enabled WRITE setEnabled NOTIFY enabledChanged); | ||
| 92 | Q_PROPERTY(SystemdInhibitorParams::What what READ what WRITE setWhat NOTIFY whatChanged); | ||
| 93 | Q_PROPERTY(QString who READ who WRITE setWho NOTIFY whoChanged); | ||
| 94 | Q_PROPERTY(QString why READ why WRITE setWhy NOTIFY whyChanged); | ||
| 95 | Q_PROPERTY(SystemdInhibitorParams::Mode mode READ mode WRITE setMode NOTIFY modeChanged); | ||
| 96 | QML_ELEMENT; | ||
| 97 | |||
| 98 | public: | ||
| 99 | explicit SystemdInhibitor(QObject* parent = nullptr): QObject(parent) {} | ||
| 100 | |||
| 101 | bool enabled() const; | ||
| 102 | void setEnabled(bool enabled); | ||
| 103 | |||
| 104 | SystemdInhibitorParams::What what() const; | ||
| 105 | void setWhat(SystemdInhibitorParams::What what); | ||
| 106 | |||
| 107 | QString who() const; | ||
| 108 | void setWho(QString who); | ||
| 109 | |||
| 110 | QString why() const; | ||
| 111 | void setWhy(QString why); | ||
| 112 | |||
| 113 | SystemdInhibitorParams::Mode mode() const; | ||
| 114 | void setMode(SystemdInhibitorParams::Mode mode); | ||
| 115 | |||
| 116 | Q_INVOKABLE void release(); | ||
| 117 | |||
| 118 | signals: | ||
| 119 | void enabledChanged(); | ||
| 120 | void whatChanged(); | ||
| 121 | void whoChanged(); | ||
| 122 | void whyChanged(); | ||
| 123 | void modeChanged(); | ||
| 124 | |||
| 125 | private: | ||
| 126 | class ActiveSystemdInhibitor { | ||
| 127 | public: | ||
| 128 | uint32_t fd = -1; | ||
| 129 | SystemdInhibitorParams::What what; | ||
| 130 | QString who; | ||
| 131 | QString why; | ||
| 132 | SystemdInhibitorParams::Mode mode; | ||
| 133 | |||
| 134 | ActiveSystemdInhibitor(SystemdInhibitorParams::What what_, QString who_, QString why_, SystemdInhibitorParams::Mode mode_); | ||
| 135 | ~ActiveSystemdInhibitor(); | ||
| 136 | }; | ||
| 137 | |||
| 138 | void update(); | ||
| 139 | |||
| 140 | bool mEnabled = true; | ||
| 141 | std::unique_ptr<ActiveSystemdInhibitor> activeInhibitor; | ||
| 142 | SystemdInhibitorParams::What mWhat = static_cast<SystemdInhibitorParams::What>(0); | ||
| 143 | QString mWho; | ||
| 144 | QString mWhy; | ||
| 145 | SystemdInhibitorParams::Mode mMode = static_cast<SystemdInhibitorParams::Mode>(0); | ||
| 146 | }; | ||
| 147 | |||
diff --git a/accounts/gkleen@sif/shell/quickshell-plugins/customplugin.h b/accounts/gkleen@sif/shell/quickshell-plugins/customplugin.h new file mode 100644 index 00000000..e66ba9e3 --- /dev/null +++ b/accounts/gkleen@sif/shell/quickshell-plugins/customplugin.h | |||
| @@ -0,0 +1,7 @@ | |||
| 1 | #include <QQmlEngineExtensionPlugin> | ||
| 2 | |||
| 3 | class CustomPlugin : public QQmlEngineExtensionPlugin | ||
| 4 | { | ||
| 5 | Q_OBJECT | ||
| 6 | Q_PLUGIN_METADATA(IID QQmlEngineExtensionInterface_iid) | ||
| 7 | }; | ||
diff --git a/accounts/gkleen@sif/shell/quickshell-plugins/default.nix b/accounts/gkleen@sif/shell/quickshell-plugins/default.nix new file mode 100644 index 00000000..20a195eb --- /dev/null +++ b/accounts/gkleen@sif/shell/quickshell-plugins/default.nix | |||
| @@ -0,0 +1,30 @@ | |||
| 1 | { lib | ||
| 2 | , stdenv | ||
| 3 | , cmake | ||
| 4 | , qt6 | ||
| 5 | , fmt | ||
| 6 | , keepassxc | ||
| 7 | , systemd | ||
| 8 | }: | ||
| 9 | |||
| 10 | stdenv.mkDerivation rec { | ||
| 11 | name = "quickshell-custom"; | ||
| 12 | |||
| 13 | src = ./.; | ||
| 14 | |||
| 15 | prePatch = '' | ||
| 16 | cp ${keepassxc.src}/src/gui/org.keepassxc.KeePassXC.MainWindow.xml . | ||
| 17 | ''; | ||
| 18 | |||
| 19 | nativeBuildInputs = [ cmake qt6.wrapQtAppsHook ]; | ||
| 20 | buildInputs = [ | ||
| 21 | qt6.qtbase | ||
| 22 | qt6.qtdeclarative | ||
| 23 | ]; | ||
| 24 | |||
| 25 | cmakeFlags = [ | ||
| 26 | (lib.cmakeFeature "INSTALL_QML_PREFIX" qt6.qtbase.qtQmlPrefix) | ||
| 27 | ]; | ||
| 28 | |||
| 29 | LC_ALL = "C.UTF-8"; | ||
| 30 | } | ||
diff --git a/accounts/gkleen@sif/shell/quickshell-plugins/org.freedesktop.DBus.Properties.xml b/accounts/gkleen@sif/shell/quickshell-plugins/org.freedesktop.DBus.Properties.xml new file mode 100644 index 00000000..7588e7a5 --- /dev/null +++ b/accounts/gkleen@sif/shell/quickshell-plugins/org.freedesktop.DBus.Properties.xml | |||
| @@ -0,0 +1,28 @@ | |||
| 1 | <!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN" | ||
| 2 | "https://www.freedesktop.org/standards/dbus/1.0/introspect.dtd"> | ||
| 3 | <node> | ||
| 4 | <interface name="org.freedesktop.DBus.Properties"> | ||
| 5 | <method name="Get"> | ||
| 6 | <arg name="interface_name" direction="in" type="s"/> | ||
| 7 | <arg name="property_name" direction="in" type="s"/> | ||
| 8 | <arg name="value" direction="out" type="v"/> | ||
| 9 | </method> | ||
| 10 | <method name="GetAll"> | ||
| 11 | <arg name="interface_name" direction="in" type="s"/> | ||
| 12 | <arg name="props" direction="out" type="a{sv}"/> | ||
| 13 | <annotation name="org.qtproject.QtDBus.QtTypeName.Out0" value="QVariantMap"/> | ||
| 14 | </method> | ||
| 15 | <method name="Set"> | ||
| 16 | <arg name="interface_name" direction="in" type="s"/> | ||
| 17 | <arg name="property_name" direction="in" type="s"/> | ||
| 18 | <arg name="value" direction="in" type="v"/> | ||
| 19 | </method> | ||
| 20 | <signal name="PropertiesChanged"> | ||
| 21 | <arg type="s" name="interface_name"/> | ||
| 22 | <arg type="a{sv}" name="changed_properties"/> | ||
| 23 | <annotation name="org.qtproject.QtDBus.QtTypeName.Out1" value="QVariantMap"/> | ||
| 24 | <arg type="as" name="invalidated_properties"/> | ||
| 25 | </signal> | ||
| 26 | </interface> | ||
| 27 | </node> | ||
| 28 | |||
diff --git a/accounts/gkleen@sif/shell/quickshell-plugins/org.freedesktop.login1.Manager.xml b/accounts/gkleen@sif/shell/quickshell-plugins/org.freedesktop.login1.Manager.xml new file mode 100644 index 00000000..120a06d9 --- /dev/null +++ b/accounts/gkleen@sif/shell/quickshell-plugins/org.freedesktop.login1.Manager.xml | |||
| @@ -0,0 +1,445 @@ | |||
| 1 | <!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN" | ||
| 2 | "https://www.freedesktop.org/standards/dbus/1.0/introspect.dtd"> | ||
| 3 | <node> | ||
| 4 | <interface name="org.freedesktop.login1.Manager"> | ||
| 5 | <property name="EnableWallMessages" type="b" access="readwrite"> | ||
| 6 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="false"/> | ||
| 7 | <annotation name="org.freedesktop.systemd1.Privileged" value="true"/> | ||
| 8 | </property> | ||
| 9 | <property name="WallMessage" type="s" access="readwrite"> | ||
| 10 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="false"/> | ||
| 11 | <annotation name="org.freedesktop.systemd1.Privileged" value="true"/> | ||
| 12 | </property> | ||
| 13 | <property name="NAutoVTs" type="u" access="read"> | ||
| 14 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 15 | </property> | ||
| 16 | <property name="KillOnlyUsers" type="as" access="read"> | ||
| 17 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 18 | </property> | ||
| 19 | <property name="KillExcludeUsers" type="as" access="read"> | ||
| 20 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 21 | </property> | ||
| 22 | <property name="KillUserProcesses" type="b" access="read"> | ||
| 23 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 24 | </property> | ||
| 25 | <property name="RebootParameter" type="s" access="read"> | ||
| 26 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="false"/> | ||
| 27 | </property> | ||
| 28 | <property name="RebootToFirmwareSetup" type="b" access="read"> | ||
| 29 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="false"/> | ||
| 30 | </property> | ||
| 31 | <property name="RebootToBootLoaderMenu" type="t" access="read"> | ||
| 32 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="false"/> | ||
| 33 | </property> | ||
| 34 | <property name="RebootToBootLoaderEntry" type="s" access="read"> | ||
| 35 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="false"/> | ||
| 36 | </property> | ||
| 37 | <property name="BootLoaderEntries" type="as" access="read"> | ||
| 38 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 39 | </property> | ||
| 40 | <property name="IdleHint" type="b" access="read"> | ||
| 41 | </property> | ||
| 42 | <property name="IdleSinceHint" type="t" access="read"> | ||
| 43 | </property> | ||
| 44 | <property name="IdleSinceHintMonotonic" type="t" access="read"> | ||
| 45 | </property> | ||
| 46 | <property name="BlockInhibited" type="s" access="read"> | ||
| 47 | </property> | ||
| 48 | <property name="BlockWeakInhibited" type="s" access="read"> | ||
| 49 | </property> | ||
| 50 | <property name="DelayInhibited" type="s" access="read"> | ||
| 51 | </property> | ||
| 52 | <property name="InhibitDelayMaxUSec" type="t" access="read"> | ||
| 53 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 54 | </property> | ||
| 55 | <property name="UserStopDelayUSec" type="t" access="read"> | ||
| 56 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 57 | </property> | ||
| 58 | <property name="SleepOperation" type="as" access="read"> | ||
| 59 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 60 | </property> | ||
| 61 | <property name="HandlePowerKey" type="s" access="read"> | ||
| 62 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 63 | </property> | ||
| 64 | <property name="HandlePowerKeyLongPress" type="s" access="read"> | ||
| 65 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 66 | </property> | ||
| 67 | <property name="HandleRebootKey" type="s" access="read"> | ||
| 68 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 69 | </property> | ||
| 70 | <property name="HandleRebootKeyLongPress" type="s" access="read"> | ||
| 71 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 72 | </property> | ||
| 73 | <property name="HandleSuspendKey" type="s" access="read"> | ||
| 74 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 75 | </property> | ||
| 76 | <property name="HandleSuspendKeyLongPress" type="s" access="read"> | ||
| 77 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 78 | </property> | ||
| 79 | <property name="HandleHibernateKey" type="s" access="read"> | ||
| 80 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 81 | </property> | ||
| 82 | <property name="HandleHibernateKeyLongPress" type="s" access="read"> | ||
| 83 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 84 | </property> | ||
| 85 | <property name="HandleLidSwitch" type="s" access="read"> | ||
| 86 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 87 | </property> | ||
| 88 | <property name="HandleLidSwitchExternalPower" type="s" access="read"> | ||
| 89 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 90 | </property> | ||
| 91 | <property name="HandleLidSwitchDocked" type="s" access="read"> | ||
| 92 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 93 | </property> | ||
| 94 | <property name="HandleSecureAttentionKey" type="s" access="read"> | ||
| 95 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 96 | </property> | ||
| 97 | <property name="HoldoffTimeoutUSec" type="t" access="read"> | ||
| 98 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 99 | </property> | ||
| 100 | <property name="IdleAction" type="s" access="read"> | ||
| 101 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 102 | </property> | ||
| 103 | <property name="IdleActionUSec" type="t" access="read"> | ||
| 104 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 105 | </property> | ||
| 106 | <property name="PreparingForShutdown" type="b" access="read"> | ||
| 107 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="false"/> | ||
| 108 | </property> | ||
| 109 | <property name="PreparingForShutdownWithMetadata" type="a{sv}" access="read"> | ||
| 110 | <annotation name="org.qtproject.QtDBus.QtTypeName" value="QVariantMap"/> | ||
| 111 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="false"/> | ||
| 112 | </property> | ||
| 113 | <property name="PreparingForSleep" type="b" access="read"> | ||
| 114 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="false"/> | ||
| 115 | </property> | ||
| 116 | <!-- <property name="ScheduledShutdown" type="(st)" access="read"> --> | ||
| 117 | <!-- </property> --> | ||
| 118 | <property name="DesignatedMaintenanceTime" type="s" access="read"> | ||
| 119 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 120 | </property> | ||
| 121 | <property name="Docked" type="b" access="read"> | ||
| 122 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="false"/> | ||
| 123 | </property> | ||
| 124 | <property name="LidClosed" type="b" access="read"> | ||
| 125 | </property> | ||
| 126 | <property name="OnExternalPower" type="b" access="read"> | ||
| 127 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="false"/> | ||
| 128 | </property> | ||
| 129 | <property name="RemoveIPC" type="b" access="read"> | ||
| 130 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 131 | </property> | ||
| 132 | <property name="RuntimeDirectorySize" type="t" access="read"> | ||
| 133 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 134 | </property> | ||
| 135 | <property name="RuntimeDirectoryInodesMax" type="t" access="read"> | ||
| 136 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 137 | </property> | ||
| 138 | <property name="InhibitorsMax" type="t" access="read"> | ||
| 139 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 140 | </property> | ||
| 141 | <property name="NCurrentInhibitors" type="t" access="read"> | ||
| 142 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="false"/> | ||
| 143 | </property> | ||
| 144 | <property name="SessionsMax" type="t" access="read"> | ||
| 145 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 146 | </property> | ||
| 147 | <property name="NCurrentSessions" type="t" access="read"> | ||
| 148 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="false"/> | ||
| 149 | </property> | ||
| 150 | <property name="StopIdleSessionUSec" type="t" access="read"> | ||
| 151 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 152 | </property> | ||
| 153 | <method name="GetSession"> | ||
| 154 | <arg type="s" name="session_id" direction="in"/> | ||
| 155 | <arg type="o" name="object_path" direction="out"/> | ||
| 156 | </method> | ||
| 157 | <method name="GetSessionByPID"> | ||
| 158 | <arg type="u" name="pid" direction="in"/> | ||
| 159 | <arg type="o" name="object_path" direction="out"/> | ||
| 160 | </method> | ||
| 161 | <method name="GetUser"> | ||
| 162 | <arg type="u" name="uid" direction="in"/> | ||
| 163 | <arg type="o" name="object_path" direction="out"/> | ||
| 164 | </method> | ||
| 165 | <method name="GetUserByPID"> | ||
| 166 | <arg type="u" name="pid" direction="in"/> | ||
| 167 | <arg type="o" name="object_path" direction="out"/> | ||
| 168 | </method> | ||
| 169 | <method name="GetSeat"> | ||
| 170 | <arg type="s" name="seat_id" direction="in"/> | ||
| 171 | <arg type="o" name="object_path" direction="out"/> | ||
| 172 | </method> | ||
| 173 | <!-- <method name="ListSessions"> --> | ||
| 174 | <!-- <arg type="a(susso)" name="sessions" direction="out"/> --> | ||
| 175 | <!-- </method> --> | ||
| 176 | <!-- <method name="ListSessionsEx"> --> | ||
| 177 | <!-- <arg type="a(sussussbto)" name="sessions" direction="out"/> --> | ||
| 178 | <!-- </method> --> | ||
| 179 | <!-- <method name="ListUsers"> --> | ||
| 180 | <!-- <arg type="a(uso)" name="users" direction="out"/> --> | ||
| 181 | <!-- </method> --> | ||
| 182 | <!-- <method name="ListSeats"> --> | ||
| 183 | <!-- <arg type="a(so)" name="seats" direction="out"/> --> | ||
| 184 | <!-- </method> --> | ||
| 185 | <!-- <method name="ListInhibitors"> --> | ||
| 186 | <!-- <arg type="a(ssssuu)" name="inhibitors" direction="out"/> --> | ||
| 187 | <!-- </method> --> | ||
| 188 | <!-- <method name="CreateSession"> --> | ||
| 189 | <!-- <arg type="u" name="uid" direction="in"/> --> | ||
| 190 | <!-- <arg type="u" name="pid" direction="in"/> --> | ||
| 191 | <!-- <arg type="s" name="service" direction="in"/> --> | ||
| 192 | <!-- <arg type="s" name="type" direction="in"/> --> | ||
| 193 | <!-- <arg type="s" name="class" direction="in"/> --> | ||
| 194 | <!-- <arg type="s" name="desktop" direction="in"/> --> | ||
| 195 | <!-- <arg type="s" name="seat_id" direction="in"/> --> | ||
| 196 | <!-- <arg type="u" name="vtnr" direction="in"/> --> | ||
| 197 | <!-- <arg type="s" name="tty" direction="in"/> --> | ||
| 198 | <!-- <arg type="s" name="display" direction="in"/> --> | ||
| 199 | <!-- <arg type="b" name="remote" direction="in"/> --> | ||
| 200 | <!-- <arg type="s" name="remote_user" direction="in"/> --> | ||
| 201 | <!-- <arg type="s" name="remote_host" direction="in"/> --> | ||
| 202 | <!-- <arg type="a(sv)" name="properties" direction="in"/> --> | ||
| 203 | <!-- <arg type="s" name="session_id" direction="out"/> --> | ||
| 204 | <!-- <arg type="o" name="object_path" direction="out"/> --> | ||
| 205 | <!-- <arg type="s" name="runtime_path" direction="out"/> --> | ||
| 206 | <!-- <arg type="h" name="fifo_fd" direction="out"/> --> | ||
| 207 | <!-- <arg type="u" name="uid" direction="out"/> --> | ||
| 208 | <!-- <arg type="s" name="seat_id" direction="out"/> --> | ||
| 209 | <!-- <arg type="u" name="vtnr" direction="out"/> --> | ||
| 210 | <!-- <arg type="b" name="existing" direction="out"/> --> | ||
| 211 | <!-- <annotation name="org.freedesktop.systemd1.Privileged" value="true"/> --> | ||
| 212 | <!-- </method> --> | ||
| 213 | <!-- <method name="CreateSessionWithPIDFD"> --> | ||
| 214 | <!-- <arg type="u" name="uid" direction="in"/> --> | ||
| 215 | <!-- <arg type="h" name="pidfd" direction="in"/> --> | ||
| 216 | <!-- <arg type="s" name="service" direction="in"/> --> | ||
| 217 | <!-- <arg type="s" name="type" direction="in"/> --> | ||
| 218 | <!-- <arg type="s" name="class" direction="in"/> --> | ||
| 219 | <!-- <arg type="s" name="desktop" direction="in"/> --> | ||
| 220 | <!-- <arg type="s" name="seat_id" direction="in"/> --> | ||
| 221 | <!-- <arg type="u" name="vtnr" direction="in"/> --> | ||
| 222 | <!-- <arg type="s" name="tty" direction="in"/> --> | ||
| 223 | <!-- <arg type="s" name="display" direction="in"/> --> | ||
| 224 | <!-- <arg type="b" name="remote" direction="in"/> --> | ||
| 225 | <!-- <arg type="s" name="remote_user" direction="in"/> --> | ||
| 226 | <!-- <arg type="s" name="remote_host" direction="in"/> --> | ||
| 227 | <!-- <arg type="t" name="flags" direction="in"/> --> | ||
| 228 | <!-- <arg type="a(sv)" name="properties" direction="in"/> --> | ||
| 229 | <!-- <arg type="s" name="session_id" direction="out"/> --> | ||
| 230 | <!-- <arg type="o" name="object_path" direction="out"/> --> | ||
| 231 | <!-- <arg type="s" name="runtime_path" direction="out"/> --> | ||
| 232 | <!-- <arg type="h" name="fifo_fd" direction="out"/> --> | ||
| 233 | <!-- <arg type="u" name="uid" direction="out"/> --> | ||
| 234 | <!-- <arg type="s" name="seat_id" direction="out"/> --> | ||
| 235 | <!-- <arg type="u" name="vtnr" direction="out"/> --> | ||
| 236 | <!-- <arg type="b" name="existing" direction="out"/> --> | ||
| 237 | <!-- <annotation name="org.freedesktop.systemd1.Privileged" value="true"/> --> | ||
| 238 | <!-- </method> --> | ||
| 239 | <method name="ReleaseSession"> | ||
| 240 | <arg type="s" name="session_id" direction="in"/> | ||
| 241 | </method> | ||
| 242 | <method name="ActivateSession"> | ||
| 243 | <arg type="s" name="session_id" direction="in"/> | ||
| 244 | </method> | ||
| 245 | <method name="ActivateSessionOnSeat"> | ||
| 246 | <arg type="s" name="session_id" direction="in"/> | ||
| 247 | <arg type="s" name="seat_id" direction="in"/> | ||
| 248 | </method> | ||
| 249 | <method name="LockSession"> | ||
| 250 | <arg type="s" name="session_id" direction="in"/> | ||
| 251 | </method> | ||
| 252 | <method name="UnlockSession"> | ||
| 253 | <arg type="s" name="session_id" direction="in"/> | ||
| 254 | </method> | ||
| 255 | <method name="LockSessions"> | ||
| 256 | </method> | ||
| 257 | <method name="UnlockSessions"> | ||
| 258 | </method> | ||
| 259 | <method name="KillSession"> | ||
| 260 | <arg type="s" name="session_id" direction="in"/> | ||
| 261 | <arg type="s" name="whom" direction="in"/> | ||
| 262 | <arg type="i" name="signal_number" direction="in"/> | ||
| 263 | </method> | ||
| 264 | <method name="KillUser"> | ||
| 265 | <arg type="u" name="uid" direction="in"/> | ||
| 266 | <arg type="i" name="signal_number" direction="in"/> | ||
| 267 | </method> | ||
| 268 | <method name="TerminateSession"> | ||
| 269 | <arg type="s" name="session_id" direction="in"/> | ||
| 270 | </method> | ||
| 271 | <method name="TerminateUser"> | ||
| 272 | <arg type="u" name="uid" direction="in"/> | ||
| 273 | </method> | ||
| 274 | <method name="TerminateSeat"> | ||
| 275 | <arg type="s" name="seat_id" direction="in"/> | ||
| 276 | </method> | ||
| 277 | <method name="SetUserLinger"> | ||
| 278 | <arg type="u" name="uid" direction="in"/> | ||
| 279 | <arg type="b" name="enable" direction="in"/> | ||
| 280 | <arg type="b" name="interactive" direction="in"/> | ||
| 281 | </method> | ||
| 282 | <method name="AttachDevice"> | ||
| 283 | <arg type="s" name="seat_id" direction="in"/> | ||
| 284 | <arg type="s" name="sysfs_path" direction="in"/> | ||
| 285 | <arg type="b" name="interactive" direction="in"/> | ||
| 286 | </method> | ||
| 287 | <method name="FlushDevices"> | ||
| 288 | <arg type="b" name="interactive" direction="in"/> | ||
| 289 | </method> | ||
| 290 | <method name="PowerOff"> | ||
| 291 | <arg type="b" name="interactive" direction="in"/> | ||
| 292 | </method> | ||
| 293 | <method name="PowerOffWithFlags"> | ||
| 294 | <arg type="t" name="flags" direction="in"/> | ||
| 295 | </method> | ||
| 296 | <method name="Reboot"> | ||
| 297 | <arg type="b" name="interactive" direction="in"/> | ||
| 298 | </method> | ||
| 299 | <method name="RebootWithFlags"> | ||
| 300 | <arg type="t" name="flags" direction="in"/> | ||
| 301 | </method> | ||
| 302 | <method name="Halt"> | ||
| 303 | <arg type="b" name="interactive" direction="in"/> | ||
| 304 | </method> | ||
| 305 | <method name="HaltWithFlags"> | ||
| 306 | <arg type="t" name="flags" direction="in"/> | ||
| 307 | </method> | ||
| 308 | <method name="Suspend"> | ||
| 309 | <arg type="b" name="interactive" direction="in"/> | ||
| 310 | </method> | ||
| 311 | <method name="SuspendWithFlags"> | ||
| 312 | <arg type="t" name="flags" direction="in"/> | ||
| 313 | </method> | ||
| 314 | <method name="Hibernate"> | ||
| 315 | <arg type="b" name="interactive" direction="in"/> | ||
| 316 | </method> | ||
| 317 | <method name="HibernateWithFlags"> | ||
| 318 | <arg type="t" name="flags" direction="in"/> | ||
| 319 | </method> | ||
| 320 | <method name="HybridSleep"> | ||
| 321 | <arg type="b" name="interactive" direction="in"/> | ||
| 322 | </method> | ||
| 323 | <method name="HybridSleepWithFlags"> | ||
| 324 | <arg type="t" name="flags" direction="in"/> | ||
| 325 | </method> | ||
| 326 | <method name="SuspendThenHibernate"> | ||
| 327 | <arg type="b" name="interactive" direction="in"/> | ||
| 328 | </method> | ||
| 329 | <method name="SuspendThenHibernateWithFlags"> | ||
| 330 | <arg type="t" name="flags" direction="in"/> | ||
| 331 | </method> | ||
| 332 | <method name="Sleep"> | ||
| 333 | <arg type="t" name="flags" direction="in"/> | ||
| 334 | </method> | ||
| 335 | <method name="CanPowerOff"> | ||
| 336 | <arg type="s" name="result" direction="out"/> | ||
| 337 | </method> | ||
| 338 | <method name="CanReboot"> | ||
| 339 | <arg type="s" name="result" direction="out"/> | ||
| 340 | </method> | ||
| 341 | <method name="CanHalt"> | ||
| 342 | <arg type="s" name="result" direction="out"/> | ||
| 343 | </method> | ||
| 344 | <method name="CanSuspend"> | ||
| 345 | <arg type="s" name="result" direction="out"/> | ||
| 346 | </method> | ||
| 347 | <method name="CanHibernate"> | ||
| 348 | <arg type="s" name="result" direction="out"/> | ||
| 349 | </method> | ||
| 350 | <method name="CanHybridSleep"> | ||
| 351 | <arg type="s" name="result" direction="out"/> | ||
| 352 | </method> | ||
| 353 | <method name="CanSuspendThenHibernate"> | ||
| 354 | <arg type="s" name="result" direction="out"/> | ||
| 355 | </method> | ||
| 356 | <method name="CanSleep"> | ||
| 357 | <arg type="s" name="result" direction="out"/> | ||
| 358 | </method> | ||
| 359 | <method name="ScheduleShutdown"> | ||
| 360 | <arg type="s" name="type" direction="in"/> | ||
| 361 | <arg type="t" name="usec" direction="in"/> | ||
| 362 | </method> | ||
| 363 | <method name="CancelScheduledShutdown"> | ||
| 364 | <arg type="b" name="cancelled" direction="out"/> | ||
| 365 | </method> | ||
| 366 | <method name="Inhibit"> | ||
| 367 | <arg type="s" name="what" direction="in"/> | ||
| 368 | <arg type="s" name="who" direction="in"/> | ||
| 369 | <arg type="s" name="why" direction="in"/> | ||
| 370 | <arg type="s" name="mode" direction="in"/> | ||
| 371 | <arg type="h" name="pipe_fd" direction="out"/> | ||
| 372 | </method> | ||
| 373 | <method name="CanRebootParameter"> | ||
| 374 | <arg type="s" name="result" direction="out"/> | ||
| 375 | </method> | ||
| 376 | <method name="SetRebootParameter"> | ||
| 377 | <arg type="s" name="parameter" direction="in"/> | ||
| 378 | </method> | ||
| 379 | <method name="CanRebootToFirmwareSetup"> | ||
| 380 | <arg type="s" name="result" direction="out"/> | ||
| 381 | </method> | ||
| 382 | <method name="SetRebootToFirmwareSetup"> | ||
| 383 | <arg type="b" name="enable" direction="in"/> | ||
| 384 | </method> | ||
| 385 | <method name="CanRebootToBootLoaderMenu"> | ||
| 386 | <arg type="s" name="result" direction="out"/> | ||
| 387 | </method> | ||
| 388 | <method name="SetRebootToBootLoaderMenu"> | ||
| 389 | <arg type="t" name="timeout" direction="in"/> | ||
| 390 | </method> | ||
| 391 | <method name="CanRebootToBootLoaderEntry"> | ||
| 392 | <arg type="s" name="result" direction="out"/> | ||
| 393 | </method> | ||
| 394 | <method name="SetRebootToBootLoaderEntry"> | ||
| 395 | <arg type="s" name="boot_loader_entry" direction="in"/> | ||
| 396 | </method> | ||
| 397 | <method name="SetWallMessage"> | ||
| 398 | <arg type="s" name="wall_message" direction="in"/> | ||
| 399 | <arg type="b" name="enable" direction="in"/> | ||
| 400 | </method> | ||
| 401 | <signal name="SecureAttentionKey"> | ||
| 402 | <arg type="s" name="seat_id"/> | ||
| 403 | <arg type="o" name="object_path"/> | ||
| 404 | </signal> | ||
| 405 | <signal name="SessionNew"> | ||
| 406 | <arg type="s" name="session_id"/> | ||
| 407 | <arg type="o" name="object_path"/> | ||
| 408 | </signal> | ||
| 409 | <signal name="SessionRemoved"> | ||
| 410 | <arg type="s" name="session_id"/> | ||
| 411 | <arg type="o" name="object_path"/> | ||
| 412 | </signal> | ||
| 413 | <signal name="UserNew"> | ||
| 414 | <arg type="u" name="uid"/> | ||
| 415 | <arg type="o" name="object_path"/> | ||
| 416 | </signal> | ||
| 417 | <signal name="UserRemoved"> | ||
| 418 | <arg type="u" name="uid"/> | ||
| 419 | <arg type="o" name="object_path"/> | ||
| 420 | </signal> | ||
| 421 | <signal name="SeatNew"> | ||
| 422 | <arg type="s" name="seat_id"/> | ||
| 423 | <arg type="o" name="object_path"/> | ||
| 424 | </signal> | ||
| 425 | <signal name="SeatRemoved"> | ||
| 426 | <arg type="s" name="seat_id"/> | ||
| 427 | <arg type="o" name="object_path"/> | ||
| 428 | </signal> | ||
| 429 | <signal name="PrepareForShutdown"> | ||
| 430 | <arg type="b" name="start"/> | ||
| 431 | </signal> | ||
| 432 | <signal name="PrepareForShutdownWithMetadata"> | ||
| 433 | <arg type="b" name="start"/> | ||
| 434 | <arg type="a{sv}" name="metadata"/> | ||
| 435 | <annotation name="org.qtproject.QtDBus.QtTypeName.Out1" value="QVariantMap"/> | ||
| 436 | </signal> | ||
| 437 | <signal name="PrepareForSleep"> | ||
| 438 | <arg type="b" name="start"/> | ||
| 439 | </signal> | ||
| 440 | </interface> | ||
| 441 | <node name="user"/> | ||
| 442 | <node name="session"/> | ||
| 443 | <node name="seat"/> | ||
| 444 | </node> | ||
| 445 | |||
diff --git a/accounts/gkleen@sif/shell/quickshell-plugins/org.freedesktop.login1.Session.xml b/accounts/gkleen@sif/shell/quickshell-plugins/org.freedesktop.login1.Session.xml new file mode 100644 index 00000000..7d6fc8ee --- /dev/null +++ b/accounts/gkleen@sif/shell/quickshell-plugins/org.freedesktop.login1.Session.xml | |||
| @@ -0,0 +1,146 @@ | |||
| 1 | <!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN" | ||
| 2 | "https://www.freedesktop.org/standards/dbus/1.0/introspect.dtd"> | ||
| 3 | <node> | ||
| 4 | <interface name="org.freedesktop.login1.Session"> | ||
| 5 | <property name="Id" type="s" access="read"> | ||
| 6 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 7 | </property> | ||
| 8 | <property name="User" type="o" access="read"> | ||
| 9 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 10 | </property> | ||
| 11 | <property name="Name" type="s" access="read"> | ||
| 12 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 13 | </property> | ||
| 14 | <property name="Timestamp" type="t" access="read"> | ||
| 15 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 16 | </property> | ||
| 17 | <property name="TimestampMonotonic" type="t" access="read"> | ||
| 18 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 19 | </property> | ||
| 20 | <property name="VTNr" type="u" access="read"> | ||
| 21 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 22 | </property> | ||
| 23 | <property name="Seat" type="o" access="read"> | ||
| 24 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 25 | </property> | ||
| 26 | <property name="TTY" type="s" access="read"> | ||
| 27 | </property> | ||
| 28 | <property name="Display" type="s" access="read"> | ||
| 29 | </property> | ||
| 30 | <property name="Remote" type="b" access="read"> | ||
| 31 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 32 | </property> | ||
| 33 | <property name="RemoteHost" type="s" access="read"> | ||
| 34 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 35 | </property> | ||
| 36 | <property name="RemoteUser" type="s" access="read"> | ||
| 37 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 38 | </property> | ||
| 39 | <property name="Service" type="s" access="read"> | ||
| 40 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 41 | </property> | ||
| 42 | <property name="Desktop" type="s" access="read"> | ||
| 43 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 44 | </property> | ||
| 45 | <property name="Scope" type="s" access="read"> | ||
| 46 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 47 | </property> | ||
| 48 | <property name="Leader" type="u" access="read"> | ||
| 49 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 50 | </property> | ||
| 51 | <property name="Audit" type="u" access="read"> | ||
| 52 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 53 | </property> | ||
| 54 | <property name="Type" type="s" access="read"> | ||
| 55 | </property> | ||
| 56 | <!-- <property name="Class" type="s" access="read"> --> | ||
| 57 | <!-- </property> --> | ||
| 58 | <property name="Active" type="b" access="read"> | ||
| 59 | </property> | ||
| 60 | <property name="State" type="s" access="read"> | ||
| 61 | </property> | ||
| 62 | <property name="IdleHint" type="b" access="read"> | ||
| 63 | </property> | ||
| 64 | <property name="IdleSinceHint" type="t" access="read"> | ||
| 65 | </property> | ||
| 66 | <property name="IdleSinceHintMonotonic" type="t" access="read"> | ||
| 67 | </property> | ||
| 68 | <property name="CanIdle" type="b" access="read"> | ||
| 69 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 70 | </property> | ||
| 71 | <property name="CanLock" type="b" access="read"> | ||
| 72 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 73 | </property> | ||
| 74 | <property name="LockedHint" type="b" access="read"> | ||
| 75 | </property> | ||
| 76 | <method name="Terminate"> | ||
| 77 | </method> | ||
| 78 | <method name="Activate"> | ||
| 79 | </method> | ||
| 80 | <!-- <method name="Lock"> --> | ||
| 81 | <!-- </method> --> | ||
| 82 | <!-- <method name="Unlock"> --> | ||
| 83 | <!-- </method> --> | ||
| 84 | <method name="SetIdleHint"> | ||
| 85 | <arg type="b" name="idle" direction="in"/> | ||
| 86 | </method> | ||
| 87 | <method name="SetLockedHint"> | ||
| 88 | <arg type="b" name="locked" direction="in"/> | ||
| 89 | </method> | ||
| 90 | <method name="Kill"> | ||
| 91 | <arg type="s" name="whom" direction="in"/> | ||
| 92 | <arg type="i" name="signal_number" direction="in"/> | ||
| 93 | </method> | ||
| 94 | <method name="TakeControl"> | ||
| 95 | <arg type="b" name="force" direction="in"/> | ||
| 96 | </method> | ||
| 97 | <method name="ReleaseControl"> | ||
| 98 | </method> | ||
| 99 | <method name="SetType"> | ||
| 100 | <arg type="s" name="type" direction="in"/> | ||
| 101 | </method> | ||
| 102 | <!-- <method name="SetClass"> --> | ||
| 103 | <!-- <arg type="s" name="class" direction="in"/> --> | ||
| 104 | <!-- </method> --> | ||
| 105 | <method name="SetDisplay"> | ||
| 106 | <arg type="s" name="display" direction="in"/> | ||
| 107 | </method> | ||
| 108 | <method name="SetTTY"> | ||
| 109 | <arg type="h" name="tty_fd" direction="in"/> | ||
| 110 | </method> | ||
| 111 | <method name="TakeDevice"> | ||
| 112 | <arg type="u" name="major" direction="in"/> | ||
| 113 | <arg type="u" name="minor" direction="in"/> | ||
| 114 | <arg type="h" name="fd" direction="out"/> | ||
| 115 | <arg type="b" name="inactive" direction="out"/> | ||
| 116 | </method> | ||
| 117 | <method name="ReleaseDevice"> | ||
| 118 | <arg type="u" name="major" direction="in"/> | ||
| 119 | <arg type="u" name="minor" direction="in"/> | ||
| 120 | </method> | ||
| 121 | <method name="PauseDeviceComplete"> | ||
| 122 | <arg type="u" name="major" direction="in"/> | ||
| 123 | <arg type="u" name="minor" direction="in"/> | ||
| 124 | </method> | ||
| 125 | <method name="SetBrightness"> | ||
| 126 | <arg type="s" name="subsystem" direction="in"/> | ||
| 127 | <arg type="s" name="name" direction="in"/> | ||
| 128 | <arg type="u" name="brightness" direction="in"/> | ||
| 129 | </method> | ||
| 130 | <signal name="PauseDevice"> | ||
| 131 | <arg type="u" name="major"/> | ||
| 132 | <arg type="u" name="minor"/> | ||
| 133 | <arg type="s" name="type"/> | ||
| 134 | </signal> | ||
| 135 | <signal name="ResumeDevice"> | ||
| 136 | <arg type="u" name="major"/> | ||
| 137 | <arg type="u" name="minor"/> | ||
| 138 | <arg type="h" name="fd"/> | ||
| 139 | </signal> | ||
| 140 | <signal name="Lock"> | ||
| 141 | </signal> | ||
| 142 | <signal name="Unlock"> | ||
| 143 | </signal> | ||
| 144 | </interface> | ||
| 145 | </node> | ||
| 146 | |||
diff --git a/accounts/gkleen@sif/shell/quickshell-plugins/org.freedesktop.systemd1.Manager.xml b/accounts/gkleen@sif/shell/quickshell-plugins/org.freedesktop.systemd1.Manager.xml new file mode 100644 index 00000000..b4f84a13 --- /dev/null +++ b/accounts/gkleen@sif/shell/quickshell-plugins/org.freedesktop.systemd1.Manager.xml | |||
| @@ -0,0 +1,817 @@ | |||
| 1 | <!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN" | ||
| 2 | "https://www.freedesktop.org/standards/dbus/1.0/introspect.dtd"> | ||
| 3 | <node> | ||
| 4 | <interface name="org.freedesktop.systemd1.Manager"> | ||
| 5 | <property name="Version" type="s" access="read"> | ||
| 6 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 7 | </property> | ||
| 8 | <property name="Features" type="s" access="read"> | ||
| 9 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 10 | </property> | ||
| 11 | <property name="Virtualization" type="s" access="read"> | ||
| 12 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 13 | </property> | ||
| 14 | <property name="ConfidentialVirtualization" type="s" access="read"> | ||
| 15 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 16 | </property> | ||
| 17 | <property name="Architecture" type="s" access="read"> | ||
| 18 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 19 | </property> | ||
| 20 | <property name="Tainted" type="s" access="read"> | ||
| 21 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 22 | </property> | ||
| 23 | <property name="FirmwareTimestamp" type="t" access="read"> | ||
| 24 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 25 | </property> | ||
| 26 | <property name="FirmwareTimestampMonotonic" type="t" access="read"> | ||
| 27 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 28 | </property> | ||
| 29 | <property name="LoaderTimestamp" type="t" access="read"> | ||
| 30 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 31 | </property> | ||
| 32 | <property name="LoaderTimestampMonotonic" type="t" access="read"> | ||
| 33 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 34 | </property> | ||
| 35 | <property name="KernelTimestamp" type="t" access="read"> | ||
| 36 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 37 | </property> | ||
| 38 | <property name="KernelTimestampMonotonic" type="t" access="read"> | ||
| 39 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 40 | </property> | ||
| 41 | <property name="InitRDTimestamp" type="t" access="read"> | ||
| 42 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 43 | </property> | ||
| 44 | <property name="InitRDTimestampMonotonic" type="t" access="read"> | ||
| 45 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 46 | </property> | ||
| 47 | <property name="UserspaceTimestamp" type="t" access="read"> | ||
| 48 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 49 | </property> | ||
| 50 | <property name="UserspaceTimestampMonotonic" type="t" access="read"> | ||
| 51 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 52 | </property> | ||
| 53 | <property name="FinishTimestamp" type="t" access="read"> | ||
| 54 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 55 | </property> | ||
| 56 | <property name="FinishTimestampMonotonic" type="t" access="read"> | ||
| 57 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 58 | </property> | ||
| 59 | <property name="ShutdownStartTimestamp" type="t" access="read"> | ||
| 60 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 61 | </property> | ||
| 62 | <property name="ShutdownStartTimestampMonotonic" type="t" access="read"> | ||
| 63 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 64 | </property> | ||
| 65 | <property name="SecurityStartTimestamp" type="t" access="read"> | ||
| 66 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 67 | </property> | ||
| 68 | <property name="SecurityStartTimestampMonotonic" type="t" access="read"> | ||
| 69 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 70 | </property> | ||
| 71 | <property name="SecurityFinishTimestamp" type="t" access="read"> | ||
| 72 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 73 | </property> | ||
| 74 | <property name="SecurityFinishTimestampMonotonic" type="t" access="read"> | ||
| 75 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 76 | </property> | ||
| 77 | <property name="GeneratorsStartTimestamp" type="t" access="read"> | ||
| 78 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 79 | </property> | ||
| 80 | <property name="GeneratorsStartTimestampMonotonic" type="t" access="read"> | ||
| 81 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 82 | </property> | ||
| 83 | <property name="GeneratorsFinishTimestamp" type="t" access="read"> | ||
| 84 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 85 | </property> | ||
| 86 | <property name="GeneratorsFinishTimestampMonotonic" type="t" access="read"> | ||
| 87 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 88 | </property> | ||
| 89 | <property name="UnitsLoadStartTimestamp" type="t" access="read"> | ||
| 90 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 91 | </property> | ||
| 92 | <property name="UnitsLoadStartTimestampMonotonic" type="t" access="read"> | ||
| 93 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 94 | </property> | ||
| 95 | <property name="UnitsLoadFinishTimestamp" type="t" access="read"> | ||
| 96 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 97 | </property> | ||
| 98 | <property name="UnitsLoadFinishTimestampMonotonic" type="t" access="read"> | ||
| 99 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 100 | </property> | ||
| 101 | <property name="UnitsLoadTimestamp" type="t" access="read"> | ||
| 102 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 103 | </property> | ||
| 104 | <property name="UnitsLoadTimestampMonotonic" type="t" access="read"> | ||
| 105 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 106 | </property> | ||
| 107 | <property name="InitRDSecurityStartTimestamp" type="t" access="read"> | ||
| 108 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 109 | </property> | ||
| 110 | <property name="InitRDSecurityStartTimestampMonotonic" type="t" access="read"> | ||
| 111 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 112 | </property> | ||
| 113 | <property name="InitRDSecurityFinishTimestamp" type="t" access="read"> | ||
| 114 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 115 | </property> | ||
| 116 | <property name="InitRDSecurityFinishTimestampMonotonic" type="t" access="read"> | ||
| 117 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 118 | </property> | ||
| 119 | <property name="InitRDGeneratorsStartTimestamp" type="t" access="read"> | ||
| 120 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 121 | </property> | ||
| 122 | <property name="InitRDGeneratorsStartTimestampMonotonic" type="t" access="read"> | ||
| 123 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 124 | </property> | ||
| 125 | <property name="InitRDGeneratorsFinishTimestamp" type="t" access="read"> | ||
| 126 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 127 | </property> | ||
| 128 | <property name="InitRDGeneratorsFinishTimestampMonotonic" type="t" access="read"> | ||
| 129 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 130 | </property> | ||
| 131 | <property name="InitRDUnitsLoadStartTimestamp" type="t" access="read"> | ||
| 132 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 133 | </property> | ||
| 134 | <property name="InitRDUnitsLoadStartTimestampMonotonic" type="t" access="read"> | ||
| 135 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 136 | </property> | ||
| 137 | <property name="InitRDUnitsLoadFinishTimestamp" type="t" access="read"> | ||
| 138 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 139 | </property> | ||
| 140 | <property name="InitRDUnitsLoadFinishTimestampMonotonic" type="t" access="read"> | ||
| 141 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 142 | </property> | ||
| 143 | <property name="LogLevel" type="s" access="readwrite"> | ||
| 144 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="false"/> | ||
| 145 | <annotation name="org.freedesktop.systemd1.Privileged" value="true"/> | ||
| 146 | </property> | ||
| 147 | <property name="LogTarget" type="s" access="readwrite"> | ||
| 148 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="false"/> | ||
| 149 | <annotation name="org.freedesktop.systemd1.Privileged" value="true"/> | ||
| 150 | </property> | ||
| 151 | <property name="NNames" type="u" access="read"> | ||
| 152 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="false"/> | ||
| 153 | </property> | ||
| 154 | <property name="NFailedUnits" type="u" access="read"> | ||
| 155 | </property> | ||
| 156 | <property name="NJobs" type="u" access="read"> | ||
| 157 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="false"/> | ||
| 158 | </property> | ||
| 159 | <property name="NInstalledJobs" type="u" access="read"> | ||
| 160 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="false"/> | ||
| 161 | </property> | ||
| 162 | <property name="NFailedJobs" type="u" access="read"> | ||
| 163 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="false"/> | ||
| 164 | </property> | ||
| 165 | <property name="Progress" type="d" access="read"> | ||
| 166 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="false"/> | ||
| 167 | </property> | ||
| 168 | <property name="Environment" type="as" access="read"> | ||
| 169 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="false"/> | ||
| 170 | </property> | ||
| 171 | <property name="ConfirmSpawn" type="b" access="read"> | ||
| 172 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 173 | </property> | ||
| 174 | <property name="ShowStatus" type="b" access="read"> | ||
| 175 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="false"/> | ||
| 176 | </property> | ||
| 177 | <property name="UnitPath" type="as" access="read"> | ||
| 178 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 179 | </property> | ||
| 180 | <property name="DefaultStandardOutput" type="s" access="read"> | ||
| 181 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 182 | </property> | ||
| 183 | <property name="DefaultStandardError" type="s" access="read"> | ||
| 184 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 185 | </property> | ||
| 186 | <property name="WatchdogDevice" type="s" access="read"> | ||
| 187 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 188 | </property> | ||
| 189 | <property name="WatchdogLastPingTimestamp" type="t" access="read"> | ||
| 190 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="false"/> | ||
| 191 | </property> | ||
| 192 | <property name="WatchdogLastPingTimestampMonotonic" type="t" access="read"> | ||
| 193 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="false"/> | ||
| 194 | </property> | ||
| 195 | <property name="RuntimeWatchdogUSec" type="t" access="readwrite"> | ||
| 196 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="false"/> | ||
| 197 | <annotation name="org.freedesktop.systemd1.Privileged" value="true"/> | ||
| 198 | </property> | ||
| 199 | <property name="RuntimeWatchdogPreUSec" type="t" access="readwrite"> | ||
| 200 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="false"/> | ||
| 201 | <annotation name="org.freedesktop.systemd1.Privileged" value="true"/> | ||
| 202 | </property> | ||
| 203 | <property name="RuntimeWatchdogPreGovernor" type="s" access="readwrite"> | ||
| 204 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="false"/> | ||
| 205 | <annotation name="org.freedesktop.systemd1.Privileged" value="true"/> | ||
| 206 | </property> | ||
| 207 | <property name="RebootWatchdogUSec" type="t" access="readwrite"> | ||
| 208 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="false"/> | ||
| 209 | <annotation name="org.freedesktop.systemd1.Privileged" value="true"/> | ||
| 210 | </property> | ||
| 211 | <property name="KExecWatchdogUSec" type="t" access="readwrite"> | ||
| 212 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="false"/> | ||
| 213 | <annotation name="org.freedesktop.systemd1.Privileged" value="true"/> | ||
| 214 | </property> | ||
| 215 | <property name="ServiceWatchdogs" type="b" access="readwrite"> | ||
| 216 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="false"/> | ||
| 217 | <annotation name="org.freedesktop.systemd1.Privileged" value="true"/> | ||
| 218 | </property> | ||
| 219 | <property name="ControlGroup" type="s" access="read"> | ||
| 220 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="false"/> | ||
| 221 | </property> | ||
| 222 | <property name="SystemState" type="s" access="read"> | ||
| 223 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="false"/> | ||
| 224 | </property> | ||
| 225 | <property name="ExitCode" type="y" access="read"> | ||
| 226 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="false"/> | ||
| 227 | </property> | ||
| 228 | <property name="DefaultTimerAccuracyUSec" type="t" access="read"> | ||
| 229 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 230 | </property> | ||
| 231 | <property name="DefaultTimeoutStartUSec" type="t" access="read"> | ||
| 232 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 233 | </property> | ||
| 234 | <property name="DefaultTimeoutStopUSec" type="t" access="read"> | ||
| 235 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 236 | </property> | ||
| 237 | <property name="DefaultTimeoutAbortUSec" type="t" access="read"> | ||
| 238 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="false"/> | ||
| 239 | </property> | ||
| 240 | <property name="DefaultDeviceTimeoutUSec" type="t" access="read"> | ||
| 241 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 242 | </property> | ||
| 243 | <property name="DefaultRestartUSec" type="t" access="read"> | ||
| 244 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 245 | </property> | ||
| 246 | <property name="DefaultStartLimitIntervalUSec" type="t" access="read"> | ||
| 247 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 248 | </property> | ||
| 249 | <property name="DefaultStartLimitBurst" type="u" access="read"> | ||
| 250 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 251 | </property> | ||
| 252 | <property name="DefaultCPUAccounting" type="b" access="read"> | ||
| 253 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 254 | </property> | ||
| 255 | <property name="DefaultBlockIOAccounting" type="b" access="read"> | ||
| 256 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 257 | </property> | ||
| 258 | <property name="DefaultIOAccounting" type="b" access="read"> | ||
| 259 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 260 | </property> | ||
| 261 | <property name="DefaultIPAccounting" type="b" access="read"> | ||
| 262 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 263 | </property> | ||
| 264 | <property name="DefaultMemoryAccounting" type="b" access="read"> | ||
| 265 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 266 | </property> | ||
| 267 | <property name="DefaultTasksAccounting" type="b" access="read"> | ||
| 268 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 269 | </property> | ||
| 270 | <property name="DefaultLimitCPU" type="t" access="read"> | ||
| 271 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 272 | </property> | ||
| 273 | <property name="DefaultLimitCPUSoft" type="t" access="read"> | ||
| 274 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 275 | </property> | ||
| 276 | <property name="DefaultLimitFSIZE" type="t" access="read"> | ||
| 277 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 278 | </property> | ||
| 279 | <property name="DefaultLimitFSIZESoft" type="t" access="read"> | ||
| 280 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 281 | </property> | ||
| 282 | <property name="DefaultLimitDATA" type="t" access="read"> | ||
| 283 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 284 | </property> | ||
| 285 | <property name="DefaultLimitDATASoft" type="t" access="read"> | ||
| 286 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 287 | </property> | ||
| 288 | <property name="DefaultLimitSTACK" type="t" access="read"> | ||
| 289 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 290 | </property> | ||
| 291 | <property name="DefaultLimitSTACKSoft" type="t" access="read"> | ||
| 292 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 293 | </property> | ||
| 294 | <property name="DefaultLimitCORE" type="t" access="read"> | ||
| 295 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 296 | </property> | ||
| 297 | <property name="DefaultLimitCORESoft" type="t" access="read"> | ||
| 298 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 299 | </property> | ||
| 300 | <property name="DefaultLimitRSS" type="t" access="read"> | ||
| 301 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 302 | </property> | ||
| 303 | <property name="DefaultLimitRSSSoft" type="t" access="read"> | ||
| 304 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 305 | </property> | ||
| 306 | <property name="DefaultLimitNOFILE" type="t" access="read"> | ||
| 307 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 308 | </property> | ||
| 309 | <property name="DefaultLimitNOFILESoft" type="t" access="read"> | ||
| 310 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 311 | </property> | ||
| 312 | <property name="DefaultLimitAS" type="t" access="read"> | ||
| 313 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 314 | </property> | ||
| 315 | <property name="DefaultLimitASSoft" type="t" access="read"> | ||
| 316 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 317 | </property> | ||
| 318 | <property name="DefaultLimitNPROC" type="t" access="read"> | ||
| 319 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 320 | </property> | ||
| 321 | <property name="DefaultLimitNPROCSoft" type="t" access="read"> | ||
| 322 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 323 | </property> | ||
| 324 | <property name="DefaultLimitMEMLOCK" type="t" access="read"> | ||
| 325 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 326 | </property> | ||
| 327 | <property name="DefaultLimitMEMLOCKSoft" type="t" access="read"> | ||
| 328 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 329 | </property> | ||
| 330 | <property name="DefaultLimitLOCKS" type="t" access="read"> | ||
| 331 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 332 | </property> | ||
| 333 | <property name="DefaultLimitLOCKSSoft" type="t" access="read"> | ||
| 334 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 335 | </property> | ||
| 336 | <property name="DefaultLimitSIGPENDING" type="t" access="read"> | ||
| 337 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 338 | </property> | ||
| 339 | <property name="DefaultLimitSIGPENDINGSoft" type="t" access="read"> | ||
| 340 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 341 | </property> | ||
| 342 | <property name="DefaultLimitMSGQUEUE" type="t" access="read"> | ||
| 343 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 344 | </property> | ||
| 345 | <property name="DefaultLimitMSGQUEUESoft" type="t" access="read"> | ||
| 346 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 347 | </property> | ||
| 348 | <property name="DefaultLimitNICE" type="t" access="read"> | ||
| 349 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 350 | </property> | ||
| 351 | <property name="DefaultLimitNICESoft" type="t" access="read"> | ||
| 352 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 353 | </property> | ||
| 354 | <property name="DefaultLimitRTPRIO" type="t" access="read"> | ||
| 355 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 356 | </property> | ||
| 357 | <property name="DefaultLimitRTPRIOSoft" type="t" access="read"> | ||
| 358 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 359 | </property> | ||
| 360 | <property name="DefaultLimitRTTIME" type="t" access="read"> | ||
| 361 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 362 | </property> | ||
| 363 | <property name="DefaultLimitRTTIMESoft" type="t" access="read"> | ||
| 364 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 365 | </property> | ||
| 366 | <property name="DefaultTasksMax" type="t" access="read"> | ||
| 367 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="false"/> | ||
| 368 | </property> | ||
| 369 | <property name="DefaultMemoryPressureThresholdUSec" type="t" access="read"> | ||
| 370 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="false"/> | ||
| 371 | </property> | ||
| 372 | <property name="DefaultMemoryPressureWatch" type="s" access="read"> | ||
| 373 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="false"/> | ||
| 374 | </property> | ||
| 375 | <property name="TimerSlackNSec" type="t" access="read"> | ||
| 376 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 377 | </property> | ||
| 378 | <property name="DefaultOOMPolicy" type="s" access="read"> | ||
| 379 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 380 | </property> | ||
| 381 | <property name="DefaultOOMScoreAdjust" type="i" access="read"> | ||
| 382 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 383 | </property> | ||
| 384 | <property name="CtrlAltDelBurstAction" type="s" access="read"> | ||
| 385 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 386 | </property> | ||
| 387 | <property name="SoftRebootsCount" type="u" access="read"> | ||
| 388 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 389 | </property> | ||
| 390 | <method name="GetUnit"> | ||
| 391 | <arg type="s" name="name" direction="in"/> | ||
| 392 | <arg type="o" name="unit" direction="out"/> | ||
| 393 | </method> | ||
| 394 | <method name="GetUnitByPID"> | ||
| 395 | <arg type="u" name="pid" direction="in"/> | ||
| 396 | <arg type="o" name="unit" direction="out"/> | ||
| 397 | </method> | ||
| 398 | <method name="GetUnitByInvocationID"> | ||
| 399 | <arg type="ay" name="invocation_id" direction="in"/> | ||
| 400 | <arg type="o" name="unit" direction="out"/> | ||
| 401 | </method> | ||
| 402 | <method name="GetUnitByControlGroup"> | ||
| 403 | <arg type="s" name="cgroup" direction="in"/> | ||
| 404 | <arg type="o" name="unit" direction="out"/> | ||
| 405 | </method> | ||
| 406 | <method name="GetUnitByPIDFD"> | ||
| 407 | <arg type="h" name="pidfd" direction="in"/> | ||
| 408 | <arg type="o" name="unit" direction="out"/> | ||
| 409 | <arg type="s" name="unit_id" direction="out"/> | ||
| 410 | <arg type="ay" name="invocation_id" direction="out"/> | ||
| 411 | </method> | ||
| 412 | <method name="LoadUnit"> | ||
| 413 | <arg type="s" name="name" direction="in"/> | ||
| 414 | <arg type="o" name="unit" direction="out"/> | ||
| 415 | </method> | ||
| 416 | <method name="StartUnit"> | ||
| 417 | <arg type="s" name="name" direction="in"/> | ||
| 418 | <arg type="s" name="mode" direction="in"/> | ||
| 419 | <arg type="o" name="job" direction="out"/> | ||
| 420 | </method> | ||
| 421 | <method name="StartUnitWithFlags"> | ||
| 422 | <arg type="s" name="name" direction="in"/> | ||
| 423 | <arg type="s" name="mode" direction="in"/> | ||
| 424 | <arg type="t" name="flags" direction="in"/> | ||
| 425 | <arg type="o" name="job" direction="out"/> | ||
| 426 | </method> | ||
| 427 | <method name="StartUnitReplace"> | ||
| 428 | <arg type="s" name="old_unit" direction="in"/> | ||
| 429 | <arg type="s" name="new_unit" direction="in"/> | ||
| 430 | <arg type="s" name="mode" direction="in"/> | ||
| 431 | <arg type="o" name="job" direction="out"/> | ||
| 432 | </method> | ||
| 433 | <method name="StopUnit"> | ||
| 434 | <arg type="s" name="name" direction="in"/> | ||
| 435 | <arg type="s" name="mode" direction="in"/> | ||
| 436 | <arg type="o" name="job" direction="out"/> | ||
| 437 | </method> | ||
| 438 | <method name="ReloadUnit"> | ||
| 439 | <arg type="s" name="name" direction="in"/> | ||
| 440 | <arg type="s" name="mode" direction="in"/> | ||
| 441 | <arg type="o" name="job" direction="out"/> | ||
| 442 | </method> | ||
| 443 | <method name="RestartUnit"> | ||
| 444 | <arg type="s" name="name" direction="in"/> | ||
| 445 | <arg type="s" name="mode" direction="in"/> | ||
| 446 | <arg type="o" name="job" direction="out"/> | ||
| 447 | </method> | ||
| 448 | <method name="TryRestartUnit"> | ||
| 449 | <arg type="s" name="name" direction="in"/> | ||
| 450 | <arg type="s" name="mode" direction="in"/> | ||
| 451 | <arg type="o" name="job" direction="out"/> | ||
| 452 | </method> | ||
| 453 | <method name="ReloadOrRestartUnit"> | ||
| 454 | <arg type="s" name="name" direction="in"/> | ||
| 455 | <arg type="s" name="mode" direction="in"/> | ||
| 456 | <arg type="o" name="job" direction="out"/> | ||
| 457 | </method> | ||
| 458 | <method name="ReloadOrTryRestartUnit"> | ||
| 459 | <arg type="s" name="name" direction="in"/> | ||
| 460 | <arg type="s" name="mode" direction="in"/> | ||
| 461 | <arg type="o" name="job" direction="out"/> | ||
| 462 | </method> | ||
| 463 | <!-- <method name="EnqueueUnitJob"> --> | ||
| 464 | <!-- <arg type="s" name="name" direction="in"/> --> | ||
| 465 | <!-- <arg type="s" name="job_type" direction="in"/> --> | ||
| 466 | <!-- <arg type="s" name="job_mode" direction="in"/> --> | ||
| 467 | <!-- <arg type="u" name="job_id" direction="out"/> --> | ||
| 468 | <!-- <arg type="o" name="job_path" direction="out"/> --> | ||
| 469 | <!-- <arg type="s" name="unit_id" direction="out"/> --> | ||
| 470 | <!-- <arg type="o" name="unit_path" direction="out"/> --> | ||
| 471 | <!-- <arg type="s" name="job_type" direction="out"/> --> | ||
| 472 | <!-- <arg type="a(uosos)" name="affected_jobs" direction="out"/> --> | ||
| 473 | <!-- </method> --> | ||
| 474 | <method name="KillUnit"> | ||
| 475 | <arg type="s" name="name" direction="in"/> | ||
| 476 | <arg type="s" name="whom" direction="in"/> | ||
| 477 | <arg type="i" name="signal" direction="in"/> | ||
| 478 | </method> | ||
| 479 | <method name="QueueSignalUnit"> | ||
| 480 | <arg type="s" name="name" direction="in"/> | ||
| 481 | <arg type="s" name="whom" direction="in"/> | ||
| 482 | <arg type="i" name="signal" direction="in"/> | ||
| 483 | <arg type="i" name="value" direction="in"/> | ||
| 484 | </method> | ||
| 485 | <method name="CleanUnit"> | ||
| 486 | <arg type="s" name="name" direction="in"/> | ||
| 487 | <arg type="as" name="mask" direction="in"/> | ||
| 488 | </method> | ||
| 489 | <method name="FreezeUnit"> | ||
| 490 | <arg type="s" name="name" direction="in"/> | ||
| 491 | </method> | ||
| 492 | <method name="ThawUnit"> | ||
| 493 | <arg type="s" name="name" direction="in"/> | ||
| 494 | </method> | ||
| 495 | <method name="ResetFailedUnit"> | ||
| 496 | <arg type="s" name="name" direction="in"/> | ||
| 497 | </method> | ||
| 498 | <!-- <method name="SetUnitProperties"> --> | ||
| 499 | <!-- <arg type="s" name="name" direction="in"/> --> | ||
| 500 | <!-- <arg type="b" name="runtime" direction="in"/> --> | ||
| 501 | <!-- <arg type="a(sv)" name="properties" direction="in"/> --> | ||
| 502 | <!-- </method> --> | ||
| 503 | <method name="BindMountUnit"> | ||
| 504 | <arg type="s" name="name" direction="in"/> | ||
| 505 | <arg type="s" name="source" direction="in"/> | ||
| 506 | <arg type="s" name="destination" direction="in"/> | ||
| 507 | <arg type="b" name="read_only" direction="in"/> | ||
| 508 | <arg type="b" name="mkdir" direction="in"/> | ||
| 509 | </method> | ||
| 510 | <!-- <method name="MountImageUnit"> --> | ||
| 511 | <!-- <arg type="s" name="name" direction="in"/> --> | ||
| 512 | <!-- <arg type="s" name="source" direction="in"/> --> | ||
| 513 | <!-- <arg type="s" name="destination" direction="in"/> --> | ||
| 514 | <!-- <arg type="b" name="read_only" direction="in"/> --> | ||
| 515 | <!-- <arg type="b" name="mkdir" direction="in"/> --> | ||
| 516 | <!-- <arg type="a(ss)" name="options" direction="in"/> --> | ||
| 517 | <!-- </method> --> | ||
| 518 | <method name="RefUnit"> | ||
| 519 | <arg type="s" name="name" direction="in"/> | ||
| 520 | </method> | ||
| 521 | <method name="UnrefUnit"> | ||
| 522 | <arg type="s" name="name" direction="in"/> | ||
| 523 | </method> | ||
| 524 | <!-- <method name="StartTransientUnit"> --> | ||
| 525 | <!-- <arg type="s" name="name" direction="in"/> --> | ||
| 526 | <!-- <arg type="s" name="mode" direction="in"/> --> | ||
| 527 | <!-- <arg type="a(sv)" name="properties" direction="in"/> --> | ||
| 528 | <!-- <arg type="a(sa(sv))" name="aux" direction="in"/> --> | ||
| 529 | <!-- <arg type="o" name="job" direction="out"/> --> | ||
| 530 | <!-- </method> --> | ||
| 531 | <!-- <method name="GetUnitProcesses"> --> | ||
| 532 | <!-- <arg type="s" name="name" direction="in"/> --> | ||
| 533 | <!-- <arg type="a(sus)" name="processes" direction="out"/> --> | ||
| 534 | <!-- </method> --> | ||
| 535 | <!-- <method name="AttachProcessesToUnit"> --> | ||
| 536 | <!-- <arg type="s" name="unit_name" direction="in"/> --> | ||
| 537 | <!-- <arg type="s" name="subcgroup" direction="in"/> --> | ||
| 538 | <!-- <arg type="au" name="pids" direction="in"/> --> | ||
| 539 | <!-- </method> --> | ||
| 540 | <method name="AbandonScope"> | ||
| 541 | <arg type="s" name="name" direction="in"/> | ||
| 542 | </method> | ||
| 543 | <method name="GetJob"> | ||
| 544 | <arg type="u" name="id" direction="in"/> | ||
| 545 | <arg type="o" name="job" direction="out"/> | ||
| 546 | </method> | ||
| 547 | <!-- <method name="GetJobAfter"> --> | ||
| 548 | <!-- <arg type="u" name="id" direction="in"/> --> | ||
| 549 | <!-- <arg type="a(usssoo)" name="jobs" direction="out"/> --> | ||
| 550 | <!-- </method> --> | ||
| 551 | <!-- <method name="GetJobBefore"> --> | ||
| 552 | <!-- <arg type="u" name="id" direction="in"/> --> | ||
| 553 | <!-- <arg type="a(usssoo)" name="jobs" direction="out"/> --> | ||
| 554 | <!-- </method> --> | ||
| 555 | <method name="CancelJob"> | ||
| 556 | <arg type="u" name="id" direction="in"/> | ||
| 557 | </method> | ||
| 558 | <method name="ClearJobs"> | ||
| 559 | </method> | ||
| 560 | <method name="ResetFailed"> | ||
| 561 | </method> | ||
| 562 | <method name="SetShowStatus"> | ||
| 563 | <arg type="s" name="mode" direction="in"/> | ||
| 564 | </method> | ||
| 565 | <!-- <method name="ListUnits"> --> | ||
| 566 | <!-- <arg type="a(ssssssouso)" name="units" direction="out"/> --> | ||
| 567 | <!-- </method> --> | ||
| 568 | <!-- <method name="ListUnitsFiltered"> --> | ||
| 569 | <!-- <arg type="as" name="states" direction="in"/> --> | ||
| 570 | <!-- <arg type="a(ssssssouso)" name="units" direction="out"/> --> | ||
| 571 | <!-- </method> --> | ||
| 572 | <!-- <method name="ListUnitsByPatterns"> --> | ||
| 573 | <!-- <arg type="as" name="states" direction="in"/> --> | ||
| 574 | <!-- <arg type="as" name="patterns" direction="in"/> --> | ||
| 575 | <!-- <arg type="a(ssssssouso)" name="units" direction="out"/> --> | ||
| 576 | <!-- </method> --> | ||
| 577 | <!-- <method name="ListUnitsByNames"> --> | ||
| 578 | <!-- <arg type="as" name="names" direction="in"/> --> | ||
| 579 | <!-- <arg type="a(ssssssouso)" name="units" direction="out"/> --> | ||
| 580 | <!-- </method> --> | ||
| 581 | <!-- <method name="ListJobs"> --> | ||
| 582 | <!-- <arg type="a(usssoo)" name="jobs" direction="out"/> --> | ||
| 583 | <!-- </method> --> | ||
| 584 | <method name="Subscribe"> | ||
| 585 | </method> | ||
| 586 | <method name="Unsubscribe"> | ||
| 587 | </method> | ||
| 588 | <method name="Dump"> | ||
| 589 | <arg type="s" name="output" direction="out"/> | ||
| 590 | </method> | ||
| 591 | <method name="DumpUnitsMatchingPatterns"> | ||
| 592 | <arg type="as" name="patterns" direction="in"/> | ||
| 593 | <arg type="s" name="output" direction="out"/> | ||
| 594 | </method> | ||
| 595 | <method name="DumpByFileDescriptor"> | ||
| 596 | <arg type="h" name="fd" direction="out"/> | ||
| 597 | </method> | ||
| 598 | <method name="DumpUnitsMatchingPatternsByFileDescriptor"> | ||
| 599 | <arg type="as" name="patterns" direction="in"/> | ||
| 600 | <arg type="h" name="fd" direction="out"/> | ||
| 601 | </method> | ||
| 602 | <method name="Reload"> | ||
| 603 | </method> | ||
| 604 | <method name="Reexecute"> | ||
| 605 | <annotation name="org.freedesktop.DBus.Method.NoReply" value="true"/> | ||
| 606 | </method> | ||
| 607 | <method name="Exit"> | ||
| 608 | <annotation name="org.freedesktop.systemd1.Privileged" value="true"/> | ||
| 609 | </method> | ||
| 610 | <method name="Reboot"> | ||
| 611 | <annotation name="org.freedesktop.systemd1.Privileged" value="true"/> | ||
| 612 | </method> | ||
| 613 | <method name="SoftReboot"> | ||
| 614 | <arg type="s" name="new_root" direction="in"/> | ||
| 615 | <annotation name="org.freedesktop.systemd1.Privileged" value="true"/> | ||
| 616 | </method> | ||
| 617 | <method name="PowerOff"> | ||
| 618 | <annotation name="org.freedesktop.systemd1.Privileged" value="true"/> | ||
| 619 | </method> | ||
| 620 | <method name="Halt"> | ||
| 621 | <annotation name="org.freedesktop.systemd1.Privileged" value="true"/> | ||
| 622 | </method> | ||
| 623 | <method name="KExec"> | ||
| 624 | <annotation name="org.freedesktop.systemd1.Privileged" value="true"/> | ||
| 625 | </method> | ||
| 626 | <method name="SwitchRoot"> | ||
| 627 | <arg type="s" name="new_root" direction="in"/> | ||
| 628 | <arg type="s" name="init" direction="in"/> | ||
| 629 | <annotation name="org.freedesktop.systemd1.Privileged" value="true"/> | ||
| 630 | </method> | ||
| 631 | <method name="SetEnvironment"> | ||
| 632 | <arg type="as" name="assignments" direction="in"/> | ||
| 633 | </method> | ||
| 634 | <method name="UnsetEnvironment"> | ||
| 635 | <arg type="as" name="names" direction="in"/> | ||
| 636 | </method> | ||
| 637 | <method name="UnsetAndSetEnvironment"> | ||
| 638 | <arg type="as" name="names" direction="in"/> | ||
| 639 | <arg type="as" name="assignments" direction="in"/> | ||
| 640 | </method> | ||
| 641 | <method name="EnqueueMarkedJobs"> | ||
| 642 | <arg type="ao" name="jobs" direction="out"/> | ||
| 643 | </method> | ||
| 644 | <!-- <method name="ListUnitFiles"> --> | ||
| 645 | <!-- <arg type="a(ss)" name="unit_files" direction="out"/> --> | ||
| 646 | <!-- </method> --> | ||
| 647 | <!-- <method name="ListUnitFilesByPatterns"> --> | ||
| 648 | <!-- <arg type="as" name="states" direction="in"/> --> | ||
| 649 | <!-- <arg type="as" name="patterns" direction="in"/> --> | ||
| 650 | <!-- <arg type="a(ss)" name="unit_files" direction="out"/> --> | ||
| 651 | <!-- </method> --> | ||
| 652 | <method name="GetUnitFileState"> | ||
| 653 | <arg type="s" name="file" direction="in"/> | ||
| 654 | <arg type="s" name="state" direction="out"/> | ||
| 655 | </method> | ||
| 656 | <!-- <method name="EnableUnitFiles"> --> | ||
| 657 | <!-- <arg type="as" name="files" direction="in"/> --> | ||
| 658 | <!-- <arg type="b" name="runtime" direction="in"/> --> | ||
| 659 | <!-- <arg type="b" name="force" direction="in"/> --> | ||
| 660 | <!-- <arg type="b" name="carries_install_info" direction="out"/> --> | ||
| 661 | <!-- <arg type="a(sss)" name="changes" direction="out"/> --> | ||
| 662 | <!-- </method> --> | ||
| 663 | <!-- <method name="DisableUnitFiles"> --> | ||
| 664 | <!-- <arg type="as" name="files" direction="in"/> --> | ||
| 665 | <!-- <arg type="b" name="runtime" direction="in"/> --> | ||
| 666 | <!-- <arg type="a(sss)" name="changes" direction="out"/> --> | ||
| 667 | <!-- </method> --> | ||
| 668 | <!-- <method name="EnableUnitFilesWithFlags"> --> | ||
| 669 | <!-- <arg type="as" name="files" direction="in"/> --> | ||
| 670 | <!-- <arg type="t" name="flags" direction="in"/> --> | ||
| 671 | <!-- <arg type="b" name="carries_install_info" direction="out"/> --> | ||
| 672 | <!-- <arg type="a(sss)" name="changes" direction="out"/> --> | ||
| 673 | <!-- </method> --> | ||
| 674 | <!-- <method name="DisableUnitFilesWithFlags"> --> | ||
| 675 | <!-- <arg type="as" name="files" direction="in"/> --> | ||
| 676 | <!-- <arg type="t" name="flags" direction="in"/> --> | ||
| 677 | <!-- <arg type="a(sss)" name="changes" direction="out"/> --> | ||
| 678 | <!-- </method> --> | ||
| 679 | <!-- <method name="DisableUnitFilesWithFlagsAndInstallInfo"> --> | ||
| 680 | <!-- <arg type="as" name="files" direction="in"/> --> | ||
| 681 | <!-- <arg type="t" name="flags" direction="in"/> --> | ||
| 682 | <!-- <arg type="b" name="carries_install_info" direction="out"/> --> | ||
| 683 | <!-- <arg type="a(sss)" name="changes" direction="out"/> --> | ||
| 684 | <!-- </method> --> | ||
| 685 | <!-- <method name="ReenableUnitFiles"> --> | ||
| 686 | <!-- <arg type="as" name="files" direction="in"/> --> | ||
| 687 | <!-- <arg type="b" name="runtime" direction="in"/> --> | ||
| 688 | <!-- <arg type="b" name="force" direction="in"/> --> | ||
| 689 | <!-- <arg type="b" name="carries_install_info" direction="out"/> --> | ||
| 690 | <!-- <arg type="a(sss)" name="changes" direction="out"/> --> | ||
| 691 | <!-- </method> --> | ||
| 692 | <!-- <method name="LinkUnitFiles"> --> | ||
| 693 | <!-- <arg type="as" name="files" direction="in"/> --> | ||
| 694 | <!-- <arg type="b" name="runtime" direction="in"/> --> | ||
| 695 | <!-- <arg type="b" name="force" direction="in"/> --> | ||
| 696 | <!-- <arg type="a(sss)" name="changes" direction="out"/> --> | ||
| 697 | <!-- </method> --> | ||
| 698 | <!-- <method name="PresetUnitFiles"> --> | ||
| 699 | <!-- <arg type="as" name="files" direction="in"/> --> | ||
| 700 | <!-- <arg type="b" name="runtime" direction="in"/> --> | ||
| 701 | <!-- <arg type="b" name="force" direction="in"/> --> | ||
| 702 | <!-- <arg type="b" name="carries_install_info" direction="out"/> --> | ||
| 703 | <!-- <arg type="a(sss)" name="changes" direction="out"/> --> | ||
| 704 | <!-- </method> --> | ||
| 705 | <!-- <method name="PresetUnitFilesWithMode"> --> | ||
| 706 | <!-- <arg type="as" name="files" direction="in"/> --> | ||
| 707 | <!-- <arg type="s" name="mode" direction="in"/> --> | ||
| 708 | <!-- <arg type="b" name="runtime" direction="in"/> --> | ||
| 709 | <!-- <arg type="b" name="force" direction="in"/> --> | ||
| 710 | <!-- <arg type="b" name="carries_install_info" direction="out"/> --> | ||
| 711 | <!-- <arg type="a(sss)" name="changes" direction="out"/> --> | ||
| 712 | <!-- </method> --> | ||
| 713 | <!-- <method name="MaskUnitFiles"> --> | ||
| 714 | <!-- <arg type="as" name="files" direction="in"/> --> | ||
| 715 | <!-- <arg type="b" name="runtime" direction="in"/> --> | ||
| 716 | <!-- <arg type="b" name="force" direction="in"/> --> | ||
| 717 | <!-- <arg type="a(sss)" name="changes" direction="out"/> --> | ||
| 718 | <!-- </method> --> | ||
| 719 | <!-- <method name="UnmaskUnitFiles"> --> | ||
| 720 | <!-- <arg type="as" name="files" direction="in"/> --> | ||
| 721 | <!-- <arg type="b" name="runtime" direction="in"/> --> | ||
| 722 | <!-- <arg type="a(sss)" name="changes" direction="out"/> --> | ||
| 723 | <!-- </method> --> | ||
| 724 | <!-- <method name="RevertUnitFiles"> --> | ||
| 725 | <!-- <arg type="as" name="files" direction="in"/> --> | ||
| 726 | <!-- <arg type="a(sss)" name="changes" direction="out"/> --> | ||
| 727 | <!-- </method> --> | ||
| 728 | <!-- <method name="SetDefaultTarget"> --> | ||
| 729 | <!-- <arg type="s" name="name" direction="in"/> --> | ||
| 730 | <!-- <arg type="b" name="force" direction="in"/> --> | ||
| 731 | <!-- <arg type="a(sss)" name="changes" direction="out"/> --> | ||
| 732 | <!-- </method> --> | ||
| 733 | <method name="GetDefaultTarget"> | ||
| 734 | <arg type="s" name="name" direction="out"/> | ||
| 735 | </method> | ||
| 736 | <!-- <method name="PresetAllUnitFiles"> --> | ||
| 737 | <!-- <arg type="s" name="mode" direction="in"/> --> | ||
| 738 | <!-- <arg type="b" name="runtime" direction="in"/> --> | ||
| 739 | <!-- <arg type="b" name="force" direction="in"/> --> | ||
| 740 | <!-- <arg type="a(sss)" name="changes" direction="out"/> --> | ||
| 741 | <!-- </method> --> | ||
| 742 | <!-- <method name="AddDependencyUnitFiles"> --> | ||
| 743 | <!-- <arg type="as" name="files" direction="in"/> --> | ||
| 744 | <!-- <arg type="s" name="target" direction="in"/> --> | ||
| 745 | <!-- <arg type="s" name="type" direction="in"/> --> | ||
| 746 | <!-- <arg type="b" name="runtime" direction="in"/> --> | ||
| 747 | <!-- <arg type="b" name="force" direction="in"/> --> | ||
| 748 | <!-- <arg type="a(sss)" name="changes" direction="out"/> --> | ||
| 749 | <!-- </method> --> | ||
| 750 | <method name="GetUnitFileLinks"> | ||
| 751 | <arg type="s" name="name" direction="in"/> | ||
| 752 | <arg type="b" name="runtime" direction="in"/> | ||
| 753 | <arg type="as" name="links" direction="out"/> | ||
| 754 | </method> | ||
| 755 | <method name="SetExitCode"> | ||
| 756 | <arg type="y" name="number" direction="in"/> | ||
| 757 | </method> | ||
| 758 | <method name="LookupDynamicUserByName"> | ||
| 759 | <arg type="s" name="name" direction="in"/> | ||
| 760 | <arg type="u" name="uid" direction="out"/> | ||
| 761 | </method> | ||
| 762 | <method name="LookupDynamicUserByUID"> | ||
| 763 | <arg type="u" name="uid" direction="in"/> | ||
| 764 | <arg type="s" name="name" direction="out"/> | ||
| 765 | </method> | ||
| 766 | <!-- <method name="GetDynamicUsers"> --> | ||
| 767 | <!-- <arg type="a(us)" name="users" direction="out"/> --> | ||
| 768 | <!-- </method> --> | ||
| 769 | <!-- <method name="DumpUnitFileDescriptorStore"> --> | ||
| 770 | <!-- <arg type="s" name="name" direction="in"/> --> | ||
| 771 | <!-- <arg type="a(suuutuusu)" name="entries" direction="out"/> --> | ||
| 772 | <!-- </method> --> | ||
| 773 | <!-- <method name="StartAuxiliaryScope"> --> | ||
| 774 | <!-- <arg type="s" name="name" direction="in"/> --> | ||
| 775 | <!-- <arg type="ah" name="pidfds" direction="in"/> --> | ||
| 776 | <!-- <arg type="t" name="flags" direction="in"/> --> | ||
| 777 | <!-- <arg type="a(sv)" name="properties" direction="in"/> --> | ||
| 778 | <!-- <arg type="o" name="job" direction="out"/> --> | ||
| 779 | <!-- <annotation name="org.freedesktop.DBus.Deprecated" value="true"/> --> | ||
| 780 | <!-- </method> --> | ||
| 781 | <signal name="UnitNew"> | ||
| 782 | <arg type="s" name="id"/> | ||
| 783 | <arg type="o" name="unit"/> | ||
| 784 | </signal> | ||
| 785 | <signal name="UnitRemoved"> | ||
| 786 | <arg type="s" name="id"/> | ||
| 787 | <arg type="o" name="unit"/> | ||
| 788 | </signal> | ||
| 789 | <signal name="JobNew"> | ||
| 790 | <arg type="u" name="id"/> | ||
| 791 | <arg type="o" name="job"/> | ||
| 792 | <arg type="s" name="unit"/> | ||
| 793 | </signal> | ||
| 794 | <signal name="JobRemoved"> | ||
| 795 | <arg type="u" name="id"/> | ||
| 796 | <arg type="o" name="job"/> | ||
| 797 | <arg type="s" name="unit"/> | ||
| 798 | <arg type="s" name="result"/> | ||
| 799 | </signal> | ||
| 800 | <signal name="StartupFinished"> | ||
| 801 | <arg type="t" name="firmware"/> | ||
| 802 | <arg type="t" name="loader"/> | ||
| 803 | <arg type="t" name="kernel"/> | ||
| 804 | <arg type="t" name="initrd"/> | ||
| 805 | <arg type="t" name="userspace"/> | ||
| 806 | <arg type="t" name="total"/> | ||
| 807 | </signal> | ||
| 808 | <signal name="UnitFilesChanged"> | ||
| 809 | </signal> | ||
| 810 | <signal name="Reloading"> | ||
| 811 | <arg type="b" name="active"/> | ||
| 812 | </signal> | ||
| 813 | </interface> | ||
| 814 | <node name="unit"/> | ||
| 815 | <node name="job"/> | ||
| 816 | </node> | ||
| 817 | |||
diff --git a/accounts/gkleen@sif/shell/quickshell/ActiveWindowDisplay.qml b/accounts/gkleen@sif/shell/quickshell/ActiveWindowDisplay.qml new file mode 100644 index 00000000..dcc23279 --- /dev/null +++ b/accounts/gkleen@sif/shell/quickshell/ActiveWindowDisplay.qml | |||
| @@ -0,0 +1,172 @@ | |||
| 1 | import QtQuick | ||
| 2 | import qs.Services | ||
| 3 | import Quickshell | ||
| 4 | import Quickshell.Widgets | ||
| 5 | |||
| 6 | Item { | ||
| 7 | id: activeWindowDisplay | ||
| 8 | |||
| 9 | required property int maxWidth | ||
| 10 | required property var screen | ||
| 11 | |||
| 12 | property var activeWindow: { | ||
| 13 | let currWindowId = Array.from(NiriService.workspaces).find(ws => { | ||
| 14 | return ws.output === screen.name && ws.is_active; | ||
| 15 | })?.active_window_id; | ||
| 16 | |||
| 17 | return currWindowId ? Array.from(NiriService.windows).find(win => win.id == currWindowId) : null; | ||
| 18 | } | ||
| 19 | property var windowEntry: activeWindow ? DesktopEntries.heuristicLookup(activeWindow.app_id) : null | ||
| 20 | |||
| 21 | anchors.verticalCenter: parent.verticalCenter | ||
| 22 | width: activeWindowDisplayContent.width | ||
| 23 | height: parent.height | ||
| 24 | |||
| 25 | WrapperMouseArea { | ||
| 26 | id: widgetMouseArea | ||
| 27 | |||
| 28 | anchors.fill: parent | ||
| 29 | |||
| 30 | hoverEnabled: true | ||
| 31 | |||
| 32 | Item { | ||
| 33 | anchors.fill: parent | ||
| 34 | |||
| 35 | Row { | ||
| 36 | id: activeWindowDisplayContent | ||
| 37 | |||
| 38 | width: childrenRect.width | ||
| 39 | height: parent.height | ||
| 40 | anchors.verticalCenter: parent.verticalCenter | ||
| 41 | spacing: 8 | ||
| 42 | |||
| 43 | IconImage { | ||
| 44 | id: activeWindowIcon | ||
| 45 | |||
| 46 | implicitSize: 14 | ||
| 47 | |||
| 48 | anchors.verticalCenter: parent.verticalCenter | ||
| 49 | |||
| 50 | source: { | ||
| 51 | let icon = activeWindowDisplay.windowEntry?.icon | ||
| 52 | if (typeof icon === 'string' || icon instanceof String) { | ||
| 53 | if (icon.includes("?path=")) { | ||
| 54 | const split = icon.split("?path=") | ||
| 55 | if (split.length !== 2) | ||
| 56 | return icon | ||
| 57 | const name = split[0] | ||
| 58 | const path = split[1] | ||
| 59 | const fileName = name.substring( | ||
| 60 | name.lastIndexOf("/") + 1) | ||
| 61 | return `file://${path}/${fileName}` | ||
| 62 | } else | ||
| 63 | icon = Quickshell.iconPath(icon); | ||
| 64 | return icon | ||
| 65 | } | ||
| 66 | return "" | ||
| 67 | } | ||
| 68 | asynchronous: true | ||
| 69 | smooth: true | ||
| 70 | mipmap: true | ||
| 71 | } | ||
| 72 | |||
| 73 | Text { | ||
| 74 | id: windowTitle | ||
| 75 | |||
| 76 | width: Math.min(implicitWidth, activeWindowDisplay.maxWidth - activeWindowIcon.width - activeWindowDisplayContent.spacing) | ||
| 77 | |||
| 78 | property var appAliases: { "Firefox": "Mozilla Firefox", "mpv Media Player": "mpv", "Thunderbird": "Mozilla Thunderbird", "Thunderbird (LMU)": "Mozilla Thunderbird" } | ||
| 79 | |||
| 80 | elide: Text.ElideRight | ||
| 81 | maximumLineCount: 1 | ||
| 82 | color: "white" | ||
| 83 | anchors.verticalCenter: parent.verticalCenter | ||
| 84 | text: { | ||
| 85 | if (!activeWindowDisplay.activeWindow) | ||
| 86 | return ""; | ||
| 87 | |||
| 88 | var title = activeWindowDisplay.activeWindow.title; | ||
| 89 | var appName = activeWindowDisplay.windowEntry?.name; | ||
| 90 | if (appAliases[appName]) | ||
| 91 | appName = appAliases[appName]; | ||
| 92 | if (appName && title.endsWith(appName)) { | ||
| 93 | const oldTitle = title; | ||
| 94 | title = title.substring(0, title.length - appName.length); | ||
| 95 | title = title.replace(/\s*(—|-)\s*$/, ""); | ||
| 96 | if (!title) | ||
| 97 | title = oldTitle; | ||
| 98 | } | ||
| 99 | return title; | ||
| 100 | } | ||
| 101 | } | ||
| 102 | } | ||
| 103 | } | ||
| 104 | } | ||
| 105 | |||
| 106 | Loader { | ||
| 107 | id: tooltipLoader | ||
| 108 | |||
| 109 | active: false | ||
| 110 | |||
| 111 | Connections { | ||
| 112 | target: widgetMouseArea | ||
| 113 | function onContainsMouseChanged() { | ||
| 114 | if (widgetMouseArea.containsMouse) | ||
| 115 | tooltipLoader.active = true; | ||
| 116 | } | ||
| 117 | } | ||
| 118 | |||
| 119 | PopupWindow { | ||
| 120 | id: tooltip | ||
| 121 | |||
| 122 | property bool nextVisible: widgetMouseArea.containsMouse || tooltipMouseArea.containsMouse | ||
| 123 | |||
| 124 | anchor { | ||
| 125 | item: widgetMouseArea | ||
| 126 | edges: Edges.Bottom | Edges.Left | ||
| 127 | } | ||
| 128 | visible: false | ||
| 129 | |||
| 130 | onNextVisibleChanged: hangTimer.restart() | ||
| 131 | |||
| 132 | Timer { | ||
| 133 | id: hangTimer | ||
| 134 | interval: tooltip.visible ? 100 : 500 | ||
| 135 | onTriggered: { | ||
| 136 | tooltip.visible = tooltip.nextVisible; | ||
| 137 | if (!tooltip.visible) | ||
| 138 | tooltipLoader.active = false; | ||
| 139 | } | ||
| 140 | } | ||
| 141 | |||
| 142 | implicitWidth: widgetTooltipText.contentWidth + 16 | ||
| 143 | implicitHeight: widgetTooltipText.contentHeight + 16 | ||
| 144 | color: "black" | ||
| 145 | |||
| 146 | WrapperMouseArea { | ||
| 147 | id: tooltipMouseArea | ||
| 148 | |||
| 149 | hoverEnabled: true | ||
| 150 | enabled: true | ||
| 151 | |||
| 152 | anchors.fill: parent | ||
| 153 | |||
| 154 | Item { | ||
| 155 | anchors.fill: parent | ||
| 156 | |||
| 157 | Text { | ||
| 158 | id: widgetTooltipText | ||
| 159 | |||
| 160 | anchors.centerIn: parent | ||
| 161 | |||
| 162 | font.pointSize: 10 | ||
| 163 | font.family: "Fira Mono" | ||
| 164 | color: "white" | ||
| 165 | |||
| 166 | text: JSON.stringify(Object.assign({}, activeWindowDisplay.activeWindow), null, 2) | ||
| 167 | } | ||
| 168 | } | ||
| 169 | } | ||
| 170 | } | ||
| 171 | } | ||
| 172 | } | ||
diff --git a/accounts/gkleen@sif/shell/quickshell/Bar.qml b/accounts/gkleen@sif/shell/quickshell/Bar.qml new file mode 100644 index 00000000..9210066c --- /dev/null +++ b/accounts/gkleen@sif/shell/quickshell/Bar.qml | |||
| @@ -0,0 +1,117 @@ | |||
| 1 | import Quickshell | ||
| 2 | import Quickshell.Wayland | ||
| 3 | import QtQuick | ||
| 4 | |||
| 5 | PanelWindow { | ||
| 6 | id: bar | ||
| 7 | |||
| 8 | required property var modelData | ||
| 9 | screen: modelData | ||
| 10 | |||
| 11 | WlrLayershell.namespace: "bar" | ||
| 12 | |||
| 13 | anchors { | ||
| 14 | top: true | ||
| 15 | left: true | ||
| 16 | right: true | ||
| 17 | } | ||
| 18 | margins { | ||
| 19 | left: 26 + 8 | ||
| 20 | right: 26 + 8 | ||
| 21 | } | ||
| 22 | |||
| 23 | implicitHeight: 21 | ||
| 24 | color: "transparent" | ||
| 25 | |||
| 26 | Rectangle { | ||
| 27 | color: Qt.rgba(0, 0, 0, 0.75) | ||
| 28 | anchors.fill: parent | ||
| 29 | // bottomLeftRadius: 8 | ||
| 30 | // bottomRightRadius: 8 | ||
| 31 | } | ||
| 32 | |||
| 33 | Row { | ||
| 34 | id: left | ||
| 35 | |||
| 36 | height: parent.height | ||
| 37 | width: childrenRect.width | ||
| 38 | anchors.left: parent.left | ||
| 39 | anchors.leftMargin: 8 | ||
| 40 | anchors.verticalCenter: parent.verticalCenter | ||
| 41 | spacing: 8 | ||
| 42 | |||
| 43 | WorkspaceSwitcher { | ||
| 44 | screen: bar.screen | ||
| 45 | } | ||
| 46 | } | ||
| 47 | |||
| 48 | Row { | ||
| 49 | id: center | ||
| 50 | |||
| 51 | height: parent.height | ||
| 52 | width: childrenRect.width | ||
| 53 | anchors.centerIn: parent | ||
| 54 | spacing: 5 | ||
| 55 | |||
| 56 | ActiveWindowDisplay { | ||
| 57 | screen: bar.screen | ||
| 58 | maxWidth: bar.width - 2*Math.max(left.width, right.width) - 2*8 | ||
| 59 | } | ||
| 60 | } | ||
| 61 | |||
| 62 | Row { | ||
| 63 | id: right | ||
| 64 | |||
| 65 | height: parent.height | ||
| 66 | width: childrenRect.width | ||
| 67 | anchors.right: parent.right | ||
| 68 | anchors.rightMargin: 8 | ||
| 69 | anchors.verticalCenter: parent.verticalCenter | ||
| 70 | spacing: 0 | ||
| 71 | |||
| 72 | // WorktimeWidget { command: "time"; } | ||
| 73 | |||
| 74 | // WorktimeWidget { command: "today"; } | ||
| 75 | |||
| 76 | KeyboardLayout {} | ||
| 77 | |||
| 78 | Item { | ||
| 79 | visible: privacy.visible | ||
| 80 | height: parent.height | ||
| 81 | width: 8 - 4 | ||
| 82 | } | ||
| 83 | |||
| 84 | PrivacyWidget { | ||
| 85 | id: privacy | ||
| 86 | } | ||
| 87 | |||
| 88 | Item { | ||
| 89 | visible: privacy.visible | ||
| 90 | height: parent.height | ||
| 91 | width: 8 - 4 | ||
| 92 | } | ||
| 93 | |||
| 94 | SystemTray {} | ||
| 95 | |||
| 96 | PipewireWidget {} | ||
| 97 | |||
| 98 | BrightnessWidget {} | ||
| 99 | |||
| 100 | BatteryWidget {} | ||
| 101 | |||
| 102 | WaylandInhibitorWidget { | ||
| 103 | window: bar | ||
| 104 | } | ||
| 105 | |||
| 106 | NotificationInhibitorWidget {} | ||
| 107 | |||
| 108 | LidSwitchInhibitorWidget {} | ||
| 109 | |||
| 110 | Item { | ||
| 111 | height: parent.height | ||
| 112 | width: 8 - 4 | ||
| 113 | } | ||
| 114 | |||
| 115 | Clock {} | ||
| 116 | } | ||
| 117 | } \ No newline at end of file | ||
diff --git a/accounts/gkleen@sif/shell/quickshell/BatteryWidget.qml b/accounts/gkleen@sif/shell/quickshell/BatteryWidget.qml new file mode 100644 index 00000000..da17df2a --- /dev/null +++ b/accounts/gkleen@sif/shell/quickshell/BatteryWidget.qml | |||
| @@ -0,0 +1,136 @@ | |||
| 1 | import QtQuick | ||
| 2 | import Quickshell | ||
| 3 | import Quickshell.Widgets | ||
| 4 | import Quickshell.Services.UPower | ||
| 5 | |||
| 6 | Item { | ||
| 7 | id: root | ||
| 8 | |||
| 9 | height: parent.height | ||
| 10 | width: batteryIcon.width + 8 | ||
| 11 | anchors.verticalCenter: parent.verticalCenter | ||
| 12 | |||
| 13 | property var batteryDevice: Array.from(UPower.devices.values).find(dev => dev.isLaptopBattery) | ||
| 14 | |||
| 15 | WrapperMouseArea { | ||
| 16 | id: widgetMouseArea | ||
| 17 | |||
| 18 | anchors.fill: parent | ||
| 19 | |||
| 20 | hoverEnabled: true | ||
| 21 | |||
| 22 | Item { | ||
| 23 | anchors.fill: parent | ||
| 24 | |||
| 25 | MaterialDesignIcon { | ||
| 26 | id: batteryIcon | ||
| 27 | |||
| 28 | implicitSize: 14 | ||
| 29 | anchors.centerIn: parent | ||
| 30 | |||
| 31 | icon: { | ||
| 32 | if (!root.batteryDevice?.ready) | ||
| 33 | return "battery-unknown"; | ||
| 34 | |||
| 35 | if (root.batteryDevice.state == UPowerDeviceState.FullyCharged) | ||
| 36 | return "power-plug-battery"; | ||
| 37 | |||
| 38 | const perdec = 10 * Math.max(1, Math.ceil(root.batteryDevice.percentage * 10)); | ||
| 39 | if (root.batteryDevice.state == UPowerDeviceState.Charging) | ||
| 40 | return `battery-charging-${perdec}`; | ||
| 41 | if (perdec == 100) | ||
| 42 | return "battery"; | ||
| 43 | return `battery-${perdec}`; | ||
| 44 | } | ||
| 45 | color: { | ||
| 46 | if (!root.batteryDevice?.ready) | ||
| 47 | return "#555"; | ||
| 48 | |||
| 49 | if (root.batteryDevice.state != UPowerDeviceState.FullyCharged && root.batteryDevice.state != UPowerDeviceState.Charging && root.batteryDevice.timeToEmpty < 20 * 60) | ||
| 50 | return "#f2201f"; | ||
| 51 | if (root.batteryDevice.state != UPowerDeviceState.FullyCharged && root.batteryDevice.state != UPowerDeviceState.Charging && root.batteryDevice.timeToEmpty < 40 * 60) | ||
| 52 | return "#f28a21"; | ||
| 53 | if (root.batteryDevice.state != UPowerDeviceState.FullyCharged) | ||
| 54 | return "#fff"; | ||
| 55 | return "#555"; | ||
| 56 | } | ||
| 57 | } | ||
| 58 | } | ||
| 59 | } | ||
| 60 | |||
| 61 | PopupWindow { | ||
| 62 | id: tooltip | ||
| 63 | |||
| 64 | property bool nextVisible: widgetMouseArea.containsMouse || tooltipMouseArea.containsMouse | ||
| 65 | |||
| 66 | anchor { | ||
| 67 | item: widgetMouseArea | ||
| 68 | edges: Edges.Bottom | Edges.Left | ||
| 69 | } | ||
| 70 | visible: false | ||
| 71 | |||
| 72 | onNextVisibleChanged: hangTimer.restart() | ||
| 73 | |||
| 74 | Timer { | ||
| 75 | id: hangTimer | ||
| 76 | interval: 100 | ||
| 77 | onTriggered: tooltip.visible = tooltip.nextVisible | ||
| 78 | } | ||
| 79 | |||
| 80 | implicitWidth: widgetTooltipText.contentWidth + 16 | ||
| 81 | implicitHeight: widgetTooltipText.contentHeight + 16 | ||
| 82 | color: "black" | ||
| 83 | |||
| 84 | WrapperMouseArea { | ||
| 85 | id: tooltipMouseArea | ||
| 86 | |||
| 87 | hoverEnabled: true | ||
| 88 | enabled: true | ||
| 89 | |||
| 90 | anchors.fill: parent | ||
| 91 | |||
| 92 | Item { | ||
| 93 | anchors.fill: parent | ||
| 94 | |||
| 95 | Text { | ||
| 96 | id: widgetTooltipText | ||
| 97 | |||
| 98 | anchors.centerIn: parent | ||
| 99 | |||
| 100 | font.pointSize: 10 | ||
| 101 | font.family: "Fira Sans" | ||
| 102 | color: "white" | ||
| 103 | |||
| 104 | text: { | ||
| 105 | const stateStr = UPowerDeviceState.toString(root.batteryDevice.state); | ||
| 106 | var outStr = stateStr; | ||
| 107 | if (root.batteryDevice.state != UPowerDeviceState.FullyCharged) | ||
| 108 | outStr += ` ${Math.round(root.batteryDevice.percentage * 100)}%`; | ||
| 109 | |||
| 110 | function formatTime(t) { | ||
| 111 | var res = ""; | ||
| 112 | for (const unit of [{ "s": "h", "v": 3600 }, { "s": "m", "v": 60 }, { "s": "s", "v": 1 }]) { | ||
| 113 | if (t < unit.v) | ||
| 114 | continue; | ||
| 115 | res += Math.floor(t / unit.v) + unit.s; | ||
| 116 | t %= unit.v; | ||
| 117 | } | ||
| 118 | return res; | ||
| 119 | } | ||
| 120 | if (root.batteryDevice.timeToEmpty != 0) { | ||
| 121 | const tStr = formatTime(Math.floor(root.batteryDevice.timeToEmpty / 60) * 60); | ||
| 122 | if (tStr) | ||
| 123 | outStr += " " + tStr; | ||
| 124 | } else if (root.batteryDevice.timeToFull != 0) { | ||
| 125 | const tStr = formatTime(Math.ceil(root.batteryDevice.timeToFull / 60) * 60); | ||
| 126 | if (tStr) | ||
| 127 | outStr += " " + tStr; | ||
| 128 | } | ||
| 129 | |||
| 130 | return outStr; | ||
| 131 | } | ||
| 132 | } | ||
| 133 | } | ||
| 134 | } | ||
| 135 | } | ||
| 136 | } | ||
diff --git a/accounts/gkleen@sif/shell/quickshell/BrightnessOSD.qml b/accounts/gkleen@sif/shell/quickshell/BrightnessOSD.qml new file mode 100644 index 00000000..a432179e --- /dev/null +++ b/accounts/gkleen@sif/shell/quickshell/BrightnessOSD.qml | |||
| @@ -0,0 +1,117 @@ | |||
| 1 | import QtQuick | ||
| 2 | import QtQuick.Layouts | ||
| 3 | import Quickshell | ||
| 4 | import Quickshell.Widgets | ||
| 5 | import qs.Services | ||
| 6 | |||
| 7 | Scope { | ||
| 8 | id: root | ||
| 9 | |||
| 10 | property bool show: false | ||
| 11 | property bool inhibited: true | ||
| 12 | |||
| 13 | Connections { | ||
| 14 | target: Brightness | ||
| 15 | |||
| 16 | function onCurrBrightnessChanged() { | ||
| 17 | root.show = true; | ||
| 18 | hideTimer.restart(); | ||
| 19 | } | ||
| 20 | } | ||
| 21 | |||
| 22 | onShowChanged: { | ||
| 23 | if (show) | ||
| 24 | hideTimer.restart(); | ||
| 25 | } | ||
| 26 | |||
| 27 | Timer { | ||
| 28 | id: hideTimer | ||
| 29 | interval: 1000 | ||
| 30 | onTriggered: root.show = false | ||
| 31 | } | ||
| 32 | |||
| 33 | Timer { | ||
| 34 | id: startInhibit | ||
| 35 | interval: 100 | ||
| 36 | running: true | ||
| 37 | onTriggered: { | ||
| 38 | root.show = false; | ||
| 39 | root.inhibited = false; | ||
| 40 | } | ||
| 41 | } | ||
| 42 | |||
| 43 | LazyLoader { | ||
| 44 | active: root.show && !root.inhibited | ||
| 45 | |||
| 46 | Variants { | ||
| 47 | model: Quickshell.screens | ||
| 48 | |||
| 49 | delegate: Scope { | ||
| 50 | id: screenScope | ||
| 51 | |||
| 52 | required property var modelData | ||
| 53 | |||
| 54 | PanelWindow { | ||
| 55 | id: window | ||
| 56 | |||
| 57 | screen: screenScope.modelData | ||
| 58 | |||
| 59 | anchors.top: true | ||
| 60 | margins.top: screen.height / 2 - 50 + 3.5 | ||
| 61 | exclusiveZone: 0 | ||
| 62 | exclusionMode: ExclusionMode.Ignore | ||
| 63 | |||
| 64 | implicitWidth: 400 | ||
| 65 | implicitHeight: 50 | ||
| 66 | |||
| 67 | mask: Region {} | ||
| 68 | |||
| 69 | color: "transparent" | ||
| 70 | |||
| 71 | Rectangle { | ||
| 72 | anchors.fill: parent | ||
| 73 | color: Qt.rgba(0, 0, 0, 0.75) | ||
| 74 | } | ||
| 75 | |||
| 76 | RowLayout { | ||
| 77 | id: layout | ||
| 78 | |||
| 79 | anchors.centerIn: parent | ||
| 80 | |||
| 81 | height: 50 - 8*2 | ||
| 82 | width: 400 - 8*2 | ||
| 83 | |||
| 84 | MaterialDesignIcon { | ||
| 85 | id: volumeIcon | ||
| 86 | |||
| 87 | implicitWidth: parent.height | ||
| 88 | implicitHeight: parent.height | ||
| 89 | |||
| 90 | icon: `brightness-${Math.min(7, Math.floor(Brightness.currBrightness * 7) + 1)}` | ||
| 91 | } | ||
| 92 | |||
| 93 | Rectangle { | ||
| 94 | Layout.fillWidth: true | ||
| 95 | |||
| 96 | implicitHeight: 10 | ||
| 97 | |||
| 98 | color: "#50ffffff" | ||
| 99 | |||
| 100 | Rectangle { | ||
| 101 | anchors { | ||
| 102 | left: parent.left | ||
| 103 | top: parent.top | ||
| 104 | bottom: parent.bottom | ||
| 105 | } | ||
| 106 | |||
| 107 | color: "white" | ||
| 108 | |||
| 109 | implicitWidth: parent.width * Brightness.currBrightness | ||
| 110 | } | ||
| 111 | } | ||
| 112 | } | ||
| 113 | } | ||
| 114 | } | ||
| 115 | } | ||
| 116 | } | ||
| 117 | } | ||
diff --git a/accounts/gkleen@sif/shell/quickshell/BrightnessWidget.qml b/accounts/gkleen@sif/shell/quickshell/BrightnessWidget.qml new file mode 100644 index 00000000..3bb5a80e --- /dev/null +++ b/accounts/gkleen@sif/shell/quickshell/BrightnessWidget.qml | |||
| @@ -0,0 +1,84 @@ | |||
| 1 | import QtQuick | ||
| 2 | import Quickshell | ||
| 3 | import Quickshell.Widgets | ||
| 4 | import qs.Services | ||
| 5 | |||
| 6 | Item { | ||
| 7 | height: parent.height | ||
| 8 | width: brightnessIcon.width + 8 | ||
| 9 | anchors.verticalCenter: parent.verticalCenter | ||
| 10 | |||
| 11 | WrapperMouseArea { | ||
| 12 | id: widgetMouseArea | ||
| 13 | |||
| 14 | anchors.fill: parent | ||
| 15 | |||
| 16 | hoverEnabled: true | ||
| 17 | |||
| 18 | property real sensitivity: (1 / 50) / 120 | ||
| 19 | onWheel: event => Brightness.currBrightness += event.angleDelta.y * sensitivity | ||
| 20 | |||
| 21 | Item { | ||
| 22 | anchors.fill: parent | ||
| 23 | |||
| 24 | MaterialDesignIcon { | ||
| 25 | id: brightnessIcon | ||
| 26 | |||
| 27 | implicitSize: 14 | ||
| 28 | anchors.centerIn: parent | ||
| 29 | |||
| 30 | icon: `brightness-${Math.min(7, Math.floor(Brightness.currBrightness * 7) + 1)}` | ||
| 31 | color: "#555" | ||
| 32 | } | ||
| 33 | } | ||
| 34 | } | ||
| 35 | |||
| 36 | PopupWindow { | ||
| 37 | id: tooltip | ||
| 38 | |||
| 39 | property bool nextVisible: widgetMouseArea.containsMouse || tooltipMouseArea.containsMouse | ||
| 40 | |||
| 41 | anchor { | ||
| 42 | item: widgetMouseArea | ||
| 43 | edges: Edges.Bottom | Edges.Left | ||
| 44 | } | ||
| 45 | visible: false | ||
| 46 | |||
| 47 | onNextVisibleChanged: hangTimer.restart() | ||
| 48 | |||
| 49 | Timer { | ||
| 50 | id: hangTimer | ||
| 51 | interval: 100 | ||
| 52 | onTriggered: tooltip.visible = tooltip.nextVisible | ||
| 53 | } | ||
| 54 | |||
| 55 | implicitWidth: widgetTooltipText.contentWidth + 16 | ||
| 56 | implicitHeight: widgetTooltipText.contentHeight + 16 | ||
| 57 | color: "black" | ||
| 58 | |||
| 59 | WrapperMouseArea { | ||
| 60 | id: tooltipMouseArea | ||
| 61 | |||
| 62 | hoverEnabled: true | ||
| 63 | enabled: true | ||
| 64 | |||
| 65 | anchors.fill: parent | ||
| 66 | |||
| 67 | Item { | ||
| 68 | anchors.fill: parent | ||
| 69 | |||
| 70 | Text { | ||
| 71 | id: widgetTooltipText | ||
| 72 | |||
| 73 | anchors.centerIn: parent | ||
| 74 | |||
| 75 | font.pointSize: 10 | ||
| 76 | font.family: "Fira Sans" | ||
| 77 | color: "white" | ||
| 78 | |||
| 79 | text: `${Math.round(Brightness.currBrightness * 100)}%` | ||
| 80 | } | ||
| 81 | } | ||
| 82 | } | ||
| 83 | } | ||
| 84 | } | ||
diff --git a/accounts/gkleen@sif/shell/quickshell/Clock.qml b/accounts/gkleen@sif/shell/quickshell/Clock.qml new file mode 100644 index 00000000..b7004528 --- /dev/null +++ b/accounts/gkleen@sif/shell/quickshell/Clock.qml | |||
| @@ -0,0 +1,295 @@ | |||
| 1 | import QtQml | ||
| 2 | import QtQuick | ||
| 3 | import Quickshell | ||
| 4 | import Custom as Custom | ||
| 5 | import QtQuick.Controls | ||
| 6 | import QtQuick.Layouts | ||
| 7 | import Quickshell.Widgets | ||
| 8 | |||
| 9 | Item { | ||
| 10 | id: clockItem | ||
| 11 | |||
| 12 | property bool calendarPopup: true | ||
| 13 | |||
| 14 | width: clock.contentWidth | ||
| 15 | height: parent.height | ||
| 16 | anchors.verticalCenter: parent.verticalCenter | ||
| 17 | |||
| 18 | WrapperMouseArea { | ||
| 19 | id: clockMouseArea | ||
| 20 | |||
| 21 | anchors.fill: parent | ||
| 22 | hoverEnabled: true | ||
| 23 | enabled: clockItem.calendarPopup | ||
| 24 | |||
| 25 | Item { | ||
| 26 | anchors.fill: parent | ||
| 27 | |||
| 28 | Text { | ||
| 29 | id: clock | ||
| 30 | color: "white" | ||
| 31 | |||
| 32 | anchors.verticalCenter: parent.verticalCenter | ||
| 33 | |||
| 34 | Custom.Chrono { | ||
| 35 | id: chrono | ||
| 36 | |||
| 37 | onDateChanged: clock.text = format("W{0:%V-%u} {0:%F} {0:%H:%M:%S%Ez}") | ||
| 38 | } | ||
| 39 | |||
| 40 | font.pointSize: 10 | ||
| 41 | font.family: "Fira Sans" | ||
| 42 | font.features: { "tnum": 1 } | ||
| 43 | } | ||
| 44 | } | ||
| 45 | } | ||
| 46 | |||
| 47 | Loader { | ||
| 48 | id: tooltipLoader | ||
| 49 | |||
| 50 | active: false | ||
| 51 | |||
| 52 | Connections { | ||
| 53 | target: clockMouseArea | ||
| 54 | function onContainsMouseChanged() { | ||
| 55 | if (clockMouseArea.containsMouse) | ||
| 56 | tooltipLoader.active = true; | ||
| 57 | } | ||
| 58 | } | ||
| 59 | |||
| 60 | sourceComponent: PopupWindow { | ||
| 61 | id: tooltip | ||
| 62 | |||
| 63 | property bool nextVisible: clockMouseArea.containsMouse || tooltipMouseArea.containsMouse | ||
| 64 | |||
| 65 | anchor { | ||
| 66 | item: clockMouseArea | ||
| 67 | edges: Edges.Bottom | Edges.Left | ||
| 68 | } | ||
| 69 | visible: false | ||
| 70 | |||
| 71 | onNextVisibleChanged: hangTimer.restart() | ||
| 72 | |||
| 73 | Timer { | ||
| 74 | id: hangTimer | ||
| 75 | interval: 100 | ||
| 76 | onTriggered: { | ||
| 77 | tooltip.visible = tooltip.nextVisible; | ||
| 78 | if (!tooltip.visible) | ||
| 79 | tooltipLoader.active = false; | ||
| 80 | } | ||
| 81 | } | ||
| 82 | |||
| 83 | implicitWidth: tooltipLayout.childrenRect.width + 16 | ||
| 84 | implicitHeight: tooltipLayout.childrenRect.height + 16 | ||
| 85 | color: "black" | ||
| 86 | |||
| 87 | onVisibleChanged: { | ||
| 88 | yearCalendar.year = chrono.date.getFullYear(); | ||
| 89 | yearCalendar.angleRem = 0; | ||
| 90 | } | ||
| 91 | |||
| 92 | WrapperMouseArea { | ||
| 93 | id: tooltipMouseArea | ||
| 94 | |||
| 95 | hoverEnabled: true | ||
| 96 | enabled: true | ||
| 97 | |||
| 98 | onWheel: event => yearCalendar.scrollYear(event) | ||
| 99 | |||
| 100 | anchors.fill: parent | ||
| 101 | |||
| 102 | Item { | ||
| 103 | id: clockTooltipContent | ||
| 104 | |||
| 105 | anchors.fill: parent | ||
| 106 | |||
| 107 | ColumnLayout { | ||
| 108 | id: tooltipLayout | ||
| 109 | |||
| 110 | anchors { | ||
| 111 | left: parent.left | ||
| 112 | top: parent.top | ||
| 113 | leftMargin: 8 | ||
| 114 | topMargin: 8 | ||
| 115 | } | ||
| 116 | |||
| 117 | Text { | ||
| 118 | id: yearLabel | ||
| 119 | |||
| 120 | horizontalAlignment: Text.AlignHCenter | ||
| 121 | |||
| 122 | font.pointSize: 14 | ||
| 123 | font.family: "Fira Sans" | ||
| 124 | font.features: { "tnum": 1 } | ||
| 125 | color: "white" | ||
| 126 | |||
| 127 | text: yearCalendar.year | ||
| 128 | |||
| 129 | Layout.fillWidth: true | ||
| 130 | Layout.bottomMargin: 8 | ||
| 131 | } | ||
| 132 | |||
| 133 | GridLayout { | ||
| 134 | property int year: chrono.date.getFullYear() | ||
| 135 | |||
| 136 | id: yearCalendar | ||
| 137 | |||
| 138 | columns: 3 | ||
| 139 | columnSpacing: 16 | ||
| 140 | rowSpacing: 16 | ||
| 141 | |||
| 142 | Layout.alignment: Qt.AlignHCenter | ||
| 143 | Layout.fillWidth: false | ||
| 144 | |||
| 145 | property real angleRem: 0 | ||
| 146 | property real sensitivity: 1 / 120 | ||
| 147 | |||
| 148 | function scrollYear(event) { | ||
| 149 | angleRem += event.angleDelta.y; | ||
| 150 | const d = Math.round(angleRem * sensitivity); | ||
| 151 | yearCalendar.year += d; | ||
| 152 | angleRem -= d / sensitivity; | ||
| 153 | } | ||
| 154 | |||
| 155 | Connections { | ||
| 156 | target: clockMouseArea | ||
| 157 | function onWheel(event) { yearCalendar.scrollYear(event); } | ||
| 158 | } | ||
| 159 | |||
| 160 | Repeater { | ||
| 161 | model: 12 | ||
| 162 | |||
| 163 | GridLayout { | ||
| 164 | columns: 2 | ||
| 165 | |||
| 166 | required property int index | ||
| 167 | property int month: index | ||
| 168 | |||
| 169 | id: monthCalendar | ||
| 170 | |||
| 171 | Layout.alignment: Qt.AlignTop | Qt.AlignRight | ||
| 172 | Layout.fillWidth: false | ||
| 173 | |||
| 174 | Text { | ||
| 175 | Layout.column: 1 | ||
| 176 | Layout.fillWidth: true | ||
| 177 | |||
| 178 | horizontalAlignment: Text.AlignHCenter | ||
| 179 | |||
| 180 | font.pointSize: 10 | ||
| 181 | font.family: "Fira Sans" | ||
| 182 | |||
| 183 | text: new Date(yearCalendar.year, monthCalendar.month, 1).toLocaleString(Qt.locale("en_DK"), "MMMM") | ||
| 184 | |||
| 185 | color: "#ffead3" | ||
| 186 | } | ||
| 187 | |||
| 188 | DayOfWeekRow { | ||
| 189 | locale: grid.locale | ||
| 190 | |||
| 191 | Layout.row: 1 | ||
| 192 | Layout.column: 1 | ||
| 193 | Layout.fillWidth: true | ||
| 194 | |||
| 195 | delegate: WrapperItem { | ||
| 196 | required property string shortName | ||
| 197 | |||
| 198 | width: dowLabel.contentWidth + 6 | ||
| 199 | |||
| 200 | Text { | ||
| 201 | id: dowLabel | ||
| 202 | |||
| 203 | anchors.fill: parent | ||
| 204 | |||
| 205 | font.pointSize: 10 | ||
| 206 | font.family: "Fira Sans" | ||
| 207 | |||
| 208 | text: parent.shortName | ||
| 209 | color: "#ffcc66" | ||
| 210 | |||
| 211 | horizontalAlignment: Text.AlignHCenter | ||
| 212 | verticalAlignment: Text.AlignVCenter | ||
| 213 | } | ||
| 214 | } | ||
| 215 | } | ||
| 216 | |||
| 217 | WeekNumberColumn { | ||
| 218 | month: grid.month | ||
| 219 | year: grid.year | ||
| 220 | locale: grid.locale | ||
| 221 | |||
| 222 | Layout.fillHeight: true | ||
| 223 | |||
| 224 | delegate: Text { | ||
| 225 | required property int weekNumber | ||
| 226 | |||
| 227 | opacity: { | ||
| 228 | const simple = new Date(weekNumber == 1 && monthCalendar.month == 12 ? yearCalendar.year + 1 : yearCalendar.year, 0, 1 + (weekNumber - 1) * 7); | ||
| 229 | const dayOfWeek = simple.getDay(); | ||
| 230 | const isoWeekStart = simple; | ||
| 231 | |||
| 232 | isoWeekStart.setDate(simple.getDate() - dayOfWeek + 1); | ||
| 233 | if (dayOfWeek > 4) { | ||
| 234 | isoWeekStart.setDate(isoWeekStart.getDate() + 7); | ||
| 235 | } | ||
| 236 | |||
| 237 | for (let i = 0; i < 7; i++) { | ||
| 238 | const dayInWeek = new Date(isoWeekStart); | ||
| 239 | dayInWeek.setDate(dayInWeek.getDate() + i); | ||
| 240 | if (dayInWeek.getMonth() == monthCalendar.month) | ||
| 241 | return 1; | ||
| 242 | } | ||
| 243 | |||
| 244 | return 0; | ||
| 245 | } | ||
| 246 | |||
| 247 | font.pointSize: 10 | ||
| 248 | font.family: "Fira Sans" | ||
| 249 | font.features: { "tnum": 1 } | ||
| 250 | |||
| 251 | text: weekNumber | ||
| 252 | color: "#99ffdd" | ||
| 253 | |||
| 254 | horizontalAlignment: Text.AlignRight | ||
| 255 | verticalAlignment: Text.AlignVCenter | ||
| 256 | } | ||
| 257 | } | ||
| 258 | |||
| 259 | MonthGrid { | ||
| 260 | id: grid | ||
| 261 | |||
| 262 | year: yearCalendar.year | ||
| 263 | month: monthCalendar.month | ||
| 264 | locale: Qt.locale("en_DK") | ||
| 265 | |||
| 266 | Layout.fillWidth: true | ||
| 267 | Layout.fillHeight: true | ||
| 268 | |||
| 269 | delegate: Text { | ||
| 270 | required property var model | ||
| 271 | |||
| 272 | opacity: model.month === monthCalendar.month ? 1 : 0 | ||
| 273 | |||
| 274 | font.pointSize: 10 | ||
| 275 | font.family: "Fira Sans" | ||
| 276 | font.features: { "tnum": 1 } | ||
| 277 | |||
| 278 | property bool today: chrono.date.getFullYear() == model.year && chrono.date.getMonth() == model.month && chrono.date.getDate() == model.day | ||
| 279 | |||
| 280 | text: model.day | ||
| 281 | color: today ? "#ff6699" : "white" | ||
| 282 | |||
| 283 | horizontalAlignment: Text.AlignRight | ||
| 284 | verticalAlignment: Text.AlignVCenter | ||
| 285 | } | ||
| 286 | } | ||
| 287 | } | ||
| 288 | } | ||
| 289 | } | ||
| 290 | } | ||
| 291 | } | ||
| 292 | } | ||
| 293 | } | ||
| 294 | } | ||
| 295 | } | ||
diff --git a/accounts/gkleen@sif/shell/quickshell/KeyboardLayout.qml b/accounts/gkleen@sif/shell/quickshell/KeyboardLayout.qml new file mode 100644 index 00000000..46302e54 --- /dev/null +++ b/accounts/gkleen@sif/shell/quickshell/KeyboardLayout.qml | |||
| @@ -0,0 +1,112 @@ | |||
| 1 | import Quickshell | ||
| 2 | import QtQuick | ||
| 3 | import qs.Services | ||
| 4 | import Quickshell.Widgets | ||
| 5 | |||
| 6 | Item { | ||
| 7 | width: kbdLabel.contentWidth + 8 | ||
| 8 | height: parent.height | ||
| 9 | anchors.verticalCenter: parent.verticalCenter | ||
| 10 | |||
| 11 | WrapperMouseArea { | ||
| 12 | id: kbdMouseArea | ||
| 13 | |||
| 14 | anchors.fill: parent | ||
| 15 | |||
| 16 | hoverEnabled: true | ||
| 17 | cursorShape: Qt.PointingHandCursor | ||
| 18 | enabled: true | ||
| 19 | onClicked: { | ||
| 20 | NiriService.sendCommand({ "Action": { "SwitchLayout": { "layout": "Next" } } }, _ => {}) | ||
| 21 | } | ||
| 22 | onWheel: event => { | ||
| 23 | NiriService.sendCommand({ "Action": { "SwitchLayout": { "layout": event.angleDelta > 0 ? "Next" : "Prev" } } }, _ => {}) | ||
| 24 | } | ||
| 25 | |||
| 26 | Rectangle { | ||
| 27 | id: kbdWidget | ||
| 28 | |||
| 29 | property var keyboardAbbrev: { "English (programmer Dvorak)": "dvp", "English (US)": "us" } | ||
| 30 | |||
| 31 | anchors.fill: parent | ||
| 32 | color: { | ||
| 33 | if (kbdMouseArea.containsMouse) { | ||
| 34 | return "#33808080"; | ||
| 35 | } | ||
| 36 | return "transparent"; | ||
| 37 | } | ||
| 38 | |||
| 39 | Text { | ||
| 40 | id: kbdLabel | ||
| 41 | |||
| 42 | font.pointSize: 10 | ||
| 43 | font.family: "Fira Sans" | ||
| 44 | color: { | ||
| 45 | if (NiriService.keyboardLayouts?.current_idx === 0) | ||
| 46 | return "#555"; | ||
| 47 | return "white"; | ||
| 48 | } | ||
| 49 | anchors.centerIn: parent | ||
| 50 | |||
| 51 | text: { | ||
| 52 | const currentLayout = NiriService.keyboardLayouts?.names?.[NiriService.keyboardLayouts.current_idx]; | ||
| 53 | if (!currentLayout) | ||
| 54 | return ""; | ||
| 55 | return kbdWidget.keyboardAbbrev[currentLayout] ? kbdWidget.keyboardAbbrev[currentLayout] : currentLayout; | ||
| 56 | } | ||
| 57 | } | ||
| 58 | } | ||
| 59 | } | ||
| 60 | |||
| 61 | PopupWindow { | ||
| 62 | id: tooltip | ||
| 63 | |||
| 64 | property bool nextVisible: kbdMouseArea.containsMouse || tooltipMouseArea.containsMouse | ||
| 65 | |||
| 66 | anchor { | ||
| 67 | item: kbdMouseArea | ||
| 68 | edges: Edges.Bottom | Edges.Left | ||
| 69 | } | ||
| 70 | visible: false | ||
| 71 | |||
| 72 | onNextVisibleChanged: hangTimer.restart() | ||
| 73 | |||
| 74 | Timer { | ||
| 75 | id: hangTimer | ||
| 76 | interval: 100 | ||
| 77 | onTriggered: tooltip.visible = tooltip.nextVisible | ||
| 78 | } | ||
| 79 | |||
| 80 | implicitWidth: kbdTooltipText.contentWidth + 16 | ||
| 81 | implicitHeight: kbdTooltipText.contentHeight + 16 | ||
| 82 | color: "black" | ||
| 83 | |||
| 84 | WrapperMouseArea { | ||
| 85 | id: tooltipMouseArea | ||
| 86 | |||
| 87 | hoverEnabled: true | ||
| 88 | enabled: true | ||
| 89 | |||
| 90 | anchors.fill: parent | ||
| 91 | |||
| 92 | Item { | ||
| 93 | anchors.fill: parent | ||
| 94 | |||
| 95 | Text { | ||
| 96 | id: kbdTooltipText | ||
| 97 | |||
| 98 | anchors.centerIn: parent | ||
| 99 | |||
| 100 | font.pointSize: 10 | ||
| 101 | font.family: "Fira Sans" | ||
| 102 | color: "white" | ||
| 103 | |||
| 104 | text: { | ||
| 105 | const currentLayout = NiriService.keyboardLayouts?.names?.[NiriService.keyboardLayouts.current_idx]; | ||
| 106 | return currentLayout || ""; | ||
| 107 | } | ||
| 108 | } | ||
| 109 | } | ||
| 110 | } | ||
| 111 | } | ||
| 112 | } | ||
diff --git a/accounts/gkleen@sif/shell/quickshell/LidSwitchInhibitorWidget.qml b/accounts/gkleen@sif/shell/quickshell/LidSwitchInhibitorWidget.qml new file mode 100644 index 00000000..8410dcda --- /dev/null +++ b/accounts/gkleen@sif/shell/quickshell/LidSwitchInhibitorWidget.qml | |||
| @@ -0,0 +1,47 @@ | |||
| 1 | import Quickshell | ||
| 2 | import QtQuick | ||
| 3 | import Quickshell.Widgets | ||
| 4 | import qs.Services | ||
| 5 | |||
| 6 | Item { | ||
| 7 | id: root | ||
| 8 | |||
| 9 | width: icon.width + 8 | ||
| 10 | height: parent.height | ||
| 11 | anchors.verticalCenter: parent.verticalCenter | ||
| 12 | |||
| 13 | WrapperMouseArea { | ||
| 14 | id: widgetMouseArea | ||
| 15 | |||
| 16 | anchors.fill: parent | ||
| 17 | |||
| 18 | hoverEnabled: true | ||
| 19 | cursorShape: Qt.PointingHandCursor | ||
| 20 | |||
| 21 | onClicked: InhibitorState.lidSwitchInhibited = !InhibitorState.lidSwitchInhibited | ||
| 22 | |||
| 23 | Rectangle { | ||
| 24 | anchors.fill: parent | ||
| 25 | color: { | ||
| 26 | if (widgetMouseArea.containsMouse) { | ||
| 27 | return "#33808080"; | ||
| 28 | } | ||
| 29 | return "transparent"; | ||
| 30 | } | ||
| 31 | |||
| 32 | Item { | ||
| 33 | anchors.fill: parent | ||
| 34 | |||
| 35 | MaterialDesignIcon { | ||
| 36 | id: icon | ||
| 37 | |||
| 38 | implicitSize: 14 | ||
| 39 | anchors.centerIn: parent | ||
| 40 | |||
| 41 | icon: InhibitorState.lidSwitchInhibited ? "laptop-off" : "laptop" | ||
| 42 | color: InhibitorState.lidSwitchInhibited ? "#f28a21" : "#555" | ||
| 43 | } | ||
| 44 | } | ||
| 45 | } | ||
| 46 | } | ||
| 47 | } | ||
diff --git a/accounts/gkleen@sif/shell/quickshell/LockSurface.qml b/accounts/gkleen@sif/shell/quickshell/LockSurface.qml new file mode 100644 index 00000000..f4f8f0cd --- /dev/null +++ b/accounts/gkleen@sif/shell/quickshell/LockSurface.qml | |||
| @@ -0,0 +1,227 @@ | |||
| 1 | import Quickshell.Widgets | ||
| 2 | import QtQuick.Effects | ||
| 3 | import QtQuick.Layouts | ||
| 4 | import QtQuick | ||
| 5 | import QtQuick.Controls | ||
| 6 | import QtQuick.Controls.Fusion | ||
| 7 | import qs.Services | ||
| 8 | import QtQml | ||
| 9 | |||
| 10 | Item { | ||
| 11 | id: lockSurface | ||
| 12 | |||
| 13 | property var screen | ||
| 14 | property list<var> messages: [] | ||
| 15 | property bool responseRequired: false | ||
| 16 | property bool responseVisible: false | ||
| 17 | property string currentText: "" | ||
| 18 | property bool authRunning: false | ||
| 19 | |||
| 20 | signal response(string responseText) | ||
| 21 | |||
| 22 | anchors.fill: parent | ||
| 23 | |||
| 24 | Item { | ||
| 25 | id: background | ||
| 26 | |||
| 27 | anchors.fill: parent | ||
| 28 | |||
| 29 | property Img current: one | ||
| 30 | property string source: selector.selected | ||
| 31 | |||
| 32 | WallpaperSelector { | ||
| 33 | id: selector | ||
| 34 | seed: lockSurface.screen?.name || "" | ||
| 35 | } | ||
| 36 | |||
| 37 | onSourceChanged: { | ||
| 38 | if (!source) | ||
| 39 | current = null; | ||
| 40 | else if (current === one) | ||
| 41 | two.update() | ||
| 42 | else | ||
| 43 | one.update() | ||
| 44 | } | ||
| 45 | |||
| 46 | Img { id: one } | ||
| 47 | Img { id: two } | ||
| 48 | |||
| 49 | component Img: Item { | ||
| 50 | id: img | ||
| 51 | |||
| 52 | property string source | ||
| 53 | |||
| 54 | function update() { | ||
| 55 | source = background.source || "" | ||
| 56 | } | ||
| 57 | |||
| 58 | anchors.fill: parent | ||
| 59 | |||
| 60 | Image { | ||
| 61 | id: imageSource | ||
| 62 | |||
| 63 | source: img.source | ||
| 64 | sourceSize: Qt.size(parent.width, parent.height) | ||
| 65 | fillMode: Image.PreserveAspectCrop | ||
| 66 | smooth: true | ||
| 67 | visible: false | ||
| 68 | asynchronous: true | ||
| 69 | cache: false | ||
| 70 | |||
| 71 | onStatusChanged: { | ||
| 72 | if (status === Image.Ready) { | ||
| 73 | background.current = img | ||
| 74 | } | ||
| 75 | } | ||
| 76 | } | ||
| 77 | |||
| 78 | MultiEffect { | ||
| 79 | id: imageEffect | ||
| 80 | |||
| 81 | source: imageSource | ||
| 82 | anchors.fill: parent | ||
| 83 | blurEnabled: true | ||
| 84 | blur: 1 | ||
| 85 | blurMax: 64 | ||
| 86 | blurMultiplier: 2 | ||
| 87 | |||
| 88 | opacity: 0 | ||
| 89 | |||
| 90 | states: State { | ||
| 91 | name: "visible" | ||
| 92 | when: background.current === img | ||
| 93 | |||
| 94 | PropertyChanges { | ||
| 95 | imageEffect.opacity: 1 | ||
| 96 | } | ||
| 97 | StateChangeScript { | ||
| 98 | name: "unloadOther" | ||
| 99 | script: { | ||
| 100 | if (img === one) | ||
| 101 | two.source = "" | ||
| 102 | if (img === two) | ||
| 103 | one.source = "" | ||
| 104 | } | ||
| 105 | } | ||
| 106 | } | ||
| 107 | |||
| 108 | transitions: Transition { | ||
| 109 | SequentialAnimation { | ||
| 110 | NumberAnimation { | ||
| 111 | target: imageEffect | ||
| 112 | properties: "opacity" | ||
| 113 | duration: { | ||
| 114 | if (img === one && two.source == "" || img === two && one.source == "") | ||
| 115 | return 0; | ||
| 116 | return 5000; | ||
| 117 | } | ||
| 118 | easing.type: Easing.OutCubic | ||
| 119 | } | ||
| 120 | ScriptAction { | ||
| 121 | scriptName: "unloadOther" | ||
| 122 | } | ||
| 123 | } | ||
| 124 | } | ||
| 125 | } | ||
| 126 | } | ||
| 127 | } | ||
| 128 | |||
| 129 | Item { | ||
| 130 | anchors { | ||
| 131 | top: lockSurface.top | ||
| 132 | left: lockSurface.left | ||
| 133 | right: lockSurface.right | ||
| 134 | } | ||
| 135 | |||
| 136 | implicitWidth: lockSurface.width | ||
| 137 | implicitHeight: 21 | ||
| 138 | |||
| 139 | Rectangle { | ||
| 140 | anchors.fill: parent | ||
| 141 | color: Qt.rgba(0, 0, 0, 0.75) | ||
| 142 | } | ||
| 143 | |||
| 144 | Clock { | ||
| 145 | anchors.centerIn: parent | ||
| 146 | calendarPopup: false | ||
| 147 | } | ||
| 148 | } | ||
| 149 | |||
| 150 | WrapperRectangle { | ||
| 151 | id: unlockUi | ||
| 152 | |||
| 153 | Keys.onPressed: event => { | ||
| 154 | if (!lockSurface.authRunning) { | ||
| 155 | event.accepted = true; | ||
| 156 | lockSurface.authRunning = true; | ||
| 157 | } | ||
| 158 | } | ||
| 159 | focus: !passwordBox.visible | ||
| 160 | |||
| 161 | visible: lockSurface.authRunning | ||
| 162 | |||
| 163 | color: Qt.rgba(0, 0, 0, 0.75) | ||
| 164 | margin: 8 | ||
| 165 | |||
| 166 | anchors.centerIn: parent | ||
| 167 | |||
| 168 | ColumnLayout { | ||
| 169 | spacing: 4 | ||
| 170 | |||
| 171 | BusyIndicator { | ||
| 172 | visible: running | ||
| 173 | running: !Array.from(lockSurface.messages).length && !lockSurface.responseRequired | ||
| 174 | } | ||
| 175 | |||
| 176 | Repeater { | ||
| 177 | model: lockSurface.messages | ||
| 178 | |||
| 179 | Text { | ||
| 180 | required property var modelData | ||
| 181 | |||
| 182 | font.pointSize: 10 | ||
| 183 | font.family: "Fira Sans" | ||
| 184 | color: modelData.error ? "#f28a21" : "#ffffff" | ||
| 185 | |||
| 186 | text: String(modelData.text).trim() | ||
| 187 | |||
| 188 | Layout.fillWidth: true | ||
| 189 | horizontalAlignment: Text.AlignHCenter | ||
| 190 | } | ||
| 191 | } | ||
| 192 | |||
| 193 | TextField { | ||
| 194 | id: passwordBox | ||
| 195 | |||
| 196 | visible: lockSurface.responseRequired | ||
| 197 | echoMode: lockSurface.responseVisible ? TextInput.Normal : TextInput.Password | ||
| 198 | inputMethodHints: Qt.ImhSensitiveData | ||
| 199 | |||
| 200 | onTextChanged: lockSurface.currentText = passwordBox.text | ||
| 201 | onAccepted: { | ||
| 202 | passwordBox.readOnly = true; | ||
| 203 | lockSurface.response(lockSurface.currentText); | ||
| 204 | } | ||
| 205 | |||
| 206 | Connections { | ||
| 207 | target: lockSurface | ||
| 208 | function onCurrentTextChanged() { | ||
| 209 | passwordBox.text = lockSurface.currentText | ||
| 210 | } | ||
| 211 | } | ||
| 212 | Connections { | ||
| 213 | target: lockSurface | ||
| 214 | function onResponseRequiredChanged() { | ||
| 215 | if (lockSurface.responseRequired) | ||
| 216 | passwordBox.readOnly = false; | ||
| 217 | passwordBox.focus = true; | ||
| 218 | passwordBox.selectAll(); | ||
| 219 | } | ||
| 220 | } | ||
| 221 | |||
| 222 | Layout.topMargin: 4 | ||
| 223 | Layout.fillWidth: true | ||
| 224 | } | ||
| 225 | } | ||
| 226 | } | ||
| 227 | } | ||
diff --git a/accounts/gkleen@sif/shell/quickshell/Lockscreen.qml b/accounts/gkleen@sif/shell/quickshell/Lockscreen.qml new file mode 100644 index 00000000..996fd41b --- /dev/null +++ b/accounts/gkleen@sif/shell/quickshell/Lockscreen.qml | |||
| @@ -0,0 +1,132 @@ | |||
| 1 | import Quickshell | ||
| 2 | import Quickshell.Wayland | ||
| 3 | import Quickshell.Io | ||
| 4 | import Quickshell.Services.Pam | ||
| 5 | import Quickshell.Services.Mpris | ||
| 6 | import Custom as Custom | ||
| 7 | import qs.Services | ||
| 8 | import QtQml | ||
| 9 | |||
| 10 | Scope { | ||
| 11 | id: lockscreen | ||
| 12 | |||
| 13 | property string currentText: "" | ||
| 14 | |||
| 15 | PamContext { | ||
| 16 | id: pam | ||
| 17 | |||
| 18 | property list<var> messages: [] | ||
| 19 | |||
| 20 | config: "quickshell" | ||
| 21 | onCompleted: result => { | ||
| 22 | if (result === PamResult.Success) { | ||
| 23 | lock.locked = false; | ||
| 24 | } | ||
| 25 | } | ||
| 26 | onPamMessage: { | ||
| 27 | messages = Array.from(messages).concat([{ "text": pam.message, "error": pam.messageIsError }]) | ||
| 28 | } | ||
| 29 | onActiveChanged: { | ||
| 30 | messages = []; | ||
| 31 | } | ||
| 32 | } | ||
| 33 | |||
| 34 | IpcHandler { | ||
| 35 | target: "Lockscreen" | ||
| 36 | |||
| 37 | function setLocked(locked: bool): void { lock.locked = locked; } | ||
| 38 | function getLocked(): bool { return lock.locked; } | ||
| 39 | } | ||
| 40 | |||
| 41 | Connections { | ||
| 42 | target: Custom.Systemd | ||
| 43 | function onSleep(before: bool) { | ||
| 44 | console.log(`received prepare for sleep ${before}`); | ||
| 45 | if (before) | ||
| 46 | lock.locked = true; | ||
| 47 | } | ||
| 48 | function onLock() { lock.locked = true; } | ||
| 49 | function onUnlock() { lock.locked = false; } | ||
| 50 | } | ||
| 51 | |||
| 52 | IdleMonitor { | ||
| 53 | id: idleMonitor | ||
| 54 | enabled: !lock.secure | ||
| 55 | timeout: 600 | ||
| 56 | respectInhibitors: true | ||
| 57 | |||
| 58 | onIsIdleChanged: { | ||
| 59 | if (idleMonitor.isIdle) | ||
| 60 | lock.locked = true; | ||
| 61 | } | ||
| 62 | } | ||
| 63 | |||
| 64 | Custom.SystemdInhibitor { | ||
| 65 | enabled: !lock.secure | ||
| 66 | |||
| 67 | what: Custom.SystemdInhibitorParams.Sleep | ||
| 68 | who: "quickshell" | ||
| 69 | why: "Lock session" | ||
| 70 | mode: Custom.SystemdInhibitorParams.Delay | ||
| 71 | } | ||
| 72 | |||
| 73 | Binding { | ||
| 74 | target: NotificationManager | ||
| 75 | property: "lockscreenActive" | ||
| 76 | value: lock.locked | ||
| 77 | } | ||
| 78 | |||
| 79 | WlSessionLock { | ||
| 80 | id: lock | ||
| 81 | |||
| 82 | onLockStateChanged: { | ||
| 83 | if (!locked && pam.active) | ||
| 84 | pam.abort(); | ||
| 85 | |||
| 86 | if (locked) { | ||
| 87 | NiriService.sendCommand({ "Action": { "PowerOffMonitors": {} } }, _ => {}); | ||
| 88 | Custom.KeePassXC.lockAllDatabases(); | ||
| 89 | Array.from(MprisProxy.players).forEach(player => { | ||
| 90 | if (player.canPause && player.isPlaying) | ||
| 91 | player.pause(); | ||
| 92 | }); | ||
| 93 | // Custom.Systemd.stopUserUnit("gpg-agent.service", "replace"); | ||
| 94 | GpgAgent.reloadAgent(); | ||
| 95 | } | ||
| 96 | } | ||
| 97 | Component.onCompleted: { (_ => {})(MprisProxy.players); } | ||
| 98 | |||
| 99 | onSecureStateChanged: Custom.Systemd.lockedHint = lock.secure | ||
| 100 | |||
| 101 | WlSessionLockSurface { | ||
| 102 | id: lockSurface | ||
| 103 | |||
| 104 | color: "black" | ||
| 105 | |||
| 106 | LockSurface { | ||
| 107 | id: surfaceContent | ||
| 108 | |||
| 109 | onResponse: responseText => pam.respond(responseText) | ||
| 110 | onAuthRunningChanged: { | ||
| 111 | if (authRunning) | ||
| 112 | pam.start(); | ||
| 113 | } | ||
| 114 | Connections { | ||
| 115 | target: pam | ||
| 116 | function onMessagesChanged() { surfaceContent.messages = pam.messages; } | ||
| 117 | function onResponseRequiredChanged() { surfaceContent.responseRequired = pam.responseRequired; } | ||
| 118 | function onActiveChanged() { surfaceContent.authRunning = pam.active; } | ||
| 119 | } | ||
| 120 | onCurrentTextChanged: lockscreen.currentText = currentText | ||
| 121 | Connections { | ||
| 122 | target: lockscreen | ||
| 123 | function onCurrentTextChanged() { surfaceContent.currentText = lockscreen.currentText; } | ||
| 124 | } | ||
| 125 | Connections { | ||
| 126 | target: lockSurface | ||
| 127 | function onScreenChanged() { surfaceContent.screen = lockSurface.screen; } | ||
| 128 | } | ||
| 129 | } | ||
| 130 | } | ||
| 131 | } | ||
| 132 | } | ||
diff --git a/accounts/gkleen@sif/shell/quickshell/MaterialDesignIcon.qml b/accounts/gkleen@sif/shell/quickshell/MaterialDesignIcon.qml new file mode 100644 index 00000000..155a009e --- /dev/null +++ b/accounts/gkleen@sif/shell/quickshell/MaterialDesignIcon.qml | |||
| @@ -0,0 +1,35 @@ | |||
| 1 | import QtQuick | ||
| 2 | import QtQuick.Effects | ||
| 3 | |||
| 4 | Item { | ||
| 5 | id: root | ||
| 6 | |||
| 7 | required property string icon | ||
| 8 | property color color: "white" | ||
| 9 | |||
| 10 | property real implicitSize: 0 | ||
| 11 | |||
| 12 | readonly property real actualSize: Math.min(root.width, root.height) | ||
| 13 | |||
| 14 | implicitWidth: root.implicitSize | ||
| 15 | implicitHeight: root.implicitSize | ||
| 16 | |||
| 17 | Image { | ||
| 18 | id: sourceImage | ||
| 19 | source: "file://" + @mdi@ + "/svg/" + root.icon + ".svg" | ||
| 20 | anchors.fill: parent | ||
| 21 | fillMode: Image.PreserveAspectFit | ||
| 22 | |||
| 23 | sourceSize.width: root.actualSize | ||
| 24 | sourceSize.height: root.actualSize | ||
| 25 | |||
| 26 | layer.enabled: true | ||
| 27 | layer.effect: MultiEffect { | ||
| 28 | id: effect | ||
| 29 | |||
| 30 | brightness: 1 | ||
| 31 | colorization: 1 | ||
| 32 | colorizationColor: root.color | ||
| 33 | } | ||
| 34 | } | ||
| 35 | } | ||
diff --git a/accounts/gkleen@sif/shell/quickshell/NiriIdle.qml b/accounts/gkleen@sif/shell/quickshell/NiriIdle.qml new file mode 100644 index 00000000..beff205c --- /dev/null +++ b/accounts/gkleen@sif/shell/quickshell/NiriIdle.qml | |||
| @@ -0,0 +1,30 @@ | |||
| 1 | import QtQml | ||
| 2 | import Quickshell | ||
| 3 | import Quickshell.Wayland | ||
| 4 | import qs.Services | ||
| 5 | import Custom as Custom | ||
| 6 | |||
| 7 | Scope { | ||
| 8 | IdleMonitor { | ||
| 9 | id: idleMonitor30 | ||
| 10 | timeout: 30 | ||
| 11 | |||
| 12 | onIsIdleChanged: Custom.Systemd.setIdleHint(idleMonitor30.isIdle) | ||
| 13 | } | ||
| 14 | IdleMonitor { | ||
| 15 | id: idleMonitor540 | ||
| 16 | timeout: 540 | ||
| 17 | |||
| 18 | onIsIdleChanged: { | ||
| 19 | if (idleMonitor540.isIdle) | ||
| 20 | NiriService.sendCommand({ "Action": { "PowerOffMonitors": {} } }, _ => {}); | ||
| 21 | } | ||
| 22 | } | ||
| 23 | Connections { | ||
| 24 | target: Custom.Systemd | ||
| 25 | function onSleep(before: bool) { | ||
| 26 | if (!before) | ||
| 27 | NiriService.sendCommand({ "Action": { "PowerOnMonitors": {} } }, _ => {}); | ||
| 28 | } | ||
| 29 | } | ||
| 30 | } | ||
diff --git a/accounts/gkleen@sif/shell/quickshell/NotificationDisplay.qml b/accounts/gkleen@sif/shell/quickshell/NotificationDisplay.qml new file mode 100644 index 00000000..cc0e49b1 --- /dev/null +++ b/accounts/gkleen@sif/shell/quickshell/NotificationDisplay.qml | |||
| @@ -0,0 +1,340 @@ | |||
| 1 | import QtQml | ||
| 2 | import QtQml.Models | ||
| 3 | import QtQuick | ||
| 4 | import Quickshell | ||
| 5 | import Quickshell.Widgets | ||
| 6 | import Quickshell.Wayland | ||
| 7 | import qs.Services | ||
| 8 | import QtQuick.Layouts | ||
| 9 | import Quickshell.Services.Notifications | ||
| 10 | |||
| 11 | Scope { | ||
| 12 | id: root | ||
| 13 | |||
| 14 | property var activeScreen: Array.from(Quickshell.screens).find(screen => screen.name === Array.from(NiriService.workspaces).find(ws => ws.is_focused)?.output) ?? null | ||
| 15 | |||
| 16 | Instantiator { | ||
| 17 | id: notifsRepeater | ||
| 18 | |||
| 19 | model: ScriptModel { | ||
| 20 | values: NotificationManager.groups | ||
| 21 | } | ||
| 22 | |||
| 23 | delegate: PanelWindow { | ||
| 24 | id: notifWindow | ||
| 25 | |||
| 26 | visible: NotificationManager.active | ||
| 27 | |||
| 28 | screen: root.activeScreen | ||
| 29 | |||
| 30 | WlrLayershell.namespace: "notifications" | ||
| 31 | |||
| 32 | required property var modelData | ||
| 33 | required property var index | ||
| 34 | |||
| 35 | property int activeIx: modelData.length - 1 | ||
| 36 | onModelDataChanged: { | ||
| 37 | notifWindow.activeIx = modelData.length - 1; | ||
| 38 | } | ||
| 39 | |||
| 40 | property color textColor: { | ||
| 41 | if (notifWindow.modelData?.[notifWindow.activeIx]?.urgency == NotificationUrgency.Low) | ||
| 42 | return "#ff999999"; | ||
| 43 | return "white"; | ||
| 44 | } | ||
| 45 | property color backgroundColor: { | ||
| 46 | if (notifWindow.modelData?.[notifWindow.activeIx]?.urgency == NotificationUrgency.Critical) | ||
| 47 | return "#dd900000"; | ||
| 48 | return "black"; | ||
| 49 | } | ||
| 50 | |||
| 51 | anchors { | ||
| 52 | right: true | ||
| 53 | top: true | ||
| 54 | } | ||
| 55 | |||
| 56 | readonly property real spaceAbove: { | ||
| 57 | var res = 0; | ||
| 58 | for (let i = 0; i < notifWindow.index; i++) { | ||
| 59 | (_ => {})(notifsRepeater.objectAt(i).modelData); | ||
| 60 | res += notifsRepeater.objectAt(i).height + 8; | ||
| 61 | } | ||
| 62 | return res; | ||
| 63 | } | ||
| 64 | |||
| 65 | margins { | ||
| 66 | right: 26 + 8 | ||
| 67 | top: 8 + spaceAbove | ||
| 68 | } | ||
| 69 | |||
| 70 | color: "transparent" | ||
| 71 | |||
| 72 | implicitHeight: Math.max(notifCount.visible ? notifCount.contentHeight : 0, notifSummary.contentHeight) + (notifBody.visible ? 8 + notifBody.contentHeight : 0) + (notifActions.visible ? 8 + notifActions.height : 0) + (notifTime.visible ? 8 + notifTime.contentHeight : 0) + 16 | ||
| 73 | implicitWidth: 400 | ||
| 74 | |||
| 75 | WrapperMouseArea { | ||
| 76 | enabled: true | ||
| 77 | |||
| 78 | anchors.fill: parent | ||
| 79 | |||
| 80 | cursorShape: Qt.PointingHandCursor | ||
| 81 | |||
| 82 | onClicked: { | ||
| 83 | for (const notif of notifWindow.modelData) | ||
| 84 | notif.dismiss(); | ||
| 85 | } | ||
| 86 | |||
| 87 | property real angleRem: 0 | ||
| 88 | property real sensitivity: 1 / 120 | ||
| 89 | onWheel: event => { | ||
| 90 | angleRem += event.angleDelta.y; | ||
| 91 | const d = Math.round(angleRem * sensitivity); | ||
| 92 | angleRem -= d / sensitivity; | ||
| 93 | notifWindow.activeIx = ((notifWindow.modelData?.length ?? 1) + notifWindow.activeIx - d) % (notifWindow.modelData?.length ?? 1); | ||
| 94 | } | ||
| 95 | |||
| 96 | Rectangle { | ||
| 97 | color: notifWindow.backgroundColor | ||
| 98 | anchors.fill: parent | ||
| 99 | border { | ||
| 100 | color: Qt.hsla(195/360, 1, 0.45, 1) | ||
| 101 | width: 2 | ||
| 102 | } | ||
| 103 | |||
| 104 | GridLayout { | ||
| 105 | id: notifLayout | ||
| 106 | |||
| 107 | width: 400 - 16 | ||
| 108 | anchors.fill: parent | ||
| 109 | anchors.margins: 8 | ||
| 110 | columnSpacing: 8 | ||
| 111 | rowSpacing: 8 | ||
| 112 | |||
| 113 | columns: notifImage.visible ? 3 : 2 | ||
| 114 | rows: { | ||
| 115 | var res = 1; | ||
| 116 | if (notifBody.visible) | ||
| 117 | res += 1; | ||
| 118 | if (notifActions.visible) | ||
| 119 | res += 1; | ||
| 120 | if (notifTime.visible) | ||
| 121 | res += 1; | ||
| 122 | return res; | ||
| 123 | } | ||
| 124 | |||
| 125 | Text { | ||
| 126 | id: notifCount | ||
| 127 | |||
| 128 | visible: notifWindow.modelData?.length > 1 ?? false | ||
| 129 | text: `${notifWindow.activeIx + 1}/${notifWindow.modelData?.length ?? ""}` | ||
| 130 | |||
| 131 | font.pointSize: 10 | ||
| 132 | font.family: "Fira Sans" | ||
| 133 | font.bold: true | ||
| 134 | font.features: { "tnum": 1 } | ||
| 135 | color: notifWindow.textColor | ||
| 136 | maximumLineCount: 1 | ||
| 137 | |||
| 138 | Layout.fillWidth: false | ||
| 139 | Layout.row: 0 | ||
| 140 | Layout.column: notifImage.visible ? 1 : 0 | ||
| 141 | } | ||
| 142 | |||
| 143 | Text { | ||
| 144 | id: notifSummary | ||
| 145 | |||
| 146 | text: notifWindow.modelData?.[notifWindow.activeIx]?.summary ?? "" | ||
| 147 | |||
| 148 | font.pointSize: 10 | ||
| 149 | font.family: "Fira Sans" | ||
| 150 | font.italic: true | ||
| 151 | color: notifWindow.textColor | ||
| 152 | maximumLineCount: 1 | ||
| 153 | elide: Text.ElideRight | ||
| 154 | |||
| 155 | Layout.fillWidth: true | ||
| 156 | Layout.row: 0 | ||
| 157 | Layout.column: (notifCount.visible ? 1 : 0) + (notifImage.visible ? 1 : 0) | ||
| 158 | Layout.columnSpan: notifCount.visible ? 1 : 2 | ||
| 159 | } | ||
| 160 | |||
| 161 | Image { | ||
| 162 | id: notifImage | ||
| 163 | |||
| 164 | visible: (notifWindow.modelData?.[notifWindow.activeIx]?.image || notifWindow.modelData?.[notifWindow.activeIx]?.appIcon) ?? false | ||
| 165 | |||
| 166 | onStatusChanged: { | ||
| 167 | if (notifImage.status == Image.Error) | ||
| 168 | notifImage.visible = false; | ||
| 169 | } | ||
| 170 | |||
| 171 | source: (notifWindow.modelData?.[notifWindow.activeIx]?.image || notifWindow.modelData?.[notifWindow.activeIx]?.appIcon) ?? "" | ||
| 172 | fillMode: Image.PreserveAspectFit | ||
| 173 | asynchronous: true | ||
| 174 | smooth: true | ||
| 175 | mipmap: true | ||
| 176 | |||
| 177 | Layout.maximumWidth: 50 | ||
| 178 | Layout.column: 0 | ||
| 179 | Layout.row: 0 | ||
| 180 | Layout.fillHeight: true | ||
| 181 | Layout.rowSpan: 1 + (notifBody.visible ? 1 : 0) + (notifTime.visible ? 1 : 0) | ||
| 182 | } | ||
| 183 | |||
| 184 | Text { | ||
| 185 | id: notifBody | ||
| 186 | |||
| 187 | visible: notifWindow.modelData?.[notifWindow.activeIx]?.body ?? false | ||
| 188 | text: notifWindow.modelData?.[notifWindow.activeIx]?.body ?? "" | ||
| 189 | textFormat: Text.RichText | ||
| 190 | wrapMode: Text.Wrap | ||
| 191 | |||
| 192 | font.pointSize: 10 | ||
| 193 | font.family: "Fira Sans" | ||
| 194 | color: notifWindow.textColor | ||
| 195 | |||
| 196 | Layout.fillWidth: true | ||
| 197 | Layout.row: 1 | ||
| 198 | Layout.column: notifImage.visible ? 1 : 0 | ||
| 199 | Layout.columnSpan: notifCount.visible ? 2 : 1 | ||
| 200 | } | ||
| 201 | |||
| 202 | Text { | ||
| 203 | id: notifTime | ||
| 204 | |||
| 205 | Connections { | ||
| 206 | target: NotificationManager.clock | ||
| 207 | function onDateChanged() { | ||
| 208 | notifTime.text = NotificationManager.formatTime(notifWindow.modelData?.[notifWindow.activeIx]?.receivedTime); | ||
| 209 | } | ||
| 210 | } | ||
| 211 | |||
| 212 | visible: notifTime.text && notifTime.text !== "now" | ||
| 213 | text: NotificationManager.formatTime(notifWindow.modelData?.[notifWindow.activeIx]?.receivedTime) | ||
| 214 | |||
| 215 | font.pointSize: 8 | ||
| 216 | font.family: "Fira Sans" | ||
| 217 | font.italic: true | ||
| 218 | color: "#555" | ||
| 219 | maximumLineCount: 1 | ||
| 220 | horizontalAlignment: Text.AlignRight | ||
| 221 | |||
| 222 | Layout.fillWidth: true | ||
| 223 | Layout.row: notifBody.visible ? 2 : 1 | ||
| 224 | Layout.column: notifImage.visible ? 1 : 0 | ||
| 225 | Layout.columnSpan: 2 | ||
| 226 | } | ||
| 227 | |||
| 228 | RowLayout { | ||
| 229 | id: notifActions | ||
| 230 | |||
| 231 | visible: notifWindow.modelData?.[notifWindow.activeIx]?.actions.length > 0 ?? false | ||
| 232 | |||
| 233 | spacing: 8 | ||
| 234 | uniformCellSizes: true | ||
| 235 | |||
| 236 | width: 400 - 16 | ||
| 237 | Layout.row: 1 + (notifBody.visible ? 1 : 0) + (notifTime.visible ? 1 : 0) | ||
| 238 | Layout.column: 0 | ||
| 239 | Layout.columnSpan: 2 + (notifImage.visible ? 1 : 0) | ||
| 240 | |||
| 241 | Repeater { | ||
| 242 | model: ScriptModel { | ||
| 243 | values: notifWindow.modelData?.[notifWindow.activeIx]?.actions | ||
| 244 | } | ||
| 245 | |||
| 246 | delegate: WrapperMouseArea { | ||
| 247 | id: actionMouseArea | ||
| 248 | |||
| 249 | required property var modelData | ||
| 250 | |||
| 251 | height: actionLabelWrapper.implicitHeight | ||
| 252 | Layout.fillWidth: true | ||
| 253 | Layout.horizontalStretchFactor: 1 | ||
| 254 | |||
| 255 | hoverEnabled: true | ||
| 256 | cursorShape: Qt.PointingHandCursor | ||
| 257 | |||
| 258 | onClicked: actionMouseArea.modelData?.invoke() | ||
| 259 | |||
| 260 | Rectangle { | ||
| 261 | anchors.fill: parent | ||
| 262 | |||
| 263 | color: actionMouseArea.containsMouse ? "#20ffffff" : "transparent" | ||
| 264 | |||
| 265 | border { | ||
| 266 | width: 2 | ||
| 267 | color: "#20ffffff" | ||
| 268 | } | ||
| 269 | |||
| 270 | WrapperItem { | ||
| 271 | id: actionLabelWrapper | ||
| 272 | |||
| 273 | margin: 8 | ||
| 274 | anchors.centerIn: parent | ||
| 275 | |||
| 276 | RowLayout { | ||
| 277 | id: actionLabelLayout | ||
| 278 | |||
| 279 | spacing: 8 | ||
| 280 | |||
| 281 | IconImage { | ||
| 282 | id: actionIcon | ||
| 283 | |||
| 284 | visible: notifWindow.modelData?.[notifWindow.activeIx]?.hasActionIcons | ||
| 285 | |||
| 286 | onStatusChanged: { | ||
| 287 | if (actionIcon.status == Image.Error) | ||
| 288 | actionIcon.visible = false; | ||
| 289 | } | ||
| 290 | |||
| 291 | implicitSize: 16 | ||
| 292 | source: { | ||
| 293 | if (!actionIcon.visible) | ||
| 294 | return ""; | ||
| 295 | |||
| 296 | let icon = actionMouseArea.modelData?.identifier ?? "" | ||
| 297 | if (icon.includes("?path=")) { | ||
| 298 | const split = icon.split("?path=") | ||
| 299 | if (split.length !== 2) | ||
| 300 | return icon | ||
| 301 | const name = split[0] | ||
| 302 | const path = split[1] | ||
| 303 | const fileName = name.substring( | ||
| 304 | name.lastIndexOf("/") + 1) | ||
| 305 | return `file://${path}/${fileName}` | ||
| 306 | } | ||
| 307 | return icon | ||
| 308 | } | ||
| 309 | asynchronous: true | ||
| 310 | smooth: true | ||
| 311 | mipmap: true | ||
| 312 | |||
| 313 | Layout.alignment: Qt.AlignVCenter | Qt.AlignHCenter | ||
| 314 | } | ||
| 315 | |||
| 316 | Text { | ||
| 317 | id: actionLabel | ||
| 318 | |||
| 319 | visible: actionMouseArea.modelData?.text ?? false | ||
| 320 | |||
| 321 | text: actionMouseArea.modelData?.text ?? "" | ||
| 322 | |||
| 323 | font.pointSize: 10 | ||
| 324 | font.family: "Fira Sans" | ||
| 325 | color: notifWindow.textColor | ||
| 326 | maximumLineCount: 1 | ||
| 327 | elide: Text.ElideRight | ||
| 328 | } | ||
| 329 | } | ||
| 330 | } | ||
| 331 | } | ||
| 332 | } | ||
| 333 | } | ||
| 334 | } | ||
| 335 | } | ||
| 336 | } | ||
| 337 | } | ||
| 338 | } | ||
| 339 | } | ||
| 340 | } | ||
diff --git a/accounts/gkleen@sif/shell/quickshell/NotificationInhibitorWidget.qml b/accounts/gkleen@sif/shell/quickshell/NotificationInhibitorWidget.qml new file mode 100644 index 00000000..b58467b3 --- /dev/null +++ b/accounts/gkleen@sif/shell/quickshell/NotificationInhibitorWidget.qml | |||
| @@ -0,0 +1,266 @@ | |||
| 1 | import Quickshell | ||
| 2 | import QtQuick | ||
| 3 | import Quickshell.Widgets | ||
| 4 | import qs.Services | ||
| 5 | import QtQuick.Controls | ||
| 6 | import QtQuick.Layouts | ||
| 7 | import QtQuick.Shapes | ||
| 8 | |||
| 9 | Item { | ||
| 10 | id: root | ||
| 11 | |||
| 12 | width: icon.width + 8 | ||
| 13 | height: parent.height | ||
| 14 | anchors.verticalCenter: parent.verticalCenter | ||
| 15 | |||
| 16 | WrapperMouseArea { | ||
| 17 | id: widgetMouseArea | ||
| 18 | |||
| 19 | anchors.fill: parent | ||
| 20 | |||
| 21 | hoverEnabled: true | ||
| 22 | cursorShape: Qt.PointingHandCursor | ||
| 23 | |||
| 24 | onClicked: NotificationManager.displayInhibited = !NotificationManager.displayInhibited | ||
| 25 | |||
| 26 | Rectangle { | ||
| 27 | anchors.fill: parent | ||
| 28 | color: { | ||
| 29 | if (widgetMouseArea.containsMouse) { | ||
| 30 | return "#33808080"; | ||
| 31 | } | ||
| 32 | return "transparent"; | ||
| 33 | } | ||
| 34 | |||
| 35 | Item { | ||
| 36 | anchors.fill: parent | ||
| 37 | |||
| 38 | MaterialDesignIcon { | ||
| 39 | id: icon | ||
| 40 | |||
| 41 | implicitSize: 14 | ||
| 42 | anchors.centerIn: parent | ||
| 43 | |||
| 44 | icon: NotificationManager.active ? "message" : "message-off" | ||
| 45 | color: { | ||
| 46 | if (!NotificationManager.active && !NotificationManager.displayInhibited) | ||
| 47 | return "#f28a21"; | ||
| 48 | if (NotificationManager.displayInhibited) | ||
| 49 | return "white"; | ||
| 50 | return "#555"; | ||
| 51 | } | ||
| 52 | } | ||
| 53 | } | ||
| 54 | } | ||
| 55 | } | ||
| 56 | |||
| 57 | Loader { | ||
| 58 | id: tooltipLoader | ||
| 59 | |||
| 60 | active: false | ||
| 61 | |||
| 62 | Connections { | ||
| 63 | target: widgetMouseArea | ||
| 64 | function onContainsMouseChanged() { | ||
| 65 | if (widgetMouseArea.containsMouse) | ||
| 66 | tooltipLoader.active = true; | ||
| 67 | } | ||
| 68 | } | ||
| 69 | |||
| 70 | sourceComponent: PopupWindow { | ||
| 71 | id: tooltip | ||
| 72 | |||
| 73 | property bool nextVisible: NotificationManager.active && (widgetMouseArea.containsMouse || tooltipMouseArea.containsMouse) | ||
| 74 | |||
| 75 | anchor { | ||
| 76 | item: widgetMouseArea | ||
| 77 | edges: Edges.Bottom | Edges.Left | ||
| 78 | } | ||
| 79 | visible: false | ||
| 80 | |||
| 81 | onNextVisibleChanged: hangTimer.restart() | ||
| 82 | |||
| 83 | Timer { | ||
| 84 | id: hangTimer | ||
| 85 | interval: tooltip.visible ? 100 : 500 | ||
| 86 | onTriggered: { | ||
| 87 | tooltip.visible = tooltip.nextVisible; | ||
| 88 | if (!tooltip.visible) | ||
| 89 | tooltipLoader.active = false; | ||
| 90 | } | ||
| 91 | } | ||
| 92 | |||
| 93 | implicitWidth: 400 | ||
| 94 | implicitHeight: Math.min(tooltip.screen.height * 0.66, Math.max(100, scroll.contentHeight + 16)) | ||
| 95 | color: "black" | ||
| 96 | |||
| 97 | WrapperMouseArea { | ||
| 98 | id: tooltipMouseArea | ||
| 99 | |||
| 100 | hoverEnabled: true | ||
| 101 | enabled: true | ||
| 102 | |||
| 103 | anchors.fill: parent | ||
| 104 | |||
| 105 | WrapperItem { | ||
| 106 | margin: 8 | ||
| 107 | |||
| 108 | ScrollView { | ||
| 109 | id: scroll | ||
| 110 | |||
| 111 | contentWidth: availableWidth | ||
| 112 | // ScrollBar.vertical.policy: ScrollBar.AlwaysOn | ||
| 113 | |||
| 114 | ColumnLayout { | ||
| 115 | id: historyLayout | ||
| 116 | anchors { | ||
| 117 | left: parent.left | ||
| 118 | right: parent.right | ||
| 119 | } | ||
| 120 | |||
| 121 | spacing: 8 | ||
| 122 | |||
| 123 | Repeater { | ||
| 124 | model: ScriptModel { | ||
| 125 | values: [...NotificationManager.history].reverse().map(o => o.notification) | ||
| 126 | } | ||
| 127 | |||
| 128 | delegate: GridLayout { | ||
| 129 | id: notif | ||
| 130 | |||
| 131 | Layout.fillWidth: true | ||
| 132 | Layout.preferredHeight: notifSummary.contentHeight + (notifBody.visible ? notifBody.contentHeight + 8 : 0) + (notifSep.visible ? notifSep.height + 8 : 0) + notifTime.contentHeight + 8 | ||
| 133 | |||
| 134 | required property var modelData | ||
| 135 | required property int index | ||
| 136 | |||
| 137 | columnSpacing: 8 | ||
| 138 | rowSpacing: 8 | ||
| 139 | |||
| 140 | columns: notifImage.visible ? 2 : 1 | ||
| 141 | rows: { | ||
| 142 | var res = 2; | ||
| 143 | if (notifBody.visible) | ||
| 144 | res += 1; | ||
| 145 | if (notifSep.visible) | ||
| 146 | res += 1; | ||
| 147 | return res; | ||
| 148 | } | ||
| 149 | |||
| 150 | Shape { | ||
| 151 | id: notifSep | ||
| 152 | |||
| 153 | visible: notif.index != 0 | ||
| 154 | |||
| 155 | height: 2 | ||
| 156 | width: 400 - 32 | ||
| 157 | |||
| 158 | ShapePath { | ||
| 159 | strokeWidth: 2 | ||
| 160 | strokeColor: "#20ffffff" | ||
| 161 | startX: 0; startY: 0; | ||
| 162 | PathLine { x: 400 - 32; y: 0; } | ||
| 163 | } | ||
| 164 | |||
| 165 | Layout.row: 0 | ||
| 166 | Layout.column: 0 | ||
| 167 | Layout.columnSpan: notifImage.visible ? 2 : 1 | ||
| 168 | Layout.alignment: Qt.AlignHCenter | ||
| 169 | } | ||
| 170 | |||
| 171 | Text { | ||
| 172 | id: notifSummary | ||
| 173 | |||
| 174 | text: notif.modelData?.summary ?? "" | ||
| 175 | |||
| 176 | font.pointSize: 10 | ||
| 177 | font.family: "Fira Sans" | ||
| 178 | font.italic: true | ||
| 179 | color: "white" | ||
| 180 | maximumLineCount: 1 | ||
| 181 | elide: Text.ElideRight | ||
| 182 | |||
| 183 | Layout.fillWidth: true | ||
| 184 | Layout.row: notifSep.visible ? 1 : 0 | ||
| 185 | Layout.column: notifImage.visible ? 1 : 0 | ||
| 186 | } | ||
| 187 | |||
| 188 | Image { | ||
| 189 | id: notifImage | ||
| 190 | |||
| 191 | visible: (notif.modelData?.image || notif.modelData?.appIcon) ?? false | ||
| 192 | |||
| 193 | onStatusChanged: { | ||
| 194 | if (notifImage.status == Image.Error) | ||
| 195 | notifImage.visible = false; | ||
| 196 | } | ||
| 197 | |||
| 198 | source: (notif.modelData?.image || notif.modelData?.appIcon) ?? "" | ||
| 199 | fillMode: Image.PreserveAspectFit | ||
| 200 | asynchronous: true | ||
| 201 | smooth: true | ||
| 202 | mipmap: true | ||
| 203 | |||
| 204 | Layout.maximumWidth: 50 | ||
| 205 | Layout.column: 0 | ||
| 206 | Layout.row: notifSep.visible ? 1 : 0 | ||
| 207 | Layout.fillHeight: true | ||
| 208 | Layout.rowSpan: notifBody.visible ? 3 : 2 | ||
| 209 | } | ||
| 210 | |||
| 211 | Text { | ||
| 212 | id: notifBody | ||
| 213 | |||
| 214 | visible: notif.modelData?.body ?? false | ||
| 215 | text: notif.modelData?.body ?? "" | ||
| 216 | textFormat: Text.RichText | ||
| 217 | wrapMode: Text.Wrap | ||
| 218 | |||
| 219 | font.pointSize: 10 | ||
| 220 | font.family: "Fira Sans" | ||
| 221 | color: "white" | ||
| 222 | |||
| 223 | Layout.fillWidth: true | ||
| 224 | Layout.row: notifSep.visible ? 2 : 1 | ||
| 225 | Layout.column: notifImage.visible ? 1 : 0 | ||
| 226 | } | ||
| 227 | |||
| 228 | Text { | ||
| 229 | id: notifTime | ||
| 230 | |||
| 231 | Connections { | ||
| 232 | target: NotificationManager.clock | ||
| 233 | function onDateChanged() { | ||
| 234 | notifTime.text = NotificationManager.formatTime(notif.modelData?.receivedTime); | ||
| 235 | } | ||
| 236 | } | ||
| 237 | |||
| 238 | text: NotificationManager.formatTime(notif.modelData?.receivedTime) | ||
| 239 | |||
| 240 | font.pointSize: 8 | ||
| 241 | font.family: "Fira Sans" | ||
| 242 | font.italic: true | ||
| 243 | color: "#555" | ||
| 244 | maximumLineCount: 1 | ||
| 245 | horizontalAlignment: Text.AlignRight | ||
| 246 | |||
| 247 | Layout.fillWidth: true | ||
| 248 | Layout.row: { | ||
| 249 | var res = 1; | ||
| 250 | if (notifSep.visible) | ||
| 251 | res += 1; | ||
| 252 | if (notifBody.visible) | ||
| 253 | res += 1; | ||
| 254 | return res; | ||
| 255 | } | ||
| 256 | Layout.column: notifImage.visible ? 1 : 0 | ||
| 257 | } | ||
| 258 | } | ||
| 259 | } | ||
| 260 | } | ||
| 261 | } | ||
| 262 | } | ||
| 263 | } | ||
| 264 | } | ||
| 265 | } | ||
| 266 | } | ||
diff --git a/accounts/gkleen@sif/shell/quickshell/PipewireWidget.qml b/accounts/gkleen@sif/shell/quickshell/PipewireWidget.qml new file mode 100644 index 00000000..9c6b65a4 --- /dev/null +++ b/accounts/gkleen@sif/shell/quickshell/PipewireWidget.qml | |||
| @@ -0,0 +1,483 @@ | |||
| 1 | import QtQuick | ||
| 2 | import QtQuick.Layouts | ||
| 3 | import QtQuick.Controls.Fusion | ||
| 4 | import Quickshell | ||
| 5 | import Quickshell.Services.Pipewire | ||
| 6 | import Quickshell.Widgets | ||
| 7 | |||
| 8 | Item { | ||
| 9 | height: parent.height | ||
| 10 | width: volumeIcon.width + 8 | ||
| 11 | anchors.verticalCenter: parent.verticalCenter | ||
| 12 | |||
| 13 | PwObjectTracker { | ||
| 14 | objects: [Pipewire.defaultAudioSink] | ||
| 15 | } | ||
| 16 | |||
| 17 | WrapperMouseArea { | ||
| 18 | id: widgetMouseArea | ||
| 19 | |||
| 20 | anchors.fill: parent | ||
| 21 | hoverEnabled: true | ||
| 22 | cursorShape: Qt.PointingHandCursor | ||
| 23 | |||
| 24 | onClicked: { | ||
| 25 | if (!Pipewire.defaultAudioSink) | ||
| 26 | return; | ||
| 27 | Pipewire.defaultAudioSink.audio.muted = !Pipewire.defaultAudioSink.audio.muted; | ||
| 28 | } | ||
| 29 | |||
| 30 | property real sensitivity: (1 / 40) / 120 | ||
| 31 | onWheel: event => { | ||
| 32 | if (!Pipewire.defaultAudioSink) | ||
| 33 | return; | ||
| 34 | Pipewire.defaultAudioSink.audio.volume += event.angleDelta.y * sensitivity; | ||
| 35 | } | ||
| 36 | |||
| 37 | Rectangle { | ||
| 38 | id: volumeWidget | ||
| 39 | |||
| 40 | anchors.fill: parent | ||
| 41 | color: { | ||
| 42 | if (widgetMouseArea.containsMouse) | ||
| 43 | return "#33808080"; | ||
| 44 | return "transparent"; | ||
| 45 | } | ||
| 46 | |||
| 47 | Item { | ||
| 48 | anchors.fill: parent | ||
| 49 | |||
| 50 | MaterialDesignIcon { | ||
| 51 | id: volumeIcon | ||
| 52 | |||
| 53 | implicitSize: 14 | ||
| 54 | anchors.centerIn: parent | ||
| 55 | |||
| 56 | icon: { | ||
| 57 | if (!Pipewire.defaultAudioSink || Pipewire.defaultAudioSink.audio.muted) | ||
| 58 | return "volume-off"; | ||
| 59 | if (Pipewire.defaultAudioSink.audio.volume <= 0.33) | ||
| 60 | return "volume-low"; | ||
| 61 | if (Pipewire.defaultAudioSink.audio.volume <= 0.67) | ||
| 62 | return "volume-medium"; | ||
| 63 | return "volume-high"; | ||
| 64 | } | ||
| 65 | color: "#555" | ||
| 66 | } | ||
| 67 | } | ||
| 68 | } | ||
| 69 | } | ||
| 70 | |||
| 71 | Loader { | ||
| 72 | id: tooltipLoader | ||
| 73 | |||
| 74 | active: false | ||
| 75 | |||
| 76 | Connections { | ||
| 77 | target: widgetMouseArea | ||
| 78 | function onContainsMouseChanged() { | ||
| 79 | if (widgetMouseArea.containsMouse) | ||
| 80 | tooltipLoader.active = true; | ||
| 81 | } | ||
| 82 | } | ||
| 83 | |||
| 84 | PwObjectTracker { | ||
| 85 | objects: Pipewire.devices | ||
| 86 | } | ||
| 87 | PwObjectTracker { | ||
| 88 | objects: Pipewire.nodes | ||
| 89 | } | ||
| 90 | |||
| 91 | sourceComponent: PopupWindow { | ||
| 92 | id: tooltip | ||
| 93 | |||
| 94 | property bool openPopup: false | ||
| 95 | property bool nextVisible: widgetMouseArea.containsMouse || tooltipMouseArea.containsMouse || openPopup | ||
| 96 | |||
| 97 | anchor { | ||
| 98 | item: widgetMouseArea | ||
| 99 | edges: Edges.Bottom | Edges.Left | ||
| 100 | } | ||
| 101 | visible: false | ||
| 102 | |||
| 103 | onNextVisibleChanged: hangTimer.restart() | ||
| 104 | |||
| 105 | Timer { | ||
| 106 | id: hangTimer | ||
| 107 | interval: 100 | ||
| 108 | onTriggered: { | ||
| 109 | tooltip.visible = tooltip.nextVisible; | ||
| 110 | if (!tooltip.visible) | ||
| 111 | tooltipLoader.active = false; | ||
| 112 | } | ||
| 113 | } | ||
| 114 | |||
| 115 | implicitWidth: tooltipContent.width | ||
| 116 | implicitHeight: tooltipContent.height | ||
| 117 | color: "transparent" | ||
| 118 | |||
| 119 | Rectangle { | ||
| 120 | width: tooltip.width | ||
| 121 | height: tooltipLayout.childrenRect.height + 16 | ||
| 122 | color: "black" | ||
| 123 | } | ||
| 124 | |||
| 125 | WrapperItem { | ||
| 126 | id: tooltipContent | ||
| 127 | |||
| 128 | bottomMargin: Math.max(0, 200 - tooltipLayout.implicitHeight) | ||
| 129 | |||
| 130 | WrapperMouseArea { | ||
| 131 | id: tooltipMouseArea | ||
| 132 | |||
| 133 | hoverEnabled: true | ||
| 134 | enabled: true | ||
| 135 | |||
| 136 | WrapperItem { | ||
| 137 | margin: 8 | ||
| 138 | bottomMargin: 8 | ||
| 139 | |||
| 140 | GridLayout { | ||
| 141 | id: tooltipLayout | ||
| 142 | |||
| 143 | columns: 4 | ||
| 144 | |||
| 145 | Repeater { | ||
| 146 | model: Array.from(Pipewire.devices.values).filter(dev => dev.type == "Audio/Device") | ||
| 147 | |||
| 148 | Item { | ||
| 149 | id: descItem | ||
| 150 | |||
| 151 | required property var modelData | ||
| 152 | required property int index | ||
| 153 | |||
| 154 | Layout.column: 0 | ||
| 155 | Layout.row: index | ||
| 156 | |||
| 157 | implicitWidth: descText.contentWidth | ||
| 158 | implicitHeight: descText.contentHeight | ||
| 159 | |||
| 160 | Text { | ||
| 161 | id: descText | ||
| 162 | |||
| 163 | color: "white" | ||
| 164 | font.pointSize: 10 | ||
| 165 | font.family: "Fira Sans" | ||
| 166 | |||
| 167 | text: descItem.modelData.description | ||
| 168 | } | ||
| 169 | } | ||
| 170 | } | ||
| 171 | |||
| 172 | Repeater { | ||
| 173 | id: defaultSinkRepeater | ||
| 174 | |||
| 175 | model: { | ||
| 176 | Array.from(Pipewire.devices.values) | ||
| 177 | .filter(dev => dev.type == "Audio/Device") | ||
| 178 | .map(device => Array.from(Pipewire.nodes.values).find(node => node.type == PwNodeType.AudioSink && node.device?.id == device.id )); | ||
| 179 | } | ||
| 180 | |||
| 181 | Item { | ||
| 182 | id: defaultSinkItem | ||
| 183 | |||
| 184 | required property var modelData | ||
| 185 | required property int index | ||
| 186 | |||
| 187 | visible: Boolean(modelData) | ||
| 188 | |||
| 189 | PwObjectTracker { | ||
| 190 | objects: [defaultSinkItem.modelData] | ||
| 191 | } | ||
| 192 | |||
| 193 | Layout.column: 1 | ||
| 194 | Layout.row: index | ||
| 195 | |||
| 196 | Layout.fillHeight: true | ||
| 197 | |||
| 198 | implicitWidth: 16 + 8 | ||
| 199 | |||
| 200 | WrapperMouseArea { | ||
| 201 | id: defaultSinkMouseArea | ||
| 202 | |||
| 203 | anchors.fill: parent | ||
| 204 | hoverEnabled: true | ||
| 205 | cursorShape: Qt.PointingHandCursor | ||
| 206 | |||
| 207 | onClicked: { | ||
| 208 | Pipewire.preferredDefaultAudioSink = defaultSinkItem.modelData | ||
| 209 | } | ||
| 210 | |||
| 211 | onWheel: event => scrollVolume(event); | ||
| 212 | property real sensitivity: (1 / 40) / 120 | ||
| 213 | function scrollVolume(event) { | ||
| 214 | defaultSinkItem.modelData.audio.volume += event.angleDelta.y * sensitivity; | ||
| 215 | } | ||
| 216 | |||
| 217 | Rectangle { | ||
| 218 | id: defaultSinkWidget | ||
| 219 | |||
| 220 | anchors.fill: parent | ||
| 221 | color: { | ||
| 222 | if (defaultSinkMouseArea.containsMouse) | ||
| 223 | return "#33808080"; | ||
| 224 | return "transparent"; | ||
| 225 | } | ||
| 226 | |||
| 227 | MaterialDesignIcon { | ||
| 228 | width: 16 | ||
| 229 | height: 16 | ||
| 230 | anchors.centerIn: parent | ||
| 231 | |||
| 232 | icon: { | ||
| 233 | if (defaultSinkItem.modelData?.id == Pipewire.defaultAudioSink?.id) | ||
| 234 | return "speaker"; | ||
| 235 | return "speaker-off"; | ||
| 236 | } | ||
| 237 | color: icon == "speaker" ? "white" : "#555" | ||
| 238 | } | ||
| 239 | } | ||
| 240 | } | ||
| 241 | |||
| 242 | PopupWindow { | ||
| 243 | id: volumeTooltip | ||
| 244 | |||
| 245 | property bool nextVisible: defaultSinkMouseArea.containsMouse || volumeTooltipMouseArea.containsMouse | ||
| 246 | |||
| 247 | anchor { | ||
| 248 | item: defaultSinkMouseArea | ||
| 249 | edges: Edges.Bottom | Edges.Left | ||
| 250 | } | ||
| 251 | visible: false | ||
| 252 | |||
| 253 | onNextVisibleChanged: volumeHangTimer.restart() | ||
| 254 | |||
| 255 | onVisibleChanged: tooltip.openPopup = volumeTooltip.visible | ||
| 256 | |||
| 257 | Timer { | ||
| 258 | id: volumeHangTimer | ||
| 259 | interval: 100 | ||
| 260 | onTriggered: volumeTooltip.visible = volumeTooltip.nextVisible | ||
| 261 | } | ||
| 262 | |||
| 263 | implicitWidth: volumeTooltipText.contentWidth + 16 | ||
| 264 | implicitHeight: volumeTooltipText.contentHeight + 16 | ||
| 265 | color: "black" | ||
| 266 | |||
| 267 | WrapperMouseArea { | ||
| 268 | id: volumeTooltipMouseArea | ||
| 269 | |||
| 270 | hoverEnabled: true | ||
| 271 | enabled: true | ||
| 272 | |||
| 273 | onWheel: event => defaultSinkMouseArea.scrollVolume(event); | ||
| 274 | |||
| 275 | anchors.fill: parent | ||
| 276 | |||
| 277 | Item { | ||
| 278 | anchors.fill: parent | ||
| 279 | |||
| 280 | Text { | ||
| 281 | id: volumeTooltipText | ||
| 282 | |||
| 283 | anchors.centerIn: parent | ||
| 284 | |||
| 285 | font.pointSize: 10 | ||
| 286 | font.family: "Fira Sans" | ||
| 287 | color: "white" | ||
| 288 | |||
| 289 | text: `${Math.round(defaultSinkItem.modelData?.audio?.volume * 100)}%` | ||
| 290 | } | ||
| 291 | } | ||
| 292 | } | ||
| 293 | } | ||
| 294 | } | ||
| 295 | } | ||
| 296 | |||
| 297 | Repeater { | ||
| 298 | id: defaultSourceRepeater | ||
| 299 | |||
| 300 | model: { | ||
| 301 | Array.from(Pipewire.devices.values) | ||
| 302 | .filter(dev => dev.type == "Audio/Device") | ||
| 303 | .map(device => Array.from(Pipewire.nodes.values).find(node => node.type == PwNodeType.AudioSource && node.device?.id == device.id )); | ||
| 304 | } | ||
| 305 | |||
| 306 | Item { | ||
| 307 | id: defaultSourceItem | ||
| 308 | |||
| 309 | required property var modelData | ||
| 310 | required property int index | ||
| 311 | |||
| 312 | visible: Boolean(modelData) | ||
| 313 | |||
| 314 | PwObjectTracker { | ||
| 315 | objects: [defaultSourceItem.modelData] | ||
| 316 | } | ||
| 317 | |||
| 318 | Layout.column: 2 | ||
| 319 | Layout.row: index | ||
| 320 | |||
| 321 | Layout.fillHeight: true | ||
| 322 | |||
| 323 | implicitWidth: 16 + 8 | ||
| 324 | |||
| 325 | WrapperMouseArea { | ||
| 326 | id: defaultSourceMouseArea | ||
| 327 | |||
| 328 | anchors.fill: parent | ||
| 329 | hoverEnabled: true | ||
| 330 | cursorShape: Qt.PointingHandCursor | ||
| 331 | |||
| 332 | onClicked: { | ||
| 333 | Pipewire.preferredDefaultAudioSource = defaultSourceItem.modelData | ||
| 334 | } | ||
| 335 | |||
| 336 | onWheel: event => scrollVolume(event); | ||
| 337 | property real sensitivity: (1 / 40) / 120 | ||
| 338 | function scrollVolume(event) { | ||
| 339 | defaultSourceItem.modelData.audio.volume += event.angleDelta.y * sensitivity; | ||
| 340 | } | ||
| 341 | |||
| 342 | Rectangle { | ||
| 343 | id: defaultSourceWidget | ||
| 344 | |||
| 345 | anchors.fill: parent | ||
| 346 | color: { | ||
| 347 | if (defaultSourceMouseArea.containsMouse) | ||
| 348 | return "#33808080"; | ||
| 349 | return "transparent"; | ||
| 350 | } | ||
| 351 | |||
| 352 | MaterialDesignIcon { | ||
| 353 | width: 16 | ||
| 354 | height: 16 | ||
| 355 | anchors.centerIn: parent | ||
| 356 | |||
| 357 | icon: { | ||
| 358 | if (defaultSourceItem.modelData?.id == Pipewire.defaultAudioSource?.id) | ||
| 359 | return "microphone"; | ||
| 360 | return "microphone-off"; | ||
| 361 | } | ||
| 362 | color: icon == "microphone" ? "white" : "#555" | ||
| 363 | } | ||
| 364 | } | ||
| 365 | } | ||
| 366 | |||
| 367 | PopupWindow { | ||
| 368 | id: volumeTooltip | ||
| 369 | |||
| 370 | property bool nextVisible: defaultSourceMouseArea.containsMouse || volumeTooltipMouseArea.containsMouse | ||
| 371 | |||
| 372 | anchor { | ||
| 373 | item: defaultSourceMouseArea | ||
| 374 | edges: Edges.Bottom | Edges.Left | ||
| 375 | } | ||
| 376 | visible: false | ||
| 377 | |||
| 378 | onNextVisibleChanged: volumeHangTimer.restart() | ||
| 379 | |||
| 380 | onVisibleChanged: tooltip.openPopup = volumeTooltip.visible | ||
| 381 | |||
| 382 | Timer { | ||
| 383 | id: volumeHangTimer | ||
| 384 | interval: 100 | ||
| 385 | onTriggered: volumeTooltip.visible = volumeTooltip.nextVisible | ||
| 386 | } | ||
| 387 | |||
| 388 | implicitWidth: volumeTooltipText.contentWidth + 16 | ||
| 389 | implicitHeight: volumeTooltipText.contentHeight + 16 | ||
| 390 | color: "black" | ||
| 391 | |||
| 392 | WrapperMouseArea { | ||
| 393 | id: volumeTooltipMouseArea | ||
| 394 | |||
| 395 | hoverEnabled: true | ||
| 396 | enabled: true | ||
| 397 | |||
| 398 | onWheel: event => defaultSourceMouseArea.scrollVolume(event); | ||
| 399 | |||
| 400 | anchors.fill: parent | ||
| 401 | |||
| 402 | Item { | ||
| 403 | anchors.fill: parent | ||
| 404 | |||
| 405 | Text { | ||
| 406 | id: volumeTooltipText | ||
| 407 | |||
| 408 | anchors.centerIn: parent | ||
| 409 | |||
| 410 | font.pointSize: 10 | ||
| 411 | font.family: "Fira Sans" | ||
| 412 | color: "white" | ||
| 413 | |||
| 414 | text: `${Math.round(defaultSourceItem.modelData?.audio?.volume * 100)}%` | ||
| 415 | } | ||
| 416 | } | ||
| 417 | } | ||
| 418 | } | ||
| 419 | } | ||
| 420 | } | ||
| 421 | |||
| 422 | Repeater { | ||
| 423 | id: profileRepeater | ||
| 424 | |||
| 425 | model: Array.from(Pipewire.devices.values).filter(dev => dev.type == "Audio/Device") | ||
| 426 | |||
| 427 | Item { | ||
| 428 | id: profileItem | ||
| 429 | |||
| 430 | required property var modelData | ||
| 431 | required property int index | ||
| 432 | |||
| 433 | PwObjectTracker { | ||
| 434 | objects: [profileItem.modelData] | ||
| 435 | } | ||
| 436 | |||
| 437 | Layout.column: 3 | ||
| 438 | Layout.row: index | ||
| 439 | |||
| 440 | Layout.fillWidth: true | ||
| 441 | |||
| 442 | implicitWidth: Math.max(profileBox.implicitWidth, 300) | ||
| 443 | implicitHeight: profileBox.height | ||
| 444 | |||
| 445 | ComboBox { | ||
| 446 | id: profileBox | ||
| 447 | |||
| 448 | model: profileItem.modelData.profiles | ||
| 449 | |||
| 450 | textRole: "description" | ||
| 451 | valueRole: "index" | ||
| 452 | onActivated: profileItem.modelData.setProfile(currentValue) | ||
| 453 | |||
| 454 | anchors.fill: parent | ||
| 455 | |||
| 456 | implicitContentWidthPolicy: ComboBox.WidestText | ||
| 457 | |||
| 458 | Connections { | ||
| 459 | target: profileItem.modelData | ||
| 460 | function onCurrentProfileChanged() { | ||
| 461 | profileBox.currentIndex = Array.from(profileItem.modelData.profiles).findIndex(profile => profile.index == profileItem.modelData.currentProfile); | ||
| 462 | } | ||
| 463 | } | ||
| 464 | Component.onCompleted: { | ||
| 465 | profileBox.currentIndex = Array.from(profileItem.modelData.profiles).findIndex(profile => profile.index == profileItem.modelData.currentProfile); | ||
| 466 | } | ||
| 467 | |||
| 468 | Connections { | ||
| 469 | target: profileBox.popup | ||
| 470 | function onVisibleChanged() { | ||
| 471 | tooltip.openPopup = profileBox.popup.visible | ||
| 472 | } | ||
| 473 | } | ||
| 474 | } | ||
| 475 | } | ||
| 476 | } | ||
| 477 | } | ||
| 478 | } | ||
| 479 | } | ||
| 480 | } | ||
| 481 | } | ||
| 482 | } | ||
| 483 | } | ||
diff --git a/accounts/gkleen@sif/shell/quickshell/PrivacyWidget.qml b/accounts/gkleen@sif/shell/quickshell/PrivacyWidget.qml new file mode 100644 index 00000000..d7ffadfe --- /dev/null +++ b/accounts/gkleen@sif/shell/quickshell/PrivacyWidget.qml | |||
| @@ -0,0 +1,49 @@ | |||
| 1 | import QtQuick | ||
| 2 | import QtQuick.Layouts | ||
| 3 | import Quickshell | ||
| 4 | import Quickshell.Widgets | ||
| 5 | import qs.Services | ||
| 6 | |||
| 7 | Item { | ||
| 8 | height: parent.height | ||
| 9 | width: layout.childrenRect.width | ||
| 10 | anchors.verticalCenter: parent.verticalCenter | ||
| 11 | |||
| 12 | visible: Array.from(Privacy.activeItems).length > 0 | ||
| 13 | |||
| 14 | RowLayout { | ||
| 15 | id: layout | ||
| 16 | |||
| 17 | anchors.fill: parent | ||
| 18 | |||
| 19 | spacing: 8 | ||
| 20 | |||
| 21 | Repeater { | ||
| 22 | model: Privacy.activeItems | ||
| 23 | |||
| 24 | Item { | ||
| 25 | id: privacyItem | ||
| 26 | |||
| 27 | required property var modelData; | ||
| 28 | |||
| 29 | height: parent.height | ||
| 30 | width: icon.width | ||
| 31 | |||
| 32 | MaterialDesignIcon { | ||
| 33 | id: icon | ||
| 34 | |||
| 35 | implicitSize: 14 | ||
| 36 | anchors.centerIn: parent | ||
| 37 | |||
| 38 | icon: { | ||
| 39 | if (privacyItem.modelData == Privacy.Item.Microphone) | ||
| 40 | return "microphone"; | ||
| 41 | if (privacyItem.modelData == Privacy.Item.Screensharing) | ||
| 42 | return "monitor-share"; | ||
| 43 | } | ||
| 44 | color: "#f2201f" | ||
| 45 | } | ||
| 46 | } | ||
| 47 | } | ||
| 48 | } | ||
| 49 | } | ||
diff --git a/accounts/gkleen@sif/shell/quickshell/Services/Brightness.qml b/accounts/gkleen@sif/shell/quickshell/Services/Brightness.qml new file mode 100644 index 00000000..8318df50 --- /dev/null +++ b/accounts/gkleen@sif/shell/quickshell/Services/Brightness.qml | |||
| @@ -0,0 +1,75 @@ | |||
| 1 | pragma Singleton | ||
| 2 | |||
| 3 | import QtQml | ||
| 4 | import Quickshell | ||
| 5 | import Quickshell.Io | ||
| 6 | import Custom as Custom | ||
| 7 | |||
| 8 | Singleton { | ||
| 9 | id: root | ||
| 10 | |||
| 11 | property string subsystem: "backlight" | ||
| 12 | property string device: "intel_backlight" | ||
| 13 | |||
| 14 | property real currBrightness | ||
| 15 | property real exponent: 4 | ||
| 16 | |||
| 17 | function calcCurrBrightness() { | ||
| 18 | if (!currFile.loaded || !maxFile.loaded) | ||
| 19 | return undefined; | ||
| 20 | const curr = Number(currFile.text()); | ||
| 21 | const max = Number(maxFile.text()); | ||
| 22 | const val = Math.pow(curr / max, 1 / root.exponent); | ||
| 23 | return val; | ||
| 24 | } | ||
| 25 | |||
| 26 | Connections { | ||
| 27 | target: currFile | ||
| 28 | function onLoaded() { | ||
| 29 | const b = root.calcCurrBrightness(); | ||
| 30 | if (typeof b !== 'undefined') | ||
| 31 | root.currBrightness = b; | ||
| 32 | } | ||
| 33 | } | ||
| 34 | Connections { | ||
| 35 | target: maxFile | ||
| 36 | function onLoaded() { | ||
| 37 | const b = root.calcCurrBrightness(); | ||
| 38 | if (typeof b !== 'undefined') | ||
| 39 | root.currBrightness = b; | ||
| 40 | } | ||
| 41 | } | ||
| 42 | |||
| 43 | onCurrBrightnessChanged: { | ||
| 44 | root.currBrightness = Math.max(0, Math.min(1, root.currBrightness)); | ||
| 45 | |||
| 46 | const prev = root.calcCurrBrightness(); | ||
| 47 | if (typeof prev === 'undefined' || Math.abs(root.currBrightness - prev) < 0.01) | ||
| 48 | return; | ||
| 49 | |||
| 50 | const max = Number(maxFile.text()); | ||
| 51 | const actual = Number(currFile.text()); | ||
| 52 | let curr = Math.max(0, Math.min(max, Math.pow(root.currBrightness, root.exponent) * max)); | ||
| 53 | if (Math.round(curr) == actual && curr < actual) | ||
| 54 | curr = Math.max(0, actual - 1); | ||
| 55 | else if (Math.round(curr) == actual && curr > actual) | ||
| 56 | curr = Math.min(max, actual + 1); | ||
| 57 | // root.currBrightness = Math.pow(curr / max, 1 / root.exponent); | ||
| 58 | Custom.Systemd.setBrightness(root.subsystem, root.device, Math.round(curr)); | ||
| 59 | } | ||
| 60 | |||
| 61 | FileView { | ||
| 62 | id: currFile | ||
| 63 | path: `/sys/class/${root.subsystem}/${root.device}/brightness` | ||
| 64 | blockAllReads: true | ||
| 65 | watchChanges: true | ||
| 66 | onFileChanged: reload() | ||
| 67 | } | ||
| 68 | FileView { | ||
| 69 | id: maxFile | ||
| 70 | path: `/sys/class/${root.subsystem}/${root.device}/max_brightness` | ||
| 71 | blockAllReads: true | ||
| 72 | watchChanges: true | ||
| 73 | onFileChanged: reload() | ||
| 74 | } | ||
| 75 | } | ||
diff --git a/accounts/gkleen@sif/shell/quickshell/Services/GpgAgent.qml b/accounts/gkleen@sif/shell/quickshell/Services/GpgAgent.qml new file mode 100644 index 00000000..3de69535 --- /dev/null +++ b/accounts/gkleen@sif/shell/quickshell/Services/GpgAgent.qml | |||
| @@ -0,0 +1,18 @@ | |||
| 1 | pragma Singleton | ||
| 2 | |||
| 3 | import Quickshell | ||
| 4 | import Quickshell.Io | ||
| 5 | |||
| 6 | Singleton { | ||
| 7 | id: root | ||
| 8 | |||
| 9 | Socket { | ||
| 10 | id: agentSocket | ||
| 11 | connected: true | ||
| 12 | path: `${Quickshell.env("XDG_RUNTIME_DIR")}/gnupg/S.gpg-agent` | ||
| 13 | } | ||
| 14 | |||
| 15 | function reloadAgent() { | ||
| 16 | agentSocket.write("RELOADAGENT\n") | ||
| 17 | } | ||
| 18 | } | ||
diff --git a/accounts/gkleen@sif/shell/quickshell/Services/InhibitorState.qml b/accounts/gkleen@sif/shell/quickshell/Services/InhibitorState.qml new file mode 100644 index 00000000..fe48fd7f --- /dev/null +++ b/accounts/gkleen@sif/shell/quickshell/Services/InhibitorState.qml | |||
| @@ -0,0 +1,22 @@ | |||
| 1 | pragma Singleton | ||
| 2 | |||
| 3 | import Quickshell | ||
| 4 | import Custom as Custom | ||
| 5 | |||
| 6 | Singleton { | ||
| 7 | id: inhibitorState | ||
| 8 | |||
| 9 | property bool waylandIdleInhibited: false | ||
| 10 | property alias lidSwitchInhibited: lidSwitchInhibitor.enabled | ||
| 11 | |||
| 12 | Custom.SystemdInhibitor { | ||
| 13 | id: lidSwitchInhibitor | ||
| 14 | |||
| 15 | enabled: false | ||
| 16 | |||
| 17 | what: Custom.SystemdInhibitorParams.HandleLidSwitch | ||
| 18 | who: "quickshell" | ||
| 19 | why: "User request" | ||
| 20 | mode: Custom.SystemdInhibitorParams.BlockWeak | ||
| 21 | } | ||
| 22 | } | ||
diff --git a/accounts/gkleen@sif/shell/quickshell/Services/MprisProxy.qml b/accounts/gkleen@sif/shell/quickshell/Services/MprisProxy.qml new file mode 100644 index 00000000..e3ab9755 --- /dev/null +++ b/accounts/gkleen@sif/shell/quickshell/Services/MprisProxy.qml | |||
| @@ -0,0 +1,8 @@ | |||
| 1 | pragma Singleton | ||
| 2 | |||
| 3 | import Quickshell | ||
| 4 | import Quickshell.Services.Mpris | ||
| 5 | |||
| 6 | Scope { | ||
| 7 | property list<var> players: Mpris.players.values | ||
| 8 | } | ||
diff --git a/accounts/gkleen@sif/shell/quickshell/Services/NiriService.qml b/accounts/gkleen@sif/shell/quickshell/Services/NiriService.qml new file mode 100644 index 00000000..cce614eb --- /dev/null +++ b/accounts/gkleen@sif/shell/quickshell/Services/NiriService.qml | |||
| @@ -0,0 +1,194 @@ | |||
| 1 | pragma Singleton | ||
| 2 | |||
| 3 | import Quickshell | ||
| 4 | import Quickshell.Io | ||
| 5 | import QtQuick | ||
| 6 | |||
| 7 | Singleton { | ||
| 8 | id: root | ||
| 9 | |||
| 10 | property var workspaces: [] | ||
| 11 | property var outputs: {} | ||
| 12 | property var keyboardLayouts: {} | ||
| 13 | property var windows: [] | ||
| 14 | readonly property string socketPath: Quickshell.env("NIRI_SOCKET") | ||
| 15 | |||
| 16 | function refreshOutputs() { | ||
| 17 | commandSocket.sendCommand("Outputs", data => { | ||
| 18 | outputs = data.Ok.Outputs; | ||
| 19 | }); | ||
| 20 | } | ||
| 21 | |||
| 22 | function sendCommand(command, callback) { | ||
| 23 | commandSocket.sendCommand(command, callback); | ||
| 24 | } | ||
| 25 | |||
| 26 | Socket { | ||
| 27 | id: eventStreamSocket | ||
| 28 | path: root.socketPath | ||
| 29 | connected: true | ||
| 30 | |||
| 31 | property bool acked: false | ||
| 32 | |||
| 33 | onConnectionStateChanged: { | ||
| 34 | if (connected) { | ||
| 35 | acked = false; | ||
| 36 | write('"EventStream"\n'); | ||
| 37 | } | ||
| 38 | } | ||
| 39 | |||
| 40 | parser: SplitParser { | ||
| 41 | onRead: line => { | ||
| 42 | try { | ||
| 43 | const event = JSON.parse(line) | ||
| 44 | |||
| 45 | // console.log(JSON.stringify(event)) | ||
| 46 | |||
| 47 | if (event.WorkspacesChanged) { | ||
| 48 | root.workspaces = event.WorkspacesChanged.workspaces | ||
| 49 | root.refreshOutputs(); | ||
| 50 | } else if (event.WorkspaceActivated) | ||
| 51 | eventWorkspaceActivated(event.WorkspaceActivated); | ||
| 52 | else if (event.WorkspaceUrgencyChanged) | ||
| 53 | eventWorkspaceUrgencyChanged(event.WorkspaceUrgencyChanged); | ||
| 54 | else if (event.WorkspaceActiveWindowChanged) | ||
| 55 | eventWorkspaceActiveWindowChanged(event.WorkspaceActiveWindowChanged); | ||
| 56 | else if (event.KeyboardLayoutsChanged) | ||
| 57 | root.keyboardLayouts = event.KeyboardLayoutsChanged.keyboard_layouts; | ||
| 58 | else if (event.KeyboardLayoutSwitched) | ||
| 59 | root.keyboardLayouts = Object.assign({}, root.keyboardLayouts, {"current_idx": event.KeyboardLayoutSwitched.idx }); | ||
| 60 | else if (event.WindowsChanged) | ||
| 61 | root.windows = event.WindowsChanged.windows | ||
| 62 | else if (event.WindowOpenedOrChanged) | ||
| 63 | eventWindowOpenedOrChanged(event.WindowOpenedOrChanged); | ||
| 64 | else if (event.WindowClosed) | ||
| 65 | eventWindowClosed(event.WindowClosed); | ||
| 66 | else if (event.WindowFocusChanged) | ||
| 67 | eventWindowFocusChanged(event.WindowFocusChanged); | ||
| 68 | else if (event.WindowUrgencyChanged) | ||
| 69 | eventWindowUrgencyChanged(event.WindowUrgencyChanged); | ||
| 70 | else if (event.WindowLayoutsChanged) | ||
| 71 | eventWindowLayoutsChanged(event.WindowLayoutsChanged); | ||
| 72 | else if (event.Ok && !eventStreamSocket.acked) { eventStreamSocket.acked = true; } | ||
| 73 | else if (event.OverviewOpenedOrClosed) {} | ||
| 74 | else if (event.ConfigLoaded) {} | ||
| 75 | else | ||
| 76 | console.log(JSON.stringify(event)); | ||
| 77 | } catch (e) { | ||
| 78 | console.warn("NiriService: Failed to parse event:", line, e) | ||
| 79 | } | ||
| 80 | } | ||
| 81 | } | ||
| 82 | } | ||
| 83 | |||
| 84 | Socket { | ||
| 85 | id: commandSocket | ||
| 86 | path: root.socketPath | ||
| 87 | connected: true | ||
| 88 | |||
| 89 | property var awaitingAnswer: null | ||
| 90 | property var cmdQueue: [] | ||
| 91 | |||
| 92 | parser: SplitParser { | ||
| 93 | onRead: line => { | ||
| 94 | if (commandSocket.awaitingAnswer === null) | ||
| 95 | return; | ||
| 96 | |||
| 97 | try { | ||
| 98 | const response = JSON.parse(line); | ||
| 99 | commandSocket.awaitingAnswer.callback(response); | ||
| 100 | commandSocket.awaitingAnswer = null; | ||
| 101 | } catch (e) { | ||
| 102 | console.warn("NiriService: Failed to parse response:", line, e) | ||
| 103 | } | ||
| 104 | commandSocket._handleQueue(); | ||
| 105 | } | ||
| 106 | } | ||
| 107 | |||
| 108 | onCmdQueueChanged: { | ||
| 109 | _handleQueue(); | ||
| 110 | } | ||
| 111 | onAwaitingAnswerChanged: { | ||
| 112 | _handleQueue(); | ||
| 113 | } | ||
| 114 | |||
| 115 | function _handleQueue() { | ||
| 116 | if (cmdQueue.length <= 0 || awaitingAnswer !== null) | ||
| 117 | return; | ||
| 118 | |||
| 119 | let localQueue = Array.from(cmdQueue); | ||
| 120 | awaitingAnswer = localQueue.shift(); | ||
| 121 | cmdQueue = localQueue; | ||
| 122 | write(JSON.stringify(awaitingAnswer.command) + '\n'); | ||
| 123 | } | ||
| 124 | |||
| 125 | function sendCommand(command, callback) { | ||
| 126 | cmdQueue = Array.from(cmdQueue).concat([{ "command": command, "callback": callback }]) | ||
| 127 | } | ||
| 128 | } | ||
| 129 | |||
| 130 | function eventWorkspaceActivated(data) { | ||
| 131 | let relevant_output = null; | ||
| 132 | Array.from(root.workspaces).forEach(ws => { | ||
| 133 | if (data.id === ws.id) | ||
| 134 | relevant_output = ws.output; | ||
| 135 | }); | ||
| 136 | root.workspaces = Array.from(root.workspaces).map(ws => { | ||
| 137 | if (data.focused) | ||
| 138 | ws.is_focused = false; | ||
| 139 | if (ws.output === relevant_output) | ||
| 140 | ws.is_active = false; | ||
| 141 | if (data.id === ws.id) { | ||
| 142 | ws.is_active = true; | ||
| 143 | ws.is_focused = data.focused; | ||
| 144 | } | ||
| 145 | return ws; | ||
| 146 | }); | ||
| 147 | } | ||
| 148 | function eventWorkspaceUrgencyChanged(data) { | ||
| 149 | root.workspaces = Array.from(root.workspaces).map(ws => { | ||
| 150 | if (data.id == ws.id) | ||
| 151 | ws.is_urgent = data.urgent; | ||
| 152 | return ws; | ||
| 153 | }); | ||
| 154 | } | ||
| 155 | function eventWorkspaceActiveWindowChanged(data) { | ||
| 156 | root.workspaces = Array.from(root.workspaces).map(ws => { | ||
| 157 | if (data.workspace_id === ws.id) | ||
| 158 | ws.active_window_id = data.active_window_id; | ||
| 159 | return ws; | ||
| 160 | }); | ||
| 161 | } | ||
| 162 | function eventWindowOpenedOrChanged(data) { | ||
| 163 | root.windows = Array.from(root.windows).map(win => { | ||
| 164 | if (data.window.is_focused) | ||
| 165 | win.is_focused = false; | ||
| 166 | return win; | ||
| 167 | }).filter(win => win.id !== data.window.id).concat([data.window]); | ||
| 168 | } | ||
| 169 | function eventWindowClosed(data) { | ||
| 170 | root.windows = Array.from(root.windows).filter(win => win.id !== data.id); | ||
| 171 | } | ||
| 172 | function eventWindowFocusChanged(data) { | ||
| 173 | root.windows = Array.from(root.windows).map(win => { | ||
| 174 | win.is_focused = win.id === data.id; | ||
| 175 | return win; | ||
| 176 | }); | ||
| 177 | } | ||
| 178 | function eventWindowUrgencyChanged(data) { | ||
| 179 | root.windows = Array.from(root.windows).map(win => { | ||
| 180 | if (win.id === data.id) | ||
| 181 | win.is_urgent = data.urgent; | ||
| 182 | return win; | ||
| 183 | }); | ||
| 184 | } | ||
| 185 | function eventWindowLayoutsChanged(data) { | ||
| 186 | root.windows = Array.from(root.windows).map(win => { | ||
| 187 | Array.from(data.changes).forEach(change => { | ||
| 188 | if (win.id === change[0]) | ||
| 189 | win.layout = change[1]; | ||
| 190 | }); | ||
| 191 | return win; | ||
| 192 | }); | ||
| 193 | } | ||
| 194 | } | ||
diff --git a/accounts/gkleen@sif/shell/quickshell/Services/NotificationManager.qml b/accounts/gkleen@sif/shell/quickshell/Services/NotificationManager.qml new file mode 100644 index 00000000..f02d1695 --- /dev/null +++ b/accounts/gkleen@sif/shell/quickshell/Services/NotificationManager.qml | |||
| @@ -0,0 +1,162 @@ | |||
| 1 | pragma Singleton | ||
| 2 | |||
| 3 | import QtQml | ||
| 4 | import Quickshell | ||
| 5 | import Quickshell.Services.Notifications | ||
| 6 | |||
| 7 | Singleton { | ||
| 8 | id: root | ||
| 9 | |||
| 10 | readonly property bool active: !root.lockscreenActive && !root.displayInhibited | ||
| 11 | property bool lockscreenActive: false | ||
| 12 | property bool displayInhibited: false | ||
| 13 | property alias trackedNotifications: server.trackedNotifications | ||
| 14 | readonly property var groups: { | ||
| 15 | function matchesGroupKey(notif, groupKey) { | ||
| 16 | var matches = true; | ||
| 17 | for (const prop in groupKey.test) { | ||
| 18 | if (notif[prop] !== groupKey.test[prop]) { | ||
| 19 | matches = false; | ||
| 20 | break; | ||
| 21 | } | ||
| 22 | } | ||
| 23 | return matches; | ||
| 24 | } | ||
| 25 | |||
| 26 | var groups = new Map(); | ||
| 27 | var notifs = new Array(); | ||
| 28 | for (const [ix, notif] of server.trackedNotifications.values.entries()) { | ||
| 29 | var didGroup = false; | ||
| 30 | for (const groupKey of root.groupKeys) { | ||
| 31 | if (!matchesGroupKey(notif, groupKey)) | ||
| 32 | continue; | ||
| 33 | |||
| 34 | const key = JSON.stringify({ | ||
| 35 | "key": groupKey, | ||
| 36 | "values": Object.assign({}, ...(Array.from(groupKey["group-by"]).map(prop => { | ||
| 37 | var res = {}; | ||
| 38 | res[prop] = notif[prop]; | ||
| 39 | return res; | ||
| 40 | }))) | ||
| 41 | }); | ||
| 42 | if (!groups.has(key)) | ||
| 43 | groups.set(key, new Array()); | ||
| 44 | groups.get(key).push({ "ix": ix, "notif": notif }); | ||
| 45 | didGroup = true; | ||
| 46 | break; | ||
| 47 | } | ||
| 48 | |||
| 49 | if (!didGroup) | ||
| 50 | notifs.push([{ "ix": ix, "notif": notif }]); | ||
| 51 | } | ||
| 52 | notifs.push(...groups.values()); | ||
| 53 | notifs.sort((as, bs) => Math.min(...(as.map(o => o.ix))) - Math.min(...(bs.map(o => o.ix)))); | ||
| 54 | return notifs.map(ns => ns.map(n => n.notif)); | ||
| 55 | } | ||
| 56 | |||
| 57 | property var groupKeys: [ | ||
| 58 | { "test": { "appName": "Element" }, "group-by": [ "summary" ] } | ||
| 59 | ]; | ||
| 60 | |||
| 61 | property int historyLimit: 100 | ||
| 62 | property var history: [] | ||
| 63 | |||
| 64 | Component { | ||
| 65 | id: expirationTimer | ||
| 66 | |||
| 67 | QtObject { | ||
| 68 | id: timer | ||
| 69 | |||
| 70 | required property QtObject parent | ||
| 71 | required property int expirationTime | ||
| 72 | |||
| 73 | property list<QtObject> data: [ | ||
| 74 | Timer { | ||
| 75 | running: root.active && !timer.expired | ||
| 76 | interval: timer.expirationTime | ||
| 77 | onTriggered: { | ||
| 78 | timer.parent.expirationTimer.destroy(); | ||
| 79 | timer.parent.expirationTimer = null; | ||
| 80 | timer.parent.expire(); | ||
| 81 | } | ||
| 82 | } | ||
| 83 | ] | ||
| 84 | } | ||
| 85 | } | ||
| 86 | |||
| 87 | Component { | ||
| 88 | id: notificationLock | ||
| 89 | |||
| 90 | RetainableLock {} | ||
| 91 | } | ||
| 92 | |||
| 93 | readonly property SystemClock clock: SystemClock { | ||
| 94 | precision: SystemClock.Minutes | ||
| 95 | } | ||
| 96 | |||
| 97 | function formatTime(time) { | ||
| 98 | const now = root.clock.date; | ||
| 99 | const diff = now - time; | ||
| 100 | const minutes = Math.ceil(diff / 60000); | ||
| 101 | const hours = Math.floor(minutes / 60); | ||
| 102 | |||
| 103 | if (hours < 1) { | ||
| 104 | if (minutes < 1) | ||
| 105 | return "now"; | ||
| 106 | if (minutes == 1) | ||
| 107 | return "1 minute"; | ||
| 108 | return `${minutes} minutes`; | ||
| 109 | } | ||
| 110 | |||
| 111 | const nowDate = new Date(now.getFullYear(), now.getMonth(), now.getDate()) | ||
| 112 | const timeDate = new Date(time.getFullYear(), time.getMonth(), time.getDate()) | ||
| 113 | const days = Math.floor((nowDate - timeDate) / (1000 * 86400)) | ||
| 114 | |||
| 115 | const timeStr = time.toLocaleTimeString(Qt.locale(), "HH:mm"); | ||
| 116 | |||
| 117 | if (days === 0) | ||
| 118 | return timeStr; | ||
| 119 | if (days === 1) | ||
| 120 | return `yesterday ${timeStr}`; | ||
| 121 | |||
| 122 | const dateStr = time.toLocaleTimeString(Qt.locale(), "YYYY-MM-DD"); | ||
| 123 | return `${dateStr} ${timeStr}`; | ||
| 124 | } | ||
| 125 | |||
| 126 | NotificationServer { | ||
| 127 | id: server | ||
| 128 | |||
| 129 | bodySupported: true | ||
| 130 | actionsSupported: true | ||
| 131 | actionIconsSupported: true | ||
| 132 | imageSupported: true | ||
| 133 | bodyMarkupSupported: true | ||
| 134 | bodyImagesSupported: true | ||
| 135 | |||
| 136 | onNotification: notification => { | ||
| 137 | var timeout = notification.expireTimeout * 1000; | ||
| 138 | if (notification.appName == "poweralertd") | ||
| 139 | timeout = 2000; | ||
| 140 | if (timeout > 0) { | ||
| 141 | Object.defineProperty(notification, "expirationTimer", { configurable: true, enumerable: true, writable: true }); | ||
| 142 | notification.expirationTimer = expirationTimer.createObject(notification, { parent: notification, expirationTime: timeout }); | ||
| 143 | } | ||
| 144 | Object.defineProperty(notification, "receivedTime", { configurable: true, enumerable: true, writable: true }); | ||
| 145 | notification.receivedTime = root.clock.date; | ||
| 146 | notification.closed.connect((reason) => server.onNotificationClosed(notification, reason)); | ||
| 147 | notification.tracked = true; | ||
| 148 | } | ||
| 149 | |||
| 150 | function onNotificationClosed(notification, reason) { | ||
| 151 | while (root.history.length >= root.historyLimit) { | ||
| 152 | root.history[0].lock.locked = false; | ||
| 153 | root.history.shift(); | ||
| 154 | } | ||
| 155 | |||
| 156 | root.history.push({ | ||
| 157 | lock: notificationLock.createObject(root, { locked: true, object: notification }), | ||
| 158 | notification: notification | ||
| 159 | }); | ||
| 160 | } | ||
| 161 | } | ||
| 162 | } | ||
diff --git a/accounts/gkleen@sif/shell/quickshell/Services/Privacy.qml b/accounts/gkleen@sif/shell/quickshell/Services/Privacy.qml new file mode 100644 index 00000000..9c813e49 --- /dev/null +++ b/accounts/gkleen@sif/shell/quickshell/Services/Privacy.qml | |||
| @@ -0,0 +1,63 @@ | |||
| 1 | pragma Singleton | ||
| 2 | |||
| 3 | import QtQml | ||
| 4 | import Quickshell | ||
| 5 | import Quickshell.Services.Pipewire | ||
| 6 | |||
| 7 | Singleton { | ||
| 8 | id: root | ||
| 9 | |||
| 10 | PwObjectTracker { | ||
| 11 | objects: Pipewire.nodes.values | ||
| 12 | } | ||
| 13 | |||
| 14 | enum Item { | ||
| 15 | Microphone, | ||
| 16 | Screensharing | ||
| 17 | } | ||
| 18 | |||
| 19 | readonly property list<var> activeItems: { | ||
| 20 | var items = []; | ||
| 21 | if (microphoneActive) | ||
| 22 | items.push(Privacy.Item.Microphone); | ||
| 23 | if (screensharingActive) | ||
| 24 | items.push(Privacy.Item.Screensharing); | ||
| 25 | return items; | ||
| 26 | } | ||
| 27 | |||
| 28 | readonly property bool microphoneActive: { | ||
| 29 | if (!Pipewire.ready || !Pipewire.nodes?.values) { | ||
| 30 | return false | ||
| 31 | } | ||
| 32 | |||
| 33 | for (const node of Pipewire.nodes.values) { | ||
| 34 | if (!node || (node.type & PwNodeType.AudioInStream) != PwNodeType.AudioInStream) | ||
| 35 | continue; | ||
| 36 | |||
| 37 | if (node.properties?.["stream.monitor"] === "true") | ||
| 38 | continue; | ||
| 39 | |||
| 40 | if (node.audio?.muted) | ||
| 41 | continue; | ||
| 42 | |||
| 43 | return true; | ||
| 44 | } | ||
| 45 | |||
| 46 | return false; | ||
| 47 | } | ||
| 48 | |||
| 49 | readonly property bool screensharingActive: { | ||
| 50 | if (!Pipewire.ready || !Pipewire.nodes?.values) { | ||
| 51 | return false | ||
| 52 | } | ||
| 53 | |||
| 54 | for (const node of Pipewire.nodes.values) { | ||
| 55 | if (!node || (node.type & PwNodeType.VideoInStream) != PwNodeType.VideoInStream) | ||
| 56 | continue; | ||
| 57 | |||
| 58 | return true; | ||
| 59 | } | ||
| 60 | |||
| 61 | return false; | ||
| 62 | } | ||
| 63 | } | ||
diff --git a/accounts/gkleen@sif/shell/quickshell/Services/WallpaperSelector.qml b/accounts/gkleen@sif/shell/quickshell/Services/WallpaperSelector.qml new file mode 100644 index 00000000..3c524955 --- /dev/null +++ b/accounts/gkleen@sif/shell/quickshell/Services/WallpaperSelector.qml | |||
| @@ -0,0 +1,8 @@ | |||
| 1 | import Custom as Custom | ||
| 2 | |||
| 3 | Custom.FileSelector { | ||
| 4 | id: root | ||
| 5 | |||
| 6 | directory: @wallpapers@ | ||
| 7 | epoch: 72000000 | ||
| 8 | } | ||
diff --git a/accounts/gkleen@sif/shell/quickshell/SystemTray.qml b/accounts/gkleen@sif/shell/quickshell/SystemTray.qml new file mode 100644 index 00000000..f7b4ed96 --- /dev/null +++ b/accounts/gkleen@sif/shell/quickshell/SystemTray.qml | |||
| @@ -0,0 +1,201 @@ | |||
| 1 | import QtQuick | ||
| 2 | import QtQuick.Effects | ||
| 3 | import Quickshell | ||
| 4 | import Quickshell.Widgets | ||
| 5 | import Quickshell.Services.SystemTray | ||
| 6 | |||
| 7 | Item { | ||
| 8 | anchors.verticalCenter: parent.verticalCenter | ||
| 9 | width: systemTrayRow.childrenRect.width | ||
| 10 | height: parent.height | ||
| 11 | clip: true | ||
| 12 | |||
| 13 | Row { | ||
| 14 | id: systemTrayRow | ||
| 15 | anchors.centerIn: parent | ||
| 16 | width: childrenRect.width | ||
| 17 | height: parent.height | ||
| 18 | spacing: 0 | ||
| 19 | |||
| 20 | Repeater { | ||
| 21 | model: ScriptModel { | ||
| 22 | values: { | ||
| 23 | var trayItems = Array.from(SystemTray.items.values).filter(item => item.status !== Status.Passive); | ||
| 24 | trayItems.sort((a, b) => a.category !== b.category ? b.category - a.category : a.id.localeCompare(b.id)) | ||
| 25 | return trayItems; | ||
| 26 | } | ||
| 27 | } | ||
| 28 | |||
| 29 | delegate: Item { | ||
| 30 | id: trayItemWrapper | ||
| 31 | |||
| 32 | required property var modelData | ||
| 33 | required property int index | ||
| 34 | |||
| 35 | property var trayItem: modelData | ||
| 36 | property string iconSource: { | ||
| 37 | let icon = trayItem && trayItem.icon | ||
| 38 | if (typeof icon === 'string' || icon instanceof String) { | ||
| 39 | if (icon.includes("?path=")) { | ||
| 40 | const split = icon.split("?path=") | ||
| 41 | if (split.length !== 2) | ||
| 42 | return icon | ||
| 43 | const name = split[0] | ||
| 44 | const path = split[1] | ||
| 45 | const fileName = name.substring( | ||
| 46 | name.lastIndexOf("/") + 1) | ||
| 47 | return `file://${path}/${fileName}` | ||
| 48 | } | ||
| 49 | return icon | ||
| 50 | } | ||
| 51 | return "" | ||
| 52 | } | ||
| 53 | |||
| 54 | width: icon.width + 6 | ||
| 55 | height: parent.height | ||
| 56 | anchors.verticalCenter: parent.verticalCenter | ||
| 57 | |||
| 58 | WrapperMouseArea { | ||
| 59 | id: trayItemArea | ||
| 60 | |||
| 61 | anchors.fill: parent | ||
| 62 | acceptedButtons: Qt.LeftButton | Qt.RightButton | ||
| 63 | hoverEnabled: true | ||
| 64 | cursorShape: trayItem.onlyMenu ? Qt.ArrowCursor : Qt.PointingHandCursor | ||
| 65 | onClicked: mouse => { | ||
| 66 | if (!trayItem) | ||
| 67 | return | ||
| 68 | |||
| 69 | if (mouse.button === Qt.LeftButton | ||
| 70 | && !trayItem.onlyMenu) { | ||
| 71 | trayItem.activate() | ||
| 72 | return | ||
| 73 | } | ||
| 74 | |||
| 75 | if (trayItem.hasMenu) { | ||
| 76 | var globalPos = mapToGlobal(0, 0) | ||
| 77 | var currentScreen = screen || Screen | ||
| 78 | var screenX = currentScreen.x || 0 | ||
| 79 | var relativeX = globalPos.x - screenX | ||
| 80 | menuAnchor.menu = trayItem.menu | ||
| 81 | menuAnchor.anchor.window = bar | ||
| 82 | menuAnchor.anchor.rect = Qt.rect( | ||
| 83 | relativeX, | ||
| 84 | 21, | ||
| 85 | parent.width, 1) | ||
| 86 | menuAnchor.open() | ||
| 87 | } | ||
| 88 | } | ||
| 89 | |||
| 90 | Rectangle { | ||
| 91 | anchors.fill: parent | ||
| 92 | color: { | ||
| 93 | if (trayItemArea.containsMouse) | ||
| 94 | return "#33808080"; | ||
| 95 | return "transparent"; | ||
| 96 | } | ||
| 97 | |||
| 98 | Item { | ||
| 99 | anchors.fill: parent | ||
| 100 | |||
| 101 | layer.enabled: true | ||
| 102 | layer.effect: MultiEffect { | ||
| 103 | colorization: 1 | ||
| 104 | colorizationColor: "#555" | ||
| 105 | } | ||
| 106 | |||
| 107 | IconImage { | ||
| 108 | id: icon | ||
| 109 | |||
| 110 | anchors.centerIn: parent | ||
| 111 | implicitSize: 16 | ||
| 112 | source: trayItemWrapper.iconSource | ||
| 113 | asynchronous: true | ||
| 114 | smooth: true | ||
| 115 | mipmap: true | ||
| 116 | |||
| 117 | layer.enabled: true | ||
| 118 | layer.effect: MultiEffect { | ||
| 119 | id: effect | ||
| 120 | |||
| 121 | brightness: 1 | ||
| 122 | } | ||
| 123 | } | ||
| 124 | } | ||
| 125 | } | ||
| 126 | } | ||
| 127 | |||
| 128 | PopupWindow { | ||
| 129 | id: tooltip | ||
| 130 | |||
| 131 | property bool nextVisible: (trayItem.tooltipTitle || trayItem.tooltipDescription) && (trayItemArea.containsMouse || tooltipMouseArea.containsMouse) && !menuAnchor.visible | ||
| 132 | |||
| 133 | anchor { | ||
| 134 | item: trayItemArea | ||
| 135 | edges: Edges.Bottom | ||
| 136 | } | ||
| 137 | |||
| 138 | visible: false | ||
| 139 | onNextVisibleChanged: hangTimer.restart() | ||
| 140 | |||
| 141 | Timer { | ||
| 142 | id: hangTimer | ||
| 143 | interval: 100 | ||
| 144 | onTriggered: tooltip.visible = tooltip.nextVisible | ||
| 145 | } | ||
| 146 | |||
| 147 | color: "black" | ||
| 148 | |||
| 149 | implicitWidth: Math.max(tooltipTitle.contentWidth, tooltipDescription.contentWidth) + 16 | ||
| 150 | implicitHeight: (trayItem.tooltipTitle ? tooltipTitle.contentHeight : 0) + (trayItem.tooltipDescription ? tooltipDescription.contentHeight : 0) + 16 | ||
| 151 | |||
| 152 | WrapperMouseArea { | ||
| 153 | id: tooltipMouseArea | ||
| 154 | |||
| 155 | hoverEnabled: true | ||
| 156 | enabled: true | ||
| 157 | |||
| 158 | margin: 4 | ||
| 159 | |||
| 160 | anchors.fill: parent | ||
| 161 | |||
| 162 | Item { | ||
| 163 | anchors.fill: parent | ||
| 164 | |||
| 165 | Column { | ||
| 166 | anchors.centerIn: parent | ||
| 167 | Text { | ||
| 168 | id: tooltipTitle | ||
| 169 | |||
| 170 | enabled: trayItem.tooltipTitle | ||
| 171 | |||
| 172 | font.pointSize: 10 | ||
| 173 | font.family: "Fira Sans" | ||
| 174 | font.bold: true | ||
| 175 | color: "white" | ||
| 176 | |||
| 177 | text: trayItem.tooltipTitle | ||
| 178 | } | ||
| 179 | |||
| 180 | Text { | ||
| 181 | id: tooltipDescription | ||
| 182 | |||
| 183 | enabled: trayItem.tooltipDescription | ||
| 184 | |||
| 185 | font.pointSize: 10 | ||
| 186 | font.family: "Fira Sans" | ||
| 187 | color: "white" | ||
| 188 | |||
| 189 | text: trayItem.tooltipDescription | ||
| 190 | } | ||
| 191 | } | ||
| 192 | } | ||
| 193 | } | ||
| 194 | } | ||
| 195 | } | ||
| 196 | } | ||
| 197 | } | ||
| 198 | QsMenuAnchor { | ||
| 199 | id: menuAnchor | ||
| 200 | } | ||
| 201 | } | ||
diff --git a/accounts/gkleen@sif/shell/quickshell/UnixIPC.qml b/accounts/gkleen@sif/shell/quickshell/UnixIPC.qml new file mode 100644 index 00000000..05a40dbc --- /dev/null +++ b/accounts/gkleen@sif/shell/quickshell/UnixIPC.qml | |||
| @@ -0,0 +1,97 @@ | |||
| 1 | import Quickshell | ||
| 2 | import Quickshell.Io | ||
| 3 | import Quickshell.Services.Pipewire | ||
| 4 | import Quickshell.Services.Mpris | ||
| 5 | import qs.Services | ||
| 6 | import Custom as Custom | ||
| 7 | import QtQml | ||
| 8 | |||
| 9 | Scope { | ||
| 10 | id: root | ||
| 11 | |||
| 12 | SocketServer { | ||
| 13 | active: true | ||
| 14 | path: `${Quickshell.env("XDG_RUNTIME_DIR")}/shell.sock` | ||
| 15 | handler: Socket { | ||
| 16 | parser: SplitParser { | ||
| 17 | onRead: line => { | ||
| 18 | const command = (() => { | ||
| 19 | try { | ||
| 20 | return JSON.parse(line); | ||
| 21 | } catch (e) { | ||
| 22 | console.warn("UnixIPC: Failed to parse command:", line, e); | ||
| 23 | } | ||
| 24 | })(); | ||
| 25 | if (!command) | ||
| 26 | return; | ||
| 27 | |||
| 28 | if (command.Volume) | ||
| 29 | root.onCommandVolume(command.Volume); | ||
| 30 | else if (command.Brightness) | ||
| 31 | root.onCommandBrightness(command.Brightness); | ||
| 32 | else if (command.LockSession) | ||
| 33 | Custom.Systemd.lockSession(); | ||
| 34 | else if (command.Suspend) | ||
| 35 | Custom.Systemd.suspend(); | ||
| 36 | else if (command.Hibernate) | ||
| 37 | Custom.Systemd.hibernate(); | ||
| 38 | else if (command.Mpris) | ||
| 39 | root.onCommandMpris(command.Mpris); | ||
| 40 | else if (command.Notifications) | ||
| 41 | root.onCommandNotifications(command.Notifications); | ||
| 42 | else | ||
| 43 | console.warn("UnixIPC: Command not handled:", JSON.stringify(command)); | ||
| 44 | } | ||
| 45 | } | ||
| 46 | |||
| 47 | onError: e => { | ||
| 48 | if (e == 1) | ||
| 49 | return; | ||
| 50 | console.warn("QLocalSocket::LocalSocketError", e); | ||
| 51 | } | ||
| 52 | } | ||
| 53 | } | ||
| 54 | |||
| 55 | PwObjectTracker { | ||
| 56 | objects: [ Pipewire.defaultAudioSink, Pipewire.defaultAudioSource ] | ||
| 57 | } | ||
| 58 | function onCommandVolume(command) { | ||
| 59 | if (command.muted === "toggle") | ||
| 60 | Pipewire.defaultAudioSink.audio.muted = !Pipewire.defaultAudioSink.audio.muted; | ||
| 61 | if (command.volume === "up") | ||
| 62 | Pipewire.defaultAudioSink.audio.volume += 0.02; | ||
| 63 | if (command.volume === "down") | ||
| 64 | Pipewire.defaultAudioSink.audio.volume -= 0.02; | ||
| 65 | |||
| 66 | if (command["mic-muted"] === "toggle") | ||
| 67 | Pipewire.defaultAudioSource.audio.muted = !Pipewire.defaultAudioSource.audio.muted; | ||
| 68 | } | ||
| 69 | |||
| 70 | function onCommandBrightness(command) { | ||
| 71 | if (command === "up") | ||
| 72 | Brightness.currBrightness += 0.02 | ||
| 73 | if (command === "down") | ||
| 74 | Brightness.currBrightness -= 0.02 | ||
| 75 | } | ||
| 76 | |||
| 77 | function onCommandMpris(command) { | ||
| 78 | if (command.PauseAll) | ||
| 79 | Array.from(MprisProxy.players).forEach(player => { | ||
| 80 | if (player.canPause && player.isPlaying) | ||
| 81 | player.pause(); | ||
| 82 | }); | ||
| 83 | } | ||
| 84 | Component.onCompleted: { (_ => {})(MprisProxy.players); } | ||
| 85 | |||
| 86 | function onCommandNotifications(command) { | ||
| 87 | if (command.DismissGroup && NotificationManager.active) { | ||
| 88 | if (NotificationManager.groups.length > 0) | ||
| 89 | for (const notif of [...NotificationManager.groups[0]]) | ||
| 90 | notif.dismiss(); | ||
| 91 | } | ||
| 92 | if (command.DismissAll && NotificationManager.active) { | ||
| 93 | for (const notif of [...NotificationManager.trackedNotifications.values]) | ||
| 94 | notif.dismiss(); | ||
| 95 | } | ||
| 96 | } | ||
| 97 | } | ||
diff --git a/accounts/gkleen@sif/shell/quickshell/VolumeOSD.qml b/accounts/gkleen@sif/shell/quickshell/VolumeOSD.qml new file mode 100644 index 00000000..653f4763 --- /dev/null +++ b/accounts/gkleen@sif/shell/quickshell/VolumeOSD.qml | |||
| @@ -0,0 +1,163 @@ | |||
| 1 | import QtQuick | ||
| 2 | import QtQuick.Layouts | ||
| 3 | import Quickshell | ||
| 4 | import Quickshell.Services.Pipewire | ||
| 5 | import Quickshell.Widgets | ||
| 6 | |||
| 7 | Scope { | ||
| 8 | id: root | ||
| 9 | |||
| 10 | property string show: "" | ||
| 11 | property bool inhibited: true | ||
| 12 | |||
| 13 | PwObjectTracker { | ||
| 14 | objects: [ Pipewire.defaultAudioSink, Pipewire.defaultAudioSource ] | ||
| 15 | } | ||
| 16 | |||
| 17 | Connections { | ||
| 18 | enabled: Pipewire.defaultAudioSink | ||
| 19 | target: Pipewire.defaultAudioSink?.audio | ||
| 20 | |||
| 21 | function onVolumeChanged() { | ||
| 22 | root.show = "sink"; | ||
| 23 | hideTimer.restart(); | ||
| 24 | } | ||
| 25 | function onMutedChanged() { | ||
| 26 | root.show = "sink"; | ||
| 27 | hideTimer.restart(); | ||
| 28 | } | ||
| 29 | } | ||
| 30 | |||
| 31 | Connections { | ||
| 32 | enabled: Pipewire.defaultAudioSource | ||
| 33 | target: Pipewire.defaultAudioSource?.audio | ||
| 34 | |||
| 35 | function onVolumeChanged() { | ||
| 36 | root.show = "source"; | ||
| 37 | hideTimer.restart(); | ||
| 38 | } | ||
| 39 | function onMutedChanged() { | ||
| 40 | root.show = "source"; | ||
| 41 | hideTimer.restart(); | ||
| 42 | } | ||
| 43 | } | ||
| 44 | |||
| 45 | onShowChanged: { | ||
| 46 | if (show) | ||
| 47 | hideTimer.restart(); | ||
| 48 | } | ||
| 49 | |||
| 50 | Timer { | ||
| 51 | id: hideTimer | ||
| 52 | interval: 1000 | ||
| 53 | onTriggered: root.show = "" | ||
| 54 | } | ||
| 55 | |||
| 56 | Timer { | ||
| 57 | id: startInhibit | ||
| 58 | interval: 100 | ||
| 59 | running: true | ||
| 60 | onTriggered: { | ||
| 61 | root.show = ""; | ||
| 62 | root.inhibited = false; | ||
| 63 | } | ||
| 64 | } | ||
| 65 | |||
| 66 | LazyLoader { | ||
| 67 | active: root.show && !root.inhibited | ||
| 68 | |||
| 69 | Variants { | ||
| 70 | model: Quickshell.screens | ||
| 71 | |||
| 72 | delegate: Scope { | ||
| 73 | id: screenScope | ||
| 74 | |||
| 75 | required property var modelData | ||
| 76 | |||
| 77 | PanelWindow { | ||
| 78 | id: window | ||
| 79 | |||
| 80 | screen: screenScope.modelData | ||
| 81 | |||
| 82 | anchors.top: true | ||
| 83 | margins.top: screen.height / 2 - 50 + 3.5 | ||
| 84 | exclusiveZone: 0 | ||
| 85 | exclusionMode: ExclusionMode.Ignore | ||
| 86 | |||
| 87 | implicitWidth: 400 | ||
| 88 | implicitHeight: 50 | ||
| 89 | |||
| 90 | mask: Region {} | ||
| 91 | |||
| 92 | color: "transparent" | ||
| 93 | |||
| 94 | Rectangle { | ||
| 95 | anchors.fill: parent | ||
| 96 | color: Qt.rgba(0, 0, 0, 0.75) | ||
| 97 | } | ||
| 98 | |||
| 99 | RowLayout { | ||
| 100 | id: layout | ||
| 101 | |||
| 102 | anchors.centerIn: parent | ||
| 103 | |||
| 104 | height: 50 - 8*2 | ||
| 105 | width: 400 - 8*2 | ||
| 106 | |||
| 107 | MaterialDesignIcon { | ||
| 108 | id: volumeIcon | ||
| 109 | |||
| 110 | implicitWidth: parent.height | ||
| 111 | implicitHeight: parent.height | ||
| 112 | |||
| 113 | icon: { | ||
| 114 | if (root.show == "sink") { | ||
| 115 | if (!Pipewire.defaultAudioSink || Pipewire.defaultAudioSink.audio.muted) | ||
| 116 | return "volume-off"; | ||
| 117 | if (Pipewire.defaultAudioSink.audio.volume <= 0.33) | ||
| 118 | return "volume-low"; | ||
| 119 | if (Pipewire.defaultAudioSink.audio.volume <= 0.67) | ||
| 120 | return "volume-medium"; | ||
| 121 | return "volume-high"; | ||
| 122 | } else if (root.show == "source") { | ||
| 123 | if (!Pipewire.defaultAudioSource || Pipewire.defaultAudioSource.audio.muted) | ||
| 124 | return "microphone-off"; | ||
| 125 | if (Pipewire.defaultAudioSource.audio.volume > 1) | ||
| 126 | return "microphone-plus"; | ||
| 127 | return "microphone"; | ||
| 128 | } | ||
| 129 | return "volume-high"; | ||
| 130 | } | ||
| 131 | } | ||
| 132 | |||
| 133 | Rectangle { | ||
| 134 | Layout.fillWidth: true | ||
| 135 | |||
| 136 | implicitHeight: 10 | ||
| 137 | |||
| 138 | color: "#50ffffff" | ||
| 139 | |||
| 140 | Rectangle { | ||
| 141 | anchors { | ||
| 142 | left: parent.left | ||
| 143 | top: parent.top | ||
| 144 | bottom: parent.bottom | ||
| 145 | } | ||
| 146 | |||
| 147 | color: Pipewire.defaultAudioSink?.audio.muted ? "#70ffffff" : "white" | ||
| 148 | |||
| 149 | implicitWidth: { | ||
| 150 | if (root.show == "sink") | ||
| 151 | return parent.width * (Pipewire.defaultAudioSink?.audio.volume ?? 0); | ||
| 152 | else if (root.show == "source") | ||
| 153 | return parent.width * Math.min(1, (Pipewire.defaultAudioSource?.audio.volume ?? 0)); | ||
| 154 | return 0; | ||
| 155 | } | ||
| 156 | } | ||
| 157 | } | ||
| 158 | } | ||
| 159 | } | ||
| 160 | } | ||
| 161 | } | ||
| 162 | } | ||
| 163 | } | ||
diff --git a/accounts/gkleen@sif/shell/quickshell/WallpaperBackground.qml b/accounts/gkleen@sif/shell/quickshell/WallpaperBackground.qml new file mode 100644 index 00000000..4f85a900 --- /dev/null +++ b/accounts/gkleen@sif/shell/quickshell/WallpaperBackground.qml | |||
| @@ -0,0 +1,89 @@ | |||
| 1 | import QtQuick | ||
| 2 | import Quickshell | ||
| 3 | import qs.Services | ||
| 4 | |||
| 5 | Item { | ||
| 6 | id: root | ||
| 7 | |||
| 8 | anchors.fill: parent | ||
| 9 | |||
| 10 | required property string screen | ||
| 11 | |||
| 12 | property Img current: one | ||
| 13 | property string source: selector.selected | ||
| 14 | |||
| 15 | WallpaperSelector { | ||
| 16 | id: selector | ||
| 17 | seed: screen | ||
| 18 | } | ||
| 19 | |||
| 20 | onSourceChanged: { | ||
| 21 | if (!source) | ||
| 22 | current = null; | ||
| 23 | else if (current === one) | ||
| 24 | two.update() | ||
| 25 | else | ||
| 26 | one.update() | ||
| 27 | } | ||
| 28 | |||
| 29 | Img { id: one } | ||
| 30 | Img { id: two } | ||
| 31 | |||
| 32 | component Img: Image { | ||
| 33 | id: img | ||
| 34 | |||
| 35 | function update() { | ||
| 36 | source = root.source || "" | ||
| 37 | } | ||
| 38 | |||
| 39 | anchors.fill: parent | ||
| 40 | fillMode: Image.PreserveAspectCrop | ||
| 41 | smooth: true | ||
| 42 | asynchronous: true | ||
| 43 | cache: false | ||
| 44 | |||
| 45 | opacity: 0 | ||
| 46 | |||
| 47 | onStatusChanged: { | ||
| 48 | if (status === Image.Ready) { | ||
| 49 | root.current = this | ||
| 50 | } | ||
| 51 | } | ||
| 52 | |||
| 53 | states: State { | ||
| 54 | name: "visible" | ||
| 55 | when: root.current === img | ||
| 56 | |||
| 57 | PropertyChanges { | ||
| 58 | img.opacity: 1 | ||
| 59 | } | ||
| 60 | StateChangeScript { | ||
| 61 | name: "unloadOther" | ||
| 62 | script: { | ||
| 63 | if (img === one) | ||
| 64 | two.source = "" | ||
| 65 | if (img === two) | ||
| 66 | one.source = "" | ||
| 67 | } | ||
| 68 | } | ||
| 69 | } | ||
| 70 | |||
| 71 | transitions: Transition { | ||
| 72 | SequentialAnimation { | ||
| 73 | NumberAnimation { | ||
| 74 | target: img | ||
| 75 | properties: "opacity" | ||
| 76 | duration: { | ||
| 77 | if (img === one && two.source == "" || img === two && one.source == "") | ||
| 78 | return 0; | ||
| 79 | return 5000; | ||
| 80 | } | ||
| 81 | easing.type: Easing.OutCubic | ||
| 82 | } | ||
| 83 | ScriptAction { | ||
| 84 | scriptName: "unloadOther" | ||
| 85 | } | ||
| 86 | } | ||
| 87 | } | ||
| 88 | } | ||
| 89 | } | ||
diff --git a/accounts/gkleen@sif/shell/quickshell/WaylandInhibitorWidget.qml b/accounts/gkleen@sif/shell/quickshell/WaylandInhibitorWidget.qml new file mode 100644 index 00000000..0512ff51 --- /dev/null +++ b/accounts/gkleen@sif/shell/quickshell/WaylandInhibitorWidget.qml | |||
| @@ -0,0 +1,56 @@ | |||
| 1 | import Quickshell | ||
| 2 | import QtQuick | ||
| 3 | import Quickshell.Widgets | ||
| 4 | import Quickshell.Wayland | ||
| 5 | import qs.Services | ||
| 6 | |||
| 7 | Item { | ||
| 8 | id: root | ||
| 9 | |||
| 10 | required property var window | ||
| 11 | |||
| 12 | width: icon.width + 8 | ||
| 13 | height: parent.height | ||
| 14 | anchors.verticalCenter: parent.verticalCenter | ||
| 15 | |||
| 16 | IdleInhibitor { | ||
| 17 | id: inhibitor | ||
| 18 | enabled: InhibitorState.waylandIdleInhibited | ||
| 19 | window: root.window | ||
| 20 | } | ||
| 21 | |||
| 22 | WrapperMouseArea { | ||
| 23 | id: widgetMouseArea | ||
| 24 | |||
| 25 | anchors.fill: parent | ||
| 26 | |||
| 27 | hoverEnabled: true | ||
| 28 | cursorShape: Qt.PointingHandCursor | ||
| 29 | |||
| 30 | onClicked: InhibitorState.waylandIdleInhibited = !InhibitorState.waylandIdleInhibited | ||
| 31 | |||
| 32 | Rectangle { | ||
| 33 | anchors.fill: parent | ||
| 34 | color: { | ||
| 35 | if (widgetMouseArea.containsMouse) { | ||
| 36 | return "#33808080"; | ||
| 37 | } | ||
| 38 | return "transparent"; | ||
| 39 | } | ||
| 40 | |||
| 41 | Item { | ||
| 42 | anchors.fill: parent | ||
| 43 | |||
| 44 | MaterialDesignIcon { | ||
| 45 | id: icon | ||
| 46 | |||
| 47 | implicitSize: 14 | ||
| 48 | anchors.centerIn: parent | ||
| 49 | |||
| 50 | icon: inhibitor.enabled ? "eye" : "eye-off" | ||
| 51 | color: inhibitor.enabled ? "white" : "#555" | ||
| 52 | } | ||
| 53 | } | ||
| 54 | } | ||
| 55 | } | ||
| 56 | } | ||
diff --git a/accounts/gkleen@sif/shell/quickshell/WorkspaceSwitcher.qml b/accounts/gkleen@sif/shell/quickshell/WorkspaceSwitcher.qml new file mode 100644 index 00000000..3ae94346 --- /dev/null +++ b/accounts/gkleen@sif/shell/quickshell/WorkspaceSwitcher.qml | |||
| @@ -0,0 +1,204 @@ | |||
| 1 | import Quickshell | ||
| 2 | import QtQuick | ||
| 3 | import qs.Services | ||
| 4 | import Quickshell.Widgets | ||
| 5 | import QtQuick.Layouts | ||
| 6 | |||
| 7 | Row { | ||
| 8 | id: workspaces | ||
| 9 | |||
| 10 | required property var screen | ||
| 11 | |||
| 12 | property var ignoreWorkspaces: @ignore_workspaces@ | ||
| 13 | |||
| 14 | height: parent.height | ||
| 15 | anchors.verticalCenter: parent.verticalCenter | ||
| 16 | spacing: 0 | ||
| 17 | |||
| 18 | Repeater { | ||
| 19 | model: ScriptModel { | ||
| 20 | values: { | ||
| 21 | let currWorkspaces = NiriService.workspaces; | ||
| 22 | const ignoreWorkspaces = Array.from(workspaces.ignoreWorkspaces); | ||
| 23 | currWorkspaces = currWorkspaces.filter(ws => ws.output == workspaces.screen.name).filter(ws => ws.is_active || ignoreWorkspaces.every(iws => iws !== ws.name)); | ||
| 24 | currWorkspaces.sort((a, b) => { | ||
| 25 | if (NiriService.outputs?.[a.output]?.logical?.x !== NiriService.outputs?.[b.output]?.logical?.x) | ||
| 26 | return NiriService.outputs?.[a.output]?.logical?.x - NiriService.outputs?.[b.output]?.logical?.x | ||
| 27 | if (NiriService.outputs?.[a.output]?.logical?.y !== NiriService.outputs?.[b.output]?.logical?.y) | ||
| 28 | return NiriService.outputs?.[a.output]?.logical?.y - NiriService.outputs?.[b.output]?.logical?.y | ||
| 29 | return a.idx - b.idx; | ||
| 30 | }); | ||
| 31 | return currWorkspaces; | ||
| 32 | } | ||
| 33 | } | ||
| 34 | |||
| 35 | Item { | ||
| 36 | id: wsItem | ||
| 37 | |||
| 38 | property var workspaceData: modelData | ||
| 39 | |||
| 40 | width: wsLabel.contentWidth + 8 | ||
| 41 | height: parent.height | ||
| 42 | anchors.verticalCenter: parent.verticalCenter | ||
| 43 | |||
| 44 | WrapperMouseArea { | ||
| 45 | id: mouseArea | ||
| 46 | |||
| 47 | anchors.fill: parent | ||
| 48 | |||
| 49 | hoverEnabled: true | ||
| 50 | cursorShape: Qt.PointingHandCursor | ||
| 51 | enabled: true | ||
| 52 | onClicked: { | ||
| 53 | NiriService.sendCommand({ "Action": { "FocusWorkspace": { "reference": { "Id": workspaceData.id } } } }, _ => {}); | ||
| 54 | } | ||
| 55 | |||
| 56 | Rectangle { | ||
| 57 | anchors.fill: parent | ||
| 58 | |||
| 59 | color: { | ||
| 60 | if (mouseArea.containsMouse) { | ||
| 61 | return "#33808080"; | ||
| 62 | } | ||
| 63 | return "transparent"; | ||
| 64 | } | ||
| 65 | |||
| 66 | Text { | ||
| 67 | id: wsLabel | ||
| 68 | |||
| 69 | anchors.centerIn: parent | ||
| 70 | |||
| 71 | font.pointSize: 10 | ||
| 72 | font.family: "Fira Sans" | ||
| 73 | color: { | ||
| 74 | if (workspaceData.is_active) | ||
| 75 | return "#23fd00"; | ||
| 76 | if (workspaceData.active_window_id === null) | ||
| 77 | return "#555"; | ||
| 78 | return "white"; | ||
| 79 | } | ||
| 80 | |||
| 81 | text: workspaceData.name ? workspaceData.name : workspaceData.idx | ||
| 82 | } | ||
| 83 | } | ||
| 84 | } | ||
| 85 | |||
| 86 | PopupWindow { | ||
| 87 | id: tooltip | ||
| 88 | |||
| 89 | property bool nextVisible: (mouseArea.containsMouse || tooltipMouseArea.containsMouse) && [...windowsModel.values].length > 0 | ||
| 90 | |||
| 91 | anchor { | ||
| 92 | item: mouseArea | ||
| 93 | edges: Edges.Bottom | Edges.Left | ||
| 94 | } | ||
| 95 | visible: false | ||
| 96 | |||
| 97 | onNextVisibleChanged: hangTimer.restart() | ||
| 98 | |||
| 99 | Timer { | ||
| 100 | id: hangTimer | ||
| 101 | interval: 100 | ||
| 102 | onTriggered: tooltip.visible = tooltip.nextVisible | ||
| 103 | } | ||
| 104 | |||
| 105 | implicitWidth: tooltipContent.implicitWidth | ||
| 106 | implicitHeight: tooltipContent.implicitHeight | ||
| 107 | color: "black" | ||
| 108 | |||
| 109 | WrapperMouseArea { | ||
| 110 | id: tooltipMouseArea | ||
| 111 | |||
| 112 | hoverEnabled: true | ||
| 113 | enabled: true | ||
| 114 | |||
| 115 | anchors.fill: parent | ||
| 116 | |||
| 117 | WrapperItem { | ||
| 118 | id: tooltipContent | ||
| 119 | |||
| 120 | margin: 0 | ||
| 121 | |||
| 122 | ColumnLayout { | ||
| 123 | spacing: 0 | ||
| 124 | |||
| 125 | Repeater { | ||
| 126 | model: ScriptModel { | ||
| 127 | id: windowsModel | ||
| 128 | |||
| 129 | values: { | ||
| 130 | let currWindows = Array.from(NiriService.windows).filter(win => win.workspace_id == wsItem.workspaceData.id); | ||
| 131 | currWindows.sort((a, b) => { | ||
| 132 | if (a.is_floating !== b.is_floating) | ||
| 133 | return b.is_floating - a.is_floating; | ||
| 134 | if (a.layout.tile_pos_in_workspace_view?.[0] !== b.layout.tile_pos_in_workspace_view?.[0]) | ||
| 135 | return a.layout.tile_pos_in_workspace_view?.[0] - b.layout.tile_pos_in_workspace_view?.[0] | ||
| 136 | if (a.layout.tile_pos_in_workspace_view?.[1] !== b.layout.tile_pos_in_workspace_view?.[1]) | ||
| 137 | return a.layout.tile_pos_in_workspace_view?.[1] - b.layout.tile_pos_in_workspace_view?.[1] | ||
| 138 | if (a.layout.pos_in_scrolling_layout?.[0] !== b.layout.pos_in_scrolling_layout?.[0]) | ||
| 139 | return a.layout.pos_in_scrolling_layout?.[0] - b.layout.pos_in_scrolling_layout?.[0] | ||
| 140 | if (a.layout.pos_in_scrolling_layout?.[1] !== b.layout.pos_in_scrolling_layout?.[1]) | ||
| 141 | return a.layout.pos_in_scrolling_layout?.[1] - b.layout.pos_in_scrolling_layout?.[1] | ||
| 142 | if (a.app_id !== b.app_id) | ||
| 143 | return a.app_id.localeCompare(b.app_id); | ||
| 144 | |||
| 145 | return a.title.localeCompare(b.title); | ||
| 146 | }); | ||
| 147 | return currWindows; | ||
| 148 | } | ||
| 149 | } | ||
| 150 | |||
| 151 | WrapperMouseArea { | ||
| 152 | id: windowMouseArea | ||
| 153 | |||
| 154 | required property int index | ||
| 155 | required property var modelData | ||
| 156 | property var windowData: modelData | ||
| 157 | |||
| 158 | hoverEnabled: true | ||
| 159 | cursorShape: Qt.PointingHandCursor | ||
| 160 | enabled: true | ||
| 161 | |||
| 162 | Layout.fillWidth: true | ||
| 163 | |||
| 164 | onClicked: { | ||
| 165 | NiriService.sendCommand({ "Action": { "FocusWindow": { "id": windowData.id } } }, _ => {}) | ||
| 166 | } | ||
| 167 | |||
| 168 | WrapperRectangle { | ||
| 169 | color: windowMouseArea.containsMouse ? "#33808080" : "transparent"; | ||
| 170 | |||
| 171 | WrapperItem { | ||
| 172 | rightMargin: 8 | ||
| 173 | leftMargin: 8 | ||
| 174 | topMargin: windowMouseArea.index == 0 ? 8 : 4 | ||
| 175 | bottomMargin: windowMouseArea.index == windowsModel.values.length - 1 ? 8 : 4 | ||
| 176 | |||
| 177 | Text { | ||
| 178 | id: windowLabel | ||
| 179 | |||
| 180 | font.pointSize: 10 | ||
| 181 | font.family: "Fira Sans" | ||
| 182 | color: { | ||
| 183 | if (windowData.is_focused) | ||
| 184 | return "#23fd00"; | ||
| 185 | if (NiriService.workspaces.find(ws => ws.id == windowData.workspace_id)?.active_window_id == windowData.id) | ||
| 186 | return "white"; | ||
| 187 | return "#555"; | ||
| 188 | } | ||
| 189 | |||
| 190 | text: windowData.title | ||
| 191 | |||
| 192 | horizontalAlignment: Text.AlignLeft | ||
| 193 | } | ||
| 194 | } | ||
| 195 | } | ||
| 196 | } | ||
| 197 | } | ||
| 198 | } | ||
| 199 | } | ||
| 200 | } | ||
| 201 | } | ||
| 202 | } | ||
| 203 | } | ||
| 204 | } | ||
diff --git a/accounts/gkleen@sif/shell/quickshell/WorktimeWidget.qml b/accounts/gkleen@sif/shell/quickshell/WorktimeWidget.qml new file mode 100644 index 00000000..04bcc581 --- /dev/null +++ b/accounts/gkleen@sif/shell/quickshell/WorktimeWidget.qml | |||
| @@ -0,0 +1,120 @@ | |||
| 1 | import QtQml | ||
| 2 | import Quickshell | ||
| 3 | import Quickshell.Io | ||
| 4 | import QtQuick | ||
| 5 | import Quickshell.Widgets | ||
| 6 | |||
| 7 | Item { | ||
| 8 | id: root | ||
| 9 | |||
| 10 | required property string command | ||
| 11 | property var state: null | ||
| 12 | |||
| 13 | height: parent.height | ||
| 14 | width: label.contentWidth + 8 | ||
| 15 | anchors.verticalCenter: parent.verticalCenter | ||
| 16 | |||
| 17 | Process { | ||
| 18 | id: process | ||
| 19 | running: true | ||
| 20 | command: [ @worktime@, root.command, "--waybar" ] | ||
| 21 | stdout: StdioCollector { | ||
| 22 | id: processCollector | ||
| 23 | onStreamFinished: { | ||
| 24 | try { | ||
| 25 | root.state = JSON.parse(processCollector.text); | ||
| 26 | } catch (e) { | ||
| 27 | console.warn("Worktime: Failed to parse output:", processCollector.text, e); | ||
| 28 | } | ||
| 29 | } | ||
| 30 | } | ||
| 31 | } | ||
| 32 | |||
| 33 | Timer { | ||
| 34 | running: true | ||
| 35 | interval: 60 | ||
| 36 | repeat: true | ||
| 37 | onTriggered: process.running = true | ||
| 38 | } | ||
| 39 | |||
| 40 | WrapperMouseArea { | ||
| 41 | id: mouseArea | ||
| 42 | |||
| 43 | anchors.fill: parent | ||
| 44 | |||
| 45 | enabled: true | ||
| 46 | hoverEnabled: true | ||
| 47 | |||
| 48 | Item { | ||
| 49 | anchors.fill: parent | ||
| 50 | |||
| 51 | Text { | ||
| 52 | id: label | ||
| 53 | |||
| 54 | anchors.centerIn: parent | ||
| 55 | |||
| 56 | visible: root.state?.text ?? false | ||
| 57 | text: root.state?.text ?? "" | ||
| 58 | |||
| 59 | font.pointSize: 10 | ||
| 60 | font.family: "Fira Sans" | ||
| 61 | color: { | ||
| 62 | if (root.state?.class == "running") | ||
| 63 | return "white"; | ||
| 64 | if (root.state?.class == "over") | ||
| 65 | return "#f28a21"; | ||
| 66 | return "#555"; | ||
| 67 | } | ||
| 68 | } | ||
| 69 | } | ||
| 70 | } | ||
| 71 | |||
| 72 | PopupWindow { | ||
| 73 | id: tooltip | ||
| 74 | |||
| 75 | property bool nextVisible: Boolean(root.state?.tooltip ?? false) && (mouseArea.containsMouse || tooltipMouseArea.containsMouse) | ||
| 76 | |||
| 77 | anchor { | ||
| 78 | item: mouseArea | ||
| 79 | edges: Edges.Bottom | Edges.Left | ||
| 80 | } | ||
| 81 | visible: false | ||
| 82 | |||
| 83 | onNextVisibleChanged: hangTimer.restart() | ||
| 84 | |||
| 85 | Timer { | ||
| 86 | id: hangTimer | ||
| 87 | interval: 100 | ||
| 88 | onTriggered: tooltip.visible = tooltip.nextVisible | ||
| 89 | } | ||
| 90 | |||
| 91 | implicitWidth: tooltipText.contentWidth + 16 | ||
| 92 | implicitHeight: tooltipText.contentHeight + 16 | ||
| 93 | color: "black" | ||
| 94 | |||
| 95 | WrapperMouseArea { | ||
| 96 | id: tooltipMouseArea | ||
| 97 | |||
| 98 | enabled: true | ||
| 99 | hoverEnabled: true | ||
| 100 | |||
| 101 | anchors.fill: parent | ||
| 102 | |||
| 103 | Item { | ||
| 104 | anchors.fill: parent | ||
| 105 | |||
| 106 | Text { | ||
| 107 | id: tooltipText | ||
| 108 | |||
| 109 | anchors.centerIn: parent | ||
| 110 | |||
| 111 | font.pointSize: 10 | ||
| 112 | font.family: "Fira Sans" | ||
| 113 | color: "white" | ||
| 114 | |||
| 115 | text: root.state?.tooltip ?? "" | ||
| 116 | } | ||
| 117 | } | ||
| 118 | } | ||
| 119 | } | ||
| 120 | } | ||
diff --git a/accounts/gkleen@sif/shell/quickshell/displaymanager.qml b/accounts/gkleen@sif/shell/quickshell/displaymanager.qml new file mode 100644 index 00000000..b452c03d --- /dev/null +++ b/accounts/gkleen@sif/shell/quickshell/displaymanager.qml | |||
| @@ -0,0 +1,115 @@ | |||
| 1 | //@ pragma UseQApplication | ||
| 2 | |||
| 3 | import Quickshell | ||
| 4 | import Quickshell.Wayland | ||
| 5 | import Quickshell.Io | ||
| 6 | import Quickshell.Services.Greetd | ||
| 7 | import QtQml | ||
| 8 | |||
| 9 | |||
| 10 | ShellRoot { | ||
| 11 | id: displaymanager | ||
| 12 | |||
| 13 | settings.watchFiles: false | ||
| 14 | |||
| 15 | property string currentText: "" | ||
| 16 | property string username: @username@ | ||
| 17 | property list<string> command: @niri_session@ | ||
| 18 | property list<var> messages: [] | ||
| 19 | property bool responseRequired: false | ||
| 20 | property bool responseVisible: false | ||
| 21 | |||
| 22 | signal startAuth() | ||
| 23 | |||
| 24 | onStartAuth: { | ||
| 25 | if (Greetd.state !== GreetdState.Inactive) | ||
| 26 | Greetd.cancelSession(); | ||
| 27 | displaymanager.messages = []; | ||
| 28 | Greetd.createSession(displaymanager.username); | ||
| 29 | } | ||
| 30 | |||
| 31 | Connections { | ||
| 32 | target: Greetd | ||
| 33 | function onStateChanged() { | ||
| 34 | console.log("greetd state: ", GreetdState.toString(Greetd.state)); | ||
| 35 | if (Greetd.state === GreetdState.ReadyToLaunch) | ||
| 36 | Greetd.launch(displaymanager.command); | ||
| 37 | } | ||
| 38 | function onAuthMessage(message: string, error: bool, responseRequired: bool, echoResponse: bool) { | ||
| 39 | displaymanager.responseVisible = echoResponse; | ||
| 40 | displaymanager.responseRequired = responseRequired; | ||
| 41 | displaymanager.messages = Array.from(displaymanager.messages).concat([{ "text": message, "error": error }]); | ||
| 42 | } | ||
| 43 | function onAuthFailure(message: string) { | ||
| 44 | displaymanager.responseRequired = false; | ||
| 45 | displaymanager.messages = Array.from(displaymanager.messages).concat([{ "text": message, "error": true }]); | ||
| 46 | } | ||
| 47 | } | ||
| 48 | |||
| 49 | Component.onCompleted: { | ||
| 50 | if (Greetd.state !== GreetdState.Inactive) | ||
| 51 | Greetd.cancelSession(); | ||
| 52 | } | ||
| 53 | |||
| 54 | Variants { | ||
| 55 | model: Quickshell.screens | ||
| 56 | |||
| 57 | delegate: Scope { | ||
| 58 | id: screenScope | ||
| 59 | |||
| 60 | required property var modelData | ||
| 61 | |||
| 62 | PanelWindow { | ||
| 63 | color: "black" | ||
| 64 | |||
| 65 | screen: screenScope.modelData | ||
| 66 | |||
| 67 | WlrLayershell.keyboardFocus: WlrKeyboardFocus.Exclusive | ||
| 68 | |||
| 69 | anchors.top: true | ||
| 70 | anchors.bottom: true | ||
| 71 | anchors.left: true | ||
| 72 | anchors.right: true | ||
| 73 | |||
| 74 | LockSurface { | ||
| 75 | id: surfaceContent | ||
| 76 | |||
| 77 | screen: screenScope.modelData | ||
| 78 | |||
| 79 | onCurrentTextChanged: displaymanager.currentText = currentText | ||
| 80 | Connections { | ||
| 81 | target: displaymanager | ||
| 82 | function onCurrentTextChanged() { surfaceContent.currentText = displaymanager.currentText; } | ||
| 83 | function onMessagesChanged() { surfaceContent.messages = Array.from(displaymanager.messages); } | ||
| 84 | function onResponseRequiredChanged() { surfaceContent.responseRequired = displaymanager.responseRequired; } | ||
| 85 | function onResponseVisibleChanged() { surfaceContent.responseVisible = displaymanager.responseVisible; } | ||
| 86 | } | ||
| 87 | |||
| 88 | onResponse: responseText => Greetd.respond(responseText); | ||
| 89 | Connections { | ||
| 90 | target: Greetd | ||
| 91 | function onStateChanged() { | ||
| 92 | if (Greetd.state === GreetdState.Authenticating) { | ||
| 93 | surfaceContent.authRunning = true; | ||
| 94 | } else { | ||
| 95 | surfaceContent.authRunning = false; | ||
| 96 | } | ||
| 97 | } | ||
| 98 | } | ||
| 99 | |||
| 100 | onAuthRunningChanged: { | ||
| 101 | if (surfaceContent.authRunning && Greetd.state !== GreetdState.Authenticating) | ||
| 102 | displaymanager.startAuth(); | ||
| 103 | } | ||
| 104 | Component.onCompleted: { | ||
| 105 | surfaceContent.authRunning = Greetd.state === GreetdState.Authenticating | ||
| 106 | surfaceContent.messages = Array.from(displaymanager.messages); | ||
| 107 | surfaceContent.responseVisible = displaymanager.responseVisible; | ||
| 108 | surfaceContent.responseRequired = displaymanager.responseRequired; | ||
| 109 | surfaceContent.currentText = displaymanager.currentText; | ||
| 110 | } | ||
| 111 | } | ||
| 112 | } | ||
| 113 | } | ||
| 114 | } | ||
| 115 | } | ||
diff --git a/accounts/gkleen@sif/shell/quickshell/shell.qml b/accounts/gkleen@sif/shell/quickshell/shell.qml new file mode 100644 index 00000000..fb8b16dc --- /dev/null +++ b/accounts/gkleen@sif/shell/quickshell/shell.qml | |||
| @@ -0,0 +1,53 @@ | |||
| 1 | //@ pragma UseQApplication | ||
| 2 | |||
| 3 | import Quickshell | ||
| 4 | import Quickshell.Wayland | ||
| 5 | |||
| 6 | ShellRoot { | ||
| 7 | settings.watchFiles: false | ||
| 8 | |||
| 9 | Variants { | ||
| 10 | model: Quickshell.screens | ||
| 11 | |||
| 12 | delegate: Scope { | ||
| 13 | id: screenScope | ||
| 14 | |||
| 15 | required property var modelData | ||
| 16 | |||
| 17 | PanelWindow { | ||
| 18 | id: bgWindow | ||
| 19 | |||
| 20 | screen: screenScope.modelData | ||
| 21 | |||
| 22 | WlrLayershell.layer: WlrLayer.Background | ||
| 23 | WlrLayershell.namespace: "background" | ||
| 24 | exclusionMode: ExclusionMode.Ignore | ||
| 25 | |||
| 26 | anchors.top: true | ||
| 27 | anchors.bottom: true | ||
| 28 | anchors.left: true | ||
| 29 | anchors.right: true | ||
| 30 | |||
| 31 | color: "black" | ||
| 32 | |||
| 33 | WallpaperBackground { | ||
| 34 | screen: bgWindow.screen.name | ||
| 35 | } | ||
| 36 | } | ||
| 37 | |||
| 38 | Bar { | ||
| 39 | modelData: screenScope.modelData | ||
| 40 | } | ||
| 41 | } | ||
| 42 | } | ||
| 43 | |||
| 44 | Lockscreen {} | ||
| 45 | NiriIdle {} | ||
| 46 | |||
| 47 | VolumeOSD {} | ||
| 48 | BrightnessOSD {} | ||
| 49 | |||
| 50 | NotificationDisplay {} | ||
| 51 | |||
| 52 | UnixIPC {} | ||
| 53 | } | ||
diff --git a/accounts/gkleen@sif/ssh-hosts.nix b/accounts/gkleen@sif/ssh-hosts.nix index 107f1e76..a250509b 100644 --- a/accounts/gkleen@sif/ssh-hosts.nix +++ b/accounts/gkleen@sif/ssh-hosts.nix | |||
| @@ -1,5 +1,12 @@ | |||
| 1 | { pkgs, ... }: | 1 | { lib, pkgs, ... }: |
| 2 | { | 2 | let |
| 3 | autosshProxyPorts = { | ||
| 4 | "ssh.math.lmu.de" = 8118; | ||
| 5 | "mathw0h" = 8122; | ||
| 6 | "mathw0e" = 8124; | ||
| 7 | }; | ||
| 8 | autosshProxy = host: "${lib.getExe pkgs.socat} - SOCKS4A:127.0.0.1:%h:%p,socksport=${toString autosshProxyPorts.${host}}"; | ||
| 9 | in { | ||
| 3 | "git.ymir" = | 10 | "git.ymir" = |
| 4 | { hostname = "ymir.yggdrasil.li"; | 11 | { hostname = "ymir.yggdrasil.li"; |
| 5 | user = "gitolite"; | 12 | user = "gitolite"; |
| @@ -290,15 +297,15 @@ | |||
| 290 | }; | 297 | }; |
| 291 | "mathw0d" = | 298 | "mathw0d" = |
| 292 | { hostname = "mathw0d.mathinst.loc"; | 299 | { hostname = "mathw0d.mathinst.loc"; |
| 293 | proxyJump = "mathw0h"; | 300 | proxyCommand = autosshProxy "mathw0h"; |
| 294 | }; | 301 | }; |
| 295 | "mathw0e" = | 302 | "mathw0e" = |
| 296 | { hostname = "mathw0e.mathinst.loc"; | 303 | { hostname = "mathw0e.mathinst.loc"; |
| 297 | proxyJump = "mathw0h"; | 304 | proxyCommand = autosshProxy "mathw0h"; |
| 298 | }; | 305 | }; |
| 299 | "mathw0f" = | 306 | "mathw0f" = |
| 300 | { hostname = "mathw0f.mathinst.loc"; | 307 | { hostname = "mathw0f.mathinst.loc"; |
| 301 | proxyJump = "mathw0h"; | 308 | proxyCommand = autosshProxy "mathw0h"; |
| 302 | }; | 309 | }; |
| 303 | "mathw0g" = | 310 | "mathw0g" = |
| 304 | { hostname = "mathw0g.mathinst.loc"; | 311 | { hostname = "mathw0g.mathinst.loc"; |
| @@ -306,8 +313,8 @@ | |||
| 306 | "mathw0h" = | 313 | "mathw0h" = |
| 307 | { hostname = "mathw0h.mathinst.loc"; | 314 | { hostname = "mathw0h.mathinst.loc"; |
| 308 | }; | 315 | }; |
| 309 | "proxy.mathw0g" = | 316 | "proxy.ssh.math.lmu.de" = |
| 310 | { hostname = "mathw0g.mathinst.loc"; | 317 | { hostname = "ssh.math.lmu.de"; |
| 311 | extraOptions = { | 318 | extraOptions = { |
| 312 | ControlPath = "none"; | 319 | ControlPath = "none"; |
| 313 | ExitOnForwardFailure = "yes"; | 320 | ExitOnForwardFailure = "yes"; |
| @@ -317,7 +324,17 @@ | |||
| 317 | }; | 324 | }; |
| 318 | "proxy.mathw0h" = | 325 | "proxy.mathw0h" = |
| 319 | { hostname = "mathw0h.mathinst.loc"; | 326 | { hostname = "mathw0h.mathinst.loc"; |
| 320 | proxyJump = "proxy.mathw0g"; | 327 | proxyCommand = autosshProxy "ssh.math.lmu.de"; |
| 328 | extraOptions = { | ||
| 329 | ControlPath = "none"; | ||
| 330 | ExitOnForwardFailure = "yes"; | ||
| 331 | ServerAliveCountMax = "15"; | ||
| 332 | ServerAliveInterval = "2"; | ||
| 333 | }; | ||
| 334 | }; | ||
| 335 | "proxy.mathw0e" = | ||
| 336 | { hostname = "mathw0e.mathinst.loc"; | ||
| 337 | proxyCommand = autosshProxy "mathw0h"; | ||
| 321 | extraOptions = { | 338 | extraOptions = { |
| 322 | ControlPath = "none"; | 339 | ControlPath = "none"; |
| 323 | ExitOnForwardFailure = "yes"; | 340 | ExitOnForwardFailure = "yes"; |
| @@ -327,7 +344,7 @@ | |||
| 327 | }; | 344 | }; |
| 328 | "vrt-kvm06" = | 345 | "vrt-kvm06" = |
| 329 | { hostname = "vrt-kvm06"; | 346 | { hostname = "vrt-kvm06"; |
| 330 | proxyJump = "mathw0e"; | 347 | proxyCommand = autosshProxy "mathw0e"; |
| 331 | user = "root"; | 348 | user = "root"; |
| 332 | extraOptions = { | 349 | extraOptions = { |
| 333 | PasswordAuthentication = "yes"; | 350 | PasswordAuthentication = "yes"; |
| @@ -336,7 +353,7 @@ | |||
| 336 | }; | 353 | }; |
| 337 | "vrt-kvm05" = | 354 | "vrt-kvm05" = |
| 338 | { hostname = "vrt-kvm05"; | 355 | { hostname = "vrt-kvm05"; |
| 339 | proxyJump = "mathw0e"; | 356 | proxyCommand = autosshProxy "mathw0e"; |
| 340 | user = "root"; | 357 | user = "root"; |
| 341 | extraOptions = { | 358 | extraOptions = { |
| 342 | PasswordAuthentication = "yes"; | 359 | PasswordAuthentication = "yes"; |
| @@ -345,7 +362,7 @@ | |||
| 345 | }; | 362 | }; |
| 346 | "vrt-kvm04" = | 363 | "vrt-kvm04" = |
| 347 | { hostname = "vrt-kvm04"; | 364 | { hostname = "vrt-kvm04"; |
| 348 | proxyJump = "mathw0e"; | 365 | proxyCommand = autosshProxy "mathw0e"; |
| 349 | user = "root"; | 366 | user = "root"; |
| 350 | extraOptions = { | 367 | extraOptions = { |
| 351 | PasswordAuthentication = "yes"; | 368 | PasswordAuthentication = "yes"; |
| @@ -354,7 +371,7 @@ | |||
| 354 | }; | 371 | }; |
| 355 | "vrt-kvm02" = | 372 | "vrt-kvm02" = |
| 356 | { hostname = "vrt-kvm02"; | 373 | { hostname = "vrt-kvm02"; |
| 357 | proxyJump = "mathw0e"; | 374 | proxyCommand = autosshProxy "mathw0e"; |
| 358 | user = "root"; | 375 | user = "root"; |
| 359 | extraOptions = { | 376 | extraOptions = { |
| 360 | PasswordAuthentication = "yes"; | 377 | PasswordAuthentication = "yes"; |
| @@ -363,7 +380,7 @@ | |||
| 363 | }; | 380 | }; |
| 364 | "vrt-kvm03" = | 381 | "vrt-kvm03" = |
| 365 | { hostname = "vrt-kvm03"; | 382 | { hostname = "vrt-kvm03"; |
| 366 | proxyJump = "mathw0e"; | 383 | proxyCommand = autosshProxy "mathw0e"; |
| 367 | user = "root"; | 384 | user = "root"; |
| 368 | extraOptions = { | 385 | extraOptions = { |
| 369 | PasswordAuthentication = "yes"; | 386 | PasswordAuthentication = "yes"; |
| @@ -372,7 +389,7 @@ | |||
| 372 | }; | 389 | }; |
| 373 | "vrt-kvm01" = | 390 | "vrt-kvm01" = |
| 374 | { hostname = "vrt-kvm01"; | 391 | { hostname = "vrt-kvm01"; |
| 375 | proxyJump = "mathw0e"; | 392 | proxyCommand = autosshProxy "mathw0e"; |
| 376 | user = "root"; | 393 | user = "root"; |
| 377 | extraOptions = { | 394 | extraOptions = { |
| 378 | PasswordAuthentication = "yes"; | 395 | PasswordAuthentication = "yes"; |
| @@ -381,39 +398,44 @@ | |||
| 381 | }; | 398 | }; |
| 382 | "tts-www01" = | 399 | "tts-www01" = |
| 383 | { hostname = "tts-www01.mathinst.loc"; | 400 | { hostname = "tts-www01.mathinst.loc"; |
| 384 | proxyJump = "mathw0h"; | 401 | proxyCommand = autosshProxy "mathw0h"; |
| 385 | user = "root"; | 402 | user = "root"; |
| 386 | }; | 403 | }; |
| 387 | "vpn-wg01" = | 404 | "vpn-wg01" = |
| 388 | { hostname = "vpn-wg01.mathinst.loc"; | 405 | { hostname = "vpn-wg01.mathinst.loc"; |
| 389 | proxyJump = "mathw0h"; | 406 | proxyCommand = autosshProxy "mathw0h"; |
| 390 | user = "root"; | 407 | user = "root"; |
| 391 | }; | 408 | }; |
| 392 | "repo-apt01" = | 409 | "repo-apt01" = |
| 393 | { hostname = "repo-apt01.mathinst.loc"; | 410 | { hostname = "repo-apt01.mathinst.loc"; |
| 394 | proxyJump = "mathw0h"; | 411 | proxyCommand = autosshProxy "mathw0h"; |
| 395 | user = "root"; | 412 | user = "root"; |
| 396 | }; | 413 | }; |
| 397 | "ldap-lmumr01" = | 414 | "ldap-lmumr01" = |
| 398 | { hostname = "ldap-lmumr01.mathinst.loc"; | 415 | { hostname = "ldap-lmumr01.mathinst.loc"; |
| 399 | proxyJump = "mathw0h"; | 416 | proxyCommand = autosshProxy "mathw0h"; |
| 400 | user = "root"; | 417 | user = "root"; |
| 401 | }; | 418 | }; |
| 402 | "mail-mi01" = | 419 | "mail-mi01" = |
| 403 | { hostname = "mail-mi01.mathinst.loc"; | 420 | { hostname = "mail-mi01.mathinst.loc"; |
| 404 | proxyJump = "mathw0h"; | 421 | proxyCommand = autosshProxy "mathw0h"; |
| 405 | }; | 422 | }; |
| 406 | "mail-www02" = | 423 | "mail-www02" = |
| 407 | { hostname = "mail-www02.mathinst.loc"; | 424 | { hostname = "mail-www02.mathinst.loc"; |
| 408 | proxyJump = "mathw0h"; | 425 | proxyCommand = autosshProxy "mathw0h"; |
| 409 | }; | 426 | }; |
| 410 | "dpl-fai01" = | 427 | "dpl-fai01" = |
| 411 | { hostname = "dpl-fai01.mathinst.loc"; | 428 | { hostname = "dpl-fai01.mathinst.loc"; |
| 412 | user = "root"; | 429 | user = "root"; |
| 413 | }; | 430 | }; |
| 431 | "dpl-fai02" = | ||
| 432 | { hostname = "dpl-fai02.mathinst.loc"; | ||
| 433 | user = "root"; | ||
| 434 | proxyJump = "mgmt01"; | ||
| 435 | }; | ||
| 414 | "math05" = | 436 | "math05" = |
| 415 | { hostname = "math05.mathinst.loc"; | 437 | { hostname = "math05.mathinst.loc"; |
| 416 | proxyJump = "mathw0h"; | 438 | proxyCommand = autosshProxy "mathw0h"; |
| 417 | extraOptions.KexAlgorithms = "+diffie-hellman-group1-sha1"; | 439 | extraOptions.KexAlgorithms = "+diffie-hellman-group1-sha1"; |
| 418 | }; | 440 | }; |
| 419 | "switch01" = | 441 | "switch01" = |
| @@ -439,20 +461,20 @@ | |||
| 439 | }; | 461 | }; |
| 440 | "www-mi01" = | 462 | "www-mi01" = |
| 441 | { hostname = "www-mi01.mathinst.loc"; | 463 | { hostname = "www-mi01.mathinst.loc"; |
| 442 | proxyJump = "mathw0h"; | 464 | proxyCommand = autosshProxy "mathw0h"; |
| 443 | }; | 465 | }; |
| 444 | "cip04" = | 466 | "cip04" = |
| 445 | { hostname = "cip04.cipmath.loc"; | 467 | { hostname = "cip04.cipmath.loc"; |
| 446 | proxyJump = "mathw0h"; | 468 | proxyCommand = autosshProxy "mathw0h"; |
| 447 | }; | 469 | }; |
| 448 | "mgmt-cls01" = | 470 | "mgmt-cls01" = |
| 449 | { user = "root"; | 471 | { user = "root"; |
| 450 | hostname = "mgmt-cls01.cipmath.loc"; | 472 | hostname = "mgmt-cls01.cipmath.loc"; |
| 451 | proxyJump = "ssh.math.lmu.de"; | 473 | proxyCommand = autosshProxy "ssh.math.lmu.de"; |
| 452 | }; | 474 | }; |
| 453 | "mgmt01" = | 475 | "mgmt01" = |
| 454 | { hostname = "mgmt01.mathinst.loc"; | 476 | { hostname = "mgmt01.mathinst.loc"; |
| 455 | proxyJump = "mathw0h"; | 477 | proxyCommand = autosshProxy "mathw0h"; |
| 456 | user = "root"; | 478 | user = "root"; |
| 457 | }; | 479 | }; |
| 458 | "ssh-lb01" = | 480 | "ssh-lb01" = |
| @@ -471,17 +493,17 @@ | |||
| 471 | "rdlx02" = { hostname = "rdlx02.mathinst.loc"; proxyJump = "mgmt01"; }; | 493 | "rdlx02" = { hostname = "rdlx02.mathinst.loc"; proxyJump = "mgmt01"; }; |
| 472 | "math0d" = | 494 | "math0d" = |
| 473 | { hostname = "math0d.mathinst.loc"; | 495 | { hostname = "math0d.mathinst.loc"; |
| 474 | proxyJump = "mathw0h"; | 496 | proxyCommand = autosshProxy "mathw0h"; |
| 475 | }; | 497 | }; |
| 476 | "dhcp01" = | 498 | "dhcp01" = |
| 477 | { hostname = "dhcp01.mathinst.loc"; | 499 | { hostname = "dhcp01.mathinst.loc"; |
| 478 | user = "root"; | 500 | user = "root"; |
| 479 | proxyJump = "mathw0h"; | 501 | proxyCommand = autosshProxy "mathw0h"; |
| 480 | }; | 502 | }; |
| 481 | "dhcp02" = | 503 | "dhcp02" = |
| 482 | { hostname = "dhcp02.mathinst.loc"; | 504 | { hostname = "dhcp02.mathinst.loc"; |
| 483 | user = "root"; | 505 | user = "root"; |
| 484 | proxyJump = "mathw0h"; | 506 | proxyCommand = autosshProxy "mathw0h"; |
| 485 | }; | 507 | }; |
| 486 | "cc-gpu-l01" = | 508 | "cc-gpu-l01" = |
| 487 | { hostname = "cc-gpu-l01.mathinst.loc"; | 509 | { hostname = "cc-gpu-l01.mathinst.loc"; |
| @@ -546,7 +568,7 @@ | |||
| 546 | user = "root"; | 568 | user = "root"; |
| 547 | }; | 569 | }; |
| 548 | "nas*" = | 570 | "nas*" = |
| 549 | { proxyJump = "mathw0e"; | 571 | { proxyCommand = autosshProxy "mathw0e"; |
| 550 | user = "admin"; | 572 | user = "admin"; |
| 551 | extraOptions = { | 573 | extraOptions = { |
| 552 | PasswordAuthentication = "yes"; | 574 | PasswordAuthentication = "yes"; |
| @@ -554,9 +576,4 @@ | |||
| 554 | HostKeyAlgorithms = "+ecdsa-sha2-nistp256"; | 576 | HostKeyAlgorithms = "+ecdsa-sha2-nistp256"; |
| 555 | }; | 577 | }; |
| 556 | }; | 578 | }; |
| 557 | "game01" = | ||
| 558 | { hostname = "game01.yggdrasil.li"; | ||
| 559 | user = "factorio"; | ||
| 560 | identityFile = "~/.ssh/gkleen@sif.midgard.yggdrasil"; | ||
| 561 | }; | ||
| 562 | } | 579 | } |
diff --git a/accounts/gkleen@sif/synadm/default.nix b/accounts/gkleen@sif/synadm/default.nix new file mode 100644 index 00000000..0a8e0d4c --- /dev/null +++ b/accounts/gkleen@sif/synadm/default.nix | |||
| @@ -0,0 +1,9 @@ | |||
| 1 | { config, pkgs, ... }: | ||
| 2 | { | ||
| 3 | home.packages = with pkgs; [ synadm ]; | ||
| 4 | sops.secrets."synadm.yaml" = { | ||
| 5 | format = "binary"; | ||
| 6 | sopsFile = ./synadm_yaml; | ||
| 7 | path = config.xdg.configHome + "/synadm.yaml"; | ||
| 8 | }; | ||
| 9 | } | ||
diff --git a/accounts/gkleen@sif/synadm/synadm_yaml b/accounts/gkleen@sif/synadm/synadm_yaml new file mode 100644 index 00000000..8d951ccc --- /dev/null +++ b/accounts/gkleen@sif/synadm/synadm_yaml | |||
| @@ -0,0 +1,15 @@ | |||
| 1 | { | ||
| 2 | "data": "ENC[AES256_GCM,data:qJy4Pmbbxja4jmW7OaHsD0mQZ7anZwLhiVmAgkavb+CqwWGDnUBXdz22/MHCbxng5NshcFSpBoCBhgY6B9V2bUiES6bH9AtMlDcs9ebKGMArBTUTnQ2MjWQGfQTqraWdNgy+n327uj9swwCH8EZXdYH/Hlv0t/re470W+VOHeXhGghQ3Y9IGz2sgfvMGr8QxaJNydZz85rgs5QUP/PglCwWIOw2mY1EX2vYwnmiAo49LmIEaxWvRi++KHaeBveDt0nlkJwzUlipL2VOKWxkgpK3yGucQn2mz+FRe1btp+4KGm8H17eUI9FO9sBwq,iv:kgM921ovwCgDYHQj3c5Rupy/8JxHehxUD2jb1k9Ik2Y=,tag:3TLQkJbv679VWy8V2TMugw==,type:str]", | ||
| 3 | "sops": { | ||
| 4 | "age": [ | ||
| 5 | { | ||
| 6 | "recipient": "age1rmmhetcmllq0ahl5qznlr0eya2zdxwl9h6y5wnl97d2wtyx5t99sm2u866", | ||
| 7 | "enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSB6bzVHUGNxZTF2WC9MYmZr\neGdVVzJXN3lGdEk3cTBER3J6UTFtcUJna2d3CjdNQmRXd2haZW1MYlJzNkk1dWVD\nVTFQc2gvS0JrejJ6SFh2MXpPWDZpRE0KLS0tIE0wTC85bEpvSnlGdGFkZVFhNjFZ\nbzRiZkxMWUg2ODNVUlBmNFlPNGRrZlkK1VXLJWcssv3ETyZSSM/Hhn5VIaI9iov9\nzShZA9Zx/FX6PYTuUMC29pJ57gKourcIxa/7HwSv/xYn1A6WcYfgSg==\n-----END AGE ENCRYPTED FILE-----\n" | ||
| 8 | } | ||
| 9 | ], | ||
| 10 | "lastmodified": "2025-05-18T11:03:42Z", | ||
| 11 | "mac": "ENC[AES256_GCM,data:yonJC68PhilAgEHNNJQ8nO53Qo3rx/LnfiOWfuMm24bOUIH9QM3WZZxpigd7bHI4eC4TqRb4LvcSi0nEURTRAhwiTqGNrWbpw2Iv3n5dhLEN9aTcetG5ZuhaXqfVUoML45/ovdBZG/0l8+XIHqxN2M/g/h4JwKoR/6lqzcrVhgo=,iv:xvxBJwy+E5zUdjhGPdZPdy7tnBIEj50hfiDJFsS3wNg=,tag:L4Fas36ZOg4h0QQwC4gjNA==,type:str]", | ||
| 12 | "unencrypted_suffix": "_unencrypted", | ||
| 13 | "version": "3.10.2" | ||
| 14 | } | ||
| 15 | } | ||
diff --git a/accounts/gkleen@sif/systemd.nix b/accounts/gkleen@sif/systemd.nix index 33bf7ef2..e601b49c 100644 --- a/accounts/gkleen@sif/systemd.nix +++ b/accounts/gkleen@sif/systemd.nix | |||
| @@ -6,7 +6,7 @@ let | |||
| 6 | cfg = config.home-manager.users.${userName}; | 6 | cfg = config.home-manager.users.${userName}; |
| 7 | 7 | ||
| 8 | autossh-socks-script = pkgs.writeScript "autossh" '' | 8 | autossh-socks-script = pkgs.writeScript "autossh" '' |
| 9 | #!${pkgs.zsh}/bin/zsh -xe | 9 | #!${lib.getExe pkgs.zsh} -xe |
| 10 | 10 | ||
| 11 | host="''${1%:*}" | 11 | host="''${1%:*}" |
| 12 | port="''${1#*:}" | 12 | port="''${1#*:}" |
| @@ -15,31 +15,29 @@ let | |||
| 15 | cmd=() | 15 | cmd=() |
| 16 | 16 | ||
| 17 | if [[ -n "''${SSHPASS_SECRET}" ]]; then | 17 | if [[ -n "''${SSHPASS_SECRET}" ]]; then |
| 18 | cmd+=(${pkgs.sshpassSecret}/bin/sshpass-secret) | 18 | cmd+=(${lib.getExe' pkgs.sshpassSecret "sshpass-secret"}) |
| 19 | cmd+=("''${(@s/:/)SSHPASS_SECRET}") | 19 | cmd+=("''${(@s/:/)SSHPASS_SECRET}") |
| 20 | cmd+=(--) | 20 | cmd+=(--) |
| 21 | fi | 21 | fi |
| 22 | 22 | ||
| 23 | cmd+=(${pkgs.openssh}/bin/ssh -vN -D localhost:''${port} "''${host}") | 23 | cmd+=(${lib.getExe' pkgs.openssh "ssh"} -vN -D 127.0.0.1:''${port} "''${host}") |
| 24 | 24 | ||
| 25 | ( exec -a "''${cmd[1]}" -- ''${cmd} ) & | 25 | ( exec -a "''${cmd[1]}" -- ''${cmd} ) & |
| 26 | pid=$! | 26 | pid=$! |
| 27 | 27 | ||
| 28 | newpid="" | 28 | newpid="" |
| 29 | i=200 | 29 | i=200 |
| 30 | while ! newpid=$(${pkgs.lsof}/bin/lsof -Pi @localhost:"''${port}" -sTCP:LISTEN -t); do | 30 | while ! newpid=$(${lib.getExe pkgs.lsof} -Pi @localhost:"''${port}" -sTCP:LISTEN -t); do |
| 31 | if ! kill -0 "''${pid}"; then | 31 | if ! kill -0 "''${pid}"; then |
| 32 | wait "''${pid}" | 32 | wait "''${pid}" |
| 33 | exit $? | 33 | exit $? |
| 34 | fi | 34 | fi |
| 35 | [[ "''${i}" -gt 0 ]] || exit 1 | 35 | [[ "''${i}" -gt 0 ]] || exit 1 |
| 36 | i=$((''${i} - 1)) | 36 | i=$((''${i} - 1)) |
| 37 | ${pkgs.coreutils}/bin/sleep 0.1 | 37 | ${lib.getExe' pkgs.coreutils "sleep"} 0.1 |
| 38 | done | 38 | done |
| 39 | 39 | ||
| 40 | ${config.systemd.package}/bin/systemd-notify --ready | 40 | ${lib.getExe' config.systemd.package "systemd-notify"} --pid=''${newpid} --ready |
| 41 | |||
| 42 | wait "''${pid}" "''${newpid}" | ||
| 43 | ''; | 41 | ''; |
| 44 | in { | 42 | in { |
| 45 | tmpfiles.rules = [ | 43 | tmpfiles.rules = [ |
| @@ -48,11 +46,11 @@ in { | |||
| 48 | ]; | 46 | ]; |
| 49 | 47 | ||
| 50 | services = { | 48 | services = { |
| 51 | sync-keepass = { | 49 | "sync-keepass@" = { |
| 52 | Service = { | 50 | Service = { |
| 53 | Type = "oneshot"; | 51 | Type = "oneshot"; |
| 54 | WorkingDirectory = "~"; | 52 | WorkingDirectory = "~"; |
| 55 | ExecStart = toString (pkgs.writers.writePython3 "sync-keepass" { | 53 | ExecStart = "${pkgs.writers.writePython3 "sync-keepass" { |
| 56 | libraries = with pkgs.python3Packages; [ python-dateutil ]; | 54 | libraries = with pkgs.python3Packages; [ python-dateutil ]; |
| 57 | } '' | 55 | } '' |
| 58 | import json | 56 | import json |
| @@ -61,13 +59,13 @@ in { | |||
| 61 | from datetime import datetime | 59 | from datetime import datetime |
| 62 | from dateutil.tz import tzlocal | 60 | from dateutil.tz import tzlocal |
| 63 | from dateutil.parser import isoparse | 61 | from dateutil.parser import isoparse |
| 64 | from sys import stderr | 62 | from sys import stderr, argv |
| 65 | 63 | ||
| 66 | 64 | ||
| 67 | remote_fs = 'surtr' | 65 | remote_fs = 'surtr' if argv[1] == 'store.kdbx' else 'mathcloud' |
| 68 | remote_file = 'store.kdbx' | 66 | remote_file = argv[1] |
| 69 | target_file = expanduser('~/store.kdbx') | 67 | target_file = expanduser(f'~/{argv[1]}') |
| 70 | meta_file = expanduser('~/.store.kdbx.json') | 68 | meta_file = expanduser(f'~/.{argv[1]}.json') |
| 71 | 69 | ||
| 72 | upload_time = None | 70 | upload_time = None |
| 73 | our_last_upload_time = None | 71 | our_last_upload_time = None |
| @@ -117,22 +115,14 @@ in { | |||
| 117 | do_upload() | 115 | do_upload() |
| 118 | elif upload_time is not None and (mod_time is None or upload_time > mod_time) and (our_last_upload_time is None or upload_time > our_last_upload_time): # noqa: E501 | 116 | elif upload_time is not None and (mod_time is None or upload_time > mod_time) and (our_last_upload_time is None or upload_time > our_last_upload_time): # noqa: E501 |
| 119 | do_download() | 117 | do_download() |
| 120 | ''); | 118 | ''} \"%I\""; |
| 121 | Environment = [ "RCLONE_PASSWORD_COMMAND=\"${pkgs.coreutils}/bin/cat ${config.sops.secrets.gkleen-rclone.path}\"" "PATH=${pkgs.rclone}/bin" ]; | 119 | Environment = [ "RCLONE_PASSWORD_COMMAND=\"${pkgs.coreutils}/bin/cat ${config.sops.secrets.gkleen-rclone.path}\"" "PATH=${pkgs.rclone}/bin" ]; |
| 122 | }; | 120 | }; |
| 123 | }; | 121 | }; |
| 124 | emacs = { | 122 | emacs = { |
| 125 | Unit = { | 123 | Unit = { |
| 126 | After = ["graphical-session-pre.target"]; | 124 | After = [ "graphical-session.target" ]; |
| 127 | }; | 125 | BindsTo = [ "graphical-session.target" ]; |
| 128 | }; | ||
| 129 | dunst = { | ||
| 130 | Service = { | ||
| 131 | ExecStart = lib.mkForce "${cfg.services.dunst.package}/bin/dunst"; | ||
| 132 | Restart = "always"; | ||
| 133 | }; | ||
| 134 | Install = { | ||
| 135 | WantedBy = ["graphical-session.target"]; | ||
| 136 | }; | 126 | }; |
| 137 | }; | 127 | }; |
| 138 | keepassxc = { | 128 | keepassxc = { |
| @@ -144,8 +134,8 @@ in { | |||
| 144 | Environment = [ "QT_QPA_PLATFORM=wayland" ]; | 134 | Environment = [ "QT_QPA_PLATFORM=wayland" ]; |
| 145 | }; | 135 | }; |
| 146 | Unit = { | 136 | Unit = { |
| 147 | Requires = ["graphical-session-pre.target"]; | 137 | After = [ "graphical-session.target" ]; |
| 148 | After = ["graphical-session-pre.target"]; | 138 | BindsTo = [ "graphical-session.target" ]; |
| 149 | }; | 139 | }; |
| 150 | }; | 140 | }; |
| 151 | mpris-proxy = { | 141 | mpris-proxy = { |
| @@ -154,7 +144,7 @@ in { | |||
| 154 | Service.ExecStart = "${pkgs.bluez}/bin/mpris-proxy"; | 144 | Service.ExecStart = "${pkgs.bluez}/bin/mpris-proxy"; |
| 155 | Install.WantedBy = [ "default.target" ]; | 145 | Install.WantedBy = [ "default.target" ]; |
| 156 | }; | 146 | }; |
| 157 | "autossh-socks@proxy.mathw0h:8119" = { | 147 | "autossh-socks@proxy.ssh.math.lmu.de:8119" = { |
| 158 | Service = { | 148 | Service = { |
| 159 | Type = "notify"; | 149 | Type = "notify"; |
| 160 | NotifyAccess = "all"; | 150 | NotifyAccess = "all"; |
| @@ -162,7 +152,7 @@ in { | |||
| 162 | Restart = "always"; | 152 | Restart = "always"; |
| 163 | RestartSec = "23s"; | 153 | RestartSec = "23s"; |
| 164 | ExecStart = "${autossh-socks-script} \"%I\""; | 154 | ExecStart = "${autossh-socks-script} \"%I\""; |
| 165 | Environment = [ "SSHPASS_SECRET=gkleen@mathw0g.math.lmu.de" ]; | 155 | Environment = [ "SSHPASS_SECRET=gkleen@ssh.math.lmu.de" ]; |
| 166 | }; | 156 | }; |
| 167 | Unit = { | 157 | Unit = { |
| 168 | StopWhenUnneeded = true; | 158 | StopWhenUnneeded = true; |
| @@ -183,44 +173,58 @@ in { | |||
| 183 | StopWhenUnneeded = true; | 173 | StopWhenUnneeded = true; |
| 184 | }; | 174 | }; |
| 185 | }; | 175 | }; |
| 186 | swayidle = { | 176 | "autossh-socks@proxy.mathw0h:8123" = { |
| 187 | Service = { | 177 | Service = { |
| 188 | RuntimeDirectory = "swayidle"; | 178 | Type = "notify"; |
| 189 | }; | 179 | NotifyAccess = "all"; |
| 190 | }; | 180 | WorkingDirectory = "~"; |
| 191 | psi-notify = { | 181 | Restart = "always"; |
| 192 | Install = { | 182 | RestartSec = "23s"; |
| 193 | WantedBy = ["graphical-session.target"]; | 183 | ExecStart = "${autossh-socks-script} \"%I\""; |
| 184 | Environment = [ "SSHPASS_SECRET=gkleen@mathw0h.mathinst.loc" ]; | ||
| 194 | }; | 185 | }; |
| 195 | Unit = { | 186 | Unit = { |
| 196 | Requires = ["graphical-session-pre.target"]; | 187 | StopWhenUnneeded = true; |
| 197 | After = ["graphical-session-pre.target"]; | 188 | StartLimitInterval = "180s"; |
| 189 | StartLimitBurst = 7; | ||
| 198 | }; | 190 | }; |
| 191 | }; | ||
| 192 | "autossh-socks@proxy.mathw0e:8125" = { | ||
| 199 | Service = { | 193 | Service = { |
| 200 | ExecStart = lib.getExe pkgs.psi-notify; | ||
| 201 | ExecReload = "${pkgs.coreutils}/bin/kill -HUP $MAINPID"; | ||
| 202 | Type = "notify"; | 194 | Type = "notify"; |
| 195 | NotifyAccess = "all"; | ||
| 196 | WorkingDirectory = "~"; | ||
| 203 | Restart = "always"; | 197 | Restart = "always"; |
| 204 | WatchdogSec = "2s"; | 198 | RestartSec = "23s"; |
| 199 | ExecStart = "${autossh-socks-script} \"%I\""; | ||
| 200 | Environment = [ "SSHPASS_SECRET=gkleen@mathw0e.mathinst.loc" ]; | ||
| 201 | }; | ||
| 202 | Unit = { | ||
| 203 | StopWhenUnneeded = true; | ||
| 204 | StartLimitInterval = "180s"; | ||
| 205 | StartLimitBurst = 7; | ||
| 205 | }; | 206 | }; |
| 206 | }; | 207 | }; |
| 207 | polkit-gnome-authentication-agent-1 = { | 208 | psi-notify = { |
| 208 | Install = { | 209 | Install = { |
| 209 | WantedBy = ["graphical-session.target"]; | 210 | WantedBy = ["graphical-session.target"]; |
| 210 | }; | 211 | }; |
| 211 | Unit = { | 212 | Unit = { |
| 212 | PartOf = ["graphical-session.target"]; | 213 | After = [ "graphical-session.target" ]; |
| 213 | Requires = ["graphical-session-pre.target"]; | 214 | PartOf = [ "graphical-session.target" ]; |
| 214 | After = ["graphical-session-pre.target"]; | ||
| 215 | }; | 215 | }; |
| 216 | Service = { | 216 | Service = { |
| 217 | ExecStart = "${pkgs.polkit_gnome}/libexec/polkit-gnome-authentication-agent-1"; | 217 | ExecStart = lib.getExe pkgs.psi-notify; |
| 218 | Restart = "on-failure"; | 218 | ExecReload = "${pkgs.coreutils}/bin/kill -HUP $MAINPID"; |
| 219 | Type = "notify"; | ||
| 220 | Restart = "always"; | ||
| 221 | WatchdogSec = "2s"; | ||
| 219 | }; | 222 | }; |
| 220 | }; | 223 | }; |
| 221 | gtklock = { | 224 | gtklock = { |
| 222 | Unit = { | 225 | Unit = { |
| 223 | Requisite = ["graphical-session.target"]; | 226 | Requisite = ["graphical-session.target"]; |
| 227 | After = [ "graphical-session.target" ]; | ||
| 224 | PartOf = ["graphical-session.target"]; | 228 | PartOf = ["graphical-session.target"]; |
| 225 | }; | 229 | }; |
| 226 | Service = { | 230 | Service = { |
| @@ -228,53 +232,55 @@ in { | |||
| 228 | RuntimeDirectory = "gtklock"; | 232 | RuntimeDirectory = "gtklock"; |
| 229 | CacheDirectory = "gtklock"; | 233 | CacheDirectory = "gtklock"; |
| 230 | ExecStartPre = [ | 234 | ExecStartPre = [ |
| 231 | "${pkgs.libsForQt5.qt5.qttools.bin}/bin/qdbus org.keepassxc.KeePassXC.MainWindow /keepassxc org.keepassxc.KeePassXC.MainWindow.lockAllDatabases" | 235 | "-${lib.getExe' pkgs.libsForQt5.qt5.qttools.bin "qdbus"} org.keepassxc.KeePassXC.MainWindow /keepassxc org.keepassxc.KeePassXC.MainWindow.lockAllDatabases" |
| 232 | "${config.systemd.package}/bin/systemctl --user stop gpg-agent.service" | 236 | "-${lib.getExe' config.systemd.package "systemctl"} --user stop gpg-agent.service" |
| 233 | (pkgs.writeShellScript "generate-css" '' | 237 | "-${lib.getExe pkgs.playerctl} -a pause" |
| 234 | set -x | 238 | "-${lib.getExe (pkgs.writeShellApplication { |
| 235 | export PATH="${lib.makeBinPath [cfg.programs.wpaperd.package pkgs.jq pkgs.coreutils pkgs.imagemagick pkgs.findutils]}:$PATH" | 239 | name = "generate-css"; |
| 240 | runtimeInputs = with pkgs; [cfg.services.wpaperd.package jq coreutils imagemagick findutils]; | ||
| 241 | text = '' | ||
| 242 | declare -A monitors | ||
| 243 | monitors=() | ||
| 244 | while IFS= read -r entry; do | ||
| 245 | path=$(jq -r ".path" <<<"$entry") | ||
| 246 | [[ -z "$path" || ! -f "$path" ]] && continue | ||
| 247 | blurred_path="$CACHE_DIRECTORY"/"$(b2sum -l 128 <<<"$path" | cut -d' ' -f1)"."''${path##*.}" | ||
| 248 | monitor=$(jq -r ".display" <<<"$entry") | ||
| 249 | if [[ ! -f "$blurred_path" ]]; then | ||
| 250 | mkdir -p "$(dirname "$blurred_path")" | ||
| 251 | magick "$path" -filter Gaussian -resize 6.25% -define filter:sigma=2.5 -resize 1600% "$blurred_path" & | ||
| 252 | fi | ||
| 253 | monitors+=([$monitor]="$blurred_path") | ||
| 254 | done < <(wpaperctl all-wallpapers -j | jq -c ".[]") | ||
| 255 | # wait | ||
| 236 | 256 | ||
| 237 | declare -A monitors | 257 | cp --no-preserve=mode ${pkgs.writeText "gtklock.css" '' |
| 238 | monitors=() | 258 | #window-box { |
| 239 | while IFS= read -r entry; do | 259 | padding: 64px; |
| 240 | path=$(jq -r ".path" <<<"$entry") | 260 | /* border: 1px solid black; */ |
| 241 | [[ -z "$path" || ! -f "$path" ]] && continue | 261 | border-radius: 4px; |
| 242 | blurred_path="$CACHE_DIRECTORY"/"$(b2sum -l 128 <<<"$path" | cut -d' ' -f1)"."''${path##*.}" | 262 | box-shadow: rgba(0, 0, 0, 0.8) 0px 4px 12px; |
| 243 | monitor=$(jq -r ".display" <<<"$entry") | 263 | /* background-color: white; */ |
| 244 | if [[ ! -f "$blurred_path" ]]; then | 264 | background-color: rgba(0, 0, 0, 0.5); |
| 245 | mkdir -p "$(dirname "$blurred_path")" | 265 | } |
| 246 | magick "$path" -filter Gaussian -resize 6.25% -define filter:sigma=2.5 -resize 1600% "$blurred_path" & | 266 | ''} "$RUNTIME_DIRECTORY"/style.css |
| 247 | fi | 267 | for monitor in "''${!monitors[@]}"; do |
| 248 | monitors+=([$monitor]="$blurred_path") | 268 | cat >>"$RUNTIME_DIRECTORY"/style.css <<EOF |
| 249 | done < <(wpaperctl all-wallpapers -j | jq -c ".[]") | 269 | window#''${monitor} { |
| 250 | wait | 270 | background-image: url("''${monitors[$monitor]}"); |
| 251 | 271 | background-repeat: no-repeat; | |
| 252 | cp --no-preserve=mode ${pkgs.writeText "gtklock.css" '' | 272 | background-size: 100% 100%; |
| 253 | #window-box { | 273 | background-origin: content-box; |
| 254 | padding: 64px; | ||
| 255 | /* border: 1px solid black; */ | ||
| 256 | border-radius: 4px; | ||
| 257 | box-shadow: rgba(0, 0, 0, 0.8) 0px 4px 12px; | ||
| 258 | /* background-color: white; */ | ||
| 259 | background-color: rgba(0, 0, 0, 0.5); | ||
| 260 | } | 274 | } |
| 261 | ''} "$RUNTIME_DIRECTORY"/style.css | 275 | EOF |
| 262 | for monitor in "''${!monitors[@]}"; do | 276 | done |
| 263 | cat >>"$RUNTIME_DIRECTORY"/style.css <<EOF | 277 | ''; |
| 264 | window#''${monitor} { | 278 | })}" |
| 265 | background-image: url("''${monitors[$monitor]}"); | ||
| 266 | background-repeat: no-repeat; | ||
| 267 | background-size: 100% 100%; | ||
| 268 | background-origin: content-box; | ||
| 269 | } | ||
| 270 | EOF | ||
| 271 | done | ||
| 272 | '') | ||
| 273 | ]; | 279 | ]; |
| 274 | NotifyAccess = "all"; | 280 | NotifyAccess = "all"; |
| 275 | ExecStart = ''${lib.getExe pkgs.gtklock} -s "''${RUNTIME_DIRECTORY}/style.css" -L ${pkgs.writeShellScript "after-lock" '' | 281 | ExecStart = ''${lib.getExe pkgs.gtklock} -s "''${RUNTIME_DIRECTORY}/style.css" -L ${pkgs.writeShellScript "after-lock" '' |
| 276 | ${cfg.wayland.windowManager.hyprland.package}/bin/hyprctl dispatch dpms off | 282 | ${lib.getExe cfg.programs.niri.package} msg action power-off-monitors |
| 277 | ${config.systemd.package}/bin/systemd-notify --ready | 283 | ${lib.getExe' config.systemd.package "systemd-notify"} --ready |
| 278 | ''}''; | 284 | ''}''; |
| 279 | }; | 285 | }; |
| 280 | }; | 286 | }; |
| @@ -322,15 +328,60 @@ in { | |||
| 322 | ExecStopPost = "${pkgs.coreutils}/bin/rm -rfv \"$CACHE_DIRECTORY\""; | 328 | ExecStopPost = "${pkgs.coreutils}/bin/rm -rfv \"$CACHE_DIRECTORY\""; |
| 323 | }; | 329 | }; |
| 324 | }; | 330 | }; |
| 331 | # wpaperd = { | ||
| 332 | # Install = { | ||
| 333 | # WantedBy = ["graphical-session.target"]; | ||
| 334 | # }; | ||
| 335 | # Unit = { | ||
| 336 | # After = [ "graphical-session.target" ]; | ||
| 337 | # PartOf = [ "graphical-session.target" ]; | ||
| 338 | # }; | ||
| 339 | # Service = { | ||
| 340 | # ExecStart = lib.getExe cfg.services.wpaperd.package; | ||
| 341 | # Type = "simple"; | ||
| 342 | # Restart = "always"; | ||
| 343 | # RestartSec = "2s"; | ||
| 344 | # }; | ||
| 345 | # }; | ||
| 346 | xembed-sni-proxy = { | ||
| 347 | Unit = { | ||
| 348 | PartOf = lib.mkForce ["tray.target"]; | ||
| 349 | }; | ||
| 350 | }; | ||
| 351 | poweralertd = { | ||
| 352 | Unit = { | ||
| 353 | After = ["graphical-session.target"]; | ||
| 354 | }; | ||
| 355 | }; | ||
| 356 | network-manager-applet = { | ||
| 357 | Unit = { | ||
| 358 | PartOf = lib.mkForce ["tray.target"]; | ||
| 359 | }; | ||
| 360 | }; | ||
| 361 | udiskie = { | ||
| 362 | Unit = { | ||
| 363 | PartOf = lib.mkForce ["tray.target"]; | ||
| 364 | }; | ||
| 365 | }; | ||
| 366 | blueman-applet = { | ||
| 367 | Unit = { | ||
| 368 | PartOf = lib.mkForce ["tray.target"]; | ||
| 369 | }; | ||
| 370 | Install = { | ||
| 371 | WantedBy = lib.mkForce ["tray.target"]; | ||
| 372 | }; | ||
| 373 | }; | ||
| 325 | } // listToAttrs (map ({host, port}: nameValuePair "proxy-to-autossh-socks@${toString port}" { | 374 | } // listToAttrs (map ({host, port}: nameValuePair "proxy-to-autossh-socks@${toString port}" { |
| 326 | Unit = { | 375 | Unit = { |
| 327 | Requires = ["autossh-socks@${host}:${toString (port + 1)}.service" "proxy-to-autossh-socks@${toString port}.socket"]; | 376 | BindsTo = ["autossh-socks@${host}:${toString (port + 1)}.service" "proxy-to-autossh-socks@${toString port}.socket"]; |
| 328 | After = ["autossh-socks@${host}:${toString (port + 1)}.service" "proxy-to-autossh-socks@${toString port}.socket"]; | 377 | After = ["autossh-socks@${host}:${toString (port + 1)}.service" "proxy-to-autossh-socks@${toString port}.socket"]; |
| 329 | }; | 378 | }; |
| 330 | Service = { | 379 | Service = { |
| 331 | ExecStart = "${config.systemd.package}/lib/systemd/systemd-socket-proxyd --exit-idle-time=10s localhost:${toString (port + 1)}"; | 380 | ExecStart = "${config.systemd.package}/lib/systemd/systemd-socket-proxyd --exit-idle-time=60s 127.0.0.1:${toString (port + 1)}"; |
| 381 | Restart = "always"; | ||
| 382 | RestartSec = "23s"; | ||
| 332 | }; | 383 | }; |
| 333 | }) [{ host = "proxy.mathw0h"; port = 8118; } { host = "proxy.vidhar"; port = 8120; }]); | 384 | }) [{ host = "proxy.ssh.math.lmu.de"; port = 8118; } { host = "proxy.vidhar"; port = 8120; } { host = "proxy.mathw0h"; port = 8122; } { host = "proxy.mathw0e"; port = 8124; }]); |
| 334 | sockets = listToAttrs (map (port: nameValuePair "proxy-to-autossh-socks@${toString port}" { | 385 | sockets = listToAttrs (map (port: nameValuePair "proxy-to-autossh-socks@${toString port}" { |
| 335 | Socket = { | 386 | Socket = { |
| 336 | ListenStream = "%I"; | 387 | ListenStream = "%I"; |
| @@ -338,7 +389,7 @@ in { | |||
| 338 | Install = { | 389 | Install = { |
| 339 | WantedBy = ["default.target"]; | 390 | WantedBy = ["default.target"]; |
| 340 | }; | 391 | }; |
| 341 | }) [8118 8120]) // { | 392 | }) [8118 8120 8122 8124]) // { |
| 342 | "yt-dlp" = { | 393 | "yt-dlp" = { |
| 343 | Socket = { | 394 | Socket = { |
| 344 | SocketMode = "0600"; | 395 | SocketMode = "0600"; |
| @@ -352,7 +403,7 @@ in { | |||
| 352 | }; | 403 | }; |
| 353 | }; | 404 | }; |
| 354 | timers = { | 405 | timers = { |
| 355 | sync-keepass = { | 406 | "sync-keepass@store.kdbx" = { |
| 356 | Timer = { | 407 | Timer = { |
| 357 | OnActiveSec = "1m"; | 408 | OnActiveSec = "1m"; |
| 358 | OnUnitActiveSec = "1m"; | 409 | OnUnitActiveSec = "1m"; |
| @@ -362,6 +413,16 @@ in { | |||
| 362 | WantedBy = ["default.target"]; | 413 | WantedBy = ["default.target"]; |
| 363 | }; | 414 | }; |
| 364 | }; | 415 | }; |
| 416 | "sync-keepass@rz.kdbx" = { | ||
| 417 | Timer = { | ||
| 418 | OnActiveSec = "1d"; | ||
| 419 | OnUnitActiveSec = "1d"; | ||
| 420 | }; | ||
| 421 | |||
| 422 | Install = { | ||
| 423 | WantedBy = ["default.target"]; | ||
| 424 | }; | ||
| 425 | }; | ||
| 365 | }; | 426 | }; |
| 366 | targets = { | 427 | targets = { |
| 367 | graphical-session = { | 428 | graphical-session = { |
| @@ -372,6 +433,9 @@ in { | |||
| 372 | }; | 433 | }; |
| 373 | tray = { | 434 | tray = { |
| 374 | Unit = { | 435 | Unit = { |
| 436 | PartOf = [ "graphical-session.target" ]; | ||
| 437 | # Requires = [ "waybar.service" ]; | ||
| 438 | After = [ "graphical-session.target" ]; # "waybar.service" ]; | ||
| 375 | Wants = ["blueman-applet.service" "udiskie.service" "network-manager-applet.service"]; | 439 | Wants = ["blueman-applet.service" "udiskie.service" "network-manager-applet.service"]; |
| 376 | }; | 440 | }; |
| 377 | }; | 441 | }; |
diff --git a/accounts/gkleen@sif/taffybar/default.nix b/accounts/gkleen@sif/taffybar/default.nix deleted file mode 100644 index 98366d8f..00000000 --- a/accounts/gkleen@sif/taffybar/default.nix +++ /dev/null | |||
| @@ -1,2 +0,0 @@ | |||
| 1 | { haskellPackages ? (import <nixpkgs> {}).haskellPackages }: | ||
| 2 | haskellPackages.callCabal2nix "gkleen-sif-taffybar" ./. {} | ||
diff --git a/accounts/gkleen@sif/taffybar/gkleen-sif-taffybar.cabal b/accounts/gkleen@sif/taffybar/gkleen-sif-taffybar.cabal deleted file mode 100644 index e32cb473..00000000 --- a/accounts/gkleen@sif/taffybar/gkleen-sif-taffybar.cabal +++ /dev/null | |||
| @@ -1,32 +0,0 @@ | |||
| 1 | name: gkleen-sif-taffybar | ||
| 2 | version: 0.0.0 | ||
| 3 | build-type: Simple | ||
| 4 | cabal-version: >=1.10 | ||
| 5 | |||
| 6 | data-files: taffybar.css | ||
| 7 | |||
| 8 | executable taffybar | ||
| 9 | hs-source-dirs: src | ||
| 10 | main-is: taffybar.hs | ||
| 11 | ghc-options: -threaded -rtsopts -with-rtsopts=-N -O2 -Wall | ||
| 12 | build-depends: base | ||
| 13 | , containers | ||
| 14 | , directory | ||
| 15 | , filepath | ||
| 16 | , gtk3 | ||
| 17 | , taffybar | ||
| 18 | , X11>=1.8 | ||
| 19 | , transformers | ||
| 20 | , gi-gtk | ||
| 21 | , time, time-locale-compat | ||
| 22 | , text | ||
| 23 | , HStringTemplate | ||
| 24 | , gtk-sni-tray | ||
| 25 | , hslogger | ||
| 26 | other-modules: Paths_gkleen_sif_taffybar | ||
| 27 | , System.Taffybar.Widget.Clock | ||
| 28 | , System.Taffybar.Widget.TooltipBattery | ||
| 29 | default-language: Haskell2010 | ||
| 30 | default-extensions: ScopedTypeVariables | ||
| 31 | , LambdaCase | ||
| 32 | , NamedFieldPuns \ No newline at end of file | ||
diff --git a/accounts/gkleen@sif/taffybar/src/System/Taffybar/Widget/Clock.hs b/accounts/gkleen@sif/taffybar/src/System/Taffybar/Widget/Clock.hs deleted file mode 100644 index e8dc480f..00000000 --- a/accounts/gkleen@sif/taffybar/src/System/Taffybar/Widget/Clock.hs +++ /dev/null | |||
| @@ -1,111 +0,0 @@ | |||
| 1 | {-# LANGUAGE OverloadedStrings #-} | ||
| 2 | module System.Taffybar.Widget.Clock | ||
| 3 | ( textClockNew | ||
| 4 | , textClockNewWith | ||
| 5 | , defaultClockConfig | ||
| 6 | , ClockConfig(..) | ||
| 7 | , ClockUpdateStrategy(..) | ||
| 8 | ) where | ||
| 9 | |||
| 10 | import Control.Monad.IO.Class | ||
| 11 | import Data.Maybe | ||
| 12 | import qualified Data.Text as T | ||
| 13 | import qualified Data.Time.Clock as Clock | ||
| 14 | import Data.Time.Format | ||
| 15 | import Data.Time.LocalTime | ||
| 16 | import qualified Data.Time.Locale.Compat as L | ||
| 17 | import GI.Gtk | ||
| 18 | import System.Taffybar.Widget.Generic.PollingLabel | ||
| 19 | |||
| 20 | type ClockFormat = L.TimeLocale -> ZonedTime -> T.Text | ||
| 21 | |||
| 22 | -- | Create the widget. I recommend passing @Nothing@ for the TimeLocale | ||
| 23 | -- parameter. The format string can include Pango markup | ||
| 24 | -- (<http://developer.gnome.org/pango/stable/PangoMarkupFormat.html>). | ||
| 25 | textClockNew :: | ||
| 26 | MonadIO m => Maybe L.TimeLocale -> ClockFormat -> Double -> m GI.Gtk.Widget | ||
| 27 | textClockNew userLocale format interval = | ||
| 28 | textClockNewWith cfg | ||
| 29 | where | ||
| 30 | cfg = defaultClockConfig { clockTimeLocale = userLocale | ||
| 31 | , clockFormat = format | ||
| 32 | , clockUpdateStrategy = ConstantInterval interval | ||
| 33 | } | ||
| 34 | |||
| 35 | data ClockUpdateStrategy | ||
| 36 | = ConstantInterval Double | ||
| 37 | | RoundedTargetInterval Int Double | ||
| 38 | deriving (Eq, Ord, Show) | ||
| 39 | |||
| 40 | data ClockConfig = ClockConfig | ||
| 41 | { clockTimeZone :: Maybe TimeZone | ||
| 42 | , clockTimeLocale :: Maybe L.TimeLocale | ||
| 43 | , clockFormat :: ClockFormat | ||
| 44 | , clockUpdateStrategy :: ClockUpdateStrategy | ||
| 45 | } | ||
| 46 | |||
| 47 | -- | A clock configuration that defaults to the current locale | ||
| 48 | defaultClockConfig :: ClockConfig | ||
| 49 | defaultClockConfig = ClockConfig | ||
| 50 | { clockTimeZone = Nothing | ||
| 51 | , clockTimeLocale = Nothing | ||
| 52 | , clockFormat = \locale zonedTime -> T.pack $ formatTime locale "%a %b %_d %r" zonedTime | ||
| 53 | , clockUpdateStrategy = RoundedTargetInterval 5 0.0 | ||
| 54 | } | ||
| 55 | |||
| 56 | systemGetTZ :: IO TimeZone | ||
| 57 | systemGetTZ = getCurrentTimeZone | ||
| 58 | |||
| 59 | -- | A configurable text-based clock widget. It currently allows for | ||
| 60 | -- a configurable time zone through the 'ClockConfig'. | ||
| 61 | -- | ||
| 62 | -- See also 'textClockNew'. | ||
| 63 | textClockNewWith :: MonadIO m => ClockConfig -> m Widget | ||
| 64 | textClockNewWith ClockConfig | ||
| 65 | { clockTimeZone = userZone | ||
| 66 | , clockTimeLocale = userLocale | ||
| 67 | , clockFormat = format | ||
| 68 | , clockUpdateStrategy = updateStrategy | ||
| 69 | } = liftIO $ do | ||
| 70 | let getTZ = maybe systemGetTZ return userZone | ||
| 71 | locale = fromMaybe L.defaultTimeLocale userLocale | ||
| 72 | |||
| 73 | let getUserZonedTime = | ||
| 74 | utcToZonedTime <$> getTZ <*> Clock.getCurrentTime | ||
| 75 | |||
| 76 | doTimeFormat = format locale | ||
| 77 | |||
| 78 | getRoundedTimeAndNextTarget = do | ||
| 79 | zonedTime <- getUserZonedTime | ||
| 80 | return $ case updateStrategy of | ||
| 81 | ConstantInterval interval -> | ||
| 82 | (doTimeFormat zonedTime, Nothing, interval) | ||
| 83 | RoundedTargetInterval roundSeconds offset -> | ||
| 84 | let roundSecondsDiffTime = fromIntegral roundSeconds | ||
| 85 | addTheRound = addLocalTime roundSecondsDiffTime | ||
| 86 | localTime = zonedTimeToLocalTime zonedTime | ||
| 87 | ourLocalTimeOfDay = localTimeOfDay localTime | ||
| 88 | seconds = round $ todSec ourLocalTimeOfDay | ||
| 89 | secondsFactor = seconds `div` roundSeconds | ||
| 90 | displaySeconds = secondsFactor * roundSeconds | ||
| 91 | baseLocalTimeOfDay = | ||
| 92 | ourLocalTimeOfDay { todSec = fromIntegral displaySeconds } | ||
| 93 | ourLocalTime = | ||
| 94 | localTime { localTimeOfDay = baseLocalTimeOfDay } | ||
| 95 | roundedLocalTime = | ||
| 96 | if seconds `mod` roundSeconds > roundSeconds `div` 2 | ||
| 97 | then addTheRound ourLocalTime | ||
| 98 | else ourLocalTime | ||
| 99 | roundedZonedTime = | ||
| 100 | zonedTime { zonedTimeToLocalTime = roundedLocalTime } | ||
| 101 | nextTarget = addTheRound ourLocalTime | ||
| 102 | amountToWait = realToFrac $ diffLocalTime nextTarget localTime | ||
| 103 | in (doTimeFormat roundedZonedTime, Nothing, amountToWait - offset) | ||
| 104 | |||
| 105 | label <- pollingLabelWithVariableDelay getRoundedTimeAndNextTarget | ||
| 106 | ebox <- eventBoxNew | ||
| 107 | containerAdd ebox label | ||
| 108 | eventBoxSetVisibleWindow ebox False | ||
| 109 | widgetShowAll ebox | ||
| 110 | toWidget ebox | ||
| 111 | |||
diff --git a/accounts/gkleen@sif/taffybar/src/System/Taffybar/Widget/TooltipBattery.hs b/accounts/gkleen@sif/taffybar/src/System/Taffybar/Widget/TooltipBattery.hs deleted file mode 100644 index 9dc52774..00000000 --- a/accounts/gkleen@sif/taffybar/src/System/Taffybar/Widget/TooltipBattery.hs +++ /dev/null | |||
| @@ -1,101 +0,0 @@ | |||
| 1 | {-# LANGUAGE OverloadedStrings #-} | ||
| 2 | {-# LANGUAGE ScopedTypeVariables #-} | ||
| 3 | module System.Taffybar.Widget.TooltipBattery ( batteryIconTooltipNew ) where | ||
| 4 | |||
| 5 | import Control.Applicative | ||
| 6 | import Control.Monad | ||
| 7 | import Control.Monad.IO.Class | ||
| 8 | import Control.Monad.Trans.Reader | ||
| 9 | import Data.Int (Int64) | ||
| 10 | import qualified Data.Text as T | ||
| 11 | import GI.Gtk | ||
| 12 | import Prelude | ||
| 13 | import StatusNotifier.Tray (scalePixbufToSize) | ||
| 14 | import System.Taffybar.Context | ||
| 15 | import System.Taffybar.Information.Battery | ||
| 16 | import System.Taffybar.Util | ||
| 17 | import System.Taffybar.Widget.Generic.AutoSizeImage | ||
| 18 | import System.Taffybar.Widget.Generic.ChannelWidget | ||
| 19 | import Text.Printf | ||
| 20 | import Text.StringTemplate | ||
| 21 | import Data.Function ((&)) | ||
| 22 | |||
| 23 | -- | Just the battery info that will be used for display (this makes combining | ||
| 24 | -- several easier). | ||
| 25 | data BatteryWidgetInfo = BWI | ||
| 26 | { seconds :: Maybe Int64 | ||
| 27 | , percent :: Double | ||
| 28 | , status :: String | ||
| 29 | , rate :: Maybe Double | ||
| 30 | } deriving (Eq, Show) | ||
| 31 | |||
| 32 | -- | Format a duration expressed as seconds to hours and minutes | ||
| 33 | formatDuration :: Int64 -> String | ||
| 34 | formatDuration secs = let minutes, hours, minutes' :: Int64 | ||
| 35 | minutes = secs `div` 60 | ||
| 36 | (hours, minutes') = minutes `divMod` 60 | ||
| 37 | in printf "%02d:%02d" hours minutes' | ||
| 38 | |||
| 39 | getBatteryWidgetInfo :: BatteryInfo -> BatteryWidgetInfo | ||
| 40 | getBatteryWidgetInfo info = | ||
| 41 | let battPctNum :: Double | ||
| 42 | battPctNum = batteryPercentage info | ||
| 43 | battTime :: Maybe Int64 | ||
| 44 | battTime = | ||
| 45 | case batteryState info of | ||
| 46 | BatteryStateCharging -> Just $ batteryTimeToFull info | ||
| 47 | BatteryStateDischarging -> Just $ batteryTimeToEmpty info | ||
| 48 | _ -> Nothing | ||
| 49 | battStatus :: String | ||
| 50 | battStatus = | ||
| 51 | case batteryState info of | ||
| 52 | BatteryStateCharging -> "↑" | ||
| 53 | BatteryStateDischarging -> "↓" | ||
| 54 | BatteryStateEmpty -> "⤓" | ||
| 55 | BatteryStateFullyCharged -> "⤒" | ||
| 56 | _ -> "?" | ||
| 57 | battRate :: Maybe Double | ||
| 58 | battRate | rawRate < 0.1 = Nothing | ||
| 59 | | otherwise = Just rawRate | ||
| 60 | where rawRate = batteryEnergyRate info | ||
| 61 | in BWI{ seconds = battTime, percent = battPctNum, status = battStatus, rate = battRate } | ||
| 62 | |||
| 63 | -- | Given (maybe summarized) battery info and format: provides the string to display | ||
| 64 | formatBattInfo :: BatteryWidgetInfo -> String -> T.Text | ||
| 65 | formatBattInfo info fmt = | ||
| 66 | let tpl = newSTMP fmt | ||
| 67 | tpl' = tpl | ||
| 68 | & setManyAttrib [ ("percentage", printf "%.0f" $ percent info) | ||
| 69 | , ("status", status info) | ||
| 70 | ] | ||
| 71 | & setManyAttrib [ ("time", formatDuration <$> seconds info) | ||
| 72 | , ("rate", printf "%.0f" <$> rate info) | ||
| 73 | ] | ||
| 74 | in render tpl' | ||
| 75 | |||
| 76 | themeLoadFlags :: [IconLookupFlags] | ||
| 77 | themeLoadFlags = [IconLookupFlagsGenericFallback, IconLookupFlagsUseBuiltin] | ||
| 78 | |||
| 79 | batteryIconTooltipNew :: String -> TaffyIO Widget | ||
| 80 | batteryIconTooltipNew format = do | ||
| 81 | DisplayBatteryChanVar (chan, _) <- setupDisplayBatteryChanVar ["IconName", "State", "Percentage", "TimeToFull", "TimeToEmpty", "EnergyRate"] | ||
| 82 | ctx <- ask | ||
| 83 | liftIO $ do | ||
| 84 | image <- imageNew | ||
| 85 | styleCtx <- widgetGetStyleContext =<< toWidget image | ||
| 86 | defaultTheme <- iconThemeGetDefault | ||
| 87 | let getCurrentBatteryIconNameStringTooltip = do | ||
| 88 | info <- runReaderT getDisplayBatteryInfo ctx | ||
| 89 | let iconNameString = T.pack $ batteryIconName info | ||
| 90 | tooltip = formatBattInfo (getBatteryWidgetInfo info) format | ||
| 91 | return (iconNameString, tooltip) | ||
| 92 | extractPixbuf info = | ||
| 93 | fst <$> iconInfoLoadSymbolicForContext info styleCtx | ||
| 94 | setIconForSize size = do | ||
| 95 | (name, tooltip) <- getCurrentBatteryIconNameStringTooltip | ||
| 96 | widgetSetTooltipMarkup image $ Just tooltip | ||
| 97 | iconThemeLookupIcon defaultTheme name size themeLoadFlags >>= | ||
| 98 | traverse extractPixbuf >>= | ||
| 99 | traverse (scalePixbufToSize size OrientationHorizontal) | ||
| 100 | updateImage <- autoSizeImage image setIconForSize OrientationHorizontal | ||
| 101 | toWidget =<< channelWidgetNew image chan (const $ postGUIASync updateImage) | ||
diff --git a/accounts/gkleen@sif/taffybar/src/taffybar.hs b/accounts/gkleen@sif/taffybar/src/taffybar.hs deleted file mode 100644 index 67ee942d..00000000 --- a/accounts/gkleen@sif/taffybar/src/taffybar.hs +++ /dev/null | |||
| @@ -1,89 +0,0 @@ | |||
| 1 | {-# LANGUAGE OverloadedStrings #-} | ||
| 2 | |||
| 3 | module Main where | ||
| 4 | |||
| 5 | import System.Taffybar (startTaffybar) | ||
| 6 | import System.Taffybar.Context (TaffybarConfig(..)) | ||
| 7 | import System.Taffybar.Hooks | ||
| 8 | import System.Taffybar.SimpleConfig hiding (SimpleTaffyConfig(cssPaths)) | ||
| 9 | import System.Taffybar.Widget | ||
| 10 | import qualified System.Taffybar.Widget.Clock as MyClock | ||
| 11 | import System.Taffybar.Widget.TooltipBattery | ||
| 12 | |||
| 13 | import Data.Time.Format | ||
| 14 | import Data.Time.LocalTime | ||
| 15 | import Data.Time.Calendar.WeekDate | ||
| 16 | |||
| 17 | import qualified Data.Text as T | ||
| 18 | |||
| 19 | import Control.Exception (SomeException, try) | ||
| 20 | import Control.Monad.Trans.Reader (mapReaderT) | ||
| 21 | |||
| 22 | import Paths_gkleen_sif_taffybar | ||
| 23 | |||
| 24 | import System.Log.Logger | ||
| 25 | |||
| 26 | |||
| 27 | main :: IO () | ||
| 28 | main = do | ||
| 29 | logger <- getLogger "System.Taffybar" | ||
| 30 | saveGlobalLogger $ setLevel INFO logger | ||
| 31 | |||
| 32 | myCssPath <- getDataFileName "taffybar.css" | ||
| 33 | startTaffybar taffybarConfig{ cssPaths = pure myCssPath } | ||
| 34 | |||
| 35 | |||
| 36 | taffybarConfig :: TaffybarConfig | ||
| 37 | taffybarConfig = | ||
| 38 | let myWorkspacesConfig = | ||
| 39 | defaultWorkspacesConfig | ||
| 40 | { maxIcons = Just 0 | ||
| 41 | , widgetGap = 7 | ||
| 42 | , showWorkspaceFn = \case | ||
| 43 | -- Workspace{ workspaceState = Empty } -> False | ||
| 44 | Workspace{ workspaceName } | workspaceName == "NSP" -> False | ||
| 45 | _other -> True | ||
| 46 | , getWindowIconPixbuf = \i d -> either (\(_ :: SomeException) -> Nothing) id <$> mapReaderT try (defaultGetWindowIconPixbuf i d) | ||
| 47 | , urgentWorkspaceState = True | ||
| 48 | } | ||
| 49 | workspaces = workspacesNew myWorkspacesConfig | ||
| 50 | clock = MyClock.textClockNewWith MyClock.defaultClockConfig | ||
| 51 | { MyClock.clockUpdateStrategy = MyClock.RoundedTargetInterval 1 0.0 | ||
| 52 | , MyClock.clockFormat = \tl zt@ZonedTime{ zonedTimeToLocalTime = LocalTime{ localDay } } | ||
| 53 | -> let date = formatTime tl "%Y-%m-%d" localDay | ||
| 54 | weekdate = "W" <> show2 woy <> "-" <> show dow | ||
| 55 | where (_, woy, dow) = toWeekDate localDay | ||
| 56 | show2 :: Int -> String | ||
| 57 | show2 x = replicate (2 - length s) '0' ++ s | ||
| 58 | where s = show x | ||
| 59 | time = formatTime tl "%H:%M:%S%Ez" zt | ||
| 60 | in T.intercalate " " $ map T.pack [weekdate, date, time] | ||
| 61 | } | ||
| 62 | layout = layoutNew defaultLayoutConfig | ||
| 63 | windowsW = windowsNew defaultWindowsConfig | ||
| 64 | { getMenuLabel = truncatedGetMenuLabel 80 | ||
| 65 | , getActiveLabel = truncatedGetActiveLabel 80 | ||
| 66 | } | ||
| 67 | worktime = commandRunnerNew 60 "worktime" [] "worktime" | ||
| 68 | worktimeToday = commandRunnerNew 60 "worktime" ["today"] "worktime today" | ||
| 69 | -- See https://github.com/taffybar/gtk-sni-tray#statusnotifierwatcher | ||
| 70 | -- for a better way to set up the sni tray | ||
| 71 | -- tray = sniTrayThatStartsWatcherEvenThoughThisIsABadWayToDoIt | ||
| 72 | tray = sniTrayNew | ||
| 73 | myConfig = defaultSimpleTaffyConfig | ||
| 74 | { startWidgets = | ||
| 75 | workspaces : map (>>= buildContentsBox) [ layout, windowsW ] | ||
| 76 | , endWidgets = map (>>= buildContentsBox) $ reverse | ||
| 77 | -- , mpris2New | ||
| 78 | [ worktime, worktimeToday | ||
| 79 | , clock | ||
| 80 | , tray | ||
| 81 | , batteryIconTooltipNew "$status$ $percentage$%$if(time)$$if(rate)$ ($rate$W $time$)$else$ ($time$)$endif$$elseif(rate)$ ($rate$W)$endif$" | ||
| 82 | ] | ||
| 83 | , barPosition = Top | ||
| 84 | , barPadding = 2 | ||
| 85 | , barHeight = ExactSize 28 | ||
| 86 | , widgetSpacing = 10 | ||
| 87 | } | ||
| 88 | in withBatteryRefresh $ withLogServer $ | ||
| 89 | withToggleServer $ toTaffyConfig myConfig | ||
diff --git a/accounts/gkleen@sif/taffybar/taffybar.css b/accounts/gkleen@sif/taffybar/taffybar.css deleted file mode 100644 index 7a297465..00000000 --- a/accounts/gkleen@sif/taffybar/taffybar.css +++ /dev/null | |||
| @@ -1,146 +0,0 @@ | |||
| 1 | @define-color transparent rgba(0.0, 0.0, 0.0, 0.0); | ||
| 2 | @define-color white #808080; | ||
| 3 | @define-color gray #202020; | ||
| 4 | @define-color green #008000; | ||
| 5 | @define-color yellow #808000; | ||
| 6 | @define-color blue #000080; | ||
| 7 | @define-color red #800000; | ||
| 8 | @define-color black #000000; | ||
| 9 | /* @define-color taffy-blue #0c7cd5; */ | ||
| 10 | @define-color taffy-blue @blue; | ||
| 11 | |||
| 12 | @define-color active-window-color @white; | ||
| 13 | @define-color urgent-window-color @taffy-blue; | ||
| 14 | @define-color font-color @white; | ||
| 15 | @define-color menu-background-color @black; | ||
| 16 | @define-color menu-font-color @white; | ||
| 17 | |||
| 18 | /* Top level styling */ | ||
| 19 | |||
| 20 | .taffy-window * { | ||
| 21 | /* | ||
| 22 | This removes any existing styling from UI elements. Taffybar will not | ||
| 23 | cohere with your gtk theme. | ||
| 24 | */ | ||
| 25 | all: unset; | ||
| 26 | |||
| 27 | font-family: "Fira Sans", sans-serif; | ||
| 28 | font-size: 21px; | ||
| 29 | color: @font-color; | ||
| 30 | } | ||
| 31 | |||
| 32 | .taffy-box { | ||
| 33 | /* border-radius: 10px; */ | ||
| 34 | background-color: @black; | ||
| 35 | } | ||
| 36 | |||
| 37 | .inner-pad { | ||
| 38 | /* padding-bottom: 5px; */ | ||
| 39 | /* padding-top: 5px; */ | ||
| 40 | padding-left: 2px; | ||
| 41 | padding-right: 2px; | ||
| 42 | } | ||
| 43 | |||
| 44 | .contents { | ||
| 45 | /* padding-bottom: 4px; */ | ||
| 46 | /* padding-top: 4px; */ | ||
| 47 | padding-right: 2px; | ||
| 48 | padding-left: 2px; | ||
| 49 | transition: background-color .5s; | ||
| 50 | border-radius: 5px; | ||
| 51 | } | ||
| 52 | |||
| 53 | /* Workspaces styling */ | ||
| 54 | |||
| 55 | .workspace-label { | ||
| 56 | padding-right: 3px; | ||
| 57 | padding-left: 2px; | ||
| 58 | font-size: 21px; | ||
| 59 | } | ||
| 60 | |||
| 61 | .workspace-label.active { | ||
| 62 | color: @green; | ||
| 63 | } | ||
| 64 | .workspace-label.visible { | ||
| 65 | color: @yellow; | ||
| 66 | } | ||
| 67 | .workspace-label.empty { | ||
| 68 | color: @gray; | ||
| 69 | } | ||
| 70 | .workspace-label.urgent { | ||
| 71 | color: @red; | ||
| 72 | } | ||
| 73 | |||
| 74 | .active .contents { | ||
| 75 | background-color: rgba(0.0, 0.0, 0.0, 0.5); | ||
| 76 | } | ||
| 77 | |||
| 78 | .visible .contents { | ||
| 79 | background-color: rgba(0.0, 0.0, 0.0, 0.2); | ||
| 80 | } | ||
| 81 | |||
| 82 | .window-icon-container { | ||
| 83 | transition: opacity .5s, box-shadow .5s; | ||
| 84 | opacity: 1; | ||
| 85 | } | ||
| 86 | |||
| 87 | /* This gives space for the box-shadow (they look like underlines) that follow. | ||
| 88 | This will actually affect all widgets, (not just the workspace icons), but | ||
| 89 | that is what we want since we want the icons to look the same. */ | ||
| 90 | .auto-size-image, .sni-tray { | ||
| 91 | padding-top: 3px; | ||
| 92 | padding-bottom: 3px; | ||
| 93 | } | ||
| 94 | |||
| 95 | .window-icon-container.active { | ||
| 96 | box-shadow: inset 0 -3px @white; | ||
| 97 | } | ||
| 98 | |||
| 99 | .window-icon-container.urgent { | ||
| 100 | box-shadow: inset 0 -3px @urgent-window-color; | ||
| 101 | } | ||
| 102 | |||
| 103 | .window-icon-container.inactive .window-icon { | ||
| 104 | padding: 0px; | ||
| 105 | } | ||
| 106 | |||
| 107 | .window-icon-container.minimized .window-icon { | ||
| 108 | opacity: .3; | ||
| 109 | } | ||
| 110 | |||
| 111 | .window-icon { | ||
| 112 | opacity: 1; | ||
| 113 | transition: opacity .5s; | ||
| 114 | } | ||
| 115 | |||
| 116 | /* Button styling */ | ||
| 117 | |||
| 118 | button { | ||
| 119 | background-color: @transparent; | ||
| 120 | border-width: 0px; | ||
| 121 | border-radius: 0px; | ||
| 122 | } | ||
| 123 | |||
| 124 | button:checked, button:hover .Contents:hover { | ||
| 125 | box-shadow: inset 0 -3px @taffy-blue; | ||
| 126 | } | ||
| 127 | |||
| 128 | /* Menu styling */ | ||
| 129 | |||
| 130 | /* The ".taffy-window" prefixed selectors are needed because if they aren't present, | ||
| 131 | the top level .Taffybar selector takes precedence */ | ||
| 132 | .taffy-window menuitem *, menuitem * { | ||
| 133 | color: @menu-font-color; | ||
| 134 | } | ||
| 135 | |||
| 136 | .taffy-window menuitem, menuitem { | ||
| 137 | background-color: @menu-background-color; | ||
| 138 | } | ||
| 139 | |||
| 140 | .taffy-window menuitem:hover, menuitem:hover { | ||
| 141 | background-color: @taffy-blue; | ||
| 142 | } | ||
| 143 | |||
| 144 | .taffy-window menuitem:hover > label, menuitem:hover > label { | ||
| 145 | color: @white; | ||
| 146 | } | ||
diff --git a/accounts/gkleen@sif/utils/async-yt-dlp.nix b/accounts/gkleen@sif/utils/async-yt-dlp.nix new file mode 100644 index 00000000..c3b82ec5 --- /dev/null +++ b/accounts/gkleen@sif/utils/async-yt-dlp.nix | |||
| @@ -0,0 +1,57 @@ | |||
| 1 | { writers, python3Packages, ... }: | ||
| 2 | |||
| 3 | writers.writePython3Bin "async-yt-dlp" { | ||
| 4 | libraries = with python3Packages; [ yt-dlp ]; | ||
| 5 | flakeIgnore = ["E501"]; | ||
| 6 | } '' | ||
| 7 | import sys | ||
| 8 | import os | ||
| 9 | |||
| 10 | import yt_dlp | ||
| 11 | import yt_dlp.options | ||
| 12 | from collections import namedtuple | ||
| 13 | import socket | ||
| 14 | from pathlib import Path | ||
| 15 | import json | ||
| 16 | |||
| 17 | create_parser = yt_dlp.options.create_parser | ||
| 18 | |||
| 19 | |||
| 20 | def parse_patched_options(opts): | ||
| 21 | patched_parser = create_parser() | ||
| 22 | patched_parser.defaults.update({ | ||
| 23 | 'ignoreerrors': False, | ||
| 24 | 'retries': 0, | ||
| 25 | 'fragment_retries': 0, | ||
| 26 | 'extract_flat': False, | ||
| 27 | 'concat_playlist': 'never', | ||
| 28 | }) | ||
| 29 | yt_dlp.options.create_parser = lambda: patched_parser | ||
| 30 | try: | ||
| 31 | return yt_dlp.parse_options(opts) | ||
| 32 | finally: | ||
| 33 | yt_dlp.options.create_parser = create_parser | ||
| 34 | |||
| 35 | |||
| 36 | default_opts = parse_patched_options([]).ydl_opts | ||
| 37 | |||
| 38 | |||
| 39 | def cli_to_api(opts): | ||
| 40 | opts = parse_patched_options(opts) | ||
| 41 | urls = opts.urls | ||
| 42 | opts = opts.ydl_opts | ||
| 43 | |||
| 44 | diff = {k: v for k, v in opts.items() if default_opts[k] != v} | ||
| 45 | if 'postprocessors' in diff: | ||
| 46 | diff['postprocessors'] = [pp for pp in diff['postprocessors'] | ||
| 47 | if pp not in default_opts['postprocessors']] | ||
| 48 | return namedtuple('Options', ('params', 'urls'))(diff, urls) | ||
| 49 | |||
| 50 | |||
| 51 | if __name__ == '__main__': | ||
| 52 | opts = cli_to_api(sys.argv[1:]) | ||
| 53 | with socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) as sock: | ||
| 54 | sock.connect(str(Path(os.environ["XDG_RUNTIME_DIR"]) / "yt-dlp.sock").encode('utf-8')) | ||
| 55 | with sock.makefile(mode='w', buffering=1, encoding='utf-8') as fh: | ||
| 56 | json.dump(opts._asdict(), fh) | ||
| 57 | '' | ||
diff --git a/accounts/gkleen@sif/utils/pdf2pdf.nix b/accounts/gkleen@sif/utils/pdf2pdf.nix new file mode 100644 index 00000000..9f4cbc3e --- /dev/null +++ b/accounts/gkleen@sif/utils/pdf2pdf.nix | |||
| @@ -0,0 +1,8 @@ | |||
| 1 | pkgs@{ lib, resholve, zsh, ghostscript_headless, ... }: | ||
| 2 | |||
| 3 | resholve.writeScriptBin "pdf2pdf" { | ||
| 4 | inputs = with pkgs; [ghostscript_headless]; | ||
| 5 | interpreter = lib.getExe zsh; | ||
| 6 | } '' | ||
| 7 | exec gs -dPDFSETTINGS=/prepress -dNOPAUSE -dBATCH -sDEVICE=pdfwrite -dSAFER -dPreserveAnnots=false "-sOutputFile=''${2}" "''${1}" | ||
| 8 | '' | ||
diff --git a/accounts/gkleen@sif/utils/sieve-edit.nix b/accounts/gkleen@sif/utils/sieve-edit.nix new file mode 100644 index 00000000..f985a3f6 --- /dev/null +++ b/accounts/gkleen@sif/utils/sieve-edit.nix | |||
| @@ -0,0 +1,24 @@ | |||
| 1 | pkgs@{ lib, resholve, zsh, sieve-connect, sops, ... }: | ||
| 2 | |||
| 3 | resholve.writeScriptBin "sieve-edit" { | ||
| 4 | inputs = with pkgs; [sieve-connect sops]; | ||
| 5 | interpreter = lib.getExe zsh; | ||
| 6 | execer = with pkgs; [ | ||
| 7 | "cannot:${lib.getExe sieve-connect}" | ||
| 8 | "cannot:${lib.getExe sops}" | ||
| 9 | ]; | ||
| 10 | } '' | ||
| 11 | host=$1; shift | ||
| 12 | case "$host" in | ||
| 13 | surtr) | ||
| 14 | sieve-connect -s surtr.yggdrasil.li -m EXTERNAL --clientkey <(sops decrypt $HOME/projects/machines/hosts/surtr/email/ca/gkleen@sif.key) --clientcert $HOME/projects/machines/hosts/surtr/email/ca/gkleen@sif.crt --edit --remotesieve sieve | ||
| 15 | ;; | ||
| 16 | ymir) | ||
| 17 | sieve-connect -s ymir.yggdrasil.li -u gkleen --edit --remotesieve sieve | ||
| 18 | ;; | ||
| 19 | *) | ||
| 20 | echo "Unknown host: ‘$host’" >&2 | ||
| 21 | return 2 | ||
| 22 | ;; | ||
| 23 | esac | ||
| 24 | '' | ||
diff --git a/accounts/gkleen@sif/xmonad/.gitignore b/accounts/gkleen@sif/xmonad/.gitignore deleted file mode 100644 index c11891cd..00000000 --- a/accounts/gkleen@sif/xmonad/.gitignore +++ /dev/null | |||
| @@ -1,4 +0,0 @@ | |||
| 1 | **/#*# | ||
| 2 | **/.stack-work/ | ||
| 3 | /stack.yaml.lock | ||
| 4 | /*.cabal | ||
diff --git a/accounts/gkleen@sif/xmonad/default.nix b/accounts/gkleen@sif/xmonad/default.nix deleted file mode 100644 index 8790c12f..00000000 --- a/accounts/gkleen@sif/xmonad/default.nix +++ /dev/null | |||
| @@ -1,7 +0,0 @@ | |||
| 1 | argumentPackages@{ ... }: | ||
| 2 | |||
| 3 | let | ||
| 4 | # defaultPackages = (import ./stackage.nix {}); | ||
| 5 | # haskellPackages = defaultPackages // argumentPackages; | ||
| 6 | haskellPackages = argumentPackages; | ||
| 7 | in haskellPackages.callPackage ./xmonad-yggdrasil.nix {} | ||
diff --git a/accounts/gkleen@sif/xmonad/lib/XMonad/Mpv.hs b/accounts/gkleen@sif/xmonad/lib/XMonad/Mpv.hs deleted file mode 100644 index e6accdcc..00000000 --- a/accounts/gkleen@sif/xmonad/lib/XMonad/Mpv.hs +++ /dev/null | |||
| @@ -1,127 +0,0 @@ | |||
| 1 | {-# LANGUAGE DeriveGeneric, OverloadedLists, OverloadedStrings, ViewPatterns, ExistentialQuantification, MultiWayIf #-} | ||
| 2 | |||
| 3 | module XMonad.Mpv | ||
| 4 | ( MpvCommand(..), MpvResponse(..), MpvException(..) | ||
| 5 | , mpv | ||
| 6 | , mpvDir | ||
| 7 | , mpvAll, mpvOne | ||
| 8 | , mpvResponse | ||
| 9 | ) where | ||
| 10 | |||
| 11 | import Data.Aeson | ||
| 12 | |||
| 13 | import Data.Monoid | ||
| 14 | |||
| 15 | import Network.Socket hiding (recv) | ||
| 16 | import Network.Socket.ByteString | ||
| 17 | |||
| 18 | import qualified Data.ByteString as BS | ||
| 19 | import qualified Data.ByteString.Char8 as CBS | ||
| 20 | import qualified Data.ByteString.Lazy as LBS | ||
| 21 | |||
| 22 | import GHC.Generics (Generic) | ||
| 23 | import Data.Typeable (Typeable) | ||
| 24 | import Data.String (IsString(..)) | ||
| 25 | |||
| 26 | import Control.Exception | ||
| 27 | |||
| 28 | import System.IO.Temp (getCanonicalTemporaryDirectory) | ||
| 29 | |||
| 30 | import Control.Monad | ||
| 31 | import Control.Exception (bracket) | ||
| 32 | import Control.Monad.IO.Class (MonadIO(..)) | ||
| 33 | |||
| 34 | import System.FilePath | ||
| 35 | import System.Directory (getDirectoryContents) | ||
| 36 | |||
| 37 | import Data.List | ||
| 38 | import Data.Either | ||
| 39 | import Data.Maybe | ||
| 40 | |||
| 41 | import Debug.Trace | ||
| 42 | |||
| 43 | |||
| 44 | data MpvCommand | ||
| 45 | = forall a. ToJSON a => MpvSetProperty String a | ||
| 46 | | MpvGetProperty String | ||
| 47 | data MpvResponse | ||
| 48 | = MpvError String | ||
| 49 | | MpvSuccess (Maybe Value) | ||
| 50 | deriving (Read, Show, Generic, Eq) | ||
| 51 | data MpvException = MpvException String | ||
| 52 | | MpvNoValue | ||
| 53 | | MpvNoParse String | ||
| 54 | deriving (Generic, Typeable, Read, Show) | ||
| 55 | instance Exception MpvException | ||
| 56 | |||
| 57 | |||
| 58 | instance ToJSON MpvCommand where | ||
| 59 | toJSON (MpvSetProperty name val) = Array ["set_property", fromString name, toJSON val] | ||
| 60 | toJSON (MpvGetProperty name) = Array ["get_property", fromString name] | ||
| 61 | |||
| 62 | instance FromJSON MpvResponse where | ||
| 63 | parseJSON = withObject "response object" $ \obj -> do | ||
| 64 | mval <- obj .:? "data" | ||
| 65 | err <- obj .: "error" | ||
| 66 | |||
| 67 | let ret | ||
| 68 | | err == "success" = MpvSuccess mval | ||
| 69 | | otherwise = MpvError err | ||
| 70 | |||
| 71 | return ret | ||
| 72 | |||
| 73 | mpvSocket :: FilePath -> (Socket -> IO a) -> IO a | ||
| 74 | mpvSocket sockPath = withSocketsDo . bracket mkSock close | ||
| 75 | where | ||
| 76 | mkSock = do | ||
| 77 | sock <- socket AF_UNIX Stream defaultProtocol | ||
| 78 | connect sock $ SockAddrUnix (traceId sockPath) | ||
| 79 | return sock | ||
| 80 | |||
| 81 | mpvResponse :: FromJSON v => MpvResponse -> IO v | ||
| 82 | mpvResponse (MpvError str) = throwIO $ MpvException str | ||
| 83 | mpvResponse (MpvSuccess Nothing) = throwIO MpvNoValue | ||
| 84 | mpvResponse (MpvSuccess (Just v)) = case fromJSON v of | ||
| 85 | Success v' -> return v' | ||
| 86 | Error str -> throwIO $ MpvNoParse str | ||
| 87 | |||
| 88 | mpv :: FilePath -> MpvCommand -> IO MpvResponse | ||
| 89 | mpv sockPath cmd = mpvSocket sockPath $ \sock -> do | ||
| 90 | let message = (`BS.append` "\n") . LBS.toStrict . encode $ Object [("command", toJSON cmd)] | ||
| 91 | traceIO $ show message | ||
| 92 | sendAll sock message | ||
| 93 | let recvAll = do | ||
| 94 | prefix <- recv sock 4096 | ||
| 95 | if | ||
| 96 | | (prefix', rest) <- CBS.break (== '\n') prefix | ||
| 97 | , not (BS.null rest) -> return prefix' | ||
| 98 | | BS.null prefix -> return prefix | ||
| 99 | | otherwise -> BS.append prefix <$> recvAll | ||
| 100 | response <- recvAll | ||
| 101 | traceIO $ show response | ||
| 102 | either (ioError . userError) return . traceShowId $ eitherDecodeStrict' response | ||
| 103 | |||
| 104 | mpvDir :: Exception e => FilePath -> (FilePath -> [(FilePath, Either e MpvResponse)] -> Maybe MpvCommand) -> IO [(FilePath, Either e MpvResponse)] | ||
| 105 | mpvDir dir step = do | ||
| 106 | socks <- filter (".sock" `isSuffixOf`) <$> getDirectoryContents dir | ||
| 107 | go [] socks | ||
| 108 | where | ||
| 109 | go acc [] = return acc | ||
| 110 | go acc (sock:socks) | ||
| 111 | | Just cmd <- step sock acc = do | ||
| 112 | res <- try $ mpv (dir </> sock) cmd | ||
| 113 | go ((sock, res) : acc) socks | ||
| 114 | | otherwise = | ||
| 115 | go acc socks | ||
| 116 | |||
| 117 | mpvAll :: FilePath -> MpvCommand -> IO [MpvResponse] | ||
| 118 | mpvAll dir cmd = do | ||
| 119 | results <- map snd <$> (mpvDir dir (\_ _ -> Just cmd) :: IO [(FilePath, Either SomeException MpvResponse)]) | ||
| 120 | mapM (either throwIO return) results | ||
| 121 | |||
| 122 | mpvOne :: FilePath -> MpvCommand -> IO (Maybe MpvResponse) | ||
| 123 | mpvOne dir cmd = listToMaybe . snd . partitionEithers . map snd <$> (mpvDir dir step :: IO [(FilePath, Either SomeException MpvResponse)]) | ||
| 124 | where | ||
| 125 | step _ results | ||
| 126 | | any (isRight . snd) results = Nothing | ||
| 127 | | otherwise = Just cmd | ||
diff --git a/accounts/gkleen@sif/xmonad/lib/XMonad/Prompt/MyPass.hs b/accounts/gkleen@sif/xmonad/lib/XMonad/Prompt/MyPass.hs deleted file mode 100644 index 1caefae5..00000000 --- a/accounts/gkleen@sif/xmonad/lib/XMonad/Prompt/MyPass.hs +++ /dev/null | |||
| @@ -1,94 +0,0 @@ | |||
| 1 | module XMonad.Prompt.MyPass | ||
| 2 | ( | ||
| 3 | -- * Usages | ||
| 4 | -- $usages | ||
| 5 | mkPassPrompt | ||
| 6 | ) where | ||
| 7 | |||
| 8 | import Control.Monad (liftM) | ||
| 9 | import XMonad.Core | ||
| 10 | import XMonad.Prompt ( XPrompt | ||
| 11 | , showXPrompt | ||
| 12 | , commandToComplete | ||
| 13 | , nextCompletion | ||
| 14 | , getNextCompletion | ||
| 15 | , XPConfig | ||
| 16 | , mkXPrompt | ||
| 17 | , searchPredicate) | ||
| 18 | import System.Directory (getHomeDirectory) | ||
| 19 | import System.FilePath (takeExtension, dropExtension, combine) | ||
| 20 | import System.Posix.Env (getEnv) | ||
| 21 | import XMonad.Util.Run (runProcessWithInput) | ||
| 22 | |||
| 23 | -- $usages | ||
| 24 | -- You can use this module with the following in your @~\/.xmonad\/xmonad.hs@: | ||
| 25 | -- | ||
| 26 | -- > import XMonad.Prompt.Pass | ||
| 27 | -- | ||
| 28 | -- Then add a keybinding for 'passPrompt', 'passGeneratePrompt' or 'passRemovePrompt': | ||
| 29 | -- | ||
| 30 | -- > , ((modMask x , xK_p) , passPrompt xpconfig) | ||
| 31 | -- > , ((modMask x .|. controlMask, xK_p) , passGeneratePrompt xpconfig) | ||
| 32 | -- > , ((modMask x .|. controlMask .|. shiftMask, xK_p), passRemovePrompt xpconfig) | ||
| 33 | -- | ||
| 34 | -- For detailed instructions on: | ||
| 35 | -- | ||
| 36 | -- - editing your key bindings, see "XMonad.Doc.Extending#Editing_key_bindings". | ||
| 37 | -- | ||
| 38 | -- - how to setup the password storage, see <http://git.zx2c4.com/password-store/about/> | ||
| 39 | -- | ||
| 40 | |||
| 41 | type Predicate = String -> String -> Bool | ||
| 42 | |||
| 43 | getPassCompl :: [String] -> Predicate -> String -> IO [String] | ||
| 44 | getPassCompl compls p s | ||
| 45 | | length s <= minL | ||
| 46 | , all ((> minL) . length) compls = return [] | ||
| 47 | | otherwise = do return $ filter (p s) compls | ||
| 48 | where | ||
| 49 | minL = 3 | ||
| 50 | |||
| 51 | type PromptLabel = String | ||
| 52 | |||
| 53 | data Pass = Pass PromptLabel | ||
| 54 | |||
| 55 | instance XPrompt Pass where | ||
| 56 | showXPrompt (Pass prompt) = prompt ++ ": " | ||
| 57 | commandToComplete _ c = c | ||
| 58 | nextCompletion _ = getNextCompletion | ||
| 59 | |||
| 60 | -- | Default password store folder in $HOME/.password-store | ||
| 61 | -- | ||
| 62 | passwordStoreFolderDefault :: String -> String | ||
| 63 | passwordStoreFolderDefault home = combine home ".password-store" | ||
| 64 | |||
| 65 | -- | Compute the password store's location. | ||
| 66 | -- Use the PASSWORD_STORE_DIR environment variable to set the password store. | ||
| 67 | -- If empty, return the password store located in user's home. | ||
| 68 | -- | ||
| 69 | passwordStoreFolder :: IO String | ||
| 70 | passwordStoreFolder = | ||
| 71 | getEnv "PASSWORD_STORE_DIR" >>= computePasswordStoreDir | ||
| 72 | where computePasswordStoreDir Nothing = liftM passwordStoreFolderDefault getHomeDirectory | ||
| 73 | computePasswordStoreDir (Just storeDir) = return storeDir | ||
| 74 | |||
| 75 | -- | A pass prompt factory | ||
| 76 | -- | ||
| 77 | mkPassPrompt :: PromptLabel -> (String -> X ()) -> XPConfig -> X () | ||
| 78 | mkPassPrompt promptLabel passwordFunction xpconfig = do | ||
| 79 | passwords <- io (passwordStoreFolder >>= getPasswords) | ||
| 80 | mkXPrompt (Pass promptLabel) xpconfig (getPassCompl passwords $ searchPredicate xpconfig) passwordFunction | ||
| 81 | |||
| 82 | -- | Retrieve the list of passwords from the password storage 'passwordStoreDir | ||
| 83 | getPasswords :: FilePath -> IO [String] | ||
| 84 | getPasswords passwordStoreDir = do | ||
| 85 | files <- runProcessWithInput "find" [ | ||
| 86 | passwordStoreDir, | ||
| 87 | "-type", "f", | ||
| 88 | "-name", "*.gpg", | ||
| 89 | "-printf", "%P\n"] [] | ||
| 90 | return $ map removeGpgExtension $ lines files | ||
| 91 | |||
| 92 | removeGpgExtension :: String -> String | ||
| 93 | removeGpgExtension file | takeExtension file == ".gpg" = dropExtension file | ||
| 94 | | otherwise = file | ||
diff --git a/accounts/gkleen@sif/xmonad/lib/XMonad/Prompt/MyShell.hs b/accounts/gkleen@sif/xmonad/lib/XMonad/Prompt/MyShell.hs deleted file mode 100644 index c268f87d..00000000 --- a/accounts/gkleen@sif/xmonad/lib/XMonad/Prompt/MyShell.hs +++ /dev/null | |||
| @@ -1,105 +0,0 @@ | |||
| 1 | module XMonad.Prompt.MyShell | ||
| 2 | ( Shell (..) | ||
| 3 | , shellPrompt | ||
| 4 | , prompt | ||
| 5 | , safePrompt | ||
| 6 | , unsafePrompt | ||
| 7 | , getCommands | ||
| 8 | , getShellCompl | ||
| 9 | , split | ||
| 10 | ) where | ||
| 11 | |||
| 12 | import Codec.Binary.UTF8.String (encodeString) | ||
| 13 | import Control.Exception as E | ||
| 14 | import Control.Monad (forM) | ||
| 15 | import Data.List (isPrefixOf) | ||
| 16 | import System.Directory (doesDirectoryExist, getDirectoryContents) | ||
| 17 | import System.Environment (getEnv) | ||
| 18 | import System.Posix.Files (getFileStatus, isDirectory) | ||
| 19 | |||
| 20 | import XMonad hiding (config) | ||
| 21 | import XMonad.Prompt | ||
| 22 | import XMonad.Util.Run | ||
| 23 | |||
| 24 | econst :: Monad m => a -> IOException -> m a | ||
| 25 | econst = const . return | ||
| 26 | |||
| 27 | data Shell = Shell String | ||
| 28 | |||
| 29 | instance XPrompt Shell where | ||
| 30 | showXPrompt (Shell q) = q | ||
| 31 | completionToCommand _ = escape | ||
| 32 | |||
| 33 | shellPrompt :: String -> XPConfig -> X () | ||
| 34 | shellPrompt q c = do | ||
| 35 | cmds <- io getCommands | ||
| 36 | mkXPrompt (Shell q) c (getShellCompl cmds) spawn | ||
| 37 | |||
| 38 | {- $spawns | ||
| 39 | See safe and unsafeSpawn in "XMonad.Util.Run". | ||
| 40 | prompt is an alias for safePrompt; | ||
| 41 | safePrompt and unsafePrompt work on the same principles, but will use | ||
| 42 | XPrompt to interactively query the user for input; the appearance is | ||
| 43 | set by passing an XPConfig as the second argument. The first argument | ||
| 44 | is the program to be run with the interactive input. | ||
| 45 | You would use these like this: | ||
| 46 | |||
| 47 | > , ((modm, xK_b), safePrompt "firefox" greenXPConfig) | ||
| 48 | > , ((modm .|. shiftMask, xK_c), prompt ("xterm" ++ " -e") greenXPConfig) | ||
| 49 | |||
| 50 | Note that you want to use safePrompt for Firefox input, as Firefox | ||
| 51 | wants URLs, and unsafePrompt for the XTerm example because this allows | ||
| 52 | you to easily start a terminal executing an arbitrary command, like | ||
| 53 | 'top'. -} | ||
| 54 | |||
| 55 | prompt, unsafePrompt, safePrompt :: String -> FilePath -> XPConfig -> X () | ||
| 56 | prompt = unsafePrompt | ||
| 57 | safePrompt q c config = mkXPrompt (Shell q) config (getShellCompl [c]) run | ||
| 58 | where run = safeSpawn c . return | ||
| 59 | unsafePrompt q c config = mkXPrompt (Shell q) config (getShellCompl [c]) run | ||
| 60 | where run a = unsafeSpawn $ c ++ " " ++ a | ||
| 61 | |||
| 62 | getShellCompl :: [String] -> String -> IO [String] | ||
| 63 | getShellCompl cmds s | s == "" || last s == ' ' = return [] | ||
| 64 | | otherwise = do | ||
| 65 | f <- fmap lines $ runProcessWithInput "bash" [] ("compgen -A file -- " | ||
| 66 | ++ s ++ "\n") | ||
| 67 | files <- case f of | ||
| 68 | [x] -> do fs <- getFileStatus (encodeString x) | ||
| 69 | if isDirectory fs then return [x ++ "/"] | ||
| 70 | else return [x] | ||
| 71 | _ -> return f | ||
| 72 | return . uniqSort $ files ++ commandCompletionFunction cmds s | ||
| 73 | |||
| 74 | commandCompletionFunction :: [String] -> String -> [String] | ||
| 75 | commandCompletionFunction cmds str | '/' `elem` str = [] | ||
| 76 | | otherwise = filter (isPrefixOf str) cmds | ||
| 77 | |||
| 78 | getCommands :: IO [String] | ||
| 79 | getCommands = do | ||
| 80 | p <- getEnv "PATH" `E.catch` econst [] | ||
| 81 | let ds = filter (/= "") $ split ':' p | ||
| 82 | es <- forM ds $ \d -> do | ||
| 83 | exists <- doesDirectoryExist d | ||
| 84 | if exists | ||
| 85 | then getDirectoryContents d | ||
| 86 | else return [] | ||
| 87 | return . uniqSort . filter ((/= '.') . head) . concat $ es | ||
| 88 | |||
| 89 | split :: Eq a => a -> [a] -> [[a]] | ||
| 90 | split _ [] = [] | ||
| 91 | split e l = | ||
| 92 | f : split e (rest ls) | ||
| 93 | where | ||
| 94 | (f,ls) = span (/=e) l | ||
| 95 | rest s | s == [] = [] | ||
| 96 | | otherwise = tail s | ||
| 97 | |||
| 98 | escape :: String -> String | ||
| 99 | escape [] = "" | ||
| 100 | escape (x:xs) | ||
| 101 | | isSpecialChar x = '\\' : x : escape xs | ||
| 102 | | otherwise = x : escape xs | ||
| 103 | |||
| 104 | isSpecialChar :: Char -> Bool | ||
| 105 | isSpecialChar = flip elem " &\\@\"'#?$*()[]{};" | ||
diff --git a/accounts/gkleen@sif/xmonad/lib/XMonad/Prompt/MySsh.hs b/accounts/gkleen@sif/xmonad/lib/XMonad/Prompt/MySsh.hs deleted file mode 100644 index 998c533e..00000000 --- a/accounts/gkleen@sif/xmonad/lib/XMonad/Prompt/MySsh.hs +++ /dev/null | |||
| @@ -1,246 +0,0 @@ | |||
| 1 | module XMonad.Prompt.MySsh | ||
| 2 | ( -- * Usage | ||
| 3 | -- $usage | ||
| 4 | sshPrompt, | ||
| 5 | Ssh, | ||
| 6 | Override (..), | ||
| 7 | mkOverride, | ||
| 8 | Conn (..), | ||
| 9 | moshCmd, | ||
| 10 | moshCmd', | ||
| 11 | sshCmd, | ||
| 12 | inTmux, | ||
| 13 | withEnv | ||
| 14 | ) where | ||
| 15 | |||
| 16 | import XMonad | ||
| 17 | import XMonad.Util.Run | ||
| 18 | import XMonad.Prompt | ||
| 19 | |||
| 20 | import System.Directory | ||
| 21 | import System.Environment | ||
| 22 | import qualified Control.Exception as E | ||
| 23 | |||
| 24 | import Control.Monad | ||
| 25 | import Data.Maybe | ||
| 26 | |||
| 27 | import Text.Parsec.String | ||
| 28 | import Text.Parsec | ||
| 29 | import Data.Char (isSpace) | ||
| 30 | |||
| 31 | econst :: Monad m => a -> E.IOException -> m a | ||
| 32 | econst = const . return | ||
| 33 | |||
| 34 | -- $usage | ||
| 35 | -- 1. In your @~\/.xmonad\/xmonad.hs@: | ||
| 36 | -- | ||
| 37 | -- > import XMonad.Prompt | ||
| 38 | -- > import XMonad.Prompt.Ssh | ||
| 39 | -- | ||
| 40 | -- 2. In your keybindings add something like: | ||
| 41 | -- | ||
| 42 | -- > , ((modm .|. controlMask, xK_s), sshPrompt defaultXPConfig) | ||
| 43 | -- | ||
| 44 | -- Keep in mind, that if you want to use the completion you have to | ||
| 45 | -- disable the "HashKnownHosts" option in your ssh_config | ||
| 46 | -- | ||
| 47 | -- For detailed instruction on editing the key binding see | ||
| 48 | -- "XMonad.Doc.Extending#Editing_key_bindings". | ||
| 49 | |||
| 50 | data Override = Override | ||
| 51 | { oUser :: Maybe String | ||
| 52 | , oHost :: String | ||
| 53 | , oPort :: Maybe Int | ||
| 54 | , oCommand :: Conn -> String | ||
| 55 | } | ||
| 56 | |||
| 57 | mkOverride = Override { oUser = Nothing, oHost = "", oPort = Nothing, oCommand = sshCmd } | ||
| 58 | sshCmd c = concat | ||
| 59 | [ "ssh -t " | ||
| 60 | , if isJust $ cUser c then (fromJust $ cUser c) ++ "@" else "" | ||
| 61 | , cHost c | ||
| 62 | , if isJust $ cPort c then " -p " ++ (show $ fromJust $ cPort c) else "" | ||
| 63 | , " -- " | ||
| 64 | , cCommand c | ||
| 65 | ] | ||
| 66 | moshCmd c = concat | ||
| 67 | [ "mosh " | ||
| 68 | , if isJust $ cUser c then (fromJust $ cUser c) ++ "@" else "" | ||
| 69 | , cHost c | ||
| 70 | , if isJust $ cPort c then " --ssh=\"ssh -p " ++ (show $ fromJust $ cPort c) ++ "\"" else "" | ||
| 71 | , " -- " | ||
| 72 | , cCommand c | ||
| 73 | ] | ||
| 74 | moshCmd' p c = concat | ||
| 75 | [ "mosh " | ||
| 76 | , "--server=" ++ p ++ " " | ||
| 77 | , if isJust $ cUser c then (fromJust $ cUser c) ++ "@" else "" | ||
| 78 | , cHost c | ||
| 79 | , if isJust $ cPort c then " --ssh=\"ssh -p " ++ (show $ fromJust $ cPort c) ++ "\"" else "" | ||
| 80 | , " -- " | ||
| 81 | , cCommand c | ||
| 82 | ] | ||
| 83 | inTmux Nothing c | ||
| 84 | | null $ cCommand c = c { cCommand = "tmux new-session" } | ||
| 85 | | otherwise = c { cCommand = "tmux new-session \"" ++ (cCommand c) ++ "\"" } | ||
| 86 | inTmux (Just h) c | ||
| 87 | | null $ cCommand c = c { cCommand = "tmux new-session -As " <> h } | ||
| 88 | | otherwise = c { cCommand = "tmux new-session \"" ++ (cCommand c) ++ "\"" } | ||
| 89 | withEnv :: [(String, String)] -> Conn -> Conn | ||
| 90 | withEnv envs c = c { cCommand = "env" ++ (concat $ map (\(n, v) -> ' ' : (n ++ "=" ++ v)) envs) ++ " " ++ (cCommand c) } | ||
| 91 | |||
| 92 | data Conn = Conn | ||
| 93 | { cUser :: Maybe String | ||
| 94 | , cHost :: String | ||
| 95 | , cPort :: Maybe Int | ||
| 96 | , cCommand :: String | ||
| 97 | } deriving (Eq, Show, Read) | ||
| 98 | |||
| 99 | data Ssh = Ssh | ||
| 100 | |||
| 101 | instance XPrompt Ssh where | ||
| 102 | showXPrompt Ssh = "SSH to: " | ||
| 103 | commandToComplete _ c = c | ||
| 104 | nextCompletion _ = getNextCompletion | ||
| 105 | |||
| 106 | toConn :: String -> Maybe Conn | ||
| 107 | toConn = toConn' . parse connParser "(unknown)" | ||
| 108 | toConn' :: Either ParseError Conn -> Maybe Conn | ||
| 109 | toConn' (Left _) = Nothing | ||
| 110 | toConn' (Right a) = Just a | ||
| 111 | |||
| 112 | connParser :: Parser Conn | ||
| 113 | connParser = do | ||
| 114 | spaces | ||
| 115 | user' <- optionMaybe $ try $ do | ||
| 116 | str <- many1 $ satisfy (\c -> (not $ isSpace c) && (c /= '@')) | ||
| 117 | char '@' | ||
| 118 | return str | ||
| 119 | host' <- many1 $ satisfy (not . isSpace) | ||
| 120 | port' <- optionMaybe $ try $ do | ||
| 121 | space | ||
| 122 | string "-p" | ||
| 123 | spaces | ||
| 124 | int <- many1 digit | ||
| 125 | (space >> return ()) <|> eof | ||
| 126 | return $ (read int :: Int) | ||
| 127 | spaces | ||
| 128 | command' <- many anyChar | ||
| 129 | eof | ||
| 130 | return $ Conn | ||
| 131 | { cHost = host' | ||
| 132 | , cUser = user' | ||
| 133 | , cPort = port' | ||
| 134 | , cCommand = command' | ||
| 135 | } | ||
| 136 | |||
| 137 | sshPrompt :: [Override] -> XPConfig -> X () | ||
| 138 | sshPrompt o c = do | ||
| 139 | sc <- io sshComplList | ||
| 140 | mkXPrompt Ssh c (mkComplFunFromList c sc) $ ssh o | ||
| 141 | |||
| 142 | ssh :: [Override] -> String -> X () | ||
| 143 | ssh overrides str = do | ||
| 144 | let cmd = applyOverrides overrides str | ||
| 145 | liftIO $ putStr "SSH Command: " | ||
| 146 | liftIO $ putStrLn cmd | ||
| 147 | runInTerm "" cmd | ||
| 148 | |||
| 149 | applyOverrides :: [Override] -> String -> String | ||
| 150 | applyOverrides [] str = "ssh " ++ str | ||
| 151 | applyOverrides (o:os) str = case (applyOverride o str) of | ||
| 152 | Just str -> str | ||
| 153 | Nothing -> applyOverrides os str | ||
| 154 | |||
| 155 | applyOverride :: Override -> String -> Maybe String | ||
| 156 | applyOverride o str = let | ||
| 157 | conn = toConn str | ||
| 158 | in | ||
| 159 | if isNothing conn then Nothing else | ||
| 160 | case (fromJust conn) `matches` o of | ||
| 161 | True -> Just $ (oCommand o) (fromJust conn) | ||
| 162 | False -> Nothing | ||
| 163 | |||
| 164 | matches :: Conn -> Override -> Bool | ||
| 165 | a `matches` b = and | ||
| 166 | [ justBool (cUser a) (oUser b) (==) | ||
| 167 | , (cHost a) == (oHost b) | ||
| 168 | , justBool (cPort a) (oPort b) (==) | ||
| 169 | ] | ||
| 170 | |||
| 171 | justBool :: Eq a => Maybe a -> Maybe a -> (a -> a -> Bool) -> Bool | ||
| 172 | justBool Nothing _ _ = True | ||
| 173 | justBool _ Nothing _ = True | ||
| 174 | justBool (Just a) (Just b) match = a `match` b | ||
| 175 | |||
| 176 | sshComplList :: IO [String] | ||
| 177 | sshComplList = uniqSort `fmap` liftM2 (++) sshComplListLocal sshComplListGlobal | ||
| 178 | |||
| 179 | sshComplListLocal :: IO [String] | ||
| 180 | sshComplListLocal = do | ||
| 181 | h <- getEnv "HOME" | ||
| 182 | s1 <- sshComplListFile $ h ++ "/.ssh/known_hosts" | ||
| 183 | s2 <- sshComplListConf $ h ++ "/.ssh/config" | ||
| 184 | return $ s1 ++ s2 | ||
| 185 | |||
| 186 | sshComplListGlobal :: IO [String] | ||
| 187 | sshComplListGlobal = do | ||
| 188 | env <- getEnv "SSH_KNOWN_HOSTS" `E.catch` econst "/nonexistent" | ||
| 189 | fs <- mapM fileExists [ env | ||
| 190 | , "/usr/local/etc/ssh/ssh_known_hosts" | ||
| 191 | , "/usr/local/etc/ssh_known_hosts" | ||
| 192 | , "/etc/ssh/ssh_known_hosts" | ||
| 193 | , "/etc/ssh_known_hosts" | ||
| 194 | ] | ||
| 195 | case catMaybes fs of | ||
| 196 | [] -> return [] | ||
| 197 | (f:_) -> sshComplListFile' f | ||
| 198 | |||
| 199 | sshComplListFile :: String -> IO [String] | ||
| 200 | sshComplListFile kh = do | ||
| 201 | f <- doesFileExist kh | ||
| 202 | if f then sshComplListFile' kh | ||
| 203 | else return [] | ||
| 204 | |||
| 205 | sshComplListFile' :: String -> IO [String] | ||
| 206 | sshComplListFile' kh = do | ||
| 207 | l <- readFile kh | ||
| 208 | return $ map (getWithPort . takeWhile (/= ',') . concat . take 1 . words) | ||
| 209 | $ filter nonComment | ||
| 210 | $ lines l | ||
| 211 | |||
| 212 | sshComplListConf :: String -> IO [String] | ||
| 213 | sshComplListConf kh = do | ||
| 214 | f <- doesFileExist kh | ||
| 215 | if f then sshComplListConf' kh | ||
| 216 | else return [] | ||
| 217 | |||
| 218 | sshComplListConf' :: String -> IO [String] | ||
| 219 | sshComplListConf' kh = do | ||
| 220 | l <- readFile kh | ||
| 221 | return $ map (!!1) | ||
| 222 | $ filter isHost | ||
| 223 | $ map words | ||
| 224 | $ lines l | ||
| 225 | where | ||
| 226 | isHost ws = take 1 ws == ["Host"] && length ws > 1 | ||
| 227 | |||
| 228 | fileExists :: String -> IO (Maybe String) | ||
| 229 | fileExists kh = do | ||
| 230 | f <- doesFileExist kh | ||
| 231 | if f then return $ Just kh | ||
| 232 | else return Nothing | ||
| 233 | |||
| 234 | nonComment :: String -> Bool | ||
| 235 | nonComment [] = False | ||
| 236 | nonComment ('#':_) = False | ||
| 237 | nonComment ('|':_) = False -- hashed, undecodeable | ||
| 238 | nonComment _ = True | ||
| 239 | |||
| 240 | getWithPort :: String -> String | ||
| 241 | getWithPort ('[':str) = host ++ " -p " ++ port | ||
| 242 | where (host,p) = break (==']') str | ||
| 243 | port = case p of | ||
| 244 | ']':':':x -> x | ||
| 245 | _ -> "22" | ||
| 246 | getWithPort str = str | ||
diff --git a/accounts/gkleen@sif/xmonad/package.yaml b/accounts/gkleen@sif/xmonad/package.yaml deleted file mode 100644 index f65137af..00000000 --- a/accounts/gkleen@sif/xmonad/package.yaml +++ /dev/null | |||
| @@ -1,31 +0,0 @@ | |||
| 1 | name: xmonad-yggdrasil | ||
| 2 | |||
| 3 | executables: | ||
| 4 | xmonad: | ||
| 5 | dependencies: | ||
| 6 | - base | ||
| 7 | - xmonad | ||
| 8 | - xmonad-contrib | ||
| 9 | - aeson | ||
| 10 | - bytestring | ||
| 11 | - text | ||
| 12 | - temporary | ||
| 13 | - filepath | ||
| 14 | - directory | ||
| 15 | - network | ||
| 16 | - unix | ||
| 17 | - utf8-string | ||
| 18 | - parsec | ||
| 19 | - process | ||
| 20 | - mtl | ||
| 21 | - X11 | ||
| 22 | - transformers | ||
| 23 | - containers | ||
| 24 | - hostname | ||
| 25 | - libnotify | ||
| 26 | - taffybar | ||
| 27 | |||
| 28 | main: xmonad.hs | ||
| 29 | source-dirs: | ||
| 30 | - . | ||
| 31 | - lib | ||
diff --git a/accounts/gkleen@sif/xmonad/stack.nix b/accounts/gkleen@sif/xmonad/stack.nix deleted file mode 100644 index 17a49e04..00000000 --- a/accounts/gkleen@sif/xmonad/stack.nix +++ /dev/null | |||
| @@ -1,17 +0,0 @@ | |||
| 1 | { ghc, nixpkgs ? import ./nixpkgs.nix {} }: | ||
| 2 | |||
| 3 | let | ||
| 4 | haskellPackages = import ./stackage.nix { inherit nixpkgs; }; | ||
| 5 | inherit (nixpkgs {}) pkgs; | ||
| 6 | in pkgs.haskell.lib.buildStackProject { | ||
| 7 | inherit ghc; | ||
| 8 | inherit (haskellPackages) stack; | ||
| 9 | name = "stackenv"; | ||
| 10 | buildInputs = (with pkgs; | ||
| 11 | [ xorg.libX11 xorg.libXrandr xorg.libXinerama xorg.libXScrnSaver xorg.libXext xorg.libXft | ||
| 12 | cairo | ||
| 13 | glib | ||
| 14 | ]) ++ (with haskellPackages; | ||
| 15 | [ | ||
| 16 | ]); | ||
| 17 | } | ||
diff --git a/accounts/gkleen@sif/xmonad/stack.yaml b/accounts/gkleen@sif/xmonad/stack.yaml deleted file mode 100644 index b8ed1147..00000000 --- a/accounts/gkleen@sif/xmonad/stack.yaml +++ /dev/null | |||
| @@ -1,10 +0,0 @@ | |||
| 1 | nix: | ||
| 2 | enable: true | ||
| 3 | shell-file: stack.nix | ||
| 4 | |||
| 5 | resolver: lts-13.21 | ||
| 6 | |||
| 7 | packages: | ||
| 8 | - . | ||
| 9 | |||
| 10 | extra-deps: [] | ||
diff --git a/accounts/gkleen@sif/xmonad/xmonad-yggdrasil.nix b/accounts/gkleen@sif/xmonad/xmonad-yggdrasil.nix deleted file mode 100644 index 7c853619..00000000 --- a/accounts/gkleen@sif/xmonad/xmonad-yggdrasil.nix +++ /dev/null | |||
| @@ -1,21 +0,0 @@ | |||
| 1 | { mkDerivation, aeson, base, bytestring, containers, directory | ||
| 2 | , filepath, hostname, hpack, mtl, network, parsec, process, lib | ||
| 3 | , temporary, transformers, unix, utf8-string, X11, xmonad | ||
| 4 | , xmonad-contrib, libnotify, taffybar | ||
| 5 | }: | ||
| 6 | mkDerivation { | ||
| 7 | pname = "xmonad-yggdrasil"; | ||
| 8 | version = "0.0.0"; | ||
| 9 | src = ./.; | ||
| 10 | isLibrary = false; | ||
| 11 | isExecutable = true; | ||
| 12 | libraryToolDepends = [ hpack ]; | ||
| 13 | executableHaskellDepends = [ | ||
| 14 | aeson base bytestring containers directory filepath hostname mtl | ||
| 15 | network parsec process temporary transformers unix utf8-string X11 | ||
| 16 | xmonad xmonad-contrib libnotify taffybar | ||
| 17 | ]; | ||
| 18 | preConfigure = "hpack"; | ||
| 19 | license = "unknown"; | ||
| 20 | hydraPlatforms = lib.platforms.none; | ||
| 21 | } | ||
diff --git a/accounts/gkleen@sif/xmonad/xmonad.hs b/accounts/gkleen@sif/xmonad/xmonad.hs deleted file mode 100644 index a44d3bb7..00000000 --- a/accounts/gkleen@sif/xmonad/xmonad.hs +++ /dev/null | |||
| @@ -1,939 +0,0 @@ | |||
| 1 | {-# LANGUAGE TupleSections, ViewPatterns, OverloadedStrings, FlexibleInstances, UndecidableInstances, MultiWayIf, NumDecimals #-} | ||
| 2 | |||
| 3 | import XMonad | ||
| 4 | import XMonad.Hooks.DynamicLog | ||
| 5 | import XMonad.Hooks.ManageDocks | ||
| 6 | import XMonad.Util.Run hiding (proc) | ||
| 7 | import XMonad.Util.Loggers | ||
| 8 | import XMonad.Util.EZConfig(additionalKeys) | ||
| 9 | import System.IO | ||
| 10 | import System.IO.Error | ||
| 11 | import System.Environment | ||
| 12 | import Data.Map (Map) | ||
| 13 | import qualified Data.Map as Map | ||
| 14 | import qualified XMonad.StackSet as W | ||
| 15 | import System.Exit | ||
| 16 | import Control.Monad.State (get) | ||
| 17 | -- import XMonad.Layout.Spiral | ||
| 18 | import Data.Ratio | ||
| 19 | import Data.List | ||
| 20 | import Data.Char | ||
| 21 | import Data.Maybe (fromMaybe, listToMaybe, maybeToList, catMaybes, isJust) | ||
| 22 | import XMonad.Layout.Tabbed | ||
| 23 | import XMonad.Prompt | ||
| 24 | import XMonad.Prompt.Input | ||
| 25 | import XMonad.Util.Scratchpad | ||
| 26 | import XMonad.Util.NamedScratchpad | ||
| 27 | import XMonad.Util.Ungrab | ||
| 28 | import Control.Monad (sequence, liftM, liftM2, join, void) | ||
| 29 | import XMonad.Util.WorkspaceCompare | ||
| 30 | import XMonad.Layout.NoBorders | ||
| 31 | import XMonad.Layout.PerWorkspace | ||
| 32 | import XMonad.Layout.SimplestFloat | ||
| 33 | import XMonad.Layout.Renamed | ||
| 34 | import XMonad.Layout.Reflect | ||
| 35 | import XMonad.Layout.OnHost | ||
| 36 | import XMonad.Layout.Combo | ||
| 37 | import XMonad.Layout.ComboP | ||
| 38 | import XMonad.Layout.Column | ||
| 39 | import XMonad.Layout.TwoPane | ||
| 40 | import XMonad.Layout.IfMax | ||
| 41 | import XMonad.Layout.LayoutBuilder | ||
| 42 | import XMonad.Layout.WindowNavigation | ||
| 43 | import XMonad.Layout.Dwindle | ||
| 44 | import XMonad.Layout.TrackFloating | ||
| 45 | import System.Process | ||
| 46 | import System.Directory (removeFile) | ||
| 47 | import System.Posix.Files | ||
| 48 | import System.FilePath ((</>)) | ||
| 49 | import Control.Concurrent | ||
| 50 | import System.Posix.Process (getProcessID) | ||
| 51 | import System.IO.Error | ||
| 52 | import System.IO | ||
| 53 | import XMonad.Hooks.ManageHelpers hiding (CW) | ||
| 54 | import XMonad.Hooks.UrgencyHook as U | ||
| 55 | import XMonad.Hooks.EwmhDesktops | ||
| 56 | import XMonad.StackSet (RationalRect (..)) | ||
| 57 | import Control.Monad (when, filterM, (<=<)) | ||
| 58 | import Graphics.X11.ExtraTypes.XF86 | ||
| 59 | import XMonad.Util.Cursor | ||
| 60 | import XMonad.Actions.Warp | ||
| 61 | import XMonad.Actions.FloatKeys | ||
| 62 | import XMonad.Util.SpawnOnce | ||
| 63 | import System.Directory | ||
| 64 | import System.FilePath | ||
| 65 | import XMonad.Actions.CopyWindow | ||
| 66 | import XMonad.Hooks.ServerMode | ||
| 67 | import XMonad.Actions.Commands | ||
| 68 | import XMonad.Actions.CycleWS | ||
| 69 | import XMonad.Actions.RotSlaves | ||
| 70 | import XMonad.Actions.UpdatePointer | ||
| 71 | import XMonad.Prompt.Window | ||
| 72 | import Data.IORef | ||
| 73 | import Data.Monoid | ||
| 74 | import Data.String | ||
| 75 | import qualified XMonad.Actions.PhysicalScreens as P | ||
| 76 | |||
| 77 | import XMonad.Layout.IM | ||
| 78 | |||
| 79 | import System.Taffybar.Support.PagerHints (pagerHints) | ||
| 80 | |||
| 81 | import XMonad.Prompt.MyShell | ||
| 82 | import XMonad.Prompt.MyPass | ||
| 83 | import XMonad.Prompt.MySsh | ||
| 84 | |||
| 85 | import XMonad.Mpv | ||
| 86 | |||
| 87 | import Network.HostName | ||
| 88 | |||
| 89 | import Control.Applicative ((<$>)) | ||
| 90 | |||
| 91 | import Libnotify as Notify hiding (appName) | ||
| 92 | import qualified Libnotify as Notify (appName) | ||
| 93 | import Libnotify (Notification) | ||
| 94 | -- import System.Information.Battery | ||
| 95 | |||
| 96 | import Data.Int (Int32) | ||
| 97 | |||
| 98 | import System.Posix.Process | ||
| 99 | import System.Posix.Signals | ||
| 100 | import System.Posix.IO as Posix | ||
| 101 | import Control.Exception | ||
| 102 | |||
| 103 | import System.IO.Unsafe | ||
| 104 | |||
| 105 | import Control.Monad.Trans.Class | ||
| 106 | import Control.Monad.Trans.Maybe | ||
| 107 | |||
| 108 | import Data.Fixed (Micro) | ||
| 109 | |||
| 110 | import qualified Data.Text as Text | ||
| 111 | import Data.Ord (comparing) | ||
| 112 | import Debug.Trace | ||
| 113 | |||
| 114 | instance MonadIO m => IsString (m ()) where | ||
| 115 | fromString = spawn | ||
| 116 | |||
| 117 | type KeyMap = Map (ButtonMask, KeySym) (X ()) | ||
| 118 | |||
| 119 | data Host = Host | ||
| 120 | { hName :: HostName | ||
| 121 | , hManageHook :: ManageHook | ||
| 122 | , hWsp :: Integer -> WorkspaceId | ||
| 123 | , hCoWsp :: String -> Maybe WorkspaceId | ||
| 124 | , hKeysMod :: XConfig Layout -> (KeyMap -> KeyMap) | ||
| 125 | , hScreens :: [P.PhysicalScreen] | ||
| 126 | , hKbLayouts :: [(String, Maybe String)] | ||
| 127 | , hCmds :: X [(String, X ())] | ||
| 128 | , hKeyUpKeys :: XConfig Layout -> KeyMap | ||
| 129 | } | ||
| 130 | |||
| 131 | defaultHost = Host { hName = "unkown" | ||
| 132 | , hManageHook = composeOne [manageScratchTerm] | ||
| 133 | , hWsp = show | ||
| 134 | , hCoWsp = const Nothing | ||
| 135 | , hKeysMod = const id | ||
| 136 | , hScreens = [0,1..] | ||
| 137 | , hKbLayouts = [ ("us", Just "dvp") | ||
| 138 | , ("us", Nothing) | ||
| 139 | , ("de", Nothing) | ||
| 140 | ] | ||
| 141 | , hCmds = return [] | ||
| 142 | , hKeyUpKeys = const Map.empty | ||
| 143 | } | ||
| 144 | |||
| 145 | browser :: String | ||
| 146 | browser = "env MOZ_USE_XINPUT2=1 firefox" | ||
| 147 | |||
| 148 | gray, darkGray, red, green :: String | ||
| 149 | gray = "#808080" | ||
| 150 | darkGray = "#202020" | ||
| 151 | red = "#800000" | ||
| 152 | green = "#008000" | ||
| 153 | |||
| 154 | hostFromName :: HostName -> Host | ||
| 155 | hostFromName h@("vali") = defaultHost { hName = h | ||
| 156 | , hManageHook = composeOne $ catMaybes [ Just manageScratchTerm | ||
| 157 | , assign "web" $ className =? ".dwb-wrapped" | ||
| 158 | , assign "web" $ className =? "Chromium" | ||
| 159 | , assign "work" $ className =? "Emacs" | ||
| 160 | , assign "media" $ className =? "mpv" | ||
| 161 | ] | ||
| 162 | , hWsp = hWsp | ||
| 163 | , hCoWsp = hCoWsp | ||
| 164 | , hKeysMod = \conf -> Map.union $ (Map.fromList $ join $ map (spawnBindings conf) [ (xK_d, ["chromium", "chromium $(xclip -o)"]) | ||
| 165 | , (xK_e, ["emacsclient -c"]) | ||
| 166 | ]) | ||
| 167 | `Map.union` | ||
| 168 | ( Map.fromList [ ((XMonad.modMask conf .|. controlMask, xK_Return), scratchpadSpawnActionCustom $ (XMonad.terminal conf) ++ " -name scratchpad -title scratchpad -e tmux new-session -D -s scratch") | ||
| 169 | ] ) | ||
| 170 | , hScreens = hScreens defaultHost | ||
| 171 | } | ||
| 172 | where | ||
| 173 | workspaceNames = Map.fromList [ (2, "web") | ||
| 174 | , (3, "work") | ||
| 175 | , (10, "media") | ||
| 176 | ] | ||
| 177 | hWsp = wspFromMap workspaceNames | ||
| 178 | hCoWsp = coWspFromMap workspaceNames | ||
| 179 | assign wsp test = (\wsp -> test -?> doShift wsp) <$> hCoWsp wsp | ||
| 180 | hostFromName h | ||
| 181 | | h `elem` ["hel", "sif"] = defaultHost { hName = h | ||
| 182 | , hManageHook = namedScratchpadManageHook scratchpads <+> composeOne (catMaybes | ||
| 183 | [ assign "mpv" $ className =? "mpv" | ||
| 184 | , assign "mpv" $ stringProperty "WM_WINDOW_ROLE" =? "presentation" | ||
| 185 | , assign "read" $ stringProperty "WM_WINDOW_ROLE" =? "presenter" | ||
| 186 | , assign "mpv" $ className =? "factorio" | ||
| 187 | , assign "mpv" $ resource =? "twitch" | ||
| 188 | , assign "web" $ className =? "chromium-browser" | ||
| 189 | , assign "web" $ className =? "Google-chrome" | ||
| 190 | , assign "work" $ (appName =? "Devtools" <&&> className =? "firefox") | ||
| 191 | , assign "work" $ className =? "Postman" | ||
| 192 | , assign "web" $ (appName =? "Navigator" <&&> className =? "firefox") | ||
| 193 | , assign "comm" $ (className =? "Emacs" <&&> title =? "Mail") | ||
| 194 | , assign "comm" $ className =? "Zulip" | ||
| 195 | , assign "comm" $ className =? "Element" | ||
| 196 | , assign "comm" $ className =? "Rocket.Chat" | ||
| 197 | , assign "comm" $ className =? "Discord" | ||
| 198 | , assign "comm" $ className =? "Rainbow" | ||
| 199 | , assign "media" $ resource =? "media" | ||
| 200 | , assign "monitor" $ className =? "Grafana" | ||
| 201 | , assign "monitor" $ className =? "Virt-viewer" | ||
| 202 | , assign "monitor" $ resource =? "htop" | ||
| 203 | , assign "monitor" $ resource =? "monitor" | ||
| 204 | , assign "monitor" $ className =? "xfreerdp" | ||
| 205 | , assign "monitor" $ className =? "org.remmina.Remmina" | ||
| 206 | , Just $ resource =? "htop" -?> centerFloat | ||
| 207 | , Just $ (className =? "Scp-dbus-service.py") -?> centerFloat | ||
| 208 | , Just $ resource =? "log" -?> centerFloat | ||
| 209 | , assign "work" $ className =? "Alacritty" | ||
| 210 | , Just $ (appName =? "Edit with Emacs FRAME") -?> centerFloat | ||
| 211 | , assign' ["work", "uni"] $ (className =? "Emacs" <&&> appName /=? "Edit with Emacs FRAME") | ||
| 212 | , assign' ["work", "uni"] $ className =? "jetbrains-idea-ce" | ||
| 213 | , assign "read" $ className =? "llpp" | ||
| 214 | , assign "read" $ className =? "Evince" | ||
| 215 | , assign "read" $ className =? "Zathura" | ||
| 216 | , assign "read" $ className =? "MuPDF" | ||
| 217 | , assign "read" $ className =? "Xournal" | ||
| 218 | , assign "read" $ appName =? "libreoffice" | ||
| 219 | , assign "read" $ appName =? "com-trollworks-gcs-app-GCS" | ||
| 220 | , assign "read" $ appName =? "Tux.py" | ||
| 221 | , assign "read" $ className =? "Gnucash" | ||
| 222 | , assign "comm" $ className =? "Skype" | ||
| 223 | , assign "comm" $ className =? "Daily" | ||
| 224 | , assign "comm" $ className =? "Pidgin" | ||
| 225 | , assign "comm" $ className =? "Thunderbird" | ||
| 226 | , assign "comm" $ className =? "Slack" | ||
| 227 | , Just $ (resource =? "xvkbd") -?> doRectFloat $ RationalRect (1 % 8) (3 % 8) (6 % 8) (4 % 8) | ||
| 228 | , Just $ (stringProperty "_NET_WM_WINDOW_TYPE" =? "_NET_WM_WINDOW_TYPE_DIALOG") -?> doFloat | ||
| 229 | , Just $ (className =? "Dunst") -?> doFloat | ||
| 230 | , Just $ (className =? "Xmessage") -?> doCenterFloat | ||
| 231 | , Just $ (className =? "Nm-openconnect-auth-dialog") -?> centerFloat | ||
| 232 | , Just $ (className =? "Pinentry") -?> doCenterFloat | ||
| 233 | , Just $ (className =? "pinentry") -?> doCenterFloat | ||
| 234 | , Just $ (stringProperty "WM_WINDOW_ROLE" =? "GtkFileChooseDialog") -?> centerFloatSmall | ||
| 235 | , Just $ (className =? "Nvidia-settings") -?> doCenterFloat | ||
| 236 | , Just $ fmap ("Minetest" `isInfixOf`) title -?> doIgnore | ||
| 237 | , Just $ fmap ("Automachef" `isInfixOf`) title -?> doIgnore | ||
| 238 | , assign "call" $ className =? "zoom" | ||
| 239 | ]) | ||
| 240 | , hWsp = hWsp | ||
| 241 | , hCoWsp = hCoWsp | ||
| 242 | , hKeysMod = \conf -> Map.union $ (Map.fromList $ join $ map (spawnBindings conf) [ (xK_e, ["emacsclient -c"]) | ||
| 243 | , (xK_d, [fromString browser, "google-chrome" {- , "notmuch-links" -}]) | ||
| 244 | , (xK_c, [ inputPrompt xPConfigMonospace "dc" ?+ dc ]) | ||
| 245 | , (xK_g, ["pidgin"]) | ||
| 246 | , (xK_s, ["skype"]) | ||
| 247 | -- , (xK_p, [mkPassPrompt "Type password" pwType xPConfig, mkPassPrompt "Show password" pwShow xPConfig, mkPassPrompt "Copy password" pwClip xPConfig]) | ||
| 248 | , (xK_w, ["sudo rewacom"]) | ||
| 249 | , (xK_y, [ "tmux new-window -dt media /var/media/link.hs $(xclip -o)" | ||
| 250 | , "tmux new-window -dt media /var/media/download.hs $(xclip -o)" | ||
| 251 | , "tmux new-window -dt media /var/media/download.hs $(xclip -o -selection clipboard)" | ||
| 252 | ]) | ||
| 253 | , (xK_l, [ "tmux new-window -dt media mpv $(xclip -o)" | ||
| 254 | , "tmux new-window -dt media mpv $(xclip -o -selection clipboard)" | ||
| 255 | , "alacritty --class media -e tmuxp load /var/media" | ||
| 256 | ]) | ||
| 257 | {- , (xK_m, [ "emacsclient -c -F \"'(title . \\\"Mail\\\")\" -e '(notmuch)'" | ||
| 258 | , "emacsclient -c -F \"'(title . \\\"Mail\\\")\" -e '(notmuch-mua-new-mail)'" | ||
| 259 | , "emacsclient -c -F \"'(title . \\\"Mail\\\")\" -e \"(browse-url-mail \"$(xclip -o)\")\"" | ||
| 260 | ]) -} | ||
| 261 | , (xK_Return, ["keynav start,windowzoom", "keynav start"]) | ||
| 262 | , (xK_t, [inputPrompt xPConfigMonospace "fuzzytime timer" ?+ fuzzytime, fuzzytime "unset", work_fuzzytime]) | ||
| 263 | , (xK_a, [inputPrompt xPConfigMonospace "adjmix" ?+ adjmix]) | ||
| 264 | , (xK_s, [ inputPromptWithCompl xPConfigMonospace "start synergy" synergyCompl ?+ synergyStart | ||
| 265 | , inputPromptWithCompl xPConfigMonospace "stop synergy" synergyCompl ?+ synergyStop | ||
| 266 | ]) | ||
| 267 | , (xK_h, [ "alacritty --class htop -e htop" | ||
| 268 | , "alacritty --class log -e journalctl -xef" | ||
| 269 | ]) | ||
| 270 | , (xK_x, [ "autorandr -c" | ||
| 271 | , "autorandr -fl def" | ||
| 272 | ]) | ||
| 273 | , (xK_z, [ "zulip -- --force-device-scale-factor=2" | ||
| 274 | ]) | ||
| 275 | ]) | ||
| 276 | `Map.union` | ||
| 277 | ( Map.fromList [ ((XMonad.modMask conf .|. controlMask, xK_Return), namedScratchpadAction scratchpads "term") | ||
| 278 | , ((XMonad.modMask conf .|. controlMask, xK_a), namedScratchpadAction scratchpads "pavucontrol") | ||
| 279 | , ((XMonad.modMask conf .|. controlMask, xK_o), namedScratchpadAction scratchpads "easyeffects") | ||
| 280 | , ((XMonad.modMask conf .|. controlMask .|. shiftMask, xK_o), namedScratchpadAction scratchpads "helvum") | ||
| 281 | , ((XMonad.modMask conf .|. controlMask, xK_w), namedScratchpadAction scratchpads "alarms") | ||
| 282 | , ((XMonad.modMask conf .|. controlMask, xK_b), namedScratchpadAction scratchpads "blueman") | ||
| 283 | , ((XMonad.modMask conf .|. controlMask, xK_p), namedScratchpadAction scratchpads "keepassxc") | ||
| 284 | , ((XMonad.modMask conf .|. controlMask, xK_t), namedScratchpadAction scratchpads "toggl") | ||
| 285 | , ((XMonad.modMask conf .|. controlMask, xK_e), namedScratchpadAction scratchpads "emacs") | ||
| 286 | , ((XMonad.modMask conf .|. controlMask, xK_m), namedScratchpadAction scratchpads "calendar") | ||
| 287 | , ((XMonad.modMask conf .|. controlMask, xK_f), namedScratchpadAction scratchpads "music") | ||
| 288 | , ((XMonad.modMask conf .|. mod1Mask, xK_Up), rotate U) | ||
| 289 | , ((XMonad.modMask conf .|. mod1Mask, xK_Down), rotate D) | ||
| 290 | , ((XMonad.modMask conf .|. mod1Mask, xK_Left), rotate L) | ||
| 291 | , ((XMonad.modMask conf .|. mod1Mask, xK_Right), rotate R) | ||
| 292 | , ((controlMask, xK_space ), "dunstctl close" ) | ||
| 293 | , ((controlMask .|. shiftMask, xK_space ), "dunstctl close-all" ) | ||
| 294 | , ((controlMask, xK_period), "dunstctl context" ) | ||
| 295 | , ((controlMask, xK_comma ), "dunstctl history-pop") | ||
| 296 | -- , ((XMonad.modMask conf .|. shiftMask, xK_a), startMute "hel") | ||
| 297 | ] ) | ||
| 298 | , hKeyUpKeys = \conf -> Map.fromList [ -- ((XMonad.modMask conf .|. shiftMask, xK_a), stopMute "hel") | ||
| 299 | ] | ||
| 300 | , hScreens = hScreens defaultHost | ||
| 301 | , hCmds = return [ ("prev-workspace", prevWS) | ||
| 302 | , ("next-workspace", nextWS) | ||
| 303 | , ("prev-window", rotAllDown) | ||
| 304 | , ("next-window", rotAllUp) | ||
| 305 | , ("banish", banishScreen LowerRight) | ||
| 306 | , ("update-gpg-tty", safeSpawn "gpg-connect-agent" ["UPDATESTARTUPTTY", "/bye"]) | ||
| 307 | , ("rescreen", rescreen) | ||
| 308 | , ("repanel", do | ||
| 309 | spawn "nm-applet" | ||
| 310 | spawn "blueman-applet" | ||
| 311 | spawn "pasystray" | ||
| 312 | spawn "kdeconnect-indicator" | ||
| 313 | spawn "dunst -print" | ||
| 314 | spawn "udiskie" | ||
| 315 | spawn "autocutsel -s PRIMARY" | ||
| 316 | spawn "autocutsel -s CLIPBOARD" | ||
| 317 | ) | ||
| 318 | , ("pause", mediaMpv $ MpvSetProperty "pause" True) | ||
| 319 | , ("unpause", mediaMpv $ MpvSetProperty "pause" False) | ||
| 320 | , ("exit", io $ exitWith ExitSuccess) | ||
| 321 | ] | ||
| 322 | } | ||
| 323 | where | ||
| 324 | withGdkScale act = void . xfork $ setEnv "GDK_SCALE" "2" >> act | ||
| 325 | workspaceNames = Map.fromList [ (1, "comm") | ||
| 326 | , (2, "web") | ||
| 327 | , (3, "work") | ||
| 328 | , (4, "read") | ||
| 329 | , (5, "monitor") | ||
| 330 | , (6, "uni") | ||
| 331 | , (8, "call") | ||
| 332 | , (9, "media") | ||
| 333 | , (10, "mpv") | ||
| 334 | ] | ||
| 335 | scratchpads = [ NS "term" "alacritty --class scratchpad --title scratchpad -e tmux new-session -AD -s scratch" (resource =? "scratchpad") centerFloat | ||
| 336 | , NS "pavucontrol" "pavucontrol" (resource =? "pavucontrol") centerFloat | ||
| 337 | , NS "helvum" "helvum" (resource =? "helvum") centerFloat | ||
| 338 | , NS "easyeffects" "easyeffects" (resource =? "easyeffects") centerFloat | ||
| 339 | , NS "alarms" "alarm-clock-applet" (className =? "Alarm-clock-applet" <&&> title =? "Alarms") centerFloat | ||
| 340 | , NS "blueman" "blueman-manager" (className =? ".blueman-manager-wrapped") centerFloat | ||
| 341 | , NS "keepassxc" "keepassxc" (className =? "KeePassXC") centerFloat | ||
| 342 | , NS "toggl" "toggldesktop" (className =? "Toggl Desktop") centerFloat | ||
| 343 | , NS "calendar" "minetime -- --force-device-scale-factor=1.6" (className =? "MineTime") centerFloat | ||
| 344 | , NS "emacs" "emacsclient -c -F \"'(title . \\\"Scratchpad\\\")\"" (className =? "Emacs" <&&> title =? "Scratchpad") centerFloat | ||
| 345 | , NS "music" "ytmdesktop" (className =? "youtube-music-desktop-app") centerFloat | ||
| 346 | ] | ||
| 347 | centerFloat = customFloating $ RationalRect (1 % 16) (1 % 16) (7 % 8) (7 % 8) | ||
| 348 | centerFloatSmall = customFloating $ RationalRect (1 % 4) (1 % 4) (1 % 2) (1 % 2) | ||
| 349 | hWsp = wspFromMap workspaceNames | ||
| 350 | hCoWsp = coWspFromMap workspaceNames | ||
| 351 | assign wsp test = (\wsp -> test -?> doShift wsp) <$> hCoWsp wsp | ||
| 352 | assign' :: [String] -> Query Bool -> Maybe MaybeManageHook | ||
| 353 | assign' wsps test = do | ||
| 354 | wsIds <- mapM hCoWsp wsps | ||
| 355 | return $ test -?> go wsIds | ||
| 356 | where | ||
| 357 | go :: [WorkspaceId] -> ManageHook | ||
| 358 | go wsps = do | ||
| 359 | visWsps <- liftX $ (\wset -> W.tag . W.workspace <$> W.current wset : W.visible wset) <$> gets windowset | ||
| 360 | case (filter (`elem` visWsps) wsps, wsps) of | ||
| 361 | (wsp : _, _) -> doShift wsp | ||
| 362 | (_, wsp : _) -> doShift wsp | ||
| 363 | ([], []) -> return mempty | ||
| 364 | rotate rot = do | ||
| 365 | safeSpawn "xrandr" ["--output", "eDP-1", "--rotate", xrandrDir] | ||
| 366 | mapM_ rotTouch touchscreens | ||
| 367 | where | ||
| 368 | xrandrDir = case rot of | ||
| 369 | U -> "normal" | ||
| 370 | L -> "left" | ||
| 371 | R -> "right" | ||
| 372 | D -> "inverted" | ||
| 373 | matrix = case rot of | ||
| 374 | U -> [ [ 1, 0, 0] | ||
| 375 | , [ 0, 1, 0] | ||
| 376 | , [ 0, 0, 1] | ||
| 377 | ] | ||
| 378 | L -> [ [ 0, -1, 1] | ||
| 379 | , [ 1, 0, 0] | ||
| 380 | , [ 0, 0, 1] | ||
| 381 | ] | ||
| 382 | R -> [ [ 0, 1, 0] | ||
| 383 | , [-1, 0, 1] | ||
| 384 | , [ 0, 0, 1] | ||
| 385 | ] | ||
| 386 | D -> [ [-1, 0, 1] | ||
| 387 | , [ 0, -1, 1] | ||
| 388 | , [ 0, 0, 1] | ||
| 389 | ] | ||
| 390 | touchscreens = [ "Wacom Co.,Ltd. Pen and multitouch sensor Finger touch" | ||
| 391 | , "Wacom Co.,Ltd. Pen and multitouch sensor Pen stylus" | ||
| 392 | , "Wacom Co.,Ltd. Pen and multitouch sensor Pen eraser" | ||
| 393 | ] | ||
| 394 | rotTouch screen = do | ||
| 395 | safeSpawn "xinput" $ ["set-prop", screen, "Coordinate Transformation Matrix"] ++ map (\n -> show n ++ ",") (concat matrix) | ||
| 396 | safeSpawn "xinput" ["map-to-output", screen, "eDP-1"] | ||
| 397 | withPw f label = io . void . forkProcess $ do | ||
| 398 | uninstallSignalHandlers | ||
| 399 | void $ createSession | ||
| 400 | (dropWhileEnd isSpace -> pw) <- readCreateProcess (proc "pass" ["show", label]) "" | ||
| 401 | void $ f pw | ||
| 402 | pwType :: String -> X () | ||
| 403 | pwType = withPw $ readCreateProcess (proc "xdotool" ["type", "--clearmodifiers", "--file", "-"]) | ||
| 404 | pwClip label = safeSpawn "pass" ["show", "--clip", label] | ||
| 405 | pwShow :: String -> X () | ||
| 406 | pwShow = withPw $ \pw -> do | ||
| 407 | xmessage <- fromMaybe "xmessage" <$> liftIO (lookupEnv "XMONAD_XMESSAGE") | ||
| 408 | readCreateProcess (proc xmessage ["-file", "-"]) pw | ||
| 409 | fuzzytime str = safeSpawn "fuzzytime" $ "timer" : words str | ||
| 410 | work_fuzzytime = io . void . forkProcess $ do | ||
| 411 | readCreateProcess (proc "worktime" []) "" >>= safeSpawn "fuzzytime" . ("timer" : ) . pure | ||
| 412 | adjmix str = safeSpawn "adjmix" $ words str | ||
| 413 | dc expr = void . xfork $ do | ||
| 414 | result <- readProcess "dc" [] $ expr ++ "f" | ||
| 415 | let | ||
| 416 | (first : rest) = filter (not . null) $ lines result | ||
| 417 | notification = Notify.summary first <> Notify.body (unlines rest) <> Notify.timeout Infinite <> Notify.urgency Normal <> Notify.appName "dc" | ||
| 418 | void $ Notify.display notification | ||
| 419 | synergyCompl = mkComplFunFromList' xPConfigMonospace ["mathw86"] | ||
| 420 | synergyStart host = safeSpawn "systemctl" ["--user", "start", "synergy-rtunnel@" ++ host ++ ".service"] | ||
| 421 | synergyStop host = safeSpawn "systemctl" ["--user", "stop", "synergy-rtunnel@" ++ host ++ ".service"] | ||
| 422 | |||
| 423 | hostFromName _ = defaultHost | ||
| 424 | |||
| 425 | -- muteRef :: IORef (Maybe (String, Notification)) | ||
| 426 | -- {-# NOINLINE muteRef #-} | ||
| 427 | -- muteRef = unsafePerformIO $ newIORef Nothing | ||
| 428 | |||
| 429 | -- startMute, stopMute :: String -> X () | ||
| 430 | -- startMute sink = liftIO $ do | ||
| 431 | -- muted <- isJust <$> readIORef muteRef | ||
| 432 | -- when (not muted) $ do | ||
| 433 | -- let | ||
| 434 | -- notification = Notify.summary "Muted" <> Notify.timeout Infinite <> Notify.urgency Normal | ||
| 435 | -- level = "0.0dB" | ||
| 436 | -- -- level <- runProcessWithInput "ssh" ["bragi", "cat", "/dev/shm/mix/" ++ sink ++ "/level"] "" | ||
| 437 | -- -- callProcess "ssh" ["bragi", "adjmix", "-t", sink, "-o", "0"] | ||
| 438 | -- hPutStrLn stderr "Mute" | ||
| 439 | -- writeIORef muteRef . Just . (level, ) =<< Notify.display notification | ||
| 440 | -- stopMute sink = liftIO $ do | ||
| 441 | -- let | ||
| 442 | -- unmute (Just (level, notification)) = do | ||
| 443 | -- hPutStrLn stderr "Unmute" | ||
| 444 | -- -- callProcess "ssh" ["bragi", "adjmix", "-t", sink, "-o", level] | ||
| 445 | -- Notify.close notification | ||
| 446 | -- unmute Nothing = return () | ||
| 447 | -- muted <- isJust <$> readIORef muteRef | ||
| 448 | -- when muted . join . atomicModifyIORef muteRef $ (Nothing, ) . unmute | ||
| 449 | |||
| 450 | wspFromMap workspaceNames = \i -> case Map.lookup i workspaceNames of | ||
| 451 | Just str -> show i ++ " " ++ str | ||
| 452 | Nothing -> show i | ||
| 453 | |||
| 454 | coWspFromMap workspaceNames = \str -> case filter ((== str) . snd) $ Map.toList workspaceNames of | ||
| 455 | [] -> Nothing | ||
| 456 | [(i, _)] -> Just $ wspFromMap workspaceNames i | ||
| 457 | _ -> Nothing | ||
| 458 | |||
| 459 | spawnModifiers = [0, controlMask, shiftMask .|. controlMask] | ||
| 460 | spawnBindings :: XConfig layout -> (KeySym, [X ()]) -> [((KeyMask, KeySym), X ())] | ||
| 461 | spawnBindings conf (k, cmds) = zipWith (\m cmd -> ((modm .|. mod1Mask .|. m, k), cmd)) spawnModifiers cmds | ||
| 462 | where | ||
| 463 | modm = XMonad.modMask conf | ||
| 464 | |||
| 465 | manageScratchTerm = (resource =? "scratchpad" <||> resource =? "keysetup") -?> doRectFloat $ RationalRect (1 % 16) (1 % 16) (7 % 8) (7 % 8) | ||
| 466 | |||
| 467 | tabbedLayout t = renamed [Replace "Tabbed"] $ reflectHoriz $ t CustomShrink $ tabbedTheme | ||
| 468 | tabbedLayoutHoriz t = renamed [Replace "Tabbed Horiz"] $ reflectVert $ t CustomShrink $ tabbedTheme | ||
| 469 | tabbedTheme = def | ||
| 470 | { activeColor = "black" | ||
| 471 | , inactiveColor = "black" | ||
| 472 | , urgentColor = "black" | ||
| 473 | , activeBorderColor = gray | ||
| 474 | , inactiveBorderColor = darkGray | ||
| 475 | , urgentBorderColor = red | ||
| 476 | , activeTextColor = gray | ||
| 477 | , inactiveTextColor = gray | ||
| 478 | , urgentTextColor = gray | ||
| 479 | , decoHeight = 32 | ||
| 480 | , fontName = "xft:Fira Sans:pixelsize=21" | ||
| 481 | } | ||
| 482 | |||
| 483 | main :: IO () | ||
| 484 | main = do | ||
| 485 | arguments <- either (const []) id <$> tryIOError getArgs | ||
| 486 | case arguments of | ||
| 487 | ["--command", s] -> do | ||
| 488 | d <- openDisplay "" | ||
| 489 | rw <- rootWindow d $ defaultScreen d | ||
| 490 | a <- internAtom d "XMONAD_COMMAND" False | ||
| 491 | m <- internAtom d s False | ||
| 492 | allocaXEvent $ \e -> do | ||
| 493 | setEventType e clientMessage | ||
| 494 | setClientMessageEvent e rw a 32 m currentTime | ||
| 495 | sendEvent d rw False structureNotifyMask e | ||
| 496 | sync d False | ||
| 497 | _ -> do | ||
| 498 | -- batteryMon <- xfork $ monitorBattery Nothing Nothing | ||
| 499 | hostname <- getHostName | ||
| 500 | let | ||
| 501 | host = hostFromName hostname | ||
| 502 | setEnv "HOST" hostname | ||
| 503 | let myConfig = withHostUrgency . ewmhFullscreen . ewmh . pagerHints $ docks def | ||
| 504 | { manageHook = hManageHook host | ||
| 505 | , terminal = "alacritty" | ||
| 506 | , layoutHook = smartBorders . avoidStruts $ windowNavigation layout' | ||
| 507 | , logHook = do | ||
| 508 | dynamicLogString xmobarPP' >>= writeProps | ||
| 509 | updatePointer (99 % 100, 98 % 100) (0, 0) | ||
| 510 | , modMask = mod4Mask | ||
| 511 | , keys = \conf -> hKeysMod host conf $ myKeys' conf host | ||
| 512 | , workspaces = take (length numKeys) $ map wsp [1..] | ||
| 513 | , startupHook = setDefaultCursor xC_left_ptr | ||
| 514 | , normalBorderColor = darkGray | ||
| 515 | , focusedBorderColor = gray | ||
| 516 | , handleEventHook = serverModeEventHookCmd' (hCmds host) <+> keyUpEventHook | ||
| 517 | } | ||
| 518 | writeProps str = do | ||
| 519 | let encodeCChar = map $ fromIntegral . fromEnum | ||
| 520 | atoms = [ "_XMONAD_WORKSPACES" | ||
| 521 | , "_XMONAD_LAYOUT" | ||
| 522 | , "_XMONAD_TITLE" | ||
| 523 | ] | ||
| 524 | (flip mapM_) (zip atoms (lines str)) $ \(atom', content) -> do | ||
| 525 | ustring <- getAtom "UTF8_STRING" | ||
| 526 | atom <- getAtom atom' | ||
| 527 | withDisplay $ \dpy -> io $ do | ||
| 528 | root <- rootWindow dpy $ defaultScreen dpy | ||
| 529 | changeProperty8 dpy root atom ustring propModeReplace $ encodeCChar content | ||
| 530 | sync dpy True | ||
| 531 | wsp = hWsp host | ||
| 532 | -- We can´t define per-host layout modifiers because we lack dependent types | ||
| 533 | layout' = onHost "skadhi" ( onWorkspace (wsp 1) (Full ||| withIM (1%5) (Title "Buddy List") tabbedLayout') $ | ||
| 534 | onWorkspace (wsp 10) Full $ | ||
| 535 | onWorkspace (wsp 2) (Full ||| tabbedLayout') $ | ||
| 536 | onWorkspace (wsp 5) tabbedLayout' $ | ||
| 537 | onWorkspace (wsp 8) (withIM (1%5) (Title "Friends") tabbedLayout') $ | ||
| 538 | defaultLayouts | ||
| 539 | ) $ | ||
| 540 | onHost "vali" ( onWorkspace (wsp 2) (Full ||| tabbedLayout' ||| combineTwo (TwoPane 0.01 0.57) Full tabbedLayout') $ | ||
| 541 | onWorkspace (wsp 3) workLayouts $ | ||
| 542 | defaultLayouts | ||
| 543 | ) $ | ||
| 544 | onHost "hel" ( onWorkspace (wsp 1) (withIM (1 % 8) (Title "Buddy List") $ trackFloating tabbedLayout') $ | ||
| 545 | onWorkspace (wsp 2) (tabbedLayout''' ||| Dwindle R CW 1 (5 % 100)) $ | ||
| 546 | onWorkspace (wsp 3) workLayouts $ | ||
| 547 | onWorkspace (wsp 6) workLayouts $ | ||
| 548 | onWorkspace (wsp 4) (tabbedLayout' ||| tabbedLayoutHoriz' ||| Dwindle R CW 1 (5 % 100)) $ | ||
| 549 | onWorkspace (wsp 5) (tabbedLayout''' ||| Dwindle R CW 1 (5 % 100)) $ | ||
| 550 | onWorkspace (wsp 10) (tabbedLayout''' ||| combineTwoP (TwoPane (1 % 100) (3 % 4)) tabbedLayout''' tabbedLayout''' (ClassName "mpv") ||| Dwindle R CW 1 (5 % 100)) $ | ||
| 551 | defaultLayouts | ||
| 552 | ) $ | ||
| 553 | onHost "sif" ( onWorkspace (wsp 1) (withIM (1 % 8) (Title "Buddy List") $ trackFloating tabbedLayout') $ | ||
| 554 | onWorkspace (wsp 2) (tabbedLayout''' ||| Dwindle R CW 1 (5 % 100)) $ | ||
| 555 | onWorkspace (wsp 3) workLayouts $ | ||
| 556 | onWorkspace (wsp 6) workLayouts $ | ||
| 557 | onWorkspace (wsp 4) (tabbedLayout' ||| tabbedLayoutHoriz' ||| Dwindle R CW 1 (5 % 100)) $ | ||
| 558 | onWorkspace (wsp 5) (tabbedLayout''' ||| Dwindle R CW 1 (5 % 100)) $ | ||
| 559 | onWorkspace (wsp 8) tabbedLayout''' $ | ||
| 560 | onWorkspace (wsp 10) (tabbedLayout''' ||| combineTwoP (TwoPane (1 % 100) (3 % 4)) tabbedLayout''' tabbedLayout''' (ClassName "mpv") ||| Dwindle R CW 1 (5 % 100)) $ | ||
| 561 | defaultLayouts | ||
| 562 | ) $ | ||
| 563 | defaultLayouts | ||
| 564 | -- tabbedLayout''' = renamed [Replace "Tabbed'"] $ IfMax 1 (noBorders Full) (tabbedLayout tabbedBottomAlways) | ||
| 565 | tabbedLayout''' = tabbedLayout tabbedBottom | ||
| 566 | tabbedLayout' = tabbedLayout tabbedBottomAlways | ||
| 567 | tabbedLayoutHoriz' = tabbedLayoutHoriz tabbedLeftAlways | ||
| 568 | defaultLayouts = {- spiralWithDir East CW (1 % 2) -} Dwindle R CW 1 (5 % 100) ||| tabbedLayout' ||| Full | ||
| 569 | -- workLayouts = {- spiralWithDir East CW (1 % 2) -} Dwindle R CW (2 % 1) (5 % 100) ||| tabbedLayout' ||| Full | ||
| 570 | workLayouts = tabbedLayout' ||| (renamed [Replace "Combined"] $ combineTwoP (TwoPane (1 % 100) (1891 % 2560)) tabbedLayout''' (Column 1.6) (ClassName "Postman" `Or` ClassName "Emacs" `Or` ClassName "jetbrains-idea-ce" `Or` (Resource "Devtools" `And` ClassName "Firefox"))) ||| Full ||| Dwindle R CW 1 (5 % 100) | ||
| 571 | sqrtTwo = approxRational (sqrt 2) (1 / 2560) | ||
| 572 | xmobarPP' = xmobarPP { ppTitle = shorten 80 | ||
| 573 | , ppSort = (liftM2 (.)) getSortByIndex $ return scratchpadFilterOutWorkspace | ||
| 574 | , ppUrgent = wrap "(" ")" . xmobarColor "#800000" "" | ||
| 575 | , ppHiddenNoWindows = xmobarColor "#202020" "" . wrap "(" ")" | ||
| 576 | , ppVisible = wrap "(" ")" . xmobarColor "#808000" "" | ||
| 577 | , ppCurrent = wrap "(" ")" . xmobarColor "#008000" "" | ||
| 578 | , ppHidden = wrap "(" ")" | ||
| 579 | , ppWsSep = " " | ||
| 580 | , ppSep = "\n" | ||
| 581 | } | ||
| 582 | withHostUrgency = case hostname of | ||
| 583 | "sif" -> withUrgencyHookC urgencyHook' $ def { suppressWhen = U.Never, remindWhen = Every 2 } | ||
| 584 | _ -> id | ||
| 585 | urgencyHook' window = do | ||
| 586 | let blinkLight = (lightHigh >> threadDelay 0.5e6) `finally` lightLow | ||
| 587 | where | ||
| 588 | lightHigh = | ||
| 589 | writeFile "/sys/class/leds/input0::capslock/brightness" =<< readFile "/sys/class/leds/input0::capslock/max_brightness" | ||
| 590 | lightLow = writeFile "/sys/class/leds/input0::capslock/brightness" "0" | ||
| 591 | runQuery ((resource =? "comm" <||> resource =? "Pidgin" <||> className =? "Gajim" <||> className =? "Skype" <||> className =? "Thunderbird") --> void (xfork blinkLight)) window | ||
| 592 | urgencyHook (BorderUrgencyHook { urgencyBorderColor = red }) window | ||
| 593 | shutdown :: SomeException -> IO a | ||
| 594 | shutdown e = do | ||
| 595 | let pids = [ -- batteryMon | ||
| 596 | ] | ||
| 597 | mapM_ (signalProcess sigTERM) pids | ||
| 598 | mapM_ (getProcessStatus False False) pids | ||
| 599 | throw e | ||
| 600 | keyUpEventHook :: Event -> X All | ||
| 601 | keyUpEventHook event = handle event >> return (All True) | ||
| 602 | where | ||
| 603 | handle (KeyEvent { ev_event_type = t, ev_state = m, ev_keycode = code }) | ||
| 604 | | t == keyRelease = withDisplay $ \dpy -> do | ||
| 605 | s <- io $ keycodeToKeysym dpy code 0 | ||
| 606 | mClean <- cleanMask m | ||
| 607 | ks <- asks $ hKeyUpKeys host . config | ||
| 608 | userCodeDef () $ whenJust (Map.lookup (mClean, s) ks) id | ||
| 609 | | otherwise = return () | ||
| 610 | handle _ = return () | ||
| 611 | handle shutdown $ launch myConfig =<< getDirectories | ||
| 612 | |||
| 613 | secs :: Int -> Int | ||
| 614 | secs = (* 1000000) | ||
| 615 | |||
| 616 | -- monitorBattery :: Maybe BatteryContext -> Maybe Notification -> IO () | ||
| 617 | -- monitorBattery Nothing n = do | ||
| 618 | -- ctx <- batteryContextNew | ||
| 619 | -- case ctx of | ||
| 620 | -- Nothing -> threadDelay (secs 10) >> monitorBattery Nothing n | ||
| 621 | -- Just _ -> monitorBattery ctx n | ||
| 622 | -- monitorBattery ctx@(Just ctx') n = do | ||
| 623 | -- batInfo <- getBatteryInfo ctx' | ||
| 624 | -- case batInfo of | ||
| 625 | -- Nothing -> threadDelay (secs 1) >> monitorBattery ctx n | ||
| 626 | -- Just batInfo -> do | ||
| 627 | -- let n' | ||
| 628 | -- | batteryState batInfo == BatteryStateDischarging | ||
| 629 | -- , timeLeft <= 1200 | ||
| 630 | -- , timeLeft > 0 = Just $ summary "Discharging" <> hint "value" percentage <> urgency u <> body (duz timeLeft ++ "left") | ||
| 631 | -- | otherwise = Nothing | ||
| 632 | -- u | ||
| 633 | -- | timeLeft <= 600 = Critical | ||
| 634 | -- | timeLeft <= 1800 = Normal | ||
| 635 | -- | otherwise = Low | ||
| 636 | -- timeLeft = batteryTimeToEmpty batInfo | ||
| 637 | -- percentage :: Int32 | ||
| 638 | -- percentage = round $ batteryPercentage batInfo | ||
| 639 | -- ts = [("s", 60), ("m", 60), ("h", 24), ("d", 365), ("y", 1)] | ||
| 640 | -- duz ms = ss | ||
| 641 | -- where (ss, _) = foldl (\(ss, x) (s, y) -> ((if rem x y > 0 then show (rem x y) ++ s ++ " " else "") ++ ss , quot x y)) ("", ms) ts | ||
| 642 | -- case n' of | ||
| 643 | -- Just n' -> Notify.display (maybe mempty reuse n <> Notify.appName "monitorBattery" <> n') >>= (\n -> threadDelay (secs 2) >> monitorBattery ctx (Just n)) | ||
| 644 | -- Nothing -> threadDelay (secs 30) >> monitorBattery ctx n | ||
| 645 | |||
| 646 | disableTouchpad, disableTrackpoint, enableTrackpoint, enableTouchpad :: X () | ||
| 647 | enableTouchpad = safeSpawn "xinput" ["enable", "SynPS/2 Synaptics TouchPad"] | ||
| 648 | disableTouchpad = safeSpawn "xinput" ["disable", "SynPS/2 Synaptics TouchPad"] | ||
| 649 | enableTrackpoint = safeSpawn "xinput" ["enable", "TPPS/2 IBM TrackPoint"] | ||
| 650 | disableTrackpoint = safeSpawn "xinput" ["disable", "TPPS/2 IBM TrackPoint"] | ||
| 651 | |||
| 652 | isDisabled :: String -> X Bool | ||
| 653 | isDisabled str = do | ||
| 654 | out <- runProcessWithInput "xinput" ["list", str] "" | ||
| 655 | return $ "disabled" `isInfixOf` out | ||
| 656 | |||
| 657 | |||
| 658 | spawnKeychain :: X () | ||
| 659 | spawnKeychain = do | ||
| 660 | home <- liftIO getHomeDirectory | ||
| 661 | let keys = (map ((home </>) . (".ssh/" ++)) ["id", "id-rsa"]) ++ ["6B13AA67"] | ||
| 662 | liftIO (maybe (return ()) (setEnv "SSH_ASKPASS") =<< findAskpass) | ||
| 663 | safeSpawn "keychain" . (["--agents", "gpg,ssh"] ++)=<< liftIO (filterM doesFileExist keys) | ||
| 664 | where | ||
| 665 | findAskpass = filter `liftM` readFile "/etc/zshrc" | ||
| 666 | filter = listToMaybe . catMaybes . map (stripPrefix "export SSH_ASKPASS=") . lines | ||
| 667 | |||
| 668 | assimilateKeychain :: X () | ||
| 669 | assimilateKeychain = liftIO $ assimilateKeychain' >> return () | ||
| 670 | assimilateKeychain' = tryIOError $ do | ||
| 671 | -- pid <- getProcessID | ||
| 672 | -- tmpDir <- lookupEnv "TMPDIR" | ||
| 673 | -- let tmpDir' = fromMaybe "/tmp" tmpDir | ||
| 674 | -- tmpFile = tmpDir' </> "xmonad-keychain" ++ (show pid) ++ ".env" | ||
| 675 | env <- runProcessWithInput "sh" ["-c", "eval $(keychain --eval --noask --agents gpg,ssh); env"] "" -- > " ++ tmpFile] "" | ||
| 676 | -- env <- readFile tmpFile | ||
| 677 | let envVars = Map.fromList $ map (\(k, v) -> (k, tail' v)) $ map (span (/= '=')) $ envLines | ||
| 678 | envVars' = Map.filterWithKey (\k _ -> k `elem` transfer) envVars | ||
| 679 | transfer = ["SSH_AUTH_SOCK", "SSH_AGENT_PID", "GPG_AGENT_INFO"] | ||
| 680 | envLines = filter (elem '=') $ lines env :: [String] | ||
| 681 | sequence $ map (\(k, c) -> setEnv k c) $ Map.toList envVars' | ||
| 682 | -- removeFile tmpFile | ||
| 683 | where | ||
| 684 | tail' [] = [] | ||
| 685 | tail' (x:xs) = xs | ||
| 686 | |||
| 687 | |||
| 688 | numKeys = [xK_parenleft, xK_parenright, xK_braceright, xK_plus, xK_braceleft, xK_bracketright, xK_bracketleft, xK_exclam, xK_equal, xK_asterisk] | ||
| 689 | |||
| 690 | instance Shrinker CustomShrink where | ||
| 691 | shrinkIt _ "" = [""] | ||
| 692 | shrinkIt s cs | ||
| 693 | | length cs >= 4 = cs : shrinkIt s ((reverse . drop 4 . reverse $ cs) ++ "...") | ||
| 694 | | otherwise = cs : shrinkIt s (init cs) | ||
| 695 | |||
| 696 | xPConfig, xPConfigMonospace :: XPConfig | ||
| 697 | xPConfig = def | ||
| 698 | { font = "xft:Fira Sans:pixelsize=21" | ||
| 699 | , height = 32 | ||
| 700 | , bgColor = "black" | ||
| 701 | , fgColor = gray | ||
| 702 | , fgHLight = green | ||
| 703 | , bgHLight = "black" | ||
| 704 | , borderColor = gray | ||
| 705 | , searchPredicate = (\needle haystack -> all (`isInfixOf` map toLower haystack) . map (map toLower) $ words needle) | ||
| 706 | , position = Top | ||
| 707 | } | ||
| 708 | xPConfigMonospace = xPConfig { font = "xft:Fira Code:pixelsize=21" } | ||
| 709 | |||
| 710 | sshOverrides host = map (\h -> mkOverride { oHost = h, oCommand = moshCmd . inTmux host} ) | ||
| 711 | [ "odin" | ||
| 712 | , "ymir" | ||
| 713 | , "surtr" | ||
| 714 | , "vidhar" | ||
| 715 | , "srv02.uniworx.de" | ||
| 716 | ] | ||
| 717 | ++ | ||
| 718 | map (\h -> mkOverride { oHost = h, oCommand = moshCmd' "/run/current-system/sw/bin/mosh-server" . withEnv [("TERM", "xterm")] . inTmux host} ) | ||
| 719 | [ "bragi", "bragi.asgard.yggdrasil" | ||
| 720 | ] | ||
| 721 | ++ | ||
| 722 | map (\h -> mkOverride { oHost = h, oCommand = sshCmd . inTmux host } ) | ||
| 723 | [ "uni2work-dev1", "srv01.uniworx.de" | ||
| 724 | ] | ||
| 725 | ++ | ||
| 726 | map (\h -> mkOverride { oHost = h, oCommand = sshCmd . withEnv [("TERM", "xterm")] . inTmux host } ) | ||
| 727 | [ "remote.cip.ifi.lmu.de" | ||
| 728 | , "uniworx3", "uniworx4", "uniworx5", "uniworxdb2" | ||
| 729 | , "testworx" | ||
| 730 | ] | ||
| 731 | |||
| 732 | backlight :: (Rational -> Rational) -> X () | ||
| 733 | backlight f = void . xfork . liftIO $ do | ||
| 734 | [ _device | ||
| 735 | , _class | ||
| 736 | , read . Text.unpack -> currentBright | ||
| 737 | , _currentPercentage | ||
| 738 | , read . Text.unpack -> maximumBright | ||
| 739 | ] <- Text.splitOn "," . Text.pack <$> readProcess "brightnessctl" ["-m"] "" | ||
| 740 | let current = currentBright % maximumBright | ||
| 741 | new' = f current * fromIntegral maximumBright | ||
| 742 | new :: Integer | ||
| 743 | new | floor new' < 0 = 0 | ||
| 744 | | ceiling new' > maximumBright = maximumBright | ||
| 745 | | new' >= maximumBright % 2 = ceiling new' | ||
| 746 | | otherwise = floor new' | ||
| 747 | callProcess "brightnessctl" ["-m", "s", show new] | ||
| 748 | |||
| 749 | cycleThrough :: [Rational] -> (Rational -> Rational) | ||
| 750 | cycleThrough opts current = fromMaybe currentOpt $ listToMaybe next' | ||
| 751 | where currentOpt = minimumBy (comparing $ abs . subtract current) opts | ||
| 752 | (_, _ : next') = break (== currentOpt) opts | ||
| 753 | |||
| 754 | cycleKbLayout :: [(String, Maybe String)] -> X () | ||
| 755 | cycleKbLayout [] = return () | ||
| 756 | cycleKbLayout layouts = liftIO $ do | ||
| 757 | next <- (getNext . extract) `liftM` runProcessWithInput "setxkbmap" ["-query"] "" | ||
| 758 | let | ||
| 759 | args = case next of | ||
| 760 | (l, Just v) -> [l, v] | ||
| 761 | (l, Nothing) -> [l] | ||
| 762 | safeSpawn "setxkbmap" args | ||
| 763 | where | ||
| 764 | extract :: String -> Maybe (String, Maybe String) | ||
| 765 | extract str = listToMaybe $ do | ||
| 766 | ["layout:", l] <- str' | ||
| 767 | [(l, Just v) | ["variant:", v] <- str'] ++ pure (l, Nothing) | ||
| 768 | where | ||
| 769 | str' = map words $ lines str | ||
| 770 | getNext :: Maybe (String, Maybe String) -> (String, Maybe String) | ||
| 771 | getNext = maybe (head layouts) getNext' | ||
| 772 | getNext' x = case elemIndex x layouts of | ||
| 773 | Nothing -> getNext Nothing | ||
| 774 | Just i -> layouts !! ((i + 1) `mod` length layouts) | ||
| 775 | |||
| 776 | mpvAll' :: MpvCommand -> IO [MpvResponse] | ||
| 777 | mpvAll' = mpvAll "/var/media/.mpv-ipc" | ||
| 778 | |||
| 779 | mpvOne' :: MpvCommand -> IO (Maybe MpvResponse) | ||
| 780 | mpvOne' = mpvOne "/var/media/.mpv-ipc" | ||
| 781 | |||
| 782 | mediaMpv :: MpvCommand -> X () | ||
| 783 | mediaMpv cmd = void . xfork $ print =<< mpvAll' cmd | ||
| 784 | |||
| 785 | mediaMpvTogglePause :: X () | ||
| 786 | mediaMpvTogglePause = void . xfork $ do | ||
| 787 | paused <- mapM mpvResponse <=< mpvAll' $ MpvGetProperty "pause" | ||
| 788 | if | ||
| 789 | | and paused -> print <=< mpvAll' $ MpvSetProperty "pause" False | ||
| 790 | | otherwise -> print <=< mpvOne' $ MpvSetProperty "pause" True | ||
| 791 | |||
| 792 | myKeys' conf host = Map.fromList $ | ||
| 793 | -- launch a terminal | ||
| 794 | [ ((modm, xK_Return), spawn $ (XMonad.terminal conf) ++ " -e tmux") | ||
| 795 | , ((modm .|. shiftMask, xK_Return), spawn $ XMonad.terminal conf) | ||
| 796 | |||
| 797 | -- launch dmenu | ||
| 798 | --, ((modm, xK_d ), spawn "exe=`dmenu_path | dmenu` && eval \"exec $exe\"") | ||
| 799 | , ((modm, xK_d ), shellPrompt "Run: " xPConfigMonospace) | ||
| 800 | , ((modm .|. shiftMask, xK_d ), prompt "Run in Terminal: " ("alacritty" ++ " -e") xPConfigMonospace) | ||
| 801 | , ((modm, xK_at ), sshPrompt (sshOverrides . Just $ hName host) xPConfigMonospace) | ||
| 802 | |||
| 803 | -- close focused window | ||
| 804 | , ((modm .|. shiftMask, xK_q ), kill) | ||
| 805 | , ((modm .|. controlMask .|. shiftMask, xK_q ), spawn "xkill") | ||
| 806 | |||
| 807 | -- Rotate through the available layout algorithms | ||
| 808 | , ((modm, xK_space ), sendMessage NextLayout) | ||
| 809 | |||
| 810 | -- Reset the layouts on the current workspace to default | ||
| 811 | , ((modm .|. controlMask, xK_r ), (setLayout $ XMonad.layoutHook conf) >> refresh) | ||
| 812 | |||
| 813 | -- Resize viewed windows to the correct size | ||
| 814 | , ((modm, xK_r ), refresh) | ||
| 815 | |||
| 816 | -- Move focus to the next window | ||
| 817 | , ((modm, xK_t ), windows W.focusDown) | ||
| 818 | |||
| 819 | -- Move focus to the previous window | ||
| 820 | , ((modm, xK_n ), windows W.focusUp ) | ||
| 821 | |||
| 822 | -- Move focus to the master window | ||
| 823 | , ((modm, xK_m ), windows W.focusMaster ) | ||
| 824 | |||
| 825 | -- Swap the focused window and the master window | ||
| 826 | , ((modm .|. shiftMask, xK_m ), windows W.swapMaster) | ||
| 827 | |||
| 828 | -- Swap the focused window with the next window | ||
| 829 | , ((modm .|. shiftMask, xK_t ), windows W.swapDown ) | ||
| 830 | |||
| 831 | -- Swap the focused window with the previous window | ||
| 832 | , ((modm .|. shiftMask, xK_n ), windows W.swapUp ) | ||
| 833 | |||
| 834 | -- Swap the focused window with the previous window | ||
| 835 | , ((modm .|. shiftMask .|. controlMask, xK_m), sendMessage SwapWindow) | ||
| 836 | |||
| 837 | , ((modm, xK_Right), sendMessage $ Go R) | ||
| 838 | , ((modm, xK_Left ), sendMessage $ Go L) | ||
| 839 | , ((modm, xK_Up ), sendMessage $ Go U) | ||
| 840 | , ((modm, xK_Down ), sendMessage $ Go D) | ||
| 841 | , ((modm .|. shiftMask , xK_Right), sendMessage $ Move R) | ||
| 842 | , ((modm .|. shiftMask , xK_Left ), sendMessage $ Move L) | ||
| 843 | , ((modm .|. shiftMask , xK_Up ), sendMessage $ Move U) | ||
| 844 | , ((modm .|. shiftMask , xK_Down ), sendMessage $ Move D) | ||
| 845 | -- , ((modm .|. controlMask, xK_Right), withFocused $ keysMoveWindow (10, 0)) | ||
| 846 | -- , ((modm .|. controlMask, xK_Left ), withFocused $ keysMoveWindow (-10, 0)) | ||
| 847 | -- , ((modm .|. controlMask, xK_Up ), withFocused $ keysMoveWindow (0, -10)) | ||
| 848 | -- , ((modm .|. controlMask, xK_Down ), withFocused $ keysMoveWindow (0, 10)) | ||
| 849 | -- Shrink the master area | ||
| 850 | , ((modm, xK_h ), sendMessage Shrink) | ||
| 851 | |||
| 852 | -- Expand the master area | ||
| 853 | , ((modm, xK_s ), sendMessage Expand) | ||
| 854 | |||
| 855 | -- Push window back into tiling | ||
| 856 | , ((modm .|. shiftMask, xK_space ), withFocused $ windows . W.sink) | ||
| 857 | , ((modm, xK_BackSpace), focusUrgent) | ||
| 858 | , ((modm .|. shiftMask, xK_BackSpace), clearUrgents) | ||
| 859 | |||
| 860 | -- Increment the number of windows in the master area | ||
| 861 | , ((modm , xK_comma ), sendMessage (IncMasterN 1)) | ||
| 862 | |||
| 863 | -- Deincrement the number of windows in the master area | ||
| 864 | , ((modm , xK_period), sendMessage (IncMasterN (-1))) | ||
| 865 | |||
| 866 | , ((0, xF86XK_AudioRaiseVolume), safeSpawn "pamixer" ["-i", "2"]) | ||
| 867 | , ((0, xF86XK_AudioLowerVolume), safeSpawn "pamixer" ["-d", "2"]) | ||
| 868 | , ((0, xF86XK_AudioMute), safeSpawn "pamixer" ["-t"]) | ||
| 869 | , ((0, xF86XK_AudioPause), mediaMpv $ MpvSetProperty "pause" False) | ||
| 870 | , ((0, {-xF86XK_AudioMicMute-} 269025202), safeSpawn "pulseaudio-ctl" ["mute-input"]) | ||
| 871 | , ((0, xF86XK_AudioPlay), mediaMpvTogglePause) | ||
| 872 | , ((0, xK_Print), do | ||
| 873 | home <- liftIO getHomeDirectory | ||
| 874 | unGrab | ||
| 875 | safeSpawn "scrot" ["-s", "-F", home </> "screenshots" </> "%Y-%m-%dT%H:%M:%S.png", "-e", "xclip -selection clipboard -t image/png -i $f"] | ||
| 876 | ) | ||
| 877 | , ((modm .|. mod1Mask, xK_space), mediaMpvTogglePause) | ||
| 878 | |||
| 879 | -- , ((0, xF86XK_MonBrightnessDown), backlight . cycleThrough $ reverse brCycle) | ||
| 880 | -- , ((0, xF86XK_MonBrightnessUp ), backlight $ cycleThrough brCycle) | ||
| 881 | , ((modm .|. shiftMask , xK_b), backlight . cycleThrough $ reverse brCycle) | ||
| 882 | , ((modm .|. shiftMask .|. controlMask, xK_b), backlight $ cycleThrough brCycle) | ||
| 883 | |||
| 884 | , ((modm , xK_Escape), cycleKbLayout (hKbLayouts host)) | ||
| 885 | , ((modm .|. controlMask, xK_Escape), safeSpawn "setxkbmap" $ fst (head $ hKbLayouts host) : maybeToList (snd . head $ hKbLayouts host)) | ||
| 886 | |||
| 887 | -- Toggle the status bar gap | ||
| 888 | -- Use this binding with avoidStruts from Hooks.ManageDocks. | ||
| 889 | -- See also the statusBar function from Hooks.DynamicLog. | ||
| 890 | -- | ||
| 891 | , ((modm , xK_b ), sendMessage ToggleStruts) | ||
| 892 | |||
| 893 | , ((modm .|. shiftMask, xK_p ), safeSpawn "playerctl" ["-a", "pause"]) | ||
| 894 | |||
| 895 | -- Quit xmonad | ||
| 896 | , ((modm .|. shiftMask, xK_e ), io (exitWith ExitSuccess)) | ||
| 897 | |||
| 898 | -- Restart xmonad | ||
| 899 | -- , ((modm .|. shiftMask .|. controlMask, xK_r ), void . xfork $ recompile False >>= flip when (safeSpawn "xmonad" ["--restart"])) | ||
| 900 | , ((modm .|. shiftMask, xK_r ), void . liftIO $ executeFile "xmonad" True [] Nothing) | ||
| 901 | , ((modm .|. shiftMask, xK_l ), void . xfork $ do | ||
| 902 | sessId <- getEnv "XDG_SESSION_ID" | ||
| 903 | safeSpawn "loginctl" ["lock-session", sessId] | ||
| 904 | ) | ||
| 905 | , ((modm .|. shiftMask, xK_s ), safeSpawn "systemctl" ["suspend"]) | ||
| 906 | , ((modm .|. shiftMask, xK_h ), inputPromptWithCompl xPConfigMonospace "systemctl" powerActCompl ?+ powerAct) | ||
| 907 | , ((modm, xK_v ), windows copyToAll) -- @@ Make focused window always visible | ||
| 908 | , ((modm .|. shiftMask, xK_v ), killAllOtherCopies) -- @@ Toggle window state back | ||
| 909 | , ((modm .|. shiftMask, xK_g ), windowPrompt xPConfig Goto wsWindows) | ||
| 910 | , ((modm , xK_g ), windowPrompt xPConfig Bring allWindows) | ||
| 911 | ] | ||
| 912 | ++ | ||
| 913 | |||
| 914 | -- | ||
| 915 | -- mod-[1..9], Switch to workspace N | ||
| 916 | -- | ||
| 917 | -- mod-[1..9], Switch to workspace N | ||
| 918 | -- mod-shift-[1..9], Move client to workspace N | ||
| 919 | -- | ||
| 920 | [((m .|. modm, k), windows $ f i) | ||
| 921 | | (i, k) <- zip (XMonad.workspaces conf) $ numKeys | ||
| 922 | , (f, m) <- [(W.greedyView, 0), (W.shift, shiftMask)] | ||
| 923 | ] | ||
| 924 | ++ | ||
| 925 | [((m .|. modm .|. controlMask, k), void . runMaybeT $ | ||
| 926 | MaybeT (P.getScreen def i) >>= MaybeT . screenWorkspace >>= lift . windows . f | ||
| 927 | ) | ||
| 928 | | (i, k) <- zip (hScreens host) [xK_g, xK_c, xK_r, xK_l] | ||
| 929 | , (f, m) <- [(W.view, 0), (W.shift, shiftMask)] | ||
| 930 | ] | ||
| 931 | where | ||
| 932 | modm = XMonad.modMask conf | ||
| 933 | |||
| 934 | brCycle = [0, 1 % 500, 1 % 250, 1 % 100, 1 % 10, 1 % 4, 1 % 2, 3 % 4, 1] | ||
| 935 | |||
| 936 | powerActWords = ["poweroff", "reboot", "hibernate", "suspend"] | ||
| 937 | powerActCompl = mkComplFunFromList' xPConfigMonospace powerActWords | ||
| 938 | powerAct act | act `elem` powerActWords = safeSpawn "systemctl" $ pure act | ||
| 939 | | otherwise = return () | ||
diff --git a/accounts/gkleen@sif/zshrc b/accounts/gkleen@sif/zshrc index e3f675a1..702990c3 100644 --- a/accounts/gkleen@sif/zshrc +++ b/accounts/gkleen@sif/zshrc | |||
| @@ -2,17 +2,14 @@ dir() { | |||
| 2 | curlArchive=false | 2 | curlArchive=false |
| 3 | templateArchive="" | 3 | templateArchive="" |
| 4 | repoUrl="" | 4 | repoUrl="" |
| 5 | nixShell="" | ||
| 6 | findNix=false | ||
| 7 | dir="" | 5 | dir="" |
| 8 | forceShell=false | 6 | forceShell=false |
| 9 | wormhole=false | 7 | wormhole=false |
| 10 | gitWorktree="" | 8 | gitWorktree="" |
| 11 | # notmuchMsg="" | ||
| 12 | quickserve=false | ||
| 13 | modifyPDF="" | 9 | modifyPDF="" |
| 10 | miniserve=false | ||
| 14 | 11 | ||
| 15 | while getopts ':t:a:s:Sd:ir:wqg:p:' arg; do | 12 | while getopts ':t:a:d:ir:wg:p:m' arg; do |
| 16 | case $arg in | 13 | case $arg in |
| 17 | "t") ;; | 14 | "t") ;; |
| 18 | "a") | 15 | "a") |
| @@ -23,16 +20,13 @@ dir() { | |||
| 23 | templateArchive=${OPTARG:a} | 20 | templateArchive=${OPTARG:a} |
| 24 | fi | 21 | fi |
| 25 | ;; | 22 | ;; |
| 26 | "s") nixShell=${OPTARG:a} ;; | ||
| 27 | "S") findNix=true ;; | ||
| 28 | "d") dir=${OPTARG} ;; | 23 | "d") dir=${OPTARG} ;; |
| 29 | "i") forceShell=true ;; | 24 | "i") forceShell=true ;; |
| 30 | "r") repoUrl=${OPTARG} ;; | 25 | "r") repoUrl=${OPTARG} ;; |
| 31 | "w") wormhole=true ;; | 26 | "w") wormhole=true ;; |
| 32 | "g") gitWorktree=${OPTARG} ;; | 27 | "g") gitWorktree=${OPTARG} ;; |
| 33 | # "n") notmuchMsg=${OPTARG} ;; | ||
| 34 | "q") quickserve=true ;; | ||
| 35 | "p") modifyPDF=${OPTARG:a} ;; | 28 | "p") modifyPDF=${OPTARG:a} ;; |
| 29 | "m") miniserve=true ;; | ||
| 36 | *) printf "Invalid option: %s\n" $arg >&2; exit 2 ;; | 30 | *) printf "Invalid option: %s\n" $arg >&2; exit 2 ;; |
| 37 | esac | 31 | esac |
| 38 | done | 32 | done |
| @@ -56,20 +50,34 @@ dir() { | |||
| 56 | gitWorktree="" | 50 | gitWorktree="" |
| 57 | fi | 51 | fi |
| 58 | 52 | ||
| 53 | miniservePIDFile="" | ||
| 54 | if [[ ${miniserve} = "true" ]]; then | ||
| 55 | miniservePIDFile=$(mktemp --tmpdir --suffix=.pid) | ||
| 56 | fi | ||
| 57 | |||
| 59 | cleanup() | 58 | cleanup() |
| 60 | { | 59 | { |
| 61 | cd ${modifyPDF:h} | 60 | if [[ -n ${modifyPDF} ]]; then |
| 62 | [[ -n ${modifyPDF} ]] && nix shell nixos#imagemagick -c convert -verbose ${dir}/${modifyPDF:t:r}_*.png(on) ${modifyPDF} | 61 | cd ${modifyPDF:h} |
| 62 | typeset -a pages | ||
| 63 | eval 'pages=(${dir}/${modifyPDF:t:r}_*.png(on))' | ||
| 64 | magick -verbose "$pages" ${modifyPDF} | ||
| 65 | modifyPDF="" | ||
| 66 | fi | ||
| 67 | if [[ -n ${miniservePIDFile} ]]; then | ||
| 68 | command kill --verbose -- $(cat ${miniservePIDFile}) && wait $(cat ${miniservePIDFile}) | ||
| 69 | miniservePIDFile="" | ||
| 70 | fi | ||
| 63 | } | 71 | } |
| 64 | 72 | ||
| 65 | ( | 73 | ( |
| 74 | set -o localoptions -o localtraps | ||
| 75 | trap 'return 1' INT TERM | ||
| 66 | trap cleanup EXIT | 76 | trap cleanup EXIT |
| 67 | 77 | ||
| 68 | cd ${dir} | 78 | cd ${dir} |
| 69 | export dir; | 79 | export dir; |
| 70 | 80 | ||
| 71 | ${findNix} && { nixShell=$(findNix) || return $? } | ||
| 72 | |||
| 73 | [[ -n ${repoUrl} ]] && git clone -- ${repoUrl} . | 81 | [[ -n ${repoUrl} ]] && git clone -- ${repoUrl} . |
| 74 | 82 | ||
| 75 | [[ -n ${modifyPDF} ]] && templateArchive=${modifyPDF} | 83 | [[ -n ${modifyPDF} ]] && templateArchive=${modifyPDF} |
| @@ -82,23 +90,23 @@ dir() { | |||
| 82 | } | 90 | } |
| 83 | trap cleanup EXIT | 91 | trap cleanup EXIT |
| 84 | 92 | ||
| 85 | if ${curlArchive}; then | 93 | if [[ $curlArchive = "true" ]]; then |
| 86 | archiveFile=$(mktemp -t "archive.XXXXXXXXXX.${templateArchive:t}") | 94 | archiveFile=$(mktemp -t "archive.XXXXXXXXXX.${templateArchive:t}") |
| 87 | 95 | ||
| 88 | curl -L -o ${archiveFile} ${templateArchive} | 96 | curl -SfL -o ${archiveFile} ${templateArchive} |
| 89 | 97 | ||
| 90 | templateArchive=${archiveFile} | 98 | templateArchive=${archiveFile} |
| 91 | fi | 99 | fi |
| 92 | 100 | ||
| 93 | unpack=true | 101 | unpack=true |
| 94 | while ${unpack}; do | 102 | while [[ $unpack = "true" ]]; do |
| 95 | case $(file --brief --mime-type --dereference ${templateArchive}) in | 103 | case $(file --brief --mime-type --dereference ${templateArchive}) in |
| 96 | application/zip) | 104 | application/zip) |
| 97 | unzip ${templateArchive} | 105 | unzip ${templateArchive} |
| 98 | unpack=false | 106 | unpack=false |
| 99 | ;; | 107 | ;; |
| 100 | application/vnd.debian.binary-package) | 108 | application/vnd.debian.binary-package) |
| 101 | nix shell nixos#binutils --command ar x ${templateArchive} | 109 | ar x ${templateArchive} |
| 102 | mkdir control data | 110 | mkdir control data |
| 103 | tar -C control -xvaf control.* | 111 | tar -C control -xvaf control.* |
| 104 | tar -C data -xvaf data.* | 112 | tar -C data -xvaf data.* |
| @@ -106,7 +114,7 @@ dir() { | |||
| 106 | ;; | 114 | ;; |
| 107 | application/x-rpm) | 115 | application/x-rpm) |
| 108 | cpioArchive=$(mktemp -t "archive.XXXXXXXXXX.${templateArchive:t:r}.cpio") | 116 | cpioArchive=$(mktemp -t "archive.XXXXXXXXXX.${templateArchive:t:r}.cpio") |
| 109 | nix shell nixos#busybox --command rpm2cpio ${templateArchive} > ${cpioArchive} | 117 | rpm2cpio ${templateArchive} > ${cpioArchive} |
| 110 | templateArchive=${cpioArchive} | 118 | templateArchive=${cpioArchive} |
| 111 | unpack=true | 119 | unpack=true |
| 112 | ;; | 120 | ;; |
| @@ -115,15 +123,19 @@ dir() { | |||
| 115 | unpack=false | 123 | unpack=false |
| 116 | ;; | 124 | ;; |
| 117 | application/pdf) | 125 | application/pdf) |
| 118 | nix shell nixos#ghostscript nixos#imagemagick -c convert -verbose -density 400 ${templateArchive} ${modifyPDF:t:r}_%0d.png | 126 | magick -verbose -density 400 ${templateArchive} ${modifyPDF:t:r}_%0d.png |
| 119 | unpack=false | 127 | unpack=false |
| 120 | ;; | 128 | ;; |
| 121 | application/octet-stream) | 129 | application/octet-stream) |
| 122 | if [[ $(file --brief --dereferenc ${templateArchive}) =~ Squashfs ]]; then | 130 | if [[ $(file --brief --dereference ${templateArchive}) =~ Squashfs ]]; then |
| 123 | nix shell nixos#squashfsTools -c unsquashfs -d . ${templateArchive} | 131 | unsquashfs -d . ${templateArchive} |
| 124 | unpack=false | 132 | unpack=false |
| 125 | fi | 133 | fi |
| 126 | ;; | 134 | ;; |
| 135 | application/x-iso9660-image) | ||
| 136 | 7z x ${templateArchive} | ||
| 137 | unpack=false | ||
| 138 | ;; | ||
| 127 | *) | 139 | *) |
| 128 | tar -xvaf ${templateArchive} | 140 | tar -xvaf ${templateArchive} |
| 129 | unpack=false | 141 | unpack=false |
| @@ -134,25 +146,21 @@ dir() { | |||
| 134 | fi | 146 | fi |
| 135 | 147 | ||
| 136 | 148 | ||
| 137 | ${wormhole} && wormhole receive --accept-file | 149 | [[ $wormhole = "true" ]] && wormhole receive --accept-file |
| 138 | 150 | ||
| 139 | 151 | ||
| 140 | if ${quickserve}; then | 152 | if [[ ${#@} -gt 0 ]]; then |
| 141 | quickserve --root . --upload . --show-hidden --tar gz | 153 | ${@} |
| 142 | fi | 154 | fi |
| 143 | 155 | ||
| 156 | cd $(pwd) # Needed for mounting to work | ||
| 144 | 157 | ||
| 145 | if [[ ${#@} -eq 0 ]] || ${forceShell}; then | 158 | if [[ ${miniserve} = "true" ]]; then |
| 146 | if [[ ${#@} -gt 0 ]]; then | 159 | miniserve --random-route --hidden --enable-tar-gz --enable-zip . & |
| 147 | if [[ -z ${nixShell} ]]; then | 160 | echo $! > "${miniservePIDFile}" |
| 148 | ${@} | 161 | fi |
| 149 | else | ||
| 150 | nix-shell ${nixShell} --run "${@}" | ||
| 151 | fi | ||
| 152 | fi | ||
| 153 | |||
| 154 | cd $(pwd) # Needed for mounting to work | ||
| 155 | 162 | ||
| 163 | if [[ ${#@} -eq 0 ]] && [[ ${miniserve} != "true" ]] || [[ $forceShell = "true" ]]; then | ||
| 156 | isSingleDir() { | 164 | isSingleDir() { |
| 157 | typeset -a contents | 165 | typeset -a contents |
| 158 | contents=(*(N) .*(N)) | 166 | contents=(*(N) .*(N)) |
| @@ -166,18 +174,9 @@ dir() { | |||
| 166 | } | 174 | } |
| 167 | while d=$(isSingleDir); do cd ${d}; done | 175 | while d=$(isSingleDir); do cd ${d}; done |
| 168 | 176 | ||
| 169 | 177 | zsh | |
| 170 | if [[ -z ${nixShell} ]]; then | 178 | elif [[ ${miniserve} == "true" ]]; then |
| 171 | zsh | 179 | wait $(cat "${miniservePIDFile}") |
| 172 | else | ||
| 173 | nix-shell ${nixShell} --run zsh | ||
| 174 | fi | ||
| 175 | else | ||
| 176 | if [[ -z ${nixShell} ]]; then | ||
| 177 | ${@} | ||
| 178 | else | ||
| 179 | nix-shell ${nixShell} --run "${@}" | ||
| 180 | fi | ||
| 181 | fi | 180 | fi |
| 182 | ) | 181 | ) |
| 183 | } | 182 | } |
| @@ -185,27 +184,30 @@ dir() { | |||
| 185 | tmpdir() { | 184 | tmpdir() { |
| 186 | cleanup() | 185 | cleanup() |
| 187 | { | 186 | { |
| 188 | cd / | 187 | cd / |
| 189 | unmount() { | 188 | unmount() { |
| 190 | printf "Unmounting %s\n" ${1} >&2 | 189 | printf "Unmounting %s\n" ${1} >&2 |
| 191 | fusermount -u ${1} || umount ${1} || sudo umount ${1} | 190 | fusermount -u ${1} || umount ${1} || sudo umount ${1} |
| 192 | } | 191 | } |
| 193 | 192 | ||
| 194 | if mountpoint -q -- ${dir}; then | 193 | if [[ -n ${dir} ]]; then |
| 195 | unmount ${dir} || return $? | 194 | if mountpoint -q -- ${dir}; then |
| 196 | else | 195 | unmount ${dir} || return $? |
| 197 | while read -d $'\0' subDir; do | 196 | else |
| 198 | mountpoint -q -- ${subDir} || continue | 197 | while read -d $'\0' subDir; do |
| 199 | unmount ${subDir} || return $? | 198 | mountpoint -q -- ${subDir} || continue |
| 200 | done <<<$(find ${dir} -xdev -type d -print0 | sort -zr) | 199 | unmount ${subDir} || return $? |
| 201 | fi | 200 | done <<<$(find ${dir} -xdev -type d -print0 | sort -zr) |
| 202 | 201 | fi | |
| 203 | rm -rfv --one-file-system -- ${dir} | 202 | |
| 203 | rm -rfv --one-file-system -- ${dir} | ||
| 204 | dir="" | ||
| 205 | fi | ||
| 204 | } | 206 | } |
| 205 | 207 | ||
| 206 | local tmpdir="" | 208 | local tmpdir="" |
| 207 | 209 | ||
| 208 | while getopts ':t:a:s:Sd:ir:wqg:p:' arg; do | 210 | while getopts ':t:a:d:ir:wg:p:m' arg; do |
| 209 | case $arg in | 211 | case $arg in |
| 210 | "t") tmpdir="=${OPTARG}" ;; | 212 | "t") tmpdir="=${OPTARG}" ;; |
| 211 | "?"|":") printf "Invalid option: %s\n" $arg >&2; exit 2 ;; | 213 | "?"|":") printf "Invalid option: %s\n" $arg >&2; exit 2 ;; |
| @@ -213,6 +215,8 @@ tmpdir() { | |||
| 213 | done | 215 | done |
| 214 | 216 | ||
| 215 | ( | 217 | ( |
| 218 | set -o localoptions -o localtraps | ||
| 219 | trap 'return 1' INT TERM | ||
| 216 | trap cleanup EXIT | 220 | trap cleanup EXIT |
| 217 | 221 | ||
| 218 | 222 | ||
| @@ -231,17 +235,7 @@ clock() { | |||
| 231 | } | 235 | } |
| 232 | 236 | ||
| 233 | public-ip() { | 237 | public-ip() { |
| 234 | curl -s -H 'Accept: application/json' $@ ifconfig.co | jq -r '.ip' | 238 | curl -sSf -H 'Accept: application/json' $@ ifconfig.co | jq -r '.ip' |
| 235 | } | ||
| 236 | |||
| 237 | nix-ghci() { | ||
| 238 | pkgExpr="" | ||
| 239 | if [[ ${#@} -gt 0 ]]; then | ||
| 240 | pkgExpr="${1}" | ||
| 241 | shift | ||
| 242 | fi | ||
| 243 | |||
| 244 | nix-shell -p "with (import <nixpkgs> {}); pkgs.haskellPackages.ghcWithPackages (p: with p; [${pkgExpr}])" --run "ghci ${@}" | ||
| 245 | } | 239 | } |
| 246 | 240 | ||
| 247 | swap() { | 241 | swap() { |
| @@ -271,14 +265,6 @@ l() { | |||
| 271 | ls --long --binary --git --time-style=iso --header $@ | 265 | ls --long --binary --git --time-style=iso --header $@ |
| 272 | } | 266 | } |
| 273 | 267 | ||
| 274 | re() { | ||
| 275 | systemctl --restart $@ | ||
| 276 | } | ||
| 277 | |||
| 278 | ure() { | ||
| 279 | systemctl --user --restart $@ | ||
| 280 | } | ||
| 281 | |||
| 282 | ssh-installer() { | 268 | ssh-installer() { |
| 283 | ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no -o IdentityFile=~/.ssh/gkleen@sif.midgard.yggdrasil $@ | 269 | ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no -o IdentityFile=~/.ssh/gkleen@sif.midgard.yggdrasil $@ |
| 284 | } | 270 | } |
| @@ -306,20 +292,7 @@ done < <(find ~/projects ~/uni -regextype posix-extended -maxdepth 2 -type d -re | |||
| 306 | sed -zr 's|(.*/([0-9]{2}[ws])/(.+))|\1 \2 \3|' | \ | 292 | sed -zr 's|(.*/([0-9]{2}[ws])/(.+))|\1 \2 \3|' | \ |
| 307 | sort -z -r -k2 | sort -z -s -k3 | uniq -z -f 2) | 293 | sort -z -r -k2 | sort -z -s -k3 | uniq -z -f 2) |
| 308 | 294 | ||
| 309 | alias '..'='cd ..' | ||
| 310 | alias rzadm=$'tmpdir -i sh -c \'mkdir adm; sshfs gkleen@mgmt01:/adm adm\'' | 295 | alias rzadm=$'tmpdir -i sh -c \'mkdir adm; sshfs gkleen@mgmt01:/adm adm\'' |
| 311 | alias mathcloud=$'tmpdir -i rclone mount --daemon mathcloud:// .' | 296 | alias mathcloud=$'tmpdir -i rclone mount --daemon mathcloud:// .' |
| 312 | alias -g L='| less' | ||
| 313 | alias -g S='&> /dev/null' | ||
| 314 | alias -g G='| grep' | ||
| 315 | alias -g B='&> /dev/null &' | ||
| 316 | alias -g BB='&> /dev/null &!' | ||
| 317 | 297 | ||
| 318 | export DEFAULT_USER=gkleen | 298 | export DEFAULT_USER=gkleen |
| 319 | |||
| 320 | bindkey -e | ||
| 321 | bindkey ';5C' emacs-forward-word | ||
| 322 | bindkey ';5D' emacs-backward-word | ||
| 323 | bindkey '^[[1;5C' emacs-forward-word | ||
| 324 | bindkey '^[[1;5D' emacs-backward-word | ||
| 325 | bindkey '^H' backward-kill-word | ||
diff --git a/accounts/gkleen@surtr.nix b/accounts/gkleen@surtr.nix index 58c4f21d..8f678ac9 100644 --- a/accounts/gkleen@surtr.nix +++ b/accounts/gkleen@surtr.nix | |||
| @@ -1,3 +1,7 @@ | |||
| 1 | { userName, ... }: { | 1 | { flake, userName, ... }: { |
| 2 | home-manager.users.${userName}.home.stateVersion = "20.09"; | 2 | imports = with flake.nixosModules.userProfiles.${userName}; [ |
| 3 | zsh tmux | ||
| 4 | ]; | ||
| 5 | |||
| 6 | config.home-manager.users.${userName}.home.stateVersion = "20.09"; | ||
| 3 | } | 7 | } |
diff --git a/accounts/gkleen@vidhar.nix b/accounts/gkleen@vidhar.nix index 8509c2f4..3a37c4bd 100644 --- a/accounts/gkleen@vidhar.nix +++ b/accounts/gkleen@vidhar.nix | |||
| @@ -1,4 +1,8 @@ | |||
| 1 | { flake, pkgs, userName, config, ... }: { | 1 | { flake, pkgs, userName, config, ... }: { |
| 2 | imports = with flake.nixosModules.userProfiles.${userName}; [ | ||
| 3 | zsh tmux | ||
| 4 | ]; | ||
| 5 | |||
| 2 | config = { | 6 | config = { |
| 3 | users.users.${userName} = { | 7 | users.users.${userName} = { |
| 4 | uid = 1000; | 8 | uid = 1000; |
diff --git a/accounts/mherold@eostre.nix b/accounts/mherold@eostre.nix index 51e4529a..0e2f37aa 100644 --- a/accounts/mherold@eostre.nix +++ b/accounts/mherold@eostre.nix | |||
| @@ -7,9 +7,9 @@ | |||
| 7 | home-manager.users.${userName} = { | 7 | home-manager.users.${userName} = { |
| 8 | home.stateVersion = "20.09"; | 8 | home.stateVersion = "20.09"; |
| 9 | 9 | ||
| 10 | nixpkgs.config = { | 10 | # nixpkgs.config = { |
| 11 | allowUnfree = true; | 11 | # allowUnfree = true; |
| 12 | }; | 12 | # }; |
| 13 | 13 | ||
| 14 | home.packages = with pkgs; [ | 14 | home.packages = with pkgs; [ |
| 15 | thunderbird libreoffice element-desktop keepassxc vlc | 15 | thunderbird libreoffice element-desktop keepassxc vlc |
diff --git a/accounts/root@installer.nix b/accounts/root@installer.nix index c7a418f8..5fe1db38 100644 --- a/accounts/root@installer.nix +++ b/accounts/root@installer.nix | |||
| @@ -1,7 +1,11 @@ | |||
| 1 | { userName, ... }: | 1 | { flake, userName, ... }: |
| 2 | 2 | ||
| 3 | { | 3 | { |
| 4 | home-manager.users.${userName} = { config, ... } : { | 4 | imports = with flake.nixosModules.userProfiles.${userName}; [ |
| 5 | zsh tmux | ||
| 6 | ]; | ||
| 7 | |||
| 8 | config.home-manager.users.${userName} = { config, ... } : { | ||
| 5 | home.stateVersion = config.home.version.release; | 9 | home.stateVersion = config.home.version.release; |
| 6 | }; | 10 | }; |
| 7 | } | 11 | } |
diff --git a/accounts/root@sif.nix b/accounts/root@sif.nix index c9e129a0..bb816230 100644 --- a/accounts/root@sif.nix +++ b/accounts/root@sif.nix | |||
| @@ -1,6 +1,10 @@ | |||
| 1 | { userName, ... }: | 1 | { flake, userName, ... }: |
| 2 | { | 2 | { |
| 3 | home-manager.users.${userName} = { | 3 | imports = with flake.nixosModules.userProfiles.${userName}; [ |
| 4 | zsh tmux | ||
| 5 | ]; | ||
| 6 | |||
| 7 | config.home-manager.users.${userName} = { | ||
| 4 | home.stateVersion = "20.09"; | 8 | home.stateVersion = "20.09"; |
| 5 | 9 | ||
| 6 | programs.ssh.matchBlocks = { | 10 | programs.ssh.matchBlocks = { |
diff --git a/accounts/root@surtr.nix b/accounts/root@surtr.nix index 58c4f21d..8f678ac9 100644 --- a/accounts/root@surtr.nix +++ b/accounts/root@surtr.nix | |||
| @@ -1,3 +1,7 @@ | |||
| 1 | { userName, ... }: { | 1 | { flake, userName, ... }: { |
| 2 | home-manager.users.${userName}.home.stateVersion = "20.09"; | 2 | imports = with flake.nixosModules.userProfiles.${userName}; [ |
| 3 | zsh tmux | ||
| 4 | ]; | ||
| 5 | |||
| 6 | config.home-manager.users.${userName}.home.stateVersion = "20.09"; | ||
| 3 | } | 7 | } |
diff --git a/accounts/root@vidhar.nix b/accounts/root@vidhar.nix index e82414a8..0fc56633 100644 --- a/accounts/root@vidhar.nix +++ b/accounts/root@vidhar.nix | |||
| @@ -1,6 +1,11 @@ | |||
| 1 | { config, userName, ... }: | 1 | { flake, config, userName, ... }: |
| 2 | |||
| 2 | { | 3 | { |
| 3 | home-manager.users.${userName} = { | 4 | imports = with flake.nixosModules.userProfiles.${userName}; [ |
| 5 | zsh tmux | ||
| 6 | ]; | ||
| 7 | |||
| 8 | config.home-manager.users.${userName} = { | ||
| 4 | home.stateVersion = "20.09"; | 9 | home.stateVersion = "20.09"; |
| 5 | 10 | ||
| 6 | programs.ssh.matchBlocks = { | 11 | programs.ssh.matchBlocks = { |
| @@ -6,22 +6,28 @@ | |||
| 6 | "nixpkgs": [ | 6 | "nixpkgs": [ |
| 7 | "nixpkgs" | 7 | "nixpkgs" |
| 8 | ], | 8 | ], |
| 9 | "poetry2nix": [ | 9 | "pre-commit-hooks-nix": "pre-commit-hooks-nix", |
| 10 | "poetry2nix" | 10 | "pyproject-build-systems": [ |
| 11 | "pyproject-build-systems" | ||
| 12 | ], | ||
| 13 | "pyproject-nix": [ | ||
| 14 | "pyproject-nix" | ||
| 11 | ], | 15 | ], |
| 12 | "pre-commit-hooks-nix": "pre-commit-hooks-nix" | 16 | "uv2nix": [ |
| 17 | "uv2nix" | ||
| 18 | ] | ||
| 13 | }, | 19 | }, |
| 14 | "locked": { | 20 | "locked": { |
| 15 | "lastModified": 1723124245, | 21 | "lastModified": 1749560907, |
| 16 | "narHash": "sha256-ThDq7vOXo6G4+C5FHqUc64CeX2c5n36tln4ZlDao6s4=", | 22 | "narHash": "sha256-zvAxxnJ5dcnjbuog0W6UvlthD1dLm5t4ZQI25jzNDW4=", |
| 17 | "owner": "gkleen", | 23 | "owner": "gkleen", |
| 18 | "repo": "backup-utils", | 24 | "repo": "backup-utils", |
| 19 | "rev": "74e65090de63fc99f056098677cc490754e2708f", | 25 | "rev": "8ed6a1d7c2e337cb2e35ed68f6d852f0d1049908", |
| 20 | "type": "gitlab" | 26 | "type": "gitlab" |
| 21 | }, | 27 | }, |
| 22 | "original": { | 28 | "original": { |
| 23 | "owner": "gkleen", | 29 | "owner": "gkleen", |
| 24 | "ref": "v0.1.6", | 30 | "ref": "v0.1.7", |
| 25 | "repo": "backup-utils", | 31 | "repo": "backup-utils", |
| 26 | "type": "gitlab" | 32 | "type": "gitlab" |
| 27 | } | 33 | } |
| @@ -33,26 +39,45 @@ | |||
| 33 | "nixpkgs": [ | 39 | "nixpkgs": [ |
| 34 | "nixpkgs" | 40 | "nixpkgs" |
| 35 | ], | 41 | ], |
| 36 | "poetry2nix": [ | 42 | "pre-commit-hooks-nix": "pre-commit-hooks-nix_2", |
| 37 | "poetry2nix" | 43 | "pyproject-build-systems": "pyproject-build-systems", |
| 44 | "pyproject-nix": [ | ||
| 45 | "pyproject-nix" | ||
| 38 | ], | 46 | ], |
| 39 | "pre-commit-hooks-nix": "pre-commit-hooks-nix_2" | 47 | "uv2nix": [ |
| 48 | "uv2nix" | ||
| 49 | ] | ||
| 40 | }, | 50 | }, |
| 41 | "locked": { | 51 | "locked": { |
| 42 | "lastModified": 1734281899, | 52 | "lastModified": 1750599403, |
| 43 | "narHash": "sha256-9QdIl3sjHY4Xij9KrBUkW1KpLB+jyxlI12UHPitlawI=", | 53 | "narHash": "sha256-MLQ7CISl00w1xq88TL2wukNq3ukzID4u7BVT4okbUik=", |
| 44 | "owner": "gkleen", | 54 | "owner": "gkleen", |
| 45 | "repo": "ca", | 55 | "repo": "ca", |
| 46 | "rev": "1e4ee9d25a5282ef7bc6774072229784fa0036f3", | 56 | "rev": "505a29233ada969b2eca76d616b0d7a8767dfb71", |
| 47 | "type": "gitlab" | 57 | "type": "gitlab" |
| 48 | }, | 58 | }, |
| 49 | "original": { | 59 | "original": { |
| 50 | "owner": "gkleen", | 60 | "owner": "gkleen", |
| 51 | "ref": "v3.1.3", | 61 | "ref": "v3.1.5", |
| 52 | "repo": "ca", | 62 | "repo": "ca", |
| 53 | "type": "gitlab" | 63 | "type": "gitlab" |
| 54 | } | 64 | } |
| 55 | }, | 65 | }, |
| 66 | "crane": { | ||
| 67 | "locked": { | ||
| 68 | "lastModified": 1731098351, | ||
| 69 | "narHash": "sha256-HQkYvKvaLQqNa10KEFGgWHfMAbWBfFp+4cAgkut+NNE=", | ||
| 70 | "owner": "ipetkov", | ||
| 71 | "repo": "crane", | ||
| 72 | "rev": "ef80ead953c1b28316cc3f8613904edc2eb90c28", | ||
| 73 | "type": "github" | ||
| 74 | }, | ||
| 75 | "original": { | ||
| 76 | "owner": "ipetkov", | ||
| 77 | "repo": "crane", | ||
| 78 | "type": "github" | ||
| 79 | } | ||
| 80 | }, | ||
| 56 | "deploy-rs": { | 81 | "deploy-rs": { |
| 57 | "inputs": { | 82 | "inputs": { |
| 58 | "flake-compat": [ | 83 | "flake-compat": [ |
| @@ -66,11 +91,11 @@ | |||
| 66 | ] | 91 | ] |
| 67 | }, | 92 | }, |
| 68 | "locked": { | 93 | "locked": { |
| 69 | "lastModified": 1727447169, | 94 | "lastModified": 1749105467, |
| 70 | "narHash": "sha256-3KyjMPUKHkiWhwR91J1YchF6zb6gvckCAY1jOE+ne0U=", | 95 | "narHash": "sha256-hXh76y/wDl15almBcqvjryB50B0BaiXJKk20f314RoE=", |
| 71 | "owner": "serokell", | 96 | "owner": "serokell", |
| 72 | "repo": "deploy-rs", | 97 | "repo": "deploy-rs", |
| 73 | "rev": "aa07eb05537d4cd025e2310397a6adcedfe72c76", | 98 | "rev": "6bc76b872374845ba9d645a2f012b764fecd765f", |
| 74 | "type": "github" | 99 | "type": "github" |
| 75 | }, | 100 | }, |
| 76 | "original": { | 101 | "original": { |
| @@ -115,11 +140,11 @@ | |||
| 115 | "flake-compat_3": { | 140 | "flake-compat_3": { |
| 116 | "flake": false, | 141 | "flake": false, |
| 117 | "locked": { | 142 | "locked": { |
| 118 | "lastModified": 1733328505, | 143 | "lastModified": 1747046372, |
| 119 | "narHash": "sha256-NeCCThCEP3eCl2l/+27kNNK7QrwZB1IJCrXfrbv5oqU=", | 144 | "narHash": "sha256-CIVLLkVgvHYbgI2UpXvIIBJ12HWgX+fjA8Xf8PUmqCY=", |
| 120 | "owner": "edolstra", | 145 | "owner": "edolstra", |
| 121 | "repo": "flake-compat", | 146 | "repo": "flake-compat", |
| 122 | "rev": "ff81ac966bb2cae68946d5ed5fc4994f96d0ffec", | 147 | "rev": "9100a0f413b0c601e0533d1d94ffd501ce2e7885", |
| 123 | "type": "github" | 148 | "type": "github" |
| 124 | }, | 149 | }, |
| 125 | "original": { | 150 | "original": { |
| @@ -132,6 +157,22 @@ | |||
| 132 | "flake-compat_4": { | 157 | "flake-compat_4": { |
| 133 | "flake": false, | 158 | "flake": false, |
| 134 | "locked": { | 159 | "locked": { |
| 160 | "lastModified": 1696426674, | ||
| 161 | "narHash": "sha256-kvjfFW7WAETZlt09AgDn1MrtKzP7t90Vf7vypd3OL1U=", | ||
| 162 | "owner": "edolstra", | ||
| 163 | "repo": "flake-compat", | ||
| 164 | "rev": "0f9255e01c2351cc7d116c072cb317785dd33b33", | ||
| 165 | "type": "github" | ||
| 166 | }, | ||
| 167 | "original": { | ||
| 168 | "owner": "edolstra", | ||
| 169 | "repo": "flake-compat", | ||
| 170 | "type": "github" | ||
| 171 | } | ||
| 172 | }, | ||
| 173 | "flake-compat_5": { | ||
| 174 | "flake": false, | ||
| 175 | "locked": { | ||
| 135 | "lastModified": 1673956053, | 176 | "lastModified": 1673956053, |
| 136 | "narHash": "sha256-4gtG9iQuiKITOjNQQeQIpoIB6b16fm+504Ch3sNKLd8=", | 177 | "narHash": "sha256-4gtG9iQuiKITOjNQQeQIpoIB6b16fm+504Ch3sNKLd8=", |
| 137 | "owner": "edolstra", | 178 | "owner": "edolstra", |
| @@ -168,11 +209,11 @@ | |||
| 168 | "nixpkgs-lib": "nixpkgs-lib_2" | 209 | "nixpkgs-lib": "nixpkgs-lib_2" |
| 169 | }, | 210 | }, |
| 170 | "locked": { | 211 | "locked": { |
| 171 | "lastModified": 1726153070, | 212 | "lastModified": 1749398372, |
| 172 | "narHash": "sha256-HO4zgY0ekfwO5bX0QH/3kJ/h4KvUDFZg8YpkNwIbg1U=", | 213 | "narHash": "sha256-tYBdgS56eXYaWVW3fsnPQ/nFlgWi/Z2Ymhyu21zVM98=", |
| 173 | "owner": "hercules-ci", | 214 | "owner": "hercules-ci", |
| 174 | "repo": "flake-parts", | 215 | "repo": "flake-parts", |
| 175 | "rev": "bcef6817a8b2aa20a5a6dbb19b43e63c5bf8619a", | 216 | "rev": "9305fe4e5c2a6fcf5ba6a3ff155720fbe4076569", |
| 176 | "type": "github" | 217 | "type": "github" |
| 177 | }, | 218 | }, |
| 178 | "original": { | 219 | "original": { |
| @@ -183,6 +224,27 @@ | |||
| 183 | }, | 224 | }, |
| 184 | "flake-parts_3": { | 225 | "flake-parts_3": { |
| 185 | "inputs": { | 226 | "inputs": { |
| 227 | "nixpkgs-lib": [ | ||
| 228 | "lanzaboote", | ||
| 229 | "nixpkgs" | ||
| 230 | ] | ||
| 231 | }, | ||
| 232 | "locked": { | ||
| 233 | "lastModified": 1730504689, | ||
| 234 | "narHash": "sha256-hgmguH29K2fvs9szpq2r3pz2/8cJd2LPS+b4tfNFCwE=", | ||
| 235 | "owner": "hercules-ci", | ||
| 236 | "repo": "flake-parts", | ||
| 237 | "rev": "506278e768c2a08bec68eb62932193e341f55c90", | ||
| 238 | "type": "github" | ||
| 239 | }, | ||
| 240 | "original": { | ||
| 241 | "owner": "hercules-ci", | ||
| 242 | "repo": "flake-parts", | ||
| 243 | "type": "github" | ||
| 244 | } | ||
| 245 | }, | ||
| 246 | "flake-parts_4": { | ||
| 247 | "inputs": { | ||
| 186 | "nixpkgs-lib": "nixpkgs-lib_3" | 248 | "nixpkgs-lib": "nixpkgs-lib_3" |
| 187 | }, | 249 | }, |
| 188 | "locked": { | 250 | "locked": { |
| @@ -202,11 +264,11 @@ | |||
| 202 | "flake-registry": { | 264 | "flake-registry": { |
| 203 | "flake": false, | 265 | "flake": false, |
| 204 | "locked": { | 266 | "locked": { |
| 205 | "lastModified": 1717415742, | 267 | "lastModified": 1744623129, |
| 206 | "narHash": "sha256-HKvoLGZUsBpjkxWkdtctGYj6RH0bl6vcw0OjTOqyzJk=", | 268 | "narHash": "sha256-nlQTQrHqM+ywXN0evDXnYEV6z6WWZB5BFQ2TkXsduKw=", |
| 207 | "owner": "NixOS", | 269 | "owner": "NixOS", |
| 208 | "repo": "flake-registry", | 270 | "repo": "flake-registry", |
| 209 | "rev": "895a65f8d5acf848136ee8fe8e8f736f0d27df96", | 271 | "rev": "1322f33d5836ae757d2e6190239252cf8402acf6", |
| 210 | "type": "github" | 272 | "type": "github" |
| 211 | }, | 273 | }, |
| 212 | "original": { | 274 | "original": { |
| @@ -296,6 +358,28 @@ | |||
| 296 | "gitignore_3": { | 358 | "gitignore_3": { |
| 297 | "inputs": { | 359 | "inputs": { |
| 298 | "nixpkgs": [ | 360 | "nixpkgs": [ |
| 361 | "lanzaboote", | ||
| 362 | "pre-commit-hooks-nix", | ||
| 363 | "nixpkgs" | ||
| 364 | ] | ||
| 365 | }, | ||
| 366 | "locked": { | ||
| 367 | "lastModified": 1709087332, | ||
| 368 | "narHash": "sha256-HG2cCnktfHsKV0s4XW83gU3F57gaTljL9KNSuG6bnQs=", | ||
| 369 | "owner": "hercules-ci", | ||
| 370 | "repo": "gitignore.nix", | ||
| 371 | "rev": "637db329424fd7e46cf4185293b9cc8c88c95394", | ||
| 372 | "type": "github" | ||
| 373 | }, | ||
| 374 | "original": { | ||
| 375 | "owner": "hercules-ci", | ||
| 376 | "repo": "gitignore.nix", | ||
| 377 | "type": "github" | ||
| 378 | } | ||
| 379 | }, | ||
| 380 | "gitignore_4": { | ||
| 381 | "inputs": { | ||
| 382 | "nixpkgs": [ | ||
| 299 | "prometheus-borg-exporter", | 383 | "prometheus-borg-exporter", |
| 300 | "pre-commit-hooks-nix", | 384 | "pre-commit-hooks-nix", |
| 301 | "nixpkgs" | 385 | "nixpkgs" |
| @@ -322,11 +406,11 @@ | |||
| 322 | ] | 406 | ] |
| 323 | }, | 407 | }, |
| 324 | "locked": { | 408 | "locked": { |
| 325 | "lastModified": 1722322032, | 409 | "lastModified": 1753177987, |
| 326 | "narHash": "sha256-pnO44gA8GcJj3oCVeGmypSGLr10+usMbJXochJWdugw=", | 410 | "narHash": "sha256-PkCc+YTrl0A/H6EV09DCr5yZpvQZ9DkuFXj/NNaEvHs=", |
| 327 | "owner": "gkleen", | 411 | "owner": "gkleen", |
| 328 | "repo": "home-manager", | 412 | "repo": "home-manager", |
| 329 | "rev": "55c1d61f06fd331f874178a6028f22be22ee7878", | 413 | "rev": "b493410fc6e427129a1caee8f50970d152a27daa", |
| 330 | "type": "github" | 414 | "type": "github" |
| 331 | }, | 415 | }, |
| 332 | "original": { | 416 | "original": { |
| @@ -343,27 +427,27 @@ | |||
| 343 | ] | 427 | ] |
| 344 | }, | 428 | }, |
| 345 | "locked": { | 429 | "locked": { |
| 346 | "lastModified": 1710245356, | 430 | "lastModified": 1749562430, |
| 347 | "narHash": "sha256-8cQGUn+N1dTgklMWMejSLN2q8Oz+7Rnqsfaw2rt3bU4=", | 431 | "narHash": "sha256-M5MqsIsf+o7yngakVUW4poBGZaghB6sUpw7SsWA55kU=", |
| 348 | "owner": "gkleen", | 432 | "owner": "gkleen", |
| 349 | "repo": "home-manager", | 433 | "repo": "home-manager", |
| 350 | "rev": "a14fe0c27d04dfa3d80abe2db743e9a7f4f2a33d", | 434 | "rev": "dca5a2df9f8a00cc34bea6ead249a8446f5f069e", |
| 351 | "type": "github" | 435 | "type": "github" |
| 352 | }, | 436 | }, |
| 353 | "original": { | 437 | "original": { |
| 354 | "owner": "gkleen", | 438 | "owner": "gkleen", |
| 355 | "ref": "nixos-late-start-23.11", | 439 | "ref": "nixos-late-start-25.05", |
| 356 | "repo": "home-manager", | 440 | "repo": "home-manager", |
| 357 | "type": "github" | 441 | "type": "github" |
| 358 | } | 442 | } |
| 359 | }, | 443 | }, |
| 360 | "impermanence": { | 444 | "impermanence": { |
| 361 | "locked": { | 445 | "locked": { |
| 362 | "lastModified": 1731242966, | 446 | "lastModified": 1737831083, |
| 363 | "narHash": "sha256-B3C3JLbGw0FtLSWCjBxU961gLNv+BOOBC6WvstKLYMw=", | 447 | "narHash": "sha256-LJggUHbpyeDvNagTUrdhe/pRVp4pnS6wVKALS782gRI=", |
| 364 | "owner": "nix-community", | 448 | "owner": "nix-community", |
| 365 | "repo": "impermanence", | 449 | "repo": "impermanence", |
| 366 | "rev": "3ed3f0eaae9fcc0a8331e77e9319c8a4abd8a71a", | 450 | "rev": "4b3e914cdf97a5b536a889e939fb2fd2b043a170", |
| 367 | "type": "github" | 451 | "type": "github" |
| 368 | }, | 452 | }, |
| 369 | "original": { | 453 | "original": { |
| @@ -373,10 +457,36 @@ | |||
| 373 | "type": "github" | 457 | "type": "github" |
| 374 | } | 458 | } |
| 375 | }, | 459 | }, |
| 460 | "lanzaboote": { | ||
| 461 | "inputs": { | ||
| 462 | "crane": "crane", | ||
| 463 | "flake-compat": "flake-compat_4", | ||
| 464 | "flake-parts": "flake-parts_3", | ||
| 465 | "nixpkgs": [ | ||
| 466 | "nixpkgs" | ||
| 467 | ], | ||
| 468 | "pre-commit-hooks-nix": "pre-commit-hooks-nix_3", | ||
| 469 | "rust-overlay": "rust-overlay" | ||
| 470 | }, | ||
| 471 | "locked": { | ||
| 472 | "lastModified": 1737639419, | ||
| 473 | "narHash": "sha256-AEEDktApTEZ5PZXNDkry2YV2k6t0dTgLPEmAZbnigXU=", | ||
| 474 | "owner": "nix-community", | ||
| 475 | "repo": "lanzaboote", | ||
| 476 | "rev": "a65905a09e2c43ff63be8c0e86a93712361f871e", | ||
| 477 | "type": "github" | ||
| 478 | }, | ||
| 479 | "original": { | ||
| 480 | "owner": "nix-community", | ||
| 481 | "ref": "v0.4.2", | ||
| 482 | "repo": "lanzaboote", | ||
| 483 | "type": "github" | ||
| 484 | } | ||
| 485 | }, | ||
| 376 | "leapseconds": { | 486 | "leapseconds": { |
| 377 | "flake": false, | 487 | "flake": false, |
| 378 | "locked": { | 488 | "locked": { |
| 379 | "narHash": "sha256-5ZaoY/bScQS7EGJRHu6vj9XWhbObmxNEaGugaGU7+lg=", | 489 | "narHash": "sha256-FJgbafPB48+5sT+7ZB8pajSsfJoISEOoaJ0d/2Ya7o8=", |
| 380 | "type": "file", | 490 | "type": "file", |
| 381 | "url": "https://data.iana.org/time-zones/tzdb/leap-seconds.list" | 491 | "url": "https://data.iana.org/time-zones/tzdb/leap-seconds.list" |
| 382 | }, | 492 | }, |
| @@ -385,6 +495,66 @@ | |||
| 385 | "url": "https://data.iana.org/time-zones/tzdb/leap-seconds.list" | 495 | "url": "https://data.iana.org/time-zones/tzdb/leap-seconds.list" |
| 386 | } | 496 | } |
| 387 | }, | 497 | }, |
| 498 | "niri-flake": { | ||
| 499 | "inputs": { | ||
| 500 | "niri-stable": "niri-stable", | ||
| 501 | "niri-unstable": "niri-unstable", | ||
| 502 | "nixpkgs": [ | ||
| 503 | "nixpkgs" | ||
| 504 | ], | ||
| 505 | "nixpkgs-stable": "nixpkgs-stable_3", | ||
| 506 | "xwayland-satellite-stable": "xwayland-satellite-stable", | ||
| 507 | "xwayland-satellite-unstable": "xwayland-satellite-unstable" | ||
| 508 | }, | ||
| 509 | "locked": { | ||
| 510 | "lastModified": 1757437545, | ||
| 511 | "narHash": "sha256-7ssbrFnmSrqtCtOySiu5ncyOBxPrR6p2nhNHrg6D+fo=", | ||
| 512 | "owner": "sodiboo", | ||
| 513 | "repo": "niri-flake", | ||
| 514 | "rev": "ef694b996daeeb8684c0adfaa9b7067a6e709054", | ||
| 515 | "type": "github" | ||
| 516 | }, | ||
| 517 | "original": { | ||
| 518 | "owner": "sodiboo", | ||
| 519 | "ref": "main", | ||
| 520 | "repo": "niri-flake", | ||
| 521 | "type": "github" | ||
| 522 | } | ||
| 523 | }, | ||
| 524 | "niri-stable": { | ||
| 525 | "flake": false, | ||
| 526 | "locked": { | ||
| 527 | "lastModified": 1756556321, | ||
| 528 | "narHash": "sha256-RLD89dfjN0RVO86C/Mot0T7aduCygPGaYbog566F0Qo=", | ||
| 529 | "owner": "YaLTeR", | ||
| 530 | "repo": "niri", | ||
| 531 | "rev": "01be0e65f4eb91a9cd624ac0b76aaeab765c7294", | ||
| 532 | "type": "github" | ||
| 533 | }, | ||
| 534 | "original": { | ||
| 535 | "owner": "YaLTeR", | ||
| 536 | "ref": "v25.08", | ||
| 537 | "repo": "niri", | ||
| 538 | "type": "github" | ||
| 539 | } | ||
| 540 | }, | ||
| 541 | "niri-unstable": { | ||
| 542 | "flake": false, | ||
| 543 | "locked": { | ||
| 544 | "lastModified": 1757671534, | ||
| 545 | "narHash": "sha256-7tfypHWNtR+wZS9K9XrvcUwyvZ3h8CxInQ2mVsjUU9A=", | ||
| 546 | "owner": "gkleen", | ||
| 547 | "repo": "niri", | ||
| 548 | "rev": "5e3611a3c5f8c819e5517d0b3f795f161579a0db", | ||
| 549 | "type": "github" | ||
| 550 | }, | ||
| 551 | "original": { | ||
| 552 | "owner": "gkleen", | ||
| 553 | "ref": "fix/locked-monitor-control", | ||
| 554 | "repo": "niri", | ||
| 555 | "type": "github" | ||
| 556 | } | ||
| 557 | }, | ||
| 388 | "nix-github-actions": { | 558 | "nix-github-actions": { |
| 389 | "inputs": { | 559 | "inputs": { |
| 390 | "nixpkgs": [ | 560 | "nixpkgs": [ |
| @@ -413,11 +583,11 @@ | |||
| 413 | ] | 583 | ] |
| 414 | }, | 584 | }, |
| 415 | "locked": { | 585 | "locked": { |
| 416 | "lastModified": 1733629314, | 586 | "lastModified": 1755404379, |
| 417 | "narHash": "sha256-U0vivjQFAwjNDYt49Krevs1murX9hKBFe2Ye0cHpgbU=", | 587 | "narHash": "sha256-Q6ZxZDBmD/B988Jjbx7/NchxOKIpOKBBrx9Yb0zMzpQ=", |
| 418 | "owner": "Mic92", | 588 | "owner": "Mic92", |
| 419 | "repo": "nix-index-database", | 589 | "repo": "nix-index-database", |
| 420 | "rev": "f1e477a7dd11e27e7f98b646349cd66bbabf2fb8", | 590 | "rev": "ebbc1c05f786ae39bb5e04e57bf2c10c44a649e3", |
| 421 | "type": "github" | 591 | "type": "github" |
| 422 | }, | 592 | }, |
| 423 | "original": { | 593 | "original": { |
| @@ -427,6 +597,27 @@ | |||
| 427 | "type": "github" | 597 | "type": "github" |
| 428 | } | 598 | } |
| 429 | }, | 599 | }, |
| 600 | "nix-monitored": { | ||
| 601 | "inputs": { | ||
| 602 | "nixpkgs": [ | ||
| 603 | "nixpkgs" | ||
| 604 | ] | ||
| 605 | }, | ||
| 606 | "locked": { | ||
| 607 | "lastModified": 1745680380, | ||
| 608 | "narHash": "sha256-Z8PknjkmIr/8ZCH+dmc2Pc+UltiOr7/oKg37PXuVvuU=", | ||
| 609 | "owner": "ners", | ||
| 610 | "repo": "nix-monitored", | ||
| 611 | "rev": "60f3baa4701d58eab86c2d1d9c3d7e820074d461", | ||
| 612 | "type": "github" | ||
| 613 | }, | ||
| 614 | "original": { | ||
| 615 | "owner": "ners", | ||
| 616 | "ref": "master", | ||
| 617 | "repo": "nix-monitored", | ||
| 618 | "type": "github" | ||
| 619 | } | ||
| 620 | }, | ||
| 430 | "nixVirt": { | 621 | "nixVirt": { |
| 431 | "inputs": { | 622 | "inputs": { |
| 432 | "nixpkgs": [ | 623 | "nixpkgs": [ |
| @@ -434,11 +625,11 @@ | |||
| 434 | ] | 625 | ] |
| 435 | }, | 626 | }, |
| 436 | "locked": { | 627 | "locked": { |
| 437 | "lastModified": 1732406038, | 628 | "lastModified": 1748140003, |
| 438 | "narHash": "sha256-BYNBN+Rtc/SX6qI7m3nmryufRPn0ZYd40yHDo9VQaNE=", | 629 | "narHash": "sha256-DNBZmuk1YRM2PmwbHzVdXumRjCUzQkMarg4iI/37rOQ=", |
| 439 | "owner": "AshleyYakeley", | 630 | "owner": "AshleyYakeley", |
| 440 | "repo": "NixVirt", | 631 | "repo": "NixVirt", |
| 441 | "rev": "fe3aaa86d4458e4f84348941297f7ba82e2a9f67", | 632 | "rev": "5dfe108fd859b122f9a96981cb6bc12297653d6c", |
| 442 | "type": "github" | 633 | "type": "github" |
| 443 | }, | 634 | }, |
| 444 | "original": { | 635 | "original": { |
| @@ -449,11 +640,11 @@ | |||
| 449 | }, | 640 | }, |
| 450 | "nixos-hardware": { | 641 | "nixos-hardware": { |
| 451 | "locked": { | 642 | "locked": { |
| 452 | "lastModified": 1733861262, | 643 | "lastModified": 1755330281, |
| 453 | "narHash": "sha256-+jjPup/ByS0LEVIrBbt7FnGugJgLeG9oc+ivFASYn2U=", | 644 | "narHash": "sha256-aJHFJWP9AuI8jUGzI77LYcSlkA9wJnOIg4ZqftwNGXA=", |
| 454 | "owner": "NixOS", | 645 | "owner": "NixOS", |
| 455 | "repo": "nixos-hardware", | 646 | "repo": "nixos-hardware", |
| 456 | "rev": "cf737e2eba82b603f54f71b10cb8fd09d22ce3f5", | 647 | "rev": "3dac8a872557e0ca8c083cdcfc2f218d18e113b0", |
| 457 | "type": "github" | 648 | "type": "github" |
| 458 | }, | 649 | }, |
| 459 | "original": { | 650 | "original": { |
| @@ -481,16 +672,16 @@ | |||
| 481 | }, | 672 | }, |
| 482 | "nixpkgs-eostre": { | 673 | "nixpkgs-eostre": { |
| 483 | "locked": { | 674 | "locked": { |
| 484 | "lastModified": 1701282334, | 675 | "lastModified": 1748026580, |
| 485 | "narHash": "sha256-MxCVrXY6v4QmfTwIysjjaX0XUhqBbxTWWB4HXtDYsdk=", | 676 | "narHash": "sha256-rWtXrcIzU5wm/C8F9LWvUfBGu5U5E7cFzPYT1pHIJaQ=", |
| 486 | "owner": "NixOS", | 677 | "owner": "NixOS", |
| 487 | "repo": "nixpkgs", | 678 | "repo": "nixpkgs", |
| 488 | "rev": "057f9aecfb71c4437d2b27d3323df7f93c010b7e", | 679 | "rev": "11cb3517b3af6af300dd6c055aeda73c9bf52c48", |
| 489 | "type": "github" | 680 | "type": "github" |
| 490 | }, | 681 | }, |
| 491 | "original": { | 682 | "original": { |
| 492 | "owner": "NixOS", | 683 | "owner": "NixOS", |
| 493 | "ref": "23.11", | 684 | "ref": "25.05", |
| 494 | "repo": "nixpkgs", | 685 | "repo": "nixpkgs", |
| 495 | "type": "github" | 686 | "type": "github" |
| 496 | } | 687 | } |
| @@ -509,14 +700,17 @@ | |||
| 509 | }, | 700 | }, |
| 510 | "nixpkgs-lib_2": { | 701 | "nixpkgs-lib_2": { |
| 511 | "locked": { | 702 | "locked": { |
| 512 | "lastModified": 1725233747, | 703 | "lastModified": 1748740939, |
| 513 | "narHash": "sha256-Ss8QWLXdr2JCBPcYChJhz4xJm+h/xjl4G0c0XlP6a74=", | 704 | "narHash": "sha256-rQaysilft1aVMwF14xIdGS3sj1yHlI6oKQNBRTF40cc=", |
| 514 | "type": "tarball", | 705 | "owner": "nix-community", |
| 515 | "url": "https://github.com/NixOS/nixpkgs/archive/356624c12086a18f2ea2825fed34523d60ccc4e3.tar.gz" | 706 | "repo": "nixpkgs.lib", |
| 707 | "rev": "656a64127e9d791a334452c6b6606d17539476e2", | ||
| 708 | "type": "github" | ||
| 516 | }, | 709 | }, |
| 517 | "original": { | 710 | "original": { |
| 518 | "type": "tarball", | 711 | "owner": "nix-community", |
| 519 | "url": "https://github.com/NixOS/nixpkgs/archive/356624c12086a18f2ea2825fed34523d60ccc4e3.tar.gz" | 712 | "repo": "nixpkgs.lib", |
| 713 | "type": "github" | ||
| 520 | } | 714 | } |
| 521 | }, | 715 | }, |
| 522 | "nixpkgs-lib_3": { | 716 | "nixpkgs-lib_3": { |
| @@ -571,22 +765,54 @@ | |||
| 571 | }, | 765 | }, |
| 572 | "nixpkgs-stable_2": { | 766 | "nixpkgs-stable_2": { |
| 573 | "locked": { | 767 | "locked": { |
| 574 | "lastModified": 1717179513, | 768 | "lastModified": 1730741070, |
| 575 | "narHash": "sha256-vboIEwIQojofItm2xGCdZCzW96U85l9nDW3ifMuAIdM=", | 769 | "narHash": "sha256-edm8WG19kWozJ/GqyYx2VjW99EdhjKwbY3ZwdlPAAlo=", |
| 576 | "owner": "NixOS", | 770 | "owner": "NixOS", |
| 577 | "repo": "nixpkgs", | 771 | "repo": "nixpkgs", |
| 578 | "rev": "63dacb46bf939521bdc93981b4cbb7ecb58427a0", | 772 | "rev": "d063c1dd113c91ab27959ba540c0d9753409edf3", |
| 579 | "type": "github" | 773 | "type": "github" |
| 580 | }, | 774 | }, |
| 581 | "original": { | 775 | "original": { |
| 582 | "owner": "NixOS", | 776 | "owner": "NixOS", |
| 583 | "ref": "24.05", | 777 | "ref": "nixos-24.05", |
| 584 | "repo": "nixpkgs", | 778 | "repo": "nixpkgs", |
| 585 | "type": "github" | 779 | "type": "github" |
| 586 | } | 780 | } |
| 587 | }, | 781 | }, |
| 588 | "nixpkgs-stable_3": { | 782 | "nixpkgs-stable_3": { |
| 589 | "locked": { | 783 | "locked": { |
| 784 | "lastModified": 1757408970, | ||
| 785 | "narHash": "sha256-aSgK4BLNFFGvDTNKPeB28lVXYqVn8RdyXDNAvgGq+k0=", | ||
| 786 | "owner": "NixOS", | ||
| 787 | "repo": "nixpkgs", | ||
| 788 | "rev": "d179d77c139e0a3f5c416477f7747e9d6b7ec315", | ||
| 789 | "type": "github" | ||
| 790 | }, | ||
| 791 | "original": { | ||
| 792 | "owner": "NixOS", | ||
| 793 | "ref": "nixos-25.05", | ||
| 794 | "repo": "nixpkgs", | ||
| 795 | "type": "github" | ||
| 796 | } | ||
| 797 | }, | ||
| 798 | "nixpkgs-stable_4": { | ||
| 799 | "locked": { | ||
| 800 | "lastModified": 1748026580, | ||
| 801 | "narHash": "sha256-rWtXrcIzU5wm/C8F9LWvUfBGu5U5E7cFzPYT1pHIJaQ=", | ||
| 802 | "owner": "NixOS", | ||
| 803 | "repo": "nixpkgs", | ||
| 804 | "rev": "11cb3517b3af6af300dd6c055aeda73c9bf52c48", | ||
| 805 | "type": "github" | ||
| 806 | }, | ||
| 807 | "original": { | ||
| 808 | "owner": "NixOS", | ||
| 809 | "ref": "25.05", | ||
| 810 | "repo": "nixpkgs", | ||
| 811 | "type": "github" | ||
| 812 | } | ||
| 813 | }, | ||
| 814 | "nixpkgs-stable_5": { | ||
| 815 | "locked": { | ||
| 590 | "lastModified": 1678872516, | 816 | "lastModified": 1678872516, |
| 591 | "narHash": "sha256-/E1YwtMtFAu2KUQKV/1+KFuReYPANM2Rzehk84VxVoc=", | 817 | "narHash": "sha256-/E1YwtMtFAu2KUQKV/1+KFuReYPANM2Rzehk84VxVoc=", |
| 592 | "owner": "NixOS", | 818 | "owner": "NixOS", |
| @@ -603,11 +829,11 @@ | |||
| 603 | }, | 829 | }, |
| 604 | "nixpkgs_2": { | 830 | "nixpkgs_2": { |
| 605 | "locked": { | 831 | "locked": { |
| 606 | "lastModified": 1733759999, | 832 | "lastModified": 1755615617, |
| 607 | "narHash": "sha256-463SNPWmz46iLzJKRzO3Q2b0Aurff3U1n0nYItxq7jU=", | 833 | "narHash": "sha256-HMwfAJBdrr8wXAkbGhtcby1zGFvs+StOp19xNsbqdOg=", |
| 608 | "owner": "NixOS", | 834 | "owner": "NixOS", |
| 609 | "repo": "nixpkgs", | 835 | "repo": "nixpkgs", |
| 610 | "rev": "a73246e2eef4c6ed172979932bc80e1404ba2d56", | 836 | "rev": "20075955deac2583bb12f07151c2df830ef346b4", |
| 611 | "type": "github" | 837 | "type": "github" |
| 612 | }, | 838 | }, |
| 613 | "original": { | 839 | "original": { |
| @@ -673,11 +899,11 @@ | |||
| 673 | "treefmt-nix": "treefmt-nix" | 899 | "treefmt-nix": "treefmt-nix" |
| 674 | }, | 900 | }, |
| 675 | "locked": { | 901 | "locked": { |
| 676 | "lastModified": 1731205797, | 902 | "lastModified": 1743690424, |
| 677 | "narHash": "sha256-F7N1mxH1VrkVNHR3JGNMRvp9+98KYO4b832KS8Gl2xI=", | 903 | "narHash": "sha256-cX98bUuKuihOaRp8dNV1Mq7u6/CQZWTPth2IJPATBXc=", |
| 678 | "owner": "nix-community", | 904 | "owner": "nix-community", |
| 679 | "repo": "poetry2nix", | 905 | "repo": "poetry2nix", |
| 680 | "rev": "f554d27c1544d9c56e5f1f8e2b8aff399803674e", | 906 | "rev": "ce2369db77f45688172384bbeb962bc6c2ea6f94", |
| 681 | "type": "github" | 907 | "type": "github" |
| 682 | }, | 908 | }, |
| 683 | "original": { | 909 | "original": { |
| @@ -715,18 +941,14 @@ | |||
| 715 | "nixpkgs": [ | 941 | "nixpkgs": [ |
| 716 | "ca-util", | 942 | "ca-util", |
| 717 | "nixpkgs" | 943 | "nixpkgs" |
| 718 | ], | ||
| 719 | "nixpkgs-stable": [ | ||
| 720 | "ca-util", | ||
| 721 | "nixpkgs" | ||
| 722 | ] | 944 | ] |
| 723 | }, | 945 | }, |
| 724 | "locked": { | 946 | "locked": { |
| 725 | "lastModified": 1726745158, | 947 | "lastModified": 1749636823, |
| 726 | "narHash": "sha256-D5AegvGoEjt4rkKedmxlSEmC+nNLMBPWFxvmYnVLhjk=", | 948 | "narHash": "sha256-WUaIlOlPLyPgz9be7fqWJA5iG6rHcGRtLERSCfUDne4=", |
| 727 | "owner": "cachix", | 949 | "owner": "cachix", |
| 728 | "repo": "pre-commit-hooks.nix", | 950 | "repo": "pre-commit-hooks.nix", |
| 729 | "rev": "4e743a6920eab45e8ba0fbe49dc459f1423a4b74", | 951 | "rev": "623c56286de5a3193aa38891a6991b28f9bab056", |
| 730 | "type": "github" | 952 | "type": "github" |
| 731 | }, | 953 | }, |
| 732 | "original": { | 954 | "original": { |
| @@ -737,11 +959,38 @@ | |||
| 737 | }, | 959 | }, |
| 738 | "pre-commit-hooks-nix_3": { | 960 | "pre-commit-hooks-nix_3": { |
| 739 | "inputs": { | 961 | "inputs": { |
| 740 | "flake-compat": "flake-compat_4", | 962 | "flake-compat": [ |
| 741 | "flake-utils": "flake-utils_2", | 963 | "lanzaboote", |
| 964 | "flake-compat" | ||
| 965 | ], | ||
| 742 | "gitignore": "gitignore_3", | 966 | "gitignore": "gitignore_3", |
| 967 | "nixpkgs": [ | ||
| 968 | "lanzaboote", | ||
| 969 | "nixpkgs" | ||
| 970 | ], | ||
| 971 | "nixpkgs-stable": "nixpkgs-stable_2" | ||
| 972 | }, | ||
| 973 | "locked": { | ||
| 974 | "lastModified": 1731363552, | ||
| 975 | "narHash": "sha256-vFta1uHnD29VUY4HJOO/D6p6rxyObnf+InnSMT4jlMU=", | ||
| 976 | "owner": "cachix", | ||
| 977 | "repo": "pre-commit-hooks.nix", | ||
| 978 | "rev": "cd1af27aa85026ac759d5d3fccf650abe7e1bbf0", | ||
| 979 | "type": "github" | ||
| 980 | }, | ||
| 981 | "original": { | ||
| 982 | "owner": "cachix", | ||
| 983 | "repo": "pre-commit-hooks.nix", | ||
| 984 | "type": "github" | ||
| 985 | } | ||
| 986 | }, | ||
| 987 | "pre-commit-hooks-nix_4": { | ||
| 988 | "inputs": { | ||
| 989 | "flake-compat": "flake-compat_5", | ||
| 990 | "flake-utils": "flake-utils_2", | ||
| 991 | "gitignore": "gitignore_4", | ||
| 743 | "nixpkgs": "nixpkgs_3", | 992 | "nixpkgs": "nixpkgs_3", |
| 744 | "nixpkgs-stable": "nixpkgs-stable_3" | 993 | "nixpkgs-stable": "nixpkgs-stable_5" |
| 745 | }, | 994 | }, |
| 746 | "locked": { | 995 | "locked": { |
| 747 | "lastModified": 1685361114, | 996 | "lastModified": 1685361114, |
| @@ -759,14 +1008,14 @@ | |||
| 759 | }, | 1008 | }, |
| 760 | "prometheus-borg-exporter": { | 1009 | "prometheus-borg-exporter": { |
| 761 | "inputs": { | 1010 | "inputs": { |
| 762 | "flake-parts": "flake-parts_3", | 1011 | "flake-parts": "flake-parts_4", |
| 763 | "nixpkgs": [ | 1012 | "nixpkgs": [ |
| 764 | "nixpkgs" | 1013 | "nixpkgs" |
| 765 | ], | 1014 | ], |
| 766 | "poetry2nix": [ | 1015 | "poetry2nix": [ |
| 767 | "poetry2nix" | 1016 | "poetry2nix" |
| 768 | ], | 1017 | ], |
| 769 | "pre-commit-hooks-nix": "pre-commit-hooks-nix_3" | 1018 | "pre-commit-hooks-nix": "pre-commit-hooks-nix_4" |
| 770 | }, | 1019 | }, |
| 771 | "locked": { | 1020 | "locked": { |
| 772 | "lastModified": 1722088088, | 1021 | "lastModified": 1722088088, |
| @@ -783,6 +1032,81 @@ | |||
| 783 | "type": "gitlab" | 1032 | "type": "gitlab" |
| 784 | } | 1033 | } |
| 785 | }, | 1034 | }, |
| 1035 | "pyproject-build-systems": { | ||
| 1036 | "inputs": { | ||
| 1037 | "nixpkgs": [ | ||
| 1038 | "ca-util", | ||
| 1039 | "nixpkgs" | ||
| 1040 | ], | ||
| 1041 | "pyproject-nix": [ | ||
| 1042 | "ca-util", | ||
| 1043 | "pyproject-nix" | ||
| 1044 | ], | ||
| 1045 | "uv2nix": [ | ||
| 1046 | "ca-util", | ||
| 1047 | "uv2nix" | ||
| 1048 | ] | ||
| 1049 | }, | ||
| 1050 | "locked": { | ||
| 1051 | "lastModified": 1749519371, | ||
| 1052 | "narHash": "sha256-UJONN7mA2stweZCoRcry2aa1XTTBL0AfUOY84Lmqhos=", | ||
| 1053 | "owner": "pyproject-nix", | ||
| 1054 | "repo": "build-system-pkgs", | ||
| 1055 | "rev": "7c06967eca687f3482624250428cc12f43c92523", | ||
| 1056 | "type": "github" | ||
| 1057 | }, | ||
| 1058 | "original": { | ||
| 1059 | "owner": "pyproject-nix", | ||
| 1060 | "repo": "build-system-pkgs", | ||
| 1061 | "type": "github" | ||
| 1062 | } | ||
| 1063 | }, | ||
| 1064 | "pyproject-build-systems_2": { | ||
| 1065 | "inputs": { | ||
| 1066 | "nixpkgs": [ | ||
| 1067 | "nixpkgs" | ||
| 1068 | ], | ||
| 1069 | "pyproject-nix": [ | ||
| 1070 | "pyproject-nix" | ||
| 1071 | ], | ||
| 1072 | "uv2nix": [ | ||
| 1073 | "uv2nix" | ||
| 1074 | ] | ||
| 1075 | }, | ||
| 1076 | "locked": { | ||
| 1077 | "lastModified": 1755484659, | ||
| 1078 | "narHash": "sha256-2FfbqsaHVQd12XFFUAinIMAuGO3853LONmva1gT3vKw=", | ||
| 1079 | "owner": "pyproject-nix", | ||
| 1080 | "repo": "build-system-pkgs", | ||
| 1081 | "rev": "9778e87c2361810ff15e287ca5895c9da4a0e900", | ||
| 1082 | "type": "github" | ||
| 1083 | }, | ||
| 1084 | "original": { | ||
| 1085 | "owner": "pyproject-nix", | ||
| 1086 | "repo": "build-system-pkgs", | ||
| 1087 | "type": "github" | ||
| 1088 | } | ||
| 1089 | }, | ||
| 1090 | "pyproject-nix": { | ||
| 1091 | "inputs": { | ||
| 1092 | "nixpkgs": [ | ||
| 1093 | "nixpkgs" | ||
| 1094 | ] | ||
| 1095 | }, | ||
| 1096 | "locked": { | ||
| 1097 | "lastModified": 1754923840, | ||
| 1098 | "narHash": "sha256-QSKpYg+Ts9HYF155ltlj40iBex39c05cpOF8gjoE2EM=", | ||
| 1099 | "owner": "pyproject-nix", | ||
| 1100 | "repo": "pyproject.nix", | ||
| 1101 | "rev": "023cd4be230eacae52635be09eef100c37ef78da", | ||
| 1102 | "type": "github" | ||
| 1103 | }, | ||
| 1104 | "original": { | ||
| 1105 | "owner": "pyproject-nix", | ||
| 1106 | "repo": "pyproject.nix", | ||
| 1107 | "type": "github" | ||
| 1108 | } | ||
| 1109 | }, | ||
| 786 | "root": { | 1110 | "root": { |
| 787 | "inputs": { | 1111 | "inputs": { |
| 788 | "backup-utils": "backup-utils", | 1112 | "backup-utils": "backup-utils", |
| @@ -794,20 +1118,47 @@ | |||
| 794 | "home-manager": "home-manager", | 1118 | "home-manager": "home-manager", |
| 795 | "home-manager-eostre": "home-manager-eostre", | 1119 | "home-manager-eostre": "home-manager-eostre", |
| 796 | "impermanence": "impermanence", | 1120 | "impermanence": "impermanence", |
| 1121 | "lanzaboote": "lanzaboote", | ||
| 1122 | "niri-flake": "niri-flake", | ||
| 797 | "nix-index-database": "nix-index-database", | 1123 | "nix-index-database": "nix-index-database", |
| 1124 | "nix-monitored": "nix-monitored", | ||
| 798 | "nixVirt": "nixVirt", | 1125 | "nixVirt": "nixVirt", |
| 799 | "nixos-hardware": "nixos-hardware", | 1126 | "nixos-hardware": "nixos-hardware", |
| 800 | "nixpkgs": "nixpkgs_2", | 1127 | "nixpkgs": "nixpkgs_2", |
| 801 | "nixpkgs-eostre": "nixpkgs-eostre", | 1128 | "nixpkgs-eostre": "nixpkgs-eostre", |
| 802 | "nixpkgs-pgbackrest": "nixpkgs-pgbackrest", | 1129 | "nixpkgs-pgbackrest": "nixpkgs-pgbackrest", |
| 803 | "nixpkgs-stable": "nixpkgs-stable_2", | 1130 | "nixpkgs-stable": "nixpkgs-stable_4", |
| 804 | "nvfetcher": "nvfetcher", | 1131 | "nvfetcher": "nvfetcher", |
| 805 | "poetry2nix": "poetry2nix", | 1132 | "poetry2nix": "poetry2nix", |
| 806 | "prometheus-borg-exporter": "prometheus-borg-exporter", | 1133 | "prometheus-borg-exporter": "prometheus-borg-exporter", |
| 1134 | "pyproject-build-systems": "pyproject-build-systems_2", | ||
| 1135 | "pyproject-nix": "pyproject-nix", | ||
| 807 | "sops-nix": "sops-nix", | 1136 | "sops-nix": "sops-nix", |
| 1137 | "uv2nix": "uv2nix", | ||
| 808 | "waybar": "waybar" | 1138 | "waybar": "waybar" |
| 809 | } | 1139 | } |
| 810 | }, | 1140 | }, |
| 1141 | "rust-overlay": { | ||
| 1142 | "inputs": { | ||
| 1143 | "nixpkgs": [ | ||
| 1144 | "lanzaboote", | ||
| 1145 | "nixpkgs" | ||
| 1146 | ] | ||
| 1147 | }, | ||
| 1148 | "locked": { | ||
| 1149 | "lastModified": 1731897198, | ||
| 1150 | "narHash": "sha256-Ou7vLETSKwmE/HRQz4cImXXJBr/k9gp4J4z/PF8LzTE=", | ||
| 1151 | "owner": "oxalica", | ||
| 1152 | "repo": "rust-overlay", | ||
| 1153 | "rev": "0be641045af6d8666c11c2c40e45ffc9667839b5", | ||
| 1154 | "type": "github" | ||
| 1155 | }, | ||
| 1156 | "original": { | ||
| 1157 | "owner": "oxalica", | ||
| 1158 | "repo": "rust-overlay", | ||
| 1159 | "type": "github" | ||
| 1160 | } | ||
| 1161 | }, | ||
| 811 | "sops-nix": { | 1162 | "sops-nix": { |
| 812 | "inputs": { | 1163 | "inputs": { |
| 813 | "nixpkgs": [ | 1164 | "nixpkgs": [ |
| @@ -815,11 +1166,11 @@ | |||
| 815 | ] | 1166 | ] |
| 816 | }, | 1167 | }, |
| 817 | "locked": { | 1168 | "locked": { |
| 818 | "lastModified": 1733965552, | 1169 | "lastModified": 1754988908, |
| 819 | "narHash": "sha256-GZ4YtqkfyTjJFVCub5yAFWsHknG1nS/zfk7MuHht4Fs=", | 1170 | "narHash": "sha256-t+voe2961vCgrzPFtZxha0/kmFSHFobzF00sT8p9h0U=", |
| 820 | "owner": "Mic92", | 1171 | "owner": "Mic92", |
| 821 | "repo": "sops-nix", | 1172 | "repo": "sops-nix", |
| 822 | "rev": "2d73fc6ac4eba4b9a83d3cb8275096fbb7ab4004", | 1173 | "rev": "3223c7a92724b5d804e9988c6b447a0d09017d48", |
| 823 | "type": "github" | 1174 | "type": "github" |
| 824 | }, | 1175 | }, |
| 825 | "original": { | 1176 | "original": { |
| @@ -854,8 +1205,9 @@ | |||
| 854 | "type": "github" | 1205 | "type": "github" |
| 855 | }, | 1206 | }, |
| 856 | "original": { | 1207 | "original": { |
| 857 | "id": "systems", | 1208 | "owner": "nix-systems", |
| 858 | "type": "indirect" | 1209 | "repo": "default", |
| 1210 | "type": "github" | ||
| 859 | } | 1211 | } |
| 860 | }, | 1212 | }, |
| 861 | "treefmt-nix": { | 1213 | "treefmt-nix": { |
| @@ -879,6 +1231,29 @@ | |||
| 879 | "type": "github" | 1231 | "type": "github" |
| 880 | } | 1232 | } |
| 881 | }, | 1233 | }, |
| 1234 | "uv2nix": { | ||
| 1235 | "inputs": { | ||
| 1236 | "nixpkgs": [ | ||
| 1237 | "nixpkgs" | ||
| 1238 | ], | ||
| 1239 | "pyproject-nix": [ | ||
| 1240 | "pyproject-nix" | ||
| 1241 | ] | ||
| 1242 | }, | ||
| 1243 | "locked": { | ||
| 1244 | "lastModified": 1755485731, | ||
| 1245 | "narHash": "sha256-k8kxwVs8Oze6q/jAaRa3RvZbb50I/K0b5uptlsh0HXI=", | ||
| 1246 | "owner": "pyproject-nix", | ||
| 1247 | "repo": "uv2nix", | ||
| 1248 | "rev": "bebbd80bf56110fcd20b425589814af28f1939eb", | ||
| 1249 | "type": "github" | ||
| 1250 | }, | ||
| 1251 | "original": { | ||
| 1252 | "owner": "pyproject-nix", | ||
| 1253 | "repo": "uv2nix", | ||
| 1254 | "type": "github" | ||
| 1255 | } | ||
| 1256 | }, | ||
| 882 | "waybar": { | 1257 | "waybar": { |
| 883 | "inputs": { | 1258 | "inputs": { |
| 884 | "flake-compat": [ | 1259 | "flake-compat": [ |
| @@ -889,19 +1264,52 @@ | |||
| 889 | ] | 1264 | ] |
| 890 | }, | 1265 | }, |
| 891 | "locked": { | 1266 | "locked": { |
| 892 | "lastModified": 1734278650, | 1267 | "lastModified": 1752562190, |
| 893 | "narHash": "sha256-z9FiyHDbKC2nwfd/qsHCxLBEogzQj73zo85lW3zIlzY=", | 1268 | "narHash": "sha256-zWOMCNe56H2PHUd3rJZ6tklZUZBLgRo85jd9IlK1g9o=", |
| 894 | "owner": "gkleen", | 1269 | "owner": "gkleen", |
| 895 | "repo": "Waybar", | 1270 | "repo": "Waybar", |
| 896 | "rev": "5432f9c1697a8d2d3e1264a5ce820d7eac26e2c6", | 1271 | "rev": "d008cd998369c40f2344a856caf39cdbbd7bd068", |
| 897 | "type": "github" | 1272 | "type": "github" |
| 898 | }, | 1273 | }, |
| 899 | "original": { | 1274 | "original": { |
| 900 | "owner": "gkleen", | 1275 | "owner": "gkleen", |
| 901 | "ref": "feat/privacy-ignore", | 1276 | "ref": "feat/niri-urgency", |
| 902 | "repo": "Waybar", | 1277 | "repo": "Waybar", |
| 903 | "type": "github" | 1278 | "type": "github" |
| 904 | } | 1279 | } |
| 1280 | }, | ||
| 1281 | "xwayland-satellite-stable": { | ||
| 1282 | "flake": false, | ||
| 1283 | "locked": { | ||
| 1284 | "lastModified": 1755491097, | ||
| 1285 | "narHash": "sha256-m+9tUfsmBeF2Gn4HWa6vSITZ4Gz1eA1F5Kh62B0N4oE=", | ||
| 1286 | "owner": "Supreeeme", | ||
| 1287 | "repo": "xwayland-satellite", | ||
| 1288 | "rev": "388d291e82ffbc73be18169d39470f340707edaa", | ||
| 1289 | "type": "github" | ||
| 1290 | }, | ||
| 1291 | "original": { | ||
| 1292 | "owner": "Supreeeme", | ||
| 1293 | "ref": "v0.7", | ||
| 1294 | "repo": "xwayland-satellite", | ||
| 1295 | "type": "github" | ||
| 1296 | } | ||
| 1297 | }, | ||
| 1298 | "xwayland-satellite-unstable": { | ||
| 1299 | "flake": false, | ||
| 1300 | "locked": { | ||
| 1301 | "lastModified": 1757179758, | ||
| 1302 | "narHash": "sha256-TIvyWzRt1miQj6Cf5Wy8Qz43XIZX7c4vTVwRLAT5S4Y=", | ||
| 1303 | "owner": "Supreeeme", | ||
| 1304 | "repo": "xwayland-satellite", | ||
| 1305 | "rev": "970728d0d9d1eada342bb8860af214b601139e58", | ||
| 1306 | "type": "github" | ||
| 1307 | }, | ||
| 1308 | "original": { | ||
| 1309 | "owner": "Supreeeme", | ||
| 1310 | "repo": "xwayland-satellite", | ||
| 1311 | "type": "github" | ||
| 1312 | } | ||
| 905 | } | 1313 | } |
| 906 | }, | 1314 | }, |
| 907 | "root": "root", | 1315 | "root": "root", |
| @@ -4,9 +4,11 @@ | |||
| 4 | nixConfig = { | 4 | nixConfig = { |
| 5 | extra-substituters = [ | 5 | extra-substituters = [ |
| 6 | "https://nix-community.cachix.org" | 6 | "https://nix-community.cachix.org" |
| 7 | "https://niri.cachix.org" | ||
| 7 | ]; | 8 | ]; |
| 8 | extra-trusted-public-keys = [ | 9 | extra-trusted-public-keys = [ |
| 9 | "nix-community.cachix.org-1:mB9FSh9qf2dCimDSUo8Zy7bkq5CX+/rkCWyvRCYg3Fs=" | 10 | "nix-community.cachix.org-1:mB9FSh9qf2dCimDSUo8Zy7bkq5CX+/rkCWyvRCYg3Fs=" |
| 11 | "niri.cachix.org-1:Wv0OmO7PsuocRKzfDoJ3mulSl7Z6oezYhGhR+3W2964=" | ||
| 10 | ]; | 12 | ]; |
| 11 | }; | 13 | }; |
| 12 | 14 | ||
| @@ -27,13 +29,13 @@ | |||
| 27 | type = "github"; | 29 | type = "github"; |
| 28 | owner = "NixOS"; | 30 | owner = "NixOS"; |
| 29 | repo = "nixpkgs"; | 31 | repo = "nixpkgs"; |
| 30 | ref = "24.05"; | 32 | ref = "25.05"; |
| 31 | }; | 33 | }; |
| 32 | nixpkgs-eostre = { | 34 | nixpkgs-eostre = { |
| 33 | type = "github"; | 35 | type = "github"; |
| 34 | owner = "NixOS"; | 36 | owner = "NixOS"; |
| 35 | repo = "nixpkgs"; | 37 | repo = "nixpkgs"; |
| 36 | ref = "23.11"; | 38 | ref = "25.05"; |
| 37 | }; | 39 | }; |
| 38 | home-manager = { | 40 | home-manager = { |
| 39 | type = "github"; | 41 | type = "github"; |
| @@ -51,7 +53,7 @@ | |||
| 51 | type = "github"; | 53 | type = "github"; |
| 52 | owner = "gkleen"; | 54 | owner = "gkleen"; |
| 53 | repo = "home-manager"; | 55 | repo = "home-manager"; |
| 54 | ref = "nixos-late-start-23.11"; | 56 | ref = "nixos-late-start-25.05"; |
| 55 | inputs = { | 57 | inputs = { |
| 56 | nixpkgs.follows = "nixpkgs-eostre"; | 58 | nixpkgs.follows = "nixpkgs-eostre"; |
| 57 | }; | 59 | }; |
| @@ -123,25 +125,43 @@ | |||
| 123 | nixpkgs.follows = "nixpkgs"; | 125 | nixpkgs.follows = "nixpkgs"; |
| 124 | }; | 126 | }; |
| 125 | }; | 127 | }; |
| 128 | pyproject-nix = { | ||
| 129 | url = "github:pyproject-nix/pyproject.nix"; | ||
| 130 | inputs.nixpkgs.follows = "nixpkgs"; | ||
| 131 | }; | ||
| 132 | uv2nix = { | ||
| 133 | url = "github:pyproject-nix/uv2nix"; | ||
| 134 | inputs.pyproject-nix.follows = "pyproject-nix"; | ||
| 135 | inputs.nixpkgs.follows = "nixpkgs"; | ||
| 136 | }; | ||
| 137 | pyproject-build-systems = { | ||
| 138 | url = "github:pyproject-nix/build-system-pkgs"; | ||
| 139 | inputs.pyproject-nix.follows = "pyproject-nix"; | ||
| 140 | inputs.uv2nix.follows = "uv2nix"; | ||
| 141 | inputs.nixpkgs.follows = "nixpkgs"; | ||
| 142 | }; | ||
| 126 | 143 | ||
| 127 | ca-util = { | 144 | ca-util = { |
| 128 | type = "gitlab"; | 145 | type = "gitlab"; |
| 129 | owner = "gkleen"; | 146 | owner = "gkleen"; |
| 130 | repo = "ca"; | 147 | repo = "ca"; |
| 131 | ref = "v3.1.3"; | 148 | ref = "v3.1.5"; |
| 132 | inputs = { | 149 | inputs = { |
| 150 | pyproject-nix.follows = "pyproject-nix"; | ||
| 151 | uv2nix.follows = "uv2nix"; | ||
| 133 | nixpkgs.follows = "nixpkgs"; | 152 | nixpkgs.follows = "nixpkgs"; |
| 134 | poetry2nix.follows = "poetry2nix"; | ||
| 135 | }; | 153 | }; |
| 136 | }; | 154 | }; |
| 137 | backup-utils = { | 155 | backup-utils = { |
| 138 | type = "gitlab"; | 156 | type = "gitlab"; |
| 139 | owner = "gkleen"; | 157 | owner = "gkleen"; |
| 140 | repo = "backup-utils"; | 158 | repo = "backup-utils"; |
| 141 | ref = "v0.1.6"; | 159 | ref = "v0.1.7"; |
| 142 | inputs = { | 160 | inputs = { |
| 143 | nixpkgs.follows = "nixpkgs"; | 161 | nixpkgs.follows = "nixpkgs"; |
| 144 | poetry2nix.follows = "poetry2nix"; | 162 | pyproject-nix.follows = "pyproject-nix"; |
| 163 | uv2nix.follows = "uv2nix"; | ||
| 164 | pyproject-build-systems.follows = "pyproject-build-systems"; | ||
| 145 | }; | 165 | }; |
| 146 | }; | 166 | }; |
| 147 | prometheus-borg-exporter = { | 167 | prometheus-borg-exporter = { |
| @@ -170,7 +190,7 @@ | |||
| 170 | type = "github"; | 190 | type = "github"; |
| 171 | owner = "gkleen"; | 191 | owner = "gkleen"; |
| 172 | repo = "Waybar"; | 192 | repo = "Waybar"; |
| 173 | ref = "feat/privacy-ignore"; | 193 | ref = "feat/niri-urgency"; |
| 174 | inputs = { | 194 | inputs = { |
| 175 | nixpkgs.follows = "nixpkgs"; | 195 | nixpkgs.follows = "nixpkgs"; |
| 176 | flake-compat.follows = "flake-compat"; | 196 | flake-compat.follows = "flake-compat"; |
| @@ -182,9 +202,41 @@ | |||
| 182 | repo = "NixVirt"; | 202 | repo = "NixVirt"; |
| 183 | inputs.nixpkgs.follows = "nixpkgs"; | 203 | inputs.nixpkgs.follows = "nixpkgs"; |
| 184 | }; | 204 | }; |
| 205 | niri-flake = { | ||
| 206 | type = "github"; | ||
| 207 | owner = "sodiboo"; | ||
| 208 | repo = "niri-flake"; | ||
| 209 | ref = "main"; | ||
| 210 | inputs = { | ||
| 211 | nixpkgs.follows = "nixpkgs"; | ||
| 212 | niri-unstable = { | ||
| 213 | type = "github"; | ||
| 214 | owner = "gkleen"; | ||
| 215 | repo = "niri"; | ||
| 216 | ref = "fix/locked-monitor-control"; | ||
| 217 | }; | ||
| 218 | }; | ||
| 219 | }; | ||
| 220 | nix-monitored = { | ||
| 221 | type = "github"; | ||
| 222 | owner = "ners"; | ||
| 223 | repo = "nix-monitored"; | ||
| 224 | ref = "master"; | ||
| 225 | inputs = { | ||
| 226 | nixpkgs.follows = "nixpkgs"; | ||
| 227 | }; | ||
| 228 | }; | ||
| 229 | lanzaboote = { | ||
| 230 | type = "github"; | ||
| 231 | owner = "nix-community"; | ||
| 232 | repo = "lanzaboote"; | ||
| 233 | ref = "v0.4.2"; | ||
| 234 | |||
| 235 | inputs.nixpkgs.follows = "nixpkgs"; | ||
| 236 | }; | ||
| 185 | }; | 237 | }; |
| 186 | 238 | ||
| 187 | outputs = { self, nixpkgs, home-manager, sops-nix, deploy-rs, nvfetcher, ... }@inputs: | 239 | outputs = { self, nixpkgs, home-manager, sops-nix, deploy-rs, nvfetcher, niri-flake, ... }@inputs: |
| 188 | let | 240 | let |
| 189 | inherit (builtins) attrNames attrValues elemAt toJSON isNull pathExists; | 241 | inherit (builtins) attrNames attrValues elemAt toJSON isNull pathExists; |
| 190 | inherit (nixpkgs) lib; | 242 | inherit (nixpkgs) lib; |
| @@ -267,9 +319,10 @@ | |||
| 267 | mkAccountModule = dir: path: accountName: | 319 | mkAccountModule = dir: path: accountName: |
| 268 | let | 320 | let |
| 269 | userName = accountUserName accountName; | 321 | userName = accountUserName accountName; |
| 322 | hostName = accountHostName accountName; | ||
| 270 | in overrideModule | 323 | in overrideModule |
| 271 | (import (dir + "/${path}")) | 324 | (import (dir + "/${path}")) |
| 272 | (inputs: inputs // { inherit userName; }) | 325 | (inputs: inputs // { inherit userName hostName; }) |
| 273 | (outputs: { _file = dir + "/${path}"; } | 326 | (outputs: { _file = dir + "/${path}"; } |
| 274 | // outputs | 327 | // outputs |
| 275 | // { imports = [self.nixosModules.users.${userName} or ({...}: { imports = defaultUserProfiles userName; })] ++ (outputs.imports or []); }); | 328 | // { imports = [self.nixosModules.users.${userName} or ({...}: { imports = defaultUserProfiles userName; })] ++ (outputs.imports or []); }); |
| @@ -285,7 +338,7 @@ | |||
| 285 | forAllUsers = genAttrs (unique (map accountUserName (attrNames self.nixosModules.accounts))); | 338 | forAllUsers = genAttrs (unique (map accountUserName (attrNames self.nixosModules.accounts))); |
| 286 | 339 | ||
| 287 | activateNixosConfigurations = forAllSystems (system: _pkgs: filterAttrs (_n: v: v != null) (mapAttrs' (hostName: nixosConfig: nameValuePair "${hostName}-activate" (if system == nixosConfig.config.nixpkgs.system then { type = "app"; program = "${nixosConfig.config.system.build.toplevel}/bin/switch-to-configuration"; } else null)) self.nixosConfigurations)); | 340 | activateNixosConfigurations = forAllSystems (system: _pkgs: filterAttrs (_n: v: v != null) (mapAttrs' (hostName: nixosConfig: nameValuePair "${hostName}-activate" (if system == nixosConfig.config.nixpkgs.system then { type = "app"; program = "${nixosConfig.config.system.build.toplevel}/bin/switch-to-configuration"; } else null)) self.nixosConfigurations)); |
| 288 | startVMs = forAllSystems (system: pkgs: mapAttrs' (hostName: nixosConfig: nameValuePair "run-${hostName}-vm" { type = "app"; program = "${nixosConfig.config.system.build.vm}/bin/run-${hostName}-vm"; }) (nixImport rec { dir = ./hosts; _import = mkNixosConfiguration [ { config.virtualisation.host.pkgs = pkgs; } ] dir; })); | 341 | # startVMs = forAllSystems (system: pkgs: mapAttrs' (hostName: nixosConfig: nameValuePair "run-${hostName}-vm" { type = "app"; program = "${nixosConfig.config.system.build.vm}/bin/run-${hostName}-vm"; }) (nixImport rec { dir = ./hosts; _import = mkNixosConfiguration [ { config.virtualisation.host.pkgs = pkgs; } ] dir; })); |
| 289 | activateHomeManagerConfigurations = forAllSystems (system: _pkgs: filterAttrs (_n: v: v != null) (listToAttrs (concatLists (mapAttrsToList (hostName: nixosConfig: mapAttrsToList (userName: userCfg: nameValuePair "${userName}@${hostName}-activate" (if system == nixosConfig.config.nixpkgs.system then { type = "app"; program = "${userCfg.home.activationPackage}/activate"; } else null)) nixosConfig.config.home-manager.users) self.nixosConfigurations)))); | 342 | activateHomeManagerConfigurations = forAllSystems (system: _pkgs: filterAttrs (_n: v: v != null) (listToAttrs (concatLists (mapAttrsToList (hostName: nixosConfig: mapAttrsToList (userName: userCfg: nameValuePair "${userName}@${hostName}-activate" (if system == nixosConfig.config.nixpkgs.system then { type = "app"; program = "${userCfg.home.activationPackage}/activate"; } else null)) nixosConfig.config.home-manager.users) self.nixosConfigurations)))); |
| 290 | installerShells = system: pkgs: mapAttrs (installerName: config: pkgs.callPackage ./installer/shell.nix { | 343 | installerShells = system: pkgs: mapAttrs (installerName: config: pkgs.callPackage ./installer/shell.nix { |
| 291 | inherit system installerName config; | 344 | inherit system installerName config; |
| @@ -322,18 +375,23 @@ | |||
| 322 | nixosConfigurations = installerNixosConfigurations // nixImport rec { dir = ./hosts; _import = mkNixosConfiguration [] dir; }; | 375 | nixosConfigurations = installerNixosConfigurations // nixImport rec { dir = ./hosts; _import = mkNixosConfiguration [] dir; }; |
| 323 | 376 | ||
| 324 | homeModules = nixImport rec { dir = ./home-modules; }; | 377 | homeModules = nixImport rec { dir = ./home-modules; }; |
| 325 | homeConfigurations = listToAttrs (concatLists (mapAttrsToList (hostname: nixosConfig: mapAttrsToList (username: configuration: nameValuePair "${username}@${hostname}" { inherit (configuration.home) activationPackage; }) nixosConfig.config.home-manager.users) self.nixosConfigurations)); | 378 | homeConfigurations = listToAttrs (concatLists (mapAttrsToList (hostname: nixosConfig: mapAttrsToList (username: nameValuePair "${username}@${hostname}") nixosConfig.config.home-manager.users) self.nixosConfigurations)); |
| 326 | 379 | ||
| 327 | overlays = mapAttrs (_name: path: mkOverlay path) overlayPaths; | 380 | overlays = mapAttrs (_name: path: mkOverlay path) overlayPaths; |
| 328 | 381 | ||
| 329 | packages = forAllSystems (system: systemPkgs: nixImport rec { dir = ./tools; _import = _path: name: import "${toString dir}/${name}" ({ inherit system; } // inputs); }); | 382 | packages = forAllSystems (system: systemPkgs: nixImport rec { dir = ./tools; _import = name: _base: import (dir + "/${name}") ({ inherit system; } // inputs); }); |
| 330 | 383 | ||
| 331 | # packages = mapAttrs (_name: filterAttrs (_name: isDerivation)) packages; | 384 | # packages = mapAttrs (_name: filterAttrs (_name: isDerivation)) packages; |
| 332 | # packages' = mapAttrs (_name: filterAttrs (_name: value: !(isDerivation value))) packages; | 385 | # packages' = mapAttrs (_name: filterAttrs (_name: value: !(isDerivation value))) packages; |
| 333 | 386 | ||
| 334 | legacyPackages = forAllSystems (system: systemPkgs: systemPkgs.override { overlays = attrValues self.overlays; }); | 387 | legacyPackages = forAllSystems (system: systemPkgs: systemPkgs.override { overlays = attrValues self.overlays; }); |
| 335 | 388 | ||
| 336 | apps = foldr recursiveUpdate {} [startVMs activateNixosConfigurations activateHomeManagerConfigurations]; | 389 | apps = foldr recursiveUpdate {} [ |
| 390 | #startVMs | ||
| 391 | activateNixosConfigurations activateHomeManagerConfigurations | ||
| 392 | ]; | ||
| 393 | |||
| 394 | lib = nixImport rec { dir = ./lib; _import = name: _base: import (dir + "/${name}") inputs; }; | ||
| 337 | 395 | ||
| 338 | devShells = forAllSystems (system: systemPkgs: { default = import ./shell.nix ({ inherit system; } // inputs); } // installerShells system systemPkgs); | 396 | devShells = forAllSystems (system: systemPkgs: { default = import ./shell.nix ({ inherit system; } // inputs); } // installerShells system systemPkgs); |
| 339 | 397 | ||
| @@ -358,10 +416,10 @@ | |||
| 358 | # path = activateHomeManager (self.nixosConfigurations.${hostname}.config.nixpkgs.system) usercfg.home; | 416 | # path = activateHomeManager (self.nixosConfigurations.${hostname}.config.nixpkgs.system) usercfg.home; |
| 359 | # }) self.nixosConfigurations.${hostname}.config.home-manager.users); | 417 | # }) self.nixosConfigurations.${hostname}.config.home-manager.users); |
| 360 | }) (nixImport { dir = ./hosts; _import = (_path: name: name); }); | 418 | }) (nixImport { dir = ./hosts; _import = (_path: name: name); }); |
| 361 | overrides = if pathExists ./deploy then nixImport { dir = ./deploy; _import = path: _name: import (./deploy + "/${path}") inputs; } else {}; | 419 | overrides = if pathExists ./deploy then nixImport rec { dir = ./deploy; _import = path: _name: import (dir + "/${path}") inputs; } else {}; |
| 362 | filterEnabled = attrs: mapAttrs (_n: v: filterAttrs (n: _v: n != "enabled") v) (filterAttrs (_n: v: v.enabled or true) attrs); | 420 | filterEnabled = attrs: mapAttrs (_n: v: filterAttrs (n: _v: n != "enabled") v) (filterAttrs (_n: v: v.enabled or true) attrs); |
| 363 | in mapAttrs (_n: v: if v ? "profiles" then v // { profiles = filterEnabled v.profiles; } else v) (filterEnabled (recursiveUpdate defaults overrides)); | 421 | in mapAttrs (_n: v: if v ? "profiles" then v // { profiles = filterEnabled v.profiles; } else v) (filterEnabled (recursiveUpdate defaults overrides)); |
| 364 | 422 | ||
| 365 | checks = builtins.mapAttrs (system: deployLib: deployLib.deployChecks self.deploy) deploy-rs.lib; | 423 | # checks = builtins.mapAttrs (system: deployLib: deployLib.deployChecks self.deploy) deploy-rs.lib; |
| 366 | }; | 424 | }; |
| 367 | } | 425 | } |
diff --git a/home-modules/nixpkgs-release-check.nix b/home-modules/nixpkgs-release-check.nix new file mode 100644 index 00000000..baf2713a --- /dev/null +++ b/home-modules/nixpkgs-release-check.nix | |||
| @@ -0,0 +1,4 @@ | |||
| 1 | { ... }: | ||
| 2 | { | ||
| 3 | config.home.enableNixpkgsReleaseCheck = false; | ||
| 4 | } | ||
diff --git a/home-modules/pandoc/default.nix b/home-modules/pandoc/default.nix new file mode 100644 index 00000000..1d16b621 --- /dev/null +++ b/home-modules/pandoc/default.nix | |||
| @@ -0,0 +1,27 @@ | |||
| 1 | { pkgs, lib, config, ... }: | ||
| 2 | |||
| 3 | let | ||
| 4 | cfg = config.programs.pandoc; | ||
| 5 | in { | ||
| 6 | options.programs.pandoc = { | ||
| 7 | germanAbbreviations = lib.mkEnableOption "importing german abbreviations" // { default = true; }; | ||
| 8 | extraAbbreviations = lib.mkOption { | ||
| 9 | type = lib.types.listOf lib.types.str; | ||
| 10 | default = []; | ||
| 11 | }; | ||
| 12 | }; | ||
| 13 | |||
| 14 | config = lib.mkIf cfg.enable { | ||
| 15 | xdg.dataFile = lib.mkIf (cfg.germanAbbreviations || cfg.extraAbbreviations != []) { | ||
| 16 | "pandoc/abbreviations".source = pkgs.runCommand "pandoc-abbreviations" { | ||
| 17 | buildInputs = [ pkgs.coreutils ]; | ||
| 18 | } '' | ||
| 19 | cat \ | ||
| 20 | <(${lib.getExe' cfg.finalPackage "pandoc"} --print-default-data-file=abbreviations) \ | ||
| 21 | ${lib.optionalString cfg.germanAbbreviations ./german_abbreviations.txt} \ | ||
| 22 | ${lib.optionalString (cfg.extraAbbreviations != []) (pkgs.writeText "abbrevs.txt" (lib.concatStringsSep "\n" cfg.extraAbbreviations))} \ | ||
| 23 | | sort | uniq >$out | ||
| 24 | ''; | ||
| 25 | }; | ||
| 26 | }; | ||
| 27 | } | ||
diff --git a/home-modules/pandoc/german_abbreviations.txt b/home-modules/pandoc/german_abbreviations.txt new file mode 100644 index 00000000..fa4c9c87 --- /dev/null +++ b/home-modules/pandoc/german_abbreviations.txt | |||
| @@ -0,0 +1,1423 @@ | |||
| 1 | &c. | ||
| 2 | A. | ||
| 3 | a. | ||
| 4 | a.a.O. | ||
| 5 | A.C.A.B. | ||
| 6 | a.D. | ||
| 7 | a.d.D. | ||
| 8 | a.g.O. | ||
| 9 | Abb. | ||
| 10 | abchas. | ||
| 11 | abds. | ||
| 12 | Abf. | ||
| 13 | Abfr. | ||
| 14 | Abg. | ||
| 15 | abgek. | ||
| 16 | abh. | ||
| 17 | Abh. | ||
| 18 | Abk. | ||
| 19 | ABl. | ||
| 20 | Abl. | ||
| 21 | Abm. | ||
| 22 | abn. | ||
| 23 | Abn. | ||
| 24 | abr. | ||
| 25 | Abr. | ||
| 26 | Abs. | ||
| 27 | abs. | ||
| 28 | Abschn. | ||
| 29 | Abst. | ||
| 30 | Abt. | ||
| 31 | abulg. | ||
| 32 | abw. | ||
| 33 | abwert. | ||
| 34 | abzgl. | ||
| 35 | accel. | ||
| 36 | accresc. | ||
| 37 | Add. | ||
| 38 | Adj. | ||
| 39 | adj. | ||
| 40 | Adr. | ||
| 41 | adv. | ||
| 42 | Adv. | ||
| 43 | adyg. | ||
| 44 | ae. | ||
| 45 | aengl. | ||
| 46 | afghan. | ||
| 47 | afr. | ||
| 48 | afranz. | ||
| 49 | afranzös. | ||
| 50 | afries. | ||
| 51 | afrik. | ||
| 52 | afrk. | ||
| 53 | afrs. | ||
| 54 | afrz. | ||
| 55 | afränk. | ||
| 56 | ags. | ||
| 57 | ahd. | ||
| 58 | Ahd. | ||
| 59 | aind. | ||
| 60 | air. | ||
| 61 | akad. | ||
| 62 | Akk. | ||
| 63 | akkad. | ||
| 64 | akt. | ||
| 65 | alb. | ||
| 66 | alban. | ||
| 67 | alem. | ||
| 68 | alemann. | ||
| 69 | all. | ||
| 70 | allg. | ||
| 71 | allj. | ||
| 72 | allm. | ||
| 73 | alltagsspr. | ||
| 74 | alphanum. | ||
| 75 | Alt. | ||
| 76 | altai. | ||
| 77 | altengl. | ||
| 78 | altfranz. | ||
| 79 | altfranzös. | ||
| 80 | altfrz. | ||
| 81 | altgr. | ||
| 82 | althochdt. | ||
| 83 | altis. | ||
| 84 | altisländ. | ||
| 85 | altpreuß. | ||
| 86 | altröm. | ||
| 87 | alttest. | ||
| 88 | alëut. | ||
| 89 | am. | ||
| 90 | amer. | ||
| 91 | amerik. | ||
| 92 | amerikan. | ||
| 93 | amhar. | ||
| 94 | amt. | ||
| 95 | amtl. | ||
| 96 | Amtm. | ||
| 97 | Amtsbl. | ||
| 98 | Amtsdt. | ||
| 99 | Amtsspr. | ||
| 100 | an. | ||
| 101 | anal. | ||
| 102 | anat. | ||
| 103 | Anat. | ||
| 104 | anatom. | ||
| 105 | andalus. | ||
| 106 | ang. | ||
| 107 | angelsächs. | ||
| 108 | Angest. | ||
| 109 | angest. | ||
| 110 | angloamerik. | ||
| 111 | anglofrz. | ||
| 112 | angloind. | ||
| 113 | Anh. | ||
| 114 | Ank. | ||
| 115 | Ankl. | ||
| 116 | Anl. | ||
| 117 | anl. | ||
| 118 | Anm. | ||
| 119 | Anm.d.Red. | ||
| 120 | Ann. | ||
| 121 | ann. | ||
| 122 | annamit. | ||
| 123 | anord. | ||
| 124 | Anord. | ||
| 125 | anschl. | ||
| 126 | Anschl. | ||
| 127 | Anschr. | ||
| 128 | antarkt. | ||
| 129 | Anthrop. | ||
| 130 | anthrop. | ||
| 131 | Anw. | ||
| 132 | aobd. | ||
| 133 | apl. | ||
| 134 | Apostr. | ||
| 135 | App. | ||
| 136 | Apr. | ||
| 137 | apreuß. | ||
| 138 | ar. | ||
| 139 | arab. | ||
| 140 | aragon. | ||
| 141 | aram. | ||
| 142 | aran. | ||
| 143 | architekt. | ||
| 144 | archäol. | ||
| 145 | arg. | ||
| 146 | argent. | ||
| 147 | arkt. | ||
| 148 | armen. | ||
| 149 | Art. | ||
| 150 | Art.-Nr. | ||
| 151 | Artt. | ||
| 152 | as. | ||
| 153 | aserbaidsch. | ||
| 154 | aslaw. | ||
| 155 | assyr. | ||
| 156 | astron. | ||
| 157 | asächs. | ||
| 158 | At.-Gew. | ||
| 159 | attr. | ||
| 160 | Attr. | ||
| 161 | Aufl. | ||
| 162 | Aug. | ||
| 163 | Ausg. | ||
| 164 | ausgen. | ||
| 165 | Aussch. | ||
| 166 | ausschl. | ||
| 167 | Ausspr. | ||
| 168 | Ausst. | ||
| 169 | austral. | ||
| 170 | awar. | ||
| 171 | awest. | ||
| 172 | Az. | ||
| 173 | aztek. | ||
| 174 | b. | ||
| 175 | B. | ||
| 176 | Ba.-Wü. | ||
| 177 | bab. | ||
| 178 | babyl. | ||
| 179 | bair. | ||
| 180 | Bakt. | ||
| 181 | Bal. | ||
| 182 | balt. | ||
| 183 | baltoslaw. | ||
| 184 | Bankw. | ||
| 185 | bas. | ||
| 186 | baschk. | ||
| 187 | bask. | ||
| 188 | Bat. | ||
| 189 | bauf. | ||
| 190 | Bauw. | ||
| 191 | bay. | ||
| 192 | bayer. | ||
| 193 | bayr. | ||
| 194 | BayVBl. | ||
| 195 | Bd. | ||
| 196 | Bde. | ||
| 197 | Bed. | ||
| 198 | Begr. | ||
| 199 | begr. | ||
| 200 | beif. | ||
| 201 | beil. | ||
| 202 | Beil. | ||
| 203 | Bem. | ||
| 204 | ben. | ||
| 205 | berbersprachl. | ||
| 206 | Bergb. | ||
| 207 | berlin. | ||
| 208 | Berufsbez. | ||
| 209 | bes. | ||
| 210 | besch. | ||
| 211 | Beschl. | ||
| 212 | best. | ||
| 213 | Best.-Nr. | ||
| 214 | Betr. | ||
| 215 | betr. | ||
| 216 | Betriebswiss. | ||
| 217 | Bev. | ||
| 218 | Bez. | ||
| 219 | bez. | ||
| 220 | bezw. | ||
| 221 | Bf. | ||
| 222 | bfn. | ||
| 223 | Bg. | ||
| 224 | bgld. | ||
| 225 | Bgld. | ||
| 226 | Bhf. | ||
| 227 | Bib. | ||
| 228 | bibl. | ||
| 229 | bildl. | ||
| 230 | bildungsspr. | ||
| 231 | Biol. | ||
| 232 | biol. | ||
| 233 | Bj. | ||
| 234 | bl. | ||
| 235 | Bl. | ||
| 236 | Blk. | ||
| 237 | Bln. | ||
| 238 | Bodenk. | ||
| 239 | bot. | ||
| 240 | Bot. | ||
| 241 | Br.-M. | ||
| 242 | Br.-Mstr. | ||
| 243 | bras. | ||
| 244 | bret. | ||
| 245 | breton. | ||
| 246 | brit. | ||
| 247 | Brm. | ||
| 248 | brn. | ||
| 249 | Bruchz. | ||
| 250 | bsd. | ||
| 251 | Bsp. | ||
| 252 | bsplsw. | ||
| 253 | bspw. | ||
| 254 | BT-Drs. | ||
| 255 | Btl. | ||
| 256 | btto. | ||
| 257 | Bttr. | ||
| 258 | Buchw. | ||
| 259 | buddh. | ||
| 260 | bulg. | ||
| 261 | bulgar. | ||
| 262 | burjat. | ||
| 263 | burmes. | ||
| 264 | Bw. | ||
| 265 | byzant. | ||
| 266 | Bz. | ||
| 267 | bzb. | ||
| 268 | bzgl. | ||
| 269 | bzw. | ||
| 270 | böhm. | ||
| 271 | Börsenw. | ||
| 272 | C. | ||
| 273 | ca. | ||
| 274 | Carp. | ||
| 275 | Cb. | ||
| 276 | cf. | ||
| 277 | chakass. | ||
| 278 | chald. | ||
| 279 | chant. | ||
| 280 | chem. | ||
| 281 | Chem. | ||
| 282 | chilen. | ||
| 283 | chin. | ||
| 284 | Chr. | ||
| 285 | christl. | ||
| 286 | chron. | ||
| 287 | Chron. | ||
| 288 | Co. | ||
| 289 | Comp. | ||
| 290 | cresc. | ||
| 291 | D. | ||
| 292 | Dankb. | ||
| 293 | dankwtw. | ||
| 294 | das. | ||
| 295 | dass. | ||
| 296 | Dat. | ||
| 297 | dbzgl. | ||
| 298 | ders. | ||
| 299 | des. | ||
| 300 | desgl. | ||
| 301 | Dez. | ||
| 302 | dgl. | ||
| 303 | Di. | ||
| 304 | dial. | ||
| 305 | dichter. | ||
| 306 | dies. | ||
| 307 | dim. | ||
| 308 | Dim. | ||
| 309 | Dimin. | ||
| 310 | dimin. | ||
| 311 | Dipl. | ||
| 312 | Dipl.-Bibl. | ||
| 313 | Dipl.-Ing. | ||
| 314 | Dipl.-Kff. | ||
| 315 | Dipl.-Kffr. | ||
| 316 | Dipl.-Kfm. | ||
| 317 | Dipl.-Kfr. | ||
| 318 | Dipl.-Psych. | ||
| 319 | Dir. | ||
| 320 | Diss. | ||
| 321 | Do. | ||
| 322 | do. | ||
| 323 | Do.-Gge. | ||
| 324 | dominikan. | ||
| 325 | dor. | ||
| 326 | Doz. | ||
| 327 | Dr. | ||
| 328 | Drchf. | ||
| 329 | Drcks. | ||
| 330 | Dres. | ||
| 331 | Drs. | ||
| 332 | Drucks. | ||
| 333 | dt. | ||
| 334 | Dtl. | ||
| 335 | dto. | ||
| 336 | Dtzd. | ||
| 337 | dz. | ||
| 338 | Dz. | ||
| 339 | dän. | ||
| 340 | E. | ||
| 341 | ebd. | ||
| 342 | Ed. | ||
| 343 | ed. | ||
| 344 | ehem. | ||
| 345 | eidg. | ||
| 346 | eig. | ||
| 347 | eigtl. | ||
| 348 | Einf. | ||
| 349 | einh. | ||
| 350 | Einl. | ||
| 351 | einschl. | ||
| 352 | Einw. | ||
| 353 | Eisenb. | ||
| 354 | Elektrot. | ||
| 355 | elektrotechn. | ||
| 356 | em. | ||
| 357 | engl. | ||
| 358 | entspr. | ||
| 359 | erb. | ||
| 360 | erf. | ||
| 361 | erg. | ||
| 362 | Erg. | ||
| 363 | erk. | ||
| 364 | Erl. | ||
| 365 | erm. | ||
| 366 | Ers.-D. | ||
| 367 | ersch. | ||
| 368 | erschl. | ||
| 369 | Erschl. | ||
| 370 | Erschl.-Geb. | ||
| 371 | Erschw. | ||
| 372 | erschw. | ||
| 373 | Erstauff. | ||
| 374 | Erstausg. | ||
| 375 | Ertr. | ||
| 376 | Erw. | ||
| 377 | Erw.-Bldg. | ||
| 378 | erwähnw. | ||
| 379 | Erzb. | ||
| 380 | erzg. | ||
| 381 | erzgeb. | ||
| 382 | eskim. | ||
| 383 | estn. | ||
| 384 | etc. | ||
| 385 | Etg. | ||
| 386 | etrusk. | ||
| 387 | etw. | ||
| 388 | eur. | ||
| 389 | europ. | ||
| 390 | ev. | ||
| 391 | evang. | ||
| 392 | evtl. | ||
| 393 | Ew. | ||
| 394 | ewen. | ||
| 395 | ewenk. | ||
| 396 | exkl. | ||
| 397 | Expl. | ||
| 398 | Ez. | ||
| 399 | f. | ||
| 400 | F. | ||
| 401 | Fa. | ||
| 402 | fachspr. | ||
| 403 | Fachspr. | ||
| 404 | Fag. | ||
| 405 | Fam. | ||
| 406 | fam. | ||
| 407 | Febr. | ||
| 408 | fem. | ||
| 409 | ff. | ||
| 410 | Fig. | ||
| 411 | fig. | ||
| 412 | finanzmath. | ||
| 413 | finn. | ||
| 414 | finnougr. | ||
| 415 | Flgh. | ||
| 416 | fläm. | ||
| 417 | Fn. | ||
| 418 | fnhd. | ||
| 419 | folg. | ||
| 420 | Forts. | ||
| 421 | Fortstzg. | ||
| 422 | Fr. | ||
| 423 | fr. | ||
| 424 | fragm. | ||
| 425 | franz. | ||
| 426 | französ. | ||
| 427 | Frdf. | ||
| 428 | frdl. | ||
| 429 | frdsprlg. | ||
| 430 | Frfr. | ||
| 431 | frfr. | ||
| 432 | Frh. | ||
| 433 | Frhf. | ||
| 434 | Frhr. | ||
| 435 | fries. | ||
| 436 | friesl. | ||
| 437 | Frk. | ||
| 438 | Frl. | ||
| 439 | Frm. | ||
| 440 | frnhd. | ||
| 441 | Frspr. | ||
| 442 | frstl. | ||
| 443 | Frt. | ||
| 444 | frtr. | ||
| 445 | Frwk. | ||
| 446 | frz. | ||
| 447 | fränk. | ||
| 448 | frühnhd. | ||
| 449 | Fs. | ||
| 450 | Fsch. | ||
| 451 | Fschr. | ||
| 452 | Fsm. | ||
| 453 | Ftm. | ||
| 454 | Fut. | ||
| 455 | fut. | ||
| 456 | färö. | ||
| 457 | förml. | ||
| 458 | g. | ||
| 459 | Ga. | ||
| 460 | gall. | ||
| 461 | galloroman. | ||
| 462 | Gart. | ||
| 463 | gaskogn. | ||
| 464 | gbd. | ||
| 465 | Gbd. | ||
| 466 | Gbf. | ||
| 467 | GBl. | ||
| 468 | Gbl. | ||
| 469 | geb. | ||
| 470 | Geb. | ||
| 471 | Geb.-T. | ||
| 472 | Gebr. | ||
| 473 | gebr. | ||
| 474 | ged. | ||
| 475 | gef. | ||
| 476 | geg. | ||
| 477 | gegr. | ||
| 478 | geh. | ||
| 479 | gek. | ||
| 480 | gel. | ||
| 481 | geleg. | ||
| 482 | gem. | ||
| 483 | gemeingerm. | ||
| 484 | gen. | ||
| 485 | Gen. | ||
| 486 | geod. | ||
| 487 | geogr. | ||
| 488 | geograf. | ||
| 489 | geograph. | ||
| 490 | geol. | ||
| 491 | geolog. | ||
| 492 | geophys. | ||
| 493 | georg. | ||
| 494 | gep. | ||
| 495 | ger. | ||
| 496 | germ. | ||
| 497 | Ges. | ||
| 498 | ges. | ||
| 499 | gesch. | ||
| 500 | gespr. | ||
| 501 | gest. | ||
| 502 | get. | ||
| 503 | Gew. | ||
| 504 | gew. | ||
| 505 | gez. | ||
| 506 | Gfsch. | ||
| 507 | Gft. | ||
| 508 | gg. | ||
| 509 | ggb. | ||
| 510 | ggbfs. | ||
| 511 | ggez. | ||
| 512 | ggf. | ||
| 513 | ggfls. | ||
| 514 | ggfs. | ||
| 515 | Ggs. | ||
| 516 | ggü. | ||
| 517 | Ghzg. | ||
| 518 | Ghzgt. | ||
| 519 | glchz. | ||
| 520 | Gld. | ||
| 521 | Glde. | ||
| 522 | gldg. | ||
| 523 | Gldr. | ||
| 524 | Gled. | ||
| 525 | gleichbed. | ||
| 526 | gleichn. | ||
| 527 | gleichz. | ||
| 528 | Glfl. | ||
| 529 | gls. | ||
| 530 | gltd. | ||
| 531 | gltg. | ||
| 532 | glz. | ||
| 533 | gm. | ||
| 534 | got. | ||
| 535 | gr. | ||
| 536 | Gr. | ||
| 537 | Gramm. | ||
| 538 | grammat. | ||
| 539 | graph. | ||
| 540 | grch. | ||
| 541 | Grchl. | ||
| 542 | Grdb. | ||
| 543 | Grdf. | ||
| 544 | Grdfl. | ||
| 545 | Grdg. | ||
| 546 | Grdl. | ||
| 547 | Grdr. | ||
| 548 | grds. | ||
| 549 | Grdst. | ||
| 550 | griech. | ||
| 551 | Grz. | ||
| 552 | grönländ. | ||
| 553 | Gstb. | ||
| 554 | Gt. | ||
| 555 | gyn. | ||
| 556 | gynäk. | ||
| 557 | gäl. | ||
| 558 | H.-I. | ||
| 559 | H.-Qu. | ||
| 560 | hait. | ||
| 561 | Handw. | ||
| 562 | Hbf. | ||
| 563 | hd. | ||
| 564 | Hd.-Bibl. | ||
| 565 | Hdb. | ||
| 566 | hdbr. | ||
| 567 | Hdbr. | ||
| 568 | hdl. | ||
| 569 | Hdl. | ||
| 570 | Hdlbg. | ||
| 571 | hebr. | ||
| 572 | hess. | ||
| 573 | hethit. | ||
| 574 | Hf. | ||
| 575 | Hg. | ||
| 576 | hg. | ||
| 577 | hindust. | ||
| 578 | hinr. | ||
| 579 | hins. | ||
| 580 | Hinw. | ||
| 581 | hist. | ||
| 582 | HJber. | ||
| 583 | Hkl. | ||
| 584 | hl. | ||
| 585 | hochd. | ||
| 586 | hochspr. | ||
| 587 | Hom. | ||
| 588 | hor. | ||
| 589 | Hpfl. | ||
| 590 | hptpl. | ||
| 591 | hpts. | ||
| 592 | Hptst. | ||
| 593 | hptw. | ||
| 594 | Hptw. | ||
| 595 | HQu. | ||
| 596 | Hr. | ||
| 597 | HReg. | ||
| 598 | Hrn. | ||
| 599 | Hrsg. | ||
| 600 | hrsg. | ||
| 601 | Hs. | ||
| 602 | Hs.-Nr. | ||
| 603 | hschr. | ||
| 604 | Hschr. | ||
| 605 | HSt. | ||
| 606 | Hubbr. | ||
| 607 | Hubr. | ||
| 608 | Hw. | ||
| 609 | Hyaz. | ||
| 610 | hydr. | ||
| 611 | hydrol. | ||
| 612 | Hzm. | ||
| 613 | i. | ||
| 614 | I.E. | ||
| 615 | i.g.O. | ||
| 616 | i.Tr. | ||
| 617 | iber. | ||
| 618 | ibid. | ||
| 619 | ide. | ||
| 620 | Ident. | ||
| 621 | ident. | ||
| 622 | idg. | ||
| 623 | ie. | ||
| 624 | illyr. | ||
| 625 | Imkerspr. | ||
| 626 | imp. | ||
| 627 | Imp. | ||
| 628 | in. | ||
| 629 | Ind. | ||
| 630 | ind. | ||
| 631 | indef. | ||
| 632 | indekl. | ||
| 633 | indian. | ||
| 634 | indiff. | ||
| 635 | indir. | ||
| 636 | indiv. | ||
| 637 | indog. | ||
| 638 | indogerm. | ||
| 639 | indogerman. | ||
| 640 | indoiran. | ||
| 641 | indon. | ||
| 642 | indones. | ||
| 643 | Inf. | ||
| 644 | inf. | ||
| 645 | Ing. | ||
| 646 | Inh. | ||
| 647 | inkl. | ||
| 648 | inn. | ||
| 649 | Ins. | ||
| 650 | insb. | ||
| 651 | insbes. | ||
| 652 | int. | ||
| 653 | intern. | ||
| 654 | intrans. | ||
| 655 | ir. | ||
| 656 | iran. | ||
| 657 | iron. | ||
| 658 | isl. | ||
| 659 | islam. | ||
| 660 | isländ. | ||
| 661 | it. | ||
| 662 | ital. | ||
| 663 | italien. | ||
| 664 | j. | ||
| 665 | J. | ||
| 666 | Jahrh. | ||
| 667 | jakut. | ||
| 668 | Jan. | ||
| 669 | jap. | ||
| 670 | japan. | ||
| 671 | jav. | ||
| 672 | jem. | ||
| 673 | jemen. | ||
| 674 | Jg. | ||
| 675 | jgdfr. | ||
| 676 | Jh. | ||
| 677 | Jhd. | ||
| 678 | Jhdt. | ||
| 679 | Jhg. | ||
| 680 | Jhs. | ||
| 681 | jidd. | ||
| 682 | jmd. | ||
| 683 | jmdm. | ||
| 684 | jmdn. | ||
| 685 | jmds. | ||
| 686 | journ. | ||
| 687 | jr. | ||
| 688 | Jr. | ||
| 689 | Jt. | ||
| 690 | Jtsd. | ||
| 691 | jugendspr. | ||
| 692 | jugendsprachl. | ||
| 693 | jugoslaw. | ||
| 694 | Jul. | ||
| 695 | jun. | ||
| 696 | Jun. | ||
| 697 | jur. | ||
| 698 | Juw. | ||
| 699 | jägersprachl. | ||
| 700 | jährl. | ||
| 701 | Jän. | ||
| 702 | jüd. | ||
| 703 | k.u.k. | ||
| 704 | K.Ö.St.V. | ||
| 705 | kalm. | ||
| 706 | kanad. | ||
| 707 | Kap. | ||
| 708 | karib. | ||
| 709 | kastil. | ||
| 710 | katal. | ||
| 711 | katalan. | ||
| 712 | kath. | ||
| 713 | kaufm. | ||
| 714 | kaukas. | ||
| 715 | kelt. | ||
| 716 | Kgr. | ||
| 717 | Kh. | ||
| 718 | kindersprachl. | ||
| 719 | kirchenlat. | ||
| 720 | kirchenslaw. | ||
| 721 | kirchl. | ||
| 722 | kirg. | ||
| 723 | Kl. | ||
| 724 | klass. | ||
| 725 | klass.-lat. | ||
| 726 | klimatol. | ||
| 727 | kol. | ||
| 728 | Komm. | ||
| 729 | Konj. | ||
| 730 | Konv. | ||
| 731 | Kop. | ||
| 732 | kop. | ||
| 733 | kopt. | ||
| 734 | korean. | ||
| 735 | Kr. | ||
| 736 | kreol. | ||
| 737 | kret. | ||
| 738 | Krh. | ||
| 739 | Krhs. | ||
| 740 | Krim.-Ob.-Insp. | ||
| 741 | krimgot. | ||
| 742 | kriminaltechn. | ||
| 743 | Krkhs. | ||
| 744 | kroat. | ||
| 745 | Krs. | ||
| 746 | Ks. | ||
| 747 | Kto. | ||
| 748 | Kto.-Nr. | ||
| 749 | kuban. | ||
| 750 | kurd. | ||
| 751 | Kurzw. | ||
| 752 | Kw. | ||
| 753 | l. | ||
| 754 | L.-Abg. | ||
| 755 | lab. | ||
| 756 | LAbg. | ||
| 757 | ladin. | ||
| 758 | landsch. | ||
| 759 | Landw. | ||
| 760 | langfr. | ||
| 761 | langj. | ||
| 762 | langob. | ||
| 763 | langobard. | ||
| 764 | lapp. | ||
| 765 | lat. | ||
| 766 | latein. | ||
| 767 | latinis. | ||
| 768 | lautl. | ||
| 769 | lautm. | ||
| 770 | lbd. | ||
| 771 | lbdg. | ||
| 772 | Ldkr. | ||
| 773 | led. | ||
| 774 | leg. | ||
| 775 | lett. | ||
| 776 | lfd. | ||
| 777 | Lfg. | ||
| 778 | Lfm. | ||
| 779 | Lfrg. | ||
| 780 | Lg. | ||
| 781 | lgfr. | ||
| 782 | Lgft. | ||
| 783 | lgj. | ||
| 784 | lig. | ||
| 785 | ling. | ||
| 786 | lit. | ||
| 787 | LL.M. | ||
| 788 | lrh. | ||
| 789 | lt. | ||
| 790 | ltd. | ||
| 791 | luth. | ||
| 792 | luxemb. | ||
| 793 | Lz. | ||
| 794 | m. | ||
| 795 | M. | ||
| 796 | M.-Schr. | ||
| 797 | m.a.W. | ||
| 798 | ma. | ||
| 799 | MA. | ||
| 800 | Mag. | ||
| 801 | malai. | ||
| 802 | marinespr. | ||
| 803 | marx. | ||
| 804 | mask. | ||
| 805 | math. | ||
| 806 | Math. | ||
| 807 | max. | ||
| 808 | Max. | ||
| 809 | mazedon. | ||
| 810 | mbl. | ||
| 811 | Mbl. | ||
| 812 | MBl. | ||
| 813 | Mbll. | ||
| 814 | md. | ||
| 815 | mdal. | ||
| 816 | mdj. | ||
| 817 | mdl. | ||
| 818 | mdls. | ||
| 819 | Mdt. | ||
| 820 | me. | ||
| 821 | mech. | ||
| 822 | meckl. | ||
| 823 | med. | ||
| 824 | melanes. | ||
| 825 | mengl. | ||
| 826 | Merc. | ||
| 827 | met. | ||
| 828 | meteorol. | ||
| 829 | meton. | ||
| 830 | mex. | ||
| 831 | mexik. | ||
| 832 | mfr. | ||
| 833 | mfranz. | ||
| 834 | mfrk. | ||
| 835 | mfrz. | ||
| 836 | mfränk. | ||
| 837 | mgl. | ||
| 838 | Mgl. | ||
| 839 | mglw. | ||
| 840 | mhd. | ||
| 841 | mhdt. | ||
| 842 | Mi. | ||
| 843 | mi. | ||
| 844 | Mia. | ||
| 845 | milit. | ||
| 846 | Mill. | ||
| 847 | min. | ||
| 848 | Min. | ||
| 849 | mind. | ||
| 850 | Mio. | ||
| 851 | mir. | ||
| 852 | Mitgl. | ||
| 853 | mitteld. | ||
| 854 | mitteldt. | ||
| 855 | mittelhochdt. | ||
| 856 | Mittw. | ||
| 857 | Mitw. | ||
| 858 | mlat. | ||
| 859 | Mme. | ||
| 860 | Mmes. | ||
| 861 | mnd. | ||
| 862 | mndd. | ||
| 863 | mniederd. | ||
| 864 | mnl. | ||
| 865 | Mo. | ||
| 866 | mod. | ||
| 867 | mong. | ||
| 868 | Mrd. | ||
| 869 | Mrz. | ||
| 870 | Mschr. | ||
| 871 | Msgr. | ||
| 872 | Msp. | ||
| 873 | mtl. | ||
| 874 | mundartl. | ||
| 875 | musik. | ||
| 876 | MwSt. | ||
| 877 | Myth. | ||
| 878 | Mz. | ||
| 879 | männl. | ||
| 880 | möbl. | ||
| 881 | n. | ||
| 882 | Nachf. | ||
| 883 | nachm. | ||
| 884 | nat. | ||
| 885 | nationalsoz. | ||
| 886 | natsoz. | ||
| 887 | Nbf. | ||
| 888 | Nbfl. | ||
| 889 | Nchf. | ||
| 890 | nd. | ||
| 891 | ndd. | ||
| 892 | ndrl. | ||
| 893 | neapolit. | ||
| 894 | Neub. | ||
| 895 | neunorweg. | ||
| 896 | neutest. | ||
| 897 | neutr. | ||
| 898 | Nfl. | ||
| 899 | ngl. | ||
| 900 | ngr. | ||
| 901 | nhbr. | ||
| 902 | nhd. | ||
| 903 | nicar. | ||
| 904 | niederd. | ||
| 905 | niederdt. | ||
| 906 | niederl. | ||
| 907 | niederld. | ||
| 908 | niem. | ||
| 909 | niger. | ||
| 910 | nihil. | ||
| 911 | nl. | ||
| 912 | nlat. | ||
| 913 | nmtl. | ||
| 914 | Nom. | ||
| 915 | nord. | ||
| 916 | nordamerik. | ||
| 917 | nordd. | ||
| 918 | norddt. | ||
| 919 | nordgerm. | ||
| 920 | nordostd. | ||
| 921 | nordostdt. | ||
| 922 | nordwestd. | ||
| 923 | nordwestdt. | ||
| 924 | norm. | ||
| 925 | norw. | ||
| 926 | norweg. | ||
| 927 | Nov. | ||
| 928 | Nr. | ||
| 929 | ntw. | ||
| 930 | Ntw. | ||
| 931 | Nutzfl. | ||
| 932 | nw. | ||
| 933 | näml. | ||
| 934 | nö. | ||
| 935 | nördl. | ||
| 936 | o. | ||
| 937 | O.K. | ||
| 938 | ob. | ||
| 939 | Ob. | ||
| 940 | Obb. | ||
| 941 | obb. | ||
| 942 | obd. | ||
| 943 | Oberlaus. | ||
| 944 | obers. | ||
| 945 | obersächs. | ||
| 946 | obj. | ||
| 947 | od. | ||
| 948 | offiz. | ||
| 949 | Offz. | ||
| 950 | Ofr. | ||
| 951 | ofrs. | ||
| 952 | Okt. | ||
| 953 | op. | ||
| 954 | Orch.-Bes. | ||
| 955 | org. | ||
| 956 | Orig. | ||
| 957 | orn. | ||
| 958 | orth. | ||
| 959 | Ortskl. | ||
| 960 | Osch. | ||
| 961 | osk. | ||
| 962 | osman. | ||
| 963 | ostd. | ||
| 964 | ostdt. | ||
| 965 | ostfr. | ||
| 966 | ostfrz. | ||
| 967 | ostgerm. | ||
| 968 | ostidg. | ||
| 969 | ostmdt. | ||
| 970 | ostmitteld. | ||
| 971 | ostniederd. | ||
| 972 | ostpr. | ||
| 973 | ostpreuß. | ||
| 974 | ostw. | ||
| 975 | osö. | ||
| 976 | Ouv. | ||
| 977 | oz. | ||
| 978 | Oz. | ||
| 979 | oö. | ||
| 980 | OÖ. | ||
| 981 | P. | ||
| 982 | p. | ||
| 983 | P.S. | ||
| 984 | pa. | ||
| 985 | palästin. | ||
| 986 | par. | ||
| 987 | parag. | ||
| 988 | Paragr. | ||
| 989 | Parl. | ||
| 990 | Part. | ||
| 991 | pass. | ||
| 992 | Pat. | ||
| 993 | pej. | ||
| 994 | pers. | ||
| 995 | peruan. | ||
| 996 | Pet. | ||
| 997 | Pf. | ||
| 998 | Pfd. | ||
| 999 | Pfg. | ||
| 1000 | Pfl. | ||
| 1001 | pharm. | ||
| 1002 | philos. | ||
| 1003 | Philos. | ||
| 1004 | phonolog. | ||
| 1005 | phryg. | ||
| 1006 | Phys. | ||
| 1007 | phys. | ||
| 1008 | phöniz. | ||
| 1009 | Pi. | ||
| 1010 | pik. | ||
| 1011 | Pkt. | ||
| 1012 | Pl. | ||
| 1013 | Plur. | ||
| 1014 | poet. | ||
| 1015 | Pol. | ||
| 1016 | pol. | ||
| 1017 | polit. | ||
| 1018 | poln. | ||
| 1019 | polynes. | ||
| 1020 | port. | ||
| 1021 | portug. | ||
| 1022 | Pos. | ||
| 1023 | pos. | ||
| 1024 | pp. | ||
| 1025 | ppa. | ||
| 1026 | preuß. | ||
| 1027 | Priv.-Doz. | ||
| 1028 | Prof. | ||
| 1029 | prot. | ||
| 1030 | Prot. | ||
| 1031 | prov. | ||
| 1032 | Prov. | ||
| 1033 | prov.-fr. | ||
| 1034 | provenz. | ||
| 1035 | Proz. | ||
| 1036 | Proz.-Bev. | ||
| 1037 | präd. | ||
| 1038 | prähist. | ||
| 1039 | Präs. | ||
| 1040 | Psych. | ||
| 1041 | psych. | ||
| 1042 | Päd. | ||
| 1043 | Q. | ||
| 1044 | q.v. | ||
| 1045 | Qmstr. | ||
| 1046 | Qt. | ||
| 1047 | qu. | ||
| 1048 | Qu. | ||
| 1049 | quadr. | ||
| 1050 | Quadr. | ||
| 1051 | qual. | ||
| 1052 | Qual. | ||
| 1053 | quant. | ||
| 1054 | Quant. | ||
| 1055 | Quar. | ||
| 1056 | Quart. | ||
| 1057 | Quat. | ||
| 1058 | quitt. | ||
| 1059 | Quitt. | ||
| 1060 | Quäst. | ||
| 1061 | r. | ||
| 1062 | r.-k. | ||
| 1063 | Rab. | ||
| 1064 | rad. | ||
| 1065 | Raff. | ||
| 1066 | Rak. | ||
| 1067 | Randb. | ||
| 1068 | Randbem. | ||
| 1069 | rat. | ||
| 1070 | Rat. | ||
| 1071 | Rb. | ||
| 1072 | rd. | ||
| 1073 | RdErl. | ||
| 1074 | Rdf. | ||
| 1075 | refl. | ||
| 1076 | Reg. | ||
| 1077 | Reg.-Bez. | ||
| 1078 | Regt. | ||
| 1079 | Rel. | ||
| 1080 | rel. | ||
| 1081 | relig. | ||
| 1082 | Rep. | ||
| 1083 | resp. | ||
| 1084 | Rg.-Präs. | ||
| 1085 | RGBl. | ||
| 1086 | rglm. | ||
| 1087 | Rgstr. | ||
| 1088 | Rgt. | ||
| 1089 | Rh. | ||
| 1090 | rh. | ||
| 1091 | rhein. | ||
| 1092 | rheinhess. | ||
| 1093 | rhet. | ||
| 1094 | rhfrk. | ||
| 1095 | Rhj. | ||
| 1096 | Rhld. | ||
| 1097 | Rhs. | ||
| 1098 | Ri. | ||
| 1099 | Richtl. | ||
| 1100 | rip. | ||
| 1101 | rk. | ||
| 1102 | roman. | ||
| 1103 | rotw. | ||
| 1104 | Rr. | ||
| 1105 | rrh. | ||
| 1106 | Rspr. | ||
| 1107 | Rtn. | ||
| 1108 | Rtt. | ||
| 1109 | rumän. | ||
| 1110 | russ. | ||
| 1111 | Rvj. | ||
| 1112 | rzp. | ||
| 1113 | rätorom. | ||
| 1114 | röm. | ||
| 1115 | röm.-kath. | ||
| 1116 | S. | ||
| 1117 | s. | ||
| 1118 | S.-Wk. | ||
| 1119 | Sa. | ||
| 1120 | Sachs. | ||
| 1121 | san. | ||
| 1122 | sanskr. | ||
| 1123 | Sat. | ||
| 1124 | sat. | ||
| 1125 | Sb. | ||
| 1126 | Sbd. | ||
| 1127 | sc. | ||
| 1128 | scherzh. | ||
| 1129 | Schill. | ||
| 1130 | schles. | ||
| 1131 | schott. | ||
| 1132 | schr. | ||
| 1133 | schriftl. | ||
| 1134 | Schussw. | ||
| 1135 | schwed. | ||
| 1136 | schweiz. | ||
| 1137 | Schwg. | ||
| 1138 | Schwp. | ||
| 1139 | schwäb. | ||
| 1140 | scil. | ||
| 1141 | Sdp. | ||
| 1142 | sek. | ||
| 1143 | sem. | ||
| 1144 | semit. | ||
| 1145 | sen. | ||
| 1146 | Sep. | ||
| 1147 | Sept. | ||
| 1148 | serb. | ||
| 1149 | serbokroat. | ||
| 1150 | Sg. | ||
| 1151 | sibir. | ||
| 1152 | Sing. | ||
| 1153 | singhal. | ||
| 1154 | Sir. | ||
| 1155 | sizilian. | ||
| 1156 | skand. | ||
| 1157 | slaw. | ||
| 1158 | slow. | ||
| 1159 | slowak. | ||
| 1160 | slowen. | ||
| 1161 | So. | ||
| 1162 | sod. | ||
| 1163 | sof. | ||
| 1164 | sog. | ||
| 1165 | sogen. | ||
| 1166 | sogl. | ||
| 1167 | soldatenspr. | ||
| 1168 | solv. | ||
| 1169 | somal. | ||
| 1170 | sorb. | ||
| 1171 | Sout. | ||
| 1172 | soz. | ||
| 1173 | soziol. | ||
| 1174 | span. | ||
| 1175 | spez. | ||
| 1176 | sportspr. | ||
| 1177 | Spr. | ||
| 1178 | sprachwiss. | ||
| 1179 | Spvg. | ||
| 1180 | Spvgg. | ||
| 1181 | spätahd. | ||
| 1182 | spätgriech. | ||
| 1183 | spätlat. | ||
| 1184 | spätmhd. | ||
| 1185 | Sr. | ||
| 1186 | ssp. | ||
| 1187 | St. | ||
| 1188 | St.-Nr. | ||
| 1189 | staatl. | ||
| 1190 | Std. | ||
| 1191 | stdl. | ||
| 1192 | stellv. | ||
| 1193 | Stellv. | ||
| 1194 | Stk. | ||
| 1195 | Str. | ||
| 1196 | str. | ||
| 1197 | Stud. | ||
| 1198 | stud. | ||
| 1199 | subsp. | ||
| 1200 | Subst. | ||
| 1201 | sumer. | ||
| 1202 | svw. | ||
| 1203 | Swk. | ||
| 1204 | syn. | ||
| 1205 | Syn. | ||
| 1206 | syr. | ||
| 1207 | sächs. | ||
| 1208 | südafrik. | ||
| 1209 | südd. | ||
| 1210 | süddt. | ||
| 1211 | südl. | ||
| 1212 | südostdt. | ||
| 1213 | südwestd. | ||
| 1214 | Süßw. | ||
| 1215 | Tab. | ||
| 1216 | Tabl. | ||
| 1217 | Taf. | ||
| 1218 | tamil. | ||
| 1219 | tatar. | ||
| 1220 | techn. | ||
| 1221 | Tel. | ||
| 1222 | telef. | ||
| 1223 | Temp. | ||
| 1224 | Terr. | ||
| 1225 | tessin. | ||
| 1226 | test. | ||
| 1227 | Tfx. | ||
| 1228 | tgl. | ||
| 1229 | Tgt. | ||
| 1230 | thrak. | ||
| 1231 | thür. | ||
| 1232 | thüring. | ||
| 1233 | Ti. | ||
| 1234 | tib. | ||
| 1235 | tirol. | ||
| 1236 | Tlr. | ||
| 1237 | tochar. | ||
| 1238 | trans. | ||
| 1239 | tsch. | ||
| 1240 | tschech. | ||
| 1241 | tschechoslowak. | ||
| 1242 | Tsd. | ||
| 1243 | tun. | ||
| 1244 | Tun. | ||
| 1245 | tunes. | ||
| 1246 | Tunes. | ||
| 1247 | tungus. | ||
| 1248 | turkotat. | ||
| 1249 | typogr. | ||
| 1250 | tägl. | ||
| 1251 | türk. | ||
| 1252 | u. | ||
| 1253 | u.a. | ||
| 1254 | Ubr. | ||
| 1255 | ue. | ||
| 1256 | ugr. | ||
| 1257 | ugs. | ||
| 1258 | ukrain. | ||
| 1259 | umbr. | ||
| 1260 | umg. | ||
| 1261 | unang. | ||
| 1262 | unbefl. | ||
| 1263 | Unf. | ||
| 1264 | unf. | ||
| 1265 | unfol. | ||
| 1266 | unfr. | ||
| 1267 | ung. | ||
| 1268 | ungar. | ||
| 1269 | ungebr. | ||
| 1270 | ungel. | ||
| 1271 | ungen. | ||
| 1272 | unges. | ||
| 1273 | ungl. | ||
| 1274 | Uni-Kl. | ||
| 1275 | Univ. | ||
| 1276 | unv. | ||
| 1277 | unverantw. | ||
| 1278 | unverb. | ||
| 1279 | unverbr. | ||
| 1280 | unverd. | ||
| 1281 | unverg. | ||
| 1282 | unverh. | ||
| 1283 | unverk. | ||
| 1284 | unverp. | ||
| 1285 | unversch. | ||
| 1286 | unverz. | ||
| 1287 | unverzgl. | ||
| 1288 | unvollst. | ||
| 1289 | unvorb. | ||
| 1290 | unvors. | ||
| 1291 | unzerbr. | ||
| 1292 | urgerm. | ||
| 1293 | urkdl. | ||
| 1294 | urspr. | ||
| 1295 | ursprüngl. | ||
| 1296 | Urt. | ||
| 1297 | usf. | ||
| 1298 | USt-IdNr. | ||
| 1299 | usw. | ||
| 1300 | uvm. | ||
| 1301 | v. | ||
| 1302 | va. | ||
| 1303 | Ver. | ||
| 1304 | Verf. | ||
| 1305 | Verg. | ||
| 1306 | vergl. | ||
| 1307 | Vergl. | ||
| 1308 | verh. | ||
| 1309 | Vers. | ||
| 1310 | vers. | ||
| 1311 | vert. | ||
| 1312 | Vfg. | ||
| 1313 | vgbl. | ||
| 1314 | vgl. | ||
| 1315 | Vgl. | ||
| 1316 | vh. | ||
| 1317 | viell. | ||
| 1318 | vj. | ||
| 1319 | Vj. | ||
| 1320 | vl. | ||
| 1321 | vlat. | ||
| 1322 | vlt. | ||
| 1323 | vmtl. | ||
| 1324 | volkst. | ||
| 1325 | Vors. | ||
| 1326 | vrstl. | ||
| 1327 | vrt. | ||
| 1328 | vs. | ||
| 1329 | vsl. | ||
| 1330 | vt. | ||
| 1331 | vulg. | ||
| 1332 | vulgärlat. | ||
| 1333 | Vwz. | ||
| 1334 | vzk. | ||
| 1335 | w. | ||
| 1336 | W. | ||
| 1337 | Wa. | ||
| 1338 | wal. | ||
| 1339 | wehrtgl. | ||
| 1340 | weibl. | ||
| 1341 | Weis. | ||
| 1342 | weißruss. | ||
| 1343 | werkt. | ||
| 1344 | westd. | ||
| 1345 | westdt. | ||
| 1346 | Westf. | ||
| 1347 | westfäl. | ||
| 1348 | westgerm. | ||
| 1349 | westl. | ||
| 1350 | westmitteld. | ||
| 1351 | westmitteldt. | ||
| 1352 | Wf. | ||
| 1353 | wf. | ||
| 1354 | Wfl. | ||
| 1355 | wg. | ||
| 1356 | wh. | ||
| 1357 | Whg. | ||
| 1358 | winzerspr. | ||
| 1359 | wirtschaftl. | ||
| 1360 | wiss. | ||
| 1361 | Wkst. | ||
| 1362 | Wkstf. | ||
| 1363 | wkts. | ||
| 1364 | wld. | ||
| 1365 | Wr. | ||
| 1366 | Ws. | ||
| 1367 | Wtb. | ||
| 1368 | Ww. | ||
| 1369 | Wwe. | ||
| 1370 | Wz. | ||
| 1371 | Xerogr. | ||
| 1372 | Xerok. | ||
| 1373 | Xyl. | ||
| 1374 | y. | ||
| 1375 | Y. | ||
| 1376 | yd. | ||
| 1377 | Yd. | ||
| 1378 | Yds. | ||
| 1379 | yds. | ||
| 1380 | Z. | ||
| 1381 | z.B. | ||
| 1382 | za. | ||
| 1383 | Zf. | ||
| 1384 | Zgm. | ||
| 1385 | zgs. | ||
| 1386 | zgst. | ||
| 1387 | zgw. | ||
| 1388 | Zi. | ||
| 1389 | Ziff. | ||
| 1390 | zit. | ||
| 1391 | Zit. | ||
| 1392 | zk. | ||
| 1393 | Zk. | ||
| 1394 | Zool. | ||
| 1395 | zool. | ||
| 1396 | Zssg. | ||
| 1397 | Zssgn. | ||
| 1398 | Ztr. | ||
| 1399 | Zub. | ||
| 1400 | zur. | ||
| 1401 | zus. | ||
| 1402 | zw. | ||
| 1403 | Zz. | ||
| 1404 | zz. | ||
| 1405 | zzgl. | ||
| 1406 | zzt. | ||
| 1407 | ägypt. | ||
| 1408 | Ökol. | ||
| 1409 | ökol. | ||
| 1410 | ökon. | ||
| 1411 | ökum. | ||
| 1412 | örtl. | ||
| 1413 | österr. | ||
| 1414 | Österr. | ||
| 1415 | östl. | ||
| 1416 | übers. | ||
| 1417 | übertr. | ||
| 1418 | überw. | ||
| 1419 | Überw. | ||
| 1420 | übl. | ||
| 1421 | üblw. | ||
| 1422 | Übn. | ||
| 1423 | übsch. | ||
diff --git a/home-modules/pandoc/german_abbreviations.txt.gup b/home-modules/pandoc/german_abbreviations.txt.gup new file mode 100755 index 00000000..abcab1da --- /dev/null +++ b/home-modules/pandoc/german_abbreviations.txt.gup | |||
| @@ -0,0 +1,35 @@ | |||
| 1 | #!/usr/bin/env nix | ||
| 2 | #!nix shell --impure --expr `` | ||
| 3 | #!nix with (import (builtins.getFlake ''nixpkgs'') {}); | ||
| 4 | #!nix python3.withPackages (ps: with ps; [ requests ]) | ||
| 5 | #!nix `` --command python3 | ||
| 6 | |||
| 7 | import requests | ||
| 8 | import json | ||
| 9 | import sys | ||
| 10 | import re | ||
| 11 | import subprocess | ||
| 12 | |||
| 13 | def wiki_cont(url, params): | ||
| 14 | continue_params = None | ||
| 15 | while True: | ||
| 16 | req_params = params | ||
| 17 | if continue_params is not None: | ||
| 18 | req_params |= continue_params | ||
| 19 | json_data = requests.get(url, req_params).json() | ||
| 20 | if "query" in json_data: | ||
| 21 | yield json_data["query"] | ||
| 22 | if "continue" not in json_data: | ||
| 23 | break | ||
| 24 | else: | ||
| 25 | continue_params = json_data["continue"] | ||
| 26 | |||
| 27 | out_re = re.compile(r"[^ ]*[^ 0-9][^ ]*\.") | ||
| 28 | |||
| 29 | subprocess.run(["gup", "--always"], check=True) | ||
| 30 | |||
| 31 | with open(sys.argv[1], 'w') as out: | ||
| 32 | for query in wiki_cont("https://de.wiktionary.org/w/api.php", {"action": "query", "list": "categorymembers", "cmtitle": "Kategorie:Abkürzung_(Deutsch)", "format": "json"}): | ||
| 33 | for item in map(lambda i: i["title"], query["categorymembers"]): | ||
| 34 | if out_re.fullmatch(item): | ||
| 35 | print(item, file=out) | ||
diff --git a/home-modules/quickshell.nix b/home-modules/quickshell.nix new file mode 100644 index 00000000..dac7089f --- /dev/null +++ b/home-modules/quickshell.nix | |||
| @@ -0,0 +1,68 @@ | |||
| 1 | { config, pkgs, lib, ... }: | ||
| 2 | |||
| 3 | let | ||
| 4 | cfg = config.programs.quickshell; | ||
| 5 | in { | ||
| 6 | disabledModules = ["programs/quickshell.nix"]; | ||
| 7 | |||
| 8 | options = { | ||
| 9 | programs.quickshell = { | ||
| 10 | enable = lib.mkEnableOption "quickshell"; | ||
| 11 | package = lib.mkPackageOption pkgs "quickshell" { nullable = true; }; | ||
| 12 | config.src = lib.mkOption { | ||
| 13 | type = lib.types.path; | ||
| 14 | }; | ||
| 15 | config.replacements = lib.mkOption { | ||
| 16 | type = lib.types.attrsOf lib.types.str; | ||
| 17 | default = {}; | ||
| 18 | }; | ||
| 19 | }; | ||
| 20 | }; | ||
| 21 | |||
| 22 | config = lib.mkIf cfg.enable { | ||
| 23 | home.packages = [ cfg.package ]; | ||
| 24 | |||
| 25 | xdg.configFile."quickshell".source = pkgs.stdenvNoCC.mkDerivation { | ||
| 26 | name = "quickshell"; | ||
| 27 | preferLocalBuild = true; | ||
| 28 | allowSubstitutes = false; | ||
| 29 | dontUnpack = true; | ||
| 30 | inherit (cfg.config) src; | ||
| 31 | buildPhase = '' | ||
| 32 | runHook preBuild | ||
| 33 | |||
| 34 | while IFS= read -r -d $'\0' file <&3; do | ||
| 35 | [[ -z $file ]] && continue | ||
| 36 | |||
| 37 | mkdir -p "$out"/"$(dirname "$file")" | ||
| 38 | substitute "$src"/"$file" "$out"/"$file" \ | ||
| 39 | ${lib.concatStringsSep " " ( | ||
| 40 | lib.concatLists (lib.mapAttrsToList (name: value: [ | ||
| 41 | "--replace-quiet" (lib.escapeShellArg "@${name}@") (lib.escapeShellArg value) | ||
| 42 | ]) cfg.config.replacements) | ||
| 43 | )} | ||
| 44 | done 3< <(find "$src" -type f -printf '%P\0') | ||
| 45 | |||
| 46 | runHook postBuild | ||
| 47 | ''; | ||
| 48 | }; | ||
| 49 | |||
| 50 | systemd.user.services.quickshell = { | ||
| 51 | Unit = { | ||
| 52 | Description = "quickshell"; | ||
| 53 | Documentation = "https://quickshell.org/docs/v${cfg.package.version}"; | ||
| 54 | PartOf = [ "graphical-session.target" ]; | ||
| 55 | After = [ "graphical-session-pre.target" ]; | ||
| 56 | }; | ||
| 57 | |||
| 58 | Service = { | ||
| 59 | ExecStart = lib.getExe cfg.package; | ||
| 60 | Restart = "always"; | ||
| 61 | }; | ||
| 62 | |||
| 63 | Install = { | ||
| 64 | WantedBy = [ "graphical-session.target" ]; | ||
| 65 | }; | ||
| 66 | }; | ||
| 67 | }; | ||
| 68 | } | ||
diff --git a/hosts/eostre/default.nix b/hosts/eostre/default.nix index fd4b15f2..d4113024 100644 --- a/hosts/eostre/default.nix +++ b/hosts/eostre/default.nix | |||
| @@ -37,14 +37,10 @@ with lib; | |||
| 37 | powerManagement.enable = true; | 37 | powerManagement.enable = true; |
| 38 | }; | 38 | }; |
| 39 | 39 | ||
| 40 | opengl.enable = true; | 40 | graphics.enable = true; |
| 41 | }; | 41 | }; |
| 42 | 42 | ||
| 43 | environment.etc."machine-id".text = "f457b21333f1491e916521151ff5d468"; | ||
| 44 | |||
| 45 | networking = { | 43 | networking = { |
| 46 | hostId = "f457b213"; | ||
| 47 | |||
| 48 | domain = "lan.yggdrasil"; | 44 | domain = "lan.yggdrasil"; |
| 49 | search = [ "lan.yggdrasil" "yggdrasil" ]; | 45 | search = [ "lan.yggdrasil" "yggdrasil" ]; |
| 50 | 46 | ||
| @@ -83,19 +79,14 @@ with lib; | |||
| 83 | ]; | 79 | ]; |
| 84 | }; | 80 | }; |
| 85 | 81 | ||
| 86 | 82 | services.displayManager.sddm = { | |
| 87 | services.xserver = { | ||
| 88 | enable = true; | 83 | enable = true; |
| 89 | displayManager.sddm = { | 84 | wayland.enable = true; |
| 90 | enable = true; | 85 | settings = { |
| 91 | settings = { | 86 | Users.HideUsers = "gkleen"; |
| 92 | Users.HideUsers = "gkleen"; | ||
| 93 | }; | ||
| 94 | }; | 87 | }; |
| 95 | desktopManager.plasma5.enable = true; | ||
| 96 | |||
| 97 | videoDrivers = [ "nvidia" ]; | ||
| 98 | }; | 88 | }; |
| 89 | services.desktopManager.plasma6.enable = true; | ||
| 99 | 90 | ||
| 100 | 91 | ||
| 101 | services.openssh = { | 92 | services.openssh = { |
diff --git a/hosts/sif/default.nix b/hosts/sif/default.nix index 7c8da63a..fb2dddc6 100644 --- a/hosts/sif/default.nix +++ b/hosts/sif/default.nix | |||
| @@ -12,11 +12,9 @@ let | |||
| 12 | in { | 12 | in { |
| 13 | imports = with flake.nixosModules.systemProfiles; [ | 13 | imports = with flake.nixosModules.systemProfiles; [ |
| 14 | ./hw.nix | 14 | ./hw.nix |
| 15 | ./mail ./libvirt | 15 | ./email ./libvirt ./greetd |
| 16 | tmpfs-root bcachefs initrd-all-crypto-modules default-locale openssh rebuild-machines | 16 | tmpfs-root bcachefs initrd-all-crypto-modules default-locale openssh rebuild-machines niri-unstable networkmanager lanzaboote |
| 17 | networkmanager | ||
| 18 | flakeInputs.nixos-hardware.nixosModules.lenovo-thinkpad-p1 | 17 | flakeInputs.nixos-hardware.nixosModules.lenovo-thinkpad-p1 |
| 19 | flakeInputs.impermanence.nixosModules.impermanence | ||
| 20 | flakeInputs.nixVirt.nixosModules.default | 18 | flakeInputs.nixVirt.nixosModules.default |
| 21 | ]; | 19 | ]; |
| 22 | 20 | ||
| @@ -34,8 +32,11 @@ in { | |||
| 34 | boot = { | 32 | boot = { |
| 35 | initrd = { | 33 | initrd = { |
| 36 | systemd = { | 34 | systemd = { |
| 37 | enable = false; | ||
| 38 | emergencyAccess = config.users.users.root.hashedPassword; | 35 | emergencyAccess = config.users.users.root.hashedPassword; |
| 36 | extraBin = { | ||
| 37 | "vim" = lib.getExe pkgs.vim; | ||
| 38 | "grep" = lib.getExe pkgs.gnugrep; | ||
| 39 | }; | ||
| 39 | }; | 40 | }; |
| 40 | luks.devices = { | 41 | luks.devices = { |
| 41 | nvm0 = { device = "/dev/disk/by-uuid/bef17e86-d929-4a60-97cb-6bfa133face7"; bypassWorkqueues = true; }; | 42 | nvm0 = { device = "/dev/disk/by-uuid/bef17e86-d929-4a60-97cb-6bfa133face7"; bypassWorkqueues = true; }; |
| @@ -49,12 +50,8 @@ in { | |||
| 49 | 50 | ||
| 50 | blacklistedKernelModules = [ "nouveau" ]; | 51 | blacklistedKernelModules = [ "nouveau" ]; |
| 51 | 52 | ||
| 52 | # Use the systemd-boot EFI boot loader. | 53 | lanzaboote.configurationLimit = 15; |
| 53 | loader = { | 54 | loader = { |
| 54 | systemd-boot = { | ||
| 55 | enable = true; | ||
| 56 | configurationLimit = 15; | ||
| 57 | }; | ||
| 58 | efi.canTouchEfiVariables = true; | 55 | efi.canTouchEfiVariables = true; |
| 59 | timeout = null; | 56 | timeout = null; |
| 60 | }; | 57 | }; |
| @@ -62,16 +59,29 @@ in { | |||
| 62 | plymouth.enable = true; | 59 | plymouth.enable = true; |
| 63 | 60 | ||
| 64 | kernelPackages = pkgs.linuxPackages_latest; | 61 | kernelPackages = pkgs.linuxPackages_latest; |
| 65 | extraModulePackages = with config.boot.kernelPackages; [ v4l2loopback ]; | ||
| 66 | kernelModules = ["v4l2loopback"]; | ||
| 67 | kernelPatches = [ | 62 | kernelPatches = [ |
| 68 | { name = "edac-config"; | 63 | { name = "edac-config"; |
| 69 | patch = null; | 64 | patch = null; |
| 70 | extraConfig = '' | 65 | structuredExtraConfig = with lib.kernel; { |
| 71 | EDAC y | 66 | EDAC = yes; |
| 72 | EDAC_IE31200 y | 67 | EDAC_IE31200 = yes; |
| 73 | ''; | 68 | }; |
| 74 | } | 69 | } |
| 70 | { name = "zswap-default"; | ||
| 71 | patch = null; | ||
| 72 | structuredExtraConfig = with lib.kernel; { | ||
| 73 | ZSWAP_DEFAULT_ON = yes; | ||
| 74 | ZSWAP_SHRINKER_DEFAULT_ON = yes; | ||
| 75 | }; | ||
| 76 | } | ||
| 77 | ]; | ||
| 78 | consoleLogLevel = 3; | ||
| 79 | kernelParams = [ | ||
| 80 | "quiet" | ||
| 81 | "boot.shell_on_fail" | ||
| 82 | "udev.log_priority=3" | ||
| 83 | "rd.systemd.show_status=auto" | ||
| 84 | "plymouth.use-simpledrm" | ||
| 75 | ]; | 85 | ]; |
| 76 | 86 | ||
| 77 | tmp.useTmpfs = true; | 87 | tmp.useTmpfs = true; |
| @@ -94,6 +104,8 @@ in { | |||
| 94 | server ptbtime2.ptb.de prefer iburst nts | 104 | server ptbtime2.ptb.de prefer iburst nts |
| 95 | server ptbtime3.ptb.de prefer iburst nts | 105 | server ptbtime3.ptb.de prefer iburst nts |
| 96 | server ptbtime4.ptb.de prefer iburst nts | 106 | server ptbtime4.ptb.de prefer iburst nts |
| 107 | pool ntppool1.time.nl prefer iburst nts | ||
| 108 | pool ntppool2.time.nl prefer iburst nts | ||
| 97 | 109 | ||
| 98 | authselectmode require | 110 | authselectmode require |
| 99 | minsources 3 | 111 | minsources 3 |
| @@ -122,40 +134,16 @@ in { | |||
| 122 | rulesetFile = ./ruleset.nft; | 134 | rulesetFile = ./ruleset.nft; |
| 123 | }; | 135 | }; |
| 124 | 136 | ||
| 125 | # firewall = { | ||
| 126 | # enable = true; | ||
| 127 | # allowedTCPPorts = [ 22 # ssh | ||
| 128 | # 8000 # quickserve | ||
| 129 | # ]; | ||
| 130 | # }; | ||
| 131 | |||
| 132 | # wlanInterfaces = { | ||
| 133 | # wlan0 = { | ||
| 134 | # device = "wlp82s0"; | ||
| 135 | # }; | ||
| 136 | # }; | ||
| 137 | |||
| 138 | # bonds = { | ||
| 139 | # "lan" = { | ||
| 140 | # interfaces = [ "wlan0" "enp0s31f6" "dock0" ]; | ||
| 141 | # driverOptions = { | ||
| 142 | # miimon = "1000"; | ||
| 143 | # mode = "active-backup"; | ||
| 144 | # primary_reselect = "always"; | ||
| 145 | # }; | ||
| 146 | # }; | ||
| 147 | # }; | ||
| 148 | |||
| 149 | useDHCP = false; | 137 | useDHCP = false; |
| 150 | useNetworkd = true; | 138 | useNetworkd = true; |
| 151 | |||
| 152 | # interfaces."tinc.yggdrasil" = { | ||
| 153 | # virtual = true; | ||
| 154 | # virtualType = config.services.tinc.networks.yggdrasil.interfaceType; | ||
| 155 | # macAddress = "5c:93:21:c3:61:39"; | ||
| 156 | # }; | ||
| 157 | }; | 139 | }; |
| 158 | 140 | ||
| 141 | environment.etc."NetworkManager/dnsmasq.d/dnssec.conf" = { | ||
| 142 | text = '' | ||
| 143 | conf-file=${pkgs.dnsmasq}/share/dnsmasq/trust-anchors.conf | ||
| 144 | dnssec | ||
| 145 | ''; | ||
| 146 | }; | ||
| 159 | environment.etc."NetworkManager/dnsmasq.d/libvirt_dnsmasq.conf" = { | 147 | environment.etc."NetworkManager/dnsmasq.d/libvirt_dnsmasq.conf" = { |
| 160 | text = '' | 148 | text = '' |
| 161 | except-interface=virbr0 | 149 | except-interface=virbr0 |
| @@ -398,19 +386,6 @@ in { | |||
| 398 | ]; | 386 | ]; |
| 399 | 387 | ||
| 400 | services = { | 388 | services = { |
| 401 | uucp = { | ||
| 402 | enable = true; | ||
| 403 | nodeName = "sif"; | ||
| 404 | remoteNodes = { | ||
| 405 | "ymir" = { | ||
| 406 | publicKeys = ["ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIG6KNtsCOl5fsZ4rV7udTulGMphJweLBoKapzerWNoLY root@ymir"]; | ||
| 407 | hostnames = ["ymir.yggdrasil.li" "ymir.niflheim.yggdrasil"]; | ||
| 408 | }; | ||
| 409 | }; | ||
| 410 | |||
| 411 | defaultCommands = lib.mkForce []; | ||
| 412 | }; | ||
| 413 | |||
| 414 | avahi.enable = true; | 389 | avahi.enable = true; |
| 415 | 390 | ||
| 416 | fwupd.enable = true; | 391 | fwupd.enable = true; |
| @@ -429,8 +404,8 @@ in { | |||
| 429 | 404 | ||
| 430 | logind = { | 405 | logind = { |
| 431 | lidSwitch = "suspend"; | 406 | lidSwitch = "suspend"; |
| 432 | lidSwitchDocked = "lock"; | 407 | lidSwitchDocked = "ignore"; |
| 433 | lidSwitchExternalPower = "lock"; | 408 | lidSwitchExternalPower = "ignore"; |
| 434 | }; | 409 | }; |
| 435 | 410 | ||
| 436 | atd = { | 411 | atd = { |
| @@ -439,7 +414,7 @@ in { | |||
| 439 | }; | 414 | }; |
| 440 | 415 | ||
| 441 | xserver = { | 416 | xserver = { |
| 442 | enable = true; | 417 | enable = false; |
| 443 | 418 | ||
| 444 | xkb = { | 419 | xkb = { |
| 445 | layout = "us"; | 420 | layout = "us"; |
| @@ -465,47 +440,13 @@ in { | |||
| 465 | }; | 440 | }; |
| 466 | libinput.enable = true; | 441 | libinput.enable = true; |
| 467 | 442 | ||
| 468 | greetd = { | 443 | envfs.enable = false; |
| 469 | enable = true; | ||
| 470 | # settings.default_session.command = let | ||
| 471 | # cfg = config.programs.regreet; | ||
| 472 | # in pkgs.writeShellScript "greeter" '' | ||
| 473 | # modprobe -r nvidia_drm | ||
| 474 | 444 | ||
| 475 | # exec ${pkgs.dbus}/bin/dbus-run-session ${lib.getExe pkgs.cage} ${lib.escapeShellArgs cfg.cageArgs} -- ${lib.getExe cfg.package} | 445 | displayManager.defaultSession = "Niri"; |
| 476 | # ''; | ||
| 477 | }; | ||
| 478 | }; | 446 | }; |
| 479 | 447 | ||
| 480 | programs.regreet = { | ||
| 481 | enable = true; | ||
| 482 | theme = { | ||
| 483 | package = pkgs.equilux-theme; | ||
| 484 | name = "Equilux-compact"; | ||
| 485 | }; | ||
| 486 | iconTheme = { | ||
| 487 | package = pkgs.paper-icon-theme; | ||
| 488 | name = "Paper-Mono-Dark"; | ||
| 489 | }; | ||
| 490 | font = { | ||
| 491 | package = pkgs.fira; | ||
| 492 | name = "Fira Sans"; | ||
| 493 | # size = 6; | ||
| 494 | }; | ||
| 495 | cageArgs = [ "-s" "-m" "last" ]; | ||
| 496 | settings = { | ||
| 497 | GTK.application_prefer_dark_theme = true; | ||
| 498 | }; | ||
| 499 | }; | ||
| 500 | programs.hyprland.enable = true; | ||
| 501 | |||
| 502 | systemd.tmpfiles.settings = { | 448 | systemd.tmpfiles.settings = { |
| 503 | "10-localtime"."/etc/localtime".L.argument = "/.bcachefs/etc/localtime"; | 449 | "10-localtime"."/etc/localtime".L.argument = "/.bcachefs/etc/localtime"; |
| 504 | |||
| 505 | "10-regreet"."/var/cache/regreet/cache.toml".C.argument = toString ((pkgs.formats.toml {}).generate "cache.toml" { | ||
| 506 | last_user = "gkleen"; | ||
| 507 | user_to_last_sess.gkleen = "Hyprland"; | ||
| 508 | }); | ||
| 509 | }; | 450 | }; |
| 510 | 451 | ||
| 511 | users = { | 452 | users = { |
| @@ -614,15 +555,15 @@ in { | |||
| 614 | }; | 555 | }; |
| 615 | 556 | ||
| 616 | nvidia = { | 557 | nvidia = { |
| 617 | open = true; | 558 | open = false; |
| 618 | modesetting.enable = true; | 559 | modesetting.enable = true; |
| 619 | powerManagement.enable = true; | 560 | powerManagement.enable = true; |
| 620 | prime = { | 561 | # prime = { |
| 621 | nvidiaBusId = "PCI:1:0:0"; | 562 | # nvidiaBusId = "PCI:1:0:0"; |
| 622 | intelBusId = "PCI:0:2:0"; | 563 | # intelBusId = "PCI:0:2:0"; |
| 623 | reverseSync.enable = true; | 564 | # reverseSync.enable = true; |
| 624 | offload.enableOffloadCmd = true; | 565 | # offload.enableOffloadCmd = true; |
| 625 | }; | 566 | # }; |
| 626 | }; | 567 | }; |
| 627 | 568 | ||
| 628 | graphics = { | 569 | graphics = { |
| @@ -665,25 +606,6 @@ in { | |||
| 665 | 606 | ||
| 666 | environment.etc."X11/xorg.conf.d/50-wacom.conf".source = lib.mkForce ./wacom.conf; | 607 | environment.etc."X11/xorg.conf.d/50-wacom.conf".source = lib.mkForce ./wacom.conf; |
| 667 | 608 | ||
| 668 | systemd.services."ac-plugged" = { | ||
| 669 | description = "Inhibit handling of lid-switch and sleep"; | ||
| 670 | |||
| 671 | path = with pkgs; [ systemd coreutils ]; | ||
| 672 | |||
| 673 | script = '' | ||
| 674 | exec systemd-inhibit --what=handle-lid-switch --why="AC is connected" --mode=block sleep infinity | ||
| 675 | ''; | ||
| 676 | |||
| 677 | serviceConfig = { | ||
| 678 | Type = "simple"; | ||
| 679 | }; | ||
| 680 | }; | ||
| 681 | |||
| 682 | services.udev.extraRules = with pkgs; lib.mkAfter '' | ||
| 683 | SUBSYSTEM=="power_supply", ENV{POWER_SUPPLY_ONLINE}=="0", RUN+="${systemd}/bin/systemctl --no-block stop ac-plugged.service" | ||
| 684 | SUBSYSTEM=="power_supply", ENV{POWER_SUPPLY_ONLINE}=="1", RUN+="${systemd}/bin/systemctl --no-block start ac-plugged.service" | ||
| 685 | ''; | ||
| 686 | |||
| 687 | systemd.services."nix-daemon".serviceConfig = { | 609 | systemd.services."nix-daemon".serviceConfig = { |
| 688 | MemoryAccounting = true; | 610 | MemoryAccounting = true; |
| 689 | MemoryHigh = "50%"; | 611 | MemoryHigh = "50%"; |
| @@ -696,6 +618,7 @@ in { | |||
| 696 | 618 | ||
| 697 | services.dbus.packages = with pkgs; | 619 | services.dbus.packages = with pkgs; |
| 698 | [ dbus dconf | 620 | [ dbus dconf |
| 621 | xdg-desktop-portal-gtk | ||
| 699 | ]; | 622 | ]; |
| 700 | 623 | ||
| 701 | services.udisks2.enable = true; | 624 | services.udisks2.enable = true; |
| @@ -704,12 +627,12 @@ in { | |||
| 704 | light.enable = true; | 627 | light.enable = true; |
| 705 | wireshark.enable = true; | 628 | wireshark.enable = true; |
| 706 | dconf.enable = true; | 629 | dconf.enable = true; |
| 707 | }; | 630 | niri.enable = true; |
| 708 | 631 | fuse.userAllowOther = true; | |
| 709 | zramSwap = { | 632 | captive-browser = { |
| 710 | enable = true; | 633 | enable = true; |
| 711 | algorithm = "zstd"; | 634 | interface = "wlp82s0"; |
| 712 | writebackDevice = "/dev/disk/by-label/swap"; | 635 | }; |
| 713 | }; | 636 | }; |
| 714 | 637 | ||
| 715 | services.pcscd.enable = true; | 638 | services.pcscd.enable = true; |
| @@ -729,6 +652,16 @@ in { | |||
| 729 | environment.sessionVariables."GTK_USE_PORTAL" = "1"; | 652 | environment.sessionVariables."GTK_USE_PORTAL" = "1"; |
| 730 | xdg.portal = { | 653 | xdg.portal = { |
| 731 | enable = true; | 654 | enable = true; |
| 655 | extraPortals = with pkgs; [ xdg-desktop-portal-gtk ]; | ||
| 656 | config.niri = { | ||
| 657 | default = ["gnome" "gtk"]; | ||
| 658 | "org.freedesktop.impl.portal.FileChooser" = ["gtk"]; | ||
| 659 | "org.freedesktop.impl.portal.OpenFile" = ["gtk"]; | ||
| 660 | "org.freedesktop.impl.portal.Access" = ["gtk"]; | ||
| 661 | "org.freedesktop.impl.portal.Notification" = ["gtk"]; | ||
| 662 | "org.freedesktop.impl.portal.Secret" = ["none"]; | ||
| 663 | "org.freedesktop.impl.portal.Inhibit" = ["none"]; | ||
| 664 | }; | ||
| 732 | }; | 665 | }; |
| 733 | 666 | ||
| 734 | environment.persistence."/.bcachefs" = { | 667 | environment.persistence."/.bcachefs" = { |
| @@ -736,36 +669,26 @@ in { | |||
| 736 | directories = [ | 669 | directories = [ |
| 737 | "/nix" | 670 | "/nix" |
| 738 | "/root" | 671 | "/root" |
| 672 | "/home" | ||
| 739 | "/var/log" | 673 | "/var/log" |
| 740 | "/var/lib/sops-nix" | 674 | "/var/lib/sops-nix" |
| 741 | "/var/lib/nixos" | 675 | "/var/lib/nixos" |
| 742 | "/var/lib/systemd" | 676 | "/var/lib/systemd" |
| 743 | "/home" | ||
| 744 | "/var/lib/chrony" | 677 | "/var/lib/chrony" |
| 745 | "/var/lib/fprint" | 678 | "/var/lib/fprint" |
| 746 | "/var/lib/bluetooth" | 679 | "/var/lib/bluetooth" |
| 747 | "/var/lib/upower" | 680 | "/var/lib/upower" |
| 748 | "/var/lib/postfix" | 681 | "/var/lib/postfix" |
| 682 | "/var/lib/regreet" | ||
| 749 | "/etc/NetworkManager/system-connections" | 683 | "/etc/NetworkManager/system-connections" |
| 750 | { directory = "/var/uucp"; user = "uucp"; group = "uucp"; mode = "0700"; } | 684 | config.boot.lanzaboote.pkiBundle |
| 751 | { directory = "/var/spool/uucp"; user = "uucp"; group = "uucp"; mode = "0750"; } | ||
| 752 | ]; | 685 | ]; |
| 753 | files = [ | 686 | files = [ |
| 754 | ]; | 687 | ]; |
| 688 | timezone = true; | ||
| 755 | }; | 689 | }; |
| 756 | 690 | ||
| 757 | systemd.services.timezone = { | 691 | security.pam.services.quickshell = {}; |
| 758 | wantedBy = [ "multi-user.target" ]; | ||
| 759 | serviceConfig = { | ||
| 760 | Type = "oneshot"; | ||
| 761 | RemainAfterExit = true; | ||
| 762 | ExecStart = "${pkgs.coreutils}/bin/cp -vP /.bcachefs/etc/localtime /etc/localtime"; | ||
| 763 | ExecStop = "${pkgs.coreutils}/bin/cp -vP /etc/localtime /.bcachefs/etc/localtime"; | ||
| 764 | }; | ||
| 765 | }; | ||
| 766 | services.tzupdate.enable = true; | ||
| 767 | |||
| 768 | security.pam.services.gtklock = {}; | ||
| 769 | 692 | ||
| 770 | home-manager.sharedModules = [ flakeInputs.nixVirt.homeModules.default ]; | 693 | home-manager.sharedModules = [ flakeInputs.nixVirt.homeModules.default ]; |
| 771 | 694 | ||
diff --git a/hosts/sif/email/default.nix b/hosts/sif/email/default.nix new file mode 100644 index 00000000..bebf7980 --- /dev/null +++ b/hosts/sif/email/default.nix | |||
| @@ -0,0 +1,111 @@ | |||
| 1 | { config, lib, pkgs, ... }: | ||
| 2 | { | ||
| 3 | services.postfix = { | ||
| 4 | enable = true; | ||
| 5 | enableSmtp = false; | ||
| 6 | enableSubmission = false; | ||
| 7 | setSendmail = true; | ||
| 8 | # networksStyle = "host"; | ||
| 9 | settings.main = { | ||
| 10 | recpipient_delimiter = "+"; | ||
| 11 | mydestination = []; | ||
| 12 | myhostname = "sif.midgard.yggdrasil"; | ||
| 13 | |||
| 14 | mydomain = "yggdrasil.li"; | ||
| 15 | |||
| 16 | local_transport = "error:5.1.1 No local delivery"; | ||
| 17 | alias_database = []; | ||
| 18 | alias_maps = []; | ||
| 19 | local_recipient_maps = []; | ||
| 20 | |||
| 21 | inet_interfaces = "loopback-only"; | ||
| 22 | |||
| 23 | message_size_limit = 0; | ||
| 24 | |||
| 25 | authorized_submit_users = "inline:{ gkleen= }"; | ||
| 26 | authorized_flush_users = "inline:{ gkleen= }"; | ||
| 27 | authorized_mailq_users = "inline:{ gkleen= }"; | ||
| 28 | |||
| 29 | smtp_generic_maps = "inline:{ root=root+sif }"; | ||
| 30 | |||
| 31 | mynetworks = ["127.0.0.0/8" "[::1]/128"]; | ||
| 32 | smtpd_client_restrictions = ["permit_mynetworks" "reject"]; | ||
| 33 | smtpd_relay_restrictions = ["permit_mynetworks" "reject"]; | ||
| 34 | |||
| 35 | sender_dependent_default_transport_maps = ''regexp:${pkgs.writeText "sender_relay" '' | ||
| 36 | /@(cip|stud)\.ifi\.(lmu|uni-muenchen)\.de$/ smtp:smtp.ifi.lmu.de | ||
| 37 | /@ifi\.(lmu|uni-muenchen)\.de$/ smtp:smtpin1.ifi.lmu.de:587 | ||
| 38 | /@math(ematik)?\.(lmu|uni-muenchen)\.de$/ smtps:smtp.math.lmu.de:465 | ||
| 39 | /@(campus\.)?lmu\.de$/ smtp:postout.lrz.de | ||
| 40 | ''}''; | ||
| 41 | sender_bcc_maps = ''regexp:${pkgs.writeText "sender_bcc" '' | ||
| 42 | /^uni2work(-[^@]*)?@ifi\.lmu\.de$/ uni2work@ifi.lmu.de | ||
| 43 | /@ifi\.lmu\.de$/ gregor.kleen@ifi.lmu.de | ||
| 44 | ''}''; | ||
| 45 | relayhost = ["[surtr.yggdrasil.li]:465"]; | ||
| 46 | default_transport = "relay"; | ||
| 47 | |||
| 48 | smtp_sasl_auth_enable = true; | ||
| 49 | smtp_sender_dependent_authentication = true; | ||
| 50 | smtp_sasl_tls_security_options = "noanonymous"; | ||
| 51 | smtp_sasl_mechanism_filter = ["plain"]; | ||
| 52 | smtp_sasl_password_maps = "regexp:/run/credentials/postfix.service/sasl_passwd"; | ||
| 53 | smtp_cname_overrides_servername = false; | ||
| 54 | smtp_always_send_ehlo = true; | ||
| 55 | smtp_tls_security_level = "dane"; | ||
| 56 | |||
| 57 | smtp_tls_loglevel = "1"; | ||
| 58 | smtp_dns_support_level = "dnssec"; | ||
| 59 | }; | ||
| 60 | settings.master = { | ||
| 61 | submission = { | ||
| 62 | type = "inet"; | ||
| 63 | private = false; | ||
| 64 | command = "smtpd"; | ||
| 65 | args = [ | ||
| 66 | "-o" "syslog_name=postfix/$service_name" | ||
| 67 | ]; | ||
| 68 | }; | ||
| 69 | smtp = { }; | ||
| 70 | smtps = { | ||
| 71 | type = "unix"; | ||
| 72 | private = true; | ||
| 73 | privileged = true; | ||
| 74 | chroot = false; | ||
| 75 | command = "smtp"; | ||
| 76 | args = [ | ||
| 77 | "-o" "smtp_tls_wrappermode=yes" | ||
| 78 | "-o" "smtp_tls_security_level=encrypt" | ||
| 79 | ]; | ||
| 80 | }; | ||
| 81 | relay = { | ||
| 82 | command = "smtp"; | ||
| 83 | args = [ | ||
| 84 | "-o" "smtp_fallback_relay=" | ||
| 85 | "-o" "smtp_tls_security_level=verify" | ||
| 86 | "-o" "smtp_tls_wrappermode=yes" | ||
| 87 | "-o" "smtp_tls_cert_file=${./relay.crt}" | ||
| 88 | "-o" "smtp_tls_key_file=/run/credentials/postfix.service/relay.key" | ||
| 89 | ]; | ||
| 90 | }; | ||
| 91 | }; | ||
| 92 | }; | ||
| 93 | |||
| 94 | systemd.services.postfix = { | ||
| 95 | serviceConfig.LoadCredential = [ | ||
| 96 | "sasl_passwd:${config.sops.secrets."postfix-sasl-passwd".path}" | ||
| 97 | "relay.key:${config.sops.secrets."relay-key".path}" | ||
| 98 | ]; | ||
| 99 | }; | ||
| 100 | |||
| 101 | sops.secrets = { | ||
| 102 | postfix-sasl-passwd = { | ||
| 103 | key = "sasl-passwd"; | ||
| 104 | sopsFile = ./secrets.yaml; | ||
| 105 | }; | ||
| 106 | relay-key = { | ||
| 107 | format = "binary"; | ||
| 108 | sopsFile = ./relay.key; | ||
| 109 | }; | ||
| 110 | }; | ||
| 111 | } | ||
diff --git a/hosts/sif/email/relay.crt b/hosts/sif/email/relay.crt new file mode 100644 index 00000000..ac13e7cb --- /dev/null +++ b/hosts/sif/email/relay.crt | |||
| @@ -0,0 +1,11 @@ | |||
| 1 | -----BEGIN CERTIFICATE----- | ||
| 2 | MIIBjDCCAQygAwIBAgIPQAAAAGgLfNoL/PSMAsutMAUGAytlcTAXMRUwEwYDVQQD | ||
| 3 | DAx5Z2dkcmFzaWwubGkwHhcNMjUwNDI1MTIwOTQ1WhcNMzUwNDI2MTIxNDQ1WjAR | ||
| 4 | MQ8wDQYDVQQDDAZna2xlZW4wKjAFBgMrZXADIQB3outi3/3F4YO7Q97WAAaMHW0a | ||
| 5 | m+Blldrgee+EZnWnD6N1MHMwHwYDVR0jBBgwFoAUTtn+VjMw6Ge1f68KD8dT1CWn | ||
| 6 | l3YwHQYDVR0OBBYEFFOa4rYZYMbXUVdKv98NB504GUhjMA4GA1UdDwEB/wQEAwID | ||
| 7 | 6DAMBgNVHRMBAf8EAjAAMBMGA1UdJQQMMAoGCCsGAQUFBwMCMAUGAytlcQNzABC0 | ||
| 8 | 0UgIt7gLZrU1TmzGoqPBris8R1DbKOJacicF5CU0MIIjHcX7mPFW8KtB4qm6KcPq | ||
| 9 | kF6IaEPmgKpX3Nubk8HJik9vhIy9ysfINcVTvzXx8pO1bxbvREJRyA/apj10nzav | ||
| 10 | yauId0cXHvN6g5RLAMsMAA== | ||
| 11 | -----END CERTIFICATE----- | ||
diff --git a/hosts/sif/email/relay.key b/hosts/sif/email/relay.key new file mode 100644 index 00000000..412a44e0 --- /dev/null +++ b/hosts/sif/email/relay.key | |||
| @@ -0,0 +1,19 @@ | |||
| 1 | { | ||
| 2 | "data": "ENC[AES256_GCM,data:lBlTuzOS75pvRmcTKT4KhHMH44RlE2SvCFAUP+GfsXws1Uai7DZ1MmbhvxxCa+pcLW19+sQYxrXLRNZWby1yOeKBJ2UQeYV5LOk9LSL/WIE3FZkCo5Dv0O0gSFKjjb61WN22a4JnHbLWADf/mLT3GZv91XfvFDo=,iv:ho8wQH3UNzX9JPW5gVcUGtxZzdVwsMFus0Z4KYe5t48=,tag:dAgZyHOva2xVVhE1nTl+lg==,type:str]", | ||
| 3 | "sops": { | ||
| 4 | "age": [ | ||
| 5 | { | ||
| 6 | "recipient": "age1rmmhetcmllq0ahl5qznlr0eya2zdxwl9h6y5wnl97d2wtyx5t99sm2u866", | ||
| 7 | "enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSB6eTVRSUdFNUZGZmcxSUlT\nWmlsOGNyWXIzMGNTZjlKbXlhcEdZUXFRVkR3Cll0T0RMd0h2UW16QkR3SHlhYmNZ\nNDFrYXh3Rkp5NWsvcWc3UFJJaHVwT1UKLS0tIHhXVEI0VHBZVkpDQ1FzWENjMmJH\nb1FQWXVUUTBiZ1pKWG00MTNqVEo2SjAKK3VOU+QgRuxWYWEcrJiVMRFCprBICz4F\ngD+9zuPUzPezyJkYwTs+M+wX5GYkXppqm5W58yQLS2UDD38sr+SRjg==\n-----END AGE ENCRYPTED FILE-----\n" | ||
| 8 | }, | ||
| 9 | { | ||
| 10 | "recipient": "age1fj65apkhfkrwyv5tx6zcs9nkjg8267fy733qph30sc7zfn7vapjqkd5kne", | ||
| 11 | "enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBzWmJmZDVFazN2bDY1TkNG\nNXpJN2twMFFjZUxMTVdSNzJwQTFiYktrcGdrCjk4eFVHTko0bFVMSlFFWm9tbjMr\nbWNHMEQ1Rm1qUVhodlB1RGw2aDc4TUEKLS0tIERBK0J5NkN4OXJEZ1ZOZXhNc1Jm\naWNnUmZGbTIxdmNkYi9TZ2h2bGs3MVEKPQGaEf7M/5/xvSOfawpIp50fB3QfFSuz\nPgkrPMneaBeUx+uBYMyEFX4rpzLIBR3pnYMjAfoc+bjWaOtGQuEqyQ==\n-----END AGE ENCRYPTED FILE-----\n" | ||
| 12 | } | ||
| 13 | ], | ||
| 14 | "lastmodified": "2025-04-25T12:14:44Z", | ||
| 15 | "mac": "ENC[AES256_GCM,data:pObl2bJA93az9E3Ya+hA3ekI8TKKZ9NNTi0KzmWZBOiQwi9FuQYtpnmmT80L1KXWyOKJV6wGdAri3mNe/ue2S0TziSbQ/4+Dj4ubFKgkH7thb5q2dFyxw5FzhYzRQiXFqD/pxcNN9uL0lQI2Al0Eci0zX8Kcd1rAQ6RzLEoSmco=,iv:zo/3QFKTUEDxLy1k5yyU7Z1JMZ7cKdYUc6GHjaTTZKQ=,tag:f63Eja3lBfwJCYAOyEt56g==,type:str]", | ||
| 16 | "unencrypted_suffix": "_unencrypted", | ||
| 17 | "version": "3.10.2" | ||
| 18 | } | ||
| 19 | } | ||
diff --git a/hosts/sif/mail/secrets.yaml b/hosts/sif/email/secrets.yaml index 3c74b710..3c74b710 100644 --- a/hosts/sif/mail/secrets.yaml +++ b/hosts/sif/email/secrets.yaml | |||
diff --git a/hosts/sif/greetd/default.nix b/hosts/sif/greetd/default.nix new file mode 100644 index 00000000..081b6346 --- /dev/null +++ b/hosts/sif/greetd/default.nix | |||
| @@ -0,0 +1,92 @@ | |||
| 1 | { config, pkgs, lib, flakeInputs, ... }: | ||
| 2 | |||
| 3 | let | ||
| 4 | gkleenConfig = config.home-manager.users."gkleen"; | ||
| 5 | toIni = lib.generators.toINI { | ||
| 6 | mkKeyValue = | ||
| 7 | key: value: | ||
| 8 | let | ||
| 9 | value' = if lib.isBool value then lib.boolToString value else toString value; | ||
| 10 | in | ||
| 11 | "${lib.escape [ "=" ] key}=${value'}"; | ||
| 12 | }; | ||
| 13 | toDconfIni = let | ||
| 14 | gvariant = import (flakeInputs.home-manager + "/modules/lib/gvariant.nix") { inherit lib; }; | ||
| 15 | mkIniKeyValue = key: value: "${key}=${toString (gvariant.mkValue value)}"; | ||
| 16 | in lib.generators.toINI { mkKeyValue = mkIniKeyValue; }; | ||
| 17 | in { | ||
| 18 | config = { | ||
| 19 | services.greetd = { | ||
| 20 | enable = true; | ||
| 21 | settings.default_session.command = lib.getExe (pkgs.writeShellApplication { | ||
| 22 | name = "sway"; | ||
| 23 | runtimeInputs = [ pkgs.sway pkgs.fontconfig ]; | ||
| 24 | runtimeEnv = { | ||
| 25 | XDG_DATA_DIRS = lib.makeSearchPath "share" [ | ||
| 26 | pkgs.equilux-theme pkgs.paper-icon-theme pkgs.fira | ||
| 27 | ]; | ||
| 28 | QT_PLUGIN_PATH = lib.makeSearchPath (pkgs.qt6.qtbase.qtPluginPrefix) [ | ||
| 29 | pkgs.qt6Packages.qtbase | ||
| 30 | ]; | ||
| 31 | QML2_IMPORT_PATH = lib.makeSearchPath (pkgs.qt6.qtbase.qtQmlPrefix) [ | ||
| 32 | pkgs.qt6Packages.qtbase | ||
| 33 | ]; | ||
| 34 | QT_QPA_PLATFORMTHEME = "gtk3"; | ||
| 35 | XDG_CONFIG_DIR = pkgs.symlinkJoin { | ||
| 36 | name = "config"; | ||
| 37 | paths = [ | ||
| 38 | (pkgs.writeTextDir "gtk-3.0/settings.ini" (toIni { | ||
| 39 | Settings = { | ||
| 40 | gtk-font-name = "Fira Sans 10"; | ||
| 41 | gtk-theme-name = "Equilux-compact"; | ||
| 42 | gtk-icon-theme-name = "Paper-Mono-Dark"; | ||
| 43 | }; | ||
| 44 | })) | ||
| 45 | ]; | ||
| 46 | }; | ||
| 47 | # XDG_CACHE_HOME = "/var/cache/greetd/greeter"; | ||
| 48 | # XDG_CONFIG_HOME = "/var/cache/greetd/greeter/config"; | ||
| 49 | }; | ||
| 50 | text = '' | ||
| 51 | exec &>/tmp/sway-$$.log | ||
| 52 | |||
| 53 | unset MANAGERPID SYSTEMD_EXEC_PID | ||
| 54 | |||
| 55 | # ${lib.getExe' pkgs.coreutils "mkdir"} -p ''${XDG_CONFIG_HOME}/dconf | ||
| 56 | ${lib.getExe pkgs.dconf} load / < ${pkgs.writeText "dconf.ini" (toDconfIni { | ||
| 57 | "org/gnome/desktop/interface" = { | ||
| 58 | "color-scheme" = "prefer-dark"; | ||
| 59 | "font-name" = "Fira Sans 10"; | ||
| 60 | "gtk-theme" = "Equilux-compact"; | ||
| 61 | "icon-theme" = "Paper-Mono-Dark"; | ||
| 62 | }; | ||
| 63 | })} | ||
| 64 | |||
| 65 | exec sway --unsupported-gpu --config ${pkgs.writeText "sway-config" '' | ||
| 66 | exec "${lib.getExe' config.systemd.package "systemctl"} --user import-environment {,WAYLAND_}DISPLAY SWAYSOCK; ${lib.getExe gkleenConfig.programs.quickshell.package} --path ${gkleenConfig.xdg.configFile."quickshell".source}/displaymanager.qml; swaymsg exit" | ||
| 67 | |||
| 68 | input type:keyboard { | ||
| 69 | xkb_layout "us,us" | ||
| 70 | xkb_variant "dvp," | ||
| 71 | xkb_options "compose:caps,grp:win_space_toggle" | ||
| 72 | } | ||
| 73 | |||
| 74 | output eDP-1 scale 1.5 | ||
| 75 | ''} | ||
| 76 | ''; | ||
| 77 | }); | ||
| 78 | }; | ||
| 79 | |||
| 80 | # security.pam.services.greetd.fprintAuth = false; | ||
| 81 | |||
| 82 | systemd.services.greetd.serviceConfig = { | ||
| 83 | ExecStartPre = ''${lib.getExe' pkgs.coreutils "install"} -d -o greeter -g greeter -m 0700 ''${CACHE_DIRECTORY}/greeter''; | ||
| 84 | # CacheDirectory = "greetd"; | ||
| 85 | }; | ||
| 86 | |||
| 87 | users.users.greeter = { | ||
| 88 | home = "/var/lib/greeter"; | ||
| 89 | createHome = true; | ||
| 90 | }; | ||
| 91 | }; | ||
| 92 | } | ||
diff --git a/hosts/sif/hw.nix b/hosts/sif/hw.nix index fc20ef7c..e567c37d 100644 --- a/hosts/sif/hw.nix +++ b/hosts/sif/hw.nix | |||
| @@ -8,15 +8,28 @@ | |||
| 8 | options = [ "fmask=0033" "dmask=0022" ]; | 8 | options = [ "fmask=0033" "dmask=0022" ]; |
| 9 | }; | 9 | }; |
| 10 | "/.bcachefs" = | 10 | "/.bcachefs" = |
| 11 | { device = "/dev/mapper/sif-nvm0:/dev/mapper/sif-nvm1"; | 11 | { options = [ |
| 12 | "x-systemd.requires=/dev/disk/by-id/dm-name-sif-nvm0" | ||
| 13 | "x-systemd.requires=/dev/disk/by-id/dm-name-sif-nvm1" | ||
| 14 | ]; | ||
| 15 | device = "/dev/disk/by-uuid/fe7bdaac-d2f3-4535-a635-e2fb97ef3802"; | ||
| 12 | fsType = "bcachefs"; | 16 | fsType = "bcachefs"; |
| 13 | neededForBoot = true; | 17 | neededForBoot = true; |
| 14 | }; | 18 | }; |
| 15 | "/var/lib/sops-nix".neededForBoot = true; | 19 | "/var/lib/sops-nix".neededForBoot = true; |
| 16 | "/var/lib/systemd".neededForBoot = true; | 20 | "/var/lib/systemd".neededForBoot = true; |
| 17 | }; | 21 | }; |
| 18 | system.etc.overlay.enable = false; | 22 | swapDevices = [ |
| 19 | systemd.sysusers.enable = false; | 23 | { label = "swap"; } |
| 24 | ]; | ||
| 25 | # system.etc.overlay.enable = false; | ||
| 26 | |||
| 27 | boot.initrd.systemd.packages = [ | ||
| 28 | (pkgs.writeTextDir "/etc/systemd/system/sysroot-.bcachefs.mount.d/block_scan.conf" '' | ||
| 29 | [Mount] | ||
| 30 | Environment=BCACHEFS_BLOCK_SCAN=1 | ||
| 31 | '') | ||
| 32 | ]; | ||
| 20 | 33 | ||
| 21 | # boot.initrd.supportedFilesystems.bcachefs = true; | 34 | # boot.initrd.supportedFilesystems.bcachefs = true; |
| 22 | # boot.initrd.systemd.units."dev-sif-nvm0:-dev-sif-nvm1.device".enable = false; | 35 | # boot.initrd.systemd.units."dev-sif-nvm0:-dev-sif-nvm1.device".enable = false; |
diff --git a/hosts/sif/libvirt/default.nix b/hosts/sif/libvirt/default.nix index d0be7dff..9712d0d9 100644 --- a/hosts/sif/libvirt/default.nix +++ b/hosts/sif/libvirt/default.nix | |||
| @@ -8,6 +8,7 @@ with flakeInputs.nixVirt.lib; | |||
| 8 | qemu.swtpm.enable = true; | 8 | qemu.swtpm.enable = true; |
| 9 | allowedBridges = ["virbr0" "rz-0971" "rz-2403"]; | 9 | allowedBridges = ["virbr0" "rz-0971" "rz-2403"]; |
| 10 | }; | 10 | }; |
| 11 | virtualisation.spiceUSBRedirection.enable = true; | ||
| 11 | virtualisation.libvirt = { | 12 | virtualisation.libvirt = { |
| 12 | enable = true; | 13 | enable = true; |
| 13 | swtpm.enable = true; | 14 | swtpm.enable = true; |
diff --git a/hosts/sif/mail/default.nix b/hosts/sif/mail/default.nix deleted file mode 100644 index f36cd599..00000000 --- a/hosts/sif/mail/default.nix +++ /dev/null | |||
| @@ -1,70 +0,0 @@ | |||
| 1 | { config, pkgs, ... }: | ||
| 2 | { | ||
| 3 | services.postfix = { | ||
| 4 | enable = true; | ||
| 5 | enableSmtp = true; | ||
| 6 | enableSubmission = false; | ||
| 7 | setSendmail = true; | ||
| 8 | networksStyle = "host"; | ||
| 9 | hostname = "sif.midgard.yggdrasil"; | ||
| 10 | destination = []; | ||
| 11 | relayHost = "uucp:ymir"; | ||
| 12 | recipientDelimiter = "+"; | ||
| 13 | masterConfig = { | ||
| 14 | uucp = { | ||
| 15 | type = "unix"; | ||
| 16 | private = true; | ||
| 17 | privileged = true; | ||
| 18 | chroot = false; | ||
| 19 | command = "pipe"; | ||
| 20 | args = [ "flags=Fqhu" "user=uucp" ''argv=${config.security.wrapperDir}/uux -z -a $sender - $nexthop!rmail ($recipient)'' ]; | ||
| 21 | }; | ||
| 22 | smtps = { | ||
| 23 | type = "unix"; | ||
| 24 | private = true; | ||
| 25 | privileged = true; | ||
| 26 | chroot = false; | ||
| 27 | command = "smtp"; | ||
| 28 | args = [ "-o" "smtp_tls_wrappermode=yes" "-o" "smtp_tls_security_level=encrypt" ]; | ||
| 29 | }; | ||
| 30 | }; | ||
| 31 | config = { | ||
| 32 | default_transport = "uucp:ymir"; | ||
| 33 | |||
| 34 | inet_interfaces = "loopback-only"; | ||
| 35 | |||
| 36 | authorized_submit_users = ["!uucp" "static:anyone"]; | ||
| 37 | message_size_limit = "0"; | ||
| 38 | |||
| 39 | sender_dependent_default_transport_maps = ''regexp:${pkgs.writeText "sender_relay" '' | ||
| 40 | /@(cip|stud)\.ifi\.(lmu|uni-muenchen)\.de$/ smtp:smtp.ifi.lmu.de | ||
| 41 | /@ifi\.(lmu|uni-muenchen)\.de$/ smtp:smtpin1.ifi.lmu.de:587 | ||
| 42 | /@math(ematik)?\.(lmu|uni-muenchen)\.de$/ smtps:smtp.math.lmu.de:465 | ||
| 43 | /@(campus\.)?lmu\.de$/ smtp:postout.lrz.de | ||
| 44 | ''}''; | ||
| 45 | sender_bcc_maps = ''regexp:${pkgs.writeText "sender_bcc" '' | ||
| 46 | /^uni2work(-[^@]*)?@ifi\.lmu\.de$/ uni2work@ifi.lmu.de | ||
| 47 | /@ifi\.lmu\.de$/ gregor.kleen@ifi.lmu.de | ||
| 48 | ''}''; | ||
| 49 | |||
| 50 | smtp_sasl_auth_enable = true; | ||
| 51 | smtp_sender_dependent_authentication = true; | ||
| 52 | smtp_sasl_tls_security_options = "noanonymous"; | ||
| 53 | smtp_sasl_mechanism_filter = ["plain"]; | ||
| 54 | smtp_sasl_password_maps = "regexp:/var/db/postfix/sasl_passwd"; | ||
| 55 | smtp_cname_overrides_servername = false; | ||
| 56 | smtp_always_send_ehlo = true; | ||
| 57 | smtp_tls_security_level = "dane"; | ||
| 58 | |||
| 59 | smtp_tls_loglevel = "1"; | ||
| 60 | smtp_dns_support_level = "dnssec"; | ||
| 61 | }; | ||
| 62 | }; | ||
| 63 | |||
| 64 | sops.secrets.postfix-sasl-passwd = { | ||
| 65 | key = "sasl-passwd"; | ||
| 66 | path = "/var/db/postfix/sasl_passwd"; | ||
| 67 | owner = "postfix"; | ||
| 68 | sopsFile = ./secrets.yaml; | ||
| 69 | }; | ||
| 70 | } | ||
diff --git a/hosts/sif/ruleset.nft b/hosts/sif/ruleset.nft index 2af8b2ee..62339f69 100644 --- a/hosts/sif/ruleset.nft +++ b/hosts/sif/ruleset.nft | |||
| @@ -61,7 +61,7 @@ table inet filter { | |||
| 61 | counter mosh-rx {} | 61 | counter mosh-rx {} |
| 62 | counter wg-rx {} | 62 | counter wg-rx {} |
| 63 | counter yggdrasil-gre-rx {} | 63 | counter yggdrasil-gre-rx {} |
| 64 | counter quickserve-rx {} | 64 | counter miniserve-rx {} |
| 65 | counter ausweisapp2-rx {} | 65 | counter ausweisapp2-rx {} |
| 66 | 66 | ||
| 67 | counter established-rx {} | 67 | counter established-rx {} |
| @@ -81,7 +81,7 @@ table inet filter { | |||
| 81 | counter mosh-tx {} | 81 | counter mosh-tx {} |
| 82 | counter wg-tx {} | 82 | counter wg-tx {} |
| 83 | counter yggdrasil-gre-tx {} | 83 | counter yggdrasil-gre-tx {} |
| 84 | counter quickserve-tx {} | 84 | counter miniserve-tx {} |
| 85 | 85 | ||
| 86 | counter tx {} | 86 | counter tx {} |
| 87 | 87 | ||
| @@ -134,7 +134,7 @@ table inet filter { | |||
| 134 | tcp dport 22 counter name ssh-rx accept | 134 | tcp dport 22 counter name ssh-rx accept |
| 135 | udp dport 60000-61000 counter name mosh-rx accept | 135 | udp dport 60000-61000 counter name mosh-rx accept |
| 136 | 136 | ||
| 137 | tcp dport 8000 counter name quickserve-rx accept | 137 | tcp dport 8080 counter name miniserve-rx accept |
| 138 | udp dport 24727 counter name ausweisapp2-rx accept | 138 | udp dport 24727 counter name ausweisapp2-rx accept |
| 139 | 139 | ||
| 140 | udp dport 51820-51822 counter name wg-rx accept | 140 | udp dport 51820-51822 counter name wg-rx accept |
| @@ -173,7 +173,7 @@ table inet filter { | |||
| 173 | udp sport 51820-51822 counter name wg-tx | 173 | udp sport 51820-51822 counter name wg-tx |
| 174 | iifname "yggdrasil-wg-*" meta l4proto gre counter name yggdrasil-gre-tx | 174 | iifname "yggdrasil-wg-*" meta l4proto gre counter name yggdrasil-gre-tx |
| 175 | 175 | ||
| 176 | tcp sport 8000 counter name quickserve-tx accept | 176 | tcp sport 8080 counter name miniserve-tx accept |
| 177 | 177 | ||
| 178 | oifname virbr0 udp sport 67 counter name libvirt-dhcp accept | 178 | oifname virbr0 udp sport 67 counter name libvirt-dhcp accept |
| 179 | oifname virbr0 udp sport 547 counter name libvirt-dhcp accept | 179 | oifname virbr0 udp sport 547 counter name libvirt-dhcp accept |
diff --git a/hosts/surtr/audiobookshelf.nix b/hosts/surtr/audiobookshelf.nix new file mode 100644 index 00000000..728851a4 --- /dev/null +++ b/hosts/surtr/audiobookshelf.nix | |||
| @@ -0,0 +1,66 @@ | |||
| 1 | { config, ... }: | ||
| 2 | |||
| 3 | { | ||
| 4 | config = { | ||
| 5 | security.acme.rfc2136Domains = { | ||
| 6 | "audiobookshelf.yggdrasil.li" = { | ||
| 7 | restartUnits = ["nginx.service"]; | ||
| 8 | }; | ||
| 9 | }; | ||
| 10 | |||
| 11 | services.nginx = { | ||
| 12 | upstreams."audiobookshelf" = { | ||
| 13 | servers = { | ||
| 14 | "[2a03:4000:52:ada:4:1::]:28982" = {}; | ||
| 15 | }; | ||
| 16 | extraConfig = '' | ||
| 17 | keepalive 8; | ||
| 18 | ''; | ||
| 19 | }; | ||
| 20 | virtualHosts = { | ||
| 21 | "audiobookshelf.yggdrasil.li" = { | ||
| 22 | kTLS = true; | ||
| 23 | http3 = true; | ||
| 24 | forceSSL = true; | ||
| 25 | sslCertificate = "/run/credentials/nginx.service/audiobookshelf.yggdrasil.li.pem"; | ||
| 26 | sslCertificateKey = "/run/credentials/nginx.service/audiobookshelf.yggdrasil.li.key.pem"; | ||
| 27 | sslTrustedCertificate = "/run/credentials/nginx.service/audiobookshelf.yggdrasil.li.chain.pem"; | ||
| 28 | extraConfig = '' | ||
| 29 | charset utf-8; | ||
| 30 | ''; | ||
| 31 | |||
| 32 | locations = { | ||
| 33 | "/".extraConfig = '' | ||
| 34 | proxy_pass http://audiobookshelf; | ||
| 35 | |||
| 36 | proxy_http_version 1.1; | ||
| 37 | proxy_set_header Upgrade $http_upgrade; | ||
| 38 | proxy_set_header Connection "upgrade"; | ||
| 39 | |||
| 40 | proxy_redirect off; | ||
| 41 | proxy_set_header Host $host; | ||
| 42 | proxy_set_header X-Real-IP $remote_addr; | ||
| 43 | proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; | ||
| 44 | proxy_set_header X-Forwarded-Host $server_name; | ||
| 45 | proxy_set_header X-Forwarded-Proto $scheme; | ||
| 46 | |||
| 47 | client_max_body_size 0; | ||
| 48 | proxy_request_buffering off; | ||
| 49 | proxy_buffering off; | ||
| 50 | ''; | ||
| 51 | }; | ||
| 52 | }; | ||
| 53 | }; | ||
| 54 | }; | ||
| 55 | |||
| 56 | systemd.services.nginx = { | ||
| 57 | serviceConfig = { | ||
| 58 | LoadCredential = [ | ||
| 59 | "audiobookshelf.yggdrasil.li.key.pem:${config.security.acme.certs."audiobookshelf.yggdrasil.li".directory}/key.pem" | ||
| 60 | "audiobookshelf.yggdrasil.li.pem:${config.security.acme.certs."audiobookshelf.yggdrasil.li".directory}/fullchain.pem" | ||
| 61 | "audiobookshelf.yggdrasil.li.chain.pem:${config.security.acme.certs."audiobookshelf.yggdrasil.li".directory}/chain.pem" | ||
| 62 | ]; | ||
| 63 | }; | ||
| 64 | }; | ||
| 65 | }; | ||
| 66 | } | ||
diff --git a/hosts/surtr/bifrost/default.nix b/hosts/surtr/bifrost/default.nix index fbfde757..52ab43f5 100644 --- a/hosts/surtr/bifrost/default.nix +++ b/hosts/surtr/bifrost/default.nix | |||
| @@ -18,7 +18,7 @@ in { | |||
| 18 | ListenPort = 51822; | 18 | ListenPort = 51822; |
| 19 | }; | 19 | }; |
| 20 | wireguardPeers = [ | 20 | wireguardPeers = [ |
| 21 | { AllowedIPs = [ "2a03:4000:52:ada:4:1::/96" ]; | 21 | { AllowedIPs = [ "2a03:4000:52:ada:4:1::/96" "2a03:4000:52:ada:6::/80" ]; |
| 22 | PublicKey = trim (readFile ../../vidhar/network/bifrost/vidhar.pub); | 22 | PublicKey = trim (readFile ../../vidhar/network/bifrost/vidhar.pub); |
| 23 | } | 23 | } |
| 24 | ]; | 24 | ]; |
| @@ -34,6 +34,8 @@ in { | |||
| 34 | routes = [ | 34 | routes = [ |
| 35 | { Destination = "2a03:4000:52:ada:4::/80"; | 35 | { Destination = "2a03:4000:52:ada:4::/80"; |
| 36 | } | 36 | } |
| 37 | { Destination = "2a03:4000:52:ada:6::/80"; | ||
| 38 | } | ||
| 37 | ]; | 39 | ]; |
| 38 | linkConfig = { | 40 | linkConfig = { |
| 39 | RequiredForOnline = false; | 41 | RequiredForOnline = false; |
diff --git a/hosts/surtr/default.nix b/hosts/surtr/default.nix index 223e1f10..1c66df2b 100644 --- a/hosts/surtr/default.nix +++ b/hosts/surtr/default.nix | |||
| @@ -6,7 +6,8 @@ with lib; | |||
| 6 | imports = with flake.nixosModules.systemProfiles; [ | 6 | imports = with flake.nixosModules.systemProfiles; [ |
| 7 | tmpfs-root qemu-guest openssh rebuild-machines zfs | 7 | tmpfs-root qemu-guest openssh rebuild-machines zfs |
| 8 | ./zfs.nix ./dns ./tls ./http ./bifrost ./matrix ./postgresql | 8 | ./zfs.nix ./dns ./tls ./http ./bifrost ./matrix ./postgresql |
| 9 | ./prometheus ./email ./vpn ./borg.nix ./etebase | 9 | ./prometheus ./email ./vpn ./borg.nix ./etebase ./immich.nix |
| 10 | ./paperless.nix ./hledger.nix ./audiobookshelf.nix ./kimai.nix | ||
| 10 | ]; | 11 | ]; |
| 11 | 12 | ||
| 12 | config = { | 13 | config = { |
| @@ -14,9 +15,6 @@ with lib; | |||
| 14 | system = "x86_64-linux"; | 15 | system = "x86_64-linux"; |
| 15 | }; | 16 | }; |
| 16 | 17 | ||
| 17 | networking.hostId = "a64cf4d7"; | ||
| 18 | environment.etc."machine-id".text = "a64cf4d793ab0a0ed3892ead609fc0bc"; | ||
| 19 | |||
| 20 | boot = { | 18 | boot = { |
| 21 | loader.grub = { | 19 | loader.grub = { |
| 22 | enable = true; | 20 | enable = true; |
| @@ -24,12 +22,20 @@ with lib; | |||
| 24 | device = "/dev/vda"; | 22 | device = "/dev/vda"; |
| 25 | }; | 23 | }; |
| 26 | 24 | ||
| 27 | |||
| 28 | tmp.useTmpfs = true; | 25 | tmp.useTmpfs = true; |
| 29 | 26 | ||
| 30 | zfs.devNodes = "/dev"; # /dev/vda2 does not show up in /dev/disk/by-id | 27 | zfs.devNodes = "/dev"; # /dev/vda2 does not show up in /dev/disk/by-id |
| 31 | 28 | ||
| 32 | kernelModules = ["ptp_kvm"]; | 29 | kernelModules = ["ptp_kvm"]; |
| 30 | kernelPatches = [ | ||
| 31 | { name = "zswap-default"; | ||
| 32 | patch = null; | ||
| 33 | structuredExtraConfig = with lib.kernel; { | ||
| 34 | ZSWAP_DEFAULT_ON = yes; | ||
| 35 | ZSWAP_SHRINKER_DEFAULT_ON = yes; | ||
| 36 | }; | ||
| 37 | } | ||
| 38 | ]; | ||
| 33 | }; | 39 | }; |
| 34 | 40 | ||
| 35 | fileSystems = { | 41 | fileSystems = { |
| @@ -38,6 +44,9 @@ with lib; | |||
| 38 | fsType = "vfat"; | 44 | fsType = "vfat"; |
| 39 | }; | 45 | }; |
| 40 | }; | 46 | }; |
| 47 | swapDevices = [ | ||
| 48 | { label = "swap"; } | ||
| 49 | ]; | ||
| 41 | 50 | ||
| 42 | networking = { | 51 | networking = { |
| 43 | hostName = "surtr"; | 52 | hostName = "surtr"; |
| @@ -163,11 +172,6 @@ with lib; | |||
| 163 | stateful = true; | 172 | stateful = true; |
| 164 | }; | 173 | }; |
| 165 | 174 | ||
| 166 | zramSwap = { | ||
| 167 | enable = true; | ||
| 168 | algorithm = "zstd"; | ||
| 169 | }; | ||
| 170 | |||
| 171 | systemd.sysusers.enable = false; | 175 | systemd.sysusers.enable = false; |
| 172 | system.etc.overlay.mutable = true; | 176 | system.etc.overlay.mutable = true; |
| 173 | boot.enableContainers = true; | 177 | boot.enableContainers = true; |
diff --git a/hosts/surtr/dns/Gupfile b/hosts/surtr/dns/Gupfile index ac96f620..70674cce 100644 --- a/hosts/surtr/dns/Gupfile +++ b/hosts/surtr/dns/Gupfile | |||
| @@ -1,2 +1,2 @@ | |||
| 1 | key.gup: | 1 | key.gup: |
| 2 | keys/*.yaml \ No newline at end of file | 2 | keys/* \ No newline at end of file |
diff --git a/hosts/surtr/dns/default.nix b/hosts/surtr/dns/default.nix index 53df798e..8aca2b97 100644 --- a/hosts/surtr/dns/default.nix +++ b/hosts/surtr/dns/default.nix | |||
| @@ -157,7 +157,7 @@ in { | |||
| 157 | ${concatMapStringsSep "\n" mkZone [ | 157 | ${concatMapStringsSep "\n" mkZone [ |
| 158 | { domain = "yggdrasil.li"; | 158 | { domain = "yggdrasil.li"; |
| 159 | addACLs = { "yggdrasil.li" = ["ymir_acme_acl"]; }; | 159 | addACLs = { "yggdrasil.li" = ["ymir_acme_acl"]; }; |
| 160 | acmeDomains = ["surtr.yggdrasil.li" "yggdrasil.li" "etesync.yggdrasil.li" "app.etesync.yggdrasil.li"]; | 160 | acmeDomains = ["surtr.yggdrasil.li" "yggdrasil.li" "etesync.yggdrasil.li" "immich.yggdrasil.li" "app.etesync.yggdrasil.li" "paperless.yggdrasil.li" "hledger.yggdrasil.li" "audiobookshelf.yggdrasil.li" "kimai.yggdrasil.li"]; |
| 161 | } | 161 | } |
| 162 | { domain = "nights.email"; | 162 | { domain = "nights.email"; |
| 163 | addACLs = { "nights.email" = ["ymir_acme_acl"]; }; | 163 | addACLs = { "nights.email" = ["ymir_acme_acl"]; }; |
| @@ -169,15 +169,9 @@ in { | |||
| 169 | { domain = "kleen.li"; | 169 | { domain = "kleen.li"; |
| 170 | addACLs = { "kleen.li" = ["ymir_acme_acl"]; }; | 170 | addACLs = { "kleen.li" = ["ymir_acme_acl"]; }; |
| 171 | } | 171 | } |
| 172 | { domain = "xmpp.li"; | ||
| 173 | addACLs = { "xmpp.li" = ["ymir_acme_acl"]; }; | ||
| 174 | } | ||
| 175 | { domain = "synapse.li"; | 172 | { domain = "synapse.li"; |
| 176 | acmeDomains = ["element.synapse.li" "turn.synapse.li" "synapse.li"]; | 173 | acmeDomains = ["element.synapse.li" "turn.synapse.li" "synapse.li"]; |
| 177 | } | 174 | } |
| 178 | { domain = "dirty-haskell.org"; | ||
| 179 | addACLs = { "dirty-haskell.org" = ["ymir_acme_acl"]; }; | ||
| 180 | } | ||
| 181 | { domain = "praseodym.org"; | 175 | { domain = "praseodym.org"; |
| 182 | addACLs = { "praseodym.org" = ["ymir_acme_acl"]; }; | 176 | addACLs = { "praseodym.org" = ["ymir_acme_acl"]; }; |
| 183 | } | 177 | } |
diff --git a/hosts/surtr/dns/key.gup b/hosts/surtr/dns/key.gup index 32d4f7d6..5b5058b3 100644 --- a/hosts/surtr/dns/key.gup +++ b/hosts/surtr/dns/key.gup | |||
| @@ -3,4 +3,4 @@ | |||
| 3 | keyName=${${2:t}%.yaml}_key | 3 | keyName=${${2:t}%.yaml}_key |
| 4 | 4 | ||
| 5 | keymgr -t ${keyName} > $1 | 5 | keymgr -t ${keyName} > $1 |
| 6 | sops -p '7ED22F4AA7BB55728B643DC5471B7D88E4EF66F8,30D3453B8CD02FE2A3E7C78C0FB536FB87AE8F51' --input-type=binary --output-type=binary -e -i $1 \ No newline at end of file | 6 | sops --input-type=binary --output-type=binary -e -i $1 |
diff --git a/hosts/surtr/dns/keys/audiobookshelf.yggdrasil.li_acme b/hosts/surtr/dns/keys/audiobookshelf.yggdrasil.li_acme new file mode 100644 index 00000000..e3af9966 --- /dev/null +++ b/hosts/surtr/dns/keys/audiobookshelf.yggdrasil.li_acme | |||
| @@ -0,0 +1,19 @@ | |||
| 1 | { | ||
| 2 | "data": "ENC[AES256_GCM,data:5BLk/MZtQsLjpdKGiZjtOH1soIXB05MkEoPWyr5m27ho7H5udDjRZE0/OjvSlMzQNjFNJc+OwbeHwaJ8sPLCnXqoFHJXikAmi0gpdFC0uN0JGYCBT1EaK7j6yVHyiqFXo6xwVSCO/zP/fbjItiuqU+B4llGx5N2I4HQqRLFoW/35PZazkR15xI1zo8LeC8T+jm16apFQw2Ih/RqsJK7XlHqnXq9SzeA2qhgkCbJf6aJ6zDS7eQVFt7qHh2mVtV7Al++bZiIkJiNJ5SJO1ck18w2t9HwXBhfMFKXvfFW0I6kKur1qSJk=,iv:rxm5PwOzXDaK+nj2k3bUqlYbIFFA49ispyfamtQqU/A=,tag:7dOua39f+0JsLldLRLr1NQ==,type:str]", | ||
| 3 | "sops": { | ||
| 4 | "age": [ | ||
| 5 | { | ||
| 6 | "recipient": "age1rmmhetcmllq0ahl5qznlr0eya2zdxwl9h6y5wnl97d2wtyx5t99sm2u866", | ||
| 7 | "enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSAwV1hIcWNNRVJXOG1xYlFK\nb1VxWG03dGVmS3dmQTltUTR0VmEzYjRFanlBCnE1M1BTZjNCQnpsNkU5ZlR5T3BR\nOHliWUV2bXMrK0w1K3JXbUE0dEhKdzgKLS0tIDdkUEZ4QUE0NUN2TXFYcklybjBS\nRVpxV2J6U1BnUng1dytWTGRkd0FrRGcKVaMCzcrX+BQqbmh95JEk3lRdfnv9uO8n\nwotKxp/+xX7GEF42BOzJ/mWcgyVjABlnWeVyaRxfpbCUrmNuFYO6XA==\n-----END AGE ENCRYPTED FILE-----\n" | ||
| 8 | }, | ||
| 9 | { | ||
| 10 | "recipient": "age19a7j77w267z04zls7m28a8hj4a0g5af6ltye2d5wypg33c3l89csd4r9zq", | ||
| 11 | "enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBkZmRFR0gwc1pjVVorK0t6\nVHU4U0xob09Dd2J5QUJXbndZdWp3cytpYWhRCnFnMlZ0cjNGdXN1RWEvQlVHSE1M\nMEdzSjlEb2NkaE1zMEJJOTlCMC9FVFEKLS0tIGJiTEJSRVZZbVUzZVFGeXQwYm1w\ndWVYQVdoVlJnRENTYzhnQm1BcWVRdW8K0ECfLVQBQuZWFFFjdHLcJBz+CDzsOOwh\nOA38qxHD4EWfXR1c3G8RDbow7nB2MGc1Zohc7qhtuTj2wL0qhVKWqA==\n-----END AGE ENCRYPTED FILE-----\n" | ||
| 12 | } | ||
| 13 | ], | ||
| 14 | "lastmodified": "2025-05-09T17:07:16Z", | ||
| 15 | "mac": "ENC[AES256_GCM,data:K5I6YXQXCUPHFBNVlXIdLLKqiNPVZh95KoHni2m16SdAvTyBab79SZ5xNvotrtKXp0iISCogEgdMm+OWbxYywEiZ+sUsxgx6RE5nAXruZOiAwzuyUr88qgCHTBZnKzgaDZlbYOgWB+LCzr8s5JbcTD4G6/RaXYyqmx7igygubHA=,iv:Zbij4eoDqoP5XYhAsDGBGqlcP5ACQAY/QngTmrJYRzs=,tag:WYxhUEZwmXkQmnpyOuP2Bg==,type:str]", | ||
| 16 | "unencrypted_suffix": "_unencrypted", | ||
| 17 | "version": "3.10.2" | ||
| 18 | } | ||
| 19 | } | ||
diff --git a/hosts/surtr/dns/keys/hledger.yggdrasil.li_acme b/hosts/surtr/dns/keys/hledger.yggdrasil.li_acme new file mode 100644 index 00000000..b3f4cfb6 --- /dev/null +++ b/hosts/surtr/dns/keys/hledger.yggdrasil.li_acme | |||
| @@ -0,0 +1,24 @@ | |||
| 1 | { | ||
| 2 | "data": "ENC[AES256_GCM,data:lCj8VYJL9z29FJ154XQtxKQLwwitCRGy4krJ6u8yw2FMzoHprEpFgm33+mFspxSKk/It2G8cfTGMZSeVkYJEHb66HNKHl0A2Fz3hwjpRjh1MZAw0wiZJlnS/LNqoGstQ2PJmTQTW3aJRMoT1GS7q/gSp/3rqySA5EOm0GgUiA3Vi7nGpkBenKDEbQbcIBXRdMOk66BCdiz5XGm/1VLQQLO9oVwY2KBnLaZSISohyGVhbIy7GT2ygoWHHxHn0c5CRVNvGNwesM1gO1NnTFrISLMWSrsDPaAtQ,iv:fa8LFjzqsf2ccfbEe5MOmerb7FzXb4xr24y1GWIMT1Q=,tag:7oQ54DKBb76Pbw1lmEHt+Q==,type:str]", | ||
| 3 | "sops": { | ||
| 4 | "kms": null, | ||
| 5 | "gcp_kms": null, | ||
| 6 | "azure_kv": null, | ||
| 7 | "hc_vault": null, | ||
| 8 | "age": [ | ||
| 9 | { | ||
| 10 | "recipient": "age1rmmhetcmllq0ahl5qznlr0eya2zdxwl9h6y5wnl97d2wtyx5t99sm2u866", | ||
| 11 | "enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBzcHJLRHh0VkpDNDU3cGZS\nMWFlVWpFemZ2RnpnSllDWU1EWGoyWUxyejNzClBGandzaFI5NXY1bG51Y3VxRk9r\nc29NbXBOaEZDblBuaVowemQydkxBdjgKLS0tICtVb2xkMmh4T0Q0cU4xQnBzZExI\namFRUnRYTWIyQ3RHNUVHWTFrUzhhK1UKqmATNmxlhkxM5PP1U6w7fSYVA8AgIRAt\nJ9WZrTffQfXMdw4RmjWcoVHFH39Fe4SteedxliCCcqjkjgSEB4Rgow==\n-----END AGE ENCRYPTED FILE-----\n" | ||
| 12 | }, | ||
| 13 | { | ||
| 14 | "recipient": "age19a7j77w267z04zls7m28a8hj4a0g5af6ltye2d5wypg33c3l89csd4r9zq", | ||
| 15 | "enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBjWlhpQWI4c0x0ZXRGYVE2\nR1dHZzZud0ZxQWsrREhJWUowWE1zem5FVFI0CkJBUnIwY1FGS3N2VnpuSkZjRllZ\ncVgyeVg4cTVjRitzL0RKb0ZQb3BsOEkKLS0tIGs3SDBkamVBNDhQUlh5dmVVZXJs\nM3VKdlFKc21GcFY0UUtiaHFvYWI4V0kKKuWYEncxe9NT2ZS3X3+l/gT4BQOrdCg8\nj2jGL+Yzy/356GO3PFTn2HHLam6KWDKaYB5TlK/zSohfUt5giQH2Lw==\n-----END AGE ENCRYPTED FILE-----\n" | ||
| 16 | } | ||
| 17 | ], | ||
| 18 | "lastmodified": "2025-02-19T17:13:51Z", | ||
| 19 | "mac": "ENC[AES256_GCM,data:XsBdMCBjB+YuBMZQrjJ5uZtaYKSqsdWVvm+IEoJflCKPIhPk2rBZ3nY8KngXFbq2fWgsYyTM83kb2trEGIEHUPuERt+mgfCI3bSlylriwgsDWihCjyBecNE+BbdXE0+YcNl8pIwBU4M+3f2StQMH22YamToLJ9i9kfKcBrirDuU=,iv:VTIdBVY3kVBMYWhYUmrP2vZ9rpH90DzF68y1aDf2EAs=,tag:YkL+nw6LNXAceZtx9vgf6A==,type:str]", | ||
| 20 | "pgp": null, | ||
| 21 | "unencrypted_suffix": "_unencrypted", | ||
| 22 | "version": "3.9.4" | ||
| 23 | } | ||
| 24 | } \ No newline at end of file | ||
diff --git a/hosts/surtr/dns/keys/immich.yggdrasil.li_acme b/hosts/surtr/dns/keys/immich.yggdrasil.li_acme new file mode 100644 index 00000000..c31234bd --- /dev/null +++ b/hosts/surtr/dns/keys/immich.yggdrasil.li_acme | |||
| @@ -0,0 +1,24 @@ | |||
| 1 | { | ||
| 2 | "data": "ENC[AES256_GCM,data:1i27jx1E4nn8/iXEN90tnQve0MX0HcXyYZoQfga1djcESDARd7kX78jncqnSdEMJPIQCyq2zmvOwEiAwyofIjSBSW2teoxD1PybSZdyvKwOnwLqpVWxgw6LORUoN5c1y4+WmnQa1SJ0a1WJwZ3cFRa3LP5JPbZzmNCZWEg+yGwVHNMsmrBSrjLRFC1NPIfX69lWGZl5VIMw2/SoSMDcOsSURWIpVSEYe9LNrc4/cKQQC/rmpXBg9ekIa8xd7NQTcDZA42bDIBQxJzqkUNV3JeIrl0C+eQw==,iv:MEDhvUvy0PfcLGim06VXkiIGgkNgaQcYqGhJraaGC6M=,tag:43CdGFe5JXOsMCHrvgl+BA==,type:str]", | ||
| 3 | "sops": { | ||
| 4 | "kms": null, | ||
| 5 | "gcp_kms": null, | ||
| 6 | "azure_kv": null, | ||
| 7 | "hc_vault": null, | ||
| 8 | "age": [ | ||
| 9 | { | ||
| 10 | "recipient": "age1rmmhetcmllq0ahl5qznlr0eya2zdxwl9h6y5wnl97d2wtyx5t99sm2u866", | ||
| 11 | "enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBLOU5OL0xxVVE2dzVOYnhP\nMUlhYlpyZGZSamU4QkpObHJLS1NJdjdwcmtjCks0OElnaHZvb3BSNnYySnVPcEUx\nazdNVUpZZGRNOVNBVTVUNUdnaC9DM00KLS0tIHUxU2dHMCt4d3hJbXlSNzBKUk1W\na05uZVRwVlMrbEZ2WEkvUy9PUVhmWXcKemRLnCC2mAkCEbZ3bC+iZmIWQCQsI+ew\ni4mmc/mUkGx8/61SR571NXIKmxSx2U5L2IK1pIKy6G/xMkPq+wDgUQ==\n-----END AGE ENCRYPTED FILE-----\n" | ||
| 12 | }, | ||
| 13 | { | ||
| 14 | "recipient": "age19a7j77w267z04zls7m28a8hj4a0g5af6ltye2d5wypg33c3l89csd4r9zq", | ||
| 15 | "enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSB5VnNUV2lRR292ZmpNeU51\nQ2YxcnVnd21ybGdpd0I1QlJuSEQwNGZYTDJrCml0YUdxaWJHTEFNYWVIY01jWGk2\nZEJSQ0VZcVV1bU9VZ3dBbUh3a0N1ekUKLS0tIFhvNnRqcnRUZFNYOVhuWkFrZ3Vk\nMjU0YnNDODJRYk9lVENLUU9KU2dkWm8KHqtuNtC39S4oiQFRhNT2OOUOY9KQvDYW\nvtcdR8MSZDE7jsqgLgGS/8lIc0GBbIwYWghgFsLmn2Bkdh2q/VuO9w==\n-----END AGE ENCRYPTED FILE-----\n" | ||
| 16 | } | ||
| 17 | ], | ||
| 18 | "lastmodified": "2025-01-03T15:31:39Z", | ||
| 19 | "mac": "ENC[AES256_GCM,data:NKUbtcQf2DfALfm9kwiirmwD3slfTh4HNIg8BT/xbySHfwsaFtmlZTkhavBNr+b5snR8opATVXnJPAoykxXq8q4G1yDeitnTw6x9KfwgyZKpbJANMTBZEwK+CnZbqYRas1bFC88+D0yWI1yUnle+NPQ8VUj+KxCiUNuWO80mWhE=,iv:2a7CawUQujcZuR1pmsm9L2KGKcrBRAOxiIWEKCkTCEM=,tag:j8caxyN2fvx2FnWXHkWKcw==,type:str]", | ||
| 20 | "pgp": null, | ||
| 21 | "unencrypted_suffix": "_unencrypted", | ||
| 22 | "version": "3.9.2" | ||
| 23 | } | ||
| 24 | } \ No newline at end of file | ||
diff --git a/hosts/surtr/dns/keys/kimai.yggdrasil.li_acme b/hosts/surtr/dns/keys/kimai.yggdrasil.li_acme new file mode 100644 index 00000000..bdfb135a --- /dev/null +++ b/hosts/surtr/dns/keys/kimai.yggdrasil.li_acme | |||
| @@ -0,0 +1,19 @@ | |||
| 1 | { | ||
| 2 | "data": "ENC[AES256_GCM,data:sKFt4pH0Xn7Qm6JFMg/2N7Ht7jtMJukfN+U3dQaoYXPbhRJ+heEtDpXV/WP4AlfbfpIOgTPW3mcmQCwKFNhS00vEsQA4728FfXZzDDmZCa3hwg51wDbL7XUOr0OePgzi86lt0Q193K6CkGqEAa1vFIb//ElEfBYIwdATbmcoAsM3mHhz58X7c1qf8LNuB93o/1N2xXXZI3NWOhOjlviTc2DAhffXDwlMJSYUhldnwtDKmLM1mooJzLgm2p9w7gRD7WPqEqZFq9uFDK69P9uX5T9hFHg=,iv:rAE4sYxxLou4tyD4RWTp3LjQP0cya95coy1MvwfEK/U=,tag:u4SSk8SZFlj0ks7d6tDocw==,type:str]", | ||
| 3 | "sops": { | ||
| 4 | "age": [ | ||
| 5 | { | ||
| 6 | "recipient": "age1rmmhetcmllq0ahl5qznlr0eya2zdxwl9h6y5wnl97d2wtyx5t99sm2u866", | ||
| 7 | "enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSB2KzdNUWhEcDB6QmtUTnVh\nNS9Nc2I4UjAzekxhRXo1UmY3SklPejV1TURJCm9NY2lVOERoMDFKTU56Mmh1NHEr\naGV4M1RoVldHV0xyc3Z0MnVqakpjMFUKLS0tIEYxSk9OUm9kMkdtcG5POWRGQVkx\nY1FEaXYwMGo0L0Z0aTVTZDA5aUFDWEUKJ+e/7lR/rNPNVnIy+wkiKiAYMxWp4L7q\nwnSTx451vSnxv9j3JWB43Y7XQC08cisWDj06ULw8FnEbKYOvTYj9mQ==\n-----END AGE ENCRYPTED FILE-----\n" | ||
| 8 | }, | ||
| 9 | { | ||
| 10 | "recipient": "age19a7j77w267z04zls7m28a8hj4a0g5af6ltye2d5wypg33c3l89csd4r9zq", | ||
| 11 | "enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBwOTU3dUEzaXM5T1VkbDRO\nMm14OG1mUkk2bDRhdnBsMHBkc3kvUzlyNlQwCktFSHJhMnhoQ2J6bC9vUHNLWTRC\nRFpYeHo3N2xjWUhjQnRwQ2Nrc1pRUmsKLS0tIDdPeFBVdkxDd1JWSmcxQ0tLMTBD\ncHU3VExZOUhYUlJvbGNoK3FMK2VIbGMKFk94P9aBY04CPIi983f3Aalgh4fnU+/K\n2mxawSMf9jz8704N5XJfmr2hwNy8hqLIn8bjsEMAPTfE1YBGga4w0g==\n-----END AGE ENCRYPTED FILE-----\n" | ||
| 12 | } | ||
| 13 | ], | ||
| 14 | "lastmodified": "2025-05-24T09:42:23Z", | ||
| 15 | "mac": "ENC[AES256_GCM,data:diCeJGvBmM0Ng722eKoFwDe7pqZrdLPSLn5j9LfdaFI64BAbSbA5bAq4NFXqdJ1vttarD2A5rEafYoXUxP8228x2GhNyWUGW5AWgBjVPUc59gjs4wYKR5HlkVMIadhTwNheEyoEjrxX40GNBgCG7X3ocOtOYKbKECp433gdAPDg=,iv:d+yJMWj2RyFnveo2ZNrpNeV+amXM+H7vdC0A2F7mwjA=,tag:yjibG2iusdprp0ORghYWhw==,type:str]", | ||
| 16 | "unencrypted_suffix": "_unencrypted", | ||
| 17 | "version": "3.10.2" | ||
| 18 | } | ||
| 19 | } | ||
diff --git a/hosts/surtr/dns/keys/paperless.yggdrasil.li_acme b/hosts/surtr/dns/keys/paperless.yggdrasil.li_acme new file mode 100644 index 00000000..bc4640db --- /dev/null +++ b/hosts/surtr/dns/keys/paperless.yggdrasil.li_acme | |||
| @@ -0,0 +1,24 @@ | |||
| 1 | { | ||
| 2 | "data": "ENC[AES256_GCM,data:wOl8KLHD0H+btq0A3UreyVF9bOXZsiTwWJkVH8GubyIQDyiDC8vQm+dfv0rz8TwcBWYpC4aMIPPflG2HsdYO4rKGQ/nBmWmxhNXjpnyRo8iKM1BGb5bxNe4eVcUVhI60NuRJDRLmtDp+0rYGT/MVYp0/mHBINsQCXWBPDoaN2PI2GSnRag/x0wcL27xgH6NDd8glcdCN5nCAPDvazA3LialkXXv7/cceA5Q/Ee6HGzPP0w212/UvBm07Z5tXnHiy5cTbAGTUBfIqC8n501jtaQhpMh/yzA1R8KwUrw==,iv:bLzsthCaanNikNS2Es4J1++E5lijEbjyW5hU4zzNBcg=,tag:eWfZ3AtcSAGv8jWXzqlAwQ==,type:str]", | ||
| 3 | "sops": { | ||
| 4 | "kms": null, | ||
| 5 | "gcp_kms": null, | ||
| 6 | "azure_kv": null, | ||
| 7 | "hc_vault": null, | ||
| 8 | "age": [ | ||
| 9 | { | ||
| 10 | "recipient": "age1rmmhetcmllq0ahl5qznlr0eya2zdxwl9h6y5wnl97d2wtyx5t99sm2u866", | ||
| 11 | "enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBNYWZKOHd2UmxWYVlIamk4\nZ04zQnAwcDdsSDBDWUZnekNva3BzRERyWXljCmNvUU1Fczc2aUN6VGl6NEJ6MGIx\nWHVpeVluWnRnbjNadGxkSmYyNE1rZzQKLS0tIGpkYVZQRDJGS21ZZHdlRk1MMm02\nQ09aanNXSWltNi9QeUNtUVQ2UEZybmMK6/qcNYLMcyKTmtROX+ZsRqDxMXwkXiAV\ndsdsWJ5+zSJuK5SEIh0fqEZ/t4pxnMcr1WieETgLSd+w0sNQS7EKPQ==\n-----END AGE ENCRYPTED FILE-----\n" | ||
| 12 | }, | ||
| 13 | { | ||
| 14 | "recipient": "age19a7j77w267z04zls7m28a8hj4a0g5af6ltye2d5wypg33c3l89csd4r9zq", | ||
| 15 | "enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSByRjdJOHdjamRHVjlZOTNV\nbTBuam5vNERIWTB6T0JkR2pLUnlQN1BGbUJNClhrZ2hPRWZtT3BERFNwdmNEMmVu\nT0dxcjNkNGIvMVJQWENoUmRhTGd6SXcKLS0tIDV6WDd1bks4K1VuVkgybjdMd0w0\nMHBsT3FmOWU0WnJsM2diQm1sTU1ON2MKtf5HZ0S1cLMx98vDKRKamS7aHIJZ0OnA\nzH4VoeVm+PKsOeqVfY+gMHLdaMEWLKYsz3B8bxIoL5pvnCdT1QAN2A==\n-----END AGE ENCRYPTED FILE-----\n" | ||
| 16 | } | ||
| 17 | ], | ||
| 18 | "lastmodified": "2025-02-13T19:23:42Z", | ||
| 19 | "mac": "ENC[AES256_GCM,data:o7zNTjkohzAouYpJUGqf8DUfYf4/g3GZgc+4cf+PjI0OF8uc1WDCPvliBFe6pf/8QMhV5DFWd2SfszWnpnQhtiIVG/2BEk5sw3P6r/SUbSErakFYHueVQKp+9rdxK6uKcHUYhO46E332AwIxTuvNeHtSBMxx0kAwQPuuD/u3L4A=,iv:aiM0sGyGMk5lfBOpB2bDFCY+UfWwyUNixieww6eOSLs=,tag:MI7xJ7RsyZgQfF1SBVVmcQ==,type:str]", | ||
| 20 | "pgp": null, | ||
| 21 | "unencrypted_suffix": "_unencrypted", | ||
| 22 | "version": "3.9.4" | ||
| 23 | } | ||
| 24 | } \ No newline at end of file | ||
diff --git a/hosts/surtr/dns/zones/email.nights.soa b/hosts/surtr/dns/zones/email.nights.soa index 913a88d4..34209a99 100644 --- a/hosts/surtr/dns/zones/email.nights.soa +++ b/hosts/surtr/dns/zones/email.nights.soa | |||
| @@ -1,7 +1,7 @@ | |||
| 1 | $ORIGIN nights.email. | 1 | $ORIGIN nights.email. |
| 2 | $TTL 3600 | 2 | $TTL 3600 |
| 3 | @ IN SOA ns.yggdrasil.li. hostmaster.yggdrasil.li ( | 3 | @ IN SOA ns.yggdrasil.li. hostmaster.yggdrasil.li ( |
| 4 | 2023013000 ; serial | 4 | 2025060700 ; serial |
| 5 | 10800 ; refresh | 5 | 10800 ; refresh |
| 6 | 3600 ; retry | 6 | 3600 ; retry |
| 7 | 604800 ; expire | 7 | 604800 ; expire |
| @@ -27,11 +27,7 @@ $TTL 3600 | |||
| 27 | 27 | ||
| 28 | _acme-challenge IN NS ns.yggdrasil.li. | 28 | _acme-challenge IN NS ns.yggdrasil.li. |
| 29 | 29 | ||
| 30 | ymir._domainkey IN TXT ( | 30 | ymir._domainkey IN CNAME ymir._domainkey.yggdrasil.li. |
| 31 | "v=DKIM1;k=rsa;p=MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAq3cCKlk+VPhyAanLZTM0BCzUT/+fmxHioZcFk0uJk1akBYj7BRofR7eVNcLKpm3rwYMQgE+9vJH9p8SV6tws9EcWc8SMCqqGZlREYM7PmLDiTSK/vjCzkygfgFCb0EBNsY2A/fpP4rTeoxrbcBSvMkq97iY5rwyw4wXZVZXLiDaCj23s8POoxTk1ClqUJZJQ5x2" | ||
| 32 | "qzrC0RfN5kLZ9A7Gq2jB09vNxpXHYqABA0bJv88JiZM7hfkp9IafJZ+yCVMaBcJs4DAxnTjNAuFD9gm+qSFVY8+yeXqL6Qjo5PbruhyZRBW8RgRYT8t5n07XRglMGKKGMwOGLanrltcyXqB+GsDZBD36RAAwjFadnxdpDyRv4SgRP7ff2tKRrORYpmpN+mKdqw5j3J/nP6bXV1oAkyh9XQkPEIDi81WT87EZziTElDzVp6A2qFOxqucAovoRk24" | ||
| 33 | "7vlsns1FApFRsp9mja0UZNObyKD1M6tP9Ep7lS76tFGMk+WDvXRJH5LEsyCpu7sSyl1r/O0M4K+KldRCqLlZd7rf8F5P8T0dn1azk05g7F4p0N/y9GNdzXbPZ9u0eZdI7SEdh8ZoOZp7NVZiBFfbWLSS5ZtyA2kbBa4i7GJ/cuAbEKOmqAkeQPiu96TGIcyjkXjS6mTPI+9UmKZYZC+OM8XdJ02y5KRoonCc19ZS8CAwEAAQ==" | ||
| 34 | ) | ||
| 35 | 31 | ||
| 36 | _xmpp-client._tcp IN SRV 5 0 5222 ymir.yggdrasil.li. | 32 | _xmpp-client._tcp IN SRV 5 0 5222 ymir.yggdrasil.li. |
| 37 | _xmpp-server._tcp IN SRV 5 0 5269 ymir.yggdrasil.li. | 33 | _xmpp-server._tcp IN SRV 5 0 5269 ymir.yggdrasil.li. |
diff --git a/hosts/surtr/dns/zones/li.141.soa b/hosts/surtr/dns/zones/li.141.soa index d42b4719..78d137bb 100644 --- a/hosts/surtr/dns/zones/li.141.soa +++ b/hosts/surtr/dns/zones/li.141.soa | |||
| @@ -1,7 +1,7 @@ | |||
| 1 | $ORIGIN 141.li. | 1 | $ORIGIN 141.li. |
| 2 | $TTL 3600 | 2 | $TTL 3600 |
| 3 | @ IN SOA ns.yggdrasil.li. hostmaster.yggdrasil.li ( | 3 | @ IN SOA ns.yggdrasil.li. hostmaster.yggdrasil.li ( |
| 4 | 2024102100 ; serial | 4 | 2025060701 ; serial |
| 5 | 10800 ; refresh | 5 | 10800 ; refresh |
| 6 | 3600 ; retry | 6 | 3600 ; retry |
| 7 | 604800 ; expire | 7 | 604800 ; expire |
| @@ -45,11 +45,8 @@ ymir IN AAAA 2a03:4000:6:d004:: | |||
| 45 | ymir IN MX 0 ymir.yggdrasil.li | 45 | ymir IN MX 0 ymir.yggdrasil.li |
| 46 | ymir IN TXT "v=spf1 redirect=ymir.yggdrasil.li" | 46 | ymir IN TXT "v=spf1 redirect=ymir.yggdrasil.li" |
| 47 | 47 | ||
| 48 | ymir._domainkey IN TXT ( | 48 | ymir._domainkey IN CNAME ymir._domainkey.yggdrasil.li. |
| 49 | "v=DKIM1;k=rsa;p=MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAq3cCKlk+VPhyAanLZTM0BCzUT/+fmxHioZcFk0uJk1akBYj7BRofR7eVNcLKpm3rwYMQgE+9vJH9p8SV6tws9EcWc8SMCqqGZlREYM7PmLDiTSK/vjCzkygfgFCb0EBNsY2A/fpP4rTeoxrbcBSvMkq97iY5rwyw4wXZVZXLiDaCj23s8POoxTk1ClqUJZJQ5x2" | 49 | surtr._domainkey IN CNAME surtr._domainkey.yggdrasil.li. |
| 50 | "qzrC0RfN5kLZ9A7Gq2jB09vNxpXHYqABA0bJv88JiZM7hfkp9IafJZ+yCVMaBcJs4DAxnTjNAuFD9gm+qSFVY8+yeXqL6Qjo5PbruhyZRBW8RgRYT8t5n07XRglMGKKGMwOGLanrltcyXqB+GsDZBD36RAAwjFadnxdpDyRv4SgRP7ff2tKRrORYpmpN+mKdqw5j3J/nP6bXV1oAkyh9XQkPEIDi81WT87EZziTElDzVp6A2qFOxqucAovoRk24" | ||
| 51 | "7vlsns1FApFRsp9mja0UZNObyKD1M6tP9Ep7lS76tFGMk+WDvXRJH5LEsyCpu7sSyl1r/O0M4K+KldRCqLlZd7rf8F5P8T0dn1azk05g7F4p0N/y9GNdzXbPZ9u0eZdI7SEdh8ZoOZp7NVZiBFfbWLSS5ZtyA2kbBa4i7GJ/cuAbEKOmqAkeQPiu96TGIcyjkXjS6mTPI+9UmKZYZC+OM8XdJ02y5KRoonCc19ZS8CAwEAAQ==" | ||
| 52 | ) | ||
| 53 | 50 | ||
| 54 | _xmpp-client._tcp IN SRV 5 0 5222 ymir.yggdrasil.li. | 51 | _xmpp-client._tcp IN SRV 5 0 5222 ymir.yggdrasil.li. |
| 55 | _xmpp-server._tcp IN SRV 5 0 5269 ymir.yggdrasil.li. | 52 | _xmpp-server._tcp IN SRV 5 0 5269 ymir.yggdrasil.li. |
| @@ -59,5 +56,3 @@ _infinoted._tcp IN SRV 5 0 6523 ymir.yggdrasil.li. | |||
| 59 | _submission._tcp IN SRV 5 0 25 ymir.yggdrasil.li. | 56 | _submission._tcp IN SRV 5 0 25 ymir.yggdrasil.li. |
| 60 | _imap._tcp IN SRV 5 0 143 ymir.yggdrasil.li. | 57 | _imap._tcp IN SRV 5 0 143 ymir.yggdrasil.li. |
| 61 | _imaps._tcp IN SRV 5 0 993 ymir.yggdrasil.li. | 58 | _imaps._tcp IN SRV 5 0 993 ymir.yggdrasil.li. |
| 62 | |||
| 63 | _factorio._udp IN SRV 5 0 34197 game01.yggdrasil.li. | ||
diff --git a/hosts/surtr/dns/zones/li.kleen.soa b/hosts/surtr/dns/zones/li.kleen.soa index a1c7d35a..5dd3e697 100644 --- a/hosts/surtr/dns/zones/li.kleen.soa +++ b/hosts/surtr/dns/zones/li.kleen.soa | |||
| @@ -1,7 +1,7 @@ | |||
| 1 | $ORIGIN kleen.li. | 1 | $ORIGIN kleen.li. |
| 2 | $TTL 3600 | 2 | $TTL 3600 |
| 3 | @ IN SOA ns.yggdrasil.li. hostmaster.yggdrasil.li ( | 3 | @ IN SOA ns.yggdrasil.li. hostmaster.yggdrasil.li ( |
| 4 | 2023013000 ; serial | 4 | 2025060701 ; serial |
| 5 | 10800 ; refresh | 5 | 10800 ; refresh |
| 6 | 3600 ; retry | 6 | 3600 ; retry |
| 7 | 604800 ; expire | 7 | 604800 ; expire |
| @@ -27,11 +27,8 @@ $TTL 3600 | |||
| 27 | 27 | ||
| 28 | _acme-challenge IN NS ns.yggdrasil.li. | 28 | _acme-challenge IN NS ns.yggdrasil.li. |
| 29 | 29 | ||
| 30 | ymir._domainkey IN TXT ( | 30 | ymir._domainkey IN CNAME ymir._domainkey.yggdrasil.li. |
| 31 | "v=DKIM1;k=rsa;p=MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAq3cCKlk+VPhyAanLZTM0BCzUT/+fmxHioZcFk0uJk1akBYj7BRofR7eVNcLKpm3rwYMQgE+9vJH9p8SV6tws9EcWc8SMCqqGZlREYM7PmLDiTSK/vjCzkygfgFCb0EBNsY2A/fpP4rTeoxrbcBSvMkq97iY5rwyw4wXZVZXLiDaCj23s8POoxTk1ClqUJZJQ5x2" | 31 | surtr._domainkey IN CNAME surtr._domainkey.yggdrasil.li. |
| 32 | "qzrC0RfN5kLZ9A7Gq2jB09vNxpXHYqABA0bJv88JiZM7hfkp9IafJZ+yCVMaBcJs4DAxnTjNAuFD9gm+qSFVY8+yeXqL6Qjo5PbruhyZRBW8RgRYT8t5n07XRglMGKKGMwOGLanrltcyXqB+GsDZBD36RAAwjFadnxdpDyRv4SgRP7ff2tKRrORYpmpN+mKdqw5j3J/nP6bXV1oAkyh9XQkPEIDi81WT87EZziTElDzVp6A2qFOxqucAovoRk24" | ||
| 33 | "7vlsns1FApFRsp9mja0UZNObyKD1M6tP9Ep7lS76tFGMk+WDvXRJH5LEsyCpu7sSyl1r/O0M4K+KldRCqLlZd7rf8F5P8T0dn1azk05g7F4p0N/y9GNdzXbPZ9u0eZdI7SEdh8ZoOZp7NVZiBFfbWLSS5ZtyA2kbBa4i7GJ/cuAbEKOmqAkeQPiu96TGIcyjkXjS6mTPI+9UmKZYZC+OM8XdJ02y5KRoonCc19ZS8CAwEAAQ==" | ||
| 34 | ) | ||
| 35 | 32 | ||
| 36 | _xmpp-client._tcp IN SRV 5 0 5222 ymir.yggdrasil.li. | 33 | _xmpp-client._tcp IN SRV 5 0 5222 ymir.yggdrasil.li. |
| 37 | _xmpp-server._tcp IN SRV 5 0 5269 ymir.yggdrasil.li. | 34 | _xmpp-server._tcp IN SRV 5 0 5269 ymir.yggdrasil.li. |
diff --git a/hosts/surtr/dns/zones/li.synapse.soa b/hosts/surtr/dns/zones/li.synapse.soa index 086d4a85..247cf025 100644 --- a/hosts/surtr/dns/zones/li.synapse.soa +++ b/hosts/surtr/dns/zones/li.synapse.soa | |||
| @@ -1,7 +1,7 @@ | |||
| 1 | $ORIGIN synapse.li. | 1 | $ORIGIN synapse.li. |
| 2 | $TTL 3600 | 2 | $TTL 3600 |
| 3 | @ IN SOA ns.yggdrasil.li. hostmaster.yggdrasil.li ( | 3 | @ IN SOA ns.yggdrasil.li. hostmaster.yggdrasil.li ( |
| 4 | 2023092100 ; serial | 4 | 2025060701 ; serial |
| 5 | 10800 ; refresh | 5 | 10800 ; refresh |
| 6 | 3600 ; retry | 6 | 3600 ; retry |
| 7 | 604800 ; expire | 7 | 604800 ; expire |
diff --git a/hosts/surtr/dns/zones/li.xmpp.soa b/hosts/surtr/dns/zones/li.xmpp.soa deleted file mode 100644 index a9e98fb4..00000000 --- a/hosts/surtr/dns/zones/li.xmpp.soa +++ /dev/null | |||
| @@ -1,43 +0,0 @@ | |||
| 1 | $ORIGIN xmpp.li. | ||
| 2 | $TTL 3600 | ||
| 3 | @ IN SOA ns.yggdrasil.li. hostmaster.yggdrasil.li ( | ||
| 4 | 2023013000 ; serial | ||
| 5 | 10800 ; refresh | ||
| 6 | 3600 ; retry | ||
| 7 | 604800 ; expire | ||
| 8 | 3600 ; min TTL | ||
| 9 | ) | ||
| 10 | IN NS ns.yggdrasil.li. | ||
| 11 | IN NS ns.inwx.de. | ||
| 12 | IN NS ns2.inwx.de. | ||
| 13 | IN NS ns3.inwx.eu. | ||
| 14 | |||
| 15 | @ IN CAA 128 issue "letsencrypt.org; validationmethods=dns-01" | ||
| 16 | @ IN CAA 128 iodef "mailto:caa@yggdrasil.li" | ||
| 17 | |||
| 18 | @ IN A 188.68.51.254 | ||
| 19 | @ IN AAAA 2a03:4000:6:d004:: | ||
| 20 | @ IN MX 0 ymir.yggdrasil.li. | ||
| 21 | @ IN TXT "v=spf1 redirect=yggdrasil.li" | ||
| 22 | |||
| 23 | * IN A 188.68.51.254 | ||
| 24 | * IN AAAA 2a03:4000:6:d004:: | ||
| 25 | * IN MX 0 ymir.yggdrasil.li. | ||
| 26 | * IN TXT "v=spf1 redirect=yggdrasil.li" | ||
| 27 | |||
| 28 | _acme-challenge IN NS ns.yggdrasil.li. | ||
| 29 | |||
| 30 | ymir._domainkey IN TXT ( | ||
| 31 | "v=DKIM1;k=rsa;p=MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAq3cCKlk+VPhyAanLZTM0BCzUT/+fmxHioZcFk0uJk1akBYj7BRofR7eVNcLKpm3rwYMQgE+9vJH9p8SV6tws9EcWc8SMCqqGZlREYM7PmLDiTSK/vjCzkygfgFCb0EBNsY2A/fpP4rTeoxrbcBSvMkq97iY5rwyw4wXZVZXLiDaCj23s8POoxTk1ClqUJZJQ5x2" | ||
| 32 | "qzrC0RfN5kLZ9A7Gq2jB09vNxpXHYqABA0bJv88JiZM7hfkp9IafJZ+yCVMaBcJs4DAxnTjNAuFD9gm+qSFVY8+yeXqL6Qjo5PbruhyZRBW8RgRYT8t5n07XRglMGKKGMwOGLanrltcyXqB+GsDZBD36RAAwjFadnxdpDyRv4SgRP7ff2tKRrORYpmpN+mKdqw5j3J/nP6bXV1oAkyh9XQkPEIDi81WT87EZziTElDzVp6A2qFOxqucAovoRk24" | ||
| 33 | "7vlsns1FApFRsp9mja0UZNObyKD1M6tP9Ep7lS76tFGMk+WDvXRJH5LEsyCpu7sSyl1r/O0M4K+KldRCqLlZd7rf8F5P8T0dn1azk05g7F4p0N/y9GNdzXbPZ9u0eZdI7SEdh8ZoOZp7NVZiBFfbWLSS5ZtyA2kbBa4i7GJ/cuAbEKOmqAkeQPiu96TGIcyjkXjS6mTPI+9UmKZYZC+OM8XdJ02y5KRoonCc19ZS8CAwEAAQ==" | ||
| 34 | ) | ||
| 35 | |||
| 36 | _xmpp-client._tcp IN SRV 5 0 5222 ymir.yggdrasil.li. | ||
| 37 | _xmpp-server._tcp IN SRV 5 0 5269 ymir.yggdrasil.li. | ||
| 38 | |||
| 39 | _infinoted._tcp IN SRV 5 0 6523 ymir.yggdrasil.li. | ||
| 40 | |||
| 41 | _submission._tcp IN SRV 5 0 25 ymir.yggdrasil.li. | ||
| 42 | _imap._tcp IN SRV 5 0 143 ymir.yggdrasil.li. | ||
| 43 | _imaps._tcp IN SRV 5 0 993 ymir.yggdrasil.li. | ||
diff --git a/hosts/surtr/dns/zones/li.yggdrasil.soa b/hosts/surtr/dns/zones/li.yggdrasil.soa index 092d23ec..500194ae 100644 --- a/hosts/surtr/dns/zones/li.yggdrasil.soa +++ b/hosts/surtr/dns/zones/li.yggdrasil.soa | |||
| @@ -1,7 +1,7 @@ | |||
| 1 | $ORIGIN yggdrasil.li. | 1 | $ORIGIN yggdrasil.li. |
| 2 | $TTL 3600 | 2 | $TTL 3600 |
| 3 | @ IN SOA ns.yggdrasil.li. hostmaster.yggdrasil.li ( | 3 | @ IN SOA ns.yggdrasil.li. hostmaster.yggdrasil.li ( |
| 4 | 2024102100 ; serial | 4 | 2025060700 ; serial |
| 5 | 10800 ; refresh | 5 | 10800 ; refresh |
| 6 | 3600 ; retry | 6 | 3600 ; retry |
| 7 | 604800 ; expire | 7 | 604800 ; expire |
| @@ -69,12 +69,54 @@ _acme-challenge.app.etesync IN NS ns.yggdrasil.li. | |||
| 69 | 69 | ||
| 70 | app.etesync IN HTTPS 1 . alpn="h2,h3" ipv4hint="202.61.241.61" ipv6hint="2a03:4000:52:ada::" | 70 | app.etesync IN HTTPS 1 . alpn="h2,h3" ipv4hint="202.61.241.61" ipv6hint="2a03:4000:52:ada::" |
| 71 | 71 | ||
| 72 | immich IN A 202.61.241.61 | ||
| 73 | immich IN AAAA 2a03:4000:52:ada:: | ||
| 74 | immich IN MX 0 surtr.yggdrasil.li | ||
| 75 | immich IN TXT "v=spf1 redirect=surtr.yggdrasil.li" | ||
| 76 | _acme-challenge.immich IN NS ns.yggdrasil.li. | ||
| 77 | |||
| 78 | immich IN HTTPS 1 . alpn="h2,h3" ipv4hint="202.61.241.61" ipv6hint="2a03:4000:52:ada::" | ||
| 79 | |||
| 80 | paperless IN A 202.61.241.61 | ||
| 81 | paperless IN AAAA 2a03:4000:52:ada:: | ||
| 82 | paperless IN MX 0 surtr.yggdrasil.li | ||
| 83 | paperless IN TXT "v=spf1 redirect=surtr.yggdrasil.li" | ||
| 84 | _acme-challenge.paperless IN NS ns.yggdrasil.li. | ||
| 85 | |||
| 86 | paperless IN HTTPS 1 . alpn="h2,h3" ipv4hint="202.61.241.61" ipv6hint="2a03:4000:52:ada::" | ||
| 87 | |||
| 88 | hledger IN A 202.61.241.61 | ||
| 89 | hledger IN AAAA 2a03:4000:52:ada:: | ||
| 90 | hledger IN MX 0 surtr.yggdrasil.li | ||
| 91 | hledger IN TXT "v=spf1 redirect=surtr.yggdrasil.li" | ||
| 92 | _acme-challenge.hledger IN NS ns.yggdrasil.li. | ||
| 93 | |||
| 94 | hledger IN HTTPS 1 . alpn="h2,h3" ipv4hint="202.61.241.61" ipv6hint="2a03:4000:52:ada::" | ||
| 95 | |||
| 96 | audiobookshelf IN A 202.61.241.61 | ||
| 97 | audiobookshelf IN AAAA 2a03:4000:52:ada:: | ||
| 98 | audiobookshelf IN MX 0 surtr.yggdrasil.li | ||
| 99 | audiobookshelf IN TXT "v=spf1 redirect=surtr.yggdrasil.li" | ||
| 100 | _acme-challenge.audiobookshelf IN NS ns.yggdrasil.li. | ||
| 101 | |||
| 102 | audiobookshelf IN HTTPS 1 . alpn="h2,h3" ipv4hint="202.61.241.61" ipv6hint="2a03:4000:52:ada::" | ||
| 103 | |||
| 104 | kimai IN A 202.61.241.61 | ||
| 105 | kimai IN AAAA 2a03:4000:52:ada:: | ||
| 106 | kimai IN MX 0 surtr.yggdrasil.li | ||
| 107 | kimai IN TXT "v=spf1 redirect=surtr.yggdrasil.li" | ||
| 108 | _acme-challenge.kimai IN NS ns.yggdrasil.li. | ||
| 109 | |||
| 110 | kimai IN HTTPS 1 . alpn="h2,h3" ipv4hint="202.61.241.61" ipv6hint="2a03:4000:52:ada::" | ||
| 111 | |||
| 72 | vidhar IN AAAA 2a03:4000:52:ada:4:1:: | 112 | vidhar IN AAAA 2a03:4000:52:ada:4:1:: |
| 73 | vidhar IN MX 0 ymir.yggdrasil.li | 113 | vidhar IN MX 0 ymir.yggdrasil.li |
| 74 | vidhar IN TXT "v=spf1 redirect=yggdrasil.li" | 114 | vidhar IN TXT "v=spf1 redirect=yggdrasil.li" |
| 75 | 115 | ||
| 76 | mailout IN A 188.68.51.254 | 116 | mailout IN A 188.68.51.254 |
| 77 | mailout IN AAAA 2a03:4000:6:d004:: | 117 | mailout IN AAAA 2a03:4000:6:d004:: |
| 118 | mailout IN A 202.61.241.61 | ||
| 119 | mailout IN AAAA 2a03:4000:52:ada:: | ||
| 78 | mailout IN MX 0 ymir.yggdrasil.li | 120 | mailout IN MX 0 ymir.yggdrasil.li |
| 79 | mailout IN TXT "v=spf1 redirect=yggdrasil.li" | 121 | mailout IN TXT "v=spf1 redirect=yggdrasil.li" |
| 80 | 122 | ||
| @@ -96,6 +138,3 @@ _infinoted._tcp IN SRV 5 0 6523 ymir.yggdrasil.li. | |||
| 96 | _submission._tcp IN SRV 5 0 25 ymir.yggdrasil.li. | 138 | _submission._tcp IN SRV 5 0 25 ymir.yggdrasil.li. |
| 97 | _imap._tcp IN SRV 5 0 143 ymir.yggdrasil.li. | 139 | _imap._tcp IN SRV 5 0 143 ymir.yggdrasil.li. |
| 98 | _imaps._tcp IN SRV 5 0 993 ymir.yggdrasil.li. | 140 | _imaps._tcp IN SRV 5 0 993 ymir.yggdrasil.li. |
| 99 | |||
| 100 | game01 IN A 94.16.107.151 | ||
| 101 | game01 IN AAAA 2a03:4000:50:13d:34ee:a2ff:fed0:328f | ||
diff --git a/hosts/surtr/dns/zones/org.dirty-haskell.soa b/hosts/surtr/dns/zones/org.dirty-haskell.soa deleted file mode 100644 index 27f0d7f9..00000000 --- a/hosts/surtr/dns/zones/org.dirty-haskell.soa +++ /dev/null | |||
| @@ -1,34 +0,0 @@ | |||
| 1 | $ORIGIN dirty-haskell.org. | ||
| 2 | $TTL 3600 | ||
| 3 | @ IN SOA ns.yggdrasil.li. hostmaster.yggdrasil.li ( | ||
| 4 | 2023013000 ; serial | ||
| 5 | 10800 ; refresh | ||
| 6 | 3600 ; retry | ||
| 7 | 604800 ; expire | ||
| 8 | 3600 ; min TTL | ||
| 9 | ) | ||
| 10 | IN NS ns.yggdrasil.li. | ||
| 11 | IN NS ns.inwx.de. | ||
| 12 | IN NS ns2.inwx.de. | ||
| 13 | IN NS ns3.inwx.eu. | ||
| 14 | |||
| 15 | @ IN CAA 128 issue "letsencrypt.org; validationmethods=dns-01" | ||
| 16 | @ IN CAA 128 iodef "mailto:caa@yggdrasil.li" | ||
| 17 | |||
| 18 | @ IN A 188.68.51.254 | ||
| 19 | @ IN AAAA 2a03:4000:6:d004:: | ||
| 20 | @ IN MX 10 ymir.yggdrasil.li. | ||
| 21 | @ IN TXT "v=spf1 redirect=yggdrasil.li" | ||
| 22 | |||
| 23 | * IN A 188.68.51.254 | ||
| 24 | * IN AAAA 2a03:4000:6:d004:: | ||
| 25 | * IN MX 0 ymir.yggdrasil.li. | ||
| 26 | * IN TXT "v=spf1 redirect=yggdrasil.li" | ||
| 27 | |||
| 28 | _acme-challenge IN NS ns.yggdrasil.li. | ||
| 29 | |||
| 30 | ymir._domainkey IN TXT ( | ||
| 31 | "v=DKIM1;k=rsa;p=MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAq3cCKlk+VPhyAanLZTM0BCzUT/+fmxHioZcFk0uJk1akBYj7BRofR7eVNcLKpm3rwYMQgE+9vJH9p8SV6tws9EcWc8SMCqqGZlREYM7PmLDiTSK/vjCzkygfgFCb0EBNsY2A/fpP4rTeoxrbcBSvMkq97iY5rwyw4wXZVZXLiDaCj23s8POoxTk1ClqUJZJQ5x2" | ||
| 32 | "qzrC0RfN5kLZ9A7Gq2jB09vNxpXHYqABA0bJv88JiZM7hfkp9IafJZ+yCVMaBcJs4DAxnTjNAuFD9gm+qSFVY8+yeXqL6Qjo5PbruhyZRBW8RgRYT8t5n07XRglMGKKGMwOGLanrltcyXqB+GsDZBD36RAAwjFadnxdpDyRv4SgRP7ff2tKRrORYpmpN+mKdqw5j3J/nP6bXV1oAkyh9XQkPEIDi81WT87EZziTElDzVp6A2qFOxqucAovoRk24" | ||
| 33 | "7vlsns1FApFRsp9mja0UZNObyKD1M6tP9Ep7lS76tFGMk+WDvXRJH5LEsyCpu7sSyl1r/O0M4K+KldRCqLlZd7rf8F5P8T0dn1azk05g7F4p0N/y9GNdzXbPZ9u0eZdI7SEdh8ZoOZp7NVZiBFfbWLSS5ZtyA2kbBa4i7GJ/cuAbEKOmqAkeQPiu96TGIcyjkXjS6mTPI+9UmKZYZC+OM8XdJ02y5KRoonCc19ZS8CAwEAAQ==" | ||
| 34 | ) | ||
diff --git a/hosts/surtr/dns/zones/org.praseodym.soa b/hosts/surtr/dns/zones/org.praseodym.soa index df505b4c..2b97ca19 100644 --- a/hosts/surtr/dns/zones/org.praseodym.soa +++ b/hosts/surtr/dns/zones/org.praseodym.soa | |||
| @@ -1,7 +1,7 @@ | |||
| 1 | $ORIGIN praseodym.org. | 1 | $ORIGIN praseodym.org. |
| 2 | $TTL 3600 | 2 | $TTL 3600 |
| 3 | @ IN SOA ns.yggdrasil.li. hostmaster.yggdrasil.li ( | 3 | @ IN SOA ns.yggdrasil.li. hostmaster.yggdrasil.li ( |
| 4 | 2023013000 ; serial | 4 | 2025060701 ; serial |
| 5 | 10800 ; refresh | 5 | 10800 ; refresh |
| 6 | 3600 ; retry | 6 | 3600 ; retry |
| 7 | 604800 ; expire | 7 | 604800 ; expire |
| @@ -32,11 +32,8 @@ surtr IN AAAA 2a03:4000:52:ada:: | |||
| 32 | surtr IN MX 0 ymir.yggdrasil.li | 32 | surtr IN MX 0 ymir.yggdrasil.li |
| 33 | surtr IN TXT "v=spf1 redirect=yggdrasil.li" | 33 | surtr IN TXT "v=spf1 redirect=yggdrasil.li" |
| 34 | 34 | ||
| 35 | ymir._domainkey IN TXT ( | 35 | ymir._domainkey IN CNAME ymir._domainkey.yggdrasil.li. |
| 36 | "v=DKIM1;k=rsa;p=MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAq3cCKlk+VPhyAanLZTM0BCzUT/+fmxHioZcFk0uJk1akBYj7BRofR7eVNcLKpm3rwYMQgE+9vJH9p8SV6tws9EcWc8SMCqqGZlREYM7PmLDiTSK/vjCzkygfgFCb0EBNsY2A/fpP4rTeoxrbcBSvMkq97iY5rwyw4wXZVZXLiDaCj23s8POoxTk1ClqUJZJQ5x2" | 36 | surtr._domainkey IN CNAME surtr._domainkey.yggdrasil.li. |
| 37 | "qzrC0RfN5kLZ9A7Gq2jB09vNxpXHYqABA0bJv88JiZM7hfkp9IafJZ+yCVMaBcJs4DAxnTjNAuFD9gm+qSFVY8+yeXqL6Qjo5PbruhyZRBW8RgRYT8t5n07XRglMGKKGMwOGLanrltcyXqB+GsDZBD36RAAwjFadnxdpDyRv4SgRP7ff2tKRrORYpmpN+mKdqw5j3J/nP6bXV1oAkyh9XQkPEIDi81WT87EZziTElDzVp6A2qFOxqucAovoRk24" | ||
| 38 | "7vlsns1FApFRsp9mja0UZNObyKD1M6tP9Ep7lS76tFGMk+WDvXRJH5LEsyCpu7sSyl1r/O0M4K+KldRCqLlZd7rf8F5P8T0dn1azk05g7F4p0N/y9GNdzXbPZ9u0eZdI7SEdh8ZoOZp7NVZiBFfbWLSS5ZtyA2kbBa4i7GJ/cuAbEKOmqAkeQPiu96TGIcyjkXjS6mTPI+9UmKZYZC+OM8XdJ02y5KRoonCc19ZS8CAwEAAQ==" | ||
| 39 | ) | ||
| 40 | 37 | ||
| 41 | _xmpp-client._tcp IN SRV 5 0 5222 ymir.yggdrasil.li. | 38 | _xmpp-client._tcp IN SRV 5 0 5222 ymir.yggdrasil.li. |
| 42 | _xmpp-server._tcp IN SRV 5 0 5269 ymir.yggdrasil.li. | 39 | _xmpp-server._tcp IN SRV 5 0 5269 ymir.yggdrasil.li. |
diff --git a/hosts/surtr/email/ccert-policy-server/ccert_policy_server/__main__.py b/hosts/surtr/email/ccert-policy-server/ccert_policy_server/__main__.py index 00182523..45619fb0 100644 --- a/hosts/surtr/email/ccert-policy-server/ccert_policy_server/__main__.py +++ b/hosts/surtr/email/ccert-policy-server/ccert_policy_server/__main__.py | |||
| @@ -28,12 +28,14 @@ class PolicyHandler(StreamRequestHandler): | |||
| 28 | 28 | ||
| 29 | allowed = False | 29 | allowed = False |
| 30 | user = None | 30 | user = None |
| 31 | relay_eligible = False | ||
| 31 | if self.args['sasl_username']: | 32 | if self.args['sasl_username']: |
| 32 | user = self.args['sasl_username'] | 33 | user = self.args['sasl_username'] |
| 33 | if self.args['ccert_subject']: | 34 | if self.args['ccert_subject']: |
| 34 | user = self.args['ccert_subject'] | 35 | user = self.args['ccert_subject'] |
| 36 | relay_eligible = True | ||
| 35 | 37 | ||
| 36 | if user: | 38 | if user and '@' in self.args['sender']: |
| 37 | with self.server.db_pool.connection() as conn: | 39 | with self.server.db_pool.connection() as conn: |
| 38 | local, domain = self.args['sender'].split(sep='@', maxsplit=1) | 40 | local, domain = self.args['sender'].split(sep='@', maxsplit=1) |
| 39 | extension = None | 41 | extension = None |
| @@ -44,10 +46,16 @@ class PolicyHandler(StreamRequestHandler): | |||
| 44 | 46 | ||
| 45 | with conn.cursor() as cur: | 47 | with conn.cursor() as cur: |
| 46 | cur.row_factory = namedtuple_row | 48 | cur.row_factory = namedtuple_row |
| 47 | cur.execute('SELECT "mailbox"."mailbox" as "user", "local", "extension", "domain" FROM "mailbox" INNER JOIN "mailbox_mapping" ON "mailbox".id = "mailbox_mapping"."mailbox" WHERE "mailbox"."mailbox" = %(user)s AND ("local" = %(local)s OR "local" IS NULL) AND ("extension" = %(extension)s OR "extension" IS NULL) AND "domain" = %(domain)s', params = {'user': user, 'local': local, 'extension': extension if extension is not None else '', 'domain': domain}, prepare=True) | 49 | |
| 48 | for record in cur: | 50 | if relay_eligible: |
| 49 | logger.debug('Received result: %s', record) | 51 | cur.execute('SELECT EXISTS(SELECT true FROM "mailbox" INNER JOIN "relay_access" ON "mailbox".id = "relay_access"."mailbox" WHERE "mailbox"."mailbox" = %(user)s AND ("domain" = %(domain)s OR %(domain)s ilike CONCAT(\'%%_.\', "domain"))) as "exists"', params = {'user': user, 'domain': domain}) |
| 50 | allowed = True | 52 | if (row := cur.fetchone()) is not None: |
| 53 | allowed = row.exists | ||
| 54 | |||
| 55 | if not allowed: | ||
| 56 | cur.execute('SELECT EXISTS(SELECT true FROM "mailbox" INNER JOIN "mailbox_mapping" ON "mailbox".id = "mailbox_mapping"."mailbox" WHERE "mailbox"."mailbox" = %(user)s AND ("local" = %(local)s OR "local" IS NULL) AND ("extension" = %(extension)s OR "extension" IS NULL) AND "domain" = %(domain)s) as "exists"', params = {'user': user, 'local': local, 'extension': extension if extension is not None else '', 'domain': domain}, prepare=True) | ||
| 57 | if (row := cur.fetchone()) is not None: | ||
| 58 | allowed = row.exists | ||
| 51 | 59 | ||
| 52 | action = '550 5.7.0 Sender address not authorized for current user' | 60 | action = '550 5.7.0 Sender address not authorized for current user' |
| 53 | if allowed: | 61 | if allowed: |
diff --git a/hosts/surtr/email/default.nix b/hosts/surtr/email/default.nix index 4196a8bc..b4b2b5c8 100644 --- a/hosts/surtr/email/default.nix +++ b/hosts/surtr/email/default.nix | |||
| @@ -1,4 +1,4 @@ | |||
| 1 | { config, pkgs, lib, flakeInputs, ... }: | 1 | { config, pkgs, lib, flake, flakeInputs, ... }: |
| 2 | 2 | ||
| 3 | with lib; | 3 | with lib; |
| 4 | 4 | ||
| @@ -15,7 +15,7 @@ let | |||
| 15 | 15 | ||
| 16 | for file in $out/pipe/bin/*; do | 16 | for file in $out/pipe/bin/*; do |
| 17 | wrapProgram $file \ | 17 | wrapProgram $file \ |
| 18 | --set PATH "${pkgs.coreutils}/bin:${pkgs.rspamd}/bin" | 18 | --set PATH "${makeBinPath (with pkgs; [coreutils rspamd])}" |
| 19 | done | 19 | done |
| 20 | ''; | 20 | ''; |
| 21 | }; | 21 | }; |
| @@ -33,12 +33,28 @@ let | |||
| 33 | }); | 33 | }); |
| 34 | }); | 34 | }); |
| 35 | }; | 35 | }; |
| 36 | internal-policy-server = | ||
| 37 | let | ||
| 38 | workspace = flakeInputs.uv2nix.lib.workspace.loadWorkspace { workspaceRoot = ./internal-policy-server; }; | ||
| 39 | pythonSet = flake.lib.pythonSet { | ||
| 40 | inherit pkgs; | ||
| 41 | python = pkgs.python312; | ||
| 42 | overlay = workspace.mkPyprojectOverlay { | ||
| 43 | sourcePreference = "wheel"; | ||
| 44 | }; | ||
| 45 | }; | ||
| 46 | virtualEnv = pythonSet.mkVirtualEnv "internal-policy-server-env" workspace.deps.default; | ||
| 47 | in virtualEnv.overrideAttrs (oldAttrs: { | ||
| 48 | meta = (oldAttrs.meta or {}) // { | ||
| 49 | mainProgram = "internal-policy-server"; | ||
| 50 | }; | ||
| 51 | }); | ||
| 36 | 52 | ||
| 37 | nftables-nologin-script = pkgs.writeScript "nftables-mail-nologin" '' | 53 | nftables-nologin-script = pkgs.resholve.writeScript "nftables-mail-nologin" { |
| 38 | #!${pkgs.zsh}/bin/zsh | 54 | inputs = with pkgs; [inetutils nftables gnugrep findutils]; |
| 39 | 55 | interpreter = lib.getExe pkgs.zsh; | |
| 56 | } '' | ||
| 40 | set -e | 57 | set -e |
| 41 | export PATH="${lib.makeBinPath (with pkgs; [inetutils nftables])}:$PATH" | ||
| 42 | 58 | ||
| 43 | typeset -a as_sets mnt_bys route route6 | 59 | typeset -a as_sets mnt_bys route route6 |
| 44 | as_sets=(${lib.escapeShellArgs config.services.email.nologin.ASSets}) | 60 | as_sets=(${lib.escapeShellArgs config.services.email.nologin.ASSets}) |
| @@ -51,7 +67,7 @@ let | |||
| 51 | elif [[ "''${line}" =~ "^route6:\s+(.+)$" ]]; then | 67 | elif [[ "''${line}" =~ "^route6:\s+(.+)$" ]]; then |
| 52 | route6+=($match[1]) | 68 | route6+=($match[1]) |
| 53 | fi | 69 | fi |
| 54 | done < <(whois -h whois.radb.net "!i''${as_set},1" | egrep -o 'AS[0-9]+' | xargs -- whois -h whois.radb.net -- -i origin) | 70 | done < <(whois -h whois.radb.net "!i''${as_set},1" | grep -Eo 'AS[0-9]+' | xargs whois -h whois.radb.net -- -i origin) |
| 55 | done | 71 | done |
| 56 | for mnt_by in $mnt_bys; do | 72 | for mnt_by in $mnt_bys; do |
| 57 | while IFS=$'\n' read line; do | 73 | while IFS=$'\n' read line; do |
| @@ -108,19 +124,20 @@ in { | |||
| 108 | services.postfix = { | 124 | services.postfix = { |
| 109 | enable = true; | 125 | enable = true; |
| 110 | enableSmtp = false; | 126 | enableSmtp = false; |
| 111 | hostname = "surtr.yggdrasil.li"; | ||
| 112 | recipientDelimiter = ""; | ||
| 113 | setSendmail = true; | 127 | setSendmail = true; |
| 114 | postmasterAlias = ""; rootAlias = ""; extraAliases = ""; | 128 | postmasterAlias = ""; rootAlias = ""; extraAliases = ""; |
| 115 | destination = []; | 129 | settings.main = { |
| 116 | sslCert = "/run/credentials/postfix.service/surtr.yggdrasil.li.pem"; | 130 | recpipient_delimiter = ""; |
| 117 | sslKey = "/run/credentials/postfix.service/surtr.yggdrasil.li.key.pem"; | 131 | mydestination = []; |
| 118 | networks = []; | 132 | mynetworks = []; |
| 119 | config = let | 133 | myhostname = "surtr.yggdrasil.li"; |
| 120 | relay_ccert = "texthash:${pkgs.writeText "relay_ccert" ""}"; | 134 | |
| 121 | in { | ||
| 122 | smtpd_tls_security_level = "may"; | 135 | smtpd_tls_security_level = "may"; |
| 123 | 136 | ||
| 137 | smtpd_tls_chain_files = [ | ||
| 138 | "/run/credentials/postfix.service/surtr.yggdrasil.li.full.pem" | ||
| 139 | ]; | ||
| 140 | |||
| 124 | #the dh params | 141 | #the dh params |
| 125 | smtpd_tls_dh1024_param_file = toString config.security.dhparams.params."postfix-1024".path; | 142 | smtpd_tls_dh1024_param_file = toString config.security.dhparams.params."postfix-1024".path; |
| 126 | smtpd_tls_dh512_param_file = toString config.security.dhparams.params."postfix-512".path; | 143 | smtpd_tls_dh512_param_file = toString config.security.dhparams.params."postfix-512".path; |
| @@ -155,21 +172,14 @@ in { | |||
| 155 | 172 | ||
| 156 | smtp_tls_connection_reuse = true; | 173 | smtp_tls_connection_reuse = true; |
| 157 | 174 | ||
| 158 | tls_server_sni_maps = ''texthash:${pkgs.writeText "sni" ( | 175 | tls_server_sni_maps = "inline:{${concatMapStringsSep ", " (domain: "{ ${domain} = /run/credentials/postfix.service/${removePrefix "." domain}.full.pem }") (concatMap (domain: [domain "mailin.${domain}" "mailsub.${domain}" ".${domain}"]) emailDomains)}}"; |
| 159 | concatMapStringsSep "\n\n" (domain: | ||
| 160 | concatMapStringsSep "\n" (subdomain: "${subdomain} /run/credentials/postfix.service/${removePrefix "." subdomain}.full.pem") | ||
| 161 | [domain "mailin.${domain}" "mailsub.${domain}" ".${domain}"] | ||
| 162 | ) emailDomains | ||
| 163 | )}''; | ||
| 164 | 176 | ||
| 165 | smtp_tls_policy_maps = "socketmap:unix:${config.services.postfix-mta-sts-resolver.settings.path}:postfix"; | 177 | smtp_tls_policy_maps = "socketmap:unix:${config.services.postfix-mta-sts-resolver.settings.path}:postfix"; |
| 166 | 178 | ||
| 167 | local_recipient_maps = ""; | 179 | local_recipient_maps = ""; |
| 168 | 180 | ||
| 169 | # 10 GiB | 181 | message_size_limit = 10 * 1024 * 1024 * 1024; |
| 170 | message_size_limit = "10737418240"; | 182 | mailbox_size_limit = 10 * 1024 * 1024 * 1024; |
| 171 | # 10 GiB | ||
| 172 | mailbox_size_limit = "10737418240"; | ||
| 173 | 183 | ||
| 174 | smtpd_delay_reject = true; | 184 | smtpd_delay_reject = true; |
| 175 | smtpd_helo_required = true; | 185 | smtpd_helo_required = true; |
| @@ -184,25 +194,26 @@ in { | |||
| 184 | dbname = email | 194 | dbname = email |
| 185 | query = SELECT action FROM virtual_mailbox_access WHERE lookup = '%s' | 195 | query = SELECT action FROM virtual_mailbox_access WHERE lookup = '%s' |
| 186 | ''}" | 196 | ''}" |
| 187 | "check_ccert_access ${relay_ccert}" | ||
| 188 | "reject_non_fqdn_helo_hostname" | 197 | "reject_non_fqdn_helo_hostname" |
| 189 | "reject_invalid_helo_hostname" | 198 | "reject_invalid_helo_hostname" |
| 190 | "reject_unauth_destination" | 199 | "reject_unauth_destination" |
| 191 | "reject_unknown_recipient_domain" | 200 | "reject_unknown_recipient_domain" |
| 192 | "reject_unverified_recipient" | 201 | "reject_unverified_recipient" |
| 202 | "check_policy_service unix:/run/postfix-internal-policy.sock" | ||
| 193 | ]; | 203 | ]; |
| 194 | unverified_recipient_reject_code = "550"; | 204 | unverified_recipient_reject_code = "550"; |
| 195 | unverified_recipient_reject_reason = "Recipient address lookup failed"; | 205 | unverified_recipient_reject_reason = "Recipient address lookup failed"; |
| 196 | address_verify_map = "internal:address_verify_map"; | 206 | address_verify_map = "internal:address_verify_map"; |
| 197 | address_verify_positive_expire_time = "1h"; | 207 | address_verify_positive_expire_time = "1h"; |
| 198 | address_verify_positive_refresh_time = "15m"; | 208 | address_verify_positive_refresh_time = "15m"; |
| 199 | address_verify_negative_expire_time = "15s"; | 209 | address_verify_negative_expire_time = "5m"; |
| 200 | address_verify_negative_refresh_time = "5s"; | 210 | address_verify_negative_refresh_time = "1m"; |
| 201 | address_verify_cache_cleanup_interval = "5s"; | 211 | address_verify_cache_cleanup_interval = "12h"; |
| 212 | address_verify_poll_count = "\${stress?15}\${stress:30}"; | ||
| 202 | address_verify_poll_delay = "1s"; | 213 | address_verify_poll_delay = "1s"; |
| 214 | address_verify_sender_ttl = "30045s"; | ||
| 203 | 215 | ||
| 204 | smtpd_relay_restrictions = [ | 216 | smtpd_relay_restrictions = [ |
| 205 | "check_ccert_access ${relay_ccert}" | ||
| 206 | "reject_unauth_destination" | 217 | "reject_unauth_destination" |
| 207 | ]; | 218 | ]; |
| 208 | 219 | ||
| @@ -213,7 +224,7 @@ in { | |||
| 213 | smtpd_client_event_limit_exceptions = ""; | 224 | smtpd_client_event_limit_exceptions = ""; |
| 214 | 225 | ||
| 215 | milter_default_action = "accept"; | 226 | milter_default_action = "accept"; |
| 216 | smtpd_milters = [config.services.opendkim.socket "local:/run/rspamd/rspamd-milter.sock"]; | 227 | smtpd_milters = [config.services.opendkim.socket "local:/run/rspamd/rspamd-milter.sock" "local:/run/postsrsd/postsrsd-milter.sock"]; |
| 217 | non_smtpd_milters = [config.services.opendkim.socket "local:/run/rspamd/rspamd-milter.sock"]; | 228 | non_smtpd_milters = [config.services.opendkim.socket "local:/run/rspamd/rspamd-milter.sock"]; |
| 218 | 229 | ||
| 219 | alias_maps = ""; | 230 | alias_maps = ""; |
| @@ -225,6 +236,37 @@ in { | |||
| 225 | bounce_queue_lifetime = "20m"; | 236 | bounce_queue_lifetime = "20m"; |
| 226 | delay_warning_time = "10m"; | 237 | delay_warning_time = "10m"; |
| 227 | 238 | ||
| 239 | failure_template_file = toString (pkgs.writeText "failure.cf" '' | ||
| 240 | Charset: us-ascii | ||
| 241 | From: Mail Delivery System <MAILER-DAEMON> | ||
| 242 | Subject: Undelivered Mail Returned to Sender | ||
| 243 | Postmaster-Subject: Postmaster Copy: Undelivered Mail | ||
| 244 | |||
| 245 | This is the mail system at host $myhostname. | ||
| 246 | |||
| 247 | I'm sorry to have to inform you that your message could not | ||
| 248 | be delivered to one or more recipients. It's attached below. | ||
| 249 | |||
| 250 | The mail system | ||
| 251 | ''); | ||
| 252 | delay_template_file = toString (pkgs.writeText "delay.cf" '' | ||
| 253 | Charset: us-ascii | ||
| 254 | From: Mail Delivery System <MAILER-DAEMON> | ||
| 255 | Subject: Delayed Mail (still being retried) | ||
| 256 | Postmaster-Subject: Postmaster Warning: Delayed Mail | ||
| 257 | |||
| 258 | This is the mail system at host $myhostname. | ||
| 259 | |||
| 260 | #################################################################### | ||
| 261 | # THIS IS A WARNING ONLY. YOU DO NOT NEED TO RESEND YOUR MESSAGE. # | ||
| 262 | #################################################################### | ||
| 263 | |||
| 264 | Your message could not be delivered for more than $delay_warning_time_minutes minute(s). | ||
| 265 | It will be retried until it is $maximal_queue_lifetime_minutes minute(s) old. | ||
| 266 | |||
| 267 | The mail system | ||
| 268 | ''); | ||
| 269 | |||
| 228 | smtpd_discard_ehlo_keyword_address_maps = "cidr:${pkgs.writeText "esmtp_access" '' | 270 | smtpd_discard_ehlo_keyword_address_maps = "cidr:${pkgs.writeText "esmtp_access" '' |
| 229 | # Allow DSN requests from local subnet only | 271 | # Allow DSN requests from local subnet only |
| 230 | 192.168.0.0/16 silent-discard | 272 | 192.168.0.0/16 silent-discard |
| @@ -235,11 +277,6 @@ in { | |||
| 235 | ::/0 silent-discard, dsn | 277 | ::/0 silent-discard, dsn |
| 236 | ''}"; | 278 | ''}"; |
| 237 | 279 | ||
| 238 | sender_canonical_maps = "tcp:localhost:${toString config.services.postsrsd.forwardPort}"; | ||
| 239 | sender_canonical_classes = "envelope_sender"; | ||
| 240 | recipient_canonical_maps = "tcp:localhost:${toString config.services.postsrsd.reversePort}"; | ||
| 241 | recipient_canonical_classes = ["envelope_recipient" "header_recipient"]; | ||
| 242 | |||
| 243 | virtual_mailbox_domains = ''pgsql:${pkgs.writeText "virtual_mailbox_domains.cf" '' | 280 | virtual_mailbox_domains = ''pgsql:${pkgs.writeText "virtual_mailbox_domains.cf" '' |
| 244 | hosts = postgresql:///email | 281 | hosts = postgresql:///email |
| 245 | dbname = email | 282 | dbname = email |
| @@ -254,13 +291,26 @@ in { | |||
| 254 | virtual_transport = "dvlmtp:unix:/run/dovecot-lmtp"; | 291 | virtual_transport = "dvlmtp:unix:/run/dovecot-lmtp"; |
| 255 | smtputf8_enable = false; | 292 | smtputf8_enable = false; |
| 256 | 293 | ||
| 257 | authorized_submit_users = "inline:{ root= postfwd= }"; | 294 | authorized_submit_users = "inline:{ root= postfwd= ${config.services.dovecot2.user}= }"; |
| 295 | authorized_flush_users = "inline:{ root= }"; | ||
| 296 | authorized_mailq_users = "inline:{ root= }"; | ||
| 258 | 297 | ||
| 259 | postscreen_access_list = ""; | 298 | postscreen_access_list = ""; |
| 260 | postscreen_denylist_action = "drop"; | 299 | postscreen_denylist_action = "drop"; |
| 261 | postscreen_greet_action = "enforce"; | 300 | postscreen_greet_action = "enforce"; |
| 301 | |||
| 302 | sender_bcc_maps = ''pgsql:${pkgs.writeText "sender_bcc_maps.cf" '' | ||
| 303 | hosts = postgresql:///email | ||
| 304 | dbname = email | ||
| 305 | query = SELECT value FROM sender_bcc_maps WHERE key = '%s' | ||
| 306 | ''}''; | ||
| 307 | recipient_bcc_maps = ''pgsql:${pkgs.writeText "recipient_bcc_maps.cf" '' | ||
| 308 | hosts = postgresql:///email | ||
| 309 | dbname = email | ||
| 310 | query = SELECT value FROM recipient_bcc_maps WHERE key = '%s' | ||
| 311 | ''}''; | ||
| 262 | }; | 312 | }; |
| 263 | masterConfig = { | 313 | settings.master = { |
| 264 | "465" = { | 314 | "465" = { |
| 265 | type = "inet"; | 315 | type = "inet"; |
| 266 | private = false; | 316 | private = false; |
| @@ -283,7 +333,7 @@ in { | |||
| 283 | hosts = postgresql:///email | 333 | hosts = postgresql:///email |
| 284 | dbname = email | 334 | dbname = email |
| 285 | query = SELECT action FROM virtual_mailbox_access WHERE lookup = '%s' OR (lookup = regexp_replace('%s', '\+[^@]*@', '@') AND NOT EXISTS (SELECT 1 FROM virtual_mailbox_access WHERE lookup = '%s')) | 335 | query = SELECT action FROM virtual_mailbox_access WHERE lookup = '%s' OR (lookup = regexp_replace('%s', '\+[^@]*@', '@') AND NOT EXISTS (SELECT 1 FROM virtual_mailbox_access WHERE lookup = '%s')) |
| 286 | ''},permit_tls_all_clientcerts,reject}'' | 336 | ''},check_policy_service unix:/run/postfix-internal-policy.sock,permit_tls_all_clientcerts,reject}'' |
| 287 | "-o" "smtpd_relay_restrictions=permit_tls_all_clientcerts,reject" | 337 | "-o" "smtpd_relay_restrictions=permit_tls_all_clientcerts,reject" |
| 288 | "-o" "{smtpd_data_restrictions = check_policy_service unix:/run/postfwd3/postfwd3.sock}" | 338 | "-o" "{smtpd_data_restrictions = check_policy_service unix:/run/postfwd3/postfwd3.sock}" |
| 289 | "-o" "unverified_sender_reject_code=550" | 339 | "-o" "unverified_sender_reject_code=550" |
| @@ -313,7 +363,7 @@ in { | |||
| 313 | hosts = postgresql:///email | 363 | hosts = postgresql:///email |
| 314 | dbname = email | 364 | dbname = email |
| 315 | query = SELECT action FROM virtual_mailbox_access WHERE lookup = '%s' OR (lookup = regexp_replace('%s', '\+[^@]*@', '@') AND NOT EXISTS (SELECT 1 FROM virtual_mailbox_access WHERE lookup = '%s')) | 365 | query = SELECT action FROM virtual_mailbox_access WHERE lookup = '%s' OR (lookup = regexp_replace('%s', '\+[^@]*@', '@') AND NOT EXISTS (SELECT 1 FROM virtual_mailbox_access WHERE lookup = '%s')) |
| 316 | ''},permit_sasl_authenticated,reject}'' | 366 | ''},check_policy_service unix:/run/postfix-internal-policy.sock,permit_sasl_authenticated,reject}'' |
| 317 | "-o" "smtpd_relay_restrictions=permit_sasl_authenticated,reject" | 367 | "-o" "smtpd_relay_restrictions=permit_sasl_authenticated,reject" |
| 318 | "-o" "{smtpd_data_restrictions = check_policy_service unix:/run/postfwd3/postfwd3.sock}" | 368 | "-o" "{smtpd_data_restrictions = check_policy_service unix:/run/postfwd3/postfwd3.sock}" |
| 319 | "-o" "unverified_sender_reject_code=550" | 369 | "-o" "unverified_sender_reject_code=550" |
| @@ -328,7 +378,10 @@ in { | |||
| 328 | maxproc = 0; | 378 | maxproc = 0; |
| 329 | args = [ | 379 | args = [ |
| 330 | "-o" "header_checks=pcre:${pkgs.writeText "header_checks_submission" '' | 380 | "-o" "header_checks=pcre:${pkgs.writeText "header_checks_submission" '' |
| 381 | if /^Received: / | ||
| 382 | !/by surtr\.yggdrasil\.li/ STRIP | ||
| 331 | /^Received: from [^ ]+ \([^ ]+ [^ ]+\)\s+(.*)$/ REPLACE Received: $1 | 383 | /^Received: from [^ ]+ \([^ ]+ [^ ]+\)\s+(.*)$/ REPLACE Received: $1 |
| 384 | endif | ||
| 332 | ''}" | 385 | ''}" |
| 333 | ]; | 386 | ]; |
| 334 | }; | 387 | }; |
| @@ -364,17 +417,19 @@ in { | |||
| 364 | 417 | ||
| 365 | services.postsrsd = { | 418 | services.postsrsd = { |
| 366 | enable = true; | 419 | enable = true; |
| 367 | domain = "surtr.yggdrasil.li"; | 420 | domains = [ "surtr.yggdrasil.li" ] ++ concatMap (domain: [".${domain}" domain]) emailDomains; |
| 368 | separator = "+"; | 421 | separator = "+"; |
| 369 | excludeDomains = [ "surtr.yggdrasil.li" | 422 | extraConfig = '' |
| 370 | ] ++ concatMap (domain: [".${domain}" domain]) emailDomains; | 423 | socketmap = unix:/run/postsrsd/postsrsd-socketmap.sock |
| 424 | milter = unix:/run/postsrsd/postsrsd-milter.sock | ||
| 425 | ''; | ||
| 371 | }; | 426 | }; |
| 372 | 427 | ||
| 373 | services.opendkim = { | 428 | services.opendkim = { |
| 374 | enable = true; | 429 | enable = true; |
| 375 | user = "postfix"; group = "postfix"; | 430 | user = "postfix"; group = "postfix"; |
| 376 | socket = "local:/run/opendkim/opendkim.sock"; | 431 | socket = "local:/run/opendkim/opendkim.sock"; |
| 377 | domains = ''csl:${concatStringsSep "," (["surtr.yggdrasil.li"] ++ emailDomains)}''; | 432 | domains = ''csl:${concatStringsSep "," (["surtr.yggdrasil.li" "yggdrasil.li" "141.li" "kleen.li" "synapse.li" "praseodym.org"] ++ emailDomains)}''; |
| 378 | selector = "surtr"; | 433 | selector = "surtr"; |
| 379 | configFile = builtins.toFile "opendkim.conf" '' | 434 | configFile = builtins.toFile "opendkim.conf" '' |
| 380 | Syslog true | 435 | Syslog true |
| @@ -478,32 +533,32 @@ in { | |||
| 478 | }; | 533 | }; |
| 479 | }; | 534 | }; |
| 480 | 535 | ||
| 481 | users.groups.${config.services.rspamd.group}.members = [ config.services.postfix.user "dovecot2" ]; | 536 | users.groups.${config.services.rspamd.group}.members = [ config.services.postfix.user config.services.dovecot2.user ]; |
| 482 | 537 | ||
| 483 | services.redis.servers.rspamd.enable = true; | 538 | services.redis.servers.rspamd.enable = true; |
| 484 | 539 | ||
| 485 | users.groups.${config.services.redis.servers.rspamd.user}.members = [ config.services.rspamd.user ]; | 540 | users.groups.${config.services.redis.servers.rspamd.user}.members = [ config.services.rspamd.user ]; |
| 486 | 541 | ||
| 542 | environment.systemPackages = with pkgs; [ dovecot_pigeonhole dovecot_fts_xapian ]; | ||
| 487 | services.dovecot2 = { | 543 | services.dovecot2 = { |
| 488 | enable = true; | 544 | enable = true; |
| 489 | enablePAM = false; | 545 | enablePAM = false; |
| 490 | sslServerCert = "/run/credentials/dovecot2.service/surtr.yggdrasil.li.pem"; | 546 | sslServerCert = "/run/credentials/dovecot.service/surtr.yggdrasil.li.pem"; |
| 491 | sslServerKey = "/run/credentials/dovecot2.service/surtr.yggdrasil.li.key.pem"; | 547 | sslServerKey = "/run/credentials/dovecot.service/surtr.yggdrasil.li.key.pem"; |
| 492 | sslCACert = toString ./ca/ca.crt; | 548 | sslCACert = toString ./ca/ca.crt; |
| 493 | mailLocation = "maildir:/var/lib/mail/%u/maildir:UTF-8:INDEX=/var/lib/dovecot/indices/%u"; | 549 | mailLocation = "maildir:/var/lib/mail/%u/maildir:UTF-8:INDEX=/var/lib/dovecot/indices/%u"; |
| 494 | modules = with pkgs; [ dovecot_pigeonhole dovecot_fts_xapian ]; | ||
| 495 | mailPlugins.globally.enable = [ "fts" "fts_xapian" ]; | 550 | mailPlugins.globally.enable = [ "fts" "fts_xapian" ]; |
| 496 | protocols = [ "lmtp" "sieve" ]; | 551 | protocols = [ "lmtp" "sieve" ]; |
| 497 | sieve = { | 552 | sieve = { |
| 498 | extensions = ["copy" "imapsieve" "variables" "imap4flags" "vacation"]; | 553 | extensions = ["copy" "imapsieve" "variables" "imap4flags" "vacation" "vacation-seconds" "vnd.dovecot.debug"]; |
| 499 | globalExtensions = ["copy" "imapsieve" "variables" "imap4flags" "vacation"]; | 554 | globalExtensions = ["copy" "imapsieve" "variables" "imap4flags" "vacation" "vacation-seconds" "vnd.dovecot.debug"]; |
| 500 | }; | 555 | }; |
| 501 | extraConfig = let | 556 | extraConfig = let |
| 502 | dovecotSqlConf = pkgs.writeText "dovecot-sql.conf" '' | 557 | dovecotSqlConf = pkgs.writeText "dovecot-sql.conf" '' |
| 503 | driver = pgsql | 558 | driver = pgsql |
| 504 | connect = dbname=email | 559 | connect = dbname=email |
| 505 | password_query = SELECT (CASE WHEN '%k' = 'valid' AND '%m' = 'EXTERNAL' THEN NULL ELSE "password" END) as password, (CASE WHEN '%k' = 'valid' AND '%m' = 'EXTERNAL' THEN true WHEN password IS NULL THEN true ELSE NULL END) as nopassword, "user", quota_rule, 'dovecot2' as uid, 'dovecot2' as gid FROM imap_user WHERE "user" = '%n' | 560 | password_query = SELECT (CASE WHEN '%k' = 'valid' AND '%m' = 'EXTERNAL' THEN NULL ELSE "password" END) as password, (CASE WHEN '%k' = 'valid' AND '%m' = 'EXTERNAL' THEN true WHEN password IS NULL THEN true ELSE NULL END) as nopassword, "user", quota_rule, '${config.services.dovecot2.user}' as uid, '${config.services.dovecot2.group}' as gid FROM imap_user WHERE "user" = '%n' |
| 506 | user_query = SELECT "user", quota_rule, 'dovecot2' as uid, 'dovecot2' as gid FROM imap_user WHERE "user" = '%n' | 561 | user_query = SELECT "user", quota_rule, '${config.services.dovecot2.user}' as uid, 'dovecot2' as gid FROM imap_user WHERE "user" = '%n' |
| 507 | iterate_query = SELECT "user" FROM imap_user | 562 | iterate_query = SELECT "user" FROM imap_user |
| 508 | ''; | 563 | ''; |
| 509 | in '' | 564 | in '' |
| @@ -511,16 +566,16 @@ in { | |||
| 511 | 566 | ||
| 512 | mail_plugins = $mail_plugins quota | 567 | mail_plugins = $mail_plugins quota |
| 513 | 568 | ||
| 514 | first_valid_uid = ${toString config.users.users.dovecot2.uid} | 569 | first_valid_uid = ${toString config.users.users.${config.services.dovecot2.user}.uid} |
| 515 | last_valid_uid = ${toString config.users.users.dovecot2.uid} | 570 | last_valid_uid = ${toString config.users.users.${config.services.dovecot2.user}.uid} |
| 516 | first_valid_gid = ${toString config.users.groups.dovecot2.gid} | 571 | first_valid_gid = ${toString config.users.groups.${config.services.dovecot2.group}.gid} |
| 517 | last_valid_gid = ${toString config.users.groups.dovecot2.gid} | 572 | last_valid_gid = ${toString config.users.groups.${config.services.dovecot2.group}.gid} |
| 518 | 573 | ||
| 519 | ${concatMapStringsSep "\n\n" (domain: | 574 | ${concatMapStringsSep "\n\n" (domain: |
| 520 | concatMapStringsSep "\n" (subdomain: '' | 575 | concatMapStringsSep "\n" (subdomain: '' |
| 521 | local_name ${subdomain} { | 576 | local_name ${subdomain} { |
| 522 | ssl_cert = </run/credentials/dovecot2.service/${subdomain}.pem | 577 | ssl_cert = </run/credentials/dovecot.service/${subdomain}.pem |
| 523 | ssl_key = </run/credentials/dovecot2.service/${subdomain}.key.pem | 578 | ssl_key = </run/credentials/dovecot.service/${subdomain}.key.pem |
| 524 | } | 579 | } |
| 525 | '') ["imap.${domain}" domain] | 580 | '') ["imap.${domain}" domain] |
| 526 | ) emailDomains} | 581 | ) emailDomains} |
| @@ -541,10 +596,10 @@ in { | |||
| 541 | auth_debug = yes | 596 | auth_debug = yes |
| 542 | 597 | ||
| 543 | service auth { | 598 | service auth { |
| 544 | user = dovecot2 | 599 | user = ${config.services.dovecot2.user} |
| 545 | } | 600 | } |
| 546 | service auth-worker { | 601 | service auth-worker { |
| 547 | user = dovecot2 | 602 | user = ${config.services.dovecot2.user} |
| 548 | } | 603 | } |
| 549 | 604 | ||
| 550 | userdb { | 605 | userdb { |
| @@ -565,7 +620,7 @@ in { | |||
| 565 | args = ${pkgs.writeText "dovecot-sql.conf" '' | 620 | args = ${pkgs.writeText "dovecot-sql.conf" '' |
| 566 | driver = pgsql | 621 | driver = pgsql |
| 567 | connect = dbname=email | 622 | connect = dbname=email |
| 568 | user_query = SELECT DISTINCT ON (extension IS NULL, local IS NULL) "user", quota_rule, 'dovecot2' as uid, 'dovecot2' as gid FROM lmtp_mapping WHERE CASE WHEN extension IS NOT NULL AND local IS NOT NULL THEN ('%n' :: citext) = local || '+' || extension AND domain = ('%d' :: citext) WHEN local IS NOT NULL THEN (local = ('%n' :: citext) OR ('%n' :: citext) ILIKE local || '+%%') AND domain = ('%d' :: citext) WHEN extension IS NOT NULL THEN ('%n' :: citext) ILIKE '%%+' || extension AND domain = ('%d' :: citext) ELSE domain = ('%d' :: citext) END ORDER BY (extension IS NULL) ASC, (local IS NULL) ASC | 623 | user_query = SELECT DISTINCT ON (extension IS NULL, local IS NULL) "user", quota_rule, '${config.services.dovecot2.user}' as uid, '${config.services.dovecot2.group}' as gid FROM lmtp_mapping WHERE CASE WHEN extension IS NOT NULL AND local IS NOT NULL THEN ('%n' :: citext) = local || '+' || extension AND domain = ('%d' :: citext) WHEN local IS NOT NULL THEN (local = ('%n' :: citext) OR ('%n' :: citext) ILIKE local || '+%%') AND domain = ('%d' :: citext) WHEN extension IS NOT NULL THEN ('%n' :: citext) ILIKE '%%+' || extension AND domain = ('%d' :: citext) ELSE domain = ('%d' :: citext) END ORDER BY (extension IS NULL) ASC, (local IS NULL) ASC |
| 569 | ''} | 624 | ''} |
| 570 | 625 | ||
| 571 | skip = never | 626 | skip = never |
| @@ -635,7 +690,7 @@ in { | |||
| 635 | quota_status_success = DUNNO | 690 | quota_status_success = DUNNO |
| 636 | quota_status_nouser = DUNNO | 691 | quota_status_nouser = DUNNO |
| 637 | quota_grace = 10%% | 692 | quota_grace = 10%% |
| 638 | quota_max_mail_size = ${config.services.postfix.config.message_size_limit} | 693 | quota_max_mail_size = ${toString config.services.postfix.settings.main.message_size_limit} |
| 639 | quota_vsizes = yes | 694 | quota_vsizes = yes |
| 640 | } | 695 | } |
| 641 | 696 | ||
| @@ -673,7 +728,7 @@ in { | |||
| 673 | plugin { | 728 | plugin { |
| 674 | plugin = fts fts_xapian | 729 | plugin = fts fts_xapian |
| 675 | fts = xapian | 730 | fts = xapian |
| 676 | fts_xapian = partial=2 full=20 attachments=1 verbose=1 | 731 | fts_xapian = partial=3 full=20 attachments=1 verbose=1 |
| 677 | 732 | ||
| 678 | fts_autoindex = yes | 733 | fts_autoindex = yes |
| 679 | 734 | ||
| @@ -688,12 +743,12 @@ in { | |||
| 688 | 743 | ||
| 689 | systemd.services.dovecot-fts-xapian-optimize = { | 744 | systemd.services.dovecot-fts-xapian-optimize = { |
| 690 | description = "Optimize dovecot indices for fts_xapian"; | 745 | description = "Optimize dovecot indices for fts_xapian"; |
| 691 | requisite = [ "dovecot2.service" ]; | 746 | requisite = [ "dovecot.service" ]; |
| 692 | after = [ "dovecot2.service" ]; | 747 | after = [ "dovecot.service" ]; |
| 693 | startAt = "*-*-* 22:00:00 Europe/Berlin"; | 748 | startAt = "*-*-* 22:00:00 Europe/Berlin"; |
| 694 | serviceConfig = { | 749 | serviceConfig = { |
| 695 | Type = "oneshot"; | 750 | Type = "oneshot"; |
| 696 | ExecStart = "${pkgs.dovecot}/bin/doveadm fts optimize -A"; | 751 | ExecStart = "${getExe' pkgs.dovecot "doveadm"} fts optimize -A"; |
| 697 | PrivateDevices = true; | 752 | PrivateDevices = true; |
| 698 | PrivateNetwork = true; | 753 | PrivateNetwork = true; |
| 699 | ProtectKernelTunables = true; | 754 | ProtectKernelTunables = true; |
| @@ -754,31 +809,29 @@ in { | |||
| 754 | 809 | ||
| 755 | security.acme.rfc2136Domains = { | 810 | security.acme.rfc2136Domains = { |
| 756 | "surtr.yggdrasil.li" = { | 811 | "surtr.yggdrasil.li" = { |
| 757 | restartUnits = [ "postfix.service" "dovecot2.service" ]; | 812 | restartUnits = [ "postfix.service" "dovecot.service" ]; |
| 758 | }; | 813 | }; |
| 759 | } // listToAttrs (map (domain: nameValuePair "spm.${domain}" { restartUnits = ["nginx.service"]; }) spmDomains) | 814 | } // listToAttrs (map (domain: nameValuePair "spm.${domain}" { restartUnits = ["nginx.service"]; }) spmDomains) |
| 760 | // listToAttrs (concatMap (domain: [ | 815 | // listToAttrs (concatMap (domain: [ |
| 761 | (nameValuePair domain { restartUnits = ["postfix.service" "dovecot2.service"]; }) | 816 | (nameValuePair domain { restartUnits = ["postfix.service" "dovecot.service"]; }) |
| 762 | (nameValuePair "mailin.${domain}" { restartUnits = ["postfix.service"]; }) | 817 | (nameValuePair "mailin.${domain}" { restartUnits = ["postfix.service"]; }) |
| 763 | (nameValuePair "mailsub.${domain}" { restartUnits = ["postfix.service"]; }) | 818 | (nameValuePair "mailsub.${domain}" { restartUnits = ["postfix.service"]; }) |
| 764 | (nameValuePair "imap.${domain}" { restartUnits = ["dovecot2.service"]; }) | 819 | (nameValuePair "imap.${domain}" { restartUnits = ["dovecot.service"]; }) |
| 765 | (nameValuePair "mta-sts.${domain}" { restartUnits = ["nginx.service"]; }) | 820 | (nameValuePair "mta-sts.${domain}" { restartUnits = ["nginx.service"]; }) |
| 766 | ]) emailDomains); | 821 | ]) emailDomains); |
| 767 | 822 | ||
| 768 | systemd.services.postfix = { | 823 | systemd.services.postfix = { |
| 769 | serviceConfig.LoadCredential = [ | 824 | serviceConfig.LoadCredential = let |
| 770 | "surtr.yggdrasil.li.key.pem:${config.security.acme.certs."surtr.yggdrasil.li".directory}/key.pem" | 825 | tlsCredential = domain: "${domain}.full.pem:${config.security.acme.certs.${domain}.directory}/full.pem"; |
| 771 | "surtr.yggdrasil.li.pem:${config.security.acme.certs."surtr.yggdrasil.li".directory}/fullchain.pem" | 826 | in [ |
| 772 | ] ++ concatMap (domain: | 827 | (tlsCredential "surtr.yggdrasil.li") |
| 773 | map (subdomain: "${subdomain}.full.pem:${config.security.acme.certs.${subdomain}.directory}/full.pem") | 828 | ] ++ concatMap (domain: map tlsCredential [domain "mailin.${domain}" "mailsub.${domain}"]) emailDomains; |
| 774 | [domain "mailin.${domain}" "mailsub.${domain}"] | ||
| 775 | ) emailDomains; | ||
| 776 | }; | 829 | }; |
| 777 | 830 | ||
| 778 | systemd.services.dovecot2 = { | 831 | systemd.services.dovecot = { |
| 779 | preStart = '' | 832 | preStart = '' |
| 780 | for f in /etc/dovecot/sieve_flag.d/*.sieve /etc/dovecot/sieve_before.d/*.sieve; do | 833 | for f in /etc/dovecot/sieve_flag.d/*.sieve /etc/dovecot/sieve_before.d/*.sieve; do |
| 781 | ${pkgs.dovecot_pigeonhole}/bin/sievec $f | 834 | ${getExe' pkgs.dovecot_pigeonhole "sievec"} $f |
| 782 | done | 835 | done |
| 783 | ''; | 836 | ''; |
| 784 | 837 | ||
| @@ -845,15 +898,16 @@ in { | |||
| 845 | charset utf-8; | 898 | charset utf-8; |
| 846 | source_charset utf-8; | 899 | source_charset utf-8; |
| 847 | ''; | 900 | ''; |
| 848 | root = pkgs.runCommand "mta-sts.${domain}" {} '' | 901 | root = pkgs.writeTextFile { |
| 849 | mkdir -p $out/.well-known | 902 | name = "mta-sts.${domain}"; |
| 850 | cp ${pkgs.writeText "mta-sts.${domain}.txt" '' | 903 | destination = "/.well-known/mta-sts.txt"; |
| 904 | text = '' | ||
| 851 | version: STSv1 | 905 | version: STSv1 |
| 852 | mode: enforce | 906 | mode: enforce |
| 853 | max_age: 2419200 | 907 | max_age: 2419200 |
| 854 | mx: mailin.${domain} | 908 | mx: mailin.${domain} |
| 855 | ''} $out/.well-known/mta-sts.txt | 909 | ''; |
| 856 | ''; | 910 | }; |
| 857 | }; | 911 | }; |
| 858 | }) emailDomains); | 912 | }) emailDomains); |
| 859 | }; | 913 | }; |
| @@ -870,7 +924,7 @@ in { | |||
| 870 | systemd.services.spm = { | 924 | systemd.services.spm = { |
| 871 | serviceConfig = { | 925 | serviceConfig = { |
| 872 | Type = "notify"; | 926 | Type = "notify"; |
| 873 | ExecStart = "${pkgs.spm}/bin/spm-server"; | 927 | ExecStart = getExe' pkgs.spm "spm-server"; |
| 874 | User = "spm"; | 928 | User = "spm"; |
| 875 | Group = "spm"; | 929 | Group = "spm"; |
| 876 | 930 | ||
| @@ -928,7 +982,7 @@ in { | |||
| 928 | serviceConfig = { | 982 | serviceConfig = { |
| 929 | Type = "notify"; | 983 | Type = "notify"; |
| 930 | 984 | ||
| 931 | ExecStart = "${ccert-policy-server}/bin/ccert-policy-server"; | 985 | ExecStart = getExe' ccert-policy-server "ccert-policy-server"; |
| 932 | 986 | ||
| 933 | Environment = [ | 987 | Environment = [ |
| 934 | "PGDATABASE=email" | 988 | "PGDATABASE=email" |
| @@ -961,6 +1015,53 @@ in { | |||
| 961 | }; | 1015 | }; |
| 962 | users.groups."postfix-ccert-sender-policy" = {}; | 1016 | users.groups."postfix-ccert-sender-policy" = {}; |
| 963 | 1017 | ||
| 1018 | systemd.sockets."postfix-internal-policy" = { | ||
| 1019 | requiredBy = ["postfix.service"]; | ||
| 1020 | wants = ["postfix-internal-policy.service"]; | ||
| 1021 | socketConfig = { | ||
| 1022 | ListenStream = "/run/postfix-internal-policy.sock"; | ||
| 1023 | }; | ||
| 1024 | }; | ||
| 1025 | systemd.services."postfix-internal-policy" = { | ||
| 1026 | after = [ "postgresql.service" ]; | ||
| 1027 | bindsTo = [ "postgresql.service" ]; | ||
| 1028 | |||
| 1029 | serviceConfig = { | ||
| 1030 | Type = "notify"; | ||
| 1031 | |||
| 1032 | ExecStart = lib.getExe internal-policy-server; | ||
| 1033 | |||
| 1034 | Environment = [ | ||
| 1035 | "PGDATABASE=email" | ||
| 1036 | ]; | ||
| 1037 | |||
| 1038 | DynamicUser = false; | ||
| 1039 | User = "postfix-internal-policy"; | ||
| 1040 | Group = "postfix-internal-policy"; | ||
| 1041 | ProtectSystem = "strict"; | ||
| 1042 | SystemCallFilter = "@system-service"; | ||
| 1043 | NoNewPrivileges = true; | ||
| 1044 | ProtectKernelTunables = true; | ||
| 1045 | ProtectKernelModules = true; | ||
| 1046 | ProtectKernelLogs = true; | ||
| 1047 | ProtectControlGroups = true; | ||
| 1048 | MemoryDenyWriteExecute = true; | ||
| 1049 | RestrictSUIDSGID = true; | ||
| 1050 | KeyringMode = "private"; | ||
| 1051 | ProtectClock = true; | ||
| 1052 | RestrictRealtime = true; | ||
| 1053 | PrivateDevices = true; | ||
| 1054 | PrivateTmp = true; | ||
| 1055 | ProtectHostname = true; | ||
| 1056 | ReadWritePaths = ["/run/postgresql"]; | ||
| 1057 | }; | ||
| 1058 | }; | ||
| 1059 | users.users."postfix-internal-policy" = { | ||
| 1060 | isSystemUser = true; | ||
| 1061 | group = "postfix-internal-policy"; | ||
| 1062 | }; | ||
| 1063 | users.groups."postfix-internal-policy" = {}; | ||
| 1064 | |||
| 964 | services.postfwd = { | 1065 | services.postfwd = { |
| 965 | enable = true; | 1066 | enable = true; |
| 966 | cache = false; | 1067 | cache = false; |
diff --git a/hosts/surtr/email/internal-policy-server/.envrc b/hosts/surtr/email/internal-policy-server/.envrc new file mode 100644 index 00000000..2c909235 --- /dev/null +++ b/hosts/surtr/email/internal-policy-server/.envrc | |||
| @@ -0,0 +1,4 @@ | |||
| 1 | use flake | ||
| 2 | |||
| 3 | [[ -d ".venv" ]] || ( uv venv && uv sync ) | ||
| 4 | . .venv/bin/activate | ||
diff --git a/hosts/surtr/email/internal-policy-server/.gitignore b/hosts/surtr/email/internal-policy-server/.gitignore new file mode 100644 index 00000000..4ccfae70 --- /dev/null +++ b/hosts/surtr/email/internal-policy-server/.gitignore | |||
| @@ -0,0 +1,2 @@ | |||
| 1 | .venv | ||
| 2 | **/__pycache__ | ||
diff --git a/hosts/surtr/email/internal-policy-server/internal_policy_server/__init__.py b/hosts/surtr/email/internal-policy-server/internal_policy_server/__init__.py new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/hosts/surtr/email/internal-policy-server/internal_policy_server/__init__.py | |||
diff --git a/hosts/surtr/email/internal-policy-server/internal_policy_server/__main__.py b/hosts/surtr/email/internal-policy-server/internal_policy_server/__main__.py new file mode 100644 index 00000000..04f1a59a --- /dev/null +++ b/hosts/surtr/email/internal-policy-server/internal_policy_server/__main__.py | |||
| @@ -0,0 +1,106 @@ | |||
| 1 | from systemd.daemon import listen_fds | ||
| 2 | from sdnotify import SystemdNotifier | ||
| 3 | from socketserver import StreamRequestHandler, ThreadingMixIn | ||
| 4 | from systemd_socketserver import SystemdSocketServer | ||
| 5 | import sys | ||
| 6 | from threading import Thread | ||
| 7 | from psycopg_pool import ConnectionPool | ||
| 8 | from psycopg.rows import namedtuple_row | ||
| 9 | |||
| 10 | import logging | ||
| 11 | |||
| 12 | |||
| 13 | class PolicyHandler(StreamRequestHandler): | ||
| 14 | def handle(self): | ||
| 15 | logger.debug('Handling new connection...') | ||
| 16 | |||
| 17 | self.args = dict() | ||
| 18 | |||
| 19 | line = None | ||
| 20 | while line := self.rfile.readline().removesuffix(b'\n'): | ||
| 21 | if b'=' not in line: | ||
| 22 | break | ||
| 23 | |||
| 24 | key, val = line.split(sep=b'=', maxsplit=1) | ||
| 25 | self.args[key.decode()] = val.decode() | ||
| 26 | |||
| 27 | logger.info('Connection parameters: %s', self.args) | ||
| 28 | |||
| 29 | allowed = False | ||
| 30 | user = None | ||
| 31 | if self.args['sasl_username']: | ||
| 32 | user = self.args['sasl_username'] | ||
| 33 | if self.args['ccert_subject']: | ||
| 34 | user = self.args['ccert_subject'] | ||
| 35 | |||
| 36 | with self.server.db_pool.connection() as conn: | ||
| 37 | local, domain = self.args['recipient'].split(sep='@', maxsplit=1) | ||
| 38 | extension = None | ||
| 39 | if '+' in local: | ||
| 40 | local, extension = local.split(sep='+', maxsplit=1) | ||
| 41 | |||
| 42 | logger.debug('Parsed recipient address: %s', {'local': local, 'extension': extension, 'domain': domain}) | ||
| 43 | |||
| 44 | with conn.cursor() as cur: | ||
| 45 | cur.row_factory = namedtuple_row | ||
| 46 | cur.execute('SELECT id, internal FROM "mailbox_mapping" WHERE ("local" = %(local)s OR "local" IS NULL) AND ("extension" = %(extension)s OR "extension" IS NULL) AND "domain" = %(domain)s', params = {'local': local, 'extension': extension if extension is not None else '', 'domain': domain}, prepare = True) | ||
| 47 | if (row := cur.fetchone()) is not None: | ||
| 48 | if not row.internal: | ||
| 49 | logger.debug('Recipient mailbox is not internal') | ||
| 50 | allowed = True | ||
| 51 | elif user: | ||
| 52 | cur.execute('SELECT EXISTS(SELECT true FROM "mailbox_mapping_access" INNER JOIN "mailbox" ON "mailbox".id = "mailbox_mapping_access"."mailbox" WHERE mailbox_mapping = %(mailbox_mapping)s AND "mailbox"."mailbox" = %(user)s) as "exists"', params = { 'mailbox_mapping': row.id, 'user': user }, prepare = True) | ||
| 53 | if (row := cur.fetchone()) is not None: | ||
| 54 | allowed = row.exists | ||
| 55 | else: | ||
| 56 | logger.debug('Recipient is not local') | ||
| 57 | allowed = True | ||
| 58 | |||
| 59 | action = '550 5.7.0 Recipient mailbox mapping not authorized for current user' | ||
| 60 | if allowed: | ||
| 61 | action = 'DUNNO' | ||
| 62 | |||
| 63 | logger.info('Reached verdict: %s', {'allowed': allowed, 'action': action}) | ||
| 64 | self.wfile.write(f'action={action}\n\n'.encode()) | ||
| 65 | |||
| 66 | class ThreadedSystemdSocketServer(ThreadingMixIn, SystemdSocketServer): | ||
| 67 | def __init__(self, fd, RequestHandlerClass): | ||
| 68 | super().__init__(fd, RequestHandlerClass) | ||
| 69 | |||
| 70 | self.db_pool = ConnectionPool(min_size=1) | ||
| 71 | self.db_pool.wait() | ||
| 72 | |||
| 73 | def main(): | ||
| 74 | global logger | ||
| 75 | logger = logging.getLogger(__name__) | ||
| 76 | console_handler = logging.StreamHandler() | ||
| 77 | console_handler.setFormatter( logging.Formatter('[%(levelname)s](%(name)s): %(message)s') ) | ||
| 78 | if sys.stderr.isatty(): | ||
| 79 | console_handler.setFormatter( logging.Formatter('%(asctime)s [%(levelname)s](%(name)s): %(message)s') ) | ||
| 80 | logger.addHandler(console_handler) | ||
| 81 | logger.setLevel(logging.DEBUG) | ||
| 82 | |||
| 83 | # log uncaught exceptions | ||
| 84 | def log_exceptions(type, value, tb): | ||
| 85 | global logger | ||
| 86 | |||
| 87 | logger.error(value) | ||
| 88 | sys.__excepthook__(type, value, tb) # calls default excepthook | ||
| 89 | |||
| 90 | sys.excepthook = log_exceptions | ||
| 91 | |||
| 92 | fds = listen_fds() | ||
| 93 | servers = [ThreadedSystemdSocketServer(fd, PolicyHandler) for fd in fds] | ||
| 94 | |||
| 95 | if servers: | ||
| 96 | for server in servers: | ||
| 97 | Thread(name=f'Server for fd{server.fileno()}', target=server.serve_forever).start() | ||
| 98 | else: | ||
| 99 | return 2 | ||
| 100 | |||
| 101 | SystemdNotifier().notify('READY=1') | ||
| 102 | |||
| 103 | return 0 | ||
| 104 | |||
| 105 | if __name__ == '__main__': | ||
| 106 | sys.exit(main()) | ||
diff --git a/hosts/surtr/email/internal-policy-server/pyproject.toml b/hosts/surtr/email/internal-policy-server/pyproject.toml new file mode 100644 index 00000000..c697cd01 --- /dev/null +++ b/hosts/surtr/email/internal-policy-server/pyproject.toml | |||
| @@ -0,0 +1,18 @@ | |||
| 1 | [project] | ||
| 2 | name = "internal-policy-server" | ||
| 3 | version = "0.1.0" | ||
| 4 | requires-python = ">=3.12" | ||
| 5 | dependencies = [ | ||
| 6 | "psycopg>=3.2.9", | ||
| 7 | "psycopg-binary>=3.2.9", | ||
| 8 | "psycopg-pool>=3.2.6", | ||
| 9 | "sdnotify>=0.3.2", | ||
| 10 | "systemd-socketserver>=1.0", | ||
| 11 | ] | ||
| 12 | |||
| 13 | [project.scripts] | ||
| 14 | internal-policy-server = "internal_policy_server.__main__:main" | ||
| 15 | |||
| 16 | [build-system] | ||
| 17 | requires = ["hatchling"] | ||
| 18 | build-backend = "hatchling.build" | ||
diff --git a/hosts/surtr/email/internal-policy-server/uv.lock b/hosts/surtr/email/internal-policy-server/uv.lock new file mode 100644 index 00000000..f7a4e729 --- /dev/null +++ b/hosts/surtr/email/internal-policy-server/uv.lock | |||
| @@ -0,0 +1,119 @@ | |||
| 1 | version = 1 | ||
| 2 | revision = 2 | ||
| 3 | requires-python = ">=3.12" | ||
| 4 | |||
| 5 | [[package]] | ||
| 6 | name = "internal-policy-server" | ||
| 7 | version = "0.1.0" | ||
| 8 | source = { editable = "." } | ||
| 9 | dependencies = [ | ||
| 10 | { name = "psycopg" }, | ||
| 11 | { name = "psycopg-binary" }, | ||
| 12 | { name = "psycopg-pool" }, | ||
| 13 | { name = "sdnotify" }, | ||
| 14 | { name = "systemd-socketserver" }, | ||
| 15 | ] | ||
| 16 | |||
| 17 | [package.metadata] | ||
| 18 | requires-dist = [ | ||
| 19 | { name = "psycopg", specifier = ">=3.2.9" }, | ||
| 20 | { name = "psycopg-binary", specifier = ">=3.2.9" }, | ||
| 21 | { name = "psycopg-pool", specifier = ">=3.2.6" }, | ||
| 22 | { name = "sdnotify", specifier = ">=0.3.2" }, | ||
| 23 | { name = "systemd-socketserver", specifier = ">=1.0" }, | ||
| 24 | ] | ||
| 25 | |||
| 26 | [[package]] | ||
| 27 | name = "psycopg" | ||
| 28 | version = "3.2.9" | ||
| 29 | source = { registry = "https://pypi.org/simple" } | ||
| 30 | dependencies = [ | ||
| 31 | { name = "typing-extensions", marker = "python_full_version < '3.13'" }, | ||
| 32 | { name = "tzdata", marker = "sys_platform == 'win32'" }, | ||
| 33 | ] | ||
| 34 | sdist = { url = "https://files.pythonhosted.org/packages/27/4a/93a6ab570a8d1a4ad171a1f4256e205ce48d828781312c0bbaff36380ecb/psycopg-3.2.9.tar.gz", hash = "sha256:2fbb46fcd17bc81f993f28c47f1ebea38d66ae97cc2dbc3cad73b37cefbff700", size = 158122, upload-time = "2025-05-13T16:11:15.533Z" } | ||
| 35 | wheels = [ | ||
| 36 | { url = "https://files.pythonhosted.org/packages/44/b0/a73c195a56eb6b92e937a5ca58521a5c3346fb233345adc80fd3e2f542e2/psycopg-3.2.9-py3-none-any.whl", hash = "sha256:01a8dadccdaac2123c916208c96e06631641c0566b22005493f09663c7a8d3b6", size = 202705, upload-time = "2025-05-13T16:06:26.584Z" }, | ||
| 37 | ] | ||
| 38 | |||
| 39 | [[package]] | ||
| 40 | name = "psycopg-binary" | ||
| 41 | version = "3.2.9" | ||
| 42 | source = { registry = "https://pypi.org/simple" } | ||
| 43 | wheels = [ | ||
| 44 | { url = "https://files.pythonhosted.org/packages/29/6f/ec9957e37a606cd7564412e03f41f1b3c3637a5be018d0849914cb06e674/psycopg_binary-3.2.9-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:be7d650a434921a6b1ebe3fff324dbc2364393eb29d7672e638ce3e21076974e", size = 4022205, upload-time = "2025-05-13T16:07:48.195Z" }, | ||
| 45 | { url = "https://files.pythonhosted.org/packages/6b/ba/497b8bea72b20a862ac95a94386967b745a472d9ddc88bc3f32d5d5f0d43/psycopg_binary-3.2.9-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:6a76b4722a529390683c0304501f238b365a46b1e5fb6b7249dbc0ad6fea51a0", size = 4083795, upload-time = "2025-05-13T16:07:50.917Z" }, | ||
| 46 | { url = "https://files.pythonhosted.org/packages/42/07/af9503e8e8bdad3911fd88e10e6a29240f9feaa99f57d6fac4a18b16f5a0/psycopg_binary-3.2.9-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:96a551e4683f1c307cfc3d9a05fec62c00a7264f320c9962a67a543e3ce0d8ff", size = 4655043, upload-time = "2025-05-13T16:07:54.857Z" }, | ||
| 47 | { url = "https://files.pythonhosted.org/packages/28/ed/aff8c9850df1648cc6a5cc7a381f11ee78d98a6b807edd4a5ae276ad60ad/psycopg_binary-3.2.9-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:61d0a6ceed8f08c75a395bc28cb648a81cf8dee75ba4650093ad1a24a51c8724", size = 4477972, upload-time = "2025-05-13T16:07:57.925Z" }, | ||
| 48 | { url = "https://files.pythonhosted.org/packages/5c/bd/8e9d1b77ec1a632818fe2f457c3a65af83c68710c4c162d6866947d08cc5/psycopg_binary-3.2.9-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ad280bbd409bf598683dda82232f5215cfc5f2b1bf0854e409b4d0c44a113b1d", size = 4737516, upload-time = "2025-05-13T16:08:01.616Z" }, | ||
| 49 | { url = "https://files.pythonhosted.org/packages/46/ec/222238f774cd5a0881f3f3b18fb86daceae89cc410f91ef6a9fb4556f236/psycopg_binary-3.2.9-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:76eddaf7fef1d0994e3d536ad48aa75034663d3a07f6f7e3e601105ae73aeff6", size = 4436160, upload-time = "2025-05-13T16:08:04.278Z" }, | ||
| 50 | { url = "https://files.pythonhosted.org/packages/37/78/af5af2a1b296eeca54ea7592cd19284739a844974c9747e516707e7b3b39/psycopg_binary-3.2.9-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:52e239cd66c4158e412318fbe028cd94b0ef21b0707f56dcb4bdc250ee58fd40", size = 3753518, upload-time = "2025-05-13T16:08:07.567Z" }, | ||
| 51 | { url = "https://files.pythonhosted.org/packages/ec/ac/8a3ed39ea069402e9e6e6a2f79d81a71879708b31cc3454283314994b1ae/psycopg_binary-3.2.9-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:08bf9d5eabba160dd4f6ad247cf12f229cc19d2458511cab2eb9647f42fa6795", size = 3313598, upload-time = "2025-05-13T16:08:09.999Z" }, | ||
| 52 | { url = "https://files.pythonhosted.org/packages/da/43/26549af068347c808fbfe5f07d2fa8cef747cfff7c695136172991d2378b/psycopg_binary-3.2.9-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:1b2cf018168cad87580e67bdde38ff5e51511112f1ce6ce9a8336871f465c19a", size = 3407289, upload-time = "2025-05-13T16:08:12.66Z" }, | ||
| 53 | { url = "https://files.pythonhosted.org/packages/67/55/ea8d227c77df8e8aec880ded398316735add8fda5eb4ff5cc96fac11e964/psycopg_binary-3.2.9-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:14f64d1ac6942ff089fc7e926440f7a5ced062e2ed0949d7d2d680dc5c00e2d4", size = 3472493, upload-time = "2025-05-13T16:08:15.672Z" }, | ||
| 54 | { url = "https://files.pythonhosted.org/packages/3c/02/6ff2a5bc53c3cd653d281666728e29121149179c73fddefb1e437024c192/psycopg_binary-3.2.9-cp312-cp312-win_amd64.whl", hash = "sha256:7a838852e5afb6b4126f93eb409516a8c02a49b788f4df8b6469a40c2157fa21", size = 2927400, upload-time = "2025-05-13T16:08:18.652Z" }, | ||
| 55 | { url = "https://files.pythonhosted.org/packages/28/0b/f61ff4e9f23396aca674ed4d5c9a5b7323738021d5d72d36d8b865b3deaf/psycopg_binary-3.2.9-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:98bbe35b5ad24a782c7bf267596638d78aa0e87abc7837bdac5b2a2ab954179e", size = 4017127, upload-time = "2025-05-13T16:08:21.391Z" }, | ||
| 56 | { url = "https://files.pythonhosted.org/packages/bc/00/7e181fb1179fbfc24493738b61efd0453d4b70a0c4b12728e2b82db355fd/psycopg_binary-3.2.9-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:72691a1615ebb42da8b636c5ca9f2b71f266be9e172f66209a361c175b7842c5", size = 4080322, upload-time = "2025-05-13T16:08:24.049Z" }, | ||
| 57 | { url = "https://files.pythonhosted.org/packages/58/fd/94fc267c1d1392c4211e54ccb943be96ea4032e761573cf1047951887494/psycopg_binary-3.2.9-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:25ab464bfba8c401f5536d5aa95f0ca1dd8257b5202eede04019b4415f491351", size = 4655097, upload-time = "2025-05-13T16:08:27.376Z" }, | ||
| 58 | { url = "https://files.pythonhosted.org/packages/41/17/31b3acf43de0b2ba83eac5878ff0dea5a608ca2a5c5dd48067999503a9de/psycopg_binary-3.2.9-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0e8aeefebe752f46e3c4b769e53f1d4ad71208fe1150975ef7662c22cca80fab", size = 4482114, upload-time = "2025-05-13T16:08:30.781Z" }, | ||
| 59 | { url = "https://files.pythonhosted.org/packages/85/78/b4d75e5fd5a85e17f2beb977abbba3389d11a4536b116205846b0e1cf744/psycopg_binary-3.2.9-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b7e4e4dd177a8665c9ce86bc9caae2ab3aa9360b7ce7ec01827ea1baea9ff748", size = 4737693, upload-time = "2025-05-13T16:08:34.625Z" }, | ||
| 60 | { url = "https://files.pythonhosted.org/packages/3b/95/7325a8550e3388b00b5e54f4ced5e7346b531eb4573bf054c3dbbfdc14fe/psycopg_binary-3.2.9-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7fc2915949e5c1ea27a851f7a472a7da7d0a40d679f0a31e42f1022f3c562e87", size = 4437423, upload-time = "2025-05-13T16:08:37.444Z" }, | ||
| 61 | { url = "https://files.pythonhosted.org/packages/1a/db/cef77d08e59910d483df4ee6da8af51c03bb597f500f1fe818f0f3b925d3/psycopg_binary-3.2.9-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:a1fa38a4687b14f517f049477178093c39c2a10fdcced21116f47c017516498f", size = 3758667, upload-time = "2025-05-13T16:08:40.116Z" }, | ||
| 62 | { url = "https://files.pythonhosted.org/packages/95/3e/252fcbffb47189aa84d723b54682e1bb6d05c8875fa50ce1ada914ae6e28/psycopg_binary-3.2.9-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:5be8292d07a3ab828dc95b5ee6b69ca0a5b2e579a577b39671f4f5b47116dfd2", size = 3320576, upload-time = "2025-05-13T16:08:43.243Z" }, | ||
| 63 | { url = "https://files.pythonhosted.org/packages/1c/cd/9b5583936515d085a1bec32b45289ceb53b80d9ce1cea0fef4c782dc41a7/psycopg_binary-3.2.9-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:778588ca9897b6c6bab39b0d3034efff4c5438f5e3bd52fda3914175498202f9", size = 3411439, upload-time = "2025-05-13T16:08:47.321Z" }, | ||
| 64 | { url = "https://files.pythonhosted.org/packages/45/6b/6f1164ea1634c87956cdb6db759e0b8c5827f989ee3cdff0f5c70e8331f2/psycopg_binary-3.2.9-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:f0d5b3af045a187aedbd7ed5fc513bd933a97aaff78e61c3745b330792c4345b", size = 3477477, upload-time = "2025-05-13T16:08:51.166Z" }, | ||
| 65 | { url = "https://files.pythonhosted.org/packages/7b/1d/bf54cfec79377929da600c16114f0da77a5f1670f45e0c3af9fcd36879bc/psycopg_binary-3.2.9-cp313-cp313-win_amd64.whl", hash = "sha256:2290bc146a1b6a9730350f695e8b670e1d1feb8446597bed0bbe7c3c30e0abcb", size = 2928009, upload-time = "2025-05-13T16:08:53.67Z" }, | ||
| 66 | ] | ||
| 67 | |||
| 68 | [[package]] | ||
| 69 | name = "psycopg-pool" | ||
| 70 | version = "3.2.6" | ||
| 71 | source = { registry = "https://pypi.org/simple" } | ||
| 72 | dependencies = [ | ||
| 73 | { name = "typing-extensions" }, | ||
| 74 | ] | ||
| 75 | sdist = { url = "https://files.pythonhosted.org/packages/cf/13/1e7850bb2c69a63267c3dbf37387d3f71a00fd0e2fa55c5db14d64ba1af4/psycopg_pool-3.2.6.tar.gz", hash = "sha256:0f92a7817719517212fbfe2fd58b8c35c1850cdd2a80d36b581ba2085d9148e5", size = 29770, upload-time = "2025-02-26T12:03:47.129Z" } | ||
| 76 | wheels = [ | ||
| 77 | { url = "https://files.pythonhosted.org/packages/47/fd/4feb52a55c1a4bd748f2acaed1903ab54a723c47f6d0242780f4d97104d4/psycopg_pool-3.2.6-py3-none-any.whl", hash = "sha256:5887318a9f6af906d041a0b1dc1c60f8f0dda8340c2572b74e10907b51ed5da7", size = 38252, upload-time = "2025-02-26T12:03:45.073Z" }, | ||
| 78 | ] | ||
| 79 | |||
| 80 | [[package]] | ||
| 81 | name = "sdnotify" | ||
| 82 | version = "0.3.2" | ||
| 83 | source = { registry = "https://pypi.org/simple" } | ||
| 84 | sdist = { url = "https://files.pythonhosted.org/packages/ce/d8/9fdc36b2a912bf78106de4b3f0de3891ff8f369e7a6f80be842b8b0b6bd5/sdnotify-0.3.2.tar.gz", hash = "sha256:73977fc746b36cc41184dd43c3fe81323e7b8b06c2bb0826c4f59a20c56bb9f1", size = 2459, upload-time = "2017-08-02T20:03:44.395Z" } | ||
| 85 | |||
| 86 | [[package]] | ||
| 87 | name = "systemd-python" | ||
| 88 | version = "235" | ||
| 89 | source = { registry = "https://pypi.org/simple" } | ||
| 90 | sdist = { url = "https://files.pythonhosted.org/packages/10/9e/ab4458e00367223bda2dd7ccf0849a72235ee3e29b36dce732685d9b7ad9/systemd-python-235.tar.gz", hash = "sha256:4e57f39797fd5d9e2d22b8806a252d7c0106c936039d1e71c8c6b8008e695c0a", size = 61677, upload-time = "2023-02-11T13:42:16.588Z" } | ||
| 91 | |||
| 92 | [[package]] | ||
| 93 | name = "systemd-socketserver" | ||
| 94 | version = "1.0" | ||
| 95 | source = { registry = "https://pypi.org/simple" } | ||
| 96 | dependencies = [ | ||
| 97 | { name = "systemd-python" }, | ||
| 98 | ] | ||
| 99 | wheels = [ | ||
| 100 | { url = "https://files.pythonhosted.org/packages/d8/4f/b28b7f08880120a26669b080ca74487c8c67e8b54dcb0467a8f0c9f38ed6/systemd_socketserver-1.0-py3-none-any.whl", hash = "sha256:987a8bfbf28d959e7c2966c742ad7bad482f05e121077defcf95bb38267db9a8", size = 3248, upload-time = "2020-04-26T05:26:40.661Z" }, | ||
| 101 | ] | ||
| 102 | |||
| 103 | [[package]] | ||
| 104 | name = "typing-extensions" | ||
| 105 | version = "4.13.2" | ||
| 106 | source = { registry = "https://pypi.org/simple" } | ||
| 107 | sdist = { url = "https://files.pythonhosted.org/packages/f6/37/23083fcd6e35492953e8d2aaaa68b860eb422b34627b13f2ce3eb6106061/typing_extensions-4.13.2.tar.gz", hash = "sha256:e6c81219bd689f51865d9e372991c540bda33a0379d5573cddb9a3a23f7caaef", size = 106967, upload-time = "2025-04-10T14:19:05.416Z" } | ||
| 108 | wheels = [ | ||
| 109 | { url = "https://files.pythonhosted.org/packages/8b/54/b1ae86c0973cc6f0210b53d508ca3641fb6d0c56823f288d108bc7ab3cc8/typing_extensions-4.13.2-py3-none-any.whl", hash = "sha256:a439e7c04b49fec3e5d3e2beaa21755cadbbdc391694e28ccdd36ca4a1408f8c", size = 45806, upload-time = "2025-04-10T14:19:03.967Z" }, | ||
| 110 | ] | ||
| 111 | |||
| 112 | [[package]] | ||
| 113 | name = "tzdata" | ||
| 114 | version = "2025.2" | ||
| 115 | source = { registry = "https://pypi.org/simple" } | ||
| 116 | sdist = { url = "https://files.pythonhosted.org/packages/95/32/1a225d6164441be760d75c2c42e2780dc0873fe382da3e98a2e1e48361e5/tzdata-2025.2.tar.gz", hash = "sha256:b60a638fcc0daffadf82fe0f57e53d06bdec2f36c4df66280ae79bce6bd6f2b9", size = 196380, upload-time = "2025-03-23T13:54:43.652Z" } | ||
| 117 | wheels = [ | ||
| 118 | { url = "https://files.pythonhosted.org/packages/5c/23/c7abc0ca0a1526a0774eca151daeb8de62ec457e77262b66b359c3c7679e/tzdata-2025.2-py2.py3-none-any.whl", hash = "sha256:1a403fada01ff9221ca8044d701868fa132215d84beb92242d9acd2147f667a8", size = 347839, upload-time = "2025-03-23T13:54:41.845Z" }, | ||
| 119 | ] | ||
diff --git a/hosts/surtr/hledger.nix b/hosts/surtr/hledger.nix new file mode 100644 index 00000000..e44933c3 --- /dev/null +++ b/hosts/surtr/hledger.nix | |||
| @@ -0,0 +1,66 @@ | |||
| 1 | { config, ... }: | ||
| 2 | |||
| 3 | { | ||
| 4 | config = { | ||
| 5 | security.acme.rfc2136Domains = { | ||
| 6 | "hledger.yggdrasil.li" = { | ||
| 7 | restartUnits = ["nginx.service"]; | ||
| 8 | }; | ||
| 9 | }; | ||
| 10 | |||
| 11 | services.nginx = { | ||
| 12 | upstreams."hledger" = { | ||
| 13 | servers = { | ||
| 14 | "[2a03:4000:52:ada:4:1::]:5000" = {}; | ||
| 15 | }; | ||
| 16 | extraConfig = '' | ||
| 17 | keepalive 8; | ||
| 18 | ''; | ||
| 19 | }; | ||
| 20 | virtualHosts = { | ||
| 21 | "hledger.yggdrasil.li" = { | ||
| 22 | kTLS = true; | ||
| 23 | http3 = true; | ||
| 24 | forceSSL = true; | ||
| 25 | sslCertificate = "/run/credentials/nginx.service/hledger.yggdrasil.li.pem"; | ||
| 26 | sslCertificateKey = "/run/credentials/nginx.service/hledger.yggdrasil.li.key.pem"; | ||
| 27 | sslTrustedCertificate = "/run/credentials/nginx.service/hledger.yggdrasil.li.chain.pem"; | ||
| 28 | extraConfig = '' | ||
| 29 | charset utf-8; | ||
| 30 | ''; | ||
| 31 | |||
| 32 | locations = { | ||
| 33 | "/".extraConfig = '' | ||
| 34 | proxy_pass http://hledger; | ||
| 35 | |||
| 36 | proxy_http_version 1.1; | ||
| 37 | proxy_set_header Upgrade $http_upgrade; | ||
| 38 | proxy_set_header Connection "upgrade"; | ||
| 39 | |||
| 40 | proxy_redirect off; | ||
| 41 | proxy_set_header Host $host; | ||
| 42 | proxy_set_header X-Real-IP $remote_addr; | ||
| 43 | proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; | ||
| 44 | proxy_set_header X-Forwarded-Host $server_name; | ||
| 45 | proxy_set_header X-Forwarded-Proto $scheme; | ||
| 46 | |||
| 47 | client_max_body_size 0; | ||
| 48 | proxy_request_buffering off; | ||
| 49 | proxy_buffering off; | ||
| 50 | ''; | ||
| 51 | }; | ||
| 52 | }; | ||
| 53 | }; | ||
| 54 | }; | ||
| 55 | |||
| 56 | systemd.services.nginx = { | ||
| 57 | serviceConfig = { | ||
| 58 | LoadCredential = [ | ||
| 59 | "hledger.yggdrasil.li.key.pem:${config.security.acme.certs."hledger.yggdrasil.li".directory}/key.pem" | ||
| 60 | "hledger.yggdrasil.li.pem:${config.security.acme.certs."hledger.yggdrasil.li".directory}/fullchain.pem" | ||
| 61 | "hledger.yggdrasil.li.chain.pem:${config.security.acme.certs."hledger.yggdrasil.li".directory}/chain.pem" | ||
| 62 | ]; | ||
| 63 | }; | ||
| 64 | }; | ||
| 65 | }; | ||
| 66 | } | ||
diff --git a/hosts/surtr/http/default.nix b/hosts/surtr/http/default.nix index f3a7154e..ea527cb5 100644 --- a/hosts/surtr/http/default.nix +++ b/hosts/surtr/http/default.nix | |||
| @@ -13,8 +13,6 @@ | |||
| 13 | recommendedTlsSettings = true; | 13 | recommendedTlsSettings = true; |
| 14 | sslDhparam = config.security.dhparams.params.nginx.path; | 14 | sslDhparam = config.security.dhparams.params.nginx.path; |
| 15 | commonHttpConfig = '' | 15 | commonHttpConfig = '' |
| 16 | ssl_ecdh_curve X448:X25519:prime256v1:secp521r1:secp384r1; | ||
| 17 | |||
| 18 | log_format main | 16 | log_format main |
| 19 | '$remote_addr "$remote_user" ' | 17 | '$remote_addr "$remote_user" ' |
| 20 | '"$host" "$request" $status $bytes_sent ' | 18 | '"$host" "$request" $status $bytes_sent ' |
diff --git a/hosts/surtr/immich.nix b/hosts/surtr/immich.nix new file mode 100644 index 00000000..61a55e77 --- /dev/null +++ b/hosts/surtr/immich.nix | |||
| @@ -0,0 +1,66 @@ | |||
| 1 | { config, ... }: | ||
| 2 | |||
| 3 | { | ||
| 4 | config = { | ||
| 5 | security.acme.rfc2136Domains = { | ||
| 6 | "immich.yggdrasil.li" = { | ||
| 7 | restartUnits = ["nginx.service"]; | ||
| 8 | }; | ||
| 9 | }; | ||
| 10 | |||
| 11 | services.nginx = { | ||
| 12 | upstreams."immich" = { | ||
| 13 | servers = { | ||
| 14 | "[2a03:4000:52:ada:4:1::]:2283" = {}; | ||
| 15 | }; | ||
| 16 | extraConfig = '' | ||
| 17 | keepalive 8; | ||
| 18 | ''; | ||
| 19 | }; | ||
| 20 | virtualHosts = { | ||
| 21 | "immich.yggdrasil.li" = { | ||
| 22 | kTLS = true; | ||
| 23 | http3 = true; | ||
| 24 | forceSSL = true; | ||
| 25 | sslCertificate = "/run/credentials/nginx.service/immich.yggdrasil.li.pem"; | ||
| 26 | sslCertificateKey = "/run/credentials/nginx.service/immich.yggdrasil.li.key.pem"; | ||
| 27 | sslTrustedCertificate = "/run/credentials/nginx.service/immich.yggdrasil.li.chain.pem"; | ||
| 28 | extraConfig = '' | ||
| 29 | charset utf-8; | ||
| 30 | ''; | ||
| 31 | |||
| 32 | locations = { | ||
| 33 | "/".extraConfig = '' | ||
| 34 | proxy_pass http://immich; | ||
| 35 | |||
| 36 | proxy_http_version 1.1; | ||
| 37 | proxy_set_header Upgrade $http_upgrade; | ||
| 38 | proxy_set_header Connection "upgrade"; | ||
| 39 | |||
| 40 | proxy_redirect off; | ||
| 41 | proxy_set_header Host $host; | ||
| 42 | proxy_set_header X-Real-IP $remote_addr; | ||
| 43 | proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; | ||
| 44 | proxy_set_header X-Forwarded-Host $server_name; | ||
| 45 | proxy_set_header X-Forwarded-Proto $scheme; | ||
| 46 | |||
| 47 | client_max_body_size 0; | ||
| 48 | proxy_request_buffering off; | ||
| 49 | proxy_buffering off; | ||
| 50 | ''; | ||
| 51 | }; | ||
| 52 | }; | ||
| 53 | }; | ||
| 54 | }; | ||
| 55 | |||
| 56 | systemd.services.nginx = { | ||
| 57 | serviceConfig = { | ||
| 58 | LoadCredential = [ | ||
| 59 | "immich.yggdrasil.li.key.pem:${config.security.acme.certs."immich.yggdrasil.li".directory}/key.pem" | ||
| 60 | "immich.yggdrasil.li.pem:${config.security.acme.certs."immich.yggdrasil.li".directory}/fullchain.pem" | ||
| 61 | "immich.yggdrasil.li.chain.pem:${config.security.acme.certs."immich.yggdrasil.li".directory}/chain.pem" | ||
| 62 | ]; | ||
| 63 | }; | ||
| 64 | }; | ||
| 65 | }; | ||
| 66 | } | ||
diff --git a/hosts/surtr/kimai.nix b/hosts/surtr/kimai.nix new file mode 100644 index 00000000..454b3d80 --- /dev/null +++ b/hosts/surtr/kimai.nix | |||
| @@ -0,0 +1,68 @@ | |||
| 1 | { config, ... }: | ||
| 2 | |||
| 3 | { | ||
| 4 | config = { | ||
| 5 | security.acme.rfc2136Domains = { | ||
| 6 | "kimai.yggdrasil.li" = { | ||
| 7 | restartUnits = ["nginx.service"]; | ||
| 8 | }; | ||
| 9 | }; | ||
| 10 | |||
| 11 | services.nginx = { | ||
| 12 | upstreams."kimai" = { | ||
| 13 | servers = { | ||
| 14 | "[2a03:4000:52:ada:6::2]:80" = {}; | ||
| 15 | }; | ||
| 16 | extraConfig = '' | ||
| 17 | keepalive 8; | ||
| 18 | ''; | ||
| 19 | }; | ||
| 20 | virtualHosts = { | ||
| 21 | "kimai.yggdrasil.li" = { | ||
| 22 | kTLS = true; | ||
| 23 | http3 = true; | ||
| 24 | forceSSL = true; | ||
| 25 | sslCertificate = "/run/credentials/nginx.service/kimai.yggdrasil.li.pem"; | ||
| 26 | sslCertificateKey = "/run/credentials/nginx.service/kimai.yggdrasil.li.key.pem"; | ||
| 27 | sslTrustedCertificate = "/run/credentials/nginx.service/kimai.yggdrasil.li.chain.pem"; | ||
| 28 | extraConfig = '' | ||
| 29 | charset utf-8; | ||
| 30 | ''; | ||
| 31 | |||
| 32 | locations = { | ||
| 33 | "/".extraConfig = '' | ||
| 34 | proxy_pass http://kimai; | ||
| 35 | |||
| 36 | proxy_http_version 1.1; | ||
| 37 | proxy_set_header Upgrade $http_upgrade; | ||
| 38 | proxy_set_header Connection "upgrade"; | ||
| 39 | |||
| 40 | proxy_redirect off; | ||
| 41 | proxy_set_header Host $host; | ||
| 42 | proxy_set_header X-Real-IP $remote_addr; | ||
| 43 | proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; | ||
| 44 | proxy_set_header X-Forwarded-Host $server_name; | ||
| 45 | proxy_set_header X-Forwarded-Proto $scheme; | ||
| 46 | |||
| 47 | client_max_body_size 0; | ||
| 48 | proxy_request_buffering off; | ||
| 49 | proxy_buffering off; | ||
| 50 | |||
| 51 | proxy_read_timeout 300; | ||
| 52 | ''; | ||
| 53 | }; | ||
| 54 | }; | ||
| 55 | }; | ||
| 56 | }; | ||
| 57 | |||
| 58 | systemd.services.nginx = { | ||
| 59 | serviceConfig = { | ||
| 60 | LoadCredential = [ | ||
| 61 | "kimai.yggdrasil.li.key.pem:${config.security.acme.certs."kimai.yggdrasil.li".directory}/key.pem" | ||
| 62 | "kimai.yggdrasil.li.pem:${config.security.acme.certs."kimai.yggdrasil.li".directory}/fullchain.pem" | ||
| 63 | "kimai.yggdrasil.li.chain.pem:${config.security.acme.certs."kimai.yggdrasil.li".directory}/chain.pem" | ||
| 64 | ]; | ||
| 65 | }; | ||
| 66 | }; | ||
| 67 | }; | ||
| 68 | } | ||
diff --git a/hosts/surtr/paperless.nix b/hosts/surtr/paperless.nix new file mode 100644 index 00000000..7bc4397c --- /dev/null +++ b/hosts/surtr/paperless.nix | |||
| @@ -0,0 +1,66 @@ | |||
| 1 | { config, ... }: | ||
| 2 | |||
| 3 | { | ||
| 4 | config = { | ||
| 5 | security.acme.rfc2136Domains = { | ||
| 6 | "paperless.yggdrasil.li" = { | ||
| 7 | restartUnits = ["nginx.service"]; | ||
| 8 | }; | ||
| 9 | }; | ||
| 10 | |||
| 11 | services.nginx = { | ||
| 12 | upstreams."paperless" = { | ||
| 13 | servers = { | ||
| 14 | "[2a03:4000:52:ada:4:1::]:28981" = {}; | ||
| 15 | }; | ||
| 16 | extraConfig = '' | ||
| 17 | keepalive 8; | ||
| 18 | ''; | ||
| 19 | }; | ||
| 20 | virtualHosts = { | ||
| 21 | "paperless.yggdrasil.li" = { | ||
| 22 | kTLS = true; | ||
| 23 | http3 = true; | ||
| 24 | forceSSL = true; | ||
| 25 | sslCertificate = "/run/credentials/nginx.service/paperless.yggdrasil.li.pem"; | ||
| 26 | sslCertificateKey = "/run/credentials/nginx.service/paperless.yggdrasil.li.key.pem"; | ||
| 27 | sslTrustedCertificate = "/run/credentials/nginx.service/paperless.yggdrasil.li.chain.pem"; | ||
| 28 | extraConfig = '' | ||
| 29 | charset utf-8; | ||
| 30 | ''; | ||
| 31 | |||
| 32 | locations = { | ||
| 33 | "/".extraConfig = '' | ||
| 34 | proxy_pass http://paperless; | ||
| 35 | |||
| 36 | proxy_http_version 1.1; | ||
| 37 | proxy_set_header Upgrade $http_upgrade; | ||
| 38 | proxy_set_header Connection "upgrade"; | ||
| 39 | |||
| 40 | proxy_redirect off; | ||
| 41 | proxy_set_header Host $host; | ||
| 42 | proxy_set_header X-Real-IP $remote_addr; | ||
| 43 | proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; | ||
| 44 | proxy_set_header X-Forwarded-Host $server_name; | ||
| 45 | proxy_set_header X-Forwarded-Proto $scheme; | ||
| 46 | |||
| 47 | client_max_body_size 0; | ||
| 48 | proxy_request_buffering off; | ||
| 49 | proxy_buffering off; | ||
| 50 | ''; | ||
| 51 | }; | ||
| 52 | }; | ||
| 53 | }; | ||
| 54 | }; | ||
| 55 | |||
| 56 | systemd.services.nginx = { | ||
| 57 | serviceConfig = { | ||
| 58 | LoadCredential = [ | ||
| 59 | "paperless.yggdrasil.li.key.pem:${config.security.acme.certs."paperless.yggdrasil.li".directory}/key.pem" | ||
| 60 | "paperless.yggdrasil.li.pem:${config.security.acme.certs."paperless.yggdrasil.li".directory}/fullchain.pem" | ||
| 61 | "paperless.yggdrasil.li.chain.pem:${config.security.acme.certs."paperless.yggdrasil.li".directory}/chain.pem" | ||
| 62 | ]; | ||
| 63 | }; | ||
| 64 | }; | ||
| 65 | }; | ||
| 66 | } | ||
diff --git a/hosts/surtr/postgresql/default.nix b/hosts/surtr/postgresql/default.nix index 583e4443..3786ea7c 100644 --- a/hosts/surtr/postgresql/default.nix +++ b/hosts/surtr/postgresql/default.nix | |||
| @@ -89,6 +89,10 @@ in { | |||
| 89 | "d /var/spool/pgbackrest 0750 postgres postgres - -" | 89 | "d /var/spool/pgbackrest 0750 postgres postgres - -" |
| 90 | ]; | 90 | ]; |
| 91 | 91 | ||
| 92 | systemd.services.postgresql.serviceConfig = { | ||
| 93 | ReadWritePaths = [ "/var/spool/pgbackrest" "/var/lib/pgbackrest/archive/surtr" ]; | ||
| 94 | }; | ||
| 95 | |||
| 92 | systemd.services.migrate-postgresql = { | 96 | systemd.services.migrate-postgresql = { |
| 93 | after = [ "postgresql.service" ]; | 97 | after = [ "postgresql.service" ]; |
| 94 | bindsTo = [ "postgresql.service" ]; | 98 | bindsTo = [ "postgresql.service" ]; |
| @@ -276,6 +280,64 @@ in { | |||
| 276 | CREATE VIEW imap_user ("user", "password", quota_rule) AS SELECT mailbox.mailbox AS "user", "password", quota_rule FROM mailbox_quota_rule INNER JOIN mailbox ON mailbox_quota_rule.mailbox = mailbox.mailbox; | 280 | CREATE VIEW imap_user ("user", "password", quota_rule) AS SELECT mailbox.mailbox AS "user", "password", quota_rule FROM mailbox_quota_rule INNER JOIN mailbox ON mailbox_quota_rule.mailbox = mailbox.mailbox; |
| 277 | 281 | ||
| 278 | COMMIT; | 282 | COMMIT; |
| 283 | |||
| 284 | BEGIN; | ||
| 285 | SELECT _v.register_patch('013-internal', ARRAY['000-base'], null); | ||
| 286 | |||
| 287 | ALTER TABLE mailbox_mapping ADD COLUMN internal bool NOT NULL DEFAULT false; | ||
| 288 | CREATE TABLE mailbox_mapping_access ( | ||
| 289 | id uuid PRIMARY KEY NOT NULL DEFAULT gen_random_uuid(), | ||
| 290 | mailbox_mapping uuid REFERENCES mailbox_mapping(id), | ||
| 291 | mailbox uuid REFERENCES mailbox(id) | ||
| 292 | ); | ||
| 293 | CREATE USER "postfix-internal-policy"; | ||
| 294 | GRANT CONNECT ON DATABASE "email" TO "postfix-internal-policy"; | ||
| 295 | ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT SELECT ON TABLES TO "postfix-internal-policy"; | ||
| 296 | GRANT SELECT ON ALL TABLES IN SCHEMA public TO "postfix-internal-policy"; | ||
| 297 | |||
| 298 | COMMIT; | ||
| 299 | |||
| 300 | BEGIN; | ||
| 301 | SELECT _v.register_patch('014-relay', ARRAY['000-base'], null); | ||
| 302 | |||
| 303 | CREATE TABLE relay_access ( | ||
| 304 | id uuid PRIMARY KEY NOT NULL DEFAULT gen_random_uuid(), | ||
| 305 | mailbox uuid REFERENCES mailbox(id), | ||
| 306 | domain citext NOT NULL CONSTRAINT domain_non_empty CHECK (domain <> ''') | ||
| 307 | ); | ||
| 308 | |||
| 309 | COMMIT; | ||
| 310 | |||
| 311 | BEGIN; | ||
| 312 | SELECT _v.register_patch('015-relay-unique', ARRAY['000-base', '014-relay'], null); | ||
| 313 | |||
| 314 | CREATE UNIQUE INDEX relay_unique ON relay_access (mailbox, domain); | ||
| 315 | |||
| 316 | COMMIT; | ||
| 317 | |||
| 318 | BEGIN; | ||
| 319 | SELECT _v.register_patch('015-sender_bcc', null, null); | ||
| 320 | |||
| 321 | CREATE TABLE sender_bcc_maps ( | ||
| 322 | id uuid PRIMARY KEY NOT NULL DEFAULT gen_random_uuid(), | ||
| 323 | key text NOT NULL CONSTRAINT key_not_empty CHECK (key <> '''), | ||
| 324 | value text NOT NULL CONSTRAINT value_not_empty CHECK (value <> '''), | ||
| 325 | CONSTRAINT key_unique UNIQUE (key) | ||
| 326 | ); | ||
| 327 | |||
| 328 | COMMIT; | ||
| 329 | |||
| 330 | BEGIN; | ||
| 331 | SELECT _v.register_patch('016-recipient_bcc', null, null); | ||
| 332 | |||
| 333 | CREATE TABLE recipient_bcc_maps ( | ||
| 334 | id uuid PRIMARY KEY NOT NULL DEFAULT gen_random_uuid(), | ||
| 335 | key text NOT NULL CONSTRAINT key_not_empty CHECK (key <> '''), | ||
| 336 | value text NOT NULL CONSTRAINT value_not_empty CHECK (value <> '''), | ||
| 337 | CONSTRAINT recipient_bcc_maps_key_unique UNIQUE (key) | ||
| 338 | ); | ||
| 339 | |||
| 340 | COMMIT; | ||
| 279 | ''} | 341 | ''} |
| 280 | 342 | ||
| 281 | psql etebase postgres -eXf ${pkgs.writeText "etebase.sql" '' | 343 | psql etebase postgres -eXf ${pkgs.writeText "etebase.sql" '' |
diff --git a/hosts/surtr/tls/default.nix b/hosts/surtr/tls/default.nix index b1c05888..b25bd2ea 100644 --- a/hosts/surtr/tls/default.nix +++ b/hosts/surtr/tls/default.nix | |||
| @@ -41,7 +41,7 @@ in { | |||
| 41 | 41 | ||
| 42 | acceptTerms = true; | 42 | acceptTerms = true; |
| 43 | # DNS challenge is slow | 43 | # DNS challenge is slow |
| 44 | preliminarySelfsigned = true; | 44 | # preliminarySelfsigned = true; |
| 45 | defaults = { | 45 | defaults = { |
| 46 | email = "phikeebaogobaegh@141.li"; | 46 | email = "phikeebaogobaegh@141.li"; |
| 47 | # We don't like NIST curves and Let's Encrypt doesn't support | 47 | # We don't like NIST curves and Let's Encrypt doesn't support |
diff --git a/hosts/surtr/tls/tsig_key.gup b/hosts/surtr/tls/tsig_key.gup index 3d81b603..46a3789e 100644 --- a/hosts/surtr/tls/tsig_key.gup +++ b/hosts/surtr/tls/tsig_key.gup | |||
| @@ -1,6 +1,6 @@ | |||
| 1 | #!/usr/bin/env zsh | 1 | #!/usr/bin/env zsh |
| 2 | 2 | ||
| 3 | keyFile=../dns/keys/${2:t}_acme.yaml | 3 | keyFile=../dns/keys/${2:t}_acme |
| 4 | gup -u $keyFile | 4 | gup -u $keyFile |
| 5 | sops -d --input-type=binary --output-type=binary ${keyFile} | yq -r '.key[0].secret' > $1 | 5 | sops -d --input-type=binary --output-type=binary ${keyFile} | yq -r '.key[0].secret' > $1 |
| 6 | sops -p '7ED22F4AA7BB55728B643DC5471B7D88E4EF66F8,30D3453B8CD02FE2A3E7C78C0FB536FB87AE8F51' --input-type=binary -e -i $1 \ No newline at end of file | 6 | sops --input-type=binary -e -i $1 |
diff --git a/hosts/surtr/tls/tsig_keys/audiobookshelf.yggdrasil.li b/hosts/surtr/tls/tsig_keys/audiobookshelf.yggdrasil.li new file mode 100644 index 00000000..8dd610dd --- /dev/null +++ b/hosts/surtr/tls/tsig_keys/audiobookshelf.yggdrasil.li | |||
| @@ -0,0 +1,19 @@ | |||
| 1 | { | ||
| 2 | "data": "ENC[AES256_GCM,data:r9jhdTlbDnCMq1QLJutn76uz1Ml8MFs7fXYRSiVYh1gafcXXsUZBq5+qqoQI,iv:un/luttuKpCiMf53fa2SRY0ffttGiYwT8DuHCKEnnEI=,tag:SkNULZSulQmP99aB/Ec+Fw==,type:str]", | ||
| 3 | "sops": { | ||
| 4 | "age": [ | ||
| 5 | { | ||
| 6 | "recipient": "age1rmmhetcmllq0ahl5qznlr0eya2zdxwl9h6y5wnl97d2wtyx5t99sm2u866", | ||
| 7 | "enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBkZGJzaEsrSU4raHlTVDVB\nczRnWVlSTTRuNXU0T3F1RTkxKytXeVJRdGpFCk9WMzNBR1NaTTMzN3BGQ2JmTjVt\nRU4rSWxCYjJPYVRzLzR0OVRYQm45TUkKLS0tIDNyMnpPN2VKUFFadTkveXRYeWps\nYUNaTjRJLzdWUnREaUVIWkpFV0FTZ2MKJS0K49SdkLW4p67FlgboHy/OVvCiUA7g\nuv5b+yotkQmh5xJwr7CUvwRewqJh56mg1yhWmE8wzpgLZMIjRXcQCQ==\n-----END AGE ENCRYPTED FILE-----\n" | ||
| 8 | }, | ||
| 9 | { | ||
| 10 | "recipient": "age19a7j77w267z04zls7m28a8hj4a0g5af6ltye2d5wypg33c3l89csd4r9zq", | ||
| 11 | "enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBQQ1h3M1lXTXVNd0d6cmtU\nU2JtUzFFblJudmEycnJONkkwME9wWm5jWVFzCnRYVEFWaVNvSW9GZ05TRWF4L2ho\nanltVytEU3ZOdHk1VHY5aGJDUkdDdmcKLS0tIEtzOFVkbmpjbWN5d0c1VEpxc1Rr\nSzJwclYxeC9TVWNaK2gwUmJSY0x1ZVUKTNivp5iS+1tzVMjMn17/ncvHcELhjQ/B\n0OVz4VpKM2wv6CjEcIMxmchqT8p8GFYVRrKUdqO2GEKOoe8ANtidWA==\n-----END AGE ENCRYPTED FILE-----\n" | ||
| 12 | } | ||
| 13 | ], | ||
| 14 | "lastmodified": "2025-05-09T17:07:16Z", | ||
| 15 | "mac": "ENC[AES256_GCM,data:SwS+8UQnPgHORobKLu+u2pNaMdKIvR+etUed8btbbne/IX/Wpxt0qyPYXNNGGRkN3KAxTHWjRRdrKU1bkuTU3ER1c94T935ExDESKJLVjzaEF5VSWCqLyUNCMsY2ANw84UES2swK4YI4zF1CP7rD8tKFFld78IWZoeQ7XNGDMRA=,iv:neLvamISgQ5+aqW1iRj9xJoXq1weNNyy7KCFG2+WRQE=,tag:66SDO61WnKU6DVElo9CImg==,type:str]", | ||
| 16 | "unencrypted_suffix": "_unencrypted", | ||
| 17 | "version": "3.10.2" | ||
| 18 | } | ||
| 19 | } | ||
diff --git a/hosts/surtr/tls/tsig_keys/hledger.yggdrasil.li b/hosts/surtr/tls/tsig_keys/hledger.yggdrasil.li new file mode 100644 index 00000000..ab6cdd68 --- /dev/null +++ b/hosts/surtr/tls/tsig_keys/hledger.yggdrasil.li | |||
| @@ -0,0 +1,24 @@ | |||
| 1 | { | ||
| 2 | "data": "ENC[AES256_GCM,data:Yd70QIj9DE6a5IN+Mf2M5p95vkRMHRg9BXaM686W7BRtthOw9m54/5FK6JWr,iv:cIOIKinkqFFPgTZdewWVY0h6kM5hGfVzuA4iYNhwK5c=,tag:Ds/oI+TOERbIdcGbI4WoEg==,type:str]", | ||
| 3 | "sops": { | ||
| 4 | "kms": null, | ||
| 5 | "gcp_kms": null, | ||
| 6 | "azure_kv": null, | ||
| 7 | "hc_vault": null, | ||
| 8 | "age": [ | ||
| 9 | { | ||
| 10 | "recipient": "age1rmmhetcmllq0ahl5qznlr0eya2zdxwl9h6y5wnl97d2wtyx5t99sm2u866", | ||
| 11 | "enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSA1NnJ4SmsvSlh3ZlliSm9C\nNG45clh3NEZWZ05jaHhVK0xqTVRFL2wxN1ZrCk5hL2p2ZjhtcDBjTEZscXFTNkY5\nemVZSUUwV2V5cFBTdWo4RWxsM2xROVEKLS0tIDBFSFlkUVJ0ajJEUENlelVFKzVk\ndnJhMURMU2o3WVBKZGNVRXBiNytqUFEKHivcSTYy5D770C0h7RsmLBmkIG9+MDoV\ngJHvfkGzXPKwmDTMKdHbIk+ctI+u0/1jMn/K2Q9OFnOIYxP3gHiFag==\n-----END AGE ENCRYPTED FILE-----\n" | ||
| 12 | }, | ||
| 13 | { | ||
| 14 | "recipient": "age19a7j77w267z04zls7m28a8hj4a0g5af6ltye2d5wypg33c3l89csd4r9zq", | ||
| 15 | "enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSArYUNCQ1U2MXpQdmM4SHR0\nTnJWZlFGTUQ1NjVqZ3Z2MGt5NFpBVFp0b0YwCkdseitkbzI1RkZyL3V5Z1pNMG9R\nVnEzRUxrTDQrS3BiMXB1QUFPeUcxZUkKLS0tIGJFODkwNFY3c0tBLzFBNjFiYjJk\nZW82bTdia2F1NHpNSG1IYmhWb1ZCTHMK9ovFx3+x5PrV4y6+RH5XA5DK2wRPXlAt\ncxxpRZIlmnvhZXIeCYE9yhHFmz3uAn0Oib1RUDblca9FlnF9tyYD6g==\n-----END AGE ENCRYPTED FILE-----\n" | ||
| 16 | } | ||
| 17 | ], | ||
| 18 | "lastmodified": "2025-02-19T17:13:51Z", | ||
| 19 | "mac": "ENC[AES256_GCM,data:ReRuK9qdZV8AbMzA9Yur0AZW+1RF3aRnfBvsKJkQtXsFdkmJQ4QkRGtL27RmjFdvQ3kXBIyhib7hYA60AJ0amduYrSScY0dtz8AurjyE4f2BGQ9/QeKRBfKXHxLvj4/xWNvS4+PVdGKkKbqIs8isz9n77WQQ3lTHop2K/TjaTuQ=,iv:gUhDK9oeUHdpQ2Fp8mFDIgPFo2JjHE0jjooL7FmvmrE=,tag:2lkwSl3j3oqamdLbM9wbow==,type:str]", | ||
| 20 | "pgp": null, | ||
| 21 | "unencrypted_suffix": "_unencrypted", | ||
| 22 | "version": "3.9.4" | ||
| 23 | } | ||
| 24 | } \ No newline at end of file | ||
diff --git a/hosts/surtr/tls/tsig_keys/immich.yggdrasil.li b/hosts/surtr/tls/tsig_keys/immich.yggdrasil.li new file mode 100644 index 00000000..73104cc1 --- /dev/null +++ b/hosts/surtr/tls/tsig_keys/immich.yggdrasil.li | |||
| @@ -0,0 +1,24 @@ | |||
| 1 | { | ||
| 2 | "data": "ENC[AES256_GCM,data:COfmT91I4+yiPhN3Hi7BTqMHyKhdKtwlzT9vNgTZc7FWTHhfuTtCHQo/rhX0,iv:RDs//AT8peUhKwIRdchCScUr/PlEzyMzQPB90S4k3g4=,tag:Lh4BULmQ6+hC+Ed8s9k0Hw==,type:str]", | ||
| 3 | "sops": { | ||
| 4 | "kms": null, | ||
| 5 | "gcp_kms": null, | ||
| 6 | "azure_kv": null, | ||
| 7 | "hc_vault": null, | ||
| 8 | "age": [ | ||
| 9 | { | ||
| 10 | "recipient": "age1rmmhetcmllq0ahl5qznlr0eya2zdxwl9h6y5wnl97d2wtyx5t99sm2u866", | ||
| 11 | "enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBGVEQvU29uWnVjMS8xcGp1\nam5hZVN3ejAxaGdpVWNia0VxT0I3dzR4YlV3ClczOGd0ZDJmUDhqb2dEdm5VeUdX\nRlR1WDNYUU9qaTYrRzhYMXBTV1JjV1UKLS0tIHBONU55RWtRSkR6K2NTNFZrUEZj\nbktqY0xBdGtiZWFFV3JUUVZTOC9YV2MKI4Ytz1NZ9+Og0GzIt/bh6L3aJUeR476g\nyRNifW4eOHf4Ne02ElpEoq6woInkxk8Ou/SJVIRmEOhjwm+qbV17gQ==\n-----END AGE ENCRYPTED FILE-----\n" | ||
| 12 | }, | ||
| 13 | { | ||
| 14 | "recipient": "age19a7j77w267z04zls7m28a8hj4a0g5af6ltye2d5wypg33c3l89csd4r9zq", | ||
| 15 | "enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBVOVNydWcyYjFrZDR2WStu\nWkJQd3VTTGtqVzdLVFR1eFdPZXBtVFBzVXc4ClREQVpKeXlhWlBFQzVFL0VGME5K\nUUhoa3A1YWdvSkZVV1FQcUh5L3RoUmMKLS0tIHE4c3A1OTNXVk9xZHJPZTlSMlQ4\nZnZUTXdjUGZuN3NoSEFSSko1aU5aQUEKHcuI2+9q7DsDwRn7mfwcSyC7AixzCC0e\nhqnGaW0HxmtLeOFuSPLdFMhhockCYGEV/907i/X6EImepWC4cf3bqA==\n-----END AGE ENCRYPTED FILE-----\n" | ||
| 16 | } | ||
| 17 | ], | ||
| 18 | "lastmodified": "2025-01-03T15:31:40Z", | ||
| 19 | "mac": "ENC[AES256_GCM,data:soDFDk35A1ULzOosZNrbhvtG3NPJDpAtLP3xrDtCBxgSGQ0lWrQ0o3MaKaJoDXQv7g/vYghmSwjH+0In0Ib3OWg0WLAlhwTEsiAn1o4JNRu/wF5aqvazOiDzFu7cyWil4Lsphy5eZgtc4IUp75SlCQc71xlNLoxudPpdcSxNLWg=,iv:jBYUZbiWM5gxFA+ZdpxpZIkz3WfgFi59tXFp242/qr8=,tag:H7ihAryZ8be+BbaqXFhRRg==,type:str]", | ||
| 20 | "pgp": null, | ||
| 21 | "unencrypted_suffix": "_unencrypted", | ||
| 22 | "version": "3.9.2" | ||
| 23 | } | ||
| 24 | } \ No newline at end of file | ||
diff --git a/hosts/surtr/tls/tsig_keys/kimai.yggdrasil.li b/hosts/surtr/tls/tsig_keys/kimai.yggdrasil.li new file mode 100644 index 00000000..b9199975 --- /dev/null +++ b/hosts/surtr/tls/tsig_keys/kimai.yggdrasil.li | |||
| @@ -0,0 +1,19 @@ | |||
| 1 | { | ||
| 2 | "data": "ENC[AES256_GCM,data:ATcU3Ix7o5d/49rD5H8je1ozTjoghrloMh5DIZ5WE3oYauUAknpGfr9xq92V,iv:vy9YK5Ot7CCjMtgAGVeAUQuaSw4F5kmmZ0GJYV9kCdQ=,tag:F/MXTUM2AI1fGXa9Ewn8yQ==,type:str]", | ||
| 3 | "sops": { | ||
| 4 | "age": [ | ||
| 5 | { | ||
| 6 | "recipient": "age1rmmhetcmllq0ahl5qznlr0eya2zdxwl9h6y5wnl97d2wtyx5t99sm2u866", | ||
| 7 | "enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBDMEF0cUdydERYVzJCa3pW\nTlo0NUFON0d5RGJFVnVTNVg3cjNEUERQMEdFClEvQW5odlNEd2F1VTFmMWQrL2RB\ncllFZVpIVVJrNTJsSGF4UEdZMnVmQzAKLS0tIFUrQkkzRVZiOFNiTnFCT1pEYVRM\nQm8wV1JkQ3RrR1dkL0FsNkhsY2kxa1kKGnAo/6oibgXexUU31THdLu6X+pRtrkjD\nZnXGPZ2xaESDVUVEYQPVpNrjt9brZGJBI1BasrkEwHAXMbJC236yYQ==\n-----END AGE ENCRYPTED FILE-----\n" | ||
| 8 | }, | ||
| 9 | { | ||
| 10 | "recipient": "age19a7j77w267z04zls7m28a8hj4a0g5af6ltye2d5wypg33c3l89csd4r9zq", | ||
| 11 | "enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSB3MGs1Z2ZqK2pqWHdVYTJH\naTlncHdPa3Zld0JhQW5Ccmc1SStWSnlDR0JrCmpML2d4TGdldUdoZCtaWVpPZVl0\nVm4waWVBS1orRS90ZS96N0Y2M29LY0UKLS0tIEI1Z2VVbVVxRUpOZEN4NnBRRklC\nQXloelZCb04xbmduTlVuL005TlRGMHMKfLB6zA3sj3HgDBC7VGfGVB6I1zJpt0PV\nkCV2yADgvAA2pT9HPg9IWAEpTPysOBiuE2jPNtFvylZYwTDHoumFnQ==\n-----END AGE ENCRYPTED FILE-----\n" | ||
| 12 | } | ||
| 13 | ], | ||
| 14 | "lastmodified": "2025-05-24T09:42:23Z", | ||
| 15 | "mac": "ENC[AES256_GCM,data:0pk1LpWPmX9td/TwJFxwWp5pTDyW78UtHXMDah+V9Tmgi8hH7ONdysgjwpDwS/c4zGnMA3qtobEL286U3//CTXt2qVsiUGLsnngzs2E6yBg8oGMYlGrch4M355Fl5ZxYsc8QLA6qWcuZ4H3QW8PnoqdJixcHoYLoxG01dzh4Bc0=,iv:zchk4enI1D80BkJLji5RLm7OTk3GeF8nYHuwqBxCXIM=,tag:bgkknPMqkSidi6bDFfv6UQ==,type:str]", | ||
| 16 | "unencrypted_suffix": "_unencrypted", | ||
| 17 | "version": "3.10.2" | ||
| 18 | } | ||
| 19 | } | ||
diff --git a/hosts/surtr/tls/tsig_keys/paperless.yggdrasil.li b/hosts/surtr/tls/tsig_keys/paperless.yggdrasil.li new file mode 100644 index 00000000..b1029931 --- /dev/null +++ b/hosts/surtr/tls/tsig_keys/paperless.yggdrasil.li | |||
| @@ -0,0 +1,24 @@ | |||
| 1 | { | ||
| 2 | "data": "ENC[AES256_GCM,data:D9l0pklD2KDZ4/TXHtXg00MmCnjCVVBG0AK9j5OxxBCyYseCTckp2P/iPOng,iv:DjvuKWPr/jldfk0eZ5+jWHN0RurdruR4Md7AMAPzRQg=,tag:h0c4m3hpATzzb6a7DVmi9w==,type:str]", | ||
| 3 | "sops": { | ||
| 4 | "kms": null, | ||
| 5 | "gcp_kms": null, | ||
| 6 | "azure_kv": null, | ||
| 7 | "hc_vault": null, | ||
| 8 | "age": [ | ||
| 9 | { | ||
| 10 | "recipient": "age1rmmhetcmllq0ahl5qznlr0eya2zdxwl9h6y5wnl97d2wtyx5t99sm2u866", | ||
| 11 | "enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSA3S05aS0ZEcVU2T3BIOG13\nSDJLTUp4OG9ZYVluK2gxbUJDSFRaQ0xnS0YwClFkKzByanJGOWFwbTlISndyU0Rx\nN1FJb3FVaUZOVDBsWEdHTVNGaGNtMVkKLS0tIDI1RmxaMlhVd1FPL1dNdlRGK0Nq\nMFpJZTNnWncrbkV5YS82ZnhGRld0UG8KIuf7bC7GVxaGeR7gwC7kGu/wtBppjq4H\nyDT05CYJf9/EE3K5aJpIOlxyqowRs2SINvIVkyd5ggYkkxCctmGXjQ==\n-----END AGE ENCRYPTED FILE-----\n" | ||
| 12 | }, | ||
| 13 | { | ||
| 14 | "recipient": "age19a7j77w267z04zls7m28a8hj4a0g5af6ltye2d5wypg33c3l89csd4r9zq", | ||
| 15 | "enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBUV3ZqTVJkNWdHeTlKK3Vl\nVTdrdzE2Z3YzVmZxelNFMDNMTUJneHNtNzFRCmQxTU84ekV4bFVLajU0ajB6ZzZK\neEpuQTcyS1o4MW9xTU5nMXVUR0gxTmcKLS0tIGVzZC8xYTc1VkU2RzA2NFQ1K2xz\nLzhPVjBUcytWNGRsdnFob3A4aWljelkKFMlmigcEVzelcEiv6WGya1dsIOJYr7YT\naBHgMttV7zzYHLqvIVJSCz+uw2FqDyqN46twmzFC0HSHeiKbvRrHVw==\n-----END AGE ENCRYPTED FILE-----\n" | ||
| 16 | } | ||
| 17 | ], | ||
| 18 | "lastmodified": "2025-02-13T19:23:42Z", | ||
| 19 | "mac": "ENC[AES256_GCM,data:0Fcgq0pOZtBBSiK8pUr/jadXMdtbZYFhUbSe+7DQpB8Fo2r8cEoT+Cpcy7tu+l9eXUiDk/tXTBJyMXaW4XWwS/Fe6Zcb95UYaYR1Y6OM9JVPYmwd6QSeC13MwzhYaCDlBiWWq69Zn8grEg7npWo/LS9LK7IEbN7EI8o7QYDI6cw=,iv:C+8ZmVTNWySQ+/6j+YirSwZzoMqXRlgstk47Efxmqps=,tag:Be/6Ve6M/Dcm/6QrbF+JTw==,type:str]", | ||
| 20 | "pgp": null, | ||
| 21 | "unencrypted_suffix": "_unencrypted", | ||
| 22 | "version": "3.9.4" | ||
| 23 | } | ||
| 24 | } \ No newline at end of file | ||
diff --git a/hosts/surtr/vpn/default.nix b/hosts/surtr/vpn/default.nix index 1bdcf74e..92223144 100644 --- a/hosts/surtr/vpn/default.nix +++ b/hosts/surtr/vpn/default.nix | |||
| @@ -1,4 +1,4 @@ | |||
| 1 | { pkgs, config, lib, ... }: | 1 | { flake, pkgs, config, lib, ... }: |
| 2 | 2 | ||
| 3 | with lib; | 3 | with lib; |
| 4 | 4 | ||
| @@ -22,7 +22,11 @@ in { | |||
| 22 | "--load-credential=surtr.priv:/run/credentials/container@vpn.service/surtr.priv" | 22 | "--load-credential=surtr.priv:/run/credentials/container@vpn.service/surtr.priv" |
| 23 | "--network-ipvlan=ens3:upstream" | 23 | "--network-ipvlan=ens3:upstream" |
| 24 | ]; | 24 | ]; |
| 25 | config = { | 25 | config = let hostConfig = config; in { config, pkgs, ... }: { |
| 26 | system.stateVersion = lib.mkIf hostConfig.containers."vpn".ephemeral config.system.nixos.release; | ||
| 27 | system.configurationRevision = mkIf (flake ? rev) flake.rev; | ||
| 28 | nixpkgs.pkgs = hostConfig.nixpkgs.pkgs; | ||
| 29 | |||
| 26 | boot.kernel.sysctl = { | 30 | boot.kernel.sysctl = { |
| 27 | "net.core.rmem_max" = 4194304; | 31 | "net.core.rmem_max" = 4194304; |
| 28 | "net.core.wmem_max" = 4194304; | 32 | "net.core.wmem_max" = 4194304; |
diff --git a/hosts/surtr/vpn/geri.pub b/hosts/surtr/vpn/geri.pub index ed5de2b2..2cd9b24e 100644 --- a/hosts/surtr/vpn/geri.pub +++ b/hosts/surtr/vpn/geri.pub | |||
| @@ -1 +1 @@ | |||
| sYuQSNZHzfegv8HRz71jnZm2nFLGeRnaGwVonhKUj2k= | hhER05bvstOTGfiAG3IJsFkBNWCUZHokBXwaiC5d534= | ||
diff --git a/hosts/surtr/zfs.nix b/hosts/surtr/zfs.nix index 17c5cd32..3795956d 100644 --- a/hosts/surtr/zfs.nix +++ b/hosts/surtr/zfs.nix | |||
| @@ -49,7 +49,7 @@ | |||
| 49 | 49 | ||
| 50 | boot.postBootCommands = '' | 50 | boot.postBootCommands = '' |
| 51 | echo "=== STARTING ZPOOL IMPORT ===" | 51 | echo "=== STARTING ZPOOL IMPORT ===" |
| 52 | ${pkgs.zfs}/bin/zpool import -a -N -d /dev | 52 | ${pkgs.zfs}/bin/zpool import -a -f -N -d /dev |
| 53 | ${pkgs.zfs}/bin/zpool status | 53 | ${pkgs.zfs}/bin/zpool status |
| 54 | ${pkgs.zfs}/bin/zfs mount -a | 54 | ${pkgs.zfs}/bin/zfs mount -a |
| 55 | echo "=== ZPOOL IMPORT COMPLETE ===" | 55 | echo "=== ZPOOL IMPORT COMPLETE ===" |
diff --git a/hosts/vidhar/audiobookshelf/abs-podcast-autoplaylist-gkleen.toml b/hosts/vidhar/audiobookshelf/abs-podcast-autoplaylist-gkleen.toml new file mode 100644 index 00000000..42920069 --- /dev/null +++ b/hosts/vidhar/audiobookshelf/abs-podcast-autoplaylist-gkleen.toml | |||
| @@ -0,0 +1,19 @@ | |||
| 1 | { | ||
| 2 | "data": "ENC[AES256_GCM,data:7STcG1J3zLHzlHi2LZgSa+pmKlYU6X1eLvjXcx7gvCuHFkXwa/ldrHdzaBXAMrQ+C+DWM4+cyhdal3ypxXueBuBEvH3P7/KofPP2A519sdZo1vDkXCzYRD3Ow74M+hX5ej6hL1mv21HayrRMPnYfH8HUWk1kd/y69EjpjvDHyCpQuw1WG5M4FDUaTd4Vh51QRCSG/Is29dEkvQowhIvNqnAR4KW9PpfjlbUPHauZ6PQLnlJmI9QCcAGJ8hHtp5T+xctTym82GAlXugTJpHnkqcxnGteu9H7UuiDMQ3aKKGYzZCpWkNHrbD1IRhzPzSjVlN2nIX3Ydp8ENNf61EGOrp5hzmV0MhwRHKfz5rE1FRVuyHcwhwBdJzfhzrL9zXZYzn9Zuf9KWwsF+1+58i9u9hPym63r4c1+RbMjNKzHc1/yhhsRDCrTwvMd/Hc6KfUi3H1wW/r02Va2bOCkYrOkWIQpBdx4ThMgkTaHr94DBMqT8n3Fzhc7+uaUsl6I9gMwTn8QCV3g4U7Mp3+/gnQLOdsCTgKr69x1iPfp7jzzbC29MWhIk+2aig+iWRMVT0n4AWIxooc3cYnfOJNtCdJwKuPUl4xNlZ22NK3XQfxBURSw0pi5KyDO1wgTVRoRJMnXZyin3bZ10OU8QKBqLp22FXpG/+mHrm3Y6yLkqfIP5ry+b86Rb7nWJlxDTwTGSoGi8sJzwNb+H9ioG7LjaSmvr7V+2aSq0+72WopAkIFSBq/yIWX3J/rNZuia6jKMY/8bSLDJz3V/YuzeUJ5feUkOS4KW1J1UkaYd6XxZ9Y8szCS/B7Ocz0YiV8fHmOzZKRn8BTmmXUIoyxO07MmwQ1shYfiVCwXjdjq2f3/CZbXNVOPvhGBYOcutUztftu3H9DRDTKrVae1TnztCVSkBSk1o52uSj0r8t6f4l1r7UG1TviOnVLVh1/6iiJVQey0m8G6ugw22zxfhI0Uea/rB70i+2UuoxrmsOfg+TgnvKBuakEBifp4rx0S5wyz05RYXkBLJTIwi2fVGmulqjZuCQNQnI3cfPI+oGcykob9IQqdo/Dwd118hNPVAwdDHyaiGXGh8eu8N2OCLQxlGZDtkVUveaEasJaZ0WWWaK97FUeoNJfUnB7Y3lNjv5u2rzviZyk4=,iv:jT21FNnHod6btDlBa3UflK3au5VmcsABs5OTMXF6oFA=,tag:Oh8cOL+edT5Wp0I1L5+vwg==,type:str]", | ||
| 3 | "sops": { | ||
| 4 | "age": [ | ||
| 5 | { | ||
| 6 | "recipient": "age1rmmhetcmllq0ahl5qznlr0eya2zdxwl9h6y5wnl97d2wtyx5t99sm2u866", | ||
| 7 | "enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSA0aE1XNUNCM1Q5V0d3R2JG\nbjJZTmdvQ21JbmtyR0ZmODFMdVBGejRoam1vCjMzMGdTb3BReDVCa2JJU0JrSHFP\ndTdicU5TRjIrTWpteDMzeGtDT0xaelkKLS0tIFhaSlFrbzFDUjRZV0lGR0cydVdZ\nY2xma0VSVXlTM1JucFJUSys4dlRvdEUK9gQNQEdKDDf1ikWzd6uTlE50WsfO/EB0\nGH2Ono6oNWbKWTyl/wRO8NzXx0nudwqq66s0oBLIdTMQOpIBBNI0XQ==\n-----END AGE ENCRYPTED FILE-----\n" | ||
| 8 | }, | ||
| 9 | { | ||
| 10 | "recipient": "age1qffdqvy9arld9zd5a5cylt0n98xhcns5shxhrhwjq5g4qa844ejselaa4l", | ||
| 11 | "enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBiRWFqSHNlY1IvMkkwaEto\ncHZHa2p1Y25SakFkS2JYMlRFcFhnZGY1dVRFCkxSWmxvcHZMampQKzdKRHI0ZVMx\nUTFtR0pHbzFaQ0xQUFA2ZERDSWpwS0UKLS0tIFBaSGczY3VWdy9TKzRDZWZ2SElY\nbVQ4dDNhQllmVmViWGs5c3V4TmNscjQKeugevQJFAN/8JrzeAm4hm2JsQGb26BCb\n3dKYnN1kJU7oVHr1aVfXwMpELNYt9poX6WTY2h9lsdHuRlqoFXAA5Q==\n-----END AGE ENCRYPTED FILE-----\n" | ||
| 12 | } | ||
| 13 | ], | ||
| 14 | "lastmodified": "2025-08-11T07:08:36Z", | ||
| 15 | "mac": "ENC[AES256_GCM,data:ZL/dOz+NC8sr8vPBsux+gFOWxUhQqMSmG1az7udhB0ckmOXtnrPBzMM1gs+5pwXLvfLux0m4xzT87+o87axIECnCq35FSuMjtEBK24OUJXsLG/q/tDv5dfRBy/976dM5W7YkBVX/uc03p8CLKf5w4XYNeRKnSwjLvWGd9runDOU=,iv:9ZIeJ5aDVVPHi3/oHqWkWtEfeivV/nFFyQ1lJWJwMu8=,tag:TfkHaopMa+Z0zk38A6/NTA==,type:str]", | ||
| 16 | "unencrypted_suffix": "_unencrypted", | ||
| 17 | "version": "3.10.2" | ||
| 18 | } | ||
| 19 | } | ||
diff --git a/hosts/vidhar/audiobookshelf/default.nix b/hosts/vidhar/audiobookshelf/default.nix new file mode 100644 index 00000000..136bcaff --- /dev/null +++ b/hosts/vidhar/audiobookshelf/default.nix | |||
| @@ -0,0 +1,21 @@ | |||
| 1 | { config, pkgs, lib, ... }: | ||
| 2 | |||
| 3 | { | ||
| 4 | config = { | ||
| 5 | services.audiobookshelf = { | ||
| 6 | enable = true; | ||
| 7 | host = "2a03:4000:52:ada:4:1::"; | ||
| 8 | port = 28982; | ||
| 9 | }; | ||
| 10 | |||
| 11 | users.groups.audiobookshelf.members = [ "gkleen" ]; | ||
| 12 | |||
| 13 | services.abs-podcast-autoplaylist = { | ||
| 14 | gkleen = {}; | ||
| 15 | }; | ||
| 16 | sops.secrets.${config.services.abs-podcast-autoplaylist.gkleen.configSecret} = { | ||
| 17 | format = "binary"; | ||
| 18 | sopsFile = ./abs-podcast-autoplaylist-gkleen.toml; | ||
| 19 | }; | ||
| 20 | }; | ||
| 21 | } | ||
diff --git a/hosts/vidhar/default.nix b/hosts/vidhar/default.nix index 42a9e80d..1c60ed22 100644 --- a/hosts/vidhar/default.nix +++ b/hosts/vidhar/default.nix | |||
| @@ -4,7 +4,7 @@ with lib; | |||
| 4 | 4 | ||
| 5 | { | 5 | { |
| 6 | imports = with flake.nixosModules.systemProfiles; [ | 6 | imports = with flake.nixosModules.systemProfiles; [ |
| 7 | ./zfs.nix ./network ./samba.nix ./dns ./prometheus ./borg ./pgbackrest | 7 | ./zfs.nix ./network ./samba.nix ./dns ./prometheus ./borg ./pgbackrest ./postgresql.nix ./immich.nix ./paperless ./hledger ./audiobookshelf ./kimai |
| 8 | tmpfs-root zfs | 8 | tmpfs-root zfs |
| 9 | initrd-all-crypto-modules default-locale openssh rebuild-machines | 9 | initrd-all-crypto-modules default-locale openssh rebuild-machines |
| 10 | build-server | 10 | build-server |
| @@ -136,7 +136,7 @@ with lib; | |||
| 136 | wantedBy = ["basic.target"]; | 136 | wantedBy = ["basic.target"]; |
| 137 | serviceConfig = { | 137 | serviceConfig = { |
| 138 | ExecStart = pkgs.writeShellScript "limit-pstate-start" '' | 138 | ExecStart = pkgs.writeShellScript "limit-pstate-start" '' |
| 139 | echo 60 > /sys/devices/system/cpu/intel_pstate/max_perf_pct | 139 | echo 40 > /sys/devices/system/cpu/intel_pstate/max_perf_pct |
| 140 | ''; | 140 | ''; |
| 141 | RemainAfterExit = true; | 141 | RemainAfterExit = true; |
| 142 | ExecStop = pkgs.writeShellScript "limit-pstate-stop" '' | 142 | ExecStop = pkgs.writeShellScript "limit-pstate-stop" '' |
| @@ -157,8 +157,6 @@ with lib; | |||
| 157 | recommendedProxySettings = true; | 157 | recommendedProxySettings = true; |
| 158 | recommendedTlsSettings = true; | 158 | recommendedTlsSettings = true; |
| 159 | commonHttpConfig = '' | 159 | commonHttpConfig = '' |
| 160 | ssl_ecdh_curve X25519:prime256v1:secp521r1:secp384r1; | ||
| 161 | |||
| 162 | log_format main | 160 | log_format main |
| 163 | '$remote_addr "$remote_user" ' | 161 | '$remote_addr "$remote_user" ' |
| 164 | '"$host" "$request" $status $bytes_sent ' | 162 | '"$host" "$request" $status $bytes_sent ' |
diff --git a/hosts/vidhar/hledger/default.nix b/hosts/vidhar/hledger/default.nix new file mode 100644 index 00000000..ae080f66 --- /dev/null +++ b/hosts/vidhar/hledger/default.nix | |||
| @@ -0,0 +1,83 @@ | |||
| 1 | { config, lib, pkgs, ... }: | ||
| 2 | { | ||
| 3 | config = { | ||
| 4 | services.hledger-web = { | ||
| 5 | enable = true; | ||
| 6 | allow = "view"; | ||
| 7 | stateDir = "/var/lib/hledger"; | ||
| 8 | journalFiles = lib.mkForce ["web.journal"]; | ||
| 9 | baseUrl = "https://hledger.yggdrasil.li"; | ||
| 10 | extraOptions = [ | ||
| 11 | "--socket=/run/hledger-web/http.sock" | ||
| 12 | ]; | ||
| 13 | }; | ||
| 14 | users = { | ||
| 15 | users.hledger.uid = 982; | ||
| 16 | groups.hledger.gid = 979; | ||
| 17 | }; | ||
| 18 | systemd.services.hledger-web = { | ||
| 19 | serviceConfig = { | ||
| 20 | UMask = "0002"; | ||
| 21 | ReadOnlyPaths = [ config.services.hledger-web.stateDir ]; | ||
| 22 | RuntimeDirectory = [ "hledger-web" ]; | ||
| 23 | PrivateDevices = true; | ||
| 24 | StateDirectory = "hledger"; | ||
| 25 | CapabilityBoundingSet = ""; | ||
| 26 | AmbientCapabilities = ""; | ||
| 27 | ProtectSystem = "strict"; | ||
| 28 | ProtectKernelTunables = true; | ||
| 29 | ProtectKernelModules = true; | ||
| 30 | ProtectControlGroups = true; | ||
| 31 | ProtectClock = true; | ||
| 32 | ProtectHostname = true; | ||
| 33 | ProtectHome = "tmpfs"; | ||
| 34 | ProtectKernelLogs = true; | ||
| 35 | ProtectProc = "invisible"; | ||
| 36 | ProcSubset = "pid"; | ||
| 37 | PrivateNetwork = false; | ||
| 38 | RestrictAddressFamilies = "AF_INET AF_INET6 AF_UNIX"; | ||
| 39 | SystemCallArchitectures = "native"; | ||
| 40 | SystemCallFilter = [ | ||
| 41 | "@system-service @resources" | ||
| 42 | "~@obsolete @privileged" | ||
| 43 | ]; | ||
| 44 | RestrictSUIDSGID = true; | ||
| 45 | RemoveIPC = true; | ||
| 46 | NoNewPrivileges = true; | ||
| 47 | RestrictRealtime = true; | ||
| 48 | RestrictNamespaces = true; | ||
| 49 | LockPersonality = true; | ||
| 50 | PrivateUsers = true; | ||
| 51 | TemporaryFileSystem = [ "/var/lib/hledger/.cache:mode=0750,uid=${toString (config.users.users.hledger.uid)},gid=${toString (config.users.groups.hledger.gid)}" ]; | ||
| 52 | }; | ||
| 53 | }; | ||
| 54 | services.nginx = { | ||
| 55 | upstreams.hledger = { | ||
| 56 | servers = { "unix:/run/hledger-web/http.sock" = {}; }; | ||
| 57 | }; | ||
| 58 | virtualHosts."hledger.yggdrasil.li" = { | ||
| 59 | listen = [ | ||
| 60 | { addr = "[2a03:4000:52:ada:4:1::]"; port = 5000; } | ||
| 61 | ]; | ||
| 62 | extraConfig = '' | ||
| 63 | set_real_ip_from 2a03:4000:52:ada:4::; | ||
| 64 | auth_basic "hledger"; | ||
| 65 | auth_basic_user_file "/run/credentials/nginx.service/hledger_users"; | ||
| 66 | ''; | ||
| 67 | locations."/" = { | ||
| 68 | proxyPass = "http://hledger/"; | ||
| 69 | proxyWebsockets = true; | ||
| 70 | }; | ||
| 71 | }; | ||
| 72 | }; | ||
| 73 | systemd.services.nginx.serviceConfig = { | ||
| 74 | SupplementaryGroups = [ "hledger" ]; | ||
| 75 | LoadCredential = [ "hledger_users:${config.sops.secrets."hledger_users".path}" ]; | ||
| 76 | }; | ||
| 77 | sops.secrets."hledger_users" = { | ||
| 78 | format = "binary"; | ||
| 79 | sopsFile = ./htpasswd; | ||
| 80 | reloadUnits = [ "nginx.service" ]; | ||
| 81 | }; | ||
| 82 | }; | ||
| 83 | } | ||
diff --git a/hosts/vidhar/hledger/htpasswd b/hosts/vidhar/hledger/htpasswd new file mode 100644 index 00000000..016cb525 --- /dev/null +++ b/hosts/vidhar/hledger/htpasswd | |||
| @@ -0,0 +1,24 @@ | |||
| 1 | { | ||
| 2 | "data": "ENC[AES256_GCM,data:9MNDIAc7ePYk3xQDorX2pU8ybJkJb33RKiJxc2DYauXFNQYxtGwCYhZwod7p7fPh3KqZxBNMRoZXr+/RnV+trsqjAcOOjnXTWLbX6nubq/xm+q0BxEjOPn7FvJF9XOblBeupldo+byGh2CMH9qQv5Fov,iv:3Tym+Mfr48OJet3qDFZPg0XjYr4sNQdNdiu0vUxmzbY=,tag:E0sxRY/jeMVlqH6uAYvD/Q==,type:str]", | ||
| 3 | "sops": { | ||
| 4 | "kms": null, | ||
| 5 | "gcp_kms": null, | ||
| 6 | "azure_kv": null, | ||
| 7 | "hc_vault": null, | ||
| 8 | "age": [ | ||
| 9 | { | ||
| 10 | "recipient": "age1rmmhetcmllq0ahl5qznlr0eya2zdxwl9h6y5wnl97d2wtyx5t99sm2u866", | ||
| 11 | "enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSA3eFBsOEM2ZUNVT2V3LytC\nTUJvUDdKc0VzMyt2cDFKYU03djBjZVFpeVY4CjByMXhPVXRJVjhKQWZvQ2xuOTE3\ncXdJV1lZaHR3cVl0Z0hQaG00M2dGbjQKLS0tIEIzenVxb3cwM3pXTUl1YUZlSlk2\nbDc3VmE5NkEyZ2tRd01OUGZibmhtUlEKxdesIdvzm8s0SmXU5R+tSbmS5Dj24jrb\nEiMERYy1g8GyHR3d2/mU5iOIdsBegSZReUVzomaMT9L7/TmubgOP3g==\n-----END AGE ENCRYPTED FILE-----\n" | ||
| 12 | }, | ||
| 13 | { | ||
| 14 | "recipient": "age1qffdqvy9arld9zd5a5cylt0n98xhcns5shxhrhwjq5g4qa844ejselaa4l", | ||
| 15 | "enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBPa2RDZzR6cEFYTFA1QkND\nbndVeHVrMVJ0MWZvRmw5VXRhOHlRYllIRWxRCjU4dks4R25LS1RZMHFnbmpQRVZz\nNXhubkJvZFc2amRwMDVtQlE0NnBKNzQKLS0tIHRyeDUxTEFPMEMzWUVkZURzODdm\nSHdqbUpvNmFTS1QveFRpRHdnWHpHb28KnvdUkMkKGiBVHQD7Yv7n6WZjihCGJAR2\nMKl2WAn4g4jzgcXPwwIAIjUrMGSIdGpwCTUDcDnlKWAbRYO2B6P17A==\n-----END AGE ENCRYPTED FILE-----\n" | ||
| 16 | } | ||
| 17 | ], | ||
| 18 | "lastmodified": "2025-02-19T17:11:17Z", | ||
| 19 | "mac": "ENC[AES256_GCM,data:yBIEqHhr4igoMlRcgg2SigKfejqeuNmuleYolsLJo+QOaW4BHITJTvLxRV1JHPpcMVQkF//zx4ZfUUrb8tTN0znGu3Jnpd0JVagbfCVyEuT6d1SB/GzyUVvoQ2GlcA9us+5gjI4oEJTQCfVqnLDBWsw+jXdr3nEIWo6Mvbqo3lI=,iv:I6Swk4wyd+96+tJKRY/FHlS7ZShMDROcbl+l+ZLRxhM=,tag:P1uQvB4NLdkPEKRMI6lLxw==,type:str]", | ||
| 20 | "pgp": null, | ||
| 21 | "unencrypted_suffix": "_unencrypted", | ||
| 22 | "version": "3.9.4" | ||
| 23 | } | ||
| 24 | } \ No newline at end of file | ||
diff --git a/hosts/vidhar/immich.nix b/hosts/vidhar/immich.nix new file mode 100644 index 00000000..a1f145a8 --- /dev/null +++ b/hosts/vidhar/immich.nix | |||
| @@ -0,0 +1,10 @@ | |||
| 1 | { ... }: | ||
| 2 | |||
| 3 | { | ||
| 4 | config = { | ||
| 5 | services.immich = { | ||
| 6 | enable = true; | ||
| 7 | host = "2a03:4000:52:ada:4:1::"; | ||
| 8 | }; | ||
| 9 | }; | ||
| 10 | } | ||
diff --git a/hosts/vidhar/kimai/default.nix b/hosts/vidhar/kimai/default.nix new file mode 100644 index 00000000..0258697b --- /dev/null +++ b/hosts/vidhar/kimai/default.nix | |||
| @@ -0,0 +1,89 @@ | |||
| 1 | { flake, config, ... }: | ||
| 2 | |||
| 3 | { | ||
| 4 | config = { | ||
| 5 | boot.enableContainers = true; | ||
| 6 | boot.kernel.sysctl = { | ||
| 7 | "net.netfilter.nf_log_all_netns" = true; | ||
| 8 | }; | ||
| 9 | |||
| 10 | containers."kimai" = { | ||
| 11 | autoStart = true; | ||
| 12 | ephemeral = true; | ||
| 13 | bindMounts = { | ||
| 14 | "/var/lib/kimai" = { | ||
| 15 | hostPath = "/var/lib/kimai/state"; | ||
| 16 | isReadOnly = false; | ||
| 17 | }; | ||
| 18 | "/var/lib/mysql" = { | ||
| 19 | hostPath = "/var/lib/kimai/mysql"; | ||
| 20 | isReadOnly = false; | ||
| 21 | }; | ||
| 22 | }; | ||
| 23 | privateNetwork = true; | ||
| 24 | # forwardPorts = [ | ||
| 25 | # { containerPort = 80; | ||
| 26 | # hostPort = 28983; | ||
| 27 | # } | ||
| 28 | # ]; | ||
| 29 | hostAddress = "192.168.52.113"; | ||
| 30 | localAddress = "192.168.52.114"; | ||
| 31 | hostAddress6 = "2a03:4000:52:ada:6::1"; | ||
| 32 | localAddress6 = "2a03:4000:52:ada:6::2"; | ||
| 33 | config = let hostConfig = config; in { config, pkgs, lib, ... }: { | ||
| 34 | system.stateVersion = lib.mkIf hostConfig.containers."kimai".ephemeral config.system.nixos.release; | ||
| 35 | system.configurationRevision = lib.mkIf (flake ? rev) flake.rev; | ||
| 36 | nixpkgs.pkgs = hostConfig.nixpkgs.pkgs; | ||
| 37 | |||
| 38 | services.kimai.sites."kimai.yggdrasil.li" = { | ||
| 39 | database.socket = "/run/mysqld/mysqld.sock"; | ||
| 40 | }; | ||
| 41 | |||
| 42 | networking = { | ||
| 43 | useDHCP = false; | ||
| 44 | useNetworkd = true; | ||
| 45 | useHostResolvConf = false; | ||
| 46 | firewall.enable = false; | ||
| 47 | nftables = { | ||
| 48 | enable = true; | ||
| 49 | rulesetFile = ./ruleset.nft; | ||
| 50 | }; | ||
| 51 | }; | ||
| 52 | |||
| 53 | services.resolved.fallbackDns = [ | ||
| 54 | "9.9.9.10#dns10.quad9.net" | ||
| 55 | "149.112.112.10#dns10.quad9.net" | ||
| 56 | "2620:fe::10#dns10.quad9.net" | ||
| 57 | "2620:fe::fe:10#dns10.quad9.net" | ||
| 58 | ]; | ||
| 59 | |||
| 60 | systemd.network = { | ||
| 61 | networks.upstream = { | ||
| 62 | name = "eth0"; | ||
| 63 | matchConfig = { | ||
| 64 | Name = "eth0"; | ||
| 65 | }; | ||
| 66 | linkConfig = { | ||
| 67 | RequiredForOnline = true; | ||
| 68 | }; | ||
| 69 | networkConfig = { | ||
| 70 | Address = [ "192.168.52.114/32" "2a03:4000:52:ada:6::2/128" ]; | ||
| 71 | LLMNR = false; | ||
| 72 | MulticastDNS = false; | ||
| 73 | }; | ||
| 74 | routes = [ | ||
| 75 | { Destination = "192.168.52.113/32"; } | ||
| 76 | { Destination = "2a03:4000:52:ada:6::1/128"; } | ||
| 77 | { Destination = "0.0.0.0/0"; | ||
| 78 | Gateway = "192.168.52.113"; | ||
| 79 | } | ||
| 80 | { Destination = "::/0"; | ||
| 81 | Gateway = "2a03:4000:52:ada:6::1"; | ||
| 82 | } | ||
| 83 | ]; | ||
| 84 | }; | ||
| 85 | }; | ||
| 86 | }; | ||
| 87 | }; | ||
| 88 | }; | ||
| 89 | } | ||
diff --git a/hosts/vidhar/kimai/ruleset.nft b/hosts/vidhar/kimai/ruleset.nft new file mode 100644 index 00000000..ad4db6d5 --- /dev/null +++ b/hosts/vidhar/kimai/ruleset.nft | |||
| @@ -0,0 +1,149 @@ | |||
| 1 | define icmp_protos = {ipv6-icmp, icmp, igmp} | ||
| 2 | |||
| 3 | table arp filter { | ||
| 4 | limit lim_arp { | ||
| 5 | rate over 50 mbytes/second burst 50 mbytes | ||
| 6 | } | ||
| 7 | |||
| 8 | counter arp-rx {} | ||
| 9 | counter arp-tx {} | ||
| 10 | |||
| 11 | counter arp-ratelimit-rx {} | ||
| 12 | counter arp-ratelimit-tx {} | ||
| 13 | |||
| 14 | chain input { | ||
| 15 | type filter hook input priority filter | ||
| 16 | policy accept | ||
| 17 | |||
| 18 | limit name lim_arp counter name arp-ratelimit-rx drop | ||
| 19 | |||
| 20 | counter name arp-rx | ||
| 21 | } | ||
| 22 | |||
| 23 | chain output { | ||
| 24 | type filter hook output priority filter | ||
| 25 | policy accept | ||
| 26 | |||
| 27 | limit name lim_arp counter name arp-ratelimit-tx drop | ||
| 28 | |||
| 29 | counter name arp-tx | ||
| 30 | } | ||
| 31 | } | ||
| 32 | |||
| 33 | table inet filter { | ||
| 34 | limit lim_reject { | ||
| 35 | rate over 1000/second burst 1000 packets | ||
| 36 | } | ||
| 37 | |||
| 38 | limit lim_icmp { | ||
| 39 | rate over 50 mbytes/second burst 50 mbytes | ||
| 40 | } | ||
| 41 | |||
| 42 | counter invalid-fw {} | ||
| 43 | counter fw-lo {} | ||
| 44 | |||
| 45 | counter reject-ratelimit-fw {} | ||
| 46 | counter reject-fw {} | ||
| 47 | counter reject-tcp-fw {} | ||
| 48 | counter reject-icmp-fw {} | ||
| 49 | |||
| 50 | counter drop-fw {} | ||
| 51 | |||
| 52 | counter invalid-rx {} | ||
| 53 | |||
| 54 | counter rx-lo {} | ||
| 55 | counter invalid-local4-rx {} | ||
| 56 | counter invalid-local6-rx {} | ||
| 57 | |||
| 58 | counter icmp-ratelimit-rx {} | ||
| 59 | counter icmp-rx {} | ||
| 60 | |||
| 61 | counter kimai-rx {} | ||
| 62 | |||
| 63 | counter established-rx {} | ||
| 64 | |||
| 65 | counter reject-ratelimit-rx {} | ||
| 66 | counter reject-rx {} | ||
| 67 | counter reject-tcp-rx {} | ||
| 68 | counter reject-icmp-rx {} | ||
| 69 | |||
| 70 | counter drop-rx {} | ||
| 71 | |||
| 72 | counter tx-lo {} | ||
| 73 | |||
| 74 | counter icmp-ratelimit-tx {} | ||
| 75 | counter icmp-tx {} | ||
| 76 | |||
| 77 | counter kimai-tx {} | ||
| 78 | |||
| 79 | counter tx {} | ||
| 80 | |||
| 81 | chain forward { | ||
| 82 | type filter hook forward priority filter | ||
| 83 | policy drop | ||
| 84 | |||
| 85 | |||
| 86 | ct state invalid log level debug prefix "kimai: drop invalid forward: " counter name invalid-fw drop | ||
| 87 | |||
| 88 | |||
| 89 | iifname lo counter name fw-lo accept | ||
| 90 | |||
| 91 | |||
| 92 | limit name lim_reject log level debug prefix "kimai: drop forward: " counter name reject-ratelimit-fw drop | ||
| 93 | log level debug prefix "kimai: reject forward: " counter name reject-fw | ||
| 94 | meta l4proto tcp ct state new counter name reject-tcp-fw reject with tcp reset | ||
| 95 | ct state new counter name reject-icmp-fw reject | ||
| 96 | |||
| 97 | |||
| 98 | counter name drop-fw | ||
| 99 | } | ||
| 100 | |||
| 101 | chain input { | ||
| 102 | type filter hook input priority filter | ||
| 103 | policy drop | ||
| 104 | |||
| 105 | |||
| 106 | ct state invalid log level debug prefix "kimai: drop invalid input: " counter name invalid-rx drop | ||
| 107 | |||
| 108 | |||
| 109 | iifname lo counter name rx-lo accept | ||
| 110 | iif != lo ip daddr 127.0.0.1/8 counter name invalid-local4-rx reject | ||
| 111 | iif != lo ip6 daddr ::1/128 counter name invalid-local6-rx reject | ||
| 112 | |||
| 113 | |||
| 114 | meta l4proto $icmp_protos limit name lim_icmp counter name icmp-ratelimit-rx drop | ||
| 115 | meta l4proto $icmp_protos counter name icmp-rx accept | ||
| 116 | |||
| 117 | |||
| 118 | tcp dport 80 counter name kimai-rx accept | ||
| 119 | |||
| 120 | |||
| 121 | ct state { established, related } counter name established-rx accept | ||
| 122 | |||
| 123 | |||
| 124 | limit name lim_reject log level debug prefix "kimai: drop input: " counter name reject-ratelimit-rx drop | ||
| 125 | log level debug prefix "kimai: reject input: " counter name reject-rx | ||
| 126 | meta l4proto tcp ct state new counter name reject-tcp-rx reject with tcp reset | ||
| 127 | ct state new counter name reject-icmp-rx reject | ||
| 128 | |||
| 129 | |||
| 130 | counter name drop-rx | ||
| 131 | } | ||
| 132 | |||
| 133 | chain output { | ||
| 134 | type filter hook output priority filter | ||
| 135 | policy accept | ||
| 136 | |||
| 137 | |||
| 138 | oifname lo counter name tx-lo accept | ||
| 139 | |||
| 140 | meta l4proto $icmp_protos limit name lim_icmp counter name icmp-ratelimit-tx drop | ||
| 141 | meta l4proto $icmp_protos counter name icmp-tx accept | ||
| 142 | |||
| 143 | |||
| 144 | tcp sport 80 counter name kimai-tx | ||
| 145 | |||
| 146 | |||
| 147 | counter name tx | ||
| 148 | } | ||
| 149 | } | ||
diff --git a/hosts/vidhar/network/default.nix b/hosts/vidhar/network/default.nix index 0643f0bb..6fcef9d8 100644 --- a/hosts/vidhar/network/default.nix +++ b/hosts/vidhar/network/default.nix | |||
| @@ -1,9 +1,9 @@ | |||
| 1 | { pkgs, lib, ... }: | 1 | { pkgs, lib, config, ... }: |
| 2 | 2 | ||
| 3 | with lib; | 3 | with lib; |
| 4 | 4 | ||
| 5 | { | 5 | { |
| 6 | imports = [ ./gpon.nix ./bifrost ./dhcp ]; | 6 | imports = [ ./pppoe.nix ./bifrost ./dhcp ]; |
| 7 | 7 | ||
| 8 | config = { | 8 | config = { |
| 9 | networking = { | 9 | networking = { |
| @@ -61,7 +61,9 @@ with lib; | |||
| 61 | firewall.enable = false; | 61 | firewall.enable = false; |
| 62 | nftables = { | 62 | nftables = { |
| 63 | enable = true; | 63 | enable = true; |
| 64 | rulesetFile = ./ruleset.nft; | 64 | rulesetFile = pkgs.replaceVars ./ruleset.nft { |
| 65 | inherit (config.networking) pppInterface; | ||
| 66 | }; | ||
| 65 | }; | 67 | }; |
| 66 | 68 | ||
| 67 | resolvconf = { | 69 | resolvconf = { |
| @@ -103,7 +105,14 @@ with lib; | |||
| 103 | /srv/nfs/nix-store 10.141.0.0/24(ro,async,root_squash) 2a03:4000:52:ada:1::/80(ro,async,root_squash) | 105 | /srv/nfs/nix-store 10.141.0.0/24(ro,async,root_squash) 2a03:4000:52:ada:1::/80(ro,async,root_squash) |
| 104 | ''; | 106 | ''; |
| 105 | }; | 107 | }; |
| 106 | settings.nfsd.vers3 = false; | 108 | settings.nfsd = { |
| 109 | rdma = true; | ||
| 110 | vers3 = false; | ||
| 111 | vers4 = true; | ||
| 112 | "vers4.0" = false; | ||
| 113 | "vers4.1" = false; | ||
| 114 | "vers4.2" = true; | ||
| 115 | }; | ||
| 107 | }; | 116 | }; |
| 108 | 117 | ||
| 109 | fileSystems = { | 118 | fileSystems = { |
diff --git a/hosts/vidhar/network/dhcp/default.nix b/hosts/vidhar/network/dhcp/default.nix index 07a83351..11460393 100644 --- a/hosts/vidhar/network/dhcp/default.nix +++ b/hosts/vidhar/network/dhcp/default.nix | |||
| @@ -1,8 +1,33 @@ | |||
| 1 | { flake, config, pkgs, lib, ... }: | 1 | { flake, config, pkgs, lib, sources, ... }: |
| 2 | 2 | ||
| 3 | with lib; | 3 | with lib; |
| 4 | 4 | ||
| 5 | { | 5 | let |
| 6 | nfsrootBaseUrl = "http://nfsroot.vidhar.yggdrasil"; | ||
| 7 | tftpIp = "10.141.0.1"; | ||
| 8 | nfsIp = tftpIp; | ||
| 9 | ipxe = pkgs.ipxe.override { | ||
| 10 | additionalTargets = { | ||
| 11 | "bin-i386-efi/ipxe.efi" = "i386-ipxe.efi"; | ||
| 12 | }; | ||
| 13 | additionalOptions = [ | ||
| 14 | "NSLOOKUP_CMD" | ||
| 15 | "PING_CMD" | ||
| 16 | "CONSOLE_CMD" | ||
| 17 | ]; | ||
| 18 | embedScript = pkgs.writeText "yggdrasil.ipxe" '' | ||
| 19 | #!ipxe | ||
| 20 | |||
| 21 | cpair --background 9 1 | ||
| 22 | cpair --background 9 3 | ||
| 23 | cpair --background 9 6 | ||
| 24 | |||
| 25 | set user-class iPXE-yggdrasil | ||
| 26 | |||
| 27 | autoboot | ||
| 28 | ''; | ||
| 29 | }; | ||
| 30 | in { | ||
| 6 | config = { | 31 | config = { |
| 7 | services.kea = { | 32 | services.kea = { |
| 8 | dhcp4 = { | 33 | dhcp4 = { |
| @@ -23,41 +48,67 @@ with lib; | |||
| 23 | }; | 48 | }; |
| 24 | 49 | ||
| 25 | client-classes = [ | 50 | client-classes = [ |
| 26 | { name = "eostre-ipxe"; | 51 | { name = "ipxe-eostre"; |
| 27 | test = "hexstring(pkt4.mac, ':') == '00:d8:61:79:c5:40' and option[77].hex == 'iPXE'"; | 52 | test = "hexstring(pkt4.mac, ':') == '00:d8:61:79:c5:40' and option[77].hex == 'iPXE-yggdrasil'"; |
| 28 | next-server = "10.141.0.1"; | 53 | next-server = tftpIp; |
| 29 | boot-file-name = "http://nfsroot.vidhar.yggdrasil/eostre/netboot.ipxe"; | 54 | boot-file-name = "${nfsrootBaseUrl}/eostre.menu.ipxe"; |
| 55 | only-if-required = true; | ||
| 56 | } | ||
| 57 | { name = "ipxe-yggdrasil"; | ||
| 58 | test = "option[77].hex == 'iPXE-yggdrasil'"; | ||
| 59 | next-server = tftpIp; | ||
| 60 | boot-file-name = "${nfsrootBaseUrl}/installer-x86_64-linux.menu.ipxe"; | ||
| 61 | only-if-required = true; | ||
| 62 | } | ||
| 63 | |||
| 64 | { name = "uefi-http"; | ||
| 65 | test = "option[client-system].hex == 0x0010"; | ||
| 66 | option-data = [ | ||
| 67 | { name = "vendor-class-identifier"; data = "HTTPClient"; } | ||
| 68 | ]; | ||
| 69 | boot-file-name = "${nfsrootBaseUrl}/ipxe.efi"; | ||
| 70 | only-if-required = true; | ||
| 71 | } | ||
| 72 | |||
| 73 | { name = "ipxe-uefi-64"; | ||
| 74 | test = "option[77].hex == 'iPXE' and (substring(option[60].hex,0,20) == 'PXEClient:Arch:00007' or substring(option[60].hex,0,20) == 'PXEClient:Arch:00008' or substring(option[60].hex,0,20) == 'PXEClient:Arch:00009')"; | ||
| 75 | boot-file-name = "${nfsrootBaseUrl}/ipxe.efi"; | ||
| 76 | only-if-required = true; | ||
| 77 | } | ||
| 78 | { name = "ipxe-uefi-32"; | ||
| 79 | test = "option[77].hex == 'iPXE' and (substring(option[60].hex,0,20) == 'PXEClient:Arch:00002' or substring(option[60].hex,0,20) == 'PXEClient:Arch:00006')"; | ||
| 80 | boot-file-name = "${nfsrootBaseUrl}/i386-ipxe.efi"; | ||
| 30 | only-if-required = true; | 81 | only-if-required = true; |
| 31 | } | 82 | } |
| 32 | { name = "ipxe"; | 83 | { name = "ipxe-legacy"; |
| 33 | test = "option[77].hex == 'iPXE'"; | 84 | test = "option[77].hex == 'iPXE' and substring(option[60].hex,0,20) == 'PXEClient:Arch:00000'"; |
| 34 | next-server = "10.141.0.1"; | 85 | boot-file-name = "${nfsrootBaseUrl}/ipxe.lkrn"; |
| 35 | boot-file-name = "http://nfsroot.vidhar.yggdrasil/installer-x86_64-linux/netboot.ipxe"; | ||
| 36 | only-if-required = true; | 86 | only-if-required = true; |
| 37 | } | 87 | } |
| 88 | |||
| 38 | { name = "uefi-64"; | 89 | { name = "uefi-64"; |
| 39 | test = "substring(option[60].hex,0,20) == 'PXEClient:Arch:00007' or substring(option[60].hex,0,20) == 'PXEClient:Arch:00008' or substring(option[60].hex,0,20) == 'PXEClient:Arch:00009'"; | 90 | test = "substring(option[60].hex,0,20) == 'PXEClient:Arch:00007' or substring(option[60].hex,0,20) == 'PXEClient:Arch:00008' or substring(option[60].hex,0,20) == 'PXEClient:Arch:00009'"; |
| 40 | only-if-required = true; | ||
| 41 | option-data = [ | 91 | option-data = [ |
| 42 | { name = "tftp-server-name"; data = "10.141.0.1"; } | 92 | { name = "tftp-server-name"; data = tftpIp; } |
| 43 | ]; | 93 | ]; |
| 44 | boot-file-name = "ipxe.efi"; | 94 | boot-file-name = "ipxe.efi"; |
| 95 | only-if-required = true; | ||
| 45 | } | 96 | } |
| 46 | { name = "uefi-32"; | 97 | { name = "uefi-32"; |
| 47 | test = "substring(option[60].hex,0,20) == 'PXEClient:Arch:00002' or substring(option[60].hex,0,20) == 'PXEClient:Arch:00006'"; | 98 | test = "substring(option[60].hex,0,20) == 'PXEClient:Arch:00002' or substring(option[60].hex,0,20) == 'PXEClient:Arch:00006'"; |
| 48 | only-if-required = true; | ||
| 49 | option-data = [ | 99 | option-data = [ |
| 50 | { name = "tftp-server-name"; data = "10.141.0.1"; } | 100 | { name = "tftp-server-name"; data = tftpIp; } |
| 51 | ]; | 101 | ]; |
| 52 | boot-file-name = "i386-ipxe.efi"; | 102 | boot-file-name = "i386-ipxe.efi"; |
| 103 | only-if-required = true; | ||
| 53 | } | 104 | } |
| 54 | { name = "legacy"; | 105 | { name = "legacy"; |
| 55 | test = "substring(option[60].hex,0,20) == 'PXEClient:Arch:00000'"; | 106 | test = "substring(option[60].hex,0,20) == 'PXEClient:Arch:00000'"; |
| 56 | only-if-required = true; | ||
| 57 | option-data = [ | 107 | option-data = [ |
| 58 | { name = "tftp-server-name"; data = "10.141.0.1"; } | 108 | { name = "tftp-server-name"; data = tftpIp; } |
| 59 | ]; | 109 | ]; |
| 60 | boot-file-name = "undionly.kpxe"; | 110 | boot-file-name = "ipxe.lkrn"; |
| 111 | only-if-required = true; | ||
| 61 | } | 112 | } |
| 62 | ]; | 113 | ]; |
| 63 | 114 | ||
| @@ -252,34 +303,78 @@ with lib; | |||
| 252 | name = "nfsroot.vidhar.yggdrasil"; | 303 | name = "nfsroot.vidhar.yggdrasil"; |
| 253 | paths = | 304 | paths = |
| 254 | (map (system: | 305 | (map (system: |
| 255 | let | 306 | pkgs.symlinkJoin { |
| 256 | installerBuild = (flake.nixosConfigurations.${"installer-${system}-nfsroot"}.extendModules { | 307 | name = "installer-${system}"; |
| 257 | modules = [ | 308 | paths = [ |
| 258 | ({ ... }: { | 309 | (builtins.addErrorContext "while evaluating installer-${system}-nfsroot" (let |
| 259 | config.nfsroot.storeDevice = "10.141.0.1:nix-store"; | 310 | installerBuild' = (flake.nixosConfigurations.${"installer-${system}-nfsroot"}.extendModules { |
| 260 | config.nfsroot.registrationUrl = "http://nfsroot.vidhar.yggdrasil/installer-${system}/registration"; | 311 | modules = [ |
| 261 | }) | 312 | ({ ... }: { |
| 262 | ]; | 313 | config.nfsroot.storeDevice = "${nfsIp}:nix-store"; |
| 263 | }).config.system.build; | 314 | config.nfsroot.registrationUrl = "${nfsrootBaseUrl}/installer-${system}/registration"; |
| 264 | in builtins.toPath (pkgs.runCommandLocal "install-${system}" {} '' | 315 | config.system.nixos.label = "installer-${system}"; |
| 265 | mkdir -p $out/installer-${system} | 316 | }) |
| 266 | install -m 0444 -t $out/installer-${system} \ | 317 | ]; |
| 267 | ${installerBuild.initialRamdisk}/initrd \ | 318 | }); |
| 268 | ${installerBuild.kernel}/bzImage \ | 319 | installerBuild = installerBuild'.config.system.build; |
| 269 | ${installerBuild.netbootIpxeScript}/netboot.ipxe \ | 320 | in builtins.toPath (pkgs.runCommandLocal "installer-${system}" {} '' |
| 270 | ${pkgs.closureInfo { rootPaths = installerBuild.storeContents; }}/registration | 321 | mkdir -p $out/installer-${system} |
| 271 | '') | 322 | install -m 0444 -t $out/installer-${system} \ |
| 272 | ) ["x86_64-linux"] | 323 | ${installerBuild.initialRamdisk}/initrd \ |
| 324 | ${installerBuild.kernel}/bzImage \ | ||
| 325 | ${installerBuild.netbootIpxeScript}/netboot.ipxe \ | ||
| 326 | ${pkgs.closureInfo { rootPaths = installerBuild.storeContents; }}/registration | ||
| 327 | install -m 0444 ${pkgs.writeText "installer-${system}.menu.ipxe" '' | ||
| 328 | #!ipxe | ||
| 329 | |||
| 330 | :start | ||
| 331 | menu iPXE boot menu for installer-${system} | ||
| 332 | item installer ${with installerBuild'; "${config.system.nixos.distroName} ${config.system.nixos.codeName} ${config.system.nixos.label} (Linux ${config.boot.kernelPackages.kernel.modDirVersion})"} | ||
| 333 | item memtest memtest86plus | ||
| 334 | item netboot netboot.xyz | ||
| 335 | item shell iPXE shell | ||
| 336 | choose --timeout 0 --default installer selected || goto shell | ||
| 337 | goto ''${selected} | ||
| 338 | |||
| 339 | :shell | ||
| 340 | shell | ||
| 341 | goto start | ||
| 342 | |||
| 343 | :installer | ||
| 344 | chain installer-${system}/netboot.ipxe | ||
| 345 | goto start | ||
| 346 | |||
| 347 | :netboot | ||
| 348 | iseq ''${platform} efi && chain --autofree netboot.xyz.efi || chain --autofree netboot.xyz.lkrn | ||
| 349 | goto start | ||
| 350 | |||
| 351 | :memtest | ||
| 352 | iseq ''${platform} efi && chain --autofree memtest.efi || chain --autofree memtest.bin | ||
| 353 | goto start | ||
| 354 | ''} $out/installer-${system}.menu.ipxe | ||
| 355 | ''))) | ||
| 356 | ]; | ||
| 357 | }) ["x86_64-linux"] | ||
| 273 | ) ++ [ | 358 | ) ++ [ |
| 274 | (let | 359 | (pkgs.runCommandLocal "utils" {} '' |
| 275 | eostreBuild = (flake.nixosConfigurations.eostre.extendModules { | 360 | mkdir $out |
| 361 | install -m 0444 -t $out \ | ||
| 362 | ${ipxe}/{ipxe.efi,i386-ipxe.efi,ipxe.lkrn} \ | ||
| 363 | ${pkgs.memtest86plus}/{memtest.efi,memtest.bin} | ||
| 364 | install -m 0444 ${sources.netbootxyz-efi.src} $out/netboot.xyz.efi | ||
| 365 | install -m 0444 ${sources.netbootxyz-lkrn.src} $out/netboot.xyz.lkrn | ||
| 366 | '') | ||
| 367 | (builtins.addErrorContext "while evaluating eostre" (let | ||
| 368 | eostreBuild' = (flake.nixosConfigurations.eostre.extendModules { | ||
| 276 | modules = [ | 369 | modules = [ |
| 277 | ({ ... }: { | 370 | ({ ... }: { |
| 278 | config.nfsroot.storeDevice = "10.141.0.1:nix-store"; | 371 | config.nfsroot.storeDevice = "${nfsIp}:nix-store"; |
| 279 | config.nfsroot.registrationUrl = "http://nfsroot.vidhar.yggdrasil/eostre/registration"; | 372 | config.nfsroot.registrationUrl = "${nfsrootBaseUrl}/eostre/registration"; |
| 373 | config.system.nixos.label = "eostre"; | ||
| 280 | }) | 374 | }) |
| 281 | ]; | 375 | ]; |
| 282 | }).config.system.build; | 376 | }); |
| 377 | eostreBuild = eostreBuild'.config.system.build; | ||
| 283 | in builtins.toPath (pkgs.runCommandLocal "eostre" {} '' | 378 | in builtins.toPath (pkgs.runCommandLocal "eostre" {} '' |
| 284 | mkdir -p $out/eostre | 379 | mkdir -p $out/eostre |
| 285 | install -m 0444 -t $out/eostre \ | 380 | install -m 0444 -t $out/eostre \ |
| @@ -287,7 +382,39 @@ with lib; | |||
| 287 | ${eostreBuild.kernel}/bzImage \ | 382 | ${eostreBuild.kernel}/bzImage \ |
| 288 | ${eostreBuild.netbootIpxeScript}/netboot.ipxe \ | 383 | ${eostreBuild.netbootIpxeScript}/netboot.ipxe \ |
| 289 | ${pkgs.closureInfo { rootPaths = eostreBuild.storeContents; }}/registration | 384 | ${pkgs.closureInfo { rootPaths = eostreBuild.storeContents; }}/registration |
| 290 | '')) | 385 | install -m 0444 ${pkgs.writeText "eostre.menu.ipxe" '' |
| 386 | #!ipxe | ||
| 387 | |||
| 388 | set menu-timeout 5000 | ||
| 389 | |||
| 390 | :start | ||
| 391 | menu iPXE boot menu for eostre | ||
| 392 | item eostre ${with eostreBuild'; "${config.system.nixos.distroName} ${config.system.nixos.codeName} ${config.system.nixos.label} (Linux ${config.boot.kernelPackages.kernel.modDirVersion})"} | ||
| 393 | item memtest memtest86plus | ||
| 394 | item netboot netboot.xyz | ||
| 395 | item shell iPXE shell | ||
| 396 | choose --timeout ''${menu-timeout} --default eostre selected || goto shell | ||
| 397 | set menu-timeout 0 | ||
| 398 | goto ''${selected} | ||
| 399 | |||
| 400 | :shell | ||
| 401 | set menu-timeout 0 | ||
| 402 | shell | ||
| 403 | goto start | ||
| 404 | |||
| 405 | :eostre | ||
| 406 | chain eostre/netboot.ipxe | ||
| 407 | goto start | ||
| 408 | |||
| 409 | :netboot | ||
| 410 | iseq ''${platform} efi && chain --autofree netboot.xyz.efi || chain --autofree netboot.xyz.lkrn | ||
| 411 | goto start | ||
| 412 | |||
| 413 | :memtest | ||
| 414 | iseq ''${platform} efi && chain --autofree memtest.efi || chain --autofree memtest.bin | ||
| 415 | goto start | ||
| 416 | ''} $out/eostre.menu.ipxe | ||
| 417 | ''))) | ||
| 291 | ]; | 418 | ]; |
| 292 | }; | 419 | }; |
| 293 | }; | 420 | }; |
| @@ -298,20 +425,12 @@ with lib; | |||
| 298 | after = [ "network.target" ]; | 425 | after = [ "network.target" ]; |
| 299 | wantedBy = [ "multi-user.target" ]; | 426 | wantedBy = [ "multi-user.target" ]; |
| 300 | serviceConfig.ExecStart = let | 427 | serviceConfig.ExecStart = let |
| 301 | ipxe = pkgs.ipxe.override { | ||
| 302 | additionalTargets = { | ||
| 303 | "bin-i386-efi/ipxe.efi" = "i386-ipxe.efi"; | ||
| 304 | }; | ||
| 305 | additionalOptions = [ | ||
| 306 | "NSLOOKUP_CMD" | ||
| 307 | ]; | ||
| 308 | }; | ||
| 309 | tftpRoot = pkgs.runCommandLocal "netboot" {} '' | 428 | tftpRoot = pkgs.runCommandLocal "netboot" {} '' |
| 310 | mkdir -p $out | 429 | mkdir -p $out |
| 311 | install -m 0444 -t $out \ | 430 | install -m 0444 -t $out \ |
| 312 | ${ipxe}/ipxe.efi ${ipxe}/i386-ipxe.efi ${ipxe}/undionly.kpxe | 431 | ${ipxe}/{ipxe.efi,i386-ipxe.efi,ipxe.lkrn} |
| 313 | ''; | 432 | ''; |
| 314 | in "${pkgs.atftp}/sbin/atftpd --daemon --no-fork --bind-address=10.141.0.1 ${tftpRoot}"; | 433 | in "${pkgs.atftp}/sbin/atftpd --daemon --no-fork --bind-address=${tftpIp} ${tftpRoot}"; |
| 315 | }; | 434 | }; |
| 316 | }; | 435 | }; |
| 317 | } | 436 | } |
diff --git a/hosts/vidhar/network/pap-secrets b/hosts/vidhar/network/pap-secrets deleted file mode 100644 index 3516de6c..00000000 --- a/hosts/vidhar/network/pap-secrets +++ /dev/null | |||
| @@ -1,26 +0,0 @@ | |||
| 1 | { | ||
| 2 | "data": "ENC[AES256_GCM,data:BOyWdys7Oja54Ijv5j+kqufdokQe0onnqw/gVxpNOMf+YI/LlzJscaEtGqPh3ehVtYoSWGumCBPrjdq/zhYq4VV/PCtdeu3MnX1CH2B5bH0mFs0eXcqeGK6NErz/b5nEglv4Z19ig2CUDlbvi8h1zZAEjxTNKhT16ItCtJnBCsIoiMl2QcTTWMyh4a02v3wA1UrOQZvFuCgCHmRoBE6vpREyB23gQdrdKLk7D1LLw5C1aZnzQOhFsgs7bVjOcBnmwTao8ntXxw==,iv:X5FgYkl3DGA/lkRsoc+5XrK3Nlp/ldnFigpXpYNfSJE=,tag:qUH7m9NzuVNnOfr3rRgaOg==,type:str]", | ||
| 3 | "sops": { | ||
| 4 | "kms": null, | ||
| 5 | "gcp_kms": null, | ||
| 6 | "azure_kv": null, | ||
| 7 | "hc_vault": null, | ||
| 8 | "age": [ | ||
| 9 | { | ||
| 10 | "recipient": "age1qffdqvy9arld9zd5a5cylt0n98xhcns5shxhrhwjq5g4qa844ejselaa4l", | ||
| 11 | "enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSAwYTFaTDZRbDd6cFBRSTNN\nVk1kcFJXRG9TT21IMDZsVmtoZjBTNDNjeDFzCkhxNEI2Ujd6SW1STG43eE5EdzZa\nS3phenJZN0RxajBXQ1BnbUhTa3htdFEKLS0tIGVlT3lReHJSQ2UvQ1FST0M0RzVP\nNmxWNzJmNlFPclJTeDUycDJiUzA4Yk0K4JHtkEPY49TGnKPZzEoEZ131RxeQEWkR\nK1ftH2ilr2tUhiErhpqxoTqfAm33xvruqTsePxh1uC7svzKtKBlS2g==\n-----END AGE ENCRYPTED FILE-----\n" | ||
| 12 | } | ||
| 13 | ], | ||
| 14 | "lastmodified": "2021-11-15T08:30:09Z", | ||
| 15 | "mac": "ENC[AES256_GCM,data:TAgZ4ktdN9sZPMo1UtwjKdTM2QBjLorcm84HYXTGYNNEorPoqrXAWOvyWRLjx+zxzpRuDLBPQHCkjwkVO2CctxnTaWPMwITbYtQqj/5ZxACuAeX8MaSximB8s5MJK2faCuVXEnFehbnnPr5Fs8ZsgHwu2iH6DU8ScLEkgckzGV0=,iv:keUbKwWfoIIBsp5Rsm2lEba1ZHAozQY2YpA6p5qDBiU=,tag:1llGytMGvOjSVYKJXGUmXg==,type:str]", | ||
| 16 | "pgp": [ | ||
| 17 | { | ||
| 18 | "created_at": "2023-01-30T10:58:50Z", | ||
| 19 | "enc": "-----BEGIN PGP MESSAGE-----\n\nhF4DXxoViZlp6dISAQdA+cwEt6Gv5oKvym4ceJek+J/5guNpmsLLXWIY5CCCSXUw\npXyQpqxm7LQnasIqYNNsNCVbB1mAu6WU6MKn0BG03YWjr8buLB+7PpwZcxeZzRfD\n0l4BAsl+vKwa2YSMCR+EWYSfeEzEVHqoGBJ60dYXuiFiNZInCik+g69PdhsGygNH\nRtIcRiCB8t94GkvdWySTq5ohi1wKOe224l9evbt4zXntVngCHxixuufLrr3Cj+EE\n=3lw4\n-----END PGP MESSAGE-----\n", | ||
| 20 | "fp": "30D3453B8CD02FE2A3E7C78C0FB536FB87AE8F51" | ||
| 21 | } | ||
| 22 | ], | ||
| 23 | "unencrypted_suffix": "_unencrypted", | ||
| 24 | "version": "3.7.1" | ||
| 25 | } | ||
| 26 | } \ No newline at end of file | ||
diff --git a/hosts/vidhar/network/gpon.nix b/hosts/vidhar/network/pppoe.nix index 1628159c..5cc84862 100644 --- a/hosts/vidhar/network/gpon.nix +++ b/hosts/vidhar/network/pppoe.nix | |||
| @@ -8,7 +8,7 @@ in { | |||
| 8 | options = { | 8 | options = { |
| 9 | networking.pppInterface = mkOption { | 9 | networking.pppInterface = mkOption { |
| 10 | type = types.str; | 10 | type = types.str; |
| 11 | default = "gpon"; | 11 | default = "ppp"; |
| 12 | }; | 12 | }; |
| 13 | }; | 13 | }; |
| 14 | 14 | ||
| @@ -26,14 +26,14 @@ in { | |||
| 26 | nodefaultroute | 26 | nodefaultroute |
| 27 | ifname ${pppInterface} | 27 | ifname ${pppInterface} |
| 28 | lcp-echo-adaptive | 28 | lcp-echo-adaptive |
| 29 | lcp-echo-failure 5 | 29 | lcp-echo-failure 10 |
| 30 | lcp-echo-interval 1 | 30 | lcp-echo-interval 1 |
| 31 | maxfail 0 | 31 | maxfail 0 |
| 32 | mtu 1492 | 32 | mtu 1492 |
| 33 | mru 1492 | 33 | mru 1492 |
| 34 | plugin pppoe.so | 34 | plugin pppoe.so |
| 35 | name telekom | 35 | user congstar |
| 36 | user 002576900250551137425220#0001@t-online.de | 36 | password congstar |
| 37 | nic-telekom | 37 | nic-telekom |
| 38 | debug | 38 | debug |
| 39 | +ipv6 | 39 | +ipv6 |
| @@ -43,62 +43,55 @@ in { | |||
| 43 | stopIfChanged = true; | 43 | stopIfChanged = true; |
| 44 | 44 | ||
| 45 | serviceConfig = { | 45 | serviceConfig = { |
| 46 | Type = lib.mkForce "notify"; | ||
| 47 | ExecStart = lib.mkForce "${getBin config.services.pppd.package}/sbin/pppd call telekom up_sdnotify nolog"; | ||
| 46 | PIDFile = "/run/pppd/${pppInterface}.pid"; | 48 | PIDFile = "/run/pppd/${pppInterface}.pid"; |
| 47 | }; | 49 | }; |
| 48 | restartTriggers = with config; [ | 50 | restartTriggers = with config; [ |
| 49 | environment.etc."ppp/ip-pre-up".source | 51 | environment.etc."ppp/ip-pre-up".source |
| 50 | environment.etc."ppp/ip-up".source | 52 | environment.etc."ppp/ip-up".source |
| 51 | environment.etc."ppp/ip-down".source | 53 | environment.etc."ppp/ip-down".source |
| 52 | # sops.secrets."pap-secrets".sopsFile | ||
| 53 | ]; | 54 | ]; |
| 54 | }; | 55 | }; |
| 55 | sops.secrets."pap-secrets" = { | ||
| 56 | format = "binary"; | ||
| 57 | sopsFile = ./pap-secrets; | ||
| 58 | path = "/etc/ppp/pap-secrets"; | ||
| 59 | }; | ||
| 60 | 56 | ||
| 61 | environment.etc = { | 57 | environment.etc = { |
| 62 | "ppp/ip-pre-up".source = let | 58 | "ppp/ip-pre-up".source = pkgs.resholve.writeScript "ip-pre-up" { |
| 63 | app = pkgs.writeShellApplication { | 59 | interpreter = pkgs.runtimeShell; |
| 64 | name = "ip-pre-up"; | 60 | inputs = [ pkgs.iproute2 pkgs.ethtool ]; |
| 65 | runtimeInputs = with pkgs; [ iproute2 ethtool ]; | 61 | execer = [ |
| 66 | text = '' | 62 | "cannot:${lib.getExe' pkgs.iproute2 "ip"}" |
| 67 | ethtool -K telekom tso off gso off gro off | 63 | "cannot:${lib.getExe' pkgs.iproute2 "tc"}" |
| 64 | ]; | ||
| 65 | } '' | ||
| 66 | ethtool -K telekom tso off gso off gro off | ||
| 68 | 67 | ||
| 69 | ip link del "ifb4${pppInterface}" || true | 68 | ip link del "ifb4${pppInterface}" || true |
| 70 | ip link add name "ifb4${pppInterface}" type ifb | 69 | ip link add name "ifb4${pppInterface}" type ifb |
| 71 | ip link set "ifb4${pppInterface}" up | 70 | ip link set "ifb4${pppInterface}" up |
| 72 | 71 | ||
| 73 | tc qdisc del dev "ifb4${pppInterface}" root || true | 72 | tc qdisc del dev "ifb4${pppInterface}" root || true |
| 74 | tc qdisc del dev "${pppInterface}" ingress || true | 73 | tc qdisc del dev "${pppInterface}" ingress || true |
| 75 | tc qdisc del dev "${pppInterface}" root || true | 74 | tc qdisc del dev "${pppInterface}" root || true |
| 76 | 75 | ||
| 77 | tc qdisc add dev "${pppInterface}" handle ffff: ingress | 76 | tc qdisc add dev "${pppInterface}" handle ffff: ingress |
| 78 | tc filter add dev "${pppInterface}" parent ffff: basic action ctinfo dscp 0x0000003f 0x00000040 action mirred egress redirect dev "ifb4${pppInterface}" | 77 | tc filter add dev "${pppInterface}" parent ffff: basic action ctinfo dscp 0x0000003f 0x00000040 action mirred egress redirect dev "ifb4${pppInterface}" |
| 79 | tc qdisc replace dev "ifb4${pppInterface}" root cake memlimit 128Mb overhead 35 mpu 74 regional diffserv4 bandwidth 285mbit | 78 | tc qdisc replace dev "ifb4${pppInterface}" root cake memlimit 128Mb overhead 35 mpu 74 regional diffserv4 bandwidth ${toString (builtins.floor (177968 * 0.95))}kbit |
| 80 | tc qdisc replace dev "${pppInterface}" root cake memlimit 128Mb overhead 35 mpu 74 regional nat diffserv4 wash bandwidth 143mbit | 79 | tc qdisc replace dev "${pppInterface}" root cake memlimit 128Mb overhead 35 mpu 74 regional nat diffserv4 wash bandwidth ${toString (builtins.floor (41216 * 0.95))}kbit |
| 81 | ''; | 80 | ''; |
| 82 | }; | 81 | "ppp/ip-up".source = pkgs.resholve.writeScript "ip-up" { |
| 83 | in "${app}/bin/${app.meta.mainProgram}"; | 82 | interpreter = pkgs.runtimeShell; |
| 84 | "ppp/ip-up".source = let | 83 | inputs = [ pkgs.iproute2 ]; |
| 85 | app = pkgs.writeShellApplication { | 84 | execer = [ "cannot:${lib.getExe' pkgs.iproute2 "ip"}" ]; |
| 86 | name = "ip-up"; | 85 | } '' |
| 87 | runtimeInputs = with pkgs; [ iproute2 ]; | 86 | ip route add default via "$5" dev "${pppInterface}" metric 512 |
| 88 | text = '' | 87 | ''; |
| 89 | ip route add default via "$5" dev "${pppInterface}" metric 512 | 88 | "ppp/ip-down".source = pkgs.resholve.writeScript "ip-down" { |
| 90 | ''; | 89 | interpreter = pkgs.runtimeShell; |
| 91 | }; | 90 | inputs = [ pkgs.iproute2 ]; |
| 92 | in "${app}/bin/${app.meta.mainProgram}"; | 91 | execer = [ "cannot:${lib.getExe' pkgs.iproute2 "ip"}" ]; |
| 93 | "ppp/ip-down".source = let | 92 | } '' |
| 94 | app = pkgs.writeShellApplication { | 93 | ip link del "ifb4${pppInterface}" |
| 95 | name = "ip-down"; | 94 | ''; |
| 96 | runtimeInputs = with pkgs; [ iproute2 ]; | ||
| 97 | text = '' | ||
| 98 | ip link del "ifb4${pppInterface}" | ||
| 99 | ''; | ||
| 100 | }; | ||
| 101 | in "${app}/bin/${app.meta.mainProgram}"; | ||
| 102 | }; | 95 | }; |
| 103 | 96 | ||
| 104 | systemd.network.networks.${pppInterface} = { | 97 | systemd.network.networks.${pppInterface} = { |
diff --git a/hosts/vidhar/network/ruleset.nft b/hosts/vidhar/network/ruleset.nft index 9f519302..dd750394 100644 --- a/hosts/vidhar/network/ruleset.nft +++ b/hosts/vidhar/network/ruleset.nft | |||
| @@ -1,18 +1,19 @@ | |||
| 1 | define icmp_protos = { ipv6-icmp, icmp, igmp } | 1 | define icmp_protos = { ipv6-icmp, icmp, igmp } |
| 2 | define bifrost_surtr = 2a03:4000:52:ada:4::/128 | ||
| 2 | 3 | ||
| 3 | table arp filter { | 4 | table arp filter { |
| 4 | limit lim_arp_local { | 5 | limit lim_arp_local { |
| 5 | rate over 50 mbytes/second burst 50 mbytes | 6 | rate over 50 mbytes/second burst 50 mbytes |
| 6 | } | 7 | } |
| 7 | limit lim_arp_gpon { | 8 | limit lim_arp_ppp { |
| 8 | rate over 7500 kbytes/second burst 7500 kbytes | 9 | rate over 7500 kbytes/second burst 7500 kbytes |
| 9 | } | 10 | } |
| 10 | 11 | ||
| 11 | counter arp-rx {} | 12 | counter arp-rx {} |
| 12 | counter arp-tx {} | 13 | counter arp-tx {} |
| 13 | 14 | ||
| 14 | counter arp-ratelimit-gpon-rx {} | 15 | counter arp-ratelimit-ppp-rx {} |
| 15 | counter arp-ratelimit-gpon-tx {} | 16 | counter arp-ratelimit-ppp-tx {} |
| 16 | 17 | ||
| 17 | counter arp-ratelimit-local-rx {} | 18 | counter arp-ratelimit-local-rx {} |
| 18 | counter arp-ratelimit-local-tx {} | 19 | counter arp-ratelimit-local-tx {} |
| @@ -21,8 +22,8 @@ table arp filter { | |||
| 21 | type filter hook input priority filter | 22 | type filter hook input priority filter |
| 22 | policy accept | 23 | policy accept |
| 23 | 24 | ||
| 24 | iifname != gpon limit name lim_arp_local counter name arp-ratelimit-local-rx drop | 25 | iifname != @pppInterface@ limit name lim_arp_local counter name arp-ratelimit-local-rx drop |
| 25 | iifname gpon limit name lim_arp_gpon counter name arp-ratelimit-gpon-rx drop | 26 | iifname @pppInterface@ limit name lim_arp_ppp counter name arp-ratelimit-ppp-rx drop |
| 26 | 27 | ||
| 27 | counter name arp-rx | 28 | counter name arp-rx |
| 28 | } | 29 | } |
| @@ -31,8 +32,8 @@ table arp filter { | |||
| 31 | type filter hook output priority filter | 32 | type filter hook output priority filter |
| 32 | policy accept | 33 | policy accept |
| 33 | 34 | ||
| 34 | oifname != gpon limit name lim_arp_local counter name arp-ratelimit-local-tx drop | 35 | oifname != @pppInterface@ limit name lim_arp_local counter name arp-ratelimit-local-tx drop |
| 35 | oifname gpon limit name lim_arp_gpon counter name arp-ratelimit-gpon-tx drop | 36 | oifname @pppInterface@ limit name lim_arp_ppp counter name arp-ratelimit-ppp-tx drop |
| 36 | 37 | ||
| 37 | counter name arp-tx | 38 | counter name arp-tx |
| 38 | } | 39 | } |
| @@ -46,11 +47,11 @@ table inet filter { | |||
| 46 | limit lim_icmp_local { | 47 | limit lim_icmp_local { |
| 47 | rate over 50 mbytes/second burst 50 mbytes | 48 | rate over 50 mbytes/second burst 50 mbytes |
| 48 | } | 49 | } |
| 49 | limit lim_icmp_gpon { | 50 | limit lim_icmp_ppp { |
| 50 | rate over 7500 kbytes/second burst 7500 kbytes | 51 | rate over 7500 kbytes/second burst 7500 kbytes |
| 51 | } | 52 | } |
| 52 | 53 | ||
| 53 | counter icmp-ratelimit-gpon-fw {} | 54 | counter icmp-ratelimit-ppp-fw {} |
| 54 | counter icmp-ratelimit-local-fw {} | 55 | counter icmp-ratelimit-local-fw {} |
| 55 | 56 | ||
| 56 | counter icmp-fw {} | 57 | counter icmp-fw {} |
| @@ -58,7 +59,8 @@ table inet filter { | |||
| 58 | counter invalid-fw {} | 59 | counter invalid-fw {} |
| 59 | counter fw-lo {} | 60 | counter fw-lo {} |
| 60 | counter fw-lan {} | 61 | counter fw-lan {} |
| 61 | counter fw-gpon {} | 62 | counter fw-ppp {} |
| 63 | counter fw-kimai {} | ||
| 62 | 64 | ||
| 63 | counter fw-cups {} | 65 | counter fw-cups {} |
| 64 | 66 | ||
| @@ -73,7 +75,7 @@ table inet filter { | |||
| 73 | counter invalid-local4-rx {} | 75 | counter invalid-local4-rx {} |
| 74 | counter invalid-local6-rx {} | 76 | counter invalid-local6-rx {} |
| 75 | 77 | ||
| 76 | counter icmp-ratelimit-gpon-rx {} | 78 | counter icmp-ratelimit-ppp-rx {} |
| 77 | counter icmp-ratelimit-local-rx {} | 79 | counter icmp-ratelimit-local-rx {} |
| 78 | counter icmp-rx {} | 80 | counter icmp-rx {} |
| 79 | 81 | ||
| @@ -90,6 +92,11 @@ table inet filter { | |||
| 90 | counter http-rx {} | 92 | counter http-rx {} |
| 91 | counter tftp-rx {} | 93 | counter tftp-rx {} |
| 92 | counter pgbackrest-rx {} | 94 | counter pgbackrest-rx {} |
| 95 | counter immich-rx {} | ||
| 96 | counter paperless-rx {} | ||
| 97 | counter hledger-rx {} | ||
| 98 | counter audiobookshelf-rx {} | ||
| 99 | counter kimai-rx {} | ||
| 93 | 100 | ||
| 94 | counter established-rx {} | 101 | counter established-rx {} |
| 95 | 102 | ||
| @@ -101,7 +108,7 @@ table inet filter { | |||
| 101 | 108 | ||
| 102 | counter tx-lo {} | 109 | counter tx-lo {} |
| 103 | 110 | ||
| 104 | counter icmp-ratelimit-gpon-tx {} | 111 | counter icmp-ratelimit-ppp-tx {} |
| 105 | counter icmp-ratelimit-local-tx {} | 112 | counter icmp-ratelimit-local-tx {} |
| 106 | counter icmp-tx {} | 113 | counter icmp-tx {} |
| 107 | 114 | ||
| @@ -118,15 +125,20 @@ table inet filter { | |||
| 118 | counter http-tx {} | 125 | counter http-tx {} |
| 119 | counter tftp-tx {} | 126 | counter tftp-tx {} |
| 120 | counter pgbackrest-tx {} | 127 | counter pgbackrest-tx {} |
| 128 | counter immich-tx {} | ||
| 129 | counter paperless-tx {} | ||
| 130 | counter hledger-tx {} | ||
| 131 | counter audiobookshelf-tx {} | ||
| 132 | counter kimai-tx {} | ||
| 121 | 133 | ||
| 122 | counter tx {} | 134 | counter tx {} |
| 123 | 135 | ||
| 124 | 136 | ||
| 125 | chain forward_icmp_accept { | 137 | chain forward_icmp_accept { |
| 126 | oifname { gpon, bifrost } limit name lim_icmp_gpon counter name icmp-ratelimit-gpon-fw drop | 138 | oifname { @pppInterface@, bifrost } limit name lim_icmp_ppp counter name icmp-ratelimit-ppp-fw drop |
| 127 | iifname { gpon, bifrost } limit name lim_icmp_gpon counter name icmp-ratelimit-gpon-fw drop | 139 | iifname { @pppInterface@, bifrost } limit name lim_icmp_ppp counter name icmp-ratelimit-ppp-fw drop |
| 128 | oifname != { gpon, bifrost } limit name lim_icmp_local counter name icmp-ratelimit-local-fw drop | 140 | oifname != { @pppInterface@, bifrost } limit name lim_icmp_local counter name icmp-ratelimit-local-fw drop |
| 129 | iifname != { gpon, bifrost } limit name lim_icmp_local counter name icmp-ratelimit-local-fw drop | 141 | iifname != { @pppInterface@, bifrost } limit name lim_icmp_local counter name icmp-ratelimit-local-fw drop |
| 130 | counter name icmp-fw accept | 142 | counter name icmp-fw accept |
| 131 | } | 143 | } |
| 132 | chain forward { | 144 | chain forward { |
| @@ -139,10 +151,15 @@ table inet filter { | |||
| 139 | 151 | ||
| 140 | iifname lo counter name fw-lo accept | 152 | iifname lo counter name fw-lo accept |
| 141 | 153 | ||
| 142 | oifname { lan, gpon, bifrost } meta l4proto $icmp_protos jump forward_icmp_accept | 154 | oifname { lan, @pppInterface@, bifrost } meta l4proto $icmp_protos jump forward_icmp_accept |
| 143 | iifname lan oifname { gpon, bifrost } counter name fw-lan accept | 155 | iifname lan oifname { @pppInterface@, bifrost } counter name fw-lan accept |
| 156 | iifname ve-kimai oifname @pppInterface@ counter name fw-kimai accept | ||
| 144 | 157 | ||
| 145 | iifname gpon oifname lan ct state { established, related } counter name fw-gpon accept | 158 | iifname @pppInterface@ oifname lan ct state { established, related } counter name fw-ppp accept |
| 159 | iifname @pppInterface@ oifname ve-kimai ct state { established, related } counter name fw-kimai accept | ||
| 160 | |||
| 161 | iifname bifrost oifname ve-kimai tcp dport 80 ip6 saddr $bifrost_surtr ip6 daddr 2a03:4000:52:ada:6::2 counter name kimai-rx accept | ||
| 162 | iifname ve-kimai oifname bifrost tcp sport 80 ip6 saddr 2a03:4000:52:ada:6::2 ip6 daddr $bifrost_surtr counter name kimai-tx accept | ||
| 146 | 163 | ||
| 147 | 164 | ||
| 148 | limit name lim_reject log level debug prefix "drop forward: " counter name reject-ratelimit-fw drop | 165 | limit name lim_reject log level debug prefix "drop forward: " counter name reject-ratelimit-fw drop |
| @@ -163,22 +180,22 @@ table inet filter { | |||
| 163 | iif != lo ip daddr 127.0.0.1/8 counter name invalid-local4-rx reject | 180 | iif != lo ip daddr 127.0.0.1/8 counter name invalid-local4-rx reject |
| 164 | iif != lo ip6 daddr ::1/128 counter name invalid-local6-rx reject | 181 | iif != lo ip6 daddr ::1/128 counter name invalid-local6-rx reject |
| 165 | 182 | ||
| 166 | iifname { bifrost, gpon } meta l4proto $icmp_protos limit name lim_icmp_gpon counter name icmp-ratelimit-gpon-rx drop | 183 | iifname { bifrost, @pppInterface@ } meta l4proto $icmp_protos limit name lim_icmp_ppp counter name icmp-ratelimit-ppp-rx drop |
| 167 | iifname != { bifrost, gpon } meta l4proto $icmp_protos limit name lim_icmp_local counter name icmp-ratelimit-local-rx drop | 184 | iifname != { bifrost, @pppInterface@ } meta l4proto $icmp_protos limit name lim_icmp_local counter name icmp-ratelimit-local-rx drop |
| 168 | meta l4proto $icmp_protos counter name icmp-rx accept | 185 | meta l4proto $icmp_protos counter name icmp-rx accept |
| 169 | 186 | ||
| 170 | iifname { lan, mgmt, gpon, yggdrasil, bifrost } tcp dport 22 counter name ssh-rx accept | 187 | iifname { lan, mgmt, @pppInterface@, yggdrasil, bifrost } tcp dport 22 counter name ssh-rx accept |
| 171 | iifname { lan, mgmt, gpon, yggdrasil, bifrost } udp dport 60000-61000 counter name mosh-rx accept | 188 | iifname { lan, mgmt, @pppInterface@, yggdrasil, bifrost } udp dport 60000-61000 counter name mosh-rx accept |
| 172 | 189 | ||
| 173 | iifname { lan, mgmt, wifibh, yggdrasil } meta l4proto { tcp, udp } th dport 53 counter name dns-rx accept | 190 | iifname { lan, mgmt, wifibh, yggdrasil } meta l4proto { tcp, udp } th dport 53 counter name dns-rx accept |
| 174 | 191 | ||
| 175 | iifname { lan, yggdrasil } tcp dport 2049 counter name nfs-rx accept | 192 | iifname { lan, yggdrasil } tcp dport 2049 counter name nfs-rx accept |
| 176 | 193 | ||
| 177 | iifname { lan, mgmt, gpon } meta protocol ip udp dport 51820 counter name wg-rx accept | 194 | iifname { lan, mgmt, @pppInterface@ } meta protocol ip udp dport 51820 counter name wg-rx accept |
| 178 | iifname { lan, mgmt, gpon } meta protocol ip6 udp dport 51821 counter name wg-rx accept | 195 | iifname { lan, mgmt, @pppInterface@ } meta protocol ip6 udp dport 51821 counter name wg-rx accept |
| 179 | iifname "yggdrasil-wg-*" meta l4proto gre counter name yggdrasil-gre-rx accept | 196 | iifname "yggdrasil-wg-*" meta l4proto gre counter name yggdrasil-gre-rx accept |
| 180 | 197 | ||
| 181 | iifname gpon meta protocol ip6 udp dport 546 udp sport 547 counter name ipv6-pd-rx accept | 198 | iifname @pppInterface@ meta protocol ip6 udp dport 546 udp sport 547 counter name ipv6-pd-rx accept |
| 182 | 199 | ||
| 183 | iifname mgmt udp dport 123 counter name ntp-rx accept | 200 | iifname mgmt udp dport 123 counter name ntp-rx accept |
| 184 | 201 | ||
| @@ -193,6 +210,11 @@ table inet filter { | |||
| 193 | 210 | ||
| 194 | tcp dport 8432 counter name pgbackrest-rx accept | 211 | tcp dport 8432 counter name pgbackrest-rx accept |
| 195 | 212 | ||
| 213 | iifname bifrost tcp dport 2283 ip6 saddr $bifrost_surtr counter name immich-rx accept | ||
| 214 | iifname bifrost tcp dport 28981 ip6 saddr $bifrost_surtr counter name paperless-rx accept | ||
| 215 | iifname bifrost tcp dport 5000 ip6 saddr $bifrost_surtr counter name hledger-rx accept | ||
| 216 | iifname bifrost tcp dport 28982 ip6 saddr $bifrost_surtr counter name audiobookshelf-rx accept | ||
| 217 | |||
| 196 | ct state { established, related } counter name established-rx accept | 218 | ct state { established, related } counter name established-rx accept |
| 197 | 219 | ||
| 198 | 220 | ||
| @@ -209,8 +231,8 @@ table inet filter { | |||
| 209 | 231 | ||
| 210 | oifname lo counter name tx-lo accept | 232 | oifname lo counter name tx-lo accept |
| 211 | 233 | ||
| 212 | oifname { bifrost, gpon } meta l4proto $icmp_protos limit name lim_icmp_gpon counter name icmp-ratelimit-gpon-tx drop | 234 | oifname { bifrost, @pppInterface@ } meta l4proto $icmp_protos limit name lim_icmp_ppp counter name icmp-ratelimit-ppp-tx drop |
| 213 | oifname != { bifrost, gpon } meta l4proto $icmp_protos limit name lim_icmp_local counter name icmp-ratelimit-local-tx drop | 235 | oifname != { bifrost, @pppInterface@ } meta l4proto $icmp_protos limit name lim_icmp_local counter name icmp-ratelimit-local-tx drop |
| 214 | meta l4proto $icmp_protos counter name icmp-tx accept | 236 | meta l4proto $icmp_protos counter name icmp-tx accept |
| 215 | 237 | ||
| 216 | 238 | ||
| @@ -240,34 +262,39 @@ table inet filter { | |||
| 240 | 262 | ||
| 241 | tcp sport 8432 counter name pgbackrest-tx accept | 263 | tcp sport 8432 counter name pgbackrest-tx accept |
| 242 | 264 | ||
| 265 | iifname bifrost tcp sport 2283 ip6 daddr $bifrost_surtr counter name immich-tx accept | ||
| 266 | iifname bifrost tcp sport 28981 ip6 daddr $bifrost_surtr counter name paperless-tx accept | ||
| 267 | iifname bifrost tcp sport 5000 ip6 daddr $bifrost_surtr counter name hledger-tx accept | ||
| 268 | iifname bifrost tcp sport 28982 ip6 daddr $bifrost_surtr counter name audiobookshelf-tx accept | ||
| 269 | |||
| 243 | 270 | ||
| 244 | counter name tx | 271 | counter name tx |
| 245 | } | 272 | } |
| 246 | } | 273 | } |
| 247 | 274 | ||
| 248 | table inet nat { | 275 | table inet nat { |
| 249 | counter gpon-nat {} | 276 | counter ppp-nat {} |
| 250 | # counter container-nat {} | 277 | counter kimai-nat {} |
| 251 | 278 | ||
| 252 | chain postrouting { | 279 | chain postrouting { |
| 253 | type nat hook postrouting priority srcnat | 280 | type nat hook postrouting priority srcnat |
| 254 | policy accept | 281 | policy accept |
| 255 | 282 | ||
| 256 | 283 | ||
| 257 | meta nfproto ipv4 oifname gpon counter name gpon-nat masquerade | 284 | meta nfproto ipv4 oifname @pppInterface@ counter name ppp-nat masquerade |
| 258 | # iifname ve-* oifname gpon counter name container-nat masquerade | 285 | iifname ve-kimai oifname @pppInterface@ counter name kimai-nat masquerade |
| 259 | } | 286 | } |
| 260 | } | 287 | } |
| 261 | 288 | ||
| 262 | table inet mss_clamp { | 289 | table inet mss_clamp { |
| 263 | counter gpon-mss-clamp {} | 290 | counter ppp-mss-clamp {} |
| 264 | 291 | ||
| 265 | chain postrouting { | 292 | chain postrouting { |
| 266 | type filter hook postrouting priority mangle | 293 | type filter hook postrouting priority mangle |
| 267 | policy accept | 294 | policy accept |
| 268 | 295 | ||
| 269 | 296 | ||
| 270 | oifname gpon tcp flags & (syn|rst) == syn counter name gpon-mss-clamp tcp option maxseg size set rt mtu | 297 | oifname @pppInterface@ tcp flags & (syn|rst) == syn counter name ppp-mss-clamp tcp option maxseg size set rt mtu |
| 271 | } | 298 | } |
| 272 | } | 299 | } |
| 273 | 300 | ||
| @@ -402,7 +429,7 @@ table inet dscpclassify { | |||
| 402 | chain postrouting { | 429 | chain postrouting { |
| 403 | type filter hook postrouting priority filter + 1; policy accept | 430 | type filter hook postrouting priority filter + 1; policy accept |
| 404 | 431 | ||
| 405 | oifname != gpon return | 432 | oifname != @pppInterface@ return |
| 406 | 433 | ||
| 407 | ip dscp cs0 goto ct_set_cs0 | 434 | ip dscp cs0 goto ct_set_cs0 |
| 408 | ip dscp lephb goto ct_set_lephb | 435 | ip dscp lephb goto ct_set_lephb |
diff --git a/hosts/vidhar/paperless/default.nix b/hosts/vidhar/paperless/default.nix new file mode 100644 index 00000000..dd02da38 --- /dev/null +++ b/hosts/vidhar/paperless/default.nix | |||
| @@ -0,0 +1,25 @@ | |||
| 1 | { config, ... }: | ||
| 2 | |||
| 3 | { | ||
| 4 | config = { | ||
| 5 | services.paperless = { | ||
| 6 | enable = true; | ||
| 7 | address = "2a03:4000:52:ada:4:1::"; | ||
| 8 | passwordFile = config.sops.secrets."paperless-rootpw".path; | ||
| 9 | settings = { | ||
| 10 | PAPERLESS_OCR_LANGUAGE = "deu+eng"; | ||
| 11 | PAPERLESS_URL = "https://paperless.yggdrasil.li"; | ||
| 12 | PAPERLESS_FILENAME_FORMAT = "{{ created_year }}/{{ document_type }}/{{ correspondent }}/{{ created }}_{{ doc_pk }}_{{ title }}"; | ||
| 13 | PAPERLESS_FILENAME_FORMAT_REMOVE_NONE = "true"; | ||
| 14 | PAPERLESS_TASK_WORKERS = "3"; | ||
| 15 | PAPERLESS_THREADS_PER_WORKER = "4"; | ||
| 16 | }; | ||
| 17 | database.createLocally = true; | ||
| 18 | }; | ||
| 19 | |||
| 20 | sops.secrets."paperless-rootpw" = { | ||
| 21 | format = "binary"; | ||
| 22 | sopsFile = ./rootpw; | ||
| 23 | }; | ||
| 24 | }; | ||
| 25 | } | ||
diff --git a/hosts/vidhar/paperless/rootpw b/hosts/vidhar/paperless/rootpw new file mode 100644 index 00000000..11f48fcb --- /dev/null +++ b/hosts/vidhar/paperless/rootpw | |||
| @@ -0,0 +1,24 @@ | |||
| 1 | { | ||
| 2 | "data": "ENC[AES256_GCM,data:Bsns3bLs7aA++eTf2Vh4g2iAXhmrMRTF,iv:zQ6hgXEvgHAloN6UMW54f2nYCvEhHPXQSBVSihHFiC0=,tag:uiGTEs07dpx12PcAjmbr9Q==,type:str]", | ||
| 3 | "sops": { | ||
| 4 | "kms": null, | ||
| 5 | "gcp_kms": null, | ||
| 6 | "azure_kv": null, | ||
| 7 | "hc_vault": null, | ||
| 8 | "age": [ | ||
| 9 | { | ||
| 10 | "recipient": "age1rmmhetcmllq0ahl5qznlr0eya2zdxwl9h6y5wnl97d2wtyx5t99sm2u866", | ||
| 11 | "enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBlVUJjdEdIZGd6UDJBRXlL\nODFyWDhHOU9oTEVCVlFiUXVXNm9XZmVuampVCkJ0YkFXTlZXVnRldmtlVkJaR3R2\nMFhpaHB5M3pLeDFkUkkzMUFydGNnOFEKLS0tIEJtNWc0V2JaaWYvQlp6TGxVdVZO\neVpzQzB5Um82TUZOeHBHeE50MGlqNWsKj1P54Fc+c5n35+Og9DwBWkvW947hgFsp\ni/G2QcaLHHJMTexTCZYsr1naSVa/cMBAbrZmtjz0HV4Q1kCJtvlrIg==\n-----END AGE ENCRYPTED FILE-----\n" | ||
| 12 | }, | ||
| 13 | { | ||
| 14 | "recipient": "age1qffdqvy9arld9zd5a5cylt0n98xhcns5shxhrhwjq5g4qa844ejselaa4l", | ||
| 15 | "enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSA1UG1QSWtXcFZoQVRBOC9D\nT2VnTW9pcTRCMForcHdZVld0c1NmNFZpWUNBCkRkMERKUVliYXRqb25saWxyb2JN\nbC9YL2ZQbytRM0ZjNmlQOTlTZTQrV2sKLS0tIFZyUWtRcXNqZUZxMGN5d0tHUng2\nVXNSdFEwMmtIVEdVRVlWeVU1YmJVSkUKRJa42k551QtiC6S0tmMv7eVN7GRqpXWz\nvzNh+BM9TOJNaTMmVesr4vXNDLOSFS3PxYv95xuOBzVg3zOHuai72g==\n-----END AGE ENCRYPTED FILE-----\n" | ||
| 16 | } | ||
| 17 | ], | ||
| 18 | "lastmodified": "2025-02-13T19:20:33Z", | ||
| 19 | "mac": "ENC[AES256_GCM,data:mG6AC3L8MMeZ0Ajr7zV1mzPcHviQw2adtGjSbrbPRw1xqN7siu6svoybv8xkahP2Grq/xKAiyfXFOFo7Uyc3ub5fSovAEolNazqybZYsyam5vHpeC23dXcEkZUJSPJ9/CSB5uI9nX3NPC64QUjCxHZ7qfH5gcXT9D12H8LSqKlQ=,iv:4Skdj8l9jlTX9Unc2xE2hCKVawHBnHR8L4kZA6H8xNw=,tag:zJsJ3S//faAn7AGwLefNoA==,type:str]", | ||
| 20 | "pgp": null, | ||
| 21 | "unencrypted_suffix": "_unencrypted", | ||
| 22 | "version": "3.9.4" | ||
| 23 | } | ||
| 24 | } \ No newline at end of file | ||
diff --git a/hosts/vidhar/pgbackrest/default.nix b/hosts/vidhar/pgbackrest/default.nix index ffb149f5..1e0828ce 100644 --- a/hosts/vidhar/pgbackrest/default.nix +++ b/hosts/vidhar/pgbackrest/default.nix | |||
| @@ -130,8 +130,9 @@ in { | |||
| 130 | }; | 130 | }; |
| 131 | 131 | ||
| 132 | systemd.tmpfiles.rules = [ | 132 | systemd.tmpfiles.rules = [ |
| 133 | "d /var/lib/pgbackrest 0750 pgbackrest pgbackrest - -" | 133 | "d /var/lib/pgbackrest 0770 pgbackrest pgbackrest - -" |
| 134 | "d /var/spool/pgbackrest 0750 pgbackrest pgbackrest - -" | 134 | "d /var/spool/pgbackrest 0770 pgbackrest pgbackrest - -" |
| 135 | "d /tmp/pgbackrest 0770 pgbackrest pgbackrest - -" | ||
| 135 | ]; | 136 | ]; |
| 136 | 137 | ||
| 137 | users = { | 138 | users = { |
| @@ -141,7 +142,9 @@ in { | |||
| 141 | isSystemUser = true; | 142 | isSystemUser = true; |
| 142 | home = "/var/lib/pgbackrest"; | 143 | home = "/var/lib/pgbackrest"; |
| 143 | }; | 144 | }; |
| 144 | groups.pgbackrest = {}; | 145 | groups.pgbackrest = { |
| 146 | members = [ "postgres" ]; | ||
| 147 | }; | ||
| 145 | }; | 148 | }; |
| 146 | 149 | ||
| 147 | systemd.services."pgbackrest-tls-server".serviceConfig = { | 150 | systemd.services."pgbackrest-tls-server".serviceConfig = { |
diff --git a/hosts/vidhar/postgresql.nix b/hosts/vidhar/postgresql.nix new file mode 100644 index 00000000..7e44e69f --- /dev/null +++ b/hosts/vidhar/postgresql.nix | |||
| @@ -0,0 +1,36 @@ | |||
| 1 | { pkgs, config, flake, flakeInputs, ... }: | ||
| 2 | |||
| 3 | let | ||
| 4 | nixpkgs-pgbackrest = import (flakeInputs.nixpkgs-pgbackrest.outPath + "/pkgs/top-level") { | ||
| 5 | overlays = [ flake.overlays.libdscp ]; | ||
| 6 | localSystem = config.nixpkgs.system; | ||
| 7 | }; | ||
| 8 | in { | ||
| 9 | config = { | ||
| 10 | services.postgresql = { | ||
| 11 | enable = true; | ||
| 12 | package = pkgs.postgresql_15; | ||
| 13 | }; | ||
| 14 | |||
| 15 | services.pgbackrest = { | ||
| 16 | settings."vidhar" = { | ||
| 17 | pg1-path = config.services.postgresql.dataDir; | ||
| 18 | |||
| 19 | repo1-path = "/var/lib/pgbackrest"; | ||
| 20 | repo1-retention-full-type = "time"; | ||
| 21 | repo1-retention-full = 14; | ||
| 22 | repo1-retention-archive = 7; | ||
| 23 | }; | ||
| 24 | |||
| 25 | backups."vidhar-daily" = { | ||
| 26 | stanza = "vidhar"; | ||
| 27 | repo = "1"; | ||
| 28 | timerConfig.OnCalendar = "daily"; | ||
| 29 | }; | ||
| 30 | }; | ||
| 31 | |||
| 32 | systemd.services.postgresql.serviceConfig = { | ||
| 33 | ReadWritePaths = [ "/var/spool/pgbackrest" "/var/lib/pgbackrest/archive/vidhar" ]; | ||
| 34 | }; | ||
| 35 | }; | ||
| 36 | } | ||
diff --git a/hosts/vidhar/prometheus/default.nix b/hosts/vidhar/prometheus/default.nix index d368ad52..df135b58 100644 --- a/hosts/vidhar/prometheus/default.nix +++ b/hosts/vidhar/prometheus/default.nix | |||
| @@ -26,7 +26,8 @@ in { | |||
| 26 | enable = true; | 26 | enable = true; |
| 27 | 27 | ||
| 28 | extraFlags = [ | 28 | extraFlags = [ |
| 29 | "--enable-feature=remote-write-receiver" | 29 | "--web.enable-remote-write-receiver" |
| 30 | "--storage.tsdb.retention.size=35GB" | ||
| 30 | ]; | 31 | ]; |
| 31 | 32 | ||
| 32 | exporters = { | 33 | exporters = { |
| @@ -144,6 +145,17 @@ in { | |||
| 144 | ]; | 145 | ]; |
| 145 | scrape_interval = "15s"; | 146 | scrape_interval = "15s"; |
| 146 | } | 147 | } |
| 148 | { job_name = "zte"; | ||
| 149 | static_configs = [ | ||
| 150 | { targets = ["localhost:9900"]; } | ||
| 151 | ]; | ||
| 152 | relabel_configs = [ | ||
| 153 | { replacement = "dsl01"; | ||
| 154 | target_label = "instance"; | ||
| 155 | } | ||
| 156 | ]; | ||
| 157 | scrape_interval = "15s"; | ||
| 158 | } | ||
| 147 | { job_name = "unbound"; | 159 | { job_name = "unbound"; |
| 148 | static_configs = [ | 160 | static_configs = [ |
| 149 | { targets = ["localhost:${toString config.services.prometheus.exporters.unbound.port}"]; } | 161 | { targets = ["localhost:${toString config.services.prometheus.exporters.unbound.port}"]; } |
| @@ -287,6 +299,22 @@ in { | |||
| 287 | } | 299 | } |
| 288 | ]; | 300 | ]; |
| 289 | } | 301 | } |
| 302 | { name = "dsl-disconnects"; | ||
| 303 | rules = [ | ||
| 304 | { record = "dsl_uptime_seconds:resets_per_hour"; | ||
| 305 | expr = "resets(dsl_uptime_seconds[1h])"; | ||
| 306 | } | ||
| 307 | { record = "dsl_uptime_seconds:resets_per_day"; | ||
| 308 | expr = "resets(dsl_uptime_seconds[1d])"; | ||
| 309 | } | ||
| 310 | { record = "dsl_uptime_seconds:resets_per_week"; | ||
| 311 | expr = "resets(dsl_uptime_seconds[1w])"; | ||
| 312 | } | ||
| 313 | { record = "dsl_uptime_seconds:avg_resets_per_day"; | ||
| 314 | expr = "avg_over_time(dsl_uptime_seconds:resets_per_day[1w])"; | ||
| 315 | } | ||
| 316 | ]; | ||
| 317 | } | ||
| 290 | ]; | 318 | ]; |
| 291 | }) | 319 | }) |
| 292 | ]; | 320 | ]; |
| @@ -424,6 +452,47 @@ in { | |||
| 424 | }; | 452 | }; |
| 425 | }; | 453 | }; |
| 426 | 454 | ||
| 455 | systemd.services."prometheus-zte-exporter@dsl01.mgmt.yggdrasil" = { | ||
| 456 | wantedBy = [ "multi-user.target" ]; | ||
| 457 | after = [ "network.target" ]; | ||
| 458 | serviceConfig = { | ||
| 459 | Restart = "always"; | ||
| 460 | PrivateTmp = true; | ||
| 461 | WorkingDirectory = "/tmp"; | ||
| 462 | DynamicUser = true; | ||
| 463 | CapabilityBoundingSet = [""]; | ||
| 464 | DeviceAllow = [""]; | ||
| 465 | LockPersonality = true; | ||
| 466 | MemoryDenyWriteExecute = true; | ||
| 467 | NoNewPrivileges = true; | ||
| 468 | PrivateDevices = true; | ||
| 469 | ProtectClock = true; | ||
| 470 | ProtectControlGroups = true; | ||
| 471 | ProtectHome = true; | ||
| 472 | ProtectHostname = true; | ||
| 473 | ProtectKernelLogs = true; | ||
| 474 | ProtectKernelModules = true; | ||
| 475 | ProtectKernelTunables = true; | ||
| 476 | ProtectSystem = "strict"; | ||
| 477 | RemoveIPC = true; | ||
| 478 | RestrictAddressFamilies = [ "AF_INET" "AF_INET6" ]; | ||
| 479 | RestrictNamespaces = true; | ||
| 480 | RestrictRealtime = true; | ||
| 481 | RestrictSUIDSGID = true; | ||
| 482 | SystemCallArchitectures = "native"; | ||
| 483 | UMask = "0077"; | ||
| 484 | |||
| 485 | Type = "simple"; | ||
| 486 | ExecStart = "${pkgs.zte-prometheus-exporter}/bin/zte-prometheus-exporter"; | ||
| 487 | Environment = "ZTE_BASEURL=http://10.141.1.3 ZTE_HOSTNAME=localhost ZTE_PORT=9900"; | ||
| 488 | EnvironmentFile = config.sops.secrets."zte_dsl01.mgmt.yggdrasil".path; | ||
| 489 | }; | ||
| 490 | }; | ||
| 491 | sops.secrets."zte_dsl01.mgmt.yggdrasil" = { | ||
| 492 | format = "binary"; | ||
| 493 | sopsFile = ./zte_dsl01.mgmt.yggdrasil; | ||
| 494 | }; | ||
| 495 | |||
| 427 | services.nginx = { | 496 | services.nginx = { |
| 428 | upstreams.prometheus = { | 497 | upstreams.prometheus = { |
| 429 | servers = { "localhost:${toString config.services.prometheus.port}" = {}; }; | 498 | servers = { "localhost:${toString config.services.prometheus.port}" = {}; }; |
diff --git a/hosts/vidhar/prometheus/zte_dsl01.mgmt.yggdrasil b/hosts/vidhar/prometheus/zte_dsl01.mgmt.yggdrasil new file mode 100644 index 00000000..1c9c1fe0 --- /dev/null +++ b/hosts/vidhar/prometheus/zte_dsl01.mgmt.yggdrasil | |||
| @@ -0,0 +1,26 @@ | |||
| 1 | { | ||
| 2 | "data": "ENC[AES256_GCM,data:nAsn7dhfDr0+V1cJjpqWn/kJQt2zGjlfQKi3n5speroJkL3IvMG/9fsTaXJQZSi2gPlrN8GbxKQ=,iv:9g0V3xRBC+sa/JPP2bUZMfg//VuKT5qI7ua9iU4QRCg=,tag:fzwih9OHUBLmx8dxL4BjGg==,type:str]", | ||
| 3 | "sops": { | ||
| 4 | "kms": null, | ||
| 5 | "gcp_kms": null, | ||
| 6 | "azure_kv": null, | ||
| 7 | "hc_vault": null, | ||
| 8 | "age": [ | ||
| 9 | { | ||
| 10 | "recipient": "age1qffdqvy9arld9zd5a5cylt0n98xhcns5shxhrhwjq5g4qa844ejselaa4l", | ||
| 11 | "enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBIaEE3bUFBY0xKSDUrVnc2\nbFpjSkNOSm56amJTNjdXcTljdDNRREhITm1NCjZrOUEwNFpxN2FmTVV5T2xCbENk\nMEFmVzlPZ29CTlJ4dVNCRUsyRFFseXcKLS0tIEhscVZ4VUVsaG9OUnBIRFE4WXA2\ncGFnbWpNMlNIQzFLc1Ryc1Z3NUl1bVUKi9zYBlF2vslGKu4GP368ApbvuxjZnQpF\nuOujXSNoEps21wY6xUENm+CbYbgaJjSgmb5c1IjAmnubVI4JVY9OyQ==\n-----END AGE ENCRYPTED FILE-----\n" | ||
| 12 | } | ||
| 13 | ], | ||
| 14 | "lastmodified": "2021-12-31T15:00:33Z", | ||
| 15 | "mac": "ENC[AES256_GCM,data:sw2NVXHLibbuOChgScLhSTjGZBjSoHpzIuRqfCW0eL3DwhL5CekG6T/oYu06KjNmxVjxwb3OmqECSU0TUvPn9ySOWwMSoBfyJpDoTHnZ+YOjOH351IOAMBNcBDJse7aLGRWW5YXKLDfmp8Dhg2hlMhCmkVwAquQjPhfmAdJfj64=,iv:wgM/BlRU2XJSGj7KvAo1WRamecffUDnFvv2+4twtsQY=,tag:0mXblJtTGMTvxndedws94A==,type:str]", | ||
| 16 | "pgp": [ | ||
| 17 | { | ||
| 18 | "created_at": "2023-01-30T10:58:49Z", | ||
| 19 | "enc": "-----BEGIN PGP MESSAGE-----\n\nhF4DXxoViZlp6dISAQdAcwl1Blp3J5wgpRJKbYI1G1yEZrRYeYuoDtYUh3ToMAQw\nd92/bIJJR5Ml91eDym9uBN0fFRRy72r6FOx4qZT7S4DhmuA84qCbASjF8bKSclc0\n0l4BBXvDS5Dz1Q7iYc+LxZjHASV1v73A+MaeCFvG/pjmHzF0z0EzBiAJD4ZWGcP0\nX2dDbjl+n9VFrvmeLRxQNh4XZW43iTXdRjwHDgm16zhd9X6VOVhr5UkC4Nyjq2Ar\n=4ZEa\n-----END PGP MESSAGE-----\n", | ||
| 20 | "fp": "30D3453B8CD02FE2A3E7C78C0FB536FB87AE8F51" | ||
| 21 | } | ||
| 22 | ], | ||
| 23 | "unencrypted_suffix": "_unencrypted", | ||
| 24 | "version": "3.7.1" | ||
| 25 | } | ||
| 26 | } \ No newline at end of file | ||
diff --git a/hosts/vidhar/zfs.nix b/hosts/vidhar/zfs.nix index 518c3287..9d667fd6 100644 --- a/hosts/vidhar/zfs.nix +++ b/hosts/vidhar/zfs.nix | |||
| @@ -34,7 +34,7 @@ with lib; | |||
| 34 | }; | 34 | }; |
| 35 | 35 | ||
| 36 | "/etc/zfs/zfs-list.cache" = | 36 | "/etc/zfs/zfs-list.cache" = |
| 37 | { device = "ssd-raid1/local/zfs-zfs--list.cache"; | 37 | { device = "ssd-raid1/local/etc-zfs-zfs--list.cache"; |
| 38 | fsType = "zfs"; | 38 | fsType = "zfs"; |
| 39 | neededForBoot = true; | 39 | neededForBoot = true; |
| 40 | }; | 40 | }; |
diff --git a/installer-profiles/cd-dvd.nix b/installer-profiles/cd-dvd.nix index 45291bad..ac12d885 100644 --- a/installer-profiles/cd-dvd.nix +++ b/installer-profiles/cd-dvd.nix | |||
| @@ -1,7 +1,13 @@ | |||
| 1 | { flakeInputs, ... }: | 1 | { flakeInputs, lib, ... }: |
| 2 | 2 | ||
| 3 | { | 3 | { |
| 4 | imports = [ | 4 | imports = [ |
| 5 | "${flakeInputs.nixpkgs.outPath}/nixos/modules/installer/cd-dvd/installation-cd-minimal.nix" | 5 | "${flakeInputs.nixpkgs.outPath}/nixos/modules/installer/cd-dvd/installation-cd-minimal.nix" |
| 6 | ]; | 6 | ]; |
| 7 | |||
| 8 | config = { | ||
| 9 | isoImage.squashfsCompression = "zstd -Xcompression-level 9"; | ||
| 10 | system.installer.channel.enable = false; | ||
| 11 | boot.loader.grub.memtest86.enable = lib.mkForce false; | ||
| 12 | }; | ||
| 7 | } | 13 | } |
diff --git a/installer-profiles/netboot.nix b/installer-profiles/netboot.nix index 28e8084d..6e39ebfb 100644 --- a/installer-profiles/netboot.nix +++ b/installer-profiles/netboot.nix | |||
| @@ -4,4 +4,9 @@ | |||
| 4 | imports = [ | 4 | imports = [ |
| 5 | "${flakeInputs.nixpkgs.outPath}/nixos/modules/installer/netboot/netboot-minimal.nix" | 5 | "${flakeInputs.nixpkgs.outPath}/nixos/modules/installer/netboot/netboot-minimal.nix" |
| 6 | ]; | 6 | ]; |
| 7 | |||
| 8 | config = { | ||
| 9 | netboot.squashfsCompression = "zstd -Xcompression-level 9"; | ||
| 10 | system.installer.channel.enable = false; | ||
| 11 | }; | ||
| 7 | } | 12 | } |
diff --git a/installer-profiles/nfsroot.nix b/installer-profiles/nfsroot.nix index 6bd875b4..a8f6def6 100644 --- a/installer-profiles/nfsroot.nix +++ b/installer-profiles/nfsroot.nix | |||
| @@ -8,4 +8,6 @@ | |||
| 8 | "${flakeInputs.nixpkgs.outPath}/nixos/modules/profiles/base.nix" | 8 | "${flakeInputs.nixpkgs.outPath}/nixos/modules/profiles/base.nix" |
| 9 | "${flakeInputs.nixpkgs.outPath}/nixos/modules/profiles/installation-device.nix" | 9 | "${flakeInputs.nixpkgs.outPath}/nixos/modules/profiles/installation-device.nix" |
| 10 | ]; | 10 | ]; |
| 11 | |||
| 12 | config.system.installer.channel.enable = false; | ||
| 11 | } | 13 | } |
diff --git a/installer/default.nix b/installer/default.nix index cd1ee064..26f38572 100644 --- a/installer/default.nix +++ b/installer/default.nix | |||
| @@ -8,7 +8,7 @@ with lib; | |||
| 8 | ]; | 8 | ]; |
| 9 | 9 | ||
| 10 | config = { | 10 | config = { |
| 11 | boot.initrd.availableKernelModules = [ "e1000e" ]; | 11 | boot.initrd.kernelModules = [ "e1000e" "virtio_net" ]; |
| 12 | 12 | ||
| 13 | hardware.cpu.amd.updateMicrocode = config.hardware.enableRedistributableFirmware; | 13 | hardware.cpu.amd.updateMicrocode = config.hardware.enableRedistributableFirmware; |
| 14 | 14 | ||
| @@ -47,7 +47,7 @@ with lib; | |||
| 47 | services.xserver.videoDrivers = [ "nvidia" ]; | 47 | services.xserver.videoDrivers = [ "nvidia" ]; |
| 48 | systemd.services.nvidia-control-devices = { | 48 | systemd.services.nvidia-control-devices = { |
| 49 | wantedBy = [ "multi-user.target" ]; | 49 | wantedBy = [ "multi-user.target" ]; |
| 50 | serviceConfig.ExecStart = "${pkgs.linuxPackages.nvidia_x11.bin}/bin/nvidia-smi"; | 50 | serviceConfig.ExecStart = lib.getExe' pkgs.linuxPackages.nvidia_x11.bin "nvidia-smi"; |
| 51 | }; | 51 | }; |
| 52 | nixpkgs.externalConfig.allowUnfree = true; | 52 | nixpkgs.externalConfig.allowUnfree = true; |
| 53 | 53 | ||
| @@ -57,6 +57,10 @@ with lib; | |||
| 57 | 57 | ||
| 58 | system.disableInstallerTools = false; | 58 | system.disableInstallerTools = false; |
| 59 | 59 | ||
| 60 | xdg.autostart.enable = lib.mkForce false; | ||
| 61 | xdg.icons.enable = lib.mkForce false; | ||
| 62 | xdg.mime.enable = lib.mkForce false; | ||
| 63 | |||
| 60 | systemd.sysusers.enable = false; | 64 | systemd.sysusers.enable = false; |
| 61 | system.machine-id.generate.enable = false; | 65 | system.machine-id.generate.enable = false; |
| 62 | system.stateVersion = config.system.nixos.release; # No state in installer | 66 | system.stateVersion = config.system.nixos.release; # No state in installer |
diff --git a/lib/pythonSet.nix b/lib/pythonSet.nix new file mode 100644 index 00000000..c69216cc --- /dev/null +++ b/lib/pythonSet.nix | |||
| @@ -0,0 +1,42 @@ | |||
| 1 | { uv2nix, pyproject-nix, pyproject-build-systems, ... }: | ||
| 2 | { pkgs, python, overlay, lib ? pkgs.lib }: | ||
| 3 | (pkgs.callPackage pyproject-nix.build.packages { | ||
| 4 | inherit python; | ||
| 5 | }).overrideScope | ||
| 6 | ( | ||
| 7 | lib.composeManyExtensions [ | ||
| 8 | pyproject-build-systems.overlays.default | ||
| 9 | overlay | ||
| 10 | (final: prev: { | ||
| 11 | sdnotify = (prev.sdnotify.override { | ||
| 12 | sourcePreference = "sdist"; | ||
| 13 | }).overrideAttrs (oldAttrs: { | ||
| 14 | nativeBuildInputs = (oldAttrs.nativeBuildInputs or []) ++ [ | ||
| 15 | (final.resolveBuildSystem { setuptools = []; }) | ||
| 16 | ]; | ||
| 17 | }); | ||
| 18 | systemd-python = (prev.systemd-python.override { | ||
| 19 | sourcePreference = "sdist"; | ||
| 20 | }).overrideAttrs (oldAttrs: { | ||
| 21 | nativeBuildInputs = (oldAttrs.nativeBuildInputs or []) ++ [ | ||
| 22 | pkgs.pkg-config pkgs.systemd.dev | ||
| 23 | (final.resolveBuildSystem { setuptools = []; }) | ||
| 24 | ]; | ||
| 25 | }); | ||
| 26 | halo = (prev.halo.override { | ||
| 27 | sourcePreference = "sdist"; | ||
| 28 | }).overrideAttrs (oldAttrs: { | ||
| 29 | nativeBuildInputs = (oldAttrs.nativeBuildInputs or []) ++ [ | ||
| 30 | (final.resolveBuildSystem { setuptools = []; }) | ||
| 31 | ]; | ||
| 32 | }); | ||
| 33 | unshare = (prev.unshare.override { | ||
| 34 | sourcePreference = "sdist"; | ||
| 35 | }).overrideAttrs (oldAttrs: { | ||
| 36 | nativeBuildInputs = (oldAttrs.nativeBuildInputs or []) ++ [ | ||
| 37 | (final.resolveBuildSystem { setuptools = []; }) | ||
| 38 | ]; | ||
| 39 | }); | ||
| 40 | }) | ||
| 41 | ] | ||
| 42 | ) | ||
diff --git a/modules/abs-podcast-autoplaylist.nix b/modules/abs-podcast-autoplaylist.nix new file mode 100644 index 00000000..f526a434 --- /dev/null +++ b/modules/abs-podcast-autoplaylist.nix | |||
| @@ -0,0 +1,55 @@ | |||
| 1 | { config, pkgs, lib, utils, ... }: | ||
| 2 | |||
| 3 | let | ||
| 4 | cfg = config.services.abs-podcast-autoplaylist; | ||
| 5 | |||
| 6 | enabledAttrs = lib.filterAttrs (_name: { enable, ... }: enable) cfg; | ||
| 7 | in { | ||
| 8 | options = { | ||
| 9 | services.abs-podcast-autoplaylist = lib.mkOption { | ||
| 10 | default = {}; | ||
| 11 | type = lib.types.attrsOf (lib.types.submodule ({ name, ... }: { | ||
| 12 | options = { | ||
| 13 | enable = lib.mkEnableOption "this instance of abs-podcast-autoplaylist" // { | ||
| 14 | default = true; | ||
| 15 | }; | ||
| 16 | cron = lib.mkOption { | ||
| 17 | type = lib.types.str; | ||
| 18 | default = "*-*-* *:00/30:00"; | ||
| 19 | }; | ||
| 20 | configSecret = lib.mkOption { | ||
| 21 | type = lib.types.str; | ||
| 22 | default = "abs-podcast-autoplaylist-${name}.toml"; | ||
| 23 | }; | ||
| 24 | }; | ||
| 25 | })); | ||
| 26 | }; | ||
| 27 | }; | ||
| 28 | |||
| 29 | config = lib.mkIf (enabledAttrs != {}) { | ||
| 30 | systemd.services = { | ||
| 31 | "abs-podcast-autoplaylist@" = { | ||
| 32 | serviceConfig = { | ||
| 33 | WorkingDirectory = "%d"; | ||
| 34 | DynamicUser = true; | ||
| 35 | ProtectHome = true; | ||
| 36 | PrivateTmp = true; | ||
| 37 | PrivateDevices = true; | ||
| 38 | Type = "oneshot"; | ||
| 39 | ExecStart = "${lib.getExe pkgs.abs-podcast-autoplaylist} %I.toml"; | ||
| 40 | TimeoutSec = "5min"; | ||
| 41 | }; | ||
| 42 | }; | ||
| 43 | } // lib.mapAttrs' (name: { configSecret, ... }: lib.nameValuePair "abs-podcast-autoplaylist@${utils.escapeSystemdPath name}" { | ||
| 44 | overrideStrategy = "asDropin"; | ||
| 45 | serviceConfig = { | ||
| 46 | LoadCredential = "${name}.toml:${config.sops.secrets.${configSecret}.path}"; | ||
| 47 | }; | ||
| 48 | }) enabledAttrs; | ||
| 49 | |||
| 50 | systemd.timers = lib.mapAttrs' (name: { cron, ... }: lib.nameValuePair "abs-podcast-autoplaylist@${utils.escapeSystemdPath name}" { | ||
| 51 | wantedBy = [ "timers.target" ]; | ||
| 52 | timerConfig.OnCalendar = cron; | ||
| 53 | }) enabledAttrs; | ||
| 54 | }; | ||
| 55 | } | ||
diff --git a/modules/backup-utils.nix b/modules/backup-utils.nix index 82a42ecd..698140da 100644 --- a/modules/backup-utils.nix +++ b/modules/backup-utils.nix | |||
| @@ -9,5 +9,8 @@ with lib; | |||
| 9 | 9 | ||
| 10 | config = { | 10 | config = { |
| 11 | services.borgsnap.archive-prefix = mkDefault "yggdrasil.${hostName}."; | 11 | services.borgsnap.archive-prefix = mkDefault "yggdrasil.${hostName}."; |
| 12 | |||
| 13 | systemd.services."zfssnap-prune".restartIfChanged = false; | ||
| 14 | systemd.services."zfssnap".restartIfChanged = false; | ||
| 12 | }; | 15 | }; |
| 13 | } | 16 | } |
diff --git a/modules/borgcopy/.envrc b/modules/borgcopy/.envrc new file mode 100644 index 00000000..01e755c1 --- /dev/null +++ b/modules/borgcopy/.envrc | |||
| @@ -0,0 +1,4 @@ | |||
| 1 | use flake | ||
| 2 | |||
| 3 | uv venv && uv sync | ||
| 4 | . .venv/bin/activate | ||
diff --git a/modules/borgcopy/.gitignore b/modules/borgcopy/.gitignore new file mode 100644 index 00000000..4ccfae70 --- /dev/null +++ b/modules/borgcopy/.gitignore | |||
| @@ -0,0 +1,2 @@ | |||
| 1 | .venv | ||
| 2 | **/__pycache__ | ||
diff --git a/modules/borgcopy/default.nix b/modules/borgcopy/default.nix index 475edbd9..af021777 100644 --- a/modules/borgcopy/default.nix +++ b/modules/borgcopy/default.nix | |||
| @@ -1,27 +1,35 @@ | |||
| 1 | { config, pkgs, lib, utils, flakeInputs, ... }: | 1 | { config, pkgs, lib, utils, flake, flakeInputs, ... }: |
| 2 | 2 | ||
| 3 | with lib; | 3 | with lib; |
| 4 | 4 | ||
| 5 | let | 5 | let |
| 6 | copyBorg = | 6 | copyBorg = let |
| 7 | with pkgs.poetry2nix; | 7 | workspace = flakeInputs.uv2nix.lib.workspace.loadWorkspace { workspaceRoot = ./.; }; |
| 8 | mkPoetryApplication { | 8 | pythonSet = flake.lib.pythonSet { |
| 9 | projectDir = cleanPythonSources { src = ./.; }; | 9 | inherit pkgs; |
| 10 | python = pkgs.python312; | ||
| 11 | overlay = workspace.mkPyprojectOverlay { | ||
| 12 | sourcePreference = "wheel"; | ||
| 13 | }; | ||
| 14 | }; | ||
| 15 | virtualEnv = pythonSet.mkVirtualEnv "copy_borg" workspace.deps.default; | ||
| 16 | in virtualEnv.overrideAttrs (oldAttrs: { | ||
| 17 | meta = (oldAttrs.meta or {}) // { | ||
| 18 | mainProgram = "copy_borg"; | ||
| 19 | }; | ||
| 10 | 20 | ||
| 11 | overrides = overrides.withDefaults (self: super: { | 21 | nativeBuildInputs = (oldAttrs.nativeBuildInputs or []) ++ [ pkgs.makeWrapper ]; |
| 12 | pyprctl = super.pyprctl.overridePythonAttrs (oldAttrs: { | ||
| 13 | buildInputs = (oldAttrs.buildInputs or []) ++ [super.setuptools]; | ||
| 14 | }); | ||
| 15 | inherit (pkgs.python3Packages) python-unshare; | ||
| 16 | }); | ||
| 17 | 22 | ||
| 18 | postInstall = '' | 23 | postInstall = '' |
| 19 | wrapProgram $out/bin/copy_borg \ | 24 | ${oldAttrs.postInstall or ""} |
| 20 | --prefix PATH : ${makeBinPath (with pkgs; [util-linux borgbackup])}:${config.security.wrapperDir} | 25 | |
| 21 | ''; | 26 | wrapProgram $out/bin/copy_borg \ |
| 22 | }; | 27 | --prefix PATH : ${makeBinPath (with pkgs; [util-linux borgbackup])}:${config.security.wrapperDir} |
| 28 | ''; | ||
| 29 | }); | ||
| 23 | 30 | ||
| 24 | copyService = name: opts: nameValuePair "copy-borg@${utils.escapeSystemdPath name}" { | 31 | copyService = name: opts: nameValuePair "copy-borg@${utils.escapeSystemdPath name}" { |
| 32 | restartIfChanged = false; | ||
| 25 | serviceConfig = { | 33 | serviceConfig = { |
| 26 | Type = "oneshot"; | 34 | Type = "oneshot"; |
| 27 | ExecStart = "${copyBorg}/bin/copy_borg --verbosity ${toString opts.verbosity} ${utils.escapeSystemdExecArgs [opts.from opts.to]}"; | 35 | ExecStart = "${copyBorg}/bin/copy_borg --verbosity ${toString opts.verbosity} ${utils.escapeSystemdExecArgs [opts.from opts.to]}"; |
diff --git a/modules/borgcopy/poetry.lock b/modules/borgcopy/poetry.lock deleted file mode 100644 index 759ecfe9..00000000 --- a/modules/borgcopy/poetry.lock +++ /dev/null | |||
| @@ -1,180 +0,0 @@ | |||
| 1 | # This file is automatically @generated by Poetry 1.4.2 and should not be changed by hand. | ||
| 2 | |||
| 3 | [[package]] | ||
| 4 | name = "colorama" | ||
| 5 | version = "0.4.6" | ||
| 6 | description = "Cross-platform colored terminal text." | ||
| 7 | category = "main" | ||
| 8 | optional = false | ||
| 9 | python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" | ||
| 10 | files = [ | ||
| 11 | {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, | ||
| 12 | {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, | ||
| 13 | ] | ||
| 14 | |||
| 15 | [[package]] | ||
| 16 | name = "halo" | ||
| 17 | version = "0.0.31" | ||
| 18 | description = "Beautiful terminal spinners in Python" | ||
| 19 | category = "main" | ||
| 20 | optional = false | ||
| 21 | python-versions = ">=3.4" | ||
| 22 | files = [ | ||
| 23 | {file = "halo-0.0.31-py2-none-any.whl", hash = "sha256:5350488fb7d2aa7c31a1344120cee67a872901ce8858f60da7946cef96c208ab"}, | ||
| 24 | {file = "halo-0.0.31.tar.gz", hash = "sha256:7b67a3521ee91d53b7152d4ee3452811e1d2a6321975137762eb3d70063cc9d6"}, | ||
| 25 | ] | ||
| 26 | |||
| 27 | [package.dependencies] | ||
| 28 | colorama = ">=0.3.9" | ||
| 29 | log-symbols = ">=0.0.14" | ||
| 30 | six = ">=1.12.0" | ||
| 31 | spinners = ">=0.0.24" | ||
| 32 | termcolor = ">=1.1.0" | ||
| 33 | |||
| 34 | [package.extras] | ||
| 35 | ipython = ["IPython (==5.7.0)", "ipywidgets (==7.1.0)"] | ||
| 36 | |||
| 37 | [[package]] | ||
| 38 | name = "humanize" | ||
| 39 | version = "4.6.0" | ||
| 40 | description = "Python humanize utilities" | ||
| 41 | category = "main" | ||
| 42 | optional = false | ||
| 43 | python-versions = ">=3.7" | ||
| 44 | files = [ | ||
| 45 | {file = "humanize-4.6.0-py3-none-any.whl", hash = "sha256:401201aca462749773f02920139f302450cb548b70489b9b4b92be39fe3c3c50"}, | ||
| 46 | {file = "humanize-4.6.0.tar.gz", hash = "sha256:5f1f22bc65911eb1a6ffe7659bd6598e33dcfeeb904eb16ee1e705a09bf75916"}, | ||
| 47 | ] | ||
| 48 | |||
| 49 | [package.extras] | ||
| 50 | tests = ["freezegun", "pytest", "pytest-cov"] | ||
| 51 | |||
| 52 | [[package]] | ||
| 53 | name = "log-symbols" | ||
| 54 | version = "0.0.14" | ||
| 55 | description = "Colored symbols for various log levels for Python" | ||
| 56 | category = "main" | ||
| 57 | optional = false | ||
| 58 | python-versions = "*" | ||
| 59 | files = [ | ||
| 60 | {file = "log_symbols-0.0.14-py3-none-any.whl", hash = "sha256:4952106ff8b605ab7d5081dd2c7e6ca7374584eff7086f499c06edd1ce56dcca"}, | ||
| 61 | {file = "log_symbols-0.0.14.tar.gz", hash = "sha256:cf0bbc6fe1a8e53f0d174a716bc625c4f87043cc21eb55dd8a740cfe22680556"}, | ||
| 62 | ] | ||
| 63 | |||
| 64 | [package.dependencies] | ||
| 65 | colorama = ">=0.3.9" | ||
| 66 | |||
| 67 | [[package]] | ||
| 68 | name = "pyprctl" | ||
| 69 | version = "0.1.3" | ||
| 70 | description = "An interface to Linux's prctl() syscall written in pure Python using ctypes." | ||
| 71 | category = "main" | ||
| 72 | optional = false | ||
| 73 | python-versions = ">=3.6" | ||
| 74 | files = [ | ||
| 75 | {file = "pyprctl-0.1.3-py3-none-any.whl", hash = "sha256:6302e5114f078fb33e5799835d0a69e2fc180bb6b28ad073515fa40c5272f1dd"}, | ||
| 76 | {file = "pyprctl-0.1.3.tar.gz", hash = "sha256:1fb54d3ab030ec02e4afc38fb9662d6634c12834e91ae7959de56a9c09f69c26"}, | ||
| 77 | ] | ||
| 78 | |||
| 79 | [[package]] | ||
| 80 | name = "python-dateutil" | ||
| 81 | version = "2.8.2" | ||
| 82 | description = "Extensions to the standard Python datetime module" | ||
| 83 | category = "main" | ||
| 84 | optional = false | ||
| 85 | python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" | ||
| 86 | files = [ | ||
| 87 | {file = "python-dateutil-2.8.2.tar.gz", hash = "sha256:0123cacc1627ae19ddf3c27a5de5bd67ee4586fbdd6440d9748f8abb483d3e86"}, | ||
| 88 | {file = "python_dateutil-2.8.2-py2.py3-none-any.whl", hash = "sha256:961d03dc3453ebbc59dbdea9e4e11c5651520a876d0f4db161e8674aae935da9"}, | ||
| 89 | ] | ||
| 90 | |||
| 91 | [package.dependencies] | ||
| 92 | six = ">=1.5" | ||
| 93 | |||
| 94 | [[package]] | ||
| 95 | name = "python-unshare" | ||
| 96 | version = "0.2" | ||
| 97 | description = "Python bindings for the Linux unshare() syscall" | ||
| 98 | category = "main" | ||
| 99 | optional = false | ||
| 100 | python-versions = "*" | ||
| 101 | files = [ | ||
| 102 | {file = "python-unshare-0.2.tar.gz", hash = "sha256:f79b7de441b6c27930b775085a6a4fd2f378b628737aaaebc2a6c519023fd47a"}, | ||
| 103 | ] | ||
| 104 | |||
| 105 | [[package]] | ||
| 106 | name = "six" | ||
| 107 | version = "1.16.0" | ||
| 108 | description = "Python 2 and 3 compatibility utilities" | ||
| 109 | category = "main" | ||
| 110 | optional = false | ||
| 111 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*" | ||
| 112 | files = [ | ||
| 113 | {file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"}, | ||
| 114 | {file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"}, | ||
| 115 | ] | ||
| 116 | |||
| 117 | [[package]] | ||
| 118 | name = "spinners" | ||
| 119 | version = "0.0.24" | ||
| 120 | description = "Spinners for terminals" | ||
| 121 | category = "main" | ||
| 122 | optional = false | ||
| 123 | python-versions = "*" | ||
| 124 | files = [ | ||
| 125 | {file = "spinners-0.0.24-py3-none-any.whl", hash = "sha256:2fa30d0b72c9650ad12bbe031c9943b8d441e41b4f5602b0ec977a19f3290e98"}, | ||
| 126 | {file = "spinners-0.0.24.tar.gz", hash = "sha256:1eb6aeb4781d72ab42ed8a01dcf20f3002bf50740d7154d12fb8c9769bf9e27f"}, | ||
| 127 | ] | ||
| 128 | |||
| 129 | [[package]] | ||
| 130 | name = "termcolor" | ||
| 131 | version = "2.2.0" | ||
| 132 | description = "ANSI color formatting for output in terminal" | ||
| 133 | category = "main" | ||
| 134 | optional = false | ||
| 135 | python-versions = ">=3.7" | ||
| 136 | files = [ | ||
| 137 | {file = "termcolor-2.2.0-py3-none-any.whl", hash = "sha256:91ddd848e7251200eac969846cbae2dacd7d71c2871e92733289e7e3666f48e7"}, | ||
| 138 | {file = "termcolor-2.2.0.tar.gz", hash = "sha256:dfc8ac3f350788f23b2947b3e6cfa5a53b630b612e6cd8965a015a776020b99a"}, | ||
| 139 | ] | ||
| 140 | |||
| 141 | [package.extras] | ||
| 142 | tests = ["pytest", "pytest-cov"] | ||
| 143 | |||
| 144 | [[package]] | ||
| 145 | name = "tqdm" | ||
| 146 | version = "4.65.0" | ||
| 147 | description = "Fast, Extensible Progress Meter" | ||
| 148 | category = "main" | ||
| 149 | optional = false | ||
| 150 | python-versions = ">=3.7" | ||
| 151 | files = [ | ||
| 152 | {file = "tqdm-4.65.0-py3-none-any.whl", hash = "sha256:c4f53a17fe37e132815abceec022631be8ffe1b9381c2e6e30aa70edc99e9671"}, | ||
| 153 | {file = "tqdm-4.65.0.tar.gz", hash = "sha256:1871fb68a86b8fb3b59ca4cdd3dcccbc7e6d613eeed31f4c332531977b89beb5"}, | ||
| 154 | ] | ||
| 155 | |||
| 156 | [package.dependencies] | ||
| 157 | colorama = {version = "*", markers = "platform_system == \"Windows\""} | ||
| 158 | |||
| 159 | [package.extras] | ||
| 160 | dev = ["py-make (>=0.1.0)", "twine", "wheel"] | ||
| 161 | notebook = ["ipywidgets (>=6)"] | ||
| 162 | slack = ["slack-sdk"] | ||
| 163 | telegram = ["requests"] | ||
| 164 | |||
| 165 | [[package]] | ||
| 166 | name = "xdg" | ||
| 167 | version = "6.0.0" | ||
| 168 | description = "Variables defined by the XDG Base Directory Specification" | ||
| 169 | category = "main" | ||
| 170 | optional = false | ||
| 171 | python-versions = ">=3.7,<4.0" | ||
| 172 | files = [ | ||
| 173 | {file = "xdg-6.0.0-py3-none-any.whl", hash = "sha256:df3510755b4395157fc04fc3b02467c777f3b3ca383257397f09ab0d4c16f936"}, | ||
| 174 | {file = "xdg-6.0.0.tar.gz", hash = "sha256:24278094f2d45e846d1eb28a2ebb92d7b67fc0cab5249ee3ce88c95f649a1c92"}, | ||
| 175 | ] | ||
| 176 | |||
| 177 | [metadata] | ||
| 178 | lock-version = "2.0" | ||
| 179 | python-versions = ">=3.10.0,<3.12" | ||
| 180 | content-hash = "3c6b538852447a8f3ae34e1be122716d47e669a2b44f7c5d3d850e5d877353c7" | ||
diff --git a/modules/borgcopy/pyproject.toml b/modules/borgcopy/pyproject.toml index f3401ed2..d76d73c6 100644 --- a/modules/borgcopy/pyproject.toml +++ b/modules/borgcopy/pyproject.toml | |||
| @@ -1,22 +1,25 @@ | |||
| 1 | [tool.poetry] | 1 | [project] |
| 2 | name = "copy_borg" | 2 | name = "copy_borg" |
| 3 | version = "0.0.0" | 3 | version = "0.0.0" |
| 4 | authors = ["Gregor Kleen <gkleen@yggdrasil.li>"] | ||
| 5 | description = "" | 4 | description = "" |
| 5 | authors = [{ name = "Gregor Kleen", email = "gkleen@yggdrasil.li" }] | ||
| 6 | requires-python = "~=3.12" | ||
| 7 | dependencies = [ | ||
| 8 | "humanize>=4.6.0,<5", | ||
| 9 | "tqdm>=4.65.0,<5", | ||
| 10 | "python-dateutil>=2.8.2,<3", | ||
| 11 | "xdg>=6.0.0,<7", | ||
| 12 | "pyprctl>=0.1.3,<0.2", | ||
| 13 | "halo>=0.0.31,<0.0.32", | ||
| 14 | "unshare>=0.22", | ||
| 15 | ] | ||
| 6 | 16 | ||
| 7 | [tool.poetry.scripts] | 17 | [project.scripts] |
| 8 | copy_borg = "copy_borg.__main__:main" | 18 | copy_borg = "copy_borg.__main__:main" |
| 9 | 19 | ||
| 10 | [tool.poetry.dependencies] | ||
| 11 | python = ">=3.10.0,<3.12" | ||
| 12 | humanize = "^4.6.0" | ||
| 13 | tqdm = "^4.65.0" | ||
| 14 | python-dateutil = "^2.8.2" | ||
| 15 | xdg = "^6.0.0" | ||
| 16 | python-unshare = "^0.2" | ||
| 17 | pyprctl = "^0.1.3" | ||
| 18 | halo = "^0.0.31" | ||
| 19 | |||
| 20 | [build-system] | 20 | [build-system] |
| 21 | requires = ["poetry-core>=1.0.0"] | 21 | requires = ["hatchling"] |
| 22 | build-backend = "poetry.core.masonry.api" \ No newline at end of file | 22 | build-backend = "hatchling.build" |
| 23 | |||
| 24 | [tool.hatch.build.targets.wheel] | ||
| 25 | packages = ["copy_borg"] | ||
diff --git a/modules/borgcopy/uv.lock b/modules/borgcopy/uv.lock new file mode 100644 index 00000000..1a282598 --- /dev/null +++ b/modules/borgcopy/uv.lock | |||
| @@ -0,0 +1,146 @@ | |||
| 1 | version = 1 | ||
| 2 | revision = 2 | ||
| 3 | requires-python = ">=3.12, <4" | ||
| 4 | |||
| 5 | [[package]] | ||
| 6 | name = "colorama" | ||
| 7 | version = "0.4.6" | ||
| 8 | source = { registry = "https://pypi.org/simple" } | ||
| 9 | sdist = { url = "https://files.pythonhosted.org/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", size = 27697, upload-time = "2022-10-25T02:36:22.414Z" } | ||
| 10 | wheels = [ | ||
| 11 | { url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335, upload-time = "2022-10-25T02:36:20.889Z" }, | ||
| 12 | ] | ||
| 13 | |||
| 14 | [[package]] | ||
| 15 | name = "copy-borg" | ||
| 16 | version = "0.0.0" | ||
| 17 | source = { editable = "." } | ||
| 18 | dependencies = [ | ||
| 19 | { name = "halo" }, | ||
| 20 | { name = "humanize" }, | ||
| 21 | { name = "pyprctl" }, | ||
| 22 | { name = "python-dateutil" }, | ||
| 23 | { name = "tqdm" }, | ||
| 24 | { name = "unshare" }, | ||
| 25 | { name = "xdg" }, | ||
| 26 | ] | ||
| 27 | |||
| 28 | [package.metadata] | ||
| 29 | requires-dist = [ | ||
| 30 | { name = "halo", specifier = ">=0.0.31,<0.0.32" }, | ||
| 31 | { name = "humanize", specifier = ">=4.6.0,<5" }, | ||
| 32 | { name = "pyprctl", specifier = ">=0.1.3,<0.2" }, | ||
| 33 | { name = "python-dateutil", specifier = ">=2.8.2,<3" }, | ||
| 34 | { name = "tqdm", specifier = ">=4.65.0,<5" }, | ||
| 35 | { name = "unshare", specifier = ">=0.22" }, | ||
| 36 | { name = "xdg", specifier = ">=6.0.0,<7" }, | ||
| 37 | ] | ||
| 38 | |||
| 39 | [[package]] | ||
| 40 | name = "halo" | ||
| 41 | version = "0.0.31" | ||
| 42 | source = { registry = "https://pypi.org/simple" } | ||
| 43 | dependencies = [ | ||
| 44 | { name = "colorama" }, | ||
| 45 | { name = "log-symbols" }, | ||
| 46 | { name = "six" }, | ||
| 47 | { name = "spinners" }, | ||
| 48 | { name = "termcolor" }, | ||
| 49 | ] | ||
| 50 | sdist = { url = "https://files.pythonhosted.org/packages/ee/48/d53580d30b1fabf25d0d1fcc3f5b26d08d2ac75a1890ff6d262f9f027436/halo-0.0.31.tar.gz", hash = "sha256:7b67a3521ee91d53b7152d4ee3452811e1d2a6321975137762eb3d70063cc9d6", size = 11666, upload-time = "2020-11-10T02:36:48.335Z" } | ||
| 51 | |||
| 52 | [[package]] | ||
| 53 | name = "humanize" | ||
| 54 | version = "4.12.3" | ||
| 55 | source = { registry = "https://pypi.org/simple" } | ||
| 56 | sdist = { url = "https://files.pythonhosted.org/packages/22/d1/bbc4d251187a43f69844f7fd8941426549bbe4723e8ff0a7441796b0789f/humanize-4.12.3.tar.gz", hash = "sha256:8430be3a615106fdfceb0b2c1b41c4c98c6b0fc5cc59663a5539b111dd325fb0", size = 80514, upload-time = "2025-04-30T11:51:07.98Z" } | ||
| 57 | wheels = [ | ||
| 58 | { url = "https://files.pythonhosted.org/packages/a0/1e/62a2ec3104394a2975a2629eec89276ede9dbe717092f6966fcf963e1bf0/humanize-4.12.3-py3-none-any.whl", hash = "sha256:2cbf6370af06568fa6d2da77c86edb7886f3160ecd19ee1ffef07979efc597f6", size = 128487, upload-time = "2025-04-30T11:51:06.468Z" }, | ||
| 59 | ] | ||
| 60 | |||
| 61 | [[package]] | ||
| 62 | name = "log-symbols" | ||
| 63 | version = "0.0.14" | ||
| 64 | source = { registry = "https://pypi.org/simple" } | ||
| 65 | dependencies = [ | ||
| 66 | { name = "colorama" }, | ||
| 67 | ] | ||
| 68 | sdist = { url = "https://files.pythonhosted.org/packages/45/87/e86645d758a4401c8c81914b6a88470634d1785c9ad09823fa4a1bd89250/log_symbols-0.0.14.tar.gz", hash = "sha256:cf0bbc6fe1a8e53f0d174a716bc625c4f87043cc21eb55dd8a740cfe22680556", size = 3211, upload-time = "2019-08-08T06:32:22.538Z" } | ||
| 69 | wheels = [ | ||
| 70 | { url = "https://files.pythonhosted.org/packages/28/5d/d710c38be68b0fb54e645048fe359c3904cc3cb64b2de9d40e1712bf110c/log_symbols-0.0.14-py3-none-any.whl", hash = "sha256:4952106ff8b605ab7d5081dd2c7e6ca7374584eff7086f499c06edd1ce56dcca", size = 3081, upload-time = "2019-08-08T06:32:20.604Z" }, | ||
| 71 | ] | ||
| 72 | |||
| 73 | [[package]] | ||
| 74 | name = "pyprctl" | ||
| 75 | version = "0.1.3" | ||
| 76 | source = { registry = "https://pypi.org/simple" } | ||
| 77 | sdist = { url = "https://files.pythonhosted.org/packages/c9/16/6ed71ebcad76c1cd5f22185bcc6b31c0ee62fc5e693b626febea8fedeba3/pyprctl-0.1.3.tar.gz", hash = "sha256:1fb54d3ab030ec02e4afc38fb9662d6634c12834e91ae7959de56a9c09f69c26", size = 18739, upload-time = "2021-10-26T23:52:03.87Z" } | ||
| 78 | wheels = [ | ||
| 79 | { url = "https://files.pythonhosted.org/packages/bf/5e/62765de39bbce8111fb1f4453a4a804913bf49179fa265fb713ed66c9d15/pyprctl-0.1.3-py3-none-any.whl", hash = "sha256:6302e5114f078fb33e5799835d0a69e2fc180bb6b28ad073515fa40c5272f1dd", size = 20016, upload-time = "2021-10-26T23:52:02.986Z" }, | ||
| 80 | ] | ||
| 81 | |||
| 82 | [[package]] | ||
| 83 | name = "python-dateutil" | ||
| 84 | version = "2.9.0.post0" | ||
| 85 | source = { registry = "https://pypi.org/simple" } | ||
| 86 | dependencies = [ | ||
| 87 | { name = "six" }, | ||
| 88 | ] | ||
| 89 | sdist = { url = "https://files.pythonhosted.org/packages/66/c0/0c8b6ad9f17a802ee498c46e004a0eb49bc148f2fd230864601a86dcf6db/python-dateutil-2.9.0.post0.tar.gz", hash = "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3", size = 342432, upload-time = "2024-03-01T18:36:20.211Z" } | ||
| 90 | wheels = [ | ||
| 91 | { url = "https://files.pythonhosted.org/packages/ec/57/56b9bcc3c9c6a792fcbaf139543cee77261f3651ca9da0c93f5c1221264b/python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427", size = 229892, upload-time = "2024-03-01T18:36:18.57Z" }, | ||
| 92 | ] | ||
| 93 | |||
| 94 | [[package]] | ||
| 95 | name = "six" | ||
| 96 | version = "1.17.0" | ||
| 97 | source = { registry = "https://pypi.org/simple" } | ||
| 98 | sdist = { url = "https://files.pythonhosted.org/packages/94/e7/b2c673351809dca68a0e064b6af791aa332cf192da575fd474ed7d6f16a2/six-1.17.0.tar.gz", hash = "sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81", size = 34031, upload-time = "2024-12-04T17:35:28.174Z" } | ||
| 99 | wheels = [ | ||
| 100 | { url = "https://files.pythonhosted.org/packages/b7/ce/149a00dd41f10bc29e5921b496af8b574d8413afcd5e30dfa0ed46c2cc5e/six-1.17.0-py2.py3-none-any.whl", hash = "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274", size = 11050, upload-time = "2024-12-04T17:35:26.475Z" }, | ||
| 101 | ] | ||
| 102 | |||
| 103 | [[package]] | ||
| 104 | name = "spinners" | ||
| 105 | version = "0.0.24" | ||
| 106 | source = { registry = "https://pypi.org/simple" } | ||
| 107 | sdist = { url = "https://files.pythonhosted.org/packages/d3/91/bb331f0a43e04d950a710f402a0986a54147a35818df0e1658551c8d12e1/spinners-0.0.24.tar.gz", hash = "sha256:1eb6aeb4781d72ab42ed8a01dcf20f3002bf50740d7154d12fb8c9769bf9e27f", size = 5308, upload-time = "2020-02-19T21:42:32.326Z" } | ||
| 108 | wheels = [ | ||
| 109 | { url = "https://files.pythonhosted.org/packages/9f/8e/3310207a68118000ca27ac878b8386123628b335ecb3d4bec4743357f0d1/spinners-0.0.24-py3-none-any.whl", hash = "sha256:2fa30d0b72c9650ad12bbe031c9943b8d441e41b4f5602b0ec977a19f3290e98", size = 5499, upload-time = "2020-02-19T21:42:30.876Z" }, | ||
| 110 | ] | ||
| 111 | |||
| 112 | [[package]] | ||
| 113 | name = "termcolor" | ||
| 114 | version = "3.1.0" | ||
| 115 | source = { registry = "https://pypi.org/simple" } | ||
| 116 | sdist = { url = "https://files.pythonhosted.org/packages/ca/6c/3d75c196ac07ac8749600b60b03f4f6094d54e132c4d94ebac6ee0e0add0/termcolor-3.1.0.tar.gz", hash = "sha256:6a6dd7fbee581909eeec6a756cff1d7f7c376063b14e4a298dc4980309e55970", size = 14324, upload-time = "2025-04-30T11:37:53.791Z" } | ||
| 117 | wheels = [ | ||
| 118 | { url = "https://files.pythonhosted.org/packages/4f/bd/de8d508070629b6d84a30d01d57e4a65c69aa7f5abe7560b8fad3b50ea59/termcolor-3.1.0-py3-none-any.whl", hash = "sha256:591dd26b5c2ce03b9e43f391264626557873ce1d379019786f99b0c2bee140aa", size = 7684, upload-time = "2025-04-30T11:37:52.382Z" }, | ||
| 119 | ] | ||
| 120 | |||
| 121 | [[package]] | ||
| 122 | name = "tqdm" | ||
| 123 | version = "4.67.1" | ||
| 124 | source = { registry = "https://pypi.org/simple" } | ||
| 125 | dependencies = [ | ||
| 126 | { name = "colorama", marker = "sys_platform == 'win32'" }, | ||
| 127 | ] | ||
| 128 | sdist = { url = "https://files.pythonhosted.org/packages/a8/4b/29b4ef32e036bb34e4ab51796dd745cdba7ed47ad142a9f4a1eb8e0c744d/tqdm-4.67.1.tar.gz", hash = "sha256:f8aef9c52c08c13a65f30ea34f4e5aac3fd1a34959879d7e59e63027286627f2", size = 169737, upload-time = "2024-11-24T20:12:22.481Z" } | ||
| 129 | wheels = [ | ||
| 130 | { url = "https://files.pythonhosted.org/packages/d0/30/dc54f88dd4a2b5dc8a0279bdd7270e735851848b762aeb1c1184ed1f6b14/tqdm-4.67.1-py3-none-any.whl", hash = "sha256:26445eca388f82e72884e0d580d5464cd801a3ea01e63e5601bdff9ba6a48de2", size = 78540, upload-time = "2024-11-24T20:12:19.698Z" }, | ||
| 131 | ] | ||
| 132 | |||
| 133 | [[package]] | ||
| 134 | name = "unshare" | ||
| 135 | version = "0.22" | ||
| 136 | source = { registry = "https://pypi.org/simple" } | ||
| 137 | sdist = { url = "https://files.pythonhosted.org/packages/15/85/2ba218129c95b894efe87506489b525f859c40f6e21cb0521ff3cec754f4/unshare-0.22.tar.gz", hash = "sha256:d521d72cca6e876f22cbd5ff5eb51f1beef75e8f9c53b599b55fa05fba1dd3a6", size = 2041, upload-time = "2019-10-17T12:58:31.498Z" } | ||
| 138 | |||
| 139 | [[package]] | ||
| 140 | name = "xdg" | ||
| 141 | version = "6.0.0" | ||
| 142 | source = { registry = "https://pypi.org/simple" } | ||
| 143 | sdist = { url = "https://files.pythonhosted.org/packages/2a/b9/0e6e6f19fb75cf5e1758f4f33c1256738f718966700cffc0fde2f966218b/xdg-6.0.0.tar.gz", hash = "sha256:24278094f2d45e846d1eb28a2ebb92d7b67fc0cab5249ee3ce88c95f649a1c92", size = 3453, upload-time = "2023-02-27T19:27:44.309Z" } | ||
| 144 | wheels = [ | ||
| 145 | { url = "https://files.pythonhosted.org/packages/dd/54/3516c1cf349060fc3578686d271eba242f10ec00b4530c2985af9faac49b/xdg-6.0.0-py3-none-any.whl", hash = "sha256:df3510755b4395157fc04fc3b02467c777f3b3ca383257397f09ab0d4c16f936", size = 3855, upload-time = "2023-02-27T19:27:42.151Z" }, | ||
| 146 | ] | ||
diff --git a/modules/envfs.nix b/modules/envfs.nix deleted file mode 100644 index b5b453a5..00000000 --- a/modules/envfs.nix +++ /dev/null | |||
| @@ -1,77 +0,0 @@ | |||
| 1 | { pkgs, config, lib, ... }: | ||
| 2 | |||
| 3 | let | ||
| 4 | cfg = config.services.envfs; | ||
| 5 | mounts = { | ||
| 6 | "/usr/bin" = { | ||
| 7 | device = "none"; | ||
| 8 | fsType = "envfs"; | ||
| 9 | options = [ | ||
| 10 | "bind-mount=/bin" | ||
| 11 | "fallback-path=${pkgs.symlinkJoin { | ||
| 12 | name = "fallback-path"; | ||
| 13 | inherit (cfg) paths; | ||
| 14 | }}" | ||
| 15 | "nofail" | ||
| 16 | ]; | ||
| 17 | }; | ||
| 18 | "/bin" = { | ||
| 19 | device = "/usr/bin"; | ||
| 20 | fsType = "none"; | ||
| 21 | options = [ "bind" "nofail" ]; | ||
| 22 | }; | ||
| 23 | }; | ||
| 24 | in { | ||
| 25 | disabledModules = [ "tasks/filesystems/envfs.nix" ]; | ||
| 26 | |||
| 27 | options = { | ||
| 28 | services.envfs = { | ||
| 29 | enable = lib.mkEnableOption "Envfs filesystem" // { | ||
| 30 | default = true; | ||
| 31 | description = '' | ||
| 32 | Fuse filesystem that returns symlinks to executables based on the PATH | ||
| 33 | of the requesting process. This is useful to execute shebangs on NixOS | ||
| 34 | that assume hard coded locations in locations like /bin or /usr/bin | ||
| 35 | etc. | ||
| 36 | ''; | ||
| 37 | }; | ||
| 38 | |||
| 39 | package = lib.mkOption { | ||
| 40 | type = lib.types.package; | ||
| 41 | default = pkgs.envfs; | ||
| 42 | defaultText = lib.literalExpression "pkgs.envfs"; | ||
| 43 | description = "Which package to use for the envfs."; | ||
| 44 | }; | ||
| 45 | |||
| 46 | paths = lib.mkOption { | ||
| 47 | type = lib.types.listOf lib.types.package; | ||
| 48 | default = [ | ||
| 49 | (pkgs.runCommand "fallback-path-environment" {} '' | ||
| 50 | mkdir -p $out | ||
| 51 | ln -s ${config.environment.usrbinenv} $out/env | ||
| 52 | ln -s ${config.environment.binsh} $out/sh | ||
| 53 | '') | ||
| 54 | ]; | ||
| 55 | defaultText = lib.literalExpression '' | ||
| 56 | [ (pkgs.runCommand "fallback-path-environment" {} ''' | ||
| 57 | mkdir -p $out | ||
| 58 | ln -s ''${config.environment.usrbinenv} $out/env | ||
| 59 | ln -s ''${config.environment.binsh} $out/sh | ||
| 60 | ''') | ||
| 61 | ] | ||
| 62 | ''; | ||
| 63 | description = "Extra packages to join into collection of fallback executables in case not other executable is found"; | ||
| 64 | }; | ||
| 65 | }; | ||
| 66 | }; | ||
| 67 | |||
| 68 | config = lib.mkIf (cfg.enable) { | ||
| 69 | environment.systemPackages = [ cfg.package ]; | ||
| 70 | # we also want these mounts in virtual machines. | ||
| 71 | fileSystems = if config.virtualisation ? qemu then lib.mkVMOverride mounts else mounts; | ||
| 72 | |||
| 73 | # We no longer need those when using envfs | ||
| 74 | system.activationScripts.usrbinenv = lib.mkForce ""; | ||
| 75 | system.activationScripts.binsh = lib.mkForce ""; | ||
| 76 | }; | ||
| 77 | } | ||
diff --git a/modules/i18n.nix b/modules/i18n.nix new file mode 100644 index 00000000..f84e8b64 --- /dev/null +++ b/modules/i18n.nix | |||
| @@ -0,0 +1,156 @@ | |||
| 1 | { | ||
| 2 | config, | ||
| 3 | lib, | ||
| 4 | pkgs, | ||
| 5 | ... | ||
| 6 | }: | ||
| 7 | let | ||
| 8 | aggregatedLocales = | ||
| 9 | (builtins.map | ||
| 10 | (l: (lib.replaceStrings [ "utf8" "utf-8" "UTF8" ] [ "UTF-8" "UTF-8" "UTF-8" ] l) + "/UTF-8") | ||
| 11 | ( | ||
| 12 | [ config.i18n.defaultLocale ] | ||
| 13 | ++ (lib.optionals (builtins.isList config.i18n.extraLocales) config.i18n.extraLocales) | ||
| 14 | ++ (lib.attrValues (lib.filterAttrs (n: _v: lib.hasPrefix "LC_" n) config.i18n.extraLocaleSettings)) | ||
| 15 | ) | ||
| 16 | ) | ||
| 17 | ++ (lib.optional (builtins.isString config.i18n.extraLocales) config.i18n.extraLocales); | ||
| 18 | in | ||
| 19 | { | ||
| 20 | disabledModules = [ "config/i18n.nix" ]; | ||
| 21 | |||
| 22 | ###### interface | ||
| 23 | |||
| 24 | options = { | ||
| 25 | |||
| 26 | i18n = { | ||
| 27 | glibcLocales = lib.mkOption { | ||
| 28 | type = lib.types.path; | ||
| 29 | default = pkgs.glibcLocales.override { | ||
| 30 | allLocales = lib.any (x: x == "all") config.i18n.supportedLocales; | ||
| 31 | locales = config.i18n.supportedLocales; | ||
| 32 | }; | ||
| 33 | defaultText = lib.literalExpression '' | ||
| 34 | pkgs.glibcLocales.override { | ||
| 35 | allLocales = lib.any (x: x == "all") config.i18n.supportedLocales; | ||
| 36 | locales = config.i18n.supportedLocales; | ||
| 37 | } | ||
| 38 | ''; | ||
| 39 | example = lib.literalExpression "pkgs.glibcLocales"; | ||
| 40 | description = '' | ||
| 41 | Customized pkg.glibcLocales package. | ||
| 42 | |||
| 43 | Changing this option can disable handling of i18n.defaultLocale | ||
| 44 | and supportedLocale. | ||
| 45 | ''; | ||
| 46 | }; | ||
| 47 | |||
| 48 | defaultLocale = lib.mkOption { | ||
| 49 | type = lib.types.str; | ||
| 50 | default = "en_US.UTF-8"; | ||
| 51 | example = "nl_NL.UTF-8"; | ||
| 52 | description = '' | ||
| 53 | The default locale. It determines the language for program | ||
| 54 | messages, the format for dates and times, sort order, and so on. | ||
| 55 | It also determines the character set, such as UTF-8. | ||
| 56 | ''; | ||
| 57 | }; | ||
| 58 | |||
| 59 | extraLocales = lib.mkOption { | ||
| 60 | type = lib.types.either (lib.types.listOf lib.types.str) (lib.types.enum [ "all" ]); | ||
| 61 | default = [ ]; | ||
| 62 | example = [ "nl_NL.UTF-8" ]; | ||
| 63 | description = '' | ||
| 64 | Additional locales that the system should support, besides the ones | ||
| 65 | configured with {option}`i18n.defaultLocale` and | ||
| 66 | {option}`i18n.extraLocaleSettings`. | ||
| 67 | Set this to `"all"` to install all available locales. | ||
| 68 | ''; | ||
| 69 | }; | ||
| 70 | |||
| 71 | extraLocaleSettings = lib.mkOption { | ||
| 72 | type = lib.types.attrsOf lib.types.str; | ||
| 73 | default = { }; | ||
| 74 | example = { | ||
| 75 | LC_MESSAGES = "en_US.UTF-8"; | ||
| 76 | LC_TIME = "de_DE.UTF-8"; | ||
| 77 | }; | ||
| 78 | description = '' | ||
| 79 | A set of additional system-wide locale settings other than | ||
| 80 | `LANG` which can be configured with | ||
| 81 | {option}`i18n.defaultLocale`. | ||
| 82 | ''; | ||
| 83 | }; | ||
| 84 | |||
| 85 | supportedLocales = lib.mkOption { | ||
| 86 | type = lib.types.listOf lib.types.str; | ||
| 87 | visible = false; | ||
| 88 | default = lib.unique ( | ||
| 89 | [ | ||
| 90 | "C.UTF-8/UTF-8" | ||
| 91 | "en_US.UTF-8/UTF-8" | ||
| 92 | ] | ||
| 93 | ++ aggregatedLocales | ||
| 94 | ); | ||
| 95 | example = [ | ||
| 96 | "en_US.UTF-8/UTF-8" | ||
| 97 | "nl_NL.UTF-8/UTF-8" | ||
| 98 | "nl_NL/ISO-8859-1" | ||
| 99 | ]; | ||
| 100 | description = '' | ||
| 101 | List of locales that the system should support. The value | ||
| 102 | `"all"` means that all locales supported by | ||
| 103 | Glibc will be installed. A full list of supported locales | ||
| 104 | can be found at <https://sourceware.org/git/?p=glibc.git;a=blob;f=localedata/SUPPORTED>. | ||
| 105 | ''; | ||
| 106 | }; | ||
| 107 | |||
| 108 | }; | ||
| 109 | |||
| 110 | }; | ||
| 111 | |||
| 112 | ###### implementation | ||
| 113 | |||
| 114 | config = { | ||
| 115 | warnings = | ||
| 116 | lib.optional | ||
| 117 | ( | ||
| 118 | !( | ||
| 119 | (lib.subtractLists config.i18n.supportedLocales aggregatedLocales) == [ ] | ||
| 120 | || lib.any (x: x == "all") config.i18n.supportedLocales | ||
| 121 | ) | ||
| 122 | ) | ||
| 123 | '' | ||
| 124 | `i18n.supportedLocales` is deprecated in favor of `i18n.extraLocales`, | ||
| 125 | and it seems you are using `i18n.supportedLocales` and forgot to | ||
| 126 | include some locales specified in `i18n.defaultLocale`, | ||
| 127 | `i18n.extraLocales` or `i18n.extraLocaleSettings`. | ||
| 128 | |||
| 129 | If you're trying to install additional locales not specified in | ||
| 130 | `i18n.defaultLocale` or `i18n.extraLocaleSettings`, consider adding | ||
| 131 | only those locales to `i18n.extraLocales`. | ||
| 132 | ''; | ||
| 133 | |||
| 134 | environment.systemPackages = | ||
| 135 | # We increase the priority a little, so that plain glibc in systemPackages can't win. | ||
| 136 | lib.optional (config.i18n.supportedLocales != [ ]) (lib.setPrio (-1) config.i18n.glibcLocales); | ||
| 137 | |||
| 138 | environment.sessionVariables = { | ||
| 139 | LANG = config.i18n.defaultLocale; | ||
| 140 | LOCALE_ARCHIVE = "/run/current-system/sw/lib/locale/locale-archive"; | ||
| 141 | } // config.i18n.extraLocaleSettings; | ||
| 142 | |||
| 143 | systemd.globalEnvironment = lib.mkIf (config.i18n.supportedLocales != [ ]) { | ||
| 144 | LOCALE_ARCHIVE = "${config.i18n.glibcLocales}/lib/locale/locale-archive"; | ||
| 145 | }; | ||
| 146 | |||
| 147 | # ‘/etc/locale.conf’ is used by systemd. | ||
| 148 | environment.etc."locale.conf".source = pkgs.writeText "locale.conf" '' | ||
| 149 | LANG=${config.i18n.defaultLocale} | ||
| 150 | ${lib.concatStringsSep "\n" ( | ||
| 151 | lib.mapAttrsToList (n: v: "${n}=${v}") config.i18n.extraLocaleSettings | ||
| 152 | )} | ||
| 153 | ''; | ||
| 154 | |||
| 155 | }; | ||
| 156 | } | ||
diff --git a/modules/impermanence-timezone.nix b/modules/impermanence-timezone.nix new file mode 100644 index 00000000..a1edbfa5 --- /dev/null +++ b/modules/impermanence-timezone.nix | |||
| @@ -0,0 +1,41 @@ | |||
| 1 | { config, lib, utils, pkgs, ... }: | ||
| 2 | |||
| 3 | { | ||
| 4 | options = { | ||
| 5 | environment.persistence = lib.mkOption { | ||
| 6 | type = lib.types.attrsOf (lib.types.submodule { | ||
| 7 | options = { | ||
| 8 | timezone = lib.mkEnableOption "storing system timezone"; | ||
| 9 | }; | ||
| 10 | }); | ||
| 11 | }; | ||
| 12 | }; | ||
| 13 | |||
| 14 | config = { | ||
| 15 | systemd = lib.mkMerge (lib.mapAttrsToList (name: cfg: lib.mkIf cfg.timezone { | ||
| 16 | services = { | ||
| 17 | "timezone@${utils.escapeSystemdPath name}" = { | ||
| 18 | wantedBy = [ "multi-user.target" ]; | ||
| 19 | serviceConfig = { | ||
| 20 | Type = "oneshot"; | ||
| 21 | RemainAfterExit = true; | ||
| 22 | ExecStart = "${pkgs.coreutils}/bin/cp -vP ${utils.escapeSystemdExecArg "${name}/etc/localtime"} /etc/localtime"; | ||
| 23 | ExecStop = "${pkgs.coreutils}/bin/cp -vP /etc/localtime ${utils.escapeSystemdExecArg "${name}/etc/localtime"}"; | ||
| 24 | }; | ||
| 25 | }; | ||
| 26 | "etc-localtime@${utils.escapeSystemdPath name}" = { | ||
| 27 | serviceConfig = { | ||
| 28 | Type = "oneshot"; | ||
| 29 | ExecStart = "${pkgs.coreutils}/bin/cp -vP /etc/localtime ${utils.escapeSystemdExecArg "${name}/etc/localtime"}"; | ||
| 30 | }; | ||
| 31 | }; | ||
| 32 | }; | ||
| 33 | paths."etc-localtime@${utils.escapeSystemdPath name}" = { | ||
| 34 | wantedBy = [ "timezone@${utils.escapeSystemdPath name}.service" ]; | ||
| 35 | after = [ "timezone@${utils.escapeSystemdPath name}.service" ]; | ||
| 36 | |||
| 37 | pathConfig.PathChanged = "/etc/localtime"; | ||
| 38 | }; | ||
| 39 | }) config.environment.persistence); | ||
| 40 | }; | ||
| 41 | } | ||
diff --git a/modules/impermanence.nix b/modules/impermanence.nix new file mode 100644 index 00000000..621576a3 --- /dev/null +++ b/modules/impermanence.nix | |||
| @@ -0,0 +1,6 @@ | |||
| 1 | { flakeInputs, ... }: | ||
| 2 | { | ||
| 3 | imports = [ | ||
| 4 | flakeInputs.impermanence.nixosModules.impermanence | ||
| 5 | ]; | ||
| 6 | } | ||
diff --git a/modules/installer.nix b/modules/installer.nix new file mode 100644 index 00000000..3e5c6d5b --- /dev/null +++ b/modules/installer.nix | |||
| @@ -0,0 +1,56 @@ | |||
| 1 | { flake, config, lib, pkgs, ... }: | ||
| 2 | |||
| 3 | let | ||
| 4 | cfg = config.installer.links; | ||
| 5 | |||
| 6 | installerOutPath = { | ||
| 7 | "cd-dvd" = _: installerBuild: "${installerBuild.config.system.build.isoImage}/iso"; | ||
| 8 | "netboot" = {system, variant}: installerBuild: pkgs.runCommandLocal "${system}-${variant}" {} '' | ||
| 9 | mkdir $out | ||
| 10 | install -m 0444 -t $out \ | ||
| 11 | ${installerBuild.config.system.build.netbootRamdisk}/initrd \ | ||
| 12 | ${installerBuild.config.system.build.kernel}/${config.system.boot.loader.kernelFile} \ | ||
| 13 | ${installerBuild.config.system.build.netbootIpxeScript}/netboot.ipxe \ | ||
| 14 | ${pkgs.ipxe.override { | ||
| 15 | additionalTargets = { | ||
| 16 | "bin-i386-efi/ipxe.efi" = "i386-ipxe.efi"; | ||
| 17 | }; | ||
| 18 | additionalOptions = [ | ||
| 19 | "NSLOOKUP_CMD" | ||
| 20 | "PING_CMD" | ||
| 21 | "CONSOLE_CMD" | ||
| 22 | ]; | ||
| 23 | embedScript = pkgs.writeText "netboot.ipxe" '' | ||
| 24 | #!ipxe | ||
| 25 | |||
| 26 | chain netboot.ipxe | ||
| 27 | ''; | ||
| 28 | }}/{ipxe.efi,i386-ipxe.efi,ipxe.lkrn} | ||
| 29 | ''; | ||
| 30 | }; | ||
| 31 | in { | ||
| 32 | options = { | ||
| 33 | installer.links = lib.mkOption { | ||
| 34 | type = lib.types.listOf (lib.types.submodule { | ||
| 35 | options = { | ||
| 36 | system = lib.mkOption { | ||
| 37 | type = lib.types.str; | ||
| 38 | }; | ||
| 39 | variant = lib.mkOption { | ||
| 40 | type = lib.types.str; | ||
| 41 | }; | ||
| 42 | }; | ||
| 43 | }); | ||
| 44 | default = []; | ||
| 45 | }; | ||
| 46 | }; | ||
| 47 | |||
| 48 | config = lib.mkIf (cfg != []) { | ||
| 49 | systemd.tmpfiles.rules = map (installer'@{system, variant}: | ||
| 50 | let | ||
| 51 | installer = "${system}-${variant}"; | ||
| 52 | installerBuild = builtins.addErrorContext "while evaluating installer-${installer}" flake.nixosConfigurations.${"installer-${installer}"}; | ||
| 53 | in "L+ /run/installer-${installer} - - - - ${installerOutPath.${variant} installer' installerBuild}" | ||
| 54 | ) cfg; | ||
| 55 | }; | ||
| 56 | } | ||
diff --git a/modules/niri.nix b/modules/niri.nix new file mode 100644 index 00000000..4e2ddf8b --- /dev/null +++ b/modules/niri.nix | |||
| @@ -0,0 +1,6 @@ | |||
| 1 | { flakeInputs, ... }: | ||
| 2 | { | ||
| 3 | imports = [ | ||
| 4 | flakeInputs.niri-flake.nixosModules.niri | ||
| 5 | ]; | ||
| 6 | } | ||
diff --git a/modules/nix-access-tokens/default.nix b/modules/nix-access-tokens/default.nix new file mode 100644 index 00000000..a3b7abfa --- /dev/null +++ b/modules/nix-access-tokens/default.nix | |||
| @@ -0,0 +1,24 @@ | |||
| 1 | { lib, config, hostName ,... }: | ||
| 2 | |||
| 3 | let | ||
| 4 | cfg = config.nix.includeAccessTokens; | ||
| 5 | in { | ||
| 6 | options = { | ||
| 7 | nix.includeAccessTokens.enable = lib.mkEnableOption "including access tokens in nix.conf" // { default = lib.elem hostName ["sif" "surtr" "vidhar"]; }; | ||
| 8 | }; | ||
| 9 | |||
| 10 | config = lib.mkIf cfg.enable { | ||
| 11 | nix = { | ||
| 12 | extraOptions = '' | ||
| 13 | !include ${config.sops.secrets.nixAccessTokens.path} | ||
| 14 | ''; | ||
| 15 | }; | ||
| 16 | |||
| 17 | sops.secrets.nixAccessTokens = { | ||
| 18 | format = "binary"; | ||
| 19 | sopsFile = ./nix.conf; | ||
| 20 | mode = "0440"; | ||
| 21 | group = "wheel"; | ||
| 22 | }; | ||
| 23 | }; | ||
| 24 | } | ||
diff --git a/modules/nix-access-tokens/nix.conf b/modules/nix-access-tokens/nix.conf new file mode 100644 index 00000000..f0b394ef --- /dev/null +++ b/modules/nix-access-tokens/nix.conf | |||
| @@ -0,0 +1,32 @@ | |||
| 1 | { | ||
| 2 | "data": "ENC[AES256_GCM,data:/cdBpvCAFpgm0YWhy1WYlA09KlU6PzVfBYVLBD0boqGqvP+8wuyDzj5KWbcKsdGhoiklODiKR0ODXNU+fA35y862PFXvSb4xVyfbdKRndYdIA4W6vyobtoC9h7B1yR9pkq9L+1tqlU30Dgy2Gndg9rWHlIo+1lO/1A==,iv:B1Px2+cxCaopHZThkEG5saOib+PNvurPIS6aeAv2uPo=,tag:K3JqRaX3/iIqD3c//YdqSQ==,type:str]", | ||
| 3 | "sops": { | ||
| 4 | "kms": null, | ||
| 5 | "gcp_kms": null, | ||
| 6 | "azure_kv": null, | ||
| 7 | "hc_vault": null, | ||
| 8 | "age": [ | ||
| 9 | { | ||
| 10 | "recipient": "age1rmmhetcmllq0ahl5qznlr0eya2zdxwl9h6y5wnl97d2wtyx5t99sm2u866", | ||
| 11 | "enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSB5NkZUUGI3M2pQYWVXeFV6\na2h2czRTeUJFekJCS012YlBkL1FDdTd3ekZ3ClJsTVh0R2JQM0Jua1JjL285RVA1\nRHhlbjlLdmNBUXVLelFGY2NGYWpLejQKLS0tIDBUWUhJNm8zWGoyQ0pBYnV1ZjBh\ndktNRkNPS1lpWXFITC81aEZJbXlONk0Km2c1xVKwSankaVs7O/utGJwRRX395upz\ndPbsOElTnbGmkb0esGtvGSPboTvK+gjn9w/GhaPyTnNDoos7GaIfyg==\n-----END AGE ENCRYPTED FILE-----\n" | ||
| 12 | }, | ||
| 13 | { | ||
| 14 | "recipient": "age1fj65apkhfkrwyv5tx6zcs9nkjg8267fy733qph30sc7zfn7vapjqkd5kne", | ||
| 15 | "enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSB6bS9iY2lua3U4U3lJa1pK\nSlZNMmFZMEU5M1V2bWRjaXIwajZJVDJPMlM4Cmd3TTNFWjVuSGdtbC9iODltTS91\nOE5XOEVEQkh0SFpVVW5jc3IzbzNpTmMKLS0tIEtrSU54QUVPa2tBZDhLYlRFWitR\nc2x6MFlxL0tobDJTek42dEcyZXpoWDgKXzQfU+o6FkbJBwmm6oaHu4sDPi822uUR\n5VY6gY/h3g2kM4cuS03Q4NJmeRxuh7cx0UqGU3j5Mf8muE1LHpYEPw==\n-----END AGE ENCRYPTED FILE-----\n" | ||
| 16 | }, | ||
| 17 | { | ||
| 18 | "recipient": "age19a7j77w267z04zls7m28a8hj4a0g5af6ltye2d5wypg33c3l89csd4r9zq", | ||
| 19 | "enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBaOVpNZ2lVT0VwbHVZNzFl\nenJsMGpnbkRvU0xOSU5obk5yT2p5ZVNzdXhNCnVlQzZtRjZNVmJLSUpKc3UwVXZs\nWi9EZ3kxZkJNeFJDSjl1L1IweTFNMXcKLS0tIDJUOTBwTldCUmlnU0tWVkZkNzJL\nejM4ajJVbVhvSm1YM2Vxa2JldllYN0UKAzxy2wkzRvCSiTy417AulpCu41z668HG\nto92eGF2ZRFfEG5LGlCKWeDcP3gM8QwKiVlm6wndbOkhMMfc4Sp3wA==\n-----END AGE ENCRYPTED FILE-----\n" | ||
| 20 | }, | ||
| 21 | { | ||
| 22 | "recipient": "age1qffdqvy9arld9zd5a5cylt0n98xhcns5shxhrhwjq5g4qa844ejselaa4l", | ||
| 23 | "enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSB2ejRHcGttNUxYZnFzTU5J\nMTFvY3daQ1VMM2xxYTgvLzZwT1owazVNenhzCktaWFF6K2s5UjI2b20rSHFNSS9E\nMVlJSmZhQm15eUs3U0hGTGpSRndmSDgKLS0tIDVrcjl4eDhwak1pRithbnRWWEZy\nVE9EOEpKdEJoRTFrTXpQVDc1cmsrU1kK/goTdUmpZPeMRbY1QzLXAa6Qpg4YYYYo\n3v3GK1bzdey8szfgIr1dHTtQEzqE2WX1swzZizDXj/RiUWx01Ky3GA==\n-----END AGE ENCRYPTED FILE-----\n" | ||
| 24 | } | ||
| 25 | ], | ||
| 26 | "lastmodified": "2025-01-25T19:58:58Z", | ||
| 27 | "mac": "ENC[AES256_GCM,data:Oza4XgnTX3vly89nGluLbEytk1dUYAiOhIYewQyDLLLSSlUIpXmWhV+X0HUQ9AX5kUrEhNbVzRdvUG/9YwoWjTJfvd7tw41IYeTqgykMNXJUfGssoutXfeij9YR+t5aJaRhlTkIWcBhUjXSUNyJCl6Z3XmzWstTPZXEU9VmAvuE=,iv:LqVwIiit+WqI5NWSboexWsmPzg7e63nWJYsNFEK1Uog=,tag:ClR6oI62WXEfIYYAY6vL0A==,type:str]", | ||
| 28 | "pgp": null, | ||
| 29 | "unencrypted_suffix": "_unencrypted", | ||
| 30 | "version": "3.9.3" | ||
| 31 | } | ||
| 32 | } \ No newline at end of file | ||
diff --git a/modules/pgbackrest.nix b/modules/pgbackrest.nix index 886840b9..550e970b 100644 --- a/modules/pgbackrest.nix +++ b/modules/pgbackrest.nix | |||
| @@ -43,6 +43,8 @@ let | |||
| 43 | loglevelType = types.enum ["off" "error" "warn" "info" "detail" "debug" "trace"]; | 43 | loglevelType = types.enum ["off" "error" "warn" "info" "detail" "debug" "trace"]; |
| 44 | inherit (utils.systemdUtils.unitOptions) unitOption; | 44 | inherit (utils.systemdUtils.unitOptions) unitOption; |
| 45 | in { | 45 | in { |
| 46 | disabledModules = ["services/backup/pgbackrest.nix"]; | ||
| 47 | |||
| 46 | options = { | 48 | options = { |
| 47 | services.pgbackrest = { | 49 | services.pgbackrest = { |
| 48 | enable = mkEnableOption "pgBackRest"; | 50 | enable = mkEnableOption "pgBackRest"; |
| @@ -216,6 +218,7 @@ in { | |||
| 216 | }; | 218 | }; |
| 217 | }; | 219 | }; |
| 218 | } // mapAttrs' (name: backupCfg: nameValuePair "pgbackrest-backup@${escapeSystemdPath name}" { | 220 | } // mapAttrs' (name: backupCfg: nameValuePair "pgbackrest-backup@${escapeSystemdPath name}" { |
| 221 | restartIfChanged = false; | ||
| 219 | description = "Perform pgBackRest Backup (${name}${optionalString (!(isNull backupCfg.repo)) " repo${backupCfg.repo}"})"; | 222 | description = "Perform pgBackRest Backup (${name}${optionalString (!(isNull backupCfg.repo)) " repo${backupCfg.repo}"})"; |
| 220 | serviceConfig = { | 223 | serviceConfig = { |
| 221 | Type = "oneshot"; | 224 | Type = "oneshot"; |
diff --git a/modules/postsrsd.nix b/modules/postsrsd.nix new file mode 100644 index 00000000..bc941e3e --- /dev/null +++ b/modules/postsrsd.nix | |||
| @@ -0,0 +1,163 @@ | |||
| 1 | { | ||
| 2 | config, | ||
| 3 | lib, | ||
| 4 | pkgs, | ||
| 5 | ... | ||
| 6 | }: | ||
| 7 | let | ||
| 8 | |||
| 9 | cfg = config.services.postsrsd; | ||
| 10 | runtimeDirectoryName = "postsrsd"; | ||
| 11 | runtimeDirectory = "/run/${runtimeDirectoryName}"; | ||
| 12 | # TODO: follow RFC 42, but we need a libconfuse format first: | ||
| 13 | # https://github.com/NixOS/nixpkgs/issues/401565 | ||
| 14 | # Arrays in `libconfuse` look like this: {"Life", "Universe", "Everything"} | ||
| 15 | # See https://www.nongnu.org/confuse/tutorial-html/ar01s03.html. | ||
| 16 | # | ||
| 17 | # Note: We're using `builtins.toJSON` to escape strings, but JSON strings | ||
| 18 | # don't have exactly the same semantics as libconfuse strings. For example, | ||
| 19 | # "${F}" gets treated as an env var reference, see above issue for details. | ||
| 20 | libconfuseDomains = "{ " + lib.concatMapStringsSep ", " builtins.toJSON cfg.domains + " }"; | ||
| 21 | configFile = pkgs.writeText "postsrsd.conf" '' | ||
| 22 | secrets-file = "''${CREDENTIALS_DIRECTORY}/secrets-file" | ||
| 23 | domains = ${libconfuseDomains} | ||
| 24 | separator = "${cfg.separator}" | ||
| 25 | |||
| 26 | # Disable postsrsd's jailing in favor of confinement with systemd. | ||
| 27 | unprivileged-user = "" | ||
| 28 | chroot-dir = "" | ||
| 29 | |||
| 30 | ${cfg.extraConfig} | ||
| 31 | ''; | ||
| 32 | |||
| 33 | in | ||
| 34 | { | ||
| 35 | imports = | ||
| 36 | map | ||
| 37 | ( | ||
| 38 | name: | ||
| 39 | lib.mkRemovedOptionModule [ "services" "postsrsd" name ] '' | ||
| 40 | `postsrsd` was upgraded to `>= 2.0.0`, with some different behaviors and configuration settings: | ||
| 41 | - NixOS Release Notes: https://nixos.org/manual/nixos/unstable/release-notes#sec-nixpkgs-release-25.05-incompatibilities | ||
| 42 | - NixOS Options Reference: https://nixos.org/manual/nixos/unstable/options#opt-services.postsrsd.enable | ||
| 43 | - Migration instructions: https://github.com/roehling/postsrsd/blob/2.0.10/README.rst#migrating-from-version-1x | ||
| 44 | - Postfix Setup: https://github.com/roehling/postsrsd/blob/2.0.10/README.rst#postfix-setup | ||
| 45 | '' | ||
| 46 | ) | ||
| 47 | [ | ||
| 48 | "domain" | ||
| 49 | "forwardPort" | ||
| 50 | "reversePort" | ||
| 51 | "timeout" | ||
| 52 | "excludeDomains" | ||
| 53 | ]; | ||
| 54 | |||
| 55 | disabledModules = [ "services/mail/postsrsd.nix" ]; | ||
| 56 | |||
| 57 | options = { | ||
| 58 | services.postsrsd = { | ||
| 59 | enable = lib.mkOption { | ||
| 60 | type = lib.types.bool; | ||
| 61 | default = false; | ||
| 62 | description = "Whether to enable the postsrsd SRS server for Postfix."; | ||
| 63 | }; | ||
| 64 | |||
| 65 | secretsFile = lib.mkOption { | ||
| 66 | type = lib.types.path; | ||
| 67 | default = "/var/lib/postsrsd/postsrsd.secret"; | ||
| 68 | description = "Secret keys used for signing and verification"; | ||
| 69 | }; | ||
| 70 | |||
| 71 | domains = lib.mkOption { | ||
| 72 | type = lib.types.listOf lib.types.str; | ||
| 73 | description = "Domain names for rewrite"; | ||
| 74 | default = [ config.networking.hostName ]; | ||
| 75 | defaultText = lib.literalExpression "[ config.networking.hostName ]"; | ||
| 76 | }; | ||
| 77 | |||
| 78 | separator = lib.mkOption { | ||
| 79 | type = lib.types.enum [ | ||
| 80 | "-" | ||
| 81 | "=" | ||
| 82 | "+" | ||
| 83 | ]; | ||
| 84 | default = "="; | ||
| 85 | description = "First separator character in generated addresses"; | ||
| 86 | }; | ||
| 87 | |||
| 88 | user = lib.mkOption { | ||
| 89 | type = lib.types.str; | ||
| 90 | default = "postsrsd"; | ||
| 91 | description = "User for the daemon"; | ||
| 92 | }; | ||
| 93 | |||
| 94 | group = lib.mkOption { | ||
| 95 | type = lib.types.str; | ||
| 96 | default = "postsrsd"; | ||
| 97 | description = "Group for the daemon"; | ||
| 98 | }; | ||
| 99 | |||
| 100 | extraConfig = lib.mkOption { | ||
| 101 | type = lib.types.lines; | ||
| 102 | default = ""; | ||
| 103 | }; | ||
| 104 | |||
| 105 | configurePostfix = lib.mkOption { | ||
| 106 | type = lib.types.bool; | ||
| 107 | default = false; | ||
| 108 | description = "noop"; | ||
| 109 | }; | ||
| 110 | }; | ||
| 111 | }; | ||
| 112 | |||
| 113 | config = lib.mkIf cfg.enable { | ||
| 114 | users.users = lib.optionalAttrs (cfg.user == "postsrsd") { | ||
| 115 | postsrsd = { | ||
| 116 | group = cfg.group; | ||
| 117 | uid = config.ids.uids.postsrsd; | ||
| 118 | }; | ||
| 119 | }; | ||
| 120 | |||
| 121 | users.groups = lib.optionalAttrs (cfg.group == "postsrsd") { | ||
| 122 | postsrsd.gid = config.ids.gids.postsrsd; | ||
| 123 | }; | ||
| 124 | |||
| 125 | systemd.services.postsrsd-generate-secrets = { | ||
| 126 | path = [ pkgs.coreutils ]; | ||
| 127 | script = '' | ||
| 128 | if [ -e "${cfg.secretsFile}" ]; then | ||
| 129 | echo "Secrets file exists. Nothing to do!" | ||
| 130 | else | ||
| 131 | echo "WARNING: secrets file not found, autogenerating!" | ||
| 132 | DIR="$(dirname "${cfg.secretsFile}")" | ||
| 133 | install -m 750 -o ${cfg.user} -g ${cfg.group} -d "$DIR" | ||
| 134 | install -m 600 -o ${cfg.user} -g ${cfg.group} <(dd if=/dev/random bs=18 count=1 | base64) "${cfg.secretsFile}" | ||
| 135 | fi | ||
| 136 | ''; | ||
| 137 | serviceConfig = { | ||
| 138 | Type = "oneshot"; | ||
| 139 | }; | ||
| 140 | }; | ||
| 141 | |||
| 142 | systemd.services.postsrsd = { | ||
| 143 | description = "PostSRSd SRS rewriting server"; | ||
| 144 | after = [ | ||
| 145 | "network.target" | ||
| 146 | "postsrsd-generate-secrets.service" | ||
| 147 | ]; | ||
| 148 | before = [ "postfix.service" ]; | ||
| 149 | wantedBy = [ "multi-user.target" ]; | ||
| 150 | requires = [ "postsrsd-generate-secrets.service" ]; | ||
| 151 | confinement.enable = true; | ||
| 152 | |||
| 153 | serviceConfig = { | ||
| 154 | ExecStart = "${lib.getExe pkgs.postsrsd} -C ${configFile}"; | ||
| 155 | User = cfg.user; | ||
| 156 | Group = cfg.group; | ||
| 157 | PermissionsStartOnly = true; | ||
| 158 | RuntimeDirectory = runtimeDirectoryName; | ||
| 159 | LoadCredential = "secrets-file:${cfg.secretsFile}"; | ||
| 160 | }; | ||
| 161 | }; | ||
| 162 | }; | ||
| 163 | } | ||
diff --git a/modules/systemd-run0.nix b/modules/systemd-run0.nix new file mode 100644 index 00000000..8575ec7c --- /dev/null +++ b/modules/systemd-run0.nix | |||
| @@ -0,0 +1,4 @@ | |||
| 1 | { config, lib, ... }: | ||
| 2 | { | ||
| 3 | config.security.pam.services.systemd-run0 = lib.mkIf (lib.versionAtLeast config.systemd.package.version "256") {}; | ||
| 4 | } | ||
diff --git a/modules/tzupdate.nix b/modules/tzupdate.nix new file mode 100644 index 00000000..6465d33f --- /dev/null +++ b/modules/tzupdate.nix | |||
| @@ -0,0 +1,81 @@ | |||
| 1 | { | ||
| 2 | config, | ||
| 3 | lib, | ||
| 4 | pkgs, | ||
| 5 | ... | ||
| 6 | }: | ||
| 7 | let | ||
| 8 | cfg = config.services.tzupdate; | ||
| 9 | in | ||
| 10 | { | ||
| 11 | disabledModules = [ "services/misc/tzupdate.nix" ]; | ||
| 12 | |||
| 13 | options.services.tzupdate = { | ||
| 14 | enable = lib.mkOption { | ||
| 15 | type = lib.types.bool; | ||
| 16 | default = false; | ||
| 17 | description = '' | ||
| 18 | Enable the tzupdate timezone updating service. This provides | ||
| 19 | a one-shot service which can be activated with systemctl to | ||
| 20 | update the timezone. | ||
| 21 | ''; | ||
| 22 | }; | ||
| 23 | |||
| 24 | package = lib.mkPackageOption pkgs "tzupdate" { }; | ||
| 25 | |||
| 26 | timer.enable = lib.mkOption { | ||
| 27 | type = lib.types.bool; | ||
| 28 | default = true; | ||
| 29 | description = '' | ||
| 30 | Enable the tzupdate timer to update the timezone automatically. | ||
| 31 | ''; | ||
| 32 | }; | ||
| 33 | |||
| 34 | timer.interval = lib.mkOption { | ||
| 35 | type = lib.types.str; | ||
| 36 | default = "hourly"; | ||
| 37 | description = '' | ||
| 38 | The interval at which the tzupdate timer should run. See | ||
| 39 | {manpage}`systemd.time(7)` to understand the format. | ||
| 40 | ''; | ||
| 41 | }; | ||
| 42 | }; | ||
| 43 | |||
| 44 | config = lib.mkIf cfg.enable { | ||
| 45 | # We need to have imperative time zone management for this to work. | ||
| 46 | # This will give users an error if they have set an explicit time | ||
| 47 | # zone, which is better than silently overriding it. | ||
| 48 | time.timeZone = null; | ||
| 49 | |||
| 50 | # We provide a one-shot service that runs at startup once network | ||
| 51 | # interfaces are up, but we can’t ensure we actually have Internet access | ||
| 52 | # at that point. It can also be run manually with `systemctl start tzupdate`. | ||
| 53 | systemd.services.tzupdate = { | ||
| 54 | description = "tzupdate timezone update service"; | ||
| 55 | wantedBy = [ "multi-user.target" ]; | ||
| 56 | wants = [ "network-online.target" ]; | ||
| 57 | after = [ "network-online.target" ]; | ||
| 58 | script = '' | ||
| 59 | timezone="$(${lib.getExe cfg.package} --print-only)" | ||
| 60 | if [[ -n "$timezone" ]]; then | ||
| 61 | echo "Setting timezone to '$timezone'" | ||
| 62 | ${lib.getExe' config.systemd.package "timedatectl"} set-timezone "$timezone" | ||
| 63 | fi | ||
| 64 | ''; | ||
| 65 | |||
| 66 | serviceConfig = { | ||
| 67 | Type = "oneshot"; | ||
| 68 | }; | ||
| 69 | }; | ||
| 70 | |||
| 71 | systemd.timers.tzupdate = { | ||
| 72 | enable = cfg.timer.enable; | ||
| 73 | timerConfig = { | ||
| 74 | OnStartupSec = "30s"; | ||
| 75 | OnCalendar = cfg.timer.interval; | ||
| 76 | Persistent = true; | ||
| 77 | }; | ||
| 78 | wantedBy = [ "timers.target" ]; | ||
| 79 | }; | ||
| 80 | }; | ||
| 81 | } | ||
diff --git a/modules/uucp.nix b/modules/uucp.nix deleted file mode 100644 index abca2acb..00000000 --- a/modules/uucp.nix +++ /dev/null | |||
| @@ -1,398 +0,0 @@ | |||
| 1 | { flake, config, lib, pkgs, ... }: | ||
| 2 | |||
| 3 | with lib; | ||
| 4 | |||
| 5 | let | ||
| 6 | portSpec = name: node: concatStringsSep "\n" (map (port: '' | ||
| 7 | port ${name}.${port} | ||
| 8 | type pipe | ||
| 9 | protocol ${node.protocols} | ||
| 10 | reliable true | ||
| 11 | command ${pkgs.openssh}/bin/ssh -x -o batchmode=yes ${name}.${port} | ||
| 12 | '') node.hostnames); | ||
| 13 | sysSpec = name: node: '' | ||
| 14 | system ${name} | ||
| 15 | time any | ||
| 16 | chat-seven-bit false | ||
| 17 | chat . "" | ||
| 18 | protocol ${node.protocols} | ||
| 19 | command-path ${concatStringsSep " " cfg.commandPath} | ||
| 20 | commands ${concatStringsSep " " node.commands} | ||
| 21 | ${concatStringsSep "\nalternate\n" (map (port: '' | ||
| 22 | port ${name}.${port} | ||
| 23 | '') node.hostnames)} | ||
| 24 | ''; | ||
| 25 | sshConfig = name: node: concatStringsSep "\n" (map (port: '' | ||
| 26 | Host ${name}.${port} | ||
| 27 | Hostname ${port} | ||
| 28 | IdentitiesOnly Yes | ||
| 29 | IdentityFile ${cfg.sshKeyDir}/${name} | ||
| 30 | '') node.hostnames); | ||
| 31 | sshKeyGen = name: node: '' | ||
| 32 | if [[ ! -e ${cfg.sshKeyDir}/${name} ]]; then | ||
| 33 | ${pkgs.openssh}/bin/ssh-keygen ${escapeShellArgs node.generateKey} -f ${cfg.sshKeyDir}/${name} | ||
| 34 | fi | ||
| 35 | ''; | ||
| 36 | restrictKey = key: '' | ||
| 37 | restrict,command="${chat}" ${key} | ||
| 38 | ''; | ||
| 39 | chat = pkgs.writeScript "chat" '' | ||
| 40 | #!${pkgs.stdenv.shell} | ||
| 41 | |||
| 42 | echo . | ||
| 43 | exec ${config.security.wrapperDir}/uucico | ||
| 44 | ''; | ||
| 45 | |||
| 46 | nodeCfg = { | ||
| 47 | options = { | ||
| 48 | commands = mkOption { | ||
| 49 | type = types.listOf types.str; | ||
| 50 | default = cfg.defaultCommands; | ||
| 51 | defaultText = literalExpression "config.services.uucp.defaultCommands"; | ||
| 52 | description = "Commands to allow for this remote"; | ||
| 53 | }; | ||
| 54 | |||
| 55 | protocols = mkOption { | ||
| 56 | type = types.separatedString ""; | ||
| 57 | default = cfg.defaultProtocols; | ||
| 58 | defaultText = literalExpression "config.services.uucp.defaultProtocols"; | ||
| 59 | description = "UUCP protocols to use for this remote"; | ||
| 60 | }; | ||
| 61 | |||
| 62 | publicKeys = mkOption { | ||
| 63 | type = types.listOf types.str; | ||
| 64 | default = []; | ||
| 65 | description = "SSH client public keys for this node"; | ||
| 66 | }; | ||
| 67 | |||
| 68 | generateKey = mkOption { | ||
| 69 | type = types.listOf types.str; | ||
| 70 | default = [ "-t" "ed25519" "-N" "" ]; | ||
| 71 | description = "Arguments to pass to `ssh-keygen` to generate a keypair for communication with this host"; | ||
| 72 | }; | ||
| 73 | |||
| 74 | hostnames = mkOption { | ||
| 75 | type = types.listOf types.str; | ||
| 76 | default = []; | ||
| 77 | description = "Hostnames to try in order when connecting"; | ||
| 78 | }; | ||
| 79 | }; | ||
| 80 | }; | ||
| 81 | |||
| 82 | cfg = config.services.uucp; | ||
| 83 | in { | ||
| 84 | options = { | ||
| 85 | services.uucp = { | ||
| 86 | enable = mkOption { | ||
| 87 | type = types.bool; | ||
| 88 | default = false; | ||
| 89 | description = '' | ||
| 90 | If enabled we set up an account accesible via uucp over ssh | ||
| 91 | ''; | ||
| 92 | }; | ||
| 93 | |||
| 94 | nodeName = mkOption { | ||
| 95 | type = types.str; | ||
| 96 | default = "nixos"; | ||
| 97 | description = "uucp node name"; | ||
| 98 | }; | ||
| 99 | |||
| 100 | sshUser = mkOption { | ||
| 101 | type = types.attrs; | ||
| 102 | default = {}; | ||
| 103 | description = "Overrides for the local uucp linux-user"; | ||
| 104 | }; | ||
| 105 | |||
| 106 | extraSSHConfig = mkOption { | ||
| 107 | type = types.str; | ||
| 108 | default = ""; | ||
| 109 | description = "Extra SSH config"; | ||
| 110 | }; | ||
| 111 | |||
| 112 | remoteNodes = mkOption { | ||
| 113 | type = types.attrsOf (types.submodule nodeCfg); | ||
| 114 | default = {}; | ||
| 115 | description = '' | ||
| 116 | Ports to set up | ||
| 117 | Names will probably need to be configured in sshConfig | ||
| 118 | ''; | ||
| 119 | }; | ||
| 120 | |||
| 121 | commandPath = mkOption { | ||
| 122 | type = types.listOf types.path; | ||
| 123 | default = [ "${pkgs.rmail}/bin" ]; | ||
| 124 | defaultText = literalExpression ''[ "''${pkgs.rmail}/bin" ]''; | ||
| 125 | description = '' | ||
| 126 | Command search path for all systems | ||
| 127 | ''; | ||
| 128 | }; | ||
| 129 | |||
| 130 | defaultCommands = mkOption { | ||
| 131 | type = types.listOf types.str; | ||
| 132 | default = ["rmail"]; | ||
| 133 | description = "Commands allowed for remotes without explicit override"; | ||
| 134 | }; | ||
| 135 | |||
| 136 | defaultProtocols = mkOption { | ||
| 137 | type = types.separatedString ""; | ||
| 138 | default = "te"; | ||
| 139 | description = "UUCP protocol to use within ssh unless overriden"; | ||
| 140 | }; | ||
| 141 | |||
| 142 | incomingProtocols = mkOption { | ||
| 143 | type = types.separatedString ""; | ||
| 144 | default = "te"; | ||
| 145 | description = "UUCP protocols to use when called"; | ||
| 146 | }; | ||
| 147 | |||
| 148 | homeDir = mkOption { | ||
| 149 | type = types.path; | ||
| 150 | default = "/var/uucp"; | ||
| 151 | description = "Home of the uucp user"; | ||
| 152 | }; | ||
| 153 | |||
| 154 | sshKeyDir = mkOption { | ||
| 155 | type = types.path; | ||
| 156 | default = "${cfg.homeDir}/.ssh/"; | ||
| 157 | defaultText = literalExpression ''''${config.services.uucp.homeDir}/.ssh/''; | ||
| 158 | description = "Directory to store ssh keypairs"; | ||
| 159 | }; | ||
| 160 | |||
| 161 | spoolDir = mkOption { | ||
| 162 | type = types.path; | ||
| 163 | default = "/var/spool/uucp"; | ||
| 164 | description = "Spool directory"; | ||
| 165 | }; | ||
| 166 | |||
| 167 | lockDir = mkOption { | ||
| 168 | type = types.path; | ||
| 169 | default = "/var/spool/uucp"; | ||
| 170 | description = "Lock directory"; | ||
| 171 | }; | ||
| 172 | |||
| 173 | pubDir = mkOption { | ||
| 174 | type = types.path; | ||
| 175 | default = "/var/spool/uucppublic"; | ||
| 176 | description = "Public directory"; | ||
| 177 | }; | ||
| 178 | |||
| 179 | logFile = mkOption { | ||
| 180 | type = types.path; | ||
| 181 | default = "/var/log/uucp"; | ||
| 182 | description = "Log file"; | ||
| 183 | }; | ||
| 184 | |||
| 185 | statFile = mkOption { | ||
| 186 | type = types.path; | ||
| 187 | default = "/var/log/uucp.stat"; | ||
| 188 | description = "Statistics file"; | ||
| 189 | }; | ||
| 190 | |||
| 191 | debugFile = mkOption { | ||
| 192 | type = types.path; | ||
| 193 | default = "/var/log/uucp.debug"; | ||
| 194 | description = "Debug file"; | ||
| 195 | }; | ||
| 196 | |||
| 197 | interval = mkOption { | ||
| 198 | type = types.nullOr types.str; | ||
| 199 | default = "1h"; | ||
| 200 | description = '' | ||
| 201 | Specification of when to run `uucico' in format used by systemd timers | ||
| 202 | The default is to do so every hour | ||
| 203 | ''; | ||
| 204 | }; | ||
| 205 | |||
| 206 | nmDispatch = mkOption { | ||
| 207 | type = types.bool; | ||
| 208 | default = config.networking.networkmanager.enable; | ||
| 209 | defaultText = literalExpression "config.networking.networkmanager.enable"; | ||
| 210 | description = '' | ||
| 211 | Install a network-manager dispatcher script to automatically | ||
| 212 | call all remotes when networking is available | ||
| 213 | ''; | ||
| 214 | }; | ||
| 215 | |||
| 216 | extraConfig = mkOption { | ||
| 217 | type = types.lines; | ||
| 218 | default = '' | ||
| 219 | run-uuxqt 1 | ||
| 220 | ''; | ||
| 221 | description = "Extra configuration to append verbatim to `/etc/uucp/config'"; | ||
| 222 | }; | ||
| 223 | |||
| 224 | extraSys = mkOption { | ||
| 225 | type = types.lines; | ||
| 226 | default = '' | ||
| 227 | protocol-parameter g packet-size 4096 | ||
| 228 | ''; | ||
| 229 | description = "Extra configuration to prepend verbatim to `/etc/uucp/sys`"; | ||
| 230 | }; | ||
| 231 | }; | ||
| 232 | }; | ||
| 233 | |||
| 234 | config = mkIf cfg.enable { | ||
| 235 | environment.etc."uucp/config" = { | ||
| 236 | text = '' | ||
| 237 | hostname ${cfg.nodeName} | ||
| 238 | |||
| 239 | spool ${cfg.spoolDir} | ||
| 240 | lockdir ${cfg.lockDir} | ||
| 241 | pubdir ${cfg.pubDir} | ||
| 242 | logfile ${cfg.logFile} | ||
| 243 | statfile ${cfg.statFile} | ||
| 244 | debugfile ${cfg.debugFile} | ||
| 245 | |||
| 246 | ${cfg.extraConfig} | ||
| 247 | ''; | ||
| 248 | }; | ||
| 249 | |||
| 250 | users.groups."uucp" = {}; | ||
| 251 | users.users."uucp" = { | ||
| 252 | name = "uucp"; | ||
| 253 | group = "uucp"; | ||
| 254 | isSystemUser = true; | ||
| 255 | isNormalUser = false; | ||
| 256 | createHome = true; | ||
| 257 | home = cfg.homeDir; | ||
| 258 | description = "User for uucp over ssh"; | ||
| 259 | useDefaultShell = true; | ||
| 260 | openssh.authorizedKeys.keys = map restrictKey (concatLists (mapAttrsToList (name: node: node.publicKeys) cfg.remoteNodes)); | ||
| 261 | } // cfg.sshUser; | ||
| 262 | |||
| 263 | system.activationScripts."uucp-sshconfig" = '' | ||
| 264 | mkdir -p ${config.users.users."uucp".home}/.ssh | ||
| 265 | chown ${config.users.users."uucp".name}:${config.users.users."uucp".group} ${config.users.users."uucp".home}/.ssh | ||
| 266 | chmod 700 ${config.users.users."uucp".home}/.ssh | ||
| 267 | ln -fs ${builtins.toFile "ssh-config" '' | ||
| 268 | ${concatStringsSep "\n" (mapAttrsToList sshConfig cfg.remoteNodes)} | ||
| 269 | |||
| 270 | ${cfg.extraSSHConfig} | ||
| 271 | ''} ${config.users.users."uucp".home}/.ssh/config | ||
| 272 | |||
| 273 | mkdir -p ${cfg.sshKeyDir} | ||
| 274 | chown ${config.users.users."uucp".name}:${config.users.users."uucp".group} ${cfg.sshKeyDir} | ||
| 275 | chmod 700 ${cfg.sshKeyDir} | ||
| 276 | |||
| 277 | ${concatStringsSep "\n" (mapAttrsToList sshKeyGen cfg.remoteNodes)} | ||
| 278 | ''; | ||
| 279 | |||
| 280 | system.activationScripts."uucp-logs" = '' | ||
| 281 | touch ${cfg.logFile} | ||
| 282 | chown ${config.users.users."uucp".name}:${config.users.users."uucp".group} ${cfg.logFile} | ||
| 283 | chmod 644 ${cfg.logFile} | ||
| 284 | touch ${cfg.statFile} | ||
| 285 | chown ${config.users.users."uucp".name}:${config.users.users."uucp".group} ${cfg.statFile} | ||
| 286 | chmod 644 ${cfg.statFile} | ||
| 287 | touch ${cfg.debugFile} | ||
| 288 | chown ${config.users.users."uucp".name}:${config.users.users."uucp".group} ${cfg.debugFile} | ||
| 289 | chmod 644 ${cfg.debugFile} | ||
| 290 | ''; | ||
| 291 | |||
| 292 | environment.etc."uucp/port" = { | ||
| 293 | text = '' | ||
| 294 | port ssh | ||
| 295 | type stdin | ||
| 296 | protocol ${cfg.incomingProtocols} | ||
| 297 | '' + concatStringsSep "\n" (mapAttrsToList portSpec cfg.remoteNodes); | ||
| 298 | }; | ||
| 299 | environment.etc."uucp/sys" = { | ||
| 300 | text = cfg.extraSys + "\n" + concatStringsSep "\n" (mapAttrsToList sysSpec cfg.remoteNodes); | ||
| 301 | }; | ||
| 302 | |||
| 303 | security.wrappers = let | ||
| 304 | wrapper = p: { | ||
| 305 | name = p; | ||
| 306 | value = { | ||
| 307 | source = "${pkgs.uucp}/bin/${p}"; | ||
| 308 | owner = "root"; | ||
| 309 | group = "root"; | ||
| 310 | setuid = true; | ||
| 311 | setgid = false; | ||
| 312 | }; | ||
| 313 | }; | ||
| 314 | in listToAttrs (map wrapper ["uucico" "cu" "uucp" "uuname" "uustat" "uux" "uuxqt"]); | ||
| 315 | |||
| 316 | nixpkgs.overlays = [(self: super: { | ||
| 317 | uucp = super.lib.overrideDerivation super.uucp (oldAttrs: { | ||
| 318 | configureFlags = "--with-newconfigdir=/etc/uucp"; | ||
| 319 | patches = [ | ||
| 320 | (super.writeText "mailprogram" '' | ||
| 321 | policy.h | 2 +- | ||
| 322 | 1 file changed, 1 insertion(+), 1 deletion(-) | ||
| 323 | |||
| 324 | diff --git a/policy.h b/policy.h | ||
| 325 | index 5afe34b..8e92c8b 100644 | ||
| 326 | --- a/policy.h | ||
| 327 | +++ b/policy.h | ||
| 328 | @@ -240,7 +240,7 @@ | ||
| 329 | the sendmail choice below. Otherwise, select one of the other | ||
| 330 | choices as appropriate. */ | ||
| 331 | #if 1 | ||
| 332 | -#define MAIL_PROGRAM "/usr/lib/sendmail -t" | ||
| 333 | +#define MAIL_PROGRAM "${config.security.wrapperDir}/sendmail -t" | ||
| 334 | /* #define MAIL_PROGRAM "/usr/sbin/sendmail -t" */ | ||
| 335 | #define MAIL_PROGRAM_TO_BODY 1 | ||
| 336 | #define MAIL_PROGRAM_SUBJECT_BODY 1 | ||
| 337 | '') | ||
| 338 | ]; | ||
| 339 | }); | ||
| 340 | rmail = super.writeScriptBin "rmail" '' | ||
| 341 | #!${super.stdenv.shell} | ||
| 342 | |||
| 343 | # Dummy UUCP rmail command for postfix/qmail systems | ||
| 344 | |||
| 345 | IFS=" " read junk from junk junk junk junk junk junk junk relay | ||
| 346 | |||
| 347 | case "$from" in | ||
| 348 | *[@!]*) ;; | ||
| 349 | *) from="$from@$relay";; | ||
| 350 | esac | ||
| 351 | |||
| 352 | exec ${config.security.wrapperDir}/sendmail -G -i -f "$from" -- "$@" | ||
| 353 | ''; | ||
| 354 | })]; | ||
| 355 | |||
| 356 | environment.systemPackages = with pkgs; [ | ||
| 357 | uucp | ||
| 358 | ]; | ||
| 359 | |||
| 360 | systemd.services."uucico@" = { | ||
| 361 | serviceConfig = { | ||
| 362 | User = "uucp"; | ||
| 363 | Type = "oneshot"; | ||
| 364 | ExecStart = "${config.security.wrapperDir}/uucico -D -S %i"; | ||
| 365 | }; | ||
| 366 | }; | ||
| 367 | |||
| 368 | systemd.timers."uucico@" = { | ||
| 369 | timerConfig.OnActiveSec = cfg.interval; | ||
| 370 | timerConfig.OnUnitActiveSec = cfg.interval; | ||
| 371 | }; | ||
| 372 | |||
| 373 | systemd.targets."multi-user" = { | ||
| 374 | wants = mapAttrsToList (name: node: "uucico@${name}.timer") cfg.remoteNodes; | ||
| 375 | }; | ||
| 376 | |||
| 377 | systemd.kill-user.enable = true; | ||
| 378 | systemd.targets."sleep" = { | ||
| 379 | after = [ "kill-user@uucp.service" ]; | ||
| 380 | wants = [ "kill-user@uucp.service" ]; | ||
| 381 | }; | ||
| 382 | |||
| 383 | networking.networkmanager.dispatcherScripts = optional cfg.nmDispatch { | ||
| 384 | type = "basic"; | ||
| 385 | source = pkgs.writeScript "callRemotes.sh" '' | ||
| 386 | #!${pkgs.stdenv.shell} | ||
| 387 | |||
| 388 | shopt -s extglob | ||
| 389 | |||
| 390 | case "''${2}" in | ||
| 391 | (?(vpn-)up) | ||
| 392 | ${concatStringsSep "\n " (mapAttrsToList (name: node: "${pkgs.systemd}/bin/systemctl start uucico@${name}.service") cfg.remoteNodes)} | ||
| 393 | ;; | ||
| 394 | esac | ||
| 395 | ''; | ||
| 396 | }; | ||
| 397 | }; | ||
| 398 | } | ||
diff --git a/nvfetcher.toml b/nvfetcher.toml index c0566373..8e3ba905 100644 --- a/nvfetcher.toml +++ b/nvfetcher.toml | |||
| @@ -78,12 +78,12 @@ git.fetchSubmodules = true | |||
| 78 | src.git = "https://github.com/jgreco/mpv-youtube-quality" | 78 | src.git = "https://github.com/jgreco/mpv-youtube-quality" |
| 79 | fetch.git = "https://github.com/jgreco/mpv-youtube-quality" | 79 | fetch.git = "https://github.com/jgreco/mpv-youtube-quality" |
| 80 | 80 | ||
| 81 | [batman-adv] | 81 | # [batman-adv] |
| 82 | src.webpage = "https://www.open-mesh.org/projects/open-mesh/wiki/Download" | 82 | # src.webpage = "https://www.open-mesh.org/projects/open-mesh/wiki/Download" |
| 83 | src.regex = "The latest version of <a[^\\>]*>batman-adv</a> is <a[^\\>]*>batman-adv-([0-9\\.]+).tar.gz</a>" | 83 | # src.regex = "The latest version of <a[^\\>]*>batman-adv</a> is <a[^\\>]*>batman-adv-([0-9\\.]+).tar.gz</a>" |
| 84 | src.from_pattern = "^.*batman-adv-([0-9\\.]+).tar.gz.*$" | 84 | # src.from_pattern = "^.*batman-adv-([0-9\\.]+).tar.gz.*$" |
| 85 | src.to_pattern = "\\1" | 85 | # src.to_pattern = "\\1" |
| 86 | fetch.tarball = "https://downloads.open-mesh.org/batman/stable/sources/batman-adv/batman-adv-$ver.tar.gz" | 86 | # fetch.tarball = "https://downloads.open-mesh.org/batman/stable/sources/batman-adv/batman-adv-$ver.tar.gz" |
| 87 | 87 | ||
| 88 | [scutiger] | 88 | [scutiger] |
| 89 | src.github_tag = "bk2204/scutiger" | 89 | src.github_tag = "bk2204/scutiger" |
| @@ -107,3 +107,23 @@ fetch.tarball = "https://github.com/JonathonReinhart/spice-record/archive/refs/t | |||
| 107 | [yt-dlp] | 107 | [yt-dlp] |
| 108 | src.pypi = "yt_dlp" | 108 | src.pypi = "yt_dlp" |
| 109 | fetch.pypi = "yt_dlp" | 109 | fetch.pypi = "yt_dlp" |
| 110 | |||
| 111 | [mako] | ||
| 112 | src.git = "https://github.com/emersion/mako" | ||
| 113 | fetch.git = "https://github.com/emersion/mako" | ||
| 114 | |||
| 115 | [swayosd] | ||
| 116 | src.git = "https://github.com/ErikReider/SwayOSD" | ||
| 117 | fetch.git = "https://github.com/ErikReider/SwayOSD" | ||
| 118 | |||
| 119 | [netbootxyz-efi] | ||
| 120 | src.github = "netbootxyz/netboot.xyz" | ||
| 121 | fetch.url = "https://github.com/netbootxyz/netboot.xyz/releases/download/$ver/netboot.xyz.efi" | ||
| 122 | |||
| 123 | [netbootxyz-lkrn] | ||
| 124 | src.github = "netbootxyz/netboot.xyz" | ||
| 125 | fetch.url = "https://github.com/netbootxyz/netboot.xyz/releases/download/$ver/netboot.xyz.lkrn" | ||
| 126 | |||
| 127 | [quickshell] | ||
| 128 | src.git = "https://git.outfoxxed.me/quickshell/quickshell.git" | ||
| 129 | fetch.git = "https://git.outfoxxed.me/quickshell/quickshell.git" | ||
diff --git a/overlays/abs-podcast-autoplaylist/.envrc b/overlays/abs-podcast-autoplaylist/.envrc new file mode 100644 index 00000000..2c909235 --- /dev/null +++ b/overlays/abs-podcast-autoplaylist/.envrc | |||
| @@ -0,0 +1,4 @@ | |||
| 1 | use flake | ||
| 2 | |||
| 3 | [[ -d ".venv" ]] || ( uv venv && uv sync ) | ||
| 4 | . .venv/bin/activate | ||
diff --git a/overlays/abs-podcast-autoplaylist/.gitignore b/overlays/abs-podcast-autoplaylist/.gitignore new file mode 100644 index 00000000..4ccfae70 --- /dev/null +++ b/overlays/abs-podcast-autoplaylist/.gitignore | |||
| @@ -0,0 +1,2 @@ | |||
| 1 | .venv | ||
| 2 | **/__pycache__ | ||
diff --git a/overlays/abs-podcast-autoplaylist/abs_podcast_autoplaylist/__init__.py b/overlays/abs-podcast-autoplaylist/abs_podcast_autoplaylist/__init__.py new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/overlays/abs-podcast-autoplaylist/abs_podcast_autoplaylist/__init__.py | |||
diff --git a/overlays/abs-podcast-autoplaylist/abs_podcast_autoplaylist/__main__.py b/overlays/abs-podcast-autoplaylist/abs_podcast_autoplaylist/__main__.py new file mode 100644 index 00000000..fd739805 --- /dev/null +++ b/overlays/abs-podcast-autoplaylist/abs_podcast_autoplaylist/__main__.py | |||
| @@ -0,0 +1,107 @@ | |||
| 1 | import click | ||
| 2 | from pathlib import Path | ||
| 3 | import tomllib | ||
| 4 | import requests | ||
| 5 | from urllib.parse import urljoin | ||
| 6 | from operator import itemgetter | ||
| 7 | import re | ||
| 8 | from frozendict import frozendict | ||
| 9 | |||
| 10 | class BearerAuth(requests.auth.AuthBase): | ||
| 11 | def __init__(self, token): | ||
| 12 | self.token = token | ||
| 13 | def __call__(self, r): | ||
| 14 | r.headers["authorization"] = "Bearer " + self.token | ||
| 15 | return r | ||
| 16 | |||
| 17 | class ABSSession(requests.Session): | ||
| 18 | def __init__(self, config): | ||
| 19 | super().__init__() | ||
| 20 | self.base_url = config['instance'] | ||
| 21 | self.auth = BearerAuth(config['api_token']) | ||
| 22 | |||
| 23 | def request(self, method, url, *args, **kwargs): | ||
| 24 | joined_url = urljoin(self.base_url, url) | ||
| 25 | return super().request(method, joined_url, *args, **kwargs) | ||
| 26 | |||
| 27 | @click.command() | ||
| 28 | @click.argument('config_file', type=click.Path(dir_okay=False, path_type=Path)) | ||
| 29 | def main(config_file: Path): | ||
| 30 | with config_file.open('rb') as fh: | ||
| 31 | config = tomllib.load(fh) | ||
| 32 | |||
| 33 | with ABSSession(config) as s: | ||
| 34 | libraries = s.get('/api/libraries').json()['libraries'] | ||
| 35 | playlists = s.get('/api/playlists').json()['playlists'] | ||
| 36 | |||
| 37 | for library_config in config['libraries']: | ||
| 38 | [library] = filter(lambda l: l['name'] == library_config['name'], libraries) | ||
| 39 | filtered_playlists = list(filter(lambda p: p['name'] == library_config['playlist'] and p['libraryId'] == library['id'], playlists)) | ||
| 40 | def get_playlist(): | ||
| 41 | playlist = None | ||
| 42 | if filtered_playlists: | ||
| 43 | [playlist] = filtered_playlists | ||
| 44 | if not playlist: | ||
| 45 | playlist = s.post('/api/playlists', json={ | ||
| 46 | 'libraryId': library['id'], | ||
| 47 | 'name': library_config['playlist'], | ||
| 48 | }).json() | ||
| 49 | return playlist | ||
| 50 | |||
| 51 | podcasts = dict() | ||
| 52 | items = s.get('/api/libraries/{}/items'.format(library['id'])).json()['results'] | ||
| 53 | for item in items: | ||
| 54 | item = s.get('/api/items/{}'.format(item['id']), json={'expanded': True}).json() | ||
| 55 | episodes = list() | ||
| 56 | for episode in sorted(item['media']['episodes'], key = itemgetter('publishedAt')): | ||
| 57 | progress = s.get('/api/me/progress/{}/{}'.format(episode['libraryItemId'], episode['id'])) | ||
| 58 | if progress.ok and progress.json()["isFinished"]: | ||
| 59 | continue | ||
| 60 | episodes.append(episode) | ||
| 61 | podcasts[item['media']['metadata']['title']] = list(map(lambda x: frozendict({ 'libraryItemId': x['libraryItemId'], 'episodeId': x['id']}), episodes)) | ||
| 62 | def lookup_podcast(expr): | ||
| 63 | expr = re.compile(expr, flags=re.I) | ||
| 64 | matches = filter(lambda t: expr.search(t), podcasts.keys()) | ||
| 65 | match list(matches): | ||
| 66 | case [x]: | ||
| 67 | return (x,) | ||
| 68 | case _: | ||
| 69 | raise RuntimeError("No unique match for ‘{}’".format(expr)) | ||
| 70 | |||
| 71 | priorities = [ | ||
| 72 | [ | ||
| 73 | k | ||
| 74 | for item in (section if type(section) is list else [section]) | ||
| 75 | for k in lookup_podcast(item) | ||
| 76 | ] | ||
| 77 | for section in library_config['priorities'] | ||
| 78 | ] | ||
| 79 | |||
| 80 | playlist_items = list() | ||
| 81 | for section in priorities: | ||
| 82 | while any(map(lambda item: item in podcasts, section)): | ||
| 83 | for item in section: | ||
| 84 | if not item in podcasts: | ||
| 85 | continue | ||
| 86 | |||
| 87 | if not podcasts[item]: | ||
| 88 | del podcasts[item] | ||
| 89 | continue | ||
| 90 | |||
| 91 | playlist_items.append(podcasts[item].pop(0)) | ||
| 92 | |||
| 93 | playlist = get_playlist() | ||
| 94 | current_playlist_items = map(lambda item: frozendict({ k: v for k, v in item.items() if k in {'libraryItemId', 'episodeId'}}), playlist['items']) | ||
| 95 | |||
| 96 | if current_playlist_items == playlist_items: | ||
| 97 | continue | ||
| 98 | |||
| 99 | to_remove = set(current_playlist_items) - set(playlist_items) | ||
| 100 | if to_remove: | ||
| 101 | s.post('/api/playlists/{}/batch/remove'.format(playlist['id']), json={'items': list(to_remove)}).raise_for_status() | ||
| 102 | playlist = get_playlist() | ||
| 103 | to_add = set(playlist_items) - set(current_playlist_items) | ||
| 104 | if to_add: | ||
| 105 | s.post('/api/playlists/{}/batch/add'.format(playlist['id']), json={'items': list(to_add)}).raise_for_status() | ||
| 106 | |||
| 107 | r = s.patch('/api/playlists/{}'.format(playlist['id']), json={'items': playlist_items}).raise_for_status() | ||
diff --git a/overlays/abs-podcast-autoplaylist/default.nix b/overlays/abs-podcast-autoplaylist/default.nix new file mode 100644 index 00000000..843f1b65 --- /dev/null +++ b/overlays/abs-podcast-autoplaylist/default.nix | |||
| @@ -0,0 +1,19 @@ | |||
| 1 | { prev, final, flake, flakeInputs, ... }: | ||
| 2 | |||
| 3 | let | ||
| 4 | workspace = flakeInputs.uv2nix.lib.workspace.loadWorkspace { workspaceRoot = ./.; }; | ||
| 5 | pythonSet = flake.lib.pythonSet { | ||
| 6 | pkgs = final; | ||
| 7 | python = final.python312; | ||
| 8 | overlay = workspace.mkPyprojectOverlay { | ||
| 9 | sourcePreference = "wheel"; | ||
| 10 | }; | ||
| 11 | }; | ||
| 12 | virtualEnv = pythonSet.mkVirtualEnv "abs-podcast-autoplaylist-env" workspace.deps.default; | ||
| 13 | in { | ||
| 14 | abs-podcast-autoplaylist = virtualEnv.overrideAttrs (oldAttrs: { | ||
| 15 | meta = (oldAttrs.meta or {}) // { | ||
| 16 | mainProgram = "abs-podcast-autoplaylist"; | ||
| 17 | }; | ||
| 18 | }); | ||
| 19 | } | ||
diff --git a/overlays/abs-podcast-autoplaylist/pyproject.toml b/overlays/abs-podcast-autoplaylist/pyproject.toml new file mode 100644 index 00000000..f52a84bc --- /dev/null +++ b/overlays/abs-podcast-autoplaylist/pyproject.toml | |||
| @@ -0,0 +1,16 @@ | |||
| 1 | [project] | ||
| 2 | name = "abs-podcast-autoplaylist" | ||
| 3 | version = "0.1.0" | ||
| 4 | requires-python = ">=3.12" | ||
| 5 | dependencies = [ | ||
| 6 | "click>=8.1.8", | ||
| 7 | "frozendict>=2.4.6", | ||
| 8 | "requests>=2.32.3", | ||
| 9 | ] | ||
| 10 | |||
| 11 | [project.scripts] | ||
| 12 | abs-podcast-autoplaylist = "abs_podcast_autoplaylist.__main__:main" | ||
| 13 | |||
| 14 | [build-system] | ||
| 15 | requires = ["hatchling"] | ||
| 16 | build-backend = "hatchling.build" | ||
diff --git a/overlays/abs-podcast-autoplaylist/uv.lock b/overlays/abs-podcast-autoplaylist/uv.lock new file mode 100644 index 00000000..17de5f0e --- /dev/null +++ b/overlays/abs-podcast-autoplaylist/uv.lock | |||
| @@ -0,0 +1,129 @@ | |||
| 1 | version = 1 | ||
| 2 | revision = 2 | ||
| 3 | requires-python = ">=3.12" | ||
| 4 | |||
| 5 | [[package]] | ||
| 6 | name = "abs-podcast-autoplaylist" | ||
| 7 | version = "0.1.0" | ||
| 8 | source = { editable = "." } | ||
| 9 | dependencies = [ | ||
| 10 | { name = "click" }, | ||
| 11 | { name = "frozendict" }, | ||
| 12 | { name = "requests" }, | ||
| 13 | ] | ||
| 14 | |||
| 15 | [package.metadata] | ||
| 16 | requires-dist = [ | ||
| 17 | { name = "click", specifier = ">=8.1.8" }, | ||
| 18 | { name = "frozendict", specifier = ">=2.4.6" }, | ||
| 19 | { name = "requests", specifier = ">=2.32.3" }, | ||
| 20 | ] | ||
| 21 | |||
| 22 | [[package]] | ||
| 23 | name = "certifi" | ||
| 24 | version = "2025.4.26" | ||
| 25 | source = { registry = "https://pypi.org/simple" } | ||
| 26 | sdist = { url = "https://files.pythonhosted.org/packages/e8/9e/c05b3920a3b7d20d3d3310465f50348e5b3694f4f88c6daf736eef3024c4/certifi-2025.4.26.tar.gz", hash = "sha256:0a816057ea3cdefcef70270d2c515e4506bbc954f417fa5ade2021213bb8f0c6", size = 160705, upload_time = "2025-04-26T02:12:29.51Z" } | ||
| 27 | wheels = [ | ||
| 28 | { url = "https://files.pythonhosted.org/packages/4a/7e/3db2bd1b1f9e95f7cddca6d6e75e2f2bd9f51b1246e546d88addca0106bd/certifi-2025.4.26-py3-none-any.whl", hash = "sha256:30350364dfe371162649852c63336a15c70c6510c2ad5015b21c2345311805f3", size = 159618, upload_time = "2025-04-26T02:12:27.662Z" }, | ||
| 29 | ] | ||
| 30 | |||
| 31 | [[package]] | ||
| 32 | name = "charset-normalizer" | ||
| 33 | version = "3.4.2" | ||
| 34 | source = { registry = "https://pypi.org/simple" } | ||
| 35 | sdist = { url = "https://files.pythonhosted.org/packages/e4/33/89c2ced2b67d1c2a61c19c6751aa8902d46ce3dacb23600a283619f5a12d/charset_normalizer-3.4.2.tar.gz", hash = "sha256:5baececa9ecba31eff645232d59845c07aa030f0c81ee70184a90d35099a0e63", size = 126367, upload_time = "2025-05-02T08:34:42.01Z" } | ||
| 36 | wheels = [ | ||
| 37 | { url = "https://files.pythonhosted.org/packages/d7/a4/37f4d6035c89cac7930395a35cc0f1b872e652eaafb76a6075943754f095/charset_normalizer-3.4.2-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:0c29de6a1a95f24b9a1aa7aefd27d2487263f00dfd55a77719b530788f75cff7", size = 199936, upload_time = "2025-05-02T08:32:33.712Z" }, | ||
| 38 | { url = "https://files.pythonhosted.org/packages/ee/8a/1a5e33b73e0d9287274f899d967907cd0bf9c343e651755d9307e0dbf2b3/charset_normalizer-3.4.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cddf7bd982eaa998934a91f69d182aec997c6c468898efe6679af88283b498d3", size = 143790, upload_time = "2025-05-02T08:32:35.768Z" }, | ||
| 39 | { url = "https://files.pythonhosted.org/packages/66/52/59521f1d8e6ab1482164fa21409c5ef44da3e9f653c13ba71becdd98dec3/charset_normalizer-3.4.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fcbe676a55d7445b22c10967bceaaf0ee69407fbe0ece4d032b6eb8d4565982a", size = 153924, upload_time = "2025-05-02T08:32:37.284Z" }, | ||
| 40 | { url = "https://files.pythonhosted.org/packages/86/2d/fb55fdf41964ec782febbf33cb64be480a6b8f16ded2dbe8db27a405c09f/charset_normalizer-3.4.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d41c4d287cfc69060fa91cae9683eacffad989f1a10811995fa309df656ec214", size = 146626, upload_time = "2025-05-02T08:32:38.803Z" }, | ||
| 41 | { url = "https://files.pythonhosted.org/packages/8c/73/6ede2ec59bce19b3edf4209d70004253ec5f4e319f9a2e3f2f15601ed5f7/charset_normalizer-3.4.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4e594135de17ab3866138f496755f302b72157d115086d100c3f19370839dd3a", size = 148567, upload_time = "2025-05-02T08:32:40.251Z" }, | ||
| 42 | { url = "https://files.pythonhosted.org/packages/09/14/957d03c6dc343c04904530b6bef4e5efae5ec7d7990a7cbb868e4595ee30/charset_normalizer-3.4.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cf713fe9a71ef6fd5adf7a79670135081cd4431c2943864757f0fa3a65b1fafd", size = 150957, upload_time = "2025-05-02T08:32:41.705Z" }, | ||
| 43 | { url = "https://files.pythonhosted.org/packages/0d/c8/8174d0e5c10ccebdcb1b53cc959591c4c722a3ad92461a273e86b9f5a302/charset_normalizer-3.4.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:a370b3e078e418187da8c3674eddb9d983ec09445c99a3a263c2011993522981", size = 145408, upload_time = "2025-05-02T08:32:43.709Z" }, | ||
| 44 | { url = "https://files.pythonhosted.org/packages/58/aa/8904b84bc8084ac19dc52feb4f5952c6df03ffb460a887b42615ee1382e8/charset_normalizer-3.4.2-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:a955b438e62efdf7e0b7b52a64dc5c3396e2634baa62471768a64bc2adb73d5c", size = 153399, upload_time = "2025-05-02T08:32:46.197Z" }, | ||
| 45 | { url = "https://files.pythonhosted.org/packages/c2/26/89ee1f0e264d201cb65cf054aca6038c03b1a0c6b4ae998070392a3ce605/charset_normalizer-3.4.2-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:7222ffd5e4de8e57e03ce2cef95a4c43c98fcb72ad86909abdfc2c17d227fc1b", size = 156815, upload_time = "2025-05-02T08:32:48.105Z" }, | ||
| 46 | { url = "https://files.pythonhosted.org/packages/fd/07/68e95b4b345bad3dbbd3a8681737b4338ff2c9df29856a6d6d23ac4c73cb/charset_normalizer-3.4.2-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:bee093bf902e1d8fc0ac143c88902c3dfc8941f7ea1d6a8dd2bcb786d33db03d", size = 154537, upload_time = "2025-05-02T08:32:49.719Z" }, | ||
| 47 | { url = "https://files.pythonhosted.org/packages/77/1a/5eefc0ce04affb98af07bc05f3bac9094513c0e23b0562d64af46a06aae4/charset_normalizer-3.4.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:dedb8adb91d11846ee08bec4c8236c8549ac721c245678282dcb06b221aab59f", size = 149565, upload_time = "2025-05-02T08:32:51.404Z" }, | ||
| 48 | { url = "https://files.pythonhosted.org/packages/37/a0/2410e5e6032a174c95e0806b1a6585eb21e12f445ebe239fac441995226a/charset_normalizer-3.4.2-cp312-cp312-win32.whl", hash = "sha256:db4c7bf0e07fc3b7d89ac2a5880a6a8062056801b83ff56d8464b70f65482b6c", size = 98357, upload_time = "2025-05-02T08:32:53.079Z" }, | ||
| 49 | { url = "https://files.pythonhosted.org/packages/6c/4f/c02d5c493967af3eda9c771ad4d2bbc8df6f99ddbeb37ceea6e8716a32bc/charset_normalizer-3.4.2-cp312-cp312-win_amd64.whl", hash = "sha256:5a9979887252a82fefd3d3ed2a8e3b937a7a809f65dcb1e068b090e165bbe99e", size = 105776, upload_time = "2025-05-02T08:32:54.573Z" }, | ||
| 50 | { url = "https://files.pythonhosted.org/packages/ea/12/a93df3366ed32db1d907d7593a94f1fe6293903e3e92967bebd6950ed12c/charset_normalizer-3.4.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:926ca93accd5d36ccdabd803392ddc3e03e6d4cd1cf17deff3b989ab8e9dbcf0", size = 199622, upload_time = "2025-05-02T08:32:56.363Z" }, | ||
| 51 | { url = "https://files.pythonhosted.org/packages/04/93/bf204e6f344c39d9937d3c13c8cd5bbfc266472e51fc8c07cb7f64fcd2de/charset_normalizer-3.4.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:eba9904b0f38a143592d9fc0e19e2df0fa2e41c3c3745554761c5f6447eedabf", size = 143435, upload_time = "2025-05-02T08:32:58.551Z" }, | ||
| 52 | { url = "https://files.pythonhosted.org/packages/22/2a/ea8a2095b0bafa6c5b5a55ffdc2f924455233ee7b91c69b7edfcc9e02284/charset_normalizer-3.4.2-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3fddb7e2c84ac87ac3a947cb4e66d143ca5863ef48e4a5ecb83bd48619e4634e", size = 153653, upload_time = "2025-05-02T08:33:00.342Z" }, | ||
| 53 | { url = "https://files.pythonhosted.org/packages/b6/57/1b090ff183d13cef485dfbe272e2fe57622a76694061353c59da52c9a659/charset_normalizer-3.4.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:98f862da73774290f251b9df8d11161b6cf25b599a66baf087c1ffe340e9bfd1", size = 146231, upload_time = "2025-05-02T08:33:02.081Z" }, | ||
| 54 | { url = "https://files.pythonhosted.org/packages/e2/28/ffc026b26f441fc67bd21ab7f03b313ab3fe46714a14b516f931abe1a2d8/charset_normalizer-3.4.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6c9379d65defcab82d07b2a9dfbfc2e95bc8fe0ebb1b176a3190230a3ef0e07c", size = 148243, upload_time = "2025-05-02T08:33:04.063Z" }, | ||
| 55 | { url = "https://files.pythonhosted.org/packages/c0/0f/9abe9bd191629c33e69e47c6ef45ef99773320e9ad8e9cb08b8ab4a8d4cb/charset_normalizer-3.4.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e635b87f01ebc977342e2697d05b56632f5f879a4f15955dfe8cef2448b51691", size = 150442, upload_time = "2025-05-02T08:33:06.418Z" }, | ||
| 56 | { url = "https://files.pythonhosted.org/packages/67/7c/a123bbcedca91d5916c056407f89a7f5e8fdfce12ba825d7d6b9954a1a3c/charset_normalizer-3.4.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:1c95a1e2902a8b722868587c0e1184ad5c55631de5afc0eb96bc4b0d738092c0", size = 145147, upload_time = "2025-05-02T08:33:08.183Z" }, | ||
| 57 | { url = "https://files.pythonhosted.org/packages/ec/fe/1ac556fa4899d967b83e9893788e86b6af4d83e4726511eaaad035e36595/charset_normalizer-3.4.2-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:ef8de666d6179b009dce7bcb2ad4c4a779f113f12caf8dc77f0162c29d20490b", size = 153057, upload_time = "2025-05-02T08:33:09.986Z" }, | ||
| 58 | { url = "https://files.pythonhosted.org/packages/2b/ff/acfc0b0a70b19e3e54febdd5301a98b72fa07635e56f24f60502e954c461/charset_normalizer-3.4.2-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:32fc0341d72e0f73f80acb0a2c94216bd704f4f0bce10aedea38f30502b271ff", size = 156454, upload_time = "2025-05-02T08:33:11.814Z" }, | ||
| 59 | { url = "https://files.pythonhosted.org/packages/92/08/95b458ce9c740d0645feb0e96cea1f5ec946ea9c580a94adfe0b617f3573/charset_normalizer-3.4.2-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:289200a18fa698949d2b39c671c2cc7a24d44096784e76614899a7ccf2574b7b", size = 154174, upload_time = "2025-05-02T08:33:13.707Z" }, | ||
| 60 | { url = "https://files.pythonhosted.org/packages/78/be/8392efc43487ac051eee6c36d5fbd63032d78f7728cb37aebcc98191f1ff/charset_normalizer-3.4.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:4a476b06fbcf359ad25d34a057b7219281286ae2477cc5ff5e3f70a246971148", size = 149166, upload_time = "2025-05-02T08:33:15.458Z" }, | ||
| 61 | { url = "https://files.pythonhosted.org/packages/44/96/392abd49b094d30b91d9fbda6a69519e95802250b777841cf3bda8fe136c/charset_normalizer-3.4.2-cp313-cp313-win32.whl", hash = "sha256:aaeeb6a479c7667fbe1099af9617c83aaca22182d6cf8c53966491a0f1b7ffb7", size = 98064, upload_time = "2025-05-02T08:33:17.06Z" }, | ||
| 62 | { url = "https://files.pythonhosted.org/packages/e9/b0/0200da600134e001d91851ddc797809e2fe0ea72de90e09bec5a2fbdaccb/charset_normalizer-3.4.2-cp313-cp313-win_amd64.whl", hash = "sha256:aa6af9e7d59f9c12b33ae4e9450619cf2488e2bbe9b44030905877f0b2324980", size = 105641, upload_time = "2025-05-02T08:33:18.753Z" }, | ||
| 63 | { url = "https://files.pythonhosted.org/packages/20/94/c5790835a017658cbfabd07f3bfb549140c3ac458cfc196323996b10095a/charset_normalizer-3.4.2-py3-none-any.whl", hash = "sha256:7f56930ab0abd1c45cd15be65cc741c28b1c9a34876ce8c17a2fa107810c0af0", size = 52626, upload_time = "2025-05-02T08:34:40.053Z" }, | ||
| 64 | ] | ||
| 65 | |||
| 66 | [[package]] | ||
| 67 | name = "click" | ||
| 68 | version = "8.1.8" | ||
| 69 | source = { registry = "https://pypi.org/simple" } | ||
| 70 | dependencies = [ | ||
| 71 | { name = "colorama", marker = "sys_platform == 'win32'" }, | ||
| 72 | ] | ||
| 73 | sdist = { url = "https://files.pythonhosted.org/packages/b9/2e/0090cbf739cee7d23781ad4b89a9894a41538e4fcf4c31dcdd705b78eb8b/click-8.1.8.tar.gz", hash = "sha256:ed53c9d8990d83c2a27deae68e4ee337473f6330c040a31d4225c9574d16096a", size = 226593, upload_time = "2024-12-21T18:38:44.339Z" } | ||
| 74 | wheels = [ | ||
| 75 | { url = "https://files.pythonhosted.org/packages/7e/d4/7ebdbd03970677812aac39c869717059dbb71a4cfc033ca6e5221787892c/click-8.1.8-py3-none-any.whl", hash = "sha256:63c132bbbed01578a06712a2d1f497bb62d9c1c0d329b7903a866228027263b2", size = 98188, upload_time = "2024-12-21T18:38:41.666Z" }, | ||
| 76 | ] | ||
| 77 | |||
| 78 | [[package]] | ||
| 79 | name = "colorama" | ||
| 80 | version = "0.4.6" | ||
| 81 | source = { registry = "https://pypi.org/simple" } | ||
| 82 | sdist = { url = "https://files.pythonhosted.org/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", size = 27697, upload_time = "2022-10-25T02:36:22.414Z" } | ||
| 83 | wheels = [ | ||
| 84 | { url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335, upload_time = "2022-10-25T02:36:20.889Z" }, | ||
| 85 | ] | ||
| 86 | |||
| 87 | [[package]] | ||
| 88 | name = "frozendict" | ||
| 89 | version = "2.4.6" | ||
| 90 | source = { registry = "https://pypi.org/simple" } | ||
| 91 | sdist = { url = "https://files.pythonhosted.org/packages/bb/59/19eb300ba28e7547538bdf603f1c6c34793240a90e1a7b61b65d8517e35e/frozendict-2.4.6.tar.gz", hash = "sha256:df7cd16470fbd26fc4969a208efadc46319334eb97def1ddf48919b351192b8e", size = 316416, upload_time = "2024-10-13T12:15:32.449Z" } | ||
| 92 | wheels = [ | ||
| 93 | { url = "https://files.pythonhosted.org/packages/04/13/d9839089b900fa7b479cce495d62110cddc4bd5630a04d8469916c0e79c5/frozendict-2.4.6-py311-none-any.whl", hash = "sha256:d065db6a44db2e2375c23eac816f1a022feb2fa98cbb50df44a9e83700accbea", size = 16148, upload_time = "2024-10-13T12:15:26.839Z" }, | ||
| 94 | { url = "https://files.pythonhosted.org/packages/ba/d0/d482c39cee2ab2978a892558cf130681d4574ea208e162da8958b31e9250/frozendict-2.4.6-py312-none-any.whl", hash = "sha256:49344abe90fb75f0f9fdefe6d4ef6d4894e640fadab71f11009d52ad97f370b9", size = 16146, upload_time = "2024-10-13T12:15:28.16Z" }, | ||
| 95 | { url = "https://files.pythonhosted.org/packages/a5/8e/b6bf6a0de482d7d7d7a2aaac8fdc4a4d0bb24a809f5ddd422aa7060eb3d2/frozendict-2.4.6-py313-none-any.whl", hash = "sha256:7134a2bb95d4a16556bb5f2b9736dceb6ea848fa5b6f3f6c2d6dba93b44b4757", size = 16146, upload_time = "2024-10-13T12:15:29.495Z" }, | ||
| 96 | ] | ||
| 97 | |||
| 98 | [[package]] | ||
| 99 | name = "idna" | ||
| 100 | version = "3.10" | ||
| 101 | source = { registry = "https://pypi.org/simple" } | ||
| 102 | sdist = { url = "https://files.pythonhosted.org/packages/f1/70/7703c29685631f5a7590aa73f1f1d3fa9a380e654b86af429e0934a32f7d/idna-3.10.tar.gz", hash = "sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9", size = 190490, upload_time = "2024-09-15T18:07:39.745Z" } | ||
| 103 | wheels = [ | ||
| 104 | { url = "https://files.pythonhosted.org/packages/76/c6/c88e154df9c4e1a2a66ccf0005a88dfb2650c1dffb6f5ce603dfbd452ce3/idna-3.10-py3-none-any.whl", hash = "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3", size = 70442, upload_time = "2024-09-15T18:07:37.964Z" }, | ||
| 105 | ] | ||
| 106 | |||
| 107 | [[package]] | ||
| 108 | name = "requests" | ||
| 109 | version = "2.32.3" | ||
| 110 | source = { registry = "https://pypi.org/simple" } | ||
| 111 | dependencies = [ | ||
| 112 | { name = "certifi" }, | ||
| 113 | { name = "charset-normalizer" }, | ||
| 114 | { name = "idna" }, | ||
| 115 | { name = "urllib3" }, | ||
| 116 | ] | ||
| 117 | sdist = { url = "https://files.pythonhosted.org/packages/63/70/2bf7780ad2d390a8d301ad0b550f1581eadbd9a20f896afe06353c2a2913/requests-2.32.3.tar.gz", hash = "sha256:55365417734eb18255590a9ff9eb97e9e1da868d4ccd6402399eaf68af20a760", size = 131218, upload_time = "2024-05-29T15:37:49.536Z" } | ||
| 118 | wheels = [ | ||
| 119 | { url = "https://files.pythonhosted.org/packages/f9/9b/335f9764261e915ed497fcdeb11df5dfd6f7bf257d4a6a2a686d80da4d54/requests-2.32.3-py3-none-any.whl", hash = "sha256:70761cfe03c773ceb22aa2f671b4757976145175cdfca038c02654d061d6dcc6", size = 64928, upload_time = "2024-05-29T15:37:47.027Z" }, | ||
| 120 | ] | ||
| 121 | |||
| 122 | [[package]] | ||
| 123 | name = "urllib3" | ||
| 124 | version = "2.4.0" | ||
| 125 | source = { registry = "https://pypi.org/simple" } | ||
| 126 | sdist = { url = "https://files.pythonhosted.org/packages/8a/78/16493d9c386d8e60e442a35feac5e00f0913c0f4b7c217c11e8ec2ff53e0/urllib3-2.4.0.tar.gz", hash = "sha256:414bc6535b787febd7567804cc015fee39daab8ad86268f1310a9250697de466", size = 390672, upload_time = "2025-04-10T15:23:39.232Z" } | ||
| 127 | wheels = [ | ||
| 128 | { url = "https://files.pythonhosted.org/packages/6b/11/cc635220681e93a0183390e26485430ca2c7b5f9d33b15c74c2861cb8091/urllib3-2.4.0-py3-none-any.whl", hash = "sha256:4e16665048960a0900c702d4a66415956a584919c03361cac9f1df5c5dd7e813", size = 128680, upload_time = "2025-04-10T15:23:37.377Z" }, | ||
| 129 | ] | ||
diff --git a/overlays/batman-adv.nix b/overlays/batman-adv.nix deleted file mode 100644 index cce7dc4f..00000000 --- a/overlays/batman-adv.nix +++ /dev/null | |||
| @@ -1,15 +0,0 @@ | |||
| 1 | { final, prev, sources, ... }: { | ||
| 2 | linuxPackages_latest = prev.linuxPackages_latest.extend (self: super: { | ||
| 3 | batman_adv = super.batman_adv.overrideAttrs (oldAttrs: { | ||
| 4 | version = "${sources.batman-adv.version}-${self.kernel.version}"; | ||
| 5 | inherit (sources.batman-adv) src; | ||
| 6 | }); | ||
| 7 | }); | ||
| 8 | |||
| 9 | linuxPackages_6_2 = prev.linuxPackages_6_2.extend (self: super: { | ||
| 10 | batman_adv = super.batman_adv.overrideAttrs (oldAttrs: { | ||
| 11 | version = "${sources.batman-adv.version}-${self.kernel.version}"; | ||
| 12 | inherit (sources.batman-adv) src; | ||
| 13 | }); | ||
| 14 | }); | ||
| 15 | } | ||
diff --git a/overlays/cake-prometheus-exporter/default.nix b/overlays/cake-prometheus-exporter/default.nix index 3d0acc2d..69a5008c 100644 --- a/overlays/cake-prometheus-exporter/default.nix +++ b/overlays/cake-prometheus-exporter/default.nix | |||
| @@ -1,19 +1,18 @@ | |||
| 1 | { final, prev, ... }: | 1 | { final, prev, ... }: |
| 2 | let | 2 | let |
| 3 | inpPython = final.python310.override {}; | 3 | inpPython = final.python310.override {}; |
| 4 | python = inpPython.withPackages (ps: with ps; []); | ||
| 4 | in { | 5 | in { |
| 5 | cake-prometheus-exporter = prev.stdenv.mkDerivation rec { | 6 | cake-prometheus-exporter = prev.stdenv.mkDerivation rec { |
| 6 | pname = "cake-prometheus-exporter"; | 7 | pname = "cake-prometheus-exporter"; |
| 7 | version = "0.0.0"; | 8 | version = "0.0.0"; |
| 8 | 9 | ||
| 9 | src = ./cake-prometheus-exporter.py; | 10 | src = prev.replaceVars ./cake-prometheus-exporter.py { inherit python; }; |
| 10 | 11 | ||
| 11 | phases = [ "buildPhase" "checkPhase" "installPhase" ]; | 12 | phases = [ "unpackPhase" "checkPhase" "installPhase" ]; |
| 12 | 13 | ||
| 13 | python = inpPython.withPackages (ps: with ps; []); | 14 | unpackPhase = '' |
| 14 | 15 | cp $src cake-prometheus-exporter | |
| 15 | buildPhase = '' | ||
| 16 | substituteAll $src cake-prometheus-exporter | ||
| 17 | ''; | 16 | ''; |
| 18 | 17 | ||
| 19 | doCheck = true; | 18 | doCheck = true; |
diff --git a/overlays/deploy-rs.nix b/overlays/deploy-rs.nix new file mode 100644 index 00000000..678c6f5f --- /dev/null +++ b/overlays/deploy-rs.nix | |||
| @@ -0,0 +1,16 @@ | |||
| 1 | { final, prev, flakeInputs, ... }: prev.lib.composeExtensions | ||
| 2 | flakeInputs.deploy-rs.overlays.default | ||
| 3 | (final: prev: { | ||
| 4 | deploy-rs = prev.deploy-rs // { | ||
| 5 | deploy-rs = prev.symlinkJoin { | ||
| 6 | name = "${prev.deploy-rs.deploy-rs.name}-wrapped"; | ||
| 7 | paths = [ prev.deploy-rs.deploy-rs ]; | ||
| 8 | buildInputs = [ prev.makeWrapper ]; | ||
| 9 | postBuild = '' | ||
| 10 | wrapProgram $out/bin/deploy \ | ||
| 11 | --prefix PATH : ${prev.lib.makeBinPath (with final; [ nix-monitored ])} | ||
| 12 | ''; | ||
| 13 | }; | ||
| 14 | }; | ||
| 15 | }) | ||
| 16 | final prev | ||
diff --git a/overlays/etesync-dav.nix b/overlays/etesync-dav.nix index cec216e2..e0ced1e3 100644 --- a/overlays/etesync-dav.nix +++ b/overlays/etesync-dav.nix | |||
| @@ -1,55 +1,58 @@ | |||
| 1 | { final, prev, ... }: { | 1 | { final, prev, ... }: { |
| 2 | etesync-dav = prev.python3Packages.buildPythonApplication rec { | 2 | etesync-dav = final.python3Packages.buildPythonApplication rec { |
| 3 | pname = "etesync-dav"; | 3 | pname = "etesync-dav"; |
| 4 | version = "0.33.4"; | 4 | version = "0.35.1"; |
| 5 | pyproject = true; | ||
| 5 | 6 | ||
| 6 | src = prev.fetchFromGitHub { | 7 | src = prev.fetchFromGitHub { |
| 7 | owner = "etesync"; | 8 | owner = "etesync"; |
| 8 | repo = "etesync-dav"; | 9 | repo = "etesync-dav"; |
| 9 | rev = "v${version}"; | 10 | tag = "v${version}"; |
| 10 | hash = "sha256-g+rK762tSWPDaBsaTwpTzfK/lqVs+Z/Qrpq2HCpipQE="; | 11 | hash = "sha256-y4BhU2kSn+RWqc5+pJQFhbwfat9cMWD0ED0EXJp25cY="; |
| 11 | }; | 12 | }; |
| 12 | 13 | ||
| 13 | dependencies = with prev.python3Packages; [ | 14 | build-system = with final.python3Packages; [ setuptools ]; |
| 15 | |||
| 16 | dependencies = with final.python3Packages; [ | ||
| 14 | appdirs | 17 | appdirs |
| 15 | etebase | 18 | etebase |
| 16 | etesync | 19 | etesync |
| 17 | flask | 20 | flask |
| 18 | flask-wtf | 21 | flask-wtf |
| 19 | msgpack | 22 | msgpack |
| 20 | setuptools | 23 | requests |
| 21 | (toPythonModule (buildPythonApplication rec { | 24 | requests.optional-dependencies.socks |
| 25 | (buildPythonApplication rec { | ||
| 22 | pname = "radicale"; | 26 | pname = "radicale"; |
| 23 | version = "3.2.3"; | 27 | version = "3.2.0"; |
| 24 | pyproject = true; | 28 | pyproject = true; |
| 25 | 29 | ||
| 26 | src = prev.fetchFromGitHub { | 30 | src = prev.fetchFromGitHub { |
| 27 | owner = "Kozea"; | 31 | owner = "Kozea"; |
| 28 | repo = "Radicale"; | 32 | repo = "Radicale"; |
| 29 | rev = "refs/tags/v${version}"; | 33 | rev = "refs/tags/v${version}"; |
| 30 | hash = "sha256-1IlnXVetQQuKBt6+QVKNeMM6qBQAiUhqc+4x3xOnSdE="; | 34 | hash = "sha256-RxC8VOfdTXJZiAroDHTKjJqGWu65Z5uyb4WK1LOqubQ="; |
| 31 | }; | 35 | }; |
| 32 | 36 | ||
| 37 | postPatch = '' | ||
| 38 | sed -i '/addopts/d' setup.cfg | ||
| 39 | ''; | ||
| 40 | |||
| 33 | build-system = [ | 41 | build-system = [ |
| 34 | setuptools | 42 | setuptools |
| 35 | ]; | 43 | ]; |
| 36 | 44 | ||
| 37 | dependencies = | 45 | dependencies = [ |
| 38 | [ | 46 | defusedxml |
| 39 | defusedxml | 47 | passlib |
| 40 | passlib | 48 | vobject |
| 41 | vobject | 49 | pika |
| 42 | pika | 50 | python-dateutil |
| 43 | python-dateutil | 51 | pytz # https://github.com/Kozea/Radicale/issues/816 |
| 44 | pytz # https://github.com/Kozea/Radicale/issues/816 | 52 | ] ++ passlib.optional-dependencies.bcrypt; |
| 45 | ] | ||
| 46 | ++ passlib.optional-dependencies.bcrypt; | ||
| 47 | 53 | ||
| 48 | doCheck = false; | 54 | doCheck = false; |
| 49 | })) | 55 | }) |
| 50 | requests | ||
| 51 | types-setuptools | ||
| 52 | requests.optional-dependencies.socks | ||
| 53 | ]; | 56 | ]; |
| 54 | 57 | ||
| 55 | doCheck = false; | 58 | doCheck = false; |
diff --git a/overlays/inwx-cdnskey/default.nix b/overlays/inwx-cdnskey/default.nix index cd564f24..e1bee0f2 100644 --- a/overlays/inwx-cdnskey/default.nix +++ b/overlays/inwx-cdnskey/default.nix | |||
| @@ -2,17 +2,16 @@ | |||
| 2 | let | 2 | let |
| 3 | packageOverrides = final.callPackage ./python-packages.nix {}; | 3 | packageOverrides = final.callPackage ./python-packages.nix {}; |
| 4 | inpPython = final.python39.override { inherit packageOverrides; }; | 4 | inpPython = final.python39.override { inherit packageOverrides; }; |
| 5 | python = inpPython.withPackages (ps: with ps; [pyxdg inwx-domrobot configparser dnspython]); | ||
| 5 | in { | 6 | in { |
| 6 | inwx-cdnskey = prev.stdenv.mkDerivation rec { | 7 | inwx-cdnskey = prev.stdenv.mkDerivation rec { |
| 7 | name = "inwx-cdnskey"; | 8 | name = "inwx-cdnskey"; |
| 8 | src = ./inwx-cdnskey.py; | 9 | src = prev.replaceVars ./inwx-cdnskey.py { inherit python; }; |
| 9 | 10 | ||
| 10 | phases = [ "buildPhase" "checkPhase" "installPhase" ]; | 11 | phases = [ "unpackPhase" "checkPhase" "installPhase" ]; |
| 11 | 12 | ||
| 12 | python = inpPython.withPackages (ps: with ps; [pyxdg inwx-domrobot configparser dnspython]); | 13 | unpackPhase = '' |
| 13 | 14 | cp $src inwx-cdnskey | |
| 14 | buildPhase = '' | ||
| 15 | substituteAll $src inwx-cdnskey | ||
| 16 | ''; | 15 | ''; |
| 17 | 16 | ||
| 18 | doCheck = true; | 17 | doCheck = true; |
diff --git a/overlays/keepassxc/database-open-dialog.patch b/overlays/keepassxc/database-open-dialog.patch new file mode 100644 index 00000000..dff84846 --- /dev/null +++ b/overlays/keepassxc/database-open-dialog.patch | |||
| @@ -0,0 +1,129 @@ | |||
| 1 | diff --git a/src/browser/BrowserService.cpp b/src/browser/BrowserService.cpp | ||
| 2 | index 60412b5a..c0497d91 100644 | ||
| 3 | --- a/src/browser/BrowserService.cpp | ||
| 4 | +++ b/src/browser/BrowserService.cpp | ||
| 5 | @@ -249,7 +249,7 @@ QJsonObject BrowserService::createNewGroup(const QString& groupName) | ||
| 6 | return result; | ||
| 7 | } | ||
| 8 | |||
| 9 | - auto dialogResult = MessageBox::warning(m_currentDatabaseWidget, | ||
| 10 | + auto dialogResult = MessageBox::warning(nullptr, | ||
| 11 | tr("KeePassXC - Create a new group"), | ||
| 12 | tr("A request for creating a new group \"%1\" has been received.\n" | ||
| 13 | "Do you want to create this group?\n") | ||
| 14 | @@ -422,7 +422,7 @@ QList<Entry*> BrowserService::confirmEntries(QList<Entry*>& entriesToConfirm, | ||
| 15 | |||
| 16 | m_dialogActive = true; | ||
| 17 | updateWindowState(); | ||
| 18 | - BrowserAccessControlDialog accessControlDialog(m_currentDatabaseWidget); | ||
| 19 | + BrowserAccessControlDialog accessControlDialog{}; | ||
| 20 | |||
| 21 | connect(m_currentDatabaseWidget, SIGNAL(databaseLockRequested()), &accessControlDialog, SLOT(reject())); | ||
| 22 | |||
| 23 | @@ -512,7 +512,7 @@ QString BrowserService::storeKey(const QString& key) | ||
| 24 | QString id; | ||
| 25 | |||
| 26 | do { | ||
| 27 | - QInputDialog keyDialog(m_currentDatabaseWidget); | ||
| 28 | + QInputDialog keyDialog{}; | ||
| 29 | connect(m_currentDatabaseWidget, SIGNAL(databaseLockRequested()), &keyDialog, SLOT(reject())); | ||
| 30 | keyDialog.setWindowTitle(tr("KeePassXC - New key association request")); | ||
| 31 | keyDialog.setLabelText(tr("You have received an association request for the following database:\n%1\n\n" | ||
| 32 | @@ -535,7 +535,7 @@ QString BrowserService::storeKey(const QString& key) | ||
| 33 | |||
| 34 | contains = db->metadata()->customData()->contains(CustomData::BrowserKeyPrefix + id); | ||
| 35 | if (contains) { | ||
| 36 | - dialogResult = MessageBox::warning(m_currentDatabaseWidget, | ||
| 37 | + dialogResult = MessageBox::warning(nullptr, | ||
| 38 | tr("KeePassXC - Overwrite existing key?"), | ||
| 39 | tr("A shared encryption key with the name \"%1\" " | ||
| 40 | "already exists.\nDo you want to overwrite it?") | ||
| 41 | @@ -595,7 +595,7 @@ QJsonObject BrowserService::showPasskeysRegisterPrompt(const QJsonObject& public | ||
| 42 | const auto existingEntries = getPasskeyEntriesWithUserHandle(rpId, userId, keyList); | ||
| 43 | |||
| 44 | raiseWindow(); | ||
| 45 | - BrowserPasskeysConfirmationDialog confirmDialog(m_currentDatabaseWidget); | ||
| 46 | + BrowserPasskeysConfirmationDialog confirmDialog{}; | ||
| 47 | confirmDialog.registerCredential(username, rpId, existingEntries, timeout); | ||
| 48 | |||
| 49 | auto dialogResult = confirmDialog.exec(); | ||
| 50 | @@ -612,7 +612,7 @@ QJsonObject BrowserService::showPasskeysRegisterPrompt(const QJsonObject& public | ||
| 51 | // If no entry is selected, show the import dialog for manual entry selection | ||
| 52 | auto selectedEntry = confirmDialog.getSelectedEntry(); | ||
| 53 | if (!selectedEntry) { | ||
| 54 | - PasskeyImporter passkeyImporter(m_currentDatabaseWidget); | ||
| 55 | + PasskeyImporter passkeyImporter{}; | ||
| 56 | const auto result = passkeyImporter.showImportDialog(db, | ||
| 57 | nullptr, | ||
| 58 | origin, | ||
| 59 | @@ -683,7 +683,7 @@ QJsonObject BrowserService::showPasskeysAuthenticationPrompt(const QJsonObject& | ||
| 60 | const auto timeout = publicKeyOptions["timeout"].toInt(); | ||
| 61 | |||
| 62 | raiseWindow(); | ||
| 63 | - BrowserPasskeysConfirmationDialog confirmDialog(m_currentDatabaseWidget); | ||
| 64 | + BrowserPasskeysConfirmationDialog confirmDialog{}; | ||
| 65 | confirmDialog.authenticateCredential(entries, rpId, timeout); | ||
| 66 | auto dialogResult = confirmDialog.exec(); | ||
| 67 | if (dialogResult == QDialog::Accepted) { | ||
| 68 | @@ -760,7 +760,7 @@ void BrowserService::addPasskeyToEntry(Entry* entry, | ||
| 69 | |||
| 70 | // Ask confirmation if entry already contains a Passkey | ||
| 71 | if (entry->hasPasskey()) { | ||
| 72 | - if (MessageBox::question(m_currentDatabaseWidget, | ||
| 73 | + if (MessageBox::question(nullptr, | ||
| 74 | tr("KeePassXC - Update passkey"), | ||
| 75 | tr("Entry already has a passkey.\nDo you want to overwrite the passkey in %1 - %2?") | ||
| 76 | .arg(entry->title(), passkeyUtils()->getUsernameFromEntry(entry)), | ||
| 77 | @@ -873,7 +873,7 @@ bool BrowserService::updateEntry(const EntryParameters& entryParameters, const Q | ||
| 78 | MessageBox::Button dialogResult = MessageBox::No; | ||
| 79 | if (!browserSettings()->alwaysAllowUpdate()) { | ||
| 80 | raiseWindow(); | ||
| 81 | - dialogResult = MessageBox::question(m_currentDatabaseWidget, | ||
| 82 | + dialogResult = MessageBox::question(nullptr, | ||
| 83 | tr("KeePassXC - Update Entry"), | ||
| 84 | tr("Do you want to update the information in %1 - %2?") | ||
| 85 | .arg(QUrl(entryParameters.siteUrl).host(), username), | ||
| 86 | @@ -909,7 +909,7 @@ bool BrowserService::deleteEntry(const QString& uuid) | ||
| 87 | return false; | ||
| 88 | } | ||
| 89 | |||
| 90 | - auto dialogResult = MessageBox::warning(m_currentDatabaseWidget, | ||
| 91 | + auto dialogResult = MessageBox::warning(nullptr, | ||
| 92 | tr("KeePassXC - Delete entry"), | ||
| 93 | tr("A request for deleting entry \"%1\" has been received.\n" | ||
| 94 | "Do you want to delete the entry?\n") | ||
| 95 | @@ -1536,7 +1536,7 @@ QSharedPointer<Database> BrowserService::selectedDatabase() | ||
| 96 | } | ||
| 97 | } | ||
| 98 | |||
| 99 | - BrowserEntrySaveDialog browserEntrySaveDialog(m_currentDatabaseWidget); | ||
| 100 | + BrowserEntrySaveDialog browserEntrySaveDialog{}; | ||
| 101 | int openDatabaseCount = browserEntrySaveDialog.setItems(databaseWidgets, m_currentDatabaseWidget); | ||
| 102 | if (openDatabaseCount > 1) { | ||
| 103 | int res = browserEntrySaveDialog.exec(); | ||
| 104 | diff --git a/src/fdosecrets/objects/Prompt.cpp b/src/fdosecrets/objects/Prompt.cpp | ||
| 105 | index e89cd499..347c98b8 100644 | ||
| 106 | --- a/src/fdosecrets/objects/Prompt.cpp | ||
| 107 | +++ b/src/fdosecrets/objects/Prompt.cpp | ||
| 108 | @@ -313,7 +313,7 @@ namespace FdoSecrets | ||
| 109 | if (!entries.isEmpty()) { | ||
| 110 | QString app = tr("%1 (PID: %2)").arg(client->name()).arg(client->pid()); | ||
| 111 | auto ac = new AccessControlDialog( | ||
| 112 | - findWindow(m_windowId), entries, app, client->processInfo(), AuthOption::Remember); | ||
| 113 | + nullptr, entries, app, client->processInfo(), AuthOption::Remember); | ||
| 114 | connect(ac, &AccessControlDialog::finished, this, &UnlockPrompt::itemUnlockFinished); | ||
| 115 | connect(ac, &AccessControlDialog::finished, ac, &AccessControlDialog::deleteLater); | ||
| 116 | ac->open(); | ||
| 117 | diff --git a/src/gui/DatabaseTabWidget.cpp b/src/gui/DatabaseTabWidget.cpp | ||
| 118 | index 805d4eab..4836199e 100644 | ||
| 119 | --- a/src/gui/DatabaseTabWidget.cpp | ||
| 120 | +++ b/src/gui/DatabaseTabWidget.cpp | ||
| 121 | @@ -41,7 +41,7 @@ DatabaseTabWidget::DatabaseTabWidget(QWidget* parent) | ||
| 122 | : QTabWidget(parent) | ||
| 123 | , m_dbWidgetStateSync(new DatabaseWidgetStateSync(this)) | ||
| 124 | , m_dbWidgetPendingLock(nullptr) | ||
| 125 | - , m_databaseOpenDialog(new DatabaseOpenDialog(this)) | ||
| 126 | + , m_databaseOpenDialog(new DatabaseOpenDialog()) | ||
| 127 | , m_databaseOpenInProgress(false) | ||
| 128 | { | ||
| 129 | auto* tabBar = new QTabBar(this); | ||
diff --git a/overlays/keepassxc/default.nix b/overlays/keepassxc/default.nix new file mode 100644 index 00000000..46b3a459 --- /dev/null +++ b/overlays/keepassxc/default.nix | |||
| @@ -0,0 +1,8 @@ | |||
| 1 | { final, prev, ... }: | ||
| 2 | { | ||
| 3 | keepassxc = prev.keepassxc.overrideAttrs (oldAttrs: { | ||
| 4 | patches = (oldAttrs.patches or []) ++ prev.lib.optional (prev.lib.versionAtLeast oldAttrs.version "2.7.9") [ | ||
| 5 | ./database-open-dialog.patch | ||
| 6 | ]; | ||
| 7 | }); | ||
| 8 | } | ||
diff --git a/overlays/lesspipe.nix b/overlays/lesspipe.nix index 3258eb70..b791f6e5 100644 --- a/overlays/lesspipe.nix +++ b/overlays/lesspipe.nix | |||
| @@ -17,7 +17,7 @@ | |||
| 17 | 17 | ||
| 18 | preFixup = '' | 18 | preFixup = '' |
| 19 | wrapProgram $out/bin/lesspipe.sh \ | 19 | wrapProgram $out/bin/lesspipe.sh \ |
| 20 | --prefix PATH : ${final.python3.pkgs.pygments}/bin:${final.file}/bin:${final.ncurses}/bin | 20 | --prefix PATH : ${prev.lib.makeBinPath (with final; [ file ncurses binutils ])} |
| 21 | ''; | 21 | ''; |
| 22 | }; | 22 | }; |
| 23 | } | 23 | } |
diff --git a/overlays/mako.nix b/overlays/mako.nix new file mode 100644 index 00000000..1c1464fb --- /dev/null +++ b/overlays/mako.nix | |||
| @@ -0,0 +1,5 @@ | |||
| 1 | { final, prev, sources, ... }: { | ||
| 2 | mako = prev.mako.overrideAttrs (oldAttrs: { | ||
| 3 | inherit (sources.mako) version src; | ||
| 4 | }); | ||
| 5 | } | ||
diff --git a/overlays/nftables-prometheus-exporter/default.nix b/overlays/nftables-prometheus-exporter/default.nix index aab0c8e9..48f668c4 100644 --- a/overlays/nftables-prometheus-exporter/default.nix +++ b/overlays/nftables-prometheus-exporter/default.nix | |||
| @@ -1,17 +1,16 @@ | |||
| 1 | { final, prev, ... }: | 1 | { final, prev, ... }: |
| 2 | let | 2 | let |
| 3 | inpPython = final.python310; | 3 | inpPython = final.python310; |
| 4 | python = inpPython.withPackages (ps: with ps; []); | ||
| 4 | in { | 5 | in { |
| 5 | nftables-prometheus-exporter = prev.stdenv.mkDerivation rec { | 6 | nftables-prometheus-exporter = prev.stdenv.mkDerivation rec { |
| 6 | name = "nftables-prometheus-exporter"; | 7 | name = "nftables-prometheus-exporter"; |
| 7 | src = ./nftables-prometheus-exporter.py; | 8 | src = prev.replaceVars ./nftables-prometheus-exporter.py { inherit python; }; |
| 8 | 9 | ||
| 9 | phases = [ "buildPhase" "checkPhase" "installPhase" ]; | 10 | phases = [ "unpackPhase" "checkPhase" "installPhase" ]; |
| 10 | 11 | ||
| 11 | python = inpPython.withPackages (ps: with ps; []); | 12 | unpackPhase = '' |
| 12 | 13 | cp $src nftables-prometheus-exporter | |
| 13 | buildPhase = '' | ||
| 14 | substituteAll $src nftables-prometheus-exporter | ||
| 15 | ''; | 14 | ''; |
| 16 | 15 | ||
| 17 | doCheck = true; | 16 | doCheck = true; |
diff --git a/overlays/nftables-prometheus-exporter/nftables-prometheus-exporter.py b/overlays/nftables-prometheus-exporter/nftables-prometheus-exporter.py index 484228c8..60ef4670 100644 --- a/overlays/nftables-prometheus-exporter/nftables-prometheus-exporter.py +++ b/overlays/nftables-prometheus-exporter/nftables-prometheus-exporter.py | |||
| @@ -42,7 +42,7 @@ class NFTMetrics: | |||
| 42 | cls._instance = cls.__new__(cls) | 42 | cls._instance = cls.__new__(cls) |
| 43 | cls._instance.attrs = None | 43 | cls._instance.attrs = None |
| 44 | return cls._instance | 44 | return cls._instance |
| 45 | 45 | ||
| 46 | 46 | ||
| 47 | def __init__(self): | 47 | def __init__(self): |
| 48 | raise RuntimeError('Call instance() instead') | 48 | raise RuntimeError('Call instance() instead') |
| @@ -62,7 +62,7 @@ class NFTMetrics: | |||
| 62 | raise RuntimeError(f'nftables json schema v{version} is not supported') | 62 | raise RuntimeError(f'nftables json schema v{version} is not supported') |
| 63 | queries[query_name] = data['nftables'][1:] | 63 | queries[query_name] = data['nftables'][1:] |
| 64 | 64 | ||
| 65 | 65 | ||
| 66 | def extract_query(query_name, type_name): | 66 | def extract_query(query_name, type_name): |
| 67 | return [ | 67 | return [ |
| 68 | item[type_name] | 68 | item[type_name] |
| @@ -98,21 +98,21 @@ class NFTMetrics: | |||
| 98 | metrics += _format_prom_metrics('nftables_counter_packets_count', 'counter', counter_packets) | 98 | metrics += _format_prom_metrics('nftables_counter_packets_count', 'counter', counter_packets) |
| 99 | 99 | ||
| 100 | map_counts = [] | 100 | map_counts = [] |
| 101 | for meter in self.attrs['maps']: | 101 | for item in self.attrs['maps']: |
| 102 | labels = { k: v for k, v in counter.items() if k not in set(['elem']) } | 102 | labels = { k: v for k, v in counter.items() if k not in set(['elem']) } |
| 103 | map_counts += [(labels, len(meter['elem']))] | 103 | map_counts += [(labels, len(item['elem']) if 'elem' in item else 0)] |
| 104 | metrics += _format_prom_metrics('nftables_map_elem_count', 'gauge', map_counts) | 104 | metrics += _format_prom_metrics('nftables_map_elem_count', 'gauge', map_counts) |
| 105 | 105 | ||
| 106 | meter_counts = [] | 106 | meter_counts = [] |
| 107 | for meter in self.attrs['meters']: | 107 | for item in self.attrs['meters']: |
| 108 | labels = { k: v for k, v in counter.items() if k not in set(['elem']) } | 108 | labels = { k: v for k, v in counter.items() if k not in set(['elem']) } |
| 109 | meter_counts += [(labels, len(meter['elem']))] | 109 | item_counts += [(labels, len(item['elem']) if 'elem' in item else 0)] |
| 110 | metrics += _format_prom_metrics('nftables_meter_elem_count', 'gauge', meter_counts) | 110 | metrics += _format_prom_metrics('nftables_meter_elem_count', 'gauge', meter_counts) |
| 111 | 111 | ||
| 112 | set_counts = [] | 112 | set_counts = [] |
| 113 | for meter in self.attrs['sets']: | 113 | for item in self.attrs['sets']: |
| 114 | labels = { k: v for k, v in counter.items() if k not in set(['elem']) } | 114 | labels = { k: v for k, v in counter.items() if k not in set(['elem']) } |
| 115 | set_counts += [(labels, len(meter['elem']))] | 115 | set_counts += [(labels, len(item['elem']) if 'elem' in item else 0)] |
| 116 | metrics += _format_prom_metrics('nftables_set_elem_count', 'gauge', set_counts) | 116 | metrics += _format_prom_metrics('nftables_set_elem_count', 'gauge', set_counts) |
| 117 | 117 | ||
| 118 | return metrics.encode('utf-8') | 118 | return metrics.encode('utf-8') |
| @@ -120,7 +120,7 @@ class NFTMetrics: | |||
| 120 | class NFTMetricsServer(BaseHTTPRequestHandler): | 120 | class NFTMetricsServer(BaseHTTPRequestHandler): |
| 121 | def log_message(self, format, *args): | 121 | def log_message(self, format, *args): |
| 122 | pass | 122 | pass |
| 123 | 123 | ||
| 124 | def do_GET(self): | 124 | def do_GET(self): |
| 125 | nft_metrics = NFTMetrics.instance() | 125 | nft_metrics = NFTMetrics.instance() |
| 126 | nft_metrics.update() | 126 | nft_metrics.update() |
| @@ -138,7 +138,7 @@ class NFTMetricsServer(BaseHTTPRequestHandler): | |||
| 138 | self.send_response(200) | 138 | self.send_response(200) |
| 139 | self.send_header("Content-type", "text/plain") | 139 | self.send_header("Content-type", "text/plain") |
| 140 | self.end_headers() | 140 | self.end_headers() |
| 141 | 141 | ||
| 142 | self.wfile.write(nft_metrics.prometheus()) | 142 | self.wfile.write(nft_metrics.prometheus()) |
| 143 | case _: | 143 | case _: |
| 144 | self.send_response(404) | 144 | self.send_response(404) |
diff --git a/overlays/niri.nix b/overlays/niri.nix new file mode 100644 index 00000000..95a918b0 --- /dev/null +++ b/overlays/niri.nix | |||
| @@ -0,0 +1,9 @@ | |||
| 1 | { final, prev, flakeInputs, ... }: prev.lib.composeExtensions | ||
| 2 | flakeInputs.niri-flake.overlays.niri | ||
| 3 | (final: prev: { | ||
| 4 | niri-unstable = prev.niri-unstable.overrideAttrs (oldAttrs: { | ||
| 5 | buildInputs = (oldAttrs.buildInputs or []) ++ [ final.libgbm ]; | ||
| 6 | doCheck = false; | ||
| 7 | }); | ||
| 8 | }) | ||
| 9 | final prev | ||
diff --git a/overlays/nix-direnv/default.nix b/overlays/nix-direnv/default.nix new file mode 100644 index 00000000..7c488e4e --- /dev/null +++ b/overlays/nix-direnv/default.nix | |||
| @@ -0,0 +1,53 @@ | |||
| 1 | { final, prev, ... }: { | ||
| 2 | nix-direnv = prev.resholve.mkDerivation rec { | ||
| 3 | pname = "nix-direnv"; | ||
| 4 | version = "3.0.6"; | ||
| 5 | |||
| 6 | patches = [ | ||
| 7 | ./static-nix.patch | ||
| 8 | ]; | ||
| 9 | |||
| 10 | src = prev.fetchFromGitHub { | ||
| 11 | owner = "nix-community"; | ||
| 12 | repo = "nix-direnv"; | ||
| 13 | rev = version; | ||
| 14 | hash = "sha256-oNqhPqgQT92yxbKmcgX4F3e2yTUPyXYG7b2xQm3TvQw="; | ||
| 15 | }; | ||
| 16 | |||
| 17 | installPhase = '' | ||
| 18 | runHook preInstall | ||
| 19 | install -m400 -D direnvrc $out/share/nix-direnv/direnvrc | ||
| 20 | runHook postInstall | ||
| 21 | ''; | ||
| 22 | |||
| 23 | solutions = { | ||
| 24 | default = { | ||
| 25 | scripts = [ "share/nix-direnv/direnvrc" ]; | ||
| 26 | interpreter = "none"; | ||
| 27 | inputs = with final; [ coreutils nix-monitored ]; | ||
| 28 | fake = { | ||
| 29 | builtin = [ | ||
| 30 | "PATH_add" | ||
| 31 | "direnv_layout_dir" | ||
| 32 | "has" | ||
| 33 | "log_error" | ||
| 34 | "log_status" | ||
| 35 | "watch_file" | ||
| 36 | ]; | ||
| 37 | function = [ | ||
| 38 | # not really a function - this is in an else branch for macOS/homebrew that | ||
| 39 | # cannot be reached when built with nix | ||
| 40 | "shasum" | ||
| 41 | ]; | ||
| 42 | }; | ||
| 43 | keep = { | ||
| 44 | "$cmd" = true; | ||
| 45 | "$direnv" = true; | ||
| 46 | }; | ||
| 47 | execer = [ | ||
| 48 | "cannot:${prev.lib.getExe' final.nix-monitored "nix"}" | ||
| 49 | ]; | ||
| 50 | }; | ||
| 51 | }; | ||
| 52 | }; | ||
| 53 | } | ||
diff --git a/overlays/nix-direnv/static-nix.patch b/overlays/nix-direnv/static-nix.patch new file mode 100644 index 00000000..5de8193f --- /dev/null +++ b/overlays/nix-direnv/static-nix.patch | |||
| @@ -0,0 +1,62 @@ | |||
| 1 | diff --git i/direnvrc w/direnvrc | ||
| 2 | index ddac0f5..fbcade6 100644 | ||
| 3 | --- i/direnvrc | ||
| 4 | +++ w/direnvrc | ||
| 5 | @@ -29,10 +29,8 @@ _nix_direnv_warning() { | ||
| 6 | |||
| 7 | _nix_direnv_error() { log_error "${_NIX_DIRENV_LOG_PREFIX}$*"; } | ||
| 8 | |||
| 9 | -_nix_direnv_nix="" | ||
| 10 | - | ||
| 11 | _nix() { | ||
| 12 | - ${_nix_direnv_nix} --extra-experimental-features "nix-command flakes" "$@" | ||
| 13 | + nix --extra-experimental-features "nix-command flakes" "$@" | ||
| 14 | } | ||
| 15 | |||
| 16 | _require_version() { | ||
| 17 | @@ -55,34 +53,6 @@ _require_cmd_version() { | ||
| 18 | _require_version "$cmd" "${BASH_REMATCH[1]}" "$required" | ||
| 19 | } | ||
| 20 | |||
| 21 | -_nix_direnv_resolve_nix() { | ||
| 22 | - local ambient_nix | ||
| 23 | - | ||
| 24 | - if ambient_nix=$(command -v nix); then | ||
| 25 | - if _require_cmd_version "${ambient_nix}" "${NIX_MIN_VERSION}"; then | ||
| 26 | - echo "${ambient_nix}" | ||
| 27 | - return 0 | ||
| 28 | - else | ||
| 29 | - _nix_direnv_warning "Nix version in PATH is too old, wanted ${NIX_MIN_VERSION}+, got $(${ambient_nix} --version), will attempt fallback" | ||
| 30 | - fi | ||
| 31 | - else | ||
| 32 | - _nix_direnv_warning "Could not find Nix in PATH, will attempt fallback" | ||
| 33 | - fi | ||
| 34 | - | ||
| 35 | - if [ -n "${NIX_DIRENV_FALLBACK_NIX}" ]; then | ||
| 36 | - if _require_cmd_version "${NIX_DIRENV_FALLBACK_NIX}" "${NIX_MIN_VERSION}"; then | ||
| 37 | - echo "${NIX_DIRENV_FALLBACK_NIX}" | ||
| 38 | - return 0 | ||
| 39 | - else | ||
| 40 | - _nix_direnv_error "Fallback Nix version is too old, wanted ${NIX_MIN_VERSION}+, got $(${NIX_DIRENV_FALLBACK_NIX} --version)" | ||
| 41 | - return 1 | ||
| 42 | - fi | ||
| 43 | - else | ||
| 44 | - _nix_direnv_error "Could not find fallback Nix binary, please add Nix to PATH or set NIX_DIRENV_FALLBACK_NIX" | ||
| 45 | - return 1 | ||
| 46 | - fi | ||
| 47 | -} | ||
| 48 | - | ||
| 49 | _nix_direnv_preflight() { | ||
| 50 | if [[ -z $direnv ]]; then | ||
| 51 | # shellcheck disable=2016 | ||
| 52 | @@ -102,10 +72,6 @@ _nix_direnv_preflight() { | ||
| 53 | fi | ||
| 54 | fi | ||
| 55 | |||
| 56 | - if ! _nix_direnv_nix=$(_nix_direnv_resolve_nix); then | ||
| 57 | - return 1 | ||
| 58 | - fi | ||
| 59 | - | ||
| 60 | local layout_dir | ||
| 61 | layout_dir=$(direnv_layout_dir) | ||
| 62 | |||
diff --git a/overlays/nix-monitored.nix b/overlays/nix-monitored.nix new file mode 100644 index 00000000..9f6caff1 --- /dev/null +++ b/overlays/nix-monitored.nix | |||
| @@ -0,0 +1,8 @@ | |||
| 1 | { final, prev, flakeInputs, ... }: prev.lib.composeExtensions | ||
| 2 | flakeInputs.nix-monitored.overlays.default | ||
| 3 | (final: prev: { | ||
| 4 | nix-monitored = prev.nix-monitored.override { | ||
| 5 | withNotify = false; | ||
| 6 | }; | ||
| 7 | }) | ||
| 8 | final prev | ||
diff --git a/overlays/persistent-nix-shell/default.nix b/overlays/persistent-nix-shell/default.nix index c36b9e86..6067cade 100644 --- a/overlays/persistent-nix-shell/default.nix +++ b/overlays/persistent-nix-shell/default.nix | |||
| @@ -5,10 +5,9 @@ | |||
| 5 | 5 | ||
| 6 | phases = [ "buildPhase" "installPhase" ]; | 6 | phases = [ "buildPhase" "installPhase" ]; |
| 7 | 7 | ||
| 8 | inherit (final) zsh; | ||
| 9 | |||
| 10 | buildPhase = '' | 8 | buildPhase = '' |
| 11 | substituteAll $src persistent-nix-shell | 9 | substitute $src persistent-nix-shell \ |
| 10 | --subst-var-by zsh ${final.zsh} | ||
| 12 | ''; | 11 | ''; |
| 13 | 12 | ||
| 14 | installPhase = '' | 13 | installPhase = '' |
diff --git a/overlays/postsrsd.nix b/overlays/postsrsd.nix new file mode 100644 index 00000000..cb1ccf30 --- /dev/null +++ b/overlays/postsrsd.nix | |||
| @@ -0,0 +1,11 @@ | |||
| 1 | { final, prev, ... }: | ||
| 2 | { | ||
| 3 | postsrsd = prev.postsrsd.overrideAttrs (oldAttrs: { | ||
| 4 | cmakeFlags = (oldAttrs.cmakeFlags or []) ++ [ | ||
| 5 | "-DWITH_MILTER=ON" | ||
| 6 | ]; | ||
| 7 | buildInputs = (oldAttrs.buildInputs or []) ++ [ | ||
| 8 | final.libmilter | ||
| 9 | ]; | ||
| 10 | }); | ||
| 11 | } | ||
diff --git a/overlays/preserve-dscp/default.nix b/overlays/preserve-dscp/default.nix index 105eccb9..208d69db 100644 --- a/overlays/preserve-dscp/default.nix +++ b/overlays/preserve-dscp/default.nix | |||
| @@ -15,7 +15,7 @@ | |||
| 15 | 15 | ||
| 16 | outputs = [ "out" "lib" ]; | 16 | outputs = [ "out" "lib" ]; |
| 17 | 17 | ||
| 18 | buildInputs = with final; [ elfutils libpcap zlib ]; | 18 | buildInputs = with final; [ elfutils libpcap libcap zlib ]; |
| 19 | nativeBuildInputs = with final; [ llvmPackages.clang llvmPackages.llvm pkg-config bpftools libmnl gnum4 glibc_multi makeWrapper ]; | 19 | nativeBuildInputs = with final; [ llvmPackages.clang llvmPackages.llvm pkg-config bpftools libmnl gnum4 glibc_multi makeWrapper ]; |
| 20 | 20 | ||
| 21 | installPhase = '' | 21 | installPhase = '' |
diff --git a/overlays/prometheus-lvm-exporter.nix b/overlays/prometheus-lvm-exporter.nix index 6b671cab..72d9c7ca 100644 --- a/overlays/prometheus-lvm-exporter.nix +++ b/overlays/prometheus-lvm-exporter.nix | |||
| @@ -3,7 +3,7 @@ | |||
| 3 | pname = "prometheus-lvm-exporter"; | 3 | pname = "prometheus-lvm-exporter"; |
| 4 | inherit (sources.prometheus-lvm-exporter) version src; | 4 | inherit (sources.prometheus-lvm-exporter) version src; |
| 5 | 5 | ||
| 6 | vendorHash = "sha256-vqxsg70ShMo4OVdzhqYDj/HT3RTpCUBGHze/EkbBJig="; | 6 | vendorHash = "sha256-CoTNTCBBugbHWDsOuZY1t8HrpdmEbbSMyVb3+1u0q+g="; |
| 7 | 7 | ||
| 8 | doCheck = false; | 8 | doCheck = false; |
| 9 | 9 | ||
diff --git a/overlays/quickshell/default.nix b/overlays/quickshell/default.nix new file mode 100644 index 00000000..c01fac20 --- /dev/null +++ b/overlays/quickshell/default.nix | |||
| @@ -0,0 +1,13 @@ | |||
| 1 | { final, prev, sources, ... }: | ||
| 2 | { | ||
| 3 | quickshell = prev.quickshell.overrideAttrs (oldAttrs: { | ||
| 4 | inherit (sources.quickshell) version src; | ||
| 5 | |||
| 6 | patches = (oldAttrs.patches or []) ++ [ | ||
| 7 | ./greetd-response.patch | ||
| 8 | ./lock-state-changed.patch | ||
| 9 | ./pipewire.patch | ||
| 10 | ./io.patch | ||
| 11 | ]; | ||
| 12 | }); | ||
| 13 | } | ||
diff --git a/overlays/quickshell/greetd-response.patch b/overlays/quickshell/greetd-response.patch new file mode 100644 index 00000000..a0efb562 --- /dev/null +++ b/overlays/quickshell/greetd-response.patch | |||
| @@ -0,0 +1,16 @@ | |||
| 1 | diff --git c/src/services/greetd/connection.cpp w/src/services/greetd/connection.cpp | ||
| 2 | index bf0d1fd..a790ab7 100644 | ||
| 3 | --- c/src/services/greetd/connection.cpp | ||
| 4 | +++ w/src/services/greetd/connection.cpp | ||
| 5 | @@ -225,6 +225,11 @@ void GreetdConnection::onSocketReady() { | ||
| 6 | |||
| 7 | this->mResponseRequired = responseRequired; | ||
| 8 | emit this->authMessage(message, error, responseRequired, echoResponse); | ||
| 9 | + | ||
| 10 | + if (!responseRequired) | ||
| 11 | + this->sendRequest({ | ||
| 12 | + {"type", "post_auth_message_response"} | ||
| 13 | + }); | ||
| 14 | } else goto unexpected; | ||
| 15 | |||
| 16 | return; | ||
diff --git a/overlays/quickshell/io.patch b/overlays/quickshell/io.patch new file mode 100644 index 00000000..961bdcaf --- /dev/null +++ b/overlays/quickshell/io.patch | |||
| @@ -0,0 +1,13 @@ | |||
| 1 | diff --git i/src/io/socket.cpp w/src/io/socket.cpp | ||
| 2 | index 371f687..d12eaeb 100644 | ||
| 3 | --- i/src/io/socket.cpp | ||
| 4 | +++ w/src/io/socket.cpp | ||
| 5 | @@ -66,7 +66,7 @@ void Socket::onSocketDisconnected() { | ||
| 6 | } | ||
| 7 | |||
| 8 | void Socket::onSocketError(QLocalSocket::LocalSocketError error) { | ||
| 9 | - qCWarning(logSocket) << "Socket error for" << this << error; | ||
| 10 | + // qCWarning(logSocket) << "Socket error for" << this << error; | ||
| 11 | emit this->error(error); | ||
| 12 | } | ||
| 13 | |||
diff --git a/overlays/quickshell/lock-state-changed.patch b/overlays/quickshell/lock-state-changed.patch new file mode 100644 index 00000000..4be273fa --- /dev/null +++ b/overlays/quickshell/lock-state-changed.patch | |||
| @@ -0,0 +1,12 @@ | |||
| 1 | diff --git i/src/wayland/session_lock.cpp w/src/wayland/session_lock.cpp | ||
| 2 | index 0ecf9ec..3dbd19b 100644 | ||
| 3 | --- i/src/wayland/session_lock.cpp | ||
| 4 | +++ w/src/wayland/session_lock.cpp | ||
| 5 | @@ -127,6 +127,7 @@ void WlSessionLock::realizeLockTarget(WlSessionLock* old) { | ||
| 6 | this->updateSurfaces(false); | ||
| 7 | |||
| 8 | if (!this->manager->lock()) this->lockTarget = false; | ||
| 9 | + emit this->lockStateChanged(); | ||
| 10 | |||
| 11 | this->updateSurfaces(true, old); | ||
| 12 | } else { | ||
diff --git a/overlays/quickshell/pipewire.patch b/overlays/quickshell/pipewire.patch new file mode 100644 index 00000000..2d98eefc --- /dev/null +++ b/overlays/quickshell/pipewire.patch | |||
| @@ -0,0 +1,488 @@ | |||
| 1 | diff --git i/src/services/pipewire/device.cpp w/src/services/pipewire/device.cpp | ||
| 2 | index 616e7d0..0c55008 100644 | ||
| 3 | --- i/src/services/pipewire/device.cpp | ||
| 4 | +++ w/src/services/pipewire/device.cpp | ||
| 5 | @@ -3,6 +3,7 @@ | ||
| 6 | #include <cstdint> | ||
| 7 | #include <functional> | ||
| 8 | #include <utility> | ||
| 9 | +#include <algorithm> | ||
| 10 | |||
| 11 | #include <pipewire/device.h> | ||
| 12 | #include <qcontainerfwd.h> | ||
| 13 | @@ -19,6 +20,8 @@ | ||
| 14 | #include <spa/pod/pod.h> | ||
| 15 | #include <spa/pod/vararg.h> | ||
| 16 | #include <spa/utils/type.h> | ||
| 17 | +#include <spa/monitor/device.h> | ||
| 18 | +#include <spa/utils/keys.h> | ||
| 19 | |||
| 20 | #include "../../core/logcat.hpp" | ||
| 21 | #include "core.hpp" | ||
| 22 | @@ -46,6 +49,25 @@ void PwDevice::unbindHooks() { | ||
| 23 | this->mWaitingForDevice = false; | ||
| 24 | } | ||
| 25 | |||
| 26 | +void PwDevice::initProps(const spa_dict* props) { | ||
| 27 | + if (const auto* deviceName = spa_dict_lookup(props, SPA_KEY_DEVICE_NAME)) { | ||
| 28 | + this->name = deviceName; | ||
| 29 | + } | ||
| 30 | + | ||
| 31 | + if (const auto* deviceDesc = spa_dict_lookup(props, SPA_KEY_DEVICE_DESCRIPTION)) { | ||
| 32 | + this->description = deviceDesc; | ||
| 33 | + } | ||
| 34 | + | ||
| 35 | + if (const auto* deviceNick = spa_dict_lookup(props, SPA_KEY_DEVICE_NICK)) { | ||
| 36 | + this->nick = deviceNick; | ||
| 37 | + } | ||
| 38 | + | ||
| 39 | + if (const auto* mediaClass = spa_dict_lookup(props, SPA_KEY_MEDIA_CLASS)) { | ||
| 40 | + this->type = mediaClass; | ||
| 41 | + } | ||
| 42 | +} | ||
| 43 | + | ||
| 44 | + | ||
| 45 | const pw_device_events PwDevice::EVENTS = { | ||
| 46 | .version = PW_VERSION_DEVICE_EVENTS, | ||
| 47 | .info = &PwDevice::onInfo, | ||
| 48 | @@ -71,6 +93,11 @@ void PwDevice::onInfo(void* data, const pw_device_info* info) { | ||
| 49 | } | ||
| 50 | |||
| 51 | break; | ||
| 52 | + } else if (param.id == SPA_PARAM_EnumProfile && param.flags & SPA_PARAM_INFO_READ) { | ||
| 53 | + self->validProfiles.clear(); | ||
| 54 | + pw_device_enum_params(self->proxy(), 0, param.id, 0, UINT32_MAX, nullptr); | ||
| 55 | + } else if (param.id == SPA_PARAM_Profile && param.flags & SPA_PARAM_INFO_READ) { | ||
| 56 | + pw_device_enum_params(self->proxy(), 0, param.id, 0, UINT32_MAX, nullptr); | ||
| 57 | } | ||
| 58 | } | ||
| 59 | } | ||
| 60 | @@ -97,6 +124,15 @@ void PwDevice::onParam( | ||
| 61 | } | ||
| 62 | |||
| 63 | self->addDeviceIndexPairs(param); | ||
| 64 | + } else if (id == SPA_PARAM_EnumProfile) { | ||
| 65 | + PwProfile profile = PwProfile::parseSpaPod(param); | ||
| 66 | + self->profilesUpdated = true; | ||
| 67 | + self->profiles.insertOrAssign(profile.index, profile); | ||
| 68 | + self->validProfiles.insert(profile.index); | ||
| 69 | + } else if (id == SPA_PARAM_Profile) { | ||
| 70 | + PwProfile profile = PwProfile::parseSpaPod(param); | ||
| 71 | + self->currentProfileUpdated = true; | ||
| 72 | + self->currentProfile = profile; | ||
| 73 | } | ||
| 74 | } | ||
| 75 | |||
| 76 | @@ -145,6 +181,21 @@ void PwDevice::polled() { | ||
| 77 | return false; | ||
| 78 | }); | ||
| 79 | } | ||
| 80 | + if (this->profilesUpdated) { | ||
| 81 | + this->profiles.removeIf([&](const std::pair<qint32, PwProfile>& entry) { | ||
| 82 | + return !this->validProfiles.contains(entry.first); | ||
| 83 | + }); | ||
| 84 | + this->profilesUpdated = false; | ||
| 85 | + QList<PwProfile> profiles = this->profiles.values(); | ||
| 86 | + std::sort(profiles.begin(), profiles.end(), [](const PwProfile& a, const PwProfile& b) { return a.index < b.index; }); | ||
| 87 | + emit this->profilesChanged(profiles); | ||
| 88 | + } | ||
| 89 | + if (this->currentProfileUpdated) { | ||
| 90 | + this->currentProfileUpdated = false; | ||
| 91 | + if (this->currentProfile) { | ||
| 92 | + emit this->currentProfileChanged(*this->currentProfile); | ||
| 93 | + } | ||
| 94 | + } | ||
| 95 | } | ||
| 96 | |||
| 97 | bool PwDevice::setVolumes(qint32 routeDevice, const QVector<float>& volumes) { | ||
| 98 | @@ -182,6 +233,15 @@ bool PwDevice::setMuted(qint32 routeDevice, bool muted) { | ||
| 99 | }); | ||
| 100 | } | ||
| 101 | |||
| 102 | +void PwDevice::setProfile(qint32 profileIndex) { | ||
| 103 | + auto buffer = std::array<uint8_t, 1024>(); | ||
| 104 | + auto builder = SPA_POD_BUILDER_INIT(buffer.data(), buffer.size()); | ||
| 105 | + auto* pod = spa_pod_builder_add_object(&builder, | ||
| 106 | + SPA_TYPE_OBJECT_ParamProfile, SPA_PARAM_Profile, | ||
| 107 | + SPA_PARAM_PROFILE_index, SPA_POD_Int(profileIndex)); | ||
| 108 | + pw_device_set_param(this->proxy(), SPA_PARAM_Profile, 0, static_cast<spa_pod*>(pod)); | ||
| 109 | +} | ||
| 110 | + | ||
| 111 | void PwDevice::waitForDevice() { this->mWaitingForDevice = true; } | ||
| 112 | bool PwDevice::waitingForDevice() const { return this->mWaitingForDevice; } | ||
| 113 | |||
| 114 | @@ -222,4 +282,24 @@ bool PwDevice::setRouteProps( | ||
| 115 | return true; | ||
| 116 | } | ||
| 117 | |||
| 118 | +PwProfile PwProfile::parseSpaPod(const spa_pod* param) { | ||
| 119 | + PwProfile profile; | ||
| 120 | + | ||
| 121 | + const auto* indexProp = spa_pod_find_prop(param, nullptr, SPA_PARAM_PROFILE_index); | ||
| 122 | + const auto* descProp = spa_pod_find_prop(param, nullptr, SPA_PARAM_PROFILE_description); | ||
| 123 | + const auto* nameProp = spa_pod_find_prop(param, nullptr, SPA_PARAM_PROFILE_name); | ||
| 124 | + | ||
| 125 | + spa_pod_get_int(&indexProp->value, &profile.index); | ||
| 126 | + | ||
| 127 | + const char* desc_cstr = nullptr; | ||
| 128 | + spa_pod_get_string(&descProp->value, &desc_cstr); | ||
| 129 | + profile.description = QString(desc_cstr); | ||
| 130 | + | ||
| 131 | + const char* name_cstr = nullptr; | ||
| 132 | + spa_pod_get_string(&nameProp->value, &name_cstr); | ||
| 133 | + profile.name = QString(name_cstr); | ||
| 134 | + | ||
| 135 | + return profile; | ||
| 136 | +} | ||
| 137 | + | ||
| 138 | } // namespace qs::service::pipewire | ||
| 139 | diff --git i/src/services/pipewire/device.hpp w/src/services/pipewire/device.hpp | ||
| 140 | index 1a1f705..ee64858 100644 | ||
| 141 | --- i/src/services/pipewire/device.hpp | ||
| 142 | +++ w/src/services/pipewire/device.hpp | ||
| 143 | @@ -1,6 +1,7 @@ | ||
| 144 | #pragma once | ||
| 145 | |||
| 146 | #include <functional> | ||
| 147 | +#include <optional> | ||
| 148 | |||
| 149 | #include <pipewire/core.h> | ||
| 150 | #include <pipewire/device.h> | ||
| 151 | @@ -17,6 +18,20 @@ | ||
| 152 | |||
| 153 | namespace qs::service::pipewire { | ||
| 154 | |||
| 155 | +struct PwProfile { | ||
| 156 | + Q_GADGET; | ||
| 157 | + Q_PROPERTY(qint32 index MEMBER index) | ||
| 158 | + Q_PROPERTY(QString description MEMBER description) | ||
| 159 | + Q_PROPERTY(QString name MEMBER name) | ||
| 160 | + | ||
| 161 | +public: | ||
| 162 | + qint32 index; | ||
| 163 | + QString description; | ||
| 164 | + QString name; | ||
| 165 | + | ||
| 166 | + static PwProfile parseSpaPod(const spa_pod* param); | ||
| 167 | +}; | ||
| 168 | + | ||
| 169 | class PwDevice; | ||
| 170 | |||
| 171 | class PwDevice: public PwBindable<pw_device, PW_TYPE_INTERFACE_Device, PW_VERSION_DEVICE> { | ||
| 172 | @@ -25,6 +40,12 @@ class PwDevice: public PwBindable<pw_device, PW_TYPE_INTERFACE_Device, PW_VERSIO | ||
| 173 | public: | ||
| 174 | void bindHooks() override; | ||
| 175 | void unbindHooks() override; | ||
| 176 | + void initProps(const spa_dict* props) override; | ||
| 177 | + | ||
| 178 | + QString name; | ||
| 179 | + QString description; | ||
| 180 | + QString nick; | ||
| 181 | + QString type; | ||
| 182 | |||
| 183 | bool setVolumes(qint32 routeDevice, const QVector<float>& volumes); | ||
| 184 | bool setMuted(qint32 routeDevice, bool muted); | ||
| 185 | @@ -32,9 +53,16 @@ public: | ||
| 186 | void waitForDevice(); | ||
| 187 | [[nodiscard]] bool waitingForDevice() const; | ||
| 188 | |||
| 189 | + void setProfile(qint32 profileIndex); | ||
| 190 | + | ||
| 191 | + QHash<qint32, PwProfile> profiles; | ||
| 192 | + std::optional<PwProfile> currentProfile; | ||
| 193 | + | ||
| 194 | signals: | ||
| 195 | void deviceReady(); | ||
| 196 | void routeVolumesChanged(qint32 routeDevice, const PwVolumeProps& volumeProps); | ||
| 197 | + void profilesChanged(QList<PwProfile> profiles); | ||
| 198 | + void currentProfileChanged(PwProfile profile); | ||
| 199 | |||
| 200 | private slots: | ||
| 201 | void polled(); | ||
| 202 | @@ -49,6 +77,11 @@ private: | ||
| 203 | QList<qint32> stagingIndexes; | ||
| 204 | void addDeviceIndexPairs(const spa_pod* param); | ||
| 205 | |||
| 206 | + bool profilesUpdated = false; | ||
| 207 | + QSet<qint32> validProfiles; | ||
| 208 | + | ||
| 209 | + bool currentProfileUpdated = false; | ||
| 210 | + | ||
| 211 | bool | ||
| 212 | setRouteProps(qint32 routeDevice, const std::function<void*(spa_pod_builder*)>& propsCallback); | ||
| 213 | |||
| 214 | diff --git i/src/services/pipewire/node.cpp w/src/services/pipewire/node.cpp | ||
| 215 | index 3e68149..4721a58 100644 | ||
| 216 | --- i/src/services/pipewire/node.cpp | ||
| 217 | +++ w/src/services/pipewire/node.cpp | ||
| 218 | @@ -145,6 +145,10 @@ void PwNode::initProps(const spa_dict* props) { | ||
| 219 | this->type = PwNodeType::VideoSink; | ||
| 220 | } else if (strcmp(mediaClass, "Video/Source") == 0) { | ||
| 221 | this->type = PwNodeType::VideoSource; | ||
| 222 | + } else if (strcmp(mediaClass, "Stream/Output/Video") == 0) { | ||
| 223 | + this->type = PwNodeType::VideoOutStream; | ||
| 224 | + } else if (strcmp(mediaClass, "Stream/Input/Video") == 0) { | ||
| 225 | + this->type = PwNodeType::VideoInStream; | ||
| 226 | } | ||
| 227 | } | ||
| 228 | |||
| 229 | diff --git i/src/services/pipewire/node.hpp w/src/services/pipewire/node.hpp | ||
| 230 | index 0d4c92e..ee6f223 100644 | ||
| 231 | --- i/src/services/pipewire/node.hpp | ||
| 232 | +++ w/src/services/pipewire/node.hpp | ||
| 233 | @@ -144,6 +144,8 @@ public: | ||
| 234 | // This is equivalent to the media class `Video/Sink` and is composed of the | ||
| 235 | // @@PwNodeType.Video and @@PwNodeType.Sink flags. | ||
| 236 | VideoSink = Video | Sink, | ||
| 237 | + VideoOutStream = Video | Sink | Stream, | ||
| 238 | + VideoInStream = Video | Source | Stream, | ||
| 239 | }; | ||
| 240 | Q_ENUM(Flag); | ||
| 241 | Q_DECLARE_FLAGS(Flags, Flag); | ||
| 242 | diff --git i/src/services/pipewire/qml.cpp w/src/services/pipewire/qml.cpp | ||
| 243 | index 9efb17e..921d12a 100644 | ||
| 244 | --- i/src/services/pipewire/qml.cpp | ||
| 245 | +++ w/src/services/pipewire/qml.cpp | ||
| 246 | @@ -9,6 +9,9 @@ | ||
| 247 | #include <qtypes.h> | ||
| 248 | #include <qvariant.h> | ||
| 249 | |||
| 250 | +#include <cstdint> | ||
| 251 | +#include <algorithm> | ||
| 252 | + | ||
| 253 | #include "../../core/model.hpp" | ||
| 254 | #include "connection.hpp" | ||
| 255 | #include "defaults.hpp" | ||
| 256 | @@ -54,6 +57,12 @@ Pipewire::Pipewire(QObject* parent): QObject(parent) { | ||
| 257 | |||
| 258 | QObject::connect(&connection->registry, &PwRegistry::nodeAdded, this, &Pipewire::onNodeAdded); | ||
| 259 | |||
| 260 | + for (auto* device: connection->registry.devices.values()) { | ||
| 261 | + this->onDeviceAdded(device); | ||
| 262 | + } | ||
| 263 | + | ||
| 264 | + QObject::connect(&connection->registry, &PwRegistry::deviceAdded, this, &Pipewire::onDeviceAdded); | ||
| 265 | + | ||
| 266 | for (auto* link: connection->registry.links.values()) { | ||
| 267 | this->onLinkAdded(link); | ||
| 268 | } | ||
| 269 | @@ -123,6 +132,19 @@ void Pipewire::onNodeRemoved(QObject* object) { | ||
| 270 | this->mNodes.removeObject(iface); | ||
| 271 | } | ||
| 272 | |||
| 273 | +ObjectModel<PwDeviceIface>* Pipewire::devices() { return &this->mDevices; } | ||
| 274 | + | ||
| 275 | +void Pipewire::onDeviceAdded(PwDevice* device) { | ||
| 276 | + auto* iface = PwDeviceIface::instance(device); | ||
| 277 | + QObject::connect(iface, &QObject::destroyed, this, &Pipewire::onDeviceRemoved); | ||
| 278 | + this->mDevices.insertObject(iface); | ||
| 279 | +} | ||
| 280 | + | ||
| 281 | +void Pipewire::onDeviceRemoved(QObject* object) { | ||
| 282 | + auto* iface = static_cast<PwDeviceIface*>(object); // NOLINT | ||
| 283 | + this->mDevices.removeObject(iface); | ||
| 284 | +} | ||
| 285 | + | ||
| 286 | ObjectModel<PwLinkIface>* Pipewire::links() { return &this->mLinks; } | ||
| 287 | |||
| 288 | void Pipewire::onLinkAdded(PwLink* link) { | ||
| 289 | @@ -357,6 +379,8 @@ QVariantMap PwNodeIface::properties() const { | ||
| 290 | |||
| 291 | PwNodeAudioIface* PwNodeIface::audio() const { return this->audioIface; } | ||
| 292 | |||
| 293 | +PwDeviceIface* PwNodeIface::device() const { return PwDeviceIface::instance(this->mNode->device); } | ||
| 294 | + | ||
| 295 | PwNodeIface* PwNodeIface::instance(PwNode* node) { | ||
| 296 | if (node == nullptr) return nullptr; | ||
| 297 | |||
| 298 | @@ -481,4 +505,42 @@ void PwObjectTracker::objectDestroyed(QObject* object) { | ||
| 299 | emit this->objectsChanged(); | ||
| 300 | } | ||
| 301 | |||
| 302 | +PwDeviceIface::PwDeviceIface(PwDevice* device): PwObjectIface(device), mDevice(device) { | ||
| 303 | + QObject::connect(device, &PwDevice::profilesChanged, this, &PwDeviceIface::deviceProfilesChanged); | ||
| 304 | + QObject::connect(device, &PwDevice::currentProfileChanged, this, &PwDeviceIface::deviceCurrentProfileChanged); | ||
| 305 | +} | ||
| 306 | + | ||
| 307 | +void PwDeviceIface::deviceProfilesChanged(QList<PwProfile>) { emit this->profilesChanged(); } | ||
| 308 | +void PwDeviceIface::deviceCurrentProfileChanged(PwProfile) { emit this->currentProfileChanged(); } | ||
| 309 | + | ||
| 310 | +quint32 PwDeviceIface::id() const { return this->mDevice->id; } | ||
| 311 | +QString PwDeviceIface::name() const { return this->mDevice->name; } | ||
| 312 | +QString PwDeviceIface::description() const { return this->mDevice->description; } | ||
| 313 | +QString PwDeviceIface::nickname() const { return this->mDevice->nick; } | ||
| 314 | +QString PwDeviceIface::type() const { return this->mDevice->type; } | ||
| 315 | +QList<PwProfile> PwDeviceIface::profiles() const { | ||
| 316 | + QList<PwProfile> profiles = this->mDevice->profiles.values(); | ||
| 317 | + std::sort(profiles.begin(), profiles.end(), [](const PwProfile& a, const PwProfile& b) { return a.index < b.index; }); | ||
| 318 | + return profiles; | ||
| 319 | +} | ||
| 320 | +qint32 PwDeviceIface::currentProfile() const { return this->mDevice->currentProfile->index; } | ||
| 321 | + | ||
| 322 | +PwDeviceIface* PwDeviceIface::instance(PwDevice* device) { | ||
| 323 | + if (device == nullptr) return nullptr; | ||
| 324 | + | ||
| 325 | + auto v = device->property("iface"); | ||
| 326 | + if (v.canConvert<PwDeviceIface*>()) { | ||
| 327 | + return v.value<PwDeviceIface*>(); | ||
| 328 | + } | ||
| 329 | + | ||
| 330 | + auto* instance = new PwDeviceIface(device); | ||
| 331 | + device->setProperty("iface", QVariant::fromValue(instance)); | ||
| 332 | + | ||
| 333 | + return instance; | ||
| 334 | +} | ||
| 335 | + | ||
| 336 | +void PwDeviceIface::setProfile(qint32 profileIndex) { | ||
| 337 | + this->mDevice->setProfile(profileIndex); | ||
| 338 | +} | ||
| 339 | + | ||
| 340 | } // namespace qs::service::pipewire | ||
| 341 | diff --git i/src/services/pipewire/qml.hpp w/src/services/pipewire/qml.hpp | ||
| 342 | index e3489a1..e5e1891 100644 | ||
| 343 | --- i/src/services/pipewire/qml.hpp | ||
| 344 | +++ w/src/services/pipewire/qml.hpp | ||
| 345 | @@ -12,11 +12,13 @@ | ||
| 346 | #include "../../core/model.hpp" | ||
| 347 | #include "link.hpp" | ||
| 348 | #include "node.hpp" | ||
| 349 | +#include "device.hpp" | ||
| 350 | #include "registry.hpp" | ||
| 351 | |||
| 352 | namespace qs::service::pipewire { | ||
| 353 | |||
| 354 | class PwNodeIface; | ||
| 355 | +class PwDeviceIface; | ||
| 356 | class PwLinkIface; | ||
| 357 | class PwLinkGroupIface; | ||
| 358 | |||
| 359 | @@ -65,6 +67,8 @@ class Pipewire: public QObject { | ||
| 360 | /// - @@PwNode.audio - if non null the node is an audio node. | ||
| 361 | QSDOC_TYPE_OVERRIDE(ObjectModel<qs::service::pipewire::PwNodeIface>*); | ||
| 362 | Q_PROPERTY(UntypedObjectModel* nodes READ nodes CONSTANT); | ||
| 363 | + QSDOC_TYPE_OVERRIDE(ObjectModel<qs::service::pipewire::PwDeviceIface>*); | ||
| 364 | + Q_PROPERTY(UntypedObjectModel* devices READ devices CONSTANT); | ||
| 365 | /// All links present in pipewire. | ||
| 366 | /// | ||
| 367 | /// Links connect pipewire nodes to each other, and can be used to determine | ||
| 368 | @@ -134,6 +138,7 @@ public: | ||
| 369 | explicit Pipewire(QObject* parent = nullptr); | ||
| 370 | |||
| 371 | [[nodiscard]] ObjectModel<PwNodeIface>* nodes(); | ||
| 372 | + [[nodiscard]] ObjectModel<PwDeviceIface>* devices(); | ||
| 373 | [[nodiscard]] ObjectModel<PwLinkIface>* links(); | ||
| 374 | [[nodiscard]] ObjectModel<PwLinkGroupIface>* linkGroups(); | ||
| 375 | |||
| 376 | @@ -159,7 +164,9 @@ signals: | ||
| 377 | |||
| 378 | private slots: | ||
| 379 | void onNodeAdded(PwNode* node); | ||
| 380 | + void onDeviceAdded(PwDevice* node); | ||
| 381 | void onNodeRemoved(QObject* object); | ||
| 382 | + void onDeviceRemoved(QObject* object); | ||
| 383 | void onLinkAdded(PwLink* link); | ||
| 384 | void onLinkRemoved(QObject* object); | ||
| 385 | void onLinkGroupAdded(PwLinkGroup* group); | ||
| 386 | @@ -167,6 +174,7 @@ private slots: | ||
| 387 | |||
| 388 | private: | ||
| 389 | ObjectModel<PwNodeIface> mNodes {this}; | ||
| 390 | + ObjectModel<PwDeviceIface> mDevices {this}; | ||
| 391 | ObjectModel<PwLinkIface> mLinks {this}; | ||
| 392 | ObjectModel<PwLinkGroupIface> mLinkGroups {this}; | ||
| 393 | }; | ||
| 394 | @@ -315,6 +323,7 @@ class PwNodeIface: public PwObjectIface { | ||
| 395 | /// > [!NOTE] The node may be used before it is fully bound, but some data | ||
| 396 | /// > may be missing or incorrect. | ||
| 397 | Q_PROPERTY(bool ready READ isReady NOTIFY readyChanged); | ||
| 398 | + Q_PROPERTY(qs::service::pipewire::PwDeviceIface* device READ device CONSTANT); | ||
| 399 | QML_NAMED_ELEMENT(PwNode); | ||
| 400 | QML_UNCREATABLE("PwNodes cannot be created directly"); | ||
| 401 | |||
| 402 | @@ -332,6 +341,7 @@ public: | ||
| 403 | [[nodiscard]] PwNodeType::Flags type() const; | ||
| 404 | [[nodiscard]] QVariantMap properties() const; | ||
| 405 | [[nodiscard]] PwNodeAudioIface* audio() const; | ||
| 406 | + [[nodiscard]] PwDeviceIface* device() const; | ||
| 407 | |||
| 408 | static PwNodeIface* instance(PwNode* node); | ||
| 409 | |||
| 410 | @@ -344,6 +354,44 @@ private: | ||
| 411 | PwNodeAudioIface* audioIface = nullptr; | ||
| 412 | }; | ||
| 413 | |||
| 414 | +class PwDeviceIface: public PwObjectIface { | ||
| 415 | + Q_OBJECT; | ||
| 416 | + Q_PROPERTY(quint32 id READ id CONSTANT); | ||
| 417 | + Q_PROPERTY(QString name READ name CONSTANT); | ||
| 418 | + Q_PROPERTY(QString description READ description CONSTANT); | ||
| 419 | + Q_PROPERTY(QString nickname READ nickname CONSTANT); | ||
| 420 | + Q_PROPERTY(QString type READ type CONSTANT); | ||
| 421 | + Q_PROPERTY(QList<PwProfile> profiles READ profiles NOTIFY profilesChanged); | ||
| 422 | + Q_PROPERTY(qint32 currentProfile READ currentProfile NOTIFY currentProfileChanged); | ||
| 423 | + | ||
| 424 | + QML_NAMED_ELEMENT(PwDevice); | ||
| 425 | + QML_UNCREATABLE("PwDevices cannot be created directly"); | ||
| 426 | + | ||
| 427 | +signals: | ||
| 428 | + void profilesChanged(); | ||
| 429 | + void currentProfileChanged(); | ||
| 430 | + | ||
| 431 | +public: | ||
| 432 | + explicit PwDeviceIface(PwDevice* node); | ||
| 433 | + | ||
| 434 | + [[nodiscard]] quint32 id() const; | ||
| 435 | + [[nodiscard]] QString name() const; | ||
| 436 | + [[nodiscard]] QString description() const; | ||
| 437 | + [[nodiscard]] QString nickname() const; | ||
| 438 | + [[nodiscard]] QString type() const; | ||
| 439 | + QList<PwProfile> profiles() const; | ||
| 440 | + qint32 currentProfile() const; | ||
| 441 | + | ||
| 442 | + Q_INVOKABLE void setProfile(qint32 profileIndex); | ||
| 443 | + | ||
| 444 | + static PwDeviceIface* instance(PwDevice* node); | ||
| 445 | +private: | ||
| 446 | + PwDevice* mDevice; | ||
| 447 | + | ||
| 448 | + void deviceProfilesChanged(QList<PwProfile> profiles); | ||
| 449 | + void deviceCurrentProfileChanged(PwProfile profile); | ||
| 450 | +}; | ||
| 451 | + | ||
| 452 | ///! A connection between pipewire nodes. | ||
| 453 | /// Note that there is one link per *channel* of a connection between nodes. | ||
| 454 | /// You usually want @@PwLinkGroup. | ||
| 455 | diff --git i/src/services/pipewire/registry.cpp w/src/services/pipewire/registry.cpp | ||
| 456 | index c08fc1d..50c6d7a 100644 | ||
| 457 | --- i/src/services/pipewire/registry.cpp | ||
| 458 | +++ w/src/services/pipewire/registry.cpp | ||
| 459 | @@ -196,6 +196,7 @@ void PwRegistry::onGlobal( | ||
| 460 | device->initProps(props); | ||
| 461 | |||
| 462 | self->devices.emplace(id, device); | ||
| 463 | + emit self->deviceAdded(device); | ||
| 464 | } | ||
| 465 | } | ||
| 466 | |||
| 467 | @@ -211,6 +212,9 @@ void PwRegistry::onGlobalRemoved(void* data, quint32 id) { | ||
| 468 | } else if (auto* node = self->nodes.value(id)) { | ||
| 469 | self->nodes.remove(id); | ||
| 470 | node->safeDestroy(); | ||
| 471 | + } else if (auto* device = self->devices.value(id)) { | ||
| 472 | + self->devices.remove(id); | ||
| 473 | + device->safeDestroy(); | ||
| 474 | } | ||
| 475 | } | ||
| 476 | |||
| 477 | diff --git i/src/services/pipewire/registry.hpp w/src/services/pipewire/registry.hpp | ||
| 478 | index 8473f04..87e0766 100644 | ||
| 479 | --- i/src/services/pipewire/registry.hpp | ||
| 480 | +++ w/src/services/pipewire/registry.hpp | ||
| 481 | @@ -132,6 +132,7 @@ public: | ||
| 482 | |||
| 483 | signals: | ||
| 484 | void nodeAdded(PwNode* node); | ||
| 485 | + void deviceAdded(PwDevice* node); | ||
| 486 | void linkAdded(PwLink* link); | ||
| 487 | void linkGroupAdded(PwLinkGroup* group); | ||
| 488 | void metadataAdded(PwMetadata* metadata); | ||
diff --git a/overlays/scutiger.nix b/overlays/scutiger.nix index 7a56adaf..fea15c5f 100644 --- a/overlays/scutiger.nix +++ b/overlays/scutiger.nix | |||
| @@ -2,7 +2,7 @@ | |||
| 2 | scutiger = final.rustPlatform.buildRustPackage { | 2 | scutiger = final.rustPlatform.buildRustPackage { |
| 3 | inherit (sources.scutiger) pname version src; | 3 | inherit (sources.scutiger) pname version src; |
| 4 | 4 | ||
| 5 | cargoHash = "sha256-d+wJ3trrldCVATsudsbglElU6q4LaS6feRocRyHal2k="; | 5 | cargoHash = if prev.lib.versionOlder prev.lib.version "24.05" then "sha256-d+wJ3trrldCVATsudsbglElU6q4LaS6feRocRyHal2k=" else "sha256-FTAEmRuO95ii84uwaALVuImiymnSAQkB2UwZ5yX0WPs="; |
| 6 | 6 | ||
| 7 | nativeBuildInputs = with final; [ pkg-config pcre2.dev zlib.dev git ]; | 7 | nativeBuildInputs = with final; [ pkg-config pcre2.dev zlib.dev git ]; |
| 8 | }; | 8 | }; |
diff --git a/overlays/spice-record.nix b/overlays/spice-record.nix index 06a114da..2d37079b 100644 --- a/overlays/spice-record.nix +++ b/overlays/spice-record.nix | |||
| @@ -8,5 +8,7 @@ | |||
| 8 | wrapProgram $out/bin/spice-record \ | 8 | wrapProgram $out/bin/spice-record \ |
| 9 | --prefix PATH : ${prev.lib.makeBinPath (with prev; [ ffmpeg-full ])} | 9 | --prefix PATH : ${prev.lib.makeBinPath (with prev; [ ffmpeg-full ])} |
| 10 | ''; | 10 | ''; |
| 11 | pyproject = true; | ||
| 12 | build-system = [ prev.python3Packages.setuptools ]; | ||
| 11 | }; | 13 | }; |
| 12 | } | 14 | } |
diff --git a/overlays/waybar-systemd-inhibit/.envrc b/overlays/waybar-systemd-inhibit/.envrc new file mode 100644 index 00000000..2c909235 --- /dev/null +++ b/overlays/waybar-systemd-inhibit/.envrc | |||
| @@ -0,0 +1,4 @@ | |||
| 1 | use flake | ||
| 2 | |||
| 3 | [[ -d ".venv" ]] || ( uv venv && uv sync ) | ||
| 4 | . .venv/bin/activate | ||
diff --git a/overlays/waybar-systemd-inhibit/.gitignore b/overlays/waybar-systemd-inhibit/.gitignore new file mode 100644 index 00000000..4ccfae70 --- /dev/null +++ b/overlays/waybar-systemd-inhibit/.gitignore | |||
| @@ -0,0 +1,2 @@ | |||
| 1 | .venv | ||
| 2 | **/__pycache__ | ||
diff --git a/overlays/waybar-systemd-inhibit/default.nix b/overlays/waybar-systemd-inhibit/default.nix new file mode 100644 index 00000000..ae6b8c75 --- /dev/null +++ b/overlays/waybar-systemd-inhibit/default.nix | |||
| @@ -0,0 +1,20 @@ | |||
| 1 | { prev, final, flake, flakeInputs, ... }: | ||
| 2 | |||
| 3 | let | ||
| 4 | workspace = flakeInputs.uv2nix.lib.workspace.loadWorkspace { workspaceRoot = ./.; }; | ||
| 5 | pythonSet = flake.lib.pythonSet { | ||
| 6 | pkgs = final; | ||
| 7 | python = final.python312; | ||
| 8 | overlay = workspace.mkPyprojectOverlay { | ||
| 9 | sourcePreference = "wheel"; | ||
| 10 | }; | ||
| 11 | }; | ||
| 12 | virtualEnv = pythonSet.mkVirtualEnv "waybar-systemd-inhibit-env" workspace.deps.default; | ||
| 13 | in { | ||
| 14 | waybar-systemd-inhibit = virtualEnv.overrideAttrs (oldAttrs: { | ||
| 15 | meta = (oldAttrs.meta or {}) // { | ||
| 16 | mainProgram = "waybar-systemd-inhibit"; | ||
| 17 | }; | ||
| 18 | nativeBuildInputs = (oldAttrs.nativeBuildInputs or []) ++ [ final.gobject-introspection final.wrapGAppsHook ]; | ||
| 19 | }); | ||
| 20 | } | ||
diff --git a/overlays/waybar-systemd-inhibit/pyproject.toml b/overlays/waybar-systemd-inhibit/pyproject.toml new file mode 100644 index 00000000..6c6240b8 --- /dev/null +++ b/overlays/waybar-systemd-inhibit/pyproject.toml | |||
| @@ -0,0 +1,17 @@ | |||
| 1 | [project] | ||
| 2 | name = "waybar-systemd-inhibit" | ||
| 3 | version = "0.1.0" | ||
| 4 | requires-python = ">=3.12" | ||
| 5 | dependencies = [ | ||
| 6 | "asyncclick>=8.1.8", | ||
| 7 | "asyncio>=3.4.3", | ||
| 8 | "dbus-next>=0.2.3", | ||
| 9 | ] | ||
| 10 | |||
| 11 | [project.scripts] | ||
| 12 | waybar-systemd-inhibit = "waybar_systemd_inhibit.__main__:main" | ||
| 13 | waybar-systemd-inhibit-toggle = "waybar_systemd_inhibit.__main__:toggle" | ||
| 14 | |||
| 15 | [build-system] | ||
| 16 | requires = ["hatchling"] | ||
| 17 | build-backend = "hatchling.build" | ||
diff --git a/overlays/waybar-systemd-inhibit/uv.lock b/overlays/waybar-systemd-inhibit/uv.lock new file mode 100644 index 00000000..4e10d145 --- /dev/null +++ b/overlays/waybar-systemd-inhibit/uv.lock | |||
| @@ -0,0 +1,102 @@ | |||
| 1 | version = 1 | ||
| 2 | revision = 2 | ||
| 3 | requires-python = ">=3.12" | ||
| 4 | |||
| 5 | [[package]] | ||
| 6 | name = "anyio" | ||
| 7 | version = "4.9.0" | ||
| 8 | source = { registry = "https://pypi.org/simple" } | ||
| 9 | dependencies = [ | ||
| 10 | { name = "idna" }, | ||
| 11 | { name = "sniffio" }, | ||
| 12 | { name = "typing-extensions", marker = "python_full_version < '3.13'" }, | ||
| 13 | ] | ||
| 14 | sdist = { url = "https://files.pythonhosted.org/packages/95/7d/4c1bd541d4dffa1b52bd83fb8527089e097a106fc90b467a7313b105f840/anyio-4.9.0.tar.gz", hash = "sha256:673c0c244e15788651a4ff38710fea9675823028a6f08a5eda409e0c9840a028", size = 190949, upload-time = "2025-03-17T00:02:54.77Z" } | ||
| 15 | wheels = [ | ||
| 16 | { url = "https://files.pythonhosted.org/packages/a1/ee/48ca1a7c89ffec8b6a0c5d02b89c305671d5ffd8d3c94acf8b8c408575bb/anyio-4.9.0-py3-none-any.whl", hash = "sha256:9f76d541cad6e36af7beb62e978876f3b41e3e04f2c1fbf0884604c0a9c4d93c", size = 100916, upload-time = "2025-03-17T00:02:52.713Z" }, | ||
| 17 | ] | ||
| 18 | |||
| 19 | [[package]] | ||
| 20 | name = "asyncclick" | ||
| 21 | version = "8.1.8" | ||
| 22 | source = { registry = "https://pypi.org/simple" } | ||
| 23 | dependencies = [ | ||
| 24 | { name = "anyio" }, | ||
| 25 | { name = "colorama", marker = "sys_platform == 'win32'" }, | ||
| 26 | ] | ||
| 27 | sdist = { url = "https://files.pythonhosted.org/packages/cb/b5/e1e5fdf1c1bb7e6e614987c120a98d9324bf8edfaa5f5cd16a6235c9d91b/asyncclick-8.1.8.tar.gz", hash = "sha256:0f0eb0f280e04919d67cf71b9fcdfb4db2d9ff7203669c40284485c149578e4c", size = 232900, upload-time = "2025-01-06T09:46:52.694Z" } | ||
| 28 | wheels = [ | ||
| 29 | { url = "https://files.pythonhosted.org/packages/14/cc/a436f0fc2d04e57a0697e0f87a03b9eaed03ad043d2d5f887f8eebcec95f/asyncclick-8.1.8-py3-none-any.whl", hash = "sha256:eb1ccb44bc767f8f0695d592c7806fdf5bd575605b4ee246ffd5fadbcfdbd7c6", size = 99093, upload-time = "2025-01-06T09:46:51.046Z" }, | ||
| 30 | { url = "https://files.pythonhosted.org/packages/92/c4/ae9e9d25522c6dc96ff167903880a0fe94d7bd31ed999198ee5017d977ed/asyncclick-8.1.8.0-py3-none-any.whl", hash = "sha256:be146a2d8075d4fe372ff4e877f23c8b5af269d16705c1948123b9415f6fd678", size = 99115, upload-time = "2025-01-06T09:50:52.72Z" }, | ||
| 31 | ] | ||
| 32 | |||
| 33 | [[package]] | ||
| 34 | name = "asyncio" | ||
| 35 | version = "3.4.3" | ||
| 36 | source = { registry = "https://pypi.org/simple" } | ||
| 37 | sdist = { url = "https://files.pythonhosted.org/packages/da/54/054bafaf2c0fb8473d423743e191fcdf49b2c1fd5e9af3524efbe097bafd/asyncio-3.4.3.tar.gz", hash = "sha256:83360ff8bc97980e4ff25c964c7bd3923d333d177aa4f7fb736b019f26c7cb41", size = 204411, upload-time = "2015-03-10T14:11:26.494Z" } | ||
| 38 | wheels = [ | ||
| 39 | { url = "https://files.pythonhosted.org/packages/22/74/07679c5b9f98a7cb0fc147b1ef1cc1853bc07a4eb9cb5731e24732c5f773/asyncio-3.4.3-py3-none-any.whl", hash = "sha256:c4d18b22701821de07bd6aea8b53d21449ec0ec5680645e5317062ea21817d2d", size = 101767, upload-time = "2015-03-10T14:05:10.959Z" }, | ||
| 40 | ] | ||
| 41 | |||
| 42 | [[package]] | ||
| 43 | name = "colorama" | ||
| 44 | version = "0.4.6" | ||
| 45 | source = { registry = "https://pypi.org/simple" } | ||
| 46 | sdist = { url = "https://files.pythonhosted.org/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", size = 27697, upload-time = "2022-10-25T02:36:22.414Z" } | ||
| 47 | wheels = [ | ||
| 48 | { url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335, upload-time = "2022-10-25T02:36:20.889Z" }, | ||
| 49 | ] | ||
| 50 | |||
| 51 | [[package]] | ||
| 52 | name = "dbus-next" | ||
| 53 | version = "0.2.3" | ||
| 54 | source = { registry = "https://pypi.org/simple" } | ||
| 55 | sdist = { url = "https://files.pythonhosted.org/packages/ce/45/6a40fbe886d60a8c26f480e7d12535502b5ba123814b3b9a0b002ebca198/dbus_next-0.2.3.tar.gz", hash = "sha256:f4eae26909332ada528c0a3549dda8d4f088f9b365153952a408e28023a626a5", size = 71112, upload-time = "2021-07-25T22:11:28.398Z" } | ||
| 56 | wheels = [ | ||
| 57 | { url = "https://files.pythonhosted.org/packages/d2/fc/c0a3f4c4eaa5a22fbef91713474666e13d0ea2a69c84532579490a9f2cc8/dbus_next-0.2.3-py3-none-any.whl", hash = "sha256:58948f9aff9db08316734c0be2a120f6dc502124d9642f55e90ac82ffb16a18b", size = 57885, upload-time = "2021-07-25T22:11:25.466Z" }, | ||
| 58 | ] | ||
| 59 | |||
| 60 | [[package]] | ||
| 61 | name = "idna" | ||
| 62 | version = "3.10" | ||
| 63 | source = { registry = "https://pypi.org/simple" } | ||
| 64 | sdist = { url = "https://files.pythonhosted.org/packages/f1/70/7703c29685631f5a7590aa73f1f1d3fa9a380e654b86af429e0934a32f7d/idna-3.10.tar.gz", hash = "sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9", size = 190490, upload-time = "2024-09-15T18:07:39.745Z" } | ||
| 65 | wheels = [ | ||
| 66 | { url = "https://files.pythonhosted.org/packages/76/c6/c88e154df9c4e1a2a66ccf0005a88dfb2650c1dffb6f5ce603dfbd452ce3/idna-3.10-py3-none-any.whl", hash = "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3", size = 70442, upload-time = "2024-09-15T18:07:37.964Z" }, | ||
| 67 | ] | ||
| 68 | |||
| 69 | [[package]] | ||
| 70 | name = "sniffio" | ||
| 71 | version = "1.3.1" | ||
| 72 | source = { registry = "https://pypi.org/simple" } | ||
| 73 | sdist = { url = "https://files.pythonhosted.org/packages/a2/87/a6771e1546d97e7e041b6ae58d80074f81b7d5121207425c964ddf5cfdbd/sniffio-1.3.1.tar.gz", hash = "sha256:f4324edc670a0f49750a81b895f35c3adb843cca46f0530f79fc1babb23789dc", size = 20372, upload-time = "2024-02-25T23:20:04.057Z" } | ||
| 74 | wheels = [ | ||
| 75 | { url = "https://files.pythonhosted.org/packages/e9/44/75a9c9421471a6c4805dbf2356f7c181a29c1879239abab1ea2cc8f38b40/sniffio-1.3.1-py3-none-any.whl", hash = "sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2", size = 10235, upload-time = "2024-02-25T23:20:01.196Z" }, | ||
| 76 | ] | ||
| 77 | |||
| 78 | [[package]] | ||
| 79 | name = "typing-extensions" | ||
| 80 | version = "4.13.2" | ||
| 81 | source = { registry = "https://pypi.org/simple" } | ||
| 82 | sdist = { url = "https://files.pythonhosted.org/packages/f6/37/23083fcd6e35492953e8d2aaaa68b860eb422b34627b13f2ce3eb6106061/typing_extensions-4.13.2.tar.gz", hash = "sha256:e6c81219bd689f51865d9e372991c540bda33a0379d5573cddb9a3a23f7caaef", size = 106967, upload-time = "2025-04-10T14:19:05.416Z" } | ||
| 83 | wheels = [ | ||
| 84 | { url = "https://files.pythonhosted.org/packages/8b/54/b1ae86c0973cc6f0210b53d508ca3641fb6d0c56823f288d108bc7ab3cc8/typing_extensions-4.13.2-py3-none-any.whl", hash = "sha256:a439e7c04b49fec3e5d3e2beaa21755cadbbdc391694e28ccdd36ca4a1408f8c", size = 45806, upload-time = "2025-04-10T14:19:03.967Z" }, | ||
| 85 | ] | ||
| 86 | |||
| 87 | [[package]] | ||
| 88 | name = "waybar-systemd-inhibit" | ||
| 89 | version = "0.1.0" | ||
| 90 | source = { editable = "." } | ||
| 91 | dependencies = [ | ||
| 92 | { name = "asyncclick" }, | ||
| 93 | { name = "asyncio" }, | ||
| 94 | { name = "dbus-next" }, | ||
| 95 | ] | ||
| 96 | |||
| 97 | [package.metadata] | ||
| 98 | requires-dist = [ | ||
| 99 | { name = "asyncclick", specifier = ">=8.1.8" }, | ||
| 100 | { name = "asyncio", specifier = ">=3.4.3" }, | ||
| 101 | { name = "dbus-next", specifier = ">=0.2.3" }, | ||
| 102 | ] | ||
diff --git a/overlays/waybar-systemd-inhibit/waybar_systemd_inhibit/__init__.py b/overlays/waybar-systemd-inhibit/waybar_systemd_inhibit/__init__.py new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/overlays/waybar-systemd-inhibit/waybar_systemd_inhibit/__init__.py | |||
diff --git a/overlays/waybar-systemd-inhibit/waybar_systemd_inhibit/__main__.py b/overlays/waybar-systemd-inhibit/waybar_systemd_inhibit/__main__.py new file mode 100644 index 00000000..35cc7fd1 --- /dev/null +++ b/overlays/waybar-systemd-inhibit/waybar_systemd_inhibit/__main__.py | |||
| @@ -0,0 +1,117 @@ | |||
| 1 | import asyncclick as click | ||
| 2 | from dbus_next.aio import MessageBus | ||
| 3 | from dbus_next import BusType, Message, PropertyAccess | ||
| 4 | import asyncio | ||
| 5 | from functools import update_wrapper | ||
| 6 | from dbus_next.service import ServiceInterface, method, dbus_property | ||
| 7 | from dbus_next import Variant, DBusError | ||
| 8 | import os | ||
| 9 | import json | ||
| 10 | |||
| 11 | class BlockInterface(ServiceInterface): | ||
| 12 | def __init__(self, system_bus, logind): | ||
| 13 | super().__init__('li.yggdrasil.WaybarSystemdInhibit') | ||
| 14 | self.system_bus = system_bus | ||
| 15 | self.logind = logind | ||
| 16 | self.fd = None | ||
| 17 | |||
| 18 | def Release(self): | ||
| 19 | if not self.fd: | ||
| 20 | return | ||
| 21 | |||
| 22 | os.close(self.fd) | ||
| 23 | self.fd = None | ||
| 24 | self.emit_properties_changed({'IsAcquired': False}) | ||
| 25 | |||
| 26 | async def Acquire(self): | ||
| 27 | if self.fd: | ||
| 28 | return | ||
| 29 | |||
| 30 | res = await self.system_bus.call(Message( | ||
| 31 | destination='org.freedesktop.login1', | ||
| 32 | path='/org/freedesktop/login1', | ||
| 33 | interface='org.freedesktop.login1.Manager', | ||
| 34 | member='Inhibit', | ||
| 35 | signature='ssss', | ||
| 36 | body=[ | ||
| 37 | "handle-lid-switch", | ||
| 38 | "waybar-systemd-inhibit", | ||
| 39 | "User request", | ||
| 40 | "block", | ||
| 41 | ], | ||
| 42 | )) | ||
| 43 | self.fd = res.unix_fds[res.body[0]] | ||
| 44 | self.emit_properties_changed({'IsAcquired': True}) | ||
| 45 | |||
| 46 | @method() | ||
| 47 | async def ToggleBlock(self): | ||
| 48 | if self.fd: | ||
| 49 | self.Release() | ||
| 50 | else: | ||
| 51 | await self.Acquire() | ||
| 52 | |||
| 53 | @dbus_property(access=PropertyAccess.READ) | ||
| 54 | def IsAcquired(self) -> 'b': | ||
| 55 | return self.fd is not None | ||
| 56 | |||
| 57 | |||
| 58 | @click.command() | ||
| 59 | async def main(): | ||
| 60 | system_bus = await MessageBus(bus_type=BusType.SYSTEM, negotiate_unix_fd=True).connect() | ||
| 61 | session_bus = await MessageBus(bus_type=BusType.SESSION).connect() | ||
| 62 | |||
| 63 | introspection = await system_bus.introspect('org.freedesktop.login1', '/org/freedesktop/login1') | ||
| 64 | obj = system_bus.get_proxy_object('org.freedesktop.login1', '/org/freedesktop/login1', introspection) | ||
| 65 | logind = obj.get_interface('org.freedesktop.login1.Manager') | ||
| 66 | properties = obj.get_interface('org.freedesktop.DBus.Properties') | ||
| 67 | |||
| 68 | def is_blocked_logind(what: str): | ||
| 69 | return "handle-lid-switch" in what.split(':') | ||
| 70 | |||
| 71 | def print_state(is_blocked: bool, is_acquired: bool = False): | ||
| 72 | icon = "󰌢" if is_blocked else "󰛧" | ||
| 73 | text = f"<span font=\"Symbols Nerd Font Mono\">{icon}</span>" | ||
| 74 | if is_acquired: | ||
| 75 | text = f"<span color=\"#f28a21\">{text}</span>" | ||
| 76 | elif is_blocked: | ||
| 77 | text = f"<span color=\"#ffffff\">{text}</span>" | ||
| 78 | print(json.dumps({'text': text, 'tooltip': ("Manually inhibited" if is_acquired else None)}, separators=(',', ':')), flush=True) | ||
| 79 | |||
| 80 | print_state(is_blocked_logind(await logind.get_block_inhibited())) | ||
| 81 | |||
| 82 | async def get_inhibit(): | ||
| 83 | introspection = await session_bus.introspect('li.yggdrasil.WaybarSystemdInhibit', '/li/yggdrasil/WaybarSystemdInhibit') | ||
| 84 | return session_bus.get_proxy_object('li.yggdrasil.WaybarSystemdInhibit', '/li/yggdrasil/WaybarSystemdInhibit', introspection) | ||
| 85 | |||
| 86 | async def on_logind_properties_changed(interface_name, changed_properties, invalidated_properties): | ||
| 87 | if 'BlockInhibited' not in changed_properties: | ||
| 88 | return | ||
| 89 | |||
| 90 | properties = (await get_inhibit()).get_interface('li.yggdrasil.WaybarSystemdInhibit') | ||
| 91 | |||
| 92 | print_state(is_blocked_logind(changed_properties['BlockInhibited'].value), await properties.get_is_acquired()) | ||
| 93 | |||
| 94 | properties.on_properties_changed(on_logind_properties_changed) | ||
| 95 | |||
| 96 | session_bus.export('/li/yggdrasil/WaybarSystemdInhibit', BlockInterface(system_bus, logind)) | ||
| 97 | await session_bus.request_name('li.yggdrasil.WaybarSystemdInhibit') | ||
| 98 | |||
| 99 | properties = (await get_inhibit()).get_interface('org.freedesktop.DBus.Properties') | ||
| 100 | |||
| 101 | async def on_inhibit_properties_changed(interface_name, changed_properties, invalidated_properties): | ||
| 102 | if 'IsAcquired' not in changed_properties: | ||
| 103 | return | ||
| 104 | |||
| 105 | print_state(is_blocked_logind(await logind.get_block_inhibited()), changed_properties['IsAcquired'].value) | ||
| 106 | |||
| 107 | properties.on_properties_changed(on_inhibit_properties_changed) | ||
| 108 | |||
| 109 | await session_bus.wait_for_disconnect() | ||
| 110 | |||
| 111 | @click.command() | ||
| 112 | async def toggle(): | ||
| 113 | session_bus = await MessageBus(bus_type=BusType.SESSION).connect() | ||
| 114 | introspection = await session_bus.introspect('li.yggdrasil.WaybarSystemdInhibit', '/li/yggdrasil/WaybarSystemdInhibit') | ||
| 115 | obj = session_bus.get_proxy_object('li.yggdrasil.WaybarSystemdInhibit', '/li/yggdrasil/WaybarSystemdInhibit', introspection) | ||
| 116 | interface = obj.get_interface('li.yggdrasil.WaybarSystemdInhibit') | ||
| 117 | await interface.call_toggle_block() | ||
diff --git a/overlays/waybar.nix b/overlays/waybar.nix index 20f37255..e7e3b807 100644 --- a/overlays/waybar.nix +++ b/overlays/waybar.nix | |||
| @@ -1,3 +1,8 @@ | |||
| 1 | { final, prev, flakeInputs, ... }: | 1 | { final, prev, flakeInputs, ... }: prev.lib.composeExtensions |
| 2 | 2 | flakeInputs.waybar.overlays.default | |
| 3 | flakeInputs.waybar.overlays.default final prev | 3 | (final: prev: { |
| 4 | waybar = prev.waybar.overrideAttrs (oldAttrs: { | ||
| 5 | dontVersionCheck = true; | ||
| 6 | }); | ||
| 7 | }) | ||
| 8 | final prev | ||
diff --git a/overlays/worktime/.envrc b/overlays/worktime/.envrc new file mode 100644 index 00000000..2c909235 --- /dev/null +++ b/overlays/worktime/.envrc | |||
| @@ -0,0 +1,4 @@ | |||
| 1 | use flake | ||
| 2 | |||
| 3 | [[ -d ".venv" ]] || ( uv venv && uv sync ) | ||
| 4 | . .venv/bin/activate | ||
diff --git a/overlays/worktime/.gitignore b/overlays/worktime/.gitignore new file mode 100644 index 00000000..4ccfae70 --- /dev/null +++ b/overlays/worktime/.gitignore | |||
| @@ -0,0 +1,2 @@ | |||
| 1 | .venv | ||
| 2 | **/__pycache__ | ||
diff --git a/overlays/worktime/default.nix b/overlays/worktime/default.nix index 5ecdf149..579cf7ad 100644 --- a/overlays/worktime/default.nix +++ b/overlays/worktime/default.nix | |||
| @@ -1,13 +1,19 @@ | |||
| 1 | { prev, ... }: | 1 | { prev, final, flake, flakeInputs, ... }: |
| 2 | 2 | ||
| 3 | with prev.poetry2nix; | 3 | let |
| 4 | 4 | workspace = flakeInputs.uv2nix.lib.workspace.loadWorkspace { workspaceRoot = ./.; }; | |
| 5 | { | 5 | pythonSet = flake.lib.pythonSet { |
| 6 | worktime = mkPoetryApplication { | 6 | pkgs = final; |
| 7 | python = prev.python310; | 7 | python = final.python312; |
| 8 | 8 | overlay = workspace.mkPyprojectOverlay { | |
| 9 | projectDir = cleanPythonSources { src = ./.; }; | 9 | sourcePreference = "wheel"; |
| 10 | 10 | }; | |
| 11 | meta.mainProgram = "worktime"; | ||
| 12 | }; | 11 | }; |
| 12 | virtualEnv = pythonSet.mkVirtualEnv "worktime" workspace.deps.default; | ||
| 13 | in { | ||
| 14 | worktime = virtualEnv.overrideAttrs (oldAttrs: { | ||
| 15 | meta = (oldAttrs.meta or {}) // { | ||
| 16 | mainProgram = "worktime"; | ||
| 17 | }; | ||
| 18 | }); | ||
| 13 | } | 19 | } |
diff --git a/overlays/worktime/poetry.lock b/overlays/worktime/poetry.lock deleted file mode 100644 index 54182b09..00000000 --- a/overlays/worktime/poetry.lock +++ /dev/null | |||
| @@ -1,248 +0,0 @@ | |||
| 1 | # This file is automatically @generated by Poetry 1.6.1 and should not be changed by hand. | ||
| 2 | |||
| 3 | [[package]] | ||
| 4 | name = "backoff" | ||
| 5 | version = "2.2.1" | ||
| 6 | description = "Function decoration for backoff and retry" | ||
| 7 | optional = false | ||
| 8 | python-versions = ">=3.7,<4.0" | ||
| 9 | files = [ | ||
| 10 | {file = "backoff-2.2.1-py3-none-any.whl", hash = "sha256:63579f9a0628e06278f7e47b7d7d5b6ce20dc65c5e96a6f3ca99a6adca0396e8"}, | ||
| 11 | {file = "backoff-2.2.1.tar.gz", hash = "sha256:03f829f5bb1923180821643f8753b0502c3b682293992485b0eef2807afa5cba"}, | ||
| 12 | ] | ||
| 13 | |||
| 14 | [[package]] | ||
| 15 | name = "certifi" | ||
| 16 | version = "2022.12.7" | ||
| 17 | description = "Python package for providing Mozilla's CA Bundle." | ||
| 18 | optional = false | ||
| 19 | python-versions = ">=3.6" | ||
| 20 | files = [ | ||
| 21 | {file = "certifi-2022.12.7-py3-none-any.whl", hash = "sha256:4ad3232f5e926d6718ec31cfc1fcadfde020920e278684144551c91769c7bc18"}, | ||
| 22 | {file = "certifi-2022.12.7.tar.gz", hash = "sha256:35824b4c3a97115964b408844d64aa14db1cc518f6562e8d7261699d1350a9e3"}, | ||
| 23 | ] | ||
| 24 | |||
| 25 | [[package]] | ||
| 26 | name = "charset-normalizer" | ||
| 27 | version = "3.1.0" | ||
| 28 | description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." | ||
| 29 | optional = false | ||
| 30 | python-versions = ">=3.7.0" | ||
| 31 | files = [ | ||
| 32 | {file = "charset-normalizer-3.1.0.tar.gz", hash = "sha256:34e0a2f9c370eb95597aae63bf85eb5e96826d81e3dcf88b8886012906f509b5"}, | ||
| 33 | {file = "charset_normalizer-3.1.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:e0ac8959c929593fee38da1c2b64ee9778733cdf03c482c9ff1d508b6b593b2b"}, | ||
| 34 | {file = "charset_normalizer-3.1.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d7fc3fca01da18fbabe4625d64bb612b533533ed10045a2ac3dd194bfa656b60"}, | ||
| 35 | {file = "charset_normalizer-3.1.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:04eefcee095f58eaabe6dc3cc2262f3bcd776d2c67005880894f447b3f2cb9c1"}, | ||
| 36 | {file = "charset_normalizer-3.1.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:20064ead0717cf9a73a6d1e779b23d149b53daf971169289ed2ed43a71e8d3b0"}, | ||
| 37 | {file = "charset_normalizer-3.1.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1435ae15108b1cb6fffbcea2af3d468683b7afed0169ad718451f8db5d1aff6f"}, | ||
| 38 | {file = "charset_normalizer-3.1.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c84132a54c750fda57729d1e2599bb598f5fa0344085dbde5003ba429a4798c0"}, | ||
| 39 | {file = "charset_normalizer-3.1.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:75f2568b4189dda1c567339b48cba4ac7384accb9c2a7ed655cd86b04055c795"}, | ||
| 40 | {file = "charset_normalizer-3.1.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:11d3bcb7be35e7b1bba2c23beedac81ee893ac9871d0ba79effc7fc01167db6c"}, | ||
| 41 | {file = "charset_normalizer-3.1.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:891cf9b48776b5c61c700b55a598621fdb7b1e301a550365571e9624f270c203"}, | ||
| 42 | {file = "charset_normalizer-3.1.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:5f008525e02908b20e04707a4f704cd286d94718f48bb33edddc7d7b584dddc1"}, | ||
| 43 | {file = "charset_normalizer-3.1.0-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:b06f0d3bf045158d2fb8837c5785fe9ff9b8c93358be64461a1089f5da983137"}, | ||
| 44 | {file = "charset_normalizer-3.1.0-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:49919f8400b5e49e961f320c735388ee686a62327e773fa5b3ce6721f7e785ce"}, | ||
| 45 | {file = "charset_normalizer-3.1.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:22908891a380d50738e1f978667536f6c6b526a2064156203d418f4856d6e86a"}, | ||
| 46 | {file = "charset_normalizer-3.1.0-cp310-cp310-win32.whl", hash = "sha256:12d1a39aa6b8c6f6248bb54550efcc1c38ce0d8096a146638fd4738e42284448"}, | ||
| 47 | {file = "charset_normalizer-3.1.0-cp310-cp310-win_amd64.whl", hash = "sha256:65ed923f84a6844de5fd29726b888e58c62820e0769b76565480e1fdc3d062f8"}, | ||
| 48 | {file = "charset_normalizer-3.1.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:9a3267620866c9d17b959a84dd0bd2d45719b817245e49371ead79ed4f710d19"}, | ||
| 49 | {file = "charset_normalizer-3.1.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:6734e606355834f13445b6adc38b53c0fd45f1a56a9ba06c2058f86893ae8017"}, | ||
| 50 | {file = "charset_normalizer-3.1.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f8303414c7b03f794347ad062c0516cee0e15f7a612abd0ce1e25caf6ceb47df"}, | ||
| 51 | {file = "charset_normalizer-3.1.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:aaf53a6cebad0eae578f062c7d462155eada9c172bd8c4d250b8c1d8eb7f916a"}, | ||
| 52 | {file = "charset_normalizer-3.1.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3dc5b6a8ecfdc5748a7e429782598e4f17ef378e3e272eeb1340ea57c9109f41"}, | ||
| 53 | {file = "charset_normalizer-3.1.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e1b25e3ad6c909f398df8921780d6a3d120d8c09466720226fc621605b6f92b1"}, | ||
| 54 | {file = "charset_normalizer-3.1.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0ca564606d2caafb0abe6d1b5311c2649e8071eb241b2d64e75a0d0065107e62"}, | ||
| 55 | {file = "charset_normalizer-3.1.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b82fab78e0b1329e183a65260581de4375f619167478dddab510c6c6fb04d9b6"}, | ||
| 56 | {file = "charset_normalizer-3.1.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:bd7163182133c0c7701b25e604cf1611c0d87712e56e88e7ee5d72deab3e76b5"}, | ||
| 57 | {file = "charset_normalizer-3.1.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:11d117e6c63e8f495412d37e7dc2e2fff09c34b2d09dbe2bee3c6229577818be"}, | ||
| 58 | {file = "charset_normalizer-3.1.0-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:cf6511efa4801b9b38dc5546d7547d5b5c6ef4b081c60b23e4d941d0eba9cbeb"}, | ||
| 59 | {file = "charset_normalizer-3.1.0-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:abc1185d79f47c0a7aaf7e2412a0eb2c03b724581139193d2d82b3ad8cbb00ac"}, | ||
| 60 | {file = "charset_normalizer-3.1.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:cb7b2ab0188829593b9de646545175547a70d9a6e2b63bf2cd87a0a391599324"}, | ||
| 61 | {file = "charset_normalizer-3.1.0-cp311-cp311-win32.whl", hash = "sha256:c36bcbc0d5174a80d6cccf43a0ecaca44e81d25be4b7f90f0ed7bcfbb5a00909"}, | ||
| 62 | {file = "charset_normalizer-3.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:cca4def576f47a09a943666b8f829606bcb17e2bc2d5911a46c8f8da45f56755"}, | ||
| 63 | {file = "charset_normalizer-3.1.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:0c95f12b74681e9ae127728f7e5409cbbef9cd914d5896ef238cc779b8152373"}, | ||
| 64 | {file = "charset_normalizer-3.1.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fca62a8301b605b954ad2e9c3666f9d97f63872aa4efcae5492baca2056b74ab"}, | ||
| 65 | {file = "charset_normalizer-3.1.0-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ac0aa6cd53ab9a31d397f8303f92c42f534693528fafbdb997c82bae6e477ad9"}, | ||
| 66 | {file = "charset_normalizer-3.1.0-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c3af8e0f07399d3176b179f2e2634c3ce9c1301379a6b8c9c9aeecd481da494f"}, | ||
| 67 | {file = "charset_normalizer-3.1.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3a5fc78f9e3f501a1614a98f7c54d3969f3ad9bba8ba3d9b438c3bc5d047dd28"}, | ||
| 68 | {file = "charset_normalizer-3.1.0-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:628c985afb2c7d27a4800bfb609e03985aaecb42f955049957814e0491d4006d"}, | ||
| 69 | {file = "charset_normalizer-3.1.0-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:74db0052d985cf37fa111828d0dd230776ac99c740e1a758ad99094be4f1803d"}, | ||
| 70 | {file = "charset_normalizer-3.1.0-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:1e8fcdd8f672a1c4fc8d0bd3a2b576b152d2a349782d1eb0f6b8e52e9954731d"}, | ||
| 71 | {file = "charset_normalizer-3.1.0-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:04afa6387e2b282cf78ff3dbce20f0cc071c12dc8f685bd40960cc68644cfea6"}, | ||
| 72 | {file = "charset_normalizer-3.1.0-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:dd5653e67b149503c68c4018bf07e42eeed6b4e956b24c00ccdf93ac79cdff84"}, | ||
| 73 | {file = "charset_normalizer-3.1.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:d2686f91611f9e17f4548dbf050e75b079bbc2a82be565832bc8ea9047b61c8c"}, | ||
| 74 | {file = "charset_normalizer-3.1.0-cp37-cp37m-win32.whl", hash = "sha256:4155b51ae05ed47199dc5b2a4e62abccb274cee6b01da5b895099b61b1982974"}, | ||
| 75 | {file = "charset_normalizer-3.1.0-cp37-cp37m-win_amd64.whl", hash = "sha256:322102cdf1ab682ecc7d9b1c5eed4ec59657a65e1c146a0da342b78f4112db23"}, | ||
| 76 | {file = "charset_normalizer-3.1.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:e633940f28c1e913615fd624fcdd72fdba807bf53ea6925d6a588e84e1151531"}, | ||
| 77 | {file = "charset_normalizer-3.1.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:3a06f32c9634a8705f4ca9946d667609f52cf130d5548881401f1eb2c39b1e2c"}, | ||
| 78 | {file = "charset_normalizer-3.1.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:7381c66e0561c5757ffe616af869b916c8b4e42b367ab29fedc98481d1e74e14"}, | ||
| 79 | {file = "charset_normalizer-3.1.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3573d376454d956553c356df45bb824262c397c6e26ce43e8203c4c540ee0acb"}, | ||
| 80 | {file = "charset_normalizer-3.1.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e89df2958e5159b811af9ff0f92614dabf4ff617c03a4c1c6ff53bf1c399e0e1"}, | ||
| 81 | {file = "charset_normalizer-3.1.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:78cacd03e79d009d95635e7d6ff12c21eb89b894c354bd2b2ed0b4763373693b"}, | ||
| 82 | {file = "charset_normalizer-3.1.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:de5695a6f1d8340b12a5d6d4484290ee74d61e467c39ff03b39e30df62cf83a0"}, | ||
| 83 | {file = "charset_normalizer-3.1.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1c60b9c202d00052183c9be85e5eaf18a4ada0a47d188a83c8f5c5b23252f649"}, | ||
| 84 | {file = "charset_normalizer-3.1.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:f645caaf0008bacf349875a974220f1f1da349c5dbe7c4ec93048cdc785a3326"}, | ||
| 85 | {file = "charset_normalizer-3.1.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:ea9f9c6034ea2d93d9147818f17c2a0860d41b71c38b9ce4d55f21b6f9165a11"}, | ||
| 86 | {file = "charset_normalizer-3.1.0-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:80d1543d58bd3d6c271b66abf454d437a438dff01c3e62fdbcd68f2a11310d4b"}, | ||
| 87 | {file = "charset_normalizer-3.1.0-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:73dc03a6a7e30b7edc5b01b601e53e7fc924b04e1835e8e407c12c037e81adbd"}, | ||
| 88 | {file = "charset_normalizer-3.1.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:6f5c2e7bc8a4bf7c426599765b1bd33217ec84023033672c1e9a8b35eaeaaaf8"}, | ||
| 89 | {file = "charset_normalizer-3.1.0-cp38-cp38-win32.whl", hash = "sha256:12a2b561af122e3d94cdb97fe6fb2bb2b82cef0cdca131646fdb940a1eda04f0"}, | ||
| 90 | {file = "charset_normalizer-3.1.0-cp38-cp38-win_amd64.whl", hash = "sha256:3160a0fd9754aab7d47f95a6b63ab355388d890163eb03b2d2b87ab0a30cfa59"}, | ||
| 91 | {file = "charset_normalizer-3.1.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:38e812a197bf8e71a59fe55b757a84c1f946d0ac114acafaafaf21667a7e169e"}, | ||
| 92 | {file = "charset_normalizer-3.1.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:6baf0baf0d5d265fa7944feb9f7451cc316bfe30e8df1a61b1bb08577c554f31"}, | ||
| 93 | {file = "charset_normalizer-3.1.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:8f25e17ab3039b05f762b0a55ae0b3632b2e073d9c8fc88e89aca31a6198e88f"}, | ||
| 94 | {file = "charset_normalizer-3.1.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3747443b6a904001473370d7810aa19c3a180ccd52a7157aacc264a5ac79265e"}, | ||
| 95 | {file = "charset_normalizer-3.1.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b116502087ce8a6b7a5f1814568ccbd0e9f6cfd99948aa59b0e241dc57cf739f"}, | ||
| 96 | {file = "charset_normalizer-3.1.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d16fd5252f883eb074ca55cb622bc0bee49b979ae4e8639fff6ca3ff44f9f854"}, | ||
| 97 | {file = "charset_normalizer-3.1.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:21fa558996782fc226b529fdd2ed7866c2c6ec91cee82735c98a197fae39f706"}, | ||
| 98 | {file = "charset_normalizer-3.1.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6f6c7a8a57e9405cad7485f4c9d3172ae486cfef1344b5ddd8e5239582d7355e"}, | ||
| 99 | {file = "charset_normalizer-3.1.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:ac3775e3311661d4adace3697a52ac0bab17edd166087d493b52d4f4f553f9f0"}, | ||
| 100 | {file = "charset_normalizer-3.1.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:10c93628d7497c81686e8e5e557aafa78f230cd9e77dd0c40032ef90c18f2230"}, | ||
| 101 | {file = "charset_normalizer-3.1.0-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:6f4f4668e1831850ebcc2fd0b1cd11721947b6dc7c00bf1c6bd3c929ae14f2c7"}, | ||
| 102 | {file = "charset_normalizer-3.1.0-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:0be65ccf618c1e7ac9b849c315cc2e8a8751d9cfdaa43027d4f6624bd587ab7e"}, | ||
| 103 | {file = "charset_normalizer-3.1.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:53d0a3fa5f8af98a1e261de6a3943ca631c526635eb5817a87a59d9a57ebf48f"}, | ||
| 104 | {file = "charset_normalizer-3.1.0-cp39-cp39-win32.whl", hash = "sha256:a04f86f41a8916fe45ac5024ec477f41f886b3c435da2d4e3d2709b22ab02af1"}, | ||
| 105 | {file = "charset_normalizer-3.1.0-cp39-cp39-win_amd64.whl", hash = "sha256:830d2948a5ec37c386d3170c483063798d7879037492540f10a475e3fd6f244b"}, | ||
| 106 | {file = "charset_normalizer-3.1.0-py3-none-any.whl", hash = "sha256:3d9098b479e78c85080c98e1e35ff40b4a31d8953102bb0fd7d1b6f8a2111a3d"}, | ||
| 107 | ] | ||
| 108 | |||
| 109 | [[package]] | ||
| 110 | name = "idna" | ||
| 111 | version = "3.4" | ||
| 112 | description = "Internationalized Domain Names in Applications (IDNA)" | ||
| 113 | optional = false | ||
| 114 | python-versions = ">=3.5" | ||
| 115 | files = [ | ||
| 116 | {file = "idna-3.4-py3-none-any.whl", hash = "sha256:90b77e79eaa3eba6de819a0c442c0b4ceefc341a7a2ab77d7562bf49f425c5c2"}, | ||
| 117 | {file = "idna-3.4.tar.gz", hash = "sha256:814f528e8dead7d329833b91c5faa87d60bf71824cd12a7530b5526063d02cb4"}, | ||
| 118 | ] | ||
| 119 | |||
| 120 | [[package]] | ||
| 121 | name = "jsonpickle" | ||
| 122 | version = "3.0.2" | ||
| 123 | description = "Python library for serializing any arbitrary object graph into JSON" | ||
| 124 | optional = false | ||
| 125 | python-versions = ">=3.7" | ||
| 126 | files = [ | ||
| 127 | {file = "jsonpickle-3.0.2-py3-none-any.whl", hash = "sha256:4a8442d97ca3f77978afa58068768dba7bff2dbabe79a9647bc3cdafd4ef019f"}, | ||
| 128 | {file = "jsonpickle-3.0.2.tar.gz", hash = "sha256:e37abba4bfb3ca4a4647d28bb9f4706436f7b46c8a8333b4a718abafa8e46b37"}, | ||
| 129 | ] | ||
| 130 | |||
| 131 | [package.extras] | ||
| 132 | docs = ["jaraco.packaging (>=3.2)", "rst.linker (>=1.9)", "sphinx"] | ||
| 133 | testing = ["ecdsa", "feedparser", "gmpy2", "numpy", "pandas", "pymongo", "pytest (>=3.5,!=3.7.3)", "pytest-black-multipy", "pytest-checkdocs (>=1.2.3)", "pytest-cov", "pytest-flake8 (>=1.1.1)", "scikit-learn", "sqlalchemy"] | ||
| 134 | testing-libs = ["simplejson", "ujson"] | ||
| 135 | |||
| 136 | [[package]] | ||
| 137 | name = "python-dateutil" | ||
| 138 | version = "2.8.2" | ||
| 139 | description = "Extensions to the standard Python datetime module" | ||
| 140 | optional = false | ||
| 141 | python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" | ||
| 142 | files = [ | ||
| 143 | {file = "python-dateutil-2.8.2.tar.gz", hash = "sha256:0123cacc1627ae19ddf3c27a5de5bd67ee4586fbdd6440d9748f8abb483d3e86"}, | ||
| 144 | {file = "python_dateutil-2.8.2-py2.py3-none-any.whl", hash = "sha256:961d03dc3453ebbc59dbdea9e4e11c5651520a876d0f4db161e8674aae935da9"}, | ||
| 145 | ] | ||
| 146 | |||
| 147 | [package.dependencies] | ||
| 148 | six = ">=1.5" | ||
| 149 | |||
| 150 | [[package]] | ||
| 151 | name = "pyxdg" | ||
| 152 | version = "0.28" | ||
| 153 | description = "PyXDG contains implementations of freedesktop.org standards in python." | ||
| 154 | optional = false | ||
| 155 | python-versions = "*" | ||
| 156 | files = [ | ||
| 157 | {file = "pyxdg-0.28-py2.py3-none-any.whl", hash = "sha256:bdaf595999a0178ecea4052b7f4195569c1ff4d344567bccdc12dfdf02d545ab"}, | ||
| 158 | {file = "pyxdg-0.28.tar.gz", hash = "sha256:3267bb3074e934df202af2ee0868575484108581e6f3cb006af1da35395e88b4"}, | ||
| 159 | ] | ||
| 160 | |||
| 161 | [[package]] | ||
| 162 | name = "requests" | ||
| 163 | version = "2.28.2" | ||
| 164 | description = "Python HTTP for Humans." | ||
| 165 | optional = false | ||
| 166 | python-versions = ">=3.7, <4" | ||
| 167 | files = [ | ||
| 168 | {file = "requests-2.28.2-py3-none-any.whl", hash = "sha256:64299f4909223da747622c030b781c0d7811e359c37124b4bd368fb8c6518baa"}, | ||
| 169 | {file = "requests-2.28.2.tar.gz", hash = "sha256:98b1b2782e3c6c4904938b84c0eb932721069dfdb9134313beff7c83c2df24bf"}, | ||
| 170 | ] | ||
| 171 | |||
| 172 | [package.dependencies] | ||
| 173 | certifi = ">=2017.4.17" | ||
| 174 | charset-normalizer = ">=2,<4" | ||
| 175 | idna = ">=2.5,<4" | ||
| 176 | urllib3 = ">=1.21.1,<1.27" | ||
| 177 | |||
| 178 | [package.extras] | ||
| 179 | socks = ["PySocks (>=1.5.6,!=1.5.7)"] | ||
| 180 | use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"] | ||
| 181 | |||
| 182 | [[package]] | ||
| 183 | name = "six" | ||
| 184 | version = "1.16.0" | ||
| 185 | description = "Python 2 and 3 compatibility utilities" | ||
| 186 | optional = false | ||
| 187 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*" | ||
| 188 | files = [ | ||
| 189 | {file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"}, | ||
| 190 | {file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"}, | ||
| 191 | ] | ||
| 192 | |||
| 193 | [[package]] | ||
| 194 | name = "tabulate" | ||
| 195 | version = "0.9.0" | ||
| 196 | description = "Pretty-print tabular data" | ||
| 197 | optional = false | ||
| 198 | python-versions = ">=3.7" | ||
| 199 | files = [ | ||
| 200 | {file = "tabulate-0.9.0-py3-none-any.whl", hash = "sha256:024ca478df22e9340661486f85298cff5f6dcdba14f3813e8830015b9ed1948f"}, | ||
| 201 | {file = "tabulate-0.9.0.tar.gz", hash = "sha256:0095b12bf5966de529c0feb1fa08671671b3368eec77d7ef7ab114be2c068b3c"}, | ||
| 202 | ] | ||
| 203 | |||
| 204 | [package.extras] | ||
| 205 | widechars = ["wcwidth"] | ||
| 206 | |||
| 207 | [[package]] | ||
| 208 | name = "toml" | ||
| 209 | version = "0.10.2" | ||
| 210 | description = "Python Library for Tom's Obvious, Minimal Language" | ||
| 211 | optional = false | ||
| 212 | python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*" | ||
| 213 | files = [ | ||
| 214 | {file = "toml-0.10.2-py2.py3-none-any.whl", hash = "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b"}, | ||
| 215 | {file = "toml-0.10.2.tar.gz", hash = "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f"}, | ||
| 216 | ] | ||
| 217 | |||
| 218 | [[package]] | ||
| 219 | name = "uritools" | ||
| 220 | version = "4.0.1" | ||
| 221 | description = "URI parsing, classification and composition" | ||
| 222 | optional = false | ||
| 223 | python-versions = "~=3.7" | ||
| 224 | files = [ | ||
| 225 | {file = "uritools-4.0.1-py3-none-any.whl", hash = "sha256:d122d394ed6e6e15ac0fddba6a5b19e9fa204e7797507815cbfb0e1455ac0475"}, | ||
| 226 | {file = "uritools-4.0.1.tar.gz", hash = "sha256:efc5c3a6de05404850685a8d3f34da8476b56aa3516fbf8eff5c8704c7a2826f"}, | ||
| 227 | ] | ||
| 228 | |||
| 229 | [[package]] | ||
| 230 | name = "urllib3" | ||
| 231 | version = "1.26.15" | ||
| 232 | description = "HTTP library with thread-safe connection pooling, file post, and more." | ||
| 233 | optional = false | ||
| 234 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*" | ||
| 235 | files = [ | ||
| 236 | {file = "urllib3-1.26.15-py2.py3-none-any.whl", hash = "sha256:aa751d169e23c7479ce47a0cb0da579e3ede798f994f5816a74e4f4500dcea42"}, | ||
| 237 | {file = "urllib3-1.26.15.tar.gz", hash = "sha256:8a388717b9476f934a21484e8c8e61875ab60644d29b9b39e11e4b9dc1c6b305"}, | ||
| 238 | ] | ||
| 239 | |||
| 240 | [package.extras] | ||
| 241 | brotli = ["brotli (>=1.0.9)", "brotlicffi (>=0.8.0)", "brotlipy (>=0.6.0)"] | ||
| 242 | secure = ["certifi", "cryptography (>=1.3.4)", "idna (>=2.0.0)", "ipaddress", "pyOpenSSL (>=0.14)", "urllib3-secure-extra"] | ||
| 243 | socks = ["PySocks (>=1.5.6,!=1.5.7,<2.0)"] | ||
| 244 | |||
| 245 | [metadata] | ||
| 246 | lock-version = "2.0" | ||
| 247 | python-versions = "^3.10" | ||
| 248 | content-hash = "d9137b4f8e37bba934abf732e4a2aeeb9924c4b6576830d8ae08bdb43b4e147f" | ||
diff --git a/overlays/worktime/pyproject.toml b/overlays/worktime/pyproject.toml index 08002d4d..42da51f5 100644 --- a/overlays/worktime/pyproject.toml +++ b/overlays/worktime/pyproject.toml | |||
| @@ -1,23 +1,28 @@ | |||
| 1 | [tool.poetry] | 1 | [project] |
| 2 | name = "worktime" | 2 | name = "worktime" |
| 3 | version = "0.1.0" | 3 | version = "1.0.0" |
| 4 | description = "" | 4 | requires-python = "~=3.12" |
| 5 | authors = ["Gregor Kleen <gkleen@yggdrasil.li>"] | 5 | dependencies = [ |
| 6 | "pyxdg>=0.28,<0.29", | ||
| 7 | "python-dateutil>=2.9.0.post0,<3", | ||
| 8 | "uritools>=4.0.3,<5", | ||
| 9 | "requests>=2.32.3,<3", | ||
| 10 | "tabulate>=0.9.0,<0.10", | ||
| 11 | "toml>=0.10.2,<0.11", | ||
| 12 | "jsonpickle>=4.0.5,<5", | ||
| 13 | "frozendict>=2.4.6", | ||
| 14 | "atomicwriter>=0.2.5", | ||
| 15 | "desktop-notify>=1.3.3", | ||
| 16 | ] | ||
| 6 | 17 | ||
| 7 | [tool.poetry.dependencies] | 18 | [project.scripts] |
| 8 | python = "^3.10" | ||
| 9 | pyxdg = "^0.28" | ||
| 10 | python-dateutil = "^2.8.2" | ||
| 11 | uritools = "^4.0.1" | ||
| 12 | requests = "^2.28.2" | ||
| 13 | tabulate = "^0.9.0" | ||
| 14 | backoff = "^2.2.1" | ||
| 15 | toml = "^0.10.2" | ||
| 16 | jsonpickle = "^3.0.2" | ||
| 17 | |||
| 18 | [tool.poetry.scripts] | ||
| 19 | worktime = "worktime.__main__:main" | 19 | worktime = "worktime.__main__:main" |
| 20 | worktime-ui = "worktime.__main__:ui" | ||
| 21 | worktime-stop = "worktime.__main__:stop" | ||
| 20 | 22 | ||
| 21 | [build-system] | 23 | [build-system] |
| 22 | requires = ["poetry-core"] | 24 | requires = ["hatchling"] |
| 23 | build-backend = "poetry.core.masonry.api" \ No newline at end of file | 25 | build-backend = "hatchling.build" |
| 26 | |||
| 27 | [dependency-groups] | ||
| 28 | dev = [] | ||
diff --git a/overlays/worktime/uv.lock b/overlays/worktime/uv.lock new file mode 100644 index 00000000..39de4ccf --- /dev/null +++ b/overlays/worktime/uv.lock | |||
| @@ -0,0 +1,248 @@ | |||
| 1 | version = 1 | ||
| 2 | revision = 2 | ||
| 3 | requires-python = ">=3.12, <4" | ||
| 4 | |||
| 5 | [[package]] | ||
| 6 | name = "atomicwriter" | ||
| 7 | version = "0.2.5" | ||
| 8 | source = { registry = "https://pypi.org/simple" } | ||
| 9 | sdist = { url = "https://files.pythonhosted.org/packages/50/b4/dd04e186eb244d1ed84b1d0ebfba19ddc7f8886b98e345aaca4208b031d2/atomicwriter-0.2.5.tar.gz", hash = "sha256:5ced6afb0579377a13e191b17a16115e14c30ec00e6c38b60403f58235a867af", size = 64990, upload-time = "2025-05-24T20:35:42.538Z" } | ||
| 10 | wheels = [ | ||
| 11 | { url = "https://files.pythonhosted.org/packages/99/7c/672a0de09b0b355a2ffa521ef25cf106f1984823379dee37f7305fdc1774/atomicwriter-0.2.5-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:5c1fab874e62ebe96f1af0e965dc1e92c4c1ef2e2e9612a444371b8fc751ec43", size = 234141, upload-time = "2025-05-24T20:34:32.74Z" }, | ||
| 12 | { url = "https://files.pythonhosted.org/packages/b9/0c/e1c5bad033284c212c0a77121b48dd4147f80e9a7cd82a9d2ce0a2160901/atomicwriter-0.2.5-cp312-cp312-macosx_11_0_x86_64.whl", hash = "sha256:8dbb67cc730be7d6bdfd5e991271bc17052be8fb2e4fa27854b47d8a76d36349", size = 245788, upload-time = "2025-05-24T20:34:33.897Z" }, | ||
| 13 | { url = "https://files.pythonhosted.org/packages/f4/d3/7036e203cc5fc4c49bf916b4ba158e0d2779de127afad5963edd7e3b9400/atomicwriter-0.2.5-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:a4e7f81932839c738425dc96ad98e4a7511b740cd3d75f480bfabbcf8e6f7eae", size = 260428, upload-time = "2025-05-24T20:34:35.533Z" }, | ||
| 14 | { url = "https://files.pythonhosted.org/packages/e5/b9/9a4d235a8d67fb442302dc0f3ea2394b7bd994bfc99b1dc0f744c7852418/atomicwriter-0.2.5-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:de37a3a5d1b57b719cfb0b81a11cab2114acfdc2c36051bf0af72d05eb644411", size = 263648, upload-time = "2025-05-24T20:34:36.72Z" }, | ||
| 15 | { url = "https://files.pythonhosted.org/packages/71/7c/32d4ddad53375de42f3e972bb0633ec76f2c31772f2e508479d4788651d9/atomicwriter-0.2.5-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:0b925e55750092fd482565b6068b8c8366fd79de526681af9e58eb209f0deeca", size = 323775, upload-time = "2025-05-24T20:34:37.968Z" }, | ||
| 16 | { url = "https://files.pythonhosted.org/packages/06/fe/6a226368a3f7ea30001fbd165f6a97f28c8f1a884896357b3d694983f5d2/atomicwriter-0.2.5-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:538f78f25e01584535782397211c66b8b3c9de90c2d1fc01a668ddce73dd0cb2", size = 340819, upload-time = "2025-05-24T20:34:39.63Z" }, | ||
| 17 | { url = "https://files.pythonhosted.org/packages/92/95/b035b2296c483fde5392c629e0b6e3844eba6e54ea965c4b8827379b0893/atomicwriter-0.2.5-cp312-cp312-win_amd64.whl", hash = "sha256:1d2d49a1b94ea7b289be9f7134d756bfb0bbf53eb0e58411334ed1b9958abe5e", size = 152789, upload-time = "2025-05-24T20:34:40.905Z" }, | ||
| 18 | { url = "https://files.pythonhosted.org/packages/da/25/caa0959ae8ce24763e24e1f45be6cb897414545d224a155f929d496d6812/atomicwriter-0.2.5-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:3f5490fd5bec378509521f7c2a19a64031a0de07d368d76733c3f76a0b9f026b", size = 233830, upload-time = "2025-05-24T20:34:42.532Z" }, | ||
| 19 | { url = "https://files.pythonhosted.org/packages/d2/76/3c41bfd4fd74bc63bec29f05a806a767258eea7cf151496b4ab015cb5323/atomicwriter-0.2.5-cp313-cp313-macosx_11_0_x86_64.whl", hash = "sha256:a4dada83ff1255c7e640363cc2a4399ab9a822d4dbc9c18f55bbf0c8b12ce056", size = 245461, upload-time = "2025-05-24T20:34:44.454Z" }, | ||
| 20 | { url = "https://files.pythonhosted.org/packages/c3/1e/5512dbdfdc3f4ab12f5923c50ae4765cc2fc65a9f112bb9dccbcbe60b395/atomicwriter-0.2.5-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:ef2cf15e67513f05ad37d4cec48e403982c6b3c07f491472effd76d2157de7e2", size = 259892, upload-time = "2025-05-24T20:34:45.688Z" }, | ||
| 21 | { url = "https://files.pythonhosted.org/packages/e5/1d/2382b6cacb119115828eb519697a555900bcfdb062efeb0f82603295402d/atomicwriter-0.2.5-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:73618f74c3c5f5401d3da0a3cd3043f23de5b6bb4a3d85bc580940a441355d25", size = 263125, upload-time = "2025-05-24T20:34:47.205Z" }, | ||
| 22 | { url = "https://files.pythonhosted.org/packages/07/d7/c4d68386161870db4a8d0452f0655a19902fa435b749c12e6ef800e89b19/atomicwriter-0.2.5-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:bbd5eda80710ddac7aefb421c79cef6b905852a827e764f0f12fcbaa88919f7a", size = 323503, upload-time = "2025-05-24T20:34:48.417Z" }, | ||
| 23 | { url = "https://files.pythonhosted.org/packages/b7/08/0fc03c0736ab8466e1b47a3ee17a528da18019cff93b7c4c2b33df82c19e/atomicwriter-0.2.5-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:b4776aaca40bc3040c3716c2adad74625c42285083ff31e8bf24a95315225c7b", size = 340156, upload-time = "2025-05-24T20:34:50.389Z" }, | ||
| 24 | { url = "https://files.pythonhosted.org/packages/fa/09/7ba888cf4d90bcabd9e82db3bdb9de50e4ef072e0ea0d375cd1931b79349/atomicwriter-0.2.5-cp313-cp313-win_amd64.whl", hash = "sha256:225ed1fbfa1996d9b0b2252f8a5d81263e51cbc797086d830f488c35b1d2ab42", size = 152274, upload-time = "2025-05-24T20:34:51.785Z" }, | ||
| 25 | { url = "https://files.pythonhosted.org/packages/2a/70/07d2ba2e0a126cfecfbfed46baf599c9e2155f4c8338fed4d3ae0041b133/atomicwriter-0.2.5-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:63b55982cfa47232f179689933bf003eefb2bd33464235883ed3ce7322cf38f3", size = 232879, upload-time = "2025-05-24T20:34:53.195Z" }, | ||
| 26 | { url = "https://files.pythonhosted.org/packages/f6/4d/397eb5435917135df93b339d849884bb1125896b1e15163c5244aa590336/atomicwriter-0.2.5-cp313-cp313t-macosx_11_0_x86_64.whl", hash = "sha256:e33f40b2a27f8831beeabb485923acb6dd067cc70bba1a63096749b3dc4747ff", size = 244386, upload-time = "2025-05-24T20:34:54.852Z" }, | ||
| 27 | { url = "https://files.pythonhosted.org/packages/8b/01/73f0b683fa55e61dd29d30e48e9a75ddb049e6dad0ac4ae1a29dbc05f21e/atomicwriter-0.2.5-cp313-cp313t-manylinux_2_28_aarch64.whl", hash = "sha256:c646e115e88147d71f845a005fc53910f22c4dc65bd634768cb90b7f34259359", size = 258255, upload-time = "2025-05-24T20:34:56.046Z" }, | ||
| 28 | { url = "https://files.pythonhosted.org/packages/4b/19/692387c1fb1b8714a9b2fab99a58850fd4136bed988814c8ff74d0c8de02/atomicwriter-0.2.5-cp313-cp313t-manylinux_2_28_x86_64.whl", hash = "sha256:47f974e986ff6514351c3ea75041009a514be0c34c225c062b0ad8a28ec9c0a3", size = 261768, upload-time = "2025-05-24T20:34:57.795Z" }, | ||
| 29 | { url = "https://files.pythonhosted.org/packages/3e/f2/4d466f52ee635cc54011713272f302584c6d1ce612c331d9989fa6fa672f/atomicwriter-0.2.5-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:e1db8b9004cd3f628166e83b25eb814b82345f9d6bc15e99b6d201c355455b45", size = 321975, upload-time = "2025-05-24T20:34:59.45Z" }, | ||
| 30 | { url = "https://files.pythonhosted.org/packages/84/ad/0189ad9783ca6609df47e06cc0cd22866a8073d46478f59c6ab3ec13e0fb/atomicwriter-0.2.5-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:a7da4a114121ab865663578b801a0520b2b518d4591af0bd294f6aac0dad243b", size = 338946, upload-time = "2025-05-24T20:35:01.501Z" }, | ||
| 31 | { url = "https://files.pythonhosted.org/packages/94/79/2c4d8f75eeb09192cf572957f031271998f3c985fabd79d513fff66ac715/atomicwriter-0.2.5-cp313-cp313t-win_amd64.whl", hash = "sha256:7aab4b3956cc17219e7e4da76e8a1bceb3d3aeaf03234f89b90e234a2adcf27b", size = 151571, upload-time = "2025-05-24T20:35:02.747Z" }, | ||
| 32 | { url = "https://files.pythonhosted.org/packages/32/19/d6a686d189c3577e7f08b33df398b959c24bf74b3cec34359104db1a24ff/atomicwriter-0.2.5-cp39-abi3-macosx_11_0_arm64.whl", hash = "sha256:8d0fccac2dfe5d884d97edbda28be9c16d55faee9bdf66f53a99384ac387cc43", size = 239320, upload-time = "2025-05-24T20:35:04.028Z" }, | ||
| 33 | { url = "https://files.pythonhosted.org/packages/8e/35/35571a4eed57816c3b5fdbefcb15f38563fbe4f3a4a7d1588c8ef899afaf/atomicwriter-0.2.5-cp39-abi3-macosx_11_0_x86_64.whl", hash = "sha256:6583c24333508839db2156d895cbbb5cd3ff20d4f9c698e341435e5b35990eaa", size = 250818, upload-time = "2025-05-24T20:35:05.21Z" }, | ||
| 34 | { url = "https://files.pythonhosted.org/packages/81/d9/145093630bc25f115a49d32d9ef66745f5cdef787492d77fd27e74d20389/atomicwriter-0.2.5-cp39-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:136a9902ae3f1c0cb262a07dd3ac85069d71f8b11347cd740030567e67d611aa", size = 265796, upload-time = "2025-05-24T20:35:06.388Z" }, | ||
| 35 | { url = "https://files.pythonhosted.org/packages/58/32/d1881adade2ebc70aa9dbb61cadabc2c00cfa99a7a5d6ba48f44e279056f/atomicwriter-0.2.5-cp39-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:0b6830434b6a49c19473c3f3975dfa0a87dec95bee81297f7393e378f9a0b82f", size = 269378, upload-time = "2025-05-24T20:35:07.578Z" }, | ||
| 36 | { url = "https://files.pythonhosted.org/packages/93/f5/2661ea763784a4991c4c7be5c932a468937bd1d4618b833a63ec638a3b76/atomicwriter-0.2.5-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:53095a01891a2901aa04c10c8de52c0ba41e0d8a4a1893318cf34ccbdbde00b7", size = 328167, upload-time = "2025-05-24T20:35:08.764Z" }, | ||
| 37 | { url = "https://files.pythonhosted.org/packages/ec/bc/e3aa521671a589bee9662d3e2108e4835a5d80e6da76e4d05d98d1c78005/atomicwriter-0.2.5-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:ecf4dc3983bb1f28b21cb09c2d96b6936d8864c559dcf151b57813cb1eae998b", size = 347153, upload-time = "2025-05-24T20:35:10.507Z" }, | ||
| 38 | { url = "https://files.pythonhosted.org/packages/59/b7/e190383e7240b1f247c6df9bc6667db8df10190cd0bb2dba8ea6bd704ea4/atomicwriter-0.2.5-cp39-abi3-win_amd64.whl", hash = "sha256:92cff264a20364301ab341b332fd0112866870b8cb35caf99a3f3fee0e6c19e8", size = 156374, upload-time = "2025-05-24T20:35:11.716Z" }, | ||
| 39 | ] | ||
| 40 | |||
| 41 | [[package]] | ||
| 42 | name = "certifi" | ||
| 43 | version = "2025.1.31" | ||
| 44 | source = { registry = "https://pypi.org/simple" } | ||
| 45 | sdist = { url = "https://files.pythonhosted.org/packages/1c/ab/c9f1e32b7b1bf505bf26f0ef697775960db7932abeb7b516de930ba2705f/certifi-2025.1.31.tar.gz", hash = "sha256:3d5da6925056f6f18f119200434a4780a94263f10d1c21d032a6f6b2baa20651", size = 167577, upload-time = "2025-01-31T02:16:47.166Z" } | ||
| 46 | wheels = [ | ||
| 47 | { url = "https://files.pythonhosted.org/packages/38/fc/bce832fd4fd99766c04d1ee0eead6b0ec6486fb100ae5e74c1d91292b982/certifi-2025.1.31-py3-none-any.whl", hash = "sha256:ca78db4565a652026a4db2bcdf68f2fb589ea80d0be70e03929ed730746b84fe", size = 166393, upload-time = "2025-01-31T02:16:45.015Z" }, | ||
| 48 | ] | ||
| 49 | |||
| 50 | [[package]] | ||
| 51 | name = "charset-normalizer" | ||
| 52 | version = "3.4.1" | ||
| 53 | source = { registry = "https://pypi.org/simple" } | ||
| 54 | sdist = { url = "https://files.pythonhosted.org/packages/16/b0/572805e227f01586461c80e0fd25d65a2115599cc9dad142fee4b747c357/charset_normalizer-3.4.1.tar.gz", hash = "sha256:44251f18cd68a75b56585dd00dae26183e102cd5e0f9f1466e6df5da2ed64ea3", size = 123188, upload-time = "2024-12-24T18:12:35.43Z" } | ||
| 55 | wheels = [ | ||
| 56 | { url = "https://files.pythonhosted.org/packages/0a/9a/dd1e1cdceb841925b7798369a09279bd1cf183cef0f9ddf15a3a6502ee45/charset_normalizer-3.4.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:73d94b58ec7fecbc7366247d3b0b10a21681004153238750bb67bd9012414545", size = 196105, upload-time = "2024-12-24T18:10:38.83Z" }, | ||
| 57 | { url = "https://files.pythonhosted.org/packages/d3/8c/90bfabf8c4809ecb648f39794cf2a84ff2e7d2a6cf159fe68d9a26160467/charset_normalizer-3.4.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dad3e487649f498dd991eeb901125411559b22e8d7ab25d3aeb1af367df5efd7", size = 140404, upload-time = "2024-12-24T18:10:44.272Z" }, | ||
| 58 | { url = "https://files.pythonhosted.org/packages/ad/8f/e410d57c721945ea3b4f1a04b74f70ce8fa800d393d72899f0a40526401f/charset_normalizer-3.4.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c30197aa96e8eed02200a83fba2657b4c3acd0f0aa4bdc9f6c1af8e8962e0757", size = 150423, upload-time = "2024-12-24T18:10:45.492Z" }, | ||
| 59 | { url = "https://files.pythonhosted.org/packages/f0/b8/e6825e25deb691ff98cf5c9072ee0605dc2acfca98af70c2d1b1bc75190d/charset_normalizer-3.4.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2369eea1ee4a7610a860d88f268eb39b95cb588acd7235e02fd5a5601773d4fa", size = 143184, upload-time = "2024-12-24T18:10:47.898Z" }, | ||
| 60 | { url = "https://files.pythonhosted.org/packages/3e/a2/513f6cbe752421f16d969e32f3583762bfd583848b763913ddab8d9bfd4f/charset_normalizer-3.4.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bc2722592d8998c870fa4e290c2eec2c1569b87fe58618e67d38b4665dfa680d", size = 145268, upload-time = "2024-12-24T18:10:50.589Z" }, | ||
| 61 | { url = "https://files.pythonhosted.org/packages/74/94/8a5277664f27c3c438546f3eb53b33f5b19568eb7424736bdc440a88a31f/charset_normalizer-3.4.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ffc9202a29ab3920fa812879e95a9e78b2465fd10be7fcbd042899695d75e616", size = 147601, upload-time = "2024-12-24T18:10:52.541Z" }, | ||
| 62 | { url = "https://files.pythonhosted.org/packages/7c/5f/6d352c51ee763623a98e31194823518e09bfa48be2a7e8383cf691bbb3d0/charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:804a4d582ba6e5b747c625bf1255e6b1507465494a40a2130978bda7b932c90b", size = 141098, upload-time = "2024-12-24T18:10:53.789Z" }, | ||
| 63 | { url = "https://files.pythonhosted.org/packages/78/d4/f5704cb629ba5ab16d1d3d741396aec6dc3ca2b67757c45b0599bb010478/charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:0f55e69f030f7163dffe9fd0752b32f070566451afe180f99dbeeb81f511ad8d", size = 149520, upload-time = "2024-12-24T18:10:55.048Z" }, | ||
| 64 | { url = "https://files.pythonhosted.org/packages/c5/96/64120b1d02b81785f222b976c0fb79a35875457fa9bb40827678e54d1bc8/charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:c4c3e6da02df6fa1410a7680bd3f63d4f710232d3139089536310d027950696a", size = 152852, upload-time = "2024-12-24T18:10:57.647Z" }, | ||
| 65 | { url = "https://files.pythonhosted.org/packages/84/c9/98e3732278a99f47d487fd3468bc60b882920cef29d1fa6ca460a1fdf4e6/charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:5df196eb874dae23dcfb968c83d4f8fdccb333330fe1fc278ac5ceeb101003a9", size = 150488, upload-time = "2024-12-24T18:10:59.43Z" }, | ||
| 66 | { url = "https://files.pythonhosted.org/packages/13/0e/9c8d4cb99c98c1007cc11eda969ebfe837bbbd0acdb4736d228ccaabcd22/charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:e358e64305fe12299a08e08978f51fc21fac060dcfcddd95453eabe5b93ed0e1", size = 146192, upload-time = "2024-12-24T18:11:00.676Z" }, | ||
| 67 | { url = "https://files.pythonhosted.org/packages/b2/21/2b6b5b860781a0b49427309cb8670785aa543fb2178de875b87b9cc97746/charset_normalizer-3.4.1-cp312-cp312-win32.whl", hash = "sha256:9b23ca7ef998bc739bf6ffc077c2116917eabcc901f88da1b9856b210ef63f35", size = 95550, upload-time = "2024-12-24T18:11:01.952Z" }, | ||
| 68 | { url = "https://files.pythonhosted.org/packages/21/5b/1b390b03b1d16c7e382b561c5329f83cc06623916aab983e8ab9239c7d5c/charset_normalizer-3.4.1-cp312-cp312-win_amd64.whl", hash = "sha256:6ff8a4a60c227ad87030d76e99cd1698345d4491638dfa6673027c48b3cd395f", size = 102785, upload-time = "2024-12-24T18:11:03.142Z" }, | ||
| 69 | { url = "https://files.pythonhosted.org/packages/38/94/ce8e6f63d18049672c76d07d119304e1e2d7c6098f0841b51c666e9f44a0/charset_normalizer-3.4.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:aabfa34badd18f1da5ec1bc2715cadc8dca465868a4e73a0173466b688f29dda", size = 195698, upload-time = "2024-12-24T18:11:05.834Z" }, | ||
| 70 | { url = "https://files.pythonhosted.org/packages/24/2e/dfdd9770664aae179a96561cc6952ff08f9a8cd09a908f259a9dfa063568/charset_normalizer-3.4.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:22e14b5d70560b8dd51ec22863f370d1e595ac3d024cb8ad7d308b4cd95f8313", size = 140162, upload-time = "2024-12-24T18:11:07.064Z" }, | ||
| 71 | { url = "https://files.pythonhosted.org/packages/24/4e/f646b9093cff8fc86f2d60af2de4dc17c759de9d554f130b140ea4738ca6/charset_normalizer-3.4.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8436c508b408b82d87dc5f62496973a1805cd46727c34440b0d29d8a2f50a6c9", size = 150263, upload-time = "2024-12-24T18:11:08.374Z" }, | ||
| 72 | { url = "https://files.pythonhosted.org/packages/5e/67/2937f8d548c3ef6e2f9aab0f6e21001056f692d43282b165e7c56023e6dd/charset_normalizer-3.4.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2d074908e1aecee37a7635990b2c6d504cd4766c7bc9fc86d63f9c09af3fa11b", size = 142966, upload-time = "2024-12-24T18:11:09.831Z" }, | ||
| 73 | { url = "https://files.pythonhosted.org/packages/52/ed/b7f4f07de100bdb95c1756d3a4d17b90c1a3c53715c1a476f8738058e0fa/charset_normalizer-3.4.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:955f8851919303c92343d2f66165294848d57e9bba6cf6e3625485a70a038d11", size = 144992, upload-time = "2024-12-24T18:11:12.03Z" }, | ||
| 74 | { url = "https://files.pythonhosted.org/packages/96/2c/d49710a6dbcd3776265f4c923bb73ebe83933dfbaa841c5da850fe0fd20b/charset_normalizer-3.4.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:44ecbf16649486d4aebafeaa7ec4c9fed8b88101f4dd612dcaf65d5e815f837f", size = 147162, upload-time = "2024-12-24T18:11:13.372Z" }, | ||
| 75 | { url = "https://files.pythonhosted.org/packages/b4/41/35ff1f9a6bd380303dea55e44c4933b4cc3c4850988927d4082ada230273/charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:0924e81d3d5e70f8126529951dac65c1010cdf117bb75eb02dd12339b57749dd", size = 140972, upload-time = "2024-12-24T18:11:14.628Z" }, | ||
| 76 | { url = "https://files.pythonhosted.org/packages/fb/43/c6a0b685fe6910d08ba971f62cd9c3e862a85770395ba5d9cad4fede33ab/charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:2967f74ad52c3b98de4c3b32e1a44e32975e008a9cd2a8cc8966d6a5218c5cb2", size = 149095, upload-time = "2024-12-24T18:11:17.672Z" }, | ||
| 77 | { url = "https://files.pythonhosted.org/packages/4c/ff/a9a504662452e2d2878512115638966e75633519ec11f25fca3d2049a94a/charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:c75cb2a3e389853835e84a2d8fb2b81a10645b503eca9bcb98df6b5a43eb8886", size = 152668, upload-time = "2024-12-24T18:11:18.989Z" }, | ||
| 78 | { url = "https://files.pythonhosted.org/packages/6c/71/189996b6d9a4b932564701628af5cee6716733e9165af1d5e1b285c530ed/charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:09b26ae6b1abf0d27570633b2b078a2a20419c99d66fb2823173d73f188ce601", size = 150073, upload-time = "2024-12-24T18:11:21.507Z" }, | ||
| 79 | { url = "https://files.pythonhosted.org/packages/e4/93/946a86ce20790e11312c87c75ba68d5f6ad2208cfb52b2d6a2c32840d922/charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:fa88b843d6e211393a37219e6a1c1df99d35e8fd90446f1118f4216e307e48cd", size = 145732, upload-time = "2024-12-24T18:11:22.774Z" }, | ||
| 80 | { url = "https://files.pythonhosted.org/packages/cd/e5/131d2fb1b0dddafc37be4f3a2fa79aa4c037368be9423061dccadfd90091/charset_normalizer-3.4.1-cp313-cp313-win32.whl", hash = "sha256:eb8178fe3dba6450a3e024e95ac49ed3400e506fd4e9e5c32d30adda88cbd407", size = 95391, upload-time = "2024-12-24T18:11:24.139Z" }, | ||
| 81 | { url = "https://files.pythonhosted.org/packages/27/f2/4f9a69cc7712b9b5ad8fdb87039fd89abba997ad5cbe690d1835d40405b0/charset_normalizer-3.4.1-cp313-cp313-win_amd64.whl", hash = "sha256:b1ac5992a838106edb89654e0aebfc24f5848ae2547d22c2c3f66454daa11971", size = 102702, upload-time = "2024-12-24T18:11:26.535Z" }, | ||
| 82 | { url = "https://files.pythonhosted.org/packages/0e/f6/65ecc6878a89bb1c23a086ea335ad4bf21a588990c3f535a227b9eea9108/charset_normalizer-3.4.1-py3-none-any.whl", hash = "sha256:d98b1668f06378c6dbefec3b92299716b931cd4e6061f3c875a71ced1780ab85", size = 49767, upload-time = "2024-12-24T18:12:32.852Z" }, | ||
| 83 | ] | ||
| 84 | |||
| 85 | [[package]] | ||
| 86 | name = "dbus-next" | ||
| 87 | version = "0.2.3" | ||
| 88 | source = { registry = "https://pypi.org/simple" } | ||
| 89 | sdist = { url = "https://files.pythonhosted.org/packages/ce/45/6a40fbe886d60a8c26f480e7d12535502b5ba123814b3b9a0b002ebca198/dbus_next-0.2.3.tar.gz", hash = "sha256:f4eae26909332ada528c0a3549dda8d4f088f9b365153952a408e28023a626a5", size = 71112, upload-time = "2021-07-25T22:11:28.398Z" } | ||
| 90 | wheels = [ | ||
| 91 | { url = "https://files.pythonhosted.org/packages/d2/fc/c0a3f4c4eaa5a22fbef91713474666e13d0ea2a69c84532579490a9f2cc8/dbus_next-0.2.3-py3-none-any.whl", hash = "sha256:58948f9aff9db08316734c0be2a120f6dc502124d9642f55e90ac82ffb16a18b", size = 57885, upload-time = "2021-07-25T22:11:25.466Z" }, | ||
| 92 | ] | ||
| 93 | |||
| 94 | [[package]] | ||
| 95 | name = "desktop-notify" | ||
| 96 | version = "1.3.3" | ||
| 97 | source = { registry = "https://pypi.org/simple" } | ||
| 98 | dependencies = [ | ||
| 99 | { name = "dbus-next" }, | ||
| 100 | ] | ||
| 101 | sdist = { url = "https://files.pythonhosted.org/packages/7a/d8/7ae5779257f5f1aa0a2d50c02d70b29522bd414692f3d3bd18ef119fe82d/desktop-notify-1.3.3.tar.gz", hash = "sha256:62934ad1f72f292f9a3af5ffe45af32814af18c396c00369385540c72bf08077", size = 7828, upload-time = "2021-01-03T16:46:36.483Z" } | ||
| 102 | wheels = [ | ||
| 103 | { url = "https://files.pythonhosted.org/packages/0a/cd/a7e3bd0262f3e8a9272fd24d0193e24dad7cb4e4edd27da48e74b5523e59/desktop_notify-1.3.3-py3-none-any.whl", hash = "sha256:8ad7ecc3a9a603dd5fa3cdc11cc6265cfbc7f6df9d8ed240f4663f43ef0de37a", size = 9937, upload-time = "2021-01-03T16:46:35.157Z" }, | ||
| 104 | ] | ||
| 105 | |||
| 106 | [[package]] | ||
| 107 | name = "frozendict" | ||
| 108 | version = "2.4.6" | ||
| 109 | source = { registry = "https://pypi.org/simple" } | ||
| 110 | sdist = { url = "https://files.pythonhosted.org/packages/bb/59/19eb300ba28e7547538bdf603f1c6c34793240a90e1a7b61b65d8517e35e/frozendict-2.4.6.tar.gz", hash = "sha256:df7cd16470fbd26fc4969a208efadc46319334eb97def1ddf48919b351192b8e", size = 316416, upload-time = "2024-10-13T12:15:32.449Z" } | ||
| 111 | wheels = [ | ||
| 112 | { url = "https://files.pythonhosted.org/packages/04/13/d9839089b900fa7b479cce495d62110cddc4bd5630a04d8469916c0e79c5/frozendict-2.4.6-py311-none-any.whl", hash = "sha256:d065db6a44db2e2375c23eac816f1a022feb2fa98cbb50df44a9e83700accbea", size = 16148, upload-time = "2024-10-13T12:15:26.839Z" }, | ||
| 113 | { url = "https://files.pythonhosted.org/packages/ba/d0/d482c39cee2ab2978a892558cf130681d4574ea208e162da8958b31e9250/frozendict-2.4.6-py312-none-any.whl", hash = "sha256:49344abe90fb75f0f9fdefe6d4ef6d4894e640fadab71f11009d52ad97f370b9", size = 16146, upload-time = "2024-10-13T12:15:28.16Z" }, | ||
| 114 | { url = "https://files.pythonhosted.org/packages/a5/8e/b6bf6a0de482d7d7d7a2aaac8fdc4a4d0bb24a809f5ddd422aa7060eb3d2/frozendict-2.4.6-py313-none-any.whl", hash = "sha256:7134a2bb95d4a16556bb5f2b9736dceb6ea848fa5b6f3f6c2d6dba93b44b4757", size = 16146, upload-time = "2024-10-13T12:15:29.495Z" }, | ||
| 115 | ] | ||
| 116 | |||
| 117 | [[package]] | ||
| 118 | name = "idna" | ||
| 119 | version = "3.10" | ||
| 120 | source = { registry = "https://pypi.org/simple" } | ||
| 121 | sdist = { url = "https://files.pythonhosted.org/packages/f1/70/7703c29685631f5a7590aa73f1f1d3fa9a380e654b86af429e0934a32f7d/idna-3.10.tar.gz", hash = "sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9", size = 190490, upload-time = "2024-09-15T18:07:39.745Z" } | ||
| 122 | wheels = [ | ||
| 123 | { url = "https://files.pythonhosted.org/packages/76/c6/c88e154df9c4e1a2a66ccf0005a88dfb2650c1dffb6f5ce603dfbd452ce3/idna-3.10-py3-none-any.whl", hash = "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3", size = 70442, upload-time = "2024-09-15T18:07:37.964Z" }, | ||
| 124 | ] | ||
| 125 | |||
| 126 | [[package]] | ||
| 127 | name = "jsonpickle" | ||
| 128 | version = "4.0.5" | ||
| 129 | source = { registry = "https://pypi.org/simple" } | ||
| 130 | sdist = { url = "https://files.pythonhosted.org/packages/d6/33/4bda317ab294722fcdfff8f63aab74af9fda3675a4652d984a101aa7587e/jsonpickle-4.0.5.tar.gz", hash = "sha256:f299818b39367c361b3f26bdba827d4249ab5d383cd93144d0f94b5417aacb35", size = 315661, upload-time = "2025-03-29T19:22:56.92Z" } | ||
| 131 | wheels = [ | ||
| 132 | { url = "https://files.pythonhosted.org/packages/dc/1b/0e79cf115e0f54f1e8f56effb6ffd2ef8f92e9c324d692ede660067f1bfe/jsonpickle-4.0.5-py3-none-any.whl", hash = "sha256:b4ac7d0a75ddcdfd93445737f1d36ff28768690d43e54bf5d0ddb1d915e580df", size = 46382, upload-time = "2025-03-29T19:22:54.252Z" }, | ||
| 133 | ] | ||
| 134 | |||
| 135 | [[package]] | ||
| 136 | name = "python-dateutil" | ||
| 137 | version = "2.9.0.post0" | ||
| 138 | source = { registry = "https://pypi.org/simple" } | ||
| 139 | dependencies = [ | ||
| 140 | { name = "six" }, | ||
| 141 | ] | ||
| 142 | sdist = { url = "https://files.pythonhosted.org/packages/66/c0/0c8b6ad9f17a802ee498c46e004a0eb49bc148f2fd230864601a86dcf6db/python-dateutil-2.9.0.post0.tar.gz", hash = "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3", size = 342432, upload-time = "2024-03-01T18:36:20.211Z" } | ||
| 143 | wheels = [ | ||
| 144 | { url = "https://files.pythonhosted.org/packages/ec/57/56b9bcc3c9c6a792fcbaf139543cee77261f3651ca9da0c93f5c1221264b/python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427", size = 229892, upload-time = "2024-03-01T18:36:18.57Z" }, | ||
| 145 | ] | ||
| 146 | |||
| 147 | [[package]] | ||
| 148 | name = "pyxdg" | ||
| 149 | version = "0.28" | ||
| 150 | source = { registry = "https://pypi.org/simple" } | ||
| 151 | sdist = { url = "https://files.pythonhosted.org/packages/b0/25/7998cd2dec731acbd438fbf91bc619603fc5188de0a9a17699a781840452/pyxdg-0.28.tar.gz", hash = "sha256:3267bb3074e934df202af2ee0868575484108581e6f3cb006af1da35395e88b4", size = 77776, upload-time = "2022-06-05T11:35:01Z" } | ||
| 152 | wheels = [ | ||
| 153 | { url = "https://files.pythonhosted.org/packages/e5/8d/cf41b66a8110670e3ad03dab9b759704eeed07fa96e90fdc0357b2ba70e2/pyxdg-0.28-py2.py3-none-any.whl", hash = "sha256:bdaf595999a0178ecea4052b7f4195569c1ff4d344567bccdc12dfdf02d545ab", size = 49520, upload-time = "2022-06-05T11:34:58.832Z" }, | ||
| 154 | ] | ||
| 155 | |||
| 156 | [[package]] | ||
| 157 | name = "requests" | ||
| 158 | version = "2.32.3" | ||
| 159 | source = { registry = "https://pypi.org/simple" } | ||
| 160 | dependencies = [ | ||
| 161 | { name = "certifi" }, | ||
| 162 | { name = "charset-normalizer" }, | ||
| 163 | { name = "idna" }, | ||
| 164 | { name = "urllib3" }, | ||
| 165 | ] | ||
| 166 | sdist = { url = "https://files.pythonhosted.org/packages/63/70/2bf7780ad2d390a8d301ad0b550f1581eadbd9a20f896afe06353c2a2913/requests-2.32.3.tar.gz", hash = "sha256:55365417734eb18255590a9ff9eb97e9e1da868d4ccd6402399eaf68af20a760", size = 131218, upload-time = "2024-05-29T15:37:49.536Z" } | ||
| 167 | wheels = [ | ||
| 168 | { url = "https://files.pythonhosted.org/packages/f9/9b/335f9764261e915ed497fcdeb11df5dfd6f7bf257d4a6a2a686d80da4d54/requests-2.32.3-py3-none-any.whl", hash = "sha256:70761cfe03c773ceb22aa2f671b4757976145175cdfca038c02654d061d6dcc6", size = 64928, upload-time = "2024-05-29T15:37:47.027Z" }, | ||
| 169 | ] | ||
| 170 | |||
| 171 | [[package]] | ||
| 172 | name = "six" | ||
| 173 | version = "1.17.0" | ||
| 174 | source = { registry = "https://pypi.org/simple" } | ||
| 175 | sdist = { url = "https://files.pythonhosted.org/packages/94/e7/b2c673351809dca68a0e064b6af791aa332cf192da575fd474ed7d6f16a2/six-1.17.0.tar.gz", hash = "sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81", size = 34031, upload-time = "2024-12-04T17:35:28.174Z" } | ||
| 176 | wheels = [ | ||
| 177 | { url = "https://files.pythonhosted.org/packages/b7/ce/149a00dd41f10bc29e5921b496af8b574d8413afcd5e30dfa0ed46c2cc5e/six-1.17.0-py2.py3-none-any.whl", hash = "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274", size = 11050, upload-time = "2024-12-04T17:35:26.475Z" }, | ||
| 178 | ] | ||
| 179 | |||
| 180 | [[package]] | ||
| 181 | name = "tabulate" | ||
| 182 | version = "0.9.0" | ||
| 183 | source = { registry = "https://pypi.org/simple" } | ||
| 184 | sdist = { url = "https://files.pythonhosted.org/packages/ec/fe/802052aecb21e3797b8f7902564ab6ea0d60ff8ca23952079064155d1ae1/tabulate-0.9.0.tar.gz", hash = "sha256:0095b12bf5966de529c0feb1fa08671671b3368eec77d7ef7ab114be2c068b3c", size = 81090, upload-time = "2022-10-06T17:21:48.54Z" } | ||
| 185 | wheels = [ | ||
| 186 | { url = "https://files.pythonhosted.org/packages/40/44/4a5f08c96eb108af5cb50b41f76142f0afa346dfa99d5296fe7202a11854/tabulate-0.9.0-py3-none-any.whl", hash = "sha256:024ca478df22e9340661486f85298cff5f6dcdba14f3813e8830015b9ed1948f", size = 35252, upload-time = "2022-10-06T17:21:44.262Z" }, | ||
| 187 | ] | ||
| 188 | |||
| 189 | [[package]] | ||
| 190 | name = "toml" | ||
| 191 | version = "0.10.2" | ||
| 192 | source = { registry = "https://pypi.org/simple" } | ||
| 193 | sdist = { url = "https://files.pythonhosted.org/packages/be/ba/1f744cdc819428fc6b5084ec34d9b30660f6f9daaf70eead706e3203ec3c/toml-0.10.2.tar.gz", hash = "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f", size = 22253, upload-time = "2020-11-01T01:40:22.204Z" } | ||
| 194 | wheels = [ | ||
| 195 | { url = "https://files.pythonhosted.org/packages/44/6f/7120676b6d73228c96e17f1f794d8ab046fc910d781c8d151120c3f1569e/toml-0.10.2-py2.py3-none-any.whl", hash = "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b", size = 16588, upload-time = "2020-11-01T01:40:20.672Z" }, | ||
| 196 | ] | ||
| 197 | |||
| 198 | [[package]] | ||
| 199 | name = "uritools" | ||
| 200 | version = "4.0.3" | ||
| 201 | source = { registry = "https://pypi.org/simple" } | ||
| 202 | sdist = { url = "https://files.pythonhosted.org/packages/d3/43/4182fb2a03145e6d38698e38b49114ce59bc8c79063452eb585a58f8ce78/uritools-4.0.3.tar.gz", hash = "sha256:ee06a182a9c849464ce9d5fa917539aacc8edd2a4924d1b7aabeeecabcae3bc2", size = 24184, upload-time = "2024-05-28T18:07:45.194Z" } | ||
| 203 | wheels = [ | ||
| 204 | { url = "https://files.pythonhosted.org/packages/e6/17/5a4510d9ca9cc8be217ce359eb54e693dca81cf4d442308b282d5131b17d/uritools-4.0.3-py3-none-any.whl", hash = "sha256:bae297d090e69a0451130ffba6f2f1c9477244aa0a5543d66aed2d9f77d0dd9c", size = 10304, upload-time = "2024-05-28T18:07:42.731Z" }, | ||
| 205 | ] | ||
| 206 | |||
| 207 | [[package]] | ||
| 208 | name = "urllib3" | ||
| 209 | version = "2.3.0" | ||
| 210 | source = { registry = "https://pypi.org/simple" } | ||
| 211 | sdist = { url = "https://files.pythonhosted.org/packages/aa/63/e53da845320b757bf29ef6a9062f5c669fe997973f966045cb019c3f4b66/urllib3-2.3.0.tar.gz", hash = "sha256:f8c5449b3cf0861679ce7e0503c7b44b5ec981bec0d1d3795a07f1ba96f0204d", size = 307268, upload-time = "2024-12-22T07:47:30.032Z" } | ||
| 212 | wheels = [ | ||
| 213 | { url = "https://files.pythonhosted.org/packages/c8/19/4ec628951a74043532ca2cf5d97b7b14863931476d117c471e8e2b1eb39f/urllib3-2.3.0-py3-none-any.whl", hash = "sha256:1cee9ad369867bfdbbb48b7dd50374c0967a0bb7710050facf0dd6911440e3df", size = 128369, upload-time = "2024-12-22T07:47:28.074Z" }, | ||
| 214 | ] | ||
| 215 | |||
| 216 | [[package]] | ||
| 217 | name = "worktime" | ||
| 218 | version = "1.0.0" | ||
| 219 | source = { editable = "." } | ||
| 220 | dependencies = [ | ||
| 221 | { name = "atomicwriter" }, | ||
| 222 | { name = "desktop-notify" }, | ||
| 223 | { name = "frozendict" }, | ||
| 224 | { name = "jsonpickle" }, | ||
| 225 | { name = "python-dateutil" }, | ||
| 226 | { name = "pyxdg" }, | ||
| 227 | { name = "requests" }, | ||
| 228 | { name = "tabulate" }, | ||
| 229 | { name = "toml" }, | ||
| 230 | { name = "uritools" }, | ||
| 231 | ] | ||
| 232 | |||
| 233 | [package.metadata] | ||
| 234 | requires-dist = [ | ||
| 235 | { name = "atomicwriter", specifier = ">=0.2.5" }, | ||
| 236 | { name = "desktop-notify", specifier = ">=1.3.3" }, | ||
| 237 | { name = "frozendict", specifier = ">=2.4.6" }, | ||
| 238 | { name = "jsonpickle", specifier = ">=4.0.5,<5" }, | ||
| 239 | { name = "python-dateutil", specifier = ">=2.9.0.post0,<3" }, | ||
| 240 | { name = "pyxdg", specifier = ">=0.28,<0.29" }, | ||
| 241 | { name = "requests", specifier = ">=2.32.3,<3" }, | ||
| 242 | { name = "tabulate", specifier = ">=0.9.0,<0.10" }, | ||
| 243 | { name = "toml", specifier = ">=0.10.2,<0.11" }, | ||
| 244 | { name = "uritools", specifier = ">=4.0.3,<5" }, | ||
| 245 | ] | ||
| 246 | |||
| 247 | [package.metadata.requires-dev] | ||
| 248 | dev = [] | ||
diff --git a/overlays/worktime/worktime/__main__.py b/overlays/worktime/worktime/__main__.py index 362c8da4..ffeb1b84 100755 --- a/overlays/worktime/worktime/__main__.py +++ b/overlays/worktime/worktime/__main__.py | |||
| @@ -1,10 +1,12 @@ | |||
| 1 | import requests | 1 | import requests |
| 2 | from requests.exceptions import HTTPError | 2 | from requests.exceptions import HTTPError |
| 3 | from requests.auth import HTTPBasicAuth | 3 | from requests.auth import HTTPBasicAuth |
| 4 | from requests.adapters import HTTPAdapter, Retry | ||
| 4 | from datetime import * | 5 | from datetime import * |
| 5 | from xdg import BaseDirectory | 6 | from xdg import BaseDirectory |
| 6 | import toml | 7 | import toml |
| 7 | from uritools import (uricompose) | 8 | from uritools import uricompose |
| 9 | from urllib.parse import urljoin | ||
| 8 | 10 | ||
| 9 | from inspect import signature | 11 | from inspect import signature |
| 10 | 12 | ||
| @@ -23,80 +25,80 @@ import argparse | |||
| 23 | from copy import deepcopy | 25 | from copy import deepcopy |
| 24 | 26 | ||
| 25 | import sys | 27 | import sys |
| 26 | from sys import stderr | 28 | from sys import stderr, stdout |
| 27 | 29 | ||
| 28 | from tabulate import tabulate | 30 | from tabulate import tabulate |
| 29 | 31 | ||
| 30 | from itertools import groupby, count | 32 | from itertools import groupby, count, islice |
| 31 | from functools import cache, partial | 33 | from functools import cache, partial |
| 32 | 34 | ||
| 33 | import backoff | ||
| 34 | |||
| 35 | from pathlib import Path | 35 | from pathlib import Path |
| 36 | 36 | ||
| 37 | from collections import defaultdict | 37 | from collections import defaultdict |
| 38 | from collections.abc import Iterable, Generator | ||
| 39 | from typing import Any | ||
| 38 | 40 | ||
| 39 | import jsonpickle | 41 | import jsonpickle |
| 40 | from hashlib import blake2s | 42 | from hashlib import blake2s |
| 43 | import json | ||
| 44 | |||
| 45 | import asyncio | ||
| 46 | |||
| 47 | from frozendict import frozendict | ||
| 48 | from contextlib import closing | ||
| 49 | import os | ||
| 50 | from time import clock_gettime_ns, CLOCK_MONOTONIC | ||
| 51 | from atomicwriter import AtomicWriter | ||
| 52 | import desktop_notify.aio as notify | ||
| 53 | |||
| 54 | class BearerAuth(requests.auth.AuthBase): | ||
| 55 | def __init__(self, token): | ||
| 56 | self.token = token | ||
| 57 | def __call__(self, r): | ||
| 58 | r.headers["authorization"] = "Bearer " + self.token | ||
| 59 | return r | ||
| 60 | |||
| 61 | class KimaiSession(requests.Session): | ||
| 62 | def __init__(self, base_url: str, api_token: str): | ||
| 63 | super().__init__() | ||
| 64 | self.base_url = base_url | ||
| 65 | self.auth = BearerAuth(api_token) | ||
| 66 | retries = Retry(total=5, backoff_factor=0.1, status_forcelist=[500, 502, 503, 504]) | ||
| 67 | super().mount(base_url, HTTPAdapter(max_retries=retries)) | ||
| 68 | |||
| 69 | def request(self, method, url, *args, **kwargs): | ||
| 70 | joined_url = urljoin(self.base_url, url) | ||
| 71 | return super().request(method, joined_url, *args, headers = {'Accept': 'application/json'} | (kwargs['headers'] if 'headers' in kwargs else {}), **{k: v for k, v in kwargs.items() if k not in ['headers']}) | ||
| 72 | |||
| 73 | class KimaiAPI(object): | ||
| 74 | def __init__(self, base_url: str, api_token: str, clients: Iterable[str]): | ||
| 75 | self._session = KimaiSession(base_url, api_token) | ||
| 76 | self._kimai_clients = self._session.get('/api/customers').json() | ||
| 77 | self._client_ids = self.resolve_clients(clients) | ||
| 78 | kimai_user = self._session.get('/api/users/me').json() | ||
| 79 | self._tz = gettz(kimai_user['timezone']) | ||
| 80 | |||
| 81 | def resolve_clients(self, clients: Iterable[str]) -> frozenset[int]: | ||
| 82 | return frozenset({ client['id'] for client in self._kimai_clients if client['name'] in clients }) | ||
| 83 | |||
| 84 | def render_datetime(self, datetime: datetime) -> str: | ||
| 85 | return datetime.astimezone(self._tz).strftime('%Y-%m-%dT%H:%M:%S') | ||
| 86 | |||
| 87 | def get_timesheets(self, params: dict[str, Any] = {}) -> Generator[Any]: | ||
| 88 | for page in count(start=1): | ||
| 89 | resp = self._session.get('/api/timesheets', params=params | {'size': 100, 'page': page}) | ||
| 90 | if resp.status_code == 404: | ||
| 91 | break | ||
| 92 | yield from resp.json() | ||
| 41 | 93 | ||
| 42 | class TogglAPISection(Enum): | 94 | def entry_durations(self, start_date: datetime, *, end_date: datetime, clients: Iterable[str] | None = None) -> Generator[timedelta]: |
| 43 | TOGGL = '/api/v9' | 95 | client_ids = None |
| 44 | REPORTS = '/reports/api/v2' | 96 | if clients is not None and not clients: |
| 45 | |||
| 46 | class TogglAPIError(Exception): | ||
| 47 | def __init__(self, response, *, http_error=None): | ||
| 48 | self.http_error = http_error | ||
| 49 | self.response = response | ||
| 50 | |||
| 51 | def __str__(self): | ||
| 52 | if not self.http_error is None: | ||
| 53 | return str(self.http_error) | ||
| 54 | else: | ||
| 55 | return self.response.text | ||
| 56 | |||
| 57 | class TogglAPI(object): | ||
| 58 | def __init__(self, api_token, workspace_id, client_ids): | ||
| 59 | self._api_token = api_token | ||
| 60 | self._workspace_id = workspace_id | ||
| 61 | self._client_ids = set(map(int, client_ids.split(','))) if client_ids else None | ||
| 62 | |||
| 63 | def _make_url(self, api=TogglAPISection.TOGGL, section=['me', 'time_entries', 'current'], params={}): | ||
| 64 | if api is TogglAPISection.REPORTS: | ||
| 65 | params.update({'user_agent': 'worktime', 'workspace_id': self._workspace_id}) | ||
| 66 | |||
| 67 | api_path = api.value | ||
| 68 | section_path = '/'.join(section) | ||
| 69 | uri = uricompose(scheme='https', host='api.track.toggl.com', path=f"{api_path}/{section_path}", query=params) | ||
| 70 | |||
| 71 | return uri | ||
| 72 | |||
| 73 | def _query(self, url, method): | ||
| 74 | response = self._raw_query(url, method) | ||
| 75 | response.raise_for_status() | ||
| 76 | return response | ||
| 77 | |||
| 78 | @backoff.on_predicate( | ||
| 79 | backoff.expo, | ||
| 80 | factor=0.1, max_value=2, | ||
| 81 | predicate=lambda r: r.status_code == 429, | ||
| 82 | max_time=10, | ||
| 83 | ) | ||
| 84 | def _raw_query(self, url, method): | ||
| 85 | headers = {'content-type': 'application/json', 'accept': 'application/json'} | ||
| 86 | response = None | ||
| 87 | |||
| 88 | if method == 'GET': | ||
| 89 | response = requests.get(url, headers=headers, auth=HTTPBasicAuth(self._api_token, 'api_token')) | ||
| 90 | elif method == 'POST': | ||
| 91 | response = requests.post(url, headers=headers, auth=HTTPBasicAuth(self._api_token, 'api_token')) | ||
| 92 | else: | ||
| 93 | raise ValueError(f"Undefined HTTP method “{method}”") | ||
| 94 | |||
| 95 | return response | ||
| 96 | |||
| 97 | def entry_durations(self, start_date, *, end_date, rounding=False, client_ids): | ||
| 98 | if client_ids is not None and not client_ids: | ||
| 99 | return | 97 | return |
| 98 | elif clients is None: | ||
| 99 | client_ids = self._client_ids | ||
| 100 | else: | ||
| 101 | client_ids = self.resolve_clients(clients) | ||
| 100 | 102 | ||
| 101 | cache_dir = Path(BaseDirectory.save_cache_path('worktime')) / 'entry_durations' | 103 | cache_dir = Path(BaseDirectory.save_cache_path('worktime')) / 'entry_durations' |
| 102 | step = timedelta(days = 120) | 104 | step = timedelta(days = 120) |
| @@ -115,11 +117,8 @@ class TogglAPI(object): | |||
| 115 | cache_key = blake2s(jsonpickle.encode({ | 117 | cache_key = blake2s(jsonpickle.encode({ |
| 116 | 'start': req_start, | 118 | 'start': req_start, |
| 117 | 'end': req_end, | 119 | 'end': req_end, |
| 118 | 'rounding': rounding, | 120 | 'client_ids': client_ids, |
| 119 | 'clients': client_ids, | 121 | }).encode('utf-8'), key = self._session.auth.token.encode('utf-8')).hexdigest() |
| 120 | 'workspace': self._workspace_id, | ||
| 121 | 'workspace_clients': self._client_ids | ||
| 122 | }).encode('utf-8'), key = self._api_token.encode('utf-8')).hexdigest() | ||
| 123 | cache_path = cache_dir / cache_key[:2] / cache_key[2:4] / f'{cache_key[4:]}.json' | 122 | cache_path = cache_dir / cache_key[:2] / cache_key[2:4] / f'{cache_key[4:]}.json' |
| 124 | try: | 123 | try: |
| 125 | with cache_path.open('r', encoding='utf-8') as ch: | 124 | with cache_path.open('r', encoding='utf-8') as ch: |
| @@ -129,85 +128,83 @@ class TogglAPI(object): | |||
| 129 | pass | 128 | pass |
| 130 | 129 | ||
| 131 | entries = list() | 130 | entries = list() |
| 132 | params = { 'since': (req_start - timedelta(days=1)).date().isoformat(), | 131 | params = { |
| 133 | 'until': (req_end + timedelta(days=1)).date().isoformat(), | 132 | 'begin': self.render_datetime(req_start), |
| 134 | 'rounding': 'yes' if rounding else 'no', | 133 | 'end': self.render_datetime(req_end), |
| 135 | 'billable': 'yes' | 134 | 'customers[]': list(client_ids), |
| 136 | } | 135 | 'billable': 1, |
| 137 | if client_ids is not None: | 136 | } |
| 138 | params |= { 'client_ids': ','.join(map(str, client_ids)) } | 137 | |
| 139 | for page in count(start = 1): | 138 | for entry in self.get_timesheets(params): |
| 140 | url = self._make_url(api = TogglAPISection.REPORTS, section = ['details'], params = params | { 'page': page }) | 139 | if entry['end'] is None: |
| 141 | r = self._query(url = url, method='GET') | 140 | continue |
| 142 | if not r or not r.json(): | 141 | |
| 143 | raise TogglAPIError(r) | 142 | start = isoparse(entry['begin']) |
| 144 | report = r.json() | 143 | end = isoparse(entry['end']) |
| 145 | for entry in report['data']: | ||
| 146 | start = isoparse(entry['start']) | ||
| 147 | end = isoparse(entry['end']) | ||
| 148 | |||
| 149 | if start > req_end or end < req_start: | ||
| 150 | continue | ||
| 151 | 144 | ||
| 152 | x = min(end, req_end) - max(start, req_start) | 145 | if start > req_end or end < req_start: |
| 153 | if cache_key: | 146 | continue |
| 154 | entries.append(x) | 147 | |
| 155 | yield x | 148 | x = min(end, req_end) - max(start, req_start) |
| 156 | if not report['data']: | 149 | if cache_key: |
| 157 | break | 150 | entries.append(x) |
| 151 | yield x | ||
| 158 | 152 | ||
| 159 | if cache_path: | 153 | if cache_path: |
| 160 | cache_path.parent.mkdir(parents=True, exist_ok=True) | 154 | cache_path.parent.mkdir(parents=True, exist_ok=True) |
| 161 | with cache_path.open('w', encoding='utf-8') as ch: | 155 | with cache_path.open('w', encoding='utf-8') as ch: |
| 162 | ch.write(jsonpickle.encode(entries)) | 156 | ch.write(jsonpickle.encode(entries)) |
| 163 | # res = timedelta(milliseconds=report['total_billable']) if report['total_billable'] else timedelta(milliseconds=0) | ||
| 164 | # return res | ||
| 165 | |||
| 166 | def get_billable_hours(self, start_date, end_date=datetime.now(timezone.utc), rounding=False): | ||
| 167 | billable_acc = timedelta(milliseconds = 0) | ||
| 168 | if 0 in self._client_ids: | ||
| 169 | url = self._make_url(api = TogglAPISection.TOGGL, section = ['workspaces', self._workspace_id, 'clients']) | ||
| 170 | r = self._query(url = url, method = 'GET') | ||
| 171 | if not r or not r.json(): | ||
| 172 | raise TogglAPIError(r) | ||
| 173 | |||
| 174 | billable_acc += sum(self.entry_durations(start_date, end_date=end_date, rounding=rounding, client_ids=None), start=timedelta(milliseconds=0)) - sum(self.entry_durations(start_date, end_date=end_date, rounding=rounding, client_ids=frozenset(map(lambda c: c['id'], r.json()))), start=timedelta(milliseconds=0)) | ||
| 175 | 157 | ||
| 176 | billable_acc += sum(self.entry_durations(start_date, end_date=end_date, rounding=rounding, client_ids=frozenset(*(self._client_ids - {0}))), start=timedelta(milliseconds=0)) | 158 | def get_billable_hours(self, start_date: datetime, end_date: datetime = datetime.now(timezone.utc)) -> timedelta: |
| 159 | return sum(self.entry_durations(start_date, end_date=end_date), start=timedelta(milliseconds=0)) | ||
| 177 | 160 | ||
| 178 | return billable_acc | 161 | def get_running_entry(self) -> Any | None: |
| 162 | kimai_entries = self._session.get('/api/timesheets/active').json() | ||
| 163 | if not kimai_entries: | ||
| 164 | return None | ||
| 165 | entry = kimai_entries[0] | ||
| 179 | 166 | ||
| 180 | def get_running_clock(self, now=datetime.now(timezone.utc)): | 167 | if entry['project']['customer']['id'] not in self._client_ids: |
| 181 | url = self._make_url(api = TogglAPISection.TOGGL, section = ['me', 'time_entries', 'current']) | 168 | return None |
| 182 | r = self._query(url = url, method='GET') | ||
| 183 | 169 | ||
| 184 | if not r or (not r.json() and r.json() is not None): | 170 | return entry |
| 185 | raise TogglAPIError(r) | ||
| 186 | 171 | ||
| 187 | if not r.json() or not r.json()['billable']: | 172 | def get_running_clock(self, now: datetime = datetime.now(timezone.utc)) -> timedelta | None: |
| 173 | entry = self.get_running_entry() | ||
| 174 | if not entry: | ||
| 188 | return None | 175 | return None |
| 176 | start = isoparse(entry['begin']) | ||
| 177 | return now - start if start <= now else None | ||
| 189 | 178 | ||
| 190 | if self._client_ids is not None: | 179 | def get_recent_entries(self) -> Generator[Any]: |
| 191 | if 'pid' in r.json() and r.json()['pid']: | 180 | step = timedelta(days = 7) |
| 192 | url = self._make_url(api = TogglAPISection.TOGGL, section = ['projects', str(r.json()['pid'])]) | 181 | now = datetime.now().astimezone(timezone.utc) |
| 193 | pr = self._query(url = url, method = 'GET') | 182 | ids = set() |
| 194 | if not pr or not pr.json(): | 183 | for req_end in (now - step * i for i in count()): |
| 195 | raise TogglAPIError(pr) | 184 | params = { |
| 196 | 185 | 'begin': self.render_datetime(req_end - step), | |
| 197 | if not pr.json(): | 186 | 'end': self.render_datetime(req_end), |
| 198 | return None | 187 | 'full': 'true', |
| 188 | } | ||
| 189 | for entry in self.get_timesheets(params): | ||
| 190 | if entry['id'] in ids: | ||
| 191 | continue | ||
| 192 | ids.add(entry['id']) | ||
| 193 | yield entry | ||
| 199 | 194 | ||
| 200 | if 'cid' in pr.json() and pr.json()['cid']: | 195 | def start_clock(self, project_id: int, activity_id: int, description: str | None = None, tags: Iterable[str] | None = None, billable: bool = True): |
| 201 | if pr.json()['cid'] not in self._client_ids: | 196 | self._session.post('/api/timesheets', json={ |
| 202 | return None | 197 | 'begin': self.render_datetime(datetime.now()), |
| 203 | elif 0 not in self._client_ids: | 198 | 'project': project_id, |
| 204 | return None | 199 | 'activity': activity_id, |
| 205 | elif 0 not in self._client_ids: | 200 | 'description': description if description else '', |
| 206 | return None | 201 | 'tags': (','.join(tags)) if tags else '', |
| 202 | 'billable': billable, | ||
| 203 | }).raise_for_status() | ||
| 207 | 204 | ||
| 208 | start = isoparse(r.json()['start']) | 205 | def stop_clock(self, running_id: int): |
| 206 | self._session.patch(f'/api/timesheets/{running_id}/stop').raise_for_status() | ||
| 209 | 207 | ||
| 210 | return now - start if start <= now else None | ||
| 211 | 208 | ||
| 212 | class Worktime(object): | 209 | class Worktime(object): |
| 213 | time_worked = timedelta() | 210 | time_worked = timedelta() |
| @@ -223,6 +220,7 @@ class Worktime(object): | |||
| 223 | leave_budget = dict() | 220 | leave_budget = dict() |
| 224 | time_per_day = None | 221 | time_per_day = None |
| 225 | workdays = None | 222 | workdays = None |
| 223 | pull_forward = dict() | ||
| 226 | 224 | ||
| 227 | @staticmethod | 225 | @staticmethod |
| 228 | @cache | 226 | @cache |
| @@ -279,10 +277,10 @@ class Worktime(object): | |||
| 279 | 277 | ||
| 280 | config = Worktime.config() | 278 | config = Worktime.config() |
| 281 | config_dir = BaseDirectory.load_first_config('worktime') | 279 | config_dir = BaseDirectory.load_first_config('worktime') |
| 282 | api = TogglAPI( | 280 | api = KimaiAPI( |
| 283 | api_token=config.get("TOGGL", {}).get("ApiToken", None), | 281 | base_url=config.get("KIMAI", {}).get("BaseUrl", None), |
| 284 | workspace_id=config.get("TOGGL", {}).get("Workspace", None), | 282 | api_token=config.get("KIMAI", {}).get("ApiToken", None), |
| 285 | client_ids=config.get("TOGGL", {}).get("ClientIds", None) | 283 | clients=config.get("KIMAI", {}).get("Clients", None) |
| 286 | ) | 284 | ) |
| 287 | date_format = config.get("WORKTIME", {}).get("DateFormat", '%Y-%m-%d') | 285 | date_format = config.get("WORKTIME", {}).get("DateFormat", '%Y-%m-%d') |
| 288 | 286 | ||
| @@ -377,10 +375,7 @@ class Worktime(object): | |||
| 377 | parse_datestr(stripped_line) | 375 | parse_datestr(stripped_line) |
| 378 | 376 | ||
| 379 | for day in [fromDay + timedelta(days = x) for x in range(0, (toDay - fromDay).days + 1)]: | 377 | for day in [fromDay + timedelta(days = x) for x in range(0, (toDay - fromDay).days + 1)]: |
| 380 | if self.end_date.date() < day or day < self.start_date.date(): | 378 | if self.would_be_workday(day) and self.start_date.date() <= day and day <= self.end_date.date(): |
| 381 | continue | ||
| 382 | |||
| 383 | if self.would_be_workday(day): | ||
| 384 | if excused_kind == 'leave': | 379 | if excused_kind == 'leave': |
| 385 | self.leave_days.add(day) | 380 | self.leave_days.add(day) |
| 386 | elif time is not None and time >= self.time_per_day(day): | 381 | elif time is not None and time >= self.time_per_day(day): |
| @@ -390,14 +385,34 @@ class Worktime(object): | |||
| 390 | if e.errno != 2: | 385 | if e.errno != 2: |
| 391 | raise e | 386 | raise e |
| 392 | 387 | ||
| 393 | pull_forward = dict() | 388 | self.time_per_day = lambda day: timedelta(hours = hours_per_week(day)) / len(self.workdays) - (holidays[day] if day in holidays else timedelta()) |
| 394 | 389 | ||
| 395 | start_day = self.start_date.date() | 390 | start_day = self.start_date.date() |
| 396 | end_day = self.end_date.date() | 391 | end_day = self.end_date.date() |
| 397 | 392 | ||
| 393 | self.extra_days_to_work = dict() | ||
| 394 | |||
| 398 | try: | 395 | try: |
| 399 | with open(Path(config_dir) / "pull-forward", 'r') as excused: | 396 | with open(Path(config_dir) / "days-to-work", 'r') as extra_days_to_work_file: |
| 400 | for line in excused: | 397 | for line in extra_days_to_work_file: |
| 398 | stripped_line = line.strip() | ||
| 399 | if stripped_line: | ||
| 400 | splitLine = stripped_line.split(' ') | ||
| 401 | if len(splitLine) == 2: | ||
| 402 | [hours, datestr] = splitLine | ||
| 403 | day = datetime.strptime(datestr, date_format).replace(tzinfo=tzlocal()).date() | ||
| 404 | self.extra_days_to_work[day] = timedelta(hours = float(hours)) | ||
| 405 | else: | ||
| 406 | day = datetime.strptime(stripped_line, date_format).replace(tzinfo=tzlocal()).date() | ||
| 407 | self.extra_days_to_work[day] = self.time_per_day(day) | ||
| 408 | except IOError as e: | ||
| 409 | if e.errno != 2: | ||
| 410 | raise e | ||
| 411 | |||
| 412 | |||
| 413 | try: | ||
| 414 | with open(Path(config_dir) / "pull-forward", 'r') as pull_forward: | ||
| 415 | for line in pull_forward: | ||
| 401 | stripped_line = line.strip() | 416 | stripped_line = line.strip() |
| 402 | if stripped_line: | 417 | if stripped_line: |
| 403 | [hours, datestr] = stripped_line.split(' ') | 418 | [hours, datestr] = stripped_line.split(' ') |
| @@ -418,44 +433,29 @@ class Worktime(object): | |||
| 418 | if not d == datetime.strptime(c, date_format).replace(tzinfo=tzlocal()).date(): break | 433 | if not d == datetime.strptime(c, date_format).replace(tzinfo=tzlocal()).date(): break |
| 419 | else: | 434 | else: |
| 420 | if d >= self.end_date.date(): | 435 | if d >= self.end_date.date(): |
| 421 | pull_forward[d] = min(timedelta(hours = float(hours)), self.time_per_day(d) - (holidays[d] if d in holidays else timedelta())) | 436 | time_for_day = self.time_per_day(d) if d.isoweekday() in self.workdays else timedelta() |
| 437 | if d in self.extra_days_to_work: | ||
| 438 | time_for_day += self.extra_days_to_work[d] | ||
| 439 | self.pull_forward[d] = min(timedelta(hours = float(hours)), time_for_day) | ||
| 422 | except IOError as e: | 440 | except IOError as e: |
| 423 | if e.errno != 2: | 441 | if e.errno != 2: |
| 424 | raise e | 442 | raise e |
| 425 | 443 | ||
| 444 | if self.pull_forward: | ||
| 445 | for year in range(self.end_date.year + 1, max(self.pull_forward.keys()).year + 1): | ||
| 446 | holidays |= {k: v * timedelta(hours = hours_per_week(k)) / len(self.workdays) for k, v in Worktime.holidays(year).items()} | ||
| 447 | |||
| 426 | self.days_to_work = dict() | 448 | self.days_to_work = dict() |
| 427 | 449 | ||
| 428 | if pull_forward: | 450 | # if self.pull_forward: |
| 429 | end_day = max(end_day, max(list(pull_forward))) | 451 | # end_day = max(end_day, max(self.pull_forward.keys())) |
| 430 | 452 | ||
| 431 | for day in [start_day + timedelta(days = x) for x in range(0, (end_day - start_day).days + 1)]: | 453 | for day in [start_day + timedelta(days = x) for x in range(0, (end_day - start_day).days + 1)]: |
| 432 | if day.isoweekday() in self.workdays: | 454 | if day.isoweekday() in self.workdays: |
| 433 | time_to_work = self.time_per_day(day) | 455 | time_to_work = self.time_per_day(day) |
| 434 | if day in holidays.keys(): | ||
| 435 | time_to_work -= holidays[day] | ||
| 436 | if time_to_work > timedelta(): | 456 | if time_to_work > timedelta(): |
| 437 | self.days_to_work[day] = time_to_work | 457 | self.days_to_work[day] = time_to_work |
| 438 | 458 | ||
| 439 | self.extra_days_to_work = dict() | ||
| 440 | |||
| 441 | try: | ||
| 442 | with open(Path(config_dir) / "days-to-work", 'r') as extra_days_to_work_file: | ||
| 443 | for line in extra_days_to_work_file: | ||
| 444 | stripped_line = line.strip() | ||
| 445 | if stripped_line: | ||
| 446 | splitLine = stripped_line.split(' ') | ||
| 447 | if len(splitLine) == 2: | ||
| 448 | [hours, datestr] = splitLine | ||
| 449 | day = datetime.strptime(datestr, date_format).replace(tzinfo=tzlocal()).date() | ||
| 450 | self.extra_days_to_work[day] = timedelta(hours = float(hours)) | ||
| 451 | else: | ||
| 452 | day = datetime.strptime(stripped_line, date_format).replace(tzinfo=tzlocal()).date() | ||
| 453 | self.extra_days_to_work[day] = self.time_per_day(day) | ||
| 454 | except IOError as e: | ||
| 455 | if e.errno != 2: | ||
| 456 | raise e | ||
| 457 | |||
| 458 | |||
| 459 | self.now_is_workday = self.is_workday(self.now.date()) | 459 | self.now_is_workday = self.is_workday(self.now.date()) |
| 460 | 460 | ||
| 461 | self.time_worked = timedelta() | 461 | self.time_worked = timedelta() |
| @@ -470,33 +470,46 @@ class Worktime(object): | |||
| 470 | self.extra_days_to_work[self.now.date()] = timedelta() | 470 | self.extra_days_to_work[self.now.date()] = timedelta() |
| 471 | 471 | ||
| 472 | self.time_to_work = sum([self.days_to_work[day] for day in self.days_to_work.keys() if day <= self.end_date.date()], timedelta()) | 472 | self.time_to_work = sum([self.days_to_work[day] for day in self.days_to_work.keys() if day <= self.end_date.date()], timedelta()) |
| 473 | for day in [d for d in list(pull_forward) if d > self.end_date.date()]: | 473 | for day in [d for d in list(self.pull_forward) if d > self.end_date.date()]: |
| 474 | days_forward = set([d for d in self.days_to_work.keys() if d >= self.end_date.date() and d < day and (not d in pull_forward or d == self.end_date.date())]) | 474 | days_forward = set([d for d in [start_day + timedelta(days = x) for x in range(0, (day - start_day).days + 1)] if d >= self.end_date.date() and d < day and (d not in self.pull_forward or d == self.end_date.date())]) |
| 475 | extra_days_forward = set([d for d in self.extra_days_to_work.keys() if d >= self.end_date.date() and d < day and (not d in pull_forward or d == self.end_date.date())]) | 475 | extra_days_forward = set([d for d in self.extra_days_to_work.keys() if d >= self.end_date.date() and d < day and (d not in self.pull_forward or d == self.end_date.date())]) |
| 476 | days_forward = days_forward.union(extra_days_forward) | 476 | days_forward |= extra_days_forward |
| 477 | 477 | ||
| 478 | extra_day_time_left = timedelta() | 478 | extra_day_time_left = timedelta() |
| 479 | for extra_day in extra_days_forward: | 479 | for extra_day in extra_days_forward: |
| 480 | day_time = max(timedelta(), self.time_per_day(extra_day) - self.extra_days_to_work[extra_day]) | 480 | day_time = max(timedelta(), self.time_per_day(extra_day) - self.extra_days_to_work[extra_day]) |
| 481 | extra_day_time_left += day_time | 481 | extra_day_time_left += day_time |
| 482 | extra_day_time = min(extra_day_time_left, pull_forward[day]) | 482 | extra_day_time = min(extra_day_time_left, self.pull_forward[day]) |
| 483 | time_forward = pull_forward[day] - extra_day_time | 483 | time_forward = self.pull_forward[day] - extra_day_time |
| 484 | if extra_day_time_left > timedelta(): | 484 | if extra_day_time_left > timedelta(): |
| 485 | for extra_day in extra_days_forward: | 485 | for extra_day in extra_days_forward: |
| 486 | day_time = max(timedelta(), self.time_per_day(extra_day) - self.extra_days_to_work[extra_day]) | 486 | day_time = max(timedelta(), self.time_per_day(extra_day) - self.extra_days_to_work[extra_day]) |
| 487 | self.extra_days_to_work[extra_day] += extra_day_time * (day_time / extra_day_time_left) | 487 | self.extra_days_to_work[extra_day] += extra_day_time * (day_time / extra_day_time_left) |
| 488 | 488 | ||
| 489 | hours_per_day_forward = time_forward / len(days_forward) if len(days_forward) > 0 else timedelta() | 489 | def days_count(days_forward): |
| 490 | r = 0 | ||
| 491 | for day in sorted(days_forward): | ||
| 492 | day_time = timedelta() | ||
| 493 | if day in self.extra_days_to_work: | ||
| 494 | day_time += self.extra_days_to_work[day] | ||
| 495 | if day in holidays and not day in self.extra_days_to_work: | ||
| 496 | day_time -= holidays[day] | ||
| 497 | if day.isoweekday() in self.workdays: | ||
| 498 | day_time += timedelta(hours = hours_per_week(day)) / len(self.workdays) | ||
| 499 | r += max(timedelta(), day_time) / (timedelta(hours = hours_per_week(day)) / len(self.workdays)) | ||
| 500 | return r | ||
| 501 | |||
| 502 | hours_per_day_forward = time_forward / days_count(days_forward) if days_count(days_forward) > 0 else timedelta() | ||
| 490 | days_forward.discard(self.end_date.date()) | 503 | days_forward.discard(self.end_date.date()) |
| 491 | 504 | ||
| 492 | self.time_pulled_forward += time_forward - hours_per_day_forward * len(days_forward) | 505 | self.time_pulled_forward += time_forward - hours_per_day_forward * days_count(days_forward) |
| 493 | 506 | ||
| 494 | if self.end_date.date() in self.extra_days_to_work: | 507 | if self.end_date.date() in self.extra_days_to_work: |
| 495 | self.time_pulled_forward += self.extra_days_to_work[self.end_date.date()] | 508 | self.time_pulled_forward += self.extra_days_to_work[self.end_date.date()] |
| 496 | 509 | ||
| 497 | self.time_to_work += self.time_pulled_forward | 510 | # self.time_to_work += self.time_pulled_forward |
| 498 | 511 | ||
| 499 | self.time_worked += api.get_billable_hours(self.start_date, self.now, rounding = config.get("WORKTIME", {}).get("rounding", True)) | 512 | self.time_worked += api.get_billable_hours(self.start_date, self.now) |
| 500 | 513 | ||
| 501 | def format_days(worktime, days, date_format=None): | 514 | def format_days(worktime, days, date_format=None): |
| 502 | if not date_format: | 515 | if not date_format: |
| @@ -518,7 +531,14 @@ def format_days(worktime, days, date_format=None): | |||
| 518 | return ', '.join(map(lambda group: ','.join(map(format_group, group)), groups)) | 531 | return ', '.join(map(lambda group: ','.join(map(format_group, group)), groups)) |
| 519 | 532 | ||
| 520 | 533 | ||
| 521 | def worktime(**args): | 534 | def tooltip_timedelta(td): |
| 535 | if td < timedelta(seconds = 0): | ||
| 536 | return "-" + tooltip_timedelta(-td) | ||
| 537 | mm, ss = divmod(td.total_seconds(), 60) | ||
| 538 | hh, mm = divmod(mm, 60) | ||
| 539 | return "%d:%02d:%02d" % (hh, mm, ss) | ||
| 540 | |||
| 541 | def worktime(pull_forward_cutoff, waybar, **args): | ||
| 522 | worktime = Worktime(**args) | 542 | worktime = Worktime(**args) |
| 523 | 543 | ||
| 524 | def format_worktime(worktime): | 544 | def format_worktime(worktime): |
| @@ -557,24 +577,41 @@ def worktime(**args): | |||
| 557 | return f"{indicator}{difference_string}" | 577 | return f"{indicator}{difference_string}" |
| 558 | else: | 578 | else: |
| 559 | difference_string = difference_string(total_minutes_difference * timedelta(minutes = 1)) | 579 | difference_string = difference_string(total_minutes_difference * timedelta(minutes = 1)) |
| 560 | if worktime.now_is_workday: | 580 | return difference_string |
| 561 | return difference_string | 581 | |
| 562 | else: | 582 | out_class = "running" if worktime.running_entry else "stopped" |
| 563 | return f"({difference_string})" | 583 | difference = worktime.time_to_work - worktime.time_worked |
| 564 | 584 | if worktime.running_entry and -min(timedelta(milliseconds=0), difference) > sum(worktime.pull_forward.values(), start=timedelta(milliseconds=0)) or not worktime.running_entry and max(timedelta(milliseconds=0), difference) > worktime.time_per_day(worktime.now.date()) and worktime.now_is_workday: | |
| 565 | if worktime.time_pulled_forward >= timedelta(minutes = 15): | 585 | out_class = "over" |
| 586 | pull_forward_sum = sum(worktime.pull_forward.values(), start=timedelta(milliseconds=0)) | ||
| 587 | if pull_forward_sum >= min(pull_forward_cutoff, timedelta(seconds = 1)): | ||
| 566 | worktime_no_pulled_forward = deepcopy(worktime) | 588 | worktime_no_pulled_forward = deepcopy(worktime) |
| 567 | worktime_no_pulled_forward.time_to_work -= worktime_no_pulled_forward.time_pulled_forward | 589 | # worktime_no_pulled_forward.time_to_work -= worktime_no_pulled_forward.time_pulled_forward |
| 568 | worktime_no_pulled_forward.time_pulled_forward = timedelta() | 590 | worktime_no_pulled_forward.time_pulled_forward = timedelta() |
| 591 | worktime_no_pulled_forward.pull_forward = dict() | ||
| 592 | worktime.time_to_work += pull_forward_sum | ||
| 569 | 593 | ||
| 570 | difference_string = format_worktime(worktime) | ||
| 571 | difference_string_no_pulled_forward = format_worktime(worktime_no_pulled_forward) | 594 | difference_string_no_pulled_forward = format_worktime(worktime_no_pulled_forward) |
| 572 | 595 | ||
| 573 | print(f"{difference_string_no_pulled_forward}…{difference_string}") | 596 | tooltip = tooltip_timedelta(worktime_no_pulled_forward.time_to_work - worktime_no_pulled_forward.time_worked) + "…" + tooltip_timedelta(difference + pull_forward_sum) |
| 597 | if pull_forward_sum >= pull_forward_cutoff: | ||
| 598 | out_text = f"{difference_string_no_pulled_forward}…{format_worktime(worktime)}" | ||
| 599 | else: | ||
| 600 | out_text = format_worktime(worktime) | ||
| 601 | else: | ||
| 602 | tooltip = tooltip_timedelta(difference) | ||
| 603 | out_text = format_worktime(worktime) | ||
| 604 | |||
| 605 | if waybar: | ||
| 606 | json.dump({"text": out_text, "class": out_class, "tooltip": tooltip}, stdout) | ||
| 574 | else: | 607 | else: |
| 575 | print(format_worktime(worktime)) | 608 | print(out_text) |
| 576 | 609 | ||
| 577 | def time_worked(now, **args): | 610 | def pull_forward(**args): |
| 611 | worktime = Worktime(**args) | ||
| 612 | print(tooltip_timedelta(sum(worktime.pull_forward.values(), start=timedelta(milliseconds=0)))) | ||
| 613 | |||
| 614 | def time_worked(now, waybar, **args): | ||
| 578 | then = now.replace(hour = 0, minute = 0, second = 0, microsecond = 0) | 615 | then = now.replace(hour = 0, minute = 0, second = 0, microsecond = 0) |
| 579 | if now.time() == time(): | 616 | if now.time() == time(): |
| 580 | now = now + timedelta(days = 1) | 617 | now = now + timedelta(days = 1) |
| @@ -584,33 +621,62 @@ def time_worked(now, **args): | |||
| 584 | 621 | ||
| 585 | worked = now.time_worked - then.time_worked | 622 | worked = now.time_worked - then.time_worked |
| 586 | 623 | ||
| 624 | out_text = None | ||
| 625 | out_class = "running" if now.running_entry else "stopped" | ||
| 626 | tooltip = tooltip_timedelta(worked) | ||
| 627 | target_time = max(then.time_per_day(then.now.date()), now.time_per_day(now.now.date())) if then.time_per_day(then.now.date()) and now.time_per_day(now.now.date()) else (then.time_per_day(then.now.date()) if then.time_per_day(then.now.date()) else now.time_per_day(now.now.date())) | ||
| 628 | difference = target_time - worked | ||
| 629 | difference_pull_forward = difference + now.time_pulled_forward | ||
| 630 | if now.running_entry and difference_pull_forward < timedelta(seconds=0): | ||
| 631 | out_class = "over" | ||
| 587 | if args['do_round']: | 632 | if args['do_round']: |
| 588 | total_minutes_difference = 5 * ceil(worked / timedelta(minutes = 5)) | 633 | total_minutes_difference = 5 * ceil(worked / timedelta(minutes = 5)) |
| 589 | (hours_difference, minutes_difference) = divmod(abs(total_minutes_difference), 60) | 634 | (hours_difference, minutes_difference) = divmod(abs(total_minutes_difference), 60) |
| 590 | sign = '' if total_minutes_difference >= 0 else '-' | 635 | sign = '' if total_minutes_difference >= 0 else '-' |
| 591 | |||
| 592 | difference_string = f"{sign}" | ||
| 593 | if hours_difference != 0: | ||
| 594 | difference_string += f"{hours_difference}h" | ||
| 595 | if hours_difference == 0 or minutes_difference != 0: | ||
| 596 | difference_string += f"{minutes_difference}m" | ||
| 597 | |||
| 598 | clockout_time = None | ||
| 599 | clockout_difference = None | ||
| 600 | if then.now_is_workday or now.now_is_workday: | ||
| 601 | target_time = max(then.time_per_day(then.now.date()), now.time_per_day(now.now.date())) if then.time_per_day(then.now.date()) and now.time_per_day(now.now.date()) else (then.time_per_day(then.now.date()) if then.time_per_day(then.now.date()) else now.time_per_day(now.now.date())); | ||
| 602 | difference = target_time - worked | ||
| 603 | clockout_difference = 5 * ceil(difference / timedelta(minutes = 5)) | ||
| 604 | clockout_time = now.now + difference | ||
| 605 | clockout_time += (5 - clockout_time.minute % 5) * timedelta(minutes = 1) | ||
| 606 | clockout_time = clockout_time.replace(second = 0, microsecond = 0) | ||
| 607 | 636 | ||
| 608 | if now.running_entry and clockout_time and clockout_difference >= 0: | 637 | difference_string = f"{sign}" |
| 609 | print(f"{difference_string}/{clockout_time:%H:%M}") | 638 | if hours_difference != 0: |
| 610 | else: | 639 | difference_string += f"{hours_difference}h" |
| 611 | print(difference_string) | 640 | if hours_difference == 0 or minutes_difference != 0: |
| 641 | difference_string += f"{minutes_difference}m" | ||
| 642 | |||
| 643 | def round_clockout_time(difference): | ||
| 644 | clockout_time = None | ||
| 645 | clockout_difference = None | ||
| 646 | exact_clockout_time = None | ||
| 647 | if then.now_is_workday or now.now_is_workday: | ||
| 648 | clockout_difference = 5 * ceil(difference / timedelta(minutes = 5)) | ||
| 649 | clockout_time = now.now + difference | ||
| 650 | exact_clockout_time = clockout_time | ||
| 651 | clockout_time += (5 - clockout_time.minute % 5) * timedelta(minutes = 1) | ||
| 652 | clockout_time = clockout_time.replace(second = 0, microsecond = 0) | ||
| 653 | |||
| 654 | return clockout_time, exact_clockout_time, clockout_difference | ||
| 655 | |||
| 656 | clockout_time, exact_clockout_time, clockout_difference = round_clockout_time(difference) | ||
| 657 | clockout_time_pull_forward, exact_clockout_time_pull_forward, clockout_difference_pull_forward = round_clockout_time(difference_pull_forward) | ||
| 658 | clockout_pull_forward_sum, exact_clockout_pull_forward_sum, _ = round_clockout_time(now.time_to_work - now.time_worked + sum(now.pull_forward.values(), start=timedelta(milliseconds=0))) | ||
| 659 | |||
| 660 | if now.running_entry and clockout_time and (clockout_difference >= 0 or clockout_difference_pull_forward >= 0): | ||
| 661 | out_text = f"{difference_string}/{clockout_time:%H:%M}" | ||
| 662 | tooltip = f"{tooltip_timedelta(worked)}/{exact_clockout_time:%H:%M:%S}" | ||
| 663 | |||
| 664 | if clockout_pull_forward_sum >= clockout_time_pull_forward and clockout_time_pull_forward != clockout_time: | ||
| 665 | out_text += f"…{clockout_time_pull_forward:%H:%M}" | ||
| 666 | if exact_clockout_pull_forward_sum >= exact_clockout_time_pull_forward and exact_clockout_time_pull_forward != exact_clockout_time: | ||
| 667 | tooltip += f"…{exact_clockout_time_pull_forward:%H:%M:%S}" | ||
| 668 | else: | ||
| 669 | out_text = difference_string | ||
| 612 | else: | 670 | else: |
| 613 | print(worked) | 671 | out_text = str(worked) |
| 672 | |||
| 673 | if not now.now_is_workday: | ||
| 674 | out_text = f'({out_text})' | ||
| 675 | |||
| 676 | if waybar: | ||
| 677 | json.dump({"text": out_text, "class": out_class, "tooltip": tooltip}, stdout) | ||
| 678 | else: | ||
| 679 | print(out_text) | ||
| 614 | 680 | ||
| 615 | def diff(now, **args): | 681 | def diff(now, **args): |
| 616 | now = now.replace(hour = 0, minute = 0, second = 0, microsecond = 0) | 682 | now = now.replace(hour = 0, minute = 0, second = 0, microsecond = 0) |
| @@ -798,18 +864,54 @@ def classification(classification_name, table, table_format, **args): | |||
| 798 | def main(): | 864 | def main(): |
| 799 | def isotime(s): | 865 | def isotime(s): |
| 800 | return datetime.fromisoformat(s).replace(tzinfo=tzlocal()) | 866 | return datetime.fromisoformat(s).replace(tzinfo=tzlocal()) |
| 867 | def duration_minutes(s): | ||
| 868 | return timedelta(minutes = float(s)) | ||
| 869 | |||
| 870 | def set_default_subparser(self, name, args=None, positional_args=0): | ||
| 871 | """default subparser selection. Call after setup, just before parse_args() | ||
| 872 | name: is the name of the subparser to call by default | ||
| 873 | args: if set is the argument list handed to parse_args() | ||
| 874 | |||
| 875 | , tested with 2.7, 3.2, 3.3, 3.4 | ||
| 876 | it works with 2.6 assuming argparse is installed | ||
| 877 | """ | ||
| 878 | subparser_found = False | ||
| 879 | for arg in sys.argv[1:]: | ||
| 880 | if arg in ['-h', '--help']: # global help if no subparser | ||
| 881 | break | ||
| 882 | else: | ||
| 883 | for x in self._subparsers._actions: | ||
| 884 | if not isinstance(x, argparse._SubParsersAction): | ||
| 885 | continue | ||
| 886 | for sp_name in x._name_parser_map.keys(): | ||
| 887 | if sp_name in sys.argv[1:]: | ||
| 888 | subparser_found = True | ||
| 889 | if not subparser_found: | ||
| 890 | # insert default in last position before global positional | ||
| 891 | # arguments, this implies no global options are specified after | ||
| 892 | # first positional argument | ||
| 893 | if args is None: | ||
| 894 | sys.argv.insert(len(sys.argv) - positional_args, name) | ||
| 895 | else: | ||
| 896 | args.insert(len(args) - positional_args, name) | ||
| 897 | |||
| 898 | argparse.ArgumentParser.set_default_subparser = set_default_subparser | ||
| 801 | 899 | ||
| 802 | config = Worktime.config() | 900 | config = Worktime.config() |
| 803 | 901 | ||
| 804 | parser = argparse.ArgumentParser(prog = "worktime", description = 'Track worktime using toggl API') | 902 | parser = argparse.ArgumentParser(prog = "worktime", description = 'Track worktime using Kimai API') |
| 805 | parser.add_argument('--time', dest = 'now', metavar = 'TIME', type = isotime, help = 'Time to calculate status for (default: current time)', default = datetime.now(tzlocal())) | 903 | parser.add_argument('--time', dest = 'now', metavar = 'TIME', type = isotime, help = 'Time to calculate status for (default: current time)', default = datetime.now(tzlocal())) |
| 806 | parser.add_argument('--start', dest = 'start_datetime', metavar = 'TIME', type = isotime, help = 'Time to calculate status from (default: None)', default = None) | 904 | parser.add_argument('--start', dest = 'start_datetime', metavar = 'TIME', type = isotime, help = 'Time to calculate status from (default: None)', default = None) |
| 807 | parser.add_argument('--no-running', dest = 'include_running', action = 'store_false') | 905 | parser.add_argument('--no-running', dest = 'include_running', action = 'store_false') |
| 808 | parser.add_argument('--no-force-day-to-work', dest = 'force_day_to_work', action = 'store_false') | 906 | parser.add_argument('--no-force-day-to-work', dest = 'force_day_to_work', action = 'store_false') |
| 809 | subparsers = parser.add_subparsers(help = 'Subcommands') | 907 | subparsers = parser.add_subparsers(help = 'Subcommands') |
| 810 | parser.set_defaults(cmd = worktime) | 908 | worktime_parser = subparsers.add_parser('time_worked', aliases = ['time', 'worked']) |
| 811 | time_worked_parser = subparsers.add_parser('time_worked', aliases = ['time', 'worked', 'today']) | 909 | worktime_parser.add_argument('--pull-forward-cutoff', dest = 'pull_forward_cutoff', metavar = 'MINUTES', type = duration_minutes, default = timedelta(minutes = 15)) |
| 910 | worktime_parser.add_argument('--waybar', action='store_true') | ||
| 911 | worktime_parser.set_defaults(cmd = worktime) | ||
| 912 | time_worked_parser = subparsers.add_parser('today') | ||
| 812 | time_worked_parser.add_argument('--no-round', dest = 'do_round', action = 'store_false') | 913 | time_worked_parser.add_argument('--no-round', dest = 'do_round', action = 'store_false') |
| 914 | time_worked_parser.add_argument('--waybar', action='store_true') | ||
| 813 | time_worked_parser.set_defaults(cmd = time_worked) | 915 | time_worked_parser.set_defaults(cmd = time_worked) |
| 814 | diff_parser = subparsers.add_parser('diff') | 916 | diff_parser = subparsers.add_parser('diff') |
| 815 | diff_parser.set_defaults(cmd = diff) | 917 | diff_parser.set_defaults(cmd = diff) |
| @@ -827,9 +929,146 @@ def main(): | |||
| 827 | classification_parser.add_argument('--table', action = 'store_true') | 929 | classification_parser.add_argument('--table', action = 'store_true') |
| 828 | classification_parser.add_argument('--table-format', dest='table_format', type=str, default='fancy_grid') | 930 | classification_parser.add_argument('--table-format', dest='table_format', type=str, default='fancy_grid') |
| 829 | classification_parser.set_defaults(cmd = partial(classification, classification_name=classification_name)) | 931 | classification_parser.set_defaults(cmd = partial(classification, classification_name=classification_name)) |
| 932 | pull_forward_parser = subparsers.add_parser('pull-forward') | ||
| 933 | pull_forward_parser.set_defaults(cmd = pull_forward) | ||
| 934 | parser.set_default_subparser('time_worked') | ||
| 830 | args = parser.parse_args() | 935 | args = parser.parse_args() |
| 831 | 936 | ||
| 832 | args.cmd(**vars(args)) | 937 | args.cmd(**vars(args)) |
| 833 | 938 | ||
| 939 | async def ui_update_options(api, cache_path): | ||
| 940 | options = set() | ||
| 941 | sort_order = dict() | ||
| 942 | entry_iter = enumerate(api.get_recent_entries()) | ||
| 943 | loop = asyncio.get_event_loop() | ||
| 944 | start = clock_gettime_ns(CLOCK_MONOTONIC) | ||
| 945 | while item := await loop.run_in_executor(None, next, entry_iter): | ||
| 946 | ix, entry = item | ||
| 947 | if len(options) >= 20 or ix >= 1000: | ||
| 948 | break | ||
| 949 | elif len(options) >= 3: | ||
| 950 | now = clock_gettime_ns(CLOCK_MONOTONIC) | ||
| 951 | if now - start >= 4000000000: | ||
| 952 | break | ||
| 953 | |||
| 954 | option = frozendict({ | ||
| 955 | 'tags': frozenset(entry['tags']), | ||
| 956 | 'activity': frozendict({'id': entry['activity']['id'], 'name': entry['activity']['name']}), | ||
| 957 | 'project': frozendict({'id': entry['project']['id'], 'customer': entry['project']['customer']['name'], 'name': entry['project']['name']}), | ||
| 958 | 'description': entry['description'] if entry['description'] else None, | ||
| 959 | 'billable': entry['billable'], | ||
| 960 | }) | ||
| 961 | sort_value = isoparse(entry['begin']) | ||
| 962 | if option in sort_order: | ||
| 963 | sort_value = max(sort_value, sort_order[option]) | ||
| 964 | sort_order[option] = sort_value | ||
| 965 | options.add(option) | ||
| 966 | |||
| 967 | options = list(sorted(options, key = lambda o: sort_order[o], reverse = True)) | ||
| 968 | |||
| 969 | with AtomicWriter(cache_path, overwrite=True) as ch: | ||
| 970 | ch.write_text(jsonpickle.encode(options)) | ||
| 971 | |||
| 972 | return options | ||
| 973 | |||
| 974 | def ui_render_option(option): | ||
| 975 | res = '' | ||
| 976 | if option['description']: | ||
| 977 | res += '„{}“, '.format(option['description']) | ||
| 978 | res += option['activity']['name'] + ', ' | ||
| 979 | res += option['project']['name'] | ||
| 980 | if option['project']['customer'] not in option['project']['name']: | ||
| 981 | res += ' ({})'.format(option['project']['customer']) | ||
| 982 | if option['tags']: | ||
| 983 | res += ', {}'.format(' '.join(map(lambda t: '#{}'.format(t), option['tags']))) | ||
| 984 | if not option['billable']: | ||
| 985 | res += ', not billable' | ||
| 986 | return res | ||
| 987 | |||
| 988 | async def ui_main(): | ||
| 989 | cache_path = Path(BaseDirectory.save_cache_path('worktime-ui')) / 'options.json' | ||
| 990 | options = None | ||
| 991 | try: | ||
| 992 | with cache_path.open('r', encoding='utf-8') as ch: | ||
| 993 | options = jsonpickle.decode(ch.read()) | ||
| 994 | except FileNotFoundError: | ||
| 995 | pass | ||
| 996 | |||
| 997 | config = Worktime.config() | ||
| 998 | api = KimaiAPI( | ||
| 999 | base_url=config.get("KIMAI", {}).get("BaseUrl", None), | ||
| 1000 | api_token=config.get("KIMAI", {}).get("ApiToken", None), | ||
| 1001 | clients=config.get("KIMAI", {}).get("Clients", None) | ||
| 1002 | ) | ||
| 1003 | running_entry = api.get_running_entry() | ||
| 1004 | |||
| 1005 | async with asyncio.TaskGroup() as tg: | ||
| 1006 | update_options = tg.create_task(ui_update_options(api, cache_path)) | ||
| 1007 | if not options: | ||
| 1008 | options = await update_options | ||
| 1009 | |||
| 1010 | read_fd, write_fd = os.pipe() | ||
| 1011 | w_pipe = open(write_fd, 'wb', 0) | ||
| 1012 | loop = asyncio.get_event_loop() | ||
| 1013 | w_transport, _ = await loop.connect_write_pipe( | ||
| 1014 | asyncio.Protocol, | ||
| 1015 | w_pipe, | ||
| 1016 | ) | ||
| 1017 | r_pipe = open(read_fd, 'rb', 0) | ||
| 1018 | |||
| 1019 | proc = await asyncio.create_subprocess_exec( | ||
| 1020 | "fuzzel", "--dmenu", "--index", "--width=60", | ||
| 1021 | stdout = asyncio.subprocess.PIPE, | ||
| 1022 | stdin = r_pipe, | ||
| 1023 | ) | ||
| 1024 | |||
| 1025 | with closing(w_transport) as t: | ||
| 1026 | if running_entry: | ||
| 1027 | t.write(b'Stop running timesheet\n') | ||
| 1028 | for option in options: | ||
| 1029 | t.write(ui_render_option(option).encode('utf-8') + b'\n') | ||
| 1030 | |||
| 1031 | stdout, _ = await proc.communicate() | ||
| 1032 | if proc.returncode != 0: | ||
| 1033 | return | ||
| 1034 | fuzzel_out = int(stdout.decode('utf-8')) | ||
| 1035 | if fuzzel_out < 0 or fuzzel_out >= len(options): | ||
| 1036 | return | ||
| 1037 | elif running_entry and fuzzel_out == 0: | ||
| 1038 | api.stop_clock(running_entry['id']) | ||
| 1039 | await notify.Server('worktime').Notify("Stopped running timesheet").set_timeout(65000).show() | ||
| 1040 | else: | ||
| 1041 | if running_entry: | ||
| 1042 | fuzzel_out -= 1 | ||
| 1043 | option = options[fuzzel_out] | ||
| 1044 | api.start_clock( | ||
| 1045 | project_id = option['project']['id'], | ||
| 1046 | activity_id = option['activity']['id'], | ||
| 1047 | description = option['description'], | ||
| 1048 | tags = option['tags'], | ||
| 1049 | billable = option['billable'], | ||
| 1050 | ) | ||
| 1051 | await notify.Server('worktime').Notify("Timesheet started…").set_timeout(65000).show() | ||
| 1052 | |||
| 1053 | |||
| 1054 | def ui(): | ||
| 1055 | asyncio.run(ui_main()) | ||
| 1056 | |||
| 1057 | async def stop_main(): | ||
| 1058 | config = Worktime.config() | ||
| 1059 | api = KimaiAPI( | ||
| 1060 | base_url=config.get("KIMAI", {}).get("BaseUrl", None), | ||
| 1061 | api_token=config.get("KIMAI", {}).get("ApiToken", None), | ||
| 1062 | clients=config.get("KIMAI", {}).get("Clients", None) | ||
| 1063 | ) | ||
| 1064 | if running_entry := api.get_running_entry(): | ||
| 1065 | api.stop_clock(running_entry['id']) | ||
| 1066 | await notify.Server('worktime').Notify("Stopped running timesheet").set_timeout(65000).show() | ||
| 1067 | else: | ||
| 1068 | await notify.Server('worktime').Notify("No timesheet currently running").set_timeout(65000).show() | ||
| 1069 | |||
| 1070 | def stop(): | ||
| 1071 | asyncio.run(stop_main()) | ||
| 1072 | |||
| 834 | if __name__ == "__main__": | 1073 | if __name__ == "__main__": |
| 835 | sys.exit(main()) | 1074 | sys.exit(main()) |
diff --git a/overlays/wttrbar/default.nix b/overlays/wttrbar/default.nix deleted file mode 100644 index 876fa699..00000000 --- a/overlays/wttrbar/default.nix +++ /dev/null | |||
| @@ -1,7 +0,0 @@ | |||
| 1 | { prev, ... }: { | ||
| 2 | wttrbar = prev.wttrbar.overrideAttrs (oldAttrs: { | ||
| 3 | patches = (oldAttrs.patches or []) ++ [ | ||
| 4 | ./icons.patch | ||
| 5 | ]; | ||
| 6 | }); | ||
| 7 | } | ||
diff --git a/overlays/wttrbar/icons.patch b/overlays/wttrbar/icons.patch deleted file mode 100644 index e7e721c8..00000000 --- a/overlays/wttrbar/icons.patch +++ /dev/null | |||
| @@ -1,154 +0,0 @@ | |||
| 1 | diff --git a/src/constants.rs b/src/constants.rs | ||
| 2 | index 81b1926..3619d8f 100644 | ||
| 3 | --- a/src/constants.rs | ||
| 4 | +++ b/src/constants.rs | ||
| 5 | @@ -1,64 +1,52 @@ | ||
| 6 | pub const WEATHER_CODES: &[(i32, &str)] = &[ | ||
| 7 | - (113, "☀️"), | ||
| 8 | - (116, "🌤️"), | ||
| 9 | - (119, "☁️"), | ||
| 10 | - (122, "🌥️"), | ||
| 11 | - (143, "🌫️"), | ||
| 12 | - (176, "🌦️"), | ||
| 13 | - (179, "🌧️"), | ||
| 14 | - (182, "🌨️"), | ||
| 15 | - (185, "🌨️"), | ||
| 16 | - (200, "🌩️"), | ||
| 17 | - (227, "❄️"), | ||
| 18 | - (230, "❄️"), | ||
| 19 | - (248, "🌫️"), | ||
| 20 | - (260, "🌫️"), | ||
| 21 | - (263, "🌧️"), | ||
| 22 | - (266, "🌧️"), | ||
| 23 | - (281, "🌦️"), | ||
| 24 | - (284, "🌦️"), | ||
| 25 | - (293, "🌧️"), | ||
| 26 | - (296, "🌧️"), | ||
| 27 | - (299, "🌧️"), | ||
| 28 | - (302, "🌧️"), | ||
| 29 | - (305, "🌧️"), | ||
| 30 | - (308, "🌧️"), | ||
| 31 | - (311, "🌧️"), | ||
| 32 | - (314, "🌧️"), | ||
| 33 | - (317, "🌧️"), | ||
| 34 | - (320, "🌨️"), | ||
| 35 | - (323, "🌨️"), | ||
| 36 | - (326, "🌨️"), | ||
| 37 | - (329, "🌨️"), | ||
| 38 | - (332, "🌨️"), | ||
| 39 | - (335, "🌨️"), | ||
| 40 | - (338, "🌨️"), | ||
| 41 | - (350, "🌨️"), | ||
| 42 | - (353, "🌧️"), | ||
| 43 | - (356, "🌧️"), | ||
| 44 | - (359, "🌧️"), | ||
| 45 | - (362, "🌨️"), | ||
| 46 | - (365, "🌨️"), | ||
| 47 | - (368, "🌨️"), | ||
| 48 | - (371, "🌨️"), | ||
| 49 | - (374, "🌨️"), | ||
| 50 | - (377, "🌨️"), | ||
| 51 | - (386, "🌩️"), | ||
| 52 | - (389, "🌨️"), | ||
| 53 | - (392, "🌨️"), | ||
| 54 | - (395, "🌨️"), | ||
| 55 | - (398, "🌨️"), | ||
| 56 | - (401, "🌨️"), | ||
| 57 | - (404, "🌨️"), | ||
| 58 | - (407, "🌨️"), | ||
| 59 | - (410, "🌨️"), | ||
| 60 | - (413, "🌨️"), | ||
| 61 | - (416, "🌨️"), | ||
| 62 | - (419, "🌨️"), | ||
| 63 | - (422, "🌨️"), | ||
| 64 | - (425, "🌨️"), | ||
| 65 | - (428, "🌨️"), | ||
| 66 | - (431, "🌨️"), | ||
| 67 | + (113, "<span font=\"Symbols Nerd Font Mono\"></span>"), | ||
| 68 | + (116, "<span font=\"Symbols Nerd Font Mono\"></span>"), | ||
| 69 | + (119, "<span font=\"Symbols Nerd Font Mono\"></span>"), | ||
| 70 | + (122, "<span font=\"Symbols Nerd Font Mono\"></span>"), | ||
| 71 | + (143, "<span font=\"Symbols Nerd Font Mono\"></span>"), | ||
| 72 | + (176, "<span font=\"Symbols Nerd Font Mono\"></span>"), | ||
| 73 | + (179, "<span font=\"Symbols Nerd Font Mono\"></span>"), | ||
| 74 | + (182, "<span font=\"Symbols Nerd Font Mono\"></span>"), | ||
| 75 | + (185, "<span font=\"Symbols Nerd Font Mono\"></span>"), | ||
| 76 | + (200, "<span font=\"Symbols Nerd Font Mono\"></span>"), | ||
| 77 | + (227, "<span font=\"Symbols Nerd Font Mono\"></span>"), | ||
| 78 | + (230, "<span font=\"Symbols Nerd Font Mono\"></span>"), | ||
| 79 | + (248, "<span font=\"Symbols Nerd Font Mono\"></span>"), | ||
| 80 | + (260, "<span font=\"Symbols Nerd Font Mono\"></span>"), | ||
| 81 | + (263, "<span font=\"Symbols Nerd Font Mono\"></span>"), | ||
| 82 | + (266, "<span font=\"Symbols Nerd Font Mono\"></span>"), | ||
| 83 | + (281, "<span font=\"Symbols Nerd Font Mono\"></span>"), | ||
| 84 | + (284, "<span font=\"Symbols Nerd Font Mono\"></span>"), | ||
| 85 | + (293, "<span font=\"Symbols Nerd Font Mono\"></span>"), | ||
| 86 | + (296, "<span font=\"Symbols Nerd Font Mono\"></span>"), | ||
| 87 | + (299, "<span font=\"Symbols Nerd Font Mono\"></span>"), | ||
| 88 | + (302, "<span font=\"Symbols Nerd Font Mono\"></span>"), | ||
| 89 | + (305, "<span font=\"Symbols Nerd Font Mono\"></span>"), | ||
| 90 | + (308, "<span font=\"Symbols Nerd Font Mono\"></span>"), | ||
| 91 | + (311, "<span font=\"Symbols Nerd Font Mono\"></span>"), | ||
| 92 | + (314, "<span font=\"Symbols Nerd Font Mono\"></span>"), | ||
| 93 | + (317, "<span font=\"Symbols Nerd Font Mono\"></span>"), | ||
| 94 | + (320, "<span font=\"Symbols Nerd Font Mono\"></span>"), | ||
| 95 | + (323, "<span font=\"Symbols Nerd Font Mono\"></span>"), | ||
| 96 | + (326, "<span font=\"Symbols Nerd Font Mono\"></span>"), | ||
| 97 | + (329, "<span font=\"Symbols Nerd Font Mono\"></span>"), | ||
| 98 | + (332, "<span font=\"Symbols Nerd Font Mono\"></span>"), | ||
| 99 | + (335, "<span font=\"Symbols Nerd Font Mono\"></span>"), | ||
| 100 | + (338, "<span font=\"Symbols Nerd Font Mono\"></span>"), | ||
| 101 | + (350, "<span font=\"Symbols Nerd Font Mono\"></span>"), | ||
| 102 | + (353, "<span font=\"Symbols Nerd Font Mono\"></span>"), | ||
| 103 | + (356, "<span font=\"Symbols Nerd Font Mono\"></span>"), | ||
| 104 | + (359, "<span font=\"Symbols Nerd Font Mono\"></span>"), | ||
| 105 | + (362, "<span font=\"Symbols Nerd Font Mono\"></span>"), | ||
| 106 | + (365, "<span font=\"Symbols Nerd Font Mono\"></span>"), | ||
| 107 | + (368, "<span font=\"Symbols Nerd Font Mono\"></span>"), | ||
| 108 | + (371, "<span font=\"Symbols Nerd Font Mono\"></span>"), | ||
| 109 | + (374, "<span font=\"Symbols Nerd Font Mono\"></span>"), | ||
| 110 | + (377, "<span font=\"Symbols Nerd Font Mono\"></span>"), | ||
| 111 | + (386, "<span font=\"Symbols Nerd Font Mono\"></span>"), | ||
| 112 | + (389, "<span font=\"Symbols Nerd Font Mono\"></span>"), | ||
| 113 | + (392, "<span font=\"Symbols Nerd Font Mono\"></span>"), | ||
| 114 | + (395, "<span font=\"Symbols Nerd Font Mono\"></span>"), | ||
| 115 | ]; | ||
| 116 | |||
| 117 | pub const ICON_PLACEHOLDER: &str = "{ICON}"; | ||
| 118 | diff --git a/src/main.rs b/src/main.rs | ||
| 119 | index 6ac4654..1b84207 100644 | ||
| 120 | --- a/src/main.rs | ||
| 121 | +++ b/src/main.rs | ||
| 122 | @@ -175,20 +175,20 @@ fn main() { | ||
| 123 | |||
| 124 | if args.fahrenheit { | ||
| 125 | tooltip += &format!( | ||
| 126 | - "⬆️ {}° ⬇️ {}° ", | ||
| 127 | + "<span font=\"Symbols Nerd Font Mono\"></span> {}° <span font=\"Symbols Nerd Font Mono\"></span> {}° ", | ||
| 128 | day["maxtempF"].as_str().unwrap(), | ||
| 129 | day["mintempF"].as_str().unwrap(), | ||
| 130 | ); | ||
| 131 | } else { | ||
| 132 | tooltip += &format!( | ||
| 133 | - "⬆️ {}° ⬇️ {}° ", | ||
| 134 | + "<span font=\"Symbols Nerd Font Mono\"></span> {}° <span font=\"Symbols Nerd Font Mono\"></span> {}° ", | ||
| 135 | day["maxtempC"].as_str().unwrap(), | ||
| 136 | day["mintempC"].as_str().unwrap(), | ||
| 137 | ); | ||
| 138 | }; | ||
| 139 | |||
| 140 | tooltip += &format!( | ||
| 141 | - "🌅 {} 🌇 {}\n", | ||
| 142 | + "<span font=\"Symbols Nerd Font Mono\"></span> {} <span font=\"Symbols Nerd Font Mono\"></span> {}\n", | ||
| 143 | format_ampm_time(day, "sunrise", args.ampm), | ||
| 144 | format_ampm_time(day, "sunset", args.ampm), | ||
| 145 | ); | ||
| 146 | @@ -207,7 +207,7 @@ fn main() { | ||
| 147 | } | ||
| 148 | |||
| 149 | let mut tooltip_line = format!( | ||
| 150 | - "{} {} {} {}", | ||
| 151 | + "{} {}{} {}", | ||
| 152 | format_time(hour["time"].as_str().unwrap(), args.ampm), | ||
| 153 | WEATHER_CODES | ||
| 154 | .iter() | ||
diff --git a/overlays/yt-dlp.nix b/overlays/yt-dlp.nix index 94ab1fdd..9a54a32b 100644 --- a/overlays/yt-dlp.nix +++ b/overlays/yt-dlp.nix | |||
| @@ -1,5 +1,7 @@ | |||
| 1 | { prev, sources, ... }: { | 1 | { prev, sources, ... }: { |
| 2 | yt-dlp = prev.yt-dlp.overrideAttrs (oldAttrs: { | 2 | yt-dlp = prev.yt-dlp.overrideAttrs (oldAttrs: { |
| 3 | inherit (sources.yt-dlp) pname version src; | 3 | inherit (sources.yt-dlp) pname version src; |
| 4 | |||
| 5 | postPatch = ""; | ||
| 4 | }); | 6 | }); |
| 5 | } | 7 | } |
diff --git a/overlays/zte-prometheus-exporter/default.nix b/overlays/zte-prometheus-exporter/default.nix index 2188e7b3..6295567d 100644 --- a/overlays/zte-prometheus-exporter/default.nix +++ b/overlays/zte-prometheus-exporter/default.nix | |||
| @@ -1,18 +1,17 @@ | |||
| 1 | { final, prev, ... }: | 1 | { final, prev, ... }: |
| 2 | let | 2 | let |
| 3 | packageOverrides = final.callPackage ./python-packages.nix {}; | 3 | packageOverrides = final.callPackage ./python-packages.nix {}; |
| 4 | inpPython = final.python310.override { inherit packageOverrides; }; | 4 | inpPython = final.python3.override { inherit packageOverrides; }; |
| 5 | python = inpPython.withPackages (ps: with ps; [pytimeparse requests]); | ||
| 5 | in { | 6 | in { |
| 6 | zte-prometheus-exporter = prev.stdenv.mkDerivation rec { | 7 | zte-prometheus-exporter = prev.stdenv.mkDerivation rec { |
| 7 | name = "zte-prometheus-exporter"; | 8 | name = "zte-prometheus-exporter"; |
| 8 | src = ./zte-prometheus-exporter.py; | 9 | src = prev.replaceVars ./zte-prometheus-exporter.py { inherit python; }; |
| 9 | 10 | ||
| 10 | phases = [ "buildPhase" "checkPhase" "installPhase" ]; | 11 | phases = [ "unpackPhase" "checkPhase" "installPhase" ]; |
| 11 | 12 | ||
| 12 | python = inpPython.withPackages (ps: with ps; [pytimeparse requests]); | 13 | unpackPhase = '' |
| 13 | 14 | cp $src zte-prometheus-exporter | |
| 14 | buildPhase = '' | ||
| 15 | substituteAll $src zte-prometheus-exporter | ||
| 16 | ''; | 15 | ''; |
| 17 | 16 | ||
| 18 | doCheck = true; | 17 | doCheck = true; |
diff --git a/overlays/zte-prometheus-exporter/zte-prometheus-exporter.py b/overlays/zte-prometheus-exporter/zte-prometheus-exporter.py index fc719a96..bc8326ff 100644 --- a/overlays/zte-prometheus-exporter/zte-prometheus-exporter.py +++ b/overlays/zte-prometheus-exporter/zte-prometheus-exporter.py | |||
| @@ -54,13 +54,13 @@ class ZTEMetrics: | |||
| 54 | cls._instance.password = environ.get('ZTE_PASSWORD') | 54 | cls._instance.password = environ.get('ZTE_PASSWORD') |
| 55 | cls._instance.attrs = None | 55 | cls._instance.attrs = None |
| 56 | return cls._instance | 56 | return cls._instance |
| 57 | 57 | ||
| 58 | 58 | ||
| 59 | def __init__(self): | 59 | def __init__(self): |
| 60 | raise RuntimeError('Call instance() instead') | 60 | raise RuntimeError('Call instance() instead') |
| 61 | 61 | ||
| 62 | _error_pattern = re.compile('^IF_ERROR(PARAM|TYPE|STR|ID)$') | 62 | _error_pattern = re.compile(r'^IF_ERROR(PARAM|TYPE|STR|ID)$') |
| 63 | _obj_pattern = re.compile('^(?:OBJ_(.+)_ID)|(?:ID_(WAN_COMFIG))$') | 63 | _obj_pattern = re.compile(r'^(?:OBJ_(.+)_ID)|(?:ID_(WAN_COMFIG))$') |
| 64 | def update(self): | 64 | def update(self): |
| 65 | attrs = dict() | 65 | attrs = dict() |
| 66 | 66 | ||
| @@ -106,6 +106,8 @@ class ZTEMetrics: | |||
| 106 | value = child.text | 106 | value = child.text |
| 107 | case _: | 107 | case _: |
| 108 | pass | 108 | pass |
| 109 | if value == '0,0': | ||
| 110 | value = '0' | ||
| 109 | if not name is None and not value is None: | 111 | if not name is None and not value is None: |
| 110 | instance_dict[name] = value | 112 | instance_dict[name] = value |
| 111 | name = None | 113 | name = None |
| @@ -120,8 +122,8 @@ class ZTEMetrics: | |||
| 120 | def json_text(self): | 122 | def json_text(self): |
| 121 | return json.dumps(self.attrs) | 123 | return json.dumps(self.attrs) |
| 122 | 124 | ||
| 123 | _link_pattern = re.compile('^IGD\.WD1\.LINE([0-9]+)$') | 125 | _link_pattern = re.compile(r'^IGD\.WD1\.LINE([0-9]+)$') |
| 124 | _eth_pattern = re.compile('^IGD\.LD1\.ETH([0-9]+)$') | 126 | _eth_pattern = re.compile(r'^IGD\.LD1\.ETH([0-9]+)$') |
| 125 | def prometheus(self): | 127 | def prometheus(self): |
| 126 | metrics = '' | 128 | metrics = '' |
| 127 | 129 | ||
| @@ -133,34 +135,41 @@ class ZTEMetrics: | |||
| 133 | link_match = self._link_pattern.match(link) | 135 | link_match = self._link_pattern.match(link) |
| 134 | link_number = link_match.group(1) | 136 | link_number = link_match.group(1) |
| 135 | 137 | ||
| 136 | if 'crc_errors_count' not in link_metrics: | 138 | link_is_up = self.attrs['DSLINTERFACE'][link]['Status'] == 'Up' |
| 137 | link_metrics['crc_errors_count'] = {'type': 'counter', 'metrics': []} | 139 | |
| 138 | link_metrics['crc_errors_count']['metrics'] += [({"direction": "up", "link": link_number}, int(self.attrs['DSLINTERFACE'][link]['UpCrc_errors']))] | 140 | if link_is_up: |
| 139 | link_metrics['crc_errors_count']['metrics'] += [({"direction": "down", "link": link_number}, int(self.attrs['DSLINTERFACE'][link]['DownCrc_errors']))] | 141 | if 'crc_errors_count' not in link_metrics: |
| 142 | link_metrics['crc_errors_count'] = {'type': 'counter', 'metrics': []} | ||
| 143 | link_metrics['crc_errors_count']['metrics'] += [({"direction": "up", "link": link_number}, int(self.attrs['DSLINTERFACE'][link]['UpCrc_errors']))] | ||
| 144 | link_metrics['crc_errors_count']['metrics'] += [({"direction": "down", "link": link_number}, int(self.attrs['DSLINTERFACE'][link]['DownCrc_errors']))] | ||
| 140 | 145 | ||
| 141 | if 'noise_margin_db' not in link_metrics: | 146 | if 'noise_margin_db' not in link_metrics: |
| 142 | link_metrics['noise_margin_db'] = {'type': 'gauge', 'metrics': []} | 147 | link_metrics['noise_margin_db'] = {'type': 'gauge', 'metrics': []} |
| 143 | link_metrics['noise_margin_db']['metrics'] += [({"direction": "up", "link": link_number}, int(self.attrs['DSLINTERFACE'][link]['Upstream_noise_margin']))] | 148 | link_metrics['noise_margin_db']['metrics'] += [({"direction": "up", "link": link_number}, int(self.attrs['DSLINTERFACE'][link]['Upstream_noise_margin']) / 10)] |
| 144 | link_metrics['noise_margin_db']['metrics'] += [({"direction": "down", "link": link_number}, int(self.attrs['DSLINTERFACE'][link]['Downstream_noise_margin']))] | 149 | link_metrics['noise_margin_db']['metrics'] += [({"direction": "down", "link": link_number}, int(self.attrs['DSLINTERFACE'][link]['Downstream_noise_margin']) / 10)] |
| 145 | 150 | ||
| 146 | if 'attenuation_db' not in link_metrics: | 151 | if 'attenuation_db' not in link_metrics: |
| 147 | link_metrics['attenuation_db'] = {'type': 'gauge', 'metrics': []} | 152 | link_metrics['attenuation_db'] = {'type': 'gauge', 'metrics': []} |
| 148 | link_metrics['attenuation_db']['metrics'] += [({"direction": "up", "link": link_number}, int(self.attrs['DSLINTERFACE'][link]['Upstream_attenuation']))] | 153 | link_metrics['attenuation_db']['metrics'] += [({"direction": "up", "link": link_number}, int(self.attrs['DSLINTERFACE'][link]['Upstream_attenuation']) / 10)] |
| 149 | link_metrics['attenuation_db']['metrics'] += [({"direction": "down", "link": link_number}, int(self.attrs['DSLINTERFACE'][link]['Downstream_attenuation']))] | 154 | link_metrics['attenuation_db']['metrics'] += [({"direction": "down", "link": link_number}, int(self.attrs['DSLINTERFACE'][link]['Downstream_attenuation']) / 10)] |
| 150 | 155 | ||
| 151 | if 'max_rate_kbps' not in link_metrics: | 156 | if 'max_rate_kbps' not in link_metrics: |
| 152 | link_metrics['max_rate_kbps'] = {'type': 'gauge', 'metrics': []} | 157 | link_metrics['max_rate_kbps'] = {'type': 'gauge', 'metrics': []} |
| 153 | link_metrics['max_rate_kbps']['metrics'] += [({"direction": "up", "link": link_number}, int(self.attrs['DSLINTERFACE'][link]['Upstream_max_rate']))] | 158 | link_metrics['max_rate_kbps']['metrics'] += [({"direction": "up", "link": link_number}, int(self.attrs['DSLINTERFACE'][link]['Upstream_max_rate']))] |
| 154 | link_metrics['max_rate_kbps']['metrics'] += [({"direction": "down", "link": link_number}, int(self.attrs['DSLINTERFACE'][link]['Downstream_max_rate']))] | 159 | link_metrics['max_rate_kbps']['metrics'] += [({"direction": "down", "link": link_number}, int(self.attrs['DSLINTERFACE'][link]['Downstream_max_rate']))] |
| 155 | 160 | ||
| 156 | if 'current_rate_kbps' not in link_metrics: | 161 | if 'current_rate_kbps' not in link_metrics: |
| 157 | link_metrics['current_rate_kbps'] = {'type': 'gauge', 'metrics': []} | 162 | link_metrics['current_rate_kbps'] = {'type': 'gauge', 'metrics': []} |
| 158 | link_metrics['current_rate_kbps']['metrics'] += [({"direction": "up", "link": link_number}, int(self.attrs['DSLINTERFACE'][link]['Upstream_current_rate']))] | 163 | link_metrics['current_rate_kbps']['metrics'] += [({"direction": "up", "link": link_number}, int(self.attrs['DSLINTERFACE'][link]['Upstream_current_rate']))] |
| 159 | link_metrics['current_rate_kbps']['metrics'] += [({"direction": "down", "link": link_number}, int(self.attrs['DSLINTERFACE'][link]['Downstream_current_rate']))] | 164 | link_metrics['current_rate_kbps']['metrics'] += [({"direction": "down", "link": link_number}, int(self.attrs['DSLINTERFACE'][link]['Downstream_current_rate']))] |
| 160 | 165 | ||
| 161 | if 'dsl_uptime_seconds' not in link_metrics: | 166 | if 'dsl_uptime_seconds' not in link_metrics: |
| 162 | link_metrics['dsl_uptime_seconds'] = {'type': 'gauge', 'metrics': []} | 167 | link_metrics['uptime_seconds'] = {'type': 'gauge', 'metrics': []} |
| 163 | link_metrics['dsl_uptime_seconds']['metrics'] += [({"link": link_number}, int(self.attrs['DSLINTERFACE'][link]['Showtime_start']))] | 168 | link_metrics['uptime_seconds']['metrics'] += [({"link": link_number}, int(self.attrs['DSLINTERFACE'][link]['Showtime_start']) if link_is_up else 0)] |
| 169 | |||
| 170 | if 'status' not in link_metrics: | ||
| 171 | link_metrics['status'] = {'type': 'gauge', 'metrics': []} | ||
| 172 | link_metrics['status']['metrics'] += [({"link": link_number, "status": self.attrs['DSLINTERFACE'][link]['Status']}, 1)] | ||
| 164 | if link_metrics: | 173 | if link_metrics: |
| 165 | for metric_name in link_metrics: | 174 | for metric_name in link_metrics: |
| 166 | metrics += _format_prom_metrics(f'dsl_{metric_name}', link_metrics[metric_name]['type'], link_metrics[metric_name]['metrics']) | 175 | metrics += _format_prom_metrics(f'dsl_{metric_name}', link_metrics[metric_name]['type'], link_metrics[metric_name]['metrics']) |
| @@ -203,7 +212,7 @@ class ZTEMetricsServer(BaseHTTPRequestHandler): | |||
| 203 | self.send_response(200) | 212 | self.send_response(200) |
| 204 | self.send_header("Content-type", "text/plain") | 213 | self.send_header("Content-type", "text/plain") |
| 205 | self.end_headers() | 214 | self.end_headers() |
| 206 | 215 | ||
| 207 | self.wfile.write(zte_metrics.prometheus()) | 216 | self.wfile.write(zte_metrics.prometheus()) |
| 208 | case _: | 217 | case _: |
| 209 | self.send_response(404) | 218 | self.send_response(404) |
| @@ -1,19 +1,29 @@ | |||
| 1 | inputs@{ system, self, deploy-rs, nvfetcher, nixpkgs, ca-util, ... }: | 1 | inputs@{ system, self, nvfetcher, nixpkgs, ca-util, ... }: |
| 2 | let | 2 | let |
| 3 | pkgs = self.legacyPackages.${system}; | 3 | pkgs = self.legacyPackages.${system}; |
| 4 | utils = import ./utils { inherit (nixpkgs) lib; }; | 4 | utils = import ./utils { inherit (nixpkgs) lib; }; |
| 5 | inherit (utils) nixImport; | 5 | inherit (utils) nixImport; |
| 6 | uv-links = pkgs.symlinkJoin { | ||
| 7 | name = "uv-links"; | ||
| 8 | paths = [ | ||
| 9 | pkgs.python312.pkgs.pygobject3 | ||
| 10 | ]; | ||
| 11 | }; | ||
| 6 | in pkgs.mkShell { | 12 | in pkgs.mkShell { |
| 7 | nativeBuildInputs = builtins.attrValues self.packages.${system} ++ (with pkgs; [ | 13 | nativeBuildInputs = builtins.attrValues self.packages.${system} ++ (with pkgs; [ |
| 8 | sops | 14 | sops |
| 9 | wireguard-tools | 15 | wireguard-tools |
| 10 | gup | 16 | gup |
| 11 | nftables | 17 | nftables |
| 12 | deploy-rs.packages.${system}.deploy-rs | 18 | deploy-rs.deploy-rs |
| 13 | knot-dns | 19 | knot-dns |
| 14 | yq | 20 | yq |
| 15 | nvfetcher.packages.${system}.default | 21 | nvfetcher.packages.${system}.default |
| 16 | ca-util.packages.${system}.ca | 22 | ca-util.packages.${system}.ca |
| 17 | poetry | 23 | poetry uv |
| 24 | ninja pkg-config cairo.dev systemd.dev | ||
| 18 | ]); | 25 | ]); |
| 26 | shellHook = '' | ||
| 27 | export UV_FIND_LINKS=${uv-links}/lib/python3.12/site-packages | ||
| 28 | ''; | ||
| 19 | } | 29 | } |
diff --git a/system-profiles/bcachefs.nix b/system-profiles/bcachefs.nix index f9f048b9..be12bf20 100644 --- a/system-profiles/bcachefs.nix +++ b/system-profiles/bcachefs.nix | |||
| @@ -1,6 +1,16 @@ | |||
| 1 | { pkgs, ... } : { | 1 | { pkgs, lib, ... } : { |
| 2 | config = { | 2 | config = { |
| 3 | boot.supportedFilesystems.bcachefs = true; | 3 | boot.supportedFilesystems.bcachefs = true; |
| 4 | environment.systemPackages = with pkgs; [ bcachefs-tools ]; | 4 | environment.systemPackages = with pkgs; [ bcachefs-tools ]; |
| 5 | |||
| 6 | boot.kernelPatches = [ | ||
| 7 | { | ||
| 8 | name = "bcachefs-casefold-fix"; | ||
| 9 | patch = null; | ||
| 10 | structuredExtraConfig = with lib.kernel; { | ||
| 11 | UNICODE = lib.mkOverride 90 no; | ||
| 12 | }; | ||
| 13 | } | ||
| 14 | ]; | ||
| 5 | }; | 15 | }; |
| 6 | } | 16 | } |
diff --git a/system-profiles/core/default.nix b/system-profiles/core/default.nix index 71d0619a..e5f9dc16 100644 --- a/system-profiles/core/default.nix +++ b/system-profiles/core/default.nix | |||
| @@ -127,36 +127,16 @@ in { | |||
| 127 | 127 | ||
| 128 | flake-registry = "${flakeInputs.flake-registry}/flake-registry.json"; | 128 | flake-registry = "${flakeInputs.flake-registry}/flake-registry.json"; |
| 129 | }; | 129 | }; |
| 130 | nixPath = [ | 130 | nixPath = map (flake: "${flake}=flake:${flake}") (attrNames config.nix.registry); |
| 131 | "nixpkgs=${pkgs.runCommand "nixpkgs" {} '' | ||
| 132 | mkdir $out | ||
| 133 | ln -s ${./nixpkgs.nix} $out/default.nix | ||
| 134 | ln -s /run/nixpkgs/lib $out/lib | ||
| 135 | ''}" | ||
| 136 | ]; | ||
| 137 | registry = | 131 | registry = |
| 138 | let override = { self = "nixos"; }; | 132 | let override = { self = "nixos"; }; |
| 139 | in mapAttrs' (inpName: inpFlake: nameValuePair | 133 | in mapAttrs' (inpName: inpFlake: nameValuePair |
| 140 | (override.${inpName} or inpName) | 134 | (override.${inpName} or inpName) |
| 141 | { flake = inpFlake; } ) flakeInputs; | 135 | { to = { type = "path"; path = inpFlake; }; } ) flakeInputs; |
| 142 | }; | 136 | }; |
| 143 | 137 | ||
| 144 | systemd.tmpfiles.rules = [ | 138 | systemd.tmpfiles.rules = [ |
| 145 | "L+ /run/nixpkgs - - - - ${flakeInputs.${config.nixpkgs.flakeInput}.outPath}" | 139 | "L+ /run/nixpkgs - - - - ${flakeInputs.${config.nixpkgs.flakeInput}.outPath}" |
| 146 | "L+ /run/nixpkgs-overlays.nix - - - - ${pkgs.writeText "overlays.nix" '' | ||
| 147 | with builtins; | ||
| 148 | |||
| 149 | attrValues (import | ||
| 150 | ( | ||
| 151 | let lock = fromJSON (readFile ${flake + "/flake.lock"}); in | ||
| 152 | fetchTarball { | ||
| 153 | url = "https://github.com/edolstra/flake-compat/archive/''${lock.nodes.flake-compat.locked.rev}.tar.gz"; | ||
| 154 | sha256 = lock.nodes.flake-compat.locked.narHash; | ||
| 155 | } | ||
| 156 | ) | ||
| 157 | { src = ${flake}; } | ||
| 158 | ).defaultNix.overlays | ||
| 159 | ''}" | ||
| 160 | "L+ /etc/nixos - - - - ${flake}" | 140 | "L+ /etc/nixos - - - - ${flake}" |
| 161 | ] ++ map (input: "L+ /run/flake-inputs/${input} - - - - ${flakeInputs.${input}.outPath}") (attrNames flakeInputs); | 141 | ] ++ map (input: "L+ /run/flake-inputs/${input} - - - - ${flakeInputs.${input}.outPath}") (attrNames flakeInputs); |
| 162 | 142 | ||
| @@ -177,11 +157,9 @@ in { | |||
| 177 | { | 157 | { |
| 178 | manual.manpages.enable = true; | 158 | manual.manpages.enable = true; |
| 179 | systemd.user.startServices = "sd-switch"; | 159 | systemd.user.startServices = "sd-switch"; |
| 180 | |||
| 181 | programs.ssh.internallyManaged = mkForce true; | ||
| 182 | } | 160 | } |
| 183 | ]; | 161 | ]; |
| 184 | extraSpecialArgs = { inherit flake flakeInputs path; }; | 162 | extraSpecialArgs = { inherit flake flakeInputs path; hostConfig = config; }; |
| 185 | }; | 163 | }; |
| 186 | 164 | ||
| 187 | sops = mkIf hasSops { | 165 | sops = mkIf hasSops { |
| @@ -202,17 +180,22 @@ in { | |||
| 202 | }; | 180 | }; |
| 203 | environment.systemPackages = with pkgs; [ git-annex scutiger ]; | 181 | environment.systemPackages = with pkgs; [ git-annex scutiger ]; |
| 204 | } | 182 | } |
| 205 | ] ++ (optional (options ? system.switch.enableNg) { | 183 | ] ++ (optional (options ? system.rebuild.enableNg) { |
| 206 | system.switch = lib.mkDefault { | 184 | system.rebuild.enableNg = lib.mkDefault true; |
| 207 | enable = false; | 185 | }) |
| 208 | enableNg = true; | 186 | ++ (optional (options ? services.userborn) { |
| 187 | services.userborn = { | ||
| 188 | enable = lib.mkDefault true; | ||
| 189 | passwordFilesLocation = lib.mkDefault "/var/lib/nixos"; | ||
| 209 | }; | 190 | }; |
| 210 | }) | 191 | }) |
| 192 | ++ (optional (!(options ? services.userborn) && (options ? system.etc)) { | ||
| 193 | systemd.sysusers.enable = lib.mkDefault true; | ||
| 194 | }) | ||
| 211 | ++ (optional (options ? system.etc) { | 195 | ++ (optional (options ? system.etc) { |
| 212 | boot.initrd.systemd.enable = lib.mkDefault true; | 196 | boot.initrd.systemd.enable = lib.mkDefault true; |
| 213 | system.etc.overlay.enable = lib.mkDefault true; | 197 | system.etc.overlay.enable = lib.mkDefault true; |
| 214 | system.etc.overlay.mutable = lib.mkDefault (!config.systemd.sysusers.enable); | 198 | system.etc.overlay.mutable = lib.mkDefault (!config.systemd.sysusers.enable); |
| 215 | systemd.sysusers.enable = lib.mkDefault true; | ||
| 216 | 199 | ||
| 217 | # Random perl remnants | 200 | # Random perl remnants |
| 218 | system.disableInstallerTools = lib.mkDefault true; | 201 | system.disableInstallerTools = lib.mkDefault true; |
diff --git a/system-profiles/default-locale.nix b/system-profiles/default-locale.nix index 2d483f04..60d338cb 100644 --- a/system-profiles/default-locale.nix +++ b/system-profiles/default-locale.nix | |||
| @@ -1,16 +1,23 @@ | |||
| 1 | { lib, ... }: | 1 | { lib, options, ... }: |
| 2 | 2 | ||
| 3 | with lib; | 3 | with lib; |
| 4 | 4 | ||
| 5 | { | 5 | { |
| 6 | i18n = { | 6 | config = foldr recursiveUpdate {} ([ |
| 7 | defaultLocale = "en_DK.UTF-8"; | 7 | { |
| 8 | extraLocaleSettings = { | 8 | i18n = { |
| 9 | "TIME_STYLE" = "long-iso"; | 9 | defaultLocale = "en_DK.UTF-8"; |
| 10 | }; | 10 | extraLocaleSettings = { |
| 11 | supportedLocales = [ "C.UTF-8/UTF-8" "en_US.UTF-8/UTF-8" "en_DK.UTF-8/UTF-8" ]; | 11 | "TIME_STYLE" = "long-iso"; |
| 12 | }; | 12 | }; |
| 13 | console.keyMap = mkDefault "dvorak-programmer"; | 13 | }; |
| 14 | console.keyMap = mkDefault "dvorak-programmer"; | ||
| 14 | 15 | ||
| 15 | time.timeZone = mkDefault "Europe/Berlin"; | 16 | time.timeZone = mkDefault "Europe/Berlin"; |
| 17 | } | ||
| 18 | ] ++ (optional (options ? i18n.extraLocales) { | ||
| 19 | i18n.extraLocales = [ "C.UTF-8" "en_US.UTF-8" "en_DK.UTF-8" ]; | ||
| 20 | }) ++ (optional (!(options ? i18n.extraLocales)) { | ||
| 21 | i18n.supportedLocales = [ "C.UTF-8/UTF-8" "en_US.UTF-8/UTF-8" "en_DK.UTF-8/UTF-8" ]; | ||
| 22 | })); | ||
| 16 | } | 23 | } |
diff --git a/system-profiles/initrd-all-crypto-modules.nix b/system-profiles/initrd-all-crypto-modules.nix index 45cd4b74..da6c781e 100644 --- a/system-profiles/initrd-all-crypto-modules.nix +++ b/system-profiles/initrd-all-crypto-modules.nix | |||
| @@ -18,7 +18,7 @@ in { | |||
| 18 | { | 18 | { |
| 19 | name = "encrypted_key"; | 19 | name = "encrypted_key"; |
| 20 | patch = null; | 20 | patch = null; |
| 21 | extraStructuredConfig.ENCRYPTED_KEYS = lib.kernel.yes; | 21 | structuredExtraConfig.ENCRYPTED_KEYS = lib.kernel.yes; |
| 22 | } | 22 | } |
| 23 | ]; | 23 | ]; |
| 24 | } | 24 | } |
diff --git a/system-profiles/lanzaboote.nix b/system-profiles/lanzaboote.nix new file mode 100644 index 00000000..f1e179cf --- /dev/null +++ b/system-profiles/lanzaboote.nix | |||
| @@ -0,0 +1,14 @@ | |||
| 1 | { flakeInputs, pkgs, ... }: | ||
| 2 | { | ||
| 3 | imports = [ | ||
| 4 | flakeInputs.lanzaboote.nixosModules.lanzaboote | ||
| 5 | ]; | ||
| 6 | |||
| 7 | config = { | ||
| 8 | environment.systemPackages = [ pkgs.sbctl ]; | ||
| 9 | boot.lanzaboote = { | ||
| 10 | enable = true; | ||
| 11 | pkiBundle = "/var/lib/sbctl"; | ||
| 12 | }; | ||
| 13 | }; | ||
| 14 | } | ||
diff --git a/system-profiles/nfsroot.nix b/system-profiles/nfsroot.nix index 1cd930d9..e3dc2d2e 100644 --- a/system-profiles/nfsroot.nix +++ b/system-profiles/nfsroot.nix | |||
| @@ -1,4 +1,4 @@ | |||
| 1 | { config, options, pkgs, lib, flake, flakeInputs, ... }: | 1 | { config, options, pkgs, lib, flake, ... }: |
| 2 | 2 | ||
| 3 | with lib; | 3 | with lib; |
| 4 | 4 | ||
| @@ -48,7 +48,7 @@ in { | |||
| 48 | fileSystems."/nix/.ro-store" = mkImageMediaOverride | 48 | fileSystems."/nix/.ro-store" = mkImageMediaOverride |
| 49 | { fsType = "nfs4"; | 49 | { fsType = "nfs4"; |
| 50 | device = cfg.storeDevice; | 50 | device = cfg.storeDevice; |
| 51 | options = [ "ro" ]; | 51 | options = [ "ro" "nfsvers=4.2" ]; |
| 52 | neededForBoot = true; | 52 | neededForBoot = true; |
| 53 | }; | 53 | }; |
| 54 | 54 | ||
| @@ -86,7 +86,7 @@ in { | |||
| 86 | mkdir -p /mnt-root/etc/ | 86 | mkdir -p /mnt-root/etc/ |
| 87 | cp /etc/resolv.conf /mnt-root/etc/resolv.conf | 87 | cp /etc/resolv.conf /mnt-root/etc/resolv.conf |
| 88 | ''; | 88 | ''; |
| 89 | networking.useDHCP = true; | 89 | networking.useDHCP = mkImageMediaOverride true; |
| 90 | networking.resolvconf.enable = false; | 90 | networking.resolvconf.enable = false; |
| 91 | networking.dhcpcd.persistent = true; | 91 | networking.dhcpcd.persistent = true; |
| 92 | 92 | ||
diff --git a/system-profiles/niri-flake.nix b/system-profiles/niri-flake.nix new file mode 100644 index 00000000..b28d51ff --- /dev/null +++ b/system-profiles/niri-flake.nix | |||
| @@ -0,0 +1,4 @@ | |||
| 1 | { ... }: | ||
| 2 | { | ||
| 3 | config.niri-flake.cache.enable = false; | ||
| 4 | } | ||
diff --git a/system-profiles/niri-unstable.nix b/system-profiles/niri-unstable.nix new file mode 100644 index 00000000..3a8b393d --- /dev/null +++ b/system-profiles/niri-unstable.nix | |||
| @@ -0,0 +1,11 @@ | |||
| 1 | { config, pkgs, lib, ... }: | ||
| 2 | { | ||
| 3 | config = { | ||
| 4 | programs.niri.package = lib.mkDefault pkgs.niri-unstable; | ||
| 5 | home-manager.sharedModules = [ | ||
| 6 | { | ||
| 7 | programs.niri.package = lib.mkDefault config.programs.niri.package; | ||
| 8 | } | ||
| 9 | ]; | ||
| 10 | }; | ||
| 11 | } | ||
diff --git a/system-profiles/rebuild-machines/default.nix b/system-profiles/rebuild-machines/default.nix index 544f47e1..de86cd74 100644 --- a/system-profiles/rebuild-machines/default.nix +++ b/system-profiles/rebuild-machines/default.nix | |||
| @@ -25,16 +25,18 @@ let | |||
| 25 | 25 | ||
| 26 | phases = [ "buildPhase" "installPhase" ]; | 26 | phases = [ "buildPhase" "installPhase" ]; |
| 27 | 27 | ||
| 28 | inherit (pkgs) zsh coreutils openssh; | ||
| 29 | inherit (cfg) scriptName; | ||
| 30 | inherit (cfg.flake) flakeOutput; | ||
| 31 | flake = cfg.flake.name; | ||
| 32 | nixosRebuild = config.system.build.nixos-rebuild; | ||
| 33 | inherit (config.security) wrapperDir; | ||
| 34 | inherit sshConfig; | ||
| 35 | |||
| 36 | buildPhase = '' | 28 | buildPhase = '' |
| 37 | substituteAll $src rebuild-machine.zsh | 29 | substitute $src rebuild-machine.zsh \ |
| 30 | --subst-var-by zsh ${pkgs.zsh} \ | ||
| 31 | --subst-var-by coreutils ${pkgs.coreutils} \ | ||
| 32 | --subst-var-by openssh ${pkgs.openssh} \ | ||
| 33 | --subst-var-by wrapperDir ${config.security.wrapperDir} \ | ||
| 34 | --subst-var-by sshConfig ${sshConfig} \ | ||
| 35 | --subst-var-by out "$out" \ | ||
| 36 | --subst-var-by nixosRebuild ${config.system.build.nixos-rebuild} \ | ||
| 37 | --subst-var-by flake ${cfg.flake.name} \ | ||
| 38 | --subst-var-by scriptName ${cfg.scriptName} \ | ||
| 39 | --subst-var-by flakeOutput ${cfg.flake.flakeOutput} | ||
| 38 | ''; | 40 | ''; |
| 39 | 41 | ||
| 40 | installPhase = '' | 42 | installPhase = '' |
diff --git a/system-profiles/zfs.nix b/system-profiles/zfs.nix index 149decee..af9f1c17 100644 --- a/system-profiles/zfs.nix +++ b/system-profiles/zfs.nix | |||
| @@ -1,8 +1,8 @@ | |||
| 1 | { pkgs, lib, ... } : { | 1 | { config, pkgs, lib, ... } : { |
| 2 | config = { | 2 | config = { |
| 3 | boot = { | 3 | boot = { |
| 4 | kernelPackages = pkgs.linuxPackages_6_11; | 4 | kernelPackages = pkgs.linuxPackages_6_12; |
| 5 | zfs.package = pkgs.zfs_unstable; | 5 | zfs.package = pkgs.zfs_2_3; |
| 6 | 6 | ||
| 7 | supportedFilesystems.zfs = true; | 7 | supportedFilesystems.zfs = true; |
| 8 | }; | 8 | }; |
diff --git a/user-profiles/core.nix b/user-profiles/core.nix index a8af48b3..57fb7628 100644 --- a/user-profiles/core.nix +++ b/user-profiles/core.nix | |||
| @@ -5,7 +5,11 @@ with lib; | |||
| 5 | { | 5 | { |
| 6 | config = { | 6 | config = { |
| 7 | users.users.${userName} = {}; # Just make sure the user is created | 7 | users.users.${userName} = {}; # Just make sure the user is created |
| 8 | home-manager.users.${userName} = {}; | 8 | home-manager.users.${userName} = let sysConfig = config; in { config, ... }: { |
| 9 | config.nix.settings = { | ||
| 10 | inherit (sysConfig.nix.settings) use-xdg-base-directories; | ||
| 11 | }; | ||
| 12 | }; | ||
| 9 | 13 | ||
| 10 | systemd.services."home-manager-${utils.escapeSystemdPath userName}" = lib.mkIf (!config.home-manager.enableSystemd) { | 14 | systemd.services."home-manager-${utils.escapeSystemdPath userName}" = lib.mkIf (!config.home-manager.enableSystemd) { |
| 11 | restartIfChanged = false; # only run once on startup, deploy to running system with deploy-rs | 15 | restartIfChanged = false; # only run once on startup, deploy to running system with deploy-rs |
diff --git a/user-profiles/feeds/alot.config b/user-profiles/feeds/alot.config deleted file mode 100644 index a14d4539..00000000 --- a/user-profiles/feeds/alot.config +++ /dev/null | |||
| @@ -1,50 +0,0 @@ | |||
| 1 | attachment_prefix="~/Downloads" | ||
| 2 | bug_on_exit=true | ||
| 3 | editor_cmd="false" | ||
| 4 | tabwidth=2 | ||
| 5 | timestamp_format="%a %d %b %H:%M:%S %Y UTC%z" | ||
| 6 | auto_remove_unread=True | ||
| 7 | #initial_command="search ( tag:inbox ) AND NOT ( tag:killed )" | ||
| 8 | initial_command="search ( tag:inbox ) AND NOT ( is:link OR is:media OR is:killed )" | ||
| 9 | |||
| 10 | [accounts] | ||
| 11 | [[private]] | ||
| 12 | realname = @realname@ | ||
| 13 | address = @address@ | ||
| 14 | |||
| 15 | [bindings] | ||
| 16 | j = | ||
| 17 | k = | ||
| 18 | 'g g' = | ||
| 19 | G = | ||
| 20 | I = search ( tag:inbox ) AND NOT ( is:killed ) | ||
| 21 | U = search ( tag:inbox ) AND NOT ( is:link OR is:media OR is:killed ) | ||
| 22 | V = search ( tag:inbox AND is:media OR ( is:live AND date:12h.. AND NOT is:unread ) ) AND NOT ( is:killed ) | ||
| 23 | W = search ( is:media ) AND NOT ( tag:inbox OR is:killed OR is:highlight ) | ||
| 24 | L = search ( tag:inbox AND is:link ) AND NOT ( is:killed ) | ||
| 25 | |||
| 26 | h = move first | ||
| 27 | t = move up | ||
| 28 | n = move down | ||
| 29 | s = move last | ||
| 30 | [[search]] | ||
| 31 | a = | ||
| 32 | s = | ||
| 33 | |||
| 34 | u = toggletags unread | ||
| 35 | i = toggletags inbox | ||
| 36 | j = untag unread,inbox | ||
| 37 | r = toggletags later | ||
| 38 | [[thread]] | ||
| 39 | s = | ||
| 40 | S = | ||
| 41 | n = | ||
| 42 | 'g j' = | ||
| 43 | 'g k' = | ||
| 44 | 'g l' = | ||
| 45 | w = save | ||
| 46 | W = save --all | ||
| 47 | 'g h' = move parent | ||
| 48 | 'g t' = move next sibling | ||
| 49 | 'g n' = move previous sibling | ||
| 50 | 'g s' = move first reply \ No newline at end of file | ||
diff --git a/user-profiles/feeds/default.nix b/user-profiles/feeds/default.nix deleted file mode 100644 index 82be90c7..00000000 --- a/user-profiles/feeds/default.nix +++ /dev/null | |||
| @@ -1,11 +0,0 @@ | |||
| 1 | { config, flakeInputs, pkgs, lib, userName, customUtils, ... }: | ||
| 2 | { | ||
| 3 | home-manager.users.${userName} = {...}: { | ||
| 4 | imports = [ | ||
| 5 | (customUtils.overrideModuleArgs | ||
| 6 | (import ./module.nix) | ||
| 7 | (inputs: inputs // { inherit flakeInputs; inherit (config.nixpkgs) system; }) | ||
| 8 | ) | ||
| 9 | ]; | ||
| 10 | }; | ||
| 11 | } | ||
diff --git a/user-profiles/feeds/imm-notmuch-insert.py b/user-profiles/feeds/imm-notmuch-insert.py deleted file mode 100644 index b7eed292..00000000 --- a/user-profiles/feeds/imm-notmuch-insert.py +++ /dev/null | |||
| @@ -1,52 +0,0 @@ | |||
| 1 | #!@python@/bin/python | ||
| 2 | |||
| 3 | import json | ||
| 4 | import sys | ||
| 5 | import subprocess | ||
| 6 | from io import BytesIO | ||
| 7 | from email.message import EmailMessage | ||
| 8 | import configparser | ||
| 9 | from os import environ | ||
| 10 | from datetime import * | ||
| 11 | from dateutil.tz import * | ||
| 12 | from dateutil.parser import isoparse | ||
| 13 | from html2text import html2text | ||
| 14 | |||
| 15 | def main(): | ||
| 16 | notmuchConfig = configparser.ConfigParser() | ||
| 17 | notmuchConfig.read(environ.get('NOTMUCH_CONFIG')) | ||
| 18 | |||
| 19 | callbackMessage = json.load(sys.stdin) | ||
| 20 | |||
| 21 | msg = EmailMessage() | ||
| 22 | authors = ', '.join(map(lambda author: author['name'], callbackMessage['feed_item']['authors'])) | ||
| 23 | if authors: | ||
| 24 | msg['From'] = f"{callbackMessage['feed_definition']['title']} ({authors}) <imm@imm.invalid>" | ||
| 25 | else: | ||
| 26 | msg['From'] = f"{callbackMessage['feed_definition']['title']} <imm@imm.invalid>" | ||
| 27 | msg['To'] = f"{notmuchConfig['user']['name']} <{notmuchConfig['user']['primary_email']}>" | ||
| 28 | if 'title' in callbackMessage['feed_item'] and callbackMessage['feed_item']['title']: | ||
| 29 | msg['Subject'] = callbackMessage['feed_item']['title'] | ||
| 30 | msg['Item-Identifier'] = f"{callbackMessage['feed_item']['identifier']}" | ||
| 31 | for link in callbackMessage['feed_item']['links']: | ||
| 32 | msg.add_header('Link', link['uri']) | ||
| 33 | date = None | ||
| 34 | if 'date' in callbackMessage['feed_item']: | ||
| 35 | date = isoparse(callbackMessage['feed_item']['date']) | ||
| 36 | else: | ||
| 37 | date = datetime.now(tzlocal()) | ||
| 38 | msg['Date'] = date.strftime('%a, %e %b %Y %T %z') | ||
| 39 | |||
| 40 | if 'content' in callbackMessage['feed_item'] and callbackMessage['feed_item']['content']: | ||
| 41 | msg.set_content(html2text(callbackMessage['feed_item']['content'])) | ||
| 42 | msg.add_alternative(callbackMessage['feed_item']['content'], subtype='html') | ||
| 43 | |||
| 44 | |||
| 45 | subprocess.run( | ||
| 46 | args=['notmuch', 'insert'], | ||
| 47 | check=True, | ||
| 48 | input=bytes(msg) | ||
| 49 | ) | ||
| 50 | |||
| 51 | if __name__ == '__main__': | ||
| 52 | sys.exit(main()) | ||
diff --git a/user-profiles/feeds/module.nix b/user-profiles/feeds/module.nix deleted file mode 100644 index 63e827eb..00000000 --- a/user-profiles/feeds/module.nix +++ /dev/null | |||
| @@ -1,236 +0,0 @@ | |||
| 1 | { config, flakeInputs, pkgs, lib, system, ... }: | ||
| 2 | |||
| 3 | with lib; | ||
| 4 | |||
| 5 | let | ||
| 6 | inherit (flakeInputs.home-manager.lib) hm; | ||
| 7 | |||
| 8 | databasePath = "${config.xdg.dataHome}/feeds"; | ||
| 9 | |||
| 10 | imm = | ||
| 11 | let | ||
| 12 | hlib = pkgs.haskell.lib; | ||
| 13 | haskellPackages = pkgs.haskellPackages.override { | ||
| 14 | overrides = finalHaskell: prevHaskell: { | ||
| 15 | uri-bytestring = finalHaskell.callCabal2nix "uri-bytestring" (pkgs.fetchFromGitHub { | ||
| 16 | owner = "gkleen"; | ||
| 17 | repo = "uri-bytestring"; | ||
| 18 | rev = "5f7f32c8274bc4d1b81d99582f5148fe3e8b637e"; | ||
| 19 | sha256 = "XLanwyCDIlMuOkpE5LbTNOBfL+1kZX+URfj9Bhs1Nsc="; | ||
| 20 | fetchSubmodules = true; | ||
| 21 | }) {}; | ||
| 22 | atom-conduit = finalHaskell.callCabal2nix "atom-conduit" (pkgs.fetchFromGitHub { | ||
| 23 | owner = "gkleen"; | ||
| 24 | repo = "atom-conduit"; | ||
| 25 | rev = "022f0182a02373f87c06a0a09817c8c41efe2425"; | ||
| 26 | sha256 = "8yEyh3ymqkoM/YP+eBqPq1I5ofzj0Qn7ojL7IWx1DPo="; | ||
| 27 | fetchSubmodules = true; | ||
| 28 | }) {}; | ||
| 29 | rss-conduit = finalHaskell.callCabal2nix "rss-condit" (pkgs.fetchFromGitHub { | ||
| 30 | owner = "gkleen"; | ||
| 31 | repo = "rss-conduit"; | ||
| 32 | rev = "dbb0960a8d3dc519f1607aa0223b3a25a49282ef"; | ||
| 33 | sha256 = "Md1XApZWkdv4JvNoaVnjz0S85LbEC6w9U3PUcwXfu94="; | ||
| 34 | fetchSubmodules = true; | ||
| 35 | }) {}; | ||
| 36 | beam-core = hlib.doJailbreak (finalHaskell.callCabal2nix "beam-core" "${beamSrc}/beam-core" {}); | ||
| 37 | beam-migrate = hlib.doJailbreak (finalHaskell.callCabal2nix "beam-migrate" "${beamSrc}/beam-migrate" {}); | ||
| 38 | beam-sqlite = hlib.doJailbreak (finalHaskell.callCabal2nix "beam-sqlite" "${beamSrc}/beam-sqlite" {}); | ||
| 39 | |||
| 40 | imm = finalHaskell.callCabal2nix "imm" (pkgs.fetchFromGitHub { | ||
| 41 | owner = "k0ral"; | ||
| 42 | repo = "imm"; | ||
| 43 | rev = "5033879667264cb44cee65671a66f6aa43f249e7"; | ||
| 44 | sha256 = "PG22caLQmAGhLZP49HsazuNd8IFKKaTuhXIQBD8v4Fs="; | ||
| 45 | fetchSubmodules = true; | ||
| 46 | }) {}; | ||
| 47 | }; | ||
| 48 | }; | ||
| 49 | beamSrc = pkgs.fetchFromGitHub { | ||
| 50 | owner = "haskell-beam"; | ||
| 51 | repo = "beam"; | ||
| 52 | rev = "efd464b079755a781c2bb7a2fc030d6c141bbb8a"; | ||
| 53 | sha256 = "8nTuBP/vD0L/qMo4h3XNrGZvpIwXuMVdj40j5gvHU6w="; | ||
| 54 | fetchSubmodules = true; | ||
| 55 | }; | ||
| 56 | in haskellPackages.imm; | ||
| 57 | immWrapped = pkgs.runCommand "${imm.name}-wrapped-${config.home.username}" | ||
| 58 | { nativeBuildInputs = with pkgs; [ makeWrapper ]; | ||
| 59 | } '' | ||
| 60 | mkdir -p $out/bin | ||
| 61 | makeWrapper ${imm}/bin/imm $out/bin/imm \ | ||
| 62 | --add-flags --callbacks=${notmuchCallbacks} | ||
| 63 | ''; | ||
| 64 | |||
| 65 | notmuchCallbacks = pkgs.writeText "imm-callbacks-${config.home.username}.dhall" '' | ||
| 66 | [ { _executable = "${immNotmuchInsert}/bin/imm-notmuch-insert" | ||
| 67 | , _arguments = [] : List Text | ||
| 68 | } | ||
| 69 | ] | ||
| 70 | ''; | ||
| 71 | |||
| 72 | immNotmuchInsert = pkgs.stdenv.mkDerivation rec { | ||
| 73 | name = "imm-notmuch-insert-${config.home.username}"; | ||
| 74 | src = ./imm-notmuch-insert.py; | ||
| 75 | |||
| 76 | phases = [ "buildPhase" "checkPhase" "installPhase" "fixupPhase" ]; | ||
| 77 | |||
| 78 | python = pkgs.python39.withPackages (ps: with ps; [ configparser python-dateutil html2text ]); | ||
| 79 | |||
| 80 | nativeBuildInputs = with pkgs; [ makeWrapper ]; | ||
| 81 | |||
| 82 | buildPhase = '' | ||
| 83 | substituteAll $src imm-notmuch-insert | ||
| 84 | ''; | ||
| 85 | |||
| 86 | doCheck = true; | ||
| 87 | checkPhase = '' | ||
| 88 | ${python}/bin/python -m py_compile imm-notmuch-insert | ||
| 89 | ''; | ||
| 90 | |||
| 91 | installPhase = '' | ||
| 92 | install -m 0755 -D -t $out/bin \ | ||
| 93 | imm-notmuch-insert | ||
| 94 | ''; | ||
| 95 | |||
| 96 | fixupPhase = '' | ||
| 97 | wrapProgram $out/bin/imm-notmuch-insert \ | ||
| 98 | --prefix PATH : ${pkgs.notmuch}/bin \ | ||
| 99 | --set NOTMUCH_CONFIG ${configPath} | ||
| 100 | ''; | ||
| 101 | }; | ||
| 102 | |||
| 103 | mkIniKeyValue = key: value: | ||
| 104 | let | ||
| 105 | tweakVal = v: | ||
| 106 | if isString v then | ||
| 107 | v | ||
| 108 | else if isList v then | ||
| 109 | concatMapStringsSep ";" tweakVal v | ||
| 110 | else if isBool v then | ||
| 111 | (if v then "true" else "false") | ||
| 112 | else | ||
| 113 | toString v; | ||
| 114 | in "${key}=${tweakVal value}"; | ||
| 115 | |||
| 116 | notmuchIni = { | ||
| 117 | database = { path = databasePath; }; | ||
| 118 | |||
| 119 | maildir = { synchronize_flags = false; }; | ||
| 120 | |||
| 121 | new = { | ||
| 122 | ignore = []; | ||
| 123 | tags = ["new"]; | ||
| 124 | }; | ||
| 125 | |||
| 126 | user = { | ||
| 127 | name = config.home.username; | ||
| 128 | primary_email = "${config.home.username}@imm.invalid"; | ||
| 129 | }; | ||
| 130 | |||
| 131 | search = { exclude_tags = ["deleted"]; }; | ||
| 132 | }; | ||
| 133 | configPath = pkgs.writeText "notmuchrc" (generators.toINI { mkKeyValue = mkIniKeyValue; } notmuchIni); | ||
| 134 | |||
| 135 | afewConfigDir = pkgs.symlinkJoin { | ||
| 136 | name = "afew-config"; | ||
| 137 | paths = [ | ||
| 138 | (pkgs.writeTextDir "config" '' | ||
| 139 | [InboxFilter] | ||
| 140 | '') | ||
| 141 | ]; | ||
| 142 | }; | ||
| 143 | |||
| 144 | notmuchHooksDir = | ||
| 145 | let | ||
| 146 | afewHook = pkgs.writeShellScript "afew" '' | ||
| 147 | exec -- ${pkgs.afew}/bin/afew -c ${afewConfigDir} -C ${configPath} --tag --new -vv | ||
| 148 | ''; | ||
| 149 | in pkgs.linkFarm "notmuch-hooks" [ | ||
| 150 | { name = "post-new"; | ||
| 151 | path = afewHook; | ||
| 152 | } | ||
| 153 | { name = "post-insert"; | ||
| 154 | path = afewHook; | ||
| 155 | } | ||
| 156 | ]; | ||
| 157 | |||
| 158 | notmuchWrapped = pkgs.runCommand "${pkgs.notmuch.name}-wrapped-${config.home.username}" | ||
| 159 | { nativeBuildInputs = with pkgs; [ makeWrapper ]; | ||
| 160 | } '' | ||
| 161 | mkdir -p $out/bin | ||
| 162 | makeWrapper ${pkgs.notmuch}/bin/notmuch $out/bin/notmuch-feeds \ | ||
| 163 | --set NOTMUCH_CONFIG ${configPath} | ||
| 164 | ''; | ||
| 165 | alotWrapped = pkgs.runCommand "${pkgs.alot.name}-wrapped-${config.home.username}" | ||
| 166 | { nativeBuildInputs = with pkgs; [ makeWrapper gnused ]; | ||
| 167 | } '' | ||
| 168 | mkdir -p $out/bin | ||
| 169 | makeWrapper ${pkgs.alot}/bin/alot $out/bin/alot-feeds \ | ||
| 170 | --prefix MAILCAPS : ${alotMailcaps} \ | ||
| 171 | --add-flags --config=${alotConfig} \ | ||
| 172 | --add-flags --notmuch-config=${configPath} | ||
| 173 | |||
| 174 | mkdir $out/share | ||
| 175 | ln -s ${pkgs.alot}/share/alot $out/share | ||
| 176 | mkdir -p $out/share/applications | ||
| 177 | sed -r 's/alot/alot-feeds/g' ${pkgs.alot}/share/applications/alot.desktop > $out/share/applications/alot-feeds.desktop | ||
| 178 | mkdir -p $out/share/zsh/site-functions | ||
| 179 | sed -r 's/alot/alot-feeds/g' ${pkgs.alot}/share/zsh/site-functions/_alot > $out/share/zsh/site-functions/_alot-feeds | ||
| 180 | ''; | ||
| 181 | |||
| 182 | alotConfig = pkgs.runCommand "alot" { | ||
| 183 | realname = notmuchIni.user.name; | ||
| 184 | address = notmuchIni.user.primary_email; | ||
| 185 | } "substituteAll ${./alot.config} $out"; | ||
| 186 | alotMailcaps = pkgs.writeText "mailcaps" '' | ||
| 187 | text/html; ${pkgs.lynx}/bin/lynx -dump -dont_wrap_pre -assume_charset=utf-8 -display_charset=utf-8 "%s"; nametemplate=%s.html; copiousoutput | ||
| 188 | ''; | ||
| 189 | in { | ||
| 190 | config = { | ||
| 191 | home.packages = [ immWrapped notmuchWrapped pkgs.notmuch.man alotWrapped ]; | ||
| 192 | |||
| 193 | home.activation.createImm = hm.dag.entryAfter ["writeBoundary"] '' | ||
| 194 | $DRY_RUN_CMD mkdir -p $VERBOSE_ARG ${config.xdg.configHome}/imm | ||
| 195 | ''; | ||
| 196 | |||
| 197 | home.activation.createFeedsDatabase = hm.dag.entryAfter ["linkGeneration" "writeBoundary"] '' | ||
| 198 | $DRY_RUN_CMD mkdir -p -m 0750 $VERBOSE_ARG ${databasePath} | ||
| 199 | $DRY_RUN_CMD mkdir -p $VERBOSE_ARG ${databasePath}/new ${databasePath}/cur ${databasePath}/tmp | ||
| 200 | if ! [[ -d ${databasePath}/.notmuch ]]; then | ||
| 201 | NOTMUCH_VERBOSE_ARG="--quiet" | ||
| 202 | if [[ -v VERBOSE ]]; then | ||
| 203 | NOTMUCH_VERBOSE_ARG="--verbose" | ||
| 204 | fi | ||
| 205 | NOTMUCH_CONFIG=${configPath} $DRY_RUN_CMD ${pkgs.notmuch}/bin/notmuch new $NOTMUCH_VERBOSE_ARG | ||
| 206 | fi | ||
| 207 | $DRY_RUN_CMD ln -Tsf $VERBOSE_ARG ${notmuchHooksDir} ${databasePath}/.notmuch/hooks | ||
| 208 | ''; | ||
| 209 | |||
| 210 | systemd.user.services."logrotate-imm" = { | ||
| 211 | Unit = { | ||
| 212 | Description = "Rotate imm logfile"; | ||
| 213 | }; | ||
| 214 | Service = { | ||
| 215 | Type = "oneshot"; | ||
| 216 | ExecStart = '' | ||
| 217 | ${pkgs.logrotate}/bin/logrotate --state ${config.xdg.configHome}/imm/imm.logrotate ${pkgs.writeText "logrotate.conf" '' | ||
| 218 | ${config.xdg.configHome}/imm/imm.log { | ||
| 219 | rotate 5 | ||
| 220 | size 1024k | ||
| 221 | } | ||
| 222 | ''} | ||
| 223 | ''; | ||
| 224 | }; | ||
| 225 | }; | ||
| 226 | systemd.user.timers."logrotate-imm" = { | ||
| 227 | Timer = { | ||
| 228 | OnActiveSec = "6h"; | ||
| 229 | OnUnitActiveSec = "6h"; | ||
| 230 | }; | ||
| 231 | Install = { | ||
| 232 | WantedBy = ["default.target"]; | ||
| 233 | }; | ||
| 234 | }; | ||
| 235 | }; | ||
| 236 | } | ||
diff --git a/user-profiles/mpv/default.nix b/user-profiles/mpv/default.nix index 2df87994..8cf330e8 100644 --- a/user-profiles/mpv/default.nix +++ b/user-profiles/mpv/default.nix | |||
| @@ -1,6 +1,6 @@ | |||
| 1 | { config, lib, userName, pkgs, sources, ... }: | 1 | { lib, userName, pkgs, sources, ... }: |
| 2 | { | 2 | { |
| 3 | home-manager.users.${userName} = { | 3 | home-manager.users.${userName} = { config, ... }: { |
| 4 | programs.mpv = { | 4 | programs.mpv = { |
| 5 | enable = true; | 5 | enable = true; |
| 6 | package = pkgs.symlinkJoin { | 6 | package = pkgs.symlinkJoin { |
| @@ -10,7 +10,7 @@ | |||
| 10 | (pkgs.stdenv.mkDerivation (sources.mpv-reload // rec { | 10 | (pkgs.stdenv.mkDerivation (sources.mpv-reload // rec { |
| 11 | installPhase = '' | 11 | installPhase = '' |
| 12 | install -d $out/share/mpv/scripts | 12 | install -d $out/share/mpv/scripts |
| 13 | install -m 0644 reload.lua $out/share/mpv/scripts/${passthru.scriptName} | 13 | install -m 0644 main.lua $out/share/mpv/scripts/${passthru.scriptName} |
| 14 | ''; | 14 | ''; |
| 15 | 15 | ||
| 16 | passthru.scriptName = "reload.lua"; | 16 | passthru.scriptName = "reload.lua"; |
| @@ -105,6 +105,8 @@ | |||
| 105 | }; | 105 | }; |
| 106 | config = { | 106 | config = { |
| 107 | ytdl = true; | 107 | ytdl = true; |
| 108 | ytdl-format = "ytdl"; | ||
| 109 | # ytdl-raw-options = "sub-langs=\"${config.programs.yt-dlp.settings.sub-langs}\""; | ||
| 108 | subs-with-matching-audio = false; | 110 | subs-with-matching-audio = false; |
| 109 | audio-display = false; | 111 | audio-display = false; |
| 110 | osd-font = "Fira Sans"; | 112 | osd-font = "Fira Sans"; |
diff --git a/user-profiles/tmux/default.nix b/user-profiles/tmux/default.nix index 11c53788..7ea0c0d5 100644 --- a/user-profiles/tmux/default.nix +++ b/user-profiles/tmux/default.nix | |||
| @@ -1,10 +1,11 @@ | |||
| 1 | { userName, pkgs, lib, ... }: | 1 | { userName, pkgs, lib, ... }: |
| 2 | { | 2 | { |
| 3 | home-manager.users.${userName} = { | 3 | home-manager.users.${userName} = { config, ... }: { |
| 4 | programs.tmux = { | 4 | programs.tmux = { |
| 5 | enable = true; | 5 | enable = true; |
| 6 | clock24 = true; | 6 | clock24 = true; |
| 7 | historyLimit = 50000; | 7 | historyLimit = 50000; |
| 8 | mouse = true; | ||
| 8 | extraConfig = lib.readFile (pkgs.stdenv.mkDerivation { | 9 | extraConfig = lib.readFile (pkgs.stdenv.mkDerivation { |
| 9 | name = "tmux.conf"; | 10 | name = "tmux.conf"; |
| 10 | src = ./tmux.conf; | 11 | src = ./tmux.conf; |
| @@ -13,11 +14,10 @@ | |||
| 13 | 14 | ||
| 14 | phases = [ "installPhase" ]; | 15 | phases = [ "installPhase" ]; |
| 15 | 16 | ||
| 16 | inherit (pkgs) zsh; | ||
| 17 | mandb = pkgs.man-db; | ||
| 18 | |||
| 19 | installPhase = '' | 17 | installPhase = '' |
| 20 | substituteAll $src $out | 18 | substitute $src $out \ |
| 19 | --subst-var-by zsh ${lib.getExe config.programs.zsh.package} \ | ||
| 20 | --subst-var-by man ${lib.getExe config.programs.man.package} | ||
| 21 | ''; | 21 | ''; |
| 22 | }); | 22 | }); |
| 23 | }; | 23 | }; |
diff --git a/user-profiles/tmux/tmux.conf b/user-profiles/tmux/tmux.conf index 415d13e7..9e658800 100644 --- a/user-profiles/tmux/tmux.conf +++ b/user-profiles/tmux/tmux.conf | |||
| @@ -1,23 +1,20 @@ | |||
| 1 | set-option -g history-limit 50000 | ||
| 2 | set-option -g status-bg black | 1 | set-option -g status-bg black |
| 3 | set-option -g status-fg white | 2 | set-option -g status-fg white |
| 4 | set-option -g clock-mode-colour white | 3 | set-option -g clock-mode-colour white |
| 5 | set-option -g clock-mode-style 24 | ||
| 6 | set-option -g bell-action any | 4 | set-option -g bell-action any |
| 7 | set-option -g default-shell @zsh@/bin/zsh | 5 | set-option -g default-shell @zsh@ |
| 8 | set-option -g update-environment 'DISPLAY SSH_ASKPASS SSH_AUTH_SOCK SSH_AGENT_PID SSH_CONNECTION WINDOWID XAUTHORITY PROMPT_INFO PATH PGHOST PGLOG' | 6 | set-option -g update-environment 'DISPLAY SSH_ASKPASS SSH_AUTH_SOCK SSH_AGENT_PID SSH_CONNECTION WINDOWID XAUTHORITY PROMPT_INFO PATH PGHOST PGLOG' |
| 9 | set-option -g mouse on | ||
| 10 | set-option -g set-clipboard on | 7 | set-option -g set-clipboard on |
| 11 | set-option -g terminal-overrides 'rxvt-uni*:XT:Ms=\E]52;%p1%s;%p2%s\007' | 8 | set-option -g terminal-overrides 'rxvt-uni*:XT:Ms=\E]52;%p1%s;%p2%s\007' |
| 12 | 9 | ||
| 13 | set-environment -g LESS " -R " | 10 | set-environment -g LESS " -R " |
| 14 | 11 | ||
| 15 | ## determine if we should enable 256-colour support | 12 | ## determine if we should enable 256-colour support |
| 16 | if "[[ ''${TERM} =~ 256color || ''${TERM} == fbterm || ''${TERM} =~ alacritty ]]" 'set -g default-terminal tmux-256color' | 13 | if "[[ ''${TERM} =~ 256color || ''${TERM} == fbterm || ''${TERM} =~ alacritty || ''${TERM} =~ kitty ]]" 'set -g default-terminal tmux-256color' |
| 17 | 14 | ||
| 18 | set-option -g status-right "" | 15 | set-option -g status-right "" |
| 19 | 16 | ||
| 20 | bind / command-prompt "split-window -h 'exec @mandb@/bin/man %%'" | 17 | bind / command-prompt "split-window -h 'exec @man@ %%'" |
| 21 | bind C clock-mode | 18 | bind C clock-mode |
| 22 | bind r switch-client -r | 19 | bind r switch-client -r |
| 23 | 20 | ||
diff --git a/user-profiles/utils.nix b/user-profiles/utils.nix index 13eb6033..edf6da11 100644 --- a/user-profiles/utils.nix +++ b/user-profiles/utils.nix | |||
| @@ -1,19 +1,6 @@ | |||
| 1 | { userName, lib, pkgs, config, ... }: | 1 | { userName, lib, pkgs, config, ... }: |
| 2 | let | 2 | let |
| 3 | cfg = config.home-manager.users.${userName}; | 3 | cfg = config.home-manager.users.${userName}; |
| 4 | |||
| 5 | wrappedLess = pkgs.less.overrideAttrs (oldAttrs: { | ||
| 6 | pname = "${oldAttrs.pname or "less"}-wrapper"; | ||
| 7 | |||
| 8 | nativeBuildInputs = (oldAttrs.nativeBuildInputs or []) ++ (with pkgs; [makeWrapper]); | ||
| 9 | |||
| 10 | postInstall = '' | ||
| 11 | ${oldAttrs.postInstall or ""} | ||
| 12 | |||
| 13 | wrapProgram $out/bin/less \ | ||
| 14 | --prefix PATH : ${lib.makeBinPath (with pkgs; [binutils])} | ||
| 15 | ''; | ||
| 16 | }); | ||
| 17 | in { | 4 | in { |
| 18 | home-manager.users.${userName} = { | 5 | home-manager.users.${userName} = { |
| 19 | programs = { | 6 | programs = { |
| @@ -55,19 +42,25 @@ in { | |||
| 55 | }; | 42 | }; |
| 56 | 43 | ||
| 57 | jq.enable = true; | 44 | jq.enable = true; |
| 45 | |||
| 46 | lesspipe.enable = true; | ||
| 47 | |||
| 48 | man.enable = true; | ||
| 49 | |||
| 50 | vim.enable = true; | ||
| 58 | }; | 51 | }; |
| 59 | 52 | ||
| 60 | home.sessionVariables = { | 53 | home.sessionVariables = { |
| 61 | LESSCOLORIZER = "pygmentize -O style=rrt"; | 54 | LESSCOLORIZER = "${lib.getExe' pkgs.python3Packages.pygments "pygmentize"} -O style=rrt"; |
| 62 | }; | 55 | }; |
| 63 | 56 | ||
| 64 | home.packages = with pkgs; [ | 57 | home.packages = with pkgs; [ |
| 65 | autossh usbutils pciutils eza silver-searcher pwgen xkcdpass | 58 | autossh usbutils pciutils eza silver-searcher pwgen xkcdpass |
| 66 | unzip magic-wormhole qrencode tty-clock dnsutils openssl sshfs | 59 | unzip magic-wormhole dnsutils openssl sshfs |
| 67 | psmisc mosh tree vnstat file pv bc zip nmap aspell | 60 | psmisc mosh tree vnstat file pv bc zip nmap aspell |
| 68 | aspellDicts.de aspellDicts.en borgbackup man-pages rsync socat | 61 | aspellDicts.de aspellDicts.en borgbackup man-pages rsync socat |
| 69 | inetutils yq cached-nix-shell persistent-nix-shell rage | 62 | inetutils yq cached-nix-shell persistent-nix-shell rage |
| 70 | smartmontools hdparm nix-output-monitor wrappedLess dscp | 63 | smartmontools hdparm nix-output-monitor less dscp |
| 71 | iputils | 64 | iputils |
| 72 | ]; | 65 | ]; |
| 73 | }; | 66 | }; |
diff --git a/user-profiles/yt-dlp.nix b/user-profiles/yt-dlp.nix index 0f0b2204..eefa673f 100644 --- a/user-profiles/yt-dlp.nix +++ b/user-profiles/yt-dlp.nix | |||
| @@ -7,17 +7,26 @@ | |||
| 7 | cookies-from-browser = "firefox::none"; | 7 | cookies-from-browser = "firefox::none"; |
| 8 | mark-watched = true; | 8 | mark-watched = true; |
| 9 | format = lib.concatStringsSep "/" [ | 9 | format = lib.concatStringsSep "/" [ |
| 10 | "bestvideo*[width<=2560][height<=1440][fps<=60][vcodec!*=av01][width>=1920]+bestaudio" | ||
| 11 | "best[width<=2560][height<=1440][fps<=60][vcodec!*=av01][width>=1920]" | ||
| 12 | "bestvideo*[vcodec!*=av01][width>=1920]+bestaudio" | ||
| 13 | "best[vcodec!*=av01][width>=1920]" | ||
| 14 | "bestvideo*[width<=2560][height<=1440][fps<=60][vcodec!*=av01][height>=1080]+bestaudio" | ||
| 15 | "best[width<=2560][height<=1440][fps<=60][vcodec!*=av01][height>=1080]" | ||
| 16 | "bestvideo*[vcodec!*=av01][height>=1080]+bestaudio" | ||
| 17 | "best[vcodec!*=av01][height>=1080]" | ||
| 10 | "bestvideo*[width<=2560][height<=1440][fps<=60]+bestaudio" | 18 | "bestvideo*[width<=2560][height<=1440][fps<=60]+bestaudio" |
| 11 | "best[width<=2560][height<=1440][fps<=60]" | 19 | "best[width<=2560][height<=1440][fps<=60]" |
| 12 | "bestvideo*+bestaudio" | 20 | "bestvideo*+bestaudio" |
| 13 | "best" | 21 | "best" |
| 14 | ]; | 22 | ]; |
| 15 | embed-subs = true; | 23 | # embed-subs = true; |
| 24 | embed-thumbnail = true; | ||
| 25 | embed-metadata = true; | ||
| 16 | # write-subs = true; | 26 | # write-subs = true; |
| 17 | # write-auto-subs = true; | 27 | # write-auto-subs = true; |
| 18 | sub-langs = "en(-(gb|us|orig))?,de(-(de|orig))?,-live_chat,-rechat"; | 28 | # sub-langs = "en(-(gb|us|orig))?,de(-(de|orig))?,-live_chat,-rechat"; |
| 19 | prefer-free-formats = true; | 29 | prefer-free-formats = true; |
| 20 | embed-metadata = true; | ||
| 21 | # downloader = "${pkgs.axel}/bin/axel"; | 30 | # downloader = "${pkgs.axel}/bin/axel"; |
| 22 | concurrent-fragments = 12; | 31 | concurrent-fragments = 12; |
| 23 | buffer-size = "16K"; | 32 | buffer-size = "16K"; |
| @@ -28,7 +37,8 @@ | |||
| 28 | # "youtube:formats=dashy" | 37 | # "youtube:formats=dashy" |
| 29 | # ]; | 38 | # ]; |
| 30 | remux-video = "mp4>mkv"; | 39 | remux-video = "mp4>mkv"; |
| 31 | output = "\"%(title)s [%(uploader)s %(webpage_url)s].%(ext)s\""; | 40 | output = lib.mkDefault "\"%(modified_date>%Y%m%d,release_date>%Y%m%d,upload_date>%Y%m%d)s %(title)s [%(uploader)s %(webpage_url)s].%(ext)s\""; |
| 41 | audio-multistreams = true; | ||
| 32 | }; | 42 | }; |
| 33 | }; | 43 | }; |
| 34 | }; | 44 | }; |
diff --git a/user-profiles/zsh/default.nix b/user-profiles/zsh/default.nix index daeb7e82..ab523a52 100644 --- a/user-profiles/zsh/default.nix +++ b/user-profiles/zsh/default.nix | |||
| @@ -1,38 +1,72 @@ | |||
| 1 | { userName, pkgs, customUtils, lib, config, ... }: | 1 | { userName, pkgs, customUtils, lib, config, ... }: |
| 2 | let | 2 | { |
| 3 | dotDir = ".config/zsh"; | 3 | config = { |
| 4 | p10kZsh = "${dotDir}/.p10k.zsh"; | 4 | home-manager.users.${userName} = let sysConfig = config; in { config, ... }: { |
| 5 | cfg = config.home-manager.users.${userName}; | 5 | config = { |
| 6 | in { | 6 | programs.zsh = { |
| 7 | home-manager.users.${userName} = { | 7 | dotDir = ".config/zsh"; |
| 8 | programs.zsh = { | 8 | enable = true; |
| 9 | inherit dotDir; | 9 | autocd = true; |
| 10 | enable = true; | 10 | enableCompletion = true; |
| 11 | autocd = true; | 11 | enableVteIntegration = true; |
| 12 | enableCompletion = true; | 12 | history = { |
| 13 | 13 | append = true; | |
| 14 | plugins = [ | 14 | expireDuplicatesFirst = true; |
| 15 | { name = "powerlevel10k"; | 15 | extended = true; |
| 16 | file = "share/zsh-powerlevel10k/powerlevel10k.zsh-theme"; | 16 | findNoDups = true; |
| 17 | src = pkgs.zsh-powerlevel10k; | 17 | }; |
| 18 | } | 18 | syntaxHighlighting.enable = true; |
| 19 | ]; | 19 | zsh-abbr = { |
| 20 | initExtraFirst = '' | 20 | enable = true; |
| 21 | if [[ $TERM == "dumb" ]]; then | 21 | abbreviations = { |
| 22 | unsetopt zle | 22 | re = "systemctl restart"; |
| 23 | PS1='$ ' | 23 | ure = "systemctl --user restart"; |
| 24 | return | 24 | st = "systemctl status"; |
| 25 | fi | 25 | ust = "systemctl --user status"; |
| 26 | ''; | 26 | }; |
| 27 | initExtraBeforeCompInit = '' | 27 | globalAbbreviations = { |
| 28 | source "${cfg.home.homeDirectory}/${p10kZsh}" | 28 | "L" = "| less"; |
| 29 | ''; | 29 | "S" = "&> /dev/null"; |
| 30 | initExtra = lib.mkAfter '' | 30 | "G" = "| grep"; |
| 31 | source ${./zshrc} | 31 | "B" = "&> /dev/null &"; |
| 32 | source "${pkgs.zsh-syntax-highlighting}/share/zsh-syntax-highlighting/zsh-syntax-highlighting.zsh" | 32 | "BB" = "&> /dev/null &!"; |
| 33 | ''; | 33 | "J" = lib.mkIf config.programs.jq.enable "| jq '.'"; |
| 34 | }; | ||
| 35 | }; | ||
| 36 | |||
| 37 | plugins = [ | ||
| 38 | { name = "powerlevel10k"; | ||
| 39 | file = "share/zsh-powerlevel10k/powerlevel10k.zsh-theme"; | ||
| 40 | src = pkgs.zsh-powerlevel10k; | ||
| 41 | } | ||
| 42 | ]; | ||
| 43 | initContent = lib.mkMerge [ | ||
| 44 | (lib.mkBefore '' | ||
| 45 | if [[ $TERM == "dumb" ]]; then | ||
| 46 | unsetopt zle | ||
| 47 | PS1='$ ' | ||
| 48 | return | ||
| 49 | fi | ||
| 50 | '') | ||
| 51 | (lib.mkOrder 550 '' | ||
| 52 | source "$HOME/${config.xdg.configFile."zsh/.p10k.zsh".target}" | ||
| 53 | '') | ||
| 54 | (lib.mkAfter '' | ||
| 55 | source ${./zshrc} | ||
| 56 | '') | ||
| 57 | ]; | ||
| 58 | }; | ||
| 59 | |||
| 60 | xdg.configFile."zsh/.p10k.zsh".source = ./p10k.zsh; | ||
| 61 | }; | ||
| 34 | }; | 62 | }; |
| 35 | 63 | ||
| 36 | home.file.${p10kZsh}.source = ./p10k.zsh; | 64 | programs.zsh.enable = true; |
| 65 | environment.pathsToLink = [ "/share/zsh" ]; | ||
| 66 | environment.shellAliases = lib.mkOverride 90 {}; | ||
| 67 | |||
| 68 | nixpkgs.externalConfig.allowUnfreePredicate = pkg: builtins.elem (lib.getName pkg) [ | ||
| 69 | "zsh-abbr" | ||
| 70 | ]; | ||
| 37 | }; | 71 | }; |
| 38 | } | 72 | } |
diff --git a/user-profiles/zsh/zshrc b/user-profiles/zsh/zshrc index a83a8069..af3aca64 100644 --- a/user-profiles/zsh/zshrc +++ b/user-profiles/zsh/zshrc | |||
| @@ -24,7 +24,12 @@ setopt ignore_eof | |||
| 24 | bindkey -e | 24 | bindkey -e |
| 25 | bindkey ';5C' emacs-forward-word | 25 | bindkey ';5C' emacs-forward-word |
| 26 | bindkey ';5D' emacs-backward-word | 26 | bindkey ';5D' emacs-backward-word |
| 27 | bindkey '^[[1;5C' emacs-forward-word | ||
| 28 | bindkey '^[[1;5D' emacs-backward-word | ||
| 29 | bindkey '^H' backward-kill-word | ||
| 27 | 30 | ||
| 28 | autoload -Uz url-quote-magic bracketed-paste-magic | 31 | autoload -Uz url-quote-magic bracketed-paste-magic |
| 29 | zle -N self-insert url-quote-magic | 32 | zle -N self-insert url-quote-magic |
| 30 | zle -N bracketed-paste bracketed-paste-magic \ No newline at end of file | 33 | zle -N bracketed-paste bracketed-paste-magic |
| 34 | |||
| 35 | setopt extended_glob | ||
diff --git a/users/gkleen/default.nix b/users/gkleen/default.nix index 4ddf4be3..5ce93de7 100644 --- a/users/gkleen/default.nix +++ b/users/gkleen/default.nix | |||
| @@ -1,7 +1,7 @@ | |||
| 1 | { flake, userName, pkgs, customUtils, lib, ... }: | 1 | { flake, userName, pkgs, customUtils, lib, ... }: |
| 2 | { | 2 | { |
| 3 | imports = with flake.nixosModules.userProfiles.${userName}; [ | 3 | imports = with flake.nixosModules.userProfiles.${userName}; [ |
| 4 | zsh tmux utils direnv | 4 | utils direnv |
| 5 | ]; | 5 | ]; |
| 6 | 6 | ||
| 7 | users.users.${userName} = { | 7 | users.users.${userName} = { |
| @@ -29,9 +29,39 @@ | |||
| 29 | userName = "Gregor Kleen"; | 29 | userName = "Gregor Kleen"; |
| 30 | delta.enable = true; | 30 | delta.enable = true; |
| 31 | extraConfig = { | 31 | extraConfig = { |
| 32 | pull.rebase = false; | 32 | core.excludesfile = toString ./gitignore; |
| 33 | pull.rebase = true; | ||
| 33 | submodule.recurse = true; | 34 | submodule.recurse = true; |
| 34 | init.defaultBranch = "main"; | 35 | init.defaultBranch = "main"; |
| 36 | column.ui = "auto"; | ||
| 37 | branch.sort = "-committerdate"; | ||
| 38 | tag.sort = "version:refname"; | ||
| 39 | diff = { | ||
| 40 | algorithm = "histogram"; | ||
| 41 | colorMoved = "plain"; | ||
| 42 | mnemonicPrefix = true; | ||
| 43 | renames = true; | ||
| 44 | }; | ||
| 45 | push = { | ||
| 46 | default = "simple"; | ||
| 47 | autoSetupRemote = true; | ||
| 48 | followTags = true; | ||
| 49 | }; | ||
| 50 | fetch = { | ||
| 51 | prune = true; | ||
| 52 | pruneTags = true; | ||
| 53 | all = true; | ||
| 54 | }; | ||
| 55 | rerere = { | ||
| 56 | enabled = true; | ||
| 57 | autoupdate = true; | ||
| 58 | }; | ||
| 59 | rebase = { | ||
| 60 | autoSquash = true; | ||
| 61 | autoStash = true; | ||
| 62 | updateRefs = true; | ||
| 63 | }; | ||
| 64 | merge.conflictstyle = "zdiff3"; | ||
| 35 | }; | 65 | }; |
| 36 | }; | 66 | }; |
| 37 | 67 | ||
diff --git a/users/gkleen/gitignore b/users/gkleen/gitignore new file mode 100644 index 00000000..f7082b20 --- /dev/null +++ b/users/gkleen/gitignore | |||
| @@ -0,0 +1,2 @@ | |||
| 1 | **/#*# | ||
| 2 | **/.#* | ||
diff --git a/users/root.nix b/users/root.nix index b61f9cfd..ed1acd50 100644 --- a/users/root.nix +++ b/users/root.nix | |||
| @@ -3,7 +3,7 @@ let | |||
| 3 | haveGKleen = flake.nixosModules.accounts ? "gkleen@${hostName}"; | 3 | haveGKleen = flake.nixosModules.accounts ? "gkleen@${hostName}"; |
| 4 | in { | 4 | in { |
| 5 | imports = with flake.nixosModules.userProfiles.${userName}; [ | 5 | imports = with flake.nixosModules.userProfiles.${userName}; [ |
| 6 | zsh tmux direnv utils | 6 | direnv utils |
| 7 | ]; | 7 | ]; |
| 8 | 8 | ||
| 9 | users.users.${userName} = lib.mkIf haveGKleen { | 9 | users.users.${userName} = lib.mkIf haveGKleen { |
